drm/msm/hdmi: make use of the drm_connector_hdmi framework

Setup the HDMI connector on the MSM HDMI outputs. Make use of
atomic_check hook and of the provided Infoframe infrastructure.

Acked-by: Maxime Ripard <mripard@kernel.org>
Reviewed-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Patchwork: https://patchwork.freedesktop.org/patch/639656/
Link: https://lore.kernel.org/r/20250226-bridge-hdmi-connector-v8-3-340af24b35cc@linaro.org
This commit is contained in:
Dmitry Baryshkov 2025-02-26 10:59:26 +02:00
parent d309bda671
commit 384d2b03d0
5 changed files with 167 additions and 152 deletions

View File

@ -170,6 +170,8 @@ config DRM_MSM_HDMI
bool "Enable HDMI support in MSM DRM driver" bool "Enable HDMI support in MSM DRM driver"
depends on DRM_MSM depends on DRM_MSM
default y default y
select DRM_DISPLAY_HDMI_HELPER
select DRM_DISPLAY_HDMI_STATE_HELPER
help help
Compile in support for the HDMI output MSM DRM driver. It can Compile in support for the HDMI output MSM DRM driver. It can
be a primary or a secondary display on device. Note that this is used be a primary or a secondary display on device. Note that this is used

View File

@ -12,6 +12,7 @@
#include <drm/drm_bridge_connector.h> #include <drm/drm_bridge_connector.h>
#include <drm/drm_of.h> #include <drm/drm_of.h>
#include <drm/display/drm_hdmi_state_helper.h>
#include <sound/hdmi-codec.h> #include <sound/hdmi-codec.h>
#include "hdmi.h" #include "hdmi.h"
@ -165,8 +166,6 @@ int msm_hdmi_modeset_init(struct hdmi *hdmi,
hdmi->dev = dev; hdmi->dev = dev;
hdmi->encoder = encoder; hdmi->encoder = encoder;
hdmi_audio_infoframe_init(&hdmi->audio.infoframe);
ret = msm_hdmi_bridge_init(hdmi); ret = msm_hdmi_bridge_init(hdmi);
if (ret) { if (ret) {
DRM_DEV_ERROR(dev->dev, "failed to create HDMI bridge: %d\n", ret); DRM_DEV_ERROR(dev->dev, "failed to create HDMI bridge: %d\n", ret);
@ -254,40 +253,12 @@ static int msm_hdmi_audio_hw_params(struct device *dev, void *data,
struct hdmi_codec_params *params) struct hdmi_codec_params *params)
{ {
struct hdmi *hdmi = dev_get_drvdata(dev); struct hdmi *hdmi = dev_get_drvdata(dev);
unsigned int chan;
unsigned int channel_allocation = 0;
unsigned int rate; unsigned int rate;
unsigned int level_shift = 0; /* 0dB */ int ret;
bool down_mix = false;
DRM_DEV_DEBUG(dev, "%u Hz, %d bit, %d channels\n", params->sample_rate, DRM_DEV_DEBUG(dev, "%u Hz, %d bit, %d channels\n", params->sample_rate,
params->sample_width, params->cea.channels); params->sample_width, params->cea.channels);
switch (params->cea.channels) {
case 2:
/* FR and FL speakers */
channel_allocation = 0;
chan = MSM_HDMI_AUDIO_CHANNEL_2;
break;
case 4:
/* FC, LFE, FR and FL speakers */
channel_allocation = 0x3;
chan = MSM_HDMI_AUDIO_CHANNEL_4;
break;
case 6:
/* RR, RL, FC, LFE, FR and FL speakers */
channel_allocation = 0x0B;
chan = MSM_HDMI_AUDIO_CHANNEL_6;
break;
case 8:
/* FRC, FLC, RR, RL, FC, LFE, FR and FL speakers */
channel_allocation = 0x1F;
chan = MSM_HDMI_AUDIO_CHANNEL_8;
break;
default:
return -EINVAL;
}
switch (params->sample_rate) { switch (params->sample_rate) {
case 32000: case 32000:
rate = HDMI_SAMPLE_RATE_32KHZ; rate = HDMI_SAMPLE_RATE_32KHZ;
@ -316,9 +287,12 @@ static int msm_hdmi_audio_hw_params(struct device *dev, void *data,
return -EINVAL; return -EINVAL;
} }
msm_hdmi_audio_set_sample_rate(hdmi, rate); ret = drm_atomic_helper_connector_hdmi_update_audio_infoframe(hdmi->connector,
msm_hdmi_audio_info_setup(hdmi, 1, chan, channel_allocation, &params->cea);
level_shift, down_mix); if (ret)
return ret;
msm_hdmi_audio_info_setup(hdmi, rate, params->cea.channels);
return 0; return 0;
} }
@ -327,7 +301,8 @@ static void msm_hdmi_audio_shutdown(struct device *dev, void *data)
{ {
struct hdmi *hdmi = dev_get_drvdata(dev); struct hdmi *hdmi = dev_get_drvdata(dev);
msm_hdmi_audio_info_setup(hdmi, 0, 0, 0, 0, 0); drm_atomic_helper_connector_hdmi_clear_audio_infoframe(hdmi->connector);
msm_hdmi_audio_disable(hdmi);
} }
static const struct hdmi_codec_ops msm_hdmi_audio_codec_ops = { static const struct hdmi_codec_ops msm_hdmi_audio_codec_ops = {

View File

@ -24,8 +24,8 @@ struct hdmi_platform_config;
struct hdmi_audio { struct hdmi_audio {
bool enabled; bool enabled;
struct hdmi_audio_infoframe infoframe;
int rate; int rate;
int channels;
}; };
struct hdmi_hdcp_ctrl; struct hdmi_hdcp_ctrl;
@ -207,12 +207,6 @@ static inline int msm_hdmi_pll_8998_init(struct platform_device *pdev)
/* /*
* audio: * audio:
*/ */
/* Supported HDMI Audio channels and rates */
#define MSM_HDMI_AUDIO_CHANNEL_2 0
#define MSM_HDMI_AUDIO_CHANNEL_4 1
#define MSM_HDMI_AUDIO_CHANNEL_6 2
#define MSM_HDMI_AUDIO_CHANNEL_8 3
#define HDMI_SAMPLE_RATE_32KHZ 0 #define HDMI_SAMPLE_RATE_32KHZ 0
#define HDMI_SAMPLE_RATE_44_1KHZ 1 #define HDMI_SAMPLE_RATE_44_1KHZ 1
#define HDMI_SAMPLE_RATE_48KHZ 2 #define HDMI_SAMPLE_RATE_48KHZ 2
@ -222,11 +216,8 @@ static inline int msm_hdmi_pll_8998_init(struct platform_device *pdev)
#define HDMI_SAMPLE_RATE_192KHZ 6 #define HDMI_SAMPLE_RATE_192KHZ 6
int msm_hdmi_audio_update(struct hdmi *hdmi); int msm_hdmi_audio_update(struct hdmi *hdmi);
int msm_hdmi_audio_info_setup(struct hdmi *hdmi, bool enabled, int msm_hdmi_audio_info_setup(struct hdmi *hdmi, int rate, int channels);
uint32_t num_of_channels, uint32_t channel_allocation, int msm_hdmi_audio_disable(struct hdmi *hdmi);
uint32_t level_shift, bool down_mix);
void msm_hdmi_audio_set_sample_rate(struct hdmi *hdmi, int rate);
/* /*
* hdmi bridge: * hdmi bridge:

View File

@ -7,9 +7,6 @@
#include <linux/hdmi.h> #include <linux/hdmi.h>
#include "hdmi.h" #include "hdmi.h"
/* maps MSM_HDMI_AUDIO_CHANNEL_n consts used by audio driver to # of channels: */
static int nchannels[] = { 2, 4, 6, 8 };
/* Supported HDMI Audio sample rates */ /* Supported HDMI Audio sample rates */
#define MSM_HDMI_SAMPLE_RATE_32KHZ 0 #define MSM_HDMI_SAMPLE_RATE_32KHZ 0
#define MSM_HDMI_SAMPLE_RATE_44_1KHZ 1 #define MSM_HDMI_SAMPLE_RATE_44_1KHZ 1
@ -74,16 +71,17 @@ static const struct hdmi_msm_audio_arcs *get_arcs(unsigned long int pixclock)
int msm_hdmi_audio_update(struct hdmi *hdmi) int msm_hdmi_audio_update(struct hdmi *hdmi)
{ {
struct hdmi_audio *audio = &hdmi->audio; struct hdmi_audio *audio = &hdmi->audio;
struct hdmi_audio_infoframe *info = &audio->infoframe;
const struct hdmi_msm_audio_arcs *arcs = NULL; const struct hdmi_msm_audio_arcs *arcs = NULL;
bool enabled = audio->enabled; bool enabled = audio->enabled;
uint32_t acr_pkt_ctrl, vbi_pkt_ctrl, aud_pkt_ctrl; uint32_t acr_pkt_ctrl, vbi_pkt_ctrl, aud_pkt_ctrl;
uint32_t infofrm_ctrl, audio_config; uint32_t audio_config;
if (!hdmi->connector->display_info.is_hdmi)
return -EINVAL;
DBG("audio: enabled=%d, channels=%d, rate=%d",
audio->enabled, audio->channels, audio->rate);
DBG("audio: enabled=%d, channels=%d, channel_allocation=0x%x, "
"level_shift_value=%d, downmix_inhibit=%d, rate=%d",
audio->enabled, info->channels, info->channel_allocation,
info->level_shift_value, info->downmix_inhibit, audio->rate);
DBG("video: power_on=%d, pixclock=%lu", hdmi->power_on, hdmi->pixclock); DBG("video: power_on=%d, pixclock=%lu", hdmi->power_on, hdmi->pixclock);
if (enabled && !(hdmi->power_on && hdmi->pixclock)) { if (enabled && !(hdmi->power_on && hdmi->pixclock)) {
@ -104,7 +102,6 @@ int msm_hdmi_audio_update(struct hdmi *hdmi)
acr_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_ACR_PKT_CTRL); acr_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_ACR_PKT_CTRL);
vbi_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_VBI_PKT_CTRL); vbi_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_VBI_PKT_CTRL);
aud_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_AUDIO_PKT_CTRL1); aud_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_AUDIO_PKT_CTRL1);
infofrm_ctrl = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0);
audio_config = hdmi_read(hdmi, REG_HDMI_AUDIO_CFG); audio_config = hdmi_read(hdmi, REG_HDMI_AUDIO_CFG);
/* Clear N/CTS selection bits */ /* Clear N/CTS selection bits */
@ -113,7 +110,6 @@ int msm_hdmi_audio_update(struct hdmi *hdmi)
if (enabled) { if (enabled) {
uint32_t n, cts, multiplier; uint32_t n, cts, multiplier;
enum hdmi_acr_cts select; enum hdmi_acr_cts select;
uint8_t buf[14];
n = arcs->lut[audio->rate].n; n = arcs->lut[audio->rate].n;
cts = arcs->lut[audio->rate].cts; cts = arcs->lut[audio->rate].cts;
@ -155,20 +151,12 @@ int msm_hdmi_audio_update(struct hdmi *hdmi)
HDMI_ACR_1_N(n)); HDMI_ACR_1_N(n));
hdmi_write(hdmi, REG_HDMI_AUDIO_PKT_CTRL2, hdmi_write(hdmi, REG_HDMI_AUDIO_PKT_CTRL2,
COND(info->channels != 2, HDMI_AUDIO_PKT_CTRL2_LAYOUT) | COND(audio->channels != 2, HDMI_AUDIO_PKT_CTRL2_LAYOUT) |
HDMI_AUDIO_PKT_CTRL2_OVERRIDE); HDMI_AUDIO_PKT_CTRL2_OVERRIDE);
acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_CONT; acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_CONT;
acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_SEND; acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_SEND;
/* configure infoframe: */
hdmi_audio_infoframe_pack(info, buf, sizeof(buf));
hdmi_write(hdmi, REG_HDMI_AUDIO_INFO0,
(buf[3] << 0) | (buf[4] << 8) |
(buf[5] << 16) | (buf[6] << 24));
hdmi_write(hdmi, REG_HDMI_AUDIO_INFO1,
(buf[7] << 0) | (buf[8] << 8));
hdmi_write(hdmi, REG_HDMI_GC, 0); hdmi_write(hdmi, REG_HDMI_GC, 0);
vbi_pkt_ctrl |= HDMI_VBI_PKT_CTRL_GC_ENABLE; vbi_pkt_ctrl |= HDMI_VBI_PKT_CTRL_GC_ENABLE;
@ -176,11 +164,6 @@ int msm_hdmi_audio_update(struct hdmi *hdmi)
aud_pkt_ctrl |= HDMI_AUDIO_PKT_CTRL1_AUDIO_SAMPLE_SEND; aud_pkt_ctrl |= HDMI_AUDIO_PKT_CTRL1_AUDIO_SAMPLE_SEND;
infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND;
infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT;
infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE;
infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE;
audio_config &= ~HDMI_AUDIO_CFG_FIFO_WATERMARK__MASK; audio_config &= ~HDMI_AUDIO_CFG_FIFO_WATERMARK__MASK;
audio_config |= HDMI_AUDIO_CFG_FIFO_WATERMARK(4); audio_config |= HDMI_AUDIO_CFG_FIFO_WATERMARK(4);
audio_config |= HDMI_AUDIO_CFG_ENGINE_ENABLE; audio_config |= HDMI_AUDIO_CFG_ENGINE_ENABLE;
@ -190,17 +173,12 @@ int msm_hdmi_audio_update(struct hdmi *hdmi)
vbi_pkt_ctrl &= ~HDMI_VBI_PKT_CTRL_GC_ENABLE; vbi_pkt_ctrl &= ~HDMI_VBI_PKT_CTRL_GC_ENABLE;
vbi_pkt_ctrl &= ~HDMI_VBI_PKT_CTRL_GC_EVERY_FRAME; vbi_pkt_ctrl &= ~HDMI_VBI_PKT_CTRL_GC_EVERY_FRAME;
aud_pkt_ctrl &= ~HDMI_AUDIO_PKT_CTRL1_AUDIO_SAMPLE_SEND; aud_pkt_ctrl &= ~HDMI_AUDIO_PKT_CTRL1_AUDIO_SAMPLE_SEND;
infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND;
infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT;
infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE;
infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE;
audio_config &= ~HDMI_AUDIO_CFG_ENGINE_ENABLE; audio_config &= ~HDMI_AUDIO_CFG_ENGINE_ENABLE;
} }
hdmi_write(hdmi, REG_HDMI_ACR_PKT_CTRL, acr_pkt_ctrl); hdmi_write(hdmi, REG_HDMI_ACR_PKT_CTRL, acr_pkt_ctrl);
hdmi_write(hdmi, REG_HDMI_VBI_PKT_CTRL, vbi_pkt_ctrl); hdmi_write(hdmi, REG_HDMI_VBI_PKT_CTRL, vbi_pkt_ctrl);
hdmi_write(hdmi, REG_HDMI_AUDIO_PKT_CTRL1, aud_pkt_ctrl); hdmi_write(hdmi, REG_HDMI_AUDIO_PKT_CTRL1, aud_pkt_ctrl);
hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, infofrm_ctrl);
hdmi_write(hdmi, REG_HDMI_AUD_INT, hdmi_write(hdmi, REG_HDMI_AUD_INT,
COND(enabled, HDMI_AUD_INT_AUD_FIFO_URUN_INT) | COND(enabled, HDMI_AUD_INT_AUD_FIFO_URUN_INT) |
@ -214,41 +192,29 @@ int msm_hdmi_audio_update(struct hdmi *hdmi)
return 0; return 0;
} }
int msm_hdmi_audio_info_setup(struct hdmi *hdmi, bool enabled, int msm_hdmi_audio_info_setup(struct hdmi *hdmi, int rate, int channels)
uint32_t num_of_channels, uint32_t channel_allocation,
uint32_t level_shift, bool down_mix)
{ {
struct hdmi_audio *audio;
if (!hdmi) if (!hdmi)
return -ENXIO; return -ENXIO;
audio = &hdmi->audio; if ((rate < 0) || (rate >= MSM_HDMI_SAMPLE_RATE_MAX))
if (num_of_channels >= ARRAY_SIZE(nchannels))
return -EINVAL; return -EINVAL;
audio->enabled = enabled; hdmi->audio.rate = rate;
audio->infoframe.channels = nchannels[num_of_channels]; hdmi->audio.channels = channels;
audio->infoframe.channel_allocation = channel_allocation; hdmi->audio.enabled = true;
audio->infoframe.level_shift_value = level_shift;
audio->infoframe.downmix_inhibit = down_mix;
return msm_hdmi_audio_update(hdmi); return msm_hdmi_audio_update(hdmi);
} }
void msm_hdmi_audio_set_sample_rate(struct hdmi *hdmi, int rate) int msm_hdmi_audio_disable(struct hdmi *hdmi)
{ {
struct hdmi_audio *audio;
if (!hdmi) if (!hdmi)
return; return -ENXIO;
audio = &hdmi->audio; hdmi->audio.rate = 0;
hdmi->audio.channels = 2;
hdmi->audio.enabled = false;
if ((rate < 0) || (rate >= MSM_HDMI_SAMPLE_RATE_MAX)) return msm_hdmi_audio_update(hdmi);
return;
audio->rate = rate;
msm_hdmi_audio_update(hdmi);
} }

