diff --git a/drivers/gpu/drm/bridge/synopsys/Makefile b/drivers/gpu/drm/bridge/synopsys/Makefile index 5dad97d920be..044f3c500a08 100644 --- a/drivers/gpu/drm/bridge/synopsys/Makefile +++ b/drivers/gpu/drm/bridge/synopsys/Makefile @@ -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 diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index ddcbdb09a36f..32e61d0039e1 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -41,6 +41,7 @@ #include "dw-hdmi.h" #include "dw-hdmi-audio.h" #include "dw-hdmi-cec.h" +#include "dw-hdmi-hdcp.h" #include @@ -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);