UPSTREAM: 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 these 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

Change-Id: I41733e40ac1a528c8a0e0dadb79c4e3b31ea5f6b
Cc: Zheng Yang <zhengyang@rock-chips.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Tested-by: Heiko Stuebner <heiko@sntech.de>
Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Link: https://patchwork.freedesktop.org/patch/msgid/1549022873-40549-5-git-send-email-narmstrong@baylibre.com
(cherry picked from commit ba9877e236)
This commit is contained in:
Neil Armstrong
2019-02-01 12:07:49 +00:00
committed by Algea Cao
parent 50f1e72b05
commit 28850f6ab7

View File

@@ -154,6 +154,7 @@ struct hdmi_vmode {
unsigned int mpixelclock;
unsigned int mpixelrepetitioninput;
unsigned int mpixelrepetitionoutput;
unsigned int mtmdsclock;
};
struct hdmi_data_info {
@@ -656,7 +657,7 @@ static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi)
static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
{
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);
mutex_unlock(&hdmi->audio_mutex);
}
@@ -665,7 +666,7 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
{
mutex_lock(&hdmi->audio_mutex);
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);
mutex_unlock(&hdmi->audio_mutex);
}
@@ -1164,7 +1165,7 @@ EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_write);
*/
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 */
if (hdmi->connector.display_info.hdmi.scdc.supported) {
@@ -1330,6 +1331,8 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
pdata->mpll_cfg_420)
mpll_config = pdata->mpll_cfg_420;
/* TOFIX Will need 420 specific PHY configuration tables */
/* PLL/MPLL Cfg - always match on final entry */
for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
if (mpixelclock <= mpll_config->mpixelclock)
@@ -1377,6 +1380,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
unsigned long mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
int ret;
dw_hdmi_phy_power_off(hdmi);
@@ -1405,7 +1409,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
}
/* Wait for resuming transmission of TMDS clock and data */
if (mpixelclock > HDMI14_MAX_TMDSCLK)
if (mtmdsclock > HDMI14_MAX_TMDSCLK)
msleep(100);
return dw_hdmi_phy_power_on(hdmi);
@@ -1680,16 +1684,20 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
struct drm_hdmi_info *hdmi_info = &hdmi->connector.display_info.hdmi;
struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
unsigned int hdisplay, vdisplay;
unsigned int vdisplay, hdisplay;
vmode->mtmdsclock = vmode->mpixelclock = mode->crtc_clock * 1000;
vmode->mpixelclock = mode->crtc_clock * 1000;
if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
vmode->mpixelclock /= 2;
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 */
inv_val = (hdmi->hdmi_data.hdcp_enable ||
vmode->mpixelclock > HDMI14_MAX_TMDSCLK ||
vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
hdmi_info->scdc.scrambling.low_rates ?
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
@@ -1757,7 +1765,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
/* Scrambling Control */
if (hdmi_info->scdc.supported) {
if (vmode->mpixelclock > HDMI14_MAX_TMDSCLK ||
if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
hdmi_info->scdc.scrambling.low_rates) {
/*
* HDMI2.0 Specifies the following procedure:
@@ -1803,7 +1811,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0);
/* Set up horizontal blanking pixel region width */
hblank = mode->htotal - mode->hdisplay;
hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0);
@@ -1811,7 +1818,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK);
/* 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, HDMI_FC_HSYNCINDELAY0);
@@ -1819,7 +1825,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
/* 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, HDMI_FC_HSYNCINWIDTH0);