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

Commit df433782 authored by Tatenda Chipeperekwa's avatar Tatenda Chipeperekwa
Browse files

drm/msm/hdcp: fix the HDCP 2.2 compliance for DisplayPort



Fix the HDCP 2.2 protocol module by adding support to handle
response time outs, restarting authentication for failed repeater
use cases, and honoring requests from the TrustZone to resend certain
messages. These fixes are needed in order to pass HDCP 2.2 compliance for
DisplayPort.

CRs-Fixed: 2284465
Change-Id: Ib9fdd2390721bb1c7470138fe1f605efac1e3bc3
Signed-off-by: default avatarTatenda Chipeperekwa <tatendac@codeaurora.org>
parent fd273d2c
Loading
Loading
Loading
Loading
+19 −20
Original line number Diff line number Diff line
@@ -144,6 +144,24 @@ static irqreturn_t dp_display_irq(int irq, void *dev_id)

	return IRQ_HANDLED;
}
static bool dp_display_is_ds_bridge(struct dp_panel *panel)
{
	return (panel->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
		DP_DWN_STRM_PORT_PRESENT);
}

static bool dp_display_is_sink_count_zero(struct dp_display_private *dp)
{
	return dp_display_is_ds_bridge(dp->panel) &&
		(dp->link->sink_count.count == 0);
}

static bool dp_display_is_ready(struct dp_display_private *dp)
{
	return dp->hpd->hpd_high && dp->is_connected &&
		!dp_display_is_sink_count_zero(dp) &&
		dp->hpd->alt_mode_cfg_done;
}

