From 28850f6ab747feaed7f5976b0d89d3b7e99c954d Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 1 Feb 2019 12:07:49 +0000 Subject: [PATCH] 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 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 Signed-off-by: Neil Armstrong Tested-by: Heiko Stuebner Reviewed-by: Andrzej Hajda Signed-off-by: Andrzej Hajda Link: https://patchwork.freedesktop.org/patch/msgid/1549022873-40549-5-git-send-email-narmstrong@baylibre.com (cherry picked from commit ba9877e2361c46cae3841181aea61e55fc2309b9) --- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 27 ++++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index a7deebc55850..9183961003e2 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -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);