drm/msm/sde: add support for crtc properties

Move sync_fence_timeout property from plane to crtc, so that
a common wait time can be applied to all planes selected for
each crtc.

Change-Id: I8108de8882667fad00df000073ddfc23202b15e0
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 7c1d630..0f71530 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -83,7 +83,6 @@
 	PLANE_PROP_ALPHA,
 	PLANE_PROP_COLOR_FILL,
 	PLANE_PROP_SYNC_FENCE,
-	PLANE_PROP_SYNC_FENCE_TIMEOUT,
 
 	/* enum/bitmask properties */
 	PLANE_PROP_ROTATION,
@@ -94,6 +93,17 @@
 	PLANE_PROP_COUNT
 };
 
+enum msm_mdp_crtc_property {
+	/* # of blob properties */
+	CRTC_PROP_BLOBCOUNT,
+
+	/* range properties */
+	CRTC_PROP_SYNC_FENCE_TIMEOUT = CRTC_PROP_BLOBCOUNT,
+
+	/* total # of properties */
+	CRTC_PROP_COUNT
+};
+
 struct msm_vblank_ctrl {
 	struct work_struct work;
 	struct list_head event_list;
@@ -171,6 +181,7 @@
 
 	/* Properties */
 	struct drm_property *plane_property[PLANE_PROP_COUNT];
+	struct drm_property *crtc_property[CRTC_PROP_COUNT];
 
 	/* VRAM carveout, used when no IOMMU: */
 	struct {
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index d498e0c..e2d0db2 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -131,7 +131,13 @@
 	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
 
 	DBG("");
+
+	if (!crtc)
+		return;
+
+	msm_property_destroy(&sde_crtc->property_info);
 	debugfs_remove_recursive(sde_crtc->debugfs_root);
+
 	drm_crtc_cleanup(crtc);
 	kfree(sde_crtc);
 }
@@ -524,10 +530,103 @@
 	}
 }
 
-static int sde_crtc_set_property(struct drm_crtc *crtc,
-		struct drm_property *property, uint64_t val)
+/**
+ * sde_crtc_destroy_state - state destroy hook
+ * @crtc: drm CRTC
+ * @state: CRTC state object to release
+ */
+static void sde_crtc_destroy_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *state)
 {
-	return -EINVAL;
+	struct sde_crtc *sde_crtc;
+	struct sde_crtc_state *cstate;
+
+	if (!crtc || !state) {
+		DRM_ERROR("invalid argument(s)\n");
+		return;
+	}
+
+	sde_crtc = to_sde_crtc(crtc);
+	cstate = to_sde_crtc_state(state);
+
+	DBG("");
+
+	__drm_atomic_helper_crtc_destroy_state(crtc, state);
+
+	/* destroy value helper */
+	msm_property_destroy_state(&sde_crtc->property_info, cstate,
+			cstate->property_values, cstate->property_blobs);
+}
+
+/**
+ * sde_crtc_duplicate_state - state duplicate hook
+ * @crtc: Pointer to drm crtc structure
+ * @Returns: Pointer to new drm_crtc_state structure
+ */
+static struct drm_crtc_state *sde_crtc_duplicate_state(struct drm_crtc *crtc)
+{
+	struct sde_crtc *sde_crtc;
+	struct sde_crtc_state *cstate, *old_cstate;
+
+	if (!crtc || !crtc->state) {
+		DRM_ERROR("invalid argument(s)\n");
+		return NULL;
+	}
+
+	sde_crtc = to_sde_crtc(crtc);
+	old_cstate = to_sde_crtc_state(crtc->state);
+	cstate = msm_property_alloc_state(&sde_crtc->property_info);
+	if (!cstate) {
+		DRM_ERROR("failed to allocate state\n");
+		return NULL;
+	}
+
+	/* duplicate value helper */
+	msm_property_duplicate_state(&sde_crtc->property_info,
+			old_cstate, cstate,
+			cstate->property_values, cstate->property_blobs);
+
+	/* duplicate base helper */
+	__drm_atomic_helper_crtc_duplicate_state(crtc, &cstate->base);
+
+	return &cstate->base;
+}
+
+/**
+ * sde_crtc_reset - reset hook for CRTCs
+ * Resets the atomic state for @crtc by freeing the state pointer (which might
+ * be NULL, e.g. at driver load time) and allocating a new empty state object.
+ * @crtc: Pointer to drm crtc structure
+ */
+static void sde_crtc_reset(struct drm_crtc *crtc)
+{
+	struct sde_crtc *sde_crtc;
+	struct sde_crtc_state *cstate;
+
+	if (!crtc) {
+		DRM_ERROR("invalid crtc\n");
+		return;
+	}
+
+	/* remove previous state, if present */
+	if (crtc->state) {
+		sde_crtc_destroy_state(crtc, crtc->state);
+		crtc->state = 0;
+	}
+
+	sde_crtc = to_sde_crtc(crtc);
+	cstate = msm_property_alloc_state(&sde_crtc->property_info);
+	if (!cstate) {
+		DRM_ERROR("failed to allocate state\n");
+		return;
+	}
+
+	/* reset value helper */
+	msm_property_reset_state(&sde_crtc->property_info, cstate,
+			cstate->property_values, cstate->property_blobs);
+
+	cstate->base.crtc = crtc;
+	crtc->state = &cstate->base;
 }
 
 static int sde_crtc_cursor_set(struct drm_crtc *crtc,
@@ -666,28 +765,6 @@
 	return 0;
 }
 
