drm/msm/sde: add basic sde object enumeration

Implement basic crtc, encoder, and plane support that
implements DRM atomic call sequence, queries hardware, and
enumerates resources as DRM objects. This includes basic
color format support, as well as querying of displays
to create appropriate encoders, crtcs. Also includes
supporting clock control logic.

Change-Id: I25a9c74b92262d81986b3441c89d51bff2d14dbb
Signed-off-by: Abhijit Kulkarni <kabhijit@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index a633433..673e5eca 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -97,4 +97,5 @@
 	sde/sde_hw_mdp_util.o \
 	sde/sde_hw_sspp.o \
 	sde/sde_hw_wb.o \
-	sde/sde_hw_pingpong.o
+	sde/sde_hw_pingpong.o \
+	sde/sde_mdp_formats.o
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 4812a5f..b46ec80 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -10,6 +10,7 @@
  * GNU General Public License for more details.
  */
 
+#include <linux/sort.h>
 #include <drm/drm_mode.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
@@ -17,73 +18,433 @@
 
 #include "sde_kms.h"
 #include "sde_hw_lm.h"
-#include "sde_hw_mdss.h"
+#include "sde_hw_mdp_ctl.h"
+
+#define CRTC_DUAL_MIXERS	2
+#define PENDING_FLIP		2
+
+#define CRTC_HW_MIXER_MAXSTAGES(c, idx) ((c)->mixer[idx].sblk->maxblendstages)
+
+struct sde_crtc_mixer {
+	struct sde_hw_dspp  *hw_dspp;
+	struct sde_hw_mixer *hw_lm;
+	struct sde_hw_ctl   *hw_ctl;
+	u32 flush_mask;
+};
 
 struct sde_crtc {
 	struct drm_crtc base;
 	char name[8];
 	struct drm_plane *plane;
 	struct drm_plane *planes[8];
+	struct drm_encoder *encoder;
 	int id;
 	bool enabled;
-	enum sde_lm mixer;
-	enum sde_ctl ctl_path;
+
+	spinlock_t lm_lock;	/* protect registers */
+
+	/* HW Resources reserved for the crtc */
+	u32  num_ctls;
+	u32  num_mixers;
+	struct sde_crtc_mixer mixer[CRTC_DUAL_MIXERS];
+
+	/*if there is a pending flip, these will be non-null */
+	struct drm_pending_vblank_event *event;
 };
 
 #define to_sde_crtc(x) container_of(x, struct sde_crtc, base)
 
+static struct sde_kms *get_kms(struct drm_crtc *crtc)
+{
+	struct msm_drm_private *priv = crtc->dev->dev_private;
+
+	return to_sde_kms(to_mdp_kms(priv->kms));
+}
+
+static inline struct sde_hw_ctl *sde_crtc_rm_get_ctl_path(enum sde_ctl idx,
+		void __iomem *addr,
+		struct sde_mdss_cfg *m)
+{
+	/*
+	 * This module keeps track of the requested hw resources state,
+	 * if the requested resource is being used it returns NULL,
+	 * otherwise it returns the hw driver struct
+	 */
+	return sde_hw_ctl_init(idx, addr, m);
+}
+
+static inline struct sde_hw_mixer *sde_crtc_rm_get_mixer(enum sde_lm idx,
+		void __iomem *addr,
+		struct sde_mdss_cfg *m)
+{
+	/*
+	 * This module keeps track of the requested hw resources state,
+	 * if the requested resource is being used it returns NULL,
+	 * otherwise it returns the hw driver struct
+	 */
+	return sde_hw_lm_init(idx, addr, m);
+}
+
+static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc,
+		struct drm_encoder *encoder)
+{
+	/*
+	 * Assign CRTC resources
+	 * num_ctls;
+	 * num_mixers;
+	 * sde_lm mixer[CRTC_MAX_PIPES];
+	 * sde_ctl ctl[CRTC_MAX_PIPES];
+	 */
+	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
+	struct sde_kms *kms = get_kms(crtc);
+	enum sde_lm lm_id[CRTC_DUAL_MIXERS];
+	enum sde_ctl ctl_id[CRTC_DUAL_MIXERS];
+	int i;
+
+	if (!kms) {
+		DBG("[%s] invalid kms\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!kms->mmio)
+		return -EINVAL;
+
+	/*
+	 * simple check validate against catalog
+	 */
+	sde_crtc->num_ctls = 1;
+	sde_crtc->num_mixers = 1;
+	ctl_id[0] = CTL_0;
+	lm_id[0] = LM_0;
+
+	/*
+	 * need to also enable MDP core clock and AHB CLK
+	 * before touching HW driver
+	 */
+	DBG("%s Enable clocks\n", __func__);
+	sde_enable(kms);
+	for (i = 0; i < sde_crtc->num_ctls; i++) {
+		sde_crtc->mixer[i].hw_ctl = sde_crtc_rm_get_ctl_path(ctl_id[i],
+				kms->mmio, kms->catalog);
+		if (!sde_crtc->mixer[i].hw_ctl) {
+			DBG("[%s], Invalid ctl_path", __func__);
+			return -EACCES;
+		}
+	}
+
+	for (i = 0; i < sde_crtc->num_mixers; i++) {
+		sde_crtc->mixer[i].hw_lm = sde_crtc_rm_get_mixer(lm_id[i],
+				kms->mmio, kms->catalog);
+		if (!sde_crtc->mixer[i].hw_lm) {
+			DBG("[%s], Invalid ctl_path", __func__);
+			return -EACCES;
+		}
+	}
+	/*
+	 * need to disable MDP core clock and AHB CLK
+	 */
+	sde_disable(kms);
+	return 0;
+}
+
 static void sde_crtc_destroy(struct drm_crtc *crtc)
 {
 	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
 
+	DBG("");
 	drm_crtc_cleanup(crtc);
 	kfree(sde_crtc);
 }
 
-static void sde_crtc_dpms(struct drm_crtc *crtc, int mode)
-{
-}
-
 static bool sde_crtc_mode_fixup(struct drm_crtc *crtc,
 		const struct drm_display_mode *mode,
 		struct drm_display_mode *adjusted_mode)
 {
+	DBG("");
 	return true;
 }
 
-static int sde_crtc_mode_set(struct drm_crtc *crtc,
-		struct drm_display_mode *mode,
-		struct drm_display_mode *adjusted_mode,
-		int x, int y,
-		struct drm_framebuffer *old_fb)
+static void sde_crtc_mode_set_nofb(struct drm_crtc *crtc)
 {
+	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
+	struct sde_crtc_mixer *mixer = sde_crtc->mixer;
+	struct drm_device *dev = crtc->dev;
+	struct sde_hw_mixer *lm;
+	unsigned long flags;
+	struct drm_display_mode *mode;
+	struct sde_hw_mixer_cfg cfg;
+	u32 mixer_width;
+	int i;
+	int rc;
+
+	DBG("");
+	if (WARN_ON(!crtc->state))
+		return;
+
+	mode = &crtc->state->adjusted_mode;
+
+	DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
+			sde_crtc->name, mode->base.id, mode->name,
+			mode->vrefresh, mode->clock,
+			mode->hdisplay, mode->hsync_start,
+			mode->hsync_end, mode->htotal,
+			mode->vdisplay, mode->vsync_start,
+			mode->vsync_end, mode->vtotal,
+			mode->type, mode->flags);
+
+	/*
+	 * reserve mixer(s) if not already avaialable
+	 * if dual mode, mixer_width = half mode width
+	 * program mode configuration on mixer(s)
+	 */
+	if ((sde_crtc->num_ctls == 0) ||
+		(sde_crtc->num_mixers == 0)) {
+		rc = sde_crtc_reserve_hw_resources(crtc, sde_crtc->encoder);
+		if (rc) {
+			dev_err(dev->dev, " error reserving HW resource for this CRTC\n");
+			return;
+		}
+	}
+
+	if (sde_crtc->num_mixers == CRTC_DUAL_MIXERS)
+		mixer_width = mode->hdisplay >> 1;
+	else
+		mixer_width = mode->hdisplay;
+
+	spin_lock_irqsave(&sde_crtc->lm_lock, flags);
+
+	for (i = 0; i < sde_crtc->num_mixers; i++) {
+		lm = mixer[i].hw_lm;
+		cfg.out_width = mixer_width;
+		cfg.out_height = mode->vdisplay;
+		cfg.right_mixer = (i == 0) ? false : true;
+		cfg.flags = 0;
+		lm->ops.setup_mixer_out(lm, &cfg);
+	}
+
+	spin_unlock_irqrestore(&sde_crtc->lm_lock, flags);
+}
+
+static void sde_crtc_get_blend_cfg(struct sde_hw_blend_cfg *cfg,
+		struct sde_plane_state *pstate)
+{
+	const struct mdp_format *format;
+	struct drm_plane *plane;
+
+	format = to_mdp_format(
+			msm_framebuffer_format(pstate->base.fb));
+	plane = pstate->base.plane;
+
+	cfg->fg.alpha_sel = ALPHA_FG_CONST;
+	cfg->bg.alpha_sel = ALPHA_BG_CONST;
+	cfg->fg.const_alpha = pstate->alpha;
+	cfg->bg.const_alpha = 0xFF - pstate->alpha;
+
+	if (format->alpha_enable && pstate->premultiplied) {
+		cfg->fg.alpha_sel = ALPHA_FG_CONST;
+		cfg->bg.alpha_sel = ALPHA_FG_PIXEL;
+		if (pstate->alpha != 0xff) {
+			cfg->bg.const_alpha = pstate->alpha;
+			cfg->bg.inv_alpha_sel = 1;
+			cfg->bg.mod_alpha = 1;
+		} else {
+			cfg->bg.inv_mode_alpha = 1;
+		}
+	} else if (format->alpha_enable) {
+		cfg->fg.alpha_sel = ALPHA_FG_PIXEL;
+		cfg->bg.alpha_sel = ALPHA_FG_PIXEL;
+		if (pstate->alpha != 0xff) {
+			cfg->bg.const_alpha = pstate->alpha;
+			cfg->fg.mod_alpha = 1;
+			cfg->bg.inv_alpha_sel = 1;
+			cfg->bg.mod_alpha = 1;
+			cfg->bg.inv_mode_alpha = 1;
+		} else {
+			cfg->bg.inv_mode_alpha = 1;
+		}
+	}
+}
+
+static void blend_setup(struct drm_crtc *crtc)
+{
+	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
+	struct sde_crtc_mixer *mixer = sde_crtc->mixer;
+	struct drm_plane *plane;
+	struct sde_plane_state *pstate, *pstates[SDE_STAGE_MAX] = {0};
+	struct sde_hw_stage_cfg stage_cfg;
+	struct sde_hw_blend_cfg blend;
+	struct sde_hw_ctl *ctl;
+	struct sde_hw_mixer *lm;
+	u32 flush_mask = 0;
+	unsigned long flags;
+	int i, j, plane_cnt = 0;
+
+	spin_lock_irqsave(&sde_crtc->lm_lock, flags);
+
+	/* ctl could be reserved already */
+	if (!sde_crtc->num_ctls)
+		goto out;
+
+	/* initialize stage cfg */
+	memset(&stage_cfg, 0, sizeof(stage_cfg));
+	memset(&blend, 0, sizeof(blend));
+
+	/* Collect all plane information */
+	drm_atomic_crtc_for_each_plane(plane, crtc) {
+		pstate = to_sde_plane_state(plane->state);
+		pstates[pstate->stage] = pstate;
+		plane_cnt++;
+		for (i = 0; i < sde_crtc->num_mixers; i++) {
+			stage_cfg.stage[pstate->stage][i] =
+				sde_plane_pipe(plane);
+
+			/* Cache the flushmask for this layer
+			 * sourcesplit is always enabled, so this layer will
+			 * be staged on both the mixers
+			 */
+			ctl = mixer[i].hw_ctl;
+			ctl->ops.get_bitmask_sspp(ctl, &flush_mask,
+					sde_plane_pipe(plane));
+		}
+	}
+
+	/*
+	 * If there is no base layer, enable border color.
+	 * currently border color is always black
+	 */
+	if ((stage_cfg.stage[SDE_STAGE_BASE][0] == SSPP_NONE) &&
+		plane_cnt) {
+		stage_cfg.border_enable = 1;
+		DBG("Border Color is enabled\n");
+	}
+
+	/* Program hw */
+	for (i = 0; i < sde_crtc->num_mixers; i++) {
+		if (!mixer[i].hw_lm)
+			continue;
+
+		if (!mixer[i].hw_ctl)
+			continue;
+
+		ctl = mixer[i].hw_ctl;
+		lm = mixer[i].hw_lm;
+
+		/* stage config */
+		ctl->ops.setup_blendstage(ctl, mixer[i].hw_lm->idx,
+			&stage_cfg);
+		/* stage config flush mask */
+		mixer[i].flush_mask = flush_mask;
+		/* get the flush mask for mixer */
+		ctl->ops.get_bitmask_mixer(ctl, &mixer[i].flush_mask,
+			mixer[i].hw_lm->idx);
+
+		/* blend config */
+		for (j = SDE_STAGE_0; j < SDE_STAGE_MAX; j++) {
+			if (!pstates[j])
+				continue;
+			sde_crtc_get_blend_cfg(&blend, pstates[j]);
+			blend.fg.alpha_sel = ALPHA_FG_CONST;
+			blend.bg.alpha_sel = ALPHA_BG_CONST;
+			blend.fg.const_alpha = pstate->alpha;
+			blend.bg.const_alpha = 0xFF - pstate->alpha;
+			lm->ops.setup_blend_config(lm, j, &blend);
+		}
+	}
+out:
+	spin_unlock_irqrestore(&sde_crtc->lm_lock, flags);
+}
+
+static void request_pending(struct drm_crtc *crtc, u32 pending)
+{
+	DBG("");
+}
+/**
+ * Flush the CTL PATH
+ */
+static u32 crtc_flush_all(struct drm_crtc *crtc)
+{
+	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
+	struct sde_hw_ctl *ctl;
+	int i;
+
+	DBG("");
+
+	for (i = 0; i < sde_crtc->num_ctls; i++) {
+		/*
+		 * Query flush_mask from encoder
+		 * and append to the ctl_path flush_mask
+		 */
+		ctl = sde_crtc->mixer[i].hw_ctl;
+		ctl->ops.get_bitmask_intf(ctl,
+				&(sde_crtc->mixer[i].flush_mask),
+				INTF_1);
+		ctl->ops.setup_flush(ctl,
+				sde_crtc->mixer[i].flush_mask);
+	}
+
 	return 0;
 }
 
-static void sde_crtc_prepare(struct drm_crtc *crtc)
+static void sde_crtc_atomic_begin(struct drm_crtc *crtc,
+		struct drm_crtc_state *old_crtc_state)
 {
+	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
+	struct drm_device *dev = crtc->dev;
+	unsigned long flags;
+
+	DBG("");
+
+	WARN_ON(sde_crtc->event);
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	sde_crtc->event = crtc->state->event;
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	/*
+	 * If no CTL has been allocated in sde_crtc_atomic_check(),
+	 * it means we are trying to flush a CRTC whose state is disabled:
+	 * nothing else needs to be done.
+	 */
+	if (unlikely(!sde_crtc->num_ctls))
+		return;
+
+	blend_setup(crtc);
+
+	/*
+	 * PP_DONE irq is only used by command mode for now.
+	 * It is better to request pending before FLUSH and START trigger
+	 * to make sure no pp_done irq missed.
+	 * This is safe because no pp_done will happen before SW trigger
+	 * in command mode.
+	 */
 }
 
-static void sde_crtc_commit(struct drm_crtc *crtc)
+static void sde_crtc_atomic_flush(struct drm_crtc *crtc,
+		struct drm_crtc_state *old_crtc_state)
 {
-}
+	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
+	struct drm_device *dev = crtc->dev;
+	unsigned long flags;
 
-static int sde_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
-		struct drm_framebuffer *old_fb)
-{
-	return 0;
-}
+	DBG("");
 
