[PATCH v2 3/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP1.x feature for DisplayPort

mac.shen mac.shen at mediatek.com
Sun Feb 4 21:50:38 PST 2024


Add HDCP1.x feature for DisplayPort.
If the sink support HDCP1.X only, the feature will do HDCP1.x
authentication when userspace request the kernel protect with
HDCP_Content_Type property as DRM_MODE_HDCP_CONTENT_TYPE0.

Changes in v2:
- remove useless code
- remove the prefix 'mdrv'
- do HDCP1.x authentication when userspace request the
  kernel protect future content communicated
per suggestion from the previous thread:
https://lore.kernel.org/all/8fff59b5567449d8201dd1138c8
fa9218a545c46.camel at mediatek.com/

Signed-off-by: mac.shen <mac.shen at mediatek.com>
---
 drivers/gpu/drm/mediatek/Makefile        |   1 +
 drivers/gpu/drm/mediatek/mtk_dp.c        |  33 +-
 drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.c | 589 +++++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h |  46 ++
 drivers/gpu/drm/mediatek/mtk_dp_reg.h    |   3 +
 5 files changed, 669 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h

diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index 50ea069b047e..9738235f76b8 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -27,6 +27,7 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
 obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
 
 mtk-dp-objs := tlc_dp_hdcp.o \
+		  mtk_dp_hdcp1x.o \
 		  mtk_dp_hdcp2.o \
 		  mtk_dp.o
 
diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index 7ff72f15528b..8cd7562dab7a 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -33,6 +33,7 @@
 
 #include "mtk_dp.h"
 #include "mtk_dp_reg.h"
+#include "mtk_dp_hdcp1x.h"
 #include "mtk_dp_hdcp2.h"
 
 #define MTK_DP_SIP_CONTROL_AARCH32	MTK_SIP_SMC_CMD(0x523)
@@ -1811,6 +1812,9 @@ void mtk_dp_check_hdcp_version(struct mtk_dp *mtk_dp, bool only_hdcp1x)
 	if (!only_hdcp1x && dp_tx_hdcp2_support(&mtk_dp->hdcp_info))
 		return;
 
+	if (dp_tx_hdcp1x_support(&mtk_dp->hdcp_info))
+		return;
+
 	if (tee_add_device(&mtk_dp->hdcp_info, HDCP_NONE) != RET_SUCCESS)
 		mtk_dp->hdcp_info.auth_status = AUTH_FAIL;
 }
@@ -1860,15 +1864,34 @@ static void mtk_dp_hdcp_handle(struct work_struct *data)
 		mtk_dp_check_hdcp_version(mtk_dp, false);
 		if (mtk_dp->hdcp_info.hdcp2_info.enable)
 			dp_tx_hdcp2_set_start_auth(&mtk_dp->hdcp_info, true);
+		else if (mtk_dp->hdcp_info.hdcp1x_info.enable &&
+			 mtk_dp->hdcp_info.hdcp_content_type != DRM_MODE_HDCP_CONTENT_TYPE1)
+			dp_tx_hdcp1x_set_start_auth(&mtk_dp->hdcp_info, true);
 		else
 			mtk_dp->hdcp_info.auth_status = AUTH_ZERO;
 	}
 
-	while (mtk_dp->hdcp_info.hdcp2_info.enable &&
-	       mtk_dp->hdcp_info.auth_status != AUTH_FAIL &&
+	while ((mtk_dp->hdcp_info.hdcp1x_info.enable ||
+		mtk_dp->hdcp_info.hdcp2_info.enable) &&
+			mtk_dp->hdcp_info.auth_status != AUTH_FAIL &&
 			mtk_dp->hdcp_info.auth_status != AUTH_PASS) {
-		if (mtk_dp->hdcp_info.hdcp2_info.enable)
+		if (mtk_dp->hdcp_info.hdcp2_info.enable) {
 			dp_tx_hdcp2_fsm(&mtk_dp->hdcp_info);
+			if (mtk_dp->hdcp_info.auth_status == AUTH_FAIL) {
+				tee_remove_device(&mtk_dp->hdcp_info);
+				mtk_dp_check_hdcp_version(mtk_dp, true);
+				if (mtk_dp->hdcp_info.hdcp1x_info.enable &&
+				    mtk_dp->hdcp_info.hdcp_content_type !=
+					DRM_MODE_HDCP_CONTENT_TYPE1) {
+					mtk_dp->hdcp_info.hdcp2_info.enable = false;
+					dp_tx_hdcp1x_set_start_auth(&mtk_dp->hdcp_info, true);
+				}
+			}
+		}
+
+		if (mtk_dp->hdcp_info.hdcp1x_info.enable &&
+		    mtk_dp->hdcp_info.hdcp_content_type != DRM_MODE_HDCP_CONTENT_TYPE1)
+			dp_tx_hdcp1x_fsm(&mtk_dp->hdcp_info);
 	}
 }
 
