[PATCH] drm/msm/dp: fixes wrong connection state caused by failure of link train

From: Kuogee Hsieh
Date: Fri Oct 02 2020 - 18:09:30 EST


Connection state is set incorrectly happen at either failure of link train
or cable plugged in while suspended. This patch fixes these problems.
This patch also replace ST_SUSPEND_PENDING with ST_DISPLAY_OFF.

Signed-off-by: Kuogee Hsieh <khsieh@xxxxxxxxxxxxxx>
---
drivers/gpu/drm/msm/dp/dp_display.c | 52 ++++++++++++++---------------
drivers/gpu/drm/msm/dp/dp_panel.c | 5 +++
2 files changed, 31 insertions(+), 26 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 431dff9de797..898c6cc1643a 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -45,7 +45,7 @@ enum {
ST_CONNECT_PENDING,
ST_CONNECTED,
ST_DISCONNECT_PENDING,
- ST_SUSPEND_PENDING,
+ ST_DISPLAY_OFF,
ST_SUSPENDED,
};

@@ -340,8 +340,6 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
}

dp_add_event(dp, EV_USER_NOTIFICATION, true, 0);
-
-
end:
return rc;
}
@@ -489,7 +487,7 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data)
mutex_lock(&dp->event_mutex);

state = atomic_read(&dp->hpd_state);
- if (state == ST_SUSPEND_PENDING) {
+ if (state == ST_DISPLAY_OFF || state == ST_SUSPENDED) {
mutex_unlock(&dp->event_mutex);
return 0;
}
@@ -511,14 +509,14 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data)
hpd->hpd_high = 1;

ret = dp_display_usbpd_configure_cb(&dp->pdev->dev);
- if (ret) { /* failed */
+ if (ret) { /* link train failed */
hpd->hpd_high = 0;
atomic_set(&dp->hpd_state, ST_DISCONNECTED);
+ } else {
+ /* start sentinel checking in case of missing uevent */
+ dp_add_event(dp, EV_CONNECT_PENDING_TIMEOUT, 0, tout);
}

- /* start sanity checking */
- dp_add_event(dp, EV_CONNECT_PENDING_TIMEOUT, 0, tout);
-
mutex_unlock(&dp->event_mutex);

/* uevent will complete connection part */
@@ -563,10 +561,6 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
mutex_lock(&dp->event_mutex);

state = atomic_read(&dp->hpd_state);
- if (state == ST_SUSPEND_PENDING) {
- mutex_unlock(&dp->event_mutex);
- return 0;
- }

if (state == ST_DISCONNECT_PENDING || state == ST_DISCONNECTED) {
mutex_unlock(&dp->event_mutex);
@@ -594,7 +588,7 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
*/
dp_display_usbpd_disconnect_cb(&dp->pdev->dev);

- /* start sanity checking */
+ /* start sentinel checking in case of missing uevent */
dp_add_event(dp, EV_DISCONNECT_PENDING_TIMEOUT, 0, DP_TIMEOUT_5_SECOND);

/* signal the disconnect event early to ensure proper teardown */
@@ -634,7 +628,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 = atomic_read(&dp->hpd_state);
- if (state == ST_SUSPEND_PENDING) {
+ if (state == ST_DISPLAY_OFF) {
mutex_unlock(&dp->event_mutex);
return 0;
}
@@ -1067,7 +1061,7 @@ static irqreturn_t dp_display_irq_handler(int irq, void *dev_id)
}

if (hpd_isr_status & DP_DP_IRQ_HPD_INT_MASK) {
- /* delete connect pending event first */
+ /* delete sentinel connect pending checking */
dp_del_event(dp, EV_CONNECT_PENDING_TIMEOUT);
dp_add_event(dp, EV_IRQ_HPD_INT, 0, 0);
}
@@ -1186,19 +1180,19 @@ static int dp_pm_resume(struct device *dev)

dp = container_of(dp_display, struct dp_display_private, dp_display);

+ /* start from dis connection state */
+ atomic_set(&dp->hpd_state, ST_DISCONNECTED);
+
dp_display_host_init(dp);

dp_catalog_ctrl_hpd_config(dp->catalog);

status = dp_catalog_hpd_get_state_status(dp->catalog);

- if (status) {
+ if (status)
dp->dp_display.is_connected = true;
- } else {
+ else
dp->dp_display.is_connected = false;
- /* make sure next resume host_init be called */
- dp->core_initialized = false;
- }

return 0;
}
@@ -1214,6 +1208,9 @@ static int dp_pm_suspend(struct device *dev)
if (dp_display->power_on == true)
dp_display_disable(dp, 0);

+ /* host_init will be called at pm_resume */
+ dp->core_initialized = false;
+
atomic_set(&dp->hpd_state, ST_SUSPENDED);

return 0;
@@ -1343,6 +1340,9 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)

mutex_lock(&dp_display->event_mutex);

+ /* delete sentinel checking */
+ dp_del_event(dp_display, EV_CONNECT_PENDING_TIMEOUT);
+
rc = dp_display_set_mode(dp, &dp_display->dp_mode);
if (rc) {
DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc);
@@ -1368,9 +1368,8 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)
dp_display_unprepare(dp);
}

- dp_del_event(dp_display, EV_CONNECT_PENDING_TIMEOUT);
-
- if (state == ST_SUSPEND_PENDING)
+ /* manual kick off plug event to train link */
+ if (state == ST_DISPLAY_OFF)
dp_add_event(dp_display, EV_IRQ_HPD_INT, 0, 0);

/* completed connection */
@@ -1402,20 +1401,21 @@ int msm_dp_display_disable(struct msm_dp *dp, struct drm_encoder *encoder)

mutex_lock(&dp_display->event_mutex);

+ /* delete sentinel checking */
+ dp_del_event(dp_display, EV_DISCONNECT_PENDING_TIMEOUT);
+
dp_display_disable(dp_display, 0);

rc = dp_display_unprepare(dp);
if (rc)
DRM_ERROR("DP display unprepare failed, rc=%d\n", rc);

- dp_del_event(dp_display, EV_DISCONNECT_PENDING_TIMEOUT);
-
state = atomic_read(&dp_display->hpd_state);
if (state == ST_DISCONNECT_PENDING) {
/* completed disconnection */
atomic_set(&dp_display->hpd_state, ST_DISCONNECTED);
} else {
- atomic_set(&dp_display->hpd_state, ST_SUSPEND_PENDING);
+ atomic_set(&dp_display->hpd_state, ST_DISPLAY_OFF);
}

mutex_unlock(&dp_display->event_mutex);
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index 18cec4fc5e0b..1b7a20dc2d8e 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -196,6 +196,11 @@ int dp_panel_read_sink_caps(struct dp_panel *dp_panel,
&panel->aux->ddc);
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)) {
+ rc = -ETIMEDOUT;
+ goto end;
+ }

/* fail safe edid */
mutex_lock(&connector->dev->mode_config.mutex);

base-commit: 6fa6fd6a3e2cb76f7d54220daa1266a94c13bc4d
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project