-static void sde_crtc_load_lut(struct drm_crtc *crtc)
-{
-}
+	WARN_ON(sde_crtc->event);
 
-static int sde_crtc_page_flip(struct drm_crtc *crtc,
-		struct drm_framebuffer *new_fb,
-		struct drm_pending_vblank_event *event,
-		uint32_t page_flip_flags)
-{
-	return 0;
+	spin_lock_irqsave(&dev->event_lock, flags);
+	sde_crtc->event = crtc->state->event;
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	/*
+	 * If no CTL has been allocated in sde_crtc_atomic_check(),
+	 * it means we are trying to flush a CRTC whose state is disabled:
+	 * nothing else needs to be done.
+	 */
+	if (unlikely(!sde_crtc->num_ctls))
+		return;
+
+	crtc_flush_all(crtc);
+
+	request_pending(crtc, PENDING_FLIP);
 }
 
 static int sde_crtc_set_property(struct drm_crtc *crtc,
@@ -92,21 +453,111 @@
 	return -EINVAL;
 }
 
+static int sde_crtc_cursor_set(struct drm_crtc *crtc,
+		struct drm_file *file, uint32_t handle,
+		uint32_t width, uint32_t height)
+{
+	return 0;
+}
+
+static int sde_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+	return 0;
+}
+
+static void sde_crtc_disable(struct drm_crtc *crtc)
+{
+	DBG("");
+}
+
+static void sde_crtc_enable(struct drm_crtc *crtc)
+{
+	DBG("");
+}
+
+struct plane_state {
+	struct drm_plane *plane;
+	struct sde_plane_state *state;
+};
+
+static int pstate_cmp(const void *a, const void *b)
+{
+	struct plane_state *pa = (struct plane_state *)a;
+	struct plane_state *pb = (struct plane_state *)b;
+
+	return pa->state->zpos - pb->state->zpos;
+}
+
+static int sde_crtc_atomic_check(struct drm_crtc *crtc,
+		struct drm_crtc_state *state)
+{
+	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
+	struct sde_kms *sde_kms = get_kms(crtc);
+	struct drm_plane *plane;
+	struct drm_device *dev = crtc->dev;
+	struct plane_state pstates[SDE_STAGE_MAX];
+	int max_stages = CRTC_HW_MIXER_MAXSTAGES(sde_kms->catalog, 0);
+	int cnt = 0, i;
+
+	DBG("%s: check", sde_crtc->name);
+
+	/* verify that there are not too many planes attached to crtc
+	 * and that we don't have conflicting mixer stages:
+	 */
+	drm_atomic_crtc_state_for_each_plane(plane, state) {
+		struct drm_plane_state *pstate;
+
+		if (cnt >= (max_stages)) {
+			dev_err(dev->dev, "too many planes!\n");
+			return -EINVAL;
+		}
+
+		pstate = state->state->plane_states[drm_plane_index(plane)];
+
+		/* plane might not have changed, in which case take
+		 * current state:
+		 */
+		if (!pstate)
+			pstate = plane->state;
+		pstates[cnt].plane = plane;
+		pstates[cnt].state = to_sde_plane_state(pstate);
+
+		cnt++;
+	}
+
+	/* assign a stage based on sorted zpos property */
+	sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL);
+
+	for (i = 0; i < cnt; i++) {
+		pstates[i].state->stage = SDE_STAGE_0 + i;
+		DBG("%s: assign pipe %d on stage=%d", sde_crtc->name,
+				sde_plane_pipe(pstates[i].plane),
+				pstates[i].state->stage);
+	}
+
+	return 0;
+}
+
 static const struct drm_crtc_funcs sde_crtc_funcs = {
-	.set_config = drm_crtc_helper_set_config,
+	.set_config = drm_atomic_helper_set_config,
 	.destroy = sde_crtc_destroy,
-	.page_flip = sde_crtc_page_flip,
+	.page_flip = drm_atomic_helper_page_flip,
 	.set_property = sde_crtc_set_property,
+	.reset = drm_atomic_helper_crtc_reset,
+	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+	.cursor_set = sde_crtc_cursor_set,
+	.cursor_move = sde_crtc_cursor_move,
 };
 
 static const struct drm_crtc_helper_funcs sde_crtc_helper_funcs = {
-	.dpms = sde_crtc_dpms,
 	.mode_fixup = sde_crtc_mode_fixup,
-	.mode_set = sde_crtc_mode_set,
-	.prepare = sde_crtc_prepare,
-	.commit = sde_crtc_commit,
-	.mode_set_base = sde_crtc_mode_set_base,
-	.load_lut = sde_crtc_load_lut,
+	.mode_set_nofb = sde_crtc_mode_set_nofb,
+	.disable = sde_crtc_disable,
+	.enable = sde_crtc_enable,
+	.atomic_check = sde_crtc_atomic_check,
+	.atomic_begin = sde_crtc_atomic_begin,
+	.atomic_flush = sde_crtc_atomic_flush,
 };
 
 uint32_t sde_crtc_vblank(struct drm_crtc *crtc)
@@ -118,20 +569,20 @@
 {
 }
 
-void sde_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane)
+static void sde_crtc_install_properties(struct drm_crtc *crtc,
+	struct drm_mode_object *obj)
 {
 }
 
-void sde_crtc_detach(struct drm_crtc *crtc, struct drm_plane *plane)
-{
-}
 
