drm: bridge: dw-hdmi: enable 3d mode
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / bridge / dw-hdmi.c
index feb4dcb45d2cd3c60c9d56bed6a6029ad4d41ca3..feef9da69c61577060a721096dcb91f5bd84b9c4 100644 (file)
@@ -39,6 +39,7 @@
 #include "dw-hdmi-audio.h"
 
 #define HDMI_EDID_LEN          512
+#define DDC_SEGMENT_ADDR       0x30
 
 #define RGB                    0
 #define YCBCR444               1
@@ -174,6 +175,7 @@ struct dw_hdmi_i2c {
 
        u8                      slave_reg;
        bool                    is_regaddr;
+       bool                    is_segment;
 };
 
 struct dw_hdmi {
@@ -221,6 +223,7 @@ struct dw_hdmi {
 #ifdef CONFIG_SWITCH
        struct switch_dev switchdev;
 #endif
+       int irq;
 
        void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
        u8 (*read)(struct dw_hdmi *hdmi, int offset);
@@ -317,8 +320,12 @@ static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi,
                reinit_completion(&i2c->cmp);
 
                hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS);
-               hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ,
-                           HDMI_I2CM_OPERATION);
+               if (i2c->is_segment)
+                       hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ_EXT,
+                                   HDMI_I2CM_OPERATION);
+               else
+                       hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ,
+                                   HDMI_I2CM_OPERATION);
 
                stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
                if (!stat)
@@ -330,6 +337,7 @@ static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi,
 
                *buf++ = hdmi_readb(hdmi, HDMI_I2CM_DATAI);
        }
+       i2c->is_segment = false;
 
        return 0;
 }
@@ -379,12 +387,6 @@ static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap,
        dev_dbg(hdmi->dev, "xfer: num: %d, addr: %#x\n", num, addr);
 
        for (i = 0; i < num; i++) {
-               if (msgs[i].addr != addr) {
-                       dev_warn(hdmi->dev,
-                                "unsupported transfer, changed slave address\n");
-                       return -EOPNOTSUPP;
-               }
-
                if (msgs[i].len == 0) {
                        dev_dbg(hdmi->dev,
                                "unsupported transfer %d/%d, no data\n",
@@ -403,15 +405,24 @@ static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap,
        /* Set slave device register address on transfer */
        i2c->is_regaddr = false;
 
+       /* Set segment pointer for I2C extended read mode operation */
+       i2c->is_segment = false;
+
        for (i = 0; i < num; i++) {
                dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
                        i + 1, num, msgs[i].len, msgs[i].flags);
-
-               if (msgs[i].flags & I2C_M_RD)
-                       ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf, msgs[i].len);
-               else
-                       ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf, msgs[i].len);
-
+               if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) {
+                       i2c->is_segment = true;
+                       hdmi_writeb(hdmi, DDC_SEGMENT_ADDR, HDMI_I2CM_SEGADDR);
+                       hdmi_writeb(hdmi, *msgs[i].buf, HDMI_I2CM_SEGPTR);
+               } else {
+                       if (msgs[i].flags & I2C_M_RD)
+                               ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf,
+                                                      msgs[i].len);
+                       else
+                               ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf,
+                                                       msgs[i].len);
+               }
                if (ret < 0)
                        break;
        }
@@ -2112,6 +2123,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
                return -ENOMEM;
 
        hdmi->connector.interlace_allowed = 1;
+       hdmi->connector.stereo_allowed = 1;
 
        hdmi->plat_data = plat_data;
        hdmi->dev = dev;
@@ -2121,6 +2133,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
        hdmi->disabled = true;
        hdmi->rxsense = true;
        hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
+       hdmi->irq = irq;
 
        mutex_init(&hdmi->mutex);
        mutex_init(&hdmi->audio_mutex);
@@ -2320,6 +2333,49 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
 
+static void dw_hdmi_reg_initial(struct dw_hdmi *hdmi)
+{
+       if (hdmi_readb(hdmi, HDMI_IH_MUTE)) {
+               initialize_hdmi_ih_mutes(hdmi);
+               hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
+                           HDMI_PHY_I2CM_INT_ADDR);
+
+               hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
+                           HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
+                           HDMI_PHY_I2CM_CTLINT_ADDR);
+
+               hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE,
+                           HDMI_PHY_POL0);
+               hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
+               hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD |
+                           HDMI_IH_PHY_STAT0_RX_SENSE),
+                           HDMI_IH_MUTE_PHY_STAT0);
+       }
+}
+
+void dw_hdmi_suspend(struct device *dev)
+{
+       struct dw_hdmi *hdmi = dev_get_drvdata(dev);
+
+       mutex_lock(&hdmi->mutex);
+       if (hdmi->irq)
+               disable_irq(hdmi->irq);
+       mutex_unlock(&hdmi->mutex);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_suspend);
+
+void dw_hdmi_resume(struct device *dev)
+{
+       struct dw_hdmi *hdmi = dev_get_drvdata(dev);
+
+       mutex_lock(&hdmi->mutex);
+       dw_hdmi_reg_initial(hdmi);
+       if (hdmi->irq)
+               enable_irq(hdmi->irq);
+       mutex_unlock(&hdmi->mutex);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_resume);
+
 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
 MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
 MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");