18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved. 38c2ecf20Sopenharmony_ci */ 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include "hdmi.h" 68c2ecf20Sopenharmony_ci#include <linux/qcom_scm.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define HDCP_REG_ENABLE 0x01 98c2ecf20Sopenharmony_ci#define HDCP_REG_DISABLE 0x00 108c2ecf20Sopenharmony_ci#define HDCP_PORT_ADDR 0x74 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define HDCP_INT_STATUS_MASK ( \ 138c2ecf20Sopenharmony_ci HDMI_HDCP_INT_CTRL_AUTH_SUCCESS_INT | \ 148c2ecf20Sopenharmony_ci HDMI_HDCP_INT_CTRL_AUTH_FAIL_INT | \ 158c2ecf20Sopenharmony_ci HDMI_HDCP_INT_CTRL_AUTH_XFER_REQ_INT | \ 168c2ecf20Sopenharmony_ci HDMI_HDCP_INT_CTRL_AUTH_XFER_DONE_INT) 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define AUTH_WORK_RETRIES_TIME 100 198c2ecf20Sopenharmony_ci#define AUTH_RETRIES_TIME 30 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* QFPROM Registers for HDMI/HDCP */ 228c2ecf20Sopenharmony_ci#define QFPROM_RAW_FEAT_CONFIG_ROW0_LSB 0x000000F8 238c2ecf20Sopenharmony_ci#define QFPROM_RAW_FEAT_CONFIG_ROW0_MSB 0x000000FC 248c2ecf20Sopenharmony_ci#define HDCP_KSV_LSB 0x000060D8 258c2ecf20Sopenharmony_ci#define HDCP_KSV_MSB 0x000060DC 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cienum DS_TYPE { /* type of downstream device */ 288c2ecf20Sopenharmony_ci DS_UNKNOWN, 298c2ecf20Sopenharmony_ci DS_RECEIVER, 308c2ecf20Sopenharmony_ci DS_REPEATER, 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cienum hdmi_hdcp_state { 348c2ecf20Sopenharmony_ci HDCP_STATE_NO_AKSV, 358c2ecf20Sopenharmony_ci HDCP_STATE_INACTIVE, 368c2ecf20Sopenharmony_ci HDCP_STATE_AUTHENTICATING, 378c2ecf20Sopenharmony_ci HDCP_STATE_AUTHENTICATED, 388c2ecf20Sopenharmony_ci HDCP_STATE_AUTH_FAILED 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct hdmi_hdcp_reg_data { 428c2ecf20Sopenharmony_ci u32 reg_id; 438c2ecf20Sopenharmony_ci u32 off; 448c2ecf20Sopenharmony_ci char *name; 458c2ecf20Sopenharmony_ci u32 reg_val; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct hdmi_hdcp_ctrl { 498c2ecf20Sopenharmony_ci struct hdmi *hdmi; 508c2ecf20Sopenharmony_ci u32 auth_retries; 518c2ecf20Sopenharmony_ci bool tz_hdcp; 528c2ecf20Sopenharmony_ci enum hdmi_hdcp_state hdcp_state; 538c2ecf20Sopenharmony_ci struct work_struct hdcp_auth_work; 548c2ecf20Sopenharmony_ci struct work_struct hdcp_reauth_work; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define AUTH_ABORT_EV 1 578c2ecf20Sopenharmony_ci#define AUTH_RESULT_RDY_EV 2 588c2ecf20Sopenharmony_ci unsigned long auth_event; 598c2ecf20Sopenharmony_ci wait_queue_head_t auth_event_queue; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci u32 ksv_fifo_w_index; 628c2ecf20Sopenharmony_ci /* 638c2ecf20Sopenharmony_ci * store aksv from qfprom 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ci u32 aksv_lsb; 668c2ecf20Sopenharmony_ci u32 aksv_msb; 678c2ecf20Sopenharmony_ci bool aksv_valid; 688c2ecf20Sopenharmony_ci u32 ds_type; 698c2ecf20Sopenharmony_ci u32 bksv_lsb; 708c2ecf20Sopenharmony_ci u32 bksv_msb; 718c2ecf20Sopenharmony_ci u8 dev_count; 728c2ecf20Sopenharmony_ci u8 depth; 738c2ecf20Sopenharmony_ci u8 ksv_list[5 * 127]; 748c2ecf20Sopenharmony_ci bool max_cascade_exceeded; 758c2ecf20Sopenharmony_ci bool max_dev_exceeded; 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int msm_hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset, 798c2ecf20Sopenharmony_ci u8 *data, u16 data_len) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci int rc; 828c2ecf20Sopenharmony_ci int retry = 5; 838c2ecf20Sopenharmony_ci struct i2c_msg msgs[] = { 848c2ecf20Sopenharmony_ci { 858c2ecf20Sopenharmony_ci .addr = addr >> 1, 868c2ecf20Sopenharmony_ci .flags = 0, 878c2ecf20Sopenharmony_ci .len = 1, 888c2ecf20Sopenharmony_ci .buf = &offset, 898c2ecf20Sopenharmony_ci }, { 908c2ecf20Sopenharmony_ci .addr = addr >> 1, 918c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 928c2ecf20Sopenharmony_ci .len = data_len, 938c2ecf20Sopenharmony_ci .buf = data, 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci }; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci DBG("Start DDC read"); 988c2ecf20Sopenharmony_ciretry: 998c2ecf20Sopenharmony_ci rc = i2c_transfer(hdmi->i2c, msgs, 2); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci retry--; 1028c2ecf20Sopenharmony_ci if (rc == 2) 1038c2ecf20Sopenharmony_ci rc = 0; 1048c2ecf20Sopenharmony_ci else if (retry > 0) 1058c2ecf20Sopenharmony_ci goto retry; 1068c2ecf20Sopenharmony_ci else 1078c2ecf20Sopenharmony_ci rc = -EIO; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci DBG("End DDC read %d", rc); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return rc; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#define HDCP_DDC_WRITE_MAX_BYTE_NUM 32 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic int msm_hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset, 1178c2ecf20Sopenharmony_ci u8 *data, u16 data_len) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci int rc; 1208c2ecf20Sopenharmony_ci int retry = 10; 1218c2ecf20Sopenharmony_ci u8 buf[HDCP_DDC_WRITE_MAX_BYTE_NUM]; 1228c2ecf20Sopenharmony_ci struct i2c_msg msgs[] = { 1238c2ecf20Sopenharmony_ci { 1248c2ecf20Sopenharmony_ci .addr = addr >> 1, 1258c2ecf20Sopenharmony_ci .flags = 0, 1268c2ecf20Sopenharmony_ci .len = 1, 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci }; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci DBG("Start DDC write"); 1318c2ecf20Sopenharmony_ci if (data_len > (HDCP_DDC_WRITE_MAX_BYTE_NUM - 1)) { 1328c2ecf20Sopenharmony_ci pr_err("%s: write size too big\n", __func__); 1338c2ecf20Sopenharmony_ci return -ERANGE; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci buf[0] = offset; 1378c2ecf20Sopenharmony_ci memcpy(&buf[1], data, data_len); 1388c2ecf20Sopenharmony_ci msgs[0].buf = buf; 1398c2ecf20Sopenharmony_ci msgs[0].len = data_len + 1; 1408c2ecf20Sopenharmony_ciretry: 1418c2ecf20Sopenharmony_ci rc = i2c_transfer(hdmi->i2c, msgs, 1); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci retry--; 1448c2ecf20Sopenharmony_ci if (rc == 1) 1458c2ecf20Sopenharmony_ci rc = 0; 1468c2ecf20Sopenharmony_ci else if (retry > 0) 1478c2ecf20Sopenharmony_ci goto retry; 1488c2ecf20Sopenharmony_ci else 1498c2ecf20Sopenharmony_ci rc = -EIO; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci DBG("End DDC write %d", rc); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return rc; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_scm_wr(struct hdmi_hdcp_ctrl *hdcp_ctrl, u32 *preg, 1578c2ecf20Sopenharmony_ci u32 *pdata, u32 count) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 1608c2ecf20Sopenharmony_ci struct qcom_scm_hdcp_req scm_buf[QCOM_SCM_HDCP_MAX_REQ_CNT]; 1618c2ecf20Sopenharmony_ci u32 resp, phy_addr, idx = 0; 1628c2ecf20Sopenharmony_ci int i, ret = 0; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci WARN_ON(!pdata || !preg || (count == 0)); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (hdcp_ctrl->tz_hdcp) { 1678c2ecf20Sopenharmony_ci phy_addr = (u32)hdmi->mmio_phy_addr; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci while (count) { 1708c2ecf20Sopenharmony_ci memset(scm_buf, 0, sizeof(scm_buf)); 1718c2ecf20Sopenharmony_ci for (i = 0; i < count && i < QCOM_SCM_HDCP_MAX_REQ_CNT; 1728c2ecf20Sopenharmony_ci i++) { 1738c2ecf20Sopenharmony_ci scm_buf[i].addr = phy_addr + preg[idx]; 1748c2ecf20Sopenharmony_ci scm_buf[i].val = pdata[idx]; 1758c2ecf20Sopenharmony_ci idx++; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci ret = qcom_scm_hdcp_req(scm_buf, i, &resp); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (ret || resp) { 1808c2ecf20Sopenharmony_ci pr_err("%s: error: scm_call ret=%d resp=%u\n", 1818c2ecf20Sopenharmony_ci __func__, ret, resp); 1828c2ecf20Sopenharmony_ci ret = -EINVAL; 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci count -= i; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci } else { 1898c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) 1908c2ecf20Sopenharmony_ci hdmi_write(hdmi, preg[i], pdata[i]); 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return ret; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_civoid msm_hdmi_hdcp_irq(struct hdmi_hdcp_ctrl *hdcp_ctrl) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 1998c2ecf20Sopenharmony_ci u32 reg_val, hdcp_int_status; 2008c2ecf20Sopenharmony_ci unsigned long flags; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci spin_lock_irqsave(&hdmi->reg_lock, flags); 2038c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_HDCP_INT_CTRL); 2048c2ecf20Sopenharmony_ci hdcp_int_status = reg_val & HDCP_INT_STATUS_MASK; 2058c2ecf20Sopenharmony_ci if (!hdcp_int_status) { 2068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hdmi->reg_lock, flags); 2078c2ecf20Sopenharmony_ci return; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci /* Clear Interrupts */ 2108c2ecf20Sopenharmony_ci reg_val |= hdcp_int_status << 1; 2118c2ecf20Sopenharmony_ci /* Clear AUTH_FAIL_INFO as well */ 2128c2ecf20Sopenharmony_ci if (hdcp_int_status & HDMI_HDCP_INT_CTRL_AUTH_FAIL_INT) 2138c2ecf20Sopenharmony_ci reg_val |= HDMI_HDCP_INT_CTRL_AUTH_FAIL_INFO_ACK; 2148c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_INT_CTRL, reg_val); 2158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hdmi->reg_lock, flags); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci DBG("hdcp irq %x", hdcp_int_status); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (hdcp_int_status & HDMI_HDCP_INT_CTRL_AUTH_SUCCESS_INT) { 2208c2ecf20Sopenharmony_ci pr_info("%s:AUTH_SUCCESS_INT received\n", __func__); 2218c2ecf20Sopenharmony_ci if (HDCP_STATE_AUTHENTICATING == hdcp_ctrl->hdcp_state) { 2228c2ecf20Sopenharmony_ci set_bit(AUTH_RESULT_RDY_EV, &hdcp_ctrl->auth_event); 2238c2ecf20Sopenharmony_ci wake_up_all(&hdcp_ctrl->auth_event_queue); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (hdcp_int_status & HDMI_HDCP_INT_CTRL_AUTH_FAIL_INT) { 2288c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_HDCP_LINK0_STATUS); 2298c2ecf20Sopenharmony_ci pr_info("%s: AUTH_FAIL_INT rcvd, LINK0_STATUS=0x%08x\n", 2308c2ecf20Sopenharmony_ci __func__, reg_val); 2318c2ecf20Sopenharmony_ci if (HDCP_STATE_AUTHENTICATED == hdcp_ctrl->hdcp_state) 2328c2ecf20Sopenharmony_ci queue_work(hdmi->workq, &hdcp_ctrl->hdcp_reauth_work); 2338c2ecf20Sopenharmony_ci else if (HDCP_STATE_AUTHENTICATING == 2348c2ecf20Sopenharmony_ci hdcp_ctrl->hdcp_state) { 2358c2ecf20Sopenharmony_ci set_bit(AUTH_RESULT_RDY_EV, &hdcp_ctrl->auth_event); 2368c2ecf20Sopenharmony_ci wake_up_all(&hdcp_ctrl->auth_event_queue); 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_msleep(struct hdmi_hdcp_ctrl *hdcp_ctrl, u32 ms, u32 ev) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci int rc; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci rc = wait_event_timeout(hdcp_ctrl->auth_event_queue, 2468c2ecf20Sopenharmony_ci !!test_bit(ev, &hdcp_ctrl->auth_event), 2478c2ecf20Sopenharmony_ci msecs_to_jiffies(ms)); 2488c2ecf20Sopenharmony_ci if (rc) { 2498c2ecf20Sopenharmony_ci pr_info("%s: msleep is canceled by event %d\n", 2508c2ecf20Sopenharmony_ci __func__, ev); 2518c2ecf20Sopenharmony_ci clear_bit(ev, &hdcp_ctrl->auth_event); 2528c2ecf20Sopenharmony_ci return -ECANCELED; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_read_validate_aksv(struct hdmi_hdcp_ctrl *hdcp_ctrl) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* Fetch aksv from QFPROM, this info should be public. */ 2638c2ecf20Sopenharmony_ci hdcp_ctrl->aksv_lsb = hdmi_qfprom_read(hdmi, HDCP_KSV_LSB); 2648c2ecf20Sopenharmony_ci hdcp_ctrl->aksv_msb = hdmi_qfprom_read(hdmi, HDCP_KSV_MSB); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* check there are 20 ones in AKSV */ 2678c2ecf20Sopenharmony_ci if ((hweight32(hdcp_ctrl->aksv_lsb) + hweight32(hdcp_ctrl->aksv_msb)) 2688c2ecf20Sopenharmony_ci != 20) { 2698c2ecf20Sopenharmony_ci pr_err("%s: AKSV QFPROM doesn't have 20 1's, 20 0's\n", 2708c2ecf20Sopenharmony_ci __func__); 2718c2ecf20Sopenharmony_ci pr_err("%s: QFPROM AKSV chk failed (AKSV=%02x%08x)\n", 2728c2ecf20Sopenharmony_ci __func__, hdcp_ctrl->aksv_msb, 2738c2ecf20Sopenharmony_ci hdcp_ctrl->aksv_lsb); 2748c2ecf20Sopenharmony_ci return -EINVAL; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci DBG("AKSV=%02x%08x", hdcp_ctrl->aksv_msb, hdcp_ctrl->aksv_lsb); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return 0; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic int msm_reset_hdcp_ddc_failures(struct hdmi_hdcp_ctrl *hdcp_ctrl) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 2848c2ecf20Sopenharmony_ci u32 reg_val, failure, nack0; 2858c2ecf20Sopenharmony_ci int rc = 0; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* Check for any DDC transfer failures */ 2888c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_HDCP_DDC_STATUS); 2898c2ecf20Sopenharmony_ci failure = reg_val & HDMI_HDCP_DDC_STATUS_FAILED; 2908c2ecf20Sopenharmony_ci nack0 = reg_val & HDMI_HDCP_DDC_STATUS_NACK0; 2918c2ecf20Sopenharmony_ci DBG("HDCP_DDC_STATUS=0x%x, FAIL=%d, NACK0=%d", 2928c2ecf20Sopenharmony_ci reg_val, failure, nack0); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (failure) { 2958c2ecf20Sopenharmony_ci /* 2968c2ecf20Sopenharmony_ci * Indicates that the last HDCP HW DDC transfer failed. 2978c2ecf20Sopenharmony_ci * This occurs when a transfer is attempted with HDCP DDC 2988c2ecf20Sopenharmony_ci * disabled (HDCP_DDC_DISABLE=1) or the number of retries 2998c2ecf20Sopenharmony_ci * matches HDCP_DDC_RETRY_CNT. 3008c2ecf20Sopenharmony_ci * Failure occurred, let's clear it. 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_ci DBG("DDC failure detected"); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* First, Disable DDC */ 3058c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_DDC_CTRL_0, 3068c2ecf20Sopenharmony_ci HDMI_HDCP_DDC_CTRL_0_DISABLE); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* ACK the Failure to Clear it */ 3098c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_HDCP_DDC_CTRL_1); 3108c2ecf20Sopenharmony_ci reg_val |= HDMI_HDCP_DDC_CTRL_1_FAILED_ACK; 3118c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_DDC_CTRL_1, reg_val); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* Check if the FAILURE got Cleared */ 3148c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_HDCP_DDC_STATUS); 3158c2ecf20Sopenharmony_ci if (reg_val & HDMI_HDCP_DDC_STATUS_FAILED) 3168c2ecf20Sopenharmony_ci pr_info("%s: Unable to clear HDCP DDC Failure\n", 3178c2ecf20Sopenharmony_ci __func__); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* Re-Enable HDCP DDC */ 3208c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_DDC_CTRL_0, 0); 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (nack0) { 3248c2ecf20Sopenharmony_ci DBG("Before: HDMI_DDC_SW_STATUS=0x%08x", 3258c2ecf20Sopenharmony_ci hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS)); 3268c2ecf20Sopenharmony_ci /* Reset HDMI DDC software status */ 3278c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_DDC_CTRL); 3288c2ecf20Sopenharmony_ci reg_val |= HDMI_DDC_CTRL_SW_STATUS_RESET; 3298c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_DDC_CTRL, reg_val); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_DDC_CTRL); 3348c2ecf20Sopenharmony_ci reg_val &= ~HDMI_DDC_CTRL_SW_STATUS_RESET; 3358c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_DDC_CTRL, reg_val); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* Reset HDMI DDC Controller */ 3388c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_DDC_CTRL); 3398c2ecf20Sopenharmony_ci reg_val |= HDMI_DDC_CTRL_SOFT_RESET; 3408c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_DDC_CTRL, reg_val); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* If previous msleep is aborted, skip this msleep */ 3438c2ecf20Sopenharmony_ci if (!rc) 3448c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_DDC_CTRL); 3478c2ecf20Sopenharmony_ci reg_val &= ~HDMI_DDC_CTRL_SOFT_RESET; 3488c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_DDC_CTRL, reg_val); 3498c2ecf20Sopenharmony_ci DBG("After: HDMI_DDC_SW_STATUS=0x%08x", 3508c2ecf20Sopenharmony_ci hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS)); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return rc; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_hw_ddc_clean(struct hdmi_hdcp_ctrl *hdcp_ctrl) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci int rc; 3598c2ecf20Sopenharmony_ci u32 hdcp_ddc_status, ddc_hw_status; 3608c2ecf20Sopenharmony_ci u32 xfer_done, xfer_req, hw_done; 3618c2ecf20Sopenharmony_ci bool hw_not_ready; 3628c2ecf20Sopenharmony_ci u32 timeout_count; 3638c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (hdmi_read(hdmi, REG_HDMI_DDC_HW_STATUS) == 0) 3668c2ecf20Sopenharmony_ci return 0; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* Wait to be clean on DDC HW engine */ 3698c2ecf20Sopenharmony_ci timeout_count = 100; 3708c2ecf20Sopenharmony_ci do { 3718c2ecf20Sopenharmony_ci hdcp_ddc_status = hdmi_read(hdmi, REG_HDMI_HDCP_DDC_STATUS); 3728c2ecf20Sopenharmony_ci ddc_hw_status = hdmi_read(hdmi, REG_HDMI_DDC_HW_STATUS); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci xfer_done = hdcp_ddc_status & HDMI_HDCP_DDC_STATUS_XFER_DONE; 3758c2ecf20Sopenharmony_ci xfer_req = hdcp_ddc_status & HDMI_HDCP_DDC_STATUS_XFER_REQ; 3768c2ecf20Sopenharmony_ci hw_done = ddc_hw_status & HDMI_DDC_HW_STATUS_DONE; 3778c2ecf20Sopenharmony_ci hw_not_ready = !xfer_done || xfer_req || !hw_done; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (hw_not_ready) 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci timeout_count--; 3838c2ecf20Sopenharmony_ci if (!timeout_count) { 3848c2ecf20Sopenharmony_ci pr_warn("%s: hw_ddc_clean failed\n", __func__); 3858c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); 3898c2ecf20Sopenharmony_ci if (rc) 3908c2ecf20Sopenharmony_ci return rc; 3918c2ecf20Sopenharmony_ci } while (1); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic void msm_hdmi_hdcp_reauth_work(struct work_struct *work) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct hdmi_hdcp_ctrl *hdcp_ctrl = container_of(work, 3998c2ecf20Sopenharmony_ci struct hdmi_hdcp_ctrl, hdcp_reauth_work); 4008c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 4018c2ecf20Sopenharmony_ci unsigned long flags; 4028c2ecf20Sopenharmony_ci u32 reg_val; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci DBG("HDCP REAUTH WORK"); 4058c2ecf20Sopenharmony_ci /* 4068c2ecf20Sopenharmony_ci * Disable HPD circuitry. 4078c2ecf20Sopenharmony_ci * This is needed to reset the HDCP cipher engine so that when we 4088c2ecf20Sopenharmony_ci * attempt a re-authentication, HW would clear the AN0_READY and 4098c2ecf20Sopenharmony_ci * AN1_READY bits in HDMI_HDCP_LINK0_STATUS register 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ci spin_lock_irqsave(&hdmi->reg_lock, flags); 4128c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_HPD_CTRL); 4138c2ecf20Sopenharmony_ci reg_val &= ~HDMI_HPD_CTRL_ENABLE; 4148c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HPD_CTRL, reg_val); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* Disable HDCP interrupts */ 4178c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_INT_CTRL, 0); 4188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hdmi->reg_lock, flags); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_RESET, 4218c2ecf20Sopenharmony_ci HDMI_HDCP_RESET_LINK0_DEAUTHENTICATE); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* Wait to be clean on DDC HW engine */ 4248c2ecf20Sopenharmony_ci if (msm_hdmi_hdcp_hw_ddc_clean(hdcp_ctrl)) { 4258c2ecf20Sopenharmony_ci pr_info("%s: reauth work aborted\n", __func__); 4268c2ecf20Sopenharmony_ci return; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* Disable encryption and disable the HDCP block */ 4308c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_CTRL, 0); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* Enable HPD circuitry */ 4338c2ecf20Sopenharmony_ci spin_lock_irqsave(&hdmi->reg_lock, flags); 4348c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_HPD_CTRL); 4358c2ecf20Sopenharmony_ci reg_val |= HDMI_HPD_CTRL_ENABLE; 4368c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HPD_CTRL, reg_val); 4378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hdmi->reg_lock, flags); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* 4408c2ecf20Sopenharmony_ci * Only retry defined times then abort current authenticating process 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_ci if (++hdcp_ctrl->auth_retries == AUTH_RETRIES_TIME) { 4438c2ecf20Sopenharmony_ci hdcp_ctrl->hdcp_state = HDCP_STATE_INACTIVE; 4448c2ecf20Sopenharmony_ci hdcp_ctrl->auth_retries = 0; 4458c2ecf20Sopenharmony_ci pr_info("%s: abort reauthentication!\n", __func__); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci return; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci DBG("Queue AUTH WORK"); 4518c2ecf20Sopenharmony_ci hdcp_ctrl->hdcp_state = HDCP_STATE_AUTHENTICATING; 4528c2ecf20Sopenharmony_ci queue_work(hdmi->workq, &hdcp_ctrl->hdcp_auth_work); 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_auth_prepare(struct hdmi_hdcp_ctrl *hdcp_ctrl) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 4588c2ecf20Sopenharmony_ci u32 link0_status; 4598c2ecf20Sopenharmony_ci u32 reg_val; 4608c2ecf20Sopenharmony_ci unsigned long flags; 4618c2ecf20Sopenharmony_ci int rc; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (!hdcp_ctrl->aksv_valid) { 4648c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_read_validate_aksv(hdcp_ctrl); 4658c2ecf20Sopenharmony_ci if (rc) { 4668c2ecf20Sopenharmony_ci pr_err("%s: ASKV validation failed\n", __func__); 4678c2ecf20Sopenharmony_ci hdcp_ctrl->hdcp_state = HDCP_STATE_NO_AKSV; 4688c2ecf20Sopenharmony_ci return -ENOTSUPP; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci hdcp_ctrl->aksv_valid = true; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci spin_lock_irqsave(&hdmi->reg_lock, flags); 4748c2ecf20Sopenharmony_ci /* disable HDMI Encrypt */ 4758c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); 4768c2ecf20Sopenharmony_ci reg_val &= ~HDMI_CTRL_ENCRYPTED; 4778c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* Enabling Software DDC */ 4808c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_DDC_ARBITRATION); 4818c2ecf20Sopenharmony_ci reg_val &= ~HDMI_DDC_ARBITRATION_HW_ARBITRATION; 4828c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_DDC_ARBITRATION, reg_val); 4838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hdmi->reg_lock, flags); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* 4868c2ecf20Sopenharmony_ci * Write AKSV read from QFPROM to the HDCP registers. 4878c2ecf20Sopenharmony_ci * This step is needed for HDCP authentication and must be 4888c2ecf20Sopenharmony_ci * written before enabling HDCP. 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_SW_LOWER_AKSV, hdcp_ctrl->aksv_lsb); 4918c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_SW_UPPER_AKSV, hdcp_ctrl->aksv_msb); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* 4948c2ecf20Sopenharmony_ci * HDCP setup prior to enabling HDCP_CTRL. 4958c2ecf20Sopenharmony_ci * Setup seed values for random number An. 4968c2ecf20Sopenharmony_ci */ 4978c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_ENTROPY_CTRL0, 0xB1FFB0FF); 4988c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_ENTROPY_CTRL1, 0xF00DFACE); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* Disable the RngCipher state */ 5018c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_HDCP_DEBUG_CTRL); 5028c2ecf20Sopenharmony_ci reg_val &= ~HDMI_HDCP_DEBUG_CTRL_RNG_CIPHER; 5038c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_DEBUG_CTRL, reg_val); 5048c2ecf20Sopenharmony_ci DBG("HDCP_DEBUG_CTRL=0x%08x", 5058c2ecf20Sopenharmony_ci hdmi_read(hdmi, REG_HDMI_HDCP_DEBUG_CTRL)); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci /* 5088c2ecf20Sopenharmony_ci * Ensure that all register writes are completed before 5098c2ecf20Sopenharmony_ci * enabling HDCP cipher 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_ci wmb(); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* 5148c2ecf20Sopenharmony_ci * Enable HDCP 5158c2ecf20Sopenharmony_ci * This needs to be done as early as possible in order for the 5168c2ecf20Sopenharmony_ci * hardware to make An available to read 5178c2ecf20Sopenharmony_ci */ 5188c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_CTRL, HDMI_HDCP_CTRL_ENABLE); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci /* 5218c2ecf20Sopenharmony_ci * If we had stale values for the An ready bit, it should most 5228c2ecf20Sopenharmony_ci * likely be cleared now after enabling HDCP cipher 5238c2ecf20Sopenharmony_ci */ 5248c2ecf20Sopenharmony_ci link0_status = hdmi_read(hdmi, REG_HDMI_HDCP_LINK0_STATUS); 5258c2ecf20Sopenharmony_ci DBG("After enabling HDCP Link0_Status=0x%08x", link0_status); 5268c2ecf20Sopenharmony_ci if (!(link0_status & 5278c2ecf20Sopenharmony_ci (HDMI_HDCP_LINK0_STATUS_AN_0_READY | 5288c2ecf20Sopenharmony_ci HDMI_HDCP_LINK0_STATUS_AN_1_READY))) 5298c2ecf20Sopenharmony_ci DBG("An not ready after enabling HDCP"); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* Clear any DDC failures from previous tries before enable HDCP*/ 5328c2ecf20Sopenharmony_ci rc = msm_reset_hdcp_ddc_failures(hdcp_ctrl); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci return rc; 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic void msm_hdmi_hdcp_auth_fail(struct hdmi_hdcp_ctrl *hdcp_ctrl) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 5408c2ecf20Sopenharmony_ci u32 reg_val; 5418c2ecf20Sopenharmony_ci unsigned long flags; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci DBG("hdcp auth failed, queue reauth work"); 5448c2ecf20Sopenharmony_ci /* clear HDMI Encrypt */ 5458c2ecf20Sopenharmony_ci spin_lock_irqsave(&hdmi->reg_lock, flags); 5468c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); 5478c2ecf20Sopenharmony_ci reg_val &= ~HDMI_CTRL_ENCRYPTED; 5488c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); 5498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hdmi->reg_lock, flags); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci hdcp_ctrl->hdcp_state = HDCP_STATE_AUTH_FAILED; 5528c2ecf20Sopenharmony_ci queue_work(hdmi->workq, &hdcp_ctrl->hdcp_reauth_work); 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic void msm_hdmi_hdcp_auth_done(struct hdmi_hdcp_ctrl *hdcp_ctrl) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 5588c2ecf20Sopenharmony_ci u32 reg_val; 5598c2ecf20Sopenharmony_ci unsigned long flags; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* 5628c2ecf20Sopenharmony_ci * Disable software DDC before going into part3 to make sure 5638c2ecf20Sopenharmony_ci * there is no Arbitration between software and hardware for DDC 5648c2ecf20Sopenharmony_ci */ 5658c2ecf20Sopenharmony_ci spin_lock_irqsave(&hdmi->reg_lock, flags); 5668c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_DDC_ARBITRATION); 5678c2ecf20Sopenharmony_ci reg_val |= HDMI_DDC_ARBITRATION_HW_ARBITRATION; 5688c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_DDC_ARBITRATION, reg_val); 5698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hdmi->reg_lock, flags); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* enable HDMI Encrypt */ 5728c2ecf20Sopenharmony_ci spin_lock_irqsave(&hdmi->reg_lock, flags); 5738c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); 5748c2ecf20Sopenharmony_ci reg_val |= HDMI_CTRL_ENCRYPTED; 5758c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); 5768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hdmi->reg_lock, flags); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci hdcp_ctrl->hdcp_state = HDCP_STATE_AUTHENTICATED; 5798c2ecf20Sopenharmony_ci hdcp_ctrl->auth_retries = 0; 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci/* 5838c2ecf20Sopenharmony_ci * hdcp authenticating part 1 5848c2ecf20Sopenharmony_ci * Wait Key/An ready 5858c2ecf20Sopenharmony_ci * Read BCAPS from sink 5868c2ecf20Sopenharmony_ci * Write BCAPS and AKSV into HDCP engine 5878c2ecf20Sopenharmony_ci * Write An and AKSV to sink 5888c2ecf20Sopenharmony_ci * Read BKSV from sink and write into HDCP engine 5898c2ecf20Sopenharmony_ci */ 5908c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_wait_key_an_ready(struct hdmi_hdcp_ctrl *hdcp_ctrl) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci int rc; 5938c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 5948c2ecf20Sopenharmony_ci u32 link0_status, keys_state; 5958c2ecf20Sopenharmony_ci u32 timeout_count; 5968c2ecf20Sopenharmony_ci bool an_ready; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci /* Wait for HDCP keys to be checked and validated */ 5998c2ecf20Sopenharmony_ci timeout_count = 100; 6008c2ecf20Sopenharmony_ci do { 6018c2ecf20Sopenharmony_ci link0_status = hdmi_read(hdmi, REG_HDMI_HDCP_LINK0_STATUS); 6028c2ecf20Sopenharmony_ci keys_state = (link0_status >> 28) & 0x7; 6038c2ecf20Sopenharmony_ci if (keys_state == HDCP_KEYS_STATE_VALID) 6048c2ecf20Sopenharmony_ci break; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci DBG("Keys not ready(%d). s=%d, l0=%0x08x", 6078c2ecf20Sopenharmony_ci timeout_count, keys_state, link0_status); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci timeout_count--; 6108c2ecf20Sopenharmony_ci if (!timeout_count) { 6118c2ecf20Sopenharmony_ci pr_err("%s: Wait key state timedout", __func__); 6128c2ecf20Sopenharmony_ci return -ETIMEDOUT; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); 6168c2ecf20Sopenharmony_ci if (rc) 6178c2ecf20Sopenharmony_ci return rc; 6188c2ecf20Sopenharmony_ci } while (1); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci timeout_count = 100; 6218c2ecf20Sopenharmony_ci do { 6228c2ecf20Sopenharmony_ci link0_status = hdmi_read(hdmi, REG_HDMI_HDCP_LINK0_STATUS); 6238c2ecf20Sopenharmony_ci an_ready = (link0_status & HDMI_HDCP_LINK0_STATUS_AN_0_READY) 6248c2ecf20Sopenharmony_ci && (link0_status & HDMI_HDCP_LINK0_STATUS_AN_1_READY); 6258c2ecf20Sopenharmony_ci if (an_ready) 6268c2ecf20Sopenharmony_ci break; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci DBG("An not ready(%d). l0_status=0x%08x", 6298c2ecf20Sopenharmony_ci timeout_count, link0_status); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci timeout_count--; 6328c2ecf20Sopenharmony_ci if (!timeout_count) { 6338c2ecf20Sopenharmony_ci pr_err("%s: Wait An timedout", __func__); 6348c2ecf20Sopenharmony_ci return -ETIMEDOUT; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); 6388c2ecf20Sopenharmony_ci if (rc) 6398c2ecf20Sopenharmony_ci return rc; 6408c2ecf20Sopenharmony_ci } while (1); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci return 0; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_send_aksv_an(struct hdmi_hdcp_ctrl *hdcp_ctrl) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci int rc = 0; 6488c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 6498c2ecf20Sopenharmony_ci u32 link0_aksv_0, link0_aksv_1; 6508c2ecf20Sopenharmony_ci u32 link0_an[2]; 6518c2ecf20Sopenharmony_ci u8 aksv[5]; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci /* Read An0 and An1 */ 6548c2ecf20Sopenharmony_ci link0_an[0] = hdmi_read(hdmi, REG_HDMI_HDCP_RCVPORT_DATA5); 6558c2ecf20Sopenharmony_ci link0_an[1] = hdmi_read(hdmi, REG_HDMI_HDCP_RCVPORT_DATA6); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci /* Read AKSV */ 6588c2ecf20Sopenharmony_ci link0_aksv_0 = hdmi_read(hdmi, REG_HDMI_HDCP_RCVPORT_DATA3); 6598c2ecf20Sopenharmony_ci link0_aksv_1 = hdmi_read(hdmi, REG_HDMI_HDCP_RCVPORT_DATA4); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci DBG("Link ASKV=%08x%08x", link0_aksv_0, link0_aksv_1); 6628c2ecf20Sopenharmony_ci /* Copy An and AKSV to byte arrays for transmission */ 6638c2ecf20Sopenharmony_ci aksv[0] = link0_aksv_0 & 0xFF; 6648c2ecf20Sopenharmony_ci aksv[1] = (link0_aksv_0 >> 8) & 0xFF; 6658c2ecf20Sopenharmony_ci aksv[2] = (link0_aksv_0 >> 16) & 0xFF; 6668c2ecf20Sopenharmony_ci aksv[3] = (link0_aksv_0 >> 24) & 0xFF; 6678c2ecf20Sopenharmony_ci aksv[4] = link0_aksv_1 & 0xFF; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci /* Write An to offset 0x18 */ 6708c2ecf20Sopenharmony_ci rc = msm_hdmi_ddc_write(hdmi, HDCP_PORT_ADDR, 0x18, (u8 *)link0_an, 6718c2ecf20Sopenharmony_ci (u16)sizeof(link0_an)); 6728c2ecf20Sopenharmony_ci if (rc) { 6738c2ecf20Sopenharmony_ci pr_err("%s:An write failed\n", __func__); 6748c2ecf20Sopenharmony_ci return rc; 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci DBG("Link0-An=%08x%08x", link0_an[0], link0_an[1]); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* Write AKSV to offset 0x10 */ 6798c2ecf20Sopenharmony_ci rc = msm_hdmi_ddc_write(hdmi, HDCP_PORT_ADDR, 0x10, aksv, 5); 6808c2ecf20Sopenharmony_ci if (rc) { 6818c2ecf20Sopenharmony_ci pr_err("%s:AKSV write failed\n", __func__); 6828c2ecf20Sopenharmony_ci return rc; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci DBG("Link0-AKSV=%02x%08x", link0_aksv_1 & 0xFF, link0_aksv_0); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci return 0; 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_recv_bksv(struct hdmi_hdcp_ctrl *hdcp_ctrl) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci int rc = 0; 6928c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 6938c2ecf20Sopenharmony_ci u8 bksv[5]; 6948c2ecf20Sopenharmony_ci u32 reg[2], data[2]; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci /* Read BKSV at offset 0x00 */ 6978c2ecf20Sopenharmony_ci rc = msm_hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x00, bksv, 5); 6988c2ecf20Sopenharmony_ci if (rc) { 6998c2ecf20Sopenharmony_ci pr_err("%s:BKSV read failed\n", __func__); 7008c2ecf20Sopenharmony_ci return rc; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci hdcp_ctrl->bksv_lsb = bksv[0] | (bksv[1] << 8) | 7048c2ecf20Sopenharmony_ci (bksv[2] << 16) | (bksv[3] << 24); 7058c2ecf20Sopenharmony_ci hdcp_ctrl->bksv_msb = bksv[4]; 7068c2ecf20Sopenharmony_ci DBG(":BKSV=%02x%08x", hdcp_ctrl->bksv_msb, hdcp_ctrl->bksv_lsb); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* check there are 20 ones in BKSV */ 7098c2ecf20Sopenharmony_ci if ((hweight32(hdcp_ctrl->bksv_lsb) + hweight32(hdcp_ctrl->bksv_msb)) 7108c2ecf20Sopenharmony_ci != 20) { 7118c2ecf20Sopenharmony_ci pr_err(": BKSV doesn't have 20 1's and 20 0's\n"); 7128c2ecf20Sopenharmony_ci pr_err(": BKSV chk fail. BKSV=%02x%02x%02x%02x%02x\n", 7138c2ecf20Sopenharmony_ci bksv[4], bksv[3], bksv[2], bksv[1], bksv[0]); 7148c2ecf20Sopenharmony_ci return -EINVAL; 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci /* Write BKSV read from sink to HDCP registers */ 7188c2ecf20Sopenharmony_ci reg[0] = REG_HDMI_HDCP_RCVPORT_DATA0; 7198c2ecf20Sopenharmony_ci data[0] = hdcp_ctrl->bksv_lsb; 7208c2ecf20Sopenharmony_ci reg[1] = REG_HDMI_HDCP_RCVPORT_DATA1; 7218c2ecf20Sopenharmony_ci data[1] = hdcp_ctrl->bksv_msb; 7228c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_scm_wr(hdcp_ctrl, reg, data, 2); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci return rc; 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_recv_bcaps(struct hdmi_hdcp_ctrl *hdcp_ctrl) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci int rc = 0; 7308c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 7318c2ecf20Sopenharmony_ci u32 reg, data; 7328c2ecf20Sopenharmony_ci u8 bcaps; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci rc = msm_hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x40, &bcaps, 1); 7358c2ecf20Sopenharmony_ci if (rc) { 7368c2ecf20Sopenharmony_ci pr_err("%s:BCAPS read failed\n", __func__); 7378c2ecf20Sopenharmony_ci return rc; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci DBG("BCAPS=%02x", bcaps); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci /* receiver (0), repeater (1) */ 7428c2ecf20Sopenharmony_ci hdcp_ctrl->ds_type = (bcaps & BIT(6)) ? DS_REPEATER : DS_RECEIVER; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* Write BCAPS to the hardware */ 7458c2ecf20Sopenharmony_ci reg = REG_HDMI_HDCP_RCVPORT_DATA12; 7468c2ecf20Sopenharmony_ci data = (u32)bcaps; 7478c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_scm_wr(hdcp_ctrl, ®, &data, 1); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci return rc; 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_auth_part1_key_exchange(struct hdmi_hdcp_ctrl *hdcp_ctrl) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 7558c2ecf20Sopenharmony_ci unsigned long flags; 7568c2ecf20Sopenharmony_ci int rc; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci /* Wait for AKSV key and An ready */ 7598c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_wait_key_an_ready(hdcp_ctrl); 7608c2ecf20Sopenharmony_ci if (rc) { 7618c2ecf20Sopenharmony_ci pr_err("%s: wait key and an ready failed\n", __func__); 7628c2ecf20Sopenharmony_ci return rc; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci /* Read BCAPS and send to HDCP engine */ 7668c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_recv_bcaps(hdcp_ctrl); 7678c2ecf20Sopenharmony_ci if (rc) { 7688c2ecf20Sopenharmony_ci pr_err("%s: read bcaps error, abort\n", __func__); 7698c2ecf20Sopenharmony_ci return rc; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci /* 7738c2ecf20Sopenharmony_ci * 1.1_Features turned off by default. 7748c2ecf20Sopenharmony_ci * No need to write AInfo since 1.1_Features is disabled. 7758c2ecf20Sopenharmony_ci */ 7768c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_RCVPORT_DATA4, 0); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci /* Send AKSV and An to sink */ 7798c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_send_aksv_an(hdcp_ctrl); 7808c2ecf20Sopenharmony_ci if (rc) { 7818c2ecf20Sopenharmony_ci pr_err("%s:An/Aksv write failed\n", __func__); 7828c2ecf20Sopenharmony_ci return rc; 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci /* Read BKSV and send to HDCP engine*/ 7868c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_recv_bksv(hdcp_ctrl); 7878c2ecf20Sopenharmony_ci if (rc) { 7888c2ecf20Sopenharmony_ci pr_err("%s:BKSV Process failed\n", __func__); 7898c2ecf20Sopenharmony_ci return rc; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci /* Enable HDCP interrupts and ack/clear any stale interrupts */ 7938c2ecf20Sopenharmony_ci spin_lock_irqsave(&hdmi->reg_lock, flags); 7948c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_INT_CTRL, 7958c2ecf20Sopenharmony_ci HDMI_HDCP_INT_CTRL_AUTH_SUCCESS_ACK | 7968c2ecf20Sopenharmony_ci HDMI_HDCP_INT_CTRL_AUTH_SUCCESS_MASK | 7978c2ecf20Sopenharmony_ci HDMI_HDCP_INT_CTRL_AUTH_FAIL_ACK | 7988c2ecf20Sopenharmony_ci HDMI_HDCP_INT_CTRL_AUTH_FAIL_MASK | 7998c2ecf20Sopenharmony_ci HDMI_HDCP_INT_CTRL_AUTH_FAIL_INFO_ACK); 8008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hdmi->reg_lock, flags); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci return 0; 8038c2ecf20Sopenharmony_ci} 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci/* read R0' from sink and pass it to HDCP engine */ 8068c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_auth_part1_recv_r0(struct hdmi_hdcp_ctrl *hdcp_ctrl) 8078c2ecf20Sopenharmony_ci{ 8088c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 8098c2ecf20Sopenharmony_ci int rc = 0; 8108c2ecf20Sopenharmony_ci u8 buf[2]; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci /* 8138c2ecf20Sopenharmony_ci * HDCP Compliance Test case 1A-01: 8148c2ecf20Sopenharmony_ci * Wait here at least 100ms before reading R0' 8158c2ecf20Sopenharmony_ci */ 8168c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 125, AUTH_ABORT_EV); 8178c2ecf20Sopenharmony_ci if (rc) 8188c2ecf20Sopenharmony_ci return rc; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci /* Read R0' at offset 0x08 */ 8218c2ecf20Sopenharmony_ci rc = msm_hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x08, buf, 2); 8228c2ecf20Sopenharmony_ci if (rc) { 8238c2ecf20Sopenharmony_ci pr_err("%s:R0' read failed\n", __func__); 8248c2ecf20Sopenharmony_ci return rc; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci DBG("R0'=%02x%02x", buf[1], buf[0]); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci /* Write R0' to HDCP registers and check to see if it is a match */ 8298c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_RCVPORT_DATA2_0, 8308c2ecf20Sopenharmony_ci (((u32)buf[1]) << 8) | buf[0]); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci return 0; 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci/* Wait for authenticating result: R0/R0' are matched or not */ 8368c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_auth_part1_verify_r0(struct hdmi_hdcp_ctrl *hdcp_ctrl) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 8398c2ecf20Sopenharmony_ci u32 link0_status; 8408c2ecf20Sopenharmony_ci int rc; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci /* wait for hdcp irq, 10 sec should be long enough */ 8438c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 10000, AUTH_RESULT_RDY_EV); 8448c2ecf20Sopenharmony_ci if (!rc) { 8458c2ecf20Sopenharmony_ci pr_err("%s: Wait Auth IRQ timeout\n", __func__); 8468c2ecf20Sopenharmony_ci return -ETIMEDOUT; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci link0_status = hdmi_read(hdmi, REG_HDMI_HDCP_LINK0_STATUS); 8508c2ecf20Sopenharmony_ci if (!(link0_status & HDMI_HDCP_LINK0_STATUS_RI_MATCHES)) { 8518c2ecf20Sopenharmony_ci pr_err("%s: Authentication Part I failed\n", __func__); 8528c2ecf20Sopenharmony_ci return -EINVAL; 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci /* Enable HDCP Encryption */ 8568c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_CTRL, 8578c2ecf20Sopenharmony_ci HDMI_HDCP_CTRL_ENABLE | 8588c2ecf20Sopenharmony_ci HDMI_HDCP_CTRL_ENCRYPTION_ENABLE); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci return 0; 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_recv_check_bstatus(struct hdmi_hdcp_ctrl *hdcp_ctrl, 8648c2ecf20Sopenharmony_ci u16 *pbstatus) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci int rc; 8678c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 8688c2ecf20Sopenharmony_ci bool max_devs_exceeded = false, max_cascade_exceeded = false; 8698c2ecf20Sopenharmony_ci u32 repeater_cascade_depth = 0, down_stream_devices = 0; 8708c2ecf20Sopenharmony_ci u16 bstatus; 8718c2ecf20Sopenharmony_ci u8 buf[2]; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci /* Read BSTATUS at offset 0x41 */ 8748c2ecf20Sopenharmony_ci rc = msm_hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x41, buf, 2); 8758c2ecf20Sopenharmony_ci if (rc) { 8768c2ecf20Sopenharmony_ci pr_err("%s: BSTATUS read failed\n", __func__); 8778c2ecf20Sopenharmony_ci goto error; 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci *pbstatus = bstatus = (buf[1] << 8) | buf[0]; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci down_stream_devices = bstatus & 0x7F; 8838c2ecf20Sopenharmony_ci repeater_cascade_depth = (bstatus >> 8) & 0x7; 8848c2ecf20Sopenharmony_ci max_devs_exceeded = (bstatus & BIT(7)) ? true : false; 8858c2ecf20Sopenharmony_ci max_cascade_exceeded = (bstatus & BIT(11)) ? true : false; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (down_stream_devices == 0) { 8888c2ecf20Sopenharmony_ci /* 8898c2ecf20Sopenharmony_ci * If no downstream devices are attached to the repeater 8908c2ecf20Sopenharmony_ci * then part II fails. 8918c2ecf20Sopenharmony_ci * todo: The other approach would be to continue PART II. 8928c2ecf20Sopenharmony_ci */ 8938c2ecf20Sopenharmony_ci pr_err("%s: No downstream devices\n", __func__); 8948c2ecf20Sopenharmony_ci rc = -EINVAL; 8958c2ecf20Sopenharmony_ci goto error; 8968c2ecf20Sopenharmony_ci } 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci /* 8998c2ecf20Sopenharmony_ci * HDCP Compliance 1B-05: 9008c2ecf20Sopenharmony_ci * Check if no. of devices connected to repeater 9018c2ecf20Sopenharmony_ci * exceed max_devices_connected from bit 7 of Bstatus. 9028c2ecf20Sopenharmony_ci */ 9038c2ecf20Sopenharmony_ci if (max_devs_exceeded) { 9048c2ecf20Sopenharmony_ci pr_err("%s: no. of devs connected exceeds max allowed", 9058c2ecf20Sopenharmony_ci __func__); 9068c2ecf20Sopenharmony_ci rc = -EINVAL; 9078c2ecf20Sopenharmony_ci goto error; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* 9118c2ecf20Sopenharmony_ci * HDCP Compliance 1B-06: 9128c2ecf20Sopenharmony_ci * Check if no. of cascade connected to repeater 9138c2ecf20Sopenharmony_ci * exceed max_cascade_connected from bit 11 of Bstatus. 9148c2ecf20Sopenharmony_ci */ 9158c2ecf20Sopenharmony_ci if (max_cascade_exceeded) { 9168c2ecf20Sopenharmony_ci pr_err("%s: no. of cascade conn exceeds max allowed", 9178c2ecf20Sopenharmony_ci __func__); 9188c2ecf20Sopenharmony_ci rc = -EINVAL; 9198c2ecf20Sopenharmony_ci goto error; 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_cierror: 9238c2ecf20Sopenharmony_ci hdcp_ctrl->dev_count = down_stream_devices; 9248c2ecf20Sopenharmony_ci hdcp_ctrl->max_cascade_exceeded = max_cascade_exceeded; 9258c2ecf20Sopenharmony_ci hdcp_ctrl->max_dev_exceeded = max_devs_exceeded; 9268c2ecf20Sopenharmony_ci hdcp_ctrl->depth = repeater_cascade_depth; 9278c2ecf20Sopenharmony_ci return rc; 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_auth_part2_wait_ksv_fifo_ready( 9318c2ecf20Sopenharmony_ci struct hdmi_hdcp_ctrl *hdcp_ctrl) 9328c2ecf20Sopenharmony_ci{ 9338c2ecf20Sopenharmony_ci int rc; 9348c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 9358c2ecf20Sopenharmony_ci u32 reg, data; 9368c2ecf20Sopenharmony_ci u32 timeout_count; 9378c2ecf20Sopenharmony_ci u16 bstatus; 9388c2ecf20Sopenharmony_ci u8 bcaps; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci /* 9418c2ecf20Sopenharmony_ci * Wait until READY bit is set in BCAPS, as per HDCP specifications 9428c2ecf20Sopenharmony_ci * maximum permitted time to check for READY bit is five seconds. 9438c2ecf20Sopenharmony_ci */ 9448c2ecf20Sopenharmony_ci timeout_count = 100; 9458c2ecf20Sopenharmony_ci do { 9468c2ecf20Sopenharmony_ci /* Read BCAPS at offset 0x40 */ 9478c2ecf20Sopenharmony_ci rc = msm_hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x40, &bcaps, 1); 9488c2ecf20Sopenharmony_ci if (rc) { 9498c2ecf20Sopenharmony_ci pr_err("%s: BCAPS read failed\n", __func__); 9508c2ecf20Sopenharmony_ci return rc; 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci if (bcaps & BIT(5)) 9548c2ecf20Sopenharmony_ci break; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci timeout_count--; 9578c2ecf20Sopenharmony_ci if (!timeout_count) { 9588c2ecf20Sopenharmony_ci pr_err("%s: Wait KSV fifo ready timedout", __func__); 9598c2ecf20Sopenharmony_ci return -ETIMEDOUT; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); 9638c2ecf20Sopenharmony_ci if (rc) 9648c2ecf20Sopenharmony_ci return rc; 9658c2ecf20Sopenharmony_ci } while (1); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_recv_check_bstatus(hdcp_ctrl, &bstatus); 9688c2ecf20Sopenharmony_ci if (rc) { 9698c2ecf20Sopenharmony_ci pr_err("%s: bstatus error\n", __func__); 9708c2ecf20Sopenharmony_ci return rc; 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci /* Write BSTATUS and BCAPS to HDCP registers */ 9748c2ecf20Sopenharmony_ci reg = REG_HDMI_HDCP_RCVPORT_DATA12; 9758c2ecf20Sopenharmony_ci data = bcaps | (bstatus << 8); 9768c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_scm_wr(hdcp_ctrl, ®, &data, 1); 9778c2ecf20Sopenharmony_ci if (rc) { 9788c2ecf20Sopenharmony_ci pr_err("%s: BSTATUS write failed\n", __func__); 9798c2ecf20Sopenharmony_ci return rc; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci return 0; 9838c2ecf20Sopenharmony_ci} 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci/* 9868c2ecf20Sopenharmony_ci * hdcp authenticating part 2: 2nd 9878c2ecf20Sopenharmony_ci * read ksv fifo from sink 9888c2ecf20Sopenharmony_ci * transfer V' from sink to HDCP engine 9898c2ecf20Sopenharmony_ci * reset SHA engine 9908c2ecf20Sopenharmony_ci */ 9918c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_transfer_v_h(struct hdmi_hdcp_ctrl *hdcp_ctrl) 9928c2ecf20Sopenharmony_ci{ 9938c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 9948c2ecf20Sopenharmony_ci int rc = 0; 9958c2ecf20Sopenharmony_ci struct hdmi_hdcp_reg_data reg_data[] = { 9968c2ecf20Sopenharmony_ci {REG_HDMI_HDCP_RCVPORT_DATA7, 0x20, "V' H0"}, 9978c2ecf20Sopenharmony_ci {REG_HDMI_HDCP_RCVPORT_DATA8, 0x24, "V' H1"}, 9988c2ecf20Sopenharmony_ci {REG_HDMI_HDCP_RCVPORT_DATA9, 0x28, "V' H2"}, 9998c2ecf20Sopenharmony_ci {REG_HDMI_HDCP_RCVPORT_DATA10, 0x2C, "V' H3"}, 10008c2ecf20Sopenharmony_ci {REG_HDMI_HDCP_RCVPORT_DATA11, 0x30, "V' H4"}, 10018c2ecf20Sopenharmony_ci }; 10028c2ecf20Sopenharmony_ci struct hdmi_hdcp_reg_data *rd; 10038c2ecf20Sopenharmony_ci u32 size = ARRAY_SIZE(reg_data); 10048c2ecf20Sopenharmony_ci u32 reg[ARRAY_SIZE(reg_data)]; 10058c2ecf20Sopenharmony_ci u32 data[ARRAY_SIZE(reg_data)]; 10068c2ecf20Sopenharmony_ci int i; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 10098c2ecf20Sopenharmony_ci rd = ®_data[i]; 10108c2ecf20Sopenharmony_ci rc = msm_hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 10118c2ecf20Sopenharmony_ci rd->off, (u8 *)&data[i], (u16)sizeof(data[i])); 10128c2ecf20Sopenharmony_ci if (rc) { 10138c2ecf20Sopenharmony_ci pr_err("%s: Read %s failed\n", __func__, rd->name); 10148c2ecf20Sopenharmony_ci goto error; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci DBG("%s =%x", rd->name, data[i]); 10188c2ecf20Sopenharmony_ci reg[i] = reg_data[i].reg_id; 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_scm_wr(hdcp_ctrl, reg, data, size); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_cierror: 10248c2ecf20Sopenharmony_ci return rc; 10258c2ecf20Sopenharmony_ci} 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_recv_ksv_fifo(struct hdmi_hdcp_ctrl *hdcp_ctrl) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci int rc; 10308c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 10318c2ecf20Sopenharmony_ci u32 ksv_bytes; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci ksv_bytes = 5 * hdcp_ctrl->dev_count; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci rc = msm_hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x43, 10368c2ecf20Sopenharmony_ci hdcp_ctrl->ksv_list, ksv_bytes); 10378c2ecf20Sopenharmony_ci if (rc) 10388c2ecf20Sopenharmony_ci pr_err("%s: KSV FIFO read failed\n", __func__); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci return rc; 10418c2ecf20Sopenharmony_ci} 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_reset_sha_engine(struct hdmi_hdcp_ctrl *hdcp_ctrl) 10448c2ecf20Sopenharmony_ci{ 10458c2ecf20Sopenharmony_ci u32 reg[2], data[2]; 10468c2ecf20Sopenharmony_ci u32 rc = 0; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci reg[0] = REG_HDMI_HDCP_SHA_CTRL; 10498c2ecf20Sopenharmony_ci data[0] = HDCP_REG_ENABLE; 10508c2ecf20Sopenharmony_ci reg[1] = REG_HDMI_HDCP_SHA_CTRL; 10518c2ecf20Sopenharmony_ci data[1] = HDCP_REG_DISABLE; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_scm_wr(hdcp_ctrl, reg, data, 2); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci return rc; 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_auth_part2_recv_ksv_fifo( 10598c2ecf20Sopenharmony_ci struct hdmi_hdcp_ctrl *hdcp_ctrl) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci int rc; 10628c2ecf20Sopenharmony_ci u32 timeout_count; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci /* 10658c2ecf20Sopenharmony_ci * Read KSV FIFO over DDC 10668c2ecf20Sopenharmony_ci * Key Selection vector FIFO Used to pull downstream KSVs 10678c2ecf20Sopenharmony_ci * from HDCP Repeaters. 10688c2ecf20Sopenharmony_ci * All bytes (DEVICE_COUNT * 5) must be read in a single, 10698c2ecf20Sopenharmony_ci * auto incrementing access. 10708c2ecf20Sopenharmony_ci * All bytes read as 0x00 for HDCP Receivers that are not 10718c2ecf20Sopenharmony_ci * HDCP Repeaters (REPEATER == 0). 10728c2ecf20Sopenharmony_ci */ 10738c2ecf20Sopenharmony_ci timeout_count = 100; 10748c2ecf20Sopenharmony_ci do { 10758c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_recv_ksv_fifo(hdcp_ctrl); 10768c2ecf20Sopenharmony_ci if (!rc) 10778c2ecf20Sopenharmony_ci break; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci timeout_count--; 10808c2ecf20Sopenharmony_ci if (!timeout_count) { 10818c2ecf20Sopenharmony_ci pr_err("%s: Recv ksv fifo timedout", __func__); 10828c2ecf20Sopenharmony_ci return -ETIMEDOUT; 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 25, AUTH_ABORT_EV); 10868c2ecf20Sopenharmony_ci if (rc) 10878c2ecf20Sopenharmony_ci return rc; 10888c2ecf20Sopenharmony_ci } while (1); 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_transfer_v_h(hdcp_ctrl); 10918c2ecf20Sopenharmony_ci if (rc) { 10928c2ecf20Sopenharmony_ci pr_err("%s: transfer V failed\n", __func__); 10938c2ecf20Sopenharmony_ci return rc; 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci /* reset SHA engine before write ksv fifo */ 10978c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_reset_sha_engine(hdcp_ctrl); 10988c2ecf20Sopenharmony_ci if (rc) { 10998c2ecf20Sopenharmony_ci pr_err("%s: fail to reset sha engine\n", __func__); 11008c2ecf20Sopenharmony_ci return rc; 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci return 0; 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci/* 11078c2ecf20Sopenharmony_ci * Write KSV FIFO to HDCP_SHA_DATA. 11088c2ecf20Sopenharmony_ci * This is done 1 byte at time starting with the LSB. 11098c2ecf20Sopenharmony_ci * Once 64 bytes have been written, we need to poll for 11108c2ecf20Sopenharmony_ci * HDCP_SHA_BLOCK_DONE before writing any further 11118c2ecf20Sopenharmony_ci * If the last byte is written, we need to poll for 11128c2ecf20Sopenharmony_ci * HDCP_SHA_COMP_DONE to wait until HW finish 11138c2ecf20Sopenharmony_ci */ 11148c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_write_ksv_fifo(struct hdmi_hdcp_ctrl *hdcp_ctrl) 11158c2ecf20Sopenharmony_ci{ 11168c2ecf20Sopenharmony_ci int i; 11178c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 11188c2ecf20Sopenharmony_ci u32 ksv_bytes, last_byte = 0; 11198c2ecf20Sopenharmony_ci u8 *ksv_fifo = NULL; 11208c2ecf20Sopenharmony_ci u32 reg_val, data, reg; 11218c2ecf20Sopenharmony_ci u32 rc = 0; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci ksv_bytes = 5 * hdcp_ctrl->dev_count; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci /* Check if need to wait for HW completion */ 11268c2ecf20Sopenharmony_ci if (hdcp_ctrl->ksv_fifo_w_index) { 11278c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_HDCP_SHA_STATUS); 11288c2ecf20Sopenharmony_ci DBG("HDCP_SHA_STATUS=%08x", reg_val); 11298c2ecf20Sopenharmony_ci if (hdcp_ctrl->ksv_fifo_w_index == ksv_bytes) { 11308c2ecf20Sopenharmony_ci /* check COMP_DONE if last write */ 11318c2ecf20Sopenharmony_ci if (reg_val & HDMI_HDCP_SHA_STATUS_COMP_DONE) { 11328c2ecf20Sopenharmony_ci DBG("COMP_DONE"); 11338c2ecf20Sopenharmony_ci return 0; 11348c2ecf20Sopenharmony_ci } else { 11358c2ecf20Sopenharmony_ci return -EAGAIN; 11368c2ecf20Sopenharmony_ci } 11378c2ecf20Sopenharmony_ci } else { 11388c2ecf20Sopenharmony_ci /* check BLOCK_DONE if not last write */ 11398c2ecf20Sopenharmony_ci if (!(reg_val & HDMI_HDCP_SHA_STATUS_BLOCK_DONE)) 11408c2ecf20Sopenharmony_ci return -EAGAIN; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci DBG("BLOCK_DONE"); 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci ksv_bytes -= hdcp_ctrl->ksv_fifo_w_index; 11478c2ecf20Sopenharmony_ci if (ksv_bytes <= 64) 11488c2ecf20Sopenharmony_ci last_byte = 1; 11498c2ecf20Sopenharmony_ci else 11508c2ecf20Sopenharmony_ci ksv_bytes = 64; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci ksv_fifo = hdcp_ctrl->ksv_list; 11538c2ecf20Sopenharmony_ci ksv_fifo += hdcp_ctrl->ksv_fifo_w_index; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci for (i = 0; i < ksv_bytes; i++) { 11568c2ecf20Sopenharmony_ci /* Write KSV byte and set DONE bit[0] for last byte*/ 11578c2ecf20Sopenharmony_ci reg_val = ksv_fifo[i] << 16; 11588c2ecf20Sopenharmony_ci if ((i == (ksv_bytes - 1)) && last_byte) 11598c2ecf20Sopenharmony_ci reg_val |= HDMI_HDCP_SHA_DATA_DONE; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci reg = REG_HDMI_HDCP_SHA_DATA; 11628c2ecf20Sopenharmony_ci data = reg_val; 11638c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_scm_wr(hdcp_ctrl, ®, &data, 1); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci if (rc) 11668c2ecf20Sopenharmony_ci return rc; 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci hdcp_ctrl->ksv_fifo_w_index += ksv_bytes; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci /* 11728c2ecf20Sopenharmony_ci *return -EAGAIN to notify caller to wait for COMP_DONE or BLOCK_DONE 11738c2ecf20Sopenharmony_ci */ 11748c2ecf20Sopenharmony_ci return -EAGAIN; 11758c2ecf20Sopenharmony_ci} 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci/* write ksv fifo into HDCP engine */ 11788c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_auth_part2_write_ksv_fifo( 11798c2ecf20Sopenharmony_ci struct hdmi_hdcp_ctrl *hdcp_ctrl) 11808c2ecf20Sopenharmony_ci{ 11818c2ecf20Sopenharmony_ci int rc; 11828c2ecf20Sopenharmony_ci u32 timeout_count; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci hdcp_ctrl->ksv_fifo_w_index = 0; 11858c2ecf20Sopenharmony_ci timeout_count = 100; 11868c2ecf20Sopenharmony_ci do { 11878c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_write_ksv_fifo(hdcp_ctrl); 11888c2ecf20Sopenharmony_ci if (!rc) 11898c2ecf20Sopenharmony_ci break; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci if (rc != -EAGAIN) 11928c2ecf20Sopenharmony_ci return rc; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci timeout_count--; 11958c2ecf20Sopenharmony_ci if (!timeout_count) { 11968c2ecf20Sopenharmony_ci pr_err("%s: Write KSV fifo timedout", __func__); 11978c2ecf20Sopenharmony_ci return -ETIMEDOUT; 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); 12018c2ecf20Sopenharmony_ci if (rc) 12028c2ecf20Sopenharmony_ci return rc; 12038c2ecf20Sopenharmony_ci } while (1); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci return 0; 12068c2ecf20Sopenharmony_ci} 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_cistatic int msm_hdmi_hdcp_auth_part2_check_v_match(struct hdmi_hdcp_ctrl *hdcp_ctrl) 12098c2ecf20Sopenharmony_ci{ 12108c2ecf20Sopenharmony_ci int rc = 0; 12118c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 12128c2ecf20Sopenharmony_ci u32 link0_status; 12138c2ecf20Sopenharmony_ci u32 timeout_count = 100; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci do { 12168c2ecf20Sopenharmony_ci link0_status = hdmi_read(hdmi, REG_HDMI_HDCP_LINK0_STATUS); 12178c2ecf20Sopenharmony_ci if (link0_status & HDMI_HDCP_LINK0_STATUS_V_MATCHES) 12188c2ecf20Sopenharmony_ci break; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci timeout_count--; 12218c2ecf20Sopenharmony_ci if (!timeout_count) { 12228c2ecf20Sopenharmony_ci pr_err("%s: HDCP V Match timedout", __func__); 12238c2ecf20Sopenharmony_ci return -ETIMEDOUT; 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); 12278c2ecf20Sopenharmony_ci if (rc) 12288c2ecf20Sopenharmony_ci return rc; 12298c2ecf20Sopenharmony_ci } while (1); 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci return 0; 12328c2ecf20Sopenharmony_ci} 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_cistatic void msm_hdmi_hdcp_auth_work(struct work_struct *work) 12358c2ecf20Sopenharmony_ci{ 12368c2ecf20Sopenharmony_ci struct hdmi_hdcp_ctrl *hdcp_ctrl = container_of(work, 12378c2ecf20Sopenharmony_ci struct hdmi_hdcp_ctrl, hdcp_auth_work); 12388c2ecf20Sopenharmony_ci int rc; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_auth_prepare(hdcp_ctrl); 12418c2ecf20Sopenharmony_ci if (rc) { 12428c2ecf20Sopenharmony_ci pr_err("%s: auth prepare failed %d\n", __func__, rc); 12438c2ecf20Sopenharmony_ci goto end; 12448c2ecf20Sopenharmony_ci } 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci /* HDCP PartI */ 12478c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_auth_part1_key_exchange(hdcp_ctrl); 12488c2ecf20Sopenharmony_ci if (rc) { 12498c2ecf20Sopenharmony_ci pr_err("%s: key exchange failed %d\n", __func__, rc); 12508c2ecf20Sopenharmony_ci goto end; 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_auth_part1_recv_r0(hdcp_ctrl); 12548c2ecf20Sopenharmony_ci if (rc) { 12558c2ecf20Sopenharmony_ci pr_err("%s: receive r0 failed %d\n", __func__, rc); 12568c2ecf20Sopenharmony_ci goto end; 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_auth_part1_verify_r0(hdcp_ctrl); 12608c2ecf20Sopenharmony_ci if (rc) { 12618c2ecf20Sopenharmony_ci pr_err("%s: verify r0 failed %d\n", __func__, rc); 12628c2ecf20Sopenharmony_ci goto end; 12638c2ecf20Sopenharmony_ci } 12648c2ecf20Sopenharmony_ci pr_info("%s: Authentication Part I successful\n", __func__); 12658c2ecf20Sopenharmony_ci if (hdcp_ctrl->ds_type == DS_RECEIVER) 12668c2ecf20Sopenharmony_ci goto end; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci /* HDCP PartII */ 12698c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_auth_part2_wait_ksv_fifo_ready(hdcp_ctrl); 12708c2ecf20Sopenharmony_ci if (rc) { 12718c2ecf20Sopenharmony_ci pr_err("%s: wait ksv fifo ready failed %d\n", __func__, rc); 12728c2ecf20Sopenharmony_ci goto end; 12738c2ecf20Sopenharmony_ci } 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_auth_part2_recv_ksv_fifo(hdcp_ctrl); 12768c2ecf20Sopenharmony_ci if (rc) { 12778c2ecf20Sopenharmony_ci pr_err("%s: recv ksv fifo failed %d\n", __func__, rc); 12788c2ecf20Sopenharmony_ci goto end; 12798c2ecf20Sopenharmony_ci } 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_auth_part2_write_ksv_fifo(hdcp_ctrl); 12828c2ecf20Sopenharmony_ci if (rc) { 12838c2ecf20Sopenharmony_ci pr_err("%s: write ksv fifo failed %d\n", __func__, rc); 12848c2ecf20Sopenharmony_ci goto end; 12858c2ecf20Sopenharmony_ci } 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci rc = msm_hdmi_hdcp_auth_part2_check_v_match(hdcp_ctrl); 12888c2ecf20Sopenharmony_ci if (rc) 12898c2ecf20Sopenharmony_ci pr_err("%s: check v match failed %d\n", __func__, rc); 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ciend: 12928c2ecf20Sopenharmony_ci if (rc == -ECANCELED) { 12938c2ecf20Sopenharmony_ci pr_info("%s: hdcp authentication canceled\n", __func__); 12948c2ecf20Sopenharmony_ci } else if (rc == -ENOTSUPP) { 12958c2ecf20Sopenharmony_ci pr_info("%s: hdcp is not supported\n", __func__); 12968c2ecf20Sopenharmony_ci } else if (rc) { 12978c2ecf20Sopenharmony_ci pr_err("%s: hdcp authentication failed\n", __func__); 12988c2ecf20Sopenharmony_ci msm_hdmi_hdcp_auth_fail(hdcp_ctrl); 12998c2ecf20Sopenharmony_ci } else { 13008c2ecf20Sopenharmony_ci msm_hdmi_hdcp_auth_done(hdcp_ctrl); 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci} 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_civoid msm_hdmi_hdcp_on(struct hdmi_hdcp_ctrl *hdcp_ctrl) 13058c2ecf20Sopenharmony_ci{ 13068c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 13078c2ecf20Sopenharmony_ci u32 reg_val; 13088c2ecf20Sopenharmony_ci unsigned long flags; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci if ((HDCP_STATE_INACTIVE != hdcp_ctrl->hdcp_state) || 13118c2ecf20Sopenharmony_ci (HDCP_STATE_NO_AKSV == hdcp_ctrl->hdcp_state)) { 13128c2ecf20Sopenharmony_ci DBG("still active or activating or no askv. returning"); 13138c2ecf20Sopenharmony_ci return; 13148c2ecf20Sopenharmony_ci } 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci /* clear HDMI Encrypt */ 13178c2ecf20Sopenharmony_ci spin_lock_irqsave(&hdmi->reg_lock, flags); 13188c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); 13198c2ecf20Sopenharmony_ci reg_val &= ~HDMI_CTRL_ENCRYPTED; 13208c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); 13218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hdmi->reg_lock, flags); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci hdcp_ctrl->auth_event = 0; 13248c2ecf20Sopenharmony_ci hdcp_ctrl->hdcp_state = HDCP_STATE_AUTHENTICATING; 13258c2ecf20Sopenharmony_ci hdcp_ctrl->auth_retries = 0; 13268c2ecf20Sopenharmony_ci queue_work(hdmi->workq, &hdcp_ctrl->hdcp_auth_work); 13278c2ecf20Sopenharmony_ci} 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_civoid msm_hdmi_hdcp_off(struct hdmi_hdcp_ctrl *hdcp_ctrl) 13308c2ecf20Sopenharmony_ci{ 13318c2ecf20Sopenharmony_ci struct hdmi *hdmi = hdcp_ctrl->hdmi; 13328c2ecf20Sopenharmony_ci unsigned long flags; 13338c2ecf20Sopenharmony_ci u32 reg_val; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci if ((HDCP_STATE_INACTIVE == hdcp_ctrl->hdcp_state) || 13368c2ecf20Sopenharmony_ci (HDCP_STATE_NO_AKSV == hdcp_ctrl->hdcp_state)) { 13378c2ecf20Sopenharmony_ci DBG("hdcp inactive or no aksv. returning"); 13388c2ecf20Sopenharmony_ci return; 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci /* 13428c2ecf20Sopenharmony_ci * Disable HPD circuitry. 13438c2ecf20Sopenharmony_ci * This is needed to reset the HDCP cipher engine so that when we 13448c2ecf20Sopenharmony_ci * attempt a re-authentication, HW would clear the AN0_READY and 13458c2ecf20Sopenharmony_ci * AN1_READY bits in HDMI_HDCP_LINK0_STATUS register 13468c2ecf20Sopenharmony_ci */ 13478c2ecf20Sopenharmony_ci spin_lock_irqsave(&hdmi->reg_lock, flags); 13488c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_HPD_CTRL); 13498c2ecf20Sopenharmony_ci reg_val &= ~HDMI_HPD_CTRL_ENABLE; 13508c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HPD_CTRL, reg_val); 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci /* 13538c2ecf20Sopenharmony_ci * Disable HDCP interrupts. 13548c2ecf20Sopenharmony_ci * Also, need to set the state to inactive here so that any ongoing 13558c2ecf20Sopenharmony_ci * reauth works will know that the HDCP session has been turned off. 13568c2ecf20Sopenharmony_ci */ 13578c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_INT_CTRL, 0); 13588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hdmi->reg_lock, flags); 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci /* 13618c2ecf20Sopenharmony_ci * Cancel any pending auth/reauth attempts. 13628c2ecf20Sopenharmony_ci * If one is ongoing, this will wait for it to finish. 13638c2ecf20Sopenharmony_ci * No more reauthentication attempts will be scheduled since we 13648c2ecf20Sopenharmony_ci * set the current state to inactive. 13658c2ecf20Sopenharmony_ci */ 13668c2ecf20Sopenharmony_ci set_bit(AUTH_ABORT_EV, &hdcp_ctrl->auth_event); 13678c2ecf20Sopenharmony_ci wake_up_all(&hdcp_ctrl->auth_event_queue); 13688c2ecf20Sopenharmony_ci cancel_work_sync(&hdcp_ctrl->hdcp_auth_work); 13698c2ecf20Sopenharmony_ci cancel_work_sync(&hdcp_ctrl->hdcp_reauth_work); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_RESET, 13728c2ecf20Sopenharmony_ci HDMI_HDCP_RESET_LINK0_DEAUTHENTICATE); 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci /* Disable encryption and disable the HDCP block */ 13758c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HDCP_CTRL, 0); 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci spin_lock_irqsave(&hdmi->reg_lock, flags); 13788c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); 13798c2ecf20Sopenharmony_ci reg_val &= ~HDMI_CTRL_ENCRYPTED; 13808c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci /* Enable HPD circuitry */ 13838c2ecf20Sopenharmony_ci reg_val = hdmi_read(hdmi, REG_HDMI_HPD_CTRL); 13848c2ecf20Sopenharmony_ci reg_val |= HDMI_HPD_CTRL_ENABLE; 13858c2ecf20Sopenharmony_ci hdmi_write(hdmi, REG_HDMI_HPD_CTRL, reg_val); 13868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hdmi->reg_lock, flags); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci hdcp_ctrl->hdcp_state = HDCP_STATE_INACTIVE; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci DBG("HDCP: Off"); 13918c2ecf20Sopenharmony_ci} 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_cistruct hdmi_hdcp_ctrl *msm_hdmi_hdcp_init(struct hdmi *hdmi) 13948c2ecf20Sopenharmony_ci{ 13958c2ecf20Sopenharmony_ci struct hdmi_hdcp_ctrl *hdcp_ctrl = NULL; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci if (!hdmi->qfprom_mmio) { 13988c2ecf20Sopenharmony_ci pr_err("%s: HDCP is not supported without qfprom\n", 13998c2ecf20Sopenharmony_ci __func__); 14008c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 14018c2ecf20Sopenharmony_ci } 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci hdcp_ctrl = kzalloc(sizeof(*hdcp_ctrl), GFP_KERNEL); 14048c2ecf20Sopenharmony_ci if (!hdcp_ctrl) 14058c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci INIT_WORK(&hdcp_ctrl->hdcp_auth_work, msm_hdmi_hdcp_auth_work); 14088c2ecf20Sopenharmony_ci INIT_WORK(&hdcp_ctrl->hdcp_reauth_work, msm_hdmi_hdcp_reauth_work); 14098c2ecf20Sopenharmony_ci init_waitqueue_head(&hdcp_ctrl->auth_event_queue); 14108c2ecf20Sopenharmony_ci hdcp_ctrl->hdmi = hdmi; 14118c2ecf20Sopenharmony_ci hdcp_ctrl->hdcp_state = HDCP_STATE_INACTIVE; 14128c2ecf20Sopenharmony_ci hdcp_ctrl->aksv_valid = false; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci if (qcom_scm_hdcp_available()) 14158c2ecf20Sopenharmony_ci hdcp_ctrl->tz_hdcp = true; 14168c2ecf20Sopenharmony_ci else 14178c2ecf20Sopenharmony_ci hdcp_ctrl->tz_hdcp = false; 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci return hdcp_ctrl; 14208c2ecf20Sopenharmony_ci} 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_civoid msm_hdmi_hdcp_destroy(struct hdmi *hdmi) 14238c2ecf20Sopenharmony_ci{ 14248c2ecf20Sopenharmony_ci if (hdmi) { 14258c2ecf20Sopenharmony_ci kfree(hdmi->hdcp_ctrl); 14268c2ecf20Sopenharmony_ci hdmi->hdcp_ctrl = NULL; 14278c2ecf20Sopenharmony_ci } 14288c2ecf20Sopenharmony_ci} 1429