diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 59829ec81694..97a7dfc620e8 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -1839,7 +1839,6 @@ static int dm_resume(void *handle)
 	struct drm_plane *plane;
 	struct drm_plane_state *new_plane_state;
 	struct dm_plane_state *dm_new_plane_state;
-	struct dm_atomic_state *dm_state = to_dm_atomic_state(dm->atomic_obj.state);
 	enum dc_connection_type new_connection_type = dc_connection_none;
 	struct dc_state *dc_state;
 	int i, r, j;
@@ -1879,11 +1878,6 @@ static int dm_resume(void *handle)
 
 		return 0;
 	}
-	/* Recreate dc_state - DC invalidates it when setting power state to S3. */
-	dc_release_state(dm_state->context);
-	dm_state->context = dc_create_state(dm->dc);
-	/* TODO: Remove dc_state->dccg, use dc->dccg directly. */
-	dc_resource_state_construct(dm->dc, dm_state->context);
 
 	/* Before powering on DC we need to re-initialize DMUB. */
 	r = dm_dmub_hw_init(adev);
@@ -2019,11 +2013,51 @@ const struct amdgpu_ip_block_version dm_ip_block =
  * *WIP*
  */
 
+struct drm_atomic_state *dm_atomic_state_alloc(struct drm_device *dev)
+{
+	struct dm_atomic_state *dm_state;
+
+	dm_state = kzalloc(sizeof(*dm_state), GFP_KERNEL);
+
+	if (!dm_state)
+		return NULL;
+
+	if (drm_atomic_state_init(dev, &dm_state->base) < 0) {
+		kfree(dm_state);
+		return NULL;
+	}
+
+	return &dm_state->base;
+}
+
+void dm_atomic_state_free(struct drm_atomic_state *state)
+{
+	struct dm_atomic_state *dm_state = to_dm_atomic_state(state);
+
+	if (dm_state->context) {
+		dc_release_state(dm_state->context);
+		dm_state->context = NULL;
+	}
+
+	drm_atomic_state_default_release(state);
+	kfree(state);
+}
+
+void dm_atomic_state_clear(struct drm_atomic_state *state)
+{
+	struct dm_atomic_state *dm_state = to_dm_atomic_state(state);
+
+	drm_atomic_state_default_clear(&dm_state->base);
+}
+
 static const struct drm_mode_config_funcs amdgpu_dm_mode_funcs = {
 	.fb_create = amdgpu_display_user_framebuffer_create,
 	.output_poll_changed = drm_fb_helper_output_poll_changed,
 	.atomic_check = amdgpu_dm_atomic_check,
 	.atomic_commit = amdgpu_dm_atomic_commit,
+	.atomic_state_alloc = dm_atomic_state_alloc,
+	.atomic_state_free = dm_atomic_state_free,
+	.atomic_state_clear = dm_atomic_state_clear,
 };
 
 static struct drm_mode_config_helper_funcs amdgpu_dm_mode_config_helperfuncs = {
@@ -2782,108 +2816,6 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev)
 }
 #endif
 