static void dp_display_hdcp_cb_work(struct work_struct *work)
{
@@ -173,7 +191,7 @@ static void dp_display_hdcp_cb_work(struct work_struct *work)
			rc = dp->hdcp.ops->authenticate(dp->hdcp.data);
		break;
	case HDCP_STATE_AUTH_FAIL:
		if (dp->power_on) {
		if (dp_display_is_ready(dp) && dp->power_on) {
			if (ops && ops->reauthenticate) {
				rc = ops->reauthenticate(dp->hdcp.data);
				if (rc)
@@ -390,18 +408,6 @@ static const struct component_ops dp_display_comp_ops = {
	.unbind = dp_display_unbind,
};

static bool dp_display_is_ds_bridge(struct dp_panel *panel)
{
	return (panel->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
		DP_DWN_STRM_PORT_PRESENT);
}

static bool dp_display_is_sink_count_zero(struct dp_display_private *dp)
{
	return dp_display_is_ds_bridge(dp->panel) &&
		(dp->link->sink_count.count == 0);
}

static void dp_display_send_hpd_event(struct dp_display_private *dp)
{
	struct drm_device *dev = NULL;
@@ -1239,13 +1245,6 @@ static int dp_display_set_mode(struct dp_display *dp_display, void *panel,
	return 0;
}

static bool dp_display_is_ready(struct dp_display_private *dp)
{
	return dp->hpd->hpd_high && dp->is_connected &&
		!dp_display_is_sink_count_zero(dp) &&
		dp->hpd->alt_mode_cfg_done;
}

static int dp_display_prepare(struct dp_display *dp_display, void *panel)
{
	struct dp_display_private *dp;
+118 −32
Original line number Diff line number Diff line
@@ -55,10 +55,13 @@
struct sde_hdcp_2x_ctrl {
	struct hdcp2_app_data app_data;
	u32 timeout_left;
	u32 wait_timeout_ms;
	u32 total_message_length;
	bool no_stored_km;
	bool feature_supported;
	bool authenticated;
	bool resend_lc_init;
	bool resend_stream_manage;
	void *client_data;
	void *hdcp2_ctx;
	struct hdcp_transport_ops *client_ops;
@@ -71,7 +74,7 @@ struct sde_hdcp_2x_ctrl {
	enum sde_hdcp_2x_device_type device_type;

	struct task_struct *thread;
	struct completion topo_wait;
	struct completion response_completion;

	struct kthread_worker worker;
	struct kthread_work wk_init;
@@ -80,6 +83,7 @@ struct sde_hdcp_2x_ctrl {
	struct kthread_work wk_timeout;
	struct kthread_work wk_clean;
	struct kthread_work wk_stream;
	struct kthread_work wk_wait;
};

static const char *sde_hdcp_2x_message_name(int msg_id)
@@ -217,6 +221,9 @@ static int sde_hdcp_2x_get_next_message(struct sde_hdcp_2x_ctrl *hdcp,
	case LC_INIT:
		return LC_SEND_L_PRIME;
	case LC_SEND_L_PRIME:
		if (hdcp->resend_lc_init)
			return LC_INIT;
		else
			return SKE_SEND_EKS;
	case SKE_SEND_EKS:
		if (!hdcp->repeater_flag)
@@ -234,6 +241,9 @@ static int sde_hdcp_2x_get_next_message(struct sde_hdcp_2x_ctrl *hdcp,
	case REP_SEND_RECV_ID_LIST:
		return REP_SEND_ACK;
	case REP_STREAM_MANAGE:
		if (hdcp->resend_stream_manage)
			return REP_STREAM_MANAGE;
		else
			return REP_STREAM_READY;
	default:
		pr_err("Uknown message ID (%d)", hdcp->last_msg);
@@ -241,13 +251,41 @@ static int sde_hdcp_2x_get_next_message(struct sde_hdcp_2x_ctrl *hdcp,
	}
}

static void sde_hdcp_2x_wait_for_response(struct sde_hdcp_2x_ctrl *hdcp)
{
	switch (hdcp->last_msg) {
	case AKE_SEND_H_PRIME:
		if (hdcp->no_stored_km)
			hdcp->wait_timeout_ms = HZ;
		else
			hdcp->wait_timeout_ms = HZ / 4;
		break;
	case AKE_SEND_PAIRING_INFO:
		hdcp->wait_timeout_ms = HZ / 4;
		break;
	case REP_SEND_RECV_ID_LIST:
		if (!hdcp->authenticated)
			hdcp->wait_timeout_ms = HZ * 3;
		else
			hdcp->wait_timeout_ms = 0;
		break;
	default:
		hdcp->wait_timeout_ms = 0;
	}

	if (hdcp->wait_timeout_ms)
		HDCP_2X_EXECUTE(wait);
}

static void sde_hdcp_2x_wakeup_client(struct sde_hdcp_2x_ctrl *hdcp,
				struct hdcp_transport_wakeup_data *data)
{
	int rc = 0;

	if (hdcp && hdcp->client_ops && hdcp->client_ops->wakeup &&
	    data && (data->cmd != HDCP_TRANSPORT_CMD_INVALID)) {
	if (!hdcp || !hdcp->client_ops || !hdcp->client_ops->wakeup ||
			!data || (data->cmd == HDCP_TRANSPORT_CMD_INVALID))
		return;

	data->abort_mask = REAUTH_REQ | LINK_INTEGRITY_FAILURE;

	if (data->cmd == HDCP_TRANSPORT_CMD_SEND_MESSAGE ||
@@ -267,7 +305,8 @@ static void sde_hdcp_2x_wakeup_client(struct sde_hdcp_2x_ctrl *hdcp,
	if (rc)
		pr_err("error sending %s to client\n",
				hdcp_transport_cmd_to_str(data->cmd));
	}

	sde_hdcp_2x_wait_for_response(hdcp);
}

static inline void sde_hdcp_2x_send_message(struct sde_hdcp_2x_ctrl *hdcp)
@@ -432,7 +471,8 @@ static void sde_hdcp_2x_msg_sent(struct sde_hdcp_2x_ctrl *hdcp)
		}
		break;
	case REP_SEND_ACK:
		pr_debug("Repeater authentication successful\n");
		pr_debug("Repeater authentication successful. update_stream=%d\n",
				hdcp->update_stream);

		if (hdcp->update_stream) {
			HDCP_2X_EXECUTE(stream);
@@ -572,7 +612,8 @@ static void sde_hdcp_2x_msg_recvd(struct sde_hdcp_2x_ctrl *hdcp)
	rc = hdcp2_app_comm(hdcp->hdcp2_ctx, HDCP2_CMD_PROCESS_MSG,
			&hdcp->app_data);
	if (rc) {
		pr_err("failed to process message from sink (%d)\n", rc);
		pr_err("failed to process sink's response to %s (%d)\n",
				sde_hdcp_2x_message_name(msg[0]), rc);
		rc = -EINVAL;
		goto exit;
	}
@@ -584,7 +625,12 @@ static void sde_hdcp_2x_msg_recvd(struct sde_hdcp_2x_ctrl *hdcp)
		goto exit;
	}

	if (msg[0] == REP_STREAM_READY) {
	out_msg = (u32)hdcp->app_data.response.data[0];

	pr_debug("message received from TZ: %s\n",
			sde_hdcp_2x_message_name(out_msg));

	if (msg[0] == REP_STREAM_READY && out_msg != REP_STREAM_MANAGE) {
		if (!hdcp->authenticated) {
			rc = hdcp2_app_comm(hdcp->hdcp2_ctx,
					HDCP2_CMD_EN_ENCRYPTION,
@@ -605,10 +651,17 @@ static void sde_hdcp_2x_msg_recvd(struct sde_hdcp_2x_ctrl *hdcp)
		goto exit;
	}

	out_msg = (u32)hdcp->app_data.response.data[0];
	hdcp->resend_lc_init = false;
	if (msg[0] == LC_SEND_L_PRIME && out_msg == LC_INIT) {
		pr_debug("resend %s\n", sde_hdcp_2x_message_name(out_msg));
		hdcp->resend_lc_init = true;
	}

	pr_debug("message received from TZ: %s\n",
			sde_hdcp_2x_message_name(out_msg));
	hdcp->resend_stream_manage = false;
	if (msg[0] == REP_STREAM_READY && out_msg == REP_STREAM_MANAGE) {
		pr_debug("resend %s\n", sde_hdcp_2x_message_name(out_msg));
		hdcp->resend_stream_manage = true;
	}

	if (out_msg == AKE_NO_STORED_KM)
		hdcp->no_stored_km = 1;
@@ -643,6 +696,36 @@ static void sde_hdcp_2x_msg_recvd_work(struct kthread_work *work)
	sde_hdcp_2x_msg_recvd(hdcp);
}

static void sde_hdcp_2x_wait_for_response_work(struct kthread_work *work)
{
	u32 timeout;
	struct sde_hdcp_2x_ctrl *hdcp = container_of(work,
			struct sde_hdcp_2x_ctrl, wk_wait);

	if (!hdcp) {
		pr_err("invalid input\n");
		return;
	}

	if (atomic_read(&hdcp->hdcp_off)) {
		pr_debug("invalid state: hdcp off\n");
		return;
	}

	reinit_completion(&hdcp->response_completion);
	timeout = wait_for_completion_timeout(&hdcp->response_completion,
			hdcp->wait_timeout_ms);
	if (!timeout) {
		pr_err("completion expired, last message = %s\n",
				sde_hdcp_2x_message_name(hdcp->last_msg));

		if (!atomic_read(&hdcp->hdcp_off))
			HDCP_2X_EXECUTE(clean);
	}

	hdcp->wait_timeout_ms = 0;
}

static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data)
{
	struct sde_hdcp_2x_ctrl *hdcp;
@@ -664,11 +747,14 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data)
	pr_debug("%s\n", sde_hdcp_2x_cmd_to_str(hdcp->wakeup_cmd));

	rc = sde_hdcp_2x_check_valid_state(hdcp);
	if (rc)
	if (rc) {
		pr_err("invalid state for command=%s\n",
				sde_hdcp_2x_cmd_to_str(hdcp->wakeup_cmd));
		goto exit;
	}

	if (!completion_done(&hdcp->topo_wait))
		complete_all(&hdcp->topo_wait);
	if (!completion_done(&hdcp->response_completion))
		complete_all(&hdcp->response_completion);

	switch (hdcp->wakeup_cmd) {
	case HDCP_2X_CMD_START:
@@ -683,7 +769,6 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data)
		break;
	case HDCP_2X_CMD_STOP:
		atomic_set(&hdcp->hdcp_off, 1);

		HDCP_2X_EXECUTE(clean);
		break;
	case HDCP_2X_CMD_MSG_SEND_SUCCESS:
@@ -765,8 +850,9 @@ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data)
	kthread_init_work(&hdcp->wk_timeout,   sde_hdcp_2x_timeout_work);
	kthread_init_work(&hdcp->wk_clean,     sde_hdcp_2x_cleanup_work);
	kthread_init_work(&hdcp->wk_stream,    sde_hdcp_2x_query_stream_work);
	kthread_init_work(&hdcp->wk_wait, sde_hdcp_2x_wait_for_response_work);

	init_completion(&hdcp->topo_wait);
	init_completion(&hdcp->response_completion);

	*data->hdcp_data = hdcp;