+/* initialize crtc */
 struct drm_crtc *sde_crtc_init(struct drm_device *dev,
 		struct drm_encoder *encoder,
 		struct drm_plane *plane, int id)
 {
 	struct drm_crtc *crtc = NULL;
 	struct sde_crtc *sde_crtc;
+	int rc;
 
 	sde_crtc = kzalloc(sizeof(*sde_crtc), GFP_KERNEL);
 	if (!sde_crtc)
@@ -140,9 +591,21 @@
 	crtc = &sde_crtc->base;
 
 	sde_crtc->id = id;
+	sde_crtc->encoder = encoder;
 
-	/* find out if we need one or two lms */
+	sde_crtc_install_properties(crtc, &crtc->base);
+
+	drm_crtc_init_with_planes(dev, crtc, plane, NULL, &sde_crtc_funcs);
 
 	drm_crtc_helper_add(crtc, &sde_crtc_helper_funcs);
+	plane->crtc = crtc;
+
+	rc = sde_crtc_reserve_hw_resources(crtc, encoder);
+	if (rc) {
+		dev_err(dev->dev, " error reserving HW resource for this CRTC\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	DBG("%s: Successfully initialized crtc\n", __func__);
 	return crtc;
 }
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 3c28e31..283d33b 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -14,82 +14,566 @@
 #include "drm_crtc.h"
 #include "drm_crtc_helper.h"
 
+#include "sde_hwio.h"
+#include "sde_hw_catalog.h"
+#include "sde_hw_intf.h"
+#include "sde_hw_mdp_ctl.h"
+#include "sde_mdp_formats.h"
+
+#include "../dsi-staging/dsi_display.h"
+
+#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
+
 struct sde_encoder {
 	struct drm_encoder base;
-	int intf;
+	spinlock_t intf_lock;
+	bool enabled;
+	uint32_t bus_scaling_client;
+	struct sde_hw_intf *hw_intf;
+	struct sde_hw_ctl *hw_ctl;
+	int drm_mode_enc;
+
+	void (*vblank_callback)(void *);
+	void *vblank_callback_data;
+
+	struct mdp_irq vblank_irq;
 };
 #define to_sde_encoder(x) container_of(x, struct sde_encoder, base)
 
-static void sde_encoder_destroy(struct drm_encoder *encoder)
+static struct sde_kms *get_kms(struct drm_encoder *drm_enc)
 {
-	struct sde_encoder *sde_encoder = to_sde_encoder(encoder);
+	struct msm_drm_private *priv = drm_enc->dev->dev_private;
 
-	drm_encoder_cleanup(encoder);
-	kfree(sde_encoder);
+	return to_sde_kms(to_mdp_kms(priv->kms));
 }
 
-static const struct drm_encoder_funcs sde_encoder_funcs = {
-	.destroy = sde_encoder_destroy,
+#ifdef CONFIG_QCOM_BUS_SCALING
+#include <linux/msm-bus.h>
+#include <linux/msm-bus-board.h>
+#define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val)		\
+	{						\
+		.src = MSM_BUS_MASTER_MDP_PORT0,	\
+		.dst = MSM_BUS_SLAVE_EBI_CH0,		\
+		.ab = (ab_val),				\
+		.ib = (ib_val),				\
+	}
+
+static struct msm_bus_vectors mdp_bus_vectors[] = {
+	MDP_BUS_VECTOR_ENTRY(0, 0),
+	MDP_BUS_VECTOR_ENTRY(2000000000, 2000000000),
+};
+static struct msm_bus_paths mdp_bus_usecases[] = { {
+						    .num_paths = 1,
+						    .vectors =
+						    &mdp_bus_vectors[0],
+						    }, {
+							.num_paths = 1,
+							.vectors =
+							&mdp_bus_vectors[1],
+							}
 };
 
-static void sde_encoder_dpms(struct drm_encoder *encoder, int mode)
+static struct msm_bus_scale_pdata mdp_bus_scale_table = {
+	.usecase = mdp_bus_usecases,
+	.num_usecases = ARRAY_SIZE(mdp_bus_usecases),
+	.name = "mdss_mdp",
+};
+
+static void bs_init(struct sde_encoder *sde_enc)
+{
+	sde_enc->bus_scaling_client =
+	    msm_bus_scale_register_client(&mdp_bus_scale_table);
+	DBG("bus scale client: %08x", sde_enc->bus_scaling_client);
+}
+
+static void bs_fini(struct sde_encoder *sde_enc)
+{
+	if (sde_enc->bus_scaling_client) {
+		msm_bus_scale_unregister_client(sde_enc->bus_scaling_client);
+		sde_enc->bus_scaling_client = 0;
+	}
+}
+
+static void bs_set(struct sde_encoder *sde_enc, int idx)
+{
+	if (sde_enc->bus_scaling_client) {
+		DBG("set bus scaling: %d", idx);
+		idx = 1;
+		msm_bus_scale_client_update_request(sde_enc->bus_scaling_client,
+						    idx);
+	}
+}
+#else
+static void bs_init(struct sde_encoder *sde_enc)
 {
 }
 
-static bool sde_encoder_mode_fixup(struct drm_encoder *encoder,
-		const struct drm_display_mode *mode,
-		struct drm_display_mode *adjusted_mode)
+static void bs_fini(struct sde_encoder *sde_enc)
 {
+}
+
+static void bs_set(struct sde_encoder *sde_enc, int idx)
+{
+}
+#endif
+
+static bool sde_encoder_mode_fixup(struct drm_encoder *drm_enc,
+				   const struct drm_display_mode *mode,
+				   struct drm_display_mode *adjusted_mode)
+{
+	DBG("");
 	return true;
 }
 
-static void sde_encoder_mode_set(struct drm_encoder *encoder,
-		struct drm_display_mode *mode,
-		struct drm_display_mode *adjusted_mode)
+static void sde_encoder_mode_set(struct drm_encoder *drm_enc,
+				 struct drm_display_mode *mode,
+				 struct drm_display_mode *adjusted_mode)
 {
+
+	struct sde_encoder *sde_enc = to_sde_encoder(drm_enc);
+	struct intf_timing_params p = {0};
+	uint32_t hsync_polarity = 0, vsync_polarity = 0;
+	struct sde_mdp_format_params *sde_fmt_params = NULL;
+	u32 fmt_fourcc = DRM_FORMAT_RGB888, fmt_mod = 0;
+	unsigned long lock_flags;
+	struct sde_hw_intf_cfg intf_cfg = {0};
+
+	mode = adjusted_mode;
+
+	DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
+	    mode->base.id, mode->name, mode->vrefresh, mode->clock,
+	    mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal,
+	    mode->vdisplay, mode->vsync_start, mode->vsync_end, mode->vtotal,
+	    mode->type, mode->flags);
+
+	/* DSI controller cannot handle active-low sync signals. */
+	if (sde_enc->hw_intf->cap->type != INTF_DSI) {
+		if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+			hsync_polarity = 1;
+		if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+			vsync_polarity = 1;
+	}
+
+	/*
+	 * For edp only:
+	 * DISPLAY_V_START = (VBP * HCYCLE) + HBP
+	 * DISPLAY_V_END = (VBP + VACTIVE) * HCYCLE - 1 - HFP
+	 */
+	/*
+	 * if (sde_enc->hw->cap->type == INTF_EDP) {
+	 *	display_v_start += mode->htotal - mode->hsync_start;
+	 *	display_v_end -= mode->hsync_start - mode->hdisplay;
+	 * }
+	 */
+
+	/*
+	 * https://www.kernel.org/doc/htmldocs/drm/ch02s05.html
+	 *  Active Region            Front Porch       Sync       Back Porch
+	 * <---------------------><----------------><---------><-------------->
+	 * <--- [hv]display ----->
+	 * <----------- [hv]sync_start ------------>
+	 * <------------------- [hv]sync_end ----------------->
+	 * <------------------------------ [hv]total ------------------------->
+	 */
+
+	sde_fmt_params = sde_mdp_get_format_params(fmt_fourcc, fmt_mod);
+
+	p.width = mode->hdisplay;	/* active width */
+	p.height = mode->vdisplay;	/* active height */
+	p.xres = p.width;	/* Display panel width */
+	p.yres = p.height;	/* Display panel height */
+	p.h_back_porch = mode->htotal - mode->hsync_end;
+	p.h_front_porch = mode->hsync_start - mode->hdisplay;
+	p.v_back_porch = mode->vtotal - mode->vsync_end;
+	p.v_front_porch = mode->vsync_start - mode->vdisplay;
+	p.hsync_pulse_width = mode->hsync_end - mode->hsync_start;
+	p.vsync_pulse_width = mode->vsync_end - mode->vsync_start;
+	p.hsync_polarity = hsync_polarity;
+	p.vsync_polarity = vsync_polarity;
+	p.border_clr = 0;
+	p.underflow_clr = 0xff;
+	p.hsync_skew = mode->hskew;
+
+	intf_cfg.intf = sde_enc->hw_intf->idx;
+	intf_cfg.wb = SDE_NONE;
+
+	spin_lock_irqsave(&sde_enc->intf_lock, lock_flags);
+	sde_enc->hw_intf->ops.setup_timing_gen(sde_enc->hw_intf, &p,
+					       sde_fmt_params);
+	sde_enc->hw_ctl->ops.setup_intf_cfg(sde_enc->hw_ctl, &intf_cfg);
+	spin_unlock_irqrestore(&sde_enc->intf_lock, lock_flags);
 }
 
-static void sde_encoder_prepare(struct drm_encoder *encoder)
+static void sde_encoder_wait_for_vblank(struct sde_encoder *sde_enc)
 {
+	struct sde_kms *sde_kms = get_kms(&sde_enc->base);
+	struct mdp_kms *mdp_kms = &sde_kms->base;
+
+	DBG("");
+	mdp_irq_wait(mdp_kms, sde_enc->vblank_irq.irqmask);
 }
 
-static void sde_encoder_commit(struct drm_encoder *encoder)
+static void sde_encoder_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus)
 {
+	struct sde_encoder *sde_enc = container_of(irq, struct sde_encoder,
+						   vblank_irq);
+	struct intf_status status = { 0 };
+	unsigned long lock_flags;
+
+	spin_lock_irqsave(&sde_enc->intf_lock, lock_flags);
+	if (sde_enc->vblank_callback)
+		sde_enc->vblank_callback(sde_enc->vblank_callback_data);
+	spin_unlock_irqrestore(&sde_enc->intf_lock, lock_flags);
+
+	sde_enc->hw_intf->ops.get_status(sde_enc->hw_intf, &status);
+}
+
+static void sde_encoder_disable(struct drm_encoder *drm_enc)
+{
+	struct sde_encoder *sde_enc = to_sde_encoder(drm_enc);
+	struct sde_kms *sde_kms = get_kms(drm_enc);
+	struct mdp_kms *mdp_kms = &(sde_kms->base);
+	unsigned long lock_flags;
+
+	DBG("");
+
+	if (WARN_ON(!sde_enc->enabled))
+		return;
+
+	spin_lock_irqsave(&sde_enc->intf_lock, lock_flags);
+	sde_enc->hw_intf->ops.enable_timing(sde_enc->hw_intf, 0);
+	spin_unlock_irqrestore(&sde_enc->intf_lock, lock_flags);
+
+	/*
+	 * Wait for a vsync so we know the ENABLE=0 latched before
+	 * the (connector) source of the vsync's gets disabled,
+	 * otherwise we end up in a funny state if we re-enable
+	 * before the disable latches, which results that some of
+	 * the settings changes for the new modeset (like new
+	 * scanout buffer) don't latch properly..
+	 */
+	sde_encoder_wait_for_vblank(sde_enc);
+
+	mdp_irq_unregister(mdp_kms, &sde_enc->vblank_irq);
+	bs_set(sde_enc, 0);
+	sde_enc->enabled = false;
+}
+
+static void sde_encoder_enable(struct drm_encoder *drm_enc)
+{
+	struct sde_encoder *sde_enc = to_sde_encoder(drm_enc);
+	struct mdp_kms *mdp_kms = &(get_kms(drm_enc)->base);
+	unsigned long lock_flags;
+
+	DBG("");
+
+	if (WARN_ON(sde_enc->enabled))
+		return;
+
+	bs_set(sde_enc, 1);
+	spin_lock_irqsave(&sde_enc->intf_lock, lock_flags);
+	sde_enc->hw_intf->ops.enable_timing(sde_enc->hw_intf, 1);
+	spin_unlock_irqrestore(&sde_enc->intf_lock, lock_flags);
+	sde_enc->enabled = true;
+
+	mdp_irq_register(mdp_kms, &sde_enc->vblank_irq);
+	DBG("Registered IRQ for intf %d mask 0x%X", sde_enc->hw_intf->idx,
+	    sde_enc->vblank_irq.irqmask);
+}
+
+void sde_encoder_get_hw_resources(struct drm_encoder *drm_enc,
+				  struct sde_encoder_hw_resources *hw_res)
+{
+	struct sde_encoder *sde_enc = to_sde_encoder(drm_enc);
+
+	DBG("");
+
+	if (WARN_ON(!hw_res))
+		return;
+
+	memset(hw_res, 0, sizeof(*hw_res));
+	hw_res->intfs[sde_enc->hw_intf->idx] = true;
+}
+
+static void sde_encoder_destroy(struct drm_encoder *drm_enc)
+{
+	struct sde_encoder *sde_enc = to_sde_encoder(drm_enc);
+
+	DBG("");
+	drm_encoder_cleanup(drm_enc);
+	bs_fini(sde_enc);
+	kfree(sde_enc->hw_intf);
+	kfree(sde_enc);
 }
 
 static const struct drm_encoder_helper_funcs sde_encoder_helper_funcs = {
-	.dpms = sde_encoder_dpms,
 	.mode_fixup = sde_encoder_mode_fixup,
 	.mode_set = sde_encoder_mode_set,
-	.prepare = sde_encoder_prepare,
-	.commit = sde_encoder_commit,
+	.disable = sde_encoder_disable,
+	.enable = sde_encoder_enable,
 };
 
-/* initialize encoder */
-struct drm_encoder *sde_encoder_init(struct drm_device *dev, int intf)
-{
-	struct drm_encoder *encoder = NULL;
-	struct sde_encoder *sde_encoder;
-	int ret;
+static const struct drm_encoder_funcs sde_encoder_funcs = {.destroy =
+	    sde_encoder_destroy,
+};
 
-	sde_encoder = kzalloc(sizeof(*sde_encoder), GFP_KERNEL);
-	if (!sde_encoder) {
+static int sde_encoder_setup_hw(struct sde_encoder *sde_enc,
+				struct sde_kms *sde_kms,
+				enum sde_intf intf_idx,
+				enum sde_ctl ctl_idx)
+{
+	int ret = 0;
+
+	DBG("");
+
+	sde_enc->hw_intf = sde_hw_intf_init(intf_idx, sde_kms->mmio,
+					    sde_kms->catalog);
+	if (!sde_enc->hw_intf)
+		return -EINVAL;
+
+	sde_enc->hw_ctl = sde_hw_ctl_init(ctl_idx, sde_kms->mmio,
+					    sde_kms->catalog);
+	if (!sde_enc->hw_ctl)
+		return -EINVAL;
+
+	return ret;
+}
+
+static int sde_encoder_virt_add_phys_vid_enc(struct sde_encoder *sde_enc,
+					     struct sde_kms *sde_kms,
+					     enum sde_intf intf_idx,
+					     enum sde_ctl ctl_idx)
+{
+	int ret = 0;
+
+	DBG("");
+
+	ret = sde_encoder_setup_hw(sde_enc, sde_kms, intf_idx, ctl_idx);
+	if (!ret) {
+		sde_enc->vblank_irq.irq = sde_encoder_vblank_irq;
+		sde_enc->vblank_irq.irqmask = 0x8000000;
+	}
+	return ret;
+}
+
+static int sde_encoder_setup_hdmi(struct sde_encoder *sde_enc,
+				  struct sde_kms *sde_kms, int *hdmi_info)
+{
+	int ret = 0;
+	enum sde_intf intf_idx = INTF_MAX;
+
+	DBG("");
+
+	sde_enc->drm_mode_enc = DRM_MODE_ENCODER_TMDS;
+
+	intf_idx = INTF_3;
+	if (intf_idx == INTF_MAX)
+		ret = -EINVAL;
+
+	if (!ret)
+		ret =
+		    sde_encoder_virt_add_phys_vid_enc(sde_enc, sde_kms,
+						      intf_idx,
+						      CTL_2);
+
+	return ret;
+}
+
+static int sde_encoder_setup_dsi(struct sde_encoder *sde_enc,
+				 struct sde_kms *sde_kms,
+				 struct dsi_display_info *dsi_info)
+{
+	int ret = 0;
+	int i = 0;
+
+	DBG("");
+
+	sde_enc->drm_mode_enc = DRM_MODE_ENCODER_DSI;
+
+	if (WARN_ON(dsi_info->num_of_h_tiles > 1)) {
+		DBG("Dual DSI mode not yet supported");
+		ret = -EINVAL;
+	}
+
+	WARN_ON(dsi_info->num_of_h_tiles != 1);
+	dsi_info->num_of_h_tiles = 1;
+
+	DBG("dsi_info->num_of_h_tiles %d h_tiled %d dsi_info->h_tile_ids %d ",
+			dsi_info->num_of_h_tiles, dsi_info->h_tiled,
+			dsi_info->h_tile_ids[0]);
+
+	for (i = 0; i < !ret && dsi_info->num_of_h_tiles; i++) {
+		enum sde_intf intf_idx = INTF_1;
+		enum sde_ctl ctl_idx = CTL_0;
+
+		if (intf_idx == INTF_MAX) {
+			DBG("Error: could not get the interface id");
+			ret = -EINVAL;
+		}
+
+		/*  Get DSI modes, create both VID & CMD Phys Encoders */
+		if (!ret)
+			ret =
+			    sde_encoder_virt_add_phys_vid_enc(sde_enc, sde_kms,
+							      intf_idx,
+							      ctl_idx);
+	}
+
+	return ret;
+}
+
+struct display_probe_info {
+	enum sde_intf_type type;
+	struct dsi_display_info dsi_info;
+	int hdmi_info;
+};
+
+static struct drm_encoder *sde_encoder_virt_init(struct drm_device *dev,
+		struct display_probe_info
+		*display)
+{
+	struct msm_drm_private *priv = dev->dev_private;
+	struct sde_kms *sde_kms = to_sde_kms(to_mdp_kms(priv->kms));
+	struct drm_encoder *drm_enc = NULL;
+	struct sde_encoder *sde_enc = NULL;
+	int ret = 0;
+
+	DBG("");
+
+	sde_enc = kzalloc(sizeof(*sde_enc), GFP_KERNEL);
+	if (!sde_enc) {
 		ret = -ENOMEM;
 		goto fail;
 	}
 
-	sde_encoder->intf = intf;
-	encoder = &sde_encoder->base;
+	if (display->type == INTF_DSI) {
+		ret =
+		    sde_encoder_setup_dsi(sde_enc, sde_kms, &display->dsi_info);
+	} else if (display->type == INTF_HDMI) {
+		ret =
+		    sde_encoder_setup_hdmi(sde_enc, sde_kms,
+					   &display->hdmi_info);
+	} else {
+		DBG("No valid displays found");
+		ret = -EINVAL;
+	}
+	if (ret)
+		goto fail;
 
-	drm_encoder_init(dev, encoder, &sde_encoder_funcs,
-			 DRM_MODE_ENCODER_TMDS);
-	drm_encoder_helper_add(encoder, &sde_encoder_helper_funcs);
+	spin_lock_init(&sde_enc->intf_lock);
+	drm_enc = &sde_enc->base;
+	drm_encoder_init(dev, drm_enc, &sde_encoder_funcs,
+			 sde_enc->drm_mode_enc);
+	drm_encoder_helper_add(drm_enc, &sde_encoder_helper_funcs);
+	bs_init(sde_enc);
 
-	return encoder;
+	DBG("Created sde_encoder for intf %d", sde_enc->hw_intf->idx);
+
+	return drm_enc;
 
 fail:
-	if (encoder)
-		sde_encoder_destroy(encoder);
+	if (drm_enc)
+		sde_encoder_destroy(drm_enc);
 
 	return ERR_PTR(ret);
 }
+
+static int sde_encoder_probe_hdmi(struct drm_device *dev)
+{
+	struct msm_drm_private *priv = dev->dev_private;
+	struct drm_encoder *enc = NULL;
+	struct display_probe_info probe_info = { 0 };
+	int ret = 0;
+
+	DBG("");
+
+	probe_info.type = INTF_HDMI;
+
+	enc = sde_encoder_virt_init(dev, &probe_info);
+	if (IS_ERR(enc))
+		ret = PTR_ERR(enc);
+	else {
+		/*  Register new encoder with the upper layer */
+		priv->encoders[priv->num_encoders++] = enc;
+	}
+	return ret;
+}
+
+static int sde_encoder_probe_dsi(struct drm_device *dev)
+{
+	struct msm_drm_private *priv = dev->dev_private;
+	u32 ret = 0;
+	u32 i = 0;
+	u32 num_displays = 0;
+
+	DBG("");
+
+	num_displays = dsi_display_get_num_of_displays();
+	DBG("num_displays %d", num_displays);
+	for (i = 0; i < num_displays; i++) {
+		struct dsi_display *dsi = dsi_display_get_display_by_index(i);
+
+		if (dsi_display_is_active(dsi)) {
+			struct display_probe_info probe_info = { 0 };
+
+			DBG("display %d/%d is active", i, num_displays);
+			probe_info.type = INTF_DSI;
+
+			ret = dsi_display_get_info(dsi, &probe_info.dsi_info);
+			if (WARN_ON(ret))
+				DBG("Failed to retrieve dsi panel info");
+			else {
+				struct drm_encoder *enc =
+				    sde_encoder_virt_init(dev,
+							  &probe_info);
+				if (IS_ERR(enc))
+					return PTR_ERR(enc);
+
+				ret = dsi_display_drm_init(dsi, enc);
+				if (ret)
+					return ret;
+
+				/*  Register new encoder with the upper layer */
+				priv->encoders[priv->num_encoders++] = enc;
+			}
+		} else
+			DBG("display %d/%d is not active", i, num_displays);
+	}
+
+	return ret;
+}
+
+void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc,
+		void (*cb)(void *), void *data) {
+	struct sde_encoder *sde_enc = to_sde_encoder(drm_enc);
+	unsigned long lock_flags;
+
+	DBG("");
+
+	spin_lock_irqsave(&sde_enc->intf_lock, lock_flags);
+	sde_enc->vblank_callback = cb;
+	sde_enc->vblank_callback_data = data;
+	spin_unlock_irqrestore(&sde_enc->intf_lock, lock_flags);
+}
+
+/* encoders init,
+ * initialize encoder based on displays
+ */
+void sde_encoders_init(struct drm_device *dev)
+{
+	struct msm_drm_private *priv = dev->dev_private;
+	int ret = 0;
+
+	DBG("");
+
+	/* Start num_encoders at 0, probe functions will increment */
+	priv->num_encoders = 0;
+	ret = sde_encoder_probe_dsi(dev);
+	if (ret)
+		DBG("Error probing DSI, %d", ret);
+	else {
+		ret = sde_encoder_probe_hdmi(dev);
+		if (ret)
+			DBG("Error probing HDMI, %d", ret);
+	}
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog_8996.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog_8996.c
index 68782de..86673e0 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog_8996.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog_8996.c
@@ -171,13 +171,13 @@
 			{.id = SSPP_VIG3, .base = 0x0000b000,
 			.features = VIG_17X_MASK, .sblk = &layer},
 
-			{.id = SSPP_RGB0, .base = 0x00001500,
+			{.id = SSPP_RGB0, .base = 0x00015000,
 			.features = RGB_17X_MASK, .sblk = &layer},
-			{.id = SSPP_RGB1, .base = 0x00001700,
+			{.id = SSPP_RGB1, .base = 0x00017000,
 			.features = RGB_17X_MASK, .sblk = &layer},
-			{.id = SSPP_RGB2, .base = 0x00001900,
+			{.id = SSPP_RGB2, .base = 0x00019000,
 			.features = RGB_17X_MASK, .sblk = &layer},
-			{.id = SSPP_RGB3, .base = 0x00001B00,
+			{.id = SSPP_RGB3, .base = 0x0001B000,
 			.features = RGB_17X_MASK, .sblk = &layer},
 
 			{.id = SSPP_DMA0, .base = 0x00025000,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_intf.c b/drivers/gpu/drm/msm/sde/sde_hw_intf.c
index 6981425..b1e85a1 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_intf.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_intf.c
@@ -70,7 +70,8 @@
 	int i;
 
 	for (i = 0; i < m->intf_count; i++) {
-		if (intf == m->intf[i].id) {
+		if ((intf == m->intf[i].id) &&
+		(m->intf[i].type != INTF_NONE)) {
 			b->base_off = addr;
 			b->blk_off = m->intf[i].base;
 			b->hwversion = m->hwversion;
@@ -158,13 +159,13 @@
 		(hsync_polarity << 0);  /* HSYNC Polarity */
 
 	if (!fmt->is_yuv)
-		panel_format = (fmt->bits[0] |
-				(fmt->bits[1] << 2) |
-				(fmt->bits[2] << 4) |
+		panel_format = (fmt->bits[C0_G_Y] |
+				(fmt->bits[C1_B_Cb] << 2) |
+				(fmt->bits[C2_R_Cr] << 4) |
 				(0x21 << 8));
 	else
-	/* Interface treats all the pixel data in RGB888 format */
-		panel_format |= (COLOR_8BIT      |
+		/* Interface treats all the pixel data in RGB888 format */
+		panel_format = (COLOR_8BIT |
 				(COLOR_8BIT << 2) |
 				(COLOR_8BIT << 4) |
 				(0x21 << 8));
@@ -354,8 +355,9 @@
 		return ERR_PTR(-ENOMEM);
 
 	cfg = _intf_offset(idx, m, addr, &c->hw);
-	if (!cfg) {
+	if (IS_ERR_OR_NULL(cfg)) {
 		kfree(c);
+		pr_err("Error Panic\n");
 		return ERR_PTR(-EINVAL);
 	}
 
@@ -371,3 +373,9 @@
 	 */
 	return c;
 }
+
+void sde_hw_intf_deinit(struct sde_hw_intf *intf)
+{
+	kfree(intf);
+}
+
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_intf.h b/drivers/gpu/drm/msm/sde/sde_hw_intf.h
index ce51906..28ff5c7 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_intf.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_intf.h
@@ -15,6 +15,7 @@
 
 #include "sde_hw_catalog.h"
 #include "sde_hw_mdss.h"
+#include "sde_hw_mdp_util.h"
 
 struct sde_hw_intf;
 
@@ -100,4 +101,6 @@
 		void __iomem *addr,
 		struct sde_mdss_cfg *m);
 
+void sde_hw_intf_deinit(struct sde_hw_intf *intf);
+
 #endif /*_SDE_HW_INTF_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_lm.c b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
index e9aeab7..0f055fa 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_lm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
@@ -58,7 +58,7 @@
 		return -EINVAL;
 
 	if ((stage - SDE_STAGE_0) <= sblk->maxblendstages)
-		return sblk->blendstage_base[stage];
+		return sblk->blendstage_base[stage - 1];
 	else
 		return -EINVAL;
 }
@@ -126,7 +126,7 @@
 
 	SDE_REG_WRITE(c, LM_BLEND0_FG_ALPHA + stage_off,
 			fg->const_alpha);
-	SDE_REG_WRITE(c, LM_BLEND0_FG_ALPHA + stage_off,
+	SDE_REG_WRITE(c, LM_BLEND0_BG_ALPHA + stage_off,
 			bg->const_alpha);
 	SDE_REG_WRITE(c, LM_OP_MODE, blend_op);
 }
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdp_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_mdp_ctl.c
index 5611515..ab2e5a3 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdp_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdp_ctl.c
@@ -15,9 +15,9 @@
 #include "sde_hw_mdp_ctl.h"
 
 #define   CTL_LAYER(lm)                 \
-	(((lm) == 5) ? (0x024) : ((lm) * 0x004))
+	(((lm) == LM_5) ? (0x024) : (((lm) - LM_0) * 0x004))
 #define   CTL_LAYER_EXT(lm)             \
-	(0x40 + ((lm) * 0x004))
+	(0x40 + (((lm) - LM_0) * 0x004))
 #define   CTL_TOP                       0x014
 #define   CTL_FLUSH                     0x018
 #define   CTL_START                     0x01C
@@ -61,15 +61,14 @@
 	return stages;
 }
 
-static inline void sde_hw_ctl_setup_flush(struct sde_hw_ctl *ctx, u32 flushbits,
-		u8 force_start)
+static inline void sde_hw_ctl_force_start(struct sde_hw_ctl *ctx)
 {
-	struct sde_hw_blk_reg_map *c = &ctx->hw;
+	SDE_REG_WRITE(&ctx->hw, CTL_START, 0x1);
+}
 
-	SDE_REG_WRITE(c, CTL_FLUSH, flushbits);
-
-	if (force_start)
-		SDE_REG_WRITE(c, CTL_START, 0x1);
+static inline void sde_hw_ctl_setup_flush(struct sde_hw_ctl *ctx, u32 flushbits)
+{
+	SDE_REG_WRITE(&ctx->hw, CTL_FLUSH, flushbits);
 }
 
 static inline int sde_hw_ctl_get_bitmask_sspp(struct sde_hw_ctl *ctx,
@@ -222,7 +221,7 @@
 		struct sde_hw_stage_cfg *cfg)
 {
 	struct sde_hw_blk_reg_map *c = &ctx->hw;
-	u32 mixercfg, mixercfg_ext;
+	u32 mixercfg, mixercfg_ext = 0;
 	int i, j;
 	u8 stages;
 	int pipes_per_stage;
@@ -237,8 +236,8 @@
 	else
 		pipes_per_stage = 1;
 
-	mixercfg = cfg->border_enable >> 24; /* BORDER_OUT */
-;
+	mixercfg = cfg->border_enable << 24; /* BORDER_OUT */
+
 	for (i = 0; i <= stages; i++) {
 		for (j = 0; j < pipes_per_stage; j++) {
 			switch (cfg->stage[i][j]) {
@@ -298,17 +297,38 @@
 	SDE_REG_WRITE(c, CTL_LAYER_EXT(lm), mixercfg_ext);
 }
 
+static void sde_hw_ctl_intf_cfg(struct sde_hw_ctl *ctx,
+		struct sde_hw_intf_cfg *cfg)
+{
+	struct sde_hw_blk_reg_map *c = &ctx->hw;
+	u32 intf_cfg = 0;
+
+	intf_cfg |= (cfg->intf & 0xF) << 4;
+
+	if (cfg->wb)
+		intf_cfg |= (cfg->wb & 0x3) + 2;
+
+	if (cfg->mode_3d) {
+		intf_cfg |= BIT(19);
+		intf_cfg |= (cfg->mode_3d - 1) << 20;
+	}
+
+	SDE_REG_WRITE(c, CTL_TOP, intf_cfg);
+}
+
 static void _setup_ctl_ops(struct sde_hw_ctl_ops *ops,
 		unsigned long cap)
 {
 	ops->setup_flush = sde_hw_ctl_setup_flush;
+	ops->setup_start = sde_hw_ctl_force_start;
+	ops->setup_intf_cfg = sde_hw_ctl_intf_cfg;
 	ops->reset = sde_hw_ctl_reset_control;
+	ops->setup_blendstage = sde_hw_ctl_setup_blendstage;
 	ops->get_bitmask_sspp = sde_hw_ctl_get_bitmask_sspp;
 	ops->get_bitmask_mixer = sde_hw_ctl_get_bitmask_mixer;
 	ops->get_bitmask_dspp = sde_hw_ctl_get_bitmask_dspp;
 	ops->get_bitmask_intf = sde_hw_ctl_get_bitmask_intf;
 	ops->get_bitmask_cdm = sde_hw_ctl_get_bitmask_cdm;
-	ops->setup_blendstage = sde_hw_ctl_setup_blendstage;
 };
 
 struct sde_hw_ctl *sde_hw_ctl_init(enum sde_ctl idx,
@@ -323,8 +343,9 @@
 		return ERR_PTR(-ENOMEM);
 
 	cfg = _ctl_offset(idx, m, addr, &c->hw);
-	if (cfg) {
+	if (IS_ERR_OR_NULL(cfg)) {
 		kfree(c);
+		pr_err("Error Panic\n");
 		return ERR_PTR(-EINVAL);
 	}
 
@@ -336,3 +357,8 @@
 
 	return c;
 }
+
+void sde_hw_ctl_destroy(struct sde_hw_ctl *ctx)
+{
+	kfree(ctx);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdp_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_mdp_ctl.h
index 14a519f..00f1ee4 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdp_ctl.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdp_ctl.h
@@ -28,13 +28,45 @@
 };
 
 /**
+ * struct sde_hw_intf_cfg :Desbribes how the mdp writes data to
+ *                         output interface
+ * @intf :                 Interface id
+ * @wb:                    writeback id
+ * @mode_3d:               3d mux configuration
+ */
+struct sde_hw_intf_cfg {
+	enum sde_intf intf;
+	enum sde_wb wb;
+	enum sde_3d_blend_mode mode_3d;
+};
+
+/**
  * struct sde_hw_ctl_ops - Interface to the wb Hw driver functions
  * Assumption is these functions will be called after clocks are enabled
  */
 struct sde_hw_ctl_ops {
+	/**
+	 * kickoff hw operation for Sw controlled interfaces
+	 * DSI cmd mode and WB interface are SW controlled
+	 * @ctx       : ctl path ctx pointer
+	 */
+	void (*setup_start)(struct sde_hw_ctl *ctx);
+
+	/**
+	 * FLUSH the modules for this control path
+	 * @ctx       : ctl path ctx pointer
+	 * @flushbits : module flushmask
+	 */
 	void (*setup_flush)(struct sde_hw_ctl *ctx,
-		u32  flushbits,
-		u8 force_start);
+		u32  flushbits);
+
+	/**
+	 * Setup ctl_path interface config
+	 * @ctx
+	 * @cfg    : interface config structure pointer
+	 */
+	void (*setup_intf_cfg)(struct sde_hw_ctl *ctx,
+		struct sde_hw_intf_cfg *cfg);
 
 	int (*reset)(struct sde_hw_ctl *c);
 
@@ -87,7 +119,7 @@
 
 /**
  * sde_hw_ctl_init(): Initializes the ctl_path hw driver object.
- * should be called before accessing every mixer.
+ * should be called before accessing every ctl path registers.
  * @idx:  ctl_path index for which driver object is required
  * @addr: mapped register io address of MDP
  * @m :   pointer to mdss catalog data
@@ -96,4 +128,10 @@
 		void __iomem *addr,
 		struct sde_mdss_cfg *m);
 
+/**
+ * sde_hw_ctl_destroy(): Destroys ctl driver context
+ * should be called to free the context
+ */
+void sde_hw_ctl_destroy(struct sde_hw_ctl *ctx);
+
 #endif /*_SDE_HW_MDP_CTL_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
index ce5a90b..da9efe5 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
@@ -16,6 +16,7 @@
 #include <linux/kernel.h>
 #include <linux/err.h>
 
+#define SDE_NONE                        0
 #define SDE_CSC_MATRIX_COEFF_SIZE	9
 #define SDE_CSC_CLAMP_SIZE		6
 #define SDE_CSC_BIAS_SIZE		3
@@ -57,7 +58,7 @@
 };
 
 enum sde_lm {
-	LM_0 = 0,
+	LM_0 = 1,
 	LM_1,
 	LM_2,
 	LM_3,
@@ -79,7 +80,7 @@
 	SDE_STAGE_MAX
 };
 enum sde_dspp {
-	DSPP_0 = 0,
+	DSPP_0 = 1,
 	DSPP_1,
 	DSPP_2,
 	DSPP_3,
@@ -87,7 +88,7 @@
 };
 
 enum sde_ctl {
-	CTL_0 = 0,
+	CTL_0 = 1,
 	CTL_1,
 	CTL_2,
 	CTL_3,
@@ -96,13 +97,13 @@
 };
 
 enum sde_cdm {
-	CDM_0 = 0,
+	CDM_0 = 1,
 	CDM_1,
 	CDM_MAX
 };
 
 enum sde_pingpong {
-	PINGPONG_0 = 0,
+	PINGPONG_0 = 1,
 	PINGPONG_1,
 	PINGPONG_2,
 	PINGPONG_3,
@@ -111,7 +112,7 @@
 };
 
 enum sde_intf {
-	INTF_0 = 0,
+	INTF_0 = 1,
 	INTF_1,
 	INTF_2,
 	INTF_3,
@@ -208,12 +209,10 @@
  * expected by the HW programming.
  */
 enum {
-	COLOR_4BIT,
-	COLOR_5BIT,
-	COLOR_6BIT,
-	COLOR_8BIT,
-	COLOR_ALPHA_1BIT = 0,
-	COLOR_ALPHA_4BIT = 1,
+	COLOR_1BIT = 0,
+	COLOR_5BIT = 1,
+	COLOR_6BIT = 2,
+	COLOR_8BIT = 3,
 };
 
 enum sde_alpha_blend_type {
@@ -224,6 +223,26 @@
 	ALPHA_MAX
 };
 
+
+/**
+ * enum sde_3d_blend_mode
+ * Desribes how the 3d data is blended
+ * @BLEND_3D_NONE      : 3d blending not enabled
+ * @BLEND_3D_FRAME_INT : Frame interleaving
+ * @BLEND_3D_H_ROW_INT : Horizontal row interleaving
+ * @BLEND_3D_V_ROW_INT : vertical row interleaving
+ * @BLEND_3D_COL_INT   : column interleaving
+ * @BLEND_3D_MAX       :
+ */
+enum sde_3d_blend_mode {
+	BLEND_3D_NONE = 0,
+	BLEND_3D_FRAME_INT,
+	BLEND_3D_H_ROW_INT,
+	BLEND_3D_V_ROW_INT,
+	BLEND_3D_COL_INT,
+	BLEND_3D_MAX
+};
+
 struct addr_info {
 	u32 plane[SDE_MAX_PLANES];
 };
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
index 181f8be..5319922 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
@@ -182,7 +182,7 @@
 	u32 opmode = 0;
 	u32 idx;
 
-	if (!_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx))
+	if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx))
 		return;
 
 	opmode = SDE_REG_READ(c, SSPP_SRC_OP_MODE + idx);
@@ -210,7 +210,7 @@
 
 	src_format = (chroma_samp << 23) | (fmt->fetch_planes << 19) |
 		(fmt->bits[C3_ALPHA] << 6) | (fmt->bits[C2_R_Cr] << 4) |
-		(fmt->bits[C0_G_Y] << 0);
+		(fmt->bits[C1_B_Cb] << 2) | (fmt->bits[C0_G_Y] << 0);
 
 	if (flags & SDE_SSPP_ROT_90)
 		src_format |= BIT(11); /* ROT90 */
@@ -235,12 +235,9 @@
 	}
 
 	/* if this is YUV pixel format, enable CSC */
-	if (fmt->is_yuv) {
-		_sspp_setup_opmode(ctx, CSC, 0x0);
-	} else {
+	if (fmt->is_yuv)
 		src_format |= BIT(15);
-		_sspp_setup_opmode(ctx, CSC, 0x1);
-	}
+	_sspp_setup_opmode(ctx, CSC, fmt->is_yuv);
 
 	opmode |= MDSS_MDP_OP_PE_OVERRIDE;
 
@@ -260,8 +257,8 @@
 	struct sde_hw_blk_reg_map *c = &ctx->hw;
 	u8 color;
 	u32 lr_pe[4], tb_pe[4], tot_req_pixels[4];
-	const u32 bytemask = 0xffff;
-	const u8 shortmask = 0xff;
+	const u32 bytemask = 0xff;
+	const u32 shortmask = 0xffff;
 	u32 idx;
 
 	if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx))
@@ -283,7 +280,7 @@
 			((pe_ext->top_ftch[color] & bytemask) << 8)|
 			(pe_ext->top_rpt[color] & bytemask);
 
-		tot_req_pixels[color] = (((cfg->src.height +
+		tot_req_pixels[color] = (((pe_ext->roi_h[color] +
 			pe_ext->num_ext_pxls_top[color] +
 			pe_ext->num_ext_pxls_btm[color]) & shortmask) << 16) |
 			((pe_ext->roi_w[color] +
@@ -323,30 +320,30 @@
 
 	scale_config = BIT(0) | BIT(1);
 	/* RGB/YUV config */
-	scale_config |= (pe_ext->horz_filter[0] & mask) << 8;
-	scale_config |= (pe_ext->vert_filter[0] & mask) << 10;
+	scale_config |= (pe_ext->horz_filter[SDE_SSPP_COMP_LUMA] & mask) << 8;
+	scale_config |= (pe_ext->vert_filter[SDE_SSPP_COMP_LUMA] & mask) << 10;
 	/* Aplha config*/
-	scale_config |= (pe_ext->horz_filter[3] & mask) << 16;
-	scale_config |= (pe_ext->vert_filter[3] & mask) << 18;
+	scale_config |= (pe_ext->horz_filter[SDE_SSPP_COMP_ALPHA] & mask) << 16;
+	scale_config |= (pe_ext->vert_filter[SDE_SSPP_COMP_ALPHA] & mask) << 18;
 
 	SDE_REG_WRITE(c, SCALE_CONFIG + idx,  scale_config);
 	SDE_REG_WRITE(c, COMP0_3_INIT_PHASE_X + idx,
-			pe_ext->init_phase_x[0]);
+			pe_ext->init_phase_x[SDE_SSPP_COMP_LUMA]);
 	SDE_REG_WRITE(c, COMP0_3_INIT_PHASE_Y + idx,
-			pe_ext->init_phase_y[0]);
+			pe_ext->init_phase_y[SDE_SSPP_COMP_LUMA]);
 	SDE_REG_WRITE(c, COMP0_3_PHASE_STEP_X + idx,
-			pe_ext->phase_step_x[0]);
+			pe_ext->phase_step_x[SDE_SSPP_COMP_LUMA]);
 	SDE_REG_WRITE(c, COMP0_3_PHASE_STEP_Y + idx,
-			pe_ext->phase_step_y[0]);
+			pe_ext->phase_step_y[SDE_SSPP_COMP_LUMA]);
 
 	SDE_REG_WRITE(c, COMP1_2_INIT_PHASE_X + idx,
-			pe_ext->init_phase_x[1]);
+			pe_ext->init_phase_x[SDE_SSPP_COMP_CHROMA]);
 	SDE_REG_WRITE(c, COMP1_2_INIT_PHASE_Y + idx,
-			pe_ext->init_phase_y[1]);
+			pe_ext->init_phase_y[SDE_SSPP_COMP_CHROMA]);
 	SDE_REG_WRITE(c, COMP1_2_PHASE_STEP_X + idx,
-			pe_ext->phase_step_x[1]);
+			pe_ext->phase_step_x[SDE_SSPP_COMP_CHROMA]);
 	SDE_REG_WRITE(c, COMP1_2_PHASE_STEP_Y + idx,
-			pe_ext->phase_step_y[0]);
+			pe_ext->phase_step_y[SDE_SSPP_COMP_CHROMA]);
 }
 
 /**
@@ -365,7 +362,7 @@
 		return;
 
 	/* program pixel extension override */
-	if (!pe_ext)
+	if (pe_ext)
 		sde_hw_sspp_setup_pe_config(ctx, cfg, pe_ext);
 
 	/* src and dest rect programming */
@@ -388,10 +385,8 @@
 		if (test_bit(SDE_SSPP_SCALAR_RGB, &ctx->cap->features) ||
 			test_bit(SDE_SSPP_SCALAR_QSEED2, &ctx->cap->features)) {
 			/* program decimation */
-			if (!cfg->horz_decimation)
-				decimation = (cfg->horz_decimation - 1) <<  8;
-			if (!cfg->vert_decimation)
-				decimation |= (cfg->vert_decimation - 1);
+			decimation = ((1 << cfg->horz_decimation) - 1) << 8;
+			decimation |= ((1 << cfg->vert_decimation) - 1);
 
 			sde_hw_sspp_setup_scalar(ctx, pe_ext);
 		}
@@ -421,7 +416,6 @@
 	for (i = 0; i < cfg->src.num_planes; i++)
 		SDE_REG_WRITE(c, SSPP_SRC0_ADDR  + idx + i*0x4,
 			cfg->addr.plane[i]);
-
 }
 
 static void sde_hw_sspp_setup_csc_8bit(struct sde_hw_pipe *ctx,
@@ -476,7 +470,6 @@
 static void sde_hw_sspp_setup_histogram_v1(struct sde_hw_pipe *ctx,
 			void *cfg)
 {
-
 }
 
 static void sde_hw_sspp_setup_memcolor(struct sde_hw_pipe *ctx,
@@ -589,3 +582,8 @@
 	return c;
 }
 
+void sde_hw_sspp_destroy(struct sde_hw_pipe *ctx)
+{
+	kfree(ctx);
+}
+
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
index 0c3873b..feb5e85 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
@@ -15,7 +15,6 @@
 
 #include "sde_hw_catalog.h"
 #include "sde_hw_mdss.h"
-#include "sde_mdp_formats.h"
 #include "sde_hw_mdp_util.h"
 
 struct sde_hw_pipe;
@@ -29,6 +28,15 @@
 #define SDE_SSPP_SOURCE_ROTATED_90 0x8
 #define SDE_SSPP_ROT_90  0x10
 
+/**
+ * Component indices
+ */
+enum {
+	SDE_SSPP_COMP_LUMA = 0,
+	SDE_SSPP_COMP_CHROMA = 1,
+	SDE_SSPP_COMP_ALPHA = 3
+};
+
 enum {
 	SDE_MDP_FRAME_LINEAR,
 	SDE_MDP_FRAME_TILE_A4X,
@@ -88,6 +96,7 @@
 	int btm_rpt[SDE_MAX_PLANES];
 
 	uint32_t roi_w[SDE_MAX_PLANES];
+	uint32_t roi_h[SDE_MAX_PLANES];
 
 	/*
 	 * Filter type to be used for scaling in horizontal and vertical
@@ -262,5 +271,12 @@
 			void __iomem *addr,
 			struct sde_mdss_cfg *m);
 
+/**
+ * sde_hw_sspp_destroy(): Destroys SSPP driver context
+ * should be called during Hw pipe cleanup.
+ * @ctx:  Pointer to SSPP driver context returned by sde_hw_sspp_init
+ */
+void sde_hw_sspp_destroy(struct sde_hw_pipe *ctx);
+
 #endif /*_SDE_HW_SSPP_H */
 
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 740b9c0..bbe1e98 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -12,31 +12,63 @@
 
 #include <drm/drm_crtc.h>
 #include "msm_drv.h"
+#include "msm_mmu.h"
 #include "sde_kms.h"
 #include "sde_hw_mdss.h"
+#include "sde_hw_intf.h"
 
-static int modeset_init_intf(struct sde_kms *sde_kms, int intf_num)
+static const char * const iommu_ports[] = {
+		"mdp_0",
+};
+
+#define DEFAULT_MDP_SRC_CLK 200000000
+
+int sde_disable(struct sde_kms *sde_kms)
 {
-	struct sde_mdss_cfg *catalog = sde_kms->catalog;
-	u32 intf_type = catalog->intf[intf_num].type;
+	DBG("");
 
-	switch (intf_type) {
-	case INTF_NONE:
-		break;
-	case INTF_DSI:
-		break;
-	case INTF_LCDC:
-		break;
-	case INTF_HDMI:
-		break;
-	case INTF_EDP:
-	default:
-		break;
-	}
+	clk_disable_unprepare(sde_kms->ahb_clk);
+	clk_disable_unprepare(sde_kms->axi_clk);
+	clk_disable_unprepare(sde_kms->core_clk);
+	if (sde_kms->lut_clk)
+		clk_disable_unprepare(sde_kms->lut_clk);
 
 	return 0;
 }
 
+int sde_enable(struct sde_kms *sde_kms)
+{
+	DBG("");
+
+	clk_prepare_enable(sde_kms->ahb_clk);
+	clk_prepare_enable(sde_kms->axi_clk);
+	clk_prepare_enable(sde_kms->core_clk);
+	if (sde_kms->lut_clk)
+		clk_prepare_enable(sde_kms->lut_clk);
+
+	return 0;
+}
+
+static void sde_prepare_commit(struct msm_kms *kms,
+		struct drm_atomic_state *state)
+{
+	struct sde_kms *sde_kms = to_sde_kms(to_mdp_kms(kms));
+
+	sde_enable(sde_kms);
+}
+
+static void sde_complete_commit(struct msm_kms *kms,
+		struct drm_atomic_state *state)
+{
+	struct sde_kms *sde_kms = to_sde_kms(to_mdp_kms(kms));
+
+	sde_disable(sde_kms);
+}
+
+static void sde_wait_for_crtc_commit_done(struct msm_kms *kms,
+						struct drm_crtc *crtc)
+{
+}
 static int modeset_init(struct sde_kms *sde_kms)
 {
 	struct msm_drm_private *priv = sde_kms->dev->dev_private;
@@ -62,8 +94,9 @@
 			|| !num_private_planes)
 			primary = false;
 
-		plane = sde_plane_init(dev, primary);
+		plane = sde_plane_init(dev, catalog->sspp[i].id, primary);
 		if (IS_ERR(plane)) {
+			pr_err("%s: sde_plane_init failed", __func__);
 			ret = PTR_ERR(plane);
 			goto fail;
 		}
@@ -71,7 +104,7 @@
 
 		if (primary)
 			primary_planes[primary_planes_idx++] = plane;
-		if (num_private_planes)
+		if (primary && num_private_planes)
 			num_private_planes--;
 	}
 
@@ -81,15 +114,21 @@
 		goto fail;
 	}
 
-	/* Create one CRTC per mixer */
-	for (i = 0; i < catalog->mixer_count; i++) {
+	/*
+	 * Enumerate displays supported
+	 */
+	sde_encoders_init(dev);
+
+	/* Create one CRTC per display */
+	for (i = 0; i < priv->num_encoders; i++) {
 		/*
-		 * Each mixer receives a private plane. We start
+		 * Each CRTC receives a private plane. We start
 		 * with first RGB, and then DMA and then VIG.
 		 */
 		struct drm_crtc *crtc;
 
-		crtc = sde_crtc_init(dev, NULL, primary_planes[i], i);
+		crtc = sde_crtc_init(dev, priv->encoders[i],
+				primary_planes[i], i);
 		if (IS_ERR(crtc)) {
 			ret = PTR_ERR(crtc);
 			goto fail;
@@ -97,11 +136,13 @@
 		priv->crtcs[priv->num_crtcs++] = crtc;
 	}
 
-	for (i = 0; i < catalog->intf_count; i++) {
-		ret = modeset_init_intf(sde_kms, i);
-		if (ret)
-			goto fail;
-	}
+	/*
+	 * Iterate through the list of encoders and
+	 * set the possible CRTCs
+	 */
+	for (i = 0; i < priv->num_encoders; i++)
+		priv->encoders[i]->possible_crtcs = (1 << priv->num_crtcs) - 1;
+
 	return 0;
 fail:
 	return ret;
@@ -137,6 +178,9 @@
 		.irq_postinstall = sde_irq_postinstall,
 		.irq_uninstall   = sde_irq_uninstall,
 		.irq             = sde_irq,
+		.prepare_commit  = sde_prepare_commit,
+		.complete_commit = sde_complete_commit,
+		.wait_for_crtc_commit_done = sde_wait_for_crtc_commit_done,
 		.enable_vblank   = sde_enable_vblank,
 		.disable_vblank  = sde_disable_vblank,
 		.get_format      = mdp_get_format,
@@ -184,6 +228,7 @@
 		ret = PTR_ERR(sde_kms->mmio);
 		goto fail;
 	}
+	pr_err("Mapped Mdp address space @%pK", sde_kms->mmio);
 
 	sde_kms->vbif = msm_ioremap(pdev, "vbif_phys", "VBIF");
 	if (IS_ERR(sde_kms->vbif)) {
@@ -247,8 +292,27 @@
 	get_clk(pdev, &sde_kms->mmagic_clk, "mmagic_clk", false);
 	get_clk(pdev, &sde_kms->iommu_clk, "iommu_clk", false);
 
+	if (sde_kms->mmagic) {
+		ret = regulator_enable(sde_kms->mmagic);
+		if (ret) {
+			dev_err(sde_kms->dev->dev,
+				"failed to enable mmagic GDSC: %d\n", ret);
+			goto fail;
+		}
+	}
+	if (sde_kms->mmagic_clk) {
+		clk_prepare_enable(sde_kms->mmagic_clk);
+		if (ret) {
+			dev_err(sde_kms->dev->dev, "failed to enable mmagic_clk\n");
+			goto undo_gdsc;
+		}
+	}
+
 	return sde_kms;
 
+undo_gdsc:
+	if (sde_kms->mmagic)
+		regulator_disable(sde_kms->mmagic);
 fail:
 	if (kms)
 		sde_destroy(kms);
@@ -256,6 +320,111 @@
 	return ERR_PTR(ret);
 }
 
+static int sde_translation_ctrl_pwr(struct sde_kms *sde_kms, bool on)
+{
+	struct device *dev = sde_kms->dev->dev;
+	int ret;
+
+	if (on) {
+		if (sde_kms->iommu_clk) {
+			ret = clk_prepare_enable(sde_kms->iommu_clk);
+			if (ret) {
+				dev_err(dev, "failed to enable iommu_clk\n");
+				goto undo_mmagic_clk;
+			}
+		}
+	} else {
+		if (sde_kms->iommu_clk)
+			clk_disable_unprepare(sde_kms->iommu_clk);
+		if (sde_kms->mmagic_clk)
+			clk_disable_unprepare(sde_kms->mmagic_clk);
+		if (sde_kms->mmagic)
+			regulator_disable(sde_kms->mmagic);
+	}
+
+	return 0;
+
+undo_mmagic_clk:
+	if (sde_kms->mmagic_clk)
+		clk_disable_unprepare(sde_kms->mmagic_clk);
+
+	return ret;
+}
+int sde_mmu_init(struct sde_kms *sde_kms)
+{
+	struct sde_mdss_cfg *catalog = sde_kms->catalog;
+	struct sde_hw_intf *intf = NULL;
+	struct iommu_domain *iommu;
+	struct msm_mmu *mmu;
+	int i, ret;
+
+	/*
+	 * Make sure things are off before attaching iommu (bootloader could
+	 * have left things on, in which case we'll start getting faults if
+	 * we don't disable):
+	 */
+	sde_enable(sde_kms);
+	for (i = 0; i < catalog->intf_count; i++) {
+		intf = sde_hw_intf_init(catalog->intf[i].id,
+				sde_kms->mmio,
+				catalog);
+		if (!IS_ERR_OR_NULL(intf)) {
+			intf->ops.enable_timing(intf, 0x0);
+			sde_hw_intf_deinit(intf);
+		}
+	}
+	sde_disable(sde_kms);
+	msleep(20);
+
+	iommu = iommu_domain_alloc(&platform_bus_type);
+
+	if (!IS_ERR_OR_NULL(iommu)) {
+		mmu = msm_smmu_new(sde_kms->dev->dev, MSM_SMMU_DOMAIN_UNSECURE);
+		if (IS_ERR(mmu)) {
+			ret = PTR_ERR(mmu);
+			dev_err(sde_kms->dev->dev,
+				"failed to init iommu: %d\n", ret);
+			iommu_domain_free(iommu);
+			goto fail;
+		}
+
+		ret = sde_translation_ctrl_pwr(sde_kms, true);
+		if (ret) {
+			dev_err(sde_kms->dev->dev,
+				"failed to power iommu: %d\n", ret);
+			mmu->funcs->destroy(mmu);
+			goto fail;
+		}
+
+		ret = mmu->funcs->attach(mmu, (const char **)iommu_ports,
+				ARRAY_SIZE(iommu_ports));
+		if (ret) {
+			dev_err(sde_kms->dev->dev,
+				"failed to attach iommu: %d\n", ret);
+			mmu->funcs->destroy(mmu);
+			goto fail;
+		}
+	} else {
+		dev_info(sde_kms->dev->dev,
+			"no iommu, fallback to phys contig buffers for scanout\n");
+		mmu = NULL;
+	}
+	sde_kms->mmu = mmu;
+
+	sde_kms->mmu_id = msm_register_mmu(sde_kms->dev, mmu);
+	if (sde_kms->mmu_id < 0) {
+		ret = sde_kms->mmu_id;
+		dev_err(sde_kms->dev->dev,
+			"failed to register sde iommu: %d\n", ret);
+		goto fail;
+	}
+
+	return 0;
+fail:
+	return ret;
+
+}
+
 struct msm_kms *sde_kms_init(struct drm_device *dev)
 {
 	struct platform_device *pdev = dev->platformdev;
@@ -282,10 +451,20 @@
 
 	sde_kms->catalog = catalog;
 
+	/* we need to set a default rate before enabling.
+	 * Set a safe rate first, before initializing catalog
+	 * later set more optimal rate based on bandwdith/clock
+	 * requirements
+	 */
+
+	clk_set_rate(sde_kms->src_clk, DEFAULT_MDP_SRC_CLK);
+	sde_enable(sde_kms);
+
 	/*
 	 * Now we need to read the HW catalog and initialize resources such as
 	 * clocks, regulators, GDSC/MMAGIC, ioremap the register ranges etc
 	 */
+	sde_mmu_init(sde_kms);
 
 	/*
 	 * modeset_init should create the DRM related objects i.e. CRTCs,
@@ -296,6 +475,14 @@
 	dev->mode_config.min_width = 0;
 	dev->mode_config.min_height = 0;
 
+	/*
+	 * we can assume the max crtc width is equal to the max supported
+	 * by LM_0
+	 * Also fixing the max height to 4k
+	 */
+	dev->mode_config.max_width =  catalog->mixer[0].sblk->maxwidth;
+	dev->mode_config.max_height = 4096;
+
 	return msm_kms;
 
 fail:
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index 1afe1bb..441398b 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -26,6 +26,7 @@
 	struct sde_mdss_cfg *catalog;
 
 	struct msm_mmu *mmu;
+	int mmu_id;
 
 	/* io/register spaces: */
 	void __iomem *mmio, *vbif;
@@ -86,18 +87,8 @@
 void sde_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc);
 
 enum sde_sspp sde_plane_pipe(struct drm_plane *plane);
-void sde_plane_install_properties(struct drm_plane *plane,
-		struct drm_mode_object *obj);
-void sde_plane_set_scanout(struct drm_plane *plane,
-		struct drm_framebuffer *fb);
-int sde_plane_mode_set(struct drm_plane *plane,
-		struct drm_crtc *crtc, struct drm_framebuffer *fb,
-		int crtc_x, int crtc_y,
-		unsigned int crtc_w, unsigned int crtc_h,
-		uint32_t src_x, uint32_t src_y,
-		uint32_t src_w, uint32_t src_h);
-void sde_plane_complete_flip(struct drm_plane *plane);
-struct drm_plane *sde_plane_init(struct drm_device *dev, bool private_plane);
+struct drm_plane *sde_plane_init(struct drm_device *dev, uint32_t pipe,
+		bool private_plane);
 
 uint32_t sde_crtc_vblank(struct drm_crtc *crtc);
 
@@ -108,7 +99,16 @@
 		struct drm_encoder *encoder,
 		struct drm_plane *plane, int id);
 
-struct drm_encoder *sde_encoder_init(struct drm_device *dev, int intf);
+struct sde_encoder_hw_resources {
+	bool intfs[INTF_MAX];
+	bool pingpongs[PINGPONG_MAX];
+};
+void sde_encoder_get_hw_resources(struct drm_encoder *encoder,
+		struct sde_encoder_hw_resources *hw_res);
+void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc,
+		void (*cb)(void *), void *data);
+void sde_encoders_init(struct drm_device *dev);
+
 
 int sde_irq_domain_init(struct sde_kms *sde_kms);
 int sde_irq_domain_fini(struct sde_kms *sde_kms);
diff --git a/drivers/gpu/drm/msm/sde/sde_mdp_formats.c b/drivers/gpu/drm/msm/sde/sde_mdp_formats.c
new file mode 100644
index 0000000..56b65d4
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_mdp_formats.c
@@ -0,0 +1,134 @@
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include "sde_mdp_formats.h"
+
+static struct sde_mdp_format_params sde_mdp_format_map[] = {
+	INTERLEAVED_RGB_FMT(ARGB8888,
+		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA,
+		true, 4, 0),
+
+	INTERLEAVED_RGB_FMT(ABGR8888,
+		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA,
+		true, 4, 0),
+
+	INTERLEAVED_RGB_FMT(RGBA8888,
+		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr,
+		true, 4, 0),
+
+	INTERLEAVED_RGB_FMT(BGRA8888,
+		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb,
+		true, 4, 0),
+
+	INTERLEAVED_RGB_FMT(XRGB8888,
+		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA,
+		true, 4, 0),
+
+	INTERLEAVED_RGB_FMT(RGB888,
+		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, 0,
+		false, 3, 0),
+
+	INTERLEAVED_RGB_FMT(BGR888,
+		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+		C2_R_Cr, C0_G_Y, C1_B_Cb, 0,
+		false, 3, 0),
+
+	INTERLEAVED_RGB_FMT(RGB565,
+		0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, 0,
+		false, 2, 0),
+
+	INTERLEAVED_RGB_FMT(BGR565,
+		0, 5, 6, 5,
+		C2_R_Cr, C0_G_Y, C1_B_Cb, 0,
+		false, 2, 0),
+
+	PSEDUO_YUV_FMT(NV12,
+		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+		C1_B_Cb, C2_R_Cr,
+		SDE_MDP_CHROMA_420, 0),
+
+	PSEDUO_YUV_FMT(NV21,
+		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+		C2_R_Cr, C1_B_Cb,
+		SDE_MDP_CHROMA_420, 0),
+
+	PSEDUO_YUV_FMT(NV16,
+		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+		C1_B_Cb, C2_R_Cr,
+		SDE_MDP_CHROMA_H2V1, 0),
+
+	PSEDUO_YUV_FMT(NV61,
+		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+		C2_R_Cr, C1_B_Cb,
+		SDE_MDP_CHROMA_H2V1, 0),
+
+	INTERLEAVED_YUV_FMT(VYUY,
+		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+		C2_R_Cr, C0_G_Y, C1_B_Cb, C0_G_Y,
+		false, SDE_MDP_CHROMA_H2V1, 4, 2,
+		0),
+
+	INTERLEAVED_YUV_FMT(UYVY,
+		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, C0_G_Y,
+		false, SDE_MDP_CHROMA_H2V1, 4, 2,
+		0),
+
+	INTERLEAVED_YUV_FMT(YUYV,
+		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+		C0_G_Y, C1_B_Cb, C0_G_Y, C2_R_Cr,
+		false, SDE_MDP_CHROMA_H2V1, 4, 2,
+		0),
+
+	INTERLEAVED_YUV_FMT(YVYU,
+		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+		C0_G_Y, C2_R_Cr, C0_G_Y, C1_B_Cb,
+		false, SDE_MDP_CHROMA_H2V1, 4, 2,
+		0),
+
+	PLANAR_YUV_FMT(YUV420,
+		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+		C2_R_Cr, C1_B_Cb, C0_G_Y,
+		false, SDE_MDP_CHROMA_420, 2,
+		0),
+
+	PLANAR_YUV_FMT(YVU420,
+		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
+		C1_B_Cb, C2_R_Cr, C0_G_Y,
+		false, SDE_MDP_CHROMA_420, 2,
+		0),
+};
+
+struct sde_mdp_format_params *sde_mdp_get_format_params(u32 format,
+		u32 fmt_modifier)
+{
+	u32 i = 0;
+	struct sde_mdp_format_params *fmt = NULL;
+
+	for (i = 0; i < sizeof(sde_mdp_format_map)/sizeof(*sde_mdp_format_map);
+			i++)
+		if (format == sde_mdp_format_map[i].format) {
+			fmt = &sde_mdp_format_map[i];
+			break;
+		}
+
+	return fmt;
+}
+
diff --git a/drivers/gpu/drm/msm/sde/sde_mdp_formats.h b/drivers/gpu/drm/msm/sde/sde_mdp_formats.h
index 4ad3ad3..67f445b 100644
--- a/drivers/gpu/drm/msm/sde/sde_mdp_formats.h
+++ b/drivers/gpu/drm/msm/sde/sde_mdp_formats.h
@@ -58,6 +58,7 @@
 	.is_yuv = true,                                                   \
 	.flag = flg                                                      \
 }
+
 #define PSEDUO_YUV_FMT(fmt, a, r, g, b, e0, e1, chroma, flg)             \
 {                                                                         \
 	.format = DRM_FORMAT_ ## fmt,                                     \
@@ -92,122 +93,12 @@
 	.flag = flg                                                         \
 }
 
-static struct sde_mdp_format_params sde_mdp_format_map[] = {
-	INTERLEAVED_RGB_FMT(ARGB8888,
-		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA,
-		true, 4, 0),
-
-	INTERLEAVED_RGB_FMT(ABGR8888,
-		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA,
-		true, 4, 0),
-
-	INTERLEAVED_RGB_FMT(RGBA8888,
-		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr,
-		true, 4, 0),
-
-	INTERLEAVED_RGB_FMT(BGRA8888,
-		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb,
-		true, 4, 0),
-
-	INTERLEAVED_RGB_FMT(XRGB8888,
-		COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA,
-		true, 4, 0),
-
-	INTERLEAVED_RGB_FMT(RGB888,
-		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, 0,
-		false, 3, 0),
-
-	INTERLEAVED_RGB_FMT(BGR888,
-		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C2_R_Cr, C0_G_Y, C1_B_Cb, 0,
-		false, 3, 0),
-
-	INTERLEAVED_RGB_FMT(RGB565,
-		0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, 0,
-		false, 2, 0),
-
-	INTERLEAVED_RGB_FMT(BGR565,
-		0, 5, 6, 5,
-		C2_R_Cr, C0_G_Y, C1_B_Cb, 0,
-		false, 2, 0),
-
-	PSEDUO_YUV_FMT(NV12,
-		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C1_B_Cb, C2_R_Cr,
-		SDE_MDP_CHROMA_420, 0),
-
-	PSEDUO_YUV_FMT(NV21,
-		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C2_R_Cr, C1_B_Cb,
-		SDE_MDP_CHROMA_420, 0),
-
-	PSEDUO_YUV_FMT(NV16,
-		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C1_B_Cb, C2_R_Cr,
-		SDE_MDP_CHROMA_H2V1, 0),
-
-	PSEDUO_YUV_FMT(NV61,
-		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C2_R_Cr, C1_B_Cb,
-		SDE_MDP_CHROMA_H2V1, 0),
-
-	INTERLEAVED_YUV_FMT(VYUY,
-		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C2_R_Cr, C0_G_Y, C1_B_Cb, C0_G_Y,
-		false, SDE_MDP_CHROMA_H2V1, 4, 2,
-		0),
-
-	INTERLEAVED_YUV_FMT(UYVY,
-		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, C0_G_Y,
-		false, SDE_MDP_CHROMA_H2V1, 4, 2,
-		0),
-
-	INTERLEAVED_YUV_FMT(YUYV,
-		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C0_G_Y, C1_B_Cb, C0_G_Y, C2_R_Cr,
-		false, SDE_MDP_CHROMA_H2V1, 4, 2,
-		0),
-
-	INTERLEAVED_YUV_FMT(YVYU,
-		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C0_G_Y, C2_R_Cr, C0_G_Y, C1_B_Cb,
-		false, SDE_MDP_CHROMA_H2V1, 4, 2,
-		0),
-
-	PLANAR_YUV_FMT(YUV420,
-		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C2_R_Cr, C1_B_Cb, C0_G_Y,
-		false, SDE_MDP_CHROMA_420, 2,
-		0),
-
-	PLANAR_YUV_FMT(YVU420,
-		0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
-		C1_B_Cb, C2_R_Cr, C0_G_Y,
-		false, SDE_MDP_CHROMA_420, 2,
-		0),
-};
-
+/**
+ * sde_mdp_get_format_params(): Returns sde format structure pointer.
+ * @format:  DRM format
+ * @fmt_modifier: DRM format modifier
+ */
 struct sde_mdp_format_params *sde_mdp_get_format_params(u32 format,
-		u32 fmt_modifier)
-{
-	u32 i = 0;
-	struct sde_mdp_format_params *fmt = NULL;
-
-	for (i = 0; i < ARRAY_SIZE(sde_mdp_format_map); i++)
-		if (format == sde_mdp_format_map[i].format) {
-			fmt = &sde_mdp_format_map[i];
-			break;
-		}
-
-	return fmt;
-}
+		u32 fmt_modifier);
 
 #endif /*_SDE_MDP_FORMATS_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 17b7303..09db61b 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -11,103 +11,732 @@
  */
 
 #include "sde_kms.h"
