[PATCH v16 09/10] drm/msm/dpu: support plane splitting in quad-pipe case

Jun Nie posted 10 patches 4 months, 3 weeks ago
There is a newer version of this series
[PATCH v16 09/10] drm/msm/dpu: support plane splitting in quad-pipe case
Posted by Jun Nie 4 months, 3 weeks ago
The content of every half of screen is sent out via one interface in
dual-DSI case. The content for every interface is blended by a LM
pair in quad-pipe case, thus a LM pair should not blend any content
that cross the half of screen in this case. Clip plane into pipes per
left and right half screen ROI if topology is quad pipe case.

The clipped rectangle on every half of screen is futher handled by two
pipes if its width exceeds a limit for a single pipe.

Signed-off-by: Jun Nie <jun.nie@linaro.org>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Reviewed-by: Jessica Zhang <jessica.zhang@oss.qualcomm.com>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c  |  11 +++
 drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h  |   2 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 137 +++++++++++++++++++++---------
 3 files changed, 110 insertions(+), 40 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
index d825eb8e40ae8bd456ede6269951339e3053d0d3..e925d93b38feac0594d735fdc2c5b9fd5ae83e6a 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
@@ -1604,6 +1604,17 @@ int dpu_crtc_vblank(struct drm_crtc *crtc, bool en)
 	return 0;
 }
 
+/**
+ * dpu_crtc_get_num_lm - Get mixer number in this CRTC pipeline
+ * @state: Pointer to drm crtc state object
+ */
+unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state)
+{
+	struct dpu_crtc_state *cstate = to_dpu_crtc_state(state);
+
+	return cstate->num_mixers;
+}
+
 #ifdef CONFIG_DEBUG_FS
 static int _dpu_debugfs_status_show(struct seq_file *s, void *data)
 {
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
index 94392b9b924546f96e738ae20920cf9afd568e6b..6eaba5696e8e6bd1246a9895c4c8714ca6589b10 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
@@ -267,4 +267,6 @@ static inline enum dpu_crtc_client_type dpu_crtc_get_client_type(
 
 void dpu_crtc_frame_event_cb(struct drm_crtc *crtc, u32 event);
 
+unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state);
+
 #endif /* _DPU_CRTC_H_ */
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
index 5ae58352cbee1251a0140879f04fc7c304cae674..89a5feb6308bcac537562c3dc4e61c16c92e460c 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
@@ -824,8 +824,12 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
 	struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state);
 	struct dpu_sw_pipe_cfg *pipe_cfg;
 	struct dpu_sw_pipe_cfg *r_pipe_cfg;
+	struct dpu_sw_pipe_cfg init_pipe_cfg;
 	struct drm_rect fb_rect = { 0 };
+	const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
 	uint32_t max_linewidth;
+	u32 num_lm;
+	int stage_id, num_stages;
 
 	min_scale = FRAC_16_16(1, MAX_UPSCALE_RATIO);
 	max_scale = MAX_DOWNSCALE_RATIO << 16;
@@ -848,13 +852,10 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
 		return -EINVAL;
 	}
 
-	/* move the assignment here, to ease handling to another pairs later */
-	pipe_cfg = &pstate->pipe_cfg[0];
-	r_pipe_cfg = &pstate->pipe_cfg[1];
-	/* state->src is 16.16, src_rect is not */
-	drm_rect_fp_to_int(&pipe_cfg->src_rect, &new_plane_state->src);
+	num_lm = dpu_crtc_get_num_lm(crtc_state);
 
-	pipe_cfg->dst_rect = new_plane_state->dst;
+	/* state->src is 16.16, src_rect is not */
+	drm_rect_fp_to_int(&init_pipe_cfg.src_rect, &new_plane_state->src);
 
 	fb_rect.x2 = new_plane_state->fb->width;
 	fb_rect.y2 = new_plane_state->fb->height;
@@ -879,35 +880,94 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
 
 	max_linewidth = pdpu->catalog->caps->max_linewidth;
 
