diff options
| author | Abhinav Kumar <abhinavk@codeaurora.org> | 2017-03-13 13:40:30 -0700 |
|---|---|---|
| committer | Abhinav Kumar <abhinavk@codeaurora.org> | 2017-04-10 23:43:16 -0700 |
| commit | 7a5cee889a02065c1dd3df1b7795cc2e4d726be9 (patch) | |
| tree | 7250f8d4fcfce83a3fdb1208eb4afc7f32951f4d | |
| parent | 31604f1c6f99f726111c21f28fe0c84120a9a3eb (diff) | |
drm/msm: add support for enabling scrambling feature
To support 4k@60fps resolution through HDMI, enable
scrambler feature from HDMI controller and communicate
it with sink device through DDC.
Change-Id: I17750db358df58499303ef9d735bf3301b02a7c1
Signed-off-by: Abhinav Kumar <abhinavk@codeaurora.org>
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c | 255 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h | 77 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c | 262 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi/hdmi.xml.h | 14 |
4 files changed, 608 insertions, 0 deletions
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index 347b78886b24..6a8b3ccd6478 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -32,6 +32,22 @@ static DEFINE_MUTEX(sde_hdmi_list_lock); static LIST_HEAD(sde_hdmi_list); +/* HDMI SCDC register offsets */ +#define HDMI_SCDC_UPDATE_0 0x10 +#define HDMI_SCDC_UPDATE_1 0x11 +#define HDMI_SCDC_TMDS_CONFIG 0x20 +#define HDMI_SCDC_SCRAMBLER_STATUS 0x21 +#define HDMI_SCDC_CONFIG_0 0x30 +#define HDMI_SCDC_STATUS_FLAGS_0 0x40 +#define HDMI_SCDC_STATUS_FLAGS_1 0x41 +#define HDMI_SCDC_ERR_DET_0_L 0x50 +#define HDMI_SCDC_ERR_DET_0_H 0x51 +#define HDMI_SCDC_ERR_DET_1_L 0x52 +#define HDMI_SCDC_ERR_DET_1_H 0x53 +#define HDMI_SCDC_ERR_DET_2_L 0x54 +#define HDMI_SCDC_ERR_DET_2_H 0x55 +#define HDMI_SCDC_ERR_DET_CHECKSUM 0x56 + static const struct of_device_id sde_hdmi_dt_match[] = { {.compatible = "qcom,hdmi-display"}, {} @@ -638,6 +654,245 @@ void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on) power_on ? "Enable" : "Disable", ctrl); } +int sde_hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset, + u8 *data, u16 data_len) +{ + int rc; + int retry = 5; + struct i2c_msg msgs[] = { + { + .addr = addr >> 1, + .flags = 0, + .len = 1, + .buf = &offset, + }, { + .addr = addr >> 1, + .flags = I2C_M_RD, + .len = data_len, + .buf = data, + } + }; + + DRM_DEBUG("Start DDC read"); + retry: + rc = i2c_transfer(hdmi->i2c, msgs, 2); + + retry--; + if (rc == 2) + rc = 0; + else if (retry > 0) + goto retry; + else + rc = -EIO; + + DRM_DEBUG("End DDC read %d", rc); + + return rc; +} + +#define DDC_WRITE_MAX_BYTE_NUM 32 + +int sde_hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset, + u8 *data, u16 data_len) +{ + int rc; + int retry = 10; + u8 buf[DDC_WRITE_MAX_BYTE_NUM]; + struct i2c_msg msgs[] = { + { + .addr = addr >> 1, + .flags = 0, + .len = 1, + } + }; + + DRM_DEBUG("Start DDC write"); + if (data_len > (DDC_WRITE_MAX_BYTE_NUM - 1)) { + SDE_ERROR("%s: write size too big\n", __func__); + return -ERANGE; + } + + buf[0] = offset; + memcpy(&buf[1], data, data_len); + msgs[0].buf = buf; + msgs[0].len = data_len + 1; + retry: + rc = i2c_transfer(hdmi->i2c, msgs, 1); + + retry--; + if (rc == 1) + rc = 0; + else if (retry > 0) + goto retry; + else + rc = -EIO; + + DRM_DEBUG("End DDC write %d", rc); + + return rc; +} + +int sde_hdmi_scdc_read(struct hdmi *hdmi, u32 data_type, u32 *val) +{ + int rc = 0; + u8 data_buf[2] = {0}; + u16 dev_addr, data_len; + u8 offset; + + if (!hdmi || !hdmi->i2c || !val) { + SDE_ERROR("Bad Parameters\n"); + return -EINVAL; + } + + if (data_type >= HDMI_TX_SCDC_MAX) { + SDE_ERROR("Unsupported data type\n"); + return -EINVAL; + } + + dev_addr = 0xA8; + + switch (data_type) { + case HDMI_TX_SCDC_SCRAMBLING_STATUS: + data_len = 1; + offset = HDMI_SCDC_SCRAMBLER_STATUS; + break; + case HDMI_TX_SCDC_SCRAMBLING_ENABLE: + case HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE: + data_len = 1; + offset = HDMI_SCDC_TMDS_CONFIG; + break; + case HDMI_TX_SCDC_CLOCK_DET_STATUS: + case HDMI_TX_SCDC_CH0_LOCK_STATUS: + case HDMI_TX_SCDC_CH1_LOCK_STATUS: + case HDMI_TX_SCDC_CH2_LOCK_STATUS: + data_len = 1; + offset = HDMI_SCDC_STATUS_FLAGS_0; + break; + case HDMI_TX_SCDC_CH0_ERROR_COUNT: + data_len = 2; + offset = HDMI_SCDC_ERR_DET_0_L; + break; + case HDMI_TX_SCDC_CH1_ERROR_COUNT: + data_len = 2; + offset = HDMI_SCDC_ERR_DET_1_L; + break; + case HDMI_TX_SCDC_CH2_ERROR_COUNT: + data_len = 2; + offset = HDMI_SCDC_ERR_DET_2_L; + break; + case HDMI_TX_SCDC_READ_ENABLE: + data_len = 1; + offset = HDMI_SCDC_CONFIG_0; + break; + default: + break; + } + + rc = sde_hdmi_ddc_read(hdmi, dev_addr, offset, data_buf, data_len); + if (rc) { + SDE_ERROR("DDC Read failed for %d\n", data_type); + return rc; + } + + switch (data_type) { + case HDMI_TX_SCDC_SCRAMBLING_STATUS: + *val = (data_buf[0] & BIT(0)) ? 1 : 0; + break; + case HDMI_TX_SCDC_SCRAMBLING_ENABLE: + *val = (data_buf[0] & BIT(0)) ? 1 : 0; + break; + case HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE: + *val = (data_buf[0] & BIT(1)) ? 1 : 0; + break; + case HDMI_TX_SCDC_CLOCK_DET_STATUS: + *val = (data_buf[0] & BIT(0)) ? 1 : 0; + break; + case HDMI_TX_SCDC_CH0_LOCK_STATUS: + *val = (data_buf[0] & BIT(1)) ? 1 : 0; + break; + case HDMI_TX_SCDC_CH1_LOCK_STATUS: + *val = (data_buf[0] & BIT(2)) ? 1 : 0; + break; + case HDMI_TX_SCDC_CH2_LOCK_STATUS: + *val = (data_buf[0] & BIT(3)) ? 1 : 0; + break; + case HDMI_TX_SCDC_CH0_ERROR_COUNT: + case HDMI_TX_SCDC_CH1_ERROR_COUNT: + case HDMI_TX_SCDC_CH2_ERROR_COUNT: + if (data_buf[1] & BIT(7)) + *val = (data_buf[0] | ((data_buf[1] & 0x7F) << 8)); + else + *val = 0; + break; + case HDMI_TX_SCDC_READ_ENABLE: + *val = (data_buf[0] & BIT(0)) ? 1 : 0; + break; + default: + break; + } + + return 0; +} + +int sde_hdmi_scdc_write(struct hdmi *hdmi, u32 data_type, u32 val) +{ + int rc = 0; + u8 data_buf[2] = {0}; + u8 read_val = 0; + u16 dev_addr, data_len; + u8 offset; + + if (!hdmi || !hdmi->i2c) { + SDE_ERROR("Bad Parameters\n"); + return -EINVAL; + } + + if (data_type >= HDMI_TX_SCDC_MAX) { + SDE_ERROR("Unsupported data type\n"); + return -EINVAL; + } + + dev_addr = 0xA8; + + switch (data_type) { + case HDMI_TX_SCDC_SCRAMBLING_ENABLE: + case HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE: + dev_addr = 0xA8; + data_len = 1; + offset = HDMI_SCDC_TMDS_CONFIG; + rc = sde_hdmi_ddc_read(hdmi, dev_addr, offset, &read_val, + data_len); + if (rc) { + SDE_ERROR("scdc read failed\n"); + return rc; + } + if (data_type == HDMI_TX_SCDC_SCRAMBLING_ENABLE) { + data_buf[0] = ((((u8)(read_val & 0xFF)) & (~BIT(0))) | + ((u8)(val & BIT(0)))); + } else { + data_buf[0] = ((((u8)(read_val & 0xFF)) & (~BIT(1))) | + (((u8)(val & BIT(0))) << 1)); + } + break; + case HDMI_TX_SCDC_READ_ENABLE: + data_len = 1; + offset = HDMI_SCDC_CONFIG_0; + data_buf[0] = (u8)(val & 0x1); + break; + default: + SDE_ERROR("Cannot write to read only reg (%d)\n", + data_type); + return -EINVAL; + } + + rc = sde_hdmi_ddc_write(hdmi, dev_addr, offset, data_buf, data_len); + if (rc) { + SDE_ERROR("DDC Read failed for %d\n", data_type); + return rc; + } + return 0; +} + int sde_hdmi_get_info(struct msm_display_info *info, void *display) { diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h index 869d1bebf9db..c2b685111c65 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h @@ -116,6 +116,37 @@ struct sde_hdmi { struct dentry *root; }; +/** + * hdmi_tx_scdc_access_type() - hdmi 2.0 DDC functionalities. + */ +enum hdmi_tx_scdc_access_type { + HDMI_TX_SCDC_SCRAMBLING_STATUS, + HDMI_TX_SCDC_SCRAMBLING_ENABLE, + HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE, + HDMI_TX_SCDC_CLOCK_DET_STATUS, + HDMI_TX_SCDC_CH0_LOCK_STATUS, + HDMI_TX_SCDC_CH1_LOCK_STATUS, + HDMI_TX_SCDC_CH2_LOCK_STATUS, + HDMI_TX_SCDC_CH0_ERROR_COUNT, + HDMI_TX_SCDC_CH1_ERROR_COUNT, + HDMI_TX_SCDC_CH2_ERROR_COUNT, + HDMI_TX_SCDC_READ_ENABLE, + HDMI_TX_SCDC_MAX, +}; + +/** + * hdmi_tx_ddc_timer_type() - hdmi DDC timer functionalities. + */ +enum hdmi_tx_ddc_timer_type { + HDMI_TX_DDC_TIMER_HDCP2P2_RD_MSG, + HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS, + HDMI_TX_DDC_TIMER_UPDATE_FLAGS, + HDMI_TX_DDC_TIMER_STATUS_FLAGS, + HDMI_TX_DDC_TIMER_CED, + HDMI_TX_DDC_TIMER_MAX, + }; + + #ifdef CONFIG_DRM_SDE_HDMI /** * sde_hdmi_get_num_of_displays() - returns number of display devices @@ -259,6 +290,52 @@ struct drm_bridge *sde_hdmi_bridge_init(struct hdmi *hdmi); void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on); /** + * sde_hdmi_ddc_read() - common hdmi ddc read API. + * @hdmi: Handle to the hdmi. + * @addr: Command address. + * @offset: Command offset. + * @data: Data buffer for read back. + * @data_len: Data buffer length. + * + * Return: error code. + */ +int sde_hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset, + u8 *data, u16 data_len); + +/** + * sde_hdmi_ddc_write() - common hdmi ddc write API. + * @hdmi: Handle to the hdmi. + * @addr: Command address. + * @offset: Command offset. + * @data: Data buffer for write. + * @data_len: Data buffer length. + * + * Return: error code. + */ +int sde_hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset, + u8 *data, u16 data_len); + +/** + * sde_hdmi_scdc_read() - hdmi 2.0 ddc read API. + * @hdmi: Handle to the hdmi. + * @data_type: DDC data type, refer to enum hdmi_tx_scdc_access_type. + * @val: Read back value. + * + * Return: error code. + */ +int sde_hdmi_scdc_read(struct hdmi *hdmi, u32 data_type, u32 *val); + +/** + * sde_hdmi_scdc_write() - hdmi 2.0 ddc write API. + * @hdmi: Handle to the hdmi. + * @data_type: DDC data type, refer to enum hdmi_tx_scdc_access_type. + * @val: Value write through DDC. + * + * Return: error code. + */ +int sde_hdmi_scdc_write(struct hdmi *hdmi, u32 data_type, u32 val); + +/** * sde_hdmi_audio_on() - enable hdmi audio. * @hdmi: Handle to the hdmi. * @params: audio setup parameters from codec. diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c index 681dca501f9b..d649458fcb7c 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c @@ -28,6 +28,13 @@ struct sde_hdmi_bridge { }; #define to_hdmi_bridge(x) container_of(x, struct sde_hdmi_bridge, base) +/* TX major version that supports scrambling */ +#define HDMI_TX_SCRAMBLER_MIN_TX_VERSION 0x04 +#define HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ 340000 +#define HDMI_TX_SCRAMBLER_TIMEOUT_MSEC 200 +/* default hsyncs for 4k@60 for 200ms */ +#define HDMI_DEFAULT_TIMEOUT_HSYNC 28571 + /* for AVI program */ #define HDMI_AVI_INFOFRAME_BUFFER_SIZE \ (HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE) @@ -101,6 +108,260 @@ static void _sde_hdmi_bridge_power_off(struct drm_bridge *bridge) } } +static int _sde_hdmi_bridge_ddc_clear_irq(struct hdmi *hdmi, + char *what) +{ + u32 ddc_int_ctrl, ddc_status, in_use, timeout; + u32 sw_done_mask = BIT(2); + u32 sw_done_ack = BIT(1); + u32 in_use_by_sw = BIT(0); + u32 in_use_by_hw = BIT(1); + + /* clear and enable interrutps */ + ddc_int_ctrl = sw_done_mask | sw_done_ack; + + hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL, ddc_int_ctrl); + + /* wait until DDC HW is free */ + timeout = 100; + do { + ddc_status = hdmi_read(hdmi, REG_HDMI_DDC_HW_STATUS); + in_use = ddc_status & (in_use_by_sw | in_use_by_hw); + if (in_use) { + SDE_DEBUG("ddc is in use by %s, timeout(%d)\n", + ddc_status & in_use_by_sw ? "sw" : "hw", + timeout); + udelay(100); + } + } while (in_use && --timeout); + + if (!timeout) { + SDE_ERROR("%s: timedout\n", what); + return -ETIMEDOUT; + } + + return 0; +} + +static int _sde_hdmi_bridge_scrambler_ddc_check_status(struct hdmi *hdmi) +{ + int rc = 0; + u32 reg_val; + + /* check for errors and clear status */ + reg_val = hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_STATUS); + if (reg_val & BIT(4)) { + SDE_ERROR("ddc aborted\n"); + reg_val |= BIT(5); + rc = -ECONNABORTED; + } + + if (reg_val & BIT(8)) { + SDE_ERROR("timed out\n"); + reg_val |= BIT(9); + rc = -ETIMEDOUT; + } + + if (reg_val & BIT(12)) { + SDE_ERROR("NACK0\n"); + reg_val |= BIT(13); + rc = -EIO; + } + if (reg_val & BIT(14)) { + SDE_ERROR("NACK1\n"); + reg_val |= BIT(15); + rc = -EIO; + } + hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_STATUS, reg_val); + + return rc; +} + +static void _sde_hdmi_bridge_scrambler_ddc_reset(struct hdmi *hdmi) +{ + u32 reg_val; + + /* clear ack and disable interrupts */ + reg_val = BIT(14) | BIT(9) | BIT(5) | BIT(1); + hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL2, reg_val); + + /* Reset DDC timers */ + reg_val = BIT(0) | hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL); + hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val); + + reg_val = hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL); + reg_val &= ~BIT(0); + hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val); +} + +static void _sde_hdmi_bridge_scrambler_ddc_disable(struct hdmi *hdmi) +{ + u32 reg_val; + + _sde_hdmi_bridge_scrambler_ddc_reset(hdmi); + /* Disable HW DDC access to RxStatus register */ + reg_val = hdmi_read(hdmi, REG_HDMI_HW_DDC_CTRL); + reg_val &= ~(BIT(8) | BIT(9)); + hdmi_write(hdmi, REG_HDMI_HW_DDC_CTRL, reg_val); +} + +static int _sde_hdmi_bridge_scrambler_status_timer_setup(struct hdmi *hdmi, + u32 timeout_hsync) +{ + u32 reg_val; + int rc; + + _sde_hdmi_bridge_ddc_clear_irq(hdmi, "scrambler"); + + hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL, + timeout_hsync); + hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL2, + timeout_hsync); + reg_val = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL5); + reg_val |= BIT(10); + hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL5, reg_val); + + reg_val = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL2); + /* Trigger interrupt if scrambler status is 0 or DDC failure */ + reg_val |= BIT(10); + reg_val &= ~(BIT(15) | BIT(16)); + reg_val |= BIT(16); + hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL2, reg_val); + + /* Enable DDC access */ + reg_val = hdmi_read(hdmi, REG_HDMI_HW_DDC_CTRL); + + reg_val &= ~(BIT(8) | BIT(9)); + reg_val |= BIT(8); + hdmi_write(hdmi, REG_HDMI_HW_DDC_CTRL, reg_val); + + /* WAIT for 200ms as per HDMI 2.0 standard for sink to respond */ + msleep(200); + + /* clear the scrambler status */ + rc = _sde_hdmi_bridge_scrambler_ddc_check_status(hdmi); + if (rc) + SDE_ERROR("scrambling ddc error %d\n", rc); + + _sde_hdmi_bridge_scrambler_ddc_disable(hdmi); + + return rc; +} + +static int _sde_hdmi_bridge_setup_ddc_timers(struct hdmi *hdmi, + u32 type, u32 to_in_num_lines) +{ + if (type >= HDMI_TX_DDC_TIMER_MAX) { + SDE_ERROR("Invalid timer type %d\n", type); + return -EINVAL; + } + + switch (type) { + case HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS: + _sde_hdmi_bridge_scrambler_status_timer_setup(hdmi, + to_in_num_lines); + break; + default: + SDE_ERROR("%d type not supported\n", type); + return -EINVAL; + } + + return 0; +} + +static inline int _sde_hdmi_bridge_get_timeout_in_hysnc( + struct drm_display_mode *mode, u32 timeout_ms) +{ + /* + * pixel clock = h_total * v_total * fps + * 1 sec = pixel clock number of pixels are transmitted. + * time taken by one line (h_total) = 1s / (v_total * fps). + * lines for give time = (time_ms * 1000) / (1000000 / (v_total * fps)) + * = (time_ms * clock) / h_total + */ + + return (timeout_ms * mode->clock / mode->htotal); +} + +static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi, + struct drm_display_mode *mode) +{ + int rc = 0; + int timeout_hsync; + u32 reg_val = 0; + u32 tmds_clock_ratio = 0; + bool scrambler_on = false; + + if (!hdmi || !mode) { + SDE_ERROR("invalid input\n"); + return -EINVAL; + } + + /* Read HDMI version */ + reg_val = hdmi_read(hdmi, REG_HDMI_VERSION); + reg_val = (reg_val & 0xF0000000) >> 28; + /* Scrambling is supported from HDMI TX 4.0 */ + if (reg_val < HDMI_TX_SCRAMBLER_MIN_TX_VERSION) { + DRM_INFO("scrambling not supported by tx\n"); + return 0; + } + + if (mode->clock > HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ) { + scrambler_on = true; + tmds_clock_ratio = 1; + } + + DRM_INFO("scrambler %s\n", scrambler_on ? "on" : "off"); + + if (scrambler_on) { + rc = sde_hdmi_scdc_write(hdmi, + HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE, + tmds_clock_ratio); + if (rc) { + SDE_ERROR("TMDS CLK RATIO ERR\n"); + return rc; + } + + reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); + reg_val |= BIT(31); /* Enable Update DATAPATH_MODE */ + reg_val |= BIT(28); /* Set SCRAMBLER_EN bit */ + + hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); + + rc = sde_hdmi_scdc_write(hdmi, + HDMI_TX_SCDC_SCRAMBLING_ENABLE, 0x1); + if (rc) { + SDE_ERROR("failed to enable scrambling\n"); + return rc; + } + + /* + * Setup hardware to periodically check for scrambler + * status bit on the sink. Sink should set this bit + * with in 200ms after scrambler is enabled. + */ + timeout_hsync = _sde_hdmi_bridge_get_timeout_in_hysnc( + mode, + HDMI_TX_SCRAMBLER_TIMEOUT_MSEC); + if (timeout_hsync <= 0) { + SDE_ERROR("err in timeout hsync calc\n"); + timeout_hsync = HDMI_DEFAULT_TIMEOUT_HSYNC; + } + SDE_DEBUG("timeout for scrambling en: %d hsyncs\n", + timeout_hsync); + + rc = _sde_hdmi_bridge_setup_ddc_timers(hdmi, + HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS, timeout_hsync); + } else { + sde_hdmi_scdc_write(hdmi, HDMI_TX_SCDC_SCRAMBLING_ENABLE, 0x0); + reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); + reg_val &= ~BIT(31); /* Disable Update DATAPATH_MODE */ + reg_val &= ~BIT(28); /* Unset SCRAMBLER_EN bit */ + hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); + } + return rc; +} + static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge) { struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); @@ -373,6 +634,7 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge, _sde_hdmi_bridge_set_spd_infoframe(hdmi, mode); DRM_DEBUG("hdmi setup info frame\n"); } + _sde_hdmi_bridge_setup_scrambler(hdmi, mode); } static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = { diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h index aef20f76bf02..0956617442af 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h @@ -563,6 +563,20 @@ static inline uint32_t HDMI_VSYNC_TOTAL_F2_V_TOTAL(uint32_t val) #define REG_HDMI_CEC_WR_CHECK_CONFIG 0x00000370 +#define REG_HDMI_DDC_INT_CTRL0 0x00000430 +#define REG_HDMI_DDC_INT_CTRL1 0x00000434 +#define REG_HDMI_DDC_INT_CTRL2 0x00000438 +#define REG_HDMI_DDC_INT_CTRL3 0x0000043C +#define REG_HDMI_DDC_INT_CTRL4 0x00000440 +#define REG_HDMI_DDC_INT_CTRL5 0x00000444 +#define REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL 0x00000464 +#define REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL 0x00000468 +#define REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL2 0x0000046C +#define REG_HDMI_SCRAMBLER_STATUS_DDC_STATUS 0x00000470 +#define REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_STATUS 0x00000474 +#define REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_STATUS2 0x00000478 +#define REG_HDMI_HW_DDC_CTRL 0x000004CC + #define REG_HDMI_8x60_PHY_REG0 0x00000300 #define HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__MASK 0x0000001c #define HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__SHIFT 2 |
