From 6aa721d00ef65944eec1ce7359441d15c05fd8db Mon Sep 17 00:00:00 2001 From: Huicong Xu Date: Fri, 7 Apr 2017 16:42:21 +0800 Subject: [PATCH] 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 Signed-off-by: Zheng Yang --- drivers/gpu/drm/bridge/synopsys/Makefile | 2 +- .../gpu/drm/bridge/synopsys/dw-hdmi-hdcp.c | 8 +- .../gpu/drm/bridge/synopsys/dw-hdmi-hdcp.h | 2 + drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 111 ++++++++++++++---- 4 files changed, 93 insertions(+), 30 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/Makefile b/drivers/gpu/drm/bridge/synopsys/Makefile index 91d746ad5de1..22d7b2628a67 100644 --- a/drivers/gpu/drm/bridge/synopsys/Makefile +++ b/drivers/gpu/drm/bridge/synopsys/Makefile @@ -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 diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-hdcp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-hdcp.c index 314e9a948d8c..24aab5043e61 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-hdcp.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-hdcp.c @@ -13,7 +13,6 @@ */ #include -#include #include #include #include @@ -25,6 +24,7 @@ #include #include #include +#include #include #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; } diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-hdcp.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-hdcp.h index abedb49e3de1..d138f91f3422 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-hdcp.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-hdcp.h @@ -15,6 +15,8 @@ #ifndef DW_HDMI_HDCP_H #define DW_HDMI_HDCP_H +#include + #define DW_HDCP_DRIVER_NAME "dw-hdmi-hdcp" #define HDCP_PRIVATE_KEY_SIZE 280 #define HDCP_KEY_SHA_SIZE 20 diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index de9ee63f2e34..4f830b682b2a 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -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);