drm/bridge: dw-hdmi: add cec driver
[platform/kernel/linux-rpi.git] / drivers / gpu / drm / bridge / synopsys / dw-hdmi.c
index a24ec4a..6aae581 100644 (file)
@@ -35,6 +35,7 @@
 
 #include "dw-hdmi.h"
 #include "dw-hdmi-audio.h"
+#include "dw-hdmi-cec.h"
 
 #include <media/cec-notifier.h>
 
@@ -133,6 +134,7 @@ struct dw_hdmi {
        unsigned int version;
 
        struct platform_device *audio;
+       struct platform_device *cec;
        struct device *dev;
        struct clk *isfr_clk;
        struct clk *iahb_clk;
@@ -1794,7 +1796,6 @@ static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi)
        hdmi_writeb(hdmi, 0xff, HDMI_AUD_HBR_MASK);
        hdmi_writeb(hdmi, 0xff, HDMI_GP_MASK);
        hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
-       hdmi_writeb(hdmi, 0xff, HDMI_CEC_MASK);
        hdmi_writeb(hdmi, 0xff, HDMI_I2CM_INT);
        hdmi_writeb(hdmi, 0xff, HDMI_I2CM_CTLINT);
 
@@ -2236,6 +2237,29 @@ static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi)
        return -ENODEV;
 }
 
+static void dw_hdmi_cec_enable(struct dw_hdmi *hdmi)
+{
+       mutex_lock(&hdmi->mutex);
+       hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE;
+       hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+       mutex_unlock(&hdmi->mutex);
+}
+
+static void dw_hdmi_cec_disable(struct dw_hdmi *hdmi)
+{
+       mutex_lock(&hdmi->mutex);
+       hdmi->mc_clkdis |= HDMI_MC_CLKDIS_CECCLK_DISABLE;
+       hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+       mutex_unlock(&hdmi->mutex);
+}
+
+static const struct dw_hdmi_cec_ops dw_hdmi_cec_ops = {
+       .write = hdmi_writeb,
+       .read = hdmi_readb,
+       .enable = dw_hdmi_cec_enable,
+       .disable = dw_hdmi_cec_disable,
+};
+
 static const struct regmap_config hdmi_regmap_8bit_config = {
        .reg_bits       = 32,
        .val_bits       = 8,
@@ -2258,6 +2282,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
        struct device_node *np = dev->of_node;
        struct platform_device_info pdevinfo;
        struct device_node *ddc_node;
+       struct dw_hdmi_cec_data cec;
        struct dw_hdmi *hdmi;
        struct resource *iores = NULL;
        int irq;
@@ -2462,6 +2487,19 @@ __dw_hdmi_probe(struct platform_device *pdev,
                hdmi->audio = platform_device_register_full(&pdevinfo);
        }
 
+       if (config0 & HDMI_CONFIG0_CEC) {
+               cec.hdmi = hdmi;
+               cec.ops = &dw_hdmi_cec_ops;
+               cec.irq = irq;
+
+               pdevinfo.name = "dw-hdmi-cec";
+               pdevinfo.data = &cec;
+               pdevinfo.size_data = sizeof(cec);
+               pdevinfo.dma_mask = 0;
+
+               hdmi->cec = platform_device_register_full(&pdevinfo);
+       }
+
        /* Reset HDMI DDC I2C master controller and mute I2CM interrupts */
        if (hdmi->i2c)
                dw_hdmi_i2c_init(hdmi);
@@ -2492,6 +2530,8 @@ static void __dw_hdmi_remove(struct dw_hdmi *hdmi)
 {
        if (hdmi->audio && !IS_ERR(hdmi->audio))
                platform_device_unregister(hdmi->audio);
+       if (!IS_ERR(hdmi->cec))
+               platform_device_unregister(hdmi->cec);
 
        /* Disable all interrupts */
        hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);