From bd9d0784e8e7f2c7be7b66bce6e6461d4fcbff04 Mon Sep 17 00:00:00 2001 From: Ajay Singh Parmar Date: Sun, 15 Nov 2015 01:56:08 -0800 Subject: [PATCH] msm: mdss: hdmi: hdcp2p2: add tethered support Provide user an option to let the HDCP2.2 authentication run on main thread. Avoid processing on module threads thus avoiding any scheduling delays. The user may choose to switch to this mode based on its requirements. Change-Id: I76ec4a18f0fc52e99bbe76b6707511d3af6151f3 Signed-off-by: Ajay Singh Parmar --- drivers/misc/hdcp.c | 345 +++++++++++--------- drivers/video/fbdev/msm/mdss_hdmi_hdcp.h | 1 + drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c | 298 ++++++++++++----- drivers/video/fbdev/msm/mdss_hdmi_tx.c | 8 +- include/linux/hdcp_qseecom.h | 14 +- 5 files changed, 426 insertions(+), 240 deletions(-) diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c index b6675fcbaaaa..66b1135fe855 100644 --- a/drivers/misc/hdcp.c +++ b/drivers/misc/hdcp.c @@ -126,11 +126,12 @@ /*This API calls the library deinit function */ #define HDCP_LIB_DEINIT SERVICE_TXMTR_CREATE_CMD(12) -enum hdcp_app_status { - LOADED, - UNLOADED, - FAILED = -1, -}; +#define HDCP_LIB_EXECUTE(x) {\ + if (handle->tethered)\ + hdcp_lib_##x(handle);\ + else\ + queue_kthread_work(&handle->worker, &handle->wk_##x);\ +} enum hdcp_state { HDCP_STATE_INIT = 0x00, @@ -337,12 +338,12 @@ struct hdcp_lib_handle { bool feature_supported; void *client_ctx; struct hdcp_client_ops *client_ops; - struct mutex hdcp_lock; struct mutex msg_lock; struct mutex wakeup_mutex; enum hdcp_state hdcp_state; enum hdcp_lib_wakeup_cmd wakeup_cmd; bool repeater_flag; + bool tethered; struct qseecom_handle *qseecom_handle; int last_msg_sent; char *last_msg_recvd_buf; @@ -353,13 +354,13 @@ struct hdcp_lib_handle { struct completion topo_wait; struct kthread_worker worker; - struct kthread_work init; - struct kthread_work msg_sent; - struct kthread_work msg_recvd; - struct kthread_work timeout; - struct kthread_work clean; - struct kthread_work topology; - struct kthread_work stream; + struct kthread_work wk_init; + struct kthread_work wk_msg_sent; + struct kthread_work wk_msg_recvd; + struct kthread_work wk_timeout; + struct kthread_work wk_clean; + struct kthread_work wk_topology; + struct kthread_work wk_stream; }; struct hdcp_lib_message_map { @@ -367,6 +368,13 @@ struct hdcp_lib_message_map { const char *msg_name; }; +static void hdcp_lib_clean(struct hdcp_lib_handle *handle); +static void hdcp_lib_init(struct hdcp_lib_handle *handle); +static void hdcp_lib_msg_sent(struct hdcp_lib_handle *handle); +static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle); +static void hdcp_lib_timeout(struct hdcp_lib_handle *handle); +static void hdcp_lib_stream(struct hdcp_lib_handle *handle); + static struct qseecom_handle *hdcp1_handle; static bool hdcp1_supported = true; @@ -467,7 +475,7 @@ static int hdcp_lib_enable_encryption(struct hdcp_lib_handle *handle) return 0; error: if (!atomic_read(&handle->hdcp_off)) - queue_kthread_work(&handle->worker, &handle->clean); + HDCP_LIB_EXECUTE(clean); return rc; } @@ -678,21 +686,17 @@ exit: return rc; } -static void hdcp_lib_query_stream_type_work(struct kthread_work *work) +static void hdcp_lib_stream(struct hdcp_lib_handle *handle) { int rc = 0; struct hdcp_query_stream_type_req *req_buf; struct hdcp_query_stream_type_rsp *rsp_buf; - struct hdcp_lib_handle *handle = container_of(work, - struct hdcp_lib_handle, stream); if (!handle) { pr_err("invalid handle\n"); return; } - mutex_lock(&handle->hdcp_lock); - /* send command to TZ */ req_buf = (struct hdcp_query_stream_type_req *)handle-> qseecom_handle->sbuf; @@ -724,12 +728,18 @@ static void hdcp_lib_query_stream_type_work(struct kthread_work *work) handle->hdcp_timeout = rsp_buf->timeout; handle->msglen = rsp_buf->msglen; exit: - mutex_unlock(&handle->hdcp_lock); - if (!rc && !atomic_read(&handle->hdcp_off)) hdcp_lib_send_message(handle); } +static void hdcp_lib_query_stream_work(struct kthread_work *work) +{ + struct hdcp_lib_handle *handle = container_of(work, + struct hdcp_lib_handle, wk_stream); + + hdcp_lib_stream(handle); +} + static bool hdcp_lib_client_feature_supported(void *phdcpcontext) { int rc = 0; @@ -759,46 +769,46 @@ exit: static void hdcp_lib_check_worker_status(struct hdcp_lib_handle *handle) { - if (!list_empty(&handle->init.node)) + if (!list_empty(&handle->wk_init.node)) pr_debug("init work queued\n"); - if (handle->worker.current_work == &handle->init) + if (handle->worker.current_work == &handle->wk_init) pr_debug("init work executing\n"); - if (!list_empty(&handle->msg_sent.node)) + if (!list_empty(&handle->wk_msg_sent.node)) pr_debug("msg_sent work queued\n"); - if (handle->worker.current_work == &handle->msg_sent) + if (handle->worker.current_work == &handle->wk_msg_sent) pr_debug("msg_sent work executing\n"); - if (!list_empty(&handle->msg_recvd.node)) + if (!list_empty(&handle->wk_msg_recvd.node)) pr_debug("msg_recvd work queued\n"); - if (handle->worker.current_work == &handle->msg_recvd) + if (handle->worker.current_work == &handle->wk_msg_recvd) pr_debug("msg_recvd work executing\n"); - if (!list_empty(&handle->timeout.node)) + if (!list_empty(&handle->wk_timeout.node)) pr_debug("timeout work queued\n"); - if (handle->worker.current_work == &handle->timeout) + if (handle->worker.current_work == &handle->wk_timeout) pr_debug("timeout work executing\n"); - if (!list_empty(&handle->clean.node)) + if (!list_empty(&handle->wk_clean.node)) pr_debug("clean work queued\n"); - if (handle->worker.current_work == &handle->clean) + if (handle->worker.current_work == &handle->wk_clean) pr_debug("clean work executing\n"); - if (!list_empty(&handle->topology.node)) + if (!list_empty(&handle->wk_topology.node)) pr_debug("topology work queued\n"); - if (handle->worker.current_work == &handle->topology) + if (handle->worker.current_work == &handle->wk_topology) pr_debug("topology work executing\n"); - if (!list_empty(&handle->stream.node)) + if (!list_empty(&handle->wk_stream.node)) pr_debug("stream work queued\n"); - if (handle->worker.current_work == &handle->stream) + if (handle->worker.current_work == &handle->wk_stream) pr_debug("stream work executing\n"); } @@ -829,6 +839,28 @@ exit: return rc; } +static void hdcp_lib_update_exec_type(void *ctx, bool tethered) +{ + struct hdcp_lib_handle *handle = ctx; + + if (!handle) + return; + + mutex_lock(&handle->wakeup_mutex); + + if (handle->tethered == tethered) { + pr_debug("exec mode same as %s\n", + tethered ? "tethered" : "threaded"); + } else { + handle->tethered = tethered; + + pr_debug("exec mode changed to %s\n", + tethered ? "tethered" : "threaded"); + } + + mutex_unlock(&handle->wakeup_mutex); +} + static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data) { struct hdcp_lib_handle *handle; @@ -846,9 +878,9 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data) handle->wakeup_cmd = data->cmd; handle->timeout_left = data->timeout; - pr_debug("%s, timeout left: %dms\n", + pr_debug("%s, timeout left: %dms, tethered %d\n", hdcp_lib_cmd_to_str(handle->wakeup_cmd), - handle->timeout_left); + handle->timeout_left, handle->tethered); rc = hdcp_lib_check_valid_state(handle); if (rc) @@ -885,42 +917,42 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data) atomic_set(&handle->hdcp_off, 0); handle->hdcp_state = HDCP_STATE_INIT; - queue_kthread_work(&handle->worker, &handle->init); + HDCP_LIB_EXECUTE(init); break; case HDCP_LIB_WKUP_CMD_STOP: atomic_set(&handle->hdcp_off, 1); - queue_kthread_work(&handle->worker, &handle->clean); + + HDCP_LIB_EXECUTE(clean); break; case HDCP_LIB_WKUP_CMD_MSG_SEND_SUCCESS: handle->last_msg_sent = handle->listener_buf[0]; - queue_kthread_work(&handle->worker, &handle->msg_sent); + HDCP_LIB_EXECUTE(msg_sent); break; case HDCP_LIB_WKUP_CMD_MSG_SEND_FAILED: case HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED: - queue_kthread_work(&handle->worker, &handle->clean); + HDCP_LIB_EXECUTE(clean); break; case HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS: - queue_kthread_work(&handle->worker, &handle->msg_recvd); + HDCP_LIB_EXECUTE(msg_recvd); break; case HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT: - queue_kthread_work(&handle->worker, &handle->timeout); + HDCP_LIB_EXECUTE(timeout); break; case HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE: - queue_kthread_work(&handle->worker, &handle->stream); + HDCP_LIB_EXECUTE(stream); break; default: pr_err("invalid wakeup command %d\n", handle->wakeup_cmd); } exit: mutex_unlock(&handle->wakeup_mutex); + return rc; } -static void hdcp_lib_msg_sent_work(struct kthread_work *work) +static void hdcp_lib_msg_sent(struct hdcp_lib_handle *handle) { - struct hdcp_lib_handle *handle = container_of(work, - struct hdcp_lib_handle, msg_sent); struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_INVALID}; if (!handle) { @@ -928,13 +960,6 @@ 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; switch (handle->last_msg_sent) { @@ -942,15 +967,15 @@ static void hdcp_lib_msg_sent_work(struct kthread_work *work) if (handle->repeater_flag) { if (!atomic_read(&handle->hdcp_off)) queue_kthread_work(&handle->worker, - &handle->topology); + &handle->wk_topology); } - if (!hdcp_lib_enable_encryption(handle)) + if (!hdcp_lib_enable_encryption(handle)) { cdata.cmd = HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS; - else + } else { if (!atomic_read(&handle->hdcp_off)) - queue_kthread_work(&handle->worker, - &handle->clean); + HDCP_LIB_EXECUTE(clean); + } break; case REPEATER_AUTH_SEND_ACK_MESSAGE_ID: pr_debug("Repeater authentication successful\n"); @@ -960,72 +985,70 @@ static void hdcp_lib_msg_sent_work(struct kthread_work *work) cdata.timeout = handle->timeout_left; } - mutex_unlock(&handle->hdcp_lock); - hdcp_lib_wakeup_client(handle, &cdata); } +static void hdcp_lib_msg_sent_work(struct kthread_work *work) +{ + struct hdcp_lib_handle *handle = container_of(work, + struct hdcp_lib_handle, wk_msg_sent); + + if (handle->wakeup_cmd != HDCP_LIB_WKUP_CMD_MSG_SEND_SUCCESS) { + pr_err("invalid wakeup command %d\n", handle->wakeup_cmd); + return; + } + + hdcp_lib_msg_sent(handle); +} + +static void hdcp_lib_init(struct hdcp_lib_handle *handle) +{ + int rc = 0; + + if (!handle) { + pr_err("invalid handle\n"); + return; + } + + if (handle->wakeup_cmd != HDCP_LIB_WKUP_CMD_START) { + pr_err("invalid wakeup command %d\n", handle->wakeup_cmd); + return; + } + + rc = hdcp_lib_library_load(handle); + if (rc) + goto exit; + + rc = hdcp_lib_txmtr_init(handle); + if (rc) + goto exit; + + hdcp_lib_send_message(handle); + + return; +exit: + HDCP_LIB_EXECUTE(clean); +} + static void hdcp_lib_init_work(struct kthread_work *work) { - int rc = 0; - bool send_msg = false; struct hdcp_lib_handle *handle = container_of(work, - struct hdcp_lib_handle, init); + struct hdcp_lib_handle, wk_init); - if (!handle) { - pr_err("invalid handle\n"); - return; - } - - mutex_lock(&handle->hdcp_lock); - - if (handle->wakeup_cmd == HDCP_LIB_WKUP_CMD_START) { - rc = hdcp_lib_library_load(handle); - if (rc) - goto exit; - - rc = hdcp_lib_txmtr_init(handle); - if (rc) - goto exit; - - send_msg = true; - } else if (handle->wakeup_cmd == HDCP_LIB_WKUP_CMD_STOP) { - rc = hdcp_lib_txmtr_deinit(handle); - if (rc) - goto exit; - - rc = hdcp_lib_library_unload(handle); - if (rc) - goto exit; - } else { - pr_err("invalid wakeup cmd: %d\n", handle->wakeup_cmd); - } -exit: - mutex_unlock(&handle->hdcp_lock); - - if (send_msg) - hdcp_lib_send_message(handle); - - if (rc && !atomic_read(&handle->hdcp_off)) - queue_kthread_work(&handle->worker, &handle->clean); + hdcp_lib_init(handle); } -static void hdcp_lib_manage_timeout_work(struct kthread_work *work) +static void hdcp_lib_timeout(struct hdcp_lib_handle *handle) { int rc = 0; - bool send_msg = false; struct hdcp_send_timeout_req *req_buf; struct hdcp_send_timeout_rsp *rsp_buf; - struct hdcp_lib_handle *handle = container_of(work, - struct hdcp_lib_handle, timeout); if (!handle) { pr_err("invalid handle\n"); return; } - mutex_lock(&handle->hdcp_lock); - req_buf = (struct hdcp_send_timeout_req *) (handle->qseecom_handle->sbuf); req_buf->commandid = HDCP_TXMTR_SEND_MESSAGE_TIMEOUT; @@ -1068,23 +1091,24 @@ static void hdcp_lib_manage_timeout_work(struct kthread_work *work) handle->hdcp_timeout = rsp_buf->timeout; handle->msglen = rsp_buf->msglen; - send_msg = true; + hdcp_lib_send_message(handle); } } error: - mutex_unlock(&handle->hdcp_lock); - - if (send_msg) - hdcp_lib_send_message(handle); - - if (rc && !atomic_read(&handle->hdcp_off)) - queue_kthread_work(&handle->worker, &handle->clean); + if (!atomic_read(&handle->hdcp_off)) + HDCP_LIB_EXECUTE(clean); } -static void hdcp_lib_cleanup_work(struct kthread_work *work) +static void hdcp_lib_manage_timeout_work(struct kthread_work *work) { struct hdcp_lib_handle *handle = container_of(work, - struct hdcp_lib_handle, clean); + struct hdcp_lib_handle, wk_timeout); + + hdcp_lib_timeout(handle); +} + +static void hdcp_lib_clean(struct hdcp_lib_handle *handle) +{ struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_INVALID}; if (!handle) { @@ -1092,15 +1116,11 @@ static void hdcp_lib_cleanup_work(struct kthread_work *work) return; }; - mutex_lock(&handle->hdcp_lock); - - cdata.context = handle->client_ctx; - cdata.cmd = HDMI_HDCP_WKUP_CMD_STATUS_FAILED; - hdcp_lib_txmtr_deinit(handle); hdcp_lib_library_unload(handle); - mutex_unlock(&handle->hdcp_lock); + cdata.context = handle->client_ctx; + cdata.cmd = HDMI_HDCP_WKUP_CMD_STATUS_FAILED; if (!atomic_read(&handle->hdcp_off)) hdcp_lib_wakeup_client(handle, &cdata); @@ -1108,23 +1128,29 @@ static void hdcp_lib_cleanup_work(struct kthread_work *work) atomic_set(&handle->hdcp_off, 1); } -static void hdcp_lib_msg_recvd_work(struct kthread_work *work) + +static void hdcp_lib_cleanup_work(struct kthread_work *work) +{ + struct hdcp_lib_handle *handle = container_of(work, + struct hdcp_lib_handle, wk_clean); + + hdcp_lib_clean(handle); +} + +static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle) { int rc = 0; + struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_INVALID}; struct hdcp_rcvd_msg_req *req_buf; struct hdcp_rcvd_msg_rsp *rsp_buf; uint32_t msglen; - char *msg = NULL; - struct hdcp_lib_handle *handle = container_of(work, - struct hdcp_lib_handle, msg_recvd); - struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_INVALID}; + char *msg; if (!handle) { pr_err("invalid handle\n"); return; } - mutex_lock(&handle->hdcp_lock); cdata.context = handle->client_ctx; mutex_lock(&handle->msg_lock); @@ -1233,19 +1259,26 @@ static void hdcp_lib_msg_recvd_work(struct kthread_work *work) exit: kzfree(msg); - mutex_unlock(&handle->hdcp_lock); hdcp_lib_wakeup_client(handle, &cdata); if (rc && !atomic_read(&handle->hdcp_off)) - queue_kthread_work(&handle->worker, &handle->clean); + HDCP_LIB_EXECUTE(clean); +} + +static void hdcp_lib_msg_recvd_work(struct kthread_work *work) +{ + struct hdcp_lib_handle *handle = container_of(work, + struct hdcp_lib_handle, wk_msg_recvd); + + hdcp_lib_msg_recvd(handle); } 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 hdcp_lib_handle, wk_topology); if (!handle) { pr_err("invalid input\n"); @@ -1258,7 +1291,7 @@ static void hdcp_lib_topology_work(struct kthread_work *work) pr_err("topology receiver id list timeout\n"); if (!atomic_read(&handle->hdcp_off)) - queue_kthread_work(&handle->worker, &handle->clean); + HDCP_LIB_EXECUTE(clean); } } @@ -1329,32 +1362,35 @@ int hdcp1_set_keys(uint32_t *aksv_msb, uint32_t *aksv_lsb) return 0; } -int hdcp_library_register(void **pphdcpcontext, - struct hdcp_client_ops *client_ops, - struct hdcp_txmtr_ops *txmtr_ops, - void *client_ctx) +int hdcp_library_register(struct hdcp_register_data *data) { int rc = 0; struct hdcp_lib_handle *handle = NULL; - if (!pphdcpcontext) { - pr_err("invalid input: context passed\n"); + if (!data) { + pr_err("invalid input\n"); return -EINVAL; } - if (!txmtr_ops) { + if (!data->txmtr_ops) { pr_err("invalid input: txmtr context\n"); return -EINVAL; } - if (!client_ops) { + if (!data->client_ops) { pr_err("invalid input: client_ops\n"); return -EINVAL; } + if (!data->hdcp_ctx) { + pr_err("invalid input: hdcp_ctx\n"); + return -EINVAL; + } + /* populate ops to be called by client */ - txmtr_ops->feature_supported = hdcp_lib_client_feature_supported; - txmtr_ops->wakeup = hdcp_lib_wakeup; + data->txmtr_ops->feature_supported = hdcp_lib_client_feature_supported; + data->txmtr_ops->wakeup = hdcp_lib_wakeup; + data->txmtr_ops->update_exec_type = hdcp_lib_update_exec_type; handle = kzalloc(sizeof(*handle), GFP_KERNEL); if (!handle) { @@ -1362,24 +1398,26 @@ int hdcp_library_register(void **pphdcpcontext, goto unlock; } - handle->client_ctx = client_ctx; - handle->client_ops = client_ops; + handle->client_ctx = data->client_ctx; + handle->client_ops = data->client_ops; + handle->tethered = data->tethered; + + pr_debug("tethered %d\n", handle->tethered); atomic_set(&handle->hdcp_off, 0); - mutex_init(&handle->hdcp_lock); mutex_init(&handle->msg_lock); mutex_init(&handle->wakeup_mutex); init_kthread_worker(&handle->worker); - init_kthread_work(&handle->init, hdcp_lib_init_work); - init_kthread_work(&handle->msg_sent, hdcp_lib_msg_sent_work); - 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_kthread_work(&handle->wk_init, hdcp_lib_init_work); + init_kthread_work(&handle->wk_msg_sent, hdcp_lib_msg_sent_work); + init_kthread_work(&handle->wk_msg_recvd, hdcp_lib_msg_recvd_work); + init_kthread_work(&handle->wk_timeout, hdcp_lib_manage_timeout_work); + init_kthread_work(&handle->wk_clean, hdcp_lib_cleanup_work); + init_kthread_work(&handle->wk_topology, hdcp_lib_topology_work); + init_kthread_work(&handle->wk_stream, hdcp_lib_query_stream_work); init_completion(&handle->topo_wait); @@ -1389,7 +1427,7 @@ int hdcp_library_register(void **pphdcpcontext, goto error; } - *((struct hdcp_lib_handle **)pphdcpcontext) = handle; + *data->hdcp_ctx = handle; handle->thread = kthread_run(kthread_worker_fn, &handle->worker, "hdcp_tz_lib"); @@ -1423,7 +1461,6 @@ void hdcp_library_deregister(void *phdcpcontext) kzfree(handle->qseecom_handle); kzfree(handle->last_msg_recvd_buf); - mutex_destroy(&handle->hdcp_lock); mutex_destroy(&handle->wakeup_mutex); kzfree(handle->listener_buf); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h b/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h index 2e8a5126d6fd..7dda3ff5c67d 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h @@ -40,6 +40,7 @@ struct hdmi_hdcp_init_data { u32 phy_addr; u32 hdmi_tx_ver; struct msm_hdmi_mode_timing_info *timing; + bool tethered; }; struct hdmi_hdcp_ops { diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c index 8607d95c2fa6..78936832ab30 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c @@ -51,6 +51,7 @@ enum hdmi_hdcp2p2_sink_status { struct hdmi_hdcp2p2_ctrl { atomic_t auth_state; + bool tethered; enum hdmi_hdcp2p2_sink_status sink_status; /* Is sink connected */ struct hdmi_hdcp_init_data init_data; /* Feature data from HDMI drv */ struct mutex mutex; /* mutex to protect access to ctrl */ @@ -77,6 +78,11 @@ struct hdmi_hdcp2p2_ctrl { struct delayed_work link_check_work; }; +static int hdmi_hdcp2p2_auth(struct hdmi_hdcp2p2_ctrl *ctrl); +static void hdmi_hdcp2p2_send_msg(struct hdmi_hdcp2p2_ctrl *ctrl); +static void hdmi_hdcp2p2_recv_msg(struct hdmi_hdcp2p2_ctrl *ctrl); +static void hdmi_hdcp2p2_auth_status(struct hdmi_hdcp2p2_ctrl *ctrl); + static inline bool hdmi_hdcp2p2_is_valid_state(struct hdmi_hdcp2p2_ctrl *ctrl) { if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_AUTHENTICATE) @@ -133,9 +139,9 @@ static int hdmi_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data) mutex_lock(&ctrl->wakeup_mutex); - pr_debug("cmd: %s, timeout %dms\n", + pr_debug("cmd: %s, timeout %dms, tethered %d\n", hdmi_hdcp_cmd_to_str(data->cmd), - data->timeout); + data->timeout, ctrl->tethered); ctrl->wakeup_cmd = data->cmd; @@ -152,6 +158,9 @@ static int hdmi_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data) if (hdmi_hdcp2p2_copy_buf(ctrl, data)) goto exit; + if (ctrl->tethered) + goto exit; + switch (ctrl->wakeup_cmd) { case HDMI_HDCP_WKUP_CMD_SEND_MESSAGE: queue_kthread_work(&ctrl->worker, &ctrl->send_msg); @@ -179,6 +188,9 @@ static inline int hdmi_hdcp2p2_wakeup_lib(struct hdmi_hdcp2p2_ctrl *ctrl, { int rc = 0; + if (ctrl) + ctrl->wakeup_cmd = HDMI_HDCP_WKUP_CMD_INVALID; + if (ctrl && ctrl->lib && ctrl->lib->wakeup && data && (data->cmd != HDCP_LIB_WKUP_CMD_INVALID)) { rc = ctrl->lib->wakeup(data); @@ -190,6 +202,55 @@ static inline int hdmi_hdcp2p2_wakeup_lib(struct hdmi_hdcp2p2_ctrl *ctrl, return rc; } +static void hdmi_hdcp2p2_run(struct hdmi_hdcp2p2_ctrl *ctrl) +{ + if (!ctrl) { + pr_err("invalid input\n"); + return; + } + + while (1) { + switch (ctrl->wakeup_cmd) { + case HDMI_HDCP_WKUP_CMD_SEND_MESSAGE: + hdmi_hdcp2p2_send_msg(ctrl); + break; + + case HDMI_HDCP_WKUP_CMD_RECV_MESSAGE: + hdmi_hdcp2p2_recv_msg(ctrl); + break; + + case HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS: + case HDMI_HDCP_WKUP_CMD_STATUS_FAILED: + hdmi_hdcp2p2_auth_status(ctrl); + default: + goto exit; + } + } +exit: + ctrl->wakeup_cmd = HDMI_HDCP_WKUP_CMD_INVALID; +} + +int hdmi_hdcp2p2_authenticate_tethered(struct hdmi_hdcp2p2_ctrl *ctrl) +{ + int rc = 0; + + if (!ctrl) { + pr_err("invalid input\n"); + rc = -EINVAL; + goto exit; + } + + rc = hdmi_hdcp2p2_auth(ctrl); + if (rc) { + pr_err("auth failed %d\n", rc); + goto exit; + } + + hdmi_hdcp2p2_run(ctrl); +exit: + return rc; +} + static void hdmi_hdcp2p2_reset(struct hdmi_hdcp2p2_ctrl *ctrl) { if (!ctrl) { @@ -217,17 +278,27 @@ static void hdmi_hdcp2p2_off(void *input) hdmi_hdcp2p2_ddc_disable(ctrl->init_data.ddc_ctrl); - cdata.context = input; - - hdmi_hdcp2p2_wakeup(&cdata); + if (ctrl->tethered) { + hdmi_hdcp2p2_auth(ctrl); + } else { + cdata.context = input; + hdmi_hdcp2p2_wakeup(&cdata); + } } static int hdmi_hdcp2p2_authenticate(void *input) { struct hdmi_hdcp2p2_ctrl *ctrl = input; struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_AUTHENTICATE}; + u32 regval; int rc = 0; + /* Enable authentication success interrupt */ + regval = DSS_REG_R(ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2); + regval |= BIT(1) | BIT(2); + + DSS_REG_W(ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2, regval); + flush_kthread_worker(&ctrl->worker); ctrl->sink_status = SINK_CONNECTED; @@ -237,15 +308,18 @@ static int hdmi_hdcp2p2_authenticate(void *input) hdmi_scrambler_ddc_disable(ctrl->init_data.ddc_ctrl); hdmi_hdcp2p2_ddc_disable(ctrl->init_data.ddc_ctrl); - cdata.context = input; - hdmi_hdcp2p2_wakeup(&cdata); + if (ctrl->tethered) { + hdmi_hdcp2p2_authenticate_tethered(ctrl); + } else { + cdata.context = input; + hdmi_hdcp2p2_wakeup(&cdata); + } return rc; } static int hdmi_hdcp2p2_reauthenticate(void *input) { - int rc = 0; struct hdmi_hdcp2p2_ctrl *ctrl = (struct hdmi_hdcp2p2_ctrl *)input; if (!ctrl) { @@ -255,11 +329,8 @@ static int hdmi_hdcp2p2_reauthenticate(void *input) hdmi_hdcp2p2_reset((struct hdmi_hdcp2p2_ctrl *)input); - rc = hdmi_hdcp2p2_authenticate(input); - - return rc; + return hdmi_hdcp2p2_authenticate(input); } - static ssize_t hdmi_hdcp2p2_sysfs_rda_sink_status(struct device *dev, struct device_attribute *attr, char *buf) { @@ -281,6 +352,54 @@ static ssize_t hdmi_hdcp2p2_sysfs_rda_sink_status(struct device *dev, return ret; } +static ssize_t hdmi_hdcp2p2_sysfs_rda_tethered(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct hdmi_hdcp2p2_ctrl *ctrl = + hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2); + + if (!ctrl) { + pr_err("invalid input\n"); + return -EINVAL; + } + + mutex_lock(&ctrl->mutex); + ret = snprintf(buf, PAGE_SIZE, "%d\n", ctrl->tethered); + mutex_unlock(&ctrl->mutex); + + return ret; +} + +static ssize_t hdmi_hdcp2p2_sysfs_wta_tethered(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct hdmi_hdcp2p2_ctrl *ctrl = + hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2); + int rc, tethered; + + if (!ctrl) { + pr_err("invalid input\n"); + return -EINVAL; + } + + mutex_lock(&ctrl->mutex); + rc = kstrtoint(buf, 10, &tethered); + if (rc) { + pr_err("kstrtoint failed. rc=%d\n", rc); + goto exit; + } + + ctrl->tethered = !!tethered; + + if (ctrl->lib && ctrl->lib->update_exec_type && ctrl->lib_ctx) + ctrl->lib->update_exec_type(ctrl->lib_ctx, ctrl->tethered); +exit: + mutex_unlock(&ctrl->mutex); + + return count; +} + static ssize_t hdmi_hdcp2p2_sysfs_rda_trigger(struct device *dev, struct device_attribute *attr, char *buf) { @@ -366,6 +485,9 @@ static ssize_t hdmi_hdcp2p2_sysfs_wta_min_level_change(struct device *dev, cdata.context = ctrl->lib_ctx; hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata); + if (ctrl->tethered) + hdmi_hdcp2p2_run(ctrl); + if (enc_notify && ctrl->init_data.notify_status) ctrl->init_data.notify_status(ctrl->init_data.cb_data, enc_lvl); @@ -424,7 +546,7 @@ static int hdmi_hdcp2p2_ddc_read_message(struct hdmi_hdcp2p2_ctrl *ctrl, return rc; } -static int hdmi_hdcp2p2_ddc_write_message(struct hdmi_hdcp2p2_ctrl *ctrl, +int hdmi_hdcp2p2_ddc_write_message(struct hdmi_hdcp2p2_ctrl *ctrl, u8 *buf, size_t size) { struct hdmi_tx_ddc_data ddc_data; @@ -503,12 +625,15 @@ static DEVICE_ATTR(sink_status, S_IRUGO, hdmi_hdcp2p2_sysfs_rda_sink_status, NULL); static DEVICE_ATTR(hdcp2_version, S_IRUGO, hdmi_hdcp2p2_sysfs_rda_hdcp2_version, NULL); +static DEVICE_ATTR(tethered, S_IRUGO | S_IWUSR, hdmi_hdcp2p2_sysfs_rda_tethered, + hdmi_hdcp2p2_sysfs_wta_tethered); static struct attribute *hdmi_hdcp2p2_fs_attrs[] = { &dev_attr_trigger.attr, &dev_attr_min_level_change.attr, &dev_attr_sink_status.attr, &dev_attr_hdcp2_version.attr, + &dev_attr_tethered.attr, NULL, }; @@ -541,22 +666,19 @@ end: return supported; } -static void hdmi_hdcp2p2_send_msg_work(struct kthread_work *work) +static void hdmi_hdcp2p2_send_msg(struct hdmi_hdcp2p2_ctrl *ctrl) { int rc = 0; - struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work, - struct hdmi_hdcp2p2_ctrl, send_msg); struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID}; - char *msg; uint32_t msglen; + char *msg = NULL; if (!ctrl) { pr_err("invalid input\n"); - return; + rc = -EINVAL; + goto exit; } - mutex_lock(&ctrl->mutex); - cdata.context = ctrl->lib_ctx; if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { @@ -569,12 +691,14 @@ static void hdmi_hdcp2p2_send_msg_work(struct kthread_work *work) if (!msglen) { mutex_unlock(&ctrl->msg_lock); + rc = -EINVAL; goto exit; } msg = kzalloc(msglen, GFP_KERNEL); if (!msg) { mutex_unlock(&ctrl->msg_lock); + rc = -ENOMEM; goto exit; } @@ -591,41 +715,46 @@ static void hdmi_hdcp2p2_send_msg_work(struct kthread_work *work) cdata.timeout = ctrl->timeout_left; } exit: - kzfree(msg); - mutex_unlock(&ctrl->mutex); + kfree(msg); hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata); } -static void hdmi_hdcp2p2_recv_msg_work(struct kthread_work *work) +static void hdmi_hdcp2p2_send_msg_work(struct kthread_work *work) { - int rc = 0; - int timeout_hsync; - char *recvd_msg_buf = NULL; struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work, - struct hdmi_hdcp2p2_ctrl, recv_msg); + struct hdmi_hdcp2p2_ctrl, send_msg); + + hdmi_hdcp2p2_send_msg(ctrl); +} + +static void hdmi_hdcp2p2_recv_msg(struct hdmi_hdcp2p2_ctrl *ctrl) +{ + int rc, timeout_hsync; + char *recvd_msg_buf = NULL; struct hdmi_tx_hdcp2p2_ddc_data *ddc_data; struct hdmi_tx_ddc_ctrl *ddc_ctrl; struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID}; if (!ctrl) { pr_err("invalid input\n"); - return; + rc = -EINVAL; + goto exit; } - mutex_lock(&ctrl->mutex); - cdata.context = ctrl->lib_ctx; + ddc_ctrl = ctrl->init_data.ddc_ctrl; + if (!ddc_ctrl) { + pr_err("invalid ddc ctrl\n"); + rc = -EINVAL; + goto exit; + } + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { pr_err("hdcp is off\n"); goto exit; } - - ddc_ctrl = ctrl->init_data.ddc_ctrl; - if (!ddc_ctrl) - goto exit; - hdmi_ddc_config(ddc_ctrl); ddc_data = &ddc_ctrl->hdcp2p2_ddc_data; @@ -667,13 +796,17 @@ static void hdmi_hdcp2p2_recv_msg_work(struct kthread_work *work) } recvd_msg_buf = kzalloc(ddc_data->message_size, GFP_KERNEL); - if (!recvd_msg_buf) + if (!recvd_msg_buf) { + rc = -ENOMEM; goto exit; + } rc = hdmi_hdcp2p2_ddc_read_message(ctrl, recvd_msg_buf, ddc_data->message_size, ctrl->timeout_left); - if (rc) + if (rc) { pr_err("error reading message %d\n", rc); + goto exit; + } cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS; cdata.recvd_msg_buf = recvd_msg_buf; @@ -685,12 +818,19 @@ exit: else if (rc) cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED; - mutex_unlock(&ctrl->mutex); hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata); kfree(recvd_msg_buf); } +static void hdmi_hdcp2p2_recv_msg_work(struct kthread_work *work) +{ + struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work, + struct hdmi_hdcp2p2_ctrl, recv_msg); + + hdmi_hdcp2p2_recv_msg(ctrl); +} + static int hdmi_hdcp2p2_link_check(struct hdmi_hdcp2p2_ctrl *ctrl) { struct hdmi_tx_ddc_ctrl *ddc_ctrl; @@ -726,48 +866,38 @@ static int hdmi_hdcp2p2_link_check(struct hdmi_hdcp2p2_ctrl *ctrl) return hdmi_hdcp2p2_ddc_read_rxstatus(ddc_ctrl, false); } -static void hdmi_hdcp2p2_auth_status_work(struct kthread_work *work) +static void hdmi_hdcp2p2_auth_status(struct hdmi_hdcp2p2_ctrl *ctrl) { - struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work, - struct hdmi_hdcp2p2_ctrl, status); - if (!ctrl) { pr_err("invalid input\n"); return; } - mutex_lock(&ctrl->mutex); - if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { pr_err("hdcp is off\n"); - goto exit; + return; } if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_FAILED) { hdmi_hdcp2p2_auth_failed(ctrl); } else if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS) { - /* Enable interrupts */ - u32 regval = DSS_REG_R(ctrl->init_data.core_io, - HDMI_HDCP_INT_CTRL2); - pr_debug("Now authenticated. Enabling interrupts\n"); - regval |= BIT(1); - regval |= BIT(2); - regval |= BIT(5); - - DSS_REG_W(ctrl->init_data.core_io, - HDMI_HDCP_INT_CTRL2, regval); - ctrl->init_data.notify_status(ctrl->init_data.cb_data, HDCP_STATE_AUTHENTICATED); if (!hdmi_hdcp2p2_link_check(ctrl)) - schedule_delayed_work(&ctrl->link_check_work, - msecs_to_jiffies(HDCP2P2_LINK_CHECK_TIME_MS)); + queue_kthread_work(&ctrl->worker, &ctrl->link); } -exit: - mutex_unlock(&ctrl->mutex); } +static void hdmi_hdcp2p2_auth_status_work(struct kthread_work *work) +{ + struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work, + struct hdmi_hdcp2p2_ctrl, status); + + hdmi_hdcp2p2_auth_status(ctrl); +} + + static void hdmi_hdcp2p2_link_schedule_work(struct work_struct *work) { struct hdmi_hdcp2p2_ctrl *ctrl = container_of(to_delayed_work(work), @@ -792,8 +922,6 @@ static void hdmi_hdcp2p2_link_work(struct kthread_work *work) return; } - mutex_lock(&ctrl->mutex); - cdata.context = ctrl->lib_ctx; ddc_ctrl = ctrl->init_data.ddc_ctrl; @@ -842,15 +970,14 @@ static void hdmi_hdcp2p2_link_work(struct kthread_work *work) } } exit: - mutex_unlock(&ctrl->mutex); - hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata); kfree(recvd_msg_buf); - if (rc) { - /* notify hdmi tx about auth failure */ - hdmi_hdcp2p2_auth_failed(ctrl); + if (ctrl->tethered) + hdmi_hdcp2p2_run(ctrl); + if (rc) { + hdmi_hdcp2p2_auth_failed(ctrl); return; } @@ -859,19 +986,16 @@ exit: msecs_to_jiffies(HDCP2P2_LINK_CHECK_TIME_MS)); } -static void hdmi_hdcp2p2_auth_work(struct kthread_work *work) +static int hdmi_hdcp2p2_auth(struct hdmi_hdcp2p2_ctrl *ctrl) { - struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work, - struct hdmi_hdcp2p2_ctrl, auth); struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID}; + int rc = 0; if (!ctrl) { pr_err("invalid input\n"); - return; + return -EINVAL; } - mutex_lock(&ctrl->mutex); - cdata.context = ctrl->lib_ctx; if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTHENTICATING) @@ -879,10 +1003,19 @@ static void hdmi_hdcp2p2_auth_work(struct kthread_work *work) else cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; - mutex_unlock(&ctrl->mutex); - - if (hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata)) + rc = hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata); + if (rc) hdmi_hdcp2p2_auth_failed(ctrl); + + return rc; +} + +static void hdmi_hdcp2p2_auth_work(struct kthread_work *work) +{ + struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work, + struct hdmi_hdcp2p2_ctrl, auth); + + hdmi_hdcp2p2_auth(ctrl); } void hdmi_hdcp2p2_deinit(void *input) @@ -912,8 +1045,8 @@ void hdmi_hdcp2p2_deinit(void *input) void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data) { - struct hdmi_hdcp2p2_ctrl *ctrl; int rc; + struct hdmi_hdcp2p2_ctrl *ctrl; static struct hdmi_hdcp_ops ops = { .hdmi_hdcp_reauthenticate = hdmi_hdcp2p2_reauthenticate, .hdmi_hdcp_authenticate = hdmi_hdcp2p2_authenticate, @@ -926,6 +1059,7 @@ void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data) }; static struct hdcp_txmtr_ops txmtr_ops; + struct hdcp_register_data register_data; pr_debug("HDCP2P2 feature initialization\n"); @@ -947,6 +1081,7 @@ void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data) ctrl->init_data = *init_data; ctrl->lib = &txmtr_ops; + ctrl->tethered = init_data->tethered; rc = sysfs_create_group(init_data->sysfs_kobj, &hdmi_hdcp2p2_fs_attr_group); @@ -964,8 +1099,13 @@ void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data) mutex_init(&ctrl->msg_lock); mutex_init(&ctrl->wakeup_mutex); - rc = hdcp_library_register(&ctrl->lib_ctx, - &client_ops, ctrl->lib, ctrl); + register_data.hdcp_ctx = &ctrl->lib_ctx; + register_data.client_ops = &client_ops; + register_data.txmtr_ops = &txmtr_ops; + register_data.client_ctx = ctrl; + register_data.tethered = ctrl->tethered; + + rc = hdcp_library_register(®ister_data); if (rc) { pr_err("Unable to register with HDCP 2.2 library\n"); goto error; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index a88648bad161..ed66718c5e5c 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -1563,9 +1563,9 @@ end: static int hdmi_tx_init_features(struct hdmi_tx_ctrl *hdmi_ctrl, struct fb_info *fbi) { - struct hdmi_edid_init_data edid_init_data; - struct hdmi_hdcp_init_data hdcp_init_data; - struct hdmi_cec_init_data cec_init_data; + struct hdmi_edid_init_data edid_init_data = {0}; + struct hdmi_hdcp_init_data hdcp_init_data = {0}; + struct hdmi_cec_init_data cec_init_data = {0}; struct resource *res = NULL; void *fd = NULL; @@ -4332,6 +4332,8 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data, case MDSS_EVENT_BLANK: if (hdmi_tx_is_hdcp_enabled(hdmi_ctrl)) { + flush_delayed_work(&hdmi_ctrl->hdcp_cb_work); + DEV_DBG("%s: Turning off HDCP\n", __func__); hdmi_ctrl->hdcp_ops->hdmi_hdcp_off( hdmi_ctrl->hdcp_data); diff --git a/include/linux/hdcp_qseecom.h b/include/linux/hdcp_qseecom.h index 94caf29cba9f..ab39126b41ef 100644 --- a/include/linux/hdcp_qseecom.h +++ b/include/linux/hdcp_qseecom.h @@ -96,7 +96,7 @@ static inline char *hdcp_lib_cmd_to_str(uint32_t cmd) struct hdcp_txmtr_ops { int (*wakeup)(struct hdcp_lib_wakeup_data *data); bool (*feature_supported)(void *phdcpcontext); - + void (*update_exec_type)(void *ctx, bool tethered); int (*hdcp_txmtr_get_state)(void *phdcpcontext, uint32_t *state); }; @@ -105,9 +105,15 @@ struct hdcp_client_ops { int (*wakeup)(struct hdmi_hdcp_wakeup_data *data); }; -int hdcp_library_register(void **pphdcpcontext, - struct hdcp_client_ops *client_ops, - struct hdcp_txmtr_ops *txmtr_ops, void *client_ctx); +struct hdcp_register_data { + struct hdcp_client_ops *client_ops; + struct hdcp_txmtr_ops *txmtr_ops; + void *client_ctx; + void **hdcp_ctx; + bool tethered; +}; + +int hdcp_library_register(struct hdcp_register_data *data); void hdcp_library_deregister(void *phdcpcontext); bool hdcp1_check_if_supported_load_app(void); int hdcp1_set_keys(uint32_t *aksv_msb, uint32_t *aksv_lsb);