From: Maxime Ripard <mripard@kernel.org>
To: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>,
Thomas Zimmermann <tzimmermann@suse.de>,
David Airlie <airlied@gmail.com>,
Simona Vetter <simona@ffwll.ch>,
Louis Chauvet <louis.chauvet@bootlin.com>,
Haneen Mohammed <hamohammed.sa@gmail.com>,
Melissa Wen <melissa.srw@gmail.com>
Cc: "Daniel Stone" <daniels@collabora.com>,
"Harry Wentland" <harry.wentland@amd.com>,
"Jocelyn Falempe" <jfalempe@redhat.com>,
"Jonas Ådahl" <jadahl@redhat.com>,
"Michel Dänzer" <mdaenzer@redhat.com>,
"Pekka Paalanen" <pekka.paalanen@collabora.com>,
"Sebastian Wick" <sebastian.wick@redhat.com>,
"Simon Ser" <contact@emersion.fr>,
"Victoria Brekenfeld" <victoria@system76.com>,
"Xaver Hugl" <xaver.hugl@kde.org>,
dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
"Maxime Ripard" <mripard@kernel.org>
Subject: [PATCH v7 12/16] drm/atomic: Allow filling a commit with pristine object states
Date: Fri, 30 Oct 2026 11:27:44 +0100 [thread overview]
Message-ID: <20261030-drm-reset-state-flag-v7-12-4e55eaeae683@kernel.org> (raw)
In-Reply-To: <20261030-drm-reset-state-flag-v7-0-4e55eaeae683@kernel.org>
The upcoming DRM_MODE_ATOMIC_RESET flag will need to create an atomic
commit that brings the entire device back to a pristine state, as if
no configuration had ever been applied.
Create drm_atomic_commit_fill_with_defaults() which iterates over all
CRTCs, planes, connectors, and color operations in the device and
inserts a fresh default state for each one into the commit. This uses
the atomic_create_state() hooks rather than atomic_duplicate_state(),
since atomic_create_state() provides exactly this pristine state on a
per-object basis.
Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
drivers/gpu/drm/drm_atomic.c | 285 +++++++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/drm_ioctl.c | 1 +
include/drm/drm_atomic.h | 2 +
3 files changed, 288 insertions(+)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 7a77051096c9..6bc0d6339f14 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1650,4 +1650,289 @@ bool drm_atomic_implements_create_state(struct drm_device *dev)
EXPORT_SYMBOL(drm_atomic_implements_create_state);
+/*
+ * Returns true if the colorop state @new differs from the colorop state
+ * @old.
+ */
+static bool drm_atomic_colorop_state_changed(const struct drm_colorop_state *old,
+ const struct drm_colorop_state *new)
+{
+ return old->bypass != new->bypass ||
+ old->curve_1d_type != new->curve_1d_type ||
+ old->multiplier != new->multiplier ||
+ old->data != new->data ||
+ old->lut1d_interpolation != new->lut1d_interpolation ||
+ old->lut3d_interpolation != new->lut3d_interpolation ||
+ old->fixed_matrix_type != new->fixed_matrix_type;
+}
+
+/**
+ * drm_atomic_commit_fill_with_defaults - populate a commit with pristine states
+ * @file_priv: DRM file the reset was requested from, or NULL
+ * @commit: atomic commit to fill
+ *
+ * Iterate over all CRTCs, planes, connectors, and color operations in
+ * the device and insert a freshly created default state for each one
+ * into @commit. The states are created through the atomic_create_state()
+ * hooks, producing the same initial state the driver starts with rather
+ * than a copy of the current hardware state.
+ *
+ * If @file_priv is a lessee, only the CRTCs, planes and connectors part
+ * of its lease are reset, and the other ones are left untouched.
+ * @file_priv is optional: if NULL, it is considered to not hold any
+ * lease, and all objects are reset.
+ *
+ * The objects that are bound to the objects being reset are still added
+ * to the commit, with their current state: the CRTC a reset plane or
+ * connector is bound to, and the connectors bound to a reset CRTC. The
+ * atomic helpers require them to be part of the commit to update the
+ * CRTC state and the routing, and they would dereference a NULL state
+ * otherwise.
+ *
+ * In the default state, all objects are disabled: CRTCs are inactive,
+ * planes are disabled, and connectors are unbound. Driver-specific
+ * properties such as color pipeline settings or max_bpc may carry
+ * non-zero defaults appropriate for basic display output.
+ *
+ * The CRTC and plane states in the commit carry color_mgmt_changed set
+ * when the reset changes the color management properties compared to the
+ * current state, so that drivers reprogram the color management
+ * hardware.
+ *
+ * The connector states keep the encoder currently in use in their
+ * best_encoder field, like a duplicated state would, so that the atomic
+ * helpers can detect the commits that don't change the connector
+ * routing.
+ *
+ * This is meant to be used with the %DRM_MODE_ATOMIC_RESET flag, which
+ * needs to bring the device back to a known baseline before applying
+ * userspace property changes on top.
+ *
+ * Returns:
+ * 0 on success, or a negative error code on failure.
+ */
+int drm_atomic_commit_fill_with_defaults(struct drm_file *file_priv,
+ struct drm_atomic_commit *commit)
+{
+ struct drm_device *dev = commit->dev;
+ struct drm_mode_config *config = &dev->mode_config;
+ struct drm_crtc *crtc;
+ struct drm_plane *plane;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+ struct drm_colorop *colorop;
+ int ret;
+
+ drm_WARN_ON(dev, !commit->acquire_ctx);
+
+ /*
+ * Private objects are ignored because none have userspace
+ * properties we might want to reset. atomic_check
+ * implementations will derive or infer their private obj state
+ * from the state that will end up being committed anyway.
+ *
+ * The CRTCs are handled before the planes and connectors so that,
+ * when those pull the CRTC they are currently bound to into the
+ * commit, a leased CRTC is already there with its pristine state
+ * while an unleased one gets duplicated and keeps its current
+ * state.
+ */
+ drm_for_each_crtc(crtc, dev) {
+ struct drm_crtc_state *crtc_state;
+
+ if (!drm_lease_held(file_priv, crtc->base.id))
+ continue;
+
+ ret = drm_modeset_lock(&crtc->mutex, commit->acquire_ctx);
+ if (ret)
+ return ret;
+
+ crtc_state = crtc->funcs->atomic_create_state(crtc);
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
+
+ /*
+ * The userspace property handlers only set
+ * color_mgmt_changed when a property write changes the
+ * value stored in the new state. Since the pristine states
+ * we create here are already set to their default values,
+ * restoring those defaults wouldn't set the flag, and
+ * neither would leaving the properties untouched, even
+ * though the commit clears whatever LUT or CTM is
+ * currently programmed. Compare with the current state
+ * so that the drivers reprogram the color management
+ * hardware.
+ */
+ crtc_state->color_mgmt_changed =
+ crtc->state->degamma_lut != crtc_state->degamma_lut ||
+ crtc->state->ctm != crtc_state->ctm ||
+ crtc->state->gamma_lut != crtc_state->gamma_lut;
+
+ drm_atomic_commit_set_crtc_state(commit, crtc, crtc_state);
+ }
+
+ drm_for_each_plane(plane, dev) {
+ struct drm_plane_state *plane_state;
+
+ if (!drm_lease_held(file_priv, plane->base.id))
+ continue;
+
+ ret = drm_modeset_lock(&plane->mutex, commit->acquire_ctx);
+ if (ret)
+ return ret;
+
+ /*
+ * Make sure the CRTC the plane is currently enabled on is
+ * part of the commit, like drm_atomic_set_crtc_for_plane()
+ * does when userspace disables a plane. The atomic helpers
+ * need the CRTC state to update planes_changed and track
+ * the commit, and would dereference a NULL CRTC state
+ * otherwise.
+ */
+ if (plane->state->crtc) {
+ struct drm_crtc_state *crtc_state;
+
+ crtc_state = drm_atomic_get_crtc_state(commit,
+ plane->state->crtc);
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
+ }
+
+ plane_state = plane->funcs->atomic_create_state(plane);
+ if (IS_ERR(plane_state))
+ return PTR_ERR(plane_state);
+
+ /*
+ * Same thing for the planes: report a color pipeline change
+ * compared to the current state.
+ */
+ plane_state->color_mgmt_changed =
+ plane->state->color_pipeline != plane_state->color_pipeline;
+
+ drm_atomic_commit_set_plane_state(commit, plane, plane_state);
+ }
+
+ drm_for_each_colorop(colorop, dev) {
+ struct drm_plane_state *plane_state;
+ struct drm_colorop_state *colorop_state;
+
+ if (!drm_lease_held(file_priv, colorop->plane->base.id))
+ continue;
+
+ ret = drm_modeset_lock(&colorop->plane->mutex, commit->acquire_ctx);
+ if (ret)
+ return ret;
+
+ colorop_state = drm_atomic_helper_colorop_create_state(colorop);
+ if (IS_ERR(colorop_state))
+ return PTR_ERR(colorop_state);
+
+ drm_atomic_commit_set_colorop_state(commit, colorop, colorop_state);
+
+ /*
+ * Report the colorop state changes on their plane state,
+ * like drm_atomic_set_property() does when userspace
+ * changes a colorop property.
+ */
+ if (drm_atomic_colorop_state_changed(colorop->state, colorop_state)) {
+ plane_state = drm_atomic_get_new_plane_state(commit,
+ colorop->plane);
+ if (plane_state)
+ plane_state->color_mgmt_changed = true;
+ }
+ }
+
+ ret = drm_modeset_lock(&config->connection_mutex, commit->acquire_ctx);
+ if (ret)
+ return ret;
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ struct drm_connector_state *connector_state;
+ struct drm_crtc_state *crtc_state;
+
+ if (!drm_lease_held(file_priv, connector->base.id)) {
+ /*
+ * The connector isn't part of the lease so it keeps
+ * its current state, but if it is bound to a CRTC
+ * that is part of the commit, pull it in like
+ * drm_atomic_add_affected_connectors() would do on a
+ * regular commit, so that its routing and encoder
+ * are properly handled.
+ */
+ if (!connector->state->crtc)
+ continue;
+
+ if (!drm_atomic_get_new_crtc_state(commit,
+ connector->state->crtc))
+ continue;
+
+ connector_state = drm_atomic_get_connector_state(commit,
+ connector);
+ if (IS_ERR(connector_state)) {
+ drm_connector_list_iter_end(&conn_iter);
+ return PTR_ERR(connector_state);
+ }
+
+ continue;
+ }
+
+ /*
+ * drm_atomic_implements_create_state() already checked
+ * this, but connectors can be hotplugged between that
+ * lockless check and the iteration here.
+ */
+ if (!connector->funcs->atomic_create_state) {
+ drm_connector_list_iter_end(&conn_iter);
+ return -EOPNOTSUPP;
+ }
+
+ connector_state = connector->funcs->atomic_create_state(connector);
+ if (IS_ERR(connector_state)) {
+ drm_connector_list_iter_end(&conn_iter);
+ ret = PTR_ERR(connector_state);
+ return ret;
+ }
+
+ /*
+ * best_encoder is a routing information maintained by the
+ * atomic helpers rather than a userspace property, and it
+ * is only carried over to a new state when that state is a
+ * duplicate of the current one. Since
+ * update_connector_routing() uses it to detect the commits
+ * that keep the connector routing unchanged, let's carry
+ * the encoder currently in use over so that a reset on
+ * which userspace re-applies the exact same configuration
+ * doesn't require a modeset.
+ */
+ connector_state->best_encoder = connector->state->best_encoder;
+
+ ret = drm_atomic_commit_set_connector_state(commit, connector, connector_state);
+ if (ret) {
+ connector->funcs->atomic_destroy_state(connector, connector_state);
+ drm_connector_list_iter_end(&conn_iter);
+ return ret;
+ }
+
+ /*
+ * Make sure the CRTC the connector is currently bound to
+ * is part of the commit, like drm_atomic_get_connector_state()
+ * does, so that the atomic helpers can update the CRTC
+ * state and the connector routing.
+ */
+ if (connector->state->crtc) {
+ crtc_state = drm_atomic_get_crtc_state(commit,
+ connector->state->crtc);
+ if (IS_ERR(crtc_state)) {
+ drm_connector_list_iter_end(&conn_iter);
+ return PTR_ERR(crtc_state);
+ }
+ }
+ }
+ drm_connector_list_iter_end(&conn_iter);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_atomic_commit_fill_with_defaults);
+
/**
* drm_atomic_add_encoder_bridges - add bridges attached to an encoder
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index 9039a39c4324..0dbf04d4aa9e 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -34,4 +34,5 @@
#include <linux/uaccess.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_auth.h>
#include <drm/drm_crtc.h>
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index 72c3d420f40c..cac74d43a34d 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -704,4 +704,6 @@ void drm_atomic_commit_clear(struct drm_atomic_commit *state);
bool drm_atomic_implements_create_state(struct drm_device *dev);
+int drm_atomic_commit_fill_with_defaults(struct drm_file *file_priv,
+ struct drm_atomic_commit *commit);
/**
--
2.55.0
next prev parent reply other threads:[~2026-10-30 10:28 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-30 10:27 [PATCH v7 00/16] drm: Add DRM_MODE_ATOMIC_RESET flag Maxime Ripard
2026-10-30 10:27 ` [PATCH v7 01/16] drm/amd/display: Fix planes atomic_create_state implementation Maxime Ripard
2026-10-30 10:27 ` [PATCH v7 02/16] drm/solomon: ssd130x: Drop the init-only assumption in ssd130x_crtc_create_state() Maxime Ripard
2026-10-30 10:39 ` Javier Martinez Canillas
2026-10-30 10:27 ` [PATCH v7 03/16] drm/atomic: Switch to krealloc_array() in drm_atomic_get_private_obj_state() Maxime Ripard
2026-10-30 10:27 ` [PATCH v7 04/16] drm/atomic: Use __GFP_ZERO instead of explicit memset " Maxime Ripard
2026-10-30 10:27 ` [PATCH v7 05/16] drm/atomic: Use __GFP_ZERO instead of explicit memset in drm_atomic_get_connector_state() Maxime Ripard
2026-10-30 10:27 ` [PATCH v7 06/16] drm/atomic: Create function to insert CRTC state into a commit Maxime Ripard
2026-10-30 10:27 ` [PATCH v7 07/16] drm/atomic: Create function to insert plane " Maxime Ripard
2026-10-30 10:27 ` [PATCH v7 08/16] drm/atomic: Create function to insert colorop " Maxime Ripard
2026-10-30 10:27 ` [PATCH v7 09/16] drm/atomic: Create function to insert private obj " Maxime Ripard
2026-10-30 10:27 ` [PATCH v7 10/16] drm/atomic: Create function to insert connector " Maxime Ripard
2026-10-30 10:27 ` [PATCH v7 11/16] drm/atomic: Add drm_atomic_implements_create_state() helper Maxime Ripard
2026-10-30 10:27 ` Maxime Ripard [this message]
2026-10-30 10:53 ` [PATCH v7 12/16] drm/atomic: Allow filling a commit with pristine object states sashiko-bot
2026-10-30 10:27 ` [PATCH v7 13/16] drm/tests: atomic: Add tests for drm_atomic_commit_fill_with_defaults() Maxime Ripard
2026-10-30 10:39 ` sashiko-bot
2026-10-30 10:27 ` [PATCH v7 14/16] drm/atomic-uapi: Add DRM_MODE_ATOMIC_RESET flag Maxime Ripard
2026-10-30 10:54 ` sashiko-bot
2026-10-30 10:27 ` [PATCH v7 15/16] drm/vkms: Switch container_of helpers to container_of_const Maxime Ripard
2026-10-30 10:27 ` [PATCH v7 16/16] drm/vkms: Add driver-specific plane property for testing Maxime Ripard
2026-10-30 10:40 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261030-drm-reset-state-flag-v7-12-4e55eaeae683@kernel.org \
--to=mripard@kernel.org \
--cc=airlied@gmail.com \
--cc=contact@emersion.fr \
--cc=daniels@collabora.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=hamohammed.sa@gmail.com \
--cc=harry.wentland@amd.com \
--cc=jadahl@redhat.com \
--cc=jfalempe@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=louis.chauvet@bootlin.com \
--cc=maarten.lankhorst@linux.intel.com \
--cc=mdaenzer@redhat.com \
--cc=melissa.srw@gmail.com \
--cc=pekka.paalanen@collabora.com \
--cc=sebastian.wick@redhat.com \
--cc=simona@ffwll.ch \
--cc=tzimmermann@suse.de \
--cc=victoria@system76.com \
--cc=xaver.hugl@kde.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.