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

Commit 49f26446 authored by Ajay Singh Parmar's avatar Ajay Singh Parmar
Browse files

msm: mdss: hdmi: fix ddc for link integrity check



Make DDC hardware to poll for rxstatus during link integrity
check and acknowledge corresponding interrupts. Stop polling and
wait for timeout on getting new message, re-authentication required
or message ready interrupts based on rxstatus.

Change-Id: Ie8b1bc1b700589196f41b00ceb759458102f4064
Signed-off-by: default avatarAjay Singh Parmar <aparmar@codeaurora.org>
parent 6969441a
Loading
Loading
Loading
Loading
+51 −23
Original line number Diff line number Diff line
@@ -185,6 +185,8 @@ static void hdmi_hdcp2p2_off(void *input)

	flush_kthread_worker(&ctrl->worker);

	hdmi_hdcp2p2_ddc_disable(ctrl->init_data.ddc_ctrl);

	cdata.context = input;

	hdmi_hdcp2p2_wakeup(&cdata);
@@ -317,6 +319,8 @@ static void hdmi_hdcp2p2_auth_failed(struct hdmi_hdcp2p2_ctrl *ctrl)

	atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL);

	hdmi_hdcp2p2_ddc_disable(ctrl->init_data.ddc_ctrl);

	/* notify hdmi tx about HDCP failure */
	ctrl->init_data.notify_status(ctrl->init_data.cb_data,
		HDCP_STATE_AUTH_FAIL);
@@ -577,7 +581,7 @@ static void hdmi_hdcp2p2_recv_msg_work(struct kthread_work *work)
	ddc_data->timer_delay_lines = lines_needed_for_given_time;
	ddc_data->read_method = HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER;

	rc = hdmi_hdcp2p2_ddc_read_rxstatus(ddc_ctrl);
	rc = hdmi_hdcp2p2_ddc_read_rxstatus(ddc_ctrl, true);
	if (rc) {
		pr_err("error reading rxstatus %d\n", rc);
		goto exit;
@@ -615,6 +619,42 @@ exit:
	kfree(recvd_msg_buf);
}

static int hdmi_hdcp2p2_link_check(struct hdmi_hdcp2p2_ctrl *ctrl)
{
	struct hdmi_tx_ddc_ctrl *ddc_ctrl;
	struct hdmi_tx_hdcp2p2_ddc_data *ddc_data;
	struct msm_hdmi_mode_timing_info *timing;
	u32 fps, v_total;
	u32 time_taken_by_one_line_us;
	u32 lines_needed_for_given_time;

	ddc_ctrl = ctrl->init_data.ddc_ctrl;
	if (!ddc_ctrl)
		return -EINVAL;

	hdmi_ddc_config(ddc_ctrl);

	ddc_data = &ddc_ctrl->hdcp2p2_ddc_data;

	memset(ddc_data, 0, sizeof(*ddc_data));

	timing = ctrl->init_data.timing;
	fps = timing->refresh_rate / HDCP2P2_KHZ_TO_HZ;
	v_total = hdmi_tx_get_v_total(timing);
	time_taken_by_one_line_us = HDCP2P2_SEC_TO_US / (v_total * fps);
	lines_needed_for_given_time = (jiffies_to_msecs(HZ / 2) *
		HDCP2P2_MS_TO_US) / time_taken_by_one_line_us;

	pr_debug("timeout for rxstatus %d\n", lines_needed_for_given_time);

	ddc_data->intr_mask = RXSTATUS_READY | RXSTATUS_MESSAGE_SIZE |
		RXSTATUS_REAUTH_REQ;
	ddc_data->timer_delay_lines = lines_needed_for_given_time;
	ddc_data->read_method = HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER;

	return hdmi_hdcp2p2_ddc_read_rxstatus(ddc_ctrl, false);
}

static void hdmi_hdcp2p2_auth_status_work(struct kthread_work *work)
{
	struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
@@ -649,7 +689,7 @@ static void hdmi_hdcp2p2_auth_status_work(struct kthread_work *work)
		ctrl->init_data.notify_status(ctrl->init_data.cb_data,
			HDCP_STATE_AUTHENTICATED);

		/* recheck within 1sec as per hdcp 2.2 standard */
		if (!hdmi_hdcp2p2_link_check(ctrl))
			schedule_delayed_work(&ctrl->link_check_work,
				msecs_to_jiffies(HDCP2P2_LINK_CHECK_TIME_MS));
	}
