From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C12033E7150 for ; Thu, 23 Apr 2026 10:19:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776939565; cv=none; b=lrLo1XIFTYqT2RdRqBki6eMn/dvGv/QYjxjRgsNLUck4E13UWJAaCoV5YUy0HsnuMxDxVdmgvmq5upAJ1sdMUWP01HXqXOIRuqO1IIwh55g37CxSM/SFy3Zjj4XyxAuNgyrO/v+eWRcE+fs0IN0ISI2bxtDBWlNidnx+hU2enmE= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776939565; c=relaxed/simple; bh=eOkvTlUu8oWadBf0o3Ro+oBpJVl068gOAp95TK1P0qg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=N9f2KQYw/0tnEBXTUGCbBoYVJfCuhBx4r/WYFJ38trH7Cp4vkkbN3qEVJFvWXWUJAo+XBF92IkAnPIyT4ackicfb+fmemnVfwo7sSIULpCKOVXctfLo08g2f/NYO78uwMfcp7g6e7VQRa8tbcnuim17gC4rwZR9Sm90d4gdagIk= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=JYaMYhFN; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="JYaMYhFN" Received: by smtp.kernel.org (Postfix) with ESMTPSA id F3AABC2BCAF; Thu, 23 Apr 2026 10:19:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1776939565; bh=eOkvTlUu8oWadBf0o3Ro+oBpJVl068gOAp95TK1P0qg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=JYaMYhFNs7+me56b0Tc6l6ogNUNod/74cfWtLnWsq4dvLDOoAhndO6Dya1nsl7BnQ s19niVuNoZPlfLWg7Jhh8O7kAVVc/Ld9H4qgN5ZemPye8EeKR2DKsCS9PF/U7EUXtb O78C7W9tEdH80V1flrC7uGyBkKfBj/LfMXwPiIHk7Oox51b1vv20B3MQ0R0V0in8N5 HepDadTE75B4koONN9dItUnWmtJPpW8cGAdHw7DpKVlVXC4KESqyLHae7IUO+zTr4S XV2hKzOghmyBUX+zLGhNjYWnZojuwLzsWnlHCTvDcGbs7hBk6yrn/NR7RNnVlMqIll d4W0Fz71E6Mdg== From: Maxime Ripard Date: Thu, 23 Apr 2026 12:18:32 +0200 Subject: [PATCH v2 19/28] drm/atomic_helper: Compare actual and readout states once the commit is done Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20260423-drm-state-readout-v2-19-8549f87cb978@kernel.org> References: <20260423-drm-state-readout-v2-0-8549f87cb978@kernel.org> In-Reply-To: <20260423-drm-state-readout-v2-0-8549f87cb978@kernel.org> To: Maarten Lankhorst , Thomas Zimmermann , David Airlie , Simona Vetter , Andrzej Hajda , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Jyri Sarha , Tomi Valkeinen Cc: Devarsh Thakkar , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, Maxime Ripard X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=21546; i=mripard@kernel.org; h=from:subject:message-id; bh=eOkvTlUu8oWadBf0o3Ro+oBpJVl068gOAp95TK1P0qg=; b=owGbwMvMwCmsHn9OcpHtvjLG02pJDJkvP37I3OhpoL3oc6/Y612Sy8t9FrRtdHL3eDQ96NpyP 734Q8sYO6ayMAhzMsiKKbI8kQk7vbx9cZWD/cofMHNYmUCGMHBxCsBEbkkw1pf/0+bmqnSdU+m+ 81SmeWKV35/ws3krFtmlrWARqo/oy7165UnCSdlN07mXs+kZ1zQkMNaH8leG7dyjXMC8vGHe1cc dJ+au2dPPcd1/wZ7wf1MZrkoHmFWInxY+ti7YPT0+s8Rlhi0A X-Developer-Key: i=mripard@kernel.org; a=openpgp; fpr=BE5675C37E818C8B5764241C254BCFC56BF6CE8D 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 successful 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 blocking commit is made, and compare the readout one with the committed one. 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 --- drivers/gpu/drm/drm_atomic_helper.c | 40 +++++++ drivers/gpu/drm/drm_atomic_sro.c | 206 +++++++++++++++++++++++++++++++++++- drivers/gpu/drm/drm_bridge.c | 3 +- include/drm/drm_atomic.h | 23 ++++ include/drm/drm_atomic_sro.h | 4 + include/drm/drm_bridge.h | 25 +++++ include/drm/drm_connector.h | 25 +++++ include/drm/drm_crtc.h | 25 +++++ include/drm/drm_plane.h | 25 +++++ 9 files changed, 374 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 9deb3dad34a6..e46727069ad0 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -29,10 +29,11 @@ #include #include #include #include +#include #include #include #include #include #include @@ -2079,10 +2080,49 @@ 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); + /* + * drm_atomic_sro_readout_and_compare() will compare the @state new + * states with whatever it will read from the hardware. + * + * Since drm_atomic_helper_swap_state() has already been called, this + * means that new states have now been installed in their respective + * object state pointer, and their access must be protected by their + * respective locks. + * + * If we're doing a non-blocking commit however, the locks will be + * dropped as soon as we return from drm_atomic_nonblocking_commit() + * through drm_modeset_drop_locks(), while this function will be + * scheduled to execute later on. This means that by the time we get + * here, we probably have dropped the locks that protect the new states + * and it's not safe to access them anymore. + * + * Thus, we can only safely call drm_atomic_sro_readout_and_compare() if + * the commit is blocking, and we still hold all the locks. + * + * The other thing to consider is that it will take time to readout and + * compare all the states, and we don't want to delay fence signalling + * since some other part of the kernel or hardware might be waiting on + * us. Fortunately, the fence is signalled through + * &drm_crtc_commit.flip_done and drm_crtc_send_vblank_event() -> + * drm_send_event_helper() with drm_pending_event.completion being set + * to drm_crtc_commit.flip_done in drm_atomic_helper_setup_commit(). + * + * We later wait for flip_done in + * drm_atomic_helper_wait_for_flip_done(), which is typically part of + * drm_mode_config_helper_funcs.atomic_commit_tail, or the equivalent + * drm_atomic_helper_wait_for_vblank() which is part of + * drm_atomic_helper_commit_tail(). + * + * Either way, we're past that point here, so we wouldn't delay the + * fence signalling. + */ + if (!nonblock) + drm_atomic_sro_readout_and_compare(state); + drm_atomic_state_put(state); } static void commit_work(struct work_struct *work) { diff --git a/drivers/gpu/drm/drm_atomic_sro.c b/drivers/gpu/drm/drm_atomic_sro.c index 0e62e9d22ecc..64c93588d9fe 100644 --- a/drivers/gpu/drm/drm_atomic_sro.c +++ b/drivers/gpu/drm/drm_atomic_sro.c @@ -22,11 +22,11 @@ * firmware at boot, rather than programming a new state. This enables * flicker-free boot (also called "fastboot" by i915): if the * firmware already configured the display, the first userspace * modeset can be skipped when the requested mode matches. * - * The SRO lifecycle has two phases. The first phase is the readout + * The SRO lifecycle has three phases. The first phase is the readout * itself: at driver registration time, each KMS object (CRTCs, planes, * connectors, bridges, private objects) has its * atomic_sro_readout_state hook called to populate a * &struct drm_atomic_sro_state from hardware registers. * @@ -37,10 +37,19 @@ * atomic_sro_install_state hook on each object. This gives drivers a * chance to acquire the resources needed to keep the hardware state * active, such as power domains, clocks, or interrupts. This hook * cannot fail. * + * The third phase is the comparison. Because getting access to every + * firmware variation is hard, and most firmware setups will be fairly + * basic, the framework also provides a way to verify the readout + * implementation during normal operation. After every blocking commit, + * the framework reads out a fresh state from hardware and compares it + * to the committed state using the atomic_sro_compare_state hooks. Any + * mismatch is reported, which allows to catch readout bugs without + * needing specific firmware configurations. + * * Drivers integrate with SRO by implementing the readout and compare * hooks in their object funcs vtables and setting the * &drm_mode_config_funcs.atomic_sro_readout_state and * &drm_mode_config_helper_funcs.atomic_sro_build_state callbacks. The * default helpers drm_atomic_helper_sro_readout_state() and @@ -110,10 +119,60 @@ static bool drm_atomic_sro_can_readout(struct drm_device *dev) drm_connector_list_iter_end(&conn_iter); return true; } +static bool drm_atomic_sro_can_compare(struct drm_device *dev) +{ + struct drm_crtc *crtc; + struct drm_plane *plane; + struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; + struct drm_private_obj *privobj; + + if (atomic_readout == DRM_ATOMIC_READOUT_SKIP_MISSING_COMPARE || + atomic_readout == DRM_ATOMIC_READOUT_SKIP_MISSING_READOUT) + return true; + + drm_for_each_privobj(privobj, dev) { + if (!privobj->funcs->atomic_sro_compare_state) { + drm_dbg_atomic(dev, + "Private object %s missing compare callback", + privobj->name); + return false; + } + } + + drm_for_each_plane(plane, dev) { + if (!plane->funcs->atomic_sro_compare_state) { + drm_dbg_atomic(dev, "Plane %s missing compare callback", + plane->name); + return false; + } + } + + drm_for_each_crtc(crtc, dev) { + if (!crtc->funcs->atomic_sro_compare_state) { + drm_dbg_atomic(dev, "CRTC %s missing compare callback", + crtc->name); + return false; + } + } + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + if (!connector->funcs->atomic_sro_compare_state) { + drm_dbg_atomic(dev, "Connector %s missing compare callback", + connector->name); + return false; + } + } + drm_connector_list_iter_end(&conn_iter); + + return true; +} + /** * drm_atomic_sro_device_can_readout - check if a device supports hardware state readout * @dev: DRM device to check * * Verifies that the device is an atomic driver, that readout is @@ -136,10 +195,14 @@ bool drm_atomic_sro_device_can_readout(struct drm_device *dev) ret = drm_atomic_sro_can_readout(dev); if (!ret) return false; + ret = drm_atomic_sro_can_compare(dev); + if (!ret) + return false; + return true; } EXPORT_SYMBOL(drm_atomic_sro_device_can_readout); struct __drm_atomic_sro_plane { @@ -646,5 +709,146 @@ void drm_atomic_sro_install_state(struct drm_atomic_sro_state *state) state->private_objs[i].ptr = NULL; } state->num_private_objs = 0; } EXPORT_SYMBOL(drm_atomic_sro_install_state); + +/** + * drm_atomic_sro_readout_and_compare - verify committed state against hardware + * @committed_state: the atomic state that was just committed + * + * Reads out a fresh &struct drm_atomic_sro_state from the hardware and + * compares it to @committed_state using the atomic_sro_compare_state + * hooks. Any mismatches are reported through the DRM error printer. + * + * This is called after blocking commits to verify that the readout + * implementation is correct. + * + * RETURNS: + * + * True if the committed state and the hardware state are identical, + * false otherwise. + */ +bool drm_atomic_sro_readout_and_compare(struct drm_atomic_state *committed_state) +{ + struct drm_device *dev = committed_state->dev; + const struct drm_mode_config_helper_funcs *funcs = + dev->mode_config.helper_private; + 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_sro_state *readout_state; + unsigned int i; + bool identical = true; + + readout_state = funcs->atomic_sro_build_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_sro_get_plane_state(readout_state, plane); + if (!readout_plane_state) { + identical = false; + continue; + } + + if (!plane_funcs->atomic_sro_compare_state) + continue; + + if (!plane_funcs->atomic_sro_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_sro_get_crtc_state(readout_state, crtc); + if (!readout_crtc_state) { + identical = false; + continue; + } + + if (!crtc_funcs->atomic_sro_compare_state) + continue; + + if (!crtc_funcs->atomic_sro_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_sro_get_connector_state(readout_state, conn); + if (!readout_conn_state) { + identical = false; + continue; + } + + if (!conn_funcs->atomic_sro_compare_state) + continue; + + if (!conn_funcs->atomic_sro_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_sro_get_private_obj_state(readout_state, obj); + if (!readout_obj_state) { + identical = false; + continue; + } + + if (!obj_funcs->atomic_sro_compare_state) + continue; + + if (!obj_funcs->atomic_sro_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_sro_state_free(readout_state); + + return identical; +} +EXPORT_SYMBOL(drm_atomic_sro_readout_and_compare); diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 08226af6b82a..6621d80f5252 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -691,11 +691,12 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, goto err_reset_bridge; } if (drm_bridge_is_atomic(bridge)) { if (bridge->funcs && - bridge->funcs->atomic_sro_readout_state) + bridge->funcs->atomic_sro_readout_state && + bridge->funcs->atomic_sro_compare_state) drm_atomic_private_obj_init(bridge->dev, &bridge->base, bridge->name, &drm_bridge_priv_state_funcs_with_sro); else drm_atomic_private_obj_init(bridge->dev, &bridge->base, bridge->name, &drm_bridge_priv_state_funcs); diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 81290c4a5ad3..428233d572fc 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -336,10 +336,33 @@ struct drm_private_state_funcs { * hardware state readout initialization sequence. */ void (*atomic_sro_install_state)(struct drm_private_obj *obj, struct drm_private_state *obj_state); + /** + * @atomic_sro_compare_state: + * + * Compares two &struct drm_private_state instances and reports + * any mismatches through @p. + * + * It is called after blocking commits to verify that the + * committed state matches what can be read back from the + * hardware. Drivers subclassing the state should implement this + * to compare their driver-private fields. + * + * This hook is mandatory for drivers implementing SRO, but can + * be left unassigned otherwise. + * + * RETURNS: + * + * True if the states are identical, false otherwise. + */ + bool (*atomic_sro_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_atomic_sro.h b/include/drm/drm_atomic_sro.h index 195154850ab4..ff4e39f65e6b 100644 --- a/include/drm/drm_atomic_sro.h +++ b/include/drm/drm_atomic_sro.h @@ -1,11 +1,14 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef DRM_ATOMIC_SRO_H_ #define DRM_ATOMIC_SRO_H_ +#include + struct drm_atomic_sro_state; +struct drm_atomic_state; struct drm_connector; struct drm_connector_state; struct drm_crtc; struct drm_crtc_state; struct drm_device; @@ -49,7 +52,8 @@ drm_atomic_sro_get_private_obj_state(struct drm_atomic_sro_state *state, void drm_atomic_sro_set_private_obj_state(struct drm_atomic_sro_state *state, struct drm_private_obj *obj, struct drm_private_state *obj_state); void drm_atomic_sro_install_state(struct drm_atomic_sro_state *state); +bool drm_atomic_sro_readout_and_compare(struct drm_atomic_state *committed_state); #endif /* DRM_ATOMIC_SRO_H_ */ diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index e0f9b7e6353a..36d558a5cd4d 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -404,10 +404,35 @@ struct drm_bridge_funcs { struct drm_atomic_sro_state *state, struct drm_bridge_state *bridge_state, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state); + /** + * @atomic_sro_compare_state: + * + * Compares two &struct drm_bridge_state instances and reports + * any mismatches through @p. + * + * It is called after blocking commits to verify that the + * committed state matches what can be read back from the + * hardware. Drivers subclassing the state should implement this + * to compare their driver-private fields, calling + * drm_atomic_helper_bridge_compare_state() first for the base + * state fields. + * + * This hook is mandatory for drivers implementing SRO, but can + * be left unassigned otherwise. + * + * RETURNS: + * + * True if the states are identical, false otherwise. + */ + bool (*atomic_sro_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 3cd20198a5e7..6ab78c0eb80d 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1670,10 +1670,35 @@ 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_sro_compare_state: + * + * Compares two &struct drm_connector_state instances and + * reports any mismatches through @p. + * + * It is called after blocking commits to verify that the + * committed state matches what can be read back from the + * hardware. Drivers subclassing the state should implement this + * to compare their driver-private fields, calling + * drm_atomic_helper_connector_compare_state() first for the + * base state fields. + * + * This hook is mandatory for drivers implementing SRO, but can + * be left unassigned otherwise. + * + * RETURNS: + * + * True if the states are identical, false otherwise. + */ + bool (*atomic_sro_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 146da65448dc..77b7922bd288 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -737,10 +737,35 @@ 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_sro_compare_state: + * + * Compares two &struct drm_crtc_state instances and reports + * any mismatches through @p. + * + * It is called after blocking commits to verify that the + * committed state matches what can be read back from the + * hardware. Drivers subclassing the state should implement this + * to compare their driver-private fields, calling + * drm_atomic_helper_crtc_compare_state() first for the base + * state fields. + * + * This hook is mandatory for drivers implementing SRO, but can + * be left unassigned otherwise. + * + * RETURNS: + * + * True if the states are identical, false otherwise. + */ + bool (*atomic_sro_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 1e02728838e2..08d97d00e839 100644 --- a/include/drm/drm_plane.h +++ b/include/drm/drm_plane.h @@ -487,10 +487,35 @@ 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_sro_compare_state: + * + * Compares two &struct drm_plane_state instances and reports + * any mismatches through @p. + * + * It is called after blocking commits to verify that the + * committed state matches what can be read back from the + * hardware. Drivers subclassing the state should implement this + * to compare their driver-private fields, calling + * drm_atomic_helper_plane_compare_state() first for the base + * state fields. + * + * This hook is mandatory for drivers implementing SRO, but can + * be left unassigned otherwise. + * + * RETURNS: + * + * True if the states are identical, false otherwise. + */ + bool (*atomic_sro_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.53.0