From nobody Fri Sep 20 05:49:20 2024 Received: from mailgw01.mediatek.com (unknown [60.244.123.138]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 29700BA28 for ; Mon, 5 Feb 2024 05:51:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=60.244.123.138 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707112296; cv=none; b=sqN210KXe6jdDeXmreomYHspRDliHSU97JVlc95ZRWr6X8Oj6+VlFs9Mr5B8maxPlwm9Gy0fneRJ640rGGJ6sUEPkQ7g//MqGRZFCsAg19mPcGONJ9MsFXdgkJRvZALMMeGGc8NWuPhqnwtN8kqWG2poy86BMWWGVtnuCgCJjtk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707112296; c=relaxed/simple; bh=wIScT4wzzBHEmeQcB8XUur7sbW7gt+B0KLEmsgKQ5Bk=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=eNPvqweHc4f621W5zEhI1HNVglsT12fAPWirpHQXHWSRY+yZedZodUfkG+LMx3C3fEwIuod6Vq8/AzGFJLEnS8k5EATfP6/C6S92RvdwMEwaj7eCUhYQPMHF6gOcHqS3ujpxO2+DBjU6I8O5ycQD3uiie+ln7+78OlPCpv++o6s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com; spf=pass smtp.mailfrom=mediatek.com; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b=qPfXc4XO; arc=none smtp.client-ip=60.244.123.138 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mediatek.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b="qPfXc4XO" X-UUID: 9afbb8c6c3ea11ee9e680517dc993faa-20240205 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Type:Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=Zj4kF6Nfh/PvZmHs+5yXsdA7oEfEbh/7MGvnWp+xHbU=; b=qPfXc4XOSXvOQkCx18AzvTQkpZUBnA8l0Db4gTjqtmp2dyyt6UI6miqsiA5R/0LT8ddjtrDmKZ0iZ90oSvsQMGzBvOfdh5HNpPqb9qmkA87toieSpr742GpEQDJzm/sc0/WLTYFGgfKSkCZ8pjuTf3YckPIYxtWZygQkdEBuEUs=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.1.37,REQID:c1d94499-2061-457c-9b04-acb7ced05c6e,IP:0,U RL:0,TC:0,Content:0,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION: release,TS:0 X-CID-META: VersionHash:6f543d0,CLOUDID:c892068f-e2c0-40b0-a8fe-7c7e47299109,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:102,TC:nil,Content:0,EDM:-3,IP:nil,U RL:1,File:nil,RT:nil,Bulk:nil,QS:nil,BEC:nil,COL:0,OSI:0,OSA:0,AV:0,LES:1, SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0 X-CID-BVR: 0,NGT X-CID-BAS: 0,NGT,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR,TF_CID_SPAM_ULS X-UUID: 9afbb8c6c3ea11ee9e680517dc993faa-20240205 Received: from mtkmbs11n1.mediatek.inc [(172.21.101.185)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 249598753; Mon, 05 Feb 2024 13:51:28 +0800 Received: from mtkmbs13n1.mediatek.inc (172.21.101.193) by mtkmbs11n2.mediatek.inc (172.21.101.187) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.26; Mon, 5 Feb 2024 13:51:27 +0800 Received: from mszsdhlt06.gcn.mediatek.inc (10.16.6.206) by mtkmbs13n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.1118.26 via Frontend Transport; Mon, 5 Feb 2024 13:51:27 +0800 From: mac.shen To: , , , , , , CC: , , , , , Subject: [PATCH v2 3/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP1.x feature for DisplayPort Date: Mon, 5 Feb 2024 13:50:38 +0800 Message-ID: <20240205055055.25340-4-mac.shen@mediatek.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240205055055.25340-1-mac.shen@mediatek.com> References: <20240205055055.25340-1-mac.shen@mediatek.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-MTK: N Content-Type: text/plain; charset="utf-8" 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 --- 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/M= akefile index 50ea069b047e..9738235f76b8 100644 --- a/drivers/gpu/drm/mediatek/Makefile +++ b/drivers/gpu/drm/mediatek/Makefile @@ -27,6 +27,7 @@ mediatek-drm-hdmi-objs :=3D mtk_cec.o \ obj-$(CONFIG_DRM_MEDIATEK_HDMI) +=3D mediatek-drm-hdmi.o =20 mtk-dp-objs :=3D tlc_dp_hdcp.o \ + mtk_dp_hdcp1x.o \ mtk_dp_hdcp2.o \ mtk_dp.o =20 diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/m= tk_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 @@ =20 #include "mtk_dp.h" #include "mtk_dp_reg.h" +#include "mtk_dp_hdcp1x.h" #include "mtk_dp_hdcp2.h" =20 #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; =20 + if (dp_tx_hdcp1x_support(&mtk_dp->hdcp_info)) + return; + if (tee_add_device(&mtk_dp->hdcp_info, HDCP_NONE) !=3D RET_SUCCESS) mtk_dp->hdcp_info.auth_status =3D 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 !=3D DRM_MODE_HDCP_CONTENT_TYPE1) + dp_tx_hdcp1x_set_start_auth(&mtk_dp->hdcp_info, true); else mtk_dp->hdcp_info.auth_status =3D AUTH_ZERO; } =20 - while (mtk_dp->hdcp_info.hdcp2_info.enable && - mtk_dp->hdcp_info.auth_status !=3D AUTH_FAIL && + while ((mtk_dp->hdcp_info.hdcp1x_info.enable || + mtk_dp->hdcp_info.hdcp2_info.enable) && + mtk_dp->hdcp_info.auth_status !=3D AUTH_FAIL && mtk_dp->hdcp_info.auth_status !=3D 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 =3D=3D 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 !=3D + DRM_MODE_HDCP_CONTENT_TYPE1) { + mtk_dp->hdcp_info.hdcp2_info.enable =3D 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 !=3D DRM_MODE_HDCP_CONTENT_TYPE1) + dp_tx_hdcp1x_fsm(&mtk_dp->hdcp_info); } } =20 @@ -1924,6 +1947,8 @@ static void mtk_dp_hdcp_atomic_check(struct mtk_dp *m= tk_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); =20 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_b= ridge *bridge, =20 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); =20 if (mtk_dp->hdcp_info.content_protection !=3D DRM_MODE_CONTENT_PROTECTION= _UNDESIRED) { mtk_dp->hdcp_info.content_protection =3D DRM_MODE_CONTENT_PROTECTION_DES= IRED; diff --git a/drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.c b/drivers/gpu/drm/med= iatek/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, boo= l enable) +{ + struct mtk_dp *mtk_dp =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + + 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_GENERA= TED); + } 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_AUT= H); + } +} + +static bool dp_tx_hdcp1x_get_r0_available(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + bool R0_available; + u32 ret; + + ret =3D mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_34A4); + if (ret & BIT(12)) + R0_available =3D true; + else + R0_available =3D false; + + return R0_available; +} + +static void dp_tx_hdcp1x_set_repeater(struct mtk_hdcp_info *hdcp_info, boo= l enable) +{ + struct mtk_dp *mtk_dp =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + + 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 ena= ble) +{ + hdcp_info->hdcp1x_info.enable =3D enable; + + if (enable) { + hdcp_info->auth_status =3D AUTH_INIT; + hdcp_info->hdcp1x_info.main_states =3D HDCP1X_main_state_A0; + hdcp_info->hdcp1x_info.sub_states =3D HDCP1X_sub_FSM_IDLE; + } else { + hdcp_info->auth_status =3D AUTH_ZERO; + hdcp_info->hdcp1x_info.main_states =3D HDCP1X_main_state_H2; + hdcp_info->hdcp1x_info.sub_states =3D 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 =3D 0; +} + +bool dp_tx_hdcp1x_support(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + u8 tmp[2]; + int ret; + + drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BCAPS, tmp, 0x1); + + hdcp_info->hdcp1x_info.enable =3D tmp[0x0] & BIT(0); + hdcp_info->hdcp1x_info.repeater =3D (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 =3D tee_add_device(hdcp_info, HDCP_VERSION_1X); + if (ret !=3D RET_SUCCESS) { + DPTXHDCPERR("1.x: HDCP TA has some error\n"); + hdcp_info->hdcp1x_info.enable =3D 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 =3D false; + hdcp_info->hdcp1x_info.r0_read =3D false; + hdcp_info->hdcp1x_info.b_status =3D 0x00; + for (i =3D 0; i < 5; i++) { + hdcp_info->hdcp1x_info.b_ksv[i] =3D 0x00; + hdcp_info->hdcp1x_info.a_ksv[i] =3D 0x00; + } + + for (i =3D 0; i < 5; i++) + hdcp_info->hdcp1x_info.v[i] =3D 0x00; + + hdcp_info->hdcp1x_info.b_info[0] =3D 0x00; + hdcp_info->hdcp1x_info.b_info[1] =3D 0x00; + hdcp_info->hdcp1x_info.max_cascade =3D false; + hdcp_info->hdcp1x_info.max_devs =3D false; + hdcp_info->hdcp1x_info.device_count =3D 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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + 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_K= SV_LEN); + + for (i =3D 0; i < DRM_HDCP_KSV_LEN; i++) { + hdcp_info->hdcp1x_info.b_ksv[i] =3D read_buffer[i]; + DPTXHDCPMSG("1.x: Bksv =3D 0x%x\n", read_buffer[i]); + } + } + + return true; +} + +static bool dp_tx_hdcp1x_check_sink_ksv_ready(struct mtk_hdcp_info *hdcp_i= nfo) +{ + struct mtk_dp *mtk_dp =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + u8 read_buffer; + + drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BSTATUS, &read_buffer, 1); + + hdcp_info->hdcp1x_info.ksv_ready =3D (read_buffer & BIT(0)) ? true : fal= se; + + 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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + u8 read_buffer[0x2]; + + drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BCAPS, read_buffer, 1); + + hdcp_info->hdcp1x_info.repeater =3D (read_buffer[0] & BIT(1)) ? true : fa= lse; + + return true; +} + +static bool dp_tx_hdcp1x_read_sink_b_info(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + u8 read_buffer[DRM_HDCP_BSTATUS_LEN]; + + drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BINFO, read_buffer, DRM_HDCP_B= STATUS_LEN); + + hdcp_info->hdcp1x_info.b_info[0] =3D read_buffer[0]; + hdcp_info->hdcp1x_info.b_info[1] =3D read_buffer[1]; + hdcp_info->hdcp1x_info.max_cascade =3D (read_buffer[1] & BIT(3)) ? true := false; + hdcp_info->hdcp1x_info.max_devs =3D (read_buffer[0] & BIT(7)) ? true : fa= lse; + hdcp_info->hdcp1x_info.device_count =3D read_buffer[0] & 0x7F; + + DPTXHDCPMSG("1.x: Binfo max_cascade_EXCEEDED =3D %d\n", hdcp_info->hdcp1x= _info.max_cascade); + DPTXHDCPMSG("1.x: Binfo DEPTH =3D %d\n", read_buffer[1] & 0x07); + DPTXHDCPMSG("1.x: Binfo max_devs_EXCEEDED =3D %d\n", hdcp_info->hdcp1x_in= fo.max_devs); + DPTXHDCPMSG("1.x: Binfo device_count =3D %d\n", hdcp_info->hdcp1x_info.de= vice_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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + u8 i; + u8 times =3D dev_count / 3; + u8 remain =3D dev_count % 3; + + if (times > 0) { + for (i =3D 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 =3D %x\n", hdcp_info->hdcp1x_info.ksvfifo[= 0]); + DPTXHDCPMSG("1.x: Read ksvfifo =3D %x\n", hdcp_info->hdcp1x_info.ksvfifo[= 1]); + DPTXHDCPMSG("1.x: Read ksvfifo =3D %x\n", hdcp_info->hdcp1x_info.ksvfifo[= 2]); + DPTXHDCPMSG("1.x: Read ksvfifo =3D %x\n", hdcp_info->hdcp1x_info.ksvfifo[= 3]); + DPTXHDCPMSG("1.x: Read ksvfifo =3D %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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + u8 read_buffer[4], i, j; + + for (i =3D 0; i < 5; i++) { + drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_V_PRIME(i), read_buffer, 4); + for (j =3D 0; j < 4; j++) { + hdcp_info->hdcp1x_info.v[(i * 4) + j] =3D read_buffer[3 - j]; + DPTXHDCPMSG("1.x: Read sink V =3D %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_inf= o) +{ + bool ret =3D false; + u8 *buffer =3D NULL; + u32 len =3D 0; + int tmp =3D 0; + + if (hdcp_info->hdcp1x_info.device_count > HDCP1X_REP_MAXDEVS) { + DPTXHDCPERR("1.x: Repeater: %d DEVs!\n", hdcp_info->hdcp1x_info.device_c= ount); + 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 =3D hdcp_info->hdcp1x_info.device_count * DRM_HDCP_KSV_LEN + HDCP1X_B= _INFO_LEN; + buffer =3D 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 =3D tee_hdcp1x_compute_compare_v(hdcp_info, buffer, len, hdcp_info->h= dcp1x_info.v); + if (tmp =3D=3D RET_COMPARE_PASS) { + DPTXHDCPMSG("1.x: Check V' PASS\n"); + ret =3D 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 =3D 0; + u8 ksv; + + for (i =3D 0; i < DRM_HDCP_KSV_LEN; i++) { + ksv =3D hdcp_info->hdcp1x_info.b_ksv[i]; + for (j =3D 0; j < 8; j++) + k +=3D (ksv >> j) & 0x01; + } + + if (k !=3D 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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + 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 =3D 0, k =3D 0; i < DRM_HDCP_KSV_LEN; i++) { + tmp =3D hdcp_info->hdcp1x_info.a_ksv[i]; + + for (j =3D 0; j < 8; j++) + k +=3D (tmp >> j) & 0x01; + DPTXHDCPMSG("1.x: Aksv 0x%x\n", tmp); + } + + if (k !=3D 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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + u8 an_value[DRM_HDCP_AN_LEN] =3D { /* 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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + u8 value[DRM_HDCP_BSTATUS_LEN]; + u8 retry_count =3D 0; + bool sink_R0_available =3D false; + bool ret; + int tmp; + + ret =3D 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 =3D ((value[0x0] & BIT(1)) =3D=3D BIT(1)) ? true : fal= se; + + if (!sink_R0_available) { + drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BSTATUS, value, 1); + sink_R0_available =3D ((value[0x0] & BIT(1)) =3D=3D BIT(1)) ? true : fa= lse; + + 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 =3D tee_compare_r0(hdcp_info, value, DRM_HDCP_RI_LEN); + if (tmp =3D=3D RET_COMPARE_PASS) + return true; + + DPTXHDCPMSG("1.x: R0 check FAIL:Rx_R0=3D0x%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)=3D (%d, %d)", + hdcp_info->hdcp1x_info.main_states, + hdcp_info->hdcp1x_info.sub_states); + hdcp_info->hdcp1x_info.main_states =3D HDCP1X_main_state_A0; + hdcp_info->hdcp1x_info.sub_states =3D 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 !=3D hdcp_info->hdcp1x_info.main_states || + hdcp_info->hdcp1x_info.sub_states !=3D pre_sub) { + DPTXHDCPMSG("1.x: State(M : S)=3D (%d, %d)", + hdcp_info->hdcp1x_info.main_states, + hdcp_info->hdcp1x_info.sub_states); + pre_main =3D hdcp_info->hdcp1x_info.main_states; + pre_sub =3D 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 =3D=3D HDCP1X_sub_FSM_auth_fail) { + tee_hdcp_enable_encrypt(hdcp_info, false, HDCP_NONE); + DPTXHDCPMSG("1.x: Authentication Fail\n"); + hdcp_info->auth_status =3D AUTH_FAIL; + hdcp_info->hdcp1x_info.main_states =3D HDCP1X_main_state_H2; + hdcp_info->hdcp1x_info.sub_states =3D 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 =3D=3D 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 =3D HDCP1X_main_state_H2; + hdcp_info->hdcp1x_info.sub_states =3D HDCP1X_sub_FSM_auth_fail; + break; + } + + dp_tx_hdcp1x_init(hdcp_info); + hdcp_info->hdcp1x_info.main_states =3D HDCP1X_main_state_A0; + hdcp_info->hdcp1x_info.sub_states =3D HDCP1X_sub_FSM_CHECKHDCPCAPABLE; + } + + /* HDCP1X_main_state_A0 */ + /* HDCP1X_sub_FSM_CHECKHDCPCAPABLE */ + if (hdcp_info->hdcp1x_info.sub_states =3D=3D HDCP1X_sub_FSM_CHECKHDCPCAP= ABLE) { + 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 =3D HDCP1X_main_state_A1; + hdcp_info->hdcp1x_info.sub_states =3D 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 =3D=3D HDCP1X_sub_FSM_exchange_KSV= ) { + dp_tx_hdcp1x_write_an(hdcp_info); + ret =3D dp_tx_hdcp1x_write_a_ksv(hdcp_info); + if (!ret) { + dp_tx_hdcp1x_state_rst(hdcp_info); + break; + } + + pre_time =3D mtk_dp_get_system_time(); + hdcp_info->hdcp1x_info.main_states =3D HDCP1X_main_state_A1; + hdcp_info->hdcp1x_info.sub_states =3D HDCP1X_sub_FSM_verify_bksv; + } + + /* HDCP1X_main_state_A1 */ + /* HDCP1X_sub_FSM_verify_bksv */ + if (hdcp_info->hdcp1x_info.sub_states =3D=3D 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 =3D mtk_dp_get_time_diff(pre_time); + if (time >=3D HDCP1X_BSTATUS_TIMEOUT_CNT) { + dp_tx_hdcp1x_state_rst(hdcp_info); + break; + } + + pre_time =3D mtk_dp_get_system_time(); + ret =3D 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 =3D HDCP1X_main_state_A2; + hdcp_info->hdcp1x_info.sub_states =3D 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 =3D=3D 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 =3D HDCP1X_main_state_A3; + hdcp_info->hdcp1x_info.sub_states =3D HDCP1X_sub_FSM_check_R0; + pre_time =3D 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 =3D=3D HDCP1X_sub_FSM_check_R0) { + /* Wait 100ms(at least) before check R0 */ + time =3D mtk_dp_get_time_diff(pre_time); + if (time < HDCP1X_R0_WDT && !hdcp_info->hdcp1x_info.r0_read) { + mdelay(10); + break; + } + + ret =3D 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 =3D HDCP1X_main_state_A5; + hdcp_info->hdcp1x_info.sub_states =3D 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 =3D=3D HDCP1X_sub_FSM_auth_done) { + DPTXHDCPMSG("1.x: Authentication done!\n"); + hdcp_info->hdcp1x_info.retry_count =3D 0; + hdcp_info->auth_status =3D AUTH_PASS; + hdcp_info->hdcp1x_info.main_states =3D HDCP1X_main_state_A4; + hdcp_info->hdcp1x_info.sub_states =3D 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 =3D=3D 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 =3D HDCP1X_main_state_A4; + hdcp_info->hdcp1x_info.sub_states =3D HDCP1X_sub_FSM_auth_done; + break; + } + + DPTXHDCPMSG("1.x: Repeater!\n"); + pre_time =3D mtk_dp_get_system_time(); + hdcp_info->hdcp1x_info.main_states =3D HDCP1X_main_state_A6; + hdcp_info->hdcp1x_info.sub_states =3D 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 =3D=3D HDCP1X_sub_FSM_polling_rdy_= bit) { + time =3D mtk_dp_get_time_diff(pre_time); + if (time > HDCP1X_REP_RDY_WDT) { + dp_tx_hdcp1x_state_rst(hdcp_info); + break; + } + + time =3D 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 =3D HDCP1X_main_state_A7; + hdcp_info->hdcp1x_info.sub_states =3D + HDCP1X_sub_FSM_auth_with_repeater; + hdcp_info->hdcp1x_info.ksv_ready =3D false; + } + } + break; + + case HDCP1X_main_state_A7: + /* HDCP1X_main_state_A7 */ + /* HDCP1X_sub_FSM_auth_with_repeater */ + if (hdcp_info->hdcp1x_info.sub_states =3D=3D HDCP1X_sub_FSM_auth_with_re= peater) { + if (hdcp_info->hdcp1x_info.max_cascade || hdcp_info->hdcp1x_info.max_de= vs) { + DPTXHDCPERR("1.x: MAX CASCADE or MAX DEVS!\n"); + dp_tx_hdcp1x_state_rst(hdcp_info); + break; + } + + ret =3D dp_tx_hdcp1x_auth_with_repeater(hdcp_info); + if (!ret) { + dp_tx_hdcp1x_state_rst(hdcp_info); + break; + } + + hdcp_info->hdcp1x_info.main_states =3D HDCP1X_main_state_A4; + hdcp_info->hdcp1x_info.sub_states =3D HDCP1X_sub_FSM_auth_done; + } + break; + + default: + break; + } +} diff --git a/drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h b/drivers/gpu/drm/med= iatek/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 =3D 0, + HDCP1X_main_state_A0 =3D 1, + HDCP1X_main_state_A1 =3D 2, + HDCP1X_main_state_A2 =3D 3, + HDCP1X_main_state_A3 =3D 4, + HDCP1X_main_state_A4 =3D 5, + HDCP1X_main_state_A5 =3D 6, + HDCP1X_main_state_A6 =3D 7, + HDCP1X_main_state_A7 =3D 8, +}; + +enum DPTX_DRV_HDCP1X_sub_states { + HDCP1X_sub_FSM_IDLE =3D 0, + HDCP1X_sub_FSM_CHECKHDCPCAPABLE =3D 1, + HDCP1X_sub_FSM_exchange_KSV =3D 2, + HDCP1X_sub_FSM_verify_bksv =3D 3, + HDCP1X_sub_FSM_computation =3D 4, + HDCP1X_sub_FSM_check_R0 =3D 5, + HDCP1X_sub_FSM_auth_done =3D 6, + HDCP1X_sub_FSM_polling_rdy_bit =3D 7, + HDCP1X_sub_FSM_auth_with_repeater =3D 8, + HDCP1X_sub_FSM_auth_fail =3D 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 ena= ble); + +#endif /* _MTK_DP_HDCP1X_H_ */ diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediat= ek/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 --=20 2.43.0