[PATCH v6 24/29] drm/msm/dp: add HPD callback for dp MST

From: Yongxing Mou

Date: Mon Aug 31 2026 - 04:37:09 EST


From: Abhinav Kumar <quic_abhinavk@xxxxxxxxxxx>

Add HPD callback for the MST module which shall be invoked from the
dp_display's HPD handler to perform MST specific operations in case
of HPD. In MST case, route the HPD messages to MST module.

Signed-off-by: Abhinav Kumar <quic_abhinavk@xxxxxxxxxxx>
Signed-off-by: Yongxing Mou <yongxing.mou@xxxxxxxxxxxxxxxx>
---
drivers/gpu/drm/msm/dp/dp_display.c | 26 +++++++++++++++++-----
drivers/gpu/drm/msm/dp/dp_mst_drm.c | 44 +++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/msm/dp/dp_mst_drm.h | 1 +
3 files changed, 66 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index c015af868801..22b925277462 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -416,6 +416,9 @@ static int msm_dp_hpd_plug_handle(struct msm_dp_display_private *dp)
dp->msm_dp_display.connector_type,
dp->link->sink_count);

+ if (dp->plugged && dp->msm_dp_display.mst_active)
+ return 0;
+
guard(mutex)(&dp->plugged_lock);

ret = pm_runtime_resume_and_get(&pdev->dev);
@@ -506,12 +509,18 @@ static int msm_dp_irq_hpd_handle(struct msm_dp_display_private *dp)
{
u32 sink_request;
int rc = 0;
+ struct msm_dp *msm_dp_display = &dp->msm_dp_display;

/* irq_hpd can happen at either connected or disconnected state */
drm_dbg_dp(dp->drm_dev, "Before, type=%d, sink_count=%d\n",
dp->msm_dp_display.connector_type,
dp->link->sink_count);

+ if (msm_dp_display->mst_active) {
+ msm_dp_mst_display_hpd_irq(&dp->msm_dp_display);
+ return 0;
+ }
+
/* check for any test request issued by sink */
rc = msm_dp_link_process_request(dp->link);
if (!rc) {
@@ -1056,10 +1065,16 @@ static irqreturn_t msm_dp_display_irq_thread(int irq, void *dev_id)
drm_bridge_hpd_notify(dp->msm_dp_display.bridge,
connector_status_connected);

- /* Send HPD as connected and distinguish it in the notifier */
- if (hpd_isr_status & DP_DP_IRQ_HPD_INT_MASK)
- drm_bridge_hpd_notify(dp->msm_dp_display.bridge,
- connector_status_connected);
+ /* Temporary until DRM IRQ support is finalized, then switch to
+ * drm_bridge_hpd_notify().
+ */
+ if (hpd_isr_status & DP_DP_IRQ_HPD_INT_MASK) {
+ if (dp->msm_dp_display.mst_active)
+ msm_dp_irq_hpd_handle(dp);
+ else
+ drm_bridge_hpd_notify(dp->msm_dp_display.bridge,
+ connector_status_connected);
+ }

ret = IRQ_HANDLED;

@@ -1717,7 +1732,8 @@ void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge,
msm_dp_hpd_plug_handle(dp);
}
} else {
- msm_dp_hpd_unplug_handle(dp);
+ if (hpd_link_status == ISR_DISCONNECTED)
+ msm_dp_hpd_unplug_handle(dp);
}

pm_runtime_put_sync(&msm_dp_display->pdev->dev);
diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
index 03f9c95ca686..2e625a7db981 100644
--- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
@@ -35,6 +35,8 @@ struct msm_dp_mst {
u32 max_streams;
/* Serializes concurrent stream link-state access across streams. */
struct mutex mst_lock;
+ /* Serializes HPD IRQ handling between IRQ handler and poll_hpd_irq. */
+ struct mutex hpd_irq_lock;
struct msm_dp_link_info link_info;
};

@@ -326,6 +328,39 @@ int msm_dp_mst_attach_encoder(struct msm_dp *dp_display, unsigned int stream_id,
return 0;
}

+void msm_dp_mst_display_hpd_irq(struct msm_dp *dp_display)
+{
+ int rc;
+ struct msm_dp_mst *mst = dp_display->msm_dp_mst;
+ u8 ack[8] = {};
+ u8 esi[4];
+ unsigned int esi_res = DP_SINK_COUNT_ESI + 1;
+ bool handled;
+
+ guard(mutex)(&mst->hpd_irq_lock);
+
+ rc = drm_dp_dpcd_read_data(mst->dp_aux, DP_SINK_COUNT_ESI, esi, 4);
+ if (rc < 0) {
+ DRM_ERROR("DPCD sink status read failed, rlen=%d\n", rc);
+ return;
+ }
+
+ drm_dbg_dp(dp_display->drm_dev, "MST HPD IRQ: esi=%*ph\n", 4, esi);
+
+ rc = drm_dp_mst_hpd_irq_handle_event(&mst->mst_mgr, esi, ack, &handled);
+
+ if (handled) {
+ rc = drm_dp_dpcd_write_byte(mst->dp_aux, esi_res, ack[1]);
+ if (rc < 0) {
+ DRM_ERROR("DPCD esi_res failed. rc=%d\n", rc);
+ return;
+ }
+
+ drm_dp_mst_hpd_irq_send_new_request(&mst->mst_mgr);
+ }
+ drm_dbg_dp(dp_display->drm_dev, "MST display hpd_irq handled:%d rc:%d\n", handled, rc);
+}
+
static struct drm_encoder *
msm_dp_mst_atomic_best_encoder(struct drm_connector *connector, struct drm_atomic_commit *state)
{
@@ -441,8 +476,16 @@ msm_dp_mst_add_connector(struct drm_dp_mst_topology_mgr *mgr,
return NULL;
}

+static void msm_dp_mst_poll_hpd_irq(struct drm_dp_mst_topology_mgr *mgr)
+{
+ struct msm_dp_mst *mst = container_of(mgr, struct msm_dp_mst, mst_mgr);
+
+ msm_dp_mst_display_hpd_irq(mst->msm_dp);
+}
+
static const struct drm_dp_mst_topology_cbs msm_dp_mst_drm_cbs = {
.add_connector = msm_dp_mst_add_connector,
+ .poll_hpd_irq = msm_dp_mst_poll_hpd_irq,
};

int msm_dp_mst_mgr_init(struct msm_dp *dp_display, u32 max_streams, struct drm_dp_aux *drm_aux)
@@ -471,6 +514,7 @@ int msm_dp_mst_mgr_init(struct msm_dp *dp_display, u32 max_streams, struct drm_d
}

mutex_init(&mst->mst_lock);
+ mutex_init(&mst->hpd_irq_lock);
dp_display->msm_dp_mst = mst;
return 0;
}
diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.h b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
index 89f73b1db2e7..8546214cbca1 100644
--- a/drivers/gpu/drm/msm/dp/dp_mst_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
@@ -10,5 +10,6 @@
int msm_dp_mst_mgr_init(struct msm_dp *dp_display, u32 max_streams, struct drm_dp_aux *drm_aux);
int msm_dp_mst_attach_encoder(struct msm_dp *dp_display, unsigned int stream_id,
struct drm_encoder *encoder);
+void msm_dp_mst_display_hpd_irq(struct msm_dp *dp_display);

#endif /* _DP_MST_DRM_H_ */

--
2.43.0