drm/msm/sde: updates to planes atomic_check

Add additional checks for in/out rectangles.

Change-Id: Ie50f6dd23135353a71f7316b5ace06786160b669
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 07d4492..6666532 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -72,7 +72,7 @@
 
 static bool sde_plane_enabled(struct drm_plane_state *state)
 {
-	return state->fb && state->crtc;
+	return state && state->fb && state->crtc;
 }
 
 /* helper to update a state's sync fence pointer from the property */
@@ -170,12 +170,12 @@
 	phase_steps[SDE_SSPP_COMP_3] = phase_steps[SDE_SSPP_COMP_0];
 
 	/* calculate scaler config, if necessary */
-	if (fmt->is_yuv || src != dst) {
+	if (SDE_FORMAT_IS_YUV(fmt) || src != dst) {
 		filter[SDE_SSPP_COMP_3] =
 			(src <= dst) ? SDE_MDP_SCALE_FILTER_BIL :
 			SDE_MDP_SCALE_FILTER_PCMN;
 
-		if (fmt->is_yuv) {
+		if (SDE_FORMAT_IS_YUV(fmt)) {
 			filter[SDE_SSPP_COMP_0] = SDE_MDP_SCALE_FILTER_CA;
 			filter[SDE_SSPP_COMP_1_2] = filter[SDE_SSPP_COMP_3];
 		} else {
@@ -205,7 +205,8 @@
 	if (plane && phase_steps && out_src && out_edge1 &&
 			out_edge2 && filter && fmt) {
 		/* handle CAF for YUV formats */
-		if (fmt->is_yuv && SDE_MDP_SCALE_FILTER_CA == *filter)
+		if (SDE_FORMAT_IS_YUV(fmt) &&
+				*filter == SDE_MDP_SCALE_FILTER_CA)
 			caf = PHASE_STEP_UNIT_SCALE;
 		else
 			caf = 0;
@@ -216,7 +217,7 @@
 				src_work /= chroma_subsampling;
 			if (post_compare)
 				src = src_work;
-			if (!(fmt->is_yuv) && (src == dst)) {
+			if (!SDE_FORMAT_IS_YUV(fmt) && (src == dst)) {
 				/* unity */
 				edge1 = 0;
 				edge2 = 0;
@@ -419,7 +420,7 @@
 		DBG("User blobs override for CSC");
 		psde->csc_ptr = &psde->csc_cfg;
 	/* revert to kernel default */
-	} else if (fmt->is_yuv) {
+	} else if (SDE_FORMAT_IS_YUV(fmt)) {
 		psde->csc_ptr = (struct sde_csc_cfg *)&sde_csc_YUV2RGB_601L;
 	} else {
 		psde->csc_ptr = (struct sde_csc_cfg *)&sde_csc_NOP;
@@ -606,7 +607,7 @@
 		/* calculate left/right/top/bottom pixel extensions */
 		tmp = DECIMATED_DIMENSION(src_w,
 				psde->pipe_cfg.horz_decimation);
-		if (fmt->is_yuv)
+		if (SDE_FORMAT_IS_YUV(fmt))
 			tmp &= ~0x1;
 		_sde_plane_setup_pixel_ext(plane, src_w, crtc_w, tmp,
 				pe->phase_step_x,
@@ -673,7 +674,7 @@
 			&psde->sharp_cfg);
 
 	/* update csc */
-	if (fmt->is_yuv)
+	if (SDE_FORMAT_IS_YUV(fmt))
 		_sde_plane_setup_csc(psde, pstate, fmt);
 
 	return 0;
@@ -705,67 +706,211 @@
 	msm_framebuffer_cleanup(fb, psde->mmu_id);
 }
 
+static int _sde_plane_atomic_check_fb(struct sde_plane *psde,
+		struct sde_plane_state *pstate,
+		struct drm_framebuffer *fb)
+{
+	return 0;
+}
+
 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;
+	struct sde_plane *psde;
+	struct sde_plane_state *pstate;
+	struct drm_plane_state *old_state;
 	const struct mdp_format *format;
+	struct sde_mdp_format_params *fmt;
+	size_t sc_u_size = 0;
+	struct sde_drm_scaler *sc_u = NULL;
+	int ret = 0;
 
+	uint32_t src_x, src_y;
+	uint32_t src_w, src_h;
+	uint32_t deci_w, deci_h, src_deci_w, src_deci_h;
+	uint32_t src_max_x, src_max_y, src_max_w, src_max_h;
+	uint32_t upscale_max, downscale_max;
+
+	DBG();
+
+	if (!plane || !state) {
+		DRM_ERROR("Invalid plane/state\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	psde = to_sde_plane(plane);
+	pstate = to_sde_plane_state(state);
+	old_state = plane->state;
+
+	if (!psde->pipe_sblk) {
+		DRM_ERROR("Invalid plane catalog\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	/* get decimation config from user space */
+	deci_w = 0;
+	deci_h = 0;
+	sc_u = _sde_plane_get_blob(pstate, PLANE_PROP_SCALER, &sc_u_size);
+	if (sc_u) {
+		switch (sc_u->version) {
+		case SDE_DRM_SCALER_V1:
+			if (!_sde_plane_verify_blob(sc_u,
+					sc_u_size,
+					&sc_u->v1,
+					sizeof(struct sde_drm_scaler_v1))) {
+				deci_w = sc_u->v1.horz_decimate;
+				deci_h = sc_u->v1.vert_decimate;
+			}
+			break;
+		default:
+			DBG("Unrecognized scaler blob v%lld", sc_u->version);
+			break;
+		}
+	}
+
+	/* src values are in Q16 fixed point, convert to integer */
+	src_x = state->src_x >> 16;
+	src_y = state->src_y >> 16;
+	src_w = state->src_w >> 16;
+	src_h = state->src_h >> 16;
+
+	src_deci_w = DECIMATED_DIMENSION(src_w, deci_w);
+	src_deci_h = DECIMATED_DIMENSION(src_h, deci_h);
+
+	src_max_x = 0xFFFF;
+	src_max_y = 0xFFFF;
+	src_max_w = 0x3FFF;
+	src_max_h = 0x3FFF;
+	upscale_max   = psde->pipe_sblk->maxupscale;
+	downscale_max = psde->pipe_sblk->maxdwnscale;
+
+	/*
+	 * Including checks from mdss
+	 * - mdss_mdp_overlay_req_check()
+	 */
 	DBG("%s: check (%d -> %d)", psde->pipe_name,
 			sde_plane_enabled(old_state), sde_plane_enabled(state));
 
 	if (sde_plane_enabled(state)) {
-		/* CIFIX: don't use mdp format? */
+		/* determine SDE format definition. State's fb is valid here. */
 		format = to_mdp_format(msm_framebuffer_format(state->fb));
-		if (MDP_FORMAT_IS_YUV(format) &&
+		fmt = sde_mdp_get_format_params(format->base.pixel_format,
+				0 /* modifier */);
+
+		/* don't check for other errors after first failure */
+		if (SDE_FORMAT_IS_YUV(fmt) &&
 			(!(psde->features & SDE_SSPP_SCALER) ||
 			 !(psde->features & BIT(SDE_SSPP_CSC)))) {
 			DRM_ERROR("Pipe doesn't support YUV\n");
+			ret = -EINVAL;
 
-			return -EINVAL;
+		/* verify source size/region */
+		} else if (!src_w || !src_h ||
+			(src_w > src_max_w) || (src_h > src_max_h) ||
+			(src_x > src_max_x) || (src_y > src_max_y) ||
+			(src_x + src_w > src_max_x) ||
+			(src_y + src_h > src_max_y)) {
+			DRM_ERROR("Invalid source (%u, %u) -> (%u, %u)\n",
+					src_x, src_y, src_x + src_w,
+					src_y + src_h);
+			ret = -EINVAL;
+
+		/* require even source for YUV */
+		} else if (SDE_FORMAT_IS_YUV(fmt) &&
+				((src_x & 0x1) || (src_y & 0x1) ||
+				 (src_w & 0x1) || (src_h & 0x1))) {
+			DRM_ERROR("Invalid odd src res/pos for YUV\n");
+			ret = -EINVAL;
+
+		/* verify scaler requirements */
+		} else if (!(psde->features & SDE_SSPP_SCALER) &&
+			((src_w != state->crtc_w) ||
+			 (src_h != state->crtc_h))) {
+			DRM_ERROR("Pipe doesn't support scaling %ux%u->%ux%u\n",
+					src_w, src_h, state->crtc_w,
+					state->crtc_h);
+			ret = -EINVAL;
+
+		/* check decimated source width */
+		} else if (src_deci_w > psde->pipe_sblk->maxlinewidth) {
+			DRM_ERROR("Invalid source [W:%u, Wd:%u] > %u\n",
+					src_w, src_deci_w,
+					psde->pipe_sblk->maxlinewidth);
+			ret = -EINVAL;
+
+		/* check max scaler capability */
+		} else if (((src_deci_w * upscale_max) < state->crtc_w) ||
+			((src_deci_h * upscale_max) < state->crtc_h) ||
+			((state->crtc_w * downscale_max) < src_deci_w) ||
+			((state->crtc_h * downscale_max) < src_deci_h)) {
+			DRM_ERROR("Too much scaling requested %ux%u -> %ux%u\n",
+					src_deci_w, src_deci_h,
+					state->crtc_w, state->crtc_h);
+			ret = -EINVAL;
+
+		/* check frame buffer */
+		} else if (_sde_plane_atomic_check_fb(
+				psde, pstate, state->fb)) {
+			ret = -EINVAL;
 		}
 
-		if (!(psde->features & SDE_SSPP_SCALER) &&
-			(((state->src_w >> 16) != state->crtc_w) ||
-			((state->src_h >> 16) != state->crtc_h))) {
-			DRM_ERROR(
-				"Unsupported Pipe scaling (%dx%d -> %dx%d)\n",
-				state->src_w >> 16, state->src_h >> 16,
-				state->crtc_w, state->crtc_h);
-
-			return -EINVAL;
+		/* check decimation (and bwc/fetch mode) */
+		if (!ret && (deci_w || deci_h)) {
+			if (SDE_FORMAT_IS_UBWC(fmt)) {
+				DRM_ERROR("No decimation with BWC\n");
+				ret = -EINVAL;
+			} else if ((deci_w > psde->pipe_sblk->maxhdeciexp) ||
+				(deci_h > psde->pipe_sblk->maxvdeciexp)) {
+				DRM_ERROR("Too much decimation requested\n");
+				ret = -EINVAL;
+			} else if (fmt->fetch_mode != SDE_MDP_FETCH_LINEAR) {
+				DRM_ERROR("Decimation requires linear fetch\n");
+				ret = -EINVAL;
+			}
 		}
 	}
 
-	if (sde_plane_enabled(state) && sde_plane_enabled(old_state)) {
-		/* we cannot change SMP block configuration during scanout: */
-		bool full_modeset = false;
+	if (!ret) {
+		if (sde_plane_enabled(state) &&
+			sde_plane_enabled(old_state)) {
+			bool full_modeset = false;
 
-		if (state->fb->pixel_format != old_state->fb->pixel_format) {
-			DBG("%s: pixel_format change!", psde->pipe_name);
-			full_modeset = true;
-		}
-		if (state->src_w != old_state->src_w) {
-			DBG("%s: src_w change!", psde->pipe_name);
-			full_modeset = true;
-		}
-		if (to_sde_plane_state(old_state)->pending) {
-			DBG("%s: still pending!", psde->pipe_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;
+			if (state->fb->pixel_format !=
+				old_state->fb->pixel_format) {
+				DBG("%s: format change!", psde->pipe_name);
+				full_modeset = true;
+			}
+			if (state->src_w != old_state->src_w ||
+				state->src_h != old_state->src_h) {
+				DBG("%s: src_w change!", psde->pipe_name);
+				full_modeset = true;
+			}
+			if (to_sde_plane_state(old_state)->pending) {
+				DBG("%s: still pending!", psde->pipe_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;
 		}
-	} else {
-		to_sde_plane_state(state)->mode_changed = true;
 	}
 
-	return 0;
+exit:
+	return ret;
+}
+
+void sde_plane_complete_flip(struct drm_plane *plane)
+{
+	if (plane && plane->state)
+		to_sde_plane_state(plane->state)->pending = false;
 }
 
 static void sde_plane_atomic_update(struct drm_plane *plane,