diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
index cdece21b81c9..7422bee8d21f 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
@@ -1354,7 +1354,7 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc,
 			 * is a shortcut. Perform actual check here, after
 			 * allocating SSPPs.
 			 */
-			rc = dpu_plane_atomic_check(plane, crtc_state->state);
+			rc = dpu_plane_virtual_atomic_check_late(plane, crtc_state->state);
 			if (rc)
 				return rc;
 		}
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
index ee906c276aa5..56cb03f1d393 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
@@ -840,20 +840,32 @@ static int dpu_plane_atomic_check_pipe(struct dpu_plane *pdpu,
 static int dpu_plane_virtual_atomic_check(struct drm_plane *plane,
 					  struct drm_atomic_state *state)
 {
+	struct dpu_plane *pdpu = to_dpu_plane(plane);
 	struct drm_plane_state *plane_state =
 		drm_atomic_get_plane_state(state, plane);
 	struct dpu_plane_state *pstate = to_dpu_plane_state(plane_state);
 	const struct dpu_format *format;
-	struct drm_crtc_state *crtc_state;
+	struct drm_crtc_state *crtc_state = NULL;
+	int ret;
+
+	if (plane_state->crtc)
+		crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
 
 	/*
-	 * Main part of checks, including drm_atomic_helper_check_plane_state()
-	 * is called from dpu_crtc_atomic_check(). Do minimal processing here.
+	 * Main part of checks is performed in
+	 * dpu_plane_virtual_atomic_check_late(), called from
+	 * dpu_crtc_atomic_check(). Do minimal processing here.
 	 */
+	ret = drm_atomic_helper_check_plane_noscale(plane_state, crtc_state,
+						    true, true);
+	if (ret) {
+		DPU_DEBUG_PLANE(to_dpu_plane(plane),
+				"Check plane state failed (%d)\n", ret);
+		return ret;
+	}
 
-	if (!plane_state->fb) {
-		plane_state->visible = false;
 
+	if (!plane_state->visible) {
 		/* resources are freed by dpu_crtc_atomic_check(), but clean them here */
 		pstate->pipe.sspp = NULL;
 		pstate->r_pipe.sspp = NULL;
@@ -861,18 +873,46 @@ static int dpu_plane_virtual_atomic_check(struct drm_plane *plane,
 		return 0;
 	}
 
+	pstate->stage = DPU_STAGE_0 + pstate->base.normalized_zpos;
+	if (pstate->stage >= pdpu->catalog->caps->max_mixer_blendstages) {
+		DPU_ERROR("> %d plane stages assigned\n",
+			  pdpu->catalog->caps->max_mixer_blendstages - DPU_STAGE_0);
+		return -EINVAL;
+	}
+
+	/* Ensure fb size is supported */
+	if (plane_state->fb->width > MAX_IMG_WIDTH ||
+	    plane_state->fb->height > MAX_IMG_HEIGHT) {
+		DPU_DEBUG_PLANE(pdpu, "invalid framebuffer %dx%d\n",
+				plane_state->fb->width,
+				plane_state->fb->height);
+		return -E2BIG;
+	}
+
 	format = to_dpu_format(msm_framebuffer_format(plane_state->fb));
-	crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
 
-	/* force resource reallocation if the format of FB has changed */
-	if (pstate->saved_fmt != format) {
+	/* force resource reallocation if the format of FB or src/dst have changed */
+	if (pstate->saved_fmt != format ||
+	    pstate->saved_src_w != plane_state->src_w ||
+	    pstate->saved_src_h != plane_state->src_h ||
+	    pstate->saved_src_w != plane_state->src_w ||
+	    pstate->saved_crtc_h != plane_state->crtc_h) {
 		crtc_state->planes_changed = true;
 		pstate->saved_fmt = format;
+		pstate->saved_src_w = plane_state->src_w;
+		pstate->saved_src_h = plane_state->src_h;
+		pstate->saved_crtc_w = plane_state->crtc_w;
+		pstate->saved_crtc_h = plane_state->crtc_h;
 	}
 
 	return 0;
 }
 
+/*
+ * Allocate backing SSPP blocks for the plane. This does not perform any
+ * additional checks on the plane, this is done in
+ * dpu_plane_virtual_atomic_check_late().
+ */
 int dpu_plane_virtual_assign_resources(struct drm_plane *plane,
 				       struct drm_crtc *crtc,
 				       struct dpu_global_state *global_state,
@@ -881,18 +921,44 @@ int dpu_plane_virtual_assign_resources(struct drm_plane *plane,
 	struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane);
 	struct dpu_plane_state *pstate;
 	struct drm_plane_state *plane_state;
+	struct dpu_sw_pipe *pipe;
+	struct dpu_sw_pipe *r_pipe;
+	struct dpu_sw_pipe_cfg *pipe_cfg;
+	struct dpu_sw_pipe_cfg *r_pipe_cfg;
 	struct dpu_hw_sspp *hw_sspp;
-	bool yuv, scale, rot90;
+	const struct dpu_format *fmt;
+	bool yuv, scale, rot90, tiled;
+	uint32_t max_linewidth;
 
 	plane_state = drm_atomic_get_plane_state(state, plane);
 	if (IS_ERR(plane_state))
 		return PTR_ERR(plane_state);
 
-	yuv = plane_state->fb ?
-		DPU_FORMAT_IS_YUV(to_dpu_format(msm_framebuffer_format(plane_state->fb))) :
-		false;
-	scale = (plane_state->src_w >> 16 != plane_state->crtc_w) ||
-		(plane_state->src_h >> 16 != plane_state->crtc_h);
+	pstate = to_dpu_plane_state(plane_state);
+
+	pipe = &pstate->pipe;
+	r_pipe = &pstate->r_pipe;
+	pipe_cfg = &pstate->pipe_cfg;
+	r_pipe_cfg = &pstate->r_pipe_cfg;
+
+	fmt = to_dpu_format(msm_framebuffer_format(plane_state->fb));
+	yuv = DPU_FORMAT_IS_YUV(fmt);
+	tiled = DPU_FORMAT_IS_UBWC(fmt);
+
+	/* state->src is 16.16, src_rect is not */
+	drm_rect_fp_to_int(&pipe_cfg->src_rect, &plane_state->src);
+	pipe_cfg->dst_rect = plane_state->dst;
+
+	scale = (drm_rect_width(&pipe_cfg->src_rect) != drm_rect_width(&pipe_cfg->dst_rect)) ||
+		(drm_rect_height(&pipe_cfg->src_rect) != drm_rect_height(&pipe_cfg->dst_rect));
+
+	max_linewidth = dpu_kms->catalog->caps->max_linewidth;
+
+	pipe->multirect_index = DPU_SSPP_RECT_SOLO;
+	pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE;
+	r_pipe->multirect_index = DPU_SSPP_RECT_SOLO;
+	r_pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE;
+	r_pipe->sspp = NULL;
 
 	rot90 = drm_rotation_90_or_270(plane_state->rotation);
 
@@ -900,21 +966,159 @@ int dpu_plane_virtual_assign_resources(struct drm_plane *plane,
 	if (!hw_sspp)
 		return -ENODEV;
 
-	pstate = to_dpu_plane_state(plane_state);
-	pstate->pipe.sspp = hw_sspp;
+	pipe->sspp = hw_sspp;
+
+	if (drm_rect_width(&pipe_cfg->src_rect) <= max_linewidth)
+		return 0;
+
+	drm_rect_rotate(&pipe_cfg->src_rect,
+			plane_state->fb->width, plane_state->fb->height,
+			plane_state->rotation);
+
+	*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;
+
+	if (yuv && pipe_cfg->src_rect.x2 & 0x1) {
+		pipe_cfg->src_rect.x2 -= 1;
+		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;
+
+	if (drm_rect_width(&pipe_cfg->src_rect) > max_linewidth ||
+	    drm_rect_width(&r_pipe_cfg->src_rect) > max_linewidth) {
+		DPU_DEBUG_PLANE(to_dpu_plane(plane),
+				"invalid src " DRM_RECT_FMT " / " DRM_RECT_FMT " line:%u\n",
+				DRM_RECT_ARG(&pipe_cfg->src_rect),
+				DRM_RECT_ARG(&r_pipe_cfg->src_rect),
+				max_linewidth);
+		return -E2BIG;
+	}
+
+	drm_rect_rotate_inv(&pipe_cfg->src_rect,
+			    plane_state->fb->width, plane_state->fb->height,
+			    plane_state->rotation);
+	drm_rect_rotate_inv(&r_pipe_cfg->src_rect,
+			    plane_state->fb->width, plane_state->fb->height,
+			    plane_state->rotation);
+
+	/*
+	 * Check if we can use parallel multirect for the wide plane.
+	 *
+	 * For tiled formats there is no point in trying multirect.
+	 * In parallel multirect case only the half of the usual width
+	 * is supported for tiled formats. If we are here, we know that
+	 * full width is more than max_linewidth, thus each rect is
+	 * wider than allowed.
+	 */
+	if (!yuv && !scale && !tiled &&
+	    test_bit(DPU_SSPP_SMART_DMA_V2, &pipe->sspp->cap->features)) {
+		pipe->multirect_index = DPU_SSPP_RECT_0;
+		pipe->multirect_mode = DPU_SSPP_MULTIRECT_PARALLEL;
+
+		r_pipe->sspp = pipe->sspp;
+		r_pipe->multirect_index = DPU_SSPP_RECT_1;
+		r_pipe->multirect_mode = DPU_SSPP_MULTIRECT_PARALLEL;
+	} else {
+		/* multirect is not possible, use two SSPP blocks */
+		hw_sspp = dpu_rm_reserve_sspp(&dpu_kms->rm, global_state, crtc, yuv, scale, rot90);
+		if (!hw_sspp)
+			return -ENODEV;
+
+		r_pipe->sspp = hw_sspp;
+	}
 
 	return 0;
 }
 
-int dpu_plane_atomic_check(struct drm_plane *plane,
-			   struct drm_atomic_state *state)
+int dpu_plane_virtual_atomic_check_late(struct drm_plane *plane,
+					struct drm_atomic_state *state)
+{
+	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
+	int ret = 0, min_scale, max_scale, hscale, vscale;
+	struct dpu_plane *pdpu = to_dpu_plane(plane);
+	struct dpu_plane_state *pstate = to_dpu_plane_state(plane_state);
+	struct dpu_sw_pipe *pipe = &pstate->pipe;
+	struct dpu_sw_pipe *r_pipe = &pstate->r_pipe;
+	const struct drm_crtc_state *crtc_state = NULL;
+	const struct dpu_format *fmt;
+	struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg;
+	struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->r_pipe_cfg;
+	unsigned int rotation;
+	uint32_t supported_rotations;
+	struct drm_rect src;
+	const struct dpu_sspp_cfg *pipe_hw_caps;
+	const struct dpu_sspp_sub_blks *sblk;
+
+	if (plane_state->crtc)
+		crtc_state = drm_atomic_get_new_crtc_state(state,
+							   plane_state->crtc);
+
+	if (!plane_state->visible)
+		return 0;
+
+	pipe_hw_caps = pstate->pipe.sspp->cap;
+	sblk = pstate->pipe.sspp->cap->sblk;
+
+	src = drm_plane_state_src(plane_state);
+	drm_rect_rotate(&src, plane_state->fb->width << 16, plane_state->fb->height << 16,
+			plane_state->rotation);
+
+	min_scale = FRAC_16_16(1, sblk->maxupscale);
+	max_scale = sblk->maxdwnscale << 16;
+	hscale = drm_rect_calc_hscale(&plane_state->src, &plane_state->dst, min_scale, max_scale);
+	vscale = drm_rect_calc_vscale(&plane_state->src, &plane_state->dst, min_scale, max_scale);
+	if (hscale < 0 || vscale < 0) {
+		drm_dbg_kms(plane->dev, "Invalid scaling of plane\n");
+		drm_rect_debug_print("src: ", &plane_state->src, true);
+		drm_rect_debug_print("dst: ", &plane_state->dst, false);
+		return -ERANGE;
+	}
+
+	fmt = to_dpu_format(msm_framebuffer_format(plane_state->fb));
+
+	ret = dpu_plane_atomic_check_pipe(pdpu, pipe, pipe_cfg, fmt);
+	if (ret)
+		return ret;
+
+	if (r_pipe->sspp) {
+		ret = dpu_plane_atomic_check_pipe(pdpu, r_pipe, r_pipe_cfg, fmt);
+		if (ret)
+			return ret;
+	}
+
+	supported_rotations = DRM_MODE_REFLECT_MASK | DRM_MODE_ROTATE_0;
+
+	if (pipe_hw_caps->features & BIT(DPU_SSPP_INLINE_ROTATION))
+		supported_rotations |= DRM_MODE_ROTATE_90;
+
+	rotation = drm_rotation_simplify(plane_state->rotation,
+					supported_rotations);
+
+	if ((pipe_hw_caps->features & BIT(DPU_SSPP_INLINE_ROTATION)) &&
+		(rotation & DRM_MODE_ROTATE_90)) {
+		ret = dpu_plane_check_inline_rotation(pdpu, sblk, pipe_cfg->src_rect, fmt);
+		if (ret)
+			return ret;
+	}
+
+	pstate->rotation = rotation;
+	pstate->needs_qos_remap = drm_atomic_crtc_needs_modeset(crtc_state);
+
+	return 0;
+}
+
+static int dpu_plane_atomic_check(struct drm_plane *plane,
+				  struct drm_atomic_state *state)
 {
 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
 										 plane);
 	int ret = 0, min_scale;
 	struct dpu_plane *pdpu = to_dpu_plane(plane);
 	struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state);
-	struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane);
 	struct dpu_sw_pipe *pipe = &pstate->pipe;
 	struct dpu_sw_pipe *r_pipe = &pstate->r_pipe;
 	const struct drm_crtc_state *crtc_state = NULL;
@@ -932,11 +1136,6 @@ int dpu_plane_atomic_check(struct drm_plane *plane,
 		crtc_state = drm_atomic_get_new_crtc_state(state,
 							   new_plane_state->crtc);
 
-	if (pdpu->pipe != SSPP_NONE) {
-		pipe->sspp = dpu_rm_get_sspp(&dpu_kms->rm, pdpu->pipe);
-		r_pipe->sspp = NULL;
-	}
-
 	pipe_hw_caps = pstate->pipe.sspp->cap;
 	sblk = pstate->pipe.sspp->cap->sblk;
 
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h
index cb1e31ef0d3f..07e0796cc100 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h
@@ -32,6 +32,10 @@
  * @needs_dirtyfb: whether attached CRTC needs pixel data explicitly flushed
  * @rotation: simplified drm rotation hint
  * @saved_fmt: format used by the plane's FB, saved for for virtual plane support
+ * @saved_src_w: cached value of plane's src_w, saved for for virtual plane support
+ * @saved_src_h: cached value of plane's src_h, saved for for virtual plane support
+ * @saved_crtc_w: cached value of plane's crtc_w, saved for for virtual plane support
+ * @saved_crtc_h: cached value of plane's crtc_h, saved for for virtual plane support
  */
 struct dpu_plane_state {
 	struct drm_plane_state base;
@@ -51,6 +55,10 @@ struct dpu_plane_state {
 	unsigned int rotation;
 
 	const struct dpu_format *saved_fmt;
+	uint32_t saved_src_w;
+	uint32_t saved_src_h;
+	uint32_t saved_crtc_w;
+	uint32_t saved_crtc_h;
 };
 
 #define to_dpu_plane_state(x) \
@@ -106,11 +114,12 @@ void dpu_plane_danger_signal_ctrl(struct drm_plane *plane, bool enable);
 static inline void dpu_plane_danger_signal_ctrl(struct drm_plane *plane, bool enable) {}
 #endif
 
-int dpu_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state);
-
 int dpu_plane_virtual_assign_resources(struct drm_plane *plane,
 				       struct drm_crtc *crtc,
 				       struct dpu_global_state *global_state,
 				       struct drm_atomic_state *state);
 
+int dpu_plane_virtual_atomic_check_late(struct drm_plane *plane,
+					struct drm_atomic_state *state);
+
 #endif /* _DPU_PLANE_H_ */
