[3/4] drm/i915: Calculate an intermediate atomic state for modesets (v2)
diff mbox

Message ID 1441850275-20353-4-git-send-email-matthew.d.roper@intel.com
State New
Headers show

Commit Message

Matt Roper Sept. 10, 2015, 1:57 a.m. UTC
To simplify the modeset process for atomic, start calculating an
"intermediate" state.  This state only modifies CRTC's that undergo a
modeset (or disable)...in that case it will be a copy of the final state
with ->active set to false (which will in turn cause various derived
state to be calculated differently).  This state represents the status
of the CRTC and planes at the point in which the CRTC's have been
disabled even if the CRTC will be running again once the full modeset is
complete.  This will allow us to calculate proper derived state fields
for this point in the modeset process which should in turn should help
us properly perform tasks like watermark calculation with less special
cases.

For review simplicity, this patch simply allocates and calculates the
intermediate state, but doesn't actually do anything with it yet.
The next patch will start actually using this intermediate state data.

v2:
 - Use a whole new top-level state object rather than just sticking
   extra plane state and crtc state arrays in the existing
   intel_atomic_state object.  (Ville)

Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Matt Roper <matthew.d.roper@intel.com>
---
 drivers/gpu/drm/i915/intel_atomic.c       | 142 +++++++++++++++++++++++++-----
 drivers/gpu/drm/i915/intel_atomic_plane.c |  36 +++++---
 drivers/gpu/drm/i915/intel_display.c      |   3 +-
 drivers/gpu/drm/i915/intel_drv.h          |  14 +++
 4 files changed, 160 insertions(+), 35 deletions(-)

Patch
diff mbox

diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
index 45b21ac..0487073 100644
--- a/drivers/gpu/drm/i915/intel_atomic.c
+++ b/drivers/gpu/drm/i915/intel_atomic.c
@@ -74,32 +74,23 @@  intel_connector_atomic_get_property(struct drm_connector *connector,
 }
 
 /*
- * intel_crtc_duplicate_state - duplicate crtc state
- * @crtc: drm crtc
- *
- * Allocates and returns a copy of the crtc state (both common and
- * Intel-specific) for the specified crtc.
- *
- * Returns: The newly allocated crtc state, or NULL on failure.
+ * Duplicate any CRTC state (not just an already committed state).
  */
-struct drm_crtc_state *
-intel_crtc_duplicate_state(struct drm_crtc *crtc)
+static struct drm_crtc_state *
+__intel_crtc_duplicate_state(struct drm_crtc_state *cs)
 {
-	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 	struct intel_crtc_state *crtc_state;
 
-	if (WARN_ON(!intel_crtc->config))
+	if (WARN_ON(!cs))
 		crtc_state = kzalloc(sizeof(*crtc_state), GFP_KERNEL);
 	else
-		crtc_state = kmemdup(intel_crtc->config,
-				     sizeof(*intel_crtc->config), GFP_KERNEL);
-
+		crtc_state = kmemdup(cs, sizeof(*crtc_state), GFP_KERNEL);
 	if (!crtc_state)
 		return NULL;
 
-	__drm_atomic_helper_crtc_duplicate_state(crtc->state, &crtc_state->base);
+	__drm_atomic_helper_crtc_duplicate_state(cs, &crtc_state->base);
 
-	crtc_state->base.crtc = crtc;
+	crtc_state->base.crtc = cs->crtc;
 
 	crtc_state->wait_for_flips = false;
 	crtc_state->disable_fbc = false;
@@ -118,6 +109,21 @@  intel_crtc_duplicate_state(struct drm_crtc *crtc)
 	return &crtc_state->base;
 }
 
+/*
+ * intel_crtc_duplicate_state - duplicate crtc state
+ * @crtc: drm crtc
+ *
+ * Allocates and returns a copy of the crtc state (both common and
+ * Intel-specific) for the specified crtc.
+ *
+ * Returns: The newly allocated crtc state, or NULL on failure.
+ */
+struct drm_crtc_state *
+intel_crtc_duplicate_state(struct drm_crtc *crtc)
+{
+	return __intel_crtc_duplicate_state(crtc->state);
+}
+
 /**
  * intel_crtc_destroy_state - destroy crtc state
  * @crtc: drm crtc
@@ -315,17 +321,113 @@  intel_atomic_state_alloc(struct drm_device *dev)
 {
 	struct intel_atomic_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
 
-	if (!state || drm_atomic_state_init(dev, &state->base) < 0) {
-		kfree(state);
-		return NULL;
-	}
+	if (!state || drm_atomic_state_init(dev, &state->base) < 0)
+		goto fail;
+
+	/* Also allocate an intermediate state */
+	state->intermediate = kzalloc(sizeof(*state->intermediate), GFP_KERNEL);
+	if (!state->intermediate ||
+	    drm_atomic_state_init(dev, state->intermediate) < 0)
+		goto fail;
 
