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

Commit c7417c17 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drm/msm/dp: Avoid HDCP cleanup during system suspend"

parents ec3e52ab 981df3b0
Loading
Loading
Loading
Loading
+32 −2
Original line number Diff line number Diff line
@@ -105,6 +105,8 @@ struct dp_display_private {
	struct work_struct connect_work;
	struct work_struct attention_work;
	struct mutex session_lock;
	bool suspended;
	bool hdcp_delayed_off;

	u32 active_stream_cnt;
	struct dp_mst mst;
@@ -315,6 +317,19 @@ static void dp_display_hdcp_cb_work(struct work_struct *work)
	if (!dp->power_on || !dp->is_connected || atomic_read(&dp->aborted))
		return;

	if (dp->suspended) {
		pr_debug("System suspending. Delay HDCP operations\n");
		queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ);
		return;
	}

	if (dp->hdcp_delayed_off) {
		if (dp->hdcp.ops && dp->hdcp.ops->off)
			dp->hdcp.ops->off(dp->hdcp.data);
		dp_display_update_hdcp_status(dp, true);
		dp->hdcp_delayed_off = false;
	}

	drm_dp_dpcd_readb(dp->aux->drm_aux, DP_SINK_STATUS, &sink_status);
	sink_status &= (DP_RECEIVE_PORT_0_STATUS | DP_RECEIVE_PORT_1_STATUS);
	if (sink_status < 1) {
@@ -936,9 +951,7 @@ static void dp_display_clean(struct dp_display_private *dp)

	dp->power_on = false;

	mutex_lock(&dp->session_lock);
	dp->ctrl->off(dp->ctrl);
	mutex_unlock(&dp->session_lock);
}

static int dp_display_handle_disconnect(struct dp_display_private *dp)
@@ -1694,6 +1707,13 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel)

	if (dp_display_is_hdcp_enabled(dp) &&
			status->hdcp_state != HDCP_STATE_INACTIVE) {

		if (dp->suspended) {
			pr_debug("Can't perform HDCP cleanup while suspended. Defer\n");
			dp->hdcp_delayed_off = true;
			goto stream;
		}

		flush_delayed_work(&dp->hdcp_cb_work);
		if (dp->mst.mst_active) {
			dp_display_hdcp_deregister_stream(dp,
@@ -2627,14 +2647,24 @@ static int dp_display_remove(struct platform_device *pdev)

static int dp_pm_prepare(struct device *dev)
{
	struct dp_display_private *dp = container_of(g_dp_display,
			struct dp_display_private, dp_display);

	dp_display_set_mst_state(g_dp_display, PM_SUSPEND);

	dp->suspended = true;

	return 0;
}

static void dp_pm_complete(struct device *dev)
{
	struct dp_display_private *dp = container_of(g_dp_display,
			struct dp_display_private, dp_display);

	dp_display_set_mst_state(g_dp_display, PM_DEFAULT);

	dp->suspended = false;
}

static const struct dev_pm_ops dp_pm_ops = {
+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;