drm/msm/sde: cache connector list in sde crtc

Determine which connectors are associated with the crtc during
the beginning of the commit request and reuse the list for the
rest of the commit. This is needed because the drm atomic states
only record connectors/objects that have been updated in the
current commit, rather than all associated plane/connector/etc.
objects.

Change-Id: I51f2379fc574033b4f87e01eccbb3ee8aef31bb3
Signed-off-by: Clarence Ip <cip@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index b38c2df..c2fc931 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -293,22 +293,49 @@
 	}
 }
 
-void sde_crtc_prepare_fence(struct drm_crtc *crtc)
+void sde_crtc_prepare_commit(struct drm_crtc *crtc,
+		struct drm_crtc_state *old_state)
 {
 	struct sde_crtc *sde_crtc;
+	struct sde_crtc_state *cstate;
+	struct drm_connector *conn;
 
-	if (!crtc) {
+	if (!crtc || !crtc->state) {
 		SDE_ERROR("invalid crtc\n");
 		return;
 	}
 
 	sde_crtc = to_sde_crtc(crtc);
-
+	cstate = to_sde_crtc_state(crtc->state);
 	MSM_EVT(crtc->dev, crtc->base.id, 0);
 
+	/* identify connectors attached to this crtc */
+	cstate->is_rt = false;
+	cstate->num_connectors = 0;
+
+	drm_for_each_connector(conn, crtc->dev)
+		if (conn->state && conn->state->crtc == crtc &&
+				cstate->num_connectors < MAX_CONNECTORS) {
+			cstate->connectors[cstate->num_connectors++] = conn;
+			sde_connector_prepare_fence(conn);
+
+			if (conn->connector_type != DRM_MODE_CONNECTOR_VIRTUAL)
+				cstate->is_rt = true;
+		}
+
+	/* prepare main output fence */
 	sde_fence_prepare(&sde_crtc->output_fence);
 }
 
+bool sde_crtc_is_rt(struct drm_crtc *crtc)
+{
+	if (!crtc || !crtc->state) {
+		SDE_ERROR("invalid crtc or state\n");
+		return true;
+	}
+	return to_sde_crtc_state(crtc->state)->is_rt;
+}
+
 /* if file!=NULL, this is preclose potential cancel-flip path */
 static void _sde_crtc_complete_flip(struct drm_crtc *crtc,
 		struct drm_file *file)
@@ -345,15 +372,27 @@
 	MSM_EVT(crtc->dev, crtc->base.id, 0);
 }
 
