[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <aPJC4CisqouIX-Wi@phenom.ffwll.local>
Date: Fri, 17 Oct 2025 15:21:36 +0200
From: Simona Vetter <simona.vetter@...ll.ch>
To: Maxime Ripard <mripard@...nel.org>
Cc: Maarten Lankhorst <maarten.lankhorst@...ux.intel.com>,
Thomas Zimmermann <tzimmermann@...e.de>,
David Airlie <airlied@...il.com>, Simona Vetter <simona@...ll.ch>,
Andrzej Hajda <andrzej.hajda@...el.com>,
Neil Armstrong <neil.armstrong@...aro.org>,
Robert Foss <rfoss@...nel.org>,
Laurent Pinchart <Laurent.pinchart@...asonboard.com>,
Jonas Karlman <jonas@...boo.se>,
Jernej Skrabec <jernej.skrabec@...il.com>,
Jyri Sarha <jyri.sarha@....fi>,
Tomi Valkeinen <tomi.valkeinen@...asonboard.com>,
Devarsh Thakkar <devarsht@...com>, dri-devel@...ts.freedesktop.org,
linux-kernel@...r.kernel.org
Subject: Re: [PATCH 13/29] drm/atomic_helper: Compare actual and readout
states once the commit is done
On Tue, Sep 02, 2025 at 10:32:41AM +0200, Maxime Ripard wrote:
> 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@...nel.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);
>
Both these callbacks for connector/crtc/plane need to be moved into the
respective _helper_funcs structs, because they're not uapi stuff.
priv/bridge funcs do not have this split, because they're not uapi visible
objects.
-Sima
> + /**
> + * @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
>
--
Simona Vetter
Software Engineer
http://blog.ffwll.ch
Powered by blists - more mailing lists