[PATCH v3] drm/msm/dp: return correct connection status after suspend
From: Kuogee Hsieh
Date: Sat Oct 10 2020 - 19:01:56 EST
During suspend, dp host controller and hpd block are disabled due to
both ahb and aux clock are disabled. Therefore hpd plug/unplug interrupts
will not be generated. At dp_pm_resume(), reinitialize both dp host
controller and hpd block so that hpd plug/unplug interrupts will be
generated and handled by driver so that hpd connection state is updated
correctly. This patch will fix link training flaky issues.
Changes in v2:
-- use container_of to cast correct dp_display_private pointer
at both dp_pm_suspend() and dp_pm_resume().
Changes in v3:
-- replace hpd_state atomic_t with u32
-- Add more information to commit message.
Signed-off-by: Kuogee Hsieh <khsieh@xxxxxxxxxxxxxx>
---
drivers/gpu/drm/msm/dp/dp_catalog.c | 13 ++++
drivers/gpu/drm/msm/dp/dp_catalog.h | 1 +
drivers/gpu/drm/msm/dp/dp_display.c | 117 +++++++++++++---------------
drivers/gpu/drm/msm/dp/dp_reg.h | 2 +
4 files changed, 72 insertions(+), 61 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index b15b4ce4ba35..4963bfe6a472 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -572,6 +572,19 @@ 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)
+{
+ struct dp_catalog_private *catalog = container_of(dp_catalog,
+ struct dp_catalog_private, dp_catalog);
+ u32 status;
+
+ status = dp_read_aux(catalog, REG_DP_DP_HPD_INT_STATUS);
+ status >>= DP_DP_HPD_STATE_STATUS_BITS_SHIFT;
+ status &= DP_DP_HPD_STATE_STATUS_BITS_MASK;
+
+ return status;
+}
+
u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index 4b7666f1fe6f..6d257dbebf29 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -97,6 +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_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,
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index e175aa3fd3a9..fd16e12ab2f8 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -108,14 +108,12 @@ struct dp_display_private {
/* event related only access by event thread */
struct mutex event_mutex;
wait_queue_head_t event_q;
- atomic_t hpd_state;
+ u32 hpd_state;
u32 event_pndx;
u32 event_gndx;
struct dp_event event_list[DP_EVENT_Q_MAX];
spinlock_t event_lock;
- struct completion resume_comp;
-
struct dp_audio *audio;
};
@@ -490,7 +488,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);
+ state = dp->hpd_state;
if (state == ST_SUSPEND_PENDING) {
mutex_unlock(&dp->event_mutex);
return 0;
@@ -508,17 +506,14 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data)
return 0;
}
- if (state == ST_SUSPENDED)
- tout = DP_TIMEOUT_NONE;
-
- atomic_set(&dp->hpd_state, ST_CONNECT_PENDING);
+ dp->hpd_state = ST_CONNECT_PENDING;
hpd->hpd_high = 1;
ret = dp_display_usbpd_configure_cb(&dp->pdev->dev);
if (ret) { /* failed */
hpd->hpd_high = 0;
- atomic_set(&dp->hpd_state, ST_DISCONNECTED);
+ dp->hpd_state = ST_DISCONNECTED;
}
/* start sanity checking */
@@ -539,10 +534,10 @@ static int dp_connect_pending_timeout(struct dp_display_private *dp, u32 data)
mutex_lock(&dp->event_mutex);
- state = atomic_read(&dp->hpd_state);
+ state = dp->hpd_state;
if (state == ST_CONNECT_PENDING) {
dp_display_enable(dp, 0);
- atomic_set(&dp->hpd_state, ST_CONNECTED);
+ dp->hpd_state = ST_CONNECTED;
}
mutex_unlock(&dp->event_mutex);
@@ -567,7 +562,7 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
mutex_lock(&dp->event_mutex);
- state = atomic_read(&dp->hpd_state);
+ state = dp->hpd_state;
if (state == ST_SUSPEND_PENDING) {
mutex_unlock(&dp->event_mutex);
return 0;
@@ -585,7 +580,7 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
return 0;
}
- atomic_set(&dp->hpd_state, ST_DISCONNECT_PENDING);
+ 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
@@ -620,10 +615,10 @@ static int dp_disconnect_pending_timeout(struct dp_display_private *dp, u32 data
mutex_lock(&dp->event_mutex);
- state = atomic_read(&dp->hpd_state);
+ state = dp->hpd_state;
if (state == ST_DISCONNECT_PENDING) {
dp_display_disable(dp, 0);
- atomic_set(&dp->hpd_state, ST_DISCONNECTED);
+ dp->hpd_state = ST_DISCONNECTED;
}
mutex_unlock(&dp->event_mutex);
@@ -638,7 +633,7 @@ static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data)
mutex_lock(&dp->event_mutex);
/* irq_hpd can happen at either connected or disconnected state */
- state = atomic_read(&dp->hpd_state);
+ state = dp->hpd_state;
if (state == ST_SUSPEND_PENDING) {
mutex_unlock(&dp->event_mutex);
return 0;
@@ -798,8 +793,6 @@ static int dp_display_enable(struct dp_display_private *dp, u32 data)
if (!rc)
dp_display->power_on = true;
- /* complete resume_comp regardless it is armed or not */
- complete(&dp->resume_comp);
return rc;
}
@@ -1151,9 +1144,6 @@ static int dp_display_probe(struct platform_device *pdev)
}
mutex_init(&dp->event_mutex);
-
- init_completion(&dp->resume_comp);
-
g_dp_display = &dp->dp_display;
/* Store DP audio handle inside DP display */
@@ -1189,20 +1179,50 @@ static int dp_display_remove(struct platform_device *pdev)
static int dp_pm_resume(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct msm_dp *dp_display = platform_get_drvdata(pdev);
+ struct dp_display_private *dp;
+ u32 status;
+
+ dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+ mutex_lock(&dp->event_mutex);
+
+ dp_display_host_init(dp);
+
+ dp_catalog_ctrl_hpd_config(dp->catalog);
+
+ status = dp_catalog_hpd_get_state_status(dp->catalog);
+
+ if (status) {
+ dp->dp_display.is_connected = true;
+ } else {
+ dp->dp_display.is_connected = false;
+ /* make sure next resume host_init be called */
+ dp->core_initialized = false;
+ }
+
+ mutex_unlock(&dp->event_mutex);
+
return 0;
}
static int dp_pm_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
- struct dp_display_private *dp = platform_get_drvdata(pdev);
+ struct msm_dp *dp_display = platform_get_drvdata(pdev);
+ struct dp_display_private *dp;
- if (!dp) {
- DRM_ERROR("DP driver bind failed. Invalid driver data\n");
- return -EINVAL;
- }
+ dp = container_of(dp_display, struct dp_display_private, dp_display);
- atomic_set(&dp->hpd_state, ST_SUSPENDED);
+ mutex_lock(&dp->event_mutex);
+
+ if (dp_display->power_on == true)
+ dp_display_disable(dp, 0);
+
+ dp->hpd_state = ST_SUSPENDED;
+
+ mutex_unlock(&dp->event_mutex);
return 0;
}
@@ -1317,19 +1337,6 @@ int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
return 0;
}
-static int dp_display_wait4resume_done(struct dp_display_private *dp)
-{
- int ret = 0;
-
- reinit_completion(&dp->resume_comp);
- if (!wait_for_completion_timeout(&dp->resume_comp,
- WAIT_FOR_RESUME_TIMEOUT_JIFFIES)) {
- DRM_ERROR("wait4resume_done timedout\n");
- ret = -ETIMEDOUT;
- }
- return ret;
-}
-
int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)
{
int rc = 0;
@@ -1344,6 +1351,8 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)
mutex_lock(&dp_display->event_mutex);
+ 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);
@@ -1358,15 +1367,7 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)
return rc;
}
- state = atomic_read(&dp_display->hpd_state);
- if (state == ST_SUSPENDED) {
- /* start link training */
- dp_add_event(dp_display, EV_HPD_PLUG_INT, 0, 0);
- mutex_unlock(&dp_display->event_mutex);
-
- /* wait until dp interface is up */
- goto resume_done;
- }
+ state = dp_display->hpd_state;
dp_display_enable(dp_display, 0);
@@ -1377,21 +1378,15 @@ 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)
dp_add_event(dp_display, EV_IRQ_HPD_INT, 0, 0);
/* completed connection */
- atomic_set(&dp_display->hpd_state, ST_CONNECTED);
+ dp_display->hpd_state = ST_CONNECTED;
mutex_unlock(&dp_display->event_mutex);
return rc;
-
-resume_done:
- dp_display_wait4resume_done(dp_display);
- return rc;
}
int msm_dp_display_pre_disable(struct msm_dp *dp, struct drm_encoder *encoder)
@@ -1415,20 +1410,20 @@ int msm_dp_display_disable(struct msm_dp *dp, struct drm_encoder *encoder)
mutex_lock(&dp_display->event_mutex);
+ 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);
+ state = dp_display->hpd_state;
if (state == ST_DISCONNECT_PENDING) {
/* completed disconnection */
- atomic_set(&dp_display->hpd_state, ST_DISCONNECTED);
+ dp_display->hpd_state = ST_DISCONNECTED;
} else {
- atomic_set(&dp_display->hpd_state, ST_SUSPEND_PENDING);
+ dp_display->hpd_state = ST_SUSPEND_PENDING;
}
mutex_unlock(&dp_display->event_mutex);
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index 43042ff90a19..268602803d9a 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -32,6 +32,8 @@
#define DP_DP_IRQ_HPD_INT_ACK (0x00000002)
#define DP_DP_HPD_REPLUG_INT_ACK (0x00000004)
#define DP_DP_HPD_UNPLUG_INT_ACK (0x00000008)
+#define DP_DP_HPD_STATE_STATUS_BITS_MASK (0x0000000F)
+#define DP_DP_HPD_STATE_STATUS_BITS_SHIFT (0x1C)
#define REG_DP_DP_HPD_INT_MASK (0x0000000C)
#define DP_DP_HPD_PLUG_INT_MASK (0x00000001)
base-commit: d1ea914925856d397b0b3241428f20b945e31434
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project