[PATCH 5/5] drm/msm/dpu: enable quad-pipe for DSC and dual-DSI case

From: Dmitry Baryshkov

Date: Fri Oct 02 2026 - 20:05:15 EST


From: Jun Nie <jun.nie@xxxxxxxxxx>

To support high-resolution cases that exceed the width constrain
or scenarios that surpass the maximum MDP clock rate, additional
pipes are necessary to enable parallel data processing within
the width constraints and MDP clock rate.

Expand pipe array size to 4. Request 4 mixers and 4 DSCs for
high-resolution cases where dual interfaces are enabled for virtual
plane case. More use cases can be incorporated later if quad-pipe
capabilities are required.

Signed-off-by: Jun Nie <jun.nie@xxxxxxxxxx>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxx>
Reviewed-by: Jessica Zhang <quic_jesszhan@xxxxxxxxxxx>
[DB: reworked the mixer and DSC selection in dpu_crtc_get_topology()]
[DB: dropped the DPU < 5 limitation]
[DB: compare the clock rate per interface]
[DB: limited the clone mode to two mixers]
Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxxxxxxxx>
---
drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 51 ++++++++++++++++++------
drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h | 6 +--
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 28 ++-----------
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 2 +
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 2 -
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 2 +-
drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h | 2 +-
7 files changed, 49 insertions(+), 44 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
index 57f50a3cd9cf..0e4f2fd0a8f3 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
@@ -200,7 +200,7 @@ static int dpu_crtc_get_lm_crc(struct drm_crtc *crtc,
struct dpu_crtc_state *crtc_state)
{
struct dpu_crtc_mixer *m;
- u32 crcs[CRTC_DUAL_MIXERS];
+ u32 crcs[CRTC_QUAD_MIXERS];

int rc = 0;
int i;
@@ -1382,39 +1382,66 @@ static struct msm_display_topology dpu_crtc_get_topology(
struct drm_crtc_state *crtc_state)
{
struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+ const struct dpu_caps *caps = dpu_kms->catalog->caps;
struct msm_display_topology topology = {0};
struct drm_encoder *drm_enc;
+ bool dsc = false;
+ u64 mode_rate;

topology.cwb_enabled = drm_crtc_in_clone_mode(crtc_state);

- drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask)
+ drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask) {
dpu_encoder_update_topology(drm_enc, &topology, crtc_state->state,
&crtc_state->adjusted_mode);
+ if (dpu_encoder_get_dsc_config(drm_enc))
+ dsc = true;
+ }

/*
* Datapath topology selection
*
* Dual display
* 2 LM, 2 INTF ( Split display using 2 interfaces)
+ * 4 LM, 2 INTF ( Split display using 2 interfaces and stream merge
+ * to support high resolution interfaces if virtual
+ * plane is enabled)
+ * If DSC is enabled, use 2:2:2 for 2 LMs case, and 4:4:2 for 4 LMs
+ * case.
*
* Single display
* 1 LM, 1 INTF
* 2 LM, 1 INTF (stream merge to support high resolution interfaces)
*
- * If DSC is enabled, use 2 LMs for 2:2:1 topology
+ * If DSC is enabled, use 2 LMs for 2:2:1 topology for single display
+ * to support legacy devices that use this topology. Use 1:1:1 topology
+ * if there is only one DSC engine in SoC.
*
* Add dspps to the reservation requirements if ctm or gamma_lut are requested
*/

- if (topology.num_intf == 2)
- topology.num_lm = 2;
- else if (topology.num_dsc == 2)
- topology.num_lm = 2;
- else if (dpu_kms->catalog->caps->has_3d_merge &&
- topology.num_dsc == 0)
- topology.num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1;
- else
- topology.num_lm = 1;
+ mode_rate = (u64)mode->hdisplay * mode->vtotal * drm_mode_vrefresh(mode);
+
+ if (topology.num_intf == 2) {
+ if (dpu_use_virtual_planes && caps->has_3d_merge &&
+ !topology.cwb_enabled &&
+ (mode->hdisplay > 2 * caps->max_mixer_width ||
+ mode_rate > 2 * dpu_kms->perf.max_core_clk_rate))
+ topology.num_lm = 4;
+ else
+ topology.num_lm = 2;
+ } else {
+ if (dsc)
+ topology.num_lm = min(2, dpu_kms->catalog->dsc_count);
+ else if (caps->has_3d_merge &&
+ (mode->hdisplay > MAX_HDISPLAY_SPLIT ||
+ mode_rate > dpu_kms->perf.max_core_clk_rate))
+ topology.num_lm = 2;
+ else
+ topology.num_lm = 1;
+ }
+
+ if (dsc)
+ topology.num_dsc = topology.num_lm;

