* [PATCH v3 0/3] Add HDCP feature for DisplayPort
@ 2024-06-08 12:01 ` mac.shen
0 siblings, 0 replies; 42+ messages in thread
From: mac.shen @ 2024-06-08 12:01 UTC (permalink / raw)
To: chunkuang.hu, p.zabel, airlied, daniel, matthias.bgg,
angelogioacchino.delregno, jitao.shi
Cc: mac.shen, shuijing.li, linux-kernel, dri-devel, linux-mediatek,
linux-arm-kernel
Add tee client application, HDCP 1.x and 2.x authentication for DisplayPort
to support the HDCP feature.
mac.shen (3):
Subject: [PATCH] drm/mediatek/dp: Add tee client application for HDCP
feature
Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort
Subject: [PATCH] drm/mediatek/dp: Add HDCP1.x feature for DisplayPort
drivers/gpu/drm/mediatek/Makefile | 7 +-
drivers/gpu/drm/mediatek/mtk_dp.c | 332 ++++++--
drivers/gpu/drm/mediatek/mtk_dp.h | 103 +++
drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.c | 577 ++++++++++++++
drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h | 17 +
drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c | 944 +++++++++++++++++++++++
drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h | 25 +
drivers/gpu/drm/mediatek/mtk_dp_reg.h | 11 +-
drivers/gpu/drm/mediatek/mtk_dpi.c | 3 +
drivers/gpu/drm/mediatek/tci.h | 143 ++++
drivers/gpu/drm/mediatek/tlc_dp_hdcp.c | 595 ++++++++++++++
drivers/gpu/drm/mediatek/tlc_dp_hdcp.h | 133 ++++
12 files changed, 2805 insertions(+), 85 deletions(-)
create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.h
create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.c
create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h
create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c
create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h
create mode 100644 drivers/gpu/drm/mediatek/tci.h
create mode 100644 drivers/gpu/drm/mediatek/tlc_dp_hdcp.c
create mode 100644 drivers/gpu/drm/mediatek/tlc_dp_hdcp.h
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* [PATCH v3 0/3] Add HDCP feature for DisplayPort @ 2024-06-08 12:01 ` mac.shen 0 siblings, 0 replies; 42+ messages in thread From: mac.shen @ 2024-06-08 12:01 UTC (permalink / raw) To: chunkuang.hu, p.zabel, airlied, daniel, matthias.bgg, angelogioacchino.delregno, jitao.shi Cc: mac.shen, shuijing.li, linux-kernel, dri-devel, linux-mediatek, linux-arm-kernel Add tee client application, HDCP 1.x and 2.x authentication for DisplayPort to support the HDCP feature. mac.shen (3): Subject: [PATCH] drm/mediatek/dp: Add tee client application for HDCP feature Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort Subject: [PATCH] drm/mediatek/dp: Add HDCP1.x feature for DisplayPort drivers/gpu/drm/mediatek/Makefile | 7 +- drivers/gpu/drm/mediatek/mtk_dp.c | 332 ++++++-- drivers/gpu/drm/mediatek/mtk_dp.h | 103 +++ drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.c | 577 ++++++++++++++ drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h | 17 + drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c | 944 +++++++++++++++++++++++ drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h | 25 + drivers/gpu/drm/mediatek/mtk_dp_reg.h | 11 +- drivers/gpu/drm/mediatek/mtk_dpi.c | 3 + drivers/gpu/drm/mediatek/tci.h | 143 ++++ drivers/gpu/drm/mediatek/tlc_dp_hdcp.c | 595 ++++++++++++++ drivers/gpu/drm/mediatek/tlc_dp_hdcp.h | 133 ++++ 12 files changed, 2805 insertions(+), 85 deletions(-) create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.h create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.c create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h create mode 100644 drivers/gpu/drm/mediatek/tci.h create mode 100644 drivers/gpu/drm/mediatek/tlc_dp_hdcp.c create mode 100644 drivers/gpu/drm/mediatek/tlc_dp_hdcp.h -- 2.43.0 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH v3 1/3] Subject: [PATCH] drm/mediatek/dp: Add tee client application for HDCP feature 2024-06-08 12:01 ` mac.shen @ 2024-06-08 12:01 ` mac.shen -1 siblings, 0 replies; 42+ messages in thread From: mac.shen @ 2024-06-08 12:01 UTC (permalink / raw) To: chunkuang.hu, p.zabel, airlied, daniel, matthias.bgg, angelogioacchino.delregno, jitao.shi Cc: mac.shen, shuijing.li, linux-kernel, dri-devel, linux-mediatek, linux-arm-kernel Add tee client application which will be used for HDCP 1.x and 2.x authentication in DisplayPort. Changes in v3: - remove useless define - refine the return value - remove the define which control the log output - remove the struct hdcp2_handler for HDCP 2.X - refine the struct for the HDCP information per suggestion from the previous thread: https://patchwork.kernel.org/project/linux-mediatek /patch/20240205055055.25340-2-mac.shen@mediatek.com/ Signed-off-by: mac.shen <mac.shen@mediatek.com> --- drivers/gpu/drm/mediatek/Makefile | 5 +- drivers/gpu/drm/mediatek/tci.h | 143 ++++++ drivers/gpu/drm/mediatek/tlc_dp_hdcp.c | 595 +++++++++++++++++++++++++ drivers/gpu/drm/mediatek/tlc_dp_hdcp.h | 133 ++++++ 4 files changed, 875 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/mediatek/tci.h create mode 100644 drivers/gpu/drm/mediatek/tlc_dp_hdcp.c create mode 100644 drivers/gpu/drm/mediatek/tlc_dp_hdcp.h diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile index 32a2ed6c0cfe..f27ddfbd2e12 100644 --- a/drivers/gpu/drm/mediatek/Makefile +++ b/drivers/gpu/drm/mediatek/Makefile @@ -27,4 +27,7 @@ mediatek-drm-hdmi-objs := mtk_cec.o \ obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o -obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o +mtk-dp-objs := tlc_dp_hdcp.o \ + mtk_dp.o + +obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk-dp.o diff --git a/drivers/gpu/drm/mediatek/tci.h b/drivers/gpu/drm/mediatek/tci.h new file mode 100644 index 000000000000..48710ce5d391 --- /dev/null +++ b/drivers/gpu/drm/mediatek/tci.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019-2024 MediaTek Inc. + */ + +#ifndef _TCI_H_ +#define _TCI_H_ + +#include <drm/display/drm_hdcp.h> + +#define CMD_DEVICE_ADDED 1 +#define CMD_DEVICE_REMOVE 2 +#define CMD_WRITE_VAL 3 +#define CMD_DEVICE_CLEAN 4 +#define CMD_ENABLE_ENCRYPT 5 + +/* V1.x */ +#define CMD_CALCULATE_LM 11 +#define CMD_COMPARE_R0 12 +#define CMD_COMPARE_V1 13 +#define CMD_GET_AKSV 14 + +/* V2.x */ +#define CMD_AKE_CERTIFICATE 20 +#define CMD_ENC_KM 21 +#define CMD_AKE_H_PRIME 22 +#define CMD_AKE_PARING 23 +#define CMD_LC_L_PRIME 24 +#define CMD_SKE_CAL_EKS 26 +#define CMD_COMPARE_V2 27 +#define CMD_COMPARE_M 28 + +#define TYPE_HDCP_PARAM_AN 10 +#define TYPE_HDCP_PARAM_RST_1 11 +#define TYPE_HDCP_PARAM_RST_2 12 +#define TYPE_HDCP_ENABLE_ENCRYPT 13 +#define TYPE_HDCP_DISABLE_ENCRYPT 14 + +/* reserved:2 */ +#define HDCP2_CERTRX_LEN (HDCP_2_2_RECEIVER_ID_LEN + HDCP_2_2_K_PUB_RX_LEN + \ + 2 + HDCP_2_2_DCP_LLC_SIG_LEN) +/* version:1 */ +#define HDCP_2_2_TXCAPS_LEN (HDCP_2_2_TXCAP_MASK_LEN + 1) +#define PARAM_LEN 1024 + +#define TCI_LENGTH sizeof(struct tci_t) + +struct cmd_hdcp_init_for_verion_t { + u32 version; + bool need_load_key; +}; + +struct cmd_hdcp_write_val_t { + u8 type; + u8 len; + u8 val[DRM_HDCP_AN_LEN]; +}; + +struct cmd_hdcp_calculate_lm_t { + u8 bksv[DRM_HDCP_KSV_LEN]; +}; + +struct cmd_hdcp_get_aksv_t { + u8 aksv[DRM_HDCP_KSV_LEN]; +}; + +struct cmd_hdcp_ake_certificate_t { + u8 certification[HDCP2_CERTRX_LEN]; + bool stored; + u8 m[HDCP_2_2_E_KH_KM_M_LEN - HDCP_2_2_E_KH_KM_LEN]; + u8 ekm[HDCP_2_2_E_KH_KM_LEN]; +}; + +struct cmd_hdcp_ake_paring_t { + u8 ekm[HDCP_2_2_E_KH_KM_LEN]; +}; + +struct cmd_hdcp_enc_km_t { + u8 enc_km[HDCP_2_2_E_KPUB_KM_LEN]; +}; + +struct cmd_hdcp_ake_h_prime_t { + u8 rtx[HDCP_2_2_RTX_LEN]; + u8 rrx[HDCP_2_2_RRX_LEN]; + u8 rx_caps[HDCP_2_2_RXCAPS_LEN]; + u8 tx_caps[HDCP_2_2_TXCAPS_LEN]; + u32 rx_h_len; + u8 rx_h[HDCP_2_2_H_PRIME_LEN]; +}; + +struct cmd_hdcp_lc_l_prime_t { + u8 rn[HDCP_2_2_RN_LEN]; + u32 rx_l_len; + u8 rx_l[HDCP_2_2_L_PRIME_LEN]; +}; + +struct cmd_hdcp_ske_eks_t { + u8 riv[HDCP_2_2_RIV_LEN]; + u32 eks_len; + u32 eks; +}; + +struct cmd_hdcp_compare_t { + u32 rx_val_len; + u8 rx_val[HDCP_2_2_MPRIME_LEN]; + u32 param_len; + u8 param[PARAM_LEN]; + u32 out_len; + u32 out; +}; + +union tci_cmd_body_t { + /* Init with special HDCP version */ + struct cmd_hdcp_init_for_verion_t cmd_hdcp_init_for_verion; + /* Write uint32 data to hw */ + struct cmd_hdcp_write_val_t cmd_hdcp_write_val; + /* Get aksv */ + struct cmd_hdcp_get_aksv_t cmd_hdcp_get_aksv; + /* Calculate r0 */ + struct cmd_hdcp_calculate_lm_t cmd_hdcp_calculate_lm; + /* Generate signature for certificate */ + struct cmd_hdcp_ake_certificate_t cmd_hdcp_ake_certificate; + /* To store ekm */ + struct cmd_hdcp_ake_paring_t cmd_hdcp_ake_paring; + /* Encrypt km for V2.2 */ + struct cmd_hdcp_enc_km_t cmd_hdcp_enc_km; + /* Compute H prime */ + struct cmd_hdcp_ake_h_prime_t cmd_hdcp_ake_h_prime; + /* Compute L prime */ + struct cmd_hdcp_lc_l_prime_t cmd_hdcp_lc_l_prime; + /* Compute eks */ + struct cmd_hdcp_ske_eks_t cmd_hdcp_ske_eks; + /* Compare */ + struct cmd_hdcp_compare_t cmd_hdcp_compare; +} __packed; + +struct tci_t { + u32 command_id; + u32 return_code; + union tci_cmd_body_t cmd_body; +}; + +#endif /* _TCI_H_ */ diff --git a/drivers/gpu/drm/mediatek/tlc_dp_hdcp.c b/drivers/gpu/drm/mediatek/tlc_dp_hdcp.c new file mode 100644 index 000000000000..8ae0548ae022 --- /dev/null +++ b/drivers/gpu/drm/mediatek/tlc_dp_hdcp.c @@ -0,0 +1,595 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019-2024 MediaTek Inc. + */ + +#include <linux/types.h> +#include <linux/string.h> +#include <linux/slab.h> +#include "tlc_dp_hdcp.h" + +#define DEFAULT_WRITE_VAL_LEN 1 +#define DEFAULT_WRITE_VAL 0 + +/* + * TA_FTPM_UUID: 99975014-3c7c-54ea-8487-a80d215ea92c + * + * Randomly generated, and must correspond to the GUID on the TA side. + * Defined here in the reference implementation: + * https://github.com/microsoft/ms-tpm-20-ref/blob/master/Samples/ARM32-FirmwareTPM/optee_ta/fTPM/include/fTPM.h#L42 + */ +static const uuid_t dp_ta_uuid = + UUID_INIT(0x99975014, 0x3c7c, 0x54ea, + 0x84, 0x87, 0xa8, 0x0d, 0x21, 0x5e, 0xa9, 0x2c); + +/* Send dp commands through the TEE shared memory */ +static int dp_tee_op_send(struct dp_tee_private *dp_tee_priv, size_t len, u32 cmd_id) +{ + struct tee_ioctl_invoke_arg transceive_args; + struct tee_shm *shm = dp_tee_priv->shm; + struct tee_param command_params[4]; + u8 *temp_buf; + int rc; + + if (len > MAX_COMMAND_SIZE) { + pr_err("[TLC_HDCP] Len %zd exceeds MAX_COMMAND_SIZE supported by dp TA\n", len); + return -EINVAL; + } + + memset(&transceive_args, 0, sizeof(transceive_args)); + memset(command_params, 0, sizeof(command_params)); + dp_tee_priv->resp_len = 0; + + /* Invoke FTPM_OPTEE_TA_SUBMIT_COMMAND function of dp TA */ + transceive_args = (struct tee_ioctl_invoke_arg) { + .func = cmd_id, + .session = dp_tee_priv->session, + .num_params = 4, + }; + + /* Fill FTPM_OPTEE_TA_SUBMIT_COMMAND parameters */ + command_params[0] = (struct tee_param) { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT, + .u.memref = { + .shm = shm, + .size = len, + .shm_offs = 0, + }, + }; + + command_params[1] = (struct tee_param) { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT, + .u.memref = { + .shm = shm, + .size = MAX_RESPONSE_SIZE, + .shm_offs = MAX_COMMAND_SIZE, + }, + }; + + rc = tee_client_invoke_func(dp_tee_priv->ctx, &transceive_args, command_params); + if (rc < 0 || transceive_args.ret != 0) { + pr_err("[TLC_HDCP] rc:%d, invoke error:0x%x\n", rc, transceive_args.ret); + return -EBUSY; + } + + temp_buf = tee_shm_get_va(shm, command_params[1].u.memref.shm_offs); + if (IS_ERR(temp_buf)) { + pr_err("[TLC_HDCP] tee_shm_get_va failed for receive\n"); + return -ENOMEM; + } + + /* Sanity checks look good, cache the response */ + memcpy(dp_tee_priv->resp_buf, temp_buf, MAX_RESPONSE_SIZE / 2); + dp_tee_priv->resp_len = MAX_RESPONSE_SIZE / 2; + + return 0; +} + +/* + * Check whether this driver supports the dp TA in the TEE instance + * represented by the params (ver/data) to this function. + * + * Note that currently this driver only supports GP Compliant OPTEE + * based dp TA. + */ +static int dp_tee_match(struct tee_ioctl_version_data *ver, const void *data) +{ + return ver->impl_id == TEE_IMPL_ID_OPTEE && + ver->gen_caps & TEE_GEN_CAP_GP; +} + +int tee_add_device(struct mtk_hdcp_info *hdcp_info, u32 version) +{ + struct tee_ioctl_open_session_arg sess_arg; + struct dp_tee_private *dp_tee_priv; + struct tci_t *tci; + int rc; + + if (hdcp_info->g_init) + tee_remove_device(hdcp_info); + + dp_tee_priv = kzalloc(sizeof(*dp_tee_priv), GFP_KERNEL); + if (!dp_tee_priv) + return -ENOMEM; + + hdcp_info->g_dp_tee_priv = dp_tee_priv; + + /* Open context with TEE driver */ + dp_tee_priv->ctx = tee_client_open_context(NULL, dp_tee_match, NULL, NULL); + if (IS_ERR(dp_tee_priv->ctx)) { + pr_err("[TLC_HDCP] tee_client_open_context failed\n"); + rc = PTR_ERR(dp_tee_priv->ctx); + goto free_priv; + } + + /* Open a session with dp TA */ + memset(&sess_arg, 0, sizeof(sess_arg)); + export_uuid(sess_arg.uuid, &dp_ta_uuid); + sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; + sess_arg.num_params = 0; + + rc = tee_client_open_session(dp_tee_priv->ctx, &sess_arg, NULL); + if (rc < 0 || sess_arg.ret != 0) { + pr_err("[TLC_HDCP] tee_client_open_session failed, err:%x\n", sess_arg.ret); + rc = -EINVAL; + goto out_tee_session; + } + dp_tee_priv->session = sess_arg.session; + + /* Allocate dynamic shared memory with dp TA */ + dp_tee_priv->shm = tee_shm_alloc_kernel_buf(dp_tee_priv->ctx, MAX_COMMAND_SIZE + + MAX_RESPONSE_SIZE); + if (IS_ERR(dp_tee_priv->shm)) { + pr_err("[TLC_HDCP] tee_shm_alloc_kernel_buf failed\n"); + rc = -ENOMEM; + goto out_shm_alloc; + } + + /* Copy parameter for add new device */ + tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_DEVICE_ADDED; + tci->cmd_body.cmd_hdcp_init_for_verion.version = version; + tci->cmd_body.cmd_hdcp_init_for_verion.need_load_key = true; + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_DEVICE_ADDED); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error:%x\n", rc); + tee_remove_device(hdcp_info); + return rc; + } + + hdcp_info->g_init = true; + + return 0; + +out_shm_alloc: + tee_client_close_session(dp_tee_priv->ctx, dp_tee_priv->session); +out_tee_session: + tee_client_close_context(dp_tee_priv->ctx); +free_priv: + kfree(dp_tee_priv); + + return rc; +} + +void tee_remove_device(struct mtk_hdcp_info *hdcp_info) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + if (!hdcp_info->g_init) + return; + + hdcp_info->g_init = false; + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_DEVICE_REMOVE; + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_DEVICE_REMOVE); + if (rc) + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + + /* Free the shared memory pool */ + tee_shm_free(dp_tee_priv->shm); + + /* Close the existing session with fTPM TA */ + tee_client_close_session(dp_tee_priv->ctx, dp_tee_priv->session); + + /* Close the context with TEE driver */ + tee_client_close_context(dp_tee_priv->ctx); + + /* Free the memory */ + kfree(dp_tee_priv); +} + +int tee_clear_paring(struct mtk_hdcp_info *hdcp_info) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_DEVICE_CLEAN; + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_DEVICE_CLEAN); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + return 0; +} + +/* Write An to HW */ +int tee_hdcp1x_set_tx_an(struct mtk_hdcp_info *hdcp_info, u8 *an_code) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_WRITE_VAL; + tci->cmd_body.cmd_hdcp_write_val.len = DRM_HDCP_AN_LEN; + tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_PARAM_AN; + memcpy(tci->cmd_body.cmd_hdcp_write_val.val, an_code, DRM_HDCP_AN_LEN); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_WRITE_VAL); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + return 0; +} + +int tee_hdcp_enable_encrypt(struct mtk_hdcp_info *hdcp_info, bool enable, u8 version) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_ENABLE_ENCRYPT; + tci->cmd_body.cmd_hdcp_write_val.type = enable ? TYPE_HDCP_ENABLE_ENCRYPT : + TYPE_HDCP_DISABLE_ENCRYPT; + + /* Set HDCP version supportted by device */ + tci->cmd_body.cmd_hdcp_write_val.len = 1; + tci->cmd_body.cmd_hdcp_write_val.val[0] = version; + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_ENABLE_ENCRYPT); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + return 0; +} + +int tee_hdcp1x_soft_rst(struct mtk_hdcp_info *hdcp_info) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_WRITE_VAL; + tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_PARAM_RST_1; + /* No need input. Set default value 0 for check */ + tci->cmd_body.cmd_hdcp_write_val.len = DEFAULT_WRITE_VAL_LEN; + memset(tci->cmd_body.cmd_hdcp_write_val.val, DEFAULT_WRITE_VAL, DEFAULT_WRITE_VAL_LEN); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_WRITE_VAL); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + return 0; +} + +int tee_hdcp2_soft_rst(struct mtk_hdcp_info *hdcp_info) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + tci->command_id = CMD_WRITE_VAL; + tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_PARAM_RST_2; + /* No need input. Set default value 0 for check */ + tci->cmd_body.cmd_hdcp_write_val.len = DEFAULT_WRITE_VAL_LEN; + memset(tci->cmd_body.cmd_hdcp_write_val.val, DEFAULT_WRITE_VAL, DEFAULT_WRITE_VAL_LEN); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_WRITE_VAL); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + return 0; +} + +/** V1.X **/ +int tee_get_aksv(struct mtk_hdcp_info *hdcp_info, u8 *aksv) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + struct tci_t *tci_resp; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_GET_AKSV; + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_GET_AKSV); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + tci_resp = (struct tci_t *)dp_tee_priv->resp_buf; + memcpy(aksv, tci_resp->cmd_body.cmd_hdcp_get_aksv.aksv, DRM_HDCP_KSV_LEN); + + return 0; +} + +/* Calculate Km base on Bksv and write it to HW */ +int tee_calculate_lm(struct mtk_hdcp_info *hdcp_info, u8 *bksv) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_CALCULATE_LM; + memcpy(tci->cmd_body.cmd_hdcp_calculate_lm.bksv, bksv, DRM_HDCP_KSV_LEN); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_CALCULATE_LM); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + return 0; +} + +/* Get r0 from HW and compare to rx_r0 */ +int tee_compare_r0(struct mtk_hdcp_info *hdcp_info, u8 *r0, u32 len) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_COMPARE_R0; + tci->cmd_body.cmd_hdcp_compare.rx_val_len = len; + memcpy(tci->cmd_body.cmd_hdcp_compare.rx_val, r0, len); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_COMPARE_R0); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + return 0; +} + +/* Compute and compare v value */ +int tee_hdcp1x_compute_compare_v(struct mtk_hdcp_info *hdcp_info, + u8 *crypto_param, u32 param_len, u8 *rx_v) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_COMPARE_V1; + tci->cmd_body.cmd_hdcp_compare.rx_val_len = 20; + tci->cmd_body.cmd_hdcp_compare.param_len = param_len; + memcpy(tci->cmd_body.cmd_hdcp_compare.rx_val, rx_v, 20); + memcpy(tci->cmd_body.cmd_hdcp_compare.param, crypto_param, param_len); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_COMPARE_V1); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + return 0; +} + +/** V2.X **/ +int tee_ake_certificate(struct mtk_hdcp_info *hdcp_info, + u8 *certificate, bool *stored, u8 *out_m, u8 *out_ekm) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + struct tci_t *tci_resp; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_AKE_CERTIFICATE; + memcpy(tci->cmd_body.cmd_hdcp_ake_certificate.certification, + certificate, HDCP2_CERTRX_LEN); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_AKE_CERTIFICATE); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + pr_info("[TLC_HDCP] verify signature: result %d\n", rc); + tci_resp = (struct tci_t *)dp_tee_priv->resp_buf; + *stored = tci_resp->cmd_body.cmd_hdcp_ake_certificate.stored; + memcpy(out_m, tci_resp->cmd_body.cmd_hdcp_ake_certificate.m, + HDCP_2_2_E_KH_KM_M_LEN - HDCP_2_2_E_KH_KM_LEN); + memcpy(out_ekm, tci_resp->cmd_body.cmd_hdcp_ake_certificate.ekm, HDCP_2_2_E_KH_KM_LEN); + + return 0; +} + +/* Encrypt km */ +int tee_enc_rsaes_oaep(struct mtk_hdcp_info *hdcp_info, u8 *ekm) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + struct tci_t *tci_resp; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_ENC_KM; + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_ENC_KM); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + tci_resp = (struct tci_t *)dp_tee_priv->resp_buf; + memcpy(ekm, tci_resp->cmd_body.cmd_hdcp_enc_km.enc_km, HDCP_2_2_E_KPUB_KM_LEN); + + return 0; +} + +/* Calculate h prime and compare to rx_h */ +int tee_ake_h_prime(struct mtk_hdcp_info *hdcp_info, + u8 *rtx, u8 *rrx, u8 *rx_caps, u8 *tx_caps, u8 *rx_h, u32 rx_h_len) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + struct tci_t *tci_resp; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_AKE_H_PRIME; + tci->cmd_body.cmd_hdcp_ake_h_prime.rx_h_len = rx_h_len; + + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rtx, rtx, HDCP_2_2_RTX_LEN); + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rrx, rrx, HDCP_2_2_RRX_LEN); + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rx_caps, rx_caps, HDCP_2_2_RXCAPS_LEN); + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.tx_caps, tx_caps, HDCP_2_2_TXCAPS_LEN); + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rx_h, rx_h, rx_h_len); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_AKE_H_PRIME); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + tci_resp = (struct tci_t *)dp_tee_priv->resp_buf; + + return tci_resp->return_code; +} + +/* Store paring info */ +int tee_ake_paring(struct mtk_hdcp_info *hdcp_info, u8 *rx_ekm) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_AKE_PARING; + memcpy(tci->cmd_body.cmd_hdcp_ake_paring.ekm, rx_ekm, HDCP_2_2_E_KH_KM_LEN); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_AKE_PARING); + if (rc) + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + + return 0; +} + +/* Calculate l prime and compare */ +int tee_lc_l_prime(struct mtk_hdcp_info *hdcp_info, u8 *rn, u8 *rx_l, u32 len) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + struct tci_t *tci_resp; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_LC_L_PRIME; + memcpy(tci->cmd_body.cmd_hdcp_lc_l_prime.rn, rn, HDCP_2_2_RN_LEN); + tci->cmd_body.cmd_hdcp_lc_l_prime.rx_l_len = len; + memcpy(tci->cmd_body.cmd_hdcp_lc_l_prime.rx_l, rx_l, len); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_LC_L_PRIME); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + tci_resp = (struct tci_t *)dp_tee_priv->resp_buf; + + return tci_resp->return_code; +} + +int tee_ske_enc_ks(struct mtk_hdcp_info *hdcp_info, u8 *riv, u8 *eks) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + u8 *share_buffer = NULL; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_SKE_CAL_EKS; + memcpy(tci->cmd_body.cmd_hdcp_ske_eks.riv, riv, HDCP_2_2_RIV_LEN); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH + 16, CMD_SKE_CAL_EKS); + if (rc) + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + + share_buffer = (u8 *)dp_tee_priv->resp_buf; + memcpy(eks, share_buffer + TCI_LENGTH, 16); + + return 0; +} + +/* Calculate and compare v prime for repeater */ +int tee_hdcp2_compute_compare_v(struct mtk_hdcp_info *hdcp_info, + u8 *crypto_param, u32 param_len, u8 *rx_v, u8 *tx_v) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + u8 *share_buffer = NULL; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_COMPARE_V2; + tci->cmd_body.cmd_hdcp_compare.rx_val_len = 16; + tci->cmd_body.cmd_hdcp_compare.param_len = param_len; + memcpy(tci->cmd_body.cmd_hdcp_compare.rx_val, rx_v, 16); + memcpy(tci->cmd_body.cmd_hdcp_compare.param, crypto_param, param_len); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_COMPARE_V2); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + share_buffer = (u8 *)dp_tee_priv->resp_buf; + memcpy(tx_v, share_buffer + TCI_LENGTH, 16); + + return 0; +} + +/* Calculate and compare m prime for repeater */ +int tee_hdcp2_compute_compare_m(struct mtk_hdcp_info *hdcp_info, + u8 *crypto_param, u32 param_len, u8 *rx_m) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_COMPARE_M; + tci->cmd_body.cmd_hdcp_compare.rx_val_len = HDCP_2_2_MPRIME_LEN; + tci->cmd_body.cmd_hdcp_compare.param_len = param_len; + memcpy(tci->cmd_body.cmd_hdcp_compare.rx_val, rx_m, HDCP_2_2_MPRIME_LEN); + memcpy(tci->cmd_body.cmd_hdcp_compare.param, crypto_param, param_len); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_COMPARE_M); + if (rc) + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + + return 0; +} diff --git a/drivers/gpu/drm/mediatek/tlc_dp_hdcp.h b/drivers/gpu/drm/mediatek/tlc_dp_hdcp.h new file mode 100644 index 000000000000..871d3fec5835 --- /dev/null +++ b/drivers/gpu/drm/mediatek/tlc_dp_hdcp.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019-2024 MediaTek Inc. + */ + +#ifndef _TLC_DP_HDCP_H_ +#define _TLC_DP_HDCP_H_ + +#include <linux/printk.h> +#include <linux/tee_drv.h> +#include <linux/uuid.h> +#include <linux/types.h> +#include <linux/sched/clock.h> +#include <drm/display/drm_dp_helper.h> +#include "tci.h" + +/* HDCP version definitions */ +#define HDCP_NONE 0x0 /* No HDCP supported, no secure data path */ +#define HDCP_V1 0x1 /* HDCP version 1.0 */ +#define HDCP_V2 0x2 /* HDCP version 2.0 Type 1 */ +#define HDCP_V2_3 0x5 /* HDCP version 2.3 Type 1 */ + +/* Max buffer size supported by dp */ +#define MAX_COMMAND_SIZE 4096 +#define MAX_RESPONSE_SIZE MAX_COMMAND_SIZE + +#define HDCP1X_REP_MAXDEVS 128 +#define HDCP1X_V_LEN 20 +#define HDCP1X_B_INFO_LEN 2 + +#define HDCP2_K_LEN 2 +#define HDCP2_STREAMID_TYPE_LEN 2 + +enum hdcp_result { + AUTH_ZERO = 0, + AUTH_INIT = 2, + AUTH_PASS = 3, + AUTH_FAIL = 4, +}; + +struct dp_tee_private { + u32 session; + size_t resp_len; + u8 resp_buf[MAX_RESPONSE_SIZE]; + struct tee_context *ctx; + struct tee_shm *shm; +}; + +struct hdcp2_info_tx { + struct hdcp2_ake_init ake_init; + struct hdcp2_ake_no_stored_km no_stored_km; + struct hdcp2_ske_send_eks send_eks; + struct hdcp2_lc_init lc_init; + struct hdcp2_rep_stream_manage stream_manage; + struct hdcp2_rep_send_ack send_ack; + struct hdcp2_tx_caps tx_caps; + u8 k[HDCP2_K_LEN]; + u8 stream_id_type[HDCP2_STREAMID_TYPE_LEN]; +}; + +struct hdcp2_info_rx { + struct hdcp2_cert_rx cert_rx; + struct hdcp2_ake_send_cert send_cert; + struct hdcp2_rep_send_receiverid_list recvid_list; + struct hdcp2_ake_send_pairing_info pairing_info; + struct hdcp2_rep_stream_ready stream_ready; + struct hdcp2_ake_send_hprime send_hprime; + struct hdcp2_lc_send_lprime send_lprime; +}; + +struct hdcp1x_info { + bool capable; + bool repeater; + u8 device_count; + u8 v[HDCP1X_V_LEN]; + u8 b_ksv[DRM_HDCP_KSV_LEN]; + u8 a_ksv[DRM_HDCP_KSV_LEN]; + u8 b_info[HDCP1X_B_INFO_LEN]; + u8 ksvfifo[DRM_HDCP_KSV_LEN * (HDCP1X_REP_MAXDEVS - 1)]; +}; + +struct hdcp2_info { + bool capable; + bool repeater; + bool stored_km; + u8 device_count; + u8 stream_id_type; + u32 seq_num_v_cnt; + atomic_t cp_irq; + int cp_irq_cached; + wait_queue_head_t cp_irq_queue; + struct hdcp2_info_tx hdcp_tx; + struct hdcp2_info_rx hdcp_rx; + struct hdcp2_ake_stored_km ake_stored_km; +}; + +struct mtk_hdcp_info { + bool g_init; + u8 auth_status; + u8 auth_version; + u32 hdcp_content_type; + u32 content_protection; + struct hdcp2_info hdcp2_info; + struct hdcp1x_info hdcp1x_info; + struct dp_tee_private *g_dp_tee_priv; +}; + +int tee_add_device(struct mtk_hdcp_info *hdcp_info, u32 version); +void tee_remove_device(struct mtk_hdcp_info *hdcp_info); +int tee_clear_paring(struct mtk_hdcp_info *hdcp_info); +int tee_calculate_lm(struct mtk_hdcp_info *hdcp_info, u8 *bksv); +int tee_get_aksv(struct mtk_hdcp_info *hdcp_info, u8 *aksv); +int tee_compare_r0(struct mtk_hdcp_info *hdcp_info, u8 *r0, u32 len); +int tee_hdcp1x_compute_compare_v(struct mtk_hdcp_info *hdcp_info, + u8 *crypto_param, u32 param_len, u8 *rx_v); +int tee_hdcp1x_set_tx_an(struct mtk_hdcp_info *hdcp_info, u8 *an_code); +int tee_hdcp1x_soft_rst(struct mtk_hdcp_info *hdcp_info); +int tee_hdcp2_soft_rst(struct mtk_hdcp_info *hdcp_info); +int tee_hdcp_enable_encrypt(struct mtk_hdcp_info *hdcp_info, bool enable, u8 version); +int tee_ake_certificate(struct mtk_hdcp_info *hdcp_info, + u8 *certificate, bool *stored, u8 *out_m, u8 *out_ekm); +int tee_enc_rsaes_oaep(struct mtk_hdcp_info *hdcp_info, u8 *ekm); +int tee_ake_h_prime(struct mtk_hdcp_info *hdcp_info, + u8 *rtx, u8 *rrx, u8 *rx_caps, u8 *tx_caps, u8 *rx_h, u32 rx_h_len); +int tee_ake_paring(struct mtk_hdcp_info *hdcp_info, u8 *rx_ekm); +int tee_lc_l_prime(struct mtk_hdcp_info *hdcp_info, u8 *rn, u8 *rx_l, u32 len); + +int tee_ske_enc_ks(struct mtk_hdcp_info *hdcp_info, u8 *riv, u8 *eks); +int tee_hdcp2_compute_compare_v(struct mtk_hdcp_info *hdcp_info, + u8 *crypto_param, u32 param_len, u8 *rx_v, u8 *tx_v); +int tee_hdcp2_compute_compare_m(struct mtk_hdcp_info *hdcp_info, + u8 *crypto_param, u32 param_len, u8 *rx_m); +#endif /* _TLC_DP_HDCP_H_ */ -- 2.43.0 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH v3 1/3] Subject: [PATCH] drm/mediatek/dp: Add tee client application for HDCP feature @ 2024-06-08 12:01 ` mac.shen 0 siblings, 0 replies; 42+ messages in thread From: mac.shen @ 2024-06-08 12:01 UTC (permalink / raw) To: chunkuang.hu, p.zabel, airlied, daniel, matthias.bgg, angelogioacchino.delregno, jitao.shi Cc: mac.shen, shuijing.li, linux-kernel, dri-devel, linux-mediatek, linux-arm-kernel Add tee client application which will be used for HDCP 1.x and 2.x authentication in DisplayPort. Changes in v3: - remove useless define - refine the return value - remove the define which control the log output - remove the struct hdcp2_handler for HDCP 2.X - refine the struct for the HDCP information per suggestion from the previous thread: https://patchwork.kernel.org/project/linux-mediatek /patch/20240205055055.25340-2-mac.shen@mediatek.com/ Signed-off-by: mac.shen <mac.shen@mediatek.com> --- drivers/gpu/drm/mediatek/Makefile | 5 +- drivers/gpu/drm/mediatek/tci.h | 143 ++++++ drivers/gpu/drm/mediatek/tlc_dp_hdcp.c | 595 +++++++++++++++++++++++++ drivers/gpu/drm/mediatek/tlc_dp_hdcp.h | 133 ++++++ 4 files changed, 875 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/mediatek/tci.h create mode 100644 drivers/gpu/drm/mediatek/tlc_dp_hdcp.c create mode 100644 drivers/gpu/drm/mediatek/tlc_dp_hdcp.h diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile index 32a2ed6c0cfe..f27ddfbd2e12 100644 --- a/drivers/gpu/drm/mediatek/Makefile +++ b/drivers/gpu/drm/mediatek/Makefile @@ -27,4 +27,7 @@ mediatek-drm-hdmi-objs := mtk_cec.o \ obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o -obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o +mtk-dp-objs := tlc_dp_hdcp.o \ + mtk_dp.o + +obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk-dp.o diff --git a/drivers/gpu/drm/mediatek/tci.h b/drivers/gpu/drm/mediatek/tci.h new file mode 100644 index 000000000000..48710ce5d391 --- /dev/null +++ b/drivers/gpu/drm/mediatek/tci.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019-2024 MediaTek Inc. + */ + +#ifndef _TCI_H_ +#define _TCI_H_ + +#include <drm/display/drm_hdcp.h> + +#define CMD_DEVICE_ADDED 1 +#define CMD_DEVICE_REMOVE 2 +#define CMD_WRITE_VAL 3 +#define CMD_DEVICE_CLEAN 4 +#define CMD_ENABLE_ENCRYPT 5 + +/* V1.x */ +#define CMD_CALCULATE_LM 11 +#define CMD_COMPARE_R0 12 +#define CMD_COMPARE_V1 13 +#define CMD_GET_AKSV 14 + +/* V2.x */ +#define CMD_AKE_CERTIFICATE 20 +#define CMD_ENC_KM 21 +#define CMD_AKE_H_PRIME 22 +#define CMD_AKE_PARING 23 +#define CMD_LC_L_PRIME 24 +#define CMD_SKE_CAL_EKS 26 +#define CMD_COMPARE_V2 27 +#define CMD_COMPARE_M 28 + +#define TYPE_HDCP_PARAM_AN 10 +#define TYPE_HDCP_PARAM_RST_1 11 +#define TYPE_HDCP_PARAM_RST_2 12 +#define TYPE_HDCP_ENABLE_ENCRYPT 13 +#define TYPE_HDCP_DISABLE_ENCRYPT 14 + +/* reserved:2 */ +#define HDCP2_CERTRX_LEN (HDCP_2_2_RECEIVER_ID_LEN + HDCP_2_2_K_PUB_RX_LEN + \ + 2 + HDCP_2_2_DCP_LLC_SIG_LEN) +/* version:1 */ +#define HDCP_2_2_TXCAPS_LEN (HDCP_2_2_TXCAP_MASK_LEN + 1) +#define PARAM_LEN 1024 + +#define TCI_LENGTH sizeof(struct tci_t) + +struct cmd_hdcp_init_for_verion_t { + u32 version; + bool need_load_key; +}; + +struct cmd_hdcp_write_val_t { + u8 type; + u8 len; + u8 val[DRM_HDCP_AN_LEN]; +}; + +struct cmd_hdcp_calculate_lm_t { + u8 bksv[DRM_HDCP_KSV_LEN]; +}; + +struct cmd_hdcp_get_aksv_t { + u8 aksv[DRM_HDCP_KSV_LEN]; +}; + +struct cmd_hdcp_ake_certificate_t { + u8 certification[HDCP2_CERTRX_LEN]; + bool stored; + u8 m[HDCP_2_2_E_KH_KM_M_LEN - HDCP_2_2_E_KH_KM_LEN]; + u8 ekm[HDCP_2_2_E_KH_KM_LEN]; +}; + +struct cmd_hdcp_ake_paring_t { + u8 ekm[HDCP_2_2_E_KH_KM_LEN]; +}; + +struct cmd_hdcp_enc_km_t { + u8 enc_km[HDCP_2_2_E_KPUB_KM_LEN]; +}; + +struct cmd_hdcp_ake_h_prime_t { + u8 rtx[HDCP_2_2_RTX_LEN]; + u8 rrx[HDCP_2_2_RRX_LEN]; + u8 rx_caps[HDCP_2_2_RXCAPS_LEN]; + u8 tx_caps[HDCP_2_2_TXCAPS_LEN]; + u32 rx_h_len; + u8 rx_h[HDCP_2_2_H_PRIME_LEN]; +}; + +struct cmd_hdcp_lc_l_prime_t { + u8 rn[HDCP_2_2_RN_LEN]; + u32 rx_l_len; + u8 rx_l[HDCP_2_2_L_PRIME_LEN]; +}; + +struct cmd_hdcp_ske_eks_t { + u8 riv[HDCP_2_2_RIV_LEN]; + u32 eks_len; + u32 eks; +}; + +struct cmd_hdcp_compare_t { + u32 rx_val_len; + u8 rx_val[HDCP_2_2_MPRIME_LEN]; + u32 param_len; + u8 param[PARAM_LEN]; + u32 out_len; + u32 out; +}; + +union tci_cmd_body_t { + /* Init with special HDCP version */ + struct cmd_hdcp_init_for_verion_t cmd_hdcp_init_for_verion; + /* Write uint32 data to hw */ + struct cmd_hdcp_write_val_t cmd_hdcp_write_val; + /* Get aksv */ + struct cmd_hdcp_get_aksv_t cmd_hdcp_get_aksv; + /* Calculate r0 */ + struct cmd_hdcp_calculate_lm_t cmd_hdcp_calculate_lm; + /* Generate signature for certificate */ + struct cmd_hdcp_ake_certificate_t cmd_hdcp_ake_certificate; + /* To store ekm */ + struct cmd_hdcp_ake_paring_t cmd_hdcp_ake_paring; + /* Encrypt km for V2.2 */ + struct cmd_hdcp_enc_km_t cmd_hdcp_enc_km; + /* Compute H prime */ + struct cmd_hdcp_ake_h_prime_t cmd_hdcp_ake_h_prime; + /* Compute L prime */ + struct cmd_hdcp_lc_l_prime_t cmd_hdcp_lc_l_prime; + /* Compute eks */ + struct cmd_hdcp_ske_eks_t cmd_hdcp_ske_eks; + /* Compare */ + struct cmd_hdcp_compare_t cmd_hdcp_compare; +} __packed; + +struct tci_t { + u32 command_id; + u32 return_code; + union tci_cmd_body_t cmd_body; +}; + +#endif /* _TCI_H_ */ diff --git a/drivers/gpu/drm/mediatek/tlc_dp_hdcp.c b/drivers/gpu/drm/mediatek/tlc_dp_hdcp.c new file mode 100644 index 000000000000..8ae0548ae022 --- /dev/null +++ b/drivers/gpu/drm/mediatek/tlc_dp_hdcp.c @@ -0,0 +1,595 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019-2024 MediaTek Inc. + */ + +#include <linux/types.h> +#include <linux/string.h> +#include <linux/slab.h> +#include "tlc_dp_hdcp.h" + +#define DEFAULT_WRITE_VAL_LEN 1 +#define DEFAULT_WRITE_VAL 0 + +/* + * TA_FTPM_UUID: 99975014-3c7c-54ea-8487-a80d215ea92c + * + * Randomly generated, and must correspond to the GUID on the TA side. + * Defined here in the reference implementation: + * https://github.com/microsoft/ms-tpm-20-ref/blob/master/Samples/ARM32-FirmwareTPM/optee_ta/fTPM/include/fTPM.h#L42 + */ +static const uuid_t dp_ta_uuid = + UUID_INIT(0x99975014, 0x3c7c, 0x54ea, + 0x84, 0x87, 0xa8, 0x0d, 0x21, 0x5e, 0xa9, 0x2c); + +/* Send dp commands through the TEE shared memory */ +static int dp_tee_op_send(struct dp_tee_private *dp_tee_priv, size_t len, u32 cmd_id) +{ + struct tee_ioctl_invoke_arg transceive_args; + struct tee_shm *shm = dp_tee_priv->shm; + struct tee_param command_params[4]; + u8 *temp_buf; + int rc; + + if (len > MAX_COMMAND_SIZE) { + pr_err("[TLC_HDCP] Len %zd exceeds MAX_COMMAND_SIZE supported by dp TA\n", len); + return -EINVAL; + } + + memset(&transceive_args, 0, sizeof(transceive_args)); + memset(command_params, 0, sizeof(command_params)); + dp_tee_priv->resp_len = 0; + + /* Invoke FTPM_OPTEE_TA_SUBMIT_COMMAND function of dp TA */ + transceive_args = (struct tee_ioctl_invoke_arg) { + .func = cmd_id, + .session = dp_tee_priv->session, + .num_params = 4, + }; + + /* Fill FTPM_OPTEE_TA_SUBMIT_COMMAND parameters */ + command_params[0] = (struct tee_param) { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT, + .u.memref = { + .shm = shm, + .size = len, + .shm_offs = 0, + }, + }; + + command_params[1] = (struct tee_param) { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT, + .u.memref = { + .shm = shm, + .size = MAX_RESPONSE_SIZE, + .shm_offs = MAX_COMMAND_SIZE, + }, + }; + + rc = tee_client_invoke_func(dp_tee_priv->ctx, &transceive_args, command_params); + if (rc < 0 || transceive_args.ret != 0) { + pr_err("[TLC_HDCP] rc:%d, invoke error:0x%x\n", rc, transceive_args.ret); + return -EBUSY; + } + + temp_buf = tee_shm_get_va(shm, command_params[1].u.memref.shm_offs); + if (IS_ERR(temp_buf)) { + pr_err("[TLC_HDCP] tee_shm_get_va failed for receive\n"); + return -ENOMEM; + } + + /* Sanity checks look good, cache the response */ + memcpy(dp_tee_priv->resp_buf, temp_buf, MAX_RESPONSE_SIZE / 2); + dp_tee_priv->resp_len = MAX_RESPONSE_SIZE / 2; + + return 0; +} + +/* + * Check whether this driver supports the dp TA in the TEE instance + * represented by the params (ver/data) to this function. + * + * Note that currently this driver only supports GP Compliant OPTEE + * based dp TA. + */ +static int dp_tee_match(struct tee_ioctl_version_data *ver, const void *data) +{ + return ver->impl_id == TEE_IMPL_ID_OPTEE && + ver->gen_caps & TEE_GEN_CAP_GP; +} + +int tee_add_device(struct mtk_hdcp_info *hdcp_info, u32 version) +{ + struct tee_ioctl_open_session_arg sess_arg; + struct dp_tee_private *dp_tee_priv; + struct tci_t *tci; + int rc; + + if (hdcp_info->g_init) + tee_remove_device(hdcp_info); + + dp_tee_priv = kzalloc(sizeof(*dp_tee_priv), GFP_KERNEL); + if (!dp_tee_priv) + return -ENOMEM; + + hdcp_info->g_dp_tee_priv = dp_tee_priv; + + /* Open context with TEE driver */ + dp_tee_priv->ctx = tee_client_open_context(NULL, dp_tee_match, NULL, NULL); + if (IS_ERR(dp_tee_priv->ctx)) { + pr_err("[TLC_HDCP] tee_client_open_context failed\n"); + rc = PTR_ERR(dp_tee_priv->ctx); + goto free_priv; + } + + /* Open a session with dp TA */ + memset(&sess_arg, 0, sizeof(sess_arg)); + export_uuid(sess_arg.uuid, &dp_ta_uuid); + sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; + sess_arg.num_params = 0; + + rc = tee_client_open_session(dp_tee_priv->ctx, &sess_arg, NULL); + if (rc < 0 || sess_arg.ret != 0) { + pr_err("[TLC_HDCP] tee_client_open_session failed, err:%x\n", sess_arg.ret); + rc = -EINVAL; + goto out_tee_session; + } + dp_tee_priv->session = sess_arg.session; + + /* Allocate dynamic shared memory with dp TA */ + dp_tee_priv->shm = tee_shm_alloc_kernel_buf(dp_tee_priv->ctx, MAX_COMMAND_SIZE + + MAX_RESPONSE_SIZE); + if (IS_ERR(dp_tee_priv->shm)) { + pr_err("[TLC_HDCP] tee_shm_alloc_kernel_buf failed\n"); + rc = -ENOMEM; + goto out_shm_alloc; + } + + /* Copy parameter for add new device */ + tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_DEVICE_ADDED; + tci->cmd_body.cmd_hdcp_init_for_verion.version = version; + tci->cmd_body.cmd_hdcp_init_for_verion.need_load_key = true; + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_DEVICE_ADDED); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error:%x\n", rc); + tee_remove_device(hdcp_info); + return rc; + } + + hdcp_info->g_init = true; + + return 0; + +out_shm_alloc: + tee_client_close_session(dp_tee_priv->ctx, dp_tee_priv->session); +out_tee_session: + tee_client_close_context(dp_tee_priv->ctx); +free_priv: + kfree(dp_tee_priv); + + return rc; +} + +void tee_remove_device(struct mtk_hdcp_info *hdcp_info) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + if (!hdcp_info->g_init) + return; + + hdcp_info->g_init = false; + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_DEVICE_REMOVE; + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_DEVICE_REMOVE); + if (rc) + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + + /* Free the shared memory pool */ + tee_shm_free(dp_tee_priv->shm); + + /* Close the existing session with fTPM TA */ + tee_client_close_session(dp_tee_priv->ctx, dp_tee_priv->session); + + /* Close the context with TEE driver */ + tee_client_close_context(dp_tee_priv->ctx); + + /* Free the memory */ + kfree(dp_tee_priv); +} + +int tee_clear_paring(struct mtk_hdcp_info *hdcp_info) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_DEVICE_CLEAN; + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_DEVICE_CLEAN); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + return 0; +} + +/* Write An to HW */ +int tee_hdcp1x_set_tx_an(struct mtk_hdcp_info *hdcp_info, u8 *an_code) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_WRITE_VAL; + tci->cmd_body.cmd_hdcp_write_val.len = DRM_HDCP_AN_LEN; + tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_PARAM_AN; + memcpy(tci->cmd_body.cmd_hdcp_write_val.val, an_code, DRM_HDCP_AN_LEN); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_WRITE_VAL); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + return 0; +} + +int tee_hdcp_enable_encrypt(struct mtk_hdcp_info *hdcp_info, bool enable, u8 version) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_ENABLE_ENCRYPT; + tci->cmd_body.cmd_hdcp_write_val.type = enable ? TYPE_HDCP_ENABLE_ENCRYPT : + TYPE_HDCP_DISABLE_ENCRYPT; + + /* Set HDCP version supportted by device */ + tci->cmd_body.cmd_hdcp_write_val.len = 1; + tci->cmd_body.cmd_hdcp_write_val.val[0] = version; + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_ENABLE_ENCRYPT); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + return 0; +} + +int tee_hdcp1x_soft_rst(struct mtk_hdcp_info *hdcp_info) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_WRITE_VAL; + tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_PARAM_RST_1; + /* No need input. Set default value 0 for check */ + tci->cmd_body.cmd_hdcp_write_val.len = DEFAULT_WRITE_VAL_LEN; + memset(tci->cmd_body.cmd_hdcp_write_val.val, DEFAULT_WRITE_VAL, DEFAULT_WRITE_VAL_LEN); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_WRITE_VAL); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + return 0; +} + +int tee_hdcp2_soft_rst(struct mtk_hdcp_info *hdcp_info) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + tci->command_id = CMD_WRITE_VAL; + tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_PARAM_RST_2; + /* No need input. Set default value 0 for check */ + tci->cmd_body.cmd_hdcp_write_val.len = DEFAULT_WRITE_VAL_LEN; + memset(tci->cmd_body.cmd_hdcp_write_val.val, DEFAULT_WRITE_VAL, DEFAULT_WRITE_VAL_LEN); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_WRITE_VAL); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + return 0; +} + +/** V1.X **/ +int tee_get_aksv(struct mtk_hdcp_info *hdcp_info, u8 *aksv) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + struct tci_t *tci_resp; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_GET_AKSV; + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_GET_AKSV); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + tci_resp = (struct tci_t *)dp_tee_priv->resp_buf; + memcpy(aksv, tci_resp->cmd_body.cmd_hdcp_get_aksv.aksv, DRM_HDCP_KSV_LEN); + + return 0; +} + +/* Calculate Km base on Bksv and write it to HW */ +int tee_calculate_lm(struct mtk_hdcp_info *hdcp_info, u8 *bksv) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_CALCULATE_LM; + memcpy(tci->cmd_body.cmd_hdcp_calculate_lm.bksv, bksv, DRM_HDCP_KSV_LEN); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_CALCULATE_LM); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + return 0; +} + +/* Get r0 from HW and compare to rx_r0 */ +int tee_compare_r0(struct mtk_hdcp_info *hdcp_info, u8 *r0, u32 len) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_COMPARE_R0; + tci->cmd_body.cmd_hdcp_compare.rx_val_len = len; + memcpy(tci->cmd_body.cmd_hdcp_compare.rx_val, r0, len); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_COMPARE_R0); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + return 0; +} + +/* Compute and compare v value */ +int tee_hdcp1x_compute_compare_v(struct mtk_hdcp_info *hdcp_info, + u8 *crypto_param, u32 param_len, u8 *rx_v) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_COMPARE_V1; + tci->cmd_body.cmd_hdcp_compare.rx_val_len = 20; + tci->cmd_body.cmd_hdcp_compare.param_len = param_len; + memcpy(tci->cmd_body.cmd_hdcp_compare.rx_val, rx_v, 20); + memcpy(tci->cmd_body.cmd_hdcp_compare.param, crypto_param, param_len); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_COMPARE_V1); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + return 0; +} + +/** V2.X **/ +int tee_ake_certificate(struct mtk_hdcp_info *hdcp_info, + u8 *certificate, bool *stored, u8 *out_m, u8 *out_ekm) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + struct tci_t *tci_resp; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_AKE_CERTIFICATE; + memcpy(tci->cmd_body.cmd_hdcp_ake_certificate.certification, + certificate, HDCP2_CERTRX_LEN); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_AKE_CERTIFICATE); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + pr_info("[TLC_HDCP] verify signature: result %d\n", rc); + tci_resp = (struct tci_t *)dp_tee_priv->resp_buf; + *stored = tci_resp->cmd_body.cmd_hdcp_ake_certificate.stored; + memcpy(out_m, tci_resp->cmd_body.cmd_hdcp_ake_certificate.m, + HDCP_2_2_E_KH_KM_M_LEN - HDCP_2_2_E_KH_KM_LEN); + memcpy(out_ekm, tci_resp->cmd_body.cmd_hdcp_ake_certificate.ekm, HDCP_2_2_E_KH_KM_LEN); + + return 0; +} + +/* Encrypt km */ +int tee_enc_rsaes_oaep(struct mtk_hdcp_info *hdcp_info, u8 *ekm) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + struct tci_t *tci_resp; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_ENC_KM; + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_ENC_KM); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + tci_resp = (struct tci_t *)dp_tee_priv->resp_buf; + memcpy(ekm, tci_resp->cmd_body.cmd_hdcp_enc_km.enc_km, HDCP_2_2_E_KPUB_KM_LEN); + + return 0; +} + +/* Calculate h prime and compare to rx_h */ +int tee_ake_h_prime(struct mtk_hdcp_info *hdcp_info, + u8 *rtx, u8 *rrx, u8 *rx_caps, u8 *tx_caps, u8 *rx_h, u32 rx_h_len) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + struct tci_t *tci_resp; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_AKE_H_PRIME; + tci->cmd_body.cmd_hdcp_ake_h_prime.rx_h_len = rx_h_len; + + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rtx, rtx, HDCP_2_2_RTX_LEN); + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rrx, rrx, HDCP_2_2_RRX_LEN); + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rx_caps, rx_caps, HDCP_2_2_RXCAPS_LEN); + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.tx_caps, tx_caps, HDCP_2_2_TXCAPS_LEN); + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rx_h, rx_h, rx_h_len); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_AKE_H_PRIME); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + tci_resp = (struct tci_t *)dp_tee_priv->resp_buf; + + return tci_resp->return_code; +} + +/* Store paring info */ +int tee_ake_paring(struct mtk_hdcp_info *hdcp_info, u8 *rx_ekm) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_AKE_PARING; + memcpy(tci->cmd_body.cmd_hdcp_ake_paring.ekm, rx_ekm, HDCP_2_2_E_KH_KM_LEN); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_AKE_PARING); + if (rc) + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + + return 0; +} + +/* Calculate l prime and compare */ +int tee_lc_l_prime(struct mtk_hdcp_info *hdcp_info, u8 *rn, u8 *rx_l, u32 len) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + struct tci_t *tci_resp; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_LC_L_PRIME; + memcpy(tci->cmd_body.cmd_hdcp_lc_l_prime.rn, rn, HDCP_2_2_RN_LEN); + tci->cmd_body.cmd_hdcp_lc_l_prime.rx_l_len = len; + memcpy(tci->cmd_body.cmd_hdcp_lc_l_prime.rx_l, rx_l, len); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_LC_L_PRIME); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + tci_resp = (struct tci_t *)dp_tee_priv->resp_buf; + + return tci_resp->return_code; +} + +int tee_ske_enc_ks(struct mtk_hdcp_info *hdcp_info, u8 *riv, u8 *eks) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + u8 *share_buffer = NULL; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_SKE_CAL_EKS; + memcpy(tci->cmd_body.cmd_hdcp_ske_eks.riv, riv, HDCP_2_2_RIV_LEN); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH + 16, CMD_SKE_CAL_EKS); + if (rc) + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + + share_buffer = (u8 *)dp_tee_priv->resp_buf; + memcpy(eks, share_buffer + TCI_LENGTH, 16); + + return 0; +} + +/* Calculate and compare v prime for repeater */ +int tee_hdcp2_compute_compare_v(struct mtk_hdcp_info *hdcp_info, + u8 *crypto_param, u32 param_len, u8 *rx_v, u8 *tx_v) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + u8 *share_buffer = NULL; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_COMPARE_V2; + tci->cmd_body.cmd_hdcp_compare.rx_val_len = 16; + tci->cmd_body.cmd_hdcp_compare.param_len = param_len; + memcpy(tci->cmd_body.cmd_hdcp_compare.rx_val, rx_v, 16); + memcpy(tci->cmd_body.cmd_hdcp_compare.param, crypto_param, param_len); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_COMPARE_V2); + if (rc) { + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + return rc; + } + + share_buffer = (u8 *)dp_tee_priv->resp_buf; + memcpy(tx_v, share_buffer + TCI_LENGTH, 16); + + return 0; +} + +/* Calculate and compare m prime for repeater */ +int tee_hdcp2_compute_compare_m(struct mtk_hdcp_info *hdcp_info, + u8 *crypto_param, u32 param_len, u8 *rx_m) +{ + struct dp_tee_private *dp_tee_priv = hdcp_info->g_dp_tee_priv; + struct tci_t *tci = (struct tci_t *)dp_tee_priv->shm->kaddr; + int rc; + + /* Copy parameters */ + tci->command_id = CMD_COMPARE_M; + tci->cmd_body.cmd_hdcp_compare.rx_val_len = HDCP_2_2_MPRIME_LEN; + tci->cmd_body.cmd_hdcp_compare.param_len = param_len; + memcpy(tci->cmd_body.cmd_hdcp_compare.rx_val, rx_m, HDCP_2_2_MPRIME_LEN); + memcpy(tci->cmd_body.cmd_hdcp_compare.param, crypto_param, param_len); + + rc = dp_tee_op_send(dp_tee_priv, TCI_LENGTH, CMD_COMPARE_M); + if (rc) + pr_err("[TLC_HDCP] tee_op_send failed, error=%x\n", rc); + + return 0; +} diff --git a/drivers/gpu/drm/mediatek/tlc_dp_hdcp.h b/drivers/gpu/drm/mediatek/tlc_dp_hdcp.h new file mode 100644 index 000000000000..871d3fec5835 --- /dev/null +++ b/drivers/gpu/drm/mediatek/tlc_dp_hdcp.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019-2024 MediaTek Inc. + */ + +#ifndef _TLC_DP_HDCP_H_ +#define _TLC_DP_HDCP_H_ + +#include <linux/printk.h> +#include <linux/tee_drv.h> +#include <linux/uuid.h> +#include <linux/types.h> +#include <linux/sched/clock.h> +#include <drm/display/drm_dp_helper.h> +#include "tci.h" + +/* HDCP version definitions */ +#define HDCP_NONE 0x0 /* No HDCP supported, no secure data path */ +#define HDCP_V1 0x1 /* HDCP version 1.0 */ +#define HDCP_V2 0x2 /* HDCP version 2.0 Type 1 */ +#define HDCP_V2_3 0x5 /* HDCP version 2.3 Type 1 */ + +/* Max buffer size supported by dp */ +#define MAX_COMMAND_SIZE 4096 +#define MAX_RESPONSE_SIZE MAX_COMMAND_SIZE + +#define HDCP1X_REP_MAXDEVS 128 +#define HDCP1X_V_LEN 20 +#define HDCP1X_B_INFO_LEN 2 + +#define HDCP2_K_LEN 2 +#define HDCP2_STREAMID_TYPE_LEN 2 + +enum hdcp_result { + AUTH_ZERO = 0, + AUTH_INIT = 2, + AUTH_PASS = 3, + AUTH_FAIL = 4, +}; + +struct dp_tee_private { + u32 session; + size_t resp_len; + u8 resp_buf[MAX_RESPONSE_SIZE]; + struct tee_context *ctx; + struct tee_shm *shm; +}; + +struct hdcp2_info_tx { + struct hdcp2_ake_init ake_init; + struct hdcp2_ake_no_stored_km no_stored_km; + struct hdcp2_ske_send_eks send_eks; + struct hdcp2_lc_init lc_init; + struct hdcp2_rep_stream_manage stream_manage; + struct hdcp2_rep_send_ack send_ack; + struct hdcp2_tx_caps tx_caps; + u8 k[HDCP2_K_LEN]; + u8 stream_id_type[HDCP2_STREAMID_TYPE_LEN]; +}; + +struct hdcp2_info_rx { + struct hdcp2_cert_rx cert_rx; + struct hdcp2_ake_send_cert send_cert; + struct hdcp2_rep_send_receiverid_list recvid_list; + struct hdcp2_ake_send_pairing_info pairing_info; + struct hdcp2_rep_stream_ready stream_ready; + struct hdcp2_ake_send_hprime send_hprime; + struct hdcp2_lc_send_lprime send_lprime; +}; + +struct hdcp1x_info { + bool capable; + bool repeater; + u8 device_count; + u8 v[HDCP1X_V_LEN]; + u8 b_ksv[DRM_HDCP_KSV_LEN]; + u8 a_ksv[DRM_HDCP_KSV_LEN]; + u8 b_info[HDCP1X_B_INFO_LEN]; + u8 ksvfifo[DRM_HDCP_KSV_LEN * (HDCP1X_REP_MAXDEVS - 1)]; +}; + +struct hdcp2_info { + bool capable; + bool repeater; + bool stored_km; + u8 device_count; + u8 stream_id_type; + u32 seq_num_v_cnt; + atomic_t cp_irq; + int cp_irq_cached; + wait_queue_head_t cp_irq_queue; + struct hdcp2_info_tx hdcp_tx; + struct hdcp2_info_rx hdcp_rx; + struct hdcp2_ake_stored_km ake_stored_km; +}; + +struct mtk_hdcp_info { + bool g_init; + u8 auth_status; + u8 auth_version; + u32 hdcp_content_type; + u32 content_protection; + struct hdcp2_info hdcp2_info; + struct hdcp1x_info hdcp1x_info; + struct dp_tee_private *g_dp_tee_priv; +}; + +int tee_add_device(struct mtk_hdcp_info *hdcp_info, u32 version); +void tee_remove_device(struct mtk_hdcp_info *hdcp_info); +int tee_clear_paring(struct mtk_hdcp_info *hdcp_info); +int tee_calculate_lm(struct mtk_hdcp_info *hdcp_info, u8 *bksv); +int tee_get_aksv(struct mtk_hdcp_info *hdcp_info, u8 *aksv); +int tee_compare_r0(struct mtk_hdcp_info *hdcp_info, u8 *r0, u32 len); +int tee_hdcp1x_compute_compare_v(struct mtk_hdcp_info *hdcp_info, + u8 *crypto_param, u32 param_len, u8 *rx_v); +int tee_hdcp1x_set_tx_an(struct mtk_hdcp_info *hdcp_info, u8 *an_code); +int tee_hdcp1x_soft_rst(struct mtk_hdcp_info *hdcp_info); +int tee_hdcp2_soft_rst(struct mtk_hdcp_info *hdcp_info); +int tee_hdcp_enable_encrypt(struct mtk_hdcp_info *hdcp_info, bool enable, u8 version); +int tee_ake_certificate(struct mtk_hdcp_info *hdcp_info, + u8 *certificate, bool *stored, u8 *out_m, u8 *out_ekm); +int tee_enc_rsaes_oaep(struct mtk_hdcp_info *hdcp_info, u8 *ekm); +int tee_ake_h_prime(struct mtk_hdcp_info *hdcp_info, + u8 *rtx, u8 *rrx, u8 *rx_caps, u8 *tx_caps, u8 *rx_h, u32 rx_h_len); +int tee_ake_paring(struct mtk_hdcp_info *hdcp_info, u8 *rx_ekm); +int tee_lc_l_prime(struct mtk_hdcp_info *hdcp_info, u8 *rn, u8 *rx_l, u32 len); + +int tee_ske_enc_ks(struct mtk_hdcp_info *hdcp_info, u8 *riv, u8 *eks); +int tee_hdcp2_compute_compare_v(struct mtk_hdcp_info *hdcp_info, + u8 *crypto_param, u32 param_len, u8 *rx_v, u8 *tx_v); +int tee_hdcp2_compute_compare_m(struct mtk_hdcp_info *hdcp_info, + u8 *crypto_param, u32 param_len, u8 *rx_m); +#endif /* _TLC_DP_HDCP_H_ */ -- 2.43.0 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-06-08 12:01 ` mac.shen -1 siblings, 0 replies; 42+ messages in thread From: mac.shen @ 2024-06-08 12:01 UTC (permalink / raw) To: chunkuang.hu, p.zabel, airlied, daniel, matthias.bgg, angelogioacchino.delregno, jitao.shi Cc: mac.shen, shuijing.li, linux-kernel, dri-devel, linux-mediatek, linux-arm-kernel Changes in v3: - refine the function to get system time - refine the flow to do HDCP with content type and protection value which set by user space - refine the flow to update content protection - refine the flow to do HDCP2.x authentication per suggestion from the previous thread: https://patchwork.kernel.org/project/linux-mediatek /patch/20240205055055.25340-3-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 | 321 +++++--- drivers/gpu/drm/mediatek/mtk_dp.h | 103 +++ drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c | 944 ++++++++++++++++++++++++ drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h | 25 + drivers/gpu/drm/mediatek/mtk_dp_reg.h | 8 +- drivers/gpu/drm/mediatek/mtk_dpi.c | 3 + 7 files changed, 1321 insertions(+), 84 deletions(-) create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.h create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile index f27ddfbd2e12..19b7625ae573 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_hdcp2.o \ mtk_dp.o obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk-dp.o diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c index 536366956447..12854a04622f 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp.c +++ b/drivers/gpu/drm/mediatek/mtk_dp.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2019-2022 MediaTek Inc. + * Copyright (c) 2019-2024 MediaTek Inc. * Copyright (c) 2022 BayLibre */ @@ -8,13 +8,13 @@ #include <drm/display/drm_dp.h> #include <drm/display/drm_dp_helper.h> #include <drm/drm_atomic_helper.h> -#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> +#include <drm/display/drm_hdcp_helper.h> #include <linux/arm-smccc.h> #include <linux/clk.h> #include <linux/delay.h> @@ -30,10 +30,10 @@ #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/soc/mediatek/mtk_sip_svc.h> -#include <sound/hdmi-codec.h> -#include <video/videomode.h> +#include "mtk_dp.h" #include "mtk_dp_reg.h" +#include "mtk_dp_hdcp2.h" #define MTK_DP_SIP_CONTROL_AARCH32 MTK_SIP_SMC_CMD(0x523) #define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE (BIT(0) | BIT(5)) @@ -52,43 +52,6 @@ #define MTK_DP_VERSION 0x11 #define MTK_DP_SDP_AUI 0x4 -enum { - MTK_DP_CAL_GLB_BIAS_TRIM = 0, - MTK_DP_CAL_CLKTX_IMPSE, - MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0, - MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1, - MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2, - MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3, - MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0, - MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1, - MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2, - MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3, - MTK_DP_CAL_MAX, -}; - -struct mtk_dp_train_info { - bool sink_ssc; - bool cable_plugged_in; - /* link_rate is in multiple of 0.27Gbps */ - int link_rate; - int lane_count; - unsigned int channel_eq_pattern; -}; - -struct mtk_dp_audio_cfg { - bool detect_monitor; - int sad_count; - int sample_rate; - int word_length_bits; - int channels; -}; - -struct mtk_dp_info { - enum dp_pixelformat format; - struct videomode vm; - struct mtk_dp_audio_cfg audio_cur_cfg; -}; - struct mtk_dp_efuse_fmt { unsigned short idx; unsigned short shift; @@ -98,44 +61,6 @@ struct mtk_dp_efuse_fmt { unsigned short default_val; }; -struct mtk_dp { - bool enabled; - bool need_debounce; - int irq; - u8 max_lanes; - u8 max_linkrate; - u8 rx_cap[DP_RECEIVER_CAP_SIZE]; - u32 cal_data[MTK_DP_CAL_MAX]; - u32 irq_thread_handle; - /* irq_thread_lock is used to protect irq_thread_handle */ - spinlock_t irq_thread_lock; - - struct device *dev; - struct drm_bridge bridge; - struct drm_bridge *next_bridge; - struct drm_connector *conn; - struct drm_device *drm_dev; - struct drm_dp_aux aux; - - const struct mtk_dp_data *data; - struct mtk_dp_info info; - struct mtk_dp_train_info train_info; - - struct platform_device *phy_dev; - struct phy *phy; - struct regmap *regs; - struct timer_list debounce_timer; - - /* For audio */ - bool audio_enable; - hdmi_codec_plugged_cb plugged_cb; - struct platform_device *audio_pdev; - - struct device *codec_dev; - /* protect the plugged_cb as it's used in both bridge ops and audio */ - struct mutex update_plugged_status_lock; -}; - struct mtk_dp_data { int bridge_type; unsigned int smc_cmd; @@ -319,12 +244,24 @@ static struct regmap_config mtk_dp_regmap_config = { .name = "mtk-dp-registers", }; +u64 mtk_dp_get_system_time(void) +{ + return ktime_get_mono_fast_ns(); +} + +u64 mtk_dp_get_time_diff(u64 pre_time) +{ + u64 post_time = mtk_dp_get_system_time(); + + return (post_time - pre_time); +} + static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b) { return container_of(b, struct mtk_dp, bridge); } -static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset) +u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset) { u32 read_val; int ret; @@ -350,8 +287,8 @@ static int mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val) return ret; } -static int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, - u32 val, u32 mask) +int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, + u32 val, u32 mask) { int ret = regmap_update_bits(mtk_dp->regs, offset, mask, val); @@ -1865,11 +1802,200 @@ static void mtk_dp_init_port(struct mtk_dp *mtk_dp) mtk_dp_digital_sw_reset(mtk_dp); } +static void mtk_dp_check_sink_esi(struct mtk_dp *mtk_dp) +{ + u8 clear_cp_irq = BIT(2); + + /* hdcp 1.x do not need irq */ + if (mtk_dp->hdcp_info.hdcp2_info.capable) { + dp_tx_hdcp2x_irq(&mtk_dp->hdcp_info); + drm_dp_dpcd_write(&mtk_dp->aux, + DP_DEVICE_SERVICE_IRQ_VECTOR, &clear_cp_irq, 0x1); + } +} + +static int mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp) +{ + ssize_t ret; + u8 sink_count; + u8 sink_count_200; + + ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT_ESI, &sink_count); + if (ret < 0) { + drm_info(mtk_dp->drm_dev, "Read sink count failed: %zd\n", ret); + return ret; + } + + ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count_200); + if (ret < 0) { + drm_info(mtk_dp->drm_dev, + "Read DP_SINK_COUNT_ESI failed: %zd\n", ret); + return ret; + } + + mtk_dp_check_sink_esi(mtk_dp); + + return 0; +} + +static void mtk_dp_hdcp_get_info(struct mtk_dp *mtk_dp) +{ + dp_tx_hdcp2x_get_info(&mtk_dp->hdcp_info); +} + +static void mtk_dp_hdcp_disable(struct mtk_dp *mtk_dp) +{ + mutex_lock(&mtk_dp->hdcp_mutex); + + if (mtk_dp->hdcp_info.auth_status == AUTH_ZERO) + goto end; + + if (mtk_dp->hdcp_info.auth_version == HDCP_VERSION_2X) + dp_tx_hdcp2x_disabel(&mtk_dp->hdcp_info); + +end: + cancel_delayed_work_sync(&mtk_dp->check_work); + mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_DESIRED); + + mutex_unlock(&mtk_dp->hdcp_mutex); +} + +static void mtk_dp_hdcp_check_work(struct work_struct *work) +{ + struct mtk_dp *mtk_dp = container_of(to_delayed_work(work), + struct mtk_dp, check_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); + } +} + +static void mtk_dp_hdcp_handle(struct work_struct *data) +{ + struct mtk_dp *mtk_dp = container_of(data, struct mtk_dp, hdcp_work); + unsigned long check_link_interval = DRM_HDCP_CHECK_PERIOD_MS; + int ret = -EINVAL; + + mutex_lock(&mtk_dp->hdcp_mutex); + + if (mtk_dp->hdcp_info.auth_status == AUTH_PASS) + goto end; + + mtk_dp_hdcp_get_info(mtk_dp); + + if (mtk_dp->hdcp_info.hdcp2_info.capable) { + ret = dp_tx_hdcp2x_enable(&mtk_dp->hdcp_info); + if (!ret) + check_link_interval = DRM_HDCP2_CHECK_PERIOD_MS; + } + + if (!ret) { + schedule_delayed_work(&mtk_dp->check_work, check_link_interval); + mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_ENABLED); + } + +end: + mutex_unlock(&mtk_dp->hdcp_mutex); +} + +static void mtk_dp_hdcp_enable(struct mtk_dp *mtk_dp) +{ + if (!mtk_dp->train_info.cable_plugged_in || !mtk_dp->enabled) + return; + + dev_info(mtk_dp->dev, "[HDCP] dp start HDCP work\n"); + queue_work(mtk_dp->hdcp_workqueue, &mtk_dp->hdcp_work); +} + +static void mtk_dp_hdcp_prop_work(struct work_struct *work) +{ + struct mtk_dp *mtk_dp; + struct drm_device *drm_dev; + + mtk_dp = container_of(work, struct mtk_dp, prop_work); + + if (!mtk_dp->conn) { + dev_err(mtk_dp->dev, "[HDCP] Connector is null\n"); + return; + } + + drm_dev = mtk_dp->conn->dev; + + drm_modeset_lock(&drm_dev->mode_config.connection_mutex, NULL); + + if (mtk_dp->hdcp_info.content_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { + dev_info(mtk_dp->dev, "[HDCP] Update CP, content protection: %d, auth status:%d\n", + mtk_dp->hdcp_info.content_protection, mtk_dp->hdcp_info.auth_status); + + /* only update between ENABLED/DESIRED */ + drm_hdcp_update_content_protection(mtk_dp->conn, + mtk_dp->hdcp_info.content_protection); + } + + drm_modeset_unlock(&drm_dev->mode_config.connection_mutex); +} + +void mtk_dp_hdcp_update_value(struct mtk_dp *mtk_dp, u32 value) +{ + if (mtk_dp->hdcp_info.content_protection == value) + return; + + mtk_dp->hdcp_info.content_protection = value; + if (mtk_dp->hdcp_info.content_protection == DRM_MODE_CONTENT_PROTECTION_UNDESIRED) + return; + + schedule_work(&mtk_dp->prop_work); +} + +static void mtk_dp_hdcp_atomic_check(struct mtk_dp *mtk_dp, struct drm_connector_state *state) +{ + unsigned int current_protection = mtk_dp->hdcp_info.content_protection; + unsigned int new_protection = state->content_protection; + bool type_changed = state->hdcp_content_type != mtk_dp->hdcp_info.hdcp_content_type; + bool protection_changed = current_protection != new_protection; + bool disable = false; + bool enable = false; + + dev_dbg(mtk_dp->dev, "[HDCP] atomic check, old[%d, %d], new[%d, %d]\n", + mtk_dp->hdcp_info.content_protection, mtk_dp->hdcp_info.hdcp_content_type, + state->content_protection, state->hdcp_content_type); + + if (type_changed) { + /* + * With the current protection as ENABLED/DESIRED, + * re-authenticate with new content type if the content type changed. + */ + mtk_dp->hdcp_info.hdcp_content_type = state->hdcp_content_type; + mtk_dp->hdcp_info.hdcp2_info.stream_id_type = state->hdcp_content_type; + + if (current_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) + disable = true; + + if (new_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) + enable = true; + } else if (protection_changed) { + if (new_protection == DRM_MODE_CONTENT_PROTECTION_UNDESIRED) + disable = true; + else + enable = true; + } + + if (disable) + mtk_dp_hdcp_disable(mtk_dp); + + if (enable) { + mtk_dp->hdcp_info.content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + mtk_dp_hdcp_enable(mtk_dp); + } +} + static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev) { struct mtk_dp *mtk_dp = dev; unsigned long flags; u32 status; + int ret; if (mtk_dp->need_debounce && mtk_dp->train_info.cable_plugged_in) msleep(100); @@ -1894,8 +2020,13 @@ static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev) } } - if (status & MTK_DP_THREAD_HPD_EVENT) + if (status & MTK_DP_THREAD_HPD_EVENT) { dev_dbg(mtk_dp->dev, "Receive IRQ from sink devices\n"); + /* check if need clear hpd irq */ + ret = mtk_dp_hpd_sink_event(mtk_dp); + if (ret) + dev_err(mtk_dp->dev, "Fail to process hpd sink event\n"); + } return IRQ_HANDLED; } @@ -2242,6 +2373,7 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge, struct drm_bridge_state *old_state) { struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge); + struct drm_connector_state *connector_state; int ret; mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state, @@ -2252,6 +2384,8 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge, return; } + connector_state = drm_atomic_get_connector_state(old_state->base.state, mtk_dp->conn); + mtk_dp_aux_panel_poweron(mtk_dp, true); /* Training */ @@ -2281,6 +2415,14 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge, mtk_dp->enabled = true; mtk_dp_update_plugged_status(mtk_dp); + /* Enable hdcp if it's desired */ + dev_dbg(mtk_dp->dev, "hdcp_content_type:%d, content protection: %d", + connector_state->hdcp_content_type, connector_state->content_protection); + if (connector_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED) { + mtk_dp->hdcp_info.hdcp_content_type = connector_state->hdcp_content_type; + mtk_dp_hdcp_enable(mtk_dp); + } + return; power_off_aux: mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, @@ -2427,6 +2569,8 @@ static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge, drm_display_mode_to_videomode(&crtc_state->adjusted_mode, &mtk_dp->info.vm); + mtk_dp_hdcp_atomic_check(mtk_dp, conn_state); + return 0; } @@ -2666,6 +2810,17 @@ static int mtk_dp_probe(struct platform_device *pdev) if (ret) return ret; + mutex_init(&mtk_dp->hdcp_mutex); + init_waitqueue_head(&mtk_dp->hdcp_info.hdcp2_info.cp_irq_queue); + INIT_WORK(&mtk_dp->hdcp_work, mtk_dp_hdcp_handle); + INIT_WORK(&mtk_dp->prop_work, mtk_dp_hdcp_prop_work); + INIT_DELAYED_WORK(&mtk_dp->check_work, mtk_dp_hdcp_check_work); + mtk_dp->hdcp_workqueue = create_workqueue("mtk_dp_hdcp_work"); + if (!mtk_dp->hdcp_workqueue) { + dev_err(mtk_dp->dev, "failed to create hdcp work queue"); + return -ENOMEM; + } + mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs; mtk_dp->bridge.of_node = dev->of_node; mtk_dp->bridge.type = mtk_dp->data->bridge_type; diff --git a/drivers/gpu/drm/mediatek/mtk_dp.h b/drivers/gpu/drm/mediatek/mtk_dp.h new file mode 100644 index 000000000000..dfeaf12c7d58 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dp.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019-2024 MediaTek Inc. + */ + +#ifndef _MTK_DP_H_ +#define _MTK_DP_H_ + +#include "tlc_dp_hdcp.h" +#include <drm/drm_bridge.h> +#include <sound/hdmi-codec.h> +#include <video/videomode.h> + +#define MS_TO_NS(x) ((x) * NSEC_PER_MSEC) + +enum { + MTK_DP_CAL_GLB_BIAS_TRIM = 0, + MTK_DP_CAL_CLKTX_IMPSE, + MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0, + MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1, + MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2, + MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3, + MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0, + MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1, + MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2, + MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3, + MTK_DP_CAL_MAX, +}; + +struct mtk_dp_audio_cfg { + bool detect_monitor; + int sad_count; + int sample_rate; + int word_length_bits; + int channels; +}; + +struct mtk_dp_info { + enum dp_pixelformat format; + struct videomode vm; + struct mtk_dp_audio_cfg audio_cur_cfg; +}; + +struct mtk_dp_train_info { + bool sink_ssc; + bool cable_plugged_in; + /* link_rate is in multiple of 0.27Gbps */ + int link_rate; + int lane_count; + unsigned int channel_eq_pattern; +}; + +struct mtk_dp { + bool enabled; + bool need_debounce; + int irq; + u8 max_lanes; + u8 max_linkrate; + u8 rx_cap[DP_RECEIVER_CAP_SIZE]; + u32 cal_data[MTK_DP_CAL_MAX]; + u32 irq_thread_handle; + /* irq_thread_lock is used to protect irq_thread_handle */ + spinlock_t irq_thread_lock; + + struct device *dev; + struct drm_bridge bridge; + struct drm_bridge *next_bridge; + struct drm_connector *conn; + struct drm_device *drm_dev; + struct drm_dp_aux aux; + + const struct mtk_dp_data *data; + struct mtk_dp_info info; + struct mtk_dp_train_info train_info; + struct mtk_hdcp_info hdcp_info; + struct work_struct hdcp_work; + struct work_struct prop_work; + struct delayed_work check_work; + struct workqueue_struct *hdcp_workqueue; + /* This mutex is used to synchronize HDCP operations in the driver */ + struct mutex hdcp_mutex; + + struct platform_device *phy_dev; + struct phy *phy; + struct regmap *regs; + struct timer_list debounce_timer; + + /* For audio */ + bool audio_enable; + hdmi_codec_plugged_cb plugged_cb; + struct platform_device *audio_pdev; + + struct device *codec_dev; + /* protect the plugged_cb as it's used in both bridge ops and audio */ + struct mutex update_plugged_status_lock; +}; + +u64 mtk_dp_get_system_time(void); +u64 mtk_dp_get_time_diff(u64 pre_time); +u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset); +int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, u32 val, u32 mask); +void mtk_dp_hdcp_update_value(struct mtk_dp *mtk_dp, u32 value); +#endif /* _MTK_DP_H_ */ diff --git a/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c new file mode 100644 index 000000000000..e8affa4343c1 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c @@ -0,0 +1,944 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019-2024 MediaTek Inc. + */ + +#include <drm/display/drm_hdcp_helper.h> + +#include "mtk_dp_hdcp2.h" +#include "mtk_dp_reg.h" +#include "mtk_dp.h" + +#define HDCP2_LC_RETRY_CNT 3 + +static u8 t_rtx[HDCP_2_2_RTX_LEN] = { + 0x18, 0xfa, 0xe4, 0x20, 0x6a, 0xfb, 0x51, 0x49 +}; + +static u8 t_tx_caps[HDCP_2_2_TXCAPS_LEN] = { + 0x02, 0x00, 0x00 +}; + +static u8 t_rn[HDCP_2_2_RN_LEN] = { + 0x32, 0x75, 0x3e, 0xa8, 0x78, 0xa6, 0x38, 0x1c +}; + +static u8 t_riv[HDCP_2_2_RIV_LEN] = { + 0x40, 0x2b, 0x6b, 0x43, 0xc5, 0xe8, 0x86, 0xd8 +}; + +static void dp_tx_hdcp2x_fill_stream_type(struct mtk_hdcp_info *hdcp_info, u8 uc_type) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + + mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34D0, uc_type, 0xff); +} + +static void dp_tx_hdcp2x_set_auth_pass(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_3400, + HDCP_SEL_DP_TRANS_P0_MASK, HDCP_SEL_DP_TRANS_P0_MASK); + mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4, + HDCP22_AUTH_DONE_DP_TRANS_P0_MASK, + HDCP22_AUTH_DONE_DP_TRANS_P0_MASK); + } else { + mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3400, 0, HDCP_SEL_DP_TRANS_P0_MASK); + mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4, 0, + HDCP22_AUTH_DONE_DP_TRANS_P0_MASK); + } +} + +static int dp_tx_hdcp2x_enable_auth(struct mtk_hdcp_info *hdcp_info, bool enable) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + u32 version; + int ret; + + dp_tx_hdcp2x_set_auth_pass(hdcp_info, enable); + if (!enable) { + ret = tee_hdcp_enable_encrypt(hdcp_info, enable, HDCP_NONE); + if (ret) + return ret; + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, 0, HDCP_FRAME_EN_DP_ENC0_P0); + + return 0; + } + + if (HDCP_2_2_HDCP1_DEVICE_CONNECTED(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[1])) + version = HDCP_V1; + else if (HDCP_2_2_HDCP_2_0_REP_CONNECTED + (hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[1])) + version = HDCP_V2; + else + version = HDCP_V2_3; + + ret = tee_hdcp_enable_encrypt(hdcp_info, enable, version); + if (ret) + return ret; + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, + HDCP_FRAME_EN_DP_ENC0_P0, HDCP_FRAME_EN_DP_ENC0_P0); + + return 0; +} + +static int dp_tx_hdcp2x_init(struct mtk_hdcp_info *hdcp_info) +{ + int ret; + + memset(&hdcp_info->hdcp2_info.hdcp_tx, 0, sizeof(struct hdcp2_info_tx)); + memset(&hdcp_info->hdcp2_info.hdcp_rx, 0, sizeof(struct hdcp2_info_rx)); + memset(&hdcp_info->hdcp2_info.ake_stored_km, 0, sizeof(struct hdcp2_ake_stored_km)); + memcpy(hdcp_info->hdcp2_info.hdcp_tx.ake_init.r_tx, t_rtx, HDCP_2_2_RTX_LEN); + memcpy(&hdcp_info->hdcp2_info.hdcp_tx.tx_caps, t_tx_caps, HDCP_2_2_TXCAPS_LEN); + memcpy(hdcp_info->hdcp2_info.hdcp_tx.lc_init.r_n, t_rn, HDCP_2_2_RN_LEN); + memcpy(hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, t_riv, HDCP_2_2_RIV_LEN); + + ret = dp_tx_hdcp2x_enable_auth(hdcp_info, false); + if (ret) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_inc_seq_num_m(struct mtk_hdcp_info *hdcp_info) +{ + u32 tmp = drm_hdcp_be24_to_cpu(hdcp_info->hdcp2_info.hdcp_tx.stream_manage.seq_num_m); + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + + if (tmp == HDCP_2_2_SEQ_NUM_MAX) { + dev_err(mtk_dp->dev, "[HDCP2.X] With seq num max\n"); + return -EINVAL; + } + + tmp++; + + drm_hdcp_cpu_to_be24(hdcp_info->hdcp2_info.hdcp_tx.stream_manage.seq_num_m, tmp); + + return 0; +} + +static int dp_tx_hdcp2x_process_rep_auth_stream_manage(struct mtk_hdcp_info *hdcp_info) +{ + hdcp_info->hdcp2_info.hdcp_tx.k[0] = 0x00; + hdcp_info->hdcp2_info.hdcp_tx.k[1] = 0x01; + + hdcp_info->hdcp2_info.hdcp_tx.stream_id_type[0] = 0x00; /* Payload ID */ + hdcp_info->hdcp2_info.hdcp_tx.stream_id_type[1] = hdcp_info->hdcp2_info.stream_id_type; + + return dp_tx_hdcp2x_inc_seq_num_m(hdcp_info); +} + +static int dp_tx_hdcp2x_recv_rep_auth_send_recv_id_list(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + u32 len = 0, len_recv_id_list = 0; + u8 *buffer = NULL; + int ret = 0; + + len_recv_id_list = hdcp_info->hdcp2_info.device_count * HDCP_2_2_RECEIVER_ID_LEN; + len = len_recv_id_list + HDCP_2_2_RXINFO_LEN + HDCP_2_2_SEQ_NUM_LEN; + buffer = kmalloc(len, GFP_KERNEL); + if (!buffer) { + dev_err(mtk_dp->dev, "[HDCP2.X] Out of memory\n"); + return -ENOMEM; + } + + memcpy(buffer, hdcp_info->hdcp2_info.hdcp_rx.recvid_list.receiver_ids, + len_recv_id_list); + memcpy(buffer + len_recv_id_list, + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info, HDCP_2_2_RXINFO_LEN); + memcpy(buffer + len_recv_id_list + HDCP_2_2_RXINFO_LEN, + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.seq_num_v, HDCP_2_2_SEQ_NUM_LEN); + + ret = tee_hdcp2_compute_compare_v(hdcp_info, buffer, len, + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.v_prime, + hdcp_info->hdcp2_info.hdcp_tx.send_ack.v); + + kfree(buffer); + + return ret; +} + +static int dp_tx_hdcp2x_recv_rep_auth_stream_ready(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + u8 *buffer = NULL; + u32 len = 0; + int ret = 0; + + len = HDCP2_STREAMID_TYPE_LEN + HDCP_2_2_SEQ_NUM_LEN; + buffer = kmalloc(len, GFP_KERNEL); + if (!buffer) { + dev_err(mtk_dp->dev, "[HDCP2.X] Out of memory\n"); + return -ENOMEM; + } + + memcpy(buffer, hdcp_info->hdcp2_info.hdcp_tx.stream_id_type, HDCP2_STREAMID_TYPE_LEN); + memcpy(buffer + HDCP2_STREAMID_TYPE_LEN, + hdcp_info->hdcp2_info.hdcp_tx.stream_manage.seq_num_m, + HDCP_2_2_SEQ_NUM_LEN); + ret = tee_hdcp2_compute_compare_m(hdcp_info, buffer, len, + hdcp_info->hdcp2_info.hdcp_rx.stream_ready.m_prime); + + kfree(buffer); + + return ret; +} + +static void dp_tx_hdcp2x_wait_for_cp_irq(struct mtk_hdcp_info *hdcp_info, int timeout) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + long ret; + +#define C (hdcp_info->hdcp2_info.cp_irq_cached != atomic_read(&hdcp_info->hdcp2_info.cp_irq)) + ret = wait_event_interruptible_timeout(hdcp_info->hdcp2_info.cp_irq_queue, C, + msecs_to_jiffies(timeout)); + if (!ret) + dev_dbg(mtk_dp->dev, "[HDCP2.X] Timedout at waiting for CP_IRQ\n"); +} + +static int dp_tx_hdcp2x_read_ake_send_cert(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ktime_t msg_end; + bool msg_expired; + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_SEND_CERT\n"); + + mdelay(HDCP_2_2_CERT_TIMEOUT_MS); + + msg_end = ktime_add_ms(ktime_get_raw(), HDCP_2_2_DP_CERT_READ_TIMEOUT_MS); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_AKE_SEND_CERT_OFFSET, + (void *)&hdcp_info->hdcp2_info.hdcp_rx.cert_rx, HDCP2_CERTRX_LEN); + if (ret < 0) + return ret; + + msg_expired = ktime_after(ktime_get_raw(), msg_end); + if (msg_expired) + dev_dbg(mtk_dp->dev, "[HDCP2.X] Timeout to read Ake send cert\n"); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RRX_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.send_cert.r_rx, HDCP_2_2_RRX_LEN); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RX_CAPS_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.send_cert.rx_caps, + HDCP_2_2_RXCAPS_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_read_ake_send_hprime(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ktime_t msg_end; + bool msg_expired; + u8 rx_status = 0; + int timeout; + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_SEND_HPRIME\n"); + + timeout = hdcp_info->hdcp2_info.stored_km ? + HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS : HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS; + + dp_tx_hdcp2x_wait_for_cp_irq(hdcp_info, timeout); + hdcp_info->hdcp2_info.cp_irq_cached = atomic_read(&hdcp_info->hdcp2_info.cp_irq); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, + HDCP_2_2_DP_RXSTATUS_LEN); + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) + return ret >= 0 ? -EIO : ret; + + if (!HDCP_2_2_DP_RXSTATUS_H_PRIME(rx_status)) + return -EAGAIN; + + msg_end = ktime_add_ms(ktime_get_raw(), HDCP_2_2_DP_HPRIME_READ_TIMEOUT_MS); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_AKE_SEND_HPRIME_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.send_hprime.h_prime, + HDCP_2_2_H_PRIME_LEN); + if (ret < 0) + return ret; + + msg_expired = ktime_after(ktime_get_raw(), msg_end); + if (msg_expired) + dev_dbg(mtk_dp->dev, "[HDCP2.X] Timeout to read AKE hprime\n"); + + return 0; +} + +static int dp_tx_hdcp2x_read_ake_send_pairing_info(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ktime_t msg_end; + bool msg_expired; + u8 rx_status = 0; + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_SEND_PAIRING_INFO\n"); + + dp_tx_hdcp2x_wait_for_cp_irq(hdcp_info, HDCP_2_2_PAIRING_TIMEOUT_MS); + hdcp_info->hdcp2_info.cp_irq_cached = atomic_read(&hdcp_info->hdcp2_info.cp_irq); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, + HDCP_2_2_DP_RXSTATUS_LEN); + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) + return ret >= 0 ? -EIO : ret; + + if (!HDCP_2_2_DP_RXSTATUS_PAIRING(rx_status)) + return -EAGAIN; + + msg_end = ktime_add_ms(ktime_get_raw(), HDCP_2_2_DP_PAIRING_READ_TIMEOUT_MS); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_AKE_SEND_PAIRING_INFO_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.pairing_info.e_kh_km, + HDCP_2_2_E_KH_KM_LEN); + if (ret < 0) + return ret; + + msg_expired = ktime_after(ktime_get_raw(), msg_end); + if (msg_expired) + dev_err(mtk_dp->dev, "[HDCP2.X] Timeout to read pairing info\n"); + + return 0; +} + +static int dp_tx_hdcp2x_read_lc_send_lprime(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_LC_SEND_LPRIME\n"); + + mdelay(HDCP_2_2_DP_LPRIME_TIMEOUT_MS); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_LC_SEND_LPRIME_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.send_lprime.l_prime, + HDCP_2_2_L_PRIME_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_read_rep_send_recvid_list(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + u8 rx_status = 0; + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_REP_SEND_RECVID_LIST\n"); + + dp_tx_hdcp2x_wait_for_cp_irq(hdcp_info, HDCP_2_2_RECVID_LIST_TIMEOUT_MS); + hdcp_info->hdcp2_info.cp_irq_cached = atomic_read(&hdcp_info->hdcp2_info.cp_irq); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, + HDCP_2_2_DP_RXSTATUS_LEN); + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) + return ret >= 0 ? -EIO : ret; + + if (!HDCP_2_2_DP_RXSTATUS_READY(rx_status)) { + dev_err(mtk_dp->dev, "[HDCP2.X] RX status no ready\n"); + return -EAGAIN; + } + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info, + HDCP_2_2_RXINFO_LEN); + if (ret < 0) + return ret; + + hdcp_info->hdcp2_info.device_count = + (HDCP_2_2_DEV_COUNT_HI(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[0]) << 4 | + HDCP_2_2_DEV_COUNT_LO(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[1])); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_SEQ_NUM_V_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.seq_num_v, + HDCP_2_2_SEQ_NUM_LEN); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_VPRIME_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.v_prime, + HDCP_2_2_V_PRIME_HALF_LEN); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RECV_ID_LIST_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.receiver_ids, + hdcp_info->hdcp2_info.device_count * HDCP_2_2_RECEIVER_ID_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_read_rep_stream_ready(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_REP_STREAM_READY\n"); + + mdelay(HDCP_2_2_STREAM_READY_TIMEOUT_MS); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REP_STREAM_READY_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.stream_ready.m_prime, + HDCP_2_2_MPRIME_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_write_ake_init(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_Init\n"); + + ret = tee_hdcp2_soft_rst(hdcp_info); + if (ret) + return ret; + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_AKE_INIT_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.ake_init.r_tx, HDCP_2_2_RTX_LEN); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_TXCAPS_OFFSET, + (void *)&hdcp_info->hdcp2_info.hdcp_tx.tx_caps, + HDCP_2_2_TXCAPS_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_write_ake_no_stored_km(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_NO_STORED_KM\n"); + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.no_stored_km.e_kpub_km, + HDCP_2_2_E_KPUB_KM_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_write_ake_stored_km(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_STORED_KM\n"); + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_AKE_STORED_KM_OFFSET, + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m, + HDCP_2_2_E_KH_KM_LEN); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_M_OFFSET, + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m + + HDCP_2_2_E_KH_KM_LEN, + HDCP_2_2_E_KH_KM_M_LEN - HDCP_2_2_E_KH_KM_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_write_lc_init(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_LC_INIT\n"); + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_LC_INIT_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.lc_init.r_n, HDCP_2_2_RN_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_write_ske_send_eks(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_SKE_SEND_EKS\n"); + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_SKE_SEND_EKS_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.send_eks.e_dkey_ks, + HDCP_2_2_E_DKEY_KS_LEN); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_RIV_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, HDCP_2_2_RIV_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_write_stream_type(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] Write stream type\n"); + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.stream_id_type, 1); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_write_send_ack(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_SEND_ACK\n"); + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REP_SEND_ACK_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.send_ack.v, + HDCP_2_2_V_PRIME_HALF_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_write_stream_manage(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_STREAM_MANAGE\n"); + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REP_STREAM_MANAGE_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.stream_manage.seq_num_m, + HDCP_2_2_SEQ_NUM_LEN); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_K_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.k, HDCP2_K_LEN); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_STREAM_ID_TYPE_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.stream_id_type, + HDCP2_STREAMID_TYPE_LEN); + if (ret < 0) + return ret; + + dp_tx_hdcp2x_fill_stream_type(hdcp_info, hdcp_info->hdcp2_info.stream_id_type); + + return 0; +} + +/* Authentication flow starts from here */ +static int dp_tx_hdcp2x_key_exchange(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + bool stored; + int ret; + + if (!hdcp_info->hdcp2_info.capable) + return -EAGAIN; + + ret = dp_tx_hdcp2x_init(hdcp_info); + if (ret) + return ret; + + ret = dp_tx_hdcp2x_write_ake_init(hdcp_info); + if (ret) + return ret; + + ret = dp_tx_hdcp2x_read_ake_send_cert(hdcp_info); + if (ret) + return ret; + + hdcp_info->hdcp2_info.repeater = + HDCP_2_2_RX_REPEATER(hdcp_info->hdcp2_info.hdcp_rx.send_cert.rx_caps[2]); + + if (drm_hdcp_check_ksvs_revoked(mtk_dp->drm_dev, + hdcp_info->hdcp2_info.hdcp_rx.send_cert.cert_rx.receiver_id, + 1) > 0) { + dev_err(mtk_dp->dev, "[HDCP2.X] Receiver ID is revoked\n"); + return -EPERM; + } + + ret = tee_ake_certificate(hdcp_info, + (u8 *)&hdcp_info->hdcp2_info.hdcp_rx.cert_rx, &stored, + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m + + HDCP_2_2_E_KH_KM_LEN, + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m); + if (ret) + return ret; + + hdcp_info->hdcp2_info.stored_km = stored; + + if (!hdcp_info->hdcp2_info.stored_km) { + ret = tee_enc_rsaes_oaep(hdcp_info, + hdcp_info->hdcp2_info.hdcp_tx.no_stored_km.e_kpub_km); + if (ret) + return ret; + + ret = dp_tx_hdcp2x_write_ake_no_stored_km(hdcp_info); + if (ret) + return ret; + + } else { + ret = dp_tx_hdcp2x_write_ake_stored_km(hdcp_info); + if (ret) + return ret; + } + + ret = dp_tx_hdcp2x_read_ake_send_hprime(hdcp_info); + if (ret) + return ret; + + ret = tee_ake_h_prime(hdcp_info, + hdcp_info->hdcp2_info.hdcp_tx.ake_init.r_tx, + hdcp_info->hdcp2_info.hdcp_rx.send_cert.r_rx, + hdcp_info->hdcp2_info.hdcp_rx.send_cert.rx_caps, + (u8 *)&hdcp_info->hdcp2_info.hdcp_tx.tx_caps, + hdcp_info->hdcp2_info.hdcp_rx.send_hprime.h_prime, + HDCP_2_2_H_PRIME_LEN); + if (ret) { + if (hdcp_info->hdcp2_info.stored_km) + tee_clear_paring(hdcp_info); + return ret; + } + + if (!hdcp_info->hdcp2_info.stored_km) { + ret = dp_tx_hdcp2x_read_ake_send_pairing_info(hdcp_info); + if (ret) + return ret; + + /* Store m, km, Ekh(km) */ + ret = tee_ake_paring(hdcp_info, + hdcp_info->hdcp2_info.hdcp_rx.pairing_info.e_kh_km); + if (ret) + return ret; + } + + return 0; +} + +static int dp_tx_hdcp2x_locality_check(struct mtk_hdcp_info *hdcp_info) +{ + int ret, i, tries = HDCP2_LC_RETRY_CNT; + + for (i = 0; i < tries; i++) { + ret = dp_tx_hdcp2x_write_lc_init(hdcp_info); + if (ret) + continue; + + ret = dp_tx_hdcp2x_read_lc_send_lprime(hdcp_info); + if (ret) + continue; + + ret = tee_lc_l_prime(hdcp_info, hdcp_info->hdcp2_info.hdcp_tx.lc_init.r_n, + hdcp_info->hdcp2_info.hdcp_rx.send_lprime.l_prime, + HDCP_2_2_L_PRIME_LEN); + if (!ret) + return 0; + } + + return ret; +} + +static int dp_tx_hdcp2x_session_key_exchange(struct mtk_hdcp_info *hdcp_info) +{ + int ret; + + ret = tee_ske_enc_ks(hdcp_info, hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, + hdcp_info->hdcp2_info.hdcp_tx.send_eks.e_dkey_ks); + if (ret) + return ret; + + ret = dp_tx_hdcp2x_write_ske_send_eks(hdcp_info); + if (ret) + return ret; + + return 0; +} + +static +int dp_tx_hdcp2x_authenticate_repeater(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + u8 *rx_info; + int ret; + + ret = dp_tx_hdcp2x_read_rep_send_recvid_list(hdcp_info); + if (ret) + return ret; + + rx_info = hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info; + + if (HDCP_2_2_MAX_CASCADE_EXCEEDED(rx_info[1]) || + HDCP_2_2_MAX_DEVS_EXCEEDED(rx_info[1])) { + dev_err(mtk_dp->dev, "[HDCP2.X] Topology max size exceeded\n"); + return -EINVAL; + } + + if (drm_hdcp_check_ksvs_revoked(mtk_dp->drm_dev, + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.receiver_ids, + hdcp_info->hdcp2_info.device_count) > 0) { + dev_err(mtk_dp->dev, "[HDCP2.X] Revoked receiver ID(s) is in list\n"); + return -EPERM; + } + + ret = dp_tx_hdcp2x_recv_rep_auth_send_recv_id_list(hdcp_info); + if (ret) + return -EINVAL; + + ret = dp_tx_hdcp2x_write_send_ack(hdcp_info); + if (ret) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_authenticate(struct mtk_hdcp_info *hdcp_info) +{ + int ret; + + ret = dp_tx_hdcp2x_key_exchange(hdcp_info); + if (ret) + return ret; + + ret = dp_tx_hdcp2x_locality_check(hdcp_info); + if (ret) + return ret; + + ret = dp_tx_hdcp2x_session_key_exchange(hdcp_info); + if (ret) + return ret; + + if (!hdcp_info->hdcp2_info.repeater) { + ret = dp_tx_hdcp2x_write_stream_type(hdcp_info); + if (ret) + return ret; + } + + if (hdcp_info->hdcp2_info.repeater) { + ret = dp_tx_hdcp2x_authenticate_repeater(hdcp_info); + if (ret) + return ret; + } + + return 0; +} + +static +int dp_tx_hdcp2x_propagate_stream_management_info(struct mtk_hdcp_info *hdcp_info) +{ + int i, ret, tries = 3; + + if (!hdcp_info->hdcp2_info.repeater) + return 0; + + for (i = 0; i < tries; i++) { + ret = dp_tx_hdcp2x_process_rep_auth_stream_manage(hdcp_info); + if (ret) + continue; + + ret = dp_tx_hdcp2x_write_stream_manage(hdcp_info); + if (ret) + continue; + + ret = dp_tx_hdcp2x_read_rep_stream_ready(hdcp_info); + if (ret) + continue; + + ret = dp_tx_hdcp2x_recv_rep_auth_stream_ready(hdcp_info); + if (!ret) + return 0; + } + + return ret; +} + +void dp_tx_hdcp2x_get_info(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + u8 tmp[3]; + ssize_t ret; + + ret = drm_dp_dpcd_read(&mtk_dp->aux, + DP_HDCP_2_2_REG_RX_CAPS_OFFSET, tmp, HDCP_2_2_RXCAPS_LEN); + if (ret < 0) + return; + + if (!HDCP_2_2_DP_HDCP_CAPABLE(tmp[2]) || tmp[0] != HDCP_2_2_RX_CAPS_VERSION_VAL) { + hdcp_info->hdcp2_info.capable = false; + } else { + hdcp_info->hdcp2_info.capable = true; + hdcp_info->hdcp2_info.repeater = HDCP_2_2_RX_REPEATER(tmp[2]); + } + + dev_info(mtk_dp->dev, "[HDCP2.X] Capable: %d, Reapeater: %d\n", + hdcp_info->hdcp2_info.capable, + hdcp_info->hdcp2_info.repeater); +} + +int dp_tx_hdcp2x_enable(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + int ret, i, tries = 3; + + hdcp_info->auth_status = AUTH_INIT; + + ret = tee_add_device(hdcp_info, HDCP_VERSION_2X); + if (ret) + goto fail2; + + for (i = 0; i < tries; i++) { + ret = dp_tx_hdcp2x_authenticate(hdcp_info); + if (ret) + continue; + + ret = dp_tx_hdcp2x_propagate_stream_management_info(hdcp_info); + if (!ret) { + dev_dbg(mtk_dp->dev, "[HDCP2.X] Stream management done\n"); + break; + } + } + if (i == tries) + goto fail1; + + msleep(HDCP_2_2_DELAY_BEFORE_ENCRYPTION_EN); + ret = dp_tx_hdcp2x_enable_auth(hdcp_info, true); + if (!ret) { + hdcp_info->auth_version = HDCP_VERSION_2X; + hdcp_info->auth_status = AUTH_PASS; + dev_info(mtk_dp->dev, "[HDCP2.X] Authentication done\n"); + + return 0; + } + +fail1: + tee_remove_device(hdcp_info); + +fail2: + hdcp_info->auth_status = AUTH_FAIL; + dev_err(mtk_dp->dev, "[HDCP2.X] Authentication fail\n"); + + return ret; +} + +int dp_tx_hdcp2x_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 = dp_tx_hdcp2x_enable_auth(hdcp_info, false); + 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, "[HDCP2.X] Disable Authentication\n"); + + return 0; +} + +int dp_tx_hdcp2x_check_link(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + u8 rx_status; + int ret = -EINVAL; + int tmp = 0; + + 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_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, + HDCP_2_2_DP_RXSTATUS_LEN); + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) { + dev_dbg(mtk_dp->dev, "[HDCP2.X] Read bstatus failed, reauth\n"); + goto disable; + } + + if (HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(rx_status)) + tmp = REAUTH_REQUEST; + else if (HDCP_2_2_DP_RXSTATUS_LINK_FAILED(rx_status)) + tmp = LINK_INTEGRITY_FAILURE; + else if (HDCP_2_2_DP_RXSTATUS_READY(rx_status)) + tmp = TOPOLOGY_CHANGE; + + if (tmp == LINK_PROTECTED) { + mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_ENABLED); + ret = 0; + goto end; + } + + if (tmp == TOPOLOGY_CHANGE) { + ret = dp_tx_hdcp2x_authenticate_repeater(hdcp_info); + if (!ret) { + mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_ENABLED); + goto end; + } + } else { + dev_info(mtk_dp->dev, "[HDCP2.X] link failed with:0x%x, retrying auth\n", tmp); + } + +disable: + ret = dp_tx_hdcp2x_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_hdcp2x_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; +} + +void dp_tx_hdcp2x_irq(struct mtk_hdcp_info *hdcp_info) +{ + atomic_inc(&hdcp_info->hdcp2_info.cp_irq); + wake_up_all(&hdcp_info->hdcp2_info.cp_irq_queue); +} diff --git a/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h new file mode 100644 index 000000000000..0532bcb9958b --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019-2024 MediaTek Inc. + */ + +#ifndef _MTK_dp_HDCP2_H_ +#define _MTK_dp_HDCP2_H_ + +#include "tlc_dp_hdcp.h" + +#define HDCP_VERSION_2X 2 + +enum check_link { + LINK_PROTECTED = 0, + TOPOLOGY_CHANGE, + LINK_INTEGRITY_FAILURE, + REAUTH_REQUEST +}; + +void dp_tx_hdcp2x_get_info(struct mtk_hdcp_info *hdcp_info); +int dp_tx_hdcp2x_enable(struct mtk_hdcp_info *hdcp_info); +int dp_tx_hdcp2x_disabel(struct mtk_hdcp_info *hdcp_info); +int dp_tx_hdcp2x_check_link(struct mtk_hdcp_info *hdcp_info); +void dp_tx_hdcp2x_irq(struct mtk_hdcp_info *hdcp_info); +#endif /* _MTK_dp_HDCP2_H_ */ diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h index 709b79480693..8c60983a26ed 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp_reg.h +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2019-2022 MediaTek Inc. + * Copyright (c) 2019-2024 MediaTek Inc. * Copyright (c) 2022 BayLibre */ #ifndef _MTK_DP_REG_H_ @@ -83,6 +83,7 @@ #define VIDEO_MUTE_SW_DP_ENC0_P0 BIT(2) #define VIDEO_MUTE_SEL_DP_ENC0_P0 BIT(3) #define ENHANCED_FRAME_EN_DP_ENC0_P0 BIT(4) +#define HDCP_FRAME_EN_DP_ENC0_P0 BIT(5) #define MTK_DP_ENC0_P0_3004 0x3004 #define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK BIT(8) #define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0 BIT(9) @@ -245,6 +246,7 @@ /* offset: TRANS_OFFSET (0x3400) */ #define MTK_DP_TRANS_P0_3400 0x3400 +#define HDCP_SEL_DP_TRANS_P0_MASK BIT(11) #define PATTERN1_EN_DP_TRANS_P0_MASK BIT(12) #define PATTERN2_EN_DP_TRANS_P0_MASK BIT(13) #define PATTERN3_EN_DP_TRANS_P0_MASK BIT(14) @@ -276,7 +278,11 @@ #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_34A4 0x34a4 +#define HDCP22_AUTH_DONE_DP_TRANS_P0_MASK BIT(4) +#define R0_AVAILABLE_DP_TRANS_P0 BIT(12) +#define REPEATER_I_DP_TRANS_P0_MASK BIT(15) #define LANE_NUM_DP_TRANS_P0_MASK GENMASK(3, 2) +#define MTK_DP_TRANS_P0_34D0 0x34D0 #define MTK_DP_TRANS_P0_3540 0x3540 #define FEC_EN_DP_TRANS_P0_MASK BIT(0) #define FEC_CLOCK_EN_MODE_DP_TRANS_P0 BIT(3) diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c index bfe8653005db..6f85753c0c82 100644 --- a/drivers/gpu/drm/mediatek/mtk_dpi.c +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c @@ -25,6 +25,7 @@ #include <drm/drm_edid.h> #include <drm/drm_of.h> #include <drm/drm_simple_kms_helper.h> +#include <drm/display/drm_hdcp_helper.h> #include "mtk_ddp_comp.h" #include "mtk_disp_drv.h" @@ -820,6 +821,8 @@ static int mtk_dpi_bind(struct device *dev, struct device *master, void *data) } drm_connector_attach_encoder(dpi->connector, &dpi->encoder); + drm_connector_attach_content_protection_property(dpi->connector, true); + return 0; err_cleanup: -- 2.43.0 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort @ 2024-06-08 12:01 ` mac.shen 0 siblings, 0 replies; 42+ messages in thread From: mac.shen @ 2024-06-08 12:01 UTC (permalink / raw) To: chunkuang.hu, p.zabel, airlied, daniel, matthias.bgg, angelogioacchino.delregno, jitao.shi Cc: mac.shen, shuijing.li, linux-kernel, dri-devel, linux-mediatek, linux-arm-kernel Changes in v3: - refine the function to get system time - refine the flow to do HDCP with content type and protection value which set by user space - refine the flow to update content protection - refine the flow to do HDCP2.x authentication per suggestion from the previous thread: https://patchwork.kernel.org/project/linux-mediatek /patch/20240205055055.25340-3-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 | 321 +++++--- drivers/gpu/drm/mediatek/mtk_dp.h | 103 +++ drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c | 944 ++++++++++++++++++++++++ drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h | 25 + drivers/gpu/drm/mediatek/mtk_dp_reg.h | 8 +- drivers/gpu/drm/mediatek/mtk_dpi.c | 3 + 7 files changed, 1321 insertions(+), 84 deletions(-) create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.h create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile index f27ddfbd2e12..19b7625ae573 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_hdcp2.o \ mtk_dp.o obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk-dp.o diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c index 536366956447..12854a04622f 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp.c +++ b/drivers/gpu/drm/mediatek/mtk_dp.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2019-2022 MediaTek Inc. + * Copyright (c) 2019-2024 MediaTek Inc. * Copyright (c) 2022 BayLibre */ @@ -8,13 +8,13 @@ #include <drm/display/drm_dp.h> #include <drm/display/drm_dp_helper.h> #include <drm/drm_atomic_helper.h> -#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> +#include <drm/display/drm_hdcp_helper.h> #include <linux/arm-smccc.h> #include <linux/clk.h> #include <linux/delay.h> @@ -30,10 +30,10 @@ #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/soc/mediatek/mtk_sip_svc.h> -#include <sound/hdmi-codec.h> -#include <video/videomode.h> +#include "mtk_dp.h" #include "mtk_dp_reg.h" +#include "mtk_dp_hdcp2.h" #define MTK_DP_SIP_CONTROL_AARCH32 MTK_SIP_SMC_CMD(0x523) #define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE (BIT(0) | BIT(5)) @@ -52,43 +52,6 @@ #define MTK_DP_VERSION 0x11 #define MTK_DP_SDP_AUI 0x4 -enum { - MTK_DP_CAL_GLB_BIAS_TRIM = 0, - MTK_DP_CAL_CLKTX_IMPSE, - MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0, - MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1, - MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2, - MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3, - MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0, - MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1, - MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2, - MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3, - MTK_DP_CAL_MAX, -}; - -struct mtk_dp_train_info { - bool sink_ssc; - bool cable_plugged_in; - /* link_rate is in multiple of 0.27Gbps */ - int link_rate; - int lane_count; - unsigned int channel_eq_pattern; -}; - -struct mtk_dp_audio_cfg { - bool detect_monitor; - int sad_count; - int sample_rate; - int word_length_bits; - int channels; -}; - -struct mtk_dp_info { - enum dp_pixelformat format; - struct videomode vm; - struct mtk_dp_audio_cfg audio_cur_cfg; -}; - struct mtk_dp_efuse_fmt { unsigned short idx; unsigned short shift; @@ -98,44 +61,6 @@ struct mtk_dp_efuse_fmt { unsigned short default_val; }; -struct mtk_dp { - bool enabled; - bool need_debounce; - int irq; - u8 max_lanes; - u8 max_linkrate; - u8 rx_cap[DP_RECEIVER_CAP_SIZE]; - u32 cal_data[MTK_DP_CAL_MAX]; - u32 irq_thread_handle; - /* irq_thread_lock is used to protect irq_thread_handle */ - spinlock_t irq_thread_lock; - - struct device *dev; - struct drm_bridge bridge; - struct drm_bridge *next_bridge; - struct drm_connector *conn; - struct drm_device *drm_dev; - struct drm_dp_aux aux; - - const struct mtk_dp_data *data; - struct mtk_dp_info info; - struct mtk_dp_train_info train_info; - - struct platform_device *phy_dev; - struct phy *phy; - struct regmap *regs; - struct timer_list debounce_timer; - - /* For audio */ - bool audio_enable; - hdmi_codec_plugged_cb plugged_cb; - struct platform_device *audio_pdev; - - struct device *codec_dev; - /* protect the plugged_cb as it's used in both bridge ops and audio */ - struct mutex update_plugged_status_lock; -}; - struct mtk_dp_data { int bridge_type; unsigned int smc_cmd; @@ -319,12 +244,24 @@ static struct regmap_config mtk_dp_regmap_config = { .name = "mtk-dp-registers", }; +u64 mtk_dp_get_system_time(void) +{ + return ktime_get_mono_fast_ns(); +} + +u64 mtk_dp_get_time_diff(u64 pre_time) +{ + u64 post_time = mtk_dp_get_system_time(); + + return (post_time - pre_time); +} + static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b) { return container_of(b, struct mtk_dp, bridge); } -static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset) +u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset) { u32 read_val; int ret; @@ -350,8 +287,8 @@ static int mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val) return ret; } -static int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, - u32 val, u32 mask) +int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, + u32 val, u32 mask) { int ret = regmap_update_bits(mtk_dp->regs, offset, mask, val); @@ -1865,11 +1802,200 @@ static void mtk_dp_init_port(struct mtk_dp *mtk_dp) mtk_dp_digital_sw_reset(mtk_dp); } +static void mtk_dp_check_sink_esi(struct mtk_dp *mtk_dp) +{ + u8 clear_cp_irq = BIT(2); + + /* hdcp 1.x do not need irq */ + if (mtk_dp->hdcp_info.hdcp2_info.capable) { + dp_tx_hdcp2x_irq(&mtk_dp->hdcp_info); + drm_dp_dpcd_write(&mtk_dp->aux, + DP_DEVICE_SERVICE_IRQ_VECTOR, &clear_cp_irq, 0x1); + } +} + +static int mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp) +{ + ssize_t ret; + u8 sink_count; + u8 sink_count_200; + + ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT_ESI, &sink_count); + if (ret < 0) { + drm_info(mtk_dp->drm_dev, "Read sink count failed: %zd\n", ret); + return ret; + } + + ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count_200); + if (ret < 0) { + drm_info(mtk_dp->drm_dev, + "Read DP_SINK_COUNT_ESI failed: %zd\n", ret); + return ret; + } + + mtk_dp_check_sink_esi(mtk_dp); + + return 0; +} + +static void mtk_dp_hdcp_get_info(struct mtk_dp *mtk_dp) +{ + dp_tx_hdcp2x_get_info(&mtk_dp->hdcp_info); +} + +static void mtk_dp_hdcp_disable(struct mtk_dp *mtk_dp) +{ + mutex_lock(&mtk_dp->hdcp_mutex); + + if (mtk_dp->hdcp_info.auth_status == AUTH_ZERO) + goto end; + + if (mtk_dp->hdcp_info.auth_version == HDCP_VERSION_2X) + dp_tx_hdcp2x_disabel(&mtk_dp->hdcp_info); + +end: + cancel_delayed_work_sync(&mtk_dp->check_work); + mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_DESIRED); + + mutex_unlock(&mtk_dp->hdcp_mutex); +} + +static void mtk_dp_hdcp_check_work(struct work_struct *work) +{ + struct mtk_dp *mtk_dp = container_of(to_delayed_work(work), + struct mtk_dp, check_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); + } +} + +static void mtk_dp_hdcp_handle(struct work_struct *data) +{ + struct mtk_dp *mtk_dp = container_of(data, struct mtk_dp, hdcp_work); + unsigned long check_link_interval = DRM_HDCP_CHECK_PERIOD_MS; + int ret = -EINVAL; + + mutex_lock(&mtk_dp->hdcp_mutex); + + if (mtk_dp->hdcp_info.auth_status == AUTH_PASS) + goto end; + + mtk_dp_hdcp_get_info(mtk_dp); + + if (mtk_dp->hdcp_info.hdcp2_info.capable) { + ret = dp_tx_hdcp2x_enable(&mtk_dp->hdcp_info); + if (!ret) + check_link_interval = DRM_HDCP2_CHECK_PERIOD_MS; + } + + if (!ret) { + schedule_delayed_work(&mtk_dp->check_work, check_link_interval); + mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_ENABLED); + } + +end: + mutex_unlock(&mtk_dp->hdcp_mutex); +} + +static void mtk_dp_hdcp_enable(struct mtk_dp *mtk_dp) +{ + if (!mtk_dp->train_info.cable_plugged_in || !mtk_dp->enabled) + return; + + dev_info(mtk_dp->dev, "[HDCP] dp start HDCP work\n"); + queue_work(mtk_dp->hdcp_workqueue, &mtk_dp->hdcp_work); +} + +static void mtk_dp_hdcp_prop_work(struct work_struct *work) +{ + struct mtk_dp *mtk_dp; + struct drm_device *drm_dev; + + mtk_dp = container_of(work, struct mtk_dp, prop_work); + + if (!mtk_dp->conn) { + dev_err(mtk_dp->dev, "[HDCP] Connector is null\n"); + return; + } + + drm_dev = mtk_dp->conn->dev; + + drm_modeset_lock(&drm_dev->mode_config.connection_mutex, NULL); + + if (mtk_dp->hdcp_info.content_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { + dev_info(mtk_dp->dev, "[HDCP] Update CP, content protection: %d, auth status:%d\n", + mtk_dp->hdcp_info.content_protection, mtk_dp->hdcp_info.auth_status); + + /* only update between ENABLED/DESIRED */ + drm_hdcp_update_content_protection(mtk_dp->conn, + mtk_dp->hdcp_info.content_protection); + } + + drm_modeset_unlock(&drm_dev->mode_config.connection_mutex); +} + +void mtk_dp_hdcp_update_value(struct mtk_dp *mtk_dp, u32 value) +{ + if (mtk_dp->hdcp_info.content_protection == value) + return; + + mtk_dp->hdcp_info.content_protection = value; + if (mtk_dp->hdcp_info.content_protection == DRM_MODE_CONTENT_PROTECTION_UNDESIRED) + return; + + schedule_work(&mtk_dp->prop_work); +} + +static void mtk_dp_hdcp_atomic_check(struct mtk_dp *mtk_dp, struct drm_connector_state *state) +{ + unsigned int current_protection = mtk_dp->hdcp_info.content_protection; + unsigned int new_protection = state->content_protection; + bool type_changed = state->hdcp_content_type != mtk_dp->hdcp_info.hdcp_content_type; + bool protection_changed = current_protection != new_protection; + bool disable = false; + bool enable = false; + + dev_dbg(mtk_dp->dev, "[HDCP] atomic check, old[%d, %d], new[%d, %d]\n", + mtk_dp->hdcp_info.content_protection, mtk_dp->hdcp_info.hdcp_content_type, + state->content_protection, state->hdcp_content_type); + + if (type_changed) { + /* + * With the current protection as ENABLED/DESIRED, + * re-authenticate with new content type if the content type changed. + */ + mtk_dp->hdcp_info.hdcp_content_type = state->hdcp_content_type; + mtk_dp->hdcp_info.hdcp2_info.stream_id_type = state->hdcp_content_type; + + if (current_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) + disable = true; + + if (new_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) + enable = true; + } else if (protection_changed) { + if (new_protection == DRM_MODE_CONTENT_PROTECTION_UNDESIRED) + disable = true; + else + enable = true; + } + + if (disable) + mtk_dp_hdcp_disable(mtk_dp); + + if (enable) { + mtk_dp->hdcp_info.content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + mtk_dp_hdcp_enable(mtk_dp); + } +} + static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev) { struct mtk_dp *mtk_dp = dev; unsigned long flags; u32 status; + int ret; if (mtk_dp->need_debounce && mtk_dp->train_info.cable_plugged_in) msleep(100); @@ -1894,8 +2020,13 @@ static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev) } } - if (status & MTK_DP_THREAD_HPD_EVENT) + if (status & MTK_DP_THREAD_HPD_EVENT) { dev_dbg(mtk_dp->dev, "Receive IRQ from sink devices\n"); + /* check if need clear hpd irq */ + ret = mtk_dp_hpd_sink_event(mtk_dp); + if (ret) + dev_err(mtk_dp->dev, "Fail to process hpd sink event\n"); + } return IRQ_HANDLED; } @@ -2242,6 +2373,7 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge, struct drm_bridge_state *old_state) { struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge); + struct drm_connector_state *connector_state; int ret; mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state, @@ -2252,6 +2384,8 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge, return; } + connector_state = drm_atomic_get_connector_state(old_state->base.state, mtk_dp->conn); + mtk_dp_aux_panel_poweron(mtk_dp, true); /* Training */ @@ -2281,6 +2415,14 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge, mtk_dp->enabled = true; mtk_dp_update_plugged_status(mtk_dp); + /* Enable hdcp if it's desired */ + dev_dbg(mtk_dp->dev, "hdcp_content_type:%d, content protection: %d", + connector_state->hdcp_content_type, connector_state->content_protection); + if (connector_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED) { + mtk_dp->hdcp_info.hdcp_content_type = connector_state->hdcp_content_type; + mtk_dp_hdcp_enable(mtk_dp); + } + return; power_off_aux: mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, @@ -2427,6 +2569,8 @@ static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge, drm_display_mode_to_videomode(&crtc_state->adjusted_mode, &mtk_dp->info.vm); + mtk_dp_hdcp_atomic_check(mtk_dp, conn_state); + return 0; } @@ -2666,6 +2810,17 @@ static int mtk_dp_probe(struct platform_device *pdev) if (ret) return ret; + mutex_init(&mtk_dp->hdcp_mutex); + init_waitqueue_head(&mtk_dp->hdcp_info.hdcp2_info.cp_irq_queue); + INIT_WORK(&mtk_dp->hdcp_work, mtk_dp_hdcp_handle); + INIT_WORK(&mtk_dp->prop_work, mtk_dp_hdcp_prop_work); + INIT_DELAYED_WORK(&mtk_dp->check_work, mtk_dp_hdcp_check_work); + mtk_dp->hdcp_workqueue = create_workqueue("mtk_dp_hdcp_work"); + if (!mtk_dp->hdcp_workqueue) { + dev_err(mtk_dp->dev, "failed to create hdcp work queue"); + return -ENOMEM; + } + mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs; mtk_dp->bridge.of_node = dev->of_node; mtk_dp->bridge.type = mtk_dp->data->bridge_type; diff --git a/drivers/gpu/drm/mediatek/mtk_dp.h b/drivers/gpu/drm/mediatek/mtk_dp.h new file mode 100644 index 000000000000..dfeaf12c7d58 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dp.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019-2024 MediaTek Inc. + */ + +#ifndef _MTK_DP_H_ +#define _MTK_DP_H_ + +#include "tlc_dp_hdcp.h" +#include <drm/drm_bridge.h> +#include <sound/hdmi-codec.h> +#include <video/videomode.h> + +#define MS_TO_NS(x) ((x) * NSEC_PER_MSEC) + +enum { + MTK_DP_CAL_GLB_BIAS_TRIM = 0, + MTK_DP_CAL_CLKTX_IMPSE, + MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0, + MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1, + MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2, + MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3, + MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0, + MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1, + MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2, + MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3, + MTK_DP_CAL_MAX, +}; + +struct mtk_dp_audio_cfg { + bool detect_monitor; + int sad_count; + int sample_rate; + int word_length_bits; + int channels; +}; + +struct mtk_dp_info { + enum dp_pixelformat format; + struct videomode vm; + struct mtk_dp_audio_cfg audio_cur_cfg; +}; + +struct mtk_dp_train_info { + bool sink_ssc; + bool cable_plugged_in; + /* link_rate is in multiple of 0.27Gbps */ + int link_rate; + int lane_count; + unsigned int channel_eq_pattern; +}; + +struct mtk_dp { + bool enabled; + bool need_debounce; + int irq; + u8 max_lanes; + u8 max_linkrate; + u8 rx_cap[DP_RECEIVER_CAP_SIZE]; + u32 cal_data[MTK_DP_CAL_MAX]; + u32 irq_thread_handle; + /* irq_thread_lock is used to protect irq_thread_handle */ + spinlock_t irq_thread_lock; + + struct device *dev; + struct drm_bridge bridge; + struct drm_bridge *next_bridge; + struct drm_connector *conn; + struct drm_device *drm_dev; + struct drm_dp_aux aux; + + const struct mtk_dp_data *data; + struct mtk_dp_info info; + struct mtk_dp_train_info train_info; + struct mtk_hdcp_info hdcp_info; + struct work_struct hdcp_work; + struct work_struct prop_work; + struct delayed_work check_work; + struct workqueue_struct *hdcp_workqueue; + /* This mutex is used to synchronize HDCP operations in the driver */ + struct mutex hdcp_mutex; + + struct platform_device *phy_dev; + struct phy *phy; + struct regmap *regs; + struct timer_list debounce_timer; + + /* For audio */ + bool audio_enable; + hdmi_codec_plugged_cb plugged_cb; + struct platform_device *audio_pdev; + + struct device *codec_dev; + /* protect the plugged_cb as it's used in both bridge ops and audio */ + struct mutex update_plugged_status_lock; +}; + +u64 mtk_dp_get_system_time(void); +u64 mtk_dp_get_time_diff(u64 pre_time); +u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset); +int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, u32 val, u32 mask); +void mtk_dp_hdcp_update_value(struct mtk_dp *mtk_dp, u32 value); +#endif /* _MTK_DP_H_ */ diff --git a/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c new file mode 100644 index 000000000000..e8affa4343c1 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c @@ -0,0 +1,944 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019-2024 MediaTek Inc. + */ + +#include <drm/display/drm_hdcp_helper.h> + +#include "mtk_dp_hdcp2.h" +#include "mtk_dp_reg.h" +#include "mtk_dp.h" + +#define HDCP2_LC_RETRY_CNT 3 + +static u8 t_rtx[HDCP_2_2_RTX_LEN] = { + 0x18, 0xfa, 0xe4, 0x20, 0x6a, 0xfb, 0x51, 0x49 +}; + +static u8 t_tx_caps[HDCP_2_2_TXCAPS_LEN] = { + 0x02, 0x00, 0x00 +}; + +static u8 t_rn[HDCP_2_2_RN_LEN] = { + 0x32, 0x75, 0x3e, 0xa8, 0x78, 0xa6, 0x38, 0x1c +}; + +static u8 t_riv[HDCP_2_2_RIV_LEN] = { + 0x40, 0x2b, 0x6b, 0x43, 0xc5, 0xe8, 0x86, 0xd8 +}; + +static void dp_tx_hdcp2x_fill_stream_type(struct mtk_hdcp_info *hdcp_info, u8 uc_type) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + + mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34D0, uc_type, 0xff); +} + +static void dp_tx_hdcp2x_set_auth_pass(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_3400, + HDCP_SEL_DP_TRANS_P0_MASK, HDCP_SEL_DP_TRANS_P0_MASK); + mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4, + HDCP22_AUTH_DONE_DP_TRANS_P0_MASK, + HDCP22_AUTH_DONE_DP_TRANS_P0_MASK); + } else { + mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3400, 0, HDCP_SEL_DP_TRANS_P0_MASK); + mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4, 0, + HDCP22_AUTH_DONE_DP_TRANS_P0_MASK); + } +} + +static int dp_tx_hdcp2x_enable_auth(struct mtk_hdcp_info *hdcp_info, bool enable) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + u32 version; + int ret; + + dp_tx_hdcp2x_set_auth_pass(hdcp_info, enable); + if (!enable) { + ret = tee_hdcp_enable_encrypt(hdcp_info, enable, HDCP_NONE); + if (ret) + return ret; + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, 0, HDCP_FRAME_EN_DP_ENC0_P0); + + return 0; + } + + if (HDCP_2_2_HDCP1_DEVICE_CONNECTED(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[1])) + version = HDCP_V1; + else if (HDCP_2_2_HDCP_2_0_REP_CONNECTED + (hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[1])) + version = HDCP_V2; + else + version = HDCP_V2_3; + + ret = tee_hdcp_enable_encrypt(hdcp_info, enable, version); + if (ret) + return ret; + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, + HDCP_FRAME_EN_DP_ENC0_P0, HDCP_FRAME_EN_DP_ENC0_P0); + + return 0; +} + +static int dp_tx_hdcp2x_init(struct mtk_hdcp_info *hdcp_info) +{ + int ret; + + memset(&hdcp_info->hdcp2_info.hdcp_tx, 0, sizeof(struct hdcp2_info_tx)); + memset(&hdcp_info->hdcp2_info.hdcp_rx, 0, sizeof(struct hdcp2_info_rx)); + memset(&hdcp_info->hdcp2_info.ake_stored_km, 0, sizeof(struct hdcp2_ake_stored_km)); + memcpy(hdcp_info->hdcp2_info.hdcp_tx.ake_init.r_tx, t_rtx, HDCP_2_2_RTX_LEN); + memcpy(&hdcp_info->hdcp2_info.hdcp_tx.tx_caps, t_tx_caps, HDCP_2_2_TXCAPS_LEN); + memcpy(hdcp_info->hdcp2_info.hdcp_tx.lc_init.r_n, t_rn, HDCP_2_2_RN_LEN); + memcpy(hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, t_riv, HDCP_2_2_RIV_LEN); + + ret = dp_tx_hdcp2x_enable_auth(hdcp_info, false); + if (ret) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_inc_seq_num_m(struct mtk_hdcp_info *hdcp_info) +{ + u32 tmp = drm_hdcp_be24_to_cpu(hdcp_info->hdcp2_info.hdcp_tx.stream_manage.seq_num_m); + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + + if (tmp == HDCP_2_2_SEQ_NUM_MAX) { + dev_err(mtk_dp->dev, "[HDCP2.X] With seq num max\n"); + return -EINVAL; + } + + tmp++; + + drm_hdcp_cpu_to_be24(hdcp_info->hdcp2_info.hdcp_tx.stream_manage.seq_num_m, tmp); + + return 0; +} + +static int dp_tx_hdcp2x_process_rep_auth_stream_manage(struct mtk_hdcp_info *hdcp_info) +{ + hdcp_info->hdcp2_info.hdcp_tx.k[0] = 0x00; + hdcp_info->hdcp2_info.hdcp_tx.k[1] = 0x01; + + hdcp_info->hdcp2_info.hdcp_tx.stream_id_type[0] = 0x00; /* Payload ID */ + hdcp_info->hdcp2_info.hdcp_tx.stream_id_type[1] = hdcp_info->hdcp2_info.stream_id_type; + + return dp_tx_hdcp2x_inc_seq_num_m(hdcp_info); +} + +static int dp_tx_hdcp2x_recv_rep_auth_send_recv_id_list(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + u32 len = 0, len_recv_id_list = 0; + u8 *buffer = NULL; + int ret = 0; + + len_recv_id_list = hdcp_info->hdcp2_info.device_count * HDCP_2_2_RECEIVER_ID_LEN; + len = len_recv_id_list + HDCP_2_2_RXINFO_LEN + HDCP_2_2_SEQ_NUM_LEN; + buffer = kmalloc(len, GFP_KERNEL); + if (!buffer) { + dev_err(mtk_dp->dev, "[HDCP2.X] Out of memory\n"); + return -ENOMEM; + } + + memcpy(buffer, hdcp_info->hdcp2_info.hdcp_rx.recvid_list.receiver_ids, + len_recv_id_list); + memcpy(buffer + len_recv_id_list, + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info, HDCP_2_2_RXINFO_LEN); + memcpy(buffer + len_recv_id_list + HDCP_2_2_RXINFO_LEN, + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.seq_num_v, HDCP_2_2_SEQ_NUM_LEN); + + ret = tee_hdcp2_compute_compare_v(hdcp_info, buffer, len, + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.v_prime, + hdcp_info->hdcp2_info.hdcp_tx.send_ack.v); + + kfree(buffer); + + return ret; +} + +static int dp_tx_hdcp2x_recv_rep_auth_stream_ready(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + u8 *buffer = NULL; + u32 len = 0; + int ret = 0; + + len = HDCP2_STREAMID_TYPE_LEN + HDCP_2_2_SEQ_NUM_LEN; + buffer = kmalloc(len, GFP_KERNEL); + if (!buffer) { + dev_err(mtk_dp->dev, "[HDCP2.X] Out of memory\n"); + return -ENOMEM; + } + + memcpy(buffer, hdcp_info->hdcp2_info.hdcp_tx.stream_id_type, HDCP2_STREAMID_TYPE_LEN); + memcpy(buffer + HDCP2_STREAMID_TYPE_LEN, + hdcp_info->hdcp2_info.hdcp_tx.stream_manage.seq_num_m, + HDCP_2_2_SEQ_NUM_LEN); + ret = tee_hdcp2_compute_compare_m(hdcp_info, buffer, len, + hdcp_info->hdcp2_info.hdcp_rx.stream_ready.m_prime); + + kfree(buffer); + + return ret; +} + +static void dp_tx_hdcp2x_wait_for_cp_irq(struct mtk_hdcp_info *hdcp_info, int timeout) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + long ret; + +#define C (hdcp_info->hdcp2_info.cp_irq_cached != atomic_read(&hdcp_info->hdcp2_info.cp_irq)) + ret = wait_event_interruptible_timeout(hdcp_info->hdcp2_info.cp_irq_queue, C, + msecs_to_jiffies(timeout)); + if (!ret) + dev_dbg(mtk_dp->dev, "[HDCP2.X] Timedout at waiting for CP_IRQ\n"); +} + +static int dp_tx_hdcp2x_read_ake_send_cert(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ktime_t msg_end; + bool msg_expired; + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_SEND_CERT\n"); + + mdelay(HDCP_2_2_CERT_TIMEOUT_MS); + + msg_end = ktime_add_ms(ktime_get_raw(), HDCP_2_2_DP_CERT_READ_TIMEOUT_MS); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_AKE_SEND_CERT_OFFSET, + (void *)&hdcp_info->hdcp2_info.hdcp_rx.cert_rx, HDCP2_CERTRX_LEN); + if (ret < 0) + return ret; + + msg_expired = ktime_after(ktime_get_raw(), msg_end); + if (msg_expired) + dev_dbg(mtk_dp->dev, "[HDCP2.X] Timeout to read Ake send cert\n"); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RRX_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.send_cert.r_rx, HDCP_2_2_RRX_LEN); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RX_CAPS_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.send_cert.rx_caps, + HDCP_2_2_RXCAPS_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_read_ake_send_hprime(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ktime_t msg_end; + bool msg_expired; + u8 rx_status = 0; + int timeout; + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_SEND_HPRIME\n"); + + timeout = hdcp_info->hdcp2_info.stored_km ? + HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS : HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS; + + dp_tx_hdcp2x_wait_for_cp_irq(hdcp_info, timeout); + hdcp_info->hdcp2_info.cp_irq_cached = atomic_read(&hdcp_info->hdcp2_info.cp_irq); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, + HDCP_2_2_DP_RXSTATUS_LEN); + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) + return ret >= 0 ? -EIO : ret; + + if (!HDCP_2_2_DP_RXSTATUS_H_PRIME(rx_status)) + return -EAGAIN; + + msg_end = ktime_add_ms(ktime_get_raw(), HDCP_2_2_DP_HPRIME_READ_TIMEOUT_MS); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_AKE_SEND_HPRIME_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.send_hprime.h_prime, + HDCP_2_2_H_PRIME_LEN); + if (ret < 0) + return ret; + + msg_expired = ktime_after(ktime_get_raw(), msg_end); + if (msg_expired) + dev_dbg(mtk_dp->dev, "[HDCP2.X] Timeout to read AKE hprime\n"); + + return 0; +} + +static int dp_tx_hdcp2x_read_ake_send_pairing_info(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ktime_t msg_end; + bool msg_expired; + u8 rx_status = 0; + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_SEND_PAIRING_INFO\n"); + + dp_tx_hdcp2x_wait_for_cp_irq(hdcp_info, HDCP_2_2_PAIRING_TIMEOUT_MS); + hdcp_info->hdcp2_info.cp_irq_cached = atomic_read(&hdcp_info->hdcp2_info.cp_irq); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, + HDCP_2_2_DP_RXSTATUS_LEN); + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) + return ret >= 0 ? -EIO : ret; + + if (!HDCP_2_2_DP_RXSTATUS_PAIRING(rx_status)) + return -EAGAIN; + + msg_end = ktime_add_ms(ktime_get_raw(), HDCP_2_2_DP_PAIRING_READ_TIMEOUT_MS); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_AKE_SEND_PAIRING_INFO_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.pairing_info.e_kh_km, + HDCP_2_2_E_KH_KM_LEN); + if (ret < 0) + return ret; + + msg_expired = ktime_after(ktime_get_raw(), msg_end); + if (msg_expired) + dev_err(mtk_dp->dev, "[HDCP2.X] Timeout to read pairing info\n"); + + return 0; +} + +static int dp_tx_hdcp2x_read_lc_send_lprime(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_LC_SEND_LPRIME\n"); + + mdelay(HDCP_2_2_DP_LPRIME_TIMEOUT_MS); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_LC_SEND_LPRIME_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.send_lprime.l_prime, + HDCP_2_2_L_PRIME_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_read_rep_send_recvid_list(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + u8 rx_status = 0; + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_REP_SEND_RECVID_LIST\n"); + + dp_tx_hdcp2x_wait_for_cp_irq(hdcp_info, HDCP_2_2_RECVID_LIST_TIMEOUT_MS); + hdcp_info->hdcp2_info.cp_irq_cached = atomic_read(&hdcp_info->hdcp2_info.cp_irq); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, + HDCP_2_2_DP_RXSTATUS_LEN); + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) + return ret >= 0 ? -EIO : ret; + + if (!HDCP_2_2_DP_RXSTATUS_READY(rx_status)) { + dev_err(mtk_dp->dev, "[HDCP2.X] RX status no ready\n"); + return -EAGAIN; + } + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info, + HDCP_2_2_RXINFO_LEN); + if (ret < 0) + return ret; + + hdcp_info->hdcp2_info.device_count = + (HDCP_2_2_DEV_COUNT_HI(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[0]) << 4 | + HDCP_2_2_DEV_COUNT_LO(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[1])); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_SEQ_NUM_V_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.seq_num_v, + HDCP_2_2_SEQ_NUM_LEN); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_VPRIME_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.v_prime, + HDCP_2_2_V_PRIME_HALF_LEN); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RECV_ID_LIST_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.receiver_ids, + hdcp_info->hdcp2_info.device_count * HDCP_2_2_RECEIVER_ID_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_read_rep_stream_ready(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_REP_STREAM_READY\n"); + + mdelay(HDCP_2_2_STREAM_READY_TIMEOUT_MS); + + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REP_STREAM_READY_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.stream_ready.m_prime, + HDCP_2_2_MPRIME_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_write_ake_init(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_Init\n"); + + ret = tee_hdcp2_soft_rst(hdcp_info); + if (ret) + return ret; + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_AKE_INIT_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.ake_init.r_tx, HDCP_2_2_RTX_LEN); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_TXCAPS_OFFSET, + (void *)&hdcp_info->hdcp2_info.hdcp_tx.tx_caps, + HDCP_2_2_TXCAPS_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_write_ake_no_stored_km(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_NO_STORED_KM\n"); + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.no_stored_km.e_kpub_km, + HDCP_2_2_E_KPUB_KM_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_write_ake_stored_km(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_STORED_KM\n"); + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_AKE_STORED_KM_OFFSET, + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m, + HDCP_2_2_E_KH_KM_LEN); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_M_OFFSET, + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m + + HDCP_2_2_E_KH_KM_LEN, + HDCP_2_2_E_KH_KM_M_LEN - HDCP_2_2_E_KH_KM_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_write_lc_init(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_LC_INIT\n"); + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_LC_INIT_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.lc_init.r_n, HDCP_2_2_RN_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_write_ske_send_eks(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_SKE_SEND_EKS\n"); + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_SKE_SEND_EKS_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.send_eks.e_dkey_ks, + HDCP_2_2_E_DKEY_KS_LEN); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_RIV_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, HDCP_2_2_RIV_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_write_stream_type(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] Write stream type\n"); + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.stream_id_type, 1); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_write_send_ack(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_SEND_ACK\n"); + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REP_SEND_ACK_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.send_ack.v, + HDCP_2_2_V_PRIME_HALF_LEN); + if (ret < 0) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_write_stream_manage(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + ssize_t ret; + + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_STREAM_MANAGE\n"); + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REP_STREAM_MANAGE_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.stream_manage.seq_num_m, + HDCP_2_2_SEQ_NUM_LEN); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_K_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.k, HDCP2_K_LEN); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_STREAM_ID_TYPE_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.stream_id_type, + HDCP2_STREAMID_TYPE_LEN); + if (ret < 0) + return ret; + + dp_tx_hdcp2x_fill_stream_type(hdcp_info, hdcp_info->hdcp2_info.stream_id_type); + + return 0; +} + +/* Authentication flow starts from here */ +static int dp_tx_hdcp2x_key_exchange(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + bool stored; + int ret; + + if (!hdcp_info->hdcp2_info.capable) + return -EAGAIN; + + ret = dp_tx_hdcp2x_init(hdcp_info); + if (ret) + return ret; + + ret = dp_tx_hdcp2x_write_ake_init(hdcp_info); + if (ret) + return ret; + + ret = dp_tx_hdcp2x_read_ake_send_cert(hdcp_info); + if (ret) + return ret; + + hdcp_info->hdcp2_info.repeater = + HDCP_2_2_RX_REPEATER(hdcp_info->hdcp2_info.hdcp_rx.send_cert.rx_caps[2]); + + if (drm_hdcp_check_ksvs_revoked(mtk_dp->drm_dev, + hdcp_info->hdcp2_info.hdcp_rx.send_cert.cert_rx.receiver_id, + 1) > 0) { + dev_err(mtk_dp->dev, "[HDCP2.X] Receiver ID is revoked\n"); + return -EPERM; + } + + ret = tee_ake_certificate(hdcp_info, + (u8 *)&hdcp_info->hdcp2_info.hdcp_rx.cert_rx, &stored, + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m + + HDCP_2_2_E_KH_KM_LEN, + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m); + if (ret) + return ret; + + hdcp_info->hdcp2_info.stored_km = stored; + + if (!hdcp_info->hdcp2_info.stored_km) { + ret = tee_enc_rsaes_oaep(hdcp_info, + hdcp_info->hdcp2_info.hdcp_tx.no_stored_km.e_kpub_km); + if (ret) + return ret; + + ret = dp_tx_hdcp2x_write_ake_no_stored_km(hdcp_info); + if (ret) + return ret; + + } else { + ret = dp_tx_hdcp2x_write_ake_stored_km(hdcp_info); + if (ret) + return ret; + } + + ret = dp_tx_hdcp2x_read_ake_send_hprime(hdcp_info); + if (ret) + return ret; + + ret = tee_ake_h_prime(hdcp_info, + hdcp_info->hdcp2_info.hdcp_tx.ake_init.r_tx, + hdcp_info->hdcp2_info.hdcp_rx.send_cert.r_rx, + hdcp_info->hdcp2_info.hdcp_rx.send_cert.rx_caps, + (u8 *)&hdcp_info->hdcp2_info.hdcp_tx.tx_caps, + hdcp_info->hdcp2_info.hdcp_rx.send_hprime.h_prime, + HDCP_2_2_H_PRIME_LEN); + if (ret) { + if (hdcp_info->hdcp2_info.stored_km) + tee_clear_paring(hdcp_info); + return ret; + } + + if (!hdcp_info->hdcp2_info.stored_km) { + ret = dp_tx_hdcp2x_read_ake_send_pairing_info(hdcp_info); + if (ret) + return ret; + + /* Store m, km, Ekh(km) */ + ret = tee_ake_paring(hdcp_info, + hdcp_info->hdcp2_info.hdcp_rx.pairing_info.e_kh_km); + if (ret) + return ret; + } + + return 0; +} + +static int dp_tx_hdcp2x_locality_check(struct mtk_hdcp_info *hdcp_info) +{ + int ret, i, tries = HDCP2_LC_RETRY_CNT; + + for (i = 0; i < tries; i++) { + ret = dp_tx_hdcp2x_write_lc_init(hdcp_info); + if (ret) + continue; + + ret = dp_tx_hdcp2x_read_lc_send_lprime(hdcp_info); + if (ret) + continue; + + ret = tee_lc_l_prime(hdcp_info, hdcp_info->hdcp2_info.hdcp_tx.lc_init.r_n, + hdcp_info->hdcp2_info.hdcp_rx.send_lprime.l_prime, + HDCP_2_2_L_PRIME_LEN); + if (!ret) + return 0; + } + + return ret; +} + +static int dp_tx_hdcp2x_session_key_exchange(struct mtk_hdcp_info *hdcp_info) +{ + int ret; + + ret = tee_ske_enc_ks(hdcp_info, hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, + hdcp_info->hdcp2_info.hdcp_tx.send_eks.e_dkey_ks); + if (ret) + return ret; + + ret = dp_tx_hdcp2x_write_ske_send_eks(hdcp_info); + if (ret) + return ret; + + return 0; +} + +static +int dp_tx_hdcp2x_authenticate_repeater(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + u8 *rx_info; + int ret; + + ret = dp_tx_hdcp2x_read_rep_send_recvid_list(hdcp_info); + if (ret) + return ret; + + rx_info = hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info; + + if (HDCP_2_2_MAX_CASCADE_EXCEEDED(rx_info[1]) || + HDCP_2_2_MAX_DEVS_EXCEEDED(rx_info[1])) { + dev_err(mtk_dp->dev, "[HDCP2.X] Topology max size exceeded\n"); + return -EINVAL; + } + + if (drm_hdcp_check_ksvs_revoked(mtk_dp->drm_dev, + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.receiver_ids, + hdcp_info->hdcp2_info.device_count) > 0) { + dev_err(mtk_dp->dev, "[HDCP2.X] Revoked receiver ID(s) is in list\n"); + return -EPERM; + } + + ret = dp_tx_hdcp2x_recv_rep_auth_send_recv_id_list(hdcp_info); + if (ret) + return -EINVAL; + + ret = dp_tx_hdcp2x_write_send_ack(hdcp_info); + if (ret) + return ret; + + return 0; +} + +static int dp_tx_hdcp2x_authenticate(struct mtk_hdcp_info *hdcp_info) +{ + int ret; + + ret = dp_tx_hdcp2x_key_exchange(hdcp_info); + if (ret) + return ret; + + ret = dp_tx_hdcp2x_locality_check(hdcp_info); + if (ret) + return ret; + + ret = dp_tx_hdcp2x_session_key_exchange(hdcp_info); + if (ret) + return ret; + + if (!hdcp_info->hdcp2_info.repeater) { + ret = dp_tx_hdcp2x_write_stream_type(hdcp_info); + if (ret) + return ret; + } + + if (hdcp_info->hdcp2_info.repeater) { + ret = dp_tx_hdcp2x_authenticate_repeater(hdcp_info); + if (ret) + return ret; + } + + return 0; +} + +static +int dp_tx_hdcp2x_propagate_stream_management_info(struct mtk_hdcp_info *hdcp_info) +{ + int i, ret, tries = 3; + + if (!hdcp_info->hdcp2_info.repeater) + return 0; + + for (i = 0; i < tries; i++) { + ret = dp_tx_hdcp2x_process_rep_auth_stream_manage(hdcp_info); + if (ret) + continue; + + ret = dp_tx_hdcp2x_write_stream_manage(hdcp_info); + if (ret) + continue; + + ret = dp_tx_hdcp2x_read_rep_stream_ready(hdcp_info); + if (ret) + continue; + + ret = dp_tx_hdcp2x_recv_rep_auth_stream_ready(hdcp_info); + if (!ret) + return 0; + } + + return ret; +} + +void dp_tx_hdcp2x_get_info(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + u8 tmp[3]; + ssize_t ret; + + ret = drm_dp_dpcd_read(&mtk_dp->aux, + DP_HDCP_2_2_REG_RX_CAPS_OFFSET, tmp, HDCP_2_2_RXCAPS_LEN); + if (ret < 0) + return; + + if (!HDCP_2_2_DP_HDCP_CAPABLE(tmp[2]) || tmp[0] != HDCP_2_2_RX_CAPS_VERSION_VAL) { + hdcp_info->hdcp2_info.capable = false; + } else { + hdcp_info->hdcp2_info.capable = true; + hdcp_info->hdcp2_info.repeater = HDCP_2_2_RX_REPEATER(tmp[2]); + } + + dev_info(mtk_dp->dev, "[HDCP2.X] Capable: %d, Reapeater: %d\n", + hdcp_info->hdcp2_info.capable, + hdcp_info->hdcp2_info.repeater); +} + +int dp_tx_hdcp2x_enable(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + int ret, i, tries = 3; + + hdcp_info->auth_status = AUTH_INIT; + + ret = tee_add_device(hdcp_info, HDCP_VERSION_2X); + if (ret) + goto fail2; + + for (i = 0; i < tries; i++) { + ret = dp_tx_hdcp2x_authenticate(hdcp_info); + if (ret) + continue; + + ret = dp_tx_hdcp2x_propagate_stream_management_info(hdcp_info); + if (!ret) { + dev_dbg(mtk_dp->dev, "[HDCP2.X] Stream management done\n"); + break; + } + } + if (i == tries) + goto fail1; + + msleep(HDCP_2_2_DELAY_BEFORE_ENCRYPTION_EN); + ret = dp_tx_hdcp2x_enable_auth(hdcp_info, true); + if (!ret) { + hdcp_info->auth_version = HDCP_VERSION_2X; + hdcp_info->auth_status = AUTH_PASS; + dev_info(mtk_dp->dev, "[HDCP2.X] Authentication done\n"); + + return 0; + } + +fail1: + tee_remove_device(hdcp_info); + +fail2: + hdcp_info->auth_status = AUTH_FAIL; + dev_err(mtk_dp->dev, "[HDCP2.X] Authentication fail\n"); + + return ret; +} + +int dp_tx_hdcp2x_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 = dp_tx_hdcp2x_enable_auth(hdcp_info, false); + 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, "[HDCP2.X] Disable Authentication\n"); + + return 0; +} + +int dp_tx_hdcp2x_check_link(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + u8 rx_status; + int ret = -EINVAL; + int tmp = 0; + + 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_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, + HDCP_2_2_DP_RXSTATUS_LEN); + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) { + dev_dbg(mtk_dp->dev, "[HDCP2.X] Read bstatus failed, reauth\n"); + goto disable; + } + + if (HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(rx_status)) + tmp = REAUTH_REQUEST; + else if (HDCP_2_2_DP_RXSTATUS_LINK_FAILED(rx_status)) + tmp = LINK_INTEGRITY_FAILURE; + else if (HDCP_2_2_DP_RXSTATUS_READY(rx_status)) + tmp = TOPOLOGY_CHANGE; + + if (tmp == LINK_PROTECTED) { + mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_ENABLED); + ret = 0; + goto end; + } + + if (tmp == TOPOLOGY_CHANGE) { + ret = dp_tx_hdcp2x_authenticate_repeater(hdcp_info); + if (!ret) { + mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_ENABLED); + goto end; + } + } else { + dev_info(mtk_dp->dev, "[HDCP2.X] link failed with:0x%x, retrying auth\n", tmp); + } + +disable: + ret = dp_tx_hdcp2x_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_hdcp2x_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; +} + +void dp_tx_hdcp2x_irq(struct mtk_hdcp_info *hdcp_info) +{ + atomic_inc(&hdcp_info->hdcp2_info.cp_irq); + wake_up_all(&hdcp_info->hdcp2_info.cp_irq_queue); +} diff --git a/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h new file mode 100644 index 000000000000..0532bcb9958b --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019-2024 MediaTek Inc. + */ + +#ifndef _MTK_dp_HDCP2_H_ +#define _MTK_dp_HDCP2_H_ + +#include "tlc_dp_hdcp.h" + +#define HDCP_VERSION_2X 2 + +enum check_link { + LINK_PROTECTED = 0, + TOPOLOGY_CHANGE, + LINK_INTEGRITY_FAILURE, + REAUTH_REQUEST +}; + +void dp_tx_hdcp2x_get_info(struct mtk_hdcp_info *hdcp_info); +int dp_tx_hdcp2x_enable(struct mtk_hdcp_info *hdcp_info); +int dp_tx_hdcp2x_disabel(struct mtk_hdcp_info *hdcp_info); +int dp_tx_hdcp2x_check_link(struct mtk_hdcp_info *hdcp_info); +void dp_tx_hdcp2x_irq(struct mtk_hdcp_info *hdcp_info); +#endif /* _MTK_dp_HDCP2_H_ */ diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h index 709b79480693..8c60983a26ed 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp_reg.h +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2019-2022 MediaTek Inc. + * Copyright (c) 2019-2024 MediaTek Inc. * Copyright (c) 2022 BayLibre */ #ifndef _MTK_DP_REG_H_ @@ -83,6 +83,7 @@ #define VIDEO_MUTE_SW_DP_ENC0_P0 BIT(2) #define VIDEO_MUTE_SEL_DP_ENC0_P0 BIT(3) #define ENHANCED_FRAME_EN_DP_ENC0_P0 BIT(4) +#define HDCP_FRAME_EN_DP_ENC0_P0 BIT(5) #define MTK_DP_ENC0_P0_3004 0x3004 #define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK BIT(8) #define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0 BIT(9) @@ -245,6 +246,7 @@ /* offset: TRANS_OFFSET (0x3400) */ #define MTK_DP_TRANS_P0_3400 0x3400 +#define HDCP_SEL_DP_TRANS_P0_MASK BIT(11) #define PATTERN1_EN_DP_TRANS_P0_MASK BIT(12) #define PATTERN2_EN_DP_TRANS_P0_MASK BIT(13) #define PATTERN3_EN_DP_TRANS_P0_MASK BIT(14) @@ -276,7 +278,11 @@ #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_34A4 0x34a4 +#define HDCP22_AUTH_DONE_DP_TRANS_P0_MASK BIT(4) +#define R0_AVAILABLE_DP_TRANS_P0 BIT(12) +#define REPEATER_I_DP_TRANS_P0_MASK BIT(15) #define LANE_NUM_DP_TRANS_P0_MASK GENMASK(3, 2) +#define MTK_DP_TRANS_P0_34D0 0x34D0 #define MTK_DP_TRANS_P0_3540 0x3540 #define FEC_EN_DP_TRANS_P0_MASK BIT(0) #define FEC_CLOCK_EN_MODE_DP_TRANS_P0 BIT(3) diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c index bfe8653005db..6f85753c0c82 100644 --- a/drivers/gpu/drm/mediatek/mtk_dpi.c +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c @@ -25,6 +25,7 @@ #include <drm/drm_edid.h> #include <drm/drm_of.h> #include <drm/drm_simple_kms_helper.h> +#include <drm/display/drm_hdcp_helper.h> #include "mtk_ddp_comp.h" #include "mtk_disp_drv.h" @@ -820,6 +821,8 @@ static int mtk_dpi_bind(struct device *dev, struct device *master, void *data) } drm_connector_attach_encoder(dpi->connector, &dpi->encoder); + drm_connector_attach_content_protection_property(dpi->connector, true); + return 0; err_cleanup: -- 2.43.0 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-07-18 5:41 ` CK Hu (胡俊光) -1 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-07-18 5:41 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > + > +static void mtk_dp_hdcp_get_info(struct mtk_dp *mtk_dp) > +{ > + dp_tx_hdcp2x_get_info(&mtk_dp->hdcp_info); dp_tx_hdcp2x_get_info() is only called by mtk_dp_hdcp_get_info() and mtk_dp_hdcp_get_info() only call mtk_dp_hdcp_get_info(), so merge these two function. Regards, CK > +} > + ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort @ 2024-07-18 5:41 ` CK Hu (胡俊光) 0 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-07-18 5:41 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) [-- Attachment #1: Type: text/plain, Size: 995 bytes --] Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > + > +static void mtk_dp_hdcp_get_info(struct mtk_dp *mtk_dp) > +{ > + dp_tx_hdcp2x_get_info(&mtk_dp->hdcp_info); dp_tx_hdcp2x_get_info() is only called by mtk_dp_hdcp_get_info() and mtk_dp_hdcp_get_info() only call mtk_dp_hdcp_get_info(), so merge these two function. Regards, CK > +} > + [-- Attachment #2: Type: text/html, Size: 2397 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-07-18 6:23 ` CK Hu (胡俊光) -1 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-07-18 6:23 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > +/* Authentication flow starts from here */ > +static int dp_tx_hdcp2x_key_exchange(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + bool stored; > + int ret; > + > + if (!hdcp_info->hdcp2_info.capable) > + return -EAGAIN; > + > + ret = dp_tx_hdcp2x_init(hdcp_info); > + if (ret) > + return ret; > + > + ret = dp_tx_hdcp2x_write_ake_init(hdcp_info); > + if (ret) > + return ret; > + > + ret = dp_tx_hdcp2x_read_ake_send_cert(hdcp_info); > + if (ret) > + return ret; > + > + hdcp_info->hdcp2_info.repeater = > + HDCP_2_2_RX_REPEATER(hdcp_info->hdcp2_info.hdcp_rx.send_cert.rx_caps[2]); > + > + if (drm_hdcp_check_ksvs_revoked(mtk_dp->drm_dev, > + hdcp_info->hdcp2_info.hdcp_rx.send_cert.cert_rx.receiver_id, > + 1) > 0) { > + dev_err(mtk_dp->dev, "[HDCP2.X] Receiver ID is revoked\n"); > + return -EPERM; > + } > + > + ret = tee_ake_certificate(hdcp_info, > + (u8 *)&hdcp_info->hdcp2_info.hdcp_rx.cert_rx, &stored, > + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m + > + HDCP_2_2_E_KH_KM_LEN, > + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m); > + if (ret) > + return ret; > + > + hdcp_info->hdcp2_info.stored_km = stored; > + > + if (!hdcp_info->hdcp2_info.stored_km) { > + ret = tee_enc_rsaes_oaep(hdcp_info, > + hdcp_info->hdcp2_info.hdcp_tx.no_stored_km.e_kpub_km); > + if (ret) > + return ret; > + > + ret = dp_tx_hdcp2x_write_ake_no_stored_km(hdcp_info); > + if (ret) > + return ret; > + > + } else { > + ret = dp_tx_hdcp2x_write_ake_stored_km(hdcp_info); > + if (ret) > + return ret; > + } > + > + ret = dp_tx_hdcp2x_read_ake_send_hprime(hdcp_info); > + if (ret) > + return ret; > + > + ret = tee_ake_h_prime(hdcp_info, > + hdcp_info->hdcp2_info.hdcp_tx.ake_init.r_tx, > + hdcp_info->hdcp2_info.hdcp_rx.send_cert.r_rx, > + hdcp_info->hdcp2_info.hdcp_rx.send_cert.rx_caps, > + (u8 *)&hdcp_info->hdcp2_info.hdcp_tx.tx_caps, > + hdcp_info->hdcp2_info.hdcp_rx.send_hprime.h_prime, > + HDCP_2_2_H_PRIME_LEN); hdcp_info->hdcp2_info.hdcp_tx.ake_init, hdcp_info->hdcp2_info.hdcp_rx.send_cert, hdcp_info->hdcp2_info.hdcp_rx.send_hprime, hdcp_info->hdcp2_info.hdcp_tx.no_stored_km, hdcp_info->hdcp2_info.stored_km, hdcp_info->hdcp2_info.hdcp_rx.pairing_info are used only in dp_tx_hdcp2x_key_exchange(), so make them as local variable in dp_tx_hdcp2x_key_exchange(). hdcp_info->hdcp2_info.hdcp_tx.tx_caps is constant value, so use t_tx_caps[] directly instead of using hdcp_info->hdcp2_info.hdcp_tx.tx_caps. Regards, CK > + if (ret) { > + if (hdcp_info->hdcp2_info.stored_km) > + tee_clear_paring(hdcp_info); > + return ret; > + } > + > + if (!hdcp_info->hdcp2_info.stored_km) { > + ret = dp_tx_hdcp2x_read_ake_send_pairing_info(hdcp_info); > + if (ret) > + return ret; > + > + /* Store m, km, Ekh(km) */ > + ret = tee_ake_paring(hdcp_info, > + hdcp_info->hdcp2_info.hdcp_rx.pairing_info.e_kh_km); > + if (ret) > + return ret; > + } > + > + return 0; > +} > + ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort @ 2024-07-18 6:23 ` CK Hu (胡俊光) 0 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-07-18 6:23 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) [-- Attachment #1: Type: text/plain, Size: 3805 bytes --] Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > +/* Authentication flow starts from here */ > +static int dp_tx_hdcp2x_key_exchange(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + bool stored; > + int ret; > + > + if (!hdcp_info->hdcp2_info.capable) > + return -EAGAIN; > + > + ret = dp_tx_hdcp2x_init(hdcp_info); > + if (ret) > + return ret; > + > + ret = dp_tx_hdcp2x_write_ake_init(hdcp_info); > + if (ret) > + return ret; > + > + ret = dp_tx_hdcp2x_read_ake_send_cert(hdcp_info); > + if (ret) > + return ret; > + > + hdcp_info->hdcp2_info.repeater = > + HDCP_2_2_RX_REPEATER(hdcp_info->hdcp2_info.hdcp_rx.send_cert.rx_caps[2]); > + > + if (drm_hdcp_check_ksvs_revoked(mtk_dp->drm_dev, > + hdcp_info->hdcp2_info.hdcp_rx.send_cert.cert_rx.receiver_id, > + 1) > 0) { > + dev_err(mtk_dp->dev, "[HDCP2.X] Receiver ID is revoked\n"); > + return -EPERM; > + } > + > + ret = tee_ake_certificate(hdcp_info, > + (u8 *)&hdcp_info->hdcp2_info.hdcp_rx.cert_rx, &stored, > + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m + > + HDCP_2_2_E_KH_KM_LEN, > + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m); > + if (ret) > + return ret; > + > + hdcp_info->hdcp2_info.stored_km = stored; > + > + if (!hdcp_info->hdcp2_info.stored_km) { > + ret = tee_enc_rsaes_oaep(hdcp_info, > + hdcp_info->hdcp2_info.hdcp_tx.no_stored_km.e_kpub_km); > + if (ret) > + return ret; > + > + ret = dp_tx_hdcp2x_write_ake_no_stored_km(hdcp_info); > + if (ret) > + return ret; > + > + } else { > + ret = dp_tx_hdcp2x_write_ake_stored_km(hdcp_info); > + if (ret) > + return ret; > + } > + > + ret = dp_tx_hdcp2x_read_ake_send_hprime(hdcp_info); > + if (ret) > + return ret; > + > + ret = tee_ake_h_prime(hdcp_info, > + hdcp_info->hdcp2_info.hdcp_tx.ake_init.r_tx, > + hdcp_info->hdcp2_info.hdcp_rx.send_cert.r_rx, > + hdcp_info->hdcp2_info.hdcp_rx.send_cert.rx_caps, > + (u8 *)&hdcp_info->hdcp2_info.hdcp_tx.tx_caps, > + hdcp_info->hdcp2_info.hdcp_rx.send_hprime.h_prime, > + HDCP_2_2_H_PRIME_LEN); hdcp_info->hdcp2_info.hdcp_tx.ake_init, hdcp_info->hdcp2_info.hdcp_rx.send_cert, hdcp_info->hdcp2_info.hdcp_rx.send_hprime, hdcp_info->hdcp2_info.hdcp_tx.no_stored_km, hdcp_info->hdcp2_info.stored_km, hdcp_info->hdcp2_info.hdcp_rx.pairing_info are used only in dp_tx_hdcp2x_key_exchange(), so make them as local variable in dp_tx_hdcp2x_key_exchange(). hdcp_info->hdcp2_info.hdcp_tx.tx_caps is constant value, so use t_tx_caps[] directly instead of using hdcp_info->hdcp2_info.hdcp_tx.tx_caps. Regards, CK > + if (ret) { > + if (hdcp_info->hdcp2_info.stored_km) > + tee_clear_paring(hdcp_info); > + return ret; > + } > + > + if (!hdcp_info->hdcp2_info.stored_km) { > + ret = dp_tx_hdcp2x_read_ake_send_pairing_info(hdcp_info); > + if (ret) > + return ret; > + > + /* Store m, km, Ekh(km) */ > + ret = tee_ake_paring(hdcp_info, > + hdcp_info->hdcp2_info.hdcp_rx.pairing_info.e_kh_km); > + if (ret) > + return ret; > + } > + > + return 0; > +} > + [-- Attachment #2: Type: text/html, Size: 6231 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-07-18 7:48 ` CK Hu (胡俊光) -1 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-07-18 7:48 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > + > +static int dp_tx_hdcp2x_write_ake_no_stored_km(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_NO_STORED_KM\n"); > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET, > + hdcp_info->hdcp2_info.hdcp_tx.no_stored_km.e_kpub_km, > + HDCP_2_2_E_KPUB_KM_LEN); > + if (ret < 0) Directly return ret here. > + return ret; > + > + return 0; > +} > + > +static int dp_tx_hdcp2x_write_ake_stored_km(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_STORED_KM\n"); > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_AKE_STORED_KM_OFFSET, > + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m, > + HDCP_2_2_E_KH_KM_LEN); > + if (ret < 0) > + return ret; > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_M_OFFSET, > + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m + > + HDCP_2_2_E_KH_KM_LEN, > + HDCP_2_2_E_KH_KM_M_LEN - HDCP_2_2_E_KH_KM_LEN); > + if (ret < 0) Ditto. Regards, CK > + return ret; > + > + return 0; > +} > + ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort @ 2024-07-18 7:48 ` CK Hu (胡俊光) 0 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-07-18 7:48 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) [-- Attachment #1: Type: text/plain, Size: 1994 bytes --] Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > + > +static int dp_tx_hdcp2x_write_ake_no_stored_km(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_NO_STORED_KM\n"); > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET, > + hdcp_info->hdcp2_info.hdcp_tx.no_stored_km.e_kpub_km, > + HDCP_2_2_E_KPUB_KM_LEN); > + if (ret < 0) Directly return ret here. > + return ret; > + > + return 0; > +} > + > +static int dp_tx_hdcp2x_write_ake_stored_km(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_STORED_KM\n"); > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_AKE_STORED_KM_OFFSET, > + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m, > + HDCP_2_2_E_KH_KM_LEN); > + if (ret < 0) > + return ret; > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_M_OFFSET, > + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m + > + HDCP_2_2_E_KH_KM_LEN, > + HDCP_2_2_E_KH_KM_M_LEN - HDCP_2_2_E_KH_KM_LEN); > + if (ret < 0) Ditto. Regards, CK > + return ret; > + > + return 0; > +} > + [-- Attachment #2: Type: text/html, Size: 3817 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-07-19 4:05 ` CK Hu (胡俊光) -1 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-07-19 4:05 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > +static int dp_tx_hdcp2x_enable_auth(struct mtk_hdcp_info *hdcp_info, bool enable) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + u32 version; > + int ret; > + > + dp_tx_hdcp2x_set_auth_pass(hdcp_info, enable); > + if (!enable) { > + ret = tee_hdcp_enable_encrypt(hdcp_info, enable, HDCP_NONE); > + if (ret) > + return ret; > + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, 0, HDCP_FRAME_EN_DP_ENC0_P0); > + > + return 0; > + } > + > + if (HDCP_2_2_HDCP1_DEVICE_CONNECTED(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[1])) > + version = HDCP_V1; dp_tx_hdcp2x_enable_auth() is called only when HDCP 2.x, but here detect that version is 1. I don't know the specification of HDCP, is this is an error case? Or if this is a normal case, please add some comment here to explain for someone who don't know HDCP specification. Regards, CK > + else if (HDCP_2_2_HDCP_2_0_REP_CONNECTED > + (hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[1])) > + version = HDCP_V2; > + else > + version = HDCP_V2_3; > + > + ret = tee_hdcp_enable_encrypt(hdcp_info, enable, version); > + if (ret) > + return ret; > + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, > + HDCP_FRAME_EN_DP_ENC0_P0, HDCP_FRAME_EN_DP_ENC0_P0); > + > + return 0; > +} > + ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort @ 2024-07-19 4:05 ` CK Hu (胡俊光) 0 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-07-19 4:05 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) [-- Attachment #1: Type: text/plain, Size: 2023 bytes --] Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > +static int dp_tx_hdcp2x_enable_auth(struct mtk_hdcp_info *hdcp_info, bool enable) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + u32 version; > + int ret; > + > + dp_tx_hdcp2x_set_auth_pass(hdcp_info, enable); > + if (!enable) { > + ret = tee_hdcp_enable_encrypt(hdcp_info, enable, HDCP_NONE); > + if (ret) > + return ret; > + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, 0, HDCP_FRAME_EN_DP_ENC0_P0); > + > + return 0; > + } > + > + if (HDCP_2_2_HDCP1_DEVICE_CONNECTED(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[1])) > + version = HDCP_V1; dp_tx_hdcp2x_enable_auth() is called only when HDCP 2.x, but here detect that version is 1. I don't know the specification of HDCP, is this is an error case? Or if this is a normal case, please add some comment here to explain for someone who don't know HDCP specification. Regards, CK > + else if (HDCP_2_2_HDCP_2_0_REP_CONNECTED > + (hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[1])) > + version = HDCP_V2; > + else > + version = HDCP_V2_3; > + > + ret = tee_hdcp_enable_encrypt(hdcp_info, enable, version); > + if (ret) > + return ret; > + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, > + HDCP_FRAME_EN_DP_ENC0_P0, HDCP_FRAME_EN_DP_ENC0_P0); > + > + return 0; > +} > + [-- Attachment #2: Type: text/html, Size: 3889 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-07-23 7:16 ` CK Hu (胡俊光) -1 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-07-23 7:16 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > + > +static int dp_tx_hdcp2x_init(struct mtk_hdcp_info *hdcp_info) > +{ > + int ret; > + > + memset(&hdcp_info->hdcp2_info.hdcp_tx, 0, sizeof(struct hdcp2_info_tx)); > + memset(&hdcp_info->hdcp2_info.hdcp_rx, 0, sizeof(struct hdcp2_info_rx)); > + memset(&hdcp_info->hdcp2_info.ake_stored_km, 0, sizeof(struct hdcp2_ake_stored_km)); > + memcpy(hdcp_info->hdcp2_info.hdcp_tx.ake_init.r_tx, t_rtx, HDCP_2_2_RTX_LEN); > + memcpy(&hdcp_info->hdcp2_info.hdcp_tx.tx_caps, t_tx_caps, HDCP_2_2_TXCAPS_LEN); > + memcpy(hdcp_info->hdcp2_info.hdcp_tx.lc_init.r_n, t_rn, HDCP_2_2_RN_LEN); > + memcpy(hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, t_riv, HDCP_2_2_RIV_LEN); > + > + ret = dp_tx_hdcp2x_enable_auth(hdcp_info, false); By default, auth is disabled. So this is redundant. Regards, CK > + if (ret) > + return ret; > + > + return 0; > +} > + ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort @ 2024-07-23 7:16 ` CK Hu (胡俊光) 0 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-07-23 7:16 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) [-- Attachment #1: Type: text/plain, Size: 1555 bytes --] Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > + > +static int dp_tx_hdcp2x_init(struct mtk_hdcp_info *hdcp_info) > +{ > + int ret; > + > + memset(&hdcp_info->hdcp2_info.hdcp_tx, 0, sizeof(struct hdcp2_info_tx)); > + memset(&hdcp_info->hdcp2_info.hdcp_rx, 0, sizeof(struct hdcp2_info_rx)); > + memset(&hdcp_info->hdcp2_info.ake_stored_km, 0, sizeof(struct hdcp2_ake_stored_km)); > + memcpy(hdcp_info->hdcp2_info.hdcp_tx.ake_init.r_tx, t_rtx, HDCP_2_2_RTX_LEN); > + memcpy(&hdcp_info->hdcp2_info.hdcp_tx.tx_caps, t_tx_caps, HDCP_2_2_TXCAPS_LEN); > + memcpy(hdcp_info->hdcp2_info.hdcp_tx.lc_init.r_n, t_rn, HDCP_2_2_RN_LEN); > + memcpy(hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, t_riv, HDCP_2_2_RIV_LEN); > + > + ret = dp_tx_hdcp2x_enable_auth(hdcp_info, false); By default, auth is disabled. So this is redundant. Regards, CK > + if (ret) > + return ret; > + > + return 0; > +} > + [-- Attachment #2: Type: text/html, Size: 3141 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-08-12 7:05 ` CK Hu (胡俊光) -1 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-08-12 7:05 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > + > +int dp_tx_hdcp2x_check_link(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + u8 rx_status; > + int ret = -EINVAL; > + int tmp = 0; > + > + 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_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, > + HDCP_2_2_DP_RXSTATUS_LEN); > + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) { > + dev_dbg(mtk_dp->dev, "[HDCP2.X] Read bstatus failed, reauth\n"); > + goto disable; > + } > + > + if (HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(rx_status)) > + tmp = REAUTH_REQUEST; > + else if (HDCP_2_2_DP_RXSTATUS_LINK_FAILED(rx_status)) > + tmp = LINK_INTEGRITY_FAILURE; > + else if (HDCP_2_2_DP_RXSTATUS_READY(rx_status)) > + tmp = TOPOLOGY_CHANGE; > + > + if (tmp == LINK_PROTECTED) { > + mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_ENABLED); > + ret = 0; > + goto end; > + } > + > + if (tmp == TOPOLOGY_CHANGE) { > + ret = dp_tx_hdcp2x_authenticate_repeater(hdcp_info); > + if (!ret) { > + mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_ENABLED); > + goto end; > + } > + } else { > + dev_info(mtk_dp->dev, "[HDCP2.X] link failed with:0x%x, retrying auth\n", tmp); > + } > + > +disable: > + ret = dp_tx_hdcp2x_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_hdcp2x_enable(hdcp_info); When something error then go here. Why enable hdcp? Regards, CK > + if (ret) > + mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_DESIRED); > + > +end: > + mutex_unlock(&mtk_dp->hdcp_mutex); > + > + return ret; > +} > + ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort @ 2024-08-12 7:05 ` CK Hu (胡俊光) 0 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-08-12 7:05 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) [-- Attachment #1: Type: text/plain, Size: 2691 bytes --] Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > + > +int dp_tx_hdcp2x_check_link(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + u8 rx_status; > + int ret = -EINVAL; > + int tmp = 0; > + > + 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_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, > + HDCP_2_2_DP_RXSTATUS_LEN); > + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) { > + dev_dbg(mtk_dp->dev, "[HDCP2.X] Read bstatus failed, reauth\n"); > + goto disable; > + } > + > + if (HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(rx_status)) > + tmp = REAUTH_REQUEST; > + else if (HDCP_2_2_DP_RXSTATUS_LINK_FAILED(rx_status)) > + tmp = LINK_INTEGRITY_FAILURE; > + else if (HDCP_2_2_DP_RXSTATUS_READY(rx_status)) > + tmp = TOPOLOGY_CHANGE; > + > + if (tmp == LINK_PROTECTED) { > + mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_ENABLED); > + ret = 0; > + goto end; > + } > + > + if (tmp == TOPOLOGY_CHANGE) { > + ret = dp_tx_hdcp2x_authenticate_repeater(hdcp_info); > + if (!ret) { > + mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_ENABLED); > + goto end; > + } > + } else { > + dev_info(mtk_dp->dev, "[HDCP2.X] link failed with:0x%x, retrying auth\n", tmp); > + } > + > +disable: > + ret = dp_tx_hdcp2x_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_hdcp2x_enable(hdcp_info); When something error then go here. Why enable hdcp? Regards, CK > + if (ret) > + mtk_dp_hdcp_update_value(mtk_dp, DRM_MODE_CONTENT_PROTECTION_DESIRED); > + > +end: > + mutex_unlock(&mtk_dp->hdcp_mutex); > + > + return ret; > +} > + [-- Attachment #2: Type: text/html, Size: 4861 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-08-12 7:25 ` CK Hu (胡俊光) -1 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-08-12 7:25 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > @@ -2242,6 +2373,7 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge, > struct drm_bridge_state *old_state) > { > struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge); > + struct drm_connector_state *connector_state; > int ret; > > mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state, > @@ -2252,6 +2384,8 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge, > return; > } > > + connector_state = drm_atomic_get_connector_state(old_state->base.state, mtk_dp->conn); > + > mtk_dp_aux_panel_poweron(mtk_dp, true); > > /* Training */ > @@ -2281,6 +2415,14 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge, > mtk_dp->enabled = true; > mtk_dp_update_plugged_status(mtk_dp); > > + /* Enable hdcp if it's desired */ > + dev_dbg(mtk_dp->dev, "hdcp_content_type:%d, content protection: %d", > + connector_state->hdcp_content_type, connector_state->content_protection); > + if (connector_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED) { > + mtk_dp->hdcp_info.hdcp_content_type = connector_state->hdcp_content_type; > + mtk_dp_hdcp_enable(mtk_dp); > + } > + > return; > power_off_aux: > mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, > @@ -2427,6 +2569,8 @@ static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge, > > drm_display_mode_to_videomode(&crtc_state->adjusted_mode, &mtk_dp->info.vm); > > + mtk_dp_hdcp_atomic_check(mtk_dp, conn_state); > + > return 0; > } In mtk_dp_bridge_atomic_enable(), you enable hdcp. So in mtk_dp_bridge_atomic_disable(), you should disable hdcp. Regards, CK > ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort @ 2024-08-12 7:25 ` CK Hu (胡俊光) 0 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-08-12 7:25 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) [-- Attachment #1: Type: text/plain, Size: 2388 bytes --] Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > @@ -2242,6 +2373,7 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge, > struct drm_bridge_state *old_state) > { > struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge); > + struct drm_connector_state *connector_state; > int ret; > > mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state, > @@ -2252,6 +2384,8 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge, > return; > } > > + connector_state = drm_atomic_get_connector_state(old_state->base.state, mtk_dp->conn); > + > mtk_dp_aux_panel_poweron(mtk_dp, true); > > /* Training */ > @@ -2281,6 +2415,14 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge, > mtk_dp->enabled = true; > mtk_dp_update_plugged_status(mtk_dp); > > + /* Enable hdcp if it's desired */ > + dev_dbg(mtk_dp->dev, "hdcp_content_type:%d, content protection: %d", > + connector_state->hdcp_content_type, connector_state->content_protection); > + if (connector_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED) { > + mtk_dp->hdcp_info.hdcp_content_type = connector_state->hdcp_content_type; > + mtk_dp_hdcp_enable(mtk_dp); > + } > + > return; > power_off_aux: > mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, > @@ -2427,6 +2569,8 @@ static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge, > > drm_display_mode_to_videomode(&crtc_state->adjusted_mode, &mtk_dp->info.vm); > > + mtk_dp_hdcp_atomic_check(mtk_dp, conn_state); > + > return 0; > } In mtk_dp_bridge_atomic_enable(), you enable hdcp. So in mtk_dp_bridge_atomic_disable(), you should disable hdcp. Regards, CK > [-- Attachment #2: Type: text/html, Size: 4406 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-08-22 5:39 ` CK Hu (胡俊光) -1 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-08-22 5:39 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > + > +static int dp_tx_hdcp2x_write_lc_init(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_LC_INIT\n"); > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_LC_INIT_OFFSET, > + hdcp_info->hdcp2_info.hdcp_tx.lc_init.r_n, HDCP_2_2_RN_LEN); > + if (ret < 0) > + return ret; > + > + return 0; This function is called only by dp_tx_hdcp2x_locality_check() and this function just does one thing. So merge this function into dp_tx_hdcp2x_locality_check(). Regards, CK > +} > + ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort @ 2024-08-22 5:39 ` CK Hu (胡俊光) 0 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-08-22 5:39 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) [-- Attachment #1: Type: text/plain, Size: 1330 bytes --] Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > + > +static int dp_tx_hdcp2x_write_lc_init(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_LC_INIT\n"); > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_LC_INIT_OFFSET, > + hdcp_info->hdcp2_info.hdcp_tx.lc_init.r_n, HDCP_2_2_RN_LEN); > + if (ret < 0) > + return ret; > + > + return 0; This function is called only by dp_tx_hdcp2x_locality_check() and this function just does one thing. So merge this function into dp_tx_hdcp2x_locality_check(). Regards, CK > +} > + [-- Attachment #2: Type: text/html, Size: 2902 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-08-22 6:20 ` CK Hu (胡俊光) -1 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-08-22 6:20 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > + > +static int dp_tx_hdcp2x_read_lc_send_lprime(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_LC_SEND_LPRIME\n"); > + > + mdelay(HDCP_2_2_DP_LPRIME_TIMEOUT_MS); > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_LC_SEND_LPRIME_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.send_lprime.l_prime, > + HDCP_2_2_L_PRIME_LEN); > + if (ret < 0) > + return ret; > + > + return 0; This function is called only by dp_tx_hdcp2x_locality_check(), and this function just does two things. So merge this function into dp_tx_hdcp2x_locality_check(). Regards, CK > +} > + ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort @ 2024-08-22 6:20 ` CK Hu (胡俊光) 0 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-08-22 6:20 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) [-- Attachment #1: Type: text/plain, Size: 1425 bytes --] Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > + > +static int dp_tx_hdcp2x_read_lc_send_lprime(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_LC_SEND_LPRIME\n"); > + > + mdelay(HDCP_2_2_DP_LPRIME_TIMEOUT_MS); > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_LC_SEND_LPRIME_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.send_lprime.l_prime, > + HDCP_2_2_L_PRIME_LEN); > + if (ret < 0) > + return ret; > + > + return 0; This function is called only by dp_tx_hdcp2x_locality_check(), and this function just does two things. So merge this function into dp_tx_hdcp2x_locality_check(). Regards, CK > +} > + [-- Attachment #2: Type: text/html, Size: 3040 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-08-23 1:07 ` CK Hu (胡俊光) -1 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-08-23 1:07 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > +static int dp_tx_hdcp2x_write_ske_send_eks(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_SKE_SEND_EKS\n"); > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_SKE_SEND_EKS_OFFSET, > + hdcp_info->hdcp2_info.hdcp_tx.send_eks.e_dkey_ks, > + HDCP_2_2_E_DKEY_KS_LEN); > + if (ret < 0) > + return ret; > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_RIV_OFFSET, > + hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, HDCP_2_2_RIV_LEN); riv is constant, so remove hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv and use t_riv directly. > + if (ret < 0) > + return ret; > + > + return 0; > +} > + > +static int dp_tx_hdcp2x_session_key_exchange(struct mtk_hdcp_info *hdcp_info) > +{ > + int ret; > + > + ret = tee_ske_enc_ks(hdcp_info, hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, > + hdcp_info->hdcp2_info.hdcp_tx.send_eks.e_dkey_ks); Ditto. Regards, CK > + if (ret) > + return ret; > + > + ret = dp_tx_hdcp2x_write_ske_send_eks(hdcp_info); > + if (ret) > + return ret; > + > + return 0; > +} > + ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort @ 2024-08-23 1:07 ` CK Hu (胡俊光) 0 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-08-23 1:07 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) [-- Attachment #1: Type: text/plain, Size: 1894 bytes --] Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > +static int dp_tx_hdcp2x_write_ske_send_eks(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_SKE_SEND_EKS\n"); > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_SKE_SEND_EKS_OFFSET, > + hdcp_info->hdcp2_info.hdcp_tx.send_eks.e_dkey_ks, > + HDCP_2_2_E_DKEY_KS_LEN); > + if (ret < 0) > + return ret; > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_RIV_OFFSET, > + hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, HDCP_2_2_RIV_LEN); riv is constant, so remove hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv and use t_riv directly. > + if (ret < 0) > + return ret; > + > + return 0; > +} > + > +static int dp_tx_hdcp2x_session_key_exchange(struct mtk_hdcp_info *hdcp_info) > +{ > + int ret; > + > + ret = tee_ske_enc_ks(hdcp_info, hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, > + hdcp_info->hdcp2_info.hdcp_tx.send_eks.e_dkey_ks); Ditto. Regards, CK > + if (ret) > + return ret; > + > + ret = dp_tx_hdcp2x_write_ske_send_eks(hdcp_info); > + if (ret) > + return ret; > + > + return 0; > +} > + [-- Attachment #2: Type: text/html, Size: 3684 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-08-23 2:44 ` CK Hu (胡俊光) -1 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-08-23 2:44 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > +static int dp_tx_hdcp2x_write_ske_send_eks(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_SKE_SEND_EKS\n"); > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_SKE_SEND_EKS_OFFSET, > + hdcp_info->hdcp2_info.hdcp_tx.send_eks.e_dkey_ks, > + HDCP_2_2_E_DKEY_KS_LEN); > + if (ret < 0) > + return ret; > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_RIV_OFFSET, > + hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, HDCP_2_2_RIV_LEN); > + if (ret < 0) > + return ret; > + > + return 0; This function is called only by dp_tx_hdcp2x_session_key_exchange() and only does two things. So merge this function into dp_tx_hdcp2x_session_key_exchange(). Regards, CK > +} > + ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort @ 2024-08-23 2:44 ` CK Hu (胡俊光) 0 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-08-23 2:44 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) [-- Attachment #1: Type: text/plain, Size: 1542 bytes --] Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > +static int dp_tx_hdcp2x_write_ske_send_eks(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_SKE_SEND_EKS\n"); > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_SKE_SEND_EKS_OFFSET, > + hdcp_info->hdcp2_info.hdcp_tx.send_eks.e_dkey_ks, > + HDCP_2_2_E_DKEY_KS_LEN); > + if (ret < 0) > + return ret; > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_RIV_OFFSET, > + hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, HDCP_2_2_RIV_LEN); > + if (ret < 0) > + return ret; > + > + return 0; This function is called only by dp_tx_hdcp2x_session_key_exchange() and only does two things. So merge this function into dp_tx_hdcp2x_session_key_exchange(). Regards, CK > +} > + [-- Attachment #2: Type: text/html, Size: 3172 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-08-23 7:30 ` CK Hu (胡俊光) -1 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-08-23 7:30 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > +static int dp_tx_hdcp2x_read_ake_send_hprime(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + ktime_t msg_end; > + bool msg_expired; > + u8 rx_status = 0; > + int timeout; > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_SEND_HPRIME\n"); > + > + timeout = hdcp_info->hdcp2_info.stored_km ? > + HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS : HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS; > + > + dp_tx_hdcp2x_wait_for_cp_irq(hdcp_info, timeout); > + hdcp_info->hdcp2_info.cp_irq_cached = atomic_read(&hdcp_info->hdcp2_info.cp_irq); cp_irq_cached is updated always after dp_tx_hdcp2x_wait_for_cp_irq(), and cp_irq_cached is only used in dp_tx_hdcp2x_wait_for_cp_irq(), so move this update into dp_tx_hdcp2x_wait_for_cp_irq(). Regards, CK > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, > + HDCP_2_2_DP_RXSTATUS_LEN); > + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) > + return ret >= 0 ? -EIO : ret; > + > + if (!HDCP_2_2_DP_RXSTATUS_H_PRIME(rx_status)) > + return -EAGAIN; > + > + msg_end = ktime_add_ms(ktime_get_raw(), HDCP_2_2_DP_HPRIME_READ_TIMEOUT_MS); > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_AKE_SEND_HPRIME_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.send_hprime.h_prime, > + HDCP_2_2_H_PRIME_LEN); > + if (ret < 0) > + return ret; > + > + msg_expired = ktime_after(ktime_get_raw(), msg_end); > + if (msg_expired) > + dev_dbg(mtk_dp->dev, "[HDCP2.X] Timeout to read AKE hprime\n"); > + > + return 0; > +} > + ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort @ 2024-08-23 7:30 ` CK Hu (胡俊光) 0 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-08-23 7:30 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) [-- Attachment #1: Type: text/plain, Size: 2305 bytes --] Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > +static int dp_tx_hdcp2x_read_ake_send_hprime(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + ktime_t msg_end; > + bool msg_expired; > + u8 rx_status = 0; > + int timeout; > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_AKE_SEND_HPRIME\n"); > + > + timeout = hdcp_info->hdcp2_info.stored_km ? > + HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS : HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS; > + > + dp_tx_hdcp2x_wait_for_cp_irq(hdcp_info, timeout); > + hdcp_info->hdcp2_info.cp_irq_cached = atomic_read(&hdcp_info->hdcp2_info.cp_irq); cp_irq_cached is updated always after dp_tx_hdcp2x_wait_for_cp_irq(), and cp_irq_cached is only used in dp_tx_hdcp2x_wait_for_cp_irq(), so move this update into dp_tx_hdcp2x_wait_for_cp_irq(). Regards, CK > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, > + HDCP_2_2_DP_RXSTATUS_LEN); > + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) > + return ret >= 0 ? -EIO : ret; > + > + if (!HDCP_2_2_DP_RXSTATUS_H_PRIME(rx_status)) > + return -EAGAIN; > + > + msg_end = ktime_add_ms(ktime_get_raw(), HDCP_2_2_DP_HPRIME_READ_TIMEOUT_MS); > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_AKE_SEND_HPRIME_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.send_hprime.h_prime, > + HDCP_2_2_H_PRIME_LEN); > + if (ret < 0) > + return ret; > + > + msg_expired = ktime_after(ktime_get_raw(), msg_end); > + if (msg_expired) > + dev_dbg(mtk_dp->dev, "[HDCP2.X] Timeout to read AKE hprime\n"); > + > + return 0; > +} > + [-- Attachment #2: Type: text/html, Size: 4292 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-10-30 8:20 ` CK Hu (胡俊光) -1 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-10-30 8:20 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, AngeloGioacchino Del Regno Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > + > +static int dp_tx_hdcp2x_read_rep_send_recvid_list(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + u8 rx_status = 0; > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_REP_SEND_RECVID_LIST\n"); > + > + dp_tx_hdcp2x_wait_for_cp_irq(hdcp_info, HDCP_2_2_RECVID_LIST_TIMEOUT_MS); > + hdcp_info->hdcp2_info.cp_irq_cached = atomic_read(&hdcp_info->hdcp2_info.cp_irq); > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, > + HDCP_2_2_DP_RXSTATUS_LEN); > + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) > + return ret >= 0 ? -EIO : ret; > + > + if (!HDCP_2_2_DP_RXSTATUS_READY(rx_status)) { > + dev_err(mtk_dp->dev, "[HDCP2.X] RX status no ready\n"); > + return -EAGAIN; In [1], intel return -ETIMEOUT and not retry. Maybe return -ETIMEOUT is better? [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/i915/display/intel_dp_hdcp.c?h=v6.12-rc5#n428 Regards, CK > + } > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info, > + HDCP_2_2_RXINFO_LEN); > + if (ret < 0) > + return ret; > + > + hdcp_info->hdcp2_info.device_count = > + (HDCP_2_2_DEV_COUNT_HI(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[0]) << 4 | > + HDCP_2_2_DEV_COUNT_LO(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[1])); > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_SEQ_NUM_V_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.seq_num_v, > + HDCP_2_2_SEQ_NUM_LEN); > + if (ret < 0) > + return ret; > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_VPRIME_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.v_prime, > + HDCP_2_2_V_PRIME_HALF_LEN); > + if (ret < 0) > + return ret; > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RECV_ID_LIST_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.receiver_ids, > + hdcp_info->hdcp2_info.device_count * HDCP_2_2_RECEIVER_ID_LEN); > + if (ret < 0) > + return ret; > + > + return 0; > +} > + ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort @ 2024-10-30 8:20 ` CK Hu (胡俊光) 0 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-10-30 8:20 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, AngeloGioacchino Del Regno Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) [-- Attachment #1: Type: text/plain, Size: 2908 bytes --] Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > + > +static int dp_tx_hdcp2x_read_rep_send_recvid_list(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + u8 rx_status = 0; > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_REP_SEND_RECVID_LIST\n"); > + > + dp_tx_hdcp2x_wait_for_cp_irq(hdcp_info, HDCP_2_2_RECVID_LIST_TIMEOUT_MS); > + hdcp_info->hdcp2_info.cp_irq_cached = atomic_read(&hdcp_info->hdcp2_info.cp_irq); > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, > + HDCP_2_2_DP_RXSTATUS_LEN); > + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) > + return ret >= 0 ? -EIO : ret; > + > + if (!HDCP_2_2_DP_RXSTATUS_READY(rx_status)) { > + dev_err(mtk_dp->dev, "[HDCP2.X] RX status no ready\n"); > + return -EAGAIN; In [1], intel return -ETIMEOUT and not retry. Maybe return -ETIMEOUT is better? [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/i915/display/intel_dp_hdcp.c?h=v6.12-rc5#n428 Regards, CK > + } > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info, > + HDCP_2_2_RXINFO_LEN); > + if (ret < 0) > + return ret; > + > + hdcp_info->hdcp2_info.device_count = > + (HDCP_2_2_DEV_COUNT_HI(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[0]) << 4 | > + HDCP_2_2_DEV_COUNT_LO(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[1])); > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_SEQ_NUM_V_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.seq_num_v, > + HDCP_2_2_SEQ_NUM_LEN); > + if (ret < 0) > + return ret; > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_VPRIME_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.v_prime, > + HDCP_2_2_V_PRIME_HALF_LEN); > + if (ret < 0) > + return ret; > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RECV_ID_LIST_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.receiver_ids, > + hdcp_info->hdcp2_info.device_count * HDCP_2_2_RECEIVER_ID_LEN); > + if (ret < 0) > + return ret; > + > + return 0; > +} > + [-- Attachment #2: Type: text/html, Size: 5138 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-11-01 7:18 ` CK Hu (胡俊光) -1 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-11-01 7:18 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, AngeloGioacchino Del Regno Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > + > +static int dp_tx_hdcp2x_read_rep_send_recvid_list(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + u8 rx_status = 0; > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_REP_SEND_RECVID_LIST\n"); > + > + dp_tx_hdcp2x_wait_for_cp_irq(hdcp_info, HDCP_2_2_RECVID_LIST_TIMEOUT_MS); > + hdcp_info->hdcp2_info.cp_irq_cached = atomic_read(&hdcp_info->hdcp2_info.cp_irq); > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, > + HDCP_2_2_DP_RXSTATUS_LEN); > + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) > + return ret >= 0 ? -EIO : ret; > + > + if (!HDCP_2_2_DP_RXSTATUS_READY(rx_status)) { > + dev_err(mtk_dp->dev, "[HDCP2.X] RX status no ready\n"); > + return -EAGAIN; > + } > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info, > + HDCP_2_2_RXINFO_LEN); In [1], intel use DP_HDCP_2_2_REG_RXINFO_OFFSET to get device count. But here you use DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET to get device count. So both command can get device count? Or something wrong? [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/i915/display/intel_dp_hdcp.c?h=v6.12-rc5#n497 Regards, CK > + if (ret < 0) > + return ret; > + > + hdcp_info->hdcp2_info.device_count = > + (HDCP_2_2_DEV_COUNT_HI(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[0]) << 4 | > + HDCP_2_2_DEV_COUNT_LO(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[1])); > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_SEQ_NUM_V_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.seq_num_v, > + HDCP_2_2_SEQ_NUM_LEN); > + if (ret < 0) > + return ret; > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_VPRIME_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.v_prime, > + HDCP_2_2_V_PRIME_HALF_LEN); > + if (ret < 0) > + return ret; > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RECV_ID_LIST_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.receiver_ids, > + hdcp_info->hdcp2_info.device_count * HDCP_2_2_RECEIVER_ID_LEN); > + if (ret < 0) > + return ret; > + > + return 0; > +} > + ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort @ 2024-11-01 7:18 ` CK Hu (胡俊光) 0 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-11-01 7:18 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, AngeloGioacchino Del Regno Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) [-- Attachment #1: Type: text/plain, Size: 3037 bytes --] Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > + > +static int dp_tx_hdcp2x_read_rep_send_recvid_list(struct mtk_hdcp_info *hdcp_info) > +{ > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + u8 rx_status = 0; > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_REP_SEND_RECVID_LIST\n"); > + > + dp_tx_hdcp2x_wait_for_cp_irq(hdcp_info, HDCP_2_2_RECVID_LIST_TIMEOUT_MS); > + hdcp_info->hdcp2_info.cp_irq_cached = atomic_read(&hdcp_info->hdcp2_info.cp_irq); > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, > + HDCP_2_2_DP_RXSTATUS_LEN); > + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) > + return ret >= 0 ? -EIO : ret; > + > + if (!HDCP_2_2_DP_RXSTATUS_READY(rx_status)) { > + dev_err(mtk_dp->dev, "[HDCP2.X] RX status no ready\n"); > + return -EAGAIN; > + } > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info, > + HDCP_2_2_RXINFO_LEN); In [1], intel use DP_HDCP_2_2_REG_RXINFO_OFFSET to get device count. But here you use DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET to get device count. So both command can get device count? Or something wrong? [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/i915/display/intel_dp_hdcp.c?h=v6.12-rc5#n497 Regards, CK > + if (ret < 0) > + return ret; > + > + hdcp_info->hdcp2_info.device_count = > + (HDCP_2_2_DEV_COUNT_HI(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[0]) << 4 | > + HDCP_2_2_DEV_COUNT_LO(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[1])); > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_SEQ_NUM_V_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.seq_num_v, > + HDCP_2_2_SEQ_NUM_LEN); > + if (ret < 0) > + return ret; > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_VPRIME_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.v_prime, > + HDCP_2_2_V_PRIME_HALF_LEN); > + if (ret < 0) > + return ret; > + > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RECV_ID_LIST_OFFSET, > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.receiver_ids, > + hdcp_info->hdcp2_info.device_count * HDCP_2_2_RECEIVER_ID_LEN); > + if (ret < 0) > + return ret; > + > + return 0; > +} > + [-- Attachment #2: Type: text/html, Size: 5327 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort 2024-11-01 7:18 ` CK Hu (胡俊光) @ 2024-11-07 7:05 ` CK Hu (胡俊光) -1 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-11-07 7:05 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, AngeloGioacchino Del Regno Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) Hi, Mac: On Fri, 2024-11-01 at 15:18 +0800, CK Hu wrote: > Hi, Mac: > > On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > > Changes in v3: > > - refine the function to get system time > > - refine the flow to do HDCP with content type and > > protection value which set by user space > > - refine the flow to update content protection > > - refine the flow to do HDCP2.x authentication > > per suggestion from the previous thread: > > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > > --- > > [snip] > > > + > > +static int dp_tx_hdcp2x_read_rep_send_recvid_list(struct mtk_hdcp_info *hdcp_info) > > +{ > > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > > + u8 rx_status = 0; > > + ssize_t ret; > > + > > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_REP_SEND_RECVID_LIST\n"); > > + > > + dp_tx_hdcp2x_wait_for_cp_irq(hdcp_info, HDCP_2_2_RECVID_LIST_TIMEOUT_MS); > > + hdcp_info->hdcp2_info.cp_irq_cached = atomic_read(&hdcp_info->hdcp2_info.cp_irq); > > + > > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, > > + HDCP_2_2_DP_RXSTATUS_LEN); > > + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) > > + return ret >= 0 ? -EIO : ret; > > + > > + if (!HDCP_2_2_DP_RXSTATUS_READY(rx_status)) { > > + dev_err(mtk_dp->dev, "[HDCP2.X] RX status no ready\n"); > > + return -EAGAIN; > > + } > > + > > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET, > > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info, > > + HDCP_2_2_RXINFO_LEN); > > In [1], intel use DP_HDCP_2_2_REG_RXINFO_OFFSET to get device count. > But here you use DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET to get device count. > So both command can get device count? Or something wrong? Forget my previous comment. In [2], intel use DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET to get device count. [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/i915/display/intel_hdcp.c?h=v6.12-rc6#n1749 Regards, CK > > [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/i915/display/intel_dp_hdcp.c?h=v6.12-rc5#n497 > > Regards, > CK > > > + if (ret < 0) > > + return ret; > > + > > + hdcp_info->hdcp2_info.device_count = > > + (HDCP_2_2_DEV_COUNT_HI(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[0]) << 4 | > > + HDCP_2_2_DEV_COUNT_LO(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[1])); > > + > > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_SEQ_NUM_V_OFFSET, > > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.seq_num_v, > > + HDCP_2_2_SEQ_NUM_LEN); > > + if (ret < 0) > > + return ret; > > + > > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_VPRIME_OFFSET, > > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.v_prime, > > + HDCP_2_2_V_PRIME_HALF_LEN); > > + if (ret < 0) > > + return ret; > > + > > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RECV_ID_LIST_OFFSET, > > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.receiver_ids, > > + hdcp_info->hdcp2_info.device_count * HDCP_2_2_RECEIVER_ID_LEN); > > + if (ret < 0) > > + return ret; > > + > > + return 0; > > +} > > + > > ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort @ 2024-11-07 7:05 ` CK Hu (胡俊光) 0 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-11-07 7:05 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, AngeloGioacchino Del Regno Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) [-- Attachment #1: Type: text/plain, Size: 3533 bytes --] Hi, Mac: On Fri, 2024-11-01 at 15:18 +0800, CK Hu wrote: > Hi, Mac: > > On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > > Changes in v3: > > - refine the function to get system time > > - refine the flow to do HDCP with content type and > > protection value which set by user space > > - refine the flow to update content protection > > - refine the flow to do HDCP2.x authentication > > per suggestion from the previous thread: > > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > > --- > > [snip] > > > + > > +static int dp_tx_hdcp2x_read_rep_send_recvid_list(struct mtk_hdcp_info *hdcp_info) > > +{ > > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > > + u8 rx_status = 0; > > + ssize_t ret; > > + > > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_REP_SEND_RECVID_LIST\n"); > > + > > + dp_tx_hdcp2x_wait_for_cp_irq(hdcp_info, HDCP_2_2_RECVID_LIST_TIMEOUT_MS); > > + hdcp_info->hdcp2_info.cp_irq_cached = atomic_read(&hdcp_info->hdcp2_info.cp_irq); > > + > > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, > > + HDCP_2_2_DP_RXSTATUS_LEN); > > + if (ret != HDCP_2_2_DP_RXSTATUS_LEN) > > + return ret >= 0 ? -EIO : ret; > > + > > + if (!HDCP_2_2_DP_RXSTATUS_READY(rx_status)) { > > + dev_err(mtk_dp->dev, "[HDCP2.X] RX status no ready\n"); > > + return -EAGAIN; > > + } > > + > > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET, > > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info, > > + HDCP_2_2_RXINFO_LEN); > > In [1], intel use DP_HDCP_2_2_REG_RXINFO_OFFSET to get device count. > But here you use DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET to get device count. > So both command can get device count? Or something wrong? Forget my previous comment. In [2], intel use DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET to get device count. [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/i915/display/intel_hdcp.c?h=v6.12-rc6#n1749 Regards, CK > > [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/i915/display/intel_dp_hdcp.c?h=v6.12-rc5#n497 > > Regards, > CK > > > + if (ret < 0) > > + return ret; > > + > > + hdcp_info->hdcp2_info.device_count = > > + (HDCP_2_2_DEV_COUNT_HI(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[0]) << 4 | > > + HDCP_2_2_DEV_COUNT_LO(hdcp_info->hdcp2_info.hdcp_rx.recvid_list.rx_info[1])); > > + > > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_SEQ_NUM_V_OFFSET, > > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.seq_num_v, > > + HDCP_2_2_SEQ_NUM_LEN); > > + if (ret < 0) > > + return ret; > > + > > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_VPRIME_OFFSET, > > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.v_prime, > > + HDCP_2_2_V_PRIME_HALF_LEN); > > + if (ret < 0) > > + return ret; > > + > > + ret = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RECV_ID_LIST_OFFSET, > > + hdcp_info->hdcp2_info.hdcp_rx.recvid_list.receiver_ids, > > + hdcp_info->hdcp2_info.device_count * HDCP_2_2_RECEIVER_ID_LEN); > > + if (ret < 0) > > + return ret; > > + > > + return 0; > > +} > > + > > [-- Attachment #2: Type: text/html, Size: 6492 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-11-01 9:01 ` CK Hu (胡俊光) -1 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-11-01 9:01 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, AngeloGioacchino Del Regno Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > +static int dp_tx_hdcp2x_write_send_ack(struct mtk_hdcp_info *hdcp_info) > +{ This function is only called by one place and this function just does one thing, so merge this function into caller function. Regards, CK > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_SEND_ACK\n"); > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REP_SEND_ACK_OFFSET, > + hdcp_info->hdcp2_info.hdcp_tx.send_ack.v, > + HDCP_2_2_V_PRIME_HALF_LEN); > + if (ret < 0) > + return ret; > + > + return 0; > +} > + ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 2/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP2.x feature for DisplayPort @ 2024-11-01 9:01 ` CK Hu (胡俊光) 0 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-11-01 9:01 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, AngeloGioacchino Del Regno Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) [-- Attachment #1: Type: text/plain, Size: 1314 bytes --] Hi, Mac: On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - refine the function to get system time > - refine the flow to do HDCP with content type and > protection value which set by user space > - refine the flow to update content protection > - refine the flow to do HDCP2.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!md6xUiYN88D2YmETs8FQgaExM2zH8S1SmAEU9GRchwpNsXOyDFul3ziVKhRpCJaj8Rcn-gvM-801runjwA$ > /patch/20240205055055.25340-3-mac.shen@mediatek.com/ > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [snip] > +static int dp_tx_hdcp2x_write_send_ack(struct mtk_hdcp_info *hdcp_info) > +{ This function is only called by one place and this function just does one thing, so merge this function into caller function. Regards, CK > + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); > + ssize_t ret; > + > + dev_dbg(mtk_dp->dev, "[HDCP2.X] HDCP_2_2_SEND_ACK\n"); > + > + ret = drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REP_SEND_ACK_OFFSET, > + hdcp_info->hdcp2_info.hdcp_tx.send_ack.v, > + HDCP_2_2_V_PRIME_HALF_LEN); > + if (ret < 0) > + return ret; > + > + return 0; > +} > + [-- Attachment #2: Type: text/html, Size: 2896 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH v3 3/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP1.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-06-08 12:01 ` mac.shen -1 siblings, 0 replies; 42+ messages in thread From: mac.shen @ 2024-06-08 12:01 UTC (permalink / raw) To: chunkuang.hu, p.zabel, airlied, daniel, matthias.bgg, angelogioacchino.delregno, jitao.shi Cc: mac.shen, shuijing.li, linux-kernel, dri-devel, linux-mediatek, linux-arm-kernel 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 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH v3 3/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP1.x feature for DisplayPort @ 2024-06-08 12:01 ` mac.shen 0 siblings, 0 replies; 42+ messages in thread From: mac.shen @ 2024-06-08 12:01 UTC (permalink / raw) To: chunkuang.hu, p.zabel, airlied, daniel, matthias.bgg, angelogioacchino.delregno, jitao.shi Cc: mac.shen, shuijing.li, linux-kernel, dri-devel, linux-mediatek, linux-arm-kernel 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 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [PATCH v3 3/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP1.x feature for DisplayPort 2024-06-08 12:01 ` mac.shen @ 2024-08-30 9:03 ` CK Hu (胡俊光) -1 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-08-30 9:03 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - remove useless code > - remove magic number > - refine the flow to do HDCP1.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!l3mVePuKN038rd5XoCuION0zP40BtWBb7148hHsXs7hCWDTRgESIkfNHuEtPQ1Rb4P9MOpJZstgE4j2LJg$ > /patch/20240205055055.25340-4-mac.shen@mediatek.com/ Move the version change information after '---'. In here, describe HDCP1.x. Does MediaTek DP has some limitation? If so, describe it. I would like to HDCP1.x patch in front of HDCP2.x because the spec development is from 1.x to 2.x. So move this patch before HDCP2.x patch. Regards, CK > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 3/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP1.x feature for DisplayPort @ 2024-08-30 9:03 ` CK Hu (胡俊光) 0 siblings, 0 replies; 42+ messages in thread From: CK Hu (胡俊光) @ 2024-08-30 9:03 UTC (permalink / raw) To: Mac Shen (沈俊), chunkuang.hu@kernel.org, Jitao Shi (石记涛), daniel@ffwll.ch, p.zabel@pengutronix.de, airlied@gmail.com, matthias.bgg@gmail.com, angelogioacchino.delregno@collabora.com Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Shuijing Li (李水静) [-- Attachment #1: Type: text/plain, Size: 829 bytes --] On Sat, 2024-06-08 at 20:01 +0800, mac.shen wrote: > Changes in v3: > - remove useless code > - remove magic number > - refine the flow to do HDCP1.x authentication > per suggestion from the previous thread: > https://urldefense.com/v3/__https://patchwork.kernel.org/project/linux-mediatek__;!!CTRNKA9wMg0ARbw!l3mVePuKN038rd5XoCuION0zP40BtWBb7148hHsXs7hCWDTRgESIkfNHuEtPQ1Rb4P9MOpJZstgE4j2LJg$ > /patch/20240205055055.25340-4-mac.shen@mediatek.com/ Move the version change information after '---'. In here, describe HDCP1.x. Does MediaTek DP has some limitation? If so, describe it. I would like to HDCP1.x patch in front of HDCP2.x because the spec development is from 1.x to 2.x. So move this patch before HDCP2.x patch. Regards, CK > > Signed-off-by: mac.shen <mac.shen@mediatek.com> > --- [-- Attachment #2: Type: text/html, Size: 2165 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
end of thread, other threads:[~2024-11-07 7:07 UTC | newest] Thread overview: 42+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-06-08 12:01 [PATCH v3 0/3] Add HDCP feature for DisplayPort mac.shen 2024-06-08 12:01 ` 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 ` 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-06-08 12:01 ` mac.shen 2024-07-18 5:41 ` CK Hu (胡俊光) 2024-07-18 5:41 ` CK Hu (胡俊光) 2024-07-18 6:23 ` CK Hu (胡俊光) 2024-07-18 6:23 ` CK Hu (胡俊光) 2024-07-18 7:48 ` CK Hu (胡俊光) 2024-07-18 7:48 ` CK Hu (胡俊光) 2024-07-19 4:05 ` CK Hu (胡俊光) 2024-07-19 4:05 ` CK Hu (胡俊光) 2024-07-23 7:16 ` CK Hu (胡俊光) 2024-07-23 7:16 ` CK Hu (胡俊光) 2024-08-12 7:05 ` CK Hu (胡俊光) 2024-08-12 7:05 ` CK Hu (胡俊光) 2024-08-12 7:25 ` CK Hu (胡俊光) 2024-08-12 7:25 ` CK Hu (胡俊光) 2024-08-22 5:39 ` CK Hu (胡俊光) 2024-08-22 5:39 ` CK Hu (胡俊光) 2024-08-22 6:20 ` CK Hu (胡俊光) 2024-08-22 6:20 ` CK Hu (胡俊光) 2024-08-23 1:07 ` CK Hu (胡俊光) 2024-08-23 1:07 ` CK Hu (胡俊光) 2024-08-23 2:44 ` CK Hu (胡俊光) 2024-08-23 2:44 ` CK Hu (胡俊光) 2024-08-23 7:30 ` CK Hu (胡俊光) 2024-08-23 7:30 ` CK Hu (胡俊光) 2024-10-30 8:20 ` CK Hu (胡俊光) 2024-10-30 8:20 ` CK Hu (胡俊光) 2024-11-01 7:18 ` CK Hu (胡俊光) 2024-11-01 7:18 ` CK Hu (胡俊光) 2024-11-07 7:05 ` CK Hu (胡俊光) 2024-11-07 7:05 ` CK Hu (胡俊光) 2024-11-01 9:01 ` CK Hu (胡俊光) 2024-11-01 9:01 ` CK Hu (胡俊光) 2024-06-08 12:01 ` [PATCH v3 3/3] Subject: [PATCH] drm/mediatek/dp: Add HDCP1.x " mac.shen 2024-06-08 12:01 ` mac.shen 2024-08-30 9:03 ` CK Hu (胡俊光) 2024-08-30 9:03 ` CK Hu (胡俊光)
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.