Merge branch 'drm-dwhdmi-devel' of git://ftp.arm.linux.org.uk/~rmk/linux-arm into...
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / bridge / dw_hdmi.c
index fc9536b2c40768b61d2bc6227a55cf72bb605b23..0083d4e7e7e2792a06a67956858b1d1eaf8f983e 100644 (file)
@@ -123,6 +123,10 @@ struct dw_hdmi {
        struct i2c_adapter *ddc;
        void __iomem *regs;
        bool sink_is_hdmi;
+       bool sink_has_audio;
+
+       struct mutex mutex;             /* for state below and previous_mode */
+       bool disabled;                  /* DRM has disabled our bridge */
 
        spinlock_t audio_lock;
        struct mutex audio_mutex;
@@ -736,9 +740,9 @@ static int hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
        return 0;
 }
 
-static void dw_hdmi_phy_enable_power(struct dw_hdmi *hdmi, u8 enable)
+static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable)
 {
-       hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+       hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0,
                         HDMI_PHY_CONF0_PDZ_OFFSET,
                         HDMI_PHY_CONF0_PDZ_MASK);
 }
@@ -878,7 +882,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
        /* REMOVE CLK TERM */
        hdmi_phy_i2c_write(hdmi, 0x8000, 0x05);  /* CKCALCTRL */
 
-       dw_hdmi_phy_enable_power(hdmi, 1);
+       dw_hdmi_phy_enable_powerdown(hdmi, false);
 
        /* toggle TMDS enable */
        dw_hdmi_phy_enable_tmds(hdmi, 0);
@@ -923,7 +927,7 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi)
                dw_hdmi_phy_sel_data_en_pol(hdmi, 1);
                dw_hdmi_phy_sel_interface_control(hdmi, 0);
                dw_hdmi_phy_enable_tmds(hdmi, 0);
-               dw_hdmi_phy_enable_power(hdmi, 0);
+               dw_hdmi_phy_enable_powerdown(hdmi, true);
 
                /* Enable CSC */
                ret = hdmi_phy_configure(hdmi, 0, 8, cscon);
@@ -1140,7 +1144,7 @@ static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi)
                return;
 
        dw_hdmi_phy_enable_tmds(hdmi, 0);
-       dw_hdmi_phy_enable_power(hdmi, 0);
+       dw_hdmi_phy_enable_powerdown(hdmi, true);
 
        hdmi->phy_enabled = false;
 }
@@ -1257,13 +1261,17 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
        /* HDMI Initialization Step B.3 */
        dw_hdmi_enable_video_path(hdmi);
 
-       /* not for DVI mode */
-       if (hdmi->sink_is_hdmi) {
-               dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
+       if (hdmi->sink_has_audio) {
+               dev_dbg(hdmi->dev, "sink has audio support\n");
 
                /* HDMI Initialization Step E - Configure audio */
                hdmi_clk_regenerator_update_pixel_clock(hdmi);
                hdmi_enable_audio_clk(hdmi);
+       }
+
+       /* not for DVI mode */
+       if (hdmi->sink_is_hdmi) {
+               dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
 
                /* HDMI Initialization Step F - Configure AVI InfoFrame */
                hdmi_config_AVI(hdmi, mode);
@@ -1370,10 +1378,12 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
 {
        struct dw_hdmi *hdmi = bridge->driver_private;
 
-       dw_hdmi_setup(hdmi, mode);
+       mutex_lock(&hdmi->mutex);
 
        /* Store the display mode for plugin/DKMS poweron events */
        memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
+
+       mutex_unlock(&hdmi->mutex);
 }
 
 static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
@@ -1387,14 +1397,20 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
 {
        struct dw_hdmi *hdmi = bridge->driver_private;
 
+       mutex_lock(&hdmi->mutex);
+       hdmi->disabled = true;
        dw_hdmi_poweroff(hdmi);
+       mutex_unlock(&hdmi->mutex);
 }
 
 static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
 {
        struct dw_hdmi *hdmi = bridge->driver_private;
 
+       mutex_lock(&hdmi->mutex);
        dw_hdmi_poweron(hdmi);
+       hdmi->disabled = false;
+       mutex_unlock(&hdmi->mutex);
 }
 
 static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
@@ -1417,7 +1433,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
        struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
                                             connector);
        struct edid *edid;
-       int ret;
+       int ret = 0;
 
        if (!hdmi->ddc)
                return 0;
@@ -1428,6 +1444,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
                        edid->width_cm, edid->height_cm);
 
                hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+               hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
                drm_mode_connector_update_edid_property(connector, edid);
                ret = drm_add_edid_modes(connector, edid);
                kfree(edid);
@@ -1435,7 +1452,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
                dev_dbg(hdmi->dev, "failed to get edid\n");
        }
 
-       return 0;
+       return ret;
 }
 
 static enum drm_mode_status
@@ -1484,7 +1501,7 @@ static struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
        .best_encoder = dw_hdmi_connector_best_encoder,
 };
 
-struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
+static struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
        .enable = dw_hdmi_bridge_enable,
        .disable = dw_hdmi_bridge_disable,
        .pre_enable = dw_hdmi_bridge_nop,
@@ -1516,20 +1533,20 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
        phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
 
        if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
+               hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
+               mutex_lock(&hdmi->mutex);
                if (phy_int_pol & HDMI_PHY_HPD) {
                        dev_dbg(hdmi->dev, "EVENT=plugin\n");
 
-                       hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
-
-                       dw_hdmi_poweron(hdmi);
+                       if (!hdmi->disabled)
+                               dw_hdmi_poweron(hdmi);
                } else {
                        dev_dbg(hdmi->dev, "EVENT=plugout\n");
 
-                       hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
-                                 HDMI_PHY_POL0);
-
-                       dw_hdmi_poweroff(hdmi);
+                       if (!hdmi->disabled)
+                               dw_hdmi_poweroff(hdmi);
                }
+               mutex_unlock(&hdmi->mutex);
                drm_helper_hpd_irq_event(hdmi->bridge->dev);
        }
 
@@ -1597,7 +1614,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
        hdmi->sample_rate = 48000;
        hdmi->ratio = 100;
        hdmi->encoder = encoder;
+       hdmi->disabled = true;
 
+       mutex_init(&hdmi->mutex);
        mutex_init(&hdmi->audio_mutex);
        spin_lock_init(&hdmi->audio_lock);