if (crtc_state->ctm || crtc_state->gamma_lut)
topology.num_dspp = topology.num_lm;
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
index 6eaba5696e8e..455073c7025b 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
@@ -210,7 +210,7 @@ struct dpu_crtc_state {

bool bw_control;
bool bw_split_vote;
- struct drm_rect lm_bounds[CRTC_DUAL_MIXERS];
+ struct drm_rect lm_bounds[CRTC_QUAD_MIXERS];

uint64_t input_fence_timeout_ns;

@@ -218,10 +218,10 @@ struct dpu_crtc_state {

/* HW Resources reserved for the crtc */
u32 num_mixers;
- struct dpu_crtc_mixer mixers[CRTC_DUAL_MIXERS];
+ struct dpu_crtc_mixer mixers[CRTC_QUAD_MIXERS];

u32 num_ctls;
- struct dpu_hw_ctl *hw_ctls[CRTC_DUAL_MIXERS];
+ struct dpu_hw_ctl *hw_ctls[CRTC_QUAD_MIXERS];

enum dpu_crtc_crc_source crc_source;
int crc_frame_skip_count;
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index 65161efc4294..2c2b98ce1093 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -55,7 +55,7 @@
#define MAX_PHYS_ENCODERS_PER_VIRTUAL \
(MAX_H_TILES_PER_DISPLAY * NUM_PHYS_ENCODER_TYPES)

-#define MAX_CHANNELS_PER_ENC 2
+#define MAX_CHANNELS_PER_ENC 4
#define MAX_CWB_PER_ENC 2

#define IDLE_SHORT_TIMEOUT 1
@@ -659,37 +659,15 @@ void dpu_encoder_update_topology(struct drm_encoder *drm_enc,
const struct drm_display_mode *adj_mode)
{
struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc);
- struct msm_drm_private *priv = dpu_enc->base.dev->dev_private;
struct msm_display_info *disp_info = &dpu_enc->disp_info;
- struct dpu_kms *dpu_kms = to_dpu_kms(priv->kms);
struct drm_connector *connector;
struct drm_connector_state *conn_state;
struct drm_framebuffer *fb;
- struct drm_dsc_config *dsc;

/* a cloned writeback taps the datapath of the real-time encoder */
if (!topology->cwb_enabled || disp_info->intf_type != INTF_WB)
topology->num_intf += dpu_enc->num_phys_encs;

- dsc = dpu_encoder_get_dsc_config(drm_enc);
-
- /* We only support 2 DSC mode (with 2 LM and 1 INTF) */
- if (dsc) {
- /*
- * Use 2 DSC encoders, 2 layer mixers and 1 or 2 interfaces
- * when Display Stream Compression (DSC) is enabled,
- * and when enough DSC blocks are available.
- * This is power-optimal and can drive up to (including) 4k
- * screens.
- */
- WARN(topology->num_intf > 2,
- "DSC topology cannot support more than 2 interfaces\n");
- if (topology->num_intf >= 2 || dpu_kms->catalog->dsc_count >= 2)
- topology->num_dsc = 2;
- else
- topology->num_dsc = 1;
- }
-
connector = drm_atomic_get_new_connector_for_encoder(state, drm_enc);
if (!connector)
return;
@@ -2177,8 +2155,8 @@ static void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys *phys_enc)
{
int i, num_lm;
struct dpu_global_state *global_state;
- struct dpu_hw_blk *hw_lm[2];
- struct dpu_hw_mixer *hw_mixer[2];
+ struct dpu_hw_blk *hw_lm[MAX_CHANNELS_PER_ENC];
+ struct dpu_hw_mixer *hw_mixer[MAX_CHANNELS_PER_ENC];
struct dpu_hw_ctl *ctl = phys_enc->hw_ctl;

/* reset all mixers for this encoder */
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
index 4942097e7613..7dfaa3ed4dff 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h
@@ -82,6 +82,8 @@ int dpu_encoder_get_crc(const struct drm_encoder *drm_enc, u32 *crcs, int pos);

bool dpu_encoder_use_dsc_merge(struct drm_encoder *drm_enc);

+struct drm_dsc_config *dpu_encoder_get_dsc_config(struct drm_encoder *drm_enc);
+
void dpu_encoder_update_topology(struct drm_encoder *drm_enc,
struct msm_display_topology *topology,
struct drm_atomic_commit *state,
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
index d24e39597f2f..0bfbce317575 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
@@ -314,8 +314,6 @@ unsigned int dpu_encoder_helper_get_cwb_mask(struct dpu_encoder_phys *phys_enc);

unsigned int dpu_encoder_helper_get_dsc(struct dpu_encoder_phys *phys_enc);

-struct drm_dsc_config *dpu_encoder_get_dsc_config(struct drm_encoder *drm_enc);
-
u32 dpu_encoder_get_drm_fmt(struct dpu_encoder_phys *phys_enc);

bool dpu_encoder_needs_periph_flush(struct dpu_encoder_phys *phys_enc);
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
index f45faf87333e..ba1554095a33 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
@@ -24,7 +24,7 @@
#define DPU_MAX_IMG_WIDTH 0x3fff
#define DPU_MAX_IMG_HEIGHT 0x3fff

-#define CRTC_DUAL_MIXERS 2
+#define CRTC_QUAD_MIXERS 4

#define MAX_XIN_COUNT 16

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h
index 0e65bf5ddc4a..fd1f3e798206 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h
@@ -34,7 +34,7 @@
#define DPU_MAX_PLANES 4
#endif

-#define STAGES_PER_PLANE 1
+#define STAGES_PER_PLANE 2
#define PIPES_PER_STAGE 2
#define PIPES_PER_PLANE (PIPES_PER_STAGE * STAGES_PER_PLANE)
#ifndef DPU_MAX_DE_CURVES

--
2.47.3