Add HDCP2.x feature for DisplayPort. When userspace request the kernel protect future content communicated over the link with Content_Protection property, the feature will do HDCP2.x authentication if the sink support HDCP2.X. Changes in v2: - remove switch case, and refine code to make more clear - remove some definitions, and use the definitions in include/drm/drm_hdcp.h - use the struct which defined in include/drm/drm_hdcp.h - do HDCP2.x authentication when userspace request the kernel protect future content communicated per suggestion from the previous thread: https://lore.kernel.org/all/8fff59b5567449d8201dd1138c8fa 9218a545c46.camel@xxxxxxxxxxxx/ Signed-off-by: mac.shen <mac.shen@xxxxxxxxxxxx> --- drivers/gpu/drm/mediatek/Makefile | 1 + drivers/gpu/drm/mediatek/mtk_dp.c | 297 +++++-- drivers/gpu/drm/mediatek/mtk_dp.h | 99 +++ drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c | 1021 +++++++++++++++++++++++ drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h | 52 ++ drivers/gpu/drm/mediatek/mtk_dp_reg.h | 4 +- drivers/gpu/drm/mediatek/mtk_dpi.c | 3 + 7 files changed, 1392 insertions(+), 85 deletions(-) create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.h create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile index c80e6c2f9336..50ea069b047e 100644 --- a/drivers/gpu/drm/mediatek/Makefile +++ b/drivers/gpu/drm/mediatek/Makefile @@ -27,6 +27,7 @@ mediatek-drm-hdmi-objs := mtk_cec.o \ obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o mtk-dp-objs := tlc_dp_hdcp.o \ + mtk_dp_hdcp2.o \ mtk_dp.o obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk-dp.o diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c index e4c16ba9902d..7ff72f15528b 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp.c +++ b/drivers/gpu/drm/mediatek/mtk_dp.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2019-2022 MediaTek Inc. + * Copyright (c) 2019-2024 MediaTek Inc. * Copyright (c) 2022 BayLibre */ @@ -8,13 +8,13 @@ #include <drm/display/drm_dp.h> #include <drm/display/drm_dp_helper.h> #include <drm/drm_atomic_helper.h> -#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> +#include <drm/display/drm_hdcp_helper.h> #include <linux/arm-smccc.h> #include <linux/clk.h> #include <linux/delay.h> @@ -30,10 +30,10 @@ #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/soc/mediatek/mtk_sip_svc.h> -#include <sound/hdmi-codec.h> -#include <video/videomode.h> +#include "mtk_dp.h" #include "mtk_dp_reg.h" +#include "mtk_dp_hdcp2.h" #define MTK_DP_SIP_CONTROL_AARCH32 MTK_SIP_SMC_CMD(0x523) #define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE (BIT(0) | BIT(5)) @@ -52,43 +52,6 @@ #define MTK_DP_VERSION 0x11 #define MTK_DP_SDP_AUI 0x4 -enum { - MTK_DP_CAL_GLB_BIAS_TRIM = 0, - MTK_DP_CAL_CLKTX_IMPSE, - MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0, - MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1, - MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2, - MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3, - MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0, - MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1, - MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2, - MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3, - MTK_DP_CAL_MAX, -}; - -struct mtk_dp_train_info { - bool sink_ssc; - bool cable_plugged_in; - /* link_rate is in multiple of 0.27Gbps */ - int link_rate; - int lane_count; - unsigned int channel_eq_pattern; -}; - -struct mtk_dp_audio_cfg { - bool detect_monitor; - int sad_count; - int sample_rate; - int word_length_bits; - int channels; -}; - -struct mtk_dp_info { - enum dp_pixelformat format; - struct videomode vm; - struct mtk_dp_audio_cfg audio_cur_cfg; -}; - struct mtk_dp_efuse_fmt { unsigned short idx; unsigned short shift; @@ -98,44 +61,6 @@ struct mtk_dp_efuse_fmt { unsigned short default_val; }; -struct mtk_dp { - bool enabled; - bool need_debounce; - int irq; - u8 max_lanes; - u8 max_linkrate; - u8 rx_cap[DP_RECEIVER_CAP_SIZE]; - u32 cal_data[MTK_DP_CAL_MAX]; - u32 irq_thread_handle; - /* irq_thread_lock is used to protect irq_thread_handle */ - spinlock_t irq_thread_lock; - - struct device *dev; - struct drm_bridge bridge; - struct drm_bridge *next_bridge; - struct drm_connector *conn; - struct drm_device *drm_dev; - struct drm_dp_aux aux; - - const struct mtk_dp_data *data; - struct mtk_dp_info info; - struct mtk_dp_train_info train_info; - - struct platform_device *phy_dev; - struct phy *phy; - struct regmap *regs; - struct timer_list debounce_timer; - - /* For audio */ - bool audio_enable; - hdmi_codec_plugged_cb plugged_cb; - struct platform_device *audio_pdev; - - struct device *codec_dev; - /* protect the plugged_cb as it's used in both bridge ops and audio */ - struct mutex update_plugged_status_lock; -}; - struct mtk_dp_data { int bridge_type; unsigned int smc_cmd; @@ -319,12 +244,28 @@ static struct regmap_config mtk_dp_regmap_config = { .name = "mtk-dp-registers", }; +u32 mtk_dp_get_system_time(void) +{ + u32 tms = (u32)((sched_clock() / 1000000) % 1000000); + return tms; +} + +u32 mtk_dp_get_time_diff(u32 pre_time) +{ + u32 post_time = mtk_dp_get_system_time(); + + if (pre_time > post_time) + return ((1000000 - pre_time) + post_time); + else + return (post_time - pre_time); +} + static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b) { return container_of(b, struct mtk_dp, bridge); } -static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset) +u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset) { u32 read_val; int ret; @@ -350,8 +291,8 @@ static int mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val) return ret; } -static int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, - u32 val, u32 mask) +int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, + u32 val, u32 mask) { int ret = regmap_update_bits(mtk_dp->regs, offset, mask, val); @@ -1865,6 +1806,160 @@ static void mtk_dp_init_port(struct mtk_dp *mtk_dp) mtk_dp_digital_sw_reset(mtk_dp); } +void mtk_dp_check_hdcp_version(struct mtk_dp *mtk_dp, bool only_hdcp1x) +{ + if (!only_hdcp1x && dp_tx_hdcp2_support(&mtk_dp->hdcp_info)) + return; + + if (tee_add_device(&mtk_dp->hdcp_info, HDCP_NONE) != RET_SUCCESS) + mtk_dp->hdcp_info.auth_status = AUTH_FAIL; +} + +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) { + dp_tx_hdcp2_irq(&mtk_dp->hdcp_info); + drm_dp_dpcd_write(&mtk_dp->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 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) + dp_tx_hdcp2_set_start_auth(&mtk_dp->hdcp_info, true); + else + mtk_dp->hdcp_info.auth_status = AUTH_ZERO; + } + + while (mtk_dp->hdcp_info.hdcp2_info.enable && + mtk_dp->hdcp_info.auth_status != AUTH_FAIL && + mtk_dp->hdcp_info.auth_status != AUTH_PASS) { + if (mtk_dp->hdcp_info.hdcp2_info.enable) + dp_tx_hdcp2_fsm(&mtk_dp->hdcp_info); + } +} + +static void mtk_dp_hdcp_prop_work(struct work_struct *work) +{ + struct mtk_dp *mtk_dp; + struct delayed_work *dework; + struct drm_device *drm_dev; + + dework = to_delayed_work(work); + mtk_dp = container_of(dework, struct mtk_dp, prop_work); + + if (!mtk_dp->conn) { + dev_err(mtk_dp->dev, "connector is null!"); + return; + } + + drm_dev = mtk_dp->conn->dev; + + drm_modeset_lock(&drm_dev->mode_config.connection_mutex, NULL); + + if (mtk_dp->hdcp_info.content_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) + mtk_dp->hdcp_info.content_protection = + (mtk_dp->hdcp_info.auth_status == AUTH_PASS) ? DRM_MODE_CONTENT_PROTECTION_ENABLED : + DRM_MODE_CONTENT_PROTECTION_DESIRED; + else + mtk_dp->hdcp_info.content_protection = + (mtk_dp->hdcp_info.auth_status == AUTH_PASS) ? DRM_MODE_CONTENT_PROTECTION_ENABLED : + DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + + dev_info(mtk_dp->dev, "update CP, content protection: %d, auth status:%d\n", + mtk_dp->hdcp_info.content_protection, mtk_dp->hdcp_info.auth_status); + drm_hdcp_update_content_protection(mtk_dp->conn, mtk_dp->hdcp_info.content_protection); + + drm_modeset_unlock(&drm_dev->mode_config.connection_mutex); +} + +static void mtk_dp_hdcp_atomic_check(struct mtk_dp *mtk_dp, struct drm_connector_state *state) +{ + unsigned int hdcp_content_type = state->hdcp_content_type; + unsigned int content_protection = state->content_protection; + + dev_dbg(mtk_dp->dev, "CP type and protection, atomic: %d, %d, driver:%d, %d\n", + hdcp_content_type, content_protection, + mtk_dp->hdcp_info.hdcp_content_type, mtk_dp->hdcp_info.content_protection); + + if ((content_protection == DRM_MODE_CONTENT_PROTECTION_UNDESIRED && + mtk_dp->hdcp_info.content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED) || + (hdcp_content_type != mtk_dp->hdcp_info.hdcp_content_type && + content_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED)) { + mtk_dp->hdcp_info.content_protection = content_protection; + + dev_dbg(mtk_dp->dev, "disable HDCP\n"); + if (mtk_dp->hdcp_info.hdcp2_info.enable) + dp_tx_hdcp2_set_start_auth(&mtk_dp->hdcp_info, false); + + drm_hdcp_update_content_protection(mtk_dp->conn, + mtk_dp->hdcp_info.content_protection); + } + + if (content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED || + (hdcp_content_type != mtk_dp->hdcp_info.hdcp_content_type && + content_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED)) { + mtk_dp->hdcp_info.content_protection = content_protection; + mtk_dp->hdcp_info.hdcp_content_type = hdcp_content_type; + mtk_dp->hdcp_info.hdcp2_info.stream_id_type = hdcp_content_type; + + if (mtk_dp->hdcp_info.auth_status == AUTH_FAIL || + mtk_dp->hdcp_info.auth_status == AUTH_ZERO) { + dev_dbg(mtk_dp->dev, "enable HDCP\n"); + mtk_dp_authentication(&mtk_dp->hdcp_info); + + queue_delayed_work(mtk_dp->hdcp_workqueue, + &mtk_dp->prop_work, msecs_to_jiffies(2000)); + } + } +} + +void mtk_dp_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); +} + static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev) { struct mtk_dp *mtk_dp = dev; @@ -1894,9 +1989,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; } @@ -2233,6 +2330,7 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge, struct drm_bridge_state *old_state) { struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge); + struct drm_connector_state *connector_state; int ret; mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state, @@ -2243,6 +2341,8 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge, return; } + connector_state = drm_atomic_get_connector_state(old_state->base.state, mtk_dp->conn); + mtk_dp_aux_panel_poweron(mtk_dp, true); /* Training */ @@ -2272,6 +2372,14 @@ static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge, mtk_dp->enabled = true; mtk_dp_update_plugged_status(mtk_dp); + /* Enable hdcp if it's desired */ + dev_info(mtk_dp->dev, "hdcp_content_type:%d, content protection: %d", + connector_state->hdcp_content_type, connector_state->content_protection); + if (connector_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED) { + mtk_dp->hdcp_info.hdcp_content_type = connector_state->hdcp_content_type; + mtk_dp_authentication(&mtk_dp->hdcp_info); + } + return; power_off_aux: mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, @@ -2284,6 +2392,17 @@ 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) + dp_tx_hdcp2_set_start_auth(&mtk_dp->hdcp_info, false); + + if (mtk_dp->hdcp_info.content_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { + mtk_dp->hdcp_info.content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + dev_info(mtk_dp->dev, "update CP, content protection: %d\n", + mtk_dp->hdcp_info.content_protection); + drm_hdcp_update_content_protection(mtk_dp->conn, + mtk_dp->hdcp_info.content_protection); + } + mtk_dp->enabled = false; mtk_dp_update_plugged_status(mtk_dp); mtk_dp_video_enable(mtk_dp, false); @@ -2418,6 +2537,8 @@ static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge, drm_display_mode_to_videomode(&crtc_state->adjusted_mode, &mtk_dp->info.vm); + mtk_dp_hdcp_atomic_check(mtk_dp, conn_state); + return 0; } @@ -2657,6 +2778,14 @@ static int mtk_dp_probe(struct platform_device *pdev) if (ret) return ret; + INIT_WORK(&mtk_dp->hdcp_work, mtk_dp_hdcp_handle); + INIT_DELAYED_WORK(&mtk_dp->prop_work, mtk_dp_hdcp_prop_work); + mtk_dp->hdcp_workqueue = create_workqueue("mtk_dp_hdcp_work"); + if (!mtk_dp->hdcp_workqueue) { + dev_err(mtk_dp->dev, "failed to create hdcp work queue"); + return -ENOMEM; + } + mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs; mtk_dp->bridge.of_node = dev->of_node; mtk_dp->bridge.type = mtk_dp->data->bridge_type; diff --git a/drivers/gpu/drm/mediatek/mtk_dp.h b/drivers/gpu/drm/mediatek/mtk_dp.h new file mode 100644 index 000000000000..85c0cf0005c0 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dp.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019-2024 MediaTek Inc. + */ + +#ifndef _MTK_DP_H_ +#define _MTK_DP_H_ + +#include "tlc_dp_hdcp.h" +#include <drm/drm_bridge.h> +#include <sound/hdmi-codec.h> +#include <video/videomode.h> + +enum { + MTK_DP_CAL_GLB_BIAS_TRIM = 0, + MTK_DP_CAL_CLKTX_IMPSE, + MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0, + MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1, + MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2, + MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3, + MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0, + MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1, + MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2, + MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3, + MTK_DP_CAL_MAX, +}; + +struct mtk_dp_audio_cfg { + bool detect_monitor; + int sad_count; + int sample_rate; + int word_length_bits; + int channels; +}; + +struct mtk_dp_info { + enum dp_pixelformat format; + struct videomode vm; + struct mtk_dp_audio_cfg audio_cur_cfg; +}; + +struct mtk_dp_train_info { + bool sink_ssc; + bool cable_plugged_in; + /* link_rate is in multiple of 0.27Gbps */ + int link_rate; + int lane_count; + unsigned int channel_eq_pattern; +}; + +struct mtk_dp { + bool enabled; + bool need_debounce; + int irq; + u8 max_lanes; + u8 max_linkrate; + u8 rx_cap[DP_RECEIVER_CAP_SIZE]; + u32 cal_data[MTK_DP_CAL_MAX]; + u32 irq_thread_handle; + /* irq_thread_lock is used to protect irq_thread_handle */ + spinlock_t irq_thread_lock; + + struct device *dev; + struct drm_bridge bridge; + struct drm_bridge *next_bridge; + struct drm_connector *conn; + struct drm_device *drm_dev; + struct drm_dp_aux aux; + + const struct mtk_dp_data *data; + struct mtk_dp_info info; + struct mtk_dp_train_info train_info; + struct mtk_hdcp_info hdcp_info; + struct work_struct hdcp_work; + struct delayed_work prop_work; + struct workqueue_struct *hdcp_workqueue; + + struct platform_device *phy_dev; + struct phy *phy; + struct regmap *regs; + struct timer_list debounce_timer; + + /* For audio */ + bool audio_enable; + hdmi_codec_plugged_cb plugged_cb; + struct platform_device *audio_pdev; + + struct device *codec_dev; + /* protect the plugged_cb as it's used in both bridge ops and audio */ + struct mutex update_plugged_status_lock; +}; + +u32 mtk_dp_get_system_time(void); +u32 mtk_dp_get_time_diff(u32 pre_time); +u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset); +int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset, u32 val, u32 mask); +void mtk_dp_authentication(struct mtk_hdcp_info *hdcp_info); + +#endif /* _MTK_DP_H_ */ diff --git a/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c new file mode 100644 index 000000000000..8528e33f3f49 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.c @@ -0,0 +1,1021 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019-2024 MediaTek Inc. + */ + +#include "mtk_dp_hdcp2.h" +#include "mtk_dp_reg.h" +#include "mtk_dp.h" + +u8 t_rtx[HDCP_2_2_RTX_LEN] = { + 0x18, 0xfa, 0xe4, 0x20, 0x6a, 0xfb, 0x51, 0x49 +}; + +u8 t_tx_caps[HDCP_2_2_TXCAPS_LEN] = { + 0x02, 0x00, 0x00 +}; + +u8 t_rn[HDCP_2_2_RN_LEN] = { + 0x32, 0x75, 0x3e, 0xa8, 0x78, 0xa6, 0x38, 0x1c +}; + +u8 t_riv[HDCP_2_2_RIV_LEN] = { + 0x40, 0x2b, 0x6b, 0x43, 0xc5, 0xe8, 0x86, 0xd8 +}; + +static void dp_tx_hdcp2_fill_stream_type(struct mtk_hdcp_info *hdcp_info, u8 uc_type) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + + mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34D0, uc_type, 0xff); +} + +static void dp_tx_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 dp_tx_hdcp2_set_auth_pass(struct mtk_hdcp_info *hdcp_info, bool enable) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + + if (enable) { + mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3400, BIT(11), BIT(11)); + mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4, BIT(4), BIT(4)); + } else { + mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3400, 0, BIT(11)); + mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4, 0, BIT(4)); + } +} + +static void dp_tx_hdcp2_enable_auth(struct mtk_hdcp_info *hdcp_info, bool enable) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + + DPTXHDCPFUNC(); + dp_tx_hdcp2_set_auth_pass(hdcp_info, enable); + if (enable) { + u32 version = HDCP_V2_3; + + if (hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.rx_info[1] & BIT(0)) + version = HDCP_V1; + else if (hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.rx_info[1] & BIT(1)) + version = HDCP_V2; + + tee_hdcp_enable_encrypt(hdcp_info, enable, version); + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, BIT(5), BIT(5)); + } else { + tee_hdcp_enable_encrypt(hdcp_info, enable, HDCP_NONE); + mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000, 0, BIT(5)); + } +} + +static int 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.ake_init.r_tx, t_rtx, HDCP_2_2_RTX_LEN); + memcpy(&hdcp_info->hdcp2_info.hdcp_tx.tx_caps, t_tx_caps, HDCP_2_2_TXCAPS_LEN); + memcpy(hdcp_info->hdcp2_info.hdcp_tx.lc_init.r_n, t_rn, HDCP_2_2_RN_LEN); + memcpy(hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, t_riv, HDCP_2_2_RIV_LEN); + + memset(&hdcp_info->hdcp2_info.hdcp_handler, 0, sizeof(struct hdcp2_handler)); + memset(&hdcp_info->hdcp2_info.ake_stored_km, 0, sizeof(struct hdcp2_ake_stored_km)); + + dp_tx_hdcp2_enable_auth(hdcp_info, false); + + return err_code; +} + +static bool dp_tx_hdcp2_inc_seq_num_m(struct mtk_hdcp_info *hdcp_info) +{ + u32 tmp = 0; + + tmp = drm_hdcp_be24_to_cpu(hdcp_info->hdcp2_info.hdcp_tx.stream_manage.seq_num_m); + + if (tmp == 0xFFFFFF) + return false; + + tmp++; + + drm_hdcp_cpu_to_be24(hdcp_info->hdcp2_info.hdcp_tx.stream_manage.seq_num_m, tmp); + return true; +} + +static bool 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 = dp_tx_hdcp2_inc_seq_num_m(hdcp_info); + + return ret; +} + +static bool 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 * HDCP_2_2_RECEIVER_ID_LEN; + len = len_recv_id_list + HDCP_2_2_RXINFO_LEN + HDCP_2_2_SEQ_NUM_LEN; + buffer = kmalloc(len, GFP_KERNEL); + if (!buffer) { + pr_err("2.x: Out of Memory\n"); + return ret; + } + + memcpy(buffer, hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.receiver_ids, + len_recv_id_list); + memcpy(buffer + len_recv_id_list, + hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.rx_info, HDCP_2_2_RXINFO_LEN); + memcpy(buffer + len_recv_id_list + HDCP_2_2_RXINFO_LEN, + hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.seq_num_v, HDCP_2_2_SEQ_NUM_LEN); + + rc = tee_hdcp2_compute_compare_v(hdcp_info, buffer, len, + hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.v_prime, + hdcp_info->hdcp2_info.hdcp_tx.send_ack.v); + + if (rc == RET_COMPARE_PASS) { + ret = true; + DPTXHDCPMSG("2.x: V' is PASS!!\n"); + } else { + DPTXHDCPMSG("2.x: V' is FAIL!!\n"); + } + + kfree(buffer); + return ret; +} + +static bool dp_tx_hdcp2_recv_rep_auth_stream_ready(struct mtk_hdcp_info *hdcp_info) +{ + bool ret = false; + u8 *buffer = NULL; + u32 len = 0; + int tmp = 0; + + len = HDCP2_STREAMID_TYPE_LEN + HDCP_2_2_SEQ_NUM_LEN; + buffer = kmalloc(len, GFP_KERNEL); + if (!buffer) { + pr_err("2.x: Out of Memory\n"); + return ret; + } + + memcpy(buffer, hdcp_info->hdcp2_info.hdcp_tx.stream_id_type, HDCP2_STREAMID_TYPE_LEN); + memcpy(buffer + HDCP2_STREAMID_TYPE_LEN, + hdcp_info->hdcp2_info.hdcp_tx.stream_manage.seq_num_m, + HDCP_2_2_SEQ_NUM_LEN); + tmp = tee_hdcp2_compute_compare_m(hdcp_info, buffer, len, + hdcp_info->hdcp2_info.hdcp_rx.stream_ready.m_prime); + + if (tmp == RET_COMPARE_PASS) { + ret = true; + DPTXHDCPMSG("2.x: M' is PASS!!\n"); + } else { + DPTXHDCPMSG("2.x: M' is FAIL!!\n"); + } + + kfree(buffer); + return ret; +} + +static bool dp_tx_hdcp2_check_seq_num_v(struct mtk_hdcp_info *hdcp_info) +{ + if ((hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.seq_num_v[0] == 0x00 && + hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.seq_num_v[1] == 0x00 && + hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.seq_num_v[2] == 0x00) && + hdcp_info->hdcp2_info.hdcp_handler.seq_num_v_cnt > 0xFFFFFF) { + DPTXHDCPMSG("2.x: SeqNumV Rollover!\n"); + return false; + } + + if ((hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.seq_num_v[0] + != (u8)((hdcp_info->hdcp2_info.hdcp_handler.seq_num_v_cnt & 0xFF0000) >> 16)) || + (hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.seq_num_v[1] + != (u8)((hdcp_info->hdcp2_info.hdcp_handler.seq_num_v_cnt & 0x00FF00) >> 8)) || + (hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.seq_num_v[2] + != (u8)((hdcp_info->hdcp2_info.hdcp_handler.seq_num_v_cnt & 0x0000FF)))) { + DPTXHDCPMSG("2.x: Invalid Seq_num_V!\n"); + return false; + } + + hdcp_info->hdcp2_info.hdcp_handler.seq_num_v_cnt++; + return true; +} + +static void dp_tx_hdcp2_err_handle(struct mtk_hdcp_info *hdcp_info, int err_msg, int line) +{ + pr_err("2.x: 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("2.x: Unknown State, line:%d\n", line); + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_H1P1, HDCP_2_2_AUTH_FAIL); + break; + + case HDCP_ERR_SEND_MSG_FAIL: + pr_err("2.x: Send Msg Fail, line:%d\n", line); + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A0F0, HDCP_2_2_NULL_MSG); + break; + case HDCP_ERR_RESPONSE_TIMEROUT: + pr_err("2.x: Response Timeout, line:%d!\n", line); + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A0F0, HDCP_2_2_NULL_MSG); + break; + + case HDCP_ERR_PROCESS_FAIL: + pr_err("2.x: Process Fail, line:%d!\n", line); + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A0F0, HDCP_2_2_NULL_MSG); + break; + + default: + pr_err("2.x: NO ERROR!"); + break; + } +} + +static bool dp_tx_hdcp2_read_msg(struct mtk_hdcp_info *hdcp_info, u8 cmd_ID) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + bool ret = false; + u8 size = 0; + + switch (cmd_ID) { + case HDCP_2_2_AKE_SEND_CERT: + drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_CERT_RX_OFFSET, + (void *)&hdcp_info->hdcp2_info.hdcp_rx.cert_rx, HDCP2_CERTRX_LEN); + drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RRX_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.send_cert.r_rx, HDCP_2_2_RRX_LEN); + drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RX_CAPS_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.send_cert.rx_caps, + HDCP_2_2_RXCAPS_LEN); + + hdcp_info->hdcp2_info.read_certrx = false; + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true; + ret = true; + DPTXHDCPMSG("2.x: HDCP_2_2_AKE_SEND_CERT\n"); + break; + + case HDCP_2_2_AKE_SEND_HPRIME: + drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_HPRIME_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.send_hprime.h_prime, + HDCP_2_2_H_PRIME_LEN); + + hdcp_info->hdcp2_info.read_h_prime = false; + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true; + ret = true; + + DPTXHDCPMSG("2.x: HDCP_2_2_AKE_SEND_HPRIME\n"); + break; + + case HDCP_2_2_AKE_SEND_PAIRING_INFO: + drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_EKH_KM_RD_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.pairing_info.e_kh_km, + HDCP_2_2_E_KH_KM_LEN); + hdcp_info->hdcp2_info.read_pairing = false; + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true; + ret = true; + DPTXHDCPMSG("2.x: HDCP_2_2_AKE_SEND_PAIRING_INFO\n"); + break; + + case HDCP_2_2_LC_SEND_LPRIME: + drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_LPRIME_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.send_lprime.l_prime, + HDCP_2_2_L_PRIME_LEN); + + hdcp_info->hdcp2_info.read_l_prime = false; + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true; + ret = true; + DPTXHDCPMSG("2.x: HDCP_2_2_LC_SEND_LPRIME\n"); + break; + + case HDCP_2_2_REP_SEND_RECVID_LIST: + drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXINFO_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.rx_info, + HDCP_2_2_RXINFO_LEN); + hdcp_info->hdcp2_info.device_count = + ((hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.rx_info[1] & 0xf0) >> 4) + | ((hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.rx_info[0] + & BIT(0)) << 4); + + drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_SEQ_NUM_V_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.seq_num_v, + HDCP_2_2_SEQ_NUM_LEN); + drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_VPRIME_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.v_prime, + HDCP_2_2_V_PRIME_HALF_LEN); + drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RECV_ID_LIST_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.receiver_ids, + hdcp_info->hdcp2_info.device_count * HDCP_2_2_RECEIVER_ID_LEN); + + hdcp_info->hdcp2_info.read_v_prime = false; + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true; + ret = true; + DPTXHDCPMSG("2.x: HDCP_2_2_REP_SEND_RECVID_LIST\n"); + break; + + case HDCP_2_2_REP_STREAM_READY: + size = drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_MPRIME_OFFSET, + hdcp_info->hdcp2_info.hdcp_rx.stream_ready.m_prime, + HDCP_2_2_MPRIME_LEN); + + if (size == HDCP_2_2_MPRIME_LEN) + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = true; + ret = true; + DPTXHDCPMSG("2.x: HDCP_2_2_REP_STREAM_READY\n"); + break; + + default: + DPTXHDCPMSG("2.x: Invalid DPTX_HDCP2_OffSETADDR_ReadMessage !\n"); + break; + } + + return ret; +} + +static bool dp_tx_hdcp2_write_msg(struct mtk_hdcp_info *hdcp_info, u8 cmd_ID) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + bool ret = false; + + switch (cmd_ID) { + case HDCP_2_2_AKE_INIT: + tee_hdcp2_soft_rst(hdcp_info); + drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_RTX_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.ake_init.r_tx, HDCP_2_2_RTX_LEN); + drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_TXCAPS_OFFSET, + (void *)&hdcp_info->hdcp2_info.hdcp_tx.tx_caps, + HDCP_2_2_TXCAPS_LEN); + + ret = true; + DPTXHDCPMSG("2.x: HDCP_2_2_AKE_Init !\n"); + break; + + case HDCP_2_2_AKE_NO_STORED_KM: + drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_EKPUB_KM_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.no_stored_km.e_kpub_km, + HDCP_2_2_E_KPUB_KM_LEN); + + ret = true; + + DPTXHDCPMSG("2.x: HDCP_2_2_AKE_NO_STORED_KM !\n"); + break; + + case HDCP_2_2_AKE_STORED_KM: + drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_EKH_KM_WR_OFFSET, + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m, + HDCP_2_2_E_KH_KM_LEN); + drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_M_OFFSET, + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m + + HDCP_2_2_E_KH_KM_LEN, + HDCP_2_2_E_KH_KM_M_LEN - HDCP_2_2_E_KH_KM_LEN); + + ret = true; + + DPTXHDCPMSG("2.x: DPTX_HDCP_2_2_AKE_STORED_KM !\n"); + break; + + case HDCP_2_2_LC_INIT: + drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_RN_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.lc_init.r_n, HDCP_2_2_RN_LEN); + + hdcp_info->hdcp2_info.read_l_prime = true; + ret = true; + + DPTXHDCPMSG("2.x: HDCP_2_2_LC_INIT !\n"); + break; + + case HDCP_2_2_SKE_SEND_EKS: + drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_EDKEY_KS_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.send_eks.e_dkey_ks, + HDCP_2_2_E_DKEY_KS_LEN); + drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_RIV_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, HDCP_2_2_RIV_LEN); + + hdcp_info->hdcp2_info.ks_exchange_done = true; + + ret = true; + DPTXHDCPMSG("2.x: HDCP_2_2_SKE_SEND_EKS !\n"); + break; + + case HDCP_2_2_STREAM_TYPE: + drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.stream_id_type, 1); + + ret = true; + DPTXHDCPMSG("HDCP2_MSG_DP_STREAM_TYPE !\n"); + break; + + case HDCP_2_2_REP_SEND_ACK: + drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_V_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.send_ack.v, + HDCP_2_2_V_PRIME_HALF_LEN); + + ret = true; + DPTXHDCPMSG("2.x: HDCP_2_2_SEND_ACK !\n"); + break; + + case HDCP_2_2_REP_STREAM_MANAGE: + drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_SEQ_NUM_M_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.stream_manage.seq_num_m, + HDCP_2_2_SEQ_NUM_LEN); + drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_K_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.k, HDCP2_K_LEN); + drm_dp_dpcd_write(&mtk_dp->aux, DP_HDCP_2_2_REG_STREAM_ID_TYPE_OFFSET, + hdcp_info->hdcp2_info.hdcp_tx.stream_id_type, + HDCP2_STREAMID_TYPE_LEN); + + dp_tx_hdcp2_fill_stream_type(hdcp_info, hdcp_info->hdcp2_info.stream_id_type); + + ret = true; + DPTXHDCPMSG("2.x: HDCP_2_2_STREAM_MANAGE !\n"); + break; + + default: + DPTXHDCPMSG("2.x: Invalid HDCP2_OffSETADDR_WriteMessage !\n"); + break; + } + + return ret; +} + +static void 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 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("2.x: 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: + /* HDCP2_MS_H1P1 */ + /* HDCP_2_2_AUTH_FAIL */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_AUTH_FAIL) { + pr_err("2.x: Authentication Fail!\n"); + dp_tx_hdcp2_enable_auth(hdcp_info, false); + hdcp_info->auth_status = AUTH_FAIL; + } + break; + + case HDCP2_MS_A0F0: + /* HDCP2_MS_A0F0 */ + /* HDCP_2_2_NULL_MSG */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_NULL_MSG) { + if (!hdcp_info->hdcp2_info.enable) { + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_H1P1, HDCP_2_2_AUTH_FAIL); + DPTXHDCPMSG("2.x: Sink Doesn't Support Hdcp2x!\n"); + break; + } + + dp_tx_hdcp2_init(hdcp_info); + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, HDCP_2_2_NULL_MSG); + DPTXHDCPMSG("2.x: Sink Support Hdcp2x!\n"); + } + break; + + case HDCP2_MS_A1F1: + /* HDCP2_MS_A1F1 */ + /* HDCP_2_2_NULL_MSG */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_NULL_MSG) { + if (hdcp_info->hdcp2_info.retry_count >= HDCP2_TX_RETRY_CNT) { + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_H1P1, HDCP_2_2_AUTH_FAIL); + pr_err("2.x: Try Max Count\n"); + break; + } + + hdcp_info->hdcp2_info.retry_count++; + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, HDCP_2_2_AKE_INIT); + } + + /* HDCP2_MS_A1F1 */ + /* HDCP_2_2_AKE_INIT */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_AKE_INIT) { + tmp = dp_tx_hdcp2_write_msg(hdcp_info, HDCP_2_2_AKE_INIT); + if (!tmp) { + err_code = HDCP_ERR_SEND_MSG_FAIL; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + dp_tx_hdcp2_rest_variable(hdcp_info); + hdcp_info->hdcp2_info.read_certrx = true; + + hdcp_info->hdcp2_info.hdcp_handler.send_ake_init = true; + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, HDCP_2_2_AKE_SEND_CERT); + pre_time = mtk_dp_get_system_time(); + } + + /* HDCP2_MS_A1F1 */ + /* HDCP_2_2_AKE_SEND_CERT */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_AKE_SEND_CERT) { + time = mtk_dp_get_time_diff(pre_time); + if (time < HDCP_2_2_CERT_TIMEOUT_MS) { + msleep(20); + break; + } + if (hdcp_info->hdcp2_info.read_certrx) + dp_tx_hdcp2_read_msg(hdcp_info, HDCP_2_2_AKE_SEND_CERT); + + if (!hdcp_info->hdcp2_info.hdcp_handler.recv_msg) + break; + + ret = tee_ake_certificate(hdcp_info, + (u8 *)&hdcp_info->hdcp2_info.hdcp_rx.cert_rx, + &stored, + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m + + HDCP_2_2_E_KH_KM_LEN, + hdcp_info->hdcp2_info.ake_stored_km.e_kh_km_m); + + if (ret != RET_COMPARE_PASS) { + err_code = HDCP_ERR_PROCESS_FAIL; + 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; + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, + hdcp_info->hdcp2_info.hdcp_handler.stored_km ? + HDCP_2_2_AKE_STORED_KM : + HDCP_2_2_AKE_NO_STORED_KM); + } + + /* HDCP2_MS_A1F1 */ + /* HDCP_2_2_AKE_NO_STORED_KM */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_AKE_NO_STORED_KM) { + DPTXHDCPMSG("2.x: Get Km, derive Ekpub(km)\n"); + + tee_enc_rsaes_oaep(hdcp_info, + hdcp_info->hdcp2_info.hdcp_tx.no_stored_km.e_kpub_km); + /* Prepare e_kpub_km to send */ + tmp = dp_tx_hdcp2_write_msg(hdcp_info, HDCP_2_2_AKE_NO_STORED_KM); + if (!tmp) { + err_code = HDCP_ERR_SEND_MSG_FAIL; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, HDCP_2_2_AKE_SEND_HPRIME); + timeout_value = HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS; + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false; + pre_time = mtk_dp_get_system_time(); + } + + /* HDCP2_MS_A1F1 */ + /* HDCP_2_2_AKE_STORED_KM */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_AKE_STORED_KM) { + /* Prepare ekh_km & M to send */ + tmp = dp_tx_hdcp2_write_msg(hdcp_info, HDCP_2_2_AKE_STORED_KM); + if (!tmp) { + err_code = HDCP_ERR_SEND_MSG_FAIL; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + err_code = HDCP_ERR_NONE; + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, HDCP_2_2_AKE_SEND_HPRIME); + timeout_value = HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS; + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false; + pre_time = mtk_dp_get_system_time(); + } + + /* HDCP2_MS_A1F1 */ + /* HDCP_2_2_AKE_SEND_HPRIME */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_AKE_SEND_HPRIME) { + if (hdcp_info->hdcp2_info.read_h_prime) + dp_tx_hdcp2_read_msg(hdcp_info, HDCP_2_2_AKE_SEND_HPRIME); + + time = mtk_dp_get_time_diff(pre_time); + if (time > timeout_value) { + err_code = HDCP_ERR_RESPONSE_TIMEROUT; + 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.ake_init.r_tx, + hdcp_info->hdcp2_info.hdcp_rx.send_cert.r_rx, + hdcp_info->hdcp2_info.hdcp_rx.send_cert.rx_caps, + (u8 *)&hdcp_info->hdcp2_info.hdcp_tx.tx_caps, + hdcp_info->hdcp2_info.hdcp_rx.send_hprime.h_prime, + HDCP_2_2_H_PRIME_LEN); + if (ret != RET_COMPARE_PASS) { + if (hdcp_info->hdcp2_info.hdcp_handler.stored_km) + tee_clear_paring(hdcp_info); + err_code = HDCP_ERR_PROCESS_FAIL; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + if (hdcp_info->hdcp2_info.hdcp_handler.stored_km) + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A2F2, HDCP_2_2_LC_INIT); + else + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A1F1, + HDCP_2_2_AKE_SEND_PAIRING_INFO); + + pre_time = mtk_dp_get_system_time(); + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false; + } + + /* HDCP2_MS_A1F1 */ + /* HDCP_2_2_AKE_SEND_PAIRING_INFO */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == + HDCP_2_2_AKE_SEND_PAIRING_INFO) { + if (hdcp_info->hdcp2_info.read_pairing) + dp_tx_hdcp2_read_msg(hdcp_info, HDCP_2_2_AKE_SEND_PAIRING_INFO); + + /* Ekh_Km must be available less than 200ms, Give mode time for some Rx */ + time = mtk_dp_get_time_diff(pre_time); + if (time > HDCP_2_2_PAIRING_TIMEOUT_MS * 2) { + err_code = HDCP_ERR_RESPONSE_TIMEROUT; + 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.pairing_info.e_kh_km); + + hdcp_info->hdcp2_info.hdcp_handler.send_pair = true; + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false; + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A2F2, HDCP_2_2_LC_INIT); + pre_time = mtk_dp_get_system_time(); + } + break; + + case HDCP2_MS_A2F2: + /* HDCP2_MS_A2F2 */ + /* HDCP_2_2_LC_INIT */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_LC_INIT) { + /* prepare Rn to send */ + tmp = dp_tx_hdcp2_write_msg(hdcp_info, HDCP_2_2_LC_INIT); + if (!tmp) { + err_code = HDCP_ERR_SEND_MSG_FAIL; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + hdcp_info->hdcp2_info.hdcp_handler.send_lc_init = true; + + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A2F2, HDCP_2_2_LC_SEND_LPRIME); + pre_time = mtk_dp_get_system_time(); + } + + /* HDCP2_MS_A2F2 */ + /* HDCP_2_2_LC_SEND_LPRIME */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_LC_SEND_LPRIME) { + time = mtk_dp_get_time_diff(pre_time); + if (time < HDCP_2_2_DP_HPRIME_READ_TIMEOUT_MS) + break; + + if (hdcp_info->hdcp2_info.read_l_prime) + dp_tx_hdcp2_read_msg(hdcp_info, HDCP_2_2_LC_SEND_LPRIME); + + if (!hdcp_info->hdcp2_info.hdcp_handler.recv_msg) + break; + + ret = tee_lc_l_prime(hdcp_info, hdcp_info->hdcp2_info.hdcp_tx.lc_init.r_n, + hdcp_info->hdcp2_info.hdcp_rx.send_lprime.l_prime, + HDCP_2_2_L_PRIME_LEN); + if (ret != RET_COMPARE_PASS) { + err_code = HDCP_ERR_PROCESS_FAIL; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + DPTXHDCPMSG("2.x: L' is PASS!!\n"); + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false; + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A3F3, HDCP_2_2_NULL_MSG); + pre_time = mtk_dp_get_system_time(); + } + break; + + case HDCP2_MS_A3F3: + /* HDCP2_MS_A3F3 */ + /* HDCP_2_2_NULL_MSG */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_NULL_MSG) { + tee_ske_enc_ks(hdcp_info, hdcp_info->hdcp2_info.hdcp_tx.send_eks.riv, + hdcp_info->hdcp2_info.hdcp_tx.send_eks.e_dkey_ks); + + tmp = dp_tx_hdcp2_write_msg(hdcp_info, HDCP_2_2_SKE_SEND_EKS); + if (!tmp) { + err_code = HDCP_ERR_SEND_MSG_FAIL; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + if (!hdcp_info->hdcp2_info.repeater) + dp_tx_hdcp2_write_msg(hdcp_info, HDCP_2_2_STREAM_TYPE); + + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A3F3, HDCP_2_2_SKE_SEND_EKS); + pre_time = mtk_dp_get_system_time(); + } + + /* HDCP2_MS_A3F3 */ + /* HDCP_2_2_SKE_SEND_EKS */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_SKE_SEND_EKS) { + time = mtk_dp_get_time_diff(pre_time); + if (time >= HDCP_2_2_DELAY_BEFORE_ENCRYPTION_EN) + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A4F4, HDCP_2_2_NULL_MSG); + } + break; + + case HDCP2_MS_A4F4: + /* HDCP2_MS_A4F4 */ + /* HDCP_2_2_NULL_MSG */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_NULL_MSG) { + if (!hdcp_info->hdcp2_info.repeater) { + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A5F5, HDCP_2_2_AUTH_DONE); + break; + } + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A6F6, + HDCP_2_2_REP_SEND_RECVID_LIST); + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false; + pre_time = mtk_dp_get_system_time(); + } + break; + + case HDCP2_MS_A5F5: + /* HDCP2_MS_A5F5 */ + /* HDCP_2_2_AUTH_DONE */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_AUTH_DONE) { + DPTXHDCPMSG("2.x: Authentication done!\n"); + hdcp_info->auth_status = AUTH_PASS; + hdcp_info->hdcp2_info.retry_count = 0; + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A5F5, HDCP_2_2_NULL_MSG); + dp_tx_hdcp2_enable_auth(hdcp_info, true); + } + break; + + case HDCP2_MS_A6F6: + /* HDCP2_MS_A6F6 */ + /* HDCP_2_2_REP_SEND_RECVID_LIST */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == + HDCP_2_2_REP_SEND_RECVID_LIST) { + if (hdcp_info->hdcp2_info.read_v_prime) + dp_tx_hdcp2_read_msg(hdcp_info, HDCP_2_2_REP_SEND_RECVID_LIST); + + time = mtk_dp_get_time_diff(pre_time); + if (time > HDCP_2_2_RECVID_LIST_TIMEOUT_MS) { + err_code = HDCP_ERR_RESPONSE_TIMEROUT; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + if (!hdcp_info->hdcp2_info.hdcp_handler.recv_msg) + break; + + pre_time = mtk_dp_get_system_time(); + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false; + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A7F7, + HDCP_2_2_REP_VERIFY_RECVID_LIST); + } + break; + + case HDCP2_MS_A7F7: + /* HDCP2_MS_A7F7 */ + /* HDCP_2_2_REP_VERIFY_RECVID_LIST */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == + HDCP_2_2_REP_VERIFY_RECVID_LIST) { + if ((hdcp_info->hdcp2_info.hdcp_rx.receiverid_list.rx_info[1] + & (BIT(2) | BIT(3))) != 0) { + pr_err("2.x: DEVS_EXCEEDED or CASCADE_EXCEDDED!\n"); + err_code = HDCP_ERR_PROCESS_FAIL; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + /* check seqNumV here */ + tmp = dp_tx_hdcp2_check_seq_num_v(hdcp_info); + if (!tmp) { + err_code = HDCP_ERR_PROCESS_FAIL; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + tmp = dp_tx_hdcp2_recv_rep_auth_send_recv_id_list(hdcp_info); + if (!tmp) { + err_code = HDCP_ERR_PROCESS_FAIL; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A8F8, HDCP_2_2_REP_SEND_ACK); + } + break; + + case HDCP2_MS_A8F8: + /* HDCP2_MS_A8F8 */ + /* HDCP_2_2_REP_SEND_ACK */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_REP_SEND_ACK) { + tmp = dp_tx_hdcp2_write_msg(hdcp_info, HDCP_2_2_REP_SEND_ACK); + if (!tmp) { + err_code = HDCP_ERR_SEND_MSG_FAIL; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + time = mtk_dp_get_time_diff(pre_time); + if (time > HDCP2_REP_SEND_ACK) { + err_code = HDCP_ERR_RESPONSE_TIMEROUT; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A9F9, HDCP_2_2_REP_STREAM_MANAGE); + hdcp_info->hdcp2_info.hdcp_handler.retry_cnt = 0; + } + break; + + case HDCP2_MS_A9F9: + /* HDCP2_MS_A9F9 */ + /* HDCP_2_2_REP_STREAM_MANAGE */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_REP_STREAM_MANAGE) { + tmp = dp_tx_hdcp2_process_rep_auth_stream_manage(hdcp_info); + if (!tmp) { + err_code = HDCP_ERR_PROCESS_FAIL; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + tmp = dp_tx_hdcp2_write_msg(hdcp_info, HDCP_2_2_REP_STREAM_MANAGE); + if (!tmp) { + err_code = HDCP_ERR_SEND_MSG_FAIL; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + pre_time = mtk_dp_get_system_time(); + hdcp_info->hdcp2_info.hdcp_handler.recv_msg = false; + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A9F9, + HDCP_2_2_REP_STREAM_READY); + } + + /* HDCP2_MS_A9F9 */ + /* HDCP_2_2_REP_STREAM_READY */ + if (hdcp_info->hdcp2_info.hdcp_handler.sub_state == HDCP_2_2_REP_STREAM_READY) { + time = mtk_dp_get_time_diff(pre_time); + if (time <= HDCP_2_2_STREAM_READY_TIMEOUT_MS / 2) + break; + + dp_tx_hdcp2_read_msg(hdcp_info, HDCP_2_2_REP_STREAM_READY); + + time = mtk_dp_get_time_diff(pre_time); + if (time > HDCP_2_2_STREAM_READY_TIMEOUT_MS) { + err_code = HDCP_ERR_RESPONSE_TIMEROUT; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + 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; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + hdcp_info->hdcp2_info.hdcp_handler.retry_cnt++; + + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A9F9, + HDCP_2_2_REP_STREAM_READY); + break; + } + + tmp = dp_tx_hdcp2_recv_rep_auth_stream_ready(hdcp_info); + if (!tmp) { + err_code = HDCP_ERR_PROCESS_FAIL; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A5F5, HDCP_2_2_AUTH_DONE); + } + break; + + default: + err_code = HDCP_ERR_UNKNOWN_STATE; + dp_tx_hdcp2_err_handle(hdcp_info, err_code, __LINE__); + break; + } + + return err_code; +} + +void 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; + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_A0F0, HDCP_2_2_NULL_MSG); + } else { + hdcp_info->auth_status = AUTH_ZERO; + dp_tx_hdcp2_set_state(hdcp_info, HDCP2_MS_H1P1, HDCP_2_2_NULL_MSG); + dp_tx_hdcp2_enable_auth(hdcp_info, false); + } + + hdcp_info->hdcp2_info.retry_count = 0; +} + +bool dp_tx_hdcp2_support(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + u8 tmp[3]; + int ret; + + drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RX_CAPS_OFFSET, tmp, HDCP_2_2_RXCAPS_LEN); + + if (HDCP_2_2_DP_HDCP_CAPABLE(tmp[2]) && tmp[0] == HDCP_2_2_RX_CAPS_VERSION_VAL) { + hdcp_info->hdcp2_info.enable = true; + hdcp_info->hdcp2_info.repeater = tmp[2] & BIT(0); + } else { + hdcp_info->hdcp2_info.enable = false; + } + + DPTXHDCPMSG("2.x: 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("2.x: HDCP TA has some error\n"); + hdcp_info->hdcp2_info.enable = false; + } + + return hdcp_info->hdcp2_info.enable; +} + +bool dp_tx_hdcp2_irq(struct mtk_hdcp_info *hdcp_info) +{ + struct mtk_dp *mtk_dp = container_of(hdcp_info, struct mtk_dp, hdcp_info); + u8 rx_status = 0; + + drm_dp_dpcd_read(&mtk_dp->aux, DP_HDCP_2_2_REG_RXSTATUS_OFFSET, &rx_status, + HDCP_2_2_DP_RXSTATUS_LEN); + + if (rx_status & BIT(0)) { + DPTXHDCPMSG("2.x: READY_BIT0 Ready!\n"); + hdcp_info->hdcp2_info.read_v_prime = true; + } + + if (rx_status & BIT(1)) { + DPTXHDCPMSG("2.x: H'_AVAILABLE Ready!\n"); + hdcp_info->hdcp2_info.read_h_prime = true; + } + + if (rx_status & BIT(2)) { + DPTXHDCPMSG("2.x: PAIRING_AVAILABLE Ready!\n"); + hdcp_info->hdcp2_info.read_pairing = true; + } + + if (rx_status & BIT(4) || rx_status & BIT(3)) { + DPTXHDCPMSG("2.x: Re-Auth HDCP2X!\n"); + dp_tx_hdcp2_set_start_auth(hdcp_info, true); + mtk_dp_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..df309d3dcc6e --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dp_hdcp2.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019-2024 MediaTek Inc. + */ + +#ifndef _MTK_dp_HDCP2_H_ +#define _MTK_dp_HDCP2_H_ + +#include "tlc_dp_hdcp.h" + +/* Timeout relative */ +#define HDCP2_REP_SEND_ACK 2000 + +/* Patch for QD980 LLCTS */ +#define HDCP2_TX_RETRY_CNT 3 +#define HDCP2_TX_LC_RETRY_CNT 1023 +#define HDCP2_STREAM_MANAGE_RETRY_CNT 8 + +/* HDCP2.2 Msg IDs */ +#define HDCP_2_2_STREAM_TYPE 20 +#define HDCP_2_2_REP_VERIFY_RECVID_LIST 21 +#define HDCP_2_2_AUTH_FAIL 22 +#define HDCP_2_2_AUTH_DONE 23 + +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_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 dp_tx_hdcp2_fsm(struct mtk_hdcp_info *hdcp_info); +void dp_tx_hdcp2_set_start_auth(struct mtk_hdcp_info *hdcp_info, bool enable); +bool dp_tx_hdcp2_support(struct mtk_hdcp_info *hdcp_info); +bool 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..5cf5059762ed 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp_reg.h +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h @@ -1,8 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2019-2022 MediaTek Inc. + * Copyright (c) 2019-2024 MediaTek Inc. * Copyright (c) 2022 BayLibre */ + #ifndef _MTK_DP_REG_H_ #define _MTK_DP_REG_H_ @@ -277,6 +278,7 @@ #define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT BIT(1) #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) diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c index 4e3d9f7b4d8c..7f4a8f7dfce2 100644 --- a/drivers/gpu/drm/mediatek/mtk_dpi.c +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c @@ -25,6 +25,7 @@ #include <drm/drm_edid.h> #include <drm/drm_of.h> #include <drm/drm_simple_kms_helper.h> +#include <drm/display/drm_hdcp_helper.h> #include "mtk_disp_drv.h" #include "mtk_dpi_regs.h" @@ -820,6 +821,8 @@ static int mtk_dpi_bind(struct device *dev, struct device *master, void *data) } drm_connector_attach_encoder(dpi->connector, &dpi->encoder); + drm_connector_attach_content_protection_property(dpi->connector, true); + return 0; err_cleanup: -- 2.43.0