View File

@ -7,6 +7,8 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <drm/drm_bridge_connector.h> #include <drm/drm_bridge_connector.h>
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
#include <drm/display/drm_hdmi_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>
#include "msm_kms.h" #include "msm_kms.h"
#include "hdmi.h" #include "hdmi.h"
@ -68,23 +70,17 @@ static void power_off(struct drm_bridge *bridge)
#define AVI_IFRAME_LINE_NUMBER 1 #define AVI_IFRAME_LINE_NUMBER 1
static void msm_hdmi_config_avi_infoframe(struct hdmi *hdmi) static int msm_hdmi_config_avi_infoframe(struct hdmi *hdmi,
const u8 *buffer, size_t len)
{ {
struct drm_crtc *crtc = hdmi->encoder->crtc; u32 buf[4] = {};
const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
union hdmi_infoframe frame;
u8 buffer[HDMI_INFOFRAME_SIZE(AVI)];
u32 val; u32 val;
int len; int i;
drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, if (len != HDMI_INFOFRAME_SIZE(AVI) || len - 3 > sizeof(buf)) {
hdmi->connector, mode);
len = hdmi_infoframe_pack(&frame, buffer, sizeof(buffer));
if (len < 0) {
DRM_DEV_ERROR(&hdmi->pdev->dev, DRM_DEV_ERROR(&hdmi->pdev->dev,
"failed to configure avi infoframe\n"); "failed to configure avi infoframe\n");
return; return -EINVAL;
} }
/* /*
@ -93,37 +89,118 @@ static void msm_hdmi_config_avi_infoframe(struct hdmi *hdmi)
* written to the LSB byte of AVI_INFO0 and the version is written to * written to the LSB byte of AVI_INFO0 and the version is written to
* the third byte from the LSB of AVI_INFO3 * the third byte from the LSB of AVI_INFO3
*/ */
hdmi_write(hdmi, REG_HDMI_AVI_INFO(0), memcpy(buf, &buffer[3], len - 3);
buffer[3] |
buffer[4] << 8 |
buffer[5] << 16 |
buffer[6] << 24);
hdmi_write(hdmi, REG_HDMI_AVI_INFO(1), buf[3] |= buffer[1] << 24;
buffer[7] |
buffer[8] << 8 |
buffer[9] << 16 |
buffer[10] << 24);
hdmi_write(hdmi, REG_HDMI_AVI_INFO(2), for (i = 0; i < ARRAY_SIZE(buf); i++)
buffer[11] | hdmi_write(hdmi, REG_HDMI_AVI_INFO(i), buf[i]);
buffer[12] << 8 |
buffer[13] << 16 |
buffer[14] << 24);
hdmi_write(hdmi, REG_HDMI_AVI_INFO(3), val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1);
buffer[15] | val |= HDMI_INFOFRAME_CTRL0_AVI_SEND |
buffer[16] << 8 | HDMI_INFOFRAME_CTRL0_AVI_CONT;
buffer[1] << 24); hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, val);
hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0,
HDMI_INFOFRAME_CTRL0_AVI_SEND |
HDMI_INFOFRAME_CTRL0_AVI_CONT);
val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1); val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1);
val &= ~HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE__MASK; val &= ~HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE__MASK;
val |= HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE(AVI_IFRAME_LINE_NUMBER); val |= HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE(AVI_IFRAME_LINE_NUMBER);
hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, val); hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, val);
return 0;
}
static int msm_hdmi_config_audio_infoframe(struct hdmi *hdmi,
const u8 *buffer, size_t len)
{
u32 val;
if (len != HDMI_INFOFRAME_SIZE(AUDIO)) {
DRM_DEV_ERROR(&hdmi->pdev->dev,
"failed to configure audio infoframe\n");
return -EINVAL;
}
hdmi_write(hdmi, REG_HDMI_AUDIO_INFO0,
buffer[3] |
buffer[4] << 8 |
buffer[5] << 16 |
buffer[6] << 24);
hdmi_write(hdmi, REG_HDMI_AUDIO_INFO1,
buffer[7] |
buffer[8] << 8 |
buffer[9] << 16 |
buffer[10] << 24);
val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1);
val |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND |
HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT |
HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE |
HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE;
hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, val);
return 0;
}
static int msm_hdmi_bridge_clear_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type)
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
u32 val;
switch (type) {
case HDMI_INFOFRAME_TYPE_AVI:
val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0);
val &= ~(HDMI_INFOFRAME_CTRL0_AVI_SEND |
HDMI_INFOFRAME_CTRL0_AVI_CONT);
hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, val);
val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1);
val &= ~HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE__MASK;
hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, val);
break;
case HDMI_INFOFRAME_TYPE_AUDIO:
val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0);
val &= ~(HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND |
HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT |
HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE |
HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE);
hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, val);
val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1);
val &= ~HDMI_INFOFRAME_CTRL1_AUDIO_INFO_LINE__MASK;
hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, val);
break;
default:
drm_dbg_driver(hdmi_bridge->base.dev, "Unsupported infoframe type %x\n", type);
}
return 0;
}
static int msm_hdmi_bridge_write_infoframe(struct drm_bridge *bridge,
enum hdmi_infoframe_type type,
const u8 *buffer, size_t len)
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
msm_hdmi_bridge_clear_infoframe(bridge, type);
switch (type) {
case HDMI_INFOFRAME_TYPE_AVI:
return msm_hdmi_config_avi_infoframe(hdmi, buffer, len);
case HDMI_INFOFRAME_TYPE_AUDIO:
return msm_hdmi_config_audio_infoframe(hdmi, buffer, len);
default:
drm_dbg_driver(hdmi_bridge->base.dev, "Unsupported infoframe type %x\n", type);
return 0;
}
} }
static void msm_hdmi_set_timings(struct hdmi *hdmi, static void msm_hdmi_set_timings(struct hdmi *hdmi,
@ -146,18 +223,20 @@ static void msm_hdmi_bridge_atomic_pre_enable(struct drm_bridge *bridge,
conn_state = drm_atomic_get_new_connector_state(state, connector); conn_state = drm_atomic_get_new_connector_state(state, connector);
crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
hdmi->pixclock = conn_state->hdmi.tmds_char_rate;
msm_hdmi_set_timings(hdmi, &crtc_state->adjusted_mode); msm_hdmi_set_timings(hdmi, &crtc_state->adjusted_mode);
if (!hdmi->power_on) { if (!hdmi->power_on) {
msm_hdmi_phy_resource_enable(phy); msm_hdmi_phy_resource_enable(phy);
msm_hdmi_power_on(bridge); msm_hdmi_power_on(bridge);
hdmi->power_on = true; hdmi->power_on = true;
if (hdmi->hdmi_mode) { if (hdmi->hdmi_mode)
msm_hdmi_config_avi_infoframe(hdmi);
msm_hdmi_audio_update(hdmi); msm_hdmi_audio_update(hdmi);
}
} }
drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
msm_hdmi_phy_powerup(phy, hdmi->pixclock); msm_hdmi_phy_powerup(phy, hdmi->pixclock);
msm_hdmi_set_mode(hdmi, true); msm_hdmi_set_mode(hdmi, true);
@ -196,8 +275,6 @@ static void msm_hdmi_set_timings(struct hdmi *hdmi,
int hstart, hend, vstart, vend; int hstart, hend, vstart, vend;
uint32_t frame_ctrl; uint32_t frame_ctrl;
hdmi->pixclock = mode->clock * 1000;
hstart = mode->htotal - mode->hsync_start; hstart = mode->htotal - mode->hsync_start;
hend = mode->htotal - mode->hsync_start + mode->hdisplay; hend = mode->htotal - mode->hsync_start + mode->hdisplay;
@ -275,18 +352,16 @@ static const struct drm_edid *msm_hdmi_bridge_edid_read(struct drm_bridge *bridg
return drm_edid; return drm_edid;
} }
static enum drm_mode_status msm_hdmi_bridge_mode_valid(struct drm_bridge *bridge, static enum drm_mode_status msm_hdmi_bridge_tmds_char_rate_valid(const struct drm_bridge *bridge,
const struct drm_display_info *info, const struct drm_display_mode *mode,
const struct drm_display_mode *mode) unsigned long long tmds_rate)
{ {
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi; struct hdmi *hdmi = hdmi_bridge->hdmi;
const struct hdmi_platform_config *config = hdmi->config; const struct hdmi_platform_config *config = hdmi->config;
struct msm_drm_private *priv = bridge->dev->dev_private; struct msm_drm_private *priv = bridge->dev->dev_private;
struct msm_kms *kms = priv->kms; struct msm_kms *kms = priv->kms;
long actual, requested; long actual;
requested = 1000 * mode->clock;
/* for mdp5/apq8074, we manage our own pixel clk (as opposed to /* for mdp5/apq8074, we manage our own pixel clk (as opposed to
* mdp4/dtv stuff where pixel clk is assigned to mdp/encoder * mdp4/dtv stuff where pixel clk is assigned to mdp/encoder
@ -294,15 +369,16 @@ static enum drm_mode_status msm_hdmi_bridge_mode_valid(struct drm_bridge *bridge
*/ */
if (kms->funcs->round_pixclk) if (kms->funcs->round_pixclk)
actual = kms->funcs->round_pixclk(kms, actual = kms->funcs->round_pixclk(kms,
requested, hdmi_bridge->hdmi->encoder); tmds_rate,
hdmi_bridge->hdmi->encoder);
else if (config->pwr_clk_cnt > 0) else if (config->pwr_clk_cnt > 0)
actual = clk_round_rate(hdmi->pwr_clks[0], requested); actual = clk_round_rate(hdmi->pwr_clks[0], tmds_rate);
else else
actual = requested; actual = tmds_rate;
DBG("requested=%ld, actual=%ld", requested, actual); DBG("requested=%lld, actual=%ld", tmds_rate, actual);
if (actual != requested) if (actual != tmds_rate)
return MODE_CLOCK_RANGE; return MODE_CLOCK_RANGE;
return 0; return 0;
@ -314,9 +390,11 @@ static const struct drm_bridge_funcs msm_hdmi_bridge_funcs = {
.atomic_reset = drm_atomic_helper_bridge_reset, .atomic_reset = drm_atomic_helper_bridge_reset,
.atomic_pre_enable = msm_hdmi_bridge_atomic_pre_enable, .atomic_pre_enable = msm_hdmi_bridge_atomic_pre_enable,
.atomic_post_disable = msm_hdmi_bridge_atomic_post_disable, .atomic_post_disable = msm_hdmi_bridge_atomic_post_disable,
.mode_valid = msm_hdmi_bridge_mode_valid,
.edid_read = msm_hdmi_bridge_edid_read, .edid_read = msm_hdmi_bridge_edid_read,
.detect = msm_hdmi_bridge_detect, .detect = msm_hdmi_bridge_detect,
.hdmi_tmds_char_rate_valid = msm_hdmi_bridge_tmds_char_rate_valid,
.hdmi_clear_infoframe = msm_hdmi_bridge_clear_infoframe,
.hdmi_write_infoframe = msm_hdmi_bridge_write_infoframe,
}; };
static void static void
@ -348,8 +426,11 @@ int msm_hdmi_bridge_init(struct hdmi *hdmi)
bridge->funcs = &msm_hdmi_bridge_funcs; bridge->funcs = &msm_hdmi_bridge_funcs;
bridge->ddc = hdmi->i2c; bridge->ddc = hdmi->i2c;
bridge->type = DRM_MODE_CONNECTOR_HDMIA; bridge->type = DRM_MODE_CONNECTOR_HDMIA;
bridge->vendor = "Qualcomm";
bridge->product = "Snapdragon";
bridge->ops = DRM_BRIDGE_OP_HPD | bridge->ops = DRM_BRIDGE_OP_HPD |
DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_DETECT |
DRM_BRIDGE_OP_HDMI |
DRM_BRIDGE_OP_EDID; DRM_BRIDGE_OP_EDID;
ret = devm_drm_bridge_add(hdmi->dev->dev, bridge); ret = devm_drm_bridge_add(hdmi->dev->dev, bridge);