drm/msm/dp: power off DP phy at suspend
[linux-2.6-microblaze.git] / drivers / gpu / drm / msm / dp / dp_display.c
index 5a39da6..051c1be 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;
@@ -199,10 +208,6 @@ static int dp_display_bind(struct device *dev, struct device *master,
 
        dp = container_of(g_dp_display,
                        struct dp_display_private, dp_display);
-       if (!dp) {
-               DRM_ERROR("DP driver bind failed. Invalid driver data\n");
-               return -EINVAL;
-       }
 
        dp->dp_display.drm_dev = drm;
        priv = drm->dev_private;
@@ -243,10 +248,6 @@ static void dp_display_unbind(struct device *dev, struct device *master,
 
        dp = container_of(g_dp_display,
                        struct dp_display_private, dp_display);
-       if (!dp) {
-               DRM_ERROR("Invalid DP driver data\n");
-               return;
-       }
 
        dp_power_client_deinit(dp->power);
        dp_aux_unregister(dp->aux);
@@ -337,6 +338,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) {
@@ -397,19 +404,9 @@ static int dp_display_usbpd_configure_cb(struct device *dev)
 
        dp = container_of(g_dp_display,
                        struct dp_display_private, dp_display);
-       if (!dp) {
-               DRM_ERROR("no driver data found\n");
-               rc = -ENODEV;
-               goto end;
-       }
 
        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;
@@ -428,11 +425,6 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev)
 
        dp = container_of(g_dp_display,
                        struct dp_display_private, dp_display);
-       if (!dp) {
-               DRM_ERROR("no driver data found\n");
-               rc = -ENODEV;
-               return rc;
-       }
 
        dp_add_event(dp, EV_USER_NOTIFICATION, false, 0);
 
@@ -493,7 +485,6 @@ static int dp_display_usbpd_attention_cb(struct device *dev)
        int rc = 0;
        u32 sink_request;
        struct dp_display_private *dp;
-       struct dp_usbpd *hpd;
 
        if (!dev) {
                DRM_ERROR("invalid dev\n");
@@ -502,12 +493,6 @@ static int dp_display_usbpd_attention_cb(struct device *dev)
 
        dp = container_of(g_dp_display,
                        struct dp_display_private, dp_display);
-       if (!dp) {
-               DRM_ERROR("no driver data found\n");
-               return -ENODEV;
-       }
-
-       hpd = dp->usbpd;
 
        /* check for any test request issued by sink */
        rc = dp_link_process_request(dp->link);
@@ -570,6 +555,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 +575,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 +608,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 +641,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 +656,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 +673,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 +690,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;
        }
@@ -720,7 +722,6 @@ static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data)
 static void dp_display_deinit_sub_modules(struct dp_display_private *dp)
 {
        dp_debug_put(dp->debug);
-       dp_ctrl_put(dp->ctrl);
        dp_panel_put(dp->panel);
        dp_aux_put(dp->aux);
        dp_audio_put(dp->audio);
@@ -814,13 +815,11 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
                rc = PTR_ERR(dp->audio);
                pr_err("failed to initialize audio, rc = %d\n", rc);
                dp->audio = NULL;
-               goto error_audio;
+               goto error_ctrl;
        }
 
        return rc;
 
-error_audio:
-       dp_ctrl_put(dp->ctrl);
 error_ctrl:
        dp_panel_put(dp->panel);
 error_link:
@@ -898,7 +897,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 +905,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;
 
@@ -1009,6 +1011,33 @@ int dp_display_get_test_bpp(struct msm_dp *dp)
                dp_display->link->test_video.test_bit_depth);
 }
 
+void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp)
+{
+       struct dp_display_private *dp_display;
+       struct drm_device *drm;
+
+       dp_display = container_of(dp, struct dp_display_private, dp_display);
+       drm = dp->drm_dev;
+
+       /*
+        * if we are reading registers we need the link clocks to be on
+        * however till DP cable is connected this will not happen as we
+        * do not know the resolution to power up with. Hence check the
+        * power_on status before dumping DP registers to avoid crash due
+        * to unclocked access
+        */
+       mutex_lock(&dp_display->event_mutex);
+
+       if (!dp->power_on) {
+               mutex_unlock(&dp_display->event_mutex);
+               return;
+       }
+
+       dp_catalog_snapshot(dp_display->catalog, disp_state);
+
+       mutex_unlock(&dp_display->event_mutex);
+}
+
 static void dp_display_config_hpd(struct dp_display_private *dp)
 {
 
@@ -1272,7 +1301,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;
@@ -1292,8 +1326,13 @@ static int dp_pm_suspend(struct device *dev)
 
        mutex_lock(&dp->event_mutex);
 
-       if (dp->core_initialized == true)
+       if (dp->core_initialized == true) {
+               /* mainlink enabled */
+               if (dp_power_clk_status(dp->power, DP_CTRL_PM))
+                       dp_ctrl_off_link_stream(dp->ctrl);
+
                dp_display_host_deinit(dp);
+       }
 
        dp->hpd_state = ST_SUSPENDED;