drm: bridge: dw-hdmi: add hdcp1.4 support

First, write hdcp key by "ProvisioningTool" if you want to
enable hdcp function, or else will auth fail.

To check whether the hdcp is enable or not
  #cat /sys/class/misc/hdmi_hdcp1x/enable
  0:hdcp is disabled
  1:hdcp is enabled, hdmi screen will be pink if it is failed;
  2:hdcp is enabled, hdmi screen will be normal if it is failed;

Enable or disable hdcp function
  #echo 0 > /sys/class/misc/hdmi_hdcp1x/enable
  #echo 1 > /sys/class/misc/hdmi_hdcp1x/enable
  #echo 2 > /sys/class/misc/hdmi_hdcp1x/enable

Get the status of hdcp
  #cat /sys/class/misc/hdmi_hdcp1x/status
  The result will be one of the follow list:
    hdcp disable;
    hdcp_auth_start
    hdcp_auth_success;
    hdcp_auth_fail;
    unknown status.

Change-Id: Iac6c7d6a1196ce9cf2869d7916bbe6c8941ec13b
Signed-off-by: Huicong Xu <xhc@rock-chips.com>
This commit is contained in:
Huicong Xu
2017-04-07 16:42:21 +08:00
committed by Tao Huang
parent cdf220b192
commit 5184a0eb6e
2 changed files with 84 additions and 23 deletions

View File

@@ -1,6 +1,6 @@
#ccflags-y := -Iinclude/drm
obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o dw-hdmi-hdcp.o
obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o
obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o

View File

