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