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

Commit 3fbc18bc authored by Christopher Braga's avatar Christopher Braga
Browse files

drm/msm/hdcp: make the HDCP protocol module asynchronous


 
Commands to the HDCP protocol layer can fail or block if
executed when the system enters suspend or shutdown.
Protocol layer blockage will occur until the system returns
into a power on state, potentially resulting in deadlocks for
clients who synchronously wait on protocol layer completion.

Asynchronously handle all requests to the protocol module to
ensure that clients can make progress regardless of the state of
the underlying HDCP handler. When an already queued request is
received by the protocol module, that request will be rescheduled
to the back of the queue.

Change-Id: I658dd09a81f21037cd90bbaa5b7d73363472e0b0
Signed-off-by: default avatarChristopher Braga <cbraga@codeaurora.org>
parent 92f4c23f
Loading
Loading
Loading
Loading
+12 −13
Original line number Diff line number Diff line
@@ -277,19 +277,22 @@ static void dp_hdcp2p2_reset(struct dp_hdcp2p2_ctrl *ctrl)
static int dp_hdcp2p2_register(void *input, bool mst_enabled)
{
	int rc;
	enum sde_hdcp_2x_device_type device_type;
	struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input;
	struct dp_hdcp2p2_ctrl *ctrl = input;
	struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_ENABLE};

	rc = dp_hdcp2p2_valid_handle(ctrl);
	if (rc)
		return rc;

	if (mst_enabled)
		device_type = HDCP_TXMTR_DP_MST;
		cdata.device_type = HDCP_TXMTR_DP_MST;
	else
		device_type = HDCP_TXMTR_DP;
		cdata.device_type = HDCP_TXMTR_DP;

	cdata.context = ctrl->lib_ctx;
	rc = ctrl->lib->wakeup(&cdata);

	return sde_hdcp_2x_enable(ctrl->lib_ctx, device_type);
	return rc;
}

static int dp_hdcp2p2_on(void *input)
@@ -315,25 +318,21 @@ static void dp_hdcp2p2_off(void *input)
{
	int rc;
	struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input;
	struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID};
	struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_DISABLE};

	rc = dp_hdcp2p2_valid_handle(ctrl);
	if (rc)
		return;

	if (atomic_read(&ctrl->auth_state) != HDCP_STATE_AUTH_FAIL) {
		cdata.cmd = HDCP_2X_CMD_STOP;
		cdata.context = ctrl->lib_ctx;
		dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
	}

	dp_hdcp2p2_set_interrupts(ctrl, false);

	dp_hdcp2p2_reset(ctrl);

	kthread_flush_worker(&ctrl->worker);

	sde_hdcp_2x_disable(ctrl->lib_ctx);
	cdata.context = ctrl->lib_ctx;
	ctrl->lib->wakeup(&cdata);

}

static int dp_hdcp2p2_authenticate(void *input)
+1 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ struct sde_hdcp_stream {
	u8 stream_id;
	u8 virtual_channel;
	u32 stream_handle;
	bool active;
};

