msm: mdss: hdmi: hdcp: load keys separately before authentication
Retrieve and load the HDCP keys before starting authentication so that HDCP internal state machine can be initialized completely on cable connection to avoid possible authentication failures. Change-Id: I873fb02589bb6c19938743d44dc56ed1cfff7260 Signed-off-by: Ajay Singh Parmar <aparmar@codeaurora.org>
This commit is contained in:
parent
aea583fcef
commit
8e9072d633
1 changed files with 116 additions and 137 deletions
|
@ -245,17 +245,112 @@ static int hdcp_scm_call(struct scm_hdcp_req *req, u32 *resp)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int hdmi_hdcp_load_keys(void *input)
|
||||
{
|
||||
int rc = 0;
|
||||
bool use_sw_keys = false;
|
||||
u32 reg_val;
|
||||
u32 ksv_lsb_addr, ksv_msb_addr;
|
||||
u32 aksv_lsb, aksv_msb;
|
||||
u8 aksv[5];
|
||||
struct dss_io_data *io;
|
||||
struct dss_io_data *qfprom_io;
|
||||
struct hdmi_hdcp_ctrl *hdcp_ctrl = input;
|
||||
|
||||
if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io ||
|
||||
!hdcp_ctrl->init_data.qfprom_io) {
|
||||
DEV_ERR("%s: invalid input\n", __func__);
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ((HDCP_STATE_INACTIVE != hdcp_ctrl->hdcp_state) &&
|
||||
(HDCP_STATE_AUTH_FAIL != hdcp_ctrl->hdcp_state)) {
|
||||
DEV_ERR("%s: %s: invalid state. returning\n", __func__,
|
||||
HDCP_STATE_NAME);
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
io = hdcp_ctrl->init_data.core_io;
|
||||
qfprom_io = hdcp_ctrl->init_data.qfprom_io;
|
||||
|
||||
/* On compatible hardware, use SW keys */
|
||||
reg_val = DSS_REG_R(qfprom_io, SEC_CTRL_HW_VERSION);
|
||||
if (reg_val >= HDCP_SEL_MIN_SEC_VERSION) {
|
||||
reg_val = DSS_REG_R(qfprom_io,
|
||||
QFPROM_RAW_FEAT_CONFIG_ROW0_MSB +
|
||||
QFPROM_RAW_VERSION_4);
|
||||
|
||||
if (!(reg_val & BIT(23)))
|
||||
use_sw_keys = true;
|
||||
}
|
||||
|
||||
if (use_sw_keys) {
|
||||
if (hdcp1_set_keys(&aksv_msb, &aksv_lsb)) {
|
||||
pr_err("%s: setting hdcp SW keys failed\n", __func__);
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
} else {
|
||||
/* Fetch aksv from QFPROM, this info should be public. */
|
||||
ksv_lsb_addr = HDCP_KSV_LSB;
|
||||
ksv_msb_addr = HDCP_KSV_MSB;
|
||||
|
||||
if (hdcp_ctrl->hdmi_tx_ver_4) {
|
||||
ksv_lsb_addr += HDCP_KSV_VERSION_4_OFFSET;
|
||||
ksv_msb_addr += HDCP_KSV_VERSION_4_OFFSET;
|
||||
}
|
||||
|
||||
aksv_lsb = DSS_REG_R(qfprom_io, ksv_lsb_addr);
|
||||
aksv_msb = DSS_REG_R(qfprom_io, ksv_msb_addr);
|
||||
}
|
||||
|
||||
DEV_DBG("%s: %s: AKSV=%02x%08x\n", __func__, HDCP_STATE_NAME,
|
||||
aksv_msb, aksv_lsb);
|
||||
|
||||
aksv[0] = aksv_lsb & 0xFF;
|
||||
aksv[1] = (aksv_lsb >> 8) & 0xFF;
|
||||
aksv[2] = (aksv_lsb >> 16) & 0xFF;
|
||||
aksv[3] = (aksv_lsb >> 24) & 0xFF;
|
||||
aksv[4] = aksv_msb & 0xFF;
|
||||
|
||||
/* check there are 20 ones in AKSV */
|
||||
if (hdmi_hdcp_count_one(aksv, 5) != 20) {
|
||||
DEV_ERR("%s: AKSV bit count failed\n", __func__);
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
DSS_REG_W(io, HDMI_HDCP_SW_LOWER_AKSV, aksv_lsb);
|
||||
DSS_REG_W(io, HDMI_HDCP_SW_UPPER_AKSV, aksv_msb);
|
||||
|
||||
/* Setup seed values for random number An */
|
||||
DSS_REG_W(io, HDMI_HDCP_ENTROPY_CTRL0, 0xB1FFB0FF);
|
||||
DSS_REG_W(io, HDMI_HDCP_ENTROPY_CTRL1, 0xF00DFACE);
|
||||
|
||||
/* Disable the RngCipher state */
|
||||
DSS_REG_W(io, HDMI_HDCP_DEBUG_CTRL,
|
||||
DSS_REG_R(io, HDMI_HDCP_DEBUG_CTRL) & ~(BIT(2)));
|
||||
|
||||
/* make sure hw is programmed */
|
||||
wmb();
|
||||
|
||||
DSS_REG_W(io, HDMI_HDCP_CTRL, BIT(0));
|
||||
|
||||
hdcp_ctrl->hdcp_state = HDCP_STATE_AUTHENTICATING;
|
||||
end:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl)
|
||||
{
|
||||
int rc;
|
||||
u32 reg_val;
|
||||
u32 qfprom_aksv_lsb, qfprom_aksv_msb;
|
||||
u32 link0_aksv_0, link0_aksv_1;
|
||||
u32 link0_bksv_0, link0_bksv_1;
|
||||
u32 link0_an_0, link0_an_1;
|
||||
u32 timeout_count;
|
||||
bool is_match, use_sw_keys = false;
|
||||
bool stale_an = false;
|
||||
bool is_match;
|
||||
struct dss_io_data *io;
|
||||
struct dss_io_data *hdcp_io;
|
||||
u8 aksv[5], *bksv = NULL;
|
||||
|
@ -264,7 +359,6 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl)
|
|||
struct hdmi_tx_ddc_data ddc_data;
|
||||
u32 link0_status, an_ready, keys_state;
|
||||
u8 buf[0xFF];
|
||||
u32 ksv_lsb_addr, ksv_msb_addr;
|
||||
|
||||
struct scm_hdcp_req scm_buf[SCM_HDCP_MAX_REG];
|
||||
u32 phy_addr;
|
||||
|
@ -290,74 +384,8 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl)
|
|||
goto error;
|
||||
}
|
||||
|
||||
/* On compatible hardware, use SW aksv */
|
||||
reg_val = DSS_REG_R(hdcp_ctrl->init_data.qfprom_io,
|
||||
SEC_CTRL_HW_VERSION);
|
||||
if (reg_val >= HDCP_SEL_MIN_SEC_VERSION) {
|
||||
reg_val = DSS_REG_R(hdcp_ctrl->init_data.qfprom_io,
|
||||
QFPROM_RAW_FEAT_CONFIG_ROW0_MSB +
|
||||
QFPROM_RAW_VERSION_4);
|
||||
if (!(reg_val & BIT(23)))
|
||||
use_sw_keys = true;
|
||||
}
|
||||
|
||||
if (use_sw_keys) {
|
||||
if (hdcp1_set_keys(&qfprom_aksv_msb, &qfprom_aksv_lsb)) {
|
||||
pr_err("%s: setting of hdcp SW keys failed\n",
|
||||
__func__);
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
/* Fetch aksv from QFPROM, this info should be public. */
|
||||
ksv_lsb_addr = HDCP_KSV_LSB;
|
||||
ksv_msb_addr = HDCP_KSV_MSB;
|
||||
if (hdcp_ctrl->hdmi_tx_ver_4) {
|
||||
ksv_lsb_addr += HDCP_KSV_VERSION_4_OFFSET;
|
||||
ksv_msb_addr += HDCP_KSV_VERSION_4_OFFSET;
|
||||
}
|
||||
qfprom_aksv_lsb = DSS_REG_R(hdcp_ctrl->init_data.qfprom_io,
|
||||
ksv_lsb_addr);
|
||||
qfprom_aksv_msb = DSS_REG_R(hdcp_ctrl->init_data.qfprom_io,
|
||||
ksv_msb_addr);
|
||||
}
|
||||
|
||||
aksv[0] = qfprom_aksv_lsb & 0xFF;
|
||||
aksv[1] = (qfprom_aksv_lsb >> 8) & 0xFF;
|
||||
aksv[2] = (qfprom_aksv_lsb >> 16) & 0xFF;
|
||||
aksv[3] = (qfprom_aksv_lsb >> 24) & 0xFF;
|
||||
aksv[4] = qfprom_aksv_msb & 0xFF;
|
||||
|
||||
/* check there are 20 ones in AKSV */
|
||||
if (hdmi_hdcp_count_one(aksv, 5) != 20) {
|
||||
DEV_ERR("%s: %s: AKSV QFPROM doesn't have 20 1's, 20 0's\n",
|
||||
__func__, HDCP_STATE_NAME);
|
||||
DEV_ERR("%s: %s: QFPROM AKSV chk failed (AKSV=%02x%08x)\n",
|
||||
__func__, HDCP_STATE_NAME, qfprom_aksv_msb,
|
||||
qfprom_aksv_lsb);
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
DEV_DBG("%s: %s: AKSV=%02x%08x\n", __func__, HDCP_STATE_NAME,
|
||||
qfprom_aksv_msb, qfprom_aksv_lsb);
|
||||
|
||||
/*
|
||||
* Write AKSV read from QFPROM to the HDCP registers.
|
||||
* This step is needed for HDCP authentication and must be
|
||||
* written before enabling HDCP.
|
||||
*/
|
||||
DSS_REG_W(io, HDMI_HDCP_SW_LOWER_AKSV, qfprom_aksv_lsb);
|
||||
DSS_REG_W(io, HDMI_HDCP_SW_UPPER_AKSV, qfprom_aksv_msb);
|
||||
|
||||
/* Check to see if link0_Status has stale values for An ready bit */
|
||||
link0_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS);
|
||||
DEV_DBG("%s: %s: Before enabling cipher Link0_status=0x%08x\n",
|
||||
__func__, HDCP_STATE_NAME, link0_status);
|
||||
if (link0_status & (BIT(8) | BIT(9))) {
|
||||
DEV_DBG("%s: %s: An ready even before enabling HDCP\n",
|
||||
__func__, HDCP_STATE_NAME);
|
||||
stale_an = true;
|
||||
}
|
||||
/* Clear any DDC failures from previous tries */
|
||||
reset_hdcp_ddc_failures(hdcp_ctrl);
|
||||
|
||||
/*
|
||||
* Read BCAPS
|
||||
|
@ -387,35 +415,6 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl)
|
|||
hdcp_ctrl->current_tp.ds_type =
|
||||
(bcaps & BIT(6)) >> 6 ? DS_REPEATER : DS_RECEIVER;
|
||||
|
||||
/*
|
||||
* HDCP setup prior to enabling HDCP_CTRL.
|
||||
* Setup seed values for random number An.
|
||||
*/
|
||||
DSS_REG_W(io, HDMI_HDCP_ENTROPY_CTRL0, 0xB1FFB0FF);
|
||||
DSS_REG_W(io, HDMI_HDCP_ENTROPY_CTRL1, 0xF00DFACE);
|
||||
|
||||
/* Disable the RngCipher state */
|
||||
DSS_REG_W(io, HDMI_HDCP_DEBUG_CTRL,
|
||||
DSS_REG_R(io, HDMI_HDCP_DEBUG_CTRL) & ~(BIT(2)));
|
||||
DEV_DBG("%s: %s: HDCP_DEBUG_CTRL=0x%08x\n", __func__, HDCP_STATE_NAME,
|
||||
DSS_REG_R(io, HDMI_HDCP_DEBUG_CTRL));
|
||||
|
||||
/*
|
||||
* Ensure that all register writes are completed before
|
||||
* enabling HDCP cipher
|
||||
*/
|
||||
wmb();
|
||||
|
||||
/*
|
||||
* Enable HDCP
|
||||
* This needs to be done as early as possible in order for the
|
||||
* hardware to make An available to read
|
||||
*/
|
||||
DSS_REG_W(io, HDMI_HDCP_CTRL, BIT(0));
|
||||
|
||||
/* Clear any DDC failures from previous tries */
|
||||
reset_hdcp_ddc_failures(hdcp_ctrl);
|
||||
|
||||
/* Write BCAPS to the hardware */
|
||||
if (hdcp_ctrl->tz_hdcp) {
|
||||
memset(scm_buf, 0x00, sizeof(scm_buf));
|
||||
|
@ -437,19 +436,6 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl)
|
|||
DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA12, bcaps);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we had stale values for the An ready bit, it should most
|
||||
* likely be cleared now after enabling HDCP cipher
|
||||
*/
|
||||
link0_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS);
|
||||
DEV_DBG("%s: %s: After enabling HDCP Link0_Status=0x%08x\n",
|
||||
__func__, HDCP_STATE_NAME, link0_status);
|
||||
if (!(link0_status & (BIT(8) | BIT(9)))) {
|
||||
DEV_DBG("%s: %s: An not ready after enabling HDCP\n",
|
||||
__func__, HDCP_STATE_NAME);
|
||||
stale_an = false;
|
||||
}
|
||||
|
||||
/* Wait for HDCP keys to be checked and validated */
|
||||
timeout_count = 100;
|
||||
keys_state = (link0_status >> 28) & 0x7;
|
||||
|
@ -497,15 +483,8 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl)
|
|||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* In cases where An_ready bits had stale values, it would be
|
||||
* better to delay reading of An to avoid any potential of this
|
||||
* read being blocked
|
||||
*/
|
||||
if (stale_an) {
|
||||
msleep(200);
|
||||
stale_an = false;
|
||||
}
|
||||
/* As per hardware recommendations, wait before reading An */
|
||||
msleep(20);
|
||||
|
||||
/* Read An0 and An1 */
|
||||
link0_an_0 = DSS_REG_R(io, HDMI_HDCP_RCVPORT_DATA5);
|
||||
|
@ -1334,11 +1313,13 @@ int hdmi_hdcp_authenticate(void *input)
|
|||
|
||||
DEV_DBG("%s: %s: Queuing work to start HDCP authentication", __func__,
|
||||
HDCP_STATE_NAME);
|
||||
mutex_lock(hdcp_ctrl->init_data.mutex);
|
||||
hdcp_ctrl->hdcp_state = HDCP_STATE_AUTHENTICATING;
|
||||
mutex_unlock(hdcp_ctrl->init_data.mutex);
|
||||
queue_delayed_work(hdcp_ctrl->init_data.workq,
|
||||
&hdcp_ctrl->hdcp_auth_work, 0);
|
||||
|
||||
if (!hdmi_hdcp_load_keys(input))
|
||||
queue_delayed_work(hdcp_ctrl->init_data.workq,
|
||||
&hdcp_ctrl->hdcp_auth_work, HZ/2);
|
||||
else
|
||||
queue_work(hdcp_ctrl->init_data.workq,
|
||||
&hdcp_ctrl->hdcp_int_work);
|
||||
|
||||
return 0;
|
||||
} /* hdmi_hdcp_authenticate */
|
||||
|
@ -1380,14 +1361,12 @@ int hdmi_hdcp_reauthenticate(void *input)
|
|||
/* Disable encryption and disable the HDCP block */
|
||||
DSS_REG_W(io, HDMI_HDCP_CTRL, 0);
|
||||
|
||||
/* Restart authentication attempt */
|
||||
DEV_DBG("%s: %s: Scheduling work to start HDCP authentication",
|
||||
__func__, HDCP_STATE_NAME);
|
||||
mutex_lock(hdcp_ctrl->init_data.mutex);
|
||||
hdcp_ctrl->hdcp_state = HDCP_STATE_AUTHENTICATING;
|
||||
mutex_unlock(hdcp_ctrl->init_data.mutex);
|
||||
queue_delayed_work(hdcp_ctrl->init_data.workq,
|
||||
&hdcp_ctrl->hdcp_auth_work, HZ/2);
|
||||
if (!hdmi_hdcp_load_keys(input))
|
||||
queue_delayed_work(hdcp_ctrl->init_data.workq,
|
||||
&hdcp_ctrl->hdcp_auth_work, HZ/2);
|
||||
else
|
||||
queue_work(hdcp_ctrl->init_data.workq,
|
||||
&hdcp_ctrl->hdcp_int_work);
|
||||
|
||||
return ret;
|
||||
} /* hdmi_hdcp_reauthenticate */
|
||||
|
|
Loading…
Add table
Reference in a new issue