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/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);