-void sde_crtc_complete_commit(struct drm_crtc *crtc)
+void sde_crtc_complete_commit(struct drm_crtc *crtc,
+		struct drm_crtc_state *old_state)
 {
-	if (!crtc) {
+	struct sde_crtc *sde_crtc;
+	struct sde_crtc_state *cstate;
+	int i;
+
+	if (!crtc || !crtc->state) {
 		SDE_ERROR("invalid crtc\n");
 		return;
 	}
 
-	/* signal out fence at end of commit */
-	sde_fence_signal(&to_sde_crtc(crtc)->output_fence, 0);
+	sde_crtc = to_sde_crtc(crtc);
+	cstate = to_sde_crtc_state(crtc->state);
+	MSM_EVT(crtc->dev, crtc->base.id, 0);
+
+	/* signal output fence(s) at end of commit */
+	sde_fence_signal(&sde_crtc->output_fence, 0);
+
+	for (i = 0; i < cstate->num_connectors; ++i)
+		sde_connector_complete_commit(cstate->connectors[i]);
 }
 
 /**
@@ -489,7 +528,7 @@
 }
 
 static void sde_crtc_atomic_begin(struct drm_crtc *crtc,
-		struct drm_crtc_state *old_crtc_state)
+		struct drm_crtc_state *old_state)
 {
 	struct sde_crtc *sde_crtc;
 	struct drm_device *dev;
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index 40dff398..90d2b5f 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -93,12 +93,20 @@
 /**
  * struct sde_crtc_state - sde container for atomic crtc state
  * @base: Base drm crtc state structure
+ * @connectors    : Currently associated drm connectors
+ * @num_connectors: Number of associated drm connectors
+ * @is_rt         : Whether or not the current commit contains RT connectors
  * @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;
+
+	struct drm_connector *connectors[MAX_CONNECTORS];
+	int num_connectors;
+	bool is_rt;
+
 	uint64_t property_values[CRTC_PROP_COUNT];
 	uint64_t input_fence_timeout_ns;
 	struct drm_property_blob *property_blobs[CRTC_PROP_COUNT];
@@ -153,10 +161,20 @@
 void sde_crtc_commit_kickoff(struct drm_crtc *crtc);
 
 /**
- * sde_crtc_prepare_fence - callback to prepare for output fences
+ * sde_crtc_prepare_commit - callback to prepare for output fences
  * @crtc: Pointer to drm crtc object
+ * @old_state: Pointer to drm crtc old state object
  */
-void sde_crtc_prepare_fence(struct drm_crtc *crtc);
+void sde_crtc_prepare_commit(struct drm_crtc *crtc,
+		struct drm_crtc_state *old_state);
+
+/**
+ * sde_crtc_complete_commit - callback signalling completion of current commit
+ * @crtc: Pointer to drm crtc object
+ * @old_state: Pointer to drm crtc old state object
+ */
+void sde_crtc_complete_commit(struct drm_crtc *crtc,
+		struct drm_crtc_state *old_state);
 
 /**
  * sde_crtc_init - create a new crtc object
@@ -167,16 +185,17 @@
 struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane);
 
 /**
- * sde_crtc_complete_commit - callback signalling completion of current commit
- * @crtc: Pointer to drm crtc object
- */
-void sde_crtc_complete_commit(struct drm_crtc *crtc);
-
-/**
  * sde_crtc_cancel_pending_flip - complete flip for clients on lastclose
  * @crtc: Pointer to drm crtc object
  * @file: client to cancel's file handle
  */
 void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
 
+/**
+ * sde_crtc_is_rt - query whether real time connectors are present on the crtc
+ * @crtc: Pointer to drm crtc structure
+ * Returns: True if a connector is present with real time constraints
+ */
+bool sde_crtc_is_rt(struct drm_crtc *crtc);
+
 #endif /* _SDE_CRTC_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index f05fe79..5750658 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -244,21 +244,17 @@
 }
 
 static void sde_complete_commit(struct msm_kms *kms,
-		struct drm_atomic_state *state)
+		struct drm_atomic_state *old_state)
 {
 	struct sde_kms *sde_kms = to_sde_kms(kms);
 	struct drm_device *dev = sde_kms->dev;
 	struct msm_drm_private *priv = dev->dev_private;
 	struct drm_crtc *crtc;
-	struct drm_crtc_state *crtc_state;
-	struct drm_connector *connector;
-	struct drm_connector_state *conn_state;
+	struct drm_crtc_state *old_crtc_state;
 	int i;
 
-	for_each_crtc_in_state(state, crtc, crtc_state, i)
-		sde_crtc_complete_commit(crtc);
-	for_each_connector_in_state(state, connector, conn_state, i)
-		sde_connector_complete_commit(connector);
+	for_each_crtc_in_state(old_state, crtc, old_crtc_state, i)
+		sde_crtc_complete_commit(crtc, old_crtc_state);
 	sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
 
 	MSM_EVT(sde_kms->dev, 0, 0);
@@ -311,18 +307,30 @@
 }
 
 static void sde_kms_prepare_fence(struct msm_kms *kms,
-		struct drm_atomic_state *state)
+		struct drm_atomic_state *old_state)
 {
 	struct drm_crtc *crtc;
-	struct drm_crtc_state *crtc_state;
-	struct drm_connector *connector;
-	struct drm_connector_state *conn_state;
-	int i;
+	struct drm_crtc_state *old_crtc_state;
+	int i, rc;
 
-	for_each_crtc_in_state(state, crtc, crtc_state, i)
-		sde_crtc_prepare_fence(crtc);
-	for_each_connector_in_state(state, connector, conn_state, i)
-		sde_connector_prepare_fence(connector);
+	if (!kms || !old_state || !old_state->dev || !old_state->acquire_ctx) {
+		SDE_ERROR("invalid argument(s)\n");
+		return;
+	}
+
+retry:
+	/* attempt to acquire ww mutex for connection */
+	rc = drm_modeset_lock(&old_state->dev->mode_config.connection_mutex,
+			       old_state->acquire_ctx);
+
+	if (rc == -EDEADLK) {
+		drm_modeset_backoff(old_state->acquire_ctx);
+		goto retry;
+	}
+
+	/* old_state actually contains updated crtc pointers */
+	for_each_crtc_in_state(old_state, crtc, old_crtc_state, i)
+		sde_crtc_prepare_commit(crtc, old_crtc_state);
 }
 
 /**
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 5257b8d..86e44ad 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -220,36 +220,6 @@
 }
 
 /**
- * _sde_plane_is_rt_pipe - check if the given plane requires real-time QoS
- * @plane:		Pointer to drm plane
- * @crtc:		Pointer to drm crtc associated with the given plane
- */
-static bool _sde_plane_is_rt_pipe(struct drm_plane *plane,
-		struct drm_crtc *crtc)
-{
-	struct sde_plane *psde = to_sde_plane(plane);
-	struct drm_connector *connector;
-	bool is_rt = false;
-
-	/* check if this plane has a physical connector interface */
-	mutex_lock(&plane->dev->mode_config.mutex);
-	drm_for_each_connector(connector, plane->dev)
-		if (connector->state &&
-				(connector->state->crtc == crtc) &&
-				(connector->connector_type
-					!= DRM_MODE_CONNECTOR_VIRTUAL)) {
-			is_rt = true;
-			break;
-		}
-	mutex_unlock(&plane->dev->mode_config.mutex);
-
-	SDE_DEBUG("plane%u: pnum:%d rt:%d\n",
-			plane->base.id, psde->pipe - SSPP_VIG0, is_rt);
-
-	return is_rt;
-}
-
-/**
  * _sde_plane_set_qos_lut - set QoS LUT of the given plane
  * @plane:		Pointer to drm plane
  * @fb:			Pointer to framebuffer associated with the given plane
@@ -420,13 +390,14 @@
 		psde->pipe_qos_cfg.danger_safe_en = false;
 	}
 
-	SDE_DEBUG("plane%u: pnum:%d ds:%d vb:%d pri[0x%x, 0x%x]\n",
+	SDE_DEBUG("plane%u: pnum:%d ds:%d vb:%d pri[0x%x, 0x%x] is_rt:%d\n",
 		plane->base.id,
 		psde->pipe - SSPP_VIG0,
 		psde->pipe_qos_cfg.danger_safe_en,
 		psde->pipe_qos_cfg.vblank_en,
 		psde->pipe_qos_cfg.creq_vblank,
-		psde->pipe_qos_cfg.danger_vblank);
+		psde->pipe_qos_cfg.danger_vblank,
+		psde->is_rt_pipe);
 
 	psde->pipe_hw->ops.setup_qos_ctrl(psde->pipe_hw,
 			&psde->pipe_qos_cfg);
@@ -1001,7 +972,7 @@
 		return 0;
 	pstate->pending = true;
 
-	psde->is_rt_pipe = _sde_plane_is_rt_pipe(plane, crtc);
+	psde->is_rt_pipe = sde_crtc_is_rt(crtc);
 	_sde_plane_set_qos_ctrl(plane, false, SDE_PLANE_QOS_PANIC_CTRL);
 
 	/* update roi config */