drm/msm/dp: deinitialize mainlink if link training failed
authorKuogee Hsieh <khsieh@codeaurora.org>
Tue, 3 Nov 2020 20:49:00 +0000 (12:49 -0800)
committerRob Clark <robdclark@chromium.org>
Tue, 10 Nov 2020 20:39:31 +0000 (12:39 -0800)
DP compo phy have to be enable to start link training. When
link training failed phy need to be disabled so that next
link traning can be proceed smoothly at next plug in. This
patch de-initialize mainlink to disable phy if link training
failed. This prevent system crash due to
disp_cc_mdss_dp_link_intf_clk stuck at "off" state.  This patch
also perform checking power_on flag at dp_display_enable() and
dp_display_disable() to avoid crashing when unplug cable while
display is off.

Signed-off-by: Kuogee Hsieh <khsieh@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@chromium.org>
drivers/gpu/drm/msm/dp/dp_catalog.c
drivers/gpu/drm/msm/dp/dp_catalog.h
drivers/gpu/drm/msm/dp/dp_ctrl.c
drivers/gpu/drm/msm/dp/dp_display.c
drivers/gpu/drm/msm/dp/dp_panel.c

index 4963bfe6a4726358406ffa2ffb4c4baf54ca2e0e..c2fe0009b0925c200b33600691f9ea007246ea44 100644 (file)
@@ -572,7 +572,7 @@ void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog)
        dp_write_aux(catalog, REG_DP_DP_HPD_CTRL, DP_DP_HPD_CTRL_HPD_EN);
 }
 
-u32 dp_catalog_hpd_get_state_status(struct dp_catalog *dp_catalog)
+u32 dp_catalog_link_is_connected(struct dp_catalog *dp_catalog)
 {
        struct dp_catalog_private *catalog = container_of(dp_catalog,
                                struct dp_catalog_private, dp_catalog);
index 6d257dbebf294ef4d2c795780b177562c70ba44e..176a9020a520cba7026cdce46012114f21526369 100644 (file)
@@ -97,7 +97,7 @@ void dp_catalog_ctrl_enable_irq(struct dp_catalog *dp_catalog, bool enable);
 void dp_catalog_hpd_config_intr(struct dp_catalog *dp_catalog,
                        u32 intr_mask, bool en);
 void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog);
-u32 dp_catalog_hpd_get_state_status(struct dp_catalog *dp_catalog);
+u32 dp_catalog_link_is_connected(struct dp_catalog *dp_catalog);
 u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog);
 void dp_catalog_ctrl_phy_reset(struct dp_catalog *dp_catalog);
 int dp_catalog_ctrl_update_vx_px(struct dp_catalog *dp_catalog, u8 v_level,
index aa40429cad42ca2e345a225154e2bb04fb8d0e46..139af85a8f2d9d7a772823335705f50ad24691a3 100644 (file)
@@ -1471,6 +1471,30 @@ static int dp_ctrl_reinitialize_mainlink(struct dp_ctrl_private *ctrl)
        return ret;
 }
 
