diff options
author | Neil Armstrong <narmstrong@baylibre.com> | 2019-02-01 07:07:49 -0500 |
---|---|---|
committer | Andrzej Hajda <a.hajda@samsung.com> | 2019-02-01 07:15:10 -0500 |
commit | ba9877e2361c46cae3841181aea61e55fc2309b9 (patch) | |
tree | c83930f4412c644a89413a136af02fc27ad23039 | |
parent | 74f6d1e1cbfd8ba399e4fb525954a5c5e8b117df (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.c | 65 |
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 | ||
104 | struct hdmi_data_info { | 105 | struct hdmi_data_info { |
@@ -543,7 +544,7 @@ static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi) | |||
543 | static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi) | 544 | static 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 | ||
657 | static 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 | |||
656 | static int hdmi_bus_fmt_color_depth(unsigned int bus_format) | 671 | static 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 | */ |
1037 | void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi) | 1053 | void 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 | ||