Skip to content

Commit 264fce6

Browse files
superna9999Andrzej Hajda
authored andcommitted
drm/bridge: dw-hdmi: Add SCDC and TMDS Scrambling support
Add support for SCDC Setup for TMDS Clock > 3.4GHz and enable TMDS Scrambling when supported or mandatory. This patch also adds an helper to setup the control bit to support the high TMDS Bit Period/TMDS Clock-Period Ratio as required with TMDS Clock > 3.4GHz for HDMI2.0 3840x2160@60/50 modes. These changes were based on work done by Huicong Xu <xhc@rock-chips.com> and Nickey Yang <nickey.yang@rock-chips.com> to support HDMI2.0 modes on the Rockchip 4.4 BSP kernel at [1] [1] https://github.com/rockchip-linux/kernel/tree/release-4.4 Cc: Nickey Yang <nickey.yang@rock-chips.com> Cc: Huicong Xu <xhc@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-2-git-send-email-narmstrong@baylibre.com
1 parent d60ea31 commit 264fce6

File tree

3 files changed

+85
-2
lines changed

3 files changed

+85
-2
lines changed

drivers/gpu/drm/bridge/synopsys/dw-hdmi.c

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <drm/drm_atomic_helper.h>
2828
#include <drm/drm_edid.h>
2929
#include <drm/drm_encoder_slave.h>
30+
#include <drm/drm_scdc_helper.h>
3031
#include <drm/drm_probe_helper.h>
3132
#include <drm/bridge/dw_hdmi.h>
3233

@@ -43,6 +44,11 @@
4344

4445
#define HDMI_EDID_LEN 512
4546

47+
/* DW-HDMI Controller >= 0x200a are at least compliant with SCDC version 1 */
48+
#define SCDC_MIN_SOURCE_VERSION 0x1
49+
50+
#define HDMI14_MAX_TMDSCLK 340000000
51+
4652
enum hdmi_datamap {
4753
RGB444_8B = 0x01,
4854
RGB444_10B = 0x03,
@@ -1015,6 +1021,33 @@ void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
10151021
}
10161022
EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_write);
10171023

1024+
/*
1025+
* HDMI2.0 Specifies the following procedure for High TMDS Bit Rates:
1026+
* - The Source shall suspend transmission of the TMDS clock and data
1027+
* - The Source shall write to the TMDS_Bit_Clock_Ratio bit to change it
1028+
* from a 0 to a 1 or from a 1 to a 0
1029+
* - The Source shall allow a minimum of 1 ms and a maximum of 100 ms from
1030+
* the time the TMDS_Bit_Clock_Ratio bit is written until resuming
1031+
* transmission of TMDS clock and data
1032+
*
1033+
* To respect the 100ms maximum delay, the dw_hdmi_set_high_tmds_clock_ratio()
1034+
* helper should called right before enabling the TMDS Clock and Data in
1035+
* the PHY configuration callback.
1036+
*/
1037+
void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi)
1038+
{
1039+
unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mpixelclock;
1040+
1041+
/* Control for TMDS Bit Period/TMDS Clock-Period Ratio */
1042+
if (hdmi->connector.display_info.hdmi.scdc.supported) {
1043+
if (mtmdsclock > HDMI14_MAX_TMDSCLK)
1044+
drm_scdc_set_high_tmds_clock_ratio(hdmi->ddc, 1);
1045+
else
1046+
drm_scdc_set_high_tmds_clock_ratio(hdmi->ddc, 0);
1047+
}
1048+
}
1049+
EXPORT_SYMBOL_GPL(dw_hdmi_set_high_tmds_clock_ratio);
1050+
10181051
static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable)
10191052
{
10201053
hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0,
@@ -1216,6 +1249,8 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
12161249

12171250
dw_hdmi_phy_power_off(hdmi);
12181251

1252+
dw_hdmi_set_high_tmds_clock_ratio(hdmi);
1253+
12191254
/* Leave low power consumption mode by asserting SVSRET. */
12201255
if (phy->has_svsret)
12211256
dw_hdmi_phy_enable_svsret(hdmi, 1);
@@ -1237,6 +1272,10 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
12371272
return ret;
12381273
}
12391274

