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

Commit a2afa385 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: mdss: hdmi: link integrity check"

parents 3d326f40 ca8f46e9
Loading
Loading
Loading
Loading
+108 −56
Original line number Diff line number Diff line
@@ -333,9 +333,11 @@ struct hdcp_lib_handle {
	uint32_t tz_ctxhandle;
	uint32_t hdcp_timeout;
	bool no_stored_km_flag;
	bool feature_supported;
	void *client_ctx;
	struct hdcp_client_ops *client_ops;
	struct mutex hdcp_lock;
	struct mutex wakeup_mutex;
	enum hdcp_state hdcp_state;
	enum hdcp_lib_wakeup_cmd wakeup_cmd;
	bool repeater_flag;
@@ -343,9 +345,10 @@ struct hdcp_lib_handle {
	int last_msg_sent;
	char *last_msg_recvd_buf;
	uint32_t last_msg_recvd_len;
	atomic_t hdcp_off_pending;
	atomic_t hdcp_off;

	struct task_struct *thread;
	struct completion topo_wait;

	struct kthread_worker worker;
	struct kthread_work init;
@@ -353,6 +356,8 @@ struct hdcp_lib_handle {
	struct kthread_work msg_recvd;
	struct kthread_work timeout;
	struct kthread_work clean;
	struct kthread_work topology;
	struct kthread_work stream;
};

struct hdcp_lib_message_map {
@@ -456,7 +461,7 @@ static int hdcp_lib_enable_encryption(struct hdcp_lib_handle *handle)
	pr_debug("success\n");
	return 0;
error:
	if (!atomic_read(&handle->hdcp_off_pending))
	if (!atomic_read(&handle->hdcp_off))
		queue_kthread_work(&handle->worker, &handle->clean);

	return rc;
@@ -668,30 +673,20 @@ exit:
	return rc;
}

static int hdcp_lib_query_stream_type(void *phdcpcontext)
static void hdcp_lib_query_stream_type_work(struct kthread_work *work)
{
	int rc = 0;
	struct hdcp_query_stream_type_req *req_buf;
	struct hdcp_query_stream_type_rsp *rsp_buf;
	struct hdcp_lib_handle *handle = phdcpcontext;
	struct hdcp_lib_handle *handle = container_of(work,
		struct hdcp_lib_handle, stream);

	if (!handle) {
		pr_err("invalid input\n");
		return -EINVAL;
	}

	if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) {
		pr_debug("hdcp library not loaded\n");
		goto exit;
	}

	if (!(handle->hdcp_state & HDCP_STATE_TXMTR_INIT)) {
		pr_err("txmtr is not initialized\n");
		rc = -EINVAL;
		goto exit;
		pr_err("invalid handle\n");
		return;
	}

	flush_kthread_worker(&handle->worker);
	mutex_lock(&handle->hdcp_lock);

	/* send command to TZ */
	req_buf = (struct hdcp_query_stream_type_req *)handle->
@@ -715,7 +710,7 @@ static int hdcp_lib_query_stream_type(void *phdcpcontext)
		goto exit;
	}

	pr_debug("message received is %s\n",
	pr_debug("message received from TZ: %s\n",
		hdcp_lib_message_name((int)rsp_buf->msg[0]));

	memset(handle->listener_buf, 0, MAX_TX_MESSAGE_SIZE);
@@ -723,15 +718,11 @@ static int hdcp_lib_query_stream_type(void *phdcpcontext)
			rsp_buf->msglen);
	handle->hdcp_timeout = rsp_buf->timeout;
	handle->msglen = rsp_buf->msglen;
exit:
	mutex_unlock(&handle->hdcp_lock);

	if (!atomic_read(&handle->hdcp_off_pending))
	if (!rc && !atomic_read(&handle->hdcp_off))
		hdcp_lib_send_message(handle);
	else
		goto exit;

	pr_debug("success\n");
exit:
	return rc;
}

static bool hdcp_lib_client_feature_supported(void *phdcpcontext)
@@ -745,9 +736,15 @@ static bool hdcp_lib_client_feature_supported(void *phdcpcontext)
		goto exit;
	}

	if (handle->feature_supported) {
		supported = true;
		goto exit;
	}

	rc = hdcp_lib_library_load(handle);
	if (!rc) {
		pr_debug("HDCP2p2 supported\n");
		handle->feature_supported = true;
		hdcp_lib_library_unload(handle);
		supported = true;
	}
@@ -767,9 +764,11 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data)
	if (!handle)
		return -EINVAL;

	mutex_lock(&handle->wakeup_mutex);

	handle->wakeup_cmd = data->cmd;

	pr_debug("wakeup_cmd: %s\n", hdcp_lib_cmd_to_str(handle->wakeup_cmd));
	pr_debug("cmd: %s\n", hdcp_lib_cmd_to_str(handle->wakeup_cmd));

	if (data->recvd_msg_len) {
		handle->last_msg_recvd_len = data->recvd_msg_len;
@@ -785,42 +784,51 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data)
			data->recvd_msg_len);
	}

	if (!completion_done(&handle->topo_wait))
		complete_all(&handle->topo_wait);

	switch (handle->wakeup_cmd) {
	case HDCP_LIB_WKUP_CMD_START:
		handle->no_stored_km_flag = 0;
		handle->repeater_flag = 0;
		handle->last_msg_sent = 0;
		atomic_set(&handle->hdcp_off, 0);
		handle->hdcp_state = HDCP_STATE_INIT;

		if (!atomic_read(&handle->hdcp_off_pending))
		queue_kthread_work(&handle->worker, &handle->init);
		break;
	case HDCP_LIB_WKUP_CMD_STOP:
		atomic_set(&handle->hdcp_off_pending, 1);
		atomic_set(&handle->hdcp_off, 1);
		queue_kthread_work(&handle->worker, &handle->clean);
		break;
	case HDCP_LIB_WKUP_CMD_MSG_SEND_SUCCESS:
		handle->last_msg_sent = handle->listener_buf[0];

		if (!atomic_read(&handle->hdcp_off_pending))
		if (!atomic_read(&handle->hdcp_off))
			queue_kthread_work(&handle->worker, &handle->msg_sent);
		break;
	case HDCP_LIB_WKUP_CMD_MSG_SEND_FAILED:
	case HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED:
		if (!atomic_read(&handle->hdcp_off_pending))
		if (!atomic_read(&handle->hdcp_off))
			queue_kthread_work(&handle->worker, &handle->clean);
		break;
	case HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS:
		if (!atomic_read(&handle->hdcp_off_pending))
		if (!atomic_read(&handle->hdcp_off))
			queue_kthread_work(&handle->worker, &handle->msg_recvd);
		break;
	case HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT:
		if (!atomic_read(&handle->hdcp_off_pending))
		if (!atomic_read(&handle->hdcp_off))
			queue_kthread_work(&handle->worker, &handle->timeout);
		break;
	case HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE:
		if (!atomic_read(&handle->hdcp_off))
			queue_kthread_work(&handle->worker, &handle->stream);
		break;
	default:
		pr_err("invalid wakeup command %d\n", handle->wakeup_cmd);
	}
exit:
	mutex_unlock(&handle->wakeup_mutex);
	return 0;
}

@@ -835,28 +843,41 @@ static void hdcp_lib_msg_sent_work(struct kthread_work *work)
		return;
	}

	if (handle->wakeup_cmd != HDCP_LIB_WKUP_CMD_MSG_SEND_SUCCESS) {
		pr_err("invalid wakeup command %d\n", handle->wakeup_cmd);
		return;
	}

	mutex_lock(&handle->hdcp_lock);

	cdata.context = handle->client_ctx;

	if (handle->wakeup_cmd == HDCP_LIB_WKUP_CMD_MSG_SEND_SUCCESS) {
		if (handle->last_msg_sent == SKE_SEND_EKS_MESSAGE_ID) {
	switch (handle->last_msg_sent) {
	case SKE_SEND_EKS_MESSAGE_ID:
		if (handle->repeater_flag) {
			if (!atomic_read(&handle->hdcp_off))
				queue_kthread_work(&handle->worker,
					&handle->topology);
		}

		if (!hdcp_lib_enable_encryption(handle))
			cdata.cmd = HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS;
		else
				cdata.cmd = HDMI_HDCP_WKUP_CMD_STATUS_FAILED;
		} else {
			if (!atomic_read(&handle->hdcp_off))
				queue_kthread_work(&handle->worker,
					&handle->clean);
		break;
	case REPEATER_AUTH_SEND_ACK_MESSAGE_ID:
		pr_debug("Repeater authentication successful\n");
		break;
	default:
		cdata.cmd = HDMI_HDCP_WKUP_CMD_RECV_MESSAGE;
		cdata.timeout = handle->hdcp_timeout;
	}
	} else {
		pr_err("invalid wakeup command %d\n", handle->wakeup_cmd);
	}

	mutex_unlock(&handle->hdcp_lock);

	hdcp_lib_wakeup_client(handle, &cdata);
	return;
}

static void hdcp_lib_init_work(struct kthread_work *work)
@@ -900,7 +921,7 @@ exit:
	if (send_msg)
		hdcp_lib_send_message(handle);

	if (rc && !atomic_read(&handle->hdcp_off_pending))
	if (rc && !atomic_read(&handle->hdcp_off))
		queue_kthread_work(&handle->worker, &handle->clean);
	return;
}
@@ -954,7 +975,7 @@ static void hdcp_lib_manage_timeout_work(struct kthread_work *work)
	if ((rsp_buf->commandid == HDCP_TXMTR_PROCESS_RECEIVED_MESSAGE) &&
		((int)rsp_buf->message[0] == LC_INIT_MESSAGE_ID) &&
			(rsp_buf->msglen == LC_INIT_MESSAGE_SIZE)) {
		if (!atomic_read(&handle->hdcp_off_pending)) {
		if (!atomic_read(&handle->hdcp_off)) {
			/* keep local copy of TZ response */
			memset(handle->listener_buf, 0, MAX_TX_MESSAGE_SIZE);
			memcpy(handle->listener_buf,
@@ -972,7 +993,7 @@ error:
	if (send_msg)
		hdcp_lib_send_message(handle);

	if (rc && !atomic_read(&handle->hdcp_off_pending))
	if (rc && !atomic_read(&handle->hdcp_off))
		queue_kthread_work(&handle->worker, &handle->clean);
}

@@ -997,10 +1018,10 @@ static void hdcp_lib_cleanup_work(struct kthread_work *work)

	mutex_unlock(&handle->hdcp_lock);

	if (atomic_read(&handle->hdcp_off_pending))
		atomic_set(&handle->hdcp_off_pending, 0);
	else
	if (!atomic_read(&handle->hdcp_off))
		hdcp_lib_wakeup_client(handle, &cdata);

	atomic_set(&handle->hdcp_off, 1);
}

static void hdcp_lib_msg_recvd_work(struct kthread_work *work)
@@ -1108,7 +1129,7 @@ static void hdcp_lib_msg_recvd_work(struct kthread_work *work)
	handle->hdcp_timeout = rsp_buf->timeout;
	handle->msglen = rsp_buf->msglen;

	if (!atomic_read(&handle->hdcp_off_pending)) {
	if (!atomic_read(&handle->hdcp_off)) {
		cdata.cmd = HDMI_HDCP_WKUP_CMD_SEND_MESSAGE;
		cdata.send_msg_buf = handle->listener_buf;
		cdata.send_msg_len = handle->msglen;
@@ -1121,10 +1142,36 @@ exit:

	hdcp_lib_wakeup_client(handle, &cdata);

	if (rc && !atomic_read(&handle->hdcp_off_pending))
	if (rc && !atomic_read(&handle->hdcp_off))
		queue_kthread_work(&handle->worker, &handle->clean);
}

static void hdcp_lib_topology_work(struct kthread_work *work)
{
	u32 timeout;
	struct hdcp_lib_handle *handle = container_of(work,
		struct hdcp_lib_handle, topology);
	struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_INVALID};

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

	cdata.context = handle->client_ctx;
	cdata.cmd = HDMI_HDCP_WKUP_CMD_RECV_MESSAGE;
	hdcp_lib_wakeup_client(handle, &cdata);

	reinit_completion(&handle->topo_wait);
	timeout = wait_for_completion_timeout(&handle->topo_wait, HZ * 3);
	if (!timeout) {
		pr_err("topology receiver id list timeout\n");

		if (!atomic_read(&handle->hdcp_off))
			queue_kthread_work(&handle->worker, &handle->clean);
	}
}

/* APIs exposed to all clients */
int hdcp1_set_keys(uint32_t *aksv_msb, uint32_t *aksv_lsb)
{
@@ -1204,7 +1251,6 @@ int hdcp_library_register(void **pphdcpcontext,
	/* populate ops to be called by client */
	txmtr_ops->feature_supported = hdcp_lib_client_feature_supported;
	txmtr_ops->wakeup = hdcp_lib_wakeup;
	txmtr_ops->hdcp_query_stream_type = hdcp_lib_query_stream_type;

	handle = kzalloc(sizeof(*handle), GFP_KERNEL);
	if (!handle) {
@@ -1215,9 +1261,10 @@ int hdcp_library_register(void **pphdcpcontext,
	handle->client_ctx = client_ctx;
	handle->client_ops = client_ops;

	atomic_set(&handle->hdcp_off_pending, 0);
	atomic_set(&handle->hdcp_off, 0);

	mutex_init(&handle->hdcp_lock);
	mutex_init(&handle->wakeup_mutex);

	init_kthread_worker(&handle->worker);

@@ -1226,6 +1273,10 @@ int hdcp_library_register(void **pphdcpcontext,
	init_kthread_work(&handle->msg_recvd, hdcp_lib_msg_recvd_work);
	init_kthread_work(&handle->timeout,   hdcp_lib_manage_timeout_work);
	init_kthread_work(&handle->clean,     hdcp_lib_cleanup_work);
	init_kthread_work(&handle->topology,  hdcp_lib_topology_work);
	init_kthread_work(&handle->stream,    hdcp_lib_query_stream_type_work);

	init_completion(&handle->topo_wait);

	handle->listener_buf = kzalloc(MAX_TX_MESSAGE_SIZE, GFP_KERNEL);
	if (!(handle->listener_buf)) {
@@ -1267,6 +1318,7 @@ void hdcp_library_deregister(void *phdcpcontext)
	kzfree(handle->qseecom_handle);

	mutex_destroy(&handle->hdcp_lock);
	mutex_destroy(&handle->wakeup_mutex);

	kzfree(handle->listener_buf);
	kzfree(handle);
+32 −9
Original line number Diff line number Diff line
@@ -366,7 +366,10 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl)
	ddc_data.retry = 5;
	ddc_data.what = "Bcaps";
	ddc_data.no_align = true;
	rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data);

	hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data;

	rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl);
	if (rc) {
		DEV_ERR("%s: %s: BCAPS read failed\n", __func__,
			HDCP_STATE_NAME);
@@ -529,7 +532,9 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl)
	ddc_data.data_buf = an;
	ddc_data.data_len = 8;
	ddc_data.what = "An";
	rc = hdmi_ddc_write(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data);
	hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data;

	rc = hdmi_ddc_write(hdcp_ctrl->init_data.ddc_ctrl);
	if (rc) {
		DEV_ERR("%s: %s: An write failed\n", __func__, HDCP_STATE_NAME);
		goto error;
@@ -542,7 +547,9 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl)
	ddc_data.data_buf = aksv;
	ddc_data.data_len = 5;
	ddc_data.what = "Aksv";
	rc = hdmi_ddc_write(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data);
	hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data;

	rc = hdmi_ddc_write(hdcp_ctrl->init_data.ddc_ctrl);
	if (rc) {
		DEV_ERR("%s: %s: AKSV write failed\n", __func__,
			HDCP_STATE_NAME);
@@ -561,7 +568,10 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl)
	ddc_data.retry = 5;
	ddc_data.what = "Bksv";
	ddc_data.no_align = true;
	rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data);

	hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data;

	rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl);
	if (rc) {
		DEV_ERR("%s: %s: BKSV read failed\n", __func__,
			HDCP_STATE_NAME);
@@ -633,7 +643,10 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl)
	ddc_data.retry = 5;
	ddc_data.what = "R0'";
	ddc_data.no_align = true;
	rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data);

	hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data;

	rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl);
	if (rc) {
		DEV_ERR("%s: %s: R0' read failed\n", __func__, HDCP_STATE_NAME);
		goto error;
@@ -684,7 +697,8 @@ do { \
	ddc_data.offset = (off); \
	memset(what, 0, sizeof(what)); \
	snprintf(what, sizeof(what), (name)); \
	rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data); \
	hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data; \
	rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl); \
	if (rc) { \
		DEV_ERR("%s: %s: Read %s failed\n", __func__, HDCP_STATE_NAME, \
			what); \
@@ -868,7 +882,10 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl)
		ddc_data.retry = 5;
		ddc_data.what = "Bcaps";
		ddc_data.no_align = false;
		rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data);

		hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data;

		rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl);
		if (rc) {
			DEV_ERR("%s: %s: BCAPS read failed\n", __func__,
				HDCP_STATE_NAME);
@@ -887,7 +904,10 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl)
	ddc_data.retry = 5;
	ddc_data.what = "Bstatuss";
	ddc_data.no_align = false;
	rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data);

	hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data;

	rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl);
	if (rc) {
		DEV_ERR("%s: %s: BSTATUS read failed\n", __func__,
			HDCP_STATE_NAME);
@@ -979,9 +999,12 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl)
	ddc_data.retry = 5;
	ddc_data.what = "KSV FIFO";
	ddc_data.no_align = true;

	hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data;

	cnt = 0;
	do {
		rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data);
		rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl);
		if (rc) {
			DEV_ERR("%s: %s: KSV FIFO read failed\n", __func__,
				HDCP_STATE_NAME);
+138 −103
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@
#define HDCP_SINK_DDC_HDCP2_RXSTATUS 0x70        /* RxStatus, 2 bytes */
#define HDCP_SINK_DDC_HDCP2_READ_MESSAGE 0x80    /* HDCP Tx reads here */

#define HDCP2P2_LINK_CHECK_TIME_MS 500 /* link check within 1 sec */

/*
 * HDCP 2.2 encryption requires the data encryption block that is present in
 * HDMI controller version 4.0.0 and above
@@ -67,6 +69,9 @@ struct hdmi_hdcp2p2_ctrl {
	struct kthread_work auth;
	struct kthread_work send_msg;
	struct kthread_work recv_msg;
	struct kthread_work link;

	struct delayed_work link_check_work;
};

static int hdmi_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data)
@@ -86,7 +91,7 @@ static int hdmi_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data)

	mutex_lock(&ctrl->wakeup_mutex);

	pr_debug("wakeup_cmd: %s\n", hdmi_hdcp_cmd_to_str(data->cmd));
	pr_debug("cmd: %s\n", hdmi_hdcp_cmd_to_str(data->cmd));

	ctrl->wakeup_cmd = data->cmd;
	ctrl->timeout = data->timeout;
@@ -275,7 +280,8 @@ static ssize_t hdmi_hdcp2p2_sysfs_wta_min_level_change(struct device *dev,
{
	struct hdmi_hdcp2p2_ctrl *ctrl =
		hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2);
	int res;
	struct hdcp_lib_wakeup_data cdata = {
		HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE};

	if (!ctrl) {
		pr_err("invalid input\n");
@@ -283,8 +289,9 @@ static ssize_t hdmi_hdcp2p2_sysfs_wta_min_level_change(struct device *dev,
	}

	pr_debug("notification of minimum level change received\n");
	res = ctrl->lib->
		hdcp_query_stream_type(ctrl->lib_ctx);

	cdata.context = ctrl->lib_ctx;
	hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);

	return  count;
}
@@ -299,8 +306,7 @@ static void hdmi_hdcp2p2_auth_failed(struct hdmi_hdcp2p2_ctrl *ctrl)
	atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL);

	/* notify hdmi tx about HDCP failure */
	ctrl->init_data.notify_status(
		ctrl->init_data.cb_data,
	ctrl->init_data.notify_status(ctrl->init_data.cb_data,
		HDCP_STATE_AUTH_FAIL);
}

@@ -310,6 +316,10 @@ static int hdmi_hdcp2p2_ddc_read_message(struct hdmi_hdcp2p2_ctrl *ctrl,
	struct hdmi_tx_ddc_data ddc_data;
	int rc;

	if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
		pr_err("hdcp is off\n");
		return -EINVAL;
	}
	memset(&ddc_data, 0, sizeof(ddc_data));
	ddc_data.dev_addr = HDCP_SINK_DDC_SLAVE_ADDR;
	ddc_data.offset = HDCP_SINK_DDC_HDCP2_READ_MESSAGE;
@@ -320,7 +330,9 @@ static int hdmi_hdcp2p2_ddc_read_message(struct hdmi_hdcp2p2_ctrl *ctrl,
	ddc_data.hard_timeout = timeout;
	ddc_data.what = "HDCP2ReadMessage";

	rc = hdmi_ddc_read(ctrl->init_data.ddc_ctrl, &ddc_data);
	ctrl->init_data.ddc_ctrl->ddc_data = ddc_data;

	rc = hdmi_ddc_read(ctrl->init_data.ddc_ctrl);
	if (rc)
		pr_err("Cannot read HDCP message register\n");
	return rc;
@@ -340,24 +352,14 @@ static int hdmi_hdcp2p2_ddc_write_message(struct hdmi_hdcp2p2_ctrl *ctrl,
	ddc_data.retry = 1;
	ddc_data.what = "HDCP2WriteMessage";

	rc = hdmi_ddc_write(ctrl->init_data.ddc_ctrl, &ddc_data);
	ctrl->init_data.ddc_ctrl->ddc_data = ddc_data;

	rc = hdmi_ddc_write(ctrl->init_data.ddc_ctrl);
	if (rc)
		pr_err("Cannot write HDCP message register");
	return rc;
}

static void hdmi_hdcp2p2_ddc_abort(struct hdmi_hdcp2p2_ctrl *ctrl)
{
	/* Abort any ongoing DDC transactions */
	struct hdmi_tx_ddc_data ddc_data;

	memset(&ddc_data, 0, sizeof(ddc_data));
	ddc_data.retry = 1;
	ddc_data.what = "HDCPAbortTransaction";
	hdmi_ddc_abort_transaction(ctrl->init_data.ddc_ctrl,
		&ddc_data);
}

static int hdmi_hdcp2p2_read_version(struct hdmi_hdcp2p2_ctrl *ctrl,
		u8 *hdcp2version)
{
@@ -373,7 +375,9 @@ static int hdmi_hdcp2p2_read_version(struct hdmi_hdcp2p2_ctrl *ctrl,
	ddc_data.retry = 1;
	ddc_data.what = "HDCP2Version";

	rc = hdmi_ddc_read(ctrl->init_data.ddc_ctrl, &ddc_data);
	ctrl->init_data.ddc_ctrl->ddc_data = ddc_data;

	rc = hdmi_ddc_read(ctrl->init_data.ddc_ctrl);
	if (rc) {
		pr_err("Cannot read HDCP2Version register");
		return rc;
@@ -424,44 +428,6 @@ static struct attribute_group hdmi_hdcp2p2_fs_attr_group = {
	.attrs = hdmi_hdcp2p2_fs_attrs,
};

static int hdmi_hdcp2p2_isr(void *input)
{
	struct hdmi_hdcp2p2_ctrl *ctrl = input;
	u32 reg_val;

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

	pr_debug("INT_CTRL0 is 0x%x\n",
		DSS_REG_R(ctrl->init_data.core_io, HDMI_DDC_INT_CTRL0));
	reg_val = DSS_REG_R(ctrl->init_data.core_io,
			HDMI_HDCP_INT_CTRL2);
	if (reg_val & BIT(0)) {
		pr_debug("HDCP 2.2 Encryption is enabled\n");
		reg_val |= BIT(1);
		DSS_REG_W(ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2,
			reg_val);
	}

	reg_val = DSS_REG_R(ctrl->init_data.core_io,
							HDMI_DDC_INT_CTRL0);
	if (reg_val & HDCP2P2_RXSTATUS_MESSAGE_SIZE_MASK) {
		DSS_REG_W(ctrl->init_data.core_io, HDMI_DDC_INT_CTRL0,
						reg_val & ~(BIT(31)));
		if (!completion_done(&ctrl->rxstatus_completion))
			complete_all(&ctrl->rxstatus_completion);
	} else if (reg_val & BIT(8)) {
		DSS_REG_W(ctrl->init_data.core_io, HDMI_DDC_INT_CTRL0,
						reg_val & ~(BIT(9) | BIT(10)));
		if (!completion_done(&ctrl->rxstatus_completion))
			complete_all(&ctrl->rxstatus_completion);
	}

	return 0;
}

static bool hdmi_hdcp2p2_feature_supported(void *input)
{
	struct hdmi_hdcp2p2_ctrl *ctrl = input;
@@ -525,12 +491,12 @@ exit:

static void hdmi_hdcp2p2_recv_msg_work(struct kthread_work *work)
{
	int rc = 0, msg_size = 0, retries = 5;
	int rc = 0;
	u64 mult;
	char *recvd_msg_buf = NULL;
	struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
		struct hdmi_hdcp2p2_ctrl, recv_msg);
	struct hdmi_tx_hdcp2p2_ddc_data hdcp2p2_ddc_data;
	struct hdmi_tx_hdcp2p2_ddc_data *ddc_data;
	struct hdmi_tx_ddc_ctrl *ddc_ctrl;
	struct msm_hdmi_mode_timing_info *timing;
	struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
@@ -550,59 +516,47 @@ static void hdmi_hdcp2p2_recv_msg_work(struct kthread_work *work)
	}

	ddc_ctrl = ctrl->init_data.ddc_ctrl;
	if (!ddc_ctrl)
		goto exit;

	do {
		timing = ctrl->init_data.timing;
	hdmi_ddc_config(ddc_ctrl);

		mult = hdmi_tx_get_v_total(timing) / 20;
		memset(&hdcp2p2_ddc_data, 0, sizeof(hdcp2p2_ddc_data));
		hdcp2p2_ddc_data.ddc_data.what = "HDCP2RxStatus";
		hdcp2p2_ddc_data.ddc_data.data_buf = (u8 *)&msg_size;
		hdcp2p2_ddc_data.ddc_data.data_len = sizeof(msg_size);
		hdcp2p2_ddc_data.rxstatus_field = RXSTATUS_MESSAGE_SIZE;
		hdcp2p2_ddc_data.timer_delay_lines = (u32)mult;
		hdcp2p2_ddc_data.irq_wait_count = 100;
		hdcp2p2_ddc_data.poll_sink = false;
	ddc_data = &ddc_ctrl->hdcp2p2_ddc_data;

		hdmi_ddc_config(ddc_ctrl);
		pr_debug("Reading rxstatus, timer delay %u\n", (u32)mult);
	memset(ddc_data, 0, sizeof(*ddc_data));

		rc = hdmi_hdcp2p2_ddc_read_rxstatus(
			ddc_ctrl, &hdcp2p2_ddc_data,
			&ctrl->rxstatus_completion);
	timing = ctrl->init_data.timing;
	mult = hdmi_tx_get_v_total(timing) / 20;
	ddc_data->intr_mask = RXSTATUS_MESSAGE_SIZE;
	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);
	if (rc) {
			pr_err("Could not read rxstatus from sink\n");
			continue;
		pr_err("error reading rxstatus %d\n", rc);
		goto exit;
	}

		if (!msg_size) {
	if (!ddc_data->message_size) {
		pr_err("recvd invalid message size\n");
		rc = -EINVAL;
			continue;
		}
	} while (rc && (retries-- > 0));

	if (rc) {
		pr_err("error reading valid rxstatus data\n");
		goto exit;
	}

	recvd_msg_buf = kzalloc(msg_size, GFP_KERNEL);
	pr_debug("rxstatus msg size %d\n", ddc_data->message_size);

	recvd_msg_buf = kzalloc(ddc_data->message_size, GFP_KERNEL);
	if (!recvd_msg_buf)
		goto exit;

	rc = hdmi_hdcp2p2_ddc_read_message(ctrl, recvd_msg_buf,
		msg_size, ctrl->timeout);
		ddc_data->message_size, ctrl->timeout);
	if (rc)
		pr_err("ERROR reading message from sink\n");

	hdmi_hdcp2p2_ddc_abort(ctrl);
	hdmi_hdcp2p2_ddc_reset(ddc_ctrl);
	hdmi_hdcp2p2_ddc_disable(ddc_ctrl);
		pr_err("error reading message %d\n", rc);

	cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS;
	cdata.recvd_msg_buf = recvd_msg_buf;
	cdata.recvd_msg_len = msg_size;
	cdata.recvd_msg_len = ddc_data->message_size;
exit:
	if (rc == -ETIMEDOUT)
		cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT;
@@ -646,14 +600,92 @@ static void hdmi_hdcp2p2_auth_status_work(struct kthread_work *work)
		DSS_REG_W(ctrl->init_data.core_io,
			HDMI_HDCP_INT_CTRL2, regval);

		ctrl->init_data.notify_status(
			ctrl->init_data.cb_data,
		ctrl->init_data.notify_status(ctrl->init_data.cb_data,
			HDCP_STATE_AUTHENTICATED);

		/* recheck within 1sec as per hdcp 2.2 standard */
		schedule_delayed_work(&ctrl->link_check_work,
			msecs_to_jiffies(HDCP2P2_LINK_CHECK_TIME_MS));
	}
exit:
	mutex_unlock(&ctrl->mutex);
}

static void hdmi_hdcp2p2_link_schedule_work(struct work_struct *work)
{
	struct hdmi_hdcp2p2_ctrl *ctrl = container_of(to_delayed_work(work),
		struct hdmi_hdcp2p2_ctrl, link_check_work);

	if (atomic_read(&ctrl->auth_state) != HDCP_STATE_INACTIVE)
		queue_kthread_work(&ctrl->worker, &ctrl->link);
}

static void hdmi_hdcp2p2_link_work(struct kthread_work *work)
{
	int rc = 0;
	struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
		struct hdmi_hdcp2p2_ctrl, link);
	struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_STOP};
	struct hdmi_tx_ddc_ctrl *ddc_ctrl;
	struct hdmi_tx_hdcp2p2_ddc_data *ddc_data;
	u64 mult;
	struct msm_hdmi_mode_timing_info *timing;

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

	mutex_lock(&ctrl->mutex);

	cdata.context = ctrl->lib_ctx;

	ddc_ctrl = ctrl->init_data.ddc_ctrl;
	if (!ddc_ctrl) {
		rc = -EINVAL;
		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_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);
	if (rc) {
		pr_err("error reading rxstatus %d\n", rc);
		goto exit;
	}

	if (ddc_data->reauth_req) {
		pr_debug("sync reported loss of synchronization, reauth\n");
		rc = -ENOLINK;
	}
exit:
	mutex_unlock(&ctrl->mutex);

	if (rc) {
		/* notify hdcp lib to stop auth */
		hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);

		/* notify hdmi tx about auth failure */
		hdmi_hdcp2p2_auth_failed(ctrl);

		return;
	}

	/* recheck within 1sec as per hdcp 2.2 standard */
	schedule_delayed_work(&ctrl->link_check_work,
		msecs_to_jiffies(HDCP2P2_LINK_CHECK_TIME_MS));
}

static void hdmi_hdcp2p2_auth_work(struct kthread_work *work)
{
	struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
@@ -707,7 +739,6 @@ void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data)
	struct hdmi_hdcp2p2_ctrl *ctrl;
	int rc;
	static struct hdmi_hdcp_ops ops = {
		.hdmi_hdcp_isr = hdmi_hdcp2p2_isr,
		.hdmi_hdcp_reauthenticate = hdmi_hdcp2p2_reauthenticate,
		.hdmi_hdcp_authenticate = hdmi_hdcp2p2_authenticate,
		.feature_supported = hdmi_hdcp2p2_feature_supported,
@@ -771,10 +802,14 @@ void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data)
	init_kthread_work(&ctrl->send_msg, hdmi_hdcp2p2_send_msg_work);
	init_kthread_work(&ctrl->recv_msg, hdmi_hdcp2p2_recv_msg_work);
	init_kthread_work(&ctrl->status,   hdmi_hdcp2p2_auth_status_work);
	init_kthread_work(&ctrl->link,     hdmi_hdcp2p2_link_work);

	ctrl->thread = kthread_run(kthread_worker_fn,
		&ctrl->worker, "hdmi_hdcp2p2");

	INIT_DELAYED_WORK(&ctrl->link_check_work,
		hdmi_hdcp2p2_link_schedule_work);

	if (IS_ERR(ctrl->thread)) {
		pr_err("unable to start hdcp2p2 thread\n");
		rc = PTR_ERR(ctrl->thread);
+15 −6
Original line number Diff line number Diff line
@@ -1291,11 +1291,13 @@ static u32 hdmi_tx_ddc_read(struct hdmi_tx_ddc_ctrl *ddc_ctrl,
			ddc_data.what        = "EDID";
			ddc_data.no_align    = false;

			ddc_ctrl->ddc_data = ddc_data;

			/* Read EDID twice with 32bit alighnment too */
			if (block < 2)
				status = hdmi_ddc_read(ddc_ctrl, &ddc_data);
				status = hdmi_ddc_read(ddc_ctrl);
			else
				status = hdmi_ddc_read_seg(ddc_ctrl, &ddc_data);
				status = hdmi_ddc_read_seg(ddc_ctrl);

			if (status)
				break;
@@ -3777,10 +3779,14 @@ static irqreturn_t hdmi_tx_isr(int irq, void *data)
		if (hdmi_cec_isr(hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC]))
			DEV_ERR("%s: hdmi_cec_isr failed\n", __func__);

	if (hdmi_ctrl->hdcp_ops && hdmi_ctrl->hdcp_feature_data)
	if (hdmi_ctrl->hdcp_ops && hdmi_ctrl->hdcp_feature_data) {
		if (hdmi_ctrl->hdcp_ops->hdmi_hdcp_isr) {
			if (hdmi_ctrl->hdcp_ops->hdmi_hdcp_isr(
				hdmi_ctrl->hdcp_feature_data))
			DEV_ERR("%s: hdmi_hdcp_isr failed\n", __func__);
				DEV_ERR("%s: hdmi_hdcp_isr failed\n",
					 __func__);
		}
	}
end:
	return IRQ_HANDLED;
} /* hdmi_tx_isr */
@@ -3865,6 +3871,7 @@ static int hdmi_tx_dev_init(struct hdmi_tx_ctrl *hdmi_ctrl)

	hdmi_ctrl->ddc_ctrl.io = &pdata->io[HDMI_TX_CORE_IO];
	init_completion(&hdmi_ctrl->ddc_ctrl.ddc_sw_done);
	init_completion(&hdmi_ctrl->ddc_ctrl.rxstatus_completion);

	hdmi_ctrl->panel_power_on = false;
	hdmi_ctrl->panel_suspend = false;
@@ -4080,6 +4087,8 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data,
			hdmi_ctrl->hdcp_ops->hdmi_hdcp_off(
				hdmi_ctrl->hdcp_feature_data);

			hdmi_ctrl->hdcp_ops = NULL;

			rc = hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM,
				false);
			if (rc)
+315 −215

File changed.

Preview size limit exceeded, changes collapsed.

Loading