Add tee client application, HDCP 1.x and 2.x authentication for DisplayPort
to support the HDCP feature.
mac.shen (3):
Subject: [PATCH] drm/mediatek/dp: Add tee client application for HDCP
feature
Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort
Subject: [PATCH] drm/mediatek/dp: Add HDCP1.x feature for DisplayPort
drivers/gpu/drm/mediatek/Makefile | 7 +-
drivers/gpu/drm/mediatek/mtk_dp.c | 324 +++++--
drivers/gpu/drm/mediatek/mtk_dp.h | 99 +++
drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.c | 589 +++++++++++++
drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h | 46 +
drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c | 1021 ++++++++++++++++++++++
drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h | 52 ++
drivers/gpu/drm/mediatek/mtk_dp_reg.h | 7 +-
drivers/gpu/drm/mediatek/mtk_dpi.c | 3 +
drivers/gpu/drm/mediatek/tci.h | 156 ++++
drivers/gpu/drm/mediatek/tlc_dp_hdcp.c | 598 +++++++++++++
drivers/gpu/drm/mediatek/tlc_dp_hdcp.h | 414 +++++++++
12 files changed, 3230 insertions(+), 86 deletions(-)
create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.h
create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.c
create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h
create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c
create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h
create mode 100644 drivers/gpu/drm/mediatek/tci.h
create mode 100644 drivers/gpu/drm/mediatek/tlc_dp_hdcp.c
create mode 100644 drivers/gpu/drm/mediatek/tlc_dp_hdcp.h
--
2.43.0
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
[email protected]/
Signed-off-by: mac.shen <[email protected]>
---
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
Add tee client application which will be used for
HDCP 1.x and 2.x authentication in DisplayPort.
Changes in v2:
- remove ca folder, and change file name with lower case
- refine the tci_t structure to make the data to tee can
through this structure
- remove aux and regs from mtk_hdcp_info structure
- remove some definitions, and use the definitions in
include/drm/drm_hdcp.h
- remove useless code
per suggestion from the previous thread:
https://lore.kernel.org/all/8fff59b5567449d8201dd1138c8fa
[email protected]/
Signed-off-by: mac.shen <[email protected]>
---
drivers/gpu/drm/mediatek/Makefile | 5 +-
drivers/gpu/drm/mediatek/tci.h | 156 +++++++
drivers/gpu/drm/mediatek/tlc_dp_hdcp.c | 598 +++++++++++++++++++++++++
drivers/gpu/drm/mediatek/tlc_dp_hdcp.h | 414 +++++++++++++++++
4 files changed, 1172 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/drm/mediatek/tci.h
create mode 100644 drivers/gpu/drm/mediatek/tlc_dp_hdcp.c
create mode 100644 drivers/gpu/drm/mediatek/tlc_dp_hdcp.h
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index d4d193f60271..c80e6c2f9336 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -26,4 +26,7 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
-obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o
+mtk-dp-objs := tlc_dp_hdcp.o \
+ mtk_dp.o
+
+obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk-dp.o
diff --git a/drivers/gpu/drm/mediatek/tci.h b/drivers/gpu/drm/mediatek/tci.h
new file mode 100644
index 000000000000..f2239ea3ffbf
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/tci.h
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019-2024 MediaTek Inc.
+ */
+
+#ifndef _TCI_H_
+#define _TCI_H_
+
+#include <drm/display/drm_hdcp.h>
+
+#define CMD_DEVICE_ADDED 1
+#define CMD_DEVICE_REMOVE 2
+#define CMD_WRITE_VAL 3
+#define CMD_DEVICE_CLEAN 4
+#define CMD_ENABLE_ENCRYPT 5
+
+/* V1.3 */
+#define CMD_CALCULATE_LM 11
+#define CMD_COMPARE_R0 12
+#define CMD_COMPARE_V1 13
+#define CMD_GET_AKSV 14
+
+/* V2.2 */
+#define CMD_AKE_CERTIFICATE 20
+#define CMD_ENC_KM 21
+#define CMD_AKE_H_PRIME 22
+#define CMD_AKE_PARING 23
+#define CMD_LC_L_PRIME 24
+#define CMD_COMPARE_L 25
+#define CMD_SKE_CAL_EKS 26
+
+#define CMD_COMPARE_V2 27
+#define CMD_COMPARE_M 28
+
+/* Need remove in furture */
+#define CMD_LOAD_KEY 50
+
+#define RET_COMPARE_PASS 0
+#define RET_COMPARE_FAIL 1
+#define RET_NEW_DEVICE 2
+#define RET_STORED_DEVICE 3
+
+#define TYPE_HDCP_PARAM_AN 10
+#define TYPE_HDCP_PARAM_RST_1 11
+#define TYPE_HDCP_PARAM_RST_2 12
+#define TYPE_HDCP_ENABLE_ENCRYPT 13
+#define TYPE_HDCP_DISABLE_ENCRYPT 14
+
+#define TYPE_HDCP13_KEY 20
+#define TYPE_HDCP22_KEY 21
+
+// reserved:2
+#define HDCP2_CERTRX_LEN (HDCP_2_2_RECEIVER_ID_LEN + HDCP_2_2_K_PUB_RX_LEN + \
+ 2 + HDCP_2_2_DCP_LLC_SIG_LEN)
+// version:1
+#define HDCP_2_2_TXCAPS_LEN (HDCP_2_2_TXCAP_MASK_LEN + 1)
+#define PARAM_LEN 1024
+
+#define TCI_LENGTH sizeof(struct tci_t)
+
+struct cmd_hdcp_init_for_verion_t {
+ u32 version;
+ bool need_load_key;
+};
+
+struct cmd_hdcp_write_val_t {
+ u8 type;
+ u8 len;
+ u8 val[DRM_HDCP_AN_LEN];
+};
+
+struct cmd_hdcp_calculate_lm_t {
+ u8 bksv[DRM_HDCP_KSV_LEN];
+};
+
+struct cmd_hdcp_get_aksv_t {
+ u8 aksv[DRM_HDCP_KSV_LEN];
+};
+
+struct cmd_hdcp_ake_certificate_t {
+ u8 certification[HDCP2_CERTRX_LEN];
+ bool stored;
+ u8 m[HDCP_2_2_E_KH_KM_M_LEN - HDCP_2_2_E_KH_KM_LEN];
+ u8 ekm[HDCP_2_2_E_KH_KM_LEN];
+};
+
+struct cmd_hdcp_ake_paring_t {
+ u8 ekm[HDCP_2_2_E_KH_KM_LEN];
+};
+
+struct cmd_hdcp_enc_km_t {
+ u8 enc_km[HDCP_2_2_E_KPUB_KM_LEN];
+};
+
+struct cmd_hdcp_ake_h_prime_t {
+ u8 rtx[HDCP_2_2_RTX_LEN];
+ u8 rrx[HDCP_2_2_RRX_LEN];
+ u8 rx_caps[HDCP_2_2_RXCAPS_LEN];
+ u8 tx_caps[HDCP_2_2_TXCAPS_LEN];
+ u32 rx_h_len;
+ u8 rx_h[HDCP_2_2_H_PRIME_LEN];
+};
+
+struct cmd_hdcp_lc_l_prime_t {
+ u8 rn[HDCP_2_2_RN_LEN];
+ u32 rx_l_len;
+ u8 rx_l[HDCP_2_2_L_PRIME_LEN];
+};
+
+struct cmd_hdcp_ske_eks_t {
+ u8 riv[HDCP_2_2_RIV_LEN];
+ u32 eks_len;
+ u32 eks;
+};
+
+struct cmd_hdcp_compare_t {
+ u32 rx_val_len;
+ u8 rx_val[HDCP_2_2_MPRIME_LEN];
+ u32 param_len;
+ u8 param[PARAM_LEN];
+ u32 out_len;
+ u32 out;
+};
+
+union tci_cmd_body_t {
+ /* Init with special HDCP version */
+ struct cmd_hdcp_init_for_verion_t cmd_hdcp_init_for_verion;
+ /* Write uint32 data to hw */
+ struct cmd_hdcp_write_val_t cmd_hdcp_write_val;
+ /* Get aksv */
+ struct cmd_hdcp_get_aksv_t cmd_hdcp_get_aksv;
+ /* Calculate r0 */
+ struct cmd_hdcp_calculate_lm_t cmd_hdcp_calculate_lm;
+ /* Generate signature for certificate */
+ struct cmd_hdcp_ake_certificate_t cmd_hdcp_ake_certificate;
+ /* To store ekm */
+ struct cmd_hdcp_ake_paring_t cmd_hdcp_ake_paring;
+ /* Encrypt km for V2.2 */
+ struct cmd_hdcp_enc_km_t cmd_hdcp_enc_km;
+ /* Compute H prime */
+ struct cmd_hdcp_ake_h_prime_t cmd_hdcp_ake_h_prime;
+ /* Compute L prime */
+ struct cmd_hdcp_lc_l_prime_t cmd_hdcp_lc_l_prime;
+ /* Compute eks */
+ struct cmd_hdcp_ske_eks_t cmd_hdcp_ske_eks;
+ /* Compare */
+ struct cmd_hdcp_compare_t cmd_hdcp_compare;
+} __packed;
+
+struct tci_t {
+ u32 command_id;
+ u32 return_code;
+ union tci_cmd_body_t cmd_body;
+};
+
+#endif /* _TCI_H_ */
diff --git a/drivers/gpu/drm/mediatek/tlc_dp_hdcp.c b/drivers/gpu/drm/mediatek/tlc_dp_hdcp.c
new file mode 100644
index 000000000000..147329324648
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/tlc_dp_hdcp.c
@@ -0,0 +1,598 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019-2024 MediaTek Inc.
+ */
+
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "tlc_dp_hdcp.h"
+
+#define DEFAULT_WRITE_VAL_LEN 1
+#define DEFAULT_WRITE_VAL 0
+
+/*
+ * TA_FTPM_UUID: 99975014-3c7c-54ea-8487-a80d215ea92c
+ *
+ * Randomly generated, and must correspond to the GUID on the TA side.
+ * Defined here in the reference implementation:
+ * https://github.com/microsoft/ms-tpm-20-ref/blob/master/Samples/ARM32-FirmwareTPM/optee_ta/fTPM/include/fTPM.h#L42
+ */
+static const uuid_t dp_ta_uuid =
+ UUID_INIT(0x99975014, 0x3c7c, 0x54ea,
+ 0x84, 0x87, 0xa8, 0x0d, 0x21, 0x5e, 0xa9, 0x2c);
+
+/**
+ * dp_tee_op_send() - send dp commands through the TEE shared memory.
+ * @len: the number of bytes to send.
+ *
+ * Return:
+ * In case of success, returns 0.
+ * On failure, -errno
+ */
+static int dp_tee_op_send(struct dp_tee_private *dp_tee_priv, size_t len, u32 cmd_id)
+{
+ int rc;
+ u8 *temp_buf;
+ struct tee_ioctl_invoke_arg transceive_args;
+ struct tee_param command_params[4];
+ struct tee_shm *shm = dp_tee_priv->shm;
+
+ if (len > MAX_COMMAND_SIZE) {
+ TLCERR("%s: len=%zd exceeds MAX_COMMAND_SIZE supported by dp TA\n", __func__, len);
+ return -EIO;
+ }
+
+ memset(&transceive_args, 0, sizeof(transceive_args));
+ memset(command_params, 0, sizeof(command_params));
+ dp_tee_priv->resp_len = 0;
+
+ /* Invoke FTPM_OPTEE_TA_SUBMIT_COMMAND function of dp TA */
+ transceive_args = (struct tee_ioctl_invoke_arg) {
+ .func = cmd_id,
+ .session = dp_tee_priv->session,
+ .num_params = 4,
+ };
+
+ /* Fill FTPM_OPTEE_TA_SUBMIT_COMMAND parameters */
+ command_params[0] = (struct tee_param) {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
+ .u.memref = {
+ .shm = shm,
+ .size = len,
+ .shm_offs = 0,
+ },
+ };
+
+ command_params[1] = (struct tee_param) {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT,
+ .u.memref = {
+ .shm = shm,
+ .size = MAX_RESPONSE_SIZE,
+ .shm_offs = MAX_COMMAND_SIZE,
+ },
+ };
+
+ rc = tee_client_invoke_func(dp_tee_priv->ctx, &transceive_args, command_params);
+ if (rc < 0 || transceive_args.ret != 0) {
+ TLCERR("%s: invoke error: 0x%x\n", __func__, transceive_args.ret);
+ return (rc < 0) ? rc : transceive_args.ret;
+ }
+
+ temp_buf = tee_shm_get_va(shm, command_params[1].u.memref.shm_offs);
+ if (IS_ERR(temp_buf)) {
+ TLCERR("%s: tee_shm_get_va failed for receive\n", __func__);
+ return PTR_ERR(temp_buf);
+ }
+
+ /* Sanity checks look good, cache the response */
+ memcpy(dp_tee_priv->resp_buf, temp_buf, MAX_RESPONSE_SIZE / 2);
+ dp_tee_priv->resp_len = MAX_RESPONSE_SIZE / 2;
+
+ return 0;
+}
+
+/*
+ * Check whether this driver supports the dp TA in the TEE instance
+ * represented by the params (ver/data) to this function.
+ */
+static int dp_tee_match(struct tee_ioctl_version_data *ver, const void *data)
+{
+ /*
+ * Currently this driver only support GP Complaint OPTEE based dp TA
+ */
+ if (ver->impl_id == TEE_IMPL_ID_OPTEE && ver->gen_caps & TEE_GEN_CAP_GP)
+ return 1;
+ else
+ return 0;
+}
+
+int tee_add_device(struct mtk_hdcp_info *hdcp_info, u32 version)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = NULL;
+ struct tee_ioctl_open_session_arg sess_arg;
+ struct tci_t *tci;
+
+ if (hdcp_info->g_init)
+ tee_remove_device(hdcp_info);
+
+ dp_tee_priv = kzalloc(sizeof(*dp_tee_priv), GFP_KERNEL);
+ if (!dp_tee_priv) {
+ kfree(dp_tee_priv);
+ TLCERR("%s: tee_alloc_memory failed\n", __func__);
+ return -ENOMEM;
+ }
+ hdcp_info->g_dp_tee_priv = dp_tee_priv;
+
+ /* Open context with TEE driver */
+ dp_tee_priv->ctx = tee_client_open_context(NULL, dp_tee_match, NULL, NULL);
+ if (IS_ERR(dp_tee_priv->ctx)) {
+ if (PTR_ERR(dp_tee_priv->ctx) == -ENOENT) {
+ kfree(dp_tee_priv);
+ return -EPROBE_DEFER;
+ }
+ kfree(dp_tee_priv);
+ TLCERR("%s: tee_client_open_context failed\n", __func__);
+ return PTR_ERR(dp_tee_priv->ctx);
+ }
+
+ /* Open a session with dp TA */
+ memset(&sess_arg, 0, sizeof(sess_arg));
+ export_uuid(sess_arg.uuid, &dp_ta_uuid);
+ sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
+ sess_arg.num_params = 0;
+
+ rc = tee_client_open_session(dp_tee_priv->ctx, &sess_arg, NULL);
+ if (rc < 0 || sess_arg.ret != 0) {
+ kfree(dp_tee_priv);
+ TLCERR("tee_client_open_session failed, err=%x\n", sess_arg.ret);
+ rc = -EINVAL;
+ goto out_tee_session;
+ }
+ dp_tee_priv->session = sess_arg.session;
+
+ /* Allocate dynamic shared memory with dp TA */
+ dp_tee_priv->shm = tee_shm_alloc_kernel_buf(dp_tee_priv->ctx, MAX_COMMAND_SIZE
+ + MAX_RESPONSE_SIZE);
+ if (IS_ERR(dp_tee_priv->shm)) {
+ kfree(dp_tee_priv);
+ TLCERR("%s: tee_shm_alloc_kernel_buf failed\n", __func__);
+ rc = -ENOMEM;
+ goto out_shm_alloc;
+ }
+ TLCINFO("Register 8k share memory successfully, (%p)",
+ dp_tee_priv->shm->kaddr);
+
+ /* Copy parameter for add new device */
+ tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+ memset(tci, 0, TCI_LENGTH);
+ tci->command_id = CMD_DEVICE_ADDED;
+ tci->cmd_body.cmd_hdcp_init_for_verion.version = version;
+ tci->cmd_body.cmd_hdcp_init_for_verion.need_load_key = true;
+
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_DEVICE_ADDED);
+ if (rc != 0) {
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+ tee_remove_device(hdcp_info);
+ return rc;
+ }
+
+ hdcp_info->g_init = true;
+
+ return rc;
+
+out_shm_alloc:
+ tee_client_close_session(dp_tee_priv->ctx, dp_tee_priv->session);
+out_tee_session:
+ tee_client_close_context(dp_tee_priv->ctx);
+
+ return rc;
+}
+
+void tee_remove_device(struct mtk_hdcp_info *hdcp_info)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+
+ if (!hdcp_info->g_init)
+ return;
+
+ hdcp_info->g_init = false;
+ memset(tci, 0, TCI_LENGTH);
+ tci->command_id = CMD_DEVICE_REMOVE;
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_DEVICE_REMOVE);
+ if (rc != 0)
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+
+ /* Free the shared memory pool */
+ tee_shm_free(dp_tee_priv->shm);
+
+ /* Close the existing session with fTPM TA */
+ tee_client_close_session(dp_tee_priv->ctx, dp_tee_priv->session);
+
+ /* Close the context with TEE driver */
+ tee_client_close_context(dp_tee_priv->ctx);
+
+ /* Free the memory */
+ kfree(dp_tee_priv);
+}
+
+int tee_clear_paring(struct mtk_hdcp_info *hdcp_info)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+
+ /* Copy parameters */
+ memset(tci, 0, TCI_LENGTH);
+ tci->command_id = CMD_DEVICE_CLEAN;
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_DEVICE_CLEAN);
+ if (rc != 0) {
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+int tee_hdcp1x_set_tx_an(struct mtk_hdcp_info *hdcp_info, u8 *an_code)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+
+ /* Copy parameters */
+ tci->command_id = CMD_WRITE_VAL;
+ tci->cmd_body.cmd_hdcp_write_val.len = DRM_HDCP_AN_LEN;
+ tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_PARAM_AN;
+ memcpy(tci->cmd_body.cmd_hdcp_write_val.val, an_code, DRM_HDCP_AN_LEN);
+
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_WRITE_VAL);
+ if (rc != 0) {
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+int tee_hdcp_enable_encrypt(struct mtk_hdcp_info *hdcp_info, bool enable, u8 version)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+
+ /* Copy parameters */
+ tci->command_id = CMD_ENABLE_ENCRYPT;
+ if (enable)
+ tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_ENABLE_ENCRYPT;
+ else
+ tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_DISABLE_ENCRYPT;
+
+ /* Set HDCP version supportted by device */
+ tci->cmd_body.cmd_hdcp_write_val.len = 1;
+ tci->cmd_body.cmd_hdcp_write_val.val[0] = version;
+
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_ENABLE_ENCRYPT);
+ if (rc != 0) {
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+int tee_hdcp1x_soft_rst(struct mtk_hdcp_info *hdcp_info)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+
+ /* Copy parameters */
+ tci->command_id = CMD_WRITE_VAL;
+ tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_PARAM_RST_1;
+ /* No need input. Set default value 0 for check */
+ tci->cmd_body.cmd_hdcp_write_val.len = DEFAULT_WRITE_VAL_LEN;
+ memset(tci->cmd_body.cmd_hdcp_write_val.val, DEFAULT_WRITE_VAL, DEFAULT_WRITE_VAL_LEN);
+
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_WRITE_VAL);
+ if (rc != 0) {
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+int tee_hdcp2_soft_rst(struct mtk_hdcp_info *hdcp_info)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+
+ tci->command_id = CMD_WRITE_VAL;
+ tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_PARAM_RST_2;
+ /* No need input. Set default value 0 for check */
+ tci->cmd_body.cmd_hdcp_write_val.len =
+ DEFAULT_WRITE_VAL_LEN;
+ memset(tci->cmd_body.cmd_hdcp_write_val.val, DEFAULT_WRITE_VAL, DEFAULT_WRITE_VAL_LEN);
+
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_WRITE_VAL);
+ if (rc != 0) {
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+/** V1.X **/
+int tee_get_aksv(struct mtk_hdcp_info *hdcp_info, u8 *aksv)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+
+ /* Copy parameters */
+ tci->command_id = CMD_GET_AKSV;
+
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_GET_AKSV);
+ if (rc != 0) {
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+ return rc;
+ }
+
+ tci = (struct tci_t *)dp_tee_priv->resp_buf;
+ memcpy(aksv, tci->cmd_body.cmd_hdcp_get_aksv.aksv, DRM_HDCP_KSV_LEN);
+
+ return rc;
+}
+
+int tee_calculate_lm(struct mtk_hdcp_info *hdcp_info, u8 *bksv)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+
+ /* Copy parameters */
+ tci->command_id = CMD_CALCULATE_LM;
+ memcpy(tci->cmd_body.cmd_hdcp_calculate_lm.bksv, bksv, DRM_HDCP_KSV_LEN);
+
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_CALCULATE_LM);
+ if (rc != 0) {
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+int tee_compare_r0(struct mtk_hdcp_info *hdcp_info, u8 *r0, u32 len)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+
+ /* Copy parameters */
+ tci->command_id = CMD_COMPARE_R0;
+ tci->cmd_body.cmd_hdcp_compare.rx_val_len = len;
+ memcpy(tci->cmd_body.cmd_hdcp_compare.rx_val, r0, len);
+
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_COMPARE_R0);
+ if (rc != 0) {
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+int tee_hdcp1x_compute_compare_v(struct mtk_hdcp_info *hdcp_info,
+ u8 *crypto_param, u32 param_len, u8 *rx_v)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+
+ /* Copy parameters */
+ tci->command_id = CMD_COMPARE_V1;
+ tci->cmd_body.cmd_hdcp_compare.rx_val_len = 20;
+ tci->cmd_body.cmd_hdcp_compare.param_len = param_len;
+ memcpy(tci->cmd_body.cmd_hdcp_compare.rx_val, rx_v, 20);
+ memcpy(tci->cmd_body.cmd_hdcp_compare.param, crypto_param, param_len);
+
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_COMPARE_V1);
+ if (rc != 0) {
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+/** V2.X **/
+int tee_ake_certificate(struct mtk_hdcp_info *hdcp_info,
+ u8 *certificate, bool *stored, u8 *out_m, u8 *out_ekm)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+
+ /* Copy parameters */
+ tci->command_id = CMD_AKE_CERTIFICATE;
+ memcpy(tci->cmd_body.cmd_hdcp_ake_certificate.certification,
+ certificate, HDCP2_CERTRX_LEN);
+
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_AKE_CERTIFICATE);
+ if (rc != 0) {
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+ return rc;
+ }
+
+ TLCINFO("verify signature: result %d", rc);
+ tci = (struct tci_t *)dp_tee_priv->resp_buf;
+ *stored = tci->cmd_body.cmd_hdcp_ake_certificate.stored;
+ memcpy(out_m, tci->cmd_body.cmd_hdcp_ake_certificate.m,
+ HDCP_2_2_E_KH_KM_M_LEN - HDCP_2_2_E_KH_KM_LEN);
+ memcpy(out_ekm, tci->cmd_body.cmd_hdcp_ake_certificate.ekm, HDCP_2_2_E_KH_KM_LEN);
+
+ return rc;
+}
+
+int tee_enc_rsaes_oaep(struct mtk_hdcp_info *hdcp_info, u8 *ekm)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+
+ /* Copy parameters */
+ tci->command_id = CMD_ENC_KM;
+
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_ENC_KM);
+ if (rc != 0) {
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+ return rc;
+ }
+
+ tci = (struct tci_t *)dp_tee_priv->resp_buf;
+ memcpy(ekm, tci->cmd_body.cmd_hdcp_enc_km.enc_km, HDCP_2_2_E_KPUB_KM_LEN);
+
+ return rc;
+}
+
+int tee_ake_h_prime(struct mtk_hdcp_info *hdcp_info,
+ u8 *rtx, u8 *rrx, u8 *rx_caps, u8 *tx_caps, u8 *rx_h, u32 rx_h_len)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+
+ /* Copy parameters */
+ tci->command_id = CMD_AKE_H_PRIME;
+ tci->cmd_body.cmd_hdcp_ake_h_prime.rx_h_len = rx_h_len;
+
+ memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rtx, rtx, HDCP_2_2_RTX_LEN);
+ memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rrx, rrx, HDCP_2_2_RRX_LEN);
+ memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rx_caps, rx_caps, HDCP_2_2_RXCAPS_LEN);
+ memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.tx_caps, tx_caps, HDCP_2_2_TXCAPS_LEN);
+ memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rx_h, rx_h, rx_h_len);
+
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_AKE_H_PRIME);
+ if (rc != 0) {
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+ return rc;
+ }
+
+ tci = (struct tci_t *)dp_tee_priv->resp_buf;
+ return tci->return_code;
+}
+
+int tee_ake_paring(struct mtk_hdcp_info *hdcp_info, u8 *rx_ekm)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+
+ /* Copy parameters */
+ tci->command_id = CMD_AKE_PARING;
+ memcpy(tci->cmd_body.cmd_hdcp_ake_paring.ekm, rx_ekm, HDCP_2_2_E_KH_KM_LEN);
+
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_AKE_PARING);
+ if (rc != 0)
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+
+ return rc;
+}
+
+int tee_lc_l_prime(struct mtk_hdcp_info *hdcp_info, u8 *rn, u8 *rx_l, u32 len)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+
+ /* Copy parameters */
+ tci->command_id = CMD_LC_L_PRIME;
+ memcpy(tci->cmd_body.cmd_hdcp_lc_l_prime.rn, rn, HDCP_2_2_RN_LEN);
+ tci->cmd_body.cmd_hdcp_lc_l_prime.rx_l_len = len;
+ memcpy(tci->cmd_body.cmd_hdcp_lc_l_prime.rx_l, rx_l, len);
+
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_LC_L_PRIME);
+ if (rc != 0) {
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+ return rc;
+ }
+
+ tci = (struct tci_t *)dp_tee_priv->resp_buf;
+ return tci->return_code;
+}
+
+int tee_ske_enc_ks(struct mtk_hdcp_info *hdcp_info, u8 *riv, u8 *eks)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+ u8 *share_buffer = NULL;
+
+ /* Copy parameters */
+ tci->command_id = CMD_SKE_CAL_EKS;
+ memcpy(tci->cmd_body.cmd_hdcp_ske_eks.riv, riv, HDCP_2_2_RIV_LEN);
+
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH + 16, CMD_SKE_CAL_EKS);
+ if (rc != 0)
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+
+ share_buffer = (u8 *)dp_tee_priv->resp_buf;
+ memcpy(eks, share_buffer + TCI_LENGTH, 16);
+
+ return rc;
+}
+
+int tee_hdcp2_compute_compare_v(struct mtk_hdcp_info *hdcp_info,
+ u8 *crypto_param, u32 param_len, u8 *rx_v, u8 *tx_v)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+ u8 *share_buffer = NULL;
+
+ /* Copy parameters */
+ tci->command_id = CMD_COMPARE_V2;
+ tci->cmd_body.cmd_hdcp_compare.rx_val_len = 16;
+ tci->cmd_body.cmd_hdcp_compare.param_len = param_len;
+ memcpy(tci->cmd_body.cmd_hdcp_compare.rx_val, rx_v, 16);
+ memcpy(tci->cmd_body.cmd_hdcp_compare.param, crypto_param, param_len);
+
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_COMPARE_V2);
+ if (rc != 0) {
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+ return rc;
+ }
+
+ share_buffer = (u8 *)dp_tee_priv->resp_buf;
+ memcpy(tx_v, share_buffer + TCI_LENGTH, 16);
+
+ return rc;
+}
+
+int tee_hdcp2_compute_compare_m(struct mtk_hdcp_info *hdcp_info,
+ u8 *crypto_param, u32 param_len, u8 *rx_m)
+{
+ int rc;
+ struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
+ struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
+
+ /* Copy parameters */
+ tci->command_id = CMD_COMPARE_M;
+ tci->cmd_body.cmd_hdcp_compare.rx_val_len = HDCP_2_2_MPRIME_LEN;
+ tci->cmd_body.cmd_hdcp_compare.param_len = param_len;
+ memcpy(tci->cmd_body.cmd_hdcp_compare.rx_val, rx_m, HDCP_2_2_MPRIME_LEN);
+ memcpy(tci->cmd_body.cmd_hdcp_compare.param, crypto_param, param_len);
+
+ rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_COMPARE_M);
+ if (rc != 0)
+ TLCERR("tee_op_send failed, error=%x\n", rc);
+
+ return rc;
+}
diff --git a/drivers/gpu/drm/mediatek/tlc_dp_hdcp.h b/drivers/gpu/drm/mediatek/tlc_dp_hdcp.h
new file mode 100644
index 000000000000..493d3c51a7a0
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/tlc_dp_hdcp.h
@@ -0,0 +1,414 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019-2024 MediaTek Inc.
+ */
+
+#ifndef _TLC_DP_HDCP_H_
+#define _TLC_DP_HDCP_H_
+
+#include <linux/printk.h>
+#include <linux/tee_drv.h>
+#include <linux/uuid.h>
+#include <linux/types.h>
+#include <linux/sched/clock.h>
+#include <drm/display/drm_dp_helper.h>
+#include "tci.h"
+
+#define TLCINFO(string, args...) pr_info("[TLC_HDCP]info: "string, ##args)
+#define TLCERR(string, args...) pr_info("[TLC_HDCP]line:%d,err:%s:"string,\
+ __LINE__, __func__, ##args)
+
+#define DPTXHDCPFUNC(fmt, arg...) \
+ pr_info("[DPTXHDCP][%s line:%d]"pr_fmt(fmt), __func__, __LINE__, ##arg)
+#define DPTXHDCPMSG(fmt, arg...) \
+ pr_info("[DPTXHDCP]"pr_fmt(fmt), ##arg)
+#define DPTXHDCPERR(fmt, arg...) \
+ pr_err("[DPTXHDCP]"pr_fmt(fmt), ##arg)
+
+#define RET_SUCCESS 0
+
+/**
+ * hdcp version definitions
+ */
+#define HDCP_NONE 0x0 // No HDCP supported, no secure data path
+#define HDCP_V1 0x1 // HDCP version 1.0
+#define HDCP_V2 0x2 // HDCP version 2.0 Type 1
+#define HDCP_V2_1 0x3 // HDCP version 2.1 Type 1
+#define HDCP_V2_2 0x4 // HDCP version 2.2 Type 1
+#define HDCP_V2_3 0x5 // HDCP version 2.3 Type 1
+
+/* Local display only(content required version use only) */
+#define HDCP_LOCAL_DISPLAY_ONLY 0xf
+#define HDCP_NO_DIGITAL_OUTPUT 0xff // No digital output
+#define HDCP_DEFAULT HDCP_NO_DIGITAL_OUTPUT // Default value
+
+#define HDCP_VERSION_1X 1
+#define HDCP_VERSION_2X 2
+
+/* max. buffer size supported by dp */
+#define MAX_COMMAND_SIZE 4096
+#define MAX_RESPONSE_SIZE 4096
+
+#define HDCP1X_REP_MAXDEVS 128
+#define HDCP1X_V_LEN 20
+#define HDCP1X_B_INFO_LEN 2
+
+#define HDCP2_K_LEN 2
+#define HDCP2_STREAMID_TYPE_LEN 2
+
+enum HDCP_RESULT {
+ AUTH_ZERO = 0,
+ AUTH_PREPARE = 1,
+ AUTH_INIT = 2,
+ AUTH_PASS = 3,
+ AUTH_FAIL = 4,
+};
+
+struct dp_header {
+ __be16 tag;
+ __be32 length;
+ union {
+ __be32 ordinal;
+ __be32 return_code;
+ };
+} __packed;
+
+/**
+ * struct dp_tee_private - fTPM's private data
+ * @session: dp TA session identifier.
+ * @resp_len: cached response buffer length.
+ * @resp_buf: cached response buffer.
+ * @ctx: TEE context handler.
+ * @shm: Memory pool shared with fTPM TA in TEE.
+ */
+struct dp_tee_private {
+ u32 session;
+ size_t resp_len;
+ u8 resp_buf[MAX_RESPONSE_SIZE];
+ struct tee_context *ctx;
+ struct tee_shm *shm;
+};
+
+struct hdcp2_info_tx {
+ struct hdcp2_ake_init ake_init;
+ struct hdcp2_ake_no_stored_km no_stored_km;
+ struct hdcp2_ske_send_eks send_eks;
+ struct hdcp2_lc_init lc_init;
+ struct hdcp2_rep_stream_manage stream_manage;
+ struct hdcp2_rep_send_ack send_ack;
+ struct hdcp2_tx_caps tx_caps;
+ u8 k[HDCP2_K_LEN];
+ u8 stream_id_type[HDCP2_STREAMID_TYPE_LEN];
+};
+
+struct hdcp2_info_rx {
+ struct hdcp2_cert_rx cert_rx;
+ struct hdcp2_ake_send_cert send_cert;
+ struct hdcp2_rep_send_receiverid_list receiverid_list;
+ struct hdcp2_ake_send_pairing_info pairing_info;
+ struct hdcp2_rep_stream_ready stream_ready;
+ struct hdcp2_ake_send_hprime send_hprime;
+ struct hdcp2_lc_send_lprime send_lprime;
+};
+
+struct hdcp2_handler {
+ u8 main_state;
+ u8 sub_state;
+ u8 down_stream_dev_cnt;
+ u8 hdcp_rx_ver;
+ bool send_ake_init:1;
+ bool get_recv_id_list:1;
+ bool stored_km:1;
+ bool send_lc_init:1;
+ bool send_ack:1;
+ bool sink_is_repeater:1;
+ bool recv_msg:1;
+ bool send_pair:1;
+ u32 seq_num_v_cnt;
+ u32 retry_cnt;
+};
+
+struct hdcp1x_info {
+ bool enable;
+ bool repeater;
+ bool r0_read;
+ bool ksv_ready;
+ bool max_cascade;
+ bool max_devs;
+ u8 b_status;
+ u8 b_ksv[DRM_HDCP_KSV_LEN];
+ u8 a_ksv[DRM_HDCP_KSV_LEN];
+ u8 v[HDCP1X_V_LEN];
+ u8 b_info[HDCP1X_B_INFO_LEN];
+ u8 ksvfifo[DRM_HDCP_KSV_LEN * (HDCP1X_REP_MAXDEVS - 1)];
+ u8 device_count;
+ u8 retry_count;
+ int main_states;
+ int sub_states;
+};
+
+struct hdcp2_info {
+ struct hdcp2_info_tx hdcp_tx;
+ struct hdcp2_info_rx hdcp_rx;
+ struct hdcp2_ake_stored_km ake_stored_km;
+ struct hdcp2_handler hdcp_handler;
+ bool enable;
+ bool repeater;
+ bool read_certrx;
+ bool read_h_prime;
+ bool read_pairing;
+ bool read_l_prime;
+ bool ks_exchange_done;
+ bool read_v_prime;
+ u8 retry_count;
+ u8 device_count;
+ u8 stream_id_type;
+};
+
+struct mtk_hdcp_info {
+ u8 auth_status;
+ bool g_init;
+ u32 hdcp_content_type;
+ u32 content_protection;
+ struct dp_tee_private *g_dp_tee_priv;
+ struct hdcp1x_info hdcp1x_info;
+ struct hdcp2_info hdcp2_info;
+};
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/*
+ *Description:
+ * A device connect and do some initializations.
+ *
+ *Input:
+ * version: HDCP version
+ *
+ *Returns:
+ * TEEC_SUCCESS success*
+ */
+int tee_add_device(struct mtk_hdcp_info *hdcp_info, u32 version);
+
+/*
+ *Description:
+ * Device disconnect.
+ *
+ *Returns:
+ * N/A
+ */
+void tee_remove_device(struct mtk_hdcp_info *hdcp_info);
+
+/*
+ *Description:
+ * Clearing paring info.
+ *
+ *Returns:
+ * TEEC_SUCCESS success*
+ */
+int tee_clear_paring(struct mtk_hdcp_info *hdcp_info);
+
+/*
+ *Description:
+ * Calculate Km base on Bksv and write it to HW.
+ *
+ *Input:
+ * bksv[5] input
+ *
+ *Returns:
+ * TEEC_SUCCESS success
+ */
+int tee_calculate_lm(struct mtk_hdcp_info *hdcp_info, u8 *bksv);
+
+/*
+ *Description:
+ * Get Aksv from TEE.
+ *
+ *Output:
+ * aksv[5]
+ *
+ *Returns:
+ * TEEC_SUCCESS success
+ */
+int tee_get_aksv(struct mtk_hdcp_info *hdcp_info, u8 *aksv);
+
+/*
+ *Description:
+ * Get r0 from HW and compare to rx_r0.
+ *
+ *Parameters:
+ * r0[len] input
+ *
+ *Returns:
+ * TEEC_SUCCESS success
+ */
+int tee_compare_r0(struct mtk_hdcp_info *hdcp_info, u8 *r0, u32 len);
+
+/*
+ *Description:
+ * Compute and compare v value.
+ *
+ *Input:
+ * crypto_param[param_len] params used to calculate
+ * rx_v[20] v value from rx
+ *
+ *Returns:
+ * RET_COMPARE_PASS verify pass
+ */
+int tee_hdcp1x_compute_compare_v(struct mtk_hdcp_info *hdcp_info,
+ u8 *crypto_param, u32 param_len, u8 *rx_v);
+
+/*
+ *Description:
+ * Write An to HW.
+ *
+ *Input:
+ * an_code[8]
+ *
+ *Returns:
+ * TEEC_SUCCESS success
+ */
+int tee_hdcp1x_set_tx_an(struct mtk_hdcp_info *hdcp_info, u8 *an_code);
+
+/*
+ *Description:
+ * Write RST to HW.
+ *
+ *Returns:
+ * TEEC_SUCCESS success
+ */
+int tee_hdcp1x_soft_rst(struct mtk_hdcp_info *hdcp_info);
+int tee_hdcp2_soft_rst(struct mtk_hdcp_info *hdcp_info);
+
+/*
+ *Description:
+ * Set enable or disable to HW.
+ *
+ *Returns:
+ * TEEC_SUCCESS success
+ */
+int tee_hdcp_enable_encrypt(struct mtk_hdcp_info *hdcp_info, bool enable, u8 version);
+
+/*
+ *Description:
+ * AKE cetificate verify.
+ *
+ *Input:
+ * certificate[522]: cert use to calculate
+ *output:
+ * stored: whether be stored before
+ * out_m[16]
+ * out_ekm[16]
+ *
+ *Returns:
+ * TEEC_SUCCESS success*
+ */
+int tee_ake_certificate(struct mtk_hdcp_info *hdcp_info,
+ u8 *certificate, bool *stored, u8 *out_m, u8 *out_ekm);
+
+/*
+ *Description:
+ * Encrypt km.
+ *
+ *Output:
+ * ekm[128]: encrypted km
+ *
+ *Returns:
+ * TEEC_SUCCESS success*
+ */
+int tee_enc_rsaes_oaep(struct mtk_hdcp_info *hdcp_info, u8 *ekm);
+
+/*
+ *Description:
+ * Calculate h prime and compare to rx_h
+ *
+ *Input:
+ * rtx[8]
+ * rrx[8]
+ * rx_caps[3]
+ * tx_caps[3]
+ * rx_h[rx_h_len]
+ *
+ *Returns:
+ * RET_COMPARE_PASS: compare pass
+ */
+int tee_ake_h_prime(struct mtk_hdcp_info *hdcp_info,
+ u8 *rtx, u8 *rrx, u8 *rx_caps, u8 *tx_caps, u8 *rx_h, u32 rx_h_len);
+
+/*
+ *Description:
+ * Store paring info.
+ *
+ *Input:
+ * rx_ekm[16]
+ *
+ *Returns:
+ * TEEC_SUCCESS success*
+ */
+int tee_ake_paring(struct mtk_hdcp_info *hdcp_info, u8 *rx_ekm);
+
+/*
+ *Description:
+ * Calculate l prime and compare.
+ *
+ *Input:
+ * rn[8]
+ * rx_l[len]
+ *
+ *Returns:
+ * RET_COMPARE_PASS compare pass
+ */
+int tee_lc_l_prime(struct mtk_hdcp_info *hdcp_info, u8 *rn, u8 *rx_l, u32 len);
+
+/*
+ *Description:
+ * Encrypt ks
+ * Write contentkey and riv to hw
+ *
+ *Input:
+ * riv[8]
+ *Output:
+ * eks[16]
+ *
+ *Returns:
+ * TEEC_SUCCESS success*
+ */
+int tee_ske_enc_ks(struct mtk_hdcp_info *hdcp_info, u8 *riv, u8 *eks);
+
+/*
+ *Description:
+ * Calculate and compare v prime for repeater.
+ *
+ *Input:
+ * crypto_param[param_len] params used to calculate
+ * rx_v[16] v value from rx
+ *Output:
+ * tx_v[16]
+ *
+ *Returns:
+ * TEEC_SUCCESS success*
+ */
+int tee_hdcp2_compute_compare_v(struct mtk_hdcp_info *hdcp_info,
+ u8 *crypto_param, u32 param_len, u8 *rx_v, u8 *tx_v);
+
+/*
+ *Description:
+ * Calculate and compare m prime for repeater.
+ *
+ *Input:
+ * crypto_param[param_len] params used to calculate
+ * rx_m[32] m value from rx
+ *
+ *Returns:
+ * RET_COMPARE_PASS verify pass
+ */
+int tee_hdcp2_compute_compare_m(struct mtk_hdcp_info *hdcp_info,
+ u8 *crypto_param, u32 param_len, u8 *rx_m);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TLC_DP_HDCP_H_ */
--
2.43.0
Add HDCP2.x feature for DisplayPort.
When userspace request the kernel protect future content communicated
over the link with Content_Protection property, the feature will do
HDCP2.x authentication if the sink support HDCP2.X.
Changes in v2:
- remove switch case, and refine code to make more clear
- remove some definitions, and use the definitions in
include/drm/drm_hdcp.h
- use the struct which defined in include/drm/drm_hdcp.h
- do HDCP2.x authentication when userspace request the
kernel protect future content communicated
per suggestion from the previous thread:
https://lore.kernel.org/all/8fff59b5567449d8201dd1138c8fa
[email protected]/
Signed-off-by: mac.shen <[email protected]>
---
drivers/gpu/drm/mediatek/Makefile | 1 +
drivers/gpu/drm/mediatek/mtk_dp.c | 297 +++++--
drivers/gpu/drm/mediatek/mtk_dp.h | 99 +++
drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c | 1021 +++++++++++++++++++++++
drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h | 52 ++
drivers/gpu/drm/mediatek/mtk_dp_reg.h | 4 +-
drivers/gpu/drm/mediatek/mtk_dpi.c | 3 +
7 files changed, 1392 insertions(+), 85 deletions(-)
create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.h
create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c
create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index c80e6c2f9336..50ea069b047e 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_hdcp2.o \
mtk_dp.o
obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk-dp.o
diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index e4c16ba9902d..7ff72f15528b 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (c) 2019-2022 MediaTek Inc.
+ * Copyright (c) 2019-2024 MediaTek Inc.
* Copyright (c) 2022 BayLibre
*/
@@ -8,13 +8,13 @@
#include <drm/display/drm_dp.h>
#include <drm/display/drm_dp_helper.h>
#include <drm/drm_atomic_helper.h>
-#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
+#include <drm/display/drm_hdcp_helper.h>
#include <linux/arm-smccc.h>
#include <linux/clk.h>
#include <linux/delay.h>
@@ -30,10 +30,10 @@
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/soc/mediatek/mtk_sip_svc.h>
-#include <sound/hdmi-codec.h>
-#include <video/videomode.h>
+#include "mtk_dp.h"
#include "mtk_dp_reg.h"
+#include "mtk_dp_hdcp2.h"
#define MTK_DP_SIP_CONTROL_AARCH32 MTK_SIP_SMC_CMD(0x523)
#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE (BIT(0) | BIT(5))
@@ -52,43 +52,6 @@
#define MTK_DP_VERSION 0x11
#define MTK_DP_SDP_AUI 0x4
-enum {
- MTK_DP_CAL_GLB_BIAS_TRIM = 0,
- MTK_DP_CAL_CLKTX_IMPSE,
- MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0,
- MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1,
- MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2,
- MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3,
- MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0,
- MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1,
- MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2,
- MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3,
- MTK_DP_CAL_MAX,
-};
-
-struct mtk_dp_train_info {
- bool sink_ssc;
- bool cable_plugged_in;
- /* link_rate is in multiple of 0.27Gbps */
- int link_rate;
- int lane_count;
- unsigned int channel_eq_pattern;
-};
-
-struct mtk_dp_audio_cfg {
- bool detect_monitor;
- int sad_count;
- int sample_rate;
- int word_length_bits;
- int channels;
-};
-
-struct mtk_dp_info {
- enum dp_pixelformat format;
- struct videomode vm;
- struct mtk_dp_audio_cfg audio_cur_cfg;
-};
-
struct mtk_dp_efuse_fmt {
unsigned short idx;
unsigned short shift;
@@ -98,44 +61,6 @@ struct mtk_dp_efuse_fmt {
unsigned short default_val;
};
-struct mtk_dp {
- bool enabled;
- bool need_debounce;
- int irq;
- u8 max_lanes;
- u8 max_linkrate;
- u8 rx_cap[DP_RECEIVER_CAP_SIZE];
- u32 cal_data[MTK_DP_CAL_MAX];
- u32 irq_thread_handle;
- /* irq_thread_lock is used to protect irq_thread_handle */
- spinlock_t irq_thread_lock;
-
- struct device *dev;
- struct drm_bridge bridge;
- struct drm_bridge *next_bridge;
- struct drm_connector *conn;
- struct drm_device *drm_dev;
- struct drm_dp_aux aux;
-
- const struct mtk_dp_data *data;
- struct mtk_dp_info info;
- struct mtk_dp_train_info train_info;
-
- struct platform_device *phy_dev;
- struct phy *phy;
- struct regmap *regs;
- struct timer_list debounce_timer;
-
- /* For audio */
- bool audio_enable;
- hdmi_codec_plugged_cb plugged_cb;
- struct platform_device *audio_pdev;
-
- struct device *codec_dev;
- /* protect the plugged_cb as it's used in both bridge ops and audio */
- struct mutex update_plugged_status_lock;
-};
-
struct mtk_dp_data {
int bridge_type;
unsigned int smc_cmd;
@@ -319,12 +244,28 @@ static struct regmap_config mtk_dp_regmap_config = {
.name = "mtk-dp-registers",
};
+u32 mtk_dp_get_system_time(void)
+{
+ u32 tms = (u32)((sched_clock() / 1000000) % 1000000);
+ return tms;
+}
+
+u32 mtk_dp_get_time_diff(u32 pre_time)
+{
+ u32 post_time = mtk_dp_get_system_time();
+
+ if (pre_time > post_time)
+ return ((1000000 - pre_time) + post_time);
+ else
+ return (post_time - pre_time);
+}
+
static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
{
return container_of(b, struct mtk_dp, bridge);
}
-static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
+u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
{
u32 read_val;
int ret;
@@ -350,8 +291,8 @@ static int mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
return ret;
}
-static int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset,
- u32 val, u32 mask)
+int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset,
+ u32 val, u32 mask)
{
int ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
@@ -1865,6 +1806,160 @@ static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
mtk_dp_digital_sw_reset(mtk_dp);
}
+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 (tee_add_device(&mtk_dp->hdcp_info, HDCP_NONE) != RET_SUCCESS)
+ mtk_dp->hdcp_info.auth_status = AUTH_FAIL;
+}
+
+static void mtk_dp_check_sink_esi(struct mtk_dp *mtk_dp)
+{
+ u8 clear_cp_irq = BIT(2);
+
+ if (mtk_dp->hdcp_info.hdcp2_info.enable) {
+ dp_tx_hdcp2_irq(&mtk_dp->hdcp_info);
+ drm_dp_dpcd_write(&mtk_dp->aux,
+ DP_DEVICE_SERVICE_IRQ_VECTOR, &clear_cp_irq, 0x1);
+ }
+ /*hdcp 1.x do not need irq*/
+}
+
+static void mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp)
+{
+ ssize_t ret;
+ u8 sink_count;
+ u8 sink_count_200;
+
+ ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT_ESI, &sink_count);
+ if (ret < 0) {
+ drm_info(mtk_dp->drm_dev, "Read sink count failed: %ld\n", ret);
+ return;
+ }
+
+ ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count_200);
+ if (ret < 0) {
+ drm_info(mtk_dp->drm_dev,
+ "Read DP_SINK_COUNT_ESI failed: %ld\n", ret);
+ return;
+ }
+
+ mtk_dp_check_sink_esi(mtk_dp);
+}
+
+static void mtk_dp_hdcp_handle(struct work_struct *data)
+{
+ struct mtk_dp *mtk_dp = container_of(data, struct mtk_dp, hdcp_work);
+
+ if (!mtk_dp->train_info.cable_plugged_in)
+ return;
+
+ if (mtk_dp->hdcp_info.auth_status == AUTH_PREPARE) {
+ 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
+ mtk_dp->hdcp_info.auth_status = AUTH_ZERO;
+ }
+
+ while (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)
+ dp_tx_hdcp2_fsm(&mtk_dp->hdcp_info);
+ }
+}
+
+static void mtk_dp_hdcp_prop_work(struct work_struct *work)
+{
+ struct mtk_dp *mtk_dp;
+ struct delayed_work *dework;
+ struct drm_device *drm_dev;
+
+ dework = to_delayed_work(work);
+ mtk_dp = container_of(dework, struct mtk_dp, prop_work);
+
+ if (!mtk_dp->conn) {
+ dev_err(mtk_dp->dev, "connector is null!");
+ return;
+ }
+
+ drm_dev = mtk_dp->conn->dev;
+
+ drm_modeset_lock(&drm_dev->mode_config.connection_mutex, NULL);
+
+ if (mtk_dp->hdcp_info.content_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ mtk_dp->hdcp_info.content_protection =
+ (mtk_dp->hdcp_info.auth_status == AUTH_PASS) ? DRM_MODE_CONTENT_PROTECTION_ENABLED :
+ DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ else
+ mtk_dp->hdcp_info.content_protection =
+ (mtk_dp->hdcp_info.auth_status == AUTH_PASS) ? DRM_MODE_CONTENT_PROTECTION_ENABLED :
+ DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
+
+ dev_info(mtk_dp->dev, "update CP, content protection: %d, auth status:%d\n",
+ mtk_dp->hdcp_info.content_protection, mtk_dp->hdcp_info.auth_status);
+ drm_hdcp_update_content_protection(mtk_dp->conn, mtk_dp->hdcp_info.content_protection);
+
+ drm_modeset_unlock(&drm_dev->mode_config.connection_mutex);
+}
+
+static void mtk_dp_hdcp_atomic_check(struct mtk_dp *mtk_dp, struct drm_connector_state *state)
+{
+ unsigned int hdcp_content_type = state->hdcp_content_type;
+ unsigned int content_protection = state->content_protection;
+
+ dev_dbg(mtk_dp->dev, "CP type and protection, atomic: %d, %d, driver:%d, %d\n",
+ hdcp_content_type, content_protection,
+ mtk_dp->hdcp_info.hdcp_content_type, mtk_dp->hdcp_info.content_protection);
+
+ if ((content_protection == DRM_MODE_CONTENT_PROTECTION_UNDESIRED &&
+ mtk_dp->hdcp_info.content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED) ||
+ (hdcp_content_type != mtk_dp->hdcp_info.hdcp_content_type &&
+ content_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED)) {
+ mtk_dp->hdcp_info.content_protection = content_protection;
+
+ 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);
+
+ drm_hdcp_update_content_protection(mtk_dp->conn,
+ mtk_dp->hdcp_info.content_protection);
+ }
+
+ if (content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED ||
+ (hdcp_content_type != mtk_dp->hdcp_info.hdcp_content_type &&
+ content_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED)) {
+ mtk_dp->hdcp_info.content_protection = content_protection;
+ mtk_dp->hdcp_info.hdcp_content_type = hdcp_content_type;
+ mtk_dp->hdcp_info.hdcp2_info.stream_id_type = hdcp_content_type;
+
+ if (mtk_dp->hdcp_info.auth_status == AUTH_FAIL ||
+ mtk_dp->hdcp_info.auth_status == AUTH_ZERO) {
+ dev_dbg(mtk_dp->dev, "enable HDCP\n");
+ mtk_dp_authentication(&mtk_dp->hdcp_info);
+
+ queue_delayed_work(mtk_dp->hdcp_workqueue,
+ &mtk_dp->prop_work, msecs_to_jiffies(2000));
+ }
+ }
+}
+
+void mtk_dp_authentication(struct mtk_hdcp_info *hdcp_info)
+{
+ struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+
+ if (!mtk_dp->train_info.cable_plugged_in)
+ return;
+
+ hdcp_info->auth_status = AUTH_PREPARE;
+
+ dev_info(mtk_dp->dev, "dp start HDCP work");
+ queue_work(mtk_dp->hdcp_workqueue, &mtk_dp->hdcp_work);
+}
+
static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
{
struct mtk_dp *mtk_dp = dev;
@@ -1894,9 +1989,11 @@ static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
}
}
- if (status & MTK_DP_THREAD_HPD_EVENT)
+ if (status & MTK_DP_THREAD_HPD_EVENT) {
dev_dbg(mtk_dp->dev, "Receive IRQ from sink devices\n");
-
+ /*check if need clear hpd irq*/
+ mtk_dp_hpd_sink_event(mtk_dp);
+ }
return IRQ_HANDLED;
}
@@ -2233,6 +2330,7 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_bridge_state *old_state)
{
struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+ struct drm_connector_state *connector_state;
int ret;
mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
@@ -2243,6 +2341,8 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
return;
}
+ connector_state = drm_atomic_get_connector_state(old_state->base.state, mtk_dp->conn);
+
mtk_dp_aux_panel_poweron(mtk_dp, true);
/* Training */
@@ -2272,6 +2372,14 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
mtk_dp->enabled = true;
mtk_dp_update_plugged_status(mtk_dp);
+ /* Enable hdcp if it's desired */
+ dev_info(mtk_dp->dev, "hdcp_content_type:%d, content protection: %d",
+ connector_state->hdcp_content_type, connector_state->content_protection);
+ if (connector_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED) {
+ mtk_dp->hdcp_info.hdcp_content_type = connector_state->hdcp_content_type;
+ mtk_dp_authentication(&mtk_dp->hdcp_info);
+ }
+
return;
power_off_aux:
mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
@@ -2284,6 +2392,17 @@ static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
{
struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+ if (mtk_dp->hdcp_info.hdcp2_info.enable)
+ dp_tx_hdcp2_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;
+ dev_info(mtk_dp->dev, "update CP, content protection: %d\n",
+ mtk_dp->hdcp_info.content_protection);
+ drm_hdcp_update_content_protection(mtk_dp->conn,
+ mtk_dp->hdcp_info.content_protection);
+ }
+
mtk_dp->enabled = false;
mtk_dp_update_plugged_status(mtk_dp);
mtk_dp_video_enable(mtk_dp, false);
@@ -2418,6 +2537,8 @@ static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
drm_display_mode_to_videomode(&crtc_state->adjusted_mode, &mtk_dp->info.vm);
+ mtk_dp_hdcp_atomic_check(mtk_dp, conn_state);
+
return 0;
}
@@ -2657,6 +2778,14 @@ static int mtk_dp_probe(struct platform_device *pdev)
if (ret)
return ret;
+ INIT_WORK(&mtk_dp->hdcp_work, mtk_dp_hdcp_handle);
+ INIT_DELAYED_WORK(&mtk_dp->prop_work, mtk_dp_hdcp_prop_work);
+ mtk_dp->hdcp_workqueue = create_workqueue("mtk_dp_hdcp_work");
+ if (!mtk_dp->hdcp_workqueue) {
+ dev_err(mtk_dp->dev, "failed to create hdcp work queue");
+ return -ENOMEM;
+ }
+
mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
mtk_dp->bridge.of_node = dev->of_node;
mtk_dp->bridge.type = mtk_dp->data->bridge_type;
diff --git a/drivers/gpu/drm/mediatek/mtk_dp.h b/drivers/gpu/drm/mediatek/mtk_dp.h
new file mode 100644
index 000000000000..85c0cf0005c0
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019-2024 MediaTek Inc.
+ */
+
+#ifndef _MTK_DP_H_
+#define _MTK_DP_H_
+
+#include "tlc_dp_hdcp.h"
+#include <drm/drm_bridge.h>
+#include <sound/hdmi-codec.h>
+#include <video/videomode.h>
+
+enum {
+ MTK_DP_CAL_GLB_BIAS_TRIM = 0,
+ MTK_DP_CAL_CLKTX_IMPSE,
+ MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0,
+ MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1,
+ MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2,
+ MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3,
+ MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0,
+ MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1,
+ MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2,
+ MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3,
+ MTK_DP_CAL_MAX,
+};
+
+struct mtk_dp_audio_cfg {
+ bool detect_monitor;
+ int sad_count;
+ int sample_rate;
+ int word_length_bits;
+ int channels;
+};
+
+struct mtk_dp_info {
+ enum dp_pixelformat format;
+ struct videomode vm;
+ struct mtk_dp_audio_cfg audio_cur_cfg;
+};
+
+struct mtk_dp_train_info {
+ bool sink_ssc;
+ bool cable_plugged_in;
+ /* link_rate is in multiple of 0.27Gbps */
+ int link_rate;
+ int lane_count;
+ unsigned int channel_eq_pattern;
+};
+
+struct mtk_dp {
+ bool enabled;
+ bool need_debounce;
+ int irq;
+ u8 max_lanes;
+ u8 max_linkrate;
+ u8 rx_cap[DP_RECEIVER_CAP_SIZE];
+ u32 cal_data[MTK_DP_CAL_MAX];
+ u32 irq_thread_handle;
+ /* irq_thread_lock is used to protect irq_thread_handle */
+ spinlock_t irq_thread_lock;
+
+ struct device *dev;
+ struct drm_bridge bridge;
+ struct drm_bridge *next_bridge;
+ struct drm_connector *conn;
+ struct drm_device *drm_dev;
+ struct drm_dp_aux aux;
+
+ const struct mtk_dp_data *data;
+ struct mtk_dp_info info;
+ struct mtk_dp_train_info train_info;
+ struct mtk_hdcp_info hdcp_info;
+ struct work_struct hdcp_work;
+ struct delayed_work prop_work;
+ struct workqueue_struct *hdcp_workqueue;
+
+ struct platform_device *phy_dev;
+ struct phy *phy;
+ struct regmap *regs;
+ struct timer_list debounce_timer;
+
+ /* For audio */
+ bool audio_enable;
+ hdmi_codec_plugged_cb plugged_cb;
+ struct platform_device *audio_pdev;
+
+ struct device *codec_dev;
+ /* protect the plugged_cb as it's used in both bridge ops and audio */
+ struct mutex update_plugged_status_lock;
+};
+
+u32 mtk_dp_get_system_time(void);
+u32 mtk_dp_get_time_diff(u32 pre_time);
+u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset);
+int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, u32 val, u32 mask);
+void mtk_dp_authentication(struct mtk_hdcp_info *hdcp_info);
+
+#endif /* _MTK_DP_H_ */
diff --git a/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c
new file mode 100644
index 000000000000..8528e33f3f49
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c
@@ -0,0 +1,1021 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019-2024 MediaTek Inc.
+ */
+
+#include "mtk_dp_hdcp2.h"
+#include "mtk_dp_reg.h"
+#include "mtk_dp.h"
+
+u8 t_rtx[HDCP_2_2_RTX_LEN] = {
+ 0x18, 0xfa, 0xe4, 0x20, 0x6a, 0xfb, 0x51, 0x49
+};
+
+u8 t_tx_caps[HDCP_2_2_TXCAPS_LEN] = {
+ 0x02, 0x00, 0x00
+};
+
+u8 t_rn[HDCP_2_2_RN_LEN] = {
+ 0x32, 0x75, 0x3e, 0xa8, 0x78, 0xa6, 0x38, 0x1c
+};
+
+u8 t_riv[HDCP_2_2_RIV_LEN] = {
+ 0x40, 0x2b, 0x6b, 0x43, 0xc5, 0xe8, 0x86, 0xd8
+};
+
+static void dp_tx_hdcp2_fill_stream_type(struct mtk_hdcp_info *hdcp_info, u8 uc_type)
+{
+ struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34D0, uc_type, 0xff);
+}
+
+static void dp_tx_hdcp2_set_state(struct mtk_hdcp_info *hdcp_info, u8 main_state, u8 sub_state)
+{
+ hdcp_info->hdcp2_info.hdcp_handler.main_state = main_state;
+ hdcp_info->hdcp2_info.hdcp_handler.sub_state = sub_state;
+}
+
+static void dp_tx_hdcp2_set_auth_pass(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_3400, BIT(11), BIT(11));
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4, BIT(4), BIT(4));
+ } else {
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3400, 0, BIT(11));
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4, 0, BIT(4));
+ }
+}
+
+static void dp_tx_hdcp2_enable_auth(struct mtk_hdcp_info *hdcp_info, bool enable)
+{
+ struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+
+ DPTXHDCPFUNC();
+ dp_tx_hdcp2_set_auth_pass(hdcp_info, enable);
+ if (enable) {
+ u32 version = HDCP_V2_3;
+
+ if (hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.rx_info[1] & BIT(0))
+ version = HDCP_V1;
+ else if (hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.rx_info[1] & BIT(1))
+ version = HDCP_V2;
+
+ tee_hdcp_enable_encrypt(hdcp_info, enable, version);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, BIT(5), BIT(5));
+ } else {
+ tee_hdcp_enable_encrypt(hdcp_info, enable, HDCP_NONE);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, 0, BIT(5));
+ }
+}
+
+static int dp_tx_hdcp2_init(struct mtk_hdcp_info *hdcp_info)
+{
+ int err_code = HDCP_ERR_NONE;
+
+ DPTXHDCPFUNC();
+
+ memset(&hdcp_info->hdcp2_info.hdcp_tx, 0, sizeof(struct hdcp2_info_tx));
+ memset(&hdcp_info->hdcp2_info.hdcp_rx, 0, sizeof(struct hdcp2_info_rx));
+ memcpy(hdcp_info->hdcp2_info.hdcp_tx.ake_init.r_tx, t_rtx, HDCP_2_2_RTX_LEN);
+ memcpy(&hdcp_info->hdcp2_info.hdcp_tx.tx_caps, t_tx_caps, HDCP_2_2_TXCAPS_LEN);
+ memcpy(hdcp_info->hdcp2_info.hdcp_tx.lc_init.r_n, t_rn, HDCP_2_2_RN_LEN);
+ memcpy(hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, t_riv, HDCP_2_2_RIV_LEN);
+
+ memset(&hdcp_info->hdcp2_info.hdcp_handler, 0, sizeof(struct hdcp2_handler));
+ memset(&hdcp_info->hdcp2_info.ake_stored_km, 0, sizeof(struct hdcp2_ake_stored_km));
+
+ dp_tx_hdcp2_enable_auth(hdcp_info, false);
+
+ return err_code;
+}
+
+static bool dp_tx_hdcp2_inc_seq_num_m(struct mtk_hdcp_info *hdcp_info)
+{
+ u32 tmp = 0;
+
+ tmp = drm_hdcp_be24_to_cpu(hdcp_info->hdcp2_info.hdcp_tx.stream_manage.seq_num_m);
+
+ if (tmp == 0xFFFFFF)
+ return false;
+
+ tmp++;
+
+ drm_hdcp_cpu_to_be24(hdcp_info->hdcp2_info.hdcp_tx.stream_manage.seq_num_m, tmp);
+ return true;
+}
+
+static bool dp_tx_hdcp2_process_rep_auth_stream_manage(struct mtk_hdcp_info *hdcp_info)
+{
+ bool ret = false;
+
+ hdcp_info->hdcp2_info.hdcp_tx.k[0] = 0x00;
+ hdcp_info->hdcp2_info.hdcp_tx.k[1] = 0x01;
+
+ hdcp_info->hdcp2_info.hdcp_tx.stream_id_type[0] = 0x00; //Payload ID
+ hdcp_info->hdcp2_info.hdcp_tx.stream_id_type[1] = hdcp_info->hdcp2_info.stream_id_type;
+
+ ret = dp_tx_hdcp2_inc_seq_num_m(hdcp_info);
+
+ return ret;
+}
+
+static bool dp_tx_hdcp2_recv_rep_auth_send_recv_id_list(struct mtk_hdcp_info *hdcp_info)
+{
+ bool ret = false;
+ u8 *buffer = NULL;
+ u32 len = 0, len_recv_id_list = 0;
+ int rc = 0;
+
+ len_recv_id_list = hdcp_info->hdcp2_info.device_count * HDCP_2_2_RECEIVER_ID_LEN;
+ len = len_recv_id_list + HDCP_2_2_RXINFO_LEN + HDCP_2_2_SEQ_NUM_LEN;
+ buffer = kmalloc(len, GFP_KERNEL);
+ if (!buffer) {
+ pr_err("2.x: Out of Memory\n");
+ return ret;
+ }
+
+ memcpy(buffer, hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.receiver_ids,
+ len_recv_id_list);
+ memcpy(buffer + len_recv_id_list,
+ hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.rx_info, HDCP_2_2_RXINFO_LEN);
+ memcpy(buffer + len_recv_id_list + HDCP_2_2_RXINFO_LEN,
+ hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.seq_num_v, HDCP_2_2_SEQ_NUM_LEN);
+
+ rc = tee_hdcp2_compute_compare_v(hdcp_info, buffer, len,
+ hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.v_prime,
+ hdcp_info->hdcp2_info.hdcp_tx.send_ack.v);
+
+ if (rc == RET_COMPARE_PASS) {
+ ret = true;
+ DPTXHDCPMSG("2.x: V' is PASS!!\n");
+ } else {
+ DPTXHDCPMSG("2.x: V' is FAIL!!\n");
+ }
+
+ kfree(buffer);
+ return ret;
+}
+
+static bool dp_tx_hdcp2_recv_rep_auth_stream_ready(struct mtk_hdcp_info *hdcp_info)
+{
+ bool ret = false;
+ u8 *buffer = NULL;
+ u32 len = 0;
+ int tmp = 0;
+
+ len = HDCP2_STREAMID_TYPE_LEN + HDCP_2_2_SEQ_NUM_LEN;
+ buffer = kmalloc(len, GFP_KERNEL);
+ if (!buffer) {
+ pr_err("2.x: Out of Memory\n");
+ return ret;
+ }
+
+ memcpy(buffer, hdcp_info->hdcp2_info.hdcp_tx.stream_id_type, HDCP2_STREAMID_TYPE_LEN);
+ memcpy(buffer + HDCP2_STREAMID_TYPE_LEN,
+ hdcp_info->hdcp2_info.hdcp_tx.stream_manage.seq_num_m,
+ HDCP_2_2_SEQ_NUM_LEN);
+ tmp = tee_hdcp2_compute_compare_m(hdcp_info, buffer, len,
+ hdcp_info->hdcp2_info.hdcp_rx.stream_ready.m_prime);
+
+ if (tmp == RET_COMPARE_PASS) {
+ ret = true;
+ DPTXHDCPMSG("2.x: M' is PASS!!\n");
+ } else {
+ DPTXHDCPMSG("2.x: M' is FAIL!!\n");
+ }
+
+ kfree(buffer);
+ return ret;
+}
+
+static bool dp_tx_hdcp2_check_seq_num_v(struct mtk_hdcp_info *hdcp_info)
+{
+ if ((hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.seq_num_v[0] == 0x00 &&
+ hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.seq_num_v[1] == 0x00 &&
+ hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.seq_num_v[2] == 0x00) &&
+ hdcp_info->hdcp2_info.hdcp_handler.seq_num_v_cnt > 0xFFFFFF) {
+ DPTXHDCPMSG("2.x: SeqNumV Rollover!\n");
+ return false;
+ }
+
+ if ((hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.seq_num_v[0]
+ != (u8)((hdcp_info->hdcp2_info.hdcp_handler.seq_num_v_cnt & 0xFF0000) >> 16)) ||
+ (hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.seq_num_v[1]
+ != (u8)((hdcp_info->hdcp2_info.hdcp_handler.seq_num_v_cnt & 0x00FF00) >> 8)) ||
+ (hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.seq_num_v[2]
+ != (u8)((hdcp_info->hdcp2_info.hdcp_handler.seq_num_v_cnt & 0x0000FF)))) {
+ DPTXHDCPMSG("2.x: Invalid Seq_num_V!\n");
+ return false;
+ }
+
+ hdcp_info->hdcp2_info.hdcp_handler.seq_num_v_cnt++;
+ return true;
+}
+
+static void dp_tx_hdcp2_err_handle(struct mtk_hdcp_info *hdcp_info, int err_msg, int line)
+{
+ pr_err("2.x: MainState:%d; SubState:%d;\n", hdcp_info->hdcp2_info.hdcp_handler.main_state,
+ hdcp_info->hdcp2_info.hdcp_handler.sub_state);
+
+ switch (err_msg) {
+ case HDCP_ERR_UNKNOWN_STATE:
+ pr_err("2.x: Unknown State, line:%d\n", line);
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_H1P1, HDCP_2_2_AUTH_FAIL);
+ break;
+
+ case HDCP_ERR_SEND_MSG_FAIL:
+ pr_err("2.x: Send Msg Fail, line:%d\n", line);
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A0F0, HDCP_2_2_NULL_MSG);
+ break;
+ case HDCP_ERR_RESPONSE_TIMEROUT:
+ pr_err("2.x: Response Timeout, line:%d!\n", line);
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A0F0, HDCP_2_2_NULL_MSG);
+ break;
+
+ case HDCP_ERR_PROCESS_FAIL:
+ pr_err("2.x: Process Fail, line:%d!\n", line);
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A0F0, HDCP_2_2_NULL_MSG);
+ break;
+
+ default:
+ pr_err("2.x: NO ERROR!");
+ break;
+ }
+}
+
+static bool dp_tx_hdcp2_read_msg(struct mtk_hdcp_info *hdcp_info, u8 cmd_ID)
+{
+ struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+ bool ret = false;
+ u8 size = 0;
+
+ switch (cmd_ID) {
+ case HDCP_2_2_AKE_SEND_CERT:
+ drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_CERT_RX_OFFSET,
+ (void *)&hdcp_info->hdcp2_info.hdcp_rx.cert_rx, HDCP2_CERTRX_LEN);
+ drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RRX_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_rx.send_cert.r_rx, HDCP_2_2_RRX_LEN);
+ drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RX_CAPS_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_rx.send_cert.rx_caps,
+ HDCP_2_2_RXCAPS_LEN);
+
+ hdcp_info->hdcp2_info.read_certrx = false;
+ hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true;
+ ret = true;
+ DPTXHDCPMSG("2.x: HDCP_2_2_AKE_SEND_CERT\n");
+ break;
+
+ case HDCP_2_2_AKE_SEND_HPRIME:
+ drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_HPRIME_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_rx.send_hprime.h_prime,
+ HDCP_2_2_H_PRIME_LEN);
+
+ hdcp_info->hdcp2_info.read_h_prime = false;
+ hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true;
+ ret = true;
+
+ DPTXHDCPMSG("2.x: HDCP_2_2_AKE_SEND_HPRIME\n");
+ break;
+
+ case HDCP_2_2_AKE_SEND_PAIRING_INFO:
+ drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_EKH_KM_RD_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_rx.pairing_info.e_kh_km,
+ HDCP_2_2_E_KH_KM_LEN);
+ hdcp_info->hdcp2_info.read_pairing = false;
+ hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true;
+ ret = true;
+ DPTXHDCPMSG("2.x: HDCP_2_2_AKE_SEND_PAIRING_INFO\n");
+ break;
+
+ case HDCP_2_2_LC_SEND_LPRIME:
+ drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_LPRIME_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_rx.send_lprime.l_prime,
+ HDCP_2_2_L_PRIME_LEN);
+
+ hdcp_info->hdcp2_info.read_l_prime = false;
+ hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true;
+ ret = true;
+ DPTXHDCPMSG("2.x: HDCP_2_2_LC_SEND_LPRIME\n");
+ break;
+
+ case HDCP_2_2_REP_SEND_RECVID_LIST:
+ drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXINFO_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.rx_info,
+ HDCP_2_2_RXINFO_LEN);
+ hdcp_info->hdcp2_info.device_count =
+ ((hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.rx_info[1] & 0xf0) >> 4)
+ | ((hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.rx_info[0]
+ & BIT(0)) << 4);
+
+ drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_SEQ_NUM_V_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.seq_num_v,
+ HDCP_2_2_SEQ_NUM_LEN);
+ drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_VPRIME_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.v_prime,
+ HDCP_2_2_V_PRIME_HALF_LEN);
+ drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RECV_ID_LIST_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.receiver_ids,
+ hdcp_info->hdcp2_info.device_count * HDCP_2_2_RECEIVER_ID_LEN);
+
+ hdcp_info->hdcp2_info.read_v_prime = false;
+ hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true;
+ ret = true;
+ DPTXHDCPMSG("2.x: HDCP_2_2_REP_SEND_RECVID_LIST\n");
+ break;
+
+ case HDCP_2_2_REP_STREAM_READY:
+ size = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_MPRIME_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_rx.stream_ready.m_prime,
+ HDCP_2_2_MPRIME_LEN);
+
+ if (size == HDCP_2_2_MPRIME_LEN)
+ hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true;
+ ret = true;
+ DPTXHDCPMSG("2.x: HDCP_2_2_REP_STREAM_READY\n");
+ break;
+
+ default:
+ DPTXHDCPMSG("2.x: Invalid DPTX_HDCP2_OffSETADDR_ReadMessage !\n");
+ break;
+ }
+
+ return ret;
+}
+
+static bool dp_tx_hdcp2_write_msg(struct mtk_hdcp_info *hdcp_info, u8 cmd_ID)
+{
+ struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+ bool ret = false;
+
+ switch (cmd_ID) {
+ case HDCP_2_2_AKE_INIT:
+ tee_hdcp2_soft_rst(hdcp_info);
+ drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_RTX_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_tx.ake_init.r_tx, HDCP_2_2_RTX_LEN);
+ drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_TXCAPS_OFFSET,
+ (void *)&hdcp_info->hdcp2_info.hdcp_tx.tx_caps,
+ HDCP_2_2_TXCAPS_LEN);
+
+ ret = true;
+ DPTXHDCPMSG("2.x: HDCP_2_2_AKE_Init !\n");
+ break;
+
+ case HDCP_2_2_AKE_NO_STORED_KM:
+ drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_EKPUB_KM_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_tx.no_stored_km.e_kpub_km,
+ HDCP_2_2_E_KPUB_KM_LEN);
+
+ ret = true;
+
+ DPTXHDCPMSG("2.x: HDCP_2_2_AKE_NO_STORED_KM !\n");
+ break;
+
+ case HDCP_2_2_AKE_STORED_KM:
+ drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_EKH_KM_WR_OFFSET,
+ hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m,
+ HDCP_2_2_E_KH_KM_LEN);
+ drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_M_OFFSET,
+ hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m +
+ HDCP_2_2_E_KH_KM_LEN,
+ HDCP_2_2_E_KH_KM_M_LEN - HDCP_2_2_E_KH_KM_LEN);
+
+ ret = true;
+
+ DPTXHDCPMSG("2.x: DPTX_HDCP_2_2_AKE_STORED_KM !\n");
+ break;
+
+ case HDCP_2_2_LC_INIT:
+ drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_RN_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_tx.lc_init.r_n, HDCP_2_2_RN_LEN);
+
+ hdcp_info->hdcp2_info.read_l_prime = true;
+ ret = true;
+
+ DPTXHDCPMSG("2.x: HDCP_2_2_LC_INIT !\n");
+ break;
+
+ case HDCP_2_2_SKE_SEND_EKS:
+ drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_EDKEY_KS_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_tx.send_eks.e_dkey_ks,
+ HDCP_2_2_E_DKEY_KS_LEN);
+ drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_RIV_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, HDCP_2_2_RIV_LEN);
+
+ hdcp_info->hdcp2_info.ks_exchange_done = true;
+
+ ret = true;
+ DPTXHDCPMSG("2.x: HDCP_2_2_SKE_SEND_EKS !\n");
+ break;
+
+ case HDCP_2_2_STREAM_TYPE:
+ drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_tx.stream_id_type, 1);
+
+ ret = true;
+ DPTXHDCPMSG("HDCP2_MSG_DP_STREAM_TYPE !\n");
+ break;
+
+ case HDCP_2_2_REP_SEND_ACK:
+ drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_V_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_tx.send_ack.v,
+ HDCP_2_2_V_PRIME_HALF_LEN);
+
+ ret = true;
+ DPTXHDCPMSG("2.x: HDCP_2_2_SEND_ACK !\n");
+ break;
+
+ case HDCP_2_2_REP_STREAM_MANAGE:
+ drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_SEQ_NUM_M_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_tx.stream_manage.seq_num_m,
+ HDCP_2_2_SEQ_NUM_LEN);
+ drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_K_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_tx.k, HDCP2_K_LEN);
+ drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_STREAM_ID_TYPE_OFFSET,
+ hdcp_info->hdcp2_info.hdcp_tx.stream_id_type,
+ HDCP2_STREAMID_TYPE_LEN);
+
+ dp_tx_hdcp2_fill_stream_type(hdcp_info, hdcp_info->hdcp2_info.stream_id_type);
+
+ ret = true;
+ DPTXHDCPMSG("2.x: HDCP_2_2_STREAM_MANAGE !\n");
+ break;
+
+ default:
+ DPTXHDCPMSG("2.x: Invalid HDCP2_OffSETADDR_WriteMessage !\n");
+ break;
+ }
+
+ return ret;
+}
+
+static void dp_tx_hdcp2_rest_variable(struct mtk_hdcp_info *hdcp_info)
+{
+ hdcp_info->hdcp2_info.read_certrx = false;
+ hdcp_info->hdcp2_info.read_h_prime = false;
+ hdcp_info->hdcp2_info.read_pairing = false;
+ hdcp_info->hdcp2_info.read_l_prime = false;
+ hdcp_info->hdcp2_info.ks_exchange_done = false;
+ hdcp_info->hdcp2_info.read_v_prime = false;
+}
+
+int dp_tx_hdcp2_fsm(struct mtk_hdcp_info *hdcp_info)
+{
+ static u32 timeout_value;
+ static u8 pre_main;
+ static u8 pre_sub;
+ static u32 pre_time;
+ int err_code = HDCP_ERR_NONE;
+ bool stored = false;
+ u32 time;
+ int ret = 0;
+ bool tmp = false;
+
+ if (pre_main != hdcp_info->hdcp2_info.hdcp_handler.main_state ||
+ hdcp_info->hdcp2_info.hdcp_handler.sub_state != pre_sub) {
+ DPTXHDCPMSG("2.x: Port(M : S)= (%d, %d)",
+ hdcp_info->hdcp2_info.hdcp_handler.main_state,
+ hdcp_info->hdcp2_info.hdcp_handler.sub_state);
+ pre_main = hdcp_info->hdcp2_info.hdcp_handler.main_state;
+ pre_sub = hdcp_info->hdcp2_info.hdcp_handler.sub_state;
+ }
+
+ switch (hdcp_info->hdcp2_info.hdcp_handler.main_state) {
+ case HDCP2_MS_H1P1:
+ /* HDCP2_MS_H1P1 */
+ /* HDCP_2_2_AUTH_FAIL */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_AUTH_FAIL) {
+ pr_err("2.x: Authentication Fail!\n");
+ dp_tx_hdcp2_enable_auth(hdcp_info, false);
+ hdcp_info->auth_status = AUTH_FAIL;
+ }
+ break;
+
+ case HDCP2_MS_A0F0:
+ /* HDCP2_MS_A0F0 */
+ /* HDCP_2_2_NULL_MSG */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_NULL_MSG) {
+ if (!hdcp_info->hdcp2_info.enable) {
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_H1P1, HDCP_2_2_AUTH_FAIL);
+ DPTXHDCPMSG("2.x: Sink Doesn't Support Hdcp2x!\n");
+ break;
+ }
+
+ dp_tx_hdcp2_init(hdcp_info);
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, HDCP_2_2_NULL_MSG);
+ DPTXHDCPMSG("2.x: Sink Support Hdcp2x!\n");
+ }
+ break;
+
+ case HDCP2_MS_A1F1:
+ /* HDCP2_MS_A1F1 */
+ /* HDCP_2_2_NULL_MSG */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_NULL_MSG) {
+ if (hdcp_info->hdcp2_info.retry_count >= HDCP2_TX_RETRY_CNT) {
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_H1P1, HDCP_2_2_AUTH_FAIL);
+ pr_err("2.x: Try Max Count\n");
+ break;
+ }
+
+ hdcp_info->hdcp2_info.retry_count++;
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, HDCP_2_2_AKE_INIT);
+ }
+
+ /* HDCP2_MS_A1F1 */
+ /* HDCP_2_2_AKE_INIT */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_AKE_INIT) {
+ tmp = dp_tx_hdcp2_write_msg(hdcp_info, HDCP_2_2_AKE_INIT);
+ if (!tmp) {
+ err_code = HDCP_ERR_SEND_MSG_FAIL;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+ dp_tx_hdcp2_rest_variable(hdcp_info);
+ hdcp_info->hdcp2_info.read_certrx = true;
+
+ hdcp_info->hdcp2_info.hdcp_handler.send_ake_init = true;
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, HDCP_2_2_AKE_SEND_CERT);
+ pre_time = mtk_dp_get_system_time();
+ }
+
+ /* HDCP2_MS_A1F1 */
+ /* HDCP_2_2_AKE_SEND_CERT */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_AKE_SEND_CERT) {
+ time = mtk_dp_get_time_diff(pre_time);
+ if (time < HDCP_2_2_CERT_TIMEOUT_MS) {
+ msleep(20);
+ break;
+ }
+ if (hdcp_info->hdcp2_info.read_certrx)
+ dp_tx_hdcp2_read_msg(hdcp_info, HDCP_2_2_AKE_SEND_CERT);
+
+ if (!hdcp_info->hdcp2_info.hdcp_handler.recv_msg)
+ break;
+
+ ret = tee_ake_certificate(hdcp_info,
+ (u8 *)&hdcp_info->hdcp2_info.hdcp_rx.cert_rx,
+ &stored,
+ hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m +
+ HDCP_2_2_E_KH_KM_LEN,
+ hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m);
+
+ if (ret != RET_COMPARE_PASS) {
+ err_code = HDCP_ERR_PROCESS_FAIL;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ hdcp_info->hdcp2_info.hdcp_handler.stored_km = stored;
+ hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false;
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1,
+ hdcp_info->hdcp2_info.hdcp_handler.stored_km ?
+ HDCP_2_2_AKE_STORED_KM :
+ HDCP_2_2_AKE_NO_STORED_KM);
+ }
+
+ /* HDCP2_MS_A1F1 */
+ /* HDCP_2_2_AKE_NO_STORED_KM */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_AKE_NO_STORED_KM) {
+ DPTXHDCPMSG("2.x: Get Km, derive Ekpub(km)\n");
+
+ tee_enc_rsaes_oaep(hdcp_info,
+ hdcp_info->hdcp2_info.hdcp_tx.no_stored_km.e_kpub_km);
+ /* Prepare e_kpub_km to send */
+ tmp = dp_tx_hdcp2_write_msg(hdcp_info, HDCP_2_2_AKE_NO_STORED_KM);
+ if (!tmp) {
+ err_code = HDCP_ERR_SEND_MSG_FAIL;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, HDCP_2_2_AKE_SEND_HPRIME);
+ timeout_value = HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS;
+ hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false;
+ pre_time = mtk_dp_get_system_time();
+ }
+
+ /* HDCP2_MS_A1F1 */
+ /* HDCP_2_2_AKE_STORED_KM */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_AKE_STORED_KM) {
+ /* Prepare ekh_km & M to send */
+ tmp = dp_tx_hdcp2_write_msg(hdcp_info, HDCP_2_2_AKE_STORED_KM);
+ if (!tmp) {
+ err_code = HDCP_ERR_SEND_MSG_FAIL;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ err_code = HDCP_ERR_NONE;
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, HDCP_2_2_AKE_SEND_HPRIME);
+ timeout_value = HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS;
+ hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false;
+ pre_time = mtk_dp_get_system_time();
+ }
+
+ /* HDCP2_MS_A1F1 */
+ /* HDCP_2_2_AKE_SEND_HPRIME */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_AKE_SEND_HPRIME) {
+ if (hdcp_info->hdcp2_info.read_h_prime)
+ dp_tx_hdcp2_read_msg(hdcp_info, HDCP_2_2_AKE_SEND_HPRIME);
+
+ time = mtk_dp_get_time_diff(pre_time);
+ if (time > timeout_value) {
+ err_code = HDCP_ERR_RESPONSE_TIMEROUT;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ if (!hdcp_info->hdcp2_info.hdcp_handler.recv_msg)
+ break;
+
+ ret = tee_ake_h_prime(hdcp_info,
+ hdcp_info->hdcp2_info.hdcp_tx.ake_init.r_tx,
+ hdcp_info->hdcp2_info.hdcp_rx.send_cert.r_rx,
+ hdcp_info->hdcp2_info.hdcp_rx.send_cert.rx_caps,
+ (u8 *)&hdcp_info->hdcp2_info.hdcp_tx.tx_caps,
+ hdcp_info->hdcp2_info.hdcp_rx.send_hprime.h_prime,
+ HDCP_2_2_H_PRIME_LEN);
+ if (ret != RET_COMPARE_PASS) {
+ if (hdcp_info->hdcp2_info.hdcp_handler.stored_km)
+ tee_clear_paring(hdcp_info);
+ err_code = HDCP_ERR_PROCESS_FAIL;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ if (hdcp_info->hdcp2_info.hdcp_handler.stored_km)
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A2F2, HDCP_2_2_LC_INIT);
+ else
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1,
+ HDCP_2_2_AKE_SEND_PAIRING_INFO);
+
+ pre_time = mtk_dp_get_system_time();
+ hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false;
+ }
+
+ /* HDCP2_MS_A1F1 */
+ /* HDCP_2_2_AKE_SEND_PAIRING_INFO */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
+ HDCP_2_2_AKE_SEND_PAIRING_INFO) {
+ if (hdcp_info->hdcp2_info.read_pairing)
+ dp_tx_hdcp2_read_msg(hdcp_info, HDCP_2_2_AKE_SEND_PAIRING_INFO);
+
+ /* Ekh_Km must be available less than 200ms, Give mode time for some Rx */
+ time = mtk_dp_get_time_diff(pre_time);
+ if (time > HDCP_2_2_PAIRING_TIMEOUT_MS * 2) {
+ err_code = HDCP_ERR_RESPONSE_TIMEROUT;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ if (!hdcp_info->hdcp2_info.hdcp_handler.recv_msg)
+ break;
+
+ /* Store m, km, Ekh(km) */
+ tee_ake_paring(hdcp_info,
+ hdcp_info->hdcp2_info.hdcp_rx.pairing_info.e_kh_km);
+
+ hdcp_info->hdcp2_info.hdcp_handler.send_pair = true;
+ hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false;
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A2F2, HDCP_2_2_LC_INIT);
+ pre_time = mtk_dp_get_system_time();
+ }
+ break;
+
+ case HDCP2_MS_A2F2:
+ /* HDCP2_MS_A2F2 */
+ /* HDCP_2_2_LC_INIT */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_LC_INIT) {
+ /* prepare Rn to send */
+ tmp = dp_tx_hdcp2_write_msg(hdcp_info, HDCP_2_2_LC_INIT);
+ if (!tmp) {
+ err_code = HDCP_ERR_SEND_MSG_FAIL;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+ hdcp_info->hdcp2_info.hdcp_handler.send_lc_init = true;
+
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A2F2, HDCP_2_2_LC_SEND_LPRIME);
+ pre_time = mtk_dp_get_system_time();
+ }
+
+ /* HDCP2_MS_A2F2 */
+ /* HDCP_2_2_LC_SEND_LPRIME */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_LC_SEND_LPRIME) {
+ time = mtk_dp_get_time_diff(pre_time);
+ if (time < HDCP_2_2_DP_HPRIME_READ_TIMEOUT_MS)
+ break;
+
+ if (hdcp_info->hdcp2_info.read_l_prime)
+ dp_tx_hdcp2_read_msg(hdcp_info, HDCP_2_2_LC_SEND_LPRIME);
+
+ if (!hdcp_info->hdcp2_info.hdcp_handler.recv_msg)
+ break;
+
+ ret = tee_lc_l_prime(hdcp_info, hdcp_info->hdcp2_info.hdcp_tx.lc_init.r_n,
+ hdcp_info->hdcp2_info.hdcp_rx.send_lprime.l_prime,
+ HDCP_2_2_L_PRIME_LEN);
+ if (ret != RET_COMPARE_PASS) {
+ err_code = HDCP_ERR_PROCESS_FAIL;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ DPTXHDCPMSG("2.x: L' is PASS!!\n");
+ hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false;
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A3F3, HDCP_2_2_NULL_MSG);
+ pre_time = mtk_dp_get_system_time();
+ }
+ break;
+
+ case HDCP2_MS_A3F3:
+ /* HDCP2_MS_A3F3 */
+ /* HDCP_2_2_NULL_MSG */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_NULL_MSG) {
+ tee_ske_enc_ks(hdcp_info, hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv,
+ hdcp_info->hdcp2_info.hdcp_tx.send_eks.e_dkey_ks);
+
+ tmp = dp_tx_hdcp2_write_msg(hdcp_info, HDCP_2_2_SKE_SEND_EKS);
+ if (!tmp) {
+ err_code = HDCP_ERR_SEND_MSG_FAIL;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ if (!hdcp_info->hdcp2_info.repeater)
+ dp_tx_hdcp2_write_msg(hdcp_info, HDCP_2_2_STREAM_TYPE);
+
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A3F3, HDCP_2_2_SKE_SEND_EKS);
+ pre_time = mtk_dp_get_system_time();
+ }
+
+ /* HDCP2_MS_A3F3 */
+ /* HDCP_2_2_SKE_SEND_EKS */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_SKE_SEND_EKS) {
+ time = mtk_dp_get_time_diff(pre_time);
+ if (time >= HDCP_2_2_DELAY_BEFORE_ENCRYPTION_EN)
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A4F4, HDCP_2_2_NULL_MSG);
+ }
+ break;
+
+ case HDCP2_MS_A4F4:
+ /* HDCP2_MS_A4F4 */
+ /* HDCP_2_2_NULL_MSG */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_NULL_MSG) {
+ if (!hdcp_info->hdcp2_info.repeater) {
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A5F5, HDCP_2_2_AUTH_DONE);
+ break;
+ }
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A6F6,
+ HDCP_2_2_REP_SEND_RECVID_LIST);
+ hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false;
+ pre_time = mtk_dp_get_system_time();
+ }
+ break;
+
+ case HDCP2_MS_A5F5:
+ /* HDCP2_MS_A5F5 */
+ /* HDCP_2_2_AUTH_DONE */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_AUTH_DONE) {
+ DPTXHDCPMSG("2.x: Authentication done!\n");
+ hdcp_info->auth_status = AUTH_PASS;
+ hdcp_info->hdcp2_info.retry_count = 0;
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A5F5, HDCP_2_2_NULL_MSG);
+ dp_tx_hdcp2_enable_auth(hdcp_info, true);
+ }
+ break;
+
+ case HDCP2_MS_A6F6:
+ /* HDCP2_MS_A6F6 */
+ /* HDCP_2_2_REP_SEND_RECVID_LIST */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
+ HDCP_2_2_REP_SEND_RECVID_LIST) {
+ if (hdcp_info->hdcp2_info.read_v_prime)
+ dp_tx_hdcp2_read_msg(hdcp_info, HDCP_2_2_REP_SEND_RECVID_LIST);
+
+ time = mtk_dp_get_time_diff(pre_time);
+ if (time > HDCP_2_2_RECVID_LIST_TIMEOUT_MS) {
+ err_code = HDCP_ERR_RESPONSE_TIMEROUT;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ if (!hdcp_info->hdcp2_info.hdcp_handler.recv_msg)
+ break;
+
+ pre_time = mtk_dp_get_system_time();
+ hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false;
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A7F7,
+ HDCP_2_2_REP_VERIFY_RECVID_LIST);
+ }
+ break;
+
+ case HDCP2_MS_A7F7:
+ /* HDCP2_MS_A7F7 */
+ /* HDCP_2_2_REP_VERIFY_RECVID_LIST */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
+ HDCP_2_2_REP_VERIFY_RECVID_LIST) {
+ if ((hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.rx_info[1]
+ & (BIT(2) | BIT(3))) != 0) {
+ pr_err("2.x: DEVS_EXCEEDED or CASCADE_EXCEDDED!\n");
+ err_code = HDCP_ERR_PROCESS_FAIL;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ /* check seqNumV here */
+ tmp = dp_tx_hdcp2_check_seq_num_v(hdcp_info);
+ if (!tmp) {
+ err_code = HDCP_ERR_PROCESS_FAIL;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ tmp = dp_tx_hdcp2_recv_rep_auth_send_recv_id_list(hdcp_info);
+ if (!tmp) {
+ err_code = HDCP_ERR_PROCESS_FAIL;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A8F8, HDCP_2_2_REP_SEND_ACK);
+ }
+ break;
+
+ case HDCP2_MS_A8F8:
+ /* HDCP2_MS_A8F8 */
+ /* HDCP_2_2_REP_SEND_ACK */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_REP_SEND_ACK) {
+ tmp = dp_tx_hdcp2_write_msg(hdcp_info, HDCP_2_2_REP_SEND_ACK);
+ if (!tmp) {
+ err_code = HDCP_ERR_SEND_MSG_FAIL;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ time = mtk_dp_get_time_diff(pre_time);
+ if (time > HDCP2_REP_SEND_ACK) {
+ err_code = HDCP_ERR_RESPONSE_TIMEROUT;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A9F9, HDCP_2_2_REP_STREAM_MANAGE);
+ hdcp_info->hdcp2_info.hdcp_handler.retry_cnt = 0;
+ }
+ break;
+
+ case HDCP2_MS_A9F9:
+ /* HDCP2_MS_A9F9 */
+ /* HDCP_2_2_REP_STREAM_MANAGE */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_REP_STREAM_MANAGE) {
+ tmp = dp_tx_hdcp2_process_rep_auth_stream_manage(hdcp_info);
+ if (!tmp) {
+ err_code = HDCP_ERR_PROCESS_FAIL;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ tmp = dp_tx_hdcp2_write_msg(hdcp_info, HDCP_2_2_REP_STREAM_MANAGE);
+ if (!tmp) {
+ err_code = HDCP_ERR_SEND_MSG_FAIL;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ pre_time = mtk_dp_get_system_time();
+ hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false;
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A9F9,
+ HDCP_2_2_REP_STREAM_READY);
+ }
+
+ /* HDCP2_MS_A9F9 */
+ /* HDCP_2_2_REP_STREAM_READY */
+ if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_REP_STREAM_READY) {
+ time = mtk_dp_get_time_diff(pre_time);
+ if (time <= HDCP_2_2_STREAM_READY_TIMEOUT_MS / 2)
+ break;
+
+ dp_tx_hdcp2_read_msg(hdcp_info, HDCP_2_2_REP_STREAM_READY);
+
+ time = mtk_dp_get_time_diff(pre_time);
+ if (time > HDCP_2_2_STREAM_READY_TIMEOUT_MS) {
+ err_code = HDCP_ERR_RESPONSE_TIMEROUT;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ if (!hdcp_info->hdcp2_info.hdcp_handler.recv_msg) {
+ if (hdcp_info->hdcp2_info.hdcp_handler.retry_cnt
+ >= HDCP2_STREAM_MANAGE_RETRY_CNT) {
+ err_code = HDCP_ERR_RESPONSE_TIMEROUT;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ hdcp_info->hdcp2_info.hdcp_handler.retry_cnt++;
+
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A9F9,
+ HDCP_2_2_REP_STREAM_READY);
+ break;
+ }
+
+ tmp = dp_tx_hdcp2_recv_rep_auth_stream_ready(hdcp_info);
+ if (!tmp) {
+ err_code = HDCP_ERR_PROCESS_FAIL;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A5F5, HDCP_2_2_AUTH_DONE);
+ }
+ break;
+
+ default:
+ err_code = HDCP_ERR_UNKNOWN_STATE;
+ dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
+ break;
+ }
+
+ return err_code;
+}
+
+void dp_tx_hdcp2_set_start_auth(struct mtk_hdcp_info *hdcp_info, bool enable)
+{
+ hdcp_info->hdcp2_info.enable = enable;
+
+ if (enable) {
+ hdcp_info->auth_status = AUTH_INIT;
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A0F0, HDCP_2_2_NULL_MSG);
+ } else {
+ hdcp_info->auth_status = AUTH_ZERO;
+ dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_H1P1, HDCP_2_2_NULL_MSG);
+ dp_tx_hdcp2_enable_auth(hdcp_info, false);
+ }
+
+ hdcp_info->hdcp2_info.retry_count = 0;
+}
+
+bool dp_tx_hdcp2_support(struct mtk_hdcp_info *hdcp_info)
+{
+ struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+ u8 tmp[3];
+ int ret;
+
+ drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RX_CAPS_OFFSET, tmp, HDCP_2_2_RXCAPS_LEN);
+
+ if (HDCP_2_2_DP_HDCP_CAPABLE(tmp[2]) && tmp[0] == HDCP_2_2_RX_CAPS_VERSION_VAL) {
+ hdcp_info->hdcp2_info.enable = true;
+ hdcp_info->hdcp2_info.repeater = tmp[2] & BIT(0);
+ } else {
+ hdcp_info->hdcp2_info.enable = false;
+ }
+
+ DPTXHDCPMSG("2.x: CAPABLE: %d, Reapeater: %d\n", hdcp_info->hdcp2_info.enable,
+ hdcp_info->hdcp2_info.repeater);
+
+ if (!hdcp_info->hdcp2_info.enable)
+ return false;
+
+ ret = tee_add_device(hdcp_info, HDCP_VERSION_2X);
+ if (ret != RET_SUCCESS) {
+ pr_err("2.x: HDCP TA has some error\n");
+ hdcp_info->hdcp2_info.enable = false;
+ }
+
+ return hdcp_info->hdcp2_info.enable;
+}
+
+bool dp_tx_hdcp2_irq(struct mtk_hdcp_info *hdcp_info)
+{
+ struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+ u8 rx_status = 0;
+
+ drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status,
+ HDCP_2_2_DP_RXSTATUS_LEN);
+
+ if (rx_status & BIT(0)) {
+ DPTXHDCPMSG("2.x: READY_BIT0 Ready!\n");
+ hdcp_info->hdcp2_info.read_v_prime = true;
+ }
+
+ if (rx_status & BIT(1)) {
+ DPTXHDCPMSG("2.x: H'_AVAILABLE Ready!\n");
+ hdcp_info->hdcp2_info.read_h_prime = true;
+ }
+
+ if (rx_status & BIT(2)) {
+ DPTXHDCPMSG("2.x: PAIRING_AVAILABLE Ready!\n");
+ hdcp_info->hdcp2_info.read_pairing = true;
+ }
+
+ if (rx_status & BIT(4) || rx_status & BIT(3)) {
+ DPTXHDCPMSG("2.x: Re-Auth HDCP2X!\n");
+ dp_tx_hdcp2_set_start_auth(hdcp_info, true);
+ mtk_dp_authentication(hdcp_info);
+ }
+
+ return true;
+}
diff --git a/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h
new file mode 100644
index 000000000000..df309d3dcc6e
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019-2024 MediaTek Inc.
+ */
+
+#ifndef _MTK_dp_HDCP2_H_
+#define _MTK_dp_HDCP2_H_
+
+#include "tlc_dp_hdcp.h"
+
+/* Timeout relative */
+#define HDCP2_REP_SEND_ACK 2000
+
+/* Patch for QD980 LLCTS */
+#define HDCP2_TX_RETRY_CNT 3
+#define HDCP2_TX_LC_RETRY_CNT 1023
+#define HDCP2_STREAM_MANAGE_RETRY_CNT 8
+
+/* HDCP2.2 Msg IDs */
+#define HDCP_2_2_STREAM_TYPE 20
+#define HDCP_2_2_REP_VERIFY_RECVID_LIST 21
+#define HDCP_2_2_AUTH_FAIL 22
+#define HDCP_2_2_AUTH_DONE 23
+
+enum ENUM_HDCP2TX_MAIN_STATE {
+ HDCP2_MS_H1P1 = 0,
+ HDCP2_MS_A0F0 = 1,
+ HDCP2_MS_A1F1 = 2,
+ HDCP2_MS_A2F2 = 3,
+ HDCP2_MS_A3F3 = 4,
+ HDCP2_MS_A4F4 = 5,
+ HDCP2_MS_A5F5 = 6,
+ HDCP2_MS_A6F6 = 7,
+ HDCP2_MS_A7F7 = 8,
+ HDCP2_MS_A8F8 = 9,
+ HDCP2_MS_A9F9 = 10
+};
+
+enum ENUM_HDCP_ERR_CODE {
+ HDCP_ERR_NONE = 0,
+ HDCP_ERR_UNKNOWN_STATE,
+ HDCP_ERR_SEND_MSG_FAIL,
+ HDCP_ERR_RESPONSE_TIMEROUT,
+ HDCP_ERR_PROCESS_FAIL
+};
+
+int dp_tx_hdcp2_fsm(struct mtk_hdcp_info *hdcp_info);
+void dp_tx_hdcp2_set_start_auth(struct mtk_hdcp_info *hdcp_info, bool enable);
+bool dp_tx_hdcp2_support(struct mtk_hdcp_info *hdcp_info);
+bool dp_tx_hdcp2_irq(struct mtk_hdcp_info *hdcp_info);
+
+#endif /* _MTK_dp_HDCP2_H_ */
diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
index 709b79480693..5cf5059762ed 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp_reg.h
+++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
@@ -1,8 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (c) 2019-2022 MediaTek Inc.
+ * Copyright (c) 2019-2024 MediaTek Inc.
* Copyright (c) 2022 BayLibre
*/
+
#ifndef _MTK_DP_REG_H_
#define _MTK_DP_REG_H_
@@ -277,6 +278,7 @@
#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT BIT(1)
#define MTK_DP_TRANS_P0_34A4 0x34a4
#define LANE_NUM_DP_TRANS_P0_MASK GENMASK(3, 2)
+#define MTK_DP_TRANS_P0_34D0 0x34D0
#define MTK_DP_TRANS_P0_3540 0x3540
#define FEC_EN_DP_TRANS_P0_MASK BIT(0)
#define FEC_CLOCK_EN_MODE_DP_TRANS_P0 BIT(3)
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
index 4e3d9f7b4d8c..7f4a8f7dfce2 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
@@ -25,6 +25,7 @@
#include <drm/drm_edid.h>
#include <drm/drm_of.h>
#include <drm/drm_simple_kms_helper.h>
+#include <drm/display/drm_hdcp_helper.h>
#include "mtk_disp_drv.h"
#include "mtk_dpi_regs.h"
@@ -820,6 +821,8 @@ static int mtk_dpi_bind(struct device *dev, struct device *master, void *data)
}
drm_connector_attach_encoder(dpi->connector, &dpi->encoder);
+ drm_connector_attach_content_protection_property(dpi->connector, true);
+
return 0;
err_cleanup:
--
2.43.0
Hi mac.shen,
kernel test robot noticed the following build warnings:
[auto build test WARNING on drm-misc/drm-misc-next]
[also build test WARNING on pza/reset/next linus/master v6.8-rc3 next-20240205]
[cannot apply to pza/imx-drm/next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/mac-shen/Subject-PATCH-drm-mediatek-dp-Add-tee-client-application-for-HDCP-feature/20240205-163727
base: git://anongit.freedesktop.org/drm/drm-misc drm-misc-next
patch link: https://lore.kernel.org/r/20240205055055.25340-2-mac.shen%40mediatek.com
patch subject: [PATCH v2 1/3] Subject: [PATCH] drm/mediatek/dp: Add tee client application for HDCP feature
config: arm64-defconfig (https://download.01.org/0day-ci/archive/20240205/[email protected]/config)
compiler: aarch64-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240205/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
>> drivers/gpu/drm/mediatek/tlc_dp_hdcp.c:34: warning: Function parameter or struct member 'dp_tee_priv' not described in 'dp_tee_op_send'
>> drivers/gpu/drm/mediatek/tlc_dp_hdcp.c:34: warning: Function parameter or struct member 'cmd_id' not described in 'dp_tee_op_send'
vim +34 drivers/gpu/drm/mediatek/tlc_dp_hdcp.c
13
14 /*
15 * TA_FTPM_UUID: 99975014-3c7c-54ea-8487-a80d215ea92c
16 *
17 * Randomly generated, and must correspond to the GUID on the TA side.
18 * Defined here in the reference implementation:
19 * https://github.com/microsoft/ms-tpm-20-ref/blob/master/Samples/ARM32-FirmwareTPM/optee_ta/fTPM/include/fTPM.h#L42
20 */
21 static const uuid_t dp_ta_uuid =
22 UUID_INIT(0x99975014, 0x3c7c, 0x54ea,
23 0x84, 0x87, 0xa8, 0x0d, 0x21, 0x5e, 0xa9, 0x2c);
24
25 /**
26 * dp_tee_op_send() - send dp commands through the TEE shared memory.
27 * @len: the number of bytes to send.
28 *
29 * Return:
30 * In case of success, returns 0.
31 * On failure, -errno
32 */
33 static int dp_tee_op_send(struct dp_tee_private *dp_tee_priv, size_t len, u32 cmd_id)
> 34 {
35 int rc;
36 u8 *temp_buf;
37 struct tee_ioctl_invoke_arg transceive_args;
38 struct tee_param command_params[4];
39 struct tee_shm *shm = dp_tee_priv->shm;
40
41 if (len > MAX_COMMAND_SIZE) {
42 TLCERR("%s: len=%zd exceeds MAX_COMMAND_SIZE supported by dp TA\n", __func__, len);
43 return -EIO;
44 }
45
46 memset(&transceive_args, 0, sizeof(transceive_args));
47 memset(command_params, 0, sizeof(command_params));
48 dp_tee_priv->resp_len = 0;
49
50 /* Invoke FTPM_OPTEE_TA_SUBMIT_COMMAND function of dp TA */
51 transceive_args = (struct tee_ioctl_invoke_arg) {
52 .func = cmd_id,
53 .session = dp_tee_priv->session,
54 .num_params = 4,
55 };
56
57 /* Fill FTPM_OPTEE_TA_SUBMIT_COMMAND parameters */
58 command_params[0] = (struct tee_param) {
59 .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
60 .u.memref = {
61 .shm = shm,
62 .size = len,
63 .shm_offs = 0,
64 },
65 };
66
67 command_params[1] = (struct tee_param) {
68 .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT,
69 .u.memref = {
70 .shm = shm,
71 .size = MAX_RESPONSE_SIZE,
72 .shm_offs = MAX_COMMAND_SIZE,
73 },
74 };
75
76 rc = tee_client_invoke_func(dp_tee_priv->ctx, &transceive_args, command_params);
77 if (rc < 0 || transceive_args.ret != 0) {
78 TLCERR("%s: invoke error: 0x%x\n", __func__, transceive_args.ret);
79 return (rc < 0) ? rc : transceive_args.ret;
80 }
81
82 temp_buf = tee_shm_get_va(shm, command_params[1].u.memref.shm_offs);
83 if (IS_ERR(temp_buf)) {
84 TLCERR("%s: tee_shm_get_va failed for receive\n", __func__);
85 return PTR_ERR(temp_buf);
86 }
87
88 /* Sanity checks look good, cache the response */
89 memcpy(dp_tee_priv->resp_buf, temp_buf, MAX_RESPONSE_SIZE / 2);
90 dp_tee_priv->resp_len = MAX_RESPONSE_SIZE / 2;
91
92 return 0;
93 }
94
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Il 05/02/24 06:50, mac.shen ha scritto:
> Add tee client application which will be used for
> HDCP 1.x and 2.x authentication in DisplayPort.
>
> Changes in v2:
> - remove ca folder, and change file name with lower case
> - refine the tci_t structure to make the data to tee can
> through this structure
> - remove aux and regs from mtk_hdcp_info structure
> - remove some definitions, and use the definitions in
> include/drm/drm_hdcp.h
> - remove useless code
> per suggestion from the previous thread:
> https://lore.kernel.org/all/8fff59b5567449d8201dd1138c8fa
> [email protected]/
>
> Signed-off-by: mac.shen <[email protected]>
> ---
> drivers/gpu/drm/mediatek/Makefile | 5 +-
> drivers/gpu/drm/mediatek/tci.h | 156 +++++++
> drivers/gpu/drm/mediatek/tlc_dp_hdcp.c | 598 +++++++++++++++++++++++++
> drivers/gpu/drm/mediatek/tlc_dp_hdcp.h | 414 +++++++++++++++++
> 4 files changed, 1172 insertions(+), 1 deletion(-)
> create mode 100644 drivers/gpu/drm/mediatek/tci.h
> create mode 100644 drivers/gpu/drm/mediatek/tlc_dp_hdcp.c
> create mode 100644 drivers/gpu/drm/mediatek/tlc_dp_hdcp.h
>
> diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> index d4d193f60271..c80e6c2f9336 100644
> --- a/drivers/gpu/drm/mediatek/Makefile
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -26,4 +26,7 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
>
> obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
>
> -obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o
> +mtk-dp-objs := tlc_dp_hdcp.o \
> + mtk_dp.o
> +
> +obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk-dp.o
> diff --git a/drivers/gpu/drm/mediatek/tci.h b/drivers/gpu/drm/mediatek/tci.h
> new file mode 100644
> index 000000000000..f2239ea3ffbf
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/tci.h
> @@ -0,0 +1,156 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019-2024 MediaTek Inc.
> + */
> +
> +#ifndef _TCI_H_
> +#define _TCI_H_
> +
> +#include <drm/display/drm_hdcp.h>
> +
> +#define CMD_DEVICE_ADDED 1
> +#define CMD_DEVICE_REMOVE 2
> +#define CMD_WRITE_VAL 3
> +#define CMD_DEVICE_CLEAN 4
> +#define CMD_ENABLE_ENCRYPT 5
> +
> +/* V1.3 */
> +#define CMD_CALCULATE_LM 11
> +#define CMD_COMPARE_R0 12
> +#define CMD_COMPARE_V1 13
> +#define CMD_GET_AKSV 14
> +
> +/* V2.2 */
> +#define CMD_AKE_CERTIFICATE 20
> +#define CMD_ENC_KM 21
> +#define CMD_AKE_H_PRIME 22
> +#define CMD_AKE_PARING 23
> +#define CMD_LC_L_PRIME 24
> +#define CMD_COMPARE_L 25
> +#define CMD_SKE_CAL_EKS 26
> +
> +#define CMD_COMPARE_V2 27
> +#define CMD_COMPARE_M 28
> +
> +/* Need remove in furture */
> +#define CMD_LOAD_KEY 50
> +
> +#define RET_COMPARE_PASS 0
> +#define RET_COMPARE_FAIL 1
> +#define RET_NEW_DEVICE 2
> +#define RET_STORED_DEVICE 3
> +
> +#define TYPE_HDCP_PARAM_AN 10
> +#define TYPE_HDCP_PARAM_RST_1 11
> +#define TYPE_HDCP_PARAM_RST_2 12
> +#define TYPE_HDCP_ENABLE_ENCRYPT 13
> +#define TYPE_HDCP_DISABLE_ENCRYPT 14
> +
> +#define TYPE_HDCP13_KEY 20
> +#define TYPE_HDCP22_KEY 21
> +
> +// reserved:2
Please, C-style comments only.
> +#define HDCP2_CERTRX_LEN (HDCP_2_2_RECEIVER_ID_LEN + HDCP_2_2_K_PUB_RX_LEN + \
> + 2 + HDCP_2_2_DCP_LLC_SIG_LEN)
> +// version:1
> +#define HDCP_2_2_TXCAPS_LEN (HDCP_2_2_TXCAP_MASK_LEN + 1)
> +#define PARAM_LEN 1024
> +
> +#define TCI_LENGTH sizeof(struct tci_t)
> +
> +struct cmd_hdcp_init_for_verion_t {
> + u32 version;
> + bool need_load_key;
> +};
> +
> +struct cmd_hdcp_write_val_t {
> + u8 type;
> + u8 len;
> + u8 val[DRM_HDCP_AN_LEN];
> +};
> +
> +struct cmd_hdcp_calculate_lm_t {
> + u8 bksv[DRM_HDCP_KSV_LEN];
> +};
> +
> +struct cmd_hdcp_get_aksv_t {
> + u8 aksv[DRM_HDCP_KSV_LEN];
> +};
> +
> +struct cmd_hdcp_ake_certificate_t {
> + u8 certification[HDCP2_CERTRX_LEN];
> + bool stored;
> + u8 m[HDCP_2_2_E_KH_KM_M_LEN - HDCP_2_2_E_KH_KM_LEN];
> + u8 ekm[HDCP_2_2_E_KH_KM_LEN];
> +};
> +
> +struct cmd_hdcp_ake_paring_t {
> + u8 ekm[HDCP_2_2_E_KH_KM_LEN];
> +};
> +
> +struct cmd_hdcp_enc_km_t {
> + u8 enc_km[HDCP_2_2_E_KPUB_KM_LEN];
> +};
> +
> +struct cmd_hdcp_ake_h_prime_t {
> + u8 rtx[HDCP_2_2_RTX_LEN];
> + u8 rrx[HDCP_2_2_RRX_LEN];
> + u8 rx_caps[HDCP_2_2_RXCAPS_LEN];
> + u8 tx_caps[HDCP_2_2_TXCAPS_LEN];
> + u32 rx_h_len;
> + u8 rx_h[HDCP_2_2_H_PRIME_LEN];
> +};
> +
> +struct cmd_hdcp_lc_l_prime_t {
> + u8 rn[HDCP_2_2_RN_LEN];
> + u32 rx_l_len;
> + u8 rx_l[HDCP_2_2_L_PRIME_LEN];
> +};
> +
> +struct cmd_hdcp_ske_eks_t {
> + u8 riv[HDCP_2_2_RIV_LEN];
> + u32 eks_len;
> + u32 eks;
> +};
> +
> +struct cmd_hdcp_compare_t {
> + u32 rx_val_len;
> + u8 rx_val[HDCP_2_2_MPRIME_LEN];
> + u32 param_len;
> + u8 param[PARAM_LEN];
> + u32 out_len;
> + u32 out;
> +};
> +
> +union tci_cmd_body_t {
> + /* Init with special HDCP version */
> + struct cmd_hdcp_init_for_verion_t cmd_hdcp_init_for_verion;
> + /* Write uint32 data to hw */
> + struct cmd_hdcp_write_val_t cmd_hdcp_write_val;
> + /* Get aksv */
> + struct cmd_hdcp_get_aksv_t cmd_hdcp_get_aksv;
> + /* Calculate r0 */
> + struct cmd_hdcp_calculate_lm_t cmd_hdcp_calculate_lm;
> + /* Generate signature for certificate */
> + struct cmd_hdcp_ake_certificate_t cmd_hdcp_ake_certificate;
> + /* To store ekm */
> + struct cmd_hdcp_ake_paring_t cmd_hdcp_ake_paring;
> + /* Encrypt km for V2.2 */
> + struct cmd_hdcp_enc_km_t cmd_hdcp_enc_km;
> + /* Compute H prime */
> + struct cmd_hdcp_ake_h_prime_t cmd_hdcp_ake_h_prime;
> + /* Compute L prime */
> + struct cmd_hdcp_lc_l_prime_t cmd_hdcp_lc_l_prime;
> + /* Compute eks */
> + struct cmd_hdcp_ske_eks_t cmd_hdcp_ske_eks;
> + /* Compare */
> + struct cmd_hdcp_compare_t cmd_hdcp_compare;
> +} __packed;
> +
> +struct tci_t {
> + u32 command_id;
> + u32 return_code;
> + union tci_cmd_body_t cmd_body;
> +};
> +
> +#endif /* _TCI_H_ */
> diff --git a/drivers/gpu/drm/mediatek/tlc_dp_hdcp.c b/drivers/gpu/drm/mediatek/tlc_dp_hdcp.c
> new file mode 100644
> index 000000000000..147329324648
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/tlc_dp_hdcp.c
> @@ -0,0 +1,598 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019-2024 MediaTek Inc.
> + */
> +
> +#include <linux/types.h>
> +#include <linux/string.h>
> +#include <linux/slab.h>
> +#include "tlc_dp_hdcp.h"
> +
> +#define DEFAULT_WRITE_VAL_LEN 1
> +#define DEFAULT_WRITE_VAL 0
> +
> +/*
> + * TA_FTPM_UUID: 99975014-3c7c-54ea-8487-a80d215ea92c
> + *
> + * Randomly generated, and must correspond to the GUID on the TA side.
> + * Defined here in the reference implementation:
> + * https://github.com/microsoft/ms-tpm-20-ref/blob/master/Samples/ARM32-FirmwareTPM/optee_ta/fTPM/include/fTPM.h#L42
> + */
> +static const uuid_t dp_ta_uuid =
> + UUID_INIT(0x99975014, 0x3c7c, 0x54ea,
> + 0x84, 0x87, 0xa8, 0x0d, 0x21, 0x5e, 0xa9, 0x2c);
> +
> +/**
> + * dp_tee_op_send() - send dp commands through the TEE shared memory.
> + * @len: the number of bytes to send.
> + *
> + * Return:
> + * In case of success, returns 0.
> + * On failure, -errno
I think that's not what you want as final format, please check the documentation.
https://docs.kernel.org/doc-guide/kernel-doc.html#return-values
> + */
> +static int dp_tee_op_send(struct dp_tee_private *dp_tee_priv, size_t len, u32 cmd_id)
> +{
> + int rc;
> + u8 *temp_buf;
> + struct tee_ioctl_invoke_arg transceive_args;
> + struct tee_param command_params[4];
> + struct tee_shm *shm = dp_tee_priv->shm;
Please reorder:
struct tee_ioctl_invoke_arg transceive_args;
struct tee_shm *shm = dp_tee_priv->shm;
struct tee_param command_params[4];
u8 *temp_buf;
int rc;
> +
> + if (len > MAX_COMMAND_SIZE) {
> + TLCERR("%s: len=%zd exceeds MAX_COMMAND_SIZE supported by dp TA\n", __func__, len);
> + return -EIO;
> + }
> +
> + memset(&transceive_args, 0, sizeof(transceive_args));
> + memset(command_params, 0, sizeof(command_params));
> + dp_tee_priv->resp_len = 0;
> +
> + /* Invoke FTPM_OPTEE_TA_SUBMIT_COMMAND function of dp TA */
> + transceive_args = (struct tee_ioctl_invoke_arg) {
> + .func = cmd_id,
> + .session = dp_tee_priv->session,
> + .num_params = 4,
> + };
> +
> + /* Fill FTPM_OPTEE_TA_SUBMIT_COMMAND parameters */
> + command_params[0] = (struct tee_param) {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
> + .u.memref = {
> + .shm = shm,
> + .size = len,
> + .shm_offs = 0,
> + },
> + };
> +
> + command_params[1] = (struct tee_param) {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT,
> + .u.memref = {
> + .shm = shm,
> + .size = MAX_RESPONSE_SIZE,
> + .shm_offs = MAX_COMMAND_SIZE,
> + },
> + };
> +
> + rc = tee_client_invoke_func(dp_tee_priv->ctx, &transceive_args, command_params);
> + if (rc < 0 || transceive_args.ret != 0) {
> + TLCERR("%s: invoke error: 0x%x\n", __func__, transceive_args.ret);
> + return (rc < 0) ? rc : transceive_args.ret;
> + }
> +
> + temp_buf = tee_shm_get_va(shm, command_params[1].u.memref.shm_offs);
> + if (IS_ERR(temp_buf)) {
> + TLCERR("%s: tee_shm_get_va failed for receive\n", __func__);
> + return PTR_ERR(temp_buf);
> + }
> +
> + /* Sanity checks look good, cache the response */
> + memcpy(dp_tee_priv->resp_buf, temp_buf, MAX_RESPONSE_SIZE / 2);
> + dp_tee_priv->resp_len = MAX_RESPONSE_SIZE / 2;
> +
> + return 0;
> +}
> +
> +/*
> + * Check whether this driver supports the dp TA in the TEE instance
> + * represented by the params (ver/data) to this function.
> + */
> +static int dp_tee_match(struct tee_ioctl_version_data *ver, const void *data)
> +{
> + /*
> + * Currently this driver only support GP Complaint OPTEE based dp TA
> + */
> + if (ver->impl_id == TEE_IMPL_ID_OPTEE && ver->gen_caps & TEE_GEN_CAP_GP)
> + return 1;
> + else
> + return 0;
/*
* Check whether this driver supports the dp TA in the TEE instance
* represented by the params (ver/data) to this function.
*
* Note that currently this driver only supports GP Compliant OPTEE
* based dp TA.
*/
static int dp_tee_match(struct tee_ioctl_version_data *ver, const void *data)
{
return ver->impl_id == TEE_IMPL_ID_OPTEE &&
ver->gen_caps & TEE_GEN_CAP_GP
}
That's shorter :-)
> +}
> +
> +int tee_add_device(struct mtk_hdcp_info *hdcp_info, u32 version)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = NULL;
You don't need this NULL assignment...
> + struct tee_ioctl_open_session_arg sess_arg;
> + struct tci_t *tci;
struct tee_ioctl_open_session_arg sess_arg;
struct dp_tee_private *dp_tee_priv;
struct tci_t *tci;
int rc;
> +
> + if (hdcp_info->g_init)
> + tee_remove_device(hdcp_info);
> +
> + dp_tee_priv = kzalloc(sizeof(*dp_tee_priv), GFP_KERNEL);
> + if (!dp_tee_priv) {
> + kfree(dp_tee_priv);
There's no need to kfree something that couldn't be allocated.
> + TLCERR("%s: tee_alloc_memory failed\n", __func__);
> + return -ENOMEM;
> + }
> + hdcp_info->g_dp_tee_priv = dp_tee_priv;
> +
> + /* Open context with TEE driver */
> + dp_tee_priv->ctx = tee_client_open_context(NULL, dp_tee_match, NULL, NULL);
> + if (IS_ERR(dp_tee_priv->ctx)) {
/* We return -EPROBE_DEFER because ... why?!
* does it even make any sense to override this
* return value? I don't think so.
*/
if (PTR_ERR(dp_tee_priv->ctx) == -ENOENT)
rc = -EPROBE_DEFER;
else
rc = PTR_ERR(dp_tee_priv->ctx);
goto free_priv;
}
> + if (PTR_ERR(dp_tee_priv->ctx) == -ENOENT) {
> + kfree(dp_tee_priv);
> + return -EPROBE_DEFER;
> + }
> + kfree(dp_tee_priv);
> + TLCERR("%s: tee_client_open_context failed\n", __func__);
> + return PTR_ERR(dp_tee_priv->ctx);
> + }
> +
> + /* Open a session with dp TA */
> + memset(&sess_arg, 0, sizeof(sess_arg));
> + export_uuid(sess_arg.uuid, &dp_ta_uuid);
> + sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
> + sess_arg.num_params = 0;
> +
> + rc = tee_client_open_session(dp_tee_priv->ctx, &sess_arg, NULL);
> + if (rc < 0 || sess_arg.ret != 0) {
> + kfree(dp_tee_priv);
We don't kfree here because the goto will take care of that already...
P.S.: same for the other instances
> + TLCERR("tee_client_open_session failed, err=%x\n", sess_arg.ret);
> + rc = -EINVAL;
> + goto out_tee_session;
> + }
> + dp_tee_priv->session = sess_arg.session;
> +
> + /* Allocate dynamic shared memory with dp TA */
> + dp_tee_priv->shm = tee_shm_alloc_kernel_buf(dp_tee_priv->ctx, MAX_COMMAND_SIZE
> + + MAX_RESPONSE_SIZE);
> + if (IS_ERR(dp_tee_priv->shm)) {
> + kfree(dp_tee_priv);
> + TLCERR("%s: tee_shm_alloc_kernel_buf failed\n", __func__);
> + rc = -ENOMEM;
> + goto out_shm_alloc;
> + }
> + TLCINFO("Register 8k share memory successfully, (%p)",
> + dp_tee_priv->shm->kaddr);
Don't print pointer addresses, it won't mean anything for most people as there will
be randomization and - if not - could be used for a vulnerability.
Please, just don't.
> +
> + /* Copy parameter for add new device */
> + tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
> + memset(tci, 0, TCI_LENGTH);
> + tci->command_id = CMD_DEVICE_ADDED;
> + tci->cmd_body.cmd_hdcp_init_for_verion.version = version;
> + tci->cmd_body.cmd_hdcp_init_for_verion.need_load_key = true;
> +
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_DEVICE_ADDED);
> + if (rc != 0) {
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> + tee_remove_device(hdcp_info);
> + return rc;
> + }
> +
> + hdcp_info->g_init = true;
> +
> + return rc;
If you intend to pass on a result of dp_tee_op_send, then pass that to a pointer
in the function param.
Here, you shall return 0.
> +
> +out_shm_alloc:
> + tee_client_close_session(dp_tee_priv->ctx, dp_tee_priv->session);
> +out_tee_session:
> + tee_client_close_context(dp_tee_priv->ctx);
free_priv:
kfree(dp_tee_priv);
> +
> + return rc;
> +}
> +
> +void tee_remove_device(struct mtk_hdcp_info *hdcp_info)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
int rc goes here
> +
> + if (!hdcp_info->g_init)
> + return;
> +
> + hdcp_info->g_init = false;
> + memset(tci, 0, TCI_LENGTH);
> + tci->command_id = CMD_DEVICE_REMOVE;
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_DEVICE_REMOVE);
> + if (rc != 0)
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> +
> + /* Free the shared memory pool */
> + tee_shm_free(dp_tee_priv->shm);
> +
> + /* Close the existing session with fTPM TA */
> + tee_client_close_session(dp_tee_priv->ctx, dp_tee_priv->session);
> +
> + /* Close the context with TEE driver */
> + tee_client_close_context(dp_tee_priv->ctx);
> +
> + /* Free the memory */
> + kfree(dp_tee_priv);
> +}
> +
> +int tee_clear_paring(struct mtk_hdcp_info *hdcp_info)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
int rc goes here
> +
> + /* Copy parameters */
> + memset(tci, 0, TCI_LENGTH);
> + tci->command_id = CMD_DEVICE_CLEAN;
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_DEVICE_CLEAN);
> + if (rc != 0) {
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> + return rc;
> + }
> +
> + return rc;
return 0;
> +}
> +
> +int tee_hdcp1x_set_tx_an(struct mtk_hdcp_info *hdcp_info, u8 *an_code)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
> +
> + /* Copy parameters */
> + tci->command_id = CMD_WRITE_VAL;
> + tci->cmd_body.cmd_hdcp_write_val.len = DRM_HDCP_AN_LEN;
> + tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_PARAM_AN;
> + memcpy(tci->cmd_body.cmd_hdcp_write_val.val, an_code, DRM_HDCP_AN_LEN);
> +
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_WRITE_VAL);
> + if (rc != 0) {
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> + return rc;
> + }
> +
> + return rc;
> +}
> +
> +int tee_hdcp_enable_encrypt(struct mtk_hdcp_info *hdcp_info, bool enable, u8 version)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
> +
> + /* Copy parameters */
> + tci->command_id = CMD_ENABLE_ENCRYPT;
tci->cmd_body.cmd_hdcp_write_val.type = enable ? TYPE_HDCP_ENABLE_ENCRYPT :
TYPE_HDCP_DISABLE_ENCRYPT;
> + if (enable)
> + tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_ENABLE_ENCRYPT;
> + else
> + tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_DISABLE_ENCRYPT;
> +
> + /* Set HDCP version supportted by device */
> + tci->cmd_body.cmd_hdcp_write_val.len = 1;
> + tci->cmd_body.cmd_hdcp_write_val.val[0] = version;
> +
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_ENABLE_ENCRYPT);
> + if (rc != 0) {
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> + return rc;
> + }
> +
return 0.... here and everywhere else.
> + return rc;
> +}
> +
> +int tee_hdcp1x_soft_rst(struct mtk_hdcp_info *hdcp_info)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
> +
> + /* Copy parameters */
> + tci->command_id = CMD_WRITE_VAL;
> + tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_PARAM_RST_1;
> + /* No need input. Set default value 0 for check */
> + tci->cmd_body.cmd_hdcp_write_val.len = DEFAULT_WRITE_VAL_LEN;
> + memset(tci->cmd_body.cmd_hdcp_write_val.val, DEFAULT_WRITE_VAL, DEFAULT_WRITE_VAL_LEN);
> +
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_WRITE_VAL);
> + if (rc != 0) {
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> + return rc;
> + }
> +
> + return rc;
> +}
> +
> +int tee_hdcp2_soft_rst(struct mtk_hdcp_info *hdcp_info)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
> +
> + tci->command_id = CMD_WRITE_VAL;
> + tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_PARAM_RST_2;
> + /* No need input. Set default value 0 for check */
> + tci->cmd_body.cmd_hdcp_write_val.len =
> + DEFAULT_WRITE_VAL_LEN;
Fits in one line.
> + memset(tci->cmd_body.cmd_hdcp_write_val.val, DEFAULT_WRITE_VAL, DEFAULT_WRITE_VAL_LEN);
> +
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_WRITE_VAL);
> + if (rc != 0) {
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> + return rc;
> + }
> +
> + return rc;
> +}
> +
> +/** V1.X **/
> +int tee_get_aksv(struct mtk_hdcp_info *hdcp_info, u8 *aksv)
> +{
Reorder the variables, here and everywhere else.
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
> +
> + /* Copy parameters */
> + tci->command_id = CMD_GET_AKSV;
> +
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_GET_AKSV);
> + if (rc != 0) {
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> + return rc;
> + }
> +
> + tci = (struct tci_t *)dp_tee_priv->resp_buf;
Use a different variable, otherwise this adds up confusion.
> + memcpy(aksv, tci->cmd_body.cmd_hdcp_get_aksv.aksv, DRM_HDCP_KSV_LEN);
> +
> + return rc;
> +}
> +
> +int tee_calculate_lm(struct mtk_hdcp_info *hdcp_info, u8 *bksv)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
> +
> + /* Copy parameters */
> + tci->command_id = CMD_CALCULATE_LM;
> + memcpy(tci->cmd_body.cmd_hdcp_calculate_lm.bksv, bksv, DRM_HDCP_KSV_LEN);
> +
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_CALCULATE_LM);
> + if (rc != 0) {
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> + return rc;
> + }
> +
> + return rc;
> +}
> +
> +int tee_compare_r0(struct mtk_hdcp_info *hdcp_info, u8 *r0, u32 len)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
> +
> + /* Copy parameters */
> + tci->command_id = CMD_COMPARE_R0;
> + tci->cmd_body.cmd_hdcp_compare.rx_val_len = len;
> + memcpy(tci->cmd_body.cmd_hdcp_compare.rx_val, r0, len);
> +
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_COMPARE_R0);
> + if (rc != 0) {
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> + return rc;
> + }
> +
> + return rc;
> +}
> +
> +int tee_hdcp1x_compute_compare_v(struct mtk_hdcp_info *hdcp_info,
> + u8 *crypto_param, u32 param_len, u8 *rx_v)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
> +
> + /* Copy parameters */
> + tci->command_id = CMD_COMPARE_V1;
> + tci->cmd_body.cmd_hdcp_compare.rx_val_len = 20;
> + tci->cmd_body.cmd_hdcp_compare.param_len = param_len;
> + memcpy(tci->cmd_body.cmd_hdcp_compare.rx_val, rx_v, 20);
> + memcpy(tci->cmd_body.cmd_hdcp_compare.param, crypto_param, param_len);
> +
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_COMPARE_V1);
> + if (rc != 0) {
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> + return rc;
> + }
> +
> + return rc;
> +}
> +
> +/** V2.X **/
> +int tee_ake_certificate(struct mtk_hdcp_info *hdcp_info,
> + u8 *certificate, bool *stored, u8 *out_m, u8 *out_ekm)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
> +
> + /* Copy parameters */
> + tci->command_id = CMD_AKE_CERTIFICATE;
> + memcpy(tci->cmd_body.cmd_hdcp_ake_certificate.certification,
> + certificate, HDCP2_CERTRX_LEN);
> +
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_AKE_CERTIFICATE);
> + if (rc != 0) {
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> + return rc;
> + }
> +
> + TLCINFO("verify signature: result %d", rc);
> + tci = (struct tci_t *)dp_tee_priv->resp_buf;
> + *stored = tci->cmd_body.cmd_hdcp_ake_certificate.stored;
> + memcpy(out_m, tci->cmd_body.cmd_hdcp_ake_certificate.m,
> + HDCP_2_2_E_KH_KM_M_LEN - HDCP_2_2_E_KH_KM_LEN);
> + memcpy(out_ekm, tci->cmd_body.cmd_hdcp_ake_certificate.ekm, HDCP_2_2_E_KH_KM_LEN);
> +
> + return rc;
> +}
> +
> +int tee_enc_rsaes_oaep(struct mtk_hdcp_info *hdcp_info, u8 *ekm)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
> +
> + /* Copy parameters */
> + tci->command_id = CMD_ENC_KM;
> +
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_ENC_KM);
> + if (rc != 0) {
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> + return rc;
> + }
> +
> + tci = (struct tci_t *)dp_tee_priv->resp_buf;
> + memcpy(ekm, tci->cmd_body.cmd_hdcp_enc_km.enc_km, HDCP_2_2_E_KPUB_KM_LEN);
> +
> + return rc;
> +}
> +
> +int tee_ake_h_prime(struct mtk_hdcp_info *hdcp_info,
> + u8 *rtx, u8 *rrx, u8 *rx_caps, u8 *tx_caps, u8 *rx_h, u32 rx_h_len)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
> +
> + /* Copy parameters */
> + tci->command_id = CMD_AKE_H_PRIME;
> + tci->cmd_body.cmd_hdcp_ake_h_prime.rx_h_len = rx_h_len;
> +
> + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rtx, rtx, HDCP_2_2_RTX_LEN);
> + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rrx, rrx, HDCP_2_2_RRX_LEN);
> + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rx_caps, rx_caps, HDCP_2_2_RXCAPS_LEN);
> + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.tx_caps, tx_caps, HDCP_2_2_TXCAPS_LEN);
> + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rx_h, rx_h, rx_h_len);
> +
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_AKE_H_PRIME);
> + if (rc != 0) {
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> + return rc;
> + }
> +
> + tci = (struct tci_t *)dp_tee_priv->resp_buf;
> + return tci->return_code;
> +}
> +
> +int tee_ake_paring(struct mtk_hdcp_info *hdcp_info, u8 *rx_ekm)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
> +
> + /* Copy parameters */
> + tci->command_id = CMD_AKE_PARING;
> + memcpy(tci->cmd_body.cmd_hdcp_ake_paring.ekm, rx_ekm, HDCP_2_2_E_KH_KM_LEN);
> +
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_AKE_PARING);
> + if (rc != 0)
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> +
> + return rc;
> +}
> +
> +int tee_lc_l_prime(struct mtk_hdcp_info *hdcp_info, u8 *rn, u8 *rx_l, u32 len)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
> +
> + /* Copy parameters */
> + tci->command_id = CMD_LC_L_PRIME;
> + memcpy(tci->cmd_body.cmd_hdcp_lc_l_prime.rn, rn, HDCP_2_2_RN_LEN);
> + tci->cmd_body.cmd_hdcp_lc_l_prime.rx_l_len = len;
> + memcpy(tci->cmd_body.cmd_hdcp_lc_l_prime.rx_l, rx_l, len);
> +
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_LC_L_PRIME);
> + if (rc != 0) {
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> + return rc;
> + }
> +
> + tci = (struct tci_t *)dp_tee_priv->resp_buf;
> + return tci->return_code;
> +}
> +
> +int tee_ske_enc_ks(struct mtk_hdcp_info *hdcp_info, u8 *riv, u8 *eks)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
> + u8 *share_buffer = NULL;
> +
> + /* Copy parameters */
> + tci->command_id = CMD_SKE_CAL_EKS;
> + memcpy(tci->cmd_body.cmd_hdcp_ske_eks.riv, riv, HDCP_2_2_RIV_LEN);
> +
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH + 16, CMD_SKE_CAL_EKS);
> + if (rc != 0)
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> +
> + share_buffer = (u8 *)dp_tee_priv->resp_buf;
> + memcpy(eks, share_buffer + TCI_LENGTH, 16);
> +
> + return rc;
> +}
> +
> +int tee_hdcp2_compute_compare_v(struct mtk_hdcp_info *hdcp_info,
> + u8 *crypto_param, u32 param_len, u8 *rx_v, u8 *tx_v)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
> + u8 *share_buffer = NULL;
> +
> + /* Copy parameters */
> + tci->command_id = CMD_COMPARE_V2;
> + tci->cmd_body.cmd_hdcp_compare.rx_val_len = 16;
> + tci->cmd_body.cmd_hdcp_compare.param_len = param_len;
> + memcpy(tci->cmd_body.cmd_hdcp_compare.rx_val, rx_v, 16);
> + memcpy(tci->cmd_body.cmd_hdcp_compare.param, crypto_param, param_len);
> +
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_COMPARE_V2);
> + if (rc != 0) {
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> + return rc;
> + }
> +
> + share_buffer = (u8 *)dp_tee_priv->resp_buf;
> + memcpy(tx_v, share_buffer + TCI_LENGTH, 16);
> +
> + return rc;
> +}
> +
> +int tee_hdcp2_compute_compare_m(struct mtk_hdcp_info *hdcp_info,
> + u8 *crypto_param, u32 param_len, u8 *rx_m)
> +{
> + int rc;
> + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv;
> + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr;
> +
> + /* Copy parameters */
> + tci->command_id = CMD_COMPARE_M;
> + tci->cmd_body.cmd_hdcp_compare.rx_val_len = HDCP_2_2_MPRIME_LEN;
> + tci->cmd_body.cmd_hdcp_compare.param_len = param_len;
> + memcpy(tci->cmd_body.cmd_hdcp_compare.rx_val, rx_m, HDCP_2_2_MPRIME_LEN);
> + memcpy(tci->cmd_body.cmd_hdcp_compare.param, crypto_param, param_len);
> +
> + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_COMPARE_M);
> + if (rc != 0)
> + TLCERR("tee_op_send failed, error=%x\n", rc);
> +
> + return rc;
> +}
> diff --git a/drivers/gpu/drm/mediatek/tlc_dp_hdcp.h b/drivers/gpu/drm/mediatek/tlc_dp_hdcp.h
> new file mode 100644
> index 000000000000..493d3c51a7a0
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/tlc_dp_hdcp.h
> @@ -0,0 +1,414 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019-2024 MediaTek Inc.
> + */
> +
> +#ifndef _TLC_DP_HDCP_H_
> +#define _TLC_DP_HDCP_H_
> +
> +#include <linux/printk.h>
> +#include <linux/tee_drv.h>
> +#include <linux/uuid.h>
> +#include <linux/types.h>
> +#include <linux/sched/clock.h>
> +#include <drm/display/drm_dp_helper.h>
> +#include "tci.h"
> +
> +#define TLCINFO(string, args...) pr_info("[TLC_HDCP]info: "string, ##args)
> +#define TLCERR(string, args...) pr_info("[TLC_HDCP]line:%d,err:%s:"string,\
> + __LINE__, __func__, ##args)
> +
> +#define DPTXHDCPFUNC(fmt, arg...) \
> + pr_info("[DPTXHDCP][%s line:%d]"pr_fmt(fmt), __func__, __LINE__, ##arg)
> +#define DPTXHDCPMSG(fmt, arg...) \
> + pr_info("[DPTXHDCP]"pr_fmt(fmt), ##arg)
> +#define DPTXHDCPERR(fmt, arg...) \
> + pr_err("[DPTXHDCP]"pr_fmt(fmt), ##arg)
You don't need those print wrappers and you can just use pr_{info,err} where
needed in the code.
Also, dev_info/dev_err would be preferred.
> +
> +#define RET_SUCCESS 0
Unused (and anyway unneeded) definition: please remove.
> +
> +/**
> + * hdcp version definitions
> + */
> +#define HDCP_NONE 0x0 // No HDCP supported, no secure data path
> +#define HDCP_V1 0x1 // HDCP version 1.0
> +#define HDCP_V2 0x2 // HDCP version 2.0 Type 1
> +#define HDCP_V2_1 0x3 // HDCP version 2.1 Type 1
> +#define HDCP_V2_2 0x4 // HDCP version 2.2 Type 1
> +#define HDCP_V2_3 0x5 // HDCP version 2.3 Type 1
Please introduce definitions in the same commits where you use them.
Also, C-style comments please.
> +
> +/* Local display only(content required version use only) */
> +#define HDCP_LOCAL_DISPLAY_ONLY 0xf
> +#define HDCP_NO_DIGITAL_OUTPUT 0xff // No digital output
> +#define HDCP_DEFAULT HDCP_NO_DIGITAL_OUTPUT // Default value
> +
> +#define HDCP_VERSION_1X 1
> +#define HDCP_VERSION_2X 2
> +
> +/* max. buffer size supported by dp */
> +#define MAX_COMMAND_SIZE 4096
> +#define MAX_RESPONSE_SIZE 4096
> +
> +#define HDCP1X_REP_MAXDEVS 128
> +#define HDCP1X_V_LEN 20
> +#define HDCP1X_B_INFO_LEN 2
> +
> +#define HDCP2_K_LEN 2
> +#define HDCP2_STREAMID_TYPE_LEN 2
> +
> +enum HDCP_RESULT {
> + AUTH_ZERO = 0,
> + AUTH_PREPARE = 1,
> + AUTH_INIT = 2,
> + AUTH_PASS = 3,
> + AUTH_FAIL = 4,
> +};
> +
> +struct dp_header {
> + __be16 tag;
> + __be32 length;
> + union {
> + __be32 ordinal;
> + __be32 return_code;
> + };
> +} __packed;
> +
> +/**
> + * struct dp_tee_private - fTPM's private data
> + * @session: dp TA session identifier.
> + * @resp_len: cached response buffer length.
> + * @resp_buf: cached response buffer.
> + * @ctx: TEE context handler.
> + * @shm: Memory pool shared with fTPM TA in TEE.
> + */
> +struct dp_tee_private {
> + u32 session;
> + size_t resp_len;
> + u8 resp_buf[MAX_RESPONSE_SIZE];
> + struct tee_context *ctx;
> + struct tee_shm *shm;
> +};
> +
> +struct hdcp2_info_tx {
> + struct hdcp2_ake_init ake_init;
> + struct hdcp2_ake_no_stored_km no_stored_km;
> + struct hdcp2_ske_send_eks send_eks;
> + struct hdcp2_lc_init lc_init;
> + struct hdcp2_rep_stream_manage stream_manage;
> + struct hdcp2_rep_send_ack send_ack;
> + struct hdcp2_tx_caps tx_caps;
> + u8 k[HDCP2_K_LEN];
> + u8 stream_id_type[HDCP2_STREAMID_TYPE_LEN];
> +};
> +
> +struct hdcp2_info_rx {
> + struct hdcp2_cert_rx cert_rx;
> + struct hdcp2_ake_send_cert send_cert;
> + struct hdcp2_rep_send_receiverid_list receiverid_list;
> + struct hdcp2_ake_send_pairing_info pairing_info;
> + struct hdcp2_rep_stream_ready stream_ready;
> + struct hdcp2_ake_send_hprime send_hprime;
> + struct hdcp2_lc_send_lprime send_lprime;
> +};
> +
> +struct hdcp2_handler {
> + u8 main_state;
> + u8 sub_state;
> + u8 down_stream_dev_cnt;
> + u8 hdcp_rx_ver;
> + bool send_ake_init:1;
> + bool get_recv_id_list:1;
> + bool stored_km:1;
> + bool send_lc_init:1;
> + bool send_ack:1;
> + bool sink_is_repeater:1;
> + bool recv_msg:1;
> + bool send_pair:1;
> + u32 seq_num_v_cnt;
> + u32 retry_cnt;
> +};
> +
> +struct hdcp1x_info {
> + bool enable;
> + bool repeater;
> + bool r0_read;
> + bool ksv_ready;
> + bool max_cascade;
> + bool max_devs;
> + u8 b_status;
> + u8 b_ksv[DRM_HDCP_KSV_LEN];
> + u8 a_ksv[DRM_HDCP_KSV_LEN];
> + u8 v[HDCP1X_V_LEN];
> + u8 b_info[HDCP1X_B_INFO_LEN];
> + u8 ksvfifo[DRM_HDCP_KSV_LEN * (HDCP1X_REP_MAXDEVS - 1)];
> + u8 device_count;
> + u8 retry_count;
> + int main_states;
> + int sub_states;
> +};
> +
> +struct hdcp2_info {
> + struct hdcp2_info_tx hdcp_tx;
> + struct hdcp2_info_rx hdcp_rx;
> + struct hdcp2_ake_stored_km ake_stored_km;
> + struct hdcp2_handler hdcp_handler;
> + bool enable;
> + bool repeater;
> + bool read_certrx;
> + bool read_h_prime;
> + bool read_pairing;
> + bool read_l_prime;
> + bool ks_exchange_done;
> + bool read_v_prime;
> + u8 retry_count;
> + u8 device_count;
> + u8 stream_id_type;
> +};
> +
> +struct mtk_hdcp_info {
> + u8 auth_status;
> + bool g_init;
> + u32 hdcp_content_type;
> + u32 content_protection;
> + struct dp_tee_private *g_dp_tee_priv;
> + struct hdcp1x_info hdcp1x_info;
> + struct hdcp2_info hdcp2_info;
> +};
> +
> +#ifdef __cplusplus
> +extern "C"
cplusplus?! This is the kernel, this is not a UAPI header and will never be, and
you don't need this ifdef at all.
> +{
> +#endif
> +
> +/*
Please use valid kerneldoc; here and everywhere else.
> + *Description:
> + * A device connect and do some initializations.
> + *
> + *Input:
> + * version: HDCP version
> + *
> + *Returns:
> + * TEEC_SUCCESS success*
> + */
> +int tee_add_device(struct mtk_hdcp_info *hdcp_info, u32 version);
> +
Regards,
Angelo
Hi mac.shen,
kernel test robot noticed the following build errors:
[auto build test ERROR on drm-misc/drm-misc-next]
[also build test ERROR on pza/reset/next linus/master v6.8-rc4 next-20240213]
[cannot apply to pza/imx-drm/next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/mac-shen/Subject-PATCH-drm-mediatek-dp-Add-tee-client-application-for-HDCP-feature/20240205-163727
base: git://anongit.freedesktop.org/drm/drm-misc drm-misc-next
patch link: https://lore.kernel.org/r/20240205055055.25340-3-mac.shen%40mediatek.com
patch subject: [PATCH v2 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort
config: arm-randconfig-001-20240213 (https://download.01.org/0day-ci/archive/20240213/[email protected]/config)
compiler: arm-linux-gnueabi-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240213/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All errors (new ones prefixed by >>):
arm-linux-gnueabi-ld: drivers/gpu/drm/mediatek/mtk_dp.o: in function `mtk_dp_get_system_time':
>> mtk_dp.c:(.text+0x41c0): undefined reference to `__aeabi_uldivmod'
>> arm-linux-gnueabi-ld: mtk_dp.c:(.text+0x41ce): undefined reference to `__aeabi_uldivmod'
arm-linux-gnueabi-ld: drivers/gpu/drm/mediatek/mtk_dp.o: in function `mtk_dp_get_time_diff':
mtk_dp.c:(.text+0x41ea): undefined reference to `__aeabi_uldivmod'
arm-linux-gnueabi-ld: mtk_dp.c:(.text+0x41f8): undefined reference to `__aeabi_uldivmod'
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi, Mac:
On Mon, 2024-02-05 at 13:50 +0800, mac.shen wrote:
> Add HDCP2.x feature for DisplayPort.
> When userspace request the kernel protect future content communicated
> over the link with Content_Protection property, the feature will do
> HDCP2.x authentication if the sink support HDCP2.X.
>
> Changes in v2:
> - remove switch case, and refine code to make more clear
> - remove some definitions, and use the definitions in
> include/drm/drm_hdcp.h
> - use the struct which defined in include/drm/drm_hdcp.h
> - do HDCP2.x authentication when userspace request the
> kernel protect future content communicated
> per suggestion from the previous thread:
> https://lore.kernel.org/all/8fff59b5567449d8201dd1138c8fa
> [email protected]/
>
> Signed-off-by: mac.shen <[email protected]>
> ---
[snip]
> +
> +int dp_tx_hdcp2_fsm(struct mtk_hdcp_info *hdcp_info)
> +{
> + static u32 timeout_value;
> + static u8 pre_main;
> + static u8 pre_sub;
> + static u32 pre_time;
> + int err_code = HDCP_ERR_NONE;
> + bool stored = false;
> + u32 time;
> + int ret = 0;
> + bool tmp = false;
> +
> + if (pre_main != hdcp_info->hdcp2_info.hdcp_handler.main_state
> ||
> + hdcp_info->hdcp2_info.hdcp_handler.sub_state != pre_sub) {
> + DPTXHDCPMSG("2.x: Port(M : S)= (%d, %d)",
> + hdcp_info-
> >hdcp2_info.hdcp_handler.main_state,
> + hdcp_info-
> >hdcp2_info.hdcp_handler.sub_state);
> + pre_main = hdcp_info-
> >hdcp2_info.hdcp_handler.main_state;
> + pre_sub = hdcp_info->hdcp2_info.hdcp_handler.sub_state;
> + }
> +
> + switch (hdcp_info->hdcp2_info.hdcp_handler.main_state) {
> + case HDCP2_MS_H1P1:
> + /* HDCP2_MS_H1P1 */
> + /* HDCP_2_2_AUTH_FAIL */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> HDCP_2_2_AUTH_FAIL) {
> + pr_err("2.x: Authentication Fail!\n");
> + dp_tx_hdcp2_enable_auth(hdcp_info, false);
> + hdcp_info->auth_status = AUTH_FAIL;
> + }
> + break;
> +
> + case HDCP2_MS_A0F0:
> + /* HDCP2_MS_A0F0 */
> + /* HDCP_2_2_NULL_MSG */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> HDCP_2_2_NULL_MSG) {
Could you refer to intel driver to get rid of this state machine? It
seams this flow does not need state machine. Just step forward. If
error happen, break the flow. If your implement is like intel driver,
it's easier for me to compare and easier to review.
[1]
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/i915/display/intel_hdcp.c?h=v6.8-rc5#n1868
Regards,
CK
> + if (!hdcp_info->hdcp2_info.enable) {
> + dp_tx_hdcp2_set_state(hdcp_info,
> HDCP2_MS_H1P1, HDCP_2_2_AUTH_FAIL);
> + DPTXHDCPMSG("2.x: Sink Doesn't Support
> Hdcp2x!\n");
> + break;
> + }
> +
> + dp_tx_hdcp2_init(hdcp_info);
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1,
> HDCP_2_2_NULL_MSG);
> + DPTXHDCPMSG("2.x: Sink Support Hdcp2x!\n");
> + }
> + break;
> +
> + case HDCP2_MS_A1F1:
> + /* HDCP2_MS_A1F1 */
> + /* HDCP_2_2_NULL_MSG */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> HDCP_2_2_NULL_MSG) {
> + if (hdcp_info->hdcp2_info.retry_count >=
> HDCP2_TX_RETRY_CNT) {
> + dp_tx_hdcp2_set_state(hdcp_info,
> HDCP2_MS_H1P1, HDCP_2_2_AUTH_FAIL);
> + pr_err("2.x: Try Max Count\n");
> + break;
> + }
> +
> + hdcp_info->hdcp2_info.retry_count++;
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1,
> HDCP_2_2_AKE_INIT);
> + }
> +
> + /* HDCP2_MS_A1F1 */
> + /* HDCP_2_2_AKE_INIT */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> HDCP_2_2_AKE_INIT) {
> + tmp = dp_tx_hdcp2_write_msg(hdcp_info,
> HDCP_2_2_AKE_INIT);
> + if (!tmp) {
> + err_code = HDCP_ERR_SEND_MSG_FAIL;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> + dp_tx_hdcp2_rest_variable(hdcp_info);
> + hdcp_info->hdcp2_info.read_certrx = true;
> +
> + hdcp_info-
> >hdcp2_info.hdcp_handler.send_ake_init = true;
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1,
> HDCP_2_2_AKE_SEND_CERT);
> + pre_time = mtk_dp_get_system_time();
> + }
> +
> + /* HDCP2_MS_A1F1 */
> + /* HDCP_2_2_AKE_SEND_CERT */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> HDCP_2_2_AKE_SEND_CERT) {
> + time = mtk_dp_get_time_diff(pre_time);
> + if (time < HDCP_2_2_CERT_TIMEOUT_MS) {
> + msleep(20);
> + break;
> + }
> + if (hdcp_info->hdcp2_info.read_certrx)
> + dp_tx_hdcp2_read_msg(hdcp_info,
> HDCP_2_2_AKE_SEND_CERT);
> +
> + if (!hdcp_info-
> >hdcp2_info.hdcp_handler.recv_msg)
> + break;
> +
> + ret = tee_ake_certificate(hdcp_info,
> + (u8 *)&hdcp_info-
> >hdcp2_info.hdcp_rx.cert_rx,
> + &stored,
> + hdcp_info-
> >hdcp2_info.ake_stored_km.e_kh_km_m +
> + HDCP_2_2_E_KH_KM_LEN,
> + hdcp_info-
> >hdcp2_info.ake_stored_km.e_kh_km_m);
> +
> + if (ret != RET_COMPARE_PASS) {
> + err_code = HDCP_ERR_PROCESS_FAIL;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + hdcp_info->hdcp2_info.hdcp_handler.stored_km =
> stored;
> + hdcp_info->hdcp2_info.hdcp_handler.recv_msg =
> false;
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1,
> + hdcp_info-
> >hdcp2_info.hdcp_handler.stored_km ?
> + HDCP_2_2_AKE_STORED_KM :
> + HDCP_2_2_AKE_NO_STORED_KM);
> + }
> +
> + /* HDCP2_MS_A1F1 */
> + /* HDCP_2_2_AKE_NO_STORED_KM */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> HDCP_2_2_AKE_NO_STORED_KM) {
> + DPTXHDCPMSG("2.x: Get Km, derive Ekpub(km)\n");
> +
> + tee_enc_rsaes_oaep(hdcp_info,
> + hdcp_info-
> >hdcp2_info.hdcp_tx.no_stored_km.e_kpub_km);
> + /* Prepare e_kpub_km to send */
> + tmp = dp_tx_hdcp2_write_msg(hdcp_info,
> HDCP_2_2_AKE_NO_STORED_KM);
> + if (!tmp) {
> + err_code = HDCP_ERR_SEND_MSG_FAIL;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1,
> HDCP_2_2_AKE_SEND_HPRIME);
> + timeout_value =
> HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS;
> + hdcp_info->hdcp2_info.hdcp_handler.recv_msg =
> false;
> + pre_time = mtk_dp_get_system_time();
> + }
> +
> + /* HDCP2_MS_A1F1 */
> + /* HDCP_2_2_AKE_STORED_KM */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> HDCP_2_2_AKE_STORED_KM) {
> + /* Prepare ekh_km & M to send */
> + tmp = dp_tx_hdcp2_write_msg(hdcp_info,
> HDCP_2_2_AKE_STORED_KM);
> + if (!tmp) {
> + err_code = HDCP_ERR_SEND_MSG_FAIL;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + err_code = HDCP_ERR_NONE;
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1,
> HDCP_2_2_AKE_SEND_HPRIME);
> + timeout_value =
> HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS;
> + hdcp_info->hdcp2_info.hdcp_handler.recv_msg =
> false;
> + pre_time = mtk_dp_get_system_time();
> + }
> +
> + /* HDCP2_MS_A1F1 */
> + /* HDCP_2_2_AKE_SEND_HPRIME */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> HDCP_2_2_AKE_SEND_HPRIME) {
> + if (hdcp_info->hdcp2_info.read_h_prime)
> + dp_tx_hdcp2_read_msg(hdcp_info,
> HDCP_2_2_AKE_SEND_HPRIME);
> +
> + time = mtk_dp_get_time_diff(pre_time);
> + if (time > timeout_value) {
> + err_code = HDCP_ERR_RESPONSE_TIMEROUT;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + if (!hdcp_info-
> >hdcp2_info.hdcp_handler.recv_msg)
> + break;
> +
> + ret = tee_ake_h_prime(hdcp_info,
> + hdcp_info-
> >hdcp2_info.hdcp_tx.ake_init.r_tx,
> + hdcp_info-
> >hdcp2_info.hdcp_rx.send_cert.r_rx,
> + hdcp_info-
> >hdcp2_info.hdcp_rx.send_cert.rx_caps,
> + (u8 *)&hdcp_info-
> >hdcp2_info.hdcp_tx.tx_caps,
> + hdcp_info-
> >hdcp2_info.hdcp_rx.send_hprime.h_prime,
> + HDCP_2_2_H_PRIME_LEN);
> + if (ret != RET_COMPARE_PASS) {
> + if (hdcp_info-
> >hdcp2_info.hdcp_handler.stored_km)
> + tee_clear_paring(hdcp_info);
> + err_code = HDCP_ERR_PROCESS_FAIL;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + if (hdcp_info-
> >hdcp2_info.hdcp_handler.stored_km)
> + dp_tx_hdcp2_set_state(hdcp_info,
> HDCP2_MS_A2F2, HDCP_2_2_LC_INIT);
> + else
> + dp_tx_hdcp2_set_state(hdcp_info,
> HDCP2_MS_A1F1,
> + HDCP_2_2_AKE_SEND
> _PAIRING_INFO);
> +
> + pre_time = mtk_dp_get_system_time();
> + hdcp_info->hdcp2_info.hdcp_handler.recv_msg =
> false;
> + }
> +
> + /* HDCP2_MS_A1F1 */
> + /* HDCP_2_2_AKE_SEND_PAIRING_INFO */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> + HDCP_2_2_AKE_SEND_PAIRING_INFO) {
> + if (hdcp_info->hdcp2_info.read_pairing)
> + dp_tx_hdcp2_read_msg(hdcp_info,
> HDCP_2_2_AKE_SEND_PAIRING_INFO);
> +
> + /* Ekh_Km must be available less than 200ms,
> Give mode time for some Rx */
> + time = mtk_dp_get_time_diff(pre_time);
> + if (time > HDCP_2_2_PAIRING_TIMEOUT_MS *
> 2) {
> + err_code = HDCP_ERR_RESPONSE_TIMEROUT;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + if (!hdcp_info-
> >hdcp2_info.hdcp_handler.recv_msg)
> + break;
> +
> + /* Store m, km, Ekh(km) */
> + tee_ake_paring(hdcp_info,
> + hdcp_info-
> >hdcp2_info.hdcp_rx.pairing_info.e_kh_km);
> +
> + hdcp_info->hdcp2_info.hdcp_handler.send_pair =
> true;
> + hdcp_info->hdcp2_info.hdcp_handler.recv_msg =
> false;
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A2F2,
> HDCP_2_2_LC_INIT);
> + pre_time = mtk_dp_get_system_time();
> + }
> + break;
> +
> + case HDCP2_MS_A2F2:
> + /* HDCP2_MS_A2F2 */
> + /* HDCP_2_2_LC_INIT */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> HDCP_2_2_LC_INIT) {
> + /* prepare Rn to send */
> + tmp = dp_tx_hdcp2_write_msg(hdcp_info,
> HDCP_2_2_LC_INIT);
> + if (!tmp) {
> + err_code = HDCP_ERR_SEND_MSG_FAIL;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> + hdcp_info->hdcp2_info.hdcp_handler.send_lc_init
> = true;
> +
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A2F2,
> HDCP_2_2_LC_SEND_LPRIME);
> + pre_time = mtk_dp_get_system_time();
> + }
> +
> + /* HDCP2_MS_A2F2 */
> + /* HDCP_2_2_LC_SEND_LPRIME */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> HDCP_2_2_LC_SEND_LPRIME) {
> + time = mtk_dp_get_time_diff(pre_time);
> + if (time < HDCP_2_2_DP_HPRIME_READ_TIMEOUT_MS)
> + break;
> +
> + if (hdcp_info->hdcp2_info.read_l_prime)
> + dp_tx_hdcp2_read_msg(hdcp_info,
> HDCP_2_2_LC_SEND_LPRIME);
> +
> + if (!hdcp_info-
> >hdcp2_info.hdcp_handler.recv_msg)
> + break;
> +
> + ret = tee_lc_l_prime(hdcp_info, hdcp_info-
> >hdcp2_info.hdcp_tx.lc_init.r_n,
> + hdcp_info-
> >hdcp2_info.hdcp_rx.send_lprime.l_prime,
> + HDCP_2_2_L_PRIME_LEN);
> + if (ret != RET_COMPARE_PASS) {
> + err_code = HDCP_ERR_PROCESS_FAIL;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + DPTXHDCPMSG("2.x: L' is PASS!!\n");
> + hdcp_info->hdcp2_info.hdcp_handler.recv_msg =
> false;
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A3F3,
> HDCP_2_2_NULL_MSG);
> + pre_time = mtk_dp_get_system_time();
> + }
> + break;
> +
> + case HDCP2_MS_A3F3:
> + /* HDCP2_MS_A3F3 */
> + /* HDCP_2_2_NULL_MSG */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> HDCP_2_2_NULL_MSG) {
> + tee_ske_enc_ks(hdcp_info, hdcp_info-
> >hdcp2_info.hdcp_tx.send_eks.riv,
> + hdcp_info-
> >hdcp2_info.hdcp_tx.send_eks.e_dkey_ks);
> +
> + tmp = dp_tx_hdcp2_write_msg(hdcp_info,
> HDCP_2_2_SKE_SEND_EKS);
> + if (!tmp) {
> + err_code = HDCP_ERR_SEND_MSG_FAIL;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + if (!hdcp_info->hdcp2_info.repeater)
> + dp_tx_hdcp2_write_msg(hdcp_info,
> HDCP_2_2_STREAM_TYPE);
> +
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A3F3,
> HDCP_2_2_SKE_SEND_EKS);
> + pre_time = mtk_dp_get_system_time();
> + }
> +
> + /* HDCP2_MS_A3F3 */
> + /* HDCP_2_2_SKE_SEND_EKS */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> HDCP_2_2_SKE_SEND_EKS) {
> + time = mtk_dp_get_time_diff(pre_time);
> + if (time >=
> HDCP_2_2_DELAY_BEFORE_ENCRYPTION_EN)
> + dp_tx_hdcp2_set_state(hdcp_info,
> HDCP2_MS_A4F4, HDCP_2_2_NULL_MSG);
> + }
> + break;
> +
> + case HDCP2_MS_A4F4:
> + /* HDCP2_MS_A4F4 */
> + /* HDCP_2_2_NULL_MSG */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> HDCP_2_2_NULL_MSG) {
> + if (!hdcp_info->hdcp2_info.repeater) {
> + dp_tx_hdcp2_set_state(hdcp_info,
> HDCP2_MS_A5F5, HDCP_2_2_AUTH_DONE);
> + break;
> + }
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A6F6,
> + HDCP_2_2_REP_SEND_RECVID_
> LIST);
> + hdcp_info->hdcp2_info.hdcp_handler.recv_msg =
> false;
> + pre_time = mtk_dp_get_system_time();
> + }
> + break;
> +
> + case HDCP2_MS_A5F5:
> + /* HDCP2_MS_A5F5 */
> + /* HDCP_2_2_AUTH_DONE */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> HDCP_2_2_AUTH_DONE) {
> + DPTXHDCPMSG("2.x: Authentication done!\n");
> + hdcp_info->auth_status = AUTH_PASS;
> + hdcp_info->hdcp2_info.retry_count = 0;
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A5F5,
> HDCP_2_2_NULL_MSG);
> + dp_tx_hdcp2_enable_auth(hdcp_info, true);
> + }
> + break;
> +
> + case HDCP2_MS_A6F6:
> + /* HDCP2_MS_A6F6 */
> + /* HDCP_2_2_REP_SEND_RECVID_LIST */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> + HDCP_2_2_REP_SEND_RECVID_LIST) {
> + if (hdcp_info->hdcp2_info.read_v_prime)
> + dp_tx_hdcp2_read_msg(hdcp_info,
> HDCP_2_2_REP_SEND_RECVID_LIST);
> +
> + time = mtk_dp_get_time_diff(pre_time);
> + if (time > HDCP_2_2_RECVID_LIST_TIMEOUT_MS) {
> + err_code = HDCP_ERR_RESPONSE_TIMEROUT;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + if (!hdcp_info-
> >hdcp2_info.hdcp_handler.recv_msg)
> + break;
> +
> + pre_time = mtk_dp_get_system_time();
> + hdcp_info->hdcp2_info.hdcp_handler.recv_msg =
> false;
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A7F7,
> + HDCP_2_2_REP_VERIFY_RECVI
> D_LIST);
> + }
> + break;
> +
> + case HDCP2_MS_A7F7:
> + /* HDCP2_MS_A7F7 */
> + /* HDCP_2_2_REP_VERIFY_RECVID_LIST */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> + HDCP_2_2_REP_VERIFY_RECVID_LIST) {
> + if ((hdcp_info-
> >hdcp2_info.hdcp_rx.receiverid_list.rx_info[1]
> + & (BIT(2) | BIT(3))) != 0) {
> + pr_err("2.x: DEVS_EXCEEDED or
> CASCADE_EXCEDDED!\n");
> + err_code = HDCP_ERR_PROCESS_FAIL;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + /* check seqNumV here */
> + tmp = dp_tx_hdcp2_check_seq_num_v(hdcp_info);
> + if (!tmp) {
> + err_code = HDCP_ERR_PROCESS_FAIL;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + tmp =
> dp_tx_hdcp2_recv_rep_auth_send_recv_id_list(hdcp_info);
> + if (!tmp) {
> + err_code = HDCP_ERR_PROCESS_FAIL;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A8F8,
> HDCP_2_2_REP_SEND_ACK);
> + }
> + break;
> +
> + case HDCP2_MS_A8F8:
> + /* HDCP2_MS_A8F8 */
> + /* HDCP_2_2_REP_SEND_ACK */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> HDCP_2_2_REP_SEND_ACK) {
> + tmp = dp_tx_hdcp2_write_msg(hdcp_info,
> HDCP_2_2_REP_SEND_ACK);
> + if (!tmp) {
> + err_code = HDCP_ERR_SEND_MSG_FAIL;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + time = mtk_dp_get_time_diff(pre_time);
> + if (time > HDCP2_REP_SEND_ACK) {
> + err_code = HDCP_ERR_RESPONSE_TIMEROUT;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A9F9,
> HDCP_2_2_REP_STREAM_MANAGE);
> + hdcp_info->hdcp2_info.hdcp_handler.retry_cnt =
> 0;
> + }
> + break;
> +
> + case HDCP2_MS_A9F9:
> + /* HDCP2_MS_A9F9 */
> + /* HDCP_2_2_REP_STREAM_MANAGE */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> HDCP_2_2_REP_STREAM_MANAGE) {
> + tmp =
> dp_tx_hdcp2_process_rep_auth_stream_manage(hdcp_info);
> + if (!tmp) {
> + err_code = HDCP_ERR_PROCESS_FAIL;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + tmp = dp_tx_hdcp2_write_msg(hdcp_info,
> HDCP_2_2_REP_STREAM_MANAGE);
> + if (!tmp) {
> + err_code = HDCP_ERR_SEND_MSG_FAIL;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + pre_time = mtk_dp_get_system_time();
> + hdcp_info->hdcp2_info.hdcp_handler.recv_msg =
> false;
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A9F9,
> + HDCP_2_2_REP_STREAM_READY
> );
> + }
> +
> + /* HDCP2_MS_A9F9 */
> + /* HDCP_2_2_REP_STREAM_READY */
> + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state ==
> HDCP_2_2_REP_STREAM_READY) {
> + time = mtk_dp_get_time_diff(pre_time);
> + if (time <= HDCP_2_2_STREAM_READY_TIMEOUT_MS /
> 2)
> + break;
> +
> + dp_tx_hdcp2_read_msg(hdcp_info,
> HDCP_2_2_REP_STREAM_READY);
> +
> + time = mtk_dp_get_time_diff(pre_time);
> + if (time > HDCP_2_2_STREAM_READY_TIMEOUT_MS) {
> + err_code = HDCP_ERR_RESPONSE_TIMEROUT;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + if (!hdcp_info-
> >hdcp2_info.hdcp_handler.recv_msg) {
> + if (hdcp_info-
> >hdcp2_info.hdcp_handler.retry_cnt
> + >=
> HDCP2_STREAM_MANAGE_RETRY_CNT) {
> + err_code =
> HDCP_ERR_RESPONSE_TIMEROUT;
> + dp_tx_hdcp2_err_handle(hdcp_inf
> o, err_code, __LINE__);
> + break;
> + }
> +
> + hdcp_info-
> >hdcp2_info.hdcp_handler.retry_cnt++;
> +
> + dp_tx_hdcp2_set_state(hdcp_info,
> HDCP2_MS_A9F9,
> + HDCP_2_2_REP_STRE
> AM_READY);
> + break;
> + }
> +
> + tmp =
> dp_tx_hdcp2_recv_rep_auth_stream_ready(hdcp_info);
> + if (!tmp) {
> + err_code = HDCP_ERR_PROCESS_FAIL;
> + dp_tx_hdcp2_err_handle(hdcp_info,
> err_code, __LINE__);
> + break;
> + }
> +
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A5F5,
> HDCP_2_2_AUTH_DONE);
> + }
> + break;
> +
> + default:
> + err_code = HDCP_ERR_UNKNOWN_STATE;
> + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__);
> + break;
> + }
> +
> + return err_code;
> +}
> +
Hi, Mac:
On Mon, 2024-02-05 at 13:50 +0800, mac.shen wrote:
> Add tee client application which will be used for
> HDCP 1.x and 2.x authentication in DisplayPort.
>
> Changes in v2:
> - remove ca folder, and change file name with lower case
> - refine the tci_t structure to make the data to tee can
> through this structure
> - remove aux and regs from mtk_hdcp_info structure
> - remove some definitions, and use the definitions in
> include/drm/drm_hdcp.h
> - remove useless code
> per suggestion from the previous thread:
> https://lore.kernel.org/all/8fff59b5567449d8201dd1138c8fa
> [email protected]/
>
> Signed-off-by: mac.shen <[email protected]>
> ---
[snip]
> +
> +#define RET_COMPARE_PASS 0
> +#define RET_COMPARE_FAIL 1
> +#define RET_NEW_DEVICE 2
> +#define RET_STORED_DEVICE 3
These definition are useless, so drop them.
Regards,
CK
> +
Hi, Mac:
On Mon, 2024-02-05 at 13:50 +0800, mac.shen wrote:
> Add tee client application which will be used for
> HDCP 1.x and 2.x authentication in DisplayPort.
>
> Changes in v2:
> - remove ca folder, and change file name with lower case
> - refine the tci_t structure to make the data to tee can
> through this structure
> - remove aux and regs from mtk_hdcp_info structure
> - remove some definitions, and use the definitions in
> include/drm/drm_hdcp.h
> - remove useless code
> per suggestion from the previous thread:
> https://lore.kernel.org/all/8fff59b5567449d8201dd1138c8fa
> [email protected]/
>
> Signed-off-by: mac.shen <[email protected]>
> ---
[snip]
> +
> +struct hdcp2_handler {
> + u8 main_state;
> + u8 sub_state;
> + u8 down_stream_dev_cnt;
Useless, drop it.
> + u8 hdcp_rx_ver;
Ditto.
> + bool send_ake_init:1;
You set send_ake_init to ture, but never check it, so it's useless,
drop it.
> + bool get_recv_id_list:1;
Useless, drop it.
> + bool stored_km:1;
> + bool send_lc_init:1;
You set send_lc_init to ture, but never check it, so it's useless, drop
it.
> + bool send_ack:1;
Useless, drop it.
> + bool sink_is_repeater:1;
Ditto.
> + bool recv_msg:1;
> + bool send_pair:1;
You set send_pair to ture, but never check it, so it's useless, drop
it.
Regards,
CK
> + u32 seq_num_v_cnt;
> + u32 retry_cnt;
> +};
> +
Hi, Mac:
On Mon, 2024-02-05 at 13:50 +0800, mac.shen wrote:
> Add HDCP2.x feature for DisplayPort.
> When userspace request the kernel protect future content communicated
> over the link with Content_Protection property, the feature will do
> HDCP2.x authentication if the sink support HDCP2.X.
>
> Changes in v2:
> - remove switch case, and refine code to make more clear
> - remove some definitions, and use the definitions in
> include/drm/drm_hdcp.h
> - use the struct which defined in include/drm/drm_hdcp.h
> - do HDCP2.x authentication when userspace request the
> kernel protect future content communicated
> per suggestion from the previous thread:
> https://lore.kernel.org/all/8fff59b5567449d8201dd1138c8fa
> [email protected]/
>
> Signed-off-by: mac.shen <[email protected]>
> ---
[snip]
>
> +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 (tee_add_device(&mtk_dp->hdcp_info, HDCP_NONE) !=
> RET_SUCCESS)
Does this mean that if rx does not support hdcp, dp tx driver still
need to create tee device to work for no hdcp? If so, I think this
should be separated to an independent patch. I like the patch sequence
to be:
1. Support NO HDCP
2. Support HDCP 1x
3. Support HDCP 2x
Regards,
CK
> + mtk_dp->hdcp_info.auth_status = AUTH_FAIL;
> +}
> +
Hi, Mac:
On Mon, 2024-02-05 at 13:50 +0800, mac.shen wrote:
> Add HDCP2.x feature for DisplayPort.
> When userspace request the kernel protect future content communicated
> over the link with Content_Protection property, the feature will do
> HDCP2.x authentication if the sink support HDCP2.X.
>
> Changes in v2:
> - remove switch case, and refine code to make more clear
> - remove some definitions, and use the definitions in
> include/drm/drm_hdcp.h
> - use the struct which defined in include/drm/drm_hdcp.h
> - do HDCP2.x authentication when userspace request the
> kernel protect future content communicated
> per suggestion from the previous thread:
> https://lore.kernel.org/all/8fff59b5567449d8201dd1138c8fa
> [email protected]/
>
> Signed-off-by: mac.shen <[email protected]>
> ---
[snip]
> +
> +static bool dp_tx_hdcp2_write_msg(struct mtk_hdcp_info *hdcp_info,
> u8 cmd_ID)
> +{
> + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp,
> hdcp_info);
> + bool ret = false;
> +
It seems no common part for each case, so I would like to separate each
case to an independent function.
Regards,
CK
> + switch (cmd_ID) {
> + case HDCP_2_2_AKE_INIT:
> + tee_hdcp2_soft_rst(hdcp_info);
> + drm_dp_dpcd_write(&mtk_dp->aux,
> DP_HDCP_2_2_REG_RTX_OFFSET,
> + hdcp_info-
> >hdcp2_info.hdcp_tx.ake_init.r_tx, HDCP_2_2_RTX_LEN);
> + drm_dp_dpcd_write(&mtk_dp->aux,
> DP_HDCP_2_2_REG_TXCAPS_OFFSET,
> + (void *)&hdcp_info-
> >hdcp2_info.hdcp_tx.tx_caps,
> + HDCP_2_2_TXCAPS_LEN);
> +
> + ret = true;
> + DPTXHDCPMSG("2.x: HDCP_2_2_AKE_Init !\n");
> + break;
> +
> + case HDCP_2_2_AKE_NO_STORED_KM:
> + drm_dp_dpcd_write(&mtk_dp->aux,
> DP_HDCP_2_2_REG_EKPUB_KM_OFFSET,
> + hdcp_info-
> >hdcp2_info.hdcp_tx.no_stored_km.e_kpub_km,
> + HDCP_2_2_E_KPUB_KM_LEN);
> +
> + ret = true;
> +
> + DPTXHDCPMSG("2.x: HDCP_2_2_AKE_NO_STORED_KM !\n");
> + break;
> +
> + case HDCP_2_2_AKE_STORED_KM:
> + drm_dp_dpcd_write(&mtk_dp->aux,
> DP_HDCP_2_2_REG_EKH_KM_WR_OFFSET,
> + hdcp_info-
> >hdcp2_info.ake_stored_km.e_kh_km_m,
> + HDCP_2_2_E_KH_KM_LEN);
> + drm_dp_dpcd_write(&mtk_dp->aux,
> DP_HDCP_2_2_REG_M_OFFSET,
> + hdcp_info-
> >hdcp2_info.ake_stored_km.e_kh_km_m +
> + HDCP_2_2_E_KH_KM_LEN,
> + HDCP_2_2_E_KH_KM_M_LEN -
> HDCP_2_2_E_KH_KM_LEN);
> +
> + ret = true;
> +
> + DPTXHDCPMSG("2.x: DPTX_HDCP_2_2_AKE_STORED_KM !\n");
> + break;
> +
> + case HDCP_2_2_LC_INIT:
> + drm_dp_dpcd_write(&mtk_dp->aux,
> DP_HDCP_2_2_REG_RN_OFFSET,
> + hdcp_info-
> >hdcp2_info.hdcp_tx.lc_init.r_n, HDCP_2_2_RN_LEN);
> +
> + hdcp_info->hdcp2_info.read_l_prime = true;
> + ret = true;
> +
> + DPTXHDCPMSG("2.x: HDCP_2_2_LC_INIT !\n");
> + break;
> +
> + case HDCP_2_2_SKE_SEND_EKS:
> + drm_dp_dpcd_write(&mtk_dp->aux,
> DP_HDCP_2_2_REG_EDKEY_KS_OFFSET,
> + hdcp_info-
> >hdcp2_info.hdcp_tx.send_eks.e_dkey_ks,
> + HDCP_2_2_E_DKEY_KS_LEN);
> + drm_dp_dpcd_write(&mtk_dp->aux,
> DP_HDCP_2_2_REG_RIV_OFFSET,
> + hdcp_info-
> >hdcp2_info.hdcp_tx.send_eks.riv, HDCP_2_2_RIV_LEN);
> +
> + hdcp_info->hdcp2_info.ks_exchange_done = true;
> +
> + ret = true;
> + DPTXHDCPMSG("2.x: HDCP_2_2_SKE_SEND_EKS !\n");
> + break;
> +
> + case HDCP_2_2_STREAM_TYPE:
> + drm_dp_dpcd_write(&mtk_dp->aux,
> DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET,
> + hdcp_info-
> >hdcp2_info.hdcp_tx.stream_id_type, 1);
> +
> + ret = true;
> + DPTXHDCPMSG("HDCP2_MSG_DP_STREAM_TYPE !\n");
> + break;
> +
> + case HDCP_2_2_REP_SEND_ACK:
> + drm_dp_dpcd_write(&mtk_dp->aux,
> DP_HDCP_2_2_REG_V_OFFSET,
> + hdcp_info-
> >hdcp2_info.hdcp_tx.send_ack.v,
> + HDCP_2_2_V_PRIME_HALF_LEN);
> +
> + ret = true;
> + DPTXHDCPMSG("2.x: HDCP_2_2_SEND_ACK !\n");
> + break;
> +
> + case HDCP_2_2_REP_STREAM_MANAGE:
> + drm_dp_dpcd_write(&mtk_dp->aux,
> DP_HDCP_2_2_REG_SEQ_NUM_M_OFFSET,
> + hdcp_info-
> >hdcp2_info.hdcp_tx.stream_manage.seq_num_m,
> + HDCP_2_2_SEQ_NUM_LEN);
> + drm_dp_dpcd_write(&mtk_dp->aux,
> DP_HDCP_2_2_REG_K_OFFSET,
> + hdcp_info->hdcp2_info.hdcp_tx.k,
> HDCP2_K_LEN);
> + drm_dp_dpcd_write(&mtk_dp->aux,
> DP_HDCP_2_2_REG_STREAM_ID_TYPE_OFFSET,
> + hdcp_info-
> >hdcp2_info.hdcp_tx.stream_id_type,
> + HDCP2_STREAMID_TYPE_LEN);
> +
> + dp_tx_hdcp2_fill_stream_type(hdcp_info, hdcp_info-
> >hdcp2_info.stream_id_type);
> +
> + ret = true;
> + DPTXHDCPMSG("2.x: HDCP_2_2_STREAM_MANAGE !\n");
> + break;
> +
> + default:
> + DPTXHDCPMSG("2.x: Invalid HDCP2_OffSETADDR_WriteMessage
> !\n");
> + break;
> + }
> +
> + return ret;
> +}
Hi, Mac:
On Mon, 2024-02-05 at 13:50 +0800, mac.shen wrote:
> Add HDCP2.x feature for DisplayPort.
> When userspace request the kernel protect future content communicated
> over the link with Content_Protection property, the feature will do
> HDCP2.x authentication if the sink support HDCP2.X.
>
> Changes in v2:
> - remove switch case, and refine code to make more clear
> - remove some definitions, and use the definitions in
> include/drm/drm_hdcp.h
> - use the struct which defined in include/drm/drm_hdcp.h
> - do HDCP2.x authentication when userspace request the
> kernel protect future content communicated
> per suggestion from the previous thread:
> https://lore.kernel.org/all/8fff59b5567449d8201dd1138c8fa
> [email protected]/
>
> Signed-off-by: mac.shen <[email protected]>
> ---
[snip]
> +
> +static void dp_tx_hdcp2_err_handle(struct mtk_hdcp_info *hdcp_info,
> int err_msg, int line)
> +{
> + pr_err("2.x: MainState:%d; SubState:%d;\n", hdcp_info-
> >hdcp2_info.hdcp_handler.main_state,
> + hdcp_info->hdcp2_info.hdcp_handler.sub_state);
> +
> + switch (err_msg) {
> + case HDCP_ERR_UNKNOWN_STATE:
> + pr_err("2.x: Unknown State, line:%d\n", line);
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_H1P1,
> HDCP_2_2_AUTH_FAIL);
For each case, you just print error message and set state. It seems not
necessary to group these cases. Where error happen, just print message
there and set state there. And not print line, you should print what
the error is.
Regards,
CK
> + break;
> +
> + case HDCP_ERR_SEND_MSG_FAIL:
> + pr_err("2.x: Send Msg Fail, line:%d\n", line);
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A0F0,
> HDCP_2_2_NULL_MSG);
> + break;
> + case HDCP_ERR_RESPONSE_TIMEROUT:
> + pr_err("2.x: Response Timeout, line:%d!\n", line);
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A0F0,
> HDCP_2_2_NULL_MSG);
> + break;
> +
> + case HDCP_ERR_PROCESS_FAIL:
> + pr_err("2.x: Process Fail, line:%d!\n", line);
> + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A0F0,
> HDCP_2_2_NULL_MSG);
> + break;
> +
> + default:
> + pr_err("2.x: NO ERROR!");
> + break;
> + }
> +}