[PATCH v2 07/20] drm/msm/dp: Serialize stream operations with HPD processing

From: Xilin Wu

Date: Thu Oct 08 2026 - 23:18:55 EST


HPD PHY test requests stop and restart the mainlink under plugged_lock,
but atomic stream enable and disable do not take that lock. Both paths
can check a resource flag before either updates it and acquire duplicate
clock or PHY references. They can also reset a link while the other path
is training it or enabling video.

Hold plugged_lock across link preparation, stream enable and post-enable,
and protect stream disable and PSR transitions with the same lock. Add
lock assertions to the internal stream helpers. Acquire the runtime PM
reference and handle the eDP plug event before entering the critical
section, and leave audio shutdown waits and runtime PM release outside.

This provides the serialization required by PHY ownership tracking and
the subsequent stream enable rollback paths.

Assisted-by: LLM
Signed-off-by: Xilin Wu <sophon@xxxxxxxxx>
---
drivers/gpu/drm/msm/dp/dp_display.c | 42 ++++++++++++++++++++++++++++++-------
1 file changed, 35 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 32a2ac89d8ed..787efbcb6c12 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -53,6 +53,7 @@ struct msm_dp_display_private {
bool phy_initialized;
bool audio_supported;

+ /* Serializes HPD, stream and PSR operations while runtime active. */
struct mutex plugged_lock;
bool plugged;

@@ -623,13 +624,10 @@ static int msm_dp_display_set_mode(struct msm_dp *msm_dp_display,
return 0;
}

-static int msm_dp_display_prepare_link(struct msm_dp_display_private *dp)
+static int msm_dp_display_prepare(struct msm_dp_display_private *dp)
{
struct msm_dp *msm_dp_display = &dp->msm_dp_display;
- int rc = 0;
- bool force_link_train = false;
-
- drm_dbg_dp(dp->drm_dev, "sink_count=%d\n", dp->link->sink_count);
+ int rc;

if (msm_dp_display->is_edp)
msm_dp_hpd_plug_handle(dp);
@@ -640,6 +638,19 @@ static int msm_dp_display_prepare_link(struct msm_dp_display_private *dp)
return rc;
}

+ return 0;
+}
+
+static int msm_dp_display_prepare_link_locked(struct msm_dp_display_private *dp)
+{
+ struct msm_dp *msm_dp_display = &dp->msm_dp_display;
+ int rc = 0;
+ bool force_link_train = false;
+
+ drm_dbg_dp(dp->drm_dev, "sink_count=%d\n", dp->link->sink_count);
+
+ lockdep_assert_held(&dp->plugged_lock);
+
if (dp->link->sink_count == 0)
return -ENOTCONN;

@@ -664,6 +675,8 @@ static int msm_dp_display_enable(struct msm_dp_display_private *dp,
int rc = 0;
struct msm_dp *msm_dp_display = &dp->msm_dp_display;

+ lockdep_assert_held(&dp->plugged_lock);
+
drm_dbg_dp(dp->drm_dev, "sink_count=%d\n", dp->link->sink_count);
if (msm_dp_display->power_on) {
drm_dbg_dp(dp->drm_dev, "Link already setup, return\n");
@@ -684,6 +697,8 @@ static int msm_dp_display_post_enable(struct msm_dp *msm_dp_display)

dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);

+ lockdep_assert_held(&dp->plugged_lock);
+
rate = dp->link->link_params.rate;

if (dp->audio_supported) {
@@ -721,6 +736,8 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp,
{
struct msm_dp *msm_dp_display = &dp->msm_dp_display;

+ lockdep_assert_held(&dp->plugged_lock);
+
if (!msm_dp_display->power_on)
return 0;

@@ -880,6 +897,8 @@ void msm_dp_display_set_psr(struct msm_dp *msm_dp_display, bool enter)
}

dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
+ guard(mutex)(&dp->plugged_lock);
+
msm_dp_ctrl_set_psr(dp->ctrl, dp->panel, enter);
}

@@ -1449,7 +1468,13 @@ void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display,

dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);

- rc = msm_dp_display_prepare_link(dp);
+ rc = msm_dp_display_prepare(dp);
+ if (rc)
+ return;
+
+ guard(mutex)(&dp->plugged_lock);
+
+ rc = msm_dp_display_prepare_link_locked(dp);
if (rc) {
DRM_ERROR("DP display prepare failed, rc=%d\n", rc);
return;
@@ -1474,6 +1499,8 @@ void msm_dp_display_atomic_disable(struct msm_dp *dp)

msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);

+ guard(mutex)(&msm_dp_display->plugged_lock);
+
/*
* If .atomic_enable() bailed out - link training failure is the common
* case - the mainlink was never brought up and ->power_on stayed false.
@@ -1511,7 +1538,8 @@ void msm_dp_display_atomic_post_disable(struct msm_dp *dp)

msm_dp_display_audio_notify_disable(msm_dp_display);

- msm_dp_display_disable(msm_dp_display, msm_dp_display->panel);
+ scoped_guard(mutex, &msm_dp_display->plugged_lock)
+ msm_dp_display_disable(msm_dp_display, msm_dp_display->panel);

msm_dp_display_unprepare(msm_dp_display);
}

--
2.56.0