Commit ba9877e2 authored by Neil Armstrong's avatar Neil Armstrong Committed by Andrzej Hajda

drm/bridge: dw-hdmi: add support for YUV420 output

In order to support the HDMI2.0 YUV420 display modes, this patch
adds support for the YUV420 TMDS Clock divided by 2 and the controller
passthrough mode.

YUV420 Synopsys PHY support will need some specific configuration table
to support theses modes.

This patch is based on work from Zheng Yang <zhengyang@rock-chips.com> in
the Rockchip Linux 4.4 BSP at [1]

[1] https://github.com/rockchip-linux/kernel/tree/release-4.4

Cc: Zheng Yang <zhengyang@rock-chips.com>
Signed-off-by: default avatarNeil Armstrong <narmstrong@baylibre.com>
Tested-by: default avatarHeiko Stuebner <heiko@sntech.de>
Reviewed-by: default avatarAndrzej Hajda <a.hajda@samsung.com>
Signed-off-by: default avatarAndrzej Hajda <a.hajda@samsung.com>
Link: https://patchwork.freedesktop.org/patch/msgid/1549022873-40549-5-git-send-email-narmstrong@baylibre.com
parent 74f6d1e1
...@@ -99,6 +99,7 @@ struct hdmi_vmode { ...@@ -99,6 +99,7 @@ struct hdmi_vmode {
unsigned int mpixelclock; unsigned int mpixelclock;
unsigned int mpixelrepetitioninput; unsigned int mpixelrepetitioninput;
unsigned int mpixelrepetitionoutput; unsigned int mpixelrepetitionoutput;
unsigned int mtmdsclock;
}; };
struct hdmi_data_info { struct hdmi_data_info {
...@@ -543,7 +544,7 @@ static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi) ...@@ -543,7 +544,7 @@ static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi)
static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi) static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
{ {
mutex_lock(&hdmi->audio_mutex); mutex_lock(&hdmi->audio_mutex);
hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock, hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock,
hdmi->sample_rate); hdmi->sample_rate);
mutex_unlock(&hdmi->audio_mutex); mutex_unlock(&hdmi->audio_mutex);
} }
...@@ -552,7 +553,7 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate) ...@@ -552,7 +553,7 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
{ {
mutex_lock(&hdmi->audio_mutex); mutex_lock(&hdmi->audio_mutex);
hdmi->sample_rate = rate; hdmi->sample_rate = rate;
hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock, hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock,
hdmi->sample_rate); hdmi->sample_rate);
mutex_unlock(&hdmi->audio_mutex); mutex_unlock(&hdmi->audio_mutex);
} }
...@@ -653,6 +654,20 @@ static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format) ...@@ -653,6 +654,20 @@ static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format)
} }
} }
static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format)
{
switch (bus_format) {
case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
return true;
default:
return false;
}
}
static int hdmi_bus_fmt_color_depth(unsigned int bus_format) static int hdmi_bus_fmt_color_depth(unsigned int bus_format)
{ {
switch (bus_format) { switch (bus_format) {
...@@ -882,7 +897,8 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi) ...@@ -882,7 +897,8 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
u8 val, vp_conf; u8 val, vp_conf;
if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) || if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) ||
hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) { hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) ||
hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) {
switch (hdmi_bus_fmt_color_depth( switch (hdmi_bus_fmt_color_depth(
hdmi->hdmi_data.enc_out_bus_format)) { hdmi->hdmi_data.enc_out_bus_format)) {
case 8: case 8:
...@@ -1036,7 +1052,7 @@ EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_write); ...@@ -1036,7 +1052,7 @@ EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_write);
*/ */
void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi) void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi)
{ {
unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mpixelclock; unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
/* Control for TMDS Bit Period/TMDS Clock-Period Ratio */ /* Control for TMDS Bit Period/TMDS Clock-Period Ratio */
if (hdmi->connector.display_info.hdmi.scdc.supported) { if (hdmi->connector.display_info.hdmi.scdc.supported) {
...@@ -1198,6 +1214,8 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi, ...@@ -1198,6 +1214,8 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr; const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
const struct dw_hdmi_phy_config *phy_config = pdata->phy_config; const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
/* TOFIX Will need 420 specific PHY configuration tables */
/* PLL/MPLL Cfg - always match on final entry */ /* PLL/MPLL Cfg - always match on final entry */
for (; mpll_config->mpixelclock != ~0UL; mpll_config++) for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
if (mpixelclock <= mpll_config->mpixelclock) if (mpixelclock <= mpll_config->mpixelclock)
...@@ -1245,6 +1263,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi) ...@@ -1245,6 +1263,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
const struct dw_hdmi_phy_data *phy = hdmi->phy.data; const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
unsigned long mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock; unsigned long mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
int ret; int ret;
dw_hdmi_phy_power_off(hdmi); dw_hdmi_phy_power_off(hdmi);
...@@ -1273,7 +1292,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi) ...@@ -1273,7 +1292,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
} }
/* Wait for resuming transmission of TMDS clock and data */ /* Wait for resuming transmission of TMDS clock and data */
if (mpixelclock > HDMI14_MAX_TMDSCLK) if (mtmdsclock > HDMI14_MAX_TMDSCLK)
msleep(100); msleep(100);
return dw_hdmi_phy_power_on(hdmi); return dw_hdmi_phy_power_on(hdmi);
...@@ -1390,6 +1409,8 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode) ...@@ -1390,6 +1409,8 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
frame.colorspace = HDMI_COLORSPACE_YUV444; frame.colorspace = HDMI_COLORSPACE_YUV444;
else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
frame.colorspace = HDMI_COLORSPACE_YUV422; frame.colorspace = HDMI_COLORSPACE_YUV422;
else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
frame.colorspace = HDMI_COLORSPACE_YUV420;
else else
frame.colorspace = HDMI_COLORSPACE_RGB; frame.colorspace = HDMI_COLORSPACE_RGB;
...@@ -1547,15 +1568,18 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, ...@@ -1547,15 +1568,18 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
struct drm_hdmi_info *hdmi_info = &hdmi->connector.display_info.hdmi; struct drm_hdmi_info *hdmi_info = &hdmi->connector.display_info.hdmi;
struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode; struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len; int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
unsigned int vdisplay; unsigned int vdisplay, hdisplay;
vmode->mpixelclock = mode->clock * 1000; vmode->mtmdsclock = vmode->mpixelclock = mode->clock * 1000;
dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock); dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
vmode->mtmdsclock /= 2;
/* Set up HDMI_FC_INVIDCONF */ /* Set up HDMI_FC_INVIDCONF */
inv_val = (hdmi->hdmi_data.hdcp_enable || inv_val = (hdmi->hdmi_data.hdcp_enable ||
vmode->mpixelclock > HDMI14_MAX_TMDSCLK || vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
hdmi_info->scdc.scrambling.low_rates ? hdmi_info->scdc.scrambling.low_rates ?
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE : HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE); HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
...@@ -1589,6 +1613,22 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, ...@@ -1589,6 +1613,22 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF); hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
hdisplay = mode->hdisplay;
hblank = mode->htotal - mode->hdisplay;
h_de_hs = mode->hsync_start - mode->hdisplay;
hsync_len = mode->hsync_end - mode->hsync_start;
/*
* When we're setting a YCbCr420 mode, we need
* to adjust the horizontal timing to suit.
*/
if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) {
hdisplay /= 2;
hblank /= 2;
h_de_hs /= 2;
hsync_len /= 2;
}
vdisplay = mode->vdisplay; vdisplay = mode->vdisplay;
vblank = mode->vtotal - mode->vdisplay; vblank = mode->vtotal - mode->vdisplay;
v_de_vs = mode->vsync_start - mode->vdisplay; v_de_vs = mode->vsync_start - mode->vdisplay;
...@@ -1607,7 +1647,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, ...@@ -1607,7 +1647,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
/* Scrambling Control */ /* Scrambling Control */
if (hdmi_info->scdc.supported) { if (hdmi_info->scdc.supported) {
if (vmode->mpixelclock > HDMI14_MAX_TMDSCLK || if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
hdmi_info->scdc.scrambling.low_rates) { hdmi_info->scdc.scrambling.low_rates) {
/* /*
* HDMI2.0 Specifies the following procedure: * HDMI2.0 Specifies the following procedure:
...@@ -1645,15 +1685,14 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, ...@@ -1645,15 +1685,14 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
} }
/* Set up horizontal active pixel width */ /* Set up horizontal active pixel width */
hdmi_writeb(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1); hdmi_writeb(hdmi, hdisplay >> 8, HDMI_FC_INHACTV1);
hdmi_writeb(hdmi, mode->hdisplay, HDMI_FC_INHACTV0); hdmi_writeb(hdmi, hdisplay, HDMI_FC_INHACTV0);
/* Set up vertical active lines */ /* Set up vertical active lines */
hdmi_writeb(hdmi, vdisplay >> 8, HDMI_FC_INVACTV1); hdmi_writeb(hdmi, vdisplay >> 8, HDMI_FC_INVACTV1);
hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0); hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0);
/* Set up horizontal blanking pixel region width */ /* Set up horizontal blanking pixel region width */
hblank = mode->htotal - mode->hdisplay;
hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1); hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0); hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0);
...@@ -1661,7 +1700,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, ...@@ -1661,7 +1700,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK); hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK);
/* Set up HSYNC active edge delay width (in pixel clks) */ /* Set up HSYNC active edge delay width (in pixel clks) */
h_de_hs = mode->hsync_start - mode->hdisplay;
hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1); hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1);
hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0); hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
...@@ -1669,7 +1707,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, ...@@ -1669,7 +1707,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY); hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
/* Set up HSYNC active pulse width (in pixel clks) */ /* Set up HSYNC active pulse width (in pixel clks) */
hsync_len = mode->hsync_end - mode->hsync_start;
hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1); hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0); hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment