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

Commit e4b91395 authored by Alhad Purnapatre's avatar Alhad Purnapatre
Browse files

msm: mdss: hdmi: HDCP 2.2 changes to work with Trustzone driver



Make HDCP 2.2 work with Trustzone QSEECOM driver that handles interaction
with the HDCP secure app that actually runs HDCP authentication protocol.

The current HDCP 2.2 driver is designed to run with a Trustzone module in
userspace that uses sysfs nodes to communicate with this driver. With
Trustzone QSEECOM driver now available to communicate with HDCP secure app
in TZ, make use of its features to communicate with the secure app.

Change-Id: Ifd66785e40c28fd161b82477ebef2b5829005875
Signed-off-by: default avatarAlhad Purnapatre <alhadp@codeaurora.org>
parent a93872da
Loading
Loading
Loading
Loading
+276 −201
Original line number Diff line number Diff line
@@ -16,11 +16,11 @@
#include <linux/stat.h>
#include <linux/types.h>

#include <linux/hdcp_qseecom.h>
#include "mdss_hdmi_hdcp.h"
#include "video/msm_hdmi_hdcp_mgr.h"
#include "mdss_hdmi_util.h"

static int hdcp2p2_authenticate(void *input);

/*
 * Defined addresses and offsets of standard HDCP 2.2 sink registers
@@ -54,7 +54,6 @@ enum hdcp2p2_sink_originated_message {
	AKE_SEND_H_PRIME_MESSAGE = 7,
	AKE_SEND_PAIRING_INFO_MESSAGE = 8,
	LC_SEND_L_PRIME_MESSAGE = 10,
	ABORT_AUTHENTICATION = 99
};

enum hdcp2p2_tx_originated_message {
@@ -63,7 +62,6 @@ enum hdcp2p2_tx_originated_message {
	AKE_STORED_KM_MESSAGE = 5,
	LC_INIT_MESSAGE = 9,
	SKE_SEND_EKS_MESSAGE = 11,
	AUTHENTICATION_FAILED = 100
};

/* Stores one single message from sink */
@@ -77,6 +75,7 @@ struct hdcp2p2_ctrl {
	enum hdmi_hdcp_state auth_state; /* Current auth message st */
	enum hdcp2p2_sink_status sink_status; /* Is sink connected */
	struct work_struct hdcp_sink_message_work; /* Polls sink for new msg */
	struct work_struct hdcp_tz_message_work; /* Sends received msg to TZ */
	struct hdmi_hdcp_init_data init_data; /* Feature data from HDMI drv */
	enum hdcp2p2_sink_originated_message next_sink_message;
	enum hdcp2p2_tx_originated_message next_tx_message;
@@ -84,8 +83,15 @@ struct hdcp2p2_ctrl {
	bool tx_has_master_key; /* true when TX has a stored Km for sink */
	struct mutex mutex; /* mutex to protect access to hdcp2p2_ctrl */
	struct hdmi_hdcp_ops *ops;
	void *hdcp_lib_handle; /* Handle to HDCP 2.2 Trustzone library */
	bool hdcp_library_init; /* Has the HDCP TZ library been initialized */
	bool hdcp_txmtr_init; /* Has the HDCP TZ transmitter been initialized */
	struct hdcp_txmtr_ops *txmtr_ops; /* Ops for driver to call into TZ */
	struct hdcp_client_ops *client_ops; /* Ops for driver to export to TZ */
};

static int hdcp2p2_authenticate(void *input);

static const char *hdcp2p2_message_name(int msg_id)
{
	/*
@@ -180,6 +186,24 @@ static ssize_t hdcp2p2_sysfs_wta_trigger(struct device *dev,
	return count;
}

static void hdcp2p2_auth_failed(struct hdcp2p2_ctrl *hdcp2p2_ctrl)
{
	mutex_lock(&hdcp2p2_ctrl->mutex);
	hdcp2p2_ctrl->auth_state = HDCP_STATE_AUTH_FAIL;
	hdcp2p2_ctrl->next_sink_message = START_AUTHENTICATION;
	hdcp2p2_ctrl->next_tx_message = AKE_INIT_MESSAGE;
	hdcp2p2_ctrl->tx_has_master_key = false;
	if (hdcp2p2_ctrl->hdcp_txmtr_init) {
		hdcp2p2_ctrl->txmtr_ops->hdcp_txmtr_deinit(
				hdcp2p2_ctrl->hdcp_lib_handle);
		hdcp2p2_ctrl->hdcp_txmtr_init = false;
	}
	mutex_unlock(&hdcp2p2_ctrl->mutex);
	hdcp2p2_ctrl->init_data.notify_status(
			hdcp2p2_ctrl->init_data.cb_data,
			HDCP_STATE_AUTH_FAIL);
}

static void hdcp2p2_advance_next_tx_message(
		struct hdcp2p2_ctrl *hdcp2p2_ctrl)
{
@@ -232,58 +256,7 @@ static void hdcp2p2_advance_next_sink_message(
	mutex_unlock(&hdcp2p2_ctrl->mutex);
}

static ssize_t hdcp2p2_sysfs_rda_message_from_sink(struct file *file,
		struct kobject *kobj, struct bin_attribute *attr, char *buffer,
		loff_t pos, size_t size)
{
	ssize_t ret;
	struct hdcp2p2_message *msg;
	struct list_head *prev, *next;
	struct device *dev = container_of(kobj, struct device, kobj);
	struct hdcp2p2_ctrl *hdcp2p2_ctrl =
		hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2);

	if (!hdcp2p2_ctrl) {
		DEV_ERR("%s: invalid input\n", __func__);
		return -EINVAL;
	}

	mutex_lock(&hdcp2p2_ctrl->mutex);
	if (list_empty(&hdcp2p2_ctrl->hdcp_sink_messages)) {
		DEV_ERR("%s: No message is available from sink\n", __func__);
		mutex_unlock(&hdcp2p2_ctrl->mutex);
		return -EINVAL;
	}
	list_for_each_safe(prev, next, &hdcp2p2_ctrl->hdcp_sink_messages) {
		msg = list_entry(prev, struct hdcp2p2_message, entry);
		if (size < msg->msg_size) {
			DEV_ERR("%s: buffer size not enough\n", __func__);
			mutex_unlock(&hdcp2p2_ctrl->mutex);
			return -EINVAL;
		}
		break;
	}

	list_del(&msg->entry);
	mutex_unlock(&hdcp2p2_ctrl->mutex);

	memmove(buffer, msg->message_bytes, msg->msg_size);
	ret = msg->msg_size;
	kfree(msg->message_bytes);
	kfree(msg);

	if (hdcp2p2_ctrl->next_sink_message == AKE_SEND_H_PRIME_MESSAGE) {
		if (!hdcp2p2_ctrl->tx_has_master_key) {
			DEV_DBG("%s: Listening for second message\n", __func__);
			hdcp2p2_advance_next_sink_message(hdcp2p2_ctrl);
			schedule_work(&hdcp2p2_ctrl->hdcp_sink_message_work);
		}
	}

	return ret;
}

static int hdcp2p2_ddc_read_message(struct hdmi_tx_ddc_ctrl *ddc_ctrl, u8 *buf,
static int hdcp2p2_ddc_read_message(struct hdcp2p2_ctrl *hdcp2p2_ctrl, u8 *buf,
		int size)
{
	struct hdmi_tx_ddc_data ddc_data;
@@ -298,13 +271,13 @@ static int hdcp2p2_ddc_read_message(struct hdmi_tx_ddc_ctrl *ddc_ctrl, u8 *buf,
	ddc_data.retry = 1;
	ddc_data.what = "HDCP2ReadMessage";

	rc = hdmi_ddc_read(ddc_ctrl, &ddc_data);
	rc = hdmi_ddc_read(hdcp2p2_ctrl->init_data.ddc_ctrl, &ddc_data);
	if (rc)
		DEV_ERR("%s: Cannot read HDCP message register", __func__);
	return rc;
}

static int hdcp2p2_ddc_write_message(struct hdmi_tx_ddc_ctrl *ddc_ctrl, u8 *buf,
static int hdcp2p2_ddc_write_message(struct hdcp2p2_ctrl *hdcp2p2_ctrl, u8 *buf,
		size_t size)
{
	struct hdmi_tx_ddc_data ddc_data;
@@ -318,13 +291,13 @@ static int hdcp2p2_ddc_write_message(struct hdmi_tx_ddc_ctrl *ddc_ctrl, u8 *buf,
	ddc_data.retry = 1;
	ddc_data.what = "HDCP2WriteMessage";

	rc = hdmi_ddc_write(ddc_ctrl, &ddc_data);
	rc = hdmi_ddc_write(hdcp2p2_ctrl->init_data.ddc_ctrl, &ddc_data);
	if (rc)
		DEV_ERR("%s: Cannot write HDCP message register", __func__);
	return rc;
}

static int hdcp2p2_read_version(struct hdmi_tx_ddc_ctrl *ddc_ctrl,
static int hdcp2p2_read_version(struct hdcp2p2_ctrl *hdcp2p2_ctrl,
		u8 *hdcp2version)
{
	struct hdmi_tx_ddc_data ddc_data;
@@ -339,7 +312,7 @@ static int hdcp2p2_read_version(struct hdmi_tx_ddc_ctrl *ddc_ctrl,
	ddc_data.retry = 1;
	ddc_data.what = "HDCP2Version";

	rc = hdmi_ddc_read(ddc_ctrl, &ddc_data);
	rc = hdmi_ddc_read(hdcp2p2_ctrl->init_data.ddc_ctrl, &ddc_data);
	if (rc) {
		DEV_ERR("%s: Cannot read HDCP2Version register", __func__);
		return rc;
@@ -360,7 +333,6 @@ static void hdcp2p2_sink_message_work(struct work_struct *work)
	struct hdcp2p2_ctrl *hdcp2p2_ctrl = container_of(work,
			struct hdcp2p2_ctrl, hdcp_sink_message_work);
	struct hdmi_tx_ddc_data ddc_data;
	struct hdmi_tx_ddc_ctrl *ddc_ctrl = hdcp2p2_ctrl->init_data.ddc_ctrl;
	int rc;
	u16 rx_status;
	int msg_size;
@@ -376,7 +348,7 @@ static void hdcp2p2_sink_message_work(struct work_struct *work)
		ddc_data.retry = 1;
		ddc_data.what = "HDCP2RxStatus";

		rc = hdmi_ddc_read(ddc_ctrl, &ddc_data);
		rc = hdmi_ddc_read(hdcp2p2_ctrl->init_data.ddc_ctrl, &ddc_data);
		if (rc) {
			DEV_ERR("%s: Error %d in read HDCP RX status register",
					__func__, rc);
@@ -396,6 +368,7 @@ static void hdcp2p2_sink_message_work(struct work_struct *work)

	if (!wait_cycles) {
		DEV_ERR("%s: Timeout in waiting for sink\n", __func__);
		goto error;
	} else {
		/* Read message from sink now */
		struct list_head *pos;
@@ -403,113 +376,75 @@ static void hdcp2p2_sink_message_work(struct work_struct *work)
				GFP_KERNEL);
		if (!message) {
			DEV_ERR("%s: Could not allocate memory\n", __func__);
			return;
			goto error;
		}
		message->message_bytes = kmalloc(msg_size, GFP_KERNEL);
		if (!message->message_bytes) {
			DEV_ERR("%s: Could not allocate memory\n", __func__);
			kfree(message);
			return;
			goto error;
		}

		hdcp2p2_ddc_read_message(ddc_ctrl, message->message_bytes,
		hdcp2p2_ddc_read_message(hdcp2p2_ctrl, message->message_bytes,
				msg_size);
		message->msg_size = msg_size;

		INIT_LIST_HEAD(&message->entry);
		list_for_each(pos, &hdcp2p2_ctrl->hdcp_sink_messages);
		list_add_tail(&message->entry, pos);
		DEV_DBG("%s: Pinging transmitter for message_available\n",
		DEV_DBG("%s: Scheduling dispatch of msg to tz transmitter\n",
				__func__);
		sysfs_notify(hdcp2p2_ctrl->init_data.sysfs_kobj, "hdcp2p2",
				"message_available");
	}
		schedule_work(&hdcp2p2_ctrl->hdcp_tz_message_work);
	}
	return;

static ssize_t hdcp2p2_sysfs_wta_message_to_sink(struct file *file,
		struct kobject *kobj, struct bin_attribute *attr, char *buffer,
		loff_t pos, size_t size)
{
	/* In here, we need to read the message from userspace, and write it
	 * out to the sink via DDC
	 */
	int rc;
	bool authenticated = false;
	struct device *dev = container_of(kobj, struct device, kobj);
	struct hdcp2p2_ctrl *hdcp2p2_ctrl =
		hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2);

	if (!hdcp2p2_ctrl) {
		DEV_ERR("%s: invalid input\n", __func__);
		return -EINVAL;
error:
	hdcp2p2_auth_failed(hdcp2p2_ctrl);
}

	DEV_DBG("%s: Received %s message from tx\n", __func__,
			hdcp2p2_message_name((int)buffer[0]));
static void hdcp2p2_tz_message_work(struct work_struct *work)
{
	struct hdcp2p2_ctrl *hdcp2p2_ctrl = container_of(work,
			struct hdcp2p2_ctrl, hdcp_tz_message_work);
	struct list_head *prev, *next;
	struct hdcp2p2_message *msg;

	switch (buffer[0]) {
	case AKE_STORED_KM_MESSAGE:
	mutex_lock(&hdcp2p2_ctrl->mutex);
		hdcp2p2_ctrl->tx_has_master_key = true;
		mutex_unlock(&hdcp2p2_ctrl->mutex);
		break;
	case SKE_SEND_EKS_MESSAGE:
		/* Send EKS message comes from TX when we are authenticated */
		authenticated = true;
		mutex_lock(&hdcp2p2_ctrl->mutex);
		hdcp2p2_ctrl->auth_state = HDCP_STATE_AUTHENTICATED;
		hdcp2p2_ctrl->next_sink_message = START_AUTHENTICATION;
		hdcp2p2_ctrl->next_tx_message = AKE_INIT_MESSAGE;
		hdcp2p2_ctrl->tx_has_master_key = false;
		mutex_unlock(&hdcp2p2_ctrl->mutex);
		hdcp2p2_ctrl->init_data.notify_status(
				hdcp2p2_ctrl->init_data.cb_data,
				HDCP_STATE_AUTHENTICATED);
		break;
	case AUTHENTICATION_FAILED:
		mutex_lock(&hdcp2p2_ctrl->mutex);
		hdcp2p2_ctrl->auth_state = HDCP_STATE_AUTH_FAIL;
		hdcp2p2_ctrl->next_sink_message = START_AUTHENTICATION;
		hdcp2p2_ctrl->next_tx_message = AKE_INIT_MESSAGE;
		hdcp2p2_ctrl->tx_has_master_key = false;
	if (list_empty(&hdcp2p2_ctrl->hdcp_sink_messages)) {
		DEV_ERR("%s: No message is available from sink\n", __func__);
		mutex_unlock(&hdcp2p2_ctrl->mutex);
		hdcp2p2_ctrl->init_data.notify_status(
				hdcp2p2_ctrl->init_data.cb_data,
				HDCP_STATE_AUTH_FAIL);
		break;
	default:
		hdcp2p2_advance_next_sink_message(hdcp2p2_ctrl);
		hdcp2p2_advance_next_tx_message(hdcp2p2_ctrl);
		return;
	}
	list_for_each_safe(prev, next, &hdcp2p2_ctrl->hdcp_sink_messages) {
		msg = list_entry(prev, struct hdcp2p2_message, entry);
		break;
	}

	/* Forward the message to the sink */
	rc = hdcp2p2_ddc_write_message(hdcp2p2_ctrl->init_data.ddc_ctrl,
			(u8 *)buffer, size);
	if (rc) {
		DEV_ERR("%s: Error in writing to sink %d\n", __func__, rc);
		return rc;
	list_del(&msg->entry);
	mutex_unlock(&hdcp2p2_ctrl->mutex);

	DEV_DBG("%s: Sending %s message from sink to TX\n", __func__,
			hdcp2p2_message_name((int)msg->message_bytes[0]));
	if (hdcp2p2_ctrl->txmtr_ops->hdcp_txmtr_process_message(
		hdcp2p2_ctrl->hdcp_lib_handle, msg->message_bytes,
		(u32)msg->msg_size)) {
		DEV_ERR("%s: Failed in sending message to TZ\n", __func__);
		hdcp2p2_auth_failed(hdcp2p2_ctrl);
		goto done;
	}

	/* Start polling sink for the next expected message in the protocol */
	if (!authenticated)

	if (hdcp2p2_ctrl->next_sink_message == AKE_SEND_H_PRIME_MESSAGE) {
		if (!hdcp2p2_ctrl->tx_has_master_key) {
			DEV_DBG("%s: Listening for second message\n", __func__);
			hdcp2p2_advance_next_sink_message(hdcp2p2_ctrl);
			schedule_work(&hdcp2p2_ctrl->hdcp_sink_message_work);
	return size;
		}
	}
done:
	kfree(msg->message_bytes);
	kfree(msg);

static ssize_t hdcp2p2_sysfs_rda_message_available(struct device *dev,
			struct device_attribute *attr, char *buf)
{
	ssize_t ret;
	struct hdcp2p2_ctrl *hdcp2p2_ctrl =
		hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2);
	mutex_lock(&hdcp2p2_ctrl->mutex);
	if (list_empty(&hdcp2p2_ctrl->hdcp_sink_messages))
		ret = snprintf(buf, PAGE_SIZE, "Unavailable\n");
	else
		ret = snprintf(buf, PAGE_SIZE, "Available\n");
	mutex_unlock(&hdcp2p2_ctrl->mutex);
	return ret;
}

static ssize_t hdcp2p2_sysfs_rda_hdcp2_version(struct device *dev,
@@ -524,8 +459,7 @@ static ssize_t hdcp2p2_sysfs_rda_hdcp2_version(struct device *dev,
		DEV_ERR("%s: invalid input\n", __func__);
		return -EINVAL;
	}
	ret = hdcp2p2_read_version(hdcp2p2_ctrl->init_data.ddc_ctrl,
			&hdcp2version);
	ret = hdcp2p2_read_version(hdcp2p2_ctrl, &hdcp2version);
	if (ret < 0)
		return ret;
	return snprintf(buf, PAGE_SIZE, "%u\n", hdcp2version);
@@ -536,47 +470,41 @@ static DEVICE_ATTR(trigger, S_IRUGO | S_IWUSR, hdcp2p2_sysfs_rda_trigger,
		hdcp2p2_sysfs_wta_trigger);
static DEVICE_ATTR(sink_status, S_IRUGO, hdcp2p2_sysfs_rda_sink_status,
		NULL);
static DEVICE_ATTR(message_available, S_IRUGO,
		hdcp2p2_sysfs_rda_message_available,
		NULL);
static DEVICE_ATTR(hdcp2_version, S_IRUGO,
		hdcp2p2_sysfs_rda_hdcp2_version,
		NULL);

static struct bin_attribute message_attr = {
	.attr.name = "message",
	.attr.mode = 0600,
	.size = 0,
	.read = hdcp2p2_sysfs_rda_message_from_sink,
	.write = hdcp2p2_sysfs_wta_message_to_sink,
};

static struct attribute *hdcp2p2_fs_attrs[] = {
	&dev_attr_trigger.attr,
	&dev_attr_sink_status.attr,
	&dev_attr_message_available.attr,
	&dev_attr_hdcp2_version.attr,
	NULL,
};

static struct bin_attribute *hdcp2p2_fs_bin_attrs[] = {
	&message_attr
};

static struct attribute_group hdcp2p2_fs_attr_group = {
	.name = "hdcp2p2",
	.attrs = hdcp2p2_fs_attrs,
	.bin_attrs = hdcp2p2_fs_bin_attrs,
};


static int hdcp2p2_isr(void *input)
{
	struct hdcp2p2_ctrl *hdcp2p2_ctrl = input;
	u32 reg_val;
	if (!hdcp2p2_ctrl) {
		DEV_ERR("%s: invalid input\n", __func__);
		return -EINVAL;
	}

	reg_val = DSS_REG_R(hdcp2p2_ctrl->init_data.core_io,
			HDMI_HDCP_INT_CTRL2);
	if (reg_val & BIT(0)) {
		DEV_DBG("%s: HDCP 2.2 Encryption is enabled\n", __func__);
		reg_val |= BIT(1);
		DSS_REG_W(hdcp2p2_ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2,
			reg_val);
	}
	return 0;
}

@@ -589,6 +517,11 @@ static void hdcp2p2_reset(struct hdcp2p2_ctrl *hdcp2p2_ctrl)
	hdcp2p2_ctrl->next_tx_message = AKE_INIT_MESSAGE;
	hdcp2p2_ctrl->auth_state = HDCP_STATE_INACTIVE;
	hdcp2p2_ctrl->tx_has_master_key = false;
	if (hdcp2p2_ctrl->hdcp_txmtr_init) {
		hdcp2p2_ctrl->txmtr_ops->hdcp_txmtr_deinit(
				hdcp2p2_ctrl->hdcp_lib_handle);
		hdcp2p2_ctrl->hdcp_txmtr_init = false;
	}
	mutex_unlock(&hdcp2p2_ctrl->mutex);
}

@@ -596,19 +529,37 @@ static void hdcp2p2_reset(struct hdcp2p2_ctrl *hdcp2p2_ctrl)
static int hdcp2p2_authenticate(void *input)
{
	struct hdcp2p2_ctrl *hdcp2p2_ctrl = input;
	int rc;

	mutex_lock(&hdcp2p2_ctrl->mutex);
	hdcp2p2_ctrl->sink_status = SINK_CONNECTED;
	hdcp2p2_ctrl->auth_state = HDCP_STATE_AUTHENTICATING;
	mutex_unlock(&hdcp2p2_ctrl->mutex);

	/*
	 * Notify HDCP Transmitter that an HDCP2.2 capable sink has been
	 * connected
	 */
	sysfs_notify(hdcp2p2_ctrl->init_data.sysfs_kobj, "hdcp2p2",
			"sink_status");
	return 0;
	if (!hdcp2p2_ctrl->hdcp_library_init) {
		rc = hdcp_library_init(&hdcp2p2_ctrl->hdcp_lib_handle,
					hdcp2p2_ctrl->client_ops,
					hdcp2p2_ctrl->txmtr_ops, hdcp2p2_ctrl);
		if (rc) {
			DEV_ERR("%s: Unable to initialize HDCP 2.2 library\n",
				__func__);
			goto done;
		}
		hdcp2p2_ctrl->hdcp_library_init = true;
		DEV_DBG("%s: Successfully initialized HDCP library\n",
			__func__);
	}

	rc = hdcp2p2_ctrl->txmtr_ops->hdcp_txmtr_init(
		hdcp2p2_ctrl->hdcp_lib_handle);
	if (rc) {
		DEV_ERR("%s: HDCP transmitter init failed\n", __func__);
		hdcp2p2_ctrl->auth_state = HDCP_STATE_AUTH_FAIL;
		goto done;
	}
	hdcp2p2_ctrl->hdcp_txmtr_init = true;
done:
	mutex_unlock(&hdcp2p2_ctrl->mutex);
	return rc;
}

static int hdcp2p2_reauthenticate(void *input)
@@ -622,6 +573,143 @@ static void hdcp2p2_off(void *input)
	hdcp2p2_reset((struct hdcp2p2_ctrl *)input);
}

static int hdcp2p2_send_message_to_sink(void *client_ctx, void *hdcp_handle,
		char *message, u32 msg_size)
{
	struct hdcp2p2_ctrl *hdcp2p2_ctrl = client_ctx;
	bool authenticated = false;
	int rc = 0;

	if (!hdcp2p2_ctrl) {
		DEV_ERR("%s: invalid input\n", __func__);
		return -EINVAL;
	}

	DEV_DBG("%s: Sending %s message from tx to sink\n", __func__,
			hdcp2p2_message_name((int)message[0]));

	switch (message[0]) {
	case AKE_STORED_KM_MESSAGE:
		mutex_lock(&hdcp2p2_ctrl->mutex);
		hdcp2p2_ctrl->tx_has_master_key = true;
		mutex_unlock(&hdcp2p2_ctrl->mutex);
		hdcp2p2_advance_next_sink_message(hdcp2p2_ctrl);
		hdcp2p2_advance_next_tx_message(hdcp2p2_ctrl);
		break;
	case SKE_SEND_EKS_MESSAGE:
		/* Send EKS message comes from TX when we are authenticated */
		authenticated = true;
		mutex_lock(&hdcp2p2_ctrl->mutex);
		hdcp2p2_ctrl->auth_state = HDCP_STATE_AUTHENTICATED;
		hdcp2p2_ctrl->next_sink_message = START_AUTHENTICATION;
		hdcp2p2_ctrl->next_tx_message = AKE_INIT_MESSAGE;
		hdcp2p2_ctrl->tx_has_master_key = false;
		mutex_unlock(&hdcp2p2_ctrl->mutex);
		hdcp2p2_ctrl->init_data.notify_status(
				hdcp2p2_ctrl->init_data.cb_data,
				HDCP_STATE_AUTHENTICATED);
		break;
	case AKE_NO_STORED_KM_MESSAGE:
		/*
		 * We need a delay to allow sink time to be ready to receive the
		 * message
		 */
		msleep(100);
		/* fall through */
	default:
		hdcp2p2_advance_next_sink_message(hdcp2p2_ctrl);
		hdcp2p2_advance_next_tx_message(hdcp2p2_ctrl);
		break;
	}

	/* Forward the message to the sink */
	rc = hdcp2p2_ddc_write_message(hdcp2p2_ctrl, message, (size_t)msg_size);
	if (rc) {
		DEV_ERR("%s: Error in writing to sink %d\n", __func__, rc);
		hdcp2p2_auth_failed(hdcp2p2_ctrl);
		return rc;
	}

	/* Start polling sink for the next expected message in the protocol */
	if (!authenticated)
		schedule_work(&hdcp2p2_ctrl->hdcp_sink_message_work);
	else {
		/* Enable interrupts */
		u32 regval = DSS_REG_R(hdcp2p2_ctrl->init_data.core_io,
				HDMI_HDCP_INT_CTRL2);
		DEV_DBG("%s: Now authenticated. Enabling interrupts\n",
				__func__);
		regval |= BIT(1);
		regval |= BIT(2);
		regval |= BIT(5);
		DSS_REG_W(hdcp2p2_ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2,
			regval);
	}

	return rc;
}

static int hdcp2p2_tz_error(void *client_ctx, void *hdcp_handle)
{
	hdcp2p2_auth_failed(client_ctx);
	return 0;
}

static int hdcp2p2_tz_timeout(void *client_ctx, void *hdcp_handle,
		bool fail_auth)
{
	/* 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(
		((struct hdcp2p2_ctrl *)client_ctx)->init_data.ddc_ctrl,
		&ddc_data);
	if (fail_auth)
		hdcp2p2_auth_failed(client_ctx);
	return 0;
}

void hdmi_hdcp2p2_deinit(void *input)
{
	struct hdcp2p2_ctrl *hdcp2p2_ctrl = (struct hdcp2p2_ctrl *)input;
	struct list_head *node, *next;
	struct hdcp2p2_message *msg;

	if (!hdcp2p2_ctrl) {
		DEV_ERR("%s: invalid input\n", __func__);
		return;
	}

	if (hdcp2p2_ctrl->hdcp_txmtr_init) {
		hdcp2p2_ctrl->txmtr_ops->hdcp_txmtr_deinit(
			hdcp2p2_ctrl->hdcp_lib_handle);
		hdcp2p2_ctrl->hdcp_txmtr_init = false;
	}

	if (hdcp2p2_ctrl->hdcp_library_init) {
		hdcp_library_deinit(hdcp2p2_ctrl->hdcp_lib_handle);
		hdcp2p2_ctrl->hdcp_library_init = false;
	}

	sysfs_remove_group(hdcp2p2_ctrl->init_data.sysfs_kobj,
				&hdcp2p2_fs_attr_group);

	if (!list_empty(&hdcp2p2_ctrl->hdcp_sink_messages))
		DEV_WARN("%s: Sink msg list is not empty\n", __func__);

	list_for_each_safe(node, next, &hdcp2p2_ctrl->hdcp_sink_messages) {
		msg = list_entry(node, struct hdcp2p2_message, entry);
		kfree(msg->message_bytes);
		kfree(msg);
	}

	mutex_destroy(&hdcp2p2_ctrl->mutex);
	kfree(hdcp2p2_ctrl);
}

void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data)
{
	struct hdcp2p2_ctrl *hdcp2p2_ctrl;
@@ -633,6 +721,14 @@ void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data)
		.hdmi_hdcp_off = hdcp2p2_off
	};

	static struct hdcp_client_ops client_ops = {
		.hdcp_send_message = hdcp2p2_send_message_to_sink,
		.hdcp_tz_error = hdcp2p2_tz_error,
		.hdcp_tz_timeout = hdcp2p2_tz_timeout
	};

	static struct hdcp_txmtr_ops txmtr_ops;

	DEV_DBG("%s: HDCP2P2 feature initialization\n", __func__);

	if (!init_data || !init_data->core_io || !init_data->mutex ||
@@ -654,15 +750,20 @@ void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data)
	}

	hdcp2p2_ctrl->init_data = *init_data;
	hdcp2p2_ctrl->client_ops = &client_ops;
	hdcp2p2_ctrl->txmtr_ops = &txmtr_ops;

	rc = sysfs_create_group(init_data->sysfs_kobj,
				&hdcp2p2_fs_attr_group);
	if (rc) {
		DEV_ERR("%s: hdcp2p2 sysfs group creation failed\n", __func__);
		hdcp_library_deinit(hdcp2p2_ctrl->hdcp_lib_handle);
		goto error;
	}
	INIT_WORK(&hdcp2p2_ctrl->hdcp_sink_message_work,
			hdcp2p2_sink_message_work);
	INIT_WORK(&hdcp2p2_ctrl->hdcp_tz_message_work,
			hdcp2p2_tz_message_work);
	INIT_LIST_HEAD(&hdcp2p2_ctrl->hdcp_sink_messages);

	hdcp2p2_ctrl->sink_status = SINK_DISCONNECTED;
@@ -673,7 +774,6 @@ void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data)
	hdcp2p2_ctrl->auth_state = HDCP_STATE_INACTIVE;
	hdcp2p2_ctrl->ops = &ops;
	mutex_init(&hdcp2p2_ctrl->mutex);

	return hdcp2p2_ctrl;

error:
@@ -681,37 +781,11 @@ error:
	return ERR_PTR(rc);
}

void hdmi_hdcp2p2_deinit(void *input)
{
	struct hdcp2p2_ctrl *hdcp2p2_ctrl = (struct hdcp2p2_ctrl *)input;
	struct list_head *node, *next;
	struct hdcp2p2_message *msg;

	if (!hdcp2p2_ctrl) {
		DEV_ERR("%s: invalid input\n", __func__);
		return;
	}
	sysfs_remove_group(hdcp2p2_ctrl->init_data.sysfs_kobj,
				&hdcp2p2_fs_attr_group);

	if (!list_empty(&hdcp2p2_ctrl->hdcp_sink_messages))
		DEV_WARN("%s: Sink msg list is not empty\n", __func__);

	list_for_each_safe(node, next, &hdcp2p2_ctrl->hdcp_sink_messages) {
		msg = list_entry(node, struct hdcp2p2_message, entry);
		kfree(msg->message_bytes);
		kfree(msg);
	}

	mutex_destroy(&hdcp2p2_ctrl->mutex);
	kfree(hdcp2p2_ctrl);
}

static bool hdcp2p2_supported(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
static bool hdcp2p2_supported(struct hdcp2p2_ctrl *hdcp2p2_ctrl)
{
	u8 hdcp2version;

	int rc = hdcp2p2_read_version(ddc_ctrl, &hdcp2version);
	int rc = hdcp2p2_read_version(hdcp2p2_ctrl, &hdcp2version);
	if (rc)
		goto error;

@@ -727,12 +801,13 @@ error:

struct hdmi_hdcp_ops *hdmi_hdcp2p2_start(void *input)
{
	struct hdcp2p2_ctrl *ctrl = input;
	struct hdcp2p2_ctrl *hdcp2p2_ctrl = input;

	DEV_DBG("%s: Checking sink capability\n", __func__);
	if (hdcp2p2_supported(ctrl->init_data.ddc_ctrl))
		return ctrl->ops;
	if (hdcp2p2_supported(hdcp2p2_ctrl))
		return hdcp2p2_ctrl->ops;
	else
		return NULL;

}
+7 −0
Original line number Diff line number Diff line
@@ -1993,6 +1993,13 @@ static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on)
		if (hdmi_edid_get_sink_mode(
			hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID]) == 0)
			reg_val &= ~BIT(1); /* DVI mode */

		/*
		 * Use DATAPATH_MODE as 1 always, the new mode that also
		 * supports scrambler and HDCP 2.2. The legacy mode should no
		 * longer be used
		 */
		reg_val |= BIT(31);
	}

	DSS_REG_W(io, HDMI_CTRL, reg_val);
+21 −0
Original line number Diff line number Diff line
@@ -1116,6 +1116,27 @@ error:
} /* hdmi_ddc_write */


int hdmi_ddc_abort_transaction(struct hdmi_tx_ddc_ctrl *ddc_ctrl,
		struct hdmi_tx_ddc_data *ddc_data)
{
	int status;

	if (!ddc_ctrl || !ddc_ctrl->io || !ddc_data) {
		DEV_ERR("%s: invalid input\n", __func__);
		return -EINVAL;
	}

	status = hdmi_ddc_clear_irq(ddc_ctrl, ddc_data->what);
	if (status)
		goto error;

	DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_ARBITRATION, BIT(12)|BIT(8));

error:
	return status;

}

int hdmi_scdc_read(struct hdmi_tx_ddc_ctrl *ctrl, u32 data_type, u32 *val)
{
	struct hdmi_tx_ddc_data data = {0};
+4 −0
Original line number Diff line number Diff line
@@ -227,6 +227,8 @@
#define HDMI_SCRAMBLER_STATUS_DDC_TIMER_STATUS  (0x00000474)
#define HDMI_SCRAMBLER_STATUS_DDC_TIMER_STATUS2 (0x00000478)
#define HDMI_HW_DDC_CTRL                 (0x000004CC)
#define HDMI_HDCP_STATUS                 (0x00000500)
#define HDMI_HDCP_INT_CTRL2              (0x00000504)

/* HDMI PHY Registers */
#define HDMI_PHY_ANA_CFG0                (0x00000000)
@@ -387,6 +389,8 @@ int hdmi_ddc_isr(struct hdmi_tx_ddc_ctrl *, u32 version);
int hdmi_ddc_write(struct hdmi_tx_ddc_ctrl *, struct hdmi_tx_ddc_data *);
int hdmi_ddc_read_seg(struct hdmi_tx_ddc_ctrl *, struct hdmi_tx_ddc_data *);
int hdmi_ddc_read(struct hdmi_tx_ddc_ctrl *, struct hdmi_tx_ddc_data *);
int hdmi_ddc_abort_transaction(struct hdmi_tx_ddc_ctrl *,
		struct hdmi_tx_ddc_data *);

int hdmi_scdc_read(struct hdmi_tx_ddc_ctrl *ctrl, u32 data_type, u32 *val);
int hdmi_scdc_write(struct hdmi_tx_ddc_ctrl *ctrl, u32 data_type, u32 val);