+static int dp_ctrl_deinitialize_mainlink(struct dp_ctrl_private *ctrl)
+{
+       struct dp_io *dp_io;
+       struct phy *phy;
+       int ret;
+
+       dp_io = &ctrl->parser->io;
+       phy = dp_io->phy;
+
+       dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);
+
+       dp_catalog_ctrl_reset(ctrl->catalog);
+
+       ret = dp_power_clk_enable(ctrl->power, DP_CTRL_PM, false);
+       if (ret) {
+               DRM_ERROR("Failed to disable link clocks. ret=%d\n", ret);
+       }
+
+       phy_power_off(phy);
+       phy_exit(phy);
+
+       return 0;
+}
+
 static int dp_ctrl_link_maintenance(struct dp_ctrl_private *ctrl)
 {
        int ret = 0;
@@ -1651,8 +1675,7 @@ int dp_ctrl_on_link(struct dp_ctrl *dp_ctrl)
        if (rc)
                return rc;
 
-       while (--link_train_max_retries &&
-               !atomic_read(&ctrl->dp_ctrl.aborted)) {
+       while (--link_train_max_retries) {
                rc = dp_ctrl_reinitialize_mainlink(ctrl);
                if (rc) {
                        DRM_ERROR("Failed to reinitialize mainlink. rc=%d\n",
@@ -1667,6 +1690,10 @@ int dp_ctrl_on_link(struct dp_ctrl *dp_ctrl)
                        break;
                } else if (training_step == DP_TRAINING_1) {
                        /* link train_1 failed */
+                       if (!dp_catalog_link_is_connected(ctrl->catalog)) {
+                               break;
+                       }
+
                        rc = dp_ctrl_link_rate_down_shift(ctrl);
                        if (rc < 0) { /* already in RBR = 1.6G */
                                if (cr.lane_0_1 & DP_LANE0_1_CR_DONE) {
@@ -1686,6 +1713,10 @@ int dp_ctrl_on_link(struct dp_ctrl *dp_ctrl)
                        }
                } else if (training_step == DP_TRAINING_2) {
                        /* link train_2 failed, lower lane rate */
+                       if (!dp_catalog_link_is_connected(ctrl->catalog)) {
+                               break;
+                       }
+
                        rc = dp_ctrl_link_lane_down_shift(ctrl);
                        if (rc < 0) {
                                /* end with failure */
@@ -1706,6 +1737,11 @@ int dp_ctrl_on_link(struct dp_ctrl *dp_ctrl)
         */
        if (rc == 0)  /* link train successfully */
                dp_ctrl_push_idle(dp_ctrl);
+       else  {
+               /* link training failed */
+               dp_ctrl_deinitialize_mainlink(ctrl);
+               rc = -ECONNRESET;
+       }
 
        return rc;
 }
index fe7cfb4237501314848bef62f8dafb66889555a9..eca111a77f5d8bfe2a158355ff159e270ef1e4b2 100644 (file)
@@ -529,6 +529,11 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data)
        if (ret) {      /* link train failed */
                hpd->hpd_high = 0;
                dp->hpd_state = ST_DISCONNECTED;
+
+               if (ret == -ECONNRESET) { /* cable unplugged */
+                       dp->core_initialized = false;
+               }
+
        } else {
                /* start sentinel checking in case of missing uevent */
                dp_add_event(dp, EV_CONNECT_PENDING_TIMEOUT, 0, tout);
@@ -794,6 +799,11 @@ static int dp_display_enable(struct dp_display_private *dp, u32 data)
 
        dp_display = g_dp_display;
 
+       if (dp_display->power_on) {
+               DRM_DEBUG_DP("Link already setup, return\n");
+               return 0;
+       }
+
        rc = dp_ctrl_on_stream(dp->ctrl);
        if (!rc)
                dp_display->power_on = true;
@@ -826,6 +836,9 @@ static int dp_display_disable(struct dp_display_private *dp, u32 data)
 
        dp_display = g_dp_display;
 
+       if (!dp_display->power_on)
+               return 0;
+
        /* wait only if audio was enabled */
        if (dp_display->audio_enabled) {
                if (!wait_for_completion_timeout(&dp->audio_comp,
@@ -1198,7 +1211,7 @@ static int dp_pm_resume(struct device *dev)
 
        dp_catalog_ctrl_hpd_config(dp->catalog);
 
-       status = dp_catalog_hpd_get_state_status(dp->catalog);
+       status = dp_catalog_link_is_connected(dp->catalog);
 
        if (status)
                dp->dp_display.is_connected = true;
index 1b7a20dc2d8e8c127ba150db7cdf529364a34f4a..97dca3e378b7bf841d7d8103b9172c19ce5ec6a9 100644 (file)
@@ -197,7 +197,7 @@ int dp_panel_read_sink_caps(struct dp_panel *dp_panel,
        if (!dp_panel->edid) {
                DRM_ERROR("panel edid read failed\n");
                /* check edid read fail is due to unplug */
-               if (!dp_catalog_hpd_get_state_status(panel->catalog)) {
+               if (!dp_catalog_link_is_connected(panel->catalog)) {
                        rc = -ETIMEDOUT;
                        goto end;
                }