-	drm_rect_rotate(&pipe_cfg->src_rect,
+	drm_rect_rotate(&init_pipe_cfg.src_rect,
 			new_plane_state->fb->width, new_plane_state->fb->height,
 			new_plane_state->rotation);
 
-	if ((drm_rect_width(&pipe_cfg->src_rect) > max_linewidth) ||
-	     _dpu_plane_calc_clk(&crtc_state->adjusted_mode, pipe_cfg) > max_mdp_clk_rate) {
-		if (drm_rect_width(&pipe_cfg->src_rect) > 2 * max_linewidth) {
-			DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n",
-					DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth);
-			return -E2BIG;
+	/*
+	 * We have 1 mixer pair cfg for 1:1:1 and 2:2:1 topology, 2 mixer pair
+	 * configs for left and right half screen in case of 4:4:2 topology.
+	 * But we may have 2 rect to split wide plane that exceeds limit with 1
+	 * config for 2:2:1. So need to handle both wide plane splitting, and
+	 * two halves of screen splitting for quad-pipe case. Check dest
+	 * rectangle left/right clipping first, then check wide rectangle
+	 * splitting in every half next.
+	 */
+	num_stages = (num_lm + 1) / 2;
+	/* iterate mixer configs for this plane, to separate left/right with the id */
+	for (stage_id = 0; stage_id < num_stages; stage_id++) {
+		struct drm_rect mixer_rect = {
+			.x1 = stage_id * mode->hdisplay / num_stages,
+			.y1 = 0,
+			.x2 = (stage_id + 1) * mode->hdisplay / num_stages,
+			.y2 = mode->vdisplay
+			};
+		int cfg_idx = stage_id * PIPES_PER_STAGE;
+
+		pipe_cfg = &pstate->pipe_cfg[cfg_idx];
+		r_pipe_cfg = &pstate->pipe_cfg[cfg_idx + 1];
+
+		drm_rect_fp_to_int(&pipe_cfg->src_rect, &new_plane_state->src);
+		pipe_cfg->dst_rect = new_plane_state->dst;
+
+		DPU_DEBUG_PLANE(pdpu, "checking src " DRM_RECT_FMT
+				" vs clip window " DRM_RECT_FMT "\n",
+				DRM_RECT_ARG(&pipe_cfg->src_rect),
+				DRM_RECT_ARG(&mixer_rect));
+
+		/*
+		 * If this plane does not fall into mixer rect, check next
+		 * mixer rect.
+		 */
+		if (!drm_rect_clip_scaled(&pipe_cfg->src_rect,
+					  &pipe_cfg->dst_rect,
+					  &mixer_rect)) {
+			memset(pipe_cfg, 0, 2 * sizeof(struct dpu_sw_pipe_cfg));
+
+			continue;
 		}
 
-		*r_pipe_cfg = *pipe_cfg;
-		pipe_cfg->src_rect.x2 = (pipe_cfg->src_rect.x1 + pipe_cfg->src_rect.x2) >> 1;
-		pipe_cfg->dst_rect.x2 = (pipe_cfg->dst_rect.x1 + pipe_cfg->dst_rect.x2) >> 1;
-		r_pipe_cfg->src_rect.x1 = pipe_cfg->src_rect.x2;
-		r_pipe_cfg->dst_rect.x1 = pipe_cfg->dst_rect.x2;
-	} else {
-		memset(r_pipe_cfg, 0, sizeof(*r_pipe_cfg));
-	}
+		pipe_cfg->dst_rect.x1 -= mixer_rect.x1;
+		pipe_cfg->dst_rect.x2 -= mixer_rect.x1;
+
+		DPU_DEBUG_PLANE(pdpu, "Got clip src:" DRM_RECT_FMT " dst: " DRM_RECT_FMT "\n",
+				DRM_RECT_ARG(&pipe_cfg->src_rect), DRM_RECT_ARG(&pipe_cfg->dst_rect));
+
+		/* Split wide rect into 2 rect */
+		if ((drm_rect_width(&pipe_cfg->src_rect) > max_linewidth) ||
+		     _dpu_plane_calc_clk(mode, pipe_cfg) > max_mdp_clk_rate) {
+
+			if (drm_rect_width(&pipe_cfg->src_rect) > 2 * max_linewidth) {
+				DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n",
+						DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth);
+				return -E2BIG;
+			}
+
+			memcpy(r_pipe_cfg, pipe_cfg, sizeof(struct dpu_sw_pipe_cfg));
+			pipe_cfg->src_rect.x2 = (pipe_cfg->src_rect.x1 + pipe_cfg->src_rect.x2) >> 1;
+			pipe_cfg->dst_rect.x2 = (pipe_cfg->dst_rect.x1 + pipe_cfg->dst_rect.x2) >> 1;
+			r_pipe_cfg->src_rect.x1 = pipe_cfg->src_rect.x2;
+			r_pipe_cfg->dst_rect.x1 = pipe_cfg->dst_rect.x2;
+			DPU_DEBUG_PLANE(pdpu, "Split wide plane into:"
+					DRM_RECT_FMT " and " DRM_RECT_FMT "\n",
+					DRM_RECT_ARG(&pipe_cfg->src_rect),
+					DRM_RECT_ARG(&r_pipe_cfg->src_rect));
+		} else {
+			memset(r_pipe_cfg, 0, sizeof(struct dpu_sw_pipe_cfg));
+		}
 
-	drm_rect_rotate_inv(&pipe_cfg->src_rect,
-			    new_plane_state->fb->width, new_plane_state->fb->height,
-			    new_plane_state->rotation);
-	if (drm_rect_width(&r_pipe_cfg->src_rect) != 0)
-		drm_rect_rotate_inv(&r_pipe_cfg->src_rect,
-				    new_plane_state->fb->width, new_plane_state->fb->height,
+		drm_rect_rotate_inv(&pipe_cfg->src_rect,
+				    new_plane_state->fb->width,
+				    new_plane_state->fb->height,
 				    new_plane_state->rotation);
 
+		if (drm_rect_width(&r_pipe_cfg->src_rect) != 0)
+			drm_rect_rotate_inv(&r_pipe_cfg->src_rect,
+					    new_plane_state->fb->width,
+					    new_plane_state->fb->height,
+					    new_plane_state->rotation);
+	}
+
 	pstate->needs_qos_remap = drm_atomic_crtc_needs_modeset(crtc_state);
 
 	return 0;
@@ -983,20 +1043,17 @@ static int dpu_plane_atomic_check_sspp(struct drm_plane *plane,
 		drm_atomic_get_new_plane_state(state, plane);
 	struct dpu_plane *pdpu = to_dpu_plane(plane);
 	struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state);
-	struct dpu_sw_pipe *pipe = &pstate->pipe[0];
-	struct dpu_sw_pipe *r_pipe = &pstate->pipe[1];
-	struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg[0];
-	struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->pipe_cfg[1];
-	int ret = 0;
-
-	ret = dpu_plane_atomic_check_pipe(pdpu, pipe, pipe_cfg,
-					  &crtc_state->adjusted_mode,
-					  new_plane_state);
-	if (ret)
-		return ret;
+	struct dpu_sw_pipe *pipe;
+	struct dpu_sw_pipe_cfg *pipe_cfg;
+	int ret = 0, i;
 
-	if (drm_rect_width(&r_pipe_cfg->src_rect) != 0) {
-		ret = dpu_plane_atomic_check_pipe(pdpu, r_pipe, r_pipe_cfg,
+	for (i = 0; i < PIPES_PER_PLANE; i++) {
+		pipe = &pstate->pipe[i];
+		pipe_cfg = &pstate->pipe_cfg[i];
+		if (!drm_rect_width(&pipe_cfg->src_rect))
+			continue;
+		DPU_DEBUG_PLANE(pdpu, "pipe %d is in use, validate it\n", i);
+		ret = dpu_plane_atomic_check_pipe(pdpu, pipe, pipe_cfg,
 						  &crtc_state->adjusted_mode,
 						  new_plane_state);
 		if (ret)

-- 
2.34.1
Re: [PATCH v16 09/10] drm/msm/dpu: support plane splitting in quad-pipe case
Posted by Jun Nie 3 weeks, 5 days ago
Jun Nie <jun.nie@linaro.org> 于2025年9月18日周四 21:30写道:
>
> The content of every half of screen is sent out via one interface in
> dual-DSI case. The content for every interface is blended by a LM
> pair in quad-pipe case, thus a LM pair should not blend any content
> that cross the half of screen in this case. Clip plane into pipes per
> left and right half screen ROI if topology is quad pipe case.
>
> The clipped rectangle on every half of screen is futher handled by two
> pipes if its width exceeds a limit for a single pipe.
>
> Signed-off-by: Jun Nie <jun.nie@linaro.org>
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> Reviewed-by: Jessica Zhang <jessica.zhang@oss.qualcomm.com>
> ---
>  drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c  |  11 +++
>  drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h  |   2 +
>  drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 137 +++++++++++++++++++++---------
>  3 files changed, 110 insertions(+), 40 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> index d825eb8e40ae8bd456ede6269951339e3053d0d3..e925d93b38feac0594d735fdc2c5b9fd5ae83e6a 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> @@ -1604,6 +1604,17 @@ int dpu_crtc_vblank(struct drm_crtc *crtc, bool en)
>         return 0;
>  }
>
> +/**
> + * dpu_crtc_get_num_lm - Get mixer number in this CRTC pipeline
> + * @state: Pointer to drm crtc state object
> + */
> +unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state)
> +{
> +       struct dpu_crtc_state *cstate = to_dpu_crtc_state(state);
> +
> +       return cstate->num_mixers;
> +}
> +
>  #ifdef CONFIG_DEBUG_FS
>  static int _dpu_debugfs_status_show(struct seq_file *s, void *data)
>  {
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> index 94392b9b924546f96e738ae20920cf9afd568e6b..6eaba5696e8e6bd1246a9895c4c8714ca6589b10 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> @@ -267,4 +267,6 @@ static inline enum dpu_crtc_client_type dpu_crtc_get_client_type(
>
>  void dpu_crtc_frame_event_cb(struct drm_crtc *crtc, u32 event);
>
> +unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state);
> +
>  #endif /* _DPU_CRTC_H_ */
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> index 5ae58352cbee1251a0140879f04fc7c304cae674..89a5feb6308bcac537562c3dc4e61c16c92e460c 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> @@ -824,8 +824,12 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
>         struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state);
>         struct dpu_sw_pipe_cfg *pipe_cfg;
>         struct dpu_sw_pipe_cfg *r_pipe_cfg;
> +       struct dpu_sw_pipe_cfg init_pipe_cfg;
>         struct drm_rect fb_rect = { 0 };
> +       const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
>         uint32_t max_linewidth;
> +       u32 num_lm;
> +       int stage_id, num_stages;
>
>         min_scale = FRAC_16_16(1, MAX_UPSCALE_RATIO);
>         max_scale = MAX_DOWNSCALE_RATIO << 16;
> @@ -848,13 +852,10 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
>                 return -EINVAL;
>         }
>
> -       /* move the assignment here, to ease handling to another pairs later */
> -       pipe_cfg = &pstate->pipe_cfg[0];
> -       r_pipe_cfg = &pstate->pipe_cfg[1];
> -       /* state->src is 16.16, src_rect is not */
> -       drm_rect_fp_to_int(&pipe_cfg->src_rect, &new_plane_state->src);
> +       num_lm = dpu_crtc_get_num_lm(crtc_state);
>
> -       pipe_cfg->dst_rect = new_plane_state->dst;
> +       /* state->src is 16.16, src_rect is not */
> +       drm_rect_fp_to_int(&init_pipe_cfg.src_rect, &new_plane_state->src);
>
>         fb_rect.x2 = new_plane_state->fb->width;
>         fb_rect.y2 = new_plane_state->fb->height;
> @@ -879,35 +880,94 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
>
>         max_linewidth = pdpu->catalog->caps->max_linewidth;
>
> -       drm_rect_rotate(&pipe_cfg->src_rect,
> +       drm_rect_rotate(&init_pipe_cfg.src_rect,
>                         new_plane_state->fb->width, new_plane_state->fb->height,
>                         new_plane_state->rotation);
>
> -       if ((drm_rect_width(&pipe_cfg->src_rect) > max_linewidth) ||
> -            _dpu_plane_calc_clk(&crtc_state->adjusted_mode, pipe_cfg) > max_mdp_clk_rate) {
> -               if (drm_rect_width(&pipe_cfg->src_rect) > 2 * max_linewidth) {
> -                       DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n",
> -                                       DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth);
> -                       return -E2BIG;
> +       /*
> +        * We have 1 mixer pair cfg for 1:1:1 and 2:2:1 topology, 2 mixer pair
> +        * configs for left and right half screen in case of 4:4:2 topology.
> +        * But we may have 2 rect to split wide plane that exceeds limit with 1
> +        * config for 2:2:1. So need to handle both wide plane splitting, and
> +        * two halves of screen splitting for quad-pipe case. Check dest
> +        * rectangle left/right clipping first, then check wide rectangle
> +        * splitting in every half next.
> +        */
> +       num_stages = (num_lm + 1) / 2;

Hi Dmitry,
Because the plane is checked before crtc is checked in the drm framework. While
the topology is decided in crtc check. Thus num_lm is 0 when this function is
called for the first time. As a result, the below iteration is not run
at all and leads
 to iommu warning.
Do you suggest to change drm framework with adding extra crtc check before
plane check, or you prefer the below line here?

num_stages = max(1, (num_lm + 1) / 2);

Jun

> +       /* iterate mixer configs for this plane, to separate left/right with the id */
> +       for (stage_id = 0; stage_id < num_stages; stage_id++) {
> +               struct drm_rect mixer_rect = {
> +                       .x1 = stage_id * mode->hdisplay / num_stages,
> +                       .y1 = 0,
> +                       .x2 = (stage_id + 1) * mode->hdisplay / num_stages,
> +                       .y2 = mode->vdisplay
> +                       };
> +               int cfg_idx = stage_id * PIPES_PER_STAGE;
> +
> +               pipe_cfg = &pstate->pipe_cfg[cfg_idx];
> +               r_pipe_cfg = &pstate->pipe_cfg[cfg_idx + 1];
> +
> +               drm_rect_fp_to_int(&pipe_cfg->src_rect, &new_plane_state->src);
> +               pipe_cfg->dst_rect = new_plane_state->dst;
> +
> +               DPU_DEBUG_PLANE(pdpu, "checking src " DRM_RECT_FMT
> +                               " vs clip window " DRM_RECT_FMT "\n",
> +                               DRM_RECT_ARG(&pipe_cfg->src_rect),
> +                               DRM_RECT_ARG(&mixer_rect));
> +
> +               /*
> +                * If this plane does not fall into mixer rect, check next
> +                * mixer rect.
> +                */
> +               if (!drm_rect_clip_scaled(&pipe_cfg->src_rect,
> +                                         &pipe_cfg->dst_rect,
> +                                         &mixer_rect)) {
> +                       memset(pipe_cfg, 0, 2 * sizeof(struct dpu_sw_pipe_cfg));
> +
> +                       continue;
>                 }
>
> -               *r_pipe_cfg = *pipe_cfg;
> -               pipe_cfg->src_rect.x2 = (pipe_cfg->src_rect.x1 + pipe_cfg->src_rect.x2) >> 1;
> -               pipe_cfg->dst_rect.x2 = (pipe_cfg->dst_rect.x1 + pipe_cfg->dst_rect.x2) >> 1;
> -               r_pipe_cfg->src_rect.x1 = pipe_cfg->src_rect.x2;
> -               r_pipe_cfg->dst_rect.x1 = pipe_cfg->dst_rect.x2;
> -       } else {
> -               memset(r_pipe_cfg, 0, sizeof(*r_pipe_cfg));
> -       }
> +               pipe_cfg->dst_rect.x1 -= mixer_rect.x1;
> +               pipe_cfg->dst_rect.x2 -= mixer_rect.x1;
> +
> +               DPU_DEBUG_PLANE(pdpu, "Got clip src:" DRM_RECT_FMT " dst: " DRM_RECT_FMT "\n",
> +                               DRM_RECT_ARG(&pipe_cfg->src_rect), DRM_RECT_ARG(&pipe_cfg->dst_rect));
> +
> +               /* Split wide rect into 2 rect */
> +               if ((drm_rect_width(&pipe_cfg->src_rect) > max_linewidth) ||
> +                    _dpu_plane_calc_clk(mode, pipe_cfg) > max_mdp_clk_rate) {
> +
> +                       if (drm_rect_width(&pipe_cfg->src_rect) > 2 * max_linewidth) {
> +                               DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n",
> +                                               DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth);
> +                               return -E2BIG;
> +                       }
> +
> +                       memcpy(r_pipe_cfg, pipe_cfg, sizeof(struct dpu_sw_pipe_cfg));
> +                       pipe_cfg->src_rect.x2 = (pipe_cfg->src_rect.x1 + pipe_cfg->src_rect.x2) >> 1;
> +                       pipe_cfg->dst_rect.x2 = (pipe_cfg->dst_rect.x1 + pipe_cfg->dst_rect.x2) >> 1;
> +                       r_pipe_cfg->src_rect.x1 = pipe_cfg->src_rect.x2;
> +                       r_pipe_cfg->dst_rect.x1 = pipe_cfg->dst_rect.x2;
> +                       DPU_DEBUG_PLANE(pdpu, "Split wide plane into:"
> +                                       DRM_RECT_FMT " and " DRM_RECT_FMT "\n",
> +                                       DRM_RECT_ARG(&pipe_cfg->src_rect),
> +                                       DRM_RECT_ARG(&r_pipe_cfg->src_rect));
> +               } else {
> +                       memset(r_pipe_cfg, 0, sizeof(struct dpu_sw_pipe_cfg));
> +               }
>
> -       drm_rect_rotate_inv(&pipe_cfg->src_rect,
> -                           new_plane_state->fb->width, new_plane_state->fb->height,
> -                           new_plane_state->rotation);
> -       if (drm_rect_width(&r_pipe_cfg->src_rect) != 0)
> -               drm_rect_rotate_inv(&r_pipe_cfg->src_rect,
> -                                   new_plane_state->fb->width, new_plane_state->fb->height,
> +               drm_rect_rotate_inv(&pipe_cfg->src_rect,
> +                                   new_plane_state->fb->width,
> +                                   new_plane_state->fb->height,
>                                     new_plane_state->rotation);
>
> +               if (drm_rect_width(&r_pipe_cfg->src_rect) != 0)
> +                       drm_rect_rotate_inv(&r_pipe_cfg->src_rect,
> +                                           new_plane_state->fb->width,
> +                                           new_plane_state->fb->height,
> +                                           new_plane_state->rotation);
> +       }
> +
>         pstate->needs_qos_remap = drm_atomic_crtc_needs_modeset(crtc_state);
>
>         return 0;
> @@ -983,20 +1043,17 @@ static int dpu_plane_atomic_check_sspp(struct drm_plane *plane,
>                 drm_atomic_get_new_plane_state(state, plane);
>         struct dpu_plane *pdpu = to_dpu_plane(plane);
>         struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state);
> -       struct dpu_sw_pipe *pipe = &pstate->pipe[0];
> -       struct dpu_sw_pipe *r_pipe = &pstate->pipe[1];
> -       struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg[0];
> -       struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->pipe_cfg[1];
> -       int ret = 0;
> -
> -       ret = dpu_plane_atomic_check_pipe(pdpu, pipe, pipe_cfg,
> -                                         &crtc_state->adjusted_mode,
> -                                         new_plane_state);
> -       if (ret)
> -               return ret;
> +       struct dpu_sw_pipe *pipe;
> +       struct dpu_sw_pipe_cfg *pipe_cfg;
> +       int ret = 0, i;
>
> -       if (drm_rect_width(&r_pipe_cfg->src_rect) != 0) {
> -               ret = dpu_plane_atomic_check_pipe(pdpu, r_pipe, r_pipe_cfg,
> +       for (i = 0; i < PIPES_PER_PLANE; i++) {
> +               pipe = &pstate->pipe[i];
> +               pipe_cfg = &pstate->pipe_cfg[i];
> +               if (!drm_rect_width(&pipe_cfg->src_rect))
> +                       continue;
> +               DPU_DEBUG_PLANE(pdpu, "pipe %d is in use, validate it\n", i);
> +               ret = dpu_plane_atomic_check_pipe(pdpu, pipe, pipe_cfg,
>                                                   &crtc_state->adjusted_mode,
>                                                   new_plane_state);
>                 if (ret)
>
> --
> 2.34.1
>
Re: [PATCH v16 09/10] drm/msm/dpu: support plane splitting in quad-pipe case
Posted by Dmitry Baryshkov 3 weeks, 5 days ago
On Wed, Jan 14, 2026 at 10:48:17PM +0800, Jun Nie wrote:
> Jun Nie <jun.nie@linaro.org> 于2025年9月18日周四 21:30写道:
> >
> > The content of every half of screen is sent out via one interface in
> > dual-DSI case. The content for every interface is blended by a LM
> > pair in quad-pipe case, thus a LM pair should not blend any content
> > that cross the half of screen in this case. Clip plane into pipes per
> > left and right half screen ROI if topology is quad pipe case.
> >
> > The clipped rectangle on every half of screen is futher handled by two
> > pipes if its width exceeds a limit for a single pipe.
> >
> > Signed-off-by: Jun Nie <jun.nie@linaro.org>
> > Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> > Reviewed-by: Jessica Zhang <jessica.zhang@oss.qualcomm.com>
> > ---
> >  drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c  |  11 +++
> >  drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h  |   2 +
> >  drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 137 +++++++++++++++++++++---------
> >  3 files changed, 110 insertions(+), 40 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > index d825eb8e40ae8bd456ede6269951339e3053d0d3..e925d93b38feac0594d735fdc2c5b9fd5ae83e6a 100644
> > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > @@ -1604,6 +1604,17 @@ int dpu_crtc_vblank(struct drm_crtc *crtc, bool en)
> >         return 0;
> >  }
> >
> > +/**
> > + * dpu_crtc_get_num_lm - Get mixer number in this CRTC pipeline
> > + * @state: Pointer to drm crtc state object
> > + */
> > +unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state)
> > +{
> > +       struct dpu_crtc_state *cstate = to_dpu_crtc_state(state);
> > +
> > +       return cstate->num_mixers;
> > +}
> > +
> >  #ifdef CONFIG_DEBUG_FS
> >  static int _dpu_debugfs_status_show(struct seq_file *s, void *data)
> >  {
> > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > index 94392b9b924546f96e738ae20920cf9afd568e6b..6eaba5696e8e6bd1246a9895c4c8714ca6589b10 100644
> > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > @@ -267,4 +267,6 @@ static inline enum dpu_crtc_client_type dpu_crtc_get_client_type(
> >
> >  void dpu_crtc_frame_event_cb(struct drm_crtc *crtc, u32 event);
> >
> > +unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state);
> > +
> >  #endif /* _DPU_CRTC_H_ */
> > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > index 5ae58352cbee1251a0140879f04fc7c304cae674..89a5feb6308bcac537562c3dc4e61c16c92e460c 100644
> > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > @@ -824,8 +824,12 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> >         struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state);
> >         struct dpu_sw_pipe_cfg *pipe_cfg;
> >         struct dpu_sw_pipe_cfg *r_pipe_cfg;
> > +       struct dpu_sw_pipe_cfg init_pipe_cfg;
> >         struct drm_rect fb_rect = { 0 };
> > +       const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> >         uint32_t max_linewidth;
> > +       u32 num_lm;
> > +       int stage_id, num_stages;
> >
> >         min_scale = FRAC_16_16(1, MAX_UPSCALE_RATIO);
> >         max_scale = MAX_DOWNSCALE_RATIO << 16;
> > @@ -848,13 +852,10 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> >                 return -EINVAL;
> >         }
> >
> > -       /* move the assignment here, to ease handling to another pairs later */
> > -       pipe_cfg = &pstate->pipe_cfg[0];
> > -       r_pipe_cfg = &pstate->pipe_cfg[1];
> > -       /* state->src is 16.16, src_rect is not */
> > -       drm_rect_fp_to_int(&pipe_cfg->src_rect, &new_plane_state->src);
> > +       num_lm = dpu_crtc_get_num_lm(crtc_state);
> >
> > -       pipe_cfg->dst_rect = new_plane_state->dst;
> > +       /* state->src is 16.16, src_rect is not */
> > +       drm_rect_fp_to_int(&init_pipe_cfg.src_rect, &new_plane_state->src);
> >
> >         fb_rect.x2 = new_plane_state->fb->width;
> >         fb_rect.y2 = new_plane_state->fb->height;
> > @@ -879,35 +880,94 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> >
> >         max_linewidth = pdpu->catalog->caps->max_linewidth;
> >
> > -       drm_rect_rotate(&pipe_cfg->src_rect,
> > +       drm_rect_rotate(&init_pipe_cfg.src_rect,
> >                         new_plane_state->fb->width, new_plane_state->fb->height,
> >                         new_plane_state->rotation);
> >
> > -       if ((drm_rect_width(&pipe_cfg->src_rect) > max_linewidth) ||
> > -            _dpu_plane_calc_clk(&crtc_state->adjusted_mode, pipe_cfg) > max_mdp_clk_rate) {
> > -               if (drm_rect_width(&pipe_cfg->src_rect) > 2 * max_linewidth) {
> > -                       DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n",
> > -                                       DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth);
> > -                       return -E2BIG;
> > +       /*
> > +        * We have 1 mixer pair cfg for 1:1:1 and 2:2:1 topology, 2 mixer pair
> > +        * configs for left and right half screen in case of 4:4:2 topology.
> > +        * But we may have 2 rect to split wide plane that exceeds limit with 1
> > +        * config for 2:2:1. So need to handle both wide plane splitting, and
> > +        * two halves of screen splitting for quad-pipe case. Check dest
> > +        * rectangle left/right clipping first, then check wide rectangle
> > +        * splitting in every half next.
> > +        */
> > +       num_stages = (num_lm + 1) / 2;
> 
> Hi Dmitry,
> Because the plane is checked before crtc is checked in the drm framework. While
> the topology is decided in crtc check. Thus num_lm is 0 when this function is
> called for the first time. As a result, the below iteration is not run
> at all and leads
>  to iommu warning.

How does it lead to IOMMU warnings?

> Do you suggest to change drm framework with adding extra crtc check before
> plane check, or you prefer the below line here?
> 
> num_stages = max(1, (num_lm + 1) / 2);

DRM framework provides enough hooks to be able to influence the order or
operations without changing the framework. But, I'd like to point out
that for the virtual plane case we already perform plane operations
from dpu_crtc_atomic_check(). You can employ the same approach.


-- 
With best wishes
Dmitry
Re: [PATCH v16 09/10] drm/msm/dpu: support plane splitting in quad-pipe case
Posted by Jun Nie 3 weeks, 4 days ago
Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> 于2026年1月15日周四 00:12写道:
>
> On Wed, Jan 14, 2026 at 10:48:17PM +0800, Jun Nie wrote:
> > Jun Nie <jun.nie@linaro.org> 于2025年9月18日周四 21:30写道:
> > >
> > > The content of every half of screen is sent out via one interface in
> > > dual-DSI case. The content for every interface is blended by a LM
> > > pair in quad-pipe case, thus a LM pair should not blend any content
> > > that cross the half of screen in this case. Clip plane into pipes per
> > > left and right half screen ROI if topology is quad pipe case.
> > >
> > > The clipped rectangle on every half of screen is futher handled by two
> > > pipes if its width exceeds a limit for a single pipe.
> > >
> > > Signed-off-by: Jun Nie <jun.nie@linaro.org>
> > > Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> > > Reviewed-by: Jessica Zhang <jessica.zhang@oss.qualcomm.com>
> > > ---
> > >  drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c  |  11 +++
> > >  drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h  |   2 +
> > >  drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 137 +++++++++++++++++++++---------
> > >  3 files changed, 110 insertions(+), 40 deletions(-)
> > >
> > > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > > index d825eb8e40ae8bd456ede6269951339e3053d0d3..e925d93b38feac0594d735fdc2c5b9fd5ae83e6a 100644
> > > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > > @@ -1604,6 +1604,17 @@ int dpu_crtc_vblank(struct drm_crtc *crtc, bool en)
> > >         return 0;
> > >  }
> > >
> > > +/**
> > > + * dpu_crtc_get_num_lm - Get mixer number in this CRTC pipeline
> > > + * @state: Pointer to drm crtc state object
> > > + */
> > > +unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state)
> > > +{
> > > +       struct dpu_crtc_state *cstate = to_dpu_crtc_state(state);
> > > +
> > > +       return cstate->num_mixers;
> > > +}
> > > +
> > >  #ifdef CONFIG_DEBUG_FS
> > >  static int _dpu_debugfs_status_show(struct seq_file *s, void *data)
> > >  {
> > > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > > index 94392b9b924546f96e738ae20920cf9afd568e6b..6eaba5696e8e6bd1246a9895c4c8714ca6589b10 100644
> > > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > > @@ -267,4 +267,6 @@ static inline enum dpu_crtc_client_type dpu_crtc_get_client_type(
> > >
> > >  void dpu_crtc_frame_event_cb(struct drm_crtc *crtc, u32 event);
> > >
> > > +unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state);
> > > +
> > >  #endif /* _DPU_CRTC_H_ */
> > > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > > index 5ae58352cbee1251a0140879f04fc7c304cae674..89a5feb6308bcac537562c3dc4e61c16c92e460c 100644
> > > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > > @@ -824,8 +824,12 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> > >         struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state);
> > >         struct dpu_sw_pipe_cfg *pipe_cfg;
> > >         struct dpu_sw_pipe_cfg *r_pipe_cfg;
> > > +       struct dpu_sw_pipe_cfg init_pipe_cfg;
> > >         struct drm_rect fb_rect = { 0 };
> > > +       const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> > >         uint32_t max_linewidth;
> > > +       u32 num_lm;
> > > +       int stage_id, num_stages;
> > >
> > >         min_scale = FRAC_16_16(1, MAX_UPSCALE_RATIO);
> > >         max_scale = MAX_DOWNSCALE_RATIO << 16;
> > > @@ -848,13 +852,10 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> > >                 return -EINVAL;
> > >         }
> > >
> > > -       /* move the assignment here, to ease handling to another pairs later */
> > > -       pipe_cfg = &pstate->pipe_cfg[0];
> > > -       r_pipe_cfg = &pstate->pipe_cfg[1];
> > > -       /* state->src is 16.16, src_rect is not */
> > > -       drm_rect_fp_to_int(&pipe_cfg->src_rect, &new_plane_state->src);
> > > +       num_lm = dpu_crtc_get_num_lm(crtc_state);
> > >
> > > -       pipe_cfg->dst_rect = new_plane_state->dst;
> > > +       /* state->src is 16.16, src_rect is not */
> > > +       drm_rect_fp_to_int(&init_pipe_cfg.src_rect, &new_plane_state->src);
> > >
> > >         fb_rect.x2 = new_plane_state->fb->width;
> > >         fb_rect.y2 = new_plane_state->fb->height;
> > > @@ -879,35 +880,94 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> > >
> > >         max_linewidth = pdpu->catalog->caps->max_linewidth;
> > >
> > > -       drm_rect_rotate(&pipe_cfg->src_rect,
> > > +       drm_rect_rotate(&init_pipe_cfg.src_rect,
> > >                         new_plane_state->fb->width, new_plane_state->fb->height,
> > >                         new_plane_state->rotation);
> > >
> > > -       if ((drm_rect_width(&pipe_cfg->src_rect) > max_linewidth) ||
> > > -            _dpu_plane_calc_clk(&crtc_state->adjusted_mode, pipe_cfg) > max_mdp_clk_rate) {
> > > -               if (drm_rect_width(&pipe_cfg->src_rect) > 2 * max_linewidth) {
> > > -                       DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n",
> > > -                                       DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth);
> > > -                       return -E2BIG;
> > > +       /*
> > > +        * We have 1 mixer pair cfg for 1:1:1 and 2:2:1 topology, 2 mixer pair
> > > +        * configs for left and right half screen in case of 4:4:2 topology.
> > > +        * But we may have 2 rect to split wide plane that exceeds limit with 1
> > > +        * config for 2:2:1. So need to handle both wide plane splitting, and
> > > +        * two halves of screen splitting for quad-pipe case. Check dest
> > > +        * rectangle left/right clipping first, then check wide rectangle
> > > +        * splitting in every half next.
> > > +        */
> > > +       num_stages = (num_lm + 1) / 2;
> >
> > Hi Dmitry,
> > Because the plane is checked before crtc is checked in the drm framework. While
> > the topology is decided in crtc check. Thus num_lm is 0 when this function is
> > called for the first time. As a result, the below iteration is not run
> > at all and leads
> >  to iommu warning.
>
> How does it lead to IOMMU warnings?

Because the pipe is not configured with width/height etc when the iteration is
skipped. I have not found the root cause so far. But per the null IOMMU iova
value, suppose it is due to DMA buffer not being prepared when DMA is started.

>
> > Do you suggest to change drm framework with adding extra crtc check before
> > plane check, or you prefer the below line here?
> >
> > num_stages = max(1, (num_lm + 1) / 2);
>
> DRM framework provides enough hooks to be able to influence the order or
> operations without changing the framework. But, I'd like to point out
> that for the virtual plane case we already perform plane operations
> from dpu_crtc_atomic_check(). You can employ the same approach.

Thanks for the suggestion! I see dpu_assign_plane_resources() is called
from crtc side, which avoids the plane splitting before topology decision.
To use this method, it looks like we are enabling the virtual plane by default.
Because the virtual plane differs from the traditional method only with the
plane splitting and resource preparation. Can we just enable the virtual
plane by default in this situation?

Jun

>
>
> --
> With best wishes
> Dmitry
Re: [PATCH v16 09/10] drm/msm/dpu: support plane splitting in quad-pipe case
Posted by Dmitry Baryshkov 3 weeks, 4 days ago
On Thu, Jan 15, 2026 at 05:34:28PM +0800, Jun Nie wrote:
> Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> 于2026年1月15日周四 00:12写道:
> >
> > On Wed, Jan 14, 2026 at 10:48:17PM +0800, Jun Nie wrote:
> > > Jun Nie <jun.nie@linaro.org> 于2025年9月18日周四 21:30写道:
> > > >
> > > > The content of every half of screen is sent out via one interface in
> > > > dual-DSI case. The content for every interface is blended by a LM
> > > > pair in quad-pipe case, thus a LM pair should not blend any content
> > > > that cross the half of screen in this case. Clip plane into pipes per
> > > > left and right half screen ROI if topology is quad pipe case.
> > > >
> > > > The clipped rectangle on every half of screen is futher handled by two
> > > > pipes if its width exceeds a limit for a single pipe.
> > > >
> > > > Signed-off-by: Jun Nie <jun.nie@linaro.org>
> > > > Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> > > > Reviewed-by: Jessica Zhang <jessica.zhang@oss.qualcomm.com>
> > > > ---
> > > >  drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c  |  11 +++
> > > >  drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h  |   2 +
> > > >  drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 137 +++++++++++++++++++++---------
> > > >  3 files changed, 110 insertions(+), 40 deletions(-)
> > > >
> > > > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > > > index d825eb8e40ae8bd456ede6269951339e3053d0d3..e925d93b38feac0594d735fdc2c5b9fd5ae83e6a 100644
> > > > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > > > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > > > @@ -1604,6 +1604,17 @@ int dpu_crtc_vblank(struct drm_crtc *crtc, bool en)
> > > >         return 0;
> > > >  }
> > > >
> > > > +/**
> > > > + * dpu_crtc_get_num_lm - Get mixer number in this CRTC pipeline
> > > > + * @state: Pointer to drm crtc state object
> > > > + */
> > > > +unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state)
> > > > +{
> > > > +       struct dpu_crtc_state *cstate = to_dpu_crtc_state(state);
> > > > +
> > > > +       return cstate->num_mixers;
> > > > +}
> > > > +
> > > >  #ifdef CONFIG_DEBUG_FS
> > > >  static int _dpu_debugfs_status_show(struct seq_file *s, void *data)
> > > >  {
> > > > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > > > index 94392b9b924546f96e738ae20920cf9afd568e6b..6eaba5696e8e6bd1246a9895c4c8714ca6589b10 100644
> > > > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > > > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > > > @@ -267,4 +267,6 @@ static inline enum dpu_crtc_client_type dpu_crtc_get_client_type(
> > > >
> > > >  void dpu_crtc_frame_event_cb(struct drm_crtc *crtc, u32 event);
> > > >
> > > > +unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state);
> > > > +
> > > >  #endif /* _DPU_CRTC_H_ */
> > > > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > > > index 5ae58352cbee1251a0140879f04fc7c304cae674..89a5feb6308bcac537562c3dc4e61c16c92e460c 100644
> > > > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > > > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > > > @@ -824,8 +824,12 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> > > >         struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state);
> > > >         struct dpu_sw_pipe_cfg *pipe_cfg;
> > > >         struct dpu_sw_pipe_cfg *r_pipe_cfg;
> > > > +       struct dpu_sw_pipe_cfg init_pipe_cfg;
> > > >         struct drm_rect fb_rect = { 0 };
> > > > +       const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> > > >         uint32_t max_linewidth;
> > > > +       u32 num_lm;
> > > > +       int stage_id, num_stages;
> > > >
> > > >         min_scale = FRAC_16_16(1, MAX_UPSCALE_RATIO);
> > > >         max_scale = MAX_DOWNSCALE_RATIO << 16;
> > > > @@ -848,13 +852,10 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> > > >                 return -EINVAL;
> > > >         }
> > > >
> > > > -       /* move the assignment here, to ease handling to another pairs later */
> > > > -       pipe_cfg = &pstate->pipe_cfg[0];
> > > > -       r_pipe_cfg = &pstate->pipe_cfg[1];
> > > > -       /* state->src is 16.16, src_rect is not */
> > > > -       drm_rect_fp_to_int(&pipe_cfg->src_rect, &new_plane_state->src);
> > > > +       num_lm = dpu_crtc_get_num_lm(crtc_state);
> > > >
> > > > -       pipe_cfg->dst_rect = new_plane_state->dst;
> > > > +       /* state->src is 16.16, src_rect is not */
> > > > +       drm_rect_fp_to_int(&init_pipe_cfg.src_rect, &new_plane_state->src);
> > > >
> > > >         fb_rect.x2 = new_plane_state->fb->width;
> > > >         fb_rect.y2 = new_plane_state->fb->height;
> > > > @@ -879,35 +880,94 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> > > >
> > > >         max_linewidth = pdpu->catalog->caps->max_linewidth;
> > > >
> > > > -       drm_rect_rotate(&pipe_cfg->src_rect,
> > > > +       drm_rect_rotate(&init_pipe_cfg.src_rect,
> > > >                         new_plane_state->fb->width, new_plane_state->fb->height,
> > > >                         new_plane_state->rotation);
> > > >
> > > > -       if ((drm_rect_width(&pipe_cfg->src_rect) > max_linewidth) ||
> > > > -            _dpu_plane_calc_clk(&crtc_state->adjusted_mode, pipe_cfg) > max_mdp_clk_rate) {
> > > > -               if (drm_rect_width(&pipe_cfg->src_rect) > 2 * max_linewidth) {
> > > > -                       DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n",
> > > > -                                       DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth);
> > > > -                       return -E2BIG;
> > > > +       /*
> > > > +        * We have 1 mixer pair cfg for 1:1:1 and 2:2:1 topology, 2 mixer pair
> > > > +        * configs for left and right half screen in case of 4:4:2 topology.
> > > > +        * But we may have 2 rect to split wide plane that exceeds limit with 1
> > > > +        * config for 2:2:1. So need to handle both wide plane splitting, and
> > > > +        * two halves of screen splitting for quad-pipe case. Check dest
> > > > +        * rectangle left/right clipping first, then check wide rectangle
> > > > +        * splitting in every half next.
> > > > +        */
> > > > +       num_stages = (num_lm + 1) / 2;
> > >
> > > Hi Dmitry,
> > > Because the plane is checked before crtc is checked in the drm framework. While
> > > the topology is decided in crtc check. Thus num_lm is 0 when this function is
> > > called for the first time. As a result, the below iteration is not run
> > > at all and leads
> > >  to iommu warning.
> >
> > How does it lead to IOMMU warnings?
> 
> Because the pipe is not configured with width/height etc when the iteration is
> skipped. I have not found the root cause so far. But per the null IOMMU iova
> value, suppose it is due to DMA buffer not being prepared when DMA is started.

I'd think, that corresponding SRC regs are either garbage or zero programmed.

> 
> >
> > > Do you suggest to change drm framework with adding extra crtc check before
> > > plane check, or you prefer the below line here?
> > >
> > > num_stages = max(1, (num_lm + 1) / 2);
> >
> > DRM framework provides enough hooks to be able to influence the order or
> > operations without changing the framework. But, I'd like to point out
> > that for the virtual plane case we already perform plane operations
> > from dpu_crtc_atomic_check(). You can employ the same approach.
> 
> Thanks for the suggestion! I see dpu_assign_plane_resources() is called
> from crtc side, which avoids the plane splitting before topology decision.
> To use this method, it looks like we are enabling the virtual plane by default.
> Because the virtual plane differs from the traditional method only with the
> plane splitting and resource preparation. Can we just enable the virtual
> plane by default in this situation?

In which situation? It is a global switch. And we need to be able to
work with it turned off, until corresponding code is dropped.

> 
> Jun
> 
> >
> >
> > --
> > With best wishes
> > Dmitry

-- 
With best wishes
Dmitry
Re: [PATCH v16 09/10] drm/msm/dpu: support plane splitting in quad-pipe case
Posted by Jun Nie 3 weeks, 4 days ago
Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> 于2026年1月15日周四 17:39写道:
>
> On Thu, Jan 15, 2026 at 05:34:28PM +0800, Jun Nie wrote:
> > Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> 于2026年1月15日周四 00:12写道:
> > >
> > > On Wed, Jan 14, 2026 at 10:48:17PM +0800, Jun Nie wrote:
> > > > Jun Nie <jun.nie@linaro.org> 于2025年9月18日周四 21:30写道:
> > > > >
> > > > > The content of every half of screen is sent out via one interface in
> > > > > dual-DSI case. The content for every interface is blended by a LM
> > > > > pair in quad-pipe case, thus a LM pair should not blend any content
> > > > > that cross the half of screen in this case. Clip plane into pipes per
> > > > > left and right half screen ROI if topology is quad pipe case.
> > > > >
> > > > > The clipped rectangle on every half of screen is futher handled by two
> > > > > pipes if its width exceeds a limit for a single pipe.
> > > > >
> > > > > Signed-off-by: Jun Nie <jun.nie@linaro.org>
> > > > > Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> > > > > Reviewed-by: Jessica Zhang <jessica.zhang@oss.qualcomm.com>
> > > > > ---
> > > > >  drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c  |  11 +++
> > > > >  drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h  |   2 +
> > > > >  drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 137 +++++++++++++++++++++---------
> > > > >  3 files changed, 110 insertions(+), 40 deletions(-)
> > > > >
> > > > > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > > > > index d825eb8e40ae8bd456ede6269951339e3053d0d3..e925d93b38feac0594d735fdc2c5b9fd5ae83e6a 100644
> > > > > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > > > > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > > > > @@ -1604,6 +1604,17 @@ int dpu_crtc_vblank(struct drm_crtc *crtc, bool en)
> > > > >         return 0;
> > > > >  }
> > > > >
> > > > > +/**
> > > > > + * dpu_crtc_get_num_lm - Get mixer number in this CRTC pipeline
> > > > > + * @state: Pointer to drm crtc state object
> > > > > + */
> > > > > +unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state)
> > > > > +{
> > > > > +       struct dpu_crtc_state *cstate = to_dpu_crtc_state(state);
> > > > > +
> > > > > +       return cstate->num_mixers;
> > > > > +}
> > > > > +
> > > > >  #ifdef CONFIG_DEBUG_FS
> > > > >  static int _dpu_debugfs_status_show(struct seq_file *s, void *data)
> > > > >  {
> > > > > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > > > > index 94392b9b924546f96e738ae20920cf9afd568e6b..6eaba5696e8e6bd1246a9895c4c8714ca6589b10 100644
> > > > > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > > > > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > > > > @@ -267,4 +267,6 @@ static inline enum dpu_crtc_client_type dpu_crtc_get_client_type(
> > > > >
> > > > >  void dpu_crtc_frame_event_cb(struct drm_crtc *crtc, u32 event);
> > > > >
> > > > > +unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state);
> > > > > +
> > > > >  #endif /* _DPU_CRTC_H_ */
> > > > > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > > > > index 5ae58352cbee1251a0140879f04fc7c304cae674..89a5feb6308bcac537562c3dc4e61c16c92e460c 100644
> > > > > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > > > > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > > > > @@ -824,8 +824,12 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> > > > >         struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state);
> > > > >         struct dpu_sw_pipe_cfg *pipe_cfg;
> > > > >         struct dpu_sw_pipe_cfg *r_pipe_cfg;
> > > > > +       struct dpu_sw_pipe_cfg init_pipe_cfg;
> > > > >         struct drm_rect fb_rect = { 0 };
> > > > > +       const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> > > > >         uint32_t max_linewidth;
> > > > > +       u32 num_lm;
> > > > > +       int stage_id, num_stages;
> > > > >
> > > > >         min_scale = FRAC_16_16(1, MAX_UPSCALE_RATIO);
> > > > >         max_scale = MAX_DOWNSCALE_RATIO << 16;
> > > > > @@ -848,13 +852,10 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> > > > >                 return -EINVAL;
> > > > >         }
> > > > >
> > > > > -       /* move the assignment here, to ease handling to another pairs later */
> > > > > -       pipe_cfg = &pstate->pipe_cfg[0];
> > > > > -       r_pipe_cfg = &pstate->pipe_cfg[1];
> > > > > -       /* state->src is 16.16, src_rect is not */
> > > > > -       drm_rect_fp_to_int(&pipe_cfg->src_rect, &new_plane_state->src);
> > > > > +       num_lm = dpu_crtc_get_num_lm(crtc_state);
> > > > >
> > > > > -       pipe_cfg->dst_rect = new_plane_state->dst;
> > > > > +       /* state->src is 16.16, src_rect is not */
> > > > > +       drm_rect_fp_to_int(&init_pipe_cfg.src_rect, &new_plane_state->src);
> > > > >
> > > > >         fb_rect.x2 = new_plane_state->fb->width;
> > > > >         fb_rect.y2 = new_plane_state->fb->height;
> > > > > @@ -879,35 +880,94 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> > > > >
> > > > >         max_linewidth = pdpu->catalog->caps->max_linewidth;
> > > > >
> > > > > -       drm_rect_rotate(&pipe_cfg->src_rect,
> > > > > +       drm_rect_rotate(&init_pipe_cfg.src_rect,
> > > > >                         new_plane_state->fb->width, new_plane_state->fb->height,
> > > > >                         new_plane_state->rotation);
> > > > >
> > > > > -       if ((drm_rect_width(&pipe_cfg->src_rect) > max_linewidth) ||
> > > > > -            _dpu_plane_calc_clk(&crtc_state->adjusted_mode, pipe_cfg) > max_mdp_clk_rate) {
> > > > > -               if (drm_rect_width(&pipe_cfg->src_rect) > 2 * max_linewidth) {
> > > > > -                       DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n",
> > > > > -                                       DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth);
> > > > > -                       return -E2BIG;
> > > > > +       /*
> > > > > +        * We have 1 mixer pair cfg for 1:1:1 and 2:2:1 topology, 2 mixer pair
> > > > > +        * configs for left and right half screen in case of 4:4:2 topology.
> > > > > +        * But we may have 2 rect to split wide plane that exceeds limit with 1
> > > > > +        * config for 2:2:1. So need to handle both wide plane splitting, and
> > > > > +        * two halves of screen splitting for quad-pipe case. Check dest
> > > > > +        * rectangle left/right clipping first, then check wide rectangle
> > > > > +        * splitting in every half next.
> > > > > +        */
> > > > > +       num_stages = (num_lm + 1) / 2;
> > > >
> > > > Hi Dmitry,
> > > > Because the plane is checked before crtc is checked in the drm framework. While
> > > > the topology is decided in crtc check. Thus num_lm is 0 when this function is
> > > > called for the first time. As a result, the below iteration is not run
> > > > at all and leads
> > > >  to iommu warning.
> > >
> > > How does it lead to IOMMU warnings?
> >
> > Because the pipe is not configured with width/height etc when the iteration is
> > skipped. I have not found the root cause so far. But per the null IOMMU iova
> > value, suppose it is due to DMA buffer not being prepared when DMA is started.
>
> I'd think, that corresponding SRC regs are either garbage or zero programmed.

You are right in that. Sorry for my words is not accurate. I mean the
DMA buffer is not
feed to DMA engine correctly.
>
> >
> > >
> > > > Do you suggest to change drm framework with adding extra crtc check before
> > > > plane check, or you prefer the below line here?
> > > >
> > > > num_stages = max(1, (num_lm + 1) / 2);
> > >
> > > DRM framework provides enough hooks to be able to influence the order or
> > > operations without changing the framework. But, I'd like to point out
> > > that for the virtual plane case we already perform plane operations
> > > from dpu_crtc_atomic_check(). You can employ the same approach.
> >
> > Thanks for the suggestion! I see dpu_assign_plane_resources() is called
> > from crtc side, which avoids the plane splitting before topology decision.
> > To use this method, it looks like we are enabling the virtual plane by default.
> > Because the virtual plane differs from the traditional method only with the
> > plane splitting and resource preparation. Can we just enable the virtual
> > plane by default in this situation?
>
> In which situation? It is a global switch. And we need to be able to
> work with it turned off, until corresponding code is dropped.

I mean the situation that the plane SSPP allocation and related resource
preparation shall be deferred until crtc calling the plane API. In this way,
the traditional plane management is almost identical with the virtual
plane method. Or could you point out what shall differ for the two methods
after we deferred the preparation? Thanks!

Jun
>
> >
> > Jun
> >
> > >
> > >
> > > --
> > > With best wishes
> > > Dmitry
>
> --
> With best wishes
> Dmitry
Re: [PATCH v16 09/10] drm/msm/dpu: support plane splitting in quad-pipe case
Posted by Jun Nie 3 weeks, 4 days ago
Jun Nie <jun.nie@linaro.org> 于2026年1月15日周四 17:57写道:
>
> Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> 于2026年1月15日周四 17:39写道:
> >
> > On Thu, Jan 15, 2026 at 05:34:28PM +0800, Jun Nie wrote:
> > > Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> 于2026年1月15日周四 00:12写道:
> > > >
> > > > On Wed, Jan 14, 2026 at 10:48:17PM +0800, Jun Nie wrote:
> > > > > Jun Nie <jun.nie@linaro.org> 于2025年9月18日周四 21:30写道:
> > > > > >
> > > > > > The content of every half of screen is sent out via one interface in
> > > > > > dual-DSI case. The content for every interface is blended by a LM
> > > > > > pair in quad-pipe case, thus a LM pair should not blend any content
> > > > > > that cross the half of screen in this case. Clip plane into pipes per
> > > > > > left and right half screen ROI if topology is quad pipe case.
> > > > > >
> > > > > > The clipped rectangle on every half of screen is futher handled by two
> > > > > > pipes if its width exceeds a limit for a single pipe.
> > > > > >
> > > > > > Signed-off-by: Jun Nie <jun.nie@linaro.org>
> > > > > > Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> > > > > > Reviewed-by: Jessica Zhang <jessica.zhang@oss.qualcomm.com>
> > > > > > ---
> > > > > >  drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c  |  11 +++
> > > > > >  drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h  |   2 +
> > > > > >  drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 137 +++++++++++++++++++++---------
> > > > > >  3 files changed, 110 insertions(+), 40 deletions(-)
> > > > > >
> > > > > > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > > > > > index d825eb8e40ae8bd456ede6269951339e3053d0d3..e925d93b38feac0594d735fdc2c5b9fd5ae83e6a 100644
> > > > > > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > > > > > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > > > > > @@ -1604,6 +1604,17 @@ int dpu_crtc_vblank(struct drm_crtc *crtc, bool en)
> > > > > >         return 0;
> > > > > >  }
> > > > > >
> > > > > > +/**
> > > > > > + * dpu_crtc_get_num_lm - Get mixer number in this CRTC pipeline
> > > > > > + * @state: Pointer to drm crtc state object
> > > > > > + */
> > > > > > +unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state)
> > > > > > +{
> > > > > > +       struct dpu_crtc_state *cstate = to_dpu_crtc_state(state);
> > > > > > +
> > > > > > +       return cstate->num_mixers;
> > > > > > +}
> > > > > > +
> > > > > >  #ifdef CONFIG_DEBUG_FS
> > > > > >  static int _dpu_debugfs_status_show(struct seq_file *s, void *data)
> > > > > >  {
> > > > > > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > > > > > index 94392b9b924546f96e738ae20920cf9afd568e6b..6eaba5696e8e6bd1246a9895c4c8714ca6589b10 100644
> > > > > > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > > > > > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > > > > > @@ -267,4 +267,6 @@ static inline enum dpu_crtc_client_type dpu_crtc_get_client_type(
> > > > > >
> > > > > >  void dpu_crtc_frame_event_cb(struct drm_crtc *crtc, u32 event);
> > > > > >
> > > > > > +unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state);
> > > > > > +
> > > > > >  #endif /* _DPU_CRTC_H_ */
> > > > > > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > > > > > index 5ae58352cbee1251a0140879f04fc7c304cae674..89a5feb6308bcac537562c3dc4e61c16c92e460c 100644
> > > > > > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > > > > > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > > > > > @@ -824,8 +824,12 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> > > > > >         struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state);
> > > > > >         struct dpu_sw_pipe_cfg *pipe_cfg;
> > > > > >         struct dpu_sw_pipe_cfg *r_pipe_cfg;
> > > > > > +       struct dpu_sw_pipe_cfg init_pipe_cfg;
> > > > > >         struct drm_rect fb_rect = { 0 };
> > > > > > +       const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> > > > > >         uint32_t max_linewidth;
> > > > > > +       u32 num_lm;
> > > > > > +       int stage_id, num_stages;
> > > > > >
> > > > > >         min_scale = FRAC_16_16(1, MAX_UPSCALE_RATIO);
> > > > > >         max_scale = MAX_DOWNSCALE_RATIO << 16;
> > > > > > @@ -848,13 +852,10 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> > > > > >                 return -EINVAL;
> > > > > >         }
> > > > > >
> > > > > > -       /* move the assignment here, to ease handling to another pairs later */
> > > > > > -       pipe_cfg = &pstate->pipe_cfg[0];
> > > > > > -       r_pipe_cfg = &pstate->pipe_cfg[1];
> > > > > > -       /* state->src is 16.16, src_rect is not */
> > > > > > -       drm_rect_fp_to_int(&pipe_cfg->src_rect, &new_plane_state->src);
> > > > > > +       num_lm = dpu_crtc_get_num_lm(crtc_state);
> > > > > >
> > > > > > -       pipe_cfg->dst_rect = new_plane_state->dst;
> > > > > > +       /* state->src is 16.16, src_rect is not */
> > > > > > +       drm_rect_fp_to_int(&init_pipe_cfg.src_rect, &new_plane_state->src);
> > > > > >
> > > > > >         fb_rect.x2 = new_plane_state->fb->width;
> > > > > >         fb_rect.y2 = new_plane_state->fb->height;
> > > > > > @@ -879,35 +880,94 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> > > > > >
> > > > > >         max_linewidth = pdpu->catalog->caps->max_linewidth;
> > > > > >
> > > > > > -       drm_rect_rotate(&pipe_cfg->src_rect,
> > > > > > +       drm_rect_rotate(&init_pipe_cfg.src_rect,
> > > > > >                         new_plane_state->fb->width, new_plane_state->fb->height,
> > > > > >                         new_plane_state->rotation);
> > > > > >
> > > > > > -       if ((drm_rect_width(&pipe_cfg->src_rect) > max_linewidth) ||
> > > > > > -            _dpu_plane_calc_clk(&crtc_state->adjusted_mode, pipe_cfg) > max_mdp_clk_rate) {
> > > > > > -               if (drm_rect_width(&pipe_cfg->src_rect) > 2 * max_linewidth) {
> > > > > > -                       DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n",
> > > > > > -                                       DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth);
> > > > > > -                       return -E2BIG;
> > > > > > +       /*
> > > > > > +        * We have 1 mixer pair cfg for 1:1:1 and 2:2:1 topology, 2 mixer pair
> > > > > > +        * configs for left and right half screen in case of 4:4:2 topology.
> > > > > > +        * But we may have 2 rect to split wide plane that exceeds limit with 1
> > > > > > +        * config for 2:2:1. So need to handle both wide plane splitting, and
> > > > > > +        * two halves of screen splitting for quad-pipe case. Check dest
> > > > > > +        * rectangle left/right clipping first, then check wide rectangle
> > > > > > +        * splitting in every half next.
> > > > > > +        */
> > > > > > +       num_stages = (num_lm + 1) / 2;
> > > > >
> > > > > Hi Dmitry,
> > > > > Because the plane is checked before crtc is checked in the drm framework. While
> > > > > the topology is decided in crtc check. Thus num_lm is 0 when this function is
> > > > > called for the first time. As a result, the below iteration is not run
> > > > > at all and leads
> > > > >  to iommu warning.
> > > >
> > > > How does it lead to IOMMU warnings?
> > >
> > > Because the pipe is not configured with width/height etc when the iteration is
> > > skipped. I have not found the root cause so far. But per the null IOMMU iova
> > > value, suppose it is due to DMA buffer not being prepared when DMA is started.
> >
> > I'd think, that corresponding SRC regs are either garbage or zero programmed.
>
> You are right in that. Sorry for my words is not accurate. I mean the
> DMA buffer is not
> feed to DMA engine correctly.
> >
> > >
> > > >
> > > > > Do you suggest to change drm framework with adding extra crtc check before
> > > > > plane check, or you prefer the below line here?
> > > > >
> > > > > num_stages = max(1, (num_lm + 1) / 2);
> > > >
> > > > DRM framework provides enough hooks to be able to influence the order or
> > > > operations without changing the framework. But, I'd like to point out
> > > > that for the virtual plane case we already perform plane operations
> > > > from dpu_crtc_atomic_check(). You can employ the same approach.
> > >
> > > Thanks for the suggestion! I see dpu_assign_plane_resources() is called
> > > from crtc side, which avoids the plane splitting before topology decision.
> > > To use this method, it looks like we are enabling the virtual plane by default.
> > > Because the virtual plane differs from the traditional method only with the
> > > plane splitting and resource preparation. Can we just enable the virtual
> > > plane by default in this situation?
> >
> > In which situation? It is a global switch. And we need to be able to
> > work with it turned off, until corresponding code is dropped.
>
> I mean the situation that the plane SSPP allocation and related resource
> preparation shall be deferred until crtc calling the plane API. In this way,
> the traditional plane management is almost identical with the virtual
> plane method. Or could you point out what shall differ for the two methods
> after we deferred the preparation? Thanks!

You just want to have different SSPP number limit for the 2 methods?
>
> Jun
> >
> > >
> > > Jun
> > >
> > > >
> > > >
> > > > --
> > > > With best wishes
> > > > Dmitry
> >
> > --
> > With best wishes
> > Dmitry
Re: [PATCH v16 09/10] drm/msm/dpu: support plane splitting in quad-pipe case
Posted by Dmitry Baryshkov 3 weeks, 4 days ago
On Thu, Jan 15, 2026 at 06:10:36PM +0800, Jun Nie wrote:
> Jun Nie <jun.nie@linaro.org> 于2026年1月15日周四 17:57写道:
> >
> > Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> 于2026年1月15日周四 17:39写道:
> > >
> > > On Thu, Jan 15, 2026 at 05:34:28PM +0800, Jun Nie wrote:
> > > > Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> 于2026年1月15日周四 00:12写道:
> > > > >
> > > > > On Wed, Jan 14, 2026 at 10:48:17PM +0800, Jun Nie wrote:
> > > > > > Jun Nie <jun.nie@linaro.org> 于2025年9月18日周四 21:30写道:
> > > > > > >
> > > > > > > The content of every half of screen is sent out via one interface in
> > > > > > > dual-DSI case. The content for every interface is blended by a LM
> > > > > > > pair in quad-pipe case, thus a LM pair should not blend any content
> > > > > > > that cross the half of screen in this case. Clip plane into pipes per
> > > > > > > left and right half screen ROI if topology is quad pipe case.
> > > > > > >
> > > > > > > The clipped rectangle on every half of screen is futher handled by two
> > > > > > > pipes if its width exceeds a limit for a single pipe.
> > > > > > >
> > > > > > > Signed-off-by: Jun Nie <jun.nie@linaro.org>
> > > > > > > Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> > > > > > > Reviewed-by: Jessica Zhang <jessica.zhang@oss.qualcomm.com>
> > > > > > > ---
> > > > > > >  drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c  |  11 +++
> > > > > > >  drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h  |   2 +
> > > > > > >  drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 137 +++++++++++++++++++++---------
> > > > > > >  3 files changed, 110 insertions(+), 40 deletions(-)
> > > > > > >
> > > > > > > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > > > > > > index d825eb8e40ae8bd456ede6269951339e3053d0d3..e925d93b38feac0594d735fdc2c5b9fd5ae83e6a 100644
> > > > > > > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > > > > > > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> > > > > > > @@ -1604,6 +1604,17 @@ int dpu_crtc_vblank(struct drm_crtc *crtc, bool en)
> > > > > > >         return 0;
> > > > > > >  }
> > > > > > >
> > > > > > > +/**
> > > > > > > + * dpu_crtc_get_num_lm - Get mixer number in this CRTC pipeline
> > > > > > > + * @state: Pointer to drm crtc state object
> > > > > > > + */
> > > > > > > +unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state)
> > > > > > > +{
> > > > > > > +       struct dpu_crtc_state *cstate = to_dpu_crtc_state(state);
> > > > > > > +
> > > > > > > +       return cstate->num_mixers;
> > > > > > > +}
> > > > > > > +
> > > > > > >  #ifdef CONFIG_DEBUG_FS
> > > > > > >  static int _dpu_debugfs_status_show(struct seq_file *s, void *data)
> > > > > > >  {
> > > > > > > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > > > > > > index 94392b9b924546f96e738ae20920cf9afd568e6b..6eaba5696e8e6bd1246a9895c4c8714ca6589b10 100644
> > > > > > > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > > > > > > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
> > > > > > > @@ -267,4 +267,6 @@ static inline enum dpu_crtc_client_type dpu_crtc_get_client_type(
> > > > > > >
> > > > > > >  void dpu_crtc_frame_event_cb(struct drm_crtc *crtc, u32 event);
> > > > > > >
> > > > > > > +unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state);
> > > > > > > +
> > > > > > >  #endif /* _DPU_CRTC_H_ */
> > > > > > > diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > > > > > > index 5ae58352cbee1251a0140879f04fc7c304cae674..89a5feb6308bcac537562c3dc4e61c16c92e460c 100644
> > > > > > > --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > > > > > > +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> > > > > > > @@ -824,8 +824,12 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> > > > > > >         struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state);
> > > > > > >         struct dpu_sw_pipe_cfg *pipe_cfg;
> > > > > > >         struct dpu_sw_pipe_cfg *r_pipe_cfg;
> > > > > > > +       struct dpu_sw_pipe_cfg init_pipe_cfg;
> > > > > > >         struct drm_rect fb_rect = { 0 };
> > > > > > > +       const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
> > > > > > >         uint32_t max_linewidth;
> > > > > > > +       u32 num_lm;
> > > > > > > +       int stage_id, num_stages;
> > > > > > >
> > > > > > >         min_scale = FRAC_16_16(1, MAX_UPSCALE_RATIO);
> > > > > > >         max_scale = MAX_DOWNSCALE_RATIO << 16;
> > > > > > > @@ -848,13 +852,10 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> > > > > > >                 return -EINVAL;
> > > > > > >         }
> > > > > > >
> > > > > > > -       /* move the assignment here, to ease handling to another pairs later */
> > > > > > > -       pipe_cfg = &pstate->pipe_cfg[0];
> > > > > > > -       r_pipe_cfg = &pstate->pipe_cfg[1];
> > > > > > > -       /* state->src is 16.16, src_rect is not */
> > > > > > > -       drm_rect_fp_to_int(&pipe_cfg->src_rect, &new_plane_state->src);
> > > > > > > +       num_lm = dpu_crtc_get_num_lm(crtc_state);
> > > > > > >
> > > > > > > -       pipe_cfg->dst_rect = new_plane_state->dst;
> > > > > > > +       /* state->src is 16.16, src_rect is not */
> > > > > > > +       drm_rect_fp_to_int(&init_pipe_cfg.src_rect, &new_plane_state->src);
> > > > > > >
> > > > > > >         fb_rect.x2 = new_plane_state->fb->width;
> > > > > > >         fb_rect.y2 = new_plane_state->fb->height;
> > > > > > > @@ -879,35 +880,94 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
> > > > > > >
> > > > > > >         max_linewidth = pdpu->catalog->caps->max_linewidth;
> > > > > > >
> > > > > > > -       drm_rect_rotate(&pipe_cfg->src_rect,
> > > > > > > +       drm_rect_rotate(&init_pipe_cfg.src_rect,
> > > > > > >                         new_plane_state->fb->width, new_plane_state->fb->height,
> > > > > > >                         new_plane_state->rotation);
> > > > > > >
> > > > > > > -       if ((drm_rect_width(&pipe_cfg->src_rect) > max_linewidth) ||
> > > > > > > -            _dpu_plane_calc_clk(&crtc_state->adjusted_mode, pipe_cfg) > max_mdp_clk_rate) {
> > > > > > > -               if (drm_rect_width(&pipe_cfg->src_rect) > 2 * max_linewidth) {
> > > > > > > -                       DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n",
> > > > > > > -                                       DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth);
> > > > > > > -                       return -E2BIG;
> > > > > > > +       /*
> > > > > > > +        * We have 1 mixer pair cfg for 1:1:1 and 2:2:1 topology, 2 mixer pair
> > > > > > > +        * configs for left and right half screen in case of 4:4:2 topology.
> > > > > > > +        * But we may have 2 rect to split wide plane that exceeds limit with 1
> > > > > > > +        * config for 2:2:1. So need to handle both wide plane splitting, and
> > > > > > > +        * two halves of screen splitting for quad-pipe case. Check dest
> > > > > > > +        * rectangle left/right clipping first, then check wide rectangle
> > > > > > > +        * splitting in every half next.
> > > > > > > +        */
> > > > > > > +       num_stages = (num_lm + 1) / 2;
> > > > > >
> > > > > > Hi Dmitry,
> > > > > > Because the plane is checked before crtc is checked in the drm framework. While
> > > > > > the topology is decided in crtc check. Thus num_lm is 0 when this function is
> > > > > > called for the first time. As a result, the below iteration is not run
> > > > > > at all and leads
> > > > > >  to iommu warning.
> > > > >
> > > > > How does it lead to IOMMU warnings?
> > > >
> > > > Because the pipe is not configured with width/height etc when the iteration is
> > > > skipped. I have not found the root cause so far. But per the null IOMMU iova
> > > > value, suppose it is due to DMA buffer not being prepared when DMA is started.
> > >
> > > I'd think, that corresponding SRC regs are either garbage or zero programmed.
> >
> > You are right in that. Sorry for my words is not accurate. I mean the
> > DMA buffer is not
> > feed to DMA engine correctly.
> > >
> > > >
> > > > >
> > > > > > Do you suggest to change drm framework with adding extra crtc check before
> > > > > > plane check, or you prefer the below line here?
> > > > > >
> > > > > > num_stages = max(1, (num_lm + 1) / 2);
> > > > >
> > > > > DRM framework provides enough hooks to be able to influence the order or
> > > > > operations without changing the framework. But, I'd like to point out
> > > > > that for the virtual plane case we already perform plane operations
> > > > > from dpu_crtc_atomic_check(). You can employ the same approach.
> > > >
> > > > Thanks for the suggestion! I see dpu_assign_plane_resources() is called
> > > > from crtc side, which avoids the plane splitting before topology decision.
> > > > To use this method, it looks like we are enabling the virtual plane by default.
> > > > Because the virtual plane differs from the traditional method only with the
> > > > plane splitting and resource preparation. Can we just enable the virtual
> > > > plane by default in this situation?
> > >
> > > In which situation? It is a global switch. And we need to be able to
> > > work with it turned off, until corresponding code is dropped.
> >
> > I mean the situation that the plane SSPP allocation and related resource
> > preparation shall be deferred until crtc calling the plane API. In this way,
> > the traditional plane management is almost identical with the virtual
> > plane method. Or could you point out what shall differ for the two methods
> > after we deferred the preparation? Thanks!
> 
> You just want to have different SSPP number limit for the 2 methods?

I'm not sure I foolow. Two methods provide different ways of handling
plane <-> SSPP relationship. In non-virtual planes, you'd still have a
fixed pipe <-> plane. I wanted to point out the way to push some of the
checks to the atomic_check(crtc) time. Bonus point if that's unified
between virtual and non-virtual paths.


-- 
With best wishes
Dmitry
Re: [PATCH v16 09/10] drm/msm/dpu: support plane splitting in quad-pipe case
Posted by Abel Vesa 1 month, 3 weeks ago
On 25-09-18 21:29:01, Jun Nie wrote:
> The content of every half of screen is sent out via one interface in
> dual-DSI case. The content for every interface is blended by a LM
> pair in quad-pipe case, thus a LM pair should not blend any content
> that cross the half of screen in this case. Clip plane into pipes per
> left and right half screen ROI if topology is quad pipe case.
> 
> The clipped rectangle on every half of screen is futher handled by two
> pipes if its width exceeds a limit for a single pipe.
> 
> Signed-off-by: Jun Nie <jun.nie@linaro.org>
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> Reviewed-by: Jessica Zhang <jessica.zhang@oss.qualcomm.com>

At least on Hamoa based devices (CRD and xps13) I have been seeing on every
boot the following:

arm-smmu 15000000.iommu: Unhandled context fault: fsr=0x402, iova=0x00000000, fsynr=0x3d0023, cbfrsynra=0x1c00, cb=13
arm-smmu 15000000.iommu: FSR    = 00000402 [Format=2 TF], SID=0x1c00
arm-smmu 15000000.iommu: FSYNR0 = 003d0023 [S1CBNDX=61 PNU PLVL=3]

Also there are some artifacts on eDP (no DP connected) on the xps13 with 2880x1800
(LGD 134WT1) panel. Not on CRD though.

Reverting this commit fixes it:
5978864e34b6 ("drm/msm/dpu: support plane splitting in quad-pipe case")

Discussing this off-list with Dmitry and Konrad, the conclusion was that
both this plus following commit should be reverted for the time being:

d7ec9366b15c ("drm/msm/dpu: Enable quad-pipe for DSC and dual-DSI case")