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:
Yi Zhou
2018-06-28 18:59:26 +08:00
committed by Yixun Lan
parent 20c6fe0f8f
commit 9cfc62a329
10 changed files with 608 additions and 33 deletions

View 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/*

View File

@@ -822,6 +822,7 @@
&drm_amhdmitx {
status = "okay";
hdcp = "disabled";
};
&drm_lcd {

View 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>;

View 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>;

View 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),)

View 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);

View 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

View File

@@ -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);

View 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)

View 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);