* [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets
@ 2025-08-25 14:15 Yongxing Mou
2025-08-25 14:15 ` [PATCH v3 01/38] drm/msm/dp: remove cached drm_edid from panel Yongxing Mou
` (37 more replies)
0 siblings, 38 replies; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:15 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
Add support for Multi-stream transport for MSM chipsets that allow
a single instance of DP controller to send multiple streams.
This series has been validated on sa8775p ride platform using multiple
MST dongles and also daisy chain method on both DP0 and DP1 upto 1080P.
With 4x4K monitors, due to lack of layer mixers that combination will not
work but this can be supported as well after some rework on the DPU side.
In addition, SST was re-validated with all these changes to ensure there
were no regressions.
This patch series was made on top of:
[1] : https://patchwork.freedesktop.org/series/151522/ (v2 to fix up HPD)
Bindings for the pixel clock for additional stream is available at :
[2] : https://patchwork.freedesktop.org/series/152718/
Overall, the patch series has been organized in the following way:
1) First set are a couple of fixes made while debugging MST but applicable
to SST as well so go ahead of everything else
2) Prepare the DP driver to get ready to handle multiple streams. This is the bulk
of the work as current DP driver design had to be adjusted to make this happen.
3) Finally, new files to handle MST related operations
Validation was done on the latest linux-next on top of above changes and
both FB console and weston compositors were validated with these changes.
To: Rob Clark <robin.clark@oss.qualcomm.com>
To: Dmitry Baryshkov <lumag@kernel.org>
To: Abhinav Kumar <abhinav.kumar@linux.dev>
To: Jessica Zhang <jessica.zhang@oss.qualcomm.com>
To: Sean Paul <sean@poorly.run>
To: Marijn Suijten <marijn.suijten@somainline.org>
To: David Airlie <airlied@gmail.com>
To: Simona Vetter <simona@ffwll.ch>
Cc: linux-arm-msm@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: freedreno@lists.freedesktop.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
Changes in v3: Fixed review comments from Dmitry
- Fixed lots of comments from series V1/V2.
- Rebased onto next-20250808.
- Rebased onto Jessica's HPD-refactor branch.
- Fixed formatting issues in commit messages under changes.
- Removed unnecessary one-line wrappers.
- Relocated MST-related .atomic_check() calls to their appropriate positions.
- Removed the logic related to slot checking in .mode_valid().
- Link to v2: https://lore.kernel.org/r/20250609-msm-dp-mst-v2-0-a54d8902a23d@quicinc.com
Changes in v2: Fixed review comments from Dmitry
- Rebase on top of next-20250606
- Add all 4 streams pixel clks support and MST2/MST3 Link clk support
- Address the formatting issues mentioned in the review comments
- Drop the cache of msm_dp_panel->drm_edid cached
- Remove the one-line wrapper funtion and redundant conditional check
- Fixed the commit messgae descriptions of some patches
- Reordered the patches and renamed some functions and variables
- Link to v1: https://lore.kernel.org/all/20241205-dp_mst-v1-0-f
8618d42a99a@quicinc.com/
---
Abhinav Kumar (30):
drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead
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
drm/msm/dp: introduce max_streams for DP controller MST support
drm/msm/dp: introduce stream_id for each DP panel
drm/msm/dp: Add support for programming p1/p2/p3 register blocks
drm/msm/dp: use stream_id to change offsets in dp_catalog
drm/msm/dp: add support to send ACT packets for MST
drm/msm/dp: Add support to enable MST in mainlink control
drm/msm/dp: no need to update tu calculation for mst
drm/msm/dp: Add support for MST channel slot allocation
drm/msm/dp: Add support for sending VCPF packets in DP controller
drm/msm/dp: Always program MST_FIFO_CONSTANT_FILL for MST use cases
drm/msm/dp: abstract out the dp_display stream helpers to accept a panel
drm/msm/dp: replace power_on with active_stream_cnt for dp_display
drm/msm/dp: Mark the SST bridge disconnected when mst is active
drm/msm/dp: add an API to initialize MST on sink side
drm/msm/dp: add dp_display_get_panel() to initialize DP panel
drm/msm/dp: add dp_mst_drm to manage DP MST bridge operations
drm/msm/dp: add connector abstraction for DP MST
drm/msm/dp: add HPD callback for dp MST
drm/msm: add support for MST non-blocking commits
drm/msm: initialize DRM MST encoders for DP controllers
drm/msm/dp: initialize dp_mst module for each DP MST controller
drm/msm/dpu: use msm_dp_get_mst_intf_id() to get the intf id
drm/msm/dp: fix the intf_type of MST interfaces
Yongxing Mou (8):
drm/msm/dp: remove cached drm_edid from panel
drm/msm/dp: splite 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: move the pixel clock control to its own API
drm/msm/dp: Add catalog support for 3rd/4th stream MST
drm/msm/dp: add MST atomic check to msm_atomic_check()
drm/msm/dp: propagate MST state changes to dp mst module
drm/msm/dp: Add MST stream support for SA8775P DP controller 0 and 1
drivers/gpu/drm/msm/Makefile | 3 +-
.../drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h | 6 +-
.../drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h | 12 +-
.../drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h | 6 +-
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 51 +-
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 2 +
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 29 +-
drivers/gpu/drm/msm/dp/dp_audio.c | 2 +-
drivers/gpu/drm/msm/dp/dp_ctrl.c | 664 +++++++++++---
drivers/gpu/drm/msm/dp/dp_ctrl.h | 23 +-
drivers/gpu/drm/msm/dp/dp_display.c | 569 ++++++++----
drivers/gpu/drm/msm/dp/dp_display.h | 36 +-
drivers/gpu/drm/msm/dp/dp_drm.c | 51 +-
drivers/gpu/drm/msm/dp/dp_drm.h | 12 -
drivers/gpu/drm/msm/dp/dp_mst_drm.c | 994 +++++++++++++++++++++
drivers/gpu/drm/msm/dp/dp_mst_drm.h | 90 ++
drivers/gpu/drm/msm/dp/dp_panel.c | 295 +++---
drivers/gpu/drm/msm/dp/dp_panel.h | 27 +-
drivers/gpu/drm/msm/dp/dp_reg.h | 46 +-
drivers/gpu/drm/msm/msm_atomic.c | 10 +-
drivers/gpu/drm/msm/msm_drv.h | 19 +
drivers/gpu/drm/msm/msm_kms.c | 2 +
22 files changed, 2529 insertions(+), 420 deletions(-)
---
base-commit: b1549501188cc9eba732c25b033df7a53ccc341f
change-id: 20250609-msm-dp-mst-cddc2f61daee
prerequisite-message-id: <20250808-hpd-refactor-v2-0-7f4e1e741aa3@oss.qualcomm.com>
prerequisite-patch-id: 0fc90e557ae4bb852757444b92af71ebb3c0698f
prerequisite-patch-id: a58ebaf429385c622869c83e83ce7ffdfe9ea27e
prerequisite-patch-id: 9840ae12755ebc1528d6719b058b86ce8ba3e5e8
prerequisite-patch-id: 8d5d7ac0302a333ecaf01d420f067d2db7195783
prerequisite-patch-id: 49532adadff58e9069a96b37f26017847fb29a3d
prerequisite-patch-id: 3d58007342ba985e1c7f0f85ebc5da29c8b67b5c
prerequisite-patch-id: 46274f99ffb986e53a48d6802e2fc05beb7b7b01
prerequisite-patch-id: 67536f5bceaf6e9aae18cb8e95874b71ef22dee4
prerequisite-patch-id: f5fb952fb63bce96161cb56db065927fa0a91e87
prerequisite-patch-id: 981c763c51c815adda57909ffe567cfa1069d5f5
prerequisite-patch-id: 3f738fbbf3634482eaf46e5bd5b83c0cf2ce0fe1
prerequisite-patch-id: 1e250d144e7bad58e2f1ea33f74ceea8e26335e0
prerequisite-message-id: <20250815-dp_mst_bindings-v6-0-e715bbbb5386@oss.qualcomm.com>
prerequisite-patch-id: ffeeb0739a4b3d310912f4bb6c0bd17802818879
prerequisite-patch-id: f0f92109d1bfffa6a1142f2aaecbd72a29b858c0
prerequisite-patch-id: 9cabb6be69b17e8580a2cffc7aa2709106cc1adf
prerequisite-patch-id: a389a2e4eca44bf62bb2c861c96596368be7a021
prerequisite-patch-id: 4f02ab9314f95984ab7dc9b852ba4d6c676746a7
prerequisite-patch-id: 62d643df7c88d8db2279def1e4b63a605e9145c0
Best regards,
--
Yongxing Mou <yongxing.mou@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v3 01/38] drm/msm/dp: remove cached drm_edid from panel
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
@ 2025-08-25 14:15 ` Yongxing Mou
2025-08-25 16:41 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead Yongxing Mou
` (36 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:15 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
The cached drm_edid in msm_dp_panel was redundant and led to unnecessary
state management complexity. This change removes the drm_edid member from
the panel structure and refactors related functions to use locally read
EDID data instead.
- Replaces msm_dp_panel_read_sink_caps() with msm_dp_panel_read_link_caps()
- Updates msm_dp_panel_handle_sink_request() to accept drm_edid as input
- Removes msm_dp_panel_get_modes() and drm_edid caching logic
- Cleans up unused drm_edid_free() calls
This simplifies EDID handling and avoids stale data issues.
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 28 +++++++++++++++-------
drivers/gpu/drm/msm/dp/dp_panel.c | 47 ++++---------------------------------
drivers/gpu/drm/msm/dp/dp_panel.h | 9 +++----
3 files changed, 26 insertions(+), 58 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 6d81950627a073baca9809690134a711e965035f..dadf31bc37763c4f07f68b76fbbe33fb77b20850 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -282,6 +282,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;
rc = drm_dp_read_dpcd_caps(dp->aux, dpcd);
if (rc)
@@ -289,7 +290,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;
+ }
+
if (rc)
goto end;
@@ -306,7 +320,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
@@ -565,7 +579,6 @@ static int msm_dp_irq_hpd_handle(struct msm_dp_display_private *dp, u32 data)
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);
}
@@ -616,7 +629,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);
@@ -624,13 +637,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:
@@ -794,8 +805,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 15b7f6c7146e1176a80b5c9d25896b1c8ede3aed..eae125972934bb2fb3b716dc47ae71cd0421bd1a 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -159,7 +159,7 @@ 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,
+int msm_dp_panel_read_link_caps(struct msm_dp_panel *msm_dp_panel,
struct drm_connector *connector)
{
int rc, bw_code;
@@ -201,25 +201,6 @@ 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;
}
@@ -246,20 +227,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;
@@ -267,7 +234,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;
@@ -280,7 +248,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)
@@ -736,10 +704,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 d2cf401506dcbaf553192d5e18c87207337664ab..618d0253b525308b392b9282098e8ca78bf32f1c 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;
@@ -50,13 +49,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,
+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);
@@ -95,5 +93,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.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
2025-08-25 14:15 ` [PATCH v3 01/38] drm/msm/dp: remove cached drm_edid from panel Yongxing Mou
@ 2025-08-25 14:15 ` Yongxing Mou
2025-08-25 16:50 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 03/38] drm/msm/dp: break up dp_display_enable into two parts Yongxing Mou
` (35 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:15 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
dp_display caches the current display mode and then passes it onto
the panel to be used for programming the panel params. Remove this
two level passing and directly populate the panel's dp_display_mode
instead.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 66 ++++++++++++++-----------------------
1 file changed, 24 insertions(+), 42 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index dadf31bc37763c4f07f68b76fbbe33fb77b20850..632a1191e4e48fecd7dbda2f6ec6b8ed0aeebc93 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -60,7 +60,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 */
@@ -649,16 +648,29 @@ 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(&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 */
- 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);
+ 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);
return 0;
}
@@ -1328,7 +1340,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;
@@ -1389,10 +1401,6 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
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");
- return;
- }
if (dp->is_edp)
msm_dp_hpd_plug_handle(msm_dp_display, 0);
@@ -1405,12 +1413,6 @@ 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);
- if (rc) {
- DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc);
- return;
- }
-
if (dp->link_ready && !dp->power_on) {
msm_dp_display_host_phy_init(msm_dp_display);
force_link_train = true;
@@ -1479,31 +1481,11 @@ void msm_dp_bridge_mode_set(struct drm_bridge *drm_bridge,
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;
+ msm_dp_display_set_mode(dp, adjusted_mode, msm_dp_panel);
/* 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;
+ msm_dp_display->ctrl->wide_bus_en = msm_dp_panel->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)
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 03/38] drm/msm/dp: break up dp_display_enable into two parts
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
2025-08-25 14:15 ` [PATCH v3 01/38] drm/msm/dp: remove cached drm_edid from panel Yongxing Mou
2025-08-25 14:15 ` [PATCH v3 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead Yongxing Mou
@ 2025-08-25 14:15 ` Yongxing Mou
2025-08-25 17:13 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 04/38] drm/msm/dp: re-arrange dp_display_disable() into functional parts Yongxing Mou
` (34 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:15 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
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>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 51 ++++++++++------
drivers/gpu/drm/msm/dp/dp_ctrl.h | 3 +-
drivers/gpu/drm/msm/dp/dp_display.c | 113 ++++++++++++++++++++++++------------
drivers/gpu/drm/msm/dp/dp_display.h | 1 +
4 files changed, 113 insertions(+), 55 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 4cf269b98029a55e6cbdfb297587de320019b833..6bfd8faa1e212c3a25964677a4462e7a3a162fa4 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -2446,27 +2446,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",
+ drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d\n",
ctrl->link->link_params.rate,
- ctrl->link->link_params.num_lanes, pixel_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",
@@ -2476,10 +2468,39 @@ 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;
}
}
+ 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 = 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, "pixel_rate=%lu\n", pixel_rate);
+
ret = clk_set_rate(ctrl->pixel_clk, pixel_rate * 1000);
if (ret) {
DRM_ERROR("Failed to set pixel clock rate. ret=%d\n", ret);
@@ -2497,12 +2518,6 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train
ctrl->stream_clks_on = true;
}
- 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);
-
/*
* 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 f68bee62713f1650354b37edb8e1d76134d8d395..1497f1a8fc2f00991356663c19c87eb9fad48a73 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 632a1191e4e48fecd7dbda2f6ec6b8ed0aeebc93..36a12150925246b168acbabf77d1206a1ef8eff5 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -674,7 +674,42 @@ 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(struct msm_dp_display_private *dp)
+{
+ int rc = 0;
+ struct msm_dp *msm_dp_display = &dp->msm_dp_display;
+ bool force_link_train = false;
+
+ drm_dbg_dp(dp->drm_dev, "sink_count=%d\n", dp->link->sink_count);
+
+ 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->link_ready && !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);
+ msm_dp_display->connector->state->link_status = DRM_LINK_STATUS_BAD;
+ }
+
+ rc = msm_dp_ctrl_prepare_stream_on(dp->ctrl, force_link_train);
+ if (!rc)
+ msm_dp_display->prepared = true;
+
+ return rc;
+}
+
+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;
@@ -685,7 +720,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;
@@ -715,13 +750,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 */
@@ -732,6 +764,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) {
/*
@@ -1395,44 +1435,34 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
struct drm_atomic_state *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;
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 (dp->is_edp)
- msm_dp_hpd_plug_handle(msm_dp_display, 0);
-
- if (pm_runtime_resume_and_get(&dp->pdev->dev)) {
- DRM_ERROR("failed to pm_runtime_resume\n");
- return;
- }
+ struct msm_dp_display_private *dp;
- if (msm_dp_display->link->sink_count == 0)
- return;
+ dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
- if (dp->link_ready && !dp->power_on) {
- msm_dp_display_host_phy_init(msm_dp_display);
- force_link_train = true;
- }
+ if (msm_dp_display->is_edp)
+ msm_dp_hpd_plug_handle(dp, 0);
- rc = msm_dp_ctrl_on_link(msm_dp_display->ctrl);
+ rc = msm_dp_display_prepare(dp);
if (rc) {
- DRM_ERROR("Failed link training (rc=%d)\n", rc);
- dp->connector->state->link_status = DRM_LINK_STATUS_BAD;
+ DRM_ERROR("DP display prepare failed, rc=%d\n", rc);
+ return;
}
- msm_dp_display_enable(msm_dp_display, force_link_train);
+ if (msm_dp_display->prepared) {
+ 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);
- if (rc) {
- DRM_ERROR("DP display post enable failed, rc=%d\n", rc);
- msm_dp_display_disable(msm_dp_display);
+ 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);
+ }
}
- 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,
@@ -1447,6 +1477,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);
+
+ msm_dp_display->prepared = false;
+}
+
void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
struct drm_atomic_state *state)
{
@@ -1462,11 +1501,13 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
if (!dp->link_ready)
drm_dbg_dp(dp->drm_dev, "type=%d is disconnected\n", dp->connector_type);
+ msm_dp_display_audio_notify_disable(msm_dp_display);
+
msm_dp_display_disable(msm_dp_display);
- drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type);
+ msm_dp_display_unprepare(msm_dp_display);
- pm_runtime_put_sync(&dp->pdev->dev);
+ drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type);
}
void msm_dp_bridge_mode_set(struct drm_bridge *drm_bridge,
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 60094061c1029bc7a06ffaa80d9403b40aa07eb1..2fbf16f27842bb7639efaa2baecac7bdf8908432 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -20,6 +20,7 @@ struct msm_dp {
bool link_ready;
bool audio_enabled;
bool power_on;
+ bool prepared;
unsigned int connector_type;
bool is_edp;
bool internal_hpd;
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 04/38] drm/msm/dp: re-arrange dp_display_disable() into functional parts
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (2 preceding siblings ...)
2025-08-25 14:15 ` [PATCH v3 03/38] drm/msm/dp: break up dp_display_enable into two parts Yongxing Mou
@ 2025-08-25 14:15 ` Yongxing Mou
2025-08-25 17:25 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 05/38] drm/msm/dp: splite msm_dp_ctrl_config_ctrl() into link parts and stream parts Yongxing Mou
` (33 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:15 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
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>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 19 +------------------
drivers/gpu/drm/msm/dp/dp_ctrl.h | 3 ++-
drivers/gpu/drm/msm/dp/dp_display.c | 10 +++++++++-
3 files changed, 12 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 6bfd8faa1e212c3a25964677a4462e7a3a162fa4..e1ff4c6bb4f0eed2e1ff931f12ba891cf81feffb 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -2549,7 +2549,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;
@@ -2557,23 +2557,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 1497f1a8fc2f00991356663c19c87eb9fad48a73..93747c0a9b3f049bc877f347f05d42b66ad0dddf 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,6 @@ 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 36a12150925246b168acbabf77d1206a1ef8eff5..4c74eb2915fd620868f658ccafc32030b1c208c6 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -773,12 +773,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.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 05/38] drm/msm/dp: splite msm_dp_ctrl_config_ctrl() into link parts and stream parts
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (3 preceding siblings ...)
2025-08-25 14:15 ` [PATCH v3 04/38] drm/msm/dp: re-arrange dp_display_disable() into functional parts Yongxing Mou
@ 2025-08-25 14:15 ` Yongxing Mou
2025-08-25 17:28 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 06/38] drm/msm/dp: extract MISC1_MISC0 configuration into a separate function Yongxing Mou
` (32 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:15 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
The DP_CONFIGURATION_CTRL register contains both link-level and
stream-specific fields. Currently, msm_dp_ctrl_config_ctrl() configures
all of them together, which makes it harder to support MST.
This patch separates the configuration into two functions:
- msm_dp_ctrl_config_ctrl_link(): handles link-related fields
- msm_dp_ctrl_config_ctrl_streams(): handles stream-specific fields
It also moves the link-related configuration out of
msm_dp_ctrl_configure_source_params().
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 49 +++++++++++++++++++++++++---------------
1 file changed, 31 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index e1ff4c6bb4f0eed2e1ff931f12ba891cf81feffb..45d6c9a7f7ddaa049443253cbf4c6fc5feda3177 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -380,26 +380,41 @@ 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;
+
+ 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);
@@ -413,10 +428,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);
}
@@ -439,10 +451,7 @@ static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl
{
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(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);
@@ -1614,7 +1623,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;
@@ -2524,6 +2533,10 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl)
*/
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.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 06/38] drm/msm/dp: extract MISC1_MISC0 configuration into a separate function
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (4 preceding siblings ...)
2025-08-25 14:15 ` [PATCH v3 05/38] drm/msm/dp: splite msm_dp_ctrl_config_ctrl() into link parts and stream parts Yongxing Mou
@ 2025-08-25 14:15 ` Yongxing Mou
2025-08-25 17:30 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 07/38] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it Yongxing Mou
` (31 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:15 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
The MISC1_MISC0 register contains stream-specific configuration bits.
To improve code clarity and modularity, this patch refactors the related
code into a new helper function: msm_dp_ctrl_config_misc1_misc0().
This separation also prepares the codebase for future MST stream
programming support.
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 45d6c9a7f7ddaa049443253cbf4c6fc5feda3177..6bfb639c1ebd33f1103c5b68dfb40701738fa267 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -447,13 +447,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_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);
@@ -467,6 +467,13 @@ 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_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.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 07/38] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (5 preceding siblings ...)
2025-08-25 14:15 ` [PATCH v3 06/38] drm/msm/dp: extract MISC1_MISC0 configuration into a separate function Yongxing Mou
@ 2025-08-25 14:15 ` Yongxing Mou
2025-08-25 17:32 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 08/38] drm/msm/dp: move the pixel clock control to its own API Yongxing Mou
` (30 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:15 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
Currently, the dp_ctrl stream APIs operate on their own dp_panel
which is cached inside the dp_ctrl's private struct. However with MST,
the cached panel represents the fixed link and not the sinks which
are hotplugged. Allow the stream related APIs to work on the panel
which is passed to them rather than the cached one. For SST cases,
this shall continue to use the cached dp_panel.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 21 +++++++++++----------
drivers/gpu/drm/msm/dp/dp_ctrl.h | 2 +-
drivers/gpu/drm/msm/dp/dp_display.c | 2 +-
3 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 6bfb639c1ebd33f1103c5b68dfb40701738fa267..261907446135904a9e479f18051974f5fea88ef1 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -469,13 +469,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 *msm_dp_panel)
{
- msm_dp_ctrl_config_ctrl_streams(ctrl, ctrl->panel);
+ msm_dp_ctrl_config_ctrl_streams(ctrl, msm_dp_panel);
- msm_dp_ctrl_config_misc1_misc0(ctrl, ctrl->panel);
+ msm_dp_ctrl_config_misc1_misc0(ctrl, msm_dp_panel);
- msm_dp_panel_timing_cfg(ctrl->panel, ctrl->msm_dp_ctrl.wide_bus_en);
+ msm_dp_panel_timing_cfg(msm_dp_panel, ctrl->msm_dp_ctrl.wide_bus_en);
}
/*
@@ -2497,7 +2498,7 @@ int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_li
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 *msm_dp_panel)
{
int ret = 0;
bool mainlink_ready = false;
@@ -2510,9 +2511,9 @@ 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 = pixel_rate_orig = ctrl->panel->msm_dp_mode.drm_mode.clock;
+ pixel_rate = pixel_rate_orig = msm_dp_panel->msm_dp_mode.drm_mode.clock;
- if (msm_dp_ctrl->wide_bus_en || ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420)
+ if (msm_dp_ctrl->wide_bus_en || msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
pixel_rate >>= 1;
drm_dbg_dp(ctrl->drm_dev, "pixel_rate=%lu\n", pixel_rate);
@@ -2544,14 +2545,14 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl)
msm_dp_setup_peripheral_flush(ctrl);
msm_dp_ctrl_config_ctrl_link(ctrl);
- msm_dp_ctrl_configure_source_params(ctrl);
+ msm_dp_ctrl_configure_source_params(ctrl, msm_dp_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);
+ msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420);
- msm_dp_panel_clear_dsc_dto(ctrl->panel);
+ msm_dp_panel_clear_dsc_dto(msm_dp_panel);
msm_dp_ctrl_setup_tr_unit(ctrl);
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index 93747c0a9b3f049bc877f347f05d42b66ad0dddf..78406c757eccf95e82f1a9d4437ebdbbc4f8ea46 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -17,7 +17,7 @@ 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_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *msm_dp_panel);
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);
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 4c74eb2915fd620868f658ccafc32030b1c208c6..d07bb40f848e0e13a0fa32aa70ffb1621edca159 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -720,7 +720,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;
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 08/38] drm/msm/dp: move the pixel clock control to its own API
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (6 preceding siblings ...)
2025-08-25 14:15 ` [PATCH v3 07/38] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it Yongxing Mou
@ 2025-08-25 14:15 ` Yongxing Mou
2025-08-25 17:34 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 09/38] drm/msm/dp: split dp_ctrl_off() into stream and link parts Yongxing Mou
` (29 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:15 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
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>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 77 +++++++++++++++++++++-------------------
1 file changed, 41 insertions(+), 36 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 261907446135904a9e479f18051974f5fea88ef1..c0001b93a194821927507028f392877db585fd2c 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -2162,6 +2162,42 @@ 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 (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;
+ }
+
+ 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;
@@ -2187,22 +2223,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);
@@ -2518,21 +2539,10 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
drm_dbg_dp(ctrl->drm_dev, "pixel_rate=%lu\n", pixel_rate);
- ret = clk_set_rate(ctrl->pixel_clk, pixel_rate * 1000);
+ ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate);
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;
+ DRM_ERROR("failed to enable pixel clk\n");
+ return ret;
}
/*
@@ -2566,7 +2576,6 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
drm_dbg_dp(ctrl->drm_dev,
"mainlink %s\n", mainlink_ready ? "READY" : "NOT READY");
-end:
return ret;
}
@@ -2600,11 +2609,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.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 09/38] drm/msm/dp: split dp_ctrl_off() into stream and link parts
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (7 preceding siblings ...)
2025-08-25 14:15 ` [PATCH v3 08/38] drm/msm/dp: move the pixel clock control to its own API Yongxing Mou
@ 2025-08-25 14:15 ` Yongxing Mou
2025-08-25 17:35 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 10/38] drm/msm/dp: make bridge helpers use dp_display to allow re-use Yongxing Mou
` (28 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:15 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
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>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 8 ++++----
drivers/gpu/drm/msm/dp/dp_ctrl.h | 3 ++-
drivers/gpu/drm/msm/dp/dp_display.c | 6 ++++--
3 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index c0001b93a194821927507028f392877db585fd2c..b25eb2fa2835f660073b5109496ac9f2f4e038d2 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -2186,7 +2186,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;
@@ -2214,7 +2214,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);
+ msm_dp_ctrl_off_pixel_clk(&ctrl->msm_dp_ctrl);
+ msm_dp_ctrl_off_link(&ctrl->msm_dp_ctrl);
ret = msm_dp_ctrl_on_link(&ctrl->msm_dp_ctrl);
if (ret) {
@@ -2595,7 +2596,7 @@ void msm_dp_ctrl_reinit_phy(struct msm_dp_ctrl *msm_dp_ctrl)
phy, phy->init_count, phy->power_count);
}
-void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl)
+void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl)
{
struct msm_dp_ctrl_private *ctrl;
struct phy *phy;
@@ -2609,7 +2610,6 @@ void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl)
msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl);
- 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 78406c757eccf95e82f1a9d4437ebdbbc4f8ea46..6ff3e9d9fa6ff0afa325a7a6f72a15009635f340 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -19,7 +19,8 @@ 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, struct msm_dp_panel *msm_dp_panel);
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);
+void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl);
+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);
void msm_dp_ctrl_handle_sink_request(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 d07bb40f848e0e13a0fa32aa70ffb1621edca159..c5c502e51b94a6ac4b9a893b43eb88e87a0c0d46 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -784,7 +784,8 @@ 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_pixel_clk(dp->ctrl);
+ msm_dp_ctrl_off_link(dp->ctrl);
/* re-init the PHY so that we can listen to Dongle disconnect */
msm_dp_ctrl_reinit_phy(dp->ctrl);
} else {
@@ -792,7 +793,8 @@ 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_pixel_clk(dp->ctrl);
+ msm_dp_ctrl_off_link(dp->ctrl);
msm_dp_display_host_phy_exit(dp);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 10/38] drm/msm/dp: make bridge helpers use dp_display to allow re-use
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (8 preceding siblings ...)
2025-08-25 14:15 ` [PATCH v3 09/38] drm/msm/dp: split dp_ctrl_off() into stream and link parts Yongxing Mou
@ 2025-08-25 14:15 ` Yongxing Mou
2025-08-25 14:15 ` [PATCH v3 11/38] drm/msm/dp: separate dp_display_prepare() into its own API Yongxing Mou
` (27 subsequent siblings)
37 siblings, 0 replies; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:15 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
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>
---
drivers/gpu/drm/msm/dp/dp_display.c | 34 ++++++++-----------------
drivers/gpu/drm/msm/dp/dp_display.h | 9 +++++++
drivers/gpu/drm/msm/dp/dp_drm.c | 49 ++++++++++++++++++++++++++++++++++++-
drivers/gpu/drm/msm/dp/dp_drm.h | 12 ---------
4 files changed, 67 insertions(+), 37 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index c5c502e51b94a6ac4b9a893b43eb88e87a0c0d46..8e95cfb98bd08088a707ffb5643a5021a288095f 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -806,24 +806,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;
@@ -1441,11 +1438,8 @@ 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_state *state)
+void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display)
{
- struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge);
- struct msm_dp *msm_dp_display = msm_dp_bridge->msm_dp_display;
int rc = 0;
struct msm_dp_display_private *dp;
@@ -1475,11 +1469,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_state *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);
@@ -1496,11 +1487,8 @@ static void msm_dp_display_unprepare(struct msm_dp_display_private *dp)
msm_dp_display->prepared = false;
}
-void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
- struct drm_atomic_state *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);
@@ -1520,12 +1508,10 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type);
}
-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_display_mode_set(struct msm_dp *dp,
+ 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;
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 2fbf16f27842bb7639efaa2baecac7bdf8908432..e12496ac73f853a2bc120e68eeb84e5a2de6aabe 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -36,5 +36,14 @@ 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);
+void msm_dp_display_mode_set(struct msm_dp *dp,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *adjusted_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);
#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 f935093c4df4681770ab487916584cc76834b0d0..b6b77ee96c30ca60e31cf76569e262a237493aeb 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -81,6 +81,53 @@ 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_state *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);
+}
+
+static void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge,
+ struct drm_atomic_state *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_state *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 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 *dp_bridge = to_dp_bridge(drm_bridge);
+ struct msm_dp *dp = dp_bridge->msm_dp_display;
+
+ msm_dp_display_mode_set(dp, mode, adjusted_mode);
+}
+
+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,
@@ -149,7 +196,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);
}
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 0d026bc9becf777aee192b9200197f477012bf8f..1066bb181a50b462203647618db6386e72a8eb32 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_drm.h
@@ -26,18 +26,6 @@ int msm_dp_bridge_init(struct msm_dp *msm_dp_display, struct drm_device *dev,
bool yuv_supported);
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_state *state);
-void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge,
- struct drm_atomic_state *state);
-void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
- struct drm_atomic_state *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_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.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 11/38] drm/msm/dp: separate dp_display_prepare() into its own API
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (9 preceding siblings ...)
2025-08-25 14:15 ` [PATCH v3 10/38] drm/msm/dp: make bridge helpers use dp_display to allow re-use Yongxing Mou
@ 2025-08-25 14:15 ` Yongxing Mou
2025-08-25 17:39 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 12/38] drm/msm/dp: introduce max_streams for DP controller MST support Yongxing Mou
` (26 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:15 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
dp_display_prepare() only prepares the link in case its not
already ready before dp_display_enable(). Hence separate it into
its own API.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 16 ++++++++++++----
drivers/gpu/drm/msm/dp/dp_display.h | 1 +
drivers/gpu/drm/msm/dp/dp_drm.c | 2 ++
3 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 8e95cfb98bd08088a707ffb5643a5021a288095f..78d932bceb581ee54116926506b1025bd159108f 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1438,7 +1438,7 @@ 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)
+void msm_dp_display_atomic_prepare(struct msm_dp *msm_dp_display)
{
int rc = 0;
struct msm_dp_display_private *dp;
@@ -1449,10 +1449,18 @@ void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display)
msm_dp_hpd_plug_handle(dp, 0);
rc = msm_dp_display_prepare(dp);
- if (rc) {
+ if (rc)
DRM_ERROR("DP display prepare failed, rc=%d\n", rc);
- return;
- }
+
+ 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);
if (msm_dp_display->prepared) {
rc = msm_dp_display_enable(dp);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index e12496ac73f853a2bc120e68eeb84e5a2de6aabe..37c6e87db90ce951274cdae61f26d76dc9ef3840 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -38,6 +38,7 @@ 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_prepare(struct msm_dp *dp_display);
void msm_dp_display_atomic_enable(struct msm_dp *dp_display);
void msm_dp_display_mode_set(struct msm_dp *dp,
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 b6b77ee96c30ca60e31cf76569e262a237493aeb..b0dba97e120566f7376e047b319a60c5bc36ba4e 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -87,6 +87,7 @@ 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_prepare(dp);
msm_dp_display_atomic_enable(dp);
}
@@ -196,6 +197,7 @@ static void msm_edp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
return;
}
+ msm_dp_display_atomic_prepare(dp);
msm_dp_display_atomic_enable(dp);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 12/38] drm/msm/dp: introduce max_streams for DP controller MST support
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (10 preceding siblings ...)
2025-08-25 14:15 ` [PATCH v3 11/38] drm/msm/dp: separate dp_display_prepare() into its own API Yongxing Mou
@ 2025-08-25 14:15 ` Yongxing Mou
2025-08-25 17:42 ` Dmitry Baryshkov
2025-09-02 9:41 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 13/38] drm/msm/dp: introduce stream_id for each DP panel Yongxing Mou
` (25 subsequent siblings)
37 siblings, 2 replies; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:15 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
Introduce the `mst_streams` field in each DP controller descriptor to
specify the number of supported MST streams. Most platforms support 2 or
4 MST streams, while platforms without MST support default to a single
stream (`DEFAULT_STREAM_COUNT = 1`).
This change also accounts for platforms with asymmetric stream support,
e.g., DP0 supporting 4 streams and DP1 supporting 2.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 21 +++++++++++++++++++++
drivers/gpu/drm/msm/dp/dp_display.h | 1 +
2 files changed, 22 insertions(+)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 78d932bceb581ee54116926506b1025bd159108f..a8477a0a180137f15cbb1401c3964636aa32626c 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -33,6 +33,7 @@ module_param(psr_enabled, bool, 0);
MODULE_PARM_DESC(psr_enabled, "enable PSR for eDP and DP displays");
#define HPD_STRING_SIZE 30
+#define DEFAULT_STREAM_COUNT 1
enum {
ISR_DISCONNECTED,
@@ -52,6 +53,7 @@ struct msm_dp_display_private {
bool core_initialized;
bool phy_initialized;
bool audio_supported;
+ bool mst_supported;
struct drm_device *drm_dev;
@@ -84,12 +86,15 @@ struct msm_dp_display_private {
void __iomem *p0_base;
size_t p0_len;
+
+ int max_stream;
};
struct msm_dp_desc {
phys_addr_t io_start;
unsigned int id;
bool wide_bus_supported;
+ int mst_streams;
};
static const struct msm_dp_desc msm_dp_desc_sa8775p[] = {
@@ -1213,6 +1218,15 @@ static int msm_dp_display_get_io(struct msm_dp_display_private *display)
return 0;
}
+int msm_dp_get_mst_max_stream(struct msm_dp *msm_dp_display)
+{
+ struct msm_dp_display_private *dp;
+
+ dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
+
+ return dp->max_stream;
+}
+
static int msm_dp_display_probe(struct platform_device *pdev)
{
int rc = 0;
@@ -1239,6 +1253,13 @@ static int msm_dp_display_probe(struct platform_device *pdev)
dp->msm_dp_display.is_edp =
(dp->msm_dp_display.connector_type == DRM_MODE_CONNECTOR_eDP);
dp->hpd_isr_status = 0;
+ dp->max_stream = DEFAULT_STREAM_COUNT;
+ dp->mst_supported = FALSE;
+
+ if (desc->mst_streams > DEFAULT_STREAM_COUNT) {
+ dp->max_stream = desc->mst_streams;
+ dp->mst_supported = TRUE;
+ }
rc = msm_dp_display_get_io(dp);
if (rc)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 37c6e87db90ce951274cdae61f26d76dc9ef3840..7727cf325a89b4892d2370a5616c4fa76fc88485 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -29,6 +29,7 @@ struct msm_dp {
bool psr_supported;
};
+int msm_dp_get_mst_max_stream(struct msm_dp *msm_dp_display);
int msm_dp_display_get_modes(struct msm_dp *msm_dp_display);
bool msm_dp_display_check_video_test(struct msm_dp *msm_dp_display);
int msm_dp_display_get_test_bpp(struct msm_dp *msm_dp_display);
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 13/38] drm/msm/dp: introduce stream_id for each DP panel
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (11 preceding siblings ...)
2025-08-25 14:15 ` [PATCH v3 12/38] drm/msm/dp: introduce max_streams for DP controller MST support Yongxing Mou
@ 2025-08-25 14:15 ` Yongxing Mou
2025-08-25 17:56 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 14/38] drm/msm/dp: Add support for programming p1/p2/p3 register blocks Yongxing Mou
` (24 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:15 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
With MST, each DP controller can handle multiple streams.
There shall be one dp_panel for each stream but the dp_display
object shall be shared among them. To represent this abstraction,
create a stream_id for each DP panel which shall be set by the
MST stream. For SST, default this to stream 0.
Use the stream ID to control the pixel clock of that respective
stream by extending the clock handles and state tracking of the
DP pixel clock to an array of max supported streams. The maximum
streams currently is 4.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 58 ++++++++++++++++++++++---------------
drivers/gpu/drm/msm/dp/dp_ctrl.h | 3 +-
drivers/gpu/drm/msm/dp/dp_display.c | 27 +++++++++++++++--
drivers/gpu/drm/msm/dp/dp_display.h | 2 ++
drivers/gpu/drm/msm/dp/dp_panel.h | 11 +++++++
5 files changed, 73 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index b25eb2fa2835f660073b5109496ac9f2f4e038d2..d4a74c6b70fb182ad8a0a786f85a0f50982d3858 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -127,7 +127,7 @@ struct msm_dp_ctrl_private {
unsigned int num_link_clks;
struct clk_bulk_data *link_clks;
- struct clk *pixel_clk;
+ struct clk *pixel_clk[DP_STREAM_MAX];
union phy_configure_opts phy_opts;
@@ -139,7 +139,7 @@ struct msm_dp_ctrl_private {
bool core_clks_on;
bool link_clks_on;
- bool stream_clks_on;
+ bool stream_clks_on[DP_STREAM_MAX];
};
static inline u32 msm_dp_read_ahb(const struct msm_dp_ctrl_private *ctrl, u32 offset)
@@ -2162,39 +2162,40 @@ 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)
+static int msm_dp_ctrl_on_pixel_clk(struct msm_dp_ctrl_private *ctrl, unsigned long pixel_rate,
+ enum msm_dp_stream_id stream_id)
{
int ret;
- ret = clk_set_rate(ctrl->pixel_clk, pixel_rate * 1000);
+ ret = clk_set_rate(ctrl->pixel_clk[stream_id], pixel_rate * 1000);
if (ret) {
DRM_ERROR("Failed to set pixel clock rate. ret=%d\n", ret);
return ret;
}
- if (ctrl->stream_clks_on) {
+ if (ctrl->stream_clks_on[stream_id]) {
drm_dbg_dp(ctrl->drm_dev, "pixel clks already enabled\n");
} else {
- ret = clk_prepare_enable(ctrl->pixel_clk);
+ ret = clk_prepare_enable(ctrl->pixel_clk[stream_id]);
if (ret) {
DRM_ERROR("Failed to start pixel clocks. ret=%d\n", ret);
return ret;
}
- ctrl->stream_clks_on = true;
+ ctrl->stream_clks_on[stream_id] = true;
}
return ret;
}
-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, enum msm_dp_stream_id stream_id)
{
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;
+ if (ctrl->stream_clks_on[stream_id]) {
+ clk_disable_unprepare(ctrl->pixel_clk[stream_id]);
+ ctrl->stream_clks_on[stream_id] = false;
}
}
@@ -2214,7 +2215,7 @@ 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_pixel_clk(&ctrl->msm_dp_ctrl);
+ msm_dp_ctrl_off_pixel_clk(&ctrl->msm_dp_ctrl, ctrl->panel->stream_id);
msm_dp_ctrl_off_link(&ctrl->msm_dp_ctrl);
ret = msm_dp_ctrl_on_link(&ctrl->msm_dp_ctrl);
@@ -2224,7 +2225,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 = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate);
+ ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate, ctrl->panel->stream_id);
msm_dp_ctrl_send_phy_test_pattern(ctrl);
@@ -2499,9 +2500,8 @@ int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_li
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",
- ctrl->core_clks_on, ctrl->link_clks_on, ctrl->stream_clks_on);
+ drm_dbg_dp(ctrl->drm_dev, "core_clk_on=%d link_clk_on=%d\n",
+ ctrl->core_clks_on, ctrl->link_clks_on);
if (!ctrl->link_clks_on) { /* link clk is off */
ret = msm_dp_ctrl_enable_mainlink_clocks(ctrl);
@@ -2540,7 +2540,7 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
drm_dbg_dp(ctrl->drm_dev, "pixel_rate=%lu\n", pixel_rate);
- ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate);
+ ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate, msm_dp_panel->stream_id);
if (ret) {
DRM_ERROR("failed to enable pixel clk\n");
return ret;
@@ -2604,8 +2604,6 @@ void msm_dp_ctrl_off_link(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_ctrl_mainlink_disable(ctrl);
msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl);
@@ -2677,10 +2675,11 @@ static const char *ctrl_clks[] = {
"ctrl_link_iface",
};
-static int msm_dp_ctrl_clk_init(struct msm_dp_ctrl *msm_dp_ctrl)
+static int msm_dp_ctrl_clk_init(struct msm_dp_ctrl *msm_dp_ctrl, int max_stream)
{
struct msm_dp_ctrl_private *ctrl;
struct device *dev;
+ char stream_id_str[15];
int i, rc;
ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
@@ -2710,9 +2709,19 @@ static int msm_dp_ctrl_clk_init(struct msm_dp_ctrl *msm_dp_ctrl)
if (rc)
return rc;
- ctrl->pixel_clk = devm_clk_get(dev, "stream_pixel");
- if (IS_ERR(ctrl->pixel_clk))
- return PTR_ERR(ctrl->pixel_clk);
+ ctrl->pixel_clk[DP_STREAM_0] = devm_clk_get(dev, "stream_pixel");
+ if (IS_ERR(ctrl->pixel_clk[DP_STREAM_0]))
+ return PTR_ERR(ctrl->pixel_clk[DP_STREAM_0]);
+
+ for (i = DP_STREAM_1; i < max_stream; i++) {
+ sprintf(stream_id_str, "stream_%d_pixel", i);
+ ctrl->pixel_clk[i] = devm_clk_get(dev, stream_id_str);
+
+ if (IS_ERR(ctrl->pixel_clk[i])) {
+ DRM_DEBUG_DP("failed to get stream %d pixel clock", i);
+ break;
+ }
+ }
return 0;
}
@@ -2720,6 +2729,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 phy *phy,
+ int max_stream,
void __iomem *ahb_base,
void __iomem *link_base)
{
@@ -2762,7 +2772,7 @@ struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link
ctrl->ahb_base = ahb_base;
ctrl->link_base = link_base;
- ret = msm_dp_ctrl_clk_init(&ctrl->msm_dp_ctrl);
+ ret = msm_dp_ctrl_clk_init(&ctrl->msm_dp_ctrl, max_stream);
if (ret) {
dev_err(dev, "failed to init clocks\n");
return ERR_PTR(ret);
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index 6ff3e9d9fa6ff0afa325a7a6f72a15009635f340..32ff1455caf0e7fcb1bd74b1f3192c6c3c03ee74 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -20,7 +20,7 @@ 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, struct msm_dp_panel *msm_dp_panel);
int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train);
void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl);
-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, enum msm_dp_stream_id stream_id);
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);
@@ -29,6 +29,7 @@ struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev,
struct msm_dp_panel *panel,
struct drm_dp_aux *aux,
struct phy *phy,
+ int max_stream,
void __iomem *ahb_base,
void __iomem *link_base);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index a8477a0a180137f15cbb1401c3964636aa32626c..3422f18bdec71a99407edfe943d31957d0e8847a 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -628,7 +628,7 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp)
}
dp->ctrl = msm_dp_ctrl_get(dev, dp->link, dp->panel, dp->aux,
- phy, dp->ahb_base, dp->link_base);
+ phy, dp->max_stream, dp->ahb_base, dp->link_base);
if (IS_ERR(dp->ctrl)) {
rc = PTR_ERR(dp->ctrl);
DRM_ERROR("failed to initialize ctrl, rc = %d\n", rc);
@@ -789,7 +789,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_pixel_clk(dp->ctrl);
+ msm_dp_ctrl_off_pixel_clk(dp->ctrl, dp->panel->stream_id);
msm_dp_ctrl_off_link(dp->ctrl);
/* re-init the PHY so that we can listen to Dongle disconnect */
msm_dp_ctrl_reinit_phy(dp->ctrl);
@@ -798,7 +798,7 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
* unplugged interrupt
* dongle unplugged out of DUT
*/
- msm_dp_ctrl_off_pixel_clk(dp->ctrl);
+ msm_dp_ctrl_off_pixel_clk(dp->ctrl, dp->panel->stream_id);
msm_dp_ctrl_off_link(dp->ctrl);
msm_dp_display_host_phy_exit(dp);
}
@@ -809,6 +809,25 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
return 0;
}
+int msm_dp_display_set_stream_id(struct msm_dp *dp,
+ struct msm_dp_panel *panel, enum msm_dp_stream_id stream_id)
+{
+ int rc = 0;
+ struct msm_dp_display_private *msm_dp_display;
+
+ msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
+
+ if (!msm_dp_display) {
+ DRM_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ if (panel)
+ panel->stream_id = stream_id;
+
+ return rc;
+}
+
/**
* msm_dp_bridge_mode_valid - callback to determine if specified mode is valid
* @dp: Pointer to dp display structure
@@ -1483,6 +1502,8 @@ 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);
+ msm_dp_display_set_stream_id(msm_dp_display, dp->panel, 0);
+
if (msm_dp_display->prepared) {
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 7727cf325a89b4892d2370a5616c4fa76fc88485..a839d0a3941eac3e277185e42fddea15ca05a17f 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -47,5 +47,7 @@ void msm_dp_display_mode_set(struct msm_dp *dp,
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);
+int msm_dp_display_set_stream_id(struct msm_dp *dp,
+ struct msm_dp_panel *panel, enum msm_dp_stream_id stream_id);
#endif /* _DP_DISPLAY_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
index 618d0253b525308b392b9282098e8ca78bf32f1c..23b3e78e40479d133893a8afe1a69cfe8c16abdf 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -27,6 +27,15 @@ struct msm_dp_panel_psr {
u8 capabilities;
};
+/* stream id */
+enum msm_dp_stream_id {
+ DP_STREAM_0,
+ DP_STREAM_1,
+ DP_STREAM_2,
+ DP_STREAM_3,
+ DP_STREAM_MAX,
+};
+
struct msm_dp_panel {
/* dpcd raw data */
u8 dpcd[DP_RECEIVER_CAP_SIZE];
@@ -40,6 +49,8 @@ struct msm_dp_panel {
bool vsc_sdp_supported;
u32 hw_revision;
+ enum msm_dp_stream_id stream_id;
+
u32 max_dp_lanes;
u32 max_dp_link_rate;
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 14/38] drm/msm/dp: Add support for programming p1/p2/p3 register blocks
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (12 preceding siblings ...)
2025-08-25 14:15 ` [PATCH v3 13/38] drm/msm/dp: introduce stream_id for each DP panel Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-25 17:59 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 15/38] drm/msm/dp: use stream_id to change offsets in dp_catalog Yongxing Mou
` (23 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
QCS8300 supports 4-stream MST. This patch adds support for the additional
pixel register blocks (p1, p2, p3), enabling multi-stream configurations.
To reduce code duplication, introduce helper functions msm_dp_read_pn and
msm_dp_write_pn. All pixel clocks (PCLKs) share the same register layout,
but use different base addresses.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 39 +++++++++++++--------
drivers/gpu/drm/msm/dp/dp_panel.c | 68 ++++++++++++++++++-------------------
2 files changed, 59 insertions(+), 48 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 3422f18bdec71a99407edfe943d31957d0e8847a..935a0c57a928b15a1e9a6f1fab2576b7b09acb8e 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -84,8 +84,8 @@ struct msm_dp_display_private {
void __iomem *link_base;
size_t link_len;
- void __iomem *p0_base;
- size_t p0_len;
+ void __iomem *pixel_base[DP_STREAM_MAX];
+ size_t pixel_len;
int max_stream;
};
@@ -619,7 +619,7 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp)
goto error_link;
}
- dp->panel = msm_dp_panel_get(dev, dp->aux, dp->link, dp->link_base, dp->p0_base);
+ dp->panel = msm_dp_panel_get(dev, dp->aux, dp->link, dp->link_base, dp->pixel_base);
if (IS_ERR(dp->panel)) {
rc = PTR_ERR(dp->panel);
DRM_ERROR("failed to initialize panel, rc = %d\n", rc);
@@ -937,8 +937,8 @@ void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp)
msm_dp_display->aux_base, "dp_aux");
msm_disp_snapshot_add_block(disp_state, msm_dp_display->link_len,
msm_dp_display->link_base, "dp_link");
- msm_disp_snapshot_add_block(disp_state, msm_dp_display->p0_len,
- msm_dp_display->p0_base, "dp_p0");
+ msm_disp_snapshot_add_block(disp_state, msm_dp_display->pixel_len,
+ msm_dp_display->pixel_base[0], "dp_p0");
}
void msm_dp_display_set_psr(struct msm_dp *msm_dp_display, bool enter)
@@ -1181,12 +1181,13 @@ static void __iomem *msm_dp_ioremap(struct platform_device *pdev, int idx, size_
#define DP_DEFAULT_AUX_SIZE 0x0200
#define DP_DEFAULT_LINK_OFFSET 0x0400
#define DP_DEFAULT_LINK_SIZE 0x0C00
-#define DP_DEFAULT_P0_OFFSET 0x1000
-#define DP_DEFAULT_P0_SIZE 0x0400
+#define DP_DEFAULT_PIXEL_OFFSET 0x1000
+#define DP_DEFAULT_PIXEL_SIZE 0x0400
static int msm_dp_display_get_io(struct msm_dp_display_private *display)
{
struct platform_device *pdev = display->msm_dp_display.pdev;
+ int i;
display->ahb_base = msm_dp_ioremap(pdev, 0, &display->ahb_len);
if (IS_ERR(display->ahb_base))
@@ -1206,7 +1207,7 @@ static int msm_dp_display_get_io(struct msm_dp_display_private *display)
* reg is specified, so fill in the sub-region offsets and
* lengths based on this single region.
*/
- if (display->ahb_len < DP_DEFAULT_P0_OFFSET + DP_DEFAULT_P0_SIZE) {
+ if (display->ahb_len < DP_DEFAULT_PIXEL_OFFSET + DP_DEFAULT_PIXEL_SIZE) {
DRM_ERROR("legacy memory region not large enough\n");
return -EINVAL;
}
@@ -1216,8 +1217,10 @@ static int msm_dp_display_get_io(struct msm_dp_display_private *display)
display->aux_len = DP_DEFAULT_AUX_SIZE;
display->link_base = display->ahb_base + DP_DEFAULT_LINK_OFFSET;
display->link_len = DP_DEFAULT_LINK_SIZE;
- display->p0_base = display->ahb_base + DP_DEFAULT_P0_OFFSET;
- display->p0_len = DP_DEFAULT_P0_SIZE;
+ for (i = DP_STREAM_0; i < display->max_stream; i++)
+ display->pixel_base[i] = display->ahb_base +
+ (i+1) * DP_DEFAULT_PIXEL_OFFSET;
+ display->pixel_len = DP_DEFAULT_PIXEL_SIZE;
return 0;
}
@@ -1228,10 +1231,18 @@ static int msm_dp_display_get_io(struct msm_dp_display_private *display)
return PTR_ERR(display->link_base);
}
- display->p0_base = msm_dp_ioremap(pdev, 3, &display->p0_len);
- if (IS_ERR(display->p0_base)) {
- DRM_ERROR("unable to remap p0 region: %pe\n", display->p0_base);
- return PTR_ERR(display->p0_base);
+ display->pixel_base[0] = msm_dp_ioremap(pdev, 3, &display->pixel_len);
+ if (IS_ERR(display->pixel_base[0])) {
+ DRM_ERROR("unable to remap p0 region: %pe\n", display->pixel_base[0]);
+ return PTR_ERR(display->pixel_base[0]);
+ }
+
+ for (i = DP_STREAM_1; i < display->max_stream; i++) {
+ /* pixels clk reg index start from 3*/
+ display->pixel_base[i] = msm_dp_ioremap(pdev, i + 3, &display->pixel_len);
+ if (IS_ERR(display->pixel_base[i]))
+ DRM_DEBUG_DP("unable to remap p%d region: %pe\n", i,
+ display->pixel_base[i]);
}
return 0;
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index eae125972934bb2fb3b716dc47ae71cd0421bd1a..e8c1cf0c7dab7217b8bfe7ecd586af33d7547ca9 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -26,7 +26,7 @@ struct msm_dp_panel_private {
struct drm_dp_aux *aux;
struct msm_dp_link *link;
void __iomem *link_base;
- void __iomem *p0_base;
+ void __iomem *pixel_base[DP_STREAM_MAX];
bool panel_on;
};
@@ -45,24 +45,24 @@ static inline void msm_dp_write_link(struct msm_dp_panel_private *panel,
writel(data, panel->link_base + offset);
}
-static inline void msm_dp_write_p0(struct msm_dp_panel_private *panel,
+static inline void msm_dp_write_pn(struct msm_dp_panel_private *panel,
u32 offset, u32 data)
{
/*
* To make sure interface reg writes happens before any other operation,
* this function uses writel() instread of writel_relaxed()
*/
- writel(data, panel->p0_base + offset);
+ writel(data, panel->pixel_base[panel->msm_dp_panel.stream_id] + offset);
}
-static inline u32 msm_dp_read_p0(struct msm_dp_panel_private *panel,
+static inline u32 msm_dp_read_pn(struct msm_dp_panel_private *panel,
u32 offset)
{
/*
* To make sure interface reg writes happens before any other operation,
* this function uses writel() instread of writel_relaxed()
*/
- return readl_relaxed(panel->p0_base + offset);
+ return readl_relaxed(panel->pixel_base[panel->msm_dp_panel.stream_id] + offset);
}
static void msm_dp_panel_read_psr_cap(struct msm_dp_panel_private *panel)
@@ -297,33 +297,33 @@ static void msm_dp_panel_tpg_enable(struct msm_dp_panel *msm_dp_panel,
display_hctl = (hsync_end_x << 16) | hsync_start_x;
- msm_dp_write_p0(panel, MMSS_DP_INTF_HSYNC_CTL, hsync_ctl);
- msm_dp_write_p0(panel, MMSS_DP_INTF_VSYNC_PERIOD_F0, vsync_period *
+ msm_dp_write_pn(panel, MMSS_DP_INTF_HSYNC_CTL, hsync_ctl);
+ msm_dp_write_pn(panel, MMSS_DP_INTF_VSYNC_PERIOD_F0, vsync_period *
hsync_period);
- msm_dp_write_p0(panel, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0, v_sync_width *
+ msm_dp_write_pn(panel, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0, v_sync_width *
hsync_period);
- msm_dp_write_p0(panel, MMSS_DP_INTF_VSYNC_PERIOD_F1, 0);
- msm_dp_write_p0(panel, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1, 0);
- msm_dp_write_p0(panel, MMSS_DP_INTF_DISPLAY_HCTL, display_hctl);
- msm_dp_write_p0(panel, MMSS_DP_INTF_ACTIVE_HCTL, 0);
- msm_dp_write_p0(panel, MMSS_INTF_DISPLAY_V_START_F0, display_v_start);
- msm_dp_write_p0(panel, MMSS_DP_INTF_DISPLAY_V_END_F0, display_v_end);
- msm_dp_write_p0(panel, MMSS_INTF_DISPLAY_V_START_F1, 0);
- msm_dp_write_p0(panel, MMSS_DP_INTF_DISPLAY_V_END_F1, 0);
- msm_dp_write_p0(panel, MMSS_DP_INTF_ACTIVE_V_START_F0, 0);
- msm_dp_write_p0(panel, MMSS_DP_INTF_ACTIVE_V_END_F0, 0);
- msm_dp_write_p0(panel, MMSS_DP_INTF_ACTIVE_V_START_F1, 0);
- msm_dp_write_p0(panel, MMSS_DP_INTF_ACTIVE_V_END_F1, 0);
- msm_dp_write_p0(panel, MMSS_DP_INTF_POLARITY_CTL, 0);
-
- msm_dp_write_p0(panel, MMSS_DP_TPG_MAIN_CONTROL,
+ msm_dp_write_pn(panel, MMSS_DP_INTF_VSYNC_PERIOD_F1, 0);
+ msm_dp_write_pn(panel, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1, 0);
+ msm_dp_write_pn(panel, MMSS_DP_INTF_DISPLAY_HCTL, display_hctl);
+ msm_dp_write_pn(panel, MMSS_DP_INTF_ACTIVE_HCTL, 0);
+ msm_dp_write_pn(panel, MMSS_INTF_DISPLAY_V_START_F0, display_v_start);
+ msm_dp_write_pn(panel, MMSS_DP_INTF_DISPLAY_V_END_F0, display_v_end);
+ msm_dp_write_pn(panel, MMSS_INTF_DISPLAY_V_START_F1, 0);
+ msm_dp_write_pn(panel, MMSS_DP_INTF_DISPLAY_V_END_F1, 0);
+ msm_dp_write_pn(panel, MMSS_DP_INTF_ACTIVE_V_START_F0, 0);
+ msm_dp_write_pn(panel, MMSS_DP_INTF_ACTIVE_V_END_F0, 0);
+ msm_dp_write_pn(panel, MMSS_DP_INTF_ACTIVE_V_START_F1, 0);
+ msm_dp_write_pn(panel, MMSS_DP_INTF_ACTIVE_V_END_F1, 0);
+ msm_dp_write_pn(panel, MMSS_DP_INTF_POLARITY_CTL, 0);
+
+ msm_dp_write_pn(panel, MMSS_DP_TPG_MAIN_CONTROL,
DP_TPG_CHECKERED_RECT_PATTERN);
- msm_dp_write_p0(panel, MMSS_DP_TPG_VIDEO_CONFIG,
+ msm_dp_write_pn(panel, MMSS_DP_TPG_VIDEO_CONFIG,
DP_TPG_VIDEO_CONFIG_BPP_8BIT |
DP_TPG_VIDEO_CONFIG_RGB);
- msm_dp_write_p0(panel, MMSS_DP_BIST_ENABLE,
+ msm_dp_write_pn(panel, MMSS_DP_BIST_ENABLE,
DP_BIST_ENABLE_DPBIST_EN);
- msm_dp_write_p0(panel, MMSS_DP_TIMING_ENGINE_EN,
+ msm_dp_write_pn(panel, MMSS_DP_TIMING_ENGINE_EN,
DP_TIMING_ENGINE_EN_EN);
drm_dbg_dp(panel->drm_dev, "%s: enabled tpg\n", __func__);
}
@@ -333,9 +333,9 @@ static void msm_dp_panel_tpg_disable(struct msm_dp_panel *msm_dp_panel)
struct msm_dp_panel_private *panel =
container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
- msm_dp_write_p0(panel, MMSS_DP_TPG_MAIN_CONTROL, 0x0);
- msm_dp_write_p0(panel, MMSS_DP_BIST_ENABLE, 0x0);
- msm_dp_write_p0(panel, MMSS_DP_TIMING_ENGINE_EN, 0x0);
+ msm_dp_write_pn(panel, MMSS_DP_TPG_MAIN_CONTROL, 0x0);
+ msm_dp_write_pn(panel, MMSS_DP_BIST_ENABLE, 0x0);
+ msm_dp_write_pn(panel, MMSS_DP_TIMING_ENGINE_EN, 0x0);
}
void msm_dp_panel_tpg_config(struct msm_dp_panel *msm_dp_panel, bool enable)
@@ -369,7 +369,7 @@ void msm_dp_panel_clear_dsc_dto(struct msm_dp_panel *msm_dp_panel)
struct msm_dp_panel_private *panel =
container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
- msm_dp_write_p0(panel, MMSS_DP_DSC_DTO, 0x0);
+ msm_dp_write_pn(panel, MMSS_DP_DSC_DTO, 0x0);
}
static void msm_dp_panel_send_vsc_sdp(struct msm_dp_panel_private *panel, struct dp_sdp *vsc_sdp)
@@ -559,7 +559,7 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en)
msm_dp_write_link(panel, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY, width_blanking);
msm_dp_write_link(panel, REG_DP_ACTIVE_HOR_VER, msm_dp_active);
- reg = msm_dp_read_p0(panel, MMSS_DP_INTF_CONFIG);
+ reg = msm_dp_read_pn(panel, MMSS_DP_INTF_CONFIG);
if (wide_bus_en)
reg |= DP_INTF_CONFIG_DATABUS_WIDEN;
else
@@ -567,7 +567,7 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en)
drm_dbg_dp(panel->drm_dev, "wide_bus_en=%d reg=%#x\n", wide_bus_en, reg);
- msm_dp_write_p0(panel, MMSS_DP_INTF_CONFIG, reg);
+ msm_dp_write_pn(panel, MMSS_DP_INTF_CONFIG, reg);
if (msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
msm_dp_panel_setup_vsc_sdp_yuv_420(msm_dp_panel);
@@ -673,7 +673,7 @@ static int msm_dp_panel_parse_dt(struct msm_dp_panel *msm_dp_panel)
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 __iomem *pixel_base[])
{
struct msm_dp_panel_private *panel;
struct msm_dp_panel *msm_dp_panel;
@@ -692,7 +692,7 @@ struct msm_dp_panel *msm_dp_panel_get(struct device *dev, struct drm_dp_aux *aux
panel->aux = aux;
panel->link = link;
panel->link_base = link_base;
- panel->p0_base = p0_base;
+ memcpy(panel->pixel_base, pixel_base, sizeof(panel->pixel_base));
msm_dp_panel = &panel->msm_dp_panel;
msm_dp_panel->max_bw_code = DP_LINK_BW_8_1;
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 15/38] drm/msm/dp: use stream_id to change offsets in dp_catalog
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (13 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 14/38] drm/msm/dp: Add support for programming p1/p2/p3 register blocks Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-25 18:01 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 16/38] drm/msm/dp: Add catalog support for 3rd/4th stream MST Yongxing Mou
` (22 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
Use the dp_panel's stream_id to adjust the offsets for stream 1
which will be used for MST in the dp_catalog. Stream 1 share the
same link clk with stream 0 with different reg offset. Also add
additional register defines for stream 1.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 24 ++++++++++---
drivers/gpu/drm/msm/dp/dp_panel.c | 72 +++++++++++++++++++++++++++------------
drivers/gpu/drm/msm/dp/dp_reg.h | 9 +++++
3 files changed, 79 insertions(+), 26 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index d4a74c6b70fb182ad8a0a786f85a0f50982d3858..b8b6a09966aed96f705bdd54cb16ea63e5f0141f 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -384,6 +384,7 @@ 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;
+ u32 reg_offset = 0;
config = msm_dp_read_link(ctrl, REG_DP_CONFIGURATION_CTRL);
@@ -400,7 +401,8 @@ static void msm_dp_ctrl_config_ctrl_streams(struct msm_dp_ctrl_private *ctrl,
drm_dbg_dp(ctrl->drm_dev, "stream DP_CONFIGURATION_CTRL=0x%x\n", config);
- msm_dp_write_link(ctrl, REG_DP_CONFIGURATION_CTRL, config);
+ if (msm_dp_panel->stream_id == DP_STREAM_1)
+ reg_offset = REG_DP1_CONFIGURATION_CTRL - REG_DP_CONFIGURATION_CTRL;
}
static void msm_dp_ctrl_config_ctrl_link(struct msm_dp_ctrl_private *ctrl)
@@ -451,12 +453,16 @@ 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;
+ u32 reg_offset = 0;
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);
+ if (msm_dp_panel->stream_id == DP_STREAM_1)
+ reg_offset = REG_DP1_MISC1_MISC0 - REG_DP_MISC1_MISC0;
+
+ misc_val = msm_dp_read_link(ctrl, REG_DP_MISC1_MISC0 + reg_offset);
/* clear bpp bits */
misc_val &= ~(0x07 << DP_MISC0_TEST_BITS_DEPTH_SHIFT);
@@ -466,7 +472,7 @@ static void msm_dp_ctrl_config_misc1_misc0(struct msm_dp_ctrl_private *ctrl,
misc_val |= DP_MISC0_SYNCHRONOUS_CLK;
drm_dbg_dp(ctrl->drm_dev, "misc settings = 0x%x\n", misc_val);
- msm_dp_write_link(ctrl, REG_DP_MISC1_MISC0, misc_val);
+ msm_dp_write_link(ctrl, REG_DP_MISC1_MISC0 + reg_offset, misc_val);
}
static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl,
@@ -2431,6 +2437,7 @@ static int msm_dp_ctrl_link_retrain(struct msm_dp_ctrl_private *ctrl)
}
static void msm_dp_ctrl_config_msa(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *msm_dp_panel,
u32 rate, u32 stream_rate_khz,
bool is_ycbcr_420)
{
@@ -2440,6 +2447,12 @@ static void msm_dp_ctrl_config_msa(struct msm_dp_ctrl_private *ctrl,
u32 const link_rate_hbr2 = 540000;
u32 const link_rate_hbr3 = 810000;
unsigned long den, num;
+ u32 mvid_reg_off = 0, nvid_reg_off = 0;
+
+ if (msm_dp_panel->stream_id == DP_STREAM_1) {
+ mvid_reg_off = REG_DP1_SOFTWARE_MVID - REG_DP_SOFTWARE_MVID;
+ nvid_reg_off = REG_DP1_SOFTWARE_NVID - REG_DP_SOFTWARE_NVID;
+ }
if (rate == link_rate_hbr3)
pixel_div = 6;
@@ -2482,8 +2495,8 @@ static void msm_dp_ctrl_config_msa(struct msm_dp_ctrl_private *ctrl,
nvid *= 3;
drm_dbg_dp(ctrl->drm_dev, "mvid=0x%x, nvid=0x%x\n", mvid, nvid);
- msm_dp_write_link(ctrl, REG_DP_SOFTWARE_MVID, mvid);
- msm_dp_write_link(ctrl, REG_DP_SOFTWARE_NVID, nvid);
+ msm_dp_write_link(ctrl, REG_DP_SOFTWARE_MVID + mvid_reg_off, mvid);
+ msm_dp_write_link(ctrl, REG_DP_SOFTWARE_NVID + nvid_reg_off, nvid);
}
int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train)
@@ -2559,6 +2572,7 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
msm_dp_ctrl_configure_source_params(ctrl, msm_dp_panel);
msm_dp_ctrl_config_msa(ctrl,
+ msm_dp_panel,
ctrl->link->link_params.rate,
pixel_rate_orig,
msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420);
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index e8c1cf0c7dab7217b8bfe7ecd586af33d7547ca9..d1af389dffcfee2d21a616de6ee027374997aaee 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -377,27 +377,35 @@ static void msm_dp_panel_send_vsc_sdp(struct msm_dp_panel_private *panel, struct
u32 header[2];
u32 val;
int i;
+ u32 offset = 0;
+
+ if (panel->msm_dp_panel.stream_id == DP_STREAM_1)
+ offset = MMSS_DP1_GENERIC0_0 - MMSS_DP_GENERIC0_0;
msm_dp_utils_pack_sdp_header(&vsc_sdp->sdp_header, header);
- msm_dp_write_link(panel, MMSS_DP_GENERIC0_0, header[0]);
- msm_dp_write_link(panel, MMSS_DP_GENERIC0_1, header[1]);
+ msm_dp_write_link(panel, MMSS_DP_GENERIC0_0 + offset, header[0]);
+ msm_dp_write_link(panel, MMSS_DP_GENERIC0_1 + offset, header[1]);
for (i = 0; i < sizeof(vsc_sdp->db); i += 4) {
val = ((vsc_sdp->db[i]) | (vsc_sdp->db[i + 1] << 8) | (vsc_sdp->db[i + 2] << 16) |
(vsc_sdp->db[i + 3] << 24));
- msm_dp_write_link(panel, MMSS_DP_GENERIC0_2 + i, val);
+ msm_dp_write_link(panel, MMSS_DP_GENERIC0_2 + i + offset, val);
}
}
static void msm_dp_panel_update_sdp(struct msm_dp_panel_private *panel)
{
u32 hw_revision = panel->msm_dp_panel.hw_revision;
+ u32 offset = 0;
+
+ if (panel->msm_dp_panel.stream_id == DP_STREAM_1)
+ offset = MMSS_DP1_SDP_CFG3 - MMSS_DP_SDP_CFG3;
if (hw_revision >= DP_HW_VERSION_1_0 &&
hw_revision < DP_HW_VERSION_1_2) {
- msm_dp_write_link(panel, MMSS_DP_SDP_CFG3, UPDATE_SDP);
- msm_dp_write_link(panel, MMSS_DP_SDP_CFG3, 0x0);
+ msm_dp_write_link(panel, MMSS_DP_SDP_CFG3 + offset, UPDATE_SDP);
+ msm_dp_write_link(panel, MMSS_DP_SDP_CFG3 + offset, 0x0);
}
}
@@ -406,16 +414,25 @@ void msm_dp_panel_enable_vsc_sdp(struct msm_dp_panel *msm_dp_panel, struct dp_sd
struct msm_dp_panel_private *panel =
container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
u32 cfg, cfg2, misc;
+ u32 misc_reg_offset = 0;
+ u32 sdp_cfg_offset = 0;
+ u32 sdp_cfg2_offset = 0;
+
+ if (msm_dp_panel->stream_id == DP_STREAM_1) {
+ misc_reg_offset = REG_DP1_MISC1_MISC0 - REG_DP_MISC1_MISC0;
+ sdp_cfg_offset = MMSS_DP1_SDP_CFG - MMSS_DP_SDP_CFG;
+ sdp_cfg2_offset = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
+ }
- cfg = msm_dp_read_link(panel, MMSS_DP_SDP_CFG);
- cfg2 = msm_dp_read_link(panel, MMSS_DP_SDP_CFG2);
- misc = msm_dp_read_link(panel, REG_DP_MISC1_MISC0);
+ cfg = msm_dp_read_link(panel, MMSS_DP_SDP_CFG + sdp_cfg_offset);
+ cfg2 = msm_dp_read_link(panel, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset);
+ misc = msm_dp_read_link(panel, REG_DP_MISC1_MISC0 + misc_reg_offset);
cfg |= GEN0_SDP_EN;
- msm_dp_write_link(panel, MMSS_DP_SDP_CFG, cfg);
+ msm_dp_write_link(panel, MMSS_DP_SDP_CFG + sdp_cfg_offset, cfg);
cfg2 |= GENERIC0_SDPSIZE_VALID;
- msm_dp_write_link(panel, MMSS_DP_SDP_CFG2, cfg2);
+ msm_dp_write_link(panel, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset, cfg2);
msm_dp_panel_send_vsc_sdp(panel, vsc_sdp);
@@ -425,7 +442,7 @@ void msm_dp_panel_enable_vsc_sdp(struct msm_dp_panel *msm_dp_panel, struct dp_sd
drm_dbg_dp(panel->drm_dev, "vsc sdp enable=1\n");
pr_debug("misc settings = 0x%x\n", misc);
- msm_dp_write_link(panel, REG_DP_MISC1_MISC0, misc);
+ msm_dp_write_link(panel, REG_DP_MISC1_MISC0 + misc_reg_offset, misc);
msm_dp_panel_update_sdp(panel);
}
@@ -435,16 +452,25 @@ void msm_dp_panel_disable_vsc_sdp(struct msm_dp_panel *msm_dp_panel)
struct msm_dp_panel_private *panel =
container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
u32 cfg, cfg2, misc;
+ u32 misc_reg_offset = 0;
+ u32 sdp_cfg_offset = 0;
+ u32 sdp_cfg2_offset = 0;
+
+ if (msm_dp_panel->stream_id == DP_STREAM_1) {
+ misc_reg_offset = REG_DP1_MISC1_MISC0 - REG_DP_MISC1_MISC0;
+ sdp_cfg_offset = MMSS_DP1_SDP_CFG - MMSS_DP_SDP_CFG;
+ sdp_cfg2_offset = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
+ }
- cfg = msm_dp_read_link(panel, MMSS_DP_SDP_CFG);
- cfg2 = msm_dp_read_link(panel, MMSS_DP_SDP_CFG2);
- misc = msm_dp_read_link(panel, REG_DP_MISC1_MISC0);
+ cfg = msm_dp_read_link(panel, MMSS_DP_SDP_CFG + sdp_cfg_offset);
+ cfg2 = msm_dp_read_link(panel, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset);
+ misc = msm_dp_read_link(panel, REG_DP_MISC1_MISC0 + misc_reg_offset);
cfg &= ~GEN0_SDP_EN;
- msm_dp_write_link(panel, MMSS_DP_SDP_CFG, cfg);
+ msm_dp_write_link(panel, MMSS_DP_SDP_CFG + sdp_cfg_offset, cfg);
cfg2 &= ~GENERIC0_SDPSIZE_VALID;
- msm_dp_write_link(panel, MMSS_DP_SDP_CFG2, cfg2);
+ msm_dp_write_link(panel, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset, cfg2);
/* switch back to MSA */
misc &= ~DP_MISC1_VSC_SDP;
@@ -452,7 +478,7 @@ void msm_dp_panel_disable_vsc_sdp(struct msm_dp_panel *msm_dp_panel)
drm_dbg_dp(panel->drm_dev, "vsc sdp enable=0\n");
pr_debug("misc settings = 0x%x\n", misc);
- msm_dp_write_link(panel, REG_DP_MISC1_MISC0, misc);
+ msm_dp_write_link(panel, REG_DP_MISC1_MISC0 + misc_reg_offset, misc);
msm_dp_panel_update_sdp(panel);
}
@@ -510,6 +536,7 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en)
u32 msm_dp_active;
u32 total;
u32 reg;
+ u32 offset = 0;
panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
drm_mode = &panel->msm_dp_panel.msm_dp_mode.drm_mode;
@@ -524,6 +551,9 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en)
drm_mode->vsync_start - drm_mode->vdisplay,
drm_mode->vsync_end - drm_mode->vsync_start);
+ if (msm_dp_panel->stream_id == DP_STREAM_1)
+ offset = REG_DP1_TOTAL_HOR_VER - REG_DP_TOTAL_HOR_VER;
+
total_hor = drm_mode->htotal;
total_ver = drm_mode->vtotal;
@@ -554,10 +584,10 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en)
msm_dp_active = data;
- msm_dp_write_link(panel, REG_DP_TOTAL_HOR_VER, total);
- msm_dp_write_link(panel, REG_DP_START_HOR_VER_FROM_SYNC, sync_start);
- msm_dp_write_link(panel, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY, width_blanking);
- msm_dp_write_link(panel, REG_DP_ACTIVE_HOR_VER, msm_dp_active);
+ msm_dp_write_link(panel, REG_DP_TOTAL_HOR_VER + offset, total);
+ msm_dp_write_link(panel, REG_DP_START_HOR_VER_FROM_SYNC + offset, sync_start);
+ msm_dp_write_link(panel, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY + offset, width_blanking);
+ msm_dp_write_link(panel, REG_DP_ACTIVE_HOR_VER + offset, msm_dp_active);
reg = msm_dp_read_pn(panel, MMSS_DP_INTF_CONFIG);
if (wide_bus_en)
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index b851efc132ea03884ce2563990fbc24c9577e724..43a9ce0539906e1f185abf250fdf161e462d9645 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -141,6 +141,7 @@
#define DP_STATE_CTRL_PUSH_IDLE (0x00000100)
#define REG_DP_CONFIGURATION_CTRL (0x00000008)
+#define REG_DP1_CONFIGURATION_CTRL (0x00000400)
#define DP_CONFIGURATION_CTRL_SYNC_ASYNC_CLK (0x00000001)
#define DP_CONFIGURATION_CTRL_STATIC_DYNAMIC_CN (0x00000002)
#define DP_CONFIGURATION_CTRL_P_INTERLACED (0x00000004)
@@ -159,11 +160,15 @@
#define REG_DP_SOFTWARE_MVID (0x00000010)
#define REG_DP_SOFTWARE_NVID (0x00000018)
#define REG_DP_TOTAL_HOR_VER (0x0000001C)
+#define REG_DP1_SOFTWARE_MVID (0x00000414)
+#define REG_DP1_SOFTWARE_NVID (0x00000418)
+#define REG_DP1_TOTAL_HOR_VER (0x0000041C)
#define REG_DP_START_HOR_VER_FROM_SYNC (0x00000020)
#define REG_DP_HSYNC_VSYNC_WIDTH_POLARITY (0x00000024)
#define REG_DP_ACTIVE_HOR_VER (0x00000028)
#define REG_DP_MISC1_MISC0 (0x0000002C)
+#define REG_DP1_MISC1_MISC0 (0x0000042C)
#define DP_MISC0_SYNCHRONOUS_CLK (0x00000001)
#define DP_MISC0_COLORIMETRY_CFG_SHIFT (0x00000001)
#define DP_MISC0_TEST_BITS_DEPTH_SHIFT (0x00000005)
@@ -230,8 +235,10 @@
#define MMSS_DP_AUDIO_CTRL_RESET (0x00000214)
#define MMSS_DP_SDP_CFG (0x00000228)
+#define MMSS_DP1_SDP_CFG (0x000004E0)
#define GEN0_SDP_EN (0x00020000)
#define MMSS_DP_SDP_CFG2 (0x0000022C)
+#define MMSS_DP1_SDP_CFG2 (0x000004E4)
#define MMSS_DP_AUDIO_TIMESTAMP_0 (0x00000230)
#define MMSS_DP_AUDIO_TIMESTAMP_1 (0x00000234)
#define GENERIC0_SDPSIZE_VALID (0x00010000)
@@ -240,6 +247,7 @@
#define MMSS_DP_AUDIO_STREAM_1 (0x00000244)
#define MMSS_DP_SDP_CFG3 (0x0000024c)
+#define MMSS_DP1_SDP_CFG3 (0x000004E8)
#define UPDATE_SDP (0x00000001)
#define MMSS_DP_EXTENSION_0 (0x00000250)
@@ -288,6 +296,7 @@
#define MMSS_DP_GENERIC1_7 (0x00000344)
#define MMSS_DP_GENERIC1_8 (0x00000348)
#define MMSS_DP_GENERIC1_9 (0x0000034C)
+#define MMSS_DP1_GENERIC0_0 (0x00000490)
#define MMSS_DP_VSCEXT_0 (0x000002D0)
#define MMSS_DP_VSCEXT_1 (0x000002D4)
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 16/38] drm/msm/dp: Add catalog support for 3rd/4th stream MST
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (14 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 15/38] drm/msm/dp: use stream_id to change offsets in dp_catalog Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-25 20:35 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 17/38] drm/msm/dp: add support to send ACT packets for MST Yongxing Mou
` (21 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
To support 4-stream MST, the link clocks for stream 3 and stream 4
are controlled by MST_2_LCLK and MST_3_LCLK. These clocks share the
same register definitions but use different base addresses.
This patch adds catalog support to enable programming of these blocks.
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 82 ++++++++++++++++++---
drivers/gpu/drm/msm/dp/dp_ctrl.h | 4 +-
drivers/gpu/drm/msm/dp/dp_display.c | 24 ++++++-
drivers/gpu/drm/msm/dp/dp_panel.c | 138 ++++++++++++++++++++++++++++++------
drivers/gpu/drm/msm/dp/dp_panel.h | 4 +-
drivers/gpu/drm/msm/dp/dp_reg.h | 14 ++++
6 files changed, 230 insertions(+), 36 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index b8b6a09966aed96f705bdd54cb16ea63e5f0141f..608a1a077301b2ef3c77c271d873bb4364abe779 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -118,6 +118,8 @@ struct msm_dp_ctrl_private {
struct msm_dp_link *link;
void __iomem *ahb_base;
void __iomem *link_base;
+ void __iomem *mst2link_base;
+ void __iomem *mst3link_base;
struct phy *phy;
@@ -172,6 +174,40 @@ static inline void msm_dp_write_link(struct msm_dp_ctrl_private *ctrl,
writel(data, ctrl->link_base + offset);
}
+static inline u32 msm_dp_read_mstlink(struct msm_dp_ctrl_private *ctrl,
+ enum msm_dp_stream_id stream_id, u32 offset)
+{
+ switch (stream_id) {
+ case DP_STREAM_2:
+ return readl_relaxed(ctrl->mst2link_base + offset);
+ case DP_STREAM_3:
+ return readl_relaxed(ctrl->mst3link_base + offset);
+ default:
+ DRM_ERROR("error stream_id\n");
+ return 0;
+ }
+}
+
+static inline void msm_dp_write_mstlink(struct msm_dp_ctrl_private *ctrl,
+ enum msm_dp_stream_id stream_id, u32 offset, u32 data)
+{
+ /*
+ * To make sure link reg writes happens before any other operation,
+ * this function uses writel() instread of writel_relaxed()
+ */
+ switch (stream_id) {
+ case DP_STREAM_2:
+ writel(data, ctrl->mst2link_base + offset);
+ break;
+ case DP_STREAM_3:
+ writel(data, ctrl->mst3link_base + offset);
+ break;
+ default:
+ DRM_ERROR("error stream_id\n");
+ break;
+ }
+}
+
static int msm_dp_aux_link_configure(struct drm_dp_aux *aux,
struct msm_dp_link_info *link)
{
@@ -386,7 +422,11 @@ static void msm_dp_ctrl_config_ctrl_streams(struct msm_dp_ctrl_private *ctrl,
u32 config = 0, tbd;
u32 reg_offset = 0;
- config = msm_dp_read_link(ctrl, REG_DP_CONFIGURATION_CTRL);
+ if (msm_dp_panel->stream_id < DP_STREAM_2)
+ config = msm_dp_read_link(ctrl, REG_DP_CONFIGURATION_CTRL);
+
+ if (msm_dp_panel->stream_id == DP_STREAM_1)
+ reg_offset = REG_DP1_CONFIGURATION_CTRL - REG_DP_CONFIGURATION_CTRL;
if (msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
config |= DP_CONFIGURATION_CTRL_RGB_YUV; /* YUV420 */
@@ -401,8 +441,11 @@ static void msm_dp_ctrl_config_ctrl_streams(struct msm_dp_ctrl_private *ctrl,
drm_dbg_dp(ctrl->drm_dev, "stream DP_CONFIGURATION_CTRL=0x%x\n", config);
- if (msm_dp_panel->stream_id == DP_STREAM_1)
- reg_offset = REG_DP1_CONFIGURATION_CTRL - REG_DP_CONFIGURATION_CTRL;
+ if (msm_dp_panel->stream_id > DP_STREAM_1)
+ msm_dp_write_mstlink(ctrl, msm_dp_panel->stream_id,
+ REG_DP_MSTLINK_CONFIGURATION_CTRL, config);
+ else
+ msm_dp_write_link(ctrl, REG_DP_CONFIGURATION_CTRL + reg_offset, config);
}
static void msm_dp_ctrl_config_ctrl_link(struct msm_dp_ctrl_private *ctrl)
@@ -462,7 +505,11 @@ static void msm_dp_ctrl_config_misc1_misc0(struct msm_dp_ctrl_private *ctrl,
if (msm_dp_panel->stream_id == DP_STREAM_1)
reg_offset = REG_DP1_MISC1_MISC0 - REG_DP_MISC1_MISC0;
- misc_val = msm_dp_read_link(ctrl, REG_DP_MISC1_MISC0 + reg_offset);
+ if (msm_dp_panel->stream_id > DP_STREAM_1)
+ misc_val = msm_dp_read_mstlink(ctrl, msm_dp_panel->stream_id,
+ REG_DP_MSTLINK_MISC1_MISC0);
+ else
+ misc_val = msm_dp_read_link(ctrl, REG_DP_MISC1_MISC0 + reg_offset);
/* clear bpp bits */
misc_val &= ~(0x07 << DP_MISC0_TEST_BITS_DEPTH_SHIFT);
@@ -472,7 +519,11 @@ static void msm_dp_ctrl_config_misc1_misc0(struct msm_dp_ctrl_private *ctrl,
misc_val |= DP_MISC0_SYNCHRONOUS_CLK;
drm_dbg_dp(ctrl->drm_dev, "misc settings = 0x%x\n", misc_val);
- msm_dp_write_link(ctrl, REG_DP_MISC1_MISC0 + reg_offset, misc_val);
+ if (msm_dp_panel->stream_id > DP_STREAM_1)
+ msm_dp_write_mstlink(ctrl, msm_dp_panel->stream_id,
+ REG_DP_MSTLINK_MISC1_MISC0, misc_val);
+ else
+ msm_dp_write_link(ctrl, REG_DP_MISC1_MISC0 + reg_offset, misc_val);
}
static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl,
@@ -2495,8 +2546,15 @@ static void msm_dp_ctrl_config_msa(struct msm_dp_ctrl_private *ctrl,
nvid *= 3;
drm_dbg_dp(ctrl->drm_dev, "mvid=0x%x, nvid=0x%x\n", mvid, nvid);
- msm_dp_write_link(ctrl, REG_DP_SOFTWARE_MVID + mvid_reg_off, mvid);
- msm_dp_write_link(ctrl, REG_DP_SOFTWARE_NVID + nvid_reg_off, nvid);
+ if (msm_dp_panel->stream_id > DP_STREAM_1) {
+ msm_dp_write_mstlink(ctrl, msm_dp_panel->stream_id,
+ REG_MSTLINK_SOFTWARE_MVID, mvid);
+ msm_dp_write_mstlink(ctrl, msm_dp_panel->stream_id,
+ REG_MSTLINK_SOFTWARE_NVID, nvid);
+ } else {
+ msm_dp_write_link(ctrl, REG_DP_SOFTWARE_MVID + mvid_reg_off, mvid);
+ msm_dp_write_link(ctrl, REG_DP_SOFTWARE_NVID + nvid_reg_off, nvid);
+ }
}
int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train)
@@ -2567,7 +2625,9 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
msm_dp_ctrl_lane_mapping(ctrl);
msm_dp_setup_peripheral_flush(ctrl);
- msm_dp_ctrl_config_ctrl_link(ctrl);
+
+ if (msm_dp_panel->stream_id == DP_STREAM_0)
+ msm_dp_ctrl_config_ctrl_link(ctrl);
msm_dp_ctrl_configure_source_params(ctrl, msm_dp_panel);
@@ -2745,7 +2805,9 @@ struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link
struct phy *phy,
int max_stream,
void __iomem *ahb_base,
- void __iomem *link_base)
+ void __iomem *link_base,
+ void __iomem *mst2link_base,
+ void __iomem *mst3link_base)
{
struct msm_dp_ctrl_private *ctrl;
int ret;
@@ -2785,6 +2847,8 @@ struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link
ctrl->phy = phy;
ctrl->ahb_base = ahb_base;
ctrl->link_base = link_base;
+ ctrl->mst2link_base = mst2link_base;
+ ctrl->mst3link_base = mst3link_base;
ret = msm_dp_ctrl_clk_init(&ctrl->msm_dp_ctrl, max_stream);
if (ret) {
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index 32ff1455caf0e7fcb1bd74b1f3192c6c3c03ee74..2baf7a1ff44dd7139d2da86390121d5e7a063e9a 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -31,7 +31,9 @@ struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev,
struct phy *phy,
int max_stream,
void __iomem *ahb_base,
- void __iomem *link_base);
+ void __iomem *link_base,
+ void __iomem *mst2link_base,
+ void __iomem *mst3link_base);
void msm_dp_ctrl_reset(struct msm_dp_ctrl *msm_dp_ctrl);
void msm_dp_ctrl_phy_init(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 935a0c57a928b15a1e9a6f1fab2576b7b09acb8e..562a5eccf3f08c5669cc7c2ad1268897e975d0c4 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -84,6 +84,12 @@ struct msm_dp_display_private {
void __iomem *link_base;
size_t link_len;
+ void __iomem *mst2link_base;
+ size_t mst2link_len;
+
+ void __iomem *mst3link_base;
+ size_t mst3link_len;
+
void __iomem *pixel_base[DP_STREAM_MAX];
size_t pixel_len;
@@ -619,7 +625,8 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp)
goto error_link;
}
- dp->panel = msm_dp_panel_get(dev, dp->aux, dp->link, dp->link_base, dp->pixel_base);
+ dp->panel = msm_dp_panel_get(dev, dp->aux, dp->link, dp->link_base,
+ dp->mst2link_base, dp->mst3link_base, dp->pixel_base);
if (IS_ERR(dp->panel)) {
rc = PTR_ERR(dp->panel);
DRM_ERROR("failed to initialize panel, rc = %d\n", rc);
@@ -628,7 +635,8 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp)
}
dp->ctrl = msm_dp_ctrl_get(dev, dp->link, dp->panel, dp->aux,
- phy, dp->max_stream, dp->ahb_base, dp->link_base);
+ phy, dp->max_stream, dp->ahb_base,
+ dp->link_base, dp->mst2link_base, dp->mst3link_base);
if (IS_ERR(dp->ctrl)) {
rc = PTR_ERR(dp->ctrl);
DRM_ERROR("failed to initialize ctrl, rc = %d\n", rc);
@@ -937,6 +945,10 @@ void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp)
msm_dp_display->aux_base, "dp_aux");
msm_disp_snapshot_add_block(disp_state, msm_dp_display->link_len,
msm_dp_display->link_base, "dp_link");
+ msm_disp_snapshot_add_block(disp_state, msm_dp_display->mst2link_len,
+ msm_dp_display->mst2link_base, "dp_mst2link");
+ msm_disp_snapshot_add_block(disp_state, msm_dp_display->mst3link_len,
+ msm_dp_display->mst3link_base, "dp_mst3link");
msm_disp_snapshot_add_block(disp_state, msm_dp_display->pixel_len,
msm_dp_display->pixel_base[0], "dp_p0");
}
@@ -1245,6 +1257,14 @@ static int msm_dp_display_get_io(struct msm_dp_display_private *display)
display->pixel_base[i]);
}
+ display->mst2link_base = msm_dp_ioremap(pdev, 7, &display->mst2link_len);
+ if (IS_ERR(display->mst2link_base))
+ DRM_DEBUG_DP("unable to remap link region: %pe\n", display->mst2link_base);
+
+ display->mst3link_base = msm_dp_ioremap(pdev, 8, &display->mst3link_len);
+ if (IS_ERR(display->mst3link_base))
+ DRM_DEBUG_DP("unable to remap link region: %pe\n", display->mst3link_base);
+
return 0;
}
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index d1af389dffcfee2d21a616de6ee027374997aaee..f792687c315a2c8203305a20b7290a93b0d791f4 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -26,6 +26,8 @@ struct msm_dp_panel_private {
struct drm_dp_aux *aux;
struct msm_dp_link *link;
void __iomem *link_base;
+ void __iomem *mst2link_base;
+ void __iomem *mst3link_base;
void __iomem *pixel_base[DP_STREAM_MAX];
bool panel_on;
};
@@ -45,6 +47,39 @@ static inline void msm_dp_write_link(struct msm_dp_panel_private *panel,
writel(data, panel->link_base + offset);
}
+static inline u32 msm_dp_read_mstlink(struct msm_dp_panel_private *panel, u32 offset)
+{
+ switch (panel->msm_dp_panel.stream_id) {
+ case DP_STREAM_2:
+ return readl_relaxed(panel->mst2link_base + offset);
+ case DP_STREAM_3:
+ return readl_relaxed(panel->mst3link_base + offset);
+ default:
+ DRM_ERROR("error stream_id\n");
+ return 0;
+ }
+}
+
+static inline void msm_dp_write_mstlink(struct msm_dp_panel_private *panel,
+ u32 offset, u32 data)
+{
+ /*
+ * To make sure link reg writes happens before any other operation,
+ * this function uses writel() instread of writel_relaxed()
+ */
+ switch (panel->msm_dp_panel.stream_id) {
+ case DP_STREAM_2:
+ writel(data, panel->mst2link_base + offset);
+ break;
+ case DP_STREAM_3:
+ writel(data, panel->mst3link_base + offset);
+ break;
+ default:
+ DRM_ERROR("error stream_id\n");
+ break;
+ }
+}
+
static inline void msm_dp_write_pn(struct msm_dp_panel_private *panel,
u32 offset, u32 data)
{
@@ -384,13 +419,22 @@ static void msm_dp_panel_send_vsc_sdp(struct msm_dp_panel_private *panel, struct
msm_dp_utils_pack_sdp_header(&vsc_sdp->sdp_header, header);
- msm_dp_write_link(panel, MMSS_DP_GENERIC0_0 + offset, header[0]);
- msm_dp_write_link(panel, MMSS_DP_GENERIC0_1 + offset, header[1]);
+ if (panel->msm_dp_panel.stream_id > DP_STREAM_1) {
+ msm_dp_write_mstlink(panel, MMSS_DP_MSTLINK_GENERIC0_0, header[0]);
+ msm_dp_write_mstlink(panel, MMSS_DP_MSTLINK_GENERIC0_1, header[1]);
+ } else {
+ msm_dp_write_link(panel, MMSS_DP_GENERIC0_0 + offset, header[0]);
+ msm_dp_write_link(panel, MMSS_DP_GENERIC0_1 + offset, header[1]);
+ }
for (i = 0; i < sizeof(vsc_sdp->db); i += 4) {
val = ((vsc_sdp->db[i]) | (vsc_sdp->db[i + 1] << 8) | (vsc_sdp->db[i + 2] << 16) |
(vsc_sdp->db[i + 3] << 24));
- msm_dp_write_link(panel, MMSS_DP_GENERIC0_2 + i + offset, val);
+
+ if (panel->msm_dp_panel.stream_id > DP_STREAM_1)
+ msm_dp_write_mstlink(panel, MMSS_DP_MSTLINK_GENERIC0_2 + i, val);
+ else
+ msm_dp_write_link(panel, MMSS_DP_GENERIC0_2 + i + offset, val);
}
}
@@ -404,8 +448,13 @@ static void msm_dp_panel_update_sdp(struct msm_dp_panel_private *panel)
if (hw_revision >= DP_HW_VERSION_1_0 &&
hw_revision < DP_HW_VERSION_1_2) {
- msm_dp_write_link(panel, MMSS_DP_SDP_CFG3 + offset, UPDATE_SDP);
- msm_dp_write_link(panel, MMSS_DP_SDP_CFG3 + offset, 0x0);
+ if (panel->msm_dp_panel.stream_id > DP_STREAM_1) {
+ msm_dp_write_mstlink(panel, MMSS_DP_MSTLINK_SDP_CFG3, UPDATE_SDP);
+ msm_dp_write_mstlink(panel, MMSS_DP_MSTLINK_SDP_CFG3, 0x00);
+ } else {
+ msm_dp_write_link(panel, MMSS_DP_SDP_CFG3 + offset, UPDATE_SDP);
+ msm_dp_write_link(panel, MMSS_DP_SDP_CFG3 + offset, 0x0);
+ }
}
}
@@ -424,15 +473,26 @@ void msm_dp_panel_enable_vsc_sdp(struct msm_dp_panel *msm_dp_panel, struct dp_sd
sdp_cfg2_offset = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
}
- cfg = msm_dp_read_link(panel, MMSS_DP_SDP_CFG + sdp_cfg_offset);
- cfg2 = msm_dp_read_link(panel, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset);
- misc = msm_dp_read_link(panel, REG_DP_MISC1_MISC0 + misc_reg_offset);
+ if (msm_dp_panel->stream_id > DP_STREAM_1) {
+ cfg = msm_dp_read_mstlink(panel, MMSS_DP_MSTLINK_SDP_CFG);
+ cfg2 = msm_dp_read_mstlink(panel, MMSS_DP_MSTLINK_SDP_CFG2);
+ misc = msm_dp_read_mstlink(panel, REG_DP_MSTLINK_MISC1_MISC0);
+ } else {
+ cfg = msm_dp_read_link(panel, MMSS_DP_SDP_CFG + sdp_cfg_offset);
+ cfg2 = msm_dp_read_link(panel, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset);
+ misc = msm_dp_read_link(panel, REG_DP_MISC1_MISC0 + misc_reg_offset);
+ }
cfg |= GEN0_SDP_EN;
- msm_dp_write_link(panel, MMSS_DP_SDP_CFG + sdp_cfg_offset, cfg);
-
cfg2 |= GENERIC0_SDPSIZE_VALID;
- msm_dp_write_link(panel, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset, cfg2);
+
+ if (msm_dp_panel->stream_id > DP_STREAM_1) {
+ msm_dp_write_mstlink(panel, MMSS_DP_MSTLINK_SDP_CFG, cfg);
+ msm_dp_write_mstlink(panel, MMSS_DP_MSTLINK_SDP_CFG2, cfg2);
+ } else {
+ msm_dp_write_link(panel, MMSS_DP_SDP_CFG + sdp_cfg_offset, cfg);
+ msm_dp_write_link(panel, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset, cfg2);
+ }
msm_dp_panel_send_vsc_sdp(panel, vsc_sdp);
@@ -442,7 +502,10 @@ void msm_dp_panel_enable_vsc_sdp(struct msm_dp_panel *msm_dp_panel, struct dp_sd
drm_dbg_dp(panel->drm_dev, "vsc sdp enable=1\n");
pr_debug("misc settings = 0x%x\n", misc);
- msm_dp_write_link(panel, REG_DP_MISC1_MISC0 + misc_reg_offset, misc);
+ if (msm_dp_panel->stream_id > DP_STREAM_1)
+ msm_dp_write_mstlink(panel, REG_DP_MSTLINK_MISC1_MISC0, misc);
+ else
+ msm_dp_write_link(panel, REG_DP_MISC1_MISC0 + misc_reg_offset, misc);
msm_dp_panel_update_sdp(panel);
}
@@ -462,15 +525,26 @@ void msm_dp_panel_disable_vsc_sdp(struct msm_dp_panel *msm_dp_panel)
sdp_cfg2_offset = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
}
- cfg = msm_dp_read_link(panel, MMSS_DP_SDP_CFG + sdp_cfg_offset);
- cfg2 = msm_dp_read_link(panel, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset);
- misc = msm_dp_read_link(panel, REG_DP_MISC1_MISC0 + misc_reg_offset);
+ if (msm_dp_panel->stream_id > DP_STREAM_1) {
+ cfg = msm_dp_read_mstlink(panel, MMSS_DP_MSTLINK_SDP_CFG);
+ cfg2 = msm_dp_read_mstlink(panel, MMSS_DP_MSTLINK_SDP_CFG2);
+ misc = msm_dp_read_mstlink(panel, REG_DP_MSTLINK_MISC1_MISC0);
+ } else {
+ cfg = msm_dp_read_link(panel, MMSS_DP_SDP_CFG + sdp_cfg_offset);
+ cfg2 = msm_dp_read_link(panel, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset);
+ misc = msm_dp_read_link(panel, REG_DP_MISC1_MISC0 + misc_reg_offset);
+ }
cfg &= ~GEN0_SDP_EN;
- msm_dp_write_link(panel, MMSS_DP_SDP_CFG + sdp_cfg_offset, cfg);
-
cfg2 &= ~GENERIC0_SDPSIZE_VALID;
- msm_dp_write_link(panel, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset, cfg2);
+
+ if (msm_dp_panel->stream_id > DP_STREAM_1) {
+ msm_dp_write_mstlink(panel, MMSS_DP_MSTLINK_SDP_CFG, cfg);
+ msm_dp_write_mstlink(panel, MMSS_DP_MSTLINK_SDP_CFG2, cfg2);
+ } else {
+ msm_dp_write_link(panel, MMSS_DP_SDP_CFG + sdp_cfg_offset, cfg);
+ msm_dp_write_link(panel, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset, cfg2);
+ }
/* switch back to MSA */
misc &= ~DP_MISC1_VSC_SDP;
@@ -478,7 +552,10 @@ void msm_dp_panel_disable_vsc_sdp(struct msm_dp_panel *msm_dp_panel)
drm_dbg_dp(panel->drm_dev, "vsc sdp enable=0\n");
pr_debug("misc settings = 0x%x\n", misc);
- msm_dp_write_link(panel, REG_DP_MISC1_MISC0 + misc_reg_offset, misc);
+ if (msm_dp_panel->stream_id > DP_STREAM_1)
+ msm_dp_write_mstlink(panel, REG_DP_MSTLINK_MISC1_MISC0, misc);
+ else
+ msm_dp_write_link(panel, REG_DP_MISC1_MISC0 + misc_reg_offset, misc);
msm_dp_panel_update_sdp(panel);
}
@@ -584,10 +661,20 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en)
msm_dp_active = data;
- msm_dp_write_link(panel, REG_DP_TOTAL_HOR_VER + offset, total);
- msm_dp_write_link(panel, REG_DP_START_HOR_VER_FROM_SYNC + offset, sync_start);
- msm_dp_write_link(panel, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY + offset, width_blanking);
- msm_dp_write_link(panel, REG_DP_ACTIVE_HOR_VER + offset, msm_dp_active);
+ if (msm_dp_panel->stream_id > DP_STREAM_1) {
+ msm_dp_write_mstlink(panel, REG_DP_MSTLINK_TOTAL_HOR_VER, total);
+ msm_dp_write_mstlink(panel, REG_DP_MSTLINK_START_HOR_VER_FROM_SYNC,
+ sync_start);
+ msm_dp_write_mstlink(panel, REG_DP_MSTLINK_HSYNC_VSYNC_WIDTH_POLARITY,
+ width_blanking);
+ msm_dp_write_mstlink(panel, REG_DP_MSTLINK_ACTIVE_HOR_VER, msm_dp_active);
+ } else {
+ msm_dp_write_link(panel, REG_DP_TOTAL_HOR_VER + offset, total);
+ msm_dp_write_link(panel, REG_DP_START_HOR_VER_FROM_SYNC + offset, sync_start);
+ msm_dp_write_link(panel, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY + offset,
+ width_blanking);
+ msm_dp_write_link(panel, REG_DP_ACTIVE_HOR_VER + offset, msm_dp_active);
+ }
reg = msm_dp_read_pn(panel, MMSS_DP_INTF_CONFIG);
if (wide_bus_en)
@@ -703,6 +790,8 @@ static int msm_dp_panel_parse_dt(struct msm_dp_panel *msm_dp_panel)
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 *mst2link_base,
+ void __iomem *mst3link_base,
void __iomem *pixel_base[])
{
struct msm_dp_panel_private *panel;
@@ -722,6 +811,9 @@ struct msm_dp_panel *msm_dp_panel_get(struct device *dev, struct drm_dp_aux *aux
panel->aux = aux;
panel->link = link;
panel->link_base = link_base;
+ panel->mst2link_base = mst2link_base;
+ panel->mst3link_base = mst3link_base;
+
memcpy(panel->pixel_base, pixel_base, sizeof(panel->pixel_base));
msm_dp_panel = &panel->msm_dp_panel;
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
index 23b3e78e40479d133893a8afe1a69cfe8c16abdf..2bfe3695994235d04e209a2785915107c6a8e413 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -103,5 +103,7 @@ static inline bool is_lane_count_valid(u32 lane_count)
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 __iomem *mst2link_base,
+ void __iomem *mst3link_base,
+ void __iomem *pixel_base[]);
#endif /* _DP_PANEL_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index 43a9ce0539906e1f185abf250fdf161e462d9645..a806d397ff9d9ad3830b1f539614bffcc955a786 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -142,6 +142,7 @@
#define REG_DP_CONFIGURATION_CTRL (0x00000008)
#define REG_DP1_CONFIGURATION_CTRL (0x00000400)
+#define REG_DP_MSTLINK_CONFIGURATION_CTRL (0x00000034)
#define DP_CONFIGURATION_CTRL_SYNC_ASYNC_CLK (0x00000001)
#define DP_CONFIGURATION_CTRL_STATIC_DYNAMIC_CN (0x00000002)
#define DP_CONFIGURATION_CTRL_P_INTERLACED (0x00000004)
@@ -163,12 +164,19 @@
#define REG_DP1_SOFTWARE_MVID (0x00000414)
#define REG_DP1_SOFTWARE_NVID (0x00000418)
#define REG_DP1_TOTAL_HOR_VER (0x0000041C)
+#define REG_MSTLINK_SOFTWARE_MVID (0x00000040)
+#define REG_MSTLINK_SOFTWARE_NVID (0x00000044)
+#define REG_DP_MSTLINK_TOTAL_HOR_VER (0x00000048)
#define REG_DP_START_HOR_VER_FROM_SYNC (0x00000020)
+#define REG_DP_MSTLINK_START_HOR_VER_FROM_SYNC (0x0000004C)
#define REG_DP_HSYNC_VSYNC_WIDTH_POLARITY (0x00000024)
+#define REG_DP_MSTLINK_HSYNC_VSYNC_WIDTH_POLARITY (0x00000050)
#define REG_DP_ACTIVE_HOR_VER (0x00000028)
+#define REG_DP_MSTLINK_ACTIVE_HOR_VER (0x00000054)
#define REG_DP_MISC1_MISC0 (0x0000002C)
#define REG_DP1_MISC1_MISC0 (0x0000042C)
+#define REG_DP_MSTLINK_MISC1_MISC0 (0x00000058)
#define DP_MISC0_SYNCHRONOUS_CLK (0x00000001)
#define DP_MISC0_COLORIMETRY_CFG_SHIFT (0x00000001)
#define DP_MISC0_TEST_BITS_DEPTH_SHIFT (0x00000005)
@@ -236,9 +244,11 @@
#define MMSS_DP_SDP_CFG (0x00000228)
#define MMSS_DP1_SDP_CFG (0x000004E0)
+#define MMSS_DP_MSTLINK_SDP_CFG (0x0000010c)
#define GEN0_SDP_EN (0x00020000)
#define MMSS_DP_SDP_CFG2 (0x0000022C)
#define MMSS_DP1_SDP_CFG2 (0x000004E4)
+#define MMSS_DP_MSTLINK_SDP_CFG2 (0x0000011c)
#define MMSS_DP_AUDIO_TIMESTAMP_0 (0x00000230)
#define MMSS_DP_AUDIO_TIMESTAMP_1 (0x00000234)
#define GENERIC0_SDPSIZE_VALID (0x00010000)
@@ -248,6 +258,7 @@
#define MMSS_DP_SDP_CFG3 (0x0000024c)
#define MMSS_DP1_SDP_CFG3 (0x000004E8)
+#define MMSS_DP_MSTLINK_SDP_CFG3 (0x00000114)
#define UPDATE_SDP (0x00000001)
#define MMSS_DP_EXTENSION_0 (0x00000250)
@@ -297,6 +308,9 @@
#define MMSS_DP_GENERIC1_8 (0x00000348)
#define MMSS_DP_GENERIC1_9 (0x0000034C)
#define MMSS_DP1_GENERIC0_0 (0x00000490)
+#define MMSS_DP_MSTLINK_GENERIC0_0 (0x000000BC)
+#define MMSS_DP_MSTLINK_GENERIC0_1 (0x000000C0)
+#define MMSS_DP_MSTLINK_GENERIC0_2 (0x000000C4)
#define MMSS_DP_VSCEXT_0 (0x000002D0)
#define MMSS_DP_VSCEXT_1 (0x000002D4)
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 17/38] drm/msm/dp: add support to send ACT packets for MST
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (15 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 16/38] drm/msm/dp: Add catalog support for 3rd/4th stream MST Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-25 21:10 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 18/38] drm/msm/dp: Add support to enable MST in mainlink control Yongxing Mou
` (20 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
Whenever virtual channel slot allocation changes, the DP
source must send the action control trigger sequence to notify
the sink about the same. This would be applicable during the
start and stop of the pixel stream. Add the infrastructure
to be able to send ACT packets for the DP controller when
operating in MST mode.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 39 +++++++++++++++++++++++++++++++++++--
drivers/gpu/drm/msm/dp/dp_ctrl.h | 4 ++--
drivers/gpu/drm/msm/dp/dp_display.c | 3 ++-
drivers/gpu/drm/msm/dp/dp_display.h | 1 +
drivers/gpu/drm/msm/dp/dp_reg.h | 2 ++
5 files changed, 44 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 608a1a077301b2ef3c77c271d873bb4364abe779..16e5ed58e791971d5dca3077cbb77bfcc186505a 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -142,6 +142,7 @@ struct msm_dp_ctrl_private {
bool core_clks_on;
bool link_clks_on;
bool stream_clks_on[DP_STREAM_MAX];
+ bool mst_active;
};
static inline u32 msm_dp_read_ahb(const struct msm_dp_ctrl_private *ctrl, u32 offset)
@@ -227,6 +228,32 @@ static int msm_dp_aux_link_configure(struct drm_dp_aux *aux,
return 0;
}
+void msm_dp_ctrl_mst_send_act(struct msm_dp_ctrl *msm_dp_ctrl)
+{
+ struct msm_dp_ctrl_private *ctrl;
+ bool act_complete;
+
+ ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
+
+ if (!ctrl->mst_active)
+ return;
+
+ msm_dp_write_link(ctrl, REG_DP_MST_ACT, 0x1);
+ /* make sure ACT signal is performed */
+ wmb();
+
+ msleep(20); /* needs 1 frame time */
+
+ act_complete = msm_dp_read_link(ctrl, REG_DP_MST_ACT);
+
+ if (!act_complete)
+ drm_dbg_dp(ctrl->drm_dev, "mst ACT trigger complete SUCCESS\n");
+ else
+ drm_dbg_dp(ctrl->drm_dev, "mst ACT trigger complete failed\n");
+
+ return;
+}
+
/*
* NOTE: resetting DP controller will also clear any pending HPD related interrupts
*/
@@ -2079,6 +2106,8 @@ static int msm_dp_ctrl_link_maintenance(struct msm_dp_ctrl_private *ctrl)
msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, DP_STATE_CTRL_SEND_VIDEO);
+ msm_dp_ctrl_mst_send_act(&ctrl->msm_dp_ctrl);
+
ret = msm_dp_ctrl_wait4video_ready(ctrl);
end:
return ret;
@@ -2275,7 +2304,7 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
msm_dp_ctrl_off_pixel_clk(&ctrl->msm_dp_ctrl, ctrl->panel->stream_id);
msm_dp_ctrl_off_link(&ctrl->msm_dp_ctrl);
- ret = msm_dp_ctrl_on_link(&ctrl->msm_dp_ctrl);
+ ret = msm_dp_ctrl_on_link(&ctrl->msm_dp_ctrl, false);
if (ret) {
DRM_ERROR("failed to enable DP link controller\n");
return ret;
@@ -2355,7 +2384,7 @@ 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, bool mst_active)
{
int rc = 0;
struct msm_dp_ctrl_private *ctrl;
@@ -2373,6 +2402,7 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl)
rate = ctrl->panel->link_info.rate;
pixel_rate = ctrl->panel->msm_dp_mode.drm_mode.clock;
+ ctrl->mst_active = mst_active;
msm_dp_ctrl_core_clk_enable(&ctrl->msm_dp_ctrl);
@@ -2643,6 +2673,8 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, DP_STATE_CTRL_SEND_VIDEO);
+ msm_dp_ctrl_mst_send_act(msm_dp_ctrl);
+
ret = msm_dp_ctrl_wait4video_ready(ctrl);
if (ret)
return ret;
@@ -2682,6 +2714,8 @@ void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl)
msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl);
+ ctrl->mst_active = false;
+
dev_pm_opp_set_rate(ctrl->dev, 0);
msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl);
@@ -2849,6 +2883,7 @@ struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link
ctrl->link_base = link_base;
ctrl->mst2link_base = mst2link_base;
ctrl->mst3link_base = mst3link_base;
+ ctrl->mst_active = false;
ret = msm_dp_ctrl_clk_init(&ctrl->msm_dp_ctrl, max_stream);
if (ret) {
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index 2baf7a1ff44dd7139d2da86390121d5e7a063e9a..abf84ddf463638900684f2511549a593783d2247 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -16,7 +16,7 @@ struct msm_dp_ctrl {
struct phy;
-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, bool mst_active);
int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *msm_dp_panel);
int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train);
void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl);
@@ -50,5 +50,5 @@ 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);
-
+void msm_dp_ctrl_mst_send_act(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 562a5eccf3f08c5669cc7c2ad1268897e975d0c4..eeba73f81c5ce7929dac88f4b47ac3741659864b 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -709,7 +709,7 @@ static int msm_dp_display_prepare(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, msm_dp_display->mst_active);
if (rc) {
DRM_ERROR("Failed link training (rc=%d)\n", rc);
msm_dp_display->connector->state->link_status = DRM_LINK_STATUS_BAD;
@@ -1557,6 +1557,7 @@ void msm_dp_display_atomic_disable(struct msm_dp *dp)
msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
msm_dp_ctrl_push_idle(msm_dp_display->ctrl);
+ msm_dp_ctrl_mst_send_act(msm_dp_display->ctrl);
}
static void msm_dp_display_unprepare(struct msm_dp_display_private *dp)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index a839d0a3941eac3e277185e42fddea15ca05a17f..9442157bca9d63467b4c43fa644651ad2cbcbef5 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -21,6 +21,7 @@ struct msm_dp {
bool audio_enabled;
bool power_on;
bool prepared;
+ bool mst_active;
unsigned int connector_type;
bool is_edp;
bool internal_hpd;
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index a806d397ff9d9ad3830b1f539614bffcc955a786..de3d0b8b52c269fd7575edf3f4096a4284ad0b8d 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -158,6 +158,8 @@
#define DP_CONFIGURATION_CTRL_BPC_SHIFT (0x08)
#define DP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT (0x0D)
+#define REG_DP_MST_ACT (0x00000500)
+
#define REG_DP_SOFTWARE_MVID (0x00000010)
#define REG_DP_SOFTWARE_NVID (0x00000018)
#define REG_DP_TOTAL_HOR_VER (0x0000001C)
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 18/38] drm/msm/dp: Add support to enable MST in mainlink control
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (16 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 17/38] drm/msm/dp: add support to send ACT packets for MST Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-25 21:24 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 19/38] drm/msm/dp: no need to update tu calculation for mst Yongxing Mou
` (19 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
Add support to program the MST enable bit in the mainlink control
register when an MST session is active or being disabled.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 17 +++++++++++++++++
drivers/gpu/drm/msm/dp/dp_reg.h | 1 +
2 files changed, 18 insertions(+)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 16e5ed58e791971d5dca3077cbb77bfcc186505a..d562377a8d2846099bf0f8757128978a162745c3 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -254,6 +254,19 @@ void msm_dp_ctrl_mst_send_act(struct msm_dp_ctrl *msm_dp_ctrl)
return;
}
+static void msm_dp_ctrl_mst_config(struct msm_dp_ctrl_private *ctrl, bool enable)
+{
+ u32 mainlink_ctrl;
+
+ mainlink_ctrl = msm_dp_read_link(ctrl, REG_DP_MAINLINK_CTRL);
+ if (enable)
+ mainlink_ctrl |= DP_MAINLINK_CTRL_MST_EN;
+ else
+ mainlink_ctrl &= ~DP_MAINLINK_CTRL_MST_EN;
+
+ msm_dp_write_link(ctrl, REG_DP_MAINLINK_CTRL, mainlink_ctrl);
+}
+
/*
* NOTE: resetting DP controller will also clear any pending HPD related interrupts
*/
@@ -2656,6 +2669,9 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
msm_dp_ctrl_lane_mapping(ctrl);
msm_dp_setup_peripheral_flush(ctrl);
+ if (ctrl->mst_active)
+ msm_dp_ctrl_mst_config(ctrl, true);
+
if (msm_dp_panel->stream_id == DP_STREAM_0)
msm_dp_ctrl_config_ctrl_link(ctrl);
@@ -2711,6 +2727,7 @@ void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl)
phy = ctrl->phy;
msm_dp_ctrl_mainlink_disable(ctrl);
+ msm_dp_ctrl_mst_config(ctrl, false);
msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl);
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index de3d0b8b52c269fd7575edf3f4096a4284ad0b8d..fda847b33f8d0d6ec4d2589586b5a3d6c9b1ccf3 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -128,6 +128,7 @@
#define DP_MAINLINK_FLUSH_MODE_UPDATE_SDP FIELD_PREP(DP_MAINLINK_CTRL_FLUSH_MODE_MASK, 1)
#define DP_MAINLINK_FLUSH_MODE_SDE_PERIPH_UPDATE FIELD_PREP(DP_MAINLINK_CTRL_FLUSH_MODE_MASK, 3)
#define DP_MAINLINK_FB_BOUNDARY_SEL (0x02000000)
+#define DP_MAINLINK_CTRL_MST_EN (0x04000100)
#define REG_DP_STATE_CTRL (0x00000004)
#define DP_STATE_CTRL_LINK_TRAINING_PATTERN1 (0x00000001)
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 19/38] drm/msm/dp: no need to update tu calculation for mst
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (17 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 18/38] drm/msm/dp: Add support to enable MST in mainlink control Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-25 21:25 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 20/38] drm/msm/dp: Add support for MST channel slot allocation Yongxing Mou
` (18 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
DP stream is transmitted in transfer units only for SST
case there is no need to calculate and program TU parameters
for MST case. Skip the TU programming for MST cases.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index d562377a8d2846099bf0f8757128978a162745c3..c313a3b4853a1571c43a9f3c9e981fbc22d51d55 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -2685,7 +2685,8 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
msm_dp_panel_clear_dsc_dto(msm_dp_panel);
- msm_dp_ctrl_setup_tr_unit(ctrl);
+ if (!ctrl->mst_active)
+ msm_dp_ctrl_setup_tr_unit(ctrl);
msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, DP_STATE_CTRL_SEND_VIDEO);
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 20/38] drm/msm/dp: Add support for MST channel slot allocation
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (18 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 19/38] drm/msm/dp: no need to update tu calculation for mst Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-25 21:52 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 21/38] drm/msm/dp: Add support for sending VCPF packets in DP controller Yongxing Mou
` (17 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
DP MST streams share 64 MTP slots in a time-multiplexed manner. This patch
adds support for calculating the rate governor, slot allocation, and slot
reservation in the DP controller.
Each MST stream can reserve its slots by calling
dp_display_set_stream_info() from its bridge callbacks.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 213 +++++++++++++++++++++++++++++++++++-
drivers/gpu/drm/msm/dp/dp_ctrl.h | 7 +-
drivers/gpu/drm/msm/dp/dp_display.c | 40 ++++---
drivers/gpu/drm/msm/dp/dp_display.h | 5 +-
drivers/gpu/drm/msm/dp/dp_panel.h | 1 +
drivers/gpu/drm/msm/dp/dp_reg.h | 14 ++-
6 files changed, 262 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index c313a3b4853a1571c43a9f3c9e981fbc22d51d55..9d58d9480fc4ab33c58218ef9beb54c64805c34c 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -109,6 +109,11 @@ struct msm_dp_vc_tu_mapping_table {
u8 tu_size_minus1;
};
+struct msm_dp_mst_ch_slot_info {
+ u32 start_slot;
+ u32 tot_slots;
+};
+
struct msm_dp_ctrl_private {
struct msm_dp_ctrl msm_dp_ctrl;
struct drm_device *drm_dev;
@@ -143,6 +148,8 @@ struct msm_dp_ctrl_private {
bool link_clks_on;
bool stream_clks_on[DP_STREAM_MAX];
bool mst_active;
+
+ struct msm_dp_mst_ch_slot_info mst_ch_info[DP_STREAM_MAX];
};
static inline u32 msm_dp_read_ahb(const struct msm_dp_ctrl_private *ctrl, u32 offset)
@@ -267,6 +274,77 @@ static void msm_dp_ctrl_mst_config(struct msm_dp_ctrl_private *ctrl, bool enable
msm_dp_write_link(ctrl, REG_DP_MAINLINK_CTRL, mainlink_ctrl);
}
+static void msm_dp_ctrl_mst_channel_alloc(struct msm_dp_ctrl_private *ctrl,
+ enum msm_dp_stream_id stream_id, u32 ch_start_slot,
+ u32 tot_slot_cnt)
+{
+ u32 i, slot_reg_1, slot_reg_2, slot;
+ u32 reg_off = 0;
+ int const num_slots_per_reg = 32;
+
+ if (ch_start_slot > DP_MAX_TIME_SLOTS ||
+ (ch_start_slot + tot_slot_cnt > DP_MAX_TIME_SLOTS)) {
+ DRM_ERROR("invalid slots start %d, tot %d\n",
+ ch_start_slot, tot_slot_cnt);
+ return;
+ }
+
+ drm_dbg_dp(ctrl->drm_dev, "stream_id %d, start_slot %d, tot_slot %d\n",
+ stream_id, ch_start_slot, tot_slot_cnt);
+
+ if (stream_id == DP_STREAM_1)
+ reg_off = REG_DP_DP1_TIMESLOT_1_32 - REG_DP_DP0_TIMESLOT_1_32;
+
+ slot_reg_1 = 0;
+ slot_reg_2 = 0;
+
+ if (ch_start_slot && tot_slot_cnt) {
+ ch_start_slot--;
+ for (i = 0; i < tot_slot_cnt; i++) {
+ if (ch_start_slot < num_slots_per_reg) {
+ slot_reg_1 |= BIT(ch_start_slot);
+ } else {
+ slot = ch_start_slot - num_slots_per_reg;
+ slot_reg_2 |= BIT(slot);
+ }
+ ch_start_slot++;
+ }
+ }
+
+ drm_dbg_dp(ctrl->drm_dev, "stream_id:%d slot_reg_1:%d, slot_reg_2:%d\n", stream_id,
+ slot_reg_1, slot_reg_2);
+
+ if (stream_id > DP_STREAM_1) {
+ msm_dp_write_mstlink(ctrl, stream_id, REG_DP_MSTLINK_TIMESLOT_1_32,
+ slot_reg_1);
+ msm_dp_write_mstlink(ctrl, stream_id, REG_DP_MSTLINK_TIMESLOT_33_63,
+ slot_reg_2);
+ } else {
+ msm_dp_write_link(ctrl, REG_DP_DP0_TIMESLOT_1_32 + reg_off, slot_reg_1);
+ msm_dp_write_link(ctrl, REG_DP_DP0_TIMESLOT_33_63 + reg_off, slot_reg_2);
+ }
+}
+
+static void msm_dp_ctrl_update_rg(struct msm_dp_ctrl_private *ctrl,
+ enum msm_dp_stream_id stream_id, u32 x_int, u32 y_frac_enum)
+{
+ u32 rg, reg_off = 0;
+
+ rg = y_frac_enum;
+ rg |= (x_int << 16);
+
+ drm_dbg_dp(ctrl->drm_dev, "stream_id: %d x_int:%d y_frac_enum:%d rg:%d\n",
+ stream_id, x_int, y_frac_enum, rg);
+
+ if (stream_id == DP_STREAM_1)
+ reg_off = REG_DP_DP1_RG - REG_DP_DP0_RG;
+
+ if (stream_id > DP_STREAM_1)
+ msm_dp_write_mstlink(ctrl, stream_id, REG_DP_MSTLINK_DP_RG, rg);
+ else
+ msm_dp_write_link(ctrl, REG_DP_DP0_RG + reg_off, rg);
+}
+
/*
* NOTE: resetting DP controller will also clear any pending HPD related interrupts
*/
@@ -2634,7 +2712,105 @@ int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_li
return ret;
}
-int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *msm_dp_panel)
+static void msm_dp_ctrl_mst_calculate_rg(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel,
+ u32 *p_x_int, u32 *p_y_frac_enum)
+{
+ u64 min_slot_cnt, max_slot_cnt;
+ u64 raw_target_sc, target_sc_fixp;
+ u64 ts_denom, ts_enum, ts_int;
+ u64 pclk = panel->msm_dp_mode.drm_mode.clock;
+ u64 lclk = 0;
+ u64 lanes = ctrl->link->link_params.num_lanes;
+ u64 bpp = panel->msm_dp_mode.bpp;
+ u64 pbn = panel->pbn;
+ u64 numerator, denominator, temp, temp1, temp2;
+ u32 x_int = 0, y_frac_enum = 0;
+ u64 target_strm_sym, ts_int_fixp, ts_frac_fixp, y_frac_enum_fixp;
+
+ lclk = ctrl->link->link_params.rate;
+
+ /* min_slot_cnt */
+ numerator = pclk * bpp * 64 * 1000;
+ denominator = lclk * lanes * 8 * 1000;
+ min_slot_cnt = drm_fixp_from_fraction(numerator, denominator);
+
+ /* max_slot_cnt */
+ numerator = pbn * 54 * 1000;
+ denominator = lclk * lanes;
+ max_slot_cnt = drm_fixp_from_fraction(numerator, denominator);
+
+ /* raw_target_sc */
+ numerator = max_slot_cnt + min_slot_cnt;
+ denominator = drm_fixp_from_fraction(2, 1);
+ raw_target_sc = drm_fixp_div(numerator, denominator);
+
+ /* target_sc */
+ temp = drm_fixp_from_fraction(256 * lanes, 1);
+ numerator = drm_fixp_mul(raw_target_sc, temp);
+ denominator = drm_fixp_from_fraction(256 * lanes, 1);
+ target_sc_fixp = drm_fixp_div(numerator, denominator);
+
+ ts_enum = 256 * lanes;
+ ts_denom = drm_fixp_from_fraction(256 * lanes, 1);
+ ts_int = drm_fixp2int(target_sc_fixp);
+
+ temp = drm_fixp2int_ceil(raw_target_sc);
+ if (temp != ts_int) {
+ temp = drm_fixp_from_fraction(ts_int, 1);
+ temp1 = raw_target_sc - temp;
+ temp2 = drm_fixp_mul(temp1, ts_denom);
+ ts_enum = drm_fixp2int(temp2);
+ }
+
+ /* target_strm_sym */
+ ts_int_fixp = drm_fixp_from_fraction(ts_int, 1);
+ ts_frac_fixp = drm_fixp_from_fraction(ts_enum, drm_fixp2int(ts_denom));
+ temp = ts_int_fixp + ts_frac_fixp;
+ temp1 = drm_fixp_from_fraction(lanes, 1);
+ target_strm_sym = drm_fixp_mul(temp, temp1);
+
+ /* x_int */
+ x_int = drm_fixp2int(target_strm_sym);
+
+ /* y_enum_frac */
+ temp = drm_fixp_from_fraction(x_int, 1);
+ temp1 = target_strm_sym - temp;
+ temp2 = drm_fixp_from_fraction(256, 1);
+ y_frac_enum_fixp = drm_fixp_mul(temp1, temp2);
+
+ temp1 = drm_fixp2int(y_frac_enum_fixp);
+ temp2 = drm_fixp2int_ceil(y_frac_enum_fixp);
+
+ y_frac_enum = (u32)((temp1 == temp2) ? temp1 : temp1 + 1);
+
+ *p_x_int = x_int;
+ *p_y_frac_enum = y_frac_enum;
+
+ drm_dbg_dp(ctrl->drm_dev, "mst lane_cnt:%llu, rate:%llu x_int:%d, y_frac:%d\n",
+ lanes, lclk, x_int, y_frac_enum);
+}
+
+static void msm_dp_ctrl_mst_stream_setup(struct msm_dp_ctrl_private *ctrl,
+ struct msm_dp_panel *panel,
+ u32 max_streams)
+{
+ u32 x_int, y_frac_enum;
+
+ if (!ctrl->mst_active)
+ return;
+
+ drm_dbg_dp(ctrl->drm_dev, "mst stream channel allocation\n");
+
+ msm_dp_ctrl_mst_stream_channel_slot_setup(&ctrl->msm_dp_ctrl, max_streams);
+
+ msm_dp_ctrl_mst_calculate_rg(ctrl, panel, &x_int, &y_frac_enum);
+
+ msm_dp_ctrl_update_rg(ctrl, panel->stream_id, x_int, y_frac_enum);
+}
+
+int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *msm_dp_panel, u32 max_streams)
{
int ret = 0;
bool mainlink_ready = false;
@@ -2688,6 +2864,8 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
if (!ctrl->mst_active)
msm_dp_ctrl_setup_tr_unit(ctrl);
+ msm_dp_ctrl_mst_stream_setup(ctrl, msm_dp_panel, max_streams);
+
msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, DP_STATE_CTRL_SEND_VIDEO);
msm_dp_ctrl_mst_send_act(msm_dp_ctrl);
@@ -2742,6 +2920,39 @@ void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl)
phy, phy->init_count, phy->power_count);
}
+void msm_dp_ctrl_set_mst_channel_info(struct msm_dp_ctrl *msm_dp_ctrl,
+ enum msm_dp_stream_id stream_id,
+ u32 start_slot, u32 tot_slots)
+{
+ struct msm_dp_ctrl_private *ctrl;
+
+ if (!msm_dp_ctrl || stream_id >= DP_STREAM_MAX) {
+ DRM_ERROR("invalid input\n");
+ return;
+ }
+
+ ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
+
+ ctrl->mst_ch_info[stream_id].start_slot = start_slot;
+ ctrl->mst_ch_info[stream_id].tot_slots = tot_slots;
+}
+
+void msm_dp_ctrl_mst_stream_channel_slot_setup(struct msm_dp_ctrl *msm_dp_ctrl, u32 max_streams)
+{
+ struct msm_dp_ctrl_private *ctrl;
+ int i;
+
+ ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
+
+ if (!ctrl->mst_active)
+ return;
+
+ for (i = DP_STREAM_0; i < max_streams; i++) {
+ msm_dp_ctrl_mst_channel_alloc(ctrl, i, ctrl->mst_ch_info[i].start_slot,
+ ctrl->mst_ch_info[i].tot_slots);
+ }
+}
+
irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl)
{
struct msm_dp_ctrl_private *ctrl;
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index abf84ddf463638900684f2511549a593783d2247..751f00c97b94dc3b9e8fae2a86e261f71f293425 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, bool mst_active);
-int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *msm_dp_panel);
+int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl,
+ struct msm_dp_panel *msm_dp_panel, u32 max_streams);
int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train);
void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl);
void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl, enum msm_dp_stream_id stream_id);
@@ -51,4 +52,8 @@ 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);
void msm_dp_ctrl_mst_send_act(struct msm_dp_ctrl *msm_dp_ctrl);
+void msm_dp_ctrl_mst_stream_channel_slot_setup(struct msm_dp_ctrl *msm_dp_ctrl, u32 max_streams);
+void msm_dp_ctrl_set_mst_channel_info(struct msm_dp_ctrl *msm_dp_ctrl,
+ enum msm_dp_stream_id stream_id,
+ u32 start_slot, u32 tot_slots);
#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 eeba73f81c5ce7929dac88f4b47ac3741659864b..17633ba79aa7642856051b69227e8f5b23d76730 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -733,7 +733,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, dp->panel, dp->max_stream);
if (!rc)
msm_dp_display->power_on = true;
@@ -817,21 +817,33 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
return 0;
}
-int msm_dp_display_set_stream_id(struct msm_dp *dp,
- struct msm_dp_panel *panel, enum msm_dp_stream_id stream_id)
+int msm_dp_display_set_stream_info(struct msm_dp *msm_dp_display, struct msm_dp_panel *panel,
+ enum msm_dp_stream_id stream_id, u32 start_slot,
+ u32 num_slots, u32 pbn, int vcpi)
{
int rc = 0;
- struct msm_dp_display_private *msm_dp_display;
+ struct msm_dp_display_private *dp;
+ const int max_slots = 64;
- 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);
- if (!msm_dp_display) {
+ if (!dp) {
DRM_ERROR("invalid input\n");
return -EINVAL;
}
- if (panel)
+ if (start_slot + num_slots > max_slots) {
+ DRM_ERROR("invalid channel info received. start:%d, slots:%d\n",
+ start_slot, num_slots);
+ return -EINVAL;
+ }
+
+ msm_dp_ctrl_set_mst_channel_info(dp->ctrl, stream_id, start_slot, num_slots);
+
+ if (panel) {
panel->stream_id = stream_id;
+ panel->pbn = pbn;
+ }
return rc;
}
@@ -1533,7 +1545,7 @@ 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);
- msm_dp_display_set_stream_id(msm_dp_display, dp->panel, 0);
+ msm_dp_display_set_stream_info(msm_dp_display, dp->panel, 0, 0, 0, 0, 0);
if (msm_dp_display->prepared) {
rc = msm_dp_display_enable(dp);
@@ -1550,14 +1562,16 @@ void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display)
drm_dbg_dp(msm_dp_display->drm_dev, "type=%d Done\n", msm_dp_display->connector_type);
}
-void msm_dp_display_atomic_disable(struct msm_dp *dp)
+void msm_dp_display_atomic_disable(struct msm_dp *msm_dp_display)
{
- struct msm_dp_display_private *msm_dp_display;
+ 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);
- msm_dp_ctrl_push_idle(msm_dp_display->ctrl);
- msm_dp_ctrl_mst_send_act(msm_dp_display->ctrl);
+ msm_dp_ctrl_push_idle(dp->ctrl);
+ msm_dp_ctrl_mst_stream_channel_slot_setup(dp->ctrl,
+ dp->max_stream);
+ msm_dp_ctrl_mst_send_act(dp->ctrl);
}
static void msm_dp_display_unprepare(struct msm_dp_display_private *dp)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 9442157bca9d63467b4c43fa644651ad2cbcbef5..fa92f763d2304f15af7c4e1e7e8aab5a6ffd3459 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -48,7 +48,8 @@ void msm_dp_display_mode_set(struct msm_dp *dp,
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);
-int msm_dp_display_set_stream_id(struct msm_dp *dp,
- struct msm_dp_panel *panel, enum msm_dp_stream_id stream_id);
+int msm_dp_display_set_stream_info(struct msm_dp *dp_display, struct msm_dp_panel *panel,
+ enum msm_dp_stream_id stream_id,
+ u32 start_slot, u32 num_slots, u32 pbn, int vcpi);
#endif /* _DP_DISPLAY_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
index 2bfe3695994235d04e209a2785915107c6a8e413..cb5bf6c99a6f7a68995f0f0ac48382dc90beca31 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -50,6 +50,7 @@ struct msm_dp_panel {
u32 hw_revision;
enum msm_dp_stream_id stream_id;
+ u32 pbn;
u32 max_dp_lanes;
u32 max_dp_link_rate;
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index fda847b33f8d0d6ec4d2589586b5a3d6c9b1ccf3..ee4debf796910e00d370ab4c687009747bae5378 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -364,6 +364,19 @@
#define REG_DP_PHY_AUX_BIST_CFG (0x00000050)
#define REG_DP_PHY_AUX_INTERRUPT_STATUS (0x000000BC)
+/* DP MST related registers */
+#define DP_MAX_TIME_SLOTS 64
+
+#define REG_DP_MSTLINK_DP_RG (0X0000011C)
+#define REG_DP_DP0_TIMESLOT_1_32 (0x00000404)
+#define REG_DP_DP0_TIMESLOT_33_63 (0x00000408)
+#define REG_DP_DP1_TIMESLOT_1_32 (0x0000040C)
+#define REG_DP_DP1_TIMESLOT_33_63 (0x00000410)
+#define REG_DP_MSTLINK_TIMESLOT_1_32 (0x00000038)
+#define REG_DP_MSTLINK_TIMESLOT_33_63 (0x0000003C)
+#define REG_DP_DP0_RG (0x000004F8)
+#define REG_DP_DP1_RG (0x000004FC)
+
/* DP HDCP 1.3 registers */
#define DP_HDCP_CTRL (0x0A0)
#define DP_HDCP_STATUS (0x0A4)
@@ -388,5 +401,4 @@
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA10 (0x018)
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA11 (0x01C)
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA12 (0x020)
-
#endif /* _DP_REG_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 21/38] drm/msm/dp: Add support for sending VCPF packets in DP controller
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (19 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 20/38] drm/msm/dp: Add support for MST channel slot allocation Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-26 21:28 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 22/38] drm/msm/dp: Always program MST_FIFO_CONSTANT_FILL for MST use cases Yongxing Mou
` (16 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
The VC Payload Fill (VCPF) sequence is inserted by the DP controller
when stream symbols are absent, typically before a stream is disabled.
This patch adds support for triggering the VCPF sequence in the MSM DP
controller.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 56 ++++++++++++++++++++++++++++++++++---
drivers/gpu/drm/msm/dp/dp_ctrl.h | 2 +-
drivers/gpu/drm/msm/dp/dp_display.c | 2 +-
drivers/gpu/drm/msm/dp/dp_reg.h | 6 ++++
4 files changed, 60 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 9d58d9480fc4ab33c58218ef9beb54c64805c34c..58e8c526253f77f306c669d474bdb2d1751b49a5 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -65,9 +65,18 @@
(PSR_UPDATE_MASK | PSR_CAPTURE_MASK | PSR_EXIT_MASK | \
PSR_UPDATE_ERROR_MASK | PSR_WAKE_ERROR_MASK)
+#define DP_INTERRUPT_STATUS5 \
+ (DP_INTR_DP0_VCPF_SENT | DP_INTR_DP1_VCPF_SENT)
+#define DP_INTERRUPT_STATUS5_MASK \
+ (DP_INTERRUPT_STATUS5 << DP_INTERRUPT_STATUS_MASK_SHIFT)
+
#define DP_CTRL_INTR_READY_FOR_VIDEO BIT(0)
#define DP_CTRL_INTR_IDLE_PATTERN_SENT BIT(3)
+#define DP_DP0_PUSH_VCPF BIT(12)
+#define DP_DP1_PUSH_VCPF BIT(14)
+#define DP_MSTLINK_PUSH_VCPF BIT(12)
+
#define MR_LINK_TRAINING1 0x8
#define MR_LINK_SYMBOL_ERM 0x80
#define MR_LINK_PRBS7 0x100
@@ -407,6 +416,8 @@ void msm_dp_ctrl_enable_irq(struct msm_dp_ctrl *msm_dp_ctrl)
DP_INTERRUPT_STATUS1_MASK);
msm_dp_write_ahb(ctrl, REG_DP_INTR_STATUS2,
DP_INTERRUPT_STATUS2_MASK);
+ msm_dp_write_ahb(ctrl, REG_DP_INTR_STATUS5,
+ DP_INTERRUPT_STATUS5_MASK);
}
void msm_dp_ctrl_disable_irq(struct msm_dp_ctrl *msm_dp_ctrl)
@@ -416,6 +427,7 @@ void msm_dp_ctrl_disable_irq(struct msm_dp_ctrl *msm_dp_ctrl)
msm_dp_write_ahb(ctrl, REG_DP_INTR_STATUS, 0x00);
msm_dp_write_ahb(ctrl, REG_DP_INTR_STATUS2, 0x00);
+ msm_dp_write_ahb(ctrl, REG_DP_INTR_STATUS5, 0x00);
}
static u32 msm_dp_ctrl_get_psr_interrupt(struct msm_dp_ctrl_private *ctrl)
@@ -435,6 +447,20 @@ static void msm_dp_ctrl_config_psr_interrupt(struct msm_dp_ctrl_private *ctrl)
msm_dp_write_ahb(ctrl, REG_DP_INTR_MASK4, DP_INTERRUPT_MASK4);
}
+static u32 msm_dp_ctrl_get_mst_interrupt(struct msm_dp_ctrl_private *ctrl)
+{
+ u32 intr, intr_ack;
+
+ intr = msm_dp_read_ahb(ctrl, REG_DP_INTR_STATUS5);
+ intr &= ~DP_INTERRUPT_STATUS5_MASK;
+ intr_ack = (intr & DP_INTERRUPT_STATUS5)
+ << DP_INTERRUPT_STATUS_ACK_SHIFT;
+ msm_dp_write_ahb(ctrl, REG_DP_INTR_STATUS5,
+ intr_ack | DP_INTERRUPT_STATUS5_MASK);
+
+ return intr;
+}
+
static void msm_dp_ctrl_psr_mainlink_enable(struct msm_dp_ctrl_private *ctrl)
{
u32 val;
@@ -518,14 +544,29 @@ static bool msm_dp_ctrl_mainlink_ready(struct msm_dp_ctrl_private *ctrl)
return true;
}
-void msm_dp_ctrl_push_idle(struct msm_dp_ctrl *msm_dp_ctrl)
+void msm_dp_ctrl_push_idle(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *msm_dp_panel)
{
struct msm_dp_ctrl_private *ctrl;
+ u32 state = 0x0;
ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
+ if (!ctrl->mst_active)
+ state |= DP_STATE_CTRL_PUSH_IDLE;
+ else if (msm_dp_panel->stream_id == DP_STREAM_0)
+ state |= DP_DP0_PUSH_VCPF;
+ else if (msm_dp_panel->stream_id == DP_STREAM_1)
+ state |= DP_DP1_PUSH_VCPF;
+ else
+ state |= DP_MSTLINK_PUSH_VCPF;
+
reinit_completion(&ctrl->idle_comp);
- msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, DP_STATE_CTRL_PUSH_IDLE);
+
+ if (msm_dp_panel->stream_id > DP_STREAM_1)
+ msm_dp_write_mstlink(ctrl, msm_dp_panel->stream_id,
+ REG_DP_MSTLINK_STATE_CTRL, state);
+ else
+ msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, state);
if (!wait_for_completion_timeout(&ctrl->idle_comp,
IDLE_PATTERN_COMPLETION_TIMEOUT_JIFFIES))
@@ -2074,7 +2115,7 @@ void msm_dp_ctrl_set_psr(struct msm_dp_ctrl *msm_dp_ctrl, bool enter)
return;
}
- msm_dp_ctrl_push_idle(msm_dp_ctrl);
+ msm_dp_ctrl_push_idle(msm_dp_ctrl, ctrl->panel);
msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, 0);
msm_dp_ctrl_psr_mainlink_disable(ctrl);
@@ -2184,7 +2225,7 @@ static int msm_dp_ctrl_link_maintenance(struct msm_dp_ctrl_private *ctrl)
int ret = 0;
int training_step = DP_TRAINING_NONE;
- msm_dp_ctrl_push_idle(&ctrl->msm_dp_ctrl);
+ msm_dp_ctrl_push_idle(&ctrl->msm_dp_ctrl, ctrl->panel);
ctrl->link->phy_params.p_level = 0;
ctrl->link->phy_params.v_level = 0;
@@ -2994,6 +3035,13 @@ irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl)
ret = IRQ_HANDLED;
}
+ isr = msm_dp_ctrl_get_mst_interrupt(ctrl);
+ if (isr & (DP_INTR_DP0_VCPF_SENT | DP_INTR_DP1_VCPF_SENT)) {
+ drm_dbg_dp(ctrl->drm_dev, "vcpf sent\n");
+ complete(&ctrl->idle_comp);
+ ret = IRQ_HANDLED;
+ }
+
/* DP aux isr */
isr = msm_dp_ctrl_get_aux_interrupt(ctrl);
if (isr)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index 751f00c97b94dc3b9e8fae2a86e261f71f293425..356030fda9a749f0caa4438ffad88c3f34ce8960 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -22,7 +22,7 @@ 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(struct msm_dp_ctrl *msm_dp_ctrl);
void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl, enum msm_dp_stream_id stream_id);
-void msm_dp_ctrl_push_idle(struct msm_dp_ctrl *msm_dp_ctrl);
+void msm_dp_ctrl_push_idle(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *msm_dp_panel);
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);
struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev,
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 17633ba79aa7642856051b69227e8f5b23d76730..e02ada7a3dc3b89618aeadd998e9a41236ee6bbf 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1568,7 +1568,7 @@ void msm_dp_display_atomic_disable(struct msm_dp *msm_dp_display)
dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
- msm_dp_ctrl_push_idle(dp->ctrl);
+ msm_dp_ctrl_push_idle(dp->ctrl, dp->panel);
msm_dp_ctrl_mst_stream_channel_slot_setup(dp->ctrl,
dp->max_stream);
msm_dp_ctrl_mst_send_act(dp->ctrl);
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index ee4debf796910e00d370ab4c687009747bae5378..70fb647a25c05dc89f6aaf21456ddf2918cecbc3 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -42,9 +42,13 @@
#define DP_INTR_FRAME_END BIT(6)
#define DP_INTR_CRC_UPDATED BIT(9)
+#define DP_INTR_DP0_VCPF_SENT BIT(0)
+#define DP_INTR_DP1_VCPF_SENT BIT(3)
+
#define REG_DP_INTR_STATUS3 (0x00000028)
#define REG_DP_INTR_STATUS4 (0x0000002C)
+#define REG_DP_INTR_STATUS5 (0x00000034)
#define PSR_UPDATE_INT (0x00000001)
#define PSR_CAPTURE_INT (0x00000004)
#define PSR_EXIT_INT (0x00000010)
@@ -377,6 +381,8 @@
#define REG_DP_DP0_RG (0x000004F8)
#define REG_DP_DP1_RG (0x000004FC)
+#define REG_DP_MSTLINK_STATE_CTRL (0x00000000)
+
/* DP HDCP 1.3 registers */
#define DP_HDCP_CTRL (0x0A0)
#define DP_HDCP_STATUS (0x0A4)
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 22/38] drm/msm/dp: Always program MST_FIFO_CONSTANT_FILL for MST use cases
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (20 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 21/38] drm/msm/dp: Add support for sending VCPF packets in DP controller Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-25 21:55 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 23/38] drm/msm/dp: abstract out the dp_display stream helpers to accept a panel Yongxing Mou
` (15 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
As per the hardware programming guide, MST_FIFO_CONSTANT_FILL must
always be programmed when operating in MST mode. This patch ensures
the register is configured accordingly.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 2 ++
drivers/gpu/drm/msm/dp/dp_panel.c | 12 ++++++++++++
drivers/gpu/drm/msm/dp/dp_panel.h | 2 ++
3 files changed, 16 insertions(+)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 58e8c526253f77f306c669d474bdb2d1751b49a5..9f8733da78cea20593b4fd4d4a07583fd17d316b 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -693,6 +693,8 @@ static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl
msm_dp_ctrl_config_misc1_misc0(ctrl, msm_dp_panel);
msm_dp_panel_timing_cfg(msm_dp_panel, ctrl->msm_dp_ctrl.wide_bus_en);
+
+ msm_dp_panel_mst_async_fifo(msm_dp_panel, ctrl->mst_active);
}
/*
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index f792687c315a2c8203305a20b7290a93b0d791f4..f58cf38a47a119790f86b40ee86d45a3ffbd951f 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -694,6 +694,18 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en)
return 0;
}
+void msm_dp_panel_mst_async_fifo(struct msm_dp_panel *msm_dp_panel, bool mst_en)
+{
+ struct msm_dp_panel_private *panel;
+
+ panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
+
+ if (mst_en)
+ msm_dp_write_pn(panel, MMSS_DP_ASYNC_FIFO_CONFIG, 0x01);
+ else
+ msm_dp_write_pn(panel, MMSS_DP_ASYNC_FIFO_CONFIG, 0x00);
+}
+
int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel)
{
struct drm_display_mode *drm_mode;
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
index cb5bf6c99a6f7a68995f0f0ac48382dc90beca31..715ffc739ac4bec4d41bf4176a04d6651e81bbd4 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -74,6 +74,8 @@ void msm_dp_panel_clear_dsc_dto(struct msm_dp_panel *msm_dp_panel);
void msm_dp_panel_enable_vsc_sdp(struct msm_dp_panel *msm_dp_panel, struct dp_sdp *vsc_sdp);
void msm_dp_panel_disable_vsc_sdp(struct msm_dp_panel *msm_dp_panel);
+void msm_dp_panel_mst_async_fifo(struct msm_dp_panel *msm_dp_panel, bool mst_en);
+
/**
* is_link_rate_valid() - validates the link rate
* @lane_rate: link rate requested by the sink
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 23/38] drm/msm/dp: abstract out the dp_display stream helpers to accept a panel
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (21 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 22/38] drm/msm/dp: Always program MST_FIFO_CONSTANT_FILL for MST use cases Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-25 22:18 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 24/38] drm/msm/dp: replace power_on with active_stream_cnt for dp_display Yongxing Mou
` (14 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
Currently the dp_display bridge helpers, in particular the
dp_display_enable()/dp_display_disable() use the cached panel.
To be able to re-use these helpers for MST use-case abstract the
helpers to use the panel which is passed in to them.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 138 +++++++++++++++++++++++-------------
drivers/gpu/drm/msm/dp/dp_display.h | 12 ++++
2 files changed, 102 insertions(+), 48 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index e02ada7a3dc3b89618aeadd998e9a41236ee6bbf..0815973e6597492e09f33359d9777c0e8ce31e0d 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -695,6 +695,11 @@ static int msm_dp_display_prepare(struct msm_dp_display_private *dp)
drm_dbg_dp(dp->drm_dev, "sink_count=%d\n", dp->link->sink_count);
+ if (msm_dp_display->prepared) {
+ drm_dbg_dp(dp->drm_dev, "Link already setup, return\n");
+ return 0;
+ }
+
rc = pm_runtime_resume_and_get(&msm_dp_display->pdev->dev);
if (rc) {
DRM_ERROR("failed to pm_runtime_resume\n");
@@ -722,7 +727,8 @@ static int msm_dp_display_prepare(struct msm_dp_display_private *dp)
return rc;
}
-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;
@@ -733,7 +739,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, dp->max_stream);
+ rc = msm_dp_ctrl_on_stream(dp->ctrl, msm_dp_panel, dp->max_stream);
if (!rc)
msm_dp_display->power_on = true;
@@ -779,37 +785,17 @@ 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);
- /* 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
- */
-
- /* set dongle to D3 (power off) mode */
- msm_dp_link_psm_config(dp->link, &dp->panel->link_info, true);
- msm_dp_ctrl_off_pixel_clk(dp->ctrl, dp->panel->stream_id);
- msm_dp_ctrl_off_link(dp->ctrl);
- /* 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, dp->panel->stream_id);
- msm_dp_ctrl_off_link(dp->ctrl);
- msm_dp_display_host_phy_exit(dp);
- }
+ msm_dp_ctrl_off_pixel_clk(dp->ctrl, msm_dp_panel->stream_id);
msm_dp_display->power_on = false;
@@ -1538,52 +1524,90 @@ void msm_dp_display_atomic_prepare(struct msm_dp *msm_dp_display)
return;
}
-void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display)
+void msm_dp_display_enable_helper(struct msm_dp *msm_dp_display, struct msm_dp_panel *msm_dp_panel)
{
struct msm_dp_display_private *dp;
int rc = 0;
dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
- msm_dp_display_set_stream_info(msm_dp_display, dp->panel, 0, 0, 0, 0, 0);
-
if (msm_dp_display->prepared) {
- rc = msm_dp_display_enable(dp);
+ rc = msm_dp_display_enable(dp, msm_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, msm_dp_panel);
}
}
drm_dbg_dp(msm_dp_display->drm_dev, "type=%d Done\n", msm_dp_display->connector_type);
}
-void msm_dp_display_atomic_disable(struct msm_dp *msm_dp_display)
+void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display)
{
struct msm_dp_display_private *dp;
dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
- msm_dp_ctrl_push_idle(dp->ctrl, dp->panel);
+ msm_dp_display_set_stream_info(msm_dp_display, dp->panel, 0, 0, 0, 0, 0);
+
+ msm_dp_display_enable_helper(msm_dp_display, dp->panel);
+}
+
+void msm_dp_display_disable_helper(struct msm_dp *msm_dp_display,
+ struct msm_dp_panel *msm_dp_panel)
+{
+ struct msm_dp_display_private *dp;
+
+ dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
+
+ msm_dp_ctrl_push_idle(dp->ctrl, msm_dp_panel);
msm_dp_ctrl_mst_stream_channel_slot_setup(dp->ctrl,
dp->max_stream);
msm_dp_ctrl_mst_send_act(dp->ctrl);
}
-static void msm_dp_display_unprepare(struct msm_dp_display_private *dp)
+void msm_dp_display_atomic_disable(struct msm_dp *msm_dp_display)
{
- struct msm_dp *msm_dp_display = &dp->msm_dp_display;
+ struct msm_dp_display_private *dp;
+
+ dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
+
+ msm_dp_display_disable_helper(msm_dp_display, dp->panel);
+}
+
+void msm_dp_display_unprepare(struct msm_dp *msm_dp_display)
+{
+ struct msm_dp_display_private *dp;
+
+ dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
+
+ if (!msm_dp_display->prepared) {
+ drm_dbg_dp(dp->drm_dev, "Link already setup, return\n");
+ return;
+ }
+
+ /* 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_link(dp->ctrl);
+
+ /* re-init the PHY so that we can listen to Dongle disconnect */
+ if (dp->link->sink_count == 0)
+ msm_dp_ctrl_reinit_phy(dp->ctrl);
+ else
+ msm_dp_display_host_phy_exit(dp);
pm_runtime_put_sync(&msm_dp_display->pdev->dev);
msm_dp_display->prepared = false;
}
-void msm_dp_display_atomic_post_disable(struct msm_dp *dp)
+void msm_dp_display_atomic_post_disable_helper(struct msm_dp *dp, struct msm_dp_panel *msm_dp_panel)
{
struct msm_dp_display_private *msm_dp_display;
@@ -1597,28 +1621,46 @@ 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_panel);
+
+ drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type);
+}
+
+void msm_dp_display_atomic_post_disable(struct msm_dp *msm_dp_display)
+{
+ struct msm_dp_display_private *dp;
+
+ dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
+
+ msm_dp_display_atomic_post_disable_helper(msm_dp_display, dp->panel);
msm_dp_display_unprepare(msm_dp_display);
+}
- drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type);
+void msm_dp_display_mode_set_helper(struct msm_dp *msm_dp,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *adjusted_mode,
+ struct msm_dp_panel *msm_dp_panel)
+{
+ struct msm_dp_display_private *dp;
+
+ dp = container_of(msm_dp, struct msm_dp_display_private, msm_dp_display);
+
+ msm_dp_display_set_mode(msm_dp, adjusted_mode, 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;
}
-void msm_dp_display_mode_set(struct msm_dp *dp,
+void msm_dp_display_mode_set(struct msm_dp *msm_dp,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode)
{
- 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;
+ struct msm_dp_display_private *dp;
- msm_dp_display_set_mode(dp, adjusted_mode, msm_dp_panel);
+ dp = container_of(msm_dp, struct msm_dp_display_private, msm_dp_display);
- /* populate wide_bus_support to different layers */
- msm_dp_display->ctrl->wide_bus_en = msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 ?
- false : msm_dp_display->wide_bus_supported;
+ msm_dp_display_mode_set_helper(msm_dp, mode, adjusted_mode, dp->panel);
}
void msm_dp_bridge_hpd_enable(struct drm_bridge *bridge)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index fa92f763d2304f15af7c4e1e7e8aab5a6ffd3459..20b7ed735b3f428e894b82ae2756d0efcfa47624 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -51,5 +51,17 @@ enum drm_mode_status msm_dp_display_mode_valid(struct msm_dp *dp,
int msm_dp_display_set_stream_info(struct msm_dp *dp_display, struct msm_dp_panel *panel,
enum msm_dp_stream_id stream_id,
u32 start_slot, u32 num_slots, u32 pbn, int vcpi);
+void msm_dp_display_enable_helper(struct msm_dp *msm_dp_display,
+ struct msm_dp_panel *msm_dp_panel);
+void msm_dp_display_disable_helper(struct msm_dp *msm_dp_display,
+ struct msm_dp_panel *msm_dp_panel);
+void msm_dp_display_mode_set_helper(struct msm_dp *msm_dp_display,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *adjusted_mode,
+ struct msm_dp_panel *msm_dp_panel);
+void msm_dp_display_atomic_post_disable_helper(struct msm_dp *msm_dp_display,
+ struct msm_dp_panel *msm_dp_panel);
+
+void msm_dp_display_unprepare(struct msm_dp *dp);
#endif /* _DP_DISPLAY_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 24/38] drm/msm/dp: replace power_on with active_stream_cnt for dp_display
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (22 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 23/38] drm/msm/dp: abstract out the dp_display stream helpers to accept a panel Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-25 22:22 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 25/38] drm/msm/dp: Mark the SST bridge disconnected when mst is active Yongxing Mou
` (13 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
For DP MST, the link clock and power domain resources stay on until
both streams have been disabled OR we receive hotplug. Introduce an
active_stream_cnt to track the number of active streams and necessary
state handling. Replace the power_on variable with active_stream_cnt
as power_on boolean works only for a single stream.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_audio.c | 2 +-
drivers/gpu/drm/msm/dp/dp_display.c | 50 ++++++++++++++++++++++++-------------
drivers/gpu/drm/msm/dp/dp_display.h | 3 ++-
3 files changed, 36 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_audio.c b/drivers/gpu/drm/msm/dp/dp_audio.c
index 41018e82efa10ec863eb4b60d8df66c23c432fa5..e84c8b8e83d9ec689c0d29e8ac69860a745a4877 100644
--- a/drivers/gpu/drm/msm/dp/dp_audio.c
+++ b/drivers/gpu/drm/msm/dp/dp_audio.c
@@ -284,7 +284,7 @@ int msm_dp_audio_prepare(struct drm_bridge *bridge,
* such cases check for connection status and bail out if not
* connected.
*/
- if (!msm_dp_display->power_on) {
+ if (!msm_dp_display_get_active_stream_cnt(msm_dp_display)) {
rc = -EINVAL;
goto end;
}
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 0815973e6597492e09f33359d9777c0e8ce31e0d..e2e6b0ea2f9dbfe49a599ca19b1d205669365c4c 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -73,6 +73,8 @@ struct msm_dp_display_private {
bool wide_bus_supported;
+ u32 active_stream_cnt;
+
struct msm_dp_audio *audio;
void __iomem *ahb_base;
@@ -175,6 +177,15 @@ static const struct of_device_id msm_dp_dt_match[] = {
{}
};
+int msm_dp_display_get_active_stream_cnt(struct msm_dp *msm_dp_display)
+{
+ struct msm_dp_display_private *dp;
+
+ dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
+
+ return dp->active_stream_cnt;
+}
+
static struct msm_dp_display_private *dev_get_dp_display_private(struct device *dev)
{
struct msm_dp *dp = dev_get_drvdata(dev);
@@ -709,15 +720,17 @@ static int msm_dp_display_prepare(struct msm_dp_display_private *dp)
if (dp->link->sink_count == 0)
return rc;
- if (msm_dp_display->link_ready && !msm_dp_display->power_on) {
+ if (msm_dp_display->link_ready && !dp->active_stream_cnt) {
msm_dp_display_host_phy_init(dp);
force_link_train = true;
}
- rc = msm_dp_ctrl_on_link(dp->ctrl, msm_dp_display->mst_active);
- if (rc) {
- DRM_ERROR("Failed link training (rc=%d)\n", rc);
- msm_dp_display->connector->state->link_status = DRM_LINK_STATUS_BAD;
+ if (!dp->active_stream_cnt) {
+ rc = msm_dp_ctrl_on_link(dp->ctrl, msm_dp_display->mst_active);
+ if (rc) {
+ DRM_ERROR("Failed link training (rc=%d)\n", rc);
+ msm_dp_display->connector->state->link_status = DRM_LINK_STATUS_BAD;
+ }
}
rc = msm_dp_ctrl_prepare_stream_on(dp->ctrl, force_link_train);
@@ -731,17 +744,10 @@ 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;
drm_dbg_dp(dp->drm_dev, "sink_count=%d\n", dp->link->sink_count);
- if (msm_dp_display->power_on) {
- drm_dbg_dp(dp->drm_dev, "Link already setup, return\n");
- return 0;
- }
rc = msm_dp_ctrl_on_stream(dp->ctrl, msm_dp_panel, dp->max_stream);
- if (!rc)
- msm_dp_display->power_on = true;
return rc;
}
@@ -788,16 +794,14 @@ static void msm_dp_display_audio_notify_disable(struct msm_dp_display_private *d
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)
+ if (!dp->active_stream_cnt)
return 0;
msm_dp_panel_disable_vsc_sdp(msm_dp_panel);
msm_dp_ctrl_off_pixel_clk(dp->ctrl, msm_dp_panel->stream_id);
- msm_dp_display->power_on = false;
+ dp->active_stream_cnt--;
drm_dbg_dp(dp->drm_dev, "sink count: %d\n", dp->link->sink_count);
return 0;
@@ -934,7 +938,7 @@ void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp)
* power_on status before dumping DP registers to avoid crash due
* to unclocked access
*/
- if (!dp->power_on)
+ if (!msm_dp_display->active_stream_cnt)
return;
msm_disp_snapshot_add_block(disp_state, msm_dp_display->ahb_len,
@@ -1543,6 +1547,8 @@ void msm_dp_display_enable_helper(struct msm_dp *msm_dp_display, struct msm_dp_p
}
}
+ dp->active_stream_cnt++;
+
drm_dbg_dp(msm_dp_display->drm_dev, "type=%d Done\n", msm_dp_display->connector_type);
}
@@ -1564,6 +1570,11 @@ void msm_dp_display_disable_helper(struct msm_dp *msm_dp_display,
dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
+ if (!dp->active_stream_cnt) {
+ drm_dbg_dp(dp->drm_dev, "no active streams\n");
+ return;
+ }
+
msm_dp_ctrl_push_idle(dp->ctrl, msm_dp_panel);
msm_dp_ctrl_mst_stream_channel_slot_setup(dp->ctrl,
dp->max_stream);
@@ -1590,6 +1601,11 @@ void msm_dp_display_unprepare(struct msm_dp *msm_dp_display)
return;
}
+ if (dp->active_stream_cnt) {
+ drm_dbg_dp(dp->drm_dev, "stream still active, return\n");
+ return;
+ }
+
/* 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);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 20b7ed735b3f428e894b82ae2756d0efcfa47624..b1ea027438d952c94f3ae80725c92e46c631bdb2 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -19,7 +19,6 @@ struct msm_dp {
struct drm_bridge *bridge;
bool link_ready;
bool audio_enabled;
- bool power_on;
bool prepared;
bool mst_active;
unsigned int connector_type;
@@ -64,4 +63,6 @@ void msm_dp_display_atomic_post_disable_helper(struct msm_dp *msm_dp_display,
void msm_dp_display_unprepare(struct msm_dp *dp);
+int msm_dp_display_get_active_stream_cnt(struct msm_dp *msm_dp_display);
+
#endif /* _DP_DISPLAY_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 25/38] drm/msm/dp: Mark the SST bridge disconnected when mst is active
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (23 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 24/38] drm/msm/dp: replace power_on with active_stream_cnt for dp_display Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-25 22:23 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 26/38] drm/msm/dp: add an API to initialize MST on sink side Yongxing Mou
` (12 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
The bridge detect function is only applicable for SST. In MST mode,
connector detection is handled by MST bridges. This patch skips
detection for the SST bridge when MST is active.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index e2e6b0ea2f9dbfe49a599ca19b1d205669365c4c..cb433103d439ac6b8089bdecf0ee6be35c914db1 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -986,7 +986,7 @@ enum drm_connector_status msm_dp_bridge_detect(struct drm_bridge *bridge, struct
priv = container_of(dp, struct msm_dp_display_private, msm_dp_display);
- if (!dp->link_ready)
+ if (!dp->link_ready || dp->mst_active)
return status;
msm_dp_aux_enable_xfers(priv->aux, true);
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 26/38] drm/msm/dp: add an API to initialize MST on sink side
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (24 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 25/38] drm/msm/dp: Mark the SST bridge disconnected when mst is active Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-26 9:26 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 27/38] drm/msm/dp: add dp_display_get_panel() to initialize DP panel Yongxing Mou
` (11 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
If the DP controller is capable of supporting multiple streams
then initialize the DP sink in MST mode by programming the DP_MSTM_CTRL
DPCD register to enable MST mode.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 55 ++++++++++++++++++++++++++++++-------
1 file changed, 45 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index cb433103d439ac6b8089bdecf0ee6be35c914db1..84df34306fb557341bea288ea8c13b0c81b11919 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -14,6 +14,7 @@
#include <linux/string_choices.h>
#include <drm/display/drm_dp_aux_bus.h>
#include <drm/display/drm_hdmi_audio_helper.h>
+#include <drm/display/drm_dp_mst_helper.h>
#include <drm/drm_edid.h>
#include "msm_drv.h"
@@ -297,6 +298,35 @@ static int msm_dp_display_lttpr_init(struct msm_dp_display_private *dp, u8 *dpcd
return lttpr_count;
}
+static void msm_dp_display_mst_init(struct msm_dp_display_private *dp)
+{
+ const unsigned long clear_mstm_ctrl_timeout_us = 100000;
+ u8 old_mstm_ctrl;
+ struct msm_dp *msm_dp = &dp->msm_dp_display;
+ int ret;
+
+ /* clear sink mst state */
+ drm_dp_dpcd_readb(dp->aux, DP_MSTM_CTRL, &old_mstm_ctrl);
+ drm_dp_dpcd_writeb(dp->aux, DP_MSTM_CTRL, 0);
+
+ /* add extra delay if MST old state is on*/
+ if (old_mstm_ctrl) {
+ drm_dbg_dp(dp->drm_dev, "wait %luus to set DP_MSTM_CTRL set 0\n",
+ clear_mstm_ctrl_timeout_us);
+ usleep_range(clear_mstm_ctrl_timeout_us,
+ clear_mstm_ctrl_timeout_us + 1000);
+ }
+
+ ret = drm_dp_dpcd_writeb(dp->aux, DP_MSTM_CTRL,
+ DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC);
+ if (ret < 0) {
+ DRM_ERROR("sink mst enablement failed\n");
+ return;
+ }
+
+ msm_dp->mst_active = true;
+}
+
static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
{
struct drm_connector *connector = dp->msm_dp_display.connector;
@@ -315,18 +345,20 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
if (rc)
goto end;
- drm_edid = drm_edid_read_ddc(connector, &dp->aux->ddc);
- drm_edid_connector_update(connector, drm_edid);
+ if (!dp->mst_supported || !drm_dp_read_mst_cap(dp->aux, dp->panel->dpcd)) {
+ 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;
- }
+ 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;
+ }
- if (rc)
- goto end;
+ if (rc)
+ goto end;
+ }
msm_dp_link_process_request(dp->link);
@@ -349,6 +381,9 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
*/
msm_dp_link_psm_config(dp->link, &dp->panel->link_info, false);
+ if (dp->mst_supported && drm_dp_read_mst_cap(dp->aux, dp->panel->dpcd))
+ msm_dp_display_mst_init(dp);
+
msm_dp_link_reset_phy_params_vx_px(dp->link);
if (!dp->msm_dp_display.internal_hpd)
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 27/38] drm/msm/dp: add dp_display_get_panel() to initialize DP panel
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (25 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 26/38] drm/msm/dp: add an API to initialize MST on sink side Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-26 16:33 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 28/38] drm/msm/dp: add dp_mst_drm to manage DP MST bridge operations Yongxing Mou
` (10 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
Add an API dp_display_get_panel() to initialize and return a DP
panel to be used by DP MST module. Since some of the fields of
DP panel are private, dp_display module needs to initialize these
parts and return the panel back.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 23 +++++++++++++++++++++++
drivers/gpu/drm/msm/dp/dp_display.h | 2 ++
2 files changed, 25 insertions(+)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 84df34306fb557341bea288ea8c13b0c81b11919..abcab3ed43b6da5ef898355cf9b7561cd9fe0404 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -632,6 +632,29 @@ static int msm_dp_irq_hpd_handle(struct msm_dp_display_private *dp, u32 data)
return 0;
}
+struct msm_dp_panel *msm_dp_display_get_panel(struct msm_dp *msm_dp_display)
+{
+ struct msm_dp_display_private *dp;
+ struct msm_dp_panel *dp_panel;
+
+ dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
+
+ dp_panel = msm_dp_panel_get(&dp->msm_dp_display.pdev->dev, dp->aux, dp->link,
+ dp->link_base, dp->mst2link_base, dp->mst3link_base,
+ dp->pixel_base);
+
+ if (IS_ERR(dp->panel)) {
+ DRM_ERROR("failed to initialize panel\n");
+ return NULL;
+ }
+
+ memcpy(dp_panel->dpcd, dp->panel->dpcd, DP_RECEIVER_CAP_SIZE);
+ memcpy(&dp_panel->link_info, &dp->panel->link_info,
+ sizeof(dp->panel->link_info));
+
+ return dp_panel;
+}
+
static void msm_dp_display_deinit_sub_modules(struct msm_dp_display_private *dp)
{
msm_dp_audio_put(dp->audio);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index b1ea027438d952c94f3ae80725c92e46c631bdb2..d5889b801d190b6f33b180ead898c1e4ebcbf8f3 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -65,4 +65,6 @@ void msm_dp_display_unprepare(struct msm_dp *dp);
int msm_dp_display_get_active_stream_cnt(struct msm_dp *msm_dp_display);
+struct msm_dp_panel *msm_dp_display_get_panel(struct msm_dp *msm_dp_display);
+
#endif /* _DP_DISPLAY_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 28/38] drm/msm/dp: add dp_mst_drm to manage DP MST bridge operations
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (26 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 27/38] drm/msm/dp: add dp_display_get_panel() to initialize DP panel Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-26 17:36 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 29/38] drm/msm/dp: add MST atomic check to msm_atomic_check() Yongxing Mou
` (9 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
Add a new file dp_mst_drm to manage the DP MST bridge operations
similar to the dp_drm file which manages the SST bridge operations.
Each MST encoder creates one bridge and each bridge is bound to its
own dp_panel abstraction to manage the operations of its pipeline.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/Makefile | 3 +-
drivers/gpu/drm/msm/dp/dp_display.h | 3 +
drivers/gpu/drm/msm/dp/dp_mst_drm.c | 556 ++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/msm/dp/dp_mst_drm.h | 86 ++++++
4 files changed, 647 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 0c0dfb25f01b193b10946fae20138caf32cf0ed2..a61fa2637ff317ed4dee715de5d12a7befa987f5 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -142,7 +142,8 @@ msm-display-$(CONFIG_DRM_MSM_DP)+= dp/dp_aux.o \
dp/dp_link.o \
dp/dp_panel.o \
dp/dp_audio.o \
- dp/dp_utils.o
+ dp/dp_utils.o \
+ dp/dp_mst_drm.o
msm-display-$(CONFIG_DRM_MSM_HDMI_HDCP) += hdmi/hdmi_hdcp.o
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index d5889b801d190b6f33b180ead898c1e4ebcbf8f3..f958de6244b556df5452a5dbec6899fb79a57193 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -7,6 +7,7 @@
#define _DP_DISPLAY_H_
#include "dp_panel.h"
+#include "dp_mst_drm.h"
#include "disp/msm_disp_snapshot.h"
#define DP_MAX_PIXEL_CLK_KHZ 675000
@@ -25,6 +26,8 @@ struct msm_dp {
bool is_edp;
bool internal_hpd;
+ struct msm_dp_mst *msm_dp_mst;
+
struct msm_dp_audio *msm_dp_audio;
bool psr_supported;
};
diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
new file mode 100644
index 0000000000000000000000000000000000000000..73de29136801ef5f45e0b2d09280fe113021b68c
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
@@ -0,0 +1,556 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+/*
+ * Copyright © 2014 Red Hat.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include "dp_mst_drm.h"
+
+#define to_msm_dp_mst_bridge(x) container_of((x), struct msm_dp_mst_bridge, base)
+#define to_msm_dp_mst_bridge_priv(x) \
+ container_of((x), struct msm_dp_mst_bridge, obj)
+#define to_msm_dp_mst_bridge_state_priv(x) \
+ container_of((x), struct msm_dp_mst_bridge_state, base)
+#define to_msm_dp_mst_bridge_state(x) \
+ to_msm_dp_mst_bridge_state_priv((x)->obj.state)
+#define to_msm_dp_mst_connector(x) \
+ container_of((x), struct msm_dp_mst_connector, connector)
+
+#define DP_MST_CONN_ID(bridge) ((bridge)->connector ? \
+ (bridge)->connector->base.id : 0)
+
+#define MAX_DPCD_TRANSACTION_BYTES 16
+
+static struct drm_private_state *msm_dp_mst_duplicate_bridge_state(struct drm_private_obj *obj)
+{
+ struct msm_dp_mst_bridge_state *mst_bridge_state;
+
+ mst_bridge_state = kmemdup(obj->state, sizeof(*mst_bridge_state), GFP_KERNEL);
+ if (!mst_bridge_state)
+ return NULL;
+
+ __drm_atomic_helper_private_obj_duplicate_state(obj, &mst_bridge_state->base);
+
+ return &mst_bridge_state->base;
+}
+
+static void msm_dp_mst_destroy_bridge_state(struct drm_private_obj *obj,
+ struct drm_private_state *state)
+{
+ struct msm_dp_mst_bridge_state *mst_bridge_state =
+ to_msm_dp_mst_bridge_state_priv(state);
+
+ kfree(mst_bridge_state);
+}
+
+static const struct drm_private_state_funcs msm_dp_mst_bridge_state_funcs = {
+ .atomic_duplicate_state = msm_dp_mst_duplicate_bridge_state,
+ .atomic_destroy_state = msm_dp_mst_destroy_bridge_state,
+};
+
+/**
+ * dp_mst_find_vcpi_slots() - Find VCPI slots for this PBN value
+ * @mgr: manager to use
+ * @pbn: payload bandwidth to convert into slots.
+ *
+ * Calculate the number of VCPI slots that will be required for the given PBN
+ * value.
+ *
+ * RETURNS:
+ * The total slots required for this port, or error.
+ */
+static int msm_dp_mst_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, int pbn)
+{
+ int num_slots;
+ struct drm_dp_mst_topology_state *state;
+
+ state = to_drm_dp_mst_topology_state(mgr->base.state);
+ num_slots = DIV_ROUND_UP(pbn, dfixed_trunc(state->pbn_div));
+
+ /* max. time slots - one slot for MTP header */
+ if (num_slots > 63)
+ return -ENOSPC;
+ return num_slots;
+}
+
+static void msm_dp_mst_update_timeslots(struct msm_dp_mst *mst,
+ struct msm_dp_mst_bridge *mst_bridge,
+ struct drm_atomic_state *state,
+ struct drm_dp_mst_port *port)
+{
+ int i;
+ struct msm_dp_mst_bridge *msm_dp_bridge;
+ struct drm_dp_mst_topology_state *mst_state;
+ struct drm_dp_mst_atomic_payload *payload;
+ struct msm_dp_mst_bridge_state *mst_bridge_state;
+ int prev_start = 0;
+ int prev_slots = 0;
+
+ mst_state = to_drm_dp_mst_topology_state(mst->mst_mgr.base.state);
+ payload = drm_atomic_get_mst_payload_state(mst_state, port);
+
+ if (!payload) {
+ DRM_ERROR("mst bridge [%d] update_timeslots failed, null payload\n",
+ mst_bridge->id);
+ return;
+ }
+
+ for (i = 0; i < mst->max_streams; i++) {
+ msm_dp_bridge = mst->mst_bridge[i];
+ if (mst_bridge == msm_dp_bridge) {
+ mst_bridge_state = to_msm_dp_mst_bridge_state(msm_dp_bridge);
+ /*
+ * When a payload was removed make sure to move any payloads after it
+ * to the left so all payloads are aligned to the left.
+ */
+ if (payload->vc_start_slot < 0) {
+ // cache the payload
+ prev_start = mst_bridge_state->start_slot;
+ prev_slots = mst_bridge_state->num_slots;
+ mst_bridge_state->pbn = 0;
+ mst_bridge_state->start_slot = 1;
+ mst_bridge_state->num_slots = 0;
+ mst_bridge_state->vcpi = 0;
+ } else { //add payload
+ mst_bridge_state->pbn = payload->pbn;
+ mst_bridge_state->start_slot = payload->vc_start_slot;
+ mst_bridge_state->num_slots = payload->time_slots;
+ mst_bridge_state->vcpi = payload->vcpi;
+ }
+ }
+ }
+
+ // Now commit all the updated payloads
+ for (i = 0; i < mst->max_streams; i++) {
+ msm_dp_bridge = mst->mst_bridge[i];
+
+ mst_bridge_state = to_msm_dp_mst_bridge_state(msm_dp_bridge);
+ //Shift payloads to the left if there was a removed payload.
+ if (payload->vc_start_slot < 0 && mst_bridge_state->start_slot > prev_start)
+ mst_bridge_state->start_slot -= prev_slots;
+
+ msm_dp_display_set_stream_info(mst->msm_dp, msm_dp_bridge->msm_dp_panel,
+ msm_dp_bridge->id, mst_bridge_state->start_slot,
+ mst_bridge_state->num_slots,
+ mst_bridge_state->pbn, mst_bridge_state->vcpi);
+ drm_dbg_dp(mst->msm_dp->drm_dev,
+ "conn:%d vcpi:%d start_slot:%d num_slots:%d, pbn:%d\n",
+ DP_MST_CONN_ID(msm_dp_bridge), mst_bridge_state->vcpi,
+ mst_bridge_state->start_slot,
+ mst_bridge_state->num_slots, mst_bridge_state->pbn);
+ }
+}
+
+static int msm_dp_mst_bridge_pre_enable_part1(struct msm_dp_mst_bridge *dp_bridge,
+ struct drm_atomic_state *state)
+{
+ struct msm_dp *dp_display = dp_bridge->display;
+ struct msm_dp_mst *mst = dp_display->msm_dp_mst;
+ struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(dp_bridge->connector);
+ struct drm_dp_mst_port *port = mst_conn->mst_port;
+ struct drm_dp_mst_topology_state *mst_state;
+ struct drm_dp_mst_atomic_payload *payload;
+ struct msm_dp_panel *dp_panel = mst_conn->dp_panel;
+ int pbn, slots;
+ int rc = 0;
+
+ mst_state = drm_atomic_get_new_mst_topology_state(state, &mst->mst_mgr);
+
+ pbn = drm_dp_calc_pbn_mode(dp_panel->msm_dp_mode.drm_mode.clock,
+ (mst_conn->connector.display_info.bpc * 3) << 4);
+
+ slots = msm_dp_mst_find_vcpi_slots(&mst->mst_mgr, pbn);
+
+ drm_dbg_dp(dp_display->drm_dev, "conn:%d pbn:%d, slots:%d\n", DP_MST_CONN_ID(dp_bridge),
+ pbn, slots);
+
+ payload = drm_atomic_get_mst_payload_state(mst_state, port);
+ if (!payload || payload->time_slots <= 0) {
+ DRM_ERROR("time slots not allocated for conn:%d\n", DP_MST_CONN_ID(dp_bridge));
+ rc = -EINVAL;
+ return rc;
+ }
+
+ drm_dp_mst_update_slots(mst_state, DP_CAP_ANSI_8B10B);
+
+ rc = drm_dp_add_payload_part1(&mst->mst_mgr, mst_state, payload);
+ if (rc) {
+ DRM_ERROR("payload allocation failure for conn:%d\n", DP_MST_CONN_ID(dp_bridge));
+ return rc;
+ }
+
+ msm_dp_mst_update_timeslots(mst, dp_bridge, state, port);
+
+ return rc;
+}
+
+static void _msm_dp_mst_bridge_pre_enable_part2(struct msm_dp_mst_bridge *dp_bridge,
+ struct drm_atomic_state *state)
+{
+ struct msm_dp *dp_display = dp_bridge->display;
+ struct msm_dp_mst *mst = dp_display->msm_dp_mst;
+ struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(dp_bridge->connector);
+ struct drm_dp_mst_port *port = mst_conn->mst_port;
+ struct drm_dp_mst_topology_state *mst_state;
+ struct drm_dp_mst_atomic_payload *payload;
+
+ drm_dp_check_act_status(&mst->mst_mgr);
+
+ mst_state = to_drm_dp_mst_topology_state(mst->mst_mgr.base.state);
+ payload = drm_atomic_get_mst_payload_state(mst_state, port);
+
+ if (!payload) {
+ DRM_ERROR("mst bridge [%d] null payload\n", dp_bridge->id);
+ return;
+ }
+
+ if (!payload->port) {
+ DRM_ERROR("mst bridge [%d] null port\n", dp_bridge->id);
+ return;
+ }
+
+ if (!payload->port->connector) {
+ DRM_ERROR("mst bridge [%d] part-2 failed, null connector\n",
+ dp_bridge->id);
+ return;
+ }
+
+ if (payload->vc_start_slot == -1) {
+ DRM_ERROR("mst bridge [%d] part-2 failed, payload alloc part 1 failed\n",
+ dp_bridge->id);
+ return;
+ }
+
+ drm_dp_add_payload_part2(&mst->mst_mgr, payload);
+
+ drm_dbg_dp(dp_display->drm_dev, "mst bridge [%d] _pre enable part-2 complete\n",
+ dp_bridge->id);
+}
+
+static void msm_dp_mst_bridge_pre_disable_part1(struct msm_dp_mst_bridge *dp_bridge,
+ struct drm_atomic_state *state)
+{
+ struct msm_dp *dp_display = dp_bridge->display;
+ struct msm_dp_mst *mst = dp_display->msm_dp_mst;
+ struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(dp_bridge->connector);
+ struct drm_dp_mst_port *port = mst_conn->mst_port;
+ struct drm_dp_mst_topology_state *old_mst_state;
+ struct drm_dp_mst_topology_state *new_mst_state;
+ const struct drm_dp_mst_atomic_payload *old_payload;
+ struct drm_dp_mst_atomic_payload *new_payload;
+
+ old_mst_state = drm_atomic_get_old_mst_topology_state(state, &mst->mst_mgr);
+
+ new_mst_state = drm_atomic_get_new_mst_topology_state(state, &mst->mst_mgr);
+
+ old_payload = drm_atomic_get_mst_payload_state(old_mst_state, port);
+ new_payload = drm_atomic_get_mst_payload_state(new_mst_state, port);
+
+ if (!old_payload || !new_payload) {
+ DRM_ERROR("mst bridge [%d] _pre disable part-1 failed, null payload\n",
+ dp_bridge->id);
+ return;
+ }
+
+ drm_dp_remove_payload_part1(&mst->mst_mgr, new_mst_state, new_payload);
+ drm_dp_remove_payload_part2(&mst->mst_mgr, new_mst_state, old_payload, new_payload);
+
+ msm_dp_mst_update_timeslots(mst, dp_bridge, state, port);
+
+ drm_dbg_dp(dp_display->drm_dev, "mst bridge [%d] _pre disable part-1 complete\n",
+ dp_bridge->id);
+}
+
+static void msm_dp_mst_bridge_atomic_pre_enable(struct drm_bridge *drm_bridge,
+ struct drm_atomic_state *state)
+{
+ int rc = 0;
+ struct msm_dp_mst_bridge *bridge;
+ struct msm_dp *dp_display;
+ struct msm_dp_mst_bridge_state *mst_bridge_state;
+ struct msm_dp_mst *dp_mst;
+
+ if (!drm_bridge) {
+ DRM_ERROR("Invalid params\n");
+ return;
+ }
+
+ bridge = to_msm_dp_mst_bridge(drm_bridge);
+ mst_bridge_state = to_msm_dp_mst_bridge_state(bridge);
+ dp_display = bridge->display;
+ dp_mst = dp_display->msm_dp_mst;
+
+ /* to cover cases of bridge_disable/bridge_enable without modeset */
+ bridge->connector = mst_bridge_state->connector;
+ bridge->msm_dp_panel = mst_bridge_state->msm_dp_panel;
+
+ if (!bridge->connector) {
+ DRM_ERROR("Invalid connector\n");
+ return;
+ }
+
+ mutex_lock(&dp_mst->mst_lock);
+ msm_dp_display_atomic_prepare(dp_display);
+
+ rc = msm_dp_mst_bridge_pre_enable_part1(bridge, state);
+ if (rc) {
+ DRM_ERROR("[%d] DP display pre-enable failed, rc=%d\n", bridge->id, rc);
+ msm_dp_display_unprepare(dp_display);
+ mutex_unlock(&dp_mst->mst_lock);
+ return;
+ }
+
+ msm_dp_display_enable_helper(dp_display, bridge->msm_dp_panel);
+
+ _msm_dp_mst_bridge_pre_enable_part2(bridge, state);
+
+ mutex_unlock(&dp_mst->mst_lock);
+
+ drm_dbg_dp(dp_display->drm_dev, "conn:%d mode:%s fps:%d vcpi:%d slots:%d to %d\n",
+ DP_MST_CONN_ID(bridge), bridge->msm_dp_panel->msm_dp_mode.drm_mode.name,
+ drm_mode_vrefresh(&bridge->msm_dp_panel->msm_dp_mode.drm_mode),
+ mst_bridge_state->vcpi, mst_bridge_state->start_slot,
+ mst_bridge_state->start_slot + mst_bridge_state->num_slots);
+}
+
+static void msm_dp_mst_bridge_atomic_disable(struct drm_bridge *drm_bridge,
+ struct drm_atomic_state *state)
+{
+ struct msm_dp_mst_bridge *bridge;
+ struct msm_dp *dp_display;
+ struct msm_dp_mst *mst;
+
+ if (!drm_bridge) {
+ DRM_ERROR("Invalid params\n");
+ return;
+ }
+
+ bridge = to_msm_dp_mst_bridge(drm_bridge);
+ if (!bridge->connector) {
+ DRM_ERROR("Invalid connector\n");
+ return;
+ }
+
+ dp_display = bridge->display;
+ mst = dp_display->msm_dp_mst;
+
+ mutex_lock(&mst->mst_lock);
+
+ msm_dp_mst_bridge_pre_disable_part1(bridge, state);
+
+ msm_dp_display_disable_helper(dp_display, bridge->msm_dp_panel);
+
+ drm_dp_check_act_status(&mst->mst_mgr);
+
+ mutex_unlock(&mst->mst_lock);
+
+ drm_dbg_dp(dp_display->drm_dev, "mst bridge:%d conn:%d disable complete\n", bridge->id,
+ DP_MST_CONN_ID(bridge));
+}
+
+static void msm_dp_mst_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
+ struct drm_atomic_state *state)
+{
+ int conn = 0;
+ struct msm_dp_mst_bridge *bridge;
+ struct msm_dp *dp_display;
+ struct msm_dp_mst *mst;
+
+ if (!drm_bridge) {
+ DRM_ERROR("Invalid params\n");
+ return;
+ }
+
+ bridge = to_msm_dp_mst_bridge(drm_bridge);
+ if (!bridge->connector) {
+ DRM_ERROR("Invalid connector\n");
+ return;
+ }
+
+ conn = DP_MST_CONN_ID(bridge);
+
+ dp_display = bridge->display;
+ mst = dp_display->msm_dp_mst;
+
+ mutex_lock(&mst->mst_lock);
+
+ msm_dp_display_atomic_post_disable_helper(dp_display, bridge->msm_dp_panel);
+
+ if (!dp_display->mst_active)
+ msm_dp_display_unprepare(dp_display);
+
+ bridge->connector = NULL;
+ bridge->msm_dp_panel = NULL;
+
+ mutex_unlock(&mst->mst_lock);
+
+ drm_dbg_dp(dp_display->drm_dev, "mst bridge:%d conn:%d post disable complete\n",
+ bridge->id, conn);
+}
+
+static int msm_dp_mst_bridge_atomic_check(struct drm_bridge *drm_bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct drm_atomic_state *state = crtc_state->state;
+ struct drm_connector *connector = conn_state->connector;
+ struct drm_dp_mst_topology_state *mst_state;
+ struct msm_dp_mst_connector *mst_conn;
+ struct msm_dp *dp_display;
+ struct msm_dp_mst *mst;
+ int rc = 0, pbn, slots;
+ u32 bpp;
+
+ if (!drm_atomic_crtc_needs_modeset(crtc_state) || !crtc_state->enable) {
+ return 0;
+ }
+
+ mst_conn = to_msm_dp_mst_connector(connector);
+ dp_display = mst_conn->msm_dp;
+ mst = dp_display->msm_dp_mst;
+
+ bpp = connector->display_info.bpc * 3;
+
+ if (!bpp)
+ bpp = 24;
+
+ pbn = drm_dp_calc_pbn_mode(crtc_state->mode.clock, bpp << 4);
+
+ mst_state = to_drm_dp_mst_topology_state(mst->mst_mgr.base.state);
+ if (IS_ERR(mst_state))
+ return PTR_ERR(mst_state);
+
+ if (!dfixed_trunc(mst_state->pbn_div)) {
+ mst_state->pbn_div =
+ drm_dp_get_vc_payload_bw(mst_conn->dp_panel->link_info.rate,
+ mst_conn->dp_panel->link_info.num_lanes);
+ }
+
+ slots = drm_dp_atomic_find_time_slots(state, &mst->mst_mgr, mst_conn->mst_port, pbn);
+
+ drm_dbg_dp(dp_display->drm_dev, "add slots, conn:%d pbn:%d slots:%d rc:%d\n",
+ connector->base.id, pbn, slots, rc);
+
+ return 0;
+}
+
+static void msm_dp_mst_bridge_mode_set(struct drm_bridge *drm_bridge,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *adjusted_mode)
+{
+ struct msm_dp_mst_bridge *bridge;
+ struct msm_dp_mst_bridge_state *mst_bridge_state;
+ struct msm_dp *dp_display;
+ struct msm_dp_panel *msm_dp_panel;
+
+ if (!drm_bridge || !mode || !adjusted_mode) {
+ DRM_ERROR("Invalid params\n");
+ return;
+ }
+
+ bridge = to_msm_dp_mst_bridge(drm_bridge);
+
+ mst_bridge_state = to_msm_dp_mst_bridge_state(bridge);
+ bridge->connector = mst_bridge_state->connector;
+ bridge->msm_dp_panel = mst_bridge_state->msm_dp_panel;
+
+ msm_dp_panel = bridge->msm_dp_panel;
+ dp_display = bridge->display;
+
+ msm_dp_display_mode_set_helper(dp_display, mode, adjusted_mode, bridge->msm_dp_panel);
+ msm_dp_panel->pbn = drm_dp_calc_pbn_mode(msm_dp_panel->msm_dp_mode.drm_mode.clock,
+ (msm_dp_panel->msm_dp_mode.bpp << 4));
+ drm_dbg_dp(dp_display->drm_dev, "mst bridge:%d conn:%d mode set complete %s\n", bridge->id,
+ DP_MST_CONN_ID(bridge), mode->name);
+}
+
+/* DP MST Bridge APIs */
+static const struct drm_bridge_funcs msm_dp_mst_bridge_ops = {
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_pre_enable = msm_dp_mst_bridge_atomic_pre_enable,
+ .atomic_disable = msm_dp_mst_bridge_atomic_disable,
+ .atomic_post_disable = msm_dp_mst_bridge_atomic_post_disable,
+ .atomic_check = msm_dp_mst_bridge_atomic_check,
+ .mode_set = msm_dp_mst_bridge_mode_set,
+};
+
+int msm_dp_mst_drm_bridge_init(struct msm_dp *dp_display, struct drm_encoder *encoder)
+{
+ int rc = 0;
+ struct msm_dp_mst_bridge *bridge = NULL;
+ struct msm_dp_mst_bridge_state *mst_bridge_state;
+ struct drm_device *dev;
+ struct msm_dp_mst *mst = dp_display->msm_dp_mst;
+ int i;
+
+ for (i = 0; i < mst->max_streams; i++) {
+ if (!mst->mst_bridge[i]->in_use) {
+ bridge = mst->mst_bridge[i];
+ bridge->encoder = encoder;
+ bridge->in_use = true;
+ bridge->id = i;
+ break;
+ }
+ }
+
+ if (i == mst->max_streams) {
+ DRM_ERROR("mst supports only %d bridges\n", i);
+ rc = -EACCES;
+ goto end;
+ }
+
+ dev = dp_display->drm_dev;
+ bridge->display = dp_display;
+ bridge->base.funcs = &msm_dp_mst_bridge_ops;
+ bridge->base.encoder = encoder;
+ bridge->base.type = dp_display->connector_type;
+ bridge->base.ops = DRM_BRIDGE_OP_MODES;
+ drm_bridge_add(&bridge->base);
+
+ rc = drm_bridge_attach(encoder, &bridge->base, NULL, 0);
+ if (rc) {
+ DRM_ERROR("failed to attach bridge, rc=%d\n", rc);
+ goto end;
+ }
+
+ mst_bridge_state = kzalloc(sizeof(*mst_bridge_state), GFP_KERNEL);
+ if (!mst_bridge_state) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ drm_atomic_private_obj_init(dev, &bridge->obj,
+ &mst_bridge_state->base,
+ &msm_dp_mst_bridge_state_funcs);
+
+ drm_dbg_dp(dp_display->drm_dev, "mst drm bridge init. bridge id:%d\n", i);
+
+ return 0;
+
+end:
+ return rc;
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.h b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
new file mode 100644
index 0000000000000000000000000000000000000000..d75731ca2e5870377026e8ad1057bdcc5f0d4c78
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef _DP_MST_DRM_H_
+#define _DP_MST_DRM_H_
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/version.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_fixed.h>
+#include <drm/drm_connector.h>
+#include <drm/display/drm_dp_helper.h>
+#include <drm/display/drm_dp_mst_helper.h>
+
+#include "dp_panel.h"
+#include "dp_display.h"
+
+struct msm_dp_mst_bridge {
+ struct drm_bridge base;
+ struct drm_private_obj obj;
+ u32 id;
+
+ bool in_use;
+
+ struct msm_dp *display;
+ struct drm_encoder *encoder;
+
+ struct drm_connector *connector;
+ struct msm_dp_panel *msm_dp_panel;
+};
+
+struct msm_dp_mst_bridge_state {
+ struct drm_private_state base;
+ struct drm_connector *connector;
+ struct msm_dp_panel *msm_dp_panel;
+
+ int vcpi;
+ int pbn;
+ int num_slots;
+ int start_slot;
+};
+
+struct msm_dp_mst {
+ struct drm_dp_mst_topology_mgr mst_mgr;
+ struct msm_dp_mst_bridge *mst_bridge[DP_STREAM_MAX];
+ struct msm_dp *msm_dp;
+ u32 max_streams;
+ struct mutex mst_lock;
+};
+
+struct msm_dp_mst_connector {
+ struct drm_connector connector;
+ struct drm_dp_mst_port *mst_port;
+ struct msm_dp *msm_dp;
+ struct msm_dp_panel *dp_panel;
+};
+
+int msm_dp_mst_drm_bridge_init(struct msm_dp *dp, struct drm_encoder *encoder);
+
+#endif /* _DP_MST_DRM_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 29/38] drm/msm/dp: add MST atomic check to msm_atomic_check()
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (27 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 28/38] drm/msm/dp: add dp_mst_drm to manage DP MST bridge operations Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-26 17:44 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 30/38] drm/msm/dp: add connector abstraction for DP MST Yongxing Mou
` (8 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
Add a call to drm_dp_mst_atomic_check() within msm_atomic_check() to ensure
proper validation of atomic state changes related to DisplayPort
Multi-Stream Transport (MST).
This complements the existing drm_atomic_helper_check() and allows the MST
framework to perform its own consistency checks during atomic updates,
such as validating connector states and topology changes.
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/msm_atomic.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
index 87a91148a731dc911f30695add4c8f5002770220..3aa0020dec3a90b693ad3d4171cfcffc091aad4c 100644
--- a/drivers/gpu/drm/msm/msm_atomic.c
+++ b/drivers/gpu/drm/msm/msm_atomic.c
@@ -6,6 +6,7 @@
#include <drm/drm_atomic_uapi.h>
#include <drm/drm_vblank.h>
+#include <drm/display/drm_dp_mst_helper.h>
#include "msm_atomic_trace.h"
#include "msm_drv.h"
@@ -207,7 +208,11 @@ int msm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
if (ret)
return ret;
- return drm_atomic_helper_check(dev, state);
+ ret = drm_atomic_helper_check(dev, state);
+ if (ret)
+ return ret;
+
+ return drm_dp_mst_atomic_check(state);
}
void msm_atomic_commit_tail(struct drm_atomic_state *state)
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 30/38] drm/msm/dp: add connector abstraction for DP MST
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (28 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 29/38] drm/msm/dp: add MST atomic check to msm_atomic_check() Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-26 18:31 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 31/38] drm/msm/dp: add HPD callback for dp MST Yongxing Mou
` (7 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
Add connector abstraction for the DP MST. Each MST encoder
is connected through a DRM bridge to a MST connector and each
MST connector has a DP panel abstraction attached to it.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_mst_drm.c | 391 +++++++++++++++++++++++++++++++++++-
drivers/gpu/drm/msm/dp/dp_mst_drm.h | 3 +
2 files changed, 393 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
index 73de29136801ef5f45e0b2d09280fe113021b68c..b4f640134af544c77ab262d2cbe0b67e1e2e1b3a 100644
--- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
@@ -25,6 +25,8 @@
* OF THIS SOFTWARE.
*/
+#include <drm/drm_edid.h>
+#include <drm/drm_managed.h>
#include "dp_mst_drm.h"
#define to_msm_dp_mst_bridge(x) container_of((x), struct msm_dp_mst_bridge, base)
@@ -525,7 +527,6 @@ int msm_dp_mst_drm_bridge_init(struct msm_dp *dp_display, struct drm_encoder *en
dev = dp_display->drm_dev;
bridge->display = dp_display;
- bridge->base.funcs = &msm_dp_mst_bridge_ops;
bridge->base.encoder = encoder;
bridge->base.type = dp_display->connector_type;
bridge->base.ops = DRM_BRIDGE_OP_MODES;
@@ -554,3 +555,391 @@ int msm_dp_mst_drm_bridge_init(struct msm_dp *dp_display, struct drm_encoder *en
end:
return rc;
}
+
+static struct msm_dp_mst_bridge_state *msm_dp_mst_br_priv_state(struct drm_atomic_state *st,
+ struct msm_dp_mst_bridge *bridge)
+{
+ struct drm_device *dev = bridge->base.dev;
+ struct drm_private_state *obj_state = drm_atomic_get_private_obj_state(st, &bridge->obj);
+
+ WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+
+ return to_msm_dp_mst_bridge_state_priv(obj_state);
+}
+
+/* DP MST Connector OPs */
+static int
+msm_dp_mst_connector_detect(struct drm_connector *connector,
+ struct drm_modeset_acquire_ctx *ctx,
+ bool force)
+{
+ struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(connector);
+ struct msm_dp *dp_display = mst_conn->msm_dp;
+ struct msm_dp_mst *mst = dp_display->msm_dp_mst;
+ enum drm_connector_status status = connector_status_disconnected;
+
+ if (drm_connector_is_unregistered(&mst_conn->connector))
+ return status;
+
+ if (dp_display->link_ready && dp_display->mst_active)
+ status = drm_dp_mst_detect_port(connector,
+ ctx, &mst->mst_mgr, mst_conn->mst_port);
+
+ drm_dbg_dp(dp_display->drm_dev, "conn:%d status:%d\n", connector->base.id, status);
+
+ return status;
+}
+
+static int msm_dp_mst_connector_get_modes(struct drm_connector *connector)
+{
+ struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(connector);
+ struct msm_dp *dp_display = mst_conn->msm_dp;
+ struct msm_dp_mst *mst = dp_display->msm_dp_mst;
+ const struct drm_edid *drm_edid;
+
+ if (drm_connector_is_unregistered(&mst_conn->connector))
+ return drm_edid_connector_update(connector, NULL);
+
+ drm_edid = drm_dp_mst_edid_read(connector, &mst->mst_mgr, mst_conn->mst_port);
+ drm_edid_connector_update(connector, drm_edid);
+
+ return drm_edid_connector_add_modes(connector);
+}
+
+static enum drm_mode_status msm_dp_mst_connector_mode_valid(struct drm_connector *connector,
+ const struct drm_display_mode *mode)
+{
+ struct msm_dp_mst_connector *mst_conn;
+ struct msm_dp *dp_display;
+ struct drm_dp_mst_port *mst_port;
+ struct msm_dp_panel *dp_panel;
+ struct msm_dp_mst *mst;
+ struct msm_dp_mst_bridge_state *mst_bridge_state;
+ u16 full_pbn, required_pbn;
+ int i, active_enc_cnt = 0;
+
+ if (drm_connector_is_unregistered(connector))
+ return 0;
+
+ mst_conn = to_msm_dp_mst_connector(connector);
+ dp_display = mst_conn->msm_dp;
+ mst = dp_display->msm_dp_mst;
+ mst_port = mst_conn->mst_port;
+ dp_panel = mst_conn->dp_panel;
+
+ if (!dp_panel || !mst_port)
+ return MODE_ERROR;
+
+ for (i = 0; i < mst->max_streams; i++) {
+ mst_bridge_state = to_msm_dp_mst_bridge_state(mst->mst_bridge[i]);
+ if (mst_bridge_state->connector &&
+ mst_bridge_state->connector != connector)
+ active_enc_cnt++;
+ }
+
+ if (active_enc_cnt < DP_STREAM_MAX)
+ full_pbn = mst_port->full_pbn;
+ else {
+ DRM_ERROR("all MST streams are active\n");
+ return MODE_BAD;
+ }
+
+ required_pbn = drm_dp_calc_pbn_mode(mode->clock, (connector->display_info.bpc * 3) << 4);
+
+ if (required_pbn > full_pbn) {
+ drm_dbg_dp(dp_display->drm_dev, "mode:%s not supported. pbn %d vs %d\n",
+ mode->name, required_pbn, full_pbn);
+ return MODE_BAD;
+ }
+
+ return msm_dp_display_mode_valid(dp_display, &dp_panel->connector->display_info, mode);
+}
+
+static struct drm_encoder *
+msm_dp_mst_atomic_best_encoder(struct drm_connector *connector, struct drm_atomic_state *state)
+{
+ struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(connector);
+ struct msm_dp *dp_display = mst_conn->msm_dp;
+ struct msm_dp_mst *mst = dp_display->msm_dp_mst;
+ struct drm_encoder *enc = NULL;
+ struct msm_dp_mst_bridge_state *mst_bridge_state;
+ u32 i;
+ struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(state,
+ connector);
+
+ if (conn_state && conn_state->best_encoder)
+ return conn_state->best_encoder;
+
+ for (i = 0; i < mst->max_streams; i++) {
+ mst_bridge_state = msm_dp_mst_br_priv_state(state, mst->mst_bridge[i]);
+ if (IS_ERR(mst_bridge_state))
+ goto end;
+
+ if (mst_bridge_state->connector == connector) {
+ enc = mst->mst_bridge[i]->encoder;
+ goto end;
+ }
+ }
+
+ for (i = 0; i < mst->max_streams; i++) {
+ mst_bridge_state = msm_dp_mst_br_priv_state(state, mst->mst_bridge[i]);
+
+ if (!mst_bridge_state->connector) {
+ mst_bridge_state->connector = connector;
+ mst_bridge_state->msm_dp_panel = mst_conn->dp_panel;
+ enc = mst->mst_bridge[i]->encoder;
+ break;
+ }
+ }
+
+end:
+ if (enc)
+ drm_dbg_dp(dp_display->drm_dev, "mst connector:%d atomic best encoder:%d\n",
+ connector->base.id, i);
+ else
+ drm_dbg_dp(dp_display->drm_dev, "mst connector:%d atomic best encoder failed\n",
+ connector->base.id);
+
+ return enc;
+}
+
+static int msm_dp_mst_connector_atomic_check(struct drm_connector *connector,
+ struct drm_atomic_state *state)
+{
+ int rc = 0, slots;
+ struct drm_connector_state *old_conn_state;
+ struct drm_connector_state *new_conn_state;
+ struct drm_crtc *old_crtc;
+ struct drm_crtc_state *crtc_state;
+ struct msm_dp_mst_bridge *bridge;
+ struct msm_dp_mst_bridge_state *mst_bridge_state;
+ struct drm_bridge *drm_bridge;
+ struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(connector);
+ struct msm_dp *dp_display = mst_conn->msm_dp;
+ struct msm_dp_mst *mst = dp_display->msm_dp_mst;
+ struct drm_dp_mst_atomic_payload *payload;
+ struct drm_dp_mst_topology_state *mst_state;
+
+ if (!state)
+ return rc;
+
+ new_conn_state = drm_atomic_get_new_connector_state(state, connector);
+ if (!new_conn_state)
+ return rc;
+
+ old_conn_state = drm_atomic_get_old_connector_state(state, connector);
+ if (!old_conn_state)
+ goto end;
+
+ old_crtc = old_conn_state->crtc;
+ if (!old_crtc)
+ goto end;
+
+ crtc_state = drm_atomic_get_new_crtc_state(state, old_crtc);
+
+ /* attempt to release vcpi slots on a modeset change for crtc state */
+ if (drm_atomic_crtc_needs_modeset(crtc_state)) {
+ if (WARN_ON(!old_conn_state->best_encoder)) {
+ rc = -EINVAL;
+ goto end;
+ }
+
+ drm_bridge = drm_bridge_chain_get_first_bridge(old_conn_state->best_encoder);
+ if (WARN_ON(!drm_bridge)) {
+ rc = -EINVAL;
+ goto end;
+ }
+ bridge = to_msm_dp_mst_bridge(drm_bridge);
+
+ mst_bridge_state = msm_dp_mst_br_priv_state(state, bridge);
+
+ mst_state = to_drm_dp_mst_topology_state(mst->mst_mgr.base.state);
+
+ payload = drm_atomic_get_mst_payload_state(mst_state, mst_conn->mst_port);
+
+ slots = payload->time_slots;
+ if (slots > 0) {
+ rc = drm_dp_atomic_release_time_slots(state,
+ &mst->mst_mgr,
+ mst_conn->mst_port);
+ if (rc) {
+ DRM_ERROR("failed releasing %d vcpi slots %d\n", slots, rc);
+ goto end;
+ }
+ }
+
+ if (!new_conn_state->crtc) {
+ /* for cases where crtc is not disabled the slots are not
+ * freed by drm_dp_atomic_release_time_slots. this results
+ * in subsequent atomic_check failing since internal slots
+ * were freed but not the DP MST mgr's
+ */
+ mst_bridge_state->num_slots = 0;
+ mst_bridge_state->connector = NULL;
+ mst_bridge_state->msm_dp_panel = NULL;
+
+ drm_dbg_dp(dp_display->drm_dev, "clear best encoder: %d\n", bridge->id);
+ }
+ }
+
+end:
+ drm_dbg_dp(dp_display->drm_dev, "mst connector:%d atomic check ret %d\n",
+ connector->base.id, rc);
+ return rc;
+}
+
+static void dp_mst_connector_destroy(struct drm_connector *connector)
+{
+ struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(connector);
+
+ drm_connector_cleanup(connector);
+ drm_dp_mst_put_port_malloc(mst_conn->mst_port);
+ kfree(mst_conn);
+}
+
+/* DRM MST callbacks */
+static const struct drm_connector_helper_funcs msm_dp_drm_mst_connector_helper_funcs = {
+ .get_modes = msm_dp_mst_connector_get_modes,
+ .detect_ctx = msm_dp_mst_connector_detect,
+ .mode_valid = msm_dp_mst_connector_mode_valid,
+ .atomic_best_encoder = msm_dp_mst_atomic_best_encoder,
+ .atomic_check = msm_dp_mst_connector_atomic_check,
+};
+
+static const struct drm_connector_funcs msm_dp_drm_mst_connector_funcs = {
+ .reset = drm_atomic_helper_connector_reset,
+ .destroy = dp_mst_connector_destroy,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static struct drm_connector *
+msm_dp_mst_add_connector(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_port *port, const char *pathprop)
+{
+ struct msm_dp_mst *dp_mst;
+ struct drm_device *dev;
+ struct msm_dp *dp_display;
+ struct msm_dp_mst_connector *mst_conn;
+ struct drm_connector *connector;
+ int rc, i;
+
+ dp_mst = container_of(mgr, struct msm_dp_mst, mst_mgr);
+
+ dp_display = dp_mst->msm_dp;
+ dev = dp_display->drm_dev;
+
+ mst_conn = kzalloc(sizeof(*mst_conn), GFP_KERNEL);
+
+ if (!mst_conn)
+ return NULL;
+
+ drm_modeset_lock_all(dev);
+
+ connector = &mst_conn->connector;
+ rc = drm_connector_dynamic_init(dev, connector,
+ &msm_dp_drm_mst_connector_funcs,
+ DRM_MODE_CONNECTOR_DisplayPort, NULL);
+ if (rc) {
+ kfree(mst_conn);
+ drm_modeset_unlock_all(dev);
+ return NULL;
+ }
+
+ mst_conn->dp_panel = msm_dp_display_get_panel(dp_display);
+ if (!mst_conn->dp_panel) {
+ DRM_ERROR("failed to get dp_panel for connector\n");
+ kfree(mst_conn);
+ drm_modeset_unlock_all(dev);
+ return NULL;
+ }
+
+ mst_conn->dp_panel->connector = connector;
+ mst_conn->msm_dp = dp_display;
+
+ drm_connector_helper_add(connector, &msm_dp_drm_mst_connector_helper_funcs);
+
+ if (connector->funcs->reset)
+ connector->funcs->reset(connector);
+
+ /* add all encoders as possible encoders */
+ for (i = 0; i < dp_mst->max_streams; i++) {
+ rc = drm_connector_attach_encoder(connector, dp_mst->mst_bridge[i]->encoder);
+
+ if (rc) {
+ DRM_ERROR("failed to attach encoder to connector, %d\n", rc);
+ kfree(mst_conn);
+ drm_modeset_unlock_all(dev);
+ return NULL;
+ }
+ }
+
+ mst_conn->mst_port = port;
+ drm_dp_mst_get_port_malloc(mst_conn->mst_port);
+
+ drm_object_attach_property(&connector->base,
+ dev->mode_config.path_property, 0);
+ drm_object_attach_property(&connector->base,
+ dev->mode_config.tile_property, 0);
+
+ drm_modeset_unlock_all(dev);
+
+ drm_dbg_dp(dp_display->drm_dev, "add MST connector id:%d\n", connector->base.id);
+
+ return connector;
+}
+
+static const struct drm_dp_mst_topology_cbs msm_dp_mst_drm_cbs = {
+ .add_connector = msm_dp_mst_add_connector,
+};
+
+int msm_dp_mst_init(struct msm_dp *dp_display, u32 max_streams, struct drm_dp_aux *drm_aux)
+{
+ struct drm_device *dev;
+ int conn_base_id = 0;
+ int ret;
+ struct msm_dp_mst *msm_dp_mst;
+
+ if (!dp_display) {
+ DRM_ERROR("invalid params\n");
+ return 0;
+ }
+
+ dev = dp_display->drm_dev;
+
+ msm_dp_mst = devm_kzalloc(dev->dev, sizeof(*msm_dp_mst), GFP_KERNEL);
+ if (!msm_dp_mst)
+ return -ENOMEM;
+
+ memset(&msm_dp_mst->mst_mgr, 0, sizeof(msm_dp_mst->mst_mgr));
+ msm_dp_mst->mst_mgr.cbs = &msm_dp_mst_drm_cbs;
+ conn_base_id = dp_display->connector->base.id;
+ msm_dp_mst->msm_dp = dp_display;
+ msm_dp_mst->max_streams = max_streams;
+
+ for (int i = 0; i < DP_STREAM_MAX; i++) {
+ msm_dp_mst->mst_bridge[i] = devm_drm_bridge_alloc(dev->dev,
+ struct msm_dp_mst_bridge, base,
+ &msm_dp_mst_bridge_ops);
+ }
+
+ msm_dp_mst->dp_aux = drm_aux;
+
+ ret = drm_dp_mst_topology_mgr_init(&msm_dp_mst->mst_mgr, dev,
+ drm_aux,
+ MAX_DPCD_TRANSACTION_BYTES,
+ max_streams,
+ conn_base_id);
+ if (ret) {
+ DRM_ERROR("DP DRM MST topology manager init failed\n");
+ return ret;
+ }
+
+ dp_display->msm_dp_mst = msm_dp_mst;
+
+ mutex_init(&msm_dp_mst->mst_lock);
+
+ drm_dbg_dp(dp_display->drm_dev, "DP DRM MST topology manager init completed\n");
+ return ret;
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.h b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
index d75731ca2e5870377026e8ad1057bdcc5f0d4c78..1484fabd92ad0075eac5369aac8ca462acbd3eda 100644
--- a/drivers/gpu/drm/msm/dp/dp_mst_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
@@ -70,6 +70,7 @@ struct msm_dp_mst {
struct drm_dp_mst_topology_mgr mst_mgr;
struct msm_dp_mst_bridge *mst_bridge[DP_STREAM_MAX];
struct msm_dp *msm_dp;
+ struct drm_dp_aux *dp_aux;
u32 max_streams;
struct mutex mst_lock;
};
@@ -83,4 +84,6 @@ struct msm_dp_mst_connector {
int msm_dp_mst_drm_bridge_init(struct msm_dp *dp, struct drm_encoder *encoder);
+int msm_dp_mst_init(struct msm_dp *dp_display, u32 max_streams, struct drm_dp_aux *drm_aux);
+
#endif /* _DP_MST_DRM_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 31/38] drm/msm/dp: add HPD callback for dp MST
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (29 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 30/38] drm/msm/dp: add connector abstraction for DP MST Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-26 18:40 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 32/38] drm/msm/dp: propagate MST state changes to dp mst module Yongxing Mou
` (6 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
Add HPD callback for the MST module which shall be invoked from the
dp_display's HPD handler to perform MST specific operations in case
of HPD. In MST case, route the HPD messages to MST module.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 15 ++++++++++++---
drivers/gpu/drm/msm/dp/dp_mst_drm.c | 34 ++++++++++++++++++++++++++++++++++
drivers/gpu/drm/msm/dp/dp_mst_drm.h | 2 ++
3 files changed, 48 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index abcab3ed43b6da5ef898355cf9b7561cd9fe0404..59720e1ad4b1193e33a4fc6aad0c401eaf9cbec8 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -500,9 +500,16 @@ static int msm_dp_display_handle_irq_hpd(struct msm_dp_display_private *dp)
static int msm_dp_display_usbpd_attention_cb(struct device *dev)
{
- int rc = 0;
- u32 sink_request;
struct msm_dp_display_private *dp = dev_get_dp_display_private(dev);
+ struct msm_dp *msm_dp_display = &dp->msm_dp_display;
+ u32 sink_request;
+ int rc = 0;
+
+ if (msm_dp_display->mst_active) {
+ if (msm_dp_aux_is_link_connected(dp->aux) != ISR_DISCONNECTED)
+ msm_dp_mst_display_hpd_irq(&dp->msm_dp_display);
+ return 0;
+ }
/* check for any test request issued by sink */
rc = msm_dp_link_process_request(dp->link);
@@ -1129,8 +1136,10 @@ static irqreturn_t msm_dp_display_irq_thread(int irq, void *dev_id)
if (hpd_isr_status & DP_DP_HPD_UNPLUG_INT_MASK)
msm_dp_display_send_hpd_notification(dp, false);
- if (hpd_isr_status & DP_DP_IRQ_HPD_INT_MASK)
+ if (hpd_isr_status & DP_DP_IRQ_HPD_INT_MASK) {
msm_dp_display_send_hpd_notification(dp, true);
+ msm_dp_irq_hpd_handle(dp, 0);
+ }
ret = IRQ_HANDLED;
diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
index b4f640134af544c77ab262d2cbe0b67e1e2e1b3a..331d08854049d9c74d49aa231f3507539986099e 100644
--- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
@@ -567,6 +567,40 @@ static struct msm_dp_mst_bridge_state *msm_dp_mst_br_priv_state(struct drm_atomi
return to_msm_dp_mst_bridge_state_priv(obj_state);
}
+/* DP MST HPD IRQ callback */
+void msm_dp_mst_display_hpd_irq(struct msm_dp *dp_display)
+{
+ int rc;
+ struct msm_dp_mst *mst = dp_display->msm_dp_mst;
+ u8 ack[8] = {};
+ u8 esi[4];
+ unsigned int esi_res = DP_SINK_COUNT_ESI + 1;
+ bool handled;
+
+ rc = drm_dp_dpcd_read(mst->dp_aux, DP_SINK_COUNT_ESI,
+ esi, 4);
+ if (rc != 4) {
+ DRM_ERROR("dpcd sink status read failed, rlen=%d\n", rc);
+ return;
+ }
+
+ drm_dbg_dp(dp_display->drm_dev, "mst irq: esi1[0x%x] esi2[0x%x] esi3[%x]\n",
+ esi[1], esi[2], esi[3]);
+
+ rc = drm_dp_mst_hpd_irq_handle_event(&mst->mst_mgr, esi, ack, &handled);
+
+ /* ack the request */
+ if (handled) {
+ rc = drm_dp_dpcd_writeb(mst->dp_aux, esi_res, ack[1]);
+
+ if (rc != 1)
+ DRM_ERROR("dpcd esi_res failed. rc=%d\n", rc);
+
+ drm_dp_mst_hpd_irq_send_new_request(&mst->mst_mgr);
+ }
+ drm_dbg_dp(dp_display->drm_dev, "mst display hpd_irq handled:%d rc:%d\n", handled, rc);
+}
+
/* DP MST Connector OPs */
static int
msm_dp_mst_connector_detect(struct drm_connector *connector,
diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.h b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
index 1484fabd92ad0075eac5369aac8ca462acbd3eda..5e1b4db8aea4506b0e1cc1cc68980dd617d3f72a 100644
--- a/drivers/gpu/drm/msm/dp/dp_mst_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
@@ -86,4 +86,6 @@ int msm_dp_mst_drm_bridge_init(struct msm_dp *dp, struct drm_encoder *encoder);
int msm_dp_mst_init(struct msm_dp *dp_display, u32 max_streams, struct drm_dp_aux *drm_aux);
+void msm_dp_mst_display_hpd_irq(struct msm_dp *dp_display);
+
#endif /* _DP_MST_DRM_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 32/38] drm/msm/dp: propagate MST state changes to dp mst module
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (30 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 31/38] drm/msm/dp: add HPD callback for dp MST Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-26 18:43 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 33/38] drm/msm: add support for MST non-blocking commits Yongxing Mou
` (5 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
Introduce APIs to update the MST state change to MST framework when
device is plugged/unplugged.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 10 +++++++++-
drivers/gpu/drm/msm/dp/dp_mst_drm.c | 15 +++++++++++++++
drivers/gpu/drm/msm/dp/dp_mst_drm.h | 1 +
3 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 59720e1ad4b1193e33a4fc6aad0c401eaf9cbec8..909c84a5c97f56138d0d62c5d856d2fd18d36b8c 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -28,6 +28,7 @@
#include "dp_drm.h"
#include "dp_audio.h"
#include "dp_debug.h"
+#include "dp_mst_drm.h"
static bool psr_enabled = false;
module_param(psr_enabled, bool, 0);
@@ -269,7 +270,6 @@ static int msm_dp_display_send_hpd_notification(struct msm_dp_display_private *d
dp->panel->video_test = false;
}
-
drm_dbg_dp(dp->drm_dev, "type=%d hpd=%d\n",
dp->msm_dp_display.connector_type, hpd);
@@ -386,6 +386,9 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
msm_dp_link_reset_phy_params_vx_px(dp->link);
+ if (dp->msm_dp_display.mst_active)
+ msm_dp_mst_display_set_mgr_state(&dp->msm_dp_display, true);
+
if (!dp->msm_dp_display.internal_hpd)
msm_dp_display_send_hpd_notification(dp, true);
@@ -608,6 +611,11 @@ static int msm_dp_hpd_unplug_handle(struct msm_dp_display_private *dp, u32 data)
if (!dp->msm_dp_display.internal_hpd)
msm_dp_display_send_hpd_notification(dp, false);
+ if (dp->msm_dp_display.mst_active) {
+ msm_dp_mst_display_set_mgr_state(&dp->msm_dp_display, false);
+ dp->msm_dp_display.mst_active = false;
+ }
+
/* signal the disconnect event early to ensure proper teardown */
msm_dp_display_handle_plugged_change(&dp->msm_dp_display, false);
diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
index 331d08854049d9c74d49aa231f3507539986099e..ca654b1963467c8220dd7ee073f25216455d0490 100644
--- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
@@ -924,6 +924,21 @@ msm_dp_mst_add_connector(struct drm_dp_mst_topology_mgr *mgr,
return connector;
}
+int msm_dp_mst_display_set_mgr_state(struct msm_dp *dp_display, bool state)
+{
+ int rc;
+ struct msm_dp_mst *mst = dp_display->msm_dp_mst;
+
+ rc = drm_dp_mst_topology_mgr_set_mst(&mst->mst_mgr, state);
+ if (rc < 0) {
+ DRM_ERROR("failed to set topology mgr state to %d. rc %d\n",
+ state, rc);
+ }
+
+ drm_dbg_dp(dp_display->drm_dev, "dp_mst_display_set_mgr_state state:%d\n", state);
+ return rc;
+}
+
static const struct drm_dp_mst_topology_cbs msm_dp_mst_drm_cbs = {
.add_connector = msm_dp_mst_add_connector,
};
diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.h b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
index 5e1b4db8aea4506b0e1cc1cc68980dd617d3f72a..8fe6cbbe741da4abb232256b3a15ba6b16ca4f3e 100644
--- a/drivers/gpu/drm/msm/dp/dp_mst_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
@@ -87,5 +87,6 @@ int msm_dp_mst_drm_bridge_init(struct msm_dp *dp, struct drm_encoder *encoder);
int msm_dp_mst_init(struct msm_dp *dp_display, u32 max_streams, struct drm_dp_aux *drm_aux);
void msm_dp_mst_display_hpd_irq(struct msm_dp *dp_display);
+int msm_dp_mst_display_set_mgr_state(struct msm_dp *dp_display, bool state);
#endif /* _DP_MST_DRM_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 33/38] drm/msm: add support for MST non-blocking commits
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (31 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 32/38] drm/msm/dp: propagate MST state changes to dp mst module Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-26 18:47 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 34/38] drm/msm: initialize DRM MST encoders for DP controllers Yongxing Mou
` (4 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
Integrate MST framework APIs with atomic_commit_setup() and
atomic_commit_tail() to support non-blocking atomic commits
for DisplayPort MST.
This patch only applies to MST. For SST, non-blocking commits are
already handled via commit_tail(), which internally calls
drm_atomic_helper_wait_for_dependencies() in the DRM core.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/msm_atomic.c | 3 +++
drivers/gpu/drm/msm/msm_kms.c | 2 ++
2 files changed, 5 insertions(+)
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
index 3aa0020dec3a90b693ad3d4171cfcffc091aad4c..b1656fb456d54af11ba8a30d4971fface114c7a1 100644
--- a/drivers/gpu/drm/msm/msm_atomic.c
+++ b/drivers/gpu/drm/msm/msm_atomic.c
@@ -4,6 +4,7 @@
* Author: Rob Clark <robdclark@gmail.com>
*/
+#include <drm/display/drm_dp_mst_helper.h>
#include <drm/drm_atomic_uapi.h>
#include <drm/drm_vblank.h>
#include <drm/display/drm_dp_mst_helper.h>
@@ -226,6 +227,8 @@ void msm_atomic_commit_tail(struct drm_atomic_state *state)
trace_msm_atomic_commit_tail_start(async, crtc_mask);
+ drm_dp_mst_atomic_wait_for_dependencies(state);
+
kms->funcs->enable_commit(kms);
/*
diff --git a/drivers/gpu/drm/msm/msm_kms.c b/drivers/gpu/drm/msm/msm_kms.c
index 6889f1c1e72121dcc735fa460ea04cdab11c6705..09776be1d3d854f4c77d7df3afa8d56f53639411 100644
--- a/drivers/gpu/drm/msm/msm_kms.c
+++ b/drivers/gpu/drm/msm/msm_kms.c
@@ -10,6 +10,7 @@
#include <linux/sched/mm.h>
#include <uapi/linux/sched/types.h>
+#include <drm/display/drm_dp_mst_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_mode_config.h>
#include <drm/drm_vblank.h>
@@ -29,6 +30,7 @@ static const struct drm_mode_config_funcs mode_config_funcs = {
static const struct drm_mode_config_helper_funcs mode_config_helper_funcs = {
.atomic_commit_tail = msm_atomic_commit_tail,
+ .atomic_commit_setup = drm_dp_mst_atomic_setup_commit,
};
static irqreturn_t msm_irq(int irq, void *arg)
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 34/38] drm/msm: initialize DRM MST encoders for DP controllers
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (32 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 33/38] drm/msm: add support for MST non-blocking commits Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-26 18:55 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 35/38] drm/msm/dp: initialize dp_mst module for each DP MST controller Yongxing Mou
` (3 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
Initialize a DPMST encoder for each MST capable DP controller
and the number of encoders it supports depends on the number
of streams it supports.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 2 ++
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 23 ++++++++++++++++++++++-
drivers/gpu/drm/msm/dp/dp_mst_drm.h | 2 --
drivers/gpu/drm/msm/msm_drv.h | 13 +++++++++++++
4 files changed, 37 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
index ca1ca2e51d7ead0eb34b27f3168e6bb06a71a11a..2eb4c39b111c1d8622e09e78ffafef017e28bbf6 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
@@ -28,6 +28,7 @@
* @h_tile_instance: Controller instance used per tile. Number of elements is
* based on num_of_h_tiles
* @is_cmd_mode Boolean to indicate if the CMD mode is requested
+ * @stream_id stream id for which the interface needs to be acquired
* @vsync_source: Source of the TE signal for DSI CMD devices
*/
struct msm_display_info {
@@ -35,6 +36,7 @@ struct msm_display_info {
uint32_t num_of_h_tiles;
uint32_t h_tile_instance[MAX_H_TILES_PER_DISPLAY];
bool is_cmd_mode;
+ int stream_id;
enum dpu_vsync_source vsync_source;
};
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index 12dcb32b472497f9e59619db4e810abfbf610c7c..0b9d9207f4f69e0d0725ff265c624828b5816a8b 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -653,7 +653,8 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev,
struct msm_display_info info;
bool yuv_supported;
int rc;
- int i;
+ int i, stream_id;
+ int stream_cnt;
for (i = 0; i < ARRAY_SIZE(priv->kms->dp); i++) {
if (!priv->kms->dp[i])
@@ -676,6 +677,26 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev,
DPU_ERROR("modeset_init failed for DP, rc = %d\n", rc);
return rc;
}
+
+ stream_cnt = msm_dp_get_mst_max_stream(priv->kms->dp[i]);
+
+ if (stream_cnt > 1) {
+ for (stream_id = 0; stream_id < stream_cnt; stream_id++) {
+ info.stream_id = stream_id;
+ encoder = dpu_encoder_init(dev, DRM_MODE_ENCODER_DPMST, &info);
+ if (IS_ERR(encoder)) {
+ DPU_ERROR("encoder init failed for dp mst display\n");
+ return PTR_ERR(encoder);
+ }
+
+ rc = msm_dp_mst_drm_bridge_init(priv->kms->dp[i], encoder);
+ if (rc) {
+ DPU_ERROR("dp mst bridge %d init failed, %d\n",
+ stream_id, rc);
+ continue;
+ }
+ }
+ }
}
return 0;
diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.h b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
index 8fe6cbbe741da4abb232256b3a15ba6b16ca4f3e..d73e3f908439094532e88945ed4d41ed092051c9 100644
--- a/drivers/gpu/drm/msm/dp/dp_mst_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
@@ -82,8 +82,6 @@ struct msm_dp_mst_connector {
struct msm_dp_panel *dp_panel;
};
-int msm_dp_mst_drm_bridge_init(struct msm_dp *dp, struct drm_encoder *encoder);
-
int msm_dp_mst_init(struct msm_dp *dp_display, u32 max_streams, struct drm_dp_aux *drm_aux);
void msm_dp_mst_display_hpd_irq(struct msm_dp *dp_display);
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 985db9febd98e35dfed51d39dac1a522abe5a351..3e64ec7b7dbe1d1107e85def9aa80277131f40bf 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -363,6 +363,9 @@ bool msm_dp_needs_periph_flush(const struct msm_dp *dp_display,
const struct drm_display_mode *mode);
bool msm_dp_wide_bus_available(const struct msm_dp *dp_display);
+int msm_dp_get_mst_max_stream(struct msm_dp *dp_display);
+int msm_dp_mst_drm_bridge_init(struct msm_dp *dp_display, struct drm_encoder *encoder);
+
#else
static inline int __init msm_dp_register(void)
{
@@ -379,6 +382,16 @@ static inline int msm_dp_modeset_init(struct msm_dp *dp_display,
return -EINVAL;
}
+static inline int msm_dp_get_mst_max_stream(struct msm_dp *dp_display)
+{
+ return -EINVAL;
+}
+
+static inline int msm_dp_mst_drm_bridge_init(struct msm_dp *dp_display, struct drm_encoder *encoder)
+{
+ return -EINVAL;
+}
+
static inline void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp_display)
{
}
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 35/38] drm/msm/dp: initialize dp_mst module for each DP MST controller
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (33 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 34/38] drm/msm: initialize DRM MST encoders for DP controllers Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-26 21:27 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 36/38] drm/msm/dpu: use msm_dp_get_mst_intf_id() to get the intf id Yongxing Mou
` (2 subsequent siblings)
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
For each MST capable DP controller, initialize a dp_mst module to
manage its DP MST operations. The DP MST module for each controller
is the central entity to manage its topology related operations as
well as interfacing with the rest of the DP driver.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 6 ++++++
drivers/gpu/drm/msm/dp/dp_display.c | 9 +++++++++
drivers/gpu/drm/msm/msm_drv.h | 6 ++++++
3 files changed, 21 insertions(+)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index 0b9d9207f4f69e0d0725ff265c624828b5816a8b..4036d3445946930e635401109ac4720ed2282c2f 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -681,6 +681,12 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev,
stream_cnt = msm_dp_get_mst_max_stream(priv->kms->dp[i]);
if (stream_cnt > 1) {
+ rc = msm_dp_mst_register(priv->kms->dp[i]);
+ if (rc) {
+ DPU_ERROR("dp_mst_init failed for DP, rc = %d\n", rc);
+ return rc;
+ }
+
for (stream_id = 0; stream_id < stream_cnt; stream_id++) {
info.stream_id = stream_id;
encoder = dpu_encoder_init(dev, DRM_MODE_ENCODER_DPMST, &info);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 909c84a5c97f56138d0d62c5d856d2fd18d36b8c..897ef653b3cea08904bb3595e8ac10fd7fcf811f 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1586,6 +1586,15 @@ int msm_dp_modeset_init(struct msm_dp *msm_dp_display, struct drm_device *dev,
return 0;
}
+inline int msm_dp_mst_register(struct msm_dp *msm_dp_display)
+{
+ struct msm_dp_display_private *dp;
+
+ dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
+
+ return msm_dp_mst_init(msm_dp_display, dp->max_stream, dp->aux);
+}
+
void msm_dp_display_atomic_prepare(struct msm_dp *msm_dp_display)
{
int rc = 0;
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 3e64ec7b7dbe1d1107e85def9aa80277131f40bf..c46c88cf06598df996a17c23631570fda078b371 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -365,6 +365,7 @@ bool msm_dp_wide_bus_available(const struct msm_dp *dp_display);
int msm_dp_get_mst_max_stream(struct msm_dp *dp_display);
int msm_dp_mst_drm_bridge_init(struct msm_dp *dp_display, struct drm_encoder *encoder);
+int msm_dp_mst_register(struct msm_dp *dp_display);
#else
static inline int __init msm_dp_register(void)
@@ -392,6 +393,11 @@ static inline int msm_dp_mst_drm_bridge_init(struct msm_dp *dp_display, struct d
return -EINVAL;
}
+static inline int msm_dp_mst_register(struct msm_dp *dp_display)
+{
+ return -EINVAL;
+}
+
static inline void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp_display)
{
}
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 36/38] drm/msm/dpu: use msm_dp_get_mst_intf_id() to get the intf id
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (34 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 35/38] drm/msm/dp: initialize dp_mst module for each DP MST controller Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-26 23:42 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 37/38] drm/msm/dp: fix the intf_type of MST interfaces Yongxing Mou
2025-08-25 14:16 ` [PATCH v3 38/38] drm/msm/dp: Add MST stream support for SA8775P DP controller 0 and 1 Yongxing Mou
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
Use msm_dp_get_mst_intf_id() to get the interface ID for the DP MST
controller as the intf_id is unique for each MST stream of each
DP controller.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 51 +++++++++++++++++++++++++----
1 file changed, 44 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index 05e5f3463e30c9a6bd5b740580720ae2bf6b3246..2eb5397d15732b224372c68d0b2b7167da9f2896 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -1429,17 +1429,52 @@ static void dpu_encoder_virt_atomic_disable(struct drm_encoder *drm_enc,
static struct dpu_hw_intf *dpu_encoder_get_intf(const struct dpu_mdss_cfg *catalog,
struct dpu_rm *dpu_rm,
- enum dpu_intf_type type, u32 controller_id)
+ enum dpu_intf_type type, int enc_type, u32 id)
{
- int i = 0;
+ int i = 0, cnt = 0;
+ int controller_id = id >> 16;
+ int stream_id = id & 0x0F;
if (type == INTF_WB)
return NULL;
- for (i = 0; i < catalog->intf_count; i++) {
- if (catalog->intf[i].type == type
- && catalog->intf[i].controller_id == controller_id) {
- return dpu_rm_get_intf(dpu_rm, catalog->intf[i].id);
+ if (enc_type == DRM_MODE_ENCODER_DPMST) {
+ /* The intf order in dpu_intf_cfg matches the mapping in the DP HPG.
+ * example:
+ * DPU_8_4_0 - DP Controller intf to stream Mapping
+ *
+ * +-------------+----------+----------+----------+----------+
+ * | stream_id | 0 | 1 | 2 | 3 |
+ * +-------------+----------+----------+----------+----------+
+ * | DP0 | INTF_0 | INTF_3 | INTF_6 | INTF_7 |
+ * | DP1 | INTF_4 | INTF_8 | | |
+ * +-------------+----------+----------+----------+----------+
+ *
+ * DPU_9_2_0 - DP Controller intf to stream Mapping
+ *
+ * +-------------+----------+----------+
+ * | Controller | 0 | 1 |
+ * +-------------+----------+----------+
+ * | DP0 | INTF_0 | INTF_3 |
+ * | DP1 | INTF_4 | INTF_8 |
+ * | DP2 | INTF_6 | INTF_7 |
+ * +-------------+----------+----------+
+ */
+ DPU_DEBUG("controller_id %d for stream_id = %d\n", controller_id, stream_id);
+ for (i = 0; i < catalog->intf_count; i++) {
+ if (catalog->intf[i].type == INTF_DP
+ && controller_id == catalog->intf[i].controller_id) {
+ if (cnt == stream_id)
+ return dpu_rm_get_intf(dpu_rm, catalog->intf[i].id);
+ cnt++;
+ }
+ }
+ } else {
+ for (i = 0; i < catalog->intf_count; i++) {
+ if (catalog->intf[i].type == type
+ && catalog->intf[i].controller_id == controller_id) {
+ return dpu_rm_get_intf(dpu_rm, catalog->intf[i].id);
+ }
}
}
@@ -2670,7 +2705,9 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
phys_params.hw_intf = dpu_encoder_get_intf(dpu_kms->catalog, &dpu_kms->rm,
disp_info->intf_type,
- controller_id);
+ dpu_enc->base.encoder_type,
+ controller_id << 16
+ | disp_info->stream_id);
if (disp_info->intf_type == INTF_WB && controller_id < WB_MAX)
phys_params.hw_wb = dpu_rm_get_wb(&dpu_kms->rm, controller_id);
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 37/38] drm/msm/dp: fix the intf_type of MST interfaces
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (35 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 36/38] drm/msm/dpu: use msm_dp_get_mst_intf_id() to get the intf id Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-27 1:18 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 38/38] drm/msm/dp: Add MST stream support for SA8775P DP controller 0 and 1 Yongxing Mou
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
From: Abhinav Kumar <quic_abhinavk@quicinc.com>
Interface type of MST interfaces is currently INTF_NONE. Update this to
INTF_DP. And correct the intf_6 intr_underrun/intr_vsync index for
dpu_8_4_sa8775p.
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h | 6 +++---
drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h | 12 ++++++------
drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h | 6 +++---
3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h
index 303d33dc7783ac91a496fa0a19860564ad0b6d5d..ea2329f0b5e977bb0ee3035ec1b3a3e23bbe5b1e 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h
@@ -319,7 +319,7 @@ static const struct dpu_intf_cfg sc8280xp_intf[] = {
}, {
.name = "intf_3", .id = INTF_3,
.base = 0x37000, .len = 0x280,
- .type = INTF_NONE,
+ .type = INTF_DP,
.controller_id = MSM_DP_CONTROLLER_0,
.prog_fetch_lines_worst_case = 24,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30),
@@ -351,7 +351,7 @@ static const struct dpu_intf_cfg sc8280xp_intf[] = {
}, {
.name = "intf_7", .id = INTF_7,
.base = 0x3b000, .len = 0x280,
- .type = INTF_NONE,
+ .type = INTF_DP,
.controller_id = MSM_DP_CONTROLLER_2,
.prog_fetch_lines_worst_case = 24,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 18),
@@ -359,7 +359,7 @@ static const struct dpu_intf_cfg sc8280xp_intf[] = {
}, {
.name = "intf_8", .id = INTF_8,
.base = 0x3c000, .len = 0x280,
- .type = INTF_NONE,
+ .type = INTF_DP,
.controller_id = MSM_DP_CONTROLLER_1,
.prog_fetch_lines_worst_case = 24,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 12),
diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h
index 0f7b4a224e4c971f482c3778c92e8c170b44223f..00fd0c8cc115a4a108363f6185edf93e771b2bf5 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h
@@ -347,7 +347,7 @@ static const struct dpu_intf_cfg sa8775p_intf[] = {
}, {
.name = "intf_3", .id = INTF_3,
.base = 0x37000, .len = 0x280,
- .type = INTF_NONE,
+ .type = INTF_DP,
.controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */
.prog_fetch_lines_worst_case = 24,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30),
@@ -363,15 +363,15 @@ static const struct dpu_intf_cfg sa8775p_intf[] = {
}, {
.name = "intf_6", .id = INTF_6,
.base = 0x3A000, .len = 0x280,
- .type = INTF_NONE,
+ .type = INTF_DP,
.controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */
.prog_fetch_lines_worst_case = 24,
- .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 17),
- .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 16),
+ .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 16),
+ .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 17),
}, {
.name = "intf_7", .id = INTF_7,
.base = 0x3b000, .len = 0x280,
- .type = INTF_NONE,
+ .type = INTF_DP,
.controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */
.prog_fetch_lines_worst_case = 24,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 18),
@@ -379,7 +379,7 @@ static const struct dpu_intf_cfg sa8775p_intf[] = {
}, {
.name = "intf_8", .id = INTF_8,
.base = 0x3c000, .len = 0x280,
- .type = INTF_NONE,
+ .type = INTF_DP,
.controller_id = MSM_DP_CONTROLLER_1, /* pair with intf_4 for DP MST */
.prog_fetch_lines_worst_case = 24,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 12),
diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h
index 7243eebb85f36f2a8ae848f2c95d21b0bc3bebef..826f65adb18b118cf8b70208837aa7979c5701b6 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h
@@ -335,7 +335,7 @@ static const struct dpu_intf_cfg x1e80100_intf[] = {
}, {
.name = "intf_3", .id = INTF_3,
.base = 0x37000, .len = 0x280,
- .type = INTF_NONE,
+ .type = INTF_DP,
.controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */
.prog_fetch_lines_worst_case = 24,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30),
@@ -367,7 +367,7 @@ static const struct dpu_intf_cfg x1e80100_intf[] = {
}, {
.name = "intf_7", .id = INTF_7,
.base = 0x3b000, .len = 0x280,
- .type = INTF_NONE,
+ .type = INTF_DP,
.controller_id = MSM_DP_CONTROLLER_2, /* pair with intf_6 for DP MST */
.prog_fetch_lines_worst_case = 24,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 18),
@@ -375,7 +375,7 @@ static const struct dpu_intf_cfg x1e80100_intf[] = {
}, {
.name = "intf_8", .id = INTF_8,
.base = 0x3c000, .len = 0x280,
- .type = INTF_NONE,
+ .type = INTF_DP,
.controller_id = MSM_DP_CONTROLLER_1, /* pair with intf_4 for DP MST */
.prog_fetch_lines_worst_case = 24,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 12),
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v3 38/38] drm/msm/dp: Add MST stream support for SA8775P DP controller 0 and 1
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
` (36 preceding siblings ...)
2025-08-25 14:16 ` [PATCH v3 37/38] drm/msm/dp: fix the intf_type of MST interfaces Yongxing Mou
@ 2025-08-25 14:16 ` Yongxing Mou
2025-08-27 1:19 ` Dmitry Baryshkov
37 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-08-25 14:16 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
This change enables SA8775P support for Multi-Stream Transport (MST),
allowing each controller to handle up to two or four DisplayPort streams.
As all necessary code for MST support was already implemented in the
previous series of patches.
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 897ef653b3cea08904bb3595e8ac10fd7fcf811f..4a94f37513c21f9a273080b572a1e50a186a45ce 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -108,8 +108,10 @@ struct msm_dp_desc {
};
static const struct msm_dp_desc msm_dp_desc_sa8775p[] = {
- { .io_start = 0x0af54000, .id = MSM_DP_CONTROLLER_0, .wide_bus_supported = true },
- { .io_start = 0x0af5c000, .id = MSM_DP_CONTROLLER_1, .wide_bus_supported = true },
+ { .io_start = 0x0af54000, .id = MSM_DP_CONTROLLER_0, .wide_bus_supported = true,
+ .mst_streams = 4},
+ { .io_start = 0x0af5c000, .id = MSM_DP_CONTROLLER_1, .wide_bus_supported = true,
+ .mst_streams = 2},
{ .io_start = 0x22154000, .id = MSM_DP_CONTROLLER_2, .wide_bus_supported = true },
{ .io_start = 0x2215c000, .id = MSM_DP_CONTROLLER_3, .wide_bus_supported = true },
{}
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v3 01/38] drm/msm/dp: remove cached drm_edid from panel
2025-08-25 14:15 ` [PATCH v3 01/38] drm/msm/dp: remove cached drm_edid from panel Yongxing Mou
@ 2025-08-25 16:41 ` Dmitry Baryshkov
2025-09-02 8:42 ` Yongxing Mou
0 siblings, 1 reply; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 16:41 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel
On Mon, Aug 25, 2025 at 10:15:47PM +0800, Yongxing Mou wrote:
> The cached drm_edid in msm_dp_panel was redundant and led to unnecessary
> state management complexity. This change removes the drm_edid member from
Please see Documentation/process/submitting-patches.rst on how to write
commit messages. Please use imperative language instead of describing
the changes.
THe patch LGTM.
> the panel structure and refactors related functions to use locally read
> EDID data instead.
>
> - Replaces msm_dp_panel_read_sink_caps() with msm_dp_panel_read_link_caps()
> - Updates msm_dp_panel_handle_sink_request() to accept drm_edid as input
> - Removes msm_dp_panel_get_modes() and drm_edid caching logic
> - Cleans up unused drm_edid_free() calls
>
> This simplifies EDID handling and avoids stale data issues.
>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_display.c | 28 +++++++++++++++-------
> drivers/gpu/drm/msm/dp/dp_panel.c | 47 ++++---------------------------------
> drivers/gpu/drm/msm/dp/dp_panel.h | 9 +++----
> 3 files changed, 26 insertions(+), 58 deletions(-)
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead
2025-08-25 14:15 ` [PATCH v3 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead Yongxing Mou
@ 2025-08-25 16:50 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 16:50 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:15:48PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> dp_display caches the current display mode and then passes it onto
> the panel to be used for programming the panel params. Remove this
> two level passing and directly populate the panel's dp_display_mode
> instead.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_display.c | 66 ++++++++++++++-----------------------
> 1 file changed, 24 insertions(+), 42 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index dadf31bc37763c4f07f68b76fbbe33fb77b20850..632a1191e4e48fecd7dbda2f6ec6b8ed0aeebc93 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -60,7 +60,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 */
> @@ -649,16 +648,29 @@ 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(&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 */
>
> - 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);
> + 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);
> return 0;
> }
>
> @@ -1328,7 +1340,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;
> @@ -1389,10 +1401,6 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
> 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");
> - return;
> - }
>
> if (dp->is_edp)
> msm_dp_hpd_plug_handle(msm_dp_display, 0);
> @@ -1405,12 +1413,6 @@ 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);
Why is it being moved from atomic_enable? You can access crtc_state here
and thus the adjusted_mode. The mode_set is clearly declared as
deprecated. We should move moving code from that function rather than
adding code to it.
> - if (rc) {
> - DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc);
> - return;
> - }
> -
> if (dp->link_ready && !dp->power_on) {
> msm_dp_display_host_phy_init(msm_dp_display);
> force_link_train = true;
> @@ -1479,31 +1481,11 @@ void msm_dp_bridge_mode_set(struct drm_bridge *drm_bridge,
> 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;
> + msm_dp_display_set_mode(dp, adjusted_mode, msm_dp_panel);
>
> /* 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;
> + msm_dp_display->ctrl->wide_bus_en = msm_dp_panel->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)
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 03/38] drm/msm/dp: break up dp_display_enable into two parts
2025-08-25 14:15 ` [PATCH v3 03/38] drm/msm/dp: break up dp_display_enable into two parts Yongxing Mou
@ 2025-08-25 17:13 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 17:13 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:15:49PM +0800, Yongxing Mou wrote:
> 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>
> ---
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 51 ++++++++++------
> drivers/gpu/drm/msm/dp/dp_ctrl.h | 3 +-
> drivers/gpu/drm/msm/dp/dp_display.c | 113 ++++++++++++++++++++++++------------
> drivers/gpu/drm/msm/dp/dp_display.h | 1 +
> 4 files changed, 113 insertions(+), 55 deletions(-)
>
> void msm_dp_bridge_mode_set(struct drm_bridge *drm_bridge,
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
> index 60094061c1029bc7a06ffaa80d9403b40aa07eb1..2fbf16f27842bb7639efaa2baecac7bdf8908432 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.h
> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
> @@ -20,6 +20,7 @@ struct msm_dp {
> bool link_ready;
> bool audio_enabled;
> bool power_on;
> + bool prepared;
Why do you need this? The msm_dp_display_prepare() function can only be
called from within the atomic_enable() callback, which is not supposed
to fail and which is always called in pairs with the atomic_disable()
callback.
> unsigned int connector_type;
> bool is_edp;
> bool internal_hpd;
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 04/38] drm/msm/dp: re-arrange dp_display_disable() into functional parts
2025-08-25 14:15 ` [PATCH v3 04/38] drm/msm/dp: re-arrange dp_display_disable() into functional parts Yongxing Mou
@ 2025-08-25 17:25 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 17:25 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:15:50PM +0800, Yongxing Mou wrote:
> 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.
I'm mostly sure that this needs to be reworked by the HPD patchset (or
as a part of this patchset). The DP controller and the AUX channel needs
to be on for as long as there is a DPRX connected to the DP port. The
atomic_enable() / atomic_disable() callbacks should only be handling the
data lanes.
Nevertheless the patch on its own is correct.
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 19 +------------------
> drivers/gpu/drm/msm/dp/dp_ctrl.h | 3 ++-
> drivers/gpu/drm/msm/dp/dp_display.c | 10 +++++++++-
> 3 files changed, 12 insertions(+), 20 deletions(-)
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 05/38] drm/msm/dp: splite msm_dp_ctrl_config_ctrl() into link parts and stream parts
2025-08-25 14:15 ` [PATCH v3 05/38] drm/msm/dp: splite msm_dp_ctrl_config_ctrl() into link parts and stream parts Yongxing Mou
@ 2025-08-25 17:28 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 17:28 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel
On Mon, Aug 25, 2025 at 10:15:51PM +0800, Yongxing Mou wrote:
> The DP_CONFIGURATION_CTRL register contains both link-level and
> stream-specific fields. Currently, msm_dp_ctrl_config_ctrl() configures
> all of them together, which makes it harder to support MST.
>
> This patch separates the configuration into two functions:
git grep "This patch" Documentation/process
> - msm_dp_ctrl_config_ctrl_link(): handles link-related fields
> - msm_dp_ctrl_config_ctrl_streams(): handles stream-specific fields
>
> It also moves the link-related configuration out of
> msm_dp_ctrl_configure_source_params().
Why? And it looks like a separate patch...
>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 49 +++++++++++++++++++++++++---------------
> 1 file changed, 31 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index e1ff4c6bb4f0eed2e1ff931f12ba891cf81feffb..45d6c9a7f7ddaa049443253cbf4c6fc5feda3177 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> @@ -380,26 +380,41 @@ 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;
> +
> + 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);
> @@ -413,10 +428,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);
> }
> @@ -439,10 +451,7 @@ static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl
> {
> 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(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);
> @@ -1614,7 +1623,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;
> @@ -2524,6 +2533,10 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl)
> */
> 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.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 06/38] drm/msm/dp: extract MISC1_MISC0 configuration into a separate function
2025-08-25 14:15 ` [PATCH v3 06/38] drm/msm/dp: extract MISC1_MISC0 configuration into a separate function Yongxing Mou
@ 2025-08-25 17:30 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 17:30 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel
On Mon, Aug 25, 2025 at 10:15:52PM +0800, Yongxing Mou wrote:
> The MISC1_MISC0 register contains stream-specific configuration bits.
> To improve code clarity and modularity, this patch refactors the related
> code into a new helper function: msm_dp_ctrl_config_misc1_misc0().
>
> This separation also prepares the codebase for future MST stream
> programming support.
>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 15 +++++++++++----
> 1 file changed, 11 insertions(+), 4 deletions(-)
>
With the commit message fixed (see previous comments, I won't repeat
that again and again):
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 07/38] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it
2025-08-25 14:15 ` [PATCH v3 07/38] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it Yongxing Mou
@ 2025-08-25 17:32 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 17:32 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:15:53PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> Currently, the dp_ctrl stream APIs operate on their own dp_panel
> which is cached inside the dp_ctrl's private struct. However with MST,
> the cached panel represents the fixed link and not the sinks which
> are hotplugged. Allow the stream related APIs to work on the panel
> which is passed to them rather than the cached one. For SST cases,
> this shall continue to use the cached dp_panel.
cached -> stored
I think that for MST case we should not be using the msm_dp_panel for
the link handling at all. Does that make sense or would it require too
big refactoring?
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 21 +++++++++++----------
> drivers/gpu/drm/msm/dp/dp_ctrl.h | 2 +-
> drivers/gpu/drm/msm/dp/dp_display.c | 2 +-
> 3 files changed, 13 insertions(+), 12 deletions(-)
>
The patch LGTM.
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 08/38] drm/msm/dp: move the pixel clock control to its own API
2025-08-25 14:15 ` [PATCH v3 08/38] drm/msm/dp: move the pixel clock control to its own API Yongxing Mou
@ 2025-08-25 17:34 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 17:34 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:15:54PM +0800, Yongxing Mou wrote:
> 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>
> ---
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 77 +++++++++++++++++++++-------------------
> 1 file changed, 41 insertions(+), 36 deletions(-)
>
> @@ -2518,21 +2539,10 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
>
> drm_dbg_dp(ctrl->drm_dev, "pixel_rate=%lu\n", pixel_rate);
>
> - ret = clk_set_rate(ctrl->pixel_clk, pixel_rate * 1000);
> + ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate);
> if (ret) {
Nit: unused curly brackets.
Other than that:
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> - 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;
> + DRM_ERROR("failed to enable pixel clk\n");
> + return ret;
> }
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 09/38] drm/msm/dp: split dp_ctrl_off() into stream and link parts
2025-08-25 14:15 ` [PATCH v3 09/38] drm/msm/dp: split dp_ctrl_off() into stream and link parts Yongxing Mou
@ 2025-08-25 17:35 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 17:35 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:15:55PM +0800, Yongxing Mou wrote:
> 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>
> ---
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 8 ++++----
> drivers/gpu/drm/msm/dp/dp_ctrl.h | 3 ++-
> drivers/gpu/drm/msm/dp/dp_display.c | 6 ++++--
> 3 files changed, 10 insertions(+), 7 deletions(-)
>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 11/38] drm/msm/dp: separate dp_display_prepare() into its own API
2025-08-25 14:15 ` [PATCH v3 11/38] drm/msm/dp: separate dp_display_prepare() into its own API Yongxing Mou
@ 2025-08-25 17:39 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 17:39 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:15:57PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> dp_display_prepare() only prepares the link in case its not
> already ready before dp_display_enable(). Hence separate it into
> its own API.
Why?
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_display.c | 16 ++++++++++++----
> drivers/gpu/drm/msm/dp/dp_display.h | 1 +
> drivers/gpu/drm/msm/dp/dp_drm.c | 2 ++
> 3 files changed, 15 insertions(+), 4 deletions(-)
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 12/38] drm/msm/dp: introduce max_streams for DP controller MST support
2025-08-25 14:15 ` [PATCH v3 12/38] drm/msm/dp: introduce max_streams for DP controller MST support Yongxing Mou
@ 2025-08-25 17:42 ` Dmitry Baryshkov
2025-09-02 9:41 ` Dmitry Baryshkov
1 sibling, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 17:42 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:15:58PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> Introduce the `mst_streams` field in each DP controller descriptor to
> specify the number of supported MST streams. Most platforms support 2 or
> 4 MST streams, while platforms without MST support default to a single
> stream (`DEFAULT_STREAM_COUNT = 1`).
>
> This change also accounts for platforms with asymmetric stream support,
> e.g., DP0 supporting 4 streams and DP1 supporting 2.
How?
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_display.c | 21 +++++++++++++++++++++
> drivers/gpu/drm/msm/dp/dp_display.h | 1 +
> 2 files changed, 22 insertions(+)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 78d932bceb581ee54116926506b1025bd159108f..a8477a0a180137f15cbb1401c3964636aa32626c 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -33,6 +33,7 @@ module_param(psr_enabled, bool, 0);
> MODULE_PARM_DESC(psr_enabled, "enable PSR for eDP and DP displays");
>
> #define HPD_STRING_SIZE 30
> +#define DEFAULT_STREAM_COUNT 1
>
> enum {
> ISR_DISCONNECTED,
> @@ -52,6 +53,7 @@ struct msm_dp_display_private {
> bool core_initialized;
> bool phy_initialized;
> bool audio_supported;
> + bool mst_supported;
Why do we need the separate boot field here? Isn't it enough to check the max_stream?
>
> struct drm_device *drm_dev;
>
> @@ -84,12 +86,15 @@ struct msm_dp_display_private {
>
> void __iomem *p0_base;
> size_t p0_len;
> +
> + int max_stream;
> };
>
> struct msm_dp_desc {
> phys_addr_t io_start;
> unsigned int id;
> bool wide_bus_supported;
> + int mst_streams;
> };
>
> static const struct msm_dp_desc msm_dp_desc_sa8775p[] = {
> @@ -1213,6 +1218,15 @@ static int msm_dp_display_get_io(struct msm_dp_display_private *display)
> return 0;
> }
>
> +int msm_dp_get_mst_max_stream(struct msm_dp *msm_dp_display)
> +{
> + struct msm_dp_display_private *dp;
> +
> + dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
> +
> + return dp->max_stream;
> +}
> +
> static int msm_dp_display_probe(struct platform_device *pdev)
> {
> int rc = 0;
> @@ -1239,6 +1253,13 @@ static int msm_dp_display_probe(struct platform_device *pdev)
> dp->msm_dp_display.is_edp =
> (dp->msm_dp_display.connector_type == DRM_MODE_CONNECTOR_eDP);
> dp->hpd_isr_status = 0;
> + dp->max_stream = DEFAULT_STREAM_COUNT;
Just use 1 it's not that magic to define it.
> + dp->mst_supported = FALSE;
> +
> + if (desc->mst_streams > DEFAULT_STREAM_COUNT) {
> + dp->max_stream = desc->mst_streams;
> + dp->mst_supported = TRUE;
> + }
>
> rc = msm_dp_display_get_io(dp);
> if (rc)
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
> index 37c6e87db90ce951274cdae61f26d76dc9ef3840..7727cf325a89b4892d2370a5616c4fa76fc88485 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.h
> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
> @@ -29,6 +29,7 @@ struct msm_dp {
> bool psr_supported;
> };
>
> +int msm_dp_get_mst_max_stream(struct msm_dp *msm_dp_display);
> int msm_dp_display_get_modes(struct msm_dp *msm_dp_display);
> bool msm_dp_display_check_video_test(struct msm_dp *msm_dp_display);
> int msm_dp_display_get_test_bpp(struct msm_dp *msm_dp_display);
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 13/38] drm/msm/dp: introduce stream_id for each DP panel
2025-08-25 14:15 ` [PATCH v3 13/38] drm/msm/dp: introduce stream_id for each DP panel Yongxing Mou
@ 2025-08-25 17:56 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 17:56 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:15:59PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> With MST, each DP controller can handle multiple streams.
> There shall be one dp_panel for each stream but the dp_display
> object shall be shared among them. To represent this abstraction,
> create a stream_id for each DP panel which shall be set by the
> MST stream. For SST, default this to stream 0.
>
> Use the stream ID to control the pixel clock of that respective
> stream by extending the clock handles and state tracking of the
> DP pixel clock to an array of max supported streams. The maximum
> streams currently is 4.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 58 ++++++++++++++++++++++---------------
> drivers/gpu/drm/msm/dp/dp_ctrl.h | 3 +-
> drivers/gpu/drm/msm/dp/dp_display.c | 27 +++++++++++++++--
> drivers/gpu/drm/msm/dp/dp_display.h | 2 ++
> drivers/gpu/drm/msm/dp/dp_panel.h | 11 +++++++
> 5 files changed, 73 insertions(+), 28 deletions(-)
> @@ -2677,10 +2675,11 @@ static const char *ctrl_clks[] = {
> "ctrl_link_iface",
> };
>
> -static int msm_dp_ctrl_clk_init(struct msm_dp_ctrl *msm_dp_ctrl)
> +static int msm_dp_ctrl_clk_init(struct msm_dp_ctrl *msm_dp_ctrl, int max_stream)
> {
> struct msm_dp_ctrl_private *ctrl;
> struct device *dev;
> + char stream_id_str[15];
A comment would be nice. Or better replace this with the array lookup,
it's much easier than snprintf.
> int i, rc;
>
> ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
> @@ -2710,9 +2709,19 @@ static int msm_dp_ctrl_clk_init(struct msm_dp_ctrl *msm_dp_ctrl)
> if (rc)
> return rc;
>
> - ctrl->pixel_clk = devm_clk_get(dev, "stream_pixel");
> - if (IS_ERR(ctrl->pixel_clk))
> - return PTR_ERR(ctrl->pixel_clk);
> + ctrl->pixel_clk[DP_STREAM_0] = devm_clk_get(dev, "stream_pixel");
> + if (IS_ERR(ctrl->pixel_clk[DP_STREAM_0]))
> + return PTR_ERR(ctrl->pixel_clk[DP_STREAM_0]);
> +
> + for (i = DP_STREAM_1; i < max_stream; i++) {
> + sprintf(stream_id_str, "stream_%d_pixel", i);
> + ctrl->pixel_clk[i] = devm_clk_get(dev, stream_id_str);
> +
> + if (IS_ERR(ctrl->pixel_clk[i])) {
> + DRM_DEBUG_DP("failed to get stream %d pixel clock", i);
> + break;
> + }
> + }
>
> return 0;
> }
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 14/38] drm/msm/dp: Add support for programming p1/p2/p3 register blocks
2025-08-25 14:16 ` [PATCH v3 14/38] drm/msm/dp: Add support for programming p1/p2/p3 register blocks Yongxing Mou
@ 2025-08-25 17:59 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 17:59 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:00PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> QCS8300 supports 4-stream MST. This patch adds support for the additional
> pixel register blocks (p1, p2, p3), enabling multi-stream configurations.
>
> To reduce code duplication, introduce helper functions msm_dp_read_pn and
> msm_dp_write_pn. All pixel clocks (PCLKs) share the same register layout,
> but use different base addresses.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_display.c | 39 +++++++++++++--------
> drivers/gpu/drm/msm/dp/dp_panel.c | 68 ++++++++++++++++++-------------------
> 2 files changed, 59 insertions(+), 48 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 3422f18bdec71a99407edfe943d31957d0e8847a..935a0c57a928b15a1e9a6f1fab2576b7b09acb8e 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -84,8 +84,8 @@ struct msm_dp_display_private {
> void __iomem *link_base;
> size_t link_len;
>
> - void __iomem *p0_base;
> - size_t p0_len;
> + void __iomem *pixel_base[DP_STREAM_MAX];
> + size_t pixel_len;
>
> int max_stream;
> };
> @@ -619,7 +619,7 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp)
> goto error_link;
> }
>
> - dp->panel = msm_dp_panel_get(dev, dp->aux, dp->link, dp->link_base, dp->p0_base);
> + dp->panel = msm_dp_panel_get(dev, dp->aux, dp->link, dp->link_base, dp->pixel_base);
Why do we need to pass pixel base here? Shouldn't it be pixel_base[P0]?
> if (IS_ERR(dp->panel)) {
> rc = PTR_ERR(dp->panel);
> DRM_ERROR("failed to initialize panel, rc = %d\n", rc);
> @@ -937,8 +937,8 @@ void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp)
> msm_dp_display->aux_base, "dp_aux");
> msm_disp_snapshot_add_block(disp_state, msm_dp_display->link_len,
> msm_dp_display->link_base, "dp_link");
> - msm_disp_snapshot_add_block(disp_state, msm_dp_display->p0_len,
> - msm_dp_display->p0_base, "dp_p0");
> + msm_disp_snapshot_add_block(disp_state, msm_dp_display->pixel_len,
> + msm_dp_display->pixel_base[0], "dp_p0");
This should add all blocks used on this platform.
> }
>
> void msm_dp_display_set_psr(struct msm_dp *msm_dp_display, bool enter)
> @@ -1181,12 +1181,13 @@ static void __iomem *msm_dp_ioremap(struct platform_device *pdev, int idx, size_
> #define DP_DEFAULT_AUX_SIZE 0x0200
> #define DP_DEFAULT_LINK_OFFSET 0x0400
> #define DP_DEFAULT_LINK_SIZE 0x0C00
> -#define DP_DEFAULT_P0_OFFSET 0x1000
> -#define DP_DEFAULT_P0_SIZE 0x0400
> +#define DP_DEFAULT_PIXEL_OFFSET 0x1000
> +#define DP_DEFAULT_PIXEL_SIZE 0x0400
No need to touch this. It's only required for legacy bindings.
>
> static int msm_dp_display_get_io(struct msm_dp_display_private *display)
> {
> struct platform_device *pdev = display->msm_dp_display.pdev;
> + int i;
>
> display->ahb_base = msm_dp_ioremap(pdev, 0, &display->ahb_len);
> if (IS_ERR(display->ahb_base))
> @@ -1206,7 +1207,7 @@ static int msm_dp_display_get_io(struct msm_dp_display_private *display)
> * reg is specified, so fill in the sub-region offsets and
> * lengths based on this single region.
> */
> - if (display->ahb_len < DP_DEFAULT_P0_OFFSET + DP_DEFAULT_P0_SIZE) {
> + if (display->ahb_len < DP_DEFAULT_PIXEL_OFFSET + DP_DEFAULT_PIXEL_SIZE) {
> DRM_ERROR("legacy memory region not large enough\n");
> return -EINVAL;
> }
> @@ -1216,8 +1217,10 @@ static int msm_dp_display_get_io(struct msm_dp_display_private *display)
> display->aux_len = DP_DEFAULT_AUX_SIZE;
> display->link_base = display->ahb_base + DP_DEFAULT_LINK_OFFSET;
> display->link_len = DP_DEFAULT_LINK_SIZE;
> - display->p0_base = display->ahb_base + DP_DEFAULT_P0_OFFSET;
> - display->p0_len = DP_DEFAULT_P0_SIZE;
> + for (i = DP_STREAM_0; i < display->max_stream; i++)
> + display->pixel_base[i] = display->ahb_base +
> + (i+1) * DP_DEFAULT_PIXEL_OFFSET;
> + display->pixel_len = DP_DEFAULT_PIXEL_SIZE;
>
> return 0;
> }
> @@ -1228,10 +1231,18 @@ static int msm_dp_display_get_io(struct msm_dp_display_private *display)
> return PTR_ERR(display->link_base);
> }
>
> - display->p0_base = msm_dp_ioremap(pdev, 3, &display->p0_len);
> - if (IS_ERR(display->p0_base)) {
> - DRM_ERROR("unable to remap p0 region: %pe\n", display->p0_base);
> - return PTR_ERR(display->p0_base);
> + display->pixel_base[0] = msm_dp_ioremap(pdev, 3, &display->pixel_len);
> + if (IS_ERR(display->pixel_base[0])) {
> + DRM_ERROR("unable to remap p0 region: %pe\n", display->pixel_base[0]);
> + return PTR_ERR(display->pixel_base[0]);
> + }
> +
> + for (i = DP_STREAM_1; i < display->max_stream; i++) {
> + /* pixels clk reg index start from 3*/
> + display->pixel_base[i] = msm_dp_ioremap(pdev, i + 3, &display->pixel_len);
> + if (IS_ERR(display->pixel_base[i]))
> + DRM_DEBUG_DP("unable to remap p%d region: %pe\n", i,
> + display->pixel_base[i]);
> }
>
> return 0;
> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
> index eae125972934bb2fb3b716dc47ae71cd0421bd1a..e8c1cf0c7dab7217b8bfe7ecd586af33d7547ca9 100644
> --- a/drivers/gpu/drm/msm/dp/dp_panel.c
> +++ b/drivers/gpu/drm/msm/dp/dp_panel.c
> @@ -26,7 +26,7 @@ struct msm_dp_panel_private {
> struct drm_dp_aux *aux;
> struct msm_dp_link *link;
> void __iomem *link_base;
> - void __iomem *p0_base;
> + void __iomem *pixel_base[DP_STREAM_MAX];
> bool panel_on;
> };
>
> @@ -45,24 +45,24 @@ static inline void msm_dp_write_link(struct msm_dp_panel_private *panel,
> writel(data, panel->link_base + offset);
> }
>
> -static inline void msm_dp_write_p0(struct msm_dp_panel_private *panel,
> +static inline void msm_dp_write_pn(struct msm_dp_panel_private *panel,
> u32 offset, u32 data)
Is it really multiplexed on the panel level? I'd assume that each panel
is connected to only one stream instance... If that's not the case, such
details must be explained in the commit message.
> {
> /*
> * To make sure interface reg writes happens before any other operation,
> * this function uses writel() instread of writel_relaxed()
> */
> - writel(data, panel->p0_base + offset);
> + writel(data, panel->pixel_base[panel->msm_dp_panel.stream_id] + offset);
> }
>
> -static inline u32 msm_dp_read_p0(struct msm_dp_panel_private *panel,
> +static inline u32 msm_dp_read_pn(struct msm_dp_panel_private *panel,
> u32 offset)
> {
> /*
> * To make sure interface reg writes happens before any other operation,
> * this function uses writel() instread of writel_relaxed()
> */
> - return readl_relaxed(panel->p0_base + offset);
> + return readl_relaxed(panel->pixel_base[panel->msm_dp_panel.stream_id] + offset);
> }
>
> static void msm_dp_panel_read_psr_cap(struct msm_dp_panel_private *panel)
> @@ -297,33 +297,33 @@ static void msm_dp_panel_tpg_enable(struct msm_dp_panel *msm_dp_panel,
> display_hctl = (hsync_end_x << 16) | hsync_start_x;
>
>
> - msm_dp_write_p0(panel, MMSS_DP_INTF_HSYNC_CTL, hsync_ctl);
> - msm_dp_write_p0(panel, MMSS_DP_INTF_VSYNC_PERIOD_F0, vsync_period *
> + msm_dp_write_pn(panel, MMSS_DP_INTF_HSYNC_CTL, hsync_ctl);
> + msm_dp_write_pn(panel, MMSS_DP_INTF_VSYNC_PERIOD_F0, vsync_period *
> hsync_period);
> - msm_dp_write_p0(panel, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0, v_sync_width *
> + msm_dp_write_pn(panel, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0, v_sync_width *
> hsync_period);
> - msm_dp_write_p0(panel, MMSS_DP_INTF_VSYNC_PERIOD_F1, 0);
> - msm_dp_write_p0(panel, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1, 0);
> - msm_dp_write_p0(panel, MMSS_DP_INTF_DISPLAY_HCTL, display_hctl);
> - msm_dp_write_p0(panel, MMSS_DP_INTF_ACTIVE_HCTL, 0);
> - msm_dp_write_p0(panel, MMSS_INTF_DISPLAY_V_START_F0, display_v_start);
> - msm_dp_write_p0(panel, MMSS_DP_INTF_DISPLAY_V_END_F0, display_v_end);
> - msm_dp_write_p0(panel, MMSS_INTF_DISPLAY_V_START_F1, 0);
> - msm_dp_write_p0(panel, MMSS_DP_INTF_DISPLAY_V_END_F1, 0);
> - msm_dp_write_p0(panel, MMSS_DP_INTF_ACTIVE_V_START_F0, 0);
> - msm_dp_write_p0(panel, MMSS_DP_INTF_ACTIVE_V_END_F0, 0);
> - msm_dp_write_p0(panel, MMSS_DP_INTF_ACTIVE_V_START_F1, 0);
> - msm_dp_write_p0(panel, MMSS_DP_INTF_ACTIVE_V_END_F1, 0);
> - msm_dp_write_p0(panel, MMSS_DP_INTF_POLARITY_CTL, 0);
> -
> - msm_dp_write_p0(panel, MMSS_DP_TPG_MAIN_CONTROL,
> + msm_dp_write_pn(panel, MMSS_DP_INTF_VSYNC_PERIOD_F1, 0);
> + msm_dp_write_pn(panel, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1, 0);
> + msm_dp_write_pn(panel, MMSS_DP_INTF_DISPLAY_HCTL, display_hctl);
> + msm_dp_write_pn(panel, MMSS_DP_INTF_ACTIVE_HCTL, 0);
> + msm_dp_write_pn(panel, MMSS_INTF_DISPLAY_V_START_F0, display_v_start);
> + msm_dp_write_pn(panel, MMSS_DP_INTF_DISPLAY_V_END_F0, display_v_end);
> + msm_dp_write_pn(panel, MMSS_INTF_DISPLAY_V_START_F1, 0);
> + msm_dp_write_pn(panel, MMSS_DP_INTF_DISPLAY_V_END_F1, 0);
> + msm_dp_write_pn(panel, MMSS_DP_INTF_ACTIVE_V_START_F0, 0);
> + msm_dp_write_pn(panel, MMSS_DP_INTF_ACTIVE_V_END_F0, 0);
> + msm_dp_write_pn(panel, MMSS_DP_INTF_ACTIVE_V_START_F1, 0);
> + msm_dp_write_pn(panel, MMSS_DP_INTF_ACTIVE_V_END_F1, 0);
> + msm_dp_write_pn(panel, MMSS_DP_INTF_POLARITY_CTL, 0);
> +
> + msm_dp_write_pn(panel, MMSS_DP_TPG_MAIN_CONTROL,
> DP_TPG_CHECKERED_RECT_PATTERN);
> - msm_dp_write_p0(panel, MMSS_DP_TPG_VIDEO_CONFIG,
> + msm_dp_write_pn(panel, MMSS_DP_TPG_VIDEO_CONFIG,
> DP_TPG_VIDEO_CONFIG_BPP_8BIT |
> DP_TPG_VIDEO_CONFIG_RGB);
> - msm_dp_write_p0(panel, MMSS_DP_BIST_ENABLE,
> + msm_dp_write_pn(panel, MMSS_DP_BIST_ENABLE,
> DP_BIST_ENABLE_DPBIST_EN);
> - msm_dp_write_p0(panel, MMSS_DP_TIMING_ENGINE_EN,
> + msm_dp_write_pn(panel, MMSS_DP_TIMING_ENGINE_EN,
> DP_TIMING_ENGINE_EN_EN);
> drm_dbg_dp(panel->drm_dev, "%s: enabled tpg\n", __func__);
> }
> @@ -333,9 +333,9 @@ static void msm_dp_panel_tpg_disable(struct msm_dp_panel *msm_dp_panel)
> struct msm_dp_panel_private *panel =
> container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
>
> - msm_dp_write_p0(panel, MMSS_DP_TPG_MAIN_CONTROL, 0x0);
> - msm_dp_write_p0(panel, MMSS_DP_BIST_ENABLE, 0x0);
> - msm_dp_write_p0(panel, MMSS_DP_TIMING_ENGINE_EN, 0x0);
> + msm_dp_write_pn(panel, MMSS_DP_TPG_MAIN_CONTROL, 0x0);
> + msm_dp_write_pn(panel, MMSS_DP_BIST_ENABLE, 0x0);
> + msm_dp_write_pn(panel, MMSS_DP_TIMING_ENGINE_EN, 0x0);
> }
>
> void msm_dp_panel_tpg_config(struct msm_dp_panel *msm_dp_panel, bool enable)
> @@ -369,7 +369,7 @@ void msm_dp_panel_clear_dsc_dto(struct msm_dp_panel *msm_dp_panel)
> struct msm_dp_panel_private *panel =
> container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
>
> - msm_dp_write_p0(panel, MMSS_DP_DSC_DTO, 0x0);
> + msm_dp_write_pn(panel, MMSS_DP_DSC_DTO, 0x0);
> }
>
> static void msm_dp_panel_send_vsc_sdp(struct msm_dp_panel_private *panel, struct dp_sdp *vsc_sdp)
> @@ -559,7 +559,7 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en)
> msm_dp_write_link(panel, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY, width_blanking);
> msm_dp_write_link(panel, REG_DP_ACTIVE_HOR_VER, msm_dp_active);
>
> - reg = msm_dp_read_p0(panel, MMSS_DP_INTF_CONFIG);
> + reg = msm_dp_read_pn(panel, MMSS_DP_INTF_CONFIG);
> if (wide_bus_en)
> reg |= DP_INTF_CONFIG_DATABUS_WIDEN;
> else
> @@ -567,7 +567,7 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en)
>
> drm_dbg_dp(panel->drm_dev, "wide_bus_en=%d reg=%#x\n", wide_bus_en, reg);
>
> - msm_dp_write_p0(panel, MMSS_DP_INTF_CONFIG, reg);
> + msm_dp_write_pn(panel, MMSS_DP_INTF_CONFIG, reg);
>
> if (msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
> msm_dp_panel_setup_vsc_sdp_yuv_420(msm_dp_panel);
> @@ -673,7 +673,7 @@ static int msm_dp_panel_parse_dt(struct msm_dp_panel *msm_dp_panel)
> 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 __iomem *pixel_base[])
> {
> struct msm_dp_panel_private *panel;
> struct msm_dp_panel *msm_dp_panel;
> @@ -692,7 +692,7 @@ struct msm_dp_panel *msm_dp_panel_get(struct device *dev, struct drm_dp_aux *aux
> panel->aux = aux;
> panel->link = link;
> panel->link_base = link_base;
> - panel->p0_base = p0_base;
> + memcpy(panel->pixel_base, pixel_base, sizeof(panel->pixel_base));
>
> msm_dp_panel = &panel->msm_dp_panel;
> msm_dp_panel->max_bw_code = DP_LINK_BW_8_1;
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 15/38] drm/msm/dp: use stream_id to change offsets in dp_catalog
2025-08-25 14:16 ` [PATCH v3 15/38] drm/msm/dp: use stream_id to change offsets in dp_catalog Yongxing Mou
@ 2025-08-25 18:01 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 18:01 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:01PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> Use the dp_panel's stream_id to adjust the offsets for stream 1
> which will be used for MST in the dp_catalog. Stream 1 share the
> same link clk with stream 0 with different reg offset. Also add
Shares what? How do we handle streams 2 and 3?
> additional register defines for stream 1.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 24 ++++++++++---
> drivers/gpu/drm/msm/dp/dp_panel.c | 72 +++++++++++++++++++++++++++------------
> drivers/gpu/drm/msm/dp/dp_reg.h | 9 +++++
> 3 files changed, 79 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index d4a74c6b70fb182ad8a0a786f85a0f50982d3858..b8b6a09966aed96f705bdd54cb16ea63e5f0141f 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> @@ -384,6 +384,7 @@ 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;
> + u32 reg_offset = 0;
>
> config = msm_dp_read_link(ctrl, REG_DP_CONFIGURATION_CTRL);
>
> @@ -400,7 +401,8 @@ static void msm_dp_ctrl_config_ctrl_streams(struct msm_dp_ctrl_private *ctrl,
>
> drm_dbg_dp(ctrl->drm_dev, "stream DP_CONFIGURATION_CTRL=0x%x\n", config);
>
> - msm_dp_write_link(ctrl, REG_DP_CONFIGURATION_CTRL, config);
> + if (msm_dp_panel->stream_id == DP_STREAM_1)
> + reg_offset = REG_DP1_CONFIGURATION_CTRL - REG_DP_CONFIGURATION_CTRL;
> }
>
> static void msm_dp_ctrl_config_ctrl_link(struct msm_dp_ctrl_private *ctrl)
> @@ -451,12 +453,16 @@ 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;
> + u32 reg_offset = 0;
>
> 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);
> + if (msm_dp_panel->stream_id == DP_STREAM_1)
> + reg_offset = REG_DP1_MISC1_MISC0 - REG_DP_MISC1_MISC0;
> +
> + misc_val = msm_dp_read_link(ctrl, REG_DP_MISC1_MISC0 + reg_offset);
>
> /* clear bpp bits */
> misc_val &= ~(0x07 << DP_MISC0_TEST_BITS_DEPTH_SHIFT);
> @@ -466,7 +472,7 @@ static void msm_dp_ctrl_config_misc1_misc0(struct msm_dp_ctrl_private *ctrl,
> misc_val |= DP_MISC0_SYNCHRONOUS_CLK;
>
> drm_dbg_dp(ctrl->drm_dev, "misc settings = 0x%x\n", misc_val);
> - msm_dp_write_link(ctrl, REG_DP_MISC1_MISC0, misc_val);
> + msm_dp_write_link(ctrl, REG_DP_MISC1_MISC0 + reg_offset, misc_val);
> }
>
> static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl,
> @@ -2431,6 +2437,7 @@ static int msm_dp_ctrl_link_retrain(struct msm_dp_ctrl_private *ctrl)
> }
>
> static void msm_dp_ctrl_config_msa(struct msm_dp_ctrl_private *ctrl,
> + struct msm_dp_panel *msm_dp_panel,
> u32 rate, u32 stream_rate_khz,
> bool is_ycbcr_420)
> {
> @@ -2440,6 +2447,12 @@ static void msm_dp_ctrl_config_msa(struct msm_dp_ctrl_private *ctrl,
> u32 const link_rate_hbr2 = 540000;
> u32 const link_rate_hbr3 = 810000;
> unsigned long den, num;
> + u32 mvid_reg_off = 0, nvid_reg_off = 0;
> +
> + if (msm_dp_panel->stream_id == DP_STREAM_1) {
> + mvid_reg_off = REG_DP1_SOFTWARE_MVID - REG_DP_SOFTWARE_MVID;
> + nvid_reg_off = REG_DP1_SOFTWARE_NVID - REG_DP_SOFTWARE_NVID;
> + }
>
> if (rate == link_rate_hbr3)
> pixel_div = 6;
> @@ -2482,8 +2495,8 @@ static void msm_dp_ctrl_config_msa(struct msm_dp_ctrl_private *ctrl,
> nvid *= 3;
>
> drm_dbg_dp(ctrl->drm_dev, "mvid=0x%x, nvid=0x%x\n", mvid, nvid);
> - msm_dp_write_link(ctrl, REG_DP_SOFTWARE_MVID, mvid);
> - msm_dp_write_link(ctrl, REG_DP_SOFTWARE_NVID, nvid);
> + msm_dp_write_link(ctrl, REG_DP_SOFTWARE_MVID + mvid_reg_off, mvid);
> + msm_dp_write_link(ctrl, REG_DP_SOFTWARE_NVID + nvid_reg_off, nvid);
> }
>
> int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train)
> @@ -2559,6 +2572,7 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
> msm_dp_ctrl_configure_source_params(ctrl, msm_dp_panel);
>
> msm_dp_ctrl_config_msa(ctrl,
> + msm_dp_panel,
> ctrl->link->link_params.rate,
> pixel_rate_orig,
> msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420);
> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
> index e8c1cf0c7dab7217b8bfe7ecd586af33d7547ca9..d1af389dffcfee2d21a616de6ee027374997aaee 100644
> --- a/drivers/gpu/drm/msm/dp/dp_panel.c
> +++ b/drivers/gpu/drm/msm/dp/dp_panel.c
> @@ -377,27 +377,35 @@ static void msm_dp_panel_send_vsc_sdp(struct msm_dp_panel_private *panel, struct
> u32 header[2];
> u32 val;
> int i;
> + u32 offset = 0;
> +
> + if (panel->msm_dp_panel.stream_id == DP_STREAM_1)
> + offset = MMSS_DP1_GENERIC0_0 - MMSS_DP_GENERIC0_0;
>
> msm_dp_utils_pack_sdp_header(&vsc_sdp->sdp_header, header);
>
> - msm_dp_write_link(panel, MMSS_DP_GENERIC0_0, header[0]);
> - msm_dp_write_link(panel, MMSS_DP_GENERIC0_1, header[1]);
> + msm_dp_write_link(panel, MMSS_DP_GENERIC0_0 + offset, header[0]);
> + msm_dp_write_link(panel, MMSS_DP_GENERIC0_1 + offset, header[1]);
>
> for (i = 0; i < sizeof(vsc_sdp->db); i += 4) {
> val = ((vsc_sdp->db[i]) | (vsc_sdp->db[i + 1] << 8) | (vsc_sdp->db[i + 2] << 16) |
> (vsc_sdp->db[i + 3] << 24));
> - msm_dp_write_link(panel, MMSS_DP_GENERIC0_2 + i, val);
> + msm_dp_write_link(panel, MMSS_DP_GENERIC0_2 + i + offset, val);
> }
> }
>
> static void msm_dp_panel_update_sdp(struct msm_dp_panel_private *panel)
> {
> u32 hw_revision = panel->msm_dp_panel.hw_revision;
> + u32 offset = 0;
> +
> + if (panel->msm_dp_panel.stream_id == DP_STREAM_1)
> + offset = MMSS_DP1_SDP_CFG3 - MMSS_DP_SDP_CFG3;
>
> if (hw_revision >= DP_HW_VERSION_1_0 &&
> hw_revision < DP_HW_VERSION_1_2) {
> - msm_dp_write_link(panel, MMSS_DP_SDP_CFG3, UPDATE_SDP);
> - msm_dp_write_link(panel, MMSS_DP_SDP_CFG3, 0x0);
> + msm_dp_write_link(panel, MMSS_DP_SDP_CFG3 + offset, UPDATE_SDP);
> + msm_dp_write_link(panel, MMSS_DP_SDP_CFG3 + offset, 0x0);
> }
> }
>
> @@ -406,16 +414,25 @@ void msm_dp_panel_enable_vsc_sdp(struct msm_dp_panel *msm_dp_panel, struct dp_sd
> struct msm_dp_panel_private *panel =
> container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
> u32 cfg, cfg2, misc;
> + u32 misc_reg_offset = 0;
> + u32 sdp_cfg_offset = 0;
> + u32 sdp_cfg2_offset = 0;
> +
> + if (msm_dp_panel->stream_id == DP_STREAM_1) {
> + misc_reg_offset = REG_DP1_MISC1_MISC0 - REG_DP_MISC1_MISC0;
> + sdp_cfg_offset = MMSS_DP1_SDP_CFG - MMSS_DP_SDP_CFG;
> + sdp_cfg2_offset = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
> + }
>
> - cfg = msm_dp_read_link(panel, MMSS_DP_SDP_CFG);
> - cfg2 = msm_dp_read_link(panel, MMSS_DP_SDP_CFG2);
> - misc = msm_dp_read_link(panel, REG_DP_MISC1_MISC0);
> + cfg = msm_dp_read_link(panel, MMSS_DP_SDP_CFG + sdp_cfg_offset);
> + cfg2 = msm_dp_read_link(panel, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset);
> + misc = msm_dp_read_link(panel, REG_DP_MISC1_MISC0 + misc_reg_offset);
>
> cfg |= GEN0_SDP_EN;
> - msm_dp_write_link(panel, MMSS_DP_SDP_CFG, cfg);
> + msm_dp_write_link(panel, MMSS_DP_SDP_CFG + sdp_cfg_offset, cfg);
>
> cfg2 |= GENERIC0_SDPSIZE_VALID;
> - msm_dp_write_link(panel, MMSS_DP_SDP_CFG2, cfg2);
> + msm_dp_write_link(panel, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset, cfg2);
>
> msm_dp_panel_send_vsc_sdp(panel, vsc_sdp);
>
> @@ -425,7 +442,7 @@ void msm_dp_panel_enable_vsc_sdp(struct msm_dp_panel *msm_dp_panel, struct dp_sd
> drm_dbg_dp(panel->drm_dev, "vsc sdp enable=1\n");
>
> pr_debug("misc settings = 0x%x\n", misc);
> - msm_dp_write_link(panel, REG_DP_MISC1_MISC0, misc);
> + msm_dp_write_link(panel, REG_DP_MISC1_MISC0 + misc_reg_offset, misc);
>
> msm_dp_panel_update_sdp(panel);
> }
> @@ -435,16 +452,25 @@ void msm_dp_panel_disable_vsc_sdp(struct msm_dp_panel *msm_dp_panel)
> struct msm_dp_panel_private *panel =
> container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
> u32 cfg, cfg2, misc;
> + u32 misc_reg_offset = 0;
> + u32 sdp_cfg_offset = 0;
> + u32 sdp_cfg2_offset = 0;
> +
> + if (msm_dp_panel->stream_id == DP_STREAM_1) {
> + misc_reg_offset = REG_DP1_MISC1_MISC0 - REG_DP_MISC1_MISC0;
> + sdp_cfg_offset = MMSS_DP1_SDP_CFG - MMSS_DP_SDP_CFG;
> + sdp_cfg2_offset = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
> + }
>
> - cfg = msm_dp_read_link(panel, MMSS_DP_SDP_CFG);
> - cfg2 = msm_dp_read_link(panel, MMSS_DP_SDP_CFG2);
> - misc = msm_dp_read_link(panel, REG_DP_MISC1_MISC0);
> + cfg = msm_dp_read_link(panel, MMSS_DP_SDP_CFG + sdp_cfg_offset);
> + cfg2 = msm_dp_read_link(panel, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset);
> + misc = msm_dp_read_link(panel, REG_DP_MISC1_MISC0 + misc_reg_offset);
>
> cfg &= ~GEN0_SDP_EN;
> - msm_dp_write_link(panel, MMSS_DP_SDP_CFG, cfg);
> + msm_dp_write_link(panel, MMSS_DP_SDP_CFG + sdp_cfg_offset, cfg);
>
> cfg2 &= ~GENERIC0_SDPSIZE_VALID;
> - msm_dp_write_link(panel, MMSS_DP_SDP_CFG2, cfg2);
> + msm_dp_write_link(panel, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset, cfg2);
>
> /* switch back to MSA */
> misc &= ~DP_MISC1_VSC_SDP;
> @@ -452,7 +478,7 @@ void msm_dp_panel_disable_vsc_sdp(struct msm_dp_panel *msm_dp_panel)
> drm_dbg_dp(panel->drm_dev, "vsc sdp enable=0\n");
>
> pr_debug("misc settings = 0x%x\n", misc);
> - msm_dp_write_link(panel, REG_DP_MISC1_MISC0, misc);
> + msm_dp_write_link(panel, REG_DP_MISC1_MISC0 + misc_reg_offset, misc);
>
> msm_dp_panel_update_sdp(panel);
> }
> @@ -510,6 +536,7 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en)
> u32 msm_dp_active;
> u32 total;
> u32 reg;
> + u32 offset = 0;
>
> panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
> drm_mode = &panel->msm_dp_panel.msm_dp_mode.drm_mode;
> @@ -524,6 +551,9 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en)
> drm_mode->vsync_start - drm_mode->vdisplay,
> drm_mode->vsync_end - drm_mode->vsync_start);
>
> + if (msm_dp_panel->stream_id == DP_STREAM_1)
> + offset = REG_DP1_TOTAL_HOR_VER - REG_DP_TOTAL_HOR_VER;
> +
> total_hor = drm_mode->htotal;
>
> total_ver = drm_mode->vtotal;
> @@ -554,10 +584,10 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en)
>
> msm_dp_active = data;
>
> - msm_dp_write_link(panel, REG_DP_TOTAL_HOR_VER, total);
> - msm_dp_write_link(panel, REG_DP_START_HOR_VER_FROM_SYNC, sync_start);
> - msm_dp_write_link(panel, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY, width_blanking);
> - msm_dp_write_link(panel, REG_DP_ACTIVE_HOR_VER, msm_dp_active);
> + msm_dp_write_link(panel, REG_DP_TOTAL_HOR_VER + offset, total);
> + msm_dp_write_link(panel, REG_DP_START_HOR_VER_FROM_SYNC + offset, sync_start);
> + msm_dp_write_link(panel, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY + offset, width_blanking);
> + msm_dp_write_link(panel, REG_DP_ACTIVE_HOR_VER + offset, msm_dp_active);
>
> reg = msm_dp_read_pn(panel, MMSS_DP_INTF_CONFIG);
> if (wide_bus_en)
> diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
> index b851efc132ea03884ce2563990fbc24c9577e724..43a9ce0539906e1f185abf250fdf161e462d9645 100644
> --- a/drivers/gpu/drm/msm/dp/dp_reg.h
> +++ b/drivers/gpu/drm/msm/dp/dp_reg.h
> @@ -141,6 +141,7 @@
> #define DP_STATE_CTRL_PUSH_IDLE (0x00000100)
>
> #define REG_DP_CONFIGURATION_CTRL (0x00000008)
> +#define REG_DP1_CONFIGURATION_CTRL (0x00000400)
> #define DP_CONFIGURATION_CTRL_SYNC_ASYNC_CLK (0x00000001)
> #define DP_CONFIGURATION_CTRL_STATIC_DYNAMIC_CN (0x00000002)
> #define DP_CONFIGURATION_CTRL_P_INTERLACED (0x00000004)
> @@ -159,11 +160,15 @@
> #define REG_DP_SOFTWARE_MVID (0x00000010)
> #define REG_DP_SOFTWARE_NVID (0x00000018)
> #define REG_DP_TOTAL_HOR_VER (0x0000001C)
> +#define REG_DP1_SOFTWARE_MVID (0x00000414)
> +#define REG_DP1_SOFTWARE_NVID (0x00000418)
> +#define REG_DP1_TOTAL_HOR_VER (0x0000041C)
> #define REG_DP_START_HOR_VER_FROM_SYNC (0x00000020)
> #define REG_DP_HSYNC_VSYNC_WIDTH_POLARITY (0x00000024)
> #define REG_DP_ACTIVE_HOR_VER (0x00000028)
>
> #define REG_DP_MISC1_MISC0 (0x0000002C)
> +#define REG_DP1_MISC1_MISC0 (0x0000042C)
> #define DP_MISC0_SYNCHRONOUS_CLK (0x00000001)
> #define DP_MISC0_COLORIMETRY_CFG_SHIFT (0x00000001)
> #define DP_MISC0_TEST_BITS_DEPTH_SHIFT (0x00000005)
> @@ -230,8 +235,10 @@
> #define MMSS_DP_AUDIO_CTRL_RESET (0x00000214)
>
> #define MMSS_DP_SDP_CFG (0x00000228)
> +#define MMSS_DP1_SDP_CFG (0x000004E0)
> #define GEN0_SDP_EN (0x00020000)
> #define MMSS_DP_SDP_CFG2 (0x0000022C)
> +#define MMSS_DP1_SDP_CFG2 (0x000004E4)
> #define MMSS_DP_AUDIO_TIMESTAMP_0 (0x00000230)
> #define MMSS_DP_AUDIO_TIMESTAMP_1 (0x00000234)
> #define GENERIC0_SDPSIZE_VALID (0x00010000)
> @@ -240,6 +247,7 @@
> #define MMSS_DP_AUDIO_STREAM_1 (0x00000244)
>
> #define MMSS_DP_SDP_CFG3 (0x0000024c)
> +#define MMSS_DP1_SDP_CFG3 (0x000004E8)
> #define UPDATE_SDP (0x00000001)
>
> #define MMSS_DP_EXTENSION_0 (0x00000250)
> @@ -288,6 +296,7 @@
> #define MMSS_DP_GENERIC1_7 (0x00000344)
> #define MMSS_DP_GENERIC1_8 (0x00000348)
> #define MMSS_DP_GENERIC1_9 (0x0000034C)
> +#define MMSS_DP1_GENERIC0_0 (0x00000490)
>
> #define MMSS_DP_VSCEXT_0 (0x000002D0)
> #define MMSS_DP_VSCEXT_1 (0x000002D4)
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 16/38] drm/msm/dp: Add catalog support for 3rd/4th stream MST
2025-08-25 14:16 ` [PATCH v3 16/38] drm/msm/dp: Add catalog support for 3rd/4th stream MST Yongxing Mou
@ 2025-08-25 20:35 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 20:35 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel
On Mon, Aug 25, 2025 at 10:16:02PM +0800, Yongxing Mou wrote:
> To support 4-stream MST, the link clocks for stream 3 and stream 4
> are controlled by MST_2_LCLK and MST_3_LCLK. These clocks share the
> same register definitions but use different base addresses.
>
> This patch adds catalog support to enable programming of these blocks.
>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 82 ++++++++++++++++++---
> drivers/gpu/drm/msm/dp/dp_ctrl.h | 4 +-
> drivers/gpu/drm/msm/dp/dp_display.c | 24 ++++++-
> drivers/gpu/drm/msm/dp/dp_panel.c | 138 ++++++++++++++++++++++++++++++------
> drivers/gpu/drm/msm/dp/dp_panel.h | 4 +-
> drivers/gpu/drm/msm/dp/dp_reg.h | 14 ++++
> 6 files changed, 230 insertions(+), 36 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index b8b6a09966aed96f705bdd54cb16ea63e5f0141f..608a1a077301b2ef3c77c271d873bb4364abe779 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> @@ -118,6 +118,8 @@ struct msm_dp_ctrl_private {
> struct msm_dp_link *link;
> void __iomem *ahb_base;
> void __iomem *link_base;
> + void __iomem *mst2link_base;
> + void __iomem *mst3link_base;
>
> struct phy *phy;
>
> @@ -172,6 +174,40 @@ static inline void msm_dp_write_link(struct msm_dp_ctrl_private *ctrl,
> writel(data, ctrl->link_base + offset);
> }
>
> +static inline u32 msm_dp_read_mstlink(struct msm_dp_ctrl_private *ctrl,
> + enum msm_dp_stream_id stream_id, u32 offset)
> +{
> + switch (stream_id) {
> + case DP_STREAM_2:
> + return readl_relaxed(ctrl->mst2link_base + offset);
> + case DP_STREAM_3:
> + return readl_relaxed(ctrl->mst3link_base + offset);
> + default:
> + DRM_ERROR("error stream_id\n");
> + return 0;
I'd totally prefer having a single set of wrappers which can handle all
4 streams. Having separate call sequences is not a good idea and it
makes it hard to change / extend it.
> + }
> +}
> +
> +static inline void msm_dp_write_mstlink(struct msm_dp_ctrl_private *ctrl,
> + enum msm_dp_stream_id stream_id, u32 offset, u32 data)
> +{
> + /*
> + * To make sure link reg writes happens before any other operation,
> + * this function uses writel() instread of writel_relaxed()
> + */
> + switch (stream_id) {
> + case DP_STREAM_2:
> + writel(data, ctrl->mst2link_base + offset);
> + break;
> + case DP_STREAM_3:
> + writel(data, ctrl->mst3link_base + offset);
> + break;
> + default:
> + DRM_ERROR("error stream_id\n");
> + break;
> + }
> +}
> +
> static int msm_dp_aux_link_configure(struct drm_dp_aux *aux,
> struct msm_dp_link_info *link)
> {
> @@ -386,7 +422,11 @@ static void msm_dp_ctrl_config_ctrl_streams(struct msm_dp_ctrl_private *ctrl,
> u32 config = 0, tbd;
> u32 reg_offset = 0;
>
> - config = msm_dp_read_link(ctrl, REG_DP_CONFIGURATION_CTRL);
> + if (msm_dp_panel->stream_id < DP_STREAM_2)
> + config = msm_dp_read_link(ctrl, REG_DP_CONFIGURATION_CTRL);
> +
> + if (msm_dp_panel->stream_id == DP_STREAM_1)
> + reg_offset = REG_DP1_CONFIGURATION_CTRL - REG_DP_CONFIGURATION_CTRL;
>
> if (msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
> config |= DP_CONFIGURATION_CTRL_RGB_YUV; /* YUV420 */
> @@ -401,8 +441,11 @@ static void msm_dp_ctrl_config_ctrl_streams(struct msm_dp_ctrl_private *ctrl,
>
> drm_dbg_dp(ctrl->drm_dev, "stream DP_CONFIGURATION_CTRL=0x%x\n", config);
>
> - if (msm_dp_panel->stream_id == DP_STREAM_1)
> - reg_offset = REG_DP1_CONFIGURATION_CTRL - REG_DP_CONFIGURATION_CTRL;
> + if (msm_dp_panel->stream_id > DP_STREAM_1)
> + msm_dp_write_mstlink(ctrl, msm_dp_panel->stream_id,
> + REG_DP_MSTLINK_CONFIGURATION_CTRL, config);
> + else
> + msm_dp_write_link(ctrl, REG_DP_CONFIGURATION_CTRL + reg_offset, config);
> }
>
> static void msm_dp_ctrl_config_ctrl_link(struct msm_dp_ctrl_private *ctrl)
[...]
> diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
> index 43a9ce0539906e1f185abf250fdf161e462d9645..a806d397ff9d9ad3830b1f539614bffcc955a786 100644
> --- a/drivers/gpu/drm/msm/dp/dp_reg.h
> +++ b/drivers/gpu/drm/msm/dp/dp_reg.h
> @@ -142,6 +142,7 @@
>
> #define REG_DP_CONFIGURATION_CTRL (0x00000008)
> #define REG_DP1_CONFIGURATION_CTRL (0x00000400)
> +#define REG_DP_MSTLINK_CONFIGURATION_CTRL (0x00000034)
Please don't mix registers from different register spaces, it's
confusing.
> #define DP_CONFIGURATION_CTRL_SYNC_ASYNC_CLK (0x00000001)
> #define DP_CONFIGURATION_CTRL_STATIC_DYNAMIC_CN (0x00000002)
> #define DP_CONFIGURATION_CTRL_P_INTERLACED (0x00000004)
> @@ -163,12 +164,19 @@
> #define REG_DP1_SOFTWARE_MVID (0x00000414)
> #define REG_DP1_SOFTWARE_NVID (0x00000418)
> #define REG_DP1_TOTAL_HOR_VER (0x0000041C)
> +#define REG_MSTLINK_SOFTWARE_MVID (0x00000040)
> +#define REG_MSTLINK_SOFTWARE_NVID (0x00000044)
> +#define REG_DP_MSTLINK_TOTAL_HOR_VER (0x00000048)
> #define REG_DP_START_HOR_VER_FROM_SYNC (0x00000020)
> +#define REG_DP_MSTLINK_START_HOR_VER_FROM_SYNC (0x0000004C)
> #define REG_DP_HSYNC_VSYNC_WIDTH_POLARITY (0x00000024)
> +#define REG_DP_MSTLINK_HSYNC_VSYNC_WIDTH_POLARITY (0x00000050)
> #define REG_DP_ACTIVE_HOR_VER (0x00000028)
> +#define REG_DP_MSTLINK_ACTIVE_HOR_VER (0x00000054)
>
> #define REG_DP_MISC1_MISC0 (0x0000002C)
> #define REG_DP1_MISC1_MISC0 (0x0000042C)
> +#define REG_DP_MSTLINK_MISC1_MISC0 (0x00000058)
> #define DP_MISC0_SYNCHRONOUS_CLK (0x00000001)
> #define DP_MISC0_COLORIMETRY_CFG_SHIFT (0x00000001)
> #define DP_MISC0_TEST_BITS_DEPTH_SHIFT (0x00000005)
> @@ -236,9 +244,11 @@
>
> #define MMSS_DP_SDP_CFG (0x00000228)
> #define MMSS_DP1_SDP_CFG (0x000004E0)
> +#define MMSS_DP_MSTLINK_SDP_CFG (0x0000010c)
> #define GEN0_SDP_EN (0x00020000)
> #define MMSS_DP_SDP_CFG2 (0x0000022C)
> #define MMSS_DP1_SDP_CFG2 (0x000004E4)
> +#define MMSS_DP_MSTLINK_SDP_CFG2 (0x0000011c)
> #define MMSS_DP_AUDIO_TIMESTAMP_0 (0x00000230)
> #define MMSS_DP_AUDIO_TIMESTAMP_1 (0x00000234)
> #define GENERIC0_SDPSIZE_VALID (0x00010000)
> @@ -248,6 +258,7 @@
>
> #define MMSS_DP_SDP_CFG3 (0x0000024c)
> #define MMSS_DP1_SDP_CFG3 (0x000004E8)
> +#define MMSS_DP_MSTLINK_SDP_CFG3 (0x00000114)
> #define UPDATE_SDP (0x00000001)
>
> #define MMSS_DP_EXTENSION_0 (0x00000250)
> @@ -297,6 +308,9 @@
> #define MMSS_DP_GENERIC1_8 (0x00000348)
> #define MMSS_DP_GENERIC1_9 (0x0000034C)
> #define MMSS_DP1_GENERIC0_0 (0x00000490)
> +#define MMSS_DP_MSTLINK_GENERIC0_0 (0x000000BC)
> +#define MMSS_DP_MSTLINK_GENERIC0_1 (0x000000C0)
> +#define MMSS_DP_MSTLINK_GENERIC0_2 (0x000000C4)
>
> #define MMSS_DP_VSCEXT_0 (0x000002D0)
> #define MMSS_DP_VSCEXT_1 (0x000002D4)
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 17/38] drm/msm/dp: add support to send ACT packets for MST
2025-08-25 14:16 ` [PATCH v3 17/38] drm/msm/dp: add support to send ACT packets for MST Yongxing Mou
@ 2025-08-25 21:10 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 21:10 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:03PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> Whenever virtual channel slot allocation changes, the DP
> source must send the action control trigger sequence to notify
> the sink about the same. This would be applicable during the
> start and stop of the pixel stream. Add the infrastructure
> to be able to send ACT packets for the DP controller when
> operating in MST mode.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 39 +++++++++++++++++++++++++++++++++++--
> drivers/gpu/drm/msm/dp/dp_ctrl.h | 4 ++--
> drivers/gpu/drm/msm/dp/dp_display.c | 3 ++-
> drivers/gpu/drm/msm/dp/dp_display.h | 1 +
> drivers/gpu/drm/msm/dp/dp_reg.h | 2 ++
> 5 files changed, 44 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index 608a1a077301b2ef3c77c271d873bb4364abe779..16e5ed58e791971d5dca3077cbb77bfcc186505a 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> @@ -142,6 +142,7 @@ struct msm_dp_ctrl_private {
> bool core_clks_on;
> bool link_clks_on;
> bool stream_clks_on[DP_STREAM_MAX];
> + bool mst_active;
> };
>
> static inline u32 msm_dp_read_ahb(const struct msm_dp_ctrl_private *ctrl, u32 offset)
> @@ -227,6 +228,32 @@ static int msm_dp_aux_link_configure(struct drm_dp_aux *aux,
> return 0;
> }
>
> +void msm_dp_ctrl_mst_send_act(struct msm_dp_ctrl *msm_dp_ctrl)
> +{
> + struct msm_dp_ctrl_private *ctrl;
> + bool act_complete;
> +
> + ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
> +
> + if (!ctrl->mst_active)
> + return;
> +
> + msm_dp_write_link(ctrl, REG_DP_MST_ACT, 0x1);
> + /* make sure ACT signal is performed */
> + wmb();
> +
> + msleep(20); /* needs 1 frame time */
> +
> + act_complete = msm_dp_read_link(ctrl, REG_DP_MST_ACT);
> +
> + if (!act_complete)
> + drm_dbg_dp(ctrl->drm_dev, "mst ACT trigger complete SUCCESS\n");
> + else
> + drm_dbg_dp(ctrl->drm_dev, "mst ACT trigger complete failed\n");
Shouldn't it return an error if the register dind't latch? Also,
shouldn't we set mst_active only if the write went through?
> +
> + return;
> +}
> +
> /*
> * NOTE: resetting DP controller will also clear any pending HPD related interrupts
> */
> @@ -2079,6 +2106,8 @@ static int msm_dp_ctrl_link_maintenance(struct msm_dp_ctrl_private *ctrl)
>
> msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, DP_STATE_CTRL_SEND_VIDEO);
>
> + msm_dp_ctrl_mst_send_act(&ctrl->msm_dp_ctrl);
> +
> ret = msm_dp_ctrl_wait4video_ready(ctrl);
> end:
> return ret;
> @@ -2275,7 +2304,7 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
> msm_dp_ctrl_off_pixel_clk(&ctrl->msm_dp_ctrl, ctrl->panel->stream_id);
> msm_dp_ctrl_off_link(&ctrl->msm_dp_ctrl);
>
> - ret = msm_dp_ctrl_on_link(&ctrl->msm_dp_ctrl);
> + ret = msm_dp_ctrl_on_link(&ctrl->msm_dp_ctrl, false);
> if (ret) {
> DRM_ERROR("failed to enable DP link controller\n");
> return ret;
> @@ -2355,7 +2384,7 @@ 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, bool mst_active)
> {
> int rc = 0;
> struct msm_dp_ctrl_private *ctrl;
> @@ -2373,6 +2402,7 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl)
>
> rate = ctrl->panel->link_info.rate;
> pixel_rate = ctrl->panel->msm_dp_mode.drm_mode.clock;
> + ctrl->mst_active = mst_active;
>
> msm_dp_ctrl_core_clk_enable(&ctrl->msm_dp_ctrl);
>
> @@ -2643,6 +2673,8 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
>
> msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, DP_STATE_CTRL_SEND_VIDEO);
>
> + msm_dp_ctrl_mst_send_act(msm_dp_ctrl);
> +
> ret = msm_dp_ctrl_wait4video_ready(ctrl);
> if (ret)
> return ret;
> @@ -2682,6 +2714,8 @@ void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl)
>
> msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl);
>
> + ctrl->mst_active = false;
> +
> dev_pm_opp_set_rate(ctrl->dev, 0);
> msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl);
>
> @@ -2849,6 +2883,7 @@ struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link
> ctrl->link_base = link_base;
> ctrl->mst2link_base = mst2link_base;
> ctrl->mst3link_base = mst3link_base;
> + ctrl->mst_active = false;
>
> ret = msm_dp_ctrl_clk_init(&ctrl->msm_dp_ctrl, max_stream);
> if (ret) {
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
> index 2baf7a1ff44dd7139d2da86390121d5e7a063e9a..abf84ddf463638900684f2511549a593783d2247 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
> @@ -16,7 +16,7 @@ struct msm_dp_ctrl {
>
> struct phy;
>
> -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, bool mst_active);
> int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *msm_dp_panel);
> int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train);
> void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl);
> @@ -50,5 +50,5 @@ 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);
> -
> +void msm_dp_ctrl_mst_send_act(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 562a5eccf3f08c5669cc7c2ad1268897e975d0c4..eeba73f81c5ce7929dac88f4b47ac3741659864b 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -709,7 +709,7 @@ static int msm_dp_display_prepare(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, msm_dp_display->mst_active);
> if (rc) {
> DRM_ERROR("Failed link training (rc=%d)\n", rc);
> msm_dp_display->connector->state->link_status = DRM_LINK_STATUS_BAD;
> @@ -1557,6 +1557,7 @@ void msm_dp_display_atomic_disable(struct msm_dp *dp)
> msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
>
> msm_dp_ctrl_push_idle(msm_dp_display->ctrl);
> + msm_dp_ctrl_mst_send_act(msm_dp_display->ctrl);
> }
>
> static void msm_dp_display_unprepare(struct msm_dp_display_private *dp)
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
> index a839d0a3941eac3e277185e42fddea15ca05a17f..9442157bca9d63467b4c43fa644651ad2cbcbef5 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.h
> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
> @@ -21,6 +21,7 @@ struct msm_dp {
> bool audio_enabled;
> bool power_on;
> bool prepared;
> + bool mst_active;
> unsigned int connector_type;
> bool is_edp;
> bool internal_hpd;
> diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
> index a806d397ff9d9ad3830b1f539614bffcc955a786..de3d0b8b52c269fd7575edf3f4096a4284ad0b8d 100644
> --- a/drivers/gpu/drm/msm/dp/dp_reg.h
> +++ b/drivers/gpu/drm/msm/dp/dp_reg.h
> @@ -158,6 +158,8 @@
> #define DP_CONFIGURATION_CTRL_BPC_SHIFT (0x08)
> #define DP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT (0x0D)
>
> +#define REG_DP_MST_ACT (0x00000500)
> +
> #define REG_DP_SOFTWARE_MVID (0x00000010)
> #define REG_DP_SOFTWARE_NVID (0x00000018)
> #define REG_DP_TOTAL_HOR_VER (0x0000001C)
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 18/38] drm/msm/dp: Add support to enable MST in mainlink control
2025-08-25 14:16 ` [PATCH v3 18/38] drm/msm/dp: Add support to enable MST in mainlink control Yongxing Mou
@ 2025-08-25 21:24 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 21:24 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:04PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> Add support to program the MST enable bit in the mainlink control
> register when an MST session is active or being disabled.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 17 +++++++++++++++++
> drivers/gpu/drm/msm/dp/dp_reg.h | 1 +
> 2 files changed, 18 insertions(+)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
> index de3d0b8b52c269fd7575edf3f4096a4284ad0b8d..fda847b33f8d0d6ec4d2589586b5a3d6c9b1ccf3 100644
> --- a/drivers/gpu/drm/msm/dp/dp_reg.h
> +++ b/drivers/gpu/drm/msm/dp/dp_reg.h
> @@ -128,6 +128,7 @@
> #define DP_MAINLINK_FLUSH_MODE_UPDATE_SDP FIELD_PREP(DP_MAINLINK_CTRL_FLUSH_MODE_MASK, 1)
> #define DP_MAINLINK_FLUSH_MODE_SDE_PERIPH_UPDATE FIELD_PREP(DP_MAINLINK_CTRL_FLUSH_MODE_MASK, 3)
> #define DP_MAINLINK_FB_BOUNDARY_SEL (0x02000000)
> +#define DP_MAINLINK_CTRL_MST_EN (0x04000100)
Why are there two bits?
>
> #define REG_DP_STATE_CTRL (0x00000004)
> #define DP_STATE_CTRL_LINK_TRAINING_PATTERN1 (0x00000001)
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 19/38] drm/msm/dp: no need to update tu calculation for mst
2025-08-25 14:16 ` [PATCH v3 19/38] drm/msm/dp: no need to update tu calculation for mst Yongxing Mou
@ 2025-08-25 21:25 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 21:25 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:05PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> DP stream is transmitted in transfer units only for SST
> case there is no need to calculate and program TU parameters
comma before 'there'.
> for MST case. Skip the TU programming for MST cases.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index d562377a8d2846099bf0f8757128978a162745c3..c313a3b4853a1571c43a9f3c9e981fbc22d51d55 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> @@ -2685,7 +2685,8 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
>
> msm_dp_panel_clear_dsc_dto(msm_dp_panel);
>
> - msm_dp_ctrl_setup_tr_unit(ctrl);
> + if (!ctrl->mst_active)
> + msm_dp_ctrl_setup_tr_unit(ctrl);
>
> msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, DP_STATE_CTRL_SEND_VIDEO);
>
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 20/38] drm/msm/dp: Add support for MST channel slot allocation
2025-08-25 14:16 ` [PATCH v3 20/38] drm/msm/dp: Add support for MST channel slot allocation Yongxing Mou
@ 2025-08-25 21:52 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 21:52 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:06PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> DP MST streams share 64 MTP slots in a time-multiplexed manner. This patch
> adds support for calculating the rate governor, slot allocation, and slot
> reservation in the DP controller.
>
> Each MST stream can reserve its slots by calling
> dp_display_set_stream_info() from its bridge callbacks.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 213 +++++++++++++++++++++++++++++++++++-
> drivers/gpu/drm/msm/dp/dp_ctrl.h | 7 +-
> drivers/gpu/drm/msm/dp/dp_display.c | 40 ++++---
> drivers/gpu/drm/msm/dp/dp_display.h | 5 +-
> drivers/gpu/drm/msm/dp/dp_panel.h | 1 +
> drivers/gpu/drm/msm/dp/dp_reg.h | 14 ++-
> 6 files changed, 262 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index c313a3b4853a1571c43a9f3c9e981fbc22d51d55..9d58d9480fc4ab33c58218ef9beb54c64805c34c 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> @@ -109,6 +109,11 @@ struct msm_dp_vc_tu_mapping_table {
> u8 tu_size_minus1;
> };
>
> +struct msm_dp_mst_ch_slot_info {
> + u32 start_slot;
> + u32 tot_slots;
> +};
> +
> struct msm_dp_ctrl_private {
> struct msm_dp_ctrl msm_dp_ctrl;
> struct drm_device *drm_dev;
> @@ -143,6 +148,8 @@ struct msm_dp_ctrl_private {
> bool link_clks_on;
> bool stream_clks_on[DP_STREAM_MAX];
> bool mst_active;
> +
> + struct msm_dp_mst_ch_slot_info mst_ch_info[DP_STREAM_MAX];
> };
>
> static inline u32 msm_dp_read_ahb(const struct msm_dp_ctrl_private *ctrl, u32 offset)
> @@ -267,6 +274,77 @@ static void msm_dp_ctrl_mst_config(struct msm_dp_ctrl_private *ctrl, bool enable
> msm_dp_write_link(ctrl, REG_DP_MAINLINK_CTRL, mainlink_ctrl);
> }
>
> +static void msm_dp_ctrl_mst_channel_alloc(struct msm_dp_ctrl_private *ctrl,
> + enum msm_dp_stream_id stream_id, u32 ch_start_slot,
> + u32 tot_slot_cnt)
> +{
> + u32 i, slot_reg_1, slot_reg_2, slot;
> + u32 reg_off = 0;
> + int const num_slots_per_reg = 32;
> +
> + if (ch_start_slot > DP_MAX_TIME_SLOTS ||
> + (ch_start_slot + tot_slot_cnt > DP_MAX_TIME_SLOTS)) {
> + DRM_ERROR("invalid slots start %d, tot %d\n",
> + ch_start_slot, tot_slot_cnt);
> + return;
> + }
> +
> + drm_dbg_dp(ctrl->drm_dev, "stream_id %d, start_slot %d, tot_slot %d\n",
> + stream_id, ch_start_slot, tot_slot_cnt);
> +
> + if (stream_id == DP_STREAM_1)
> + reg_off = REG_DP_DP1_TIMESLOT_1_32 - REG_DP_DP0_TIMESLOT_1_32;
> +
> + slot_reg_1 = 0;
> + slot_reg_2 = 0;
> +
> + if (ch_start_slot && tot_slot_cnt) {
> + ch_start_slot--;
> + for (i = 0; i < tot_slot_cnt; i++) {
You can replace loops with maths.
> + if (ch_start_slot < num_slots_per_reg) {
> + slot_reg_1 |= BIT(ch_start_slot);
> + } else {
> + slot = ch_start_slot - num_slots_per_reg;
> + slot_reg_2 |= BIT(slot);
> + }
> + ch_start_slot++;
> + }
> + }
> +
> + drm_dbg_dp(ctrl->drm_dev, "stream_id:%d slot_reg_1:%d, slot_reg_2:%d\n", stream_id,
> + slot_reg_1, slot_reg_2);
> +
> + if (stream_id > DP_STREAM_1) {
> + msm_dp_write_mstlink(ctrl, stream_id, REG_DP_MSTLINK_TIMESLOT_1_32,
> + slot_reg_1);
> + msm_dp_write_mstlink(ctrl, stream_id, REG_DP_MSTLINK_TIMESLOT_33_63,
> + slot_reg_2);
> + } else {
> + msm_dp_write_link(ctrl, REG_DP_DP0_TIMESLOT_1_32 + reg_off, slot_reg_1);
> + msm_dp_write_link(ctrl, REG_DP_DP0_TIMESLOT_33_63 + reg_off, slot_reg_2);
> + }
> +}
> +
> +static void msm_dp_ctrl_update_rg(struct msm_dp_ctrl_private *ctrl,
> + enum msm_dp_stream_id stream_id, u32 x_int, u32 y_frac_enum)
> +{
> + u32 rg, reg_off = 0;
> +
> + rg = y_frac_enum;
> + rg |= (x_int << 16);
> +
> + drm_dbg_dp(ctrl->drm_dev, "stream_id: %d x_int:%d y_frac_enum:%d rg:%d\n",
> + stream_id, x_int, y_frac_enum, rg);
> +
> + if (stream_id == DP_STREAM_1)
> + reg_off = REG_DP_DP1_RG - REG_DP_DP0_RG;
> +
> + if (stream_id > DP_STREAM_1)
> + msm_dp_write_mstlink(ctrl, stream_id, REG_DP_MSTLINK_DP_RG, rg);
> + else
> + msm_dp_write_link(ctrl, REG_DP_DP0_RG + reg_off, rg);
> +}
> +
> /*
> * NOTE: resetting DP controller will also clear any pending HPD related interrupts
> */
> @@ -2634,7 +2712,105 @@ int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_li
> return ret;
> }
>
> -int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *msm_dp_panel)
> +static void msm_dp_ctrl_mst_calculate_rg(struct msm_dp_ctrl_private *ctrl,
> + struct msm_dp_panel *panel,
> + u32 *p_x_int, u32 *p_y_frac_enum)
A comment would be appreciated.
> +{
> + u64 min_slot_cnt, max_slot_cnt;
> + u64 raw_target_sc, target_sc_fixp;
> + u64 ts_denom, ts_enum, ts_int;
> + u64 pclk = panel->msm_dp_mode.drm_mode.clock;
> + u64 lclk = 0;
> + u64 lanes = ctrl->link->link_params.num_lanes;
> + u64 bpp = panel->msm_dp_mode.bpp;
> + u64 pbn = panel->pbn;
> + u64 numerator, denominator, temp, temp1, temp2;
> + u32 x_int = 0, y_frac_enum = 0;
> + u64 target_strm_sym, ts_int_fixp, ts_frac_fixp, y_frac_enum_fixp;
> +
> + lclk = ctrl->link->link_params.rate;
> +
> + /* min_slot_cnt */
> + numerator = pclk * bpp * 64 * 1000;
> + denominator = lclk * lanes * 8 * 1000;
> + min_slot_cnt = drm_fixp_from_fraction(numerator, denominator);
> +
> + /* max_slot_cnt */
> + numerator = pbn * 54 * 1000;
> + denominator = lclk * lanes;
> + max_slot_cnt = drm_fixp_from_fraction(numerator, denominator);
> +
> + /* raw_target_sc */
> + numerator = max_slot_cnt + min_slot_cnt;
> + denominator = drm_fixp_from_fraction(2, 1);
> + raw_target_sc = drm_fixp_div(numerator, denominator);
> +
> + /* target_sc */
> + temp = drm_fixp_from_fraction(256 * lanes, 1);
> + numerator = drm_fixp_mul(raw_target_sc, temp);
> + denominator = drm_fixp_from_fraction(256 * lanes, 1);
> + target_sc_fixp = drm_fixp_div(numerator, denominator);
> +
> + ts_enum = 256 * lanes;
> + ts_denom = drm_fixp_from_fraction(256 * lanes, 1);
> + ts_int = drm_fixp2int(target_sc_fixp);
> +
> + temp = drm_fixp2int_ceil(raw_target_sc);
> + if (temp != ts_int) {
> + temp = drm_fixp_from_fraction(ts_int, 1);
> + temp1 = raw_target_sc - temp;
> + temp2 = drm_fixp_mul(temp1, ts_denom);
> + ts_enum = drm_fixp2int(temp2);
> + }
> +
> + /* target_strm_sym */
> + ts_int_fixp = drm_fixp_from_fraction(ts_int, 1);
> + ts_frac_fixp = drm_fixp_from_fraction(ts_enum, drm_fixp2int(ts_denom));
> + temp = ts_int_fixp + ts_frac_fixp;
> + temp1 = drm_fixp_from_fraction(lanes, 1);
> + target_strm_sym = drm_fixp_mul(temp, temp1);
> +
> + /* x_int */
> + x_int = drm_fixp2int(target_strm_sym);
> +
> + /* y_enum_frac */
> + temp = drm_fixp_from_fraction(x_int, 1);
> + temp1 = target_strm_sym - temp;
> + temp2 = drm_fixp_from_fraction(256, 1);
> + y_frac_enum_fixp = drm_fixp_mul(temp1, temp2);
> +
> + temp1 = drm_fixp2int(y_frac_enum_fixp);
> + temp2 = drm_fixp2int_ceil(y_frac_enum_fixp);
> +
> + y_frac_enum = (u32)((temp1 == temp2) ? temp1 : temp1 + 1);
> +
> + *p_x_int = x_int;
> + *p_y_frac_enum = y_frac_enum;
> +
> + drm_dbg_dp(ctrl->drm_dev, "mst lane_cnt:%llu, rate:%llu x_int:%d, y_frac:%d\n",
globally, for all patches: s/\<mst\>/MST/g
> + lanes, lclk, x_int, y_frac_enum);
> +}
> +
> +static void msm_dp_ctrl_mst_stream_setup(struct msm_dp_ctrl_private *ctrl,
> + struct msm_dp_panel *panel,
> + u32 max_streams)
It's not that max_streams can change... I'd totally prefer to stop
passing it as params.
> +{
> + u32 x_int, y_frac_enum;
> +
> + if (!ctrl->mst_active)
> + return;
> +
> + drm_dbg_dp(ctrl->drm_dev, "mst stream channel allocation\n");
> +
> + msm_dp_ctrl_mst_stream_channel_slot_setup(&ctrl->msm_dp_ctrl, max_streams);
> +
> + msm_dp_ctrl_mst_calculate_rg(ctrl, panel, &x_int, &y_frac_enum);
> +
> + msm_dp_ctrl_update_rg(ctrl, panel->stream_id, x_int, y_frac_enum);
> +}
> +
> +int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl,
> + struct msm_dp_panel *msm_dp_panel, u32 max_streams)
> {
> int ret = 0;
> bool mainlink_ready = false;
> @@ -2688,6 +2864,8 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
> if (!ctrl->mst_active)
> msm_dp_ctrl_setup_tr_unit(ctrl);
>
> + msm_dp_ctrl_mst_stream_setup(ctrl, msm_dp_panel, max_streams);
> +
> msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, DP_STATE_CTRL_SEND_VIDEO);
>
> msm_dp_ctrl_mst_send_act(msm_dp_ctrl);
> @@ -2742,6 +2920,39 @@ void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl)
> phy, phy->init_count, phy->power_count);
> }
>
> +void msm_dp_ctrl_set_mst_channel_info(struct msm_dp_ctrl *msm_dp_ctrl,
> + enum msm_dp_stream_id stream_id,
> + u32 start_slot, u32 tot_slots)
> +{
> + struct msm_dp_ctrl_private *ctrl;
> +
> + if (!msm_dp_ctrl || stream_id >= DP_STREAM_MAX) {
> + DRM_ERROR("invalid input\n");
> + return;
> + }
> +
> + ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
> +
> + ctrl->mst_ch_info[stream_id].start_slot = start_slot;
> + ctrl->mst_ch_info[stream_id].tot_slots = tot_slots;
> +}
> +
> +void msm_dp_ctrl_mst_stream_channel_slot_setup(struct msm_dp_ctrl *msm_dp_ctrl, u32 max_streams)
> +{
> + struct msm_dp_ctrl_private *ctrl;
> + int i;
> +
> + ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
> +
> + if (!ctrl->mst_active)
> + return;
> +
> + for (i = DP_STREAM_0; i < max_streams; i++) {
> + msm_dp_ctrl_mst_channel_alloc(ctrl, i, ctrl->mst_ch_info[i].start_slot,
> + ctrl->mst_ch_info[i].tot_slots);
> + }
> +}
> +
> irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl)
> {
> struct msm_dp_ctrl_private *ctrl;
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
> index abf84ddf463638900684f2511549a593783d2247..751f00c97b94dc3b9e8fae2a86e261f71f293425 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, bool mst_active);
> -int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *msm_dp_panel);
> +int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl,
> + struct msm_dp_panel *msm_dp_panel, u32 max_streams);
> int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train);
> void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl);
> void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl, enum msm_dp_stream_id stream_id);
> @@ -51,4 +52,8 @@ 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);
> void msm_dp_ctrl_mst_send_act(struct msm_dp_ctrl *msm_dp_ctrl);
> +void msm_dp_ctrl_mst_stream_channel_slot_setup(struct msm_dp_ctrl *msm_dp_ctrl, u32 max_streams);
> +void msm_dp_ctrl_set_mst_channel_info(struct msm_dp_ctrl *msm_dp_ctrl,
> + enum msm_dp_stream_id stream_id,
> + u32 start_slot, u32 tot_slots);
> #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 eeba73f81c5ce7929dac88f4b47ac3741659864b..17633ba79aa7642856051b69227e8f5b23d76730 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -733,7 +733,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, dp->panel, dp->max_stream);
> if (!rc)
> msm_dp_display->power_on = true;
>
> @@ -817,21 +817,33 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
> return 0;
> }
>
> -int msm_dp_display_set_stream_id(struct msm_dp *dp,
> - struct msm_dp_panel *panel, enum msm_dp_stream_id stream_id)
> +int msm_dp_display_set_stream_info(struct msm_dp *msm_dp_display, struct msm_dp_panel *panel,
> + enum msm_dp_stream_id stream_id, u32 start_slot,
> + u32 num_slots, u32 pbn, int vcpi)
vcpi isn't being used at this patch, don't add it.
> {
> int rc = 0;
> - struct msm_dp_display_private *msm_dp_display;
> + struct msm_dp_display_private *dp;
> + const int max_slots = 64;
>
> - 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);
>
> - if (!msm_dp_display) {
> + if (!dp) {
> DRM_ERROR("invalid input\n");
> return -EINVAL;
> }
>
> - if (panel)
> + if (start_slot + num_slots > max_slots) {
> + DRM_ERROR("invalid channel info received. start:%d, slots:%d\n",
> + start_slot, num_slots);
> + return -EINVAL;
> + }
> +
> + msm_dp_ctrl_set_mst_channel_info(dp->ctrl, stream_id, start_slot, num_slots);
> +
> + if (panel) {
The panel is always passed, as far as I can see.
> panel->stream_id = stream_id;
> + panel->pbn = pbn;
> + }
>
> return rc;
> }
> @@ -1533,7 +1545,7 @@ 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);
>
> - msm_dp_display_set_stream_id(msm_dp_display, dp->panel, 0);
> + msm_dp_display_set_stream_info(msm_dp_display, dp->panel, 0, 0, 0, 0, 0);
>
> if (msm_dp_display->prepared) {
> rc = msm_dp_display_enable(dp);
> @@ -1550,14 +1562,16 @@ void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display)
> drm_dbg_dp(msm_dp_display->drm_dev, "type=%d Done\n", msm_dp_display->connector_type);
> }
>
> -void msm_dp_display_atomic_disable(struct msm_dp *dp)
> +void msm_dp_display_atomic_disable(struct msm_dp *msm_dp_display)
> {
> - struct msm_dp_display_private *msm_dp_display;
> + 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);
>
> - msm_dp_ctrl_push_idle(msm_dp_display->ctrl);
> - msm_dp_ctrl_mst_send_act(msm_dp_display->ctrl);
> + msm_dp_ctrl_push_idle(dp->ctrl);
> + msm_dp_ctrl_mst_stream_channel_slot_setup(dp->ctrl,
> + dp->max_stream);
> + msm_dp_ctrl_mst_send_act(dp->ctrl);
> }
>
> static void msm_dp_display_unprepare(struct msm_dp_display_private *dp)
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
> index 9442157bca9d63467b4c43fa644651ad2cbcbef5..fa92f763d2304f15af7c4e1e7e8aab5a6ffd3459 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.h
> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
> @@ -48,7 +48,8 @@ void msm_dp_display_mode_set(struct msm_dp *dp,
> 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);
> -int msm_dp_display_set_stream_id(struct msm_dp *dp,
> - struct msm_dp_panel *panel, enum msm_dp_stream_id stream_id);
> +int msm_dp_display_set_stream_info(struct msm_dp *dp_display, struct msm_dp_panel *panel,
> + enum msm_dp_stream_id stream_id,
> + u32 start_slot, u32 num_slots, u32 pbn, int vcpi);
>
> #endif /* _DP_DISPLAY_H_ */
> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
> index 2bfe3695994235d04e209a2785915107c6a8e413..cb5bf6c99a6f7a68995f0f0ac48382dc90beca31 100644
> --- a/drivers/gpu/drm/msm/dp/dp_panel.h
> +++ b/drivers/gpu/drm/msm/dp/dp_panel.h
> @@ -50,6 +50,7 @@ struct msm_dp_panel {
> u32 hw_revision;
>
> enum msm_dp_stream_id stream_id;
> + u32 pbn;
>
> u32 max_dp_lanes;
> u32 max_dp_link_rate;
> diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
> index fda847b33f8d0d6ec4d2589586b5a3d6c9b1ccf3..ee4debf796910e00d370ab4c687009747bae5378 100644
> --- a/drivers/gpu/drm/msm/dp/dp_reg.h
> +++ b/drivers/gpu/drm/msm/dp/dp_reg.h
> @@ -364,6 +364,19 @@
> #define REG_DP_PHY_AUX_BIST_CFG (0x00000050)
> #define REG_DP_PHY_AUX_INTERRUPT_STATUS (0x000000BC)
>
> +/* DP MST related registers */
> +#define DP_MAX_TIME_SLOTS 64
> +
> +#define REG_DP_MSTLINK_DP_RG (0X0000011C)
> +#define REG_DP_DP0_TIMESLOT_1_32 (0x00000404)
> +#define REG_DP_DP0_TIMESLOT_33_63 (0x00000408)
> +#define REG_DP_DP1_TIMESLOT_1_32 (0x0000040C)
> +#define REG_DP_DP1_TIMESLOT_33_63 (0x00000410)
> +#define REG_DP_MSTLINK_TIMESLOT_1_32 (0x00000038)
> +#define REG_DP_MSTLINK_TIMESLOT_33_63 (0x0000003C)
> +#define REG_DP_DP0_RG (0x000004F8)
> +#define REG_DP_DP1_RG (0x000004FC)
> +
> /* DP HDCP 1.3 registers */
> #define DP_HDCP_CTRL (0x0A0)
> #define DP_HDCP_STATUS (0x0A4)
> @@ -388,5 +401,4 @@
> #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA10 (0x018)
> #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA11 (0x01C)
> #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA12 (0x020)
> -
> #endif /* _DP_REG_H_ */
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 22/38] drm/msm/dp: Always program MST_FIFO_CONSTANT_FILL for MST use cases
2025-08-25 14:16 ` [PATCH v3 22/38] drm/msm/dp: Always program MST_FIFO_CONSTANT_FILL for MST use cases Yongxing Mou
@ 2025-08-25 21:55 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 21:55 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:08PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> As per the hardware programming guide, MST_FIFO_CONSTANT_FILL must
> always be programmed when operating in MST mode. This patch ensures
> the register is configured accordingly.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 2 ++
> drivers/gpu/drm/msm/dp/dp_panel.c | 12 ++++++++++++
> drivers/gpu/drm/msm/dp/dp_panel.h | 2 ++
> 3 files changed, 16 insertions(+)
>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 23/38] drm/msm/dp: abstract out the dp_display stream helpers to accept a panel
2025-08-25 14:16 ` [PATCH v3 23/38] drm/msm/dp: abstract out the dp_display stream helpers to accept a panel Yongxing Mou
@ 2025-08-25 22:18 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 22:18 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:09PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> Currently the dp_display bridge helpers, in particular the
> dp_display_enable()/dp_display_disable() use the cached panel.
> To be able to re-use these helpers for MST use-case abstract the
> helpers to use the panel which is passed in to them.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_display.c | 138 +++++++++++++++++++++++-------------
> drivers/gpu/drm/msm/dp/dp_display.h | 12 ++++
> 2 files changed, 102 insertions(+), 48 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index e02ada7a3dc3b89618aeadd998e9a41236ee6bbf..0815973e6597492e09f33359d9777c0e8ce31e0d 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -695,6 +695,11 @@ static int msm_dp_display_prepare(struct msm_dp_display_private *dp)
>
> drm_dbg_dp(dp->drm_dev, "sink_count=%d\n", dp->link->sink_count);
>
> + if (msm_dp_display->prepared) {
Why is it a part of this patch?
> + drm_dbg_dp(dp->drm_dev, "Link already setup, return\n");
> + return 0;
> + }
> +
> rc = pm_runtime_resume_and_get(&msm_dp_display->pdev->dev);
> if (rc) {
> DRM_ERROR("failed to pm_runtime_resume\n");
> @@ -722,7 +727,8 @@ static int msm_dp_display_prepare(struct msm_dp_display_private *dp)
> return rc;
> }
>
> -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)
These changes look fine, but they raise an interesting question: should
we rework the interface, making the msm_dp_panel the top-level object,
which then controls the ctrl, link, etc.?
> {
> int rc = 0;
> struct msm_dp *msm_dp_display = &dp->msm_dp_display;
> @@ -733,7 +739,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, dp->max_stream);
> + rc = msm_dp_ctrl_on_stream(dp->ctrl, msm_dp_panel, dp->max_stream);
> if (!rc)
> msm_dp_display->power_on = true;
>
> @@ -779,37 +785,17 @@ 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);
>
> - /* 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
> - */
> -
> - /* set dongle to D3 (power off) mode */
> - msm_dp_link_psm_config(dp->link, &dp->panel->link_info, true);
> - msm_dp_ctrl_off_pixel_clk(dp->ctrl, dp->panel->stream_id);
> - msm_dp_ctrl_off_link(dp->ctrl);
> - /* 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, dp->panel->stream_id);
> - msm_dp_ctrl_off_link(dp->ctrl);
> - msm_dp_display_host_phy_exit(dp);
> - }
> + msm_dp_ctrl_off_pixel_clk(dp->ctrl, msm_dp_panel->stream_id);
This doesn't seems to be a part of this patch. Maybe move it to the
previous patch, reworking this piece of code?
>
> msm_dp_display->power_on = false;
>
> @@ -1538,52 +1524,90 @@ void msm_dp_display_atomic_prepare(struct msm_dp *msm_dp_display)
> return;
> }
>
> -void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display)
> +void msm_dp_display_enable_helper(struct msm_dp *msm_dp_display, struct msm_dp_panel *msm_dp_panel)
> {
> struct msm_dp_display_private *dp;
> int rc = 0;
>
> dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
>
> - msm_dp_display_set_stream_info(msm_dp_display, dp->panel, 0, 0, 0, 0, 0);
> -
Please, don't mix different kinds of refactoring. You've promised to
bring in msm_dp_panel as an argument, but then you are also
restructuring functions. Try making your changes more logical or atomic.
> if (msm_dp_display->prepared) {
> - rc = msm_dp_display_enable(dp);
> + rc = msm_dp_display_enable(dp, msm_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, msm_dp_panel);
> }
> }
>
> drm_dbg_dp(msm_dp_display->drm_dev, "type=%d Done\n", msm_dp_display->connector_type);
> }
>
> -void msm_dp_display_atomic_disable(struct msm_dp *msm_dp_display)
> +void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display)
> {
> struct msm_dp_display_private *dp;
>
> dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
>
> - msm_dp_ctrl_push_idle(dp->ctrl, dp->panel);
> + msm_dp_display_set_stream_info(msm_dp_display, dp->panel, 0, 0, 0, 0, 0);
> +
> + msm_dp_display_enable_helper(msm_dp_display, dp->panel);
> +}
> +
> +void msm_dp_display_disable_helper(struct msm_dp *msm_dp_display,
> + struct msm_dp_panel *msm_dp_panel)
> +{
> + struct msm_dp_display_private *dp;
> +
> + dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
> +
> + msm_dp_ctrl_push_idle(dp->ctrl, msm_dp_panel);
> msm_dp_ctrl_mst_stream_channel_slot_setup(dp->ctrl,
> dp->max_stream);
> msm_dp_ctrl_mst_send_act(dp->ctrl);
> }
>
> -static void msm_dp_display_unprepare(struct msm_dp_display_private *dp)
> +void msm_dp_display_atomic_disable(struct msm_dp *msm_dp_display)
> {
> - struct msm_dp *msm_dp_display = &dp->msm_dp_display;
> + struct msm_dp_display_private *dp;
> +
> + dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
> +
> + msm_dp_display_disable_helper(msm_dp_display, dp->panel);
> +}
> +
> +void msm_dp_display_unprepare(struct msm_dp *msm_dp_display)
> +{
> + struct msm_dp_display_private *dp;
> +
> + dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
> +
> + if (!msm_dp_display->prepared) {
> + drm_dbg_dp(dp->drm_dev, "Link already setup, return\n");
> + return;
> + }
> +
> + /* 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_link(dp->ctrl);
> +
> + /* re-init the PHY so that we can listen to Dongle disconnect */
> + if (dp->link->sink_count == 0)
> + msm_dp_ctrl_reinit_phy(dp->ctrl);
> + else
> + msm_dp_display_host_phy_exit(dp);
>
> pm_runtime_put_sync(&msm_dp_display->pdev->dev);
>
> msm_dp_display->prepared = false;
> }
>
> -void msm_dp_display_atomic_post_disable(struct msm_dp *dp)
> +void msm_dp_display_atomic_post_disable_helper(struct msm_dp *dp, struct msm_dp_panel *msm_dp_panel)
> {
> struct msm_dp_display_private *msm_dp_display;
>
> @@ -1597,28 +1621,46 @@ 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_panel);
> +
> + drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type);
> +}
> +
> +void msm_dp_display_atomic_post_disable(struct msm_dp *msm_dp_display)
> +{
> + struct msm_dp_display_private *dp;
> +
> + dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
> +
> + msm_dp_display_atomic_post_disable_helper(msm_dp_display, dp->panel);
>
> msm_dp_display_unprepare(msm_dp_display);
> +}
>
> - drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type);
> +void msm_dp_display_mode_set_helper(struct msm_dp *msm_dp,
> + const struct drm_display_mode *mode,
> + const struct drm_display_mode *adjusted_mode,
> + struct msm_dp_panel *msm_dp_panel)
> +{
> + struct msm_dp_display_private *dp;
> +
> + dp = container_of(msm_dp, struct msm_dp_display_private, msm_dp_display);
> +
> + msm_dp_display_set_mode(msm_dp, adjusted_mode, 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;
> }
>
> -void msm_dp_display_mode_set(struct msm_dp *dp,
> +void msm_dp_display_mode_set(struct msm_dp *msm_dp,
> const struct drm_display_mode *mode,
> const struct drm_display_mode *adjusted_mode)
> {
> - 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;
> + struct msm_dp_display_private *dp;
>
> - msm_dp_display_set_mode(dp, adjusted_mode, msm_dp_panel);
> + dp = container_of(msm_dp, struct msm_dp_display_private, msm_dp_display);
>
> - /* populate wide_bus_support to different layers */
> - msm_dp_display->ctrl->wide_bus_en = msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 ?
> - false : msm_dp_display->wide_bus_supported;
> + msm_dp_display_mode_set_helper(msm_dp, mode, adjusted_mode, dp->panel);
> }
>
> void msm_dp_bridge_hpd_enable(struct drm_bridge *bridge)
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
> index fa92f763d2304f15af7c4e1e7e8aab5a6ffd3459..20b7ed735b3f428e894b82ae2756d0efcfa47624 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.h
> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
> @@ -51,5 +51,17 @@ enum drm_mode_status msm_dp_display_mode_valid(struct msm_dp *dp,
> int msm_dp_display_set_stream_info(struct msm_dp *dp_display, struct msm_dp_panel *panel,
> enum msm_dp_stream_id stream_id,
> u32 start_slot, u32 num_slots, u32 pbn, int vcpi);
> +void msm_dp_display_enable_helper(struct msm_dp *msm_dp_display,
> + struct msm_dp_panel *msm_dp_panel);
> +void msm_dp_display_disable_helper(struct msm_dp *msm_dp_display,
> + struct msm_dp_panel *msm_dp_panel);
> +void msm_dp_display_mode_set_helper(struct msm_dp *msm_dp_display,
> + const struct drm_display_mode *mode,
> + const struct drm_display_mode *adjusted_mode,
> + struct msm_dp_panel *msm_dp_panel);
> +void msm_dp_display_atomic_post_disable_helper(struct msm_dp *msm_dp_display,
> + struct msm_dp_panel *msm_dp_panel);
> +
> +void msm_dp_display_unprepare(struct msm_dp *dp);
>
> #endif /* _DP_DISPLAY_H_ */
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 24/38] drm/msm/dp: replace power_on with active_stream_cnt for dp_display
2025-08-25 14:16 ` [PATCH v3 24/38] drm/msm/dp: replace power_on with active_stream_cnt for dp_display Yongxing Mou
@ 2025-08-25 22:22 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 22:22 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:10PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> For DP MST, the link clock and power domain resources stay on until
> both streams have been disabled OR we receive hotplug. Introduce an
> active_stream_cnt to track the number of active streams and necessary
> state handling. Replace the power_on variable with active_stream_cnt
> as power_on boolean works only for a single stream.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_audio.c | 2 +-
> drivers/gpu/drm/msm/dp/dp_display.c | 50 ++++++++++++++++++++++++-------------
> drivers/gpu/drm/msm/dp/dp_display.h | 3 ++-
> 3 files changed, 36 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_audio.c b/drivers/gpu/drm/msm/dp/dp_audio.c
> index 41018e82efa10ec863eb4b60d8df66c23c432fa5..e84c8b8e83d9ec689c0d29e8ac69860a745a4877 100644
> --- a/drivers/gpu/drm/msm/dp/dp_audio.c
> +++ b/drivers/gpu/drm/msm/dp/dp_audio.c
> @@ -284,7 +284,7 @@ int msm_dp_audio_prepare(struct drm_bridge *bridge,
> * such cases check for connection status and bail out if not
> * connected.
> */
> - if (!msm_dp_display->power_on) {
> + if (!msm_dp_display_get_active_stream_cnt(msm_dp_display)) {
Why can't it be a part of the public structure?
> rc = -EINVAL;
> goto end;
> }
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 0815973e6597492e09f33359d9777c0e8ce31e0d..e2e6b0ea2f9dbfe49a599ca19b1d205669365c4c 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -73,6 +73,8 @@ struct msm_dp_display_private {
>
> bool wide_bus_supported;
>
> + u32 active_stream_cnt;
> +
> struct msm_dp_audio *audio;
>
> void __iomem *ahb_base;
> @@ -175,6 +177,15 @@ static const struct of_device_id msm_dp_dt_match[] = {
> {}
> };
>
> +int msm_dp_display_get_active_stream_cnt(struct msm_dp *msm_dp_display)
> +{
> + struct msm_dp_display_private *dp;
> +
> + dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
> +
> + return dp->active_stream_cnt;
> +}
> +
> static struct msm_dp_display_private *dev_get_dp_display_private(struct device *dev)
> {
> struct msm_dp *dp = dev_get_drvdata(dev);
> @@ -709,15 +720,17 @@ static int msm_dp_display_prepare(struct msm_dp_display_private *dp)
> if (dp->link->sink_count == 0)
> return rc;
>
> - if (msm_dp_display->link_ready && !msm_dp_display->power_on) {
> + if (msm_dp_display->link_ready && !dp->active_stream_cnt) {
> msm_dp_display_host_phy_init(dp);
> force_link_train = true;
> }
>
> - rc = msm_dp_ctrl_on_link(dp->ctrl, msm_dp_display->mst_active);
> - if (rc) {
> - DRM_ERROR("Failed link training (rc=%d)\n", rc);
> - msm_dp_display->connector->state->link_status = DRM_LINK_STATUS_BAD;
> + if (!dp->active_stream_cnt) {
Such changes need at least some explanation in the commit message or,
better, split to a separate patch.
> + rc = msm_dp_ctrl_on_link(dp->ctrl, msm_dp_display->mst_active);
> + if (rc) {
> + DRM_ERROR("Failed link training (rc=%d)\n", rc);
> + msm_dp_display->connector->state->link_status = DRM_LINK_STATUS_BAD;
> + }
> }
>
> rc = msm_dp_ctrl_prepare_stream_on(dp->ctrl, force_link_train);
> @@ -731,17 +744,10 @@ 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;
>
> drm_dbg_dp(dp->drm_dev, "sink_count=%d\n", dp->link->sink_count);
> - if (msm_dp_display->power_on) {
> - drm_dbg_dp(dp->drm_dev, "Link already setup, return\n");
> - return 0;
> - }
>
> rc = msm_dp_ctrl_on_stream(dp->ctrl, msm_dp_panel, dp->max_stream);
> - if (!rc)
> - msm_dp_display->power_on = true;
>
> return rc;
> }
> @@ -788,16 +794,14 @@ static void msm_dp_display_audio_notify_disable(struct msm_dp_display_private *d
> 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)
> + if (!dp->active_stream_cnt)
> return 0;
>
> msm_dp_panel_disable_vsc_sdp(msm_dp_panel);
>
> msm_dp_ctrl_off_pixel_clk(dp->ctrl, msm_dp_panel->stream_id);
>
> - msm_dp_display->power_on = false;
> + dp->active_stream_cnt--;
>
> drm_dbg_dp(dp->drm_dev, "sink count: %d\n", dp->link->sink_count);
> return 0;
> @@ -934,7 +938,7 @@ void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp)
> * power_on status before dumping DP registers to avoid crash due
> * to unclocked access
> */
> - if (!dp->power_on)
> + if (!msm_dp_display->active_stream_cnt)
> return;
>
> msm_disp_snapshot_add_block(disp_state, msm_dp_display->ahb_len,
> @@ -1543,6 +1547,8 @@ void msm_dp_display_enable_helper(struct msm_dp *msm_dp_display, struct msm_dp_p
> }
> }
>
> + dp->active_stream_cnt++;
This should be a part of the 'stream_on' function. Also, which locking
ensures that there is no concurrent modification of this var?
> +
> drm_dbg_dp(msm_dp_display->drm_dev, "type=%d Done\n", msm_dp_display->connector_type);
> }
>
> @@ -1564,6 +1570,11 @@ void msm_dp_display_disable_helper(struct msm_dp *msm_dp_display,
>
> dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
>
> + if (!dp->active_stream_cnt) {
> + drm_dbg_dp(dp->drm_dev, "no active streams\n");
> + return;
> + }
> +
> msm_dp_ctrl_push_idle(dp->ctrl, msm_dp_panel);
> msm_dp_ctrl_mst_stream_channel_slot_setup(dp->ctrl,
> dp->max_stream);
> @@ -1590,6 +1601,11 @@ void msm_dp_display_unprepare(struct msm_dp *msm_dp_display)
> return;
> }
>
> + if (dp->active_stream_cnt) {
> + drm_dbg_dp(dp->drm_dev, "stream still active, return\n");
> + return;
> + }
> +
> /* 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);
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
> index 20b7ed735b3f428e894b82ae2756d0efcfa47624..b1ea027438d952c94f3ae80725c92e46c631bdb2 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.h
> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
> @@ -19,7 +19,6 @@ struct msm_dp {
> struct drm_bridge *bridge;
> bool link_ready;
> bool audio_enabled;
> - bool power_on;
> bool prepared;
> bool mst_active;
> unsigned int connector_type;
> @@ -64,4 +63,6 @@ void msm_dp_display_atomic_post_disable_helper(struct msm_dp *msm_dp_display,
>
> void msm_dp_display_unprepare(struct msm_dp *dp);
>
> +int msm_dp_display_get_active_stream_cnt(struct msm_dp *msm_dp_display);
> +
> #endif /* _DP_DISPLAY_H_ */
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 25/38] drm/msm/dp: Mark the SST bridge disconnected when mst is active
2025-08-25 14:16 ` [PATCH v3 25/38] drm/msm/dp: Mark the SST bridge disconnected when mst is active Yongxing Mou
@ 2025-08-25 22:23 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-25 22:23 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:11PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> The bridge detect function is only applicable for SST. In MST mode,
> connector detection is handled by MST bridges. This patch skips
> detection for the SST bridge when MST is active.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_display.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 26/38] drm/msm/dp: add an API to initialize MST on sink side
2025-08-25 14:16 ` [PATCH v3 26/38] drm/msm/dp: add an API to initialize MST on sink side Yongxing Mou
@ 2025-08-26 9:26 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-26 9:26 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:12PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> If the DP controller is capable of supporting multiple streams
> then initialize the DP sink in MST mode by programming the DP_MSTM_CTRL
> DPCD register to enable MST mode.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_display.c | 55 ++++++++++++++++++++++++++++++-------
> 1 file changed, 45 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index cb433103d439ac6b8089bdecf0ee6be35c914db1..84df34306fb557341bea288ea8c13b0c81b11919 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -14,6 +14,7 @@
> #include <linux/string_choices.h>
> #include <drm/display/drm_dp_aux_bus.h>
> #include <drm/display/drm_hdmi_audio_helper.h>
> +#include <drm/display/drm_dp_mst_helper.h>
> #include <drm/drm_edid.h>
>
> #include "msm_drv.h"
> @@ -297,6 +298,35 @@ static int msm_dp_display_lttpr_init(struct msm_dp_display_private *dp, u8 *dpcd
> return lttpr_count;
> }
>
> +static void msm_dp_display_mst_init(struct msm_dp_display_private *dp)
> +{
> + const unsigned long clear_mstm_ctrl_timeout_us = 100000;
> + u8 old_mstm_ctrl;
> + struct msm_dp *msm_dp = &dp->msm_dp_display;
> + int ret;
> +
> + /* clear sink mst state */
> + drm_dp_dpcd_readb(dp->aux, DP_MSTM_CTRL, &old_mstm_ctrl);
> + drm_dp_dpcd_writeb(dp->aux, DP_MSTM_CTRL, 0);
> +
> + /* add extra delay if MST old state is on*/
> + if (old_mstm_ctrl) {
> + drm_dbg_dp(dp->drm_dev, "wait %luus to set DP_MSTM_CTRL set 0\n",
> + clear_mstm_ctrl_timeout_us);
> + usleep_range(clear_mstm_ctrl_timeout_us,
> + clear_mstm_ctrl_timeout_us + 1000);
> + }
> +
> + ret = drm_dp_dpcd_writeb(dp->aux, DP_MSTM_CTRL,
> + DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC);
> + if (ret < 0) {
I didn't catch this up in the previous patches. We have two sets of DPCD
accessors: the older ones which can return error or the size of the data
that was actually read / written (which might be less than the size of
the buffer passed to the function) and newer ones, which return error or
0. drm_dp_dpcd_writeb() is from the first group, so if it was
successful, it should be returning 1. It's all a pain to handle, so
please start using newer accessors in your patches (the full conversion
of the MSM driver is on my todo list, but it's intrusive, so was
delaying it...).
TL;DR: inside your code please use drm_dp_dpcd_read_byte() /
drm_dp_dpcd_write_byte() / drm_dp_dpcd_read_data() /
drm_dp_dpcd_write_data().
> + DRM_ERROR("sink mst enablement failed\n");
> + return;
> + }
> +
> + msm_dp->mst_active = true;
> +}
> +
> static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
> {
> struct drm_connector *connector = dp->msm_dp_display.connector;
> @@ -315,18 +345,20 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
> if (rc)
> goto end;
>
> - drm_edid = drm_edid_read_ddc(connector, &dp->aux->ddc);
> - drm_edid_connector_update(connector, drm_edid);
> + if (!dp->mst_supported || !drm_dp_read_mst_cap(dp->aux, dp->panel->dpcd)) {
In several previous patches, the functions initializing MST can
return an error, leaving the user with half-initialized MST. Are those
errors a safety coding or do they have some real-use usecase? In the
latter case, it would be nice to keep SST wokring if enabling MST fails.
> + 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;
> - }
> + 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;
> + }
>
> - if (rc)
> - goto end;
> + if (rc)
> + goto end;
> + }
>
> msm_dp_link_process_request(dp->link);
>
> @@ -349,6 +381,9 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
> */
> msm_dp_link_psm_config(dp->link, &dp->panel->link_info, false);
>
> + if (dp->mst_supported && drm_dp_read_mst_cap(dp->aux, dp->panel->dpcd))
> + msm_dp_display_mst_init(dp);
> +
> msm_dp_link_reset_phy_params_vx_px(dp->link);
>
> if (!dp->msm_dp_display.internal_hpd)
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 27/38] drm/msm/dp: add dp_display_get_panel() to initialize DP panel
2025-08-25 14:16 ` [PATCH v3 27/38] drm/msm/dp: add dp_display_get_panel() to initialize DP panel Yongxing Mou
@ 2025-08-26 16:33 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-26 16:33 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:13PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> Add an API dp_display_get_panel() to initialize and return a DP
> panel to be used by DP MST module. Since some of the fields of
> DP panel are private, dp_display module needs to initialize these
> parts and return the panel back.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_display.c | 23 +++++++++++++++++++++++
> drivers/gpu/drm/msm/dp/dp_display.h | 2 ++
> 2 files changed, 25 insertions(+)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 84df34306fb557341bea288ea8c13b0c81b11919..abcab3ed43b6da5ef898355cf9b7561cd9fe0404 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -632,6 +632,29 @@ static int msm_dp_irq_hpd_handle(struct msm_dp_display_private *dp, u32 data)
> return 0;
> }
>
> +struct msm_dp_panel *msm_dp_display_get_panel(struct msm_dp *msm_dp_display)
> +{
> + struct msm_dp_display_private *dp;
> + struct msm_dp_panel *dp_panel;
> +
> + dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
> +
> + dp_panel = msm_dp_panel_get(&dp->msm_dp_display.pdev->dev, dp->aux, dp->link,
> + dp->link_base, dp->mst2link_base, dp->mst3link_base,
> + dp->pixel_base);
> +
> + if (IS_ERR(dp->panel)) {
> + DRM_ERROR("failed to initialize panel\n");
> + return NULL;
> + }
> +
> + memcpy(dp_panel->dpcd, dp->panel->dpcd, DP_RECEIVER_CAP_SIZE);
> + memcpy(&dp_panel->link_info, &dp->panel->link_info,
> + sizeof(dp->panel->link_info));
Both these lines show that link_info and dpcd belong to msm_dp_display
rather than the panel. The panel should only be describing properties of
the particular sink.
> +
> + return dp_panel;
> +}
> +
> static void msm_dp_display_deinit_sub_modules(struct msm_dp_display_private *dp)
> {
> msm_dp_audio_put(dp->audio);
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
> index b1ea027438d952c94f3ae80725c92e46c631bdb2..d5889b801d190b6f33b180ead898c1e4ebcbf8f3 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.h
> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
> @@ -65,4 +65,6 @@ void msm_dp_display_unprepare(struct msm_dp *dp);
>
> int msm_dp_display_get_active_stream_cnt(struct msm_dp *msm_dp_display);
>
> +struct msm_dp_panel *msm_dp_display_get_panel(struct msm_dp *msm_dp_display);
> +
> #endif /* _DP_DISPLAY_H_ */
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 28/38] drm/msm/dp: add dp_mst_drm to manage DP MST bridge operations
2025-08-25 14:16 ` [PATCH v3 28/38] drm/msm/dp: add dp_mst_drm to manage DP MST bridge operations Yongxing Mou
@ 2025-08-26 17:36 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-26 17:36 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:14PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> Add a new file dp_mst_drm to manage the DP MST bridge operations
> similar to the dp_drm file which manages the SST bridge operations.
> Each MST encoder creates one bridge and each bridge is bound to its
> own dp_panel abstraction to manage the operations of its pipeline.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/Makefile | 3 +-
> drivers/gpu/drm/msm/dp/dp_display.h | 3 +
> drivers/gpu/drm/msm/dp/dp_mst_drm.c | 556 ++++++++++++++++++++++++++++++++++++
> drivers/gpu/drm/msm/dp/dp_mst_drm.h | 86 ++++++
> 4 files changed, 647 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> index 0c0dfb25f01b193b10946fae20138caf32cf0ed2..a61fa2637ff317ed4dee715de5d12a7befa987f5 100644
> --- a/drivers/gpu/drm/msm/Makefile
> +++ b/drivers/gpu/drm/msm/Makefile
> @@ -142,7 +142,8 @@ msm-display-$(CONFIG_DRM_MSM_DP)+= dp/dp_aux.o \
> dp/dp_link.o \
> dp/dp_panel.o \
> dp/dp_audio.o \
> - dp/dp_utils.o
> + dp/dp_utils.o \
> + dp/dp_mst_drm.o
>
> msm-display-$(CONFIG_DRM_MSM_HDMI_HDCP) += hdmi/hdmi_hdcp.o
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
> index d5889b801d190b6f33b180ead898c1e4ebcbf8f3..f958de6244b556df5452a5dbec6899fb79a57193 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.h
> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
> @@ -7,6 +7,7 @@
> #define _DP_DISPLAY_H_
>
> #include "dp_panel.h"
> +#include "dp_mst_drm.h"
> #include "disp/msm_disp_snapshot.h"
>
> #define DP_MAX_PIXEL_CLK_KHZ 675000
> @@ -25,6 +26,8 @@ struct msm_dp {
> bool is_edp;
> bool internal_hpd;
>
> + struct msm_dp_mst *msm_dp_mst;
> +
> struct msm_dp_audio *msm_dp_audio;
> bool psr_supported;
> };
> diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..73de29136801ef5f45e0b2d09280fe113021b68c
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> @@ -0,0 +1,556 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +
> +/*
> + * Copyright © 2014 Red Hat.
Is it based on? Using the code? C&P?
> + *
> + * Permission to use, copy, modify, distribute, and sell this software and its
> + * documentation for any purpose is hereby granted without fee, provided that
> + * the above copyright notice appear in all copies and that both that copyright
> + * notice and this permission notice appear in supporting documentation, and
> + * that the name of the copyright holders not be used in advertising or
> + * publicity pertaining to distribution of the software without specific,
> + * written prior permission. The copyright holders make no representations
> + * about the suitability of this software for any purpose. It is provided "as
> + * is" without express or implied warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
> + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
> + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
> + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
> + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
> + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
> + * OF THIS SOFTWARE.
Why? Anyway, MIT licence would be absorbed into GPL-2.0-only (or it
should be GPL-2.0-only OR MIT)
> + */
> +
> +#include "dp_mst_drm.h"
> +
> +#define to_msm_dp_mst_bridge(x) container_of((x), struct msm_dp_mst_bridge, base)
> +#define to_msm_dp_mst_bridge_priv(x) \
Unused
> + container_of((x), struct msm_dp_mst_bridge, obj)
> +#define to_msm_dp_mst_bridge_state_priv(x) \
Why is it _priv? There is no 'private' bridge state.
> + container_of((x), struct msm_dp_mst_bridge_state, base)
> +#define to_msm_dp_mst_bridge_state(x) \
> + to_msm_dp_mst_bridge_state_priv((x)->obj.state)
> +#define to_msm_dp_mst_connector(x) \
> + container_of((x), struct msm_dp_mst_connector, connector)
> +
> +#define DP_MST_CONN_ID(bridge) ((bridge)->connector ? \
> + (bridge)->connector->base.id : 0)
> +
> +#define MAX_DPCD_TRANSACTION_BYTES 16
> +
> +static struct drm_private_state *msm_dp_mst_duplicate_bridge_state(struct drm_private_obj *obj)
> +{
> + struct msm_dp_mst_bridge_state *mst_bridge_state;
> +
> + mst_bridge_state = kmemdup(obj->state, sizeof(*mst_bridge_state), GFP_KERNEL);
> + if (!mst_bridge_state)
> + return NULL;
> +
> + __drm_atomic_helper_private_obj_duplicate_state(obj, &mst_bridge_state->base);
> +
> + return &mst_bridge_state->base;
> +}
> +
> +static void msm_dp_mst_destroy_bridge_state(struct drm_private_obj *obj,
> + struct drm_private_state *state)
> +{
> + struct msm_dp_mst_bridge_state *mst_bridge_state =
> + to_msm_dp_mst_bridge_state_priv(state);
> +
> + kfree(mst_bridge_state);
> +}
> +
> +static const struct drm_private_state_funcs msm_dp_mst_bridge_state_funcs = {
> + .atomic_duplicate_state = msm_dp_mst_duplicate_bridge_state,
> + .atomic_destroy_state = msm_dp_mst_destroy_bridge_state,
> +};
> +
> +/**
> + * dp_mst_find_vcpi_slots() - Find VCPI slots for this PBN value
> + * @mgr: manager to use
> + * @pbn: payload bandwidth to convert into slots.
> + *
> + * Calculate the number of VCPI slots that will be required for the given PBN
> + * value.
> + *
> + * RETURNS:
Return: foo, abc or def
> + * The total slots required for this port, or error.
> + */
> +static int msm_dp_mst_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, int pbn)
> +{
> + int num_slots;
> + struct drm_dp_mst_topology_state *state;
> +
> + state = to_drm_dp_mst_topology_state(mgr->base.state);
> + num_slots = DIV_ROUND_UP(pbn, dfixed_trunc(state->pbn_div));
Don't you get those as payload->time_slots ?
> +
> + /* max. time slots - one slot for MTP header */
> + if (num_slots > 63)
> + return -ENOSPC;
> + return num_slots;
> +}
> +
> +static void msm_dp_mst_update_timeslots(struct msm_dp_mst *mst,
> + struct msm_dp_mst_bridge *mst_bridge,
> + struct drm_atomic_state *state,
> + struct drm_dp_mst_port *port)
> +{
> + int i;
> + struct msm_dp_mst_bridge *msm_dp_bridge;
> + struct drm_dp_mst_topology_state *mst_state;
> + struct drm_dp_mst_atomic_payload *payload;
> + struct msm_dp_mst_bridge_state *mst_bridge_state;
> + int prev_start = 0;
> + int prev_slots = 0;
> +
> + mst_state = to_drm_dp_mst_topology_state(mst->mst_mgr.base.state);
> + payload = drm_atomic_get_mst_payload_state(mst_state, port);
> +
> + if (!payload) {
> + DRM_ERROR("mst bridge [%d] update_timeslots failed, null payload\n",
> + mst_bridge->id);
> + return;
> + }
> +
> + for (i = 0; i < mst->max_streams; i++) {
> + msm_dp_bridge = mst->mst_bridge[i];
> + if (mst_bridge == msm_dp_bridge) {
> + mst_bridge_state = to_msm_dp_mst_bridge_state(msm_dp_bridge);
> + /*
> + * When a payload was removed make sure to move any payloads after it
> + * to the left so all payloads are aligned to the left.
> + */
The comment is not valid anymore, as far as I understand.
> + if (payload->vc_start_slot < 0) {
> + // cache the payload
> + prev_start = mst_bridge_state->start_slot;
> + prev_slots = mst_bridge_state->num_slots;
> + mst_bridge_state->pbn = 0;
> + mst_bridge_state->start_slot = 1;
> + mst_bridge_state->num_slots = 0;
> + mst_bridge_state->vcpi = 0;
> + } else { //add payload
> + mst_bridge_state->pbn = payload->pbn;
> + mst_bridge_state->start_slot = payload->vc_start_slot;
> + mst_bridge_state->num_slots = payload->time_slots;
> + mst_bridge_state->vcpi = payload->vcpi;
I still see a lot of manual slot management here. Can't we use the
struct drm_dp_mst_atomic_payload directly instead of copying and caching
everything?
> + }
> + }
> + }
> +
> + // Now commit all the updated payloads
> + for (i = 0; i < mst->max_streams; i++) {
> + msm_dp_bridge = mst->mst_bridge[i];
> +
> + mst_bridge_state = to_msm_dp_mst_bridge_state(msm_dp_bridge);
> + //Shift payloads to the left if there was a removed payload.
> + if (payload->vc_start_slot < 0 && mst_bridge_state->start_slot > prev_start)
> + mst_bridge_state->start_slot -= prev_slots;
Well... Don't MST helpers do this already for you? I see corresponding
part in drm_dp_remove_payload_part2().
> +
> + msm_dp_display_set_stream_info(mst->msm_dp, msm_dp_bridge->msm_dp_panel,
> + msm_dp_bridge->id, mst_bridge_state->start_slot,
> + mst_bridge_state->num_slots,
> + mst_bridge_state->pbn, mst_bridge_state->vcpi);
> + drm_dbg_dp(mst->msm_dp->drm_dev,
> + "conn:%d vcpi:%d start_slot:%d num_slots:%d, pbn:%d\n",
> + DP_MST_CONN_ID(msm_dp_bridge), mst_bridge_state->vcpi,
> + mst_bridge_state->start_slot,
> + mst_bridge_state->num_slots, mst_bridge_state->pbn);
> + }
> +}
> +
> +static int msm_dp_mst_bridge_pre_enable_part1(struct msm_dp_mst_bridge *dp_bridge,
> + struct drm_atomic_state *state)
> +{
> + struct msm_dp *dp_display = dp_bridge->display;
> + struct msm_dp_mst *mst = dp_display->msm_dp_mst;
> + struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(dp_bridge->connector);
> + struct drm_dp_mst_port *port = mst_conn->mst_port;
> + struct drm_dp_mst_topology_state *mst_state;
> + struct drm_dp_mst_atomic_payload *payload;
> + struct msm_dp_panel *dp_panel = mst_conn->dp_panel;
> + int pbn, slots;
> + int rc = 0;
> +
> + mst_state = drm_atomic_get_new_mst_topology_state(state, &mst->mst_mgr);
> +
> + pbn = drm_dp_calc_pbn_mode(dp_panel->msm_dp_mode.drm_mode.clock,
> + (mst_conn->connector.display_info.bpc * 3) << 4);
> +
> + slots = msm_dp_mst_find_vcpi_slots(&mst->mst_mgr, pbn);
payload->time_slots ?
> +
> + drm_dbg_dp(dp_display->drm_dev, "conn:%d pbn:%d, slots:%d\n", DP_MST_CONN_ID(dp_bridge),
> + pbn, slots);
> +
> + payload = drm_atomic_get_mst_payload_state(mst_state, port);
> + if (!payload || payload->time_slots <= 0) {
> + DRM_ERROR("time slots not allocated for conn:%d\n", DP_MST_CONN_ID(dp_bridge));
> + rc = -EINVAL;
> + return rc;
> + }
> +
> + drm_dp_mst_update_slots(mst_state, DP_CAP_ANSI_8B10B);
> +
> + rc = drm_dp_add_payload_part1(&mst->mst_mgr, mst_state, payload);
> + if (rc) {
> + DRM_ERROR("payload allocation failure for conn:%d\n", DP_MST_CONN_ID(dp_bridge));
> + return rc;
> + }
> +
> + msm_dp_mst_update_timeslots(mst, dp_bridge, state, port);
> +
> + return rc;
> +}
> +
> +static void _msm_dp_mst_bridge_pre_enable_part2(struct msm_dp_mst_bridge *dp_bridge,
> + struct drm_atomic_state *state)
> +{
> + struct msm_dp *dp_display = dp_bridge->display;
> + struct msm_dp_mst *mst = dp_display->msm_dp_mst;
> + struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(dp_bridge->connector);
> + struct drm_dp_mst_port *port = mst_conn->mst_port;
> + struct drm_dp_mst_topology_state *mst_state;
> + struct drm_dp_mst_atomic_payload *payload;
> +
> + drm_dp_check_act_status(&mst->mst_mgr);
> +
> + mst_state = to_drm_dp_mst_topology_state(mst->mst_mgr.base.state);
> + payload = drm_atomic_get_mst_payload_state(mst_state, port);
> +
> + if (!payload) {
Kill all defensive coding. If something can not happen, there is no need
to check for it.
> + DRM_ERROR("mst bridge [%d] null payload\n", dp_bridge->id);
> + return;
> + }
> +
> + if (!payload->port) {
> + DRM_ERROR("mst bridge [%d] null port\n", dp_bridge->id);
> + return;
> + }
> +
> + if (!payload->port->connector) {
> + DRM_ERROR("mst bridge [%d] part-2 failed, null connector\n",
> + dp_bridge->id);
> + return;
> + }
> +
> + if (payload->vc_start_slot == -1) {
> + DRM_ERROR("mst bridge [%d] part-2 failed, payload alloc part 1 failed\n",
> + dp_bridge->id);
> + return;
> + }
> +
> + drm_dp_add_payload_part2(&mst->mst_mgr, payload);
> +
> + drm_dbg_dp(dp_display->drm_dev, "mst bridge [%d] _pre enable part-2 complete\n",
> + dp_bridge->id);
> +}
> +
> +static void msm_dp_mst_bridge_pre_disable_part1(struct msm_dp_mst_bridge *dp_bridge,
> + struct drm_atomic_state *state)
> +{
> + struct msm_dp *dp_display = dp_bridge->display;
> + struct msm_dp_mst *mst = dp_display->msm_dp_mst;
> + struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(dp_bridge->connector);
> + struct drm_dp_mst_port *port = mst_conn->mst_port;
> + struct drm_dp_mst_topology_state *old_mst_state;
> + struct drm_dp_mst_topology_state *new_mst_state;
> + const struct drm_dp_mst_atomic_payload *old_payload;
> + struct drm_dp_mst_atomic_payload *new_payload;
> +
> + old_mst_state = drm_atomic_get_old_mst_topology_state(state, &mst->mst_mgr);
> +
> + new_mst_state = drm_atomic_get_new_mst_topology_state(state, &mst->mst_mgr);
> +
> + old_payload = drm_atomic_get_mst_payload_state(old_mst_state, port);
> + new_payload = drm_atomic_get_mst_payload_state(new_mst_state, port);
> +
> + if (!old_payload || !new_payload) {
> + DRM_ERROR("mst bridge [%d] _pre disable part-1 failed, null payload\n",
> + dp_bridge->id);
> + return;
> + }
> +
> + drm_dp_remove_payload_part1(&mst->mst_mgr, new_mst_state, new_payload);
> + drm_dp_remove_payload_part2(&mst->mst_mgr, new_mst_state, old_payload, new_payload);
> +
> + msm_dp_mst_update_timeslots(mst, dp_bridge, state, port);
> +
> + drm_dbg_dp(dp_display->drm_dev, "mst bridge [%d] _pre disable part-1 complete\n",
> + dp_bridge->id);
> +}
> +
> +static void msm_dp_mst_bridge_atomic_pre_enable(struct drm_bridge *drm_bridge,
> + struct drm_atomic_state *state)
> +{
> + int rc = 0;
> + struct msm_dp_mst_bridge *bridge;
> + struct msm_dp *dp_display;
> + struct msm_dp_mst_bridge_state *mst_bridge_state;
> + struct msm_dp_mst *dp_mst;
> +
> + if (!drm_bridge) {
> + DRM_ERROR("Invalid params\n");
> + return;
> + }
> +
> + bridge = to_msm_dp_mst_bridge(drm_bridge);
> + mst_bridge_state = to_msm_dp_mst_bridge_state(bridge);
> + dp_display = bridge->display;
> + dp_mst = dp_display->msm_dp_mst;
> +
> + /* to cover cases of bridge_disable/bridge_enable without modeset */
> + bridge->connector = mst_bridge_state->connector;
> + bridge->msm_dp_panel = mst_bridge_state->msm_dp_panel;
> +
> + if (!bridge->connector) {
> + DRM_ERROR("Invalid connector\n");
> + return;
> + }
> +
> + mutex_lock(&dp_mst->mst_lock);
> + msm_dp_display_atomic_prepare(dp_display);
> +
> + rc = msm_dp_mst_bridge_pre_enable_part1(bridge, state);
> + if (rc) {
> + DRM_ERROR("[%d] DP display pre-enable failed, rc=%d\n", bridge->id, rc);
> + msm_dp_display_unprepare(dp_display);
> + mutex_unlock(&dp_mst->mst_lock);
> + return;
> + }
> +
> + msm_dp_display_enable_helper(dp_display, bridge->msm_dp_panel);
> +
> + _msm_dp_mst_bridge_pre_enable_part2(bridge, state);
> +
> + mutex_unlock(&dp_mst->mst_lock);
> +
> + drm_dbg_dp(dp_display->drm_dev, "conn:%d mode:%s fps:%d vcpi:%d slots:%d to %d\n",
> + DP_MST_CONN_ID(bridge), bridge->msm_dp_panel->msm_dp_mode.drm_mode.name,
> + drm_mode_vrefresh(&bridge->msm_dp_panel->msm_dp_mode.drm_mode),
> + mst_bridge_state->vcpi, mst_bridge_state->start_slot,
> + mst_bridge_state->start_slot + mst_bridge_state->num_slots);
> +}
> +
> +static void msm_dp_mst_bridge_atomic_disable(struct drm_bridge *drm_bridge,
> + struct drm_atomic_state *state)
> +{
> + struct msm_dp_mst_bridge *bridge;
> + struct msm_dp *dp_display;
> + struct msm_dp_mst *mst;
> +
> + if (!drm_bridge) {
> + DRM_ERROR("Invalid params\n");
> + return;
> + }
> +
> + bridge = to_msm_dp_mst_bridge(drm_bridge);
> + if (!bridge->connector) {
> + DRM_ERROR("Invalid connector\n");
> + return;
> + }
> +
> + dp_display = bridge->display;
> + mst = dp_display->msm_dp_mst;
> +
> + mutex_lock(&mst->mst_lock);
> +
> + msm_dp_mst_bridge_pre_disable_part1(bridge, state);
> +
> + msm_dp_display_disable_helper(dp_display, bridge->msm_dp_panel);
> +
> + drm_dp_check_act_status(&mst->mst_mgr);
> +
> + mutex_unlock(&mst->mst_lock);
> +
> + drm_dbg_dp(dp_display->drm_dev, "mst bridge:%d conn:%d disable complete\n", bridge->id,
> + DP_MST_CONN_ID(bridge));
> +}
> +
> +static void msm_dp_mst_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
> + struct drm_atomic_state *state)
> +{
> + int conn = 0;
> + struct msm_dp_mst_bridge *bridge;
> + struct msm_dp *dp_display;
> + struct msm_dp_mst *mst;
> +
> + if (!drm_bridge) {
> + DRM_ERROR("Invalid params\n");
> + return;
> + }
> +
> + bridge = to_msm_dp_mst_bridge(drm_bridge);
> + if (!bridge->connector) {
> + DRM_ERROR("Invalid connector\n");
> + return;
> + }
> +
> + conn = DP_MST_CONN_ID(bridge);
> +
> + dp_display = bridge->display;
> + mst = dp_display->msm_dp_mst;
> +
> + mutex_lock(&mst->mst_lock);
> +
> + msm_dp_display_atomic_post_disable_helper(dp_display, bridge->msm_dp_panel);
> +
> + if (!dp_display->mst_active)
> + msm_dp_display_unprepare(dp_display);
> +
> + bridge->connector = NULL;
> + bridge->msm_dp_panel = NULL;
> +
> + mutex_unlock(&mst->mst_lock);
> +
> + drm_dbg_dp(dp_display->drm_dev, "mst bridge:%d conn:%d post disable complete\n",
> + bridge->id, conn);
> +}
> +
> +static int msm_dp_mst_bridge_atomic_check(struct drm_bridge *drm_bridge,
> + struct drm_bridge_state *bridge_state,
> + struct drm_crtc_state *crtc_state,
> + struct drm_connector_state *conn_state)
> +{
> + struct drm_atomic_state *state = crtc_state->state;
> + struct drm_connector *connector = conn_state->connector;
> + struct drm_dp_mst_topology_state *mst_state;
> + struct msm_dp_mst_connector *mst_conn;
> + struct msm_dp *dp_display;
> + struct msm_dp_mst *mst;
> + int rc = 0, pbn, slots;
> + u32 bpp;
> +
> + if (!drm_atomic_crtc_needs_modeset(crtc_state) || !crtc_state->enable) {
> + return 0;
> + }
> +
> + mst_conn = to_msm_dp_mst_connector(connector);
> + dp_display = mst_conn->msm_dp;
> + mst = dp_display->msm_dp_mst;
> +
> + bpp = connector->display_info.bpc * 3;
> +
> + if (!bpp)
> + bpp = 24;
> +
> + pbn = drm_dp_calc_pbn_mode(crtc_state->mode.clock, bpp << 4);
> +
> + mst_state = to_drm_dp_mst_topology_state(mst->mst_mgr.base.state);
> + if (IS_ERR(mst_state))
> + return PTR_ERR(mst_state);
> +
> + if (!dfixed_trunc(mst_state->pbn_div)) {
> + mst_state->pbn_div =
> + drm_dp_get_vc_payload_bw(mst_conn->dp_panel->link_info.rate,
> + mst_conn->dp_panel->link_info.num_lanes);
> + }
> +
> + slots = drm_dp_atomic_find_time_slots(state, &mst->mst_mgr, mst_conn->mst_port, pbn);
> +
> + drm_dbg_dp(dp_display->drm_dev, "add slots, conn:%d pbn:%d slots:%d rc:%d\n",
> + connector->base.id, pbn, slots, rc);
> +
> + return 0;
> +}
> +
> +static void msm_dp_mst_bridge_mode_set(struct drm_bridge *drm_bridge,
> + const struct drm_display_mode *mode,
> + const struct drm_display_mode *adjusted_mode)
It's clearly defined as deprecated, not to be used by new drivers. Any
reason for not following it?
> +{
> + struct msm_dp_mst_bridge *bridge;
> + struct msm_dp_mst_bridge_state *mst_bridge_state;
> + struct msm_dp *dp_display;
> + struct msm_dp_panel *msm_dp_panel;
> +
> + if (!drm_bridge || !mode || !adjusted_mode) {
Ugh. No overprotective coding.
> + DRM_ERROR("Invalid params\n");
> + return;
> + }
> +
> + bridge = to_msm_dp_mst_bridge(drm_bridge);
> +
> + mst_bridge_state = to_msm_dp_mst_bridge_state(bridge);
> + bridge->connector = mst_bridge_state->connector;
> + bridge->msm_dp_panel = mst_bridge_state->msm_dp_panel;
Why? All important functions should have access to bridge state and thus state fields.
> +
> + msm_dp_panel = bridge->msm_dp_panel;
> + dp_display = bridge->display;
> +
> + msm_dp_display_mode_set_helper(dp_display, mode, adjusted_mode, bridge->msm_dp_panel);
> + msm_dp_panel->pbn = drm_dp_calc_pbn_mode(msm_dp_panel->msm_dp_mode.drm_mode.clock,
> + (msm_dp_panel->msm_dp_mode.bpp << 4));
> + drm_dbg_dp(dp_display->drm_dev, "mst bridge:%d conn:%d mode set complete %s\n", bridge->id,
> + DP_MST_CONN_ID(bridge), mode->name);
> +}
> +
> +/* DP MST Bridge APIs */
> +static const struct drm_bridge_funcs msm_dp_mst_bridge_ops = {
> + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> + .atomic_reset = drm_atomic_helper_bridge_reset,
> + .atomic_pre_enable = msm_dp_mst_bridge_atomic_pre_enable,
> + .atomic_disable = msm_dp_mst_bridge_atomic_disable,
> + .atomic_post_disable = msm_dp_mst_bridge_atomic_post_disable,
> + .atomic_check = msm_dp_mst_bridge_atomic_check,
> + .mode_set = msm_dp_mst_bridge_mode_set,
> +};
> +
> +int msm_dp_mst_drm_bridge_init(struct msm_dp *dp_display, struct drm_encoder *encoder)
> +{
> + int rc = 0;
> + struct msm_dp_mst_bridge *bridge = NULL;
> + struct msm_dp_mst_bridge_state *mst_bridge_state;
> + struct drm_device *dev;
> + struct msm_dp_mst *mst = dp_display->msm_dp_mst;
> + int i;
> +
> + for (i = 0; i < mst->max_streams; i++) {
> + if (!mst->mst_bridge[i]->in_use) {
> + bridge = mst->mst_bridge[i];
> + bridge->encoder = encoder;
> + bridge->in_use = true;
> + bridge->id = i;
> + break;
> + }
> + }
> +
> + if (i == mst->max_streams) {
> + DRM_ERROR("mst supports only %d bridges\n", i);
> + rc = -EACCES;
> + goto end;
> + }
> +
> + dev = dp_display->drm_dev;
> + bridge->display = dp_display;
> + bridge->base.funcs = &msm_dp_mst_bridge_ops;
> + bridge->base.encoder = encoder;
> + bridge->base.type = dp_display->connector_type;
> + bridge->base.ops = DRM_BRIDGE_OP_MODES;
> + drm_bridge_add(&bridge->base);
> +
> + rc = drm_bridge_attach(encoder, &bridge->base, NULL, 0);
> + if (rc) {
> + DRM_ERROR("failed to attach bridge, rc=%d\n", rc);
> + goto end;
> + }
> +
> + mst_bridge_state = kzalloc(sizeof(*mst_bridge_state), GFP_KERNEL);
> + if (!mst_bridge_state) {
> + rc = -ENOMEM;
> + goto end;
> + }
> +
> + drm_atomic_private_obj_init(dev, &bridge->obj,
> + &mst_bridge_state->base,
> + &msm_dp_mst_bridge_state_funcs);
Why do you register a separate object if you already have bridge and you
can use bridge's state?
> +
> + drm_dbg_dp(dp_display->drm_dev, "mst drm bridge init. bridge id:%d\n", i);
> +
> + return 0;
> +
> +end:
> + return rc;
> +}
> diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.h b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..d75731ca2e5870377026e8ad1057bdcc5f0d4c78
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
> @@ -0,0 +1,86 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
> + *
> + * Permission to use, copy, modify, distribute, and sell this software and its
> + * documentation for any purpose is hereby granted without fee, provided that
> + * the above copyright notice appear in all copies and that both that copyright
> + * notice and this permission notice appear in supporting documentation, and
> + * that the name of the copyright holders not be used in advertising or
> + * publicity pertaining to distribution of the software without specific,
> + * written prior permission. The copyright holders make no representations
> + * about the suitability of this software for any purpose. It is provided "as
> + * is" without express or implied warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
> + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
> + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
> + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
> + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
> + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
> + * OF THIS SOFTWARE.
Please check what you are sending. The SPDX header says GPL-2.0-only,
while the licence text is MIT. Why?
> + */
> +
> +#ifndef _DP_MST_DRM_H_
> +#define _DP_MST_DRM_H_
> +
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/errno.h>
> +#include <linux/version.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_fixed.h>
> +#include <drm/drm_connector.h>
> +#include <drm/display/drm_dp_helper.h>
> +#include <drm/display/drm_dp_mst_helper.h>
Are all those includes necessary here? Replace them with forward struct
definitions and move includes to the source file. Only keep those, which
are actually necessary for the contents of the header.
> +
> +#include "dp_panel.h"
> +#include "dp_display.h"
> +
> +struct msm_dp_mst_bridge {
> + struct drm_bridge base;
> + struct drm_private_obj obj;
> + u32 id;
> +
> + bool in_use;
What does it mean? In use currently for one of outputs?
> +
> + struct msm_dp *display;
> + struct drm_encoder *encoder;
> +
> + struct drm_connector *connector;
Why do you have connector both as a part of the bridge and bridge state?
Please describe design decisions in the commit mesage or as comments.
> + struct msm_dp_panel *msm_dp_panel;
> +};
> +
> +struct msm_dp_mst_bridge_state {
> + struct drm_private_state base;
> + struct drm_connector *connector;
> + struct msm_dp_panel *msm_dp_panel;
> +
> + int vcpi;
> + int pbn;
> + int num_slots;
> + int start_slot;
I'd definitely prefer to have payload pointer here, if that's also a
part of the state.
> +};
> +
> +struct msm_dp_mst {
> + struct drm_dp_mst_topology_mgr mst_mgr;
> + struct msm_dp_mst_bridge *mst_bridge[DP_STREAM_MAX];
> + struct msm_dp *msm_dp;
> + u32 max_streams;
> + struct mutex mst_lock;
> +};
> +
> +struct msm_dp_mst_connector {
> + struct drm_connector connector;
> + struct drm_dp_mst_port *mst_port;
> + struct msm_dp *msm_dp;
> + struct msm_dp_panel *dp_panel;
> +};
> +
> +int msm_dp_mst_drm_bridge_init(struct msm_dp *dp, struct drm_encoder *encoder);
> +
> +#endif /* _DP_MST_DRM_H_ */
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 29/38] drm/msm/dp: add MST atomic check to msm_atomic_check()
2025-08-25 14:16 ` [PATCH v3 29/38] drm/msm/dp: add MST atomic check to msm_atomic_check() Yongxing Mou
@ 2025-08-26 17:44 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-26 17:44 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel
On Mon, Aug 25, 2025 at 10:16:15PM +0800, Yongxing Mou wrote:
> Add a call to drm_dp_mst_atomic_check() within msm_atomic_check() to ensure
> proper validation of atomic state changes related to DisplayPort
> Multi-Stream Transport (MST).
>
> This complements the existing drm_atomic_helper_check() and allows the MST
> framework to perform its own consistency checks during atomic updates,
> such as validating connector states and topology changes.
>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/msm_atomic.c | 7 ++++++-
> 1 file changed, 6 insertions(+), 1 deletion(-)
>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 30/38] drm/msm/dp: add connector abstraction for DP MST
2025-08-25 14:16 ` [PATCH v3 30/38] drm/msm/dp: add connector abstraction for DP MST Yongxing Mou
@ 2025-08-26 18:31 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-26 18:31 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:16PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> Add connector abstraction for the DP MST. Each MST encoder
> is connected through a DRM bridge to a MST connector and each
> MST connector has a DP panel abstraction attached to it.
What's the functionality split between the connector and bridge? Please
explain it here. Do we really need a bridge if we have a non-trivial
connector implementation?
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_mst_drm.c | 391 +++++++++++++++++++++++++++++++++++-
> drivers/gpu/drm/msm/dp/dp_mst_drm.h | 3 +
> 2 files changed, 393 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> index 73de29136801ef5f45e0b2d09280fe113021b68c..b4f640134af544c77ab262d2cbe0b67e1e2e1b3a 100644
> --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> @@ -25,6 +25,8 @@
> * OF THIS SOFTWARE.
> */
>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_managed.h>
> #include "dp_mst_drm.h"
>
> #define to_msm_dp_mst_bridge(x) container_of((x), struct msm_dp_mst_bridge, base)
> @@ -525,7 +527,6 @@ int msm_dp_mst_drm_bridge_init(struct msm_dp *dp_display, struct drm_encoder *en
>
> dev = dp_display->drm_dev;
> bridge->display = dp_display;
> - bridge->base.funcs = &msm_dp_mst_bridge_ops;
> bridge->base.encoder = encoder;
> bridge->base.type = dp_display->connector_type;
> bridge->base.ops = DRM_BRIDGE_OP_MODES;
> @@ -554,3 +555,391 @@ int msm_dp_mst_drm_bridge_init(struct msm_dp *dp_display, struct drm_encoder *en
> end:
> return rc;
> }
> +
> +static struct msm_dp_mst_bridge_state *msm_dp_mst_br_priv_state(struct drm_atomic_state *st,
> + struct msm_dp_mst_bridge *bridge)
> +{
> + struct drm_device *dev = bridge->base.dev;
> + struct drm_private_state *obj_state = drm_atomic_get_private_obj_state(st, &bridge->obj);
> +
> + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
> +
> + return to_msm_dp_mst_bridge_state_priv(obj_state);
> +}
> +
> +/* DP MST Connector OPs */
> +static int
> +msm_dp_mst_connector_detect(struct drm_connector *connector,
> + struct drm_modeset_acquire_ctx *ctx,
> + bool force)
> +{
> + struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(connector);
> + struct msm_dp *dp_display = mst_conn->msm_dp;
> + struct msm_dp_mst *mst = dp_display->msm_dp_mst;
> + enum drm_connector_status status = connector_status_disconnected;
> +
> + if (drm_connector_is_unregistered(&mst_conn->connector))
> + return status;
Can detect be called for unregistered connector?
> +
> + if (dp_display->link_ready && dp_display->mst_active)
> + status = drm_dp_mst_detect_port(connector,
> + ctx, &mst->mst_mgr, mst_conn->mst_port);
I think this should be wrapped in the pm_runtime calls.
> +
> + drm_dbg_dp(dp_display->drm_dev, "conn:%d status:%d\n", connector->base.id, status);
Do we need this?
> +
> + return status;
> +}
> +
> +static int msm_dp_mst_connector_get_modes(struct drm_connector *connector)
> +{
> + struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(connector);
> + struct msm_dp *dp_display = mst_conn->msm_dp;
> + struct msm_dp_mst *mst = dp_display->msm_dp_mst;
> + const struct drm_edid *drm_edid;
> +
> + if (drm_connector_is_unregistered(&mst_conn->connector))
> + return drm_edid_connector_update(connector, NULL);
Is there a need for this? I don't see a check in nouveau code.
> +
> + drm_edid = drm_dp_mst_edid_read(connector, &mst->mst_mgr, mst_conn->mst_port);
> + drm_edid_connector_update(connector, drm_edid);
> +
> + return drm_edid_connector_add_modes(connector);
> +}
> +
> +static enum drm_mode_status msm_dp_mst_connector_mode_valid(struct drm_connector *connector,
> + const struct drm_display_mode *mode)
> +{
> + struct msm_dp_mst_connector *mst_conn;
> + struct msm_dp *dp_display;
> + struct drm_dp_mst_port *mst_port;
> + struct msm_dp_panel *dp_panel;
> + struct msm_dp_mst *mst;
> + struct msm_dp_mst_bridge_state *mst_bridge_state;
> + u16 full_pbn, required_pbn;
> + int i, active_enc_cnt = 0;
> +
> + if (drm_connector_is_unregistered(connector))
> + return 0;
> +
> + mst_conn = to_msm_dp_mst_connector(connector);
> + dp_display = mst_conn->msm_dp;
> + mst = dp_display->msm_dp_mst;
> + mst_port = mst_conn->mst_port;
> + dp_panel = mst_conn->dp_panel;
> +
> + if (!dp_panel || !mst_port)
> + return MODE_ERROR;
> +
> + for (i = 0; i < mst->max_streams; i++) {
> + mst_bridge_state = to_msm_dp_mst_bridge_state(mst->mst_bridge[i]);
> + if (mst_bridge_state->connector &&
> + mst_bridge_state->connector != connector)
> + active_enc_cnt++;
> + }
> +
> + if (active_enc_cnt < DP_STREAM_MAX)
> + full_pbn = mst_port->full_pbn;
> + else {
> + DRM_ERROR("all MST streams are active\n");
> + return MODE_BAD;
And if the stream becomes unused, who will call the mode_valid? This
callback should validate if the mode can be enabled at all, not taking
care about other MST streams, connectors, etc. If the user overcommits,
e.g. by selecting 4 8K modes, then atomic_check() will fail, but it
still should be possible to disable all other connectors and get the max
mode supported here.
> + }
> +
> + required_pbn = drm_dp_calc_pbn_mode(mode->clock, (connector->display_info.bpc * 3) << 4);
You should not be using connector's BPC here. It can be lowered to fit
the mode. It should be (6 * 3) << 4
> +
> + if (required_pbn > full_pbn) {
> + drm_dbg_dp(dp_display->drm_dev, "mode:%s not supported. pbn %d vs %d\n",
> + mode->name, required_pbn, full_pbn);
> + return MODE_BAD;
MODE_CLOCK_HIGH
> + }
> +
> + return msm_dp_display_mode_valid(dp_display, &dp_panel->connector->display_info, mode);
> +}
> +
> +static struct drm_encoder *
> +msm_dp_mst_atomic_best_encoder(struct drm_connector *connector, struct drm_atomic_state *state)
> +{
> + struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(connector);
> + struct msm_dp *dp_display = mst_conn->msm_dp;
> + struct msm_dp_mst *mst = dp_display->msm_dp_mst;
> + struct drm_encoder *enc = NULL;
> + struct msm_dp_mst_bridge_state *mst_bridge_state;
> + u32 i;
> + struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(state,
> + connector);
> +
> + if (conn_state && conn_state->best_encoder)
> + return conn_state->best_encoder;
> +
> + for (i = 0; i < mst->max_streams; i++) {
> + mst_bridge_state = msm_dp_mst_br_priv_state(state, mst->mst_bridge[i]);
> + if (IS_ERR(mst_bridge_state))
> + goto end;
> +
> + if (mst_bridge_state->connector == connector) {
> + enc = mst->mst_bridge[i]->encoder;
> + goto end;
> + }
> + }
> +
> + for (i = 0; i < mst->max_streams; i++) {
> + mst_bridge_state = msm_dp_mst_br_priv_state(state, mst->mst_bridge[i]);
> +
> + if (!mst_bridge_state->connector) {
> + mst_bridge_state->connector = connector;
> + mst_bridge_state->msm_dp_panel = mst_conn->dp_panel;
> + enc = mst->mst_bridge[i]->encoder;
> + break;
> + }
> + }
> +
> +end:
> + if (enc)
> + drm_dbg_dp(dp_display->drm_dev, "mst connector:%d atomic best encoder:%d\n",
> + connector->base.id, i);
> + else
> + drm_dbg_dp(dp_display->drm_dev, "mst connector:%d atomic best encoder failed\n",
> + connector->base.id);
> +
> + return enc;
> +}
> +
> +static int msm_dp_mst_connector_atomic_check(struct drm_connector *connector,
> + struct drm_atomic_state *state)
> +{
> + int rc = 0, slots;
> + struct drm_connector_state *old_conn_state;
> + struct drm_connector_state *new_conn_state;
> + struct drm_crtc *old_crtc;
> + struct drm_crtc_state *crtc_state;
> + struct msm_dp_mst_bridge *bridge;
> + struct msm_dp_mst_bridge_state *mst_bridge_state;
> + struct drm_bridge *drm_bridge;
> + struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(connector);
> + struct msm_dp *dp_display = mst_conn->msm_dp;
> + struct msm_dp_mst *mst = dp_display->msm_dp_mst;
> + struct drm_dp_mst_atomic_payload *payload;
> + struct drm_dp_mst_topology_state *mst_state;
> +
> + if (!state)
> + return rc;
> +
> + new_conn_state = drm_atomic_get_new_connector_state(state, connector);
> + if (!new_conn_state)
> + return rc;
> +
> + old_conn_state = drm_atomic_get_old_connector_state(state, connector);
> + if (!old_conn_state)
> + goto end;
> +
> + old_crtc = old_conn_state->crtc;
> + if (!old_crtc)
> + goto end;
> +
> + crtc_state = drm_atomic_get_new_crtc_state(state, old_crtc);
> +
> + /* attempt to release vcpi slots on a modeset change for crtc state */
> + if (drm_atomic_crtc_needs_modeset(crtc_state)) {
> + if (WARN_ON(!old_conn_state->best_encoder)) {
> + rc = -EINVAL;
> + goto end;
> + }
> +
> + drm_bridge = drm_bridge_chain_get_first_bridge(old_conn_state->best_encoder);
Why do we have it here rather than in bridge's atomic_check?
> + if (WARN_ON(!drm_bridge)) {
> + rc = -EINVAL;
> + goto end;
> + }
> + bridge = to_msm_dp_mst_bridge(drm_bridge);
> +
> + mst_bridge_state = msm_dp_mst_br_priv_state(state, bridge);
> +
> + mst_state = to_drm_dp_mst_topology_state(mst->mst_mgr.base.state);
> +
> + payload = drm_atomic_get_mst_payload_state(mst_state, mst_conn->mst_port);
> +
> + slots = payload->time_slots;
> + if (slots > 0) {
> + rc = drm_dp_atomic_release_time_slots(state,
> + &mst->mst_mgr,
> + mst_conn->mst_port);
> + if (rc) {
> + DRM_ERROR("failed releasing %d vcpi slots %d\n", slots, rc);
> + goto end;
> + }
> + }
> +
> + if (!new_conn_state->crtc) {
> + /* for cases where crtc is not disabled the slots are not
> + * freed by drm_dp_atomic_release_time_slots. this results
> + * in subsequent atomic_check failing since internal slots
> + * were freed but not the DP MST mgr's
> + */
> + mst_bridge_state->num_slots = 0;
> + mst_bridge_state->connector = NULL;
> + mst_bridge_state->msm_dp_panel = NULL;
> +
> + drm_dbg_dp(dp_display->drm_dev, "clear best encoder: %d\n", bridge->id);
> + }
> + }
> +
> +end:
> + drm_dbg_dp(dp_display->drm_dev, "mst connector:%d atomic check ret %d\n",
> + connector->base.id, rc);
> + return rc;
> +}
> +
> +static void dp_mst_connector_destroy(struct drm_connector *connector)
> +{
> + struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(connector);
> +
> + drm_connector_cleanup(connector);
> + drm_dp_mst_put_port_malloc(mst_conn->mst_port);
> + kfree(mst_conn);
> +}
> +
> +/* DRM MST callbacks */
> +static const struct drm_connector_helper_funcs msm_dp_drm_mst_connector_helper_funcs = {
> + .get_modes = msm_dp_mst_connector_get_modes,
> + .detect_ctx = msm_dp_mst_connector_detect,
> + .mode_valid = msm_dp_mst_connector_mode_valid,
> + .atomic_best_encoder = msm_dp_mst_atomic_best_encoder,
> + .atomic_check = msm_dp_mst_connector_atomic_check,
> +};
> +
> +static const struct drm_connector_funcs msm_dp_drm_mst_connector_funcs = {
> + .reset = drm_atomic_helper_connector_reset,
> + .destroy = dp_mst_connector_destroy,
> + .fill_modes = drm_helper_probe_single_connector_modes,
> + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static struct drm_connector *
> +msm_dp_mst_add_connector(struct drm_dp_mst_topology_mgr *mgr,
> + struct drm_dp_mst_port *port, const char *pathprop)
> +{
> + struct msm_dp_mst *dp_mst;
> + struct drm_device *dev;
> + struct msm_dp *dp_display;
> + struct msm_dp_mst_connector *mst_conn;
> + struct drm_connector *connector;
> + int rc, i;
> +
> + dp_mst = container_of(mgr, struct msm_dp_mst, mst_mgr);
> +
> + dp_display = dp_mst->msm_dp;
> + dev = dp_display->drm_dev;
> +
> + mst_conn = kzalloc(sizeof(*mst_conn), GFP_KERNEL);
> +
> + if (!mst_conn)
> + return NULL;
> +
> + drm_modeset_lock_all(dev);
> +
> + connector = &mst_conn->connector;
> + rc = drm_connector_dynamic_init(dev, connector,
> + &msm_dp_drm_mst_connector_funcs,
> + DRM_MODE_CONNECTOR_DisplayPort, NULL);
> + if (rc) {
> + kfree(mst_conn);
> + drm_modeset_unlock_all(dev);
> + return NULL;
> + }
> +
> + mst_conn->dp_panel = msm_dp_display_get_panel(dp_display);
> + if (!mst_conn->dp_panel) {
> + DRM_ERROR("failed to get dp_panel for connector\n");
> + kfree(mst_conn);
> + drm_modeset_unlock_all(dev);
> + return NULL;
> + }
> +
> + mst_conn->dp_panel->connector = connector;
> + mst_conn->msm_dp = dp_display;
> +
> + drm_connector_helper_add(connector, &msm_dp_drm_mst_connector_helper_funcs);
> +
> + if (connector->funcs->reset)
> + connector->funcs->reset(connector);
> +
> + /* add all encoders as possible encoders */
> + for (i = 0; i < dp_mst->max_streams; i++) {
> + rc = drm_connector_attach_encoder(connector, dp_mst->mst_bridge[i]->encoder);
> +
> + if (rc) {
> + DRM_ERROR("failed to attach encoder to connector, %d\n", rc);
> + kfree(mst_conn);
> + drm_modeset_unlock_all(dev);
> + return NULL;
> + }
> + }
> +
> + mst_conn->mst_port = port;
> + drm_dp_mst_get_port_malloc(mst_conn->mst_port);
> +
> + drm_object_attach_property(&connector->base,
> + dev->mode_config.path_property, 0);
Where do we set the property then?
> + drm_object_attach_property(&connector->base,
> + dev->mode_config.tile_property, 0);
> +
> + drm_modeset_unlock_all(dev);
> +
> + drm_dbg_dp(dp_display->drm_dev, "add MST connector id:%d\n", connector->base.id);
> +
> + return connector;
> +}
> +
> +static const struct drm_dp_mst_topology_cbs msm_dp_mst_drm_cbs = {
> + .add_connector = msm_dp_mst_add_connector,
No .poll_hpd_irq ?
> +};
> +
> +int msm_dp_mst_init(struct msm_dp *dp_display, u32 max_streams, struct drm_dp_aux *drm_aux)
> +{
> + struct drm_device *dev;
> + int conn_base_id = 0;
> + int ret;
> + struct msm_dp_mst *msm_dp_mst;
> +
> + if (!dp_display) {
> + DRM_ERROR("invalid params\n");
> + return 0;
> + }
Can't we just trust the driver?
> +
> + dev = dp_display->drm_dev;
> +
> + msm_dp_mst = devm_kzalloc(dev->dev, sizeof(*msm_dp_mst), GFP_KERNEL);
> + if (!msm_dp_mst)
> + return -ENOMEM;
> +
> + memset(&msm_dp_mst->mst_mgr, 0, sizeof(msm_dp_mst->mst_mgr));
> + msm_dp_mst->mst_mgr.cbs = &msm_dp_mst_drm_cbs;
> + conn_base_id = dp_display->connector->base.id;
> + msm_dp_mst->msm_dp = dp_display;
> + msm_dp_mst->max_streams = max_streams;
> +
> + for (int i = 0; i < DP_STREAM_MAX; i++) {
> + msm_dp_mst->mst_bridge[i] = devm_drm_bridge_alloc(dev->dev,
> + struct msm_dp_mst_bridge, base,
> + &msm_dp_mst_bridge_ops);
This should have been a part of the previous patch.
> + }
> +
> + msm_dp_mst->dp_aux = drm_aux;
> +
> + ret = drm_dp_mst_topology_mgr_init(&msm_dp_mst->mst_mgr, dev,
> + drm_aux,
> + MAX_DPCD_TRANSACTION_BYTES,
> + max_streams,
> + conn_base_id);
> + if (ret) {
> + DRM_ERROR("DP DRM MST topology manager init failed\n");
> + return ret;
> + }
> +
> + dp_display->msm_dp_mst = msm_dp_mst;
> +
> + mutex_init(&msm_dp_mst->mst_lock);
> +
> + drm_dbg_dp(dp_display->drm_dev, "DP DRM MST topology manager init completed\n");
Why do we need so many debug messages? They make the log overspammed and
hard to follow.
> + return ret;
> +}
> diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.h b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
> index d75731ca2e5870377026e8ad1057bdcc5f0d4c78..1484fabd92ad0075eac5369aac8ca462acbd3eda 100644
> --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.h
> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
> @@ -70,6 +70,7 @@ struct msm_dp_mst {
> struct drm_dp_mst_topology_mgr mst_mgr;
> struct msm_dp_mst_bridge *mst_bridge[DP_STREAM_MAX];
> struct msm_dp *msm_dp;
> + struct drm_dp_aux *dp_aux;
> u32 max_streams;
> struct mutex mst_lock;
> };
> @@ -83,4 +84,6 @@ struct msm_dp_mst_connector {
>
> int msm_dp_mst_drm_bridge_init(struct msm_dp *dp, struct drm_encoder *encoder);
>
> +int msm_dp_mst_init(struct msm_dp *dp_display, u32 max_streams, struct drm_dp_aux *drm_aux);
> +
> #endif /* _DP_MST_DRM_H_ */
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 31/38] drm/msm/dp: add HPD callback for dp MST
2025-08-25 14:16 ` [PATCH v3 31/38] drm/msm/dp: add HPD callback for dp MST Yongxing Mou
@ 2025-08-26 18:40 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-26 18:40 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:17PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> Add HPD callback for the MST module which shall be invoked from the
> dp_display's HPD handler to perform MST specific operations in case
> of HPD. In MST case, route the HPD messages to MST module.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_display.c | 15 ++++++++++++---
> drivers/gpu/drm/msm/dp/dp_mst_drm.c | 34 ++++++++++++++++++++++++++++++++++
> drivers/gpu/drm/msm/dp/dp_mst_drm.h | 2 ++
> 3 files changed, 48 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index abcab3ed43b6da5ef898355cf9b7561cd9fe0404..59720e1ad4b1193e33a4fc6aad0c401eaf9cbec8 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -500,9 +500,16 @@ static int msm_dp_display_handle_irq_hpd(struct msm_dp_display_private *dp)
>
> static int msm_dp_display_usbpd_attention_cb(struct device *dev)
> {
> - int rc = 0;
> - u32 sink_request;
> struct msm_dp_display_private *dp = dev_get_dp_display_private(dev);
> + struct msm_dp *msm_dp_display = &dp->msm_dp_display;
> + u32 sink_request;
> + int rc = 0;
> +
> + if (msm_dp_display->mst_active) {
> + if (msm_dp_aux_is_link_connected(dp->aux) != ISR_DISCONNECTED)
> + msm_dp_mst_display_hpd_irq(&dp->msm_dp_display);
> + return 0;
> + }
>
> /* check for any test request issued by sink */
> rc = msm_dp_link_process_request(dp->link);
> @@ -1129,8 +1136,10 @@ static irqreturn_t msm_dp_display_irq_thread(int irq, void *dev_id)
> if (hpd_isr_status & DP_DP_HPD_UNPLUG_INT_MASK)
> msm_dp_display_send_hpd_notification(dp, false);
>
> - if (hpd_isr_status & DP_DP_IRQ_HPD_INT_MASK)
> + if (hpd_isr_status & DP_DP_IRQ_HPD_INT_MASK) {
> msm_dp_display_send_hpd_notification(dp, true);
> + msm_dp_irq_hpd_handle(dp, 0);
Why is it a part of this patch?? It has nothing to do with MST.
> + }
>
> ret = IRQ_HANDLED;
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> index b4f640134af544c77ab262d2cbe0b67e1e2e1b3a..331d08854049d9c74d49aa231f3507539986099e 100644
> --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> @@ -567,6 +567,40 @@ static struct msm_dp_mst_bridge_state *msm_dp_mst_br_priv_state(struct drm_atomi
> return to_msm_dp_mst_bridge_state_priv(obj_state);
> }
>
> +/* DP MST HPD IRQ callback */
> +void msm_dp_mst_display_hpd_irq(struct msm_dp *dp_display)
> +{
> + int rc;
> + struct msm_dp_mst *mst = dp_display->msm_dp_mst;
> + u8 ack[8] = {};
> + u8 esi[4];
> + unsigned int esi_res = DP_SINK_COUNT_ESI + 1;
> + bool handled;
> +
> + rc = drm_dp_dpcd_read(mst->dp_aux, DP_SINK_COUNT_ESI,
> + esi, 4);
> + if (rc != 4) {
> + DRM_ERROR("dpcd sink status read failed, rlen=%d\n", rc);
It's DPCD, not dpcd.
> + return;
> + }
> +
> + drm_dbg_dp(dp_display->drm_dev, "mst irq: esi1[0x%x] esi2[0x%x] esi3[%x]\n",
> + esi[1], esi[2], esi[3]);
> +
> + rc = drm_dp_mst_hpd_irq_handle_event(&mst->mst_mgr, esi, ack, &handled);
> +
> + /* ack the request */
> + if (handled) {
> + rc = drm_dp_dpcd_writeb(mst->dp_aux, esi_res, ack[1]);
> +
> + if (rc != 1)
No empty space, drm_dp_dpcd_write_byte().
> + DRM_ERROR("dpcd esi_res failed. rc=%d\n", rc);
> +
> + drm_dp_mst_hpd_irq_send_new_request(&mst->mst_mgr);
> + }
> + drm_dbg_dp(dp_display->drm_dev, "mst display hpd_irq handled:%d rc:%d\n", handled, rc);
> +}
> +
> /* DP MST Connector OPs */
> static int
> msm_dp_mst_connector_detect(struct drm_connector *connector,
> diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.h b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
> index 1484fabd92ad0075eac5369aac8ca462acbd3eda..5e1b4db8aea4506b0e1cc1cc68980dd617d3f72a 100644
> --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.h
> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
> @@ -86,4 +86,6 @@ int msm_dp_mst_drm_bridge_init(struct msm_dp *dp, struct drm_encoder *encoder);
>
> int msm_dp_mst_init(struct msm_dp *dp_display, u32 max_streams, struct drm_dp_aux *drm_aux);
>
> +void msm_dp_mst_display_hpd_irq(struct msm_dp *dp_display);
> +
> #endif /* _DP_MST_DRM_H_ */
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 32/38] drm/msm/dp: propagate MST state changes to dp mst module
2025-08-25 14:16 ` [PATCH v3 32/38] drm/msm/dp: propagate MST state changes to dp mst module Yongxing Mou
@ 2025-08-26 18:43 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-26 18:43 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:18PM +0800, Yongxing Mou wrote:
> Introduce APIs to update the MST state change to MST framework when
> device is plugged/unplugged.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_display.c | 10 +++++++++-
> drivers/gpu/drm/msm/dp/dp_mst_drm.c | 15 +++++++++++++++
> drivers/gpu/drm/msm/dp/dp_mst_drm.h | 1 +
> 3 files changed, 25 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 59720e1ad4b1193e33a4fc6aad0c401eaf9cbec8..909c84a5c97f56138d0d62c5d856d2fd18d36b8c 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -28,6 +28,7 @@
> #include "dp_drm.h"
> #include "dp_audio.h"
> #include "dp_debug.h"
> +#include "dp_mst_drm.h"
>
> static bool psr_enabled = false;
> module_param(psr_enabled, bool, 0);
> @@ -269,7 +270,6 @@ static int msm_dp_display_send_hpd_notification(struct msm_dp_display_private *d
> dp->panel->video_test = false;
> }
>
> -
Unrelated
> drm_dbg_dp(dp->drm_dev, "type=%d hpd=%d\n",
> dp->msm_dp_display.connector_type, hpd);
>
> @@ -386,6 +386,9 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
>
> msm_dp_link_reset_phy_params_vx_px(dp->link);
>
> + if (dp->msm_dp_display.mst_active)
> + msm_dp_mst_display_set_mgr_state(&dp->msm_dp_display, true);
I'd say, this should be a part of the previous patch.
> +
> if (!dp->msm_dp_display.internal_hpd)
> msm_dp_display_send_hpd_notification(dp, true);
>
> @@ -608,6 +611,11 @@ static int msm_dp_hpd_unplug_handle(struct msm_dp_display_private *dp, u32 data)
> if (!dp->msm_dp_display.internal_hpd)
> msm_dp_display_send_hpd_notification(dp, false);
>
> + if (dp->msm_dp_display.mst_active) {
> + msm_dp_mst_display_set_mgr_state(&dp->msm_dp_display, false);
> + dp->msm_dp_display.mst_active = false;
> + }
> +
> /* signal the disconnect event early to ensure proper teardown */
> msm_dp_display_handle_plugged_change(&dp->msm_dp_display, false);
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> index 331d08854049d9c74d49aa231f3507539986099e..ca654b1963467c8220dd7ee073f25216455d0490 100644
> --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> @@ -924,6 +924,21 @@ msm_dp_mst_add_connector(struct drm_dp_mst_topology_mgr *mgr,
> return connector;
> }
>
> +int msm_dp_mst_display_set_mgr_state(struct msm_dp *dp_display, bool state)
> +{
> + int rc;
> + struct msm_dp_mst *mst = dp_display->msm_dp_mst;
Reverse X-mas
> +
> + rc = drm_dp_mst_topology_mgr_set_mst(&mst->mst_mgr, state);
> + if (rc < 0) {
> + DRM_ERROR("failed to set topology mgr state to %d. rc %d\n",
> + state, rc);
> + }
> +
> + drm_dbg_dp(dp_display->drm_dev, "dp_mst_display_set_mgr_state state:%d\n", state);
> + return rc;
> +}
> +
> static const struct drm_dp_mst_topology_cbs msm_dp_mst_drm_cbs = {
> .add_connector = msm_dp_mst_add_connector,
> };
> diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.h b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
> index 5e1b4db8aea4506b0e1cc1cc68980dd617d3f72a..8fe6cbbe741da4abb232256b3a15ba6b16ca4f3e 100644
> --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.h
> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
> @@ -87,5 +87,6 @@ int msm_dp_mst_drm_bridge_init(struct msm_dp *dp, struct drm_encoder *encoder);
> int msm_dp_mst_init(struct msm_dp *dp_display, u32 max_streams, struct drm_dp_aux *drm_aux);
>
> void msm_dp_mst_display_hpd_irq(struct msm_dp *dp_display);
> +int msm_dp_mst_display_set_mgr_state(struct msm_dp *dp_display, bool state);
>
> #endif /* _DP_MST_DRM_H_ */
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 33/38] drm/msm: add support for MST non-blocking commits
2025-08-25 14:16 ` [PATCH v3 33/38] drm/msm: add support for MST non-blocking commits Yongxing Mou
@ 2025-08-26 18:47 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-26 18:47 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:19PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> Integrate MST framework APIs with atomic_commit_setup() and
> atomic_commit_tail() to support non-blocking atomic commits
> for DisplayPort MST.
>
> This patch only applies to MST. For SST, non-blocking commits are
> already handled via commit_tail(), which internally calls
> drm_atomic_helper_wait_for_dependencies() in the DRM core.
I think this should be squashed into the the commit adding MST
atomic_check call.
Otherwise:
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/msm_atomic.c | 3 +++
> drivers/gpu/drm/msm/msm_kms.c | 2 ++
> 2 files changed, 5 insertions(+)
>
> diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
> index 3aa0020dec3a90b693ad3d4171cfcffc091aad4c..b1656fb456d54af11ba8a30d4971fface114c7a1 100644
> --- a/drivers/gpu/drm/msm/msm_atomic.c
> +++ b/drivers/gpu/drm/msm/msm_atomic.c
> @@ -4,6 +4,7 @@
> * Author: Rob Clark <robdclark@gmail.com>
> */
>
> +#include <drm/display/drm_dp_mst_helper.h>
> #include <drm/drm_atomic_uapi.h>
> #include <drm/drm_vblank.h>
> #include <drm/display/drm_dp_mst_helper.h>
> @@ -226,6 +227,8 @@ void msm_atomic_commit_tail(struct drm_atomic_state *state)
>
> trace_msm_atomic_commit_tail_start(async, crtc_mask);
>
> + drm_dp_mst_atomic_wait_for_dependencies(state);
> +
> kms->funcs->enable_commit(kms);
>
> /*
> diff --git a/drivers/gpu/drm/msm/msm_kms.c b/drivers/gpu/drm/msm/msm_kms.c
> index 6889f1c1e72121dcc735fa460ea04cdab11c6705..09776be1d3d854f4c77d7df3afa8d56f53639411 100644
> --- a/drivers/gpu/drm/msm/msm_kms.c
> +++ b/drivers/gpu/drm/msm/msm_kms.c
> @@ -10,6 +10,7 @@
> #include <linux/sched/mm.h>
> #include <uapi/linux/sched/types.h>
>
> +#include <drm/display/drm_dp_mst_helper.h>
> #include <drm/drm_drv.h>
> #include <drm/drm_mode_config.h>
> #include <drm/drm_vblank.h>
> @@ -29,6 +30,7 @@ static const struct drm_mode_config_funcs mode_config_funcs = {
>
> static const struct drm_mode_config_helper_funcs mode_config_helper_funcs = {
> .atomic_commit_tail = msm_atomic_commit_tail,
> + .atomic_commit_setup = drm_dp_mst_atomic_setup_commit,
> };
>
> static irqreturn_t msm_irq(int irq, void *arg)
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 34/38] drm/msm: initialize DRM MST encoders for DP controllers
2025-08-25 14:16 ` [PATCH v3 34/38] drm/msm: initialize DRM MST encoders for DP controllers Yongxing Mou
@ 2025-08-26 18:55 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-26 18:55 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:20PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> Initialize a DPMST encoder for each MST capable DP controller
> and the number of encoders it supports depends on the number
> of streams it supports.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 2 ++
> drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 23 ++++++++++++++++++++++-
> drivers/gpu/drm/msm/dp/dp_mst_drm.h | 2 --
> drivers/gpu/drm/msm/msm_drv.h | 13 +++++++++++++
> 4 files changed, 37 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
> index ca1ca2e51d7ead0eb34b27f3168e6bb06a71a11a..2eb4c39b111c1d8622e09e78ffafef017e28bbf6 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
> @@ -28,6 +28,7 @@
> * @h_tile_instance: Controller instance used per tile. Number of elements is
> * based on num_of_h_tiles
> * @is_cmd_mode Boolean to indicate if the CMD mode is requested
> + * @stream_id stream id for which the interface needs to be acquired
> * @vsync_source: Source of the TE signal for DSI CMD devices
> */
> struct msm_display_info {
> @@ -35,6 +36,7 @@ struct msm_display_info {
> uint32_t num_of_h_tiles;
> uint32_t h_tile_instance[MAX_H_TILES_PER_DISPLAY];
> bool is_cmd_mode;
> + int stream_id;
> enum dpu_vsync_source vsync_source;
> };
>
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> index 12dcb32b472497f9e59619db4e810abfbf610c7c..0b9d9207f4f69e0d0725ff265c624828b5816a8b 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> @@ -653,7 +653,8 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev,
> struct msm_display_info info;
> bool yuv_supported;
> int rc;
> - int i;
> + int i, stream_id;
> + int stream_cnt;
>
> for (i = 0; i < ARRAY_SIZE(priv->kms->dp); i++) {
> if (!priv->kms->dp[i])
> @@ -676,6 +677,26 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev,
> DPU_ERROR("modeset_init failed for DP, rc = %d\n", rc);
> return rc;
> }
> +
> + stream_cnt = msm_dp_get_mst_max_stream(priv->kms->dp[i]);
> +
> + if (stream_cnt > 1) {
> + for (stream_id = 0; stream_id < stream_cnt; stream_id++) {
> + info.stream_id = stream_id;
> + encoder = dpu_encoder_init(dev, DRM_MODE_ENCODER_DPMST, &info);
> + if (IS_ERR(encoder)) {
> + DPU_ERROR("encoder init failed for dp mst display\n");
> + return PTR_ERR(encoder);
> + }
> +
> + rc = msm_dp_mst_drm_bridge_init(priv->kms->dp[i], encoder);
This is an implementation detail. We should be asking to init MST, which
might or might not be a bridge.
> + if (rc) {
> + DPU_ERROR("dp mst bridge %d init failed, %d\n",
DP, MST, no 'bridge'.
> + stream_id, rc);
> + continue;
> + }
> + }
> + }
> }
>
> return 0;
> diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.h b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
> index 8fe6cbbe741da4abb232256b3a15ba6b16ca4f3e..d73e3f908439094532e88945ed4d41ed092051c9 100644
> --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.h
> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
> @@ -82,8 +82,6 @@ struct msm_dp_mst_connector {
> struct msm_dp_panel *dp_panel;
> };
>
> -int msm_dp_mst_drm_bridge_init(struct msm_dp *dp, struct drm_encoder *encoder);
> -
Squash this and the next chunks to the corresponding patches.
> int msm_dp_mst_init(struct msm_dp *dp_display, u32 max_streams, struct drm_dp_aux *drm_aux);
>
> void msm_dp_mst_display_hpd_irq(struct msm_dp *dp_display);
> diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
> index 985db9febd98e35dfed51d39dac1a522abe5a351..3e64ec7b7dbe1d1107e85def9aa80277131f40bf 100644
> --- a/drivers/gpu/drm/msm/msm_drv.h
> +++ b/drivers/gpu/drm/msm/msm_drv.h
> @@ -363,6 +363,9 @@ bool msm_dp_needs_periph_flush(const struct msm_dp *dp_display,
> const struct drm_display_mode *mode);
> bool msm_dp_wide_bus_available(const struct msm_dp *dp_display);
>
> +int msm_dp_get_mst_max_stream(struct msm_dp *dp_display);
> +int msm_dp_mst_drm_bridge_init(struct msm_dp *dp_display, struct drm_encoder *encoder);
> +
> #else
> static inline int __init msm_dp_register(void)
> {
> @@ -379,6 +382,16 @@ static inline int msm_dp_modeset_init(struct msm_dp *dp_display,
> return -EINVAL;
> }
>
> +static inline int msm_dp_get_mst_max_stream(struct msm_dp *dp_display)
> +{
> + return -EINVAL;
> +}
> +
> +static inline int msm_dp_mst_drm_bridge_init(struct msm_dp *dp_display, struct drm_encoder *encoder)
> +{
> + return -EINVAL;
> +}
> +
> static inline void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp_display)
> {
> }
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 35/38] drm/msm/dp: initialize dp_mst module for each DP MST controller
2025-08-25 14:16 ` [PATCH v3 35/38] drm/msm/dp: initialize dp_mst module for each DP MST controller Yongxing Mou
@ 2025-08-26 21:27 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-26 21:27 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:21PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> For each MST capable DP controller, initialize a dp_mst module to
> manage its DP MST operations. The DP MST module for each controller
> is the central entity to manage its topology related operations as
> well as interfacing with the rest of the DP driver.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 6 ++++++
> drivers/gpu/drm/msm/dp/dp_display.c | 9 +++++++++
> drivers/gpu/drm/msm/msm_drv.h | 6 ++++++
> 3 files changed, 21 insertions(+)
>
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> index 0b9d9207f4f69e0d0725ff265c624828b5816a8b..4036d3445946930e635401109ac4720ed2282c2f 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> @@ -681,6 +681,12 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev,
> stream_cnt = msm_dp_get_mst_max_stream(priv->kms->dp[i]);
>
> if (stream_cnt > 1) {
> + rc = msm_dp_mst_register(priv->kms->dp[i]);
> + if (rc) {
> + DPU_ERROR("dp_mst_init failed for DP, rc = %d\n", rc);
> + return rc;
> + }
> +
> for (stream_id = 0; stream_id < stream_cnt; stream_id++) {
> info.stream_id = stream_id;
> encoder = dpu_encoder_init(dev, DRM_MODE_ENCODER_DPMST, &info);
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 909c84a5c97f56138d0d62c5d856d2fd18d36b8c..897ef653b3cea08904bb3595e8ac10fd7fcf811f 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -1586,6 +1586,15 @@ int msm_dp_modeset_init(struct msm_dp *msm_dp_display, struct drm_device *dev,
> return 0;
> }
>
> +inline int msm_dp_mst_register(struct msm_dp *msm_dp_display)
Why is it inline? Also please move this to a corresponding patch (the
one which adds msm_dp_mst_init).
> +{
> + struct msm_dp_display_private *dp;
> +
> + dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
> +
> + return msm_dp_mst_init(msm_dp_display, dp->max_stream, dp->aux);
> +}
> +
> void msm_dp_display_atomic_prepare(struct msm_dp *msm_dp_display)
> {
> int rc = 0;
> diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
> index 3e64ec7b7dbe1d1107e85def9aa80277131f40bf..c46c88cf06598df996a17c23631570fda078b371 100644
> --- a/drivers/gpu/drm/msm/msm_drv.h
> +++ b/drivers/gpu/drm/msm/msm_drv.h
> @@ -365,6 +365,7 @@ bool msm_dp_wide_bus_available(const struct msm_dp *dp_display);
>
> int msm_dp_get_mst_max_stream(struct msm_dp *dp_display);
> int msm_dp_mst_drm_bridge_init(struct msm_dp *dp_display, struct drm_encoder *encoder);
> +int msm_dp_mst_register(struct msm_dp *dp_display);
>
> #else
> static inline int __init msm_dp_register(void)
> @@ -392,6 +393,11 @@ static inline int msm_dp_mst_drm_bridge_init(struct msm_dp *dp_display, struct d
> return -EINVAL;
> }
>
> +static inline int msm_dp_mst_register(struct msm_dp *dp_display)
> +{
> + return -EINVAL;
> +}
> +
> static inline void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp_display)
> {
> }
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 21/38] drm/msm/dp: Add support for sending VCPF packets in DP controller
2025-08-25 14:16 ` [PATCH v3 21/38] drm/msm/dp: Add support for sending VCPF packets in DP controller Yongxing Mou
@ 2025-08-26 21:28 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-26 21:28 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:07PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> The VC Payload Fill (VCPF) sequence is inserted by the DP controller
> when stream symbols are absent, typically before a stream is disabled.
> This patch adds support for triggering the VCPF sequence in the MSM DP
> controller.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 56 ++++++++++++++++++++++++++++++++++---
> drivers/gpu/drm/msm/dp/dp_ctrl.h | 2 +-
> drivers/gpu/drm/msm/dp/dp_display.c | 2 +-
> drivers/gpu/drm/msm/dp/dp_reg.h | 6 ++++
> 4 files changed, 60 insertions(+), 6 deletions(-)
>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 36/38] drm/msm/dpu: use msm_dp_get_mst_intf_id() to get the intf id
2025-08-25 14:16 ` [PATCH v3 36/38] drm/msm/dpu: use msm_dp_get_mst_intf_id() to get the intf id Yongxing Mou
@ 2025-08-26 23:42 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-26 23:42 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:22PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> Use msm_dp_get_mst_intf_id() to get the interface ID for the DP MST
> controller as the intf_id is unique for each MST stream of each
> DP controller.
I think we have one sensible exception: SC8180X, where we have several
DP controllers and one shared DP interface, but let's forget about it
for now...
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 51 +++++++++++++++++++++++++----
> 1 file changed, 44 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index 05e5f3463e30c9a6bd5b740580720ae2bf6b3246..2eb5397d15732b224372c68d0b2b7167da9f2896 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -1429,17 +1429,52 @@ static void dpu_encoder_virt_atomic_disable(struct drm_encoder *drm_enc,
>
> static struct dpu_hw_intf *dpu_encoder_get_intf(const struct dpu_mdss_cfg *catalog,
> struct dpu_rm *dpu_rm,
> - enum dpu_intf_type type, u32 controller_id)
> + enum dpu_intf_type type, int enc_type, u32 id)
> {
> - int i = 0;
> + int i = 0, cnt = 0;
> + int controller_id = id >> 16;
> + int stream_id = id & 0x0F;
>
> if (type == INTF_WB)
> return NULL;
>
> - for (i = 0; i < catalog->intf_count; i++) {
> - if (catalog->intf[i].type == type
> - && catalog->intf[i].controller_id == controller_id) {
> - return dpu_rm_get_intf(dpu_rm, catalog->intf[i].id);
> + if (enc_type == DRM_MODE_ENCODER_DPMST) {
> + /* The intf order in dpu_intf_cfg matches the mapping in the DP HPG.
> + * example:
> + * DPU_8_4_0 - DP Controller intf to stream Mapping
> + *
> + * +-------------+----------+----------+----------+----------+
> + * | stream_id | 0 | 1 | 2 | 3 |
> + * +-------------+----------+----------+----------+----------+
> + * | DP0 | INTF_0 | INTF_3 | INTF_6 | INTF_7 |
> + * | DP1 | INTF_4 | INTF_8 | | |
> + * +-------------+----------+----------+----------+----------+
> + *
> + * DPU_9_2_0 - DP Controller intf to stream Mapping
> + *
> + * +-------------+----------+----------+
> + * | Controller | 0 | 1 |
> + * +-------------+----------+----------+
> + * | DP0 | INTF_0 | INTF_3 |
> + * | DP1 | INTF_4 | INTF_8 |
> + * | DP2 | INTF_6 | INTF_7 |
> + * +-------------+----------+----------+
> + */
> + DPU_DEBUG("controller_id %d for stream_id = %d\n", controller_id, stream_id);
> + for (i = 0; i < catalog->intf_count; i++) {
> + if (catalog->intf[i].type == INTF_DP
> + && controller_id == catalog->intf[i].controller_id) {
&& should be on the previous line
> + if (cnt == stream_id)
if (cnt++ == stream_id) return;
> + return dpu_rm_get_intf(dpu_rm, catalog->intf[i].id);
> + cnt++;
> + }
> + }
return NULL, drop else{}
> + } else {
> + for (i = 0; i < catalog->intf_count; i++) {
> + if (catalog->intf[i].type == type
> + && catalog->intf[i].controller_id == controller_id) {
> + return dpu_rm_get_intf(dpu_rm, catalog->intf[i].id);
> + }
> }
> }
>
> @@ -2670,7 +2705,9 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
>
> phys_params.hw_intf = dpu_encoder_get_intf(dpu_kms->catalog, &dpu_kms->rm,
> disp_info->intf_type,
> - controller_id);
> + dpu_enc->base.encoder_type,
> + controller_id << 16
> + | disp_info->stream_id);
No need to, just pass whole disp_info pointer.
>
> if (disp_info->intf_type == INTF_WB && controller_id < WB_MAX)
> phys_params.hw_wb = dpu_rm_get_wb(&dpu_kms->rm, controller_id);
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 37/38] drm/msm/dp: fix the intf_type of MST interfaces
2025-08-25 14:16 ` [PATCH v3 37/38] drm/msm/dp: fix the intf_type of MST interfaces Yongxing Mou
@ 2025-08-27 1:18 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-27 1:18 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:16:23PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> Interface type of MST interfaces is currently INTF_NONE. Update this to
> INTF_DP. And correct the intf_6 intr_underrun/intr_vsync index for
> dpu_8_4_sa8775p.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h | 6 +++---
> drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h | 12 ++++++------
> drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h | 6 +++---
Also, as far as I remember, INTF_3 is not a DP1, but DP0 MST on the following platforms:
- 4.0, SDM845
- 5.0, SM8150
- 5.2, SM7150
- 5.3, SM6150
- 6.0, SM8250
- 7.0, SM8350
- 8.1, SM8450
- 9.0, SM8550
- 9.1, SAR2130P
- 10.0, SM8650
Please update them them as a separate patch.
For this patch:
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 38/38] drm/msm/dp: Add MST stream support for SA8775P DP controller 0 and 1
2025-08-25 14:16 ` [PATCH v3 38/38] drm/msm/dp: Add MST stream support for SA8775P DP controller 0 and 1 Yongxing Mou
@ 2025-08-27 1:19 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-08-27 1:19 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel
On Mon, Aug 25, 2025 at 10:16:24PM +0800, Yongxing Mou wrote:
> This change enables SA8775P support for Multi-Stream Transport (MST),
> allowing each controller to handle up to two or four DisplayPort streams.
> As all necessary code for MST support was already implemented in the
> previous series of patches.
Why do you update only SA8775P? Please update all the platforms at once.
>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_display.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 897ef653b3cea08904bb3595e8ac10fd7fcf811f..4a94f37513c21f9a273080b572a1e50a186a45ce 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -108,8 +108,10 @@ struct msm_dp_desc {
> };
>
> static const struct msm_dp_desc msm_dp_desc_sa8775p[] = {
> - { .io_start = 0x0af54000, .id = MSM_DP_CONTROLLER_0, .wide_bus_supported = true },
> - { .io_start = 0x0af5c000, .id = MSM_DP_CONTROLLER_1, .wide_bus_supported = true },
> + { .io_start = 0x0af54000, .id = MSM_DP_CONTROLLER_0, .wide_bus_supported = true,
> + .mst_streams = 4},
> + { .io_start = 0x0af5c000, .id = MSM_DP_CONTROLLER_1, .wide_bus_supported = true,
> + .mst_streams = 2},
> { .io_start = 0x22154000, .id = MSM_DP_CONTROLLER_2, .wide_bus_supported = true },
> { .io_start = 0x2215c000, .id = MSM_DP_CONTROLLER_3, .wide_bus_supported = true },
> {}
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 01/38] drm/msm/dp: remove cached drm_edid from panel
2025-08-25 16:41 ` Dmitry Baryshkov
@ 2025-09-02 8:42 ` Yongxing Mou
2025-09-02 9:36 ` Dmitry Baryshkov
0 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-09-02 8:42 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel
On 8/26/2025 12:41 AM, Dmitry Baryshkov wrote:
> On Mon, Aug 25, 2025 at 10:15:47PM +0800, Yongxing Mou wrote:
>> The cached drm_edid in msm_dp_panel was redundant and led to unnecessary
>> state management complexity. This change removes the drm_edid member from
>
> Please see Documentation/process/submitting-patches.rst on how to write
> commit messages. Please use imperative language instead of describing
> the changes.
>
> THe patch LGTM.
>
Thanks, will update it in next version. Since the HPD refactor series
are unlikely to be merged soon. Can I separate out some patches from the
MST series that don't have dependencies and send them individually to
make it get applied? This would help reduce the number of the MST series.
>> the panel structure and refactors related functions to use locally read
>> EDID data instead.
>>
>> - Replaces msm_dp_panel_read_sink_caps() with msm_dp_panel_read_link_caps()
>> - Updates msm_dp_panel_handle_sink_request() to accept drm_edid as input
>> - Removes msm_dp_panel_get_modes() and drm_edid caching logic
>> - Cleans up unused drm_edid_free() calls
>>
>> This simplifies EDID handling and avoids stale data issues.
>>
>> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
>> ---
>> drivers/gpu/drm/msm/dp/dp_display.c | 28 +++++++++++++++-------
>> drivers/gpu/drm/msm/dp/dp_panel.c | 47 ++++---------------------------------
>> drivers/gpu/drm/msm/dp/dp_panel.h | 9 +++----
>> 3 files changed, 26 insertions(+), 58 deletions(-)
>>
>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 01/38] drm/msm/dp: remove cached drm_edid from panel
2025-09-02 8:42 ` Yongxing Mou
@ 2025-09-02 9:36 ` Dmitry Baryshkov
2025-09-02 10:19 ` Yongxing Mou
0 siblings, 1 reply; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-09-02 9:36 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel
On Tue, Sep 02, 2025 at 04:42:18PM +0800, Yongxing Mou wrote:
>
>
> On 8/26/2025 12:41 AM, Dmitry Baryshkov wrote:
> > On Mon, Aug 25, 2025 at 10:15:47PM +0800, Yongxing Mou wrote:
> > > The cached drm_edid in msm_dp_panel was redundant and led to unnecessary
> > > state management complexity. This change removes the drm_edid member from
> >
> > Please see Documentation/process/submitting-patches.rst on how to write
> > commit messages. Please use imperative language instead of describing
> > the changes.
> >
> > THe patch LGTM.
> >
> Thanks, will update it in next version. Since the HPD refactor series are
> unlikely to be merged soon. Can I separate out some patches from the MST
> series that don't have dependencies and send them individually to make it
> get applied? This would help reduce the number of the MST series.
Yes, of course. Please keep version number monothonic for those patches
(e.g. by telling b4 that it should start from v4).
>
> > > the panel structure and refactors related functions to use locally read
> > > EDID data instead.
> > >
> > > - Replaces msm_dp_panel_read_sink_caps() with msm_dp_panel_read_link_caps()
> > > - Updates msm_dp_panel_handle_sink_request() to accept drm_edid as input
> > > - Removes msm_dp_panel_get_modes() and drm_edid caching logic
> > > - Cleans up unused drm_edid_free() calls
> > >
> > > This simplifies EDID handling and avoids stale data issues.
> > >
> > > Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> > > ---
> > > drivers/gpu/drm/msm/dp/dp_display.c | 28 +++++++++++++++-------
> > > drivers/gpu/drm/msm/dp/dp_panel.c | 47 ++++---------------------------------
> > > drivers/gpu/drm/msm/dp/dp_panel.h | 9 +++----
> > > 3 files changed, 26 insertions(+), 58 deletions(-)
> > >
> >
>
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 12/38] drm/msm/dp: introduce max_streams for DP controller MST support
2025-08-25 14:15 ` [PATCH v3 12/38] drm/msm/dp: introduce max_streams for DP controller MST support Yongxing Mou
2025-08-25 17:42 ` Dmitry Baryshkov
@ 2025-09-02 9:41 ` Dmitry Baryshkov
1 sibling, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-09-02 9:41 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel, Abhinav Kumar
On Mon, Aug 25, 2025 at 10:15:58PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>
> Introduce the `mst_streams` field in each DP controller descriptor to
> specify the number of supported MST streams. Most platforms support 2 or
> 4 MST streams, while platforms without MST support default to a single
> stream (`DEFAULT_STREAM_COUNT = 1`).
>
> This change also accounts for platforms with asymmetric stream support,
> e.g., DP0 supporting 4 streams and DP1 supporting 2.
>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> ---
> drivers/gpu/drm/msm/dp/dp_display.c | 21 +++++++++++++++++++++
> drivers/gpu/drm/msm/dp/dp_display.h | 1 +
> 2 files changed, 22 insertions(+)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 78d932bceb581ee54116926506b1025bd159108f..a8477a0a180137f15cbb1401c3964636aa32626c 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -33,6 +33,7 @@ module_param(psr_enabled, bool, 0);
> MODULE_PARM_DESC(psr_enabled, "enable PSR for eDP and DP displays");
>
> #define HPD_STRING_SIZE 30
> +#define DEFAULT_STREAM_COUNT 1
>
> enum {
> ISR_DISCONNECTED,
> @@ -52,6 +53,7 @@ struct msm_dp_display_private {
> bool core_initialized;
> bool phy_initialized;
> bool audio_supported;
> + bool mst_supported;
>
> struct drm_device *drm_dev;
>
> @@ -84,12 +86,15 @@ struct msm_dp_display_private {
>
> void __iomem *p0_base;
> size_t p0_len;
> +
> + int max_stream;
> };
>
> struct msm_dp_desc {
> phys_addr_t io_start;
> unsigned int id;
> bool wide_bus_supported;
> + int mst_streams;
> };
>
> static const struct msm_dp_desc msm_dp_desc_sa8775p[] = {
> @@ -1213,6 +1218,15 @@ static int msm_dp_display_get_io(struct msm_dp_display_private *display)
> return 0;
> }
>
> +int msm_dp_get_mst_max_stream(struct msm_dp *msm_dp_display)
> +{
> + struct msm_dp_display_private *dp;
> +
> + dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
> +
> + return dp->max_stream;
> +}
> +
> static int msm_dp_display_probe(struct platform_device *pdev)
> {
> int rc = 0;
> @@ -1239,6 +1253,13 @@ static int msm_dp_display_probe(struct platform_device *pdev)
> dp->msm_dp_display.is_edp =
> (dp->msm_dp_display.connector_type == DRM_MODE_CONNECTOR_eDP);
> dp->hpd_isr_status = 0;
> + dp->max_stream = DEFAULT_STREAM_COUNT;
> + dp->mst_supported = FALSE;
> +
> + if (desc->mst_streams > DEFAULT_STREAM_COUNT) {
> + dp->max_stream = desc->mst_streams;
We should keep compatibility with earlier DT files which didn't define
enough stream clocks for DP MST case. Please check how many stream
clocks are actually present in the DT and set max_stream accordingly.
> + dp->mst_supported = TRUE;
> + }
>
> rc = msm_dp_display_get_io(dp);
> if (rc)
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
> index 37c6e87db90ce951274cdae61f26d76dc9ef3840..7727cf325a89b4892d2370a5616c4fa76fc88485 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.h
> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
> @@ -29,6 +29,7 @@ struct msm_dp {
> bool psr_supported;
> };
>
> +int msm_dp_get_mst_max_stream(struct msm_dp *msm_dp_display);
> int msm_dp_display_get_modes(struct msm_dp *msm_dp_display);
> bool msm_dp_display_check_video_test(struct msm_dp *msm_dp_display);
> int msm_dp_display_get_test_bpp(struct msm_dp *msm_dp_display);
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 01/38] drm/msm/dp: remove cached drm_edid from panel
2025-09-02 9:36 ` Dmitry Baryshkov
@ 2025-09-02 10:19 ` Yongxing Mou
2025-09-02 12:34 ` Dmitry Baryshkov
0 siblings, 1 reply; 81+ messages in thread
From: Yongxing Mou @ 2025-09-02 10:19 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel
On 9/2/2025 5:36 PM, Dmitry Baryshkov wrote:
> On Tue, Sep 02, 2025 at 04:42:18PM +0800, Yongxing Mou wrote:
>>
>>
>> On 8/26/2025 12:41 AM, Dmitry Baryshkov wrote:
>>> On Mon, Aug 25, 2025 at 10:15:47PM +0800, Yongxing Mou wrote:
>>>> The cached drm_edid in msm_dp_panel was redundant and led to unnecessary
>>>> state management complexity. This change removes the drm_edid member from
>>>
>>> Please see Documentation/process/submitting-patches.rst on how to write
>>> commit messages. Please use imperative language instead of describing
>>> the changes.
>>>
>>> THe patch LGTM.
>>>
>> Thanks, will update it in next version. Since the HPD refactor series are
>> unlikely to be merged soon. Can I separate out some patches from the MST
>> series that don't have dependencies and send them individually to make it
>> get applied? This would help reduce the number of the MST series.
>
> Yes, of course. Please keep version number monothonic for those patches
> (e.g. by telling b4 that it should start from v4).
>
Sure. Thanks, I want to confirm whether the patches should be sent
individually or grouped into a series? They seem to be logically
unrelated. I was originally planning to send each one separately.>>
>>>> the panel structure and refactors related functions to use locally read
>>>> EDID data instead.
>>>>
>>>> - Replaces msm_dp_panel_read_sink_caps() with msm_dp_panel_read_link_caps()
>>>> - Updates msm_dp_panel_handle_sink_request() to accept drm_edid as input
>>>> - Removes msm_dp_panel_get_modes() and drm_edid caching logic
>>>> - Cleans up unused drm_edid_free() calls
>>>>
>>>> This simplifies EDID handling and avoids stale data issues.
>>>>
>>>> Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
>>>> ---
>>>> drivers/gpu/drm/msm/dp/dp_display.c | 28 +++++++++++++++-------
>>>> drivers/gpu/drm/msm/dp/dp_panel.c | 47 ++++---------------------------------
>>>> drivers/gpu/drm/msm/dp/dp_panel.h | 9 +++----
>>>> 3 files changed, 26 insertions(+), 58 deletions(-)
>>>>
>>>
>>
>>
>
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v3 01/38] drm/msm/dp: remove cached drm_edid from panel
2025-09-02 10:19 ` Yongxing Mou
@ 2025-09-02 12:34 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-09-02 12:34 UTC (permalink / raw)
To: Yongxing Mou
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
linux-arm-msm, dri-devel, freedreno, linux-kernel
On Tue, Sep 02, 2025 at 06:19:11PM +0800, Yongxing Mou wrote:
>
>
> On 9/2/2025 5:36 PM, Dmitry Baryshkov wrote:
> > On Tue, Sep 02, 2025 at 04:42:18PM +0800, Yongxing Mou wrote:
> > >
> > >
> > > On 8/26/2025 12:41 AM, Dmitry Baryshkov wrote:
> > > > On Mon, Aug 25, 2025 at 10:15:47PM +0800, Yongxing Mou wrote:
> > > > > The cached drm_edid in msm_dp_panel was redundant and led to unnecessary
> > > > > state management complexity. This change removes the drm_edid member from
> > > >
> > > > Please see Documentation/process/submitting-patches.rst on how to write
> > > > commit messages. Please use imperative language instead of describing
> > > > the changes.
> > > >
> > > > THe patch LGTM.
> > > >
> > > Thanks, will update it in next version. Since the HPD refactor series are
> > > unlikely to be merged soon. Can I separate out some patches from the MST
> > > series that don't have dependencies and send them individually to make it
> > > get applied? This would help reduce the number of the MST series.
> >
> > Yes, of course. Please keep version number monothonic for those patches
> > (e.g. by telling b4 that it should start from v4).
> >
> Sure. Thanks, I want to confirm whether the patches should be sent
> individually or grouped into a series? They seem to be logically unrelated.
> I was originally planning to send each one separately.>>
You can send them separately.
> > > > > the panel structure and refactors related functions to use locally read
> > > > > EDID data instead.
> > > > >
> > > > > - Replaces msm_dp_panel_read_sink_caps() with msm_dp_panel_read_link_caps()
> > > > > - Updates msm_dp_panel_handle_sink_request() to accept drm_edid as input
> > > > > - Removes msm_dp_panel_get_modes() and drm_edid caching logic
> > > > > - Cleans up unused drm_edid_free() calls
> > > > >
> > > > > This simplifies EDID handling and avoids stale data issues.
> > > > >
> > > > > Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
> > > > > ---
> > > > > drivers/gpu/drm/msm/dp/dp_display.c | 28 +++++++++++++++-------
> > > > > drivers/gpu/drm/msm/dp/dp_panel.c | 47 ++++---------------------------------
> > > > > drivers/gpu/drm/msm/dp/dp_panel.h | 9 +++----
> > > > > 3 files changed, 26 insertions(+), 58 deletions(-)
> > > > >
> > > >
> > >
> > >
> >
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
end of thread, other threads:[~2025-09-02 12:35 UTC | newest]
Thread overview: 81+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-25 14:15 [PATCH v3 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
2025-08-25 14:15 ` [PATCH v3 01/38] drm/msm/dp: remove cached drm_edid from panel Yongxing Mou
2025-08-25 16:41 ` Dmitry Baryshkov
2025-09-02 8:42 ` Yongxing Mou
2025-09-02 9:36 ` Dmitry Baryshkov
2025-09-02 10:19 ` Yongxing Mou
2025-09-02 12:34 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead Yongxing Mou
2025-08-25 16:50 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 03/38] drm/msm/dp: break up dp_display_enable into two parts Yongxing Mou
2025-08-25 17:13 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 04/38] drm/msm/dp: re-arrange dp_display_disable() into functional parts Yongxing Mou
2025-08-25 17:25 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 05/38] drm/msm/dp: splite msm_dp_ctrl_config_ctrl() into link parts and stream parts Yongxing Mou
2025-08-25 17:28 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 06/38] drm/msm/dp: extract MISC1_MISC0 configuration into a separate function Yongxing Mou
2025-08-25 17:30 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 07/38] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it Yongxing Mou
2025-08-25 17:32 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 08/38] drm/msm/dp: move the pixel clock control to its own API Yongxing Mou
2025-08-25 17:34 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 09/38] drm/msm/dp: split dp_ctrl_off() into stream and link parts Yongxing Mou
2025-08-25 17:35 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 10/38] drm/msm/dp: make bridge helpers use dp_display to allow re-use Yongxing Mou
2025-08-25 14:15 ` [PATCH v3 11/38] drm/msm/dp: separate dp_display_prepare() into its own API Yongxing Mou
2025-08-25 17:39 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 12/38] drm/msm/dp: introduce max_streams for DP controller MST support Yongxing Mou
2025-08-25 17:42 ` Dmitry Baryshkov
2025-09-02 9:41 ` Dmitry Baryshkov
2025-08-25 14:15 ` [PATCH v3 13/38] drm/msm/dp: introduce stream_id for each DP panel Yongxing Mou
2025-08-25 17:56 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 14/38] drm/msm/dp: Add support for programming p1/p2/p3 register blocks Yongxing Mou
2025-08-25 17:59 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 15/38] drm/msm/dp: use stream_id to change offsets in dp_catalog Yongxing Mou
2025-08-25 18:01 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 16/38] drm/msm/dp: Add catalog support for 3rd/4th stream MST Yongxing Mou
2025-08-25 20:35 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 17/38] drm/msm/dp: add support to send ACT packets for MST Yongxing Mou
2025-08-25 21:10 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 18/38] drm/msm/dp: Add support to enable MST in mainlink control Yongxing Mou
2025-08-25 21:24 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 19/38] drm/msm/dp: no need to update tu calculation for mst Yongxing Mou
2025-08-25 21:25 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 20/38] drm/msm/dp: Add support for MST channel slot allocation Yongxing Mou
2025-08-25 21:52 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 21/38] drm/msm/dp: Add support for sending VCPF packets in DP controller Yongxing Mou
2025-08-26 21:28 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 22/38] drm/msm/dp: Always program MST_FIFO_CONSTANT_FILL for MST use cases Yongxing Mou
2025-08-25 21:55 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 23/38] drm/msm/dp: abstract out the dp_display stream helpers to accept a panel Yongxing Mou
2025-08-25 22:18 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 24/38] drm/msm/dp: replace power_on with active_stream_cnt for dp_display Yongxing Mou
2025-08-25 22:22 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 25/38] drm/msm/dp: Mark the SST bridge disconnected when mst is active Yongxing Mou
2025-08-25 22:23 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 26/38] drm/msm/dp: add an API to initialize MST on sink side Yongxing Mou
2025-08-26 9:26 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 27/38] drm/msm/dp: add dp_display_get_panel() to initialize DP panel Yongxing Mou
2025-08-26 16:33 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 28/38] drm/msm/dp: add dp_mst_drm to manage DP MST bridge operations Yongxing Mou
2025-08-26 17:36 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 29/38] drm/msm/dp: add MST atomic check to msm_atomic_check() Yongxing Mou
2025-08-26 17:44 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 30/38] drm/msm/dp: add connector abstraction for DP MST Yongxing Mou
2025-08-26 18:31 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 31/38] drm/msm/dp: add HPD callback for dp MST Yongxing Mou
2025-08-26 18:40 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 32/38] drm/msm/dp: propagate MST state changes to dp mst module Yongxing Mou
2025-08-26 18:43 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 33/38] drm/msm: add support for MST non-blocking commits Yongxing Mou
2025-08-26 18:47 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 34/38] drm/msm: initialize DRM MST encoders for DP controllers Yongxing Mou
2025-08-26 18:55 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 35/38] drm/msm/dp: initialize dp_mst module for each DP MST controller Yongxing Mou
2025-08-26 21:27 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 36/38] drm/msm/dpu: use msm_dp_get_mst_intf_id() to get the intf id Yongxing Mou
2025-08-26 23:42 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 37/38] drm/msm/dp: fix the intf_type of MST interfaces Yongxing Mou
2025-08-27 1:18 ` Dmitry Baryshkov
2025-08-25 14:16 ` [PATCH v3 38/38] drm/msm/dp: Add MST stream support for SA8775P DP controller 0 and 1 Yongxing Mou
2025-08-27 1:19 ` Dmitry Baryshkov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).