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__ */