+	state->intermediate->acquire_ctx = state->base.acquire_ctx;
 	return &state->base;
+
+fail:
+	if (state)
+		kfree(state->intermediate);
+	drm_atomic_state_default_release(&state->base);
+	kfree(state);
+	return NULL;
+}
+
+void
+intel_atomic_state_free(struct drm_atomic_state *s)
+{
+	struct intel_atomic_state *state = to_intel_atomic_state(s);
+
+	if (WARN_ON(!s))
+		return;
+
+	drm_atomic_state_default_release(state->intermediate);
+	drm_atomic_state_default_release(s);
 }
 
 void intel_atomic_state_clear(struct drm_atomic_state *s)
 {
 	struct intel_atomic_state *state = to_intel_atomic_state(s);
+
+	drm_atomic_state_default_clear(state->intermediate);
 	drm_atomic_state_default_clear(&state->base);
 	state->dpll_set = false;
 }
+
+/**
+ * intel_atomic_check_planes - validate state object for planes changes
+ * @dev: DRM device
+ * @state: the driver state object
+ *
+ * An Intel-specific replacement for drm_atomic_helper_check_planes().  In
+ * addition to calling the ->atomic_check() entrypoints for all plane/crtc
+ * states present in the atomic transaction, it also creates an additional
+ * 'intermediate' state that corresponds to the point in the commit pipeline
+ * where any CRTC's performing a modeset (or disable) have been disabled but
+ * not yet reenabled.  Specifically, crtc_state->active = false for each CRTC
+ * involved in a modeset and the corresponding derived state is updated to
+ * reflect that.
+ */
+int
+intel_atomic_check_planes(struct drm_device *dev,
+			  struct drm_atomic_state *state)
+{
+	struct intel_atomic_state *istate = to_intel_atomic_state(state);
+	struct drm_atomic_state *inter = istate->intermediate;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *cstate;
+	struct drm_plane *plane;
+	struct drm_plane_state *pstate;
+	unsigned needs_ms = 0;
+	int i, ret;
+
+	/*
+	 * Start by checking calculating/checking the final state as usual;
+	 * if that doesn't work, then there's no point in worrying about
+	 * intermediate states.
+	 */
+	ret = drm_atomic_helper_check_planes(dev, state);
+	if (ret)
+		return ret;
+
+	/*
+	 * For CRTC's undergoing a modeset, copy CRTC and plane state into
+	 * the intermediate state, but update crtc_state->active = false.
+	 */
+	for_each_crtc_in_state(state, crtc, cstate, i) {
+		if (!drm_atomic_crtc_needs_modeset(cstate))
+			continue;
+
+		needs_ms |= drm_crtc_mask(crtc);
+		inter->crtcs[i] = crtc;
+		inter->crtc_states[i] = __intel_crtc_duplicate_state(cstate);
+		inter->crtc_states[i]->state = inter;
+		inter->crtc_states[i]->active = false;
+
+		if (drm_atomic_crtc_needs_modeset(cstate))
+			istate->any_ms = true;
+	}
+	for_each_plane_in_state(state, plane, pstate, i) {
+		crtc = plane->state->crtc ?: pstate->crtc;
+		if (!crtc || !(needs_ms & drm_crtc_mask(crtc)))
+			continue;
+
+		inter->planes[i] = plane;
+		inter->plane_states[i] = __intel_plane_duplicate_state(pstate);
+		inter->plane_states[i]->state = inter;
+	}
+
+	/*
+	 * Run all the plane/crtc check handlers to update the derived state
+	 * fields to reflect the ->active change.
+	 */
+	return drm_atomic_helper_check_planes(dev, inter);
+}
diff --git a/drivers/gpu/drm/i915/intel_atomic_plane.c b/drivers/gpu/drm/i915/intel_atomic_plane.c
index afbbfe0..b893853 100644
--- a/drivers/gpu/drm/i915/intel_atomic_plane.c
+++ b/drivers/gpu/drm/i915/intel_atomic_plane.c
@@ -61,38 +61,46 @@  intel_create_plane_state(struct drm_plane *plane)
 	return state;
 }
 
-/**
- * intel_plane_duplicate_state - duplicate plane state
- * @plane: drm plane
- *
- * Allocates and returns a copy of the plane state (both common and
- * Intel-specific) for the specified plane.
- *
- * Returns: The newly allocated plane state, or NULL on failure.
+/*
+ * Duplicate any plane state (not just an already committed state).
  */
 struct drm_plane_state *
