hdmitx: add hdcp function in drm
authorYi Zhou <yi.zhou@amlogic.com>
Thu, 28 Jun 2018 10:59:26 +0000 (18:59 +0800)
committerYixun Lan <yixun.lan@amlogic.com>
Tue, 10 Jul 2018 07:54:19 +0000 (00:54 -0700)
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 <yi.zhou@amlogic.com>
MAINTAINERS
arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_drm_buildroot.dts
arch/arm64/boot/dts/amlogic/meson_drm.dtsi
arch/arm64/boot/dts/amlogic/mesong12a_drm.dtsi
drivers/amlogic/drm/Makefile
drivers/amlogic/drm/am_meson_hdcp.c [new file with mode: 0644]
drivers/amlogic/drm/am_meson_hdcp.h [new file with mode: 0644]
drivers/amlogic/drm/am_meson_hdmi.c
drivers/amlogic/drm/am_meson_hdmi.h
drivers/amlogic/media/vout/hdmitx/hdmi_tx_20/hdmi_tx_main.c

index 7caae05..6804e66 100644 (file)
@@ -13524,8 +13524,8 @@ M: Tao Zeng <tao.zeng@amlogic.com>
 F: arch/arm/mach-meson/Makefile.boot
 
 HDMITX OUTPUT DRIVER
-M:      Yi Zhou <yi.zhou@amlogic.com>
-M:     Zongdong Jiao <zongdong.jiao@amlogic.com>
+M:      Zongdong Jiao <zongdong.jiao@amlogic.com>
+M:     Yi Zhou <yi.zhou@amlogic.com>
 M:     Kaifu Hu <kaifu.hu@amlogic.com>
 S:     Maintained
 F:     drivers/amlogic/media/vout/hdmitx/*
index a08ed99..bda97c9 100644 (file)
@@ -18,6 +18,7 @@
 / {
        drm_amhdmitx: drm-amhdmitx {
                status = "disabled";
+               hdcp = "disabled";
                compatible = "amlogic,drm-amhdmitx";
                dev_name = "meson-amhdmitx";
                interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>;
index 2865461..9708a62 100644 (file)
@@ -39,6 +39,7 @@
 
        drm_amhdmitx: drm-amhdmitx {
                status = "disabled";
+               hdcp = "disabled";
                compatible = "amlogic,drm-amhdmitx";
                dev_name = "meson-amhdmitx";
                interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>;
index 8e2e74f..2446575 100644 (file)
@@ -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 (file)
index 0000000..3c45c26
--- /dev/null
@@ -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 <drm/drm_modeset_helper.h>
+#include <drm/drmP.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_helper.h>
+
+#include <linux/component.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/amlogic/media/vout/vout_notify.h>
+#include <linux/amlogic/media/vout/hdmi_tx/hdmi_tx_module.h>
+#include <linux/arm-smccc.h>
+
+#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 (file)
index 0000000..f95d13f
--- /dev/null
@@ -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
index 4badabe..a2efd9c 100644 (file)
 #include <drm/drm_edid.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_connector.h>
 
 #include <linux/component.h>
 #include <linux/irq.h>
 #include <linux/io.h>
 #include <linux/delay.h>
 #include <linux/jiffies.h>
+#include <linux/kthread.h>
+#include <linux/device.h>
+
 #include <linux/workqueue.h>
 #include <linux/amlogic/media/vout/vout_notify.h>
 #include <linux/amlogic/media/vout/hdmi_tx/hdmi_tx_module.h>
-
 #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 void am_hdmi_connector_destroy(struct drm_connector *connector)
+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);
index 7f26784..37542dc 100644 (file)
@@ -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)
index 77d8235..402c08e 100644 (file)
@@ -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);