From e8591b7730777af87c1ea2b6d7742118c2531bf3 Mon Sep 17 00:00:00 2001 From: Ajay Singh Parmar Date: Mon, 28 Sep 2015 20:55:43 -0700 Subject: [PATCH] msm: mdss: hdmi: create separate environments for hdcp lib and hdmi Isolate the execution environments for HDMI HDCP2.2 driver and HDCP library by creating separate threads and executing each work on dedicated kworker. Do not call each other's functions directly. Wakeup the other thread when needed and let the independent module execute their corresponding work and acknowledge by waking up other thread back. Change-Id: I67bca61b92c831451ce3482a759a214b1e5d6578 Signed-off-by: Ajay Singh Parmar --- drivers/misc/hdcp.c | 899 ++++++++-------- drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c | 1077 +++++++++---------- drivers/video/fbdev/msm/mdss_hdmi_util.c | 3 + include/linux/hdcp_qseecom.h | 71 +- 4 files changed, 1037 insertions(+), 1013 deletions(-) diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c index e42d4fb5587e..3acfb3e272cf 100644 --- a/drivers/misc/hdcp.c +++ b/drivers/misc/hdcp.c @@ -28,8 +28,8 @@ #include #include #include -#include #include +#include #include "qseecom_kernel.h" @@ -312,54 +312,58 @@ struct __attribute__ ((__packed__)) repeater_info_struct { }; /* - * struct hdcp2p2_handle - handle for hdcp client + * struct hdcp_lib_handle - handle for hdcp client * @qseecom_handle - for sending commands to qseecom - * @hdcp_workqueue - work queue for hdcp thread - * @auth_work - work placed in the work queue * @listener_buf - buffer containing message shared with the client * @msglen - size message in the buffer * @tz_ctxhandle - context handle shared with tz * @hdcp_timeout - timeout in msecs shared for hdcp messages - * @ske_flag - flag to indicate that msg from tz is ske_send_eks * @client_ctx - client context maintained by hdmi * @client_ops - handle to call APIs exposed by hdcp client * @timeout_lock - this lock protects hdcp_timeout field * @msg_lock - this lock protects the message buffer */ -struct hdcp2p2_handle { - struct workqueue_struct *hdcp_workqueue; - struct work_struct auth_work; +struct hdcp_lib_handle { unsigned char *listener_buf; uint32_t msglen; uint32_t tz_ctxhandle; uint32_t hdcp_timeout; - bool ske_flag; bool no_stored_km_flag; void *client_ctx; struct hdcp_client_ops *client_ops; struct mutex hdcp_lock; enum hdcp_state hdcp_state; + enum hdcp_lib_wakeup_cmd wakeup_cmd; bool repeater_flag; struct qseecom_handle *qseecom_handle; - uint32_t ref_cnt; - atomic_t hdcp_off; + int last_msg_sent; + char *last_msg_recvd_buf; + uint32_t last_msg_recvd_len; + atomic_t hdcp_off_pending; + + struct task_struct *thread; + + 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 hdcp2p2_message_map { +struct hdcp_lib_message_map { int msg_id; const char *msg_name; }; -static void hdcp2p2_cleanup(void *phdcpcontext); - -static const char *hdcp2p2_message_name(int msg_id) +static const char *hdcp_lib_message_name(int msg_id) { /* * Message ID map. The first number indicates the message number * assigned to the message by the HDCP 2.2 spec. This is also the first * byte of every HDCP 2.2 authentication protocol message. */ - static struct hdcp2p2_message_map hdcp2p2_msg_map[] = { + static struct hdcp_lib_message_map hdcp_lib_msg_map[] = { {2, "AKE_INIT"}, {3, "AKE_SEND_CERT"}, {4, "AKE_NO_STORED_KM"}, @@ -376,283 +380,115 @@ static const char *hdcp2p2_message_name(int msg_id) }; int i; - for (i = 0; i < ARRAY_SIZE(hdcp2p2_msg_map); i++) { - if (msg_id == hdcp2p2_msg_map[i].msg_id) - return hdcp2p2_msg_map[i].msg_name; + for (i = 0; i < ARRAY_SIZE(hdcp_lib_msg_map); i++) { + if (msg_id == hdcp_lib_msg_map[i].msg_id) + return hdcp_lib_msg_map[i].msg_name; } return "UNKNOWN"; } -static int hdcp2p2_manage_timeout(struct hdcp2p2_handle *handle) +static inline int hdcp_lib_report_error(struct hdcp_lib_handle *handle) { - int rc = 0; - struct hdcp_send_timeout_req *req_buf; - struct hdcp_send_timeout_rsp *rsp_buf; + int rc = -EINVAL; - if (!handle) { - pr_err("invalid input\n"); - rc = -EINVAL; - goto exit; + if (handle->client_ops->wakeup) { + rc = handle->client_ops->wakeup( + handle->client_ctx, HDMI_HDCP_STATUS_FAIL, + 0, 0, 0); + if (rc) + pr_err("error: report error\n"); + } else { + pr_err("error: client ops wakeup not defined\n"); } - req_buf = (struct hdcp_send_timeout_req *) - (handle->qseecom_handle->sbuf); - req_buf->commandid = HDCP_TXMTR_SEND_MESSAGE_TIMEOUT; - req_buf->ctxhandle = handle->tz_ctxhandle; - - rsp_buf = (struct hdcp_send_timeout_rsp *)(handle-> - qseecom_handle->sbuf + QSEECOM_ALIGN(sizeof( - struct hdcp_send_timeout_req))); - - rc = qseecom_send_command(handle->qseecom_handle, req_buf, - QSEECOM_ALIGN(sizeof(struct hdcp_send_timeout_req)), rsp_buf, - QSEECOM_ALIGN(sizeof(struct hdcp_send_timeout_rsp))); - - if ((rc < 0) || (rsp_buf->status != HDCP_SUCCESS)) { - pr_err("qseecom cmd failed for with err = %d status = %d\n", - rc, rsp_buf->status); - rc = -EINVAL; - goto exit; - } - - if (rsp_buf->commandid == HDCP_TXMTR_SEND_MESSAGE_TIMEOUT) { - pr_err("HDCP_TXMTR_SEND_MESSAGE_TIMEOUT\n"); - rc = -EAGAIN; - goto exit; - } - - /* - * if the response contains LC_Init message - * send the message again to TZ - */ - if ((rsp_buf->commandid == HDCP_TXMTR_PROCESS_RECEIVED_MESSAGE) && - ((int)rsp_buf->message[0] == LC_INIT_MESSAGE_ID) && - (rsp_buf->msglen == LC_INIT_MESSAGE_SIZE)) { - if (!atomic_read(&handle->hdcp_off)) - queue_work(handle->hdcp_workqueue, &handle->auth_work); - else - pr_err("not queueing auth work, hdcp off underway\n"); - } -exit: return rc; } -static int hdcp2p2_enable_encryption(struct hdcp2p2_handle *handle) +static inline int hdcp_lib_recv_message(struct hdcp_lib_handle *handle) +{ + int rc = -EINVAL; + + if (handle->client_ops->wakeup) { + rc = handle->client_ops->wakeup( + handle->client_ctx, HDMI_HDCP_RECV_MESSAGE, + 0, 0, handle->hdcp_timeout); + if (rc) + pr_err("error receiving message from client\n"); + } else { + pr_err("error: client ops wakeup not defined\n"); + } + + return rc; +} + +static inline int hdcp_lib_send_message(struct hdcp_lib_handle *handle) +{ + int rc = -EINVAL; + + pr_debug("send msg: %s to sink\n", + hdcp_lib_message_name((int)handle->listener_buf[0])); + + if (handle->client_ops->wakeup) { + rc = handle->client_ops->wakeup( + handle->client_ctx, HDMI_HDCP_SEND_MESSAGE, + handle->listener_buf, handle->msglen, 0); + if (!rc) + handle->last_msg_sent = (int)handle->listener_buf[0]; + else + pr_err("error sending message to client\n"); + } else { + pr_err("error: client ops wakeup not defined\n"); + } + + return rc; +} + +static int hdcp_lib_enable_encryption(struct hdcp_lib_handle *handle) { int rc = 0; struct hdcp_set_hw_key_req *req_buf; struct hdcp_set_hw_key_rsp *rsp_buf; - uint32_t timeout = 0; - if (handle->ske_flag) { - /* - * wait for 200ms before enabling encryption - * as per hdcp2p2 sepcifications. - */ - msleep(SLEEP_SET_HW_KEY_MS); + /* + * wait for 200ms before enabling encryption + * as per hdcp2p2 sepcifications. + */ + msleep(SLEEP_SET_HW_KEY_MS); - req_buf = (struct hdcp_set_hw_key_req *)( - handle->qseecom_handle->sbuf); - req_buf->commandid = HDCP_TXMTR_SET_HW_KEY; - req_buf->ctxhandle = handle->tz_ctxhandle; - - rsp_buf = (struct hdcp_set_hw_key_rsp *)( - handle->qseecom_handle->sbuf + QSEECOM_ALIGN( - sizeof(struct hdcp_set_hw_key_req))); - - rc = qseecom_send_command(handle->qseecom_handle, req_buf, - QSEECOM_ALIGN(sizeof(struct hdcp_set_hw_key_req)), - rsp_buf, QSEECOM_ALIGN(sizeof( - struct hdcp_set_hw_key_rsp))); - - if ((rc < 0) || (rsp_buf->status < 0)) { - pr_err("qseecom cmd failed with err = %d status = %d\n", - rc, rsp_buf->status); - timeout = -1; - goto exit; - } - - /* reached an authenticated state */ - handle->hdcp_state |= HDCP_STATE_AUTHENTICATED; - - /* if not a repeater then there is no need to start the timer */ - if (!handle->repeater_flag) - goto exit; - - /* set the timeout value to the actual - 200ms */ - timeout = handle->hdcp_timeout - SLEEP_SET_HW_KEY_MS; - } -exit: - return timeout; -} - -static int hdcp2p2_send_message(struct hdcp2p2_handle *handle) -{ - int rc = -EINVAL; - char *rcvr_buf; - uint32_t timeout; - - if (!handle) { - pr_err("invalid input\n"); - goto exit; - } - - if (!handle->listener_buf) { - pr_err("no message to copy\n"); - goto exit; - } - - if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) { - pr_err("hdcp library not loaded\n"); - goto exit; - } - - if (!(handle->hdcp_state & HDCP_STATE_TXMTR_INIT)) { - pr_err("txmtr is not initialized\n"); - goto exit; - } - - rcvr_buf = kzalloc(MAX_TX_MESSAGE_SIZE, GFP_KERNEL); - if (!rcvr_buf) - goto exit; - - memcpy(rcvr_buf, handle->listener_buf, MAX_TX_MESSAGE_SIZE); - - timeout = handle->hdcp_timeout; - - if (atomic_read(&handle->hdcp_off)) { - pr_err("send msg check: hdcp off underway\n"); - rc = 0; - goto exit; - } - - if (handle->client_ops->hdcp_send_message) { - rc = handle->client_ops->hdcp_send_message( - handle->client_ctx, - rcvr_buf, handle->msglen); - if (rc) { - pr_err("error sending message to client\n"); - goto exit; - } - } else { - pr_err("send_msg client ops not defined\n"); - goto exit; - } - - rc = hdcp2p2_enable_encryption(handle); - if (rc < 0) { - pr_err("error enabling ecryption\n"); - goto exit; - } else if (rc > 0) { - timeout = rc; - } - - if (handle->hdcp_timeout == 0) { - rc = 0; - goto exit; - } - - if (atomic_read(&handle->hdcp_off)) { - pr_err("recv msg check: hdcp off underway\n"); - rc = 0; - goto exit; - } - - if (handle->client_ops->hdcp_recv_message) { - rc = handle->client_ops->hdcp_recv_message( - handle->client_ctx, rcvr_buf, - handle->msglen, timeout); - if (rc == -ETIMEDOUT) { - pr_err("msg read time %dms expired\n", timeout); - rc = hdcp2p2_manage_timeout(handle); - } - } else { - pr_err("recv_msg client ops not defined\n"); - } -exit: - kzfree(rcvr_buf); - - if (rc) - hdcp2p2_cleanup(handle); - - return rc; -} - -static void hdcp2p2_authenticate_work(struct work_struct *work) -{ - struct hdcp2p2_handle *handle = container_of(work, - struct hdcp2p2_handle, auth_work); - - hdcp2p2_send_message(handle); -} - -static int hdcp2p2_txmtr_query_stream_type(void *phdcpcontext) -{ - int rc = 0; - struct hdcp_query_stream_type_req *req_buf; - struct hdcp_query_stream_type_rsp *rsp_buf; - struct hdcp2p2_handle *handle = phdcpcontext; - - if (!handle) { - pr_err("invalid input\n"); - return -EINVAL; - } - - if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) { - pr_debug("hdcp library not loaded\n"); - goto end; - } - - if (!(handle->hdcp_state & HDCP_STATE_TXMTR_INIT)) { - pr_err("txmtr is not initialized\n"); - rc = -EINVAL; - goto end; - } - - flush_work(&handle->auth_work); - - /* send command to TZ */ - req_buf = (struct hdcp_query_stream_type_req *)handle-> - qseecom_handle->sbuf; - req_buf->commandid = HDCP_TXMTR_QUERY_STREAM_TYPE; + req_buf = (struct hdcp_set_hw_key_req *)( + handle->qseecom_handle->sbuf); + req_buf->commandid = HDCP_TXMTR_SET_HW_KEY; req_buf->ctxhandle = handle->tz_ctxhandle; - rsp_buf = (struct hdcp_query_stream_type_rsp *)(handle-> - qseecom_handle->sbuf + QSEECOM_ALIGN(sizeof( - struct hdcp_query_stream_type_req))); + + rsp_buf = (struct hdcp_set_hw_key_rsp *)( + handle->qseecom_handle->sbuf + QSEECOM_ALIGN( + sizeof(struct hdcp_set_hw_key_req))); rc = qseecom_send_command(handle->qseecom_handle, req_buf, - QSEECOM_ALIGN(sizeof(struct hdcp_query_stream_type_req)), rsp_buf, - QSEECOM_ALIGN(sizeof(struct hdcp_query_stream_type_rsp))); + QSEECOM_ALIGN(sizeof(struct hdcp_set_hw_key_req)), + rsp_buf, QSEECOM_ALIGN(sizeof( + struct hdcp_set_hw_key_rsp))); - if ((rc < 0) || (rsp_buf->status < 0) || (rsp_buf->msglen <= 0) || - (rsp_buf->commandid != HDCP_TXMTR_QUERY_STREAM_TYPE) || - (rsp_buf->msg == NULL)) { - pr_err("qseecom cmd failed with err=%d status=%d\n", + if ((rc < 0) || (rsp_buf->status < 0)) { + pr_err("qseecom cmd failed with err = %d status = %d\n", rc, rsp_buf->status); - rc = -1; - goto end; + rc = -EINVAL; + goto error; } - pr_debug("message received is %s\n", - hdcp2p2_message_name((int)rsp_buf->msg[0])); + /* reached an authenticated state */ + handle->hdcp_state |= HDCP_STATE_AUTHENTICATED; - memset(handle->listener_buf, 0, MAX_TX_MESSAGE_SIZE); - memcpy(handle->listener_buf, (unsigned char *)rsp_buf->msg, - rsp_buf->msglen); - handle->hdcp_timeout = rsp_buf->timeout; - handle->msglen = rsp_buf->msglen; + pr_debug("success\n"); + return 0; +error: + if (!atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->clean); - if (!atomic_read(&handle->hdcp_off)) - queue_work(handle->hdcp_workqueue, &handle->auth_work); - else - pr_err("not queueing auth work, hdcp off underway\n"); - - pr_debug("hdcp txmtr query stream type success\n"); -end: return rc; } -static int hdcp2p2_library_load(struct hdcp2p2_handle *handle) +static int hdcp_lib_library_load(struct hdcp_lib_handle *handle) { int rc = 0; struct hdcp_init_req *req_buf; @@ -660,12 +496,12 @@ static int hdcp2p2_library_load(struct hdcp2p2_handle *handle) if (!handle) { pr_err("invalid input\n"); - goto end; + goto exit; } - if (handle->ref_cnt > 0) { - pr_err("error: ref_cnt %d, should be 0\n", handle->ref_cnt); - goto end; + if (handle->hdcp_state & HDCP_STATE_APP_LOADED) { + pr_err("library already loaded\n"); + return rc; } /* @@ -676,7 +512,7 @@ static int hdcp2p2_library_load(struct hdcp2p2_handle *handle) TZAPP_NAME, QSEECOM_SBUFF_SIZE); if (rc) { pr_err("qseecom_start_app failed %d\n", rc); - goto end; + goto exit; } pr_debug("qseecom_start_app success\n"); @@ -693,18 +529,17 @@ static int hdcp2p2_library_load(struct hdcp2p2_handle *handle) if (rc < 0) { pr_err("qseecom cmd failed err = %d\n", rc); - goto end; + goto exit; } - pr_debug("loading secure app success\n"); + pr_debug("success\n"); - handle->ref_cnt++; handle->hdcp_state |= HDCP_STATE_APP_LOADED; -end: +exit: return rc; } -static int hdcp2p2_library_unload(struct hdcp2p2_handle *handle) +static int hdcp_lib_library_unload(struct hdcp_lib_handle *handle) { int rc = 0; struct hdcp_deinit_req *req_buf; @@ -712,12 +547,12 @@ static int hdcp2p2_library_unload(struct hdcp2p2_handle *handle) if (!handle) { pr_err("invalid input\n"); - goto end; + goto exit; } - if (!handle->ref_cnt) { - pr_err("error: ref_cnt 0\n"); - goto end; + if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) { + pr_err("library not loaded\n"); + return rc; } /* unloading app by sending hdcp_lib_deinit cmd */ @@ -733,26 +568,23 @@ static int hdcp2p2_library_unload(struct hdcp2p2_handle *handle) if (rc < 0) { pr_err("qseecom cmd failed err = %d\n", rc); - goto end; + goto exit; } /* deallocate the resources for qseecom handle */ rc = qseecom_shutdown_app(&handle->qseecom_handle); if (rc) { pr_err("qseecom_shutdown_app failed err: %d\n", rc); - goto end; + goto exit; } - pr_debug("unloading secure app success\n"); - - handle->ref_cnt--; handle->hdcp_state &= ~HDCP_STATE_APP_LOADED; -end: - + pr_debug("success\n"); +exit: return rc; } -static int hdcp2p2_txmtr_init(struct hdcp2p2_handle *handle) +static int hdcp_lib_txmtr_init(struct hdcp_lib_handle *handle) { int rc = 0; struct hdcp_init_req *req_buf; @@ -789,10 +621,14 @@ static int hdcp2p2_txmtr_init(struct hdcp2p2_handle *handle) (rsp_buf->msglen <= 0) || (rsp_buf->message == NULL)) { pr_err("qseecom cmd failed with err = %d, status = %d\n", rc, rsp_buf->status); - rc = -1; + rc = -EINVAL; goto exit; } + pr_debug("recvd %s from TZ at %dms\n", + hdcp_lib_message_name((int)rsp_buf->message[0]), + jiffies_to_msecs(jiffies)); + /* send the response to HDMI driver */ memset(handle->listener_buf, 0, MAX_TX_MESSAGE_SIZE); memcpy(handle->listener_buf, (unsigned char *)rsp_buf->message, @@ -803,17 +639,12 @@ static int hdcp2p2_txmtr_init(struct hdcp2p2_handle *handle) handle->tz_ctxhandle = rsp_buf->ctxhandle; handle->hdcp_state |= HDCP_STATE_TXMTR_INIT; - if (!atomic_read(&handle->hdcp_off)) - queue_work(handle->hdcp_workqueue, &handle->auth_work); - else - pr_err("not queueing auth work, hdcp off underway\n"); - - pr_debug("hdcp txmtr successfully initialized\n"); + pr_debug("success\n"); exit: return rc; } -static int hdcp2p2_txmtr_deinit(struct hdcp2p2_handle *handle) +static int hdcp_lib_txmtr_deinit(struct hdcp_lib_handle *handle) { int rc = 0; struct hdcp_deinit_req *req_buf; @@ -853,148 +684,372 @@ static int hdcp2p2_txmtr_deinit(struct hdcp2p2_handle *handle) (rsp_buf->commandid != HDCP_TXMTR_DEINIT)) { pr_err("qseecom cmd failed with err = %d status = %d\n", rc, rsp_buf->status); - rc = -1; + rc = -EINVAL; goto exit; } handle->hdcp_state &= ~HDCP_STATE_TXMTR_INIT; - pr_debug("hdcp txmtr successfully deinitialized\n"); + pr_debug("success\n"); exit: return rc; } -static bool hdcp2p2_client_feature_supported(void *phdcpcontext) +static int hdcp_lib_query_stream_type(void *phdcpcontext) { int rc = 0; - bool supported = false; - struct hdcp2p2_handle *handle = phdcpcontext; - - if (!handle) { - pr_err("invalid input\n"); - goto end; - } - - rc = hdcp2p2_library_load(handle); - if (!rc) { - pr_debug("HDCP2p2 supported\n"); - hdcp2p2_library_unload(handle); - supported = true; - } -end: - return supported; -} - -static int hdcp2p2_client_start(void *phdcpcontext) -{ - int rc = 0; - struct hdcp2p2_handle *handle = phdcpcontext; + struct hdcp_query_stream_type_req *req_buf; + struct hdcp_query_stream_type_rsp *rsp_buf; + struct hdcp_lib_handle *handle = phdcpcontext; if (!handle) { pr_err("invalid input\n"); return -EINVAL; } - mutex_lock(&handle->hdcp_lock); + if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) { + pr_debug("hdcp library not loaded\n"); + goto exit; + } - handle->no_stored_km_flag = 0; - handle->ske_flag = 0; - handle->repeater_flag = 0; - handle->ref_cnt = 0; - handle->hdcp_state = HDCP_STATE_INIT; + if (!(handle->hdcp_state & HDCP_STATE_TXMTR_INIT)) { + pr_err("txmtr is not initialized\n"); + rc = -EINVAL; + goto exit; + } - rc = hdcp2p2_library_load(handle); - if (rc) - pr_err("error loading library\n"); + flush_kthread_worker(&handle->worker); + + /* send command to TZ */ + req_buf = (struct hdcp_query_stream_type_req *)handle-> + qseecom_handle->sbuf; + req_buf->commandid = HDCP_TXMTR_QUERY_STREAM_TYPE; + req_buf->ctxhandle = handle->tz_ctxhandle; + rsp_buf = (struct hdcp_query_stream_type_rsp *)(handle-> + qseecom_handle->sbuf + QSEECOM_ALIGN(sizeof( + struct hdcp_query_stream_type_req))); + + rc = qseecom_send_command(handle->qseecom_handle, req_buf, + QSEECOM_ALIGN(sizeof(struct hdcp_query_stream_type_req)), rsp_buf, + QSEECOM_ALIGN(sizeof(struct hdcp_query_stream_type_rsp))); + + if ((rc < 0) || (rsp_buf->status < 0) || (rsp_buf->msglen <= 0) || + (rsp_buf->commandid != HDCP_TXMTR_QUERY_STREAM_TYPE) || + (rsp_buf->msg == NULL)) { + pr_err("qseecom cmd failed with err=%d status=%d\n", + rc, rsp_buf->status); + rc = -EINVAL; + goto exit; + } + + pr_debug("message received is %s\n", + hdcp_lib_message_name((int)rsp_buf->msg[0])); + + memset(handle->listener_buf, 0, MAX_TX_MESSAGE_SIZE); + memcpy(handle->listener_buf, (unsigned char *)rsp_buf->msg, + rsp_buf->msglen); + handle->hdcp_timeout = rsp_buf->timeout; + handle->msglen = rsp_buf->msglen; + + if (!atomic_read(&handle->hdcp_off_pending)) + hdcp_lib_send_message(handle); else - rc = hdcp2p2_txmtr_init(handle); - - mutex_unlock(&handle->hdcp_lock); + goto exit; + pr_debug("success\n"); +exit: return rc; } -static int hdcp2p2_client_end(struct hdcp2p2_handle *handle) +static bool hdcp_lib_client_feature_supported(void *phdcpcontext) { + int rc = 0; + bool supported = false; + struct hdcp_lib_handle *handle = phdcpcontext; + + if (!handle) { + pr_err("invalid input\n"); + goto exit; + } + + rc = hdcp_lib_library_load(handle); + if (!rc) { + pr_debug("HDCP2p2 supported\n"); + hdcp_lib_library_unload(handle); + supported = true; + } +exit: + return supported; +} + +static int hdcp_lib_wakeup(void *phdcpcontext, + enum hdcp_lib_wakeup_cmd cmd, char *msg, uint32_t msglen) +{ + struct hdcp_lib_handle *handle = phdcpcontext; + int rc = 0; + if (!handle) return -EINVAL; mutex_lock(&handle->hdcp_lock); + handle->wakeup_cmd = cmd; + mutex_unlock(&handle->hdcp_lock); - hdcp2p2_txmtr_deinit(handle); - hdcp2p2_library_unload(handle); + if (handle->wakeup_cmd == HDCP_WKUP_CMD_STOP) { + atomic_set(&handle->hdcp_off_pending, 1); + flush_kthread_worker(&handle->worker); + atomic_set(&handle->hdcp_off_pending, 0); + } + + mutex_lock(&handle->hdcp_lock); + + pr_debug("wakeup_cmd: %s\n", hdcp_cmd_to_str(handle->wakeup_cmd)); + + if (msglen) { + handle->last_msg_recvd_len = msglen; + + handle->last_msg_recvd_buf = kzalloc(msglen, GFP_KERNEL); + if (!handle->last_msg_recvd_buf) { + rc = -ENOMEM; + goto exit; + } + + memcpy(handle->last_msg_recvd_buf, msg, msglen); + } + + switch (handle->wakeup_cmd) { + case HDCP_WKUP_CMD_START: + handle->no_stored_km_flag = 0; + handle->repeater_flag = 0; + handle->hdcp_state = HDCP_STATE_INIT; + + if (!atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->init); + break; + case HDCP_WKUP_CMD_STOP: + /* wait for any work being executed */ + queue_kthread_work(&handle->worker, &handle->clean); + break; + case HDCP_WKUP_CMD_MSG_SEND_SUCCESS: + if (!atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->msg_sent); + break; + case HDCP_WKUP_CMD_MSG_SEND_FAILED: + case HDCP_WKUP_CMD_MSG_RECV_FAILED: + if (!atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->clean); + break; + case HDCP_WKUP_CMD_MSG_RECV_SUCCESS: + if (!atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->msg_recvd); + break; + case HDCP_WKUP_CMD_MSG_RECV_TIMEOUT: + if (!atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->timeout); + break; + default: + pr_err("invalid wakeup command %d\n", handle->wakeup_cmd); + } +exit: + mutex_unlock(&handle->hdcp_lock); + return 0; +} + +static void hdcp_lib_msg_sent_work(struct kthread_work *work) +{ + struct hdcp_lib_handle *handle = container_of(work, + struct hdcp_lib_handle, msg_sent); + + if (!handle) { + pr_err("invalid handle\n"); + return; + } + + mutex_lock(&handle->hdcp_lock); + + if (handle->wakeup_cmd == HDCP_WKUP_CMD_MSG_SEND_SUCCESS) { + if (handle->last_msg_sent == SKE_SEND_EKS_MESSAGE_ID) { + if (!hdcp_lib_enable_encryption(handle)) + handle->client_ops->wakeup( + handle->client_ctx, + HDMI_HDCP_STATUS_SUCCESS, + 0, 0, 0); + } else { + hdcp_lib_recv_message(handle); + } + } else { + pr_err("invalid wakeup command %d\n", handle->wakeup_cmd); + } mutex_unlock(&handle->hdcp_lock); - return 0; + return; } -static int hdcp2p2_client_stop(void *phdcpcontext) +static void hdcp_lib_init_work(struct kthread_work *work) { - struct hdcp2p2_handle *handle = phdcpcontext; + int rc = 0; + struct hdcp_lib_handle *handle = container_of(work, + struct hdcp_lib_handle, init); - if (!handle) - return -EINVAL; + if (!handle) { + pr_err("invalid handle\n"); + return; + } - atomic_set(&handle->hdcp_off, 1); + mutex_lock(&handle->hdcp_lock); - flush_work(&handle->auth_work); + if (handle->wakeup_cmd == HDCP_WKUP_CMD_START) { + rc = hdcp_lib_library_load(handle); + if (rc) + goto exit; - hdcp2p2_client_end(handle); + rc = hdcp_lib_txmtr_init(handle); + if (rc) + goto exit; - atomic_set(&handle->hdcp_off, 0); + rc = hdcp_lib_send_message(handle); + if (rc) + goto exit; + } else if (handle->wakeup_cmd == HDCP_WKUP_CMD_STOP) { + rc = hdcp_lib_txmtr_deinit(handle); + if (rc) + goto exit; - return 0; + rc = hdcp_lib_library_unload(handle); + if (rc) + goto exit; + } else { + pr_err("invalid wakeup cmd: %d\n", handle->wakeup_cmd); + } +exit: + if (rc && !atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->clean); + + mutex_unlock(&handle->hdcp_lock); + return; } -static void hdcp2p2_cleanup(void *phdcpcontext) +static void hdcp_lib_manage_timeout_work(struct kthread_work *work) { - struct hdcp2p2_handle *handle = phdcpcontext; + int rc = 0; + 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; + req_buf->ctxhandle = handle->tz_ctxhandle; + + rsp_buf = (struct hdcp_send_timeout_rsp *)(handle-> + qseecom_handle->sbuf + QSEECOM_ALIGN(sizeof( + struct hdcp_send_timeout_req))); + + rc = qseecom_send_command(handle->qseecom_handle, req_buf, + QSEECOM_ALIGN(sizeof(struct hdcp_send_timeout_req)), rsp_buf, + QSEECOM_ALIGN(sizeof(struct hdcp_send_timeout_rsp))); + + if ((rc < 0) || (rsp_buf->status != HDCP_SUCCESS)) { + pr_err("qseecom cmd failed for with err = %d status = %d\n", + rc, rsp_buf->status); + rc = -EINVAL; + goto error; + } + + if (rsp_buf->commandid == HDCP_TXMTR_SEND_MESSAGE_TIMEOUT) { + pr_err("HDCP_TXMTR_SEND_MESSAGE_TIMEOUT\n"); + rc = -EINVAL; + goto error; + } + + /* + * if the response contains LC_Init message + * send the message again to TZ + */ + if ((rsp_buf->commandid == HDCP_TXMTR_PROCESS_RECEIVED_MESSAGE) && + ((int)rsp_buf->message[0] == LC_INIT_MESSAGE_ID) && + (rsp_buf->msglen == LC_INIT_MESSAGE_SIZE)) { + if (!atomic_read(&handle->hdcp_off_pending)) { + /* keep local copy of TZ response */ + memset(handle->listener_buf, 0, MAX_TX_MESSAGE_SIZE); + memcpy(handle->listener_buf, + (unsigned char *)rsp_buf->message, + rsp_buf->msglen); + handle->hdcp_timeout = rsp_buf->timeout; + handle->msglen = rsp_buf->msglen; + + hdcp_lib_send_message(handle); + } + } +error: + if (rc && !atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->clean); + + mutex_unlock(&handle->hdcp_lock); +} + +static void hdcp_lib_cleanup_work(struct kthread_work *work) +{ + struct hdcp_lib_handle *handle = container_of(work, + struct hdcp_lib_handle, clean); if (!handle) { pr_err("invalid input\n"); return; }; - if (atomic_read(&handle->hdcp_off)) { + mutex_lock(&handle->hdcp_lock); + + if (atomic_read(&handle->hdcp_off_pending)) { pr_err("hdcp off underway\n"); - return; - } - - hdcp2p2_client_end(handle); - - /* notify client so that client can cleanup */ - handle->client_ops->hdcp_tz_error(handle->client_ctx); -} - -static int hdcp2p2_txmtr_process_message(void *phdcpcontext, - unsigned char *msg, uint32_t msglen) -{ - int rc = 0; - struct hdcp2p2_handle *handle = phdcpcontext; - struct hdcp_rcvd_msg_req *req_buf; - struct hdcp_rcvd_msg_rsp *rsp_buf; - - if (!handle) { - pr_err("invalid input\n"); - rc = -EINVAL; goto exit; } - if ((!msg) || (msglen <= 0)) { + hdcp_lib_txmtr_deinit(handle); + hdcp_lib_library_unload(handle); + + if (handle->wakeup_cmd != HDCP_WKUP_CMD_STOP) + hdcp_lib_report_error(handle); +exit: + mutex_unlock(&handle->hdcp_lock); +} + +static void hdcp_lib_msg_recvd_work(struct kthread_work *work) +{ + int rc = 0; + struct hdcp_rcvd_msg_req *req_buf; + struct hdcp_rcvd_msg_rsp *rsp_buf; + uint32_t msglen; + char *msg; + struct hdcp_lib_handle *handle = container_of(work, + struct hdcp_lib_handle, msg_recvd); + + if (!handle) { + pr_err("invalid handle\n"); + return; + } + + mutex_lock(&handle->hdcp_lock); + + msg = handle->last_msg_recvd_buf; + msglen = handle->last_msg_recvd_len; + + if (msglen <= 0) { pr_err("invalid msg\n"); rc = -EINVAL; goto exit; } - if (!(handle->hdcp_state & HDCP_STATE_APP_LOADED)) { - pr_err("hdcp library is not loaded\n"); - goto exit; - } - - if (!(handle->hdcp_state & HDCP_STATE_TXMTR_INIT)) { - pr_err("txmtr not initialized\n"); - goto exit; - } + pr_debug("msg received: %s from sink\n", + hdcp_lib_message_name((int)msg[0])); /* send the message to QSEECOM */ req_buf = (struct hdcp_rcvd_msg_req *)(handle-> @@ -1008,14 +1063,18 @@ static int hdcp2p2_txmtr_process_message(void *phdcpcontext, qseecom_handle->sbuf + QSEECOM_ALIGN(sizeof( struct hdcp_rcvd_msg_req))); + pr_debug("writing %s to TZ at %dms\n", + hdcp_lib_message_name((int)msg[0]), + jiffies_to_msecs(jiffies)); + rc = qseecom_send_command(handle->qseecom_handle, req_buf, QSEECOM_ALIGN(sizeof(struct hdcp_rcvd_msg_req)), rsp_buf, QSEECOM_ALIGN(sizeof(struct hdcp_rcvd_msg_rsp))); - /* No response was obtained from TZ */ + /* get next message from sink if we receive H PRIME on no store km */ if ((msg[0] == AKE_SEND_H_PRIME_MESSAGE_ID) && handle->no_stored_km_flag) { - pr_debug("Got HPrime from tx, nothing sent to rx\n"); + hdcp_lib_recv_message(handle); goto exit; } @@ -1030,12 +1089,13 @@ static int hdcp2p2_txmtr_process_message(void *phdcpcontext, (rsp_buf->msg == NULL)) { pr_err("qseecom cmd failed with err=%d status=%d\n", rc, rsp_buf->status); - rc = -1; + rc = -EINVAL; goto exit; } - pr_debug("message received is %s\n", - hdcp2p2_message_name((int)rsp_buf->msg[0])); + pr_debug("recvd %s from TZ at %dms\n", + hdcp_lib_message_name((int)rsp_buf->msg[0]), + jiffies_to_msecs(jiffies)); /* set the flag if response is AKE_No_Stored_km */ if (((int)rsp_buf->msg[0] == AKE_NO_STORED_KM_MESSAGE_ID)) { @@ -1045,15 +1105,9 @@ static int hdcp2p2_txmtr_process_message(void *phdcpcontext, handle->no_stored_km_flag = 0; } - /* - * set ske flag is response is SKE_SEND_EKS - * also set repeater flag if it's a repeater - */ - handle->ske_flag = 0; + /* check if it's a repeater */ if ((rsp_buf->msg[0] == SKE_SEND_EKS_MESSAGE_ID) && (rsp_buf->msglen == SKE_SEND_EKS_MESSAGE_SIZE)) { - handle->ske_flag = 1; - /* check if it's a repeater */ if ((rsp_buf->flag == HDCP_TXMTR_SUBSTATE_WAITING_FOR_RECIEVERID_LIST) && (rsp_buf->timeout > 0)) @@ -1066,15 +1120,15 @@ static int hdcp2p2_txmtr_process_message(void *phdcpcontext, handle->hdcp_timeout = rsp_buf->timeout; handle->msglen = rsp_buf->msglen; - if (!atomic_read(&handle->hdcp_off)) - queue_work(handle->hdcp_workqueue, &handle->auth_work); - else - pr_err("not queueing auth work, hdcp off underway\n"); - - pr_debug("hdcp txmtr process message success\n"); - + if (!atomic_read(&handle->hdcp_off_pending)) + hdcp_lib_send_message(handle); exit: - return rc; + kzfree(handle->last_msg_recvd_buf); + + if (rc && !atomic_read(&handle->hdcp_off_pending)) + queue_kthread_work(&handle->worker, &handle->clean); + + mutex_unlock(&handle->hdcp_lock); } /* APIs exposed to all clients */ @@ -1136,7 +1190,7 @@ int hdcp_library_register(void **pphdcpcontext, void *client_ctx) { int rc = 0; - struct hdcp2p2_handle *handle = NULL; + struct hdcp_lib_handle *handle = NULL; if (!pphdcpcontext) { pr_err("invalid input: context passed\n"); @@ -1154,12 +1208,9 @@ int hdcp_library_register(void **pphdcpcontext, } /* populate ops to be called by client */ - txmtr_ops->start = hdcp2p2_client_start; - txmtr_ops->stop = hdcp2p2_client_stop; - txmtr_ops->feature_supported = hdcp2p2_client_feature_supported; - txmtr_ops->process_message = hdcp2p2_txmtr_process_message; - txmtr_ops->hdcp_txmtr_query_stream_type = - hdcp2p2_txmtr_query_stream_type; + txmtr_ops->feature_supported = hdcp_lib_client_feature_supported; + txmtr_ops->wakeup = hdcp_lib_wakeup; + txmtr_ops->hdcp_query_stream_type = hdcp_lib_query_stream_type; handle = kzalloc(sizeof(*handle), GFP_KERNEL); if (!handle) { @@ -1170,16 +1221,17 @@ int hdcp_library_register(void **pphdcpcontext, handle->client_ctx = client_ctx; handle->client_ops = client_ops; + atomic_set(&handle->hdcp_off_pending, 0); + mutex_init(&handle->hdcp_lock); - handle->hdcp_workqueue = create_singlethread_workqueue("hdcp-module"); - if (handle->hdcp_workqueue == NULL) { - pr_err("hdcp wq creation failed\n"); - rc = -EFAULT; - goto error; - } + init_kthread_worker(&handle->worker); - INIT_WORK(&handle->auth_work, hdcp2p2_authenticate_work); + 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); handle->listener_buf = kzalloc(MAX_TX_MESSAGE_SIZE, GFP_KERNEL); if (!(handle->listener_buf)) { @@ -1187,18 +1239,20 @@ int hdcp_library_register(void **pphdcpcontext, goto error; } - *((struct hdcp2p2_handle **)pphdcpcontext) = handle; + *((struct hdcp_lib_handle **)pphdcpcontext) = handle; - pr_debug("hdcp lib successfully registered\n"); + handle->thread = kthread_run(kthread_worker_fn, + &handle->worker, "hdcp_tz_lib"); + + if (IS_ERR(handle->thread)) { + pr_err("unable to start lib thread\n"); + rc = PTR_ERR(handle->thread); + handle->thread = NULL; + goto error; + } return 0; error: - /* deallocate resources */ - if (handle->hdcp_workqueue != NULL) { - destroy_workqueue(handle->hdcp_workqueue); - handle->hdcp_workqueue = NULL; - } - kzfree(handle->listener_buf); handle->listener_buf = NULL; kzfree(handle); @@ -1209,15 +1263,14 @@ unlock: void hdcp_library_deregister(void *phdcpcontext) { - struct hdcp2p2_handle *handle = phdcpcontext; + struct hdcp_lib_handle *handle = phdcpcontext; if (!handle) return; - kzfree(handle->qseecom_handle); + kthread_stop(handle->thread); - if (handle->hdcp_workqueue) - destroy_workqueue(handle->hdcp_workqueue); + kzfree(handle->qseecom_handle); mutex_destroy(&handle->hdcp_lock); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c index 060adcf5d319..8d4ad007efba 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c @@ -10,18 +10,20 @@ * GNU General Public License for more details. */ +#define pr_fmt(fmt) "%s: " fmt, __func__ + #include #include #include #include #include +#include #include #include "mdss_hdmi_hdcp.h" #include "video/msm_hdmi_hdcp_mgr.h" #include "mdss_hdmi_util.h" - /* * Defined addresses and offsets of standard HDCP 2.2 sink registers * for DDC, as defined in HDCP 2.2 spec section 2.14 table 2.7 @@ -38,237 +40,197 @@ */ #define MIN_HDMI_TX_MAJOR_VERSION 4 -struct hdcp2p2_message_map { - int msg_id; - const char *msg_name; -}; - -enum hdcp2p2_sink_status { +enum hdmi_hdcp2p2_sink_status { SINK_DISCONNECTED, SINK_CONNECTED }; -enum hdcp2p2_sink_originated_message { - START_AUTHENTICATION = 0, - AKE_SEND_CERT_MESSAGE = 3, - AKE_SEND_H_PRIME_MESSAGE = 7, - AKE_SEND_PAIRING_INFO_MESSAGE = 8, - LC_SEND_L_PRIME_MESSAGE = 10, -}; - -enum hdcp2p2_tx_originated_message { - AKE_INIT_MESSAGE = 2, - AKE_NO_STORED_KM_MESSAGE = 4, - AKE_STORED_KM_MESSAGE = 5, - LC_INIT_MESSAGE = 9, - SKE_SEND_EKS_MESSAGE = 11, -}; - -/* Stores one single message from sink */ -struct hdcp2p2_message { - u8 *message_bytes; /* Message buffer */ - size_t msg_size; /* Byte size of the message buffer */ -}; - -struct hdcp2p2_ctrl { - enum hdmi_hdcp_state auth_state; /* Current auth message st */ - enum hdcp2p2_sink_status sink_status; /* Is sink connected */ +struct hdmi_hdcp2p2_ctrl { + atomic_t auth_state; + enum hdmi_hdcp2p2_sink_status sink_status; /* Is sink connected */ struct hdmi_hdcp_init_data init_data; /* Feature data from HDMI drv */ - enum hdcp2p2_sink_originated_message next_sink_message; - enum hdcp2p2_tx_originated_message next_tx_message; - bool tx_has_master_key; /* true when TX has a stored Km for sink */ - struct mutex mutex; /* mutex to protect access to hdcp2p2_ctrl */ + struct mutex mutex; /* mutex to protect access to ctrl */ struct hdmi_hdcp_ops *ops; - void *hdcp_lib_handle; /* Handle to HDCP 2.2 Trustzone library */ + void *lib_ctx; /* Handle to HDCP 2.2 Trustzone library */ struct hdcp_txmtr_ops *txmtr_ops; /* Ops for driver to call into TZ */ struct hdcp_client_ops *client_ops; /* Ops for driver to export to TZ */ struct completion rxstatus_completion; /* Rx status interrupt */ + + enum hdmi_hdcp_cmd wakeup_cmd; + char *send_msg_buf; + uint32_t send_msg_len; + uint32_t timeout; + + struct task_struct *thread; + struct kthread_worker worker; + struct kthread_work status; + struct kthread_work auth; + struct kthread_work send_msg; + struct kthread_work recv_msg; }; -static int hdcp2p2_authenticate(void *input); - -static const char *hdcp2p2_message_name(int msg_id) +static void hdmi_hdcp2p2_reset(struct hdmi_hdcp2p2_ctrl *ctrl) { - /* - * Message ID map. The first number indicates the message number - * assigned to the message by the HDCP 2.2 spec. This is also the first - * byte of every HDCP 2.2 authentication protocol message. - */ - static struct hdcp2p2_message_map hdcp2p2_msg_map[] = { - {2, "AKE_INIT"}, - {3, "AKE_SEND_CERT"}, - {4, "AKE_NO_STORED_KM"}, - {5, "AKE_STORED_KM"}, - {7, "AKE_SEND_H_PRIME"}, - {8, "AKE_SEND_PAIRING_INFO"}, - {9, "LC_INIT"}, - {10, "LC_SEND_L_PRIME"}, - {11, "SKE_SEND_EKS"}, - {12, "REPEATER_AUTH_SEND_RECEIVERID_LIST"}, - {15, "REPEATER_AUTH_SEND_ACK"}, - {16, "REPEATER_AUTH_STREAM_MANAGE"}, - {17, "REPEATER_AUTH_STREAM_READY"}, - }; - int i; - - for (i = 0; i < ARRAY_SIZE(hdcp2p2_msg_map); i++) { - if (msg_id == hdcp2p2_msg_map[i].msg_id) - return hdcp2p2_msg_map[i].msg_name; + if (!ctrl) { + pr_err("invalid input\n"); + return; } - return "UNKNOWN"; + + ctrl->sink_status = SINK_DISCONNECTED; + atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE); } -static ssize_t hdcp2p2_sysfs_rda_sink_status(struct device *dev, - struct device_attribute *attr, char *buf) +static void hdmi_hdcp2p2_off(void *input) { - struct hdcp2p2_ctrl *hdcp2p2_ctrl = - hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2); - ssize_t ret; + struct hdmi_hdcp2p2_ctrl *ctrl = (struct hdmi_hdcp2p2_ctrl *)input; - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (!ctrl) { + pr_err("invalid input\n"); + return; + } + + hdmi_hdcp2p2_reset(ctrl); + + flush_kthread_worker(&ctrl->worker); + + mutex_lock(&ctrl->mutex); + queue_kthread_work(&ctrl->worker, &ctrl->auth); + mutex_unlock(&ctrl->mutex); +} + +static int hdmi_hdcp2p2_authenticate(void *input) +{ + struct hdmi_hdcp2p2_ctrl *ctrl = input; + int rc = 0; + + flush_kthread_worker(&ctrl->worker); + + mutex_lock(&ctrl->mutex); + ctrl->sink_status = SINK_CONNECTED; + atomic_set(&ctrl->auth_state, HDCP_STATE_AUTHENTICATING); + + queue_kthread_work(&ctrl->worker, &ctrl->auth); + + mutex_unlock(&ctrl->mutex); + return rc; +} + +static int hdmi_hdcp2p2_reauthenticate(void *input) +{ + int rc = 0; + struct hdmi_hdcp2p2_ctrl *ctrl = (struct hdmi_hdcp2p2_ctrl *)input; + + if (!ctrl) { + pr_err("invalid input\n"); return -EINVAL; } - mutex_lock(&hdcp2p2_ctrl->mutex); - if (hdcp2p2_ctrl->sink_status == SINK_CONNECTED) + hdmi_hdcp2p2_reset((struct hdmi_hdcp2p2_ctrl *)input); + + rc = hdmi_hdcp2p2_authenticate(input); + + return rc; +} + +static ssize_t hdmi_hdcp2p2_sysfs_rda_sink_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hdmi_hdcp2p2_ctrl *ctrl = + hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2); + ssize_t ret; + + if (!ctrl) { + pr_err("invalid input\n"); + return -EINVAL; + } + + mutex_lock(&ctrl->mutex); + if (ctrl->sink_status == SINK_CONNECTED) ret = scnprintf(buf, PAGE_SIZE, "Connected\n"); else ret = scnprintf(buf, PAGE_SIZE, "Disconnected\n"); - mutex_unlock(&hdcp2p2_ctrl->mutex); + mutex_unlock(&ctrl->mutex); return ret; } -static ssize_t hdcp2p2_sysfs_rda_trigger(struct device *dev, +static ssize_t hdmi_hdcp2p2_sysfs_rda_trigger(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; - struct hdcp2p2_ctrl *hdcp2p2_ctrl = + struct hdmi_hdcp2p2_ctrl *ctrl = hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2); - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (!ctrl) { + pr_err("invalid input\n"); return -EINVAL; } - mutex_lock(&hdcp2p2_ctrl->mutex); - if (hdcp2p2_ctrl->sink_status == SINK_CONNECTED) + mutex_lock(&ctrl->mutex); + if (ctrl->sink_status == SINK_CONNECTED) ret = scnprintf(buf, PAGE_SIZE, "Triggered\n"); else ret = scnprintf(buf, PAGE_SIZE, "Not triggered\n"); - mutex_unlock(&hdcp2p2_ctrl->mutex); + mutex_unlock(&ctrl->mutex); return ret; } -static ssize_t hdcp2p2_sysfs_wta_trigger(struct device *dev, +static ssize_t hdmi_hdcp2p2_sysfs_wta_trigger(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct hdcp2p2_ctrl *hdcp2p2_ctrl = + struct hdmi_hdcp2p2_ctrl *ctrl = hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2); - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (!ctrl) { + pr_err("invalid input\n"); return -EINVAL; } - mutex_lock(&hdcp2p2_ctrl->mutex); - hdcp2p2_ctrl->sink_status = SINK_CONNECTED; - mutex_unlock(&hdcp2p2_ctrl->mutex); + mutex_lock(&ctrl->mutex); + ctrl->sink_status = SINK_CONNECTED; + mutex_unlock(&ctrl->mutex); - DEV_DBG("%s: HDCP 2.2 authentication triggered\n", __func__); - hdcp2p2_authenticate(hdcp2p2_ctrl); + pr_debug("HDCP 2.2 authentication triggered\n"); + hdmi_hdcp2p2_authenticate(ctrl); return count; } -static ssize_t hdcp2p2_sysfs_wta_min_level_change(struct device *dev, +static ssize_t hdmi_hdcp2p2_sysfs_wta_min_level_change(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct hdcp2p2_ctrl *hdcp2p2_ctrl = + struct hdmi_hdcp2p2_ctrl *ctrl = hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2); int res; - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (!ctrl) { + pr_err("invalid input\n"); return -EINVAL; } - DEV_DBG("%s: notification of minimum level change received\n", - __func__); - res = hdcp2p2_ctrl->txmtr_ops-> - hdcp_txmtr_query_stream_type(hdcp2p2_ctrl->hdcp_lib_handle); + pr_debug("notification of minimum level change received\n"); + res = ctrl->txmtr_ops-> + hdcp_query_stream_type(ctrl->lib_ctx); return count; } -static void hdcp2p2_auth_failed(struct hdcp2p2_ctrl *hdcp2p2_ctrl) +static void hdmi_hdcp2p2_auth_failed(struct hdmi_hdcp2p2_ctrl *ctrl) { - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (!ctrl) { + pr_err("invalid input\n"); return; } - hdcp2p2_ctrl->auth_state = HDCP_STATE_AUTH_FAIL; - hdcp2p2_ctrl->next_sink_message = START_AUTHENTICATION; - hdcp2p2_ctrl->next_tx_message = AKE_INIT_MESSAGE; - hdcp2p2_ctrl->tx_has_master_key = false; + atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL); /* notify hdmi tx about HDCP failure */ - hdcp2p2_ctrl->init_data.notify_status( - hdcp2p2_ctrl->init_data.cb_data, + ctrl->init_data.notify_status( + ctrl->init_data.cb_data, HDCP_STATE_AUTH_FAIL); } -static void hdcp2p2_advance_next_tx_message( - struct hdcp2p2_ctrl *hdcp2p2_ctrl) -{ - switch (hdcp2p2_ctrl->next_tx_message) { - case AKE_INIT_MESSAGE: - hdcp2p2_ctrl->next_tx_message = - AKE_NO_STORED_KM_MESSAGE; - break; - case AKE_NO_STORED_KM_MESSAGE: - case AKE_STORED_KM_MESSAGE: - hdcp2p2_ctrl->next_tx_message = LC_INIT_MESSAGE; - break; - case LC_INIT_MESSAGE: - hdcp2p2_ctrl->next_tx_message = SKE_SEND_EKS_MESSAGE; - break; - default: - break; - } -} - -static void hdcp2p2_advance_next_sink_message( - struct hdcp2p2_ctrl *hdcp2p2_ctrl) -{ - switch (hdcp2p2_ctrl->next_sink_message) { - case START_AUTHENTICATION: - hdcp2p2_ctrl->next_sink_message = AKE_SEND_CERT_MESSAGE; - break; - case AKE_SEND_CERT_MESSAGE: - hdcp2p2_ctrl->next_sink_message = - AKE_SEND_H_PRIME_MESSAGE; - break; - case AKE_SEND_H_PRIME_MESSAGE: - if (hdcp2p2_ctrl->tx_has_master_key) - hdcp2p2_ctrl->next_sink_message = - LC_SEND_L_PRIME_MESSAGE; - else - hdcp2p2_ctrl->next_sink_message = - AKE_SEND_PAIRING_INFO_MESSAGE; - break; - case AKE_SEND_PAIRING_INFO_MESSAGE: - hdcp2p2_ctrl->next_sink_message = - LC_SEND_L_PRIME_MESSAGE; - break; - default: - break; - } -} - -static int hdcp2p2_ddc_read_message(struct hdcp2p2_ctrl *hdcp2p2_ctrl, u8 *buf, - int size, u32 timeout) +static int hdmi_hdcp2p2_ddc_read_message(struct hdmi_hdcp2p2_ctrl *ctrl, + u8 *buf, int size, u32 timeout) { struct hdmi_tx_ddc_data ddc_data; int rc; @@ -283,14 +245,14 @@ static int hdcp2p2_ddc_read_message(struct hdcp2p2_ctrl *hdcp2p2_ctrl, u8 *buf, ddc_data.hard_timeout = timeout; ddc_data.what = "HDCP2ReadMessage"; - rc = hdmi_ddc_read(hdcp2p2_ctrl->init_data.ddc_ctrl, &ddc_data); + rc = hdmi_ddc_read(ctrl->init_data.ddc_ctrl, &ddc_data); if (rc) - DEV_ERR("%s: Cannot read HDCP message register", __func__); + pr_err("Cannot read HDCP message register\n"); return rc; } -static int hdcp2p2_ddc_write_message(struct hdcp2p2_ctrl *hdcp2p2_ctrl, u8 *buf, - size_t size) +static int hdmi_hdcp2p2_ddc_write_message(struct hdmi_hdcp2p2_ctrl *ctrl, + u8 *buf, size_t size) { struct hdmi_tx_ddc_data ddc_data; int rc; @@ -303,13 +265,13 @@ static int hdcp2p2_ddc_write_message(struct hdcp2p2_ctrl *hdcp2p2_ctrl, u8 *buf, ddc_data.retry = 1; ddc_data.what = "HDCP2WriteMessage"; - rc = hdmi_ddc_write(hdcp2p2_ctrl->init_data.ddc_ctrl, &ddc_data); + rc = hdmi_ddc_write(ctrl->init_data.ddc_ctrl, &ddc_data); if (rc) - DEV_ERR("%s: Cannot write HDCP message register", __func__); + pr_err("Cannot write HDCP message register"); return rc; } -static void hdcp2p2_ddc_abort(struct hdcp2p2_ctrl *hdcp2p2_ctrl) +static void hdmi_hdcp2p2_ddc_abort(struct hdmi_hdcp2p2_ctrl *ctrl) { /* Abort any ongoing DDC transactions */ struct hdmi_tx_ddc_data ddc_data; @@ -317,11 +279,11 @@ static void hdcp2p2_ddc_abort(struct hdcp2p2_ctrl *hdcp2p2_ctrl) memset(&ddc_data, 0, sizeof(ddc_data)); ddc_data.retry = 1; ddc_data.what = "HDCPAbortTransaction"; - hdmi_ddc_abort_transaction(hdcp2p2_ctrl->init_data.ddc_ctrl, + hdmi_ddc_abort_transaction(ctrl->init_data.ddc_ctrl, &ddc_data); } -static int hdcp2p2_read_version(struct hdcp2p2_ctrl *hdcp2p2_ctrl, +static int hdmi_hdcp2p2_read_version(struct hdmi_hdcp2p2_ctrl *ctrl, u8 *hdcp2version) { struct hdmi_tx_ddc_data ddc_data; @@ -336,95 +298,201 @@ static int hdcp2p2_read_version(struct hdcp2p2_ctrl *hdcp2p2_ctrl, ddc_data.retry = 1; ddc_data.what = "HDCP2Version"; - rc = hdmi_ddc_read(hdcp2p2_ctrl->init_data.ddc_ctrl, &ddc_data); + rc = hdmi_ddc_read(ctrl->init_data.ddc_ctrl, &ddc_data); if (rc) { - DEV_ERR("%s: Cannot read HDCP2Version register", __func__); + pr_err("Cannot read HDCP2Version register"); return rc; } - DEV_DBG("%s: Read HDCP2Version as %u\n", __func__, *hdcp2version); + pr_debug("Read HDCP2Version as %u\n", *hdcp2version); return rc; } -static int hdcp2p2_read_message_from_sink(struct hdcp2p2_ctrl *hdcp2p2_ctrl, - int msg_size, u32 timeout) +static ssize_t hdmi_hdcp2p2_sysfs_rda_hdcp2_version(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 hdcp2version; + 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; + } + ret = hdmi_hdcp2p2_read_version(ctrl, &hdcp2version); + if (ret < 0) + return ret; + return snprintf(buf, PAGE_SIZE, "%u\n", hdcp2version); +} + + +static DEVICE_ATTR(trigger, S_IRUGO | S_IWUSR, hdmi_hdcp2p2_sysfs_rda_trigger, + hdmi_hdcp2p2_sysfs_wta_trigger); +static DEVICE_ATTR(min_level_change, S_IWUSR, NULL, + hdmi_hdcp2p2_sysfs_wta_min_level_change); +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 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, + NULL, +}; + +static struct attribute_group hdmi_hdcp2p2_fs_attr_group = { + .name = "hdcp2p2", + .attrs = hdmi_hdcp2p2_fs_attrs, +}; + +static int hdmi_hdcp2p2_isr(void *input) +{ + struct hdmi_hdcp2p2_ctrl *ctrl = input; + u32 reg_val; + + if (!ctrl) { + pr_err("invalid input\n"); + return -EINVAL; + } + + pr_debug("INT_CTRL0 is 0x%x\n", + DSS_REG_R(ctrl->init_data.core_io, HDMI_DDC_INT_CTRL0)); + reg_val = DSS_REG_R(ctrl->init_data.core_io, + HDMI_HDCP_INT_CTRL2); + if (reg_val & BIT(0)) { + pr_debug("HDCP 2.2 Encryption is enabled\n"); + reg_val |= BIT(1); + DSS_REG_W(ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2, + reg_val); + } + + reg_val = DSS_REG_R(ctrl->init_data.core_io, + HDMI_DDC_INT_CTRL0); + if (reg_val & HDCP2P2_RXSTATUS_MESSAGE_SIZE_MASK) { + DSS_REG_W(ctrl->init_data.core_io, HDMI_DDC_INT_CTRL0, + reg_val & ~(BIT(31))); + if (!completion_done(&ctrl->rxstatus_completion)) + complete_all(&ctrl->rxstatus_completion); + } else if (reg_val & BIT(8)) { + DSS_REG_W(ctrl->init_data.core_io, HDMI_DDC_INT_CTRL0, + reg_val & ~(BIT(9) | BIT(10))); + if (!completion_done(&ctrl->rxstatus_completion)) + complete_all(&ctrl->rxstatus_completion); + } + + return 0; +} + +static bool hdmi_hdcp2p2_feature_supported(void *input) +{ + struct hdmi_hdcp2p2_ctrl *ctrl = input; + struct hdcp_txmtr_ops *lib = NULL; + bool supported = false; + + if (!ctrl) { + pr_err("invalid input\n"); + goto end; + } + + lib = ctrl->txmtr_ops; + if (!lib) { + pr_err("invalid lib ops data\n"); + goto end; + } + + if (lib->feature_supported) + supported = lib->feature_supported( + ctrl->lib_ctx); +end: + return supported; +} + +static void hdmi_hdcp2p2_send_msg_work(struct kthread_work *work) { - bool read_next_message = false; int rc = 0; - struct hdcp2p2_message *message = kmalloc(sizeof(*message), - GFP_KERNEL); + struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work, + struct hdmi_hdcp2p2_ctrl, send_msg); + struct hdcp_txmtr_ops *lib = NULL; + enum hdcp_lib_wakeup_cmd cmd; - if (!message) { - DEV_ERR("%s: Could not allocate memory\n", __func__); + 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; } - message->message_bytes = kmalloc(msg_size, GFP_KERNEL); - if (!message->message_bytes) - goto exit; + lib = ctrl->txmtr_ops; - DEV_DBG("%s: reading message from sink\n", __func__); - rc = hdcp2p2_ddc_read_message(hdcp2p2_ctrl, message->message_bytes, - msg_size, timeout); + if (!lib) { + pr_err("invalid lib data\n"); + goto exit; + } + + /* Forward the message to the sink */ + rc = hdmi_hdcp2p2_ddc_write_message(ctrl, + ctrl->send_msg_buf, + (size_t)ctrl->send_msg_len); if (rc) { - DEV_ERR("%s: ERROR reading message from sink\n", __func__); - goto exit; + pr_err("Error sending msg to sink %d\n", rc); + cmd = HDCP_WKUP_CMD_MSG_SEND_FAILED; + } else { + cmd = HDCP_WKUP_CMD_MSG_SEND_SUCCESS; } - message->msg_size = msg_size; - DEV_DBG("%s: msg recvd: %s, size: %d\n", __func__, - hdcp2p2_message_name((int)message->message_bytes[0]), - msg_size); + if (lib->wakeup) + lib->wakeup(ctrl->lib_ctx, cmd, 0, 0); +exit: + kzfree(ctrl->send_msg_buf); + mutex_unlock(&ctrl->mutex); +} - if (hdcp2p2_ctrl->txmtr_ops->process_message) { - rc = hdcp2p2_ctrl->txmtr_ops->process_message( - hdcp2p2_ctrl->hdcp_lib_handle, message->message_bytes, - (u32)message->msg_size); - if (rc) - goto exit; - } else { - DEV_ERR("%s: process message not defined\n", __func__); +static void hdmi_hdcp2p2_recv_msg_work(struct kthread_work *work) +{ + int rc = 0, msg_size = 0, retries = 5; + u64 mult; + char *recvd_msg_buf = NULL; + struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work, + struct hdmi_hdcp2p2_ctrl, recv_msg); + struct hdcp_txmtr_ops *lib = NULL; + enum hdcp_lib_wakeup_cmd cmd; + struct hdmi_tx_hdcp2p2_ddc_data hdcp2p2_ddc_data; + struct hdmi_tx_ddc_ctrl *ddc_ctrl; + struct msm_hdmi_mode_timing_info *timing; + + if (!ctrl) { + pr_err("invalid input\n"); + return; + } + + mutex_lock(&ctrl->mutex); + + lib = ctrl->txmtr_ops; + + if (!lib) { + pr_err("invalid lib data\n"); rc = -EINVAL; goto exit; } - if (hdcp2p2_ctrl->next_sink_message == AKE_SEND_H_PRIME_MESSAGE) { - if (!hdcp2p2_ctrl->tx_has_master_key) { - DEV_DBG("%s: Listening for second message\n", __func__); - hdcp2p2_advance_next_sink_message(hdcp2p2_ctrl); - read_next_message = true; - } + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { + pr_err("hdcp is off\n"); + goto exit; } - if (read_next_message) - rc = 1; -exit: - kfree(message->message_bytes); - kfree(message); - - return rc; -} - -static int hdcp2p2_sink_message_read(struct hdcp2p2_ctrl *hdcp2p2_ctrl, - u32 timeout) -{ - struct hdmi_tx_hdcp2p2_ddc_data hdcp2p2_ddc_data; - struct hdmi_tx_ddc_ctrl *ddc_ctrl; - int rc = 0; - int msg_size; - bool read_next_message = false; - u64 mult; - struct msm_hdmi_mode_timing_info *timing; - - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid hdcp2p2 data\n", __func__); - return -EINVAL; - } - - ddc_ctrl = hdcp2p2_ctrl->init_data.ddc_ctrl; + ddc_ctrl = ctrl->init_data.ddc_ctrl; do { - timing = hdcp2p2_ctrl->init_data.timing; + timing = ctrl->init_data.timing; mult = hdmi_tx_get_v_total(timing) / 20; memset(&hdcp2p2_ddc_data, 0, sizeof(hdcp2p2_ddc_data)); @@ -437,467 +505,318 @@ static int hdcp2p2_sink_message_read(struct hdcp2p2_ctrl *hdcp2p2_ctrl, hdcp2p2_ddc_data.poll_sink = false; hdmi_ddc_config(ddc_ctrl); - DEV_DBG("%s: Reading rxstatus, timer delay %u\n", - __func__, (u32)mult); + pr_debug("Reading rxstatus, timer delay %u\n", (u32)mult); rc = hdmi_hdcp2p2_ddc_read_rxstatus( ddc_ctrl, &hdcp2p2_ddc_data, - &hdcp2p2_ctrl->rxstatus_completion); + &ctrl->rxstatus_completion); if (rc) { - DEV_ERR("%s: Could not read rxstatus from sink\n", - __func__); - goto exit; - } else { - DEV_DBG("%s: SUCCESS reading rxstatus\n", __func__); + pr_err("Could not read rxstatus from sink\n"); + continue; } if (!msg_size) { - DEV_ERR("%s: recvd invalid message size\n", __func__); + pr_err("recvd invalid message size\n"); rc = -EINVAL; - goto exit; + continue; } + } while (rc && (retries-- > 0)); - rc = hdcp2p2_read_message_from_sink( - hdcp2p2_ctrl, msg_size, timeout); - if (rc > 0) { - read_next_message = true; - rc = 0; - } - - hdcp2p2_ddc_abort(hdcp2p2_ctrl); - hdmi_hdcp2p2_ddc_reset(ddc_ctrl); - hdmi_hdcp2p2_ddc_disable(ddc_ctrl); - } while (read_next_message); -exit: - return rc; -} - -static ssize_t hdcp2p2_sysfs_rda_hdcp2_version(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 hdcp2version; - ssize_t ret; - struct hdcp2p2_ctrl *hdcp2p2_ctrl = - hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP2P2); - - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); - return -EINVAL; - } - ret = hdcp2p2_read_version(hdcp2p2_ctrl, &hdcp2version); - if (ret < 0) - return ret; - return snprintf(buf, PAGE_SIZE, "%u\n", hdcp2version); -} - - -static DEVICE_ATTR(trigger, S_IRUGO | S_IWUSR, hdcp2p2_sysfs_rda_trigger, - hdcp2p2_sysfs_wta_trigger); -static DEVICE_ATTR(min_level_change, S_IWUSR, NULL, - hdcp2p2_sysfs_wta_min_level_change); -static DEVICE_ATTR(sink_status, S_IRUGO, hdcp2p2_sysfs_rda_sink_status, - NULL); -static DEVICE_ATTR(hdcp2_version, S_IRUGO, - hdcp2p2_sysfs_rda_hdcp2_version, - NULL); - - -static struct attribute *hdcp2p2_fs_attrs[] = { - &dev_attr_trigger.attr, - &dev_attr_min_level_change.attr, - &dev_attr_sink_status.attr, - &dev_attr_hdcp2_version.attr, - NULL, -}; - -static struct attribute_group hdcp2p2_fs_attr_group = { - .name = "hdcp2p2", - .attrs = hdcp2p2_fs_attrs, -}; - - -static int hdcp2p2_isr(void *input) -{ - struct hdcp2p2_ctrl *hdcp2p2_ctrl = input; - u32 reg_val; - - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); - return -EINVAL; - } - - DEV_DBG("%s\n INT_CTRL0 is 0x%x\n", __func__, - DSS_REG_R(hdcp2p2_ctrl->init_data.core_io, HDMI_DDC_INT_CTRL0)); - reg_val = DSS_REG_R(hdcp2p2_ctrl->init_data.core_io, - HDMI_HDCP_INT_CTRL2); - if (reg_val & BIT(0)) { - DEV_DBG("%s: HDCP 2.2 Encryption is enabled\n", __func__); - reg_val |= BIT(1); - DSS_REG_W(hdcp2p2_ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2, - reg_val); - } - - reg_val = DSS_REG_R(hdcp2p2_ctrl->init_data.core_io, - HDMI_DDC_INT_CTRL0); - if (reg_val & HDCP2P2_RXSTATUS_MESSAGE_SIZE_MASK) { - DSS_REG_W(hdcp2p2_ctrl->init_data.core_io, HDMI_DDC_INT_CTRL0, - reg_val & ~(BIT(31))); - if (!completion_done(&hdcp2p2_ctrl->rxstatus_completion)) - complete_all(&hdcp2p2_ctrl->rxstatus_completion); - } else if (reg_val & BIT(8)) { - DSS_REG_W(hdcp2p2_ctrl->init_data.core_io, HDMI_DDC_INT_CTRL0, - reg_val & ~(BIT(9) | BIT(10))); - if (!completion_done(&hdcp2p2_ctrl->rxstatus_completion)) - complete_all(&hdcp2p2_ctrl->rxstatus_completion); - } - - return 0; -} - -static bool hdcp2p2_feature_supported(void *input) -{ - struct hdcp2p2_ctrl *hdcp2p2_ctrl = input; - struct hdcp_txmtr_ops *lib = NULL; - bool supported = false; - - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); - goto end; - } - - lib = hdcp2p2_ctrl->txmtr_ops; - if (!lib) { - DEV_ERR("%s: invalid lib ops data\n", __func__); - goto end; - } - - if (lib->feature_supported) - supported = lib->feature_supported( - hdcp2p2_ctrl->hdcp_lib_handle); -end: - return supported; -} - -static void hdcp2p2_reset(struct hdcp2p2_ctrl *hdcp2p2_ctrl) -{ - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); - return; - } - - hdcp2p2_ctrl->sink_status = SINK_DISCONNECTED; - hdcp2p2_ctrl->next_sink_message = START_AUTHENTICATION; - hdcp2p2_ctrl->next_tx_message = AKE_INIT_MESSAGE; - hdcp2p2_ctrl->auth_state = HDCP_STATE_INACTIVE; - hdcp2p2_ctrl->tx_has_master_key = false; -} - -static int hdcp2p2_authenticate(void *input) -{ - struct hdcp2p2_ctrl *hdcp2p2_ctrl = input; - struct hdcp_txmtr_ops *lib = NULL; - int rc = 0; - - mutex_lock(&hdcp2p2_ctrl->mutex); - hdcp2p2_ctrl->sink_status = SINK_CONNECTED; - hdcp2p2_ctrl->auth_state = HDCP_STATE_AUTHENTICATING; - - lib = hdcp2p2_ctrl->txmtr_ops; - if (!lib) { - DEV_ERR("%s: invalid lib ops data\n", __func__); - goto done; - } - - if (!lib->start) { - DEV_ERR("%s: lib start not defined\n", __func__); - goto done; - } - - rc = lib->start(hdcp2p2_ctrl->hdcp_lib_handle); if (rc) { - DEV_ERR("%s: lib start failed\n", __func__); - hdcp2p2_ctrl->auth_state = HDCP_STATE_AUTH_FAIL; - - goto done; - } -done: - mutex_unlock(&hdcp2p2_ctrl->mutex); - return rc; -} - -static int hdcp2p2_reauthenticate(void *input) -{ - struct hdcp2p2_ctrl *hdcp2p2_ctrl = (struct hdcp2p2_ctrl *)input; - - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); - return -EINVAL; + pr_err("error reading valid rxstatus data\n"); + goto exit; } - hdcp2p2_reset((struct hdcp2p2_ctrl *)input); + recvd_msg_buf = kzalloc(msg_size, GFP_KERNEL); + if (!recvd_msg_buf) + goto exit; - return hdcp2p2_authenticate(input); + rc = hdmi_hdcp2p2_ddc_read_message(ctrl, recvd_msg_buf, + msg_size, ctrl->timeout); + if (rc) + pr_err("ERROR reading message from sink\n"); + + hdmi_hdcp2p2_ddc_abort(ctrl); + hdmi_hdcp2p2_ddc_reset(ddc_ctrl); + hdmi_hdcp2p2_ddc_disable(ddc_ctrl); +exit: + if (rc == -ETIMEDOUT) + cmd = HDCP_WKUP_CMD_MSG_RECV_TIMEOUT; + else if (rc) + cmd = HDCP_WKUP_CMD_MSG_RECV_FAILED; + else + cmd = HDCP_WKUP_CMD_MSG_RECV_SUCCESS; + + if (lib->wakeup) + lib->wakeup(ctrl->lib_ctx, cmd, + recvd_msg_buf, msg_size); + + kfree(recvd_msg_buf); + mutex_unlock(&ctrl->mutex); } -static void hdcp2p2_off(void *input) +static void hdmi_hdcp2p2_auth_status_work(struct kthread_work *work) { - struct hdcp2p2_ctrl *hdcp2p2_ctrl = (struct hdcp2p2_ctrl *)input; - struct hdcp_txmtr_ops *lib = NULL; + struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work, + struct hdmi_hdcp2p2_ctrl, status); - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (!ctrl) { + pr_err("invalid input\n"); return; } - mutex_lock(&hdcp2p2_ctrl->mutex); - hdcp2p2_reset(hdcp2p2_ctrl); + mutex_lock(&ctrl->mutex); - lib = hdcp2p2_ctrl->txmtr_ops; - - if (lib && lib->stop) - lib->stop(hdcp2p2_ctrl->hdcp_lib_handle); - - mutex_unlock(&hdcp2p2_ctrl->mutex); -} - -static int hdcp2p2_send_message_to_sink(void *client_ctx, - char *message, u32 msg_size) -{ - struct hdcp2p2_ctrl *hdcp2p2_ctrl = client_ctx; - int rc = 0; - - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { + pr_err("hdcp is off\n"); goto exit; } - if (hdcp2p2_ctrl->auth_state == HDCP_STATE_INACTIVE) { - DEV_DBG("%s: hdcp is off\n", __func__); - goto exit; - } - - DEV_DBG("%s: Sending %s message from tx to sink\n", __func__, - hdcp2p2_message_name((int)message[0])); - - switch (message[0]) { - case AKE_STORED_KM_MESSAGE: - hdcp2p2_ctrl->tx_has_master_key = true; - hdcp2p2_advance_next_sink_message(hdcp2p2_ctrl); - hdcp2p2_advance_next_tx_message(hdcp2p2_ctrl); - break; - case SKE_SEND_EKS_MESSAGE: - /* Send EKS message comes from TX when we are authenticated */ - hdcp2p2_ctrl->auth_state = HDCP_STATE_AUTHENTICATED; - hdcp2p2_ctrl->next_sink_message = START_AUTHENTICATION; - hdcp2p2_ctrl->next_tx_message = AKE_INIT_MESSAGE; - hdcp2p2_ctrl->tx_has_master_key = false; - hdcp2p2_ctrl->init_data.notify_status( - hdcp2p2_ctrl->init_data.cb_data, - HDCP_STATE_AUTHENTICATED); - break; - case AKE_NO_STORED_KM_MESSAGE: - /* - * We need a delay to allow sink time to be ready to receive the - * message - */ - msleep(100); - /* fall through */ - default: - hdcp2p2_advance_next_sink_message(hdcp2p2_ctrl); - hdcp2p2_advance_next_tx_message(hdcp2p2_ctrl); - break; - } - - /* Forward the message to the sink */ - rc = hdcp2p2_ddc_write_message(hdcp2p2_ctrl, message, (size_t)msg_size); - if (rc) - DEV_ERR("%s: Error in writing to sink %d\n", __func__, rc); - else - DEV_DBG("%s: SUCCESS\n", __func__); -exit: - mutex_unlock(&hdcp2p2_ctrl->mutex); - - return rc; -} - -static int hdcp2p2_recv_message_from_sink(void *client_ctx, - char *message, u32 msg_size, u32 timeout) -{ - struct hdcp2p2_ctrl *hdcp2p2_ctrl = client_ctx; - int rc = 0; - - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid ctrl\n", __func__); - return -EINVAL; - } - - if (!message) { - DEV_ERR("%s: invalid message\n", __func__); - return -EINVAL; - } - - if (hdcp2p2_ctrl->auth_state == HDCP_STATE_INACTIVE) { - DEV_DBG("%s: hdcp is off\n", __func__); - return 0; - } - - /* Start polling sink for the next expected message in the protocol */ - if (message[0] != SKE_SEND_EKS_MESSAGE) { - rc = hdcp2p2_sink_message_read(hdcp2p2_ctrl, timeout); - } else { + if (ctrl->wakeup_cmd == HDMI_HDCP_STATUS_FAIL) { + hdmi_hdcp2p2_auth_failed(ctrl); + } else if (ctrl->wakeup_cmd == HDMI_HDCP_STATUS_SUCCESS) { /* Enable interrupts */ - u32 regval = DSS_REG_R(hdcp2p2_ctrl->init_data.core_io, + u32 regval = DSS_REG_R(ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2); - DEV_DBG("%s: Now authenticated. Enabling interrupts\n", - __func__); + pr_debug("Now authenticated. Enabling interrupts\n"); regval |= BIT(1); regval |= BIT(2); regval |= BIT(5); - DSS_REG_W(hdcp2p2_ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2, - regval); - } - return rc; + 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); + } +exit: + mutex_unlock(&ctrl->mutex); } -static int hdcp2p2_tz_error(void *client_ctx) +static void hdmi_hdcp2p2_auth_work(struct kthread_work *work) { - struct hdcp2p2_ctrl *hdcp2p2_ctrl = (struct hdcp2p2_ctrl *)client_ctx; + struct hdmi_hdcp2p2_ctrl *ctrl = container_of(work, + struct hdmi_hdcp2p2_ctrl, auth); + enum hdcp_lib_wakeup_cmd cmd; + struct hdcp_txmtr_ops *lib = NULL; - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + 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; + } + + lib = ctrl->txmtr_ops; + if (lib && lib->wakeup) { + if (atomic_read(&ctrl->auth_state) == + HDCP_STATE_AUTHENTICATING) + cmd = HDCP_WKUP_CMD_START; + else + cmd = HDCP_WKUP_CMD_STOP; + + lib->wakeup(ctrl->lib_ctx, cmd, 0, 0); + } +exit: + mutex_unlock(&ctrl->mutex); +} + +static int hdmi_hdcp2p2_wakeup(void *client_ctx, enum hdmi_hdcp_cmd cmd, + char *msg, uint32_t msglen, uint32_t timeout) +{ + struct hdmi_hdcp2p2_ctrl *ctrl = client_ctx; + + if (!ctrl) { + pr_err("invalid input\n"); return -EINVAL; } - hdcp2p2_auth_failed(hdcp2p2_ctrl); + mutex_lock(&ctrl->mutex); + pr_debug("wakeup_cmd: %s\n", hdmi_hdcp_cmd_to_str(cmd)); + + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { + pr_err("hdcp is off\n"); + goto exit; + } + + ctrl->wakeup_cmd = cmd; + ctrl->send_msg_len = msglen; + ctrl->timeout = timeout; + + if (msglen) { + ctrl->send_msg_buf = kzalloc( + ctrl->send_msg_len, GFP_KERNEL); + + if (!ctrl->send_msg_buf) + goto exit; + + memcpy(ctrl->send_msg_buf, msg, msglen); + } + + switch (ctrl->wakeup_cmd) { + case HDMI_HDCP_SEND_MESSAGE: + queue_kthread_work(&ctrl->worker, &ctrl->send_msg); + break; + case HDMI_HDCP_RECV_MESSAGE: + queue_kthread_work(&ctrl->worker, &ctrl->recv_msg); + break; + case HDMI_HDCP_STATUS_SUCCESS: + case HDMI_HDCP_STATUS_FAIL: + queue_kthread_work(&ctrl->worker, &ctrl->status); + break; + default: + pr_err("invalid wakeup command %d\n", + ctrl->wakeup_cmd); + } +exit: + mutex_unlock(&ctrl->mutex); return 0; } void hdmi_hdcp2p2_deinit(void *input) { - struct hdcp2p2_ctrl *hdcp2p2_ctrl = (struct hdcp2p2_ctrl *)input; + struct hdmi_hdcp2p2_ctrl *ctrl = (struct hdmi_hdcp2p2_ctrl *)input; struct hdcp_txmtr_ops *lib = NULL; - if (!hdcp2p2_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (!ctrl) { + pr_err("invalid input\n"); return; } - lib = hdcp2p2_ctrl->txmtr_ops; + lib = ctrl->txmtr_ops; - if (lib && lib->stop) - lib->stop(hdcp2p2_ctrl->hdcp_lib_handle); + if (lib && lib->wakeup) + lib->wakeup(ctrl->lib_ctx, + HDCP_WKUP_CMD_STOP, 0, 0); - sysfs_remove_group(hdcp2p2_ctrl->init_data.sysfs_kobj, - &hdcp2p2_fs_attr_group); + kthread_stop(ctrl->thread); - mutex_destroy(&hdcp2p2_ctrl->mutex); - kfree(hdcp2p2_ctrl); + sysfs_remove_group(ctrl->init_data.sysfs_kobj, + &hdmi_hdcp2p2_fs_attr_group); + + mutex_destroy(&ctrl->mutex); + kfree(ctrl); } void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data) { - struct hdcp2p2_ctrl *hdcp2p2_ctrl; + struct hdmi_hdcp2p2_ctrl *ctrl; int rc; static struct hdmi_hdcp_ops ops = { - .hdmi_hdcp_isr = hdcp2p2_isr, - .hdmi_hdcp_reauthenticate = hdcp2p2_reauthenticate, - .hdmi_hdcp_authenticate = hdcp2p2_authenticate, - .feature_supported = hdcp2p2_feature_supported, - .hdmi_hdcp_off = hdcp2p2_off + .hdmi_hdcp_isr = hdmi_hdcp2p2_isr, + .hdmi_hdcp_reauthenticate = hdmi_hdcp2p2_reauthenticate, + .hdmi_hdcp_authenticate = hdmi_hdcp2p2_authenticate, + .feature_supported = hdmi_hdcp2p2_feature_supported, + .hdmi_hdcp_off = hdmi_hdcp2p2_off }; static struct hdcp_client_ops client_ops = { - .hdcp_send_message = hdcp2p2_send_message_to_sink, - .hdcp_recv_message = hdcp2p2_recv_message_from_sink, - .hdcp_tz_error = hdcp2p2_tz_error, + .wakeup = hdmi_hdcp2p2_wakeup, }; static struct hdcp_txmtr_ops txmtr_ops; - DEV_DBG("%s: HDCP2P2 feature initialization\n", __func__); + pr_debug("HDCP2P2 feature initialization\n"); if (!init_data || !init_data->core_io || !init_data->mutex || !init_data->ddc_ctrl || !init_data->notify_status || !init_data->workq || !init_data->cb_data) { - DEV_ERR("%s: invalid input\n", __func__); + pr_err("invalid input\n"); return ERR_PTR(-EINVAL); } if (init_data->hdmi_tx_ver < MIN_HDMI_TX_MAJOR_VERSION) { - DEV_ERR("%s: HDMI Tx does not support HDCP 2.2\n", __func__); + pr_err("HDMI Tx does not support HDCP 2.2\n"); return ERR_PTR(-ENODEV); } - hdcp2p2_ctrl = kzalloc(sizeof(*hdcp2p2_ctrl), GFP_KERNEL); - if (!hdcp2p2_ctrl) { + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) return ERR_PTR(-ENOMEM); - } - hdcp2p2_ctrl->init_data = *init_data; - hdcp2p2_ctrl->client_ops = &client_ops; - hdcp2p2_ctrl->txmtr_ops = &txmtr_ops; + ctrl->init_data = *init_data; + ctrl->client_ops = &client_ops; + ctrl->txmtr_ops = &txmtr_ops; rc = sysfs_create_group(init_data->sysfs_kobj, - &hdcp2p2_fs_attr_group); + &hdmi_hdcp2p2_fs_attr_group); if (rc) { - DEV_ERR("%s: hdcp2p2 sysfs group creation failed\n", __func__); + pr_err("hdcp2p2 sysfs group creation failed\n"); goto error; } - init_completion(&hdcp2p2_ctrl->rxstatus_completion); + init_completion(&ctrl->rxstatus_completion); - hdcp2p2_ctrl->sink_status = SINK_DISCONNECTED; + ctrl->sink_status = SINK_DISCONNECTED; - hdcp2p2_ctrl->next_sink_message = START_AUTHENTICATION; - hdcp2p2_ctrl->next_tx_message = AKE_INIT_MESSAGE; - hdcp2p2_ctrl->tx_has_master_key = false; - hdcp2p2_ctrl->auth_state = HDCP_STATE_INACTIVE; - hdcp2p2_ctrl->ops = &ops; - mutex_init(&hdcp2p2_ctrl->mutex); + atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE); - rc = hdcp_library_register(&hdcp2p2_ctrl->hdcp_lib_handle, - hdcp2p2_ctrl->client_ops, - hdcp2p2_ctrl->txmtr_ops, hdcp2p2_ctrl); + ctrl->ops = &ops; + mutex_init(&ctrl->mutex); + + rc = hdcp_library_register(&ctrl->lib_ctx, + ctrl->client_ops, + ctrl->txmtr_ops, ctrl); if (rc) { - DEV_ERR("%s: Unable to register with HDCP 2.2 library\n", - __func__); + pr_err("Unable to register with HDCP 2.2 library\n"); goto error; } - return hdcp2p2_ctrl; + init_kthread_worker(&ctrl->worker); + init_kthread_work(&ctrl->auth, hdmi_hdcp2p2_auth_work); + init_kthread_work(&ctrl->send_msg, hdmi_hdcp2p2_send_msg_work); + init_kthread_work(&ctrl->recv_msg, hdmi_hdcp2p2_recv_msg_work); + init_kthread_work(&ctrl->status, hdmi_hdcp2p2_auth_status_work); + + ctrl->thread = kthread_run(kthread_worker_fn, + &ctrl->worker, "hdmi_hdcp2p2"); + + if (IS_ERR(ctrl->thread)) { + pr_err("unable to start hdcp2p2 thread\n"); + rc = PTR_ERR(ctrl->thread); + ctrl->thread = NULL; + goto error; + } + + return ctrl; error: - kfree(hdcp2p2_ctrl); + kfree(ctrl); return ERR_PTR(rc); } -static bool hdcp2p2_supported(struct hdcp2p2_ctrl *hdcp2p2_ctrl) +static bool hdmi_hdcp2p2_supported(struct hdmi_hdcp2p2_ctrl *ctrl) { u8 hdcp2version; - int rc = hdcp2p2_read_version(hdcp2p2_ctrl, &hdcp2version); + int rc = hdmi_hdcp2p2_read_version(ctrl, &hdcp2version); if (rc) goto error; if (hdcp2version & BIT(2)) { - DEV_DBG("%s: Sink is HDCP 2.2 capable\n", __func__); + pr_debug("Sink is HDCP 2.2 capable\n"); return true; } error: - DEV_DBG("%s: Sink is not HDCP 2.2 capable\n", __func__); + pr_debug("Sink is not HDCP 2.2 capable\n"); return false; } struct hdmi_hdcp_ops *hdmi_hdcp2p2_start(void *input) { - struct hdcp2p2_ctrl *hdcp2p2_ctrl = input; + struct hdmi_hdcp2p2_ctrl *ctrl = input; - DEV_DBG("%s: Checking sink capability\n", __func__); - if (hdcp2p2_supported(hdcp2p2_ctrl)) - return hdcp2p2_ctrl->ops; + pr_debug("Checking sink capability\n"); + if (hdmi_hdcp2p2_supported(ctrl)) + return ctrl->ops; else return NULL; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.c b/drivers/video/fbdev/msm/mdss_hdmi_util.c index 20bcf3e5fa9f..53c8006b0071 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_util.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_util.c @@ -633,6 +633,7 @@ again: time_out_count = wait_for_completion_timeout( &ddc_ctrl->ddc_sw_done, wait_time); + DEV_DBG("ddc read done at %dms\n", jiffies_to_msecs(jiffies)); DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL, BIT(1)); if (!time_out_count) { @@ -1089,6 +1090,8 @@ again: time_out_count = wait_for_completion_timeout( &ddc_ctrl->ddc_sw_done, HZ/2); + DEV_DBG("DDC write done at %dms\n", jiffies_to_msecs(jiffies)); + reg_val = DSS_REG_R(ddc_ctrl->io, HDMI_DDC_INT_CTRL); DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL, reg_val & (~BIT(2))); if (!time_out_count) { diff --git a/include/linux/hdcp_qseecom.h b/include/linux/hdcp_qseecom.h index 0a31b66e6a72..8d7f19ad97ff 100644 --- a/include/linux/hdcp_qseecom.h +++ b/include/linux/hdcp_qseecom.h @@ -14,25 +14,74 @@ #define __HDCP_QSEECOM_H #include +enum hdcp_lib_wakeup_cmd { + HDCP_WKUP_CMD_START, + HDCP_WKUP_CMD_STOP, + HDCP_WKUP_CMD_MSG_SEND_SUCCESS, + HDCP_WKUP_CMD_MSG_SEND_FAILED, + HDCP_WKUP_CMD_MSG_RECV_SUCCESS, + HDCP_WKUP_CMD_MSG_RECV_FAILED, + HDCP_WKUP_CMD_MSG_RECV_TIMEOUT, +}; + +enum hdmi_hdcp_cmd { + HDMI_HDCP_SEND_MESSAGE, + HDMI_HDCP_RECV_MESSAGE, + HDMI_HDCP_STATUS_SUCCESS, + HDMI_HDCP_STATUS_FAIL +}; + +static inline char *hdmi_hdcp_cmd_to_str(uint32_t cmd) +{ + switch (cmd) { + case HDMI_HDCP_SEND_MESSAGE: + return "HDMI_HDCP_SEND_MESSAGE"; + case HDMI_HDCP_RECV_MESSAGE: + return "HDMI_HDCP_RECV_MESSAGE"; + case HDMI_HDCP_STATUS_SUCCESS: + return "HDMI_HDCP_STATUS_SUCCESS"; + case HDMI_HDCP_STATUS_FAIL: + return "HDMI_HDCP_STATUS_FAIL"; + default: + return "???"; + } +} + +static inline char *hdcp_cmd_to_str(uint32_t cmd) +{ + switch (cmd) { + case HDCP_WKUP_CMD_START: + return "HDCP_WKUP_CMD_START"; + case HDCP_WKUP_CMD_STOP: + return "HDCP_WKUP_CMD_STOP"; + case HDCP_WKUP_CMD_MSG_SEND_SUCCESS: + return "HDCP_WKUP_CMD_MSG_SEND_SUCCESS"; + case HDCP_WKUP_CMD_MSG_SEND_FAILED: + return "HDCP_WKUP_CMD_MSG_SEND_FAILED"; + case HDCP_WKUP_CMD_MSG_RECV_SUCCESS: + return "HDCP_WKUP_CMD_MSG_RECV_SUCCESS"; + case HDCP_WKUP_CMD_MSG_RECV_FAILED: + return "HDCP_WKUP_CMD_MSG_RECV_FAILED"; + case HDCP_WKUP_CMD_MSG_RECV_TIMEOUT: + return "HDCP_WKUP_CMD_MSG_RECV_TIMEOUT"; + default: + return "???"; + } +} + struct hdcp_txmtr_ops { - int (*start)(void *phdcpcontext); - int (*stop)(void *phdcpcontext); + int (*wakeup)(void *phdcpcontext, enum hdcp_lib_wakeup_cmd cmd, + char *msg, uint32_t len); bool (*feature_supported)(void *phdcpcontext); - int (*process_message)(void *phdcpcontext, - unsigned char *msg, uint32_t msg_size); int (*hdcp_txmtr_get_state)(void *phdcpcontext, uint32_t *state); - int (*hdcp_txmtr_query_stream_type)(void *phdcpcontext); + int (*hdcp_query_stream_type)(void *phdcpcontext); }; struct hdcp_client_ops { - int (*hdcp_send_message)(void *client_ctx, - char *message, uint32_t msg_size); - int (*hdcp_recv_message)(void *client_ctx, - char *message, uint32_t msg_size, - u32 timeout); - int (*hdcp_tz_error)(void *client_ctx); + int (*wakeup)(void *client_ctx, enum hdmi_hdcp_cmd cmd, + char *msg, uint32_t msglen, uint32_t timeout); }; int hdcp_library_register(void **pphdcpcontext,