@@ -41,6 +41,7 @@
#include "dw-hdmi.h"
#include "dw-hdmi-audio.h"
#include "dw-hdmi-cec.h"
#include "dw-hdmi-hdcp.h"
#include <media/cec-notifier.h>
@@ -176,7 +177,6 @@ struct hdmi_data_info {
unsigned int enc_out_encoding;
unsigned int quant_range;
unsigned int pix_repet_factor;
unsigned int hdcp_enable;
struct hdmi_vmode video_mode;
bool update;
};
@@ -209,6 +209,7 @@ struct dw_hdmi_phy_data {
struct dw_hdmi {
struct drm_connector connector;
struct drm_bridge bridge;
struct platform_device *hdcp_dev;
unsigned int version;
@@ -222,6 +223,7 @@ struct dw_hdmi {
struct hdmi_data_info hdmi_data;
const struct dw_hdmi_plat_data *plat_data;
struct dw_hdcp *hdcp;
int vic;
int irq;
@@ -1704,23 +1706,36 @@ static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = {
* HDMI TX Setup
*/
static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi)
static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi,
const struct drm_display_mode *mode)
{
u8 de;
struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
u8 vsync_pol, hsync_pol, data_pol, hdmi_dvi;
if (hdmi->hdmi_data.video_mode.mdataenablepolarity)
de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH;
else
de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW;
/* Configure the video polarity */
vsync_pol = mode->flags & DRM_MODE_FLAG_PVSYNC ?
HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_HIGH :
HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_LOW;
hsync_pol = mode->flags & DRM_MODE_FLAG_PHSYNC ?
HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH :
HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW;
data_pol = vmode->mdataenablepolarity ?
HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH :
HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW;
hdmi_modb(hdmi, vsync_pol | hsync_pol | data_pol,
HDMI_A_VIDPOLCFG_VSYNCPOL_MASK |
HDMI_A_VIDPOLCFG_HSYNCPOL_MASK |
HDMI_A_VIDPOLCFG_DATAENPOL_MASK,
HDMI_A_VIDPOLCFG);
/* disable rx detect */
hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE,
HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0);
/* Config the display mode */
hdmi_dvi = hdmi->sink_is_hdmi ? HDMI_A_HDCPCFG0_HDMIDVI_HDMI :
HDMI_A_HDCPCFG0_HDMIDVI_DVI;
hdmi_modb(hdmi, hdmi_dvi, HDMI_A_HDCPCFG0_HDMIDVI_MASK,
HDMI_A_HDCPCFG0);
hdmi_modb(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG);
hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE,
HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1);
if (hdmi->hdcp)
hdmi->hdcp->hdcp_start(hdmi->hdcp);
}
static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
@@ -2054,12 +2069,11 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
vmode->mtmdsclock /= 2;
/* Set up HDMI_FC_INVIDCONF */
inv_val = (hdmi->hdmi_data.hdcp_enable ||
(dw_hdmi_support_scdc(hdmi) &&
inv_val = ((dw_hdmi_support_scdc(hdmi) &&
(vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
hdmi_info->scdc.scrambling.low_rates)) ?
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
inv_val |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH :
@@ -2367,7 +2381,6 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
*/
hdmi->hdmi_data.pix_repet_factor =
(mode->flags & DRM_MODE_FLAG_DBLCLK) ? 1 : 0;
hdmi->hdmi_data.hdcp_enable = 0;
hdmi->hdmi_data.video_mode.mdataenablepolarity = true;
/* HDMI Initialization Step B.1 */
@@ -2413,7 +2426,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
hdmi_video_packetize(hdmi);
hdmi_video_csc(hdmi);
hdmi_video_sample(hdmi);
hdmi_tx_hdcp_config(hdmi);
hdmi_tx_hdcp_config(hdmi, mode);
dw_hdmi_clear_overflow(hdmi);
@@ -2497,6 +2510,8 @@ static void dw_hdmi_poweroff(struct dw_hdmi *hdmi)
hdmi->phy.enabled = false;
}
if (hdmi->hdcp)
hdmi->hdcp->hdcp_stop(hdmi->hdcp);
hdmi->bridge_is_on = false;
}
@@ -2976,7 +2991,7 @@ static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi)
static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
{
struct dw_hdmi *hdmi = dev_id;
u8 intr_stat;
u8 intr_stat, hdcp_stat;
irqreturn_t ret = IRQ_NONE;
if (hdmi->i2c)
@@ -2988,6 +3003,13 @@ static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
return IRQ_WAKE_THREAD;
}
hdcp_stat = hdmi_readb(hdmi, HDMI_A_APIINTSTAT);
if (hdcp_stat) {
dev_dbg(hdmi->dev, "HDCP irq %#x\n", hdcp_stat);
hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
return IRQ_WAKE_THREAD;
}
return ret;
}
@@ -3022,7 +3044,7 @@ EXPORT_SYMBOL_GPL(dw_hdmi_setup_rx_sense);
static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
{
struct dw_hdmi *hdmi = dev_id;
u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat;
u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat, hdcp_stat;
intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
@@ -3067,6 +3089,13 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
HDMI_IH_MUTE_PHY_STAT0);
hdcp_stat = hdmi_readb(hdmi, HDMI_A_APIINTSTAT);
if (hdcp_stat) {
if (hdmi->hdcp)
hdmi->hdcp->hdcp_isr(hdmi->hdcp, hdcp_stat);
hdmi_writeb(hdmi, hdcp_stat, HDMI_A_APIINTCLR);
hdmi_writeb(hdmi, 0x00, HDMI_A_APIINTMSK);
}
return IRQ_HANDLED;
}
@@ -3478,6 +3507,35 @@ static void dw_hdmi_register_debugfs(struct device *dev, struct dw_hdmi *hdmi)
hdmi, &dw_hdmi_phy_fops);
}
static void dw_hdmi_register_hdcp(struct device *dev, struct dw_hdmi *hdmi,
u32 val)
{
struct dw_hdcp hdmi_hdcp = {
.hdmi = hdmi,
.write = hdmi_writeb,
.read = hdmi_readb,
.regs = hdmi->regs,
.reg_io_width = val,
.enable = 0,
};
struct platform_device_info hdcp_device_info = {
.parent = dev,
.id = PLATFORM_DEVID_AUTO,
.res = NULL,
.num_res = 0,
.name = DW_HDCP_DRIVER_NAME,
.data = &hdmi_hdcp,
.size_data = sizeof(hdmi_hdcp),
.dma_mask = DMA_BIT_MASK(32),
};
hdmi->hdcp_dev = platform_device_register_full(&hdcp_device_info);
if (IS_ERR(hdmi->hdcp_dev))
dev_err(dev, "failed to register hdcp!\n");
else
hdmi->hdcp = hdmi->hdcp_dev->dev.platform_data;
}
static struct dw_hdmi *
__dw_hdmi_probe(struct platform_device *pdev,
const struct dw_hdmi_plat_data *plat_data)
@@ -3766,6 +3824,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
dw_hdmi_i2c_init(hdmi);
dw_hdmi_register_debugfs(dev, hdmi);
dw_hdmi_register_hdcp(dev, hdmi, val);
return hdmi;
@@ -3793,6 +3852,8 @@ static void __dw_hdmi_remove(struct dw_hdmi *hdmi)
{
if (hdmi->audio && !IS_ERR(hdmi->audio))
platform_device_unregister(hdmi->audio);
if (hdmi->hdcp_dev && !IS_ERR(hdmi->hdcp_dev))
platform_device_unregister(hdmi->hdcp_dev);
if (!IS_ERR(hdmi->cec))
platform_device_unregister(hdmi->cec);