diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
index f382c3405786..5bffef6ec9dc 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
@@ -105,7 +105,7 @@ static u32 crtc_flush(struct drm_crtc *crtc, u32 flush_mask)
 static u32 crtc_flush_all(struct drm_crtc *crtc)
 {
 	struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state);
-	struct mdp5_hw_mixer *mixer;
+	struct mdp5_hw_mixer *mixer, *r_mixer;
 	struct drm_plane *plane;
 	uint32_t flush_mask = 0;
 
@@ -120,6 +120,10 @@ static u32 crtc_flush_all(struct drm_crtc *crtc)
 	mixer = mdp5_cstate->pipeline.mixer;
 	flush_mask |= mdp_ctl_flush_mask_lm(mixer->lm);
 
+	r_mixer = mdp5_cstate->pipeline.r_mixer;
+	if (r_mixer)
+		flush_mask |= mdp_ctl_flush_mask_lm(r_mixer->lm);
+
 	return crtc_flush(crtc, flush_mask);
 }
 
@@ -151,7 +155,7 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file)
 
 	if (ctl && !crtc->state->enable) {
 		/* set STAGE_UNUSED for all layers */
-		mdp5_ctl_blend(ctl, pipeline, NULL, 0, 0);
+		mdp5_ctl_blend(ctl, pipeline, NULL, NULL, 0, 0);
 		/* XXX: What to do here? */
 		/* mdp5_crtc->ctl = NULL; */
 	}
@@ -193,6 +197,12 @@ static inline u32 mdp5_lm_use_fg_alpha_mask(enum mdp_mixer_stage_id stage)
 }
 
 /*
+ * left/right pipe offsets for the stage array used in blend_setup()
+ */
+#define PIPE_LEFT	0
+#define PIPE_RIGHT	1
+
+/*
  * blend_setup() - blend all the planes of a CRTC
  *
  * If no base layer is available, border will be enabled as the base layer.
@@ -211,10 +221,13 @@ static void blend_setup(struct drm_crtc *crtc)
 	const struct mdp_format *format;
 	struct mdp5_hw_mixer *mixer = pipeline->mixer;
 	uint32_t lm = mixer->lm;
+	struct mdp5_hw_mixer *r_mixer = pipeline->r_mixer;
+	uint32_t r_lm = r_mixer ? r_mixer->lm : 0;
 	struct mdp5_ctl *ctl = mdp5_cstate->ctl;
 	uint32_t blend_op, fg_alpha, bg_alpha, ctl_blend_flags = 0;
 	unsigned long flags;
-	enum mdp5_pipe stage[STAGE_MAX + 1] = { SSPP_NONE };
+	enum mdp5_pipe stage[STAGE_MAX + 1][MAX_PIPE_STAGE] = { SSPP_NONE };
+	enum mdp5_pipe r_stage[STAGE_MAX + 1][MAX_PIPE_STAGE] = { SSPP_NONE };
 	int i, plane_cnt = 0;
 	bool bg_alpha_enabled = false;
 	u32 mixer_op_mode = 0;
@@ -233,7 +246,15 @@ static void blend_setup(struct drm_crtc *crtc)
 	drm_atomic_crtc_for_each_plane(plane, crtc) {
 		pstate = to_mdp5_plane_state(plane->state);
 		pstates[pstate->stage] = pstate;
-		stage[pstate->stage] = mdp5_plane_pipe(plane);
+		stage[pstate->stage][PIPE_LEFT] = mdp5_plane_pipe(plane);
+		/*
+		 * if we have a right mixer, stage the same pipe as we
+		 * have on the left mixer
+		 */
+		if (r_mixer)
+			r_stage[pstate->stage][PIPE_LEFT] =
+						mdp5_plane_pipe(plane);
+
 		plane_cnt++;
 	}
 
@@ -299,12 +320,23 @@ static void blend_setup(struct drm_crtc *crtc)
 				blender(i)), fg_alpha);
 		mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_BG_ALPHA(lm,
 				blender(i)), bg_alpha);
+		if (r_mixer) {
+			mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_OP_MODE(r_lm,
+					blender(i)), blend_op);
+			mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_FG_ALPHA(r_lm,
+					blender(i)), fg_alpha);
+			mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_BG_ALPHA(r_lm,
+					blender(i)), bg_alpha);
+		}
 	}
 
 	mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(lm), mixer_op_mode);
+	if (r_mixer)
+		mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(r_lm),
+			   mixer_op_mode);
 
-	mdp5_ctl_blend(ctl, pipeline, stage, plane_cnt, ctl_blend_flags);
-
+	mdp5_ctl_blend(ctl, pipeline, stage, r_stage, plane_cnt,
+		       ctl_blend_flags);
 out:
 	spin_unlock_irqrestore(&mdp5_crtc->lm_lock, flags);
 }
@@ -315,6 +347,7 @@ static void mdp5_crtc_mode_set_nofb(struct drm_crtc *crtc)
 	struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state);
 	struct mdp5_kms *mdp5_kms = get_kms(crtc);
 	struct mdp5_hw_mixer *mixer = mdp5_cstate->pipeline.mixer;
