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>
Signed-off-by: Zheng Yang <zhengyang@rock-chips.com>
This commit is contained in:
Huicong Xu
2017-04-07 16:42:21 +08:00
committed by Tao Huang
parent 40052a9330
commit 6aa721d00e
4 changed files with 93 additions and 30 deletions

View File

@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
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

@@ -13,7 +13,6 @@
*/
#include <linux/clk.h>
#include <linux/cryptohash.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/hdmi.h>
@@ -25,6 +24,7 @@
#include <linux/of_device.h>
#include <linux/spinlock.h>
#include <linux/soc/rockchip/rk_vendor_storage.h>
#include <crypto/sha.h>
#include <drm/bridge/dw_hdmi.h>
#include "dw-hdmi.h"
@@ -149,14 +149,14 @@ static void sha_reset(struct sha_t *sha)
for (i = 0; i < sizeof(sha->mlength); i++)
sha->mlength[i] = 0;
sha_init(sha->mdigest);
sha1_init(sha->mdigest);
}
static void sha_processblock(struct sha_t *sha)
{
u32 array[SHA_WORKSPACE_WORDS];
u32 array[SHA1_WORKSPACE_WORDS];
sha_transform(sha->mdigest, sha->mblock, array);
sha1_transform(sha->mdigest, sha->mblock, array);
sha->mindex = 0;
}

View File

@@ -15,6 +15,8 @@
#ifndef DW_HDMI_HDCP_H
#define DW_HDMI_HDCP_H
#include <linux/miscdevice.h>
#define DW_HDCP_DRIVER_NAME "dw-hdmi-hdcp"
#define HDCP_PRIVATE_KEY_SIZE 280
#define HDCP_KEY_SHA_SIZE 20

View File

@@ -39,6 +39,7 @@
#include "dw-hdmi-audio.h"
#include "dw-hdmi-cec.h"
#include "dw-hdmi-hdcp.h"
#include "dw-hdmi.h"
#define DDC_CI_ADDR 0x37
@@ -212,7 +213,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 rgb_limited_range;
};
@@ -245,6 +245,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;
@@ -258,6 +259,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;
@@ -1926,23 +1928,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->hdcp_start(hdmi->hdcp);
}
static void hdmi_config_AVI(struct dw_hdmi *hdmi,
@@ -2224,13 +2239,12 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
vmode->mtmdsclock /= 2;
dev_dbg(hdmi->dev, "final tmdsclock = %d\n", vmode->mtmdsclock);
/* Set up HDMI_FC_INVIDCONF */
inv_val = (hdmi->hdmi_data.hdcp_enable ||
(dw_hdmi_support_scdc(hdmi, display) &&
(vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
hdmi_info->scdc.scrambling.low_rates)) ?
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
/* Set up HDMI_FC_INVIDCONF
* Some display equipments require that the interval
* between Video Data and Data island must be at least 58 pixels,
* and fc_invidconf.HDCP_keepout set (1'b1) can meet the requirement.
*/
inv_val = HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE;
inv_val |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH :
@@ -2544,7 +2558,6 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi,
*/
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 */
@@ -2590,7 +2603,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi,
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);
@@ -2666,6 +2679,8 @@ static void dw_hdmi_poweroff(struct dw_hdmi *hdmi)
hdmi->phy.enabled = false;
}
if (hdmi->hdcp && hdmi->hdcp->hdcp_stop)
hdmi->hdcp->hdcp_stop(hdmi->hdcp);
hdmi->bridge_is_on = false;
}
@@ -3469,7 +3484,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)
@@ -3481,6 +3496,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;
}
@@ -3515,7 +3537,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);
@@ -3562,6 +3584,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;
}
@@ -3989,6 +4018,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;
}
/* -----------------------------------------------------------------------------
* Probe/remove API, used from platforms based on the DRM bridge API.
*/
@@ -4305,6 +4363,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
drm_bridge_add(&hdmi->bridge);
dw_hdmi_register_debugfs(dev, hdmi);
dw_hdmi_register_hdcp(dev, hdmi, val);
return hdmi;
@@ -4327,6 +4386,8 @@ 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);