drm/msm/sde: enable explicit input fences

Enable color fill if input fence times out. Move CSC/solid fill
updates to the flush point of the commit. Move sde_plane_flush()
call to execute before crtc_flush_all().

Change-Id: I9fd1fa5b98dae509e8957ef0300aa00735903fdd
Signed-off-by: Clarence Ip <cip@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 0f71530..b477423 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -82,7 +82,7 @@
 	PLANE_PROP_ZPOS = PLANE_PROP_BLOBCOUNT,
 	PLANE_PROP_ALPHA,
 	PLANE_PROP_COLOR_FILL,
-	PLANE_PROP_SYNC_FENCE,
+	PLANE_PROP_INPUT_FENCE,
 
 	/* enum/bitmask properties */
 	PLANE_PROP_ROTATION,
@@ -98,7 +98,7 @@
 	CRTC_PROP_BLOBCOUNT,
 
 	/* range properties */
-	CRTC_PROP_SYNC_FENCE_TIMEOUT = CRTC_PROP_BLOBCOUNT,
+	CRTC_PROP_INPUT_FENCE_TIMEOUT = CRTC_PROP_BLOBCOUNT,
 
 	/* total # of properties */
 	CRTC_PROP_COUNT
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index e2d0db2..76d7645 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -12,6 +12,7 @@
 
 #include <linux/sort.h>
 #include <linux/debugfs.h>
+#include <linux/ktime.h>
 #include <uapi/drm/sde_drm.h>
 #include <drm/drm_mode.h>
 #include <drm/drm_crtc.h>
@@ -31,6 +32,9 @@
 /*#define DBG_IRQ      DBG*/
 #define DBG_IRQ(fmt, ...)
 
+/* default input fence timeout, in ms */
+#define SDE_CRTC_INPUT_FENCE_TIMEOUT    2000
+
 static struct sde_kms *get_kms(struct drm_crtc *crtc)
 {
 	struct msm_drm_private *priv = crtc->dev->dev_private;
@@ -452,15 +456,82 @@
 	return 0;
 }
 
+/**
+ * _sde_crtc_set_input_fence_timeout - update ns version of in fence timeout
+ * @cstate: Pointer to sde crtc state
+ */
+static void _sde_crtc_set_input_fence_timeout(struct sde_crtc_state *cstate)
+{
+	if (!cstate) {
+		DRM_ERROR("invalid cstate\n");
+		return;
+	}
+	cstate->input_fence_timeout_ns =
+		sde_crtc_get_property(cstate, CRTC_PROP_INPUT_FENCE_TIMEOUT);
+	cstate->input_fence_timeout_ns *= NSEC_PER_MSEC;
+}
+
+/**
+ * _sde_crtc_wait_for_fences - wait for incoming framebuffer sync fences
+ * @crtc: Pointer to CRTC object
+ */
+static void _sde_crtc_wait_for_fences(struct drm_crtc *crtc)
+{
+	struct drm_plane *plane = NULL;
+	uint32_t wait_ms = 1;
+	u64 ktime_end;
+	s64 ktime_wait; /* need signed 64-bit type */
+
+	DBG("");
+
+	if (!crtc || !crtc->state) {
+		DRM_ERROR("invalid crtc/state %pK\n", crtc);
+		return;
+	}
+
+	/* use monotonic timer to limit total fence wait time */
+	ktime_end = ktime_get_ns() +
+		to_sde_crtc_state(crtc->state)->input_fence_timeout_ns;
+
+	/*
+	 * Wait for fences sequentially, as all of them need to be signalled
+	 * before we can proceed.
+	 *
+	 * Limit total wait time to INPUT_FENCE_TIMEOUT, but still call
+	 * sde_plane_wait_input_fence with wait_ms == 0 after the timeout so
+	 * that each plane can check its fence status and react appropriately
+	 * if its fence has timed out.
+	 */
+	drm_atomic_crtc_for_each_plane(plane, crtc) {
+		if (wait_ms) {
+			/* determine updated wait time */
+			ktime_wait = ktime_end - ktime_get_ns();
+			if (ktime_wait >= 0)
+				wait_ms = ktime_wait / NSEC_PER_MSEC;
+			else
+				wait_ms = 0;
+		}
+		sde_plane_wait_input_fence(plane, wait_ms);
+	}
+}
+
 static void sde_crtc_atomic_begin(struct drm_crtc *crtc,
 		struct drm_crtc_state *old_crtc_state)
 {
-	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
-	struct drm_device *dev = crtc->dev;
+	struct sde_crtc *sde_crtc;
+	struct drm_device *dev;
 	unsigned long flags;
 
 	DBG("");
 
+	if (!crtc) {
+		DRM_ERROR("invalid crtc\n");
+		return;
+	}
+
+	sde_crtc = to_sde_crtc(crtc);
+	dev = crtc->dev;
+
 	if (sde_crtc->event) {
 		WARN_ON(sde_crtc->event);
 	} else {
@@ -499,11 +570,22 @@
 static void sde_crtc_atomic_flush(struct drm_crtc *crtc,
 		struct drm_crtc_state *old_crtc_state)
 {
-	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
-	struct drm_device *dev = crtc->dev;
+	struct sde_crtc *sde_crtc;
+	struct drm_device *dev;
 	struct drm_plane *plane;
 	unsigned long flags;
 
+	if (!crtc) {
+		DRM_ERROR("invalid crtc\n");
+		return;
+	}
+
+	DBG("");
+
+	sde_crtc = to_sde_crtc(crtc);
+
+	dev = crtc->dev;
+
 	if (sde_crtc->event) {
 		DBG("already received sde_crtc->event");
 	} else {
@@ -521,13 +603,20 @@
 	if (unlikely(!sde_crtc->num_ctls))
 		return;
 
+	/* wait for acquire fences before anything else is done */
+	_sde_crtc_wait_for_fences(crtc);
+
+	/*
+	 * Final plane updates: Give each plane a chance to complete all
+	 *                      required writes/flushing before crtc's "flush
+	 *                      everything" call below.
+	 */
+	drm_atomic_crtc_for_each_plane(plane, crtc)
+		sde_plane_flush(plane);
+
 	crtc_flush_all(crtc);
 
 	request_pending(crtc, PENDING_FLIP);
-
-	drm_atomic_crtc_for_each_plane(plane, crtc) {
-		sde_plane_complete_flip(plane);
-	}
 }
 
 /**
@@ -625,6 +714,8 @@
 	msm_property_reset_state(&sde_crtc->property_info, cstate,
 			cstate->property_values, cstate->property_blobs);
 
+	_sde_crtc_set_input_fence_timeout(cstate);
+
 	cstate->base.crtc = crtc;
 	crtc->state = &cstate->base;
 }
@@ -648,8 +739,8 @@
 
 static void sde_crtc_enable(struct drm_crtc *crtc)
 {
-	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
-	struct sde_crtc_mixer *mixer = sde_crtc->mixer;
+	struct sde_crtc *sde_crtc;
+	struct sde_crtc_mixer *mixer;
 	struct sde_hw_mixer *lm;
 	unsigned long flags;
 	struct drm_display_mode *mode;
@@ -658,8 +749,16 @@
 	int i;
 	int rc;
 
+	if (!crtc) {
+		DRM_ERROR("invalid crtc\n");
+		return;
+	}
+
 	DBG("");
 
+	sde_crtc = to_sde_crtc(crtc);
+	mixer = sde_crtc->mixer;
+
 	if (WARN_ON(!crtc->state))
 		return;
 
@@ -717,13 +816,26 @@
 static int sde_crtc_atomic_check(struct drm_crtc *crtc,
 		struct drm_crtc_state *state)
 {
-	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
-	struct sde_kms *sde_kms = get_kms(crtc);
+	struct sde_crtc *sde_crtc;
+	struct sde_kms *sde_kms;
 	struct drm_plane *plane;
 	struct plane_state pstates[SDE_STAGE_MAX];
-	int max_stages = CRTC_HW_MIXER_MAXSTAGES(sde_kms->catalog, 0);
+	int max_stages;
 	int cnt = 0, i;
 
+	if (!crtc) {
+		DRM_ERROR("invalid crtc\n");
+		return -EINVAL;
+	}
+
+	sde_crtc = to_sde_crtc(crtc);
+	sde_kms = get_kms(crtc);
+	if (!sde_kms) {
+		DRM_ERROR("invalid kms\n");
+		return -EINVAL;
+	}
+	max_stages = CRTC_HW_MIXER_MAXSTAGES(sde_kms->catalog, 0);
+
 	DBG("%s: check", sde_crtc->name);
 
 	/* verify that there are not too many planes attached to crtc
@@ -814,8 +926,9 @@
 
 	/* range properties */
 	msm_property_install_range(&sde_crtc->property_info,
-			"sync_fence_timeout", 0, ~0, 10000,
-			CRTC_PROP_SYNC_FENCE_TIMEOUT);
+			"input_fence_timeout",
+			0, ~0, SDE_CRTC_INPUT_FENCE_TIMEOUT,
+			CRTC_PROP_INPUT_FENCE_TIMEOUT);
 }
 
 /**
@@ -833,7 +946,7 @@
 {
 	struct sde_crtc *sde_crtc;
 	struct sde_crtc_state *cstate;
-	int ret = -EINVAL;
+	int idx, ret = -EINVAL;
 
 	if (!crtc || !state || !property) {
 		DRM_ERROR("invalid argument(s)\n");
@@ -843,6 +956,12 @@
 		ret = msm_property_atomic_set(&sde_crtc->property_info,
 				cstate->property_values, cstate->property_blobs,
 				property, val);
+		if (!ret) {
+			idx = msm_property_index(&sde_crtc->property_info,
+					property);
+			if (idx == CRTC_PROP_INPUT_FENCE_TIMEOUT)
+				_sde_crtc_set_input_fence_timeout(cstate);
+		}
 	}
 
 	return ret;
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index c069bef..16d7bf4 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -93,11 +93,13 @@
  * struct sde_crtc_state - sde container for atomic crtc state
  * @base: Base drm crtc state structure
  * @property_values: Current crtc property values
+ * @input_fence_timeout_ns : Cached input fence timeout, in ns
  * @property_blobs: Reference pointers for blob properties
  */
 struct sde_crtc_state {
 	struct drm_crtc_state base;
 	uint64_t property_values[CRTC_PROP_COUNT];
+	uint64_t input_fence_timeout_ns;
 	struct drm_property_blob *property_blobs[CRTC_PROP_COUNT];
 };
 
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index afb0444..7d06981 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -129,8 +129,8 @@
 	/* blob properties */
 	struct drm_property_blob *property_blobs[PLANE_PROP_BLOBCOUNT];
 
-	/* dereferenced sync fence pointer */
-	void *sync_fence;
+	/* dereferenced input fence pointer */
+	void *input_fence;
 
 	/* assigned by crtc blender */
 	enum sde_stage stage;
@@ -373,20 +373,20 @@
  * Plane functions
  */
 enum sde_sspp sde_plane_pipe(struct drm_plane *plane);
-void sde_plane_complete_flip(struct drm_plane *plane);
+void sde_plane_flush(struct drm_plane *plane);
 struct drm_plane *sde_plane_init(struct drm_device *dev,
 		uint32_t pipe, bool primary_plane);
 
 /**
- * sde_plane_wait_sync_fence - wait for sync fence object
+ * sde_plane_wait_input_fence - wait for input fence object
  * @plane:   Pointer to DRM plane object
  * @wait_ms: Wait timeout value
  * Returns: Zero on success
  */
-int sde_plane_wait_sync_fence(struct drm_plane *plane, uint32_t wait_ms);
+int sde_plane_wait_input_fence(struct drm_plane *plane, uint32_t wait_ms);
 
 /**
- * sde_plane_color_fill(): Enables color fill on plane
+ * sde_plane_color_fill - Enables color fill on plane
  * @plane:  Pointer to DRM plane object
  * @color:  RGB fill color value, [23..16] Blue, [15..8] Green, [7..0] Red
  * @alpha:  8-bit fill alpha value, 255 selects 100% alpha
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 0aa6dbf..a7c38bf 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -31,6 +31,8 @@
 
 #define SDE_NAME_SIZE  12
 
+#define SDE_PLANE_COLOR_FILL_FLAG	BIT(31)
+
 struct sde_plane {
 	struct drm_plane base;
 
@@ -48,6 +50,8 @@
 	struct sde_hw_pixel_ext pixel_ext;
 	struct sde_hw_sharp_cfg sharp_cfg;
 	struct sde_hw_scaler3_cfg scaler3_cfg;
+	uint32_t color_fill;
+	bool is_error;
 
 	struct sde_csc_cfg csc_cfg;
 	struct sde_csc_cfg *csc_ptr;
@@ -72,27 +76,28 @@
 	return state && state->fb && state->crtc;
 }
 
-/* helper to update a state's sync fence pointer from the property */
-static void _sde_plane_update_sync_fence(struct drm_plane *plane,
+/* helper to update a state's input fence pointer from the property */
+static void _sde_plane_set_input_fence(struct drm_plane *plane,
 		struct sde_plane_state *pstate, uint64_t fd)
 {
 	if (!plane || !pstate)
 		return;
 
 	/* clear previous reference */
-	if (pstate->sync_fence)
-		sde_sync_put(pstate->sync_fence);
+	if (pstate->input_fence)
+		sde_sync_put(pstate->input_fence);
 
 	/* get fence pointer for later */
-	pstate->sync_fence = sde_sync_get(fd);
+	pstate->input_fence = sde_sync_get(fd);
 
 	DBG("0x%llX", fd);
 }
 
-int sde_plane_wait_sync_fence(struct drm_plane *plane, uint32_t wait_ms)
+int sde_plane_wait_input_fence(struct drm_plane *plane, uint32_t wait_ms)
 {
+	struct sde_plane *psde;
 	struct sde_plane_state *pstate;
-	void *sync_fence;
+	void *input_fence;
 	int ret = -EINVAL;
 
 	if (!plane) {
@@ -100,18 +105,27 @@
 	} else if (!plane->state) {
 		DRM_ERROR("Invalid plane state\n");
 	} else {
+		psde = to_sde_plane(plane);
 		pstate = to_sde_plane_state(plane->state);
-		sync_fence = pstate->sync_fence;
+		input_fence = pstate->input_fence;
 
-		if (sync_fence) {
-			DBG("%s", to_sde_plane(plane)->pipe_name);
-			ret = sde_sync_wait(sync_fence, (long)wait_ms);
-			if (!ret)
-				DBG("signaled");
-			else if (ret == -ETIME)
-				DRM_ERROR("timeout\n");
-			else
-				DRM_ERROR("error %d\n", ret);
+		if (input_fence) {
+			ret = sde_sync_wait(input_fence, wait_ms);
+			switch (ret) {
+			case 0:
+				DBG("%s signaled", psde->pipe_name);
+				break;
+			case -ETIME:
+				DRM_ERROR("timeout on %s, %ums\n",
+						psde->pipe_name, wait_ms);
+				psde->is_error = true;
+				break;
+			default:
+				DRM_ERROR("error on %s, %d\n",
+						psde->pipe_name, ret);
+				psde->is_error = true;
+				break;
+			}
 		} else {
 			ret = 0;
 		}
@@ -437,15 +451,11 @@
 			DRM_ERROR("invalid csc blob, v%lld\n", csc->version);
 	}
 
+	/* revert to kernel default if override not available */
 	if (psde->csc_ptr)
 		DBG("user blob override for csc");
-	/* revert to kernel default */
 	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;
-
-	psde->pipe_hw->ops.setup_csc(psde->pipe_hw, psde->csc_ptr);
 }
 
 static void _sde_plane_setup_scaler(struct sde_plane *psde,
@@ -619,7 +629,14 @@
 	}
 }
 
-int sde_plane_color_fill(struct drm_plane *plane,
+/**
+ * _sde_plane_color_fill - enables color fill on plane
+ * @plane:  Pointer to DRM plane object
+ * @color:  RGB fill color value, [23..16] Blue, [15..8] Green, [7..0] Red
+ * @alpha:  8-bit fill alpha value, 255 selects 100% alpha
+ * Returns: 0 on success
+ */
+static int _sde_plane_color_fill(struct drm_plane *plane,
 		uint32_t color, uint32_t alpha)
 {
 	struct sde_plane *psde;
@@ -636,6 +653,8 @@
 		return -EINVAL;
 	}
 
+	DBG("");
+
 	/*
 	 * select fill format to match user property expectation,
 	 * h/w only supports RGB variants
@@ -676,7 +695,7 @@
 {
 	struct sde_plane *psde;
 	struct sde_plane_state *pstate;
-	uint32_t nplanes, color_fill;
+	uint32_t nplanes;
 	uint32_t src_flags;
 	const struct sde_format *fmt;
 
@@ -747,12 +766,11 @@
 	fmt = psde->pipe_cfg.src.format;
 
 	/* check for color fill */
-	color_fill = (uint32_t)sde_plane_get_property(pstate,
+	psde->color_fill = (uint32_t)sde_plane_get_property(pstate,
 			PLANE_PROP_COLOR_FILL);
-	if (color_fill & BIT(31)) {
-		/* force 100% alpha, stop other processing */
-		return sde_plane_color_fill(plane, color_fill, 0xFF);
-	}
+	if (psde->color_fill & SDE_PLANE_COLOR_FILL_FLAG)
+		/* skip remaining processing on color fill */
+		return 0;
 
 	_sde_plane_set_scanout(plane, pstate, &psde->pipe_cfg, fb);
 
@@ -778,6 +796,8 @@
 	/* update csc */
 	if (SDE_FORMAT_IS_YUV(fmt))
 		_sde_plane_setup_csc(psde, pstate, fmt);
+	else
+		psde->csc_ptr = 0;
 
 	return 0;
 }
@@ -1005,9 +1025,34 @@
 	return ret;
 }
 
-void sde_plane_complete_flip(struct drm_plane *plane)
+/**
+ * sde_plane_flush - final plane operations before commit flush
+ * @plane: Pointer to drm plane structure
+ */
+void sde_plane_flush(struct drm_plane *plane)
 {
-	if (plane && plane->state)
+	struct sde_plane *psde;
+
+	if (!plane)
+		return;
+
+	psde = to_sde_plane(plane);
+
+	/*
+	 * These updates have to be done immediately before the plane flush
+	 * timing, and may not be moved to the atomic_update/mode_set functions.
+	 */
+	if (psde->is_error)
+		/* force white frame with 0% alpha pipe output on error */
+		_sde_plane_color_fill(plane, 0xFFFFFF, 0x0);
+	else if (psde->color_fill & SDE_PLANE_COLOR_FILL_FLAG)
+		/* force 100% alpha */
+		_sde_plane_color_fill(plane, psde->color_fill, 0xFF);
+	else if (psde->pipe_hw && psde->csc_ptr && psde->pipe_hw->ops.setup_csc)
+		psde->pipe_hw->ops.setup_csc(psde->pipe_hw, psde->csc_ptr);
+
+	/* flag h/w flush complete */
+	if (plane->state)
 		to_sde_plane_state(plane->state)->pending = false;
 }
 
@@ -1024,6 +1069,7 @@
 	}
 
 	sde_plane = to_sde_plane(plane);
+	sde_plane->is_error = false;
 	state = plane->state;
 	pstate = to_sde_plane_state(state);
 
@@ -1084,8 +1130,9 @@
 				0, 0xFFFFFFFF, 0,
 				PLANE_PROP_COLOR_FILL);
 
-	msm_property_install_range(&psde->property_info, "sync_fence",
-			0, ~0, ~0, PLANE_PROP_SYNC_FENCE);
+	msm_property_install_range(&psde->property_info, "input_fence",
+			0, ~0, ~0,
+			PLANE_PROP_INPUT_FENCE);
 
 	/* standard properties */
 	msm_property_install_rotation(&psde->property_info,
@@ -1133,9 +1180,8 @@
 		if (!ret) {
 			idx = msm_property_index(&psde->property_info,
 					property);
-			if (idx == PLANE_PROP_SYNC_FENCE)
-				_sde_plane_update_sync_fence(plane,
-						pstate, val);
+			if (idx == PLANE_PROP_INPUT_FENCE)
+				_sde_plane_set_input_fence(plane, pstate, val);
 		}
 	}
 
@@ -1223,8 +1269,8 @@
 		drm_framebuffer_unreference(state->fb);
 
 	/* remove ref count for fence */
-	if (pstate->sync_fence)
-		sde_sync_put(pstate->sync_fence);
+	if (pstate->input_fence)
+		sde_sync_put(pstate->input_fence);
 
 	/* destroy value helper */
 	msm_property_destroy_state(&psde->property_info, pstate,
@@ -1258,10 +1304,10 @@
 		drm_framebuffer_reference(pstate->base.fb);
 
 	/* add ref count for fence */
-	if (pstate->sync_fence) {
-		pstate->sync_fence = 0;
-		_sde_plane_update_sync_fence(plane, pstate, pstate->
-				property_values[PLANE_PROP_SYNC_FENCE]);
+	if (pstate->input_fence) {
+		pstate->input_fence = 0;
+		_sde_plane_set_input_fence(plane, pstate, pstate->
+				property_values[PLANE_PROP_INPUT_FENCE]);
 	}
 
 	pstate->mode_changed = false;