struct sde_hdcp_init_data {
+154 −168
Original line number Diff line number Diff line
@@ -76,13 +76,13 @@ struct sde_hdcp_2x_ctrl {
	u8 min_enc_level;
	struct list_head stream_handles;
	u8 stream_count;
	struct stream_info *streams;
	u8 num_streams;

	struct task_struct *thread;
	struct completion response_completion;

	struct kthread_worker worker;
	struct kthread_work wk_enable;
	struct kthread_work wk_disable;
	struct kthread_work wk_init;
	struct kthread_work wk_start_auth;
	struct kthread_work wk_msg_sent;
@@ -92,8 +92,7 @@ struct sde_hdcp_2x_ctrl {
	struct kthread_work wk_stream;
	struct kthread_work wk_wait;
	struct kthread_work wk_send_type;
	struct kthread_work wk_open_stream;
	struct kthread_work wk_close_stream;
	struct kthread_work wk_manage_stream;
};

static const char *sde_hdcp_2x_message_name(int msg_id)
@@ -343,6 +342,8 @@ static bool sde_hdcp_2x_client_feature_supported(void *data)
{
	struct sde_hdcp_2x_ctrl *hdcp = data;

	kthread_flush_work(&hdcp->wk_enable);

	return hdcp2_feature_supported(hdcp->hdcp2_ctx);
}

@@ -361,24 +362,20 @@ static void sde_hdcp_2x_force_encryption(void *data, bool enable)

static int sde_hdcp_2x_check_valid_state(struct sde_hdcp_2x_ctrl *hdcp)
{
	int rc = 0;

	if (!list_empty(&hdcp->worker.work_list))
		sde_hdcp_2x_check_worker_status(hdcp);

	if (hdcp->wakeup_cmd == HDCP_2X_CMD_START) {
		if (!list_empty(&hdcp->worker.work_list)) {
			rc = -EBUSY;
			goto exit;
		}
	} else {
		if (atomic_read(&hdcp->hdcp_off)) {
	if (!hdcp->hdcp2_ctx)
		kthread_flush_work(&hdcp->wk_enable);

	if (hdcp->wakeup_cmd != HDCP_2X_CMD_ENABLE && !hdcp->hdcp2_ctx) {
		pr_err("HDCP enable must be called\n");
		return -EINVAL;
	} else if (atomic_read(&hdcp->hdcp_off)) {
		pr_debug("hdcp2.2 session tearing down\n");
			goto exit;
		}
	}
exit:
	return rc;

	return 0;
}

static void sde_hdcp_2x_clean(struct sde_hdcp_2x_ctrl *hdcp)
@@ -852,61 +849,37 @@ static struct list_head *sde_hdcp_2x_stream_present(
	return entry;
}

static void sde_hdcp_2x_open_stream(struct sde_hdcp_2x_ctrl *hdcp)
static void sde_hdcp_2x_manage_stream_work(struct kthread_work *work)
{
	int rc;
	size_t i, iterations;
	u8 stream_id;
	u8 virtual_channel;
	u32 stream_handle = 0;
	struct list_head *entry;
	struct list_head *element;
	struct sde_hdcp_stream *stream_entry;
	struct sde_hdcp_2x_ctrl *hdcp =
		container_of(work, struct sde_hdcp_2x_ctrl, wk_manage_stream);
	bool query_streams = false;

	if (!hdcp->streams) {
		pr_err("Array of streams to register is NULL\n");
		return;
	}

	iterations = min(hdcp->num_streams, (u8)(MAX_STREAM_COUNT));

	for (i  = 0; i < iterations; i++) {
		if (hdcp->stream_count == MAX_STREAM_COUNT) {
			pr_debug("Registered the maximum amount of streams\n");
			break;
		}

		stream_id = hdcp->streams[i].stream_id;
		virtual_channel = hdcp->streams[i].virtual_channel;

		pr_debug("Opening stream %d, virtual channel %d\n",
			stream_id, virtual_channel);
	entry = hdcp->stream_handles.next;
	while (entry != &hdcp->stream_handles) {
		stream_entry = list_entry(entry, struct sde_hdcp_stream, list);
		element = entry;
		entry = entry->next;

		if (sde_hdcp_2x_stream_present(hdcp, stream_id,
				virtual_channel)) {
			pr_debug("Stream %d, virtual channel %d already open\n",
				stream_id, virtual_channel);
			continue;
		}

		rc = hdcp2_open_stream(hdcp->hdcp2_ctx, virtual_channel,
				stream_id, &stream_handle);
		if (rc) {
		if (!stream_entry->active) {
			hdcp2_close_stream(hdcp->hdcp2_ctx,
				stream_entry->stream_handle);
			hdcp->stream_count--;
			list_del(element);
			kzfree(stream_entry);
			query_streams = true;
		} else if (!stream_entry->stream_handle) {
			if (hdcp2_open_stream(hdcp->hdcp2_ctx,
					stream_entry->virtual_channel,
					stream_entry->stream_id,
					&stream_entry->stream_handle))
				pr_err("Unable to open stream %d, virtual channel %d\n",
				stream_id, virtual_channel);
		} else {
			struct sde_hdcp_stream *stream =
				kzalloc(sizeof(struct sde_hdcp_stream),
					GFP_KERNEL);
			if (!stream)
				break;

			INIT_LIST_HEAD(&stream->list);
			stream->stream_handle = stream_handle;
			stream->stream_id = stream_id;
			stream->virtual_channel = virtual_channel;

			list_add(&stream->list, &hdcp->stream_handles);
			hdcp->stream_count++;

					stream_entry->stream_id,
					stream_entry->virtual_channel);
			else
				query_streams = true;
		}
	}
@@ -915,75 +888,74 @@ static void sde_hdcp_2x_open_stream(struct sde_hdcp_2x_ctrl *hdcp)
		HDCP_2X_EXECUTE(stream);
}

static void sde_hdcp_2x_open_stream_work(struct kthread_work *work)
{
	struct sde_hdcp_2x_ctrl *hdcp =
		container_of(work, struct sde_hdcp_2x_ctrl, wk_open_stream);

	sde_hdcp_2x_open_stream(hdcp);
}

static void sde_hdcp_2x_close_stream(struct sde_hdcp_2x_ctrl *hdcp)
static bool sde_remove_streams(struct sde_hdcp_2x_ctrl *hdcp,
		struct stream_info *streams, u8 num_streams)
{
	int rc;
	size_t i, iterations;
	u8 i;
	u8 stream_id;
	u8 virtual_channel;
	struct list_head *entry;
	struct sde_hdcp_stream *stream_entry;
	bool query_streams = false;

	if (!hdcp->streams) {
		pr_err("Array of streams to register is NULL\n");
		return;
	}

	iterations = min(hdcp->num_streams, (u8)(MAX_STREAM_COUNT));

	for (i = 0; i < iterations; i++) {
		if (hdcp->stream_count == 0) {
			pr_debug("No streams are currently registered\n");
			return;
		}

		stream_id = hdcp->streams[i].stream_id;
		virtual_channel = hdcp->streams[i].virtual_channel;

		pr_debug("Closing stream %d, virtual channel %d\n",
			stream_id, virtual_channel);
	bool changed = false;

	for (i = 0 ; i < num_streams; i++) {
		stream_id = streams[i].stream_id;
		virtual_channel = streams[i].virtual_channel;
		entry = sde_hdcp_2x_stream_present(hdcp, stream_id,
			virtual_channel);

		if (!entry) {
			pr_err("Unable to find stream %d, virtual channel %d\n"
				, stream_id, virtual_channel);
		if (!entry)
			continue;
		}

		stream_entry = list_entry(entry, struct sde_hdcp_stream,
			list);

		rc = hdcp2_close_stream(hdcp->hdcp2_ctx,
			stream_entry->stream_handle);
		if (rc)
			pr_err("Unable to close stream %d, virtual channel %d\n"
				, stream_id, virtual_channel);
		if (!stream_entry->stream_handle) {
			/* Stream wasn't fully initialized so remove it */
			hdcp->stream_count--;
			list_del(entry);
			kzfree(stream_entry);
		query_streams = true;
		} else {
			stream_entry->active = false;
		}
		changed = true;
	}

	if (query_streams && hdcp->authenticated)
		HDCP_2X_EXECUTE(stream);
	return changed;
}

static void sde_hdcp_2x_close_stream_work(struct kthread_work *work)
static bool sde_add_streams(struct sde_hdcp_2x_ctrl *hdcp,
		struct stream_info *streams, u8 num_streams)
{
	struct sde_hdcp_2x_ctrl *hdcp =
		container_of(work, struct sde_hdcp_2x_ctrl, wk_close_stream);
	sde_hdcp_2x_close_stream(hdcp);
	u8 i;
	u8 stream_id;
	u8 virtual_channel;
	struct sde_hdcp_stream *stream;
	bool changed = false;

	for (i = 0 ; i < num_streams; i++) {
		stream_id = streams[i].stream_id;
		virtual_channel = streams[i].virtual_channel;

		if (sde_hdcp_2x_stream_present(hdcp, stream_id,
				virtual_channel))
			continue;

		stream = kzalloc(sizeof(struct sde_hdcp_stream), GFP_KERNEL);
		if (!stream)
			continue;

		INIT_LIST_HEAD(&stream->list);
		stream->stream_handle = 0;
		stream->stream_id = stream_id;
		stream->virtual_channel = virtual_channel;
		stream->active = true;

		list_add(&stream->list, &hdcp->stream_handles);
		hdcp->stream_count++;
		changed = true;
	}

	return changed;
}

static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data)
@@ -1017,7 +989,18 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data)
		complete_all(&hdcp->response_completion);

	switch (hdcp->wakeup_cmd) {
	case HDCP_2X_CMD_ENABLE:
		kthread_cancel_work_sync(&hdcp->wk_enable);
		hdcp->device_type = data->device_type;
		HDCP_2X_EXECUTE(enable);
		break;
	case HDCP_2X_CMD_DISABLE:
		if (!atomic_xchg(&hdcp->hdcp_off, 1))
			HDCP_2X_EXECUTE(clean);
		HDCP_2X_EXECUTE(disable);
		break;
	case HDCP_2X_CMD_START:
		kthread_cancel_work_sync(&hdcp->wk_init);
		hdcp->no_stored_km = 0;
		hdcp->repeater_flag = false;
		hdcp->update_stream = false;
@@ -1028,6 +1011,7 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data)
		HDCP_2X_EXECUTE(init);
		break;
	case HDCP_2X_CMD_START_AUTH:
		kthread_cancel_work_sync(&hdcp->wk_start_auth);
		HDCP_2X_EXECUTE(start_auth);
		break;
	case HDCP_2X_CMD_STOP:
@@ -1035,6 +1019,7 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data)
		HDCP_2X_EXECUTE(clean);
		break;
	case HDCP_2X_CMD_MSG_SEND_SUCCESS:
		kthread_cancel_work_sync(&hdcp->wk_msg_sent);
		HDCP_2X_EXECUTE(msg_sent);
		break;
	case HDCP_2X_CMD_MSG_SEND_FAILED:
@@ -1043,34 +1028,37 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data)
		HDCP_2X_EXECUTE(clean);
		break;
	case HDCP_2X_CMD_MSG_RECV_SUCCESS:
		kthread_cancel_work_sync(&hdcp->wk_msg_recvd);
		HDCP_2X_EXECUTE(msg_recvd);
		break;
	case HDCP_2X_CMD_MSG_RECV_TIMEOUT:
		kthread_cancel_work_sync(&hdcp->wk_timeout);
		HDCP_2X_EXECUTE(timeout);
		break;
	case HDCP_2X_CMD_QUERY_STREAM_TYPE:
		kthread_cancel_work_sync(&hdcp->wk_stream);
		HDCP_2X_EXECUTE(stream);
		break;
	case HDCP_2X_CMD_MIN_ENC_LEVEL:
		kthread_cancel_work_sync(&hdcp->wk_send_type);
		hdcp->min_enc_level = data->min_enc_level;
		if (!hdcp->repeater_flag) {
			HDCP_2X_EXECUTE(send_type);
			break;
		}

