From 9cfc62a3294b7078877983babc8a73cb79d2392d Mon Sep 17 00:00:00 2001 From: Yi Zhou Date: Thu, 28 Jun 2018 18:59:26 +0800 Subject: [PATCH] hdmitx: add hdcp function in drm PD#158474: hdmitx: add hdcp function in drm 1.update drm library files about hdcp upstream (24557865c8b1a6d0eaccaac47aabd9b23badf8fd) 2.add hdcp state machine 3.add hdcp 1.4 4.add hdcp 2.2 a) fix hdcp_tx22 b) add uevent for hdcp_tx22 Change-Id: If1254d2d42775ea45459b8e3072395f480bd6438 Signed-off-by: Yi Zhou --- MAINTAINERS | 4 +- .../g12a_s905d2_u200_drm_buildroot.dts | 1 + arch/arm64/boot/dts/amlogic/meson_drm.dtsi | 1 + .../arm64/boot/dts/amlogic/mesong12a_drm.dtsi | 1 + drivers/amlogic/drm/Makefile | 2 +- drivers/amlogic/drm/am_meson_hdcp.c | 408 ++++++++++++++++++ drivers/amlogic/drm/am_meson_hdcp.h | 41 ++ drivers/amlogic/drm/am_meson_hdmi.c | 163 +++++-- drivers/amlogic/drm/am_meson_hdmi.h | 15 +- .../vout/hdmitx/hdmi_tx_20/hdmi_tx_main.c | 5 +- 10 files changed, 608 insertions(+), 33 deletions(-) create mode 100644 drivers/amlogic/drm/am_meson_hdcp.c create mode 100644 drivers/amlogic/drm/am_meson_hdcp.h diff --git a/MAINTAINERS b/MAINTAINERS index 7caae05320d7..6804e66bea64 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13524,8 +13524,8 @@ M: Tao Zeng F: arch/arm/mach-meson/Makefile.boot HDMITX OUTPUT DRIVER -M: Yi Zhou -M: Zongdong Jiao +M: Zongdong Jiao +M: Yi Zhou M: Kaifu Hu S: Maintained F: drivers/amlogic/media/vout/hdmitx/* diff --git a/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_drm_buildroot.dts b/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_drm_buildroot.dts index c2884ea4f88a..495c6afa0f23 100644 --- a/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_drm_buildroot.dts +++ b/arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_drm_buildroot.dts @@ -822,6 +822,7 @@ &drm_amhdmitx { status = "okay"; + hdcp = "disabled"; }; &drm_lcd { diff --git a/arch/arm64/boot/dts/amlogic/meson_drm.dtsi b/arch/arm64/boot/dts/amlogic/meson_drm.dtsi index a08ed993bde9..bda97c94c997 100644 --- a/arch/arm64/boot/dts/amlogic/meson_drm.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson_drm.dtsi @@ -18,6 +18,7 @@ / { drm_amhdmitx: drm-amhdmitx { status = "disabled"; + hdcp = "disabled"; compatible = "amlogic,drm-amhdmitx"; dev_name = "meson-amhdmitx"; interrupts = ; diff --git a/arch/arm64/boot/dts/amlogic/mesong12a_drm.dtsi b/arch/arm64/boot/dts/amlogic/mesong12a_drm.dtsi index 2865461e3dc8..9708a6296f86 100644 --- a/arch/arm64/boot/dts/amlogic/mesong12a_drm.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesong12a_drm.dtsi @@ -39,6 +39,7 @@ drm_amhdmitx: drm-amhdmitx { status = "disabled"; + hdcp = "disabled"; compatible = "amlogic,drm-amhdmitx"; dev_name = "meson-amhdmitx"; interrupts = ; diff --git a/drivers/amlogic/drm/Makefile b/drivers/amlogic/drm/Makefile index 8e2e74f3eab5..2446575c1cf4 100644 --- a/drivers/amlogic/drm/Makefile +++ b/drivers/amlogic/drm/Makefile @@ -15,7 +15,7 @@ ifneq ($(CONFIG_DRM_MESON_VPU),) endif ifneq ($(CONFIG_DRM_MESON_HDMI),) - meson_hdmi-y += am_meson_hdmi.o + meson_hdmi-y += am_meson_hdmi.o am_meson_hdcp.o endif ifneq ($(CONFIG_DRM_MESON_PANEL),) diff --git a/drivers/amlogic/drm/am_meson_hdcp.c b/drivers/amlogic/drm/am_meson_hdcp.c new file mode 100644 index 000000000000..3c45c26e981c --- /dev/null +++ b/drivers/amlogic/drm/am_meson_hdcp.c @@ -0,0 +1,408 @@ +/* + * drivers/amlogic/drm/am_meson_hdcp.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "am_meson_hdmi.h" +#include "am_meson_hdcp.h" + +static int hdcp_topo_st = -1; +static int hdmitx_hdcp_opr(unsigned int val) +{ + struct arm_smccc_res res; + + if (val == 1) { /* HDCP14_ENABLE */ + arm_smccc_smc(0x82000010, 0, 0, 0, 0, 0, 0, 0, &res); + } + if (val == 2) { /* HDCP14_RESULT */ + arm_smccc_smc(0x82000011, 0, 0, 0, 0, 0, 0, 0, &res); + return (unsigned int)((res.a0)&0xffffffff); + } + if (val == 0) { /* HDCP14_INIT */ + arm_smccc_smc(0x82000012, 0, 0, 0, 0, 0, 0, 0, &res); + } + if (val == 3) { /* HDCP14_EN_ENCRYPT */ + arm_smccc_smc(0x82000013, 0, 0, 0, 0, 0, 0, 0, &res); + } + if (val == 4) { /* HDCP14_OFF */ + arm_smccc_smc(0x82000014, 0, 0, 0, 0, 0, 0, 0, &res); + } + if (val == 5) { /* HDCP_MUX_22 */ + arm_smccc_smc(0x82000015, 0, 0, 0, 0, 0, 0, 0, &res); + } + if (val == 6) { /* HDCP_MUX_14 */ + arm_smccc_smc(0x82000016, 0, 0, 0, 0, 0, 0, 0, &res); + } + if (val == 7) { /* HDCP22_RESULT */ + arm_smccc_smc(0x82000017, 0, 0, 0, 0, 0, 0, 0, &res); + return (unsigned int)((res.a0)&0xffffffff); + } + if (val == 0xa) { /* HDCP14_KEY_LSTORE */ + arm_smccc_smc(0x8200001a, 0, 0, 0, 0, 0, 0, 0, &res); + return (unsigned int)((res.a0)&0xffffffff); + } + if (val == 0xb) { /* HDCP22_KEY_LSTORE */ + arm_smccc_smc(0x8200001b, 0, 0, 0, 0, 0, 0, 0, &res); + return (unsigned int)((res.a0)&0xffffffff); + } + if (val == 0xc) { /* HDCP22_KEY_SET_DUK */ + arm_smccc_smc(0x8200001c, 0, 0, 0, 0, 0, 0, 0, &res); + return (unsigned int)((res.a0)&0xffffffff); + } + if (val == 0xd) { /* HDCP22_SET_TOPO */ + arm_smccc_smc(0x82000083, hdcp_topo_st, 0, 0, 0, 0, 0, 0, &res); + } + if (val == 0xe) { /* HDCP22_GET_TOPO */ + arm_smccc_smc(0x82000084, 0, 0, 0, 0, 0, 0, 0, &res); + return (unsigned int)((res.a0)&0xffffffff); + } + return -1; +} + +static void get_hdcp_bstatus(void) +{ + int ret1 = 0; + int ret2 = 0; + + hdmitx_set_reg_bits(HDMITX_DWC_A_KSVMEMCTRL, 1, 0, 1); + hdmitx_poll_reg(HDMITX_DWC_A_KSVMEMCTRL, (1<<1), 2 * HZ); + ret1 = hdmitx_rd_reg(HDMITX_DWC_HDCP_BSTATUS_0); + ret2 = hdmitx_rd_reg(HDMITX_DWC_HDCP_BSTATUS_1); + hdmitx_set_reg_bits(HDMITX_DWC_A_KSVMEMCTRL, 0, 0, 1); + DRM_INFO("BSTATUS0 = 0x%x BSTATUS1 = 0x%x\n", ret1, ret2); +} + +static void hdcp14_events_handle(unsigned long arg) +{ + struct am_hdmi_tx *am_hdmi = (struct am_hdmi_tx *)arg; + unsigned int bcaps_6_rp; + static unsigned int st_flag = -1; + + bcaps_6_rp = !!(hdmitx_rd_reg(HDMITX_DWC_A_HDCPOBS3) & (1 << 6)); + if (st_flag != hdmitx_rd_reg(HDMITX_DWC_A_APIINTSTAT)) { + st_flag = hdmitx_rd_reg(HDMITX_DWC_A_APIINTSTAT); + DRM_INFO("hdcp14: instat: 0x%x\n", st_flag); + } + if (st_flag & (1 << 7)) { + hdmitx_wr_reg(HDMITX_DWC_A_APIINTCLR, 1 << 7); + hdmitx_hdcp_opr(3); + get_hdcp_bstatus(); + } + + if (st_flag & (1 << 1)) { + hdmitx_wr_reg(HDMITX_DWC_A_APIINTCLR, (1 << 1)); + hdmitx_wr_reg(HDMITX_DWC_A_KSVMEMCTRL, 0x1); + hdmitx_poll_reg(HDMITX_DWC_A_KSVMEMCTRL, (1<<1), 2 * HZ); + if (hdmitx_rd_reg(HDMITX_DWC_A_KSVMEMCTRL) & (1 << 1)) + ;//hdcp_ksv_sha1_calc(hdev); todo + else { + DRM_INFO("hdcptx14: KSV List memory access denied\n"); + return; + } + hdmitx_wr_reg(HDMITX_DWC_A_KSVMEMCTRL, 0x4); + } + + if (am_hdmi->hdcp_try_times) + mod_timer(&am_hdmi->hdcp_timer, jiffies + HZ / 100); + else + return; + am_hdmi->hdcp_try_times--; +} + +static void hdcp14_start_timer(struct am_hdmi_tx *am_hdmi) +{ + static int init_flag; + + if (!init_flag) { + init_flag = 1; + init_timer(&am_hdmi->hdcp_timer); + am_hdmi->hdcp_timer.data = (ulong)am_hdmi; + am_hdmi->hdcp_timer.function = hdcp14_events_handle; + am_hdmi->hdcp_timer.expires = jiffies + HZ / 100; + add_timer(&am_hdmi->hdcp_timer); + am_hdmi->hdcp_try_times = 500; + return; + } + am_hdmi->hdcp_try_times = 500; + am_hdmi->hdcp_timer.expires = jiffies + HZ / 100; + mod_timer(&am_hdmi->hdcp_timer, jiffies + HZ / 100); +} + +static int am_hdcp14_enable(struct am_hdmi_tx *am_hdmi) +{ + am_hdmi->hdcp_mode = HDCP_MODE14; + hdmitx_ddc_hw_op(DDC_MUX_DDC); + hdmitx_hdcp_opr(6); + hdmitx_hdcp_opr(1); + hdcp14_start_timer(am_hdmi); + return 0; +} + +static int am_hdcp14_disable(struct am_hdmi_tx *am_hdmi) +{ + hdmitx_hdcp_opr(4); + return 0; +} + +static void set_pkf_duk_nonce(void) +{ + static int nonce_mode = 1; /* 1: use HW nonce 0: use SW nonce */ + + /* Configure duk/pkf */ + hdmitx_hdcp_opr(0xc); + if (nonce_mode == 1) + hdmitx_wr_reg(HDMITX_TOP_SKP_CNTL_STAT, 0xf); + else { + hdmitx_wr_reg(HDMITX_TOP_SKP_CNTL_STAT, 0xe); +/* Configure nonce[127:0]. + * MSB must be written the last to assert nonce_vld signal. + */ + hdmitx_wr_reg(HDMITX_TOP_NONCE_0, 0x32107654); + hdmitx_wr_reg(HDMITX_TOP_NONCE_1, 0xba98fedc); + hdmitx_wr_reg(HDMITX_TOP_NONCE_2, 0xcdef89ab); + hdmitx_wr_reg(HDMITX_TOP_NONCE_3, 0x45670123); + hdmitx_wr_reg(HDMITX_TOP_NONCE_0, 0x76543210); + hdmitx_wr_reg(HDMITX_TOP_NONCE_1, 0xfedcba98); + hdmitx_wr_reg(HDMITX_TOP_NONCE_2, 0x89abcdef); + hdmitx_wr_reg(HDMITX_TOP_NONCE_3, 0x01234567); + } + udelay(10); +} + +static void am_sysfs_hdcp_event(struct drm_device *dev, unsigned int flag) +{ + char *envp1[2] = { "HDCP22=1", NULL }; + char *envp0[2] = { "HDCP22=0", NULL }; + + DRM_INFO("generating hdcp22: %d\n event\n", flag); + if (flag) + kobject_uevent_env(&dev->primary->kdev->kobj, + KOBJ_CHANGE, envp1); + else + kobject_uevent_env(&dev->primary->kdev->kobj, + KOBJ_CHANGE, envp0); +} + +static int am_hdcp22_enable(struct am_hdmi_tx *am_hdmi) +{ + am_hdmi->hdcp_mode = HDCP_MODE22; + hdmitx_ddc_hw_op(DDC_MUX_DDC); + hdmitx_set_reg_bits(HDMITX_DWC_MC_CLKDIS, 1, 6, 1); + udelay(5); + hdmitx_set_reg_bits(HDMITX_DWC_HDCP22REG_CTRL, 3, 1, 2); + hdmitx_set_reg_bits(HDMITX_TOP_SW_RESET, 1, 5, 1); + udelay(10); + hdmitx_set_reg_bits(HDMITX_TOP_SW_RESET, 0, 5, 1); + udelay(10); + hdmitx_wr_reg(HDMITX_DWC_HDCP22REG_MASK, 0); + hdmitx_wr_reg(HDMITX_DWC_HDCP22REG_MUTE, 0); + set_pkf_duk_nonce(); + + /*uevent to open hdcp_tx22*/ + am_sysfs_hdcp_event(am_hdmi->connector.dev, 1); + return 0; +} + +static int am_hdcp22_disable(struct am_hdmi_tx *am_hdmi) +{ + hdmitx_hdcp_opr(6); + /*uevent to close hdcp_tx22*/ + am_sysfs_hdcp_event(am_hdmi->connector.dev, 0); + return 0; +} + +void am_hdcp_disable(struct am_hdmi_tx *am_hdmi) +{ + if (am_hdmi->hdcp_mode == HDCP_MODE22) + am_hdcp22_disable(am_hdmi); + else if (am_hdmi->hdcp_mode == HDCP_MODE14) + am_hdcp14_disable(am_hdmi); +} +EXPORT_SYMBOL(am_hdcp_disable); + +static int is_hdcp_hdmirx_supported(struct am_hdmi_tx *am_hdmi) +{ + unsigned int hdcp_rx_type = 0x1; + int st; + + /*if tx has hdcp22, then check if rx support hdcp22*/ + if (am_hdmi->hdcp_tx_type & 0x2) { + hdmitx_ddc_hw_op(DDC_MUX_DDC); + //mutex_lock(&am_hdmi->hdcp_mutex); + hdmitx_wr_reg(HDMITX_DWC_I2CM_SLAVE, HDCP_SLAVE); + hdmitx_wr_reg(HDMITX_DWC_I2CM_ADDRESS, HDCP2_VERSION); + hdmitx_wr_reg(HDMITX_DWC_I2CM_OPERATION, 1 << 0); + mdelay(2); + if (hdmitx_rd_reg(HDMITX_DWC_IH_I2CM_STAT0) & (1 << 0)) { + st = 0; + DRM_INFO("ddc rd8b error 0x%02x 0x%02x\n", + HDCP_SLAVE, HDCP2_VERSION); + } else + st = 1; + hdmitx_wr_reg(HDMITX_DWC_IH_I2CM_STAT0, 0x7); + if (hdmitx_rd_reg(HDMITX_DWC_I2CM_DATAI) & (1 << 2)) + hdcp_rx_type = 0x3; + //mutex_unlock(&am_hdmi->hdcp_mutex); + } else { + /*if tx has hdcp14 or no key, then rx support hdcp14 acquiescently*/ + hdcp_rx_type = 0x1; + } + am_hdmi->hdcp_rx_type = hdcp_rx_type; + + DRM_INFO("hdmirx support hdcp14: %d\n", hdcp_rx_type & 0x1); + DRM_INFO("hdmirx support hdcp22: %d\n", (hdcp_rx_type & 0x2) >> 1); + return hdcp_rx_type; +} + +int am_hdcp14_auth(struct am_hdmi_tx *am_hdmi) +{ + return hdmitx_hdcp_opr(0x2); +} + +int am_hdcp22_auth(struct am_hdmi_tx *am_hdmi) +{ + return hdmitx_hdcp_opr(0x7); +} + +/*firstly,check the hdmirx key + *if hdmirx has hdcp22 key, start hdcp22. check auth status, + *if failure,then start hdcp14 + *if hdmirx has hdcp14 key, start hdcp 14 + */ +int am_hdcp_work(void *data) +{ + struct am_hdmi_tx *am_hdmi = data; + struct drm_connector_state *state = am_hdmi->connector.state; + int hdcp_fsm = 0; + + is_hdcp_hdmirx_supported(am_hdmi); + if ((am_hdmi->hdcp_tx_type & 0x2) && + (am_hdmi->hdcp_rx_type & 0x2)) + hdcp_fsm = HDCP22_ENABLE; + else + hdcp_fsm = HDCP14_ENABLE; + + while (hdcp_fsm) { + if (am_hdmi->hdcp_stop_flag) + hdcp_fsm = HDCP_QUIT; + + switch (hdcp_fsm) { + case HDCP22_ENABLE: + am_hdcp22_enable(am_hdmi); + DRM_INFO("hdcp22 work after 10s\n"); + /*this time is used to debug*/ + msleep_interruptible(10000); + hdcp_fsm = HDCP22_AUTH; + break; + case HDCP22_AUTH: + if (am_hdcp22_auth(am_hdmi)) + hdcp_fsm = HDCP22_SUCCESS; + else + hdcp_fsm = HDCP22_FAIL; + break; + case HDCP22_SUCCESS: + state->content_protection = + DRM_MODE_CONTENT_PROTECTION_ENABLED; + DRM_DEBUG("hdcp22 is authenticated successfully\n"); + hdcp_fsm = HDCP22_AUTH; + msleep_interruptible(200); + break; + case HDCP22_FAIL: + am_hdcp22_disable(am_hdmi); + state->content_protection = + DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + DRM_INFO("hdcp22 failure and start hdcp14\n"); + hdcp_fsm = HDCP14_ENABLE; + msleep_interruptible(2000); + break; + case HDCP14_ENABLE: + if ((am_hdmi->hdcp_tx_type & 0x1) == 0) { + hdcp_fsm = HDCP_QUIT; + break; + } + am_hdcp14_enable(am_hdmi); + msleep_interruptible(500); + hdcp_fsm = HDCP14_AUTH; + break; + case HDCP14_AUTH: + if (am_hdcp14_auth(am_hdmi)) + hdcp_fsm = HDCP14_SUCCESS; + else + hdcp_fsm = HDCP14_FAIL; + break; + case HDCP14_SUCCESS: + state->content_protection = + DRM_MODE_CONTENT_PROTECTION_ENABLED; + DRM_DEBUG("hdcp14 is authenticated successfully\n"); + hdcp_fsm = HDCP14_AUTH; + msleep_interruptible(200); + break; + case HDCP14_FAIL: + am_hdcp14_disable(am_hdmi); + state->content_protection = + DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + DRM_DEBUG("hdcp14 failure\n"); + hdcp_fsm = HDCP_QUIT; + break; + case HDCP_QUIT: + default: + break; + } + } + return 0; +} +EXPORT_SYMBOL(am_hdcp_work); + +int am_hdcp_init(struct am_hdmi_tx *am_hdmi) +{ + int ret; + + ret = drm_connector_attach_content_protection_property( + &am_hdmi->connector); + if (ret) + return ret; + return 0; +} +EXPORT_SYMBOL(am_hdcp_init); + +/*bit0:hdcp14 bit 1:hdcp22*/ +int is_hdcp_hdmitx_supported(struct am_hdmi_tx *am_hdmi) +{ + unsigned int hdcp_tx_type = 0; + + hdcp_tx_type |= hdmitx_hdcp_opr(0xa); + hdcp_tx_type |= ((hdmitx_hdcp_opr(0xb)) << 1); + am_hdmi->hdcp_tx_type = hdcp_tx_type; + DRM_INFO("hdmitx support hdcp14: %d\n", hdcp_tx_type & 0x1); + DRM_INFO("hdmitx support hdcp22: %d\n", (hdcp_tx_type & 0x2) >> 1); + return hdcp_tx_type; +} +EXPORT_SYMBOL(is_hdcp_hdmitx_supported); diff --git a/drivers/amlogic/drm/am_meson_hdcp.h b/drivers/amlogic/drm/am_meson_hdcp.h new file mode 100644 index 000000000000..f95d13f6db08 --- /dev/null +++ b/drivers/amlogic/drm/am_meson_hdcp.h @@ -0,0 +1,41 @@ +/* + * drivers/amlogic/drm/am_meson_hdcp.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef __AM_MESON_HDCP_H +#define __AM_MESON_HDCP_H + +#define HDCP_SLAVE 0x3a +#define HDCP2_VERSION 0x50 +#define HDCP_MODE14 1 +#define HDCP_MODE22 2 + +#define HDCP_QUIT 0 +#define HDCP14_ENABLE 1 +#define HDCP14_AUTH 2 +#define HDCP14_SUCCESS 3 +#define HDCP14_FAIL 4 +#define HDCP22_ENABLE 5 +#define HDCP22_AUTH 6 +#define HDCP22_SUCCESS 7 +#define HDCP22_FAIL 8 + +int am_hdcp_init(struct am_hdmi_tx *am_hdmi); +int is_hdcp_hdmitx_supported(struct am_hdmi_tx *am_hdmi); +int am_hdcp_work(void *data); +void am_hdcp_disable(struct am_hdmi_tx *am_hdmi); + +#endif diff --git a/drivers/amlogic/drm/am_meson_hdmi.c b/drivers/amlogic/drm/am_meson_hdmi.c index 4badabe6aca1..a2efd9c41972 100644 --- a/drivers/amlogic/drm/am_meson_hdmi.c +++ b/drivers/amlogic/drm/am_meson_hdmi.c @@ -19,17 +19,21 @@ #include #include #include +#include #include #include #include #include #include +#include +#include + #include #include #include - #include "am_meson_hdmi.h" +#include "am_meson_hdcp.h" #define DEVICE_NAME "amhdmitx" struct am_hdmi_tx am_hdmi_info; @@ -174,16 +178,42 @@ static enum drm_connector_status am_hdmi_connector_detect return connector_status_unknown; } -static int -am_hdmi_probe_single_connector_modes(struct drm_connector *connector, - uint32_t maxX, uint32_t maxY) +static int am_hdmi_connector_set_property(struct drm_connector *connector, + struct drm_property *property, uint64_t val) { - return drm_helper_probe_single_connector_modes(connector, 1920, 1080); + struct am_hdmi_tx *am_hdmi = to_am_hdmi(connector); + struct drm_connector_state *state = am_hdmi->connector.state; + + if (property == connector->content_protection_property) { + DRM_INFO("property:%s val: %lld\n", property->name, val); + if (val == DRM_MODE_CONTENT_PROTECTION_ENABLED) { + DRM_DEBUG_KMS("only drivers can set CP Enabled\n"); + return -EINVAL; + } + state->content_protection = val; + } + /*other parperty todo*/ + return 0; +} + +static int am_hdmi_connector_atomic_get_property + (struct drm_connector *connector, + const struct drm_connector_state *state, + struct drm_property *property, uint64_t *val) +{ + if (property == connector->content_protection_property) { + DRM_INFO("get content_protection val: %d\n", + state->content_protection); + *val = state->content_protection; + } else { + DRM_DEBUG_ATOMIC("Unknown property %s\n", property->name); + return -EINVAL; + } + return 0; } static void am_hdmi_connector_destroy(struct drm_connector *connector) { - drm_connector_unregister(connector); drm_connector_cleanup(connector); } @@ -198,7 +228,9 @@ struct drm_connector_helper_funcs am_hdmi_connector_helper_funcs = { static const struct drm_connector_funcs am_hdmi_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .detect = am_hdmi_connector_detect, - .fill_modes = am_hdmi_probe_single_connector_modes, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = am_hdmi_connector_set_property, + .atomic_get_property = am_hdmi_connector_atomic_get_property, .destroy = am_hdmi_connector_destroy, .reset = drm_atomic_helper_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, @@ -209,7 +241,8 @@ void am_hdmi_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - char *attr; + const char attr1[16] = "rgb,8bit"; + const char attr2[16] = "420,8bit"; int vic; struct am_hdmi_tx *am_hdmi = &am_hdmi_info; @@ -223,16 +256,16 @@ void am_hdmi_encoder_mode_set(struct drm_encoder *encoder, sizeof(am_hdmi->previous_mode)); if ((vic == 96) || (vic == 97) || (vic == 101) || (vic == 102) || (vic == 106) || (vic == 107)) - attr = "420,8bit"; + setup_attr(attr2); else - attr = "rgb,8bit"; - setup_attr(attr); + setup_attr(attr1); } -void am_hdmi_encoder_enable( - struct drm_encoder *encoder) +void am_hdmi_encoder_enable(struct drm_encoder *encoder) { enum vmode_e vmode = get_current_vmode(); + struct am_hdmi_tx *am_hdmi = to_am_hdmi(encoder); + struct drm_connector_state *state = am_hdmi->connector.state; if (vmode == VMODE_HDMI) DRM_INFO("am_hdmi_encoder_enable\n"); @@ -242,13 +275,58 @@ void am_hdmi_encoder_enable( vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE_PRE, &vmode); set_vout_vmode(vmode); vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE, &vmode); - return; + mdelay(1000); + if (state->content_protection == + DRM_MODE_CONTENT_PROTECTION_DESIRED) { + if (am_hdmi->hdcp_tx_type) { + am_hdmi->hdcp_stop_flag = 0; + am_hdmi->hdcp_work = kthread_run(am_hdcp_work, + (void *)am_hdmi, "kthread_hdcp_task"); + } else + DRM_INFO("hdmitx doesn't has hdcp key\n"); + } +} + +void am_hdmi_encoder_disable(struct drm_encoder *encoder) +{ + struct am_hdmi_tx *am_hdmi = to_am_hdmi(encoder); + struct drm_connector_state *state = am_hdmi->connector.state; + + /*need to add hdmitx disable function ..todo*/ + if (state->content_protection != + DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { + state->content_protection = + DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + am_hdmi->hdcp_stop_flag = 1; + kthread_stop(am_hdmi->hdcp_work); + am_hdcp_disable(am_hdmi); + } +} + +static int am_hdmi_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct am_hdmi_tx *am_hdmi = to_am_hdmi(encoder); + + DRM_INFO("content_protection:%d\n", conn_state->content_protection); + + if (conn_state->content_protection == + DRM_MODE_CONTENT_PROTECTION_ENABLED) { + kthread_stop(am_hdmi->hdcp_work); + am_hdcp_disable(am_hdmi); + conn_state->content_protection = + DRM_MODE_CONTENT_PROTECTION_DESIRED; + } + return 0; } static const struct drm_encoder_helper_funcs - am_hdmi_encoder_helper_funcs = { + am_hdmi_encoder_helper_funcs = { .mode_set = am_hdmi_encoder_mode_set, .enable = am_hdmi_encoder_enable, + .disable = am_hdmi_encoder_disable, + .atomic_check = am_hdmi_encoder_atomic_check, }; static const struct drm_encoder_funcs am_hdmi_encoder_funcs = { @@ -279,8 +357,6 @@ static int am_hdmi_i2c_write(struct am_hdmi_tx *am_hdmi, stat = wait_for_completion_timeout(&i2c->cmp, HZ / 100); stat = 1; - if (!stat) - return -EAGAIN; /* Check for error condition on the bus */ if (i2c->stat & 1) return -EIO; @@ -313,8 +389,6 @@ static int am_hdmi_i2c_read(struct am_hdmi_tx *am_hdmi, stat = wait_for_completion_timeout(&i2c->cmp, HZ / 100); stat = 1; - if (!stat) - return -EAGAIN; /* Check for error condition on the bus */ if (i2c->stat & 0x1) @@ -408,8 +482,10 @@ static struct i2c_adapter *am_hdmi_i2c_adapter(struct am_hdmi_tx *am_hdmi) int ret; i2c = devm_kzalloc(am_hdmi->priv->dev, sizeof(*i2c), GFP_KERNEL); - if (!i2c) + if (!i2c) { ret = -ENOMEM; + DRM_INFO("error : %d\n", ret); + } mutex_init(&i2c->lock); init_completion(&i2c->cmp); @@ -473,6 +549,33 @@ static irqreturn_t am_hdmi_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static int amhdmitx_get_dt_info(struct am_hdmi_tx *am_hdmi) +{ + struct device_node *hdcp_node; + unsigned char *hdcp_status; + int ret = 0; + + hdcp_node = of_find_node_by_path("/drm-amhdmitx"); + if (hdcp_node) { + ret = of_property_read_string(hdcp_node, "hdcp", + (const char **)&(hdcp_status)); + if (ret) + DRM_INFO("not find hdcp_feature\n"); + else { + if (memcmp(hdcp_status, "okay", 4) == 0) + am_hdmi->hdcp_feature = 1; + else + am_hdmi->hdcp_feature = 0; + DRM_INFO("hdcp_feature: %d\n", + am_hdmi->hdcp_feature); + } + } else { + DRM_INFO("not find drm_amhdmitx\n"); + } + return 0; +} + + static const struct of_device_id am_meson_hdmi_dt_ids[] = { { .compatible = "amlogic,drm-amhdmitx", }, {}, @@ -481,7 +584,7 @@ static const struct of_device_id am_meson_hdmi_dt_ids[] = { MODULE_DEVICE_TABLE(of, am_meson_hdmi_dt_ids); static int am_meson_hdmi_bind(struct device *dev, - struct device *master, void *data) + struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = data; @@ -489,7 +592,6 @@ static int am_meson_hdmi_bind(struct device *dev, struct am_hdmi_tx *am_hdmi; struct drm_connector *connector; struct drm_encoder *encoder; - int ret; int irq; @@ -500,7 +602,7 @@ static int am_meson_hdmi_bind(struct device *dev, memcpy(&am_hdmi_info, am_hdmi, sizeof(*am_hdmi)); am_hdmi = &am_hdmi_info; - DRM_INFO("hdmi connector init\n"); + DRM_INFO("drm hdmitx init and version:%s\n", DRM_HDMITX_VER); am_hdmi->priv = priv; encoder = &am_hdmi->encoder; connector = &am_hdmi->connector; @@ -549,14 +651,21 @@ static int am_meson_hdmi_bind(struct device *dev, dev_err(am_hdmi->priv->dev, "failed to request hdmi irq: %d\n", ret); } - hdmitx_hpd_hw_op(HPD_UNMUX_HPD); - mdelay(20); - hdmitx_hpd_hw_op(HPD_MUX_HPD); + + /*HDCP INIT*/ + amhdmitx_get_dt_info(am_hdmi); + if (am_hdmi->hdcp_feature) { + if (is_hdcp_hdmitx_supported(am_hdmi)) { + ret = am_hdcp_init(am_hdmi); + if (ret) + DRM_DEBUG_KMS("HDCP init failed, skipping.\n"); + } + } return 0; } static void am_meson_hdmi_unbind(struct device *dev, - struct device *master, void *data) + struct device *master, void *data) { am_hdmi_info.connector.funcs->destroy(&am_hdmi_info.connector); am_hdmi_info.encoder.funcs->destroy(&am_hdmi_info.encoder); diff --git a/drivers/amlogic/drm/am_meson_hdmi.h b/drivers/amlogic/drm/am_meson_hdmi.h index 7f2678441ab9..37542dc81f4d 100644 --- a/drivers/amlogic/drm/am_meson_hdmi.h +++ b/drivers/amlogic/drm/am_meson_hdmi.h @@ -18,8 +18,9 @@ #define __AM_MESON_HDMI_H #include "am_meson_drv.h" -#define DDC_SEGMENT_ADDR 0x30 -#define VIC_MAX_NUM 512 +#define DDC_SEGMENT_ADDR 0x30 +#define VIC_MAX_NUM 512 +#define DRM_HDMITX_VER "20180705" struct am_hdmi_data { unsigned int vic; @@ -65,6 +66,16 @@ struct am_hdmi_tx { const char *hpd_pin; const char *ddc_pin; unsigned int hpd_flag;/*0:none 1:up 2:down*/ + struct mutex hdcp_mutex; + unsigned int hdcp_feature; + unsigned int hdcp_tx_type;/*bit0:hdcp14 bit 1:hdcp22*/ + unsigned int hdcp_rx_type;/*bit0:hdcp14 bit 1:hdcp22*/ + struct timer_list hdcp_timer; + unsigned int hdcp_mode; + unsigned int hdcp_state; + unsigned int hdcp_stop_flag;/*turn off hdcp state machine*/ + unsigned int hdcp_try_times; + struct task_struct *hdcp_work; }; #define to_am_hdmi(x) container_of(x, struct am_hdmi_tx, x) diff --git a/drivers/amlogic/media/vout/hdmitx/hdmi_tx_20/hdmi_tx_main.c b/drivers/amlogic/media/vout/hdmitx/hdmi_tx_20/hdmi_tx_main.c index 77d823521e6a..402c08ebfab0 100644 --- a/drivers/amlogic/media/vout/hdmitx/hdmi_tx_20/hdmi_tx_main.c +++ b/drivers/amlogic/media/vout/hdmitx/hdmi_tx_20/hdmi_tx_main.c @@ -574,7 +574,10 @@ ssize_t store_attr(struct device *dev, void setup_attr(const char *buf) { - store_attr(NULL, NULL, buf, 0); + char attr[16] = {0}; + + memcpy(attr, buf, sizeof(attr)); + memcpy(hdmitx_device.fmt_attr, attr, sizeof(hdmitx_device.fmt_attr)); } EXPORT_SYMBOL(setup_attr);