From: mac.shen <mac.shen@mediatek.com>
To: <chunkuang.hu@kernel.org>, <p.zabel@pengutronix.de>,
<airlied@gmail.com>, <daniel@ffwll.ch>, <matthias.bgg@gmail.com>,
<angelogioacchino.delregno@collabora.com>,
<jitao.shi@mediatek.com>
Cc: <mac.shen@mediatek.com>, <shuijing.li@mediatek.com>,
<linux-kernel@vger.kernel.org>, <dri-devel@lists.freedesktop.org>,
<linux-mediatek@lists.infradead.org>,
<linux-arm-kernel@lists.infradead.org>
Subject: [PATCH v3 3/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP1.x feature for DisplayPort
Date: Sat, 8 Jun 2024 20:01:06 +0800 [thread overview]
Message-ID: <20240608120219.21817-4-mac.shen@mediatek.com> (raw)
In-Reply-To: <20240608120219.21817-1-mac.shen@mediatek.com>
Changes in v3:
- remove useless code
- remove magic number
- refine the flow to do HDCP1.x authentication
per suggestion from the previous thread:
https://patchwork.kernel.org/project/linux-mediatek
/patch/20240205055055.25340-4-mac.shen@mediatek.com/
Signed-off-by: mac.shen <mac.shen@mediatek.com>
---
drivers/gpu/drm/mediatek/Makefile | 1 +
drivers/gpu/drm/mediatek/mtk_dp.c | 11 +
drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.c | 577 +++++++++++++++++++++++
drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h | 17 +
drivers/gpu/drm/mediatek/mtk_dp_reg.h | 3 +
5 files changed, 609 insertions(+)
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 19b7625ae573..a90c3294bfbe 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -28,6 +28,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 12854a04622f..3925eb2be064 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)
@@ -1841,6 +1842,7 @@ static int mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp)
static void mtk_dp_hdcp_get_info(struct mtk_dp *mtk_dp)
{
dp_tx_hdcp2x_get_info(&mtk_dp->hdcp_info);
+ dp_tx_hdcp1x_get_info(&mtk_dp->hdcp_info);
}
static void mtk_dp_hdcp_disable(struct mtk_dp *mtk_dp)
@@ -1852,6 +1854,8 @@ static void mtk_dp_hdcp_disable(struct mtk_dp *mtk_dp)
if (mtk_dp->hdcp_info.auth_version == HDCP_VERSION_2X)
dp_tx_hdcp2x_disabel(&mtk_dp->hdcp_info);
+ else if (mtk_dp->hdcp_info.auth_version == HDCP_VERSION_1X)
+ dp_tx_hdcp1x_disabel(&mtk_dp->hdcp_info);
end:
cancel_delayed_work_sync(&mtk_dp->check_work);
@@ -1868,6 +1872,9 @@ static void mtk_dp_hdcp_check_work(struct work_struct *work)
if (mtk_dp->hdcp_info.auth_version == HDCP_VERSION_2X &&
(!dp_tx_hdcp2x_check_link(&mtk_dp->hdcp_info))) {
schedule_delayed_work(&mtk_dp->check_work, DRM_HDCP2_CHECK_PERIOD_MS);
+ } else if (mtk_dp->hdcp_info.auth_version == HDCP_VERSION_1X &&
+ (!dp_tx_hdcp1x_check_link(&mtk_dp->hdcp_info))) {
+ schedule_delayed_work(&mtk_dp->check_work, DRM_HDCP_CHECK_PERIOD_MS);
}
}
@@ -1890,6 +1897,10 @@ static void mtk_dp_hdcp_handle(struct work_struct *data)
check_link_interval = DRM_HDCP2_CHECK_PERIOD_MS;
}
+ if (ret && mtk_dp->hdcp_info.hdcp1x_info.capable &&
+ mtk_dp->hdcp_info.hdcp_content_type != DRM_MODE_HDCP_CONTENT_TYPE1)
+ ret = dp_tx_hdcp1x_enable(&mtk_dp->hdcp_info);
+
if (!ret) {
schedule_delayed_work(&mtk_dp->check_work, check_link_interval);
mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_ENABLED);
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..0fcf23c378df
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.c
@@ -0,0 +1,577 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019-2024 MediaTek Inc.
+ */
+
+#include <drm/display/drm_hdcp_helper.h>
+
+#include "mtk_dp_hdcp1x.h"
+#include "mtk_dp_reg.h"
+#include "mtk_dp.h"
+
+#define HDCP1X_R0_WDT 100
+#define HDCP1X_REP_RDY_WDT 5000
+
+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 & R0_AVAILABLE_DP_TRANS_P0)
+ 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,
+ REPEATER_I_DP_TRANS_P0_MASK, REPEATER_I_DP_TRANS_P0_MASK);
+ else
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4, 0, REPEATER_I_DP_TRANS_P0_MASK);
+}
+
+static int dp_tx_hdcp1x_init(struct mtk_hdcp_info *hdcp_info)
+{
+ int ret;
+ u8 i;
+
+ 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.device_count = 0x00;
+
+ ret = tee_hdcp_enable_encrypt(hdcp_info, false, HDCP_NONE);
+ if (ret)
+ return ret;
+
+ dp_tx_hdcp1x_start_cipher(hdcp_info, false);
+
+ ret = tee_hdcp1x_soft_rst(hdcp_info);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int 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;
+ ssize_t ret;
+
+ if (hdcp_info->hdcp1x_info.capable) {
+ ret = drm_dp_dpcd_read(&mtk_dp->aux,
+ DP_AUX_HDCP_BKSV, read_buffer, DRM_HDCP_KSV_LEN);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < DRM_HDCP_KSV_LEN; i++) {
+ hdcp_info->hdcp1x_info.b_ksv[i] = read_buffer[i];
+ dev_dbg(mtk_dp->dev, "[HDCP1.X] Bksv:0x%x\n", read_buffer[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int 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;
+ ssize_t ret;
+
+ ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BSTATUS, &read_buffer, 1);
+ if (ret < 0)
+ return ret;
+
+ return (read_buffer & DP_BSTATUS_READY) ? 0 : -EAGAIN;
+}
+
+static int 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];
+ ssize_t ret;
+
+ ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BINFO, read_buffer, DRM_HDCP_BSTATUS_LEN);
+ if (ret < 0)
+ return ret;
+
+ hdcp_info->hdcp1x_info.b_info[0] = read_buffer[0];
+ hdcp_info->hdcp1x_info.b_info[1] = read_buffer[1];
+ hdcp_info->hdcp1x_info.device_count = DRM_HDCP_NUM_DOWNSTREAM(read_buffer[0]);
+
+ dev_dbg(mtk_dp->dev, "[HDCP1.X] Binfo max_cascade_EXCEEDED:%lu\n",
+ DRM_HDCP_MAX_CASCADE_EXCEEDED(read_buffer[1]));
+ dev_dbg(mtk_dp->dev, "[HDCP1.X] Binfo DEPTH:%d\n", read_buffer[1] & 0x07);
+ dev_dbg(mtk_dp->dev, "[HDCP1.X] Binfo max_devs_EXCEEDED:%lu\n",
+ DRM_HDCP_MAX_DEVICE_EXCEEDED(read_buffer[0]));
+ dev_dbg(mtk_dp->dev, "[HDCP1.X] Binfo device_count:%d\n",
+ hdcp_info->hdcp1x_info.device_count);
+
+ return 0;
+}
+
+static int 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 times = dev_count / 3;
+ u8 remain = dev_count % 3;
+ ssize_t ret;
+ u8 i;
+
+ if (times > 0) {
+ for (i = 0; i < times; i++) {
+ ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_KSV_FIFO,
+ hdcp_info->hdcp1x_info.ksvfifo + i * 15, 15);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ if (remain > 0) {
+ ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_KSV_FIFO,
+ hdcp_info->hdcp1x_info.ksvfifo + times * 15, remain * 5);
+ if (ret < 0)
+ return ret;
+ }
+
+ dev_dbg(mtk_dp->dev, "[HDCP1.X] Read ksvfifo:0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+ hdcp_info->hdcp1x_info.ksvfifo[0],
+ hdcp_info->hdcp1x_info.ksvfifo[1],
+ hdcp_info->hdcp1x_info.ksvfifo[2],
+ hdcp_info->hdcp1x_info.ksvfifo[3],
+ hdcp_info->hdcp1x_info.ksvfifo[4]);
+
+ return 0;
+}
+
+static int 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;
+ ssize_t ret;
+
+ for (i = 0; i < 5; i++) {
+ ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_V_PRIME(i), read_buffer, 4);
+ if (ret < 0)
+ return ret;
+
+ for (j = 0; j < 4; j++) {
+ hdcp_info->hdcp1x_info.v[(i * 4) + j] = read_buffer[3 - j];
+ dev_dbg(mtk_dp->dev, "[HDCP1.X] Read sink V:0x%x\n",
+ hdcp_info->hdcp1x_info.v[(i * 4) + j]);
+ }
+ }
+
+ return 0;
+}
+
+static int dp_tx_hdcp1x_auth_with_repeater(struct mtk_hdcp_info *hdcp_info)
+{
+ struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+ u8 *buffer;
+ u32 len;
+ int ret;
+
+ if (hdcp_info->hdcp1x_info.device_count > HDCP1X_REP_MAXDEVS) {
+ dev_err(mtk_dp->dev, "[HDCP1.X] Repeater:%d exceed max devs\n",
+ hdcp_info->hdcp1x_info.device_count);
+ return -EINVAL;
+ }
+
+ ret = dp_tx_hdcp1x_read_sink_ksv(hdcp_info, hdcp_info->hdcp1x_info.device_count);
+ if (ret)
+ return ret;
+
+ ret = dp_tx_hdcp1x_read_sink_sha_v(hdcp_info);
+ if (ret)
+ return ret;
+
+ len = hdcp_info->hdcp1x_info.device_count * DRM_HDCP_KSV_LEN + HDCP1X_B_INFO_LEN;
+ buffer = kmalloc(len, GFP_KERNEL);
+ if (!buffer) {
+ dev_err(mtk_dp->dev, "[HDCP1.X] Out of Memory\n");
+ return -ENOMEM;
+ }
+
+ 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);
+ ret = tee_hdcp1x_compute_compare_v(hdcp_info, buffer, len, hdcp_info->hdcp1x_info.v);
+ if (!ret)
+ dev_dbg(mtk_dp->dev, "[HDCP1.X] Check V' pass\n");
+ else
+ dev_err(mtk_dp->dev, "[HDCP1.X] Check V' fail\n");
+
+ kfree(buffer);
+
+ return ret;
+}
+
+static int dp_tx_hdcp1x_verify_b_ksv(struct mtk_hdcp_info *hdcp_info)
+{
+ struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, 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) {
+ dev_err(mtk_dp->dev, "[HDCP1.X] Check BKSV 20'1' 20'0' fail\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int 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);
+ int i, k, j;
+ ssize_t ret;
+ u8 tmp;
+
+ ret = tee_get_aksv(hdcp_info, hdcp_info->hdcp1x_info.a_ksv);
+ if (ret)
+ return ret;
+
+ ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_AUX_HDCP_AKSV, hdcp_info->hdcp1x_info.a_ksv,
+ DRM_HDCP_KSV_LEN);
+ if (ret < 0)
+ return ret;
+
+ 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;
+ dev_dbg(mtk_dp->dev, "[HDCP1.X] Aksv:0x%x\n", tmp);
+ }
+
+ if (k != 20) {
+ dev_err(mtk_dp->dev, "[HDCP1.X] Check AKSV 20'1' 20'0' fail\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int 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};
+ int ret;
+
+ ret = tee_hdcp1x_set_tx_an(hdcp_info, an_value);
+ if (ret)
+ return ret;
+
+ ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_AUX_HDCP_AN, an_value, DRM_HDCP_AN_LEN);
+ if (ret < 0)
+ return ret;
+
+ mdelay(5);
+
+ return 0;
+}
+
+static int 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];
+ bool sink_R0_available = false;
+ int i, tries;
+ ssize_t ret;
+ bool tmp;
+
+ tmp = dp_tx_hdcp1x_get_r0_available(hdcp_info);
+ if (!tmp) {
+ dev_err(mtk_dp->dev, "[HDCP1.X] Fail to get R0 available\n");
+ return -EINVAL;
+ }
+
+ tries = 2;
+ for (i = 0; i < tries; i++) {
+ ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BSTATUS, value, 1);
+ if (ret < 0)
+ continue;
+
+ sink_R0_available = (value[0x0] & DP_BSTATUS_R0_PRIME_READY) ? true : false;
+ if (sink_R0_available)
+ break;
+ }
+
+ if (i == tries) {
+ dev_err(mtk_dp->dev, "[HDCP1.X] R0 no available\n");
+ return -EINVAL;
+ }
+
+ tries = 3;
+ while (i < tries) {
+ ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_RI_PRIME, value, DRM_HDCP_RI_LEN);
+ if (ret < 0)
+ return ret;
+
+ ret = tee_compare_r0(hdcp_info, value, DRM_HDCP_RI_LEN);
+ if (!ret)
+ return ret;
+
+ dev_dbg(mtk_dp->dev, "[HDCP1.X] R0 check FAIL, Rx_R0:0x%x, 0x%x, retry\n",
+ value[0x1], value[0x0]);
+ mdelay(5);
+
+ i++;
+ }
+
+ dev_err(mtk_dp->dev, "[HDCP1.X] R0 check fail\n");
+ return -EINVAL;
+}
+
+/* Implements Part 1 of the HDCP authorization procedure */
+static int dp_tx_hdcp1x_auth(struct mtk_hdcp_info *hdcp_info)
+{
+ struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+ int ret, i, tries = 2;
+ bool expired;
+ ktime_t end;
+
+ if (!hdcp_info->hdcp1x_info.capable)
+ return -EAGAIN;
+
+ ret = dp_tx_hdcp1x_init(hdcp_info);
+ if (ret)
+ return ret;
+
+ ret = dp_tx_hdcp1x_write_an(hdcp_info);
+ if (ret)
+ return ret;
+ ret = dp_tx_hdcp1x_write_a_ksv(hdcp_info);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < tries; i++) {
+ ret = dp_tx_hdcp1x_read_sink_b_ksv(hdcp_info);
+ if (ret)
+ continue;
+
+ ret = dp_tx_hdcp1x_verify_b_ksv(hdcp_info);
+ if (!ret)
+ break;
+ }
+ if (i == tries)
+ return -ENODEV;
+ if (drm_hdcp_check_ksvs_revoked(mtk_dp->drm_dev, hdcp_info->hdcp1x_info.b_ksv, 1) > 0) {
+ dev_err(mtk_dp->dev, "[HDCP1.X] BKSV is revoked\n");
+ return -EPERM;
+ }
+
+ dp_tx_hdcp1x_set_repeater(hdcp_info, hdcp_info->hdcp1x_info.repeater);
+
+ ret = tee_calculate_lm(hdcp_info, hdcp_info->hdcp1x_info.b_ksv);
+ if (ret)
+ return ret;
+ dp_tx_hdcp1x_start_cipher(hdcp_info, true);
+
+ /* Wait 100ms(at least) before check R0 */
+ msleep(HDCP1X_R0_WDT);
+ ret = dp_tx_hdcp1x_check_r0(hdcp_info);
+ if (ret)
+ return ret;
+ ret = tee_hdcp_enable_encrypt(hdcp_info, true, HDCP_V1);
+ if (ret)
+ return ret;
+
+ if (!hdcp_info->hdcp1x_info.repeater)
+ return 0;
+
+ /* Check ksv ready (defined max time as 5s in spec) */
+ end = ktime_add_ms(ktime_get_raw(), HDCP1X_REP_RDY_WDT);
+ for (;;) {
+ ret = dp_tx_hdcp1x_check_sink_ksv_ready(hdcp_info);
+ if (!ret)
+ break;
+
+ expired = ktime_after(ktime_get_raw(), end);
+ if (expired) {
+ ret = -ETIMEDOUT;
+ dev_err(mtk_dp->dev, "[HDCP1.X] Check sink ksv ready timeout\n");
+ goto fail;
+ }
+
+ msleep(100);
+ }
+
+ ret = dp_tx_hdcp1x_check_sink_ksv_ready(hdcp_info);
+ if (ret)
+ goto fail;
+
+ ret = dp_tx_hdcp1x_read_sink_b_info(hdcp_info);
+ if (ret)
+ goto fail;
+
+ ret = dp_tx_hdcp1x_auth_with_repeater(hdcp_info);
+ if (ret)
+ goto fail;
+
+ return 0;
+
+fail:
+ tee_hdcp_enable_encrypt(hdcp_info, false, HDCP_NONE);
+
+ return ret;
+}
+
+void dp_tx_hdcp1x_get_info(struct mtk_hdcp_info *hdcp_info)
+{
+ struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+ u8 tmp[2];
+ ssize_t ret;
+
+ ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BCAPS, tmp, 0x1);
+ if (ret < 0)
+ return;
+
+ hdcp_info->hdcp1x_info.capable = tmp[0x0] & DP_BCAPS_HDCP_CAPABLE;
+ hdcp_info->hdcp1x_info.repeater = tmp[0x0] & DP_BCAPS_REPEATER_PRESENT;
+
+ dev_info(mtk_dp->dev, "[HDCP1.X] Capable:%d, Reapeater:%d\n",
+ hdcp_info->hdcp1x_info.capable,
+ hdcp_info->hdcp1x_info.repeater);
+}
+
+int dp_tx_hdcp1x_enable(struct mtk_hdcp_info *hdcp_info)
+{
+ struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+ int ret = 0, i, tries = 3;
+
+ hdcp_info->auth_status = AUTH_INIT;
+
+ ret = tee_add_device(hdcp_info, HDCP_VERSION_1X);
+ if (ret)
+ goto fail;
+
+ for (i = 0; i < tries; i++) {
+ ret = dp_tx_hdcp1x_auth(hdcp_info);
+ if (!ret) {
+ hdcp_info->auth_version = HDCP_VERSION_1X;
+ hdcp_info->auth_status = AUTH_PASS;
+ dev_info(mtk_dp->dev, "[HDCP1.X] Authentication done\n");
+
+ return 0;
+ }
+ }
+
+ tee_remove_device(hdcp_info);
+
+fail:
+ hdcp_info->auth_status = AUTH_FAIL;
+ dev_err(mtk_dp->dev, "[HDCP1.X] Authentication fail\n");
+
+ return ret;
+}
+
+int dp_tx_hdcp1x_disabel(struct mtk_hdcp_info *hdcp_info)
+{
+ struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+ int ret;
+
+ if (hdcp_info->auth_status == AUTH_PASS) {
+ ret = tee_hdcp_enable_encrypt(hdcp_info, false, HDCP_NONE);
+ if (ret)
+ return ret;
+
+ dp_tx_hdcp1x_start_cipher(hdcp_info, false);
+
+ ret = tee_hdcp1x_soft_rst(hdcp_info);
+ if (ret)
+ return ret;
+ }
+
+ tee_remove_device(hdcp_info);
+
+ hdcp_info->auth_version = HDCP_NONE;
+ hdcp_info->auth_status = AUTH_ZERO;
+ dev_info(mtk_dp->dev, "[HDCP1.X] Disable Authentication\n");
+
+ return 0;
+}
+
+int dp_tx_hdcp1x_check_link(struct mtk_hdcp_info *hdcp_info)
+{
+ struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info);
+ int ret = -EINVAL;
+ u8 bstatus;
+
+ mutex_lock(&mtk_dp->hdcp_mutex);
+
+ if (mtk_dp->hdcp_info.auth_status != AUTH_PASS)
+ goto end;
+
+ if (!mtk_dp->train_info.cable_plugged_in || !mtk_dp->enabled)
+ goto disable;
+
+ ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BSTATUS, &bstatus, 1);
+ if (ret != 1) {
+ dev_dbg(mtk_dp->dev, "[HDCP1.X] Read bstatus failed, reauth\n");
+ goto disable;
+ }
+
+ ret = bstatus & (DP_BSTATUS_LINK_FAILURE | DP_BSTATUS_REAUTH_REQ);
+
+ if (!ret) {
+ mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_ENABLED);
+ goto end;
+ }
+
+disable:
+ ret = dp_tx_hdcp1x_disabel(hdcp_info);
+ if (ret || !mtk_dp->train_info.cable_plugged_in || !mtk_dp->enabled) {
+ ret = -EAGAIN;
+ mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_DESIRED);
+ goto end;
+ }
+
+ ret = dp_tx_hdcp1x_enable(hdcp_info);
+ if (ret)
+ mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_DESIRED);
+
+end:
+ mutex_unlock(&mtk_dp->hdcp_mutex);
+
+ return ret;
+}
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..f0a19c491791
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h
@@ -0,0 +1,17 @@
+/* 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 HDCP_VERSION_1X 1
+
+void dp_tx_hdcp1x_get_info(struct mtk_hdcp_info *hdcp_info);
+int dp_tx_hdcp1x_enable(struct mtk_hdcp_info *hdcp_info);
+int dp_tx_hdcp1x_disabel(struct mtk_hdcp_info *hdcp_info);
+int dp_tx_hdcp1x_check_link(struct mtk_hdcp_info *hdcp_info);
+#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 8c60983a26ed..c04ea5dda6bd 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp_reg.h
+++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
@@ -277,6 +277,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 HDCP22_AUTH_DONE_DP_TRANS_P0_MASK BIT(4)
#define R0_AVAILABLE_DP_TRANS_P0 BIT(12)
--
2.43.0
next prev parent reply other threads:[~2024-06-08 12:02 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-06-08 12:01 [PATCH v3 0/3] Add HDCP feature for DisplayPort mac.shen
2024-06-08 12:01 ` [PATCH v3 1/3] Subject: [PATCH] drm/mediatek/dp: Add tee client application for HDCP feature mac.shen
2024-06-08 12:01 ` [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort mac.shen
2024-07-18 5:41 ` CK Hu (胡俊光)
2024-07-18 6:23 ` CK Hu (胡俊光)
2024-07-18 7:48 ` CK Hu (胡俊光)
2024-07-19 4:05 ` CK Hu (胡俊光)
2024-07-23 7:16 ` CK Hu (胡俊光)
2024-08-12 7:05 ` CK Hu (胡俊光)
2024-08-12 7:25 ` CK Hu (胡俊光)
2024-08-22 5:39 ` CK Hu (胡俊光)
2024-08-22 6:20 ` CK Hu (胡俊光)
2024-08-23 1:07 ` CK Hu (胡俊光)
2024-08-23 2:44 ` CK Hu (胡俊光)
2024-08-23 7:30 ` CK Hu (胡俊光)
2024-10-30 8:20 ` CK Hu (胡俊光)
2024-11-01 7:18 ` CK Hu (胡俊光)
2024-11-07 7:05 ` CK Hu (胡俊光)
2024-11-01 9:01 ` CK Hu (胡俊光)
2024-06-08 12:01 ` mac.shen [this message]
2024-08-30 9:03 ` [PATCH v3 3/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP1.x " CK Hu (胡俊光)
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240608120219.21817-4-mac.shen@mediatek.com \
--to=mac.shen@mediatek.com \
--cc=airlied@gmail.com \
--cc=angelogioacchino.delregno@collabora.com \
--cc=chunkuang.hu@kernel.org \
--cc=daniel@ffwll.ch \
--cc=dri-devel@lists.freedesktop.org \
--cc=jitao.shi@mediatek.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mediatek@lists.infradead.org \
--cc=matthias.bgg@gmail.com \
--cc=p.zabel@pengutronix.de \
--cc=shuijing.li@mediatek.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox