drm/msm/dp: handle irq_hpd with sink_count = 0 correctly
[linux-2.6-microblaze.git] / drivers / gpu / drm / msm / dp / dp_display.c
index 5a39da6..cdec0a3 100644 (file)
@@ -178,6 +178,15 @@ static int dp_del_event(struct dp_display_private *dp_priv, u32 event)
        return 0;
 }
 
+void dp_display_signal_audio_start(struct msm_dp *dp_display)
+{
+       struct dp_display_private *dp;
+
+       dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+       reinit_completion(&dp->audio_comp);
+}
+
 void dp_display_signal_audio_complete(struct msm_dp *dp_display)
 {
        struct dp_display_private *dp;
@@ -337,6 +346,12 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
        dp->dp_display.max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ;
        dp->dp_display.max_dp_lanes = dp->parser->max_dp_lanes;
 
+       /*
+        * set sink to normal operation mode -- D0
+        * before dpcd read
+        */
+       dp_link_psm_config(dp->link, &dp->panel->link_info, false);
+
        dp_link_reset_phy_params_vx_px(dp->link);
        rc = dp_ctrl_on_link(dp->ctrl);
        if (rc) {
@@ -405,11 +420,6 @@ static int dp_display_usbpd_configure_cb(struct device *dev)
 
        dp_display_host_init(dp, false);
 
-       /*
-        * set sink to normal operation mode -- D0
-        * before dpcd read
-        */
-       dp_link_psm_config(dp->link, &dp->panel->link_info, false);
        rc = dp_display_process_hpd_high(dp);
 end:
        return rc;
@@ -570,6 +580,10 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data)
                dp_add_event(dp, EV_CONNECT_PENDING_TIMEOUT, 0, tout);
        }
 
+       /* enable HDP irq_hpd/replug interrupt */
+       dp_catalog_hpd_config_intr(dp->catalog,
+               DP_DP_IRQ_HPD_INT_MASK | DP_DP_HPD_REPLUG_INT_MASK, true);
+
        mutex_unlock(&dp->event_mutex);
 
        /* uevent will complete connection part */
@@ -586,10 +600,8 @@ static int dp_connect_pending_timeout(struct dp_display_private *dp, u32 data)
        mutex_lock(&dp->event_mutex);
 
        state = dp->hpd_state;
-       if (state == ST_CONNECT_PENDING) {
-               dp_display_enable(dp, 0);
+       if (state == ST_CONNECT_PENDING)
                dp->hpd_state = ST_CONNECTED;
-       }
 
        mutex_unlock(&dp->event_mutex);
 
@@ -621,7 +633,26 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
        mutex_lock(&dp->event_mutex);
 
        state = dp->hpd_state;
-       if (state == ST_DISCONNECT_PENDING || state == ST_DISCONNECTED) {
+
+       /* disable irq_hpd/replug interrupts */
+       dp_catalog_hpd_config_intr(dp->catalog,
+               DP_DP_IRQ_HPD_INT_MASK | DP_DP_HPD_REPLUG_INT_MASK, false);
+
+       /* unplugged, no more irq_hpd handle */
+       dp_del_event(dp, EV_IRQ_HPD_INT);
+
+       if (state == ST_DISCONNECTED) {
+               /* triggered by irq_hdp with sink_count = 0 */
+               if (dp->link->sink_count == 0) {
+                       dp_ctrl_off_phy(dp->ctrl);
+                       hpd->hpd_high = 0;
+                       dp->core_initialized = false;
+               }
+               mutex_unlock(&dp->event_mutex);
+               return 0;
+       }
+
+       if (state == ST_DISCONNECT_PENDING) {
                mutex_unlock(&dp->event_mutex);
                return 0;
        }
@@ -635,9 +666,8 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
 
        dp->hpd_state = ST_DISCONNECT_PENDING;
 
-       /* disable HPD plug interrupt until disconnect is done */
-       dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK
-                               | DP_DP_IRQ_HPD_INT_MASK, false);
+       /* disable HPD plug interrupts */
+       dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK, false);
 
        hpd->hpd_high = 0;
 
@@ -651,11 +681,10 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
        dp_add_event(dp, EV_DISCONNECT_PENDING_TIMEOUT, 0, DP_TIMEOUT_5_SECOND);
 
        /* signal the disconnect event early to ensure proper teardown */
-       reinit_completion(&dp->audio_comp);
        dp_display_handle_plugged_change(g_dp_display, false);
 
-       dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK |
-                                       DP_DP_IRQ_HPD_INT_MASK, true);
+       /* enable HDP plug interrupt to prepare for next plugin */
+       dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK, true);
 
        /* uevent will complete disconnection part */
        mutex_unlock(&dp->event_mutex);
@@ -669,10 +698,8 @@ static int dp_disconnect_pending_timeout(struct dp_display_private *dp, u32 data
        mutex_lock(&dp->event_mutex);
 
        state =  dp->hpd_state;
-       if (state == ST_DISCONNECT_PENDING) {
-               dp_display_disable(dp, 0);
+       if (state == ST_DISCONNECT_PENDING)
                dp->hpd_state = ST_DISCONNECTED;
-       }
 
        mutex_unlock(&dp->event_mutex);
 
@@ -688,7 +715,7 @@ static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data)
 
        /* irq_hpd can happen at either connected or disconnected state */
        state =  dp->hpd_state;
-       if (state == ST_DISPLAY_OFF) {
+       if (state == ST_DISPLAY_OFF || state == ST_SUSPENDED) {
                mutex_unlock(&dp->event_mutex);
                return 0;
        }
@@ -898,7 +925,6 @@ static int dp_display_disable(struct dp_display_private *dp, u32 data)
        /* wait only if audio was enabled */
        if (dp_display->audio_enabled) {
                /* signal the disconnect event */
-               reinit_completion(&dp->audio_comp);
                dp_display_handle_plugged_change(dp_display, false);
                if (!wait_for_completion_timeout(&dp->audio_comp,
                                HZ * 5))
@@ -907,9 +933,13 @@ static int dp_display_disable(struct dp_display_private *dp, u32 data)
 
        dp_display->audio_enabled = false;
 
-       dp_ctrl_off(dp->ctrl);
-
-       dp->core_initialized = false;
+       /* triggered by irq_hpd with sink_count = 0 */
+       if (dp->link->sink_count == 0) {
+               dp_ctrl_off_link_stream(dp->ctrl);
+       } else {
+               dp_ctrl_off(dp->ctrl);
+               dp->core_initialized = false;
+       }
 
        dp_display->power_on = false;
 
@@ -1272,7 +1302,12 @@ static int dp_pm_resume(struct device *dev)
 
        status = dp_catalog_link_is_connected(dp->catalog);
 
-       if (status)
+       /*
+        * can not declared display is connected unless
+        * HDMI cable is plugged in and sink_count of
+        * dongle become 1
+        */
+       if (status && dp->link->sink_count)
                dp->dp_display.is_connected = true;
        else
                dp->dp_display.is_connected = false;