+#include "sde_hwio.h"
+#include "sde_hw_mdp_ctl.h"
+#include "sde_mdp_formats.h"
+#include "sde_hw_sspp.h"
+
+#define DECIMATED_DIMENSION(dim, deci) (((dim) + ((1 << (deci)) - 1)) >> (deci))
+#define PHASE_STEP_SHIFT	21
+#define PHASE_STEP_UNIT_SCALE   ((int) (1 << PHASE_STEP_SHIFT))
+#define PHASE_RESIDUAL		15
+
+#define SDE_PLANE_FEATURE_SCALER \
+	(BIT(SDE_SSPP_SCALAR_QSEED2)| \
+		BIT(SDE_SSPP_SCALAR_QSEED3)| \
+		BIT(SDE_SSPP_SCALAR_RGB))
+
+#ifndef SDE_PLANE_DEBUG_START
+#define SDE_PLANE_DEBUG_START()
+#endif
+
+#ifndef SDE_PLANE_DEBUG_END
+#define SDE_PLANE_DEBUG_END()
+#endif
 
 struct sde_plane {
 	struct drm_plane base;
 	const char *name;
+
+	int mmu_id;
+
+	enum sde_sspp pipe;
+	uint32_t features;      /* capabilities from catalog */
+	uint32_t flush_mask;    /* used to commit pipe registers */
 	uint32_t nformats;
 	uint32_t formats[32];
+
+	struct sde_hw_pipe *pipe_hw;
+	struct sde_hw_pipe_cfg pipe_cfg;
+	struct sde_hw_pixel_ext pixel_ext;
 };
 #define to_sde_plane(x) container_of(x, struct sde_plane, base)
 
