drm/msm/sde: assign relative mixer stages based on zpos

Assign mixer stages based on relative zpos ordering rather than
using the zpos property value directly. This avoids problems caused
by the zpos property value being greater than the available number
of stages.

Change-Id: I9f5a5a12ba5be49fbe1fbee78ddc7fadfeaedc9d
Signed-off-by: Clarence Ip <cip@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 7086050..6436ff7 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -784,8 +784,28 @@
 struct plane_state {
 	struct sde_plane_state *sde_pstate;
 	struct drm_plane_state *drm_pstate;
+
+	int stage;
 };
 
+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;
+	int rc = 0;
+	int pa_zpos, pb_zpos;
+
+	pa_zpos = sde_plane_get_property(pa->sde_pstate, PLANE_PROP_ZPOS);
+	pb_zpos = sde_plane_get_property(pb->sde_pstate, PLANE_PROP_ZPOS);
+
+	if (pa_zpos != pb_zpos)
+		rc = pa_zpos - pb_zpos;
+	else
+		rc = pa->drm_pstate->crtc_x - pb->drm_pstate->crtc_x;
+
+	return rc;
+}
+
 static int sde_crtc_atomic_check(struct drm_crtc *crtc,
 		struct drm_crtc_state *state)
 {
@@ -820,16 +840,19 @@
 	 /* get plane state for all drm planes associated with crtc state */
 	drm_atomic_crtc_state_for_each_plane(plane, state) {
 		pstate = drm_atomic_get_plane_state(state->state, plane);
-		if (IS_ERR(pstate)) {
-			SDE_ERROR("%s: failed to get plane:%d state\n",
-					sde_crtc->name,
-					plane->base.id);
-			rc = -EINVAL;
+		if (IS_ERR_OR_NULL(pstate)) {
+			rc = PTR_ERR(pstate);
+			SDE_ERROR("%s: failed to get plane%d state, %d\n",
+					sde_crtc->name, plane->base.id, rc);
 			goto end;
 		}
+		if (cnt >= ARRAY_SIZE(pstates))
+			continue;
 
 		pstates[cnt].sde_pstate = to_sde_plane_state(pstate);
 		pstates[cnt].drm_pstate = pstate;
+		pstates[cnt].stage = sde_plane_get_property(
+				pstates[cnt].sde_pstate, PLANE_PROP_ZPOS);
 		cnt++;
 
 		if (CHECK_LAYER_BOUNDS(pstate->crtc_y, pstate->crtc_h,
@@ -845,11 +868,30 @@
 		}
 	}
 
-	for (i = 0; i < cnt; i++) {
-		z_pos = sde_plane_get_property(pstates[i].sde_pstate,
-			PLANE_PROP_ZPOS);
+	if (!sde_is_custom_client()) {
+		int stage_old = pstates[0].stage;
 
-		if (pstates[i].drm_pstate->crtc_x < mixer_width) {
+		/* assign mixer stages based on sorted zpos property */
+		sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL);
+		z_pos = 0;
+		for (i = 0; i < cnt; i++) {
+			if (stage_old != pstates[i].stage)
+				++z_pos;
+			stage_old = pstates[i].stage;
+			pstates[i].stage = z_pos;
+		}
+	}
+
+	for (i = 0; i < cnt; i++) {
+		z_pos = pstates[i].stage;
+
+		/* verify z_pos setting before using it */
+		if (z_pos >= SDE_STAGE_MAX) {
+			SDE_ERROR("> %d plane stages assigned\n",
+					SDE_STAGE_MAX - SDE_STAGE_0);
+			rc = -EINVAL;
+			goto end;
+		} else if (pstates[i].drm_pstate->crtc_x < mixer_width) {
 			if (left_crtc_zpos_cnt[z_pos] == 2) {
 				SDE_ERROR("> 2 plane @ stage%d on left\n",
 					z_pos);
@@ -866,7 +908,7 @@
 			}
 			right_crtc_zpos_cnt[z_pos]++;
 		}
-		pstates[i].sde_pstate->stage = z_pos;
+		pstates[i].sde_pstate->stage = z_pos + SDE_STAGE_0;
 		SDE_DEBUG("%s: zpos %d", sde_crtc->name, z_pos);
 	}
 
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 0b4d779..710021c 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -916,7 +916,7 @@
 static int _sde_plane_mode_set(struct drm_plane *plane,
 				struct drm_plane_state *state)
 {
-	uint32_t nplanes, src_flags, zpos, split_w;
+	uint32_t nplanes, src_flags;
 	struct sde_plane *psde;
 	struct sde_plane_state *pstate;
 	const struct sde_format *fmt;
@@ -1030,13 +1030,6 @@
 		} else if (psde->pipe_hw->ops.setup_rects) {
 			_sde_plane_setup_scaler(psde, fmt, pstate);
 
-			/* base layer source split needs update */
-			zpos = sde_plane_get_property(pstate, PLANE_PROP_ZPOS);
-			if (zpos == SDE_STAGE_BASE) {
-				split_w = get_crtc_split_width(crtc);
-				if (psde->pipe_cfg.dst_rect.x >= split_w)
-					psde->pipe_cfg.dst_rect.x -= split_w;
-			}
 			psde->pipe_hw->ops.setup_rects(psde->pipe_hw,
 					&psde->pipe_cfg, &psde->pixel_ext);
 		}
@@ -1395,7 +1388,7 @@
 
 /* helper to install properties which are common to planes and crtcs */
 static void _sde_plane_install_properties(struct drm_plane *plane,
-	u32 max_blendstages)
+	struct sde_mdss_cfg *catalog)
 {
 	static const struct drm_prop_enum_list e_blend_op[] = {
 		{SDE_DRM_BLEND_OP_NOT_DEFINED,    "not_defined"},
@@ -1409,6 +1402,8 @@
 	const struct sde_format_extended *format_list;
 	struct sde_kms_info *info;
 	struct sde_plane *psde = to_sde_plane(plane);
+	int zpos_max = 255;
+	int zpos_def = 0;
 
 	if (!plane || !psde) {
 		SDE_ERROR("invalid plane\n");
@@ -1417,10 +1412,21 @@
 		SDE_ERROR("invalid plane, pipe_hw %d pipe_sblk %d\n",
 				psde->pipe_hw != 0, psde->pipe_sblk != 0);
 		return;
+	} else if (!catalog) {
+		SDE_ERROR("invalid catalog\n");
+		return;
 	}
 
-	msm_property_install_range(&psde->property_info, "zpos", 0x0, 0,
-		max_blendstages, SDE_STAGE_BASE, PLANE_PROP_ZPOS);
+	if (sde_is_custom_client()) {
+		if (catalog->mixer_count && catalog->mixer)
+			zpos_max = catalog->mixer[0].sblk->maxblendstages;
+	} else if (plane->type != DRM_PLANE_TYPE_PRIMARY) {
+		/* reserve zpos == 0 for primary planes */
+		zpos_def = drm_plane_index(plane) + 1;
+	}
+
+	msm_property_install_range(&psde->property_info, "zpos",
+		0x0, 0, zpos_max, zpos_def, PLANE_PROP_ZPOS);
 
 	msm_property_install_range(&psde->property_info, "alpha",
 		0x0, 0, 255, 255, PLANE_PROP_ALPHA);
@@ -1887,7 +1893,7 @@
 	struct msm_drm_private *priv;
 	struct sde_kms *kms;
 	enum drm_plane_type type;
-	int ret = -EINVAL, max_blendstages = 255;
+	int ret = -EINVAL;
 
 	if (!dev) {
 		SDE_ERROR("[%u]device is NULL\n", pipe);
@@ -1943,9 +1949,6 @@
 		goto clean_sspp;
 	}
 
-	if (kms->catalog && kms->catalog->mixer_count && kms->catalog->mixer)
-		max_blendstages = kms->catalog->mixer[0].sblk->maxblendstages;
-
 	/* add plane to DRM framework */
 	psde->nformats = sde_populate_formats(psde->pipe_sblk->format_list,
 			psde->formats,
@@ -1976,7 +1979,7 @@
 			PLANE_PROP_COUNT, PLANE_PROP_BLOBCOUNT,
 			sizeof(struct sde_plane_state));
 
-	_sde_plane_install_properties(plane, max_blendstages);
+	_sde_plane_install_properties(plane, kms->catalog);
 
 	/* save user friendly pipe name for later */
 	snprintf(psde->pipe_name, SDE_NAME_SIZE, "plane%u", plane->base.id);