drm/msm/sde: enable blending across all planes

Updates to sspp to enable dma pipe support, and adjust plane
property creation based on pipe availability. Enable support
for the BLEND_OP property within the crtc.

Change-Id: Iec063eb2a3b206fb3d66fb2a176536d7838b08bf
Signed-off-by: Clarence Ip <cip@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 00daa6b..8817a3b 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -43,6 +43,8 @@
 	struct sde_hw_sharp_cfg sharp_cfg;
 	struct sde_hw_scaler3_cfg scaler3_cfg;
 
+	const struct sde_sspp_sub_blks *pipe_sblk;
+
 	char pipe_name[SDE_NAME_SIZE];
 
 	/* debugfs related stuff */
@@ -53,12 +55,6 @@
 };
 #define to_sde_plane(x) container_of(x, struct sde_plane, base)
 
-/* macro to obtain int/enum value from plane state, no error checking */
-#define SDE_PLANE_GETINT(S, X)   ((S)->property_values[(X)])
-
-/* macro to obtain blob ptr from plane state, no error checking */
-#define SDE_PLANE_GETBLOB(S, X)  ((S)->property_blobs[(X)])
-
 static bool sde_plane_enabled(struct drm_plane_state *state)
 {
 	return state->fb && state->crtc;
@@ -74,7 +70,7 @@
 
 	if (pipe_cfg && fb && psde->pipe_hw->ops.setup_sourceaddress) {
 		/* stride */
-		if (SDE_PLANE_GETINT(pstate, PLANE_PROP_SRC_CONFIG) &
+		if (sde_plane_get_property(pstate, PLANE_PROP_SRC_CONFIG) &
 				BIT(SDE_DRM_DEINTERLACE))
 			shift = 1;
 		else
@@ -109,7 +105,7 @@
 		enum sde_hw_filter *filter, struct sde_mdp_format_params *fmt,
 		uint32_t chroma_subsampling)
 {
-	/* calcualte phase steps, leave init phase as zero */
+	/* calculate phase steps, leave init phase as zero */
 	phase_steps[SDE_SSPP_COMP_0] =
 		mult_frac(1 << PHASE_STEP_SHIFT, src, dst);
 	phase_steps[SDE_SSPP_COMP_1_2] =
@@ -224,8 +220,10 @@
 	size_t len = 0;
 	void *ret = 0;
 
-	if (pstate && (property < PLANE_PROP_BLOBCOUNT)) {
-		blob = SDE_PLANE_GETBLOB(pstate, property);
+	if (!pstate || (property >= PLANE_PROP_BLOBCOUNT)) {
+		DRM_ERROR("Invalid argument(s)\n");
+	} else {
+		blob = pstate->property_blobs[property];
 		if (blob) {
 			len = blob->length;
 			ret = &blob->data;
@@ -429,21 +427,21 @@
 
 	/* decimation */
 	psde->pipe_cfg.horz_decimation =
-		SDE_PLANE_GETINT(pstate, PLANE_PROP_H_DECIMATE);
+		sde_plane_get_property(pstate, PLANE_PROP_H_DECIMATE);
 	psde->pipe_cfg.vert_decimation =
-		SDE_PLANE_GETINT(pstate, PLANE_PROP_V_DECIMATE);
+		sde_plane_get_property(pstate, PLANE_PROP_V_DECIMATE);
 
 	/* flags */
 	DBG("Flags 0x%llX, rotation 0x%llX",
-			SDE_PLANE_GETINT(pstate, PLANE_PROP_SRC_CONFIG),
-			SDE_PLANE_GETINT(pstate, PLANE_PROP_ROTATION));
-	if (SDE_PLANE_GETINT(pstate, PLANE_PROP_ROTATION) &
+			sde_plane_get_property(pstate, PLANE_PROP_SRC_CONFIG),
+			sde_plane_get_property(pstate, PLANE_PROP_ROTATION));
+	if (sde_plane_get_property(pstate, PLANE_PROP_ROTATION) &
 		BIT(DRM_REFLECT_X))
 		src_fmt_flags |= SDE_SSPP_FLIP_LR;
-	if (SDE_PLANE_GETINT(pstate, PLANE_PROP_ROTATION) &
+	if (sde_plane_get_property(pstate, PLANE_PROP_ROTATION) &
 		BIT(DRM_REFLECT_Y))
 		src_fmt_flags |= SDE_SSPP_FLIP_UD;
-	if (SDE_PLANE_GETINT(pstate, PLANE_PROP_SRC_CONFIG) &
+	if (sde_plane_get_property(pstate, PLANE_PROP_SRC_CONFIG) &
 		BIT(SDE_DRM_DEINTERLACE)) {
 		src_h /= 2;
 		src_y  = DIV_ROUND_UP(src_y, 2);
@@ -594,8 +592,8 @@
 		}
 	}
 
-	if (psde->pipe_hw->ops.setup_sourceformat)
-		psde->pipe_hw->ops.setup_sourceformat(psde->pipe_hw,
+	if (psde->pipe_hw->ops.setup_format)
+		psde->pipe_hw->ops.setup_format(psde->pipe_hw,
 				&psde->pipe_cfg, src_fmt_flags);
 	if (psde->pipe_hw->ops.setup_rects)
 		psde->pipe_hw->ops.setup_rects(psde->pipe_hw,
@@ -831,9 +829,40 @@
 	}
 }
 
+static int _sde_plane_get_property_index(struct drm_plane *plane,
+		struct drm_property *property)
+{
+	struct drm_property **prop_array;
+	int idx = PLANE_PROP_COUNT;
+
+	if (!plane) {
+		DRM_ERROR("Invalid plane\n");
+	} else if (!plane->dev || !plane->dev->dev_private) {
+		/* don't access dev_private if !dev */
+		DRM_ERROR("Invalid device\n");
+	} else if (!property) {
+		DRM_ERROR("Incoming property is NULL\n");
+	} else {
+		prop_array = ((struct msm_drm_private *)
+				(plane->dev->dev_private))->plane_property;
+		if (!prop_array)
+			/* should never hit this */
+			DRM_ERROR("Invalid property array\n");
+
+		/* linear search is okay */
+		for (idx = 0; idx < PLANE_PROP_COUNT; ++idx) {
+			if (prop_array[idx] == property)
+				break;
+		}
+	}
+
+	return idx;
+}
+
 /* 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_mode_object *obj,
+		struct sde_mdss_cfg *catalog)
 {
 	static const struct drm_prop_enum_list e_blend_op[] = {
 		{SDE_DRM_BLEND_OP_NOT_DEFINED,    "not_defined"},
@@ -847,33 +876,29 @@
 	struct sde_plane *psde = to_sde_plane(plane);
 	struct drm_device *dev = plane->dev;
 	struct msm_drm_private *dev_priv = dev->dev_private;
-	const struct sde_sspp_sub_blks *sblk = 0;
-	const struct sde_sspp_cfg *cfg = 0;
 
 	DBG("");
 
-	if (psde && psde->pipe_hw)
-		cfg = psde->pipe_hw->cap;
-	if (cfg)
-		sblk = cfg->sblk;
-	if (!sblk) {
+	if (!psde || !psde->pipe_sblk || !catalog) {
 		DRM_ERROR("Failed to identify catalog definition\n");
 		return;
 	}
 
 	/* range properties */
-	_sde_plane_install_range_property(plane, dev, "zpos", 1, 255, 1,
+	_sde_plane_install_range_property(plane, dev, "zpos", 0, 255, 1,
 			&(dev_priv->plane_property[PLANE_PROP_ZPOS]));
 
 	_sde_plane_install_range_property(plane, dev, "alpha", 0, 255, 255,
 			&(dev_priv->plane_property[PLANE_PROP_ALPHA]));
 
+	/* max range of first pipe will be used */
 	_sde_plane_install_range_property(plane, dev, "h_decimate",
-			0, sblk->maxhdeciexp, 0,
+			0, psde->pipe_sblk->maxhdeciexp, 0,
 			&(dev_priv->plane_property[PLANE_PROP_H_DECIMATE]));
 
+	/* max range of first pipe will be used */
 	_sde_plane_install_range_property(plane, dev, "v_decimate",
-			0, sblk->maxvdeciexp, 0,
+			0, psde->pipe_sblk->maxvdeciexp, 0,
 			&(dev_priv->plane_property[PLANE_PROP_V_DECIMATE]));
 
 	_sde_plane_install_range_property(plane, dev, "sync_fence", 0, ~0, 0,
@@ -892,60 +917,56 @@
 			&(dev_priv->plane_property[PLANE_PROP_SRC_CONFIG]));
 
 	/* blob properties */
-	_sde_plane_install_blob_property(plane, dev, "scaler",
+	if (psde->features & SDE_SSPP_SCALER)
+		_sde_plane_install_blob_property(plane, dev, "scaler",
 			&(dev_priv->plane_property[PLANE_PROP_SCALER]));
-	_sde_plane_install_blob_property(plane, dev, "csc",
-			&(dev_priv->plane_property[PLANE_PROP_CSC]));
+	if (psde->features & BIT(SDE_SSPP_CSC))
+		_sde_plane_install_blob_property(plane, dev, "csc",
+				&(dev_priv->plane_property[PLANE_PROP_CSC]));
 }
 
 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 drm_property_blob *blob, **prp_blob;
-	struct msm_drm_private *dev_priv = dev->dev_private;
+	struct drm_property_blob *blob, **pr_blob;
 	int idx, ret = -EINVAL;
 
 	DBG("");
 
-	pstate = to_sde_plane_state(state);
+	idx = _sde_plane_get_property_index(plane, property);
+	if (!state) {
+		DRM_ERROR("Invalid state\n");
+	} else if (idx < PLANE_PROP_COUNT) {
+		DBG("Set property %d <= %d", idx, (int)val);
+		pstate = to_sde_plane_state(state);
 
-	for (idx = 0; idx < PLANE_PROP_COUNT && ret; ++idx) {
-		if (dev_priv->plane_property[idx] == property) {
-			DBG("Set property %d <= %d", idx, (int)val);
+		/* extra handling for incoming blob properties */
+		if ((property->flags & DRM_MODE_PROP_BLOB) &&
+			(idx < PLANE_PROP_BLOBCOUNT)) {
+			/* DRM lookup also takes a reference */
+			blob = drm_property_lookup_blob(plane->dev,
+				(uint32_t)val);
+			if (!blob) {
+				DRM_ERROR("Blob not found\n");
+				val = 0;
+			} else {
+				DBG("Blob %u saved", blob->base.id);
+				val = blob->base.id;
 
-			/* extra handling for incoming blob properties */
-			if ((property->flags & DRM_MODE_PROP_BLOB) &&
-				(idx < PLANE_PROP_BLOBCOUNT)) {
-				/* DRM lookup also takes a reference */
-				blob = drm_property_lookup_blob(dev,
-					(uint32_t)val);
-				if (!blob) {
-					DRM_ERROR("Blob not found\n");
-					val = 0;
-				} else {
-					DBG("Blob %u saved", blob->base.id);
-					val = blob->base.id;
-
-					/* save blobs for later */
-					prp_blob = &pstate->property_blobs[idx];
-					/* need to clear previous reference */
-					if (*prp_blob)
-						drm_property_unreference_blob(
-						    *prp_blob);
-					*prp_blob = blob;
-				}
+				/* save blobs for later */
+				pr_blob = &pstate->property_blobs[idx];
+				/* need to clear previous reference */
+				if (*pr_blob)
+					drm_property_unreference_blob(*pr_blob);
+				*pr_blob = blob;
 			}
-			pstate->property_values[idx] = val;
-			ret = 0;
 		}
+		pstate->property_values[idx] = val;
+		ret = 0;
 	}
 
-	if (ret == -EINVAL)
-		DRM_ERROR("Invalid property set\n");
-
 	return ret;
 }
 
@@ -955,6 +976,10 @@
 	int rc;
 
 	DBG("");
+
+	if (!plane)
+		return -EINVAL;
+
 	rc = sde_plane_atomic_set_property(plane, plane->state, property, val);
 	return rc;
 }
@@ -963,26 +988,24 @@
 		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 idx, ret = -EINVAL;
 
 	DBG("");
-	pstate = to_sde_plane_state(state);
 
-	for (idx = 0; idx < PLANE_PROP_COUNT; ++idx) {
-		if (dev_priv->plane_property[idx] == property) {
-			*val = pstate->property_values[idx];
-			DBG("Get property %d %lld", idx, *val);
-			ret = 0;
-			break;
-		}
+	idx = _sde_plane_get_property_index(plane, property);
+	if (!state) {
+		DRM_ERROR("Invalid state\n");
+	} else if (!val) {
+		DRM_ERROR("Value pointer is NULL\n");
+	} else if (idx < PLANE_PROP_COUNT) {
+		pstate = to_sde_plane_state(state);
+
+		*val = pstate->property_values[idx];
+		DBG("Get property %d %lld", idx, *val);
+		ret = 0;
 	}
 
-	if (ret == -EINVAL)
-		DRM_ERROR("Invalid property get\n");
-
 	return ret;
 }
 
@@ -997,14 +1020,14 @@
 
 		debugfs_remove_recursive(psde->debugfs_root);
 
-		if (psde->pipe_hw)
-			sde_hw_sspp_destroy(psde->pipe_hw);
-
 		drm_plane_helper_disable(plane);
 
 		/* this will destroy the states as well */
 		drm_plane_cleanup(plane);
 
+		if (psde->pipe_hw)
+			sde_hw_sspp_destroy(psde->pipe_hw);
+
 		kfree(psde);
 	}
 }
@@ -1075,7 +1098,6 @@
 
 	/* assign default blend parameters */
 	pstate->property_values[PLANE_PROP_ALPHA] = 255;
-	pstate->property_values[PLANE_PROP_PREMULTIPLIED] = 0;
 
 	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
 		pstate->property_values[PLANE_PROP_ZPOS] = STAGE_BASE;
@@ -1131,7 +1153,7 @@
 					sde_debugfs_get_root(kms));
 		if (psde->debugfs_root) {
 			/* don't error check these */
-			debugfs_create_x32("features", 0444,
+			debugfs_create_x32("features", 0644,
 					psde->debugfs_root, &psde->features);
 
 			/* add register dump support */
@@ -1162,15 +1184,19 @@
 
 /* initialize plane */
 struct drm_plane *sde_plane_init(struct drm_device *dev,
-		uint32_t pipe, bool private_plane)
+		uint32_t pipe, bool primary_plane)
 {
 	struct drm_plane *plane = NULL;
 	struct sde_plane *psde;
 	struct msm_drm_private *priv;
 	struct sde_kms *kms;
-	struct sde_mdss_cfg *sde_cat;
-	int ret;
 	enum drm_plane_type type;
+	int ret = -EINVAL;
+
+	if (!dev) {
+		DRM_ERROR("[%u]Device is NULL\n", pipe);
+		goto exit;
+	}
 
 	priv = dev->dev_private;
 	if (!priv) {
@@ -1184,61 +1210,80 @@
 	}
 	kms = to_sde_kms(priv->kms);
 
+	if (!kms->catalog) {
+		DRM_ERROR("[%u]Invalid catalog reference\n", pipe);
+		goto exit;
+	}
+
 	/* create and zero local structure */
 	psde = kzalloc(sizeof(*psde), GFP_KERNEL);
 	if (!psde) {
+		DRM_ERROR("[%u]Failed to allocate local plane struct\n", pipe);
 		ret = -ENOMEM;
-		goto fail;
+		goto exit;
 	}
 
+	/* cache local stuff for later */
 	plane = &psde->base;
-
 	psde->pipe = pipe;
+	psde->mmu_id = kms->mmu_id;
 
-	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;
+	/* initialize underlying h/w driver */
+	psde->pipe_hw = sde_hw_sspp_init(pipe, kms->mmio, kms->catalog);
+	if (IS_ERR(psde->pipe_hw)) {
+		DRM_ERROR("[%u]SSPP init failed\n", pipe);
+		ret = PTR_ERR(psde->pipe_hw);
+		goto clean_plane;
+	} else if (!psde->pipe_hw->cap || !psde->pipe_hw->cap->sblk) {
+		DRM_ERROR("[%u]SSPP init returned invalid cfg\n", pipe);
+		goto clean_sspp;
 	}
+
+	/* cache features mask for later */
+	psde->features = psde->pipe_hw->cap->features;
+	psde->pipe_sblk = psde->pipe_hw->cap->sblk;
+
+	/* add plane to DRM framework */
 	psde->nformats = mdp_get_formats(psde->formats,
 		ARRAY_SIZE(psde->formats),
 		!(psde->features & BIT(SDE_SSPP_CSC)) ||
 		!(psde->features & SDE_SSPP_SCALER));
 
-	type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
+	if (!psde->nformats) {
+		DRM_ERROR("[%u]No valid formats for plane\n", pipe);
+		goto clean_sspp;
+	}
+
+	if (psde->features & BIT(SDE_SSPP_CURSOR))
+		type = DRM_PLANE_TYPE_CURSOR;
+	else if (primary_plane)
+		type = DRM_PLANE_TYPE_PRIMARY;
+	else
+		type = DRM_PLANE_TYPE_OVERLAY;
 	ret = drm_universal_plane_init(dev, plane, 0xff, &sde_plane_funcs,
 				psde->formats, psde->nformats,
 				type);
 	if (ret)
-		goto fail;
+		goto clean_sspp;
 
+	/* success! finalize initialization */
 	drm_plane_helper_add(plane, &sde_plane_helper_funcs);
 
-	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;
-	}
-
-	_sde_plane_install_properties(plane, &plane->base);
+	_sde_plane_install_properties(plane, &plane->base, kms->catalog);
 
 	/* save user friendly pipe name for later */
 	snprintf(psde->pipe_name, SDE_NAME_SIZE, "plane%u", plane->base.id);
 
 	_sde_plane_init_debugfs(psde, kms);
 
-	DRM_INFO("Successfully created plane for %s\n", psde->pipe_name);
+	DRM_INFO("[%u]Successfully created %s\n", pipe, psde->pipe_name);
 	return plane;
 
-fail:
-	DRM_ERROR("Plane creation failed\n");
-	if (plane)
-		sde_plane_destroy(plane);
+clean_sspp:
+	if (psde && psde->pipe_hw)
+		sde_hw_sspp_destroy(psde->pipe_hw);
+clean_plane:
+	kfree(psde);
 exit:
 	return ERR_PTR(ret);
 }