diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
index 09ea7c3d8951..0f6e545d74ba 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
@@ -373,6 +373,30 @@ static void mdp5_crtc_enable(struct drm_crtc *crtc)
 	mdp5_crtc->enabled = true;
 }
 
+int mdp5_crtc_setup_pipeline(struct drm_crtc *crtc,
+			     struct drm_crtc_state *new_crtc_state)
+{
+	struct mdp5_crtc_state *mdp5_cstate =
+			to_mdp5_crtc_state(new_crtc_state);
+	struct mdp5_pipeline *pipeline = &mdp5_cstate->pipeline;
+	bool new_mixer = false;
+
+	new_mixer = !pipeline->mixer;
+
+	if (new_mixer) {
+		struct mdp5_hw_mixer *old_mixer = pipeline->mixer;
+
+		pipeline->mixer = mdp5_mixer_assign(new_crtc_state->state, crtc,
+						    MDP_LM_CAP_DISPLAY);
+		if (IS_ERR(pipeline->mixer))
+			return PTR_ERR(pipeline->mixer);
+
+		mdp5_mixer_release(new_crtc_state->state, old_mixer);
+	}
+
+	return 0;
+}
+
 struct plane_state {
 	struct drm_plane *plane;
 	struct mdp5_plane_state *state;
@@ -405,6 +429,7 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc,
 	const struct drm_plane_state *pstate;
 	bool cursor_plane = false;
 	int cnt = 0, base = 0, i;
+	int ret;
 
 	DBG("%s: check", crtc->name);
 
@@ -418,6 +443,12 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc,
 			cursor_plane = true;
 	}
 
+	ret = mdp5_crtc_setup_pipeline(crtc, state);
+	if (ret) {
+		dev_err(dev->dev, "couldn't assign mixers %d\n", ret);
+		return ret;
+	}
+
 	/* assign a stage based on sorted zpos property */
 	sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL);
 
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
index 9dcd893a5c06..9525c990f2dc 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
@@ -90,6 +90,7 @@ struct mdp5_state *mdp5_get_state(struct drm_atomic_state *s)
 
 	/* Copy state: */
 	new_state->hwpipe = mdp5_kms->state->hwpipe;
+	new_state->hwmixer = mdp5_kms->state->hwmixer;
 	if (mdp5_kms->smp)
 		new_state->smp = mdp5_kms->state->smp;
 
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
index b875f453b930..535848e93f55 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
@@ -96,6 +96,7 @@ struct mdp5_kms {
  */
 struct mdp5_state {
 	struct mdp5_hw_pipe_state hwpipe;
+	struct mdp5_hw_mixer_state hwmixer;
 	struct mdp5_smp_state smp;
 };
 
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c
index 032dc0a9638f..6f353c4886e4 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c
@@ -16,6 +16,61 @@
 
 #include "mdp5_kms.h"
 
+struct mdp5_hw_mixer *mdp5_mixer_assign(struct drm_atomic_state *s,
+					struct drm_crtc *crtc, uint32_t caps)
+{
+	struct msm_drm_private *priv = s->dev->dev_private;
+	struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms));
+	struct mdp5_state *state = mdp5_get_state(s);
+	struct mdp5_hw_mixer_state *new_state;
+	struct mdp5_hw_mixer *mixer = NULL;
+	int i;
+
+	if (IS_ERR(state))
+		return ERR_CAST(state);
+
+	new_state = &state->hwmixer;
+
+	for (i = 0; i < mdp5_kms->num_hwmixers; i++) {
+		struct mdp5_hw_mixer *cur = mdp5_kms->hwmixers[i];
+
+		/* skip if already in-use */
+		if (new_state->hwmixer_to_crtc[cur->idx])
+			continue;
+
+		/* skip if doesn't support some required caps: */
+		if (caps & ~cur->caps)
+			continue;
+
+		if (!mixer)
+			mixer = cur;
+	}
+
+	if (!mixer)
+		return ERR_PTR(-ENOMEM);
+
+	new_state->hwmixer_to_crtc[mixer->idx] = crtc;
+
+	return mixer;
+}
+
+void mdp5_mixer_release(struct drm_atomic_state *s, struct mdp5_hw_mixer *mixer)
+{
+	struct mdp5_state *state = mdp5_get_state(s);
+	struct mdp5_hw_mixer_state *new_state = &state->hwmixer;
+
+	if (!mixer)
+		return;
+
+	if (WARN_ON(!new_state->hwmixer_to_crtc[mixer->idx]))
+		return;
+
+	DBG("%s: release from crtc %s", mixer->name,
+	    new_state->hwmixer_to_crtc[mixer->idx]->name);
+
+	new_state->hwmixer_to_crtc[mixer->idx] = NULL;
+}
+
 void mdp5_mixer_destroy(struct mdp5_hw_mixer *mixer)
 {
 	kfree(mixer);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h
index 08d2173703bf..18ed933ae574 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h
@@ -31,7 +31,16 @@ struct mdp5_hw_mixer {
 	uint32_t flush_mask;      /* used to commit LM registers */
 };
 
+/* global atomic state of assignment between CRTCs and Layer Mixers: */
+struct mdp5_hw_mixer_state {
+	struct drm_crtc *hwmixer_to_crtc[8];
+};
+
 struct mdp5_hw_mixer *mdp5_mixer_init(const struct mdp5_lm_instance *lm);
 void mdp5_mixer_destroy(struct mdp5_hw_mixer *lm);
+struct mdp5_hw_mixer *mdp5_mixer_assign(struct drm_atomic_state *s,
+					struct drm_crtc *crtc, uint32_t caps);
+void mdp5_mixer_release(struct drm_atomic_state *s,
+			struct mdp5_hw_mixer *mixer);
 
 #endif /* __MDP5_LM_H__ */
