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>,
Andrzej Hajda <andrzej.hajda@intel.com>,
Neil Armstrong <neil.armstrong@linaro.org>,
Robert Foss <rfoss@kernel.org>,
Laurent Pinchart <Laurent.pinchart@ideasonboard.com>,
Jonas Karlman <jonas@kwiboo.se>,
Jernej Skrabec <jernej.skrabec@gmail.com>,
Jyri Sarha <jyri.sarha@iki.fi>,
Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Cc: Devarsh Thakkar <devarsht@ti.com>,
dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
Maxime Ripard <mripard@kernel.org>
Subject: [PATCH 13/29] drm/atomic_helper: Compare actual and readout states once the commit is done
Date: Tue, 02 Sep 2025 10:32:41 +0200 [thread overview]
Message-ID: <20250902-drm-state-readout-v1-13-14ad5315da3f@kernel.org> (raw)
In-Reply-To: <20250902-drm-state-readout-v1-0-14ad5315da3f@kernel.org>
The new atomic state readout infrastructure can be hard to test because
getting access to every firmware variation is hard, but also because
most firmware setup will be pretty basic and won't test a wide range of
features. Noticing whether it was sucessful or not is also not very
convenient.
In order to make it easier, we can however provide some infrastructure
to read out a new state every time a non-blocking commit is made, and
compare the readout one with the committed one. And since we do this
only on non-blocking commits, the time penalty doesn't matter.
To do so, we introduce a new hook for every state, atomic_compare_state,
that takes two state instances and is supposed to return whether they
are identical or not.
Signed-off-by: Maxime Ripard <mripard@kernel.org>
---
drivers/gpu/drm/drm_atomic_helper.c | 113 ++++++++++++++++++++++++++++++++++++
include/drm/drm_atomic.h | 14 +++++
include/drm/drm_bridge.h | 14 +++++
include/drm/drm_connector.h | 14 +++++
include/drm/drm_crtc.h | 14 +++++
include/drm/drm_plane.h | 14 +++++
6 files changed, 183 insertions(+)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 14d9bc282ca570964e494936090898b2dc6bee31..aa8f52b5d5a5e6146a6472eebaf02e675c35ccd2 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -428,10 +428,120 @@ void drm_atomic_helper_readout_state(struct drm_device *dev)
drm_atomic_helper_install_readout_state(state);
drm_atomic_state_put(state);
}
EXPORT_SYMBOL(drm_atomic_helper_readout_state);
+static bool drm_atomic_helper_readout_compare(struct drm_atomic_state *committed_state)
+{
+ struct drm_device *dev = committed_state->dev;
+ struct drm_printer p = drm_err_printer(dev, NULL);
+ struct drm_private_state *new_obj_state;
+ struct drm_private_obj *obj;
+ struct drm_plane_state *new_plane_state;
+ struct drm_plane *plane;
+ struct drm_crtc_state *new_crtc_state;
+ struct drm_crtc *crtc;
+ struct drm_connector_state *new_conn_state;
+ struct drm_connector *conn;
+ struct drm_atomic_state *readout_state;
+ unsigned int i;
+ bool identical = true;
+
+ readout_state = drm_atomic_build_readout_state(dev);
+ if (WARN_ON(IS_ERR(readout_state)))
+ return false;
+
+ for_each_new_plane_in_state(committed_state, plane, new_plane_state, i) {
+ const struct drm_plane_funcs *plane_funcs =
+ plane->funcs;
+ struct drm_plane_state *readout_plane_state;
+
+ readout_plane_state = drm_atomic_get_old_plane_state(readout_state, plane);
+ if (!readout_plane_state) {
+ identical = false;
+ continue;
+ }
+
+ if (!plane_funcs->atomic_compare_state)
+ continue;
+
+ if (!plane_funcs->atomic_compare_state(plane, &p, new_plane_state, readout_plane_state)) {
+ drm_warn(dev, "[PLANE:%d:%s] Committed and Readout PLANE state don't match\n",
+ plane->base.id, plane->name);
+ identical = false;
+ continue;
+ }
+ }
+
+ for_each_new_crtc_in_state(committed_state, crtc, new_crtc_state, i) {
+ const struct drm_crtc_funcs *crtc_funcs = crtc->funcs;
+ struct drm_crtc_state *readout_crtc_state;
+
+ readout_crtc_state = drm_atomic_get_old_crtc_state(readout_state, crtc);
+ if (!readout_crtc_state) {
+ identical = false;
+ continue;
+ }
+
+ if (!crtc_funcs->atomic_compare_state)
+ continue;
+
+ if (!crtc_funcs->atomic_compare_state(crtc, &p, new_crtc_state, readout_crtc_state)) {
+ drm_warn(dev, "[CRTC:%d:%s] Committed and Readout CRTC state don't match\n",
+ crtc->base.id, crtc->name);
+ identical = false;
+ continue;
+ }
+ }
+
+ for_each_new_connector_in_state(committed_state, conn, new_conn_state, i) {
+ const struct drm_connector_funcs *conn_funcs =
+ conn->funcs;
+ struct drm_connector_state *readout_conn_state;
+
+ readout_conn_state = drm_atomic_get_old_connector_state(readout_state, conn);
+ if (!readout_conn_state) {
+ identical = false;
+ continue;
+ }
+
+ if (!conn_funcs->atomic_compare_state)
+ continue;
+
+ if (!conn_funcs->atomic_compare_state(conn, &p, new_conn_state, readout_conn_state)) {
+ drm_warn(dev, "[CONNECTOR:%d:%s] Committed and Readout connector state don't match\n",
+ conn->base.id, conn->name);
+ identical = false;
+ continue;
+ }
+ }
+
+ for_each_new_private_obj_in_state(committed_state, obj, new_obj_state, i) {
+ const struct drm_private_state_funcs *obj_funcs = obj->funcs;
+ struct drm_private_state *readout_obj_state;
+
+ readout_obj_state = drm_atomic_get_old_private_obj_state(readout_state, obj);
+ if (!readout_obj_state) {
+ identical = false;
+ continue;
+ }
+
+ if (!obj_funcs->atomic_compare_state)
+ continue;
+
+ if (!obj_funcs->atomic_compare_state(obj, &p, new_obj_state, readout_obj_state)) {
+ drm_warn(dev, "Committed and Readout private object state don't match\n");
+ identical = false;
+ continue;
+ }
+ }
+
+ drm_atomic_state_put(readout_state);
+
+ return identical;
+}
+
/**
* DOC: overview
*
* This helper library provides implementations of check and commit functions on
* top of the CRTC modeset helper callbacks and the plane helper callbacks. It
@@ -2382,10 +2492,13 @@ static void commit_tail(struct drm_atomic_state *state, bool nonblock)
(unsigned long)commit_time_ms,
new_self_refresh_mask);
drm_atomic_helper_commit_cleanup_done(state);
+ if (!nonblock)
+ drm_atomic_helper_readout_compare(state);
+
drm_atomic_state_put(state);
}
static void commit_work(struct work_struct *work)
{
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index f13f926d21047e42bb9ac692c2dd4b88f2ebd91c..d75a9c7e23adf7fa264df766b47526f75e9cc753 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -226,10 +226,24 @@ struct drm_private_state_funcs {
* Frees the private object state created with @atomic_duplicate_state.
*/
void (*atomic_destroy_state)(struct drm_private_obj *obj,
struct drm_private_state *state);
+ /**
+ * @atomic_compare_state
+ *
+ * Compares two &struct drm_private_state instances.
+ *
+ * RETURNS:
+ *
+ * True if the states are identical, false otherwise.
+ */
+ bool (*atomic_compare_state)(struct drm_private_obj *obj,
+ struct drm_printer *p,
+ struct drm_private_state *a,
+ struct drm_private_state *b);
+
/**
* @atomic_print_state:
*
* If driver subclasses &struct drm_private_state, it should implement
* this optional hook for printing additional driver specific state.
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 15b63053f01869786831936ba28b7efc1e55e2e8..5ea63b51a4dd4cb00468afcf7d126c774f63ade0 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -511,10 +511,24 @@ struct drm_bridge_funcs {
int (*atomic_readout_state)(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state);
+ /**
+ * @atomic_compare_state
+ *
+ * Compares two &struct drm_bridge_state instances.
+ *
+ * RETURNS:
+ *
+ * True if the states are identical, false otherwise.
+ */
+ bool (*atomic_compare_state)(struct drm_bridge *bridge,
+ struct drm_printer *p,
+ struct drm_bridge_state *a,
+ struct drm_bridge_state *b);
+
/**
* @atomic_duplicate_state:
*
* Duplicate the current bridge state object (which is guaranteed to be
* non-NULL).
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index f68bd9627c085c6d2463b847aaa245ccc651f27b..dc2c77b04df9010cbfb2028de8ef8c747003c489 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1534,10 +1534,24 @@ struct drm_connector_funcs {
* This callback is mandatory for atomic drivers.
*/
void (*atomic_destroy_state)(struct drm_connector *connector,
struct drm_connector_state *state);
+ /**
+ * @atomic_compare_state
+ *
+ * Compares two &struct drm_connector_state instances.
+ *
+ * RETURNS:
+ *
+ * True if the states are identical, false otherwise.
+ */
+ bool (*atomic_compare_state)(struct drm_connector *connector,
+ struct drm_printer *p,
+ struct drm_connector_state *a,
+ struct drm_connector_state *b);
+
/**
* @atomic_set_property:
*
* Decode a driver-private property value and store the decoded value
* into the passed-in state structure. Since the atomic core decodes all
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 11e3299cfad1572c6e507918c7cceae7a28ba4cf..21c20ecdda40f3d155d3c140e06b3801270f5262 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -676,10 +676,24 @@ struct drm_crtc_funcs {
* This callback is mandatory for atomic drivers.
*/
void (*atomic_destroy_state)(struct drm_crtc *crtc,
struct drm_crtc_state *state);
+ /**
+ * @atomic_compare_state
+ *
+ * Compares two &struct drm_crtc_state instances.
+ *
+ * RETURNS:
+ *
+ * True if the states are identical, false otherwise.
+ */
+ bool (*atomic_compare_state)(struct drm_crtc *crtc,
+ struct drm_printer *p,
+ struct drm_crtc_state *a,
+ struct drm_crtc_state *b);
+
/**
* @atomic_set_property:
*
* Decode a driver-private property value and store the decoded value
* into the passed-in state structure. Since the atomic core decodes all
diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
index 691a267c857a228f674ef02a63fb6d1ff9e379a8..c24c10ccc8e8f2ba23e77e279aef61ae86e320c7 100644
--- a/include/drm/drm_plane.h
+++ b/include/drm/drm_plane.h
@@ -449,10 +449,24 @@ struct drm_plane_funcs {
* This callback is mandatory for atomic drivers.
*/
void (*atomic_destroy_state)(struct drm_plane *plane,
struct drm_plane_state *state);
+ /**
+ * @atomic_compare_state
+ *
+ * Compares two &struct drm_plane_state instances.
+ *
+ * RETURNS:
+ *
+ * True if the states are identical, false otherwise.
+ */
+ bool (*atomic_compare_state)(struct drm_plane *plane,
+ struct drm_printer *p,
+ struct drm_plane_state *a,
+ struct drm_plane_state *b);
+
/**
* @atomic_set_property:
*
* Decode a driver-private property value and store the decoded value
* into the passed-in state structure. Since the atomic core decodes all
--
2.50.1
next prev parent reply other threads:[~2025-09-02 8:33 UTC|newest]
Thread overview: 57+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-02 8:32 [PATCH 00/29] drm: Implement state readout support Maxime Ripard
2025-09-02 8:32 ` [PATCH 01/29] drm/atomic: Document atomic state lifetime Maxime Ripard
2025-09-02 13:08 ` Thomas Zimmermann
2025-09-02 18:59 ` Laurent Pinchart
2025-09-02 8:32 ` [PATCH 02/29] drm/atomic: Fix unused but set warning in for_each_old_plane_in_state Maxime Ripard
2025-09-02 13:10 ` Thomas Zimmermann
2025-09-02 19:25 ` Laurent Pinchart
2025-09-02 8:32 ` [PATCH 03/29] drm/atomic: Fix unused but set warning in for_each_old_private_obj_in_state Maxime Ripard
2025-09-02 13:10 ` Thomas Zimmermann
2025-09-02 19:26 ` Laurent Pinchart
2025-09-02 8:32 ` [PATCH 04/29] drm/atomic_helper: Skip over NULL private_obj pointers Maxime Ripard
2025-09-02 13:13 ` Thomas Zimmermann
2025-09-02 19:29 ` Laurent Pinchart
2025-09-02 8:32 ` [PATCH 05/29] drm/atomic_state_helper: Fix bridge state initialization Maxime Ripard
2025-09-02 13:18 ` Thomas Zimmermann
2025-09-02 19:49 ` Laurent Pinchart
2025-09-02 8:32 ` [PATCH 06/29] drm/bridge: Implement atomic_print_state Maxime Ripard
2025-09-02 13:22 ` Thomas Zimmermann
2025-09-02 20:22 ` Laurent Pinchart
2025-09-02 8:32 ` [PATCH 07/29] drm/atomic: Implement drm_atomic_print_old_state Maxime Ripard
2025-09-02 13:26 ` Thomas Zimmermann
2025-09-02 20:35 ` Laurent Pinchart
2025-09-02 8:32 ` [PATCH 08/29] drm/atomic: Only call atomic_destroy_state on a !NULL pointer Maxime Ripard
2025-09-02 13:30 ` Thomas Zimmermann
2025-09-02 20:52 ` Laurent Pinchart
2025-09-02 8:32 ` [PATCH 09/29] drm/modeset: Create atomic_reset hook Maxime Ripard
2025-09-02 21:04 ` Laurent Pinchart
2025-09-02 8:32 ` [PATCH 10/29] drm/atomic: Add atomic_state_readout infrastructure Maxime Ripard
2025-09-02 13:44 ` Thomas Zimmermann
2025-09-02 8:32 ` [PATCH 11/29] drm/crtc: Drop no_vblank bit field Maxime Ripard
2025-09-02 13:45 ` Thomas Zimmermann
2025-09-02 8:32 ` [PATCH 12/29] drm/atomic_helper: Pass nonblock to commit_tail Maxime Ripard
2025-09-02 13:46 ` Thomas Zimmermann
2025-09-02 8:32 ` Maxime Ripard [this message]
2025-09-02 8:32 ` [PATCH 14/29] drm/atomic_state_helper: Provide comparison macros Maxime Ripard
2025-09-02 8:32 ` [PATCH 15/29] drm/atomic_state_helper: Provide atomic_compare_state helpers Maxime Ripard
2025-09-02 8:32 ` [PATCH 16/29] drm/encoder: Create get_current_crtc hook Maxime Ripard
2025-09-02 8:32 ` [PATCH 17/29] drm/bridge_connector: Implement hw readout for connector Maxime Ripard
2025-09-02 8:32 ` [PATCH 18/29] drm/tidss: Convert to drm logging Maxime Ripard
2025-09-02 13:49 ` Thomas Zimmermann
2025-09-02 8:32 ` [PATCH 19/29] drm/tidss: Remove ftrace-like logs Maxime Ripard
2025-09-02 13:50 ` Thomas Zimmermann
2025-09-02 8:32 ` [PATCH 20/29] drm/tidss: crtc: Change variable name Maxime Ripard
2025-09-02 13:51 ` Thomas Zimmermann
2025-09-02 8:32 ` [PATCH 21/29] drm/tidss: crtc: Implement destroy_state Maxime Ripard
2025-09-02 13:52 ` Thomas Zimmermann
2025-09-02 8:32 ` [PATCH 22/29] drm/tidss: crtc: Cleanup reset implementation Maxime Ripard
2025-09-02 13:54 ` Thomas Zimmermann
2025-09-02 8:32 ` [PATCH 23/29] drm/tidss: dispc: Add format lookup by hw value Maxime Ripard
2025-09-02 8:32 ` [PATCH 24/29] drm/tidss: dispc: Improve mode checking logs Maxime Ripard
2025-09-02 14:06 ` Thomas Zimmermann
2025-09-02 8:32 ` [PATCH 25/29] drm/tidss: dispc: Move dispc_device definition to headers Maxime Ripard
2025-09-02 8:32 ` [PATCH 26/29] drm/tidss: dispc: make accessors accessible to other parts of the driver Maxime Ripard
2025-09-02 8:32 ` [PATCH 27/29] drm/tidss: Implement readout support Maxime Ripard
2025-09-02 8:32 ` [PATCH 28/29] drm/tidss: encoder: implement get_current_crtc Maxime Ripard
2025-09-02 8:32 ` [PATCH 29/29] drm/bridge: sii902x: Implement hw state readout Maxime Ripard
2025-09-02 14:13 ` [PATCH 00/29] drm: Implement state readout support Thomas Zimmermann
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=20250902-drm-state-readout-v1-13-14ad5315da3f@kernel.org \
--to=mripard@kernel.org \
--cc=Laurent.pinchart@ideasonboard.com \
--cc=airlied@gmail.com \
--cc=andrzej.hajda@intel.com \
--cc=devarsht@ti.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=jernej.skrabec@gmail.com \
--cc=jonas@kwiboo.se \
--cc=jyri.sarha@iki.fi \
--cc=linux-kernel@vger.kernel.org \
--cc=maarten.lankhorst@linux.intel.com \
--cc=neil.armstrong@linaro.org \
--cc=rfoss@kernel.org \
--cc=simona@ffwll.ch \
--cc=tomi.valkeinen@ideasonboard.com \
--cc=tzimmermann@suse.de \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).