@@ -672,11 +712,9 @@ static void hdmi_hdcp2p2_link_work(struct kthread_work *work)
	struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
		struct hdmi_hdcp2p2_ctrl, link);
	struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
	struct hdmi_tx_ddc_ctrl *ddc_ctrl;
	struct hdmi_tx_hdcp2p2_ddc_data *ddc_data;
	u64 mult;
	struct msm_hdmi_mode_timing_info *timing;
	char *recvd_msg_buf = NULL;
	struct hdmi_tx_hdcp2p2_ddc_data *ddc_data;
	struct hdmi_tx_ddc_ctrl *ddc_ctrl;

	if (!ctrl) {
		pr_err("invalid input\n");
@@ -694,26 +732,14 @@ static void hdmi_hdcp2p2_link_work(struct kthread_work *work)
		goto exit;
	}

	hdmi_ddc_config(ddc_ctrl);

	ddc_data = &ddc_ctrl->hdcp2p2_ddc_data;

	memset(ddc_data, 0, sizeof(*ddc_data));

	timing = ctrl->init_data.timing;
	mult = hdmi_tx_get_v_total(timing) / 20;
	ddc_data->intr_mask = RXSTATUS_READY | RXSTATUS_MESSAGE_SIZE |
		RXSTATUS_REAUTH_REQ;
	ddc_data->timer_delay_lines = (u32)mult;
	ddc_data->read_method = HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER;

	rc = hdmi_hdcp2p2_ddc_read_rxstatus(ddc_ctrl);
	rc = hdmi_ddc_check_status(ddc_ctrl);
	if (rc) {
		pr_err("error reading rxstatus %d\n", rc);
		cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
		goto exit;
	}

	ddc_data = &ddc_ctrl->hdcp2p2_ddc_data;

	if (ddc_data->reauth_req) {
		pr_debug("sync reported loss of synchronization, reauth\n");
		rc = -ENOLINK;
@@ -740,6 +766,8 @@ static void hdmi_hdcp2p2_link_work(struct kthread_work *work)
			cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS;
			cdata.recvd_msg_buf = recvd_msg_buf;
			cdata.recvd_msg_len = ddc_data->message_size;

			hdmi_hdcp2p2_link_check(ctrl);
		}
	}
exit:
+70 −53
Original line number Diff line number Diff line
@@ -745,6 +745,48 @@ 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_check_status(struct hdmi_tx_ddc_ctrl *ctrl)
{
	int rc = 0;
	u32 reg_val;

	if (!ctrl) {
		pr_err("invalid ddc ctrl\n");
		return -EINVAL;
	}

	/* check for errors and clear status */
	reg_val = DSS_REG_R(ctrl->io, HDMI_HDCP2P2_DDC_STATUS);

	if (reg_val & BIT(4)) {
		pr_err("ddc aborted\n");
		reg_val |= BIT(5);
		rc = -ECONNABORTED;
	}

	if (reg_val & BIT(8)) {
		pr_err("timed out\n");
		reg_val |= BIT(9);
		rc = -ETIMEDOUT;
	}

	if (reg_val & BIT(12)) {
		pr_err("NACK0\n");
		reg_val |= BIT(13);
		rc = -EIO;
	}

	if (reg_val & BIT(14)) {
		pr_err("NACK1\n");
		reg_val |= BIT(15);
		rc = -EIO;
	}

	DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_STATUS, reg_val);

	return rc;
}

static int hdmi_ddc_hdcp2p2_isr(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
{
	struct dss_io_data *io = NULL;
@@ -808,12 +850,14 @@ static int hdmi_ddc_hdcp2p2_isr(struct hdmi_tx_ddc_ctrl *ddc_ctrl)

	/* check for message size interrupt */
	if (intr0 & BIT(31)) {
		/* get the message size bits 29:20 */
		data->message_size = (intr0 & (0x3FF << 20)) >> 20;

		if (data->message_size) {
			/* ack and disable message size interrupt */
			intr0 |= BIT(30);
			intr0 &= ~BIT(31);

		/* get the message size bits 29:20 */
		data->message_size = (intr0 & (0x3FF << 20)) >> 20;
		}
	}

	/* check for ready/not ready interrupt */
@@ -845,35 +889,34 @@ static int hdmi_ddc_hdcp2p2_isr(struct hdmi_tx_ddc_ctrl *ddc_ctrl)

	/* check for ddc fail interrupt */
	if (intr0 & BIT(10)) {
		/* ack and disable ddc fail interrupt */
		/* ack ddc fail interrupt */
		intr0 |= BIT(9);
		intr0 &= ~BIT(10);

		data->ddc_max_retries_fail = (intr0 & BIT(8)) ? true : false;
	}

	/* check for ddc done interrupt */
	if (intr0 & BIT(6)) {
		/* ack and disable ddc done interrupt */
		/* ack ddc done interrupt */
		intr0 |= BIT(5);
		intr0 &= ~BIT(6);

		data->ddc_done = (intr0 & BIT(4)) ? true : false;
	}

	/* check for ddc read req interrupt */
	if (intr0 & BIT(2)) {
		/* ack and disable read req interrupt */
		/* ack read req interrupt */
		intr0 |= BIT(1);
		intr0 &= ~BIT(2);

		data->ddc_read_req = (intr0 & BIT(0)) ? true : false;
	}

	DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL0, intr0);

	if (data->message_size || data->ready || data->reauth_req) {
		if (!completion_done(&ddc_ctrl->rxstatus_completion))
			complete_all(&ddc_ctrl->rxstatus_completion);
	}

	return 0;
}
@@ -1587,7 +1630,7 @@ void hdmi_hdcp2p2_ddc_disable(struct hdmi_tx_ddc_ctrl *ctrl)
	DSS_REG_W(ctrl->io, HDMI_HW_DDC_CTRL, reg_val);
}

