[PATCH v5 25/25] drm/msm/dp: add HPD callback for dp MST
From: Yongxing Mou
Date: Mon Jun 29 2026 - 09:31:51 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 | 22 ++++++++++++++----
drivers/gpu/drm/msm/dp/dp_mst_drm.c | 46 +++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/msm/dp/dp_mst_drm.h | 1 +
3 files changed, 65 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 6eac390af2e0..49a7bc5e031e 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -455,6 +455,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);
@@ -550,12 +553,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) {
@@ -1111,9 +1120,13 @@ static irqreturn_t msm_dp_display_irq_thread(int irq, void *dev_id)
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);
+ 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;
@@ -1776,7 +1789,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 12b47a413793..56f7a84e77d1 100644
--- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
@@ -36,6 +36,8 @@ struct msm_dp_mst {
struct drm_dp_aux *dp_aux;
u32 max_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;
};
@@ -288,6 +290,41 @@ int msm_dp_mst_display_set_mgr_state(struct msm_dp *dp_display, bool state)
return rc;
}
+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 irq: esi1[0x%x] esi2[0x%x] esi3[%x]\n",
+ esi[1], esi[2], esi[3]);
+
+ rc = drm_dp_mst_hpd_irq_handle_event(&mst->mst_mgr, esi, ack, &handled);
+
+ /* ack the request */
+ 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);
+}
+
/* DP MST Connector OPs */
static int
msm_dp_mst_connector_detect(struct drm_connector *connector,
@@ -502,8 +539,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_init(struct msm_dp *dp_display, u32 max_streams, struct drm_dp_aux *drm_aux)
@@ -532,6 +577,7 @@ int msm_dp_mst_init(struct msm_dp *dp_display, u32 max_streams, struct drm_dp_au
}
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 5d411529f681..08e145399cfc 100644
--- a/drivers/gpu/drm/msm/dp/dp_mst_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
@@ -9,5 +9,6 @@
int msm_dp_mst_init(struct msm_dp *dp_display, u32 max_streams, struct drm_dp_aux *drm_aux);
int msm_dp_mst_display_set_mgr_state(struct msm_dp *dp_display, bool state);
+void msm_dp_mst_display_hpd_irq(struct msm_dp *dp_display);
#endif /* _DP_MST_DRM_H_ */
--
2.43.0