[PATCH v4 13/39] drm/msm/dp: introduce stream_id for each DP panel

Yongxing Mou
2026-04-10T09:35:02Z

From: Abhinav Kumar <quic_abhinavk@quicinc.com>

With MST, each DP controller can handle multiple streams.
There shall be one dp_panel for each stream but the dp_display
object shall be shared among them. To represent this abstraction,
create a stream_id for each DP panel which shall be set by the
MST stream. For SST, default this to stream 0.

Use the stream ID to control the pixel clock of that respective
stream by extending the clock handles and state tracking of the
DP pixel clock to an array of max supported streams. The maximum
streams currently is 4.

Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_ctrl.c | 57 +++++++++++++++++++++++--------------
drivers/gpu/drm/msm/dp/dp_ctrl.h | 2 +-
drivers/gpu/drm/msm/dp/dp_display.c | 24 ++++++++++++++--
drivers/gpu/drm/msm/dp/dp_display.h | 2 ++
drivers/gpu/drm/msm/dp/dp_panel.h | 11 +++++++
5 files changed, 71 insertions(+), 25 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 120ec00884e5..fb6396727628 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -127,7 +127,7 @@ struct msm_dp_ctrl_private {
unsigned int num_link_clks;
struct clk_bulk_data *link_clks;

- struct clk *pixel_clk;
+ struct clk *pixel_clk[DP_STREAM_MAX];

union phy_configure_opts phy_opts;

@@ -139,7 +139,7 @@ struct msm_dp_ctrl_private {

bool core_clks_on;
bool link_clks_on;
- bool stream_clks_on;
+ bool stream_clks_on[DP_STREAM_MAX];
};

static inline u32 msm_dp_read_ahb(const struct msm_dp_ctrl_private *ctrl, u32 offset)
@@ -2176,39 +2176,40 @@ static bool msm_dp_ctrl_send_phy_test_pattern(struct msm_dp_ctrl_private *ctrl)
return success;
}

-static int msm_dp_ctrl_on_pixel_clk(struct msm_dp_ctrl_private *ctrl, unsigned long pixel_rate)
+static int msm_dp_ctrl_on_pixel_clk(struct msm_dp_ctrl_private *ctrl, unsigned long pixel_rate,
+ enum msm_dp_stream_id stream_id)
{
int ret;

- ret = clk_set_rate(ctrl->pixel_clk, pixel_rate * 1000);
+ ret = clk_set_rate(ctrl->pixel_clk[stream_id], pixel_rate * 1000);
if (ret) {
DRM_ERROR("Failed to set pixel clock rate. ret=%d\n", ret);
return ret;
}

- if (ctrl->stream_clks_on) {
+ if (ctrl->stream_clks_on[stream_id]) {
drm_dbg_dp(ctrl->drm_dev, "pixel clks already enabled\n");
} else {
- ret = clk_prepare_enable(ctrl->pixel_clk);
+ ret = clk_prepare_enable(ctrl->pixel_clk[stream_id]);
if (ret) {
DRM_ERROR("Failed to start pixel clocks. ret=%d\n", ret);
return ret;
}
- ctrl->stream_clks_on = true;
+ ctrl->stream_clks_on[stream_id] = true;
}

return ret;
}

-void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl)
+void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl, enum msm_dp_stream_id stream_id)
{
struct msm_dp_ctrl_private *ctrl;

ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);

- if (ctrl->stream_clks_on) {
- clk_disable_unprepare(ctrl->pixel_clk);
- ctrl->stream_clks_on = false;
+ if (ctrl->stream_clks_on[stream_id]) {
+ clk_disable_unprepare(ctrl->pixel_clk[stream_id]);
+ ctrl->stream_clks_on[stream_id] = false;
}
}

@@ -2228,7 +2229,7 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
* running. Add the global reset just before disabling the
* link clocks and core clocks.
*/
- msm_dp_ctrl_off_pixel_clk(&ctrl->msm_dp_ctrl);
+ msm_dp_ctrl_off_pixel_clk(&ctrl->msm_dp_ctrl, ctrl->panel->stream_id);
msm_dp_ctrl_off_link(&ctrl->msm_dp_ctrl);

ret = msm_dp_ctrl_on_link(&ctrl->msm_dp_ctrl);
@@ -2238,7 +2239,7 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl
}

pixel_rate = ctrl->panel->msm_dp_mode.drm_mode.clock;
- ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate);
+ ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate, ctrl->panel->stream_id);

msm_dp_ctrl_send_phy_test_pattern(ctrl);

@@ -2525,9 +2526,8 @@ int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_li
ctrl->link->link_params.rate,
ctrl->link->link_params.num_lanes);

- drm_dbg_dp(ctrl->drm_dev,
- "core_clk_on=%d link_clk_on=%d stream_clk_on=%d\n",
- ctrl->core_clks_on, ctrl->link_clks_on, ctrl->stream_clks_on);
+ drm_dbg_dp(ctrl->drm_dev, "core_clk_on=%d link_clk_on=%d\n",
+ ctrl->core_clks_on, ctrl->link_clks_on);