@@ -1924,6 +1947,8 @@ static void mtk_dp_hdcp_atomic_check(struct mtk_dp *mtk_dp, struct drm_connector
 		dev_dbg(mtk_dp->dev, "disable HDCP\n");
 		if (mtk_dp->hdcp_info.hdcp2_info.enable)
 			dp_tx_hdcp2_set_start_auth(&mtk_dp->hdcp_info, false);
+		else if (mtk_dp->hdcp_info.hdcp1x_info.enable)
+			dp_tx_hdcp1x_set_start_auth(&mtk_dp->hdcp_info, false);
 
 		drm_hdcp_update_content_protection(mtk_dp->conn,
 						   mtk_dp->hdcp_info.content_protection);
@@ -2394,6 +2419,8 @@ static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
 
 	if (mtk_dp->hdcp_info.hdcp2_info.enable)
 		dp_tx_hdcp2_set_start_auth(&mtk_dp->hdcp_info, false);
+	else if (mtk_dp->hdcp_info.hdcp1x_info.enable)
+		dp_tx_hdcp1x_set_start_auth(&mtk_dp->hdcp_info, false);
 
 	if (mtk_dp->hdcp_info.content_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
 		mtk_dp->hdcp_info.content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
diff --git a/drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.c b/drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.c
new file mode 100644
index 000000000000..33b6cad39714
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019-2024 MediaTek Inc.
+ */
+
+#include "mtk_dp_hdcp1x.h"
+#include "mtk_dp_reg.h"
+#include "mtk_dp.h"
+
+static void dp_tx_hdcp1x_start_cipher(struct mtk_hdcp_info *hdcp_info, bool enable)
+{
+	struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+
+	if (enable) {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3480, REQ_BLOCK_CIPHER_AUTH,
+				   REQ_BLOCK_CIPHER_AUTH);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3480, KM_GENERATED, KM_GENERATED);
+	} else {
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3480, 0, KM_GENERATED);
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3480, 0, REQ_BLOCK_CIPHER_AUTH);
+	}
+}
+
+static bool dp_tx_hdcp1x_get_r0_available(struct mtk_hdcp_info *hdcp_info)
+{
+	struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+	bool R0_available;
+	u32 ret;
+
+	ret = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_34A4);
+	if (ret & BIT(12))
+		R0_available = true;
+	else
+		R0_available = false;
+
+	return R0_available;
+}
+
+static void dp_tx_hdcp1x_set_repeater(struct mtk_hdcp_info *hdcp_info, bool enable)
+{
+	struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+
+	if (enable)
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4, BIT(15), BIT(15));
+	else
+		mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4, 0,  BIT(15));
+}
+
+void dp_tx_hdcp1x_set_start_auth(struct mtk_hdcp_info *hdcp_info, bool enable)
+{
+	hdcp_info->hdcp1x_info.enable = enable;
+
+	if (enable) {
+		hdcp_info->auth_status = AUTH_INIT;
+		hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_A0;
+		hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_IDLE;
+	} else {
+		hdcp_info->auth_status = AUTH_ZERO;
+		hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_H2;
+		hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_IDLE;
+		tee_hdcp_enable_encrypt(hdcp_info, false, HDCP_NONE);
+		dp_tx_hdcp1x_start_cipher(hdcp_info, false);
+		tee_hdcp1x_soft_rst(hdcp_info);
+	}
+
+	hdcp_info->hdcp1x_info.retry_count = 0;
+}
+
+bool dp_tx_hdcp1x_support(struct mtk_hdcp_info *hdcp_info)
+{
+	struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+	u8 tmp[2];
+	int ret;
+
+	drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BCAPS, tmp, 0x1);
+
+	hdcp_info->hdcp1x_info.enable = tmp[0x0] & BIT(0);
+	hdcp_info->hdcp1x_info.repeater = (tmp[0x0] & BIT(1)) >> 1;
+
+	DPTXHDCPMSG("1.x: CAPABLE: %d, Reapeater: %d\n",
+		    hdcp_info->hdcp1x_info.enable,
+		hdcp_info->hdcp1x_info.repeater);
+
+	if (!hdcp_info->hdcp1x_info.enable)
+		return false;
+
+	ret = tee_add_device(hdcp_info, HDCP_VERSION_1X);
+	if (ret != RET_SUCCESS) {
+		DPTXHDCPERR("1.x: HDCP TA has some error\n");
+		hdcp_info->hdcp1x_info.enable = false;
+	}
+
+	return hdcp_info->hdcp1x_info.enable;
+}
+
+static bool dp_tx_hdcp1x_init(struct mtk_hdcp_info *hdcp_info)
+{
+	u8 i;
+
+	hdcp_info->hdcp1x_info.ksv_ready = false;
+	hdcp_info->hdcp1x_info.r0_read = false;
+	hdcp_info->hdcp1x_info.b_status = 0x00;
+	for (i = 0; i < 5; i++) {
+		hdcp_info->hdcp1x_info.b_ksv[i] = 0x00;
+		hdcp_info->hdcp1x_info.a_ksv[i] = 0x00;
+	}
+
+	for (i = 0; i < 5; i++)
+		hdcp_info->hdcp1x_info.v[i] = 0x00;
+
+	hdcp_info->hdcp1x_info.b_info[0] = 0x00;
+	hdcp_info->hdcp1x_info.b_info[1] = 0x00;
+	hdcp_info->hdcp1x_info.max_cascade = false;
+	hdcp_info->hdcp1x_info.max_devs = false;
+	hdcp_info->hdcp1x_info.device_count = 0x00;
+
+	tee_hdcp_enable_encrypt(hdcp_info, false, HDCP_NONE);
+	dp_tx_hdcp1x_start_cipher(hdcp_info, false);
+	tee_hdcp1x_soft_rst(hdcp_info);
+
+	return true;
+}
+
+static bool dp_tx_hdcp1x_read_sink_b_ksv(struct mtk_hdcp_info *hdcp_info)
+{
+	struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+	u8 read_buffer[DRM_HDCP_KSV_LEN], i;
+
+	if (hdcp_info->hdcp1x_info.enable) {
+		drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BKSV, read_buffer, DRM_HDCP_KSV_LEN);
+
+		for (i = 0; i < DRM_HDCP_KSV_LEN; i++) {
+			hdcp_info->hdcp1x_info.b_ksv[i] = read_buffer[i];
+			DPTXHDCPMSG("1.x: Bksv = 0x%x\n", read_buffer[i]);
+		}
+	}
+
+	return true;
+}
+
+static bool dp_tx_hdcp1x_check_sink_ksv_ready(struct mtk_hdcp_info *hdcp_info)
+{
+	struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+	u8 read_buffer;
+
+	drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BSTATUS, &read_buffer, 1);
+
+	hdcp_info->hdcp1x_info.ksv_ready = (read_buffer & BIT(0))  ? true : false;
+
+	return hdcp_info->hdcp1x_info.ksv_ready;
+}
+
+static bool dp_tx_hdcp1x_check_sink_cap(struct mtk_hdcp_info *hdcp_info)
+{
+	struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+	u8  read_buffer[0x2];
+
+	drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BCAPS, read_buffer, 1);
+
+	hdcp_info->hdcp1x_info.repeater = (read_buffer[0] & BIT(1)) ? true : false;
+
+	return true;
+}
+
+static bool dp_tx_hdcp1x_read_sink_b_info(struct mtk_hdcp_info *hdcp_info)
+{
+	struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+	u8 read_buffer[DRM_HDCP_BSTATUS_LEN];
+
+	drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BINFO, read_buffer, DRM_HDCP_BSTATUS_LEN);
+
+	hdcp_info->hdcp1x_info.b_info[0] = read_buffer[0];
+	hdcp_info->hdcp1x_info.b_info[1] = read_buffer[1];
+	hdcp_info->hdcp1x_info.max_cascade = (read_buffer[1] & BIT(3)) ? true : false;
+	hdcp_info->hdcp1x_info.max_devs = (read_buffer[0] & BIT(7)) ? true : false;
+	hdcp_info->hdcp1x_info.device_count = read_buffer[0] & 0x7F;
+
+	DPTXHDCPMSG("1.x: Binfo max_cascade_EXCEEDED = %d\n", hdcp_info->hdcp1x_info.max_cascade);
+	DPTXHDCPMSG("1.x: Binfo DEPTH = %d\n", read_buffer[1] & 0x07);
+	DPTXHDCPMSG("1.x: Binfo max_devs_EXCEEDED = %d\n", hdcp_info->hdcp1x_info.max_devs);
+	DPTXHDCPMSG("1.x: Binfo device_count = %d\n", hdcp_info->hdcp1x_info.device_count);
+	return true;
+}
+
+static bool dp_tx_hdcp1x_read_sink_ksv(struct mtk_hdcp_info *hdcp_info, u8 dev_count)
+{
+	struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+	u8 i;
+	u8 times = dev_count / 3;
+	u8 remain = dev_count % 3;
+
+	if (times > 0) {
+		for (i = 0; i < times; i++)
+			drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_KSV_FIFO,
+					 hdcp_info->hdcp1x_info.ksvfifo + i * 15, 15);
+	}
+
+	if (remain > 0)
+		drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_KSV_FIFO,
+				 hdcp_info->hdcp1x_info.ksvfifo + times * 15, remain * 5);
+
+	DPTXHDCPMSG("1.x: Read ksvfifo = %x\n",	hdcp_info->hdcp1x_info.ksvfifo[0]);
+	DPTXHDCPMSG("1.x: Read ksvfifo = %x\n",	hdcp_info->hdcp1x_info.ksvfifo[1]);
+	DPTXHDCPMSG("1.x: Read ksvfifo = %x\n",	hdcp_info->hdcp1x_info.ksvfifo[2]);
+	DPTXHDCPMSG("1.x: Read ksvfifo = %x\n",	hdcp_info->hdcp1x_info.ksvfifo[3]);
+	DPTXHDCPMSG("1.x: Read ksvfifo = %x\n",	hdcp_info->hdcp1x_info.ksvfifo[4]);
+
+	return true;
+}
+
+static bool dp_tx_hdcp1x_read_sink_sha_v(struct mtk_hdcp_info *hdcp_info)
+{
+	struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+	u8 read_buffer[4], i, j;
+
+	for (i = 0; i < 5; i++) {
+		drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_V_PRIME(i), read_buffer, 4);
+		for (j = 0; j < 4; j++) {
+			hdcp_info->hdcp1x_info.v[(i * 4) + j] = read_buffer[3 - j];
+			DPTXHDCPMSG("1.x: Read sink V = %x\n",
+				    hdcp_info->hdcp1x_info.v[(i * 4) + j]);
+		}
+	}
+
+	return true;
+}
+
+static bool dp_tx_hdcp1x_auth_with_repeater(struct mtk_hdcp_info *hdcp_info)
+{
+	bool ret = false;
+	u8 *buffer = NULL;
+	u32 len = 0;
+	int tmp = 0;
+
+	if (hdcp_info->hdcp1x_info.device_count > HDCP1X_REP_MAXDEVS) {
+		DPTXHDCPERR("1.x: Repeater: %d DEVs!\n", hdcp_info->hdcp1x_info.device_count);
+		return false;
+	}
+
+	dp_tx_hdcp1x_read_sink_ksv(hdcp_info, hdcp_info->hdcp1x_info.device_count);
+	dp_tx_hdcp1x_read_sink_sha_v(hdcp_info);
+
+	len = hdcp_info->hdcp1x_info.device_count * DRM_HDCP_KSV_LEN + HDCP1X_B_INFO_LEN;
+	buffer = kmalloc(len, GFP_KERNEL);
+	if (!buffer) {
+		DPTXHDCPERR("1.x: Out of Memory\n");
+		return false;
+	}
+
+	memcpy(buffer, hdcp_info->hdcp1x_info.ksvfifo, len - HDCP1X_B_INFO_LEN);
+	memcpy(buffer + (len - HDCP1X_B_INFO_LEN), hdcp_info->hdcp1x_info.b_info,
+	       HDCP1X_B_INFO_LEN);
+	tmp = tee_hdcp1x_compute_compare_v(hdcp_info, buffer, len, hdcp_info->hdcp1x_info.v);
+	if (tmp == RET_COMPARE_PASS) {
+		DPTXHDCPMSG("1.x: Check V' PASS\n");
+		ret = true;
+	} else {
+		DPTXHDCPMSG("1.x: Check V' Fail\n");
+	}
+
+	kfree(buffer);
+	return ret;
+}
+
+static bool dp_tx_hdcp1x_verify_b_ksv(struct mtk_hdcp_info *hdcp_info)
+{
+	int i, j, k = 0;
+	u8 ksv;
+
+	for (i = 0; i < DRM_HDCP_KSV_LEN; i++) {
+		ksv = hdcp_info->hdcp1x_info.b_ksv[i];
+		for (j = 0; j < 8; j++)
+			k += (ksv >> j) & 0x01;
+	}
+
+	if (k != 20) {
+		DPTXHDCPERR("1.x: Check BKSV 20'1' 20'0' Fail\n");
+		return false;
+	}
+
+	return true;
+}
+
+static bool dp_tx_hdcp1x_write_a_ksv(struct mtk_hdcp_info *hdcp_info)
+{
+	struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+	u8 tmp;
+	int i, k, j;
+
+	tee_get_aksv(hdcp_info, hdcp_info->hdcp1x_info.a_ksv);
+	drm_dp_dpcd_write(&mtk_dp->aux, DP_AUX_HDCP_AKSV, hdcp_info->hdcp1x_info.a_ksv,
+			  DRM_HDCP_KSV_LEN);
+
+	for (i = 0, k = 0; i < DRM_HDCP_KSV_LEN; i++) {
+		tmp = hdcp_info->hdcp1x_info.a_ksv[i];
+
+		for (j = 0; j < 8; j++)
+			k += (tmp >> j) & 0x01;
+		DPTXHDCPMSG("1.x: Aksv 0x%x\n", tmp);
+	}
+
+	if (k != 20) {
+		DPTXHDCPERR("1.x: Check AKSV 20'1' 20'0' Fail\n");
+		return false;
+	}
+
+	return true;
+}
+
+static void dp_tx_hdcp1x_write_an(struct mtk_hdcp_info *hdcp_info)
+{
+	struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+	u8 an_value[DRM_HDCP_AN_LEN] = { /* on DP Spec p99 */
+		0x03, 0x04, 0x07, 0x0C, 0x13, 0x1C, 0x27, 0x34};
+
+	tee_hdcp1x_set_tx_an(hdcp_info, an_value);
+	drm_dp_dpcd_write(&mtk_dp->aux, DP_AUX_HDCP_AN, an_value, DRM_HDCP_AN_LEN);
+	mdelay(5);
+}
+
+static bool dp_tx_hdcp1x_check_r0(struct mtk_hdcp_info *hdcp_info)
+{
+	struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+	u8 value[DRM_HDCP_BSTATUS_LEN];
+	u8 retry_count = 0;
+	bool sink_R0_available = false;
+	bool ret;
+	int tmp;
+
+	ret = dp_tx_hdcp1x_get_r0_available(hdcp_info);
+	if (!ret) {
+		DPTXHDCPERR("1.x: ERR: R0 No Available\n");
+		return false;
+	}
+
+	if (!hdcp_info->hdcp1x_info.r0_read) {
+		drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BSTATUS, value, 1);
+		sink_R0_available = ((value[0x0] & BIT(1)) == BIT(1)) ? true : false;
+
+		if (!sink_R0_available) {
+			drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BSTATUS, value, 1);
+			sink_R0_available = ((value[0x0] & BIT(1)) == BIT(1)) ? true : false;
+
+			if (!sink_R0_available)
+				return false;
+		}
+	}
+
+	while (retry_count < 3) {
+		drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_RI_PRIME, value, DRM_HDCP_RI_LEN);
+
+		tmp = tee_compare_r0(hdcp_info, value, DRM_HDCP_RI_LEN);
+		if (tmp == RET_COMPARE_PASS)
+			return true;
+
+		DPTXHDCPMSG("1.x: R0 check FAIL:Rx_R0=0x%x%x\n", value[0x1], value[0x0]);
+		mdelay(5);
+
+		retry_count++;
+	}
+	return false;
+}
+
+static void dp_tx_hdcp1x_state_rst(struct mtk_hdcp_info *hdcp_info)
+{
+	DPTXHDCPMSG("1.x: Before State Reset:(M : S)= (%d, %d)",
+		    hdcp_info->hdcp1x_info.main_states,
+		hdcp_info->hdcp1x_info.sub_states);
+	hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_A0;
+	hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_IDLE;
+}
+
+void dp_tx_hdcp1x_fsm(struct mtk_hdcp_info *hdcp_info)
+{
+	static int pre_main, pre_sub;
+	static u32 pre_time;
+	u32 time;
+	bool ret;
+
+	if (pre_main != hdcp_info->hdcp1x_info.main_states ||
+	    hdcp_info->hdcp1x_info.sub_states != pre_sub) {
+		DPTXHDCPMSG("1.x: State(M : S)= (%d, %d)",
+			    hdcp_info->hdcp1x_info.main_states,
+			hdcp_info->hdcp1x_info.sub_states);
+		pre_main = hdcp_info->hdcp1x_info.main_states;
+		pre_sub = hdcp_info->hdcp1x_info.sub_states;
+	}
+
+	switch (hdcp_info->hdcp1x_info.main_states) {
+	case HDCP1X_main_state_H2:
+		/* HDCP1X_main_state_H2 */
+		/* HDCP1X_sub_FSM_auth_fail */
+		if (hdcp_info->hdcp1x_info.sub_states == HDCP1X_sub_FSM_auth_fail) {
+			tee_hdcp_enable_encrypt(hdcp_info, false, HDCP_NONE);
+			DPTXHDCPMSG("1.x: Authentication Fail\n");
+			hdcp_info->auth_status = AUTH_FAIL;
+			hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_H2;
+			hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_IDLE;
+		}
+		break;
+
+	case HDCP1X_main_state_A0:
+		/* HDCP1X_main_state_A0 */
+		/* HDCP1X_sub_FSM_IDLE */
+		if (hdcp_info->hdcp1x_info.sub_states == HDCP1X_sub_FSM_IDLE) {
+			if (hdcp_info->hdcp1x_info.retry_count > HDCP1X_REAUNTH_COUNT) {
+				DPTXHDCPMSG("1.x: Too much retry!\n");
+				hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_H2;
+				hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_auth_fail;
+				break;
+			}
+
+			dp_tx_hdcp1x_init(hdcp_info);
+			hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_A0;
+			hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_CHECKHDCPCAPABLE;
+		}
+
+		/* HDCP1X_main_state_A0 */
+		/* HDCP1X_sub_FSM_CHECKHDCPCAPABLE */
+		if (hdcp_info->hdcp1x_info.sub_states == HDCP1X_sub_FSM_CHECKHDCPCAPABLE) {
+			if (!hdcp_info->hdcp1x_info.enable) {
+				dp_tx_hdcp1x_state_rst(hdcp_info);
+				break;
+			}
+
+			hdcp_info->hdcp1x_info.retry_count++;
+			hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_A1;
+			hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_exchange_KSV;
+		}
+		break;
+
+	case HDCP1X_main_state_A1:
+		/* HDCP1X_main_state_A1 */
+		/* HDCP1X_sub_FSM_exchange_KSV */
+		if (hdcp_info->hdcp1x_info.sub_states == HDCP1X_sub_FSM_exchange_KSV) {
+			dp_tx_hdcp1x_write_an(hdcp_info);
+			ret = dp_tx_hdcp1x_write_a_ksv(hdcp_info);
+			if (!ret) {
+				dp_tx_hdcp1x_state_rst(hdcp_info);
+				break;
+			}
+
+			pre_time = mtk_dp_get_system_time();
+			hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_A1;
+			hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_verify_bksv;
+		}
+
+		/* HDCP1X_main_state_A1 */
+		/* HDCP1X_sub_FSM_verify_bksv */
+		if (hdcp_info->hdcp1x_info.sub_states == HDCP1X_sub_FSM_verify_bksv) {
+			dp_tx_hdcp1x_read_sink_b_ksv(hdcp_info);
+			dp_tx_hdcp1x_set_repeater(hdcp_info, hdcp_info->hdcp1x_info.repeater);
+
+			time = mtk_dp_get_time_diff(pre_time);
+			if (time >= HDCP1X_BSTATUS_TIMEOUT_CNT) {
+				dp_tx_hdcp1x_state_rst(hdcp_info);
+				break;
+			}
+
+			pre_time = mtk_dp_get_system_time();
+			ret = dp_tx_hdcp1x_verify_b_ksv(hdcp_info);
+			if (!ret) {
+				dp_tx_hdcp1x_state_rst(hdcp_info);
+				DPTXHDCPMSG("1.x: Invalid BKSV!!\n");
+				break;
+			}
+
+			hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_A2;
+			hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_computation;
+		}
+		break;
+
+	case HDCP1X_main_state_A2:
+		/* HDCP1X_main_state_A2 */
+		/* HDCP1X_sub_FSM_computation */
+		if (hdcp_info->hdcp1x_info.sub_states == HDCP1X_sub_FSM_computation) {
+			tee_calculate_lm(hdcp_info, hdcp_info->hdcp1x_info.b_ksv);
+			dp_tx_hdcp1x_start_cipher(hdcp_info, true);
+			hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_A3;
+			hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_check_R0;
+			pre_time = mtk_dp_get_system_time();
+		}
+		break;
+
+	case HDCP1X_main_state_A3:
+		/* HDCP1X_main_state_A3 */
+		/* HDCP1X_sub_FSM_check_R0 */
+		if (hdcp_info->hdcp1x_info.sub_states == HDCP1X_sub_FSM_check_R0) {
+			/* Wait 100ms(at least) before check R0 */
+			time = mtk_dp_get_time_diff(pre_time);
+			if (time < HDCP1X_R0_WDT && !hdcp_info->hdcp1x_info.r0_read) {
+				mdelay(10);
+				break;
+			}
+
+			ret = dp_tx_hdcp1x_check_r0(hdcp_info);
+			if (!ret) {
+				dp_tx_hdcp1x_state_rst(hdcp_info);
+				break;
+			}
+
+			tee_hdcp_enable_encrypt(hdcp_info, true, HDCP_V1);
+			hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_A5;
+			hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_IDLE;
+		}
+		break;
+
+	case HDCP1X_main_state_A4:
+		/* HDCP1X_main_state_A4 */
+		/* HDCP1X_sub_FSM_auth_done */
+		if (hdcp_info->hdcp1x_info.sub_states == HDCP1X_sub_FSM_auth_done) {
+			DPTXHDCPMSG("1.x: Authentication done!\n");
+			hdcp_info->hdcp1x_info.retry_count = 0;
+			hdcp_info->auth_status = AUTH_PASS;
+			hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_A4;
+			hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_IDLE;
+
+			/* unmute */
+		}
+		break;
+
+	case HDCP1X_main_state_A5:
+		/* HDCP1X_main_state_A5 */
+		/* HDCP1X_sub_FSM_IDLE */
+		if (hdcp_info->hdcp1x_info.sub_states == HDCP1X_sub_FSM_IDLE) {
+			dp_tx_hdcp1x_check_sink_cap(hdcp_info);
+			if (!hdcp_info->hdcp1x_info.repeater) {
+				DPTXHDCPMSG("1.x: No Repeater!\n");
+				hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_A4;
+				hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_auth_done;
+				break;
+			}
+
+			DPTXHDCPMSG("1.x: Repeater!\n");
+			pre_time = mtk_dp_get_system_time();
+			hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_A6;
+			hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_polling_rdy_bit;
+		}
+		break;
+
+	case HDCP1X_main_state_A6:
+		/* HDCP1X_main_state_A6 */
+		/* HDCP1X_sub_FSM_polling_rdy_bit */
+		if (hdcp_info->hdcp1x_info.sub_states == HDCP1X_sub_FSM_polling_rdy_bit) {
+			time = mtk_dp_get_time_diff(pre_time);
+			if (time > HDCP1X_REP_RDY_WDT) {
+				dp_tx_hdcp1x_state_rst(hdcp_info);
+				break;
+			}
+
+			time = mtk_dp_get_time_diff(pre_time);
+			if (!hdcp_info->hdcp1x_info.ksv_ready && time > HDCP1X_REP_RDY_WDT / 2)
+				dp_tx_hdcp1x_check_sink_ksv_ready(hdcp_info);
+
+			if (hdcp_info->hdcp1x_info.ksv_ready) {
+				dp_tx_hdcp1x_read_sink_b_info(hdcp_info);
+				hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_A7;
+				hdcp_info->hdcp1x_info.sub_states =
+					HDCP1X_sub_FSM_auth_with_repeater;
+				hdcp_info->hdcp1x_info.ksv_ready = false;
+			}
+		}
+		break;
+
+	case HDCP1X_main_state_A7:
+		/* HDCP1X_main_state_A7 */
+		/* HDCP1X_sub_FSM_auth_with_repeater */
+		if (hdcp_info->hdcp1x_info.sub_states == HDCP1X_sub_FSM_auth_with_repeater) {
+			if (hdcp_info->hdcp1x_info.max_cascade || hdcp_info->hdcp1x_info.max_devs) {
+				DPTXHDCPERR("1.x: MAX CASCADE or MAX DEVS!\n");
+				dp_tx_hdcp1x_state_rst(hdcp_info);
+				break;
+			}
+
+			ret = dp_tx_hdcp1x_auth_with_repeater(hdcp_info);
+			if (!ret) {
+				dp_tx_hdcp1x_state_rst(hdcp_info);
+				break;
+			}
+
+			hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_A4;
+			hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_auth_done;
+		}
+		break;
+
+	default:
+		break;
+	}
+}
diff --git a/drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h b/drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h
new file mode 100644
index 000000000000..4787c5bd876a
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019-2024 MediaTek Inc.
+ */
+
+#ifndef _MTK_DP_HDCP1X_H_
+#define _MTK_DP_HDCP1X_H_
+
+#include "tlc_dp_hdcp.h"
+
+#define HDCP1X_BSTATUS_TIMEOUT_CNT              600
+#define HDCP1X_R0_WDT                           100
+#define HDCP1X_REP_RDY_WDT                      5000
+
+#define HDCP1X_REAUNTH_COUNT          3
+
+enum DPTX_DRV_HDCP1X_main_states {
+	HDCP1X_main_state_H2 = 0,
+	HDCP1X_main_state_A0 = 1,
+	HDCP1X_main_state_A1 = 2,
+	HDCP1X_main_state_A2 = 3,
+	HDCP1X_main_state_A3 = 4,
+	HDCP1X_main_state_A4 = 5,
+	HDCP1X_main_state_A5 = 6,
+	HDCP1X_main_state_A6 = 7,
+	HDCP1X_main_state_A7 = 8,
+};
+
+enum DPTX_DRV_HDCP1X_sub_states {
+	HDCP1X_sub_FSM_IDLE              = 0,
+	HDCP1X_sub_FSM_CHECKHDCPCAPABLE	= 1,
+	HDCP1X_sub_FSM_exchange_KSV       = 2,
+	HDCP1X_sub_FSM_verify_bksv        = 3,
+	HDCP1X_sub_FSM_computation       = 4,
+	HDCP1X_sub_FSM_check_R0           = 5,
+	HDCP1X_sub_FSM_auth_done          = 6,
+	HDCP1X_sub_FSM_polling_rdy_bit     = 7,
+	HDCP1X_sub_FSM_auth_with_repeater  = 8,
+	HDCP1X_sub_FSM_auth_fail          = 9,
+};
+
+bool dp_tx_hdcp1x_support(struct mtk_hdcp_info *hdcp_info);
+void dp_tx_hdcp1x_fsm(struct mtk_hdcp_info *hdcp_info);
+void dp_tx_hdcp1x_set_start_auth(struct mtk_hdcp_info *hdcp_info, bool enable);
+
+#endif /* _MTK_DP_HDCP1X_H_ */
diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
index 5cf5059762ed..4481c853c375 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp_reg.h
+++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
@@ -276,6 +276,9 @@
 #define MTK_DP_TRANS_P0_3430				0x3430
 #define HPD_INT_THD_ECO_DP_TRANS_P0_MASK			GENMASK(1, 0)
 #define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT		BIT(1)
+#define MTK_DP_TRANS_P0_3480                0x3480
+#define REQ_BLOCK_CIPHER_AUTH                           BIT(12)
+#define KM_GENERATED                                    BIT(4)
 #define MTK_DP_TRANS_P0_34A4				0x34a4
 #define LANE_NUM_DP_TRANS_P0_MASK				GENMASK(3, 2)
 #define MTK_DP_TRANS_P0_34D0                0x34D0
-- 
2.43.0




More information about the linux-arm-kernel mailing list