drm/msm/sde: add retire fence support

Add a property to the sde connector to support Android's retire
fence semantics.

Change-Id: Ibd557cc7a881859fd410aae8529addeedfa347c1
Signed-off-by: Clarence Ip <cip@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index 28e8cbc..d95068b 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -32,6 +32,7 @@
 	msm_property_destroy(&c_conn->property_info);
 
 	drm_connector_unregister(connector);
+	sde_fence_deinit(&c_conn->retire_fence);
 	drm_connector_cleanup(connector);
 	kfree(c_conn);
 }
@@ -258,10 +259,12 @@
 	c_state = to_sde_connector_state(state);
 
 	idx = msm_property_index(&c_conn->property_info, property);
-
-	/* get cached property value */
-	rc = msm_property_atomic_get(&c_conn->property_info,
-			c_state->property_values, 0, property, val);
+	if (idx == CONNECTOR_PROP_RETIRE_FENCE)
+		rc = sde_fence_create(&c_conn->retire_fence, val);
+	else
+		/* get cached property value */
+		rc = msm_property_atomic_get(&c_conn->property_info,
+				c_state->property_values, 0, property, val);
 
 	/* allow for custom override */
 	if (c_conn->ops.get_property)
@@ -273,6 +276,27 @@
 	return rc;
 }
 
+void sde_connector_prepare_fence(struct drm_connector *connector)
+{
+	if (!connector) {
+		SDE_ERROR("invalid connector\n");
+		return;
+	}
+
+	sde_fence_prepare(&to_sde_connector(connector)->retire_fence);
+}
+
+void sde_connector_complete_commit(struct drm_connector *connector)
+{
+	if (!connector) {
+		SDE_ERROR("invalid connector\n");
+		return;
+	}
+
+	/* signal connector's retire fence */
+	sde_fence_signal(&to_sde_connector(connector)->retire_fence, 0);
+}
+
 static enum drm_connector_status
 sde_connector_detect(struct drm_connector *connector, bool force)
 {
@@ -429,10 +453,23 @@
 			"conn%u",
 			c_conn->base.base.id);
 
+	/*
+	 * Initialize retire fence support. Set fence offset to 0 for virtual
+	 * connectors so that the fence signals at the end of the current commit
+	 * and 1 for others so that the fence signals after one additional
+	 * commit.
+	 */
+	rc = sde_fence_init(dev, &c_conn->retire_fence, c_conn->name,
+			connector_type == DRM_MODE_CONNECTOR_VIRTUAL ? 0 : 1);
+	if (rc) {
+		SDE_ERROR("failed to init fence, %d\n", rc);
+		goto error_cleanup_conn;
+	}
+
 	rc = drm_connector_register(&c_conn->base);
 	if (rc) {
 		SDE_ERROR("failed to register drm connector, %d\n", rc);
-		goto error_cleanup_conn;
+		goto error_cleanup_fence;
 	}
 
 	rc = drm_mode_connector_attach_encoder(&c_conn->base, encoder);
@@ -476,6 +513,9 @@
 		kfree(info);
 	}
 
+	msm_property_install_range(&c_conn->property_info, "RETIRE_FENCE",
+			0, ~0, ~0, CONNECTOR_PROP_RETIRE_FENCE);
+
 	rc = msm_property_install_get_status(&c_conn->property_info);
 	if (rc) {
 		SDE_ERROR("failed to create one or more properties\n");
@@ -492,6 +532,8 @@
 	msm_property_destroy(&c_conn->property_info);
 error_unregister_conn:
 	drm_connector_unregister(&c_conn->base);
+error_cleanup_fence:
+	sde_fence_deinit(&c_conn->retire_fence);
 error_cleanup_conn:
 	drm_connector_cleanup(&c_conn->base);
 error_free_conn: