linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets
@ 2025-06-09 12:21 Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 01/38] drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid Yongxing Mou
                   ` (39 more replies)
  0 siblings, 40 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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/seriedds/142010/ (v2 to fix up HPD)

Bindings for the pixel clock for additional stream is available at :

[2] : https://patchwork.freedesktop.org/series/142016/

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 <quic_yongmou@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 (35):
      drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid
      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: move the pixel clock control to its own API
      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 the max_streams for dp controller
      drm/msm/dp: introduce stream_id for each DP panel
      drm/msm/dp: add support for programming p1/p2/p3 register block
      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 program mst support in mainlink
      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 to send vcpf packets in dp controller
      drm/msm/dp: always program MST_FIFO_CONSTANT_FILL for MST
      drm/msm/dp: abstract out the dp_display stream helpers to accept a panel
      drm/msm/dp: move link related operations to dp_display_unprepare()
      drm/msm/dp: replace power_on with active_stream_cnt for dp_display
      drm/msm/dp: make the SST bridge disconnected when mst is active
      drm/msm/dp: add an API to initialize MST on sink side
      drm/msm/dp: skip reading the EDID for MST cases
      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 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: mark ST_DISCONNECTED only if all streams are disabled
      drm/msm/dp: fix the intf_type of MST interfaces

Yongxing Mou (3):
      drm/msm/dp: Add catalog support for 3rd/4th stream MST
      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_4_sa8775p.h    |    8 +-
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c        |   21 +-
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h        |    2 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c            |   72 +-
 drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h            |    2 +-
 drivers/gpu/drm/msm/dp/dp_audio.c                  |    2 +-
 drivers/gpu/drm/msm/dp/dp_catalog.c                |  558 ++++++++--
 drivers/gpu/drm/msm/dp/dp_catalog.h                |   64 +-
 drivers/gpu/drm/msm/dp/dp_ctrl.c                   |  474 ++++++---
 drivers/gpu/drm/msm/dp/dp_ctrl.h                   |   22 +-
 drivers/gpu/drm/msm/dp/dp_display.c                |  510 +++++++---
 drivers/gpu/drm/msm/dp/dp_display.h                |   33 +-
 drivers/gpu/drm/msm/dp/dp_drm.c                    |   53 +-
 drivers/gpu/drm/msm/dp/dp_drm.h                    |   12 -
 drivers/gpu/drm/msm/dp/dp_mst_drm.c                | 1065 ++++++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_mst_drm.h                |  106 ++
 drivers/gpu/drm/msm/dp/dp_panel.c                  |   66 +-
 drivers/gpu/drm/msm/dp/dp_panel.h                  |   10 +-
 drivers/gpu/drm/msm/dp/dp_reg.h                    |   46 +-
 drivers/gpu/drm/msm/msm_atomic.c                   |    3 +
 drivers/gpu/drm/msm/msm_drv.h                      |   19 +
 drivers/gpu/drm/msm/msm_kms.c                      |    2 +
 23 files changed, 2725 insertions(+), 428 deletions(-)
---
base-commit: 475c850a7fdd0915b856173186d5922899d65686
change-id: 20250609-msm-dp-mst-cddc2f61daee
prerequisite-message-id: <20250529-hpd_display_off-v1-0-ce33bac2987c@oss.qualcomm.com>
prerequisite-patch-id: a1f426b99b4a99d63daa9902cde9ee38ae1128d1
prerequisite-patch-id: ae9e0a0db8edd05da06f9673e9de56761654ed3c
prerequisite-patch-id: 7cb84491c6c3cf73480343218c543d090f8cb5e2
prerequisite-patch-id: f32638e79dd498db2075735392e85729b1b691fc
prerequisite-message-id: <20250530-dp_mst_bindings-v2-0-f925464d32a8@oss.qualcomm.com>
prerequisite-patch-id: e505c21f653c8e18ce83cad1fc787c13a6c8ed12
prerequisite-patch-id: cfdd5c37d38b2a4f1386af4021ba3920c6d8dcf8
prerequisite-patch-id: f4abdddcb90c8203044395f4768d794214fe3225
prerequisite-patch-id: 45013dfaf34856422b7b6b3d2ee42d81a917177b
prerequisite-patch-id: 2f35bedb0410bead1b66cbfaf51984fc7016828f

Best regards,
-- 
Yongxing Mou <quic_yongmou@quicinc.com>


^ permalink raw reply	[flat|nested] 108+ messages in thread

* [PATCH v2 01/38] drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:41   ` Dmitry Baryshkov
  2025-06-09 12:21 ` [PATCH v2 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead Yongxing Mou
                   ` (38 subsequent siblings)
  39 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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>

In preparation of DP MST where link caps are read for the
immediate downstream device and the edid is read through
sideband messaging, split the msm_dp_panel_read_sink_caps() into
two parts which read the link parameters and the edid parts
respectively. Also drop the panel drm_edid cached as we actually
don't need it.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_display.c | 13 +++++----
 drivers/gpu/drm/msm/dp/dp_panel.c   | 55 ++++++++++++++++++++-----------------
 drivers/gpu/drm/msm/dp/dp_panel.h   |  6 ++--
 3 files changed, 40 insertions(+), 34 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 6f05a939ce9e648e9601597155999b6f85adfcff..4a9b65647cdef1ed6c3bb851f93df0db8be977af 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -389,7 +389,11 @@ 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);
+	if (rc)
+		goto end;
+
+	rc = msm_dp_panel_read_edid(dp->panel, connector);
 	if (rc)
 		goto end;
 
@@ -720,7 +724,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);
 }
 
@@ -783,7 +786,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->catalog);
@@ -791,13 +794,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:
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index 4e8ab75c771b1e3a2d62f75e9993e1062118482b..d9041e235104a74b3cc50ff2e307eae0c4301ef3 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -118,14 +118,13 @@ static u32 msm_dp_panel_get_supported_bpp(struct msm_dp_panel *msm_dp_panel,
 	return min_supported_bpp;
 }
 
-int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
-	struct drm_connector *connector)
+int msm_dp_panel_read_link_caps(struct msm_dp_panel *msm_dp_panel)
 {
 	int rc, bw_code;
 	int count;
 	struct msm_dp_panel_private *panel;
 
-	if (!msm_dp_panel || !connector) {
+	if (!msm_dp_panel) {
 		DRM_ERROR("invalid input\n");
 		return -EINVAL;
 	}
@@ -160,26 +159,29 @@ 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;
+	return rc;
+}
 
-	drm_edid_free(msm_dp_panel->drm_edid);
+int msm_dp_panel_read_edid(struct msm_dp_panel *msm_dp_panel, struct drm_connector *connector)
+{
+	struct msm_dp_panel_private *panel;
+	const struct drm_edid *drm_edid;
+
+	panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
 
-	msm_dp_panel->drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
+	drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
 
-	drm_edid_connector_update(connector, msm_dp_panel->drm_edid);
+	drm_edid_connector_update(connector, drm_edid);
 
-	if (!msm_dp_panel->drm_edid) {
+	if (!drm_edid) {
 		DRM_ERROR("panel edid read failed\n");
 		/* check edid read fail is due to unplug */
 		if (!msm_dp_catalog_link_is_connected(panel->catalog)) {
-			rc = -ETIMEDOUT;
-			goto end;
+			return -ETIMEDOUT;
 		}
 	}
 
-end:
-	return rc;
+	return 0;
 }
 
 u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel,
@@ -208,15 +210,20 @@ u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel,
 int msm_dp_panel_get_modes(struct msm_dp_panel *msm_dp_panel,
 	struct drm_connector *connector)
 {
+	struct msm_dp_panel_private *panel;
+	const struct drm_edid *drm_edid;
+
 	if (!msm_dp_panel) {
 		DRM_ERROR("invalid input\n");
 		return -EINVAL;
 	}
 
-	if (msm_dp_panel->drm_edid)
-		return drm_edid_connector_add_modes(connector);
+	panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
+
+	drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
+	drm_edid_connector_update(connector, drm_edid);
 
-	return 0;
+	return drm_edid_connector_add_modes(connector);
 }
 
 static u8 msm_dp_panel_get_edid_checksum(const struct edid *edid)
@@ -229,6 +236,7 @@ static u8 msm_dp_panel_get_edid_checksum(const struct edid *edid)
 void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel)
 {
 	struct msm_dp_panel_private *panel;
+	const struct drm_edid *drm_edid;
 
 	if (!msm_dp_panel) {
 		DRM_ERROR("invalid input\n");
@@ -238,8 +246,13 @@ void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel)
 	panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
 
 	if (panel->link->sink_request & DP_TEST_LINK_EDID_READ) {
+		drm_edid = drm_edid_read_ddc(msm_dp_panel->connector, &panel->aux->ddc);
+
+		if (!drm_edid)
+			return;
+
 		/* 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)
@@ -515,11 +528,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 4906f4f09f2451cfed3c1007f38b4db7dfdb1d90..7f139478e1012d5b8f1f745f0de5fc3943745428 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -32,7 +32,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;
@@ -51,7 +50,9 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel);
 int msm_dp_panel_read_sink_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);
+			      u32 mode_pclk_khz);
+int msm_dp_panel_read_link_caps(struct msm_dp_panel *dp_panel);
+int msm_dp_panel_read_edid(struct msm_dp_panel *dp_panel, struct drm_connector *connector);
 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);
@@ -86,5 +87,4 @@ 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, struct msm_dp_catalog *catalog);
-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] 108+ messages in thread

* [PATCH v2 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 01/38] drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:48   ` Dmitry Baryshkov
  2025-06-09 12:21 ` [PATCH v2 03/38] drm/msm/dp: break up dp_display_enable into two parts Yongxing Mou
                   ` (37 subsequent siblings)
  39 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 populated the panel's dp_display_mode
instead.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_display.c | 76 ++++++++++++++-----------------------
 1 file changed, 29 insertions(+), 47 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 4a9b65647cdef1ed6c3bb851f93df0db8be977af..9d2db9cbd2552470a36a63f70f517c35436f7280 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -92,7 +92,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 */
@@ -806,16 +805,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;
+
+	msm_dp_panel->msm_dp_mode.v_active_low =
+		!!(adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC);
+	msm_dp_panel->msm_dp_mode.h_active_low =
+		!!(adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC);
+	msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 =
+		drm_mode_is_420_only(&msm_dp_panel->connector->display_info, adjusted_mode) &&
+		msm_dp_panel->vsc_sdp_supported;
 
-	drm_mode_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_init_panel_info(msm_dp_panel);
 	return 0;
 }
 
@@ -1431,10 +1443,13 @@ bool msm_dp_needs_periph_flush(const struct msm_dp *msm_dp_display,
 bool msm_dp_wide_bus_available(const 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);
 
-	if (dp->msm_dp_mode.out_fmt_is_yuv_420)
+	dp_panel = dp->panel;
+
+	if (dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
 		return false;
 
 	return dp->wide_bus_supported;
@@ -1496,10 +1511,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);
@@ -1517,15 +1528,6 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
 		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);
-		mutex_unlock(&msm_dp_display->event_mutex);
-		return;
-	}
-
-	hpd_state =  msm_dp_display->hpd_state;
-
 	if (hpd_state == ST_CONNECTED && !dp->power_on) {
 		msm_dp_display_host_phy_init(msm_dp_display);
 		force_link_train = true;
@@ -1604,33 +1606,13 @@ 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->catalog->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;
+	msm_dp_display->catalog->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] 108+ messages in thread

* [PATCH v2 03/38] drm/msm/dp: break up dp_display_enable into two parts
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 01/38] drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:59   ` Dmitry Baryshkov
  2025-06-09 12:21 ` [PATCH v2 04/38] drm/msm/dp: re-arrange dp_display_disable() into functional parts Yongxing Mou
                   ` (36 subsequent siblings)
  39 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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. Splite these two parts into
prepare/enable APIs, to support MST bridges_enable inserte
the MST payloads funcs between enable stream_clks and programe
register.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_ctrl.c    | 57 +++++++++++++--------
 drivers/gpu/drm/msm/dp/dp_ctrl.h    |  3 +-
 drivers/gpu/drm/msm/dp/dp_display.c | 99 +++++++++++++++++++++++++++----------
 drivers/gpu/drm/msm/dp/dp_display.h |  1 +
 4 files changed, 111 insertions(+), 49 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index a50bfafbb4ea85c114c958ea0ed24362a1f23136..1e13ca81b0155a37a4ed7a2e83c918293d703a37 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -1980,40 +1980,61 @@ static int msm_dp_ctrl_link_retrain(struct msm_dp_ctrl_private *ctrl)
 	return msm_dp_ctrl_setup_main_link(ctrl, &training_step);
 }
 
-int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train)
+int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train)
 {
 	int ret = 0;
-	bool mainlink_ready = false;
 	struct msm_dp_ctrl_private *ctrl;
-	unsigned long pixel_rate;
-	unsigned long pixel_rate_orig;
 
 	if (!msm_dp_ctrl)
 		return -EINVAL;
 
 	ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
 
-	pixel_rate = pixel_rate_orig = ctrl->panel->msm_dp_mode.drm_mode.clock;
-
-	if (msm_dp_ctrl->wide_bus_en || ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420)
-		pixel_rate >>= 1;
-
-	drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d, pixel_rate=%lu\n",
-		ctrl->link->link_params.rate,
-		ctrl->link->link_params.num_lanes, pixel_rate);
+	drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d\n",
+		   ctrl->link->link_params.rate,
+		   ctrl->link->link_params.num_lanes);
 
 	drm_dbg_dp(ctrl->drm_dev,
-		"core_clk_on=%d link_clk_on=%d stream_clk_on=%d\n",
-		ctrl->core_clks_on, ctrl->link_clks_on, ctrl->stream_clks_on);
+		   "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);
 
 	if (!ctrl->link_clks_on) { /* link clk is off */
 		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);
@@ -2031,12 +2052,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 b7abfedbf5749c25877a0b8ba3af3d8ed4b23d67..42745c912adbad7221c78f5cecefa730bfda1e75 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -18,7 +18,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 *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_link(struct msm_dp_ctrl *msm_dp_ctrl);
 void msm_dp_ctrl_off(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 9d2db9cbd2552470a36a63f70f517c35436f7280..5ac5dcf35b789f2bda052a2c17aae20aa48d8e18 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -831,7 +831,37 @@ 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);
+	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");
+		return rc;
+	}
+
+	if (dp->hpd_state == ST_CONNECTED && !msm_dp_display->power_on) {
+		msm_dp_display_host_phy_init(dp);
+		force_link_train = true;
+	}
+
+	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;
@@ -842,7 +872,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;
 
@@ -872,13 +902,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 */
@@ -889,6 +916,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) {
 		/*
@@ -1506,9 +1541,8 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
 	struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge);
 	struct msm_dp *dp = msm_dp_bridge->msm_dp_display;
 	int rc = 0;
+
 	struct msm_dp_display_private *msm_dp_display;
-	u32 hpd_state;
-	bool force_link_train = false;
 
 	msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
 
@@ -1516,29 +1550,23 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
 		msm_dp_hpd_plug_handle(msm_dp_display, 0);
 
 	mutex_lock(&msm_dp_display->event_mutex);
-	if (pm_runtime_resume_and_get(&dp->pdev->dev)) {
-		DRM_ERROR("failed to pm_runtime_resume\n");
-		mutex_unlock(&msm_dp_display->event_mutex);
-		return;
-	}
 
-	hpd_state = msm_dp_display->hpd_state;
-	if (hpd_state == ST_DISCONNECT_PENDING) {
+	rc = msm_dp_display_prepare(msm_dp_display);
+	if (rc) {
+		DRM_ERROR("DP display prepare failed, rc=%d\n", rc);
 		mutex_unlock(&msm_dp_display->event_mutex);
 		return;
 	}
 
-	if (hpd_state == ST_CONNECTED && !dp->power_on) {
-		msm_dp_display_host_phy_init(msm_dp_display);
-		force_link_train = true;
-	}
-
-	msm_dp_display_enable(msm_dp_display, force_link_train);
-
-	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);
+	if (dp->prepared) {
+		rc = msm_dp_display_enable(msm_dp_display);
+		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);
+		}
 	}
 
 	/* completed connection */
@@ -1560,6 +1588,20 @@ 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;
+
+	if (!msm_dp_display->prepared) {
+		drm_dbg_dp(dp->drm_dev, "Link already setup, return\n");
+		return;
+	}
+
+	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)
 {
@@ -1580,6 +1622,8 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
 		drm_dbg_dp(dp->drm_dev, "type=%d wrong hpd_state=%d\n",
 			   dp->connector_type, hpd_state);
 
+	msm_dp_display_audio_notify_disable(msm_dp_display);
+
 	msm_dp_display_disable(msm_dp_display);
 
 	hpd_state =  msm_dp_display->hpd_state;
@@ -1588,9 +1632,10 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
 		msm_dp_display->hpd_state = ST_DISCONNECTED;
 	}
 
+	msm_dp_display_unprepare(msm_dp_display);
+
 	drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type);
 
-	pm_runtime_put_sync(&dp->pdev->dev);
 	mutex_unlock(&msm_dp_display->event_mutex);
 }
 
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index cc6e2cab36e9c0b1527ff292e547cbb4d69fd95c..2394840e9f28e136705004c3e6af93fbe13c33c5 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -19,6 +19,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] 108+ messages in thread

* [PATCH v2 04/38] drm/msm/dp: re-arrange dp_display_disable() into functional parts
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (2 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 03/38] drm/msm/dp: break up dp_display_enable into two parts Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 13:05   ` Dmitry Baryshkov
  2025-06-09 12:21 ` [PATCH v2 05/38] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it Yongxing Mou
                   ` (35 subsequent siblings)
  39 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_ctrl.c    | 29 +++++++++++++++--------------
 drivers/gpu/drm/msm/dp/dp_ctrl.h    |  5 ++++-
 drivers/gpu/drm/msm/dp/dp_display.c | 16 +++++++---------
 3 files changed, 26 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 1e13ca81b0155a37a4ed7a2e83c918293d703a37..1ce3cca121d0c56b493e282c76eb9202371564cf 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -2081,30 +2081,31 @@ 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_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl)
 {
 	struct msm_dp_ctrl_private *ctrl;
-	struct phy *phy;
 
 	ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
-	phy = ctrl->phy;
-
 	msm_dp_catalog_panel_disable_vsc_sdp(ctrl->catalog);
+}
 
-	/* set dongle to D3 (power off) mode */
-	msm_dp_link_psm_config(ctrl->link, &ctrl->panel->link_info, true);
+void msm_dp_ctrl_psm_config(struct msm_dp_ctrl *msm_dp_ctrl)
+{
+	struct msm_dp_ctrl_private *ctrl;
 
-	msm_dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);
+	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;
-	}
+	/* set dongle to D3 (power off) mode */
+	msm_dp_link_psm_config(ctrl->link, &ctrl->panel->link_info, true);
+}
 
-	dev_pm_opp_set_rate(ctrl->dev, 0);
-	msm_dp_ctrl_link_clk_disable(&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;
 
-	phy_power_off(phy);
+	ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
+	phy = ctrl->phy;
 
 	/* aux channel down, reinit phy */
 	phy_exit(phy);
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index 42745c912adbad7221c78f5cecefa730bfda1e75..edbe5766db74c4e4179141d895f9cb85e514f29b 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -20,7 +20,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 *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_link(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);
@@ -42,4 +41,8 @@ void msm_dp_ctrl_config_psr(struct msm_dp_ctrl *msm_dp_ctrl);
 int msm_dp_ctrl_core_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl);
 void msm_dp_ctrl_core_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl);
 
+void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl);
+void msm_dp_ctrl_psm_config(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 5ac5dcf35b789f2bda052a2c17aae20aa48d8e18..a5ca498cb970d0c6a4095b0b7fc6269c2dc3ad31 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -925,17 +925,15 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
 	if (!msm_dp_display->power_on)
 		return 0;
 
+	msm_dp_ctrl_clear_vsc_sdp_pkt(dp->ctrl);
+
+	/* 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);
+		msm_dp_ctrl_psm_config(dp->ctrl);
+		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
-		 * dongle unplugged out of DUT
-		 */
 		msm_dp_ctrl_off(dp->ctrl);
 		msm_dp_display_host_phy_exit(dp);
 	}

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 05/38] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (3 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 04/38] drm/msm/dp: re-arrange dp_display_disable() into functional parts Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 13:12   ` Dmitry Baryshkov
  2025-06-09 12:21 ` [PATCH v2 06/38] drm/msm/dp: move the pixel clock control to its own API Yongxing Mou
                   ` (34 subsequent siblings)
  39 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_ctrl.c    | 37 ++++++++++++++++++++-----------------
 drivers/gpu/drm/msm/dp/dp_ctrl.h    |  5 +++--
 drivers/gpu/drm/msm/dp/dp_display.c |  4 ++--
 3 files changed, 25 insertions(+), 21 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 1ce3cca121d0c56b493e282c76eb9202371564cf..aee8e37655812439dfb65ae90ccb61b14e6e261f 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -135,7 +135,8 @@ 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(struct msm_dp_ctrl_private *ctrl,
+				    struct msm_dp_panel *msm_dp_panel)
 {
 	u32 config = 0, tbd;
 	const u8 *dpcd = ctrl->panel->dpcd;
@@ -143,7 +144,7 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl)
 	/* 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)
+	if (msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
 		config |= DP_CONFIGURATION_CTRL_RGB_YUV; /* YUV420 */
 
 	/* Scrambler reset enable */
@@ -151,7 +152,7 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl)
 		config |= DP_CONFIGURATION_CTRL_ASSR;
 
 	tbd = msm_dp_link_get_test_bits_depth(ctrl->link,
-			ctrl->panel->msm_dp_mode.bpp);
+			msm_dp_panel->msm_dp_mode.bpp);
 
 	config |= tbd << DP_CONFIGURATION_CTRL_BPC_SHIFT;
 
@@ -174,20 +175,21 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl)
 	msm_dp_catalog_ctrl_config_ctrl(ctrl->catalog, config);
 }
 
-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)
 {
 	u32 cc, tb;
 
 	msm_dp_catalog_ctrl_lane_mapping(ctrl->catalog);
 	msm_dp_catalog_setup_peripheral_flush(ctrl->catalog);
 
-	msm_dp_ctrl_config_ctrl(ctrl);
+	msm_dp_ctrl_config_ctrl(ctrl, msm_dp_panel);
 
 	tb = msm_dp_link_get_test_bits_depth(ctrl->link,
-		ctrl->panel->msm_dp_mode.bpp);
+		msm_dp_panel->msm_dp_mode.bpp);
 	cc = msm_dp_link_get_colorimetry_config(ctrl->link);
 	msm_dp_catalog_ctrl_config_misc(ctrl->catalog, cc, tb);
-	msm_dp_panel_timing_cfg(ctrl->panel);
+	msm_dp_panel_timing_cfg(msm_dp_panel);
 }
 
 /*
@@ -1317,7 +1319,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(ctrl, ctrl->panel);
 
 	link_info.num_lanes = ctrl->link->link_params.num_lanes;
 	link_info.rate = ctrl->link->link_params.rate;
@@ -1735,7 +1737,8 @@ static bool msm_dp_ctrl_send_phy_test_pattern(struct msm_dp_ctrl_private *ctrl)
 	return success;
 }
 
-static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl)
+static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl,
+						struct msm_dp_panel *msm_dp_panel)
 {
 	int ret;
 	unsigned long pixel_rate;
@@ -1759,7 +1762,7 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
 		return ret;
 	}
 
-	pixel_rate = ctrl->panel->msm_dp_mode.drm_mode.clock;
+	pixel_rate = msm_dp_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);
@@ -1797,7 +1800,7 @@ void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl)
 
 	if (sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
 		drm_dbg_dp(ctrl->drm_dev, "PHY_TEST_PATTERN request\n");
-		if (msm_dp_ctrl_process_phy_test_request(ctrl)) {
+		if (msm_dp_ctrl_process_phy_test_request(ctrl, ctrl->panel)) {
 			DRM_ERROR("process phy_test_req failed\n");
 			return;
 		}
@@ -2015,7 +2018,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;
@@ -2028,9 +2031,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);
@@ -2058,12 +2061,12 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl)
 	 */
 	reinit_completion(&ctrl->video_comp);
 
-	msm_dp_ctrl_configure_source_params(ctrl);
+	msm_dp_ctrl_configure_source_params(ctrl, msm_dp_panel);
 
 	msm_dp_catalog_ctrl_config_msa(ctrl->catalog,
 		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_ctrl_setup_tr_unit(ctrl);
 
@@ -2081,7 +2084,7 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl)
 	return ret;
 }
 
-void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl)
+void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *dp_panel)
 {
 	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 edbe5766db74c4e4179141d895f9cb85e514f29b..fbe458c5a17bda0586097a61d925f608d99f9224 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -18,7 +18,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 *dp_ctrl, bool force_link_train);
 void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl);
 void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl);
@@ -41,7 +41,8 @@ void msm_dp_ctrl_config_psr(struct msm_dp_ctrl *msm_dp_ctrl);
 int msm_dp_ctrl_core_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl);
 void msm_dp_ctrl_core_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl);
 
-void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl);
+void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl,
+				   struct msm_dp_panel *msm_dp_panel);
 void msm_dp_ctrl_psm_config(struct msm_dp_ctrl *msm_dp_ctrl);
 void msm_dp_ctrl_reinit_phy(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 a5ca498cb970d0c6a4095b0b7fc6269c2dc3ad31..17ccea4047500848c4fb3eda87a10e29b18e0cfb 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -872,7 +872,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;
 
@@ -925,7 +925,7 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
 	if (!msm_dp_display->power_on)
 		return 0;
 
-	msm_dp_ctrl_clear_vsc_sdp_pkt(dp->ctrl);
+	msm_dp_ctrl_clear_vsc_sdp_pkt(dp->ctrl, dp->panel);
 
 	/* dongle is still connected but sinks are disconnected */
 	if (dp->link->sink_count == 0) {

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 06/38] drm/msm/dp: move the pixel clock control to its own API
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (4 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 05/38] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 13:16   ` Dmitry Baryshkov
  2025-06-09 12:21 ` [PATCH v2 07/38] drm/msm/dp: split dp_ctrl_off() into stream and link parts Yongxing Mou
                   ` (33 subsequent siblings)
  39 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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>

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 <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_ctrl.c | 98 +++++++++++++++++++++-------------------
 1 file changed, 52 insertions(+), 46 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index aee8e37655812439dfb65ae90ccb61b14e6e261f..ed00dd2538d98ddbc6bdcbd5fa154fd7043c48d6 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -97,7 +97,7 @@ struct msm_dp_ctrl_private {
 
 	bool core_clks_on;
 	bool link_clks_on;
-	bool stream_clks_on;
+	bool pixel_clks_on;
 };
 
 static int msm_dp_aux_link_configure(struct drm_dp_aux *aux,
@@ -1406,8 +1406,8 @@ int msm_dp_ctrl_core_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl)
 	ctrl->core_clks_on = true;
 
 	drm_dbg_dp(ctrl->drm_dev, "enable core clocks \n");
-	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
-		   str_on_off(ctrl->stream_clks_on),
+	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
+		   str_on_off(ctrl->pixel_clks_on),
 		   str_on_off(ctrl->link_clks_on),
 		   str_on_off(ctrl->core_clks_on));
 
@@ -1425,8 +1425,8 @@ void msm_dp_ctrl_core_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl)
 	ctrl->core_clks_on = false;
 
 	drm_dbg_dp(ctrl->drm_dev, "disable core clocks \n");
-	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
-		   str_on_off(ctrl->stream_clks_on),
+	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
+		   str_on_off(ctrl->pixel_clks_on),
 		   str_on_off(ctrl->link_clks_on),
 		   str_on_off(ctrl->core_clks_on));
 }
@@ -1456,8 +1456,8 @@ static int msm_dp_ctrl_link_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl)
 	ctrl->link_clks_on = true;
 
 	drm_dbg_dp(ctrl->drm_dev, "enable link clocks\n");
-	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
-		   str_on_off(ctrl->stream_clks_on),
+	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
+		   str_on_off(ctrl->pixel_clks_on),
 		   str_on_off(ctrl->link_clks_on),
 		   str_on_off(ctrl->core_clks_on));
 
@@ -1475,8 +1475,8 @@ static void msm_dp_ctrl_link_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl)
 	ctrl->link_clks_on = false;
 
 	drm_dbg_dp(ctrl->drm_dev, "disabled link clocks\n");
-	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
-		   str_on_off(ctrl->stream_clks_on),
+	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
+		   str_on_off(ctrl->pixel_clks_on),
 		   str_on_off(ctrl->link_clks_on),
 		   str_on_off(ctrl->core_clks_on));
 }
@@ -1737,6 +1737,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->pixel_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->pixel_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->pixel_clks_on) {
+		clk_disable_unprepare(ctrl->pixel_clk);
+		ctrl->pixel_clks_on = false;
+	}
+}
+
 static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl,
 						struct msm_dp_panel *msm_dp_panel)
 {
@@ -1763,22 +1799,7 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
 	}
 
 	pixel_rate = msm_dp_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);
 
@@ -1998,8 +2019,8 @@ int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_li
 		   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);
+		   "core_clk_on=%d link_clk_on=%d pixel_clks_on=%d\n",
+		   ctrl->core_clks_on, ctrl->link_clks_on, ctrl->pixel_clks_on);
 
 	if (!ctrl->link_clks_on) { /* link clk is off */
 		ret = msm_dp_ctrl_enable_mainlink_clocks(ctrl);
@@ -2038,21 +2059,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;
 	}
 
 	/*
@@ -2080,7 +2090,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;
 }
 
@@ -2154,10 +2163,7 @@ void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl)
 
 	msm_dp_catalog_ctrl_reset(ctrl->catalog);
 
-	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] 108+ messages in thread

* [PATCH v2 07/38] drm/msm/dp: split dp_ctrl_off() into stream and link parts
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (5 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 06/38] drm/msm/dp: move the pixel clock control to its own API Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 08/38] drm/msm/dp: make bridge helpers use dp_display to allow re-use Yongxing Mou
                   ` (32 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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>
---
 drivers/gpu/drm/msm/dp/dp_ctrl.c    | 29 +++--------------------------
 drivers/gpu/drm/msm/dp/dp_ctrl.h    |  2 +-
 drivers/gpu/drm/msm/dp/dp_display.c |  6 ++++--
 3 files changed, 8 insertions(+), 29 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index ed00dd2538d98ddbc6bdcbd5fa154fd7043c48d6..c990a7a835fc9645c2dd7e1ec8604e991baf754a 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -1761,7 +1761,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;
 
@@ -1790,7 +1790,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) {
@@ -2149,30 +2150,6 @@ void msm_dp_ctrl_off_link(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)
-{
-	struct msm_dp_ctrl_private *ctrl;
-	struct phy *phy;
-
-	ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
-	phy = ctrl->phy;
-
-	msm_dp_catalog_panel_disable_vsc_sdp(ctrl->catalog);
-
-	msm_dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);
-
-	msm_dp_catalog_ctrl_reset(ctrl->catalog);
-
-	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);
-
-	phy_power_off(phy);
-	drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n",
-			phy, phy->init_count, phy->power_count);
-}
-
 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 fbe458c5a17bda0586097a61d925f608d99f9224..f14778c5dfaad7e9cb697697d873977d109e6cfc 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -21,7 +21,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 *dp_ctrl, bool force_link_train);
 void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl);
-void msm_dp_ctrl_off(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 17ccea4047500848c4fb3eda87a10e29b18e0cfb..4076db4cac9cb889a12505159f4e710a92ffa8a6 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -930,11 +930,13 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
 	/* dongle is still connected but sinks are disconnected */
 	if (dp->link->sink_count == 0) {
 		msm_dp_ctrl_psm_config(dp->ctrl);
-		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 {
-		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] 108+ messages in thread

* [PATCH v2 08/38] drm/msm/dp: make bridge helpers use dp_display to allow re-use
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (6 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 07/38] drm/msm/dp: split dp_ctrl_off() into stream and link parts Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 09/38] drm/msm/dp: separate dp_display_prepare() into its own API Yongxing Mou
                   ` (31 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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>
---
 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 4076db4cac9cb889a12505159f4e710a92ffa8a6..c29ee79b4ef355e9d40ff41f8fff37e2136abb39 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -948,24 +948,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;
@@ -1535,11 +1532,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 *dp)
 {
-	struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge);
-	struct msm_dp *dp = msm_dp_bridge->msm_dp_display;
 	int rc = 0;
 
 	struct msm_dp_display_private *msm_dp_display;
@@ -1576,11 +1570,8 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
 	mutex_unlock(&msm_dp_display->event_mutex);
 }
 
-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);
@@ -1602,11 +1593,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;
 	u32 hpd_state;
 	struct msm_dp_display_private *msm_dp_display;
 
@@ -1639,12 +1627,10 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
 	mutex_unlock(&msm_dp_display->event_mutex);
 }
 
-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 2394840e9f28e136705004c3e6af93fbe13c33c5..8e3176e6a8af923110f414d1af5def909ef1e2c6 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -35,5 +35,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 f222d7ccaa88b7d0a2365bdd11842ef88535f663..c8ca8ba470cfe4fed618a3da05c1eb662257ad95 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -99,6 +99,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,
@@ -167,7 +214,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 d8c9b905f8bfb5abe47c1cb26d17bc605e3e1ba6..81e628c32279210f6e09f2fc68b2aeafd76fd1fe 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_drm.h
@@ -25,18 +25,6 @@ int msm_dp_bridge_init(struct msm_dp *msm_dp_display, struct drm_device *dev,
 		   struct drm_encoder *encoder,
 		   bool yuv_supported);
 
-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] 108+ messages in thread

* [PATCH v2 09/38] drm/msm/dp: separate dp_display_prepare() into its own API
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (7 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 08/38] drm/msm/dp: make bridge helpers use dp_display to allow re-use Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 10/38] drm/msm/dp: introduce the max_streams for dp controller Yongxing Mou
                   ` (30 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_display.c | 18 ++++++++++++++----
 drivers/gpu/drm/msm/dp/dp_display.h |  1 +
 drivers/gpu/drm/msm/dp/dp_drm.c     |  2 ++
 3 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index c29ee79b4ef355e9d40ff41f8fff37e2136abb39..7ed4034c89605596c874a6bf9a5a19586d6c13fb 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1532,10 +1532,9 @@ 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 *dp)
+void msm_dp_display_atomic_prepare(struct msm_dp *dp)
 {
 	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);
@@ -1548,10 +1547,21 @@ void msm_dp_display_atomic_enable(struct msm_dp *dp)
 	rc = msm_dp_display_prepare(msm_dp_display);
 	if (rc) {
 		DRM_ERROR("DP display prepare failed, rc=%d\n", rc);
-		mutex_unlock(&msm_dp_display->event_mutex);
-		return;
 	}
 
+	mutex_unlock(&msm_dp_display->event_mutex);
+}
+
+void msm_dp_display_atomic_enable(struct msm_dp *dp)
+{
+	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);
+
+	mutex_lock(&msm_dp_display->event_mutex);
+
 	if (dp->prepared) {
 		rc = msm_dp_display_enable(msm_dp_display);
 		if (rc)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 8e3176e6a8af923110f414d1af5def909ef1e2c6..4e3540f2eb21afb14583b0d521dd9817fefd2f70 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -37,6 +37,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 c8ca8ba470cfe4fed618a3da05c1eb662257ad95..f0144cf3c5876d94c44a44adad766f242609113e 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -105,6 +105,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);
 }
 
@@ -214,6 +215,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] 108+ messages in thread

* [PATCH v2 10/38] drm/msm/dp: introduce the max_streams for dp controller
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (8 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 09/38] drm/msm/dp: separate dp_display_prepare() into its own API Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 11/38] drm/msm/dp: introduce stream_id for each DP panel Yongxing Mou
                   ` (29 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 for each DP controller to specify the number
of supported MST streams. On most platforms, the supported MST streams
number is 2 or 4. For platforms that do not support MST, define
DEFAULT_STREAM_COUNT as 1. Also exist platform that DP0 supports 4
streams while DP1 support 2 streams.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_display.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 7ed4034c89605596c874a6bf9a5a19586d6c13fb..4e0213cae4aafb468681db27b3c3fef28d44b2c0 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -34,6 +34,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,
@@ -83,6 +84,7 @@ struct msm_dp_display_private {
 	bool core_initialized;
 	bool phy_initialized;
 	bool audio_supported;
+	bool mst_supported;
 
 	struct drm_device *drm_dev;
 
@@ -110,12 +112,14 @@ struct msm_dp_display_private {
 	bool wide_bus_supported;
 
 	struct msm_dp_audio *audio;
+	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[] = {
@@ -1330,6 +1334,14 @@ 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->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_init_sub_modules(dp);
 	if (rc) {
 		DRM_ERROR("init sub module failed\n");

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 11/38] drm/msm/dp: introduce stream_id for each DP panel
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (9 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 10/38] drm/msm/dp: introduce the max_streams for dp controller Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 12/38] drm/msm/dp: add support for programming p1/p2/p3 register block Yongxing Mou
                   ` (28 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_catalog.h |  9 ++++++
 drivers/gpu/drm/msm/dp/dp_ctrl.c    | 55 ++++++++++++++++++++++---------------
 drivers/gpu/drm/msm/dp/dp_ctrl.h    |  4 +--
 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   |  2 ++
 6 files changed, 72 insertions(+), 27 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index 6678b0ac9a67881244884d59487fa288d33d1be7..f9e7506afcf4f4d932665462ca316f6c66c662a6 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -31,6 +31,15 @@
 #define DP_HW_VERSION_1_0	0x10000000
 #define DP_HW_VERSION_1_2	0x10020000
 
+/* 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_catalog {
 	bool wide_bus_en;
 };
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index c990a7a835fc9645c2dd7e1ec8604e991baf754a..7db23968c4227557d3cea9bfbf5aa3c3e17a87d8 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -87,7 +87,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;
 
@@ -97,7 +97,7 @@ struct msm_dp_ctrl_private {
 
 	bool core_clks_on;
 	bool link_clks_on;
-	bool pixel_clks_on;
+	bool pixel_clks_on[DP_STREAM_MAX];
 };
 
 static int msm_dp_aux_link_configure(struct drm_dp_aux *aux,
@@ -1737,39 +1737,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->pixel_clks_on) {
+	if (ctrl->pixel_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->pixel_clks_on = true;
+		ctrl->pixel_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->pixel_clks_on) {
-		clk_disable_unprepare(ctrl->pixel_clk);
-		ctrl->pixel_clks_on = false;
+	if (ctrl->pixel_clks_on[stream_id]) {
+		clk_disable_unprepare(ctrl->pixel_clk[stream_id]);
+		ctrl->pixel_clks_on[stream_id] = false;
 	}
 }
 
@@ -1790,7 +1791,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, msm_dp_panel->stream_id);
 	msm_dp_ctrl_off_link(&ctrl->msm_dp_ctrl);
 
 	ret = msm_dp_ctrl_on_link(&ctrl->msm_dp_ctrl);
@@ -1800,7 +1801,7 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
 	}
 
 	pixel_rate = msm_dp_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, msm_dp_panel->stream_id);
 
 	msm_dp_ctrl_send_phy_test_pattern(ctrl);
 
@@ -2020,8 +2021,8 @@ int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_li
 		   ctrl->link->link_params.num_lanes);
 
 	drm_dbg_dp(ctrl->drm_dev,
-		   "core_clk_on=%d link_clk_on=%d pixel_clks_on=%d\n",
-		   ctrl->core_clks_on, ctrl->link_clks_on, ctrl->pixel_clks_on);
+		   "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);
@@ -2060,7 +2061,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;
@@ -2205,10 +2206,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);
@@ -2238,17 +2240,26 @@ 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;
 }
 
 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 msm_dp_catalog *catalog,
-			struct phy *phy)
+			struct phy *phy, int max_stream)
 {
 	struct msm_dp_ctrl_private *ctrl;
 	int ret;
@@ -2289,7 +2300,7 @@ struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link
 	ctrl->dev      = dev;
 	ctrl->phy      = phy;
 
-	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 f14778c5dfaad7e9cb697697d873977d109e6cfc..e20ba90dbd879ae3d6b1f5eb5922f969bc12d079 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -21,14 +21,14 @@ 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 *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);
 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 msm_dp_catalog *catalog,
-			struct phy *phy);
+			struct phy *phy, int max_stream);
 
 void msm_dp_ctrl_reset_irq_ctrl(struct msm_dp_ctrl *msm_dp_ctrl, bool enable);
 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 4e0213cae4aafb468681db27b3c3fef28d44b2c0..a13e4007858701787562eded7b6fc8f7171e167b 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -784,7 +784,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,
 			       dp->catalog,
-			       phy);
+			       phy, dp->max_stream);
 	if (IS_ERR(dp->ctrl)) {
 		rc = PTR_ERR(dp->ctrl);
 		DRM_ERROR("failed to initialize ctrl, rc = %d\n", rc);
@@ -934,12 +934,12 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
 	/* dongle is still connected but sinks are disconnected */
 	if (dp->link->sink_count == 0) {
 		msm_dp_ctrl_psm_config(dp->ctrl);
-		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);
 	} else {
-		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);
 	}
@@ -950,6 +950,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
@@ -1574,6 +1593,8 @@ void msm_dp_display_atomic_enable(struct msm_dp *dp)
 
 	mutex_lock(&msm_dp_display->event_mutex);
 
+	msm_dp_display_set_stream_id(dp, msm_dp_display->panel, 0);
+
 	if (dp->prepared) {
 		rc = msm_dp_display_enable(msm_dp_display);
 		if (rc)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 4e3540f2eb21afb14583b0d521dd9817fefd2f70..09b30596fd3366af55ae246e3a83ee9e88995382 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -45,5 +45,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 7f139478e1012d5b8f1f745f0de5fc3943745428..170f819644b473b454f88cd4e8762959f4513613 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -38,6 +38,8 @@ struct msm_dp_panel {
 	bool video_test;
 	bool vsc_sdp_supported;
 
+	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] 108+ messages in thread

* [PATCH v2 12/38] drm/msm/dp: add support for programming p1/p2/p3 register block
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (10 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 11/38] drm/msm/dp: introduce stream_id for each DP panel Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 13/38] drm/msm/dp: use stream_id to change offsets in dp_catalog Yongxing Mou
                   ` (27 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 support 4 streams MST and SA8775P support 2 x 2 MST. Add
support for other 3 pixels register block, and introduced
msm_dp_read_pn/msm_dp_write_pn to minimize repetitive code. All PCLK
share the same register definitions with different base addresses.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_catalog.c | 128 +++++++++++++++++++++---------------
 drivers/gpu/drm/msm/dp/dp_catalog.h |  18 +++--
 drivers/gpu/drm/msm/dp/dp_ctrl.c    |   2 +-
 drivers/gpu/drm/msm/dp/dp_display.c |   2 +-
 drivers/gpu/drm/msm/dp/dp_panel.c   |   8 ++-
 5 files changed, 93 insertions(+), 65 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index 7b7eadb2f83b169d8df27ee93589abe05b38f3ae..d2d975805de93e929cbdadb90609b6bf3e2cf1bf 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -60,8 +60,8 @@
 #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
 
 struct dss_io_region {
 	size_t len;
@@ -72,7 +72,7 @@ struct dss_io_data {
 	struct dss_io_region ahb;
 	struct dss_io_region aux;
 	struct dss_io_region link;
-	struct dss_io_region p0;
+	struct dss_io_region pixels[DP_STREAM_MAX];
 };
 
 struct msm_dp_catalog_private {
@@ -87,11 +87,17 @@ void msm_dp_catalog_snapshot(struct msm_dp_catalog *msm_dp_catalog, struct msm_d
 	struct msm_dp_catalog_private *catalog = container_of(msm_dp_catalog,
 			struct msm_dp_catalog_private, msm_dp_catalog);
 	struct dss_io_data *dss = &catalog->io;
+	char reg_name[6];
+	int i;
 
 	msm_disp_snapshot_add_block(disp_state, dss->ahb.len, dss->ahb.base, "dp_ahb");
 	msm_disp_snapshot_add_block(disp_state, dss->aux.len, dss->aux.base, "dp_aux");
 	msm_disp_snapshot_add_block(disp_state, dss->link.len, dss->link.base, "dp_link");
-	msm_disp_snapshot_add_block(disp_state, dss->p0.len, dss->p0.base, "dp_p0");
+	for (i = DP_STREAM_0; i < DP_STREAM_MAX; i++) {
+		sprintf(reg_name, "dp_p%d", i);
+		msm_disp_snapshot_add_block(disp_state, dss->pixels[i].len,
+			dss->pixels[i].base, reg_name);
+	}
 }
 
 static inline u32 msm_dp_read_aux(struct msm_dp_catalog_private *catalog, u32 offset)
@@ -124,24 +130,24 @@ static inline void msm_dp_write_ahb(struct msm_dp_catalog_private *catalog,
 	writel(data, catalog->io.ahb.base + offset);
 }
 
-static inline void msm_dp_write_p0(struct msm_dp_catalog_private *catalog,
-			       u32 offset, u32 data)
+static inline void msm_dp_write_pn(struct msm_dp_catalog_private *catalog,
+			       enum msm_dp_stream_id stream_id, 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, catalog->io.p0.base + offset);
+	writel(data, catalog->io.pixels[stream_id].base + offset);
 }
 
-static inline u32 msm_dp_read_p0(struct msm_dp_catalog_private *catalog,
-			       u32 offset)
+static inline u32 msm_dp_read_pn(struct msm_dp_catalog_private *catalog,
+			       enum msm_dp_stream_id stream_id, u32 offset)
 {
 	/*
 	 * To make sure interface reg writes happens before any other operation,
 	 * this function uses writel() instread of writel_relaxed()
 	 */
-	return readl_relaxed(catalog->io.p0.base + offset);
+	return  readl_relaxed(catalog->io.pixels[stream_id].base + offset);
 }
 
 static inline u32 msm_dp_read_link(struct msm_dp_catalog_private *catalog, u32 offset)
@@ -430,8 +436,8 @@ void msm_dp_catalog_setup_peripheral_flush(struct msm_dp_catalog *msm_dp_catalog
 }
 
 void msm_dp_catalog_ctrl_config_msa(struct msm_dp_catalog *msm_dp_catalog,
-					u32 rate, u32 stream_rate_khz,
-					bool is_ycbcr_420)
+				    enum msm_dp_stream_id stream_id, u32 rate, u32 stream_rate_khz,
+				    bool is_ycbcr_420)
 {
 	u32 pixel_m, pixel_n;
 	u32 mvid, nvid, pixel_div = 0, dispcc_input_rate;
@@ -486,7 +492,7 @@ void msm_dp_catalog_ctrl_config_msa(struct msm_dp_catalog *msm_dp_catalog,
 	drm_dbg_dp(catalog->drm_dev, "mvid=0x%x, nvid=0x%x\n", mvid, nvid);
 	msm_dp_write_link(catalog, REG_DP_SOFTWARE_MVID, mvid);
 	msm_dp_write_link(catalog, REG_DP_SOFTWARE_NVID, nvid);
-	msm_dp_write_p0(catalog, MMSS_DP_DSC_DTO, 0x0);
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_DSC_DTO, 0x0);
 }
 
 int msm_dp_catalog_ctrl_set_pattern_state_bit(struct msm_dp_catalog *msm_dp_catalog,
@@ -843,8 +849,9 @@ u32 msm_dp_catalog_ctrl_read_phy_pattern(struct msm_dp_catalog *msm_dp_catalog)
 }
 
 /* panel related catalog functions */
-int msm_dp_catalog_panel_timing_cfg(struct msm_dp_catalog *msm_dp_catalog, u32 total,
-				u32 sync_start, u32 width_blanking, u32 msm_dp_active)
+int msm_dp_catalog_panel_timing_cfg(struct msm_dp_catalog *msm_dp_catalog,
+				    enum msm_dp_stream_id stream_id, u32 total, u32 sync_start,
+				    u32 width_blanking, u32 msm_dp_active)
 {
 	struct msm_dp_catalog_private *catalog = container_of(msm_dp_catalog,
 				struct msm_dp_catalog_private, msm_dp_catalog);
@@ -855,7 +862,7 @@ int msm_dp_catalog_panel_timing_cfg(struct msm_dp_catalog *msm_dp_catalog, u32 t
 	msm_dp_write_link(catalog, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY, width_blanking);
 	msm_dp_write_link(catalog, REG_DP_ACTIVE_HOR_VER, msm_dp_active);
 
-	reg = msm_dp_read_p0(catalog, MMSS_DP_INTF_CONFIG);
+	reg = msm_dp_read_pn(catalog, stream_id, MMSS_DP_INTF_CONFIG);
 
 	if (msm_dp_catalog->wide_bus_en)
 		reg |= DP_INTF_CONFIG_DATABUS_WIDEN;
@@ -865,7 +872,7 @@ int msm_dp_catalog_panel_timing_cfg(struct msm_dp_catalog *msm_dp_catalog, u32 t
 
 	DRM_DEBUG_DP("wide_bus_en=%d reg=%#x\n", msm_dp_catalog->wide_bus_en, reg);
 
-	msm_dp_write_p0(catalog, MMSS_DP_INTF_CONFIG, reg);
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_INTF_CONFIG, reg);
 	return 0;
 }
 
@@ -963,7 +970,8 @@ void msm_dp_catalog_panel_disable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog)
 }
 
 void msm_dp_catalog_panel_tpg_enable(struct msm_dp_catalog *msm_dp_catalog,
-				struct drm_display_mode *drm_mode)
+				     enum msm_dp_stream_id stream_id,
+				     struct drm_display_mode *drm_mode)
 {
 	struct msm_dp_catalog_private *catalog = container_of(msm_dp_catalog,
 				struct msm_dp_catalog_private, msm_dp_catalog);
@@ -998,45 +1006,46 @@ void msm_dp_catalog_panel_tpg_enable(struct msm_dp_catalog *msm_dp_catalog,
 	display_hctl = (hsync_end_x << 16) | hsync_start_x;
 
 
-	msm_dp_write_p0(catalog, MMSS_DP_INTF_HSYNC_CTL, hsync_ctl);
-	msm_dp_write_p0(catalog, MMSS_DP_INTF_VSYNC_PERIOD_F0, vsync_period *
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_INTF_HSYNC_CTL, hsync_ctl);
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_INTF_VSYNC_PERIOD_F0, vsync_period *
 			hsync_period);
-	msm_dp_write_p0(catalog, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0, v_sync_width *
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0, v_sync_width *
 			hsync_period);
-	msm_dp_write_p0(catalog, MMSS_DP_INTF_VSYNC_PERIOD_F1, 0);
-	msm_dp_write_p0(catalog, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1, 0);
-	msm_dp_write_p0(catalog, MMSS_DP_INTF_DISPLAY_HCTL, display_hctl);
-	msm_dp_write_p0(catalog, MMSS_DP_INTF_ACTIVE_HCTL, 0);
-	msm_dp_write_p0(catalog, MMSS_INTF_DISPLAY_V_START_F0, display_v_start);
-	msm_dp_write_p0(catalog, MMSS_DP_INTF_DISPLAY_V_END_F0, display_v_end);
-	msm_dp_write_p0(catalog, MMSS_INTF_DISPLAY_V_START_F1, 0);
-	msm_dp_write_p0(catalog, MMSS_DP_INTF_DISPLAY_V_END_F1, 0);
-	msm_dp_write_p0(catalog, MMSS_DP_INTF_ACTIVE_V_START_F0, 0);
-	msm_dp_write_p0(catalog, MMSS_DP_INTF_ACTIVE_V_END_F0, 0);
-	msm_dp_write_p0(catalog, MMSS_DP_INTF_ACTIVE_V_START_F1, 0);
-	msm_dp_write_p0(catalog, MMSS_DP_INTF_ACTIVE_V_END_F1, 0);
-	msm_dp_write_p0(catalog, MMSS_DP_INTF_POLARITY_CTL, 0);
-
-	msm_dp_write_p0(catalog, MMSS_DP_TPG_MAIN_CONTROL,
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_INTF_VSYNC_PERIOD_F1, 0);
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1, 0);
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_INTF_DISPLAY_HCTL, display_hctl);
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_INTF_ACTIVE_HCTL, 0);
+	msm_dp_write_pn(catalog, stream_id, MMSS_INTF_DISPLAY_V_START_F0, display_v_start);
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_INTF_DISPLAY_V_END_F0, display_v_end);
+	msm_dp_write_pn(catalog, stream_id, MMSS_INTF_DISPLAY_V_START_F1, 0);
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_INTF_DISPLAY_V_END_F1, 0);
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_INTF_ACTIVE_V_START_F0, 0);
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_INTF_ACTIVE_V_END_F0, 0);
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_INTF_ACTIVE_V_START_F1, 0);
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_INTF_ACTIVE_V_END_F1, 0);
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_INTF_POLARITY_CTL, 0);
+
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_TPG_MAIN_CONTROL,
 				DP_TPG_CHECKERED_RECT_PATTERN);
-	msm_dp_write_p0(catalog, MMSS_DP_TPG_VIDEO_CONFIG,
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_TPG_VIDEO_CONFIG,
 				DP_TPG_VIDEO_CONFIG_BPP_8BIT |
 				DP_TPG_VIDEO_CONFIG_RGB);
-	msm_dp_write_p0(catalog, MMSS_DP_BIST_ENABLE,
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_BIST_ENABLE,
 				DP_BIST_ENABLE_DPBIST_EN);
-	msm_dp_write_p0(catalog, MMSS_DP_TIMING_ENGINE_EN,
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_TIMING_ENGINE_EN,
 				DP_TIMING_ENGINE_EN_EN);
 	drm_dbg_dp(catalog->drm_dev, "%s: enabled tpg\n", __func__);
 }
 
-void msm_dp_catalog_panel_tpg_disable(struct msm_dp_catalog *msm_dp_catalog)
+void msm_dp_catalog_panel_tpg_disable(struct msm_dp_catalog *msm_dp_catalog,
+				      enum msm_dp_stream_id stream_id)
 {
 	struct msm_dp_catalog_private *catalog = container_of(msm_dp_catalog,
 				struct msm_dp_catalog_private, msm_dp_catalog);
 
-	msm_dp_write_p0(catalog, MMSS_DP_TPG_MAIN_CONTROL, 0x0);
-	msm_dp_write_p0(catalog, MMSS_DP_BIST_ENABLE, 0x0);
-	msm_dp_write_p0(catalog, MMSS_DP_TIMING_ENGINE_EN, 0x0);
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_TPG_MAIN_CONTROL, 0x0);
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_BIST_ENABLE, 0x0);
+	msm_dp_write_pn(catalog, stream_id, MMSS_DP_TIMING_ENGINE_EN, 0x0);
 }
 
 static void __iomem *msm_dp_ioremap(struct platform_device *pdev, int idx, size_t *len)
@@ -1051,10 +1060,11 @@ static void __iomem *msm_dp_ioremap(struct platform_device *pdev, int idx, size_
 	return base;
 }
 
-static int msm_dp_catalog_get_io(struct msm_dp_catalog_private *catalog)
+static int msm_dp_catalog_get_io(struct msm_dp_catalog_private *catalog, int max_stream)
 {
 	struct platform_device *pdev = to_platform_device(catalog->dev);
 	struct dss_io_data *dss = &catalog->io;
+	int i;
 
 	dss->ahb.base = msm_dp_ioremap(pdev, 0, &dss->ahb.len);
 	if (IS_ERR(dss->ahb.base))
@@ -1070,7 +1080,7 @@ static int msm_dp_catalog_get_io(struct msm_dp_catalog_private *catalog)
 		 * lengths based on this single region.
 		 */
 		if (PTR_ERR(dss->aux.base) == -EINVAL) {
-			if (dss->ahb.len < DP_DEFAULT_P0_OFFSET + DP_DEFAULT_P0_SIZE) {
+			if (dss->ahb.len < DP_DEFAULT_PIXEL_OFFSET + DP_DEFAULT_PIXEL_SIZE) {
 				DRM_ERROR("legacy memory region not large enough\n");
 				return -EINVAL;
 			}
@@ -1080,8 +1090,11 @@ static int msm_dp_catalog_get_io(struct msm_dp_catalog_private *catalog)
 			dss->aux.len = DP_DEFAULT_AUX_SIZE;
 			dss->link.base = dss->ahb.base + DP_DEFAULT_LINK_OFFSET;
 			dss->link.len = DP_DEFAULT_LINK_SIZE;
-			dss->p0.base = dss->ahb.base + DP_DEFAULT_P0_OFFSET;
-			dss->p0.len = DP_DEFAULT_P0_SIZE;
+			for (i = DP_STREAM_0; i < max_stream; i++) {
+				dss->pixels[i].base =
+					dss->ahb.base + (i + 1) * DP_DEFAULT_PIXEL_OFFSET;
+				dss->pixels[i].len = DP_DEFAULT_PIXEL_SIZE;
+			}
 		} else {
 			DRM_ERROR("unable to remap aux region: %pe\n", dss->aux.base);
 			return PTR_ERR(dss->aux.base);
@@ -1093,17 +1106,26 @@ static int msm_dp_catalog_get_io(struct msm_dp_catalog_private *catalog)
 			return PTR_ERR(dss->link.base);
 		}
 
-		dss->p0.base = msm_dp_ioremap(pdev, 3, &dss->p0.len);
-		if (IS_ERR(dss->p0.base)) {
-			DRM_ERROR("unable to remap p0 region: %pe\n", dss->p0.base);
-			return PTR_ERR(dss->p0.base);
+		dss->pixels[0].base = msm_dp_ioremap(pdev, 3, &dss->pixels[0].len);
+		if (IS_ERR(dss->pixels[0].base)) {
+			DRM_ERROR("unable to remap p0 region: %pe\n", dss->pixels[0].base);
+			return PTR_ERR(dss->pixels[0].base);
+		}
+
+		for (i = DP_STREAM_1; i < max_stream; i++) {
+			/* pixels clk reg index start from 3*/
+			dss->pixels[i].base = msm_dp_ioremap(pdev, i + 3, &dss->pixels[i].len);
+
+			if (IS_ERR(dss->pixels[i].base))
+				DRM_DEBUG_DP("unable to remap p%d region: %pe\n", i,
+					      dss->pixels[i].base);
 		}
 	}
 
 	return 0;
 }
 
-struct msm_dp_catalog *msm_dp_catalog_get(struct device *dev)
+struct msm_dp_catalog *msm_dp_catalog_get(struct device *dev, int max_stream)
 {
 	struct msm_dp_catalog_private *catalog;
 	int ret;
@@ -1114,7 +1136,7 @@ struct msm_dp_catalog *msm_dp_catalog_get(struct device *dev)
 
 	catalog->dev = dev;
 
-	ret = msm_dp_catalog_get_io(catalog);
+	ret = msm_dp_catalog_get_io(catalog, max_stream);
 	if (ret)
 		return ERR_PTR(ret);
 
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index f9e7506afcf4f4d932665462ca316f6c66c662a6..9bfb83873ce8749bec173a2bc2366c429f91354a 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -67,8 +67,9 @@ void msm_dp_catalog_ctrl_mainlink_ctrl(struct msm_dp_catalog *msm_dp_catalog, bo
 void msm_dp_catalog_ctrl_psr_mainlink_enable(struct msm_dp_catalog *msm_dp_catalog, bool enable);
 void msm_dp_catalog_setup_peripheral_flush(struct msm_dp_catalog *msm_dp_catalog);
 void msm_dp_catalog_ctrl_config_misc(struct msm_dp_catalog *msm_dp_catalog, u32 cc, u32 tb);
-void msm_dp_catalog_ctrl_config_msa(struct msm_dp_catalog *msm_dp_catalog, u32 rate,
-				u32 stream_rate_khz, bool is_ycbcr_420);
+void msm_dp_catalog_ctrl_config_msa(struct msm_dp_catalog *msm_dp_catalog,
+				    enum msm_dp_stream_id stream_id, u32 rate, u32 stream_rate_khz,
+				    bool is_ycbcr_420);
 int msm_dp_catalog_ctrl_set_pattern_state_bit(struct msm_dp_catalog *msm_dp_catalog, u32 pattern);
 u32 msm_dp_catalog_hw_revision(const struct msm_dp_catalog *msm_dp_catalog);
 void msm_dp_catalog_ctrl_reset(struct msm_dp_catalog *msm_dp_catalog);
@@ -93,15 +94,18 @@ void msm_dp_catalog_ctrl_send_phy_pattern(struct msm_dp_catalog *msm_dp_catalog,
 u32 msm_dp_catalog_ctrl_read_phy_pattern(struct msm_dp_catalog *msm_dp_catalog);
 
 /* DP Panel APIs */
-int msm_dp_catalog_panel_timing_cfg(struct msm_dp_catalog *msm_dp_catalog, u32 total,
-				u32 sync_start, u32 width_blanking, u32 msm_dp_active);
+int msm_dp_catalog_panel_timing_cfg(struct msm_dp_catalog *msm_dp_catalog,
+				    enum msm_dp_stream_id stream_id, u32 total, u32 sync_start,
+				    u32 width_blanking, u32 msm_dp_active);
 void msm_dp_catalog_panel_enable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog, struct dp_sdp *vsc_sdp);
 void msm_dp_catalog_panel_disable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog);
 void msm_dp_catalog_panel_tpg_enable(struct msm_dp_catalog *msm_dp_catalog,
-				struct drm_display_mode *drm_mode);
-void msm_dp_catalog_panel_tpg_disable(struct msm_dp_catalog *msm_dp_catalog);
+				     enum msm_dp_stream_id stream_id,
+				     struct drm_display_mode *drm_mode);
+void msm_dp_catalog_panel_tpg_disable(struct msm_dp_catalog *msm_dp_catalog,
+				      enum msm_dp_stream_id stream_id);
 
-struct msm_dp_catalog *msm_dp_catalog_get(struct device *dev);
+struct msm_dp_catalog *msm_dp_catalog_get(struct device *dev, int max_stream);
 
 /* DP Audio APIs */
 void msm_dp_catalog_write_audio_stream(struct msm_dp_catalog *msm_dp_catalog,
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 7db23968c4227557d3cea9bfbf5aa3c3e17a87d8..594dd6fe9381403b8ab085441276d2ce26612efe 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -2075,7 +2075,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_catalog_ctrl_config_msa(ctrl->catalog,
+	msm_dp_catalog_ctrl_config_msa(ctrl->catalog, msm_dp_panel->stream_id,
 		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_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index a13e4007858701787562eded7b6fc8f7171e167b..76d07685f5a89109723b68ba9d97c0b0332b18aa 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -748,7 +748,7 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp)
 		goto error;
 	}
 
-	dp->catalog = msm_dp_catalog_get(dev);
+	dp->catalog = msm_dp_catalog_get(dev, dp->max_stream);
 	if (IS_ERR(dp->catalog)) {
 		rc = PTR_ERR(dp->catalog);
 		DRM_ERROR("failed to initialize catalog, rc = %d\n", rc);
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index d9041e235104a74b3cc50ff2e307eae0c4301ef3..fb9216673f0d39d10af864346ebfa884dbe1f499 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -285,12 +285,13 @@ void msm_dp_panel_tpg_config(struct msm_dp_panel *msm_dp_panel, bool enable)
 	}
 
 	if (!enable) {
-		msm_dp_catalog_panel_tpg_disable(catalog);
+		msm_dp_catalog_panel_tpg_disable(catalog, msm_dp_panel->stream_id);
 		return;
 	}
 
 	drm_dbg_dp(panel->drm_dev, "calling catalog tpg_enable\n");
-	msm_dp_catalog_panel_tpg_enable(catalog, &panel->msm_dp_panel.msm_dp_mode.drm_mode);
+	msm_dp_catalog_panel_tpg_enable(catalog, msm_dp_panel->stream_id,
+					&msm_dp_panel->msm_dp_mode.drm_mode);
 }
 
 static int msm_dp_panel_setup_vsc_sdp_yuv_420(struct msm_dp_panel *msm_dp_panel)
@@ -395,7 +396,8 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel)
 
 	msm_dp_active = data;
 
-	msm_dp_catalog_panel_timing_cfg(catalog, total, sync_start, width_blanking, msm_dp_active);
+	msm_dp_catalog_panel_timing_cfg(catalog, msm_dp_panel->stream_id, total, sync_start,
+					width_blanking, msm_dp_active);
 
 	if (msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
 		msm_dp_panel_setup_vsc_sdp_yuv_420(msm_dp_panel);

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 13/38] drm/msm/dp: use stream_id to change offsets in dp_catalog
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (11 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 12/38] drm/msm/dp: add support for programming p1/p2/p3 register block Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 14/38] drm/msm/dp: Add catalog support for 3rd/4th stream MST Yongxing Mou
                   ` (26 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_catalog.c | 106 +++++++++++++++++++++++++-----------
 drivers/gpu/drm/msm/dp/dp_catalog.h |  12 ++--
 drivers/gpu/drm/msm/dp/dp_ctrl.c    |   7 ++-
 drivers/gpu/drm/msm/dp/dp_panel.c   |   3 +-
 drivers/gpu/drm/msm/dp/dp_reg.h     |  14 ++++-
 5 files changed, 102 insertions(+), 40 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index d2d975805de93e929cbdadb90609b6bf3e2cf1bf..0f23f69765f63a25650ed6612f1b83338483a142 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -319,14 +319,20 @@ void msm_dp_catalog_ctrl_state_ctrl(struct msm_dp_catalog *msm_dp_catalog, u32 s
 	msm_dp_write_link(catalog, REG_DP_STATE_CTRL, state);
 }
 
-void msm_dp_catalog_ctrl_config_ctrl(struct msm_dp_catalog *msm_dp_catalog, u32 cfg)
+void msm_dp_catalog_ctrl_config_ctrl(struct msm_dp_catalog *msm_dp_catalog,
+				     enum msm_dp_stream_id stream_id, u32 cfg)
 {
 	struct msm_dp_catalog_private *catalog = container_of(msm_dp_catalog,
 				struct msm_dp_catalog_private, msm_dp_catalog);
+	u32 reg_offset = 0;
 
-	drm_dbg_dp(catalog->drm_dev, "DP_CONFIGURATION_CTRL=0x%x\n", cfg);
+	if (stream_id == DP_STREAM_1)
+		reg_offset = REG_DP1_CONFIGURATION_CTRL - REG_DP_CONFIGURATION_CTRL;
 
-	msm_dp_write_link(catalog, REG_DP_CONFIGURATION_CTRL, cfg);
+	msm_dp_write_link(catalog, REG_DP_CONFIGURATION_CTRL + reg_offset, cfg);
+
+	drm_dbg_dp(catalog->drm_dev, "stream_id:%d DP_CONFIGURATION_CTRL=0x%x\n",
+		stream_id, cfg);
 }
 
 void msm_dp_catalog_ctrl_lane_mapping(struct msm_dp_catalog *msm_dp_catalog)
@@ -398,14 +404,19 @@ void msm_dp_catalog_ctrl_mainlink_ctrl(struct msm_dp_catalog *msm_dp_catalog,
 }
 
 void msm_dp_catalog_ctrl_config_misc(struct msm_dp_catalog *msm_dp_catalog,
-					u32 colorimetry_cfg,
-					u32 test_bits_depth)
+				     enum msm_dp_stream_id stream_id, u32 colorimetry_cfg,
+				     u32 test_bits_depth)
 {
 	u32 misc_val;
+	u32 reg_offset = 0;
+
 	struct msm_dp_catalog_private *catalog = container_of(msm_dp_catalog,
 				struct msm_dp_catalog_private, msm_dp_catalog);
 
-	misc_val = msm_dp_read_link(catalog, REG_DP_MISC1_MISC0);
+	if (stream_id == DP_STREAM_1)
+		reg_offset = REG_DP1_MISC1_MISC0 - REG_DP_MISC1_MISC0;
+
+	misc_val = msm_dp_read_link(catalog, REG_DP_MISC1_MISC0 + reg_offset);
 
 	/* clear bpp bits */
 	misc_val &= ~(0x07 << DP_MISC0_TEST_BITS_DEPTH_SHIFT);
@@ -415,7 +426,7 @@ void msm_dp_catalog_ctrl_config_misc(struct msm_dp_catalog *msm_dp_catalog,
 	misc_val |= DP_MISC0_SYNCHRONOUS_CLK;
 
 	drm_dbg_dp(catalog->drm_dev, "misc settings = 0x%x\n", misc_val);
-	msm_dp_write_link(catalog, REG_DP_MISC1_MISC0, misc_val);
+	msm_dp_write_link(catalog, REG_DP_MISC1_MISC0 + reg_offset, misc_val);
 }
 
 void msm_dp_catalog_setup_peripheral_flush(struct msm_dp_catalog *msm_dp_catalog)
@@ -445,10 +456,16 @@ void msm_dp_catalog_ctrl_config_msa(struct msm_dp_catalog *msm_dp_catalog,
 	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;
 
 	struct msm_dp_catalog_private *catalog = container_of(msm_dp_catalog,
 				struct msm_dp_catalog_private, msm_dp_catalog);
 
+	if (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;
 	else if (rate == 162000 || rate == 270000)
@@ -490,8 +507,8 @@ void msm_dp_catalog_ctrl_config_msa(struct msm_dp_catalog *msm_dp_catalog,
 		nvid *= 3;
 
 	drm_dbg_dp(catalog->drm_dev, "mvid=0x%x, nvid=0x%x\n", mvid, nvid);
-	msm_dp_write_link(catalog, REG_DP_SOFTWARE_MVID, mvid);
-	msm_dp_write_link(catalog, REG_DP_SOFTWARE_NVID, nvid);
+	msm_dp_write_link(catalog, REG_DP_SOFTWARE_MVID + mvid_reg_off, mvid);
+	msm_dp_write_link(catalog, REG_DP_SOFTWARE_NVID + nvid_reg_off, nvid);
 	msm_dp_write_pn(catalog, stream_id, MMSS_DP_DSC_DTO, 0x0);
 }
 
@@ -856,11 +873,15 @@ int msm_dp_catalog_panel_timing_cfg(struct msm_dp_catalog *msm_dp_catalog,
 	struct msm_dp_catalog_private *catalog = container_of(msm_dp_catalog,
 				struct msm_dp_catalog_private, msm_dp_catalog);
 	u32 reg;
+	u32 offset = 0;
 
-	msm_dp_write_link(catalog, REG_DP_TOTAL_HOR_VER, total);
-	msm_dp_write_link(catalog, REG_DP_START_HOR_VER_FROM_SYNC, sync_start);
-	msm_dp_write_link(catalog, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY, width_blanking);
-	msm_dp_write_link(catalog, REG_DP_ACTIVE_HOR_VER, msm_dp_active);
+	if (stream_id == DP_STREAM_1)
+		offset = REG_DP1_TOTAL_HOR_VER - REG_DP_TOTAL_HOR_VER;
+
+	msm_dp_write_link(catalog, REG_DP_TOTAL_HOR_VER + offset, total);
+	msm_dp_write_link(catalog, REG_DP_START_HOR_VER_FROM_SYNC + offset, sync_start);
+	msm_dp_write_link(catalog, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY + offset, width_blanking);
+	msm_dp_write_link(catalog, REG_DP_ACTIVE_HOR_VER + offset, msm_dp_active);
 
 	reg = msm_dp_read_pn(catalog, stream_id, MMSS_DP_INTF_CONFIG);
 
@@ -876,59 +897,80 @@ int msm_dp_catalog_panel_timing_cfg(struct msm_dp_catalog *msm_dp_catalog,
 	return 0;
 }
 
-static void msm_dp_catalog_panel_send_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog, struct dp_sdp *vsc_sdp)
+static void msm_dp_catalog_panel_send_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog,
+					      enum msm_dp_stream_id stream_id,
+					      struct dp_sdp *vsc_sdp)
 {
 	struct msm_dp_catalog_private *catalog;
 	u32 header[2];
 	u32 val;
 	int i;
+	u32 msm_dp_generic_offset = 0;
 
 	catalog = container_of(msm_dp_catalog, struct msm_dp_catalog_private, msm_dp_catalog);
 
+	if (stream_id == DP_STREAM_1)
+		msm_dp_generic_offset = MMSS_DP1_GENERIC0_0 - MMSS_DP_GENERIC0_0;
+
 	msm_dp_utils_pack_sdp_header(&vsc_sdp->sdp_header, header);
 
-	msm_dp_write_link(catalog, MMSS_DP_GENERIC0_0, header[0]);
-	msm_dp_write_link(catalog, MMSS_DP_GENERIC0_1, header[1]);
+	msm_dp_write_link(catalog, MMSS_DP_GENERIC0_0 + msm_dp_generic_offset, header[0]);
+	msm_dp_write_link(catalog, MMSS_DP_GENERIC0_1 + msm_dp_generic_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(catalog, MMSS_DP_GENERIC0_2 + i, val);
+		msm_dp_write_link(catalog, MMSS_DP_GENERIC0_2 + i + msm_dp_generic_offset, val);
 	}
 }
 
-static void msm_dp_catalog_panel_update_sdp(struct msm_dp_catalog *msm_dp_catalog)
+static void msm_dp_catalog_panel_update_sdp(struct msm_dp_catalog *msm_dp_catalog,
+					    enum msm_dp_stream_id stream_id)
 {
 	struct msm_dp_catalog_private *catalog;
 	u32 hw_revision;
+	u32 sdp_cfg3_offset = 0;
 
 	catalog = container_of(msm_dp_catalog, struct msm_dp_catalog_private, msm_dp_catalog);
 
+	if (stream_id == DP_STREAM_1)
+		sdp_cfg3_offset = MMSS_DP1_SDP_CFG3 - MMSS_DP_SDP_CFG3;
+
 	hw_revision = msm_dp_catalog_hw_revision(msm_dp_catalog);
 	if (hw_revision < DP_HW_VERSION_1_2 && hw_revision >= DP_HW_VERSION_1_0) {
-		msm_dp_write_link(catalog, MMSS_DP_SDP_CFG3, 0x01);
-		msm_dp_write_link(catalog, MMSS_DP_SDP_CFG3, 0x00);
+		msm_dp_write_link(catalog, MMSS_DP_SDP_CFG3 + sdp_cfg3_offset, 0x01);
+		msm_dp_write_link(catalog, MMSS_DP_SDP_CFG3 + sdp_cfg3_offset, 0x00);
 	}
 }
 
-void msm_dp_catalog_panel_enable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog, struct dp_sdp *vsc_sdp)
+void msm_dp_catalog_panel_enable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog,
+					 enum msm_dp_stream_id stream_id, struct dp_sdp *vsc_sdp)
 {
 	struct msm_dp_catalog_private *catalog;
 	u32 cfg, cfg2, misc;
+	u32 misc_reg_offset = 0;
+	u32 sdp_cfg_offset = 0;
+	u32 sdp_cfg2_offset = 0;
 
 	catalog = container_of(msm_dp_catalog, struct msm_dp_catalog_private, msm_dp_catalog);
 
-	cfg = msm_dp_read_link(catalog, MMSS_DP_SDP_CFG);
-	cfg2 = msm_dp_read_link(catalog, MMSS_DP_SDP_CFG2);
-	misc = msm_dp_read_link(catalog, REG_DP_MISC1_MISC0);
+	if (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(catalog, MMSS_DP_SDP_CFG + sdp_cfg_offset);
+	cfg2 = msm_dp_read_link(catalog, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset);
+	misc = msm_dp_read_link(catalog, REG_DP_MISC1_MISC0 + misc_reg_offset);
 
 	cfg |= GEN0_SDP_EN;
-	msm_dp_write_link(catalog, MMSS_DP_SDP_CFG, cfg);
+	msm_dp_write_link(catalog, MMSS_DP_SDP_CFG + sdp_cfg_offset, cfg);
 
 	cfg2 |= GENERIC0_SDPSIZE_VALID;
-	msm_dp_write_link(catalog, MMSS_DP_SDP_CFG2, cfg2);
+	msm_dp_write_link(catalog, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset, cfg2);
 
-	msm_dp_catalog_panel_send_vsc_sdp(msm_dp_catalog, vsc_sdp);
+	msm_dp_catalog_panel_send_vsc_sdp(msm_dp_catalog, stream_id, vsc_sdp);
 
 	/* indicates presence of VSC (BIT(6) of MISC1) */
 	misc |= DP_MISC1_VSC_SDP;
@@ -936,12 +978,14 @@ void msm_dp_catalog_panel_enable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog,
 	drm_dbg_dp(catalog->drm_dev, "vsc sdp enable=1\n");
 
 	pr_debug("misc settings = 0x%x\n", misc);
-	msm_dp_write_link(catalog, REG_DP_MISC1_MISC0, misc);
 
-	msm_dp_catalog_panel_update_sdp(msm_dp_catalog);
+	msm_dp_write_link(catalog, REG_DP_MISC1_MISC0 + misc_reg_offset, misc);
+
+	msm_dp_catalog_panel_update_sdp(msm_dp_catalog, stream_id);
 }
 
-void msm_dp_catalog_panel_disable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog)
+void msm_dp_catalog_panel_disable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog,
+					  enum msm_dp_stream_id stream_id)
 {
 	struct msm_dp_catalog_private *catalog;
 	u32 cfg, cfg2, misc;
@@ -966,7 +1010,7 @@ void msm_dp_catalog_panel_disable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog)
 	pr_debug("misc settings = 0x%x\n", misc);
 	msm_dp_write_link(catalog, REG_DP_MISC1_MISC0, misc);
 
-	msm_dp_catalog_panel_update_sdp(msm_dp_catalog);
+	msm_dp_catalog_panel_update_sdp(msm_dp_catalog, stream_id);
 }
 
 void msm_dp_catalog_panel_tpg_enable(struct msm_dp_catalog *msm_dp_catalog,
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index 9bfb83873ce8749bec173a2bc2366c429f91354a..ef0e3fadcd04ae499d6340d05fd74392235bed76 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -61,12 +61,14 @@ u32 msm_dp_catalog_aux_get_irq(struct msm_dp_catalog *msm_dp_catalog);
 
 /* DP Controller APIs */
 void msm_dp_catalog_ctrl_state_ctrl(struct msm_dp_catalog *msm_dp_catalog, u32 state);
-void msm_dp_catalog_ctrl_config_ctrl(struct msm_dp_catalog *msm_dp_catalog, u32 config);
+void msm_dp_catalog_ctrl_config_ctrl(struct msm_dp_catalog *msm_dp_catalog,
+				     enum msm_dp_stream_id stream_id, u32 config);
 void msm_dp_catalog_ctrl_lane_mapping(struct msm_dp_catalog *msm_dp_catalog);
 void msm_dp_catalog_ctrl_mainlink_ctrl(struct msm_dp_catalog *msm_dp_catalog, bool enable);
 void msm_dp_catalog_ctrl_psr_mainlink_enable(struct msm_dp_catalog *msm_dp_catalog, bool enable);
 void msm_dp_catalog_setup_peripheral_flush(struct msm_dp_catalog *msm_dp_catalog);
-void msm_dp_catalog_ctrl_config_misc(struct msm_dp_catalog *msm_dp_catalog, u32 cc, u32 tb);
+void msm_dp_catalog_ctrl_config_misc(struct msm_dp_catalog *msm_dp_catalog,
+				     enum msm_dp_stream_id stream_id,  u32 cc, u32 tb);
 void msm_dp_catalog_ctrl_config_msa(struct msm_dp_catalog *msm_dp_catalog,
 				    enum msm_dp_stream_id stream_id, u32 rate, u32 stream_rate_khz,
 				    bool is_ycbcr_420);
@@ -97,8 +99,10 @@ u32 msm_dp_catalog_ctrl_read_phy_pattern(struct msm_dp_catalog *msm_dp_catalog);
 int msm_dp_catalog_panel_timing_cfg(struct msm_dp_catalog *msm_dp_catalog,
 				    enum msm_dp_stream_id stream_id, u32 total, u32 sync_start,
 				    u32 width_blanking, u32 msm_dp_active);
-void msm_dp_catalog_panel_enable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog, struct dp_sdp *vsc_sdp);
-void msm_dp_catalog_panel_disable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog);
+void msm_dp_catalog_panel_enable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog,
+					 enum msm_dp_stream_id stream_id, struct dp_sdp *vsc_sdp);
+void msm_dp_catalog_panel_disable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog,
+					  enum msm_dp_stream_id stream_id);
 void msm_dp_catalog_panel_tpg_enable(struct msm_dp_catalog *msm_dp_catalog,
 				     enum msm_dp_stream_id stream_id,
 				     struct drm_display_mode *drm_mode);
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 594dd6fe9381403b8ab085441276d2ce26612efe..6c3c27b48b3b13f205ce28b823ae26d5dc7d9a10 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -172,7 +172,7 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl,
 	if (ctrl->panel->psr_cap.version)
 		config |= DP_CONFIGURATION_CTRL_SEND_VSC;
 
-	msm_dp_catalog_ctrl_config_ctrl(ctrl->catalog, config);
+	msm_dp_catalog_ctrl_config_ctrl(ctrl->catalog, msm_dp_panel->stream_id, config);
 }
 
 static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl,
@@ -188,7 +188,7 @@ static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl
 	tb = msm_dp_link_get_test_bits_depth(ctrl->link,
 		msm_dp_panel->msm_dp_mode.bpp);
 	cc = msm_dp_link_get_colorimetry_config(ctrl->link);
-	msm_dp_catalog_ctrl_config_misc(ctrl->catalog, cc, tb);
+	msm_dp_catalog_ctrl_config_misc(ctrl->catalog, msm_dp_panel->stream_id, cc, tb);
 	msm_dp_panel_timing_cfg(msm_dp_panel);
 }
 
@@ -2100,7 +2100,8 @@ void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_d
 	struct msm_dp_ctrl_private *ctrl;
 
 	ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
-	msm_dp_catalog_panel_disable_vsc_sdp(ctrl->catalog);
+	msm_dp_catalog_panel_disable_vsc_sdp(ctrl->catalog, dp_panel->stream_id);
+
 }
 
 void msm_dp_ctrl_psm_config(struct msm_dp_ctrl *msm_dp_ctrl)
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index fb9216673f0d39d10af864346ebfa884dbe1f499..a07237b41db148c02fdfaef18aab5cbf71779b57 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -310,6 +310,7 @@ static int msm_dp_panel_setup_vsc_sdp_yuv_420(struct msm_dp_panel *msm_dp_panel)
 
 	panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
 	catalog = panel->catalog;
+
 	msm_dp_mode = &msm_dp_panel->msm_dp_mode;
 
 	memset(&vsc_sdp_data, 0, sizeof(vsc_sdp_data));
@@ -336,7 +337,7 @@ static int msm_dp_panel_setup_vsc_sdp_yuv_420(struct msm_dp_panel *msm_dp_panel)
 		return len;
 	}
 
-	msm_dp_catalog_panel_enable_vsc_sdp(catalog, &vsc_sdp);
+	msm_dp_catalog_panel_enable_vsc_sdp(catalog, msm_dp_panel->stream_id, &vsc_sdp);
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index 3835c7f5cb984406f8fc52ea765ef2315e0d175b..623a58163f7028dcaac2f4d4416ee091db9f5024 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -122,6 +122,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)
@@ -138,13 +139,17 @@
 #define DP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT	(0x0D)
 
 #define REG_DP_SOFTWARE_MVID			(0x00000010)
+#define REG_DP1_SOFTWARE_MVID			(0x00000414)
 #define REG_DP_SOFTWARE_NVID			(0x00000018)
+#define REG_DP1_SOFTWARE_NVID			(0x00000418)
 #define REG_DP_TOTAL_HOR_VER			(0x0000001C)
+#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)
@@ -211,8 +216,11 @@
 #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)
@@ -221,6 +229,8 @@
 #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)
@@ -270,6 +280,8 @@
 #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)
 #define MMSS_DP_VSCEXT_2			(0x000002D8)

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 14/38] drm/msm/dp: Add catalog support for 3rd/4th stream MST
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (12 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 13/38] drm/msm/dp: use stream_id to change offsets in dp_catalog Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 15/38] drm/msm/dp: add support to send ACT packets for MST Yongxing Mou
                   ` (25 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 the 4 streams MST, the link clocks for stream 3/4
are controlled by MST_2_LCLK and MST_3_LCLK. They share the same
definition but have different base addresses. Add corresponding
catalog support to be able to proggram this block.

Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_catalog.c | 182 ++++++++++++++++++++++++++++++------
 drivers/gpu/drm/msm/dp/dp_reg.h     |  14 +++
 2 files changed, 169 insertions(+), 27 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index 0f23f69765f63a25650ed6612f1b83338483a142..07d06cd36eea5ea1dd90daf01422b8ecdffb13fe 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -72,6 +72,8 @@ struct dss_io_data {
 	struct dss_io_region ahb;
 	struct dss_io_region aux;
 	struct dss_io_region link;
+	struct dss_io_region mst2_link;
+	struct dss_io_region mst3_link;
 	struct dss_io_region pixels[DP_STREAM_MAX];
 };
 
@@ -93,6 +95,10 @@ void msm_dp_catalog_snapshot(struct msm_dp_catalog *msm_dp_catalog, struct msm_d
 	msm_disp_snapshot_add_block(disp_state, dss->ahb.len, dss->ahb.base, "dp_ahb");
 	msm_disp_snapshot_add_block(disp_state, dss->aux.len, dss->aux.base, "dp_aux");
 	msm_disp_snapshot_add_block(disp_state, dss->link.len, dss->link.base, "dp_link");
+	msm_disp_snapshot_add_block(disp_state, dss->mst2_link.len, dss->mst2_link.base,
+				    "dp_mst2_link");
+	msm_disp_snapshot_add_block(disp_state, dss->mst3_link.len, dss->mst3_link.base,
+				    "dp_mst3_link");
 	for (i = DP_STREAM_0; i < DP_STREAM_MAX; i++) {
 		sprintf(reg_name, "dp_p%d", i);
 		msm_disp_snapshot_add_block(disp_state, dss->pixels[i].len,
@@ -165,6 +171,36 @@ static inline void msm_dp_write_link(struct msm_dp_catalog_private *catalog,
 	writel(data, catalog->io.link.base + offset);
 }
 
+static inline u32 msm_dp_read_mst_link(struct msm_dp_catalog_private *catalog,
+				       enum msm_dp_stream_id stream_id, u32 offset)
+{
+	u32 ret = 0;
+
+	if (stream_id == DP_STREAM_2)
+		ret = readl_relaxed(catalog->io.mst2_link.base + offset);
+	else if (stream_id == DP_STREAM_3)
+		ret = readl_relaxed(catalog->io.mst3_link.base + offset);
+	else
+		DRM_ERROR("invalid stream id");
+
+	return ret;
+}
+
+static inline void msm_dp_write_mst_link(struct msm_dp_catalog_private *catalog,
+					 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()
+	 */
+	if (stream_id == DP_STREAM_2)
+		writel(data, catalog->io.mst2_link.base + offset);
+	else if (stream_id == DP_STREAM_3)
+		writel(data, catalog->io.mst3_link.base + offset);
+	else
+		DRM_ERROR("invalid stream id");
+}
+
 /* aux related catalog functions */
 u32 msm_dp_catalog_aux_read_data(struct msm_dp_catalog *msm_dp_catalog)
 {
@@ -329,7 +365,10 @@ void msm_dp_catalog_ctrl_config_ctrl(struct msm_dp_catalog *msm_dp_catalog,
 	if (stream_id == DP_STREAM_1)
 		reg_offset = REG_DP1_CONFIGURATION_CTRL - REG_DP_CONFIGURATION_CTRL;
 
-	msm_dp_write_link(catalog, REG_DP_CONFIGURATION_CTRL + reg_offset, cfg);
+	if (stream_id > DP_STREAM_1)
+		msm_dp_write_mst_link(catalog, stream_id, REG_DP_MST_LINK_CONFIGURATION_CTRL, cfg);
+	else
+		msm_dp_write_link(catalog, REG_DP_CONFIGURATION_CTRL + reg_offset, cfg);
 
 	drm_dbg_dp(catalog->drm_dev, "stream_id:%d DP_CONFIGURATION_CTRL=0x%x\n",
 		stream_id, cfg);
@@ -416,7 +455,10 @@ void msm_dp_catalog_ctrl_config_misc(struct msm_dp_catalog *msm_dp_catalog,
 	if (stream_id == DP_STREAM_1)
 		reg_offset = REG_DP1_MISC1_MISC0 - REG_DP_MISC1_MISC0;
 
-	misc_val = msm_dp_read_link(catalog, REG_DP_MISC1_MISC0 + reg_offset);
+	if (stream_id > DP_STREAM_1)
+		misc_val = msm_dp_read_mst_link(catalog, stream_id, REG_DP_MST_LINK_MISC1_MISC0);
+	else
+		misc_val = msm_dp_read_link(catalog, REG_DP_MISC1_MISC0 + reg_offset);
 
 	/* clear bpp bits */
 	misc_val &= ~(0x07 << DP_MISC0_TEST_BITS_DEPTH_SHIFT);
@@ -426,7 +468,11 @@ void msm_dp_catalog_ctrl_config_misc(struct msm_dp_catalog *msm_dp_catalog,
 	misc_val |= DP_MISC0_SYNCHRONOUS_CLK;
 
 	drm_dbg_dp(catalog->drm_dev, "misc settings = 0x%x\n", misc_val);
-	msm_dp_write_link(catalog, REG_DP_MISC1_MISC0 + reg_offset, misc_val);
+
+	if (stream_id > DP_STREAM_1)
+		msm_dp_write_mst_link(catalog, stream_id, REG_DP_MST_LINK_MISC1_MISC0, misc_val);
+	else
+		msm_dp_write_link(catalog, REG_DP_MISC1_MISC0 + reg_offset, misc_val);
 }
 
 void msm_dp_catalog_setup_peripheral_flush(struct msm_dp_catalog *msm_dp_catalog)
@@ -506,9 +552,15 @@ void msm_dp_catalog_ctrl_config_msa(struct msm_dp_catalog *msm_dp_catalog,
 	if (link_rate_hbr3 == rate)
 		nvid *= 3;
 
-	drm_dbg_dp(catalog->drm_dev, "mvid=0x%x, nvid=0x%x\n", mvid, nvid);
-	msm_dp_write_link(catalog, REG_DP_SOFTWARE_MVID + mvid_reg_off, mvid);
-	msm_dp_write_link(catalog, REG_DP_SOFTWARE_NVID + nvid_reg_off, nvid);
+	drm_dbg_dp(catalog->drm_dev, "stream_id:%d mvid=0x%x, nvid=0x%x\n", stream_id, mvid, nvid);
+	if (stream_id > DP_STREAM_1) {
+		msm_dp_write_mst_link(catalog, stream_id, REG_MST_LINK_SOFTWARE_MVID, mvid);
+		msm_dp_write_mst_link(catalog, stream_id, REG_MST_LINK_SOFTWARE_NVID, nvid);
+	} else {
+		msm_dp_write_link(catalog, REG_DP_SOFTWARE_MVID + mvid_reg_off, mvid);
+		msm_dp_write_link(catalog, REG_DP_SOFTWARE_NVID + nvid_reg_off, nvid);
+	}
+
 	msm_dp_write_pn(catalog, stream_id, MMSS_DP_DSC_DTO, 0x0);
 }
 
@@ -878,10 +930,21 @@ int msm_dp_catalog_panel_timing_cfg(struct msm_dp_catalog *msm_dp_catalog,
 	if (stream_id == DP_STREAM_1)
 		offset = REG_DP1_TOTAL_HOR_VER - REG_DP_TOTAL_HOR_VER;
 
-	msm_dp_write_link(catalog, REG_DP_TOTAL_HOR_VER + offset, total);
-	msm_dp_write_link(catalog, REG_DP_START_HOR_VER_FROM_SYNC + offset, sync_start);
-	msm_dp_write_link(catalog, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY + offset, width_blanking);
-	msm_dp_write_link(catalog, REG_DP_ACTIVE_HOR_VER + offset, msm_dp_active);
+	if (stream_id > DP_STREAM_1) {
+		msm_dp_write_mst_link(catalog, stream_id, REG_DP_MST_LINK_TOTAL_HOR_VER, total);
+		msm_dp_write_mst_link(catalog, stream_id, REG_DP_MST_LINK_START_HOR_VER_FROM_SYNC,
+				      sync_start);
+		msm_dp_write_mst_link(catalog, stream_id,
+				      REG_DP_MST_LINK_HSYNC_VSYNC_WIDTH_POLARITY, width_blanking);
+		msm_dp_write_mst_link(catalog, stream_id, REG_DP_MST_LINK_ACTIVE_HOR_VER,
+				      msm_dp_active);
+	} else {
+		msm_dp_write_link(catalog, REG_DP_TOTAL_HOR_VER + offset, total);
+		msm_dp_write_link(catalog, REG_DP_START_HOR_VER_FROM_SYNC + offset, sync_start);
+		msm_dp_write_link(catalog, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY + offset,
+				  width_blanking);
+		msm_dp_write_link(catalog, REG_DP_ACTIVE_HOR_VER + offset, msm_dp_active);
+	}
 
 	reg = msm_dp_read_pn(catalog, stream_id, MMSS_DP_INTF_CONFIG);
 
@@ -914,13 +977,24 @@ static void msm_dp_catalog_panel_send_vsc_sdp(struct msm_dp_catalog *msm_dp_cata
 
 	msm_dp_utils_pack_sdp_header(&vsc_sdp->sdp_header, header);
 
-	msm_dp_write_link(catalog, MMSS_DP_GENERIC0_0 + msm_dp_generic_offset, header[0]);
-	msm_dp_write_link(catalog, MMSS_DP_GENERIC0_1 + msm_dp_generic_offset, header[1]);
+	if (stream_id > DP_STREAM_1) {
+		msm_dp_write_mst_link(catalog, stream_id, MMSS_DP_MST_LINK_GENERIC0_0, header[0]);
+		msm_dp_write_mst_link(catalog, stream_id, MMSS_DP_MST_LINK_GENERIC0_1, header[1]);
+	} else {
+		msm_dp_write_link(catalog, MMSS_DP_GENERIC0_0 + msm_dp_generic_offset, header[0]);
+		msm_dp_write_link(catalog, MMSS_DP_GENERIC0_1 + msm_dp_generic_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(catalog, MMSS_DP_GENERIC0_2 + i + msm_dp_generic_offset, val);
+
+		if (stream_id > DP_STREAM_1)
+			msm_dp_write_mst_link(catalog, stream_id, MMSS_DP_MST_LINK_GENERIC0_2 + i,
+					      val);
+		else
+			msm_dp_write_link(catalog, MMSS_DP_GENERIC0_2 + i + msm_dp_generic_offset,
+					  val);
 	}
 }
 
@@ -938,8 +1012,13 @@ static void msm_dp_catalog_panel_update_sdp(struct msm_dp_catalog *msm_dp_catalo
 
 	hw_revision = msm_dp_catalog_hw_revision(msm_dp_catalog);
 	if (hw_revision < DP_HW_VERSION_1_2 && hw_revision >= DP_HW_VERSION_1_0) {
-		msm_dp_write_link(catalog, MMSS_DP_SDP_CFG3 + sdp_cfg3_offset, 0x01);
-		msm_dp_write_link(catalog, MMSS_DP_SDP_CFG3 + sdp_cfg3_offset, 0x00);
+		if (stream_id > DP_STREAM_1) {
+			msm_dp_write_mst_link(catalog, stream_id, MMSS_DP_MST_LINK_SDP_CFG3, 0x01);
+			msm_dp_write_mst_link(catalog, stream_id, MMSS_DP_MST_LINK_SDP_CFG3, 0x00);
+		} else {
+			msm_dp_write_link(catalog, MMSS_DP_SDP_CFG3 + sdp_cfg3_offset, 0x01);
+			msm_dp_write_link(catalog, MMSS_DP_SDP_CFG3 + sdp_cfg3_offset, 0x00);
+		}
 	}
 }
 
@@ -960,15 +1039,27 @@ void msm_dp_catalog_panel_enable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog,
 		sdp_cfg2_offset = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
 	}
 
-	cfg = msm_dp_read_link(catalog, MMSS_DP_SDP_CFG + sdp_cfg_offset);
-	cfg2 = msm_dp_read_link(catalog, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset);
-	misc = msm_dp_read_link(catalog, REG_DP_MISC1_MISC0 + misc_reg_offset);
+	if (stream_id > DP_STREAM_1) {
+		cfg = msm_dp_read_mst_link(catalog, stream_id, MMSS_DP_MST_LINK_SDP_CFG);
+		cfg2 = msm_dp_read_mst_link(catalog, stream_id, MMSS_DP_MST_LINK_SDP_CFG2);
+		misc = msm_dp_read_mst_link(catalog, stream_id, REG_DP_MST_LINK_MISC1_MISC0);
+	} else {
+		cfg = msm_dp_read_link(catalog, MMSS_DP_SDP_CFG + sdp_cfg_offset);
+		cfg2 = msm_dp_read_link(catalog, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset);
+		misc = msm_dp_read_link(catalog, REG_DP_MISC1_MISC0 + misc_reg_offset);
+	}
 
 	cfg |= GEN0_SDP_EN;
-	msm_dp_write_link(catalog, MMSS_DP_SDP_CFG + sdp_cfg_offset, cfg);
+	if (stream_id > DP_STREAM_1)
+		msm_dp_write_mst_link(catalog, stream_id, MMSS_DP_MST_LINK_SDP_CFG, cfg);
+	else
+		msm_dp_write_link(catalog, MMSS_DP_SDP_CFG + sdp_cfg_offset, cfg);
 
 	cfg2 |= GENERIC0_SDPSIZE_VALID;
-	msm_dp_write_link(catalog, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset, cfg2);
+	if (stream_id > DP_STREAM_1)
+		msm_dp_write_mst_link(catalog, stream_id, MMSS_DP_MST_LINK_SDP_CFG2, cfg2);
+	else
+		msm_dp_write_link(catalog, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset, cfg2);
 
 	msm_dp_catalog_panel_send_vsc_sdp(msm_dp_catalog, stream_id, vsc_sdp);
 
@@ -979,7 +1070,10 @@ void msm_dp_catalog_panel_enable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog,
 
 	pr_debug("misc settings = 0x%x\n", misc);
 
-	msm_dp_write_link(catalog, REG_DP_MISC1_MISC0 + misc_reg_offset, misc);
+	if (stream_id > DP_STREAM_1)
+		msm_dp_write_mst_link(catalog, stream_id, REG_DP_MST_LINK_MISC1_MISC0, misc);
+	else
+		msm_dp_write_link(catalog, REG_DP_MISC1_MISC0 + misc_reg_offset, misc);
 
 	msm_dp_catalog_panel_update_sdp(msm_dp_catalog, stream_id);
 }
@@ -989,18 +1083,39 @@ void msm_dp_catalog_panel_disable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog,
 {
 	struct msm_dp_catalog_private *catalog;
 	u32 cfg, cfg2, misc;
+	u32 misc_reg_offset = 0;
+	u32 sdp_cfg_offset = 0;
+	u32 sdp_cfg2_offset = 0;
 
 	catalog = container_of(msm_dp_catalog, struct msm_dp_catalog_private, msm_dp_catalog);
 
-	cfg = msm_dp_read_link(catalog, MMSS_DP_SDP_CFG);
-	cfg2 = msm_dp_read_link(catalog, MMSS_DP_SDP_CFG2);
-	misc = msm_dp_read_link(catalog, REG_DP_MISC1_MISC0);
+	if (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;
+	}
+
+	if (stream_id > DP_STREAM_1) {
+		cfg = msm_dp_read_mst_link(catalog, stream_id, MMSS_DP_MST_LINK_SDP_CFG);
+		cfg2 = msm_dp_read_mst_link(catalog, stream_id, MMSS_DP_MST_LINK_SDP_CFG2);
+		misc = msm_dp_read_mst_link(catalog, stream_id, REG_DP_MST_LINK_MISC1_MISC0);
+	} else {
+		cfg = msm_dp_read_link(catalog, MMSS_DP_SDP_CFG + sdp_cfg_offset);
+		cfg2 = msm_dp_read_link(catalog, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset);
+		misc = msm_dp_read_link(catalog, REG_DP_MISC1_MISC0 + misc_reg_offset);
+	}
 
 	cfg &= ~GEN0_SDP_EN;
-	msm_dp_write_link(catalog, MMSS_DP_SDP_CFG, cfg);
+	if (stream_id > DP_STREAM_1)
+		msm_dp_write_mst_link(catalog, stream_id, MMSS_DP_MST_LINK_SDP_CFG, cfg);
+	else
+		msm_dp_write_link(catalog, MMSS_DP_SDP_CFG + sdp_cfg_offset, cfg);
 
 	cfg2 &= ~GENERIC0_SDPSIZE_VALID;
-	msm_dp_write_link(catalog, MMSS_DP_SDP_CFG2, cfg2);
+	if (stream_id > DP_STREAM_1)
+		msm_dp_write_mst_link(catalog, stream_id, MMSS_DP_MST_LINK_SDP_CFG2, cfg2);
+	else
+		msm_dp_write_link(catalog, MMSS_DP_SDP_CFG2 + sdp_cfg2_offset, cfg2);
 
 	/* switch back to MSA */
 	misc &= ~DP_MISC1_VSC_SDP;
@@ -1008,7 +1123,10 @@ void msm_dp_catalog_panel_disable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog,
 	drm_dbg_dp(catalog->drm_dev, "vsc sdp enable=0\n");
 
 	pr_debug("misc settings = 0x%x\n", misc);
-	msm_dp_write_link(catalog, REG_DP_MISC1_MISC0, misc);
+	if (stream_id > DP_STREAM_1)
+		msm_dp_write_mst_link(catalog, stream_id, REG_DP_MST_LINK_MISC1_MISC0, misc);
+	else
+		msm_dp_write_link(catalog, REG_DP_MISC1_MISC0 + misc_reg_offset, misc);
 
 	msm_dp_catalog_panel_update_sdp(msm_dp_catalog, stream_id);
 }
@@ -1164,6 +1282,16 @@ static int msm_dp_catalog_get_io(struct msm_dp_catalog_private *catalog, int max
 				DRM_DEBUG_DP("unable to remap p%d region: %pe\n", i,
 					      dss->pixels[i].base);
 		}
+
+		dss->mst2_link.base = msm_dp_ioremap(pdev, 7, &dss->mst2_link.len);
+		if (IS_ERR(dss->mst2_link.base))
+			DRM_DEBUG_DP("unable to remap mst2_link region: %pe\n",
+				     dss->mst2_link.base);
+
+		dss->mst3_link.base = msm_dp_ioremap(pdev, 8, &dss->mst3_link.len);
+		if (IS_ERR(dss->mst3_link.base))
+			DRM_DEBUG_DP("unable to remap mst3_link region: %pe\n",
+				     dss->mst3_link.base);
 	}
 
 	return 0;
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index 623a58163f7028dcaac2f4d4416ee091db9f5024..06b3d570336347e38fba8cd0dfb44d25046e5950 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -123,6 +123,7 @@
 
 #define REG_DP_CONFIGURATION_CTRL		(0x00000008)
 #define REG_DP1_CONFIGURATION_CTRL		(0x00000400)
+#define REG_DP_MST_LINK_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)
@@ -140,15 +141,22 @@
 
 #define REG_DP_SOFTWARE_MVID			(0x00000010)
 #define REG_DP1_SOFTWARE_MVID			(0x00000414)
+#define REG_MST_LINK_SOFTWARE_MVID		(0x00000040)
 #define REG_DP_SOFTWARE_NVID			(0x00000018)
 #define REG_DP1_SOFTWARE_NVID			(0x00000418)
+#define REG_MST_LINK_SOFTWARE_NVID		(0x00000044)
 #define REG_DP_TOTAL_HOR_VER			(0x0000001C)
 #define REG_DP1_TOTAL_HOR_VER			(0x0000041C)
+#define REG_DP_MST_LINK_TOTAL_HOR_VER		(0x00000048)
 #define REG_DP_START_HOR_VER_FROM_SYNC		(0x00000020)
+#define REG_DP_MST_LINK_START_HOR_VER_FROM_SYNC	(0x0000004C)
 #define REG_DP_HSYNC_VSYNC_WIDTH_POLARITY	(0x00000024)
+#define REG_DP_MST_LINK_HSYNC_VSYNC_WIDTH_POLARITY	(0x00000050)
 #define REG_DP_ACTIVE_HOR_VER			(0x00000028)
+#define REG_DP_MST_LINK_ACTIVE_HOR_VER		(0x00000054)
 #define REG_DP_MISC1_MISC0			(0x0000002C)
 #define REG_DP1_MISC1_MISC0			(0x0000042C)
+#define REG_DP_MST_LINK_MISC1_MISC0		(0x00000058)
 
 #define DP_MISC0_SYNCHRONOUS_CLK		(0x00000001)
 #define DP_MISC0_COLORIMETRY_CFG_SHIFT		(0x00000001)
@@ -217,9 +225,11 @@
 
 #define MMSS_DP_SDP_CFG				(0x00000228)
 #define MMSS_DP1_SDP_CFG			(0x000004E0)
+#define MMSS_DP_MST_LINK_SDP_CFG		(0x0000010c)
 #define GEN0_SDP_EN				(0x00020000)
 #define MMSS_DP_SDP_CFG2			(0x0000022C)
 #define MMSS_DP1_SDP_CFG2			(0x000004E4)
+#define MMSS_DP_MST_LINK_SDP_CFG2		(0x0000011c)
 
 #define MMSS_DP_AUDIO_TIMESTAMP_0		(0x00000230)
 #define MMSS_DP_AUDIO_TIMESTAMP_1		(0x00000234)
@@ -230,6 +240,7 @@
 
 #define MMSS_DP_SDP_CFG3			(0x0000024c)
 #define MMSS_DP1_SDP_CFG3			(0x000004E8)
+#define MMSS_DP_MST_LINK_SDP_CFG3		(0x00000114)
 
 #define UPDATE_SDP				(0x00000001)
 
@@ -281,6 +292,9 @@
 #define MMSS_DP_GENERIC1_9			(0x0000034C)
 
 #define MMSS_DP1_GENERIC0_0			(0x00000490)
+#define MMSS_DP_MST_LINK_GENERIC0_0		(0x000000BC)
+#define MMSS_DP_MST_LINK_GENERIC0_1		(0x000000C0)
+#define MMSS_DP_MST_LINK_GENERIC0_2		(0x000000C4)
 
 #define MMSS_DP_VSCEXT_0			(0x000002D0)
 #define MMSS_DP_VSCEXT_1			(0x000002D4)

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 15/38] drm/msm/dp: add support to send ACT packets for MST
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (13 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 14/38] drm/msm/dp: Add catalog support for 3rd/4th stream MST Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 16/38] drm/msm/dp: add support to program mst support in mainlink Yongxing Mou
                   ` (24 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_catalog.c | 21 +++++++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_catalog.h |  4 ++++
 drivers/gpu/drm/msm/dp/dp_ctrl.c    | 35 +++++++++++++++++++++++++++++++++--
 drivers/gpu/drm/msm/dp/dp_ctrl.h    |  3 ++-
 drivers/gpu/drm/msm/dp/dp_display.c |  6 +++++-
 drivers/gpu/drm/msm/dp/dp_display.h |  1 +
 drivers/gpu/drm/msm/dp/dp_reg.h     |  2 ++
 7 files changed, 68 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index 07d06cd36eea5ea1dd90daf01422b8ecdffb13fe..e6920008356991ad0c023664d5e286433ccf5d0b 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -1131,6 +1131,27 @@ void msm_dp_catalog_panel_disable_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog,
 	msm_dp_catalog_panel_update_sdp(msm_dp_catalog, stream_id);
 }
 
+void msm_dp_catalog_trigger_act(struct msm_dp_catalog *msm_dp_catalog)
+{
+	struct msm_dp_catalog_private *catalog;
+
+	catalog = container_of(msm_dp_catalog, struct msm_dp_catalog_private, msm_dp_catalog);
+
+	msm_dp_write_link(catalog, REG_DP_MST_ACT, 0x1);
+
+	/* make sure ACT signal is performed */
+	wmb();
+}
+
+bool msm_dp_catalog_read_act_complete_sts(struct msm_dp_catalog *msm_dp_catalog)
+{
+	struct msm_dp_catalog_private *catalog;
+
+	catalog = container_of(msm_dp_catalog, struct msm_dp_catalog_private, msm_dp_catalog);
+
+	return msm_dp_read_link(catalog, REG_DP_MST_ACT);
+}
+
 void msm_dp_catalog_panel_tpg_enable(struct msm_dp_catalog *msm_dp_catalog,
 				     enum msm_dp_stream_id stream_id,
 				     struct drm_display_mode *drm_mode)
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index ef0e3fadcd04ae499d6340d05fd74392235bed76..88ef98c9b0cb2d273a0c190ff7b2783b5e254937 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -127,4 +127,8 @@ void msm_dp_catalog_audio_enable(struct msm_dp_catalog *catalog, bool enable);
 void msm_dp_catalog_audio_config_sdp(struct msm_dp_catalog *catalog);
 void msm_dp_catalog_audio_sfe_level(struct msm_dp_catalog *catalog, u32 safe_to_exit_level);
 
+/* DP MST APIs */
+void msm_dp_catalog_trigger_act(struct msm_dp_catalog *dp_catalog);
+bool msm_dp_catalog_read_act_complete_sts(struct msm_dp_catalog *dp_catalog);
+
 #endif /* _DP_CATALOG_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 6c3c27b48b3b13f205ce28b823ae26d5dc7d9a10..0088fefd88e517e01bb66f02661c877173181d8a 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -98,6 +98,7 @@ struct msm_dp_ctrl_private {
 	bool core_clks_on;
 	bool link_clks_on;
 	bool pixel_clks_on[DP_STREAM_MAX];
+	bool mst_active;
 };
 
 static int msm_dp_aux_link_configure(struct drm_dp_aux *aux,
@@ -1662,6 +1663,26 @@ static int msm_dp_ctrl_deinitialize_mainlink(struct msm_dp_ctrl_private *ctrl)
 	return 0;
 }
 
+int 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);
+
+	msm_dp_catalog_trigger_act(ctrl->catalog);
+	msleep(20); /* needs 1 frame time */
+
+	act_complete = msm_dp_catalog_read_act_complete_sts(ctrl->catalog);
+
+	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 0;
+}
+
 static int msm_dp_ctrl_link_maintenance(struct msm_dp_ctrl_private *ctrl)
 {
 	int ret = 0;
@@ -1680,6 +1701,9 @@ static int msm_dp_ctrl_link_maintenance(struct msm_dp_ctrl_private *ctrl)
 
 	msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_SEND_VIDEO);
 
+	if (ctrl->mst_active)
+		msm_dp_ctrl_mst_send_act(&ctrl->msm_dp_ctrl);
+
 	ret = msm_dp_ctrl_wait4video_ready(ctrl);
 end:
 	return ret;
@@ -1794,7 +1818,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, msm_dp_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, ctrl->mst_active);
 	if (ret) {
 		DRM_ERROR("failed to enable DP link controller\n");
 		return ret;
@@ -1874,7 +1898,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;
@@ -1893,6 +1917,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);
 
 	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
@@ -2084,6 +2109,9 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
 
 	msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_SEND_VIDEO);
 
+	if (ctrl->mst_active)
+		msm_dp_ctrl_mst_send_act(msm_dp_ctrl);
+
 	ret = msm_dp_ctrl_wait4video_ready(ctrl);
 	if (ret)
 		return ret;
@@ -2140,6 +2168,8 @@ void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl)
 
 	msm_dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);
 
+	ctrl->mst_active = false;
+
 	dev_pm_opp_set_rate(ctrl->dev, 0);
 	msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl);
 
@@ -2300,6 +2330,7 @@ struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link
 	ctrl->catalog  = catalog;
 	ctrl->dev      = dev;
 	ctrl->phy      = phy;
+	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 e20ba90dbd879ae3d6b1f5eb5922f969bc12d079..c74228bd3e4ac9e8498b75d6f5641834f662bfdc 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_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 *dp_ctrl, bool force_link_train);
 void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl);
@@ -45,5 +45,6 @@ void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl,
 				   struct msm_dp_panel *msm_dp_panel);
 void msm_dp_ctrl_psm_config(struct msm_dp_ctrl *msm_dp_ctrl);
 void msm_dp_ctrl_reinit_phy(struct msm_dp_ctrl *msm_dp_ctrl);
+int msm_dp_ctrl_mst_send_act(struct msm_dp_ctrl *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 76d07685f5a89109723b68ba9d97c0b0332b18aa..83e73b7cf471ae967866b7d87da3abe2c1bb60d2 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -385,6 +385,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];
+	struct msm_dp *dp_display = &dp->msm_dp_display;
 
 	rc = drm_dp_read_dpcd_caps(dp->aux, dpcd);
 	if (rc)
@@ -420,7 +421,7 @@ 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);
 
 	msm_dp_link_reset_phy_params_vx_px(dp->link);
-	rc = msm_dp_ctrl_on_link(dp->ctrl);
+	rc = msm_dp_ctrl_on_link(dp->ctrl, dp_display->mst_active);
 	if (rc) {
 		DRM_ERROR("failed to complete DP link training\n");
 		goto end;
@@ -1620,6 +1621,9 @@ 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);
+
+	if (dp->mst_active)
+		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 09b30596fd3366af55ae246e3a83ee9e88995382..2751bf1c786e190cd7c7b514b23e90a6ed2ca3b9 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 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 06b3d570336347e38fba8cd0dfb44d25046e5950..b993487925dbd78e8f78e09fbf52ab38f268b71a 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -139,6 +139,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_DP1_SOFTWARE_MVID			(0x00000414)
 #define REG_MST_LINK_SOFTWARE_MVID		(0x00000040)

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 16/38] drm/msm/dp: add support to program mst support in mainlink
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (14 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 15/38] drm/msm/dp: add support to send ACT packets for MST Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 17/38] drm/msm/dp: no need to update tu calculation for mst Yongxing Mou
                   ` (23 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 enabled bit in the mainlink
control when a mst session is active and disabled.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_catalog.c | 17 +++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_catalog.h |  1 +
 drivers/gpu/drm/msm/dp/dp_ctrl.c    |  4 ++++
 drivers/gpu/drm/msm/dp/dp_reg.h     |  1 +
 4 files changed, 23 insertions(+)

diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index e6920008356991ad0c023664d5e286433ccf5d0b..ff0fb742d122646dd45fcd868a61532c630419d1 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -407,6 +407,23 @@ void msm_dp_catalog_ctrl_psr_mainlink_enable(struct msm_dp_catalog *msm_dp_catal
 	msm_dp_write_link(catalog, REG_DP_MAINLINK_CTRL, val);
 }
 
+void msm_dp_catalog_mst_config(struct msm_dp_catalog *msm_dp_catalog, bool enable)
+{
+	struct msm_dp_catalog_private *catalog = container_of(msm_dp_catalog,
+							      struct msm_dp_catalog_private,
+							      msm_dp_catalog);
+
+	u32 mainlink_ctrl;
+
+	mainlink_ctrl = msm_dp_read_link(catalog, 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(catalog, REG_DP_MAINLINK_CTRL, mainlink_ctrl);
+}
+
 void msm_dp_catalog_ctrl_mainlink_ctrl(struct msm_dp_catalog *msm_dp_catalog,
 						bool enable)
 {
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index 88ef98c9b0cb2d273a0c190ff7b2783b5e254937..e2c5e0235834527998d40caddd1649b434e1f180 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -130,5 +130,6 @@ void msm_dp_catalog_audio_sfe_level(struct msm_dp_catalog *catalog, u32 safe_to_
 /* DP MST APIs */
 void msm_dp_catalog_trigger_act(struct msm_dp_catalog *dp_catalog);
 bool msm_dp_catalog_read_act_complete_sts(struct msm_dp_catalog *dp_catalog);
+void msm_dp_catalog_mst_config(struct msm_dp_catalog *dp_catalog, bool enable);
 
 #endif /* _DP_CATALOG_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 0088fefd88e517e01bb66f02661c877173181d8a..64d9862841a22b8298c6e4eb6e488834e38ddcd0 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -184,6 +184,9 @@ static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl
 	msm_dp_catalog_ctrl_lane_mapping(ctrl->catalog);
 	msm_dp_catalog_setup_peripheral_flush(ctrl->catalog);
 
+	if (ctrl->mst_active)
+		msm_dp_catalog_mst_config(ctrl->catalog, true);
+
 	msm_dp_ctrl_config_ctrl(ctrl, msm_dp_panel);
 
 	tb = msm_dp_link_get_test_bits_depth(ctrl->link,
@@ -2167,6 +2170,7 @@ void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl)
 	phy = ctrl->phy;
 
 	msm_dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);
+	msm_dp_catalog_mst_config(ctrl->catalog, false);
 
 	ctrl->mst_active = false;
 
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index b993487925dbd78e8f78e09fbf52ab38f268b71a..f079c1132d5aa9a97a0bef314e31f64977d7ffc4 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -109,6 +109,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] 108+ messages in thread

* [PATCH v2 17/38] drm/msm/dp: no need to update tu calculation for mst
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (15 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 16/38] drm/msm/dp: add support to program mst support in mainlink Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 18/38] drm/msm/dp: add support for mst channel slot allocation Yongxing Mou
                   ` (22 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 <quic_yongmou@quicinc.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 64d9862841a22b8298c6e4eb6e488834e38ddcd0..596a949827a4db7c5ce3e804d31c3db8e048cebe 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -2108,7 +2108,8 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
 		pixel_rate_orig,
 		msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420);
 
-	msm_dp_ctrl_setup_tr_unit(ctrl);
+	if (!ctrl->mst_active)
+		msm_dp_ctrl_setup_tr_unit(ctrl);
 
 	msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_SEND_VIDEO);
 

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 18/38] drm/msm/dp: add support for mst channel slot allocation
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (16 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 17/38] drm/msm/dp: no need to update tu calculation for mst Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 19/38] drm/msm/dp: add support to send vcpf packets in dp controller Yongxing Mou
                   ` (21 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 streams, the 64 MTP slots are time-shared between
the streams. Add the support to calculate the rate governor,
slots and reservation of the slots to the DP controller. Each
DP MST stream shall reserve its streams by calling the
dp_display_set_stream_info() from its bridge calls.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_catalog.c |  88 +++++++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_catalog.h |   8 ++
 drivers/gpu/drm/msm/dp/dp_ctrl.c    | 148 ++++++++++++++++++++++++++++++++++--
 drivers/gpu/drm/msm/dp/dp_ctrl.h    |   7 +-
 drivers/gpu/drm/msm/dp/dp_display.c |  28 +++++--
 drivers/gpu/drm/msm/dp/dp_display.h |   5 +-
 drivers/gpu/drm/msm/dp/dp_panel.h   |   2 +
 drivers/gpu/drm/msm/dp/dp_reg.h     |  11 +++
 8 files changed, 283 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index ff0fb742d122646dd45fcd868a61532c630419d1..baa3a93a9bb3109c3110503e4d1dc1f6e3cdc2f8 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -1169,6 +1169,94 @@ bool msm_dp_catalog_read_act_complete_sts(struct msm_dp_catalog *msm_dp_catalog)
 	return msm_dp_read_link(catalog, REG_DP_MST_ACT);
 }
 
+void msm_dp_catalog_mst_channel_alloc(struct msm_dp_catalog *msm_dp_catalog,
+				      enum msm_dp_stream_id stream_id, u32 ch_start_slot,
+				      u32 tot_slot_cnt)
+{
+	struct msm_dp_catalog_private *catalog;
+	u32 i, slot_reg_1, slot_reg_2, slot;
+	u32 reg_off = 0;
+	int const num_slots_per_reg = 32;
+
+	if (!msm_dp_catalog || stream_id >= DP_STREAM_MAX) {
+		DRM_ERROR("invalid input. stream_id %d\n", stream_id);
+		return;
+	}
+
+	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;
+	}
+
+	catalog = container_of(msm_dp_catalog, struct msm_dp_catalog_private, msm_dp_catalog);
+
+	drm_dbg_dp(catalog->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(catalog->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_mst_link(catalog, stream_id, REG_DP_MST_LINK_TIMESLOT_1_32,
+				      slot_reg_1);
+		msm_dp_write_mst_link(catalog, stream_id, REG_DP_MST_LINK_TIMESLOT_33_63,
+				      slot_reg_2);
+	} else {
+		msm_dp_write_link(catalog, REG_DP_DP0_TIMESLOT_1_32 + reg_off, slot_reg_1);
+		msm_dp_write_link(catalog, REG_DP_DP0_TIMESLOT_33_63 + reg_off, slot_reg_2);
+	}
+}
+
+void msm_dp_catalog_ctrl_update_rg(struct msm_dp_catalog *msm_dp_catalog,
+				   enum msm_dp_stream_id stream_id, u32 x_int, u32 y_frac_enum)
+{
+	struct msm_dp_catalog_private *catalog;
+
+	u32 rg, reg_off = 0;
+
+	if (!msm_dp_catalog || stream_id >= DP_STREAM_MAX) {
+		DRM_ERROR("invalid input. stream %d\n", stream_id);
+		return;
+	}
+
+	catalog = container_of(msm_dp_catalog, struct msm_dp_catalog_private, msm_dp_catalog);
+
+	rg = y_frac_enum;
+	rg |= (x_int << 16);
+
+	drm_dbg_dp(catalog->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_mst_link(catalog, stream_id, REG_DP_MST_LINK_DP_RG, rg);
+	else
+		msm_dp_write_link(catalog, REG_DP_DP0_RG + reg_off, rg);
+}
+
 void msm_dp_catalog_panel_tpg_enable(struct msm_dp_catalog *msm_dp_catalog,
 				     enum msm_dp_stream_id stream_id,
 				     struct drm_display_mode *drm_mode)
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index e2c5e0235834527998d40caddd1649b434e1f180..8d3887e00cfd4e62aacf51795fb54aef071e34ee 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -31,6 +31,8 @@
 #define DP_HW_VERSION_1_0	0x10000000
 #define DP_HW_VERSION_1_2	0x10020000
 
+#define DP_MAX_TIME_SLOTS 64
+
 /* stream id */
 enum msm_dp_stream_id {
 	DP_STREAM_0,
@@ -132,4 +134,10 @@ void msm_dp_catalog_trigger_act(struct msm_dp_catalog *dp_catalog);
 bool msm_dp_catalog_read_act_complete_sts(struct msm_dp_catalog *dp_catalog);
 void msm_dp_catalog_mst_config(struct msm_dp_catalog *dp_catalog, bool enable);
 
+void msm_dp_catalog_mst_channel_alloc(struct msm_dp_catalog *dp_catalog,
+				      enum msm_dp_stream_id stream_id, u32 ch_start_slot,
+				      u32 tot_slot_cnt);
+void msm_dp_catalog_ctrl_update_rg(struct msm_dp_catalog *dp_catalog,
+				   enum msm_dp_stream_id stream_id, u32 x_int, u32 y_frac_enum);
+
 #endif /* _DP_CATALOG_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 596a949827a4db7c5ce3e804d31c3db8e048cebe..5c3bfc39ebeaee1cd88f9cd9ced80b92fba21669 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -70,6 +70,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;
@@ -99,6 +104,8 @@ struct msm_dp_ctrl_private {
 	bool link_clks_on;
 	bool pixel_clks_on[DP_STREAM_MAX];
 	bool mst_active;
+
+	struct msm_dp_mst_ch_slot_info mst_ch_info[DP_STREAM_MAX];
 };
 
 static int msm_dp_aux_link_configure(struct drm_dp_aux *aux,
@@ -2069,7 +2076,103 @@ 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;
+
+	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_catalog_ctrl_update_rg(ctrl->catalog, 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;
@@ -2111,6 +2214,9 @@ 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);
 
+	if (ctrl->mst_active)
+		msm_dp_ctrl_mst_stream_setup(ctrl, msm_dp_panel, max_streams);
+
 	msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_SEND_VIDEO);
 
 	if (ctrl->mst_active)
@@ -2178,13 +2284,45 @@ void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl)
 	dev_pm_opp_set_rate(ctrl->dev, 0);
 	msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl);
 
-	DRM_DEBUG_DP("Before, phy=%p init_count=%d power_on=%d\n",
-		phy, phy->init_count, phy->power_count);
+	drm_dbg_dp(ctrl->drm_dev, "Before, phy=%p init_count=%d power_on=%d\n",
+		   phy, phy->init_count, phy->power_count);
 
 	phy_power_off(phy);
 
-	DRM_DEBUG_DP("After, phy=%p init_count=%d power_on=%d\n",
-		phy, phy->init_count, phy->power_count);
+	drm_dbg_dp(ctrl->drm_dev, "After, phy=%p init_count=%d power_on=%d\n",
+		   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 strm,
+				      u32 start_slot, u32 tot_slots)
+{
+	struct msm_dp_ctrl_private *ctrl;
+
+	if (!msm_dp_ctrl || strm >= 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[strm].start_slot = start_slot;
+	ctrl->mst_ch_info[strm].tot_slots = tot_slots;
+}
+
+// TO-DO : Check if we can do a dealloc instead of this one during teardown
+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);
+
+	for (i = DP_STREAM_0; i < max_streams; i++) {
+		msm_dp_catalog_mst_channel_alloc(ctrl->catalog,
+						 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)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index c74228bd3e4ac9e8498b75d6f5641834f662bfdc..074efc0cc80bcc19d8beba8c831c6ed16ab9eb9a 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -18,7 +18,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 *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);
@@ -46,5 +47,9 @@ void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl,
 void msm_dp_ctrl_psm_config(struct msm_dp_ctrl *msm_dp_ctrl);
 void msm_dp_ctrl_reinit_phy(struct msm_dp_ctrl *msm_dp_ctrl);
 int msm_dp_ctrl_mst_send_act(struct msm_dp_ctrl *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 strm,
+				      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 83e73b7cf471ae967866b7d87da3abe2c1bb60d2..47475e4c76d5d701890b8ace0b63685fd4466c94 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -877,7 +877,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;
 
@@ -951,11 +951,14 @@ 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 *dp, 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;
+	const int max_slots = 64;
+
 
 	msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
 
@@ -964,8 +967,18 @@ int msm_dp_display_set_stream_id(struct msm_dp *dp,
 		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(msm_dp_display->ctrl, stream_id, start_slot, num_slots);
+
+	if (panel) {
 		panel->stream_id = stream_id;
+		panel->pbn = pbn;
+	}
 
 	return rc;
 }
@@ -1594,7 +1607,7 @@ void msm_dp_display_atomic_enable(struct msm_dp *dp)
 
 	mutex_lock(&msm_dp_display->event_mutex);
 
-	msm_dp_display_set_stream_id(dp, msm_dp_display->panel, 0);
+	msm_dp_display_set_stream_info(dp, msm_dp_display->panel, 0, 0, 0, 0, 0);
 
 	if (dp->prepared) {
 		rc = msm_dp_display_enable(msm_dp_display);
@@ -1622,8 +1635,11 @@ void msm_dp_display_atomic_disable(struct msm_dp *dp)
 
 	msm_dp_ctrl_push_idle(msm_dp_display->ctrl);
 
-	if (dp->mst_active)
+	if (msm_dp_display->mst_supported) {
+		msm_dp_ctrl_mst_stream_channel_slot_setup(msm_dp_display->ctrl,
+							  msm_dp_display->max_stream);
 		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 2751bf1c786e190cd7c7b514b23e90a6ed2ca3b9..10a5be9337cf9b2ea90b3061ef8d0d6de3282431 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -46,7 +46,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, 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 170f819644b473b454f88cd4e8762959f4513613..15047f964037d00012b968e70b238ce1809c0fb1 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -44,6 +44,8 @@ struct msm_dp_panel {
 	u32 max_dp_link_rate;
 
 	u32 max_bw_code;
+
+	u32 pbn;
 };
 
 int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel);
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index f079c1132d5aa9a97a0bef314e31f64977d7ffc4..16182c7298eb351fd261391498e031d6213e96df 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -348,6 +348,17 @@
 #define REG_DP_PHY_AUX_BIST_CFG			(0x00000050)
 #define REG_DP_PHY_AUX_INTERRUPT_STATUS         (0x000000BC)
 
+/* DP MST related registers */
+#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_MST_LINK_TIMESLOT_1_32		(0x00000038)
+#define REG_DP_MST_LINK_TIMESLOT_33_63		(0x0000003C)
+#define REG_DP_DP0_RG				(0x000004F8)
+#define REG_DP_DP1_RG				(0x000004FC)
+#define REG_DP_MST_LINK_DP_RG			(0X0000011C)
+
 /* DP HDCP 1.3 registers */
 #define DP_HDCP_CTRL                                   (0x0A0)
 #define DP_HDCP_STATUS                                 (0x0A4)

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 19/38] drm/msm/dp: add support to send vcpf packets in dp controller
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (17 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 18/38] drm/msm/dp: add support for mst channel slot allocation Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 20/38] drm/msm/dp: always program MST_FIFO_CONSTANT_FILL for MST Yongxing Mou
                   ` (20 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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>

VC payload fill sequence is inserted by the DP controller in the
absence of stream symbols that is before stream is disabled. Add
support to send the VCPF sequence for msm dp controller.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_catalog.c | 38 ++++++++++++++++++++++++++++--
 drivers/gpu/drm/msm/dp/dp_catalog.h | 10 ++++++--
 drivers/gpu/drm/msm/dp/dp_ctrl.c    | 46 +++++++++++++++++++++++++++++++++++--
 drivers/gpu/drm/msm/dp/dp_ctrl.h    |  1 +
 drivers/gpu/drm/msm/dp/dp_display.c |  5 +++-
 drivers/gpu/drm/msm/dp/dp_reg.h     |  4 +++-
 6 files changed, 96 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index baa3a93a9bb3109c3110503e4d1dc1f6e3cdc2f8..f2a5170723585ed6ddab2c12d2e5c5d6dee5eff5 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -50,6 +50,11 @@
 	(PSR_UPDATE_INT | PSR_CAPTURE_INT | PSR_EXIT_INT | \
 	PSR_UPDATE_ERROR_INT | PSR_WAKE_ERROR_INT)
 
+#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_INTERRUPT_MASK4 \
 	(PSR_UPDATE_MASK | PSR_CAPTURE_MASK | PSR_EXIT_MASK | \
 	PSR_UPDATE_ERROR_MASK | PSR_WAKE_ERROR_MASK)
@@ -355,6 +360,15 @@ void msm_dp_catalog_ctrl_state_ctrl(struct msm_dp_catalog *msm_dp_catalog, u32 s
 	msm_dp_write_link(catalog, REG_DP_STATE_CTRL, state);
 }
 
+void msm_dp_catalog_ctrl_mst_state_ctrl(struct msm_dp_catalog *msm_dp_catalog,
+					enum msm_dp_stream_id stream_id, u32 state)
+{
+	struct msm_dp_catalog_private *catalog = container_of(msm_dp_catalog,
+				struct msm_dp_catalog_private, msm_dp_catalog);
+
+	msm_dp_write_mst_link(catalog, stream_id, REG_DP_MST_LINK_STATE_CTRL, state);
+}
+
 void msm_dp_catalog_ctrl_config_ctrl(struct msm_dp_catalog *msm_dp_catalog,
 				     enum msm_dp_stream_id stream_id, u32 cfg)
 {
@@ -682,9 +696,12 @@ void msm_dp_catalog_ctrl_enable_irq(struct msm_dp_catalog *msm_dp_catalog,
 				DP_INTERRUPT_STATUS1_MASK);
 		msm_dp_write_ahb(catalog, REG_DP_INTR_STATUS2,
 				DP_INTERRUPT_STATUS2_MASK);
+		msm_dp_write_ahb(catalog, REG_DP_INTR_STATUS5,
+				 DP_INTERRUPT_STATUS5_MASK);
 	} else {
 		msm_dp_write_ahb(catalog, REG_DP_INTR_STATUS, 0x00);
 		msm_dp_write_ahb(catalog, REG_DP_INTR_STATUS2, 0x00);
+		msm_dp_write_ahb(catalog, REG_DP_INTR_STATUS5, 0x00);
 	}
 }
 
@@ -808,7 +825,7 @@ u32 msm_dp_catalog_hpd_get_intr_status(struct msm_dp_catalog *msm_dp_catalog)
 	return isr & (mask | ~DP_DP_HPD_INT_MASK);
 }
 
-u32 msm_dp_catalog_ctrl_read_psr_interrupt_status(struct msm_dp_catalog *msm_dp_catalog)
+u32 msm_dp_catalog_ctrl_get_interrupt_4(struct msm_dp_catalog *msm_dp_catalog)
 {
 	struct msm_dp_catalog_private *catalog = container_of(msm_dp_catalog,
 				struct msm_dp_catalog_private, msm_dp_catalog);
@@ -822,7 +839,7 @@ u32 msm_dp_catalog_ctrl_read_psr_interrupt_status(struct msm_dp_catalog *msm_dp_
 	return intr;
 }
 
-int msm_dp_catalog_ctrl_get_interrupt(struct msm_dp_catalog *msm_dp_catalog)
+int msm_dp_catalog_ctrl_get_interrupt_2(struct msm_dp_catalog *msm_dp_catalog)
 {
 	struct msm_dp_catalog_private *catalog = container_of(msm_dp_catalog,
 				struct msm_dp_catalog_private, msm_dp_catalog);
@@ -838,6 +855,23 @@ int msm_dp_catalog_ctrl_get_interrupt(struct msm_dp_catalog *msm_dp_catalog)
 	return intr;
 }
 
+int msm_dp_catalog_ctrl_get_interrupt_5(struct msm_dp_catalog *msm_dp_catalog)
+{
+	struct msm_dp_catalog_private *catalog = container_of(msm_dp_catalog,
+							      struct msm_dp_catalog_private,
+							      msm_dp_catalog);
+	u32 intr, intr_ack;
+
+	intr = msm_dp_read_ahb(catalog, REG_DP_INTR_STATUS5);
+	intr &= ~DP_INTERRUPT_STATUS5_MASK;
+	intr_ack = (intr & DP_INTERRUPT_STATUS5)
+			<< DP_INTERRUPT_STATUS_ACK_SHIFT;
+	msm_dp_write_ahb(catalog, REG_DP_INTR_STATUS5,
+			 intr_ack | DP_INTERRUPT_STATUS5_MASK);
+
+	return intr;
+}
+
 void msm_dp_catalog_ctrl_phy_reset(struct msm_dp_catalog *msm_dp_catalog)
 {
 	struct msm_dp_catalog_private *catalog = container_of(msm_dp_catalog,
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index 8d3887e00cfd4e62aacf51795fb54aef071e34ee..a1ecab2f386f321ea9d176b3cf3f894a230c9085 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -28,6 +28,9 @@
 #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 DP_HW_VERSION_1_0	0x10000000
 #define DP_HW_VERSION_1_2	0x10020000
 
@@ -63,6 +66,8 @@ u32 msm_dp_catalog_aux_get_irq(struct msm_dp_catalog *msm_dp_catalog);
 
 /* DP Controller APIs */
 void msm_dp_catalog_ctrl_state_ctrl(struct msm_dp_catalog *msm_dp_catalog, u32 state);
+void msm_dp_catalog_ctrl_mst_state_ctrl(struct msm_dp_catalog *msm_dp_catalog,
+					enum msm_dp_stream_id stream_id, u32 state);
 void msm_dp_catalog_ctrl_config_ctrl(struct msm_dp_catalog *msm_dp_catalog,
 				     enum msm_dp_stream_id stream_id, u32 config);
 void msm_dp_catalog_ctrl_lane_mapping(struct msm_dp_catalog *msm_dp_catalog);
@@ -88,8 +93,9 @@ void msm_dp_catalog_ctrl_set_psr(struct msm_dp_catalog *msm_dp_catalog, bool ent
 u32 msm_dp_catalog_link_is_connected(struct msm_dp_catalog *msm_dp_catalog);
 u32 msm_dp_catalog_hpd_get_intr_status(struct msm_dp_catalog *msm_dp_catalog);
 void msm_dp_catalog_ctrl_phy_reset(struct msm_dp_catalog *msm_dp_catalog);
-int msm_dp_catalog_ctrl_get_interrupt(struct msm_dp_catalog *msm_dp_catalog);
-u32 msm_dp_catalog_ctrl_read_psr_interrupt_status(struct msm_dp_catalog *msm_dp_catalog);
+int msm_dp_catalog_ctrl_get_interrupt_2(struct msm_dp_catalog *msm_dp_catalog);
+int msm_dp_catalog_ctrl_get_interrupt_5(struct msm_dp_catalog *msm_dp_catalog);
+u32 msm_dp_catalog_ctrl_get_interrupt_4(struct msm_dp_catalog *msm_dp_catalog);
 void msm_dp_catalog_ctrl_update_transfer_unit(struct msm_dp_catalog *msm_dp_catalog,
 				u32 msm_dp_tu, u32 valid_boundary,
 				u32 valid_boundary2);
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 5c3bfc39ebeaee1cd88f9cd9ced80b92fba21669..a61514d4b5d42cd92eb13aa7d6759cdc9c0dda71 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -28,6 +28,12 @@
 
 #define DP_CTRL_INTR_READY_FOR_VIDEO     BIT(0)
 #define DP_CTRL_INTR_IDLE_PATTERN_SENT  BIT(3)
+#define DP_CTRL_INTR_DP0_VCPF_SENT       BIT(0)
+#define DP_CTRL_INTR_DP1_VCPF_SENT       BIT(3)
+
+#define MST_DP0_PUSH_VCPF		BIT(12)
+#define MST_DP1_PUSH_VCPF		BIT(14)
+#define MST_LINK_PUSH_VCPF		BIT(12)
 
 #define MR_LINK_TRAINING1  0x8
 #define MR_LINK_SYMBOL_ERM 0x80
@@ -143,6 +149,35 @@ void msm_dp_ctrl_push_idle(struct msm_dp_ctrl *msm_dp_ctrl)
 	drm_dbg_dp(ctrl->drm_dev, "mainlink off\n");
 }
 
+void msm_dp_ctrl_push_vcpf(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *msm_dp_panel)
+{
+	struct msm_dp_ctrl_private *ctrl;
+	enum msm_dp_stream_id stream_id = msm_dp_panel->stream_id;
+	u32 state = 0x0;
+
+	ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
+
+	if (msm_dp_panel->stream_id == DP_STREAM_0)
+		state |= MST_DP0_PUSH_VCPF;
+	else if (msm_dp_panel->stream_id == DP_STREAM_1)
+		state |= MST_DP1_PUSH_VCPF;
+	else
+		state |= MST_LINK_PUSH_VCPF;
+
+	reinit_completion(&ctrl->idle_comp);
+
+	if (stream_id > DP_STREAM_1)
+		msm_dp_catalog_ctrl_mst_state_ctrl(ctrl->catalog, stream_id, state);
+	else
+		msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, state);
+
+	if (!wait_for_completion_timeout(&ctrl->idle_comp,
+					 IDLE_PATTERN_COMPLETION_TIMEOUT_JIFFIES))
+		pr_warn("PUSH_VCPF pattern timedout\n");
+
+	drm_dbg_dp(ctrl->drm_dev, "dp ctrl push vcpf\n");
+}
+
 static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl,
 				    struct msm_dp_panel *msm_dp_panel)
 {
@@ -2337,7 +2372,7 @@ irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl)
 	ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
 
 	if (ctrl->panel->psr_cap.version) {
-		isr = msm_dp_catalog_ctrl_read_psr_interrupt_status(ctrl->catalog);
+		isr = msm_dp_catalog_ctrl_get_interrupt_4(ctrl->catalog);
 
 		if (isr)
 			complete(&ctrl->psr_op_comp);
@@ -2352,7 +2387,7 @@ irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl)
 			drm_dbg_dp(ctrl->drm_dev, "PSR frame capture done\n");
 	}
 
-	isr = msm_dp_catalog_ctrl_get_interrupt(ctrl->catalog);
+	isr = msm_dp_catalog_ctrl_get_interrupt_2(ctrl->catalog);
 
 
 	if (isr & DP_CTRL_INTR_READY_FOR_VIDEO) {
@@ -2367,6 +2402,13 @@ irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl)
 		ret = IRQ_HANDLED;
 	}
 
+	isr = msm_dp_catalog_ctrl_get_interrupt_5(ctrl->catalog);
+	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;
+	}
+
 	return ret;
 }
 
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index 074efc0cc80bcc19d8beba8c831c6ed16ab9eb9a..98d779599be7d04e1c8a7f8b473a3496a119a9c1 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -51,5 +51,6 @@ void msm_dp_ctrl_mst_stream_channel_slot_setup(struct msm_dp_ctrl *msm_dp_ctrl,
 void msm_dp_ctrl_set_mst_channel_info(struct msm_dp_ctrl *msm_dp_ctrl,
 				      enum msm_dp_stream_id strm,
 				      u32 start_slot, u32 tot_slots);
+void msm_dp_ctrl_push_vcpf(struct msm_dp_ctrl *dp_ctrl, struct msm_dp_panel *msm_dp_panel);
 
 #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 47475e4c76d5d701890b8ace0b63685fd4466c94..754e6e1fedf2c116911e85e4cdac1110e88b2056 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1633,7 +1633,10 @@ 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);
+	if (msm_dp_display->mst_supported)
+		msm_dp_ctrl_push_vcpf(msm_dp_display->ctrl, msm_dp_display->panel);
+	else
+		msm_dp_ctrl_push_idle(msm_dp_display->ctrl);
 
 	if (msm_dp_display->mst_supported) {
 		msm_dp_ctrl_mst_stream_channel_slot_setup(msm_dp_display->ctrl,
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index 16182c7298eb351fd261391498e031d6213e96df..9d15f3fc26a28fba1ae960bff67bd9976f664eaa 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -24,8 +24,9 @@
 #define REG_DP_INTR_STATUS			(0x00000020)
 #define REG_DP_INTR_STATUS2			(0x00000024)
 #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)
@@ -112,6 +113,7 @@
 #define DP_MAINLINK_CTRL_MST_EN			(0x04000100)
 
 #define REG_DP_STATE_CTRL			(0x00000004)
+#define REG_DP_MST_LINK_STATE_CTRL		(0x00000000)
 #define DP_STATE_CTRL_LINK_TRAINING_PATTERN1	(0x00000001)
 #define DP_STATE_CTRL_LINK_TRAINING_PATTERN2	(0x00000002)
 #define DP_STATE_CTRL_LINK_TRAINING_PATTERN3	(0x00000004)

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 20/38] drm/msm/dp: always program MST_FIFO_CONSTANT_FILL for MST
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (18 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 19/38] drm/msm/dp: add support to send vcpf packets in dp controller Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 21/38] drm/msm/dp: abstract out the dp_display stream helpers to accept a panel Yongxing Mou
                   ` (19 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 required by the hardware programming guide, always program
the MST_FIFO_CONSTANT_FILL for MST use-cases.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_catalog.c | 16 ++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_catalog.h |  2 ++
 drivers/gpu/drm/msm/dp/dp_ctrl.c    |  2 ++
 3 files changed, 20 insertions(+)

diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index f2a5170723585ed6ddab2c12d2e5c5d6dee5eff5..b536530be160ed58ef17bf71385c3c4fc7617132 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -1011,6 +1011,22 @@ int msm_dp_catalog_panel_timing_cfg(struct msm_dp_catalog *msm_dp_catalog,
 	return 0;
 }
 
+int msm_dp_catalog_mst_async_fifo(struct msm_dp_catalog *msm_dp_catalog,
+				  enum msm_dp_stream_id stream_id, bool enable)
+{
+	struct msm_dp_catalog_private *catalog = container_of(msm_dp_catalog,
+							      struct msm_dp_catalog_private,
+							      msm_dp_catalog);
+
+	/* enable MST_FIFO_CONSTANT_FILL */
+	if (enable)
+		msm_dp_write_pn(catalog, stream_id, MMSS_DP_ASYNC_FIFO_CONFIG, 0x01);
+	else
+		msm_dp_write_pn(catalog, stream_id, MMSS_DP_ASYNC_FIFO_CONFIG, 0x00);
+
+	return 0;
+}
+
 static void msm_dp_catalog_panel_send_vsc_sdp(struct msm_dp_catalog *msm_dp_catalog,
 					      enum msm_dp_stream_id stream_id,
 					      struct dp_sdp *vsc_sdp)
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index a1ecab2f386f321ea9d176b3cf3f894a230c9085..7576458d4ef13def4f4d18073bd25ec0ffabde28 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -145,5 +145,7 @@ void msm_dp_catalog_mst_channel_alloc(struct msm_dp_catalog *dp_catalog,
 				      u32 tot_slot_cnt);
 void msm_dp_catalog_ctrl_update_rg(struct msm_dp_catalog *dp_catalog,
 				   enum msm_dp_stream_id stream_id, u32 x_int, u32 y_frac_enum);
+int msm_dp_catalog_mst_async_fifo(struct msm_dp_catalog *dp_catalog,
+				  enum msm_dp_stream_id stream_id, bool enable);
 
 #endif /* _DP_CATALOG_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index a61514d4b5d42cd92eb13aa7d6759cdc9c0dda71..7056d5638829e8510054614b19afbcaf8aba55b5 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -236,6 +236,8 @@ static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl
 	cc = msm_dp_link_get_colorimetry_config(ctrl->link);
 	msm_dp_catalog_ctrl_config_misc(ctrl->catalog, msm_dp_panel->stream_id, cc, tb);
 	msm_dp_panel_timing_cfg(msm_dp_panel);
+
+	msm_dp_catalog_mst_async_fifo(ctrl->catalog, msm_dp_panel->stream_id, ctrl->mst_active);
 }
 
 /*

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 21/38] drm/msm/dp: abstract out the dp_display stream helpers to accept a panel
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (19 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 20/38] drm/msm/dp: always program MST_FIFO_CONSTANT_FILL for MST Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 22/38] drm/msm/dp: move link related operations to dp_display_unprepare() Yongxing Mou
                   ` (18 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_display.c | 95 ++++++++++++++++++++++++++-----------
 drivers/gpu/drm/msm/dp/dp_display.h |  8 ++++
 2 files changed, 75 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 754e6e1fedf2c116911e85e4cdac1110e88b2056..68c74f30255d96f13ead817b69f481f41d53eec6 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -866,7 +866,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;
@@ -877,7 +878,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;
 
@@ -923,14 +924,15 @@ 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_ctrl_clear_vsc_sdp_pkt(dp->ctrl, dp->panel);
+	msm_dp_ctrl_clear_vsc_sdp_pkt(dp->ctrl, msm_dp_panel);
 
 	/* dongle is still connected but sinks are disconnected */
 	if (dp->link->sink_count == 0) {
@@ -1597,7 +1599,7 @@ void msm_dp_display_atomic_prepare(struct msm_dp *dp)
 	mutex_unlock(&msm_dp_display->event_mutex);
 }
 
-void msm_dp_display_atomic_enable(struct msm_dp *dp)
+void msm_dp_display_enable_helper(struct msm_dp *dp, struct msm_dp_panel *msm_dp_panel)
 {
 	int rc = 0;
 
@@ -1607,16 +1609,14 @@ void msm_dp_display_atomic_enable(struct msm_dp *dp)
 
 	mutex_lock(&msm_dp_display->event_mutex);
 
-	msm_dp_display_set_stream_info(dp, msm_dp_display->panel, 0, 0, 0, 0, 0);
-
 	if (dp->prepared) {
-		rc = msm_dp_display_enable(msm_dp_display);
+		rc = msm_dp_display_enable(msm_dp_display, msm_dp_panel);
 		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);
+			msm_dp_display_disable(msm_dp_display, msm_dp_panel);
 		}
 	}
 
@@ -1627,14 +1627,25 @@ void msm_dp_display_atomic_enable(struct msm_dp *dp)
 	mutex_unlock(&msm_dp_display->event_mutex);
 }
 
-void msm_dp_display_atomic_disable(struct msm_dp *dp)
+void msm_dp_display_atomic_enable(struct msm_dp *msm_dp)
+{
+	struct msm_dp_display_private *msm_dp_display;
+
+	msm_dp_display = container_of(msm_dp, struct msm_dp_display_private, msm_dp_display);
+
+	msm_dp_display_set_stream_info(msm_dp, msm_dp_display->panel, 0, 0, 0, 0, 0);
+
+	msm_dp_display_enable_helper(msm_dp, msm_dp_display->panel);
+}
+
+void msm_dp_display_disable_helper(struct msm_dp *dp, struct msm_dp_panel *msm_dp_panel)
 {
 	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->mst_supported)
-		msm_dp_ctrl_push_vcpf(msm_dp_display->ctrl, msm_dp_display->panel);
+		msm_dp_ctrl_push_vcpf(msm_dp_display->ctrl, msm_dp_panel);
 	else
 		msm_dp_ctrl_push_idle(msm_dp_display->ctrl);
 
@@ -1645,21 +1656,30 @@ void msm_dp_display_atomic_disable(struct msm_dp *dp)
 	}
 }
 
-static void msm_dp_display_unprepare(struct msm_dp_display_private *dp)
+void msm_dp_display_atomic_disable(struct msm_dp *msm_dp)
 {
-	struct msm_dp *msm_dp_display = &dp->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");
+	msm_dp_display = container_of(msm_dp, struct msm_dp_display_private, msm_dp_display);
+
+	msm_dp_display_disable_helper(msm_dp, msm_dp_display->panel);
+}
+
+static void msm_dp_display_unprepare(struct msm_dp_display_private *msm_dp_display_priv)
+{
+	struct msm_dp *msm_dp = &msm_dp_display_priv->msm_dp_display;
+
+	if (!msm_dp->prepared) {
+		drm_dbg_dp(msm_dp->drm_dev, "Link already setup, return\n");
 		return;
 	}
 
-	pm_runtime_put_sync(&msm_dp_display->pdev->dev);
+	pm_runtime_put_sync(&msm_dp->pdev->dev);
 
-	msm_dp_display->prepared = false;
+	msm_dp->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)
 {
 	u32 hpd_state;
 	struct msm_dp_display_private *msm_dp_display;
@@ -1678,7 +1698,7 @@ void msm_dp_display_atomic_post_disable(struct msm_dp *dp)
 
 	msm_dp_display_audio_notify_disable(msm_dp_display);
 
-	msm_dp_display_disable(msm_dp_display);
+	msm_dp_display_disable(msm_dp_display, msm_dp_panel);
 
 	hpd_state =  msm_dp_display->hpd_state;
 	if (hpd_state == ST_DISCONNECT_PENDING) {
@@ -1686,24 +1706,32 @@ void msm_dp_display_atomic_post_disable(struct msm_dp *dp)
 		msm_dp_display->hpd_state = ST_DISCONNECTED;
 	}
 
-	msm_dp_display_unprepare(msm_dp_display);
-
 	drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type);
 
 	mutex_unlock(&msm_dp_display->event_mutex);
 }
 
-void msm_dp_display_mode_set(struct msm_dp *dp,
-			     const struct drm_display_mode *mode,
-			     const struct drm_display_mode *adjusted_mode)
+void msm_dp_display_atomic_post_disable(struct msm_dp *msm_dp)
 {
 	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;
+	msm_dp_display = container_of(msm_dp, struct msm_dp_display_private, msm_dp_display);
+
+	msm_dp_display_atomic_post_disable_helper(msm_dp, msm_dp_display->panel);
+
+	msm_dp_display_unprepare(msm_dp_display);
+}
+
+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 *msm_dp_display;
 
-	msm_dp_display_set_mode(dp, adjusted_mode, msm_dp_panel);
+	msm_dp_display = 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 */
 	msm_dp_display->ctrl->wide_bus_en = msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 ?
@@ -1712,6 +1740,17 @@ void msm_dp_display_mode_set(struct msm_dp *dp,
 		false : msm_dp_display->wide_bus_supported;
 }
 
+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;
+
+	msm_dp_display = container_of(msm_dp, struct msm_dp_display_private, msm_dp_display);
+
+	msm_dp_display_mode_set_helper(msm_dp, mode, adjusted_mode, msm_dp_display->panel);
+}
+
 void msm_dp_bridge_hpd_enable(struct drm_bridge *bridge)
 {
 	struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(bridge);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 10a5be9337cf9b2ea90b3061ef8d0d6de3282431..96fec50601400ca44d1cdd310f2353e301874099 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -49,5 +49,13 @@ enum drm_mode_status msm_dp_display_mode_valid(struct msm_dp *dp,
 int msm_dp_display_set_stream_info(struct msm_dp *dp, 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, struct msm_dp_panel *msm_dp_panel);
+void msm_dp_display_disable_helper(struct msm_dp *msm_dp, struct msm_dp_panel *msm_dp_panel);
+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);
+void msm_dp_display_atomic_post_disable_helper(struct msm_dp *msm_dp,
+					       struct msm_dp_panel *msm_dp_panel);
 
 #endif /* _DP_DISPLAY_H_ */

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 22/38] drm/msm/dp: move link related operations to dp_display_unprepare()
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (20 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 21/38] drm/msm/dp: abstract out the dp_display stream helpers to accept a panel Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 23/38] drm/msm/dp: replace power_on with active_stream_cnt for dp_display Yongxing Mou
                   ` (17 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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>

Move the link related operations to dp_display_unprepare() and keep
only stream related operations in dp_display_disable().

Make dp_display_unprepare() available to other clients such as DP MST.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_display.c | 34 ++++++++++++++++++----------------
 drivers/gpu/drm/msm/dp/dp_display.h |  2 ++
 2 files changed, 20 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 68c74f30255d96f13ead817b69f481f41d53eec6..ea6113d1138b382f69d726cb16ceb04d82cbbe3b 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -934,18 +934,7 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp,
 
 	msm_dp_ctrl_clear_vsc_sdp_pkt(dp->ctrl, msm_dp_panel);
 
-	/* dongle is still connected but sinks are disconnected */
-	if (dp->link->sink_count == 0) {
-		msm_dp_ctrl_psm_config(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);
-	} else {
-		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, dp->panel->stream_id);
 
 	msm_dp_display->power_on = false;
 
@@ -961,7 +950,6 @@ int msm_dp_display_set_stream_info(struct msm_dp *dp, struct msm_dp_panel *panel
 	struct msm_dp_display_private *msm_dp_display;
 	const int max_slots = 64;
 
-
 	msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
 
 	if (!msm_dp_display) {
@@ -1665,15 +1653,29 @@ void msm_dp_display_atomic_disable(struct msm_dp *msm_dp)
 	msm_dp_display_disable_helper(msm_dp, msm_dp_display->panel);
 }
 
-static void msm_dp_display_unprepare(struct msm_dp_display_private *msm_dp_display_priv)
+void msm_dp_display_unprepare(struct msm_dp *msm_dp)
 {
-	struct msm_dp *msm_dp = &msm_dp_display_priv->msm_dp_display;
+	struct msm_dp_display_private *msm_dp_display;
+
+	msm_dp_display = container_of(msm_dp, struct msm_dp_display_private, msm_dp_display);
 
 	if (!msm_dp->prepared) {
 		drm_dbg_dp(msm_dp->drm_dev, "Link already setup, return\n");
 		return;
 	}
 
+	/* dongle is still connected but sinks are disconnected */
+	if (msm_dp_display->link->sink_count == 0)
+		msm_dp_ctrl_psm_config(msm_dp_display->ctrl);
+
+	msm_dp_ctrl_off_link(msm_dp_display->ctrl);
+
+	/* re-init the PHY so that we can listen to Dongle disconnect */
+	if (msm_dp_display->link->sink_count == 0)
+		msm_dp_ctrl_reinit_phy(msm_dp_display->ctrl);
+	else
+		msm_dp_display_host_phy_exit(msm_dp_display);
+
 	pm_runtime_put_sync(&msm_dp->pdev->dev);
 
 	msm_dp->prepared = false;
@@ -1719,7 +1721,7 @@ void msm_dp_display_atomic_post_disable(struct msm_dp *msm_dp)
 
 	msm_dp_display_atomic_post_disable_helper(msm_dp, msm_dp_display->panel);
 
-	msm_dp_display_unprepare(msm_dp_display);
+	msm_dp_display_unprepare(msm_dp);
 }
 
 void msm_dp_display_mode_set_helper(struct msm_dp *msm_dp,
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 96fec50601400ca44d1cdd310f2353e301874099..0b73087cbc421b4d2b895033f17892189ef68cb2 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -58,4 +58,6 @@ void msm_dp_display_mode_set_helper(struct msm_dp *msm_dp,
 void msm_dp_display_atomic_post_disable_helper(struct msm_dp *msm_dp,
 					       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] 108+ messages in thread

* [PATCH v2 23/38] drm/msm/dp: replace power_on with active_stream_cnt for dp_display
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (21 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 22/38] drm/msm/dp: move link related operations to dp_display_unprepare() Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 24/38] drm/msm/dp: make the SST bridge disconnected when mst is active Yongxing Mou
                   ` (16 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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>
---
 drivers/gpu/drm/msm/dp/dp_audio.c   |  2 +-
 drivers/gpu/drm/msm/dp/dp_display.c | 42 ++++++++++++++++++++++++-------------
 drivers/gpu/drm/msm/dp/dp_display.h |  3 ++-
 3 files changed, 31 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_audio.c b/drivers/gpu/drm/msm/dp/dp_audio.c
index f8bfb908f9b4bf93ad5480f0785e3aed23dde160..ba9ec1fb7c4d70d0f544fe5876d57290aab6e2c9 100644
--- a/drivers/gpu/drm/msm/dp/dp_audio.c
+++ b/drivers/gpu/drm/msm/dp/dp_audio.c
@@ -192,7 +192,7 @@ int msm_dp_audio_prepare(struct drm_connector *connector,
 	 * 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 ea6113d1138b382f69d726cb16ceb04d82cbbe3b..5c8b38babc778f3575ab26ae4238b5f9fdb65b51 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -111,6 +111,8 @@ struct msm_dp_display_private {
 
 	bool wide_bus_supported;
 
+	u32 active_stream_cnt;
+
 	struct msm_dp_audio *audio;
 	int max_stream;
 };
@@ -189,6 +191,15 @@ static const struct of_device_id msm_dp_dt_match[] = {
 	{}
 };
 
+int msm_dp_display_get_active_stream_cnt(struct msm_dp *msm_dp)
+{
+	struct msm_dp_display_private *msm_dp_display;
+
+	msm_dp_display = container_of(msm_dp, struct msm_dp_display_private, msm_dp_display);
+
+	return msm_dp_display->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);
@@ -679,7 +690,7 @@ static int msm_dp_hpd_unplug_handle(struct msm_dp_display_private *dp, u32 data)
 	 */
 	msm_dp_display_notify_disconnect(&dp->msm_dp_display.pdev->dev);
 
-	if (!dp->msm_dp_display.power_on) {
+	if (!dp->active_stream_cnt) {
 		dp->hpd_state = ST_DISCONNECTED;
 	} else {
 		dp->hpd_state = ST_DISCONNECT_PENDING;
@@ -854,7 +865,7 @@ static int msm_dp_display_prepare(struct msm_dp_display_private *dp)
 		return rc;
 	}
 
-	if (dp->hpd_state == ST_CONNECTED && !msm_dp_display->power_on) {
+	if (dp->hpd_state == ST_CONNECTED && !dp->active_stream_cnt) {
 		msm_dp_display_host_phy_init(dp);
 		force_link_train = true;
 	}
@@ -870,17 +881,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;
 }
@@ -927,16 +931,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_ctrl_clear_vsc_sdp_pkt(dp->ctrl, msm_dp_panel);
 
 	msm_dp_ctrl_off_pixel_clk(dp->ctrl, 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;
@@ -1076,7 +1078,7 @@ void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp)
 	 */
 	mutex_lock(&msm_dp_display->event_mutex);
 
-	if (!dp->power_on) {
+	if (!msm_dp_display->active_stream_cnt) {
 		mutex_unlock(&msm_dp_display->event_mutex);
 		return;
 	}
@@ -1608,6 +1610,8 @@ void msm_dp_display_enable_helper(struct msm_dp *dp, struct msm_dp_panel *msm_dp
 		}
 	}
 
+	msm_dp_display->active_stream_cnt++;
+
 	/* completed connection */
 	msm_dp_display->hpd_state = ST_CONNECTED;
 
@@ -1632,6 +1636,11 @@ void msm_dp_display_disable_helper(struct msm_dp *dp, struct msm_dp_panel *msm_d
 
 	msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
 
+	if (!msm_dp_display->active_stream_cnt) {
+		drm_dbg_dp(dp->drm_dev, "no active streams\n");
+		return;
+	}
+
 	if (msm_dp_display->mst_supported)
 		msm_dp_ctrl_push_vcpf(msm_dp_display->ctrl, msm_dp_panel);
 	else
@@ -1664,6 +1673,11 @@ void msm_dp_display_unprepare(struct msm_dp *msm_dp)
 		return;
 	}
 
+	if (msm_dp_display->active_stream_cnt) {
+		drm_dbg_dp(msm_dp->drm_dev, "stream still active, return\n");
+		return;
+	}
+
 	/* dongle is still connected but sinks are disconnected */
 	if (msm_dp_display->link->sink_count == 0)
 		msm_dp_ctrl_psm_config(msm_dp_display->ctrl);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 0b73087cbc421b4d2b895033f17892189ef68cb2..7b0efa342aaf878f3ae7315cb55902bf8bdcb6b9 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -18,7 +18,6 @@ struct msm_dp {
 	struct drm_bridge *next_bridge;
 	bool link_ready;
 	bool audio_enabled;
-	bool power_on;
 	bool prepared;
 	bool mst_active;
 	unsigned int connector_type;
@@ -60,4 +59,6 @@ void msm_dp_display_atomic_post_disable_helper(struct msm_dp *msm_dp,
 
 void msm_dp_display_unprepare(struct msm_dp *dp);
 
+int msm_dp_display_get_active_stream_cnt(struct msm_dp *msm_dp);
+
 #endif /* _DP_DISPLAY_H_ */

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 24/38] drm/msm/dp: make the SST bridge disconnected when mst is active
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (22 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 23/38] drm/msm/dp: replace power_on with active_stream_cnt for dp_display Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 25/38] drm/msm/dp: add an API to initialize MST on sink side Yongxing Mou
                   ` (15 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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>

This bridge detect function only work for SST, MST connectors detect
controlled by MST bridges. Skip detect in MST cases.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_drm.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index f0144cf3c5876d94c44a44adad766f242609113e..18a6f71b2792baab461bb368c353c56885ad68fe 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -29,7 +29,7 @@ static enum drm_connector_status msm_dp_bridge_detect(struct drm_bridge *bridge)
 	drm_dbg_dp(dp->drm_dev, "link_ready = %s\n",
 		str_true_false(dp->link_ready));
 
-	return (dp->link_ready) ? connector_status_connected :
+	return (dp->link_ready && !dp->mst_active) ? connector_status_connected :
 					connector_status_disconnected;
 }
 

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 25/38] drm/msm/dp: add an API to initialize MST on sink side
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (23 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 24/38] drm/msm/dp: make the SST bridge disconnected when mst is active Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 26/38] drm/msm/dp: skip reading the EDID for MST cases Yongxing Mou
                   ` (14 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_display.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 5c8b38babc778f3575ab26ae4238b5f9fdb65b51..88cae0ca66015377e59bee757462edeae5ae91bf 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"
@@ -390,6 +391,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;
@@ -431,6 +461,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);
 	rc = msm_dp_ctrl_on_link(dp->ctrl, dp_display->mst_active);
 	if (rc) {

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 26/38] drm/msm/dp: skip reading the EDID for MST cases
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (24 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 25/38] drm/msm/dp: add an API to initialize MST on sink side Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 15:58   ` Dmitry Baryshkov
  2025-06-09 12:21 ` [PATCH v2 27/38] drm/msm/dp: add dp_display_get_panel() to initialize DP panel Yongxing Mou
                   ` (13 subsequent siblings)
  39 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 MST cases, EDID is handled through AUX sideband messaging.
Skip the EDID read during hotplug handle for MST cases.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_display.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 88cae0ca66015377e59bee757462edeae5ae91bf..b1b025d1d356046f8f9e3d243fc774185df24318 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -438,9 +438,11 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
 	if (rc)
 		goto end;
 
-	rc = msm_dp_panel_read_edid(dp->panel, connector);
-	if (rc)
-		goto end;
+	if (!dp->mst_supported || !drm_dp_read_mst_cap(dp->aux, dp->panel->dpcd)) {
+		rc = msm_dp_panel_read_edid(dp->panel, connector);
+		if (rc)
+			goto end;
+	}
 
 	msm_dp_link_process_request(dp->link);
 

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 27/38] drm/msm/dp: add dp_display_get_panel() to initialize DP panel
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (25 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 26/38] drm/msm/dp: skip reading the EDID for MST cases Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 28/38] drm/msm/dp: add dp_mst_drm to manage DP MST bridge operations Yongxing Mou
                   ` (12 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_display.c | 21 +++++++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_display.h |  2 ++
 2 files changed, 23 insertions(+)

diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index b1b025d1d356046f8f9e3d243fc774185df24318..8ac1215d8a635bc5477e79e08264c344ed1b27ac 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -771,6 +771,27 @@ 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 *dp_display)
+{
+	struct msm_dp_display_private *dp;
+	struct msm_dp_panel *dp_panel;
+
+	dp = container_of(dp_display, struct msm_dp_display_private, msm_dp_display);
+
+	dp_panel = msm_dp_panel_get(&dp_display->pdev->dev, dp->aux, dp->link, dp->catalog);
+
+	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 7b0efa342aaf878f3ae7315cb55902bf8bdcb6b9..85eaa55fdcb7d9d8713849ec64a2cc9b08924425 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -61,4 +61,6 @@ void msm_dp_display_unprepare(struct msm_dp *dp);
 
 int msm_dp_display_get_active_stream_cnt(struct msm_dp *msm_dp);
 
+struct msm_dp_panel *msm_dp_display_get_panel(struct msm_dp *dp_display);
+
 #endif /* _DP_DISPLAY_H_ */

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 28/38] drm/msm/dp: add dp_mst_drm to manage DP MST bridge operations
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (26 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 27/38] drm/msm/dp: add dp_display_get_panel() to initialize DP panel Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 15:57   ` Dmitry Baryshkov
  2025-06-09 12:21 ` [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST Yongxing Mou
                   ` (11 subsequent siblings)
  39 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 <quic_yongmou@quicinc.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 | 501 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_mst_drm.h | 100 +++++++
 4 files changed, 606 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 7a2ada6e2d74a902879e4f12a78ed475e5209ec2..d317e5ecda28dfd708ccdc5d3d27d4cf0b78b9d5 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 85eaa55fdcb7d9d8713849ec64a2cc9b08924425..9ca045ed2b4f1a9bc3254b733d1ce5332ebaba96 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
@@ -24,6 +25,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..a3ea34ae63511db0ac920cbeebe30c4e2320b8c4
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
@@ -0,0 +1,501 @@
+// 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"
+
+static struct drm_private_state *msm_dp_mst_duplicate_bridge_state(struct drm_private_obj *obj)
+{
+	struct msm_dp_mst_bridge_state *state;
+
+	state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
+
+	return &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 *priv_state =
+		to_msm_dp_mst_bridge_priv_state(state);
+
+	kfree(priv_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_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;
+	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) {
+			/*
+			 * 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 = msm_dp_bridge->start_slot;
+				prev_slots = msm_dp_bridge->num_slots;
+				msm_dp_bridge->pbn = 0;
+				msm_dp_bridge->start_slot = 1;
+				msm_dp_bridge->num_slots = 0;
+				msm_dp_bridge->vcpi = 0;
+			} else { //add payload
+				msm_dp_bridge->pbn = payload->pbn;
+				msm_dp_bridge->start_slot = payload->vc_start_slot;
+				msm_dp_bridge->num_slots = payload->time_slots;
+				msm_dp_bridge->vcpi = payload->vcpi;
+			}
+		}
+	}
+
+	// Now commit all the updated payloads
+	for (i = 0; i < mst->max_streams; i++) {
+		msm_dp_bridge = &mst->mst_bridge[i];
+
+		//Shift payloads to the left if there was a removed payload.
+		if (payload->vc_start_slot < 0 && msm_dp_bridge->start_slot > prev_start)
+			msm_dp_bridge->start_slot -= prev_slots;
+
+		msm_dp_display_set_stream_info(mst->msm_dp, msm_dp_bridge->msm_dp_panel,
+					       msm_dp_bridge->id, msm_dp_bridge->start_slot,
+					       msm_dp_bridge->num_slots,
+					       msm_dp_bridge->pbn, msm_dp_bridge->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), msm_dp_bridge->vcpi,
+			   msm_dp_bridge->start_slot,
+			   msm_dp_bridge->num_slots, msm_dp_bridge->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 *msm_dp = dp_bridge->display;
+	struct msm_dp_mst *mst = msm_dp->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(msm_dp->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, 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 *msm_dp = dp_bridge->display;
+	struct msm_dp_mst *mst = msm_dp->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(msm_dp->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 *msm_dp = dp_bridge->display;
+	struct msm_dp_mst *mst = msm_dp->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, port);
+
+	drm_dbg_dp(msm_dp->drm_dev, "mst bridge [%d] _pre disable part-1 complete\n",
+		   dp_bridge->id);
+}
+
+static void _msm_dp_mst_bridge_pre_disable_part2(struct msm_dp_mst_bridge *dp_bridge)
+{
+	struct msm_dp *msm_dp = dp_bridge->display;
+	struct msm_dp_mst *mst = msm_dp->msm_dp_mst;
+
+	drm_dp_check_act_status(&mst->mst_mgr);
+
+	drm_dbg_dp(msm_dp->drm_dev, "mst bridge [%d] _pre disable part-2 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;
+	struct msm_dp_mst_bridge_state *msm_dp_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);
+	msm_dp_bridge_state = to_msm_dp_mst_bridge_state(bridge);
+	dp = bridge->display;
+	dp_mst = dp->msm_dp_mst;
+
+	/* to cover cases of bridge_disable/bridge_enable without modeset */
+	bridge->connector = msm_dp_bridge_state->connector;
+	bridge->msm_dp_panel = msm_dp_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);
+
+	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);
+		mutex_unlock(&dp_mst->mst_lock);
+		return;
+	}
+
+	msm_dp_display_enable_helper(dp, bridge->msm_dp_panel);
+
+	_msm_dp_mst_bridge_pre_enable_part2(bridge, state);
+
+	mutex_unlock(&dp_mst->mst_lock);
+
+	drm_dbg_dp(dp->drm_dev, "conn:%d mode:%s fps:%d vcpi:%d slots:%d to %d\n",
+		   DP_MST_CONN_ID(bridge), bridge->drm_mode.name,
+		   drm_mode_vrefresh(&bridge->drm_mode),
+		   bridge->vcpi, bridge->start_slot,
+		   bridge->start_slot + bridge->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;
+	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 = bridge->display;
+	mst = dp->msm_dp_mst;
+
+	mutex_lock(&mst->mst_lock);
+
+	_msm_dp_mst_bridge_pre_disable_part1(bridge, state);
+
+	msm_dp_display_disable_helper(dp, bridge->msm_dp_panel);
+
+	_msm_dp_mst_bridge_pre_disable_part2(bridge);
+
+	mutex_unlock(&mst->mst_lock);
+
+	drm_dbg_dp(dp->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;
+	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 = bridge->display;
+	mst = dp->msm_dp_mst;
+
+	mutex_lock(&mst->mst_lock);
+
+	msm_dp_display_atomic_post_disable_helper(dp, bridge->msm_dp_panel);
+
+	if (!dp->mst_active)
+		msm_dp_display_unprepare(dp);
+
+	bridge->connector = NULL;
+	bridge->msm_dp_panel =  NULL;
+
+	mutex_unlock(&mst->mst_lock);
+
+	drm_dbg_dp(dp->drm_dev, "mst bridge:%d conn:%d post disable complete\n",
+		   bridge->id, conn);
+}
+
+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 *dp_bridge_state;
+	struct msm_dp *dp;
+	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);
+
+	dp_bridge_state = to_msm_dp_mst_bridge_state(bridge);
+	bridge->connector = dp_bridge_state->connector;
+	bridge->msm_dp_panel = dp_bridge_state->msm_dp_panel;
+
+	msm_dp_panel = bridge->msm_dp_panel;
+	dp = bridge->display;
+
+	memcpy(&bridge->drm_mode, adjusted_mode, sizeof(bridge->drm_mode));
+	msm_dp_display_mode_set_helper(dp, 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->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,
+	.mode_set     = msm_dp_mst_bridge_mode_set,
+};
+
+int msm_dp_mst_drm_bridge_init(struct msm_dp *dp, struct drm_encoder *encoder)
+{
+	int rc = 0;
+	struct msm_dp_mst_bridge *bridge = NULL;
+	struct msm_dp_mst_bridge_state *state;
+	struct drm_device *dev;
+	struct msm_dp_mst *mst = dp->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->drm_dev;
+	bridge->display = dp;
+	bridge->base.funcs = &msm_dp_mst_bridge_ops;
+	bridge->base.encoder = encoder;
+	bridge->base.type = dp->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;
+	}
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state) {
+		rc = -ENOMEM;
+		goto end;
+	}
+
+	drm_atomic_private_obj_init(dev, &bridge->obj,
+				    &state->base,
+				    &msm_dp_mst_bridge_state_funcs);
+
+	drm_dbg_dp(dp->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..cdde59da7bb937ad67fa818a728082f8fd43a3a6
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
@@ -0,0 +1,100 @@
+/* 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"
+
+#define DP_MST_CONN_ID(bridge) ((bridge)->connector ? \
+		(bridge)->connector->base.id : 0)
+
+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_display_mode drm_mode;
+	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_bridge_state {
+	struct drm_private_state base;
+	struct drm_connector *connector;
+	struct msm_dp_panel *msm_dp_panel;
+	int num_slots;
+};
+
+struct msm_dp_mst {
+	struct drm_dp_mst_topology_mgr mst_mgr;
+	struct msm_dp_mst_bridge *mst_bridge;
+	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;
+};
+
+#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_priv_state(x) \
+		container_of((x), struct msm_dp_mst_bridge_state, base)
+#define to_msm_dp_mst_bridge_state(x) \
+		to_msm_dp_mst_bridge_priv_state((x)->obj.state)
+#define to_msm_dp_mst_connector(x) \
+		container_of((x), struct msm_dp_mst_connector, connector)
+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] 108+ messages in thread

* [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (27 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 28/38] drm/msm/dp: add dp_mst_drm to manage DP MST bridge operations Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 15:44   ` Dmitry Baryshkov
  2025-06-09 15:51   ` Dmitry Baryshkov
  2025-06-09 12:21 ` [PATCH v2 30/38] drm/msm/dp: add HPD callback for dp MST Yongxing Mou
                   ` (10 subsequent siblings)
  39 siblings, 2 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_mst_drm.c | 515 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_mst_drm.h |   3 +
 2 files changed, 518 insertions(+)

diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
index a3ea34ae63511db0ac920cbeebe30c4e2320b8c4..489fa46aa518ff1cc5f4769b2153fc5153c4cb41 100644
--- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
@@ -25,8 +25,12 @@
  * OF THIS SOFTWARE.
  */
 
+#include <drm/drm_edid.h>
+#include <drm/drm_managed.h>
 #include "dp_mst_drm.h"
 
+#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 *state;
@@ -79,6 +83,61 @@ static int msm_dp_mst_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, int p
 	return num_slots;
 }
 
+static int msm_dp_mst_get_mst_pbn_div(struct msm_dp_panel *msm_dp_panel)
+{
+	struct msm_dp_link_info *link_info;
+
+	link_info = &msm_dp_panel->link_info;
+
+	return link_info->rate * link_info->num_lanes / 54000;
+}
+
+static int msm_dp_mst_compute_config(struct drm_atomic_state *state,
+				      struct msm_dp_mst *mst, struct drm_connector *connector,
+				      struct drm_display_mode *mode)
+{
+	int slots = 0, pbn;
+	struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(connector);
+	int rc = 0;
+	struct drm_dp_mst_topology_state *mst_state;
+	int pbn_div;
+	struct msm_dp *dp_display = mst->msm_dp;
+	u32 bpp;
+
+	bpp = connector->display_info.bpc * 3;
+
+	pbn = drm_dp_calc_pbn_mode(mode->clock, bpp << 4);
+
+	mst_state = to_drm_dp_mst_topology_state(mst->mst_mgr.base.state);
+
+	if (!dfixed_trunc(mst_state->pbn_div)) {
+		pbn_div = msm_dp_mst_get_mst_pbn_div(mst_conn->dp_panel);
+		mst_state->pbn_div.full = dfixed_const(pbn_div);
+	}
+
+	rc = drm_dp_atomic_find_time_slots(state, &mst->mst_mgr, mst_conn->mst_port, pbn);
+	if (rc < 0) {
+		DRM_ERROR("conn:%d failed to find vcpi slots. pbn:%d, rc:%d\n",
+			  connector->base.id, pbn, rc);
+		goto end;
+	}
+
+	slots = rc;
+
+	rc = drm_dp_mst_atomic_check(state);
+	if (rc) {
+		DRM_ERROR("conn:%d mst atomic check failed: rc=%d\n", connector->base.id, rc);
+		slots = 0;
+		goto end;
+	}
+
+	drm_dbg_dp(dp_display->drm_dev, "conn:%d pbn:%d slots:%d rc:%d\n",
+		   connector->base.id, pbn, slots, rc);
+
+end:
+	return (rc < 0 ? rc : slots);
+}
+
 static void _msm_dp_mst_update_timeslots(struct msm_dp_mst *mst,
 					 struct msm_dp_mst_bridge *mst_bridge,
 					 struct drm_dp_mst_port *port)
@@ -499,3 +558,459 @@ int msm_dp_mst_drm_bridge_init(struct msm_dp *dp, struct drm_encoder *encoder)
 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;
+
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+
+	return to_msm_dp_mst_bridge_priv_state(drm_atomic_get_private_obj_state(st,
+										&bridge->obj));
+}
+
+/* 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;
+	int ret;
+
+	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);
+	ret = drm_edid_connector_add_modes(connector);
+	drm_edid_free(drm_edid);
+
+	return ret;
+}
+
+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;
+	u16 full_pbn, required_pbn;
+	int available_slots, required_slots;
+	struct msm_dp_mst_bridge_state *dp_bridge_state;
+	int i, slots_in_use = 0, active_enc_cnt = 0;
+	const u32 tot_slots = 63;
+
+	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++) {
+		dp_bridge_state = to_msm_dp_mst_bridge_state(&mst->mst_bridge[i]);
+		if (dp_bridge_state->connector &&
+		    dp_bridge_state->connector != connector) {
+			active_enc_cnt++;
+			slots_in_use += dp_bridge_state->num_slots;
+		}
+	}
+
+	if (active_enc_cnt < DP_STREAM_MAX) {
+		full_pbn = mst_port->full_pbn;
+		available_slots = tot_slots - slots_in_use;
+	} 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);
+
+	required_slots = msm_dp_mst_find_vcpi_slots(&mst->mst_mgr, required_pbn);
+
+	if (required_pbn > full_pbn || required_slots > available_slots) {
+		drm_dbg_dp(dp_display->drm_dev,
+			   "mode:%s not supported. pbn %d vs %d slots %d vs %d\n",
+			   mode->name, required_pbn, full_pbn,
+			   required_slots, available_slots);
+		return MODE_BAD;
+	}
+
+	return msm_dp_display_mode_valid(dp_display, &dp_display->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 *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++) {
+		bridge_state = msm_dp_mst_br_priv_state(state, &mst->mst_bridge[i]);
+		if (IS_ERR(bridge_state))
+			goto end;
+
+		if (bridge_state->connector == connector) {
+			enc = mst->mst_bridge[i].encoder;
+			goto end;
+		}
+	}
+
+	for (i = 0; i < mst->max_streams; i++) {
+		bridge_state = msm_dp_mst_br_priv_state(state, &mst->mst_bridge[i]);
+
+		if (!bridge_state->connector) {
+			bridge_state->connector = connector;
+			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, i;
+	bool vcpi_released = false;
+	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 *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;
+
+	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 mode_set;
+
+	old_crtc = old_conn_state->crtc;
+	if (!old_crtc)
+		goto mode_set;
+
+	crtc_state = drm_atomic_get_new_crtc_state(state, old_crtc);
+
+	for (i = 0; i < mst->max_streams; i++) {
+		bridge = &mst->mst_bridge[i];
+		drm_dbg_dp(dp_display->drm_dev, "bridge id:%d, vcpi:%d, pbn:%d, slots:%d\n",
+			   bridge->id, bridge->vcpi, bridge->pbn,
+			   bridge->num_slots);
+	}
+
+	/*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);
+
+		bridge_state = msm_dp_mst_br_priv_state(state, bridge);
+		if (IS_ERR(bridge_state)) {
+			rc = PTR_ERR(bridge_state);
+			goto end;
+		}
+
+		if (WARN_ON(bridge_state->connector != connector)) {
+			rc = -EINVAL;
+			goto end;
+		}
+
+		slots = bridge_state->num_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;
+			}
+			vcpi_released = true;
+		}
+
+		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
+			 */
+			bridge_state->num_slots = 0;
+			bridge_state->connector = NULL;
+			bridge_state->msm_dp_panel = NULL;
+
+			drm_dbg_dp(dp_display->drm_dev, "clear best encoder: %d\n", bridge->id);
+		}
+	}
+
+mode_set:
+	if (!new_conn_state->crtc)
+		goto end;
+
+	crtc_state = drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
+
+	if (drm_atomic_crtc_needs_modeset(crtc_state) && crtc_state->active) {
+		if (WARN_ON(!new_conn_state->best_encoder)) {
+			rc = -EINVAL;
+			goto end;
+		}
+
+		drm_bridge = drm_bridge_chain_get_first_bridge(new_conn_state->best_encoder);
+		if (WARN_ON(!drm_bridge)) {
+			rc = -EINVAL;
+			goto end;
+		}
+		bridge = to_msm_dp_mst_bridge(drm_bridge);
+
+		bridge_state = msm_dp_mst_br_priv_state(state, bridge);
+		if (IS_ERR(bridge_state)) {
+			rc = PTR_ERR(bridge_state);
+			goto end;
+		}
+
+		if (WARN_ON(bridge_state->connector != connector)) {
+			rc = -EINVAL;
+			goto end;
+		}
+
+		/*
+		 * check if vcpi slots are trying to get allocated in same phase
+		 * as deallocation. If so, go to end to avoid allocation.
+		 */
+		if (vcpi_released) {
+			drm_dbg_dp(dp_display->drm_dev,
+				   "skipping allocation since vcpi was released in the same state\n");
+			goto end;
+		}
+
+		if (WARN_ON(bridge_state->num_slots)) {
+			rc = -EINVAL;
+			goto end;
+		}
+
+		slots = msm_dp_mst_compute_config(state, mst, connector, &crtc_state->mode);
+		if (slots < 0) {
+			rc = slots;
+			goto end;
+		}
+
+		bridge_state->num_slots = slots;
+	}
+
+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);
+}
+
+/* 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_connector;
+	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_connector = devm_kzalloc(dev->dev, sizeof(*mst_connector), GFP_KERNEL);
+
+	drm_modeset_lock_all(dev);
+
+	rc = drm_connector_dynamic_init(dev, &mst_connector->connector,
+					&msm_dp_drm_mst_connector_funcs,
+					DRM_MODE_CONNECTOR_DisplayPort, NULL);
+	if (rc) {
+		drm_modeset_unlock_all(dev);
+		return NULL;
+	}
+
+	mst_connector->dp_panel = msm_dp_display_get_panel(dp_display);
+	if (!mst_connector->dp_panel) {
+		DRM_ERROR("failed to get dp_panel for connector\n");
+		drm_modeset_unlock_all(dev);
+		return NULL;
+	}
+
+	mst_connector->dp_panel->connector = &mst_connector->connector;
+	mst_connector->msm_dp = dp_display;
+	connector = &mst_connector->connector;
+	drm_connector_helper_add(&mst_connector->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(&mst_connector->connector,
+						  dp_mst->mst_bridge[i].encoder);
+		if (rc) {
+			DRM_ERROR("failed to attach encoder to connector, %d\n", rc);
+			drm_modeset_unlock_all(dev);
+			return NULL;
+		}
+	}
+
+	mst_connector->mst_port = port;
+	drm_dp_mst_get_port_malloc(mst_connector->mst_port);
+
+	drm_object_attach_property(&mst_connector->connector.base,
+				   dev->mode_config.path_property, 0);
+	drm_object_attach_property(&mst_connector->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",
+		   mst_connector->connector.base.id);
+
+	return &mst_connector->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;
+
+	msm_dp_mst->mst_bridge = drmm_kzalloc(dev, max_streams * sizeof(struct msm_dp_mst_bridge),
+					      GFP_KERNEL);
+
+	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 cdde59da7bb937ad67fa818a728082f8fd43a3a6..7b08346e0bb9189009c02745c243722c01d79493 100644
--- a/drivers/gpu/drm/msm/dp/dp_mst_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
@@ -75,6 +75,7 @@ struct msm_dp_mst {
 	struct drm_dp_mst_topology_mgr mst_mgr;
 	struct msm_dp_mst_bridge *mst_bridge;
 	struct msm_dp *msm_dp;
+	struct drm_dp_aux *dp_aux;
 	u32 max_streams;
 	struct mutex mst_lock;
 };
@@ -97,4 +98,6 @@ struct msm_dp_mst_connector {
 		container_of((x), struct msm_dp_mst_connector, 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] 108+ messages in thread

* [PATCH v2 30/38] drm/msm/dp: add HPD callback for dp MST
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (28 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 15:01   ` Dmitry Baryshkov
  2025-06-09 12:21 ` [PATCH v2 31/38] drm/msm/dp: propagate MST state changes to dp mst module Yongxing Mou
                   ` (9 subsequent siblings)
  39 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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>
---
 drivers/gpu/drm/msm/dp/dp_display.c | 20 ++++++++++++++------
 drivers/gpu/drm/msm/dp/dp_mst_drm.c | 34 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_mst_drm.h |  2 ++
 3 files changed, 50 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 8ac1215d8a635bc5477e79e08264c344ed1b27ac..323d1c0a9efa4fa30ce97317e873607c54409a11 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -599,9 +599,10 @@ 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;
 
 	/* check for any test request issued by sink */
 	rc = msm_dp_link_process_request(dp->link);
@@ -609,12 +610,18 @@ static int msm_dp_display_usbpd_attention_cb(struct device *dev)
 		sink_request = dp->link->sink_request;
 		drm_dbg_dp(dp->drm_dev, "hpd_state=%d sink_request=%d\n",
 					dp->hpd_state, sink_request);
-		if (sink_request & DS_PORT_STATUS_CHANGED)
-			rc = msm_dp_display_handle_port_status_changed(dp);
-		else
+		if (sink_request & DS_PORT_STATUS_CHANGED) {
+			if (!msm_dp_display->mst_active)
+				rc = msm_dp_display_handle_port_status_changed(dp);
+		} else {
 			rc = msm_dp_display_handle_irq_hpd(dp);
+		}
 	}
 
+	/* let MST specific IRQ events be handled by its callback */
+	if (msm_dp_display->mst_active)
+		msm_dp_mst_display_hpd_irq(&dp->msm_dp_display);
+
 	return rc;
 }
 
@@ -754,7 +761,8 @@ static int msm_dp_irq_hpd_handle(struct msm_dp_display_private *dp, u32 data)
 	drm_dbg_dp(dp->drm_dev, "Before, type=%d hpd_state=%d\n",
 			dp->msm_dp_display.connector_type, state);
 
-	if (state == ST_MAINLINK_READY || state == ST_DISCONNECT_PENDING) {
+	if ((state == ST_MAINLINK_READY || state == ST_DISCONNECT_PENDING) &&
+	    !dp->msm_dp_display.mst_active) {
 		/* wait until ST_CONNECTED */
 		msm_dp_add_event(dp, EV_IRQ_HPD_INT, 0, 1); /* delay = 1 */
 		mutex_unlock(&dp->event_mutex);
diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
index 489fa46aa518ff1cc5f4769b2153fc5153c4cb41..5f00ec2f83ad09b94fd6b602fc37f91cb55acf71 100644
--- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
@@ -570,6 +570,40 @@ static struct msm_dp_mst_bridge_state *msm_dp_mst_br_priv_state(struct drm_atomi
 										&bridge->obj));
 }
 
+/* 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 7b08346e0bb9189009c02745c243722c01d79493..d40cb8483f971b1331172b4e5da2c8f67e55e119 100644
--- a/drivers/gpu/drm/msm/dp/dp_mst_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
@@ -100,4 +100,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] 108+ messages in thread

* [PATCH v2 31/38] drm/msm/dp: propagate MST state changes to dp mst module
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (29 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 30/38] drm/msm/dp: add HPD callback for dp MST Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 14:56   ` Dmitry Baryshkov
  2025-06-09 12:21 ` [PATCH v2 32/38] drm/msm: add support for non-blocking commits Yongxing Mou
                   ` (8 subsequent siblings)
  39 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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: Yongxing Mou <quic_yongmou@quicinc.com>
Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_display.c | 20 ++++++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_mst_drm.c | 15 +++++++++++++++
 drivers/gpu/drm/msm/dp/dp_mst_drm.h |  1 +
 3 files changed, 36 insertions(+)

diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 323d1c0a9efa4fa30ce97317e873607c54409a11..9dbcf4553cad70c9e3722160a87403fc815765d7 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -29,6 +29,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);
@@ -420,6 +421,17 @@ static void msm_dp_display_mst_init(struct msm_dp_display_private *dp)
 	msm_dp->mst_active = true;
 }
 
+static void msm_dp_display_set_mst_mgr_state(struct msm_dp_display_private *dp,
+					     bool state)
+{
+	if (!dp->msm_dp_display.mst_active)
+		return;
+
+	msm_dp_mst_display_set_mgr_state(&dp->msm_dp_display, state);
+
+	drm_dbg_dp(dp->drm_dev, "mst_mgr_state: %d\n", state);
+}
+
 static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
 {
 	struct drm_connector *connector = dp->msm_dp_display.connector;
@@ -473,6 +485,8 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
 		goto end;
 	}
 
+	msm_dp_display_set_mst_mgr_state(dp, true);
+
 	msm_dp_add_event(dp, EV_USER_NOTIFICATION, true, 0);
 
 end:
@@ -539,6 +553,12 @@ static int msm_dp_display_usbpd_configure_cb(struct device *dev)
 static int msm_dp_display_notify_disconnect(struct device *dev)
 {
 	struct msm_dp_display_private *dp = dev_get_dp_display_private(dev);
+	struct msm_dp *dp_display = &dp->msm_dp_display;
+
+	if (dp_display->mst_active) {
+		msm_dp_mst_display_set_mgr_state(&dp->msm_dp_display, false);
+		dp_display->mst_active = false;
+	}
 
 	msm_dp_add_event(dp, EV_USER_NOTIFICATION, false, 0);
 
diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
index 5f00ec2f83ad09b94fd6b602fc37f91cb55acf71..e8297d2222b07146fcfb2d98ab2053dd77717744 100644
--- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
@@ -998,6 +998,21 @@ msm_dp_mst_add_connector(struct drm_dp_mst_topology_mgr *mgr,
 	return &mst_connector->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 d40cb8483f971b1331172b4e5da2c8f67e55e119..de31baa7548a51ff02e758ec43b03a0a31a217a0 100644
--- a/drivers/gpu/drm/msm/dp/dp_mst_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
@@ -101,5 +101,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] 108+ messages in thread

* [PATCH v2 32/38] drm/msm: add support for non-blocking commits
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (30 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 31/38] drm/msm/dp: propagate MST state changes to dp mst module Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 14:50   ` Dmitry Baryshkov
  2025-06-09 12:21 ` [PATCH v2 33/38] drm/msm: initialize DRM MST encoders for DP controllers Yongxing Mou
                   ` (7 subsequent siblings)
  39 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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>

Hook up the mst framework APIs with atomic_commit_setup() and
atomic_commit_tail() APIs to handle non-blocking commits.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.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 87a91148a731dc911f30695add4c8f5002770220..655a5c90487d7176422c2be630aa029d2c64042e 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>
 
@@ -221,6 +222,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 35d5397e73b4c5cb90b1770e8570277e782be7ec..ec33afe27506892591e4c3e2671dab7a872b5525 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>
@@ -28,6 +29,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] 108+ messages in thread

* [PATCH v2 33/38] drm/msm: initialize DRM MST encoders for DP controllers
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (31 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 32/38] drm/msm: add support for non-blocking commits Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 14:17   ` Dmitry Baryshkov
  2025-06-09 12:21 ` [PATCH v2 34/38] drm/msm/dp: initialize dp_mst module for each DP MST controller Yongxing Mou
                   ` (6 subsequent siblings)
  39 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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>

Initiliaze 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 <quic_yongmou@quicinc.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_display.c         | 14 ++++++++++++++
 drivers/gpu/drm/msm/msm_drv.h               | 13 +++++++++++++
 4 files changed, 51 insertions(+), 1 deletion(-)

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 1fd82b6747e9058ce11dc2620729921492d5ebdd..45fedf7e74e9c6dfed4bde57eb675e3dd1762fc7 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -652,7 +652,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->dp); i++) {
 		if (!priv->dp[i])
@@ -675,6 +676,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->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_bridge_init(priv->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_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 9dbcf4553cad70c9e3722160a87403fc815765d7..ab1ad0cb6427eb4f86ee8ac6c76788b1a78892a8 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1417,6 +1417,20 @@ static int msm_dp_display_get_connector_type(struct platform_device *pdev,
 	return connector_type;
 }
 
+int msm_dp_get_mst_max_stream(struct msm_dp *dp_display)
+{
+	struct msm_dp_display_private *dp_priv;
+
+	dp_priv = container_of(dp_display, struct msm_dp_display_private, msm_dp_display);
+
+	return dp_priv->max_stream;
+}
+
+int msm_dp_mst_bridge_init(struct msm_dp *dp_display, struct drm_encoder *encoder)
+{
+	return msm_dp_mst_drm_bridge_init(dp_display, encoder);
+}
+
 static int msm_dp_display_probe(struct platform_device *pdev)
 {
 	int rc = 0;
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index a65077855201746c37ee742364b61116565f3794..dd403107b640ee5ef333d2773b52e38e3869155f 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -372,6 +372,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_bridge_init(struct msm_dp *dp_display, struct drm_encoder *encoder);
+
 #else
 static inline int __init msm_dp_register(void)
 {
@@ -388,6 +391,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_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] 108+ messages in thread

* [PATCH v2 34/38] drm/msm/dp: initialize dp_mst module for each DP MST controller
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (32 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 33/38] drm/msm: initialize DRM MST encoders for DP controllers Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 14:27   ` Dmitry Baryshkov
  2025-06-09 12:21 ` [PATCH v2 35/38] drm/msm/dpu: use msm_dp_get_mst_intf_id() to get the intf id Yongxing Mou
                   ` (5 subsequent siblings)
  39 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 <quic_yongmou@quicinc.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 45fedf7e74e9c6dfed4bde57eb675e3dd1762fc7..e030476dc4c69448886c29bcfe8ff3105949b129 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -680,6 +680,12 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev,
 		stream_cnt = msm_dp_get_mst_max_stream(priv->dp[i]);
 
 		if (stream_cnt > 1) {
+			rc = msm_dp_mst_register(priv->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 ab1ad0cb6427eb4f86ee8ac6c76788b1a78892a8..526389c718edccbac9b5a91e8dabf0d84ed1a8b0 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1667,6 +1667,15 @@ int msm_dp_modeset_init(struct msm_dp *msm_dp_display, struct drm_device *dev,
 	return 0;
 }
 
+int msm_dp_mst_register(struct msm_dp *dp)
+{
+	struct msm_dp_display_private *dp_display;
+
+	dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
+
+	return msm_dp_mst_init(dp, dp_display->max_stream, dp_display->aux);
+}
+
 void msm_dp_display_atomic_prepare(struct msm_dp *dp)
 {
 	int rc = 0;
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index dd403107b640ee5ef333d2773b52e38e3869155f..1496700c38ad73d6edcf56fbb0ebf66505c608bf 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -374,6 +374,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_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)
@@ -401,6 +402,11 @@ static inline int msm_dp_mst_bridge_init(struct msm_dp *dp_display, struct drm_e
 	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] 108+ messages in thread

* [PATCH v2 35/38] drm/msm/dpu: use msm_dp_get_mst_intf_id() to get the intf id
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (33 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 34/38] drm/msm/dp: initialize dp_mst module for each DP MST controller Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 14:44   ` Dmitry Baryshkov
  2025-06-09 12:21 ` [PATCH v2 36/38] drm/msm/dp: mark ST_DISCONNECTED only if all streams are disabled Yongxing Mou
                   ` (4 subsequent siblings)
  39 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 intf 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 <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 21 ++++++++++----
 drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c     | 43 +++++++++++++++++++++++++++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h     |  2 +-
 3 files changed, 60 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index 7020098360e474ee149824a488d912a7ad8ed06a..75cc2d475440fcdc941aa9eb19e78a87e83b5f5f 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -2611,11 +2611,13 @@ uint32_t dpu_encoder_get_clones(struct drm_encoder *drm_enc)
 
 static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
 				 struct dpu_kms *dpu_kms,
-				 struct msm_display_info *disp_info)
+				 struct msm_display_info *disp_info,
+				 int drm_enc_mode)
 {
 	int ret = 0;
 	int i = 0;
 	struct dpu_enc_phys_init_params phys_params;
+	u32 intf_id;
 
 	if (!dpu_enc) {
 		DPU_ERROR("invalid arg(s), enc %d\n", dpu_enc != NULL);
@@ -2658,9 +2660,18 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
 		DPU_DEBUG("h_tile_instance %d = %d, split_role %d\n",
 				i, controller_id, phys_params.split_role);
 
-		phys_params.hw_intf = dpu_encoder_get_intf(dpu_kms->catalog, &dpu_kms->rm,
-							   disp_info->intf_type,
-							   controller_id);
+		if (drm_enc_mode == DRM_MODE_ENCODER_DPMST) {
+			intf_id = dpu_kms_get_mst_intf_id(dpu_kms, controller_id,
+							     disp_info->stream_id);
+			DPU_DEBUG("intf_id %d for disp_info->stream_id = %d\n", intf_id,
+				  disp_info->stream_id);
+			phys_params.hw_intf = dpu_rm_get_intf(&dpu_kms->rm, intf_id);
+
+		} else {
+			phys_params.hw_intf = dpu_encoder_get_intf(dpu_kms->catalog, &dpu_kms->rm,
+								   disp_info->intf_type,
+								   controller_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);
@@ -2760,7 +2771,7 @@ struct drm_encoder *dpu_encoder_init(struct drm_device *dev,
 	mutex_init(&dpu_enc->enc_lock);
 	mutex_init(&dpu_enc->rc_lock);
 
-	ret = dpu_encoder_setup_display(dpu_enc, dpu_kms, disp_info);
+	ret = dpu_encoder_setup_display(dpu_enc, dpu_kms, disp_info, drm_enc_mode);
 	if (ret) {
 		DPU_ERROR("failed to setup encoder\n");
 		return ERR_PTR(-ENOMEM);
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index e030476dc4c69448886c29bcfe8ff3105949b129..f82dcf7c6dd31f078bbe4afe55d4667a4867f0b7 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -574,6 +574,49 @@ static int dpu_kms_dsi_set_te_source(struct msm_display_info *info,
 	return -EINVAL;
 }
 
+u32 dpu_kms_get_mst_intf_id(struct dpu_kms *dpu_kms, int controller_id, int stream_id)
+{
+	const struct dpu_mdss_cfg *catalog = dpu_kms->catalog;
+	int i;
+	int cnt = 0;
+	u32 intf_id = INTF_0;
+
+	/* The intf order in dpu_intf_cfg matches the mapping in the DP HPG.
+	 * 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   |
+	 * +-------------+----------+----------+
+	 */
+	for (i = 0; i < catalog->intf_count; i++) {
+		const struct dpu_intf_cfg *intf = &catalog->intf[i];
+
+		if (intf->type == INTF_DP && controller_id == intf->controller_id) {
+			if (cnt == stream_id) {
+				intf_id = intf->id;
+				break;
+			}
+
+			cnt++;
+		}
+	}
+
+	return intf_id;
+}
+
 static int _dpu_kms_initialize_dsi(struct drm_device *dev,
 				    struct msm_drm_private *priv,
 				    struct dpu_kms *dpu_kms)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h
index a57ec2ec106083e8f93578e4307e8b13ae549c08..388cd8f84fd579ce30a69989be5ac116bb727878 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h
@@ -168,5 +168,5 @@ int dpu_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc);
 void dpu_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc);
 
 unsigned long dpu_kms_get_clk_rate(struct dpu_kms *dpu_kms, char *clock_name);
-
+u32 dpu_kms_get_mst_intf_id(struct dpu_kms *dpu_kms, int controller_id, int stream_id);
 #endif /* __dpu_kms_H__ */

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 36/38] drm/msm/dp: mark ST_DISCONNECTED only if all streams are disabled
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (34 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 35/38] drm/msm/dpu: use msm_dp_get_mst_intf_id() to get the intf id Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 12:21 ` [PATCH v2 37/38] drm/msm/dp: fix the intf_type of MST interfaces Yongxing Mou
                   ` (3 subsequent siblings)
  39 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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>

HPD state machine assumes only one active stream. Fix it to account
for both while marking the state of the hpd as disconnected.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.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 526389c718edccbac9b5a91e8dabf0d84ed1a8b0..75f2fd7c75eae81e5c843f8ae2d1ce12ad0cad7e 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1824,7 +1824,7 @@ void msm_dp_display_atomic_post_disable_helper(struct msm_dp *dp, struct msm_dp_
 	msm_dp_display_disable(msm_dp_display, msm_dp_panel);
 
 	hpd_state =  msm_dp_display->hpd_state;
-	if (hpd_state == ST_DISCONNECT_PENDING) {
+	if (hpd_state == ST_DISCONNECT_PENDING && !msm_dp_display->active_stream_cnt) {
 		/* completed disconnection */
 		msm_dp_display->hpd_state = ST_DISCONNECTED;
 	}

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 108+ messages in thread

* [PATCH v2 37/38] drm/msm/dp: fix the intf_type of MST interfaces
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (35 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 36/38] drm/msm/dp: mark ST_DISCONNECTED only if all streams are disabled Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 14:45   ` Dmitry Baryshkov
  2025-06-09 12:21 ` [PATCH v2 38/38] drm/msm/dp: Add MST stream support for SA8775P DP controller 0 and 1 Yongxing Mou
                   ` (2 subsequent siblings)
  39 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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.
Fix this to INTF_DP.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
---
 drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

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 c248b3b55c410d8e374b8b659eeddbb657bbe854..b81bf54388557c380df0018921c6c1dbb7839d08 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
@@ -374,7 +374,7 @@ static const struct dpu_intf_cfg sa8775p_intf[] = {
 		.name = "intf_3", .id = INTF_3,
 		.base = 0x37000, .len = 0x280,
 		.features = INTF_SC7280_MASK,
-		.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),
@@ -392,7 +392,7 @@ static const struct dpu_intf_cfg sa8775p_intf[] = {
 		.name = "intf_6", .id = INTF_6,
 		.base = 0x3A000, .len = 0x280,
 		.features = INTF_SC7280_MASK,
-		.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),
@@ -401,7 +401,7 @@ static const struct dpu_intf_cfg sa8775p_intf[] = {
 		.name = "intf_7", .id = INTF_7,
 		.base = 0x3b000, .len = 0x280,
 		.features = INTF_SC7280_MASK,
-		.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),
@@ -410,7 +410,7 @@ static const struct dpu_intf_cfg sa8775p_intf[] = {
 		.name = "intf_8", .id = INTF_8,
 		.base = 0x3c000, .len = 0x280,
 		.features = INTF_SC7280_MASK,
-		.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] 108+ messages in thread

* [PATCH v2 38/38] drm/msm/dp: Add MST stream support for SA8775P DP controller 0 and 1
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (36 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 37/38] drm/msm/dp: fix the intf_type of MST interfaces Yongxing Mou
@ 2025-06-09 12:21 ` Yongxing Mou
  2025-06-09 14:47   ` Dmitry Baryshkov
  2025-06-09 12:36 ` [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Dmitry Baryshkov
  2025-06-09 16:07 ` Dmitry Baryshkov
  39 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-09 12:21 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 support for Multi-Stream Transport (MST), allowing
each controller to handle up to two DisplayPort streams. As all
necessary code for MST support was already implemented in the previous
series of patches.

Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.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 75f2fd7c75eae81e5c843f8ae2d1ce12ad0cad7e..16196dcc9ff4ac6a35b6bcd8d433b08f7d18fe5b 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -127,8 +127,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 = 2},
+	{ .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] 108+ messages in thread

* Re: [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (37 preceding siblings ...)
  2025-06-09 12:21 ` [PATCH v2 38/38] drm/msm/dp: Add MST stream support for SA8775P DP controller 0 and 1 Yongxing Mou
@ 2025-06-09 12:36 ` Dmitry Baryshkov
  2025-06-10  4:47   ` Yongxing Mou
  2025-06-09 16:07 ` Dmitry Baryshkov
  39 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 12:36 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:19PM +0800, Yongxing Mou wrote:
> 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.

Which means that you didn't validate the MST interaction with the USB-C
stack (there is a significant difference in the way HPD event is handled
in the Linux kernel).

> 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/seriedds/142010/ (v2 to fix up HPD)

This series has serious concerns and most likely will not be merged. Not
to mention that the URL is invalid.

> 
> Bindings for the pixel clock for additional stream is available at :
> 
> [2] : https://patchwork.freedesktop.org/series/142016/

This series needs another revision.

Not to mention that I plan to land [3] this cycle

[3] http://lore.kernel.org/dri-devel/20250518-fd-dp-audio-fixup-v6-0-2f0ec3ec000d@oss.qualcomm.com


> 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.

Validation should be using IGT for testing. Please ensure that there are
no regressions.

> 
> 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 <quic_yongmou@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 (35):
>       drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid
>       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: move the pixel clock control to its own API
>       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 the max_streams for dp controller
>       drm/msm/dp: introduce stream_id for each DP panel
>       drm/msm/dp: add support for programming p1/p2/p3 register block
>       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 program mst support in mainlink
>       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 to send vcpf packets in dp controller
>       drm/msm/dp: always program MST_FIFO_CONSTANT_FILL for MST
>       drm/msm/dp: abstract out the dp_display stream helpers to accept a panel
>       drm/msm/dp: move link related operations to dp_display_unprepare()
>       drm/msm/dp: replace power_on with active_stream_cnt for dp_display
>       drm/msm/dp: make the SST bridge disconnected when mst is active
>       drm/msm/dp: add an API to initialize MST on sink side
>       drm/msm/dp: skip reading the EDID for MST cases
>       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 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: mark ST_DISCONNECTED only if all streams are disabled
>       drm/msm/dp: fix the intf_type of MST interfaces
> 
> Yongxing Mou (3):
>       drm/msm/dp: Add catalog support for 3rd/4th stream MST
>       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_4_sa8775p.h    |    8 +-
>  drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c        |   21 +-
>  drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h        |    2 +
>  drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c            |   72 +-
>  drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h            |    2 +-
>  drivers/gpu/drm/msm/dp/dp_audio.c                  |    2 +-
>  drivers/gpu/drm/msm/dp/dp_catalog.c                |  558 ++++++++--
>  drivers/gpu/drm/msm/dp/dp_catalog.h                |   64 +-
>  drivers/gpu/drm/msm/dp/dp_ctrl.c                   |  474 ++++++---
>  drivers/gpu/drm/msm/dp/dp_ctrl.h                   |   22 +-
>  drivers/gpu/drm/msm/dp/dp_display.c                |  510 +++++++---
>  drivers/gpu/drm/msm/dp/dp_display.h                |   33 +-
>  drivers/gpu/drm/msm/dp/dp_drm.c                    |   53 +-
>  drivers/gpu/drm/msm/dp/dp_drm.h                    |   12 -
>  drivers/gpu/drm/msm/dp/dp_mst_drm.c                | 1065 ++++++++++++++++++++
>  drivers/gpu/drm/msm/dp/dp_mst_drm.h                |  106 ++
>  drivers/gpu/drm/msm/dp/dp_panel.c                  |   66 +-
>  drivers/gpu/drm/msm/dp/dp_panel.h                  |   10 +-
>  drivers/gpu/drm/msm/dp/dp_reg.h                    |   46 +-
>  drivers/gpu/drm/msm/msm_atomic.c                   |    3 +
>  drivers/gpu/drm/msm/msm_drv.h                      |   19 +
>  drivers/gpu/drm/msm/msm_kms.c                      |    2 +
>  23 files changed, 2725 insertions(+), 428 deletions(-)
> ---
> base-commit: 475c850a7fdd0915b856173186d5922899d65686
> change-id: 20250609-msm-dp-mst-cddc2f61daee
> prerequisite-message-id: <20250529-hpd_display_off-v1-0-ce33bac2987c@oss.qualcomm.com>
> prerequisite-patch-id: a1f426b99b4a99d63daa9902cde9ee38ae1128d1
> prerequisite-patch-id: ae9e0a0db8edd05da06f9673e9de56761654ed3c
> prerequisite-patch-id: 7cb84491c6c3cf73480343218c543d090f8cb5e2
> prerequisite-patch-id: f32638e79dd498db2075735392e85729b1b691fc
> prerequisite-message-id: <20250530-dp_mst_bindings-v2-0-f925464d32a8@oss.qualcomm.com>
> prerequisite-patch-id: e505c21f653c8e18ce83cad1fc787c13a6c8ed12
> prerequisite-patch-id: cfdd5c37d38b2a4f1386af4021ba3920c6d8dcf8
> prerequisite-patch-id: f4abdddcb90c8203044395f4768d794214fe3225
> prerequisite-patch-id: 45013dfaf34856422b7b6b3d2ee42d81a917177b
> prerequisite-patch-id: 2f35bedb0410bead1b66cbfaf51984fc7016828f
> 
> Best regards,
> -- 
> Yongxing Mou <quic_yongmou@quicinc.com>
> 

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 01/38] drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid
  2025-06-09 12:21 ` [PATCH v2 01/38] drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid Yongxing Mou
@ 2025-06-09 12:41   ` Dmitry Baryshkov
  2025-06-25  8:43     ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 12:41 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:20PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
> 
> In preparation of DP MST where link caps are read for the
> immediate downstream device and the edid is read through

EDID, not edid. Please review all your patches for up/down case.

> sideband messaging, split the msm_dp_panel_read_sink_caps() into
> two parts which read the link parameters and the edid parts
> respectively. Also drop the panel drm_edid cached as we actually
> don't need it.

Also => separate change. 

> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> ---
>  drivers/gpu/drm/msm/dp/dp_display.c | 13 +++++----
>  drivers/gpu/drm/msm/dp/dp_panel.c   | 55 ++++++++++++++++++++-----------------
>  drivers/gpu/drm/msm/dp/dp_panel.h   |  6 ++--
>  3 files changed, 40 insertions(+), 34 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 6f05a939ce9e648e9601597155999b6f85adfcff..4a9b65647cdef1ed6c3bb851f93df0db8be977af 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -389,7 +389,11 @@ 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);
> +	if (rc)
> +		goto end;
> +
> +	rc = msm_dp_panel_read_edid(dp->panel, connector);
>  	if (rc)
>  		goto end;
>  
> @@ -720,7 +724,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);
>  }
>  
> @@ -783,7 +786,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->catalog);
> @@ -791,13 +794,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:
> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
> index 4e8ab75c771b1e3a2d62f75e9993e1062118482b..d9041e235104a74b3cc50ff2e307eae0c4301ef3 100644
> --- a/drivers/gpu/drm/msm/dp/dp_panel.c
> +++ b/drivers/gpu/drm/msm/dp/dp_panel.c
> @@ -118,14 +118,13 @@ static u32 msm_dp_panel_get_supported_bpp(struct msm_dp_panel *msm_dp_panel,
>  	return min_supported_bpp;
>  }
>  
> -int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
> -	struct drm_connector *connector)
> +int msm_dp_panel_read_link_caps(struct msm_dp_panel *msm_dp_panel)
>  {
>  	int rc, bw_code;
>  	int count;
>  	struct msm_dp_panel_private *panel;
>  
> -	if (!msm_dp_panel || !connector) {
> +	if (!msm_dp_panel) {
>  		DRM_ERROR("invalid input\n");
>  		return -EINVAL;
>  	}
> @@ -160,26 +159,29 @@ 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;
> +	return rc;
> +}
>  
> -	drm_edid_free(msm_dp_panel->drm_edid);
> +int msm_dp_panel_read_edid(struct msm_dp_panel *msm_dp_panel, struct drm_connector *connector)
> +{
> +	struct msm_dp_panel_private *panel;
> +	const struct drm_edid *drm_edid;
> +
> +	panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
>  
> -	msm_dp_panel->drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
> +	drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
>  
> -	drm_edid_connector_update(connector, msm_dp_panel->drm_edid);
> +	drm_edid_connector_update(connector, drm_edid);
>  
> -	if (!msm_dp_panel->drm_edid) {
> +	if (!drm_edid) {
>  		DRM_ERROR("panel edid read failed\n");
>  		/* check edid read fail is due to unplug */
>  		if (!msm_dp_catalog_link_is_connected(panel->catalog)) {
> -			rc = -ETIMEDOUT;
> -			goto end;
> +			return -ETIMEDOUT;
>  		}
>  	}
>  
> -end:
> -	return rc;
> +	return 0;
>  }
>  
>  u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel,
> @@ -208,15 +210,20 @@ u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel,
>  int msm_dp_panel_get_modes(struct msm_dp_panel *msm_dp_panel,
>  	struct drm_connector *connector)
>  {
> +	struct msm_dp_panel_private *panel;
> +	const struct drm_edid *drm_edid;
> +
>  	if (!msm_dp_panel) {
>  		DRM_ERROR("invalid input\n");
>  		return -EINVAL;
>  	}
>  
> -	if (msm_dp_panel->drm_edid)
> -		return drm_edid_connector_add_modes(connector);
> +	panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
> +
> +	drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
> +	drm_edid_connector_update(connector, drm_edid);

If EDID has been read and processed after HPD high event, why do we need
to re-read it again? Are we expecting that EDID will change?

>  
> -	return 0;
> +	return drm_edid_connector_add_modes(connector);
>  }
>  
>  static u8 msm_dp_panel_get_edid_checksum(const struct edid *edid)
> @@ -229,6 +236,7 @@ static u8 msm_dp_panel_get_edid_checksum(const struct edid *edid)
>  void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel)
>  {
>  	struct msm_dp_panel_private *panel;
> +	const struct drm_edid *drm_edid;
>  
>  	if (!msm_dp_panel) {
>  		DRM_ERROR("invalid input\n");
> @@ -238,8 +246,13 @@ void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel)
>  	panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
>  
>  	if (panel->link->sink_request & DP_TEST_LINK_EDID_READ) {
> +		drm_edid = drm_edid_read_ddc(msm_dp_panel->connector, &panel->aux->ddc);

And again....

> +
> +		if (!drm_edid)
> +			return;
> +
>  		/* 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)
> @@ -515,11 +528,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);
> -}

Too many changes to be stuffed under the hood of "Also perform foo"

> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
> index 4906f4f09f2451cfed3c1007f38b4db7dfdb1d90..7f139478e1012d5b8f1f745f0de5fc3943745428 100644
> --- a/drivers/gpu/drm/msm/dp/dp_panel.h
> +++ b/drivers/gpu/drm/msm/dp/dp_panel.h
> @@ -32,7 +32,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;
> @@ -51,7 +50,9 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel);
>  int msm_dp_panel_read_sink_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);
> +			      u32 mode_pclk_khz);
> +int msm_dp_panel_read_link_caps(struct msm_dp_panel *dp_panel);
> +int msm_dp_panel_read_edid(struct msm_dp_panel *dp_panel, struct drm_connector *connector);
>  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);
> @@ -86,5 +87,4 @@ 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, struct msm_dp_catalog *catalog);
> -void msm_dp_panel_put(struct msm_dp_panel *msm_dp_panel);
>  #endif /* _DP_PANEL_H_ */
> 
> -- 
> 2.34.1
> 

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead
  2025-06-09 12:21 ` [PATCH v2 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead Yongxing Mou
@ 2025-06-09 12:48   ` Dmitry Baryshkov
  2025-06-25 12:34     ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 12:48 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:21PM +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 populated the panel's dp_display_mode
> instead.

- Why do we need to cache / copy it anyway? Can't we just pass the
  corresponding drm_atomic_state / drm_crtc_state / drm_display_mode ?

> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> ---
>  drivers/gpu/drm/msm/dp/dp_display.c | 76 ++++++++++++++-----------------------
>  1 file changed, 29 insertions(+), 47 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 4a9b65647cdef1ed6c3bb851f93df0db8be977af..9d2db9cbd2552470a36a63f70f517c35436f7280 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -92,7 +92,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 */
> @@ -806,16 +805,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;
> +
> +	msm_dp_panel->msm_dp_mode.v_active_low =
> +		!!(adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC);
> +	msm_dp_panel->msm_dp_mode.h_active_low =
> +		!!(adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC);
> +	msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 =
> +		drm_mode_is_420_only(&msm_dp_panel->connector->display_info, adjusted_mode) &&
> +		msm_dp_panel->vsc_sdp_supported;
>  
> -	drm_mode_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_init_panel_info(msm_dp_panel);
>  	return 0;
>  }
>  
> @@ -1431,10 +1443,13 @@ bool msm_dp_needs_periph_flush(const struct msm_dp *msm_dp_display,
>  bool msm_dp_wide_bus_available(const 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);
>  
> -	if (dp->msm_dp_mode.out_fmt_is_yuv_420)
> +	dp_panel = dp->panel;
> +
> +	if (dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
>  		return false;
>  
>  	return dp->wide_bus_supported;
> @@ -1496,10 +1511,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);
> @@ -1517,15 +1528,6 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
>  		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);
> -		mutex_unlock(&msm_dp_display->event_mutex);
> -		return;
> -	}

It should be done other way around: keep this call and drop
msm_dp_bridge_mode_set().

> -
> -	hpd_state =  msm_dp_display->hpd_state;
> -
>  	if (hpd_state == ST_CONNECTED && !dp->power_on) {
>  		msm_dp_display_host_phy_init(msm_dp_display);
>  		force_link_train = true;
> @@ -1604,33 +1606,13 @@ 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->catalog->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;
> +	msm_dp_display->catalog->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] 108+ messages in thread

* Re: [PATCH v2 03/38] drm/msm/dp: break up dp_display_enable into two parts
  2025-06-09 12:21 ` [PATCH v2 03/38] drm/msm/dp: break up dp_display_enable into two parts Yongxing Mou
@ 2025-06-09 12:59   ` Dmitry Baryshkov
  2025-08-06  9:24     ` Yongxing Mou
  2025-08-13  9:36     ` Yongxing Mou
  0 siblings, 2 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 12:59 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:22PM +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. Splite these two parts into
> prepare/enable APIs, to support MST bridges_enable inserte

typos

> the MST payloads funcs between enable stream_clks and programe
> register.
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> ---
>  drivers/gpu/drm/msm/dp/dp_ctrl.c    | 57 +++++++++++++--------
>  drivers/gpu/drm/msm/dp/dp_ctrl.h    |  3 +-
>  drivers/gpu/drm/msm/dp/dp_display.c | 99 +++++++++++++++++++++++++++----------
>  drivers/gpu/drm/msm/dp/dp_display.h |  1 +
>  4 files changed, 111 insertions(+), 49 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index a50bfafbb4ea85c114c958ea0ed24362a1f23136..1e13ca81b0155a37a4ed7a2e83c918293d703a37 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> @@ -1980,40 +1980,61 @@ static int msm_dp_ctrl_link_retrain(struct msm_dp_ctrl_private *ctrl)
>  	return msm_dp_ctrl_setup_main_link(ctrl, &training_step);
>  }
>  
> -int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train)
> +int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train)
>  {
>  	int ret = 0;
> -	bool mainlink_ready = false;
>  	struct msm_dp_ctrl_private *ctrl;
> -	unsigned long pixel_rate;
> -	unsigned long pixel_rate_orig;
>  
>  	if (!msm_dp_ctrl)
>  		return -EINVAL;
>  
>  	ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
>  
> -	pixel_rate = pixel_rate_orig = ctrl->panel->msm_dp_mode.drm_mode.clock;
> -
> -	if (msm_dp_ctrl->wide_bus_en || ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420)
> -		pixel_rate >>= 1;
> -
> -	drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d, pixel_rate=%lu\n",
> -		ctrl->link->link_params.rate,
> -		ctrl->link->link_params.num_lanes, pixel_rate);
> +	drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d\n",
> +		   ctrl->link->link_params.rate,
> +		   ctrl->link->link_params.num_lanes);

Please don't mix whitespace changes with the actual code changes. It
makes reviewing the patch much harder.

>  
>  	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);
> +		   "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);
>  
>  	if (!ctrl->link_clks_on) { /* link clk is off */
>  		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);
> @@ -2031,12 +2052,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 b7abfedbf5749c25877a0b8ba3af3d8ed4b23d67..42745c912adbad7221c78f5cecefa730bfda1e75 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
> @@ -18,7 +18,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 *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_link(struct msm_dp_ctrl *msm_dp_ctrl);
>  void msm_dp_ctrl_off(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 9d2db9cbd2552470a36a63f70f517c35436f7280..5ac5dcf35b789f2bda052a2c17aae20aa48d8e18 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -831,7 +831,37 @@ 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);
> +	if (msm_dp_display->prepared) {
> +		drm_dbg_dp(dp->drm_dev, "Link already setup, return\n");
> +		return 0;
> +	}

How can it be prepared here? It is called at the beginning of the
.atomic_enable() only, so there is no way this can be true.

> +
> +	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->hpd_state == ST_CONNECTED && !msm_dp_display->power_on) {
> +		msm_dp_display_host_phy_init(dp);
> +		force_link_train = true;
> +	}
> +
> +	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;
> @@ -842,7 +872,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;
>  
> @@ -872,13 +902,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 */
> @@ -889,6 +916,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) {
>  		/*
> @@ -1506,9 +1541,8 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
>  	struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge);
>  	struct msm_dp *dp = msm_dp_bridge->msm_dp_display;
>  	int rc = 0;
> +
>  	struct msm_dp_display_private *msm_dp_display;
> -	u32 hpd_state;
> -	bool force_link_train = false;
>  
>  	msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
>  
> @@ -1516,29 +1550,23 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
>  		msm_dp_hpd_plug_handle(msm_dp_display, 0);
>  
>  	mutex_lock(&msm_dp_display->event_mutex);
> -	if (pm_runtime_resume_and_get(&dp->pdev->dev)) {
> -		DRM_ERROR("failed to pm_runtime_resume\n");
> -		mutex_unlock(&msm_dp_display->event_mutex);
> -		return;
> -	}
>  
> -	hpd_state = msm_dp_display->hpd_state;
> -	if (hpd_state == ST_DISCONNECT_PENDING) {
> +	rc = msm_dp_display_prepare(msm_dp_display);
> +	if (rc) {
> +		DRM_ERROR("DP display prepare failed, rc=%d\n", rc);
>  		mutex_unlock(&msm_dp_display->event_mutex);
>  		return;
>  	}
>  
> -	if (hpd_state == ST_CONNECTED && !dp->power_on) {
> -		msm_dp_display_host_phy_init(msm_dp_display);
> -		force_link_train = true;
> -	}
> -
> -	msm_dp_display_enable(msm_dp_display, force_link_train);
> -
> -	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);
> +	if (dp->prepared) {
> +		rc = msm_dp_display_enable(msm_dp_display);
> +		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);
> +		}
>  	}
>  
>  	/* completed connection */
> @@ -1560,6 +1588,20 @@ 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;
> +
> +	if (!msm_dp_display->prepared) {
> +		drm_dbg_dp(dp->drm_dev, "Link already setup, return\n");
> +		return;
> +	}

Why/ how is it possible?

> +
> +	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)
>  {
> @@ -1580,6 +1622,8 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
>  		drm_dbg_dp(dp->drm_dev, "type=%d wrong hpd_state=%d\n",
>  			   dp->connector_type, hpd_state);
>  
> +	msm_dp_display_audio_notify_disable(msm_dp_display);
> +
>  	msm_dp_display_disable(msm_dp_display);
>  
>  	hpd_state =  msm_dp_display->hpd_state;
> @@ -1588,9 +1632,10 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
>  		msm_dp_display->hpd_state = ST_DISCONNECTED;
>  	}
>  
> +	msm_dp_display_unprepare(msm_dp_display);
> +
>  	drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type);
>  
> -	pm_runtime_put_sync(&dp->pdev->dev);
>  	mutex_unlock(&msm_dp_display->event_mutex);
>  }
>  
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
> index cc6e2cab36e9c0b1527ff292e547cbb4d69fd95c..2394840e9f28e136705004c3e6af93fbe13c33c5 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.h
> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
> @@ -19,6 +19,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
> 

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 04/38] drm/msm/dp: re-arrange dp_display_disable() into functional parts
  2025-06-09 12:21 ` [PATCH v2 04/38] drm/msm/dp: re-arrange dp_display_disable() into functional parts Yongxing Mou
@ 2025-06-09 13:05   ` Dmitry Baryshkov
  2025-08-06  9:30     ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 13:05 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:23PM +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.
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> ---
>  drivers/gpu/drm/msm/dp/dp_ctrl.c    | 29 +++++++++++++++--------------
>  drivers/gpu/drm/msm/dp/dp_ctrl.h    |  5 ++++-
>  drivers/gpu/drm/msm/dp/dp_display.c | 16 +++++++---------
>  3 files changed, 26 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index 1e13ca81b0155a37a4ed7a2e83c918293d703a37..1ce3cca121d0c56b493e282c76eb9202371564cf 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> @@ -2081,30 +2081,31 @@ 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_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl)
>  {
>  	struct msm_dp_ctrl_private *ctrl;
> -	struct phy *phy;
>  
>  	ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
> -	phy = ctrl->phy;
> -
>  	msm_dp_catalog_panel_disable_vsc_sdp(ctrl->catalog);
> +}
>  
> -	/* set dongle to D3 (power off) mode */
> -	msm_dp_link_psm_config(ctrl->link, &ctrl->panel->link_info, true);
> +void msm_dp_ctrl_psm_config(struct msm_dp_ctrl *msm_dp_ctrl)

I'm not a fan of (almost) one-line wrappers.

> +{
> +	struct msm_dp_ctrl_private *ctrl;
>  
> -	msm_dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);
> +	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;
> -	}
> +	/* set dongle to D3 (power off) mode */
> +	msm_dp_link_psm_config(ctrl->link, &ctrl->panel->link_info, true);
> +}
>  
> -	dev_pm_opp_set_rate(ctrl->dev, 0);
> -	msm_dp_ctrl_link_clk_disable(&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;
>  
> -	phy_power_off(phy);
> +	ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
> +	phy = ctrl->phy;
>  
>  	/* aux channel down, reinit phy */
>  	phy_exit(phy);
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
> index 42745c912adbad7221c78f5cecefa730bfda1e75..edbe5766db74c4e4179141d895f9cb85e514f29b 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
> @@ -20,7 +20,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 *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_link(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);
> @@ -42,4 +41,8 @@ void msm_dp_ctrl_config_psr(struct msm_dp_ctrl *msm_dp_ctrl);
>  int msm_dp_ctrl_core_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl);
>  void msm_dp_ctrl_core_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl);
>  
> +void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl);
> +void msm_dp_ctrl_psm_config(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 5ac5dcf35b789f2bda052a2c17aae20aa48d8e18..a5ca498cb970d0c6a4095b0b7fc6269c2dc3ad31 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -925,17 +925,15 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
>  	if (!msm_dp_display->power_on)
>  		return 0;
>  
> +	msm_dp_ctrl_clear_vsc_sdp_pkt(dp->ctrl);
> +
> +	/* 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);
> +		msm_dp_ctrl_psm_config(dp->ctrl);
> +		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
> -		 * dongle unplugged out of DUT
> -		 */
>  		msm_dp_ctrl_off(dp->ctrl);
>  		msm_dp_display_host_phy_exit(dp);
>  	}
> 
> -- 
> 2.34.1
> 

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 05/38] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it
  2025-06-09 12:21 ` [PATCH v2 05/38] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it Yongxing Mou
@ 2025-06-09 13:12   ` Dmitry Baryshkov
  2025-08-13  9:52     ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 13:12 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:24PM +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.
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> ---
>  drivers/gpu/drm/msm/dp/dp_ctrl.c    | 37 ++++++++++++++++++++-----------------
>  drivers/gpu/drm/msm/dp/dp_ctrl.h    |  5 +++--
>  drivers/gpu/drm/msm/dp/dp_display.c |  4 ++--
>  3 files changed, 25 insertions(+), 21 deletions(-)

I think previous review comments got ignored. Please step back and
review them. Maybe I should ask you to go back to v1 and actually check
all the review comments there?

> 
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index 1ce3cca121d0c56b493e282c76eb9202371564cf..aee8e37655812439dfb65ae90ccb61b14e6e261f 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> @@ -135,7 +135,8 @@ 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(struct msm_dp_ctrl_private *ctrl,
> +				    struct msm_dp_panel *msm_dp_panel)
>  {
>  	u32 config = 0, tbd;
>  	const u8 *dpcd = ctrl->panel->dpcd;
> @@ -143,7 +144,7 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl)
>  	/* 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)
> +	if (msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
>  		config |= DP_CONFIGURATION_CTRL_RGB_YUV; /* YUV420 */
>  
>  	/* Scrambler reset enable */
> @@ -151,7 +152,7 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl)
>  		config |= DP_CONFIGURATION_CTRL_ASSR;
>  
>  	tbd = msm_dp_link_get_test_bits_depth(ctrl->link,
> -			ctrl->panel->msm_dp_mode.bpp);
> +			msm_dp_panel->msm_dp_mode.bpp);
>  
>  	config |= tbd << DP_CONFIGURATION_CTRL_BPC_SHIFT;
>  
> @@ -174,20 +175,21 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl)
>  	msm_dp_catalog_ctrl_config_ctrl(ctrl->catalog, config);
>  }
>  
> -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)
>  {
>  	u32 cc, tb;
>  
>  	msm_dp_catalog_ctrl_lane_mapping(ctrl->catalog);
>  	msm_dp_catalog_setup_peripheral_flush(ctrl->catalog);
>  
> -	msm_dp_ctrl_config_ctrl(ctrl);
> +	msm_dp_ctrl_config_ctrl(ctrl, msm_dp_panel);
>  
>  	tb = msm_dp_link_get_test_bits_depth(ctrl->link,
> -		ctrl->panel->msm_dp_mode.bpp);
> +		msm_dp_panel->msm_dp_mode.bpp);
>  	cc = msm_dp_link_get_colorimetry_config(ctrl->link);
>  	msm_dp_catalog_ctrl_config_misc(ctrl->catalog, cc, tb);
> -	msm_dp_panel_timing_cfg(ctrl->panel);
> +	msm_dp_panel_timing_cfg(msm_dp_panel);
>  }
>  
>  /*
> @@ -1317,7 +1319,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(ctrl, ctrl->panel);

Could you please explain, when is it fine to use ctrl->panel and when it
is not? Here you are passing msm_dp_panel to configure DP link for link
training. I don't think we need the panel for that, so just using
ctrl->panel here is incorrect too.

>  
>  	link_info.num_lanes = ctrl->link->link_params.num_lanes;
>  	link_info.rate = ctrl->link->link_params.rate;
> @@ -1735,7 +1737,8 @@ static bool msm_dp_ctrl_send_phy_test_pattern(struct msm_dp_ctrl_private *ctrl)
>  	return success;
>  }
>  
> -static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl)
> +static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl,
> +						struct msm_dp_panel *msm_dp_panel)
>  {
>  	int ret;
>  	unsigned long pixel_rate;
> @@ -1759,7 +1762,7 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
>  		return ret;
>  	}
>  
> -	pixel_rate = ctrl->panel->msm_dp_mode.drm_mode.clock;
> +	pixel_rate = msm_dp_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);
> @@ -1797,7 +1800,7 @@ void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl)
>  
>  	if (sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
>  		drm_dbg_dp(ctrl->drm_dev, "PHY_TEST_PATTERN request\n");
> -		if (msm_dp_ctrl_process_phy_test_request(ctrl)) {
> +		if (msm_dp_ctrl_process_phy_test_request(ctrl, ctrl->panel)) {
>  			DRM_ERROR("process phy_test_req failed\n");
>  			return;
>  		}
> @@ -2015,7 +2018,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;
> @@ -2028,9 +2031,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);
> @@ -2058,12 +2061,12 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl)
>  	 */
>  	reinit_completion(&ctrl->video_comp);
>  
> -	msm_dp_ctrl_configure_source_params(ctrl);
> +	msm_dp_ctrl_configure_source_params(ctrl, msm_dp_panel);
>  
>  	msm_dp_catalog_ctrl_config_msa(ctrl->catalog,
>  		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_ctrl_setup_tr_unit(ctrl);
>  
> @@ -2081,7 +2084,7 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl)
>  	return ret;
>  }
>  
> -void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl)
> +void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *dp_panel)
>  {
>  	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 edbe5766db74c4e4179141d895f9cb85e514f29b..fbe458c5a17bda0586097a61d925f608d99f9224 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
> @@ -18,7 +18,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 *dp_ctrl, bool force_link_train);
>  void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl);
>  void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl);
> @@ -41,7 +41,8 @@ void msm_dp_ctrl_config_psr(struct msm_dp_ctrl *msm_dp_ctrl);
>  int msm_dp_ctrl_core_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl);
>  void msm_dp_ctrl_core_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl);
>  
> -void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl);
> +void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl,
> +				   struct msm_dp_panel *msm_dp_panel);
>  void msm_dp_ctrl_psm_config(struct msm_dp_ctrl *msm_dp_ctrl);
>  void msm_dp_ctrl_reinit_phy(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 a5ca498cb970d0c6a4095b0b7fc6269c2dc3ad31..17ccea4047500848c4fb3eda87a10e29b18e0cfb 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -872,7 +872,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;
>  
> @@ -925,7 +925,7 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
>  	if (!msm_dp_display->power_on)
>  		return 0;
>  
> -	msm_dp_ctrl_clear_vsc_sdp_pkt(dp->ctrl);
> +	msm_dp_ctrl_clear_vsc_sdp_pkt(dp->ctrl, dp->panel);
>  
>  	/* dongle is still connected but sinks are disconnected */
>  	if (dp->link->sink_count == 0) {
> 
> -- 
> 2.34.1
> 

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 06/38] drm/msm/dp: move the pixel clock control to its own API
  2025-06-09 12:21 ` [PATCH v2 06/38] drm/msm/dp: move the pixel clock control to its own API Yongxing Mou
@ 2025-06-09 13:16   ` Dmitry Baryshkov
  2025-08-13 11:56     ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 13:16 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:25PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
> 
> 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 <quic_yongmou@quicinc.com>
> ---
>  drivers/gpu/drm/msm/dp/dp_ctrl.c | 98 +++++++++++++++++++++-------------------
>  1 file changed, 52 insertions(+), 46 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index aee8e37655812439dfb65ae90ccb61b14e6e261f..ed00dd2538d98ddbc6bdcbd5fa154fd7043c48d6 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> @@ -97,7 +97,7 @@ struct msm_dp_ctrl_private {
>  
>  	bool core_clks_on;
>  	bool link_clks_on;
> -	bool stream_clks_on;
> +	bool pixel_clks_on;

As you are touching this part, how many paths lead to pixel clock being
enabled and/or disabled? Can we sort them out and drop this flag, making
sure that the clock can be enabled only in one place and disabled in
another one (hopefully)?

>  };
>  
>  static int msm_dp_aux_link_configure(struct drm_dp_aux *aux,
> @@ -1406,8 +1406,8 @@ int msm_dp_ctrl_core_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl)
>  	ctrl->core_clks_on = true;
>  
>  	drm_dbg_dp(ctrl->drm_dev, "enable core clocks \n");
> -	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
> -		   str_on_off(ctrl->stream_clks_on),
> +	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
> +		   str_on_off(ctrl->pixel_clks_on),
>  		   str_on_off(ctrl->link_clks_on),
>  		   str_on_off(ctrl->core_clks_on));
>  
> @@ -1425,8 +1425,8 @@ void msm_dp_ctrl_core_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl)
>  	ctrl->core_clks_on = false;
>  
>  	drm_dbg_dp(ctrl->drm_dev, "disable core clocks \n");
> -	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
> -		   str_on_off(ctrl->stream_clks_on),
> +	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
> +		   str_on_off(ctrl->pixel_clks_on),
>  		   str_on_off(ctrl->link_clks_on),
>  		   str_on_off(ctrl->core_clks_on));
>  }
> @@ -1456,8 +1456,8 @@ static int msm_dp_ctrl_link_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl)
>  	ctrl->link_clks_on = true;
>  
>  	drm_dbg_dp(ctrl->drm_dev, "enable link clocks\n");
> -	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
> -		   str_on_off(ctrl->stream_clks_on),
> +	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
> +		   str_on_off(ctrl->pixel_clks_on),
>  		   str_on_off(ctrl->link_clks_on),
>  		   str_on_off(ctrl->core_clks_on));
>  
> @@ -1475,8 +1475,8 @@ static void msm_dp_ctrl_link_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl)
>  	ctrl->link_clks_on = false;
>  
>  	drm_dbg_dp(ctrl->drm_dev, "disabled link clocks\n");
> -	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
> -		   str_on_off(ctrl->stream_clks_on),
> +	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
> +		   str_on_off(ctrl->pixel_clks_on),
>  		   str_on_off(ctrl->link_clks_on),
>  		   str_on_off(ctrl->core_clks_on));
>  }
> @@ -1737,6 +1737,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->pixel_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->pixel_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->pixel_clks_on) {
> +		clk_disable_unprepare(ctrl->pixel_clk);
> +		ctrl->pixel_clks_on = false;
> +	}
> +}
> +
>  static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl,
>  						struct msm_dp_panel *msm_dp_panel)
>  {
> @@ -1763,22 +1799,7 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
>  	}
>  
>  	pixel_rate = msm_dp_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);
>  
> @@ -1998,8 +2019,8 @@ int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_li
>  		   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);
> +		   "core_clk_on=%d link_clk_on=%d pixel_clks_on=%d\n",
> +		   ctrl->core_clks_on, ctrl->link_clks_on, ctrl->pixel_clks_on);
>  
>  	if (!ctrl->link_clks_on) { /* link clk is off */
>  		ret = msm_dp_ctrl_enable_mainlink_clocks(ctrl);
> @@ -2038,21 +2059,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;
>  	}
>  
>  	/*
> @@ -2080,7 +2090,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;
>  }
>  
> @@ -2154,10 +2163,7 @@ void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl)
>  
>  	msm_dp_catalog_ctrl_reset(ctrl->catalog);
>  
> -	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
> 

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 33/38] drm/msm: initialize DRM MST encoders for DP controllers
  2025-06-09 12:21 ` [PATCH v2 33/38] drm/msm: initialize DRM MST encoders for DP controllers Yongxing Mou
@ 2025-06-09 14:17   ` Dmitry Baryshkov
  2025-08-14  9:11     ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 14:17 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:52PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
> 
> Initiliaze 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 <quic_yongmou@quicinc.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_display.c         | 14 ++++++++++++++

Please don't mix DP and DPU changes in a single patch.

>  drivers/gpu/drm/msm/msm_drv.h               | 13 +++++++++++++
>  4 files changed, 51 insertions(+), 1 deletion(-)
> 
> 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 1fd82b6747e9058ce11dc2620729921492d5ebdd..45fedf7e74e9c6dfed4bde57eb675e3dd1762fc7 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> @@ -652,7 +652,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->dp); i++) {
>  		if (!priv->dp[i])
> @@ -675,6 +676,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->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_bridge_init(priv->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_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 9dbcf4553cad70c9e3722160a87403fc815765d7..ab1ad0cb6427eb4f86ee8ac6c76788b1a78892a8 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -1417,6 +1417,20 @@ static int msm_dp_display_get_connector_type(struct platform_device *pdev,
>  	return connector_type;
>  }
>  
> +int msm_dp_get_mst_max_stream(struct msm_dp *dp_display)
> +{
> +	struct msm_dp_display_private *dp_priv;
> +
> +	dp_priv = container_of(dp_display, struct msm_dp_display_private, msm_dp_display);
> +
> +	return dp_priv->max_stream;
> +}
> +
> +int msm_dp_mst_bridge_init(struct msm_dp *dp_display, struct drm_encoder *encoder)
> +{
> +	return msm_dp_mst_drm_bridge_init(dp_display, encoder);

What's the point in this oneliner?

> +}
> +
>  static int msm_dp_display_probe(struct platform_device *pdev)
>  {
>  	int rc = 0;
> diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
> index a65077855201746c37ee742364b61116565f3794..dd403107b640ee5ef333d2773b52e38e3869155f 100644
> --- a/drivers/gpu/drm/msm/msm_drv.h
> +++ b/drivers/gpu/drm/msm/msm_drv.h
> @@ -372,6 +372,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_bridge_init(struct msm_dp *dp_display, struct drm_encoder *encoder);
> +
>  #else
>  static inline int __init msm_dp_register(void)
>  {
> @@ -388,6 +391,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_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] 108+ messages in thread

* Re: [PATCH v2 34/38] drm/msm/dp: initialize dp_mst module for each DP MST controller
  2025-06-09 12:21 ` [PATCH v2 34/38] drm/msm/dp: initialize dp_mst module for each DP MST controller Yongxing Mou
@ 2025-06-09 14:27   ` Dmitry Baryshkov
  0 siblings, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 14:27 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:53PM +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.

I think there is an ordering issue. Previos patch has already registered
MST-related objects, but only this patch provides a way to init it.

> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.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 45fedf7e74e9c6dfed4bde57eb675e3dd1762fc7..e030476dc4c69448886c29bcfe8ff3105949b129 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> @@ -680,6 +680,12 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev,
>  		stream_cnt = msm_dp_get_mst_max_stream(priv->dp[i]);
>  
>  		if (stream_cnt > 1) {
> +			rc = msm_dp_mst_register(priv->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 ab1ad0cb6427eb4f86ee8ac6c76788b1a78892a8..526389c718edccbac9b5a91e8dabf0d84ed1a8b0 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -1667,6 +1667,15 @@ int msm_dp_modeset_init(struct msm_dp *msm_dp_display, struct drm_device *dev,
>  	return 0;
>  }
>  
> +int msm_dp_mst_register(struct msm_dp *dp)
> +{
> +	struct msm_dp_display_private *dp_display;
> +
> +	dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
> +
> +	return msm_dp_mst_init(dp, dp_display->max_stream, dp_display->aux);

Inline

> +}
> +
>  void msm_dp_display_atomic_prepare(struct msm_dp *dp)
>  {
>  	int rc = 0;
> diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
> index dd403107b640ee5ef333d2773b52e38e3869155f..1496700c38ad73d6edcf56fbb0ebf66505c608bf 100644
> --- a/drivers/gpu/drm/msm/msm_drv.h
> +++ b/drivers/gpu/drm/msm/msm_drv.h
> @@ -374,6 +374,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_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)
> @@ -401,6 +402,11 @@ static inline int msm_dp_mst_bridge_init(struct msm_dp *dp_display, struct drm_e
>  	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] 108+ messages in thread

* Re: [PATCH v2 35/38] drm/msm/dpu: use msm_dp_get_mst_intf_id() to get the intf id
  2025-06-09 12:21 ` [PATCH v2 35/38] drm/msm/dpu: use msm_dp_get_mst_intf_id() to get the intf id Yongxing Mou
@ 2025-06-09 14:44   ` Dmitry Baryshkov
  0 siblings, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 14:44 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:54PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
> 
> Use msm_dp_get_mst_intf_id() to get the intf 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 <quic_yongmou@quicinc.com>
> ---
>  drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 21 ++++++++++----
>  drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c     | 43 +++++++++++++++++++++++++++++
>  drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h     |  2 +-
>  3 files changed, 60 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index 7020098360e474ee149824a488d912a7ad8ed06a..75cc2d475440fcdc941aa9eb19e78a87e83b5f5f 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -2611,11 +2611,13 @@ uint32_t dpu_encoder_get_clones(struct drm_encoder *drm_enc)
>  
>  static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
>  				 struct dpu_kms *dpu_kms,
> -				 struct msm_display_info *disp_info)
> +				 struct msm_display_info *disp_info,
> +				 int drm_enc_mode)

Rather than passing additional argument with very special case, I'd
prefer if you expanded dpu_intf_type enum.

>  {
>  	int ret = 0;
>  	int i = 0;
>  	struct dpu_enc_phys_init_params phys_params;
> +	u32 intf_id;
>  
>  	if (!dpu_enc) {
>  		DPU_ERROR("invalid arg(s), enc %d\n", dpu_enc != NULL);
> @@ -2658,9 +2660,18 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
>  		DPU_DEBUG("h_tile_instance %d = %d, split_role %d\n",
>  				i, controller_id, phys_params.split_role);
>  
> -		phys_params.hw_intf = dpu_encoder_get_intf(dpu_kms->catalog, &dpu_kms->rm,
> -							   disp_info->intf_type,
> -							   controller_id);
> +		if (drm_enc_mode == DRM_MODE_ENCODER_DPMST) {
> +			intf_id = dpu_kms_get_mst_intf_id(dpu_kms, controller_id,
> +							     disp_info->stream_id);
> +			DPU_DEBUG("intf_id %d for disp_info->stream_id = %d\n", intf_id,
> +				  disp_info->stream_id);

If stream_id was a part of the h_tile_instance[] (e.g. by using high
bits of the ID), then there is no need to patch this function at all:
pass DP_CONTROLLER_n | DP_MST_STREAM(m) through h_tile_instance, then
parse it in dpu_encoder_get_intf().

> +			phys_params.hw_intf = dpu_rm_get_intf(&dpu_kms->rm, intf_id);
> +
> +		} else {
> +			phys_params.hw_intf = dpu_encoder_get_intf(dpu_kms->catalog, &dpu_kms->rm,
> +								   disp_info->intf_type,
> +								   controller_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);
> @@ -2760,7 +2771,7 @@ struct drm_encoder *dpu_encoder_init(struct drm_device *dev,
>  	mutex_init(&dpu_enc->enc_lock);
>  	mutex_init(&dpu_enc->rc_lock);
>  
> -	ret = dpu_encoder_setup_display(dpu_enc, dpu_kms, disp_info);
> +	ret = dpu_encoder_setup_display(dpu_enc, dpu_kms, disp_info, drm_enc_mode);
>  	if (ret) {
>  		DPU_ERROR("failed to setup encoder\n");
>  		return ERR_PTR(-ENOMEM);
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> index e030476dc4c69448886c29bcfe8ff3105949b129..f82dcf7c6dd31f078bbe4afe55d4667a4867f0b7 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> @@ -574,6 +574,49 @@ static int dpu_kms_dsi_set_te_source(struct msm_display_info *info,
>  	return -EINVAL;
>  }
>  
> +u32 dpu_kms_get_mst_intf_id(struct dpu_kms *dpu_kms, int controller_id, int stream_id)
> +{

This goes logically next to dpu_encoder_get_intf().

> +	const struct dpu_mdss_cfg *catalog = dpu_kms->catalog;
> +	int i;
> +	int cnt = 0;
> +	u32 intf_id = INTF_0;
> +
> +	/* The intf order in dpu_intf_cfg matches the mapping in the DP HPG.
> +	 * 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   |
> +	 * +-------------+----------+----------+
> +	 */
> +	for (i = 0; i < catalog->intf_count; i++) {
> +		const struct dpu_intf_cfg *intf = &catalog->intf[i];
> +
> +		if (intf->type == INTF_DP && controller_id == intf->controller_id) {
> +			if (cnt == stream_id) {
> +				intf_id = intf->id;
> +				break;
> +			}
> +
> +			cnt++;
> +		}
> +	}
> +
> +	return intf_id;
> +}
> +
>  static int _dpu_kms_initialize_dsi(struct drm_device *dev,
>  				    struct msm_drm_private *priv,
>  				    struct dpu_kms *dpu_kms)
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h
> index a57ec2ec106083e8f93578e4307e8b13ae549c08..388cd8f84fd579ce30a69989be5ac116bb727878 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h
> @@ -168,5 +168,5 @@ int dpu_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc);
>  void dpu_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc);
>  
>  unsigned long dpu_kms_get_clk_rate(struct dpu_kms *dpu_kms, char *clock_name);
> -
> +u32 dpu_kms_get_mst_intf_id(struct dpu_kms *dpu_kms, int controller_id, int stream_id);
>  #endif /* __dpu_kms_H__ */
> 
> -- 
> 2.34.1
> 

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 37/38] drm/msm/dp: fix the intf_type of MST interfaces
  2025-06-09 12:21 ` [PATCH v2 37/38] drm/msm/dp: fix the intf_type of MST interfaces Yongxing Mou
@ 2025-06-09 14:45   ` Dmitry Baryshkov
  0 siblings, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 14:45 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:56PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
> 
> Interface type of MST interfaces is currently INTF_NONE.
> Fix this to INTF_DP.
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> ---
>  drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)

Please fix through the whole catalog rather than just touching one
machine.

Also, wrong subject. This is not a DP patch. Please fix subjects for all
your patches.

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 38/38] drm/msm/dp: Add MST stream support for SA8775P DP controller 0 and 1
  2025-06-09 12:21 ` [PATCH v2 38/38] drm/msm/dp: Add MST stream support for SA8775P DP controller 0 and 1 Yongxing Mou
@ 2025-06-09 14:47   ` Dmitry Baryshkov
  0 siblings, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 14:47 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel

On Mon, Jun 09, 2025 at 08:21:57PM +0800, Yongxing Mou wrote:
> This change enables support for Multi-Stream Transport (MST), allowing
> each controller to handle up to two DisplayPort streams. As all
> necessary code for MST support was already implemented in the previous
> series of patches.
> 
> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.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 75f2fd7c75eae81e5c843f8ae2d1ce12ad0cad7e..16196dcc9ff4ac6a35b6bcd8d433b08f7d18fe5b 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -127,8 +127,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 = 2},

I thought few patches ago you wrote that this controller should support
4 MST streams.

> +	{ .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] 108+ messages in thread

* Re: [PATCH v2 32/38] drm/msm: add support for non-blocking commits
  2025-06-09 12:21 ` [PATCH v2 32/38] drm/msm: add support for non-blocking commits Yongxing Mou
@ 2025-06-09 14:50   ` Dmitry Baryshkov
  2025-08-14  8:54     ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 14:50 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:51PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
> 
> Hook up the mst framework APIs with atomic_commit_setup() and
> atomic_commit_tail() APIs to handle non-blocking commits.

Were non-blocking commits not supported before this patch?

> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> ---
>  drivers/gpu/drm/msm/msm_atomic.c | 3 +++
>  drivers/gpu/drm/msm/msm_kms.c    | 2 ++
>  2 files changed, 5 insertions(+)
> 

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 31/38] drm/msm/dp: propagate MST state changes to dp mst module
  2025-06-09 12:21 ` [PATCH v2 31/38] drm/msm/dp: propagate MST state changes to dp mst module Yongxing Mou
@ 2025-06-09 14:56   ` Dmitry Baryshkov
  2025-08-14  8:24     ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 14:56 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:50PM +0800, Yongxing Mou wrote:
> Introduce APIs to update the MST state change to MST framework when
> device is plugged/unplugged.
> 
> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>

Hmm, who is the author of the patch?

> ---
>  drivers/gpu/drm/msm/dp/dp_display.c | 20 ++++++++++++++++++++
>  drivers/gpu/drm/msm/dp/dp_mst_drm.c | 15 +++++++++++++++
>  drivers/gpu/drm/msm/dp/dp_mst_drm.h |  1 +
>  3 files changed, 36 insertions(+)
> 
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 323d1c0a9efa4fa30ce97317e873607c54409a11..9dbcf4553cad70c9e3722160a87403fc815765d7 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -29,6 +29,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);
> @@ -420,6 +421,17 @@ static void msm_dp_display_mst_init(struct msm_dp_display_private *dp)
>  	msm_dp->mst_active = true;
>  }
>  
> +static void msm_dp_display_set_mst_mgr_state(struct msm_dp_display_private *dp,
> +					     bool state)
> +{
> +	if (!dp->msm_dp_display.mst_active)
> +		return;
> +
> +	msm_dp_mst_display_set_mgr_state(&dp->msm_dp_display, state);
> +
> +	drm_dbg_dp(dp->drm_dev, "mst_mgr_state: %d\n", state);

Yet-another-oneliner? Noooo. Really, no.

> +}
> +
>  static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
>  {
>  	struct drm_connector *connector = dp->msm_dp_display.connector;

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 30/38] drm/msm/dp: add HPD callback for dp MST
  2025-06-09 12:21 ` [PATCH v2 30/38] drm/msm/dp: add HPD callback for dp MST Yongxing Mou
@ 2025-06-09 15:01   ` Dmitry Baryshkov
  0 siblings, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 15:01 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:49PM +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>

Missing SoB

> ---
>  drivers/gpu/drm/msm/dp/dp_display.c | 20 ++++++++++++++------
>  drivers/gpu/drm/msm/dp/dp_mst_drm.c | 34 ++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/msm/dp/dp_mst_drm.h |  2 ++

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST
  2025-06-09 12:21 ` [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST Yongxing Mou
@ 2025-06-09 15:44   ` Dmitry Baryshkov
  2025-06-11 12:06     ` Yongxing Mou
  2025-06-09 15:51   ` Dmitry Baryshkov
  1 sibling, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 15:44 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:48PM +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.
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> ---
>  drivers/gpu/drm/msm/dp/dp_mst_drm.c | 515 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/msm/dp/dp_mst_drm.h |   3 +
>  2 files changed, 518 insertions(+)

It generally feels liks 80% of this patch is a generic code. Please
extract generic DP MST connector and push it under drm/display. Other DP
MST drivers should be able to use it.

> 
> diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> index a3ea34ae63511db0ac920cbeebe30c4e2320b8c4..489fa46aa518ff1cc5f4769b2153fc5153c4cb41 100644
> --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> @@ -25,8 +25,12 @@
>   * OF THIS SOFTWARE.
>   */
>  
> +#include <drm/drm_edid.h>
> +#include <drm/drm_managed.h>
>  #include "dp_mst_drm.h"
>  
> +#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 *state;
> @@ -79,6 +83,61 @@ static int msm_dp_mst_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, int p
>  	return num_slots;
>  }
>  
> +static int msm_dp_mst_get_mst_pbn_div(struct msm_dp_panel *msm_dp_panel)
> +{
> +	struct msm_dp_link_info *link_info;
> +
> +	link_info = &msm_dp_panel->link_info;
> +
> +	return link_info->rate * link_info->num_lanes / 54000;
> +}
> +
> +static int msm_dp_mst_compute_config(struct drm_atomic_state *state,
> +				      struct msm_dp_mst *mst, struct drm_connector *connector,
> +				      struct drm_display_mode *mode)
> +{
> +	int slots = 0, pbn;
> +	struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(connector);
> +	int rc = 0;
> +	struct drm_dp_mst_topology_state *mst_state;
> +	int pbn_div;
> +	struct msm_dp *dp_display = mst->msm_dp;
> +	u32 bpp;
> +
> +	bpp = connector->display_info.bpc * 3;
> +
> +	pbn = drm_dp_calc_pbn_mode(mode->clock, bpp << 4);

Is this going to change if DSC is in place? Will it bring fractional BPP
here?

> +
> +	mst_state = to_drm_dp_mst_topology_state(mst->mst_mgr.base.state);
> +
> +	if (!dfixed_trunc(mst_state->pbn_div)) {
> +		pbn_div = msm_dp_mst_get_mst_pbn_div(mst_conn->dp_panel);
> +		mst_state->pbn_div.full = dfixed_const(pbn_div);

It looks like this should be using drm_dp_get_vc_payload_bw() (i915,
nouveau). Any reason not to do so?

> +	}
> +
> +	rc = drm_dp_atomic_find_time_slots(state, &mst->mst_mgr, mst_conn->mst_port, pbn);
> +	if (rc < 0) {
> +		DRM_ERROR("conn:%d failed to find vcpi slots. pbn:%d, rc:%d\n",
> +			  connector->base.id, pbn, rc);
> +		goto end;
> +	}
> +
> +	slots = rc;
> +
> +	rc = drm_dp_mst_atomic_check(state);
> +	if (rc) {
> +		DRM_ERROR("conn:%d mst atomic check failed: rc=%d\n", connector->base.id, rc);
> +		slots = 0;

no need to

> +		goto end;
> +	}
> +
> +	drm_dbg_dp(dp_display->drm_dev, "conn:%d pbn:%d slots:%d rc:%d\n",
> +		   connector->base.id, pbn, slots, rc);
> +
> +end:
> +	return (rc < 0 ? rc : slots);
> +}
> +
>  static void _msm_dp_mst_update_timeslots(struct msm_dp_mst *mst,
>  					 struct msm_dp_mst_bridge *mst_bridge,
>  					 struct drm_dp_mst_port *port)
> @@ -499,3 +558,459 @@ int msm_dp_mst_drm_bridge_init(struct msm_dp *dp, struct drm_encoder *encoder)
>  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;
> +
> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
> +
> +	return to_msm_dp_mst_bridge_priv_state(drm_atomic_get_private_obj_state(st,
> +										&bridge->obj));

Save drm_atomic_get_private_obj_state() result to a variable. It will
make it readable.

> +}
> +
> +/* 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;
> +	int ret;
> +
> +	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);
> +	ret = drm_edid_connector_add_modes(connector);
> +	drm_edid_free(drm_edid);
> +
> +	return ret;

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;
> +	u16 full_pbn, required_pbn;
> +	int available_slots, required_slots;
> +	struct msm_dp_mst_bridge_state *dp_bridge_state;
> +	int i, slots_in_use = 0, active_enc_cnt = 0;
> +	const u32 tot_slots = 63;
> +
> +	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++) {
> +		dp_bridge_state = to_msm_dp_mst_bridge_state(&mst->mst_bridge[i]);
> +		if (dp_bridge_state->connector &&
> +		    dp_bridge_state->connector != connector) {
> +			active_enc_cnt++;
> +			slots_in_use += dp_bridge_state->num_slots;
> +		}
> +	}
> +
> +	if (active_enc_cnt < DP_STREAM_MAX) {
> +		full_pbn = mst_port->full_pbn;
> +		available_slots = tot_slots - slots_in_use;
> +	} 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);
> +
> +	required_slots = msm_dp_mst_find_vcpi_slots(&mst->mst_mgr, required_pbn);
> +
> +	if (required_pbn > full_pbn || required_slots > available_slots) {
> +		drm_dbg_dp(dp_display->drm_dev,
> +			   "mode:%s not supported. pbn %d vs %d slots %d vs %d\n",
> +			   mode->name, required_pbn, full_pbn,
> +			   required_slots, available_slots);
> +		return MODE_BAD;
> +	}
> +
> +	return msm_dp_display_mode_valid(dp_display, &dp_display->connector->display_info, mode);
> +}
> +
> +static struct drm_encoder *
> +msm_dp_mst_atomic_best_encoder(struct drm_connector *connector, struct drm_atomic_state *state)

Do we need this callback? Don't we have a fixed relationship between
connectors and encoders?

> +{
> +	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 *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++) {
> +		bridge_state = msm_dp_mst_br_priv_state(state, &mst->mst_bridge[i]);
> +		if (IS_ERR(bridge_state))
> +			goto end;
> +
> +		if (bridge_state->connector == connector) {
> +			enc = mst->mst_bridge[i].encoder;
> +			goto end;
> +		}
> +	}
> +
> +	for (i = 0; i < mst->max_streams; i++) {
> +		bridge_state = msm_dp_mst_br_priv_state(state, &mst->mst_bridge[i]);
> +
> +		if (!bridge_state->connector) {
> +			bridge_state->connector = connector;
> +			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, i;
> +	bool vcpi_released = false;
> +	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 *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;
> +
> +	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 mode_set;
> +
> +	old_crtc = old_conn_state->crtc;
> +	if (!old_crtc)
> +		goto mode_set;
> +
> +	crtc_state = drm_atomic_get_new_crtc_state(state, old_crtc);
> +
> +	for (i = 0; i < mst->max_streams; i++) {
> +		bridge = &mst->mst_bridge[i];
> +		drm_dbg_dp(dp_display->drm_dev, "bridge id:%d, vcpi:%d, pbn:%d, slots:%d\n",
> +			   bridge->id, bridge->vcpi, bridge->pbn,
> +			   bridge->num_slots);
> +	}
> +
> +	/*attempt to release vcpi slots on a modeset change for crtc state*/

missing spaces in the block comment

> +	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);

This really looks like this should be a bridge's callback.

> +		if (WARN_ON(!drm_bridge)) {
> +			rc = -EINVAL;
> +			goto end;
> +		}
> +		bridge = to_msm_dp_mst_bridge(drm_bridge);
> +
> +		bridge_state = msm_dp_mst_br_priv_state(state, bridge);
> +		if (IS_ERR(bridge_state)) {
> +			rc = PTR_ERR(bridge_state);
> +			goto end;
> +		}
> +
> +		if (WARN_ON(bridge_state->connector != connector)) {
> +			rc = -EINVAL;
> +			goto end;
> +		}
> +
> +		slots = bridge_state->num_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;
> +			}
> +			vcpi_released = true;
> +		}
> +
> +		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
> +			 */
> +			bridge_state->num_slots = 0;
> +			bridge_state->connector = NULL;
> +			bridge_state->msm_dp_panel = NULL;
> +
> +			drm_dbg_dp(dp_display->drm_dev, "clear best encoder: %d\n", bridge->id);
> +		}
> +	}

This looks like there are several functions fused together. Please
unfuse those into small and neat code blocks.

> +
> +mode_set:
> +	if (!new_conn_state->crtc)
> +		goto end;
> +
> +	crtc_state = drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
> +
> +	if (drm_atomic_crtc_needs_modeset(crtc_state) && crtc_state->active) {

Use of crtc_state->active doesn't look correct.

> +		if (WARN_ON(!new_conn_state->best_encoder)) {
> +			rc = -EINVAL;
> +			goto end;
> +		}
> +
> +		drm_bridge = drm_bridge_chain_get_first_bridge(new_conn_state->best_encoder);
> +		if (WARN_ON(!drm_bridge)) {
> +			rc = -EINVAL;
> +			goto end;
> +		}
> +		bridge = to_msm_dp_mst_bridge(drm_bridge);
> +
> +		bridge_state = msm_dp_mst_br_priv_state(state, bridge);
> +		if (IS_ERR(bridge_state)) {
> +			rc = PTR_ERR(bridge_state);
> +			goto end;
> +		}
> +
> +		if (WARN_ON(bridge_state->connector != connector)) {
> +			rc = -EINVAL;
> +			goto end;
> +		}

Can all of this actually happen?

> +
> +		/*
> +		 * check if vcpi slots are trying to get allocated in same phase
> +		 * as deallocation. If so, go to end to avoid allocation.
> +		 */
> +		if (vcpi_released) {
> +			drm_dbg_dp(dp_display->drm_dev,
> +				   "skipping allocation since vcpi was released in the same state\n");
> +			goto end;
> +		}
> +
> +		if (WARN_ON(bridge_state->num_slots)) {
> +			rc = -EINVAL;
> +			goto end;
> +		}
> +
> +		slots = msm_dp_mst_compute_config(state, mst, connector, &crtc_state->mode);
> +		if (slots < 0) {
> +			rc = slots;
> +			goto end;
> +		}
> +
> +		bridge_state->num_slots = slots;
> +	}
> +
> +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);
> +}
> +
> +/* 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_connector;
> +	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_connector = devm_kzalloc(dev->dev, sizeof(*mst_connector), GFP_KERNEL);

This shows that somebody doesn't understand the reason for drmm and the
difference between devm and drmm and the lifetime of the objects. Do you
see two issues in this line?

Let me help you. Please use normal (non-managed) memory here. It is the
only correct way to allocate memory for MST connectors.

> +
> +	drm_modeset_lock_all(dev);
> +
> +	rc = drm_connector_dynamic_init(dev, &mst_connector->connector,
> +					&msm_dp_drm_mst_connector_funcs,
> +					DRM_MODE_CONNECTOR_DisplayPort, NULL);
> +	if (rc) {
> +		drm_modeset_unlock_all(dev);
> +		return NULL;
> +	}
> +
> +	mst_connector->dp_panel = msm_dp_display_get_panel(dp_display);
> +	if (!mst_connector->dp_panel) {
> +		DRM_ERROR("failed to get dp_panel for connector\n");
> +		drm_modeset_unlock_all(dev);
> +		return NULL;
> +	}
> +
> +	mst_connector->dp_panel->connector = &mst_connector->connector;
> +	mst_connector->msm_dp = dp_display;
> +	connector = &mst_connector->connector;
> +	drm_connector_helper_add(&mst_connector->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(&mst_connector->connector,
> +						  dp_mst->mst_bridge[i].encoder);
> +		if (rc) {
> +			DRM_ERROR("failed to attach encoder to connector, %d\n", rc);
> +			drm_modeset_unlock_all(dev);
> +			return NULL;
> +		}
> +	}
> +
> +	mst_connector->mst_port = port;
> +	drm_dp_mst_get_port_malloc(mst_connector->mst_port);
> +
> +	drm_object_attach_property(&mst_connector->connector.base,
> +				   dev->mode_config.path_property, 0);
> +	drm_object_attach_property(&mst_connector->connector.base,
> +				   dev->mode_config.tile_property, 0);

subconnector? Or do we report the subconnector only for the main DP
port?

> +
> +	drm_modeset_unlock_all(dev);
> +
> +	drm_dbg_dp(dp_display->drm_dev, "add mst connector id:%d\n",

Each time I see "mst" in the text message, I feel a bit sad. It's MST
(or SST).

> +		   mst_connector->connector.base.id);
> +
> +	return &mst_connector->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;
> +
> +	msm_dp_mst->mst_bridge = drmm_kzalloc(dev, max_streams * sizeof(struct msm_dp_mst_bridge),
> +					      GFP_KERNEL);
> +
> +	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 cdde59da7bb937ad67fa818a728082f8fd43a3a6..7b08346e0bb9189009c02745c243722c01d79493 100644
> --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.h
> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
> @@ -75,6 +75,7 @@ struct msm_dp_mst {
>  	struct drm_dp_mst_topology_mgr mst_mgr;
>  	struct msm_dp_mst_bridge *mst_bridge;
>  	struct msm_dp *msm_dp;
> +	struct drm_dp_aux *dp_aux;
>  	u32 max_streams;
>  	struct mutex mst_lock;
>  };
> @@ -97,4 +98,6 @@ struct msm_dp_mst_connector {
>  		container_of((x), struct msm_dp_mst_connector, 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] 108+ messages in thread

* Re: [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST
  2025-06-09 12:21 ` [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST Yongxing Mou
  2025-06-09 15:44   ` Dmitry Baryshkov
@ 2025-06-09 15:51   ` Dmitry Baryshkov
  2025-06-16 12:43     ` Yongxing Mou
  1 sibling, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 15:51 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:48PM +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.
> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> ---
>  drivers/gpu/drm/msm/dp/dp_mst_drm.c | 515 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/msm/dp/dp_mst_drm.h |   3 +
>  2 files changed, 518 insertions(+)

> +
> +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;
> +	u16 full_pbn, required_pbn;
> +	int available_slots, required_slots;
> +	struct msm_dp_mst_bridge_state *dp_bridge_state;
> +	int i, slots_in_use = 0, active_enc_cnt = 0;
> +	const u32 tot_slots = 63;
> +
> +	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++) {
> +		dp_bridge_state = to_msm_dp_mst_bridge_state(&mst->mst_bridge[i]);
> +		if (dp_bridge_state->connector &&
> +		    dp_bridge_state->connector != connector) {
> +			active_enc_cnt++;
> +			slots_in_use += dp_bridge_state->num_slots;
> +		}
> +	}
> +
> +	if (active_enc_cnt < DP_STREAM_MAX) {
> +		full_pbn = mst_port->full_pbn;
> +		available_slots = tot_slots - slots_in_use;
> +	} 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);
> +
> +	required_slots = msm_dp_mst_find_vcpi_slots(&mst->mst_mgr, required_pbn);
> +
> +	if (required_pbn > full_pbn || required_slots > available_slots) {
> +		drm_dbg_dp(dp_display->drm_dev,
> +			   "mode:%s not supported. pbn %d vs %d slots %d vs %d\n",
> +			   mode->name, required_pbn, full_pbn,
> +			   required_slots, available_slots);
> +		return MODE_BAD;
> +	}

I almost missed this. Could you please point me, do other drivers
perform mode_valid() check based on the current slots available or not?
Could you please point me to the relevant code in other drivers? Because
it doesn't look correct to me. The mode on the screen remains valid no
matter if I plug or unplug other devices. The atomic_check() should fail
if we don't have enough resources (which includes slots).

> +
> +	return msm_dp_display_mode_valid(dp_display, &dp_display->connector->display_info, mode);
> +}
> +

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 28/38] drm/msm/dp: add dp_mst_drm to manage DP MST bridge operations
  2025-06-09 12:21 ` [PATCH v2 28/38] drm/msm/dp: add dp_mst_drm to manage DP MST bridge operations Yongxing Mou
@ 2025-06-09 15:57   ` Dmitry Baryshkov
  2025-06-11 11:39     ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 15:57 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:47PM +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.

I don't see this patch being improved from v1. Please follow the
feedback or provide a _good_ reason to ignore it.

> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.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 | 501 ++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/msm/dp/dp_mst_drm.h | 100 +++++++
>  4 files changed, 606 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> index 7a2ada6e2d74a902879e4f12a78ed475e5209ec2..d317e5ecda28dfd708ccdc5d3d27d4cf0b78b9d5 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 85eaa55fdcb7d9d8713849ec64a2cc9b08924425..9ca045ed2b4f1a9bc3254b733d1ce5332ebaba96 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
> @@ -24,6 +25,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..a3ea34ae63511db0ac920cbeebe30c4e2320b8c4
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> @@ -0,0 +1,501 @@
> +// 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"
> +
> +static struct drm_private_state *msm_dp_mst_duplicate_bridge_state(struct drm_private_obj *obj)
> +{
> +	struct msm_dp_mst_bridge_state *state;
> +
> +	state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
> +
> +	return &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 *priv_state =
> +		to_msm_dp_mst_bridge_priv_state(state);
> +
> +	kfree(priv_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_dp_mst_port *port)

As I wrote:

drm_dp_atomic_find_time_slots() should be called from atomic_check(),
this function must be dropped.

> +{
> +	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;
> +	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) {
> +			/*
> +			 * 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 = msm_dp_bridge->start_slot;
> +				prev_slots = msm_dp_bridge->num_slots;
> +				msm_dp_bridge->pbn = 0;
> +				msm_dp_bridge->start_slot = 1;
> +				msm_dp_bridge->num_slots = 0;
> +				msm_dp_bridge->vcpi = 0;
> +			} else { //add payload
> +				msm_dp_bridge->pbn = payload->pbn;
> +				msm_dp_bridge->start_slot = payload->vc_start_slot;
> +				msm_dp_bridge->num_slots = payload->time_slots;
> +				msm_dp_bridge->vcpi = payload->vcpi;
> +			}
> +		}
> +	}
> +
> +	// Now commit all the updated payloads
> +	for (i = 0; i < mst->max_streams; i++) {
> +		msm_dp_bridge = &mst->mst_bridge[i];
> +
> +		//Shift payloads to the left if there was a removed payload.
> +		if (payload->vc_start_slot < 0 && msm_dp_bridge->start_slot > prev_start)
> +			msm_dp_bridge->start_slot -= prev_slots;
> +
> +		msm_dp_display_set_stream_info(mst->msm_dp, msm_dp_bridge->msm_dp_panel,
> +					       msm_dp_bridge->id, msm_dp_bridge->start_slot,
> +					       msm_dp_bridge->num_slots,
> +					       msm_dp_bridge->pbn, msm_dp_bridge->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), msm_dp_bridge->vcpi,
> +			   msm_dp_bridge->start_slot,
> +			   msm_dp_bridge->num_slots, msm_dp_bridge->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 *msm_dp = dp_bridge->display;
> +	struct msm_dp_mst *mst = msm_dp->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(msm_dp->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, 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 *msm_dp = dp_bridge->display;
> +	struct msm_dp_mst *mst = msm_dp->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(msm_dp->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 *msm_dp = dp_bridge->display;
> +	struct msm_dp_mst *mst = msm_dp->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, port);
> +
> +	drm_dbg_dp(msm_dp->drm_dev, "mst bridge [%d] _pre disable part-1 complete\n",
> +		   dp_bridge->id);
> +}
> +
> +static void _msm_dp_mst_bridge_pre_disable_part2(struct msm_dp_mst_bridge *dp_bridge)
> +{
> +	struct msm_dp *msm_dp = dp_bridge->display;
> +	struct msm_dp_mst *mst = msm_dp->msm_dp_mst;
> +
> +	drm_dp_check_act_status(&mst->mst_mgr);
> +
> +	drm_dbg_dp(msm_dp->drm_dev, "mst bridge [%d] _pre disable part-2 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;
> +	struct msm_dp_mst_bridge_state *msm_dp_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);
> +	msm_dp_bridge_state = to_msm_dp_mst_bridge_state(bridge);
> +	dp = bridge->display;
> +	dp_mst = dp->msm_dp_mst;
> +
> +	/* to cover cases of bridge_disable/bridge_enable without modeset */
> +	bridge->connector = msm_dp_bridge_state->connector;
> +	bridge->msm_dp_panel = msm_dp_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);
> +
> +	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);
> +		mutex_unlock(&dp_mst->mst_lock);
> +		return;
> +	}
> +
> +	msm_dp_display_enable_helper(dp, bridge->msm_dp_panel);
> +
> +	_msm_dp_mst_bridge_pre_enable_part2(bridge, state);
> +
> +	mutex_unlock(&dp_mst->mst_lock);
> +
> +	drm_dbg_dp(dp->drm_dev, "conn:%d mode:%s fps:%d vcpi:%d slots:%d to %d\n",
> +		   DP_MST_CONN_ID(bridge), bridge->drm_mode.name,
> +		   drm_mode_vrefresh(&bridge->drm_mode),
> +		   bridge->vcpi, bridge->start_slot,
> +		   bridge->start_slot + bridge->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;
> +	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 = bridge->display;
> +	mst = dp->msm_dp_mst;
> +
> +	mutex_lock(&mst->mst_lock);
> +
> +	_msm_dp_mst_bridge_pre_disable_part1(bridge, state);
> +
> +	msm_dp_display_disable_helper(dp, bridge->msm_dp_panel);
> +
> +	_msm_dp_mst_bridge_pre_disable_part2(bridge);
> +
> +	mutex_unlock(&mst->mst_lock);
> +
> +	drm_dbg_dp(dp->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;
> +	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 = bridge->display;
> +	mst = dp->msm_dp_mst;
> +
> +	mutex_lock(&mst->mst_lock);
> +
> +	msm_dp_display_atomic_post_disable_helper(dp, bridge->msm_dp_panel);
> +
> +	if (!dp->mst_active)
> +		msm_dp_display_unprepare(dp);
> +
> +	bridge->connector = NULL;
> +	bridge->msm_dp_panel =  NULL;
> +
> +	mutex_unlock(&mst->mst_lock);
> +
> +	drm_dbg_dp(dp->drm_dev, "mst bridge:%d conn:%d post disable complete\n",
> +		   bridge->id, conn);
> +}
> +
> +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 *dp_bridge_state;
> +	struct msm_dp *dp;
> +	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);
> +
> +	dp_bridge_state = to_msm_dp_mst_bridge_state(bridge);
> +	bridge->connector = dp_bridge_state->connector;
> +	bridge->msm_dp_panel = dp_bridge_state->msm_dp_panel;
> +
> +	msm_dp_panel = bridge->msm_dp_panel;
> +	dp = bridge->display;
> +
> +	memcpy(&bridge->drm_mode, adjusted_mode, sizeof(bridge->drm_mode));
> +	msm_dp_display_mode_set_helper(dp, 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->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,
> +	.mode_set     = msm_dp_mst_bridge_mode_set,
> +};
> +
> +int msm_dp_mst_drm_bridge_init(struct msm_dp *dp, struct drm_encoder *encoder)
> +{
> +	int rc = 0;
> +	struct msm_dp_mst_bridge *bridge = NULL;
> +	struct msm_dp_mst_bridge_state *state;
> +	struct drm_device *dev;
> +	struct msm_dp_mst *mst = dp->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->drm_dev;
> +	bridge->display = dp;
> +	bridge->base.funcs = &msm_dp_mst_bridge_ops;
> +	bridge->base.encoder = encoder;
> +	bridge->base.type = dp->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;
> +	}
> +
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state) {
> +		rc = -ENOMEM;
> +		goto end;
> +	}
> +
> +	drm_atomic_private_obj_init(dev, &bridge->obj,
> +				    &state->base,
> +				    &msm_dp_mst_bridge_state_funcs);
> +
> +	drm_dbg_dp(dp->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..cdde59da7bb937ad67fa818a728082f8fd43a3a6
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
> @@ -0,0 +1,100 @@
> +/* 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"
> +
> +#define DP_MST_CONN_ID(bridge) ((bridge)->connector ? \
> +		(bridge)->connector->base.id : 0)
> +
> +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_display_mode drm_mode;
> +	struct drm_connector *connector;
> +	struct msm_dp_panel *msm_dp_panel;
> +
> +	int vcpi;
> +	int pbn;
> +	int num_slots;
> +	int start_slot;

All of these should go to the bridge_state or be dropped.

> +};
> +
> +struct msm_dp_mst_bridge_state {
> +	struct drm_private_state base;
> +	struct drm_connector *connector;
> +	struct msm_dp_panel *msm_dp_panel;
> +	int num_slots;
> +};
> +
> +struct msm_dp_mst {
> +	struct drm_dp_mst_topology_mgr mst_mgr;
> +	struct msm_dp_mst_bridge *mst_bridge;
> +	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;
> +};
> +
> +#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_priv_state(x) \
> +		container_of((x), struct msm_dp_mst_bridge_state, base)
> +#define to_msm_dp_mst_bridge_state(x) \
> +		to_msm_dp_mst_bridge_priv_state((x)->obj.state)
> +#define to_msm_dp_mst_connector(x) \
> +		container_of((x), struct msm_dp_mst_connector, connector)

Please check, which of these structures and conversion macros can go
into the source file because they are not used outside of it.

> +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] 108+ messages in thread

* Re: [PATCH v2 26/38] drm/msm/dp: skip reading the EDID for MST cases
  2025-06-09 12:21 ` [PATCH v2 26/38] drm/msm/dp: skip reading the EDID for MST cases Yongxing Mou
@ 2025-06-09 15:58   ` Dmitry Baryshkov
  2025-08-14  8:22     ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 15:58 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:45PM +0800, Yongxing Mou wrote:
> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
> 
> For MST cases, EDID is handled through AUX sideband messaging.
> Skip the EDID read during hotplug handle for MST cases.

Why? It makes sense to read it during the HPD processing, ping HDMI
codec, update CEC info, etc.

> 
> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> ---
>  drivers/gpu/drm/msm/dp/dp_display.c | 8 +++++---
>  1 file changed, 5 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 88cae0ca66015377e59bee757462edeae5ae91bf..b1b025d1d356046f8f9e3d243fc774185df24318 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -438,9 +438,11 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
>  	if (rc)
>  		goto end;
>  
> -	rc = msm_dp_panel_read_edid(dp->panel, connector);
> -	if (rc)
> -		goto end;
> +	if (!dp->mst_supported || !drm_dp_read_mst_cap(dp->aux, dp->panel->dpcd)) {
> +		rc = msm_dp_panel_read_edid(dp->panel, connector);
> +		if (rc)
> +			goto end;
> +	}
>  
>  	msm_dp_link_process_request(dp->link);
>  
> 
> -- 
> 2.34.1
> 

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets
  2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
                   ` (38 preceding siblings ...)
  2025-06-09 12:36 ` [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Dmitry Baryshkov
@ 2025-06-09 16:07 ` Dmitry Baryshkov
  2025-06-10  4:31   ` Yongxing Mou
  39 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-09 16:07 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 09, 2025 at 08:21:19PM +0800, Yongxing Mou wrote:
> 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/seriedds/142010/ (v2 to fix up HPD)
> 
> Bindings for the pixel clock for additional stream is available at :
> 
> [2] : https://patchwork.freedesktop.org/series/142016/
> 
> 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.

I went about a halfway through the series. During that I noticed ignored
feedback, missing reviewer tags, invalid or strange authorship / SoB
tags, overgrown functions, one line wrappers, etc.

Please rebase on top of [3], review the patch series on your own,
removing / reworking all the bits that can catch my eye, implement the
feedback, ask questions where you don't understand it, etc. After that
please post v3. I'll most likely skip the rest of the patches for now.

[3] https://lore.kernel.org/dri-devel/20250518-fd-dp-audio-fixup-v6-0-2f0ec3ec000d@oss.qualcomm.com

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets
  2025-06-09 16:07 ` Dmitry Baryshkov
@ 2025-06-10  4:31   ` Yongxing Mou
  2025-06-10  8:31     ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-10  4:31 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/10 0:07, Dmitry Baryshkov wrote:
> On Mon, Jun 09, 2025 at 08:21:19PM +0800, Yongxing Mou wrote:
>> 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/seriedds/142010/ (v2 to fix up HPD)
>>
>> Bindings for the pixel clock for additional stream is available at :
>>
>> [2] : https://patchwork.freedesktop.org/series/142016/
>>
>> 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.
> 
> I went about a halfway through the series. During that I noticed ignored
> feedback, missing reviewer tags, invalid or strange authorship / SoB
> tags, overgrown functions, one line wrappers, etc.
> 
> Please rebase on top of [3], review the patch series on your own,
> removing / reworking all the bits that can catch my eye, implement the
> feedback, ask questions where you don't understand it, etc. After that
> please post v3. I'll most likely skip the rest of the patches for now.
> 
> [3] https://lore.kernel.org/dri-devel/20250518-fd-dp-audio-fixup-v6-0-2f0ec3ec000d@oss.qualcomm.com
> 
Hi, Dmitry, thanks for your reviewing very much. Actually i have 
reviewed all the comments on Patch v1. Sorry for not cleaning up 
everything as expected. I will address and discuss all the remaining 
items in the comments for Patch v2, and will follow up with Patch v3 
accordingly.


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets
  2025-06-09 12:36 ` [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Dmitry Baryshkov
@ 2025-06-10  4:47   ` Yongxing Mou
  2025-06-10  8:30     ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-10  4:47 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/9 20:36, Dmitry Baryshkov wrote:
> On Mon, Jun 09, 2025 at 08:21:19PM +0800, Yongxing Mou wrote:
>> 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.
> 
> Which means that you didn't validate the MST interaction with the USB-C
> stack (there is a significant difference in the way HPD event is handled
> in the Linux kernel).
> 
Yes. this patch series not test with USB-DP. Actually, our initial plan 
was 2x2 MST on SA8775P and 4 MST on ACS8300. All of them are native DP 
interface, not USB-DP. So can we only support SA8775P/QCS8300 in this 
series. We don't plan to support other platform in this patch series.
>> 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/seriedds/142010/ (v2 to fix up HPD)
> 
> This series has serious concerns and most likely will not be merged. Not
> to mention that the URL is invalid.
> 
Got it. Sorry for the typo in the URL. So should we drop this patch 
series or wait the state machine rework from Jessica zhang ?
>>
>> Bindings for the pixel clock for additional stream is available at :
>>
>> [2] : https://patchwork.freedesktop.org/series/142016/
> 
> This series needs another revision.
> 
Got it.
> Not to mention that I plan to land [3] this cycle
> 
> [3] http://lore.kernel.org/dri-devel/20250518-fd-dp-audio-fixup-v6-0-2f0ec3ec000d@oss.qualcomm.com
> 
> 
Got it. will rebase on this patch series.
>> 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.
> 
> Validation should be using IGT for testing. Please ensure that there are
> no regressions.
> 
>>
>> 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 <quic_yongmou@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 (35):
>>        drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid
>>        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: move the pixel clock control to its own API
>>        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 the max_streams for dp controller
>>        drm/msm/dp: introduce stream_id for each DP panel
>>        drm/msm/dp: add support for programming p1/p2/p3 register block
>>        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 program mst support in mainlink
>>        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 to send vcpf packets in dp controller
>>        drm/msm/dp: always program MST_FIFO_CONSTANT_FILL for MST
>>        drm/msm/dp: abstract out the dp_display stream helpers to accept a panel
>>        drm/msm/dp: move link related operations to dp_display_unprepare()
>>        drm/msm/dp: replace power_on with active_stream_cnt for dp_display
>>        drm/msm/dp: make the SST bridge disconnected when mst is active
>>        drm/msm/dp: add an API to initialize MST on sink side
>>        drm/msm/dp: skip reading the EDID for MST cases
>>        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 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: mark ST_DISCONNECTED only if all streams are disabled
>>        drm/msm/dp: fix the intf_type of MST interfaces
>>
>> Yongxing Mou (3):
>>        drm/msm/dp: Add catalog support for 3rd/4th stream MST
>>        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_4_sa8775p.h    |    8 +-
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c        |   21 +-
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h        |    2 +
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c            |   72 +-
>>   drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h            |    2 +-
>>   drivers/gpu/drm/msm/dp/dp_audio.c                  |    2 +-
>>   drivers/gpu/drm/msm/dp/dp_catalog.c                |  558 ++++++++--
>>   drivers/gpu/drm/msm/dp/dp_catalog.h                |   64 +-
>>   drivers/gpu/drm/msm/dp/dp_ctrl.c                   |  474 ++++++---
>>   drivers/gpu/drm/msm/dp/dp_ctrl.h                   |   22 +-
>>   drivers/gpu/drm/msm/dp/dp_display.c                |  510 +++++++---
>>   drivers/gpu/drm/msm/dp/dp_display.h                |   33 +-
>>   drivers/gpu/drm/msm/dp/dp_drm.c                    |   53 +-
>>   drivers/gpu/drm/msm/dp/dp_drm.h                    |   12 -
>>   drivers/gpu/drm/msm/dp/dp_mst_drm.c                | 1065 ++++++++++++++++++++
>>   drivers/gpu/drm/msm/dp/dp_mst_drm.h                |  106 ++
>>   drivers/gpu/drm/msm/dp/dp_panel.c                  |   66 +-
>>   drivers/gpu/drm/msm/dp/dp_panel.h                  |   10 +-
>>   drivers/gpu/drm/msm/dp/dp_reg.h                    |   46 +-
>>   drivers/gpu/drm/msm/msm_atomic.c                   |    3 +
>>   drivers/gpu/drm/msm/msm_drv.h                      |   19 +
>>   drivers/gpu/drm/msm/msm_kms.c                      |    2 +
>>   23 files changed, 2725 insertions(+), 428 deletions(-)
>> ---
>> base-commit: 475c850a7fdd0915b856173186d5922899d65686
>> change-id: 20250609-msm-dp-mst-cddc2f61daee
>> prerequisite-message-id: <20250529-hpd_display_off-v1-0-ce33bac2987c@oss.qualcomm.com>
>> prerequisite-patch-id: a1f426b99b4a99d63daa9902cde9ee38ae1128d1
>> prerequisite-patch-id: ae9e0a0db8edd05da06f9673e9de56761654ed3c
>> prerequisite-patch-id: 7cb84491c6c3cf73480343218c543d090f8cb5e2
>> prerequisite-patch-id: f32638e79dd498db2075735392e85729b1b691fc
>> prerequisite-message-id: <20250530-dp_mst_bindings-v2-0-f925464d32a8@oss.qualcomm.com>
>> prerequisite-patch-id: e505c21f653c8e18ce83cad1fc787c13a6c8ed12
>> prerequisite-patch-id: cfdd5c37d38b2a4f1386af4021ba3920c6d8dcf8
>> prerequisite-patch-id: f4abdddcb90c8203044395f4768d794214fe3225
>> prerequisite-patch-id: 45013dfaf34856422b7b6b3d2ee42d81a917177b
>> prerequisite-patch-id: 2f35bedb0410bead1b66cbfaf51984fc7016828f
>>
>> Best regards,
>> -- 
>> Yongxing Mou <quic_yongmou@quicinc.com>
>>
> 


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets
  2025-06-10  4:47   ` Yongxing Mou
@ 2025-06-10  8:30     ` Dmitry Baryshkov
  2025-06-11 12:08       ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-10  8:30 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Tue, Jun 10, 2025 at 12:47:00PM +0800, Yongxing Mou wrote:
> 
> 
> On 2025/6/9 20:36, Dmitry Baryshkov wrote:
> > On Mon, Jun 09, 2025 at 08:21:19PM +0800, Yongxing Mou wrote:
> > > 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.
> > 
> > Which means that you didn't validate the MST interaction with the USB-C
> > stack (there is a significant difference in the way HPD event is handled
> > in the Linux kernel).
> > 
> Yes. this patch series not test with USB-DP. Actually, our initial plan was
> 2x2 MST on SA8775P and 4 MST on ACS8300. All of them are native DP
> interface, not USB-DP. So can we only support SA8775P/QCS8300 in this
> series. We don't plan to support other platform in this patch series.

I'm sorry, it doesn't work this way. This is not a product kernel,
limited to a selected set of platforms. So, you have to hook MST support
in both paths. The series is not going to be merged tomorrow, so you
have enough time to validate MST on the platforms providing DP over
USB-C.

> > > 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/seriedds/142010/ (v2 to fix up HPD)
> > 
> > This series has serious concerns and most likely will not be merged. Not
> > to mention that the URL is invalid.
> > 
> Got it. Sorry for the typo in the URL. So should we drop this patch series
> or wait the state machine rework from Jessica zhang ?

Please work with Jessica. As I wrote, I do not intend to land the
mentioned series.

> > > 
> > > Bindings for the pixel clock for additional stream is available at :
> > > 
> > > [2] : https://patchwork.freedesktop.org/series/142016/
> > 
> > This series needs another revision.
> > 
> Got it.
> > Not to mention that I plan to land [3] this cycle
> > 
> > [3] http://lore.kernel.org/dri-devel/20250518-fd-dp-audio-fixup-v6-0-2f0ec3ec000d@oss.qualcomm.com
> > 
> > 
> Got it. will rebase on this patch series.
> > > 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.
> > 
> > Validation should be using IGT for testing. Please ensure that there are
> > no regressions.
> > 
> > > 
> > > 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 <quic_yongmou@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 (35):
> > >        drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid
> > >        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: move the pixel clock control to its own API
> > >        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 the max_streams for dp controller
> > >        drm/msm/dp: introduce stream_id for each DP panel
> > >        drm/msm/dp: add support for programming p1/p2/p3 register block
> > >        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 program mst support in mainlink
> > >        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 to send vcpf packets in dp controller
> > >        drm/msm/dp: always program MST_FIFO_CONSTANT_FILL for MST
> > >        drm/msm/dp: abstract out the dp_display stream helpers to accept a panel
> > >        drm/msm/dp: move link related operations to dp_display_unprepare()
> > >        drm/msm/dp: replace power_on with active_stream_cnt for dp_display
> > >        drm/msm/dp: make the SST bridge disconnected when mst is active
> > >        drm/msm/dp: add an API to initialize MST on sink side
> > >        drm/msm/dp: skip reading the EDID for MST cases
> > >        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 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: mark ST_DISCONNECTED only if all streams are disabled
> > >        drm/msm/dp: fix the intf_type of MST interfaces
> > > 
> > > Yongxing Mou (3):
> > >        drm/msm/dp: Add catalog support for 3rd/4th stream MST
> > >        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_4_sa8775p.h    |    8 +-
> > >   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c        |   21 +-
> > >   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h        |    2 +
> > >   drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c            |   72 +-
> > >   drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h            |    2 +-
> > >   drivers/gpu/drm/msm/dp/dp_audio.c                  |    2 +-
> > >   drivers/gpu/drm/msm/dp/dp_catalog.c                |  558 ++++++++--
> > >   drivers/gpu/drm/msm/dp/dp_catalog.h                |   64 +-
> > >   drivers/gpu/drm/msm/dp/dp_ctrl.c                   |  474 ++++++---
> > >   drivers/gpu/drm/msm/dp/dp_ctrl.h                   |   22 +-
> > >   drivers/gpu/drm/msm/dp/dp_display.c                |  510 +++++++---
> > >   drivers/gpu/drm/msm/dp/dp_display.h                |   33 +-
> > >   drivers/gpu/drm/msm/dp/dp_drm.c                    |   53 +-
> > >   drivers/gpu/drm/msm/dp/dp_drm.h                    |   12 -
> > >   drivers/gpu/drm/msm/dp/dp_mst_drm.c                | 1065 ++++++++++++++++++++
> > >   drivers/gpu/drm/msm/dp/dp_mst_drm.h                |  106 ++
> > >   drivers/gpu/drm/msm/dp/dp_panel.c                  |   66 +-
> > >   drivers/gpu/drm/msm/dp/dp_panel.h                  |   10 +-
> > >   drivers/gpu/drm/msm/dp/dp_reg.h                    |   46 +-
> > >   drivers/gpu/drm/msm/msm_atomic.c                   |    3 +
> > >   drivers/gpu/drm/msm/msm_drv.h                      |   19 +
> > >   drivers/gpu/drm/msm/msm_kms.c                      |    2 +
> > >   23 files changed, 2725 insertions(+), 428 deletions(-)
> > > ---
> > > base-commit: 475c850a7fdd0915b856173186d5922899d65686
> > > change-id: 20250609-msm-dp-mst-cddc2f61daee
> > > prerequisite-message-id: <20250529-hpd_display_off-v1-0-ce33bac2987c@oss.qualcomm.com>
> > > prerequisite-patch-id: a1f426b99b4a99d63daa9902cde9ee38ae1128d1
> > > prerequisite-patch-id: ae9e0a0db8edd05da06f9673e9de56761654ed3c
> > > prerequisite-patch-id: 7cb84491c6c3cf73480343218c543d090f8cb5e2
> > > prerequisite-patch-id: f32638e79dd498db2075735392e85729b1b691fc
> > > prerequisite-message-id: <20250530-dp_mst_bindings-v2-0-f925464d32a8@oss.qualcomm.com>
> > > prerequisite-patch-id: e505c21f653c8e18ce83cad1fc787c13a6c8ed12
> > > prerequisite-patch-id: cfdd5c37d38b2a4f1386af4021ba3920c6d8dcf8
> > > prerequisite-patch-id: f4abdddcb90c8203044395f4768d794214fe3225
> > > prerequisite-patch-id: 45013dfaf34856422b7b6b3d2ee42d81a917177b
> > > prerequisite-patch-id: 2f35bedb0410bead1b66cbfaf51984fc7016828f
> > > 
> > > Best regards,
> > > -- 
> > > Yongxing Mou <quic_yongmou@quicinc.com>
> > > 
> > 
> 

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets
  2025-06-10  4:31   ` Yongxing Mou
@ 2025-06-10  8:31     ` Dmitry Baryshkov
  0 siblings, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-10  8:31 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Tue, Jun 10, 2025 at 12:31:40PM +0800, Yongxing Mou wrote:
> 
> 
> On 2025/6/10 0:07, Dmitry Baryshkov wrote:
> > On Mon, Jun 09, 2025 at 08:21:19PM +0800, Yongxing Mou wrote:
> > > 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/seriedds/142010/ (v2 to fix up HPD)
> > > 
> > > Bindings for the pixel clock for additional stream is available at :
> > > 
> > > [2] : https://patchwork.freedesktop.org/series/142016/
> > > 
> > > 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.
> > 
> > I went about a halfway through the series. During that I noticed ignored
> > feedback, missing reviewer tags, invalid or strange authorship / SoB
> > tags, overgrown functions, one line wrappers, etc.
> > 
> > Please rebase on top of [3], review the patch series on your own,
> > removing / reworking all the bits that can catch my eye, implement the
> > feedback, ask questions where you don't understand it, etc. After that
> > please post v3. I'll most likely skip the rest of the patches for now.
> > 
> > [3] https://lore.kernel.org/dri-devel/20250518-fd-dp-audio-fixup-v6-0-2f0ec3ec000d@oss.qualcomm.com
> > 
> Hi, Dmitry, thanks for your reviewing very much. Actually i have reviewed
> all the comments on Patch v1. Sorry for not cleaning up everything as
> expected. I will address and discuss all the remaining items in the comments
> for Patch v2, and will follow up with Patch v3 accordingly.

Please discuss the items where they have been raised - either in v1 or
v2 thread.

> 

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 28/38] drm/msm/dp: add dp_mst_drm to manage DP MST bridge operations
  2025-06-09 15:57   ` Dmitry Baryshkov
@ 2025-06-11 11:39     ` Yongxing Mou
  2025-06-11 14:27       ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-11 11:39 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/9 23:57, Dmitry Baryshkov wrote:
> On Mon, Jun 09, 2025 at 08:21:47PM +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.
> 
> I don't see this patch being improved from v1. Please follow the
> feedback or provide a _good_ reason to ignore it.
> 
Sorry for that, will explain one by one later..
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.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 | 501 ++++++++++++++++++++++++++++++++++++
>>   drivers/gpu/drm/msm/dp/dp_mst_drm.h | 100 +++++++
>>   4 files changed, 606 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
>> index 7a2ada6e2d74a902879e4f12a78ed475e5209ec2..d317e5ecda28dfd708ccdc5d3d27d4cf0b78b9d5 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 85eaa55fdcb7d9d8713849ec64a2cc9b08924425..9ca045ed2b4f1a9bc3254b733d1ce5332ebaba96 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
>> @@ -24,6 +25,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..a3ea34ae63511db0ac920cbeebe30c4e2320b8c4
>> --- /dev/null
>> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
>> @@ -0,0 +1,501 @@
>> +// 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"
>> +
>> +static struct drm_private_state *msm_dp_mst_duplicate_bridge_state(struct drm_private_obj *obj)
>> +{
>> +	struct msm_dp_mst_bridge_state *state;
>> +
>> +	state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
>> +	if (!state)
>> +		return NULL;
>> +
>> +	__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
>> +
>> +	return &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 *priv_state =
>> +		to_msm_dp_mst_bridge_priv_state(state);
>> +
>> +	kfree(priv_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_dp_mst_port *port)
> 
> As I wrote:
> 
> drm_dp_atomic_find_time_slots() should be called from atomic_check(),
> this function must be dropped.
> 
This func not call drm_dp_atomic_find_time_slots(). 
drm_dp_atomic_find_time_slots() called in msm_dp_mst_compute_config in 
the previous one patch. msm_dp_mst_compute_config is in atomic_check().
>> +{
>> +	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;
>> +	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) {
>> +			/*
>> +			 * When a payload was removed make sure to move any payloads after it
>> +			 * to the left so all payloads are aligned to the left.
>> +			 */
> 
> :-(
> 
Emm,  will get back to this part later.
>> +			if (payload->vc_start_slot < 0) {
>> +				// cache the payload
>> +				prev_start = msm_dp_bridge->start_slot;
>> +				prev_slots = msm_dp_bridge->num_slots;
>> +				msm_dp_bridge->pbn = 0;
>> +				msm_dp_bridge->start_slot = 1;
>> +				msm_dp_bridge->num_slots = 0;
>> +				msm_dp_bridge->vcpi = 0;
>> +			} else { //add payload
>> +				msm_dp_bridge->pbn = payload->pbn;
>> +				msm_dp_bridge->start_slot = payload->vc_start_slot;
>> +				msm_dp_bridge->num_slots = payload->time_slots;
>> +				msm_dp_bridge->vcpi = payload->vcpi;
>> +			}
>> +		}
>> +	}
>> +
>> +	// Now commit all the updated payloads
>> +	for (i = 0; i < mst->max_streams; i++) {
>> +		msm_dp_bridge = &mst->mst_bridge[i];
>> +
>> +		//Shift payloads to the left if there was a removed payload.
>> +		if (payload->vc_start_slot < 0 && msm_dp_bridge->start_slot > prev_start)
>> +			msm_dp_bridge->start_slot -= prev_slots;
>> +
>> +		msm_dp_display_set_stream_info(mst->msm_dp, msm_dp_bridge->msm_dp_panel,
>> +					       msm_dp_bridge->id, msm_dp_bridge->start_slot,
>> +					       msm_dp_bridge->num_slots,
>> +					       msm_dp_bridge->pbn, msm_dp_bridge->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), msm_dp_bridge->vcpi,
>> +			   msm_dp_bridge->start_slot,
>> +			   msm_dp_bridge->num_slots, msm_dp_bridge->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 *msm_dp = dp_bridge->display;
>> +	struct msm_dp_mst *mst = msm_dp->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(msm_dp->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, 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 *msm_dp = dp_bridge->display;
>> +	struct msm_dp_mst *mst = msm_dp->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(msm_dp->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 *msm_dp = dp_bridge->display;
>> +	struct msm_dp_mst *mst = msm_dp->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, port);
>> +
>> +	drm_dbg_dp(msm_dp->drm_dev, "mst bridge [%d] _pre disable part-1 complete\n",
>> +		   dp_bridge->id);
>> +}
>> +
>> +static void _msm_dp_mst_bridge_pre_disable_part2(struct msm_dp_mst_bridge *dp_bridge)
>> +{
>> +	struct msm_dp *msm_dp = dp_bridge->display;
>> +	struct msm_dp_mst *mst = msm_dp->msm_dp_mst;
>> +
>> +	drm_dp_check_act_status(&mst->mst_mgr);
>> +
>> +	drm_dbg_dp(msm_dp->drm_dev, "mst bridge [%d] _pre disable part-2 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;
>> +	struct msm_dp_mst_bridge_state *msm_dp_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);
>> +	msm_dp_bridge_state = to_msm_dp_mst_bridge_state(bridge);
>> +	dp = bridge->display;
>> +	dp_mst = dp->msm_dp_mst;
>> +
>> +	/* to cover cases of bridge_disable/bridge_enable without modeset */
>> +	bridge->connector = msm_dp_bridge_state->connector;
>> +	bridge->msm_dp_panel = msm_dp_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);
>> +
>> +	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);
>> +		mutex_unlock(&dp_mst->mst_lock);
>> +		return;
>> +	}
>> +
>> +	msm_dp_display_enable_helper(dp, bridge->msm_dp_panel);
>> +
>> +	_msm_dp_mst_bridge_pre_enable_part2(bridge, state);
>> +
>> +	mutex_unlock(&dp_mst->mst_lock);
>> +
>> +	drm_dbg_dp(dp->drm_dev, "conn:%d mode:%s fps:%d vcpi:%d slots:%d to %d\n",
>> +		   DP_MST_CONN_ID(bridge), bridge->drm_mode.name,
>> +		   drm_mode_vrefresh(&bridge->drm_mode),
>> +		   bridge->vcpi, bridge->start_slot,
>> +		   bridge->start_slot + bridge->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;
>> +	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 = bridge->display;
>> +	mst = dp->msm_dp_mst;
>> +
>> +	mutex_lock(&mst->mst_lock);
>> +
>> +	_msm_dp_mst_bridge_pre_disable_part1(bridge, state);
>> +
>> +	msm_dp_display_disable_helper(dp, bridge->msm_dp_panel);
>> +
>> +	_msm_dp_mst_bridge_pre_disable_part2(bridge);
>> +
>> +	mutex_unlock(&mst->mst_lock);
>> +
>> +	drm_dbg_dp(dp->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;
>> +	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 = bridge->display;
>> +	mst = dp->msm_dp_mst;
>> +
>> +	mutex_lock(&mst->mst_lock);
>> +
>> +	msm_dp_display_atomic_post_disable_helper(dp, bridge->msm_dp_panel);
>> +
>> +	if (!dp->mst_active)
>> +		msm_dp_display_unprepare(dp);
>> +
>> +	bridge->connector = NULL;
>> +	bridge->msm_dp_panel =  NULL;
>> +
>> +	mutex_unlock(&mst->mst_lock);
>> +
>> +	drm_dbg_dp(dp->drm_dev, "mst bridge:%d conn:%d post disable complete\n",
>> +		   bridge->id, conn);
>> +}
>> +
>> +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 *dp_bridge_state;
>> +	struct msm_dp *dp;
>> +	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);
>> +
>> +	dp_bridge_state = to_msm_dp_mst_bridge_state(bridge);
>> +	bridge->connector = dp_bridge_state->connector;
>> +	bridge->msm_dp_panel = dp_bridge_state->msm_dp_panel;
>> +
>> +	msm_dp_panel = bridge->msm_dp_panel;
>> +	dp = bridge->display;
>> +
>> +	memcpy(&bridge->drm_mode, adjusted_mode, sizeof(bridge->drm_mode));
>> +	msm_dp_display_mode_set_helper(dp, 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->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,
>> +	.mode_set     = msm_dp_mst_bridge_mode_set,
>> +};
>> +
>> +int msm_dp_mst_drm_bridge_init(struct msm_dp *dp, struct drm_encoder *encoder)
>> +{
>> +	int rc = 0;
>> +	struct msm_dp_mst_bridge *bridge = NULL;
>> +	struct msm_dp_mst_bridge_state *state;
>> +	struct drm_device *dev;
>> +	struct msm_dp_mst *mst = dp->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->drm_dev;
>> +	bridge->display = dp;
>> +	bridge->base.funcs = &msm_dp_mst_bridge_ops;
>> +	bridge->base.encoder = encoder;
>> +	bridge->base.type = dp->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;
>> +	}
>> +
>> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
>> +	if (!state) {
>> +		rc = -ENOMEM;
>> +		goto end;
>> +	}
>> +
>> +	drm_atomic_private_obj_init(dev, &bridge->obj,
>> +				    &state->base,
>> +				    &msm_dp_mst_bridge_state_funcs);
>> +
>> +	drm_dbg_dp(dp->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..cdde59da7bb937ad67fa818a728082f8fd43a3a6
>> --- /dev/null
>> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
>> @@ -0,0 +1,100 @@
>> +/* 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"
>> +
>> +#define DP_MST_CONN_ID(bridge) ((bridge)->connector ? \
>> +		(bridge)->connector->base.id : 0)
>> +
>> +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_display_mode drm_mode;
>> +	struct drm_connector *connector;
>> +	struct msm_dp_panel *msm_dp_panel;
>> +
>> +	int vcpi;
>> +	int pbn;
>> +	int num_slots;
>> +	int start_slot;
> 
> All of these should go to the bridge_state or be dropped.
> 

Got it. will move to bridge_state.
>> +};
>> +
>> +struct msm_dp_mst_bridge_state {
>> +	struct drm_private_state base;
>> +	struct drm_connector *connector;
>> +	struct msm_dp_panel *msm_dp_panel;
>> +	int num_slots;
>> +};
>> +
>> +struct msm_dp_mst {
>> +	struct drm_dp_mst_topology_mgr mst_mgr;
>> +	struct msm_dp_mst_bridge *mst_bridge;
>> +	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;
>> +};
>> +
>> +#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_priv_state(x) \
>> +		container_of((x), struct msm_dp_mst_bridge_state, base)
>> +#define to_msm_dp_mst_bridge_state(x) \
>> +		to_msm_dp_mst_bridge_priv_state((x)->obj.state)
>> +#define to_msm_dp_mst_connector(x) \
>> +		container_of((x), struct msm_dp_mst_connector, connector)
> 
> Please check, which of these structures and conversion macros can go
> into the source file because they are not used outside of it.
> 
Got it.
>> +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	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST
  2025-06-09 15:44   ` Dmitry Baryshkov
@ 2025-06-11 12:06     ` Yongxing Mou
  2025-06-11 14:31       ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-11 12:06 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/9 23:44, Dmitry Baryshkov wrote:
> On Mon, Jun 09, 2025 at 08:21:48PM +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.
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>> ---
>>   drivers/gpu/drm/msm/dp/dp_mst_drm.c | 515 ++++++++++++++++++++++++++++++++++++
>>   drivers/gpu/drm/msm/dp/dp_mst_drm.h |   3 +
>>   2 files changed, 518 insertions(+)
> 
> It generally feels liks 80% of this patch is a generic code. Please
> extract generic DP MST connector and push it under drm/display. Other DP
> MST drivers should be able to use it.
> 
>>
>> diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
>> index a3ea34ae63511db0ac920cbeebe30c4e2320b8c4..489fa46aa518ff1cc5f4769b2153fc5153c4cb41 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
>> @@ -25,8 +25,12 @@
>>    * OF THIS SOFTWARE.
>>    */
>>   
>> +#include <drm/drm_edid.h>
>> +#include <drm/drm_managed.h>
>>   #include "dp_mst_drm.h"
>>   
>> +#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 *state;
>> @@ -79,6 +83,61 @@ static int msm_dp_mst_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, int p
>>   	return num_slots;
>>   }
>>   
>> +static int msm_dp_mst_get_mst_pbn_div(struct msm_dp_panel *msm_dp_panel)
>> +{
>> +	struct msm_dp_link_info *link_info;
>> +
>> +	link_info = &msm_dp_panel->link_info;
>> +
>> +	return link_info->rate * link_info->num_lanes / 54000;
>> +}
>> +
>> +static int msm_dp_mst_compute_config(struct drm_atomic_state *state,
>> +				      struct msm_dp_mst *mst, struct drm_connector *connector,
>> +				      struct drm_display_mode *mode)
>> +{
>> +	int slots = 0, pbn;
>> +	struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(connector);
>> +	int rc = 0;
>> +	struct drm_dp_mst_topology_state *mst_state;
>> +	int pbn_div;
>> +	struct msm_dp *dp_display = mst->msm_dp;
>> +	u32 bpp;
>> +
>> +	bpp = connector->display_info.bpc * 3;
>> +
>> +	pbn = drm_dp_calc_pbn_mode(mode->clock, bpp << 4);
> 
> Is this going to change if DSC is in place? Will it bring fractional BPP
> here?
> 
Actually, in this patch series, MST not support DSC. So we just don't 
consider this scenario.
>> +
>> +	mst_state = to_drm_dp_mst_topology_state(mst->mst_mgr.base.state);
>> +
>> +	if (!dfixed_trunc(mst_state->pbn_div)) {
>> +		pbn_div = msm_dp_mst_get_mst_pbn_div(mst_conn->dp_panel);
>> +		mst_state->pbn_div.full = dfixed_const(pbn_div);
> 
> It looks like this should be using drm_dp_get_vc_payload_bw() (i915,
> nouveau). Any reason not to do so?
> 
Got it. Thanks, we can use drm_dp_get_vc_payload_bw.
>> +	}
>> +
>> +	rc = drm_dp_atomic_find_time_slots(state, &mst->mst_mgr, mst_conn->mst_port, pbn);
>> +	if (rc < 0) {
>> +		DRM_ERROR("conn:%d failed to find vcpi slots. pbn:%d, rc:%d\n",
>> +			  connector->base.id, pbn, rc);
>> +		goto end;
>> +	}
>> +
>> +	slots = rc;
>> +
>> +	rc = drm_dp_mst_atomic_check(state);
>> +	if (rc) {
>> +		DRM_ERROR("conn:%d mst atomic check failed: rc=%d\n", connector->base.id, rc);
>> +		slots = 0;
> 
> no need to
> 
>> +		goto end;
>> +	}
>> +
>> +	drm_dbg_dp(dp_display->drm_dev, "conn:%d pbn:%d slots:%d rc:%d\n",
>> +		   connector->base.id, pbn, slots, rc);
>> +
>> +end:
>> +	return (rc < 0 ? rc : slots);
>> +}
>> +
>>   static void _msm_dp_mst_update_timeslots(struct msm_dp_mst *mst,
>>   					 struct msm_dp_mst_bridge *mst_bridge,
>>   					 struct drm_dp_mst_port *port)
>> @@ -499,3 +558,459 @@ int msm_dp_mst_drm_bridge_init(struct msm_dp *dp, struct drm_encoder *encoder)
>>   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;
>> +
>> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>> +
>> +	return to_msm_dp_mst_bridge_priv_state(drm_atomic_get_private_obj_state(st,
>> +										&bridge->obj));
> 
> Save drm_atomic_get_private_obj_state() result to a variable. It will
> make it readable.
> 
Got it.
>> +}
>> +
>> +/* 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;
>> +	int ret;
>> +
>> +	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);
>> +	ret = drm_edid_connector_add_modes(connector);
>> +	drm_edid_free(drm_edid);
>> +
>> +	return ret;
> 
> 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;
>> +	u16 full_pbn, required_pbn;
>> +	int available_slots, required_slots;
>> +	struct msm_dp_mst_bridge_state *dp_bridge_state;
>> +	int i, slots_in_use = 0, active_enc_cnt = 0;
>> +	const u32 tot_slots = 63;
>> +
>> +	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++) {
>> +		dp_bridge_state = to_msm_dp_mst_bridge_state(&mst->mst_bridge[i]);
>> +		if (dp_bridge_state->connector &&
>> +		    dp_bridge_state->connector != connector) {
>> +			active_enc_cnt++;
>> +			slots_in_use += dp_bridge_state->num_slots;
>> +		}
>> +	}
>> +
>> +	if (active_enc_cnt < DP_STREAM_MAX) {
>> +		full_pbn = mst_port->full_pbn;
>> +		available_slots = tot_slots - slots_in_use;
>> +	} 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);
>> +
>> +	required_slots = msm_dp_mst_find_vcpi_slots(&mst->mst_mgr, required_pbn);
>> +
>> +	if (required_pbn > full_pbn || required_slots > available_slots) {
>> +		drm_dbg_dp(dp_display->drm_dev,
>> +			   "mode:%s not supported. pbn %d vs %d slots %d vs %d\n",
>> +			   mode->name, required_pbn, full_pbn,
>> +			   required_slots, available_slots);
>> +		return MODE_BAD;
>> +	}
>> +
>> +	return msm_dp_display_mode_valid(dp_display, &dp_display->connector->display_info, mode);
>> +}
>> +
>> +static struct drm_encoder *
>> +msm_dp_mst_atomic_best_encoder(struct drm_connector *connector, struct drm_atomic_state *state)
> 
> Do we need this callback? Don't we have a fixed relationship between
> connectors and encoders?
> 
>> +{
>> +	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 *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++) {
>> +		bridge_state = msm_dp_mst_br_priv_state(state, &mst->mst_bridge[i]);
>> +		if (IS_ERR(bridge_state))
>> +			goto end;
>> +
>> +		if (bridge_state->connector == connector) {
>> +			enc = mst->mst_bridge[i].encoder;
>> +			goto end;
>> +		}
>> +	}
>> +
>> +	for (i = 0; i < mst->max_streams; i++) {
>> +		bridge_state = msm_dp_mst_br_priv_state(state, &mst->mst_bridge[i]);
>> +
>> +		if (!bridge_state->connector) {
>> +			bridge_state->connector = connector;
>> +			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, i;
>> +	bool vcpi_released = false;
>> +	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 *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;
>> +
>> +	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 mode_set;
>> +
>> +	old_crtc = old_conn_state->crtc;
>> +	if (!old_crtc)
>> +		goto mode_set;
>> +
>> +	crtc_state = drm_atomic_get_new_crtc_state(state, old_crtc);
>> +
>> +	for (i = 0; i < mst->max_streams; i++) {
>> +		bridge = &mst->mst_bridge[i];
>> +		drm_dbg_dp(dp_display->drm_dev, "bridge id:%d, vcpi:%d, pbn:%d, slots:%d\n",
>> +			   bridge->id, bridge->vcpi, bridge->pbn,
>> +			   bridge->num_slots);
>> +	}
>> +
>> +	/*attempt to release vcpi slots on a modeset change for crtc state*/
> 
> missing spaces in the block comment
> 
>> +	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);
> 
> This really looks like this should be a bridge's callback.
> 
>> +		if (WARN_ON(!drm_bridge)) {
>> +			rc = -EINVAL;
>> +			goto end;
>> +		}
>> +		bridge = to_msm_dp_mst_bridge(drm_bridge);
>> +
>> +		bridge_state = msm_dp_mst_br_priv_state(state, bridge);
>> +		if (IS_ERR(bridge_state)) {
>> +			rc = PTR_ERR(bridge_state);
>> +			goto end;
>> +		}
>> +
>> +		if (WARN_ON(bridge_state->connector != connector)) {
>> +			rc = -EINVAL;
>> +			goto end;
>> +		}
>> +
>> +		slots = bridge_state->num_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;
>> +			}
>> +			vcpi_released = true;
>> +		}
>> +
>> +		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
>> +			 */
>> +			bridge_state->num_slots = 0;
>> +			bridge_state->connector = NULL;
>> +			bridge_state->msm_dp_panel = NULL;
>> +
>> +			drm_dbg_dp(dp_display->drm_dev, "clear best encoder: %d\n", bridge->id);
>> +		}
>> +	}
> 
> This looks like there are several functions fused together. Please
> unfuse those into small and neat code blocks.
> 
>> +
>> +mode_set:
>> +	if (!new_conn_state->crtc)
>> +		goto end;
>> +
>> +	crtc_state = drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
>> +
>> +	if (drm_atomic_crtc_needs_modeset(crtc_state) && crtc_state->active) {
> 
> Use of crtc_state->active doesn't look correct.
> 
>> +		if (WARN_ON(!new_conn_state->best_encoder)) {
>> +			rc = -EINVAL;
>> +			goto end;
>> +		}
>> +
>> +		drm_bridge = drm_bridge_chain_get_first_bridge(new_conn_state->best_encoder);
>> +		if (WARN_ON(!drm_bridge)) {
>> +			rc = -EINVAL;
>> +			goto end;
>> +		}
>> +		bridge = to_msm_dp_mst_bridge(drm_bridge);
>> +
>> +		bridge_state = msm_dp_mst_br_priv_state(state, bridge);
>> +		if (IS_ERR(bridge_state)) {
>> +			rc = PTR_ERR(bridge_state);
>> +			goto end;
>> +		}
>> +
>> +		if (WARN_ON(bridge_state->connector != connector)) {
>> +			rc = -EINVAL;
>> +			goto end;
>> +		}
> 
> Can all of this actually happen?
> 
>> +
>> +		/*
>> +		 * check if vcpi slots are trying to get allocated in same phase
>> +		 * as deallocation. If so, go to end to avoid allocation.
>> +		 */
>> +		if (vcpi_released) {
>> +			drm_dbg_dp(dp_display->drm_dev,
>> +				   "skipping allocation since vcpi was released in the same state\n");
>> +			goto end;
>> +		}
>> +
>> +		if (WARN_ON(bridge_state->num_slots)) {
>> +			rc = -EINVAL;
>> +			goto end;
>> +		}
>> +
>> +		slots = msm_dp_mst_compute_config(state, mst, connector, &crtc_state->mode);
>> +		if (slots < 0) {
>> +			rc = slots;
>> +			goto end;
>> +		}
>> +
>> +		bridge_state->num_slots = slots;
>> +	}
>> +
>> +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);
>> +}
>> +
>> +/* 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_connector;
>> +	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_connector = devm_kzalloc(dev->dev, sizeof(*mst_connector), GFP_KERNEL);
> 
> This shows that somebody doesn't understand the reason for drmm and the
> difference between devm and drmm and the lifetime of the objects. Do you
> see two issues in this line?
> 
> Let me help you. Please use normal (non-managed) memory here. It is the
> only correct way to allocate memory for MST connectors.
> 
Thanks for point it.. it will lead to mem leak.. so we need to use 
kzalloc()...
>> +
>> +	drm_modeset_lock_all(dev);
>> +
>> +	rc = drm_connector_dynamic_init(dev, &mst_connector->connector,
>> +					&msm_dp_drm_mst_connector_funcs,
>> +					DRM_MODE_CONNECTOR_DisplayPort, NULL);
>> +	if (rc) {
>> +		drm_modeset_unlock_all(dev);
>> +		return NULL;
>> +	}
>> +
>> +	mst_connector->dp_panel = msm_dp_display_get_panel(dp_display);
>> +	if (!mst_connector->dp_panel) {
>> +		DRM_ERROR("failed to get dp_panel for connector\n");
>> +		drm_modeset_unlock_all(dev);
>> +		return NULL;
>> +	}
>> +
>> +	mst_connector->dp_panel->connector = &mst_connector->connector;
>> +	mst_connector->msm_dp = dp_display;
>> +	connector = &mst_connector->connector;
>> +	drm_connector_helper_add(&mst_connector->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(&mst_connector->connector,
>> +						  dp_mst->mst_bridge[i].encoder);
>> +		if (rc) {
>> +			DRM_ERROR("failed to attach encoder to connector, %d\n", rc);
>> +			drm_modeset_unlock_all(dev);
>> +			return NULL;
>> +		}
>> +	}
>> +
>> +	mst_connector->mst_port = port;
>> +	drm_dp_mst_get_port_malloc(mst_connector->mst_port);
>> +
>> +	drm_object_attach_property(&mst_connector->connector.base,
>> +				   dev->mode_config.path_property, 0);
>> +	drm_object_attach_property(&mst_connector->connector.base,
>> +				   dev->mode_config.tile_property, 0);
> 
> subconnector? Or do we report the subconnector only for the main DP
> port?
> 
>> +
>> +	drm_modeset_unlock_all(dev);
>> +
>> +	drm_dbg_dp(dp_display->drm_dev, "add mst connector id:%d\n",
> 
> Each time I see "mst" in the text message, I feel a bit sad. It's MST
> (or SST).
> 
Got it .. thanks, will fix all the typo...
>> +		   mst_connector->connector.base.id);
>> +
>> +	return &mst_connector->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;
>> +
>> +	msm_dp_mst->mst_bridge = drmm_kzalloc(dev, max_streams * sizeof(struct msm_dp_mst_bridge),
>> +					      GFP_KERNEL);
>> +
>> +	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 cdde59da7bb937ad67fa818a728082f8fd43a3a6..7b08346e0bb9189009c02745c243722c01d79493 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.h
>> @@ -75,6 +75,7 @@ struct msm_dp_mst {
>>   	struct drm_dp_mst_topology_mgr mst_mgr;
>>   	struct msm_dp_mst_bridge *mst_bridge;
>>   	struct msm_dp *msm_dp;
>> +	struct drm_dp_aux *dp_aux;
>>   	u32 max_streams;
>>   	struct mutex mst_lock;
>>   };
>> @@ -97,4 +98,6 @@ struct msm_dp_mst_connector {
>>   		container_of((x), struct msm_dp_mst_connector, 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	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets
  2025-06-10  8:30     ` Dmitry Baryshkov
@ 2025-06-11 12:08       ` Yongxing Mou
  2025-06-11 14:35         ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-11 12:08 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/10 16:30, Dmitry Baryshkov wrote:
> On Tue, Jun 10, 2025 at 12:47:00PM +0800, Yongxing Mou wrote:
>>
>>
>> On 2025/6/9 20:36, Dmitry Baryshkov wrote:
>>> On Mon, Jun 09, 2025 at 08:21:19PM +0800, Yongxing Mou wrote:
>>>> 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.
>>>
>>> Which means that you didn't validate the MST interaction with the USB-C
>>> stack (there is a significant difference in the way HPD event is handled
>>> in the Linux kernel).
>>>
>> Yes. this patch series not test with USB-DP. Actually, our initial plan was
>> 2x2 MST on SA8775P and 4 MST on ACS8300. All of them are native DP
>> interface, not USB-DP. So can we only support SA8775P/QCS8300 in this
>> series. We don't plan to support other platform in this patch series.
> 
> I'm sorry, it doesn't work this way. This is not a product kernel,
> limited to a selected set of platforms. So, you have to hook MST support
> in both paths. The series is not going to be merged tomorrow, so you
> have enough time to validate MST on the platforms providing DP over
> USB-C.
> 
Would it be okay to implement USB-C DP MST in a separate patch series later?
>>>> 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/seriedds/142010/ (v2 to fix up HPD)
>>>
>>> This series has serious concerns and most likely will not be merged. Not
>>> to mention that the URL is invalid.
>>>
>> Got it. Sorry for the typo in the URL. So should we drop this patch series
>> or wait the state machine rework from Jessica zhang ?
> 
> Please work with Jessica. As I wrote, I do not intend to land the
> mentioned series.
> 
>>>>
>>>> Bindings for the pixel clock for additional stream is available at :
>>>>
>>>> [2] : https://patchwork.freedesktop.org/series/142016/
>>>
>>> This series needs another revision.
>>>
>> Got it.
>>> Not to mention that I plan to land [3] this cycle
>>>
>>> [3] http://lore.kernel.org/dri-devel/20250518-fd-dp-audio-fixup-v6-0-2f0ec3ec000d@oss.qualcomm.com
>>>
>>>
>> Got it. will rebase on this patch series.
>>>> 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.
>>>
>>> Validation should be using IGT for testing. Please ensure that there are
>>> no regressions.
>>>
>>>>
>>>> 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 <quic_yongmou@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 (35):
>>>>         drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid
>>>>         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: move the pixel clock control to its own API
>>>>         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 the max_streams for dp controller
>>>>         drm/msm/dp: introduce stream_id for each DP panel
>>>>         drm/msm/dp: add support for programming p1/p2/p3 register block
>>>>         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 program mst support in mainlink
>>>>         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 to send vcpf packets in dp controller
>>>>         drm/msm/dp: always program MST_FIFO_CONSTANT_FILL for MST
>>>>         drm/msm/dp: abstract out the dp_display stream helpers to accept a panel
>>>>         drm/msm/dp: move link related operations to dp_display_unprepare()
>>>>         drm/msm/dp: replace power_on with active_stream_cnt for dp_display
>>>>         drm/msm/dp: make the SST bridge disconnected when mst is active
>>>>         drm/msm/dp: add an API to initialize MST on sink side
>>>>         drm/msm/dp: skip reading the EDID for MST cases
>>>>         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 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: mark ST_DISCONNECTED only if all streams are disabled
>>>>         drm/msm/dp: fix the intf_type of MST interfaces
>>>>
>>>> Yongxing Mou (3):
>>>>         drm/msm/dp: Add catalog support for 3rd/4th stream MST
>>>>         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_4_sa8775p.h    |    8 +-
>>>>    drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c        |   21 +-
>>>>    drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h        |    2 +
>>>>    drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c            |   72 +-
>>>>    drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h            |    2 +-
>>>>    drivers/gpu/drm/msm/dp/dp_audio.c                  |    2 +-
>>>>    drivers/gpu/drm/msm/dp/dp_catalog.c                |  558 ++++++++--
>>>>    drivers/gpu/drm/msm/dp/dp_catalog.h                |   64 +-
>>>>    drivers/gpu/drm/msm/dp/dp_ctrl.c                   |  474 ++++++---
>>>>    drivers/gpu/drm/msm/dp/dp_ctrl.h                   |   22 +-
>>>>    drivers/gpu/drm/msm/dp/dp_display.c                |  510 +++++++---
>>>>    drivers/gpu/drm/msm/dp/dp_display.h                |   33 +-
>>>>    drivers/gpu/drm/msm/dp/dp_drm.c                    |   53 +-
>>>>    drivers/gpu/drm/msm/dp/dp_drm.h                    |   12 -
>>>>    drivers/gpu/drm/msm/dp/dp_mst_drm.c                | 1065 ++++++++++++++++++++
>>>>    drivers/gpu/drm/msm/dp/dp_mst_drm.h                |  106 ++
>>>>    drivers/gpu/drm/msm/dp/dp_panel.c                  |   66 +-
>>>>    drivers/gpu/drm/msm/dp/dp_panel.h                  |   10 +-
>>>>    drivers/gpu/drm/msm/dp/dp_reg.h                    |   46 +-
>>>>    drivers/gpu/drm/msm/msm_atomic.c                   |    3 +
>>>>    drivers/gpu/drm/msm/msm_drv.h                      |   19 +
>>>>    drivers/gpu/drm/msm/msm_kms.c                      |    2 +
>>>>    23 files changed, 2725 insertions(+), 428 deletions(-)
>>>> ---
>>>> base-commit: 475c850a7fdd0915b856173186d5922899d65686
>>>> change-id: 20250609-msm-dp-mst-cddc2f61daee
>>>> prerequisite-message-id: <20250529-hpd_display_off-v1-0-ce33bac2987c@oss.qualcomm.com>
>>>> prerequisite-patch-id: a1f426b99b4a99d63daa9902cde9ee38ae1128d1
>>>> prerequisite-patch-id: ae9e0a0db8edd05da06f9673e9de56761654ed3c
>>>> prerequisite-patch-id: 7cb84491c6c3cf73480343218c543d090f8cb5e2
>>>> prerequisite-patch-id: f32638e79dd498db2075735392e85729b1b691fc
>>>> prerequisite-message-id: <20250530-dp_mst_bindings-v2-0-f925464d32a8@oss.qualcomm.com>
>>>> prerequisite-patch-id: e505c21f653c8e18ce83cad1fc787c13a6c8ed12
>>>> prerequisite-patch-id: cfdd5c37d38b2a4f1386af4021ba3920c6d8dcf8
>>>> prerequisite-patch-id: f4abdddcb90c8203044395f4768d794214fe3225
>>>> prerequisite-patch-id: 45013dfaf34856422b7b6b3d2ee42d81a917177b
>>>> prerequisite-patch-id: 2f35bedb0410bead1b66cbfaf51984fc7016828f
>>>>
>>>> Best regards,
>>>> -- 
>>>> Yongxing Mou <quic_yongmou@quicinc.com>
>>>>
>>>
>>


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 28/38] drm/msm/dp: add dp_mst_drm to manage DP MST bridge operations
  2025-06-11 11:39     ` Yongxing Mou
@ 2025-06-11 14:27       ` Dmitry Baryshkov
  0 siblings, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-11 14:27 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Wed, Jun 11, 2025 at 07:39:24PM +0800, Yongxing Mou wrote:
> 
> 
> On 2025/6/9 23:57, Dmitry Baryshkov wrote:
> > On Mon, Jun 09, 2025 at 08:21:47PM +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.
> > 
> > I don't see this patch being improved from v1. Please follow the
> > feedback or provide a _good_ reason to ignore it.
> > 
> Sorry for that, will explain one by one later..
> > > 
> > > Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> > > Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.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 | 501 ++++++++++++++++++++++++++++++++++++
> > >   drivers/gpu/drm/msm/dp/dp_mst_drm.h | 100 +++++++
> > >   4 files changed, 606 insertions(+), 1 deletion(-)
> > > 
> > > diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> > > index 7a2ada6e2d74a902879e4f12a78ed475e5209ec2..d317e5ecda28dfd708ccdc5d3d27d4cf0b78b9d5 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 85eaa55fdcb7d9d8713849ec64a2cc9b08924425..9ca045ed2b4f1a9bc3254b733d1ce5332ebaba96 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
> > > @@ -24,6 +25,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..a3ea34ae63511db0ac920cbeebe30c4e2320b8c4
> > > --- /dev/null
> > > +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> > > @@ -0,0 +1,501 @@
> > > +// 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"
> > > +
> > > +static struct drm_private_state *msm_dp_mst_duplicate_bridge_state(struct drm_private_obj *obj)
> > > +{
> > > +	struct msm_dp_mst_bridge_state *state;
> > > +
> > > +	state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
> > > +	if (!state)
> > > +		return NULL;
> > > +
> > > +	__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
> > > +
> > > +	return &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 *priv_state =
> > > +		to_msm_dp_mst_bridge_priv_state(state);
> > > +
> > > +	kfree(priv_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_dp_mst_port *port)
> > 
> > As I wrote:
> > 
> > drm_dp_atomic_find_time_slots() should be called from atomic_check(),
> > this function must be dropped.
> > 
> This func not call drm_dp_atomic_find_time_slots().
> drm_dp_atomic_find_time_slots() called in msm_dp_mst_compute_config in the
> previous one patch. msm_dp_mst_compute_config is in atomic_check().

I'm sorry for not being explicit enough. It copies data from the state
into the msm_dp_bridge. There should be no need to do that. Use the
payload / MST state directly.

> > > +{
> > > +	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;
> > > +	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) {
> > > +			/*
> > > +			 * When a payload was removed make sure to move any payloads after it
> > > +			 * to the left so all payloads are aligned to the left.
> > > +			 */
> > 
> > :-(
> > 
> Emm,  will get back to this part later.
> > > +			if (payload->vc_start_slot < 0) {
> > > +				// cache the payload
> > > +				prev_start = msm_dp_bridge->start_slot;
> > > +				prev_slots = msm_dp_bridge->num_slots;
> > > +				msm_dp_bridge->pbn = 0;
> > > +				msm_dp_bridge->start_slot = 1;
> > > +				msm_dp_bridge->num_slots = 0;
> > > +				msm_dp_bridge->vcpi = 0;
> > > +			} else { //add payload
> > > +				msm_dp_bridge->pbn = payload->pbn;
> > > +				msm_dp_bridge->start_slot = payload->vc_start_slot;
> > > +				msm_dp_bridge->num_slots = payload->time_slots;
> > > +				msm_dp_bridge->vcpi = payload->vcpi;
> > > +			}
> > > +		}
> > > +	}
> > > +
> > > +	// Now commit all the updated payloads
> > > +	for (i = 0; i < mst->max_streams; i++) {
> > > +		msm_dp_bridge = &mst->mst_bridge[i];
> > > +
> > > +		//Shift payloads to the left if there was a removed payload.
> > > +		if (payload->vc_start_slot < 0 && msm_dp_bridge->start_slot > prev_start)
> > > +			msm_dp_bridge->start_slot -= prev_slots;
> > > +
> > > +		msm_dp_display_set_stream_info(mst->msm_dp, msm_dp_bridge->msm_dp_panel,
> > > +					       msm_dp_bridge->id, msm_dp_bridge->start_slot,
> > > +					       msm_dp_bridge->num_slots,
> > > +					       msm_dp_bridge->pbn, msm_dp_bridge->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), msm_dp_bridge->vcpi,
> > > +			   msm_dp_bridge->start_slot,
> > > +			   msm_dp_bridge->num_slots, msm_dp_bridge->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 *msm_dp = dp_bridge->display;
> > > +	struct msm_dp_mst *mst = msm_dp->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(msm_dp->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, 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 *msm_dp = dp_bridge->display;
> > > +	struct msm_dp_mst *mst = msm_dp->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(msm_dp->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 *msm_dp = dp_bridge->display;
> > > +	struct msm_dp_mst *mst = msm_dp->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, port);
> > > +
> > > +	drm_dbg_dp(msm_dp->drm_dev, "mst bridge [%d] _pre disable part-1 complete\n",
> > > +		   dp_bridge->id);
> > > +}
> > > +
> > > +static void _msm_dp_mst_bridge_pre_disable_part2(struct msm_dp_mst_bridge *dp_bridge)
> > > +{
> > > +	struct msm_dp *msm_dp = dp_bridge->display;
> > > +	struct msm_dp_mst *mst = msm_dp->msm_dp_mst;
> > > +
> > > +	drm_dp_check_act_status(&mst->mst_mgr);
> > > +
> > > +	drm_dbg_dp(msm_dp->drm_dev, "mst bridge [%d] _pre disable part-2 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;
> > > +	struct msm_dp_mst_bridge_state *msm_dp_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);
> > > +	msm_dp_bridge_state = to_msm_dp_mst_bridge_state(bridge);
> > > +	dp = bridge->display;
> > > +	dp_mst = dp->msm_dp_mst;
> > > +
> > > +	/* to cover cases of bridge_disable/bridge_enable without modeset */
> > > +	bridge->connector = msm_dp_bridge_state->connector;
> > > +	bridge->msm_dp_panel = msm_dp_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);
> > > +
> > > +	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);
> > > +		mutex_unlock(&dp_mst->mst_lock);
> > > +		return;
> > > +	}
> > > +
> > > +	msm_dp_display_enable_helper(dp, bridge->msm_dp_panel);
> > > +
> > > +	_msm_dp_mst_bridge_pre_enable_part2(bridge, state);
> > > +
> > > +	mutex_unlock(&dp_mst->mst_lock);
> > > +
> > > +	drm_dbg_dp(dp->drm_dev, "conn:%d mode:%s fps:%d vcpi:%d slots:%d to %d\n",
> > > +		   DP_MST_CONN_ID(bridge), bridge->drm_mode.name,
> > > +		   drm_mode_vrefresh(&bridge->drm_mode),
> > > +		   bridge->vcpi, bridge->start_slot,
> > > +		   bridge->start_slot + bridge->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;
> > > +	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 = bridge->display;
> > > +	mst = dp->msm_dp_mst;
> > > +
> > > +	mutex_lock(&mst->mst_lock);
> > > +
> > > +	_msm_dp_mst_bridge_pre_disable_part1(bridge, state);
> > > +
> > > +	msm_dp_display_disable_helper(dp, bridge->msm_dp_panel);
> > > +
> > > +	_msm_dp_mst_bridge_pre_disable_part2(bridge);
> > > +
> > > +	mutex_unlock(&mst->mst_lock);
> > > +
> > > +	drm_dbg_dp(dp->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;
> > > +	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 = bridge->display;
> > > +	mst = dp->msm_dp_mst;
> > > +
> > > +	mutex_lock(&mst->mst_lock);
> > > +
> > > +	msm_dp_display_atomic_post_disable_helper(dp, bridge->msm_dp_panel);
> > > +
> > > +	if (!dp->mst_active)
> > > +		msm_dp_display_unprepare(dp);
> > > +
> > > +	bridge->connector = NULL;
> > > +	bridge->msm_dp_panel =  NULL;
> > > +
> > > +	mutex_unlock(&mst->mst_lock);
> > > +
> > > +	drm_dbg_dp(dp->drm_dev, "mst bridge:%d conn:%d post disable complete\n",
> > > +		   bridge->id, conn);
> > > +}
> > > +
> > > +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 *dp_bridge_state;
> > > +	struct msm_dp *dp;
> > > +	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);
> > > +
> > > +	dp_bridge_state = to_msm_dp_mst_bridge_state(bridge);
> > > +	bridge->connector = dp_bridge_state->connector;
> > > +	bridge->msm_dp_panel = dp_bridge_state->msm_dp_panel;
> > > +
> > > +	msm_dp_panel = bridge->msm_dp_panel;
> > > +	dp = bridge->display;
> > > +
> > > +	memcpy(&bridge->drm_mode, adjusted_mode, sizeof(bridge->drm_mode));
> > > +	msm_dp_display_mode_set_helper(dp, 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->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,
> > > +	.mode_set     = msm_dp_mst_bridge_mode_set,
> > > +};
> > > +

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST
  2025-06-11 12:06     ` Yongxing Mou
@ 2025-06-11 14:31       ` Dmitry Baryshkov
  2025-06-16 14:09         ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-11 14:31 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Wed, Jun 11, 2025 at 08:06:28PM +0800, Yongxing Mou wrote:
> 
> 
> On 2025/6/9 23:44, Dmitry Baryshkov wrote:
> > On Mon, Jun 09, 2025 at 08:21:48PM +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.
> > > 
> > > Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> > > Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> > > ---
> > >   drivers/gpu/drm/msm/dp/dp_mst_drm.c | 515 ++++++++++++++++++++++++++++++++++++
> > >   drivers/gpu/drm/msm/dp/dp_mst_drm.h |   3 +
> > >   2 files changed, 518 insertions(+)
> > 
> > It generally feels liks 80% of this patch is a generic code. Please
> > extract generic DP MST connector and push it under drm/display. Other DP
> > MST drivers should be able to use it.
> > 
> > > 
> > > diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> > > index a3ea34ae63511db0ac920cbeebe30c4e2320b8c4..489fa46aa518ff1cc5f4769b2153fc5153c4cb41 100644
> > > --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> > > +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> > > @@ -25,8 +25,12 @@
> > >    * OF THIS SOFTWARE.
> > >    */
> > > +#include <drm/drm_edid.h>
> > > +#include <drm/drm_managed.h>
> > >   #include "dp_mst_drm.h"
> > > +#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 *state;
> > > @@ -79,6 +83,61 @@ static int msm_dp_mst_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, int p
> > >   	return num_slots;
> > >   }
> > > +static int msm_dp_mst_get_mst_pbn_div(struct msm_dp_panel *msm_dp_panel)
> > > +{
> > > +	struct msm_dp_link_info *link_info;
> > > +
> > > +	link_info = &msm_dp_panel->link_info;
> > > +
> > > +	return link_info->rate * link_info->num_lanes / 54000;
> > > +}
> > > +
> > > +static int msm_dp_mst_compute_config(struct drm_atomic_state *state,
> > > +				      struct msm_dp_mst *mst, struct drm_connector *connector,
> > > +				      struct drm_display_mode *mode)
> > > +{
> > > +	int slots = 0, pbn;
> > > +	struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(connector);
> > > +	int rc = 0;
> > > +	struct drm_dp_mst_topology_state *mst_state;
> > > +	int pbn_div;
> > > +	struct msm_dp *dp_display = mst->msm_dp;
> > > +	u32 bpp;
> > > +
> > > +	bpp = connector->display_info.bpc * 3;
> > > +
> > > +	pbn = drm_dp_calc_pbn_mode(mode->clock, bpp << 4);
> > 
> > Is this going to change if DSC is in place? Will it bring fractional BPP
> > here?
> > 
> Actually, in this patch series, MST not support DSC. So we just don't
> consider this scenario.

But you still can answer the question.


[...]

> > > +
> > > +	return msm_dp_display_mode_valid(dp_display, &dp_display->connector->display_info, mode);
> > > +}
> > > +
> > > +static struct drm_encoder *
> > > +msm_dp_mst_atomic_best_encoder(struct drm_connector *connector, struct drm_atomic_state *state)
> > 
> > Do we need this callback? Don't we have a fixed relationship between
> > connectors and encoders?

This was left unanswered.

> > 
> > > +{
> > > +	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 *bridge_state;
> > > +	u32 i;
> > > +	struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(state,
> > > +										    connector);
> > > +
> > 

[...]

> > > +	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);
> > 
> > This really looks like this should be a bridge's callback.

And this one

> > 
> > > +		if (WARN_ON(!drm_bridge)) {
> > > +			rc = -EINVAL;
> > > +			goto end;
> > > +		}
> > > +		bridge = to_msm_dp_mst_bridge(drm_bridge);
> > > +
> > > +		bridge_state = msm_dp_mst_br_priv_state(state, bridge);
> > > +		if (IS_ERR(bridge_state)) {
> > > +			rc = PTR_ERR(bridge_state);
> > > +			goto end;
> > > +		}
> > > +
> > > +		if (WARN_ON(bridge_state->connector != connector)) {
> > > +			rc = -EINVAL;
> > > +			goto end;
> > > +		}
> > > +
> > > +		slots = bridge_state->num_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;
> > > +			}
> > > +			vcpi_released = true;
> > > +		}
> > > +
> > > +		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
> > > +			 */
> > > +			bridge_state->num_slots = 0;
> > > +			bridge_state->connector = NULL;
> > > +			bridge_state->msm_dp_panel = NULL;
> > > +
> > > +			drm_dbg_dp(dp_display->drm_dev, "clear best encoder: %d\n", bridge->id);
> > > +		}
> > > +	}
> > 
> > This looks like there are several functions fused together. Please
> > unfuse those into small and neat code blocks.

And this :-D

> > 
> > > +
> > > +mode_set:
> > > +	if (!new_conn_state->crtc)
> > > +		goto end;
> > > +
> > > +	crtc_state = drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
> > > +
> > > +	if (drm_atomic_crtc_needs_modeset(crtc_state) && crtc_state->active) {
> > 
> > Use of crtc_state->active doesn't look correct.


...

> > 
> > > +		if (WARN_ON(!new_conn_state->best_encoder)) {
> > > +			rc = -EINVAL;
> > > +			goto end;
> > > +		}
> > > +
> > > +		drm_bridge = drm_bridge_chain_get_first_bridge(new_conn_state->best_encoder);
> > > +		if (WARN_ON(!drm_bridge)) {
> > > +			rc = -EINVAL;
> > > +			goto end;
> > > +		}
> > > +		bridge = to_msm_dp_mst_bridge(drm_bridge);
> > > +
> > > +		bridge_state = msm_dp_mst_br_priv_state(state, bridge);
> > > +		if (IS_ERR(bridge_state)) {
> > > +			rc = PTR_ERR(bridge_state);
> > > +			goto end;
> > > +		}
> > > +
> > > +		if (WARN_ON(bridge_state->connector != connector)) {
> > > +			rc = -EINVAL;
> > > +			goto end;
> > > +		}
> > 
> > Can all of this actually happen?

...

> > 
> > > +
> > > +		/*
> > > +		 * check if vcpi slots are trying to get allocated in same phase
> > > +		 * as deallocation. If so, go to end to avoid allocation.
> > > +		 */
> > > +		if (vcpi_released) {
> > > +			drm_dbg_dp(dp_display->drm_dev,
> > > +				   "skipping allocation since vcpi was released in the same state\n");
> > > +			goto end;
> > > +		}
> > > +
> > > +		if (WARN_ON(bridge_state->num_slots)) {
> > > +			rc = -EINVAL;
> > > +			goto end;
> > > +		}
> > > +
> > > +		slots = msm_dp_mst_compute_config(state, mst, connector, &crtc_state->mode);
> > > +		if (slots < 0) {
> > > +			rc = slots;
> > > +			goto end;
> > > +		}
> > > +
> > > +		bridge_state->num_slots = slots;
> > > +	}
> > > +
> > > +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);
> > > +}
> > > +
> > > +/* 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_connector;
> > > +	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_connector = devm_kzalloc(dev->dev, sizeof(*mst_connector), GFP_KERNEL);
> > 
> > This shows that somebody doesn't understand the reason for drmm and the
> > difference between devm and drmm and the lifetime of the objects. Do you
> > see two issues in this line?
> > 
> > Let me help you. Please use normal (non-managed) memory here. It is the
> > only correct way to allocate memory for MST connectors.
> > 
> Thanks for point it.. it will lead to mem leak.. so we need to use
> kzalloc()...

- Did you understand why devm is unsuitable here?
- Why drmm is also unsutable?
- What is the implication of using kzalloc() here?

> > > +
> > > +	drm_modeset_lock_all(dev);
> > > +
> > > +	rc = drm_connector_dynamic_init(dev, &mst_connector->connector,
> > > +					&msm_dp_drm_mst_connector_funcs,
> > > +					DRM_MODE_CONNECTOR_DisplayPort, NULL);
> > > +	if (rc) {
> > > +		drm_modeset_unlock_all(dev);
> > > +		return NULL;
> > > +	}
> > > +
> > > +	mst_connector->dp_panel = msm_dp_display_get_panel(dp_display);
> > > +	if (!mst_connector->dp_panel) {
> > > +		DRM_ERROR("failed to get dp_panel for connector\n");
> > > +		drm_modeset_unlock_all(dev);
> > > +		return NULL;
> > > +	}
> > > +
> > > +	mst_connector->dp_panel->connector = &mst_connector->connector;
> > > +	mst_connector->msm_dp = dp_display;
> > > +	connector = &mst_connector->connector;
> > > +	drm_connector_helper_add(&mst_connector->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(&mst_connector->connector,
> > > +						  dp_mst->mst_bridge[i].encoder);
> > > +		if (rc) {
> > > +			DRM_ERROR("failed to attach encoder to connector, %d\n", rc);
> > > +			drm_modeset_unlock_all(dev);
> > > +			return NULL;
> > > +		}
> > > +	}
> > > +
> > > +	mst_connector->mst_port = port;
> > > +	drm_dp_mst_get_port_malloc(mst_connector->mst_port);
> > > +
> > > +	drm_object_attach_property(&mst_connector->connector.base,
> > > +				   dev->mode_config.path_property, 0);
> > > +	drm_object_attach_property(&mst_connector->connector.base,
> > > +				   dev->mode_config.tile_property, 0);
> > 
> > subconnector? Or do we report the subconnector only for the main DP
> > port?


...

> > 
> > > +
> > > +	drm_modeset_unlock_all(dev);
> > > +
-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets
  2025-06-11 12:08       ` Yongxing Mou
@ 2025-06-11 14:35         ` Dmitry Baryshkov
  0 siblings, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-11 14:35 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Wed, Jun 11, 2025 at 08:08:49PM +0800, Yongxing Mou wrote:
> 
> 
> On 2025/6/10 16:30, Dmitry Baryshkov wrote:
> > On Tue, Jun 10, 2025 at 12:47:00PM +0800, Yongxing Mou wrote:
> > > 
> > > 
> > > On 2025/6/9 20:36, Dmitry Baryshkov wrote:
> > > > On Mon, Jun 09, 2025 at 08:21:19PM +0800, Yongxing Mou wrote:
> > > > > 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.
> > > > 
> > > > Which means that you didn't validate the MST interaction with the USB-C
> > > > stack (there is a significant difference in the way HPD event is handled
> > > > in the Linux kernel).
> > > > 
> > > Yes. this patch series not test with USB-DP. Actually, our initial plan was
> > > 2x2 MST on SA8775P and 4 MST on ACS8300. All of them are native DP
> > > interface, not USB-DP. So can we only support SA8775P/QCS8300 in this
> > > series. We don't plan to support other platform in this patch series.
> > 
> > I'm sorry, it doesn't work this way. This is not a product kernel,
> > limited to a selected set of platforms. So, you have to hook MST support
> > in both paths. The series is not going to be merged tomorrow, so you
> > have enough time to validate MST on the platforms providing DP over
> > USB-C.
> > 
> Would it be okay to implement USB-C DP MST in a separate patch series later?

What will happen if I plug in MST hub after applying this series?
Also... There were several items beforehand, which were left out 'to be
fixed later'. Up to now, it didn't happen. So... I'm really tempted to
say 'No' here.

Also, as I wrote, HPD rework would need to go first (and it wasn't even
posted). One of the reasons why I demand that is to be able to
streamline HPD handling and use the same code path for both USB-DP and
DP-connector.

> > > > > 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/seriedds/142010/ (v2 to fix up HPD)
> > > > 
> > > > This series has serious concerns and most likely will not be merged. Not
> > > > to mention that the URL is invalid.
> > > > 
> > > Got it. Sorry for the typo in the URL. So should we drop this patch series
> > > or wait the state machine rework from Jessica zhang ?
> > 
> > Please work with Jessica. As I wrote, I do not intend to land the
> > mentioned series.
> > 
> > > > > 
> > > > > Bindings for the pixel clock for additional stream is available at :
> > > > > 
> > > > > [2] : https://patchwork.freedesktop.org/series/142016/
> > > > 
> > > > This series needs another revision.
> > > > 
> > > Got it.
> > > > Not to mention that I plan to land [3] this cycle
> > > > 
> > > > [3] http://lore.kernel.org/dri-devel/20250518-fd-dp-audio-fixup-v6-0-2f0ec3ec000d@oss.qualcomm.com
> > > > 
> > > > 
> > > Got it. will rebase on this patch series.
> > > > > 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.
> > > > 
> > > > Validation should be using IGT for testing. Please ensure that there are
> > > > no regressions.

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST
  2025-06-09 15:51   ` Dmitry Baryshkov
@ 2025-06-16 12:43     ` Yongxing Mou
  2025-06-16 13:48       ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-16 12:43 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/9 23:51, Dmitry Baryshkov wrote:
> On Mon, Jun 09, 2025 at 08:21:48PM +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.
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>> ---
>>   drivers/gpu/drm/msm/dp/dp_mst_drm.c | 515 ++++++++++++++++++++++++++++++++++++
>>   drivers/gpu/drm/msm/dp/dp_mst_drm.h |   3 +
>>   2 files changed, 518 insertions(+)
> 
>> +
>> +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;
>> +	u16 full_pbn, required_pbn;
>> +	int available_slots, required_slots;
>> +	struct msm_dp_mst_bridge_state *dp_bridge_state;
>> +	int i, slots_in_use = 0, active_enc_cnt = 0;
>> +	const u32 tot_slots = 63;
>> +
>> +	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++) {
>> +		dp_bridge_state = to_msm_dp_mst_bridge_state(&mst->mst_bridge[i]);
>> +		if (dp_bridge_state->connector &&
>> +		    dp_bridge_state->connector != connector) {
>> +			active_enc_cnt++;
>> +			slots_in_use += dp_bridge_state->num_slots;
>> +		}
>> +	}
>> +
>> +	if (active_enc_cnt < DP_STREAM_MAX) {
>> +		full_pbn = mst_port->full_pbn;
>> +		available_slots = tot_slots - slots_in_use;
>> +	} 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);
>> +
>> +	required_slots = msm_dp_mst_find_vcpi_slots(&mst->mst_mgr, required_pbn);
>> +
>> +	if (required_pbn > full_pbn || required_slots > available_slots) {
>> +		drm_dbg_dp(dp_display->drm_dev,
>> +			   "mode:%s not supported. pbn %d vs %d slots %d vs %d\n",
>> +			   mode->name, required_pbn, full_pbn,
>> +			   required_slots, available_slots);
>> +		return MODE_BAD;
>> +	}
> 
> I almost missed this. Could you please point me, do other drivers
> perform mode_valid() check based on the current slots available or not?
> Could you please point me to the relevant code in other drivers? Because
> it doesn't look correct to me. The mode on the screen remains valid no
> matter if I plug or unplug other devices. The atomic_check() should fail
> if we don't have enough resources (which includes slots).
> 
Currently, I haven't found other drivers checking available slots during 
mode_valid(). Intel will check the PBN in here. This condition can help 
us in the following case:

Assume two downstream devices both support 4K 60Hz 10-bit. In MST mode, 
when the first device occupies the 4Kx60Hzx10bit mode, the remaining 
bandwidth is insufficient to support the same mode for the second device.

If we check the slots in mode_valid(), the second device will reject the 
4Kx60Hzx10bit mode but accept 4Kx30Hzx10bit. However, if the check is 
done in atomic_check(), the second device will display a black screen 
(because 4Kx60Hzx10bit is considered valid in mode_valid() but failed in 
atomic_check()).
>> +
>> +	return msm_dp_display_mode_valid(dp_display, &dp_display->connector->display_info, mode);
>> +}
>> +
> 


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST
  2025-06-16 12:43     ` Yongxing Mou
@ 2025-06-16 13:48       ` Dmitry Baryshkov
  2025-06-17  7:52         ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-16 13:48 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Mon, Jun 16, 2025 at 08:43:40PM +0800, Yongxing Mou wrote:
> 
> 
> On 2025/6/9 23:51, Dmitry Baryshkov wrote:
> > On Mon, Jun 09, 2025 at 08:21:48PM +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.
> > > 
> > > Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> > > Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> > > ---
> > >   drivers/gpu/drm/msm/dp/dp_mst_drm.c | 515 ++++++++++++++++++++++++++++++++++++
> > >   drivers/gpu/drm/msm/dp/dp_mst_drm.h |   3 +
> > >   2 files changed, 518 insertions(+)
> > 
> > > +
> > > +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;
> > > +	u16 full_pbn, required_pbn;
> > > +	int available_slots, required_slots;
> > > +	struct msm_dp_mst_bridge_state *dp_bridge_state;
> > > +	int i, slots_in_use = 0, active_enc_cnt = 0;
> > > +	const u32 tot_slots = 63;
> > > +
> > > +	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++) {
> > > +		dp_bridge_state = to_msm_dp_mst_bridge_state(&mst->mst_bridge[i]);
> > > +		if (dp_bridge_state->connector &&
> > > +		    dp_bridge_state->connector != connector) {
> > > +			active_enc_cnt++;
> > > +			slots_in_use += dp_bridge_state->num_slots;
> > > +		}
> > > +	}
> > > +
> > > +	if (active_enc_cnt < DP_STREAM_MAX) {
> > > +		full_pbn = mst_port->full_pbn;
> > > +		available_slots = tot_slots - slots_in_use;
> > > +	} 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);
> > > +
> > > +	required_slots = msm_dp_mst_find_vcpi_slots(&mst->mst_mgr, required_pbn);
> > > +
> > > +	if (required_pbn > full_pbn || required_slots > available_slots) {
> > > +		drm_dbg_dp(dp_display->drm_dev,
> > > +			   "mode:%s not supported. pbn %d vs %d slots %d vs %d\n",
> > > +			   mode->name, required_pbn, full_pbn,
> > > +			   required_slots, available_slots);
> > > +		return MODE_BAD;
> > > +	}
> > 
> > I almost missed this. Could you please point me, do other drivers
> > perform mode_valid() check based on the current slots available or not?
> > Could you please point me to the relevant code in other drivers? Because
> > it doesn't look correct to me. The mode on the screen remains valid no
> > matter if I plug or unplug other devices. The atomic_check() should fail
> > if we don't have enough resources (which includes slots).
> > 
> Currently, I haven't found other drivers checking available slots during
> mode_valid(). Intel will check the PBN in here.

pointer? Also, what do AMD and nouveau do?

> This condition can help us
> in the following case:
> 
> Assume two downstream devices both support 4K 60Hz 10-bit. In MST mode, when
> the first device occupies the 4Kx60Hzx10bit mode, the remaining bandwidth is
> insufficient to support the same mode for the second device.
> 
> If we check the slots in mode_valid(), the second device will reject the
> 4Kx60Hzx10bit mode but accept 4Kx30Hzx10bit. However, if the check is done
> in atomic_check(), the second device will display a black screen (because
> 4Kx60Hzx10bit is considered valid in mode_valid() but failed in
> atomic_check()).

If we filter modes in mode_valid(), then consider the following
scenario: we plug monitor A, plug monitor B, then unplug monitor A. At
this point we only have monitor B, but it has all modes filtered when A
has been plugged. So, it is impossible to select 4k@60x10, even though
it is a perfectly valid mode now.

Also, with the check happening in the atomic_check() the user will not
get the black screen: the commit will get rejected, letting userspace to
lower the mode for the second monitor.

> > > +
> > > +	return msm_dp_display_mode_valid(dp_display, &dp_display->connector->display_info, mode);
> > > +}
> > > +
> > 
> 

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST
  2025-06-11 14:31       ` Dmitry Baryshkov
@ 2025-06-16 14:09         ` Yongxing Mou
  2025-06-16 14:47           ` Dmitry Baryshkov
       [not found]           ` <bd0fba5c-9e38-4a40-adf9-cc70fa2d0f57@oss.qualcomm.com>
  0 siblings, 2 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-16 14:09 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/11 22:31, Dmitry Baryshkov wrote:
> On Wed, Jun 11, 2025 at 08:06:28PM +0800, Yongxing Mou wrote:
>>
>>
>> On 2025/6/9 23:44, Dmitry Baryshkov wrote:
>>> On Mon, Jun 09, 2025 at 08:21:48PM +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.
>>>>
>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>>>> ---
>>>>    drivers/gpu/drm/msm/dp/dp_mst_drm.c | 515 ++++++++++++++++++++++++++++++++++++
>>>>    drivers/gpu/drm/msm/dp/dp_mst_drm.h |   3 +
>>>>    2 files changed, 518 insertions(+)
>>>
>>> It generally feels liks 80% of this patch is a generic code. Please
>>> extract generic DP MST connector and push it under drm/display. Other DP
>>> MST drivers should be able to use it.
>>>
>>>>
>>>> diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
>>>> index a3ea34ae63511db0ac920cbeebe30c4e2320b8c4..489fa46aa518ff1cc5f4769b2153fc5153c4cb41 100644
>>>> --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
>>>> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
>>>> @@ -25,8 +25,12 @@
>>>>     * OF THIS SOFTWARE.
>>>>     */
>>>> +#include <drm/drm_edid.h>
>>>> +#include <drm/drm_managed.h>
>>>>    #include "dp_mst_drm.h"
>>>> +#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 *state;
>>>> @@ -79,6 +83,61 @@ static int msm_dp_mst_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, int p
>>>>    	return num_slots;
>>>>    }
>>>> +static int msm_dp_mst_get_mst_pbn_div(struct msm_dp_panel *msm_dp_panel)
>>>> +{
>>>> +	struct msm_dp_link_info *link_info;
>>>> +
>>>> +	link_info = &msm_dp_panel->link_info;
>>>> +
>>>> +	return link_info->rate * link_info->num_lanes / 54000;
>>>> +}
>>>> +
>>>> +static int msm_dp_mst_compute_config(struct drm_atomic_state *state,
>>>> +				      struct msm_dp_mst *mst, struct drm_connector *connector,
>>>> +				      struct drm_display_mode *mode)
>>>> +{
>>>> +	int slots = 0, pbn;
>>>> +	struct msm_dp_mst_connector *mst_conn = to_msm_dp_mst_connector(connector);
>>>> +	int rc = 0;
>>>> +	struct drm_dp_mst_topology_state *mst_state;
>>>> +	int pbn_div;
>>>> +	struct msm_dp *dp_display = mst->msm_dp;
>>>> +	u32 bpp;
>>>> +
>>>> +	bpp = connector->display_info.bpc * 3;
>>>> +
>>>> +	pbn = drm_dp_calc_pbn_mode(mode->clock, bpp << 4);
>>>
>>> Is this going to change if DSC is in place? Will it bring fractional BPP
>>> here?
>>>
>> Actually, in this patch series, MST not support DSC. So we just don't
>> consider this scenario.
> 
> But you still can answer the question.
> 
> 
> [...]
> 
1.Emm, for my current understanding, if DSC is enabled, the BPP should 
change and recaculated.
Will it bring fractional BPP here?
 >>>I'm not entirely sure about this answer. I checked how other drivers 
call this function, and they all use bpp << 4, so can we assume that 
this way of calling it is valid?
>>>> +
>>>> +	return msm_dp_display_mode_valid(dp_display, &dp_display->connector->display_info, mode);
>>>> +}
>>>> +
>>>> +static struct drm_encoder *
>>>> +msm_dp_mst_atomic_best_encoder(struct drm_connector *connector, struct drm_atomic_state *state)
>>>
>>> Do we need this callback? Don't we have a fixed relationship between
>>> connectors and encoders?
> 
> This was left unanswered.
> 
Sorry, I didn't mean to skip any questions — I just planned to reply a 
bit later. Apologies for the confusion.
For this question, yes , we don't have the fixed relationship between 
them. Under the current codes, the Connector selects the available 
encoder and bridge in order from index 0 to 4 (up to max_streams) when 
the connector's status changes to 'connected'.
>>>
>>>> +{
>>>> +	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 *bridge_state;
>>>> +	u32 i;
>>>> +	struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(state,
>>>> +										    connector);
>>>> +
>>>
> 
> [...]
> 
>>>> +	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);
>>>
>>> This really looks like this should be a bridge's callback.
> 
> And this one
> 
Emm, the bridge does not implement atomic_check(). All MST-related 
checks (such as drm_dp_atomic_release_time_slots, 
drm_dp_mst_atomic_check, or others) are performed in the connector's 
atomic_check function. I believe this is because both num_slots and pbn 
are stored in the bridge, and we call this to get the drm_bridge..
>>>
>>>> +		if (WARN_ON(!drm_bridge)) {
>>>> +			rc = -EINVAL;
>>>> +			goto end;
>>>> +		}
>>>> +		bridge = to_msm_dp_mst_bridge(drm_bridge);
>>>> +
>>>> +		bridge_state = msm_dp_mst_br_priv_state(state, bridge);
>>>> +		if (IS_ERR(bridge_state)) {
>>>> +			rc = PTR_ERR(bridge_state);
>>>> +			goto end;
>>>> +		}
>>>> +
>>>> +		if (WARN_ON(bridge_state->connector != connector)) {
>>>> +			rc = -EINVAL;
>>>> +			goto end;
>>>> +		}
>>>> +
>>>> +		slots = bridge_state->num_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;
>>>> +			}
>>>> +			vcpi_released = true;
>>>> +		}
>>>> +
>>>> +		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
>>>> +			 */
>>>> +			bridge_state->num_slots = 0;
>>>> +			bridge_state->connector = NULL;
>>>> +			bridge_state->msm_dp_panel = NULL;
>>>> +
>>>> +			drm_dbg_dp(dp_display->drm_dev, "clear best encoder: %d\n", bridge->id);
>>>> +		}
>>>> +	}
>>>
>>> This looks like there are several functions fused together. Please
>>> unfuse those into small and neat code blocks.
> 
> And this :-D
> 
Got it.. this code only do one thing, check and try to release 
time_slots.. we can try to package it into small functions..
>>>
>>>> +
>>>> +mode_set:
>>>> +	if (!new_conn_state->crtc)
>>>> +		goto end;
>>>> +
>>>> +	crtc_state = drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
>>>> +
>>>> +	if (drm_atomic_crtc_needs_modeset(crtc_state) && crtc_state->active) {
>>>
>>> Use of crtc_state->active doesn't look correct.
> 
> 
> ...
> 
Sorry, I'm still not quite sure where the issue is. Could you please 
help point it out? Thanks~~
>>>
>>>> +		if (WARN_ON(!new_conn_state->best_encoder)) {
>>>> +			rc = -EINVAL;
>>>> +			goto end;
>>>> +		}
>>>> +
>>>> +		drm_bridge = drm_bridge_chain_get_first_bridge(new_conn_state->best_encoder);
>>>> +		if (WARN_ON(!drm_bridge)) {
>>>> +			rc = -EINVAL;
>>>> +			goto end;
>>>> +		}
>>>> +		bridge = to_msm_dp_mst_bridge(drm_bridge);
>>>> +
>>>> +		bridge_state = msm_dp_mst_br_priv_state(state, bridge);
>>>> +		if (IS_ERR(bridge_state)) {
>>>> +			rc = PTR_ERR(bridge_state);
>>>> +			goto end;
>>>> +		}
>>>> +
>>>> +		if (WARN_ON(bridge_state->connector != connector)) {
>>>> +			rc = -EINVAL;
>>>> +			goto end;
>>>> +		}
>>>
>>> Can all of this actually happen?
> 
> ...
> 
Actually not, I haven't encountered it yet. I'm not sure how to trigger 
it, but it might occur under race conditions? Or we just remove it 
untill some case it really happen..
>>>
>>>> +
>>>> +		/*
>>>> +		 * check if vcpi slots are trying to get allocated in same phase
>>>> +		 * as deallocation. If so, go to end to avoid allocation.
>>>> +		 */
>>>> +		if (vcpi_released) {
>>>> +			drm_dbg_dp(dp_display->drm_dev,
>>>> +				   "skipping allocation since vcpi was released in the same state\n");
>>>> +			goto end;
>>>> +		}
>>>> +
>>>> +		if (WARN_ON(bridge_state->num_slots)) {
>>>> +			rc = -EINVAL;
>>>> +			goto end;
>>>> +		}
>>>> +
>>>> +		slots = msm_dp_mst_compute_config(state, mst, connector, &crtc_state->mode);
>>>> +		if (slots < 0) {
>>>> +			rc = slots;
>>>> +			goto end;
>>>> +		}
>>>> +
>>>> +		bridge_state->num_slots = slots;
>>>> +	}
>>>> +
>>>> +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);
>>>> +}
>>>> +
>>>> +/* 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_connector;
>>>> +	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_connector = devm_kzalloc(dev->dev, sizeof(*mst_connector), GFP_KERNEL);
>>>
>>> This shows that somebody doesn't understand the reason for drmm and the
>>> difference between devm and drmm and the lifetime of the objects. Do you
>>> see two issues in this line?
>>>
>>> Let me help you. Please use normal (non-managed) memory here. It is the
>>> only correct way to allocate memory for MST connectors.
>>>
>> Thanks for point it.. it will lead to mem leak.. so we need to use
>> kzalloc()...
> 
> - Did you understand why devm is unsuitable here?
> - Why drmm is also unsutable?
> - What is the implication of using kzalloc() here?
> 
For my understanding, memory allocated with devm_kzalloc is released 
when the device is removed, while memory allocated with drmm_kzalloc is 
released when the DRM device is unregistered. I believe this is because 
the allocation and release of connectors happen during hotplug events, 
which have a different lifecycle from other devices. If we use 
kzalloc(), we would need to manually free the memory.
>>>> +
>>>> +	drm_modeset_lock_all(dev);
>>>> +
>>>> +	rc = drm_connector_dynamic_init(dev, &mst_connector->connector,
>>>> +					&msm_dp_drm_mst_connector_funcs,
>>>> +					DRM_MODE_CONNECTOR_DisplayPort, NULL);
>>>> +	if (rc) {
>>>> +		drm_modeset_unlock_all(dev);
>>>> +		return NULL;
>>>> +	}
>>>> +
>>>> +	mst_connector->dp_panel = msm_dp_display_get_panel(dp_display);
>>>> +	if (!mst_connector->dp_panel) {
>>>> +		DRM_ERROR("failed to get dp_panel for connector\n");
>>>> +		drm_modeset_unlock_all(dev);
>>>> +		return NULL;
>>>> +	}
>>>> +
>>>> +	mst_connector->dp_panel->connector = &mst_connector->connector;
>>>> +	mst_connector->msm_dp = dp_display;
>>>> +	connector = &mst_connector->connector;
>>>> +	drm_connector_helper_add(&mst_connector->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(&mst_connector->connector,
>>>> +						  dp_mst->mst_bridge[i].encoder);
>>>> +		if (rc) {
>>>> +			DRM_ERROR("failed to attach encoder to connector, %d\n", rc);
>>>> +			drm_modeset_unlock_all(dev);
>>>> +			return NULL;
>>>> +		}
>>>> +	}
>>>> +
>>>> +	mst_connector->mst_port = port;
>>>> +	drm_dp_mst_get_port_malloc(mst_connector->mst_port);
>>>> +
>>>> +	drm_object_attach_property(&mst_connector->connector.base,
>>>> +				   dev->mode_config.path_property, 0);
>>>> +	drm_object_attach_property(&mst_connector->connector.base,
>>>> +				   dev->mode_config.tile_property, 0);
>>>
>>> subconnector? Or do we report the subconnector only for the main DP
>>> port?
> 
> 
> ...
> 
Sorry, I'm not quite sure what 'subconnector' means in this context... 
Could you please help explain it a bit more? From what I’ve seen in 
other drivers, these two properties are registered for each MST connector.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/i915/display/intel_dp_mst.c?h=v6.16-rc2#n1618
>>>
>>>> +
>>>> +	drm_modeset_unlock_all(dev);
>>>> +


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST
  2025-06-16 14:09         ` Yongxing Mou
@ 2025-06-16 14:47           ` Dmitry Baryshkov
       [not found]           ` <bd0fba5c-9e38-4a40-adf9-cc70fa2d0f57@oss.qualcomm.com>
  1 sibling, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-16 14:47 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar


On 16/06/2025 17:09, Yongxing Mou wrote:
 >
 >
 > On 2025/6/11 22:31, Dmitry Baryshkov wrote:
 >> On Wed, Jun 11, 2025 at 08:06:28PM +0800, Yongxing Mou wrote:
 >>>
 >>>
 >>> On 2025/6/9 23:44, Dmitry Baryshkov wrote:
 >>>> On Mon, Jun 09, 2025 at 08:21:48PM +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.
 >>>>>
 >>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
 >>>>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
 >>>>> ---
 >>>>>    drivers/gpu/drm/msm/dp/dp_mst_drm.c | 515 
++++++++++++++++++++++ ++++++++++++++
 >>>>>    drivers/gpu/drm/msm/dp/dp_mst_drm.h |   3 +
 >>>>>    2 files changed, 518 insertions(+)
 >>>>
 >>>> It generally feels liks 80% of this patch is a generic code. Please
 >>>> extract generic DP MST connector and push it under drm/display. 
Other DP
 >>>> MST drivers should be able to use it.
 >>>>
 >>>>>
 >>>>> diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c 
b/drivers/gpu/drm/ msm/dp/dp_mst_drm.c
 >>>>> index 
a3ea34ae63511db0ac920cbeebe30c4e2320b8c4..489fa46aa518ff1cc5f4769b2153fc5153c4cb41 
100644
 >>>>> --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
 >>>>> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
 >>>>> @@ -25,8 +25,12 @@
 >>>>>     * OF THIS SOFTWARE.
 >>>>>     */
 >>>>> +#include <drm/drm_edid.h>
 >>>>> +#include <drm/drm_managed.h>
 >>>>>    #include "dp_mst_drm.h"
 >>>>> +#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 *state;
 >>>>> @@ -79,6 +83,61 @@ static int msm_dp_mst_find_vcpi_slots(struct 
drm_dp_mst_topology_mgr *mgr, int p
 >>>>>        return num_slots;
 >>>>>    }
 >>>>> +static int msm_dp_mst_get_mst_pbn_div(struct msm_dp_panel 
*msm_dp_panel)
 >>>>> +{
 >>>>> +    struct msm_dp_link_info *link_info;
 >>>>> +
 >>>>> +    link_info = &msm_dp_panel->link_info;
 >>>>> +
 >>>>> +    return link_info->rate * link_info->num_lanes / 54000;
 >>>>> +}
 >>>>> +
 >>>>> +static int msm_dp_mst_compute_config(struct drm_atomic_state *state,
 >>>>> +                      struct msm_dp_mst *mst, struct 
drm_connector *connector,
 >>>>> +                      struct drm_display_mode *mode)
 >>>>> +{
 >>>>> +    int slots = 0, pbn;
 >>>>> +    struct msm_dp_mst_connector *mst_conn = 
to_msm_dp_mst_connector(connector);
 >>>>> +    int rc = 0;
 >>>>> +    struct drm_dp_mst_topology_state *mst_state;
 >>>>> +    int pbn_div;
 >>>>> +    struct msm_dp *dp_display = mst->msm_dp;
 >>>>> +    u32 bpp;
 >>>>> +
 >>>>> +    bpp = connector->display_info.bpc * 3;
 >>>>> +
 >>>>> +    pbn = drm_dp_calc_pbn_mode(mode->clock, bpp << 4);
 >>>>
 >>>> Is this going to change if DSC is in place? Will it bring 
fractional BPP
 >>>> here?
 >>>>
 >>> Actually, in this patch series, MST not support DSC. So we just don't
 >>> consider this scenario.
 >>
 >> But you still can answer the question.
 >>
 >>
 >> [...]
 >>
 > 1.Emm, for my current understanding, if DSC is enabled, the BPP 
should change and recaculated.
 > Will it bring fractional BPP here?

That's what I am asking

 >  >>>I'm not entirely sure about this answer. I checked how other 
drivers call this function, and they all use bpp << 4, so can we assume 
that this way of calling it is valid?

It is valid. I'm trying to understand the implications and future changes.

 >>>>> +
 >>>>> +    return msm_dp_display_mode_valid(dp_display, &dp_display- 
 >connector->display_info, mode);
 >>>>> +}
 >>>>> +
 >>>>> +static struct drm_encoder *
 >>>>> +msm_dp_mst_atomic_best_encoder(struct drm_connector *connector, 
struct drm_atomic_state *state)
 >>>>
 >>>> Do we need this callback? Don't we have a fixed relationship between
 >>>> connectors and encoders?
 >>
 >> This was left unanswered.
 >>
 > Sorry, I didn't mean to skip any questions — I just planned to reply 
a bit later. Apologies for the confusion.
 > For this question, yes , we don't have the fixed relationship between 
them. Under the current codes, the Connector selects the available 
encoder and bridge in order from index 0 to 4 (up to max_streams) when 
the connector's status changes to 'connected'.

Why? Can we have 1:1 relationship as we do with other bridges?

 >>>>
 >>>>> +{
 >>>>> +    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 *bridge_state;
 >>>>> +    u32 i;
 >>>>> +    struct drm_connector_state *conn_state = 
drm_atomic_get_new_connector_state(state,
 >>>>> +                                            connector);
 >>>>> +
 >>>>
 >>
 >> [...]
 >>
 >>>>> +    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);
 >>>>
 >>>> This really looks like this should be a bridge's callback.
 >>
 >> And this one
 >>
 > Emm, the bridge does not implement atomic_check(). All MST-related 
checks (such as drm_dp_atomic_release_time_slots, 
drm_dp_mst_atomic_check, or others) are performed in the connector's 
atomic_check function. I believe this is because both num_slots and pbn 
are stored in the bridge, and we call this to get the drm_bridge..

So, please split them into connector and bridge checks, calling them 
from corresponding hooks. It might be easier to migrate completely to 
the bridge's atomic_check(). At least it will save us from this clumsy 
code getting the bridge for the connector.


 >>>>
 >>>>> +        if (WARN_ON(!drm_bridge)) {
 >>>>> +            rc = -EINVAL;
 >>>>> +            goto end;
 >>>>> +        }
 >>>>> +        bridge = to_msm_dp_mst_bridge(drm_bridge);
 >>>>> +
 >>>>> +        bridge_state = msm_dp_mst_br_priv_state(state, bridge);
 >>>>> +        if (IS_ERR(bridge_state)) {
 >>>>> +            rc = PTR_ERR(bridge_state);
 >>>>> +            goto end;
 >>>>> +        }
 >>>>> +
 >>>>> +        if (WARN_ON(bridge_state->connector != connector)) {
 >>>>> +            rc = -EINVAL;
 >>>>> +            goto end;
 >>>>> +        }
 >>>>> +
 >>>>> +        slots = bridge_state->num_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;
 >>>>> +            }
 >>>>> +            vcpi_released = true;
 >>>>> +        }
 >>>>> +
 >>>>> +        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
 >>>>> +             */
 >>>>> +            bridge_state->num_slots = 0;
 >>>>> +            bridge_state->connector = NULL;
 >>>>> +            bridge_state->msm_dp_panel = NULL;
 >>>>> +
 >>>>> +            drm_dbg_dp(dp_display->drm_dev, "clear best encoder: 
%d\n", bridge->id);
 >>>>> +        }
 >>>>> +    }
 >>>>
 >>>> This looks like there are several functions fused together. Please
 >>>> unfuse those into small and neat code blocks.
 >>
 >> And this 😂
 >>
 > Got it.. this code only do one thing, check and try to release 
time_slots.. we can try to package it into small functions..

I still don't understand, why do we need to release time_slots here 
instead of using MST helpers.

 >>>>
 >>>>> +
 >>>>> +mode_set:
 >>>>> +    if (!new_conn_state->crtc)
 >>>>> +        goto end;
 >>>>> +
 >>>>> +    crtc_state = drm_atomic_get_new_crtc_state(state, 
new_conn_state->crtc);
 >>>>> +
 >>>>> +    if (drm_atomic_crtc_needs_modeset(crtc_state) && crtc_state- 
 >active) {
 >>>>
 >>>> Use of crtc_state->active doesn't look correct.
 >>
 >>
 >> ...
 >>
 > Sorry, I'm still not quite sure where the issue is. Could you please 
help point it out? Thanks~~


Please refer to the documentation for drm_crtc_state::active. The 
drivers are not supposed to use this field in checks.

 >>>>
 >>>>> +        if (WARN_ON(!new_conn_state->best_encoder)) {
 >>>>> +            rc = -EINVAL;
 >>>>> +            goto end;
 >>>>> +        }
 >>>>> +
 >>>>> +        drm_bridge = 
drm_bridge_chain_get_first_bridge(new_conn_state->best_encoder);
 >>>>> +        if (WARN_ON(!drm_bridge)) {
 >>>>> +            rc = -EINVAL;
 >>>>> +            goto end;
 >>>>> +        }
 >>>>> +        bridge = to_msm_dp_mst_bridge(drm_bridge);
 >>>>> +
 >>>>> +        bridge_state = msm_dp_mst_br_priv_state(state, bridge);
 >>>>> +        if (IS_ERR(bridge_state)) {
 >>>>> +            rc = PTR_ERR(bridge_state);
 >>>>> +            goto end;
 >>>>> +        }
 >>>>> +
 >>>>> +        if (WARN_ON(bridge_state->connector != connector)) {
 >>>>> +            rc = -EINVAL;
 >>>>> +            goto end;
 >>>>> +        }
 >>>>
 >>>> Can all of this actually happen?
 >>
 >> ...
 >>
 > Actually not, I haven't encountered it yet. I'm not sure how to 
trigger it, but it might occur under race conditions? Or we just remove 
it untill some case it really happen..

No. You actually think whether this condition can happen, then keep it 
if it can (and drop it if it can not happen).

 >>>>
 >>>>> +
 >>>>> +        /*
 >>>>> +         * check if vcpi slots are trying to get allocated in 
same phase
 >>>>> +         * as deallocation. If so, go to end to avoid allocation.
 >>>>> +         */
 >>>>> +        if (vcpi_released) {
 >>>>> +            drm_dbg_dp(dp_display->drm_dev,
 >>>>> +                   "skipping allocation since vcpi was released 
in the same state\n");
 >>>>> +            goto end;
 >>>>> +        }
 >>>>> +
 >>>>> +        if (WARN_ON(bridge_state->num_slots)) {
 >>>>> +            rc = -EINVAL;
 >>>>> +            goto end;
 >>>>> +        }
 >>>>> +
 >>>>> +        slots = msm_dp_mst_compute_config(state, mst, connector, 
&crtc_state->mode);
 >>>>> +        if (slots < 0) {
 >>>>> +            rc = slots;
 >>>>> +            goto end;
 >>>>> +        }
 >>>>> +
 >>>>> +        bridge_state->num_slots = slots;
 >>>>> +    }
 >>>>> +
 >>>>> +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);
 >>>>> +}
 >>>>> +
 >>>>> +/* 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_connector;
 >>>>> +    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_connector = devm_kzalloc(dev->dev, 
sizeof(*mst_connector), GFP_KERNEL);
 >>>>
 >>>> This shows that somebody doesn't understand the reason for drmm 
and the
 >>>> difference between devm and drmm and the lifetime of the objects. 
Do you
 >>>> see two issues in this line?
 >>>>
 >>>> Let me help you. Please use normal (non-managed) memory here. It 
is the
 >>>> only correct way to allocate memory for MST connectors.
 >>>>
 >>> Thanks for point it.. it will lead to mem leak.. so we need to use
 >>> kzalloc()...
 >>
 >> - Did you understand why devm is unsuitable here?
 >> - Why drmm is also unsutable?
 >> - What is the implication of using kzalloc() here?
 >>
 > For my understanding, memory allocated with devm_kzalloc is released 
when the device is removed, while memory allocated with drmm_kzalloc is 
released when the DRM device is unregistered. I believe this is because 
the allocation and release of connectors happen during hotplug events, 
which have a different lifecycle from other devices. If we use 
kzalloc(), we would need to manually free the memory.

Ack, thanks.

 >>>>> +
 >>>>> +    drm_modeset_lock_all(dev);
 >>>>> +
 >>>>> +    rc = drm_connector_dynamic_init(dev, &mst_connector->connector,
 >>>>> +                    &msm_dp_drm_mst_connector_funcs,
 >>>>> +                    DRM_MODE_CONNECTOR_DisplayPort, NULL);
 >>>>> +    if (rc) {
 >>>>> +        drm_modeset_unlock_all(dev);
 >>>>> +        return NULL;
 >>>>> +    }
 >>>>> +
 >>>>> +    mst_connector->dp_panel = msm_dp_display_get_panel(dp_display);
 >>>>> +    if (!mst_connector->dp_panel) {
 >>>>> +        DRM_ERROR("failed to get dp_panel for connector\n");
 >>>>> +        drm_modeset_unlock_all(dev);
 >>>>> +        return NULL;
 >>>>> +    }
 >>>>> +
 >>>>> +    mst_connector->dp_panel->connector = &mst_connector->connector;
 >>>>> +    mst_connector->msm_dp = dp_display;
 >>>>> +    connector = &mst_connector->connector;
 >>>>> +    drm_connector_helper_add(&mst_connector->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(&mst_connector->connector,
 >>>>> +                          dp_mst->mst_bridge[i].encoder);
 >>>>> +        if (rc) {
 >>>>> +            DRM_ERROR("failed to attach encoder to connector, 
%d\n", rc);
 >>>>> +            drm_modeset_unlock_all(dev);
 >>>>> +            return NULL;
 >>>>> +        }
 >>>>> +    }
 >>>>> +
 >>>>> +    mst_connector->mst_port = port;
 >>>>> +    drm_dp_mst_get_port_malloc(mst_connector->mst_port);
 >>>>> +
 >>>>> +    drm_object_attach_property(&mst_connector->connector.base,
 >>>>> +                   dev->mode_config.path_property, 0);
 >>>>> +    drm_object_attach_property(&mst_connector->connector.base,
 >>>>> +                   dev->mode_config.tile_property, 0);
 >>>>
 >>>> subconnector? Or do we report the subconnector only for the main DP
 >>>> port?
 >>
 >>
 >> ...
 >>
 > Sorry, I'm not quite sure what 'subconnector' means in this 
context... Could you please help explain it a bit more? From what I’ve 
seen in other drivers, these two properties are registered for each MST 
connector.
 > 
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/ 
drivers/gpu/drm/i915/display/intel_dp_mst.c?h=v6.16-rc2#n1618

I was thinking about the 
drm_connector_attach_dp_subconnector_property(), but it seems it's not 
used for MST connectors.

However, we should definitely be setting the path property, see 
drm_connector_set_path_property().


 >>>>
 >>>>> +
 >>>>> +    drm_modeset_unlock_all(dev);
 >>>>> +
 >


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST
  2025-06-16 13:48       ` Dmitry Baryshkov
@ 2025-06-17  7:52         ` Yongxing Mou
  2025-06-17 10:04           ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-17  7:52 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/16 21:48, Dmitry Baryshkov wrote:
> On Mon, Jun 16, 2025 at 08:43:40PM +0800, Yongxing Mou wrote:
>>
>>
>> On 2025/6/9 23:51, Dmitry Baryshkov wrote:
>>> On Mon, Jun 09, 2025 at 08:21:48PM +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.
>>>>
>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>>>> ---
>>>>    drivers/gpu/drm/msm/dp/dp_mst_drm.c | 515 ++++++++++++++++++++++++++++++++++++
>>>>    drivers/gpu/drm/msm/dp/dp_mst_drm.h |   3 +
>>>>    2 files changed, 518 insertions(+)
>>>
>>>> +
>>>> +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;
>>>> +	u16 full_pbn, required_pbn;
>>>> +	int available_slots, required_slots;
>>>> +	struct msm_dp_mst_bridge_state *dp_bridge_state;
>>>> +	int i, slots_in_use = 0, active_enc_cnt = 0;
>>>> +	const u32 tot_slots = 63;
>>>> +
>>>> +	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++) {
>>>> +		dp_bridge_state = to_msm_dp_mst_bridge_state(&mst->mst_bridge[i]);
>>>> +		if (dp_bridge_state->connector &&
>>>> +		    dp_bridge_state->connector != connector) {
>>>> +			active_enc_cnt++;
>>>> +			slots_in_use += dp_bridge_state->num_slots;
>>>> +		}
>>>> +	}
>>>> +
>>>> +	if (active_enc_cnt < DP_STREAM_MAX) {
>>>> +		full_pbn = mst_port->full_pbn;
>>>> +		available_slots = tot_slots - slots_in_use;
>>>> +	} 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);
>>>> +
>>>> +	required_slots = msm_dp_mst_find_vcpi_slots(&mst->mst_mgr, required_pbn);
>>>> +
>>>> +	if (required_pbn > full_pbn || required_slots > available_slots) {
>>>> +		drm_dbg_dp(dp_display->drm_dev,
>>>> +			   "mode:%s not supported. pbn %d vs %d slots %d vs %d\n",
>>>> +			   mode->name, required_pbn, full_pbn,
>>>> +			   required_slots, available_slots);
>>>> +		return MODE_BAD;
>>>> +	}
>>>
>>> I almost missed this. Could you please point me, do other drivers
>>> perform mode_valid() check based on the current slots available or not?
>>> Could you please point me to the relevant code in other drivers? Because
>>> it doesn't look correct to me. The mode on the screen remains valid no
>>> matter if I plug or unplug other devices. The atomic_check() should fail
>>> if we don't have enough resources (which includes slots).
>>>
>> Currently, I haven't found other drivers checking available slots during
>> mode_valid(). Intel will check the PBN in here.
> 
> pointer? Also, what do AMD and nouveau do?
> 
Hi,here is the link:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/i915/display/intel_dp_mst.c?h=v6.16-rc2#n1504

nouveau just check the mode_rate and ds_max_dotclock in MST connector 
mode_valid().
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/nouveau/nouveau_dp.c?h=v6.16-rc2#n527

The AMD driver seems much more complex, and I can't understand all the 
logic. It looks like AMD always tries to enable DSC and use the smallest 
possible bandwidth.
>> This condition can help us
>> in the following case:
>>
>> Assume two downstream devices both support 4K 60Hz 10-bit. In MST mode, when
>> the first device occupies the 4Kx60Hzx10bit mode, the remaining bandwidth is
>> insufficient to support the same mode for the second device.
>>
>> If we check the slots in mode_valid(), the second device will reject the
>> 4Kx60Hzx10bit mode but accept 4Kx30Hzx10bit. However, if the check is done
>> in atomic_check(), the second device will display a black screen (because
>> 4Kx60Hzx10bit is considered valid in mode_valid() but failed in
>> atomic_check()).
> 
> If we filter modes in mode_valid(), then consider the following
> scenario: we plug monitor A, plug monitor B, then unplug monitor A. At
> this point we only have monitor B, but it has all modes filtered when A
> has been plugged. So, it is impossible to select 4k@60x10, even though
> it is a perfectly valid mode now.
> 
> Also, with the check happening in the atomic_check() the user will not
> get the black screen: the commit will get rejected, letting userspace to
> lower the mode for the second monitor.
> 
Oh, this scenario is indeed just as you described. So let's remove this 
part of the logic and let userspace decide the final mode.
>>>> +
>>>> +	return msm_dp_display_mode_valid(dp_display, &dp_display->connector->display_info, mode);
>>>> +}
>>>> +
>>>
>>
> 


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST
  2025-06-17  7:52         ` Yongxing Mou
@ 2025-06-17 10:04           ` Dmitry Baryshkov
  0 siblings, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-17 10:04 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On 17/06/2025 10:52, Yongxing Mou wrote:
> 
> 
> On 2025/6/16 21:48, Dmitry Baryshkov wrote:
>> On Mon, Jun 16, 2025 at 08:43:40PM +0800, Yongxing Mou wrote:
>>>
>>>
>>> On 2025/6/9 23:51, Dmitry Baryshkov wrote:
>>>> On Mon, Jun 09, 2025 at 08:21:48PM +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.
>>>>>
>>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>>>>> ---
>>>>>    drivers/gpu/drm/msm/dp/dp_mst_drm.c | 515 ++++++++++++++++++++++ 
>>>>> ++++++++++++++
>>>>>    drivers/gpu/drm/msm/dp/dp_mst_drm.h |   3 +
>>>>>    2 files changed, 518 insertions(+)
>>>>
>>>>> +
>>>>> +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;
>>>>> +    u16 full_pbn, required_pbn;
>>>>> +    int available_slots, required_slots;
>>>>> +    struct msm_dp_mst_bridge_state *dp_bridge_state;
>>>>> +    int i, slots_in_use = 0, active_enc_cnt = 0;
>>>>> +    const u32 tot_slots = 63;
>>>>> +
>>>>> +    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++) {
>>>>> +        dp_bridge_state = to_msm_dp_mst_bridge_state(&mst- 
>>>>> >mst_bridge[i]);
>>>>> +        if (dp_bridge_state->connector &&
>>>>> +            dp_bridge_state->connector != connector) {
>>>>> +            active_enc_cnt++;
>>>>> +            slots_in_use += dp_bridge_state->num_slots;
>>>>> +        }
>>>>> +    }
>>>>> +
>>>>> +    if (active_enc_cnt < DP_STREAM_MAX) {
>>>>> +        full_pbn = mst_port->full_pbn;
>>>>> +        available_slots = tot_slots - slots_in_use;
>>>>> +    } 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);
>>>>> +
>>>>> +    required_slots = msm_dp_mst_find_vcpi_slots(&mst->mst_mgr, 
>>>>> required_pbn);
>>>>> +
>>>>> +    if (required_pbn > full_pbn || required_slots > 
>>>>> available_slots) {
>>>>> +        drm_dbg_dp(dp_display->drm_dev,
>>>>> +               "mode:%s not supported. pbn %d vs %d slots %d vs 
>>>>> %d\n",
>>>>> +               mode->name, required_pbn, full_pbn,
>>>>> +               required_slots, available_slots);
>>>>> +        return MODE_BAD;
>>>>> +    }
>>>>
>>>> I almost missed this. Could you please point me, do other drivers
>>>> perform mode_valid() check based on the current slots available or not?
>>>> Could you please point me to the relevant code in other drivers? 
>>>> Because
>>>> it doesn't look correct to me. The mode on the screen remains valid no
>>>> matter if I plug or unplug other devices. The atomic_check() should 
>>>> fail
>>>> if we don't have enough resources (which includes slots).
>>>>
>>> Currently, I haven't found other drivers checking available slots during
>>> mode_valid(). Intel will check the PBN in here.
>>
>> pointer? Also, what do AMD and nouveau do?
>>
> Hi,here is the link:
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/ 
> drivers/gpu/drm/i915/display/intel_dp_mst.c?h=v6.16-rc2#n1504
> 
> nouveau just check the mode_rate and ds_max_dotclock in MST connector 
> mode_valid().
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/ 
> drivers/gpu/drm/nouveau/nouveau_dp.c?h=v6.16-rc2#n527
> 
> The AMD driver seems much more complex, and I can't understand all the 
> logic. It looks like AMD always tries to enable DSC and use the smallest 
> possible bandwidth.
>>> This condition can help us
>>> in the following case:
>>>
>>> Assume two downstream devices both support 4K 60Hz 10-bit. In MST 
>>> mode, when
>>> the first device occupies the 4Kx60Hzx10bit mode, the remaining 
>>> bandwidth is
>>> insufficient to support the same mode for the second device.
>>>
>>> If we check the slots in mode_valid(), the second device will reject the
>>> 4Kx60Hzx10bit mode but accept 4Kx30Hzx10bit. However, if the check is 
>>> done
>>> in atomic_check(), the second device will display a black screen 
>>> (because
>>> 4Kx60Hzx10bit is considered valid in mode_valid() but failed in
>>> atomic_check()).
>>
>> If we filter modes in mode_valid(), then consider the following
>> scenario: we plug monitor A, plug monitor B, then unplug monitor A. At
>> this point we only have monitor B, but it has all modes filtered when A
>> has been plugged. So, it is impossible to select 4k@60x10, even though
>> it is a perfectly valid mode now.
>>
>> Also, with the check happening in the atomic_check() the user will not
>> get the black screen: the commit will get rejected, letting userspace to
>> lower the mode for the second monitor.
>>
> Oh, this scenario is indeed just as you described. So let's remove this 
> part of the logic and let userspace decide the final mode.

Ack. I think all three major drivers don't perform a check against 
currently allocated slots.

>>>>> +
>>>>> +    return msm_dp_display_mode_valid(dp_display, &dp_display- 
>>>>> >connector->display_info, mode);
>>>>> +}
>>>>> +
>>>>
>>>
>>
> 


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST
       [not found]             ` <ad1db558-c33e-4788-9f25-cac6c21713f1@quicinc.com>
@ 2025-06-19 11:33               ` Dmitry Baryshkov
  2025-06-24  9:56                 ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-19 11:33 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

[initially I responded off-list by mistake, sorry for the confusion and 
possible duplicates]

On 19/06/2025 12:26, Yongxing Mou wrote:
> 
> 
> On 2025/6/16 22:41, Dmitry Baryshkov wrote:
>> On 16/06/2025 17:09, Yongxing Mou wrote:
>>>
>>>
>>> On 2025/6/11 22:31, Dmitry Baryshkov wrote:
>>>> On Wed, Jun 11, 2025 at 08:06:28PM +0800, Yongxing Mou wrote:
>>>>>
>>>>>
>>>>> On 2025/6/9 23:44, Dmitry Baryshkov wrote:
>>>>>> On Mon, Jun 09, 2025 at 08:21:48PM +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.
>>>>>>>
>>>>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>>>>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>>>>>>> ---
>>>>>>>    drivers/gpu/drm/msm/dp/dp_mst_drm.c | 515 ++++++++++++++++++++ 
>>>>>>> + + ++++++++++++++
>>>>>>>    drivers/gpu/drm/msm/dp/dp_mst_drm.h |   3 +
>>>>>>>    2 files changed, 518 insertions(+)
>>>>>>
>>>>>> It generally feels liks 80% of this patch is a generic code. Please
>>>>>> extract generic DP MST connector and push it under drm/display. 
>>>>>> Other DP
>>>>>> MST drivers should be able to use it.
>>>>>>
>>>>>>>
>>>>>>> diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/ 
>>>>>>> drm/ msm/dp/dp_mst_drm.c
>>>>>>> index 
>>>>>>> a3ea34ae63511db0ac920cbeebe30c4e2320b8c4..489fa46aa518ff1cc5f4769b2153fc5153c4cb41 100644
>>>>>>> --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
>>>>>>> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
>>>>>>> @@ -25,8 +25,12 @@
>>>>>>>     * OF THIS SOFTWARE.
>>>>>>>     */
>>>>>>> +#include <drm/drm_edid.h>
>>>>>>> +#include <drm/drm_managed.h>
>>>>>>>    #include "dp_mst_drm.h"
>>>>>>> +#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 *state;
>>>>>>> @@ -79,6 +83,61 @@ static int msm_dp_mst_find_vcpi_slots(struct 
>>>>>>> drm_dp_mst_topology_mgr *mgr, int p
>>>>>>>        return num_slots;
>>>>>>>    }
>>>>>>> +static int msm_dp_mst_get_mst_pbn_div(struct msm_dp_panel 
>>>>>>> *msm_dp_panel)
>>>>>>> +{
>>>>>>> +    struct msm_dp_link_info *link_info;
>>>>>>> +
>>>>>>> +    link_info = &msm_dp_panel->link_info;
>>>>>>> +
>>>>>>> +    return link_info->rate * link_info->num_lanes / 54000;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int msm_dp_mst_compute_config(struct drm_atomic_state 
>>>>>>> *state,
>>>>>>> +                      struct msm_dp_mst *mst, struct 
>>>>>>> drm_connector *connector,
>>>>>>> +                      struct drm_display_mode *mode)
>>>>>>> +{
>>>>>>> +    int slots = 0, pbn;
>>>>>>> +    struct msm_dp_mst_connector *mst_conn = 
>>>>>>> to_msm_dp_mst_connector(connector);
>>>>>>> +    int rc = 0;
>>>>>>> +    struct drm_dp_mst_topology_state *mst_state;
>>>>>>> +    int pbn_div;
>>>>>>> +    struct msm_dp *dp_display = mst->msm_dp;
>>>>>>> +    u32 bpp;
>>>>>>> +
>>>>>>> +    bpp = connector->display_info.bpc * 3;
>>>>>>> +
>>>>>>> +    pbn = drm_dp_calc_pbn_mode(mode->clock, bpp << 4);
>>>>>>
>>>>>> Is this going to change if DSC is in place? Will it bring 
>>>>>> fractional BPP
>>>>>> here?
>>>>>>
>>>>> Actually, in this patch series, MST not support DSC. So we just don't
>>>>> consider this scenario.
>>>>
>>>> But you still can answer the question.
>>>>
>>>>
>>>> [...]
>>>>
>>> 1.Emm, for my current understanding, if DSC is enabled, the BPP 
>>> should change and recaculated.
>>> Will it bring fractional BPP here?
>>
>> That's what I am asking
>>
>>>  >>>I'm not entirely sure about this answer. I checked how other 
>>> drivers call this function, and they all use bpp << 4, so can we 
>>> assume that this way of calling it is valid?
>>
>> It is valid. I'm trying to understand the implications and future 
>> changes.
>>
>>>>>>> +
>>>>>>> +    return msm_dp_display_mode_valid(dp_display, &dp_display- 
>>>>>>> >connector->display_info, mode);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static struct drm_encoder *
>>>>>>> +msm_dp_mst_atomic_best_encoder(struct drm_connector *connector, 
>>>>>>> struct drm_atomic_state *state)
>>>>>>
>>>>>> Do we need this callback? Don't we have a fixed relationship between
>>>>>> connectors and encoders?
>>>>
>>>> This was left unanswered.
>>>>
>>> Sorry, I didn't mean to skip any questions — I just planned to reply 
>>> a bit later. Apologies for the confusion.
>>> For this question, yes , we don't have the fixed relationship between 
>>> them. Under the current codes, the Connector selects the available 
>>> encoder and bridge in order from index 0 to 4 (up to max_streams) 
>>> when the connector's status changes to 'connected'.
>>
>> Why? Can we have 1:1 relationship as we do with other bridges?
>>
> Emm, It may be because the number of MST connectors is not fixed, but 
> rather determined by the number of output ports on the dongle. For 
> example, in a 2-MST case, there are 2 encoders, but there could be four 
> MST connectors if the dongle has four DP output ports. add_connector() 
> creates MST connectors based on the number of outputs on the dongle, 
> rather than the actual number of connected devices.

Ack, this should be a part of the commit message.

>>>>>>
>>>>>>> +{
>>>>>>> +    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 *bridge_state;
>>>>>>> +    u32 i;
>>>>>>> +    struct drm_connector_state *conn_state = 
>>>>>>> drm_atomic_get_new_connector_state(state,
>>>>>>> +                                            connector);
>>>>>>> +
>>>>>>
>>>>
>>>> [...]
>>>>
>>>>>>> +    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);
>>>>>>
>>>>>> This really looks like this should be a bridge's callback.
>>>>
>>>> And this one
>>>>
>>> Emm, the bridge does not implement atomic_check(). All MST-related 
>>> checks (such as drm_dp_atomic_release_time_slots, 
>>> drm_dp_mst_atomic_check, or others) are performed in the connector's 
>>> atomic_check function. I believe this is because both num_slots and 
>>> pbn are stored in the bridge, and we call this to get the drm_bridge..
>>
>> So, please split them into connector and bridge checks, calling them 
>> from corresponding hooks. It might be easier to migrate completely to 
>> the bridge's atomic_check(). At least it will save us from this clumsy 
>> code getting the bridge for the connector.
>>
> Maybe we don't need to move to bridge's atomic_check(). Connector's 
> atomic_check() do 2 things: 1.Call drm_dp_atomic_release_time_slots when 
> unplug, 2. Call drm_dp_atomic_find_time_slots and 
> drm_dp_mst_atomic_check when plug in.

Actually... I don't think you are calling it in a correct way. It should 
be called from the drm_mode_config.atomic_check, not from connector's 
atomic_check(). See how nouveau driver does it. Even documentation 
insists that it should be called _after_ checking the rest of the state.

> 3 functions need drm_atomic_state, 
> but it seems that drm_bridge_funcs.atomic_check() does not pass in 
> drm_atomic_state. 

You can get drm_atomic_state from bridge_state->base.state, 
crtc_state->state, connector_state->state, that's not really an issue.

> Actually both 2 actions only occur when need modeset. 
> Maybe we can optimize this function in the following ways: (1) reduce 
> unnecessary references to drm_bridge, especially since bridge_state- 
>  >num_slots can replace with payload->time_slots; (2)As your comments, 
> split the function into smaller parts to better reflect the logic.

Yes, please. Getting rid of bridge_state->num_slots is a good path forward.

>>
>>>>>>
>>>>>>> +        if (WARN_ON(!drm_bridge)) {
>>>>>>> +            rc = -EINVAL;
>>>>>>> +            goto end;
>>>>>>> +        }
>>>>>>> +        bridge = to_msm_dp_mst_bridge(drm_bridge);
>>>>>>> +
>>>>>>> +        bridge_state = msm_dp_mst_br_priv_state(state, bridge);
>>>>>>> +        if (IS_ERR(bridge_state)) {
>>>>>>> +            rc = PTR_ERR(bridge_state);
>>>>>>> +            goto end;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        if (WARN_ON(bridge_state->connector != connector)) {
>>>>>>> +            rc = -EINVAL;
>>>>>>> +            goto end;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        slots = bridge_state->num_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;
>>>>>>> +            }
>>>>>>> +            vcpi_released = true;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        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
>>>>>>> +             */
>>>>>>> +            bridge_state->num_slots = 0;
>>>>>>> +            bridge_state->connector = NULL;
>>>>>>> +            bridge_state->msm_dp_panel = NULL;
>>>>>>> +
>>>>>>> +            drm_dbg_dp(dp_display->drm_dev, "clear best encoder: 
>>>>>>> %d\n", bridge->id);
>>>>>>> +        }
>>>>>>> +    }
>>>>>>
>>>>>> This looks like there are several functions fused together. Please
>>>>>> unfuse those into small and neat code blocks.
>>>>
>>>> And this :-D
>>>>
>>> Got it.. this code only do one thing, check and try to release 
>>> time_slots.. we can try to package it into small functions..
>>
>> I still don't understand, why do we need to release time_slots here 
>> instead of using MST helpers.
>>
> Emm , just as above reply.. when we need modeset, call 
> drm_dp_atomic_release_time_slots to release payload and then clear 
> bridge_state cached ..

I don't see other drivers limiting drm_dp_atomic_release_time_slots() to 
the modeset case. Any reason for MSM driver to deviate from that?



>>>>>>
>>>>>>> +
>>>>>>> +mode_set:
>>>>>>> +    if (!new_conn_state->crtc)
>>>>>>> +        goto end;
>>>>>>> +
>>>>>>> +    crtc_state = drm_atomic_get_new_crtc_state(state, 
>>>>>>> new_conn_state->crtc);
>>>>>>> +
>>>>>>> +    if (drm_atomic_crtc_needs_modeset(crtc_state) && crtc_state- 
>>>>>>> >active) {
>>>>>>
>>>>>> Use of crtc_state->active doesn't look correct.
>>>>
>>>>
>>>> ...
>>>>
>>> Sorry, I'm still not quite sure where the issue is. Could you please 
>>> help point it out? Thanks~~
>>
>>
>> Please refer to the documentation for drm_crtc_state::active. The 
>> drivers are not supposed to use this field in checks.
>>
> Got it , so maybe drm_crtc_state::enable might more appropriate here..

Well, why do you need it in the first place? This will determine a 
correct set of conditions.


>>>>>>
>>>>>>> +        if (WARN_ON(!new_conn_state->best_encoder)) {
>>>>>>> +            rc = -EINVAL;
>>>>>>> +            goto end;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        drm_bridge = 
>>>>>>> drm_bridge_chain_get_first_bridge(new_conn_state->best_encoder);
>>>>>>> +        if (WARN_ON(!drm_bridge)) {
>>>>>>> +            rc = -EINVAL;
>>>>>>> +            goto end;
>>>>>>> +        }
>>>>>>> +        bridge = to_msm_dp_mst_bridge(drm_bridge);
>>>>>>> +
>>>>>>> +        bridge_state = msm_dp_mst_br_priv_state(state, bridge);
>>>>>>> +        if (IS_ERR(bridge_state)) {
>>>>>>> +            rc = PTR_ERR(bridge_state);
>>>>>>> +            goto end;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        if (WARN_ON(bridge_state->connector != connector)) {
>>>>>>> +            rc = -EINVAL;
>>>>>>> +            goto end;
>>>>>>> +        }
>>>>>>
>>>>>> Can all of this actually happen?
>>>>
>>>> ...
>>>>
>>> Actually not, I haven't encountered it yet. I'm not sure how to 
>>> trigger it, but it might occur under race conditions? Or we just 
>>> remove it untill some case it really happen..
>>
>> No. You actually think whether this condition can happen, then keep it 
>> if it can (and drop it if it can not happen).
>>
> Got it. Let me test a few different cases to see if these scenarios occur.

No. It's not about testing. It's about asserting if the scenario can 
occur or not per your call stacks and per the design / contract.


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST
  2025-06-19 11:33               ` Dmitry Baryshkov
@ 2025-06-24  9:56                 ` Yongxing Mou
  2025-06-24 22:25                   ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-24  9:56 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/19 19:33, Dmitry Baryshkov wrote:
> [initially I responded off-list by mistake, sorry for the confusion and 
> possible duplicates]
> 
> On 19/06/2025 12:26, Yongxing Mou wrote:
>>
>>
>> On 2025/6/16 22:41, Dmitry Baryshkov wrote:
>>> On 16/06/2025 17:09, Yongxing Mou wrote:
>>>>
>>>>
>>>> On 2025/6/11 22:31, Dmitry Baryshkov wrote:
>>>>> On Wed, Jun 11, 2025 at 08:06:28PM +0800, Yongxing Mou wrote:
>>>>>>
>>>>>>
>>>>>> On 2025/6/9 23:44, Dmitry Baryshkov wrote:
>>>>>>> On Mon, Jun 09, 2025 at 08:21:48PM +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.
>>>>>>>>
>>>>>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>>>>>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>>>>>>>> ---
>>>>>>>>    drivers/gpu/drm/msm/dp/dp_mst_drm.c | 515 +++++++++++++++++++ 
>>>>>>>> + + + ++++++++++++++
>>>>>>>>    drivers/gpu/drm/msm/dp/dp_mst_drm.h |   3 +
>>>>>>>>    2 files changed, 518 insertions(+)
>>>>>>>
>>>>>>> It generally feels liks 80% of this patch is a generic code. Please
>>>>>>> extract generic DP MST connector and push it under drm/display. 
>>>>>>> Other DP
>>>>>>> MST drivers should be able to use it.
>>>>>>>
>>>>>>>>
>>>>>>>> diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/ 
>>>>>>>> drm/ msm/dp/dp_mst_drm.c
>>>>>>>> index 
>>>>>>>> a3ea34ae63511db0ac920cbeebe30c4e2320b8c4..489fa46aa518ff1cc5f4769b2153fc5153c4cb41 100644
>>>>>>>> --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
>>>>>>>> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
>>>>>>>> @@ -25,8 +25,12 @@
>>>>>>>>     * OF THIS SOFTWARE.
>>>>>>>>     */
>>>>>>>> +#include <drm/drm_edid.h>
>>>>>>>> +#include <drm/drm_managed.h>
>>>>>>>>    #include "dp_mst_drm.h"
>>>>>>>> +#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 *state;
>>>>>>>> @@ -79,6 +83,61 @@ static int msm_dp_mst_find_vcpi_slots(struct 
>>>>>>>> drm_dp_mst_topology_mgr *mgr, int p
>>>>>>>>        return num_slots;
>>>>>>>>    }
>>>>>>>> +static int msm_dp_mst_get_mst_pbn_div(struct msm_dp_panel 
>>>>>>>> *msm_dp_panel)
>>>>>>>> +{
>>>>>>>> +    struct msm_dp_link_info *link_info;
>>>>>>>> +
>>>>>>>> +    link_info = &msm_dp_panel->link_info;
>>>>>>>> +
>>>>>>>> +    return link_info->rate * link_info->num_lanes / 54000;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static int msm_dp_mst_compute_config(struct drm_atomic_state 
>>>>>>>> *state,
>>>>>>>> +                      struct msm_dp_mst *mst, struct 
>>>>>>>> drm_connector *connector,
>>>>>>>> +                      struct drm_display_mode *mode)
>>>>>>>> +{
>>>>>>>> +    int slots = 0, pbn;
>>>>>>>> +    struct msm_dp_mst_connector *mst_conn = 
>>>>>>>> to_msm_dp_mst_connector(connector);
>>>>>>>> +    int rc = 0;
>>>>>>>> +    struct drm_dp_mst_topology_state *mst_state;
>>>>>>>> +    int pbn_div;
>>>>>>>> +    struct msm_dp *dp_display = mst->msm_dp;
>>>>>>>> +    u32 bpp;
>>>>>>>> +
>>>>>>>> +    bpp = connector->display_info.bpc * 3;
>>>>>>>> +
>>>>>>>> +    pbn = drm_dp_calc_pbn_mode(mode->clock, bpp << 4);
>>>>>>>
>>>>>>> Is this going to change if DSC is in place? Will it bring 
>>>>>>> fractional BPP
>>>>>>> here?
>>>>>>>
>>>>>> Actually, in this patch series, MST not support DSC. So we just don't
>>>>>> consider this scenario.
>>>>>
>>>>> But you still can answer the question.
>>>>>
>>>>>
>>>>> [...]
>>>>>
>>>> 1.Emm, for my current understanding, if DSC is enabled, the BPP 
>>>> should change and recaculated.
>>>> Will it bring fractional BPP here?
>>>
>>> That's what I am asking
>>>
>>>>  >>>I'm not entirely sure about this answer. I checked how other 
>>>> drivers call this function, and they all use bpp << 4, so can we 
>>>> assume that this way of calling it is valid?
>>>
>>> It is valid. I'm trying to understand the implications and future 
>>> changes.
>>>
>>>>>>>> +
>>>>>>>> +    return msm_dp_display_mode_valid(dp_display, &dp_display- 
>>>>>>>> >connector->display_info, mode);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static struct drm_encoder *
>>>>>>>> +msm_dp_mst_atomic_best_encoder(struct drm_connector *connector, 
>>>>>>>> struct drm_atomic_state *state)
>>>>>>>
>>>>>>> Do we need this callback? Don't we have a fixed relationship between
>>>>>>> connectors and encoders?
>>>>>
>>>>> This was left unanswered.
>>>>>
>>>> Sorry, I didn't mean to skip any questions — I just planned to reply 
>>>> a bit later. Apologies for the confusion.
>>>> For this question, yes , we don't have the fixed relationship 
>>>> between them. Under the current codes, the Connector selects the 
>>>> available encoder and bridge in order from index 0 to 4 (up to 
>>>> max_streams) when the connector's status changes to 'connected'.
>>>
>>> Why? Can we have 1:1 relationship as we do with other bridges?
>>>
>> Emm, It may be because the number of MST connectors is not fixed, but 
>> rather determined by the number of output ports on the dongle. For 
>> example, in a 2-MST case, there are 2 encoders, but there could be 
>> four MST connectors if the dongle has four DP output ports. 
>> add_connector() creates MST connectors based on the number of outputs 
>> on the dongle, rather than the actual number of connected devices.
> 
> Ack, this should be a part of the commit message.
> 
>>>>>>>
>>>>>>>> +{
>>>>>>>> +    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 *bridge_state;
>>>>>>>> +    u32 i;
>>>>>>>> +    struct drm_connector_state *conn_state = 
>>>>>>>> drm_atomic_get_new_connector_state(state,
>>>>>>>> +                                            connector);
>>>>>>>> +
>>>>>>>
>>>>>
>>>>> [...]
>>>>>
>>>>>>>> +    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);
>>>>>>>
>>>>>>> This really looks like this should be a bridge's callback.
>>>>>
>>>>> And this one
>>>>>
>>>> Emm, the bridge does not implement atomic_check(). All MST-related 
>>>> checks (such as drm_dp_atomic_release_time_slots, 
>>>> drm_dp_mst_atomic_check, or others) are performed in the connector's 
>>>> atomic_check function. I believe this is because both num_slots and 
>>>> pbn are stored in the bridge, and we call this to get the drm_bridge..
>>>
>>> So, please split them into connector and bridge checks, calling them 
>>> from corresponding hooks. It might be easier to migrate completely to 
>>> the bridge's atomic_check(). At least it will save us from this 
>>> clumsy code getting the bridge for the connector.
>>>
>> Maybe we don't need to move to bridge's atomic_check(). Connector's 
>> atomic_check() do 2 things: 1.Call drm_dp_atomic_release_time_slots 
>> when unplug, 2. Call drm_dp_atomic_find_time_slots and 
>> drm_dp_mst_atomic_check when plug in.
> 
> Actually... I don't think you are calling it in a correct way. It should 
> be called from the drm_mode_config.atomic_check, not from connector's 
> atomic_check(). See how nouveau driver does it. Even documentation 
> insists that it should be called _after_ checking the rest of the state.
> 
In the documentation, drm_dp_atomic_find_time_slots should be placed in 
drm_encoder_helper_funcs.atomic_check(), 
drm_dp_atomic_release_time_slots in 
&drm_connector_helper_funcs.atomic_check(), and drm_dp_mst_atomic_check 
in &drm_mode_config_funcs.atomic_check(). So maybe we can move these 
atomic_check() calls back to their original positions as do, as 
recommended in the documenttation.
>> 3 functions need drm_atomic_state, but it seems that 
>> drm_bridge_funcs.atomic_check() does not pass in drm_atomic_state. 
> 
> You can get drm_atomic_state from bridge_state->base.state, crtc_state- 
>  >state, connector_state->state, that's not really an issue.
> 
>> Actually both 2 actions only occur when need modeset. Maybe we can 
>> optimize this function in the following ways: (1) reduce unnecessary 
>> references to drm_bridge, especially since bridge_state-  >num_slots 
>> can replace with payload->time_slots; (2)As your comments, split the 
>> function into smaller parts to better reflect the logic.
> 
> Yes, please. Getting rid of bridge_state->num_slots is a good path forward.
> 
Emm, even if we can drop bridge_state->num_slots, we still need to 
access bridge_state when clearing bridge_state->connector and 
bridge_state->msm_dp_panel, so it might not be possible to completely 
eliminate the use of bridge_state here.
>>>
>>>>>>>
>>>>>>>> +        if (WARN_ON(!drm_bridge)) {
>>>>>>>> +            rc = -EINVAL;
>>>>>>>> +            goto end;
>>>>>>>> +        }
>>>>>>>> +        bridge = to_msm_dp_mst_bridge(drm_bridge);
>>>>>>>> +
>>>>>>>> +        bridge_state = msm_dp_mst_br_priv_state(state, bridge);
>>>>>>>> +        if (IS_ERR(bridge_state)) {
>>>>>>>> +            rc = PTR_ERR(bridge_state);
>>>>>>>> +            goto end;
>>>>>>>> +        }
>>>>>>>> +
>>>>>>>> +        if (WARN_ON(bridge_state->connector != connector)) {
>>>>>>>> +            rc = -EINVAL;
>>>>>>>> +            goto end;
>>>>>>>> +        }
>>>>>>>> +
>>>>>>>> +        slots = bridge_state->num_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;
>>>>>>>> +            }
>>>>>>>> +            vcpi_released = true;
>>>>>>>> +        }
>>>>>>>> +
>>>>>>>> +        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
>>>>>>>> +             */
>>>>>>>> +            bridge_state->num_slots = 0;
>>>>>>>> +            bridge_state->connector = NULL;
>>>>>>>> +            bridge_state->msm_dp_panel = NULL;
>>>>>>>> +
>>>>>>>> +            drm_dbg_dp(dp_display->drm_dev, "clear best 
>>>>>>>> encoder: %d\n", bridge->id);
>>>>>>>> +        }
>>>>>>>> +    }
>>>>>>>
>>>>>>> This looks like there are several functions fused together. Please
>>>>>>> unfuse those into small and neat code blocks.
>>>>>
>>>>> And this :-D
>>>>>
>>>> Got it.. this code only do one thing, check and try to release 
>>>> time_slots.. we can try to package it into small functions..
>>>
>>> I still don't understand, why do we need to release time_slots here 
>>> instead of using MST helpers.
>>>
>> Emm , just as above reply.. when we need modeset, call 
>> drm_dp_atomic_release_time_slots to release payload and then clear 
>> bridge_state cached ..
> 
> I don't see other drivers limiting drm_dp_atomic_release_time_slots() to 
> the modeset case. Any reason for MSM driver to deviate from that?
> 
Actually, you are right. I think the drm_dp_atomic_release_time_slots 
function can handle the slots releases quite well by itself. This 
function can handle various changes in crtc_state quite well. These 
constraints are more about supporting the cleanup of 
bridge_state->connector and bridge_state->msm_dp_panel.
> 
> 
>>>>>>>
>>>>>>>> +
>>>>>>>> +mode_set:
>>>>>>>> +    if (!new_conn_state->crtc)
>>>>>>>> +        goto end;
>>>>>>>> +
>>>>>>>> +    crtc_state = drm_atomic_get_new_crtc_state(state, 
>>>>>>>> new_conn_state->crtc);
>>>>>>>> +
>>>>>>>> +    if (drm_atomic_crtc_needs_modeset(crtc_state) && 
>>>>>>>> crtc_state- >active) {
>>>>>>>
>>>>>>> Use of crtc_state->active doesn't look correct.
>>>>>
>>>>>
>>>>> ...
>>>>>
>>>> Sorry, I'm still not quite sure where the issue is. Could you please 
>>>> help point it out? Thanks~~
>>>
>>>
>>> Please refer to the documentation for drm_crtc_state::active. The 
>>> drivers are not supposed to use this field in checks.
>>>
>> Got it , so maybe drm_crtc_state::enable might more appropriate here..
> 
> Well, why do you need it in the first place? This will determine a 
> correct set of conditions.
> 
Got it. Let me look into whether this can be optimized.
> 
>>>>>>>
>>>>>>>> +        if (WARN_ON(!new_conn_state->best_encoder)) {
>>>>>>>> +            rc = -EINVAL;
>>>>>>>> +            goto end;
>>>>>>>> +        }
>>>>>>>> +
>>>>>>>> +        drm_bridge = 
>>>>>>>> drm_bridge_chain_get_first_bridge(new_conn_state->best_encoder);
>>>>>>>> +        if (WARN_ON(!drm_bridge)) {
>>>>>>>> +            rc = -EINVAL;
>>>>>>>> +            goto end;
>>>>>>>> +        }
>>>>>>>> +        bridge = to_msm_dp_mst_bridge(drm_bridge);
>>>>>>>> +
>>>>>>>> +        bridge_state = msm_dp_mst_br_priv_state(state, bridge);
>>>>>>>> +        if (IS_ERR(bridge_state)) {
>>>>>>>> +            rc = PTR_ERR(bridge_state);
>>>>>>>> +            goto end;
>>>>>>>> +        }
>>>>>>>> +
>>>>>>>> +        if (WARN_ON(bridge_state->connector != connector)) {
>>>>>>>> +            rc = -EINVAL;
>>>>>>>> +            goto end;
>>>>>>>> +        }
>>>>>>>
>>>>>>> Can all of this actually happen?
>>>>>
>>>>> ...
>>>>>
>>>> Actually not, I haven't encountered it yet. I'm not sure how to 
>>>> trigger it, but it might occur under race conditions? Or we just 
>>>> remove it untill some case it really happen..
>>>
>>> No. You actually think whether this condition can happen, then keep 
>>> it if it can (and drop it if it can not happen).
>>>
>> Got it. Let me test a few different cases to see if these scenarios 
>> occur.
> 
> No. It's not about testing. It's about asserting if the scenario can 
> occur or not per your call stacks and per the design / contract.
> 
Got it. Let me check this part again. I don't think these errors would 
occur under the current design. But if the system enters a  inconsistent 
error state, this code could help improve our stability.
> 


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST
  2025-06-24  9:56                 ` Yongxing Mou
@ 2025-06-24 22:25                   ` Dmitry Baryshkov
  0 siblings, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-24 22:25 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Tue, 24 Jun 2025 at 12:57, Yongxing Mou <quic_yongmou@quicinc.com> wrote:
>
>
>
> On 2025/6/19 19:33, Dmitry Baryshkov wrote:
> > [initially I responded off-list by mistake, sorry for the confusion and
> > possible duplicates]
> >
> > On 19/06/2025 12:26, Yongxing Mou wrote:
> >>
> >>
> >> On 2025/6/16 22:41, Dmitry Baryshkov wrote:
> >>> On 16/06/2025 17:09, Yongxing Mou wrote:
> >>>>
> >>>>
> >>>> On 2025/6/11 22:31, Dmitry Baryshkov wrote:
> >>>>> On Wed, Jun 11, 2025 at 08:06:28PM +0800, Yongxing Mou wrote:
> >>>>>>
> >>>>>>
> >>>>>> On 2025/6/9 23:44, Dmitry Baryshkov wrote:
> >>>>>>> On Mon, Jun 09, 2025 at 08:21:48PM +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.
> >>>>>>>>
> >>>>>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> >>>>>>>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> >>>>>>>> ---
> >>>>>>>>    drivers/gpu/drm/msm/dp/dp_mst_drm.c | 515 +++++++++++++++++++
> >>>>>>>> + + + ++++++++++++++
> >>>>>>>>    drivers/gpu/drm/msm/dp/dp_mst_drm.h |   3 +
> >>>>>>>>    2 files changed, 518 insertions(+)
> >>>>>>>
> >>>>>>> It generally feels liks 80% of this patch is a generic code. Please
> >>>>>>> extract generic DP MST connector and push it under drm/display.
> >>>>>>> Other DP
> >>>>>>> MST drivers should be able to use it.
> >>>>>>>
> >>>>>>>>
> >>>>>>>> diff --git a/drivers/gpu/drm/msm/dp/dp_mst_drm.c b/drivers/gpu/
> >>>>>>>> drm/ msm/dp/dp_mst_drm.c
> >>>>>>>> index
> >>>>>>>> a3ea34ae63511db0ac920cbeebe30c4e2320b8c4..489fa46aa518ff1cc5f4769b2153fc5153c4cb41 100644
> >>>>>>>> --- a/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> >>>>>>>> +++ b/drivers/gpu/drm/msm/dp/dp_mst_drm.c
> >>>>>>>> @@ -25,8 +25,12 @@
> >>>>>>>>     * OF THIS SOFTWARE.
> >>>>>>>>     */
> >>>>>>>> +#include <drm/drm_edid.h>
> >>>>>>>> +#include <drm/drm_managed.h>
> >>>>>>>>    #include "dp_mst_drm.h"
> >>>>>>>> +#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 *state;
> >>>>>>>> @@ -79,6 +83,61 @@ static int msm_dp_mst_find_vcpi_slots(struct
> >>>>>>>> drm_dp_mst_topology_mgr *mgr, int p
> >>>>>>>>        return num_slots;
> >>>>>>>>    }
> >>>>>>>> +static int msm_dp_mst_get_mst_pbn_div(struct msm_dp_panel
> >>>>>>>> *msm_dp_panel)
> >>>>>>>> +{
> >>>>>>>> +    struct msm_dp_link_info *link_info;
> >>>>>>>> +
> >>>>>>>> +    link_info = &msm_dp_panel->link_info;
> >>>>>>>> +
> >>>>>>>> +    return link_info->rate * link_info->num_lanes / 54000;
> >>>>>>>> +}
> >>>>>>>> +
> >>>>>>>> +static int msm_dp_mst_compute_config(struct drm_atomic_state
> >>>>>>>> *state,
> >>>>>>>> +                      struct msm_dp_mst *mst, struct
> >>>>>>>> drm_connector *connector,
> >>>>>>>> +                      struct drm_display_mode *mode)
> >>>>>>>> +{
> >>>>>>>> +    int slots = 0, pbn;
> >>>>>>>> +    struct msm_dp_mst_connector *mst_conn =
> >>>>>>>> to_msm_dp_mst_connector(connector);
> >>>>>>>> +    int rc = 0;
> >>>>>>>> +    struct drm_dp_mst_topology_state *mst_state;
> >>>>>>>> +    int pbn_div;
> >>>>>>>> +    struct msm_dp *dp_display = mst->msm_dp;
> >>>>>>>> +    u32 bpp;
> >>>>>>>> +
> >>>>>>>> +    bpp = connector->display_info.bpc * 3;
> >>>>>>>> +
> >>>>>>>> +    pbn = drm_dp_calc_pbn_mode(mode->clock, bpp << 4);
> >>>>>>>
> >>>>>>> Is this going to change if DSC is in place? Will it bring
> >>>>>>> fractional BPP
> >>>>>>> here?
> >>>>>>>
> >>>>>> Actually, in this patch series, MST not support DSC. So we just don't
> >>>>>> consider this scenario.
> >>>>>
> >>>>> But you still can answer the question.
> >>>>>
> >>>>>
> >>>>> [...]
> >>>>>
> >>>> 1.Emm, for my current understanding, if DSC is enabled, the BPP
> >>>> should change and recaculated.
> >>>> Will it bring fractional BPP here?
> >>>
> >>> That's what I am asking
> >>>
> >>>>  >>>I'm not entirely sure about this answer. I checked how other
> >>>> drivers call this function, and they all use bpp << 4, so can we
> >>>> assume that this way of calling it is valid?
> >>>
> >>> It is valid. I'm trying to understand the implications and future
> >>> changes.
> >>>
> >>>>>>>> +
> >>>>>>>> +    return msm_dp_display_mode_valid(dp_display, &dp_display-
> >>>>>>>> >connector->display_info, mode);
> >>>>>>>> +}
> >>>>>>>> +
> >>>>>>>> +static struct drm_encoder *
> >>>>>>>> +msm_dp_mst_atomic_best_encoder(struct drm_connector *connector,
> >>>>>>>> struct drm_atomic_state *state)
> >>>>>>>
> >>>>>>> Do we need this callback? Don't we have a fixed relationship between
> >>>>>>> connectors and encoders?
> >>>>>
> >>>>> This was left unanswered.
> >>>>>
> >>>> Sorry, I didn't mean to skip any questions — I just planned to reply
> >>>> a bit later. Apologies for the confusion.
> >>>> For this question, yes , we don't have the fixed relationship
> >>>> between them. Under the current codes, the Connector selects the
> >>>> available encoder and bridge in order from index 0 to 4 (up to
> >>>> max_streams) when the connector's status changes to 'connected'.
> >>>
> >>> Why? Can we have 1:1 relationship as we do with other bridges?
> >>>
> >> Emm, It may be because the number of MST connectors is not fixed, but
> >> rather determined by the number of output ports on the dongle. For
> >> example, in a 2-MST case, there are 2 encoders, but there could be
> >> four MST connectors if the dongle has four DP output ports.
> >> add_connector() creates MST connectors based on the number of outputs
> >> on the dongle, rather than the actual number of connected devices.
> >
> > Ack, this should be a part of the commit message.
> >
> >>>>>>>
> >>>>>>>> +{
> >>>>>>>> +    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 *bridge_state;
> >>>>>>>> +    u32 i;
> >>>>>>>> +    struct drm_connector_state *conn_state =
> >>>>>>>> drm_atomic_get_new_connector_state(state,
> >>>>>>>> +                                            connector);
> >>>>>>>> +
> >>>>>>>
> >>>>>
> >>>>> [...]
> >>>>>
> >>>>>>>> +    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);
> >>>>>>>
> >>>>>>> This really looks like this should be a bridge's callback.
> >>>>>
> >>>>> And this one
> >>>>>
> >>>> Emm, the bridge does not implement atomic_check(). All MST-related
> >>>> checks (such as drm_dp_atomic_release_time_slots,
> >>>> drm_dp_mst_atomic_check, or others) are performed in the connector's
> >>>> atomic_check function. I believe this is because both num_slots and
> >>>> pbn are stored in the bridge, and we call this to get the drm_bridge..
> >>>
> >>> So, please split them into connector and bridge checks, calling them
> >>> from corresponding hooks. It might be easier to migrate completely to
> >>> the bridge's atomic_check(). At least it will save us from this
> >>> clumsy code getting the bridge for the connector.
> >>>
> >> Maybe we don't need to move to bridge's atomic_check(). Connector's
> >> atomic_check() do 2 things: 1.Call drm_dp_atomic_release_time_slots
> >> when unplug, 2. Call drm_dp_atomic_find_time_slots and
> >> drm_dp_mst_atomic_check when plug in.
> >
> > Actually... I don't think you are calling it in a correct way. It should
> > be called from the drm_mode_config.atomic_check, not from connector's
> > atomic_check(). See how nouveau driver does it. Even documentation
> > insists that it should be called _after_ checking the rest of the state.
> >
> In the documentation, drm_dp_atomic_find_time_slots should be placed in
> drm_encoder_helper_funcs.atomic_check(),

I'm not sure about this. Our encoders are implemented by the DPU
driver, so I'd rather not call MST functions from it.
You might call it from drm_bridge_funcs::atomic_check(), it is being
called at the same stage.

> drm_dp_atomic_release_time_slots in
> &drm_connector_helper_funcs.atomic_check(), and drm_dp_mst_atomic_check
> in &drm_mode_config_funcs.atomic_check(). So maybe we can move these
> atomic_check() calls back to their original positions as do, as
> recommended in the documenttation.

These two are fine. Please move them to proper places.

> >> 3 functions need drm_atomic_state, but it seems that
> >> drm_bridge_funcs.atomic_check() does not pass in drm_atomic_state.
> >
> > You can get drm_atomic_state from bridge_state->base.state, crtc_state-
> >  >state, connector_state->state, that's not really an issue.
> >
> >> Actually both 2 actions only occur when need modeset. Maybe we can
> >> optimize this function in the following ways: (1) reduce unnecessary
> >> references to drm_bridge, especially since bridge_state-  >num_slots
> >> can replace with payload->time_slots; (2)As your comments, split the
> >> function into smaller parts to better reflect the logic.
> >
> > Yes, please. Getting rid of bridge_state->num_slots is a good path forward.
> >
> Emm, even if we can drop bridge_state->num_slots, we still need to
> access bridge_state when clearing bridge_state->connector and
> bridge_state->msm_dp_panel, so it might not be possible to completely
> eliminate the use of bridge_state here.

This is fine. We should drop state-related data from
msm_dp_mst_bridge, the msm_dp_mst_bridge_state can stay.
I wanted to get rid of unnecessary slots management in the MSM DP
driver, the state is fine to exist.

> >>>
> >>>>>>>
> >>>>>>>> +        if (WARN_ON(!drm_bridge)) {
> >>>>>>>> +            rc = -EINVAL;
> >>>>>>>> +            goto end;
> >>>>>>>> +        }
> >>>>>>>> +        bridge = to_msm_dp_mst_bridge(drm_bridge);
> >>>>>>>> +
> >>>>>>>> +        bridge_state = msm_dp_mst_br_priv_state(state, bridge);
> >>>>>>>> +        if (IS_ERR(bridge_state)) {
> >>>>>>>> +            rc = PTR_ERR(bridge_state);
> >>>>>>>> +            goto end;
> >>>>>>>> +        }
> >>>>>>>> +
> >>>>>>>> +        if (WARN_ON(bridge_state->connector != connector)) {
> >>>>>>>> +            rc = -EINVAL;
> >>>>>>>> +            goto end;
> >>>>>>>> +        }
> >>>>>>>> +
> >>>>>>>> +        slots = bridge_state->num_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;
> >>>>>>>> +            }
> >>>>>>>> +            vcpi_released = true;
> >>>>>>>> +        }
> >>>>>>>> +
> >>>>>>>> +        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
> >>>>>>>> +             */
> >>>>>>>> +            bridge_state->num_slots = 0;
> >>>>>>>> +            bridge_state->connector = NULL;
> >>>>>>>> +            bridge_state->msm_dp_panel = NULL;
> >>>>>>>> +
> >>>>>>>> +            drm_dbg_dp(dp_display->drm_dev, "clear best
> >>>>>>>> encoder: %d\n", bridge->id);
> >>>>>>>> +        }
> >>>>>>>> +    }
> >>>>>>>
> >>>>>>> This looks like there are several functions fused together. Please
> >>>>>>> unfuse those into small and neat code blocks.
> >>>>>
> >>>>> And this :-D
> >>>>>
> >>>> Got it.. this code only do one thing, check and try to release
> >>>> time_slots.. we can try to package it into small functions..
> >>>
> >>> I still don't understand, why do we need to release time_slots here
> >>> instead of using MST helpers.
> >>>
> >> Emm , just as above reply.. when we need modeset, call
> >> drm_dp_atomic_release_time_slots to release payload and then clear
> >> bridge_state cached ..
> >
> > I don't see other drivers limiting drm_dp_atomic_release_time_slots() to
> > the modeset case. Any reason for MSM driver to deviate from that?
> >
> Actually, you are right. I think the drm_dp_atomic_release_time_slots
> function can handle the slots releases quite well by itself. This
> function can handle various changes in crtc_state quite well. These
> constraints are more about supporting the cleanup of
> bridge_state->connector and bridge_state->msm_dp_panel.

Can't we keep msm_dp_panel in the connector's state and access it by
getting the state by the connector?

> >
> >
> >>>>>>>
> >>>>>>>> +
> >>>>>>>> +mode_set:
> >>>>>>>> +    if (!new_conn_state->crtc)
> >>>>>>>> +        goto end;
> >>>>>>>> +
> >>>>>>>> +    crtc_state = drm_atomic_get_new_crtc_state(state,
> >>>>>>>> new_conn_state->crtc);
> >>>>>>>> +
> >>>>>>>> +    if (drm_atomic_crtc_needs_modeset(crtc_state) &&
> >>>>>>>> crtc_state- >active) {
> >>>>>>>
> >>>>>>> Use of crtc_state->active doesn't look correct.
> >>>>>
> >>>>>
> >>>>> ...
> >>>>>
> >>>> Sorry, I'm still not quite sure where the issue is. Could you please
> >>>> help point it out? Thanks~~
> >>>
> >>>
> >>> Please refer to the documentation for drm_crtc_state::active. The
> >>> drivers are not supposed to use this field in checks.
> >>>
> >> Got it , so maybe drm_crtc_state::enable might more appropriate here..
> >
> > Well, why do you need it in the first place? This will determine a
> > correct set of conditions.
> >
> Got it. Let me look into whether this can be optimized.
> >
> >>>>>>>
> >>>>>>>> +        if (WARN_ON(!new_conn_state->best_encoder)) {
> >>>>>>>> +            rc = -EINVAL;
> >>>>>>>> +            goto end;
> >>>>>>>> +        }
> >>>>>>>> +
> >>>>>>>> +        drm_bridge =
> >>>>>>>> drm_bridge_chain_get_first_bridge(new_conn_state->best_encoder);
> >>>>>>>> +        if (WARN_ON(!drm_bridge)) {
> >>>>>>>> +            rc = -EINVAL;
> >>>>>>>> +            goto end;
> >>>>>>>> +        }
> >>>>>>>> +        bridge = to_msm_dp_mst_bridge(drm_bridge);
> >>>>>>>> +
> >>>>>>>> +        bridge_state = msm_dp_mst_br_priv_state(state, bridge);
> >>>>>>>> +        if (IS_ERR(bridge_state)) {
> >>>>>>>> +            rc = PTR_ERR(bridge_state);
> >>>>>>>> +            goto end;
> >>>>>>>> +        }
> >>>>>>>> +
> >>>>>>>> +        if (WARN_ON(bridge_state->connector != connector)) {
> >>>>>>>> +            rc = -EINVAL;
> >>>>>>>> +            goto end;
> >>>>>>>> +        }
> >>>>>>>
> >>>>>>> Can all of this actually happen?
> >>>>>
> >>>>> ...
> >>>>>
> >>>> Actually not, I haven't encountered it yet. I'm not sure how to
> >>>> trigger it, but it might occur under race conditions? Or we just
> >>>> remove it untill some case it really happen..
> >>>
> >>> No. You actually think whether this condition can happen, then keep
> >>> it if it can (and drop it if it can not happen).
> >>>
> >> Got it. Let me test a few different cases to see if these scenarios
> >> occur.
> >
> > No. It's not about testing. It's about asserting if the scenario can
> > occur or not per your call stacks and per the design / contract.
> >
> Got it. Let me check this part again. I don't think these errors would
> occur under the current design. But if the system enters a  inconsistent
> error state, this code could help improve our stability.

Then please eliminate a way for the system to enter an 'inconsistent
error state'.

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 01/38] drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid
  2025-06-09 12:41   ` Dmitry Baryshkov
@ 2025-06-25  8:43     ` Yongxing Mou
  2025-06-25 13:32       ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-25  8:43 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/9 20:41, Dmitry Baryshkov wrote:
> On Mon, Jun 09, 2025 at 08:21:20PM +0800, Yongxing Mou wrote:
>> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>
>> In preparation of DP MST where link caps are read for the
>> immediate downstream device and the edid is read through
> 
> EDID, not edid. Please review all your patches for up/down case.
> 
Got it. Thanks~
>> sideband messaging, split the msm_dp_panel_read_sink_caps() into
>> two parts which read the link parameters and the edid parts
>> respectively. Also drop the panel drm_edid cached as we actually
>> don't need it.
> 
> Also => separate change.
> 
Got it.
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>> ---
>>   drivers/gpu/drm/msm/dp/dp_display.c | 13 +++++----
>>   drivers/gpu/drm/msm/dp/dp_panel.c   | 55 ++++++++++++++++++++-----------------
>>   drivers/gpu/drm/msm/dp/dp_panel.h   |  6 ++--
>>   3 files changed, 40 insertions(+), 34 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
>> index 6f05a939ce9e648e9601597155999b6f85adfcff..4a9b65647cdef1ed6c3bb851f93df0db8be977af 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>> @@ -389,7 +389,11 @@ 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);
>> +	if (rc)
>> +		goto end;
>> +
>> +	rc = msm_dp_panel_read_edid(dp->panel, connector);
>>   	if (rc)
>>   		goto end;
>>   
>> @@ -720,7 +724,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);
>>   }
>>   
>> @@ -783,7 +786,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->catalog);
>> @@ -791,13 +794,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:
>> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
>> index 4e8ab75c771b1e3a2d62f75e9993e1062118482b..d9041e235104a74b3cc50ff2e307eae0c4301ef3 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_panel.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_panel.c
>> @@ -118,14 +118,13 @@ static u32 msm_dp_panel_get_supported_bpp(struct msm_dp_panel *msm_dp_panel,
>>   	return min_supported_bpp;
>>   }
>>   
>> -int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
>> -	struct drm_connector *connector)
>> +int msm_dp_panel_read_link_caps(struct msm_dp_panel *msm_dp_panel)
>>   {
>>   	int rc, bw_code;
>>   	int count;
>>   	struct msm_dp_panel_private *panel;
>>   
>> -	if (!msm_dp_panel || !connector) {
>> +	if (!msm_dp_panel) {
>>   		DRM_ERROR("invalid input\n");
>>   		return -EINVAL;
>>   	}
>> @@ -160,26 +159,29 @@ 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;
>> +	return rc;
>> +}
>>   
>> -	drm_edid_free(msm_dp_panel->drm_edid);
>> +int msm_dp_panel_read_edid(struct msm_dp_panel *msm_dp_panel, struct drm_connector *connector)
>> +{
>> +	struct msm_dp_panel_private *panel;
>> +	const struct drm_edid *drm_edid;
>> +
>> +	panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
>>   
>> -	msm_dp_panel->drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
>> +	drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
>>   
>> -	drm_edid_connector_update(connector, msm_dp_panel->drm_edid);
>> +	drm_edid_connector_update(connector, drm_edid);
>>   
>> -	if (!msm_dp_panel->drm_edid) {
>> +	if (!drm_edid) {
>>   		DRM_ERROR("panel edid read failed\n");
>>   		/* check edid read fail is due to unplug */
>>   		if (!msm_dp_catalog_link_is_connected(panel->catalog)) {
>> -			rc = -ETIMEDOUT;
>> -			goto end;
>> +			return -ETIMEDOUT;
>>   		}
>>   	}
>>   
>> -end:
>> -	return rc;
>> +	return 0;
>>   }
>>   
>>   u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel,
>> @@ -208,15 +210,20 @@ u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel,
>>   int msm_dp_panel_get_modes(struct msm_dp_panel *msm_dp_panel,
>>   	struct drm_connector *connector)
>>   {
>> +	struct msm_dp_panel_private *panel;
>> +	const struct drm_edid *drm_edid;
>> +
>>   	if (!msm_dp_panel) {
>>   		DRM_ERROR("invalid input\n");
>>   		return -EINVAL;
>>   	}
>>   
>> -	if (msm_dp_panel->drm_edid)
>> -		return drm_edid_connector_add_modes(connector);
>> +	panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
>> +
>> +	drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
>> +	drm_edid_connector_update(connector, drm_edid);
> 
> If EDID has been read and processed after HPD high event, why do we need
> to re-read it again? Are we expecting that EDID will change?
> 
Here we indeed don't need to read the EDID again, so we can directly 
call drm_edid_connector_add_modes. Thanks.
>>   
>> -	return 0;
>> +	return drm_edid_connector_add_modes(connector);
>>   }
>>   
>>   static u8 msm_dp_panel_get_edid_checksum(const struct edid *edid)
>> @@ -229,6 +236,7 @@ static u8 msm_dp_panel_get_edid_checksum(const struct edid *edid)
>>   void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel)
>>   {
>>   	struct msm_dp_panel_private *panel;
>> +	const struct drm_edid *drm_edid;
>>   
>>   	if (!msm_dp_panel) {
>>   		DRM_ERROR("invalid input\n");
>> @@ -238,8 +246,13 @@ void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel)
>>   	panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
>>   
>>   	if (panel->link->sink_request & DP_TEST_LINK_EDID_READ) {
>> +		drm_edid = drm_edid_read_ddc(msm_dp_panel->connector, &panel->aux->ddc);
> 
> And again....
> 
Here we need the struct edid,since we drop the cached drm_edid, so we 
need to read it again. Or we can return the drm_edid from 
msm_dp_panel_read_edid and pass it to msm_dp_panel_handle_sink_request, 
then we don't need to read drm_edid here. Emm, I'm still a bit curious 
why we can't cache the drm_edid? It would help us to access it when 
needed. Emm, i see other drivers also cache it.
>> +
>> +		if (!drm_edid)
>> +			return;
>> +
>>   		/* 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)
>> @@ -515,11 +528,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);
>> -}
> 
> Too many changes to be stuffed under the hood of "Also perform foo"
> 
Got it, thanks , will split  this patch and refactor the commit msg.
>> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
>> index 4906f4f09f2451cfed3c1007f38b4db7dfdb1d90..7f139478e1012d5b8f1f745f0de5fc3943745428 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_panel.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_panel.h
>> @@ -32,7 +32,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;
>> @@ -51,7 +50,9 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel);
>>   int msm_dp_panel_read_sink_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);
>> +			      u32 mode_pclk_khz);
>> +int msm_dp_panel_read_link_caps(struct msm_dp_panel *dp_panel);
>> +int msm_dp_panel_read_edid(struct msm_dp_panel *dp_panel, struct drm_connector *connector);
>>   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);
>> @@ -86,5 +87,4 @@ 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, struct msm_dp_catalog *catalog);
>> -void msm_dp_panel_put(struct msm_dp_panel *msm_dp_panel);
>>   #endif /* _DP_PANEL_H_ */
>>
>> -- 
>> 2.34.1
>>
> 


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead
  2025-06-09 12:48   ` Dmitry Baryshkov
@ 2025-06-25 12:34     ` Yongxing Mou
  2025-06-25 14:03       ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-25 12:34 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/9 20:48, Dmitry Baryshkov wrote:
> On Mon, Jun 09, 2025 at 08:21:21PM +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 populated the panel's dp_display_mode
>> instead.
> 
> - Why do we need to cache / copy it anyway? Can't we just pass the
>    corresponding drm_atomic_state / drm_crtc_state / drm_display_mode ?
> 
This part works as follows: .mode_set() copies the adjusted_mode into 
msm_dp_display_private->msm_dp_display_mode, and also parses and stores 
variables such as v_active_low/h_active_low/out_fmt_is_yuv_420 and ... 
When @drm_bridge_funcs.atomic_enable() is called, it copies 
msm_dp_display->msm_dp_mode into dp_panel->msm_dp_mode and initializes 
panel_info in msm_dp_display_set_mode(). Then when go to 
msm_dp_ctrl_on_stream(), the parameters are updated into the 
corresponding hardware registers.

This design has been in place since the first version of the DP driver 
and has remained largely unchanged. Originally, the drm_mode would be 
passed in two stages: from msm_dp_display->msm_dp_mode to 
dp_panel->msm_dp_mode. Since in MST mode each stream requires its own 
drm_mode and stored in dp_panel, we simplified the two-stage transfer 
into a single step (.mode_set() do all things and store in 
msm_dp_panel). Meanwhile we modified the msm_dp_display_set_mode 
function to accept a msm_dp_panel parameter, allowing the MST bridge 
funcs' mode_set() to reuse this part code.

The following patches: 
https://patchwork.freedesktop.org/patch/657573/?series=142207&rev=2 and 
https://patchwork.freedesktop.org/patch/657593/?series=142207&rev=2, 
introduce msm_dp_display_*_helper functions to help reuse common code 
across MST/SST/eDP drm_bridge_funcs.

If we drop msm_dp_mode from dp_panel and use drm_display_mode, it might 
introduce a large number of changes that are not directly related to 
MST. Actually i think the presence of msm_dp_display_mode seems to 
simplify the work in msm_dp_panel_timing_cfg(), this patch series we 
want to focus on MST parts, so would we consider optimizing them later?

Thanks~
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>> ---
>>   drivers/gpu/drm/msm/dp/dp_display.c | 76 ++++++++++++++-----------------------
>>   1 file changed, 29 insertions(+), 47 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
>> index 4a9b65647cdef1ed6c3bb851f93df0db8be977af..9d2db9cbd2552470a36a63f70f517c35436f7280 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>> @@ -92,7 +92,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 */
>> @@ -806,16 +805,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;
>> +
>> +	msm_dp_panel->msm_dp_mode.v_active_low =
>> +		!!(adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC);
>> +	msm_dp_panel->msm_dp_mode.h_active_low =
>> +		!!(adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC);
>> +	msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 =
>> +		drm_mode_is_420_only(&msm_dp_panel->connector->display_info, adjusted_mode) &&
>> +		msm_dp_panel->vsc_sdp_supported;
>>   
>> -	drm_mode_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_init_panel_info(msm_dp_panel);
>>   	return 0;
>>   }
>>   
>> @@ -1431,10 +1443,13 @@ bool msm_dp_needs_periph_flush(const struct msm_dp *msm_dp_display,
>>   bool msm_dp_wide_bus_available(const 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);
>>   
>> -	if (dp->msm_dp_mode.out_fmt_is_yuv_420)
>> +	dp_panel = dp->panel;
>> +
>> +	if (dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
>>   		return false;
>>   
>>   	return dp->wide_bus_supported;
>> @@ -1496,10 +1511,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);
>> @@ -1517,15 +1528,6 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
>>   		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);
>> -		mutex_unlock(&msm_dp_display->event_mutex);
>> -		return;
>> -	}
> 
> It should be done other way around: keep this call and drop
> msm_dp_bridge_mode_set().
> 
Emm as reply in last comments..
>> -
>> -	hpd_state =  msm_dp_display->hpd_state;
>> -
>>   	if (hpd_state == ST_CONNECTED && !dp->power_on) {
>>   		msm_dp_display_host_phy_init(msm_dp_display);
>>   		force_link_train = true;
>> @@ -1604,33 +1606,13 @@ 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->catalog->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;
>> +	msm_dp_display->catalog->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	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 01/38] drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid
  2025-06-25  8:43     ` Yongxing Mou
@ 2025-06-25 13:32       ` Dmitry Baryshkov
  2025-06-27  7:49         ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-25 13:32 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Wed, Jun 25, 2025 at 04:43:55PM +0800, Yongxing Mou wrote:
> 
> 
> On 2025/6/9 20:41, Dmitry Baryshkov wrote:
> > On Mon, Jun 09, 2025 at 08:21:20PM +0800, Yongxing Mou wrote:
> > > From: Abhinav Kumar <quic_abhinavk@quicinc.com>
> > > 
> > > In preparation of DP MST where link caps are read for the
> > > immediate downstream device and the edid is read through
> > 
> > EDID, not edid. Please review all your patches for up/down case.
> > 
> Got it. Thanks~
> > > sideband messaging, split the msm_dp_panel_read_sink_caps() into
> > > two parts which read the link parameters and the edid parts
> > > respectively. Also drop the panel drm_edid cached as we actually
> > > don't need it.
> > 
> > Also => separate change.
> > 
> Got it.
> > > 
> > > Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> > > Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> > > ---
> > >   drivers/gpu/drm/msm/dp/dp_display.c | 13 +++++----
> > >   drivers/gpu/drm/msm/dp/dp_panel.c   | 55 ++++++++++++++++++++-----------------
> > >   drivers/gpu/drm/msm/dp/dp_panel.h   |  6 ++--
> > >   3 files changed, 40 insertions(+), 34 deletions(-)
> > > 
> > > diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> > > index 6f05a939ce9e648e9601597155999b6f85adfcff..4a9b65647cdef1ed6c3bb851f93df0db8be977af 100644
> > > --- a/drivers/gpu/drm/msm/dp/dp_display.c
> > > +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> > > @@ -389,7 +389,11 @@ 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);
> > > +	if (rc)
> > > +		goto end;
> > > +
> > > +	rc = msm_dp_panel_read_edid(dp->panel, connector);
> > >   	if (rc)
> > >   		goto end;
> > > @@ -720,7 +724,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);
> > >   }
> > > @@ -783,7 +786,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->catalog);
> > > @@ -791,13 +794,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:
> > > diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
> > > index 4e8ab75c771b1e3a2d62f75e9993e1062118482b..d9041e235104a74b3cc50ff2e307eae0c4301ef3 100644
> > > --- a/drivers/gpu/drm/msm/dp/dp_panel.c
> > > +++ b/drivers/gpu/drm/msm/dp/dp_panel.c
> > > @@ -118,14 +118,13 @@ static u32 msm_dp_panel_get_supported_bpp(struct msm_dp_panel *msm_dp_panel,
> > >   	return min_supported_bpp;
> > >   }
> > > -int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
> > > -	struct drm_connector *connector)
> > > +int msm_dp_panel_read_link_caps(struct msm_dp_panel *msm_dp_panel)
> > >   {
> > >   	int rc, bw_code;
> > >   	int count;
> > >   	struct msm_dp_panel_private *panel;
> > > -	if (!msm_dp_panel || !connector) {
> > > +	if (!msm_dp_panel) {
> > >   		DRM_ERROR("invalid input\n");
> > >   		return -EINVAL;
> > >   	}
> > > @@ -160,26 +159,29 @@ 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;
> > > +	return rc;
> > > +}
> > > -	drm_edid_free(msm_dp_panel->drm_edid);
> > > +int msm_dp_panel_read_edid(struct msm_dp_panel *msm_dp_panel, struct drm_connector *connector)
> > > +{
> > > +	struct msm_dp_panel_private *panel;
> > > +	const struct drm_edid *drm_edid;
> > > +
> > > +	panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
> > > -	msm_dp_panel->drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
> > > +	drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
> > > -	drm_edid_connector_update(connector, msm_dp_panel->drm_edid);
> > > +	drm_edid_connector_update(connector, drm_edid);
> > > -	if (!msm_dp_panel->drm_edid) {
> > > +	if (!drm_edid) {
> > >   		DRM_ERROR("panel edid read failed\n");
> > >   		/* check edid read fail is due to unplug */
> > >   		if (!msm_dp_catalog_link_is_connected(panel->catalog)) {
> > > -			rc = -ETIMEDOUT;
> > > -			goto end;
> > > +			return -ETIMEDOUT;
> > >   		}
> > >   	}
> > > -end:
> > > -	return rc;
> > > +	return 0;
> > >   }
> > >   u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel,
> > > @@ -208,15 +210,20 @@ u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel,
> > >   int msm_dp_panel_get_modes(struct msm_dp_panel *msm_dp_panel,
> > >   	struct drm_connector *connector)
> > >   {
> > > +	struct msm_dp_panel_private *panel;
> > > +	const struct drm_edid *drm_edid;
> > > +
> > >   	if (!msm_dp_panel) {
> > >   		DRM_ERROR("invalid input\n");
> > >   		return -EINVAL;
> > >   	}
> > > -	if (msm_dp_panel->drm_edid)
> > > -		return drm_edid_connector_add_modes(connector);
> > > +	panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
> > > +
> > > +	drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
> > > +	drm_edid_connector_update(connector, drm_edid);
> > 
> > If EDID has been read and processed after HPD high event, why do we need
> > to re-read it again? Are we expecting that EDID will change?
> > 
> Here we indeed don't need to read the EDID again, so we can directly call
> drm_edid_connector_add_modes. Thanks.
> > > -	return 0;
> > > +	return drm_edid_connector_add_modes(connector);
> > >   }
> > >   static u8 msm_dp_panel_get_edid_checksum(const struct edid *edid)
> > > @@ -229,6 +236,7 @@ static u8 msm_dp_panel_get_edid_checksum(const struct edid *edid)
> > >   void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel)
> > >   {
> > >   	struct msm_dp_panel_private *panel;
> > > +	const struct drm_edid *drm_edid;
> > >   	if (!msm_dp_panel) {
> > >   		DRM_ERROR("invalid input\n");
> > > @@ -238,8 +246,13 @@ void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel)
> > >   	panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
> > >   	if (panel->link->sink_request & DP_TEST_LINK_EDID_READ) {
> > > +		drm_edid = drm_edid_read_ddc(msm_dp_panel->connector, &panel->aux->ddc);
> > 
> > And again....
> > 
> Here we need the struct edid,since we drop the cached drm_edid, so we need
> to read it again. Or we can return the drm_edid from msm_dp_panel_read_edid
> and pass it to msm_dp_panel_handle_sink_request, then we don't need to read
> drm_edid here. Emm, I'm still a bit curious why we can't cache the drm_edid?
> It would help us to access it when needed. Emm, i see other drivers also
> cache it.

Yes, they can cache EDID. However, in this case we don't even need it at
all. This piece needs to be rewritten to use
drm_dp_send_real_edid_checksum(), connector->real_edid_checksum.

Corresponding changes can be submitted separately.


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead
  2025-06-25 12:34     ` Yongxing Mou
@ 2025-06-25 14:03       ` Dmitry Baryshkov
  2025-06-27  8:40         ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-25 14:03 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Wed, Jun 25, 2025 at 08:34:18PM +0800, Yongxing Mou wrote:
> 
> 
> On 2025/6/9 20:48, Dmitry Baryshkov wrote:
> > On Mon, Jun 09, 2025 at 08:21:21PM +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 populated the panel's dp_display_mode
> > > instead.
> > 
> > - Why do we need to cache / copy it anyway? Can't we just pass the
> >    corresponding drm_atomic_state / drm_crtc_state / drm_display_mode ?
> > 
> This part works as follows: .mode_set() copies the adjusted_mode into
> msm_dp_display_private->msm_dp_display_mode, and also parses and stores
> variables such as v_active_low/h_active_low/out_fmt_is_yuv_420 and ... When
> @drm_bridge_funcs.atomic_enable() is called, it copies
> msm_dp_display->msm_dp_mode into dp_panel->msm_dp_mode and initializes
> panel_info in msm_dp_display_set_mode(). Then when go to
> msm_dp_ctrl_on_stream(), the parameters are updated into the corresponding
> hardware registers.

So, if we do everything during .atomic_enable(), there would be no need
to store and/or copy anything. All the data is available and can be used
as is.

> 
> This design has been in place since the first version of the DP driver and
> has remained largely unchanged.

Yes... The point is that you are touching this piece of code anyway,
let's make it nicer.

> Originally, the drm_mode would be passed in
> two stages: from msm_dp_display->msm_dp_mode to dp_panel->msm_dp_mode. Since
> in MST mode each stream requires its own drm_mode and stored in dp_panel, we
> simplified the two-stage transfer into a single step (.mode_set() do all
> things and store in msm_dp_panel). Meanwhile we modified the
> msm_dp_display_set_mode function to accept a msm_dp_panel parameter,
> allowing the MST bridge funcs' mode_set() to reuse this part code.
> 
> The following patches:
> https://patchwork.freedesktop.org/patch/657573/?series=142207&rev=2 and
> https://patchwork.freedesktop.org/patch/657593/?series=142207&rev=2,
> introduce msm_dp_display_*_helper functions to help reuse common code across
> MST/SST/eDP drm_bridge_funcs.
> 
> If we drop msm_dp_mode from dp_panel and use drm_display_mode, it might
> introduce a large number of changes that are not directly related to MST.
> Actually i think the presence of msm_dp_display_mode seems to simplify the
> work in msm_dp_panel_timing_cfg(), this patch series we want to focus on MST
> parts, so would we consider optimizing them later?

Sure... But then you have to change two places. If you optimize it
first, you have to touch only place. And it can be even submitted
separately.

> 
> Thanks~
> > > 
> > > Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> > > Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> > > ---
> > >   drivers/gpu/drm/msm/dp/dp_display.c | 76 ++++++++++++++-----------------------
> > >   1 file changed, 29 insertions(+), 47 deletions(-)
> > > 
> > > diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> > > index 4a9b65647cdef1ed6c3bb851f93df0db8be977af..9d2db9cbd2552470a36a63f70f517c35436f7280 100644
> > > --- a/drivers/gpu/drm/msm/dp/dp_display.c
> > > +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> > > @@ -92,7 +92,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 */
> > > @@ -806,16 +805,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;
> > > +
> > > +	msm_dp_panel->msm_dp_mode.v_active_low =
> > > +		!!(adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC);
> > > +	msm_dp_panel->msm_dp_mode.h_active_low =
> > > +		!!(adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC);
> > > +	msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 =
> > > +		drm_mode_is_420_only(&msm_dp_panel->connector->display_info, adjusted_mode) &&
> > > +		msm_dp_panel->vsc_sdp_supported;
> > > -	drm_mode_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_init_panel_info(msm_dp_panel);
> > >   	return 0;
> > >   }
> > > @@ -1431,10 +1443,13 @@ bool msm_dp_needs_periph_flush(const struct msm_dp *msm_dp_display,
> > >   bool msm_dp_wide_bus_available(const 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);
> > > -	if (dp->msm_dp_mode.out_fmt_is_yuv_420)
> > > +	dp_panel = dp->panel;
> > > +
> > > +	if (dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
> > >   		return false;
> > >   	return dp->wide_bus_supported;
> > > @@ -1496,10 +1511,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);
> > > @@ -1517,15 +1528,6 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
> > >   		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);
> > > -		mutex_unlock(&msm_dp_display->event_mutex);
> > > -		return;
> > > -	}
> > 
> > It should be done other way around: keep this call and drop
> > msm_dp_bridge_mode_set().
> > 
> Emm as reply in last comments..

Yep. Drop .mode_set, the callback is even described as deprecated.

> > > -
> > > -	hpd_state =  msm_dp_display->hpd_state;
> > > -
> > >   	if (hpd_state == ST_CONNECTED && !dp->power_on) {
> > >   		msm_dp_display_host_phy_init(msm_dp_display);
> > >   		force_link_train = true;
> > > @@ -1604,33 +1606,13 @@ 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->catalog->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;
> > > +	msm_dp_display->catalog->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] 108+ messages in thread

* Re: [PATCH v2 01/38] drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid
  2025-06-25 13:32       ` Dmitry Baryshkov
@ 2025-06-27  7:49         ` Yongxing Mou
  2025-06-27 12:40           ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-06-27  7:49 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/25 21:32, Dmitry Baryshkov wrote:
> On Wed, Jun 25, 2025 at 04:43:55PM +0800, Yongxing Mou wrote:
>>
>>
>> On 2025/6/9 20:41, Dmitry Baryshkov wrote:
>>> On Mon, Jun 09, 2025 at 08:21:20PM +0800, Yongxing Mou wrote:
>>>> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>>
>>>> In preparation of DP MST where link caps are read for the
>>>> immediate downstream device and the edid is read through
>>>
>>> EDID, not edid. Please review all your patches for up/down case.
>>>
>> Got it. Thanks~
>>>> sideband messaging, split the msm_dp_panel_read_sink_caps() into
>>>> two parts which read the link parameters and the edid parts
>>>> respectively. Also drop the panel drm_edid cached as we actually
>>>> don't need it.
>>>
>>> Also => separate change.
>>>
>> Got it.
>>>>
>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>>>> ---
>>>>    drivers/gpu/drm/msm/dp/dp_display.c | 13 +++++----
>>>>    drivers/gpu/drm/msm/dp/dp_panel.c   | 55 ++++++++++++++++++++-----------------
>>>>    drivers/gpu/drm/msm/dp/dp_panel.h   |  6 ++--
>>>>    3 files changed, 40 insertions(+), 34 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
>>>> index 6f05a939ce9e648e9601597155999b6f85adfcff..4a9b65647cdef1ed6c3bb851f93df0db8be977af 100644
>>>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>>>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>>>> @@ -389,7 +389,11 @@ 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);
>>>> +	if (rc)
>>>> +		goto end;
>>>> +
>>>> +	rc = msm_dp_panel_read_edid(dp->panel, connector);
>>>>    	if (rc)
>>>>    		goto end;
>>>> @@ -720,7 +724,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);
>>>>    }
>>>> @@ -783,7 +786,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->catalog);
>>>> @@ -791,13 +794,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:
>>>> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
>>>> index 4e8ab75c771b1e3a2d62f75e9993e1062118482b..d9041e235104a74b3cc50ff2e307eae0c4301ef3 100644
>>>> --- a/drivers/gpu/drm/msm/dp/dp_panel.c
>>>> +++ b/drivers/gpu/drm/msm/dp/dp_panel.c
>>>> @@ -118,14 +118,13 @@ static u32 msm_dp_panel_get_supported_bpp(struct msm_dp_panel *msm_dp_panel,
>>>>    	return min_supported_bpp;
>>>>    }
>>>> -int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
>>>> -	struct drm_connector *connector)
>>>> +int msm_dp_panel_read_link_caps(struct msm_dp_panel *msm_dp_panel)
>>>>    {
>>>>    	int rc, bw_code;
>>>>    	int count;
>>>>    	struct msm_dp_panel_private *panel;
>>>> -	if (!msm_dp_panel || !connector) {
>>>> +	if (!msm_dp_panel) {
>>>>    		DRM_ERROR("invalid input\n");
>>>>    		return -EINVAL;
>>>>    	}
>>>> @@ -160,26 +159,29 @@ 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;
>>>> +	return rc;
>>>> +}
>>>> -	drm_edid_free(msm_dp_panel->drm_edid);
>>>> +int msm_dp_panel_read_edid(struct msm_dp_panel *msm_dp_panel, struct drm_connector *connector)
>>>> +{
>>>> +	struct msm_dp_panel_private *panel;
>>>> +	const struct drm_edid *drm_edid;
>>>> +
>>>> +	panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
>>>> -	msm_dp_panel->drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
>>>> +	drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
>>>> -	drm_edid_connector_update(connector, msm_dp_panel->drm_edid);
>>>> +	drm_edid_connector_update(connector, drm_edid);
>>>> -	if (!msm_dp_panel->drm_edid) {
>>>> +	if (!drm_edid) {
>>>>    		DRM_ERROR("panel edid read failed\n");
>>>>    		/* check edid read fail is due to unplug */
>>>>    		if (!msm_dp_catalog_link_is_connected(panel->catalog)) {
>>>> -			rc = -ETIMEDOUT;
>>>> -			goto end;
>>>> +			return -ETIMEDOUT;
>>>>    		}
>>>>    	}
>>>> -end:
>>>> -	return rc;
>>>> +	return 0;
>>>>    }
>>>>    u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel,
>>>> @@ -208,15 +210,20 @@ u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel,
>>>>    int msm_dp_panel_get_modes(struct msm_dp_panel *msm_dp_panel,
>>>>    	struct drm_connector *connector)
>>>>    {
>>>> +	struct msm_dp_panel_private *panel;
>>>> +	const struct drm_edid *drm_edid;
>>>> +
>>>>    	if (!msm_dp_panel) {
>>>>    		DRM_ERROR("invalid input\n");
>>>>    		return -EINVAL;
>>>>    	}
>>>> -	if (msm_dp_panel->drm_edid)
>>>> -		return drm_edid_connector_add_modes(connector);
>>>> +	panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
>>>> +
>>>> +	drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
>>>> +	drm_edid_connector_update(connector, drm_edid);
>>>
>>> If EDID has been read and processed after HPD high event, why do we need
>>> to re-read it again? Are we expecting that EDID will change?
>>>
>> Here we indeed don't need to read the EDID again, so we can directly call
>> drm_edid_connector_add_modes. Thanks.
>>>> -	return 0;
>>>> +	return drm_edid_connector_add_modes(connector);
>>>>    }
>>>>    static u8 msm_dp_panel_get_edid_checksum(const struct edid *edid)
>>>> @@ -229,6 +236,7 @@ static u8 msm_dp_panel_get_edid_checksum(const struct edid *edid)
>>>>    void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel)
>>>>    {
>>>>    	struct msm_dp_panel_private *panel;
>>>> +	const struct drm_edid *drm_edid;
>>>>    	if (!msm_dp_panel) {
>>>>    		DRM_ERROR("invalid input\n");
>>>> @@ -238,8 +246,13 @@ void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel)
>>>>    	panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
>>>>    	if (panel->link->sink_request & DP_TEST_LINK_EDID_READ) {
>>>> +		drm_edid = drm_edid_read_ddc(msm_dp_panel->connector, &panel->aux->ddc);
>>>
>>> And again....
>>>
>> Here we need the struct edid,since we drop the cached drm_edid, so we need
>> to read it again. Or we can return the drm_edid from msm_dp_panel_read_edid
>> and pass it to msm_dp_panel_handle_sink_request, then we don't need to read
>> drm_edid here. Emm, I'm still a bit curious why we can't cache the drm_edid?
>> It would help us to access it when needed. Emm, i see other drivers also
>> cache it.
> 
> Yes, they can cache EDID. However, in this case we don't even need it at
> all. This piece needs to be rewritten to use
> drm_dp_send_real_edid_checksum(), connector->real_edid_checksum.
> 
> Corresponding changes can be submitted separately.
> 
Got it, thanks, will separate this patch from MST patches..  Even if we 
use drm_dp_send_real_edid_checksum to send 
connector->real_edid_checksum, that’s only when the EDID state is incorrect.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c?h=v6.16-rc3#n1020 

  When the EDID is read correctly, it should send edid->checksum instead.
> 


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead
  2025-06-25 14:03       ` Dmitry Baryshkov
@ 2025-06-27  8:40         ` Yongxing Mou
  2025-06-27 12:44           ` Dmitry Baryshkov
  2025-06-27 13:37           ` Dmitry Baryshkov
  0 siblings, 2 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-06-27  8:40 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/25 22:03, Dmitry Baryshkov wrote:
> On Wed, Jun 25, 2025 at 08:34:18PM +0800, Yongxing Mou wrote:
>>
>>
>> On 2025/6/9 20:48, Dmitry Baryshkov wrote:
>>> On Mon, Jun 09, 2025 at 08:21:21PM +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 populated the panel's dp_display_mode
>>>> instead.
>>>
>>> - Why do we need to cache / copy it anyway? Can't we just pass the
>>>     corresponding drm_atomic_state / drm_crtc_state / drm_display_mode ?
>>>
>> This part works as follows: .mode_set() copies the adjusted_mode into
>> msm_dp_display_private->msm_dp_display_mode, and also parses and stores
>> variables such as v_active_low/h_active_low/out_fmt_is_yuv_420 and ... When
>> @drm_bridge_funcs.atomic_enable() is called, it copies
>> msm_dp_display->msm_dp_mode into dp_panel->msm_dp_mode and initializes
>> panel_info in msm_dp_display_set_mode(). Then when go to
>> msm_dp_ctrl_on_stream(), the parameters are updated into the corresponding
>> hardware registers.
> 
> So, if we do everything during .atomic_enable(), there would be no need
> to store and/or copy anything. All the data is available and can be used
> as is.
> 
Got it. Let me confirm—can we keep msm_dp_mode or drm_display_mode in 
msm_dp_panel? Mabey debug node will use this ..
>>
>> This design has been in place since the first version of the DP driver and
>> has remained largely unchanged.
> 
> Yes... The point is that you are touching this piece of code anyway,
> let's make it nicer.
> 
Agree with this point.
>> Originally, the drm_mode would be passed in
>> two stages: from msm_dp_display->msm_dp_mode to dp_panel->msm_dp_mode. Since
>> in MST mode each stream requires its own drm_mode and stored in dp_panel, we
>> simplified the two-stage transfer into a single step (.mode_set() do all
>> things and store in msm_dp_panel). Meanwhile we modified the
>> msm_dp_display_set_mode function to accept a msm_dp_panel parameter,
>> allowing the MST bridge funcs' mode_set() to reuse this part code.
>>
>> The following patches:
>> https://patchwork.freedesktop.org/patch/657573/?series=142207&rev=2 and
>> https://patchwork.freedesktop.org/patch/657593/?series=142207&rev=2,
>> introduce msm_dp_display_*_helper functions to help reuse common code across
>> MST/SST/eDP drm_bridge_funcs.
>>
>> If we drop msm_dp_mode from dp_panel and use drm_display_mode, it might
>> introduce a large number of changes that are not directly related to MST.
>> Actually i think the presence of msm_dp_display_mode seems to simplify the
>> work in msm_dp_panel_timing_cfg(), this patch series we want to focus on MST
>> parts, so would we consider optimizing them later?
> 
> Sure... But then you have to change two places. If you optimize it
> first, you have to touch only place. And it can be even submitted
> separately.
> 
Understood, that’s indeed the case. I just want to prioritize the MST 
patch and have it merged first, since it involves changes to lots of 
files. Thanks~~
>>
>> Thanks~
>>>>
>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>>>> ---
>>>>    drivers/gpu/drm/msm/dp/dp_display.c | 76 ++++++++++++++-----------------------
>>>>    1 file changed, 29 insertions(+), 47 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
>>>> index 4a9b65647cdef1ed6c3bb851f93df0db8be977af..9d2db9cbd2552470a36a63f70f517c35436f7280 100644
>>>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>>>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>>>> @@ -92,7 +92,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 */
>>>> @@ -806,16 +805,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;
>>>> +
>>>> +	msm_dp_panel->msm_dp_mode.v_active_low =
>>>> +		!!(adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC);
>>>> +	msm_dp_panel->msm_dp_mode.h_active_low =
>>>> +		!!(adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC);
>>>> +	msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 =
>>>> +		drm_mode_is_420_only(&msm_dp_panel->connector->display_info, adjusted_mode) &&
>>>> +		msm_dp_panel->vsc_sdp_supported;
>>>> -	drm_mode_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_init_panel_info(msm_dp_panel);
>>>>    	return 0;
>>>>    }
>>>> @@ -1431,10 +1443,13 @@ bool msm_dp_needs_periph_flush(const struct msm_dp *msm_dp_display,
>>>>    bool msm_dp_wide_bus_available(const 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);
>>>> -	if (dp->msm_dp_mode.out_fmt_is_yuv_420)
>>>> +	dp_panel = dp->panel;
>>>> +
>>>> +	if (dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
>>>>    		return false;
>>>>    	return dp->wide_bus_supported;
>>>> @@ -1496,10 +1511,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);
>>>> @@ -1517,15 +1528,6 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
>>>>    		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);
>>>> -		mutex_unlock(&msm_dp_display->event_mutex);
>>>> -		return;
>>>> -	}
>>>
>>> It should be done other way around: keep this call and drop
>>> msm_dp_bridge_mode_set().
>>>
>> Emm as reply in last comments..
> 
> Yep. Drop .mode_set, the callback is even described as deprecated.
> 
Thanks, the documentation does state that.
>>>> -
>>>> -	hpd_state =  msm_dp_display->hpd_state;
>>>> -
>>>>    	if (hpd_state == ST_CONNECTED && !dp->power_on) {
>>>>    		msm_dp_display_host_phy_init(msm_dp_display);
>>>>    		force_link_train = true;
>>>> @@ -1604,33 +1606,13 @@ 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->catalog->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;
>>>> +	msm_dp_display->catalog->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	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 01/38] drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid
  2025-06-27  7:49         ` Yongxing Mou
@ 2025-06-27 12:40           ` Dmitry Baryshkov
  2025-08-06  9:03             ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-27 12:40 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On 27/06/2025 10:49, Yongxing Mou wrote:
> 
> 
> On 2025/6/25 21:32, Dmitry Baryshkov wrote:
>> On Wed, Jun 25, 2025 at 04:43:55PM +0800, Yongxing Mou wrote:
>>>
>>>
>>> On 2025/6/9 20:41, Dmitry Baryshkov wrote:
>>>> On Mon, Jun 09, 2025 at 08:21:20PM +0800, Yongxing Mou wrote:
>>>>> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>>>
>>>>> In preparation of DP MST where link caps are read for the
>>>>> immediate downstream device and the edid is read through
>>>>
>>>> EDID, not edid. Please review all your patches for up/down case.
>>>>
>>> Got it. Thanks~
>>>>> sideband messaging, split the msm_dp_panel_read_sink_caps() into
>>>>> two parts which read the link parameters and the edid parts
>>>>> respectively. Also drop the panel drm_edid cached as we actually
>>>>> don't need it.
>>>>
>>>> Also => separate change.
>>>>
>>> Got it.
>>>>>
>>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>>>>> ---
>>>>>    drivers/gpu/drm/msm/dp/dp_display.c | 13 +++++----
>>>>>    drivers/gpu/drm/msm/dp/dp_panel.c   | 55 +++++++++++++++++++ 
>>>>> +-----------------
>>>>>    drivers/gpu/drm/msm/dp/dp_panel.h   |  6 ++--
>>>>>    3 files changed, 40 insertions(+), 34 deletions(-)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/ 
>>>>> msm/dp/dp_display.c
>>>>> index 
>>>>> 6f05a939ce9e648e9601597155999b6f85adfcff..4a9b65647cdef1ed6c3bb851f93df0db8be977af 100644
>>>>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>>>>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>>>>> @@ -389,7 +389,11 @@ 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);
>>>>> +    if (rc)
>>>>> +        goto end;
>>>>> +
>>>>> +    rc = msm_dp_panel_read_edid(dp->panel, connector);
>>>>>        if (rc)
>>>>>            goto end;
>>>>> @@ -720,7 +724,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);
>>>>>    }
>>>>> @@ -783,7 +786,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- 
>>>>> >catalog);
>>>>> @@ -791,13 +794,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:
>>>>> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/ 
>>>>> msm/dp/dp_panel.c
>>>>> index 
>>>>> 4e8ab75c771b1e3a2d62f75e9993e1062118482b..d9041e235104a74b3cc50ff2e307eae0c4301ef3 100644
>>>>> --- a/drivers/gpu/drm/msm/dp/dp_panel.c
>>>>> +++ b/drivers/gpu/drm/msm/dp/dp_panel.c
>>>>> @@ -118,14 +118,13 @@ static u32 
>>>>> msm_dp_panel_get_supported_bpp(struct msm_dp_panel *msm_dp_panel,
>>>>>        return min_supported_bpp;
>>>>>    }
>>>>> -int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
>>>>> -    struct drm_connector *connector)
>>>>> +int msm_dp_panel_read_link_caps(struct msm_dp_panel *msm_dp_panel)
>>>>>    {
>>>>>        int rc, bw_code;
>>>>>        int count;
>>>>>        struct msm_dp_panel_private *panel;
>>>>> -    if (!msm_dp_panel || !connector) {
>>>>> +    if (!msm_dp_panel) {
>>>>>            DRM_ERROR("invalid input\n");
>>>>>            return -EINVAL;
>>>>>        }
>>>>> @@ -160,26 +159,29 @@ 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;
>>>>> +    return rc;
>>>>> +}
>>>>> -    drm_edid_free(msm_dp_panel->drm_edid);
>>>>> +int msm_dp_panel_read_edid(struct msm_dp_panel *msm_dp_panel, 
>>>>> struct drm_connector *connector)
>>>>> +{
>>>>> +    struct msm_dp_panel_private *panel;
>>>>> +    const struct drm_edid *drm_edid;
>>>>> +
>>>>> +    panel = container_of(msm_dp_panel, struct 
>>>>> msm_dp_panel_private, msm_dp_panel);
>>>>> -    msm_dp_panel->drm_edid = drm_edid_read_ddc(connector, &panel- 
>>>>> >aux->ddc);
>>>>> +    drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
>>>>> -    drm_edid_connector_update(connector, msm_dp_panel->drm_edid);
>>>>> +    drm_edid_connector_update(connector, drm_edid);
>>>>> -    if (!msm_dp_panel->drm_edid) {
>>>>> +    if (!drm_edid) {
>>>>>            DRM_ERROR("panel edid read failed\n");
>>>>>            /* check edid read fail is due to unplug */
>>>>>            if (!msm_dp_catalog_link_is_connected(panel->catalog)) {
>>>>> -            rc = -ETIMEDOUT;
>>>>> -            goto end;
>>>>> +            return -ETIMEDOUT;
>>>>>            }
>>>>>        }
>>>>> -end:
>>>>> -    return rc;
>>>>> +    return 0;
>>>>>    }
>>>>>    u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel,
>>>>> @@ -208,15 +210,20 @@ u32 msm_dp_panel_get_mode_bpp(struct 
>>>>> msm_dp_panel *msm_dp_panel,
>>>>>    int msm_dp_panel_get_modes(struct msm_dp_panel *msm_dp_panel,
>>>>>        struct drm_connector *connector)
>>>>>    {
>>>>> +    struct msm_dp_panel_private *panel;
>>>>> +    const struct drm_edid *drm_edid;
>>>>> +
>>>>>        if (!msm_dp_panel) {
>>>>>            DRM_ERROR("invalid input\n");
>>>>>            return -EINVAL;
>>>>>        }
>>>>> -    if (msm_dp_panel->drm_edid)
>>>>> -        return drm_edid_connector_add_modes(connector);
>>>>> +    panel = container_of(msm_dp_panel, struct 
>>>>> msm_dp_panel_private, msm_dp_panel);
>>>>> +
>>>>> +    drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
>>>>> +    drm_edid_connector_update(connector, drm_edid);
>>>>
>>>> If EDID has been read and processed after HPD high event, why do we 
>>>> need
>>>> to re-read it again? Are we expecting that EDID will change?
>>>>
>>> Here we indeed don't need to read the EDID again, so we can directly 
>>> call
>>> drm_edid_connector_add_modes. Thanks.
>>>>> -    return 0;
>>>>> +    return drm_edid_connector_add_modes(connector);
>>>>>    }
>>>>>    static u8 msm_dp_panel_get_edid_checksum(const struct edid *edid)
>>>>> @@ -229,6 +236,7 @@ static u8 msm_dp_panel_get_edid_checksum(const 
>>>>> struct edid *edid)
>>>>>    void msm_dp_panel_handle_sink_request(struct msm_dp_panel 
>>>>> *msm_dp_panel)
>>>>>    {
>>>>>        struct msm_dp_panel_private *panel;
>>>>> +    const struct drm_edid *drm_edid;
>>>>>        if (!msm_dp_panel) {
>>>>>            DRM_ERROR("invalid input\n");
>>>>> @@ -238,8 +246,13 @@ void msm_dp_panel_handle_sink_request(struct 
>>>>> msm_dp_panel *msm_dp_panel)
>>>>>        panel = container_of(msm_dp_panel, struct 
>>>>> msm_dp_panel_private, msm_dp_panel);
>>>>>        if (panel->link->sink_request & DP_TEST_LINK_EDID_READ) {
>>>>> +        drm_edid = drm_edid_read_ddc(msm_dp_panel->connector, 
>>>>> &panel->aux->ddc);
>>>>
>>>> And again....
>>>>
>>> Here we need the struct edid,since we drop the cached drm_edid, so we 
>>> need
>>> to read it again. Or we can return the drm_edid from 
>>> msm_dp_panel_read_edid
>>> and pass it to msm_dp_panel_handle_sink_request, then we don't need 
>>> to read
>>> drm_edid here. Emm, I'm still a bit curious why we can't cache the 
>>> drm_edid?
>>> It would help us to access it when needed. Emm, i see other drivers also
>>> cache it.
>>
>> Yes, they can cache EDID. However, in this case we don't even need it at
>> all. This piece needs to be rewritten to use
>> drm_dp_send_real_edid_checksum(), connector->real_edid_checksum.
>>
>> Corresponding changes can be submitted separately.
>>
> Got it, thanks, will separate this patch from MST patches..  Even if we 
> use drm_dp_send_real_edid_checksum to send connector- 
>  >real_edid_checksum, that’s only when the EDID state is incorrect.
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/ 
> drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c?h=v6.16-rc3#n1020
>   When the EDID is read correctly, it should send edid->checksum instead.

I wonder if we should fix the drm_edid to always set real_edid_checksum 
instead.

>>
> 


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead
  2025-06-27  8:40         ` Yongxing Mou
@ 2025-06-27 12:44           ` Dmitry Baryshkov
  2025-08-06  9:22             ` Yongxing Mou
  2025-06-27 13:37           ` Dmitry Baryshkov
  1 sibling, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-27 12:44 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On 27/06/2025 11:40, Yongxing Mou wrote:
> 
> 
> On 2025/6/25 22:03, Dmitry Baryshkov wrote:
>> On Wed, Jun 25, 2025 at 08:34:18PM +0800, Yongxing Mou wrote:
>>>
>>>
>>> On 2025/6/9 20:48, Dmitry Baryshkov wrote:
>>>> On Mon, Jun 09, 2025 at 08:21:21PM +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 populated the panel's dp_display_mode
>>>>> instead.
>>>>
>>>> - Why do we need to cache / copy it anyway? Can't we just pass the
>>>>     corresponding drm_atomic_state / drm_crtc_state / 
>>>> drm_display_mode ?
>>>>
>>> This part works as follows: .mode_set() copies the adjusted_mode into
>>> msm_dp_display_private->msm_dp_display_mode, and also parses and stores
>>> variables such as v_active_low/h_active_low/out_fmt_is_yuv_420 
>>> and ... When
>>> @drm_bridge_funcs.atomic_enable() is called, it copies
>>> msm_dp_display->msm_dp_mode into dp_panel->msm_dp_mode and initializes
>>> panel_info in msm_dp_display_set_mode(). Then when go to
>>> msm_dp_ctrl_on_stream(), the parameters are updated into the 
>>> corresponding
>>> hardware registers.
>>
>> So, if we do everything during .atomic_enable(), there would be no need
>> to store and/or copy anything. All the data is available and can be used
>> as is.
>>
> Got it. Let me confirm—can we keep msm_dp_mode or drm_display_mode in 
> msm_dp_panel? Mabey debug node will use this ..

Please don't. I really dislike storing drm_atomic_state-related 
variables in a non-state structure. I think it makes it easier to 
mistakenly update or to use a stale value.

Debug code already prints modes in debugfs/dri/N/state. If we need any 
other state-related prints, they should go to the same file.

>>>
>>> This design has been in place since the first version of the DP 
>>> driver and
>>> has remained largely unchanged.
>>
>> Yes... The point is that you are touching this piece of code anyway,
>> let's make it nicer.
>>
> Agree with this point.
>>> Originally, the drm_mode would be passed in
>>> two stages: from msm_dp_display->msm_dp_mode to dp_panel- 
>>> >msm_dp_mode. Since
>>> in MST mode each stream requires its own drm_mode and stored in 
>>> dp_panel, we
>>> simplified the two-stage transfer into a single step (.mode_set() do all
>>> things and store in msm_dp_panel). Meanwhile we modified the
>>> msm_dp_display_set_mode function to accept a msm_dp_panel parameter,
>>> allowing the MST bridge funcs' mode_set() to reuse this part code.
>>>
>>> The following patches:
>>> https://patchwork.freedesktop.org/patch/657573/?series=142207&rev=2 and
>>> https://patchwork.freedesktop.org/patch/657593/?series=142207&rev=2,
>>> introduce msm_dp_display_*_helper functions to help reuse common code 
>>> across
>>> MST/SST/eDP drm_bridge_funcs.
>>>
>>> If we drop msm_dp_mode from dp_panel and use drm_display_mode, it might
>>> introduce a large number of changes that are not directly related to 
>>> MST.
>>> Actually i think the presence of msm_dp_display_mode seems to 
>>> simplify the
>>> work in msm_dp_panel_timing_cfg(), this patch series we want to focus 
>>> on MST
>>> parts, so would we consider optimizing them later?
>>
>> Sure... But then you have to change two places. If you optimize it
>> first, you have to touch only place. And it can be even submitted
>> separately.
>>
> Understood, that’s indeed the case. I just want to prioritize the MST 
> patch and have it merged first, since it involves changes to lots of 
> files. Thanks~~
>>>
>>> Thanks~
>>>>>
>>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>>>>> ---
>>>>>    drivers/gpu/drm/msm/dp/dp_display.c | 76 +++++++++++++ 
>>>>> +-----------------------
>>>>>    1 file changed, 29 insertions(+), 47 deletions(-)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/ 
>>>>> msm/dp/dp_display.c
>>>>> index 
>>>>> 4a9b65647cdef1ed6c3bb851f93df0db8be977af..9d2db9cbd2552470a36a63f70f517c35436f7280 100644
>>>>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>>>>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>>>>> @@ -92,7 +92,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 */
>>>>> @@ -806,16 +805,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;
>>>>> +
>>>>> +    msm_dp_panel->msm_dp_mode.v_active_low =
>>>>> +        !!(adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC);
>>>>> +    msm_dp_panel->msm_dp_mode.h_active_low =
>>>>> +        !!(adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC);
>>>>> +    msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 =
>>>>> +        drm_mode_is_420_only(&msm_dp_panel->connector- 
>>>>> >display_info, adjusted_mode) &&
>>>>> +        msm_dp_panel->vsc_sdp_supported;
>>>>> -    drm_mode_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_init_panel_info(msm_dp_panel);
>>>>>        return 0;
>>>>>    }
>>>>> @@ -1431,10 +1443,13 @@ bool msm_dp_needs_periph_flush(const struct 
>>>>> msm_dp *msm_dp_display,
>>>>>    bool msm_dp_wide_bus_available(const 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);
>>>>> -    if (dp->msm_dp_mode.out_fmt_is_yuv_420)
>>>>> +    dp_panel = dp->panel;
>>>>> +
>>>>> +    if (dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
>>>>>            return false;
>>>>>        return dp->wide_bus_supported;
>>>>> @@ -1496,10 +1511,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);
>>>>> @@ -1517,15 +1528,6 @@ void msm_dp_bridge_atomic_enable(struct 
>>>>> drm_bridge *drm_bridge,
>>>>>            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);
>>>>> -        mutex_unlock(&msm_dp_display->event_mutex);
>>>>> -        return;
>>>>> -    }
>>>>
>>>> It should be done other way around: keep this call and drop
>>>> msm_dp_bridge_mode_set().
>>>>
>>> Emm as reply in last comments..
>>
>> Yep. Drop .mode_set, the callback is even described as deprecated.
>>
> Thanks, the documentation does state that.
>>>>> -
>>>>> -    hpd_state =  msm_dp_display->hpd_state;
>>>>> -
>>>>>        if (hpd_state == ST_CONNECTED && !dp->power_on) {
>>>>>            msm_dp_display_host_phy_init(msm_dp_display);
>>>>>            force_link_train = true;
>>>>> @@ -1604,33 +1606,13 @@ 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->catalog->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;
>>>>> +    msm_dp_display->catalog->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] 108+ messages in thread

* Re: [PATCH v2 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead
  2025-06-27  8:40         ` Yongxing Mou
  2025-06-27 12:44           ` Dmitry Baryshkov
@ 2025-06-27 13:37           ` Dmitry Baryshkov
  1 sibling, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-06-27 13:37 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On 27/06/2025 11:40, Yongxing Mou wrote:
> 
> 
> On 2025/6/25 22:03, Dmitry Baryshkov wrote:
>> On Wed, Jun 25, 2025 at 08:34:18PM +0800, Yongxing Mou wrote:
>>>
>>>
>>> On 2025/6/9 20:48, Dmitry Baryshkov wrote:
>>>> On Mon, Jun 09, 2025 at 08:21:21PM +0800, Yongxing Mou wrote:
>>>>> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>>>

>>> Originally, the drm_mode would be passed in
>>> two stages: from msm_dp_display->msm_dp_mode to dp_panel- 
>>> >msm_dp_mode. Since
>>> in MST mode each stream requires its own drm_mode and stored in 
>>> dp_panel, we
>>> simplified the two-stage transfer into a single step (.mode_set() do all
>>> things and store in msm_dp_panel). Meanwhile we modified the
>>> msm_dp_display_set_mode function to accept a msm_dp_panel parameter,
>>> allowing the MST bridge funcs' mode_set() to reuse this part code.
>>>
>>> The following patches:
>>> https://patchwork.freedesktop.org/patch/657573/?series=142207&rev=2 and
>>> https://patchwork.freedesktop.org/patch/657593/?series=142207&rev=2,
>>> introduce msm_dp_display_*_helper functions to help reuse common code 
>>> across
>>> MST/SST/eDP drm_bridge_funcs.
>>>
>>> If we drop msm_dp_mode from dp_panel and use drm_display_mode, it might
>>> introduce a large number of changes that are not directly related to 
>>> MST.
>>> Actually i think the presence of msm_dp_display_mode seems to 
>>> simplify the
>>> work in msm_dp_panel_timing_cfg(), this patch series we want to focus 
>>> on MST
>>> parts, so would we consider optimizing them later?
>>
>> Sure... But then you have to change two places. If you optimize it
>> first, you have to touch only place. And it can be even submitted
>> separately.
>>
> Understood, that’s indeed the case. I just want to prioritize the MST 
> patch and have it merged first, since it involves changes to lots of 
> files. Thanks~~

I'm sorry, I hit enter too quickly. The MST will not be merged until we 
get DP HPD rework in, that's a prerequisite from my side. So, while that 
is getting sorted out, we can fix minor issues and perform minor 
cleanups, like the checksums or drm_mode refactoring.

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 01/38] drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid
  2025-06-27 12:40           ` Dmitry Baryshkov
@ 2025-08-06  9:03             ` Yongxing Mou
  2025-08-06 10:39               ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-08-06  9:03 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/27 20:40, Dmitry Baryshkov wrote:
> On 27/06/2025 10:49, Yongxing Mou wrote:
>>
>>
>> On 2025/6/25 21:32, Dmitry Baryshkov wrote:
>>> On Wed, Jun 25, 2025 at 04:43:55PM +0800, Yongxing Mou wrote:
>>>>
>>>>
>>>> On 2025/6/9 20:41, Dmitry Baryshkov wrote:
>>>>> On Mon, Jun 09, 2025 at 08:21:20PM +0800, Yongxing Mou wrote:
>>>>>> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>>>>
>>>>>> In preparation of DP MST where link caps are read for the
>>>>>> immediate downstream device and the edid is read through
>>>>>
>>>>> EDID, not edid. Please review all your patches for up/down case.
>>>>>
>>>> Got it. Thanks~
>>>>>> sideband messaging, split the msm_dp_panel_read_sink_caps() into
>>>>>> two parts which read the link parameters and the edid parts
>>>>>> respectively. Also drop the panel drm_edid cached as we actually
>>>>>> don't need it.
>>>>>
>>>>> Also => separate change.
>>>>>
>>>> Got it.
>>>>>>
>>>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>>>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>>>>>> ---
>>>>>>    drivers/gpu/drm/msm/dp/dp_display.c | 13 +++++----
>>>>>>    drivers/gpu/drm/msm/dp/dp_panel.c   | 55 +++++++++++++++++++ 
>>>>>> +-----------------
>>>>>>    drivers/gpu/drm/msm/dp/dp_panel.h   |  6 ++--
>>>>>>    3 files changed, 40 insertions(+), 34 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/ 
>>>>>> drm/ msm/dp/dp_display.c
>>>>>> index 
>>>>>> 6f05a939ce9e648e9601597155999b6f85adfcff..4a9b65647cdef1ed6c3bb851f93df0db8be977af 100644
>>>>>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>>>>>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>>>>>> @@ -389,7 +389,11 @@ 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);
>>>>>> +    if (rc)
>>>>>> +        goto end;
>>>>>> +
>>>>>> +    rc = msm_dp_panel_read_edid(dp->panel, connector);
>>>>>>        if (rc)
>>>>>>            goto end;
>>>>>> @@ -720,7 +724,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);
>>>>>>    }
>>>>>> @@ -783,7 +786,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- 
>>>>>> >catalog);
>>>>>> @@ -791,13 +794,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:
>>>>>> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/ 
>>>>>> msm/dp/dp_panel.c
>>>>>> index 
>>>>>> 4e8ab75c771b1e3a2d62f75e9993e1062118482b..d9041e235104a74b3cc50ff2e307eae0c4301ef3 100644
>>>>>> --- a/drivers/gpu/drm/msm/dp/dp_panel.c
>>>>>> +++ b/drivers/gpu/drm/msm/dp/dp_panel.c
>>>>>> @@ -118,14 +118,13 @@ static u32 
>>>>>> msm_dp_panel_get_supported_bpp(struct msm_dp_panel *msm_dp_panel,
>>>>>>        return min_supported_bpp;
>>>>>>    }
>>>>>> -int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
>>>>>> -    struct drm_connector *connector)
>>>>>> +int msm_dp_panel_read_link_caps(struct msm_dp_panel *msm_dp_panel)
>>>>>>    {
>>>>>>        int rc, bw_code;
>>>>>>        int count;
>>>>>>        struct msm_dp_panel_private *panel;
>>>>>> -    if (!msm_dp_panel || !connector) {
>>>>>> +    if (!msm_dp_panel) {
>>>>>>            DRM_ERROR("invalid input\n");
>>>>>>            return -EINVAL;
>>>>>>        }
>>>>>> @@ -160,26 +159,29 @@ 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;
>>>>>> +    return rc;
>>>>>> +}
>>>>>> -    drm_edid_free(msm_dp_panel->drm_edid);
>>>>>> +int msm_dp_panel_read_edid(struct msm_dp_panel *msm_dp_panel, 
>>>>>> struct drm_connector *connector)
>>>>>> +{
>>>>>> +    struct msm_dp_panel_private *panel;
>>>>>> +    const struct drm_edid *drm_edid;
>>>>>> +
>>>>>> +    panel = container_of(msm_dp_panel, struct 
>>>>>> msm_dp_panel_private, msm_dp_panel);
>>>>>> -    msm_dp_panel->drm_edid = drm_edid_read_ddc(connector, &panel- 
>>>>>> >aux->ddc);
>>>>>> +    drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
>>>>>> -    drm_edid_connector_update(connector, msm_dp_panel->drm_edid);
>>>>>> +    drm_edid_connector_update(connector, drm_edid);
>>>>>> -    if (!msm_dp_panel->drm_edid) {
>>>>>> +    if (!drm_edid) {
>>>>>>            DRM_ERROR("panel edid read failed\n");
>>>>>>            /* check edid read fail is due to unplug */
>>>>>>            if (!msm_dp_catalog_link_is_connected(panel->catalog)) {
>>>>>> -            rc = -ETIMEDOUT;
>>>>>> -            goto end;
>>>>>> +            return -ETIMEDOUT;
>>>>>>            }
>>>>>>        }
>>>>>> -end:
>>>>>> -    return rc;
>>>>>> +    return 0;
>>>>>>    }
>>>>>>    u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel,
>>>>>> @@ -208,15 +210,20 @@ u32 msm_dp_panel_get_mode_bpp(struct 
>>>>>> msm_dp_panel *msm_dp_panel,
>>>>>>    int msm_dp_panel_get_modes(struct msm_dp_panel *msm_dp_panel,
>>>>>>        struct drm_connector *connector)
>>>>>>    {
>>>>>> +    struct msm_dp_panel_private *panel;
>>>>>> +    const struct drm_edid *drm_edid;
>>>>>> +
>>>>>>        if (!msm_dp_panel) {
>>>>>>            DRM_ERROR("invalid input\n");
>>>>>>            return -EINVAL;
>>>>>>        }
>>>>>> -    if (msm_dp_panel->drm_edid)
>>>>>> -        return drm_edid_connector_add_modes(connector);
>>>>>> +    panel = container_of(msm_dp_panel, struct 
>>>>>> msm_dp_panel_private, msm_dp_panel);
>>>>>> +
>>>>>> +    drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
>>>>>> +    drm_edid_connector_update(connector, drm_edid);
>>>>>
>>>>> If EDID has been read and processed after HPD high event, why do we 
>>>>> need
>>>>> to re-read it again? Are we expecting that EDID will change?
>>>>>
>>>> Here we indeed don't need to read the EDID again, so we can directly 
>>>> call
>>>> drm_edid_connector_add_modes. Thanks.
>>>>>> -    return 0;
>>>>>> +    return drm_edid_connector_add_modes(connector);
>>>>>>    }
>>>>>>    static u8 msm_dp_panel_get_edid_checksum(const struct edid *edid)
>>>>>> @@ -229,6 +236,7 @@ static u8 msm_dp_panel_get_edid_checksum(const 
>>>>>> struct edid *edid)
>>>>>>    void msm_dp_panel_handle_sink_request(struct msm_dp_panel 
>>>>>> *msm_dp_panel)
>>>>>>    {
>>>>>>        struct msm_dp_panel_private *panel;
>>>>>> +    const struct drm_edid *drm_edid;
>>>>>>        if (!msm_dp_panel) {
>>>>>>            DRM_ERROR("invalid input\n");
>>>>>> @@ -238,8 +246,13 @@ void msm_dp_panel_handle_sink_request(struct 
>>>>>> msm_dp_panel *msm_dp_panel)
>>>>>>        panel = container_of(msm_dp_panel, struct 
>>>>>> msm_dp_panel_private, msm_dp_panel);
>>>>>>        if (panel->link->sink_request & DP_TEST_LINK_EDID_READ) {
>>>>>> +        drm_edid = drm_edid_read_ddc(msm_dp_panel->connector, 
>>>>>> &panel->aux->ddc);
>>>>>
>>>>> And again....
>>>>>
>>>> Here we need the struct edid,since we drop the cached drm_edid, so 
>>>> we need
>>>> to read it again. Or we can return the drm_edid from 
>>>> msm_dp_panel_read_edid
>>>> and pass it to msm_dp_panel_handle_sink_request, then we don't need 
>>>> to read
>>>> drm_edid here. Emm, I'm still a bit curious why we can't cache the 
>>>> drm_edid?
>>>> It would help us to access it when needed. Emm, i see other drivers 
>>>> also
>>>> cache it.
>>>
>>> Yes, they can cache EDID. However, in this case we don't even need it at
>>> all. This piece needs to be rewritten to use
>>> drm_dp_send_real_edid_checksum(), connector->real_edid_checksum.
>>>
>>> Corresponding changes can be submitted separately.
>>>
>> Got it, thanks, will separate this patch from MST patches..  Even if 
>> we use drm_dp_send_real_edid_checksum to send connector- 
>>  >real_edid_checksum, that’s only when the EDID state is incorrect.
>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/ 
>> tree/ drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c? 
>> h=v6.16-rc3#n1020
>>   When the EDID is read correctly, it should send edid->checksum instead.
> 
> I wonder if we should fix the drm_edid to always set real_edid_checksum 
> instead.
> 
Emm, can i understand that is another issue exist in the currently DRM..

>>>
>>
> 
> 


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead
  2025-06-27 12:44           ` Dmitry Baryshkov
@ 2025-08-06  9:22             ` Yongxing Mou
  2025-08-06 10:41               ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-08-06  9:22 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/27 20:44, Dmitry Baryshkov wrote:
> On 27/06/2025 11:40, Yongxing Mou wrote:
>>
>>
>> On 2025/6/25 22:03, Dmitry Baryshkov wrote:
>>> On Wed, Jun 25, 2025 at 08:34:18PM +0800, Yongxing Mou wrote:
>>>>
>>>>
>>>> On 2025/6/9 20:48, Dmitry Baryshkov wrote:
>>>>> On Mon, Jun 09, 2025 at 08:21:21PM +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 populated the panel's dp_display_mode
>>>>>> instead.
>>>>>
>>>>> - Why do we need to cache / copy it anyway? Can't we just pass the
>>>>>     corresponding drm_atomic_state / drm_crtc_state / 
>>>>> drm_display_mode ?
>>>>>
>>>> This part works as follows: .mode_set() copies the adjusted_mode into
>>>> msm_dp_display_private->msm_dp_display_mode, and also parses and stores
>>>> variables such as v_active_low/h_active_low/out_fmt_is_yuv_420 
>>>> and ... When
>>>> @drm_bridge_funcs.atomic_enable() is called, it copies
>>>> msm_dp_display->msm_dp_mode into dp_panel->msm_dp_mode and initializes
>>>> panel_info in msm_dp_display_set_mode(). Then when go to
>>>> msm_dp_ctrl_on_stream(), the parameters are updated into the 
>>>> corresponding
>>>> hardware registers.
>>>
>>> So, if we do everything during .atomic_enable(), there would be no need
>>> to store and/or copy anything. All the data is available and can be used
>>> as is.
>>>
>> Got it. Let me confirm—can we keep msm_dp_mode or drm_display_mode in 
>> msm_dp_panel? Mabey debug node will use this ..
> 
> Please don't. I really dislike storing drm_atomic_state-related 
> variables in a non-state structure. I think it makes it easier to 
> mistakenly update or to use a stale value.
> 
> Debug code already prints modes in debugfs/dri/N/state. If we need any 
> other state-related prints, they should go to the same file.
> 
Hi, I got this point.. i go through the driver. since lots of funcs used 
msm_dp_mode cached. so maybe it is not a very small change.. I’d like to 
prioritize MST first, and then submit this patch once I got time..

>>>>
>>>> This design has been in place since the first version of the DP 
>>>> driver and
>>>> has remained largely unchanged.
>>>
>>> Yes... The point is that you are touching this piece of code anyway,
>>> let's make it nicer.
>>>
>> Agree with this point.
>>>> Originally, the drm_mode would be passed in
>>>> two stages: from msm_dp_display->msm_dp_mode to dp_panel- 
>>>> >msm_dp_mode. Since
>>>> in MST mode each stream requires its own drm_mode and stored in 
>>>> dp_panel, we
>>>> simplified the two-stage transfer into a single step (.mode_set() do 
>>>> all
>>>> things and store in msm_dp_panel). Meanwhile we modified the
>>>> msm_dp_display_set_mode function to accept a msm_dp_panel parameter,
>>>> allowing the MST bridge funcs' mode_set() to reuse this part code.
>>>>
>>>> The following patches:
>>>> https://patchwork.freedesktop.org/patch/657573/?series=142207&rev=2 and
>>>> https://patchwork.freedesktop.org/patch/657593/?series=142207&rev=2,
>>>> introduce msm_dp_display_*_helper functions to help reuse common 
>>>> code across
>>>> MST/SST/eDP drm_bridge_funcs.
>>>>
>>>> If we drop msm_dp_mode from dp_panel and use drm_display_mode, it might
>>>> introduce a large number of changes that are not directly related to 
>>>> MST.
>>>> Actually i think the presence of msm_dp_display_mode seems to 
>>>> simplify the
>>>> work in msm_dp_panel_timing_cfg(), this patch series we want to 
>>>> focus on MST
>>>> parts, so would we consider optimizing them later?
>>>
>>> Sure... But then you have to change two places. If you optimize it
>>> first, you have to touch only place. And it can be even submitted
>>> separately.
>>>
>> Understood, that’s indeed the case. I just want to prioritize the MST 
>> patch and have it merged first, since it involves changes to lots of 
>> files. Thanks~~
>>>>
>>>> Thanks~
>>>>>>
>>>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>>>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>>>>>> ---
>>>>>>    drivers/gpu/drm/msm/dp/dp_display.c | 76 +++++++++++++ 
>>>>>> +-----------------------
>>>>>>    1 file changed, 29 insertions(+), 47 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/ 
>>>>>> drm/ msm/dp/dp_display.c
>>>>>> index 
>>>>>> 4a9b65647cdef1ed6c3bb851f93df0db8be977af..9d2db9cbd2552470a36a63f70f517c35436f7280 100644
>>>>>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>>>>>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>>>>>> @@ -92,7 +92,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 */
>>>>>> @@ -806,16 +805,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;
>>>>>> +
>>>>>> +    msm_dp_panel->msm_dp_mode.v_active_low =
>>>>>> +        !!(adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC);
>>>>>> +    msm_dp_panel->msm_dp_mode.h_active_low =
>>>>>> +        !!(adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC);
>>>>>> +    msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 =
>>>>>> +        drm_mode_is_420_only(&msm_dp_panel->connector- 
>>>>>> >display_info, adjusted_mode) &&
>>>>>> +        msm_dp_panel->vsc_sdp_supported;
>>>>>> -    drm_mode_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_init_panel_info(msm_dp_panel);
>>>>>>        return 0;
>>>>>>    }
>>>>>> @@ -1431,10 +1443,13 @@ bool msm_dp_needs_periph_flush(const 
>>>>>> struct msm_dp *msm_dp_display,
>>>>>>    bool msm_dp_wide_bus_available(const 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);
>>>>>> -    if (dp->msm_dp_mode.out_fmt_is_yuv_420)
>>>>>> +    dp_panel = dp->panel;
>>>>>> +
>>>>>> +    if (dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
>>>>>>            return false;
>>>>>>        return dp->wide_bus_supported;
>>>>>> @@ -1496,10 +1511,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);
>>>>>> @@ -1517,15 +1528,6 @@ void msm_dp_bridge_atomic_enable(struct 
>>>>>> drm_bridge *drm_bridge,
>>>>>>            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);
>>>>>> -        mutex_unlock(&msm_dp_display->event_mutex);
>>>>>> -        return;
>>>>>> -    }
>>>>>
>>>>> It should be done other way around: keep this call and drop
>>>>> msm_dp_bridge_mode_set().
>>>>>
>>>> Emm as reply in last comments..
>>>
>>> Yep. Drop .mode_set, the callback is even described as deprecated.
>>>
>> Thanks, the documentation does state that.
>>>>>> -
>>>>>> -    hpd_state =  msm_dp_display->hpd_state;
>>>>>> -
>>>>>>        if (hpd_state == ST_CONNECTED && !dp->power_on) {
>>>>>>            msm_dp_display_host_phy_init(msm_dp_display);
>>>>>>            force_link_train = true;
>>>>>> @@ -1604,33 +1606,13 @@ 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->catalog->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;
>>>>>> +    msm_dp_display->catalog->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	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 03/38] drm/msm/dp: break up dp_display_enable into two parts
  2025-06-09 12:59   ` Dmitry Baryshkov
@ 2025-08-06  9:24     ` Yongxing Mou
  2025-08-13  9:36     ` Yongxing Mou
  1 sibling, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-08-06  9:24 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/9 20:59, Dmitry Baryshkov wrote:
> On Mon, Jun 09, 2025 at 08:21:22PM +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. Splite these two parts into
>> prepare/enable APIs, to support MST bridges_enable inserte
> 
> typos
> 
..Got it. thanks.
>> the MST payloads funcs between enable stream_clks and programe
>> register.
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>> ---
>>   drivers/gpu/drm/msm/dp/dp_ctrl.c    | 57 +++++++++++++--------
>>   drivers/gpu/drm/msm/dp/dp_ctrl.h    |  3 +-
>>   drivers/gpu/drm/msm/dp/dp_display.c | 99 +++++++++++++++++++++++++++----------
>>   drivers/gpu/drm/msm/dp/dp_display.h |  1 +
>>   4 files changed, 111 insertions(+), 49 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> index a50bfafbb4ea85c114c958ea0ed24362a1f23136..1e13ca81b0155a37a4ed7a2e83c918293d703a37 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> @@ -1980,40 +1980,61 @@ static int msm_dp_ctrl_link_retrain(struct msm_dp_ctrl_private *ctrl)
>>   	return msm_dp_ctrl_setup_main_link(ctrl, &training_step);
>>   }
>>   
>> -int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train)
>> +int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train)
>>   {
>>   	int ret = 0;
>> -	bool mainlink_ready = false;
>>   	struct msm_dp_ctrl_private *ctrl;
>> -	unsigned long pixel_rate;
>> -	unsigned long pixel_rate_orig;
>>   
>>   	if (!msm_dp_ctrl)
>>   		return -EINVAL;
>>   
>>   	ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
>>   
>> -	pixel_rate = pixel_rate_orig = ctrl->panel->msm_dp_mode.drm_mode.clock;
>> -
>> -	if (msm_dp_ctrl->wide_bus_en || ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420)
>> -		pixel_rate >>= 1;
>> -
>> -	drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d, pixel_rate=%lu\n",
>> -		ctrl->link->link_params.rate,
>> -		ctrl->link->link_params.num_lanes, pixel_rate);
>> +	drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d\n",
>> +		   ctrl->link->link_params.rate,
>> +		   ctrl->link->link_params.num_lanes);
> 
> Please don't mix whitespace changes with the actual code changes. It
> makes reviewing the patch much harder.
> 
Okay.
>>   
>>   	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);
>> +		   "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);
>>   
>>   	if (!ctrl->link_clks_on) { /* link clk is off */
>>   		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);
>> @@ -2031,12 +2052,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 b7abfedbf5749c25877a0b8ba3af3d8ed4b23d67..42745c912adbad7221c78f5cecefa730bfda1e75 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
>> @@ -18,7 +18,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 *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_link(struct msm_dp_ctrl *msm_dp_ctrl);
>>   void msm_dp_ctrl_off(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 9d2db9cbd2552470a36a63f70f517c35436f7280..5ac5dcf35b789f2bda052a2c17aae20aa48d8e18 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>> @@ -831,7 +831,37 @@ 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);
>> +	if (msm_dp_display->prepared) {
>> +		drm_dbg_dp(dp->drm_dev, "Link already setup, return\n");
>> +		return 0;
>> +	}
> 
> How can it be prepared here? It is called at the beginning of the
> .atomic_enable() only, so there is no way this can be true.
> 
Thanks. will update it..
>> +
>> +	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->hpd_state == ST_CONNECTED && !msm_dp_display->power_on) {
>> +		msm_dp_display_host_phy_init(dp);
>> +		force_link_train = true;
>> +	}
>> +
>> +	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;
>> @@ -842,7 +872,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;
>>   
>> @@ -872,13 +902,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 */
>> @@ -889,6 +916,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) {
>>   		/*
>> @@ -1506,9 +1541,8 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
>>   	struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge);
>>   	struct msm_dp *dp = msm_dp_bridge->msm_dp_display;
>>   	int rc = 0;
>> +
>>   	struct msm_dp_display_private *msm_dp_display;
>> -	u32 hpd_state;
>> -	bool force_link_train = false;
>>   
>>   	msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
>>   
>> @@ -1516,29 +1550,23 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
>>   		msm_dp_hpd_plug_handle(msm_dp_display, 0);
>>   
>>   	mutex_lock(&msm_dp_display->event_mutex);
>> -	if (pm_runtime_resume_and_get(&dp->pdev->dev)) {
>> -		DRM_ERROR("failed to pm_runtime_resume\n");
>> -		mutex_unlock(&msm_dp_display->event_mutex);
>> -		return;
>> -	}
>>   
>> -	hpd_state = msm_dp_display->hpd_state;
>> -	if (hpd_state == ST_DISCONNECT_PENDING) {
>> +	rc = msm_dp_display_prepare(msm_dp_display);
>> +	if (rc) {
>> +		DRM_ERROR("DP display prepare failed, rc=%d\n", rc);
>>   		mutex_unlock(&msm_dp_display->event_mutex);
>>   		return;
>>   	}
>>   
>> -	if (hpd_state == ST_CONNECTED && !dp->power_on) {
>> -		msm_dp_display_host_phy_init(msm_dp_display);
>> -		force_link_train = true;
>> -	}
>> -
>> -	msm_dp_display_enable(msm_dp_display, force_link_train);
>> -
>> -	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);
>> +	if (dp->prepared) {
>> +		rc = msm_dp_display_enable(msm_dp_display);
>> +		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);
>> +		}
>>   	}
>>   
>>   	/* completed connection */
>> @@ -1560,6 +1588,20 @@ 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;
>> +
>> +	if (!msm_dp_display->prepared) {
>> +		drm_dbg_dp(dp->drm_dev, "Link already setup, return\n");
>> +		return;
>> +	}
> 
> Why/ how is it possible?
> 
will check it.
>> +
>> +	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)
>>   {
>> @@ -1580,6 +1622,8 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
>>   		drm_dbg_dp(dp->drm_dev, "type=%d wrong hpd_state=%d\n",
>>   			   dp->connector_type, hpd_state);
>>   
>> +	msm_dp_display_audio_notify_disable(msm_dp_display);
>> +
>>   	msm_dp_display_disable(msm_dp_display);
>>   
>>   	hpd_state =  msm_dp_display->hpd_state;
>> @@ -1588,9 +1632,10 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
>>   		msm_dp_display->hpd_state = ST_DISCONNECTED;
>>   	}
>>   
>> +	msm_dp_display_unprepare(msm_dp_display);
>> +
>>   	drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type);
>>   
>> -	pm_runtime_put_sync(&dp->pdev->dev);
>>   	mutex_unlock(&msm_dp_display->event_mutex);
>>   }
>>   
>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
>> index cc6e2cab36e9c0b1527ff292e547cbb4d69fd95c..2394840e9f28e136705004c3e6af93fbe13c33c5 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_display.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
>> @@ -19,6 +19,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	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 04/38] drm/msm/dp: re-arrange dp_display_disable() into functional parts
  2025-06-09 13:05   ` Dmitry Baryshkov
@ 2025-08-06  9:30     ` Yongxing Mou
  0 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-08-06  9:30 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/9 21:05, Dmitry Baryshkov wrote:
> On Mon, Jun 09, 2025 at 08:21:23PM +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.
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>> ---
>>   drivers/gpu/drm/msm/dp/dp_ctrl.c    | 29 +++++++++++++++--------------
>>   drivers/gpu/drm/msm/dp/dp_ctrl.h    |  5 ++++-
>>   drivers/gpu/drm/msm/dp/dp_display.c | 16 +++++++---------
>>   3 files changed, 26 insertions(+), 24 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> index 1e13ca81b0155a37a4ed7a2e83c918293d703a37..1ce3cca121d0c56b493e282c76eb9202371564cf 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> @@ -2081,30 +2081,31 @@ 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_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl)
>>   {
>>   	struct msm_dp_ctrl_private *ctrl;
>> -	struct phy *phy;
>>   
>>   	ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
>> -	phy = ctrl->phy;
>> -
>>   	msm_dp_catalog_panel_disable_vsc_sdp(ctrl->catalog);
>> +}
>>   
>> -	/* set dongle to D3 (power off) mode */
>> -	msm_dp_link_psm_config(ctrl->link, &ctrl->panel->link_info, true);
>> +void msm_dp_ctrl_psm_config(struct msm_dp_ctrl *msm_dp_ctrl)
> 
> I'm not a fan of (almost) one-line wrappers.
> after reabse to latest code. Here can remove wrappers..

>> +{
>> +	struct msm_dp_ctrl_private *ctrl;
>>   
>> -	msm_dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);
>> +	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;
>> -	}
>> +	/* set dongle to D3 (power off) mode */
>> +	msm_dp_link_psm_config(ctrl->link, &ctrl->panel->link_info, true);
>> +}
>>   
>> -	dev_pm_opp_set_rate(ctrl->dev, 0);
>> -	msm_dp_ctrl_link_clk_disable(&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;
>>   
>> -	phy_power_off(phy);
>> +	ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
>> +	phy = ctrl->phy;
>>   
>>   	/* aux channel down, reinit phy */
>>   	phy_exit(phy);
>> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
>> index 42745c912adbad7221c78f5cecefa730bfda1e75..edbe5766db74c4e4179141d895f9cb85e514f29b 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
>> @@ -20,7 +20,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 *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_link(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);
>> @@ -42,4 +41,8 @@ void msm_dp_ctrl_config_psr(struct msm_dp_ctrl *msm_dp_ctrl);
>>   int msm_dp_ctrl_core_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl);
>>   void msm_dp_ctrl_core_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl);
>>   
>> +void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl);
>> +void msm_dp_ctrl_psm_config(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 5ac5dcf35b789f2bda052a2c17aae20aa48d8e18..a5ca498cb970d0c6a4095b0b7fc6269c2dc3ad31 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>> @@ -925,17 +925,15 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
>>   	if (!msm_dp_display->power_on)
>>   		return 0;
>>   
>> +	msm_dp_ctrl_clear_vsc_sdp_pkt(dp->ctrl);
>> +
>> +	/* 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);
>> +		msm_dp_ctrl_psm_config(dp->ctrl);
>> +		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
>> -		 * dongle unplugged out of DUT
>> -		 */
>>   		msm_dp_ctrl_off(dp->ctrl);
>>   		msm_dp_display_host_phy_exit(dp);
>>   	}
>>
>> -- 
>> 2.34.1
>>
> 


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 01/38] drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid
  2025-08-06  9:03             ` Yongxing Mou
@ 2025-08-06 10:39               ` Dmitry Baryshkov
  0 siblings, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-08-06 10:39 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On 06/08/2025 14:33, Yongxing Mou wrote:
> 
> 
> On 2025/6/27 20:40, Dmitry Baryshkov wrote:
>> On 27/06/2025 10:49, Yongxing Mou wrote:
>>>
>>>
>>> On 2025/6/25 21:32, Dmitry Baryshkov wrote:
>>>> On Wed, Jun 25, 2025 at 04:43:55PM +0800, Yongxing Mou wrote:
>>>>>
>>>>>
>>>>> On 2025/6/9 20:41, Dmitry Baryshkov wrote:
>>>>>> On Mon, Jun 09, 2025 at 08:21:20PM +0800, Yongxing Mou wrote:
>>>>>>> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>>>>>
>>>>>>> In preparation of DP MST where link caps are read for the
>>>>>>> immediate downstream device and the edid is read through
>>>>>>
>>>>>> EDID, not edid. Please review all your patches for up/down case.
>>>>>>
>>>>> Got it. Thanks~
>>>>>>> sideband messaging, split the msm_dp_panel_read_sink_caps() into
>>>>>>> two parts which read the link parameters and the edid parts
>>>>>>> respectively. Also drop the panel drm_edid cached as we actually
>>>>>>> don't need it.
>>>>>>
>>>>>> Also => separate change.
>>>>>>
>>>>> Got it.
>>>>>>>
>>>>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>>>>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>>>>>>> ---
>>>>>>>    drivers/gpu/drm/msm/dp/dp_display.c | 13 +++++----
>>>>>>>    drivers/gpu/drm/msm/dp/dp_panel.c   | 55 +++++++++++++++++++ 
>>>>>>> +-----------------
>>>>>>>    drivers/gpu/drm/msm/dp/dp_panel.h   |  6 ++--
>>>>>>>    3 files changed, 40 insertions(+), 34 deletions(-)
>>>>>>>
>>>>>>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/ 
>>>>>>> drm/ msm/dp/dp_display.c
>>>>>>> index 
>>>>>>> 6f05a939ce9e648e9601597155999b6f85adfcff..4a9b65647cdef1ed6c3bb851f93df0db8be977af 100644
>>>>>>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>>>>>>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>>>>>>> @@ -389,7 +389,11 @@ 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);
>>>>>>> +    if (rc)
>>>>>>> +        goto end;
>>>>>>> +
>>>>>>> +    rc = msm_dp_panel_read_edid(dp->panel, connector);
>>>>>>>        if (rc)
>>>>>>>            goto end;
>>>>>>> @@ -720,7 +724,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);
>>>>>>>    }
>>>>>>> @@ -783,7 +786,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- 
>>>>>>> >catalog);
>>>>>>> @@ -791,13 +794,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:
>>>>>>> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/ 
>>>>>>> msm/dp/dp_panel.c
>>>>>>> index 
>>>>>>> 4e8ab75c771b1e3a2d62f75e9993e1062118482b..d9041e235104a74b3cc50ff2e307eae0c4301ef3 100644
>>>>>>> --- a/drivers/gpu/drm/msm/dp/dp_panel.c
>>>>>>> +++ b/drivers/gpu/drm/msm/dp/dp_panel.c
>>>>>>> @@ -118,14 +118,13 @@ static u32 
>>>>>>> msm_dp_panel_get_supported_bpp(struct msm_dp_panel *msm_dp_panel,
>>>>>>>        return min_supported_bpp;
>>>>>>>    }
>>>>>>> -int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
>>>>>>> -    struct drm_connector *connector)
>>>>>>> +int msm_dp_panel_read_link_caps(struct msm_dp_panel *msm_dp_panel)
>>>>>>>    {
>>>>>>>        int rc, bw_code;
>>>>>>>        int count;
>>>>>>>        struct msm_dp_panel_private *panel;
>>>>>>> -    if (!msm_dp_panel || !connector) {
>>>>>>> +    if (!msm_dp_panel) {
>>>>>>>            DRM_ERROR("invalid input\n");
>>>>>>>            return -EINVAL;
>>>>>>>        }
>>>>>>> @@ -160,26 +159,29 @@ 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;
>>>>>>> +    return rc;
>>>>>>> +}
>>>>>>> -    drm_edid_free(msm_dp_panel->drm_edid);
>>>>>>> +int msm_dp_panel_read_edid(struct msm_dp_panel *msm_dp_panel, 
>>>>>>> struct drm_connector *connector)
>>>>>>> +{
>>>>>>> +    struct msm_dp_panel_private *panel;
>>>>>>> +    const struct drm_edid *drm_edid;
>>>>>>> +
>>>>>>> +    panel = container_of(msm_dp_panel, struct 
>>>>>>> msm_dp_panel_private, msm_dp_panel);
>>>>>>> -    msm_dp_panel->drm_edid = drm_edid_read_ddc(connector, 
>>>>>>> &panel- >aux->ddc);
>>>>>>> +    drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
>>>>>>> -    drm_edid_connector_update(connector, msm_dp_panel->drm_edid);
>>>>>>> +    drm_edid_connector_update(connector, drm_edid);
>>>>>>> -    if (!msm_dp_panel->drm_edid) {
>>>>>>> +    if (!drm_edid) {
>>>>>>>            DRM_ERROR("panel edid read failed\n");
>>>>>>>            /* check edid read fail is due to unplug */
>>>>>>>            if (!msm_dp_catalog_link_is_connected(panel->catalog)) {
>>>>>>> -            rc = -ETIMEDOUT;
>>>>>>> -            goto end;
>>>>>>> +            return -ETIMEDOUT;
>>>>>>>            }
>>>>>>>        }
>>>>>>> -end:
>>>>>>> -    return rc;
>>>>>>> +    return 0;
>>>>>>>    }
>>>>>>>    u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel,
>>>>>>> @@ -208,15 +210,20 @@ u32 msm_dp_panel_get_mode_bpp(struct 
>>>>>>> msm_dp_panel *msm_dp_panel,
>>>>>>>    int msm_dp_panel_get_modes(struct msm_dp_panel *msm_dp_panel,
>>>>>>>        struct drm_connector *connector)
>>>>>>>    {
>>>>>>> +    struct msm_dp_panel_private *panel;
>>>>>>> +    const struct drm_edid *drm_edid;
>>>>>>> +
>>>>>>>        if (!msm_dp_panel) {
>>>>>>>            DRM_ERROR("invalid input\n");
>>>>>>>            return -EINVAL;
>>>>>>>        }
>>>>>>> -    if (msm_dp_panel->drm_edid)
>>>>>>> -        return drm_edid_connector_add_modes(connector);
>>>>>>> +    panel = container_of(msm_dp_panel, struct 
>>>>>>> msm_dp_panel_private, msm_dp_panel);
>>>>>>> +
>>>>>>> +    drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc);
>>>>>>> +    drm_edid_connector_update(connector, drm_edid);
>>>>>>
>>>>>> If EDID has been read and processed after HPD high event, why do 
>>>>>> we need
>>>>>> to re-read it again? Are we expecting that EDID will change?
>>>>>>
>>>>> Here we indeed don't need to read the EDID again, so we can 
>>>>> directly call
>>>>> drm_edid_connector_add_modes. Thanks.
>>>>>>> -    return 0;
>>>>>>> +    return drm_edid_connector_add_modes(connector);
>>>>>>>    }
>>>>>>>    static u8 msm_dp_panel_get_edid_checksum(const struct edid *edid)
>>>>>>> @@ -229,6 +236,7 @@ static u8 
>>>>>>> msm_dp_panel_get_edid_checksum(const struct edid *edid)
>>>>>>>    void msm_dp_panel_handle_sink_request(struct msm_dp_panel 
>>>>>>> *msm_dp_panel)
>>>>>>>    {
>>>>>>>        struct msm_dp_panel_private *panel;
>>>>>>> +    const struct drm_edid *drm_edid;
>>>>>>>        if (!msm_dp_panel) {
>>>>>>>            DRM_ERROR("invalid input\n");
>>>>>>> @@ -238,8 +246,13 @@ void msm_dp_panel_handle_sink_request(struct 
>>>>>>> msm_dp_panel *msm_dp_panel)
>>>>>>>        panel = container_of(msm_dp_panel, struct 
>>>>>>> msm_dp_panel_private, msm_dp_panel);
>>>>>>>        if (panel->link->sink_request & DP_TEST_LINK_EDID_READ) {
>>>>>>> +        drm_edid = drm_edid_read_ddc(msm_dp_panel->connector, 
>>>>>>> &panel->aux->ddc);
>>>>>>
>>>>>> And again....
>>>>>>
>>>>> Here we need the struct edid,since we drop the cached drm_edid, so 
>>>>> we need
>>>>> to read it again. Or we can return the drm_edid from 
>>>>> msm_dp_panel_read_edid
>>>>> and pass it to msm_dp_panel_handle_sink_request, then we don't need 
>>>>> to read
>>>>> drm_edid here. Emm, I'm still a bit curious why we can't cache the 
>>>>> drm_edid?
>>>>> It would help us to access it when needed. Emm, i see other drivers 
>>>>> also
>>>>> cache it.
>>>>
>>>> Yes, they can cache EDID. However, in this case we don't even need 
>>>> it at
>>>> all. This piece needs to be rewritten to use
>>>> drm_dp_send_real_edid_checksum(), connector->real_edid_checksum.
>>>>
>>>> Corresponding changes can be submitted separately.
>>>>
>>> Got it, thanks, will separate this patch from MST patches..  Even if 
>>> we use drm_dp_send_real_edid_checksum to send connector- 
>>>  >real_edid_checksum, that’s only when the EDID state is incorrect.
>>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/ 
>>> tree/ drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c? 
>>> h=v6.16-rc3#n1020
>>>   When the EDID is read correctly, it should send edid->checksum 
>>> instead.
>>
>> I wonder if we should fix the drm_edid to always set 
>> real_edid_checksum instead.
>>
> Emm, can i understand that is another issue exist in the currently DRM..

Currently it's more of a design issue.

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead
  2025-08-06  9:22             ` Yongxing Mou
@ 2025-08-06 10:41               ` Dmitry Baryshkov
  0 siblings, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-08-06 10:41 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On 06/08/2025 14:52, Yongxing Mou wrote:
> 
> 
> On 2025/6/27 20:44, Dmitry Baryshkov wrote:
>> On 27/06/2025 11:40, Yongxing Mou wrote:
>>>
>>>
>>> On 2025/6/25 22:03, Dmitry Baryshkov wrote:
>>>> On Wed, Jun 25, 2025 at 08:34:18PM +0800, Yongxing Mou wrote:
>>>>>
>>>>>
>>>>> On 2025/6/9 20:48, Dmitry Baryshkov wrote:
>>>>>> On Mon, Jun 09, 2025 at 08:21:21PM +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 populated the panel's dp_display_mode
>>>>>>> instead.
>>>>>>
>>>>>> - Why do we need to cache / copy it anyway? Can't we just pass the
>>>>>>     corresponding drm_atomic_state / drm_crtc_state / 
>>>>>> drm_display_mode ?
>>>>>>
>>>>> This part works as follows: .mode_set() copies the adjusted_mode into
>>>>> msm_dp_display_private->msm_dp_display_mode, and also parses and 
>>>>> stores
>>>>> variables such as v_active_low/h_active_low/out_fmt_is_yuv_420 
>>>>> and ... When
>>>>> @drm_bridge_funcs.atomic_enable() is called, it copies
>>>>> msm_dp_display->msm_dp_mode into dp_panel->msm_dp_mode and initializes
>>>>> panel_info in msm_dp_display_set_mode(). Then when go to
>>>>> msm_dp_ctrl_on_stream(), the parameters are updated into the 
>>>>> corresponding
>>>>> hardware registers.
>>>>
>>>> So, if we do everything during .atomic_enable(), there would be no need
>>>> to store and/or copy anything. All the data is available and can be 
>>>> used
>>>> as is.
>>>>
>>> Got it. Let me confirm—can we keep msm_dp_mode or drm_display_mode in 
>>> msm_dp_panel? Mabey debug node will use this ..
>>
>> Please don't. I really dislike storing drm_atomic_state-related 
>> variables in a non-state structure. I think it makes it easier to 
>> mistakenly update or to use a stale value.
>>
>> Debug code already prints modes in debugfs/dri/N/state. If we need any 
>> other state-related prints, they should go to the same file.
>>
> Hi, I got this point.. i go through the driver. since lots of funcs used 
> msm_dp_mode cached. so maybe it is not a very small change.. I’d like to 
> prioritize MST first, and then submit this patch once I got time..

Ack

> 
>>>>>
>>>>> This design has been in place since the first version of the DP 
>>>>> driver and
>>>>> has remained largely unchanged.
>>>>
>>>> Yes... The point is that you are touching this piece of code anyway,
>>>> let's make it nicer.
>>>>
>>> Agree with this point.
>>>>> Originally, the drm_mode would be passed in
>>>>> two stages: from msm_dp_display->msm_dp_mode to dp_panel- 
>>>>> >msm_dp_mode. Since
>>>>> in MST mode each stream requires its own drm_mode and stored in 
>>>>> dp_panel, we
>>>>> simplified the two-stage transfer into a single step (.mode_set() 
>>>>> do all
>>>>> things and store in msm_dp_panel). Meanwhile we modified the
>>>>> msm_dp_display_set_mode function to accept a msm_dp_panel parameter,
>>>>> allowing the MST bridge funcs' mode_set() to reuse this part code.
>>>>>
>>>>> The following patches:
>>>>> https://patchwork.freedesktop.org/patch/657573/?series=142207&rev=2 
>>>>> and
>>>>> https://patchwork.freedesktop.org/patch/657593/?series=142207&rev=2,
>>>>> introduce msm_dp_display_*_helper functions to help reuse common 
>>>>> code across
>>>>> MST/SST/eDP drm_bridge_funcs.
>>>>>
>>>>> If we drop msm_dp_mode from dp_panel and use drm_display_mode, it 
>>>>> might
>>>>> introduce a large number of changes that are not directly related 
>>>>> to MST.
>>>>> Actually i think the presence of msm_dp_display_mode seems to 
>>>>> simplify the
>>>>> work in msm_dp_panel_timing_cfg(), this patch series we want to 
>>>>> focus on MST
>>>>> parts, so would we consider optimizing them later?
>>>>
>>>> Sure... But then you have to change two places. If you optimize it
>>>> first, you have to touch only place. And it can be even submitted
>>>> separately.
>>>>
>>> Understood, that’s indeed the case. I just want to prioritize the MST 
>>> patch and have it merged first, since it involves changes to lots of 
>>> files. Thanks~~
>>>>>
>>>>> Thanks~
>>>>>>>
>>>>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>>>>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>>>>>>> ---
>>>>>>>    drivers/gpu/drm/msm/dp/dp_display.c | 76 +++++++++++++ 
>>>>>>> +-----------------------
>>>>>>>    1 file changed, 29 insertions(+), 47 deletions(-)
>>>>>>>
>>>>>>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/ 
>>>>>>> drm/ msm/dp/dp_display.c
>>>>>>> index 
>>>>>>> 4a9b65647cdef1ed6c3bb851f93df0db8be977af..9d2db9cbd2552470a36a63f70f517c35436f7280 100644
>>>>>>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>>>>>>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>>>>>>> @@ -92,7 +92,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 */
>>>>>>> @@ -806,16 +805,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;
>>>>>>> +
>>>>>>> +    msm_dp_panel->msm_dp_mode.v_active_low =
>>>>>>> +        !!(adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC);
>>>>>>> +    msm_dp_panel->msm_dp_mode.h_active_low =
>>>>>>> +        !!(adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC);
>>>>>>> +    msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 =
>>>>>>> +        drm_mode_is_420_only(&msm_dp_panel->connector- 
>>>>>>> >display_info, adjusted_mode) &&
>>>>>>> +        msm_dp_panel->vsc_sdp_supported;
>>>>>>> -    drm_mode_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_init_panel_info(msm_dp_panel);
>>>>>>>        return 0;
>>>>>>>    }
>>>>>>> @@ -1431,10 +1443,13 @@ bool msm_dp_needs_periph_flush(const 
>>>>>>> struct msm_dp *msm_dp_display,
>>>>>>>    bool msm_dp_wide_bus_available(const 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);
>>>>>>> -    if (dp->msm_dp_mode.out_fmt_is_yuv_420)
>>>>>>> +    dp_panel = dp->panel;
>>>>>>> +
>>>>>>> +    if (dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
>>>>>>>            return false;
>>>>>>>        return dp->wide_bus_supported;
>>>>>>> @@ -1496,10 +1511,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);
>>>>>>> @@ -1517,15 +1528,6 @@ void msm_dp_bridge_atomic_enable(struct 
>>>>>>> drm_bridge *drm_bridge,
>>>>>>>            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);
>>>>>>> -        mutex_unlock(&msm_dp_display->event_mutex);
>>>>>>> -        return;
>>>>>>> -    }
>>>>>>
>>>>>> It should be done other way around: keep this call and drop
>>>>>> msm_dp_bridge_mode_set().
>>>>>>
>>>>> Emm as reply in last comments..
>>>>
>>>> Yep. Drop .mode_set, the callback is even described as deprecated.
>>>>
>>> Thanks, the documentation does state that.
>>>>>>> -
>>>>>>> -    hpd_state =  msm_dp_display->hpd_state;
>>>>>>> -
>>>>>>>        if (hpd_state == ST_CONNECTED && !dp->power_on) {
>>>>>>>            msm_dp_display_host_phy_init(msm_dp_display);
>>>>>>>            force_link_train = true;
>>>>>>> @@ -1604,33 +1606,13 @@ 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->catalog->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;
>>>>>>> +    msm_dp_display->catalog->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] 108+ messages in thread

* Re: [PATCH v2 03/38] drm/msm/dp: break up dp_display_enable into two parts
  2025-06-09 12:59   ` Dmitry Baryshkov
  2025-08-06  9:24     ` Yongxing Mou
@ 2025-08-13  9:36     ` Yongxing Mou
  2025-08-13 12:59       ` Dmitry Baryshkov
  1 sibling, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-08-13  9:36 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/9 20:59, Dmitry Baryshkov wrote:
> On Mon, Jun 09, 2025 at 08:21:22PM +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. Splite these two parts into
>> prepare/enable APIs, to support MST bridges_enable inserte
> 
> typos
> 
>> the MST payloads funcs between enable stream_clks and programe
>> register.
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>> ---
>>   drivers/gpu/drm/msm/dp/dp_ctrl.c    | 57 +++++++++++++--------
>>   drivers/gpu/drm/msm/dp/dp_ctrl.h    |  3 +-
>>   drivers/gpu/drm/msm/dp/dp_display.c | 99 +++++++++++++++++++++++++++----------
>>   drivers/gpu/drm/msm/dp/dp_display.h |  1 +
>>   4 files changed, 111 insertions(+), 49 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> index a50bfafbb4ea85c114c958ea0ed24362a1f23136..1e13ca81b0155a37a4ed7a2e83c918293d703a37 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> @@ -1980,40 +1980,61 @@ static int msm_dp_ctrl_link_retrain(struct msm_dp_ctrl_private *ctrl)
>>   	return msm_dp_ctrl_setup_main_link(ctrl, &training_step);
>>   }
>>   
>> -int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train)
>> +int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train)
>>   {
>>   	int ret = 0;
>> -	bool mainlink_ready = false;
>>   	struct msm_dp_ctrl_private *ctrl;
>> -	unsigned long pixel_rate;
>> -	unsigned long pixel_rate_orig;
>>   
>>   	if (!msm_dp_ctrl)
>>   		return -EINVAL;
>>   
>>   	ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
>>   
>> -	pixel_rate = pixel_rate_orig = ctrl->panel->msm_dp_mode.drm_mode.clock;
>> -
>> -	if (msm_dp_ctrl->wide_bus_en || ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420)
>> -		pixel_rate >>= 1;
>> -
>> -	drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d, pixel_rate=%lu\n",
>> -		ctrl->link->link_params.rate,
>> -		ctrl->link->link_params.num_lanes, pixel_rate);
>> +	drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d\n",
>> +		   ctrl->link->link_params.rate,
>> +		   ctrl->link->link_params.num_lanes);
> 
> Please don't mix whitespace changes with the actual code changes. It
> makes reviewing the patch much harder.
> 
>>   
>>   	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);
>> +		   "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);
>>   
>>   	if (!ctrl->link_clks_on) { /* link clk is off */
>>   		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);
>> @@ -2031,12 +2052,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 b7abfedbf5749c25877a0b8ba3af3d8ed4b23d67..42745c912adbad7221c78f5cecefa730bfda1e75 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
>> @@ -18,7 +18,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 *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_link(struct msm_dp_ctrl *msm_dp_ctrl);
>>   void msm_dp_ctrl_off(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 9d2db9cbd2552470a36a63f70f517c35436f7280..5ac5dcf35b789f2bda052a2c17aae20aa48d8e18 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>> @@ -831,7 +831,37 @@ 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);
>> +	if (msm_dp_display->prepared) {
>> +		drm_dbg_dp(dp->drm_dev, "Link already setup, return\n");
>> +		return 0;
>> +	}
> 
> How can it be prepared here? It is called at the beginning of the
> .atomic_enable() only, so there is no way this can be true.
> 
Emm, sorry for forget this case.. Whern MST enabled, 
msm_dp_display_prepare() will be called from 
mst_bridge_atomic_pre_enable, that means, when second stream called this 
func, it already prepared, so we should skip here. so this condition 
will really hit in MST case..
>> +
>> +	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->hpd_state == ST_CONNECTED && !msm_dp_display->power_on) {
>> +		msm_dp_display_host_phy_init(dp);
>> +		force_link_train = true;
>> +	}
>> +
>> +	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;
>> @@ -842,7 +872,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;
>>   
>> @@ -872,13 +902,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 */
>> @@ -889,6 +916,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) {
>>   		/*
>> @@ -1506,9 +1541,8 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
>>   	struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge);
>>   	struct msm_dp *dp = msm_dp_bridge->msm_dp_display;
>>   	int rc = 0;
>> +
>>   	struct msm_dp_display_private *msm_dp_display;
>> -	u32 hpd_state;
>> -	bool force_link_train = false;
>>   
>>   	msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
>>   
>> @@ -1516,29 +1550,23 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
>>   		msm_dp_hpd_plug_handle(msm_dp_display, 0);
>>   
>>   	mutex_lock(&msm_dp_display->event_mutex);
>> -	if (pm_runtime_resume_and_get(&dp->pdev->dev)) {
>> -		DRM_ERROR("failed to pm_runtime_resume\n");
>> -		mutex_unlock(&msm_dp_display->event_mutex);
>> -		return;
>> -	}
>>   
>> -	hpd_state = msm_dp_display->hpd_state;
>> -	if (hpd_state == ST_DISCONNECT_PENDING) {
>> +	rc = msm_dp_display_prepare(msm_dp_display);
>> +	if (rc) {
>> +		DRM_ERROR("DP display prepare failed, rc=%d\n", rc);
>>   		mutex_unlock(&msm_dp_display->event_mutex);
>>   		return;
>>   	}
>>   
>> -	if (hpd_state == ST_CONNECTED && !dp->power_on) {
>> -		msm_dp_display_host_phy_init(msm_dp_display);
>> -		force_link_train = true;
>> -	}
>> -
>> -	msm_dp_display_enable(msm_dp_display, force_link_train);
>> -
>> -	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);
>> +	if (dp->prepared) {
>> +		rc = msm_dp_display_enable(msm_dp_display);
>> +		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);
>> +		}
>>   	}
>>   
>>   	/* completed connection */
>> @@ -1560,6 +1588,20 @@ 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;
>> +
>> +	if (!msm_dp_display->prepared) {
>> +		drm_dbg_dp(dp->drm_dev, "Link already setup, return\n");
>> +		return;
>> +	}
> 
> Why/ how is it possible?
> 
>> +
>> +	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)
>>   {
>> @@ -1580,6 +1622,8 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
>>   		drm_dbg_dp(dp->drm_dev, "type=%d wrong hpd_state=%d\n",
>>   			   dp->connector_type, hpd_state);
>>   
>> +	msm_dp_display_audio_notify_disable(msm_dp_display);
>> +
>>   	msm_dp_display_disable(msm_dp_display);
>>   
>>   	hpd_state =  msm_dp_display->hpd_state;
>> @@ -1588,9 +1632,10 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
>>   		msm_dp_display->hpd_state = ST_DISCONNECTED;
>>   	}
>>   
>> +	msm_dp_display_unprepare(msm_dp_display);
>> +
>>   	drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type);
>>   
>> -	pm_runtime_put_sync(&dp->pdev->dev);
>>   	mutex_unlock(&msm_dp_display->event_mutex);
>>   }
>>   
>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
>> index cc6e2cab36e9c0b1527ff292e547cbb4d69fd95c..2394840e9f28e136705004c3e6af93fbe13c33c5 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_display.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
>> @@ -19,6 +19,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	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 05/38] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it
  2025-06-09 13:12   ` Dmitry Baryshkov
@ 2025-08-13  9:52     ` Yongxing Mou
  2025-08-13 13:20       ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-08-13  9:52 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/9 21:12, Dmitry Baryshkov wrote:
> On Mon, Jun 09, 2025 at 08:21:24PM +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.
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>> ---
>>   drivers/gpu/drm/msm/dp/dp_ctrl.c    | 37 ++++++++++++++++++++-----------------
>>   drivers/gpu/drm/msm/dp/dp_ctrl.h    |  5 +++--
>>   drivers/gpu/drm/msm/dp/dp_display.c |  4 ++--
>>   3 files changed, 25 insertions(+), 21 deletions(-)
> 
> I think previous review comments got ignored. Please step back and
> review them. Maybe I should ask you to go back to v1 and actually check
> all the review comments there?
> 
Sorry for that.. i will check all the comments again.. thanks
>>
>> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> index 1ce3cca121d0c56b493e282c76eb9202371564cf..aee8e37655812439dfb65ae90ccb61b14e6e261f 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> @@ -135,7 +135,8 @@ 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(struct msm_dp_ctrl_private *ctrl,
>> +				    struct msm_dp_panel *msm_dp_panel)
>>   {
>>   	u32 config = 0, tbd;
>>   	const u8 *dpcd = ctrl->panel->dpcd;
>> @@ -143,7 +144,7 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl)
>>   	/* 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)
>> +	if (msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
>>   		config |= DP_CONFIGURATION_CTRL_RGB_YUV; /* YUV420 */
>>   
>>   	/* Scrambler reset enable */
>> @@ -151,7 +152,7 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl)
>>   		config |= DP_CONFIGURATION_CTRL_ASSR;
>>   
>>   	tbd = msm_dp_link_get_test_bits_depth(ctrl->link,
>> -			ctrl->panel->msm_dp_mode.bpp);
>> +			msm_dp_panel->msm_dp_mode.bpp);
>>   
>>   	config |= tbd << DP_CONFIGURATION_CTRL_BPC_SHIFT;
>>   
>> @@ -174,20 +175,21 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl)
>>   	msm_dp_catalog_ctrl_config_ctrl(ctrl->catalog, config);
>>   }
>>   
>> -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)
>>   {
>>   	u32 cc, tb;
>>   
>>   	msm_dp_catalog_ctrl_lane_mapping(ctrl->catalog);
>>   	msm_dp_catalog_setup_peripheral_flush(ctrl->catalog);
>>   
>> -	msm_dp_ctrl_config_ctrl(ctrl);
>> +	msm_dp_ctrl_config_ctrl(ctrl, msm_dp_panel);
>>   
>>   	tb = msm_dp_link_get_test_bits_depth(ctrl->link,
>> -		ctrl->panel->msm_dp_mode.bpp);
>> +		msm_dp_panel->msm_dp_mode.bpp);
>>   	cc = msm_dp_link_get_colorimetry_config(ctrl->link);
>>   	msm_dp_catalog_ctrl_config_misc(ctrl->catalog, cc, tb);
>> -	msm_dp_panel_timing_cfg(ctrl->panel);
>> +	msm_dp_panel_timing_cfg(msm_dp_panel);
>>   }
>>   
>>   /*
>> @@ -1317,7 +1319,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(ctrl, ctrl->panel);
> 
> Could you please explain, when is it fine to use ctrl->panel and when it
> is not? Here you are passing msm_dp_panel to configure DP link for link
> training. I don't think we need the panel for that, so just using
> ctrl->panel here is incorrect too.
> 
Emm, If we need to program registers related to the pixel clock or DP 
link with MST(all of them need pass the stream_id to determine the 
register address), we should pass in msm_dp_panel. If we're only 
programming the other parts not related to the stream_id, passing in 
ctrl->panel is sufficient.
here in link tranning, it's right, we actually don't need to pass in the 
panel. But since in msm_dp_ctrl_config_ctrl, we will write config to 
DP0/DP1 CONFIGURATION_CTRL, even mst2/mst3 link CONFIGURATION_CTRL. and 
this func will also been called in msm_dp_ctrl_configure_source_params. 
so we need add ctrl->panel here.
>>   
>>   	link_info.num_lanes = ctrl->link->link_params.num_lanes;
>>   	link_info.rate = ctrl->link->link_params.rate;
>> @@ -1735,7 +1737,8 @@ static bool msm_dp_ctrl_send_phy_test_pattern(struct msm_dp_ctrl_private *ctrl)
>>   	return success;
>>   }
>>   
>> -static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl)
>> +static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl,
>> +						struct msm_dp_panel *msm_dp_panel)
>>   {
>>   	int ret;
>>   	unsigned long pixel_rate;
>> @@ -1759,7 +1762,7 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
>>   		return ret;
>>   	}
>>   
>> -	pixel_rate = ctrl->panel->msm_dp_mode.drm_mode.clock;
>> +	pixel_rate = msm_dp_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);
>> @@ -1797,7 +1800,7 @@ void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl)
>>   
>>   	if (sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
>>   		drm_dbg_dp(ctrl->drm_dev, "PHY_TEST_PATTERN request\n");
>> -		if (msm_dp_ctrl_process_phy_test_request(ctrl)) {
>> +		if (msm_dp_ctrl_process_phy_test_request(ctrl, ctrl->panel)) {
>>   			DRM_ERROR("process phy_test_req failed\n");
>>   			return;
>>   		}
>> @@ -2015,7 +2018,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;
>> @@ -2028,9 +2031,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);
>> @@ -2058,12 +2061,12 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl)
>>   	 */
>>   	reinit_completion(&ctrl->video_comp);
>>   
>> -	msm_dp_ctrl_configure_source_params(ctrl);
>> +	msm_dp_ctrl_configure_source_params(ctrl, msm_dp_panel);
>>   
>>   	msm_dp_catalog_ctrl_config_msa(ctrl->catalog,
>>   		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_ctrl_setup_tr_unit(ctrl);
>>   
>> @@ -2081,7 +2084,7 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl)
>>   	return ret;
>>   }
>>   
>> -void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl)
>> +void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *dp_panel)
>>   {
>>   	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 edbe5766db74c4e4179141d895f9cb85e514f29b..fbe458c5a17bda0586097a61d925f608d99f9224 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
>> @@ -18,7 +18,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 *dp_ctrl, bool force_link_train);
>>   void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl);
>>   void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl);
>> @@ -41,7 +41,8 @@ void msm_dp_ctrl_config_psr(struct msm_dp_ctrl *msm_dp_ctrl);
>>   int msm_dp_ctrl_core_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl);
>>   void msm_dp_ctrl_core_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl);
>>   
>> -void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl);
>> +void msm_dp_ctrl_clear_vsc_sdp_pkt(struct msm_dp_ctrl *msm_dp_ctrl,
>> +				   struct msm_dp_panel *msm_dp_panel);
>>   void msm_dp_ctrl_psm_config(struct msm_dp_ctrl *msm_dp_ctrl);
>>   void msm_dp_ctrl_reinit_phy(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 a5ca498cb970d0c6a4095b0b7fc6269c2dc3ad31..17ccea4047500848c4fb3eda87a10e29b18e0cfb 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>> @@ -872,7 +872,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;
>>   
>> @@ -925,7 +925,7 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
>>   	if (!msm_dp_display->power_on)
>>   		return 0;
>>   
>> -	msm_dp_ctrl_clear_vsc_sdp_pkt(dp->ctrl);
>> +	msm_dp_ctrl_clear_vsc_sdp_pkt(dp->ctrl, dp->panel);
>>   
>>   	/* dongle is still connected but sinks are disconnected */
>>   	if (dp->link->sink_count == 0) {
>>
>> -- 
>> 2.34.1
>>
> 


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 06/38] drm/msm/dp: move the pixel clock control to its own API
  2025-06-09 13:16   ` Dmitry Baryshkov
@ 2025-08-13 11:56     ` Yongxing Mou
  2025-08-13 13:05       ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-08-13 11:56 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/9 21:16, Dmitry Baryshkov wrote:
> On Mon, Jun 09, 2025 at 08:21:25PM +0800, Yongxing Mou wrote:
>> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>
>> 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 <quic_yongmou@quicinc.com>
>> ---
>>   drivers/gpu/drm/msm/dp/dp_ctrl.c | 98 +++++++++++++++++++++-------------------
>>   1 file changed, 52 insertions(+), 46 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> index aee8e37655812439dfb65ae90ccb61b14e6e261f..ed00dd2538d98ddbc6bdcbd5fa154fd7043c48d6 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> @@ -97,7 +97,7 @@ struct msm_dp_ctrl_private {
>>   
>>   	bool core_clks_on;
>>   	bool link_clks_on;
>> -	bool stream_clks_on;
>> +	bool pixel_clks_on;
> 
> As you are touching this part, how many paths lead to pixel clock being
> enabled and/or disabled? Can we sort them out and drop this flag, making
> sure that the clock can be enabled only in one place and disabled in
> another one (hopefully)?
> 
Here we only have 2 paths to enable/disable pixel, 
1.msm_dp_ctrl_process_phy_test_request 2.msm_dp_display_enable/disable.
both of them are in pairs. Maybe we can keep this state to make it 
easier to access the on/off status of each of them in the case of 4 MST 
streams. when we get the snapshot of the pixel clk, we can visit here.
>>   };
>>   
>>   static int msm_dp_aux_link_configure(struct drm_dp_aux *aux,
>> @@ -1406,8 +1406,8 @@ int msm_dp_ctrl_core_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl)
>>   	ctrl->core_clks_on = true;
>>   
>>   	drm_dbg_dp(ctrl->drm_dev, "enable core clocks \n");
>> -	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
>> -		   str_on_off(ctrl->stream_clks_on),
>> +	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
>> +		   str_on_off(ctrl->pixel_clks_on),
>>   		   str_on_off(ctrl->link_clks_on),
>>   		   str_on_off(ctrl->core_clks_on));
>>   
>> @@ -1425,8 +1425,8 @@ void msm_dp_ctrl_core_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl)
>>   	ctrl->core_clks_on = false;
>>   
>>   	drm_dbg_dp(ctrl->drm_dev, "disable core clocks \n");
>> -	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
>> -		   str_on_off(ctrl->stream_clks_on),
>> +	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
>> +		   str_on_off(ctrl->pixel_clks_on),
>>   		   str_on_off(ctrl->link_clks_on),
>>   		   str_on_off(ctrl->core_clks_on));
>>   }
>> @@ -1456,8 +1456,8 @@ static int msm_dp_ctrl_link_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl)
>>   	ctrl->link_clks_on = true;
>>   
>>   	drm_dbg_dp(ctrl->drm_dev, "enable link clocks\n");
>> -	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
>> -		   str_on_off(ctrl->stream_clks_on),
>> +	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
>> +		   str_on_off(ctrl->pixel_clks_on),
>>   		   str_on_off(ctrl->link_clks_on),
>>   		   str_on_off(ctrl->core_clks_on));
>>   
>> @@ -1475,8 +1475,8 @@ static void msm_dp_ctrl_link_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl)
>>   	ctrl->link_clks_on = false;
>>   
>>   	drm_dbg_dp(ctrl->drm_dev, "disabled link clocks\n");
>> -	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
>> -		   str_on_off(ctrl->stream_clks_on),
>> +	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
>> +		   str_on_off(ctrl->pixel_clks_on),
>>   		   str_on_off(ctrl->link_clks_on),
>>   		   str_on_off(ctrl->core_clks_on));
>>   }
>> @@ -1737,6 +1737,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->pixel_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->pixel_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->pixel_clks_on) {
>> +		clk_disable_unprepare(ctrl->pixel_clk);
>> +		ctrl->pixel_clks_on = false;
>> +	}
>> +}
>> +
>>   static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl,
>>   						struct msm_dp_panel *msm_dp_panel)
>>   {
>> @@ -1763,22 +1799,7 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
>>   	}
>>   
>>   	pixel_rate = msm_dp_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);
>>   
>> @@ -1998,8 +2019,8 @@ int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_li
>>   		   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);
>> +		   "core_clk_on=%d link_clk_on=%d pixel_clks_on=%d\n",
>> +		   ctrl->core_clks_on, ctrl->link_clks_on, ctrl->pixel_clks_on);
>>   
>>   	if (!ctrl->link_clks_on) { /* link clk is off */
>>   		ret = msm_dp_ctrl_enable_mainlink_clocks(ctrl);
>> @@ -2038,21 +2059,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;
>>   	}
>>   
>>   	/*
>> @@ -2080,7 +2090,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;
>>   }
>>   
>> @@ -2154,10 +2163,7 @@ void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl)
>>   
>>   	msm_dp_catalog_ctrl_reset(ctrl->catalog);
>>   
>> -	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	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 03/38] drm/msm/dp: break up dp_display_enable into two parts
  2025-08-13  9:36     ` Yongxing Mou
@ 2025-08-13 12:59       ` Dmitry Baryshkov
  2025-08-14  8:14         ` Yongxing Mou
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-08-13 12:59 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Wed, Aug 13, 2025 at 05:36:10PM +0800, Yongxing Mou wrote:
> 
> 
> On 2025/6/9 20:59, Dmitry Baryshkov wrote:
> > On Mon, Jun 09, 2025 at 08:21:22PM +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. Splite these two parts into
> > > prepare/enable APIs, to support MST bridges_enable inserte
> > 
> > typos
> > 
> > > the MST payloads funcs between enable stream_clks and programe
> > > register.
> > > 
> > > Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> > > Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> > > ---
> > >   drivers/gpu/drm/msm/dp/dp_ctrl.c    | 57 +++++++++++++--------
> > >   drivers/gpu/drm/msm/dp/dp_ctrl.h    |  3 +-
> > >   drivers/gpu/drm/msm/dp/dp_display.c | 99 +++++++++++++++++++++++++++----------
> > >   drivers/gpu/drm/msm/dp/dp_display.h |  1 +
> > >   4 files changed, 111 insertions(+), 49 deletions(-)
> > > 

> > > @@ -831,7 +831,37 @@ 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);
> > > +	if (msm_dp_display->prepared) {
> > > +		drm_dbg_dp(dp->drm_dev, "Link already setup, return\n");
> > > +		return 0;
> > > +	}
> > 
> > How can it be prepared here? It is called at the beginning of the
> > .atomic_enable() only, so there is no way this can be true.
> > 
> Emm, sorry for forget this case.. Whern MST enabled,
> msm_dp_display_prepare() will be called from mst_bridge_atomic_pre_enable,
> that means, when second stream called this func, it already prepared, so we
> should skip here. so this condition will really hit in MST case..

Then it should be refcounted. And, ideally, this should come later as a
part of one of MST-enablement patches, when it actually makes sense

> > > +
> > > +	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->hpd_state == ST_CONNECTED && !msm_dp_display->power_on) {
> > > +		msm_dp_display_host_phy_init(dp);
> > > +		force_link_train = true;
> > > +	}
> > > +

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 06/38] drm/msm/dp: move the pixel clock control to its own API
  2025-08-13 11:56     ` Yongxing Mou
@ 2025-08-13 13:05       ` Dmitry Baryshkov
  2025-08-13 13:21         ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-08-13 13:05 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Wed, Aug 13, 2025 at 07:56:41PM +0800, Yongxing Mou wrote:
> 
> 
> On 2025/6/9 21:16, Dmitry Baryshkov wrote:
> > On Mon, Jun 09, 2025 at 08:21:25PM +0800, Yongxing Mou wrote:
> > > From: Abhinav Kumar <quic_abhinavk@quicinc.com>
> > > 
> > > 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 <quic_yongmou@quicinc.com>
> > > ---
> > >   drivers/gpu/drm/msm/dp/dp_ctrl.c | 98 +++++++++++++++++++++-------------------
> > >   1 file changed, 52 insertions(+), 46 deletions(-)
> > > 
> > > diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> > > index aee8e37655812439dfb65ae90ccb61b14e6e261f..ed00dd2538d98ddbc6bdcbd5fa154fd7043c48d6 100644
> > > --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> > > +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> > > @@ -97,7 +97,7 @@ struct msm_dp_ctrl_private {
> > >   	bool core_clks_on;
> > >   	bool link_clks_on;
> > > -	bool stream_clks_on;
> > > +	bool pixel_clks_on;
> > 
> > As you are touching this part, how many paths lead to pixel clock being
> > enabled and/or disabled? Can we sort them out and drop this flag, making
> > sure that the clock can be enabled only in one place and disabled in
> > another one (hopefully)?
> > 
> Here we only have 2 paths to enable/disable pixel,
> 1.msm_dp_ctrl_process_phy_test_request 2.msm_dp_display_enable/disable.
> both of them are in pairs. Maybe we can keep this state to make it easier to
> access the on/off status of each of them in the case of 4 MST streams. when
> we get the snapshot of the pixel clk, we can visit here.

I don't think I completely follow the MST part. I last reviewed your
patches some time ago, so I might have forgotten part of the series.

We need some refcounting or state processing in order to identify if
there is at least one active stream. Only if we are transitioning from
the full-off to the at-least-one-on or from the last-one-on to the
all-off we need to toggle the pixel clock. This means that we don't need
the pixel_clks_on flag but some other kind of variable.

Note, I might be wrong here, please feel free to point me to a patch
which handles that.

> > >   };
> > >   static int msm_dp_aux_link_configure(struct drm_dp_aux *aux,
> > > @@ -1406,8 +1406,8 @@ int msm_dp_ctrl_core_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl)
> > >   	ctrl->core_clks_on = true;
> > >   	drm_dbg_dp(ctrl->drm_dev, "enable core clocks \n");
> > > -	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
> > > -		   str_on_off(ctrl->stream_clks_on),
> > > +	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
> > > +		   str_on_off(ctrl->pixel_clks_on),
> > >   		   str_on_off(ctrl->link_clks_on),
> > >   		   str_on_off(ctrl->core_clks_on));
> > > @@ -1425,8 +1425,8 @@ void msm_dp_ctrl_core_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl)
> > >   	ctrl->core_clks_on = false;
> > >   	drm_dbg_dp(ctrl->drm_dev, "disable core clocks \n");
> > > -	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
> > > -		   str_on_off(ctrl->stream_clks_on),
> > > +	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
> > > +		   str_on_off(ctrl->pixel_clks_on),
> > >   		   str_on_off(ctrl->link_clks_on),
> > >   		   str_on_off(ctrl->core_clks_on));
> > >   }
> > > @@ -1456,8 +1456,8 @@ static int msm_dp_ctrl_link_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl)
> > >   	ctrl->link_clks_on = true;
> > >   	drm_dbg_dp(ctrl->drm_dev, "enable link clocks\n");
> > > -	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
> > > -		   str_on_off(ctrl->stream_clks_on),
> > > +	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
> > > +		   str_on_off(ctrl->pixel_clks_on),
> > >   		   str_on_off(ctrl->link_clks_on),
> > >   		   str_on_off(ctrl->core_clks_on));
> > > @@ -1475,8 +1475,8 @@ static void msm_dp_ctrl_link_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl)
> > >   	ctrl->link_clks_on = false;
> > >   	drm_dbg_dp(ctrl->drm_dev, "disabled link clocks\n");
> > > -	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
> > > -		   str_on_off(ctrl->stream_clks_on),
> > > +	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
> > > +		   str_on_off(ctrl->pixel_clks_on),
> > >   		   str_on_off(ctrl->link_clks_on),
> > >   		   str_on_off(ctrl->core_clks_on));
> > >   }
> > > @@ -1737,6 +1737,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->pixel_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->pixel_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->pixel_clks_on) {
> > > +		clk_disable_unprepare(ctrl->pixel_clk);
> > > +		ctrl->pixel_clks_on = false;
> > > +	}
> > > +}
> > > +
> > >   static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl,
> > >   						struct msm_dp_panel *msm_dp_panel)
> > >   {
> > > @@ -1763,22 +1799,7 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
> > >   	}
> > >   	pixel_rate = msm_dp_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);
> > > @@ -1998,8 +2019,8 @@ int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_li
> > >   		   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);
> > > +		   "core_clk_on=%d link_clk_on=%d pixel_clks_on=%d\n",
> > > +		   ctrl->core_clks_on, ctrl->link_clks_on, ctrl->pixel_clks_on);
> > >   	if (!ctrl->link_clks_on) { /* link clk is off */
> > >   		ret = msm_dp_ctrl_enable_mainlink_clocks(ctrl);
> > > @@ -2038,21 +2059,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;
> > >   	}
> > >   	/*
> > > @@ -2080,7 +2090,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;
> > >   }
> > > @@ -2154,10 +2163,7 @@ void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl)
> > >   	msm_dp_catalog_ctrl_reset(ctrl->catalog);
> > > -	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
> > > 
> > 
> 

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 05/38] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it
  2025-08-13  9:52     ` Yongxing Mou
@ 2025-08-13 13:20       ` Dmitry Baryshkov
  0 siblings, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-08-13 13:20 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Wed, Aug 13, 2025 at 05:52:04PM +0800, Yongxing Mou wrote:
> 
> 
> On 2025/6/9 21:12, Dmitry Baryshkov wrote:
> > On Mon, Jun 09, 2025 at 08:21:24PM +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.
> > > 
> > > Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> > > Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> > > ---
> > >   drivers/gpu/drm/msm/dp/dp_ctrl.c    | 37 ++++++++++++++++++++-----------------
> > >   drivers/gpu/drm/msm/dp/dp_ctrl.h    |  5 +++--
> > >   drivers/gpu/drm/msm/dp/dp_display.c |  4 ++--
> > >   3 files changed, 25 insertions(+), 21 deletions(-)
> > 
> > I think previous review comments got ignored. Please step back and
> > review them. Maybe I should ask you to go back to v1 and actually check
> > all the review comments there?
> > 
> Sorry for that.. i will check all the comments again.. thanks
> > > 
> > > diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> > > index 1ce3cca121d0c56b493e282c76eb9202371564cf..aee8e37655812439dfb65ae90ccb61b14e6e261f 100644
> > > --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> > > +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> > > @@ -135,7 +135,8 @@ 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(struct msm_dp_ctrl_private *ctrl,
> > > +				    struct msm_dp_panel *msm_dp_panel)
> > >   {
> > >   	u32 config = 0, tbd;
> > >   	const u8 *dpcd = ctrl->panel->dpcd;
> > > @@ -143,7 +144,7 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl)
> > >   	/* 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)
> > > +	if (msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420)
> > >   		config |= DP_CONFIGURATION_CTRL_RGB_YUV; /* YUV420 */
> > >   	/* Scrambler reset enable */
> > > @@ -151,7 +152,7 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl)
> > >   		config |= DP_CONFIGURATION_CTRL_ASSR;
> > >   	tbd = msm_dp_link_get_test_bits_depth(ctrl->link,
> > > -			ctrl->panel->msm_dp_mode.bpp);
> > > +			msm_dp_panel->msm_dp_mode.bpp);
> > >   	config |= tbd << DP_CONFIGURATION_CTRL_BPC_SHIFT;
> > > @@ -174,20 +175,21 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl)
> > >   	msm_dp_catalog_ctrl_config_ctrl(ctrl->catalog, config);
> > >   }
> > > -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)
> > >   {
> > >   	u32 cc, tb;
> > >   	msm_dp_catalog_ctrl_lane_mapping(ctrl->catalog);
> > >   	msm_dp_catalog_setup_peripheral_flush(ctrl->catalog);
> > > -	msm_dp_ctrl_config_ctrl(ctrl);
> > > +	msm_dp_ctrl_config_ctrl(ctrl, msm_dp_panel);
> > >   	tb = msm_dp_link_get_test_bits_depth(ctrl->link,
> > > -		ctrl->panel->msm_dp_mode.bpp);
> > > +		msm_dp_panel->msm_dp_mode.bpp);
> > >   	cc = msm_dp_link_get_colorimetry_config(ctrl->link);
> > >   	msm_dp_catalog_ctrl_config_misc(ctrl->catalog, cc, tb);
> > > -	msm_dp_panel_timing_cfg(ctrl->panel);
> > > +	msm_dp_panel_timing_cfg(msm_dp_panel);
> > >   }
> > >   /*
> > > @@ -1317,7 +1319,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(ctrl, ctrl->panel);
> > 
> > Could you please explain, when is it fine to use ctrl->panel and when it
> > is not? Here you are passing msm_dp_panel to configure DP link for link
> > training. I don't think we need the panel for that, so just using
> > ctrl->panel here is incorrect too.
> > 
> Emm, If we need to program registers related to the pixel clock or DP link
> with MST(all of them need pass the stream_id to determine the register
> address), we should pass in msm_dp_panel. If we're only programming the
> other parts not related to the stream_id, passing in ctrl->panel is
> sufficient.

What is stored in ctrl->panel in the MST case? Can we split it into the
link and sink parts? E.g. dpcd or downstream_ports are not a part of the
panel itself. They describe a link between DPTX and DPRX.

Downstream_ports might be even more fun. How do we support MST hub being
connected through another MST hub?

> here in link tranning, it's right, we actually don't need to pass in the
> panel. But since in msm_dp_ctrl_config_ctrl, we will write config to DP0/DP1
> CONFIGURATION_CTRL, even mst2/mst3 link CONFIGURATION_CTRL. and this func
> will also been called in msm_dp_ctrl_configure_source_params. so we need add
> ctrl->panel here.

I'd prefer a cleaner interface here. Could you please separate
stream-dependent and stream-independent parts?

> > >   	link_info.num_lanes = ctrl->link->link_params.num_lanes;
> > >   	link_info.rate = ctrl->link->link_params.rate;

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 06/38] drm/msm/dp: move the pixel clock control to its own API
  2025-08-13 13:05       ` Dmitry Baryshkov
@ 2025-08-13 13:21         ` Dmitry Baryshkov
  0 siblings, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-08-13 13:21 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Wed, Aug 13, 2025 at 04:05:59PM +0300, Dmitry Baryshkov wrote:
> On Wed, Aug 13, 2025 at 07:56:41PM +0800, Yongxing Mou wrote:
> > 
> > 
> > On 2025/6/9 21:16, Dmitry Baryshkov wrote:
> > > On Mon, Jun 09, 2025 at 08:21:25PM +0800, Yongxing Mou wrote:
> > > > From: Abhinav Kumar <quic_abhinavk@quicinc.com>
> > > > 
> > > > 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 <quic_yongmou@quicinc.com>
> > > > ---
> > > >   drivers/gpu/drm/msm/dp/dp_ctrl.c | 98 +++++++++++++++++++++-------------------
> > > >   1 file changed, 52 insertions(+), 46 deletions(-)
> > > > 
> > > > diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> > > > index aee8e37655812439dfb65ae90ccb61b14e6e261f..ed00dd2538d98ddbc6bdcbd5fa154fd7043c48d6 100644
> > > > --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> > > > +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> > > > @@ -97,7 +97,7 @@ struct msm_dp_ctrl_private {
> > > >   	bool core_clks_on;
> > > >   	bool link_clks_on;
> > > > -	bool stream_clks_on;
> > > > +	bool pixel_clks_on;
> > > 
> > > As you are touching this part, how many paths lead to pixel clock being
> > > enabled and/or disabled? Can we sort them out and drop this flag, making
> > > sure that the clock can be enabled only in one place and disabled in
> > > another one (hopefully)?
> > > 
> > Here we only have 2 paths to enable/disable pixel,
> > 1.msm_dp_ctrl_process_phy_test_request 2.msm_dp_display_enable/disable.
> > both of them are in pairs. Maybe we can keep this state to make it easier to
> > access the on/off status of each of them in the case of 4 MST streams. when
> > we get the snapshot of the pixel clk, we can visit here.
> 
> I don't think I completely follow the MST part. I last reviewed your
> patches some time ago, so I might have forgotten part of the series.
> 
> We need some refcounting or state processing in order to identify if
> there is at least one active stream. Only if we are transitioning from
> the full-off to the at-least-one-on or from the last-one-on to the
> all-off we need to toggle the pixel clock. This means that we don't need
> the pixel_clks_on flag but some other kind of variable.

Oh, I'm sorry. It was about the pixel clock, not a link clock. But then
I also don't understand the MST case.

> 
> Note, I might be wrong here, please feel free to point me to a patch
> which handles that.
> 
> > > >   };
> > > >   static int msm_dp_aux_link_configure(struct drm_dp_aux *aux,
> > > > @@ -1406,8 +1406,8 @@ int msm_dp_ctrl_core_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl)
> > > >   	ctrl->core_clks_on = true;
> > > >   	drm_dbg_dp(ctrl->drm_dev, "enable core clocks \n");
> > > > -	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
> > > > -		   str_on_off(ctrl->stream_clks_on),
> > > > +	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
> > > > +		   str_on_off(ctrl->pixel_clks_on),
> > > >   		   str_on_off(ctrl->link_clks_on),
> > > >   		   str_on_off(ctrl->core_clks_on));
> > > > @@ -1425,8 +1425,8 @@ void msm_dp_ctrl_core_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl)
> > > >   	ctrl->core_clks_on = false;
> > > >   	drm_dbg_dp(ctrl->drm_dev, "disable core clocks \n");
> > > > -	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
> > > > -		   str_on_off(ctrl->stream_clks_on),
> > > > +	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
> > > > +		   str_on_off(ctrl->pixel_clks_on),
> > > >   		   str_on_off(ctrl->link_clks_on),
> > > >   		   str_on_off(ctrl->core_clks_on));
> > > >   }
> > > > @@ -1456,8 +1456,8 @@ static int msm_dp_ctrl_link_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl)
> > > >   	ctrl->link_clks_on = true;
> > > >   	drm_dbg_dp(ctrl->drm_dev, "enable link clocks\n");
> > > > -	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
> > > > -		   str_on_off(ctrl->stream_clks_on),
> > > > +	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
> > > > +		   str_on_off(ctrl->pixel_clks_on),
> > > >   		   str_on_off(ctrl->link_clks_on),
> > > >   		   str_on_off(ctrl->core_clks_on));
> > > > @@ -1475,8 +1475,8 @@ static void msm_dp_ctrl_link_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl)
> > > >   	ctrl->link_clks_on = false;
> > > >   	drm_dbg_dp(ctrl->drm_dev, "disabled link clocks\n");
> > > > -	drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n",
> > > > -		   str_on_off(ctrl->stream_clks_on),
> > > > +	drm_dbg_dp(ctrl->drm_dev, "pixel_clks:%s link_clks:%s core_clks:%s\n",
> > > > +		   str_on_off(ctrl->pixel_clks_on),
> > > >   		   str_on_off(ctrl->link_clks_on),
> > > >   		   str_on_off(ctrl->core_clks_on));
> > > >   }
> > > > @@ -1737,6 +1737,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->pixel_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->pixel_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->pixel_clks_on) {
> > > > +		clk_disable_unprepare(ctrl->pixel_clk);
> > > > +		ctrl->pixel_clks_on = false;
> > > > +	}
> > > > +}
> > > > +
> > > >   static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl,
> > > >   						struct msm_dp_panel *msm_dp_panel)
> > > >   {
> > > > @@ -1763,22 +1799,7 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
> > > >   	}
> > > >   	pixel_rate = msm_dp_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);
> > > > @@ -1998,8 +2019,8 @@ int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_li
> > > >   		   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);
> > > > +		   "core_clk_on=%d link_clk_on=%d pixel_clks_on=%d\n",
> > > > +		   ctrl->core_clks_on, ctrl->link_clks_on, ctrl->pixel_clks_on);
> > > >   	if (!ctrl->link_clks_on) { /* link clk is off */
> > > >   		ret = msm_dp_ctrl_enable_mainlink_clocks(ctrl);
> > > > @@ -2038,21 +2059,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;
> > > >   	}
> > > >   	/*
> > > > @@ -2080,7 +2090,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;
> > > >   }
> > > > @@ -2154,10 +2163,7 @@ void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl)
> > > >   	msm_dp_catalog_ctrl_reset(ctrl->catalog);
> > > > -	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
> > > > 
> > > 
> > 
> 
> -- 
> With best wishes
> Dmitry

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 03/38] drm/msm/dp: break up dp_display_enable into two parts
  2025-08-13 12:59       ` Dmitry Baryshkov
@ 2025-08-14  8:14         ` Yongxing Mou
  0 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-08-14  8:14 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/8/13 20:59, Dmitry Baryshkov wrote:
> On Wed, Aug 13, 2025 at 05:36:10PM +0800, Yongxing Mou wrote:
>>
>>
>> On 2025/6/9 20:59, Dmitry Baryshkov wrote:
>>> On Mon, Jun 09, 2025 at 08:21:22PM +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. Splite these two parts into
>>>> prepare/enable APIs, to support MST bridges_enable inserte
>>>
>>> typos
>>>
>>>> the MST payloads funcs between enable stream_clks and programe
>>>> register.
>>>>
>>>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>>>> ---
>>>>    drivers/gpu/drm/msm/dp/dp_ctrl.c    | 57 +++++++++++++--------
>>>>    drivers/gpu/drm/msm/dp/dp_ctrl.h    |  3 +-
>>>>    drivers/gpu/drm/msm/dp/dp_display.c | 99 +++++++++++++++++++++++++++----------
>>>>    drivers/gpu/drm/msm/dp/dp_display.h |  1 +
>>>>    4 files changed, 111 insertions(+), 49 deletions(-)
>>>>
> 
>>>> @@ -831,7 +831,37 @@ 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);
>>>> +	if (msm_dp_display->prepared) {
>>>> +		drm_dbg_dp(dp->drm_dev, "Link already setup, return\n");
>>>> +		return 0;
>>>> +	}
>>>
>>> How can it be prepared here? It is called at the beginning of the
>>> .atomic_enable() only, so there is no way this can be true.
>>>
>> Emm, sorry for forget this case.. Whern MST enabled,
>> msm_dp_display_prepare() will be called from mst_bridge_atomic_pre_enable,
>> that means, when second stream called this func, it already prepared, so we
>> should skip here. so this condition will really hit in MST case..
> 
> Then it should be refcounted. And, ideally, this should come later as a
> part of one of MST-enablement patches, when it actually makes sense
> 
Okay. I will move this part to appropriate patch.
>>>> +
>>>> +	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->hpd_state == ST_CONNECTED && !msm_dp_display->power_on) {
>>>> +		msm_dp_display_host_phy_init(dp);
>>>> +		force_link_train = true;
>>>> +	}
>>>> +
> 


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 26/38] drm/msm/dp: skip reading the EDID for MST cases
  2025-06-09 15:58   ` Dmitry Baryshkov
@ 2025-08-14  8:22     ` Yongxing Mou
  2025-08-14  9:27       ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-08-14  8:22 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/9 23:58, Dmitry Baryshkov wrote:
> On Mon, Jun 09, 2025 at 08:21:45PM +0800, Yongxing Mou wrote:
>> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>
>> For MST cases, EDID is handled through AUX sideband messaging.
>> Skip the EDID read during hotplug handle for MST cases.
> 
> Why? It makes sense to read it during the HPD processing, ping HDMI
> codec, update CEC info, etc.
> 
For MST case to read EDID. we will use drm_dp_mst_edid_read when MST 
connetors .get_modes() called.
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>> ---
>>   drivers/gpu/drm/msm/dp/dp_display.c | 8 +++++---
>>   1 file changed, 5 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
>> index 88cae0ca66015377e59bee757462edeae5ae91bf..b1b025d1d356046f8f9e3d243fc774185df24318 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>> @@ -438,9 +438,11 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
>>   	if (rc)
>>   		goto end;
>>   
>> -	rc = msm_dp_panel_read_edid(dp->panel, connector);
>> -	if (rc)
>> -		goto end;
>> +	if (!dp->mst_supported || !drm_dp_read_mst_cap(dp->aux, dp->panel->dpcd)) {
>> +		rc = msm_dp_panel_read_edid(dp->panel, connector);
>> +		if (rc)
>> +			goto end;
>> +	}
>>   
>>   	msm_dp_link_process_request(dp->link);
>>   
>>
>> -- 
>> 2.34.1
>>
> 


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 31/38] drm/msm/dp: propagate MST state changes to dp mst module
  2025-06-09 14:56   ` Dmitry Baryshkov
@ 2025-08-14  8:24     ` Yongxing Mou
  0 siblings, 0 replies; 108+ messages in thread
From: Yongxing Mou @ 2025-08-14  8:24 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/9 22:56, Dmitry Baryshkov wrote:
> On Mon, Jun 09, 2025 at 08:21:50PM +0800, Yongxing Mou wrote:
>> Introduce APIs to update the MST state change to MST framework when
>> device is plugged/unplugged.
>>
>> Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> 
> Hmm, who is the author of the patch?
> 
Sorry for that. will update it . author is Abhinav
>> ---
>>   drivers/gpu/drm/msm/dp/dp_display.c | 20 ++++++++++++++++++++
>>   drivers/gpu/drm/msm/dp/dp_mst_drm.c | 15 +++++++++++++++
>>   drivers/gpu/drm/msm/dp/dp_mst_drm.h |  1 +
>>   3 files changed, 36 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
>> index 323d1c0a9efa4fa30ce97317e873607c54409a11..9dbcf4553cad70c9e3722160a87403fc815765d7 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>> @@ -29,6 +29,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);
>> @@ -420,6 +421,17 @@ static void msm_dp_display_mst_init(struct msm_dp_display_private *dp)
>>   	msm_dp->mst_active = true;
>>   }
>>   
>> +static void msm_dp_display_set_mst_mgr_state(struct msm_dp_display_private *dp,
>> +					     bool state)
>> +{
>> +	if (!dp->msm_dp_display.mst_active)
>> +		return;
>> +
>> +	msm_dp_mst_display_set_mgr_state(&dp->msm_dp_display, state);
>> +
>> +	drm_dbg_dp(dp->drm_dev, "mst_mgr_state: %d\n", state);
> 
> Yet-another-oneliner? Noooo. Really, no.
> 
Got it. Will remove it and call msm_dp_mst_display_set_mgr_state directly.
>> +}
>> +
>>   static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
>>   {
>>   	struct drm_connector *connector = dp->msm_dp_display.connector;
> 


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 32/38] drm/msm: add support for non-blocking commits
  2025-06-09 14:50   ` Dmitry Baryshkov
@ 2025-08-14  8:54     ` Yongxing Mou
  2025-08-14  9:28       ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-08-14  8:54 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/9 22:50, Dmitry Baryshkov wrote:
> On Mon, Jun 09, 2025 at 08:21:51PM +0800, Yongxing Mou wrote:
>> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>
>> Hook up the mst framework APIs with atomic_commit_setup() and
>> atomic_commit_tail() APIs to handle non-blocking commits.
> 
> Were non-blocking commits not supported before this patch?
> 
This patch only work for MST case. and for SST case, i'm not sure.  I 
see commit_tail() called drm_atomic_helper_wait_for_dependencies() in 
DRM codes.. Hopefully this can answer your question.
>>
>> Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
>> ---
>>   drivers/gpu/drm/msm/msm_atomic.c | 3 +++
>>   drivers/gpu/drm/msm/msm_kms.c    | 2 ++
>>   2 files changed, 5 insertions(+)
>>
> 


^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 33/38] drm/msm: initialize DRM MST encoders for DP controllers
  2025-06-09 14:17   ` Dmitry Baryshkov
@ 2025-08-14  9:11     ` Yongxing Mou
  2025-08-14  9:29       ` Dmitry Baryshkov
  0 siblings, 1 reply; 108+ messages in thread
From: Yongxing Mou @ 2025-08-14  9:11 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar



On 2025/6/9 22:17, Dmitry Baryshkov wrote:
> On Mon, Jun 09, 2025 at 08:21:52PM +0800, Yongxing Mou wrote:
>> From: Abhinav Kumar <quic_abhinavk@quicinc.com>
>>
>> Initiliaze 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 <quic_yongmou@quicinc.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_display.c         | 14 ++++++++++++++
> 
> Please don't mix DP and DPU changes in a single patch.
> 
Got it, thanks, will separate it.
>>   drivers/gpu/drm/msm/msm_drv.h               | 13 +++++++++++++
>>   4 files changed, 51 insertions(+), 1 deletion(-)
>>
>> 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 1fd82b6747e9058ce11dc2620729921492d5ebdd..45fedf7e74e9c6dfed4bde57eb675e3dd1762fc7 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
>> @@ -652,7 +652,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->dp); i++) {
>>   		if (!priv->dp[i])
>> @@ -675,6 +676,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->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_bridge_init(priv->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_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
>> index 9dbcf4553cad70c9e3722160a87403fc815765d7..ab1ad0cb6427eb4f86ee8ac6c76788b1a78892a8 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>> @@ -1417,6 +1417,20 @@ static int msm_dp_display_get_connector_type(struct platform_device *pdev,
>>   	return connector_type;
>>   }
>>   
>> +int msm_dp_get_mst_max_stream(struct msm_dp *dp_display)
>> +{
>> +	struct msm_dp_display_private *dp_priv;
>> +
>> +	dp_priv = container_of(dp_display, struct msm_dp_display_private, msm_dp_display);
>> +
>> +	return dp_priv->max_stream;
>> +}
>> +
>> +int msm_dp_mst_bridge_init(struct msm_dp *dp_display, struct drm_encoder *encoder)
>> +{
>> +	return msm_dp_mst_drm_bridge_init(dp_display, encoder);
> 
> What's the point in this oneliner?
> 
Emm, here we consider declaring the msm_dp_mst_drm_bridge_init() in 
msm_drv.h and drop the one-line wrapper.


>> +}
>> +
>>   static int msm_dp_display_probe(struct platform_device *pdev)
>>   {
>>   	int rc = 0;
>> diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
>> index a65077855201746c37ee742364b61116565f3794..dd403107b640ee5ef333d2773b52e38e3869155f 100644
>> --- a/drivers/gpu/drm/msm/msm_drv.h
>> +++ b/drivers/gpu/drm/msm/msm_drv.h
>> @@ -372,6 +372,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_bridge_init(struct msm_dp *dp_display, struct drm_encoder *encoder);
>> +
>>   #else
>>   static inline int __init msm_dp_register(void)
>>   {
>> @@ -388,6 +391,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_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	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 26/38] drm/msm/dp: skip reading the EDID for MST cases
  2025-08-14  8:22     ` Yongxing Mou
@ 2025-08-14  9:27       ` Dmitry Baryshkov
  0 siblings, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-08-14  9:27 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Thu, Aug 14, 2025 at 04:22:44PM +0800, Yongxing Mou wrote:
> 
> 
> On 2025/6/9 23:58, Dmitry Baryshkov wrote:
> > On Mon, Jun 09, 2025 at 08:21:45PM +0800, Yongxing Mou wrote:
> > > From: Abhinav Kumar <quic_abhinavk@quicinc.com>
> > > 
> > > For MST cases, EDID is handled through AUX sideband messaging.
> > > Skip the EDID read during hotplug handle for MST cases.
> > 
> > Why? It makes sense to read it during the HPD processing, ping HDMI
> > codec, update CEC info, etc.
> > 
> For MST case to read EDID. we will use drm_dp_mst_edid_read when MST
> connetors .get_modes() called.

I see that other MST drivers indeed read EDID from get_modes(). The only
issue that I can foresee is the audio handling. We've discussed that
some time ago (for the HDMI implementation) and it's generally expected
that we notify ASoC (and provide ELD) from the HPD (detect) path rather
than the get_modes() or e.g. atomic_enable().

Note: AMD / Radeon, Nouveau and Intel drivers don't have this problem
since they don't use ASoC and hdmi_codec ops.

> > > 
> > > Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> > > Signed-off-by: Yongxing Mou <quic_yongmou@quicinc.com>
> > > ---
> > >   drivers/gpu/drm/msm/dp/dp_display.c | 8 +++++---
> > >   1 file changed, 5 insertions(+), 3 deletions(-)
> > > 
> > > diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> > > index 88cae0ca66015377e59bee757462edeae5ae91bf..b1b025d1d356046f8f9e3d243fc774185df24318 100644
> > > --- a/drivers/gpu/drm/msm/dp/dp_display.c
> > > +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> > > @@ -438,9 +438,11 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
> > >   	if (rc)
> > >   		goto end;
> > > -	rc = msm_dp_panel_read_edid(dp->panel, connector);
> > > -	if (rc)
> > > -		goto end;
> > > +	if (!dp->mst_supported || !drm_dp_read_mst_cap(dp->aux, dp->panel->dpcd)) {
> > > +		rc = msm_dp_panel_read_edid(dp->panel, connector);
> > > +		if (rc)
> > > +			goto end;
> > > +	}
> > >   	msm_dp_link_process_request(dp->link);
> > > 
> > > -- 
> > > 2.34.1
> > > 
> > 
> 

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 32/38] drm/msm: add support for non-blocking commits
  2025-08-14  8:54     ` Yongxing Mou
@ 2025-08-14  9:28       ` Dmitry Baryshkov
  0 siblings, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-08-14  9:28 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Thu, Aug 14, 2025 at 04:54:16PM +0800, Yongxing Mou wrote:
> 
> 
> On 2025/6/9 22:50, Dmitry Baryshkov wrote:
> > On Mon, Jun 09, 2025 at 08:21:51PM +0800, Yongxing Mou wrote:
> > > From: Abhinav Kumar <quic_abhinavk@quicinc.com>
> > > 
> > > Hook up the mst framework APIs with atomic_commit_setup() and
> > > atomic_commit_tail() APIs to handle non-blocking commits.
> > 
> > Were non-blocking commits not supported before this patch?
> > 
> This patch only work for MST case. and for SST case, i'm not sure.  I see
> commit_tail() called drm_atomic_helper_wait_for_dependencies() in DRM
> codes.. Hopefully this can answer your question.

This should be a part of the commit message.

> > > 
> > > Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
> > > ---
> > >   drivers/gpu/drm/msm/msm_atomic.c | 3 +++
> > >   drivers/gpu/drm/msm/msm_kms.c    | 2 ++
> > >   2 files changed, 5 insertions(+)
> > > 
> > 
> 

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 108+ messages in thread

* Re: [PATCH v2 33/38] drm/msm: initialize DRM MST encoders for DP controllers
  2025-08-14  9:11     ` Yongxing Mou
@ 2025-08-14  9:29       ` Dmitry Baryshkov
  0 siblings, 0 replies; 108+ messages in thread
From: Dmitry Baryshkov @ 2025-08-14  9:29 UTC (permalink / raw)
  To: Yongxing Mou
  Cc: Rob Clark, Abhinav Kumar, Jessica Zhang, Sean Paul,
	Marijn Suijten, David Airlie, Simona Vetter, linux-arm-msm,
	dri-devel, freedreno, linux-kernel, Abhinav Kumar

On Thu, Aug 14, 2025 at 05:11:13PM +0800, Yongxing Mou wrote:
> 
> 
> On 2025/6/9 22:17, Dmitry Baryshkov wrote:
> > On Mon, Jun 09, 2025 at 08:21:52PM +0800, Yongxing Mou wrote:
> > > From: Abhinav Kumar <quic_abhinavk@quicinc.com>
> > > 
> > > Initiliaze 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 <quic_yongmou@quicinc.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_display.c         | 14 ++++++++++++++
> > 
> > Please don't mix DP and DPU changes in a single patch.
> > 
> Got it, thanks, will separate it.
> > >   drivers/gpu/drm/msm/msm_drv.h               | 13 +++++++++++++
> > >   4 files changed, 51 insertions(+), 1 deletion(-)
> > > 
> > > 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 1fd82b6747e9058ce11dc2620729921492d5ebdd..45fedf7e74e9c6dfed4bde57eb675e3dd1762fc7 100644
> > > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> > > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> > > @@ -652,7 +652,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->dp); i++) {
> > >   		if (!priv->dp[i])
> > > @@ -675,6 +676,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->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_bridge_init(priv->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_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> > > index 9dbcf4553cad70c9e3722160a87403fc815765d7..ab1ad0cb6427eb4f86ee8ac6c76788b1a78892a8 100644
> > > --- a/drivers/gpu/drm/msm/dp/dp_display.c
> > > +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> > > @@ -1417,6 +1417,20 @@ static int msm_dp_display_get_connector_type(struct platform_device *pdev,
> > >   	return connector_type;
> > >   }
> > > +int msm_dp_get_mst_max_stream(struct msm_dp *dp_display)
> > > +{
> > > +	struct msm_dp_display_private *dp_priv;
> > > +
> > > +	dp_priv = container_of(dp_display, struct msm_dp_display_private, msm_dp_display);
> > > +
> > > +	return dp_priv->max_stream;
> > > +}
> > > +
> > > +int msm_dp_mst_bridge_init(struct msm_dp *dp_display, struct drm_encoder *encoder)
> > > +{
> > > +	return msm_dp_mst_drm_bridge_init(dp_display, encoder);
> > 
> > What's the point in this oneliner?
> > 
> Emm, here we consider declaring the msm_dp_mst_drm_bridge_init() in
> msm_drv.h and drop the one-line wrapper.

Just do it, please.

> 
> 
> > > +}
> > > +
> > >   static int msm_dp_display_probe(struct platform_device *pdev)
> > >   {
> > >   	int rc = 0;
> > > diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
> > > index a65077855201746c37ee742364b61116565f3794..dd403107b640ee5ef333d2773b52e38e3869155f 100644
> > > --- a/drivers/gpu/drm/msm/msm_drv.h
> > > +++ b/drivers/gpu/drm/msm/msm_drv.h
> > > @@ -372,6 +372,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_bridge_init(struct msm_dp *dp_display, struct drm_encoder *encoder);
> > > +
> > >   #else
> > >   static inline int __init msm_dp_register(void)
> > >   {
> > > @@ -388,6 +391,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_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] 108+ messages in thread

end of thread, other threads:[~2025-08-14  9:29 UTC | newest]

Thread overview: 108+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-09 12:21 [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 01/38] drm/msm/dp: split msm_dp_panel_read_sink_caps() into two parts and drop panel drm_edid Yongxing Mou
2025-06-09 12:41   ` Dmitry Baryshkov
2025-06-25  8:43     ` Yongxing Mou
2025-06-25 13:32       ` Dmitry Baryshkov
2025-06-27  7:49         ` Yongxing Mou
2025-06-27 12:40           ` Dmitry Baryshkov
2025-08-06  9:03             ` Yongxing Mou
2025-08-06 10:39               ` Dmitry Baryshkov
2025-06-09 12:21 ` [PATCH v2 02/38] drm/msm/dp: remove dp_display's dp_mode and use dp_panel's instead Yongxing Mou
2025-06-09 12:48   ` Dmitry Baryshkov
2025-06-25 12:34     ` Yongxing Mou
2025-06-25 14:03       ` Dmitry Baryshkov
2025-06-27  8:40         ` Yongxing Mou
2025-06-27 12:44           ` Dmitry Baryshkov
2025-08-06  9:22             ` Yongxing Mou
2025-08-06 10:41               ` Dmitry Baryshkov
2025-06-27 13:37           ` Dmitry Baryshkov
2025-06-09 12:21 ` [PATCH v2 03/38] drm/msm/dp: break up dp_display_enable into two parts Yongxing Mou
2025-06-09 12:59   ` Dmitry Baryshkov
2025-08-06  9:24     ` Yongxing Mou
2025-08-13  9:36     ` Yongxing Mou
2025-08-13 12:59       ` Dmitry Baryshkov
2025-08-14  8:14         ` Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 04/38] drm/msm/dp: re-arrange dp_display_disable() into functional parts Yongxing Mou
2025-06-09 13:05   ` Dmitry Baryshkov
2025-08-06  9:30     ` Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 05/38] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it Yongxing Mou
2025-06-09 13:12   ` Dmitry Baryshkov
2025-08-13  9:52     ` Yongxing Mou
2025-08-13 13:20       ` Dmitry Baryshkov
2025-06-09 12:21 ` [PATCH v2 06/38] drm/msm/dp: move the pixel clock control to its own API Yongxing Mou
2025-06-09 13:16   ` Dmitry Baryshkov
2025-08-13 11:56     ` Yongxing Mou
2025-08-13 13:05       ` Dmitry Baryshkov
2025-08-13 13:21         ` Dmitry Baryshkov
2025-06-09 12:21 ` [PATCH v2 07/38] drm/msm/dp: split dp_ctrl_off() into stream and link parts Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 08/38] drm/msm/dp: make bridge helpers use dp_display to allow re-use Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 09/38] drm/msm/dp: separate dp_display_prepare() into its own API Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 10/38] drm/msm/dp: introduce the max_streams for dp controller Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 11/38] drm/msm/dp: introduce stream_id for each DP panel Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 12/38] drm/msm/dp: add support for programming p1/p2/p3 register block Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 13/38] drm/msm/dp: use stream_id to change offsets in dp_catalog Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 14/38] drm/msm/dp: Add catalog support for 3rd/4th stream MST Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 15/38] drm/msm/dp: add support to send ACT packets for MST Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 16/38] drm/msm/dp: add support to program mst support in mainlink Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 17/38] drm/msm/dp: no need to update tu calculation for mst Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 18/38] drm/msm/dp: add support for mst channel slot allocation Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 19/38] drm/msm/dp: add support to send vcpf packets in dp controller Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 20/38] drm/msm/dp: always program MST_FIFO_CONSTANT_FILL for MST Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 21/38] drm/msm/dp: abstract out the dp_display stream helpers to accept a panel Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 22/38] drm/msm/dp: move link related operations to dp_display_unprepare() Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 23/38] drm/msm/dp: replace power_on with active_stream_cnt for dp_display Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 24/38] drm/msm/dp: make the SST bridge disconnected when mst is active Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 25/38] drm/msm/dp: add an API to initialize MST on sink side Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 26/38] drm/msm/dp: skip reading the EDID for MST cases Yongxing Mou
2025-06-09 15:58   ` Dmitry Baryshkov
2025-08-14  8:22     ` Yongxing Mou
2025-08-14  9:27       ` Dmitry Baryshkov
2025-06-09 12:21 ` [PATCH v2 27/38] drm/msm/dp: add dp_display_get_panel() to initialize DP panel Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 28/38] drm/msm/dp: add dp_mst_drm to manage DP MST bridge operations Yongxing Mou
2025-06-09 15:57   ` Dmitry Baryshkov
2025-06-11 11:39     ` Yongxing Mou
2025-06-11 14:27       ` Dmitry Baryshkov
2025-06-09 12:21 ` [PATCH v2 29/38] drm/msm/dp: add connector abstraction for DP MST Yongxing Mou
2025-06-09 15:44   ` Dmitry Baryshkov
2025-06-11 12:06     ` Yongxing Mou
2025-06-11 14:31       ` Dmitry Baryshkov
2025-06-16 14:09         ` Yongxing Mou
2025-06-16 14:47           ` Dmitry Baryshkov
     [not found]           ` <bd0fba5c-9e38-4a40-adf9-cc70fa2d0f57@oss.qualcomm.com>
     [not found]             ` <ad1db558-c33e-4788-9f25-cac6c21713f1@quicinc.com>
2025-06-19 11:33               ` Dmitry Baryshkov
2025-06-24  9:56                 ` Yongxing Mou
2025-06-24 22:25                   ` Dmitry Baryshkov
2025-06-09 15:51   ` Dmitry Baryshkov
2025-06-16 12:43     ` Yongxing Mou
2025-06-16 13:48       ` Dmitry Baryshkov
2025-06-17  7:52         ` Yongxing Mou
2025-06-17 10:04           ` Dmitry Baryshkov
2025-06-09 12:21 ` [PATCH v2 30/38] drm/msm/dp: add HPD callback for dp MST Yongxing Mou
2025-06-09 15:01   ` Dmitry Baryshkov
2025-06-09 12:21 ` [PATCH v2 31/38] drm/msm/dp: propagate MST state changes to dp mst module Yongxing Mou
2025-06-09 14:56   ` Dmitry Baryshkov
2025-08-14  8:24     ` Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 32/38] drm/msm: add support for non-blocking commits Yongxing Mou
2025-06-09 14:50   ` Dmitry Baryshkov
2025-08-14  8:54     ` Yongxing Mou
2025-08-14  9:28       ` Dmitry Baryshkov
2025-06-09 12:21 ` [PATCH v2 33/38] drm/msm: initialize DRM MST encoders for DP controllers Yongxing Mou
2025-06-09 14:17   ` Dmitry Baryshkov
2025-08-14  9:11     ` Yongxing Mou
2025-08-14  9:29       ` Dmitry Baryshkov
2025-06-09 12:21 ` [PATCH v2 34/38] drm/msm/dp: initialize dp_mst module for each DP MST controller Yongxing Mou
2025-06-09 14:27   ` Dmitry Baryshkov
2025-06-09 12:21 ` [PATCH v2 35/38] drm/msm/dpu: use msm_dp_get_mst_intf_id() to get the intf id Yongxing Mou
2025-06-09 14:44   ` Dmitry Baryshkov
2025-06-09 12:21 ` [PATCH v2 36/38] drm/msm/dp: mark ST_DISCONNECTED only if all streams are disabled Yongxing Mou
2025-06-09 12:21 ` [PATCH v2 37/38] drm/msm/dp: fix the intf_type of MST interfaces Yongxing Mou
2025-06-09 14:45   ` Dmitry Baryshkov
2025-06-09 12:21 ` [PATCH v2 38/38] drm/msm/dp: Add MST stream support for SA8775P DP controller 0 and 1 Yongxing Mou
2025-06-09 14:47   ` Dmitry Baryshkov
2025-06-09 12:36 ` [PATCH v2 00/38] drm/msm/dp: Add MST support for MSM chipsets Dmitry Baryshkov
2025-06-10  4:47   ` Yongxing Mou
2025-06-10  8:30     ` Dmitry Baryshkov
2025-06-11 12:08       ` Yongxing Mou
2025-06-11 14:35         ` Dmitry Baryshkov
2025-06-09 16:07 ` Dmitry Baryshkov
2025-06-10  4:31   ` Yongxing Mou
2025-06-10  8:31     ` 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).