* [PATCH v7 00/15] drm/msm/dp: Prerequisite cleanup for upcoming MST support
@ 2026-06-09 9:46 Yongxing Mou
2026-06-09 9:46 ` [PATCH v7 01/15] drm/msm/dp: remove cached drm_edid from panel Yongxing Mou
` (14 more replies)
0 siblings, 15 replies; 28+ messages in thread
From: Yongxing Mou @ 2026-06-09 9:46 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Yongxing Mou,
Dmitry Baryshkov, Abhinav Kumar
This series is the SST-only prerequisite portion of the MSM DP MST
work. It refactors the existing DP code paths so that MST can
plug in cleanly in a follow-up series, without bundling the cleanup
with MST functionality in the same submission.
SST behaviour is preserved end-to-end; no new functionality is added
here. The intent is to land these refactors first to keep the
follow-up MST series focused, smaller, and easier to review.
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
Changes in v7:
- patch 7: Use WARN_ON_ONCE() when pixel clock is already on. [Konrad]
- Link to v6: https://lore.kernel.org/r/20260602-dp_mstclean-v6-0-2c17ff40a9b2@oss.qualcomm.com
Changes in v6:
- patch 4: fixed the RMW comments. [Dmitry]
- patch 10: do not rename the existing struct. [Dmitry]
- Link to v5: https://lore.kernel.org/r/20260528-dp_mstclean-v5-0-a9221c1f1f3b@oss.qualcomm.com
Changes in v5:
- Split out from v4: only the SST prerequisite cleanup (patches 1-15)
is sent here; the MST implementation will follow on top.
- Rebased onto linux-next-20260527; adapted bridge callbacks to the
new drm_atomic_commit* API.
- patch 3: added Suggested-by: Dmitry Baryshkov.
- patch 4: fixed "splite" typo, reworded body, added RMW comment
covering both SST and MST paths. [Dmitry]
- patch 10: drop cached panel from msm_dp_ctrl_private; pass panel
explicitly to all stream-related dp_ctrl APIs. [Dmitry]
- patch 13/14: introduce bridge wrappers and atomic_prepare with
drm_atomic_commit* from the start to preserve bisectability.
- patch 15: fixed pass panel inside the func. [Dmitry]
- Link to v4: https://lore.kernel.org/all/20260410-msm-dp-mst-v4-0-b20518dea8de@oss.qualcomm.com/
---
Abhinav Kumar (6):
drm/msm/dp: break up dp_display_enable into two parts
drm/msm/dp: re-arrange dp_display_disable() into functional parts
drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it
drm/msm/dp: split dp_ctrl_off() into stream and link parts
drm/msm/dp: make bridge helpers use dp_display to allow re-use
drm/msm/dp: separate dp_display_prepare() into its own API
Yongxing Mou (9):
drm/msm/dp: remove cached drm_edid from panel
drm/msm/dp: drop deprecated .mode_set() and use .atomic_enable
drm/msm/dp: move mode setup into msm_dp_panel_init_panel_info()
drm/msm/dp: split msm_dp_ctrl_config_ctrl() into link parts and stream parts
drm/msm/dp: extract MISC1_MISC0 configuration into a separate function
drm/msm/dp: split link setup from source params
drm/msm/dp: move the pixel clock control to its own API
drm/msm/dp: simplify link and clock disable sequence
drm/msm/dp: pass panel to display enable/disable helpers
drivers/gpu/drm/msm/dp/dp_ctrl.c | 382 ++++++++++++++++++++----------------
drivers/gpu/drm/msm/dp/dp_ctrl.h | 30 ++-
drivers/gpu/drm/msm/dp/dp_display.c | 273 +++++++++++++-------------
drivers/gpu/drm/msm/dp/dp_display.h | 8 +
drivers/gpu/drm/msm/dp/dp_drm.c | 43 +++-
drivers/gpu/drm/msm/dp/dp_drm.h | 12 --
drivers/gpu/drm/msm/dp/dp_panel.c | 75 ++-----
drivers/gpu/drm/msm/dp/dp_panel.h | 17 +-
8 files changed, 451 insertions(+), 389 deletions(-)
---
base-commit: e7d700e14934e68f86338c5610cf2ae76798b663
change-id: 20260528-dp_mstclean-f094cea8ca24
Best regards,
--
Yongxing Mou <yongxing.mou@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v7 01/15] drm/msm/dp: remove cached drm_edid from panel
2026-06-09 9:46 [PATCH v7 00/15] drm/msm/dp: Prerequisite cleanup for upcoming MST support Yongxing Mou
@ 2026-06-09 9:46 ` Yongxing Mou
2026-06-09 10:04 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 02/15] drm/msm/dp: drop deprecated .mode_set() and use .atomic_enable Yongxing Mou
` (13 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Yongxing Mou @ 2026-06-09 9:46 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Yongxing Mou,
Dmitry Baryshkov
The cached drm_edid seems unnecessary here. Use the drm_edid pointer
directly in the plug stage instead of caching it. Remove the cached
drm_edid and the corresponding oneliner to simplify the code.
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 28 +++++++++++-------
drivers/gpu/drm/msm/dp/dp_panel.c | 57 ++++---------------------------------
drivers/gpu/drm/msm/dp/dp_panel.h | 13 +++------
3 files changed, 27 insertions(+), 71 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 6800c628adb4..e3682c4d6077 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -269,6 +269,7 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
const struct drm_display_info *info = &connector->display_info;
int rc = 0;
u8 dpcd[DP_RECEIVER_CAP_SIZE];
+ const struct drm_edid *drm_edid = NULL;
rc = drm_dp_read_dpcd_caps(dp->aux, dpcd);
if (rc)
@@ -276,10 +277,20 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
dp->link->lttpr_count = msm_dp_display_lttpr_init(dp, dpcd);
- rc = msm_dp_panel_read_sink_caps(dp->panel, connector);
+ rc = msm_dp_panel_read_link_caps(dp->panel, connector);
if (rc)
goto end;
+ drm_edid = drm_edid_read_ddc(connector, &dp->aux->ddc);
+ drm_edid_connector_update(connector, drm_edid);
+
+ if (!drm_edid) {
+ DRM_ERROR("panel edid read failed\n");
+ /* check edid read fail is due to unplug */
+ if (!msm_dp_aux_is_link_connected(dp->aux))
+ return -ETIMEDOUT;
+ }
+
msm_dp_link_process_request(dp->link);
if (!dp->msm_dp_display.is_edp)
@@ -291,7 +302,7 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
dp->msm_dp_display.psr_supported = dp->panel->psr_cap.version && psr_enabled;
dp->audio_supported = info->has_audio;
- msm_dp_panel_handle_sink_request(dp->panel);
+ msm_dp_panel_handle_sink_request(dp->panel, drm_edid);
/*
* set sink to normal operation mode -- D0
@@ -302,6 +313,7 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
msm_dp_link_reset_phy_params_vx_px(dp->link);
end:
+ drm_edid_free(drm_edid);
return rc;
}
@@ -453,7 +465,7 @@ static int msm_dp_hpd_unplug_handle(struct msm_dp_display_private *dp)
/* Don't forget modes for eDP */
if (!dp->msm_dp_display.is_edp)
- msm_dp_panel_unplugged(dp->panel, dp->msm_dp_display.connector);
+ drm_edid_connector_update(dp->msm_dp_display.connector, NULL);
/* triggered by irq_hdp with sink_count = 0 */
if (dp->link->sink_count == 0)
@@ -515,7 +527,6 @@ static int msm_dp_irq_hpd_handle(struct msm_dp_display_private *dp)
static void msm_dp_display_deinit_sub_modules(struct msm_dp_display_private *dp)
{
msm_dp_audio_put(dp->audio);
- msm_dp_panel_put(dp->panel);
msm_dp_aux_put(dp->aux);
}
@@ -566,7 +577,7 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp)
rc = PTR_ERR(dp->ctrl);
DRM_ERROR("failed to initialize ctrl, rc = %d\n", rc);
dp->ctrl = NULL;
- goto error_ctrl;
+ goto error_link;
}
dp->audio = msm_dp_audio_get(dp->msm_dp_display.pdev, dp->link_base);
@@ -574,13 +585,11 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp)
rc = PTR_ERR(dp->audio);
pr_err("failed to initialize audio, rc = %d\n", rc);
dp->audio = NULL;
- goto error_ctrl;
+ goto error_link;
}
return rc;
-error_ctrl:
- msm_dp_panel_put(dp->panel);
error_link:
msm_dp_aux_put(dp->aux);
error:
@@ -744,8 +753,7 @@ int msm_dp_display_get_modes(struct msm_dp *dp)
msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
- return msm_dp_panel_get_modes(msm_dp_display->panel,
- dp->connector);
+ return drm_edid_connector_add_modes(msm_dp_display->panel->connector);
}
bool msm_dp_display_check_video_test(struct msm_dp *dp)
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index 6bb021820d7c..bde4a772d22c 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -232,8 +232,8 @@ static u32 msm_dp_panel_get_supported_bpp(struct msm_dp_panel *msm_dp_panel,
return min_supported_bpp;
}
-int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
- struct drm_connector *connector)
+int msm_dp_panel_read_link_caps(struct msm_dp_panel *msm_dp_panel,
+ struct drm_connector *connector)
{
int rc, bw_code;
int count;
@@ -271,36 +271,9 @@ int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
rc = drm_dp_read_downstream_info(panel->aux, msm_dp_panel->dpcd,
msm_dp_panel->downstream_ports);
- if (rc)
- return rc;
-
- drm_edid_free(msm_dp_panel->drm_edid);
-
- msm_dp_panel->drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
-
- drm_edid_connector_update(connector, msm_dp_panel->drm_edid);
-
- if (!msm_dp_panel->drm_edid) {
- DRM_ERROR("panel edid read failed\n");
- /* check edid read fail is due to unplug */
- if (!msm_dp_aux_is_link_connected(panel->aux)) {
- rc = -ETIMEDOUT;
- goto end;
- }
- }
-
-end:
return rc;
}
-void msm_dp_panel_unplugged(struct msm_dp_panel *msm_dp_panel,
- struct drm_connector *connector)
-{
- drm_edid_connector_update(connector, NULL);
- drm_edid_free(msm_dp_panel->drm_edid);
- msm_dp_panel->drm_edid = NULL;
-}
-
u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel,
u32 mode_edid_bpp, u32 mode_pclk_khz)
{
@@ -324,20 +297,6 @@ u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel,
return bpp;
}
-int msm_dp_panel_get_modes(struct msm_dp_panel *msm_dp_panel,
- struct drm_connector *connector)
-{
- if (!msm_dp_panel) {
- DRM_ERROR("invalid input\n");
- return -EINVAL;
- }
-
- if (msm_dp_panel->drm_edid)
- return drm_edid_connector_add_modes(connector);
-
- return 0;
-}
-
static u8 msm_dp_panel_get_edid_checksum(const struct edid *edid)
{
edid += edid->extensions;
@@ -345,7 +304,8 @@ static u8 msm_dp_panel_get_edid_checksum(const struct edid *edid)
return edid->checksum;
}
-void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel)
+void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel,
+ const struct drm_edid *drm_edid)
{
struct msm_dp_panel_private *panel;
@@ -358,7 +318,7 @@ void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel)
if (panel->link->sink_request & DP_TEST_LINK_EDID_READ) {
/* FIXME: get rid of drm_edid_raw() */
- const struct edid *edid = drm_edid_raw(msm_dp_panel->drm_edid);
+ const struct edid *edid = drm_edid_raw(drm_edid);
u8 checksum;
if (edid)
@@ -755,10 +715,3 @@ struct msm_dp_panel *msm_dp_panel_get(struct device *dev, struct drm_dp_aux *aux
return msm_dp_panel;
}
-void msm_dp_panel_put(struct msm_dp_panel *msm_dp_panel)
-{
- if (!msm_dp_panel)
- return;
-
- drm_edid_free(msm_dp_panel->drm_edid);
-}
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
index 9173e90a5053..53b7b4463551 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -33,7 +33,6 @@ struct msm_dp_panel {
u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
struct msm_dp_link_info link_info;
- const struct drm_edid *drm_edid;
struct drm_connector *connector;
struct msm_dp_display_mode msm_dp_mode;
struct msm_dp_panel_psr psr_cap;
@@ -47,15 +46,12 @@ struct msm_dp_panel {
int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel);
int msm_dp_panel_deinit(struct msm_dp_panel *msm_dp_panel);
int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en);
-int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
- struct drm_connector *connector);
-void msm_dp_panel_unplugged(struct msm_dp_panel *msm_dp_panel,
- struct drm_connector *connector);
+int msm_dp_panel_read_link_caps(struct msm_dp_panel *msm_dp_panel,
+ struct drm_connector *connector);
u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel, u32 mode_max_bpp,
u32 mode_pclk_khz);
-int msm_dp_panel_get_modes(struct msm_dp_panel *msm_dp_panel,
- struct drm_connector *connector);
-void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel);
+void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel,
+ const struct drm_edid *drm_edid);
void msm_dp_panel_tpg_config(struct msm_dp_panel *msm_dp_panel, bool enable);
void msm_dp_panel_clear_dsc_dto(struct msm_dp_panel *msm_dp_panel);
@@ -94,5 +90,4 @@ struct msm_dp_panel *msm_dp_panel_get(struct device *dev, struct drm_dp_aux *aux
struct msm_dp_link *link,
void __iomem *link_base,
void __iomem *p0_base);
-void msm_dp_panel_put(struct msm_dp_panel *msm_dp_panel);
#endif /* _DP_PANEL_H_ */
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 02/15] drm/msm/dp: drop deprecated .mode_set() and use .atomic_enable
2026-06-09 9:46 [PATCH v7 00/15] drm/msm/dp: Prerequisite cleanup for upcoming MST support Yongxing Mou
2026-06-09 9:46 ` [PATCH v7 01/15] drm/msm/dp: remove cached drm_edid from panel Yongxing Mou
@ 2026-06-09 9:46 ` Yongxing Mou
2026-06-09 10:06 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 03/15] drm/msm/dp: move mode setup into msm_dp_panel_init_panel_info() Yongxing Mou
` (12 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Yongxing Mou @ 2026-06-09 9:46 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Yongxing Mou,
Abhinav Kumar, Dmitry Baryshkov
The bridge .mode_set() callback is deprecated. Remove it and move all
mode setup logic to .atomic_enable(), where the adjusted_mode is
available from the atomic CRTC state.
Drop msm_dp_mode from msm_dp_display_private and store the mode directly
in the panel, as it was only used as a temporary cache. Both changes are
limited to msm_dp_display_set_mode and are kept in a single patch.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 81 ++++++++++++++-----------------------
drivers/gpu/drm/msm/dp/dp_drm.c | 2 -
drivers/gpu/drm/msm/dp/dp_drm.h | 3 --
3 files changed, 31 insertions(+), 55 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index e3682c4d6077..181d238addfc 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -63,7 +63,6 @@ struct msm_dp_display_private {
struct msm_dp_panel *panel;
struct msm_dp_ctrl *ctrl;
- struct msm_dp_display_mode msm_dp_mode;
struct msm_dp msm_dp_display;
/* wait for audio signaling */
@@ -597,16 +596,33 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp)
}
static int msm_dp_display_set_mode(struct msm_dp *msm_dp_display,
- struct msm_dp_display_mode *mode)
+ const struct drm_display_mode *adjusted_mode,
+ struct msm_dp_panel *msm_dp_panel)
{
struct msm_dp_display_private *dp;
+ u32 bpp;
dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
- drm_mode_copy(&dp->panel->msm_dp_mode.drm_mode, &mode->drm_mode);
- dp->panel->msm_dp_mode.bpp = mode->bpp;
- dp->panel->msm_dp_mode.out_fmt_is_yuv_420 = mode->out_fmt_is_yuv_420;
- msm_dp_panel_init_panel_info(dp->panel);
+ drm_mode_copy(&msm_dp_panel->msm_dp_mode.drm_mode, adjusted_mode);
+ if (msm_dp_display_check_video_test(msm_dp_display))
+ bpp = msm_dp_display_get_test_bpp(msm_dp_display);
+ else
+ bpp = msm_dp_panel->connector->display_info.bpc * 3;
+
+ msm_dp_panel->msm_dp_mode.bpp = bpp ? bpp : 24; /* Default bpp */
+ msm_dp_panel->msm_dp_mode.v_active_low =
+ !!(adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC);
+ msm_dp_panel->msm_dp_mode.h_active_low =
+ !!(adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC);
+ msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 =
+ drm_mode_is_420_only(&msm_dp_panel->connector->display_info, adjusted_mode) &&
+ msm_dp_panel->vsc_sdp_supported;
+ msm_dp_panel_init_panel_info(msm_dp_panel);
+
+ /* populate wide_bus_support to different layers */
+ dp->ctrl->wide_bus_en =
+ msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 ? false : dp->wide_bus_supported;
return 0;
}
@@ -1309,7 +1325,7 @@ bool msm_dp_wide_bus_available(const struct msm_dp *msm_dp_display)
dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
- if (dp->msm_dp_mode.out_fmt_is_yuv_420)
+ if (dp->panel->msm_dp_mode.out_fmt_is_yuv_420)
return false;
return dp->wide_bus_supported;
@@ -1365,15 +1381,19 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
{
struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge);
struct msm_dp *dp = msm_dp_bridge->msm_dp_display;
+ struct drm_crtc *crtc;
+ struct drm_crtc_state *crtc_state;
int rc = 0;
struct msm_dp_display_private *msm_dp_display;
bool force_link_train = false;
msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
- if (!msm_dp_display->msm_dp_mode.drm_mode.clock) {
- DRM_ERROR("invalid params\n");
+
+ crtc = drm_atomic_get_new_crtc_for_encoder(state,
+ drm_bridge->encoder);
+ if (!crtc)
return;
- }
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
if (dp->is_edp)
msm_dp_hpd_plug_handle(msm_dp_display);
@@ -1386,7 +1406,7 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
if (msm_dp_display->link->sink_count == 0)
return;
- rc = msm_dp_display_set_mode(dp, &msm_dp_display->msm_dp_mode);
+ rc = msm_dp_display_set_mode(dp, &crtc_state->adjusted_mode, msm_dp_display->panel);
if (rc) {
DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc);
return;
@@ -1446,45 +1466,6 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
pm_runtime_put_sync(&dp->pdev->dev);
}
-void msm_dp_bridge_mode_set(struct drm_bridge *drm_bridge,
- const struct drm_display_mode *mode,
- const struct drm_display_mode *adjusted_mode)
-{
- struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge);
- struct msm_dp *dp = msm_dp_bridge->msm_dp_display;
- struct msm_dp_display_private *msm_dp_display;
- struct msm_dp_panel *msm_dp_panel;
-
- msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
- msm_dp_panel = msm_dp_display->panel;
-
- memset(&msm_dp_display->msm_dp_mode, 0x0, sizeof(struct msm_dp_display_mode));
-
- if (msm_dp_display_check_video_test(dp))
- msm_dp_display->msm_dp_mode.bpp = msm_dp_display_get_test_bpp(dp);
- else /* Default num_components per pixel = 3 */
- msm_dp_display->msm_dp_mode.bpp = dp->connector->display_info.bpc * 3;
-
- if (!msm_dp_display->msm_dp_mode.bpp)
- msm_dp_display->msm_dp_mode.bpp = 24; /* Default bpp */
-
- drm_mode_copy(&msm_dp_display->msm_dp_mode.drm_mode, adjusted_mode);
-
- msm_dp_display->msm_dp_mode.v_active_low =
- !!(msm_dp_display->msm_dp_mode.drm_mode.flags & DRM_MODE_FLAG_NVSYNC);
-
- msm_dp_display->msm_dp_mode.h_active_low =
- !!(msm_dp_display->msm_dp_mode.drm_mode.flags & DRM_MODE_FLAG_NHSYNC);
-
- msm_dp_display->msm_dp_mode.out_fmt_is_yuv_420 =
- drm_mode_is_420_only(&dp->connector->display_info, adjusted_mode) &&
- msm_dp_panel->vsc_sdp_supported;
-
- /* populate wide_bus_support to different layers */
- msm_dp_display->ctrl->wide_bus_en =
- msm_dp_display->msm_dp_mode.out_fmt_is_yuv_420 ? false : msm_dp_display->wide_bus_supported;
-}
-
void msm_dp_bridge_hpd_enable(struct drm_bridge *bridge)
{
struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(bridge);
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index b659d22f5f28..6ac5bac903d9 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -56,7 +56,6 @@ static const struct drm_bridge_funcs msm_dp_bridge_ops = {
.atomic_enable = msm_dp_bridge_atomic_enable,
.atomic_disable = msm_dp_bridge_atomic_disable,
.atomic_post_disable = msm_dp_bridge_atomic_post_disable,
- .mode_set = msm_dp_bridge_mode_set,
.mode_valid = msm_dp_bridge_mode_valid,
.get_modes = msm_dp_bridge_get_modes,
.detect = msm_dp_bridge_detect,
@@ -233,7 +232,6 @@ static const struct drm_bridge_funcs msm_edp_bridge_ops = {
.atomic_enable = msm_edp_bridge_atomic_enable,
.atomic_disable = msm_edp_bridge_atomic_disable,
.atomic_post_disable = msm_edp_bridge_atomic_post_disable,
- .mode_set = msm_dp_bridge_mode_set,
.mode_valid = msm_edp_bridge_mode_valid,
.atomic_reset = drm_atomic_helper_bridge_reset,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h
index 041aa026ae2e..4bd788ea05d5 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_drm.h
@@ -36,9 +36,6 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge,
const struct drm_display_info *info,
const struct drm_display_mode *mode);
-void msm_dp_bridge_mode_set(struct drm_bridge *drm_bridge,
- const struct drm_display_mode *mode,
- const struct drm_display_mode *adjusted_mode);
void msm_dp_bridge_hpd_enable(struct drm_bridge *bridge);
void msm_dp_bridge_hpd_disable(struct drm_bridge *bridge);
void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge,
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 03/15] drm/msm/dp: move mode setup into msm_dp_panel_init_panel_info()
2026-06-09 9:46 [PATCH v7 00/15] drm/msm/dp: Prerequisite cleanup for upcoming MST support Yongxing Mou
2026-06-09 9:46 ` [PATCH v7 01/15] drm/msm/dp: remove cached drm_edid from panel Yongxing Mou
2026-06-09 9:46 ` [PATCH v7 02/15] drm/msm/dp: drop deprecated .mode_set() and use .atomic_enable Yongxing Mou
@ 2026-06-09 9:46 ` Yongxing Mou
2026-06-09 9:46 ` [PATCH v7 04/15] drm/msm/dp: split msm_dp_ctrl_config_ctrl() into link parts and stream parts Yongxing Mou
` (11 subsequent siblings)
14 siblings, 0 replies; 28+ messages in thread
From: Yongxing Mou @ 2026-06-09 9:46 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Yongxing Mou,
Dmitry Baryshkov
The display layer directly assigns msm_dp_panel mode fields (bpp,
sync polarity, yuv420 flag) instead of letting the panel manage its
own state. Pass adjusted_mode and bpp as parameters to
msm_dp_panel_init_panel_info() and move the assignments inside it.
Suggested-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 11 +----------
drivers/gpu/drm/msm/dp/dp_panel.c | 18 +++++++++++++++---
drivers/gpu/drm/msm/dp/dp_panel.h | 4 +++-
3 files changed, 19 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 181d238addfc..f33c754b83c3 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -604,21 +604,12 @@ static int msm_dp_display_set_mode(struct msm_dp *msm_dp_display,
dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
- drm_mode_copy(&msm_dp_panel->msm_dp_mode.drm_mode, adjusted_mode);
if (msm_dp_display_check_video_test(msm_dp_display))
bpp = msm_dp_display_get_test_bpp(msm_dp_display);
else
bpp = msm_dp_panel->connector->display_info.bpc * 3;
- msm_dp_panel->msm_dp_mode.bpp = bpp ? bpp : 24; /* Default bpp */
- msm_dp_panel->msm_dp_mode.v_active_low =
- !!(adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC);
- msm_dp_panel->msm_dp_mode.h_active_low =
- !!(adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC);
- msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 =
- drm_mode_is_420_only(&msm_dp_panel->connector->display_info, adjusted_mode) &&
- msm_dp_panel->vsc_sdp_supported;
- msm_dp_panel_init_panel_info(msm_dp_panel);
+ msm_dp_panel_init_panel_info(msm_dp_panel, adjusted_mode, bpp ? bpp : 24);
/* populate wide_bus_support to different layers */
dp->ctrl->wide_bus_en =
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index bde4a772d22c..e76dad0f6663 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -647,15 +647,27 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en)
return 0;
}
-int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel)
+int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel,
+ const struct drm_display_mode *adjusted_mode,
+ u32 bpp)
{
struct drm_display_mode *drm_mode;
struct msm_dp_panel_private *panel;
- drm_mode = &msm_dp_panel->msm_dp_mode.drm_mode;
-
panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
+ drm_mode_copy(&msm_dp_panel->msm_dp_mode.drm_mode, adjusted_mode);
+ msm_dp_panel->msm_dp_mode.bpp = bpp;
+ msm_dp_panel->msm_dp_mode.v_active_low =
+ !!(adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC);
+ msm_dp_panel->msm_dp_mode.h_active_low =
+ !!(adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC);
+ msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 =
+ drm_mode_is_420_only(&msm_dp_panel->connector->display_info, adjusted_mode) &&
+ msm_dp_panel->vsc_sdp_supported;
+
+ drm_mode = &msm_dp_panel->msm_dp_mode.drm_mode;
+
/*
* print resolution info as this is a result
* of user initiated action of cable connection
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
index 53b7b4463551..4519ac374220 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -43,7 +43,9 @@ struct msm_dp_panel {
u32 max_bw_code;
};
-int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel);
+int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel,
+ const struct drm_display_mode *adjusted_mode,
+ u32 bpp);
int msm_dp_panel_deinit(struct msm_dp_panel *msm_dp_panel);
int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en);
int msm_dp_panel_read_link_caps(struct msm_dp_panel *msm_dp_panel,
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 04/15] drm/msm/dp: split msm_dp_ctrl_config_ctrl() into link parts and stream parts
2026-06-09 9:46 [PATCH v7 00/15] drm/msm/dp: Prerequisite cleanup for upcoming MST support Yongxing Mou
` (2 preceding siblings ...)
2026-06-09 9:46 ` [PATCH v7 03/15] drm/msm/dp: move mode setup into msm_dp_panel_init_panel_info() Yongxing Mou
@ 2026-06-09 9:46 ` Yongxing Mou
2026-06-09 10:04 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 05/15] drm/msm/dp: extract MISC1_MISC0 configuration into a separate function Yongxing Mou
` (10 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Yongxing Mou @ 2026-06-09 9:46 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Yongxing Mou,
Dmitry Baryshkov
The DP_CONFIGURATION_CTRL register contains both link-level and
stream-specific fields. Currently, msm_dp_ctrl_config_ctrl() configures
all of them together. Separate the configuration into link parts and
stream parts to support MST.
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 46 +++++++++++++++++++++++++++-------------
1 file changed, 31 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 86ef8c89ad44..ed2ba47881fd 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -388,26 +388,44 @@ void msm_dp_ctrl_push_idle(struct msm_dp_ctrl *msm_dp_ctrl)
drm_dbg_dp(ctrl->drm_dev, "mainlink off\n");
}
-static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl)
+static void msm_dp_ctrl_config_ctrl_streams(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *msm_dp_panel)
{
u32 config = 0, tbd;
+
+ /*
+ * RMW: Called from atomic_enable(). Serialized by the DRM atomic framework.
+ */
+ config = msm_dp_read_link(ctrl, REG_DP_CONFIGURATION_CTRL);
+
+ if (msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
+ config |= DP_CONFIGURATION_CTRL_RGB_YUV; /* YUV420 */
+
+ tbd = msm_dp_link_get_test_bits_depth(ctrl->link,
+ msm_dp_panel->msm_dp_mode.bpp);
+
+ config |= tbd << DP_CONFIGURATION_CTRL_BPC_SHIFT;
+
+ if (msm_dp_panel->psr_cap.version)
+ config |= DP_CONFIGURATION_CTRL_SEND_VSC;
+
+ drm_dbg_dp(ctrl->drm_dev, "stream DP_CONFIGURATION_CTRL=0x%x\n", config);
+
+ msm_dp_write_link(ctrl, REG_DP_CONFIGURATION_CTRL, config);
+}
+
+static void msm_dp_ctrl_config_ctrl_link(struct msm_dp_ctrl_private *ctrl)
+{
+ u32 config = 0;
const u8 *dpcd = ctrl->panel->dpcd;
/* Default-> LSCLK DIV: 1/4 LCLK */
config |= (2 << DP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT);
- if (ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420)
- config |= DP_CONFIGURATION_CTRL_RGB_YUV; /* YUV420 */
-
/* Scrambler reset enable */
if (drm_dp_alternate_scrambler_reset_cap(dpcd))
config |= DP_CONFIGURATION_CTRL_ASSR;
- tbd = msm_dp_link_get_test_bits_depth(ctrl->link,
- ctrl->panel->msm_dp_mode.bpp);
-
- config |= tbd << DP_CONFIGURATION_CTRL_BPC_SHIFT;
-
/* Num of Lanes */
config |= ((ctrl->link->link_params.num_lanes - 1)
<< DP_CONFIGURATION_CTRL_NUM_OF_LANES_SHIFT);
@@ -421,10 +439,7 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl)
config |= DP_CONFIGURATION_CTRL_STATIC_DYNAMIC_CN;
config |= DP_CONFIGURATION_CTRL_SYNC_ASYNC_CLK;
- if (ctrl->panel->psr_cap.version)
- config |= DP_CONFIGURATION_CTRL_SEND_VSC;
-
- drm_dbg_dp(ctrl->drm_dev, "DP_CONFIGURATION_CTRL=0x%x\n", config);
+ drm_dbg_dp(ctrl->drm_dev, "link DP_CONFIGURATION_CTRL=0x%x\n", config);
msm_dp_write_link(ctrl, REG_DP_CONFIGURATION_CTRL, config);
}
@@ -450,7 +465,8 @@ static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl
msm_dp_ctrl_lane_mapping(ctrl);
msm_dp_setup_peripheral_flush(ctrl);
- msm_dp_ctrl_config_ctrl(ctrl);
+ msm_dp_ctrl_config_ctrl_link(ctrl);
+ msm_dp_ctrl_config_ctrl_streams(ctrl, ctrl->panel);
test_bits_depth = msm_dp_link_get_test_bits_depth(ctrl->link, ctrl->panel->msm_dp_mode.bpp);
colorimetry_cfg = msm_dp_link_get_colorimetry_config(ctrl->link);
@@ -1628,7 +1644,7 @@ static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl,
u8 assr;
struct msm_dp_link_info link_info = {0};
- msm_dp_ctrl_config_ctrl(ctrl);
+ msm_dp_ctrl_config_ctrl_link(ctrl);
link_info.num_lanes = ctrl->link->link_params.num_lanes;
link_info.rate = ctrl->link->link_params.rate;
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 05/15] drm/msm/dp: extract MISC1_MISC0 configuration into a separate function
2026-06-09 9:46 [PATCH v7 00/15] drm/msm/dp: Prerequisite cleanup for upcoming MST support Yongxing Mou
` (3 preceding siblings ...)
2026-06-09 9:46 ` [PATCH v7 04/15] drm/msm/dp: split msm_dp_ctrl_config_ctrl() into link parts and stream parts Yongxing Mou
@ 2026-06-09 9:46 ` Yongxing Mou
2026-06-09 9:46 ` [PATCH v7 06/15] drm/msm/dp: split link setup from source params Yongxing Mou
` (9 subsequent siblings)
14 siblings, 0 replies; 28+ messages in thread
From: Yongxing Mou @ 2026-06-09 9:46 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Yongxing Mou,
Dmitry Baryshkov
Refactor the MISC1_MISC0 register configuration into a standalone helper
function to support MST.
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index ed2ba47881fd..71d45b2c4daf 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -458,17 +458,13 @@ static void msm_dp_ctrl_lane_mapping(struct msm_dp_ctrl_private *ctrl)
ln_mapping);
}
-static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl)
+static void msm_dp_ctrl_config_misc1_misc0(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *msm_dp_panel)
{
u32 colorimetry_cfg, test_bits_depth, misc_val;
- msm_dp_ctrl_lane_mapping(ctrl);
- msm_dp_setup_peripheral_flush(ctrl);
-
- msm_dp_ctrl_config_ctrl_link(ctrl);
- msm_dp_ctrl_config_ctrl_streams(ctrl, ctrl->panel);
-
- test_bits_depth = msm_dp_link_get_test_bits_depth(ctrl->link, ctrl->panel->msm_dp_mode.bpp);
+ test_bits_depth = msm_dp_link_get_test_bits_depth(ctrl->link,
+ msm_dp_panel->msm_dp_mode.bpp);
colorimetry_cfg = msm_dp_link_get_colorimetry_config(ctrl->link);
misc_val = msm_dp_read_link(ctrl, REG_DP_MISC1_MISC0);
@@ -482,6 +478,17 @@ static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl
drm_dbg_dp(ctrl->drm_dev, "misc settings = 0x%x\n", misc_val);
msm_dp_write_link(ctrl, REG_DP_MISC1_MISC0, misc_val);
+}
+
+static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl)
+{
+ msm_dp_ctrl_lane_mapping(ctrl);
+ msm_dp_setup_peripheral_flush(ctrl);
+
+ msm_dp_ctrl_config_ctrl_link(ctrl);
+ msm_dp_ctrl_config_ctrl_streams(ctrl, ctrl->panel);
+
+ msm_dp_ctrl_config_misc1_misc0(ctrl, ctrl->panel);
msm_dp_panel_timing_cfg(ctrl->panel, ctrl->msm_dp_ctrl.wide_bus_en);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 06/15] drm/msm/dp: split link setup from source params
2026-06-09 9:46 [PATCH v7 00/15] drm/msm/dp: Prerequisite cleanup for upcoming MST support Yongxing Mou
` (4 preceding siblings ...)
2026-06-09 9:46 ` [PATCH v7 05/15] drm/msm/dp: extract MISC1_MISC0 configuration into a separate function Yongxing Mou
@ 2026-06-09 9:46 ` Yongxing Mou
2026-06-09 10:05 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 07/15] drm/msm/dp: move the pixel clock control to its own API Yongxing Mou
` (8 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Yongxing Mou @ 2026-06-09 9:46 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Yongxing Mou,
Dmitry Baryshkov
msm_dp_ctrl_configure_source_params() should only handle stream-related
configuration. Move the link setup out of it so MST can program link and
stream settings separately.
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 71d45b2c4daf..1c2eccec6ec6 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -482,10 +482,6 @@ static void msm_dp_ctrl_config_misc1_misc0(struct msm_dp_ctrl_private *ctrl,
static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl)
{
- msm_dp_ctrl_lane_mapping(ctrl);
- msm_dp_setup_peripheral_flush(ctrl);
-
- msm_dp_ctrl_config_ctrl_link(ctrl);
msm_dp_ctrl_config_ctrl_streams(ctrl, ctrl->panel);
msm_dp_ctrl_config_misc1_misc0(ctrl, ctrl->panel);
@@ -2551,6 +2547,10 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train
*/
reinit_completion(&ctrl->video_comp);
+ msm_dp_ctrl_lane_mapping(ctrl);
+ msm_dp_setup_peripheral_flush(ctrl);
+ msm_dp_ctrl_config_ctrl_link(ctrl);
+
msm_dp_ctrl_configure_source_params(ctrl);
msm_dp_ctrl_config_msa(ctrl,
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 07/15] drm/msm/dp: move the pixel clock control to its own API
2026-06-09 9:46 [PATCH v7 00/15] drm/msm/dp: Prerequisite cleanup for upcoming MST support Yongxing Mou
` (5 preceding siblings ...)
2026-06-09 9:46 ` [PATCH v7 06/15] drm/msm/dp: split link setup from source params Yongxing Mou
@ 2026-06-09 9:46 ` Yongxing Mou
2026-06-09 10:04 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 08/15] drm/msm/dp: break up dp_display_enable into two parts Yongxing Mou
` (7 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Yongxing Mou @ 2026-06-09 9:46 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Yongxing Mou,
Abhinav Kumar, Dmitry Baryshkov
Enable/Disable of DP pixel clock happens in multiple code paths
leading to code duplication. Move it into individual helpers so that
the helpers can be called wherever necessary.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 80 ++++++++++++++++++++--------------------
1 file changed, 41 insertions(+), 39 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 1c2eccec6ec6..a2c44088e6a6 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -2171,6 +2171,41 @@ static bool msm_dp_ctrl_send_phy_test_pattern(struct msm_dp_ctrl_private *ctrl)
return success;
}
+static int msm_dp_ctrl_on_pixel_clk(struct msm_dp_ctrl_private *ctrl, unsigned long pixel_rate)
+{
+ int ret;
+
+ ret = clk_set_rate(ctrl->pixel_clk, pixel_rate * 1000);
+ if (ret) {
+ DRM_ERROR("Failed to set pixel clock rate. ret=%d\n", ret);
+ return ret;
+ }
+
+ if (WARN_ON_ONCE(ctrl->stream_clks_on))
+ return 0;
+
+ ret = clk_prepare_enable(ctrl->pixel_clk);
+ if (ret) {
+ DRM_ERROR("Failed to start pixel clocks. ret=%d\n", ret);
+ return ret;
+ }
+ ctrl->stream_clks_on = true;
+
+ return ret;
+}
+
+static void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl)
+{
+ struct msm_dp_ctrl_private *ctrl;
+
+ ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
+
+ if (ctrl->stream_clks_on) {
+ clk_disable_unprepare(ctrl->pixel_clk);
+ ctrl->stream_clks_on = false;
+ }
+}
+
static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl)
{
int ret;
@@ -2196,22 +2231,7 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
}
pixel_rate = ctrl->panel->msm_dp_mode.drm_mode.clock;
- ret = clk_set_rate(ctrl->pixel_clk, pixel_rate * 1000);
- if (ret) {
- DRM_ERROR("Failed to set pixel clock rate. ret=%d\n", ret);
- return ret;
- }
-
- if (ctrl->stream_clks_on) {
- drm_dbg_dp(ctrl->drm_dev, "pixel clks already enabled\n");
- } else {
- ret = clk_prepare_enable(ctrl->pixel_clk);
- if (ret) {
- DRM_ERROR("Failed to start pixel clocks. ret=%d\n", ret);
- return ret;
- }
- ctrl->stream_clks_on = true;
- }
+ ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate);
msm_dp_ctrl_send_phy_test_pattern(ctrl);
@@ -2514,26 +2534,13 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train
ret = msm_dp_ctrl_enable_mainlink_clocks(ctrl);
if (ret) {
DRM_ERROR("Failed to start link clocks. ret=%d\n", ret);
- goto end;
+ return ret;
}
}
- ret = clk_set_rate(ctrl->pixel_clk, pixel_rate * 1000);
- if (ret) {
- DRM_ERROR("Failed to set pixel clock rate. ret=%d\n", ret);
- goto end;
- }
-
- if (ctrl->stream_clks_on) {
- drm_dbg_dp(ctrl->drm_dev, "pixel clks already enabled\n");
- } else {
- ret = clk_prepare_enable(ctrl->pixel_clk);
- if (ret) {
- DRM_ERROR("Failed to start pixel clocks. ret=%d\n", ret);
- goto end;
- }
- ctrl->stream_clks_on = true;
- }
+ ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate);
+ if (ret)
+ return ret;
if (force_link_train || !msm_dp_ctrl_channel_eq_ok(ctrl))
msm_dp_ctrl_link_retrain(ctrl);
@@ -2572,7 +2579,6 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train
drm_dbg_dp(ctrl->drm_dev,
"mainlink %s\n", mainlink_ready ? "READY" : "NOT READY");
-end:
return ret;
}
@@ -2620,11 +2626,7 @@ void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl)
msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl);
- if (ctrl->stream_clks_on) {
- clk_disable_unprepare(ctrl->pixel_clk);
- ctrl->stream_clks_on = false;
- }
-
+ msm_dp_ctrl_off_pixel_clk(msm_dp_ctrl);
dev_pm_opp_set_rate(ctrl->dev, 0);
msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl);
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 08/15] drm/msm/dp: break up dp_display_enable into two parts
2026-06-09 9:46 [PATCH v7 00/15] drm/msm/dp: Prerequisite cleanup for upcoming MST support Yongxing Mou
` (6 preceding siblings ...)
2026-06-09 9:46 ` [PATCH v7 07/15] drm/msm/dp: move the pixel clock control to its own API Yongxing Mou
@ 2026-06-09 9:46 ` Yongxing Mou
2026-06-09 9:59 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 09/15] drm/msm/dp: re-arrange dp_display_disable() into functional parts Yongxing Mou
` (6 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Yongxing Mou @ 2026-06-09 9:46 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Yongxing Mou,
Abhinav Kumar, Dmitry Baryshkov
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
dp_display_enable() currently re-trains the link if needed and then
enables the pixel clock, programs the controller to start sending the
pixel stream. Split these two parts into prepare/enable APIs, to support
MST bridges_enable insert the MST payloads funcs between enable
stream_clks and program register.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 48 +++++++++++------
drivers/gpu/drm/msm/dp/dp_ctrl.h | 3 +-
drivers/gpu/drm/msm/dp/dp_display.c | 105 +++++++++++++++++++++++-------------
3 files changed, 102 insertions(+), 54 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index a2c44088e6a6..546d899dde23 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -2504,27 +2504,19 @@ static void msm_dp_ctrl_config_msa(struct msm_dp_ctrl_private *ctrl,
msm_dp_write_link(ctrl, REG_DP_SOFTWARE_NVID, nvid);
}
-int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train)
+int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train)
{
int ret = 0;
- bool mainlink_ready = false;
struct msm_dp_ctrl_private *ctrl;
- unsigned long pixel_rate;
- unsigned long pixel_rate_orig;
if (!msm_dp_ctrl)
return -EINVAL;
ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
- pixel_rate = pixel_rate_orig = ctrl->panel->msm_dp_mode.drm_mode.clock;
-
- if (msm_dp_ctrl->wide_bus_en || ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420)
- pixel_rate >>= 1;
-
- drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d, pixel_rate=%lu\n",
- ctrl->link->link_params.rate,
- ctrl->link->link_params.num_lanes, pixel_rate);
+ drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d\n",
+ ctrl->link->link_params.rate,
+ ctrl->link->link_params.num_lanes);
drm_dbg_dp(ctrl->drm_dev,
"core_clk_on=%d link_clk_on=%d stream_clk_on=%d\n",
@@ -2538,16 +2530,40 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train
}
}
- ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate);
- if (ret)
- return ret;
-
if (force_link_train || !msm_dp_ctrl_channel_eq_ok(ctrl))
msm_dp_ctrl_link_retrain(ctrl);
/* stop txing train pattern to end link training */
msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX);
+ return ret;
+}
+
+int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl)
+{
+ int ret = 0;
+ bool mainlink_ready = false;
+ struct msm_dp_ctrl_private *ctrl;
+ unsigned long pixel_rate;
+ unsigned long pixel_rate_orig;
+
+ if (!msm_dp_ctrl)
+ return -EINVAL;
+
+ ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
+
+ pixel_rate_orig = ctrl->panel->msm_dp_mode.drm_mode.clock;
+ pixel_rate = pixel_rate_orig;
+
+ if (msm_dp_ctrl->wide_bus_en || ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420)
+ pixel_rate >>= 1;
+
+ drm_dbg_dp(ctrl->drm_dev, "pixel_rate=%lu\n", pixel_rate);
+
+ ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate);
+ if (ret)
+ return ret;
+
/*
* Set up transfer unit values and set controller state to send
* video.
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index f68bee62713f..1497f1a8fc2f 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -17,7 +17,8 @@ struct msm_dp_ctrl {
struct phy;
int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl);
-int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train);
+int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl);
+int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train);
void msm_dp_ctrl_off_link_stream(struct msm_dp_ctrl *msm_dp_ctrl);
void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl);
void msm_dp_ctrl_push_idle(struct msm_dp_ctrl *msm_dp_ctrl);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index f33c754b83c3..cf859f880943 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -617,7 +617,40 @@ static int msm_dp_display_set_mode(struct msm_dp *msm_dp_display,
return 0;
}
-static int msm_dp_display_enable(struct msm_dp_display_private *dp, bool force_link_train)
+static int msm_dp_display_prepare_link(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);
+
+ if (msm_dp_display->is_edp)
+ msm_dp_hpd_plug_handle(dp);
+
+ rc = pm_runtime_resume_and_get(&msm_dp_display->pdev->dev);
+ if (rc) {
+ DRM_ERROR("failed to pm_runtime_resume\n");
+ return rc;
+ }
+
+ if (dp->link->sink_count == 0)
+ return rc;
+
+ if (!msm_dp_display->power_on) {
+ msm_dp_display_host_phy_init(dp);
+ force_link_train = true;
+ }
+
+ rc = msm_dp_ctrl_on_link(dp->ctrl);
+ if (rc)
+ DRM_ERROR("Failed link training (rc=%d)\n", rc);
+ // TODO: schedule drm_connector_set_link_status_property()
+
+ return msm_dp_ctrl_prepare_stream_on(dp->ctrl, force_link_train);
+}
+
+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;
@@ -628,7 +661,7 @@ static int msm_dp_display_enable(struct msm_dp_display_private *dp, bool force_l
return 0;
}
- rc = msm_dp_ctrl_on_stream(dp->ctrl, force_link_train);
+ rc = msm_dp_ctrl_on_stream(dp->ctrl);
if (!rc)
msm_dp_display->power_on = true;
@@ -658,13 +691,10 @@ static int msm_dp_display_post_enable(struct msm_dp *msm_dp_display)
return 0;
}
-static int msm_dp_display_disable(struct msm_dp_display_private *dp)
+static void msm_dp_display_audio_notify_disable(struct msm_dp_display_private *dp)
{
struct msm_dp *msm_dp_display = &dp->msm_dp_display;
- if (!msm_dp_display->power_on)
- return 0;
-
/* wait only if audio was enabled */
if (msm_dp_display->audio_enabled) {
/* signal the disconnect event */
@@ -675,6 +705,14 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
}
msm_dp_display->audio_enabled = false;
+}
+
+static int msm_dp_display_disable(struct msm_dp_display_private *dp)
+{
+ struct msm_dp *msm_dp_display = &dp->msm_dp_display;
+
+ if (!msm_dp_display->power_on)
+ return 0;
if (dp->link->sink_count == 0) {
/*
@@ -1371,14 +1409,13 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
struct drm_atomic_commit *state)
{
struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge);
- struct msm_dp *dp = msm_dp_bridge->msm_dp_display;
+ struct msm_dp *msm_dp_display = msm_dp_bridge->msm_dp_display;
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
int rc = 0;
- struct msm_dp_display_private *msm_dp_display;
- bool force_link_train = false;
+ struct msm_dp_display_private *dp;
- msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
+ dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
crtc = drm_atomic_get_new_crtc_for_encoder(state,
drm_bridge->encoder);
@@ -1386,44 +1423,29 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
return;
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
- if (dp->is_edp)
- msm_dp_hpd_plug_handle(msm_dp_display);
-
- if (pm_runtime_resume_and_get(&dp->pdev->dev)) {
- DRM_ERROR("failed to pm_runtime_resume\n");
- return;
- }
-
- if (msm_dp_display->link->sink_count == 0)
- return;
-
- rc = msm_dp_display_set_mode(dp, &crtc_state->adjusted_mode, msm_dp_display->panel);
+ rc = msm_dp_display_set_mode(msm_dp_display, &crtc_state->adjusted_mode, dp->panel);
if (rc) {
DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc);
return;
}
- if (!dp->power_on) {
- msm_dp_display_host_phy_init(msm_dp_display);
- force_link_train = true;
- }
-
- rc = msm_dp_ctrl_on_link(msm_dp_display->ctrl);
+ rc = msm_dp_display_prepare_link(dp);
if (rc) {
- DRM_ERROR("Failed link training (rc=%d)\n", rc);
- // TODO: schedule drm_connector_set_link_status_property()
+ DRM_ERROR("DP display prepare failed, rc=%d\n", rc);
return;
}
- msm_dp_display_enable(msm_dp_display, force_link_train);
+ rc = msm_dp_display_enable(dp);
+ if (rc)
+ DRM_ERROR("DP display enable failed, rc=%d\n", rc);
- rc = msm_dp_display_post_enable(dp);
+ rc = msm_dp_display_post_enable(msm_dp_display);
if (rc) {
DRM_ERROR("DP display post enable failed, rc=%d\n", rc);
- msm_dp_display_disable(msm_dp_display);
+ msm_dp_display_disable(dp);
}
- drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type);
+ drm_dbg_dp(msm_dp_display->drm_dev, "type=%d Done\n", msm_dp_display->connector_type);
}
void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge,
@@ -1438,6 +1460,15 @@ void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge,
msm_dp_ctrl_push_idle(msm_dp_display->ctrl);
}
+static void msm_dp_display_unprepare(struct msm_dp_display_private *dp)
+{
+ struct msm_dp *msm_dp_display = &dp->msm_dp_display;
+
+ pm_runtime_put_sync(&msm_dp_display->pdev->dev);
+
+ drm_dbg_dp(dp->drm_dev, "type=%d Done\n", msm_dp_display->connector_type);
+}
+
void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
struct drm_atomic_commit *state)
{
@@ -1450,11 +1481,11 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
if (dp->is_edp)
msm_dp_hpd_unplug_handle(msm_dp_display);
- msm_dp_display_disable(msm_dp_display);
+ msm_dp_display_audio_notify_disable(msm_dp_display);
- drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type);
+ msm_dp_display_disable(msm_dp_display);
- pm_runtime_put_sync(&dp->pdev->dev);
+ msm_dp_display_unprepare(msm_dp_display);
}
void msm_dp_bridge_hpd_enable(struct drm_bridge *bridge)
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 09/15] drm/msm/dp: re-arrange dp_display_disable() into functional parts
2026-06-09 9:46 [PATCH v7 00/15] drm/msm/dp: Prerequisite cleanup for upcoming MST support Yongxing Mou
` (7 preceding siblings ...)
2026-06-09 9:46 ` [PATCH v7 08/15] drm/msm/dp: break up dp_display_enable into two parts Yongxing Mou
@ 2026-06-09 9:46 ` Yongxing Mou
2026-06-09 10:17 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 10/15] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it Yongxing Mou
` (5 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Yongxing Mou @ 2026-06-09 9:46 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Yongxing Mou,
Abhinav Kumar, Dmitry Baryshkov
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
dp_display_disable() handles special case of when monitor is
disconnected from the dongle while the dongle stays connected
thereby needing a separate function dp_ctrl_off_link_stream()
for this. However with a slight rework this can still be handled
by keeping common paths same for regular and special case.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 19 +------------------
drivers/gpu/drm/msm/dp/dp_ctrl.h | 2 +-
drivers/gpu/drm/msm/dp/dp_display.c | 10 +++++++++-
3 files changed, 11 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 546d899dde23..626b95d4d015 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -2598,7 +2598,7 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl)
return ret;
}
-void msm_dp_ctrl_off_link_stream(struct msm_dp_ctrl *msm_dp_ctrl)
+void msm_dp_ctrl_reinit_phy(struct msm_dp_ctrl *msm_dp_ctrl)
{
struct msm_dp_ctrl_private *ctrl;
struct phy *phy;
@@ -2606,23 +2606,6 @@ void msm_dp_ctrl_off_link_stream(struct msm_dp_ctrl *msm_dp_ctrl)
ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
phy = ctrl->phy;
- msm_dp_panel_disable_vsc_sdp(ctrl->panel);
-
- /* set dongle to D3 (power off) mode */
- msm_dp_link_psm_config(ctrl->link, &ctrl->panel->link_info, true);
-
- msm_dp_ctrl_mainlink_disable(ctrl);
-
- if (ctrl->stream_clks_on) {
- clk_disable_unprepare(ctrl->pixel_clk);
- ctrl->stream_clks_on = false;
- }
-
- dev_pm_opp_set_rate(ctrl->dev, 0);
- msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl);
-
- phy_power_off(phy);
-
/* aux channel down, reinit phy */
phy_exit(phy);
phy_init(phy);
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index 1497f1a8fc2f..5d615f50d13b 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -19,7 +19,6 @@ struct phy;
int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl);
int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl);
int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train);
-void msm_dp_ctrl_off_link_stream(struct msm_dp_ctrl *msm_dp_ctrl);
void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl);
void msm_dp_ctrl_push_idle(struct msm_dp_ctrl *msm_dp_ctrl);
irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl);
@@ -46,4 +45,5 @@ void msm_dp_ctrl_core_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl);
void msm_dp_ctrl_enable_irq(struct msm_dp_ctrl *msm_dp_ctrl);
void msm_dp_ctrl_disable_irq(struct msm_dp_ctrl *msm_dp_ctrl);
+void msm_dp_ctrl_reinit_phy(struct msm_dp_ctrl *msm_dp_ctrl);
#endif /* _DP_CTRL_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index cf859f880943..b8dab3f8a7c2 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -714,12 +714,20 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
if (!msm_dp_display->power_on)
return 0;
+ msm_dp_panel_disable_vsc_sdp(dp->panel);
+
+ /* dongle is still connected but sinks are disconnected */
if (dp->link->sink_count == 0) {
/*
* irq_hpd with sink_count = 0
* hdmi unplugged out of dongle
*/
- msm_dp_ctrl_off_link_stream(dp->ctrl);
+
+ /* set dongle to D3 (power off) mode */
+ msm_dp_link_psm_config(dp->link, &dp->panel->link_info, true);
+ msm_dp_ctrl_off(dp->ctrl);
+ /* re-init the PHY so that we can listen to Dongle disconnect */
+ msm_dp_ctrl_reinit_phy(dp->ctrl);
} else {
/*
* unplugged interrupt
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 10/15] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it
2026-06-09 9:46 [PATCH v7 00/15] drm/msm/dp: Prerequisite cleanup for upcoming MST support Yongxing Mou
` (8 preceding siblings ...)
2026-06-09 9:46 ` [PATCH v7 09/15] drm/msm/dp: re-arrange dp_display_disable() into functional parts Yongxing Mou
@ 2026-06-09 9:46 ` Yongxing Mou
2026-06-09 9:46 ` [PATCH v7 11/15] drm/msm/dp: split dp_ctrl_off() into stream and link parts Yongxing Mou
` (4 subsequent siblings)
14 siblings, 0 replies; 28+ messages in thread
From: Yongxing Mou @ 2026-06-09 9:46 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Yongxing Mou,
Abhinav Kumar, Dmitry Baryshkov
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
With MST, multiple sinks share a single DP controller, so a cached
panel in msm_dp_ctrl_private can no longer represent the per-stream
sink. Drop the cache and pass panel explicitly to all stream-related
dp_ctrl APIs.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 196 ++++++++++++++++++++----------------
drivers/gpu/drm/msm/dp/dp_ctrl.h | 28 ++++--
drivers/gpu/drm/msm/dp/dp_display.c | 24 ++---
3 files changed, 140 insertions(+), 108 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 626b95d4d015..87c3a5517911 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -114,7 +114,6 @@ struct msm_dp_ctrl_private {
struct drm_device *drm_dev;
struct device *dev;
struct drm_dp_aux *aux;
- struct msm_dp_panel *panel;
struct msm_dp_link *link;
void __iomem *ahb_base;
void __iomem *link_base;
@@ -202,7 +201,8 @@ static int msm_dp_aux_link_configure(struct drm_dp_aux *aux,
/*
* NOTE: resetting DP controller will also clear any pending HPD related interrupts
*/
-void msm_dp_ctrl_reset(struct msm_dp_ctrl *msm_dp_ctrl)
+void msm_dp_ctrl_reset(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel)
{
struct msm_dp_ctrl_private *ctrl =
container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
@@ -219,7 +219,7 @@ void msm_dp_ctrl_reset(struct msm_dp_ctrl *msm_dp_ctrl)
if (!ctrl->hw_revision) {
ctrl->hw_revision = msm_dp_read_ahb(ctrl, REG_DP_HW_VERSION);
- ctrl->panel->hw_revision = ctrl->hw_revision;
+ panel->hw_revision = ctrl->hw_revision;
}
}
@@ -414,10 +414,11 @@ static void msm_dp_ctrl_config_ctrl_streams(struct msm_dp_ctrl_private *ctrl,
msm_dp_write_link(ctrl, REG_DP_CONFIGURATION_CTRL, config);
}
-static void msm_dp_ctrl_config_ctrl_link(struct msm_dp_ctrl_private *ctrl)
+static void msm_dp_ctrl_config_ctrl_link(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel)
{
u32 config = 0;
- const u8 *dpcd = ctrl->panel->dpcd;
+ const u8 *dpcd = panel->dpcd;
/* Default-> LSCLK DIV: 1/4 LCLK */
config |= (2 << DP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT);
@@ -480,13 +481,14 @@ static void msm_dp_ctrl_config_misc1_misc0(struct msm_dp_ctrl_private *ctrl,
msm_dp_write_link(ctrl, REG_DP_MISC1_MISC0, misc_val);
}
-static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl)
+static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel)
{
- msm_dp_ctrl_config_ctrl_streams(ctrl, ctrl->panel);
+ msm_dp_ctrl_config_ctrl_streams(ctrl, panel);
- msm_dp_ctrl_config_misc1_misc0(ctrl, ctrl->panel);
+ msm_dp_ctrl_config_misc1_misc0(ctrl, panel);
- msm_dp_panel_timing_cfg(ctrl->panel, ctrl->msm_dp_ctrl.wide_bus_en);
+ msm_dp_panel_timing_cfg(panel, ctrl->msm_dp_ctrl.wide_bus_en);
}
/*
@@ -1256,20 +1258,21 @@ static void _dp_ctrl_calc_tu(struct msm_dp_ctrl_private *ctrl,
}
static void msm_dp_ctrl_calc_tu_parameters(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel,
struct msm_dp_vc_tu_mapping_table *tu_table)
{
struct msm_dp_tu_calc_input in;
- struct drm_display_mode *drm_mode;
+ const struct drm_display_mode *drm_mode;
- drm_mode = &ctrl->panel->msm_dp_mode.drm_mode;
+ drm_mode = &panel->msm_dp_mode.drm_mode;
in.lclk = ctrl->link->link_params.rate / 1000;
in.pclk_khz = drm_mode->clock;
in.hactive = drm_mode->hdisplay;
in.hporch = drm_mode->htotal - drm_mode->hdisplay;
in.nlanes = ctrl->link->link_params.num_lanes;
- in.bpp = ctrl->panel->msm_dp_mode.bpp;
- in.pixel_enc = ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420 ? 420 : 444;
+ in.bpp = panel->msm_dp_mode.bpp;
+ in.pixel_enc = panel->msm_dp_mode.out_fmt_is_yuv_420 ? 420 : 444;
in.dsc_en = 0;
in.async_en = 0;
in.fec_en = 0;
@@ -1279,14 +1282,15 @@ static void msm_dp_ctrl_calc_tu_parameters(struct msm_dp_ctrl_private *ctrl,
_dp_ctrl_calc_tu(ctrl, &in, tu_table);
}
-static void msm_dp_ctrl_setup_tr_unit(struct msm_dp_ctrl_private *ctrl)
+static void msm_dp_ctrl_setup_tr_unit(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel)
{
u32 msm_dp_tu = 0x0;
u32 valid_boundary = 0x0;
u32 valid_boundary2 = 0x0;
struct msm_dp_vc_tu_mapping_table tu_calc_table;
- msm_dp_ctrl_calc_tu_parameters(ctrl, &tu_calc_table);
+ msm_dp_ctrl_calc_tu_parameters(ctrl, panel, &tu_calc_table);
msm_dp_tu |= tu_calc_table.tu_size_minus1;
valid_boundary |= tu_calc_table.valid_boundary_link;
@@ -1438,6 +1442,7 @@ static int msm_dp_ctrl_set_pattern_state_bit(struct msm_dp_ctrl_private *ctrl,
}
static int msm_dp_ctrl_link_train_1(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel,
int *training_step, enum drm_dp_phy dp_phy)
{
int delay_us;
@@ -1446,7 +1451,7 @@ static int msm_dp_ctrl_link_train_1(struct msm_dp_ctrl_private *ctrl,
int const maximum_retries = 4;
delay_us = drm_dp_read_clock_recovery_delay(ctrl->aux,
- ctrl->panel->dpcd, dp_phy, false);
+ panel->dpcd, dp_phy, false);
msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, 0);
@@ -1532,14 +1537,15 @@ static int msm_dp_ctrl_link_rate_down_shift(struct msm_dp_ctrl_private *ctrl)
return ret;
}
-static int msm_dp_ctrl_link_lane_down_shift(struct msm_dp_ctrl_private *ctrl)
+static int msm_dp_ctrl_link_lane_down_shift(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel)
{
if (ctrl->link->link_params.num_lanes == 1)
return -1;
ctrl->link->link_params.num_lanes /= 2;
- ctrl->link->link_params.rate = ctrl->panel->link_info.rate;
+ ctrl->link->link_params.rate = panel->link_info.rate;
ctrl->link->phy_params.p_level = 0;
ctrl->link->phy_params.v_level = 0;
@@ -1548,6 +1554,7 @@ static int msm_dp_ctrl_link_lane_down_shift(struct msm_dp_ctrl_private *ctrl)
}
static void msm_dp_ctrl_clear_training_pattern(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel,
enum drm_dp_phy dp_phy)
{
int delay_us;
@@ -1555,11 +1562,12 @@ static void msm_dp_ctrl_clear_training_pattern(struct msm_dp_ctrl_private *ctrl,
msm_dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_DISABLE, dp_phy);
delay_us = drm_dp_read_channel_eq_delay(ctrl->aux,
- ctrl->panel->dpcd, dp_phy, false);
+ panel->dpcd, dp_phy, false);
fsleep(delay_us);
}
static int msm_dp_ctrl_link_train_2(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel,
int *training_step, enum drm_dp_phy dp_phy)
{
int delay_us;
@@ -1570,16 +1578,16 @@ static int msm_dp_ctrl_link_train_2(struct msm_dp_ctrl_private *ctrl,
u8 link_status[DP_LINK_STATUS_SIZE];
delay_us = drm_dp_read_channel_eq_delay(ctrl->aux,
- ctrl->panel->dpcd, dp_phy, false);
+ panel->dpcd, dp_phy, false);
msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, 0);
*training_step = DP_TRAINING_2;
- if (drm_dp_tps4_supported(ctrl->panel->dpcd)) {
+ if (drm_dp_tps4_supported(panel->dpcd)) {
pattern = DP_TRAINING_PATTERN_4;
state_ctrl_bit = 4;
- } else if (drm_dp_tps3_supported(ctrl->panel->dpcd)) {
+ } else if (drm_dp_tps3_supported(panel->dpcd)) {
pattern = DP_TRAINING_PATTERN_3;
state_ctrl_bit = 3;
} else {
@@ -1616,18 +1624,19 @@ static int msm_dp_ctrl_link_train_2(struct msm_dp_ctrl_private *ctrl,
}
static int msm_dp_ctrl_link_train_1_2(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel,
int *training_step, enum drm_dp_phy dp_phy)
{
int ret;
- ret = msm_dp_ctrl_link_train_1(ctrl, training_step, dp_phy);
+ ret = msm_dp_ctrl_link_train_1(ctrl, panel, training_step, dp_phy);
if (ret) {
DRM_ERROR("link training #1 on phy %d failed. ret=%d\n", dp_phy, ret);
return ret;
}
drm_dbg_dp(ctrl->drm_dev, "link training #1 on phy %d successful\n", dp_phy);
- ret = msm_dp_ctrl_link_train_2(ctrl, training_step, dp_phy);
+ ret = msm_dp_ctrl_link_train_2(ctrl, panel, training_step, dp_phy);
if (ret) {
DRM_ERROR("link training #2 on phy %d failed. ret=%d\n", dp_phy, ret);
return ret;
@@ -1638,16 +1647,17 @@ static int msm_dp_ctrl_link_train_1_2(struct msm_dp_ctrl_private *ctrl,
}
static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel,
int *training_step)
{
int i;
int ret = 0;
- const u8 *dpcd = ctrl->panel->dpcd;
+ const u8 *dpcd = panel->dpcd;
u8 encoding[] = { 0, DP_SET_ANSI_8B10B };
u8 assr;
struct msm_dp_link_info link_info = {0};
- msm_dp_ctrl_config_ctrl_link(ctrl);
+ msm_dp_ctrl_config_ctrl_link(ctrl, panel);
link_info.num_lanes = ctrl->link->link_params.num_lanes;
link_info.rate = ctrl->link->link_params.rate;
@@ -1670,8 +1680,8 @@ static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl,
for (i = ctrl->link->lttpr_count - 1; i >= 0; i--) {
enum drm_dp_phy dp_phy = DP_PHY_LTTPR(i);
- ret = msm_dp_ctrl_link_train_1_2(ctrl, training_step, dp_phy);
- msm_dp_ctrl_clear_training_pattern(ctrl, dp_phy);
+ ret = msm_dp_ctrl_link_train_1_2(ctrl, panel, training_step, dp_phy);
+ msm_dp_ctrl_clear_training_pattern(ctrl, panel, dp_phy);
if (ret)
break;
@@ -1682,7 +1692,7 @@ static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl,
goto end;
}
- ret = msm_dp_ctrl_link_train_1_2(ctrl, training_step, DP_PHY_DPRX);
+ ret = msm_dp_ctrl_link_train_1_2(ctrl, panel, training_step, DP_PHY_DPRX);
if (ret) {
DRM_ERROR("link training on sink failed. ret=%d\n", ret);
goto end;
@@ -1695,6 +1705,7 @@ static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl,
}
static int msm_dp_ctrl_setup_main_link(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel,
int *training_step)
{
int ret = 0;
@@ -1710,7 +1721,7 @@ static int msm_dp_ctrl_setup_main_link(struct msm_dp_ctrl_private *ctrl,
* a link training pattern, we have to first do soft reset.
*/
- ret = msm_dp_ctrl_link_train(ctrl, training_step);
+ ret = msm_dp_ctrl_link_train(ctrl, panel, training_step);
return ret;
}
@@ -1809,11 +1820,12 @@ static void msm_dp_ctrl_link_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl)
str_on_off(ctrl->core_clks_on));
}
-static int msm_dp_ctrl_enable_mainlink_clocks(struct msm_dp_ctrl_private *ctrl)
+static int msm_dp_ctrl_enable_mainlink_clocks(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel)
{
int ret = 0;
struct phy *phy = ctrl->phy;
- const u8 *dpcd = ctrl->panel->dpcd;
+ const u8 *dpcd = panel->dpcd;
ctrl->phy_opts.dp.lanes = ctrl->link->link_params.num_lanes;
ctrl->phy_opts.dp.link_rate = ctrl->link->link_params.rate / 100;
@@ -1865,13 +1877,14 @@ static void msm_dp_ctrl_psr_exit(struct msm_dp_ctrl_private *ctrl)
msm_dp_write_link(ctrl, REG_PSR_CMD, cmd);
}
-void msm_dp_ctrl_config_psr(struct msm_dp_ctrl *msm_dp_ctrl)
+void msm_dp_ctrl_config_psr(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel)
{
struct msm_dp_ctrl_private *ctrl = container_of(msm_dp_ctrl,
struct msm_dp_ctrl_private, msm_dp_ctrl);
u32 cfg;
- if (!ctrl->panel->psr_cap.version)
+ if (!panel->psr_cap.version)
return;
/* enable PSR1 function */
@@ -1886,12 +1899,13 @@ void msm_dp_ctrl_config_psr(struct msm_dp_ctrl *msm_dp_ctrl)
drm_dp_dpcd_write(ctrl->aux, DP_PSR_EN_CFG, &cfg, 1);
}
-void msm_dp_ctrl_set_psr(struct msm_dp_ctrl *msm_dp_ctrl, bool enter)
+void msm_dp_ctrl_set_psr(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel, bool enter)
{
struct msm_dp_ctrl_private *ctrl = container_of(msm_dp_ctrl,
struct msm_dp_ctrl_private, msm_dp_ctrl);
- if (!ctrl->panel->psr_cap.version)
+ if (!panel->psr_cap.version)
return;
/*
@@ -1961,7 +1975,8 @@ void msm_dp_ctrl_phy_exit(struct msm_dp_ctrl *msm_dp_ctrl)
phy_exit(phy);
}
-static int msm_dp_ctrl_reinitialize_mainlink(struct msm_dp_ctrl_private *ctrl)
+static int msm_dp_ctrl_reinitialize_mainlink(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel)
{
struct phy *phy = ctrl->phy;
int ret = 0;
@@ -1982,7 +1997,7 @@ static int msm_dp_ctrl_reinitialize_mainlink(struct msm_dp_ctrl_private *ctrl)
/* hw recommended delay before re-enabling clocks */
msleep(20);
- ret = msm_dp_ctrl_enable_mainlink_clocks(ctrl);
+ ret = msm_dp_ctrl_enable_mainlink_clocks(ctrl, panel);
if (ret) {
DRM_ERROR("Failed to enable mainlink clks. ret=%d\n", ret);
return ret;
@@ -1991,7 +2006,8 @@ static int msm_dp_ctrl_reinitialize_mainlink(struct msm_dp_ctrl_private *ctrl)
return ret;
}
-static int msm_dp_ctrl_deinitialize_mainlink(struct msm_dp_ctrl_private *ctrl)
+static int msm_dp_ctrl_deinitialize_mainlink(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel)
{
struct phy *phy;
@@ -1999,7 +2015,7 @@ static int msm_dp_ctrl_deinitialize_mainlink(struct msm_dp_ctrl_private *ctrl)
msm_dp_ctrl_mainlink_disable(ctrl);
- msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl);
+ msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl, panel);
dev_pm_opp_set_rate(ctrl->dev, 0);
msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl);
@@ -2013,7 +2029,8 @@ static int msm_dp_ctrl_deinitialize_mainlink(struct msm_dp_ctrl_private *ctrl)
return 0;
}
-static int msm_dp_ctrl_link_maintenance(struct msm_dp_ctrl_private *ctrl)
+static int msm_dp_ctrl_link_maintenance(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel)
{
int ret = 0;
int training_step = DP_TRAINING_NONE;
@@ -2023,11 +2040,11 @@ static int msm_dp_ctrl_link_maintenance(struct msm_dp_ctrl_private *ctrl)
ctrl->link->phy_params.p_level = 0;
ctrl->link->phy_params.v_level = 0;
- ret = msm_dp_ctrl_setup_main_link(ctrl, &training_step);
+ ret = msm_dp_ctrl_setup_main_link(ctrl, panel, &training_step);
if (ret)
goto end;
- msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX);
+ msm_dp_ctrl_clear_training_pattern(ctrl, panel, DP_PHY_DPRX);
msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, DP_STATE_CTRL_SEND_VIDEO);
@@ -2206,7 +2223,8 @@ static void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl)
}
}
-static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl)
+static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel)
{
int ret;
unsigned long pixel_rate;
@@ -2222,15 +2240,15 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
* running. Add the global reset just before disabling the
* link clocks and core clocks.
*/
- msm_dp_ctrl_off(&ctrl->msm_dp_ctrl);
+ msm_dp_ctrl_off(&ctrl->msm_dp_ctrl, panel);
- ret = msm_dp_ctrl_on_link(&ctrl->msm_dp_ctrl);
+ ret = msm_dp_ctrl_on_link(&ctrl->msm_dp_ctrl, panel);
if (ret) {
DRM_ERROR("failed to enable DP link controller\n");
return ret;
}
- pixel_rate = ctrl->panel->msm_dp_mode.drm_mode.clock;
+ pixel_rate = panel->msm_dp_mode.drm_mode.clock;
ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate);
msm_dp_ctrl_send_phy_test_pattern(ctrl);
@@ -2238,7 +2256,8 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
return 0;
}
-void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl)
+void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel)
{
struct msm_dp_ctrl_private *ctrl;
u32 sink_request = 0x0;
@@ -2253,14 +2272,14 @@ void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl)
if (sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
drm_dbg_dp(ctrl->drm_dev, "PHY_TEST_PATTERN request\n");
- if (msm_dp_ctrl_process_phy_test_request(ctrl)) {
+ if (msm_dp_ctrl_process_phy_test_request(ctrl, panel)) {
DRM_ERROR("process phy_test_req failed\n");
return;
}
}
if (sink_request & DP_LINK_STATUS_UPDATED) {
- if (msm_dp_ctrl_link_maintenance(ctrl)) {
+ if (msm_dp_ctrl_link_maintenance(ctrl, panel)) {
DRM_ERROR("LM failed: TEST_LINK_TRAINING\n");
return;
}
@@ -2268,7 +2287,7 @@ void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl)
if (sink_request & DP_TEST_LINK_TRAINING) {
msm_dp_link_send_test_response(ctrl->link);
- if (msm_dp_ctrl_link_maintenance(ctrl)) {
+ if (msm_dp_ctrl_link_maintenance(ctrl, panel)) {
DRM_ERROR("LM failed: TEST_LINK_TRAINING\n");
return;
}
@@ -2304,7 +2323,8 @@ static bool msm_dp_ctrl_channel_eq_ok(struct msm_dp_ctrl_private *ctrl)
return drm_dp_channel_eq_ok(link_status, num_lanes);
}
-int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl)
+int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel)
{
int rc = 0;
struct msm_dp_ctrl_private *ctrl;
@@ -2320,8 +2340,8 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl)
ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
- rate = ctrl->panel->link_info.rate;
- pixel_rate = ctrl->panel->msm_dp_mode.drm_mode.clock;
+ rate = panel->link_info.rate;
+ pixel_rate = panel->msm_dp_mode.drm_mode.clock;
msm_dp_ctrl_core_clk_enable(&ctrl->msm_dp_ctrl);
@@ -2333,8 +2353,8 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl)
} else {
ctrl->link->link_params.rate = rate;
ctrl->link->link_params.num_lanes =
- ctrl->panel->link_info.num_lanes;
- if (ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420)
+ panel->link_info.num_lanes;
+ if (panel->msm_dp_mode.out_fmt_is_yuv_420)
pixel_rate >>= 1;
}
@@ -2342,13 +2362,13 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl)
ctrl->link->link_params.rate, ctrl->link->link_params.num_lanes,
pixel_rate);
- rc = msm_dp_ctrl_enable_mainlink_clocks(ctrl);
+ rc = msm_dp_ctrl_enable_mainlink_clocks(ctrl, panel);
if (rc)
return rc;
while (--link_train_max_retries) {
training_step = DP_TRAINING_NONE;
- rc = msm_dp_ctrl_setup_main_link(ctrl, &training_step);
+ rc = msm_dp_ctrl_setup_main_link(ctrl, panel, &training_step);
if (rc == 0) {
/* training completed successfully */
break;
@@ -2367,7 +2387,7 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl)
* some lanes are ready,
* reduce lane number
*/
- rc = msm_dp_ctrl_link_lane_down_shift(ctrl);
+ rc = msm_dp_ctrl_link_lane_down_shift(ctrl, panel);
if (rc < 0) { /* lane == 1 already */
/* end with failure */
break;
@@ -2388,7 +2408,7 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl)
ctrl->link->link_params.num_lanes))
rc = msm_dp_ctrl_link_rate_down_shift(ctrl);
else
- rc = msm_dp_ctrl_link_lane_down_shift(ctrl);
+ rc = msm_dp_ctrl_link_lane_down_shift(ctrl, panel);
if (rc < 0) {
/* end with failure */
@@ -2396,10 +2416,10 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl)
}
/* stop link training before start re training */
- msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX);
+ msm_dp_ctrl_clear_training_pattern(ctrl, panel, DP_PHY_DPRX);
}
- rc = msm_dp_ctrl_reinitialize_mainlink(ctrl);
+ rc = msm_dp_ctrl_reinitialize_mainlink(ctrl, panel);
if (rc) {
DRM_ERROR("Failed to reinitialize mainlink. rc=%d\n", rc);
break;
@@ -2420,20 +2440,21 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl)
* link training failed
* end txing train pattern here
*/
- msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX);
+ msm_dp_ctrl_clear_training_pattern(ctrl, panel, DP_PHY_DPRX);
- msm_dp_ctrl_deinitialize_mainlink(ctrl);
+ msm_dp_ctrl_deinitialize_mainlink(ctrl, panel);
rc = -ECONNRESET;
}
return rc;
}
-static int msm_dp_ctrl_link_retrain(struct msm_dp_ctrl_private *ctrl)
+static int msm_dp_ctrl_link_retrain(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel)
{
int training_step = DP_TRAINING_NONE;
- return msm_dp_ctrl_setup_main_link(ctrl, &training_step);
+ return msm_dp_ctrl_setup_main_link(ctrl, panel, &training_step);
}
static void msm_dp_ctrl_config_msa(struct msm_dp_ctrl_private *ctrl,
@@ -2504,7 +2525,9 @@ static void msm_dp_ctrl_config_msa(struct msm_dp_ctrl_private *ctrl,
msm_dp_write_link(ctrl, REG_DP_SOFTWARE_NVID, nvid);
}
-int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train)
+int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel,
+ bool force_link_train)
{
int ret = 0;
struct msm_dp_ctrl_private *ctrl;
@@ -2523,7 +2546,7 @@ int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_li
ctrl->core_clks_on, ctrl->link_clks_on, ctrl->stream_clks_on);
if (!ctrl->link_clks_on) { /* link clk is off */
- ret = msm_dp_ctrl_enable_mainlink_clocks(ctrl);
+ ret = msm_dp_ctrl_enable_mainlink_clocks(ctrl, panel);
if (ret) {
DRM_ERROR("Failed to start link clocks. ret=%d\n", ret);
return ret;
@@ -2531,15 +2554,15 @@ int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_li
}
if (force_link_train || !msm_dp_ctrl_channel_eq_ok(ctrl))
- msm_dp_ctrl_link_retrain(ctrl);
+ msm_dp_ctrl_link_retrain(ctrl, panel);
/* stop txing train pattern to end link training */
- msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX);
+ msm_dp_ctrl_clear_training_pattern(ctrl, panel, DP_PHY_DPRX);
return ret;
}
-int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl)
+int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *panel)
{
int ret = 0;
bool mainlink_ready = false;
@@ -2552,10 +2575,10 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl)
ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
- pixel_rate_orig = ctrl->panel->msm_dp_mode.drm_mode.clock;
+ pixel_rate_orig = panel->msm_dp_mode.drm_mode.clock;
pixel_rate = pixel_rate_orig;
- if (msm_dp_ctrl->wide_bus_en || ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420)
+ if (msm_dp_ctrl->wide_bus_en || panel->msm_dp_mode.out_fmt_is_yuv_420)
pixel_rate >>= 1;
drm_dbg_dp(ctrl->drm_dev, "pixel_rate=%lu\n", pixel_rate);
@@ -2572,18 +2595,18 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl)
msm_dp_ctrl_lane_mapping(ctrl);
msm_dp_setup_peripheral_flush(ctrl);
- msm_dp_ctrl_config_ctrl_link(ctrl);
+ msm_dp_ctrl_config_ctrl_link(ctrl, panel);
- msm_dp_ctrl_configure_source_params(ctrl);
+ msm_dp_ctrl_configure_source_params(ctrl, panel);
msm_dp_ctrl_config_msa(ctrl,
ctrl->link->link_params.rate,
pixel_rate_orig,
- ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420);
+ panel->msm_dp_mode.out_fmt_is_yuv_420);
- msm_dp_panel_clear_dsc_dto(ctrl->panel);
+ msm_dp_panel_clear_dsc_dto(panel);
- msm_dp_ctrl_setup_tr_unit(ctrl);
+ msm_dp_ctrl_setup_tr_unit(ctrl, panel);
msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, DP_STATE_CTRL_SEND_VIDEO);
@@ -2611,7 +2634,8 @@ void msm_dp_ctrl_reinit_phy(struct msm_dp_ctrl *msm_dp_ctrl)
phy_init(phy);
}
-void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl)
+void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel)
{
struct msm_dp_ctrl_private *ctrl;
struct phy *phy;
@@ -2619,11 +2643,11 @@ void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl)
ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
phy = ctrl->phy;
- msm_dp_panel_disable_vsc_sdp(ctrl->panel);
+ msm_dp_panel_disable_vsc_sdp(panel);
msm_dp_ctrl_mainlink_disable(ctrl);
- msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl);
+ msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl, panel);
msm_dp_ctrl_off_pixel_clk(msm_dp_ctrl);
dev_pm_opp_set_rate(ctrl->dev, 0);
@@ -2632,7 +2656,8 @@ void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl)
phy_power_off(phy);
}
-irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl)
+irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel)
{
struct msm_dp_ctrl_private *ctrl;
u32 isr;
@@ -2643,7 +2668,7 @@ irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl)
ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
- if (ctrl->panel->psr_cap.version) {
+ if (panel->psr_cap.version) {
isr = msm_dp_ctrl_get_psr_interrupt(ctrl);
if (isr)
@@ -2732,7 +2757,7 @@ static int msm_dp_ctrl_clk_init(struct msm_dp_ctrl *msm_dp_ctrl)
}
struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link,
- struct msm_dp_panel *panel, struct drm_dp_aux *aux,
+ struct drm_dp_aux *aux,
struct phy *phy,
void __iomem *ahb_base,
void __iomem *link_base)
@@ -2740,7 +2765,7 @@ struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link
struct msm_dp_ctrl_private *ctrl;
int ret;
- if (!dev || !panel || !aux || !link) {
+ if (!dev || !aux || !link) {
DRM_ERROR("invalid input\n");
return ERR_PTR(-EINVAL);
}
@@ -2768,7 +2793,6 @@ struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link
init_completion(&ctrl->video_comp);
/* in parameters */
- ctrl->panel = panel;
ctrl->aux = aux;
ctrl->link = link;
ctrl->dev = dev;
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index 5d615f50d13b..00b430392a52 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -16,28 +16,36 @@ struct msm_dp_ctrl {
struct phy;
-int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl);
-int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl);
-int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train);
-void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl);
+int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel);
+int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *panel);
+int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel,
+ bool force_link_train);
+void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel);
void msm_dp_ctrl_push_idle(struct msm_dp_ctrl *msm_dp_ctrl);
-irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl);
-void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl);
+irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel);
+void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel);
struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev,
struct msm_dp_link *link,
- struct msm_dp_panel *panel,
struct drm_dp_aux *aux,
struct phy *phy,
void __iomem *ahb_base,
void __iomem *link_base);
-void msm_dp_ctrl_reset(struct msm_dp_ctrl *msm_dp_ctrl);
+void msm_dp_ctrl_reset(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel);
void msm_dp_ctrl_phy_init(struct msm_dp_ctrl *msm_dp_ctrl);
void msm_dp_ctrl_phy_exit(struct msm_dp_ctrl *msm_dp_ctrl);
void msm_dp_ctrl_irq_phy_exit(struct msm_dp_ctrl *msm_dp_ctrl);
-void msm_dp_ctrl_set_psr(struct msm_dp_ctrl *msm_dp_ctrl, bool enable);
-void msm_dp_ctrl_config_psr(struct msm_dp_ctrl *msm_dp_ctrl);
+void msm_dp_ctrl_set_psr(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel, bool enable);
+void msm_dp_ctrl_config_psr(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel);
int msm_dp_ctrl_core_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl);
void msm_dp_ctrl_core_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index b8dab3f8a7c2..230e14615a23 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -359,7 +359,7 @@ static void msm_dp_display_host_init(struct msm_dp_display_private *dp)
dp->phy_initialized);
msm_dp_ctrl_core_clk_enable(dp->ctrl);
- msm_dp_ctrl_reset(dp->ctrl);
+ msm_dp_ctrl_reset(dp->ctrl, dp->panel);
msm_dp_ctrl_enable_irq(dp->ctrl);
msm_dp_aux_init(dp->aux);
dp->core_initialized = true;
@@ -371,7 +371,7 @@ static void msm_dp_display_host_deinit(struct msm_dp_display_private *dp)
dp->msm_dp_display.connector_type, dp->core_initialized,
dp->phy_initialized);
- msm_dp_ctrl_reset(dp->ctrl);
+ msm_dp_ctrl_reset(dp->ctrl, dp->panel);
msm_dp_ctrl_disable_irq(dp->ctrl);
msm_dp_aux_deinit(dp->aux);
msm_dp_ctrl_core_clk_disable(dp->ctrl);
@@ -392,7 +392,7 @@ static int msm_dp_display_handle_irq_hpd(struct msm_dp_display_private *dp)
drm_dbg_dp(dp->drm_dev, "%d\n", sink_request);
- msm_dp_ctrl_handle_sink_request(dp->ctrl);
+ msm_dp_ctrl_handle_sink_request(dp->ctrl, dp->panel);
if (sink_request & DP_TEST_LINK_VIDEO_PATTERN)
msm_dp_display_handle_video_request(dp);
@@ -570,7 +570,7 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp)
goto error_link;
}
- dp->ctrl = msm_dp_ctrl_get(dev, dp->link, dp->panel, dp->aux,
+ dp->ctrl = msm_dp_ctrl_get(dev, dp->link, dp->aux,
phy, dp->ahb_base, dp->link_base);
if (IS_ERR(dp->ctrl)) {
rc = PTR_ERR(dp->ctrl);
@@ -642,12 +642,12 @@ static int msm_dp_display_prepare_link(struct msm_dp_display_private *dp)
force_link_train = true;
}
- rc = msm_dp_ctrl_on_link(dp->ctrl);
+ rc = msm_dp_ctrl_on_link(dp->ctrl, dp->panel);
if (rc)
DRM_ERROR("Failed link training (rc=%d)\n", rc);
// TODO: schedule drm_connector_set_link_status_property()
- return msm_dp_ctrl_prepare_stream_on(dp->ctrl, force_link_train);
+ return msm_dp_ctrl_prepare_stream_on(dp->ctrl, dp->panel, force_link_train);
}
static int msm_dp_display_enable(struct msm_dp_display_private *dp)
@@ -661,7 +661,7 @@ static int msm_dp_display_enable(struct msm_dp_display_private *dp)
return 0;
}
- rc = msm_dp_ctrl_on_stream(dp->ctrl);
+ rc = msm_dp_ctrl_on_stream(dp->ctrl, dp->panel);
if (!rc)
msm_dp_display->power_on = true;
@@ -686,7 +686,7 @@ static int msm_dp_display_post_enable(struct msm_dp *msm_dp_display)
msm_dp_display_handle_plugged_change(msm_dp_display, true);
if (msm_dp_display->psr_supported)
- msm_dp_ctrl_config_psr(dp->ctrl);
+ msm_dp_ctrl_config_psr(dp->ctrl, dp->panel);
return 0;
}
@@ -725,7 +725,7 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
/* set dongle to D3 (power off) mode */
msm_dp_link_psm_config(dp->link, &dp->panel->link_info, true);
- msm_dp_ctrl_off(dp->ctrl);
+ msm_dp_ctrl_off(dp->ctrl, dp->panel);
/* re-init the PHY so that we can listen to Dongle disconnect */
msm_dp_ctrl_reinit_phy(dp->ctrl);
} else {
@@ -733,7 +733,7 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
* unplugged interrupt
* dongle unplugged out of DUT
*/
- msm_dp_ctrl_off(dp->ctrl);
+ msm_dp_ctrl_off(dp->ctrl, dp->panel);
msm_dp_display_host_phy_exit(dp);
}
@@ -869,7 +869,7 @@ 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);
- msm_dp_ctrl_set_psr(dp->ctrl, enter);
+ msm_dp_ctrl_set_psr(dp->ctrl, dp->panel, enter);
}
/**
@@ -979,7 +979,7 @@ static irqreturn_t msm_dp_display_irq_handler(int irq, void *dev_id)
}
/* DP controller isr */
- ret |= msm_dp_ctrl_isr(dp->ctrl);
+ ret |= msm_dp_ctrl_isr(dp->ctrl, dp->panel);
return ret;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 11/15] drm/msm/dp: split dp_ctrl_off() into stream and link parts
2026-06-09 9:46 [PATCH v7 00/15] drm/msm/dp: Prerequisite cleanup for upcoming MST support Yongxing Mou
` (9 preceding siblings ...)
2026-06-09 9:46 ` [PATCH v7 10/15] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it Yongxing Mou
@ 2026-06-09 9:46 ` Yongxing Mou
2026-06-09 10:04 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 12/15] drm/msm/dp: simplify link and clock disable sequence Yongxing Mou
` (3 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Yongxing Mou @ 2026-06-09 9:46 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Yongxing Mou,
Abhinav Kumar, Dmitry Baryshkov
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
Split dp_ctrl_off() into stream and link parts so that for MST
cases we can control the link and pixel parts separately.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 10 +++++-----
drivers/gpu/drm/msm/dp/dp_ctrl.h | 5 +++--
drivers/gpu/drm/msm/dp/dp_display.c | 7 ++++---
3 files changed, 12 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 87c3a5517911..90fba03de7f0 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -2211,7 +2211,7 @@ static int msm_dp_ctrl_on_pixel_clk(struct msm_dp_ctrl_private *ctrl, unsigned l
return ret;
}
-static void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl)
+void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl)
{
struct msm_dp_ctrl_private *ctrl;
@@ -2240,7 +2240,8 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
* running. Add the global reset just before disabling the
* link clocks and core clocks.
*/
- msm_dp_ctrl_off(&ctrl->msm_dp_ctrl, panel);
+ msm_dp_ctrl_off_pixel_clk(&ctrl->msm_dp_ctrl);
+ msm_dp_ctrl_off_link(&ctrl->msm_dp_ctrl, panel);
ret = msm_dp_ctrl_on_link(&ctrl->msm_dp_ctrl, panel);
if (ret) {
@@ -2634,8 +2635,8 @@ void msm_dp_ctrl_reinit_phy(struct msm_dp_ctrl *msm_dp_ctrl)
phy_init(phy);
}
-void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl,
- struct msm_dp_panel *panel)
+void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel)
{
struct msm_dp_ctrl_private *ctrl;
struct phy *phy;
@@ -2649,7 +2650,6 @@ void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl,
msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl, panel);
- msm_dp_ctrl_off_pixel_clk(msm_dp_ctrl);
dev_pm_opp_set_rate(ctrl->dev, 0);
msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl);
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index 00b430392a52..5902cf7e746a 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -22,8 +22,9 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl,
struct msm_dp_panel *panel,
bool force_link_train);
-void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl,
- struct msm_dp_panel *panel);
+void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *panel);
+void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl);
void msm_dp_ctrl_push_idle(struct msm_dp_ctrl *msm_dp_ctrl);
irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl,
struct msm_dp_panel *panel);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 230e14615a23..8f472633da82 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -725,15 +725,16 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
/* set dongle to D3 (power off) mode */
msm_dp_link_psm_config(dp->link, &dp->panel->link_info, true);
- msm_dp_ctrl_off(dp->ctrl, dp->panel);
- /* re-init the PHY so that we can listen to Dongle disconnect */
+ msm_dp_ctrl_off_pixel_clk(dp->ctrl);
+ msm_dp_ctrl_off_link(dp->ctrl, dp->panel);
msm_dp_ctrl_reinit_phy(dp->ctrl);
} else {
/*
* unplugged interrupt
* dongle unplugged out of DUT
*/
- msm_dp_ctrl_off(dp->ctrl, dp->panel);
+ msm_dp_ctrl_off_pixel_clk(dp->ctrl);
+ msm_dp_ctrl_off_link(dp->ctrl, dp->panel);
msm_dp_display_host_phy_exit(dp);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 12/15] drm/msm/dp: simplify link and clock disable sequence
2026-06-09 9:46 [PATCH v7 00/15] drm/msm/dp: Prerequisite cleanup for upcoming MST support Yongxing Mou
` (10 preceding siblings ...)
2026-06-09 9:46 ` [PATCH v7 11/15] drm/msm/dp: split dp_ctrl_off() into stream and link parts Yongxing Mou
@ 2026-06-09 9:46 ` Yongxing Mou
2026-06-09 10:02 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 13/15] drm/msm/dp: make bridge helpers use dp_display to allow re-use Yongxing Mou
` (2 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Yongxing Mou @ 2026-06-09 9:46 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Yongxing Mou,
Dmitry Baryshkov
Move the common disable steps out of the sink_count check to make the
flow easier to follow.
No functional change intended.
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 26 +++++++++-----------------
1 file changed, 9 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 8f472633da82..63e5b191f95c 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -716,27 +716,19 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
msm_dp_panel_disable_vsc_sdp(dp->panel);
- /* dongle is still connected but sinks are disconnected */
- if (dp->link->sink_count == 0) {
- /*
- * irq_hpd with sink_count = 0
- * hdmi unplugged out of dongle
- */
+ msm_dp_ctrl_off_pixel_clk(dp->ctrl);
- /* set dongle to D3 (power off) mode */
+ /* dongle is still connected but sinks are disconnected */
+ if (dp->link->sink_count == 0)
msm_dp_link_psm_config(dp->link, &dp->panel->link_info, true);
- msm_dp_ctrl_off_pixel_clk(dp->ctrl);
- msm_dp_ctrl_off_link(dp->ctrl, dp->panel);
+
+ msm_dp_ctrl_off_link(dp->ctrl, dp->panel);
+
+ if (dp->link->sink_count == 0)
+ /* re-init the PHY so that we can listen to Dongle disconnect */
msm_dp_ctrl_reinit_phy(dp->ctrl);
- } else {
- /*
- * unplugged interrupt
- * dongle unplugged out of DUT
- */
- msm_dp_ctrl_off_pixel_clk(dp->ctrl);
- msm_dp_ctrl_off_link(dp->ctrl, dp->panel);
+ else
msm_dp_display_host_phy_exit(dp);
- }
msm_dp_display->power_on = false;
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 13/15] drm/msm/dp: make bridge helpers use dp_display to allow re-use
2026-06-09 9:46 [PATCH v7 00/15] drm/msm/dp: Prerequisite cleanup for upcoming MST support Yongxing Mou
` (11 preceding siblings ...)
2026-06-09 9:46 ` [PATCH v7 12/15] drm/msm/dp: simplify link and clock disable sequence Yongxing Mou
@ 2026-06-09 9:46 ` Yongxing Mou
2026-06-09 10:09 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 14/15] drm/msm/dp: separate dp_display_prepare() into its own API Yongxing Mou
2026-06-09 9:46 ` [PATCH v7 15/15] drm/msm/dp: pass panel to display enable/disable helpers Yongxing Mou
14 siblings, 1 reply; 28+ messages in thread
From: Yongxing Mou @ 2026-06-09 9:46 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Yongxing Mou,
Abhinav Kumar, Dmitry Baryshkov
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
dp_bridge helpers take drm_bridge as an input and extract the
dp_display object to be used in the dp_display module. Rather than
doing it in a roundabout way, directly pass the dp_display object
to these helpers so that the MST bridge can also re-use the same
helpers.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 29 +++++++++------------------
drivers/gpu/drm/msm/dp/dp_display.h | 7 +++++++
drivers/gpu/drm/msm/dp/dp_drm.c | 39 ++++++++++++++++++++++++++++++++++++-
drivers/gpu/drm/msm/dp/dp_drm.h | 9 ---------
4 files changed, 54 insertions(+), 30 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 63e5b191f95c..2d5ef087648c 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -738,24 +738,21 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
/**
* msm_dp_bridge_mode_valid - callback to determine if specified mode is valid
- * @bridge: Pointer to drm bridge structure
+ * @dp: Pointer to dp display structure
* @info: display info
* @mode: Pointer to drm mode structure
* Returns: Validity status for specified mode
*/
-enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge,
- const struct drm_display_info *info,
- const struct drm_display_mode *mode)
+enum drm_mode_status msm_dp_display_mode_valid(struct msm_dp *dp,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
{
const u32 num_components = 3, default_bpp = 24;
struct msm_dp_display_private *msm_dp_display;
struct msm_dp_link_info *link_info;
u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0;
- struct msm_dp *dp;
int mode_pclk_khz = mode->clock;
- dp = to_dp_bridge(bridge)->msm_dp_display;
-
if (!dp || !mode_pclk_khz || !dp->connector) {
DRM_ERROR("invalid params\n");
return -EINVAL;
@@ -1406,11 +1403,9 @@ int msm_dp_modeset_init(struct msm_dp *msm_dp_display, struct drm_device *dev,
return 0;
}
-void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
- struct drm_atomic_commit *state)
+void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display,
+ struct drm_atomic_commit *state)
{
- struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge);
- struct msm_dp *msm_dp_display = msm_dp_bridge->msm_dp_display;
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
int rc = 0;
@@ -1419,7 +1414,7 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
crtc = drm_atomic_get_new_crtc_for_encoder(state,
- drm_bridge->encoder);
+ msm_dp_display->bridge->encoder);
if (!crtc)
return;
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
@@ -1449,11 +1444,8 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
drm_dbg_dp(msm_dp_display->drm_dev, "type=%d Done\n", msm_dp_display->connector_type);
}
-void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge,
- struct drm_atomic_commit *state)
+void msm_dp_display_atomic_disable(struct msm_dp *dp)
{
- struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge);
- struct msm_dp *dp = msm_dp_bridge->msm_dp_display;
struct msm_dp_display_private *msm_dp_display;
msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
@@ -1470,11 +1462,8 @@ static void msm_dp_display_unprepare(struct msm_dp_display_private *dp)
drm_dbg_dp(dp->drm_dev, "type=%d Done\n", msm_dp_display->connector_type);
}
-void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
- struct drm_atomic_commit *state)
+void msm_dp_display_atomic_post_disable(struct msm_dp *dp)
{
- struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge);
- struct msm_dp *dp = msm_dp_bridge->msm_dp_display;
struct msm_dp_display_private *msm_dp_display;
msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 0b65e16c790d..5116f7bbbd02 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -33,5 +33,12 @@ void msm_dp_display_signal_audio_start(struct msm_dp *msm_dp_display);
void msm_dp_display_signal_audio_complete(struct msm_dp *msm_dp_display);
void msm_dp_display_set_psr(struct msm_dp *dp, bool enter);
void msm_dp_display_debugfs_init(struct msm_dp *msm_dp_display, struct dentry *dentry, bool is_edp);
+void msm_dp_display_atomic_post_disable(struct msm_dp *dp_display);
+void msm_dp_display_atomic_disable(struct msm_dp *dp_display);
+void msm_dp_display_atomic_enable(struct msm_dp *dp_display,
+ struct drm_atomic_commit *state);
+enum drm_mode_status msm_dp_display_mode_valid(struct msm_dp *dp,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode);
#endif /* _DP_DISPLAY_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index 6ac5bac903d9..6b8923d9dff4 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -49,6 +49,43 @@ static void msm_dp_bridge_debugfs_init(struct drm_bridge *bridge, struct dentry
msm_dp_display_debugfs_init(dp, root, false);
}
+static void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
+ struct drm_atomic_commit *state)
+{
+ struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
+ struct msm_dp *dp = dp_bridge->msm_dp_display;
+
+ msm_dp_display_atomic_enable(dp, state);
+}
+
+static void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge,
+ struct drm_atomic_commit *state)
+{
+ struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
+ struct msm_dp *dp = dp_bridge->msm_dp_display;
+
+ msm_dp_display_atomic_disable(dp);
+}
+
+static void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
+ struct drm_atomic_commit *state)
+{
+ struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
+ struct msm_dp *dp = dp_bridge->msm_dp_display;
+
+ msm_dp_display_atomic_post_disable(dp);
+}
+
+static enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *drm_bridge,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
+ struct msm_dp *dp = dp_bridge->msm_dp_display;
+
+ return msm_dp_display_mode_valid(dp, info, mode);
+}
+
static const struct drm_bridge_funcs msm_dp_bridge_ops = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
@@ -115,7 +152,7 @@ static void msm_edp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
return;
}
- msm_dp_bridge_atomic_enable(drm_bridge, state);
+ msm_dp_display_atomic_enable(dp, state);
}
static void msm_edp_bridge_atomic_disable(struct drm_bridge *drm_bridge,
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h
index 4bd788ea05d5..da412c788503 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_drm.h
@@ -27,15 +27,6 @@ int msm_dp_bridge_init(struct msm_dp *msm_dp_display, struct drm_device *dev,
enum drm_connector_status msm_dp_bridge_detect(struct drm_bridge *bridge,
struct drm_connector *connector);
-void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
- struct drm_atomic_commit *state);
-void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge,
- struct drm_atomic_commit *state);
-void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
- struct drm_atomic_commit *state);
-enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge,
- const struct drm_display_info *info,
- const struct drm_display_mode *mode);
void msm_dp_bridge_hpd_enable(struct drm_bridge *bridge);
void msm_dp_bridge_hpd_disable(struct drm_bridge *bridge);
void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge,
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 14/15] drm/msm/dp: separate dp_display_prepare() into its own API
2026-06-09 9:46 [PATCH v7 00/15] drm/msm/dp: Prerequisite cleanup for upcoming MST support Yongxing Mou
` (12 preceding siblings ...)
2026-06-09 9:46 ` [PATCH v7 13/15] drm/msm/dp: make bridge helpers use dp_display to allow re-use Yongxing Mou
@ 2026-06-09 9:46 ` Yongxing Mou
2026-06-09 10:10 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 15/15] drm/msm/dp: pass panel to display enable/disable helpers Yongxing Mou
14 siblings, 1 reply; 28+ messages in thread
From: Yongxing Mou @ 2026-06-09 9:46 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Yongxing Mou,
Abhinav Kumar, Dmitry Baryshkov
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
For MST, the link setup should only be done once when multiple sinks are
enabled, while stream setup may run multiple times for each sink. Split
the link-related preparation out of msm_dp_display_atomic_enable() so it
can be called separately before the per-stream enable path.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 16 +++++++++++-----
drivers/gpu/drm/msm/dp/dp_display.h | 5 +++--
drivers/gpu/drm/msm/dp/dp_drm.c | 6 ++++--
3 files changed, 18 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 2d5ef087648c..cd1f2899b733 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1403,8 +1403,8 @@ int msm_dp_modeset_init(struct msm_dp *msm_dp_display, struct drm_device *dev,
return 0;
}
-void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display,
- struct drm_atomic_commit *state)
+void msm_dp_display_atomic_prepare(struct msm_dp *msm_dp_display,
+ struct drm_atomic_commit *state)
{
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
@@ -1426,10 +1426,16 @@ void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display,
}
rc = msm_dp_display_prepare_link(dp);
- if (rc) {
+ if (rc)
DRM_ERROR("DP display prepare failed, rc=%d\n", rc);
- return;
- }
+}
+
+void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display)
+{
+ struct msm_dp_display_private *dp;
+ int rc = 0;
+
+ dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
rc = msm_dp_display_enable(dp);
if (rc)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 5116f7bbbd02..43ed79093e24 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -35,8 +35,9 @@ void msm_dp_display_set_psr(struct msm_dp *dp, bool enter);
void msm_dp_display_debugfs_init(struct msm_dp *msm_dp_display, struct dentry *dentry, bool is_edp);
void msm_dp_display_atomic_post_disable(struct msm_dp *dp_display);
void msm_dp_display_atomic_disable(struct msm_dp *dp_display);
-void msm_dp_display_atomic_enable(struct msm_dp *dp_display,
- struct drm_atomic_commit *state);
+void msm_dp_display_atomic_prepare(struct msm_dp *dp_display,
+ struct drm_atomic_commit *state);
+void msm_dp_display_atomic_enable(struct msm_dp *dp_display);
enum drm_mode_status msm_dp_display_mode_valid(struct msm_dp *dp,
const struct drm_display_info *info,
const struct drm_display_mode *mode);
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index 6b8923d9dff4..4bf1a5b7c3f9 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -55,7 +55,8 @@ static void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
struct msm_dp *dp = dp_bridge->msm_dp_display;
- msm_dp_display_atomic_enable(dp, state);
+ msm_dp_display_atomic_prepare(dp, state);
+ msm_dp_display_atomic_enable(dp);
}
static void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge,
@@ -152,7 +153,8 @@ static void msm_edp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
return;
}
- msm_dp_display_atomic_enable(dp, state);
+ msm_dp_display_atomic_prepare(dp, state);
+ msm_dp_display_atomic_enable(dp);
}
static void msm_edp_bridge_atomic_disable(struct drm_bridge *drm_bridge,
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v7 15/15] drm/msm/dp: pass panel to display enable/disable helpers
2026-06-09 9:46 [PATCH v7 00/15] drm/msm/dp: Prerequisite cleanup for upcoming MST support Yongxing Mou
` (13 preceding siblings ...)
2026-06-09 9:46 ` [PATCH v7 14/15] drm/msm/dp: separate dp_display_prepare() into its own API Yongxing Mou
@ 2026-06-09 9:46 ` Yongxing Mou
2026-06-09 10:13 ` sashiko-bot
14 siblings, 1 reply; 28+ messages in thread
From: Yongxing Mou @ 2026-06-09 9:46 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Yongxing Mou,
Dmitry Baryshkov
Pass struct msm_dp_panel to the display enable/disable helpers to make
them easier to reuse for MST stream handling.
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index cd1f2899b733..bea5bfb22967 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -650,7 +650,8 @@ static int msm_dp_display_prepare_link(struct msm_dp_display_private *dp)
return msm_dp_ctrl_prepare_stream_on(dp->ctrl, dp->panel, force_link_train);
}
-static int msm_dp_display_enable(struct msm_dp_display_private *dp)
+static int msm_dp_display_enable(struct msm_dp_display_private *dp,
+ struct msm_dp_panel *msm_dp_panel)
{
int rc = 0;
struct msm_dp *msm_dp_display = &dp->msm_dp_display;
@@ -661,7 +662,7 @@ static int msm_dp_display_enable(struct msm_dp_display_private *dp)
return 0;
}
- rc = msm_dp_ctrl_on_stream(dp->ctrl, dp->panel);
+ rc = msm_dp_ctrl_on_stream(dp->ctrl, msm_dp_panel);
if (!rc)
msm_dp_display->power_on = true;
@@ -707,20 +708,21 @@ static void msm_dp_display_audio_notify_disable(struct msm_dp_display_private *d
msm_dp_display->audio_enabled = false;
}
-static int msm_dp_display_disable(struct msm_dp_display_private *dp)
+static int msm_dp_display_disable(struct msm_dp_display_private *dp,
+ struct msm_dp_panel *msm_dp_panel)
{
struct msm_dp *msm_dp_display = &dp->msm_dp_display;
if (!msm_dp_display->power_on)
return 0;
- msm_dp_panel_disable_vsc_sdp(dp->panel);
+ msm_dp_panel_disable_vsc_sdp(msm_dp_panel);
msm_dp_ctrl_off_pixel_clk(dp->ctrl);
/* dongle is still connected but sinks are disconnected */
if (dp->link->sink_count == 0)
- msm_dp_link_psm_config(dp->link, &dp->panel->link_info, true);
+ msm_dp_link_psm_config(dp->link, &msm_dp_panel->link_info, true);
msm_dp_ctrl_off_link(dp->ctrl, dp->panel);
@@ -1437,14 +1439,14 @@ 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_enable(dp);
+ rc = msm_dp_display_enable(dp, dp->panel);
if (rc)
DRM_ERROR("DP display enable failed, rc=%d\n", rc);
rc = msm_dp_display_post_enable(msm_dp_display);
if (rc) {
DRM_ERROR("DP display post enable failed, rc=%d\n", rc);
- msm_dp_display_disable(dp);
+ msm_dp_display_disable(dp, dp->panel);
}
drm_dbg_dp(msm_dp_display->drm_dev, "type=%d Done\n", msm_dp_display->connector_type);
@@ -1479,7 +1481,7 @@ 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_disable(msm_dp_display, msm_dp_display->panel);
msm_dp_display_unprepare(msm_dp_display);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH v7 08/15] drm/msm/dp: break up dp_display_enable into two parts
2026-06-09 9:46 ` [PATCH v7 08/15] drm/msm/dp: break up dp_display_enable into two parts Yongxing Mou
@ 2026-06-09 9:59 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-06-09 9:59 UTC (permalink / raw)
To: Yongxing Mou; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] Missing error return when sink_count == 0 leads to unclocked hardware accesses and kernel panic.
- [High] Missing early return when link training fails causes stream enablement on a broken link.
--
commit b8a02a483af0ac409e7c1790ebfbe296067ea49d
Author: Abhinav Kumar <quic_abhinavk@quicinc.com>
drm/msm/dp: break up dp_display_enable into two parts
This commit splits dp_display_enable() into two parts: prepare and
enable APIs. This allows inserting MST payload functions between enabling
stream clocks and programming the registers.
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 155beefdfdef2..2cf4c984c7ca7 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
[ ... ]
> @@ -617,7 +617,40 @@ static int msm_dp_display_set_mode(struct msm_dp *msm_dp_display,
> return 0;
> }
>
> -static int msm_dp_display_enable(struct msm_dp_display_private *dp, bool force_link_train)
> +static int msm_dp_display_prepare_link(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);
> +
> + if (msm_dp_display->is_edp)
> + msm_dp_hpd_plug_handle(dp);
> +
> + rc = pm_runtime_resume_and_get(&msm_dp_display->pdev->dev);
> + if (rc) {
> + DRM_ERROR("failed to pm_runtime_resume\n");
> + return rc;
> + }
> +
> + if (dp->link->sink_count == 0)
> + return rc;
[Severity: Critical]
Does this unintentionally allow the enable sequence to continue when the
sink_count is 0?
Since pm_runtime_resume_and_get() succeeded, rc is 0 here. Returning 0
causes the caller, msm_dp_bridge_atomic_enable(), to continue directly into
msm_dp_display_enable() because the prepare step appears successful:
msm_dp_bridge_atomic_enable() {
...
rc = msm_dp_display_prepare_link(dp);
if (rc) {
DRM_ERROR("DP display prepare failed, rc=%d\n", rc);
return;
}
rc = msm_dp_display_enable(dp);
...
}
Because msm_dp_ctrl_on_link() was skipped, the DP link clocks are not
enabled. Will the subsequent call to msm_dp_ctrl_on_stream() within
msm_dp_display_enable() then perform an unclocked hardware access when
writing to REG_DP_STATE_CTRL, potentially triggering a bus stall and kernel
panic?
> +
> + if (!msm_dp_display->power_on) {
> + msm_dp_display_host_phy_init(dp);
> + force_link_train = true;
> + }
> +
> + rc = msm_dp_ctrl_on_link(dp->ctrl);
> + if (rc)
> + DRM_ERROR("Failed link training (rc=%d)\n", rc);
> + // TODO: schedule drm_connector_set_link_status_property()
> +
> + return msm_dp_ctrl_prepare_stream_on(dp->ctrl, force_link_train);
> +}
[Severity: High]
If msm_dp_ctrl_on_link() fails, does this mask the failure and force the
pipeline to stream video over a broken link?
In the original code, an error from msm_dp_ctrl_on_link() would explicitly
return and abort the atomic enable sequence. Here, the error is logged, but
execution falls through to msm_dp_ctrl_prepare_stream_on(), which often
returns 0.
If it returns 0, the caller receives a success status and proceeds to
configure and start the video stream on an uninitialized link.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609-dp_mstclean-v7-0-ea04113e8233@oss.qualcomm.com?part=8
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 12/15] drm/msm/dp: simplify link and clock disable sequence
2026-06-09 9:46 ` [PATCH v7 12/15] drm/msm/dp: simplify link and clock disable sequence Yongxing Mou
@ 2026-06-09 10:02 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-06-09 10:02 UTC (permalink / raw)
To: Yongxing Mou; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The patch introduces a Time-of-Check to Time-of-Use (TOCTOU) race by splitting a single `sink_count` evaluation into two unprotected checks, risking an inconsistent hardware disable sequence.
--
commit 4b86b22d6f1aa41eba1cece8a907cf0b3a12b55c
Author: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
drm/msm/dp: simplify link and clock disable sequence
This patch refactors the disable sequence by pulling common steps out
of the sink_count condition block to simplify the flow.
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
[ ... ]
> @@ -716,27 +716,19 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
>
> msm_dp_panel_disable_vsc_sdp(dp->panel);
>
> - /* dongle is still connected but sinks are disconnected */
> - if (dp->link->sink_count == 0) {
[ ... ]
> + msm_dp_ctrl_off_pixel_clk(dp->ctrl);
>
> - /* set dongle to D3 (power off) mode */
> + /* dongle is still connected but sinks are disconnected */
> + if (dp->link->sink_count == 0)
> msm_dp_link_psm_config(dp->link, &dp->panel->link_info, true);
> - msm_dp_ctrl_off_pixel_clk(dp->ctrl);
> - msm_dp_ctrl_off_link(dp->ctrl, dp->panel);
> +
> + msm_dp_ctrl_off_link(dp->ctrl, dp->panel);
> +
> + if (dp->link->sink_count == 0)
[Severity: High]
Could this double-evaluation of dp->link->sink_count introduce a race
condition?
The original code checked dp->link->sink_count == 0 exactly once. By
checking it twice, if an asynchronously processed HPD IRQ event modifies
the sink_count between the two checks, could the driver execute a
mismatched hardware disable sequence?
For instance, if sink_count changes from 0 to 1 after the first check,
the driver would perform msm_dp_link_psm_config(), but then take the
else branch and call msm_dp_display_host_phy_exit() below. This would
leave the PHY completely powered down, potentially breaking the ability
to detect future dongle disconnects.
Would it be safer to read sink_count once into a local variable and
evaluate the local variable for both conditions?
> + /* re-init the PHY so that we can listen to Dongle disconnect */
> msm_dp_ctrl_reinit_phy(dp->ctrl);
> - } else {
> - /*
> - * unplugged interrupt
> - * dongle unplugged out of DUT
> - */
> - msm_dp_ctrl_off_pixel_clk(dp->ctrl);
> - msm_dp_ctrl_off_link(dp->ctrl, dp->panel);
> + else
> msm_dp_display_host_phy_exit(dp);
> - }
>
> msm_dp_display->power_on = false;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609-dp_mstclean-v7-0-ea04113e8233@oss.qualcomm.com?part=12
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 11/15] drm/msm/dp: split dp_ctrl_off() into stream and link parts
2026-06-09 9:46 ` [PATCH v7 11/15] drm/msm/dp: split dp_ctrl_off() into stream and link parts Yongxing Mou
@ 2026-06-09 10:04 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-06-09 10:04 UTC (permalink / raw)
To: Yongxing Mou; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] The error code returned by `msm_dp_ctrl_on_pixel_clk()` is ignored in `msm_dp_ctrl_process_phy_test_request()`, allowing execution to proceed even if clock enablement fails.
--
commit 63742fe8cd27d164a8457069750896b0e989b7f3
Author: Abhinav Kumar <quic_abhinavk@quicinc.com>
drm/msm/dp: split dp_ctrl_off() into stream and link parts
This commit splits dp_ctrl_off() into stream and link parts so that for
MST cases we can control the link and pixel parts separately.
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index 87c3a5517911f..90fba03de7f0b 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
[ ... ]
> @@ -2240,7 +2240,8 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
> * running. Add the global reset just before disabling the
> * link clocks and core clocks.
> */
> - msm_dp_ctrl_off(&ctrl->msm_dp_ctrl, panel);
> + msm_dp_ctrl_off_pixel_clk(&ctrl->msm_dp_ctrl);
> + msm_dp_ctrl_off_link(&ctrl->msm_dp_ctrl, panel);
>
> ret = msm_dp_ctrl_on_link(&ctrl->msm_dp_ctrl, panel);
> if (ret) {
[Severity: High]
This isn't a bug introduced by this patch, but while looking at the rest of
msm_dp_ctrl_process_phy_test_request(), the return value from enabling the
pixel clock appears to be completely ignored:
ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate);
msm_dp_ctrl_send_phy_test_pattern(ctrl);
return 0;
If msm_dp_ctrl_on_pixel_clk() fails to prepare and enable the clock, it
returns a negative error code, but the code still continues execution. Does
processing the PHY test pattern and commanding the DP controller without a
successfully enabled stream clock leave the hardware in an undefined state?
Should we be checking this return value and exiting early on failure?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609-dp_mstclean-v7-0-ea04113e8233@oss.qualcomm.com?part=11
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 04/15] drm/msm/dp: split msm_dp_ctrl_config_ctrl() into link parts and stream parts
2026-06-09 9:46 ` [PATCH v7 04/15] drm/msm/dp: split msm_dp_ctrl_config_ctrl() into link parts and stream parts Yongxing Mou
@ 2026-06-09 10:04 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-06-09 10:04 UTC (permalink / raw)
To: Yongxing Mou; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Splitting `msm_dp_ctrl_config_ctrl()` causes stream configuration state to be permanently lost during dynamic link maintenance, leading to display corruption.
--
commit 83fda55d30fe1cd098ad8cc76e151a0398a09b6a
Author: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
drm/msm/dp: split msm_dp_ctrl_config_ctrl() into link parts and stream parts
The DP_CONFIGURATION_CTRL register contains both link-level and
stream-specific fields. Currently, msm_dp_ctrl_config_ctrl() configures
all of them together. Separate the configuration into link parts and
stream parts to support MST.
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index 86ef8c89ad443..ed2ba47881fd0 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
[ ... ]
> +static void msm_dp_ctrl_config_ctrl_link(struct msm_dp_ctrl_private *ctrl)
> +{
> + u32 config = 0;
[ ... ]
> - if (ctrl->panel->psr_cap.version)
> - config |= DP_CONFIGURATION_CTRL_SEND_VSC;
> -
> - drm_dbg_dp(ctrl->drm_dev, "DP_CONFIGURATION_CTRL=0x%x\n", config);
> + drm_dbg_dp(ctrl->drm_dev, "link DP_CONFIGURATION_CTRL=0x%x\n", config);
>
> msm_dp_write_link(ctrl, REG_DP_CONFIGURATION_CTRL, config);
> }
[ ... ]
> @@ -1628,7 +1644,7 @@ static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl,
> u8 assr;
> struct msm_dp_link_info link_info = {0};
>
> - msm_dp_ctrl_config_ctrl(ctrl);
> + msm_dp_ctrl_config_ctrl_link(ctrl);
[Severity: High]
Does splitting this configuration cause the stream state to be permanently lost
during dynamic link maintenance?
Before this patch, msm_dp_ctrl_config_ctrl() programmed both link and stream
parameters. Now, msm_dp_ctrl_config_ctrl_link() initializes config = 0 and
overwrites REG_DP_CONFIGURATION_CTRL, which erases any existing stream
configuration.
While normal initialization restores stream parameters by calling
msm_dp_ctrl_configure_source_params() after link training, dynamic link
maintenance paths (like msm_dp_ctrl_link_maintenance() calling
msm_dp_ctrl_setup_main_link()) perform link training and then transition
directly to DP_STATE_CTRL_SEND_VIDEO without re-applying the stream
parameters:
msm_dp_ctrl_link_maintenance()
...
ret = msm_dp_ctrl_setup_main_link(ctrl, &training_step);
...
msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, DP_STATE_CTRL_SEND_VIDEO);
Since msm_dp_ctrl_setup_main_link() eventually triggers
msm_dp_ctrl_link_train() which calls msm_dp_ctrl_config_ctrl_link(), will this
result in the DP stream coming back up with critical parameters cleared (e.g.,
0 BPC, no VSC, incorrect color format), breaking the display output?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609-dp_mstclean-v7-0-ea04113e8233@oss.qualcomm.com?part=4
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 07/15] drm/msm/dp: move the pixel clock control to its own API
2026-06-09 9:46 ` [PATCH v7 07/15] drm/msm/dp: move the pixel clock control to its own API Yongxing Mou
@ 2026-06-09 10:04 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-06-09 10:04 UTC (permalink / raw)
To: Yongxing Mou; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] Unhandled error return from `msm_dp_ctrl_on_pixel_clk()` causing potential system crash.
- [High] Invalid `WARN_ON_ONCE` escalation leading to potential Denial of Service (DoS) on hardware timeouts.
Pre-existing issues:
- [High] Lockless concurrent access to `ctrl->stream_clks_on` between DRM atomic commit and HPD IRQ threads.
--
commit 4ab86d41ec16eead989a4b3a8579ccd95556770a
Author: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
drm/msm/dp: move the pixel clock control to its own API
Enable/Disable of DP pixel clock happens in multiple code paths
leading to code duplication. Move it into individual helpers so that
the helpers can be called wherever necessary.
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index 1c2eccec6ec6d..a2c44088e6a68 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> @@ -2171,6 +2171,41 @@ static bool msm_dp_ctrl_send_phy_test_pattern(struct msm_dp_ctrl_private *ctrl)
> return success;
> }
>
> +static int msm_dp_ctrl_on_pixel_clk(struct msm_dp_ctrl_private *ctrl, unsigned long pixel_rate)
> +{
> + int ret;
> +
> + ret = clk_set_rate(ctrl->pixel_clk, pixel_rate * 1000);
> + if (ret) {
> + DRM_ERROR("Failed to set pixel clock rate. ret=%d\n", ret);
> + return ret;
> + }
> +
> + if (WARN_ON_ONCE(ctrl->stream_clks_on))
> + return 0;
[Severity: High]
Does this WARN_ON_ONCE() introduce a potential system crash with panic_on_warn
enabled?
If msm_dp_ctrl_wait4video_ready() in msm_dp_ctrl_on_stream() times out, the
function returns an error but leaves ctrl->stream_clks_on as true. This causes
the caller, msm_dp_display_enable(), to refuse setting
msm_dp_display->power_on = true.
Later, on teardown, msm_dp_display_disable() bails out early because power_on
is false, skipping msm_dp_ctrl_off_pixel_clk() and leaving
ctrl->stream_clks_on as true.
On the next modeset or hotplug attempt, when msm_dp_ctrl_on_pixel_clk() is
called again, it will trigger this newly added warning. Since external
display hardware timeouts can be triggered by a faulty sink, is it safe to
escalate this from the previous debug print to a warning?
[Severity: High]
This is a pre-existing issue, but does lockless concurrent access to
ctrl->stream_clks_on between DRM atomic commit and HPD IRQ threads risk a NoC
error?
The boolean flag ctrl->stream_clks_on and the pixel clock are accessed and
modified in msm_dp_ctrl_on_pixel_clk() and msm_dp_ctrl_off_pixel_clk()
without any synchronization primitives.
msm_dp_ctrl_on_pixel_clk() can be executed by the DRM atomic commit thread
while the asynchronous HPD IRQ worker thread simultaneously handles a PHY test
request via msm_dp_ctrl_process_phy_test_request(). If Thread 1 evaluates
ctrl->stream_clks_on as true to disable the clock concurrently with Thread 2
accessing DP PHY registers, can it lead to a synchronous external abort when
accessing unclocked DP registers?
> +
> + ret = clk_prepare_enable(ctrl->pixel_clk);
> + if (ret) {
> + DRM_ERROR("Failed to start pixel clocks. ret=%d\n", ret);
> + return ret;
> + }
> + ctrl->stream_clks_on = true;
> +
> + return ret;
> +}
> +
> +static void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl)
> +{
[ ... ]
> @@ -2196,22 +2231,7 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
> }
>
> pixel_rate = ctrl->panel->msm_dp_mode.drm_mode.clock;
> - ret = clk_set_rate(ctrl->pixel_clk, pixel_rate * 1000);
> - if (ret) {
> - DRM_ERROR("Failed to set pixel clock rate. ret=%d\n", ret);
> - return ret;
> - }
> -
> - if (ctrl->stream_clks_on) {
> - drm_dbg_dp(ctrl->drm_dev, "pixel clks already enabled\n");
> - } else {
> - ret = clk_prepare_enable(ctrl->pixel_clk);
> - if (ret) {
> - DRM_ERROR("Failed to start pixel clocks. ret=%d\n", ret);
> - return ret;
> - }
> - ctrl->stream_clks_on = true;
> - }
> + ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate);
>
> msm_dp_ctrl_send_phy_test_pattern(ctrl);
[Severity: High]
Does this code ignore the return value of msm_dp_ctrl_on_pixel_clk()?
In msm_dp_ctrl_process_phy_test_request(), ret is assigned from
msm_dp_ctrl_on_pixel_clk(), but it isn't checked before calling
msm_dp_ctrl_send_phy_test_pattern().
If clock enablement fails (e.g., due to an unsupported clock rate requested by
a malicious DP sink), the function unconditionally proceeds to send the PHY
test pattern. Will accessing DP PHY and mainlink hardware registers without
the required clock running trigger a synchronous external abort and panic the
kernel?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609-dp_mstclean-v7-0-ea04113e8233@oss.qualcomm.com?part=7
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 01/15] drm/msm/dp: remove cached drm_edid from panel
2026-06-09 9:46 ` [PATCH v7 01/15] drm/msm/dp: remove cached drm_edid from panel Yongxing Mou
@ 2026-06-09 10:04 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-06-09 10:04 UTC (permalink / raw)
To: Yongxing Mou; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Unsynchronized updates to connector->edid_blob_ptr and connector->display_info leading to data races and potential Use-After-Free.
--
commit 6f73beae4d0acbc00f38ca7b1a881d309a66701f
Author: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
drm/msm/dp: remove cached drm_edid from panel
This commit simplifies the EDID handling in the MSM DP driver by removing the
cached drm_edid from the panel structure. It uses the drm_edid pointer
directly in the plug stage instead and updates the connector accordingly.
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index dc6f33809ca5f..bce165bac106b 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -276,10 +277,20 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
>
> dp->link->lttpr_count = msm_dp_display_lttpr_init(dp, dpcd);
>
> - rc = msm_dp_panel_read_sink_caps(dp->panel, connector);
> + rc = msm_dp_panel_read_link_caps(dp->panel, connector);
> if (rc)
> goto end;
>
> + drm_edid = drm_edid_read_ddc(connector, &dp->aux->ddc);
> + drm_edid_connector_update(connector, drm_edid);
[Severity: Critical]
This is a pre-existing issue, but does msm_dp_display_process_hpd_high()
need to hold drm_mode_config.mutex before calling
drm_edid_connector_update()?
Looking at the DRM core guidelines, connector->edid_blob_ptr and
connector->display_info are protected by drm_mode_config.mutex.
Since this runs from an unprotected IRQ worker context upon hotplug, could
an unsynchronized update cause a use-after-free? If userspace concurrently
invokes a DRM_IOCTL_MODE_GETCONNECTOR request, it might read the old blob
while it is being replaced and freed here.
> +
> + if (!drm_edid) {
> + DRM_ERROR("panel edid read failed\n");
[ ... ]
> @@ -453,7 +465,7 @@ static int msm_dp_hpd_unplug_handle(struct msm_dp_display_private *dp)
>
> /* Don't forget modes for eDP */
> if (!dp->msm_dp_display.is_edp)
> - msm_dp_panel_unplugged(dp->panel, dp->msm_dp_display.connector);
> + drm_edid_connector_update(dp->msm_dp_display.connector, NULL);
[Severity: Critical]
This is a pre-existing issue, but does msm_dp_hpd_unplug_handle() similarly
need synchronization?
Clearing the EDID property without the mode config mutex might race with
other readers of the connector state.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609-dp_mstclean-v7-0-ea04113e8233@oss.qualcomm.com?part=1
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 06/15] drm/msm/dp: split link setup from source params
2026-06-09 9:46 ` [PATCH v7 06/15] drm/msm/dp: split link setup from source params Yongxing Mou
@ 2026-06-09 10:05 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-06-09 10:05 UTC (permalink / raw)
To: Yongxing Mou; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Link retraining erases active stream configurations due to overwriting shared register
--
commit c62d46d098f37a3918af36fbf87cf140b8d49f60
Author: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
drm/msm/dp: split link setup from source params
msm_dp_ctrl_configure_source_params() should only handle stream-related
configuration. Move the link setup out of it so MST can program link and
stream settings separately.
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index 71d45b2c4daf8..1c2eccec6ec6d 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
[ ... ]
> @@ -2551,6 +2547,10 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train
> */
> reinit_completion(&ctrl->video_comp);
>
> + msm_dp_ctrl_lane_mapping(ctrl);
> + msm_dp_setup_peripheral_flush(ctrl);
> + msm_dp_ctrl_config_ctrl_link(ctrl);
[Severity: High]
This isn't a bug introduced by this patch, but looking at how link setup
and stream setup share registers, can msm_dp_ctrl_config_ctrl_link() wipe
out active stream configurations?
In msm_dp_ctrl_config_ctrl_link(), the REG_DP_CONFIGURATION_CTRL register
state is built from 0 rather than reading the existing state:
u32 config = 0;
const u8 *dpcd = panel->dpcd;
/* Default-> LSCLK DIV: 1/4 LCLK */
config |= (2 << DP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT);
...
msm_dp_write_link(ctrl, REG_DP_CONFIGURATION_CTRL, config);
When msm_dp_ctrl_link_maintenance() triggers link retraining due to a sink
request, it eventually calls msm_dp_ctrl_config_ctrl_link(). Because this
function overwrites the shared REG_DP_CONFIGURATION_CTRL register without
a read-modify-write, does it erase all stream configurations (like
DP_CONFIGURATION_CTRL_RGB_YUV or DP_CONFIGURATION_CTRL_SEND_VSC)
previously set by msm_dp_ctrl_config_ctrl_streams()?
If the maintenance routine then resumes sending video, this might leave the
hardware outputting an incorrect stream format. Would it be safer to use
msm_dp_read_link() to preserve the existing register state?
> +
> msm_dp_ctrl_configure_source_params(ctrl);
>
> msm_dp_ctrl_config_msa(ctrl,
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609-dp_mstclean-v7-0-ea04113e8233@oss.qualcomm.com?part=6
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 02/15] drm/msm/dp: drop deprecated .mode_set() and use .atomic_enable
2026-06-09 9:46 ` [PATCH v7 02/15] drm/msm/dp: drop deprecated .mode_set() and use .atomic_enable Yongxing Mou
@ 2026-06-09 10:06 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-06-09 10:06 UTC (permalink / raw)
To: Yongxing Mou; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Moving mode setup to `.atomic_enable` causes the upstream DPU encoder to read stale wide-bus configuration data during its own enablement.
Pre-existing issues:
- [High] Mode validation callback incorrectly evaluates wide bus availability based on the state of the currently active mode rather than the candidate mode.
--
commit 1b0de5b490fbc6b7e19b00f71948fa7d36954fe7
Author: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
drm/msm/dp: drop deprecated .mode_set() and use .atomic_enable
The bridge .mode_set() callback is deprecated. Remove it and move all
mode setup logic to .atomic_enable(), where the adjusted_mode is
available from the atomic CRTC state.
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index bce165bac106b..57eafd4724627 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
[ ... ]
> @@ -1309,7 +1325,7 @@ bool msm_dp_wide_bus_available(const struct msm_dp *msm_dp_display)
>
> dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
>
> - if (dp->msm_dp_mode.out_fmt_is_yuv_420)
> + if (dp->panel->msm_dp_mode.out_fmt_is_yuv_420)
[Severity: High]
This isn't a bug introduced by this patch, but doesn't this function
check the currently active mode's format rather than the candidate mode's
format?
During mode validation in msm_dp_display_mode_valid(), the driver calls
msm_dp_wide_bus_available() to adjust pixel clock limits. Since this reads
the active mode's YUV420 state, wouldn't it incorrectly assume wide bus is
unavailable for a candidate RGB mode if the currently active mode happens
to be YUV420?
This might cause valid high-resolution RGB modes to be rejected because
their clock exceeds DP_MAX_PIXEL_CLK_KHZ. Additionally, isn't this reading
a state field that could be locklessly mutated by concurrent atomic commits?
> return false;
>
> return dp->wide_bus_supported;
[ ... ]
> @@ -1386,7 +1406,7 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
> if (msm_dp_display->link->sink_count == 0)
> return;
>
> - rc = msm_dp_display_set_mode(dp, &msm_dp_display->msm_dp_mode);
> + rc = msm_dp_display_set_mode(dp, &crtc_state->adjusted_mode, msm_dp_display->panel);
[Severity: High]
By moving the mode setup here into the bridge's .atomic_enable callback,
could this cause the upstream DPU encoder to read stale wide-bus
configuration data?
The DRM atomic helper sequence executes the encoder's .atomic_enable hook
before the bridge's .atomic_enable hook. In the encoder's enable sequence:
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c:dpu_encoder_virt_atomic_enable()
dpu_enc->wide_bus_en = dpu_encoder_is_widebus_enabled(drm_enc);
This calls down to msm_dp_wide_bus_available(), which checks the
out_fmt_is_yuv_420 flag.
Since the bridge hasn't updated its mode state yet via
msm_dp_display_set_mode(), won't the encoder read the stale value from the
previous modeset?
This seems like it could incorrectly enable or disable wide bus support
when switching between RGB and YUV420 modes, potentially leading to a
hardware configuration mismatch and display corruption.
> if (rc) {
> DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc);
> return;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609-dp_mstclean-v7-0-ea04113e8233@oss.qualcomm.com?part=2
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 13/15] drm/msm/dp: make bridge helpers use dp_display to allow re-use
2026-06-09 9:46 ` [PATCH v7 13/15] drm/msm/dp: make bridge helpers use dp_display to allow re-use Yongxing Mou
@ 2026-06-09 10:09 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-06-09 10:09 UTC (permalink / raw)
To: Yongxing Mou; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] Hardcoded base encoder in `msm_dp_display_atomic_enable` breaks MST reuse
- [Low] Kernel-doc mismatch for `msm_dp_display_mode_valid`
Pre-existing issues:
- [High] `msm_dp_display_mode_valid` ignores the `info` parameter, validating against the wrong sink in MST
--
commit 79702b8fc2a6d1a516b0d64051edab10a8d7764e
Author: Abhinav Kumar <quic_abhinavk@quicinc.com>
Subject: drm/msm/dp: make bridge helpers use dp_display to allow re-use
This commit modifies the DP bridge helpers to directly take the dp_display
object rather than a drm_bridge pointer. This refactoring is intended to
allow the MST bridge to reuse the same helpers.
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index f1970e6dd7e46..cdb7dd4cd7cbd 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -738,24 +738,21 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
>
> /**
> * msm_dp_bridge_mode_valid - callback to determine if specified mode is valid
> - * @bridge: Pointer to drm bridge structure
> + * @dp: Pointer to dp display structure
> * @info: display info
> * @mode: Pointer to drm mode structure
> * Returns: Validity status for specified mode
> */
[Severity: Low]
This isn't a bug, but there is a mismatch in the documentation. The
kernel-doc still refers to the old function name msm_dp_bridge_mode_valid,
while the function was renamed to msm_dp_display_mode_valid.
> -enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge,
> - const struct drm_display_info *info,
> - const struct drm_display_mode *mode)
> +enum drm_mode_status msm_dp_display_mode_valid(struct msm_dp *dp,
> + const struct drm_display_info *info,
> + const struct drm_display_mode *mode)
[Severity: High]
This is a pre-existing issue, but since these helpers are being exposed for
MST bridge reuse, does it cause problems that the info parameter is ignored?
Inside msm_dp_display_mode_valid(), the code checks capabilities against
dp->connector->display_info instead of using the provided info parameter:
if ((drm_mode_is_420_only(&dp->connector->display_info, mode) &&
msm_dp_display->panel->vsc_sdp_supported) ||
msm_dp_wide_bus_available(dp))
mode_pclk_khz /= 2;
If this is used for an MST stream, won't this validate the mode against the
main DP connector's capabilities rather than the specific MST sink's
capabilities?
[ ... ]
> @@ -1406,11 +1403,9 @@ int msm_dp_modeset_init(struct msm_dp *msm_dp_display, struct drm_device *dev,
> return 0;
> }
>
> -void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
> - struct drm_atomic_commit *state)
> +void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display,
> + struct drm_atomic_commit *state)
> {
> - struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge);
> - struct msm_dp *msm_dp_display = msm_dp_bridge->msm_dp_display;
> struct drm_crtc *crtc;
> struct drm_crtc_state *crtc_state;
> int rc = 0;
> @@ -1419,7 +1414,7 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
> dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
>
> crtc = drm_atomic_get_new_crtc_for_encoder(state,
> - drm_bridge->encoder);
> + msm_dp_display->bridge->encoder);
[Severity: High]
Will this hardcoded encoder lookup break MST support?
In an MST topology, each stream operates through its own distinct virtual
drm_bridge and drm_encoder. By hardcoding msm_dp_display->bridge->encoder
here, the primary DP encoder is used instead of the encoder for the specific
bridge being updated.
If the primary encoder isn't participating in the MST stream's atomic commit,
won't drm_atomic_get_new_crtc_for_encoder() return NULL and cause the
helper to bail out silently without programming the mode?
> if (!crtc)
> return;
> crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609-dp_mstclean-v7-0-ea04113e8233@oss.qualcomm.com?part=13
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 14/15] drm/msm/dp: separate dp_display_prepare() into its own API
2026-06-09 9:46 ` [PATCH v7 14/15] drm/msm/dp: separate dp_display_prepare() into its own API Yongxing Mou
@ 2026-06-09 10:10 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-06-09 10:10 UTC (permalink / raw)
To: Yongxing Mou; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Splitting `msm_dp_display_atomic_enable()` into `prepare` and `enable` phases removes the early-return mechanism on errors, leading to unclocked hardware accesses and kernel panics.
--
commit 8e66b9cc12ba1b2ae6719ca3e9b5d64d23812cf9
Author: Abhinav Kumar <quic_abhinavk@quicinc.com>
drm/msm/dp: separate dp_display_prepare() into its own API
For MST, the link setup should only be done once when multiple sinks are
enabled, while stream setup may run multiple times for each sink. Split
the link-related preparation out of msm_dp_display_atomic_enable() so it
can be called separately before the per-stream enable path.
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index cdb7dd4cd7cbd..03320071fc077 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
[ ... ]
> -void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display,
> - struct drm_atomic_commit *state)
> +void msm_dp_display_atomic_prepare(struct msm_dp *msm_dp_display,
> + struct drm_atomic_commit *state)
> {
[ ... ]
> rc = msm_dp_display_prepare_link(dp);
> - if (rc) {
> + if (rc)
> DRM_ERROR("DP display prepare failed, rc=%d\n", rc);
> - return;
> - }
> +}
[Severity: High]
Since msm_dp_display_atomic_prepare() now returns void, doesn't this
remove the early-return mechanism if msm_dp_display_prepare_link() fails?
If power or clock enablement fails here, the error is logged but not
propagated to the caller.
[ ... ]
> diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
> index 6b8923d9dff40..4bf1a5b7c3f97 100644
> --- a/drivers/gpu/drm/msm/dp/dp_drm.c
> +++ b/drivers/gpu/drm/msm/dp/dp_drm.c
[ ... ]
> @@ -55,7 +55,8 @@ static void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
> struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
> struct msm_dp *dp = dp_bridge->msm_dp_display;
>
> - msm_dp_display_atomic_enable(dp, state);
> + msm_dp_display_atomic_prepare(dp, state);
> + msm_dp_display_atomic_enable(dp);
> }
[Severity: High]
Will this unconditionally execute the enable phase even if the prepare
phase failed?
If msm_dp_display_atomic_prepare() encounters an error (like a pm_runtime
failure), calling msm_dp_display_atomic_enable() immediately after might
attempt to write to unclocked DP controller registers in msm_dp_ctrl_on_stream().
Can this trigger a synchronous external abort and an immediate kernel
panic on Qualcomm platforms?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609-dp_mstclean-v7-0-ea04113e8233@oss.qualcomm.com?part=14
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 15/15] drm/msm/dp: pass panel to display enable/disable helpers
2026-06-09 9:46 ` [PATCH v7 15/15] drm/msm/dp: pass panel to display enable/disable helpers Yongxing Mou
@ 2026-06-09 10:13 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-06-09 10:13 UTC (permalink / raw)
To: Yongxing Mou; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [Medium] Incomplete refactoring in `msm_dp_display_disable` leaving `dp->panel` usage.
Pre-existing issues:
- [High] Missing early return in `msm_dp_display_atomic_enable` on display enable failure.
- [High] Sleeping functions called from atomic context in `atomic_post_disable`.
--
commit a04f2001a63e30ecc08234abf0151576d165e356
Author: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
drm/msm/dp: pass panel to display enable/disable helpers
This patch passes struct msm_dp_panel to the display enable/disable helpers.
This aims to make them easier to reuse for MST stream handling.
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 03320071fc077..888ecf34f1602 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
[ ... ]
> @@ -707,20 +708,21 @@ static void msm_dp_display_audio_notify_disable(struct msm_dp_display_private *d
> msm_dp_display->audio_enabled = false;
> }
>
> -static int msm_dp_display_disable(struct msm_dp_display_private *dp)
> +static int msm_dp_display_disable(struct msm_dp_display_private *dp,
> + struct msm_dp_panel *msm_dp_panel)
> {
> struct msm_dp *msm_dp_display = &dp->msm_dp_display;
>
> if (!msm_dp_display->power_on)
> return 0;
>
> - msm_dp_panel_disable_vsc_sdp(dp->panel);
> + msm_dp_panel_disable_vsc_sdp(msm_dp_panel);
>
> msm_dp_ctrl_off_pixel_clk(dp->ctrl);
>
> /* dongle is still connected but sinks are disconnected */
> if (dp->link->sink_count == 0)
> - msm_dp_link_psm_config(dp->link, &dp->panel->link_info, true);
> + msm_dp_link_psm_config(dp->link, &msm_dp_panel->link_info, true);
>
> msm_dp_ctrl_off_link(dp->ctrl, dp->panel);
[Severity: Medium]
Will this disable VSC SDP on the wrong panel when reused for MST streams?
It looks like msm_dp_ctrl_off_link() still operates on the primary dp->panel
instead of the newly passed msm_dp_panel parameter.
>
[ ... ]
> @@ -1437,14 +1439,14 @@ 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_enable(dp);
> + rc = msm_dp_display_enable(dp, dp->panel);
> if (rc)
> DRM_ERROR("DP display enable failed, rc=%d\n", rc);
>
> rc = msm_dp_display_post_enable(msm_dp_display);
[Severity: High]
This is a pre-existing issue, but does execution proceed to post-enable even if
msm_dp_display_enable() fails?
If msm_dp_display_enable() fails, the error is logged, but there is no early
return. This allows msm_dp_display_post_enable() to proceed and configure
hardware features like PSR when clocks and power might not be properly
initialized, which could lead to hardware timeouts or unclocked access faults.
> if (rc) {
> DRM_ERROR("DP display post enable failed, rc=%d\n", rc);
> - msm_dp_display_disable(dp);
> + msm_dp_display_disable(dp, dp->panel);
> }
>
> drm_dbg_dp(msm_dp_display->drm_dev, "type=%d Done\n", msm_dp_display->connector_type);
> @@ -1479,7 +1481,7 @@ void msm_dp_display_atomic_post_disable(struct msm_dp *dp)
>
> msm_dp_display_audio_notify_disable(msm_dp_display);
[Severity: High]
This isn't a bug introduced by this patch, but does this call sequence
sleep while in an atomic context?
msm_dp_display_atomic_post_disable() is an atomic commit callback, yet it calls
msm_dp_display_audio_notify_disable(), which can sleep:
msm_dp_display_audio_notify_disable() {
...
if (!wait_for_completion_timeout(&dp->audio_comp, HZ * 5))
...
}
>
> - msm_dp_display_disable(msm_dp_display);
> + msm_dp_display_disable(msm_dp_display, msm_dp_display->panel);
[Severity: High]
This is also a pre-existing issue, but does msm_dp_display_disable()
similarly sleep in this atomic context?
It calls msm_dp_link_psm_config(), which acquires a mutex:
msm_dp_link_psm_config() {
...
mutex_lock(&link->psm_mutex);
...
}
Calling sleeping functions in atomic contexts is forbidden by DRM/KMS
guidelines and could lead to kernel warnings, deadlocks, or system instability.
>
> msm_dp_display_unprepare(msm_dp_display);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609-dp_mstclean-v7-0-ea04113e8233@oss.qualcomm.com?part=15
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v7 09/15] drm/msm/dp: re-arrange dp_display_disable() into functional parts
2026-06-09 9:46 ` [PATCH v7 09/15] drm/msm/dp: re-arrange dp_display_disable() into functional parts Yongxing Mou
@ 2026-06-09 10:17 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-06-09 10:17 UTC (permalink / raw)
To: Yongxing Mou; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Replacing msm_dp_ctrl_off_link_stream() with msm_dp_ctrl_off() for the disconnected monitor case inadvertently resets the DP controller, clearing HPD interrupt masks and permanently breaking dongle unplug detection.
Pre-existing issues:
- [High] The display disable sequence invokes sleeping functions from atomic context during the bridge post_disable callback.
--
commit 4edd0bb0b0901115e8cbd3c69301ff0b85ef4340
Author: Abhinav Kumar <quic_abhinavk@quicinc.com>
drm/msm/dp: re-arrange dp_display_disable() into functional parts
dp_display_disable() handles special case of when monitor is
disconnected from the dongle while the dongle stays connected
thereby needing a separate function dp_ctrl_off_link_stream()
for this. However with a slight rework this can still be handled
by keeping common paths same for regular and special case.
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 2cf4c984c7ca7..e1d90b8539c27 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
[ ... ]
> @@ -714,12 +714,20 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
> if (!msm_dp_display->power_on)
> return 0;
>
> + msm_dp_panel_disable_vsc_sdp(dp->panel);
> +
> + /* dongle is still connected but sinks are disconnected */
> if (dp->link->sink_count == 0) {
> /*
> * irq_hpd with sink_count = 0
> * hdmi unplugged out of dongle
> */
> - msm_dp_ctrl_off_link_stream(dp->ctrl);
> +
> + /* set dongle to D3 (power off) mode */
> + msm_dp_link_psm_config(dp->link, &dp->panel->link_info, true);
> + msm_dp_ctrl_off(dp->ctrl);
[Severity: High]
Does replacing msm_dp_ctrl_off_link_stream() with msm_dp_ctrl_off() here
permanently break dongle unplug detection?
msm_dp_ctrl_off() calls msm_dp_ctrl_reset(), which performs a software
reset (DP_SW_RESET) on the controller:
sw_reset |= DP_SW_RESET;
msm_dp_write_ahb(ctrl, REG_DP_SW_RESET, sw_reset);
This clears the controller's register state, including REG_DP_INTR_STATUS
and REG_DP_DP_HPD_INT_MASK.
Because the dongle is still connected, the driver holds a runtime PM reference
and doesn't transition through suspend/resume. Will this cause the HPD
interrupts to remain permanently masked, causing the driver to silently miss
subsequent dongle unplug events since msm_dp_ctrl_enable_irq() is never called
again?
[Severity: High]
This isn't a bug introduced by this patch, but does calling msm_dp_ctrl_off()
here invoke sleeping functions from atomic context?
msm_dp_display_disable() is invoked from the bridge atomic disable path, which
runs in atomic context where sleeping is forbidden. However, msm_dp_ctrl_off()
calls msm_dp_ctrl_reset(), which executes:
usleep_range(1000, 1100);
Is this safe to run in atomic commit paths?
> + /* re-init the PHY so that we can listen to Dongle disconnect */
> + msm_dp_ctrl_reinit_phy(dp->ctrl);
[Severity: High]
This is a pre-existing issue, but does calling msm_dp_ctrl_reinit_phy()
here also invoke sleeping functions from atomic context?
msm_dp_ctrl_reinit_phy() calls generic PHY framework functions (phy_exit()
and phy_init()) that acquire mutexes internally, which is forbidden in atomic
context.
> } else {
> /*
> * unplugged interrupt
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609-dp_mstclean-v7-0-ea04113e8233@oss.qualcomm.com?part=9
^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2026-06-09 10:17 UTC | newest]
Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-09 9:46 [PATCH v7 00/15] drm/msm/dp: Prerequisite cleanup for upcoming MST support Yongxing Mou
2026-06-09 9:46 ` [PATCH v7 01/15] drm/msm/dp: remove cached drm_edid from panel Yongxing Mou
2026-06-09 10:04 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 02/15] drm/msm/dp: drop deprecated .mode_set() and use .atomic_enable Yongxing Mou
2026-06-09 10:06 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 03/15] drm/msm/dp: move mode setup into msm_dp_panel_init_panel_info() Yongxing Mou
2026-06-09 9:46 ` [PATCH v7 04/15] drm/msm/dp: split msm_dp_ctrl_config_ctrl() into link parts and stream parts Yongxing Mou
2026-06-09 10:04 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 05/15] drm/msm/dp: extract MISC1_MISC0 configuration into a separate function Yongxing Mou
2026-06-09 9:46 ` [PATCH v7 06/15] drm/msm/dp: split link setup from source params Yongxing Mou
2026-06-09 10:05 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 07/15] drm/msm/dp: move the pixel clock control to its own API Yongxing Mou
2026-06-09 10:04 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 08/15] drm/msm/dp: break up dp_display_enable into two parts Yongxing Mou
2026-06-09 9:59 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 09/15] drm/msm/dp: re-arrange dp_display_disable() into functional parts Yongxing Mou
2026-06-09 10:17 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 10/15] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it Yongxing Mou
2026-06-09 9:46 ` [PATCH v7 11/15] drm/msm/dp: split dp_ctrl_off() into stream and link parts Yongxing Mou
2026-06-09 10:04 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 12/15] drm/msm/dp: simplify link and clock disable sequence Yongxing Mou
2026-06-09 10:02 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 13/15] drm/msm/dp: make bridge helpers use dp_display to allow re-use Yongxing Mou
2026-06-09 10:09 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 14/15] drm/msm/dp: separate dp_display_prepare() into its own API Yongxing Mou
2026-06-09 10:10 ` sashiko-bot
2026-06-09 9:46 ` [PATCH v7 15/15] drm/msm/dp: pass panel to display enable/disable helpers Yongxing Mou
2026-06-09 10:13 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.