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 
146a99099fSThomas Zimmermann #include <drm/display/drm_hdcp_helper.h>
156a3608eaSParshuram Thombare 
166a3608eaSParshuram Thombare #include "cdns-mhdp8546-hdcp.h"
176a3608eaSParshuram Thombare 
cdns_mhdp_secure_mailbox_read(struct cdns_mhdp_device * mhdp)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 
cdns_mhdp_secure_mailbox_write(struct cdns_mhdp_device * mhdp,u8 val)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 
cdns_mhdp_secure_mailbox_recv_header(struct cdns_mhdp_device * mhdp,u8 module_id,u8 opcode,u16 req_size)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 
cdns_mhdp_secure_mailbox_recv_data(struct cdns_mhdp_device * mhdp,u8 * buff,u16 buff_size)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 
cdns_mhdp_secure_mailbox_send(struct cdns_mhdp_device * mhdp,u8 module_id,u8 opcode,u16 size,u8 * message)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 
cdns_mhdp_hdcp_get_status(struct cdns_mhdp_device * mhdp,u16 * hdcp_port_status)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 
cdns_mhdp_hdcp_handle_status(struct cdns_mhdp_device * mhdp,u16 status)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 
cdns_mhdp_hdcp_rx_id_valid_response(struct cdns_mhdp_device * mhdp,u8 valid)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 
cdns_mhdp_hdcp_rx_id_valid(struct cdns_mhdp_device * mhdp,u8 * recv_num,u8 * hdcp_rx_id)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 
cdns_mhdp_hdcp_km_stored_resp(struct cdns_mhdp_device * mhdp,u32 size,u8 * km)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 
cdns_mhdp_hdcp_tx_is_km_stored(struct cdns_mhdp_device * mhdp,u8 * resp,u32 size)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 
cdns_mhdp_hdcp_tx_config(struct cdns_mhdp_device * mhdp,u8 hdcp_cfg)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 
cdns_mhdp_hdcp_set_config(struct cdns_mhdp_device * mhdp,u8 hdcp_config,bool enable)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 
cdns_mhdp_hdcp_auth_check(struct cdns_mhdp_device * mhdp)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 
cdns_mhdp_hdcp_check_receviers(struct cdns_mhdp_device * mhdp)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 
cdns_mhdp_hdcp_auth_22(struct cdns_mhdp_device * mhdp)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 
cdns_mhdp_hdcp_auth_14(struct cdns_mhdp_device * mhdp)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 
cdns_mhdp_hdcp_auth(struct cdns_mhdp_device * mhdp,u8 hdcp_config)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 
_cdns_mhdp_hdcp_disable(struct cdns_mhdp_device * mhdp)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 
_cdns_mhdp_hdcp_enable(struct cdns_mhdp_device * mhdp,u8 content_type)4046a3608eaSParshuram Thombare static int _cdns_mhdp_hdcp_enable(struct cdns_mhdp_device *mhdp, u8 content_type)
4056a3608eaSParshuram Thombare {
406*99705f3fSTomi Valkeinen 	int ret = -EINVAL;
407*99705f3fSTomi Valkeinen 	int tries = 3;
4086a3608eaSParshuram Thombare 	u32 i;
4096a3608eaSParshuram Thombare 
4106a3608eaSParshuram Thombare 	for (i = 0; i < tries; i++) {
4116a3608eaSParshuram Thombare 		if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0 ||
4126a3608eaSParshuram Thombare 		    content_type == DRM_MODE_HDCP_CONTENT_TYPE1) {
4136a3608eaSParshuram Thombare 			ret = cdns_mhdp_hdcp_auth(mhdp, HDCP_TX_2);
4146a3608eaSParshuram Thombare 			if (!ret)
4156a3608eaSParshuram Thombare 				return 0;
4166a3608eaSParshuram Thombare 			_cdns_mhdp_hdcp_disable(mhdp);
4176a3608eaSParshuram Thombare 		}
4186a3608eaSParshuram Thombare 
4196a3608eaSParshuram Thombare 		if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) {
4206a3608eaSParshuram Thombare 			ret = cdns_mhdp_hdcp_auth(mhdp, HDCP_TX_1);
4216a3608eaSParshuram Thombare 			if (!ret)
4226a3608eaSParshuram Thombare 				return 0;
4236a3608eaSParshuram Thombare 			_cdns_mhdp_hdcp_disable(mhdp);
4246a3608eaSParshuram Thombare 		}
4256a3608eaSParshuram Thombare 	}
4266a3608eaSParshuram Thombare 
4276a3608eaSParshuram Thombare 	dev_err(mhdp->dev, "HDCP authentication failed (%d tries/%d)\n",
4286a3608eaSParshuram Thombare 		tries, ret);
4296a3608eaSParshuram Thombare 
4306a3608eaSParshuram Thombare 	return ret;
4316a3608eaSParshuram Thombare }
4326a3608eaSParshuram Thombare 
cdns_mhdp_hdcp_check_link(struct cdns_mhdp_device * mhdp)4336a3608eaSParshuram Thombare static int cdns_mhdp_hdcp_check_link(struct cdns_mhdp_device *mhdp)
4346a3608eaSParshuram Thombare {
4356a3608eaSParshuram Thombare 	u16 hdcp_port_status;
4366a3608eaSParshuram Thombare 	int ret = 0;
4376a3608eaSParshuram Thombare 
4386a3608eaSParshuram Thombare 	mutex_lock(&mhdp->hdcp.mutex);
4396a3608eaSParshuram Thombare 	if (mhdp->hdcp.value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
4406a3608eaSParshuram Thombare 		goto out;
4416a3608eaSParshuram Thombare 
4426a3608eaSParshuram Thombare 	ret = cdns_mhdp_hdcp_get_status(mhdp, &hdcp_port_status);
4436a3608eaSParshuram Thombare 	if (!ret && hdcp_port_status & HDCP_PORT_STS_AUTH)
4446a3608eaSParshuram Thombare 		goto out;
4456a3608eaSParshuram Thombare 
4466a3608eaSParshuram Thombare 	dev_err(mhdp->dev,
4476a3608eaSParshuram Thombare 		"[%s:%d] HDCP link failed, retrying authentication\n",
4486a3608eaSParshuram Thombare 		mhdp->connector.name, mhdp->connector.base.id);
4496a3608eaSParshuram Thombare 
4506a3608eaSParshuram Thombare 	ret = _cdns_mhdp_hdcp_disable(mhdp);
4516a3608eaSParshuram Thombare 	if (ret) {
4526a3608eaSParshuram Thombare 		mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
4536a3608eaSParshuram Thombare 		schedule_work(&mhdp->hdcp.prop_work);
4546a3608eaSParshuram Thombare 		goto out;
4556a3608eaSParshuram Thombare 	}
4566a3608eaSParshuram Thombare 
4576a3608eaSParshuram Thombare 	ret = _cdns_mhdp_hdcp_enable(mhdp, mhdp->hdcp.hdcp_content_type);
4586a3608eaSParshuram Thombare 	if (ret) {
4596a3608eaSParshuram Thombare 		mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
4606a3608eaSParshuram Thombare 		schedule_work(&mhdp->hdcp.prop_work);
4616a3608eaSParshuram Thombare 	}
4626a3608eaSParshuram Thombare out:
4636a3608eaSParshuram Thombare 	mutex_unlock(&mhdp->hdcp.mutex);
4646a3608eaSParshuram Thombare 	return ret;
4656a3608eaSParshuram Thombare }
4666a3608eaSParshuram Thombare 
cdns_mhdp_hdcp_check_work(struct work_struct * work)4676a3608eaSParshuram Thombare static void cdns_mhdp_hdcp_check_work(struct work_struct *work)
4686a3608eaSParshuram Thombare {
4696a3608eaSParshuram Thombare 	struct delayed_work *d_work = to_delayed_work(work);
4706a3608eaSParshuram Thombare 	struct cdns_mhdp_hdcp *hdcp = container_of(d_work,
4716a3608eaSParshuram Thombare 						   struct cdns_mhdp_hdcp,
4726a3608eaSParshuram Thombare 						   check_work);
4736a3608eaSParshuram Thombare 	struct cdns_mhdp_device *mhdp = container_of(hdcp,
4746a3608eaSParshuram Thombare 						     struct cdns_mhdp_device,
4756a3608eaSParshuram Thombare 						     hdcp);
4766a3608eaSParshuram Thombare 
4776a3608eaSParshuram Thombare 	if (!cdns_mhdp_hdcp_check_link(mhdp))
4786a3608eaSParshuram Thombare 		schedule_delayed_work(&hdcp->check_work,
4796a3608eaSParshuram Thombare 				      DRM_HDCP_CHECK_PERIOD_MS);
4806a3608eaSParshuram Thombare }
4816a3608eaSParshuram Thombare 
cdns_mhdp_hdcp_prop_work(struct work_struct * work)4826a3608eaSParshuram Thombare static void cdns_mhdp_hdcp_prop_work(struct work_struct *work)
4836a3608eaSParshuram Thombare {
4846a3608eaSParshuram Thombare 	struct cdns_mhdp_hdcp *hdcp = container_of(work,
4856a3608eaSParshuram Thombare 						   struct cdns_mhdp_hdcp,
4866a3608eaSParshuram Thombare 						   prop_work);
4876a3608eaSParshuram Thombare 	struct cdns_mhdp_device *mhdp = container_of(hdcp,
4886a3608eaSParshuram Thombare 						     struct cdns_mhdp_device,
4896a3608eaSParshuram Thombare 						     hdcp);
4906a3608eaSParshuram Thombare 	struct drm_device *dev = mhdp->connector.dev;
4916a3608eaSParshuram Thombare 	struct drm_connector_state *state;
4926a3608eaSParshuram Thombare 
4936a3608eaSParshuram Thombare 	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
4946a3608eaSParshuram Thombare 	mutex_lock(&mhdp->hdcp.mutex);
4956a3608eaSParshuram Thombare 	if (mhdp->hdcp.value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
4966a3608eaSParshuram Thombare 		state = mhdp->connector.state;
4976a3608eaSParshuram Thombare 		state->content_protection = mhdp->hdcp.value;
4986a3608eaSParshuram Thombare 	}
4996a3608eaSParshuram Thombare 	mutex_unlock(&mhdp->hdcp.mutex);
5006a3608eaSParshuram Thombare 	drm_modeset_unlock(&dev->mode_config.connection_mutex);
5016a3608eaSParshuram Thombare }
5026a3608eaSParshuram Thombare 
cdns_mhdp_hdcp_set_lc(struct cdns_mhdp_device * mhdp,u8 * val)5036a3608eaSParshuram Thombare int cdns_mhdp_hdcp_set_lc(struct cdns_mhdp_device *mhdp, u8 *val)
5046a3608eaSParshuram Thombare {
5056a3608eaSParshuram Thombare 	int ret;
5066a3608eaSParshuram Thombare 
5076a3608eaSParshuram Thombare 	mutex_lock(&mhdp->mbox_mutex);
5086a3608eaSParshuram Thombare 	ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_GENERAL,
5096a3608eaSParshuram Thombare 					    HDCP_GENERAL_SET_LC_128,
5106a3608eaSParshuram Thombare 					    16, val);
5116a3608eaSParshuram Thombare 	mutex_unlock(&mhdp->mbox_mutex);
5126a3608eaSParshuram Thombare 
5136a3608eaSParshuram Thombare 	return ret;
5146a3608eaSParshuram Thombare }
5156a3608eaSParshuram Thombare 
5166a3608eaSParshuram Thombare int
cdns_mhdp_hdcp_set_public_key_param(struct cdns_mhdp_device * mhdp,struct cdns_hdcp_tx_public_key_param * val)5176a3608eaSParshuram Thombare cdns_mhdp_hdcp_set_public_key_param(struct cdns_mhdp_device *mhdp,
5186a3608eaSParshuram Thombare 				    struct cdns_hdcp_tx_public_key_param *val)
5196a3608eaSParshuram Thombare {
5206a3608eaSParshuram Thombare 	int ret;
5216a3608eaSParshuram Thombare 
5226a3608eaSParshuram Thombare 	mutex_lock(&mhdp->mbox_mutex);
5236a3608eaSParshuram Thombare 	ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX,
5246a3608eaSParshuram Thombare 					    HDCP2X_TX_SET_PUBLIC_KEY_PARAMS,
5256a3608eaSParshuram Thombare 					    sizeof(*val), (u8 *)val);
5266a3608eaSParshuram Thombare 	mutex_unlock(&mhdp->mbox_mutex);
5276a3608eaSParshuram Thombare 
5286a3608eaSParshuram Thombare 	return ret;
5296a3608eaSParshuram Thombare }
5306a3608eaSParshuram Thombare 
cdns_mhdp_hdcp_enable(struct cdns_mhdp_device * mhdp,u8 content_type)5316a3608eaSParshuram Thombare int cdns_mhdp_hdcp_enable(struct cdns_mhdp_device *mhdp, u8 content_type)
5326a3608eaSParshuram Thombare {
5336a3608eaSParshuram Thombare 	int ret;
5346a3608eaSParshuram Thombare 
5356a3608eaSParshuram Thombare 	mutex_lock(&mhdp->hdcp.mutex);
5366a3608eaSParshuram Thombare 	ret = _cdns_mhdp_hdcp_enable(mhdp, content_type);
5376a3608eaSParshuram Thombare 	if (ret)
5386a3608eaSParshuram Thombare 		goto out;
5396a3608eaSParshuram Thombare 
5406a3608eaSParshuram Thombare 	mhdp->hdcp.hdcp_content_type = content_type;
5416a3608eaSParshuram Thombare 	mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
5426a3608eaSParshuram Thombare 	schedule_work(&mhdp->hdcp.prop_work);
5436a3608eaSParshuram Thombare 	schedule_delayed_work(&mhdp->hdcp.check_work,
5446a3608eaSParshuram Thombare 			      DRM_HDCP_CHECK_PERIOD_MS);
5456a3608eaSParshuram Thombare out:
5466a3608eaSParshuram Thombare 	mutex_unlock(&mhdp->hdcp.mutex);
5476a3608eaSParshuram Thombare 	return ret;
5486a3608eaSParshuram Thombare }
5496a3608eaSParshuram Thombare 
cdns_mhdp_hdcp_disable(struct cdns_mhdp_device * mhdp)5506a3608eaSParshuram Thombare int cdns_mhdp_hdcp_disable(struct cdns_mhdp_device *mhdp)
5516a3608eaSParshuram Thombare {
5526a3608eaSParshuram Thombare 	int ret = 0;
5536a3608eaSParshuram Thombare 
5546a3608eaSParshuram Thombare 	mutex_lock(&mhdp->hdcp.mutex);
5556a3608eaSParshuram Thombare 	if (mhdp->hdcp.value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
5566a3608eaSParshuram Thombare 		mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
5576a3608eaSParshuram Thombare 		schedule_work(&mhdp->hdcp.prop_work);
5586a3608eaSParshuram Thombare 		ret = _cdns_mhdp_hdcp_disable(mhdp);
5596a3608eaSParshuram Thombare 	}
5606a3608eaSParshuram Thombare 	mutex_unlock(&mhdp->hdcp.mutex);
5616a3608eaSParshuram Thombare 	cancel_delayed_work_sync(&mhdp->hdcp.check_work);
5626a3608eaSParshuram Thombare 
5636a3608eaSParshuram Thombare 	return ret;
5646a3608eaSParshuram Thombare }
5656a3608eaSParshuram Thombare 
cdns_mhdp_hdcp_init(struct cdns_mhdp_device * mhdp)5666a3608eaSParshuram Thombare void cdns_mhdp_hdcp_init(struct cdns_mhdp_device *mhdp)
5676a3608eaSParshuram Thombare {
5686a3608eaSParshuram Thombare 	INIT_DELAYED_WORK(&mhdp->hdcp.check_work, cdns_mhdp_hdcp_check_work);
5696a3608eaSParshuram Thombare 	INIT_WORK(&mhdp->hdcp.prop_work, cdns_mhdp_hdcp_prop_work);
5706a3608eaSParshuram Thombare 	mutex_init(&mhdp->hdcp.mutex);
5716a3608eaSParshuram Thombare }
572