-static const struct drm_crtc_funcs sde_crtc_funcs = {
-	.set_config = drm_atomic_helper_set_config,
-	.destroy = sde_crtc_destroy,
-	.page_flip = drm_atomic_helper_page_flip,
-	.set_property = sde_crtc_set_property,
-	.reset = drm_atomic_helper_crtc_reset,
-	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
-	.cursor_set = sde_crtc_cursor_set,
-	.cursor_move = sde_crtc_cursor_move,
-};
-
-static const struct drm_crtc_helper_funcs sde_crtc_helper_funcs = {
-	.mode_fixup = sde_crtc_mode_fixup,
-	.mode_set_nofb = sde_crtc_mode_set_nofb,
-	.disable = sde_crtc_disable,
-	.enable = sde_crtc_enable,
-	.atomic_check = sde_crtc_atomic_check,
-	.atomic_begin = sde_crtc_atomic_begin,
-	.atomic_flush = sde_crtc_atomic_flush,
-};
-
 int sde_crtc_vblank(struct drm_crtc *crtc, bool en)
 {
 	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
@@ -716,9 +793,104 @@
 {
 }
 
-static void sde_crtc_install_properties(struct drm_crtc *crtc,
-	struct drm_mode_object *obj)
+/**
+ * sde_crtc_install_properties - install all drm properties for crtc
+ * @crtc: Pointer to drm crtc structure
+ */
+static void sde_crtc_install_properties(struct drm_crtc *crtc)
 {
+	struct sde_crtc *sde_crtc;
+	struct drm_device *dev;
+
+	DBG("");
+
+	if (!crtc) {
+		DRM_ERROR("invalid crtc\n");
+		return;
+	}
+
+	sde_crtc = to_sde_crtc(crtc);
+	dev = crtc->dev;
+
+	/* range properties */
+	msm_property_install_range(&sde_crtc->property_info,
+			"sync_fence_timeout", 0, ~0, 10000,
+			CRTC_PROP_SYNC_FENCE_TIMEOUT);
+}
+
+/**
+ * sde_crtc_atomic_set_property - atomically set a crtc drm property
+ * @crtc: Pointer to drm crtc structure
+ * @state: Pointer to drm crtc state structure
+ * @property: Pointer to targeted drm property
+ * @val: Updated property value
+ * @Returns: Zero on success
+ */
+static int sde_crtc_atomic_set_property(struct drm_crtc *crtc,
+		struct drm_crtc_state *state,
+		struct drm_property *property,
+		uint64_t val)
+{
+	struct sde_crtc *sde_crtc;
+	struct sde_crtc_state *cstate;
+	int ret = -EINVAL;
+
+	if (!crtc || !state || !property) {
+		DRM_ERROR("invalid argument(s)\n");
+	} else {
+		sde_crtc = to_sde_crtc(crtc);
+		cstate = to_sde_crtc_state(state);
+		ret = msm_property_atomic_set(&sde_crtc->property_info,
+				cstate->property_values, cstate->property_blobs,
+				property, val);
+	}
+
+	return ret;
+}
+
+/**
+ * sde_crtc_set_property - set a crtc drm property
+ * @crtc: Pointer to drm crtc structure
+ * @property: Pointer to targeted drm property
+ * @val: Updated property value
+ * @Returns: Zero on success
+ */
+static int sde_crtc_set_property(struct drm_crtc *crtc,
+		struct drm_property *property, uint64_t val)
+{
+	DBG("");
+
+	return sde_crtc_atomic_set_property(crtc, crtc->state, property, val);
+}
+
+/**
+ * sde_crtc_atomic_get_property - retrieve a crtc drm property
+ * @crtc: Pointer to drm crtc structure
+ * @state: Pointer to drm crtc state structure
+ * @property: Pointer to targeted drm property
+ * @val: Pointer to variable for receiving property value
+ * @Returns: Zero on success
+ */
+static int sde_crtc_atomic_get_property(struct drm_crtc *crtc,
+		const struct drm_crtc_state *state,
+		struct drm_property *property,
+		uint64_t *val)
+{
+	struct sde_crtc *sde_crtc;
+	struct sde_crtc_state *cstate;
+	int ret = -EINVAL;
+
+	if (!crtc || !state) {
+		DRM_ERROR("invalid argument(s)\n");
+	} else {
+		sde_crtc = to_sde_crtc(crtc);
+		cstate = to_sde_crtc_state(state);
+		ret = msm_property_atomic_get(&sde_crtc->property_info,
+				cstate->property_values, cstate->property_blobs,
+				property, val);
+	}
+
+	return ret;
 }
 
 static int _sde_debugfs_mixer_read(struct seq_file *s, void *data)
@@ -762,6 +934,30 @@
 	return single_open(file, _sde_debugfs_mixer_read, inode->i_private);
 }
 