+	struct mdp5_hw_mixer *r_mixer = mdp5_cstate->pipeline.r_mixer;
 	uint32_t lm = mixer->lm;
 	unsigned long flags;
 	struct drm_display_mode *mode;
@@ -337,6 +370,10 @@ static void mdp5_crtc_mode_set_nofb(struct drm_crtc *crtc)
 	mdp5_write(mdp5_kms, REG_MDP5_LM_OUT_SIZE(lm),
 			MDP5_LM_OUT_SIZE_WIDTH(mode->hdisplay) |
 			MDP5_LM_OUT_SIZE_HEIGHT(mode->vdisplay));
+	if (r_mixer)
+		mdp5_write(mdp5_kms, REG_MDP5_LM_OUT_SIZE(r_mixer->lm),
+			   MDP5_LM_OUT_SIZE_WIDTH(mode->hdisplay) |
+			   MDP5_LM_OUT_SIZE_HEIGHT(mode->vdisplay));
 	spin_unlock_irqrestore(&mdp5_crtc->lm_lock, flags);
 }
 
@@ -618,6 +655,10 @@ static int mdp5_crtc_cursor_set(struct drm_crtc *crtc,
 	if (!ctl)
 		return -EINVAL;
 
+	/* don't support LM cursors when we we have source split enabled */
+	if (mdp5_cstate->pipeline.r_mixer)
+		return -EINVAL;
+
 	if (!handle) {
 		DBG("Cursor off");
 		cursor_enable = false;
@@ -691,6 +732,10 @@ static int mdp5_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
 	uint32_t roi_h;
 	unsigned long flags;
 
+	/* don't support LM cursors when we we have source split enabled */
+	if (mdp5_cstate->pipeline.r_mixer)
+		return -EINVAL;
+
 	/* In case the CRTC is disabled, just drop the cursor update */
 	if (unlikely(!crtc->state->enable))
 		return 0;
@@ -720,12 +765,17 @@ mdp5_crtc_atomic_print_state(struct drm_printer *p,
 {
 	struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(state);
 	struct mdp5_pipeline *pipeline = &mdp5_cstate->pipeline;
+	struct mdp5_kms *mdp5_kms = get_kms(state->crtc);
 
 	if (WARN_ON(!pipeline))
 		return;
 
 	drm_printf(p, "\thwmixer=%s\n", pipeline->mixer ?
 			pipeline->mixer->name : "(null)");
+
+	if (mdp5_kms->caps & MDP_CAP_SRC_SPLIT)
+		drm_printf(p, "\tright hwmixer=%s\n", pipeline->r_mixer ?
+			   pipeline->r_mixer->name : "(null)");
 }
 
 static void mdp5_crtc_reset(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c
index 9a0109410974..1fdbb936877f 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c
@@ -172,9 +172,12 @@ int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline)
 	struct mdp5_kms *mdp5_kms = get_kms(ctl_mgr);
 	struct mdp5_interface *intf = pipeline->intf;
 	struct mdp5_hw_mixer *mixer = pipeline->mixer;
+	struct mdp5_hw_mixer *r_mixer = pipeline->r_mixer;
 
 	ctl->start_mask = mdp_ctl_flush_mask_lm(mixer->lm) |
 			  mdp_ctl_flush_mask_encoder(intf);
+	if (r_mixer)
+		ctl->start_mask |= mdp_ctl_flush_mask_lm(r_mixer->lm);
 
 	/* Virtual interfaces need not set a display intf (e.g.: Writeback) */
 	if (!mdp5_cfg_intf_is_virtual(intf->type))
@@ -224,8 +227,11 @@ static void refill_start_mask(struct mdp5_ctl *ctl,
 {
 	struct mdp5_interface *intf = pipeline->intf;
 	struct mdp5_hw_mixer *mixer = pipeline->mixer;
+	struct mdp5_hw_mixer *r_mixer = pipeline->r_mixer;
 
 	ctl->start_mask = mdp_ctl_flush_mask_lm(mixer->lm);
+	if (r_mixer)
+		ctl->start_mask |= mdp_ctl_flush_mask_lm(r_mixer->lm);
 
 	/*
 	 * Writeback encoder needs to program & flush
@@ -282,6 +288,11 @@ int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline,
 		return -EINVAL;
 	}
 
+	if (pipeline->r_mixer) {
+		dev_err(ctl_mgr->dev->dev, "unsupported configuration");
+		return -EINVAL;
+	}
+
 	spin_lock_irqsave(&ctl->hw_lock, flags);
 
 	blend_cfg = ctl_read(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, mixer->lm));
@@ -344,24 +355,40 @@ static u32 mdp_ctl_blend_ext_mask(enum mdp5_pipe pipe,
 	}
 }
 
+#define PIPE_LEFT	0
+#define PIPE_RIGHT	1
 int mdp5_ctl_blend(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline,
-		   enum mdp5_pipe *stage, u32 stage_cnt, u32 ctl_blend_op_flags)
+		   enum mdp5_pipe stage[][MAX_PIPE_STAGE],
+		   enum mdp5_pipe r_stage[][MAX_PIPE_STAGE],
+		   u32 stage_cnt, u32 ctl_blend_op_flags)
 {
 	struct mdp5_hw_mixer *mixer = pipeline->mixer;
+	struct mdp5_hw_mixer *r_mixer = pipeline->r_mixer;
 	unsigned long flags;
 	u32 blend_cfg = 0, blend_ext_cfg = 0;
+	u32 r_blend_cfg = 0, r_blend_ext_cfg = 0;
 	int i, start_stage;
 
 	if (ctl_blend_op_flags & MDP5_CTL_BLEND_OP_FLAG_BORDER_OUT) {
 		start_stage = STAGE0;
 		blend_cfg |= MDP5_CTL_LAYER_REG_BORDER_COLOR;
+		if (r_mixer)
+			r_blend_cfg |= MDP5_CTL_LAYER_REG_BORDER_COLOR;
 	} else {
 		start_stage = STAGE_BASE;
 	}
 
 	for (i = start_stage; stage_cnt && i <= STAGE_MAX; i++) {
-		blend_cfg |= mdp_ctl_blend_mask(stage[i], i);
-		blend_ext_cfg |= mdp_ctl_blend_ext_mask(stage[i], i);
+		blend_cfg |=
+			mdp_ctl_blend_mask(stage[i][PIPE_LEFT], i);
+		blend_ext_cfg |=
+			mdp_ctl_blend_ext_mask(stage[i][PIPE_LEFT], i);
+		if (r_mixer) {
+			r_blend_cfg |=
+				mdp_ctl_blend_mask(r_stage[i][PIPE_LEFT], i);
+			r_blend_ext_cfg |=
+				mdp_ctl_blend_ext_mask(r_stage[i][PIPE_LEFT], i);
+		}
 	}
 
 	spin_lock_irqsave(&ctl->hw_lock, flags);
@@ -371,12 +398,23 @@ int mdp5_ctl_blend(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline,
 	ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, mixer->lm), blend_cfg);
 	ctl_write(ctl, REG_MDP5_CTL_LAYER_EXT_REG(ctl->id, mixer->lm),
 		  blend_ext_cfg);
+	if (r_mixer) {
+		ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, r_mixer->lm),
+			  r_blend_cfg);
+		ctl_write(ctl, REG_MDP5_CTL_LAYER_EXT_REG(ctl->id, r_mixer->lm),
+			  r_blend_ext_cfg);
+	}
 	spin_unlock_irqrestore(&ctl->hw_lock, flags);
 
 	ctl->pending_ctl_trigger = mdp_ctl_flush_mask_lm(mixer->lm);
+	if (r_mixer)
+		ctl->pending_ctl_trigger |= mdp_ctl_flush_mask_lm(r_mixer->lm);
 
 	DBG("lm%d: blend config = 0x%08x. ext_cfg = 0x%08x", mixer->lm,
 		blend_cfg, blend_ext_cfg);
+	if (r_mixer)
+		DBG("lm%d: blend config = 0x%08x. ext_cfg = 0x%08x",
+		    r_mixer->lm, r_blend_cfg, r_blend_ext_cfg);
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h
index 751dd861cfd8..b63120388dc6 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h
@@ -46,6 +46,8 @@ int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline,
 			int cursor_id, bool enable);
 int mdp5_ctl_pair(struct mdp5_ctl *ctlx, struct mdp5_ctl *ctly, bool enable);
 
+#define MAX_PIPE_STAGE		2
+
 /*
  * mdp5_ctl_blend() - Blend multiple layers on a Layer Mixer (LM)
  *
@@ -59,8 +61,9 @@ int mdp5_ctl_pair(struct mdp5_ctl *ctlx, struct mdp5_ctl *ctly, bool enable);
  */
 #define MDP5_CTL_BLEND_OP_FLAG_BORDER_OUT	BIT(0)
 int mdp5_ctl_blend(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline,
-		   enum mdp5_pipe *stage, u32 stage_cnt,
-		   u32 ctl_blend_op_flags);
+		   enum mdp5_pipe stage[][MAX_PIPE_STAGE],
+		   enum mdp5_pipe r_stage[][MAX_PIPE_STAGE],
+		   u32 stage_cnt, u32 ctl_blend_op_flags);
 
 /**
  * mdp_ctl_flush_mask...() - Register FLUSH masks
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
index 7c899cc7388a..759cf069f2ab 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
@@ -125,6 +125,7 @@ struct mdp5_plane_state {
 struct mdp5_pipeline {
 	struct mdp5_interface *intf;
 	struct mdp5_hw_mixer *mixer;
+	struct mdp5_hw_mixer *r_mixer;	/* right mixer */
 };
 
 struct mdp5_crtc_state {