-/*
- * Acquires the lock for the atomic state object and returns
- * the new atomic state.
- *
- * This should only be called during atomic check.
- */
-static int dm_atomic_get_state(struct drm_atomic_state *state,
-			       struct dm_atomic_state **dm_state)
-{
-	struct drm_device *dev = state->dev;
-	struct amdgpu_device *adev = dev->dev_private;
-	struct amdgpu_display_manager *dm = &adev->dm;
-	struct drm_private_state *priv_state;
-
-	if (*dm_state)
-		return 0;
-
-	priv_state = drm_atomic_get_private_obj_state(state, &dm->atomic_obj);
-	if (IS_ERR(priv_state))
-		return PTR_ERR(priv_state);
-
-	*dm_state = to_dm_atomic_state(priv_state);
-
-	return 0;
-}
-
-static struct dm_atomic_state *
-dm_atomic_get_new_state(struct drm_atomic_state *state)
-{
-	struct drm_device *dev = state->dev;
-	struct amdgpu_device *adev = dev->dev_private;
-	struct amdgpu_display_manager *dm = &adev->dm;
-	struct drm_private_obj *obj;
-	struct drm_private_state *new_obj_state;
-	int i;
-
-	for_each_new_private_obj_in_state(state, obj, new_obj_state, i) {
-		if (obj->funcs == dm->atomic_obj.funcs)
-			return to_dm_atomic_state(new_obj_state);
-	}
-
-	return NULL;
-}
-
-static struct dm_atomic_state *
-dm_atomic_get_old_state(struct drm_atomic_state *state)
-{
-	struct drm_device *dev = state->dev;
-	struct amdgpu_device *adev = dev->dev_private;
-	struct amdgpu_display_manager *dm = &adev->dm;
-	struct drm_private_obj *obj;
-	struct drm_private_state *old_obj_state;
-	int i;
-
-	for_each_old_private_obj_in_state(state, obj, old_obj_state, i) {
-		if (obj->funcs == dm->atomic_obj.funcs)
-			return to_dm_atomic_state(old_obj_state);
-	}
-
-	return NULL;
-}
-
-static struct drm_private_state *
-dm_atomic_duplicate_state(struct drm_private_obj *obj)
-{
-	struct dm_atomic_state *old_state, *new_state;
-
-	new_state = kzalloc(sizeof(*new_state), GFP_KERNEL);
-	if (!new_state)
-		return NULL;
-
-	__drm_atomic_helper_private_obj_duplicate_state(obj, &new_state->base);
-
-	old_state = to_dm_atomic_state(obj->state);
-
-	if (old_state && old_state->context)
-		new_state->context = dc_copy_state(old_state->context);
-
-	if (!new_state->context) {
-		kfree(new_state);
-		return NULL;
-	}
-
-	return &new_state->base;
-}
-
-static void dm_atomic_destroy_state(struct drm_private_obj *obj,
-				    struct drm_private_state *state)
-{
-	struct dm_atomic_state *dm_state = to_dm_atomic_state(state);
-
-	if (dm_state && dm_state->context)
-		dc_release_state(dm_state->context);
-
-	kfree(dm_state);
-}
-
-static struct drm_private_state_funcs dm_atomic_state_funcs = {
-	.atomic_duplicate_state = dm_atomic_duplicate_state,
-	.atomic_destroy_state = dm_atomic_destroy_state,
-};
-
 static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev)
 {
 	struct dm_atomic_state *state;
@@ -2916,11 +2848,6 @@ static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev)
 
 	dc_resource_state_copy_construct_current(adev->dm.dc, state->context);
 
-	drm_atomic_private_obj_init(adev->ddev,
-				    &adev->dm.atomic_obj,
-				    &state->base,
-				    &dm_atomic_state_funcs);
-
 	r = amdgpu_display_modeset_create_props(adev);
 	if (r)
 		return r;
@@ -3360,7 +3287,6 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
 static void amdgpu_dm_destroy_drm_device(struct amdgpu_display_manager *dm)
 {
 	drm_mode_config_cleanup(dm->ddev);
-	drm_atomic_private_obj_fini(&dm->atomic_obj);
 	return;
 }
 
@@ -7533,7 +7459,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
 	struct drm_device *dev = state->dev;
 	struct amdgpu_device *adev = dev->dev_private;
 	struct amdgpu_display_manager *dm = &adev->dm;
-	struct dm_atomic_state *dm_state;
+	struct dm_atomic_state *dm_state = to_dm_atomic_state(state);
 	struct dc_state *dc_state = NULL, *dc_state_temp = NULL;
 	uint32_t i, j;
 	struct drm_crtc *crtc;
@@ -7547,8 +7473,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
 
 	drm_atomic_helper_update_legacy_modeset_state(dev, state);
 
