From nobody Fri Sep 20 01:44:13 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 88EA417BB7 for ; Sat, 8 Jun 2024 12:02:37 +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=1717848160; cv=none; b=WkJTZNmdAiV40naRkM5zB8p7h88q2fbI7YGXDEHIrlFIOXDGsUQAysLSHUFkqc5L8uNzBkY17l6bods9kQ2rTR2g3z1ZoJWSI96bEJBLvDiokHq79PoeG47a5eEKerYSAs30PAaVchmhvU5eBbK/GMAph3ybUvM6t5qV1tlsM2g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1717848160; c=relaxed/simple; bh=5+p325MwC5wwMts3nQonIl4Sq80wog4+UK39Nak6/kU=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=LjixRe0/epOBlzlu30M9+XrOBl0vE27vnpPv2b3Umyz58Csj9NJeEVsDgQDnT9+RZcZB96A3YNysA8732xqaxZ/jwuXn7aV1w0DEHHzTfp6vzzyLvEfEwcyoGMbLxwoH3sl3jgEnVv7NMn++WBBza2sIQFv0w++rybsYS51iGzY= 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=Y1VTRn4E; 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="Y1VTRn4E" X-UUID: f8af7bdc258e11efa54bbfbb386b949c-20240608 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=Fha0wMvCu4yV3fQ8EuLJs0uXnWh36Q6XM6sfU6rGTKo=; b=Y1VTRn4EgQrV3xny3cpeACZie2WL1xmSOxiz6knTGEXXldxCtv0RmRdTDVmbC0BxVsgG6v9eIBtSa8VE4NG7BtdjPNWX/9liqdDJ3m8GdrbkF39atK1wgUQ9VOQZovrabXTxZs2FCRl+hZP3yACuCVOMRTgjMIx5gbjhPg+wwD8=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.1.39,REQID:ed226e83-af3f-4629-8e99-2d8cdb0c6fcf,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:393d96e,CLOUDID:dc24dd84-4f93-4875-95e7-8c66ea833d57,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,ARC: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: f8af7bdc258e11efa54bbfbb386b949c-20240608 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 1054684970; Sat, 08 Jun 2024 20:02:25 +0800 Received: from mtkmbs13n1.mediatek.inc (172.21.101.193) by mtkmbs11n1.mediatek.inc (172.21.101.185) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.26; Sat, 8 Jun 2024 20:02:24 +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; Sat, 8 Jun 2024 20:02:24 +0800 From: mac.shen To: , , , , , , CC: , , , , , 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 Message-ID: <20240608120219.21817-4-mac.shen@mediatek.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240608120219.21817-1-mac.shen@mediatek.com> References: <20240608120219.21817-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" 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 --- 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/M= akefile index 19b7625ae573..a90c3294bfbe 100644 --- a/drivers/gpu/drm/mediatek/Makefile +++ b/drivers/gpu/drm/mediatek/Makefile @@ -28,6 +28,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 12854a04622f..3925eb2be064 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) @@ -1841,6 +1842,7 @@ static int mtk_dp_hpd_sink_event(struct mtk_dp *mtk_d= p) 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); } =20 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) =20 if (mtk_dp->hdcp_info.auth_version =3D=3D HDCP_VERSION_2X) dp_tx_hdcp2x_disabel(&mtk_dp->hdcp_info); + else if (mtk_dp->hdcp_info.auth_version =3D=3D HDCP_VERSION_1X) + dp_tx_hdcp1x_disabel(&mtk_dp->hdcp_info); =20 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 =3D=3D 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 =3D=3D HDCP_VERSION_1X && + (!dp_tx_hdcp1x_check_link(&mtk_dp->hdcp_info))) { + schedule_delayed_work(&mtk_dp->check_work, DRM_HDCP_CHECK_PERIOD_MS); } } =20 @@ -1890,6 +1897,10 @@ static void mtk_dp_hdcp_handle(struct work_struct *d= ata) check_link_interval =3D DRM_HDCP2_CHECK_PERIOD_MS; } =20 + if (ret && mtk_dp->hdcp_info.hdcp1x_info.capable && + mtk_dp->hdcp_info.hdcp_content_type !=3D DRM_MODE_HDCP_CONTENT_TYPE1) + ret =3D 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/med= iatek/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 + +#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, 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 & R0_AVAILABLE_DP_TRANS_P0) + 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, + 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 =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.device_count =3D 0x00; + + ret =3D tee_hdcp_enable_encrypt(hdcp_info, false, HDCP_NONE); + if (ret) + return ret; + + dp_tx_hdcp1x_start_cipher(hdcp_info, false); + + ret =3D 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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + u8 read_buffer[DRM_HDCP_KSV_LEN], i; + ssize_t ret; + + if (hdcp_info->hdcp1x_info.capable) { + ret =3D drm_dp_dpcd_read(&mtk_dp->aux, + DP_AUX_HDCP_BKSV, read_buffer, DRM_HDCP_KSV_LEN); + if (ret < 0) + return ret; + + for (i =3D 0; i < DRM_HDCP_KSV_LEN; i++) { + hdcp_info->hdcp1x_info.b_ksv[i] =3D 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_in= fo) +{ + struct mtk_dp *mtk_dp =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + u8 read_buffer; + ssize_t ret; + + ret =3D 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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + u8 read_buffer[DRM_HDCP_BSTATUS_LEN]; + ssize_t ret; + + ret =3D drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BINFO, read_buffer, DR= M_HDCP_BSTATUS_LEN); + if (ret < 0) + return ret; + + 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.device_count =3D DRM_HDCP_NUM_DOWNSTREAM(read_buff= er[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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + u8 times =3D dev_count / 3; + u8 remain =3D dev_count % 3; + ssize_t ret; + u8 i; + + if (times > 0) { + for (i =3D 0; i < times; i++) { + ret =3D 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 =3D 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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + u8 read_buffer[4], i, j; + ssize_t ret; + + for (i =3D 0; i < 5; i++) { + ret =3D drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_V_PRIME(i), read_buff= er, 4); + if (ret < 0) + return ret; + + for (j =3D 0; j < 4; j++) { + hdcp_info->hdcp1x_info.v[(i * 4) + j] =3D 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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + 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 =3D dp_tx_hdcp1x_read_sink_ksv(hdcp_info, hdcp_info->hdcp1x_info.devi= ce_count); + if (ret) + return ret; + + ret =3D dp_tx_hdcp1x_read_sink_sha_v(hdcp_info); + if (ret) + return ret; + + len =3D hdcp_info->hdcp1x_info.device_count * DRM_HDCP_KSV_LEN + HDCP1X_B= _INFO_LEN; + buffer =3D 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 =3D tee_hdcp1x_compute_compare_v(hdcp_info, buffer, len, hdcp_info->h= dcp1x_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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + 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) { + 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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + int i, k, j; + ssize_t ret; + u8 tmp; + + ret =3D tee_get_aksv(hdcp_info, hdcp_info->hdcp1x_info.a_ksv); + if (ret) + return ret; + + ret =3D drm_dp_dpcd_write(&mtk_dp->aux, DP_AUX_HDCP_AKSV, hdcp_info->hdcp= 1x_info.a_ksv, + DRM_HDCP_KSV_LEN); + if (ret < 0) + return ret; + + 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; + dev_dbg(mtk_dp->dev, "[HDCP1.X] Aksv:0x%x\n", tmp); + } + + if (k !=3D 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 =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}; + int ret; + + ret =3D tee_hdcp1x_set_tx_an(hdcp_info, an_value); + if (ret) + return ret; + + ret =3D drm_dp_dpcd_write(&mtk_dp->aux, DP_AUX_HDCP_AN, an_value, DRM_HDC= P_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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + u8 value[DRM_HDCP_BSTATUS_LEN]; + bool sink_R0_available =3D false; + int i, tries; + ssize_t ret; + bool tmp; + + tmp =3D 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 =3D 2; + for (i =3D 0; i < tries; i++) { + ret =3D drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BSTATUS, value, 1); + if (ret < 0) + continue; + + sink_R0_available =3D (value[0x0] & DP_BSTATUS_R0_PRIME_READY) ? true : = false; + if (sink_R0_available) + break; + } + + if (i =3D=3D tries) { + dev_err(mtk_dp->dev, "[HDCP1.X] R0 no available\n"); + return -EINVAL; + } + + tries =3D 3; + while (i < tries) { + ret =3D drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_RI_PRIME, value, DRM_= HDCP_RI_LEN); + if (ret < 0) + return ret; + + ret =3D 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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + int ret, i, tries =3D 2; + bool expired; + ktime_t end; + + if (!hdcp_info->hdcp1x_info.capable) + return -EAGAIN; + + ret =3D dp_tx_hdcp1x_init(hdcp_info); + if (ret) + return ret; + + ret =3D dp_tx_hdcp1x_write_an(hdcp_info); + if (ret) + return ret; + ret =3D dp_tx_hdcp1x_write_a_ksv(hdcp_info); + if (ret) + return ret; + + for (i =3D 0; i < tries; i++) { + ret =3D dp_tx_hdcp1x_read_sink_b_ksv(hdcp_info); + if (ret) + continue; + + ret =3D dp_tx_hdcp1x_verify_b_ksv(hdcp_info); + if (!ret) + break; + } + if (i =3D=3D 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 =3D 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 =3D dp_tx_hdcp1x_check_r0(hdcp_info); + if (ret) + return ret; + ret =3D 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 =3D ktime_add_ms(ktime_get_raw(), HDCP1X_REP_RDY_WDT); + for (;;) { + ret =3D dp_tx_hdcp1x_check_sink_ksv_ready(hdcp_info); + if (!ret) + break; + + expired =3D ktime_after(ktime_get_raw(), end); + if (expired) { + ret =3D -ETIMEDOUT; + dev_err(mtk_dp->dev, "[HDCP1.X] Check sink ksv ready timeout\n"); + goto fail; + } + + msleep(100); + } + + ret =3D dp_tx_hdcp1x_check_sink_ksv_ready(hdcp_info); + if (ret) + goto fail; + + ret =3D dp_tx_hdcp1x_read_sink_b_info(hdcp_info); + if (ret) + goto fail; + + ret =3D 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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + u8 tmp[2]; + ssize_t ret; + + ret =3D drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BCAPS, tmp, 0x1); + if (ret < 0) + return; + + hdcp_info->hdcp1x_info.capable =3D tmp[0x0] & DP_BCAPS_HDCP_CAPABLE; + hdcp_info->hdcp1x_info.repeater =3D 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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + int ret =3D 0, i, tries =3D 3; + + hdcp_info->auth_status =3D AUTH_INIT; + + ret =3D tee_add_device(hdcp_info, HDCP_VERSION_1X); + if (ret) + goto fail; + + for (i =3D 0; i < tries; i++) { + ret =3D dp_tx_hdcp1x_auth(hdcp_info); + if (!ret) { + hdcp_info->auth_version =3D HDCP_VERSION_1X; + hdcp_info->auth_status =3D AUTH_PASS; + dev_info(mtk_dp->dev, "[HDCP1.X] Authentication done\n"); + + return 0; + } + } + + tee_remove_device(hdcp_info); + +fail: + hdcp_info->auth_status =3D 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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + int ret; + + if (hdcp_info->auth_status =3D=3D AUTH_PASS) { + ret =3D tee_hdcp_enable_encrypt(hdcp_info, false, HDCP_NONE); + if (ret) + return ret; + + dp_tx_hdcp1x_start_cipher(hdcp_info, false); + + ret =3D tee_hdcp1x_soft_rst(hdcp_info); + if (ret) + return ret; + } + + tee_remove_device(hdcp_info); + + hdcp_info->auth_version =3D HDCP_NONE; + hdcp_info->auth_status =3D 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 =3D container_of(hdcp_info, struct mtk_dp, hdcp_inf= o); + int ret =3D -EINVAL; + u8 bstatus; + + mutex_lock(&mtk_dp->hdcp_mutex); + + if (mtk_dp->hdcp_info.auth_status !=3D AUTH_PASS) + goto end; + + if (!mtk_dp->train_info.cable_plugged_in || !mtk_dp->enabled) + goto disable; + + ret =3D drm_dp_dpcd_read(&mtk_dp->aux, DP_AUX_HDCP_BSTATUS, &bstatus, 1); + if (ret !=3D 1) { + dev_dbg(mtk_dp->dev, "[HDCP1.X] Read bstatus failed, reauth\n"); + goto disable; + } + + ret =3D 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 =3D dp_tx_hdcp1x_disabel(hdcp_info); + if (ret || !mtk_dp->train_info.cable_plugged_in || !mtk_dp->enabled) { + ret =3D -EAGAIN; + mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_DESIRED); + goto end; + } + + ret =3D 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/med= iatek/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/mediat= ek/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) --=20 2.43.0