drm/msm/sde: add planes color fill/fence timeout

To prepare for input fence support, add a property to configure the
input fence timeout. Also, enable support for color fill (through
a plane property) so that it can be enabled if input fences ever time
out.

Change-Id: Iae79ebadb4731db444f39d3b7207b65cb2aa9243
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 6666532..fa35b7a 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -92,10 +92,12 @@
 	DBG("0x%llX", fd);
 }
 
-void *sde_plane_get_sync_fence(struct drm_plane *plane)
+int sde_plane_wait_sync_fence(struct drm_plane *plane)
 {
 	struct sde_plane_state *pstate;
-	void *ret = NULL;
+	void *sync_fence;
+	long wait_ms;
+	int ret = -EINVAL;
 
 	if (!plane) {
 		DRM_ERROR("Invalid plane\n");
@@ -103,11 +105,24 @@
 		DRM_ERROR("Invalid plane state\n");
 	} else {
 		pstate = to_sde_plane_state(plane->state);
-		ret = pstate->sync_fence;
+		sync_fence = pstate->sync_fence;
 
-		DBG("%s", to_sde_plane(plane)->pipe_name);
+		if (sync_fence) {
+			wait_ms = (long)sde_plane_get_property(pstate,
+					PLANE_PROP_SYNC_FENCE_TIMEOUT);
+
+			DBG("%s", to_sde_plane(plane)->pipe_name);
+			ret = sde_sync_wait(sync_fence, wait_ms);
+			if (!ret)
+				DBG("signaled");
+			else if (ret == -ETIME)
+				DRM_ERROR("timeout\n");
+			else
+				DRM_ERROR("error %d\n", ret);
+		} else {
+			ret = 0;
+		}
 	}
-
 	return ret;
 }
 
@@ -148,7 +163,7 @@
 	}
 }
 