		kthread_cancel_work_sync(&hdcp->wk_stream);
		HDCP_2X_EXECUTE(stream);
		break;
	case HDCP_2X_CMD_OPEN_STREAMS:
		hdcp->streams = data->streams;
		hdcp->num_streams = data->num_streams;
		HDCP_2X_EXECUTE(open_stream);
		kthread_flush_work(&hdcp->wk_open_stream);
		kthread_cancel_work_sync(&hdcp->wk_manage_stream);
		if (sde_add_streams(hdcp, data->streams, data->num_streams))
			HDCP_2X_EXECUTE(manage_stream);
		break;
	case HDCP_2X_CMD_CLOSE_STREAMS:
		hdcp->streams = data->streams;
		hdcp->num_streams = data->num_streams;
		HDCP_2X_EXECUTE(close_stream);
		kthread_flush_work(&hdcp->wk_close_stream);
		kthread_cancel_work_sync(&hdcp->wk_manage_stream);
		if (sde_remove_streams(hdcp, data->streams, data->num_streams))
			HDCP_2X_EXECUTE(manage_stream);
		break;
	default:
		pr_err("invalid wakeup command %d\n", hdcp->wakeup_cmd);
@@ -1081,6 +1069,37 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data)
	return rc;
}

static void sde_hdcp_2x_enable_work(struct kthread_work *work)
{
	struct sde_hdcp_2x_ctrl *hdcp =
		container_of(work, struct sde_hdcp_2x_ctrl, wk_enable);

	if (!hdcp)
		return;

	if (hdcp->hdcp2_ctx) {
		pr_debug("HDCP library context already acquired\n");
		return;
	}

	hdcp->hdcp2_ctx = hdcp2_init(hdcp->device_type);
	if (!hdcp->hdcp2_ctx)
		pr_err("Unable to acquire HDCP library handle\n");
}