+static const struct drm_crtc_funcs sde_crtc_funcs = {
+	.set_config = drm_atomic_helper_set_config,
+	.destroy = sde_crtc_destroy,
+	.page_flip = drm_atomic_helper_page_flip,
+	.set_property = sde_crtc_set_property,
+	.atomic_set_property = sde_crtc_atomic_set_property,
+	.atomic_get_property = sde_crtc_atomic_get_property,
+	.reset = sde_crtc_reset,
+	.atomic_duplicate_state = sde_crtc_duplicate_state,
+	.atomic_destroy_state = sde_crtc_destroy_state,
+	.cursor_set = sde_crtc_cursor_set,
+	.cursor_move = sde_crtc_cursor_move,
+};
+
+static const struct drm_crtc_helper_funcs sde_crtc_helper_funcs = {
+	.mode_fixup = sde_crtc_mode_fixup,
+	.mode_set_nofb = sde_crtc_mode_set_nofb,
+	.disable = sde_crtc_disable,
+	.enable = sde_crtc_enable,
+	.atomic_check = sde_crtc_atomic_check,
+	.atomic_begin = sde_crtc_atomic_begin,
+	.atomic_flush = sde_crtc_atomic_flush,
+};
+
 static void _sde_crtc_init_debugfs(struct sde_crtc *sde_crtc,
 		struct sde_kms *sde_kms)
 {
@@ -810,8 +1006,6 @@
 	sde_crtc->encoder = encoder;
 	spin_lock_init(&sde_crtc->lm_lock);
 
-	sde_crtc_install_properties(crtc, &crtc->base);
-
 	drm_crtc_init_with_planes(dev, crtc, plane, NULL, &sde_crtc_funcs);
 
 	drm_crtc_helper_add(crtc, &sde_crtc_helper_funcs);
@@ -828,6 +1022,14 @@
 
 	_sde_crtc_init_debugfs(sde_crtc, kms);
 
+	/* create CRTC properties */
+	msm_property_init(&sde_crtc->property_info, &crtc->base, dev,
+			priv->crtc_property, sde_crtc->property_data,
+			CRTC_PROP_COUNT, CRTC_PROP_BLOBCOUNT,
+			sizeof(struct sde_crtc_state));
+
+	sde_crtc_install_properties(crtc);
+
 	DBG("%s: Successfully initialized crtc", sde_crtc->name);
 	return crtc;
 }
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index aaad958..c069bef 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -14,6 +14,8 @@
 #define _SDE_CRTC_H_
 
 #include "drm_crtc.h"