-static void _sde_plane_setup_scaler3(struct drm_plane *plane,
+static void _sde_plane_setup_scaler3(struct sde_plane *psde,
 		uint32_t src_w, uint32_t src_h, uint32_t dst_w, uint32_t dst_h,
 		struct sde_hw_scaler3_cfg *scale_cfg,
 		struct sde_mdp_format_params *fmt,
@@ -156,11 +171,28 @@
 {
 }
 
-static void _sde_plane_setup_scaler2(struct drm_plane *plane,
+/**
+ * _sde_plane_setup_scaler2(): Determine default scaler phase steps/filter type
+ * @psde: Pointer to SDE plane object
+ * @src: Source size
+ * @dst: Destination size
+ * @phase_steps: Pointer to output array for phase steps
+ * @filter: Pointer to output array for filter type
+ * @fmt: Pointer to format definition
+ * @chroma_subsampling: Subsampling amount for chroma channel
+ *
+ * Returns: 0 on success
+ */
+static int _sde_plane_setup_scaler2(struct sde_plane *psde,
 		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)
 {
+	if (!psde || !phase_steps || !filter || !fmt) {
+		DRM_ERROR("Invalid arguments\n");
+		return -EINVAL;
+	}
+
 	/* calculate phase steps, leave init phase as zero */
 	phase_steps[SDE_SSPP_COMP_0] =
 		mult_frac(1 << PHASE_STEP_SHIFT, src, dst);
@@ -185,13 +217,30 @@
 		}
 	} else {
 		/* disable scaler */
+		DBG("Disable scaler");
 		filter[SDE_SSPP_COMP_0] = SDE_MDP_SCALE_FILTER_MAX;
 		filter[SDE_SSPP_COMP_1_2] = SDE_MDP_SCALE_FILTER_MAX;
 		filter[SDE_SSPP_COMP_3] = SDE_MDP_SCALE_FILTER_MAX;
 	}
+	return 0;
 }
 
-static void _sde_plane_setup_pixel_ext(struct drm_plane *plane,
+/**
+ * _sde_plane_setup_pixel_ext - determine default pixel extension values
+ * @psde: Pointer to SDE plane object
+ * @src: Source size
+ * @dst: Destination size
+ * @decimated_src: Source size after decimation, if any
+ * @phase_steps: Pointer to output array for phase steps
+ * @out_src: Output array for pixel extension values
+ * @out_edge1: Output array for pixel extension first edge
+ * @out_edge2: Output array for pixel extension second edge
+ * @filter: Pointer to array for filter type
+ * @fmt: Pointer to format definition
+ * @chroma_subsampling: Subsampling amount for chroma channel
+ * @post_compare: Whether to chroma subsampled source size for comparisions
+ */
+static void _sde_plane_setup_pixel_ext(struct sde_plane *psde,
 		uint32_t src, uint32_t dst, uint32_t decimated_src,
 		uint32_t *phase_steps, uint32_t *out_src, int *out_edge1,
 		int *out_edge2, enum sde_hw_filter *filter,
@@ -202,7 +251,7 @@
 	uint32_t src_work;
 	int i, tmp;
 
-	if (plane && phase_steps && out_src && out_edge1 &&
+	if (psde && phase_steps && out_src && out_edge1 &&
 			out_edge2 && filter && fmt) {
 		/* handle CAF for YUV formats */
 		if (SDE_FORMAT_IS_YUV(fmt) &&
@@ -429,95 +478,20 @@
 	psde->pipe_hw->ops.setup_csc(psde->pipe_hw, psde->csc_ptr);
 }
 
-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)
+static void _sde_plane_setup_scaler(struct sde_plane *psde,
+		struct sde_mdp_format_params *fmt,
+		struct sde_plane_state *pstate)
 {
-	struct sde_plane *psde;
-	struct sde_plane_state *pstate;
-	const struct mdp_format *format;
-	uint32_t nplanes, pix_format, tmp;
-	uint32_t chroma_subsmpl_h, chroma_subsmpl_v;
-	uint32_t src_fmt_flags;
-	int i;
-	struct sde_mdp_format_params *fmt;
-	struct sde_hw_pixel_ext *pe;
-	size_t sc_u_size = 0;
+	struct sde_hw_pixel_ext *pe = NULL;
 	struct sde_drm_scaler *sc_u = NULL;
 	struct sde_drm_scaler_v1 *sc_u1 = NULL;
+	size_t sc_u_size = 0;
+	uint32_t chroma_subsmpl_h, chroma_subsmpl_v;
+	uint32_t tmp;
+	int i;
 
-	DBG("");
-
-	if (!plane || !plane->state) {
-		DRM_ERROR("Invalid plane/state\n");
-		return -EINVAL;
-	}
-	if (!crtc || !fb) {
-		DRM_ERROR("Invalid crtc/fb\n");
-		return -EINVAL;
-	}
-
-	psde = to_sde_plane(plane);
-	pstate = to_sde_plane_state(plane->state);
-	nplanes = drm_format_num_planes(fb->pixel_format);
-
-	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->pipe_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));
-	src_fmt_flags = 0;
-
-	psde->pipe_cfg.src.format = sde_mdp_get_format_params(pix_format,
-			fb->modifier[0]);
-	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, pstate, &psde->pipe_cfg, fb);
-
-	/* flags */
-	DBG("Flags 0x%llX, rotation 0x%llX",
-			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_get_property(pstate, PLANE_PROP_ROTATION) &
-		BIT(DRM_REFLECT_Y))
-		src_fmt_flags |= SDE_SSPP_FLIP_UD;
-	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);
-		src_y &= ~0x1;
-	}
-
-	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;
-
-	/* get sde pixel format definition */
-	fmt = psde->pipe_cfg.src.format;
+	if (!psde || !fmt)
+		return;
 
 	pe = &(psde->pixel_ext);
 	memset(pe, 0, sizeof(struct sde_hw_pixel_ext));
@@ -543,21 +517,28 @@
 	if (sc_u1 && (sc_u1->enable & SDE_DRM_SCALER_DECIMATE)) {
 		psde->pipe_cfg.horz_decimation = sc_u1->horz_decimate;
 		psde->pipe_cfg.vert_decimation = sc_u1->vert_decimate;
+	} else {
+		psde->pipe_cfg.horz_decimation = 0;
+		psde->pipe_cfg.vert_decimation = 0;
 	}
 
 	/* don't chroma subsample if decimating */
 	chroma_subsmpl_h = psde->pipe_cfg.horz_decimation ? 1 :
-		drm_format_horz_chroma_subsampling(pix_format);
+		drm_format_horz_chroma_subsampling(fmt->format);
 	chroma_subsmpl_v = psde->pipe_cfg.vert_decimation ? 1 :