static void sde_hdcp_2x_disable_work(struct kthread_work *work)
{
	struct sde_hdcp_2x_ctrl *hdcp =
		container_of(work, struct sde_hdcp_2x_ctrl, wk_disable);

	if (!hdcp->hdcp2_ctx)
		return;

	hdcp2_deinit(hdcp->hdcp2_ctx);
	hdcp->hdcp2_ctx = NULL;
}


int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data)
{
	int rc = 0;
@@ -1127,6 +1146,8 @@ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data)

	kthread_init_worker(&hdcp->worker);

	kthread_init_work(&hdcp->wk_enable,      sde_hdcp_2x_enable_work);
	kthread_init_work(&hdcp->wk_disable,      sde_hdcp_2x_disable_work);
	kthread_init_work(&hdcp->wk_init,      sde_hdcp_2x_init_work);
	kthread_init_work(&hdcp->wk_start_auth, sde_hdcp_2x_start_auth_work);
	kthread_init_work(&hdcp->wk_msg_sent,  sde_hdcp_2x_msg_sent_work);
@@ -1136,9 +1157,8 @@ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data)
	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);
	kthread_init_work(&hdcp->wk_send_type,    sde_hdcp_2x_send_type_work);
	kthread_init_work(&hdcp->wk_open_stream, sde_hdcp_2x_open_stream_work);
	kthread_init_work(&hdcp->wk_close_stream,
				sde_hdcp_2x_close_stream_work);
	kthread_init_work(&hdcp->wk_manage_stream,
			sde_hdcp_2x_manage_stream_work);

	init_completion(&hdcp->response_completion);