int hdmi_hdcp2p2_ddc_read_rxstatus(struct hdmi_tx_ddc_ctrl *ctrl)
int hdmi_hdcp2p2_ddc_read_rxstatus(struct hdmi_tx_ddc_ctrl *ctrl, bool wait)
{
	u32 reg_val;
	u32 intr_en_mask;
@@ -1631,7 +1674,7 @@ int hdmi_hdcp2p2_ddc_read_rxstatus(struct hdmi_tx_ddc_ctrl *ctrl)
	 * 3. DDC_TIMEOUT_TIMER: Timeout in hsyncs which starts counting when
	 *	a request is made and stops when it is accepted by DDC arbiter
	 */
	timeout = data->timer_delay_lines & 0xffff;
	timeout = data->timer_delay_lines;
	pr_debug("timeout: %d\n", timeout);
	DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_TIMER_CTRL, timeout);

@@ -1681,6 +1724,7 @@ int hdmi_hdcp2p2_ddc_read_rxstatus(struct hdmi_tx_ddc_ctrl *ctrl)
		/* If we are using SW_TRIGGER, then go ahead and trigger it */
		DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_SW_TRIGGER, 1);

		if (wait) {
			reinit_completion(&ctrl->rxstatus_completion);
			/* max timeout as per hdcp 2.2 std is 1 sec */
			timeout = wait_for_completion_timeout(
@@ -1690,39 +1734,12 @@ int hdmi_hdcp2p2_ddc_read_rxstatus(struct hdmi_tx_ddc_ctrl *ctrl)
				pr_err("sw ddc rxstatus timeout\n");
				rc = -ETIMEDOUT;
			}
	}

	/* check for errors and clear status */
	reg_val = DSS_REG_R(ctrl->io, HDMI_HDCP2P2_DDC_STATUS);

	if (reg_val & BIT(4)) {
		pr_err("ddc aborted\n");
		reg_val |= BIT(5);
		rc = -ECONNABORTED;
	}
			rc = hdmi_ddc_check_status(ctrl);

	if (reg_val & BIT(8)) {
		pr_err("timed out\n");
		reg_val |= BIT(9);
		rc = -ETIMEDOUT;
			hdmi_hdcp2p2_ddc_disable(ctrl);
		}

	if (reg_val & BIT(12)) {
		pr_err("NACK0\n");
		reg_val |= BIT(13);
		rc = -EIO;
	}

	if (reg_val & BIT(14)) {
		pr_err("NACK1\n");
		reg_val |= BIT(15);
		rc = -EIO;
	}

	DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_STATUS, reg_val);

	/* Disable hardware access to RxStatus register */
	hdmi_hdcp2p2_ddc_disable(ctrl);

	return rc;
}
+2 −1
Original line number Diff line number Diff line
@@ -490,6 +490,7 @@ int hdmi_setup_ddc_timers(struct hdmi_tx_ddc_ctrl *ctrl,
			  u32 type, u32 to_in_num_lines);
void hdmi_hdcp2p2_ddc_reset(struct hdmi_tx_ddc_ctrl *ctrl);
void hdmi_hdcp2p2_ddc_disable(struct hdmi_tx_ddc_ctrl *ctrl);
int hdmi_hdcp2p2_ddc_read_rxstatus(struct hdmi_tx_ddc_ctrl *ctrl);
int hdmi_hdcp2p2_ddc_read_rxstatus(struct hdmi_tx_ddc_ctrl *ctrl, bool wait);
int hdmi_ddc_check_status(struct hdmi_tx_ddc_ctrl *ctrl);

#endif /* __HDMI_UTIL_H__ */