-		drm_format_vert_chroma_subsampling(pix_format);
+		drm_format_vert_chroma_subsampling(fmt->format);
 
 	/* update scaler */
 	if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) {
 		if (sc_u1 && (sc_u1->enable & SDE_DRM_SCALER_SCALER_3))
-			DBG("QSEED3 blob detected");
+			DBG("SCALER3 blob detected");
 		else
-			_sde_plane_setup_scaler3(plane, src_w, src_h, crtc_w,
-					crtc_h, &psde->scaler3_cfg, fmt,
+			_sde_plane_setup_scaler3(psde,
+					psde->pipe_cfg.src_rect.w,
+					psde->pipe_cfg.src_rect.h,
+					psde->pipe_cfg.dst_rect.w,
+					psde->pipe_cfg.dst_rect.h,
+					&psde->scaler3_cfg, fmt,
 					chroma_subsmpl_h, chroma_subsmpl_v);
 	} else {
 		/* always calculate basic scaler config */
@@ -574,10 +555,14 @@
 			}
 		} else {
 			/* calculate phase steps */
-			_sde_plane_setup_scaler2(plane, src_w, crtc_w,
+			_sde_plane_setup_scaler2(psde,
+					psde->pipe_cfg.src_rect.w,
+					psde->pipe_cfg.dst_rect.w,
 					pe->phase_step_x,
 					pe->horz_filter, fmt, chroma_subsmpl_h);
-			_sde_plane_setup_scaler2(plane, src_h, crtc_h,
+			_sde_plane_setup_scaler2(psde,
+					psde->pipe_cfg.src_rect.h,
+					psde->pipe_cfg.dst_rect.h,
 					pe->phase_step_y,
 					pe->vert_filter, fmt, chroma_subsmpl_v);
 		}
@@ -586,6 +571,7 @@
 	/* update pixel extensions */
 	if (sc_u1 && (sc_u1->enable & SDE_DRM_SCALER_PIX_EXT)) {
 		/* populate from user space */
+		DBG("PIXEXT blob detected");
 		for (i = 0; i < SDE_MAX_PLANES; i++) {
 			pe->num_ext_pxls_left[i] = sc_u1->lr.num_pxls_start[i];
 			pe->num_ext_pxls_right[i] = sc_u1->lr.num_pxls_end[i];
@@ -605,20 +591,22 @@
 		}
 	} else {
 		/* calculate left/right/top/bottom pixel extensions */
-		tmp = DECIMATED_DIMENSION(src_w,
+		tmp = DECIMATED_DIMENSION(psde->pipe_cfg.src_rect.w,
 				psde->pipe_cfg.horz_decimation);
 		if (SDE_FORMAT_IS_YUV(fmt))
 			tmp &= ~0x1;
-		_sde_plane_setup_pixel_ext(plane, src_w, crtc_w, tmp,
+		_sde_plane_setup_pixel_ext(psde, psde->pipe_cfg.src_rect.w,
+				psde->pipe_cfg.dst_rect.w, tmp,
 				pe->phase_step_x,
 				pe->roi_w,
 				pe->num_ext_pxls_left,
 				pe->num_ext_pxls_right, pe->horz_filter, fmt,
 				chroma_subsmpl_h, 0);
 
-		tmp = DECIMATED_DIMENSION(src_h,
+		tmp = DECIMATED_DIMENSION(psde->pipe_cfg.src_rect.h,
 				psde->pipe_cfg.vert_decimation);
-		_sde_plane_setup_pixel_ext(plane, src_h, crtc_h, tmp,
+		_sde_plane_setup_pixel_ext(psde, psde->pipe_cfg.src_rect.h,
+				psde->pipe_cfg.dst_rect.h, tmp,
 				pe->phase_step_y,
 				pe->roi_h,
 				pe->num_ext_pxls_top,
@@ -655,10 +643,152 @@
 					pe->num_ext_pxls_btm[i];
 		}
 	}
+}
+
+int sde_plane_color_fill(struct drm_plane *plane,
+		uint32_t color, uint32_t alpha)
+{
+	struct sde_plane *psde;
+	struct sde_mdp_format_params *fmt;
+
+	if (!plane) {
+		DRM_ERROR("Invalid plane\n");
+		return -EINVAL;
+	}
+
+	psde = to_sde_plane(plane);
+	if (!psde->pipe_hw) {
+		DRM_ERROR("Invalid plane h/w pointer\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * select fill format to match user property expectation,
+	 * h/w only supports RGB variants
+	 */
+	fmt = sde_mdp_get_format_params(DRM_FORMAT_ABGR8888, 0);
+
+	/* update sspp */
+	if (fmt && psde->pipe_hw->ops.setup_solidfill) {
+		psde->pipe_hw->ops.setup_solidfill(psde->pipe_hw,
+				(color & 0xFFFFFF) | ((alpha & 0xFF) << 24));
+
+		/* override scaler/decimation if solid fill */
+		psde->pipe_cfg.src_rect.x = 0;
+		psde->pipe_cfg.src_rect.y = 0;
+		psde->pipe_cfg.src_rect.w = psde->pipe_cfg.dst_rect.w;
+		psde->pipe_cfg.src_rect.h = psde->pipe_cfg.dst_rect.h;
+
+		_sde_plane_setup_scaler(psde, fmt, 0);
+
+		if (psde->pipe_hw->ops.setup_format)
+			psde->pipe_hw->ops.setup_format(psde->pipe_hw,
+					fmt, SDE_SSPP_SOLID_FILL);
+
+		if (psde->pipe_hw->ops.setup_rects)
+			psde->pipe_hw->ops.setup_rects(psde->pipe_hw,
+					&psde->pipe_cfg, &psde->pixel_ext);
+	}
+
+	return 0;
+}
+
+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;
+	struct sde_plane_state *pstate;
+	const struct mdp_format *format;
+	uint32_t nplanes, tmp;
+	uint32_t src_flags;
+	struct sde_mdp_format_params *fmt;
+
+	DBG("");
+
+	if (!plane || !plane->state) {
+		DRM_ERROR("Invalid plane/state\n");
+		return -EINVAL;
+	}
+	if (!crtc || !fb) {
+		DRM_ERROR("Invalid crtc/fb\n");
+		return -EINVAL;
+	}
+
+	psde = to_sde_plane(plane);
+	pstate = to_sde_plane_state(plane->state);
+	nplanes = drm_format_num_planes(fb->pixel_format);
+
+	format = to_mdp_format(msm_framebuffer_format(fb));
+	tmp = 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->pipe_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));
+	src_flags = 0;
+
+	psde->pipe_cfg.src.format = sde_mdp_get_format_params(tmp,
+			fb->modifier[0]);
+	psde->pipe_cfg.src.width = fb->width;
+	psde->pipe_cfg.src.height = fb->height;
+	psde->pipe_cfg.src.num_planes = nplanes;
+
+	/* flags */
+	DBG("Flags 0x%llX, rotation 0x%llX",
+			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_flags |= SDE_SSPP_FLIP_LR;
+	if (sde_plane_get_property(pstate, PLANE_PROP_ROTATION) &
+		BIT(DRM_REFLECT_Y))
+		src_flags |= SDE_SSPP_FLIP_UD;
+	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);
+		src_y &= ~0x1;
+	}
+
+	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;
+
+	/* get sde pixel format definition */
+	fmt = psde->pipe_cfg.src.format;
+
+	/* check for color fill */
+	tmp = (uint32_t)sde_plane_get_property(pstate, PLANE_PROP_COLOR_FILL);
+	if (tmp & BIT(31)) {
+		/* force 100% alpha, stop other processing */
+		return sde_plane_color_fill(plane, tmp, 0xFF);
+	}
+
+	_sde_plane_set_scanout(plane, pstate, &psde->pipe_cfg, fb);
+
+	_sde_plane_setup_scaler(psde, fmt, pstate);
 
 	if (psde->pipe_hw->ops.setup_format)
 		psde->pipe_hw->ops.setup_format(psde->pipe_hw,
-				&psde->pipe_cfg, src_fmt_flags);
+				fmt, src_flags);
 	if (psde->pipe_hw->ops.setup_rects)
 		psde->pipe_hw->ops.setup_rects(psde->pipe_hw,
 				&psde->pipe_cfg, &psde->pixel_ext);
@@ -1134,8 +1264,8 @@
 
 	DBG("");
 
-	if (!psde || !psde->pipe_sblk || !catalog) {
-		DRM_ERROR("Failed to identify catalog definition\n");
+	if (!psde || !psde->pipe_hw || !psde->pipe_sblk || !catalog) {
+		DRM_ERROR("Catalog or h/w driver definition error\n");
 		return;
 	}
 
@@ -1148,9 +1278,18 @@
 	_sde_plane_install_range_property(plane, dev, "alpha", 0, 255, 255,
 			PLANE_PROP_ALPHA);
 
+	if (psde->pipe_hw->ops.setup_solidfill)
+		_sde_plane_install_range_property(plane, dev, "color_fill",
+				0, 0xFFFFFFFF, 0,
+				PLANE_PROP_COLOR_FILL);
+
 	_sde_plane_install_range_property(plane, dev, "sync_fence", 0, ~0, ~0,
 			PLANE_PROP_SYNC_FENCE);
 
+	_sde_plane_install_range_property(plane, dev, "sync_fence_timeout",
+			0, ~0, 10000,
+			PLANE_PROP_SYNC_FENCE_TIMEOUT);
+
 	/* standard properties */
 	_sde_plane_install_rotation_property(plane, dev, PLANE_PROP_ROTATION);