msm: mdss: hdmi: add scrambling support for hdmi driver
Add support for enabling scrambler on hosts that support scrambling. Scrambling is a new freature added in HDMI 2.0 specification to reduce EMI interference at higher clock frequencies. Scrambling is enabled for HDMI modes that have pixel clock greater than 340MHz. For lower pixel clock frequencies, scrambling is enabled if sink supports scrambling at lower frequencies. Change-Id: I3aa224a32e768e2754a9e056a58ca90808a26ec6 Signed-off-by: Vinu Deokaran <vinud@codeaurora.org>
This commit is contained in:
parent
4e095a46e7
commit
b19a1c8b15
5 changed files with 567 additions and 27 deletions
|
@ -61,6 +61,20 @@
|
|||
((d & 0xff) + ((d >> 8) & 0xff) + \
|
||||
((d >> 16) & 0xff) + ((d >> 24) & 0xff))
|
||||
|
||||
|
||||
/*
|
||||
* Pixel Clock to TMDS Character Rate Ratios.
|
||||
*/
|
||||
#define HDMI_TX_YUV420_24BPP_PCLK_TMDS_CH_RATE_RATIO 2
|
||||
#define HDMI_TX_YUV422_24BPP_PCLK_TMDS_CH_RATE_RATIO 1
|
||||
#define HDMI_TX_RGB_24BPP_PCLK_TMDS_CH_RATE_RATIO 1
|
||||
|
||||
#define HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ 340000
|
||||
#define HDMI_TX_SCRAMBLER_TIMEOUT_USEC 200000
|
||||
|
||||
#define HDMI_TX_KHZ_TO_HZ 1000
|
||||
#define HDMI_TX_MHZ_TO_HZ 1000000
|
||||
|
||||
/* Enable HDCP by default */
|
||||
static bool hdcp_feature_on = true;
|
||||
|
||||
|
@ -236,6 +250,39 @@ static const struct hdmi_tx_audio_acr_arry hdmi_tx_audio_acr_lut[] = {
|
|||
{20480, 247500} } },
|
||||
};
|
||||
|
||||
static int hdmi_tx_get_version(struct hdmi_tx_ctrl *hdmi_ctrl)
|
||||
{
|
||||
int rc;
|
||||
int reg_val;
|
||||
struct dss_io_data *io;
|
||||
|
||||
rc = hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_HPD_PM, true);
|
||||
if (rc) {
|
||||
DEV_ERR("%s: Failed to read HDMI version\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
|
||||
if (!io->base) {
|
||||
DEV_ERR("%s: core io not inititalized\n", __func__);
|
||||
rc = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
reg_val = DSS_REG_R(io, HDMI_VERSION);
|
||||
reg_val = (reg_val & 0xF0000000) >> 28;
|
||||
hdmi_ctrl->hdmi_tx_ver = reg_val;
|
||||
|
||||
rc = hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_HPD_PM, false);
|
||||
if (rc) {
|
||||
DEV_ERR("%s: FAILED to disable power\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fail:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int register_hdmi_cable_notification(struct hdmi_cable_notify *handler)
|
||||
{
|
||||
struct hdmi_tx_ctrl *hdmi_ctrl = NULL;
|
||||
|
@ -1461,11 +1508,9 @@ static int hdmi_tx_video_setup(struct hdmi_tx_ctrl *hdmi_ctrl)
|
|||
if (hdmi_ctrl->vid_cfg.avi_iframe.pixel_format == MDP_Y_CBCR_H2V2)
|
||||
div = 1;
|
||||
|
||||
total_h = (timing->active_h >> div) + (timing->front_porch_h >> div) +
|
||||
(timing->back_porch_h >> div) + (timing->pulse_width_h >> div)
|
||||
- 1;
|
||||
total_v = timing->active_v + timing->front_porch_v +
|
||||
timing->back_porch_v + timing->pulse_width_v - 1;
|
||||
total_h = (hdmi_tx_get_h_total(timing) >> div) - 1;
|
||||
total_v = hdmi_tx_get_v_total(timing) - 1;
|
||||
|
||||
if (((total_v << 16) & 0xE0000000) || (total_h & 0xFFFFE000)) {
|
||||
DEV_ERR("%s: total v=%d or h=%d is larger than supported\n",
|
||||
__func__, total_v, total_h);
|
||||
|
@ -2740,6 +2785,107 @@ static void hdmi_tx_audio_off(struct hdmi_tx_ctrl *hdmi_ctrl)
|
|||
DEV_INFO("HDMI Audio: Disabled\n");
|
||||
} /* hdmi_tx_audio_off */
|
||||
|
||||
static int hdmi_tx_setup_scrambler(struct hdmi_tx_ctrl *hdmi_ctrl)
|
||||
{
|
||||
int rc = 0;
|
||||
u32 rate = 0;
|
||||
u32 reg_val = 0;
|
||||
bool scrambler_on = false;
|
||||
struct dss_io_data *io = NULL;
|
||||
const struct msm_hdmi_mode_timing_info *timing = NULL;
|
||||
|
||||
if (!hdmi_ctrl) {
|
||||
DEV_ERR("%s: Bad input parameters\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
|
||||
if (!io->base) {
|
||||
DEV_ERR("%s: core io is not initialized\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
timing = hdmi_ctrl->vid_cfg.timing;
|
||||
if (!timing) {
|
||||
DEV_ERR("%s: Invalid timing info\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Scrambling is supported from HDMI TX 4.0 */
|
||||
if (hdmi_ctrl->hdmi_tx_ver < HDMI_TX_SCRAMBLER_MIN_TX_VERSION) {
|
||||
DEV_DBG("%s: HDMI TX does not support scrambling\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (hdmi_ctrl->vid_cfg.avi_iframe.pixel_format) {
|
||||
case MDP_Y_CBCR_H2V2:
|
||||
rate = timing->pixel_freq /
|
||||
HDMI_TX_YUV420_24BPP_PCLK_TMDS_CH_RATE_RATIO;
|
||||
break;
|
||||
case MDP_Y_CBCR_H2V1:
|
||||
rate = timing->pixel_freq /
|
||||
HDMI_TX_YUV422_24BPP_PCLK_TMDS_CH_RATE_RATIO;
|
||||
break;
|
||||
default:
|
||||
rate = timing->pixel_freq /
|
||||
HDMI_TX_RGB_24BPP_PCLK_TMDS_CH_RATE_RATIO;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rate > HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ) {
|
||||
scrambler_on = true;
|
||||
} else {
|
||||
if (hdmi_edid_get_sink_scrambler_support(
|
||||
hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID]))
|
||||
scrambler_on = true;
|
||||
}
|
||||
|
||||
if (scrambler_on) {
|
||||
reg_val = DSS_REG_R(io, HDMI_CTRL);
|
||||
reg_val |= BIT(31); /* Enable Update DATAPATH_MODE */
|
||||
reg_val |= BIT(28); /* Set SCRAMBLER_EN bit */
|
||||
|
||||
DSS_REG_W(io, HDMI_CTRL, reg_val);
|
||||
|
||||
rc = hdmi_scdc_write(&hdmi_ctrl->ddc_ctrl,
|
||||
HDMI_TX_SCDC_SCRAMBLING_ENABLE,
|
||||
0x1);
|
||||
} else {
|
||||
rc = hdmi_scdc_write(&hdmi_ctrl->ddc_ctrl,
|
||||
HDMI_TX_SCDC_SCRAMBLING_ENABLE,
|
||||
0x0);
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
DEV_ERR("%s: SCDC write failed\n", __func__);
|
||||
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.
|
||||
*/
|
||||
if (scrambler_on) {
|
||||
u64 mult;
|
||||
u64 div;
|
||||
/* calculate number of lines sent in 200ms */
|
||||
mult = HDMI_TX_SCRAMBLER_TIMEOUT_USEC *
|
||||
((u64)timing->pixel_freq * HDMI_TX_KHZ_TO_HZ);
|
||||
div = hdmi_tx_get_v_total(timing) * HDMI_TX_MHZ_TO_HZ;
|
||||
if (div)
|
||||
do_div(mult, div);
|
||||
else
|
||||
mult = 0;
|
||||
|
||||
rc = hdmi_setup_ddc_timers(&hdmi_ctrl->ddc_ctrl,
|
||||
HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS,
|
||||
(u32)mult);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hdmi_tx_start(struct hdmi_tx_ctrl *hdmi_ctrl)
|
||||
{
|
||||
int rc = 0;
|
||||
|
@ -2782,6 +2928,12 @@ static int hdmi_tx_start(struct hdmi_tx_ctrl *hdmi_ctrl)
|
|||
hdmi_tx_set_spd_infoframe(hdmi_ctrl);
|
||||
}
|
||||
|
||||
rc = hdmi_tx_setup_scrambler(hdmi_ctrl);
|
||||
if (rc) {
|
||||
DEV_ERR("%s: Scrambler setup failed\n", __func__);
|
||||
hdmi_tx_set_mode(hdmi_ctrl, false);
|
||||
return rc;
|
||||
}
|
||||
/* todo: CEC */
|
||||
|
||||
DEV_INFO("%s: HDMI Core: Initialized\n", __func__);
|
||||
|
@ -3185,7 +3337,8 @@ static irqreturn_t hdmi_tx_isr(int irq, void *data)
|
|||
queue_work(hdmi_ctrl->workq, &hdmi_ctrl->hpd_int_work);
|
||||
}
|
||||
|
||||
if (hdmi_ddc_isr(&hdmi_ctrl->ddc_ctrl))
|
||||
if (hdmi_ddc_isr(&hdmi_ctrl->ddc_ctrl,
|
||||
hdmi_ctrl->hdmi_tx_ver))
|
||||
DEV_ERR("%s: hdmi_ddc_isr failed\n", __func__);
|
||||
|
||||
if (hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC])
|
||||
|
@ -4172,7 +4325,6 @@ static int hdmi_tx_probe(struct platform_device *pdev)
|
|||
struct device_node *of_node = pdev->dev.of_node;
|
||||
struct hdmi_tx_ctrl *hdmi_ctrl = NULL;
|
||||
struct mdss_panel_cfg *pan_cfg = NULL;
|
||||
|
||||
if (!of_node) {
|
||||
DEV_ERR("%s: FAILED: of_node not found\n", __func__);
|
||||
rc = -ENODEV;
|
||||
|
@ -4236,6 +4388,13 @@ static int hdmi_tx_probe(struct platform_device *pdev)
|
|||
goto failed_dev_init;
|
||||
}
|
||||
|
||||
rc = hdmi_tx_get_version(hdmi_ctrl);
|
||||
if (rc) {
|
||||
DEV_ERR("%s: FAILED: hdmi_tx_get_version. rc=%d\n",
|
||||
__func__, rc);
|
||||
goto failed_reg_panel;
|
||||
}
|
||||
|
||||
rc = hdmi_tx_register_panel(hdmi_ctrl);
|
||||
if (rc) {
|
||||
DEV_ERR("%s: FAILED: register_panel. rc=%d\n", __func__, rc);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
|
@ -125,6 +125,7 @@ struct hdmi_video_config {
|
|||
|
||||
struct hdmi_tx_ctrl {
|
||||
struct platform_device *pdev;
|
||||
u32 hdmi_tx_ver;
|
||||
struct hdmi_tx_platform_data pdata;
|
||||
struct mdss_panel_data panel_data;
|
||||
struct mdss_util_intf *mdss_util;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
|
@ -17,11 +17,71 @@
|
|||
|
||||
#define RESOLUTION_NAME_STR_LEN 30
|
||||
|
||||
#define HDMI_SCDC_UNKNOWN_REGISTER "Unknown register"
|
||||
|
||||
static struct msm_hdmi_mode_timing_info
|
||||
hdmi_supported_video_mode_lut[HDMI_VFRMT_MAX];
|
||||
|
||||
static char res_buf[RESOLUTION_NAME_STR_LEN];
|
||||
|
||||
static void hdmi_scrambler_status_timer_setup(struct hdmi_tx_ddc_ctrl *ctrl,
|
||||
u32 to_in_num_lines)
|
||||
{
|
||||
u32 reg_val;
|
||||
|
||||
DSS_REG_W(ctrl->io, HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL,
|
||||
to_in_num_lines);
|
||||
DSS_REG_W(ctrl->io, HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL2,
|
||||
0xFFFF);
|
||||
reg_val = DSS_REG_R(ctrl->io, HDMI_DDC_INT_CTRL5);
|
||||
reg_val |= BIT(10);
|
||||
DSS_REG_W(ctrl->io, HDMI_DDC_INT_CTRL5, reg_val);
|
||||
|
||||
reg_val = DSS_REG_R(ctrl->io, HDMI_DDC_INT_CTRL2);
|
||||
/* Trigger interrupt if scrambler status is 0 or DDC failure */
|
||||
reg_val |= BIT(10);
|
||||
reg_val &= 0x18000;
|
||||
reg_val |= (0x2 << 15);
|
||||
DSS_REG_W(ctrl->io, HDMI_DDC_INT_CTRL2, reg_val);
|
||||
|
||||
/* Enable DDC access */
|
||||
reg_val = DSS_REG_R(ctrl->io, HDMI_HW_DDC_CTRL);
|
||||
|
||||
reg_val &= ~0x300;
|
||||
reg_val |= (0x1 << 8);
|
||||
DSS_REG_W(ctrl->io, HDMI_HW_DDC_CTRL, reg_val);
|
||||
}
|
||||
|
||||
static inline char *hdmi_scdc_reg2string(u32 type)
|
||||
{
|
||||
switch (type) {
|
||||
case HDMI_TX_SCDC_SCRAMBLING_STATUS:
|
||||
return "HDMI_TX_SCDC_SCRAMBLING_STATUS";
|
||||
case HDMI_TX_SCDC_SCRAMBLING_ENABLE:
|
||||
return "HDMI_TX_SCDC_SCRAMBLING_ENABLE";
|
||||
case HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE:
|
||||
return "HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE";
|
||||
case HDMI_TX_SCDC_CLOCK_DET_STATUS:
|
||||
return "HDMI_TX_SCDC_CLOCK_DET_STATUS";
|
||||
case HDMI_TX_SCDC_CH0_LOCK_STATUS:
|
||||
return "HDMI_TX_SCDC_CH0_LOCK_STATUS";
|
||||
case HDMI_TX_SCDC_CH1_LOCK_STATUS:
|
||||
return "HDMI_TX_SCDC_CH1_LOCK_STATUS";
|
||||
case HDMI_TX_SCDC_CH2_LOCK_STATUS:
|
||||
return "HDMI_TX_SCDC_CH2_LOCK_STATUS";
|
||||
case HDMI_TX_SCDC_CH0_ERROR_COUNT:
|
||||
return "HDMI_TX_SCDC_CH0_ERROR_COUNT";
|
||||
case HDMI_TX_SCDC_CH1_ERROR_COUNT:
|
||||
return "HDMI_TX_SCDC_CH1_ERROR_COUNT";
|
||||
case HDMI_TX_SCDC_CH2_ERROR_COUNT:
|
||||
return "HDMI_TX_SCDC_CH2_ERROR_COUNT";
|
||||
case HDMI_TX_SCDC_READ_ENABLE:
|
||||
return"HDMI_TX_SCDC_READ_ENABLE";
|
||||
default:
|
||||
return HDMI_SCDC_UNKNOWN_REGISTER;
|
||||
}
|
||||
}
|
||||
|
||||
const char *msm_hdmi_mode_2string(u32 mode)
|
||||
{
|
||||
static struct msm_hdmi_mode_timing_info *ri;
|
||||
|
@ -454,9 +514,11 @@ void hdmi_ddc_config(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
|
|||
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_REF, (1 << 16) | (19 << 0));
|
||||
} /* hdmi_ddc_config */
|
||||
|
||||
int hdmi_ddc_isr(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
|
||||
int hdmi_ddc_isr(struct hdmi_tx_ddc_ctrl *ddc_ctrl, u32 version)
|
||||
{
|
||||
u32 ddc_int_ctrl;
|
||||
u32 ddc_timer_int;
|
||||
bool scrambler_timer_off = false;
|
||||
|
||||
if (!ddc_ctrl || !ddc_ctrl->io) {
|
||||
DEV_ERR("%s: invalid input\n", __func__);
|
||||
|
@ -472,7 +534,44 @@ int hdmi_ddc_isr(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
|
|||
}
|
||||
|
||||
DEV_DBG("%s: ddc_int_ctrl=%04x\n", __func__, ddc_int_ctrl);
|
||||
if (version < HDMI_TX_SCRAMBLER_MIN_TX_VERSION)
|
||||
goto end;
|
||||
|
||||
ddc_timer_int = DSS_REG_R_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL2);
|
||||
if (ddc_timer_int & BIT(12)) {
|
||||
/* DDC_INT_CTRL2.SCRAMBLER_STATUS_NOT is set */
|
||||
DEV_ERR("%s: Sink cannot descramble the signal\n",
|
||||
__func__);
|
||||
/* Clear interrupt */
|
||||
ddc_timer_int |= BIT(14);
|
||||
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL2,
|
||||
ddc_timer_int);
|
||||
scrambler_timer_off = true;
|
||||
}
|
||||
|
||||
ddc_timer_int = DSS_REG_R_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL5);
|
||||
if (ddc_timer_int & BIT(8)) {
|
||||
/*
|
||||
* DDC_INT_CTRL5.SCRAMBLER_STATUS_DDC_REQ_TIMEOUT
|
||||
* is set
|
||||
*/
|
||||
DEV_ERR(
|
||||
"%s: DDC timeout while reading SCRAMBLER STATUS\n",
|
||||
__func__);
|
||||
ddc_timer_int |= BIT(13);
|
||||
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL5,
|
||||
ddc_timer_int);
|
||||
scrambler_timer_off = true;
|
||||
}
|
||||
|
||||
/* Disable scrambler status timer if it has been acknowledged */
|
||||
if (scrambler_timer_off) {
|
||||
u32 regval = DSS_REG_R_ND(ddc_ctrl->io,
|
||||
HDMI_HW_DDC_CTRL);
|
||||
regval &= 0x300;
|
||||
DSS_REG_W_ND(ddc_ctrl->io, HDMI_HW_DDC_CTRL, regval);
|
||||
}
|
||||
end:
|
||||
return 0;
|
||||
} /* hdmi_ddc_isr */
|
||||
|
||||
|
@ -755,20 +854,10 @@ again:
|
|||
* STOP0 = 0x0 (do NOT insert STOP bit)
|
||||
* CNT0 = 0x1 (single byte transaction excluding address)
|
||||
*/
|
||||
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_TRANS0, BIT(12) | BIT(16));
|
||||
|
||||
/*
|
||||
* 5. Write to HDMI_I2C_TRANSACTION1 with the following fields set in
|
||||
* order to handle characteristics of portion #3
|
||||
* RW1 = 0x1 (read)
|
||||
* START1 = 0x1 (insert START bit)
|
||||
* STOP1 = 0x1 (insert STOP bit)
|
||||
* CNT1 = data_len (0xN (write N bytes of data))
|
||||
* Byte count for second transition (excluding the first
|
||||
* Byte which is usually the address)
|
||||
*/
|
||||
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_TRANS1,
|
||||
BIT(13) | ((ddc_data->data_len-1) << 16));
|
||||
reg_val = (ddc_data->data_len + 1) << 16;
|
||||
reg_val |= BIT(12);
|
||||
reg_val |= BIT(13);
|
||||
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_TRANS0, reg_val);
|
||||
|
||||
/* Trigger the I2C transfer */
|
||||
/*
|
||||
|
@ -780,7 +869,7 @@ again:
|
|||
* GO = 0x1 (kicks off hardware)
|
||||
*/
|
||||
reinit_completion(&ddc_ctrl->ddc_sw_done);
|
||||
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_CTRL, BIT(0) | BIT(20));
|
||||
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_CTRL, BIT(0));
|
||||
|
||||
time_out_count = wait_for_completion_timeout(
|
||||
&ddc_ctrl->ddc_sw_done, HZ/2);
|
||||
|
@ -834,3 +923,213 @@ again:
|
|||
error:
|
||||
return status;
|
||||
} /* hdmi_ddc_write */
|
||||
|
||||
|
||||
int hdmi_scdc_read(struct hdmi_tx_ddc_ctrl *ctrl, u32 data_type, u32 *val)
|
||||
{
|
||||
struct hdmi_tx_ddc_data data = {0};
|
||||
int rc = 0;
|
||||
u8 data_buf[2] = {0};
|
||||
|
||||
if (!ctrl || !ctrl->io || !val) {
|
||||
DEV_ERR("%s: Bad Parameters\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (data_type >= HDMI_TX_SCDC_MAX) {
|
||||
DEV_ERR("%s: Unsupported data type\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data.what = hdmi_scdc_reg2string(data_type);
|
||||
data.dev_addr = 0xA8;
|
||||
data.no_align = true;
|
||||
data.retry = 1;
|
||||
data.data_buf = data_buf;
|
||||
|
||||
switch (data_type) {
|
||||
case HDMI_TX_SCDC_SCRAMBLING_STATUS:
|
||||
data.data_len = 1;
|
||||
data.request_len = 1;
|
||||
data.offset = HDMI_SCDC_SCRAMBLER_STATUS;
|
||||
break;
|
||||
case HDMI_TX_SCDC_SCRAMBLING_ENABLE:
|
||||
case HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE:
|
||||
data.data_len = 1;
|
||||
data.request_len = 1;
|
||||
data.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.data_len = 1;
|
||||
data.request_len = 1;
|
||||
data.offset = HDMI_SCDC_STATUS_FLAGS_0;
|
||||
break;
|
||||
case HDMI_TX_SCDC_CH0_ERROR_COUNT:
|
||||
data.data_len = 2;
|
||||
data.request_len = 2;
|
||||
data.offset = HDMI_SCDC_ERR_DET_0_L;
|
||||
break;
|
||||
case HDMI_TX_SCDC_CH1_ERROR_COUNT:
|
||||
data.data_len = 2;
|
||||
data.request_len = 2;
|
||||
data.offset = HDMI_SCDC_ERR_DET_1_L;
|
||||
break;
|
||||
case HDMI_TX_SCDC_CH2_ERROR_COUNT:
|
||||
data.data_len = 2;
|
||||
data.request_len = 2;
|
||||
data.offset = HDMI_SCDC_ERR_DET_2_L;
|
||||
break;
|
||||
case HDMI_TX_SCDC_READ_ENABLE:
|
||||
data.data_len = 1;
|
||||
data.request_len = 1;
|
||||
data.offset = HDMI_SCDC_CONFIG_0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
rc = hdmi_ddc_read(ctrl, &data);
|
||||
if (rc) {
|
||||
DEV_ERR("%s: DDC Read failed for %s\n", __func__, data.what);
|
||||
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 hdmi_scdc_write(struct hdmi_tx_ddc_ctrl *ctrl, u32 data_type, u32 val)
|
||||
{
|
||||
struct hdmi_tx_ddc_data data = {0};
|
||||
struct hdmi_tx_ddc_data rdata = {0};
|
||||
int rc = 0;
|
||||
u8 data_buf[2] = {0};
|
||||
u8 read_val = 0;
|
||||
|
||||
if (!ctrl || !ctrl->io) {
|
||||
DEV_ERR("%s: Bad Parameters\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (data_type >= HDMI_TX_SCDC_MAX) {
|
||||
DEV_ERR("%s: Unsupported data type\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data.what = hdmi_scdc_reg2string(data_type);
|
||||
data.dev_addr = 0xA8;
|
||||
data.no_align = true;
|
||||
data.retry = 1;
|
||||
data.data_buf = data_buf;
|
||||
|
||||
switch (data_type) {
|
||||
case HDMI_TX_SCDC_SCRAMBLING_ENABLE:
|
||||
case HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE:
|
||||
rdata.what = "TMDS CONFIG";
|
||||
rdata.dev_addr = 0xA8;
|
||||
rdata.no_align = true;
|
||||
rdata.retry = 2;
|
||||
rdata.data_buf = &read_val;
|
||||
rdata.data_len = 1;
|
||||
rdata.offset = HDMI_SCDC_TMDS_CONFIG;
|
||||
rdata.request_len = 1;
|
||||
rc = hdmi_ddc_read(ctrl, &rdata);
|
||||
if (rc) {
|
||||
DEV_ERR("%s: scdc read failed\n", __func__);
|
||||
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));
|
||||
}
|
||||
data.data_len = 1;
|
||||
data.request_len = 1;
|
||||
data.offset = HDMI_SCDC_TMDS_CONFIG;
|
||||
break;
|
||||
case HDMI_TX_SCDC_READ_ENABLE:
|
||||
data.data_len = 1;
|
||||
data.request_len = 1;
|
||||
data.offset = HDMI_SCDC_CONFIG_0;
|
||||
data_buf[0] = (u8)(val & 0x1);
|
||||
break;
|
||||
default:
|
||||
DEV_ERR("%s Cannot write to read only reg (%d)\n", __func__,
|
||||
data_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = hdmi_ddc_write(ctrl, &data);
|
||||
if (rc) {
|
||||
DEV_ERR("%s: DDC Read failed for %s\n", __func__, data.what);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hdmi_setup_ddc_timers(struct hdmi_tx_ddc_ctrl *ctrl,
|
||||
u32 type, u32 to_in_num_lines)
|
||||
{
|
||||
if (!ctrl) {
|
||||
DEV_ERR("%s: Invalid parameters\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (type >= HDMI_TX_DDC_TIMER_MAX) {
|
||||
DEV_ERR("%s: Invalid timer type %d\n", __func__, type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS:
|
||||
hdmi_scrambler_status_timer_setup(ctrl, to_in_num_lines);
|
||||
break;
|
||||
default:
|
||||
DEV_ERR("%s: %d type not supported\n", __func__, type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
|
@ -207,6 +207,19 @@
|
|||
#define HDMI_CEC_RD_TOTAL_RANGE (0x00000368)
|
||||
#define HDMI_CEC_RD_ERR_RESP_LO (0x0000036C)
|
||||
#define HDMI_CEC_WR_CHECK_CONFIG (0x00000370)
|
||||
#define HDMI_DDC_INT_CTRL0 (0x00000430)
|
||||
#define HDMI_DDC_INT_CTRL1 (0x00000434)
|
||||
#define HDMI_DDC_INT_CTRL2 (0x00000438)
|
||||
#define HDMI_DDC_INT_CTRL3 (0x0000043C)
|
||||
#define HDMI_DDC_INT_CTRL4 (0x00000440)
|
||||
#define HDMI_DDC_INT_CTRL5 (0x00000444)
|
||||
#define HDMI_SCRAMBLER_STATUS_DDC_CTRL (0x00000464)
|
||||
#define HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL (0x00000468)
|
||||
#define HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL2 (0x0000046C)
|
||||
#define HDMI_SCRAMBLER_STATUS_DDC_STATUS (0x00000470)
|
||||
#define HDMI_SCRAMBLER_STATUS_DDC_TIMER_STATUS (0x00000474)
|
||||
#define HDMI_SCRAMBLER_STATUS_DDC_TIMER_STATUS2 (0x00000478)
|
||||
#define HDMI_HW_DDC_CTRL (0x000004CC)
|
||||
|
||||
/* HDMI PHY Registers */
|
||||
#define HDMI_PHY_ANA_CFG0 (0x00000000)
|
||||
|
@ -232,6 +245,25 @@
|
|||
#define LPASS_LPAIF_RDDMA_CTL0 (0xFE152000)
|
||||
#define LPASS_LPAIF_RDDMA_PER_CNT0 (0x00000014)
|
||||
|
||||
/* TX major version that supports scrambling */
|
||||
#define HDMI_TX_SCRAMBLER_MIN_TX_VERSION 0x04
|
||||
|
||||
/* 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
|
||||
|
||||
enum hdmi_tx_feature_type {
|
||||
HDMI_TX_FEAT_EDID,
|
||||
HDMI_TX_FEAT_HDCP,
|
||||
|
@ -239,6 +271,30 @@ enum hdmi_tx_feature_type {
|
|||
HDMI_TX_FEAT_MAX,
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
struct hdmi_tx_ddc_ctrl {
|
||||
struct dss_io_data *io;
|
||||
struct completion ddc_sw_done;
|
||||
|
@ -268,9 +324,14 @@ void *hdmi_get_featuredata_from_sysfs_dev(struct device *device, u32 type);
|
|||
|
||||
/* DDC */
|
||||
void hdmi_ddc_config(struct hdmi_tx_ddc_ctrl *);
|
||||
int hdmi_ddc_isr(struct hdmi_tx_ddc_ctrl *);
|
||||
int hdmi_ddc_isr(struct hdmi_tx_ddc_ctrl *, u32 version);
|
||||
int hdmi_ddc_write(struct hdmi_tx_ddc_ctrl *, struct hdmi_tx_ddc_data *);
|
||||
int hdmi_ddc_read_seg(struct hdmi_tx_ddc_ctrl *, struct hdmi_tx_ddc_data *);
|
||||
int hdmi_ddc_read(struct hdmi_tx_ddc_ctrl *, struct hdmi_tx_ddc_data *);
|
||||
|
||||
int hdmi_scdc_read(struct hdmi_tx_ddc_ctrl *ctrl, u32 data_type, u32 *val);
|
||||
int hdmi_scdc_write(struct hdmi_tx_ddc_ctrl *ctrl, u32 data_type, u32 val);
|
||||
int hdmi_setup_ddc_timers(struct hdmi_tx_ddc_ctrl *ctrl,
|
||||
u32 type, u32 to_in_num_lines);
|
||||
|
||||
#endif /* __HDMI_UTIL_H__ */
|
||||
|
|
|
@ -259,4 +259,24 @@ static inline void MSM_HDMI_MODES_SET_SUPP_TIMINGS(
|
|||
}
|
||||
}
|
||||
|
||||
static inline int hdmi_tx_get_v_total(const struct msm_hdmi_mode_timing_info *t)
|
||||
{
|
||||
if (t) {
|
||||
return t->active_v + t->front_porch_v + t->pulse_width_v +
|
||||
t->back_porch_v;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int hdmi_tx_get_h_total(const struct msm_hdmi_mode_timing_info *t)
|
||||
{
|
||||
if (t) {
|
||||
return t->active_h + t->front_porch_h + t->pulse_width_h +
|
||||
t->back_porch_h;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* _UAPI_MSM_HDMI_MODES_H__ */
|
||||
|
|
Loading…
Add table
Reference in a new issue