Add tee client application, HDCP 1.x and 2.x authentication for DisplayPort to support the HDCP feature. Signed-off-by: mac.shen <mac.shen@xxxxxxxxxxxx> --- drivers/gpu/drm/mediatek/Makefile | 7 +- drivers/gpu/drm/mediatek/ca/tci.h | 143 +++ drivers/gpu/drm/mediatek/ca/tlDPHdcpCMD.h | 36 + drivers/gpu/drm/mediatek/ca/tlcDpHdcp.c | 638 +++++++++++++ drivers/gpu/drm/mediatek/ca/tlcDpHdcp.h | 305 +++++++ drivers/gpu/drm/mediatek/mtk_dp.c | 159 +++- drivers/gpu/drm/mediatek/mtk_dp.h | 17 + drivers/gpu/drm/mediatek/mtk_dp_hdcp.h | 154 ++++ drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.c | 646 +++++++++++++ drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h | 55 ++ drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c | 1008 +++++++++++++++++++++ drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h | 75 ++ drivers/gpu/drm/mediatek/mtk_dp_reg.h | 6 +- 13 files changed, 3233 insertions(+), 16 deletions(-) create mode 100644 drivers/gpu/drm/mediatek/ca/tci.h create mode 100644 drivers/gpu/drm/mediatek/ca/tlDPHdcpCMD.h create mode 100644 drivers/gpu/drm/mediatek/ca/tlcDpHdcp.c create mode 100644 drivers/gpu/drm/mediatek/ca/tlcDpHdcp.h create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.h create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp.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 diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile index d4d193f60271..6839c96221e3 100644 --- a/drivers/gpu/drm/mediatek/Makefile +++ b/drivers/gpu/drm/mediatek/Makefile @@ -26,4 +26,9 @@ 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 := mtk_dp_hdcp1x.o \ + mtk_dp_hdcp2.o \ + ca/tlcDpHdcp.o \ + mtk_dp.o + +obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk-dp.o diff --git a/drivers/gpu/drm/mediatek/ca/tci.h b/drivers/gpu/drm/mediatek/ca/tci.h new file mode 100644 index 000000000000..527f77d9308d --- /dev/null +++ b/drivers/gpu/drm/mediatek/ca/tci.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019-2023 MediaTek Inc. + */ + +#ifndef _TCI_H_ +#define _TCI_H_ + +#define RET_COMPARE_PASS 0 +#define RET_COMPARE_FAIL 1 +#define RET_NEW_DEVICE 2 +#define RET_STORED_DEVICE 3 + +#define AN_LEN 8 +#define AKSV_LEN 5 +#define BKSV_LEN 5 +#define CERT_LEN 522 +#define EKM_LEN 16 +#define M_LEN 16 +#define ENC_KM_LEN 128 +#define RXX_LEN 8 +#define CAPS_LEN 3 +#define RN_LEN 8 +#define RIV_LEN 8 + +#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 + +#define TYPE_HDCP13_KEY 20 +#define TYPE_HDCP22_KEY 21 + +#define TCI_LENGTH sizeof(struct tci_t) + +struct cryptokeys_t { + u8 type; + u32 len; + u32 key; +}; + +struct cmd_hdcp_init_for_verion_t { + u32 version; + bool need_load_key; +}; + +struct cmd_hdcp_write_val_t { + u8 type; + u8 len; + u32 val; +}; + +struct cmd_hdcp_calculate_lm_t { + u8 bksv[BKSV_LEN]; +}; + +struct cmd_hdcp_get_aksv_t { + u8 aksv[AKSV_LEN]; +}; + +struct cmd_hdcp_sha1_t { + u32 message_len; + u32 message_addr; +}; + +struct cmd_hdcp_ake_certificate_t { + u8 certification[CERT_LEN]; + bool stored; + u8 m[M_LEN]; + u8 ekm[EKM_LEN]; +}; + +struct cmd_hdcp_ake_paring_t { + u8 ekm[EKM_LEN]; +}; + +struct cmd_hdcp_enc_km_t { + u8 enc_km[ENC_KM_LEN]; +}; + +struct cmd_hdcp_ake_h_prime_t { + u8 rtx[RXX_LEN]; + u8 rrx[RXX_LEN]; + u8 rx_caps[CAPS_LEN]; + u8 tx_caps[CAPS_LEN]; + u32 rx_h_len; + u32 rx_h; +}; + +struct cmd_hdcp_lc_l_prime_t { + u8 rn[RN_LEN]; + u32 rx_l_len; + u32 rx_l; +}; + +struct cmd_hdcp_ske_eks_t { + u8 riv[RIV_LEN]; + u32 eks_len; + u32 eks; +}; + +struct cmd_hdcp_compare_t { + u32 rx_val_len; + u32 rx_val; + u32 param_len; + u32 param; + 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/ca/tlDPHdcpCMD.h b/drivers/gpu/drm/mediatek/ca/tlDPHdcpCMD.h new file mode 100644 index 000000000000..5687aec9bdd9 --- /dev/null +++ b/drivers/gpu/drm/mediatek/ca/tlDPHdcpCMD.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019-2023 MediaTek Inc. + */ + +#ifndef _TLDPHDCP_CMD_H_ +#define _TLDPHDCP_CMD_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.3 */ +#define CMD_CALCULATE_LM 11 +#define CMD_COMPARE_R0 12 +#define CMD_COMPARE_V1 13 +#define CMD_GET_AKSV 14 + +/* V2.2 */ +#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_COMPARE_L 25 +#define CMD_SKE_CAL_EKS 26 + +#define CMD_COMPARE_V2 27 +#define CMD_COMPARE_M 28 + +/* Need remove in furture */ +#define CMD_LOAD_KEY 50 + +#endif /* _TLDPHDCP_CMD_H_ */ diff --git a/drivers/gpu/drm/mediatek/ca/tlcDpHdcp.c b/drivers/gpu/drm/mediatek/ca/tlcDpHdcp.c new file mode 100644 index 000000000000..2979d38ee230 --- /dev/null +++ b/drivers/gpu/drm/mediatek/ca/tlcDpHdcp.c @@ -0,0 +1,638 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019-2023 MediaTek Inc. + */ + +#include <linux/types.h> +#include <linux/string.h> +#include <linux/slab.h> +#include "tlcDpHdcp.h" + +#define AN_LEN 8 +#define AKSV_LEN 5 +#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); + +/** + * dp_tee_op_send() - send dp commands through the TEE shared memory. + * @chip: the tpm_chip description as specified in driver/char/tpm/tpm.h + * @buf: the buffer to send. + * @len: the number of bytes to send. + * + * Return: + * In case of success, returns 0. + * On failure, -errno + */ +static int dp_tee_op_send(struct dp_tee_private *dp_tee_priv, u8 *buf, size_t len, u32 cmd_id) +{ + int rc; + u8 *temp_buf; + struct tee_ioctl_invoke_arg transceive_args; + struct tee_param command_params[4]; + struct tee_shm *shm = dp_tee_priv->shm; + + if (len > MAX_COMMAND_SIZE) { + TLCERR("%s: len=%zd exceeds MAX_COMMAND_SIZE supported by dp TA\n", __func__, len); + return -EIO; + } + + 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, + }, + }; + + temp_buf = tee_shm_get_va(shm, 0); + if (IS_ERR(temp_buf)) { + TLCERR("%s: tee_shm_get_va failed for transmit\n", __func__); + return PTR_ERR(temp_buf); + } + + memcpy(temp_buf, buf, len); + + 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) { + TLCERR("%s: invoke error: 0x%x\n", __func__, transceive_args.ret); + return (rc < 0) ? rc : transceive_args.ret; + } + + temp_buf = tee_shm_get_va(shm, command_params[1].u.memref.shm_offs); + if (IS_ERR(temp_buf)) { + TLCERR("%s: tee_shm_get_va failed for receive\n", __func__); + return PTR_ERR(temp_buf); + } + + /* 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. + */ +static int dp_tee_match(struct tee_ioctl_version_data *ver, const void *data) +{ + /* + * Currently this driver only support GP Complaint OPTEE based dp TA + */ + if (ver->impl_id == TEE_IMPL_ID_OPTEE && ver->gen_caps & TEE_GEN_CAP_GP) + return 1; + else + return 0; +} + +int tee_add_device(struct mtk_hdcp_info *hdcp_info, u32 version) +{ + int rc; + struct dp_tee_private *dp_tee_priv = NULL; + struct tee_ioctl_open_session_arg sess_arg; + struct tci_t *tci; + + if (hdcp_info->g_init) + tee_remove_device(hdcp_info); + + dp_tee_priv = kzalloc(sizeof(*dp_tee_priv), GFP_KERNEL); + if (!dp_tee_priv) { + kfree(dp_tee_priv); + TLCERR("%s: tee_alloc_memory failed\n", __func__); + 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)) { + if (PTR_ERR(dp_tee_priv->ctx) == -ENOENT) { + kfree(dp_tee_priv); + return -EPROBE_DEFER; + } + kfree(dp_tee_priv); + TLCERR("%s: tee_client_open_context failed\n", __func__); + return PTR_ERR(dp_tee_priv->ctx); + } + + /* 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) { + kfree(dp_tee_priv); + TLCERR("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)) { + kfree(dp_tee_priv); + TLCERR("%s: tee_shm_alloc_kernel_buf failed\n", __func__); + rc = -ENOMEM; + goto out_shm_alloc; + } + TLCINFO("Register 8k share memory successfully, (%p)", + dp_tee_priv->shm->kaddr); + + /* 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, (u8 *)tci, TCI_LENGTH, CMD_DEVICE_ADDED); + if (rc != 0) { + TLCERR("tee_op_send failed, error=%x\n", rc); + tee_remove_device(hdcp_info); + return rc; + } + + hdcp_info->g_init = true; + + return rc; + +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); + + return rc; +} + +void tee_remove_device(struct mtk_hdcp_info *hdcp_info) +{ + int rc; + 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; + + 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, (u8 *)tci, TCI_LENGTH, CMD_DEVICE_REMOVE); + if (rc != 0) + TLCERR("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) +{ + int rc; + 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; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_DEVICE_CLEAN; + rc = dp_tee_op_send(dp_tee_priv, (u8 *)tci, TCI_LENGTH, CMD_DEVICE_CLEAN); + if (rc != 0) { + TLCERR("tee_op_send failed, error=%x\n", rc); + return rc; + } + + return rc; +} + +int tee_hdcp1x_set_tx_an(struct mtk_hdcp_info *hdcp_info, u8 *an_code) +{ + int rc; + 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 = (u8 *)dp_tee_priv->shm->kaddr; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_WRITE_VAL; + tci->cmd_body.cmd_hdcp_write_val.len = AN_LEN; + tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_PARAM_AN; + memcpy(share_buffer + TCI_LENGTH, an_code, AN_LEN); + + rc = dp_tee_op_send(dp_tee_priv, (u8 *)tci, TCI_LENGTH + AN_LEN, CMD_WRITE_VAL); + if (rc != 0) { + TLCERR("tee_op_send failed, error=%x\n", rc); + return rc; + } + + return rc; +} + +int tee_hdcp_enable_encrypt(struct mtk_hdcp_info *hdcp_info, bool enable, u8 version) +{ + int rc; + 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 = (u8 *)dp_tee_priv->shm->kaddr; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_ENABLE_ENCRYPT; + if (enable) + tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_ENABLE_ENCRYPT; + else + tci->cmd_body.cmd_hdcp_write_val.type = TYPE_HDCP_DISABLE_ENCRYPT; + + /* Set HDCP version supportted by device */ + tci->cmd_body.cmd_hdcp_write_val.len = 1; + memset(share_buffer + TCI_LENGTH, version, 1); + + rc = dp_tee_op_send(dp_tee_priv, (u8 *)tci, TCI_LENGTH + 1, CMD_ENABLE_ENCRYPT); + if (rc != 0) { + TLCERR("tee_op_send failed, error=%x\n", rc); + return rc; + } + + return rc; +} + +int tee_hdcp1x_soft_rst(struct mtk_hdcp_info *hdcp_info) +{ + int rc; + 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 = (u8 *)dp_tee_priv->shm->kaddr; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + 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(share_buffer + TCI_LENGTH, DEFAULT_WRITE_VAL, DEFAULT_WRITE_VAL_LEN); + + rc = dp_tee_op_send(dp_tee_priv, (u8 *)tci, TCI_LENGTH + + DEFAULT_WRITE_VAL_LEN, CMD_WRITE_VAL); + if (rc != 0) { + TLCERR("tee_op_send failed, error=%x\n", rc); + return rc; + } + + return rc; +} + +int tee_hdcp2_soft_rst(struct mtk_hdcp_info *hdcp_info) +{ + int rc; + 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 = (u8 *)dp_tee_priv->shm->kaddr; + + memset(tci, 0, TCI_LENGTH); + 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(share_buffer + TCI_LENGTH, DEFAULT_WRITE_VAL, DEFAULT_WRITE_VAL_LEN); + + rc = dp_tee_op_send(dp_tee_priv, (u8 *)tci, TCI_LENGTH + + DEFAULT_WRITE_VAL_LEN, CMD_WRITE_VAL); + if (rc != 0) { + TLCERR("tee_op_send failed, error=%x\n", rc); + return rc; + } + + return rc; +} + +/** V1.X **/ +int tee_get_aksv(struct mtk_hdcp_info *hdcp_info, u8 *aksv) +{ + int rc; + 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; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_GET_AKSV; + + rc = dp_tee_op_send(dp_tee_priv, (u8 *)tci, TCI_LENGTH, CMD_GET_AKSV); + if (rc != 0) { + TLCERR("tee_op_send failed, error=%x\n", rc); + return rc; + } + + tci = (struct tci_t *)dp_tee_priv->resp_buf; + memcpy(aksv, tci->cmd_body.cmd_hdcp_get_aksv.aksv, AKSV_LEN); + + return rc; +} + +int tee_calculate_lm(struct mtk_hdcp_info *hdcp_info, u8 *bksv) +{ + int rc; + 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; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_CALCULATE_LM; + memcpy(tci->cmd_body.cmd_hdcp_calculate_lm.bksv, bksv, BKSV_LEN); + + rc = dp_tee_op_send(dp_tee_priv, (u8 *)tci, TCI_LENGTH, CMD_CALCULATE_LM); + if (rc != 0) { + TLCERR("tee_op_send failed, error=%x\n", rc); + return rc; + } + + return rc; +} + +int tee_compare_r0(struct mtk_hdcp_info *hdcp_info, u8 *r0, u32 len) +{ + int rc; + 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 = (u8 *)dp_tee_priv->shm->kaddr; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_COMPARE_R0; + tci->cmd_body.cmd_hdcp_compare.rx_val_len = len; + memcpy(share_buffer + TCI_LENGTH, r0, len); + + rc = dp_tee_op_send(dp_tee_priv, (u8 *)tci, TCI_LENGTH + len, CMD_COMPARE_R0); + if (rc != 0) { + TLCERR("tee_op_send failed, error=%x\n", rc); + return rc; + } + + return rc; +} + +int tee_hdcp1x_compute_compare_v(struct mtk_hdcp_info *hdcp_info, + u8 *crypto_param, u32 param_len, u8 *rx_v) +{ + int rc; + 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 = (u8 *)dp_tee_priv->shm->kaddr; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + 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(share_buffer + TCI_LENGTH, rx_v, 20); + memcpy(share_buffer + TCI_LENGTH + 20, crypto_param, param_len); + + rc = dp_tee_op_send(dp_tee_priv, (u8 *)tci, TCI_LENGTH + param_len + 20, CMD_COMPARE_V1); + if (rc != 0) { + TLCERR("tee_op_send failed, error=%x\n", rc); + return rc; + } + + return rc; +} + +/** V2.X **/ +int tee_ake_certificate(struct mtk_hdcp_info *hdcp_info, + u8 *certificate, bool *stored, u8 *out_m, u8 *out_ekm) +{ + int rc; + 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; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_AKE_CERTIFICATE; + memcpy(tci->cmd_body.cmd_hdcp_ake_certificate.certification, + certificate, CERT_LEN); + + rc = dp_tee_op_send(dp_tee_priv, (u8 *)tci, TCI_LENGTH, CMD_AKE_CERTIFICATE); + if (rc != 0) { + TLCERR("tee_op_send failed, error=%x\n", rc); + return rc; + } + + TLCINFO("verify signature: result %d", rc); + tci = (struct tci_t *)dp_tee_priv->resp_buf; + *stored = tci->cmd_body.cmd_hdcp_ake_certificate.stored; + memcpy(out_m, tci->cmd_body.cmd_hdcp_ake_certificate.m, M_LEN); + memcpy(out_ekm, tci->cmd_body.cmd_hdcp_ake_certificate.ekm, EKM_LEN); + + return rc; +} + +int tee_enc_rsaes_oaep(struct mtk_hdcp_info *hdcp_info, u8 *ekm) +{ + int rc; + 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; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_ENC_KM; + + rc = dp_tee_op_send(dp_tee_priv, (u8 *)tci, TCI_LENGTH, CMD_ENC_KM); + if (rc != 0) { + TLCERR("tee_op_send failed, error=%x\n", rc); + return rc; + } + + tci = (struct tci_t *)dp_tee_priv->resp_buf; + memcpy(ekm, tci->cmd_body.cmd_hdcp_enc_km.enc_km, ENC_KM_LEN); + + return rc; +} + +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 rc; + 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 = (u8 *)dp_tee_priv->shm->kaddr; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + 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, RXX_LEN); + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rrx, rrx, RXX_LEN); + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.rx_caps, rx_caps, CAPS_LEN); + memcpy(tci->cmd_body.cmd_hdcp_ake_h_prime.tx_caps, tx_caps, CAPS_LEN); + memcpy(share_buffer + TCI_LENGTH, rx_h, rx_h_len); + + rc = dp_tee_op_send(dp_tee_priv, (u8 *)tci, TCI_LENGTH + rx_h_len, CMD_AKE_H_PRIME); + if (rc != 0) { + TLCERR("tee_op_send failed, error=%x\n", rc); + return rc; + } + + tci = (struct tci_t *)dp_tee_priv->resp_buf; + return tci->return_code; +} + +int tee_ake_paring(struct mtk_hdcp_info *hdcp_info, u8 *rx_ekm) +{ + int rc; + 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; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_AKE_PARING; + memcpy(tci->cmd_body.cmd_hdcp_ake_paring.ekm, rx_ekm, EKM_LEN); + + rc = dp_tee_op_send(dp_tee_priv, (u8 *)tci, TCI_LENGTH, CMD_AKE_PARING); + if (rc != 0) + TLCERR("tee_op_send failed, error=%x\n", rc); + + return rc; +} + +int tee_lc_l_prime(struct mtk_hdcp_info *hdcp_info, u8 *rn, u8 *rx_l, u32 len) +{ + int rc; + 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 = (u8 *)dp_tee_priv->shm->kaddr; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_LC_L_PRIME; + memcpy(tci->cmd_body.cmd_hdcp_lc_l_prime.rn, rn, RN_LEN); + tci->cmd_body.cmd_hdcp_lc_l_prime.rx_l_len = len; + memcpy(share_buffer + TCI_LENGTH, rx_l, len); + + rc = dp_tee_op_send(dp_tee_priv, (u8 *)tci, TCI_LENGTH + len, CMD_LC_L_PRIME); + if (rc != 0) { + TLCERR("tee_op_send failed, error=%x\n", rc); + return rc; + } + + tci = (struct tci_t *)dp_tee_priv->resp_buf; + return tci->return_code; +} + +int tee_ske_enc_ks(struct mtk_hdcp_info *hdcp_info, u8 *riv, u8 *eks) +{ + int rc; + 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 = (u8 *)dp_tee_priv->shm->kaddr; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_SKE_CAL_EKS; + memcpy(tci->cmd_body.cmd_hdcp_ske_eks.riv, riv, RIV_LEN); + + rc = dp_tee_op_send(dp_tee_priv, (u8 *)tci, TCI_LENGTH + 16, CMD_SKE_CAL_EKS); + if (rc != 0) + TLCERR("tee_op_send failed, error=%x\n", rc); + + share_buffer = (u8 *)dp_tee_priv->resp_buf; + memcpy(eks, share_buffer + TCI_LENGTH, 16); + + return rc; +} + +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 rc; + 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 = (u8 *)dp_tee_priv->shm->kaddr; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + 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(share_buffer + TCI_LENGTH, rx_v, 16); + memcpy(share_buffer + TCI_LENGTH + 16, crypto_param, param_len); + + rc = dp_tee_op_send(dp_tee_priv, (u8 *)tci, TCI_LENGTH + param_len + 16, CMD_COMPARE_V2); + if (rc != 0) { + TLCERR("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 rc; +} + +int tee_hdcp2_compute_compare_m(struct mtk_hdcp_info *hdcp_info, + u8 *crypto_param, u32 param_len, u8 *rx_m) +{ + int rc; + 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 = (u8 *)dp_tee_priv->shm->kaddr; + + /* Copy parameters */ + memset(tci, 0, TCI_LENGTH); + tci->command_id = CMD_COMPARE_M; + tci->cmd_body.cmd_hdcp_compare.rx_val_len = 32; + tci->cmd_body.cmd_hdcp_compare.param_len = param_len; + memcpy(share_buffer + TCI_LENGTH, rx_m, 32); + memcpy(share_buffer + TCI_LENGTH + 32, crypto_param, param_len); + + rc = dp_tee_op_send(dp_tee_priv, (u8 *)tci, TCI_LENGTH + param_len + 32, CMD_COMPARE_M); + if (rc != 0) + TLCERR("tee_op_send failed, error=%x\n", rc); + + return rc; +} + diff --git a/drivers/gpu/drm/mediatek/ca/tlcDpHdcp.h b/drivers/gpu/drm/mediatek/ca/tlcDpHdcp.h new file mode 100644 index 000000000000..72e713dd96bf --- /dev/null +++ b/drivers/gpu/drm/mediatek/ca/tlcDpHdcp.h @@ -0,0 +1,305 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019-2023 MediaTek Inc. + */ + +#ifndef _TLCDPHDCP_H_ +#define _TLCDPHDCP_H_ + +#include <linux/printk.h> +#include <linux/tee_drv.h> +#include <linux/uuid.h> +#include "tci.h" +#include "tlDPHdcpCMD.h" +#include "mtk_dp_hdcp.h" + +#define TLCINFO(string, args...) pr_info("[TLC_HDCP]info: "string, ##args) +#define TLCERR(string, args...) pr_info("[TLC_HDCP]line:%d,err:%s:"string,\ + __LINE__, __func__, ##args) + +#define RET_SUCCESS 0 + +/** + * 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_1 0x3 // HDCP version 2.1 Type 1 +#define HDCP_V2_2 0x4 // HDCP version 2.2 Type 1 +#define HDCP_V2_3 0x5 // HDCP version 2.3 Type 1 + +/* Local display only(content required version use only) */ +#define HDCP_LOCAL_DISPLAY_ONLY 0xf +#define HDCP_NO_DIGITAL_OUTPUT 0xff // No digital output +#define HDCP_DEFAULT HDCP_NO_DIGITAL_OUTPUT // Default value + +#define HDCP_VERSION_1X 1 +#define HDCP_VERSION_2X 2 + +/* max. buffer size supported by dp */ +#define MAX_COMMAND_SIZE 4096 +#define MAX_RESPONSE_SIZE 4096 + +struct dp_header { + __be16 tag; + __be32 length; + union { + __be32 ordinal; + __be32 return_code; + }; +} __packed; + +/** + * struct dp_tee_private - fTPM's private data + * @session: dp TA session identifier. + * @resp_len: cached response buffer length. + * @resp_buf: cached response buffer. + * @ctx: TEE context handler. + * @shm: Memory pool shared with fTPM TA in TEE. + */ +struct dp_tee_private { + u32 session; + size_t resp_len; + u8 resp_buf[MAX_RESPONSE_SIZE]; + struct tee_context *ctx; + struct tee_shm *shm; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* + *Description: + * A device connect and do some initializations. + * + *Input: + * version: HDCP version + * + *Returns: + * TEEC_SUCCESS success* + */ +int tee_add_device(struct mtk_hdcp_info *hdcp_info, u32 version); + +/* + *Description: + * Device disconnect. + * + *Returns: + * N/A + */ +void tee_remove_device(struct mtk_hdcp_info *hdcp_info); + +/* + *Description: + * Clearing paring info. + * + *Returns: + * TEEC_SUCCESS success* + */ +int tee_clear_paring(struct mtk_hdcp_info *hdcp_info); + +/* + *Description: + * Calculate Km base on Bksv and write it to HW. + * + *Input: + * bksv[5] input + * + *Returns: + * TEEC_SUCCESS success + */ +int tee_calculate_lm(struct mtk_hdcp_info *hdcp_info, u8 *bksv); + +/* + *Description: + * Get Aksv from TEE. + * + *Output: + * aksv[5] + * + *Returns: + * TEEC_SUCCESS success + */ +int tee_get_aksv(struct mtk_hdcp_info *hdcp_info, u8 *aksv); + +/* + *Description: + * Get r0 from HW and compare to rx_r0. + * + *Parameters: + * r0[len] input + * + *Returns: + * TEEC_SUCCESS success + */ +int tee_compare_r0(struct mtk_hdcp_info *hdcp_info, u8 *r0, u32 len); + +/* + *Description: + * Compute and compare v value. + * + *Input: + * crypto_param[param_len] params used to calculate + * rx_v[20] v value from rx + * + *Returns: + * RET_COMPARE_PASS verify pass + */ +int tee_hdcp1x_compute_compare_v(struct mtk_hdcp_info *hdcp_info, + u8 *crypto_param, u32 param_len, u8 *rx_v); + +/* + *Description: + * Write An to HW. + * + *Input: + * an_code[8] + * + *Returns: + * TEEC_SUCCESS success + */ +int tee_hdcp1x_set_tx_an(struct mtk_hdcp_info *hdcp_info, u8 *an_code); + +/* + *Description: + * Write RST to HW. + * + *Returns: + * TEEC_SUCCESS success + */ +int tee_hdcp1x_soft_rst(struct mtk_hdcp_info *hdcp_info); +int tee_hdcp2_soft_rst(struct mtk_hdcp_info *hdcp_info); + +/* + *Description: + * Set enable or disable to HW. + * + *Returns: + * TEEC_SUCCESS success + */ +int tee_hdcp_enable_encrypt(struct mtk_hdcp_info *hdcp_info, bool enable, u8 version); + +/* + *Description: + * AKE cetificate verify. + * + *Input: + * certificate[522]: cert use to calculate + *output: + * stored: whether be stored before + * out_m[16] + * out_ekm[16] + * + *Returns: + * TEEC_SUCCESS success* + */ +int tee_ake_certificate(struct mtk_hdcp_info *hdcp_info, + u8 *certificate, bool *stored, u8 *out_m, u8 *out_ekm); + +/* + *Description: + * Encrypt km. + * + *Output: + * ekm[128]: encrypted km + * + *Returns: + * TEEC_SUCCESS success* + */ +int tee_enc_rsaes_oaep(struct mtk_hdcp_info *hdcp_info, u8 *ekm); + +/* + *Description: + * Calculate h prime and compare to rx_h + * + *Input: + * rtx[8] + * rrx[8] + * rx_caps[3] + * tx_caps[3] + * rx_h[rx_h_len] + * + *Returns: + * RET_COMPARE_PASS: compare pass + */ +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); + +/* + *Description: + * Store paring info. + * + *Input: + * rx_ekm[16] + * + *Returns: + * TEEC_SUCCESS success* + */ +int tee_ake_paring(struct mtk_hdcp_info *hdcp_info, u8 *rx_ekm); + +/* + *Description: + * Calculate l prime and compare. + * + *Input: + * rn[8] + * rx_l[len] + * + *Returns: + * RET_COMPARE_PASS compare pass + */ +int tee_lc_l_prime(struct mtk_hdcp_info *hdcp_info, u8 *rn, u8 *rx_l, u32 len); + +/* + *Description: + * Encrypt ks + * Write contentkey and riv to hw + * + *Input: + * riv[8] + *Output: + * eks[16] + * + *Returns: + * TEEC_SUCCESS success* + */ +int tee_ske_enc_ks(struct mtk_hdcp_info *hdcp_info, u8 *riv, u8 *eks); + +/* + *Description: + * Calculate and compare v prime for repeater. + * + *Input: + * crypto_param[param_len] params used to calculate + * rx_v[16] v value from rx + *Output: + * tx_v[16] + * + *Returns: + * TEEC_SUCCESS success* + */ +int tee_hdcp2_compute_compare_v(struct mtk_hdcp_info *hdcp_info, + u8 *crypto_param, u32 param_len, u8 *rx_v, u8 *tx_v); + +/* + *Description: + * Calculate and compare m prime for repeater. + * + *Input: + * crypto_param[param_len] params used to calculate + * rx_m[32] m value from rx + * + *Returns: + * RET_COMPARE_PASS verify pass + */ +int tee_hdcp2_compute_compare_m(struct mtk_hdcp_info *hdcp_info, + u8 *crypto_param, u32 param_len, u8 *rx_m); + +#ifdef __cplusplus +} +#endif + +#endif /* _TLCDPHDCP_H_ */ diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c index e4c16ba9902d..a5fa682f2b3c 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-2023 MediaTek Inc. * Copyright (c) 2022 BayLibre */ @@ -33,7 +33,12 @@ #include <sound/hdmi-codec.h> #include <video/videomode.h> +#include "mtk_dp.h" #include "mtk_dp_reg.h" +#include "mtk_dp_hdcp.h" +#include "mtk_dp_hdcp1x.h" +#include "mtk_dp_hdcp2.h" +#include "ca/tlcDpHdcp.h" #define MTK_DP_SIP_CONTROL_AARCH32 MTK_SIP_SMC_CMD(0x523) #define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE (BIT(0) | BIT(5)) @@ -120,6 +125,9 @@ struct mtk_dp { 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 workqueue_struct *hdcp_workqueue; struct platform_device *phy_dev; struct phy *phy; @@ -324,21 +332,25 @@ 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_reg_read(struct regmap *regs, u32 offset) { u32 read_val; int ret; - ret = regmap_read(mtk_dp->regs, offset, &read_val); + ret = regmap_read(regs, offset, &read_val); if (ret) { - dev_err(mtk_dp->dev, "Failed to read register 0x%x: %d\n", - offset, ret); + pr_err("Failed to read register 0x%x: %d\n", offset, ret); return 0; } return read_val; } +u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset) +{ + return mtk_dp_reg_read(mtk_dp->regs, offset); +} + static int mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val) { int ret = regmap_write(mtk_dp->regs, offset, val); @@ -350,18 +362,23 @@ 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_reg_update_bits(struct regmap *regs, u32 offset, + u32 val, u32 mask) { - int ret = regmap_update_bits(mtk_dp->regs, offset, mask, val); + int ret = regmap_update_bits(regs, offset, mask, val); if (ret) - dev_err(mtk_dp->dev, - "Failed to update register 0x%x with value 0x%x, mask 0x%x\n", - offset, val, mask); + pr_err("Failed to update register 0x%x with value 0x%x, mask 0x%x\n", + offset, val, mask); return ret; } +int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, + u32 val, u32 mask) +{ + return mtk_dp_reg_update_bits(mtk_dp->regs, offset, val, mask); +} + static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32 offset, u8 *buf, size_t length) { @@ -1865,6 +1882,40 @@ 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); + + if (mtk_dp->hdcp_info.hdcp2_info.enable) { + mdrv_dp_tx_hdcp2_irq(&mtk_dp->hdcp_info); + drm_dp_dpcd_write(mtk_dp->hdcp_info.aux, + DP_DEVICE_SERVICE_IRQ_VECTOR, &clear_cp_irq, 0x1); + } + /*hdcp 1.x do not need irq*/ +} + +static void 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: %ld\n", ret); + return; + } + + 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: %ld\n", ret); + return; + } + + mtk_dp_check_sink_esi(mtk_dp); +} + static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev) { struct mtk_dp *mtk_dp = dev; @@ -1894,9 +1945,11 @@ 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*/ + mtk_dp_hpd_sink_event(mtk_dp); + } return IRQ_HANDLED; } @@ -2271,7 +2324,7 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge, mtk_dp->enabled = true; mtk_dp_update_plugged_status(mtk_dp); - + mtk_dp_re_authentication(&mtk_dp->hdcp_info); return; power_off_aux: mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, @@ -2284,6 +2337,11 @@ static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge, { struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge); + if (mtk_dp->hdcp_info.hdcp2_info.enable) + mdrv_dp_tx_hdcp2_set_start_auth(&mtk_dp->hdcp_info, false); + else if (mtk_dp->hdcp_info.hdcp1x_info.enable) + mdrv_dp_tx_hdcp1x_set_start_auth(&mtk_dp->hdcp_info, false); + mtk_dp->enabled = false; mtk_dp_update_plugged_status(mtk_dp); mtk_dp_video_enable(mtk_dp, false); @@ -2589,6 +2647,69 @@ static int mtk_dp_edp_link_panel(struct drm_dp_aux *mtk_aux) return 0; } +void mtk_dp_re_authentication(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + + if (!mtk_dp->train_info.cable_plugged_in) + return; + + hdcp_info->auth_status = AUTH_PREPARE; + + dev_info(mtk_dp->dev, "dp start HDCP work"); + queue_work(mtk_dp->hdcp_workqueue, &mtk_dp->hdcp_work); +} + +void mtk_dp_check_hdcp_version(struct mtk_dp *mtk_dp, bool only_hdcp1x) +{ + if (!only_hdcp1x && mdrv_dp_tx_hdcp2_support(&mtk_dp->hdcp_info)) + return; + + if (mdrv_dp_tx_hdcp1x_support(&mtk_dp->hdcp_info)) + return; + + if (tee_add_device(&mtk_dp->hdcp_info, HDCP_NONE) != RET_SUCCESS) + mtk_dp->hdcp_info.auth_status = AUTH_FAIL; +} + +static void mtk_dp_hdcp_handle(struct work_struct *data) +{ + struct mtk_dp *mtk_dp = container_of(data, struct mtk_dp, hdcp_work); + + if (!mtk_dp->train_info.cable_plugged_in) + return; + + if (mtk_dp->hdcp_info.auth_status == AUTH_PREPARE) { + mtk_dp_check_hdcp_version(mtk_dp, false); + if (mtk_dp->hdcp_info.hdcp2_info.enable) + mdrv_dp_tx_hdcp2_set_start_auth(&mtk_dp->hdcp_info, true); + else if (mtk_dp->hdcp_info.hdcp1x_info.enable) + mdrv_dp_tx_hdcp1x_set_start_auth(&mtk_dp->hdcp_info, true); + else + mtk_dp->hdcp_info.auth_status = AUTH_ZERO; + } + + while ((mtk_dp->hdcp_info.hdcp1x_info.enable || + mtk_dp->hdcp_info.hdcp2_info.enable) && + mtk_dp->hdcp_info.auth_status != AUTH_FAIL && + mtk_dp->hdcp_info.auth_status != AUTH_PASS) { + if (mtk_dp->hdcp_info.hdcp2_info.enable) { + mdrv_dp_tx_hdcp2_fsm(&mtk_dp->hdcp_info); + if (mtk_dp->hdcp_info.auth_status == AUTH_FAIL) { + tee_remove_device(&mtk_dp->hdcp_info); + mtk_dp_check_hdcp_version(mtk_dp, true); + if (mtk_dp->hdcp_info.hdcp1x_info.enable) { + mtk_dp->hdcp_info.hdcp2_info.enable = false; + mdrv_dp_tx_hdcp1x_set_start_auth(&mtk_dp->hdcp_info, true); + } + } + } + + if (mtk_dp->hdcp_info.hdcp1x_info.enable) + mdrv_dp_tx_hdcp1x_fsm(&mtk_dp->hdcp_info); + } +} + static int mtk_dp_probe(struct platform_device *pdev) { struct mtk_dp *mtk_dp; @@ -2657,6 +2778,16 @@ static int mtk_dp_probe(struct platform_device *pdev) if (ret) return ret; + INIT_WORK(&mtk_dp->hdcp_work, mtk_dp_hdcp_handle); + 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->hdcp_info.aux = &mtk_dp->aux; + mtk_dp->hdcp_info.regs = mtk_dp->regs; + 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..3eeb33664598 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dp.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019-2023 MediaTek Inc. + */ + +#ifndef _MTK_DP_H_ +#define _MTK_DP_H_ + +#include "mtk_dp_hdcp.h" + +u32 mtk_dp_reg_read(struct regmap *regs, u32 offset); + +int mtk_dp_reg_update_bits(struct regmap *regs, u32 offset, u32 val, u32 mask); + +void mtk_dp_re_authentication(struct mtk_hdcp_info *hdcp_info); + +#endif /* _MTK_DP_H_ */ diff --git a/drivers/gpu/drm/mediatek/mtk_dp_hdcp.h b/drivers/gpu/drm/mediatek/mtk_dp_hdcp.h new file mode 100644 index 000000000000..8575a6b6b8d7 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dp_hdcp.h @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019-2023 MediaTek Inc. + */ + +#ifndef _MTK_DP_HDCP_H_ +#define _MTK_DP_HDCP_H_ + +#include <linux/types.h> +#include <drm/display/drm_dp_helper.h> +#include <linux/sched/clock.h> + +#define HDCP2_RXSTATUS_SIZE 1 +#define HDCP2_RTX_SIZE 8 +#define HDCP2_RRX_SIZE 8 +#define HDCP2_TXCAPS_SIZE 3 +#define HDCP2_RXCAPS_SIZE 3 +#define HDCP2_M_SIZE 16 +#define HDCP2_KD_SIZE 32 +#define HDCP2_CERTRX_SIZE 522 +#define HDCP2_EKPUBKM_SIZE 128 +#define HDCP2_EKHKM_SIZE 16 +#define HDCP2_KM_SIZE 16 +#define HDCP2_KH_SIZE 16 +#define HDCP2_RN_SIZE 8 +#define HDCP2_LPRIME_SIZE 32 +#define HDCP2_EDKEYKS_SIZE 16 +#define HDCP2_KS_SIZE 16 +#define HDCP2_RIV_SIZE 8 +#define HDCP2_RXINFO_SIZE 2 +#define HDCP2_SEQ_NUM_V_SIZE 3 +#define HDCP2_RECVID_SIZE 5 +#define HDCP2_VPRIME_SIZE 16 +#define HDCP2_RECV_ID_LIST_SIZE 155 +#define HDCP2_SEQ_NUM_M_SIZE 3 +#define HDCP2_STREAMID_TYPE_SIZE 2 +#define HDCP2_K_SIZE 2 +#define HDCP2_REP_MPRIME_SIZE 32 +#define HDCP2_HPRIME_SIZE 32 +#define HDCP2_RX_ENCKEY_SIZE 104 +#define HDCP2_TX_ENCKEY_SIZE 448 +#define HDCP2_LC128_SIZE 16 +#define HDCP2_KPUBRX_MODULUS_SIZE 128 +#define HDCP2_KPUBRX_EXPONENT_SIZE 3 +#define HDCP2_KPUBRX_RESERVED_SIZE 2 +#define HDCP2_MAX_DEVICE_COUNT 31 +#define HDCP2_MAX_DEPTH_LEVEL 4 +#define HDCP2_KPUB_SIZE 384 + +struct hdcp2_info_tx { + u8 rtx[HDCP2_RTX_SIZE]; + u8 tx_caps[HDCP2_TXCAPS_SIZE]; + u8 ekpub_km[HDCP2_EKPUBKM_SIZE]; + u8 eks[HDCP2_EDKEYKS_SIZE]; + u8 v_prime[HDCP2_VPRIME_SIZE]; + u8 rn[HDCP2_RN_SIZE]; + u8 riv[HDCP2_RIV_SIZE]; + u8 seq_num_m[HDCP2_SEQ_NUM_M_SIZE]; + u8 k[HDCP2_K_SIZE]; + u8 stream_id_type[HDCP2_STREAMID_TYPE_SIZE]; +}; + +struct hdcp2_info_rx { + u8 cert[HDCP2_CERTRX_SIZE]; + u8 rrx[HDCP2_RRX_SIZE]; + u8 rx_caps[HDCP2_RXCAPS_SIZE]; + u8 rx_info[HDCP2_RXINFO_SIZE]; + u8 ekh_km[HDCP2_EKHKM_SIZE]; + u8 v_prime[HDCP2_VPRIME_SIZE]; + u8 m_prime[HDCP2_REP_MPRIME_SIZE]; + u8 h_prime[HDCP2_HPRIME_SIZE]; + u8 l_prime[HDCP2_LPRIME_SIZE]; + u8 recv_id_list[HDCP2_MAX_DEVICE_COUNT * HDCP2_RECVID_SIZE]; + u8 seq_num_v[HDCP2_SEQ_NUM_V_SIZE]; +}; + +struct hdcp2_handler { + u8 main_state; + u8 sub_state; + u8 down_stream_dev_cnt; + u8 hdcp_rx_ver; + bool send_ake_init:1; + bool get_recv_id_list:1; + bool stored_km:1; + bool send_lc_init:1; + bool send_ack:1; + bool sink_is_repeater:1; + bool recv_msg:1; + bool send_pair:1; + u32 seq_num_v_cnt; + u32 retry_cnt; +}; + +struct hdcp2_pairing_info { + u8 m[HDCP2_M_SIZE]; + u8 ekh_km[HDCP2_EKHKM_SIZE]; +}; + +struct hdcp1x_info { + bool enable; + bool repeater; + bool r0_read; + bool ksv_ready; + bool max_cascade; + bool max_devs; + u8 b_status; + u8 b_ksv[5]; + u8 a_ksv[5]; + u8 v[20]; + u8 b_info[2]; + u8 ksvfifo[5 * 127]; + u8 device_count; + u8 retry_count; + int main_states; + int sub_states; +}; + +struct hdcp2_info { + struct hdcp2_info_tx hdcp_tx; + struct hdcp2_info_rx hdcp_rx; + struct hdcp2_handler hdcp_handler; + struct hdcp2_pairing_info stored_pairing_info; + bool enable; + bool repeater; + bool read_certrx; + bool read_h_prime; + bool read_pairing; + bool read_l_prime; + bool ks_exchange_done; + bool read_v_prime; + u8 retry_count; + u8 device_count; + u8 stream_id_type; +}; + +struct mtk_hdcp_info { + u8 auth_status; + bool g_init; + struct dp_tee_private *g_dp_tee_priv; + struct drm_dp_aux *aux; + struct regmap *regs; + struct hdcp1x_info hdcp1x_info; + struct hdcp2_info hdcp2_info; +}; + +enum HDCP_RESULT { + AUTH_ZERO = 0, + AUTH_PREPARE = 1, + AUTH_INIT = 2, + AUTH_PASS = 3, + AUTH_FAIL = 4, +}; + +#endif /* _MTK_DP_HDCP_H_ */ 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..2f30516e6009 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.c @@ -0,0 +1,646 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019-2023 MediaTek Inc. + */ + +#include "mtk_dp_hdcp1x.h" +#include "mtk_dp_reg.h" +#include "ca/tlcDpHdcp.h" +#include "mtk_dp_hdcp.h" +#include "mtk_dp.h" +#include <linux/regmap.h> + +static u32 get_system_time(void) +{ + u32 tms = (u32)((sched_clock() / 1000000) % 1000000); + return tms; +} + +static u32 get_time_diff(u32 pre_time) +{ + u32 post_time = get_system_time(); + + if (pre_time > post_time) + return ((1000000 - pre_time) + post_time); + else + return (post_time - pre_time); +} + +static void mhal_dp_tx_hdcp1x_start_cipher(struct mtk_hdcp_info *hdcp_info, bool enable) +{ + if (enable) { + mtk_dp_reg_update_bits(hdcp_info->regs, MTK_DP_TRANS_P0_3480, + REQ_BLOCK_CIPHER_AUTH, REQ_BLOCK_CIPHER_AUTH); + mtk_dp_reg_update_bits(hdcp_info->regs, MTK_DP_TRANS_P0_3480, + KM_GENERATED, KM_GENERATED); + } else { + mtk_dp_reg_update_bits(hdcp_info->regs, MTK_DP_TRANS_P0_3480, 0, KM_GENERATED); + mtk_dp_reg_update_bits(hdcp_info->regs, MTK_DP_TRANS_P0_3480, 0, + REQ_BLOCK_CIPHER_AUTH); + } +} + +static bool mhal_dp_tx_hdcp1x_get_r0_available(struct mtk_hdcp_info *hdcp_info) +{ + bool R0_available; + u32 ret; + + ret = mtk_dp_reg_read(hdcp_info->regs, MTK_DP_TRANS_P0_34A4); + if (ret & BIT(12)) + R0_available = true; + else + R0_available = false; + + return R0_available; +} + +static void mhal_dp_tx_hdcp1x_set_repeater(struct mtk_hdcp_info *hdcp_info, bool enable) +{ + if (enable) + mtk_dp_reg_update_bits(hdcp_info->regs, MTK_DP_TRANS_P0_34A4, BIT(15), BIT(15)); + else + mtk_dp_reg_update_bits(hdcp_info->regs, MTK_DP_TRANS_P0_34A4, 0, BIT(15)); + +#ifdef IF_ZERO + if (hdcp_info->hdcp1x_info.repeater) { + u8 temp; + + temp = BIT(0); /* REAUTHENTICATION_ENABLE_IRQ_HPD */ + drm_dp_dpcd_write(hdcp_info->aux, DPCD_6803B, &temp, 1); + } +#endif +} + +void mdrv_dp_tx_hdcp1x_set_start_auth(struct mtk_hdcp_info *hdcp_info, bool enable) +{ + hdcp_info->hdcp1x_info.enable = enable; + + if (enable) { + hdcp_info->auth_status = AUTH_INIT; + hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_A0; + hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_IDLE; + } else { + hdcp_info->auth_status = AUTH_ZERO; + hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_H2; + hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_IDLE; + tee_hdcp_enable_encrypt(hdcp_info, false, HDCP_NONE); + mhal_dp_tx_hdcp1x_start_cipher(hdcp_info, false); + tee_hdcp1x_soft_rst(hdcp_info); + } + + hdcp_info->hdcp1x_info.retry_count = 0; +} + +bool mdrv_dp_tx_hdcp1x_support(struct mtk_hdcp_info *hdcp_info) +{ + u8 temp_buffer[2]; + int ret; + + drm_dp_dpcd_read(hdcp_info->aux, DP_AUX_HDCP_BCAPS, temp_buffer, 0x1); + + hdcp_info->hdcp1x_info.enable = temp_buffer[0x0] & BIT(0); + hdcp_info->hdcp1x_info.repeater = (temp_buffer[0x0] & BIT(1)) >> 1; + + pr_info("HDCP1.3 CAPABLE: %d, Reapeater: %d\n", + hdcp_info->hdcp1x_info.enable, + hdcp_info->hdcp1x_info.repeater); + + if (!hdcp_info->hdcp1x_info.enable) + return false; + + ret = tee_add_device(hdcp_info, HDCP_VERSION_1X); + if (ret != RET_SUCCESS) { + pr_err("HDCP TA has some error\n"); + hdcp_info->hdcp1x_info.enable = false; + } + + return hdcp_info->hdcp1x_info.enable; +} + +static bool mdrv_dp_tx_hdcp1x_init(struct mtk_hdcp_info *hdcp_info) +{ + u8 i; + + hdcp_info->hdcp1x_info.ksv_ready = false; + hdcp_info->hdcp1x_info.r0_read = false; + hdcp_info->hdcp1x_info.b_status = 0x00; + for (i = 0; i < 5; i++) { + hdcp_info->hdcp1x_info.b_ksv[i] = 0x00; + hdcp_info->hdcp1x_info.a_ksv[i] = 0x00; + } + + for (i = 0; i < 5; i++) + hdcp_info->hdcp1x_info.v[i] = 0x00; + + hdcp_info->hdcp1x_info.b_info[0] = 0x00; + hdcp_info->hdcp1x_info.b_info[1] = 0x00; + hdcp_info->hdcp1x_info.max_cascade = false; + hdcp_info->hdcp1x_info.max_devs = false; + hdcp_info->hdcp1x_info.device_count = 0x00; + + tee_hdcp_enable_encrypt(hdcp_info, false, HDCP_NONE); + mhal_dp_tx_hdcp1x_start_cipher(hdcp_info, false); + tee_hdcp1x_soft_rst(hdcp_info); + + return true; +} + +static bool mdrv_dp_tx_hdcp1x_read_sink_b_ksv(struct mtk_hdcp_info *hdcp_info) +{ + u8 read_buffer[5], i; + + if (hdcp_info->hdcp1x_info.enable) { + drm_dp_dpcd_read(hdcp_info->aux, DP_AUX_HDCP_BKSV, read_buffer, 5); + + for (i = 0; i < 5; i++) { + hdcp_info->hdcp1x_info.b_ksv[i] = + read_buffer[i]; + pr_info("Bksv = 0x%x\n", read_buffer[i]); + } + } + + return true; +} + +static bool mdrv_dp_tx_hdcp1x_check_sink_ksv_ready(struct mtk_hdcp_info *hdcp_info) +{ + u8 read_buffer; + + drm_dp_dpcd_read(hdcp_info->aux, DP_AUX_HDCP_BSTATUS, &read_buffer, 1); + + hdcp_info->hdcp1x_info.ksv_ready = + (read_buffer & BIT(0)) ? true : false; + + return hdcp_info->hdcp1x_info.ksv_ready; +} + +static bool mdrv_dp_tx_hdcp1x_check_sink_cap(struct mtk_hdcp_info *hdcp_info) +{ + u8 read_buffer[0x2]; + + drm_dp_dpcd_read(hdcp_info->aux, DP_AUX_HDCP_BCAPS, read_buffer, 1); + + hdcp_info->hdcp1x_info.repeater = + (read_buffer[0] & BIT(1)) ? true : false; + + return true; +} + +static bool mdrv_dp_tx_hdcp1x_read_sink_b_info(struct mtk_hdcp_info *hdcp_info) +{ + u8 read_buffer[2]; + + drm_dp_dpcd_read(hdcp_info->aux, DP_AUX_HDCP_BINFO, read_buffer, 2); + + hdcp_info->hdcp1x_info.b_info[0] = read_buffer[0]; + hdcp_info->hdcp1x_info.b_info[1] = read_buffer[1]; + hdcp_info->hdcp1x_info.max_cascade = + (read_buffer[1] & BIT(3)) ? true : false; + hdcp_info->hdcp1x_info.max_devs = + (read_buffer[0] & BIT(7)) ? true : false; + hdcp_info->hdcp1x_info.device_count = read_buffer[0] & 0x7F; + + pr_info("HDCP Binfo max_cascade_EXCEEDED = %d\n", + hdcp_info->hdcp1x_info.max_cascade); + pr_info("HDCP Binfo DEPTH = %d\n", read_buffer[1] & 0x07); + pr_info("HDCP Binfo max_devs_EXCEEDED = %d\n", + hdcp_info->hdcp1x_info.max_devs); + pr_info("HDCP Binfo device_count = %d\n", + hdcp_info->hdcp1x_info.device_count); + return true; +} + +static bool mdrv_dp_tx_hdcp1x_read_sink_ksv(struct mtk_hdcp_info *hdcp_info, + u8 dev_count) +{ + u8 i; + u8 times = dev_count / 3; + u8 remain = dev_count % 3; + + if (times > 0) { + for (i = 0; i < times; i++) + drm_dp_dpcd_read(hdcp_info->aux, DP_AUX_HDCP_KSV_FIFO, + hdcp_info->hdcp1x_info.ksvfifo + i * 15, + 15); + } + + if (remain > 0) + drm_dp_dpcd_read(hdcp_info->aux, DP_AUX_HDCP_KSV_FIFO, + hdcp_info->hdcp1x_info.ksvfifo + times * 15, + remain * 5); + + pr_info("HDCP Read ksvfifo = %x\n", hdcp_info->hdcp1x_info.ksvfifo[0]); + pr_info("HDCP Read ksvfifo = %x\n", hdcp_info->hdcp1x_info.ksvfifo[1]); + pr_info("HDCP Read ksvfifo = %x\n", hdcp_info->hdcp1x_info.ksvfifo[2]); + pr_info("HDCP Read ksvfifo = %x\n", hdcp_info->hdcp1x_info.ksvfifo[3]); + pr_info("HDCP Read ksvfifo = %x\n", hdcp_info->hdcp1x_info.ksvfifo[4]); + + return true; +} + +static bool mdrv_dp_tx_hdcp1x_read_sink_sha_v(struct mtk_hdcp_info *hdcp_info) +{ + u8 read_buffer[4], i, j; + + for (i = 0; i < 5; i++) { + drm_dp_dpcd_read(hdcp_info->aux, + 0x68014 + (i * 4), read_buffer, 4); + for (j = 0; j < 4; j++) { + hdcp_info->hdcp1x_info.v[(i * 4) + j] = + read_buffer[3 - j]; + pr_info("HDCP Read sink V = %x\n", + hdcp_info->hdcp1x_info.v[(i * 4) + j]); + } + } + + return true; +} + +static bool mdrv_dp_tx_hdcp1x_auth_with_repeater(struct mtk_hdcp_info *hdcp_info) +{ + bool ret = false; + u8 *buffer = NULL; + u32 len = 0; + int tmp = 0; + + if (hdcp_info->hdcp1x_info.device_count > HDCP1X_REP_MAXDEVS) { + pr_err("HDCP Repeater: %d DEVs!\n", + hdcp_info->hdcp1x_info.device_count); + return false; + } + + mdrv_dp_tx_hdcp1x_read_sink_ksv(hdcp_info, + hdcp_info->hdcp1x_info.device_count); + mdrv_dp_tx_hdcp1x_read_sink_sha_v(hdcp_info); + + len = hdcp_info->hdcp1x_info.device_count * 5 + 2; + buffer = kmalloc(len, GFP_KERNEL); + if (!buffer) { + pr_err("Out of Memory\n"); + return false; + } + + memcpy(buffer, hdcp_info->hdcp1x_info.ksvfifo, len - 2); + memcpy(buffer + (len - 2), hdcp_info->hdcp1x_info.b_info, 2); + tmp = tee_hdcp1x_compute_compare_v(hdcp_info, buffer, len, hdcp_info->hdcp1x_info.v); + if (tmp == RET_COMPARE_PASS) { + pr_info("Check V' PASS\n"); + ret = true; + } else { + pr_info("Check V' Fail\n"); + } + + kfree(buffer); + return ret; +} + +static bool mdrv_dp_tx_hdcp1x_verify_b_ksv(struct mtk_hdcp_info *hdcp_info) +{ + int i, j, k = 0; + u8 ksv; + + for (i = 0; i < 5; i++) { + ksv = hdcp_info->hdcp1x_info.b_ksv[i]; + for (j = 0; j < 8; j++) + k += (ksv >> j) & 0x01; + } + + if (k != 20) { + pr_err("Check BKSV 20'1' 20'0' Fail\n"); + return false; + } + + return true; +} + +static bool mdrv_dp_tx_hdcp1x_write_a_ksv(struct mtk_hdcp_info *hdcp_info) +{ + u8 temp; + int i, k, j; + + tee_get_aksv(hdcp_info, hdcp_info->hdcp1x_info.a_ksv); + drm_dp_dpcd_write(hdcp_info->aux, DP_AUX_HDCP_AKSV, + hdcp_info->hdcp1x_info.a_ksv, 5); + + for (i = 0, k = 0; i < 5; i++) { + temp = hdcp_info->hdcp1x_info.a_ksv[i]; + + for (j = 0; j < 8; j++) + k += (temp >> j) & 0x01; + pr_info("Aksv 0x%x\n", temp); + } + + if (k != 20) { + pr_err("Check AKSV 20'1' 20'0' Fail\n"); + return false; + } + + return true; +} + +static void mdrv_dp_tx_hdcp1x_write_an(struct mtk_hdcp_info *hdcp_info) +{ + u8 an_value[0x8] = { /* on DP Spec p99 */ + 0x03, 0x04, 0x07, 0x0C, 0x13, 0x1C, 0x27, 0x34}; + + tee_hdcp1x_set_tx_an(hdcp_info, an_value); + drm_dp_dpcd_write(hdcp_info->aux, DP_AUX_HDCP_AN, an_value, 8); + mdelay(5); +} + +static bool mdrv_dp_tx_hdcp1x_check_r0(struct mtk_hdcp_info *hdcp_info) +{ + u8 temp_value[2]; + u8 retry_count = 0; + bool sink_R0_available = false; + bool ret; + int tmp; + + ret = mhal_dp_tx_hdcp1x_get_r0_available(hdcp_info); + if (!ret) { + pr_err("HDCP ERR: R0 No Available\n"); + return false; + } + + if (!hdcp_info->hdcp1x_info.r0_read) { + drm_dp_dpcd_read(hdcp_info->aux, DP_AUX_HDCP_BSTATUS, temp_value, 1); + sink_R0_available = + ((temp_value[0x0] & BIT(1)) == BIT(1)) ? true : false; + + if (!sink_R0_available) { + drm_dp_dpcd_read(hdcp_info->aux, + DP_AUX_HDCP_BSTATUS, temp_value, 1); + sink_R0_available = + ((temp_value[0x0] & BIT(1)) == BIT(1)) + ? true : false; + + if (!sink_R0_available) + return false; + } + } + + while (retry_count < 3) { + drm_dp_dpcd_read(hdcp_info->aux, + DP_AUX_HDCP_RI_PRIME, temp_value, 2); + + tmp = tee_compare_r0(hdcp_info, temp_value, 2); + if (tmp == RET_COMPARE_PASS) + return true; + + pr_info("R0 check FAIL:Rx_R0=0x%x%x\n", + temp_value[0x1], temp_value[0x0]); + mdelay(5); + + retry_count++; + } + return false; +} + +static void mdrv_dp_tx_hdcp1x_state_rst(struct mtk_hdcp_info *hdcp_info) +{ + pr_info("Before State Reset:(M : S)= (%d, %d)", + hdcp_info->hdcp1x_info.main_states, + hdcp_info->hdcp1x_info.sub_states); + hdcp_info->hdcp1x_info.main_states = HDCP1X_main_state_A0; + hdcp_info->hdcp1x_info.sub_states = HDCP1X_sub_FSM_IDLE; +} + +void mdrv_dp_tx_hdcp1x_fsm(struct mtk_hdcp_info *hdcp_info) +{ + static int pre_main, pre_sub; + static u32 pre_time; + u32 time; + bool ret; + + if (pre_main != hdcp_info->hdcp1x_info.main_states || + hdcp_info->hdcp1x_info.sub_states != pre_sub) { + pr_info("HDCP1.x State(M : S)= (%d, %d)", + hdcp_info->hdcp1x_info.main_states, + hdcp_info->hdcp1x_info.sub_states); + pre_main = hdcp_info->hdcp1x_info.main_states; + pre_sub = hdcp_info->hdcp1x_info.sub_states; + } + + switch (hdcp_info->hdcp1x_info.main_states) { + case HDCP1X_main_state_H2: + switch (hdcp_info->hdcp1x_info.sub_states) { + case HDCP1X_sub_FSM_IDLE: + break; + case HDCP1X_sub_FSM_auth_fail: + tee_hdcp_enable_encrypt(hdcp_info, false, HDCP_NONE); + pr_info("HDCP1.3 Authentication Fail\n"); + hdcp_info->auth_status = AUTH_FAIL; + hdcp_info->hdcp1x_info.main_states = + HDCP1X_main_state_H2; + hdcp_info->hdcp1x_info.sub_states = + HDCP1X_sub_FSM_IDLE; + break; + } + break; + case HDCP1X_main_state_A0: + switch (hdcp_info->hdcp1x_info.sub_states) { + case HDCP1X_sub_FSM_IDLE: + if (hdcp_info->hdcp1x_info.retry_count + > HDCP1X_REAUNTH_COUNT) { + pr_info("Too much retry!\n"); + hdcp_info->hdcp1x_info.main_states = + HDCP1X_main_state_H2; + hdcp_info->hdcp1x_info.sub_states = + HDCP1X_sub_FSM_auth_fail; + } else { + mdrv_dp_tx_hdcp1x_init(hdcp_info); + hdcp_info->hdcp1x_info.main_states = + HDCP1X_main_state_A0; + hdcp_info->hdcp1x_info.sub_states = + HDCP1X_sub_FSM_CHECKHDCPCAPABLE; + } + + break; + case HDCP1X_sub_FSM_CHECKHDCPCAPABLE: + if (hdcp_info->hdcp1x_info.enable) { + hdcp_info->hdcp1x_info.retry_count++; + hdcp_info->hdcp1x_info.main_states = + HDCP1X_main_state_A1; + hdcp_info->hdcp1x_info.sub_states = + HDCP1X_sub_FSM_exchange_KSV; + } else { + mdrv_dp_tx_hdcp1x_state_rst(hdcp_info); + } + break; + } + break; + case HDCP1X_main_state_A1: + switch (hdcp_info->hdcp1x_info.sub_states) { + case HDCP1X_sub_FSM_exchange_KSV: + mdrv_dp_tx_hdcp1x_write_an(hdcp_info); + ret = mdrv_dp_tx_hdcp1x_write_a_ksv(hdcp_info); + if (ret) { + pre_time = get_system_time(); + hdcp_info->hdcp1x_info.main_states = + HDCP1X_main_state_A1; + hdcp_info->hdcp1x_info.sub_states = + HDCP1X_sub_FSM_verify_bksv; + } else { + mdrv_dp_tx_hdcp1x_state_rst(hdcp_info); + } + break; + + case HDCP1X_sub_FSM_verify_bksv: + mdrv_dp_tx_hdcp1x_read_sink_b_ksv(hdcp_info); + mhal_dp_tx_hdcp1x_set_repeater(hdcp_info, + hdcp_info->hdcp1x_info.repeater); + + time = get_time_diff(pre_time); + if (time < HDCP1X_BSTATUS_TIMEOUT_CNT) { + pre_time = get_system_time(); + ret = mdrv_dp_tx_hdcp1x_verify_b_ksv(hdcp_info); + if (ret) { + hdcp_info->hdcp1x_info.main_states = + HDCP1X_main_state_A2; + hdcp_info->hdcp1x_info.sub_states = + HDCP1X_sub_FSM_computation; + } else { + mdrv_dp_tx_hdcp1x_state_rst(hdcp_info); + pr_info("Invalid BKSV!!\n"); + } + } else { + mdrv_dp_tx_hdcp1x_state_rst(hdcp_info); + } + break; + } + break; + + case HDCP1X_main_state_A2: + switch (hdcp_info->hdcp1x_info.sub_states) { + case HDCP1X_sub_FSM_computation: + tee_calculate_lm(hdcp_info, hdcp_info->hdcp1x_info.b_ksv); + mhal_dp_tx_hdcp1x_start_cipher(hdcp_info, true); + hdcp_info->hdcp1x_info.main_states = + HDCP1X_main_state_A3; + hdcp_info->hdcp1x_info.sub_states = + HDCP1X_sub_FSM_check_R0; + pre_time = get_system_time(); + break; + } + break; + + case HDCP1X_main_state_A3: + switch (hdcp_info->hdcp1x_info.sub_states) { + case HDCP1X_sub_FSM_check_R0: + /* Wait 100ms(at least) before check R0 */ + time = get_time_diff(pre_time); + if (time < HDCP1X_R0_WDT && + !hdcp_info->hdcp1x_info.r0_read) { + mdelay(10); + break; + } + + ret = mdrv_dp_tx_hdcp1x_check_r0(hdcp_info); + if (ret) { + tee_hdcp_enable_encrypt(hdcp_info, true, HDCP_V1); + hdcp_info->hdcp1x_info.main_states = + HDCP1X_main_state_A5; + hdcp_info->hdcp1x_info.sub_states = + HDCP1X_sub_FSM_IDLE; + } else { + mdrv_dp_tx_hdcp1x_state_rst(hdcp_info); + } + + break; + } + break; + + case HDCP1X_main_state_A4: + switch (hdcp_info->hdcp1x_info.sub_states) { + case HDCP1X_sub_FSM_IDLE: + break; + case HDCP1X_sub_FSM_auth_done: + pr_info("HDCP1X: Authentication done!\n"); + hdcp_info->hdcp1x_info.retry_count = 0; + hdcp_info->auth_status = AUTH_PASS; + hdcp_info->hdcp1x_info.main_states = + HDCP1X_main_state_A4; + hdcp_info->hdcp1x_info.sub_states = + HDCP1X_sub_FSM_IDLE; + + /* unmute */ + break; + } + break; + + case HDCP1X_main_state_A5: + switch (hdcp_info->hdcp1x_info.sub_states) { + case HDCP1X_sub_FSM_IDLE: + mdrv_dp_tx_hdcp1x_check_sink_cap(hdcp_info); + if (hdcp_info->hdcp1x_info.repeater) { + pr_info("HDCP1X:Repeater!\n"); + pre_time = get_system_time(); + hdcp_info->hdcp1x_info.main_states = + HDCP1X_main_state_A6; + hdcp_info->hdcp1x_info.sub_states = + HDCP1X_sub_FSM_polling_rdy_bit; + } else { + pr_info("HDCP1X:No Repeater!\n"); + hdcp_info->hdcp1x_info.main_states = + HDCP1X_main_state_A4; + hdcp_info->hdcp1x_info.sub_states = + HDCP1X_sub_FSM_auth_done; + } + break; + } + break; + + case HDCP1X_main_state_A6: + switch (hdcp_info->hdcp1x_info.sub_states) { + case HDCP1X_sub_FSM_polling_rdy_bit: + time = get_time_diff(pre_time); + if (time > HDCP1X_REP_RDY_WDT) { + mdrv_dp_tx_hdcp1x_state_rst(hdcp_info); + break; + } + + time = get_time_diff(pre_time); + if (!hdcp_info->hdcp1x_info.ksv_ready && + time > HDCP1X_REP_RDY_WDT / 2) + mdrv_dp_tx_hdcp1x_check_sink_ksv_ready(hdcp_info); + + if (hdcp_info->hdcp1x_info.ksv_ready) { + mdrv_dp_tx_hdcp1x_read_sink_b_info(hdcp_info); + hdcp_info->hdcp1x_info.main_states = + HDCP1X_main_state_A7; + hdcp_info->hdcp1x_info.sub_states = + HDCP1X_sub_FSM_auth_with_repeater; + hdcp_info->hdcp1x_info.ksv_ready = false; + } + break; + } + break; + + case HDCP1X_main_state_A7: + switch (hdcp_info->hdcp1x_info.sub_states) { + case HDCP1X_sub_FSM_auth_with_repeater: + if (hdcp_info->hdcp1x_info.max_cascade || + hdcp_info->hdcp1x_info.max_devs){ + pr_err("MAX CASCADE or MAX DEVS!\n"); + mdrv_dp_tx_hdcp1x_state_rst(hdcp_info); + break; + } + + ret = mdrv_dp_tx_hdcp1x_auth_with_repeater(hdcp_info); + if (ret) { + hdcp_info->hdcp1x_info.main_states = + HDCP1X_main_state_A4; + hdcp_info->hdcp1x_info.sub_states = + HDCP1X_sub_FSM_auth_done; + } else { + mdrv_dp_tx_hdcp1x_state_rst(hdcp_info); + } + + break; + } + break; + + default: + break; + } +} diff --git a/drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h b/drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h new file mode 100644 index 000000000000..2fd169f5c3cb --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dp_hdcp1x.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019-2023 MediaTek Inc. + */ + +#ifndef _MTK_DP_HDCP1X_H_ +#define _MTK_DP_HDCP1X_H_ + +#include "mtk_dp_hdcp.h" + +#define DP_HDCP1_BINFO_SIZE 2 +#define DP_HDCP1_BCAPS_SIZE 1 +#define DP_HDCP1_BSTATUS_SIZE 1 +#define DP_HDCP1_AN_SIZE 8 +#define DP_HDCP1_AKSV_SIZE 5 +#define DP_HDCP1_BKSV_SIZE 5 +#define DP_HDCP1_AINFO_SIZE 1 + +#define HDCP1X_BSTATUS_TIMEOUT_CNT 600 +#define HDCP1X_R0_WDT 100 +#define HDCP1X_REP_RDY_WDT 5000 + +#define HDCP1X_REP_MAXDEVS 128 +#define HDCP1X_REAUNTH_COUNT 3 + +enum DPTX_DRV_HDCP1X_main_states { + HDCP1X_main_state_H2 = 0, + HDCP1X_main_state_A0 = 1, + HDCP1X_main_state_A1 = 2, + HDCP1X_main_state_A2 = 3, + HDCP1X_main_state_A3 = 4, + HDCP1X_main_state_A4 = 5, + HDCP1X_main_state_A5 = 6, + HDCP1X_main_state_A6 = 7, + HDCP1X_main_state_A7 = 8, +}; + +enum DPTX_DRV_HDCP1X_sub_states { + HDCP1X_sub_FSM_IDLE = 0, + HDCP1X_sub_FSM_CHECKHDCPCAPABLE = 1, + HDCP1X_sub_FSM_exchange_KSV = 2, + HDCP1X_sub_FSM_verify_bksv = 3, + HDCP1X_sub_FSM_computation = 4, + HDCP1X_sub_FSM_check_R0 = 5, + HDCP1X_sub_FSM_auth_done = 6, + HDCP1X_sub_FSM_polling_rdy_bit = 7, + HDCP1X_sub_FSM_auth_with_repeater = 8, + HDCP1X_sub_FSM_auth_fail = 9, +}; + +bool mdrv_dp_tx_hdcp1x_support(struct mtk_hdcp_info *hdcp_info); +void mdrv_dp_tx_hdcp1x_fsm(struct mtk_hdcp_info *hdcp_info); +void mdrv_dp_tx_hdcp1x_set_start_auth(struct mtk_hdcp_info *hdcp_info, bool enable); + +#endif /* _MTK_DP_HDCP1X_H_ */ diff --git a/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c new file mode 100644 index 000000000000..09100292182d --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c @@ -0,0 +1,1008 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019-2023 MediaTek Inc. + */ + +#include "mtk_dp_hdcp2.h" +#include "mtk_dp_reg.h" +#include "ca/tlcDpHdcp.h" +#include "mtk_dp_hdcp.h" +#include "mtk_dp.h" +#include <linux/regmap.h> + +#define DPTXHDCPFUNC(fmt, arg...) \ + pr_info("[DPTXHDCP][%s line:%d]"pr_fmt(fmt), __func__, __LINE__, ##arg) + +#define DPTXHDCPMSG(fmt, arg...) \ + pr_info("[DPTXHDCP]"pr_fmt(fmt), ##arg) + +u8 t_rtx[HDCP2_RTX_SIZE] = { + 0x18, 0xfa, 0xe4, 0x20, 0x6a, 0xfb, 0x51, 0x49 +}; + +u8 t_tx_caps[HDCP2_TXCAPS_SIZE] = { + 0x02, 0x00, 0x00 +}; + +u8 t_rn[HDCP2_RN_SIZE] = { + 0x32, 0x75, 0x3e, 0xa8, 0x78, 0xa6, 0x38, 0x1c +}; + +u8 t_riv[HDCP2_RIV_SIZE] = { + 0x40, 0x2b, 0x6b, 0x43, 0xc5, 0xe8, 0x86, 0xd8 +}; + +static u32 get_system_time(void) +{ + u32 tms = (u32)((sched_clock() / 1000000) % 1000000); + return tms; +} + +static u32 get_time_diff(u32 pre_time) +{ + u32 post_time = get_system_time(); + + if (pre_time > post_time) + return ((1000000 - pre_time) + post_time); + else + return (post_time - pre_time); +} + +static void mhal_dp_tx_hdcp2_fill_stream_type(struct mtk_hdcp_info *hdcp_info, u8 uc_type) +{ + mtk_dp_reg_update_bits(hdcp_info->regs, MTK_DP_TRANS_P0_34D0, uc_type, 0xff); +} + +static void mdrv_dp_tx_hdcp2_set_state(struct mtk_hdcp_info *hdcp_info, u8 main_state, u8 sub_state) +{ + hdcp_info->hdcp2_info.hdcp_handler.main_state = main_state; + hdcp_info->hdcp2_info.hdcp_handler.sub_state = sub_state; +} + +static void mdrv_dp_tx_hdcp2_set_auth_pass(struct mtk_hdcp_info *hdcp_info, bool enable) +{ + if (enable) { + mtk_dp_reg_update_bits(hdcp_info->regs, MTK_DP_TRANS_P0_3400, BIT(11), BIT(11)); + mtk_dp_reg_update_bits(hdcp_info->regs, MTK_DP_TRANS_P0_34A4, BIT(4), BIT(4)); + } else { + mtk_dp_reg_update_bits(hdcp_info->regs, MTK_DP_TRANS_P0_3400, 0, BIT(11)); + mtk_dp_reg_update_bits(hdcp_info->regs, MTK_DP_TRANS_P0_34A4, 0, BIT(4)); + } +} + +static void mdrv_dp_tx_hdcp2_enable_auth(struct mtk_hdcp_info *hdcp_info, bool enable) +{ + DPTXHDCPFUNC(); + mdrv_dp_tx_hdcp2_set_auth_pass(hdcp_info, enable); + if (enable) { + u32 version = HDCP_V2_3; + + if (hdcp_info->hdcp2_info.hdcp_rx.rx_info[1] & BIT(0)) + version = HDCP_V1; + else if (hdcp_info->hdcp2_info.hdcp_rx.rx_info[1] & BIT(1)) + version = HDCP_V2; + + tee_hdcp_enable_encrypt(hdcp_info, enable, version); + mtk_dp_reg_update_bits(hdcp_info->regs, MTK_DP_ENC0_P0_3000, BIT(5), BIT(5)); + } else { + tee_hdcp_enable_encrypt(hdcp_info, enable, HDCP_NONE); + mtk_dp_reg_update_bits(hdcp_info->regs, MTK_DP_ENC0_P0_3000, 0, BIT(5)); + } +} + +static int mdrv_dp_tx_hdcp2_init(struct mtk_hdcp_info *hdcp_info) +{ + int err_code = HDCP_ERR_NONE; + + DPTXHDCPFUNC(); + + 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)); + memcpy(hdcp_info->hdcp2_info.hdcp_tx.rtx, t_rtx, HDCP2_RTX_SIZE); + memcpy(hdcp_info->hdcp2_info.hdcp_tx.tx_caps, t_tx_caps, HDCP2_TXCAPS_SIZE); + memcpy(hdcp_info->hdcp2_info.hdcp_tx.rn, t_rn, HDCP2_RN_SIZE); + memcpy(hdcp_info->hdcp2_info.hdcp_tx.riv, t_riv, HDCP2_RIV_SIZE); + + memset(&hdcp_info->hdcp2_info.hdcp_handler, 0, sizeof(struct hdcp2_handler)); + memset(&hdcp_info->hdcp2_info.stored_pairing_info, 0, sizeof(struct hdcp2_pairing_info)); + + mdrv_dp_tx_hdcp2_enable_auth(hdcp_info, false); + + return err_code; +} + +static bool mdrv_dp_tx_hdcp2_inc_seq_num_m(struct mtk_hdcp_info *hdcp_info) +{ + u8 i = 0; + u32 temp_value = 0; + + for (i = 0; i < HDCP2_SEQ_NUM_M_SIZE; i++) + temp_value |= hdcp_info->hdcp2_info.hdcp_tx.seq_num_m[i] << (i * 8); + + if (temp_value == 0xFFFFFF) + return false; + + temp_value++; + + for (i = 0; i < HDCP2_SEQ_NUM_M_SIZE; i++) + hdcp_info->hdcp2_info.hdcp_tx.seq_num_m[i] = + (temp_value & ((u32)0xFF << (i * 8))) >> (i * 8); + return true; +} + +static bool mdrv_dp_tx_hdcp2_process_rep_auth_stream_manage(struct mtk_hdcp_info *hdcp_info) +{ + bool ret = false; + + 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; + + ret = mdrv_dp_tx_hdcp2_inc_seq_num_m(hdcp_info); + + return ret; +} + +static bool mdrv_dp_tx_hdcp2_recv_rep_auth_send_recv_id_list(struct mtk_hdcp_info *hdcp_info) +{ + bool ret = false; + u8 *buffer = NULL; + u32 len = 0, len_recv_id_list = 0; + int rc = 0; + + len_recv_id_list = + hdcp_info->hdcp2_info.device_count * HDCP2_RECVID_SIZE; + len = len_recv_id_list + HDCP2_RXINFO_SIZE + HDCP2_SEQ_NUM_V_SIZE; + buffer = kmalloc(len, GFP_KERNEL); + if (!buffer) { + pr_err("Out of Memory\n"); + return ret; + } + + memcpy(buffer, hdcp_info->hdcp2_info.hdcp_rx.recv_id_list, len_recv_id_list); + memcpy(buffer + len_recv_id_list, hdcp_info->hdcp2_info.hdcp_rx.rx_info, HDCP2_RXINFO_SIZE); + memcpy(buffer + len_recv_id_list + HDCP2_RXINFO_SIZE, + hdcp_info->hdcp2_info.hdcp_rx.seq_num_v, HDCP2_SEQ_NUM_V_SIZE); + + rc = tee_hdcp2_compute_compare_v(hdcp_info, buffer, len, + hdcp_info->hdcp2_info.hdcp_rx.v_prime, + hdcp_info->hdcp2_info.hdcp_tx.v_prime); + + if (rc == RET_COMPARE_PASS) { + ret = true; + DPTXHDCPMSG("V' is PASS!!\n"); + } else { + DPTXHDCPMSG("V' is FAIL!!\n"); + } + + kfree(buffer); + return ret; +} + +static bool mdrv_dp_tx_hdcp2_recv_rep_auth_stream_ready(struct mtk_hdcp_info *hdcp_info) +{ + bool ret = false; + u8 *buffer = NULL; + u32 len = 0; + int temp = 0; + + len = HDCP2_STREAMID_TYPE_SIZE + HDCP2_SEQ_NUM_M_SIZE; + buffer = kmalloc(len, GFP_KERNEL); + if (!buffer) { + pr_err("Out of Memory\n"); + return ret; + } + + memcpy(buffer, hdcp_info->hdcp2_info.hdcp_tx.stream_id_type, HDCP2_STREAMID_TYPE_SIZE); + memcpy(buffer + HDCP2_STREAMID_TYPE_SIZE, hdcp_info->hdcp2_info.hdcp_tx.seq_num_m, + HDCP2_SEQ_NUM_M_SIZE); + temp = tee_hdcp2_compute_compare_m(hdcp_info, buffer, len, + hdcp_info->hdcp2_info.hdcp_rx.m_prime); + + if (temp == RET_COMPARE_PASS) { + ret = true; + DPTXHDCPMSG("M' is PASS!!\n"); + } else { + DPTXHDCPMSG("M' is FAIL!!\n"); + } + + kfree(buffer); + return ret; +} + +static bool mdrv_dp_tx_hdcp2_check_seq_num_v(struct mtk_hdcp_info *hdcp_info) +{ + if ((hdcp_info->hdcp2_info.hdcp_rx.seq_num_v[0] == 0x00 && + hdcp_info->hdcp2_info.hdcp_rx.seq_num_v[1] == 0x00 && + hdcp_info->hdcp2_info.hdcp_rx.seq_num_v[2] == 0x00) && + hdcp_info->hdcp2_info.hdcp_handler.seq_num_v_cnt > 0xFFFFFF) { + DPTXHDCPMSG("SeqNumV Rollover!\n"); + return false; + } + + if ((hdcp_info->hdcp2_info.hdcp_rx.seq_num_v[0] + != (u8)((hdcp_info->hdcp2_info.hdcp_handler.seq_num_v_cnt & 0xFF0000) >> 16)) || + (hdcp_info->hdcp2_info.hdcp_rx.seq_num_v[1] + != (u8)((hdcp_info->hdcp2_info.hdcp_handler.seq_num_v_cnt & 0x00FF00) >> 8)) || + (hdcp_info->hdcp2_info.hdcp_rx.seq_num_v[2] + != (u8)((hdcp_info->hdcp2_info.hdcp_handler.seq_num_v_cnt & 0x0000FF)))) { + DPTXHDCPMSG("Invalid Seq_num_V!\n"); + return false; + } + + hdcp_info->hdcp2_info.hdcp_handler.seq_num_v_cnt++; + return true; +} + +static void mdrv_dp_tx_hdcp2_err_handle(struct mtk_hdcp_info *hdcp_info, int err_msg, int line) +{ + pr_err("MainState:%d; SubState:%d;\n", hdcp_info->hdcp2_info.hdcp_handler.main_state, + hdcp_info->hdcp2_info.hdcp_handler.sub_state); + + switch (err_msg) { + case HDCP_ERR_UNKNOWN_STATE: + pr_err("Unknown State, line:%d\n", line); + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_H1P1, HDCP2_MSG_AUTH_FAIL); + break; + + case HDCP_ERR_SEND_MSG_FAIL: + pr_err("Send Msg Fail, line:%d\n", line); + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A0F0, HDCP2_MSG_ZERO); + break; + case HDCP_ERR_RESPONSE_TIMEROUT: + pr_err("Response Timeout, line:%d!\n", line); + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A0F0, HDCP2_MSG_ZERO); + break; + + case HDCP_ERR_PROCESS_FAIL: + pr_err("Process Fail, line:%d!\n", line); + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A0F0, HDCP2_MSG_ZERO); + break; + + default: + pr_err("NO ERROR!"); + break; + } +} + +static bool mdrv_dp_tx_hdcp2_read_msg(struct mtk_hdcp_info *hdcp_info, u8 cmd_ID) +{ + bool ret = false; + u8 size = 0; + + switch (cmd_ID) { + case HDCP2_MSG_AKE_SEND_CERT: + drm_dp_dpcd_read(hdcp_info->aux, DP_HDCP_2_2_REG_CERT_RX_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.cert, HDCP2_CERTRX_SIZE); + drm_dp_dpcd_read(hdcp_info->aux, DP_HDCP_2_2_REG_RRX_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.rrx, HDCP2_RRX_SIZE); + drm_dp_dpcd_read(hdcp_info->aux, DP_HDCP_2_2_REG_RX_CAPS_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.rx_caps, HDCP2_RXCAPS_SIZE); + + hdcp_info->hdcp2_info.read_certrx = false; + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true; + ret = true; + DPTXHDCPMSG("HDCP2_MSG_AKE_SEND_CERT\n"); + break; + + case HDCP2_MSG_AKE_SEND_H_PRIME: + drm_dp_dpcd_read(hdcp_info->aux, DP_HDCP_2_2_REG_HPRIME_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.h_prime, HDCP2_HPRIME_SIZE); + + hdcp_info->hdcp2_info.read_h_prime = false; + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true; + ret = true; + + DPTXHDCPMSG("HDCP2_MSG_AKE_SEND_H_PRIME\n"); + break; + + case HDCP2_MSG_AKE_SEND_PAIRING_INFO: + drm_dp_dpcd_read(hdcp_info->aux, DP_HDCP_2_2_REG_EKH_KM_RD_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.ekh_km, HDCP2_EKHKM_SIZE); + hdcp_info->hdcp2_info.read_pairing = false; + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true; + ret = true; + DPTXHDCPMSG("HDCP2_MSG_AKE_SEND_PAIRING_INFO\n"); + break; + + case HDCP2_MSG_LC_SEND_L_PRIME: + drm_dp_dpcd_read(hdcp_info->aux, DP_HDCP_2_2_REG_LPRIME_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.l_prime, HDCP2_LPRIME_SIZE); + + hdcp_info->hdcp2_info.read_l_prime = false; + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true; + ret = true; + DPTXHDCPMSG("HDCP2_MSG_LC_SEND_L_PRIME\n"); + break; + + case HDCP2_MSG_REPAUTH_SEND_RECVID_LIST: + drm_dp_dpcd_read(hdcp_info->aux, DP_HDCP_2_2_REG_RXINFO_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.rx_info, HDCP2_RXINFO_SIZE); + hdcp_info->hdcp2_info.device_count = + ((hdcp_info->hdcp2_info.hdcp_rx.rx_info[1] & 0xf0) >> 4) + | ((hdcp_info->hdcp2_info.hdcp_rx.rx_info[0] & BIT(0)) << 4); + + drm_dp_dpcd_read(hdcp_info->aux, DP_HDCP_2_2_REG_SEQ_NUM_V_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.seq_num_v, HDCP2_SEQ_NUM_V_SIZE); + drm_dp_dpcd_read(hdcp_info->aux, DP_HDCP_2_2_REG_VPRIME_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.v_prime, HDCP2_VPRIME_SIZE); + drm_dp_dpcd_read(hdcp_info->aux, DP_HDCP_2_2_REG_RECV_ID_LIST_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.recv_id_list, + hdcp_info->hdcp2_info.device_count + * HDCP2_RECVID_SIZE); + + hdcp_info->hdcp2_info.read_v_prime = false; + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true; + ret = true; + DPTXHDCPMSG("HDCP2_MSG_REPAUTH_SEND_RECVID_LIST\n"); + break; + + case HDCP2_MSG_REPAUTH_STREAM_READY: + size = drm_dp_dpcd_read(hdcp_info->aux, DP_HDCP_2_2_REG_MPRIME_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.m_prime, + HDCP2_REP_MPRIME_SIZE); + + if (size == HDCP2_REP_MPRIME_SIZE) + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true; + ret = true; + DPTXHDCPMSG("HDCP2_MSG_REPAUTH_STREAM_READY\n"); + break; + + default: + DPTXHDCPMSG("Invalid DPTX_HDCP2_OffSETADDR_ReadMessage !\n"); + break; + } + + return ret; +} + +static bool mdrv_dp_tx_hdcp2_write_msg(struct mtk_hdcp_info *hdcp_info, u8 cmd_ID) +{ + bool ret = false; + + switch (cmd_ID) { + case HDCP2_MSG_AKE_INIT: + tee_hdcp2_soft_rst(hdcp_info); + drm_dp_dpcd_write(hdcp_info->aux, DP_HDCP_2_2_REG_RTX_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.rtx, HDCP2_RTX_SIZE); + drm_dp_dpcd_write(hdcp_info->aux, DP_HDCP_2_2_REG_TXCAPS_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.tx_caps, HDCP2_TXCAPS_SIZE); + + ret = true; + DPTXHDCPMSG("HDCP2_MSG_AKE_Init !\n"); + break; + + case HDCP2_MSG_AKE_NO_STORED_KM: + drm_dp_dpcd_write(hdcp_info->aux, DP_HDCP_2_2_REG_EKPUB_KM_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.ekpub_km, HDCP2_EKPUBKM_SIZE); + + ret = true; + + DPTXHDCPMSG("HDCP2_MSG_AKE_NO_STORED_KM !\n"); + break; + + case HDCP2_MSG_AKE_STORED_KM: + drm_dp_dpcd_write(hdcp_info->aux, DP_HDCP_2_2_REG_EKH_KM_WR_OFFSET, + hdcp_info->hdcp2_info.stored_pairing_info.ekh_km, + HDCP2_EKHKM_SIZE); + drm_dp_dpcd_write(hdcp_info->aux, DP_HDCP_2_2_REG_M_OFFSET, + hdcp_info->hdcp2_info.stored_pairing_info.m, HDCP2_M_SIZE); + + ret = true; + + DPTXHDCPMSG("DPTX_HDCP2_MSG_AKE_STORED_KM !\n"); + break; + + case HDCP2_MSG_LC_INIT: + drm_dp_dpcd_write(hdcp_info->aux, DP_HDCP_2_2_REG_RN_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.rn, HDCP2_RN_SIZE); + + hdcp_info->hdcp2_info.read_l_prime = true; + ret = true; + + DPTXHDCPMSG("HDCP2_MSG_LC_INIT !\n"); + break; + + case HDCP2_MSG_SKE_SEND_EKS: + drm_dp_dpcd_write(hdcp_info->aux, DP_HDCP_2_2_REG_EDKEY_KS_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.eks, HDCP2_EDKEYKS_SIZE); + drm_dp_dpcd_write(hdcp_info->aux, DP_HDCP_2_2_REG_RIV_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.riv, HDCP2_RIV_SIZE); + + hdcp_info->hdcp2_info.ks_exchange_done = true; + + ret = true; + DPTXHDCPMSG("HDCP2_MSG_SKE_SEND_EKS !\n"); + break; + + case HDCP2_MSG_REPAUTH_SEND_ACK: + drm_dp_dpcd_write(hdcp_info->aux, DP_HDCP_2_2_REG_V_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.v_prime, HDCP2_VPRIME_SIZE); + + ret = true; + DPTXHDCPMSG("HDCP2_MSG_SEND_ACK !\n"); + break; + + case HDCP2_MSG_REPAUTH_STREAM_MANAGE: + drm_dp_dpcd_write(hdcp_info->aux, DP_HDCP_2_2_REG_SEQ_NUM_M_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.seq_num_m, HDCP2_SEQ_NUM_M_SIZE); + drm_dp_dpcd_write(hdcp_info->aux, DP_HDCP_2_2_REG_K_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.k, HDCP2_K_SIZE); + drm_dp_dpcd_write(hdcp_info->aux, DP_HDCP_2_2_REG_STREAM_ID_TYPE_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.stream_id_type, + HDCP2_STREAMID_TYPE_SIZE); + + mhal_dp_tx_hdcp2_fill_stream_type(hdcp_info, + hdcp_info->hdcp2_info.stream_id_type); + + ret = true; + DPTXHDCPMSG("HDCP2_MSG_STREAM_MANAGE !\n"); + break; + + default: + DPTXHDCPMSG("Invalid HDCP2_OffSETADDR_WriteMessage !\n"); + break; + } + + return ret; +} + +static void mdrv_dp_tx_hdcp2_rest_variable(struct mtk_hdcp_info *hdcp_info) +{ + hdcp_info->hdcp2_info.read_certrx = false; + hdcp_info->hdcp2_info.read_h_prime = false; + hdcp_info->hdcp2_info.read_pairing = false; + hdcp_info->hdcp2_info.read_l_prime = false; + hdcp_info->hdcp2_info.ks_exchange_done = false; + hdcp_info->hdcp2_info.read_v_prime = false; +} + +int mdrv_dp_tx_hdcp2_fsm(struct mtk_hdcp_info *hdcp_info) +{ + static u32 timeout_value; + static u8 pre_main; + static u8 pre_sub; + static u32 pre_time; + int err_code = HDCP_ERR_NONE; + bool stored = false; + u32 time; + int ret = 0; + bool tmp = false; + + if (pre_main != hdcp_info->hdcp2_info.hdcp_handler.main_state || + hdcp_info->hdcp2_info.hdcp_handler.sub_state != pre_sub) { + DPTXHDCPMSG("Port(M : S)= (%d, %d)", hdcp_info->hdcp2_info.hdcp_handler.main_state, + hdcp_info->hdcp2_info.hdcp_handler.sub_state); + pre_main = hdcp_info->hdcp2_info.hdcp_handler.main_state; + pre_sub = hdcp_info->hdcp2_info.hdcp_handler.sub_state; + } + + switch (hdcp_info->hdcp2_info.hdcp_handler.main_state) { + case HDCP2_MS_H1P1: + switch (hdcp_info->hdcp2_info.hdcp_handler.sub_state) { + case HDCP2_MSG_ZERO: + break; + case HDCP2_MSG_AUTH_FAIL: + pr_err("HDCP2.x Authentication Fail\n"); + mdrv_dp_tx_hdcp2_enable_auth(hdcp_info, false); + hdcp_info->auth_status = AUTH_FAIL; + break; + } + break; + case HDCP2_MS_A0F0: + switch (hdcp_info->hdcp2_info.hdcp_handler.sub_state) { + case HDCP2_MSG_ZERO: + if (hdcp_info->hdcp2_info.enable) { + mdrv_dp_tx_hdcp2_init(hdcp_info); + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, + HDCP2_MSG_ZERO); + DPTXHDCPMSG("Sink Support Hdcp2x!\n"); + } else { + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_H1P1, + HDCP2_MSG_AUTH_FAIL); + DPTXHDCPMSG("Sink Doesn't Support Hdcp2x!\n"); + } + break; + } + break; + + case HDCP2_MS_A1F1: + switch (hdcp_info->hdcp2_info.hdcp_handler.sub_state) { + case HDCP2_MSG_ZERO: + if (hdcp_info->hdcp2_info.retry_count + < HDCP2_TX_RETRY_CNT) { + hdcp_info->hdcp2_info.retry_count++; + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, + HDCP2_MSG_AKE_INIT); + } else { + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_H1P1, + HDCP2_MSG_AUTH_FAIL); + pr_err("Try Max Count\n"); + } + break; + + case HDCP2_MSG_AKE_INIT: + tmp = mdrv_dp_tx_hdcp2_write_msg(hdcp_info, HDCP2_MSG_AKE_INIT); + if (!tmp) { + err_code = HDCP_ERR_SEND_MSG_FAIL; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + mdrv_dp_tx_hdcp2_rest_variable(hdcp_info); + hdcp_info->hdcp2_info.read_certrx = true; + + hdcp_info->hdcp2_info.hdcp_handler.send_ake_init = true; + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, + HDCP2_MSG_AKE_SEND_CERT); + pre_time = get_system_time(); + break; + + case HDCP2_MSG_AKE_SEND_CERT: + time = get_time_diff(pre_time); + if (time < HDCP2_AKESENDCERT_WDT) { + msleep(20); + break; + } + if (hdcp_info->hdcp2_info.read_certrx) + mdrv_dp_tx_hdcp2_read_msg(hdcp_info, HDCP2_MSG_AKE_SEND_CERT); + + if (!hdcp_info->hdcp2_info.hdcp_handler.recv_msg) + break; + + ret = tee_ake_certificate(hdcp_info, hdcp_info->hdcp2_info.hdcp_rx.cert, + &stored, + hdcp_info->hdcp2_info.stored_pairing_info.m, + hdcp_info->hdcp2_info.stored_pairing_info.ekh_km); + + if (ret != RET_COMPARE_PASS) { + err_code = HDCP_ERR_PROCESS_FAIL; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + hdcp_info->hdcp2_info.hdcp_handler.stored_km = stored; + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false; + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, + hdcp_info->hdcp2_info.hdcp_handler.stored_km ? + HDCP2_MSG_AKE_STORED_KM : + HDCP2_MSG_AKE_NO_STORED_KM); + break; + + case HDCP2_MSG_AKE_NO_STORED_KM: + DPTXHDCPMSG("4. Get Km, derive Ekpub(km)\n"); + + tee_enc_rsaes_oaep(hdcp_info, hdcp_info->hdcp2_info.hdcp_tx.ekpub_km); + /* Prepare ekpub_km to send */ + tmp = mdrv_dp_tx_hdcp2_write_msg(hdcp_info, + HDCP2_MSG_AKE_NO_STORED_KM); + if (!tmp) { + err_code = HDCP_ERR_SEND_MSG_FAIL; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, + HDCP2_MSG_AKE_SEND_H_PRIME); + timeout_value = HDCP2_AKESENDHPRIME_NO_STORED_WDT; + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false; + pre_time = get_system_time(); + break; + case HDCP2_MSG_AKE_STORED_KM: + /* Prepare ekh_km & M to send */ + tmp = mdrv_dp_tx_hdcp2_write_msg(hdcp_info, HDCP2_MSG_AKE_STORED_KM); + if (!tmp) { + err_code = HDCP_ERR_SEND_MSG_FAIL; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + err_code = HDCP_ERR_NONE; + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, + HDCP2_MSG_AKE_SEND_H_PRIME); + timeout_value = HDCP2_AKESENDHPRIME_STORED_WDT; + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false; + pre_time = get_system_time(); + break; + + case HDCP2_MSG_AKE_SEND_H_PRIME: + if (hdcp_info->hdcp2_info.read_h_prime) { + mdrv_dp_tx_hdcp2_read_msg(hdcp_info, + HDCP2_MSG_AKE_SEND_H_PRIME); + } + time = get_time_diff(pre_time); + if (time > timeout_value) { + err_code = HDCP_ERR_RESPONSE_TIMEROUT; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + if (!hdcp_info->hdcp2_info.hdcp_handler.recv_msg) + break; + + ret = tee_ake_h_prime(hdcp_info, hdcp_info->hdcp2_info.hdcp_tx.rtx, + hdcp_info->hdcp2_info.hdcp_rx.rrx, + hdcp_info->hdcp2_info.hdcp_rx.rx_caps, + hdcp_info->hdcp2_info.hdcp_tx.tx_caps, + hdcp_info->hdcp2_info.hdcp_rx.h_prime, + HDCP2_HPRIME_SIZE); + if (ret != RET_COMPARE_PASS) { + if (hdcp_info->hdcp2_info.hdcp_handler.stored_km) + tee_clear_paring(hdcp_info); + err_code = HDCP_ERR_PROCESS_FAIL; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + if (hdcp_info->hdcp2_info.hdcp_handler.stored_km) + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A2F2, + HDCP2_MSG_LC_INIT); + else + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, + HDCP2_MSG_AKE_SEND_PAIRING_INFO); + + pre_time = get_system_time(); + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false; + break; + + case HDCP2_MSG_AKE_SEND_PAIRING_INFO: + if (hdcp_info->hdcp2_info.read_pairing) + mdrv_dp_tx_hdcp2_read_msg(hdcp_info, + HDCP2_MSG_AKE_SEND_PAIRING_INFO); + + /* Ekh_Km must be available less than 200ms, Give mode time for some Rx */ + time = get_time_diff(pre_time); + if (time > HDCP2_AKESENDPAIRINGINFO_WDT * 2) { + err_code = HDCP_ERR_RESPONSE_TIMEROUT; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + if (!hdcp_info->hdcp2_info.hdcp_handler.recv_msg) + break; + + /* Store m, km, Ekh(km) */ + tee_ake_paring(hdcp_info, hdcp_info->hdcp2_info.hdcp_rx.ekh_km); + + hdcp_info->hdcp2_info.hdcp_handler.send_pair = true; + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false; + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A2F2, HDCP2_MSG_LC_INIT); + pre_time = get_system_time(); + break; + } + break; + + case HDCP2_MS_A2F2: + switch (hdcp_info->hdcp2_info.hdcp_handler.sub_state) { + case HDCP2_MSG_LC_INIT: + /* prepare Rn to send */ + tmp = mdrv_dp_tx_hdcp2_write_msg(hdcp_info, HDCP2_MSG_LC_INIT); + if (!tmp) { + err_code = HDCP_ERR_SEND_MSG_FAIL; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + hdcp_info->hdcp2_info.hdcp_handler.send_lc_init = true; + + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A2F2, + HDCP2_MSG_LC_SEND_L_PRIME); + pre_time = get_system_time(); + break; + + case HDCP2_MSG_LC_SEND_L_PRIME: + time = get_time_diff(pre_time); + if (time < HDCP2_LCSENDLPRIME_WDT) + break; + + if (hdcp_info->hdcp2_info.read_l_prime) + mdrv_dp_tx_hdcp2_read_msg(hdcp_info, + HDCP2_MSG_LC_SEND_L_PRIME); + + if (!hdcp_info->hdcp2_info.hdcp_handler.recv_msg) + break; + + ret = tee_lc_l_prime(hdcp_info, hdcp_info->hdcp2_info.hdcp_tx.rn, + hdcp_info->hdcp2_info.hdcp_rx.l_prime, + HDCP2_LPRIME_SIZE); + if (ret != RET_COMPARE_PASS) { + err_code = HDCP_ERR_PROCESS_FAIL; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + DPTXHDCPMSG("L' is PASS!!\n"); + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false; + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A3F3, HDCP2_MSG_ZERO); + pre_time = get_system_time(); + break; + } + break; + + case HDCP2_MS_A3F3: + switch (hdcp_info->hdcp2_info.hdcp_handler.sub_state) { + case HDCP2_MSG_ZERO: + tee_ske_enc_ks(hdcp_info, hdcp_info->hdcp2_info.hdcp_tx.riv, + hdcp_info->hdcp2_info.hdcp_tx.eks); + + tmp = mdrv_dp_tx_hdcp2_write_msg(hdcp_info, HDCP2_MSG_SKE_SEND_EKS); + if (!tmp) { + err_code = HDCP_ERR_SEND_MSG_FAIL; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A3F3, + HDCP2_MSG_SKE_SEND_EKS); + pre_time = get_system_time(); + break; + + case HDCP2_MSG_SKE_SEND_EKS: + time = get_time_diff(pre_time); + if (time >= HDCP2_ENC_EN_TIMER) { + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A4F4, + HDCP2_MSG_ZERO); + } + break; + } + break; + + case HDCP2_MS_A4F4: + switch (hdcp_info->hdcp2_info.hdcp_handler.sub_state) { + case HDCP2_MSG_ZERO: + if (!hdcp_info->hdcp2_info.repeater) { + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A5F5, + HDCP2_MSG_AUTH_DONE); + } else { + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A6F6, + HDCP2_MSG_REPAUTH_SEND_RECVID_LIST); + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false; + pre_time = get_system_time(); + } + break; + } + break; + + case HDCP2_MS_A5F5: + switch (hdcp_info->hdcp2_info.hdcp_handler.sub_state) { + case HDCP2_MSG_ZERO: + break; + case HDCP2_MSG_AUTH_DONE: + DPTXHDCPMSG("HDCP2.x Authentication done.\n"); + hdcp_info->auth_status = AUTH_PASS; + hdcp_info->hdcp2_info.retry_count = 0; + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A5F5, HDCP2_MSG_ZERO); + mdrv_dp_tx_hdcp2_enable_auth(hdcp_info, true); + break; + } + break; + case HDCP2_MS_A6F6: + switch (hdcp_info->hdcp2_info.hdcp_handler.sub_state) { + case HDCP2_MSG_REPAUTH_SEND_RECVID_LIST: + if (hdcp_info->hdcp2_info.read_v_prime) + mdrv_dp_tx_hdcp2_read_msg(hdcp_info, + HDCP2_MSG_REPAUTH_SEND_RECVID_LIST); + + time = get_time_diff(pre_time); + if (time > HDCP2_REPAUTHSENDRECVID_WDT) { + err_code = HDCP_ERR_RESPONSE_TIMEROUT; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + if (!hdcp_info->hdcp2_info.hdcp_handler.recv_msg) + break; + + pre_time = get_system_time(); + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false; + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A7F7, + HDCP2_MSG_REPAUTH_VERIFY_RECVID_LIST); + break; + } + break; + + case HDCP2_MS_A7F7: + switch (hdcp_info->hdcp2_info.hdcp_handler.sub_state) { + case HDCP2_MSG_REPAUTH_VERIFY_RECVID_LIST: + if ((hdcp_info->hdcp2_info.hdcp_rx.rx_info[1] & (BIT(2) | BIT(3))) != 0) { + pr_err("DEVS_EXCEEDED or CASCADE_EXCEDDED!\n"); + err_code = HDCP_ERR_PROCESS_FAIL; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + /* check seqNumV here */ + tmp = mdrv_dp_tx_hdcp2_check_seq_num_v(hdcp_info); + if (!tmp) { + err_code = HDCP_ERR_PROCESS_FAIL; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + tmp = mdrv_dp_tx_hdcp2_recv_rep_auth_send_recv_id_list(hdcp_info); + if (!tmp) { + err_code = HDCP_ERR_PROCESS_FAIL; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A8F8, + HDCP2_MSG_REPAUTH_SEND_ACK); + break; + } + break; + + case HDCP2_MS_A8F8: + switch (hdcp_info->hdcp2_info.hdcp_handler.sub_state) { + case HDCP2_MSG_REPAUTH_SEND_ACK: + tmp = mdrv_dp_tx_hdcp2_write_msg(hdcp_info, + HDCP2_MSG_REPAUTH_SEND_ACK); + if (!tmp) { + err_code = HDCP_ERR_SEND_MSG_FAIL; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + time = get_time_diff(pre_time); + if (time > HDCP2_REP_SEND_ACK) { + err_code = HDCP_ERR_RESPONSE_TIMEROUT; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A9F9, + HDCP2_MSG_REPAUTH_STREAM_MANAGE); + hdcp_info->hdcp2_info.hdcp_handler.retry_cnt = 0; + break; + } + break; + + case HDCP2_MS_A9F9: + switch (hdcp_info->hdcp2_info.hdcp_handler.sub_state) { + case HDCP2_MSG_REPAUTH_STREAM_MANAGE: + tmp = mdrv_dp_tx_hdcp2_process_rep_auth_stream_manage(hdcp_info); + if (!tmp) { + err_code = HDCP_ERR_PROCESS_FAIL; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + tmp = mdrv_dp_tx_hdcp2_write_msg(hdcp_info, + HDCP2_MSG_REPAUTH_STREAM_MANAGE); + if (!tmp) { + err_code = HDCP_ERR_SEND_MSG_FAIL; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + pre_time = get_system_time(); + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false; + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A9F9, + HDCP2_MSG_REPAUTH_STREAM_READY); + break; + case HDCP2_MSG_REPAUTH_STREAM_READY: + time = get_time_diff(pre_time); + if (time > HDCP2_REPAUTHSTREAMRDY_WDT / 2) + mdrv_dp_tx_hdcp2_read_msg(hdcp_info, + HDCP2_MSG_REPAUTH_STREAM_READY); + else + break; + + time = get_time_diff(pre_time); + if (time > HDCP2_REPAUTHSTREAMRDY_WDT) { + err_code = HDCP_ERR_RESPONSE_TIMEROUT; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } else if (!hdcp_info->hdcp2_info.hdcp_handler.recv_msg) { + if (hdcp_info->hdcp2_info.hdcp_handler.retry_cnt + >= HDCP2_STREAM_MANAGE_RETRY_CNT) { + err_code = HDCP_ERR_RESPONSE_TIMEROUT; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + hdcp_info->hdcp2_info.hdcp_handler.retry_cnt++; + + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A9F9, + HDCP2_MSG_REPAUTH_STREAM_READY); + break; + } + + tmp = mdrv_dp_tx_hdcp2_recv_rep_auth_stream_ready(hdcp_info); + if (!tmp) { + err_code = HDCP_ERR_PROCESS_FAIL; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A5F5, + HDCP2_MSG_AUTH_DONE); + break; + } + break; + default: + err_code = HDCP_ERR_UNKNOWN_STATE; + mdrv_dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + return err_code; +} + +void mdrv_dp_tx_hdcp2_set_start_auth(struct mtk_hdcp_info *hdcp_info, bool enable) +{ + hdcp_info->hdcp2_info.enable = enable; + + if (enable) { + hdcp_info->auth_status = AUTH_INIT; + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A0F0, HDCP2_MSG_ZERO); + } else { + hdcp_info->auth_status = AUTH_ZERO; + mdrv_dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_H1P1, HDCP2_MSG_ZERO); + mdrv_dp_tx_hdcp2_enable_auth(hdcp_info, false); + } + + hdcp_info->hdcp2_info.retry_count = 0; +} + +bool mdrv_dp_tx_hdcp2_support(struct mtk_hdcp_info *hdcp_info) +{ + u8 temp_buffer[3]; + int ret; + + drm_dp_dpcd_read(hdcp_info->aux, DP_HDCP_2_2_REG_RX_CAPS_OFFSET, temp_buffer, 0x3); + + if ((temp_buffer[2] & BIT(1)) && temp_buffer[0] == 0x02) { + hdcp_info->hdcp2_info.enable = true; + hdcp_info->hdcp2_info.repeater = temp_buffer[2] & BIT(0); + } else { + hdcp_info->hdcp2_info.enable = false; + } + + DPTXHDCPMSG("HDCP.2x CAPABLE: %d, Reapeater: %d\n", + hdcp_info->hdcp2_info.enable, + hdcp_info->hdcp2_info.repeater); + + if (!hdcp_info->hdcp2_info.enable) + return false; + + ret = tee_add_device(hdcp_info, HDCP_VERSION_2X); + if (ret != RET_SUCCESS) { + pr_err("HDCP TA has some error\n"); + hdcp_info->hdcp2_info.enable = false; + } + + return hdcp_info->hdcp2_info.enable; +} + +bool mdrv_dp_tx_hdcp2_irq(struct mtk_hdcp_info *hdcp_info) +{ + u8 rx_status = 0; + + drm_dp_dpcd_read(hdcp_info->aux, DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, + HDCP2_RXSTATUS_SIZE); + + if (rx_status & BIT(0)) { + DPTXHDCPMSG("READY_BIT0 Ready!\n"); + hdcp_info->hdcp2_info.read_v_prime = true; + } + + if (rx_status & BIT(1)) { + DPTXHDCPMSG("H'_AVAILABLE Ready!\n"); + hdcp_info->hdcp2_info.read_h_prime = true; + } + + if (rx_status & BIT(2)) { + DPTXHDCPMSG("PAIRING_AVAILABLE Ready!\n"); + hdcp_info->hdcp2_info.read_pairing = true; + } + + if (rx_status & BIT(4) || rx_status & BIT(3)) { + DPTXHDCPMSG("Re-Auth HDCP2X!\n"); + mdrv_dp_tx_hdcp2_set_start_auth(hdcp_info, true); + mtk_dp_re_authentication(hdcp_info); + } + + return true; +} + 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..f4f4b3dc7c9b --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019-2023 MediaTek Inc. + */ + +#ifndef _MTK_dp_HDCP2_H_ +#define _MTK_dp_HDCP2_H_ + +#include "mtk_dp_hdcp.h" + +/* Timeout relative */ +#define HDCP2_AKESENDCERT_WDT 100 // 100ms +#define HDCP2_AKESENDHPRIME_NO_STORED_WDT 1000 // 1sec +#define HDCP2_AKESENDHPRIME_STORED_WDT 200 // 200ms +#define HDCP2_AKESENDPAIRINGINFO_WDT 200 // 200ms +#define HDCP2_LCSENDLPRIME_WDT 7 // 7ms +#define HDCP2_ENC_EN_TIMER 200 // 200 ms +#define HDCP2_REPAUTHSENDRECVID_WDT 3000 // 3 sec +#define HDCP2_REP_SEND_ACK 2000 // 2 Sec +#define HDCP2_REPAUTHSTREAMRDY_WDT 100 // 100 ms + +/* Patch for QD980 LLCTS */ +#define HDCP2_TX_RETRY_CNT 3 +#define HDCP2_TX_LC_RETRY_CNT 1023 +#define HDCP2_STREAM_MANAGE_RETRY_CNT 8 + +enum ENUM_HDCP2TX_MAIN_STATE { + HDCP2_MS_H1P1 = 0, + HDCP2_MS_A0F0 = 1, + HDCP2_MS_A1F1 = 2, + HDCP2_MS_A2F2 = 3, + HDCP2_MS_A3F3 = 4, + HDCP2_MS_A4F4 = 5, + HDCP2_MS_A5F5 = 6, + HDCP2_MS_A6F6 = 7, + HDCP2_MS_A7F7 = 8, + HDCP2_MS_A8F8 = 9, + HDCP2_MS_A9F9 = 10 +}; + +enum ENUM_HDCP2_MSG_LIST { + HDCP2_MSG_ZERO = 0, + HDCP2_MSG_AKE_INIT = 1, + HDCP2_MSG_AKE_SEND_CERT = 2, + HDCP2_MSG_AKE_NO_STORED_KM = 3, + HDCP2_MSG_AKE_STORED_KM = 4, + HDCP2_MSG_AKE_SEND_H_PRIME = 5, + HDCP2_MSG_AKE_SEND_PAIRING_INFO = 6, + HDCP2_MSG_LC_INIT = 7, + HDCP2_MSG_LC_SEND_L_PRIME = 8, + HDCP2_MSG_SKE_SEND_EKS = 9, + HDCP2_MSG_REPAUTH_SEND_RECVID_LIST = 10, + HDCP2_MSG_REPAUTH_VERIFY_RECVID_LIST = 11, + HDCP2_MSG_REPAUTH_SEND_ACK = 12, + HDCP2_MSG_REPAUTH_STREAM_MANAGE = 13, + HDCP2_MSG_REPAUTH_STREAM_READY = 14, + HDCP2_MSG_AUTH_FAIL = 15, + HDCP2_MSG_AUTH_DONE = 16, +}; + +enum ENUM_HDCP_ERR_CODE { + HDCP_ERR_NONE = 0, + HDCP_ERR_UNKNOWN_STATE, + HDCP_ERR_SEND_MSG_FAIL, + HDCP_ERR_RESPONSE_TIMEROUT, + HDCP_ERR_PROCESS_FAIL +}; + +int mdrv_dp_tx_hdcp2_fsm(struct mtk_hdcp_info *hdcp_info); +void mdrv_dp_tx_hdcp2_set_start_auth(struct mtk_hdcp_info *hdcp_info, bool enable); +bool mdrv_dp_tx_hdcp2_support(struct mtk_hdcp_info *hdcp_info); +bool mdrv_dp_tx_hdcp2_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..a1e4531c0e59 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-2023 MediaTek Inc. * Copyright (c) 2022 BayLibre */ #ifndef _MTK_DP_REG_H_ @@ -275,8 +275,12 @@ #define MTK_DP_TRANS_P0_3430 0x3430 #define HPD_INT_THD_ECO_DP_TRANS_P0_MASK GENMASK(1, 0) #define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT BIT(1) +#define MTK_DP_TRANS_P0_3480 0x3480 +#define REQ_BLOCK_CIPHER_AUTH BIT(12) +#define KM_GENERATED BIT(4) #define MTK_DP_TRANS_P0_34A4 0x34a4 #define LANE_NUM_DP_TRANS_P0_MASK GENMASK(3, 2) +#define MTK_DP_TRANS_P0_34D0 0x34D0 #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) -- 2.40.1