mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 02:50:49 +09:00
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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user