-static int sde_plane_update(struct drm_plane *plane,
+static bool sde_plane_enabled(struct drm_plane_state *state)
+{
+	return state->fb && state->crtc;
+}
+
+static void sde_plane_set_scanout(struct drm_plane *plane,
+		struct sde_hw_pipe_cfg *pipe_cfg, struct drm_framebuffer *fb)
+{
+	struct sde_plane *psde = to_sde_plane(plane);
+	int i;
+
+	if (pipe_cfg && fb && psde->pipe_hw->ops.setup_sourceaddress) {
+		/* stride */
+		i = min_t(int, ARRAY_SIZE(fb->pitches), SDE_MAX_PLANES);
+		while (i) {
+			--i;
+			pipe_cfg->src.ystride[i] = fb->pitches[i];
+		}
+
+		/* address */
+		for (i = 0; i < ARRAY_SIZE(pipe_cfg->addr.plane); ++i)
+			pipe_cfg->addr.plane[i] = msm_framebuffer_iova(fb,
+					psde->mmu_id, i);
+
+		/* hw driver */
+		psde->pipe_hw->ops.setup_sourceaddress(psde->pipe_hw, pipe_cfg);
+	}
+}
+
+static void sde_plane_scale_helper(struct drm_plane *plane,
+		uint32_t src, uint32_t dst, uint32_t *phase_steps,
+		enum sde_hw_filter *filter, struct sde_mdp_format_params *fmt,
+		uint32_t chroma_subsampling)
+{
+	/* calcualte phase steps, leave init phase as zero */
+	phase_steps[SDE_SSPP_COMP_LUMA] =
+		mult_frac(1 << PHASE_STEP_SHIFT, src, dst);
+	phase_steps[SDE_SSPP_COMP_CHROMA] =
+		phase_steps[SDE_SSPP_COMP_LUMA] / chroma_subsampling;
+
+	/* calculate scaler config, if necessary */
+	if (src != dst) {
+		filter[SDE_SSPP_COMP_ALPHA] = (src < dst) ?
+				SDE_MDP_SCALE_FILTER_BIL :
+				SDE_MDP_SCALE_FILTER_PCMN;
+
+		if (fmt->is_yuv)
+			filter[SDE_SSPP_COMP_LUMA] = SDE_MDP_SCALE_FILTER_CA;
+		else
+			filter[SDE_SSPP_COMP_LUMA] =
+					filter[SDE_SSPP_COMP_ALPHA];
+	}
+}
+
+/* CIFIX: clean up fmt/subsampling params once we're using fourcc formats */
+static void _sde_plane_pixel_ext_helper(struct drm_plane *plane,
+		uint32_t src, uint32_t dst, uint32_t decimated_src,
+		uint32_t *phase_steps, uint32_t *out_src, int *out_edge1,
+		int *out_edge2, struct sde_mdp_format_params *fmt,
+		uint32_t chroma_subsampling, bool post_compare)
+{
+	/* CIFIX: adapted from mdss_mdp_pipe_calc_pixel_extn() */
+	int64_t edge1, edge2, caf;
+	uint32_t src_work;
+	int i, tmp;
+
+	if (plane && phase_steps && out_src && out_edge1 && out_edge2 && fmt) {
+		/* enable CAF for YUV formats */
+		if (fmt->is_yuv)
+			caf = PHASE_STEP_UNIT_SCALE;
+		else
+			caf = 0;
+
+		for (i = 0; i < SDE_MAX_PLANES; i++) {
+			src_work = decimated_src;
+			if (i == 1 || i == 2)
+				src_work /= chroma_subsampling;
+			if (post_compare)
+				src = src_work;
+			if (!(fmt->is_yuv) && (src == dst)) {
+				/* unity */
+				edge1 = 0;
+				edge2 = 0;
+			} else if (dst >= src) {
+				/* upscale */
+				edge1 = (1 << PHASE_RESIDUAL);
+				edge1 -= caf;
+				edge2 = (1 << PHASE_RESIDUAL);
+				edge2 += (dst - 1) * *(phase_steps + i);
+				edge2 -= (src_work - 1) * PHASE_STEP_UNIT_SCALE;
+				edge2 += caf;
+				edge2 = -(edge2);
+			} else {
+				/* downscale */
+				edge1 = 0;
+				edge2 = (dst - 1) * *(phase_steps + i);
+				edge2 -= (src_work - 1) * PHASE_STEP_UNIT_SCALE;
+				edge2 += *(phase_steps + i);
+				edge2 = -(edge2);
+			}
+
+			/* only enable CAF for luma plane */
+			caf = 0;
+
+			/* populate output arrays */
+			*(out_src + i) = src_work;
+
+			/* edge updates taken from __pxl_extn_helper */
+			/* CIFIX: why are we casting first to uint32_t? */
+			if (edge1 >= 0) {
+				tmp = (uint32_t)edge1;
+				tmp >>= PHASE_STEP_SHIFT;
+				*(out_edge1 + i) = -tmp;
+			} else {
+				tmp = (uint32_t)(-edge1);
+				*(out_edge1 + i) = (tmp + PHASE_STEP_UNIT_SCALE
+						- 1) >> PHASE_STEP_SHIFT;
+			}
+			if (edge2 >= 0) {
+				tmp = (uint32_t)edge2;
+				tmp >>= PHASE_STEP_SHIFT;
+				*(out_edge2 + i) = -tmp;
+			} else {
+				tmp = (uint32_t)(-edge2);
+				*(out_edge2 + i) = (tmp + PHASE_STEP_UNIT_SCALE
+						- 1) >> PHASE_STEP_SHIFT;
+			}
+		}
+	}
+}
+
+static int sde_plane_mode_set(struct drm_plane *plane,
 		struct drm_crtc *crtc, struct drm_framebuffer *fb,
 		int crtc_x, int crtc_y,
 		unsigned int crtc_w, unsigned int crtc_h,
 		uint32_t src_x, uint32_t src_y,
 		uint32_t src_w, uint32_t src_h)
 {
+	struct sde_plane *psde = to_sde_plane(plane);
+	struct sde_plane_state *pstate;
+	const struct mdp_format *format;
+	uint32_t nplanes, pix_format, tmp;
+	int i;
+	struct sde_mdp_format_params *fmt;
+	struct sde_hw_pixel_ext *pe;
+	int ret = 0;
+
+	SDE_PLANE_DEBUG_START();
+	nplanes = drm_format_num_planes(fb->pixel_format);
+
+	pstate = to_sde_plane_state(plane->state);
+
+	format = to_mdp_format(msm_framebuffer_format(fb));
+	pix_format = format->base.pixel_format;
+
+	/* src values are in Q16 fixed point, convert to integer */
+	src_x = src_x >> 16;
+	src_y = src_y >> 16;
+	src_w = src_w >> 16;
+	src_h = src_h >> 16;
+
+	DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", psde->name,
+			fb->base.id, src_x, src_y, src_w, src_h,
+			crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h);
+
+	/* update format configuration */
+	memset(&(psde->pipe_cfg), 0, sizeof(struct sde_hw_pipe_cfg));
+
+	psde->pipe_cfg.src.format = sde_mdp_get_format_params(pix_format,
+			0/* CIFIX: fmt_modifier */);
+	psde->pipe_cfg.src.width = fb->width;
+	psde->pipe_cfg.src.height = fb->height;
+	psde->pipe_cfg.src.num_planes = nplanes;
+
+	sde_plane_set_scanout(plane, &psde->pipe_cfg, fb);
+
+	psde->pipe_cfg.src_rect.x = src_x;
+	psde->pipe_cfg.src_rect.y = src_y;
+	psde->pipe_cfg.src_rect.w = src_w;
+	psde->pipe_cfg.src_rect.h = src_h;
+
+	psde->pipe_cfg.dst_rect.x = crtc_x;
+	psde->pipe_cfg.dst_rect.y = crtc_y;
+	psde->pipe_cfg.dst_rect.w = crtc_w;
+	psde->pipe_cfg.dst_rect.h = crtc_h;
+
+	psde->pipe_cfg.horz_decimation = 0;
+	psde->pipe_cfg.vert_decimation = 0;
+
+	/* get sde pixel format definition */
+	fmt = psde->pipe_cfg.src.format;
+
+	/* update pixel extensions */
+	pe = &(psde->pixel_ext);
+	if (!pe->enable_pxl_ext) {
+		uint32_t chroma_subsample_h, chroma_subsample_v;
+
+		chroma_subsample_h = psde->pipe_cfg.horz_decimation ? 1 :
+			drm_format_horz_chroma_subsampling(pix_format);
+		chroma_subsample_v = psde->pipe_cfg.vert_decimation ? 1 :
+			drm_format_vert_chroma_subsampling(pix_format);
+
+		memset(pe, 0, sizeof(struct sde_hw_pixel_ext));
+
+		/* calculate phase steps */
+		sde_plane_scale_helper(plane, src_w, crtc_w,
+				pe->phase_step_x,
+				pe->horz_filter, fmt, chroma_subsample_h);
+		sde_plane_scale_helper(plane, src_h, crtc_h,
+				pe->phase_step_y,
+				pe->vert_filter, fmt, chroma_subsample_v);
+
+		/* calculate left/right/top/bottom pixel extentions */
+		tmp = DECIMATED_DIMENSION(src_w,
+				psde->pipe_cfg.horz_decimation);
+		if (fmt->is_yuv)
+			tmp &= ~0x1;
+		_sde_plane_pixel_ext_helper(plane, src_w, crtc_w, tmp,
+				pe->phase_step_x,
+				pe->roi_w,
+				pe->num_ext_pxls_left,
+				pe->num_ext_pxls_right, fmt,
+				chroma_subsample_h, 0);
+
+		tmp = DECIMATED_DIMENSION(src_h,
+				psde->pipe_cfg.vert_decimation);
+		_sde_plane_pixel_ext_helper(plane, src_h, crtc_h, tmp,
+				pe->phase_step_y,
+				pe->roi_h,
+				pe->num_ext_pxls_top,
+				pe->num_ext_pxls_btm, fmt,
+				chroma_subsample_v, 1);
+
+		/* CIFIX: port "Single pixel rgb scale adjustment"? */
+
+		for (i = 0; i < SDE_MAX_PLANES; i++) {
+			if (pe->num_ext_pxls_left[i] >= 0)
+				pe->left_rpt[i] =
+					pe->num_ext_pxls_left[i];
+			else
+				pe->left_ftch[i] =
+					pe->num_ext_pxls_left[i];
+
+			if (pe->num_ext_pxls_right[i] >= 0)
+				pe->right_rpt[i] =
+					pe->num_ext_pxls_right[i];
+			else
+				pe->right_ftch[i] =
+					pe->num_ext_pxls_right[i];
+
+			if (pe->num_ext_pxls_top[i] >= 0)
+				pe->top_rpt[i] =
+					pe->num_ext_pxls_top[i];
+			else
+				pe->top_ftch[i] =
+					pe->num_ext_pxls_top[i];
+
+			if (pe->num_ext_pxls_btm[i] >= 0)
+				pe->btm_rpt[i] =
+					pe->num_ext_pxls_btm[i];
+			else
+				pe->btm_ftch[i] =
+					pe->num_ext_pxls_btm[i];
+		}
+	}
+
+	if (psde->pipe_hw->ops.setup_sourceformat)
+		psde->pipe_hw->ops.setup_sourceformat(psde->pipe_hw,
+				&psde->pipe_cfg, 0 /* CIFIX: flags */);
+	if (psde->pipe_hw->ops.setup_rects)
+		psde->pipe_hw->ops.setup_rects(psde->pipe_hw,
+				&psde->pipe_cfg, &psde->pixel_ext);
+
+	/* update csc */
+
+	SDE_PLANE_DEBUG_END();
+	return ret;
+}
+
+static int sde_plane_prepare_fb(struct drm_plane *plane,
+		const struct drm_plane_state *new_state)
+{
+	struct drm_framebuffer *fb = new_state->fb;
+	struct sde_plane *psde = to_sde_plane(plane);
+
+	if (!new_state->fb)
+		return 0;
+
+	SDE_PLANE_DEBUG_START();
+	SDE_PLANE_DEBUG_END();
+	DBG("%s: prepare: FB[%u]", psde->name, fb->base.id);
+	return msm_framebuffer_prepare(fb, psde->mmu_id);
+}
+
+static void sde_plane_cleanup_fb(struct drm_plane *plane,
+		const struct drm_plane_state *old_state)
+{
+	struct drm_framebuffer *fb = old_state->fb;
+	struct sde_plane *psde = to_sde_plane(plane);
+
+	if (!fb)
+		return;
+
+	SDE_PLANE_DEBUG_START();
+	SDE_PLANE_DEBUG_END();
+	DBG("%s: cleanup: FB[%u]", psde->name, fb->base.id);
+	msm_framebuffer_cleanup(fb, psde->mmu_id);
+}
+
+static int sde_plane_atomic_check(struct drm_plane *plane,
+		struct drm_plane_state *state)
+{
+	struct sde_plane *psde = to_sde_plane(plane);
+	struct drm_plane_state *old_state = plane->state;
+	const struct mdp_format *format;
+
+	SDE_PLANE_DEBUG_START();
+	SDE_PLANE_DEBUG_END();
+	DBG("%s: check (%d -> %d)", psde->name,
+			sde_plane_enabled(old_state), sde_plane_enabled(state));
+
+	if (sde_plane_enabled(state)) {
+		/* CIFIX: don't use mdp format? */
+		format = to_mdp_format(msm_framebuffer_format(state->fb));
+		if (MDP_FORMAT_IS_YUV(format) &&
+			(!(psde->features & SDE_PLANE_FEATURE_SCALER) ||
+			 !(psde->features & BIT(SDE_SSPP_CSC)))) {
+			dev_err(plane->dev->dev,
+				"Pipe doesn't support YUV\n");
+
+			return -EINVAL;
+		}
+
+		if (!(psde->features & SDE_PLANE_FEATURE_SCALER) &&
+			(((state->src_w >> 16) != state->crtc_w) ||
+			((state->src_h >> 16) != state->crtc_h))) {
+			dev_err(plane->dev->dev,
+				"Pipe doesn't support scaling (%dx%d -> %dx%d)\n",
+				state->src_w >> 16, state->src_h >> 16,
+				state->crtc_w, state->crtc_h);
+
+			return -EINVAL;
+		}
+	}
+
+	if (sde_plane_enabled(state) && sde_plane_enabled(old_state)) {
+		/* we cannot change SMP block configuration during scanout: */
+		bool full_modeset = false;
+
+		if (state->fb->pixel_format != old_state->fb->pixel_format) {
+			DBG("%s: pixel_format change!", psde->name);
+			full_modeset = true;
+		}
+		if (state->src_w != old_state->src_w) {
+			DBG("%s: src_w change!", psde->name);
+			full_modeset = true;
+		}
+		if (to_sde_plane_state(old_state)->pending) {
+			DBG("%s: still pending!", psde->name);
+			full_modeset = true;
+		}
+		if (full_modeset) {
+			struct drm_crtc_state *crtc_state =
+					drm_atomic_get_crtc_state(state->state,
+							state->crtc);
+			crtc_state->mode_changed = true;
+			to_sde_plane_state(state)->mode_changed = true;
+		}
+	} else {
+		to_sde_plane_state(state)->mode_changed = true;
+	}
+
 	return 0;
 }
 
