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;