drm/msm/dp: power off DP phy at suspend
[linux-2.6-microblaze.git] / drivers / gpu / drm / msm / dp / dp_display.c
index 1784e11..051c1be 100644 (file)
@@ -208,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;
@@ -252,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);
@@ -346,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) {
@@ -406,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;
@@ -437,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);
 
@@ -502,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");
@@ -511,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);
@@ -579,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 */
@@ -628,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;
        }
@@ -642,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;
 
@@ -660,8 +658,8 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
        /* signal the disconnect event early to ensure proper teardown */
        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);
@@ -692,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;
        }
@@ -724,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);
@@ -818,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:
@@ -910,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;
 
@@ -1012,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)
 {
 
@@ -1300,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;