aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeil Armstrong <narmstrong@baylibre.com>2019-02-01 07:07:49 -0500
committerAndrzej Hajda <a.hajda@samsung.com>2019-02-01 07:15:10 -0500
commitba9877e2361c46cae3841181aea61e55fc2309b9 (patch)
treec83930f4412c644a89413a136af02fc27ad23039
parent74f6d1e1cbfd8ba399e4fb525954a5c5e8b117df (diff)
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: 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
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.c65
1 files changed, 51 insertions, 14 deletions
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 6d5a2e98eeee..a63e5f0dae56 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -99,6 +99,7 @@ struct hdmi_vmode {
99 unsigned int mpixelclock; 99 unsigned int mpixelclock;
100 unsigned int mpixelrepetitioninput; 100 unsigned int mpixelrepetitioninput;
101 unsigned int mpixelrepetitionoutput; 101 unsigned int mpixelrepetitionoutput;
102 unsigned int mtmdsclock;
102}; 103};
103 104
104struct hdmi_data_info { 105struct hdmi_data_info {
@@ -543,7 +544,7 @@ static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi)
543static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi) 544static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
544{ 545{
545 mutex_lock(&hdmi->audio_mutex); 546 mutex_lock(&hdmi->audio_mutex);
546 hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock, 547 hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock,
547 hdmi->sample_rate); 548 hdmi->sample_rate);
548 mutex_unlock(&hdmi->audio_mutex); 549 mutex_unlock(&hdmi->audio_mutex);
549} 550}
@@ -552,7 +553,7 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
552{ 553{
553 mutex_lock(&hdmi->audio_mutex); 554 mutex_lock(&hdmi->audio_mutex);
554 hdmi->sample_rate = rate; 555 hdmi->sample_rate = rate;
555 hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock, 556 hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock,
556 hdmi->sample_rate); 557 hdmi->sample_rate);
557 mutex_unlock(&hdmi->audio_mutex); 558 mutex_unlock(&hdmi->audio_mutex);
558} 559}
@@ -653,6 +654,20 @@ static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format)
653 } 654 }
654} 655}
655 656
657static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format)
658{
659 switch (bus_format) {
660 case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
661 case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
662 case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
663 case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
664 return true;
665
666 default:
667 return false;
668 }
669}
670
656static int hdmi_bus_fmt_color_depth(unsigned int bus_format) 671static int hdmi_bus_fmt_color_depth(unsigned int bus_format)
657{ 672{
658 switch (bus_format) { 673 switch (bus_format) {
@@ -882,7 +897,8 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
882 u8 val, vp_conf; 897 u8 val, vp_conf;
883 898
884 if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) || 899 if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) ||
885 hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) { 900 hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) ||
901 hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) {
886 switch (hdmi_bus_fmt_color_depth( 902 switch (hdmi_bus_fmt_color_depth(
887 hdmi->hdmi_data.enc_out_bus_format)) { 903 hdmi->hdmi_data.enc_out_bus_format)) {
888 case 8: 904 case 8:
@@ -1036,7 +1052,7 @@ EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_write);
1036 */ 1052 */
1037void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi) 1053void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi)
1038{ 1054{
1039 unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mpixelclock; 1055 unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
1040 1056
1041 /* Control for TMDS Bit Period/TMDS Clock-Period Ratio */ 1057 /* Control for TMDS Bit Period/TMDS Clock-Period Ratio */
1042 if (hdmi->connector.display_info.hdmi.scdc.supported) { 1058 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 const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr; 1214 const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
1199 const struct dw_hdmi_phy_config *phy_config = pdata->phy_config; 1215 const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
1200 1216
1217 /* TOFIX Will need 420 specific PHY configuration tables */
1218
1201 /* PLL/MPLL Cfg - always match on final entry */ 1219 /* PLL/MPLL Cfg - always match on final entry */
1202 for (; mpll_config->mpixelclock != ~0UL; mpll_config++) 1220 for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
1203 if (mpixelclock <= mpll_config->mpixelclock) 1221 if (mpixelclock <= mpll_config->mpixelclock)
@@ -1245,6 +1263,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
1245 const struct dw_hdmi_phy_data *phy = hdmi->phy.data; 1263 const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
1246 const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; 1264 const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
1247 unsigned long mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock; 1265 unsigned long mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
1266 unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
1248 int ret; 1267 int ret;
1249 1268
1250 dw_hdmi_phy_power_off(hdmi); 1269 dw_hdmi_phy_power_off(hdmi);
@@ -1273,7 +1292,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
1273 } 1292 }
1274 1293
1275 /* Wait for resuming transmission of TMDS clock and data */ 1294 /* Wait for resuming transmission of TMDS clock and data */
1276 if (mpixelclock > HDMI14_MAX_TMDSCLK) 1295 if (mtmdsclock > HDMI14_MAX_TMDSCLK)
1277 msleep(100); 1296 msleep(100);
1278 1297
1279 return dw_hdmi_phy_power_on(hdmi); 1298 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 frame.colorspace = HDMI_COLORSPACE_YUV444; 1409 frame.colorspace = HDMI_COLORSPACE_YUV444;
1391 else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) 1410 else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
1392 frame.colorspace = HDMI_COLORSPACE_YUV422; 1411 frame.colorspace = HDMI_COLORSPACE_YUV422;
1412 else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
1413 frame.colorspace = HDMI_COLORSPACE_YUV420;
1393 else 1414 else
1394 frame.colorspace = HDMI_COLORSPACE_RGB; 1415 frame.colorspace = HDMI_COLORSPACE_RGB;
1395 1416
@@ -1547,15 +1568,18 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
1547 struct drm_hdmi_info *hdmi_info = &hdmi->connector.display_info.hdmi; 1568 struct drm_hdmi_info *hdmi_info = &hdmi->connector.display_info.hdmi;
1548 struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode; 1569 struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
1549 int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len; 1570 int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
1550 unsigned int vdisplay; 1571 unsigned int vdisplay, hdisplay;
1551 1572
1552 vmode->mpixelclock = mode->clock * 1000; 1573 vmode->mtmdsclock = vmode->mpixelclock = mode->clock * 1000;
1553 1574
1554 dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock); 1575 dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
1555 1576
1577 if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
1578 vmode->mtmdsclock /= 2;
1579
1556 /* Set up HDMI_FC_INVIDCONF */ 1580 /* Set up HDMI_FC_INVIDCONF */
1557 inv_val = (hdmi->hdmi_data.hdcp_enable || 1581 inv_val = (hdmi->hdmi_data.hdcp_enable ||
1558 vmode->mpixelclock > HDMI14_MAX_TMDSCLK || 1582 vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
1559 hdmi_info->scdc.scrambling.low_rates ? 1583 hdmi_info->scdc.scrambling.low_rates ?
1560 HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE : 1584 HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
1561 HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE); 1585 HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
@@ -1589,6 +1613,22 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
1589 1613
1590 hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF); 1614 hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
1591 1615
1616 hdisplay = mode->hdisplay;
1617 hblank = mode->htotal - mode->hdisplay;
1618 h_de_hs = mode->hsync_start - mode->hdisplay;
1619 hsync_len = mode->hsync_end - mode->hsync_start;
1620
1621 /*
1622 * When we're setting a YCbCr420 mode, we need
1623 * to adjust the horizontal timing to suit.
1624 */
1625 if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) {
1626 hdisplay /= 2;
1627 hblank /= 2;
1628 h_de_hs /= 2;
1629 hsync_len /= 2;
1630 }
1631
1592 vdisplay = mode->vdisplay; 1632 vdisplay = mode->vdisplay;
1593 vblank = mode->vtotal - mode->vdisplay; 1633 vblank = mode->vtotal - mode->vdisplay;
1594 v_de_vs = mode->vsync_start - mode->vdisplay; 1634 v_de_vs = mode->vsync_start - mode->vdisplay;
@@ -1607,7 +1647,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
1607 1647
1608 /* Scrambling Control */ 1648 /* Scrambling Control */
1609 if (hdmi_info->scdc.supported) { 1649 if (hdmi_info->scdc.supported) {
1610 if (vmode->mpixelclock > HDMI14_MAX_TMDSCLK || 1650 if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
1611 hdmi_info->scdc.scrambling.low_rates) { 1651 hdmi_info->scdc.scrambling.low_rates) {
1612 /* 1652 /*
1613 * HDMI2.0 Specifies the following procedure: 1653 * HDMI2.0 Specifies the following procedure:
@@ -1645,15 +1685,14 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
1645 } 1685 }
1646 1686
1647 /* Set up horizontal active pixel width */ 1687 /* Set up horizontal active pixel width */
1648 hdmi_writeb(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1); 1688 hdmi_writeb(hdmi, hdisplay >> 8, HDMI_FC_INHACTV1);
1649 hdmi_writeb(hdmi, mode->hdisplay, HDMI_FC_INHACTV0); 1689 hdmi_writeb(hdmi, hdisplay, HDMI_FC_INHACTV0);
1650 1690
1651 /* Set up vertical active lines */ 1691 /* Set up vertical active lines */
1652 hdmi_writeb(hdmi, vdisplay >> 8, HDMI_FC_INVACTV1); 1692 hdmi_writeb(hdmi, vdisplay >> 8, HDMI_FC_INVACTV1);
1653 hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0); 1693 hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0);
1654 1694
1655 /* Set up horizontal blanking pixel region width */ 1695 /* Set up horizontal blanking pixel region width */
1656 hblank = mode->htotal - mode->hdisplay;
1657 hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1); 1696 hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
1658 hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0); 1697 hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0);
1659 1698
@@ -1661,7 +1700,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
1661 hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK); 1700 hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK);
1662 1701
1663 /* Set up HSYNC active edge delay width (in pixel clks) */ 1702 /* Set up HSYNC active edge delay width (in pixel clks) */
1664 h_de_hs = mode->hsync_start - mode->hdisplay;
1665 hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1); 1703 hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1);
1666 hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0); 1704 hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
1667 1705
@@ -1669,7 +1707,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
1669 hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY); 1707 hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
1670 1708
1671 /* Set up HSYNC active pulse width (in pixel clks) */ 1709 /* Set up HSYNC active pulse width (in pixel clks) */
1672 hsync_len = mode->hsync_end - mode->hsync_start;
1673 hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1); 1710 hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
1674 hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0); 1711 hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
1675 1712