]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
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)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 29 Jun 2022 06:59:48 +0000 (08:59 +0200)
[ Upstream commit 231a04fcc6cb5b0e5f72c015d36462a17355f925 ]

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>
Signed-off-by: Sasha Levin <sashal@kernel.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 aeca8b2ac5c6b07342306fd9e2ad3ca355593fd3..2da6982efdbfc1a44b0fc74a522334da8bef8b66 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 c83a1650437da567a80249116f86c07157cca134..b9ca844ce2ad02ecd82312858974f2371d3b7ebd 100644 (file)
@@ -1460,6 +1460,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;
@@ -1640,8 +1664,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",
@@ -1656,6 +1679,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) {
@@ -1675,6 +1702,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 */
@@ -1695,6 +1726,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 4b18ab71ae59af0a2098d9082e2dd0ef38fafa6a..d504cf68283a83f9c55c34c5cdc87d33fda59353 100644 (file)
@@ -556,6 +556,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);
@@ -827,6 +832,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;
@@ -859,6 +869,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) {
                /* signal the disconnect event */
@@ -1245,7 +1258,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 550871ba6e5a37970b6f66e39e943f3a3e914398..4e8a19114e87d705e6cbf83b7b0a3131fc293bbb 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;
                }