16a3608eaSParshuram Thombare // SPDX-License-Identifier: GPL-2.0 26a3608eaSParshuram Thombare /* 36a3608eaSParshuram Thombare * Cadence MHDP8546 DP bridge driver. 46a3608eaSParshuram Thombare * 56a3608eaSParshuram Thombare * Copyright (C) 2020 Cadence Design Systems, Inc. 66a3608eaSParshuram Thombare * 76a3608eaSParshuram Thombare */ 86a3608eaSParshuram Thombare 96a3608eaSParshuram Thombare #include <linux/io.h> 106a3608eaSParshuram Thombare #include <linux/iopoll.h> 116a3608eaSParshuram Thombare 126a3608eaSParshuram Thombare #include <asm/unaligned.h> 136a3608eaSParshuram Thombare 14*6a99099fSThomas Zimmermann #include <drm/display/drm_hdcp_helper.h> 156a3608eaSParshuram Thombare 166a3608eaSParshuram Thombare #include "cdns-mhdp8546-hdcp.h" 176a3608eaSParshuram Thombare 186a3608eaSParshuram Thombare static int cdns_mhdp_secure_mailbox_read(struct cdns_mhdp_device *mhdp) 196a3608eaSParshuram Thombare { 206a3608eaSParshuram Thombare int ret, empty; 216a3608eaSParshuram Thombare 226a3608eaSParshuram Thombare WARN_ON(!mutex_is_locked(&mhdp->mbox_mutex)); 236a3608eaSParshuram Thombare 246a3608eaSParshuram Thombare ret = readx_poll_timeout(readl, mhdp->sapb_regs + CDNS_MAILBOX_EMPTY, 256a3608eaSParshuram Thombare empty, !empty, MAILBOX_RETRY_US, 266a3608eaSParshuram Thombare MAILBOX_TIMEOUT_US); 276a3608eaSParshuram Thombare if (ret < 0) 286a3608eaSParshuram Thombare return ret; 296a3608eaSParshuram Thombare 306a3608eaSParshuram Thombare return readl(mhdp->sapb_regs + CDNS_MAILBOX_RX_DATA) & 0xff; 316a3608eaSParshuram Thombare } 326a3608eaSParshuram Thombare 336a3608eaSParshuram Thombare static int cdns_mhdp_secure_mailbox_write(struct cdns_mhdp_device *mhdp, 346a3608eaSParshuram Thombare u8 val) 356a3608eaSParshuram Thombare { 366a3608eaSParshuram Thombare int ret, full; 376a3608eaSParshuram Thombare 386a3608eaSParshuram Thombare WARN_ON(!mutex_is_locked(&mhdp->mbox_mutex)); 396a3608eaSParshuram Thombare 406a3608eaSParshuram Thombare ret = readx_poll_timeout(readl, mhdp->sapb_regs + CDNS_MAILBOX_FULL, 416a3608eaSParshuram Thombare full, !full, MAILBOX_RETRY_US, 426a3608eaSParshuram Thombare MAILBOX_TIMEOUT_US); 436a3608eaSParshuram Thombare if (ret < 0) 446a3608eaSParshuram Thombare return ret; 456a3608eaSParshuram Thombare 466a3608eaSParshuram Thombare writel(val, mhdp->sapb_regs + CDNS_MAILBOX_TX_DATA); 476a3608eaSParshuram Thombare 486a3608eaSParshuram Thombare return 0; 496a3608eaSParshuram Thombare } 506a3608eaSParshuram Thombare 516a3608eaSParshuram Thombare static int cdns_mhdp_secure_mailbox_recv_header(struct cdns_mhdp_device *mhdp, 526a3608eaSParshuram Thombare u8 module_id, 536a3608eaSParshuram Thombare u8 opcode, 546a3608eaSParshuram Thombare u16 req_size) 556a3608eaSParshuram Thombare { 566a3608eaSParshuram Thombare u32 mbox_size, i; 576a3608eaSParshuram Thombare u8 header[4]; 586a3608eaSParshuram Thombare int ret; 596a3608eaSParshuram Thombare 606a3608eaSParshuram Thombare /* read the header of the message */ 616a3608eaSParshuram Thombare for (i = 0; i < sizeof(header); i++) { 626a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_read(mhdp); 636a3608eaSParshuram Thombare if (ret < 0) 646a3608eaSParshuram Thombare return ret; 656a3608eaSParshuram Thombare 666a3608eaSParshuram Thombare header[i] = ret; 676a3608eaSParshuram Thombare } 686a3608eaSParshuram Thombare 696a3608eaSParshuram Thombare mbox_size = get_unaligned_be16(header + 2); 706a3608eaSParshuram Thombare 716a3608eaSParshuram Thombare if (opcode != header[0] || module_id != header[1] || 726a3608eaSParshuram Thombare (opcode != HDCP_TRAN_IS_REC_ID_VALID && req_size != mbox_size)) { 736a3608eaSParshuram Thombare for (i = 0; i < mbox_size; i++) 746a3608eaSParshuram Thombare if (cdns_mhdp_secure_mailbox_read(mhdp) < 0) 756a3608eaSParshuram Thombare break; 766a3608eaSParshuram Thombare return -EINVAL; 776a3608eaSParshuram Thombare } 786a3608eaSParshuram Thombare 796a3608eaSParshuram Thombare return 0; 806a3608eaSParshuram Thombare } 816a3608eaSParshuram Thombare 826a3608eaSParshuram Thombare static int cdns_mhdp_secure_mailbox_recv_data(struct cdns_mhdp_device *mhdp, 836a3608eaSParshuram Thombare u8 *buff, u16 buff_size) 846a3608eaSParshuram Thombare { 856a3608eaSParshuram Thombare int ret; 866a3608eaSParshuram Thombare u32 i; 876a3608eaSParshuram Thombare 886a3608eaSParshuram Thombare for (i = 0; i < buff_size; i++) { 896a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_read(mhdp); 906a3608eaSParshuram Thombare if (ret < 0) 916a3608eaSParshuram Thombare return ret; 926a3608eaSParshuram Thombare 936a3608eaSParshuram Thombare buff[i] = ret; 946a3608eaSParshuram Thombare } 956a3608eaSParshuram Thombare 966a3608eaSParshuram Thombare return 0; 976a3608eaSParshuram Thombare } 986a3608eaSParshuram Thombare 996a3608eaSParshuram Thombare static int cdns_mhdp_secure_mailbox_send(struct cdns_mhdp_device *mhdp, 1006a3608eaSParshuram Thombare u8 module_id, 1016a3608eaSParshuram Thombare u8 opcode, 1026a3608eaSParshuram Thombare u16 size, 1036a3608eaSParshuram Thombare u8 *message) 1046a3608eaSParshuram Thombare { 1056a3608eaSParshuram Thombare u8 header[4]; 1066a3608eaSParshuram Thombare int ret; 1076a3608eaSParshuram Thombare u32 i; 1086a3608eaSParshuram Thombare 1096a3608eaSParshuram Thombare header[0] = opcode; 1106a3608eaSParshuram Thombare header[1] = module_id; 1116a3608eaSParshuram Thombare put_unaligned_be16(size, header + 2); 1126a3608eaSParshuram Thombare 1136a3608eaSParshuram Thombare for (i = 0; i < sizeof(header); i++) { 1146a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_write(mhdp, header[i]); 1156a3608eaSParshuram Thombare if (ret) 1166a3608eaSParshuram Thombare return ret; 1176a3608eaSParshuram Thombare } 1186a3608eaSParshuram Thombare 1196a3608eaSParshuram Thombare for (i = 0; i < size; i++) { 1206a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_write(mhdp, message[i]); 1216a3608eaSParshuram Thombare if (ret) 1226a3608eaSParshuram Thombare return ret; 1236a3608eaSParshuram Thombare } 1246a3608eaSParshuram Thombare 1256a3608eaSParshuram Thombare return 0; 1266a3608eaSParshuram Thombare } 1276a3608eaSParshuram Thombare 1286a3608eaSParshuram Thombare static int cdns_mhdp_hdcp_get_status(struct cdns_mhdp_device *mhdp, 1296a3608eaSParshuram Thombare u16 *hdcp_port_status) 1306a3608eaSParshuram Thombare { 1316a3608eaSParshuram Thombare u8 hdcp_status[HDCP_STATUS_SIZE]; 1326a3608eaSParshuram Thombare int ret; 1336a3608eaSParshuram Thombare 1346a3608eaSParshuram Thombare mutex_lock(&mhdp->mbox_mutex); 1356a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, 1366a3608eaSParshuram Thombare HDCP_TRAN_STATUS_CHANGE, 0, NULL); 1376a3608eaSParshuram Thombare if (ret) 1386a3608eaSParshuram Thombare goto err_get_hdcp_status; 1396a3608eaSParshuram Thombare 1406a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_recv_header(mhdp, MB_MODULE_ID_HDCP_TX, 1416a3608eaSParshuram Thombare HDCP_TRAN_STATUS_CHANGE, 1426a3608eaSParshuram Thombare sizeof(hdcp_status)); 1436a3608eaSParshuram Thombare if (ret) 1446a3608eaSParshuram Thombare goto err_get_hdcp_status; 1456a3608eaSParshuram Thombare 1466a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_recv_data(mhdp, hdcp_status, 1476a3608eaSParshuram Thombare sizeof(hdcp_status)); 1486a3608eaSParshuram Thombare if (ret) 1496a3608eaSParshuram Thombare goto err_get_hdcp_status; 1506a3608eaSParshuram Thombare 1516a3608eaSParshuram Thombare *hdcp_port_status = ((u16)(hdcp_status[0] << 8) | hdcp_status[1]); 1526a3608eaSParshuram Thombare 1536a3608eaSParshuram Thombare err_get_hdcp_status: 1546a3608eaSParshuram Thombare mutex_unlock(&mhdp->mbox_mutex); 1556a3608eaSParshuram Thombare 1566a3608eaSParshuram Thombare return ret; 1576a3608eaSParshuram Thombare } 1586a3608eaSParshuram Thombare 1596a3608eaSParshuram Thombare static u8 cdns_mhdp_hdcp_handle_status(struct cdns_mhdp_device *mhdp, 1606a3608eaSParshuram Thombare u16 status) 1616a3608eaSParshuram Thombare { 1626a3608eaSParshuram Thombare u8 err = GET_HDCP_PORT_STS_LAST_ERR(status); 1636a3608eaSParshuram Thombare 1646a3608eaSParshuram Thombare if (err) 1656a3608eaSParshuram Thombare dev_dbg(mhdp->dev, "HDCP Error = %d", err); 1666a3608eaSParshuram Thombare 1676a3608eaSParshuram Thombare return err; 1686a3608eaSParshuram Thombare } 1696a3608eaSParshuram Thombare 1706a3608eaSParshuram Thombare static int cdns_mhdp_hdcp_rx_id_valid_response(struct cdns_mhdp_device *mhdp, 1716a3608eaSParshuram Thombare u8 valid) 1726a3608eaSParshuram Thombare { 1736a3608eaSParshuram Thombare int ret; 1746a3608eaSParshuram Thombare 1756a3608eaSParshuram Thombare mutex_lock(&mhdp->mbox_mutex); 1766a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, 1776a3608eaSParshuram Thombare HDCP_TRAN_RESPOND_RECEIVER_ID_VALID, 1786a3608eaSParshuram Thombare 1, &valid); 1796a3608eaSParshuram Thombare mutex_unlock(&mhdp->mbox_mutex); 1806a3608eaSParshuram Thombare 1816a3608eaSParshuram Thombare return ret; 1826a3608eaSParshuram Thombare } 1836a3608eaSParshuram Thombare 1846a3608eaSParshuram Thombare static int cdns_mhdp_hdcp_rx_id_valid(struct cdns_mhdp_device *mhdp, 1856a3608eaSParshuram Thombare u8 *recv_num, u8 *hdcp_rx_id) 1866a3608eaSParshuram Thombare { 1876a3608eaSParshuram Thombare u8 rec_id_hdr[2]; 1886a3608eaSParshuram Thombare u8 status; 1896a3608eaSParshuram Thombare int ret; 1906a3608eaSParshuram Thombare 1916a3608eaSParshuram Thombare mutex_lock(&mhdp->mbox_mutex); 1926a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, 1936a3608eaSParshuram Thombare HDCP_TRAN_IS_REC_ID_VALID, 0, NULL); 1946a3608eaSParshuram Thombare if (ret) 1956a3608eaSParshuram Thombare goto err_rx_id_valid; 1966a3608eaSParshuram Thombare 1976a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_recv_header(mhdp, MB_MODULE_ID_HDCP_TX, 1986a3608eaSParshuram Thombare HDCP_TRAN_IS_REC_ID_VALID, 1996a3608eaSParshuram Thombare sizeof(status)); 2006a3608eaSParshuram Thombare if (ret) 2016a3608eaSParshuram Thombare goto err_rx_id_valid; 2026a3608eaSParshuram Thombare 2036a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_recv_data(mhdp, rec_id_hdr, 2); 2046a3608eaSParshuram Thombare if (ret) 2056a3608eaSParshuram Thombare goto err_rx_id_valid; 2066a3608eaSParshuram Thombare 2076a3608eaSParshuram Thombare *recv_num = rec_id_hdr[0]; 2086a3608eaSParshuram Thombare 2096a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_recv_data(mhdp, hdcp_rx_id, 5 * *recv_num); 2106a3608eaSParshuram Thombare 2116a3608eaSParshuram Thombare err_rx_id_valid: 2126a3608eaSParshuram Thombare mutex_unlock(&mhdp->mbox_mutex); 2136a3608eaSParshuram Thombare 2146a3608eaSParshuram Thombare return ret; 2156a3608eaSParshuram Thombare } 2166a3608eaSParshuram Thombare 2176a3608eaSParshuram Thombare static int cdns_mhdp_hdcp_km_stored_resp(struct cdns_mhdp_device *mhdp, 2186a3608eaSParshuram Thombare u32 size, u8 *km) 2196a3608eaSParshuram Thombare { 2206a3608eaSParshuram Thombare int ret; 2216a3608eaSParshuram Thombare 2226a3608eaSParshuram Thombare mutex_lock(&mhdp->mbox_mutex); 2236a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, 2246a3608eaSParshuram Thombare HDCP2X_TX_RESPOND_KM, size, km); 2256a3608eaSParshuram Thombare mutex_unlock(&mhdp->mbox_mutex); 2266a3608eaSParshuram Thombare 2276a3608eaSParshuram Thombare return ret; 2286a3608eaSParshuram Thombare } 2296a3608eaSParshuram Thombare 2306a3608eaSParshuram Thombare static int cdns_mhdp_hdcp_tx_is_km_stored(struct cdns_mhdp_device *mhdp, 2316a3608eaSParshuram Thombare u8 *resp, u32 size) 2326a3608eaSParshuram Thombare { 2336a3608eaSParshuram Thombare int ret; 2346a3608eaSParshuram Thombare 2356a3608eaSParshuram Thombare mutex_lock(&mhdp->mbox_mutex); 2366a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, 2376a3608eaSParshuram Thombare HDCP2X_TX_IS_KM_STORED, 0, NULL); 2386a3608eaSParshuram Thombare if (ret) 2396a3608eaSParshuram Thombare goto err_is_km_stored; 2406a3608eaSParshuram Thombare 2416a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_recv_header(mhdp, MB_MODULE_ID_HDCP_TX, 2426a3608eaSParshuram Thombare HDCP2X_TX_IS_KM_STORED, 2436a3608eaSParshuram Thombare size); 2446a3608eaSParshuram Thombare if (ret) 2456a3608eaSParshuram Thombare goto err_is_km_stored; 2466a3608eaSParshuram Thombare 2476a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_recv_data(mhdp, resp, size); 2486a3608eaSParshuram Thombare err_is_km_stored: 2496a3608eaSParshuram Thombare mutex_unlock(&mhdp->mbox_mutex); 2506a3608eaSParshuram Thombare 2516a3608eaSParshuram Thombare return ret; 2526a3608eaSParshuram Thombare } 2536a3608eaSParshuram Thombare 2546a3608eaSParshuram Thombare static int cdns_mhdp_hdcp_tx_config(struct cdns_mhdp_device *mhdp, 2556a3608eaSParshuram Thombare u8 hdcp_cfg) 2566a3608eaSParshuram Thombare { 2576a3608eaSParshuram Thombare int ret; 2586a3608eaSParshuram Thombare 2596a3608eaSParshuram Thombare mutex_lock(&mhdp->mbox_mutex); 2606a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, 2616a3608eaSParshuram Thombare HDCP_TRAN_CONFIGURATION, 1, &hdcp_cfg); 2626a3608eaSParshuram Thombare mutex_unlock(&mhdp->mbox_mutex); 2636a3608eaSParshuram Thombare 2646a3608eaSParshuram Thombare return ret; 2656a3608eaSParshuram Thombare } 2666a3608eaSParshuram Thombare 2676a3608eaSParshuram Thombare static int cdns_mhdp_hdcp_set_config(struct cdns_mhdp_device *mhdp, 2686a3608eaSParshuram Thombare u8 hdcp_config, bool enable) 2696a3608eaSParshuram Thombare { 2706a3608eaSParshuram Thombare u16 hdcp_port_status; 2716a3608eaSParshuram Thombare u32 ret_event; 2726a3608eaSParshuram Thombare u8 hdcp_cfg; 2736a3608eaSParshuram Thombare int ret; 2746a3608eaSParshuram Thombare 2756a3608eaSParshuram Thombare hdcp_cfg = hdcp_config | (enable ? 0x04 : 0) | 2766a3608eaSParshuram Thombare (HDCP_CONTENT_TYPE_0 << 3); 2776a3608eaSParshuram Thombare cdns_mhdp_hdcp_tx_config(mhdp, hdcp_cfg); 2786a3608eaSParshuram Thombare ret_event = cdns_mhdp_wait_for_sw_event(mhdp, CDNS_HDCP_TX_STATUS); 2796a3608eaSParshuram Thombare if (!ret_event) 2806a3608eaSParshuram Thombare return -1; 2816a3608eaSParshuram Thombare 2826a3608eaSParshuram Thombare ret = cdns_mhdp_hdcp_get_status(mhdp, &hdcp_port_status); 2836a3608eaSParshuram Thombare if (ret || cdns_mhdp_hdcp_handle_status(mhdp, hdcp_port_status)) 2846a3608eaSParshuram Thombare return -1; 2856a3608eaSParshuram Thombare 2866a3608eaSParshuram Thombare return 0; 2876a3608eaSParshuram Thombare } 2886a3608eaSParshuram Thombare 2896a3608eaSParshuram Thombare static int cdns_mhdp_hdcp_auth_check(struct cdns_mhdp_device *mhdp) 2906a3608eaSParshuram Thombare { 2916a3608eaSParshuram Thombare u16 hdcp_port_status; 2926a3608eaSParshuram Thombare u32 ret_event; 2936a3608eaSParshuram Thombare int ret; 2946a3608eaSParshuram Thombare 2956a3608eaSParshuram Thombare ret_event = cdns_mhdp_wait_for_sw_event(mhdp, CDNS_HDCP_TX_STATUS); 2966a3608eaSParshuram Thombare if (!ret_event) 2976a3608eaSParshuram Thombare return -1; 2986a3608eaSParshuram Thombare 2996a3608eaSParshuram Thombare ret = cdns_mhdp_hdcp_get_status(mhdp, &hdcp_port_status); 3006a3608eaSParshuram Thombare if (ret || cdns_mhdp_hdcp_handle_status(mhdp, hdcp_port_status)) 3016a3608eaSParshuram Thombare return -1; 3026a3608eaSParshuram Thombare 3036a3608eaSParshuram Thombare if (hdcp_port_status & 1) { 3046a3608eaSParshuram Thombare dev_dbg(mhdp->dev, "Authentication completed successfully!\n"); 3056a3608eaSParshuram Thombare return 0; 3066a3608eaSParshuram Thombare } 3076a3608eaSParshuram Thombare 3086a3608eaSParshuram Thombare dev_dbg(mhdp->dev, "Authentication failed\n"); 3096a3608eaSParshuram Thombare 3106a3608eaSParshuram Thombare return -1; 3116a3608eaSParshuram Thombare } 3126a3608eaSParshuram Thombare 3136a3608eaSParshuram Thombare static int cdns_mhdp_hdcp_check_receviers(struct cdns_mhdp_device *mhdp) 3146a3608eaSParshuram Thombare { 3156a3608eaSParshuram Thombare u8 hdcp_rec_id[HDCP_MAX_RECEIVERS][HDCP_RECEIVER_ID_SIZE_BYTES]; 3166a3608eaSParshuram Thombare u8 hdcp_num_rec; 3176a3608eaSParshuram Thombare u32 ret_event; 3186a3608eaSParshuram Thombare 3196a3608eaSParshuram Thombare ret_event = cdns_mhdp_wait_for_sw_event(mhdp, 3206a3608eaSParshuram Thombare CDNS_HDCP_TX_IS_RCVR_ID_VALID); 3216a3608eaSParshuram Thombare if (!ret_event) 3226a3608eaSParshuram Thombare return -1; 3236a3608eaSParshuram Thombare 3246a3608eaSParshuram Thombare hdcp_num_rec = 0; 3256a3608eaSParshuram Thombare memset(&hdcp_rec_id, 0, sizeof(hdcp_rec_id)); 3266a3608eaSParshuram Thombare cdns_mhdp_hdcp_rx_id_valid(mhdp, &hdcp_num_rec, (u8 *)hdcp_rec_id); 3276a3608eaSParshuram Thombare cdns_mhdp_hdcp_rx_id_valid_response(mhdp, 1); 3286a3608eaSParshuram Thombare 3296a3608eaSParshuram Thombare return 0; 3306a3608eaSParshuram Thombare } 3316a3608eaSParshuram Thombare 3326a3608eaSParshuram Thombare static int cdns_mhdp_hdcp_auth_22(struct cdns_mhdp_device *mhdp) 3336a3608eaSParshuram Thombare { 3346a3608eaSParshuram Thombare u8 resp[HDCP_STATUS_SIZE]; 3356a3608eaSParshuram Thombare u16 hdcp_port_status; 3366a3608eaSParshuram Thombare u32 ret_event; 3376a3608eaSParshuram Thombare int ret; 3386a3608eaSParshuram Thombare 3396a3608eaSParshuram Thombare dev_dbg(mhdp->dev, "HDCP: Start 2.2 Authentication\n"); 3406a3608eaSParshuram Thombare ret_event = cdns_mhdp_wait_for_sw_event(mhdp, 3416a3608eaSParshuram Thombare CDNS_HDCP2_TX_IS_KM_STORED); 3426a3608eaSParshuram Thombare if (!ret_event) 3436a3608eaSParshuram Thombare return -1; 3446a3608eaSParshuram Thombare 3456a3608eaSParshuram Thombare if (ret_event & CDNS_HDCP_TX_STATUS) { 3466a3608eaSParshuram Thombare mhdp->sw_events &= ~CDNS_HDCP_TX_STATUS; 3476a3608eaSParshuram Thombare ret = cdns_mhdp_hdcp_get_status(mhdp, &hdcp_port_status); 3486a3608eaSParshuram Thombare if (ret || cdns_mhdp_hdcp_handle_status(mhdp, hdcp_port_status)) 3496a3608eaSParshuram Thombare return -1; 3506a3608eaSParshuram Thombare } 3516a3608eaSParshuram Thombare 3526a3608eaSParshuram Thombare cdns_mhdp_hdcp_tx_is_km_stored(mhdp, resp, sizeof(resp)); 3536a3608eaSParshuram Thombare cdns_mhdp_hdcp_km_stored_resp(mhdp, 0, NULL); 3546a3608eaSParshuram Thombare 3556a3608eaSParshuram Thombare if (cdns_mhdp_hdcp_check_receviers(mhdp)) 3566a3608eaSParshuram Thombare return -1; 3576a3608eaSParshuram Thombare 3586a3608eaSParshuram Thombare return 0; 3596a3608eaSParshuram Thombare } 3606a3608eaSParshuram Thombare 3616a3608eaSParshuram Thombare static inline int cdns_mhdp_hdcp_auth_14(struct cdns_mhdp_device *mhdp) 3626a3608eaSParshuram Thombare { 3636a3608eaSParshuram Thombare dev_dbg(mhdp->dev, "HDCP: Starting 1.4 Authentication\n"); 3646a3608eaSParshuram Thombare return cdns_mhdp_hdcp_check_receviers(mhdp); 3656a3608eaSParshuram Thombare } 3666a3608eaSParshuram Thombare 3676a3608eaSParshuram Thombare static int cdns_mhdp_hdcp_auth(struct cdns_mhdp_device *mhdp, 3686a3608eaSParshuram Thombare u8 hdcp_config) 3696a3608eaSParshuram Thombare { 3706a3608eaSParshuram Thombare int ret; 3716a3608eaSParshuram Thombare 3726a3608eaSParshuram Thombare ret = cdns_mhdp_hdcp_set_config(mhdp, hdcp_config, true); 3736a3608eaSParshuram Thombare if (ret) 3746a3608eaSParshuram Thombare goto auth_failed; 3756a3608eaSParshuram Thombare 3766a3608eaSParshuram Thombare if (hdcp_config == HDCP_TX_1) 3776a3608eaSParshuram Thombare ret = cdns_mhdp_hdcp_auth_14(mhdp); 3786a3608eaSParshuram Thombare else 3796a3608eaSParshuram Thombare ret = cdns_mhdp_hdcp_auth_22(mhdp); 3806a3608eaSParshuram Thombare 3816a3608eaSParshuram Thombare if (ret) 3826a3608eaSParshuram Thombare goto auth_failed; 3836a3608eaSParshuram Thombare 3846a3608eaSParshuram Thombare ret = cdns_mhdp_hdcp_auth_check(mhdp); 3856a3608eaSParshuram Thombare if (ret) 3866a3608eaSParshuram Thombare ret = cdns_mhdp_hdcp_auth_check(mhdp); 3876a3608eaSParshuram Thombare 3886a3608eaSParshuram Thombare auth_failed: 3896a3608eaSParshuram Thombare return ret; 3906a3608eaSParshuram Thombare } 3916a3608eaSParshuram Thombare 3926a3608eaSParshuram Thombare static int _cdns_mhdp_hdcp_disable(struct cdns_mhdp_device *mhdp) 3936a3608eaSParshuram Thombare { 3946a3608eaSParshuram Thombare int ret; 3956a3608eaSParshuram Thombare 3966a3608eaSParshuram Thombare dev_dbg(mhdp->dev, "[%s:%d] HDCP is being disabled...\n", 3976a3608eaSParshuram Thombare mhdp->connector.name, mhdp->connector.base.id); 3986a3608eaSParshuram Thombare 3996a3608eaSParshuram Thombare ret = cdns_mhdp_hdcp_set_config(mhdp, 0, false); 4006a3608eaSParshuram Thombare 4016a3608eaSParshuram Thombare return ret; 4026a3608eaSParshuram Thombare } 4036a3608eaSParshuram Thombare 4046a3608eaSParshuram Thombare static int _cdns_mhdp_hdcp_enable(struct cdns_mhdp_device *mhdp, u8 content_type) 4056a3608eaSParshuram Thombare { 4066a3608eaSParshuram Thombare int ret, tries = 3; 4076a3608eaSParshuram Thombare u32 i; 4086a3608eaSParshuram Thombare 4096a3608eaSParshuram Thombare for (i = 0; i < tries; i++) { 4106a3608eaSParshuram Thombare if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0 || 4116a3608eaSParshuram Thombare content_type == DRM_MODE_HDCP_CONTENT_TYPE1) { 4126a3608eaSParshuram Thombare ret = cdns_mhdp_hdcp_auth(mhdp, HDCP_TX_2); 4136a3608eaSParshuram Thombare if (!ret) 4146a3608eaSParshuram Thombare return 0; 4156a3608eaSParshuram Thombare _cdns_mhdp_hdcp_disable(mhdp); 4166a3608eaSParshuram Thombare } 4176a3608eaSParshuram Thombare 4186a3608eaSParshuram Thombare if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) { 4196a3608eaSParshuram Thombare ret = cdns_mhdp_hdcp_auth(mhdp, HDCP_TX_1); 4206a3608eaSParshuram Thombare if (!ret) 4216a3608eaSParshuram Thombare return 0; 4226a3608eaSParshuram Thombare _cdns_mhdp_hdcp_disable(mhdp); 4236a3608eaSParshuram Thombare } 4246a3608eaSParshuram Thombare } 4256a3608eaSParshuram Thombare 4266a3608eaSParshuram Thombare dev_err(mhdp->dev, "HDCP authentication failed (%d tries/%d)\n", 4276a3608eaSParshuram Thombare tries, ret); 4286a3608eaSParshuram Thombare 4296a3608eaSParshuram Thombare return ret; 4306a3608eaSParshuram Thombare } 4316a3608eaSParshuram Thombare 4326a3608eaSParshuram Thombare static int cdns_mhdp_hdcp_check_link(struct cdns_mhdp_device *mhdp) 4336a3608eaSParshuram Thombare { 4346a3608eaSParshuram Thombare u16 hdcp_port_status; 4356a3608eaSParshuram Thombare int ret = 0; 4366a3608eaSParshuram Thombare 4376a3608eaSParshuram Thombare mutex_lock(&mhdp->hdcp.mutex); 4386a3608eaSParshuram Thombare if (mhdp->hdcp.value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED) 4396a3608eaSParshuram Thombare goto out; 4406a3608eaSParshuram Thombare 4416a3608eaSParshuram Thombare ret = cdns_mhdp_hdcp_get_status(mhdp, &hdcp_port_status); 4426a3608eaSParshuram Thombare if (!ret && hdcp_port_status & HDCP_PORT_STS_AUTH) 4436a3608eaSParshuram Thombare goto out; 4446a3608eaSParshuram Thombare 4456a3608eaSParshuram Thombare dev_err(mhdp->dev, 4466a3608eaSParshuram Thombare "[%s:%d] HDCP link failed, retrying authentication\n", 4476a3608eaSParshuram Thombare mhdp->connector.name, mhdp->connector.base.id); 4486a3608eaSParshuram Thombare 4496a3608eaSParshuram Thombare ret = _cdns_mhdp_hdcp_disable(mhdp); 4506a3608eaSParshuram Thombare if (ret) { 4516a3608eaSParshuram Thombare mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_DESIRED; 4526a3608eaSParshuram Thombare schedule_work(&mhdp->hdcp.prop_work); 4536a3608eaSParshuram Thombare goto out; 4546a3608eaSParshuram Thombare } 4556a3608eaSParshuram Thombare 4566a3608eaSParshuram Thombare ret = _cdns_mhdp_hdcp_enable(mhdp, mhdp->hdcp.hdcp_content_type); 4576a3608eaSParshuram Thombare if (ret) { 4586a3608eaSParshuram Thombare mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_DESIRED; 4596a3608eaSParshuram Thombare schedule_work(&mhdp->hdcp.prop_work); 4606a3608eaSParshuram Thombare } 4616a3608eaSParshuram Thombare out: 4626a3608eaSParshuram Thombare mutex_unlock(&mhdp->hdcp.mutex); 4636a3608eaSParshuram Thombare return ret; 4646a3608eaSParshuram Thombare } 4656a3608eaSParshuram Thombare 4666a3608eaSParshuram Thombare static void cdns_mhdp_hdcp_check_work(struct work_struct *work) 4676a3608eaSParshuram Thombare { 4686a3608eaSParshuram Thombare struct delayed_work *d_work = to_delayed_work(work); 4696a3608eaSParshuram Thombare struct cdns_mhdp_hdcp *hdcp = container_of(d_work, 4706a3608eaSParshuram Thombare struct cdns_mhdp_hdcp, 4716a3608eaSParshuram Thombare check_work); 4726a3608eaSParshuram Thombare struct cdns_mhdp_device *mhdp = container_of(hdcp, 4736a3608eaSParshuram Thombare struct cdns_mhdp_device, 4746a3608eaSParshuram Thombare hdcp); 4756a3608eaSParshuram Thombare 4766a3608eaSParshuram Thombare if (!cdns_mhdp_hdcp_check_link(mhdp)) 4776a3608eaSParshuram Thombare schedule_delayed_work(&hdcp->check_work, 4786a3608eaSParshuram Thombare DRM_HDCP_CHECK_PERIOD_MS); 4796a3608eaSParshuram Thombare } 4806a3608eaSParshuram Thombare 4816a3608eaSParshuram Thombare static void cdns_mhdp_hdcp_prop_work(struct work_struct *work) 4826a3608eaSParshuram Thombare { 4836a3608eaSParshuram Thombare struct cdns_mhdp_hdcp *hdcp = container_of(work, 4846a3608eaSParshuram Thombare struct cdns_mhdp_hdcp, 4856a3608eaSParshuram Thombare prop_work); 4866a3608eaSParshuram Thombare struct cdns_mhdp_device *mhdp = container_of(hdcp, 4876a3608eaSParshuram Thombare struct cdns_mhdp_device, 4886a3608eaSParshuram Thombare hdcp); 4896a3608eaSParshuram Thombare struct drm_device *dev = mhdp->connector.dev; 4906a3608eaSParshuram Thombare struct drm_connector_state *state; 4916a3608eaSParshuram Thombare 4926a3608eaSParshuram Thombare drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); 4936a3608eaSParshuram Thombare mutex_lock(&mhdp->hdcp.mutex); 4946a3608eaSParshuram Thombare if (mhdp->hdcp.value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { 4956a3608eaSParshuram Thombare state = mhdp->connector.state; 4966a3608eaSParshuram Thombare state->content_protection = mhdp->hdcp.value; 4976a3608eaSParshuram Thombare } 4986a3608eaSParshuram Thombare mutex_unlock(&mhdp->hdcp.mutex); 4996a3608eaSParshuram Thombare drm_modeset_unlock(&dev->mode_config.connection_mutex); 5006a3608eaSParshuram Thombare } 5016a3608eaSParshuram Thombare 5026a3608eaSParshuram Thombare int cdns_mhdp_hdcp_set_lc(struct cdns_mhdp_device *mhdp, u8 *val) 5036a3608eaSParshuram Thombare { 5046a3608eaSParshuram Thombare int ret; 5056a3608eaSParshuram Thombare 5066a3608eaSParshuram Thombare mutex_lock(&mhdp->mbox_mutex); 5076a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_GENERAL, 5086a3608eaSParshuram Thombare HDCP_GENERAL_SET_LC_128, 5096a3608eaSParshuram Thombare 16, val); 5106a3608eaSParshuram Thombare mutex_unlock(&mhdp->mbox_mutex); 5116a3608eaSParshuram Thombare 5126a3608eaSParshuram Thombare return ret; 5136a3608eaSParshuram Thombare } 5146a3608eaSParshuram Thombare 5156a3608eaSParshuram Thombare int 5166a3608eaSParshuram Thombare cdns_mhdp_hdcp_set_public_key_param(struct cdns_mhdp_device *mhdp, 5176a3608eaSParshuram Thombare struct cdns_hdcp_tx_public_key_param *val) 5186a3608eaSParshuram Thombare { 5196a3608eaSParshuram Thombare int ret; 5206a3608eaSParshuram Thombare 5216a3608eaSParshuram Thombare mutex_lock(&mhdp->mbox_mutex); 5226a3608eaSParshuram Thombare ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, 5236a3608eaSParshuram Thombare HDCP2X_TX_SET_PUBLIC_KEY_PARAMS, 5246a3608eaSParshuram Thombare sizeof(*val), (u8 *)val); 5256a3608eaSParshuram Thombare mutex_unlock(&mhdp->mbox_mutex); 5266a3608eaSParshuram Thombare 5276a3608eaSParshuram Thombare return ret; 5286a3608eaSParshuram Thombare } 5296a3608eaSParshuram Thombare 5306a3608eaSParshuram Thombare int cdns_mhdp_hdcp_enable(struct cdns_mhdp_device *mhdp, u8 content_type) 5316a3608eaSParshuram Thombare { 5326a3608eaSParshuram Thombare int ret; 5336a3608eaSParshuram Thombare 5346a3608eaSParshuram Thombare mutex_lock(&mhdp->hdcp.mutex); 5356a3608eaSParshuram Thombare ret = _cdns_mhdp_hdcp_enable(mhdp, content_type); 5366a3608eaSParshuram Thombare if (ret) 5376a3608eaSParshuram Thombare goto out; 5386a3608eaSParshuram Thombare 5396a3608eaSParshuram Thombare mhdp->hdcp.hdcp_content_type = content_type; 5406a3608eaSParshuram Thombare mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_ENABLED; 5416a3608eaSParshuram Thombare schedule_work(&mhdp->hdcp.prop_work); 5426a3608eaSParshuram Thombare schedule_delayed_work(&mhdp->hdcp.check_work, 5436a3608eaSParshuram Thombare DRM_HDCP_CHECK_PERIOD_MS); 5446a3608eaSParshuram Thombare out: 5456a3608eaSParshuram Thombare mutex_unlock(&mhdp->hdcp.mutex); 5466a3608eaSParshuram Thombare return ret; 5476a3608eaSParshuram Thombare } 5486a3608eaSParshuram Thombare 5496a3608eaSParshuram Thombare int cdns_mhdp_hdcp_disable(struct cdns_mhdp_device *mhdp) 5506a3608eaSParshuram Thombare { 5516a3608eaSParshuram Thombare int ret = 0; 5526a3608eaSParshuram Thombare 5536a3608eaSParshuram Thombare mutex_lock(&mhdp->hdcp.mutex); 5546a3608eaSParshuram Thombare if (mhdp->hdcp.value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { 5556a3608eaSParshuram Thombare mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; 5566a3608eaSParshuram Thombare schedule_work(&mhdp->hdcp.prop_work); 5576a3608eaSParshuram Thombare ret = _cdns_mhdp_hdcp_disable(mhdp); 5586a3608eaSParshuram Thombare } 5596a3608eaSParshuram Thombare mutex_unlock(&mhdp->hdcp.mutex); 5606a3608eaSParshuram Thombare cancel_delayed_work_sync(&mhdp->hdcp.check_work); 5616a3608eaSParshuram Thombare 5626a3608eaSParshuram Thombare return ret; 5636a3608eaSParshuram Thombare } 5646a3608eaSParshuram Thombare 5656a3608eaSParshuram Thombare void cdns_mhdp_hdcp_init(struct cdns_mhdp_device *mhdp) 5666a3608eaSParshuram Thombare { 5676a3608eaSParshuram Thombare INIT_DELAYED_WORK(&mhdp->hdcp.check_work, cdns_mhdp_hdcp_check_work); 5686a3608eaSParshuram Thombare INIT_WORK(&mhdp->hdcp.prop_work, cdns_mhdp_hdcp_prop_work); 5696a3608eaSParshuram Thombare mutex_init(&mhdp->hdcp.mutex); 5706a3608eaSParshuram Thombare } 571