-	dm_state = dm_atomic_get_new_state(state);
-	if (dm_state && dm_state->context) {
+	if (dm_state->context) {
 		dc_state = dm_state->context;
 	} else {
 		/* No state changes, retain current state. */
@@ -8052,10 +7977,8 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
 				struct drm_crtc *crtc,
 				struct drm_crtc_state *old_crtc_state,
 				struct drm_crtc_state *new_crtc_state,
-				bool enable,
 				bool *lock_and_validation_needed)
 {
-	struct dm_atomic_state *dm_state = NULL;
 	struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state;
 	struct dc_stream_state *new_stream;
 	int ret = 0;
@@ -8077,7 +8000,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
 	aconnector = amdgpu_dm_find_first_crtc_matching_connector(state, crtc);
 
 	/* TODO This hack should go away */
-	if (aconnector && enable) {
+	if (aconnector) {
 		/* Make sure fake sink is created in plug-in scenario */
 		drm_new_conn_state = drm_atomic_get_new_connector_state(state,
 							    &aconnector->base);
@@ -8155,36 +8078,20 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
 		new_crtc_state->active_changed,
 		new_crtc_state->connectors_changed);
 
-	/* Remove stream for any changed/disabled CRTC */
-	if (!enable) {
-
-		if (!dm_old_crtc_state->stream)
-			goto skip_modeset;
-
-		ret = dm_atomic_get_state(state, &dm_state);
-		if (ret)
-			goto fail;
-
-		DRM_DEBUG_DRIVER("Disabling DRM crtc: %d\n",
-				crtc->base.id);
+	/* Remove stream for changed CRTC - can't reuse it for validation. */
+	if (dm_new_crtc_state->stream) {
+		DRM_DEBUG_DRIVER("Disabling DRM crtc: %d\n", crtc->base.id);
 
-		/* i.e. reset mode */
-		if (dc_remove_stream_from_ctx(
-				dm->dc,
-				dm_state->context,
-				dm_old_crtc_state->stream) != DC_OK) {
-			ret = -EINVAL;
-			goto fail;
-		}
-
-		dc_stream_release(dm_old_crtc_state->stream);
+		dc_stream_release(dm_new_crtc_state->stream);
 		dm_new_crtc_state->stream = NULL;
 
 		reset_freesync_config_for_crtc(dm_new_crtc_state);
 
 		*lock_and_validation_needed = true;
+	}
 
-	} else {/* Add stream for any updated/enabled CRTC */
+	/* Add stream for any updated/enabled CRTC - active implies enabled. */
+	if (new_crtc_state->active) {
 		/*
 		 * Quick fix to prevent NULL pointer on new_stream when
 		 * added MST connectors not found in existing crtc_state in the chained mode
@@ -8193,35 +8100,13 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
 		if (!aconnector || (!aconnector->dc_sink && aconnector->mst_port))
 			goto skip_modeset;
 
-		if (modereset_required(new_crtc_state))
-			goto skip_modeset;
-
-		if (modeset_required(new_crtc_state, new_stream,
-				     dm_old_crtc_state->stream)) {
-
-			WARN_ON(dm_new_crtc_state->stream);
-
-			ret = dm_atomic_get_state(state, &dm_state);
-			if (ret)
-				goto fail;
-
-			dm_new_crtc_state->stream = new_stream;
+		WARN_ON(dm_new_crtc_state->stream);
+		dm_new_crtc_state->stream = new_stream;
+		dc_stream_retain(new_stream);
 
-			dc_stream_retain(new_stream);
+		DRM_DEBUG_DRIVER("Enabling DRM crtc: %d\n", crtc->base.id);
 
-			DRM_DEBUG_DRIVER("Enabling DRM crtc: %d\n",
-						crtc->base.id);
-
-			if (dc_add_stream_to_ctx(
-					dm->dc,
-					dm_state->context,
-					dm_new_crtc_state->stream) != DC_OK) {
-				ret = -EINVAL;
-				goto fail;
-			}
-
-			*lock_and_validation_needed = true;
-		}
+		*lock_and_validation_needed = true;
 	}
 
 skip_modeset:
@@ -8233,7 +8118,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
 	 * We want to do dc stream updates that do not require a
 	 * full modeset below.
 	 */
-	if (!(enable && aconnector && new_crtc_state->active))
+	if (!(aconnector && new_crtc_state->enable && new_crtc_state->active))
 		return 0;
 	/*
 	 * Given above conditions, the dc state cannot be NULL because:
@@ -8281,10 +8166,12 @@ static bool should_reset_plane(struct drm_atomic_state *state,
 			       struct drm_plane_state *old_plane_state,
 			       struct drm_plane_state *new_plane_state)
 {
-	struct drm_plane *other;
-	struct drm_plane_state *old_other_state, *new_other_state;
 	struct drm_crtc_state *new_crtc_state;
-	int i;
+	struct dm_plane_state *old_dm_plane_state, *new_dm_plane_state;
+
+	/* Cursor planes don't affect bandwidth. */
+	if (plane->type == DRM_PLANE_TYPE_CURSOR)
+		return false;
 
 	/*
 	 * TODO: Remove this hack once the checks below are sufficient
@@ -8312,71 +8199,50 @@ static bool should_reset_plane(struct drm_atomic_state *state,
 	if (new_crtc_state->color_mgmt_changed)
 		return true;
 
+	/* Plane scaling can change with a modeset, so reset. */
 	if (drm_atomic_crtc_needs_modeset(new_crtc_state))
 		return true;
 
-	/*
-	 * If there are any new primary or overlay planes being added or
-	 * removed then the z-order can potentially change. To ensure
-	 * correct z-order and pipe acquisition the current DC architecture
-	 * requires us to remove and recreate all existing planes.
-	 *
-	 * TODO: Come up with a more elegant solution for this.
-	 */
-	for_each_oldnew_plane_in_state(state, other, old_other_state, new_other_state, i) {
-		struct dm_plane_state *old_dm_plane_state, *new_dm_plane_state;
-
-		if (other->type == DRM_PLANE_TYPE_CURSOR)
-			continue;
-
-		if (old_other_state->crtc != new_plane_state->crtc &&
-		    new_other_state->crtc != new_plane_state->crtc)
-			continue;
-
-		if (old_other_state->crtc != new_other_state->crtc)
-			return true;
-
-		/* Src/dst size and scaling updates. */
-		if (old_other_state->src_w != new_other_state->src_w ||
-		    old_other_state->src_h != new_other_state->src_h ||
-		    old_other_state->crtc_w != new_other_state->crtc_w ||
-		    old_other_state->crtc_h != new_other_state->crtc_h)
-			return true;
+	/* Src/dst size and scaling updates. */
+	if (old_plane_state->src_w != new_plane_state->src_w ||
+	    old_plane_state->src_h != new_plane_state->src_h ||
+	    old_plane_state->crtc_w != new_plane_state->crtc_w ||
+	    old_plane_state->crtc_h != new_plane_state->crtc_h)
+		return true;
 
-		/* Rotation / mirroring updates. */
-		if (old_other_state->rotation != new_other_state->rotation)
-			return true;
+	/* Rotation / mirroring updates. */
+	if (old_plane_state->rotation != new_plane_state->rotation)
+		return true;
 
-		/* Blending updates. */
-		if (old_other_state->pixel_blend_mode !=
-		    new_other_state->pixel_blend_mode)
-			return true;
+	/* Blending updates. */
+	if (old_plane_state->pixel_blend_mode !=
+	    new_plane_state->pixel_blend_mode)
+		return true;
 
-		/* Alpha updates. */
-		if (old_other_state->alpha != new_other_state->alpha)
-			return true;
+	/* Alpha updates. */
+	if (old_plane_state->alpha != new_plane_state->alpha)
+		return true;
 
-		/* Colorspace changes. */
-		if (old_other_state->color_range != new_other_state->color_range ||
-		    old_other_state->color_encoding != new_other_state->color_encoding)
-			return true;
+	/* Colorspace changes. */
+	if (old_plane_state->color_range != new_plane_state->color_range ||
+	    old_plane_state->color_encoding != new_plane_state->color_encoding)
+		return true;
 
-		/* Framebuffer checks fall at the end. */
-		if (!old_other_state->fb || !new_other_state->fb)
-			continue;
+	/* Framebuffer checks fall at the end. */
+	if (!old_plane_state->fb || !new_plane_state->fb)
+		return false;
 
-		/* Pixel format changes can require bandwidth updates. */
-		if (old_other_state->fb->format != new_other_state->fb->format)
-			return true;
+	/* Pixel format changes can require bandwidth updates. */
+	if (old_plane_state->fb->format != new_plane_state->fb->format)
+		return true;
 
-		old_dm_plane_state = to_dm_plane_state(old_other_state);
-		new_dm_plane_state = to_dm_plane_state(new_other_state);
+	old_dm_plane_state = to_dm_plane_state(old_plane_state);
+	new_dm_plane_state = to_dm_plane_state(new_plane_state);
 
-		/* Tiling and DCC changes also require bandwidth updates. */
-		if (old_dm_plane_state->tiling_flags !=
-		    new_dm_plane_state->tiling_flags)
-			return true;
-	}
+	/* Tiling and DCC changes also require bandwidth updates. */
+	if (old_dm_plane_state->tiling_flags !=
+	    new_dm_plane_state->tiling_flags)
+		return true;
 
 	return false;
 }
@@ -8386,15 +8252,12 @@ static int dm_update_plane_state(struct dc *dc,
 				 struct drm_plane *plane,
 				 struct drm_plane_state *old_plane_state,
 				 struct drm_plane_state *new_plane_state,
-				 bool enable,
 				 bool *lock_and_validation_needed)
 {
-
-	struct dm_atomic_state *dm_state = NULL;
 	struct drm_crtc *new_plane_crtc, *old_plane_crtc;
-	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
-	struct dm_crtc_state *dm_new_crtc_state, *dm_old_crtc_state;
-	struct dm_plane_state *dm_new_plane_state, *dm_old_plane_state;
+	struct drm_crtc_state *new_crtc_state;
+	struct dm_crtc_state *dm_new_crtc_state;
+	struct dm_plane_state *dm_new_plane_state;
 	struct amdgpu_crtc *new_acrtc;
 	bool needs_reset;
 	int ret = 0;
@@ -8403,12 +8266,12 @@ static int dm_update_plane_state(struct dc *dc,
 	new_plane_crtc = new_plane_state->crtc;
 	old_plane_crtc = old_plane_state->crtc;
 	dm_new_plane_state = to_dm_plane_state(new_plane_state);
-	dm_old_plane_state = to_dm_plane_state(old_plane_state);
 
 	/*TODO Implement better atomic check for cursor plane */
 	if (plane->type == DRM_PLANE_TYPE_CURSOR) {
-		if (!enable || !new_plane_crtc ||
-			drm_atomic_plane_disabling(plane->state, new_plane_state))
+		/* Cursor disabled is always OK. */
+		if (!new_plane_crtc ||
+		    drm_atomic_plane_disabling(plane->state, new_plane_state))
 			return 0;
 
 		new_acrtc = to_amdgpu_crtc(new_plane_crtc);
@@ -8423,123 +8286,72 @@ static int dm_update_plane_state(struct dc *dc,
 		return 0;
 	}
 
+	/* Check if the plane requires a reset for bandwidth validation. */
 	needs_reset = should_reset_plane(state, plane, old_plane_state,
 					 new_plane_state);
 
-	/* Remove any changed/removed planes */
-	if (!enable) {
-		if (!needs_reset)
-			return 0;
-
-		if (!old_plane_crtc)
-			return 0;
-
-		old_crtc_state = drm_atomic_get_old_crtc_state(
-				state, old_plane_crtc);
-		dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
-		if (!dm_old_crtc_state->stream)
-			return 0;
+	/* Exit early if the plane hasn't been trivially modified. */
+	if (!needs_reset)
+		return 0;
 
+	/**
+	 * Remove exisiting plane, if any - we can't reuse it for validation
+	 * because we'd be modifying the current state applied to HW.
+	 */
+	if (dm_new_plane_state->dc_state) {
 		DRM_DEBUG_ATOMIC("Disabling DRM plane: %d on DRM crtc %d\n",
-				plane->base.id, old_plane_crtc->base.id);
-
-		ret = dm_atomic_get_state(state, &dm_state);
-		if (ret)
-			return ret;
+				 plane->base.id, old_plane_crtc->base.id);
 
-		if (!dc_remove_plane_from_context(
-				dc,
-				dm_old_crtc_state->stream,
-				dm_old_plane_state->dc_state,
-				dm_state->context)) {
-
-			ret = EINVAL;
-			return ret;
-		}
-
-
-		dc_plane_state_release(dm_old_plane_state->dc_state);
+		dc_plane_state_release(dm_new_plane_state->dc_state);
 		dm_new_plane_state->dc_state = NULL;
 
 		*lock_and_validation_needed = true;
+	}
 
-	} else { /* Add new planes */
-		struct dc_plane_state *dc_new_plane_state;
-
-		if (drm_atomic_plane_disabling(plane->state, new_plane_state))
-			return 0;
-
-		if (!new_plane_crtc)
-			return 0;
-
-		new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_crtc);
-		dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
-
-		if (!dm_new_crtc_state->stream)
-			return 0;
-
-		if (!needs_reset)
-			return 0;
-
-		ret = dm_plane_helper_check_state(new_plane_state, new_crtc_state);
-		if (ret)
-			return ret;
-
-		WARN_ON(dm_new_plane_state->dc_state);
-
-		dc_new_plane_state = dc_create_plane_state(dc);
-		if (!dc_new_plane_state)
-			return -ENOMEM;
-
-		DRM_DEBUG_DRIVER("Enabling DRM plane: %d on DRM crtc %d\n",
-				plane->base.id, new_plane_crtc->base.id);
+	/**
+	 * If the plane is disabling exit early. Also serves as a guarantee
+	 * that we have a framebuffer below if we do have a CRTC.
+	 */
+	if (drm_atomic_plane_disabling(plane->state, new_plane_state))
+		return 0;
 
-		ret = fill_dc_plane_attributes(
-			new_plane_crtc->dev->dev_private,
-			dc_new_plane_state,
-			new_plane_state,
-			new_crtc_state);
-		if (ret) {
-			dc_plane_state_release(dc_new_plane_state);
-			return ret;
-		}
+	/* If we don't have a CRTC then the plane is disabled. */
+	if (!new_plane_crtc)
+		return 0;
 
-		ret = dm_atomic_get_state(state, &dm_state);
-		if (ret) {
-			dc_plane_state_release(dc_new_plane_state);
-			return ret;
-		}
+	new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_crtc);
+	dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
 
-		/*
-		 * Any atomic check errors that occur after this will
-		 * not need a release. The plane state will be attached
-		 * to the stream, and therefore part of the atomic
-		 * state. It'll be released when the atomic state is
-		 * cleaned.
-		 */
-		if (!dc_add_plane_to_context(
-				dc,
-				dm_new_crtc_state->stream,
-				dc_new_plane_state,
-				dm_state->context)) {
+	/* Don't enable the plane if there's no stream for output. */
+	if (!dm_new_crtc_state->stream)
+		return 0;
 
-			dc_plane_state_release(dc_new_plane_state);
-			return -EINVAL;
-		}
+	/**
+	 * Freeing the plane will take care of freeing the dc_state
+	 * on failure, so we don't need to explicitly release below.
+	 */
+	dm_new_plane_state->dc_state = dc_create_plane_state(dc);
+	if (!dm_new_plane_state->dc_state)
+		return -ENOMEM;
 
-		dm_new_plane_state->dc_state = dc_new_plane_state;
+	DRM_DEBUG_DRIVER("Enabling DRM plane: %d on DRM crtc %d\n",
+			 plane->base.id, new_plane_crtc->base.id);
 
-		/* Tell DC to do a full surface update every time there
-		 * is a plane change. Inefficient, but works for now.
-		 */
-		dm_new_plane_state->dc_state->update_flags.bits.full_update = 1;
+	ret = fill_dc_plane_attributes(new_plane_crtc->dev->dev_private,
+				       dm_new_plane_state->dc_state,
+				       new_plane_state, new_crtc_state);
+	if (ret)
+		return ret;
 
-		*lock_and_validation_needed = true;
-	}
+	/**
+	 * Tell DC to do a full surface update every time there
+	 * is a plane change. Inefficient, but works for now.
+	 */
+	dm_new_plane_state->dc_state->update_flags.bits.full_update = 1;
 
+	*lock_and_validation_needed = true;
 
-	return ret;
+	return 0;
 }
 
 #if defined(CONFIG_DRM_AMD_DC_DCN)
@@ -8567,6 +8379,113 @@ static int add_affected_mst_dsc_crtcs(struct drm_atomic_state *state, struct drm
 }
 #endif
 
+static int dm_atomic_state_init_context(struct drm_device *dev,
+					struct drm_atomic_state *state)
+{
+	struct dm_atomic_state *dm_state = to_dm_atomic_state(state);
+	struct amdgpu_device *adev = dev->dev_private;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *new_crtc_state;
+	struct dm_crtc_state *new_dm_crtc_state;
+	struct drm_plane *plane;
+	struct drm_plane_state *new_plane_state, *old_plane_state;
+	struct dm_plane_state *new_dm_plane_state;
+	int ret, i;
+
+	dm_state->context = dc_create_state(adev->dm.dc);
+	if (!dm_state->context)
+		return -ENOMEM;
+
+	/**
+	 * DC validation requires an absolute set of streams and planes to work
+	 * with so add all planes and CRTCs to the state to make this work.
+	 * Pipe allocation can change so there's no easy way to work around
+	 * this constraint.
+	 *
+	 * Unfortunately this also means that whenever userspace requests a
+	 * change that only needs to modify one CRTC's planes we still have to
+	 * stall out fast updates affecting other CRTCs - introducing judder
+	 * in multi-monitor scenarios.
+	 *
+	 * Userspace should avoid frequent updates to properties that can
+	 * require bandwidth changes.
+	 */
+	drm_for_each_crtc(crtc, dev) {
+		new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
+		if (IS_ERR(new_crtc_state))
+			return PTR_ERR(new_crtc_state);
+
+		/**
+		 * Cursor planes may not strictly be necessary here
+		 * but it's probably safer to add them.
+		 */
+		ret = drm_atomic_add_affected_planes(state, crtc);
+		if (ret)
+			return ret;
+	}
+
+	for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
+		new_dm_crtc_state = to_dm_crtc_state(new_crtc_state);
+
+		if (!new_dm_crtc_state->stream)
+			continue;
+
+		if (dc_add_stream_to_ctx(adev->dm.dc, dm_state->context,
+					 new_dm_crtc_state->stream) != DC_OK)
+			return -EINVAL;
+	}
+
+	/**
+	 * Planes are added in reverse order to ensure correct blending order
+	 * in DC - planes with higher priority go first, and the cursor and
+	 * MPO planes are at the very end of the list.
+	 */
+	for_each_oldnew_plane_in_state_reverse(state, plane, old_plane_state, new_plane_state, i) {
+		/* Cursors aren't real hardware planes. */
+		if (plane->type == DRM_PLANE_TYPE_CURSOR)
+			continue;
+
+		if (!new_plane_state->crtc)
+			continue;
+
+		new_crtc_state = drm_atomic_get_new_crtc_state(
+			state, new_plane_state->crtc);
+
+		if (!new_crtc_state) {
+			DRM_WARN("No CRTC state found: plane=%d crtc=%d\n",
+				 plane->base.id,
+				 new_plane_state->crtc->base.id);
+			return -EINVAL;
+		}
+
+		new_dm_crtc_state = to_dm_crtc_state(new_crtc_state);
+
+		/* Skip planes for disabled streams. */
+		if (!new_dm_crtc_state->stream)
+			continue;
+
+		new_dm_plane_state = to_dm_plane_state(new_plane_state);
+
+		if (!new_dm_plane_state->dc_state) {
+			DRM_WARN("No DC state found: plane=%d crtc=%d\n",
+				 plane->base.id,
+				 new_plane_state->crtc->base.id);
+			return -EINVAL;
+		}
+
+		if (!dc_add_plane_to_context(
+			    adev->dm.dc, new_dm_crtc_state->stream,
+			    new_dm_plane_state->dc_state, dm_state->context)) {
+			DRM_DEBUG_KMS(
+				"Couldn't add plane to context: plane=%d\n",
+				plane->base.id);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
 /**
  * amdgpu_dm_atomic_check() - Atomic check implementation for AMDgpu DM.
  * @dev: The DRM device
@@ -8595,7 +8514,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
 				  struct drm_atomic_state *state)
 {
 	struct amdgpu_device *adev = dev->dev_private;
-	struct dm_atomic_state *dm_state = NULL;
+	struct dm_atomic_state *dm_state = to_dm_atomic_state(state);
 	struct dc *dc = adev->dm.dc;
 	struct drm_connector *connector;
 	struct drm_connector_state *old_con_state, *new_con_state;
@@ -8607,6 +8526,11 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
 	int ret, i;
 	bool lock_and_validation_needed = false;
 
+	/**
+	 * Check for modesets on CRTCs in the new state. DRM internally checks
+	 * that the mode has actually changed for us as well in here, so we can
+	 * avoid modesets.
+	 */
 	ret = drm_atomic_helper_check_modeset(dev, state);
 	if (ret)
 		goto fail;
@@ -8634,6 +8558,18 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
 			new_crtc_state->connectors_changed = true;
 	}
 
+	/**
+	 * Add all required objects for performing the commit and stalling out
+	 * other commits that may be touching the same resources.
+	 */
+
+	/**
+	 * Pass one: Add all affected CRTCs on a MST DSC topology that has a
+	 * CRTC undergoing a modeset and mark mode_changed = true for each one.
+	 *
+	 * Optimization: DSC can only be supported on DCN2 onwards, so we can
+	 * skip on earlier ASIC.
+	 */
 #if defined(CONFIG_DRM_AMD_DC_DCN)
 	if (adev->asic_type >= CHIP_NAVI10) {
 		for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
@@ -8645,6 +8581,8 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
 		}
 	}
 #endif
+
+	/* Pass two: Add connectors and planes for CRTCs as needed.  */
 	for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
 		if (!drm_atomic_crtc_needs_modeset(new_crtc_state) &&
 		    !new_crtc_state->color_mgmt_changed &&
@@ -8663,42 +8601,6 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
 			goto fail;
 	}
 
-	/*
-	 * Add all primary and overlay planes on the CRTC to the state
-	 * whenever a plane is enabled to maintain correct z-ordering
-	 * and to enable fast surface updates.
-	 */
-	drm_for_each_crtc(crtc, dev) {
-		bool modified = false;
-
-		for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
-			if (plane->type == DRM_PLANE_TYPE_CURSOR)
-				continue;
-
-			if (new_plane_state->crtc == crtc ||
-			    old_plane_state->crtc == crtc) {
-				modified = true;
-				break;
-			}
-		}
-
-		if (!modified)
-			continue;
-
-		drm_for_each_plane_mask(plane, state->dev, crtc->state->plane_mask) {
-			if (plane->type == DRM_PLANE_TYPE_CURSOR)
-				continue;
-
-			new_plane_state =
-				drm_atomic_get_plane_state(state, plane);
-
-			if (IS_ERR(new_plane_state)) {
-				ret = PTR_ERR(new_plane_state);
-				goto fail;
-			}
-		}
-	}
-
 	/* Prepass for updating tiling flags on new planes. */
 	for_each_new_plane_in_state(state, plane, new_plane_state, i) {
 		struct dm_plane_state *new_dm_plane_state = to_dm_plane_state(new_plane_state);
@@ -8710,45 +8612,21 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
 			goto fail;
 	}
 
-	/* Remove exiting planes if they are modified */
-	for_each_oldnew_plane_in_state_reverse(state, plane, old_plane_state, new_plane_state, i) {
-		ret = dm_update_plane_state(dc, state, plane,
-					    old_plane_state,
-					    new_plane_state,
-					    false,
-					    &lock_and_validation_needed);
-		if (ret)
-			goto fail;
-	}
-
-	/* Disable all crtcs which require disable */
+	/* Update modified CRTCs. */
 	for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
 		ret = dm_update_crtc_state(&adev->dm, state, crtc,
 					   old_crtc_state,
 					   new_crtc_state,
-					   false,
 					   &lock_and_validation_needed);
 		if (ret)
 			goto fail;
 	}
 
-	/* Enable all crtcs which require enable */
-	for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
-		ret = dm_update_crtc_state(&adev->dm, state, crtc,
-					   old_crtc_state,
-					   new_crtc_state,
-					   true,
-					   &lock_and_validation_needed);
-		if (ret)
-			goto fail;
-	}
-
-	/* Add new/modified planes */
+	/* Update modified planes. */
 	for_each_oldnew_plane_in_state_reverse(state, plane, old_plane_state, new_plane_state, i) {
 		ret = dm_update_plane_state(dc, state, plane,
 					    old_plane_state,
 					    new_plane_state,
-					    true,
 					    &lock_and_validation_needed);
 		if (ret)
 			goto fail;
@@ -8812,10 +8690,11 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
 	 * DRM state directly - we can end up disabling interrupts too early
 	 * if we don't.
 	 *
-	 * TODO: Remove this stall and drop DM state private objects.
+	 * TODO: Remove this stall.
 	 */
 	if (lock_and_validation_needed) {
-		ret = dm_atomic_get_state(state, &dm_state);
+		/* Create a new DC context to validate. */
+		ret = dm_atomic_state_init_context(dev, state);
 		if (ret)
 			goto fail;
 
@@ -8848,47 +8727,6 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
 			ret = -EINVAL;
 			goto fail;
 		}
-	} else {
-		/*
-		 * The commit is a fast update. Fast updates shouldn't change
-		 * the DC context, affect global validation, and can have their
-		 * commit work done in parallel with other commits not touching
-		 * the same resource. If we have a new DC context as part of
-		 * the DM atomic state from validation we need to free it and
-		 * retain the existing one instead.
-		 *
-		 * Furthermore, since the DM atomic state only contains the DC
-		 * context and can safely be annulled, we can free the state
-		 * and clear the associated private object now to free
-		 * some memory and avoid a possible use-after-free later.
-		 */
-
-		for (i = 0; i < state->num_private_objs; i++) {
-			struct drm_private_obj *obj = state->private_objs[i].ptr;
-
-			if (obj->funcs == adev->dm.atomic_obj.funcs) {
-				int j = state->num_private_objs-1;
-
-				dm_atomic_destroy_state(obj,
-						state->private_objs[i].state);
-
-				/* If i is not at the end of the array then the
-				 * last element needs to be moved to where i was
-				 * before the array can safely be truncated.
-				 */
-				if (i != j)
-					state->private_objs[i] =
-						state->private_objs[j];
-
-				state->private_objs[j].ptr = NULL;
-				state->private_objs[j].state = NULL;
-				state->private_objs[j].old_state = NULL;
-				state->private_objs[j].new_state = NULL;
-
-				state->num_private_objs = j;
-				break;
-			}
-		}
 	}
 
 	/* Store the overall update type for use later in atomic check. */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
index ad025f5cfd3e..1c3aa7cb83b9 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -217,15 +217,6 @@ struct amdgpu_display_manager {
 	struct drm_device *ddev;
 	u16 display_indexes_num;
 
-	/**
-	 * @atomic_obj:
-	 *
-	 * In combination with &dm_atomic_state it helps manage
-	 * global atomic state that doesn't map cleanly into existing
-	 * drm resources, like &dc_context.
-	 */
-	struct drm_private_obj atomic_obj;
-
 	/**
 	 * @dc_lock:
 	 *
@@ -440,7 +431,7 @@ struct dm_crtc_state {
 #define to_dm_crtc_state(x) container_of(x, struct dm_crtc_state, base)
 
 struct dm_atomic_state {
-	struct drm_private_state base;
+	struct drm_atomic_state base;
 
 	struct dc_state *context;
 };