if (!ctrl->link_clks_on) { /* link clk is off */
ret = msm_dp_ctrl_enable_mainlink_clocks(ctrl);
@@ -2567,7 +2567,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)
return ret;

@@ -2629,8 +2629,6 @@ void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl)
ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
phy = ctrl->phy;

- msm_dp_panel_disable_vsc_sdp(ctrl->panel);
-
msm_dp_ctrl_mainlink_disable(ctrl);

msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl);
@@ -2702,6 +2700,13 @@ static const char *ctrl_clks[] = {
"ctrl_link_iface",
};

+static const char * const pixel_clks[] = {
+ "stream_pixel",
+ "stream_1_pixel",
+ "stream_2_pixel",
+ "stream_3_pixel",
+};
+
static int msm_dp_ctrl_clk_init(struct msm_dp_ctrl *msm_dp_ctrl)
{
struct msm_dp_ctrl_private *ctrl;
@@ -2735,9 +2740,17 @@ 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);
+ for (i = DP_STREAM_0; i < DP_STREAM_MAX; i++) {
+ ctrl->pixel_clk[i] = devm_clk_get(dev, pixel_clks[i]);
+
+ if (i == 0 && IS_ERR(ctrl->pixel_clk[i]))
+ return PTR_ERR(ctrl->pixel_clk[i]);
+
+ if (IS_ERR(ctrl->pixel_clk[i])) {
+ DRM_DEBUG_DP("stream %d pixel clock not exist", i);
+ break;
+ }
+ }

return 0;
}
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index b83be2252a9b..b9f0705b03ba 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -20,7 +20,7 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl);
int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *msm_dp_panel);
int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train);
void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl);
-void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl);
+void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl, enum msm_dp_stream_id stream_id);
void msm_dp_ctrl_push_idle(struct msm_dp_ctrl *msm_dp_ctrl);
irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl);
void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 32ad00e326ba..736b621c0531 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -730,7 +730,7 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)

/* set dongle to D3 (power off) mode */
msm_dp_link_psm_config(dp->link, &dp->panel->link_info, true);
- msm_dp_ctrl_off_pixel_clk(dp->ctrl);
+ msm_dp_ctrl_off_pixel_clk(dp->ctrl, dp->panel->stream_id);
msm_dp_ctrl_off_link(dp->ctrl);
/* re-init the PHY so that we can listen to Dongle disconnect */
msm_dp_ctrl_reinit_phy(dp->ctrl);
@@ -739,7 +739,7 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
* unplugged interrupt
* dongle unplugged out of DUT
*/
- msm_dp_ctrl_off_pixel_clk(dp->ctrl);
+ msm_dp_ctrl_off_pixel_clk(dp->ctrl, dp->panel->stream_id);
msm_dp_ctrl_off_link(dp->ctrl);
msm_dp_display_host_phy_exit(dp);
}
@@ -750,6 +750,24 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp)
return 0;
}

+int msm_dp_display_set_stream_info(struct msm_dp *msm_dp_display,
+ struct msm_dp_panel *panel, enum msm_dp_stream_id stream_id)
+{
+ int rc = 0;
+ struct msm_dp_display_private *dp;
+
+ dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
+
+ if (!dp) {
+ DRM_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ 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
@@ -1451,6 +1469,8 @@ void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display)

dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);

+ msm_dp_display_set_stream_info(msm_dp_display, dp->panel, 0);
+
rc = msm_dp_display_enable(dp);
if (rc)
DRM_ERROR("DP display enable failed, rc=%d\n", rc);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 295da7ae0047..a5c6ed5b18e4 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -41,5 +41,7 @@ void msm_dp_display_atomic_enable(struct msm_dp *dp_display);
enum drm_mode_status msm_dp_display_mode_valid(struct msm_dp *dp,
const struct drm_display_info *info,
const struct drm_display_mode *mode);
+int msm_dp_display_set_stream_info(struct msm_dp *msm_dp_display,
+ struct msm_dp_panel *panel, enum msm_dp_stream_id stream_id);

#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 53b7b4463551..21f7f30e6dfd 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -27,6 +27,15 @@ struct msm_dp_panel_psr {
u8 capabilities;
};

+/* stream id */
+enum msm_dp_stream_id {
+ DP_STREAM_0,
+ DP_STREAM_1,
+ DP_STREAM_2,
+ DP_STREAM_3,
+ DP_STREAM_MAX,
+};
+
struct msm_dp_panel {
/* dpcd raw data */
u8 dpcd[DP_RECEIVER_CAP_SIZE];
@@ -40,6 +49,8 @@ struct msm_dp_panel {
bool vsc_sdp_supported;
u32 hw_revision;

+ enum msm_dp_stream_id stream_id;
+
u32 max_bw_code;
};

--
2.43.0