-static int sde_plane_disable(struct drm_plane *plane)
+static void sde_plane_atomic_update(struct drm_plane *plane,
+				     struct drm_plane_state *old_state)
 {
-	return 0;
+	struct sde_plane *sde_plane = to_sde_plane(plane);
+	struct drm_plane_state *state = plane->state;
+
+	DBG("%s: update", sde_plane->name);
+
+	SDE_PLANE_DEBUG_START();
+	if (!sde_plane_enabled(state)) {
+		to_sde_plane_state(state)->pending = true;
+	} else if (to_sde_plane_state(state)->mode_changed) {
+		int ret;
+
+		to_sde_plane_state(state)->pending = true;
+		ret = sde_plane_mode_set(plane,
+				state->crtc, state->fb,
+				state->crtc_x, state->crtc_y,
+				state->crtc_w, state->crtc_h,
+				state->src_x,  state->src_y,
+				state->src_w, state->src_h);
+		/* atomic_check should have ensured that this doesn't fail */
+		WARN_ON(ret < 0);
+	} else {
+		sde_plane_set_scanout(plane, &sde_plane->pipe_cfg, state->fb);
+	}
+	SDE_PLANE_DEBUG_END();
+}
+
+/* helper to install properties which are common to planes and crtcs */
+static void sde_plane_install_properties(struct drm_plane *plane,
+		struct drm_mode_object *obj)
+{
+	struct drm_device *dev = plane->dev;
+	struct msm_drm_private *dev_priv = dev->dev_private;
+	struct drm_property *prop;
+
+	SDE_PLANE_DEBUG_START();
+#define INSTALL_PROPERTY(name, NAME, init_val, fnc, ...) {}
+
+#define INSTALL_RANGE_PROPERTY(name, NAME, min, max, init_val) \
+		INSTALL_PROPERTY(name, NAME, init_val, \
+				create_range, min, max)
+
+#define INSTALL_ENUM_PROPERTY(name, NAME, init_val) \
+		INSTALL_PROPERTY(name, NAME, init_val, \
+				create_enum, name##_prop_enum_list, \
+				ARRAY_SIZE(name##_prop_enum_list))
+
+	INSTALL_RANGE_PROPERTY(zpos, ZPOS, 1, 255, 1);
+
+#undef INSTALL_RANGE_PROPERTY
+#undef INSTALL_ENUM_PROPERTY
+#undef INSTALL_PROPERTY
+	SDE_PLANE_DEBUG_END();
+}
+
+static int sde_plane_atomic_set_property(struct drm_plane *plane,
+		struct drm_plane_state *state, struct drm_property *property,
+		uint64_t val)
+{
+	struct drm_device *dev = plane->dev;
+	struct sde_plane_state *pstate;
+	struct msm_drm_private *dev_priv = dev->dev_private;
+	int ret = 0;
+
+	SDE_PLANE_DEBUG_START();
+
+	pstate = to_sde_plane_state(state);
+
+#define SET_PROPERTY(name, NAME, type) do { \
+		if (dev_priv->plane_property[PLANE_PROP_##NAME] == property) { \
+			pstate->name = (type)val; \
+			DBG("Set property %s %d", #name, (type)val); \
+			goto done; \
+		} \
+	} while (0)
+
+	SET_PROPERTY(zpos, ZPOS, uint8_t);
+
+	dev_err(dev->dev, "Invalid property\n");
+	ret = -EINVAL;
+done:
+	SDE_PLANE_DEBUG_END();
+	return ret;
+#undef SET_PROPERTY
+}
+
+static int sde_plane_set_property(struct drm_plane *plane,
+		struct drm_property *property, uint64_t val)
+{
+	int rc;
+
+	SDE_PLANE_DEBUG_START();
+	rc = sde_plane_atomic_set_property(plane, plane->state, property,
+		val);
+	SDE_PLANE_DEBUG_END();
+	return rc;
+}
+
+static int sde_plane_atomic_get_property(struct drm_plane *plane,
+		const struct drm_plane_state *state,
+		struct drm_property *property, uint64_t *val)
+{
+	struct drm_device *dev = plane->dev;
+	struct sde_plane_state *pstate;
+	struct msm_drm_private *dev_priv = dev->dev_private;
+	int ret = 0;
+
+	SDE_PLANE_DEBUG_START();
+	pstate = to_sde_plane_state(state);
+
+#define GET_PROPERTY(name, NAME, type) do { \
+		if (dev_priv->plane_property[PLANE_PROP_##NAME] == property) { \
+			*val = pstate->name; \
+			DBG("Get property %s %lld", #name, *val); \
+			goto done; \
+		} \
+	} while (0)
+
+	GET_PROPERTY(zpos, ZPOS, uint8_t);
+
+	dev_err(dev->dev, "Invalid property\n");
+	ret = -EINVAL;
+done:
+	SDE_PLANE_DEBUG_END();
+	return ret;
+#undef SET_PROPERTY
 }
 
 static void sde_plane_destroy(struct drm_plane *plane)
 {
-	struct sde_plane *sde_plane = to_sde_plane(plane);
-	struct msm_drm_private *priv = plane->dev->dev_private;
+	struct sde_plane *psde = to_sde_plane(plane);
 
-	if (priv->kms)
-		sde_plane_disable(plane);
+	SDE_PLANE_DEBUG_START();
 
+	if (psde->pipe_hw)
+		sde_hw_sspp_destroy(psde->pipe_hw);
+
+	drm_plane_helper_disable(plane);
 	drm_plane_cleanup(plane);
 
-	kfree(sde_plane);
+	kfree(psde);
+
+	SDE_PLANE_DEBUG_END();
 }
 
-/* helper to install properties which are common to planes and crtcs */
-void sde_plane_install_properties(struct drm_plane *plane,
-		struct drm_mode_object *obj)
+static void sde_plane_destroy_state(struct drm_plane *plane,
+		struct drm_plane_state *state)
 {
+	SDE_PLANE_DEBUG_START();
+	if (state->fb)
+		drm_framebuffer_unreference(state->fb);
+
+	kfree(to_sde_plane_state(state));
+	SDE_PLANE_DEBUG_END();
 }
 
-int sde_plane_set_property(struct drm_plane *plane,
-		struct drm_property *property, uint64_t val)
+static struct drm_plane_state *
+sde_plane_duplicate_state(struct drm_plane *plane)
 {
-	return -EINVAL;
+	struct sde_plane_state *pstate;
+
+	if (WARN_ON(!plane->state))
+		return NULL;
+
+	SDE_PLANE_DEBUG_START();
+	pstate = kmemdup(to_sde_plane_state(plane->state),
+			sizeof(*pstate), GFP_KERNEL);
+
+	if (pstate && pstate->base.fb)
+		drm_framebuffer_reference(pstate->base.fb);
+
+	pstate->mode_changed = false;
+	pstate->pending = false;
+	SDE_PLANE_DEBUG_END();
+
+	return &pstate->base;
+}
+
+static void sde_plane_reset(struct drm_plane *plane)
+{
+	struct sde_plane_state *pstate;
+
+	SDE_PLANE_DEBUG_START();
+	if (plane->state && plane->state->fb)
+		drm_framebuffer_unreference(plane->state->fb);
+
+	kfree(to_sde_plane_state(plane->state));
+	pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
+
+	memset(pstate, 0, sizeof(struct sde_plane_state));
+
+	/* assign default blend parameters */
+	pstate->alpha = 255;
+	pstate->premultiplied = 0;
+
+	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
+		pstate->zpos = STAGE_BASE;
+	else
+		pstate->zpos = STAGE0 + drm_plane_index(plane);
+
+	pstate->base.plane = plane;
+
+	plane->state = &pstate->base;
+	SDE_PLANE_DEBUG_END();
 }
 
 static const struct drm_plane_funcs sde_plane_funcs = {
-		.update_plane = sde_plane_update,
-		.disable_plane = sde_plane_disable,
+		.update_plane = drm_atomic_helper_update_plane,
+		.disable_plane = drm_atomic_helper_disable_plane,
 		.destroy = sde_plane_destroy,
 		.set_property = sde_plane_set_property,
+		.atomic_set_property = sde_plane_atomic_set_property,
+		.atomic_get_property = sde_plane_atomic_get_property,
+		.reset = sde_plane_reset,
+		.atomic_duplicate_state = sde_plane_duplicate_state,
+		.atomic_destroy_state = sde_plane_destroy_state,
 };
 
-void sde_plane_set_scanout(struct drm_plane *plane,
-		struct drm_framebuffer *fb)
-{
-}
+static const struct drm_plane_helper_funcs sde_plane_helper_funcs = {
+		.prepare_fb = sde_plane_prepare_fb,
+		.cleanup_fb = sde_plane_cleanup_fb,
+		.atomic_check = sde_plane_atomic_check,
+		.atomic_update = sde_plane_atomic_update,
+};
 
-int sde_plane_mode_set(struct drm_plane *plane,
-		struct drm_crtc *crtc, struct drm_framebuffer *fb,
-		int crtc_x, int crtc_y,
-		unsigned int crtc_w, unsigned int crtc_h,
-		uint32_t src_x, uint32_t src_y,
-		uint32_t src_w, uint32_t src_h)
+enum sde_sspp sde_plane_pipe(struct drm_plane *plane)
 {
-	return 0;
+	struct sde_plane *sde_plane = to_sde_plane(plane);
+
+	return sde_plane->pipe;
 }
 
 /* initialize plane */
-struct drm_plane *sde_plane_init(struct drm_device *dev, bool private_plane)
+struct drm_plane *sde_plane_init(struct drm_device *dev, uint32_t pipe,
+		bool private_plane)
 {
+	static const char tmp_name[] = "---";
 	struct drm_plane *plane = NULL;
-	struct sde_plane *sde_plane;
+	struct sde_plane *psde;
+	struct sde_hw_ctl *sde_ctl;
+	struct msm_drm_private *priv;
+	struct sde_kms *kms;
+	struct sde_mdss_cfg *sde_cat;
 	int ret;
 	enum drm_plane_type type;
 
-	sde_plane = kzalloc(sizeof(*sde_plane), GFP_KERNEL);
-	if (!sde_plane) {
+	priv = dev->dev_private;
+	kms = to_sde_kms(to_mdp_kms(priv->kms));
+
+	psde = kzalloc(sizeof(*psde), GFP_KERNEL);
+	if (!psde) {
 		ret = -ENOMEM;
 		goto fail;
 	}
 
-	plane = &sde_plane->base;
+	memset(psde, 0, sizeof(*psde));
+
+	plane = &psde->base;
+
+	psde->pipe = pipe;
+	psde->name = tmp_name;
+
+	if (kms) {
+		/* mmu id for buffer mapping */
+		psde->mmu_id = kms->mmu_id;
+
+		/* check catalog for features mask */
+		sde_cat = kms->catalog;
+		if (sde_cat)
+			psde->features = sde_cat->sspp[pipe].features;
+	}
+	psde->nformats = mdp_get_formats(psde->formats,
+		ARRAY_SIZE(psde->formats),
+		!(psde->features & BIT(SDE_SSPP_CSC)) ||
+		!(psde->features & SDE_PLANE_FEATURE_SCALER));
 
 	type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
-	drm_universal_plane_init(dev, plane, 0xff, &sde_plane_funcs,
-				 sde_plane->formats, sde_plane->nformats,
-				 type);
+	ret = drm_universal_plane_init(dev, plane, 0xff, &sde_plane_funcs,
+				psde->formats, psde->nformats,
+				type);
+	if (ret)
+		goto fail;
+
+	drm_plane_helper_add(plane, &sde_plane_helper_funcs);
 
 	sde_plane_install_properties(plane, &plane->base);
 
+	psde->pipe_hw = sde_hw_sspp_init(pipe, kms->mmio, sde_cat);
+	if (IS_ERR(psde->pipe_hw)) {
+		ret = PTR_ERR(psde->pipe_hw);
+		psde->pipe_hw = NULL;
+		goto fail;
+	}
+
+	/* cache flush mask for later */
+	sde_ctl = sde_hw_ctl_init(CTL_0, kms->mmio, sde_cat);
+	if (!IS_ERR(sde_ctl)) {
+		if (sde_ctl->ops.get_bitmask_sspp)
+			sde_ctl->ops.get_bitmask_sspp(sde_ctl,
+					&psde->flush_mask, pipe);
+		sde_hw_ctl_destroy(sde_ctl);
+	}
+
+	pr_err("%s: Successfully created plane\n", __func__);
 	return plane;
 
 fail:
+	pr_err("%s: Plane creation failed\n", __func__);
 	if (plane)
 		sde_plane_destroy(plane);