From d4e9e455d69dc29a947bd773e3562b1cac41c811 Mon Sep 17 00:00:00 2001 From: Ajay Singh Parmar Date: Mon, 2 Nov 2015 14:58:21 -0800 Subject: [PATCH] msm: mdss: hdmi: hdcp2.2: use strict timeouts as per standard Implement timers as per HDCP 2.2 standard for every message exchanged with sink. The standard provides round trip timeout values for each transaction. Use these timeout values and consider it as authentication failure if timer expires. Change-Id: I1fb647b70b8c3aafaeb6e91fd3f636d387247133 Signed-off-by: Ajay Singh Parmar --- drivers/misc/hdcp.c | 11 +- drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c | 121 ++++++++++++++------ drivers/video/fbdev/msm/mdss_hdmi_util.c | 65 +++++++---- drivers/video/fbdev/msm/mdss_hdmi_util.h | 6 +- 4 files changed, 144 insertions(+), 59 deletions(-) diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c index 0a32d11f6161..0df388a841d6 100644 --- a/drivers/misc/hdcp.c +++ b/drivers/misc/hdcp.c @@ -332,6 +332,7 @@ struct hdcp_lib_handle { uint32_t msglen; uint32_t tz_ctxhandle; uint32_t hdcp_timeout; + uint32_t timeout_left; bool no_stored_km_flag; bool feature_supported; void *client_ctx; @@ -842,7 +843,11 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data) mutex_lock(&handle->wakeup_mutex); handle->wakeup_cmd = data->cmd; - pr_debug("%s\n", hdcp_lib_cmd_to_str(handle->wakeup_cmd)); + handle->timeout_left = data->timeout; + + pr_debug("%s, timeout left: %dms\n", + hdcp_lib_cmd_to_str(handle->wakeup_cmd), + handle->timeout_left); rc = hdcp_lib_check_valid_state(handle); if (rc) @@ -874,6 +879,8 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data) handle->no_stored_km_flag = 0; handle->repeater_flag = 0; handle->last_msg_sent = 0; + handle->hdcp_timeout = 0; + handle->timeout_left = 0; atomic_set(&handle->hdcp_off, 0); handle->hdcp_state = HDCP_STATE_INIT; @@ -949,7 +956,7 @@ static void hdcp_lib_msg_sent_work(struct kthread_work *work) break; default: cdata.cmd = HDMI_HDCP_WKUP_CMD_RECV_MESSAGE; - cdata.timeout = handle->hdcp_timeout; + cdata.timeout = handle->timeout_left; } mutex_unlock(&handle->hdcp_lock); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c index 421ab07ca930..0673609ad5bd 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c @@ -34,11 +34,12 @@ #define HDCP_SINK_DDC_HDCP2_RXSTATUS 0x70 /* RxStatus, 2 bytes */ #define HDCP_SINK_DDC_HDCP2_READ_MESSAGE 0x80 /* HDCP Tx reads here */ -#define HDCP2P2_LINK_CHECK_TIME_MS 500 /* link check within 1 sec */ +#define HDCP2P2_LINK_CHECK_TIME_MS 900 /* link check within 1 sec */ #define HDCP2P2_SEC_TO_US 1000000 #define HDCP2P2_MS_TO_US 1000 #define HDCP2P2_KHZ_TO_HZ 1000 +#define HDCP2P2_DEFAULT_TIMEOUT 500 /* * HDCP 2.2 encryption requires the data encryption block that is present in @@ -66,6 +67,7 @@ struct hdmi_hdcp2p2_ctrl { char *send_msg_buf; uint32_t send_msg_len; uint32_t timeout; + uint32_t timeout_left; struct task_struct *thread; struct kthread_worker worker; @@ -78,6 +80,45 @@ struct hdmi_hdcp2p2_ctrl { struct delayed_work link_check_work; }; +static inline bool hdmi_hdcp2p2_is_valid_state(struct hdmi_hdcp2p2_ctrl *ctrl) +{ + if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_AUTHENTICATE) + return true; + + if (atomic_read(&ctrl->auth_state) != HDCP_STATE_INACTIVE) + return true; + + return false; +} + +static int hdmi_hdcp2p2_copy_buf(struct hdmi_hdcp2p2_ctrl *ctrl, + struct hdmi_hdcp_wakeup_data *data) +{ + mutex_lock(&ctrl->msg_lock); + + if (!data->send_msg_len) { + mutex_unlock(&ctrl->msg_lock); + return 0; + } + + ctrl->send_msg_len = data->send_msg_len; + + kzfree(ctrl->send_msg_buf); + + ctrl->send_msg_buf = kzalloc(data->send_msg_len, GFP_KERNEL); + + if (!ctrl->send_msg_buf) { + mutex_unlock(&ctrl->msg_lock); + return -ENOMEM; + } + + memcpy(ctrl->send_msg_buf, data->send_msg_buf, ctrl->send_msg_len); + + mutex_unlock(&ctrl->msg_lock); + + return 0; +} + static int hdmi_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data) { struct hdmi_hdcp2p2_ctrl *ctrl; @@ -95,43 +136,35 @@ static int hdmi_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data) mutex_lock(&ctrl->wakeup_mutex); - pr_debug("cmd: %s\n", hdmi_hdcp_cmd_to_str(data->cmd)); + pr_debug("cmd: %s, timeout %dms\n", + hdmi_hdcp_cmd_to_str(data->cmd), + data->timeout); ctrl->wakeup_cmd = data->cmd; - ctrl->timeout = data->timeout; - mutex_lock(&ctrl->msg_lock); - if (data->send_msg_len) { - ctrl->send_msg_len = data->send_msg_len; + if (data->timeout) + ctrl->timeout = data->timeout; + else + ctrl->timeout = HDCP2P2_DEFAULT_TIMEOUT; - kzfree(ctrl->send_msg_buf); - - ctrl->send_msg_buf = kzalloc( - data->send_msg_len, GFP_KERNEL); - - if (!ctrl->send_msg_buf) { - mutex_unlock(&ctrl->msg_lock); - goto exit; - } - - memcpy(ctrl->send_msg_buf, data->send_msg_buf, - ctrl->send_msg_len); + if (!hdmi_hdcp2p2_is_valid_state(ctrl)) { + pr_err("invalid state\n"); + goto exit; } - mutex_unlock(&ctrl->msg_lock); + + if (hdmi_hdcp2p2_copy_buf(ctrl, data)) + goto exit; switch (ctrl->wakeup_cmd) { case HDMI_HDCP_WKUP_CMD_SEND_MESSAGE: - if (atomic_read(&ctrl->auth_state) != HDCP_STATE_INACTIVE) - queue_kthread_work(&ctrl->worker, &ctrl->send_msg); + queue_kthread_work(&ctrl->worker, &ctrl->send_msg); break; case HDMI_HDCP_WKUP_CMD_RECV_MESSAGE: - if (atomic_read(&ctrl->auth_state) != HDCP_STATE_INACTIVE) - queue_kthread_work(&ctrl->worker, &ctrl->recv_msg); + queue_kthread_work(&ctrl->worker, &ctrl->recv_msg); break; case HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS: case HDMI_HDCP_WKUP_CMD_STATUS_FAILED: - if (atomic_read(&ctrl->auth_state) != HDCP_STATE_INACTIVE) - queue_kthread_work(&ctrl->worker, &ctrl->status); + queue_kthread_work(&ctrl->worker, &ctrl->status); break; case HDMI_HDCP_WKUP_CMD_AUTHENTICATE: queue_kthread_work(&ctrl->worker, &ctrl->auth); @@ -366,6 +399,7 @@ static int hdmi_hdcp2p2_ddc_read_message(struct hdmi_hdcp2p2_ctrl *ctrl, pr_err("hdcp is off\n"); return -EINVAL; } + memset(&ddc_data, 0, sizeof(ddc_data)); ddc_data.dev_addr = HDCP_SINK_DDC_SLAVE_ADDR; ddc_data.offset = HDCP_SINK_DDC_HDCP2_READ_MESSAGE; @@ -378,9 +412,14 @@ static int hdmi_hdcp2p2_ddc_read_message(struct hdmi_hdcp2p2_ctrl *ctrl, ctrl->init_data.ddc_ctrl->ddc_data = ddc_data; + pr_debug("read msg timeout %dms\n", timeout); + rc = hdmi_ddc_read(ctrl->init_data.ddc_ctrl); if (rc) pr_err("Cannot read HDCP message register\n"); + + ctrl->timeout_left = ctrl->init_data.ddc_ctrl->ddc_data.timeout_left; + return rc; } @@ -396,6 +435,7 @@ static int hdmi_hdcp2p2_ddc_write_message(struct hdmi_hdcp2p2_ctrl *ctrl, ddc_data.data_buf = buf; ddc_data.data_len = size; ddc_data.retry = 1; + ddc_data.hard_timeout = ctrl->timeout; ddc_data.what = "HDCP2WriteMessage"; ctrl->init_data.ddc_ctrl->ddc_data = ddc_data; @@ -403,6 +443,9 @@ static int hdmi_hdcp2p2_ddc_write_message(struct hdmi_hdcp2p2_ctrl *ctrl, rc = hdmi_ddc_write(ctrl->init_data.ddc_ctrl); if (rc) pr_err("Cannot write HDCP message register\n"); + + ctrl->timeout_left = ctrl->init_data.ddc_ctrl->ddc_data.timeout_left; + return rc; } @@ -545,6 +588,7 @@ static void hdmi_hdcp2p2_send_msg_work(struct kthread_work *work) cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_SEND_FAILED; } else { cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_SEND_SUCCESS; + cdata.timeout = ctrl->timeout_left; } exit: kzfree(msg); @@ -605,10 +649,13 @@ static void hdmi_hdcp2p2_recv_msg_work(struct kthread_work *work) lines_needed_for_given_time = (ctrl->timeout * HDCP2P2_MS_TO_US) / time_taken_by_one_line_us; - pr_debug("timeout for rxstatus %d\n", lines_needed_for_given_time); + pr_debug("timeout for rxstatus %dms, %d hsyncs\n", + ctrl->timeout, lines_needed_for_given_time); ddc_data->intr_mask = RXSTATUS_MESSAGE_SIZE; - ddc_data->timer_delay_lines = lines_needed_for_given_time; + ddc_data->timeout_ms = ctrl->timeout; + ddc_data->timeout_hsync = lines_needed_for_given_time; + ddc_data->periodic_timer_hsync = lines_needed_for_given_time / 20; ddc_data->read_method = HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER; rc = hdmi_hdcp2p2_ddc_read_rxstatus(ddc_ctrl, true); @@ -617,26 +664,30 @@ static void hdmi_hdcp2p2_recv_msg_work(struct kthread_work *work) goto exit; } + ctrl->timeout_left = ddc_data->timeout_left; + + pr_debug("timeout left after rxstatus %dms, msg size %d\n", + ctrl->timeout_left, ddc_data->message_size); + if (!ddc_data->message_size) { pr_err("recvd invalid message size\n"); rc = -EINVAL; goto exit; } - pr_debug("rxstatus msg size %d\n", ddc_data->message_size); - recvd_msg_buf = kzalloc(ddc_data->message_size, GFP_KERNEL); if (!recvd_msg_buf) goto exit; rc = hdmi_hdcp2p2_ddc_read_message(ctrl, recvd_msg_buf, - ddc_data->message_size, ctrl->timeout); + ddc_data->message_size, ctrl->timeout_left); if (rc) pr_err("error reading message %d\n", rc); cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS; cdata.recvd_msg_buf = recvd_msg_buf; cdata.recvd_msg_len = ddc_data->message_size; + cdata.timeout = ctrl->timeout_left; exit: if (rc == -ETIMEDOUT) cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT; @@ -672,14 +723,16 @@ static int hdmi_hdcp2p2_link_check(struct hdmi_hdcp2p2_ctrl *ctrl) fps = timing->refresh_rate / HDCP2P2_KHZ_TO_HZ; v_total = hdmi_tx_get_v_total(timing); time_taken_by_one_line_us = HDCP2P2_SEC_TO_US / (v_total * fps); - lines_needed_for_given_time = (jiffies_to_msecs(HZ / 2) * + lines_needed_for_given_time = (jiffies_to_msecs((HZ / 2) + (HZ / 4)) * HDCP2P2_MS_TO_US) / time_taken_by_one_line_us; - pr_debug("timeout for rxstatus %d\n", lines_needed_for_given_time); + pr_debug("timeout for rxstatus %d hsyncs\n", + lines_needed_for_given_time); ddc_data->intr_mask = RXSTATUS_READY | RXSTATUS_MESSAGE_SIZE | RXSTATUS_REAUTH_REQ; - ddc_data->timer_delay_lines = lines_needed_for_given_time; + ddc_data->timeout_hsync = lines_needed_for_given_time; + ddc_data->periodic_timer_hsync = lines_needed_for_given_time; ddc_data->read_method = HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER; return hdmi_hdcp2p2_ddc_read_rxstatus(ddc_ctrl, false); @@ -788,7 +841,7 @@ static void hdmi_hdcp2p2_link_work(struct kthread_work *work) } rc = hdmi_hdcp2p2_ddc_read_message(ctrl, recvd_msg_buf, - ddc_data->message_size, ctrl->timeout); + ddc_data->message_size, HDCP2P2_DEFAULT_TIMEOUT); if (rc) { cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; pr_err("error reading message %d\n", rc); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.c b/drivers/video/fbdev/msm/mdss_hdmi_util.c index df8d72ee7cdf..09138eb9056d 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_util.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_util.c @@ -515,23 +515,22 @@ static int hdmi_ddc_clear_irq(struct hdmi_tx_ddc_ctrl *ddc_ctrl, return -EINVAL; } + /* clear and enable interrutps */ + ddc_int_ctrl = sw_done_mask | sw_done_ack | hw_done_mask | hw_done_ack; + + DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL, ddc_int_ctrl); + /* wait until DDC HW is free */ timeout = 100; do { - --timeout; ddc_status = DSS_REG_R_ND(ddc_ctrl->io, HDMI_DDC_HW_STATUS); in_use = ddc_status & (in_use_by_sw | in_use_by_hw); if (in_use) { pr_debug("ddc is in use by %s\n", ddc_status & in_use_by_sw ? "sw" : "hw"); - msleep(20); + udelay(100); } - } while (in_use && timeout); - - /* clear and enable interrutps */ - ddc_int_ctrl = sw_done_mask | sw_done_ack | hw_done_mask | hw_done_ack; - - DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL, ddc_int_ctrl); + } while (in_use && --timeout); if (!timeout) { pr_err("%s: timedout\n", what); @@ -652,11 +651,15 @@ again: &ddc_ctrl->ddc_sw_done, wait_time); pr_debug("ddc read done at %dms\n", jiffies_to_msecs(jiffies)); + ddc_data->timeout_left = jiffies_to_msecs(time_out_count); + DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL, BIT(1)); if (!time_out_count) { if (ddc_data->retry-- > 0) { pr_debug("failed timout, retry=%d\n", ddc_data->retry); - goto again; + + if (!ddc_data->hard_timeout) + goto again; } status = -ETIMEDOUT; pr_err("timedout(7), Int Ctrl=%08x\n", @@ -809,8 +812,7 @@ static int hdmi_ddc_hdcp2p2_isr(struct hdmi_tx_ddc_ctrl *ddc_ctrl) intr2 = DSS_REG_R(io, HDMI_HDCP_INT_CTRL2); intr0 = DSS_REG_R(io, HDMI_DDC_INT_CTRL0); - pr_debug("INT_CTRL0 :0x%x\n", intr0); - pr_debug("INT_CTRL2 :0x%x\n", intr2); + pr_debug("ctl0: 0x%x, ctl1: 0x%x\n", intr0, intr2); /* check for encryption ready interrupt */ if (intr2 & BIT(2)) { @@ -1204,6 +1206,7 @@ int hdmi_ddc_write(struct hdmi_tx_ddc_ctrl *ddc_ctrl) int status = 0, retry = 10; u32 time_out_count; struct hdmi_tx_ddc_data *ddc_data; + u32 wait_time; if (!ddc_ctrl || !ddc_ctrl->io) { pr_err("invalid input\n"); @@ -1286,8 +1289,19 @@ again: reinit_completion(&ddc_ctrl->ddc_sw_done); DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_CTRL, BIT(0)); + if (ddc_data->hard_timeout) { + pr_debug("using hard_timeout %dms\n", ddc_data->hard_timeout); + wait_time = msecs_to_jiffies(ddc_data->hard_timeout); + } else { + wait_time = HZ/2; + } + + pr_debug("timeout for ddc write %d jiffies\n", wait_time); + time_out_count = wait_for_completion_timeout( - &ddc_ctrl->ddc_sw_done, HZ/2); + &ddc_ctrl->ddc_sw_done, wait_time); + + ddc_data->timeout_left = jiffies_to_msecs(time_out_count); pr_debug("DDC write done at %dms\n", jiffies_to_msecs(jiffies)); @@ -1297,7 +1311,9 @@ again: if (retry-- > 0) { pr_debug("%s: failed timout, retry=%d\n", ddc_data->what, retry); - goto again; + + if (!ddc_data->hard_timeout) + goto again; } status = -ETIMEDOUT; pr_err("%s: timedout, Int Ctrl=%08x\n", @@ -1635,6 +1651,7 @@ int hdmi_hdcp2p2_ddc_read_rxstatus(struct hdmi_tx_ddc_ctrl *ctrl, bool wait) u32 reg_val; u32 intr_en_mask; u32 timeout; + u32 timer; int rc = 0; struct hdmi_tx_hdcp2p2_ddc_data *data; @@ -1674,9 +1691,11 @@ int hdmi_hdcp2p2_ddc_read_rxstatus(struct hdmi_tx_ddc_ctrl *ctrl, bool wait) * 3. DDC_TIMEOUT_TIMER: Timeout in hsyncs which starts counting when * a request is made and stops when it is accepted by DDC arbiter */ - timeout = data->timer_delay_lines; - pr_debug("timeout: %d\n", timeout); - DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_TIMER_CTRL, timeout); + timeout = data->timeout_hsync; + timer = data->periodic_timer_hsync; + pr_debug("timeout: %d hsyncs, timer %d hsync\n", timeout, timer); + + DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_TIMER_CTRL, timer); /* Set both urgent and hw-timeout fields to the same value */ DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_TIMER_CTRL2, @@ -1711,8 +1730,6 @@ int hdmi_hdcp2p2_ddc_read_rxstatus(struct hdmi_tx_ddc_ctrl *ctrl, bool wait) reg_val = DSS_REG_R(ctrl->io, HDMI_HW_DDC_CTRL); reg_val &= ~(BIT(1) | BIT(0)); - pr_debug("data->read_method %d\n", data->read_method); - if (data->read_method == HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER) reg_val |= BIT(1) | BIT(0); else @@ -1725,16 +1742,20 @@ int hdmi_hdcp2p2_ddc_read_rxstatus(struct hdmi_tx_ddc_ctrl *ctrl, bool wait) DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_SW_TRIGGER, 1); if (wait) { + u32 wait_timeout; + reinit_completion(&ctrl->rxstatus_completion); - /* max timeout as per hdcp 2.2 std is 1 sec */ - timeout = wait_for_completion_timeout( + + wait_timeout = wait_for_completion_timeout( &ctrl->rxstatus_completion, - msecs_to_jiffies(HDMI_SEC_TO_MS)); - if (!timeout) { + msecs_to_jiffies(data->timeout_ms)); + if (!wait_timeout) { pr_err("sw ddc rxstatus timeout\n"); rc = -ETIMEDOUT; } + data->timeout_left = jiffies_to_msecs(wait_timeout); + rc = hdmi_ddc_check_status(ctrl); hdmi_hdcp2p2_ddc_disable(ctrl); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.h b/drivers/video/fbdev/msm/mdss_hdmi_util.h index 4530fd414b12..439a1c408eae 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_util.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_util.h @@ -405,6 +405,7 @@ struct hdmi_tx_ddc_data { u32 request_len; u32 no_align; u32 hard_timeout; + u32 timeout_left; int retry; }; @@ -416,7 +417,10 @@ enum hdmi_tx_hdcp2p2_rxstatus_intr_mask { struct hdmi_tx_hdcp2p2_ddc_data { enum hdmi_tx_hdcp2p2_rxstatus_intr_mask intr_mask; - u32 timer_delay_lines; + u32 timeout_ms; + u32 timeout_hsync; + u32 periodic_timer_hsync; + u32 timeout_left; u32 read_method; u32 message_size; bool encryption_ready;