@@ -1164,41 +1184,6 @@ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data)
	return rc;
}

int sde_hdcp_2x_enable(void *data, enum sde_hdcp_2x_device_type device_type)
{
	int rc =  0;
	struct sde_hdcp_2x_ctrl *hdcp = data;

	if (!hdcp)
		return  -EINVAL;

	if (hdcp->hdcp2_ctx) {
		pr_debug("HDCP library context already acquired\n");
		return 0;
	}

	hdcp->device_type = device_type;
	hdcp->hdcp2_ctx = hdcp2_init(hdcp->device_type);
	if (!hdcp->hdcp2_ctx) {
		pr_err("Unable to acquire HDCP library handle\n");
		return -ENOMEM;
	}

	return rc;
}

void sde_hdcp_2x_disable(void *data)
{
	struct sde_hdcp_2x_ctrl *hdcp = data;

	if (!hdcp->hdcp2_ctx)
		return;

	kthread_flush_worker(&hdcp->worker);
	hdcp2_deinit(hdcp->hdcp2_ctx);
	hdcp->hdcp2_ctx = NULL;
}

void sde_hdcp_2x_deregister(void *data)
{
	struct sde_hdcp_2x_ctrl *hdcp = data;
@@ -1206,8 +1191,9 @@ void sde_hdcp_2x_deregister(void *data)
	if (!hdcp)
		return;

	sde_hdcp_2x_disable(data);
	kthread_stop(hdcp->thread);
	hdcp2_deinit(hdcp->hdcp2_ctx);
	hdcp->hdcp2_ctx = NULL;
	mutex_destroy(&hdcp->wakeup_mutex);
	kzfree(hdcp);
}
+3 −0
Original line number Diff line number Diff line
@@ -39,6 +39,8 @@
 */
enum sde_hdcp_2x_wakeup_cmd {
	HDCP_2X_CMD_INVALID,
	HDCP_2X_CMD_ENABLE,
	HDCP_2X_CMD_DISABLE,
	HDCP_2X_CMD_START,
	HDCP_2X_CMD_START_AUTH,
	HDCP_2X_CMD_STOP,
@@ -93,6 +95,7 @@ enum sde_hdcp_2x_device_type {
 */
struct sde_hdcp_2x_wakeup_data {
	enum sde_hdcp_2x_wakeup_cmd cmd;
	enum sde_hdcp_2x_device_type device_type;
	void *context;
	uint32_t total_message_length;
	uint32_t timeout;