From d13a32bd1a8dadc995c67e4eba2eea0216da23cd Mon Sep 17 00:00:00 2001 From: Mahadevan P Date: Thu, 8 Oct 2026 22:53:47 +0530 Subject: [PATCH 01/10] FROMLIST: drm/msm/dpu: split modes a single layer mixer cannot clock The layer mixer count is picked purely from the mode width, via the MAX_HDISPLAY_SPLIT threshold. Width is only half of what constrains a mixer: it also processes one pixel per core clock cycle, so a mode narrow enough to stay under the width threshold can still demand a higher pixel rate than one mixer sustains. A 1080 wide panel at a few hundred Hz is enough to get there. Such a mode is currently given a single mixer and then has to be clocked past the maximum core clock rate, which that mixer cannot do. Factor the decision out into dpu_crtc_num_lm_for_mode() and have it ask for a second mixer when the adjusted mode clock does not fit the maximum core clock rate, in addition to the existing width test. Modes that already fit within one mixer are unaffected, so the only decisions that change are the ones that could not be driven as they were. Assisted-by: LLM Link: https://lore.kernel.org/all/20261008-lm_fixes_final-v4-1-fa986071c3c1@oss.qualcomm.com/ Signed-off-by: Mahadevan P --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 25 +++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index cbb7caa194c1e..28e0562931f35 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1369,6 +1369,25 @@ static int dpu_crtc_reassign_planes(struct drm_crtc *crtc, struct drm_crtc_state #define MAX_CHANNELS_PER_CRTC PIPES_PER_PLANE #define MAX_HDISPLAY_SPLIT 1080 +static u32 dpu_crtc_num_lm_for_mode(struct dpu_kms *dpu_kms, + const struct drm_display_mode *mode) +{ + u64 mode_clk; + + if (!dpu_kms->catalog->caps->has_3d_merge) + return 1; + + if (mode->hdisplay > MAX_HDISPLAY_SPLIT) + return 2; + + mode_clk = dpu_core_perf_adjusted_mode_clk(mode->clock, + dpu_kms->perf.perf_cfg); + if (mode_clk * 1000 > dpu_kms->perf.max_core_clk_rate) + return 2; + + return 1; +} + static struct msm_display_topology dpu_crtc_get_topology( struct drm_crtc *crtc, struct dpu_kms *dpu_kms, @@ -1402,8 +1421,8 @@ static struct msm_display_topology dpu_crtc_get_topology( * enabled. This is because in cases where CWB is enabled, num_intf will * count both the WB and real-time phys encoders. * - * For non-DSC CWB usecases, have the num_lm be decided by the - * (mode->hdisplay > MAX_HDISPLAY_SPLIT) check. + * For non-DSC CWB usecases, have the num_lm be decided by + * dpu_crtc_num_lm_for_mode(). */ if (topology.num_intf == 2 && !topology.cwb_enabled) @@ -1412,7 +1431,7 @@ static struct msm_display_topology dpu_crtc_get_topology( 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; + topology.num_lm = dpu_crtc_num_lm_for_mode(dpu_kms, mode); else topology.num_lm = 1; From f24426427386b8b1407aa46c86c2f34065dd988a Mon Sep 17 00:00:00 2001 From: Mahadevan P Date: Thu, 8 Oct 2026 22:53:48 +0530 Subject: [PATCH 02/10] FROMLIST: drm/msm/dpu: do not assume a merged datapath when validating mode clock dpu_crtc_mode_valid() halves the adjusted mode clock whenever the hardware has a 3d_mux block, assuming the mode will be driven by two layer mixers. Modes no wider than MAX_HDISPLAY_SPLIT are driven by a single mixer, so for those the check permits twice the pixel rate the datapath can sustain. Divide by the mixer count the mode will really be driven by, as reported by dpu_crtc_num_lm_for_mode(). Split modes still get the halved rate, single mixer modes are held to what one mixer sustains, and parts without a 3d_mux divide by one. Assisted-by: LLM Link: https://lore.kernel.org/all/20261008-lm_fixes_final-v4-2-fa986071c3c1@oss.qualcomm.com/ Signed-off-by: Mahadevan P --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 28e0562931f35..a07f067c3c3f5 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1622,8 +1622,7 @@ static enum drm_mode_status dpu_crtc_mode_valid(struct drm_crtc *crtc, adjusted_mode_clk = dpu_core_perf_adjusted_mode_clk(mode->clock, dpu_kms->perf.perf_cfg); - if (dpu_kms->catalog->caps->has_3d_merge) - adjusted_mode_clk /= 2; + adjusted_mode_clk /= dpu_crtc_num_lm_for_mode(dpu_kms, mode); /* * The given mode, adjusted for the perf clock factor, should not exceed From 54cfa7c7688948ea17078e64ba9ea49cb9320102 Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Thu, 8 Oct 2026 22:53:49 +0530 Subject: [PATCH 03/10] FROMLIST: drm/msm/dpu: check mode against PINGPONG or DSC max width LM block doesn't have a hardware buffer (unlike PINGPONG and DSC encoders). As such, don't use ephemeral max_mixer_width and MAX_HDISPLAY_SPLIT to validate requested modes. Instead use PP and DSC buffer widths. While on the DPU 8.x+ supports a max linewidth of 8960 for PINGPONG_0, there is some additional logic that needs to be added to the resource manager to specifically try and reserve PINGPONG_0 for modes that are greater than 5k. The layer-mixer count for a merge-capable, non-DSC mode is chosen by dpu_crtc_num_lm_for_mode(); feed the PINGPONG/DSC derived width to it as the split threshold in place of the removed MAX_HDISPLAY_SPLIT, so the pixel-rate floor added earlier keeps high-refresh modes that now fit within a single PINGPONG buffer split across two mixers. [MP: rebased on msm-next; fed PINGPONG/DSC width into dpu_crtc_num_lm_for_mode() instead of open-coding the width test] Link: https://lore.kernel.org/all/20261008-lm_fixes_final-v4-3-fa986071c3c1@oss.qualcomm.com/ Signed-off-by: Jessica Zhang Tested-by: Xilin Wu [DB: reworked to drop catalog changes, updated commit message] Signed-off-by: Dmitry Baryshkov Signed-off-by: Mahadevan P --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 54 +++++++++++++++---- .../gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 7 +++ 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index a07f067c3c3f5..44e7fc24dd36f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -762,6 +762,22 @@ void dpu_crtc_complete_commit(struct drm_crtc *crtc) _dpu_crtc_complete_flip(crtc); } +static int msm_display_get_max_pingpong_width(struct dpu_kms *dpu_kms) +{ + /* + * Note: While, for DPU 8.x+, PINGPONG_0 can technically support up to + * 8k resolutions, this requires reworking the RM to try to reserve + * PINGPONG_0 for modes greater than 5k. + * + * Once this additional logic is implemented, we can probably drop this + * helper and use the reserved PINGPONG's max_linewidth + */ + if (dpu_kms->catalog->mdss_ver->core_major_ver < 6) + return DPU_1_x_MAX_PINGPONG_WIDTH; + else + return DPU_6_x_MAX_PINGPONG_WIDTH; +} + static int _dpu_crtc_check_and_setup_lm_bounds(struct drm_crtc *crtc, struct drm_crtc_state *state) { @@ -769,13 +785,14 @@ static int _dpu_crtc_check_and_setup_lm_bounds(struct drm_crtc *crtc, struct drm_display_mode *adj_mode = &state->adjusted_mode; u32 crtc_split_width = adj_mode->hdisplay / cstate->num_mixers; struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc); + int max_pingpong_width = msm_display_get_max_pingpong_width(dpu_kms); int i; /* if we cannot merge 2 LMs (no 3d mux) better to fail earlier * before even checking the width after the split */ if (!dpu_kms->catalog->caps->has_3d_merge && - adj_mode->hdisplay > dpu_kms->catalog->caps->max_mixer_width) + adj_mode->hdisplay > max_pingpong_width) return -E2BIG; for (i = 0; i < cstate->num_mixers; i++) { @@ -787,7 +804,7 @@ static int _dpu_crtc_check_and_setup_lm_bounds(struct drm_crtc *crtc, trace_dpu_crtc_setup_lm_bounds(DRMID(crtc), i, r); - if (drm_rect_width(r) > dpu_kms->catalog->caps->max_mixer_width) + if (drm_rect_width(r) > max_pingpong_width) return -E2BIG; } @@ -1367,17 +1384,17 @@ static int dpu_crtc_reassign_planes(struct drm_crtc *crtc, struct drm_crtc_state } #define MAX_CHANNELS_PER_CRTC PIPES_PER_PLANE -#define MAX_HDISPLAY_SPLIT 1080 static u32 dpu_crtc_num_lm_for_mode(struct dpu_kms *dpu_kms, - const struct drm_display_mode *mode) + const struct drm_display_mode *mode, + u32 max_hdisplay_split) { u64 mode_clk; if (!dpu_kms->catalog->caps->has_3d_merge) return 1; - if (mode->hdisplay > MAX_HDISPLAY_SPLIT) + if (mode->hdisplay > max_hdisplay_split) return 2; mode_clk = dpu_core_perf_adjusted_mode_clk(mode->clock, @@ -1396,6 +1413,7 @@ static struct msm_display_topology dpu_crtc_get_topology( struct drm_display_mode *mode = &crtc_state->adjusted_mode; struct msm_display_topology topology = {0}; struct drm_encoder *drm_enc; + u32 max_hdisplay_split; drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask) dpu_encoder_update_topology(drm_enc, &topology, crtc_state->state, @@ -1403,6 +1421,19 @@ static struct msm_display_topology dpu_crtc_get_topology( topology.cwb_enabled = drm_crtc_in_clone_mode(crtc_state); + max_hdisplay_split = msm_display_get_max_pingpong_width(dpu_kms); + + if (topology.num_dsc > 0) { + u32 max_dsc_encoder_width; + + if (dpu_kms->catalog->mdss_ver->core_major_ver < 6) + max_dsc_encoder_width = DPU_1_x_MAX_DSC_ENCODER_WIDTH; + else + max_dsc_encoder_width = DPU_8_x_MAX_DSC_ENCODER_WIDTH; + + max_hdisplay_split = min(max_hdisplay_split, max_dsc_encoder_width); + } + /* * Datapath topology selection * @@ -1431,7 +1462,8 @@ static struct msm_display_topology dpu_crtc_get_topology( topology.num_lm = 2; else if (dpu_kms->catalog->caps->has_3d_merge && topology.num_dsc == 0) - topology.num_lm = dpu_crtc_num_lm_for_mode(dpu_kms, mode); + topology.num_lm = dpu_crtc_num_lm_for_mode(dpu_kms, mode, + max_hdisplay_split); else topology.num_lm = 1; @@ -1610,19 +1642,21 @@ static enum drm_mode_status dpu_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode) { struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc); + int max_pingpong_width = msm_display_get_max_pingpong_width(dpu_kms); u64 adjusted_mode_clk; /* if there is no 3d_mux block we cannot merge LMs so we cannot * split the large layer into 2 LMs, filter out such modes */ if (!dpu_kms->catalog->caps->has_3d_merge && - mode->hdisplay > dpu_kms->catalog->caps->max_mixer_width) + mode->hdisplay > max_pingpong_width) return MODE_BAD_HVALUE; adjusted_mode_clk = dpu_core_perf_adjusted_mode_clk(mode->clock, dpu_kms->perf.perf_cfg); - adjusted_mode_clk /= dpu_crtc_num_lm_for_mode(dpu_kms, mode); + adjusted_mode_clk /= dpu_crtc_num_lm_for_mode(dpu_kms, mode, + max_pingpong_width); /* * The given mode, adjusted for the perf clock factor, should not exceed @@ -1632,10 +1666,10 @@ static enum drm_mode_status dpu_crtc_mode_valid(struct drm_crtc *crtc, return MODE_CLOCK_HIGH; /* - * max crtc width is equal to the max mixer width * 2 and max height is 4K + * max crtc width is equal to the max pingpong width * 2 and max height is 4K */ return drm_mode_validate_size(mode, - 2 * dpu_kms->catalog->caps->max_mixer_width, + 2 * max_pingpong_width, 4096); } 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 70d5ed4732f2e..8bce736cc8c4b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h @@ -24,6 +24,13 @@ #define DPU_MAX_IMG_WIDTH 0x3fff #define DPU_MAX_IMG_HEIGHT 0x3fff +#define DPU_1_x_MAX_PINGPONG_WIDTH 4096 +#define DPU_6_x_MAX_PINGPONG_WIDTH 5120 +#define DPU_8_x_MAX_PINGPONG_0_WIDTH 8960 + +#define DPU_1_x_MAX_DSC_ENCODER_WIDTH 2048 +#define DPU_8_x_MAX_DSC_ENCODER_WIDTH 2560 + #define CRTC_DUAL_MIXERS 2 #define MAX_XIN_COUNT 16 From f474ea8970f388588fc40dfe8367c4a74ba842fa Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Thu, 8 Oct 2026 22:53:50 +0530 Subject: [PATCH 04/10] FROMLIST: drm/msm/dpu: filter writeback modes using writeback maxlinewidth Maximum width of the writeback mode is limited by the hardware buffer in the WB block rather than by the LM properties (LM doesn't have an actual buffer). Use the actual hardware limit (the writeback maxlinewidth) to filter modes. Link: https://lore.kernel.org/all/20261008-lm_fixes_final-v4-4-fa986071c3c1@oss.qualcomm.com/ Signed-off-by: Jessica Zhang [DB: fixed commit message] Signed-off-by: Dmitry Baryshkov Signed-off-by: Mahadevan P --- drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c index 6f2370c9dd988..604ab30c40553 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c @@ -15,14 +15,7 @@ static int dpu_wb_conn_get_modes(struct drm_connector *connector) struct msm_drm_private *priv = dev->dev_private; struct dpu_kms *dpu_kms = to_dpu_kms(priv->kms); - /* - * We should ideally be limiting the modes only to the maxlinewidth but - * on some chipsets this will allow even 4k modes to be added which will - * fail the per SSPP bandwidth checks. So, till we have dual-SSPP support - * and source split support added lets limit the modes based on max_mixer_width - * as 4K modes can then be supported. - */ - return drm_add_modes_noedid(connector, dpu_kms->catalog->caps->max_mixer_width, + return drm_add_modes_noedid(connector, dpu_kms->catalog->wb->maxlinewidth, dev->mode_config.max_height); } From b4d981e447bce597d7851e67bae3fc4ad395b842 Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Thu, 8 Oct 2026 22:53:51 +0530 Subject: [PATCH 05/10] BACKPORT: drm/msm/dpu: remove max_mixer_width from catalog Remove the now-unused max_mixer_width field from the HW catalog. It doesn't represent an actual hardware constraint. [MP: rebased on msm-next; also drop the field from the milos, eliza and kaanapali catalogs added since v3] Link: https://lore.kernel.org/all/20261008-lm_fixes_final-v4-5-fa986071c3c1@oss.qualcomm.com/ Signed-off-by: Jessica Zhang Reviewed-by: Dmitry Baryshkov Signed-off-by: Dmitry Baryshkov [Mahadevan: dropped dpu_10_2_milos.h and dpu_12_4_eliza.h hunks, not present in resolute-qcom-devel] Signed-off-by: Mahadevan P --- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_14_msm8937.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_15_msm8917.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_7_msm8996.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_0_msm8998.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_2_sdm660.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_3_sdm630.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_3_sm6115.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_5_qcm2290.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_9_sm6375.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h | 1 - drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h | 1 - drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 2 -- 32 files changed, 33 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h index bd8139b5d7112..abafd333acaed 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h @@ -8,7 +8,6 @@ #define _DPU_10_0_SM8650_H static const struct dpu_caps sm8650_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0xb, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h index 73c19a942d295..6abbb2de88686 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_0_sm8750.h @@ -9,7 +9,6 @@ #define _DPU_12_0_SM8750_H static const struct dpu_caps sm8750_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0xb, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h index a3b590cca21dc..8d057c4b1ef6c 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h @@ -7,7 +7,6 @@ #define _DPU_12_2_GLYMUR_H static const struct dpu_caps glymur_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0xb, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h index 12a9b8bf04785..132eaadf83251 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h @@ -7,7 +7,6 @@ #define _DPU_13_0_KAANAPALI_H static const struct dpu_caps kaanapali_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0xb, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_14_msm8937.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_14_msm8937.h index 29e0eba91930f..c6923e0093cd6 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_14_msm8937.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_14_msm8937.h @@ -7,7 +7,6 @@ #define _DPU_1_14_MSM8937_H static const struct dpu_caps msm8937_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_LINE_WIDTH, .max_mixer_blendstages = 0x4, .max_linewidth = DEFAULT_DPU_LINE_WIDTH, .pixel_ram_size = 40 * 1024, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_15_msm8917.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_15_msm8917.h index cb1ee4b63f9fe..14d0619c14799 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_15_msm8917.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_15_msm8917.h @@ -7,7 +7,6 @@ #define _DPU_1_14_MSM8917_H static const struct dpu_caps msm8917_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_LINE_WIDTH, .max_mixer_blendstages = 0x4, .max_linewidth = DEFAULT_DPU_LINE_WIDTH, .pixel_ram_size = 16 * 1024, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h index 2162ff917b0f8..66a294086be9a 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h @@ -7,7 +7,6 @@ #define _DPU_1_16_MSM8953_H static const struct dpu_caps msm8953_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_LINE_WIDTH, .max_mixer_blendstages = 0x4, .max_linewidth = DEFAULT_DPU_LINE_WIDTH, .pixel_ram_size = 40 * 1024, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_7_msm8996.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_7_msm8996.h index 8af63db315b45..1885ea92a8080 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_7_msm8996.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_7_msm8996.h @@ -9,7 +9,6 @@ #define _DPU_1_7_MSM8996_H static const struct dpu_caps msm8996_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0x7, .has_src_split = true, .max_linewidth = DEFAULT_DPU_OUTPUT_LINE_WIDTH, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_0_msm8998.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_0_msm8998.h index b1b03d8b30fa0..356549a7eb37c 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_0_msm8998.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_0_msm8998.h @@ -8,7 +8,6 @@ #define _DPU_3_0_MSM8998_H static const struct dpu_caps msm8998_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0x7, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_2_sdm660.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_2_sdm660.h index 64df4e80ea43d..d4ffa30a68faf 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_2_sdm660.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_2_sdm660.h @@ -7,7 +7,6 @@ #define _DPU_3_2_SDM660_H static const struct dpu_caps sdm660_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0x7, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_3_sdm630.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_3_sdm630.h index b409af8999182..cd5e036a49195 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_3_sdm630.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_3_sdm630.h @@ -7,7 +7,6 @@ #define _DPU_3_3_SDM630_H static const struct dpu_caps sdm630_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_LINE_WIDTH, .max_mixer_blendstages = 0x7, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h index 1a201cb7746d3..0a323f4576f38 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h @@ -8,7 +8,6 @@ #define _DPU_4_0_SDM845_H static const struct dpu_caps sdm845_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0xb, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h index adad0114fbb77..9a5f11e33ca89 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h @@ -8,7 +8,6 @@ #define _DPU_5_0_SM8150_H static const struct dpu_caps sm8150_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0xb, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h index b572cfa7ed357..04afc22d9fadc 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h @@ -8,7 +8,6 @@ #define _DPU_5_1_SC8180X_H static const struct dpu_caps sc8180x_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0xb, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h index d81d3c616b3b0..4989cdf7ffce0 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h @@ -8,7 +8,6 @@ #define _DPU_5_2_SM7150_H static const struct dpu_caps sm7150_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0xb, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h index e65f198db9a78..26840213ae844 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h @@ -7,7 +7,6 @@ #define _DPU_5_3_SM6150_H static const struct dpu_caps sm6150_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0x9, .has_dim_layer = true, .has_idle_pc = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h index fbf50f279e662..42d6d1a6cce47 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h @@ -9,7 +9,6 @@ #define _DPU_5_4_SM6125_H static const struct dpu_caps sm6125_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0x6, .has_dim_layer = true, .has_idle_pc = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h index 1562b8363f3fa..cba96a2cbaff4 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h @@ -8,7 +8,6 @@ #define _DPU_6_0_SM8250_H static const struct dpu_caps sm8250_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0xb, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h index c990ba3b5db02..afe83f5e4349a 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h @@ -8,7 +8,6 @@ #define _DPU_6_2_SC7180_H static const struct dpu_caps sc7180_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0x9, .has_dim_layer = true, .has_idle_pc = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_3_sm6115.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_3_sm6115.h index 343ff54823826..90d696707227e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_3_sm6115.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_3_sm6115.h @@ -8,7 +8,6 @@ #define _DPU_6_3_SM6115_H static const struct dpu_caps sm6115_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_LINE_WIDTH, .max_mixer_blendstages = 0x4, .has_dim_layer = true, .has_idle_pc = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h index 093d16bdc450a..d7a94b7c69f10 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h @@ -9,7 +9,6 @@ #define _DPU_6_4_SM6350_H static const struct dpu_caps sm6350_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0x7, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_5_qcm2290.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_5_qcm2290.h index 47053bf9b0a20..fadbfd4f9f950 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_5_qcm2290.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_5_qcm2290.h @@ -8,7 +8,6 @@ #define _DPU_6_5_QCM2290_H static const struct dpu_caps qcm2290_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_LINE_WIDTH, .max_mixer_blendstages = 0x4, .has_dim_layer = true, .has_idle_pc = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_9_sm6375.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_9_sm6375.h index 98190ee7ec7ac..9427ec024d604 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_9_sm6375.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_9_sm6375.h @@ -9,7 +9,6 @@ #define _DPU_6_9_SM6375_H static const struct dpu_caps sm6375_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_LINE_WIDTH, .max_mixer_blendstages = 0x4, .has_dim_layer = true, .has_idle_pc = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h index 1d8a32d109905..99697d8815bc0 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h @@ -8,7 +8,6 @@ #define _DPU_7_0_SM8350_H static const struct dpu_caps sm8350_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0xb, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h index 2f8688224f343..223ba5b0e8b35 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h @@ -8,7 +8,6 @@ #define _DPU_7_2_SC7280_H static const struct dpu_caps sc7280_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0x7, .has_dim_layer = true, .has_idle_pc = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h index c407f01abca79..341bb8127694f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h @@ -8,7 +8,6 @@ #define _DPU_8_0_SC8280XP_H static const struct dpu_caps sc8280xp_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 11, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h index 6a09da5ed6704..f6576c804091a 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h @@ -8,7 +8,6 @@ #define _DPU_8_1_SM8450_H static const struct dpu_caps sm8450_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0xb, .has_src_split = true, .has_dim_layer = true, 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 0b78180eca813..38b25350634e0 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 @@ -7,7 +7,6 @@ #define _DPU_8_4_SA8775P_H static const struct dpu_caps sa8775p_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0xb, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h index 1f06710dafad0..2665bdc005145 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h @@ -8,7 +8,6 @@ #define _DPU_9_0_SM8550_H static const struct dpu_caps sm8550_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0xb, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h index 078f5285c46b7..5c7339d6ef160 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_1_sar2130p.h @@ -8,7 +8,6 @@ #define _DPU_9_1_SAR2130P_H static const struct dpu_caps sar2130p_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0xb, .has_src_split = true, .has_dim_layer = true, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h index 4d2bf5c7ac982..a0ad803e2dc37 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h @@ -7,7 +7,6 @@ #define _DPU_9_2_X1E80100_H static const struct dpu_caps x1e80100_dpu_caps = { - .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, .max_mixer_blendstages = 0xb, .has_src_split = true, .has_dim_layer = true, 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 8bce736cc8c4b..20f39d71c2a60 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h @@ -273,7 +273,6 @@ struct dpu_rotation_cfg { /** * struct dpu_caps - define DPU capabilities - * @max_mixer_width max layer mixer line width support. * @max_mixer_blendstages max layer mixer blend stages or * supported z order * @has_src_split source split feature status @@ -286,7 +285,6 @@ struct dpu_rotation_cfg { * @max_vdeci_exp max vertical decimation supported (max is 2^value) */ struct dpu_caps { - u32 max_mixer_width; u32 max_mixer_blendstages; bool has_src_split; bool has_dim_layer; From 9e944863b0aded7dfd17865e3553e4a800bb35a4 Mon Sep 17 00:00:00 2001 From: Mahadevan P Date: Thu, 8 Oct 2026 22:53:52 +0530 Subject: [PATCH 06/10] FROMLIST: drm/msm/dpu: limit the mode width to a pipe if the LM has no source split Layer mixers without DPU_MIXER_SOURCESPLIT stage one pipe per blend level, so a plane is fetched by a single pipe of at most max_linewidth pixels. max_mixer_width used to reject wider modes. Now that the mode is checked against the PINGPONG width (4096/5120), keep max_linewidth as an extra bound when source split isn't available. Assisted-by: LLM Link: https://lore.kernel.org/all/20261008-lm_fixes_final-v4-6-fa986071c3c1@oss.qualcomm.com/ Signed-off-by: Mahadevan P --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 44e7fc24dd36f..6a777536f247a 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -795,6 +795,10 @@ static int _dpu_crtc_check_and_setup_lm_bounds(struct drm_crtc *crtc, adj_mode->hdisplay > max_pingpong_width) return -E2BIG; + if (!test_bit(DPU_MIXER_SOURCESPLIT, &dpu_kms->catalog->mixer[0].features) && + adj_mode->hdisplay > dpu_kms->catalog->caps->max_linewidth) + return -E2BIG; + for (i = 0; i < cstate->num_mixers; i++) { struct drm_rect *r = &cstate->lm_bounds[i]; r->x1 = crtc_split_width * i; @@ -1652,6 +1656,10 @@ static enum drm_mode_status dpu_crtc_mode_valid(struct drm_crtc *crtc, mode->hdisplay > max_pingpong_width) return MODE_BAD_HVALUE; + if (!test_bit(DPU_MIXER_SOURCESPLIT, &dpu_kms->catalog->mixer[0].features) && + mode->hdisplay > dpu_kms->catalog->caps->max_linewidth) + return MODE_BAD_HVALUE; + adjusted_mode_clk = dpu_core_perf_adjusted_mode_clk(mode->clock, dpu_kms->perf.perf_cfg); From 810013187e64260a5738cf9c64f21af5f2dc6b06 Mon Sep 17 00:00:00 2001 From: Mahadevan P Date: Sat, 10 Oct 2026 19:08:21 +0530 Subject: [PATCH 07/10] drm/msm/dpu: keep SSPP priority order for split planes on DPU < 5.0 Before DPU 5.0, the layer mixer places the higher priority SSPP on the left of a source-split pair. Priority follows the SSPP index (VIG, RGB, DMA), while allocation prefers DMA, RGB, then VIG. Swap the two SSPPs when the right one has the lower index. Both are reserved with the same requirements. Same-SSPP parallel multirect already puts RECT_0, the higher priority rectangle, on the left. Fixes: 8c62a31607f6 ("drm/msm/dpu: allow using two SSPP blocks for a single plane") Assisted-by: LLM Signed-off-by: Mahadevan P --- drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c index 547d084f29442..038ba5d6271fc 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c @@ -1233,6 +1233,10 @@ static int dpu_plane_assign_resource_in_stage(struct dpu_sw_pipe *pipe, r_pipe->multirect_index = DPU_SSPP_RECT_SOLO; r_pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE; + if (dpu_kms->catalog->mdss_ver->core_major_ver < 5 && + r_pipe->sspp->idx < pipe->sspp->idx) + swap(pipe->sspp, r_pipe->sspp); + return 0; } From 942c6d7ca186b81e6755275dc1650a8675992674 Mon Sep 17 00:00:00 2001 From: Mahadevan P Date: Sat, 10 Oct 2026 19:08:22 +0530 Subject: [PATCH 08/10] drm/msm/dpu: add SSPP op to program the source split order DPU 5.0 introduced SRC_SPLIT_ORDER in bit 4 of SSPP_SRC_OP_MODE and SSPP_SRC_OP_MODE_REC1. For source-split pairs using legacy CTL routing, this field identifies the left source with 0 and the right source with 1. Add a setup_src_split_order() operation for the SSPP register layout used on DPU 5.0 through 12.x. Select SSPP_SRC_OP_MODE for SOLO or RECT0 and SSPP_SRC_OP_MODE_REC1 for RECT1, and update only the ordering bit. Assisted-by: LLM Signed-off-by: Mahadevan P --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c | 35 +++++++++++++++++++++ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.h | 11 +++++++ 2 files changed, 46 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c index d3da700092343..60299f63acd57 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c @@ -84,6 +84,7 @@ #define MDSS_MDP_OP_IGC_EN BIT(16) #define MDSS_MDP_OP_FLIP_UD BIT(14) #define MDSS_MDP_OP_FLIP_LR BIT(13) +#define MDSS_MDP_OP_SRC_SPLIT_ORDER BIT(4) #define MDSS_MDP_OP_BWC_EN BIT(0) #define MDSS_MDP_OP_PE_OVERRIDE BIT(31) #define MDSS_MDP_OP_BWC_LOSSLESS (0 << 1) @@ -260,6 +261,38 @@ static void dpu_hw_sspp_setup_format(struct dpu_sw_pipe *pipe, ubwc_ctrl_off, ubwc_error_off); } +void dpu_hw_setup_src_split_order_impl(struct dpu_hw_sspp *ctx, + u32 op_mode_off, bool right) +{ + u32 opmode; + + opmode = DPU_REG_READ(&ctx->hw, op_mode_off); + if (right) + opmode |= MDSS_MDP_OP_SRC_SPLIT_ORDER; + else + opmode &= ~MDSS_MDP_OP_SRC_SPLIT_ORDER; + + DPU_REG_WRITE(&ctx->hw, op_mode_off, opmode); +} + +static void dpu_hw_sspp_setup_src_split_order(struct dpu_sw_pipe *pipe, + bool right) +{ + struct dpu_hw_sspp *ctx = pipe->sspp; + u32 op_mode_off; + + if (!ctx) + return; + + if (pipe->multirect_index == DPU_SSPP_RECT_SOLO || + pipe->multirect_index == DPU_SSPP_RECT_0) + op_mode_off = SSPP_SRC_OP_MODE; + else + op_mode_off = SSPP_SRC_OP_MODE_REC1; + + dpu_hw_setup_src_split_order_impl(ctx, op_mode_off, right); +} + void dpu_hw_setup_format_impl(struct dpu_sw_pipe *pipe, const struct msm_format *fmt, u32 flags, struct dpu_hw_sspp *ctx, u32 op_mode_off, u32 unpack_pat_off, u32 format_off, u32 ubwc_ctrl_off, @@ -635,6 +668,8 @@ static void _setup_layer_ops(struct dpu_hw_sspp *c, c->ops.setup_sourceaddress = dpu_hw_sspp_setup_sourceaddress; c->ops.setup_solidfill = dpu_hw_sspp_setup_solidfill; c->ops.setup_pe = dpu_hw_sspp_setup_pe_config; + if (mdss_rev->core_major_ver >= 5) + c->ops.setup_src_split_order = dpu_hw_sspp_setup_src_split_order; if (test_bit(DPU_SSPP_QOS, &features)) { c->ops.setup_qos_lut = dpu_hw_sspp_setup_qos_lut; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.h index 69d68cc916b2e..9b9ea2e00a00d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.h @@ -236,6 +236,14 @@ struct dpu_hw_sspp_ops { void (*setup_multirect)(struct dpu_sw_pipe *pipe); + /** + * @setup_src_split_order: program the position of the pipe within + * a source split pair + * @pipe: Pointer to pipe context + * @right: true if the pipe fetches the right half of the pair + */ + void (*setup_src_split_order)(struct dpu_sw_pipe *pipe, bool right); + /** * @setup_sharpening: setup sharpening * @ctx: Pointer to pipe context @@ -349,6 +357,9 @@ void dpu_hw_setup_multirect_impl(struct dpu_sw_pipe *pipe, struct dpu_hw_sspp *ctx, u32 op_mode_off); +void dpu_hw_setup_src_split_order_impl(struct dpu_hw_sspp *ctx, + u32 op_mode_off, bool right); + void dpu_hw_setup_format_impl(struct dpu_sw_pipe *pipe, const struct msm_format *fmt, u32 flags, struct dpu_hw_sspp *ctx, u32 op_mode_off, u32 unpack_pat_off, u32 format_off, From 7252441eed4e0740776af77515984d2d59ee2260 Mon Sep 17 00:00:00 2001 From: Mahadevan P Date: Sat, 10 Oct 2026 19:08:23 +0530 Subject: [PATCH 09/10] drm/msm/dpu: program the source split order for split planes Virtual planes can use two SSPPs when a plane exceeds the single-pipe width or clock limit and parallel multirect cannot be used. Program SRC_SPLIT_ORDER during mixer setup from the pipes' destination X positions. Set the bit for the right source and clear it for the left source or a source without a sibling. Fixes: 8c62a31607f6 ("drm/msm/dpu: allow using two SSPP blocks for a single plane") Assisted-by: LLM Signed-off-by: Mahadevan P --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 6a777536f247a..016de895be3ca 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -438,6 +438,18 @@ static void _dpu_crtc_blend_setup_pipe(struct drm_crtc *crtc, mixer[lm_idx].lm_ctl->ops.update_pending_flush_sspp(mixer[lm_idx].lm_ctl, sspp_idx); } +static bool _dpu_crtc_pipe_is_right(const struct dpu_plane_state *pstate, + unsigned int pipe_idx) +{ + unsigned int other_idx = pipe_idx ^ 1; + + if (!pstate->pipe[other_idx].sspp) + return false; + + return pstate->pipe_cfg[pipe_idx].dst_rect.x1 > + pstate->pipe_cfg[other_idx].dst_rect.x1; +} + static void _dpu_crtc_blend_setup_mixer(struct drm_crtc *crtc, struct dpu_crtc *dpu_crtc, struct dpu_crtc_mixer *mixer, struct dpu_hw_stage_cfg *stage_cfg) @@ -447,10 +459,13 @@ static void _dpu_crtc_blend_setup_mixer(struct drm_crtc *crtc, struct drm_plane_state *state; struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state); struct dpu_plane_state *pstate = NULL; + struct dpu_sw_pipe *pipe; const struct msm_format *format; struct dpu_hw_ctl *ctl = mixer->lm_ctl; u32 lm_idx, stage, i, pipe_idx, head_pipe_in_stage, lms_in_pair; bool bg_alpha_enable = false; + bool src_split = test_bit(DPU_MIXER_SOURCESPLIT, + &mixer->hw_lm->cap->features); DECLARE_BITMAP(active_fetch, SSPP_MAX); DECLARE_BITMAP(active_pipes, SSPP_MAX); @@ -479,6 +494,10 @@ static void _dpu_crtc_blend_setup_mixer(struct drm_crtc *crtc, pipe_idx = i + head_pipe_in_stage; if (!pstate->pipe[pipe_idx].sspp) continue; + pipe = &pstate->pipe[pipe_idx]; + if (src_split && pipe->sspp->ops.setup_src_split_order) + pipe->sspp->ops.setup_src_split_order(pipe, + _dpu_crtc_pipe_is_right(pstate, pipe_idx)); lms_in_pair = min(cstate->num_mixers - (stage * PIPES_PER_STAGE), PIPES_PER_STAGE); set_bit(pstate->pipe[pipe_idx].sspp->idx, active_fetch); From 89dd9d24628078a01e983539e094ed4a52c8c03e Mon Sep 17 00:00:00 2001 From: Mahadevan P Date: Sat, 10 Oct 2026 19:08:24 +0530 Subject: [PATCH 10/10] drm/msm/dpu: add source split order op for DPU 13.x DPU 13.x places the SSPP op-mode register in separate REC0 and REC1 banks. Add the source split order operation for this layout, using the shared helper to update SRC_SPLIT_ORDER in the selected bank. Assisted-by: LLM Signed-off-by: Mahadevan P --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp_v13.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp_v13.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp_v13.c index f8f96ad971d78..bdf1201d81344 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp_v13.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp_v13.c @@ -149,6 +149,19 @@ static void dpu_hw_sspp_setup_format_v13(struct dpu_sw_pipe *pipe, unpack_pat_off, format_off, ubwc_ctrl_off, ubwc_err_off); } +static void dpu_hw_sspp_setup_src_split_order_v13(struct dpu_sw_pipe *pipe, + bool right) +{ + struct dpu_hw_sspp *ctx = pipe->sspp; + u32 offset; + + if (!ctx) + return; + + offset = dpu_hw_sspp_calculate_rect_off(pipe->multirect_index, ctx); + dpu_hw_setup_src_split_order_impl(ctx, offset + SSPP_REC_SRC_OP_MODE, right); +} + static void dpu_hw_sspp_setup_pe_config_v13(struct dpu_hw_sspp *ctx, struct dpu_hw_pixel_ext *pe_ext) { @@ -299,6 +312,7 @@ void dpu_hw_sspp_init_v13(struct dpu_hw_sspp *c, c->ops.setup_sourceaddress = dpu_hw_sspp_setup_sourceaddress_v13; c->ops.setup_solidfill = dpu_hw_sspp_setup_solidfill_v13; c->ops.setup_pe = dpu_hw_sspp_setup_pe_config_v13; + c->ops.setup_src_split_order = dpu_hw_sspp_setup_src_split_order_v13; if (test_bit(DPU_SSPP_QOS, &features)) { c->ops.setup_qos_lut = dpu_hw_sspp_setup_qos_lut_v13;