Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3d4d5668 authored by Ajay Singh Parmar's avatar Ajay Singh Parmar
Browse files

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: default avatarAjay Singh Parmar <aparmar@codeaurora.org>
parent a8371783
Loading
Loading
Loading
Loading
+114 −135
Original line number Diff line number Diff line
@@ -245,120 +245,148 @@ static int hdcp_scm_call(struct scm_hdcp_req *req, u32 *resp)
	return ret;
}

static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl)
static int hdmi_hdcp_load_keys(void *input)
{
	int rc;
	int rc = 0;
	bool use_sw_keys = false;
	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;
	struct dss_io_data *io;
	struct dss_io_data *hdcp_io;
	u8 aksv[5], *bksv = NULL;
	u8 an[8];
	u8 bcaps;
	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;
	u32 ret  = 0;
	u32 resp = 0;
	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 error;
		goto end;
	}

	phy_addr = hdcp_ctrl->init_data.phy_addr;
	bksv = hdcp_ctrl->current_tp.bksv;
	io = hdcp_ctrl->init_data.core_io;
	hdcp_io = hdcp_ctrl->init_data.hdcp_io;

	if (HDCP_STATE_AUTHENTICATING != hdcp_ctrl->hdcp_state) {
	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 error;
		goto end;
	}

	/* On compatible hardware, use SW aksv */
	reg_val = DSS_REG_R(hdcp_ctrl->init_data.qfprom_io,
			SEC_CTRL_HW_VERSION);
	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(hdcp_ctrl->init_data.qfprom_io,
		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(&qfprom_aksv_msb, &qfprom_aksv_lsb)) {
			pr_err("%s: setting of hdcp SW keys failed\n",
						__func__);
		if (hdcp1_set_keys(&aksv_msb, &aksv_lsb)) {
			pr_err("%s: setting hdcp SW keys failed\n", __func__);
			rc = -EINVAL;
			goto error;
			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;
		}
		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_lsb = DSS_REG_R(qfprom_io, ksv_lsb_addr);
		aksv_msb = DSS_REG_R(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;
	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: %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);
		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 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;
	struct dss_io_data *io;
	struct dss_io_data *hdcp_io;
	u8 aksv[5], *bksv = NULL;
	u8 an[8];
	u8 bcaps;
	struct hdmi_tx_ddc_data ddc_data;
	u32 link0_status, an_ready, keys_state;
	u8 buf[0xFF];

	struct scm_hdcp_req scm_buf[SCM_HDCP_MAX_REG];
	u32 phy_addr;
	u32 ret  = 0;
	u32 resp = 0;

	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 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);
	phy_addr = hdcp_ctrl->init_data.phy_addr;
	bksv = hdcp_ctrl->current_tp.bksv;
	io = hdcp_ctrl->init_data.core_io;
	hdcp_io = hdcp_ctrl->init_data.hdcp_io;

	/* 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;
	if (HDCP_STATE_AUTHENTICATING != hdcp_ctrl->hdcp_state) {
		DEV_ERR("%s: %s: invalid state. returning\n", __func__,
			HDCP_STATE_NAME);
		rc = -EINVAL;
		goto error;
	}

	/* Clear any DDC failures from previous tries */
	reset_hdcp_ddc_failures(hdcp_ctrl);

	/*
	 * Read BCAPS
	 * We need to first try to read an HDCP register on the sink to see if
@@ -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);

	if (!hdmi_hdcp_load_keys(input))
		queue_delayed_work(hdcp_ctrl->init_data.workq,
		&hdcp_ctrl->hdcp_auth_work, 0);
			&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);
	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 */