-intel_plane_duplicate_state(struct drm_plane *plane)
+__intel_plane_duplicate_state(struct drm_plane_state *ps)
 {
 	struct drm_plane_state *state;
 	struct intel_plane_state *intel_state;
 
-	if (WARN_ON(!plane->state))
-		intel_state = intel_create_plane_state(plane);
+	if (WARN_ON(!ps))
+		intel_state = intel_create_plane_state(ps->plane);
 	else
-		intel_state = kmemdup(plane->state, sizeof(*intel_state),
-				      GFP_KERNEL);
+		intel_state = kmemdup(ps, sizeof(*intel_state), GFP_KERNEL);
 
 	if (!intel_state)
 		return NULL;
 
 	state = &intel_state->base;
 
-	__drm_atomic_helper_plane_duplicate_state(plane->state, state);
+	__drm_atomic_helper_plane_duplicate_state(ps, state);
 
 	return state;
 }
 
 /**
+ * intel_plane_duplicate_state - duplicate plane state
+ * @plane: drm plane
+ *
+ * Allocates and returns a copy of the plane state (both common and
+ * Intel-specific) for the specified plane.
+ *
+ * Returns: The newly allocated plane state, or NULL on failure.
+ */
+struct drm_plane_state *
+intel_plane_duplicate_state(struct drm_plane *plane)
+{
+	return __intel_plane_duplicate_state(plane->state);
+}
+
+/**
  * intel_plane_destroy_state - destroy plane state
  * @plane: drm plane
  * @state: state object to destroy
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 46931f9..5081a9e 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -13024,7 +13024,7 @@  static int intel_atomic_check(struct drm_device *dev,
 		to_intel_atomic_state(state)->cdclk =
 			to_i915(state->dev)->cdclk_freq;
 
-	return drm_atomic_helper_check_planes(state->dev, state);
+	return intel_atomic_check_planes(state->dev, state);
 }
 
 /**
@@ -14312,6 +14312,7 @@  static const struct drm_mode_config_funcs intel_mode_funcs = {
 	.atomic_check = intel_atomic_check,
 	.atomic_commit = intel_atomic_commit,
 	.atomic_state_alloc = intel_atomic_state_alloc,
+	.atomic_state_free = intel_atomic_state_free,
 	.atomic_state_clear = intel_atomic_state_clear,
 };
 
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 8b7e3d9..e2aa7b6 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -237,6 +237,15 @@  typedef struct dpll {
 struct intel_atomic_state {
 	struct drm_atomic_state base;
 
+	/*
+	 * A second top-level state object that holds the intermediate state
+	 * that the driver passes through when performing a modeset or
+	 * disabling CRTC's.  This state corresponds to the point where we've
+	 * disabled the CRTC's undergoing modesets.
+	 */
+	struct drm_atomic_state *intermediate;
+
+	bool any_ms;
 	unsigned int cdclk;
 	bool dpll_set;
 	struct intel_shared_dpll_config shared_dpll[I915_NUM_PLLS];
@@ -1419,6 +1428,7 @@  struct drm_crtc_state *intel_crtc_duplicate_state(struct drm_crtc *crtc);
 void intel_crtc_destroy_state(struct drm_crtc *crtc,
 			       struct drm_crtc_state *state);
 struct drm_atomic_state *intel_atomic_state_alloc(struct drm_device *dev);
+void intel_atomic_state_free(struct drm_atomic_state *s);
 void intel_atomic_state_clear(struct drm_atomic_state *);
 struct intel_shared_dpll_config *
 intel_atomic_get_shared_dpll_state(struct drm_atomic_state *s);
@@ -1437,6 +1447,8 @@  intel_atomic_get_crtc_state(struct drm_atomic_state *state,
 int intel_atomic_setup_scalers(struct drm_device *dev,
 	struct intel_crtc *intel_crtc,
 	struct intel_crtc_state *crtc_state);
+int intel_atomic_check_planes(struct drm_device *dev,
+			      struct drm_atomic_state *state);
 
 /* intel_atomic_plane.c */
 struct intel_plane_state *intel_create_plane_state(struct drm_plane *plane);
@@ -1444,5 +1456,7 @@  struct drm_plane_state *intel_plane_duplicate_state(struct drm_plane *plane);
 void intel_plane_destroy_state(struct drm_plane *plane,
 			       struct drm_plane_state *state);
 extern const struct drm_plane_helper_funcs intel_plane_helper_funcs;
+struct drm_plane_state *
+__intel_plane_duplicate_state(struct drm_plane_state *ps);
 
 #endif /* __INTEL_DRV_H__ */