mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
hdmitx: add hdcp function in drm
PD#158474: hdmitx: add hdcp function in drm
1.update drm library files about hdcp
upstream (24557865c8)
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>
This commit is contained in:
@@ -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/*
|
||||
|
||||
@@ -822,6 +822,7 @@
|
||||
|
||||
&drm_amhdmitx {
|
||||
status = "okay";
|
||||
hdcp = "disabled";
|
||||
};
|
||||
|
||||
&drm_lcd {
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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),)
|
||||
|
||||
408
drivers/amlogic/drm/am_meson_hdcp.c
Normal file
408
drivers/amlogic/drm/am_meson_hdcp.c
Normal file
@@ -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);
|
||||
41
drivers/amlogic/drm/am_meson_hdcp.h
Normal file
41
drivers/amlogic/drm/am_meson_hdcp.h
Normal file
@@ -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
|
||||
@@ -19,17 +19,21 @@
|
||||
#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 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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user