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.ca...@mediatek.com/ Signed-off-by: mac.shen <mac.s...@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