drm/msm/sde: input fence support for planes
Consolidate property default definitions to a single location, and
enable planes-related support for the input fence property.
Add sde_fence.h/c for Android fence API wrappers.
Change-Id: I8785f8cdaf32d62373678aa9c35dd94d39d7992f
Signed-off-by: Clarence Ip <cip@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index e9d0039..0ce53b7 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -1,5 +1,6 @@
ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/msm
ccflags-$(CONFIG_DRM_MSM_DSI) += -Idrivers/gpu/drm/msm/dsi
+ccflags-$(CONFIG_SYNC) += -Idrivers/staging/android
msm-y := \
adreno/adreno_device.o \
@@ -64,6 +65,7 @@
msm_ringbuffer.o
msm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o
+msm-$(CONFIG_SYNC) += sde/sde_fence.o
msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o
msm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_pll_8960.o
msm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_phy_8996.o
diff --git a/drivers/gpu/drm/msm/sde/sde_fence.c b/drivers/gpu/drm/msm/sde/sde_fence.c
new file mode 100644
index 0000000..de1e871
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_fence.c
@@ -0,0 +1,35 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <sync.h>
+#include "sde_kms.h"
+#include "sde_fence.h"
+
+void *sde_sync_get(uint64_t fd)
+{
+ /* force signed compare, fdget accepts an int argument */
+ return (signed int)fd >= 0 ? sync_fence_fdget(fd) : NULL;
+}
+
+void sde_sync_put(void *fence)
+{
+ if (fence)
+ sync_fence_put(fence);
+}
+
+int sde_sync_wait(void *fence, long timeout_ms)
+{
+ if (!fence)
+ return -EINVAL;
+ return sync_fence_wait(fence, timeout_ms);
+}
+
diff --git a/drivers/gpu/drm/msm/sde/sde_fence.h b/drivers/gpu/drm/msm/sde/sde_fence.h
new file mode 100644
index 0000000..465fa59
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_fence.h
@@ -0,0 +1,54 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SDE_FENCE_H_
+#define _SDE_FENCE_H_
+
+#ifndef CONFIG_SYNC
+#define sde_sync_get(D) (0)
+#define sde_sync_put(F)
+#define sde_sync_wait(F, T) (0)
+#else
+
+/**
+ * sde_sync_get - Query sync fence object from a file handle
+ *
+ * On success, this function also increments the refcount of the sync fence
+ *
+ * @fd: Integer sync fence handle
+ *
+ * Return: Pointer to sync fence object, or NULL
+ */
+void *sde_sync_get(uint64_t fd);
+
+/**
+ * sde_sync_put - Releases a sync fence object acquired by @sde_sync_get
+ *
+ * This function decrements the sync fence's reference count; the object will
+ * be released if the reference count goes to zero.
+ *
+ * @fence: Pointer to sync fence
+ */
+void sde_sync_put(void *fence);
+
+/**
+ * sde_sync_wait - Query sync fence object from a file handle
+ *
+ * @fence: Pointer to sync fence
+ * @timeout_ms: Time to wait, in milliseconds. Waits forever if timeout_ms < 0
+ *
+ * Return: Zero on success, or -ETIME on timeout
+ */
+int sde_sync_wait(void *fence, long timeout_ms);
+#endif
+
+#endif /* _SDE_FENCE_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index c4d3881..38d6182 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -129,6 +129,9 @@
/* blob properties */
struct drm_property_blob *property_blobs[PLANE_PROP_BLOBCOUNT];
+ /* dereferenced sync fence pointer */
+ void *sync_fence;
+
/* assigned by crtc blender */
enum sde_stage stage;
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 4bbfc1a..be0181a 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -12,6 +12,7 @@
#include <linux/debugfs.h>
#include <uapi/drm/sde_drm.h>
#include "sde_kms.h"
+#include "sde_fence.h"
#include "sde_formats.h"
#include "sde_hw_sspp.h"
@@ -50,6 +51,9 @@
char pipe_name[SDE_NAME_SIZE];
+ /* cache property default values (for reset) */
+ uint64_t property_defaults[PLANE_PROP_COUNT];
+
/* debugfs related stuff */
struct dentry *debugfs_root;
struct sde_debugfs_regset32 debugfs_src;
@@ -63,15 +67,56 @@
return 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,
+ 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);
+
+ /* get fence pointer for later */
+ pstate->sync_fence = sde_sync_get(fd);
+
+ DBG("0x%llX", fd);
+}
+
+void *sde_plane_get_sync_fence(struct drm_plane *plane)
+{
+ struct sde_plane_state *pstate;
+ void *ret = NULL;
+
+ if (!plane) {
+ DRM_ERROR("Invalid plane\n");
+ } else if (!plane->state) {
+ DRM_ERROR("Invalid plane state\n");
+ } else {
+ pstate = to_sde_plane_state(plane->state);
+ ret = pstate->sync_fence;
+
+ DBG("%s", to_sde_plane(plane)->pipe_name);
+ }
+
+ return ret;
+}
+
static void _sde_plane_set_scanout(struct drm_plane *plane,
struct sde_plane_state *pstate,
struct sde_hw_pipe_cfg *pipe_cfg, struct drm_framebuffer *fb)
{
- struct sde_plane *psde = to_sde_plane(plane);
+ struct sde_plane *psde;
unsigned int shift;
int i;
- if (pipe_cfg && fb && psde->pipe_hw->ops.setup_sourceaddress) {
+ if (!plane || !pstate || !pipe_cfg || !fb)
+ return;
+
+ psde = to_sde_plane(plane);
+
+ if (psde->pipe_hw && psde->pipe_hw->ops.setup_sourceaddress) {
/* stride */
if (sde_plane_get_property(pstate, PLANE_PROP_SRC_CONFIG) &
BIT(SDE_DRM_DEINTERLACE))
@@ -405,6 +450,7 @@
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);
@@ -634,7 +680,7 @@
if (!new_state->fb)
return 0;
- DBG("%s: prepare: FB[%u]", psde->pipe_name, fb->base.id);
+ DBG("%s: FB[%u]", psde->pipe_name, fb->base.id);
return msm_framebuffer_prepare(fb, psde->mmu_id);
}
@@ -647,7 +693,7 @@
if (!fb)
return;
- DBG("%s: cleanup: FB[%u]", psde->pipe_name, fb->base.id);
+ DBG("%s: FB[%u]", psde->pipe_name, fb->base.id);
msm_framebuffer_cleanup(fb, psde->mmu_id);
}
@@ -721,8 +767,6 @@
struct drm_plane_state *state;
struct sde_plane_state *pstate;
- DBG("%s: update", sde_plane->pipe_name);
-
if (!plane || !plane->state) {
DRM_ERROR("Invalid plane/state\n");
return;
@@ -732,6 +776,8 @@
state = plane->state;
pstate = to_sde_plane_state(state);
+ DBG("%s: update", sde_plane->pipe_name);
+
if (!sde_plane_enabled(state)) {
pstate->pending = true;
} else if (pstate->mode_changed) {
@@ -752,12 +798,28 @@
}
}
+static inline struct drm_property **_sde_plane_get_property_entry(
+ struct drm_device *dev, enum msm_mdp_plane_property property)
+{
+ struct msm_drm_private *priv;
+
+ if (!dev || !dev->dev_private || (property >= PLANE_PROP_COUNT))
+ return NULL;
+
+ priv = dev->dev_private;
+
+ return &(priv->plane_property[property]);
+}
+
static void _sde_plane_install_range_property(struct drm_plane *plane,
struct drm_device *dev, const char *name,
uint64_t min, uint64_t max, uint64_t init,
- struct drm_property **prop)
+ enum msm_mdp_plane_property property)
{
- if (plane && dev && name && prop) {
+ struct drm_property **prop;
+
+ prop = _sde_plane_get_property_entry(dev, property);
+ if (plane && name && prop) {
/* only create the property once */
if (*prop == 0) {
*prop = drm_property_create_range(dev,
@@ -766,6 +828,9 @@
DRM_ERROR("Create %s property failed\n", name);
}
+ /* save init value for later */
+ to_sde_plane(plane)->property_defaults[property] = init;
+
/* always attach property, if created */
if (*prop)
drm_object_attach_property(&plane->base, *prop, init);
@@ -773,9 +838,13 @@
}
static void _sde_plane_install_rotation_property(struct drm_plane *plane,
- struct drm_device *dev, struct drm_property **prop)
+ struct drm_device *dev, enum msm_mdp_plane_property property)
{
- if (plane && dev && prop) {
+ struct sde_plane *psde;
+ struct drm_property **prop;
+
+ prop = _sde_plane_get_property_entry(dev, property);
+ if (plane && prop) {
/* only create the property once */
if (*prop == 0) {
*prop = drm_mode_create_rotation_property(dev,
@@ -785,18 +854,27 @@
DRM_ERROR("Create rotation property failed\n");
}
+ /* save init value for later */
+ psde = to_sde_plane(plane);
+ psde->property_defaults[property] = 0;
+
/* always attach property, if created */
if (*prop)
- drm_object_attach_property(&plane->base, *prop, 0);
+ drm_object_attach_property(&plane->base, *prop,
+ psde->property_defaults[property]);
}
}
static void _sde_plane_install_enum_property(struct drm_plane *plane,
struct drm_device *dev, const char *name, int is_bitmask,
const struct drm_prop_enum_list *values, int num_values,
- struct drm_property **prop)
+ enum msm_mdp_plane_property property)
{
- if (plane && dev && name && prop && values && num_values) {
+ struct sde_plane *psde;
+ struct drm_property **prop;
+
+ prop = _sde_plane_get_property_entry(dev, property);
+ if (plane && name && prop && values && num_values) {
/* only create the property once */
if (*prop == 0) {
/* 'bitmask' is a special type of 'enum' */
@@ -812,17 +890,26 @@
DRM_ERROR("Create %s property failed\n", name);
}
+ /* save init value for later */
+ psde = to_sde_plane(plane);
+ psde->property_defaults[property] = 0;
+
/* always attach property, if created */
if (*prop)
- drm_object_attach_property(&plane->base, *prop, 0);
+ drm_object_attach_property(&plane->base, *prop,
+ psde->property_defaults[property]);
}
}
static void _sde_plane_install_blob_property(struct drm_plane *plane,
struct drm_device *dev, const char *name,
- struct drm_property **prop)
+ enum msm_mdp_plane_property property)
{
- if (plane && dev && name && prop) {
+ struct sde_plane *psde;
+ struct drm_property **prop;
+
+ prop = _sde_plane_get_property_entry(dev, property);
+ if (plane && name && prop && (property < PLANE_PROP_BLOBCOUNT)) {
/* only create the property once */
if (*prop == 0) {
/* use 'create' for blob property place holder */
@@ -832,9 +919,14 @@
DRM_ERROR("Create %s property failed\n", name);
}
+ /* save init value for later */
+ psde = to_sde_plane(plane);
+ psde->property_defaults[property] = 0;
+
/* always attach property, if created */
if (*prop)
- drm_object_attach_property(&plane->base, *prop, 0);
+ drm_object_attach_property(&plane->base, *prop,
+ psde->property_defaults[property]);
}
}
@@ -852,8 +944,7 @@
} else if (!property) {
DRM_ERROR("Incoming property is NULL\n");
} else {
- prop_array = ((struct msm_drm_private *)
- (plane->dev->dev_private))->plane_property;
+ prop_array = _sde_plane_get_property_entry(plane->dev, 0);
if (!prop_array)
/* should never hit this */
DRM_ERROR("Invalid property array\n");
@@ -863,6 +954,9 @@
if (prop_array[idx] == property)
break;
}
+
+ if (idx == PLANE_PROP_COUNT)
+ DRM_ERROR("Invalid property pointer\n");
}
return idx;
@@ -884,7 +978,6 @@
};
struct sde_plane *psde = to_sde_plane(plane);
struct drm_device *dev = plane->dev;
- struct msm_drm_private *dev_priv = dev->dev_private;
DBG("");
@@ -894,34 +987,35 @@
}
/* range properties */
- _sde_plane_install_range_property(plane, dev, "zpos", 0, 255, 1,
- &(dev_priv->plane_property[PLANE_PROP_ZPOS]));
+ _sde_plane_install_range_property(plane, dev, "zpos", 0, 255,
+ plane->type == DRM_PLANE_TYPE_PRIMARY ?
+ STAGE_BASE : STAGE0 + drm_plane_index(plane),
+ PLANE_PROP_ZPOS);
_sde_plane_install_range_property(plane, dev, "alpha", 0, 255, 255,
- &(dev_priv->plane_property[PLANE_PROP_ALPHA]));
+ PLANE_PROP_ALPHA);
- _sde_plane_install_range_property(plane, dev, "sync_fence", 0, ~0, 0,
- &(dev_priv->plane_property[PLANE_PROP_SYNC_FENCE]));
+ _sde_plane_install_range_property(plane, dev, "sync_fence", 0, ~0, ~0,
+ PLANE_PROP_SYNC_FENCE);
/* standard properties */
- _sde_plane_install_rotation_property(plane, dev,
- &(dev_priv->plane_property[PLANE_PROP_ROTATION]));
+ _sde_plane_install_rotation_property(plane, dev, PLANE_PROP_ROTATION);
/* enum/bitmask properties */
_sde_plane_install_enum_property(plane, dev, "blend_op", 0,
e_blend_op, ARRAY_SIZE(e_blend_op),
- &(dev_priv->plane_property[PLANE_PROP_BLEND_OP]));
+ PLANE_PROP_BLEND_OP);
_sde_plane_install_enum_property(plane, dev, "src_config", 1,
e_src_config, ARRAY_SIZE(e_src_config),
- &(dev_priv->plane_property[PLANE_PROP_SRC_CONFIG]));
+ PLANE_PROP_SRC_CONFIG);
/* blob properties */
if (psde->features & SDE_SSPP_SCALER)
_sde_plane_install_blob_property(plane, dev, "scaler",
- &(dev_priv->plane_property[PLANE_PROP_SCALER]));
+ PLANE_PROP_SCALER);
if (psde->features & BIT(SDE_SSPP_CSC))
_sde_plane_install_blob_property(plane, dev, "csc",
- &(dev_priv->plane_property[PLANE_PROP_CSC]));
+ PLANE_PROP_CSC);
}
static int sde_plane_atomic_set_property(struct drm_plane *plane,
@@ -941,7 +1035,7 @@
DBG("Set property %d <= %d", idx, (int)val);
pstate = to_sde_plane_state(state);
- /* extra handling for incoming blob properties */
+ /* extra handling for incoming properties */
if ((property->flags & DRM_MODE_PROP_BLOB) &&
(idx < PLANE_PROP_BLOBCOUNT)) {
/* DRM lookup also takes a reference */
@@ -961,6 +1055,8 @@
drm_property_unreference_blob(*pr_blob);
*pr_blob = blob;
}
+ } else if (idx == PLANE_PROP_SYNC_FENCE) {
+ _sde_plane_update_sync_fence(plane, pstate, val);
}
pstate->property_values[idx] = val;
ret = 0;
@@ -972,15 +1068,13 @@
static int sde_plane_set_property(struct drm_plane *plane,
struct drm_property *property, uint64_t val)
{
- int rc;
-
DBG("");
if (!plane)
return -EINVAL;
- rc = sde_plane_atomic_set_property(plane, plane->state, property, val);
- return rc;
+ return sde_plane_atomic_set_property(plane,
+ plane->state, property, val);
}
static int sde_plane_atomic_get_property(struct drm_plane *plane,
@@ -990,8 +1084,6 @@
struct sde_plane_state *pstate;
int idx, ret = -EINVAL;
- DBG("");
-
idx = _sde_plane_get_property_index(plane, property);
if (!state) {
DRM_ERROR("Invalid state\n");
@@ -1001,7 +1093,7 @@
pstate = to_sde_plane_state(state);
*val = pstate->property_values[idx];
- DBG("Get property %d %lld", idx, *val);
+ DBG("%d 0x%llX", idx, *val);
ret = 0;
}
@@ -1039,18 +1131,26 @@
DBG("");
+ if (!plane || !state) {
+ DRM_ERROR("Invalid plane/state\n");
+ return;
+ }
+
/* remove ref count for frame buffers */
if (state->fb)
drm_framebuffer_unreference(state->fb);
pstate = to_sde_plane_state(state);
+ /* remove ref count for fence */
+ if (pstate->sync_fence)
+ sde_sync_put(pstate->sync_fence);
+
/* remove ref count for blobs */
for (i = 0; i < PLANE_PROP_BLOBCOUNT; ++i)
if (pstate->property_blobs[i])
drm_property_unreference_blob(
pstate->property_blobs[i]);
-
kfree(pstate);
}
@@ -1063,14 +1163,23 @@
if (WARN_ON(!plane->state))
return NULL;
- DBG("");
pstate = kmemdup(to_sde_plane_state(plane->state),
sizeof(*pstate), GFP_KERNEL);
+
+ DBG("");
+
if (pstate) {
/* add ref count for frame buffer */
if (pstate->base.fb)
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]);
+ }
+
/* add ref count for blobs */
for (i = 0; i < PLANE_PROP_BLOBCOUNT; ++i)
if (pstate->property_blobs[i])
@@ -1086,23 +1195,32 @@
static void sde_plane_reset(struct drm_plane *plane)
{
+ struct sde_plane *psde;
struct sde_plane_state *pstate;
+ int i;
DBG("");
- if (plane->state && plane->state->fb)
- drm_framebuffer_unreference(plane->state->fb);
- kfree(to_sde_plane_state(plane->state));
+ if (!plane) {
+ DRM_ERROR("Invalid plane\n");
+ return;
+ }
+
+ /* remove previous state, if present */
+ if (plane->state)
+ sde_plane_destroy_state(plane, plane->state);
+ plane->state = 0;
+
pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
+ if (!pstate) {
+ DRM_ERROR("Failed to re-allocate plane state\n");
+ return;
+ }
- /* assign default blend parameters */
- pstate->property_values[PLANE_PROP_ALPHA] = 255;
-
- if (plane->type == DRM_PLANE_TYPE_PRIMARY)
- pstate->property_values[PLANE_PROP_ZPOS] = STAGE_BASE;
- else
- pstate->property_values[PLANE_PROP_ZPOS] =
- STAGE0 + drm_plane_index(plane);
+ /* assign default property values */
+ psde = to_sde_plane(plane);
+ for (i = 0; i < PLANE_PROP_COUNT; ++i)
+ pstate->property_values[i] = psde->property_defaults[i];
pstate->base.plane = plane;