+#include "msm_prop.h"
+#include "sde_kms.h"
 
 #define CRTC_DUAL_MIXERS	2
 #define SDE_CRTC_NAME_SIZE	12
@@ -54,6 +56,8 @@
  * @pending       : Whether or not an update is pending
  * @vsync_count   : Running count of received vsync events
  * @drm_requested_vblank : Whether vblanks have been enabled in the encoder
+ * @property_info : Opaque structure for generic property support
+ * @property_defaults : Array of default values for generic property support
  * @stage_cfg     : H/w mixer stage configuration
  * @debugfs_root  : Parent of debugfs node
  */
@@ -76,10 +80,37 @@
 	u32 vsync_count;
 	bool drm_requested_vblank;
 
+	struct msm_property_info property_info;
+	struct msm_property_data property_data[CRTC_PROP_COUNT];
+
 	struct sde_hw_stage_cfg stage_cfg;
 	struct dentry *debugfs_root;
 };
 
 #define to_sde_crtc(x) container_of(x, struct sde_crtc, base)
 
+/**
+ * struct sde_crtc_state - sde container for atomic crtc state
+ * @base: Base drm crtc state structure
+ * @property_values: Current crtc property values
+ * @property_blobs: Reference pointers for blob properties
+ */
+struct sde_crtc_state {
+	struct drm_crtc_state base;
+	uint64_t property_values[CRTC_PROP_COUNT];
+	struct drm_property_blob *property_blobs[CRTC_PROP_COUNT];
+};
+
+#define to_sde_crtc_state(x) \
+	container_of(x, struct sde_crtc_state, base)
+
+/**
+ * sde_crtc_get_property - query integer value of crtc property
+ * @S: Pointer to crtc state
+ * @X: Property index, from enum msm_mdp_crtc_property
+ * Returns: Integer value of requested property
+ */
+#define sde_crtc_get_property(S, X) \
+	((S) && ((X) < CRTC_PROP_COUNT) ? ((S)->property_values[(X)]) : 0)
+
 #endif /* _SDE_CRTC_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index 915732e..afb0444 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -376,7 +376,14 @@
 void sde_plane_complete_flip(struct drm_plane *plane);
 struct drm_plane *sde_plane_init(struct drm_device *dev,
 		uint32_t pipe, bool primary_plane);
-int sde_plane_wait_sync_fence(struct drm_plane *plane);
+
+/**
+ * sde_plane_wait_sync_fence - wait for sync 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);
 
 /**
  * sde_plane_color_fill(): Enables color fill on plane
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 424d88d..0aa6dbf 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -89,11 +89,10 @@
 	DBG("0x%llX", fd);
 }
 
-int sde_plane_wait_sync_fence(struct drm_plane *plane)
+int sde_plane_wait_sync_fence(struct drm_plane *plane, uint32_t wait_ms)
 {
 	struct sde_plane_state *pstate;
 	void *sync_fence;
-	long wait_ms;
 	int ret = -EINVAL;
 
 	if (!plane) {
@@ -105,11 +104,8 @@
 		sync_fence = pstate->sync_fence;
 
 		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);
+			ret = sde_sync_wait(sync_fence, (long)wait_ms);
 			if (!ret)
 				DBG("signaled");
 			else if (ret == -ETIME)
@@ -1091,10 +1087,6 @@
 	msm_property_install_range(&psde->property_info, "sync_fence",
 			0, ~0, ~0, PLANE_PROP_SYNC_FENCE);
 
-	msm_property_install_range(&psde->property_info, "sync_fence_timeout",
-			0, ~0, 10000,
-			PLANE_PROP_SYNC_FENCE_TIMEOUT);
-
 	/* standard properties */
 	msm_property_install_rotation(&psde->property_info,
 			BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y),