1275+
/* Wait for resuming transmission of TMDS clock and data */
1276+
if (mpixelclock > HDMI14_MAX_TMDSCLK)
1277+
msleep(100);
1278+
12401279
return dw_hdmi_phy_power_on(hdmi);
12411280
}
12421281

@@ -1504,7 +1543,8 @@ static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi,
15041543
static void hdmi_av_composer(struct dw_hdmi *hdmi,
15051544
const struct drm_display_mode *mode)
15061545
{
1507-
u8 inv_val;
1546+
u8 inv_val, bytes;
1547+
struct drm_hdmi_info *hdmi_info = &hdmi->connector.display_info.hdmi;
15081548
struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
15091549
int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
15101550
unsigned int vdisplay;
@@ -1514,7 +1554,9 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
15141554
dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
15151555

15161556
/* Set up HDMI_FC_INVIDCONF */
1517-
inv_val = (hdmi->hdmi_data.hdcp_enable ?
1557+
inv_val = (hdmi->hdmi_data.hdcp_enable ||
1558+
vmode->mpixelclock > HDMI14_MAX_TMDSCLK ||
1559+
hdmi_info->scdc.scrambling.low_rates ?
15181560
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
15191561
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
15201562

@@ -1563,6 +1605,45 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
15631605
vsync_len /= 2;
15641606
}
15651607

1608+
/* Scrambling Control */
1609+
if (hdmi_info->scdc.supported) {
1610+
if (vmode->mpixelclock > HDMI14_MAX_TMDSCLK ||
1611+
hdmi_info->scdc.scrambling.low_rates) {
1612+
/*
1613+
* HDMI2.0 Specifies the following procedure:
1614+
* After the Source Device has determined that
1615+
* SCDC_Present is set (=1), the Source Device should
1616+
* write the accurate Version of the Source Device
1617+
* to the Source Version field in the SCDCS.
1618+
* Source Devices compliant shall set the
1619+
* Source Version = 1.
1620+
*/
1621+
drm_scdc_readb(&hdmi->i2c->adap, SCDC_SINK_VERSION,
1622+
&bytes);
1623+
drm_scdc_writeb(&hdmi->i2c->adap, SCDC_SOURCE_VERSION,
1624+
min_t(u8, bytes, SCDC_MIN_SOURCE_VERSION));
1625+
1626+
/* Enabled Scrambling in the Sink */
1627+
drm_scdc_set_scrambling(&hdmi->i2c->adap, 1);
1628+
1629+
/*
1630+
* To activate the scrambler feature, you must ensure
1631+
* that the quasi-static configuration bit
1632+
* fc_invidconf.HDCP_keepout is set at configuration
1633+
* time, before the required mc_swrstzreq.tmdsswrst_req
1634+
* reset request is issued.
1635+
*/
1636+
hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ,
1637+
HDMI_MC_SWRSTZ);
1638+
hdmi_writeb(hdmi, 1, HDMI_FC_SCRAMBLER_CTRL);
1639+
} else {
1640+
hdmi_writeb(hdmi, 0, HDMI_FC_SCRAMBLER_CTRL);
1641+
hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ,
1642+
HDMI_MC_SWRSTZ);
1643+
drm_scdc_set_scrambling(&hdmi->i2c->adap, 0);
1644+
}
1645+
}
1646+
15661647
/* Set up horizontal active pixel width */
15671648
hdmi_writeb(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1);
15681649
hdmi_writeb(hdmi, mode->hdisplay, HDMI_FC_INHACTV0);

drivers/gpu/drm/bridge/synopsys/dw-hdmi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@
255255
#define HDMI_FC_MASK2 0x10DA
256256
#define HDMI_FC_POL2 0x10DB
257257
#define HDMI_FC_PRCONF 0x10E0
258+
#define HDMI_FC_SCRAMBLER_CTRL 0x10E1
258259

259260
#define HDMI_FC_GMD_STAT 0x1100
260261
#define HDMI_FC_GMD_EN 0x1101

include/drm/bridge/dw_hdmi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense);
159159
void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate);
160160
void dw_hdmi_audio_enable(struct dw_hdmi *hdmi);
161161
void dw_hdmi_audio_disable(struct dw_hdmi *hdmi);
162+
void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi);
162163

163164
/* PHY configuration */
164165
void dw_hdmi_phy_i2c_set_addr(struct dw_hdmi *hdmi, u8 address);

0 commit comments

Comments
 (0)