From 83af37f1e59df7ae6662c0622f20acb93667d065 Mon Sep 17 00:00:00 2001 From: Christopher Braga Date: Mon, 22 Apr 2019 14:33:25 -0400 Subject: [PATCH 1/5] disp: msm: 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: Christopher Braga --- msm/dp/dp_hdcp2p2.c | 24 ++-- msm/sde_hdcp.h | 1 + msm/sde_hdcp_2x.c | 279 +++++++++++++++++++++++--------------------- msm/sde_hdcp_2x.h | 6 +- 4 files changed, 161 insertions(+), 149 deletions(-) diff --git a/msm/dp/dp_hdcp2p2.c b/msm/dp/dp_hdcp2p2.c index f71c25e66c..61404be550 100644 --- a/msm/dp/dp_hdcp2p2.c +++ b/msm/dp/dp_hdcp2p2.c @@ -238,19 +238,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; - return sde_hdcp_2x_enable(ctrl->lib_ctx, device_type); + cdata.context = ctrl->lib_ctx; + rc = ctrl->lib->wakeup(&cdata); + + return rc; } static int dp_hdcp2p2_on(void *input) @@ -276,25 +279,20 @@ 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_park(ctrl->thread); - sde_hdcp_2x_disable(ctrl->lib_ctx); + cdata.context = ctrl->lib_ctx; + ctrl->lib->wakeup(&cdata); } static int dp_hdcp2p2_authenticate(void *input) diff --git a/msm/sde_hdcp.h b/msm/sde_hdcp.h index 5c1dc4af20..49a6a951f5 100644 --- a/msm/sde_hdcp.h +++ b/msm/sde_hdcp.h @@ -51,6 +51,7 @@ struct sde_hdcp_stream { u8 stream_id; u8 virtual_channel; u32 stream_handle; + bool active; }; struct sde_hdcp_init_data { diff --git a/msm/sde_hdcp_2x.c b/msm/sde_hdcp_2x.c index f578e09cd2..5131bc3f9a 100644 --- a/msm/sde_hdcp_2x.c +++ b/msm/sde_hdcp_2x.c @@ -49,6 +49,7 @@ struct sde_hdcp_2x_ctrl { u32 timeout_left; u32 wait_timeout_ms; u32 total_message_length; + atomic_t enable_pending; bool no_stored_km; bool feature_supported; bool force_encryption; @@ -66,8 +67,6 @@ 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; @@ -301,6 +300,9 @@ static bool sde_hdcp_2x_client_feature_supported(void *data) { struct sde_hdcp_2x_ctrl *hdcp = data; + while (atomic_read(&hdcp->enable_pending)) + usleep_range(1000, 1500); + return hdcp2_feature_supported(hdcp->hdcp2_ctx); } @@ -671,62 +673,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(struct sde_hdcp_2x_ctrl *hdcp) { - int rc; - size_t iterations, i; - u8 stream_id; - u8 virtual_channel; - u32 stream_handle = 0; + struct list_head *entry; + struct list_head *element; + 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 == 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); - - 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) { - 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++; + 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 (!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_entry->stream_id, + stream_entry->virtual_channel); + else + query_streams = true; } } @@ -734,62 +711,78 @@ static void sde_hdcp_2x_open_stream(struct sde_hdcp_2x_ctrl *hdcp) sde_hdcp_2x_query_stream(hdcp); } -static void sde_hdcp_2x_close_stream(struct sde_hdcp_2x_ctrl *hdcp) + +static bool sde_hdcp_2x_remove_streams(struct sde_hdcp_2x_ctrl *hdcp, + struct stream_info *streams, u8 num_streams) { - int rc; - size_t iterations, i; + 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); - hdcp->stream_count--; - list_del(entry); - kzfree(stream_entry); - query_streams = true; + if (!stream_entry->stream_handle) { + /* Stream wasn't fully initialized so remove it */ + hdcp->stream_count--; + list_del(entry); + kzfree(stream_entry); + } else { + stream_entry->active = false; + } + changed = true; } - if (query_streams && hdcp->authenticated) - sde_hdcp_2x_query_stream(hdcp); + return changed; } +static bool sde_hdcp_2x_add_streams(struct sde_hdcp_2x_ctrl *hdcp, + struct stream_info *streams, u8 num_streams) +{ + 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; +} + + /** sde_hdcp_2x_wakeup() - wakeup the module to execute a requested command * @data: data required for executing corresponding command. * @@ -813,18 +806,22 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data) hdcp->timeout_left = data->timeout; hdcp->total_message_length = data->total_message_length; hdcp->min_enc_level = data->min_enc_level; - hdcp->streams = data->streams; - hdcp->num_streams = data->num_streams; if (!completion_done(&hdcp->response_completion)) complete_all(&hdcp->response_completion); - kfifo_put(&hdcp->cmd_q, data->cmd); - switch (data->cmd) { + case HDCP_2X_CMD_ENABLE: + if (!atomic_cmpxchg(&hdcp->enable_pending, 0, 1)) { + hdcp->device_type = data->device_type; + kfifo_put(&hdcp->cmd_q, data->cmd); + wake_up(&hdcp->wait_q); + } + break; case HDCP_2X_CMD_STOP: atomic_set(&hdcp->hdcp_off, 1); + kfifo_put(&hdcp->cmd_q, data->cmd); kthread_park(hdcp->thread); break; case HDCP_2X_CMD_START: @@ -836,10 +833,26 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data) hdcp->timeout_left = 0; atomic_set(&hdcp->hdcp_off, 0); + kfifo_put(&hdcp->cmd_q, data->cmd); kthread_unpark(hdcp->thread); wake_up(&hdcp->wait_q); break; + case HDCP_2X_CMD_OPEN_STREAMS: + if (sde_hdcp_2x_add_streams(hdcp, data->streams, + data->num_streams)) { + kfifo_put(&hdcp->cmd_q, data->cmd); + wake_up(&hdcp->wait_q); + } + break; + case HDCP_2X_CMD_CLOSE_STREAMS: + if (sde_hdcp_2x_remove_streams(hdcp, data->streams, + data->num_streams)) { + kfifo_put(&hdcp->cmd_q, data->cmd); + wake_up(&hdcp->wait_q); + } + break; default: + kfifo_put(&hdcp->cmd_q, data->cmd); wake_up(&hdcp->wait_q); break; } @@ -847,6 +860,30 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data) return rc; } +static void sde_hdcp_2x_enable(struct sde_hdcp_2x_ctrl *hdcp) +{ + 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(struct sde_hdcp_2x_ctrl *hdcp) +{ + if (!hdcp->hdcp2_ctx) + return; + + hdcp2_deinit(hdcp->hdcp2_ctx); + hdcp->hdcp2_ctx = NULL; +} + static int sde_hdcp_2x_main(void *data) { struct sde_hdcp_2x_ctrl *hdcp = data; @@ -870,6 +907,15 @@ static int sde_hdcp_2x_main(void *data) continue; switch (cmd) { + case HDCP_2X_CMD_ENABLE: + sde_hdcp_2x_enable(hdcp); + atomic_set(&hdcp->enable_pending, 0); + break; + case HDCP_2X_CMD_DISABLE: + if (!atomic_xchg(&hdcp->hdcp_off, 1)) + sde_hdcp_2x_clean(hdcp); + sde_hdcp_2x_disable(hdcp); + break; case HDCP_2X_CMD_START: sde_hdcp_2x_init(hdcp); break; @@ -904,10 +950,8 @@ static int sde_hdcp_2x_main(void *data) sde_hdcp_2x_query_stream(hdcp); break; case HDCP_2X_CMD_OPEN_STREAMS: - sde_hdcp_2x_open_stream(hdcp); - break; case HDCP_2X_CMD_CLOSE_STREAMS: - sde_hdcp_2x_close_stream(hdcp); + sde_hdcp_2x_manage_stream(hdcp); break; default: break; @@ -961,6 +1005,7 @@ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data) init_waitqueue_head(&hdcp->wait_q); atomic_set(&hdcp->hdcp_off, 1); + atomic_set(&hdcp->enable_pending, 0); init_completion(&hdcp->response_completion); @@ -985,40 +1030,6 @@ unlock: 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; - - hdcp2_deinit(hdcp->hdcp2_ctx); - hdcp->hdcp2_ctx = NULL; -} - void sde_hdcp_2x_deregister(void *data) { struct sde_hdcp_2x_ctrl *hdcp = data; @@ -1026,7 +1037,7 @@ void sde_hdcp_2x_deregister(void *data) if (!hdcp) return; - sde_hdcp_2x_disable(data); kthread_stop(hdcp->thread); + sde_hdcp_2x_disable(data); kzfree(hdcp); } diff --git a/msm/sde_hdcp_2x.h b/msm/sde_hdcp_2x.h index cfcd7ce1b5..4669564dec 100644 --- a/msm/sde_hdcp_2x.h +++ b/msm/sde_hdcp_2x.h @@ -32,6 +32,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, @@ -79,6 +81,7 @@ enum sde_hdcp_2x_device_type { /** * struct sde_hdcp_2x_lib_wakeup_data - command and data send to HDCP driver * @cmd: command type + * @device_type type of device in use by the HDCP driver * @context: void pointer to the HDCP driver instance * @buf: message received from the sink * @buf_len: length of message received from the sink @@ -88,6 +91,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; @@ -211,7 +215,5 @@ struct sde_hdcp_2x_register_data { /* functions for the HDCP 2.2 state machine module */ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data); -int sde_hdcp_2x_enable(void *data, enum sde_hdcp_2x_device_type device_type); -void sde_hdcp_2x_disable(void *data); void sde_hdcp_2x_deregister(void *data); #endif From c828b764c824ab29c09bcb01d54be65cd89f672b Mon Sep 17 00:00:00 2001 From: Christopher Braga Date: Mon, 22 Apr 2019 14:57:08 -0400 Subject: [PATCH 2/5] disp: msm: dp: Avoid HDCP cleanup during system suspend Calls to QSEECOM are disallowed when the system is heading into a suspend to RAM state. Delay all HDCP cleanup until after resume to ensure HDCP is properly handled. Change-Id: I7ebf567e50c28d39fc3c99dcb5d571a5d1e8dd93 Signed-off-by: Christopher Braga --- msm/dp/dp_display.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 8ac7f46ce4..5b9b3921bf 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -97,6 +97,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; @@ -307,6 +309,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) { @@ -890,9 +905,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) @@ -1649,6 +1662,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, @@ -2588,14 +2608,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 = { From a09ee5a065a1218dcb7c9acf73515d442dbe3113 Mon Sep 17 00:00:00 2001 From: Christopher Braga Date: Mon, 22 Apr 2019 16:13:48 -0400 Subject: [PATCH 3/5] disp: msm: update hdcp 2.2 stream management sequence When HDCP stream management needs to be re-performed, the SDE_HDCP state machine breaks down and reads or writes to the wrong DPCD HDCP offsets. This results in an authentication failure, effectively allowing stream management to succeed only on the first attempt. Update the SDE_HDCP stream management logic to properly take its current state into account in re-management scenarios. This will reduce the need for full re-authentications upon content type updates. Change-Id: Iebefbb060e49a7082b2d4c9efbed22a83a2a5ec5 Signed-off-by: Christopher Braga --- msm/sde_hdcp_2x.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/msm/sde_hdcp_2x.c b/msm/sde_hdcp_2x.c index 5131bc3f9a..d54b3961d2 100644 --- a/msm/sde_hdcp_2x.c +++ b/msm/sde_hdcp_2x.c @@ -193,10 +193,7 @@ 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; + return REP_STREAM_READY; default: pr_err("Unknown message ID (%d)\n", hdcp->last_msg); return -EINVAL; @@ -406,6 +403,12 @@ static void sde_hdcp_2x_query_stream(struct sde_hdcp_2x_ctrl *hdcp) return; } + if (!hdcp->authenticated && + hdcp->app_data.response.data[0] != REP_SEND_ACK) { + pr_debug("invalid state. HDCP repeater not authenticated\n"); + return; + } + rc = hdcp2_app_comm(hdcp->hdcp2_ctx, HDCP2_CMD_QUERY_STREAM, &hdcp->app_data); if (rc) @@ -420,8 +423,11 @@ static void sde_hdcp_2x_query_stream(struct sde_hdcp_2x_ctrl *hdcp) pr_debug("[tz]: %s\n", sde_hdcp_2x_message_name( hdcp->app_data.response.data[0])); exit: - if (!rc && !atomic_read(&hdcp->hdcp_off)) + if (!rc && !atomic_read(&hdcp->hdcp_off)) { + /* Modify last message to ensure the proper message is sent */ + hdcp->last_msg = REP_SEND_ACK; sde_hdcp_2x_send_message(hdcp); + } } static void sde_hdcp_2x_initialize_command(struct sde_hdcp_2x_ctrl *hdcp, @@ -624,9 +630,8 @@ static void sde_hdcp_2x_msg_recvd(struct sde_hdcp_2x_ctrl *hdcp) if (msg[0] == LC_SEND_L_PRIME && out_msg == LC_INIT) hdcp->resend_lc_init = true; - hdcp->resend_stream_manage = false; if (msg[0] == REP_STREAM_READY && out_msg == REP_STREAM_MANAGE) - hdcp->resend_stream_manage = true; + pr_debug("resend %s\n", sde_hdcp_2x_message_name(out_msg)); if (out_msg == AKE_NO_STORED_KM) hdcp->no_stored_km = true; From 7893b22bd257b2a5b029c1952e17c698d029b770 Mon Sep 17 00:00:00 2001 From: Christopher Braga Date: Mon, 22 Apr 2019 16:30:44 -0400 Subject: [PATCH 4/5] disp: msm: Account for in progress stream management If stream registration is invoked while HDCP is still authenticating, the new stream information may not get sent to the downstream repeater. Check if stream management is already in progress during stream registration, and queue another round of stream management if so. Change-Id: I497e9a4da310870fe792293033f1fb0ce8fbee4c Signed-off-by: Christopher Braga --- msm/sde_hdcp_2x.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/msm/sde_hdcp_2x.c b/msm/sde_hdcp_2x.c index d54b3961d2..c4f7c210d0 100644 --- a/msm/sde_hdcp_2x.c +++ b/msm/sde_hdcp_2x.c @@ -193,6 +193,7 @@ 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: + hdcp->resend_stream_manage = false; return REP_STREAM_READY; default: pr_err("Unknown message ID (%d)\n", hdcp->last_msg); @@ -602,7 +603,9 @@ static void sde_hdcp_2x_msg_recvd(struct sde_hdcp_2x_ctrl *hdcp) pr_debug("[tz]: %s\n", sde_hdcp_2x_message_name(out_msg)); if (msg[0] == REP_STREAM_READY && out_msg != REP_STREAM_MANAGE) { - if (!hdcp->authenticated) { + if (hdcp->resend_stream_manage) { + pr_debug("resend stream management\n"); + } else if (!hdcp->authenticated) { rc = hdcp2_app_comm(hdcp->hdcp2_ctx, HDCP2_CMD_EN_ENCRYPTION, &hdcp->app_data); @@ -712,8 +715,14 @@ static void sde_hdcp_2x_manage_stream(struct sde_hdcp_2x_ctrl *hdcp) } } - if (query_streams && hdcp->authenticated) - sde_hdcp_2x_query_stream(hdcp); + if (query_streams) { + if (hdcp->authenticated) { + sde_hdcp_2x_query_stream(hdcp); + } else if (hdcp->last_msg == REP_STREAM_MANAGE || + hdcp->last_msg == REP_STREAM_READY) { + hdcp->resend_stream_manage = true; + } + } } From 9b4453096ab191a8809155bd04fa5abd2b0a34e8 Mon Sep 17 00:00:00 2001 From: Christopher Braga Date: Mon, 22 Apr 2019 16:39:22 -0400 Subject: [PATCH 5/5] disp: msm: dp: Prevent disconnect from stopping HDCP If a DP stream is disconnected when HDCP is not fully authenticated, authentication may not continue for remaining streams. This occurs due to the worker thread rescheduling itself on auth fail while disconnect cancels any pending work. Update disconnect logic to wait for HDCP authentication to complete before stream de-registration, and reschedule the HDCP worker if HDCP did not fully authenticate prior to disconnect. Change-Id: I0f8934e4f34e4f5a8015587e852131795a3dad21 Signed-off-by: Christopher Braga --- msm/dp/dp_display.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 5b9b3921bf..0d83844a6a 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -99,6 +99,7 @@ struct dp_display_private { struct mutex session_lock; bool suspended; bool hdcp_delayed_off; + bool hdcp_abort; u32 active_stream_cnt; struct dp_mst mst; @@ -306,7 +307,8 @@ static void dp_display_hdcp_cb_work(struct work_struct *work) dp = container_of(dw, struct dp_display_private, hdcp_cb_work); - if (!dp->power_on || !dp->is_connected || atomic_read(&dp->aborted)) + if (!dp->power_on || !dp->is_connected || atomic_read(&dp->aborted) || + dp->hdcp_abort) return; if (dp->suspended) { @@ -1660,13 +1662,16 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel) goto end; } + dp->hdcp_abort = true; + cancel_delayed_work_sync(&dp->hdcp_cb_work); if (dp_display_is_hdcp_enabled(dp) && status->hdcp_state != HDCP_STATE_INACTIVE) { + bool off = true; if (dp->suspended) { pr_debug("Can't perform HDCP cleanup while suspended. Defer\n"); dp->hdcp_delayed_off = true; - goto stream; + goto clean; } flush_delayed_work(&dp->hdcp_cb_work); @@ -1677,18 +1682,19 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel) if (i != dp_panel->stream_id && dp->active_panels[i]) { pr_debug("Streams are still active. Skip disabling HDCP\n"); - goto stream; + off = false; } } } - if (dp->hdcp.ops->off) - dp->hdcp.ops->off(dp->hdcp.data); - - dp_display_update_hdcp_status(dp, true); + if (off) { + if (dp->hdcp.ops->off) + dp->hdcp.ops->off(dp->hdcp.data); + dp_display_update_hdcp_status(dp, true); + } } -stream: +clean: if (dp_panel->audio_supported) dp_panel->audio->off(dp_panel->audio); @@ -1701,8 +1707,10 @@ end: static int dp_display_disable(struct dp_display *dp_display, void *panel) { + int i; struct dp_display_private *dp = NULL; struct dp_panel *dp_panel = NULL; + struct dp_link_hdcp_status *status; if (!dp_display || !panel) { pr_err("invalid input\n"); @@ -1711,6 +1719,7 @@ static int dp_display_disable(struct dp_display *dp_display, void *panel) dp = container_of(dp_display, struct dp_display_private, dp_display); dp_panel = panel; + status = &dp->link->hdcp_status; mutex_lock(&dp->session_lock); @@ -1721,6 +1730,16 @@ static int dp_display_disable(struct dp_display *dp_display, void *panel) dp_display_stream_disable(dp, dp_panel); dp_display_update_dsc_resources(dp, dp_panel, false); + + dp->hdcp_abort = false; + for (i = DP_STREAM_0; i < DP_STREAM_MAX; i++) { + if (dp->active_panels[i]) { + if (status->hdcp_state != HDCP_STATE_AUTHENTICATED) + queue_delayed_work(dp->wq, &dp->hdcp_cb_work, + HZ/4); + break; + } + } end: mutex_unlock(&dp->session_lock); return 0;