Rust for Linux List
 help / color / mirror / Atom feed
* [RFC PATCH 0/5] rust: drm: minimal KMS bindings, EDID read, rotation, HDCP defs
@ 2026-06-17 15:02 Mike Lothian
  2026-06-17 15:02 ` [RFC PATCH 1/4] rust: drm: add minimal KMS bindings for simple-display-pipe drivers Mike Lothian
                   ` (5 more replies)
  0 siblings, 6 replies; 29+ messages in thread
From: Mike Lothian @ 2026-06-17 15:02 UTC (permalink / raw)
  To: dri-devel
  Cc: Mike Lothian, rust-for-linux, Danilo Krummrich, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel

This RFC series exposes enough of the C KMS API to Rust for a driver to
bring up an atomic mode-setting pipeline with the drm_simple_display_pipe
helper (one CRTC + primary plane + encoder) and install a real EDID read
off its own transport:

  1/4  KMS headers + Driver::FEAT_MODESET/FEAT_ATOMIC
  2/4  <drm/drm_edid.h>: drm_edid_alloc/_connector_update/_add_modes
  3/4  pub drm::Device::as_raw() escape hatch (separate, contentious bit)
  4/4  <drm/drm_blend.h> (atomic plane rotation property) + a rust_helper
       for drm_atomic_get_new_crtc_state() (inspect proposed CRTC state
       from a mode_config atomic_check)

It is deliberately an RFC and interim: it does NOT add safe Rust KMS
abstractions (CRTC/plane/connector wrappers - an active upstream effort),
only the raw binding surface plus the trait/visibility tweaks a driver
needs to call the C helpers itself.

FEAT_MODESET / FEAT_ATOMIC follow the existing FEAT_RENDER idiom exactly
(per-feature bool associated consts folded into compute_features()), so
GEM-only drivers are unaffected.

drm::Device::as_raw() is made pub as a documented temporary escape hatch
for calling the C KMS helpers until safe wrappers exist. That is the part
most likely to draw objections, so it is split into its own patch (3/3):
the feature flags and header bindings in 1/3 and 2/3 are useful on their
own and can land even if the escape hatch is reworked or dropped (e.g.
replaced by narrow per-helper safe wrappers).

drm_blend.h (4/4) exposes drm_plane_create_rotation_property() so a
driver can offer the standard atomic rotation property, and adds a
rust_helper for the static-inline drm_atomic_get_new_crtc_state() so a
driver can read a CRTC's proposed state during atomic_check() (the vino
driver uses it to reject a multi-head modeset that would exceed the USB
bandwidth). Like the rest of the series it is interim raw-binding surface.

drm_hdcp.h (5/5) exposes <drm/display/drm_hdcp.h> so a Rust HDCP driver
can use the canonical HDCP 2.2 message IDs, length constants and hdcp2_*
message structs instead of redefining them (the vino driver runs HDCP 2.2
over a vendor USB transport and reuses only the message definitions, not
the drm_hdcp_helper state machine). It only pulls in <linux/types.h>.

Note: a related but independent fix - using drm_dev_unplug() instead of
drm_dev_unregister() in Registration::drop, which closes a real
use-after-free for Rust DRM drivers on a hot-removable bus - is sent as a
standalone, non-RFC patch ("rust: drm: unplug the device on Registration
drop") rather than buried in this RFC, since it stands alone and should
not be gated on the KMS-surface discussion.

Factored out of an out-of-tree in-kernel Rust DisplayLink DL3 dock driver
that drives a virtual connector and reads the dock's downstream EDID; but
the bindings are generic (the same path simpledrm/gud/udl use).
Compile-tested in-tree against drm-next.

Mike Lothian (3):
  rust: drm: add minimal KMS bindings for simple-display-pipe drivers
  rust: drm: expose drm_edid.h for reading connector EDID
  rust: drm: expose drm::Device::as_raw()
  rust: drm: expose drm_blend.h and the atomic new-CRTC-state helper
  rust: drm: expose <drm/display/drm_hdcp.h> HDCP 2.2 message definitions

 rust/bindings/bindings_helper.h | 10 ++++++++++
 rust/kernel/drm/device.rs       | 18 +++++++++++++++++-
 rust/kernel/drm/driver.rs       | 17 +++++++++++++++++
 3 files changed, 44 insertions(+), 1 deletion(-)

--
2.54.0

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [RFC PATCH 1/4] rust: drm: add minimal KMS bindings for simple-display-pipe drivers
  2026-06-17 15:02 [RFC PATCH 0/5] rust: drm: minimal KMS bindings, EDID read, rotation, HDCP defs Mike Lothian
@ 2026-06-17 15:02 ` Mike Lothian
  2026-06-17 15:02 ` [RFC PATCH 2/4] rust: drm: expose drm_edid.h for reading connector EDID Mike Lothian
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-06-17 15:02 UTC (permalink / raw)
  To: dri-devel
  Cc: Mike Lothian, rust-for-linux, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, David Airlie, Simona Vetter,
	Daniel Almeida, Greg Kroah-Hartman, Alexandre Courbot, Asahi Lina,
	Lorenzo Stoakes, Joel Fernandes, linux-kernel

The Rust DRM abstractions currently cover GEM and the device/driver
registration boilerplate, but expose nothing of the KMS (mode-setting)
API, so a Rust driver cannot bring up a display pipeline without
dropping to C. Add the minimum needed to drive an atomic mode-setting
pipeline built on the drm_simple_display_pipe helper (one CRTC + a
primary plane + encoder), the same path simpledrm/gud/udl use.

Two pieces:

 - bindings_helper.h: pull in the KMS headers (drm_atomic_helper,
   drm_connector, drm_gem_atomic_helper, drm_gem_framebuffer_helper,
   drm_managed, drm_modes, drm_modeset_helper_vtables,
   drm_probe_helper, drm_simple_kms_helper) so bindgen emits the
   drm_simple_display_pipe, drm_connector, drm_display_mode and helper
   bindings a KMS driver calls (drm_simple_display_pipe_init,
   drm_connector_init, drm_helper_probe_single_connector_modes,
   drm_gem_fb_create, drm_cvt_mode and the drm_atomic_helper_* family).

 - drm::driver: add FEAT_MODESET / FEAT_ATOMIC and the matching
   Driver::FEAT_MODESET / Driver::FEAT_ATOMIC associated consts, folded
   into compute_features() exactly like the existing FEAT_RENDER. They
   default to false, so GEM-only drivers are unaffected.

No safe CRTC/plane/connector wrappers are added here; this is the raw
binding surface a driver needs to call the C helpers itself, and is
meant to be superseded by the in-progress safe KMS layer. The raw
drm_device pointer those helpers need is exposed by a separate follow-up
patch ("expose drm::Device::as_raw()") so the escape hatch can be
reviewed on its own.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/bindings/bindings_helper.h |  9 +++++++++
 rust/kernel/drm/device.rs       |  8 ++++++++
 rust/kernel/drm/driver.rs       | 17 +++++++++++++++++
 3 files changed, 34 insertions(+)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 14671e1825bb..877f82b927a0 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -32,13 +32,22 @@
 
 #include <linux/acpi.h>
 #include <linux/gpu_buddy.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_connector.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_file.h>
 #include <drm/drm_gem.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_gem_shmem_helper.h>
 #include <drm/drm_gpuvm.h>
 #include <drm/drm_ioctl.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
 #include <kunit/test.h>
 #include <linux/auxiliary_bus.h>
 #include <linux/bitmap.h>
diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs
index 477cf771fb10..1d3319b713e2 100644
--- a/rust/kernel/drm/device.rs
+++ b/rust/kernel/drm/device.rs
@@ -165,6 +165,14 @@ const fn compute_features() -> u32 {
             features |= drm::driver::FEAT_RENDER;
         }
 
+        if T::FEAT_MODESET {
+            features |= drm::driver::FEAT_MODESET;
+        }
+
+        if T::FEAT_ATOMIC {
+            features |= drm::driver::FEAT_ATOMIC;
+        }
+
         features
     }
 
diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs
index 25f7e233884d..6a79594510fd 100644
--- a/rust/kernel/drm/driver.rs
+++ b/rust/kernel/drm/driver.rs
@@ -22,6 +22,10 @@
 pub(crate) const FEAT_GEM: u32 = bindings::drm_driver_feature_DRIVER_GEM;
 /// Driver supports render nodes, i.e.: /dev/dri/renderDXX devices.
 pub(crate) const FEAT_RENDER: u32 = bindings::drm_driver_feature_DRIVER_RENDER;
+/// Driver supports mode setting (KMS).
+pub(crate) const FEAT_MODESET: u32 = bindings::drm_driver_feature_DRIVER_MODESET;
+/// Driver supports atomic mode setting.
+pub(crate) const FEAT_ATOMIC: u32 = bindings::drm_driver_feature_DRIVER_ATOMIC;
 
 /// Information data for a DRM Driver.
 pub struct DriverInfo {
@@ -131,6 +135,19 @@ pub trait Driver {
     /// usable from the render node (i.e. marked DRM_RENDER_ALLOW), whereas
     /// userspace processes using the master node can invoke any ioctl.
     const FEAT_RENDER: bool = false;
+
+    /// Sets the `DRIVER_MODESET` feature for this driver.
+    ///
+    /// When enabled, the driver advertises kernel mode-setting (KMS): it owns a
+    /// `drm_mode_config` and drives CRTCs, planes, encoders and connectors. Leave
+    /// `false` for a render-only (GEM) driver.
+    const FEAT_MODESET: bool = false;
+
+    /// Sets the `DRIVER_ATOMIC` feature for this driver.
+    ///
+    /// When enabled, the driver advertises the atomic mode-setting uAPI. It only
+    /// has an effect together with [`Driver::FEAT_MODESET`].
+    const FEAT_ATOMIC: bool = false;
 }
 
 /// The registration type of a `drm::Device`.
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH 2/4] rust: drm: expose drm_edid.h for reading connector EDID
  2026-06-17 15:02 [RFC PATCH 0/5] rust: drm: minimal KMS bindings, EDID read, rotation, HDCP defs Mike Lothian
  2026-06-17 15:02 ` [RFC PATCH 1/4] rust: drm: add minimal KMS bindings for simple-display-pipe drivers Mike Lothian
@ 2026-06-17 15:02 ` Mike Lothian
  2026-06-17 15:02 ` [RFC PATCH 3/4] rust: drm: expose drm::Device::as_raw() Mike Lothian
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-06-17 15:02 UTC (permalink / raw)
  To: dri-devel
  Cc: Mike Lothian, rust-for-linux, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Daniel Almeida,
	Greg Kroah-Hartman, Alexandre Courbot, Asahi Lina,
	Lorenzo Stoakes, Joel Fernandes, linux-kernel

Pull <drm/drm_edid.h> into bindings_helper.h so bindgen emits
drm_edid_alloc(), drm_edid_free(), drm_edid_connector_update(),
drm_edid_connector_add_modes() and drm_add_modes_noedid().

This lets a Rust driver read the real downstream EDID over its own
transport and install it through the DRM core (parse / validate / derive
the mode list) instead of synthesising a fixed mode — the same flow
gud and udl use. Raw bindings only; no abstraction.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/bindings/bindings_helper.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 877f82b927a0..8d45195df6e6 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -36,6 +36,7 @@
 #include <drm/drm_connector.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_file.h>
 #include <drm/drm_gem.h>
 #include <drm/drm_gem_atomic_helper.h>
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH 3/4] rust: drm: expose drm::Device::as_raw()
  2026-06-17 15:02 [RFC PATCH 0/5] rust: drm: minimal KMS bindings, EDID read, rotation, HDCP defs Mike Lothian
  2026-06-17 15:02 ` [RFC PATCH 1/4] rust: drm: add minimal KMS bindings for simple-display-pipe drivers Mike Lothian
  2026-06-17 15:02 ` [RFC PATCH 2/4] rust: drm: expose drm_edid.h for reading connector EDID Mike Lothian
@ 2026-06-17 15:02 ` Mike Lothian
  2026-06-17 15:02 ` [RFC PATCH 4/4] rust: drm: expose drm_blend.h and the atomic new-CRTC-state accessor Mike Lothian
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-06-17 15:02 UTC (permalink / raw)
  To: dri-devel
  Cc: Mike Lothian, rust-for-linux, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, David Airlie, Simona Vetter,
	Daniel Almeida, Greg Kroah-Hartman, Alexandre Courbot, Asahi Lina,
	Lorenzo Stoakes, Joel Fernandes, linux-kernel

A KMS driver built on the minimal bindings added earlier in this series
needs the raw `struct drm_device` pointer to hand to the C KMS helpers
(drm_simple_display_pipe_init, drm_connector_init, the drm_atomic_helper_*
family, …) until safe Rust KMS abstractions cover them.

Make drm::Device::as_raw() pub, documented as a temporary escape hatch
with the caller's locking/lifetime obligations spelled out. This is the
part of the KMS work most likely to draw objections, so it is split into
its own patch: it can be dropped or reworked (e.g. replaced by narrow safe
wrappers, or kept pub(crate) behind per-helper methods) without disturbing
the feature flags and header bindings, which are useful on their own.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/kernel/drm/device.rs | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs
index 1d3319b713e2..c89c947b399a 100644
--- a/rust/kernel/drm/device.rs
+++ b/rust/kernel/drm/device.rs
@@ -296,7 +296,15 @@ pub struct Device<T: drm::Driver, C: DeviceContext = Registered> {
 }
 
 impl<T: drm::Driver, C: DeviceContext> Device<T, C> {
-    pub(crate) fn as_raw(&self) -> *mut bindings::drm_device {
+    /// Returns a raw pointer to the underlying `struct drm_device`.
+    ///
+    /// This is a temporary escape hatch for drivers that need to call C KMS helpers
+    /// not yet covered by safe Rust abstractions; it is expected to be removed (or
+    /// narrowed to `pub(crate)`) once the in-progress safe KMS layer can express
+    /// those operations. The returned pointer is valid for as long as `self` is
+    /// borrowed; the caller must uphold the locking and lifetime rules of every C
+    /// function it is passed to.
+    pub fn as_raw(&self) -> *mut bindings::drm_device {
         self.dev.get()
     }
 
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH 4/4] rust: drm: expose drm_blend.h and the atomic new-CRTC-state accessor
  2026-06-17 15:02 [RFC PATCH 0/5] rust: drm: minimal KMS bindings, EDID read, rotation, HDCP defs Mike Lothian
                   ` (2 preceding siblings ...)
  2026-06-17 15:02 ` [RFC PATCH 3/4] rust: drm: expose drm::Device::as_raw() Mike Lothian
@ 2026-06-17 15:02 ` Mike Lothian
  2026-06-17 15:11 ` [RFC PATCH 0/5] rust: drm: minimal KMS bindings, EDID read, rotation, HDCP defs Miguel Ojeda
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
  5 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-06-17 15:02 UTC (permalink / raw)
  To: dri-devel
  Cc: Mike Lothian, rust-for-linux, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, David Airlie, Simona Vetter,
	Daniel Almeida, Greg Kroah-Hartman, Alexandre Courbot, Asahi Lina,
	Lorenzo Stoakes, Joel Fernandes, linux-kernel

Two further KMS binding prerequisites used by the in-tree DisplayLink
DL3 (vino) driver, beyond the simple-display-pipe header set:

- drm_blend.h, for drm_plane_create_rotation_property() and the
  DRM_MODE_ROTATE_* / DRM_MODE_REFLECT_* constants, so a driver can
  attach the standard atomic plane rotation property.

- a rust_helper for drm_atomic_get_new_crtc_state(): it is static
  inline in <drm/drm_atomic.h> and so is not callable from Rust
  directly. It returns a CRTC's new state within an atomic commit
  (NULL when the CRTC is not part of the commit), letting a driver
  inspect the proposed configuration from its mode_config
  atomic_check(). vino uses it to sum the pixel clocks of all heads
  and reject a modeset that would exceed the dock's USB bandwidth.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/bindings/bindings_helper.h |  1 +
 rust/helpers/drm.c              | 12 ++++++++++++
 2 files changed, 13 insertions(+)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 5b311944cc14..5ff21c33385e 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -31,6 +31,7 @@
 #include <linux/acpi.h>
 #include <linux/gpu_buddy.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_connector.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
diff --git a/rust/helpers/drm.c b/rust/helpers/drm.c
index 65f3f22b0e1d..5087daa23ed8 100644
--- a/rust/helpers/drm.c
+++ b/rust/helpers/drm.c
@@ -1,11 +1,23 @@
 // SPDX-License-Identifier: GPL-2.0
 
+#include <drm/drm_atomic.h>
 #include <drm/drm_gem.h>
 #include <drm/drm_gem_shmem_helper.h>
 #include <drm/drm_vma_manager.h>
 
 #ifdef CONFIG_DRM
 
+/* Read-only accessor for a CRTC's *new* state within an atomic commit (returns NULL if the CRTC
+ * is not part of the commit). It is static-inline in <drm/drm_atomic.h>, so Rust cannot call it
+ * directly; the vino driver uses it in its combined-bandwidth atomic_check.
+ */
+__rust_helper struct drm_crtc_state *
+rust_helper_drm_atomic_get_new_crtc_state(const struct drm_atomic_commit *state,
+					  struct drm_crtc *crtc)
+{
+	return drm_atomic_get_new_crtc_state(state, crtc);
+}
+
 __rust_helper void rust_helper_drm_gem_object_get(struct drm_gem_object *obj)
 {
 	drm_gem_object_get(obj);
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH 0/5] rust: drm: minimal KMS bindings, EDID read, rotation, HDCP defs
  2026-06-17 15:02 [RFC PATCH 0/5] rust: drm: minimal KMS bindings, EDID read, rotation, HDCP defs Mike Lothian
                   ` (3 preceding siblings ...)
  2026-06-17 15:02 ` [RFC PATCH 4/4] rust: drm: expose drm_blend.h and the atomic new-CRTC-state accessor Mike Lothian
@ 2026-06-17 15:11 ` Miguel Ojeda
  2026-06-17 15:29   ` Mike Lothian
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
  5 siblings, 1 reply; 29+ messages in thread
From: Miguel Ojeda @ 2026-06-17 15:11 UTC (permalink / raw)
  To: mike
  Cc: dri-devel, rust-for-linux, Danilo Krummrich, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel

Hi Mike,

A few questions on the cover letter that may give context to
readers... (for this and the other series you just sent)

On Wed, Jun 17, 2026 at 5:02 PM Mike Lothian <mike@fireburn.co.uk> wrote:
>
> It is deliberately an RFC and interim: it does NOT add safe Rust KMS
> abstractions (CRTC/plane/connector wrappers - an active upstream effort),
> only the raw binding surface plus the trait/visibility tweaks a driver
> needs to call the C helpers itself.

Do you mean this is not meant to go upstream?

Or do you actually want to land this interim state? If so, then it
needs to be well-justified why we would want to make this exception.

> drm::Device::as_raw() is made pub as a documented temporary escape hatch
> for calling the C KMS helpers until safe wrappers exist. That is the part
> most likely to draw objections, so it is split into its own patch (3/3):
> the feature flags and header bindings in 1/3 and 2/3 are useful on their
> own and can land even if the escape hatch is reworked or dropped (e.g.
> replaced by narrow per-helper safe wrappers).

Yeah... this sort of escape hatches aren't great. What is the reason
for not directly working on safe abstractions?

> Factored out of an out-of-tree in-kernel Rust DisplayLink DL3 dock driver
> that drives a virtual connector and reads the dock's downstream EDID; but
> the bindings are generic (the same path simpledrm/gud/udl use).
> Compile-tested in-tree against drm-next.

What is the use case? It is an out-of-tree driver? If so, the
maintainers may not be able to take the code unless there is an actual
user in-tree.

Or is this driver expected to land upstream? Do you have a link to the
source code?

Thanks!

Cheers,
Miguel

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH 0/5] rust: drm: minimal KMS bindings, EDID read, rotation, HDCP defs
  2026-06-17 15:11 ` [RFC PATCH 0/5] rust: drm: minimal KMS bindings, EDID read, rotation, HDCP defs Miguel Ojeda
@ 2026-06-17 15:29   ` Mike Lothian
  0 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-06-17 15:29 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: dri-devel, rust-for-linux, Danilo Krummrich, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel

On Wed, 17 Jun 2026 at 16:11, Miguel Ojeda
<miguel.ojeda.sandonis@gmail.com> wrote:
>
> Hi Mike,
>
> A few questions on the cover letter that may give context to
> readers... (for this and the other series you just sent)
>

Thanks for taking a look

> On Wed, Jun 17, 2026 at 5:02 PM Mike Lothian <mike@fireburn.co.uk> wrote:
> >
> > It is deliberately an RFC and interim: it does NOT add safe Rust KMS
> > abstractions (CRTC/plane/connector wrappers - an active upstream effort),
> > only the raw binding surface plus the trait/visibility tweaks a driver
> > needs to call the C helpers itself.
>
> Do you mean this is not meant to go upstream?
>
> Or do you actually want to land this interim state? If so, then it
> needs to be well-justified why we would want to make this exception.

I'm aiming for this to go upstream, I'm a bit stuck though so I've
posted what I've got so far

> > drm::Device::as_raw() is made pub as a documented temporary escape hatch
> > for calling the C KMS helpers until safe wrappers exist. That is the part
> > most likely to draw objections, so it is split into its own patch (3/3):
> > the feature flags and header bindings in 1/3 and 2/3 are useful on their
> > own and can land even if the escape hatch is reworked or dropped (e.g.
> > replaced by narrow per-helper safe wrappers).
>
> Yeah... this sort of escape hatches aren't great. What is the reason
> for not directly working on safe abstractions?
>

If you're happy to give guidance I'll work on safe abstractions

> > Factored out of an out-of-tree in-kernel Rust DisplayLink DL3 dock driver
> > that drives a virtual connector and reads the dock's downstream EDID; but
> > the bindings are generic (the same path simpledrm/gud/udl use).
> > Compile-tested in-tree against drm-next.
>
> What is the use case? It is an out-of-tree driver? If so, the
> maintainers may not be able to take the code unless there is an actual
> user in-tree.
>
> Or is this driver expected to land upstream? Do you have a link to the
> source code?

Its going to be a complete driver, but I've banging my head against a
wall with the bring up

The project info is at:

https://github.com/FireBurn/vino-scripts
https://github.com/FireBurn/linux/tree/vino
and also at
https://gitlab.freedesktop.org/FireBurn/linux/-/tree/vino

I've posted these RFCs so make sure I'm going in the right direction
and haven't missed something stupid that's stopping bring up

> Thanks!
>
> Cheers,
> Miguel

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings
  2026-06-17 15:02 [RFC PATCH 0/5] rust: drm: minimal KMS bindings, EDID read, rotation, HDCP defs Mike Lothian
                   ` (4 preceding siblings ...)
  2026-06-17 15:11 ` [RFC PATCH 0/5] rust: drm: minimal KMS bindings, EDID read, rotation, HDCP defs Miguel Ojeda
@ 2026-07-03  3:00 ` Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 01/18] rust: drm: kms: forward-port the safe mode-object layer onto the typestate device Mike Lothian
                     ` (17 more replies)
  5 siblings, 18 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:00 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

v1 of this series took the "raw C KMS + Device::as_raw() escape hatch"
route because Lyude Paul's safe Rust KMS mode-object layer wasn't
landable yet. Both Danilo and Miguel pushed back on that in review: work
on the safe abstractions instead, coordinate with Lyude. This v2 does
that -- and in the meantime upstream itself moved: drm::Driver's base
trait now requires a Kms associated type routed through exactly that safe
layer, so the v1 approach doesn't compile against current drm-next at all
any more. Posting the real port is the only way to keep a KMS driver
building.

  1/18 rust: drm: kms: forward-port the safe mode-object layer
  2/18 rust: drm: kms: adapt the port to current drm-next
  3/18 rust: drm: kms: break the Driver* trait well-formedness cycle
  4/18 rust: drm: kms: build the kernel crate clean under -Znext-solver
  5/18 rust: drm: expose <drm/display/drm_hdcp.h> HDCP 2.2 messages
  6/18 rust: drm: kms: add a Framebuffer::vmap() guard
  7/18 rust: drm: kms: add safe accessors for common state and modes
  8/18 rust: drm: tyr: add the Kms associated type
  9/18 rust: drm: add drm_event delivery
 10/18 rust: drm: allow drivers to declare ioctls from their own uAPI module
 11/18 rust: platform: add runtime platform device creation
 12/18 rust: drm: framebuffer: geometry accessors, refcounting, byte-slice vmap
 13/18 rust: i2c: add adapter-provider (bus controller) registration
 14/18 rust: add sysfs device attribute groups
 15/18 rust: drm: support hardware cursor planes (sleepable event delivery)
 16/18 rust: drm: add CRTC gamma LUT and plane rotation property
 17/18 rust: drm: kms: add connector detect() and mode_valid() hooks
 18/18 rust: drm: kms: add plane damage-clip accessors

Patches 1-4 are a from-scratch forward-port of Lyude's rvkms-slim safe
KMS mode-object layer (CRTC/plane/connector/encoder/vblank/atomic state)
onto her newer rust-stuck typestate Device design, which isn't in mainline
yet either -- see the companion "rust: sync/drm: locking and
DRM-registration prerequisites" postings this depends on. It's a
mechanical type-level port with one real problem: the ported mode-object
traits are mutually recursive in a way the (then-current) trait solver
can't evaluate without unbounded memory. Patch 3 pins the associated-state
types to break the infinite projection regress; that trades it for a
coinductive cycle the old solver still can't resolve, which patch 4 fixes
by scoping -Znext-solver to the kernel crate only (core/alloc stay on the
old solver, since core.o ICEs under -Znext-solver on this toolchain).
rust/kernel.o then builds clean, safe KMS layer included.

Patches 5-7 are what vino needed once it started using the ported layer:
patch 5 re-adds the HDCP 2.2 message-ID header v1 carried; patch 6 adds a
Framebuffer::vmap() RAII guard; patch 7 adds safe accessors for the handful
of raw drm_kms fields and helpers a real driver reaches for
(drm::Device::hotplug_event(), CRTC-state mode(), plane-state
crtc_w()/crtc_h(), DisplayMode geometry getters, connector EDID modes) so
vino needs no raw escape hatch at all. v1's Device::as_raw() escape hatch
is dropped entirely.

Patch 8 is base drift: tyr predates the Kms associated type becoming
required and needed PhantomData<Self> added, same as nova already had.

Patches 9-18 are new in v2: generic bindings a *second* DRM driver needs
beyond the safe KMS layer. Alongside vino we are rewriting DisplayLink's
out-of-tree evdi module in Rust (it presents virtual displays whose
scanout is pulled by a userspace daemon over a drm_event + ioctl ABI), and
patches 9-15 are the seven things it needs that the layer did not provide,
none of them evdi-specific:

  9/18  drm::event -- a safe EventChannel/EventPayload for delivering
        drm_events to a userspace client, with the receiver drm_file only
        ever touched under the device event_lock so a send can't race a
        file close (the classic NULL/UAF flush bug). event_lock() moves to
        the core drm::Device since events are DRM-core, not KMS.
 10/18  declare_drm_ioctls_ext! -- declare_drm_ioctls! resolves argument
        types and numbers from the in-tree kernel::uapi crate, which an
        out-of-tree driver with its own ioctl range can't extend; the _ext
        variant takes the type and number per entry instead. The original
        macro is untouched.
 11/18  platform::RegisteredDevice -- the provider side of the platform
        bus (create/register a platform_device at runtime), so a virtual
        driver can spawn its own devices; platform.rs previously only bound
        to existing ones.
 12/18  Framebuffer geometry accessors, reference counting (ARef via
        drm_framebuffer_get/put helpers) and FramebufferVmap::as_bytes(),
        so a driver can hold a flipped-in framebuffer and read its pixels
        back without unsafe -- what evdi's GRABPIX does.
 13/18  i2c::BusController/BusAdapter -- the controller (provider) side of
        the I2C bus, so a driver can present a virtual bus and service the
        transfers; i2c.rs previously only registered I2C client drivers.
        Both evdi and vino use it for their DDC/CI channel.
 14/18  sysfs::DeviceAttributes/AttributeGroup -- expose sysfs control files
        on a root device with name-dispatched show/store; evdi uses it for
        its /sys/devices/evdi/{count,add,remove_all} card-lifecycle files.
 15/18  hardware cursor support -- rework drm::event to guard the receiver
        with a mutex so a GEM handle for the cursor bitmap can be created
        for the client (sleepable), plus plane position/hotspot and
        framebuffer format/handle accessors; both evdi and vino advertise a
        cursor plane.

Patches 16-18 round out the KMS feature set with more generic mode-object
support the pre-safe-KMS vino driver had that the ported layer didn't:
patch 16 a CRTC GAMMA_LUT (drm_crtc_enable_color_mgmt + a gamma_lut()
state accessor) and a plane rotation property
(drm_plane_create_rotation_property + rotation()/placement accessors);
patch 17 optional connector detect()/mode_valid() hooks (report hotplug
state, prune modes the driver can't drive), gated in the vtable like the
plane/CRTC optionals; patch 18 the RawPlaneState::damage_merged() and
for_each_damage_clip() accessors over drm_atomic_helper_damage_merged() /
drm_atomic_helper_damage_iter() so a driver can repaint only the region(s)
a client marked dirty. All three are generic KMS, not vino-specific.

vino uses patches 1-8 and 16-18; 9-15 are included here (rather than a
separate series) because they are small DRM/platform/i2c/sysfs binding
additions of the same kind and share this series' base. The evdi driver is
posted as a separate companion series ("[RFC PATCH] drm/evdi: a Rust EVDI
virtual display") -- a second, working consumer of these same bindings.

Known gaps, not fixed here: the connector layer has no detect_ctx()/
writeback support; no per-plane alpha/blend-mode property yet.

The primary consumer is drm/vino, posted alongside ("[RFC PATCH v2 00/9]
drm/vino: DisplayLink DL3 dock driver"). Source:
  https://github.com/FireBurn/vino-scripts
  https://github.com/FireBurn/linux/tree/vino
  https://gitlab.freedesktop.org/FireBurn/linux/-/tree/vino

Changes since v1:
 - Dropped the raw C KMS approach (FEAT_MODESET/FEAT_ATOMIC, drm_edid.h,
   drm_blend.h, Device::as_raw()); replaced by the safe KMS layer
   (patches 1-4), Framebuffer::vmap() (6) and safe accessors (7).
 - Kept the HDCP header (patch 5).
 - Added the evdi/vino bindings (patches 9-18): drm_event delivery,
   out-of-uAPI ioctls, platform-device creation, framebuffer accessors,
   i2c adapter-provider, sysfs attributes, hardware cursor, CRTC gamma /
   plane rotation, connector detect()/mode_valid(), and plane damage-clip
   accessors.

Mike Lothian (18):
  rust: drm: kms: forward-port the safe mode-object layer onto the
    typestate device
  rust: drm: kms: adapt the port to current drm-next
  rust: drm: kms: break the Driver* trait well-formedness cycle
  rust: drm: kms: build the kernel crate clean under -Znext-solver
  rust: drm: expose <drm/display/drm_hdcp.h> HDCP 2.2 message
    definitions
  rust: drm: kms: add a Framebuffer::vmap() guard
  rust: drm: kms: add safe accessors for common state and connector
    modes
  rust: drm: tyr: add the Kms associated type
  rust: drm: add drm_event delivery
  rust: drm: allow drivers to declare ioctls from their own uAPI module
  rust: platform: add runtime platform device creation
  rust: drm: framebuffer: add geometry accessors, refcounting and a
    byte-slice vmap
  rust: i2c: add adapter-provider (bus controller) registration
  rust: add sysfs device attribute groups
  rust: drm: support hardware cursor planes with sleepable event
    delivery
  rust: drm: add CRTC gamma LUT and plane rotation property bindings
  rust: drm: kms: add connector detect() and mode_valid() hooks
  rust: drm: kms: add plane damage-clip accessors

 drivers/gpu/drm/tyr/driver.rs      |    1 +
 rust/Makefile                      |    6 +-
 rust/bindings/bindings_helper.h    |   11 +
 rust/helpers/drm/atomic.c          |   32 +
 rust/helpers/drm/drm.c             |    3 +
 rust/helpers/drm/framebuffer.c     |   13 +
 rust/helpers/drm/vblank.c          |    8 +
 rust/kernel/drm/device.rs          |   31 +-
 rust/kernel/drm/driver.rs          |    8 +-
 rust/kernel/drm/event.rs           |  216 +++++
 rust/kernel/drm/ioctl.rs           |   86 ++
 rust/kernel/drm/kms.rs             |  432 +++++++++-
 rust/kernel/drm/kms/atomic.rs      |  865 +++++++++++++++++++
 rust/kernel/drm/kms/connector.rs   | 1109 +++++++++++++++++++++++++
 rust/kernel/drm/kms/crtc.rs        | 1152 ++++++++++++++++++++++++++
 rust/kernel/drm/kms/encoder.rs     |  409 +++++++++
 rust/kernel/drm/kms/framebuffer.rs |  217 +++++
 rust/kernel/drm/kms/modes.rs       |  151 ++++
 rust/kernel/drm/kms/plane.rs       | 1230 ++++++++++++++++++++++++++++
 rust/kernel/drm/kms/vblank.rs      |  461 +++++++++++
 rust/kernel/drm/mod.rs             |    1 +
 rust/kernel/i2c.rs                 |  192 +++++
 rust/kernel/lib.rs                 |    1 +
 rust/kernel/platform.rs            |   77 ++
 rust/kernel/sysfs.rs               |  245 ++++++
 25 files changed, 6930 insertions(+), 27 deletions(-)
 create mode 100644 rust/helpers/drm/atomic.c
 create mode 100644 rust/helpers/drm/framebuffer.c
 create mode 100644 rust/helpers/drm/vblank.c
 create mode 100644 rust/kernel/drm/event.rs
 create mode 100644 rust/kernel/drm/kms/atomic.rs
 create mode 100644 rust/kernel/drm/kms/connector.rs
 create mode 100644 rust/kernel/drm/kms/crtc.rs
 create mode 100644 rust/kernel/drm/kms/encoder.rs
 create mode 100644 rust/kernel/drm/kms/framebuffer.rs
 create mode 100644 rust/kernel/drm/kms/modes.rs
 create mode 100644 rust/kernel/drm/kms/plane.rs
 create mode 100644 rust/kernel/drm/kms/vblank.rs
 create mode 100644 rust/kernel/sysfs.rs

-- 
2.55.0


^ permalink raw reply	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 01/18] rust: drm: kms: forward-port the safe mode-object layer onto the typestate device
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
@ 2026-07-03  3:00   ` Mike Lothian
  2026-07-07 21:46     ` lyude
  2026-07-07 22:21     ` lyude
  2026-07-03  3:00   ` [RFC PATCH v2 02/18] rust: drm: kms: adapt the port to current drm-next Mike Lothian
                     ` (16 subsequent siblings)
  17 siblings, 2 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:00 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

Port Lyude Paul's rvkms-slim safe mode-object layer (CRTC/plane/connector/
encoder/vblank/atomic state) onto her newer rust-stuck typestate Device
design (ParentDevice/DeviceContext/RegistrationData), which the preceding
cherry-picked commits bring in. Source: lyude/rvkms-slim + lyude/rust/rust-stuck.

Same module split as rvkms-slim (kms/{atomic,connector,crtc,encoder,
framebuffer,modes,plane,vblank}.rs), same trait shape, adjusted to the
typestate device's generic Ctx/State parameters. This is a mechanical
type-level port, not new design -- diff against lyude/rvkms-slim to see
exactly what moved.

Does not build yet. The port's mode-object traits are mutually
recursive in a way the current trait solver can't evaluate; the next
few commits get it to a clean build.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-sonnet-5 [Claude-Code]
---
 rust/kernel/drm/kms.rs             |  395 +++++++++-
 rust/kernel/drm/kms/atomic.rs      |  864 ++++++++++++++++++++++
 rust/kernel/drm/kms/connector.rs   |  997 +++++++++++++++++++++++++
 rust/kernel/drm/kms/crtc.rs        | 1110 ++++++++++++++++++++++++++++
 rust/kernel/drm/kms/encoder.rs     |  409 ++++++++++
 rust/kernel/drm/kms/framebuffer.rs |   70 ++
 rust/kernel/drm/kms/modes.rs       |   76 ++
 rust/kernel/drm/kms/plane.rs       | 1095 +++++++++++++++++++++++++++
 rust/kernel/drm/kms/vblank.rs      |  461 ++++++++++++
 9 files changed, 5469 insertions(+), 8 deletions(-)
 create mode 100644 rust/kernel/drm/kms/atomic.rs
 create mode 100644 rust/kernel/drm/kms/connector.rs
 create mode 100644 rust/kernel/drm/kms/crtc.rs
 create mode 100644 rust/kernel/drm/kms/encoder.rs
 create mode 100644 rust/kernel/drm/kms/framebuffer.rs
 create mode 100644 rust/kernel/drm/kms/modes.rs
 create mode 100644 rust/kernel/drm/kms/plane.rs
 create mode 100644 rust/kernel/drm/kms/vblank.rs

diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs
index 084ed0aebd0e..11b09d2175db 100644
--- a/rust/kernel/drm/kms.rs
+++ b/rust/kernel/drm/kms.rs
@@ -3,17 +3,37 @@
 //! KMS driver abstractions for rust.
 
 use crate::{
+    container_of,
     drm::{
-        device::Device,
+        device::{Device, DeviceContext},
         driver::Driver,
         private::Sealed,
         Uninit, //
     },
     error::to_result,
-    prelude::*, //
+    prelude::*,
+    sync::{Mutex, MutexGuard},
+    types::*,
 };
 use bindings;
-use core::{marker::PhantomData, ops::Deref};
+use core::{
+    cell::Cell,
+    marker::PhantomData,
+    ops::Deref,
+    ptr::{self, addr_of_mut, NonNull},
+};
+
+// Forward-ported mode-object layer (see Documentation/gpu/rust/vino-kms-forward-port.md).
+// Modules are declared here as each is grafted onto the rust-stuck typestate design.
+// `modes` is self-contained and ported as-is; the rest follow per the plan's order.
+pub mod atomic;
+pub mod connector;
+pub mod crtc;
+pub mod encoder;
+pub mod framebuffer;
+pub mod modes;
+pub mod plane;
+pub mod vblank;
 
 /// The C vtable for a [`Device`].
 ///
@@ -77,13 +97,21 @@ impl KmsContext for Probing {}
 /// This type is guaranteed to represent a [`Device`] that has not been registered with userspace,
 /// and is in the process of setting up KMS support. It carries a [`KmsDeviceContext`] to indicate
 /// which stage of the KMS setup process this [`Device`] is currently in.
-pub struct NewKmsDevice<'a, T: Driver, C: KmsContext>(&'a Device<T, Uninit>, PhantomData<C>);
+pub struct NewKmsDevice<'a, T: Driver, C: KmsContext> {
+    drm: &'a Device<T, Uninit>,
+    /// Tracks whether any CRTC created during probe requested vblank support, so that
+    /// [`drm_vblank_init()`] can be called afterwards. Set by [`crtc::Crtc::new()`].
+    ///
+    /// [`drm_vblank_init()`]: srctree/include/drm/drm_vblank.h
+    pub(crate) has_vblanks: Cell<bool>,
+    _ctx: PhantomData<C>,
+}
 
 impl<'a, T: Driver, C: KmsContext> Deref for NewKmsDevice<'a, T, C> {
     type Target = Device<T, Uninit>;
 
     fn deref(&self) -> &Self::Target {
-        self.0
+        self.drm
     }
 }
 
@@ -95,15 +123,56 @@ fn deref(&self) -> &Self::Target {
 /// [`PhantomData<Self>`]: PhantomData
 #[vtable]
 pub trait KmsDriver: Driver {
+    /// The driver's [`DriverConnector`](connector::DriverConnector) implementation.
+    ///
+    /// TODO: This will be unneeded once we support multiple `DriverConnector` implementations.
+    type Connector: connector::DriverConnector;
+
+    /// The driver's [`DriverPlane`](plane::DriverPlane) implementation.
+    ///
+    /// TODO: This will be unneeded once we support multiple `DriverPlane` implementations.
+    type Plane: plane::DriverPlane;
+
+    /// The driver's [`DriverCrtc`](crtc::DriverCrtc) implementation.
+    ///
+    /// TODO: This will be unneeded once we support multiple `DriverCrtc` implementations.
+    type Crtc: crtc::DriverCrtc;
+
+    /// The driver's [`DriverEncoder`](encoder::DriverEncoder) implementation.
+    ///
+    /// TODO: This will be unneeded once we support multiple `DriverEncoder` implementations.
+    type Encoder: encoder::DriverEncoder;
+
     /// Return a [`ModeConfigInfo`] structure for this [`device::Device`].
     fn mode_config_info(dev: &Device<Self, Uninit>) -> Result<ModeConfigInfo>
     where
         Self: Sized;
 
     /// Create mode objects like [`crtc::Crtc`], [`plane::Plane`], etc. for this device
-    fn probe(drm: NewKmsDevice<'_, Self, Probing>) -> Result
+    fn probe(drm: &NewKmsDevice<'_, Self, Probing>) -> Result
     where
         Self: Sized;
+
+    /// The optional [`atomic_commit_tail`] callback for this [`Device`].
+    ///
+    /// It must return a [`CommittedAtomicState`](atomic::CommittedAtomicState) to prove that it has
+    /// signaled completion of the hw commit phase. Drivers may use this function to customize the
+    /// order in which commits are performed during the atomic commit phase.
+    ///
+    /// If not provided, DRM will use its own default atomic commit tail helper
+    /// `drm_atomic_helper_commit_tail`.
+    ///
+    /// [`atomic_commit_tail`]: srctree/include/drm/drm_modeset_helper_vtables.h
+    fn atomic_commit_tail<'a>(
+        _state: atomic::AtomicCommitTail<'a, Self>,
+        _modeset_token: atomic::ModesetsReadyToken<'_>,
+        _plane_update_token: atomic::PlaneUpdatesReadyToken<'_>,
+    ) -> atomic::CommittedAtomicState<'a, Self>
+    where
+        Self: Sized,
+    {
+        build_error::build_error("This function should not be reachable")
+    }
 }
 
 impl<T: KmsDriver> private::KmsImpl for T {
@@ -123,7 +192,11 @@ impl<T: KmsDriver> private::KmsImpl for T {
 
         kms_helper_vtable: bindings::drm_mode_config_helper_funcs {
             atomic_commit_setup: None,
-            atomic_commit_tail: None,
+            atomic_commit_tail: if T::HAS_ATOMIC_COMMIT_TAIL {
+                Some(atomic::commit_tail_callback::<T>)
+            } else {
+                None
+            },
         },
     });
 
@@ -156,7 +229,19 @@ unsafe fn probe_kms(drm: &Device<Self::Driver, Uninit>) -> Result<ModeConfigInfo
         // SAFETY: We just setup all of the info required to call this function above.
         to_result(unsafe { bindings::drmm_mode_config_init(drm.as_raw()) })?;
 
-        T::probe(NewKmsDevice(&drm, PhantomData))?;
+        let new_kms = NewKmsDevice {
+            drm: &drm,
+            has_vblanks: Cell::new(false),
+            _ctx: PhantomData,
+        };
+        T::probe(&new_kms)?;
+
+        if new_kms.has_vblanks.get() {
+            // SAFETY: `has_vblanks` is only set when CRTCs with vblank support were created during
+            // probe (the only place static mode objects are created), so the vblank state is ready
+            // to be initialized.
+            to_result(unsafe { bindings::drm_vblank_init(drm.as_raw(), drm.num_crtcs()) })?;
+        }
 
         // TODO: In the future, we should add a hook here for initializing each state via hardware
         // state readback.
@@ -192,3 +277,297 @@ pub struct ModeConfigInfo {
     /// An optional default fourcc format code to be preferred for clients.
     pub preferred_fourcc: Option<u32>,
 }
+
+// ---------------------------------------------------------------------------
+// Mode-object framework (forward-ported from lyude/rvkms-slim's kms.rs).
+//
+// Adapted to rust-stuck's typestate device: `Device<T, C>` defaults `C` to
+// `Registered`, so most `Device<T>` references port unchanged; only creation
+// paths (during probe) use the `Uninit` context, reached via `NewKmsDevice`.
+// ---------------------------------------------------------------------------
+
+/// Implement the repetitive from_opaque/try_from_opaque methods for all mode object and state
+/// types.
+///
+/// Because there are so many different ways of accessing mode objects, their states, etc. we need a
+/// macro that we can use for consistently implementing try_from_opaque()/from_opaque() functions to
+/// convert from Opaque mode objects to fully typed mode objects. This macro handles that, and can
+/// generate said functions for any kind of type which the original mode object driver trait can be
+/// derived from.
+macro_rules! impl_from_opaque_mode_obj {
+    (
+        fn <
+            $( $lifetime:lifetime, )?
+            $( $decl_bound_id:ident ),*
+        > ($opaque:ty) -> $inner_ret_ty:ty
+        $(
+            where
+                $( $extra_bound_id:ident : $extra_trait:ident<$( $extra_assoc:ident = $extra_param_match:ident ),+> ),+
+        )? ;
+        use
+            $obj_trait_param:ident as $obj_trait:ident,
+            $drv_trait_param:ident as KmsDriver<$drv_assoc_trait:ident = ...>
+    ) => {
+        #[doc = "Try to convert `opaque` into a fully qualified `Self`."]
+        #[doc = ""]
+        #[doc = concat!("This will try to convert `opaque` into `Self` if it shares the same [`",
+                        stringify!($obj_trait), "`] implementation as `Self`.")]
+        pub fn try_from_opaque<$( $lifetime, )? $( $decl_bound_id ),* >(
+            opaque: $opaque
+        ) -> Result<$inner_ret_ty, $opaque>
+        where
+            $drv_trait_param: KmsDriver<$drv_assoc_trait = $obj_trait_param>,
+            $obj_trait_param: $obj_trait<Driver = $drv_trait_param>
+            $( , $( $extra_bound_id: $extra_trait<$( $extra_assoc = $extra_param_match ),+> ),+ )?
+        {
+            // FIXME: What we really want to be doing here is comparing vtable pointers, but this is
+            // currently blocked on getting unique vtable macros to ensure that each vtable has a
+            // consistent memory pointer.
+            // For the time being, we simply restrict things to one object type per driver and do a
+            // transmutation based on that assumption holding true.
+            // SAFETY: We currently only allow one object type per-driver, so this transmute is
+            // always safe.
+            Ok(unsafe { core::mem::transmute(opaque) })
+        }
+
+        #[doc = "Convert `opaque` into a fully qualified `Self`."]
+        #[doc = ""]
+        #[doc = concat!("This is an infallible version of [`Self::try_from_opaque`]. This ",
+                        "function is mainly useful for drivers where only a single [`",
+                        stringify!($obj_trait), "`] implementation exists.")]
+        #[doc = ""]
+        #[doc = "# Panics"]
+        #[doc = ""]
+        #[doc = concat!("This function will panic if `opaque` belongs to a different [`",
+                        stringify!($obj_trait), "`] implementation.")]
+        pub fn from_opaque<$( $lifetime, )? $( $decl_bound_id ),* >(
+            opaque: $opaque
+        ) -> $inner_ret_ty
+        where
+            $drv_trait_param: KmsDriver<$drv_assoc_trait = $obj_trait_param>,
+            $obj_trait_param: $obj_trait<Driver = $drv_trait_param>
+            $( , $( $extra_bound_id: $extra_trait<$( $extra_assoc = $extra_param_match ),+> ),+ )?
+        {
+            Self::try_from_opaque(opaque)
+                .map_or(None, |o| Some(o))
+                .expect(concat!("Passed ", stringify!($opaque), " does not share this ",
+                                stringify!($obj_trait), " implementation."))
+        }
+    };
+}
+
+pub(crate) use impl_from_opaque_mode_obj;
+
+impl<T: KmsDriver, C: DeviceContext> Device<T, C> {
+    /// Retrieve a pointer to the mode_config mutex
+    #[inline]
+    pub(crate) fn mode_config_mutex(&self) -> &Mutex<()> {
+        // SAFETY: This lock is initialized for as long as `Device<T>` is exposed to users
+        unsafe { Mutex::from_raw(addr_of_mut!((*self.as_raw()).mode_config.mutex)) }
+    }
+
+    /// Return the number of registered [`Crtc`](crtc::Crtc) objects on this [`Device`].
+    #[inline]
+    pub fn num_crtcs(&self) -> u32 {
+        // SAFETY:
+        // * This can only be modified during the single-threaded context before registration, so
+        //   this is safe
+        // * num_crtc is always >= 0, so casting to u32 is fine
+        unsafe { (*self.as_raw()).mode_config.num_crtc as u32 }
+    }
+}
+
+impl<T: KmsDriver> Device<T> {
+    /// Acquire the [`mode_config.mutex`] for this [`Device`].
+    #[inline]
+    pub fn mode_config_lock(&self) -> ModeConfigGuard<'_, T> {
+        // INVARIANT: We're locking mode_config.mutex, fulfilling our invariant that this lock is
+        // held throughout ModeConfigGuard's lifetime.
+        ModeConfigGuard(self.mode_config_mutex().lock(), PhantomData)
+    }
+}
+
+/// A modesetting object in DRM.
+///
+/// This is any type of object where the underlying C object contains a [`struct drm_mode_object`].
+/// This type requires [`Send`] + [`Sync`] as all modesetting objects in DRM are able to be sent
+/// between threads.
+///
+/// This type is only implemented by the DRM crate itself.
+///
+/// # Safety
+///
+/// [`raw_mode_obj()`] must always return a valid pointer to an initialized
+/// [`struct drm_mode_object`].
+///
+/// [`struct drm_mode_object`]: srctree/include/drm/drm_mode_object.h
+/// [`raw_mode_obj()`]: ModeObject::raw_mode_obj()
+pub unsafe trait ModeObject: Sealed + Send + Sync {
+    /// The parent driver for this [`ModeObject`].
+    type Driver: KmsDriver;
+
+    /// Return the [`Device`] for this [`ModeObject`].
+    fn drm_dev(&self) -> &Device<Self::Driver>;
+
+    /// Return a pointer to the [`struct drm_mode_object`] for this [`ModeObject`].
+    ///
+    /// [`struct drm_mode_object`]: (srctree/include/drm/drm_mode_object.h)
+    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object;
+}
+
+/// A trait for modesetting objects which don't come with their own reference-counting.
+///
+/// Some [`ModeObject`] types in DRM do not have a reference count. These types are considered
+/// "static" and share the lifetime of their parent [`Device`]. To retrieve an owned reference to
+/// such types, see [`KmsRef`].
+///
+/// # Safety
+///
+/// This trait must only be implemented for modesetting objects which do not have a refcount within
+/// their [`struct drm_mode_object`], otherwise [`KmsRef`] can't guarantee the object will stay
+/// alive.
+///
+/// [`struct drm_mode_object`]: (srctree/include/drm/drm_mode_object.h)
+pub unsafe trait StaticModeObject: ModeObject {}
+
+/// An owned reference to a [`StaticModeObject`].
+///
+/// Note that since [`StaticModeObject`] types share the lifetime of their parent [`Device`], the
+/// parent [`Device`] will stay alive as long as this type exists. Thus, users should be aware that
+/// storing a [`KmsRef`] within a [`ModeObject`] is a circular reference.
+///
+/// # Invariants
+///
+/// `self.0` points to a valid instance of `T` throughout the lifetime of this type.
+pub struct KmsRef<T: StaticModeObject>(NonNull<T>);
+
+// SAFETY: Owned references to DRM device are thread-safe.
+unsafe impl<T: StaticModeObject> Send for KmsRef<T> {}
+// SAFETY: Owned references to DRM device are thread-safe.
+unsafe impl<T: StaticModeObject> Sync for KmsRef<T> {}
+
+impl<T: StaticModeObject> From<&T> for KmsRef<T> {
+    fn from(value: &T) -> Self {
+        // INVARIANT: Because the lifetime of the StaticModeObject is the same as the lifetime of
+        // its parent device, we can ensure that `value` remains alive by incrementing the device's
+        // reference count. The device will only disappear once we drop this reference in `Drop`.
+        value.drm_dev().inc_ref();
+
+        Self(value.into())
+    }
+}
+
+impl<T: StaticModeObject> Drop for KmsRef<T> {
+    fn drop(&mut self) {
+        // SAFETY: We're reclaiming the reference we leaked in From<&T>
+        drop(unsafe { ARef::from_raw(self.drm_dev().into()) })
+    }
+}
+
+impl<T: StaticModeObject> Deref for KmsRef<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        // SAFETY: We're guaranteed object will point to a valid object as long as we hold dev
+        unsafe { self.0.as_ref() }
+    }
+}
+
+impl<T: StaticModeObject> Clone for KmsRef<T> {
+    fn clone(&self) -> Self {
+        // INVARIANT: See `From<&T>`; we keep the parent device alive for this new reference.
+        self.drm_dev().inc_ref();
+
+        Self(self.0)
+    }
+}
+
+macro_rules! impl_aref_for_mode_object {
+    (impl $( < $( $param:ident: $bound:ident ),+ > )? for $type:ty) => {
+        // SAFETY: drm_mode_object_get()/put() ensure the type is ref-counted according to the
+        // safety contract
+        unsafe impl $( < $( $param: $bound ),+ > )? kernel::types::AlwaysRefCounted for $type {
+            #[inline]
+            fn inc_ref(&self) {
+                // SAFETY: We're guaranteed by the safety contract of `ModeObject` that
+                // `raw_mode_obj()` always returns a pointer to an initialized `drm_mode_object`.
+                unsafe { kernel::bindings::drm_mode_object_get(self.raw_mode_obj()) }
+            }
+
+            #[inline]
+            unsafe fn dec_ref(obj: core::ptr::NonNull<Self>) {
+                // SAFETY: We're guaranteed by the safety contract of `ModeObject` that
+                // `raw_mode_obj()` always returns a pointer to an initialized `drm_mode_object`.
+                unsafe { kernel::bindings::drm_mode_object_put(obj.as_ref().raw_mode_obj()) }
+            }
+        }
+    };
+}
+
+pub(super) use impl_aref_for_mode_object;
+
+/// A trait for any object related to a [`ModeObject`] that can return its vtable.
+///
+/// This reference will be used for checking whether an opaque representation of a mode object uses a
+/// specific driver trait implementation.
+///
+/// # Safety
+///
+/// `ModeObjectVtable::vtable()` must always return a valid pointer to the relevant mode object's
+/// vtable.
+pub(crate) unsafe trait ModeObjectVtable {
+    /// The type for the auto-generated vtable.
+    type Vtable;
+
+    /// Return a static reference to the auto-generated vtable for the relevant mode object.
+    fn vtable(&self) -> *const Self::Vtable;
+}
+
+/// A mode config guard.
+///
+/// This is an exclusive primitive that represents when [`drm_device.mode_config.mutex`] is held - as
+/// some modesetting operations (particularly ones related to [`connectors`](connector)) are still
+/// protected under this single lock. The lock will be dropped once this object is dropped.
+///
+/// # Invariants
+///
+/// - `self.0` is contained within a [`struct drm_mode_config`], which is contained within a
+///   [`struct drm_device`].
+/// - The [`KmsDriver`] implementation of that [`struct drm_device`] is always `T`.
+/// - This type proves that [`drm_device.mode_config.mutex`] is acquired.
+///
+/// [`struct drm_mode_config`]: (srctree/include/drm/drm_device.h)
+/// [`drm_device.mode_config.mutex`]: (srctree/include/drm/drm_device.h)
+/// [`struct drm_device`]: (srctree/include/drm/drm_device.h)
+pub struct ModeConfigGuard<'a, T: KmsDriver>(MutexGuard<'a, ()>, PhantomData<T>);
+
+impl<'a, T: KmsDriver> ModeConfigGuard<'a, T> {
+    /// Return the [`Device`] that this [`ModeConfigGuard`] belongs to.
+    pub fn drm_dev(&self) -> &'a Device<T> {
+        let lock: *mut bindings::mutex = ptr::from_ref(self.0.lock_ref()).cast_mut().cast();
+
+        // SAFETY:
+        // - `self` is embedded within a `drm_mode_config` via our type invariants
+        // - `self.0.lock` has an equivalent data type to `mutex` via its type invariants.
+        let mode_config = unsafe { container_of!(lock, bindings::drm_mode_config, mutex) };
+
+        // SAFETY: And that `drm_mode_config` lives in a `drm_device` via type invariants.
+        unsafe {
+            Device::from_raw(container_of!(
+                mode_config,
+                bindings::drm_device,
+                mode_config
+            ))
+        }
+    }
+
+    /// Assert that the given device is the owner of this mode config guard.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `dev` is different from the owning device for this mode config guard.
+    #[inline]
+    pub(crate) fn assert_owner(&self, dev: &Device<T>) {
+        assert!(ptr::eq(self.drm_dev(), dev));
+    }
+}
diff --git a/rust/kernel/drm/kms/atomic.rs b/rust/kernel/drm/kms/atomic.rs
new file mode 100644
index 000000000000..cc14bff47abd
--- /dev/null
+++ b/rust/kernel/drm/kms/atomic.rs
@@ -0,0 +1,864 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! [`struct drm_atomic_state`] related bindings for rust.
+//!
+//! [`struct drm_atomic_state`]: srctree/include/drm/drm_atomic.h
+use super::{connector::*, crtc::*, plane::*, KmsDriver, ModeObject};
+use crate::{
+    bindings,
+    drm::device::Device,
+    error::{from_err_ptr, to_result},
+    prelude::*,
+    types::*,
+};
+use core::{cell::Cell, marker::*, mem::ManuallyDrop, ops::*, ptr::NonNull};
+
+/// The main wrapper around [`struct drm_atomic_state`].
+///
+/// This type is usually embedded within another interface such as an [`AtomicStateMutator`].
+///
+/// # Invariants
+///
+/// - The data layout of this type is identical to [`struct drm_atomic_state`].
+/// - `state` is initialized for as long as this type is exposed to users.
+///
+/// [`struct drm_atomic_state`]: srctree/include/drm/drm_atomic.h
+#[repr(transparent)]
+pub struct AtomicState<T: KmsDriver> {
+    pub(super) state: Opaque<bindings::drm_atomic_state>,
+    _p: PhantomData<T>,
+}
+
+impl<T: KmsDriver> AtomicState<T> {
+    /// Reconstruct an immutable reference to an atomic state from the given pointer
+    ///
+    /// # Safety
+    ///
+    /// `ptr` must point to a valid initialized instance of [`struct drm_atomic_state`].
+    ///
+    /// [`struct drm_atomic_state`]: srctree/include/drm/drm_atomic.h
+    #[allow(dead_code)]
+    pub(super) unsafe fn from_raw<'a>(ptr: *const bindings::drm_atomic_state) -> &'a Self {
+        // SAFETY: Our data layout is identical
+        // INVARIANT: Our safety contract upholds the guarantee that `state` is initialized for as
+        // long as this type is exposed to users.
+        unsafe { &*ptr.cast() }
+    }
+
+    pub(crate) fn as_raw(&self) -> *mut bindings::drm_atomic_state {
+        self.state.get()
+    }
+
+    /// Return the [`Device`] associated with this [`AtomicState`].
+    pub fn drm_dev(&self) -> &Device<T> {
+        // SAFETY:
+        // - `state` is initialized via our type invariants.
+        // - `dev` is invariant throughout the lifetime of `AtomicState`
+        unsafe { Device::from_raw((*self.state.get()).dev) }
+    }
+
+    /// Return the old atomic state for `crtc`, if it is present within this [`AtomicState`].
+    pub fn get_old_crtc_state<C>(&self, crtc: &C) -> Option<&C::State>
+    where
+        C: ModesettableCrtc + ModeObject<Driver = T>,
+    {
+        // SAFETY: This function either returns NULL or a valid pointer to a `drm_crtc_state`
+        unsafe {
+            bindings::drm_atomic_get_old_crtc_state(self.as_raw(), crtc.as_raw())
+                .as_ref()
+                .map(|p| C::State::from_raw(p))
+        }
+    }
+
+    /// Return the old atomic state for `plane`, if it is present within this [`AtomicState`].
+    pub fn get_old_plane_state<P>(&self, plane: &P) -> Option<&P::State>
+    where
+        P: ModesettablePlane + ModeObject<Driver = T>,
+    {
+        // SAFETY: This function either returns NULL or a valid pointer to a `drm_plane_state`
+        unsafe {
+            bindings::drm_atomic_get_old_plane_state(self.as_raw(), plane.as_raw())
+                .as_ref()
+                .map(|p| P::State::from_raw(p))
+        }
+    }
+
+    /// Return the old atomic state for `connector` if it is present within this [`AtomicState`].
+    pub fn get_old_connector_state<C>(&self, connector: &C) -> Option<&C::State>
+    where
+        C: ModesettableConnector + ModeObject<Driver = T>,
+    {
+        // SAFETY: This function either returns NULL or a valid pointer to a `drm_connector_state`.
+        unsafe {
+            bindings::drm_atomic_get_old_connector_state(self.as_raw(), connector.as_raw())
+                .as_ref()
+                .map(|p| C::State::from_raw(p))
+        }
+    }
+}
+
+// SAFETY: DRM atomic state objects are always reference counted and the get/put functions satisfy
+// the requirements.
+unsafe impl<T: KmsDriver> AlwaysRefCounted for AtomicState<T> {
+    fn inc_ref(&self) {
+        // SAFETY: `state` is initialized for as long as this type is exposed to users
+        unsafe { bindings::drm_atomic_state_get(self.state.get()) }
+    }
+
+    unsafe fn dec_ref(obj: NonNull<Self>) {
+        // SAFETY: `obj` contains a valid non-null pointer to an initialized `Self`.
+        unsafe { bindings::drm_atomic_state_put(obj.as_ptr().cast()) }
+    }
+}
+
+/// A smart-pointer for modifying the contents of an atomic state.
+///
+/// As it's not unreasonable for a modesetting driver to want to have references to the state of
+/// multiple modesetting objects at once, along with mutating multiple states for unique modesetting
+/// objects at once, this type provides a mechanism for safely doing both of these things.
+///
+/// To honor Rust's aliasing rules regarding mutable references, this structure ensures only one
+/// mutable reference to a mode object's atomic state may exist at a time - and refuses to provide
+/// another if one has already been taken out using runtime checks.
+pub struct AtomicStateMutator<T: KmsDriver> {
+    /// The state being mutated. Note that the use of `ManuallyDrop` here is because mutators are
+    /// only constructed in FFI callbacks and thus borrow their references to the atomic state from
+    /// DRM. Composers, which make use of mutators internally, can potentially be owned by rust code
+    /// if a driver is performing an atomic commit internally - and thus will call the drop
+    /// implementation here.
+    state: ManuallyDrop<ARef<AtomicState<T>>>,
+
+    /// Bitmask of borrowed CRTC state objects
+    pub(super) borrowed_crtcs: Cell<u32>,
+    /// Bitmask of borrowed plane state objects
+    pub(super) borrowed_planes: Cell<u32>,
+    /// Bitmask of borrowed connector state objects
+    pub(super) borrowed_connectors: Cell<u32>,
+}
+
+impl<T: KmsDriver> AtomicStateMutator<T> {
+    /// Construct a new [`AtomicStateMutator`]
+    ///
+    /// # Safety
+    ///
+    /// `ptr` must point to a valid `drm_atomic_state`
+    #[allow(dead_code)]
+    pub(super) unsafe fn new(ptr: NonNull<bindings::drm_atomic_state>) -> Self {
+        Self {
+            // SAFETY: The data layout of `AtomicState<T>` is identical to drm_atomic_state
+            // We use `ManuallyDrop` because `AtomicStateMutator` is only ever provided to users in
+            // the context of KMS callbacks. As such, skipping ref inc/dec for the atomic state is
+            // convienent for our bindings.
+            state: ManuallyDrop::new(unsafe { ARef::from_raw(ptr.cast()) }),
+            borrowed_planes: Cell::default(),
+            borrowed_crtcs: Cell::default(),
+            borrowed_connectors: Cell::default(),
+        }
+    }
+
+    pub(crate) fn as_raw(&self) -> *mut bindings::drm_atomic_state {
+        self.state.as_raw()
+    }
+
+    /// Return the [`Device`] for this [`AtomicStateMutator`].
+    pub fn drm_dev(&self) -> &Device<T> {
+        self.state.drm_dev()
+    }
+
+    /// Retrieve the last committed atomic state for `crtc` if `crtc` has already been added to the
+    /// atomic state being composed.
+    ///
+    /// Returns `None` otherwise.
+    pub fn get_old_crtc_state<C>(&self, crtc: &C) -> Option<&C::State>
+    where
+        C: ModesettableCrtc + ModeObject<Driver = T>,
+    {
+        self.state.get_old_crtc_state(crtc)
+    }
+
+    /// Retrieve the last committed atomic state for `connector` if `connector` has already been
+    /// added to the atomic state being composed.
+    ///
+    /// Returns `None` otherwise.
+    pub fn get_old_connector_state<C>(&self, connector: &C) -> Option<&C::State>
+    where
+        C: ModesettableConnector + ModeObject<Driver = T>,
+    {
+        self.state.get_old_connector_state(connector)
+    }
+
+    /// Retrieve the last committed atomic state for `plane` if `plane` has already been added to
+    /// the atomic state being composed.
+    ///
+    /// Returns `None` otherwise.
+    pub fn get_old_plane_state<P>(&self, plane: &P) -> Option<&P::State>
+    where
+        P: ModesettablePlane + ModeObject<Driver = T>,
+    {
+        self.state.get_old_plane_state(plane)
+    }
+
+    /// Return a composer for `plane`s new atomic state if it was previously added to the atomic
+    /// state being composed.
+    ///
+    /// Returns `None` otherwise, or if another mutator still exists for this state.
+    pub fn get_new_crtc_state<C>(&self, crtc: &C) -> Option<CrtcStateMutator<'_, C::State>>
+    where
+        C: ModesettableCrtc + ModeObject<Driver = T>,
+    {
+        // SAFETY: DRM either returns NULL or a valid pointer to a `drm_crtc_state`
+        let state =
+            unsafe { bindings::drm_atomic_get_new_crtc_state(self.as_raw(), crtc.as_raw()) };
+
+        CrtcStateMutator::<C::State>::new(self, NonNull::new(state)?)
+    }
+
+    /// Return a composer for `plane`s new atomic state if it was previously added to the atomic
+    /// state being composed.
+    ///
+    /// Returns `None` otherwise, or if another mutator still exists for this state.
+    pub fn get_new_plane_state<P>(&self, plane: &P) -> Option<PlaneStateMutator<'_, P::State>>
+    where
+        P: ModesettablePlane + ModeObject<Driver = T>,
+    {
+        // SAFETY: DRM either returns NULL or a valid pointer to a `drm_plane_state`.
+        let state =
+            unsafe { bindings::drm_atomic_get_new_plane_state(self.as_raw(), plane.as_raw()) };
+
+        PlaneStateMutator::<P::State>::new(self, NonNull::new(state)?)
+    }
+
+    /// Return a composer for `crtc`s new atomic state if it was previously added to the atomic
+    /// state being composed.
+    ///
+    /// Returns `None` otherwise, or if another mutator still exists for this state.
+    pub fn get_new_connector_state<C>(
+        &self,
+        connector: &C,
+    ) -> Option<ConnectorStateMutator<'_, C::State>>
+    where
+        C: ModesettableConnector + ModeObject<Driver = T>,
+    {
+        // SAFETY: DRM either returns NULL or a valid pointer to a `drm_connector_state`
+        let state = unsafe {
+            bindings::drm_atomic_get_new_connector_state(self.as_raw(), connector.as_raw())
+        };
+
+        ConnectorStateMutator::<C::State>::new(self, NonNull::new(state)?)
+    }
+}
+
+/// An [`AtomicStateMutator`] wrapper which is not yet part of any commit operation.
+///
+/// Since it's not yet part of a commit operation, new mode objects may be added to the state. It
+/// also holds a reference to the underlying [`AtomicState`] that will be released when this object
+/// is dropped.
+pub struct AtomicStateComposer<T: KmsDriver>(AtomicStateMutator<T>);
+
+impl<T: KmsDriver> Deref for AtomicStateComposer<T> {
+    type Target = AtomicStateMutator<T>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<T: KmsDriver> Drop for AtomicStateComposer<T> {
+    fn drop(&mut self) {
+        // SAFETY: We're in drop, so this is guaranteed to be the last possible reference
+        unsafe { ManuallyDrop::drop(&mut self.0.state) }
+    }
+}
+
+impl<T: KmsDriver> AtomicStateComposer<T> {
+    /// # Safety
+    ///
+    /// The caller guarantees that `ptr` points to a valid instance of `drm_atomic_state`.
+    pub(crate) unsafe fn new(ptr: NonNull<bindings::drm_atomic_state>) -> Self {
+        // SAFETY: see `AtomicStateMutator::from_raw()`
+        Self(unsafe { AtomicStateMutator::new(ptr) })
+    }
+
+    /// Attempt to add the state for `crtc` to the atomic state for this composer if it hasn't
+    /// already been added, and create a mutator for it.
+    ///
+    /// If a composer already exists for this `crtc`, this function returns `Error(EBUSY)`. If
+    /// attempting to add the state fails, another error code will be returned.
+    pub fn add_crtc_state<C>(&self, crtc: &C) -> Result<CrtcStateMutator<'_, C::State>>
+    where
+        C: ModesettableCrtc + ModeObject<Driver = T>,
+    {
+        // SAFETY: DRM will only return a valid pointer to a `drm_crtc_state` - or an error.
+        let state = unsafe {
+            from_err_ptr(bindings::drm_atomic_get_crtc_state(
+                self.as_raw(),
+                crtc.as_raw(),
+            ))
+            .map(|c| NonNull::new_unchecked(c))
+        }?;
+
+        CrtcStateMutator::<C::State>::new(self, state).ok_or(EBUSY)
+    }
+
+    /// Attempt to add the state for `plane` to the atomic state for this composer if it hasn't
+    /// already been added, and create a mutator for it.
+    ///
+    /// If a composer already exists for this `plane`, this function returns `Error(EBUSY)`. If
+    /// attempting to add the state fails, another error code will be returned.
+    pub fn add_plane_state<P>(&self, plane: &P) -> Result<PlaneStateMutator<'_, P::State>>
+    where
+        P: ModesettablePlane + ModeObject<Driver = T>,
+    {
+        // SAFETY: DRM will only return a valid pointer to a `drm_plane_state` - or an error.
+        let state = unsafe {
+            from_err_ptr(bindings::drm_atomic_get_plane_state(
+                self.as_raw(),
+                plane.as_raw(),
+            ))
+            .map(|p| NonNull::new_unchecked(p))
+        }?;
+
+        PlaneStateMutator::<P::State>::new(self, state).ok_or(EBUSY)
+    }
+
+    /// Attempt to add the state for `connector` to the atomic state for this composer if it hasn't
+    /// already been added, and create a mutator for it.
+    ///
+    /// If a composer already exists for this `connector`, this function returns `Error(EBUSY)`. If
+    /// attempting to add the state fails, another error code will be returned.
+    pub fn add_connector_state<C>(
+        &self,
+        connector: &C,
+    ) -> Result<ConnectorStateMutator<'_, C::State>>
+    where
+        C: ModesettableConnector + ModeObject<Driver = T>,
+    {
+        // SAFETY: DRM will only return a valid pointer to a `drm_plane_state` - or an error.
+        let state = unsafe {
+            from_err_ptr(bindings::drm_atomic_get_connector_state(
+                self.as_raw(),
+                connector.as_raw(),
+            ))
+            .map(|c| NonNull::new_unchecked(c))
+        }?;
+
+        ConnectorStateMutator::<C::State>::new(self, state).ok_or(EBUSY)
+    }
+
+    /// Attempt to add any planes affected by changes on `crtc` to this [`AtomicStateComposer`].
+    ///
+    /// Will return an [`Error`] if this fails.
+    pub fn add_affected_planes<C>(&self, crtc: &C) -> Result
+    where
+        C: ModesettableCrtc + ModeObject<Driver = T>,
+    {
+        // SAFETY: Both .as_raw() values are guaranteed to return a valid pointer
+        to_result(unsafe { bindings::drm_atomic_add_affected_planes(self.as_raw(), crtc.as_raw()) })
+    }
+}
+
+/// A macro for declaring the repetitive take_all(), take_state(), etc. methods for atomic state
+/// token types.
+///
+/// It is assumed that $token_name refers to a struct that contains two members:
+///
+/// - `state`: This should be the atomic state type to use
+/// - The object in question. The name of this member is generated by converting $obj to lowercase.
+///
+/// The struct should have one lifetime ($lifetime_a) declared, and one meta-variable ($meta) which
+/// should be bound to the Driver* trait for the given mode object.
+macro_rules! impl_atomic_state_token_ops {
+    (
+        $token_name:ident,
+        $state:ident,
+        $obj:ident,
+        use <$lifetime_a:lifetime, $meta:ident>
+    ) => {
+        kernel::macros::paste! {
+            /// Create a new token.
+            ///
+            /// # Safety
+            ///
+            /// To use this function it must be known in the current context that:
+            ///
+            /// - The object has had its atomic states added to `state`.
+            /// - No state mutator can possibly be taken out for the objects new state.
+            pub(crate) unsafe fn new(
+                [<$obj:lower>]: &$lifetime_a $obj<$meta>,
+                state: &$lifetime_a $state<$meta::Driver>,
+            ) -> Self {
+                Self { [<$obj:lower>], state }
+            }
+
+            #[doc = concat!("Get the [`", stringify!($obj), "`] associated with this",
+                            " [`", stringify!($token_name), "`].")]
+            pub fn [<$obj:lower>](&self) -> &$lifetime_a $obj<$meta> {
+                self.[<$obj:lower>]
+            }
+
+            /// Exchange this token for a (atomic_state, old_state, new_state) tuple.
+            pub fn take_all(self) -> (
+                &$lifetime_a $state<$meta::Driver>,
+                &$lifetime_a [<$obj State>]<$meta::State>,
+                [<$obj StateMutator>]<$lifetime_a, [<$obj State>]<$meta::State>>,
+            ) {
+                let (old_state, new_state) = (
+                    self.state.[<get_old_ $obj:lower _state>](self.[<$obj:lower>]),
+                    self.state.[<get_new_ $obj:lower _state>](self.[<$obj:lower>]),
+                );
+
+                // SAFETY:
+                // - Both the old and new object state are present in `state` via our type
+                //   invariants.
+                // - The new state is guaranteed to have no mutators taken out via our type
+                //   invariants.
+                let (old_state, new_state) = unsafe {
+                    (old_state.unwrap_unchecked(), new_state.unwrap_unchecked())
+                };
+
+                (self.state, old_state, new_state)
+            }
+
+            #[doc = concat!("Exchange this token for the old [`", stringify!($obj), "State`].")]
+            pub fn take_old_state(self) -> &$lifetime_a [<$obj State>]<$meta::State> {
+                let old = self.state.[<get_old_ $obj:lower _state>](self.[<$obj:lower>]);
+
+                // SAFETY: The old state is guaranteed to be present in `state` via our type
+                // invariants.
+                unsafe { old.unwrap_unchecked() }
+            }
+
+            #[doc = concat!("Exchange this token for the new [`", stringify!($obj), "State`].")]
+            pub fn take_new_state(
+                self
+            ) -> [<$obj StateMutator>]<$lifetime_a, [<$obj State>]<$meta::State>> {
+                let new = self.state.[<get_new_ $obj:lower _state>](self.[<$obj:lower>]);
+
+                // SAFETY:
+                // - The new state is guaranteed to be present in our `state` via our type
+                //   invariants.
+                // - The new state is guaranteed not to have any mutators taken out for it via our
+                //   type invariants.
+                unsafe { new.unwrap_unchecked() }
+            }
+
+            #[doc = concat!("Exchange this token for both the old and new [`",
+                            stringify!($obj), "State`].")]
+            pub fn take_old_new_state(self) -> (
+                &$lifetime_a [<$obj State>]<$meta::State>,
+                [<$obj StateMutator>]<$lifetime_a, [<$obj State>]<$meta::State>>,
+            ) {
+                let (old_state, new_state) = (
+                    self.state.[<get_old_ $obj:lower _state>](self.[<$obj:lower>]),
+                    self.state.[<get_new_ $obj:lower _state>](self.[<$obj:lower>]),
+                );
+
+                // SAFETY:
+                // - Both the old and new object state are present in `state` via our type
+                //   invariants.
+                // - The new state is guaranteed to have no mutators taken out via our type
+                //   invariants.
+                let (old_state, new_state) = unsafe {
+                    (old_state.unwrap_unchecked(), new_state.unwrap_unchecked())
+                };
+
+                (old_state, new_state)
+            }
+
+            #[doc = concat!("Exchange this token for both the [`", stringify!($state),
+                            "`] and the old [`", stringify!($obj), "State`].")]
+            pub fn take_state_old_state(self) -> (
+                &$lifetime_a $state<$meta::Driver>,
+                &$lifetime_a [<$obj State>]<$meta::State>,
+            ) {
+                let old = self.state.[<get_old_ $obj:lower _state>](self.[<$obj:lower>]);
+
+                // SAFETY: The old state is guaranteed to be present in `state` via our type
+                // invariants.
+                (self.state, unsafe { old.unwrap_unchecked() })
+            }
+
+            #[doc = concat!("Exchange this token for both the [`", stringify!($state),
+                            "`] and the new [`", stringify!($obj), "State`].")]
+            pub fn take_state_new_state(self) -> (
+                &$lifetime_a $state<$meta::Driver>,
+                [<$obj StateMutator>]<$lifetime_a, [<$obj State>]<$meta::State>>,
+            ) {
+                let new = self.state.[<get_new_ $obj:lower _state>](self.[<$obj:lower>]);
+
+                // SAFETY:
+                // - The new state is guaranteed to be present in `state` via our type
+                //   invariants.
+                // - The new state is guaranteed to have no mutators taken out via our type
+                //   invariants.
+                (self.state, unsafe { new.unwrap_unchecked() })
+            }
+        }
+
+        #[doc = concat!("Exchange this token for the [`", stringify!($state), "`].")]
+        pub fn take_state(self) -> &$lifetime_a $state<$meta::Driver> {
+            self.state
+        }
+    };
+}
+
+pub(crate) use impl_atomic_state_token_ops;
+
+/// A token proving that no modesets for a commit have completed.
+///
+/// This token is proof that no commits have yet completed, and is provided as an argument to
+/// [`KmsDriver::atomic_commit_tail`]. This may be used with
+/// [`AtomicCommitTail::commit_modeset_disables`].
+pub struct ModesetsReadyToken<'a>(PhantomData<&'a ()>);
+
+/// A token proving that modeset disables for a commit have completed.
+///
+/// This token is proof that an implementor's [`KmsDriver::atomic_commit_tail`] phase has finished
+/// committing any operations which disable mode objects. It is returned by
+/// [`AtomicCommitTail::commit_modeset_disables`], and can be used with
+/// [`AtomicCommitTail::commit_modeset_enables`] to acquire a [`EnablesCommittedToken`].
+pub struct DisablesCommittedToken<'a>(PhantomData<&'a ()>);
+
+/// A token proving that modeset enables for a commit have completed.
+///
+/// This token is proof that an implementor's [`KmsDriver::atomic_commit_tail`] phase has finished
+/// committing any operations which enable mode objects. It is returned by
+/// [`AtomicCommitTail::commit_modeset_enables`].
+pub struct EnablesCommittedToken<'a>(PhantomData<&'a ()>);
+
+/// A token proving that no plane updates for a commit have completed.
+///
+/// This token is proof that no plane updates have yet been completed within an implementor's
+/// [`KmsDriver::atomic_commit_tail`] implementation, and that we are ready to begin updating planes. It
+/// is provided as an argument to [`KmsDriver::atomic_commit_tail`].
+pub struct PlaneUpdatesReadyToken<'a>(PhantomData<&'a ()>);
+
+/// A token proving that all plane updates for a commit have completed.
+///
+/// This token is proof that all plane updates within an implementor's [`KmsDriver::atomic_commit_tail`]
+/// implementation have completed. It is returned by [`AtomicCommitTail::commit_planes`].
+pub struct PlaneUpdatesCommittedToken<'a>(PhantomData<&'a ()>);
+
+/// An [`AtomicState`] interface that allows a driver to control the [`atomic_commit_tail`]
+/// callback.
+///
+/// This object is provided as an argument to [`KmsDriver::atomic_commit_tail`], and represents an atomic
+/// state within the commit tail phase which is still in the process of being committed to hardware.
+/// It may be used to control the order in which the commit process happens.
+///
+/// # Invariants
+///
+/// Same as [`AtomicState`].
+///
+/// [`atomic_commit_tail`]: srctree/include/drm/drm_modeset_helper_vtables.h
+pub struct AtomicCommitTail<'a, T: KmsDriver>(&'a AtomicState<T>);
+
+impl<'a, T: KmsDriver> AtomicCommitTail<'a, T> {
+    /// Commit modesets which would disable outputs.
+    ///
+    /// This function commits any modesets which would shut down outputs, along with preparing them
+    /// for a new mode (if needed).
+    ///
+    /// Since it is physically impossible to disable an output multiple times, and since it is
+    /// logically unsound to disable an output within an atomic commit after the output was enabled
+    /// in the same commit - this function requires a [`ModesetsReadyToken`] to consume and returns
+    /// a [`DisablesCommittedToken`].
+    ///
+    /// If compatibility with legacy CRTC helpers is desired, this
+    /// should be called before [`commit_planes`] which is what the default commit function does.
+    /// But drivers with different needs can group the modeset commits tgether and do the plane
+    /// commits at the end. This is useful for drivers doing runtime PM since then plane updates
+    /// only happen when the CRTC is actually enabled.
+    ///
+    /// [`commit_planes`]: AtomicCommitTail::commit_planes
+    #[inline]
+    #[must_use]
+    pub fn commit_modeset_disables<'b>(
+        &mut self,
+        _token: ModesetsReadyToken<'_>,
+    ) -> DisablesCommittedToken<'b> {
+        // SAFETY: Both `as_raw()` calls are guaranteed to return valid pointers
+        unsafe {
+            bindings::drm_atomic_helper_commit_modeset_disables(
+                self.0.drm_dev().as_raw(),
+                self.0.as_raw(),
+            )
+        }
+
+        DisablesCommittedToken(PhantomData)
+    }
+
+    /// Commit all plane updates.
+    ///
+    /// This function performs all plane updates for the given [`AtomicCommitTail`]. Since it is
+    /// logically unsound to perform the same plane update more then once in a given atomic commit,
+    /// this function requires a [`PlaneUpdatesReadyToken`] to consume and returns a
+    /// [`PlaneUpdatesCommittedToken`] to prove that plane updates for the state have completed.
+    #[inline]
+    #[must_use]
+    pub fn commit_planes<'b>(
+        &mut self,
+        _token: PlaneUpdatesReadyToken<'_>,
+        flags: PlaneCommitFlags,
+    ) -> PlaneUpdatesCommittedToken<'b> {
+        // SAFETY: Both `as_raw()` calls are guaranteed to return valid pointers
+        unsafe {
+            bindings::drm_atomic_helper_commit_planes(
+                self.0.drm_dev().as_raw(),
+                self.0.as_raw(),
+                flags.into(),
+            )
+        }
+
+        PlaneUpdatesCommittedToken(PhantomData)
+    }
+
+    /// Commit modesets which would enable outputs.
+    ///
+    /// This function commits any modesets in the given [`AtomicCommitTail`] which would enable
+    /// outputs, along with preparing them for their new modes (if needed).
+    ///
+    /// Since it is logically unsound to enable an output before any disabling modesets within the
+    /// same atomic commit have been performed, and physically impossible to enable the same output
+    /// multiple times - this function requires a [`DisablesCommittedToken`] to consume and returns
+    /// a [`EnablesCommittedToken`] which may be used as proof that all modesets in the state have
+    /// been completed.
+    #[inline]
+    #[must_use]
+    pub fn commit_modeset_enables<'b>(
+        &mut self,
+        _token: DisablesCommittedToken<'_>,
+    ) -> EnablesCommittedToken<'b> {
+        // SAFETY: Both `as_raw()` calls are guaranteed to return valid pointers
+        unsafe {
+            bindings::drm_atomic_helper_commit_modeset_enables(
+                self.0.drm_dev().as_raw(),
+                self.0.as_raw(),
+            )
+        }
+
+        EnablesCommittedToken(PhantomData)
+    }
+
+    /// Fake vblank events if needed.
+    ///
+    /// Note that this is still relevant to drivers which don't implement [`VblankSupport`] for any
+    /// of their CRTCs.
+    ///
+    /// TODO: more doc
+    ///
+    /// [`VblankSupport`]: super::vblank::VblankSupport
+    pub fn fake_vblank(&mut self) {
+        // SAFETY: `as_raw()` is guaranteed to always return a valid pointer
+        unsafe { bindings::drm_atomic_helper_fake_vblank(self.0.as_raw()) }
+    }
+
+    /// Signal completion of the hardware commit step.
+    ///
+    /// This swaps the atomic state into the relevant atomic state pointers and marks the hardware
+    /// commit step as completed. Since this step can only happen after all plane updates and
+    /// modesets within an [`AtomicCommitTail`] have been completed, it requires both a
+    /// [`EnablesCommittedToken`] and a [`PlaneUpdatesCommittedToken`] to consume. After this
+    /// function is called, the caller no longer has exclusive access to the underlying atomic
+    /// state. As such, this function consumes the [`AtomicCommitTail`] object and returns a
+    /// [`CommittedAtomicState`] accessor for performing post-hw commit tasks.
+    pub fn commit_hw_done<'b>(
+        self,
+        _modeset_token: EnablesCommittedToken<'_>,
+        _plane_updates_token: PlaneUpdatesCommittedToken<'_>,
+    ) -> CommittedAtomicState<'b, T>
+    where
+        'a: 'b,
+    {
+        // SAFETY: we consume the `AtomicCommitTail` object, making it impossible for the user to
+        // mutate the state after this function has been called - which upholds the safety
+        // requirements of the C API allowing us to safely call this function
+        unsafe { bindings::drm_atomic_helper_commit_hw_done(self.0.as_raw()) };
+
+        CommittedAtomicState(self.0)
+    }
+}
+
+// The actual raw C callback for custom atomic commit tail implementations
+pub(crate) unsafe extern "C" fn commit_tail_callback<T: KmsDriver>(
+    state: *mut bindings::drm_atomic_state,
+) {
+    // SAFETY:
+    // - We're guaranteed by DRM that `state` always points to a valid instance of
+    //   `bindings::drm_atomic_state`
+    // - This conversion is safe via the type invariants
+    let state = unsafe { AtomicState::from_raw(state.cast_const()) };
+
+    T::atomic_commit_tail(
+        AtomicCommitTail(state),
+        ModesetsReadyToken(PhantomData),
+        PlaneUpdatesReadyToken(PhantomData),
+    );
+}
+
+/// An [`AtomicState`] which was just committed with [`AtomicCommitTail::commit_hw_done`].
+///
+/// This object represents an [`AtomicState`] which has been fully committed to hardware, and as
+/// such may no longer be mutated as it is visible to userspace. It may be used to control what
+/// happens immediately after an atomic commit finishes within the [`atomic_commit_tail`] callback.
+///
+/// Since acquiring this object means that all modesetting locks have been dropped, a non-blocking
+/// commit could happen at the same time an [`atomic_commit_tail`] implementer has access to this
+/// object. Thus, it cannot be assumed that this object represents the current hardware state - and
+/// instead only represents the final result of the [`AtomicCommitTail`] that was just committed.
+///
+/// # Invariants
+///
+/// It may be assumed that [`drm_atomic_helper_commit_hw_done`] has been called as long as this type
+/// exists.
+///
+/// [`atomic_commit_tail`]: KmsDriver::atomic_commit_tail
+/// [`drm_atomic_helper_commit_hw_done`]: srctree/include/drm/drm_atomic_helper.h
+pub struct CommittedAtomicState<'a, T: KmsDriver>(&'a AtomicState<T>);
+
+impl<'a, T: KmsDriver> CommittedAtomicState<'a, T> {
+    /// Wait for page flips on this state to complete
+    pub fn wait_for_flip_done(&self) {
+        // SAFETY: `drm_atomic_helper_commit_hw_done` has been called via our invariants
+        unsafe {
+            bindings::drm_atomic_helper_wait_for_flip_done(
+                self.0.drm_dev().as_raw(),
+                self.0.as_raw(),
+            )
+        }
+    }
+}
+
+impl<'a, T: KmsDriver> Drop for CommittedAtomicState<'a, T> {
+    fn drop(&mut self) {
+        // SAFETY:
+        // * This interface represents the last atomic state accessor which could be affected as a
+        //   result of resources from an atomic commit being cleaned up.
+        unsafe {
+            bindings::drm_atomic_helper_cleanup_planes(self.0.drm_dev().as_raw(), self.0.as_raw())
+        }
+    }
+}
+
+/// An enumator representing a single flag in [`PlaneCommitFlags`].
+///
+/// This is a non-exhaustive list, as the C side could add more later.
+#[derive(Copy, Clone, PartialEq, Eq)]
+#[repr(u32)]
+#[non_exhaustive]
+pub enum PlaneCommitFlag {
+    /// Don't notify applications of plane updates for newly-disabled planes. Drivers are encouraged
+    /// to set this flag by default, as otherwise they need to ignore plane updates for disabled
+    /// planes by hand.
+    ActiveOnly = (1 << 0),
+    /// Tell the DRM core that the display hardware requires that a [`Crtc`]'s planes must be
+    /// disabled when the [`Crtc`] is disabled. When not specified,
+    /// [`AtomicCommitTail::commit_planes`] will skip the atomic disable callbacks for a plane if
+    /// the [`Crtc`] in the old [`PlaneState`] needs a modesetting operation. It is still up to the
+    /// driver to disable said planes in their [`DriverCrtc::atomic_disable`] callback.
+    NoDisableAfterModeset = (1 << 1),
+}
+
+impl BitOr for PlaneCommitFlag {
+    type Output = PlaneCommitFlags;
+
+    fn bitor(self, rhs: Self) -> Self::Output {
+        PlaneCommitFlags(self as u32 | rhs as u32)
+    }
+}
+
+impl BitOr<PlaneCommitFlags> for PlaneCommitFlag {
+    type Output = PlaneCommitFlags;
+
+    fn bitor(self, rhs: PlaneCommitFlags) -> Self::Output {
+        PlaneCommitFlags(self as u32 | rhs.0)
+    }
+}
+
+/// A bitmask for controlling the behavior of [`AtomicCommitTail::commit_planes`].
+///
+/// This corresponds to the `DRM_PLANE_COMMIT_*` flags on the C side. Note that this bitmask does
+/// not discard unknown values in order to ensure that adding new flags on the C side of things does
+/// not break anything in the future.
+#[derive(Copy, Clone, Default, PartialEq, Eq)]
+pub struct PlaneCommitFlags(u32);
+
+impl From<PlaneCommitFlag> for PlaneCommitFlags {
+    fn from(value: PlaneCommitFlag) -> Self {
+        Self(value as u32)
+    }
+}
+
+impl From<PlaneCommitFlags> for u32 {
+    fn from(value: PlaneCommitFlags) -> Self {
+        value.0
+    }
+}
+
+impl BitOr for PlaneCommitFlags {
+    type Output = Self;
+
+    fn bitor(self, rhs: Self) -> Self::Output {
+        Self(self.0 | rhs.0)
+    }
+}
+
+impl BitOrAssign for PlaneCommitFlags {
+    fn bitor_assign(&mut self, rhs: Self) {
+        *self = *self | rhs
+    }
+}
+
+impl BitAnd for PlaneCommitFlags {
+    type Output = PlaneCommitFlags;
+
+    fn bitand(self, rhs: Self) -> Self::Output {
+        Self(self.0 & rhs.0)
+    }
+}
+
+impl BitAndAssign for PlaneCommitFlags {
+    fn bitand_assign(&mut self, rhs: Self) {
+        *self = *self & rhs
+    }
+}
+
+impl BitOr<PlaneCommitFlag> for PlaneCommitFlags {
+    type Output = Self;
+
+    fn bitor(self, rhs: PlaneCommitFlag) -> Self::Output {
+        self | Self::from(rhs)
+    }
+}
+
+impl BitOrAssign<PlaneCommitFlag> for PlaneCommitFlags {
+    fn bitor_assign(&mut self, rhs: PlaneCommitFlag) {
+        *self = *self | rhs
+    }
+}
+
+impl BitAnd<PlaneCommitFlag> for PlaneCommitFlags {
+    type Output = PlaneCommitFlags;
+
+    fn bitand(self, rhs: PlaneCommitFlag) -> Self::Output {
+        self & Self::from(rhs)
+    }
+}
+
+impl BitAndAssign<PlaneCommitFlag> for PlaneCommitFlags {
+    fn bitand_assign(&mut self, rhs: PlaneCommitFlag) {
+        *self = *self & rhs
+    }
+}
+
+impl PlaneCommitFlags {
+    /// Create a new bitmask.
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    /// Check if the bitmask has the given commit flag set.
+    pub fn has(&self, flag: PlaneCommitFlag) -> bool {
+        *self & flag == flag.into()
+    }
+}
diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs
new file mode 100644
index 000000000000..05ec64cf6fa2
--- /dev/null
+++ b/rust/kernel/drm/kms/connector.rs
@@ -0,0 +1,997 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! DRM display connectors.
+//!
+//! C header: [`include/drm/drm_connector.h`](srctree/include/drm/drm_connector.h)
+
+use super::{
+    atomic::*, encoder::*, KmsDriver, ModeConfigGuard, ModeObject, ModeObjectVtable, Sealed
+};
+use crate::{
+    alloc::KBox,
+    bindings,
+    drm::{device::Device, kms::{NewKmsDevice, Probing}},
+    error::to_result,
+    prelude::*,
+    types::{NotThreadSafe, Opaque},
+};
+use core::{
+    cell::Cell,
+    marker::*,
+    mem::{self, ManuallyDrop},
+    ops::*,
+    ptr::{null_mut, NonNull},
+    stringify,
+};
+use macros::paste;
+
+/// A macro for generating our type ID enumerator.
+macro_rules! declare_conn_types {
+    ($( $oldname:ident as $newname:ident ),+) => {
+        /// An enumerator for all possible [`Connector`] type IDs.
+        #[repr(i32)]
+        #[non_exhaustive]
+        #[derive(Copy, Clone, PartialEq, Eq)]
+        pub enum Type {
+            // Note: bindgen defaults the macro values to u32 and not i32, but DRM takes them as an
+            // i32 - so just do the conversion here
+            $(
+                #[doc = concat!("The connector type ID for a ", stringify!($newname), " connector.")]
+                $newname = paste!(crate::bindings::[<DRM_MODE_CONNECTOR_ $oldname>]) as i32
+            ),+,
+
+            // 9PinDIN is special because of the 9, making it an invalid ident. Just define it here
+            // manually since it's the only one
+
+            /// The connector type ID for a 9PinDIN connector.
+            _9PinDin = crate::bindings::DRM_MODE_CONNECTOR_9PinDIN as i32
+        }
+    };
+}
+
+declare_conn_types! {
+    Unknown     as Unknown,
+    Composite   as Composite,
+    Component   as Component,
+    DisplayPort as DisplayPort,
+    VGA         as Vga,
+    DVII        as DviI,
+    DVID        as DviD,
+    DVIA        as DviA,
+    SVIDEO      as SVideo,
+    LVDS        as Lvds,
+    HDMIA       as HdmiA,
+    HDMIB       as HdmiB,
+    TV          as Tv,
+    eDP         as Edp,
+    VIRTUAL     as Virtual,
+    DSI         as Dsi,
+    DPI         as Dpi,
+    WRITEBACK   as Writeback,
+    SPI         as Spi,
+    USB         as Usb
+}
+
+/// The main trait for implementing the [`struct drm_connector`] API for [`Connector`].
+///
+/// Any KMS driver should have at least one implementation of this type, which allows them to create
+/// [`Connector`] objects. Additionally, a driver may store driver-private data within the type that
+/// implements [`DriverConnector`] - and it will be made available when using a fully typed
+/// [`Connector`] object.
+///
+/// # Invariants
+///
+/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in
+///   [`struct drm_connector`] pointers are contained within a [`Connector<Self>`].
+/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in
+///   [`struct drm_connector_state`] pointers are contained within a
+///   [`ConnectorState<Self::State>`].
+///
+/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h
+/// [`struct drm_connector_state`]: srctree/include/drm/drm_connector.h
+#[vtable]
+pub trait DriverConnector: Send + Sync + Sized {
+    /// The generated C vtable for this [`DriverConnector`] implementation
+    const OPS: &'static DriverConnectorOps = &DriverConnectorOps {
+        funcs: bindings::drm_connector_funcs {
+            dpms: None,
+            atomic_get_property: None,
+            atomic_set_property: None,
+            early_unregister: None,
+            late_register: None,
+            set_property: None,
+            reset: Some(connector_reset_callback::<Self::State>),
+            atomic_print_state: None,
+            atomic_destroy_state: Some(atomic_destroy_state_callback::<Self::State>),
+            destroy: Some(connector_destroy_callback::<Self>),
+            force: None,
+            detect: None,
+            fill_modes: Some(bindings::drm_helper_probe_single_connector_modes),
+            debugfs_init: None,
+            oob_hotplug_event: None,
+            atomic_duplicate_state: Some(atomic_duplicate_state_callback::<Self::State>),
+        },
+        helper_funcs: bindings::drm_connector_helper_funcs {
+            mode_valid: None,
+            atomic_check: None,
+            get_modes: Some(get_modes_callback::<Self>),
+            detect_ctx: None,
+            enable_hpd: None,
+            disable_hpd: None,
+            best_encoder: None,
+            atomic_commit: None,
+            mode_valid_ctx: None,
+            atomic_best_encoder: None,
+            prepare_writeback_job: None,
+            cleanup_writeback_job: None,
+        },
+    };
+
+    /// The type to pass to the `args` field of [`UnregisteredConnector::new`].
+    ///
+    /// This type will be made available in in the `args` argument of [`Self::new`]. Drivers which
+    /// don't need this can simply pass [`()`] here.
+    type Args;
+
+    /// The parent [`KmsDriver`] implementation.
+    type Driver: KmsDriver;
+
+    /// The [`DriverConnectorState`] implementation for this [`DriverConnector`].
+    ///
+    /// See [`DriverConnectorState`] for more info.
+    type State: DriverConnectorState;
+
+    /// The constructor for creating a [`Connector`] using this [`DriverConnector`] implementation.
+    ///
+    /// Drivers may use this to instantiate their [`DriverConnector`] object.
+    fn new(device: &Device<Self::Driver>, args: Self::Args) -> impl PinInit<Self, Error>;
+
+    /// Retrieve a list of available display modes for this [`Connector`].
+    fn get_modes<'a>(
+        connector: ConnectorGuard<'a, Self>,
+        guard: &ModeConfigGuard<'a, Self::Driver>,
+    ) -> i32;
+}
+
+/// The generated C vtable for a [`DriverConnector`].
+///
+/// This type is created internally by DRM.
+pub struct DriverConnectorOps {
+    funcs: bindings::drm_connector_funcs,
+    helper_funcs: bindings::drm_connector_helper_funcs,
+}
+
+/// The main interface for a [`struct drm_connector`].
+///
+/// This type is the main interface for dealing with DRM connectors. In addition, it also allows
+/// immutable access to whatever private data is contained within an implementor's
+/// [`DriverConnector`] type.
+///
+/// # Invariants
+///
+/// - The DRM C API and our interface guarantees that only the user has mutable access to `state`,
+///   up until [`drm_atomic_helper_commit_hw_done`] is called. Therefore, `connector` follows rust's
+///   data aliasing rules and does not need to be behind an [`Opaque`] type.
+/// - `connector` and `inner` are initialized for as long as this object is made available to users.
+/// - The data layout of this structure begins with [`struct drm_connector`].
+/// - The atomic state for this type can always be assumed to be of type
+///   [`ConnectorState<T::State>`].
+///
+/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h
+/// [`drm_atomic_helper_commit_hw_done`]: srctree/include/drm/drm_atomic_helper.h
+#[repr(C)]
+#[pin_data]
+pub struct Connector<T: DriverConnector> {
+    connector: Opaque<bindings::drm_connector>,
+    #[pin]
+    inner: T,
+    #[pin]
+    _p: PhantomPinned,
+}
+
+impl<T: DriverConnector> Sealed for Connector<T> {}
+
+impl<T: DriverConnector> Deref for Connector<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl<T: DriverConnector> Connector<T> {
+    super::impl_from_opaque_mode_obj! {
+        fn <'a, D>(&'a OpaqueConnector<D>) -> &'a Self;
+        use
+            T as DriverConnector,
+            D as KmsDriver<Connector = ...>
+    }
+
+    /// Acquire a [`ConnectorGuard`] for this connector from a [`ModeConfigGuard`].
+    ///
+    /// This verifies using the provided reference that the given guard is actually for the same
+    /// device as this connector's parent.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `guard` is not a [`ModeConfigGuard`] for this connector's parent [`Device`].
+    pub fn guard<'a>(&'a self, guard: &ModeConfigGuard<'a, T::Driver>) -> ConnectorGuard<'a, T> {
+        guard.assert_owner(self.drm_dev());
+        ConnectorGuard(self)
+    }
+}
+
+/// A trait implemented by any type that acts as a [`struct drm_connector`] interface.
+///
+/// This is implemented internally by DRM.
+///
+/// # Safety
+///
+/// [`as_raw()`] must always return a pointer to a valid initialized [`struct drm_connector`].
+///
+/// [`as_raw()`]: AsRawConnector::as_raw()
+/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h
+pub unsafe trait AsRawConnector {
+    /// Return the raw [`struct drm_connector`] for this DRM connector.
+    ///
+    /// Drivers should never use this directly
+    ///
+    /// [`struct drm_Connector`]: srctree/include/drm/drm_connector.h
+    fn as_raw(&self) -> *mut bindings::drm_connector;
+
+    /// Convert a raw `bindings::drm_connector` pointer into an object of this type.
+    ///
+    /// # Safety
+    ///
+    /// Callers promise that `ptr` points to a valid instance of this type.
+    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a Self;
+}
+
+/// A supertrait of [`AsRawConnector`] for [`struct drm_connector`] interfaces that can perform
+/// modesets.
+///
+/// This is implemented internally by DRM.
+///
+/// # Safety
+///
+/// Any object implementing this trait must only be made directly available to the user after
+/// [`create_objects`] has completed.
+///
+/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h
+/// [`create_objects`]: KmsDriver::create_objects
+pub unsafe trait ModesettableConnector: AsRawConnector {
+    /// The type that should be returned for a plane state acquired using this plane interface
+    type State: FromRawConnectorState;
+}
+
+// SAFETY: Our connector interfaces are guaranteed to be thread-safe
+unsafe impl<T: DriverConnector> Send for Connector<T> {}
+
+// SAFETY: Our connector interfaces are guaranteed to be thread-safe
+unsafe impl<T: DriverConnector> Sync for Connector<T> {}
+
+// SAFETY: We don't expose Connector<T> to users before `base` is initialized in ::new(), so
+// `raw_mode_obj` always returns a valid pointer to a bindings::drm_mode_object.
+unsafe impl<T: DriverConnector> ModeObject for Connector<T> {
+    type Driver = T::Driver;
+
+    fn drm_dev(&self) -> &Device<Self::Driver> {
+        // SAFETY: The parent device for a DRM connector will never outlive the connector, and this
+        // pointer is invariant through the lifetime of the connector
+        unsafe { Device::from_raw((*self.as_raw()).dev) }
+    }
+
+    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
+        // SAFETY: We don't expose DRM connectors to users before `base` is initialized
+        unsafe { &raw mut (*self.as_raw()).base }
+    }
+}
+
+// Connectors are refcounted objects.
+super::impl_aref_for_mode_object! {
+    impl<T: DriverConnector> for Connector<T>
+}
+
+// SAFETY: `funcs` is initialized by DRM when the connector is allocated
+unsafe impl<T: DriverConnector> ModeObjectVtable for Connector<T> {
+    type Vtable = bindings::drm_connector_funcs;
+
+    fn vtable(&self) -> *const Self::Vtable {
+        // SAFETY: `funcs` is initialized by DRM when the connector is allocated
+        unsafe { *self.as_raw() }.funcs
+    }
+}
+
+// SAFETY:
+// * Via our type variants our data layout starts with `drm_connector`
+// * Since we don't expose `Connector` to users before it has been initialized, this and our data
+//   layout ensure that `as_raw()` always returns a valid pointer to a `drm_connector`.
+unsafe impl<T: DriverConnector> AsRawConnector for Connector<T> {
+    fn as_raw(&self) -> *mut bindings::drm_connector {
+        self.connector.get()
+    }
+
+    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a Self {
+        // SAFETY: Our data layout starts with `bindings::drm_connector`
+        unsafe { &*ptr.cast() }
+    }
+}
+
+// SAFETY: We only expose this object to users directly after KmsDriver::create_objects has been
+// called.
+unsafe impl<T: DriverConnector> ModesettableConnector for Connector<T> {
+    type State = ConnectorState<T::State>;
+}
+
+/// A [`Connector`] that has not yet been registered with userspace.
+///
+/// KMS registration is single-threaded, so this object is not thread-safe.
+///
+/// # Invariants
+///
+/// - This object can only exist before its respective KMS device has been registered.
+/// - Otherwise, it inherits all invariants of [`Connector`] and has an identical data layout.
+pub struct UnregisteredConnector<T: DriverConnector>(Connector<T>, NotThreadSafe);
+
+// SAFETY: We share the invariants of `Connector`
+unsafe impl<T: DriverConnector> AsRawConnector for UnregisteredConnector<T> {
+    fn as_raw(&self) -> *mut bindings::drm_connector {
+        self.0.as_raw()
+    }
+
+    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a Self {
+        // SAFETY: This is another from_raw() call, so this function shares the same safety contract
+        let connector = unsafe { Connector::<T>::from_raw(ptr) };
+
+        // SAFETY: Our data layout is identical via our type invariants.
+        unsafe { mem::transmute(connector) }
+    }
+}
+
+impl<T: DriverConnector> Deref for UnregisteredConnector<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0.inner
+    }
+}
+
+impl<T: DriverConnector> UnregisteredConnector<T> {
+    /// Construct a new [`UnregisteredConnector`].
+    ///
+    /// A driver may use this to create new [`UnregisteredConnector`] objects.
+    ///
+    /// [`KmsDriver::create_objects`]: kernel::drm::kms::KmsDriver::create_objects
+    pub fn new<'a>(
+        dev: &'a NewKmsDevice<'a, T::Driver, Probing>,
+        type_: Type,
+        args: T::Args,
+    ) -> Result<&'a Self> {
+        let new: Pin<KBox<Connector<T>>> = KBox::try_pin_init(
+            try_pin_init!(Connector {
+                connector: Opaque::new(bindings::drm_connector {
+                    helper_private: &T::OPS.helper_funcs,
+                    ..Default::default()
+                }),
+                inner <- T::new(dev, args),
+                _p: PhantomPinned,
+            }),
+            GFP_KERNEL,
+        )?;
+
+        // SAFETY:
+        // - `dev` will hold a reference to the new connector, and thus outlives us.
+        // - We just allocated `new` above
+        // - `new` starts with `drm_connector` via its type invariants.
+        to_result(unsafe {
+            bindings::drm_connector_init(dev.as_raw(), new.as_raw(), &T::OPS.funcs, type_ as i32)
+        })?;
+
+        // SAFETY: We don't move anything
+        let this = unsafe { Pin::into_inner_unchecked(new) };
+
+        // We'll re-assemble the box in connector_destroy_callback()
+        let this = KBox::into_raw(this);
+
+        // UnregisteredConnector has an equivalent data layout
+        let this: *mut Self = this.cast();
+
+        // SAFETY: We just allocated the connector above, so this pointer must be valid
+        Ok(unsafe { &*this })
+    }
+
+    /// Attach an encoder to this [`Connector`].
+    #[must_use]
+    pub fn attach_encoder(&self, encoder: &impl AsRawEncoder) -> Result {
+        // SAFETY:
+        // - Both as_raw() calls are guaranteed to return a valid pointer
+        // - We're guaranteed this connector is not registered via our type invariants, thus this
+        //   function is safe to call
+        to_result(unsafe {
+            bindings::drm_connector_attach_encoder(self.as_raw(), encoder.as_raw())
+        })
+    }
+}
+
+/// Common methods available on any type which implements [`AsRawConnector`].
+///
+/// This is implemented internally by DRM, and provides many of the basic methods for working with
+/// connectors.
+pub trait RawConnector: AsRawConnector {
+    /// Return the index of this DRM connector
+    #[inline]
+    fn index(&self) -> u32 {
+        // SAFETY: The index is initialized by the time we expose DRM connector objects to users,
+        // and is invariant throughout the lifetime of the connector
+        unsafe { (*self.as_raw()).index }
+    }
+
+    /// Return the bitmask derived from this DRM connector's index
+    #[inline]
+    fn mask(&self) -> u32 {
+        1 << self.index()
+    }
+}
+impl<T: AsRawConnector> RawConnector for T {}
+
+unsafe extern "C" fn connector_destroy_callback<T: DriverConnector>(
+    connector: *mut bindings::drm_connector,
+) {
+    // SAFETY: DRM guarantees that `connector` points to a valid initialized `drm_connector`.
+    unsafe {
+        bindings::drm_connector_unregister(connector);
+        bindings::drm_connector_cleanup(connector);
+    };
+
+    // SAFETY:
+    // - We originally created the connector in a `Box`
+    // - We are guaranteed to hold the last remaining reference to this connector
+    // - This cast is safe via `DriverConnector`s type invariants.
+    drop(unsafe { KBox::from_raw(connector as *mut Connector<T>) });
+}
+
+unsafe extern "C" fn get_modes_callback<T: DriverConnector>(
+    connector: *mut bindings::drm_connector,
+) -> core::ffi::c_int {
+    // SAFETY: This is safe via `DriverConnector`s type invariants.
+    let connector = unsafe { Connector::<T>::from_raw(connector) };
+
+    // SAFETY: This FFI callback is only called while `mode_config.lock` is held
+    // We use ManuallyDrop here to prevent the lock from being released after the callback
+    // completes, as that should be handled by DRM.
+    let guard = ManuallyDrop::new(unsafe { ModeConfigGuard::new(connector.drm_dev()) });
+
+    T::get_modes(connector.guard(&guard), &guard)
+}
+
+/// A [`struct drm_connector`] without a known [`DriverConnector`] implementation.
+///
+/// This is mainly for situations where our bindings can't infer the [`DriverConnector`]
+/// implementation for a [`struct drm_connector`] automatically. It is identical to [`Connector`],
+/// except that it does not provide access to the driver's private data.
+///
+/// # Invariants
+///
+/// - `connector` is initialized for as long as this object is exposed to users.
+/// - The data layout of this type is equivalent to [`struct drm_connector`].
+///
+/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h
+#[repr(transparent)]
+pub struct OpaqueConnector<T: KmsDriver> {
+    connector: Opaque<bindings::drm_connector>,
+    _p: PhantomData<T>,
+}
+
+impl<T: KmsDriver> Sealed for OpaqueConnector<T> {}
+
+// SAFETY:
+// - Via our type variants our data layout starts is identical to `drm_connector`
+// - Since we don't expose `OpaqueConnector` to users before it has been initialized, this and our
+//   data layout ensure that `as_raw()` always returns a valid pointer to a `drm_connector`.
+unsafe impl<T: KmsDriver> AsRawConnector for OpaqueConnector<T> {
+    fn as_raw(&self) -> *mut bindings::drm_connector {
+        self.connector.get()
+    }
+
+    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a Self {
+        // SAFETY: Our data layout is identical to `bindings::drm_connector`
+        unsafe { &*ptr.cast() }
+    }
+}
+
+// SAFETY: We only expose this object to users directly after KmsDriver::create_objects has been
+// called.
+unsafe impl<T: KmsDriver> ModesettableConnector for OpaqueConnector<T> {
+    type State = OpaqueConnectorState<T>;
+}
+
+// SAFETY: We don't expose OpaqueConnector<T> to users before `base` is initialized in
+// Connector::new(), so `raw_mode_obj` always returns a valid pointer to a bindings::drm_mode_object.
+unsafe impl<T: KmsDriver> ModeObject for OpaqueConnector<T> {
+    type Driver = T;
+
+    fn drm_dev(&self) -> &Device<Self::Driver> {
+        // SAFETY: The parent device for a DRM connector will never outlive the connector, and this
+        // pointer is invariant through the lifetime of the connector
+        unsafe { Device::from_raw((*self.as_raw()).dev) }
+    }
+
+    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
+        // SAFETY: We don't expose DRM connectors to users before `base` is initialized
+        unsafe { &mut (*self.as_raw()).base }
+    }
+}
+
+super::impl_aref_for_mode_object! {
+    impl<T: KmsDriver> for OpaqueConnector<T>
+}
+
+// SAFETY: `funcs` is initialized by DRM when the connector is allocated
+unsafe impl<T: KmsDriver> ModeObjectVtable for OpaqueConnector<T> {
+    type Vtable = bindings::drm_connector_funcs;
+
+    fn vtable(&self) -> *const Self::Vtable {
+        // SAFETY: `funcs` is initialized by DRM when the connector is allocated
+        unsafe { *self.as_raw() }.funcs
+    }
+}
+
+// SAFETY: Our connector interfaces are guaranteed to be thread-safe
+unsafe impl<T: KmsDriver> Send for OpaqueConnector<T> {}
+unsafe impl<T: KmsDriver> Sync for OpaqueConnector<T> {}
+
+/// A privileged [`Connector`] obtained while holding a [`ModeConfigGuard`].
+///
+/// This provides access to various methods for [`Connector`] that must happen under lock, such as
+/// setting resolution preferences and adding display modes.
+///
+/// # Invariants
+///
+/// Shares the invariants of [`ModeConfigGuard`].
+#[derive(Copy, Clone)]
+pub struct ConnectorGuard<'a, T: DriverConnector>(&'a Connector<T>);
+
+impl<T: DriverConnector> Deref for ConnectorGuard<'_, T> {
+    type Target = Connector<T>;
+
+    fn deref(&self) -> &Self::Target {
+        self.0
+    }
+}
+
+impl<'a, T: DriverConnector> ConnectorGuard<'a, T> {
+    /// Add modes for a [`ConnectorGuard`] without an EDID.
+    ///
+    /// Add the specified modes to the connector's mode list up to the given maximum resultion.
+    /// Returns how many modes were added.
+    pub fn add_modes_noedid(&self, (max_h, max_v): (u32, u32)) -> i32 {
+        // SAFETY: We hold the locks required to call this via our type invariants.
+        unsafe { bindings::drm_add_modes_noedid(self.as_raw(), max_h, max_v) }
+    }
+
+    /// Set the preferred display mode for the underlying [`Connector`].
+    pub fn set_preferred_mode(&self, (h_pref, w_pref): (u32, u32)) {
+        // SAFETY: We hold the locks required to call this via our type invariants.
+        unsafe { bindings::drm_set_preferred_mode(self.as_raw(), h_pref, w_pref) }
+    }
+}
+
+/// A trait implemented by any type which can produce a reference to a
+/// [`struct drm_connector_state`].
+///
+/// This is implemented internally by DRM.
+///
+/// [`struct drm_connector_state`]: srctree/include/drm/drm_connector.h
+pub trait AsRawConnectorState: private::AsRawConnectorState {
+    /// The type that represents this connector state's DRM connector.
+    type Connector: AsRawConnector;
+}
+
+pub(super) mod private {
+    use super::*;
+
+    /// Trait for retrieving references to the base connector state contained within any connector
+    /// state compatible type
+    #[allow(unreachable_pub)]
+    pub trait AsRawConnectorState {
+        /// Return an immutable reference to the raw connector state.
+        fn as_raw(&self) -> &bindings::drm_connector_state;
+
+        /// Get a mutable reference to the raw [`struct drm_connector_state`] contained within this
+        /// type.
+        ///
+        ///
+        /// # Safety
+        ///
+        /// The caller promises this mutable reference will not be used to modify any contents of
+        /// [`struct drm_connector_state`] which DRM would consider to be static - like the
+        /// backpointer to the DRM connector that owns this state. This also means the mutable
+        /// reference should never be exposed outside of this crate.
+        ///
+        /// [`struct drm_connector_state`]: srctree/include/drm/drm_connector.h
+        unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_connector_state;
+    }
+}
+
+pub(super) use private::AsRawConnectorState as AsRawConnectorStatePrivate;
+
+/// A trait implemented for any type which can be constructed directly from a
+/// [`struct drm_connector_state`] pointer.
+///
+/// This is implemented internally by DRM.
+///
+/// [`struct drm_connector_state`]: srctree/include/drm/drm_connector.h
+pub trait FromRawConnectorState: AsRawConnectorState {
+    /// Get an immutable reference to this type from the given raw [`struct drm_connector_state`]
+    /// pointer.
+    ///
+    /// # Safety
+    ///
+    /// - The caller guarantees `ptr` is contained within a valid instance of `Self`.
+    /// - The caller guarantees that `ptr` cannot not be modified for the lifetime of `'a`.
+    ///
+    /// [`struct drm_connector_state`]: srctree/include/drm/drm_connector.h
+    unsafe fn from_raw<'a>(ptr: *const bindings::drm_connector_state) -> &'a Self;
+
+    /// Get a mutable reference to this type from the given raw [`struct drm_connector_state`]
+    /// pointer.
+    ///
+    /// # Safety
+    ///
+    /// - The caller guarantees that `ptr` is contained within a valid instance of `Self`.
+    /// - The caller guarantees that `ptr` cannot have any other references taken out for the
+    ///   lifetime of `'a`.
+    ///
+    /// [`struct drm_connector_state`]: srctree/include/drm/drm_connector.h
+    unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_connector_state) -> &'a mut Self;
+}
+
+/// Common methods available on any type which implements [`AsRawConnectorState`].
+///
+/// This is implemented internally by DRM, and provides many of the basic methods for working with
+/// the atomic state of [`Connector`]s.
+pub trait RawConnectorState: AsRawConnectorState {
+    /// Return the connector that this atomic state belongs to.
+    fn connector(&self) -> &Self::Connector {
+        // SAFETY: This is guaranteed safe by type invariance, and we're guaranteed by DRM that
+        // `self.state.connector` points to a valid instance of a `Connector<T>`
+        unsafe { Self::Connector::from_raw((*self.as_raw()).connector) }
+    }
+}
+impl<T: AsRawConnectorState> RawConnectorState for T {}
+
+/// The main interface for a [`struct drm_connector_state`].
+///
+/// This type is the main interface for dealing with the atomic state of DRM connectors. In
+/// addition, it allows access to whatever private data is contained within an implementor's
+/// [`DriverConnectorState`] type.
+///
+/// # Invariants
+///
+/// - The DRM C API and our interface guarantees that only the user has mutable access to `state`,
+///   up until [`drm_atomic_helper_commit_hw_done`] is called. Therefore, `connector` follows rust's
+///   data aliasing rules and does not need to be behind an [`Opaque`] type.
+/// - `state` and `inner` initialized for as long as this object is exposed to users.
+/// - The data layout of this structure begins with [`struct drm_connector_state`].
+/// - The connector for this atomic state can always be assumed to be of type
+///   [`Connector<T::Connector>`].
+///
+/// [`struct drm_connector_state`]: srctree/include/drm/drm_connector.h
+/// [`drm_atomic_helper_commit_hw_done`]: srctree/include/drm/drm_atomic_helper.h
+#[derive(Default)]
+#[repr(C)]
+pub struct ConnectorState<T: DriverConnectorState> {
+    state: bindings::drm_connector_state,
+    inner: T,
+}
+
+/// The main trait for implementing the [`struct drm_connector_state`] API for a [`Connector`].
+///
+/// A driver may store driver-private data within the implementor's type, which will be available
+/// when using a full typed [`ConnectorState`] object.
+///
+/// # Invariants
+///
+/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in
+///   [`struct drm_connector`] pointers are contained within a [`Connector<Self::Connector>`].
+/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in
+///   [`struct drm_connector_state`] pointers are contained within a [`ConnectorState<Self>`].
+///
+/// [`struct drm_connector`]: srctree/include/drm_connector.h
+/// [`struct drm_connector_state`]: srctree/include/drm_connector.h
+pub trait DriverConnectorState: Clone + Default + Sized {
+    /// The parent [`DriverConnector`].
+    type Connector: DriverConnector;
+}
+
+impl<T: DriverConnectorState> Sealed for ConnectorState<T> {}
+
+impl<T: DriverConnectorState> AsRawConnectorState for ConnectorState<T> {
+    type Connector = Connector<T::Connector>;
+}
+
+impl<T: DriverConnectorState> private::AsRawConnectorState for ConnectorState<T> {
+    fn as_raw(&self) -> &bindings::drm_connector_state {
+        &self.state
+    }
+
+    unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_connector_state {
+        &mut self.state
+    }
+}
+
+impl<T: DriverConnectorState> FromRawConnectorState for ConnectorState<T> {
+    unsafe fn from_raw<'a>(ptr: *const bindings::drm_connector_state) -> &'a Self {
+        // Our data layout starts with `bindings::drm_connector_state`.
+        let ptr: *const Self = ptr.cast();
+
+        // SAFETY:
+        // - Our safety contract requires that `ptr` be contained within `Self`.
+        // - Our safety contract requires the caller ensure that it is safe for us to take an
+        //   immutable reference.
+        unsafe { &*ptr }
+    }
+
+    unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_connector_state) -> &'a mut Self {
+        // Our data layout starts with `bindings::drm_connector_state`.
+        let ptr: *mut Self = ptr.cast();
+
+        // SAFETY:
+        // - Our safety contract requires that `ptr` be contained within `Self`.
+        // - Our safety contract requires the caller ensure it is safe for us to take a mutable
+        //   reference.
+        unsafe { &mut *ptr }
+    }
+}
+
+// SAFETY: `funcs` is initialized by DRM when the connector is allocated
+unsafe impl<T: DriverConnectorState> ModeObjectVtable for ConnectorState<T> {
+    type Vtable = bindings::drm_connector_funcs;
+
+    fn vtable(&self) -> *const Self::Vtable {
+        self.connector().vtable()
+    }
+}
+
+impl<T: DriverConnectorState> ConnectorState<T> {
+    super::impl_from_opaque_mode_obj! {
+        fn <'a, D, C>(&'a OpaqueConnectorState<D>) -> &'a Self
+        where
+            T: DriverConnectorState<Connector = C>;
+        use
+            C as DriverConnector,
+            D as KmsDriver<Connector = ...>
+    }
+}
+
+/// A [`struct drm_connector_state`] without a known [`DriverConnectorState`] implementation.
+///
+/// This is mainly for situations where our bindings can't infer the [`DriverConnectorState`]
+/// implementation for a [`struct drm_connector_state`] automatically. It is identical to
+/// [`Connector`], except that it does not provide access to the driver's private data.
+///
+/// # Invariants
+///
+/// - `state` is initialized for as long as this object is exposed to users.
+/// - The data layout of this type is identical to [`struct drm_connector_state`].
+/// - The DRM C API and our interface guarantees that only the user has mutable access to `state`,
+///   up until [`drm_atomic_helper_commit_hw_done`] is called. Therefore, `connector` follows rust's
+///   data aliasing rules and does not need to be behind an [`Opaque`] type.
+///
+/// [`struct drm_connector_state`]: srctree/include/drm/drm_connector.h
+/// [`drm_atomic_helper_commit_hw_done`]: srctree/include/drm/drm_atomic_helper.h
+#[repr(transparent)]
+pub struct OpaqueConnectorState<T: KmsDriver> {
+    state: bindings::drm_connector_state,
+    _p: PhantomData<T>,
+}
+
+impl<T: KmsDriver> AsRawConnectorState for OpaqueConnectorState<T> {
+    type Connector = OpaqueConnector<T>;
+}
+
+impl<T: KmsDriver> private::AsRawConnectorState for OpaqueConnectorState<T> {
+    fn as_raw(&self) -> &bindings::drm_connector_state {
+        &self.state
+    }
+
+    unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_connector_state {
+        &mut self.state
+    }
+}
+
+impl<T: KmsDriver> FromRawConnectorState for OpaqueConnectorState<T> {
+    unsafe fn from_raw<'a>(ptr: *const bindings::drm_connector_state) -> &'a Self {
+        // SAFETY: Our data layout is identical to `bindings::drm_connector_state`
+        unsafe { &*ptr.cast() }
+    }
+
+    unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_connector_state) -> &'a mut Self {
+        // SAFETY: Our data layout is identical to `bindings::drm_connector_state`
+        unsafe { &mut *ptr.cast() }
+    }
+}
+
+// SAFETY: See OpaqueConnector's ModeObjectVtable implementation
+unsafe impl<T: KmsDriver> ModeObjectVtable for OpaqueConnectorState<T> {
+    type Vtable = bindings::drm_connector_funcs;
+
+    fn vtable(&self) -> *const Self::Vtable {
+        self.connector().vtable()
+    }
+}
+
+/// An interface for mutating a [`Connector`]s atomic state.
+///
+/// This type is typically returned by an [`AtomicStateMutator`] within contexts where it is
+/// possible to safely mutate a connector's state. In order to uphold rust's data-aliasing rules,
+/// only [`ConnectorStateMutator`] may exist at a time.
+pub struct ConnectorStateMutator<'a, T: FromRawConnectorState> {
+    state: &'a mut T,
+    mask: &'a Cell<u32>,
+}
+
+impl<'a, T: FromRawConnectorState> ConnectorStateMutator<'a, T> {
+    pub(super) fn new<D: KmsDriver>(
+        mutator: &'a AtomicStateMutator<D>,
+        state: NonNull<bindings::drm_connector_state>,
+    ) -> Option<Self> {
+        // SAFETY:
+        // - `connector` is invariant throughout the lifetime of the atomic state.
+        // - `state` is initialized by the time it is passed to this function.
+        // - We're guaranteed that `state` is compatible with `drm_connector` by type invariants.
+        let connector = unsafe { T::Connector::from_raw((*state.as_ptr()).connector) };
+        let conn_mask = connector.mask();
+        let borrowed_mask = mutator.borrowed_connectors.get();
+
+        if borrowed_mask & conn_mask == 0 {
+            mutator.borrowed_connectors.set(borrowed_mask | conn_mask);
+            Some(Self {
+                mask: &mutator.borrowed_connectors,
+                // SAFETY: We're guaranteed `state` is of `T` by type invariance, and we just
+                // confirmed by checking `borrowed_connectors` that no other mutable borrows have
+                // been taken out for `state`
+                state: unsafe { T::from_raw_mut(state.as_ptr()) },
+            })
+        } else {
+            None
+        }
+    }
+}
+
+impl<'a, T: DriverConnectorState> Deref for ConnectorStateMutator<'a, ConnectorState<T>> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.state.inner
+    }
+}
+
+impl<'a, T: DriverConnectorState> DerefMut for ConnectorStateMutator<'a, ConnectorState<T>> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.state.inner
+    }
+}
+
+impl<'a, T: FromRawConnectorState> Drop for ConnectorStateMutator<'a, T> {
+    fn drop(&mut self) {
+        let mask = self.state.connector().mask();
+        self.mask.set(self.mask.get() & !mask);
+    }
+}
+
+impl<'a, T: FromRawConnectorState> AsRawConnectorState for ConnectorStateMutator<'a, T> {
+    type Connector = T::Connector;
+}
+
+impl<'a, T: FromRawConnectorState> private::AsRawConnectorState for ConnectorStateMutator<'a, T> {
+    fn as_raw(&self) -> &bindings::drm_connector_state {
+        self.state.as_raw()
+    }
+
+    unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_connector_state {
+        // SAFETY: We're bound by the same safety contract as this function
+        unsafe { self.state.as_raw_mut() }
+    }
+}
+
+// SAFETY: we inherit the safety guarantees of `T`
+unsafe impl<'a, T> ModeObjectVtable for ConnectorStateMutator<'a, T>
+where
+    T: FromRawConnectorState + ModeObjectVtable,
+{
+    type Vtable = T::Vtable;
+
+    fn vtable(&self) -> *const Self::Vtable {
+        self.state.vtable()
+    }
+}
+
+impl<'a, T: DriverConnectorState> ConnectorStateMutator<'a, ConnectorState<T>> {
+    super::impl_from_opaque_mode_obj! {
+        fn <D, C>(ConnectorStateMutator<'a, OpaqueConnectorState<D>>) -> Self
+        where
+            T: DriverConnectorState<Connector = C>;
+        use
+            C as DriverConnector,
+            D as KmsDriver<Connector = ...>
+    }
+}
+
+unsafe extern "C" fn atomic_duplicate_state_callback<T: DriverConnectorState>(
+    connector: *mut bindings::drm_connector,
+) -> *mut bindings::drm_connector_state {
+    // SAFETY: DRM guarantees that `connector` points to a valid initialized `drm_connector`.
+    let state = unsafe { (*connector).state };
+    if state.is_null() {
+        return null_mut();
+    }
+
+    // SAFETY:
+    // - We just verified that `state` is non-null
+    // - This cast is guaranteed to be safe via our type invariants.
+    let state = unsafe { ConnectorState::<T>::from_raw(state) };
+
+    let new: Result<KBox<_>> = KBox::init(
+        init!(ConnectorState::<T> {
+            inner: state.inner.clone(),
+            state: bindings::drm_connector_state {
+                ..Default::default()
+            },
+        }),
+        GFP_KERNEL,
+    );
+
+    if let Ok(mut new) = new {
+        // SAFETY:
+        // - `new` provides a valid pointer to a newly allocated `drm_plane_state` via type
+        //   invariants
+        // - This initializes `new` via memcpy()
+        unsafe {
+            bindings::__drm_atomic_helper_connector_duplicate_state(connector, new.as_raw_mut())
+        };
+
+        KBox::into_raw(new).cast()
+    } else {
+        null_mut()
+    }
+}
+
+unsafe extern "C" fn atomic_destroy_state_callback<T: DriverConnectorState>(
+    _connector: *mut bindings::drm_connector,
+    connector_state: *mut bindings::drm_connector_state,
+) {
+    // SAFETY: DRM guarantees that `state` points to a valid instance of `drm_connector_state`
+    unsafe { bindings::__drm_atomic_helper_connector_destroy_state(connector_state) };
+
+    // SAFETY:
+    // - DRM guarantees we are the only one with access to this `drm_connector_state`
+    // - This cast is safe via our type invariants.
+    drop(unsafe { KBox::from_raw(connector_state.cast::<ConnectorState<T>>()) });
+}
+
+unsafe extern "C" fn connector_reset_callback<T: DriverConnectorState>(
+    connector: *mut bindings::drm_connector,
+) {
+    // SAFETY: DRM guarantees that `state` points to a valid instance of `drm_connector_state`
+    let state = unsafe { (*connector).state };
+    if !state.is_null() {
+        // SAFETY:
+        // - We're guaranteed `connector` is `Connector<T>` via type invariants
+        // - We're guaranteed `state` is `ConnectorState<T>` via type invariants.
+        unsafe { atomic_destroy_state_callback::<T>(connector, state) }
+
+        // SAFETY: No special requirements here, DRM expects this to be NULL
+        unsafe { (*connector).state = null_mut() };
+    }
+
+    // Unfortunately, this is the best we can do at the moment as this FFI callback was mistakenly
+    // presumed to be infallible :(
+    let new = KBox::new(ConnectorState::<T>::default(), GFP_KERNEL).expect("Blame the API, sorry!");
+
+    // DRM takes ownership of the state from here, resets it, and then assigns it to the connector
+    // SAFETY:
+    // - DRM guarantees that `connector` points to a valid instance of `drm_connector`.
+    // - The cast to `drm_connector_state` is safe via `ConnectorState`s type invariants.
+    unsafe { bindings::__drm_atomic_helper_connector_reset(connector, Box::into_raw(new).cast()) };
+}
diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs
new file mode 100644
index 000000000000..b9d095854ba6
--- /dev/null
+++ b/rust/kernel/drm/kms/crtc.rs
@@ -0,0 +1,1110 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! DRM CRTCs.
+//!
+//! C header: [`include/drm/drm_crtc.h`](srctree/include/drm/drm_crtc.h)
+
+use super::{
+    atomic::*, plane::*, vblank::*, KmsDriver, ModeObject, ModeObjectVtable, StaticModeObject,
+    NewKmsDevice, Probing, Sealed,
+};
+use crate::{
+    alloc::KBox,
+    bindings,
+    drm::device::Device,
+    error::{from_result, to_result},
+    prelude::*,
+    types::{NotThreadSafe, Opaque},
+};
+use core::{
+    cell::{Cell, UnsafeCell},
+    marker::*,
+    mem::{self, ManuallyDrop},
+    ops::{Deref, DerefMut},
+    ptr::{null, null_mut, NonNull},
+};
+use macros::vtable;
+
+/// The main trait for implementing the [`struct drm_crtc`] API for [`Crtc`].
+///
+/// Any KMS driver should have at least one implementation of this type, which allows them to create
+/// [`Crtc`] objects. Additionally, a driver may store driver-private data within the type that
+/// implements [`DriverCrtc`] - and it will be made available when using a fully typed [`Crtc`]
+/// object.
+///
+/// # Invariants
+///
+/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in
+///   [`struct drm_crtc`] pointers are contained within a [`Crtc<Self>`].
+/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in
+///   [`struct drm_crtc_state`] pointers are contained within a [`CrtcState<Self::State>`].
+///
+/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
+/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h
+#[vtable]
+pub trait DriverCrtc: Send + Sync + Sized {
+    /// The generated C vtable for this [`DriverCrtc`] implementation.
+    const OPS: &'static DriverCrtcOps = &DriverCrtcOps {
+        funcs: bindings::drm_crtc_funcs {
+            atomic_destroy_state: Some(atomic_destroy_state_callback::<Self::State>),
+            atomic_duplicate_state: Some(atomic_duplicate_state_callback::<Self::State>),
+            atomic_get_property: None,
+            atomic_print_state: None,
+            atomic_set_property: None,
+            cursor_move: None,
+            cursor_set2: None,
+            cursor_set: None,
+            destroy: Some(crtc_destroy_callback::<Self>),
+            disable_vblank: <Self::VblankImpl as VblankImpl>::VBLANK_OPS.disable_vblank,
+            early_unregister: None,
+            enable_vblank: <Self::VblankImpl as VblankImpl>::VBLANK_OPS.enable_vblank,
+            gamma_set: None,
+            get_crc_sources: None,
+            get_vblank_counter: None,
+            get_vblank_timestamp: <Self::VblankImpl as VblankImpl>::VBLANK_OPS.get_vblank_timestamp,
+            late_register: None,
+            page_flip: Some(bindings::drm_atomic_helper_page_flip),
+            page_flip_target: None,
+            reset: Some(crtc_reset_callback::<Self::State>),
+            set_config: Some(bindings::drm_atomic_helper_set_config),
+            set_crc_source: None,
+            set_property: None,
+            verify_crc_source: None,
+        },
+
+        helper_funcs: bindings::drm_crtc_helper_funcs {
+            atomic_disable: if Self::HAS_ATOMIC_DISABLE {
+                Some(atomic_disable_callback::<Self>)
+            } else {
+                None
+            },
+            atomic_enable: if Self::HAS_ATOMIC_ENABLE {
+                Some(atomic_enable_callback::<Self>)
+            } else {
+                None
+            },
+            atomic_check: if Self::HAS_ATOMIC_CHECK {
+                Some(atomic_check_callback::<Self>)
+            } else {
+                None
+            },
+            dpms: None,
+            commit: None,
+            prepare: None,
+            disable: None,
+            mode_set: None,
+            mode_valid: None,
+            mode_fixup: None,
+            atomic_begin: if Self::HAS_ATOMIC_BEGIN {
+                Some(atomic_begin_callback::<Self>)
+            } else {
+                None
+            },
+            atomic_flush: if Self::HAS_ATOMIC_FLUSH {
+                Some(atomic_flush_callback::<Self>)
+            } else {
+                None
+            },
+            mode_set_nofb: None,
+            mode_set_base: None,
+            mode_set_base_atomic: None,
+            get_scanout_position: None,
+        },
+    };
+
+    /// The type to pass to the `args` field of [`UnregisteredCrtc::new`].
+    ///
+    /// This type will be made available in in the `args` argument of [`Self::new`]. Drivers which
+    /// don't need this can simply pass [`()`] here.
+    type Args;
+
+    /// The parent [`KmsDriver`] implementation.
+    type Driver: KmsDriver;
+
+    /// The [`DriverCrtcState`] implementation for this [`DriverCrtc`].
+    ///
+    /// See [`DriverCrtcState`] for more info.
+    type State: DriverCrtcState;
+
+    /// The driver's optional hardware vblank implementation
+    ///
+    /// See [`VblankSupport`] for more info. Drivers that don't care about this can just pass
+    /// [`PhantomData<Self>`].
+    type VblankImpl: VblankImpl<Crtc = Self>;
+
+    /// The constructor for creating a [`Crtc`] using this [`DriverCrtc`] implementation.
+    ///
+    /// Drivers may use this to instantiate their [`DriverCrtc`] object.
+    fn new(device: &Device<Self::Driver>, args: &Self::Args) -> impl PinInit<Self, Error>;
+
+    /// The optional [`drm_crtc_helper_funcs.atomic_check`] hook for this crtc.
+    ///
+    /// Drivers may use this to customize the atomic check phase of their [`Crtc`] objects. The
+    /// result of this function determines whether the atomic check passed or failed.
+    ///
+    /// [`drm_crtc_helper_funcs.atomic_check`]: srctree/include/drm/drm_modeset_helper_vtables.h
+    fn atomic_check(_check: CrtcAtomicCheck<'_, Self>) -> Result {
+        build_error::build_error("This should not be reachable")
+    }
+
+    /// The optional [`drm_crtc_helper_funcs.atomic_begin`] hook.
+    ///
+    /// This hook will be called before a set of [`Plane`] updates are performed for the given
+    /// [`Crtc`].
+    ///
+    /// [`drm_crtc_helper_funcs.atomic_begin`]: srctree/include/drm/drm_modeset_helper_vtables.h
+    fn atomic_begin(_commit: CrtcAtomicCommit<'_, Self>) {
+        build_error::build_error("This should not be reachable")
+    }
+
+    /// The optional [`drm_crtc_helper_funcs.atomic_flush`] hook.
+    ///
+    /// This hook will be called after a set of [`Plane`] updates are performed for the given
+    /// [`Crtc`].
+    ///
+    /// [`drm_crtc_helper_funcs.atomic_flush`]: srctree/include/drm/drm_modeset_helper_vtables.h
+    fn atomic_flush(_commit: CrtcAtomicCommit<'_, Self>) {
+        build_error::build_error("This should never be reachable")
+    }
+
+    /// The optional [`drm_crtc_helper_funcs.atomic_enable`] hook.
+    ///
+    /// This hook will be called before enabling a [`Crtc`] in an atomic commit.
+    ///
+    /// [`drm_crtc_helper_funcs.atomic_enable`]: srctree/include/drm/drm_modeset_helper_vtables.h
+    fn atomic_enable(_commit: CrtcAtomicCommit<'_, Self>) {
+        build_error::build_error("This should never be reachable")
+    }
+
+    /// The optional [`drm_crtc_helper_funcs.atomic_disable`] hook.
+    ///
+    /// This hook will be called before disabling a [`Crtc`] in an atomic commit.
+    ///
+    /// [`drm_crtc_helper_funcs.atomic_disable`]: srctree/include/drm/drm_modeset_helper_vtables.h
+    fn atomic_disable(_commit: CrtcAtomicCommit<'_, Self>) {
+        build_error::build_error("This should never be reachable")
+    }
+}
+
+/// The generated C vtable for a [`DriverCrtc`].
+///
+/// This type is created internally by DRM.
+pub struct DriverCrtcOps {
+    funcs: bindings::drm_crtc_funcs,
+    helper_funcs: bindings::drm_crtc_helper_funcs,
+}
+
+/// The main interface for a [`struct drm_crtc`].
+///
+/// This type is the main interface for dealing with DRM CRTCs. In addition, it also allows
+/// immutable access to whatever private data is contained within an implementor's [`DriverCrtc`]
+/// type.
+///
+/// # Invariants
+///
+/// - `crtc` and `inner` are initialized for as long as this object is made available to users.
+/// - The data layout of this structure begins with [`struct drm_crtc`].
+/// - The atomic state for this type can always be assumed to be of type [`CrtcState<T::State>`].
+///
+/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
+#[repr(C)]
+#[pin_data]
+pub struct Crtc<T: DriverCrtc> {
+    // The FFI drm_crtc object
+    crtc: Opaque<bindings::drm_crtc>,
+    /// The driver's private inner data
+    #[pin]
+    inner: T,
+    #[pin]
+    _p: PhantomPinned,
+}
+
+impl<T: DriverCrtc> Sealed for Crtc<T> {}
+
+// SAFETY: Our CRTC interfaces are guaranteed to be thread-safe
+unsafe impl<T: DriverCrtc> Send for Crtc<T> {}
+
+// SAFETY: Our CRTC interfaces are guaranteed to be thread-safe
+unsafe impl<T: DriverCrtc> Sync for Crtc<T> {}
+
+impl<T: DriverCrtc> Deref for Crtc<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+// SAFETY: We don't expose Crtc<T> to users before `base` is initialized in ::new(), so
+// `raw_mode_obj` always returns a valid pointer to a bindings::drm_mode_object.
+unsafe impl<T: DriverCrtc> ModeObject for Crtc<T> {
+    type Driver = T::Driver;
+
+    fn drm_dev(&self) -> &Device<Self::Driver> {
+        // SAFETY: DRM connectors exist for as long as the device does, so this pointer is always
+        // valid
+        unsafe { Device::from_raw((*self.as_raw()).dev) }
+    }
+
+    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
+        // SAFETY: We don't expose Crtc<T> to users before it's initialized, so `base` is always
+        // initialized
+        unsafe { &raw mut (*self.as_raw()).base }
+    }
+}
+
+// SAFETY: CRTCs are non-refcounted modesetting objects
+unsafe impl<T: DriverCrtc> StaticModeObject for Crtc<T> {}
+
+// SAFETY: `funcs` is initialized when the crtc is allocated
+unsafe impl<T: DriverCrtc> ModeObjectVtable for Crtc<T> {
+    type Vtable = bindings::drm_crtc_funcs;
+
+    fn vtable(&self) -> *const Self::Vtable {
+        // SAFETY: `as_raw()` always returns a valid pointer to a CRTC
+        unsafe { *self.as_raw() }.funcs
+    }
+}
+
+impl<T: DriverCrtc> Crtc<T> {
+    super::impl_from_opaque_mode_obj! {
+        fn <'a, D>(&'a OpaqueCrtc<D>) -> &'a Self;
+        use
+            T as DriverCrtc,
+            D as KmsDriver<Crtc = ...>
+    }
+
+    pub(crate) fn get_vblank_ptr(&self) -> *mut bindings::drm_vblank_crtc {
+        // SAFETY: FFI Call with no special requirements
+        unsafe { bindings::drm_crtc_vblank_crtc(self.as_raw()) }
+    }
+
+    pub(crate) const fn has_vblank() -> bool {
+        T::OPS.funcs.enable_vblank.is_some()
+    }
+}
+
+/// A [`Crtc`] that has not yet been registered with userspace.
+///
+/// KMS registration is single-threaded, so this object is not thread-safe.
+///
+/// # Invariants
+///
+/// - This object can only exist before its respective KMS device has been registered.
+/// - Otherwise, it inherits all invariants of [`Crtc`] and has an identical data layout.
+pub struct UnregisteredCrtc<T: DriverCrtc>(Crtc<T>, NotThreadSafe);
+
+impl<T: DriverCrtc> UnregisteredCrtc<T> {
+    /// Construct a new [`UnregisteredCrtc`].
+    ///
+    /// A driver may use this from their [`KmsDriver::create_objects`] callback in order to
+    /// construct new [`UnregisteredCrtc`] objects.
+    ///
+    /// [`KmsDriver::create_objects`]: kernel::drm::kms::KmsDriver::create_objects
+    pub fn new<'a, 'b: 'a, PrimaryData, CursorData>(
+        dev: &'a NewKmsDevice<'a, T::Driver, Probing>,
+        primary: &'a UnregisteredPlane<PrimaryData>,
+        cursor: Option<&'a UnregisteredPlane<CursorData>>,
+        name: Option<&CStr>,
+        args: T::Args,
+    ) -> Result<&'a Self>
+    where
+        PrimaryData: DriverPlane<Driver = T::Driver>,
+        CursorData: DriverPlane<Driver = T::Driver>,
+    {
+        if Crtc::<T>::has_vblank() {
+            dev.has_vblanks.set(true)
+        }
+
+        let this: Pin<KBox<Crtc<T>>> = KBox::try_pin_init(
+            try_pin_init!(Crtc {
+                crtc: Opaque::new(bindings::drm_crtc {
+                    helper_private: &T::OPS.helper_funcs,
+                    ..Default::default()
+                }),
+                inner <- T::new(dev, &args),
+                _p: PhantomPinned,
+            }),
+            GFP_KERNEL,
+        )?;
+
+        // SAFETY:
+        // - `dev` handles destroying the CRTC and thus will outlive us.
+        // - We just allocated `this`, and we won't move it since it's pinned
+        // - `primary` and `cursor` share the lifetime 'a with `dev`
+        // - This function will memcpy the contents of `name` into its own storage.
+        to_result(unsafe {
+            bindings::drm_crtc_init_with_planes(
+                dev.as_raw(),
+                this.as_raw(),
+                primary.as_raw(),
+                cursor.map_or(null_mut(), |c| c.as_raw()),
+                &T::OPS.funcs,
+                name.map_or(null(), |n| n.as_char_ptr()),
+            )
+        })?;
+
+        // SAFETY: We don't move anything
+        let this = unsafe { Pin::into_inner_unchecked(this) };
+
+        // We'll re-assemble the box in crtc_destroy_callback()
+        let this = KBox::into_raw(this);
+
+        // UnregisteredCrtc has an equivalent data layout
+        let this: *mut Self = this.cast();
+
+        // SAFETY: We just allocated the crtc above, so this pointer must be valid
+        Ok(unsafe { &*this })
+    }
+}
+
+// SAFETY: We inherit all relevant invariants of `Crtc`
+unsafe impl<T: DriverCrtc> AsRawCrtc for UnregisteredCrtc<T> {
+    fn as_raw(&self) -> *mut bindings::drm_crtc {
+        self.0.as_raw()
+    }
+
+    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) -> &'a Self {
+        // SAFETY: This is another from_raw() call, so this function shares the same safety contract
+        let crtc = unsafe { Crtc::<T>::from_raw(ptr) };
+
+        // SAFETY: Our data layout is identical via our type invariants.
+        unsafe { mem::transmute(crtc) }
+    }
+}
+
+impl<T: DriverCrtc> Deref for UnregisteredCrtc<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0.inner
+    }
+}
+
+/// A trait implemented by any type that acts as a [`struct drm_crtc`] interface.
+///
+/// This is implemented internally by DRM.
+///
+/// # Safety
+///
+/// [`as_raw()`] must always return a valid pointer to a [`struct drm_crtc`].
+///
+/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
+/// [`as_raw()`]: AsRawCrtc::as_raw()
+pub unsafe trait AsRawCrtc {
+    /// Return a raw pointer to the `bindings::drm_crtc` for this object
+    fn as_raw(&self) -> *mut bindings::drm_crtc;
+
+    /// Convert a raw [`struct drm_crtc`] pointer into an object of this type.
+    ///
+    /// # Safety
+    ///
+    /// Callers promise that `ptr` points to a valid instance of this type
+    ///
+    /// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
+    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) -> &'a Self;
+}
+
+// SAFETY:
+// - Via our type variants our data layout starts with `drm_crtc`
+// - Since we don't expose `crtc` to users before it has been initialized, this and our data
+//   layout ensure that `as_raw()` always returns a valid pointer to a `drm_crtc`.
+unsafe impl<T: DriverCrtc> AsRawCrtc for Crtc<T> {
+    fn as_raw(&self) -> *mut bindings::drm_crtc {
+        self.crtc.get()
+    }
+
+    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) -> &'a Self {
+        // Our data layout start with `bindings::drm_crtc`.
+        let ptr: *mut Self = ptr.cast();
+
+        // SAFETY: Our safety contract requires that `ptr` point to a valid intance of `Self`.
+        unsafe { &*ptr }
+    }
+}
+
+// SAFETY: We only expose this object to users directly after KmsDriver::create_objects has been
+// called.
+unsafe impl<T: DriverCrtc> ModesettableCrtc for Crtc<T> {
+    type State = CrtcState<T::State>;
+}
+
+/// A supertrait of [`AsRawCrtc`] for [`struct drm_crtc`] interfaces that can perform modesets.
+///
+/// This is implemented internally by DRM.
+///
+/// # Safety
+///
+/// Any object implementing this trait must only be made directly available to the user after
+/// [`create_objects`] has completed.
+///
+/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
+/// [`create_objects`]: KmsDriver::create_objects
+pub unsafe trait ModesettableCrtc: AsRawCrtc {
+    /// The type that should be returned for a CRTC state acquired using this CRTC interface
+    type State: FromRawCrtcState;
+}
+
+/// Common methods available on any type which implements [`AsRawCrtc`].
+///
+/// This is implemented internally by DRM, and provides many of the basic methods for working with
+/// CRTCs.
+pub trait RawCrtc: AsRawCrtc {
+    /// Return the index of this CRTC.
+    fn index(&self) -> u32 {
+        // SAFETY: The index is initialized by the time we expose Crtc objects to users, and is
+        // invariant throughout the lifetime of the Crtc
+        unsafe { (*self.as_raw()).index }
+    }
+
+    /// Return the index of this DRM CRTC in the form of a bitmask.
+    fn mask(&self) -> u32 {
+        1 << self.index()
+    }
+}
+impl<T: AsRawCrtc> RawCrtc for T {}
+
+/// A [`struct drm_crtc`] without a known [`DriverCrtc`] implementation.
+///
+/// This is mainly for situations where our bindings can't infer the [`DriverCrtc`] implementation
+/// for a [`struct drm_crtc`] automatically. It is identical to [`Crtc`], except that it does not
+/// provide access to the driver's private data.
+///
+/// It may be upcasted to a full [`Crtc`] using [`Crtc::from_opaque`] or
+/// [`Crtc::try_from_opaque`].
+///
+/// # Invariants
+///
+/// - `crtc` is initialized for as long as this object is made available to users.
+/// - The data layout of this structure is equivalent to [`struct drm_crtc`].
+///
+/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
+#[repr(transparent)]
+pub struct OpaqueCrtc<T: KmsDriver> {
+    crtc: Opaque<bindings::drm_crtc>,
+    _p: PhantomData<T>,
+}
+
+impl<T: KmsDriver> Sealed for OpaqueCrtc<T> {}
+
+// SAFETY:
+// - Via our type variants our data layout is identical to `drm_crtc`
+// - Since we don't expose `OpaqueCrtc` to users before it has been initialized, this and our data
+//   layout ensure that `as_raw()` always returns a valid pointer to a `drm_crtc`.
+unsafe impl<T: KmsDriver> AsRawCrtc for OpaqueCrtc<T> {
+    fn as_raw(&self) -> *mut bindings::drm_crtc {
+        self.crtc.get()
+    }
+
+    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) -> &'a Self {
+        // SAFETY: Our data layout starts with `bindings::drm_crtc`
+        unsafe { &*ptr.cast() }
+    }
+}
+
+// SAFETY: We only expose this object to users directly after KmsDriver::create_objects has been
+// called.
+unsafe impl<T: KmsDriver> ModesettableCrtc for OpaqueCrtc<T> {
+    type State = OpaqueCrtcState<T>;
+}
+
+// SAFETY: We don't expose OpaqueCrtc<T> to users before `base` is initialized in Crtc::<T>::new(),
+// so `raw_mode_obj` always returns a valid pointer to a bindings::drm_mode_object.
+unsafe impl<T: KmsDriver> ModeObject for OpaqueCrtc<T> {
+    type Driver = T;
+
+    fn drm_dev(&self) -> &Device<Self::Driver> {
+        // SAFETY: The parent device for a DRM connector will never outlive the connector, and this
+        // pointer is invariant through the lifetime of the connector
+        unsafe { Device::from_raw((*self.as_raw()).dev) }
+    }
+
+    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
+        // SAFETY: We don't expose DRM connectors to users before `base` is initialized
+        unsafe { &raw mut (*self.as_raw()).base }
+    }
+}
+
+// SAFETY: CRTCs are non-refcounted modesetting objects
+unsafe impl<T: KmsDriver> StaticModeObject for OpaqueCrtc<T> {}
+
+// SAFETY: Our CRTC interface is guaranteed to be thread-safe
+unsafe impl<T: KmsDriver> Send for OpaqueCrtc<T> {}
+
+// SAFETY: Our CRTC interface is guaranteed to be thread-safe
+unsafe impl<T: KmsDriver> Sync for OpaqueCrtc<T> {}
+
+// SAFETY: `funcs` is initialized when the CRTC is allocated
+unsafe impl<T: KmsDriver> ModeObjectVtable for OpaqueCrtc<T> {
+    type Vtable = bindings::drm_crtc_funcs;
+
+    fn vtable(&self) -> *const Self::Vtable {
+        // SAFETY: `as_raw()` always returns a valid pointer to a crtc
+        unsafe { (*self.as_raw()).funcs }
+    }
+}
+
+impl<T: DriverCrtcState> Sealed for CrtcState<T> {}
+
+/// The main trait for implementing the [`struct drm_crtc_state`] API for a [`Crtc`].
+///
+/// A driver may store driver-private data within the implementor's type, which will be available
+/// when using a full typed [`CrtcState`] object.
+///
+/// # Invariants
+///
+/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in
+///   [`struct drm_crtc`] pointers are contained within a [`Crtc<Self::Crtc>`].
+/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in
+///   [`struct drm_crtc_state`] pointers are contained within a [`CrtcState<Self>`].
+///
+/// [`struct drm_crtc`]: srctree/include/drm_crtc.h
+/// [`struct drm_crtc_state`]: srctree/include/drm_crtc.h
+pub trait DriverCrtcState: Clone + Default + Unpin {
+    /// The parent CRTC driver for this CRTC state
+    type Crtc: DriverCrtc<State = Self>
+    where
+        Self: Sized;
+}
+
+/// The main interface for a [`struct drm_crtc_state`].
+///
+/// This type is the main interface for dealing with the atomic state of DRM crtcs. In addition, it
+/// allows access to whatever private data is contained within an implementor's [`DriverCrtcState`]
+/// type.
+///
+/// # Invariants
+///
+/// - `state` and `inner` initialized for as long as this object is exposed to users.
+/// - The data layout of this structure begins with [`struct drm_crtc_state`].
+/// - The CRTC for this type can always be assumed to be of type [`Crtc<T::Crtc>`].
+///
+/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h
+#[repr(C)]
+pub struct CrtcState<T: DriverCrtcState> {
+    // It should be noted that CrtcState is a bit of an oddball - it's the only atomic state
+    // structure that can be modified after it has been swapped in, which is why we need to have
+    // `state` within an `Opaque<>`…
+    state: Opaque<bindings::drm_crtc_state>,
+
+    // …it is also one of the few atomic states that some drivers will embed work structures into,
+    // which means there's a good chance in the future we may have pinned data here - making it
+    // impossible for us to hold a mutable or immutable reference to the CrtcState. In preparation
+    // for that possibility, we keep `T` in an UnsafeCell.
+    inner: UnsafeCell<T>,
+}
+
+impl<T: DriverCrtcState> Deref for CrtcState<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        // SAFETY: Our interface ensures that `inner` will not be modified unless only a single
+        // mutable reference exists to `inner`, so this is safe
+        unsafe { &*self.inner.get() }
+    }
+}
+
+impl<T: DriverCrtcState> DerefMut for CrtcState<T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        self.inner.get_mut()
+    }
+}
+
+// SAFETY: Shares the safety guarantee of Crtc<T>'s ModeObjectVtable impl
+unsafe impl<T: DriverCrtcState> ModeObjectVtable for CrtcState<T> {
+    type Vtable = bindings::drm_crtc_funcs;
+
+    fn vtable(&self) -> *const Self::Vtable {
+        self.crtc().vtable()
+    }
+}
+
+impl<T: DriverCrtcState> CrtcState<T> {
+    super::impl_from_opaque_mode_obj! {
+        fn <'a, D, C>(&'a OpaqueCrtcState<D>) -> &'a Self
+        where
+            T: DriverCrtcState<Crtc = C>;
+        use
+            C as DriverCrtc,
+            D as KmsDriver<Crtc = ...>
+    }
+}
+
+/// A trait implemented by any type which can produce a reference to a [`struct drm_crtc_state`].
+///
+/// This is implemented internally by DRM.
+///
+/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h
+pub trait AsRawCrtcState: private::AsRawCrtcState {
+    /// The type that this CRTC state interface returns to represent the parent CRTC
+    type Crtc: ModesettableCrtc;
+}
+
+pub(crate) mod private {
+    use super::*;
+
+    #[allow(unreachable_pub)]
+    pub trait AsRawCrtcState {
+        /// Return a raw pointer to the DRM CRTC state
+        ///
+        /// Note that CRTC states are the only atomic state in KMS which don't nicely follow rust's
+        /// data aliasing rules already.
+        fn as_raw(&self) -> *mut bindings::drm_crtc_state;
+    }
+}
+
+pub(super) use private::AsRawCrtcState as AsRawCrtcStatePrivate;
+
+/// Common methods available on any type which implements [`AsRawCrtcState`].
+///
+/// This is implemented internally by DRM, and provides many of the basic methods for working with
+/// the atomic state of [`Crtc`]s.
+pub trait RawCrtcState: AsRawCrtcState {
+    /// Return the CRTC that owns this state.
+    fn crtc(&self) -> &Self::Crtc {
+        // SAFETY:
+        // - This type conversion is guaranteed by type invariance
+        // - Our interface ensures that this access follows rust's data-aliasing rules
+        // - `crtc` is guaranteed to never be NULL and is invariant throughout the lifetime of the
+        //   state
+        unsafe { <Self::Crtc as AsRawCrtc>::from_raw((*self.as_raw()).crtc) }
+    }
+
+    /// Returns whether or not the CRTC is active in this atomic state.
+    fn active(&self) -> bool {
+        // SAFETY: `active` and the rest of its containing bitfield can only be modified from the
+        // atomic check context, and are invariant beyond that point - so our interface can ensure
+        // this access is serialized
+        unsafe { (*self.as_raw()).active }
+    }
+}
+impl<T: AsRawCrtcState> RawCrtcState for T {}
+
+/// A trait implemented for any type which can be constructed directly from a
+/// [`struct drm_crtc_state`] pointer.
+///
+/// This is implemented internally by DRM.
+///
+/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h
+pub trait FromRawCrtcState: AsRawCrtcState {
+    /// Obtain a reference back to this type from a raw DRM crtc state pointer
+    ///
+    /// # Safety
+    ///
+    /// Callers must ensure that ptr contains a valid instance of this type.
+    unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc_state) -> &'a Self;
+}
+
+impl<T: DriverCrtcState> private::AsRawCrtcState for CrtcState<T> {
+    #[inline]
+    fn as_raw(&self) -> *mut bindings::drm_crtc_state {
+        self.state.get()
+    }
+}
+
+impl<T: DriverCrtcState> AsRawCrtcState for CrtcState<T> {
+    type Crtc = Crtc<T::Crtc>;
+}
+
+impl<T: DriverCrtcState> FromRawCrtcState for CrtcState<T> {
+    unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc_state) -> &'a Self {
+        // SAFETY: Our data layout starts with `bindings::drm_crtc_state`
+        unsafe { &*(ptr.cast()) }
+    }
+}
+
+/// A [`struct drm_crtc_state`] without a known [`DriverCrtcState`] implementation.
+///
+/// This is mainly for situations where our bindings can't infer the [`DriverCrtcState`]
+/// implementation for a [`struct drm_crtc_state`] automatically. It is identical to [`Crtc`],
+/// except that it does not provide access to the driver's private data.
+///
+/// # Invariants
+///
+/// - `state` is initialized for as long as this object is exposed to users.
+/// - The data layout of this type is identical to [`struct drm_crtc_state`].
+///
+/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h
+#[repr(transparent)]
+pub struct OpaqueCrtcState<T: KmsDriver> {
+    state: Opaque<bindings::drm_crtc_state>,
+    _p: PhantomData<T>,
+}
+
+impl<T: KmsDriver> AsRawCrtcState for OpaqueCrtcState<T> {
+    type Crtc = OpaqueCrtc<T>;
+}
+
+impl<T: KmsDriver> private::AsRawCrtcState for OpaqueCrtcState<T> {
+    fn as_raw(&self) -> *mut bindings::drm_crtc_state {
+        self.state.get()
+    }
+}
+
+impl<T: KmsDriver> FromRawCrtcState for OpaqueCrtcState<T> {
+    unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc_state) -> &'a Self {
+        // SAFETY: Our data layout is identical to `bindings::drm_crtc_state`
+        unsafe { &*(ptr.cast()) }
+    }
+}
+
+// SAFETY: Shares the safety guarantees of OpaqueCrtc<T>'s ModeObjectVtable impl
+unsafe impl<T: KmsDriver> ModeObjectVtable for OpaqueCrtcState<T> {
+    type Vtable = bindings::drm_crtc_funcs;
+
+    fn vtable(&self) -> *const Self::Vtable {
+        self.crtc().vtable()
+    }
+}
+
+/// An interface for mutating a [`Crtc`]s atomic state.
+///
+/// This type is typically returned by an [`AtomicStateMutator`] within contexts where it is
+/// possible to safely mutate a plane's state. In order to uphold rust's data-aliasing rules, only
+/// [`CrtcStateMutator`] may exist at a time.
+///
+/// # Invariants
+///
+/// `self.state` always points to a valid instance of a [`FromRawCrtcState`] object.
+pub struct CrtcStateMutator<'a, T: FromRawCrtcState> {
+    state: NonNull<T>,
+    mask: &'a Cell<u32>,
+}
+
+impl<'a, T: FromRawCrtcState> CrtcStateMutator<'a, T> {
+    pub(super) fn new<D: KmsDriver>(
+        mutator: &'a AtomicStateMutator<D>,
+        state: NonNull<bindings::drm_crtc_state>,
+    ) -> Option<Self> {
+        // SAFETY: `crtc` is invariant throughout the lifetime of the atomic state, and always
+        // points to a valid `Crtc<T::Crtc>`
+        let crtc = unsafe { T::Crtc::from_raw((*state.as_ptr()).crtc) };
+        let crtc_mask = crtc.mask();
+        let borrowed_mask = mutator.borrowed_crtcs.get();
+
+        if borrowed_mask & crtc_mask == 0 {
+            mutator.borrowed_crtcs.set(borrowed_mask | crtc_mask);
+            Some(Self {
+                mask: &mutator.borrowed_crtcs,
+                state: state.cast(),
+            })
+        } else {
+            None
+        }
+    }
+}
+
+impl<'a, T: DriverCrtcState> CrtcStateMutator<'a, CrtcState<T>> {
+    super::impl_from_opaque_mode_obj! {
+        fn <D, C>(CrtcStateMutator<'a, OpaqueCrtcState<D>>) -> Self
+        where
+            T: DriverCrtcState<Crtc = C>;
+        use
+            T as DriverCrtc,
+            D as KmsDriver<Crtc = ...>
+    }
+}
+
+impl<'a, T: FromRawCrtcState> Drop for CrtcStateMutator<'a, T> {
+    fn drop(&mut self) {
+        // SAFETY: Our interface is proof that we are the only ones with a reference to this data
+        let mask = unsafe { self.state.as_ref() }.crtc().mask();
+        self.mask.set(self.mask.get() & !mask);
+    }
+}
+
+impl<'a, T: DriverCrtcState> Deref for CrtcStateMutator<'a, CrtcState<T>> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        // SAFETY: Our interface ensures that `self.state.inner` follows rust's data-aliasing rules,
+        // so this is safe
+        unsafe { &*(*self.state.as_ptr()).inner.get() }
+    }
+}
+
+impl<'a, T: DriverCrtcState> DerefMut for CrtcStateMutator<'a, CrtcState<T>> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        // SAFETY: Our interface ensures that `self.state.inner` follows rust's data-aliasing rules,
+        // so this is safe
+        unsafe { (*self.state.as_ptr()).inner.get_mut() }
+    }
+}
+
+impl<'a, T: FromRawCrtcState> AsRawCrtcState for CrtcStateMutator<'a, T> {
+    type Crtc = T::Crtc;
+}
+
+impl<'a, T: FromRawCrtcState> private::AsRawCrtcState for CrtcStateMutator<'a, T> {
+    fn as_raw(&self) -> *mut bindings::drm_crtc_state {
+        self.state.as_ptr().cast()
+    }
+}
+
+// SAFETY: Shares the safety guarantees of T::Crtc's ModeObjectVtable impl
+unsafe impl<'a, T: FromRawCrtcState> ModeObjectVtable for CrtcStateMutator<'a, T>
+where
+    T::Crtc: ModeObjectVtable,
+{
+    type Vtable = <T::Crtc as ModeObjectVtable>::Vtable;
+
+    fn vtable(&self) -> *const Self::Vtable {
+        self.crtc().vtable()
+    }
+}
+
+/// A token provided during [`atomic_check`] callbacks for accessing the crtc, atomic state, and new
+/// and old states of the crtc.
+///
+/// # Invariants
+///
+/// This token is proof that the old and new atomic state of `crtc` are present in `state` and do
+/// not have any mutators taken out.
+///
+/// [`atomic_check`]: DriverCrtc::atomic_check
+pub struct CrtcAtomicCheck<'a, T: DriverCrtc> {
+    state: &'a AtomicStateComposer<T::Driver>,
+    crtc: &'a Crtc<T>,
+}
+
+impl<'a, T: DriverCrtc> CrtcAtomicCheck<'a, T> {
+    impl_atomic_state_token_ops!(
+        CrtcAtomicCheck,
+        AtomicStateComposer,
+        Crtc,
+        use <'a, T>
+    );
+}
+
+/// A token provided to [`DriverCrtc`] callbacks during the atomic commit phase for accessing the
+/// crtc, atomic state, new and old states of the crtc.
+///
+/// # Invariants
+///
+/// This token is proof that the old and new atomic state of `crtc` are present in `state` and do
+/// not have any mutators taken out.
+pub struct CrtcAtomicCommit<'a, T: DriverCrtc> {
+    state: &'a AtomicStateMutator<T::Driver>,
+    crtc: &'a Crtc<T>,
+}
+
+impl<'a, T: DriverCrtc> CrtcAtomicCommit<'a, T> {
+    impl_atomic_state_token_ops!(
+        CrtcAtomicCommit,
+        AtomicStateMutator,
+        Crtc,
+        use <'a, T>
+    );
+}
+
+unsafe extern "C" fn crtc_destroy_callback<T: DriverCrtc>(crtc: *mut bindings::drm_crtc) {
+    // SAFETY: DRM guarantees that `crtc` points to a valid initialized `drm_crtc`.
+    unsafe { bindings::drm_crtc_cleanup(crtc) };
+
+    // SAFETY:
+    // - DRM guarantees we are now the only one with access to this [`drm_crtc`].
+    // - This cast is safe via `DriverCrtc`s type invariants.
+    // - We created this as a pinned type originally
+    drop(unsafe { Pin::new_unchecked(KBox::from_raw(crtc as *mut Crtc<T>)) });
+}
+
+unsafe extern "C" fn atomic_duplicate_state_callback<T: DriverCrtcState>(
+    crtc: *mut bindings::drm_crtc,
+) -> *mut bindings::drm_crtc_state {
+    // SAFETY: DRM guarantees that `crtc` points to a valid initialized `drm_crtc`.
+    let state = unsafe { (*crtc).state };
+    if state.is_null() {
+        return null_mut();
+    }
+
+    // SAFETY: This cast is safe via `DriverCrtcState`s type invariants.
+    let crtc = unsafe { Crtc::<T::Crtc>::from_raw(crtc) };
+
+    // SAFETY: This cast is safe via `DriverCrtcState`s type invariants.
+    let state = unsafe { CrtcState::<T>::from_raw(state) };
+
+    let new: Result<KBox<_>> = KBox::try_init(
+        try_init!(CrtcState {
+            inner: UnsafeCell::new((*state).clone()),
+            state: Opaque::new(Default::default()),
+        }),
+        GFP_KERNEL,
+    );
+
+    if let Ok(new) = new {
+        let new = KBox::into_raw(new).cast();
+
+        // SAFETY:
+        // - `new` provides a valid pointer to a newly allocated `drm_crtc_state` via type
+        // invariants
+        // - This initializes `new` via memcpy()
+        unsafe { bindings::__drm_atomic_helper_crtc_duplicate_state(crtc.as_raw(), new) }
+
+        new
+    } else {
+        null_mut()
+    }
+}
+
+unsafe extern "C" fn atomic_destroy_state_callback<T: DriverCrtcState>(
+    _crtc: *mut bindings::drm_crtc,
+    crtc_state: *mut bindings::drm_crtc_state,
+) {
+    // SAFETY: DRM guarantees that `state` points to a valid instance of `drm_crtc_state`
+    unsafe { bindings::__drm_atomic_helper_crtc_destroy_state(crtc_state) };
+
+    // SAFETY:
+    // - DRM guarantees we are the only one with access to this `drm_crtc_state`
+    // - This cast is safe via our type invariants.
+    // - All data in `CrtcState` is either Unpin, or pinned
+    drop(unsafe { KBox::from_raw(crtc_state as *mut CrtcState<T>) });
+}
+
+unsafe extern "C" fn crtc_reset_callback<T: DriverCrtcState>(crtc: *mut bindings::drm_crtc) {
+    // SAFETY: DRM guarantees that `state` points to a valid instance of `drm_crtc_state`
+    let state = unsafe { (*crtc).state };
+    if !state.is_null() {
+        // SAFETY:
+        // - We're guaranteed `crtc` is `Crtc<T>` via type invariants
+        // - We're guaranteed `state` is `CrtcState<T>` via type invariants.
+        unsafe { atomic_destroy_state_callback::<T>(crtc, state) }
+
+        // SAFETY: No special requirements here, DRM expects this to be NULL
+        unsafe {
+            (*crtc).state = null_mut();
+        }
+    }
+
+    // SAFETY: `crtc` is guaranteed to be of type `Crtc<T::Crtc>` by type invariance
+    let crtc = unsafe { Crtc::<T::Crtc>::from_raw(crtc) };
+
+    // Unfortunately, this is the best we can do at the moment as this FFI callback was mistakenly
+    // presumed to be infallible :(
+    let new: KBox<CrtcState<T>> = KBox::try_init(
+        try_init!(CrtcState {
+            state: Opaque::new(Default::default()),
+            inner: UnsafeCell::new(Default::default()),
+        }),
+        GFP_KERNEL,
+    )
+    .expect("Unfortunately, this API was presumed infallible");
+
+    // SAFETY: DRM takes ownership of the state from here, and will never move it
+    unsafe { bindings::__drm_atomic_helper_crtc_reset(crtc.as_raw(), KBox::into_raw(new).cast()) };
+}
+
+unsafe extern "C" fn atomic_check_callback<T: DriverCrtc>(
+    crtc: *mut bindings::drm_crtc,
+    state: *mut bindings::drm_atomic_state,
+) -> i32 {
+    // SAFETY:
+    // - We're guaranteed `crtc` is of type `Crtc<T>` via type invariants.
+    // - We're guaranteed by DRM that `crtc` is pointing to a valid initialized state.
+    let crtc = unsafe { Crtc::from_raw(crtc) };
+
+    // SAFETY: DRM guarantees `state` points to a valid `drm_atomic_state`
+    // We use a ManuallyDrop here to avoid AtomicStateComposer dropping an owned reference we never
+    // acquired.
+    let state =
+        unsafe { ManuallyDrop::new(AtomicStateComposer::new(NonNull::new_unchecked(state))) };
+
+    // SAFETY:
+    // - Since we're in the atomic check callback, we're guaranteed by DRM that both the old and
+    //   new crtc state are present in this atomic state
+    // - We just created the state composer above, so other composers cannot be taken out on the
+    //   crtc state yet.
+    let check = unsafe { CrtcAtomicCheck::new(crtc, &state) };
+
+    from_result(|| {
+        T::atomic_check(check)?;
+        Ok(0)
+    })
+}
+
+unsafe extern "C" fn atomic_begin_callback<T: DriverCrtc>(
+    crtc: *mut bindings::drm_crtc,
+    state: *mut bindings::drm_atomic_state,
+) {
+    // SAFETY:
+    // * We're guaranteed `crtc` is of type `Crtc<T>` via type invariants.
+    // * We're guaranteed by DRM that `crtc` is pointing to a valid initialized state.
+    let crtc = unsafe { Crtc::from_raw(crtc) };
+
+    // SAFETY: We're guaranteed by DRM that `state` points to a valid instance of `drm_atomic_state`
+    let state = unsafe { AtomicStateMutator::new(NonNull::new_unchecked(state)) };
+
+    // SAFETY:
+    // - Since we're in the atomic_begin callback, we're guaranteed by DRM that both the old and new
+    //   crtc state are resent in this atomic state.
+    // - We just created the state mutator above, so other mutators cannot be taken out on the crtc
+    //   state yet.
+    let commit = unsafe { CrtcAtomicCommit::new(crtc, &state) };
+
+    T::atomic_begin(commit);
+}
+
+unsafe extern "C" fn atomic_flush_callback<T: DriverCrtc>(
+    crtc: *mut bindings::drm_crtc,
+    state: *mut bindings::drm_atomic_state,
+) {
+    // SAFETY:
+    // - We're guaranteed `crtc` is of type `Crtc<T>` via type invariants.
+    // - We're guaranteed by DRM that `crtc` is pointing to a valid initialized state.
+    let crtc = unsafe { Crtc::from_raw(crtc) };
+
+    // SAFETY: We're guaranteed by DRM that `state` points to a valid instance of `drm_atomic_state`
+    let state = unsafe { AtomicStateMutator::new(NonNull::new_unchecked(state)) };
+
+    // SAFETY:
+    // - Since we're in the atomic_flush callback, we're guaranteed by DRM that both the old and new
+    //   crtc state are resent in this atomic state.
+    // - We just created the state mutator above, so other mutators cannot be taken out on the crtc
+    //   state yet.
+    let commit = unsafe { CrtcAtomicCommit::new(crtc, &state) };
+
+    T::atomic_flush(commit);
+}
+
+unsafe extern "C" fn atomic_enable_callback<T: DriverCrtc>(
+    crtc: *mut bindings::drm_crtc,
+    state: *mut bindings::drm_atomic_state,
+) {
+    // SAFETY:
+    // - We're guaranteed `crtc` is of type `Crtc<T>` via type invariants.
+    // - We're guaranteed by DRM that `crtc` is pointing to a valid initialized state.
+    let crtc = unsafe { Crtc::from_raw(crtc) };
+
+    // SAFETY: DRM never passes an invalid ptr for `state`
+    let state = unsafe { AtomicStateMutator::new(NonNull::new_unchecked(state)) };
+
+    // SAFETY:
+    // - Since we're in the atomic_enable callback, we're guaranteed by DRM that both the old and
+    //   new crtc state are present in this atomic state.
+    // - We just created the state mutator above, so other mutators cannot be taken out on the crtc
+    //   state yet.
+    let commit = unsafe { CrtcAtomicCommit::new(crtc, &state) };
+
+    T::atomic_enable(commit);
+}
+
+unsafe extern "C" fn atomic_disable_callback<T: DriverCrtc>(
+    crtc: *mut bindings::drm_crtc,
+    state: *mut bindings::drm_atomic_state,
+) {
+    // SAFETY:
+    // - We're guaranteed `crtc` points to a valid instance of `drm_crtc`
+    // - We're guaranteed that `crtc` is of type `Plane<T>` by `DriverPlane`s type invariants.
+    let crtc = unsafe { Crtc::from_raw(crtc) };
+
+    // SAFETY: We're guaranteed that `state` points to a valid `drm_crtc_state` by DRM
+    let state = unsafe { AtomicStateMutator::new(NonNull::new_unchecked(state)) };
+
+    // SAFETY:
+    // - Since we're in the atomic_disable callback, we're guaranteed by DRM that both the old and
+    //   new crtc state are present in this atomic state.
+    // - We just created the state mutator above, so other mutators cannot be taken out on the crtc
+    //   state yet.
+    let commit = unsafe { CrtcAtomicCommit::new(crtc, &state) };
+
+    T::atomic_disable(commit);
+}
diff --git a/rust/kernel/drm/kms/encoder.rs b/rust/kernel/drm/kms/encoder.rs
new file mode 100644
index 000000000000..5f860faf8b61
--- /dev/null
+++ b/rust/kernel/drm/kms/encoder.rs
@@ -0,0 +1,409 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! DRM encoders.
+//!
+//! C header: [`include/drm/drm_encoder.h`](srctree/include/drm/drm_encoder.h)
+
+use super::{
+    KmsDriver, ModeObject, ModeObjectVtable, NewKmsDevice, Probing, StaticModeObject, Sealed
+};
+use crate::{
+    alloc::KBox,
+    drm::device::Device,
+    error::to_result,
+    prelude::*,
+    types::{NotThreadSafe, Opaque},
+};
+use bindings;
+use core::{
+    marker::*,
+    mem,
+    ops::Deref,
+    ptr::null,
+};
+use macros::paste;
+
+/// A macro for generating our type ID enumerator.
+macro_rules! declare_encoder_types {
+    ($( $oldname:ident as $newname:ident ),+) => {
+        #[repr(i32)]
+        #[non_exhaustive]
+        #[derive(Copy, Clone, PartialEq, Eq)]
+        /// An enumerator for all possible [`Encoder`] type IDs.
+        pub enum Type {
+            // Note: bindgen defaults the macro values to u32 and not i32, but DRM takes them as an
+            // i32 - so just do the conversion here
+            $(
+                #[doc = concat!("The encoder type ID for a ", stringify!($newname), " encoder.")]
+                $newname = paste!(crate::bindings::[<DRM_MODE_ENCODER_ $oldname>]) as i32
+            ),+
+        }
+    };
+}
+
+declare_encoder_types! {
+    NONE     as None,
+    DAC      as Dac,
+    TMDS     as Tmds,
+    LVDS     as Lvds,
+    VIRTUAL  as Virtual,
+    DSI      as Dsi,
+    DPMST    as DpMst,
+    DPI      as Dpi
+}
+
+/// The main trait for implementing the [`struct drm_encoder`] API for [`Encoder`].
+///
+/// Any KMS driver should have at least one implementation of this type, which allows them to create
+/// [`Encoder`] objects. Additionally, a driver may store driver-private data within the type that
+/// implements [`DriverEncoder`] - and it will be made available when using a fully typed
+/// [`Encoder`] object.
+///
+/// # Invariants
+///
+/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in
+///   [`struct drm_encoder`] pointers are contained within a [`Encoder<Self>`].
+///
+/// [`struct drm_encoder`]: srctree/include/drm/drm_encoder.h
+#[vtable]
+pub trait DriverEncoder: Send + Sync + Sized {
+    /// The generated C vtable for this [`DriverEncoder`] implementation.
+    const OPS: &'static DriverEncoderOps = &DriverEncoderOps {
+        funcs: bindings::drm_encoder_funcs {
+            reset: None,
+            destroy: Some(encoder_destroy_callback::<Self>),
+            late_register: None,
+            early_unregister: None,
+            debugfs_init: None,
+        },
+        helper_funcs: bindings::drm_encoder_helper_funcs {
+            dpms: None,
+            mode_valid: None,
+            mode_fixup: None,
+            prepare: None,
+            mode_set: None,
+            commit: None,
+            detect: None,
+            enable: None,
+            disable: None,
+            atomic_check: None,
+            atomic_enable: None,
+            atomic_disable: None,
+            atomic_mode_set: None,
+        },
+    };
+
+    /// The parent driver for this drm_encoder implementation
+    type Driver: KmsDriver;
+
+    /// The type to pass to the `args` field of [`UnregisteredEncoder::new`].
+    ///
+    /// This type will be made available in in the `args` argument of [`Self::new`]. Drivers which
+    /// don't need this can simply pass [`()`] here.
+    type Args;
+
+    /// The constructor for creating a [`Encoder`] using this [`DriverEncoder`] implementation.
+    ///
+    /// Drivers may use this to instantiate their [`DriverEncoder`] object.
+    fn new(device: &Device<Self::Driver>, args: Self::Args) -> impl PinInit<Self, Error>;
+}
+
+/// The generated C vtable for a [`DriverEncoder`].
+///
+/// This type is created internally by DRM.
+pub struct DriverEncoderOps {
+    funcs: bindings::drm_encoder_funcs,
+    helper_funcs: bindings::drm_encoder_helper_funcs,
+}
+
+/// A trait implemented by any type that acts as a [`struct drm_encoder`] interface.
+///
+/// This is implemented internally by DRM.
+///
+/// # Safety
+///
+/// [`as_raw()`] must always return a valid pointer to a [`struct drm_encoder`].
+///
+/// [`struct drm_encoder`]: srctree/include/drm/drm_encoder.h
+/// [`as_raw()`]: AsRawEncoder::as_raw()
+pub unsafe trait AsRawEncoder {
+    /// Return the raw `bindings::drm_encoder` for this DRM encoder.
+    ///
+    /// Drivers should never use this directly
+    fn as_raw(&self) -> *mut bindings::drm_encoder;
+
+    /// Convert a raw `bindings::drm_encoder` pointer into an object of this type.
+    ///
+    /// # Safety
+    ///
+    /// Callers promise that `ptr` points to a valid instance of this type
+    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encoder) -> &'a Self;
+}
+
+/// The main interface for a [`struct drm_encoder`].
+///
+/// This type is the main interface for dealing with DRM encoders. In addition, it also allows
+/// immutable access to whatever private data is contained within an implementor's
+/// [`DriverEncoder`] type.
+///
+/// # Invariants
+///
+/// - `encoder` and `inner` are initialized for as long as this object is made available to users.
+/// - The data layout of this structure begins with [`struct drm_encoder`].
+///
+/// [`struct drm_encoder`]: srctree/include/drm/drm_encoder.h
+#[repr(C)]
+#[pin_data]
+pub struct Encoder<T: DriverEncoder> {
+    /// The FFI drm_encoder object
+    encoder: Opaque<bindings::drm_encoder>,
+    /// The driver's private inner data
+    #[pin]
+    inner: T,
+    #[pin]
+    _p: PhantomPinned,
+}
+
+impl<T: DriverEncoder> Sealed for Encoder<T> {}
+
+// SAFETY: Our interface is thread-safe.
+unsafe impl<T: DriverEncoder> Send for Encoder<T> {}
+// SAFETY: Our interface is thread-safe.
+unsafe impl<T: DriverEncoder> Sync for Encoder<T> {}
+
+// SAFETY: We don't expose Encoder<T> to users before `base` is initialized in ::new(), so
+// `raw_mode_obj` always returns a valid pointer to a bindings::drm_mode_object.
+unsafe impl<T: DriverEncoder> ModeObject for Encoder<T> {
+    type Driver = T::Driver;
+
+    fn drm_dev(&self) -> &Device<Self::Driver> {
+        // SAFETY: DRM encoders exist for as long as the device does, so this pointer is always
+        // valid
+        unsafe { Device::from_raw((*self.encoder.get()).dev) }
+    }
+
+    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
+        // SAFETY: We don't expose Encoder<T> to users before it's initialized, so `base` is always
+        // initialized
+        unsafe { &raw mut (*self.encoder.get()).base }
+    }
+}
+
+// SAFETY: Encoders do not have a refcount
+unsafe impl<T: DriverEncoder> StaticModeObject for Encoder<T> {}
+
+impl<T: DriverEncoder> Deref for Encoder<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+// SAFETY:
+// - Via our type invariants our data layout starts with `drm_encoder`.
+// - Since we don't expose `Encoder` to users befre it has been initialized, this and our data
+//   layout ensure that `as_raw()` always returns a valid pointer to a `drm_encoder`.
+unsafe impl<T: DriverEncoder> AsRawEncoder for Encoder<T> {
+    fn as_raw(&self) -> *mut bindings::drm_encoder {
+        self.encoder.get()
+    }
+
+    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encoder) -> &'a Self {
+        // SAFETY: Our data layout is starts with to `bindings::drm_encoder`
+        unsafe { &*ptr.cast() }
+    }
+}
+
+// SAFETY: `funcs` is initialized when the encoder is allocated
+unsafe impl<T: DriverEncoder> ModeObjectVtable for Encoder<T> {
+    type Vtable = bindings::drm_encoder_funcs;
+
+    fn vtable(&self) -> *const Self::Vtable {
+        // SAFETY: `as_raw()` always returns a valid pointer to an encoder
+        unsafe { *self.as_raw() }.funcs
+    }
+}
+
+impl<T: DriverEncoder> Encoder<T> {
+    super::impl_from_opaque_mode_obj! {
+        fn <'a, D>(&'a OpaqueEncoder<D>) -> &'a Self;
+        use
+            T as DriverEncoder,
+            D as KmsDriver<Encoder = ...>
+    }
+}
+
+/// A [`Encoder`] that has not yet been registered with userspace.
+///
+/// KMS registration is single-threaded, so this object is not thread-safe.
+///
+/// # Invariants
+///
+/// - This object can only exist before its respective KMS device has been registered.
+/// - Otherwise, it inherits all invariants of [`Encoder`] and has an identical data layout.
+pub struct UnregisteredEncoder<T: DriverEncoder>(Encoder<T>, NotThreadSafe);
+
+// SAFETY: We inherit all relevant invariants of `Encoder`
+unsafe impl<T: DriverEncoder> AsRawEncoder for UnregisteredEncoder<T> {
+    fn as_raw(&self) -> *mut bindings::drm_encoder {
+        self.0.as_raw()
+    }
+
+    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encoder) -> &'a Self {
+        // SAFETY: This is another from_raw() call, so this function shares the same safety contract
+        let encoder = unsafe { Encoder::<T>::from_raw(ptr) };
+
+        // SAFETY: Our data layout is identical via our type invariants.
+        unsafe { mem::transmute(encoder) }
+    }
+}
+
+impl<T: DriverEncoder> Deref for UnregisteredEncoder<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0.inner
+    }
+}
+
+impl<T: DriverEncoder> UnregisteredEncoder<T> {
+    /// Construct a new [`UnregisteredEncoder`].
+    ///
+    /// A driver may use this from their [`KmsDriver::create_objects`] callback in order to
+    /// construct new [`UnregisteredEncoder`] objects.
+    ///
+    /// [`KmsDriver::create_objects`]: kernel::drm::kms::KmsDriver::create_objects
+    pub fn new<'a, 'b: 'a>(
+        dev: &'a NewKmsDevice<'a, T::Driver, Probing>,
+        type_: Type,
+        possible_crtcs: u32,
+        possible_clones: u32,
+        name: Option<&CStr>,
+        args: T::Args,
+    ) -> Result<&'b Self> {
+        let this: Pin<KBox<Encoder<T>>> = KBox::try_pin_init(
+            try_pin_init!(Encoder {
+                encoder: Opaque::new(bindings::drm_encoder {
+                    helper_private: &T::OPS.helper_funcs,
+                    possible_crtcs,
+                    possible_clones,
+                    ..Default::default()
+                }),
+                inner <- T::new(dev, args),
+                _p: PhantomPinned
+            }),
+            GFP_KERNEL,
+        )?;
+
+        // SAFETY:
+        // - `dev` is responsible for destroying the encoder and thus outlives us.
+        // - as_raw() returns valid pointers for each type here
+        // - This initializes `this`
+        // - Our type is proof that this is being called before KMS device registration
+        // - `name` is optional and will be auto-generated by DRM if passed as NULL
+        to_result(unsafe {
+            bindings::drm_encoder_init(
+                dev.as_raw(),
+                this.as_raw(),
+                &T::OPS.funcs,
+                type_ as _,
+                name.map_or(null(), |n| n.as_char_ptr()),
+            )
+        })?;
+
+        // SAFETY: We don't move anything
+        let this = unsafe { Pin::into_inner_unchecked(this) };
+
+        // We'll re-assemble the box in encoder_destroy_callback()
+        let this = KBox::into_raw(this);
+
+        // UnregisteredEncoder has an equivalent data layout
+        let this: *mut Self = this.cast();
+
+        // SAFETY: We just allocated the encoder above, so this pointer must be valid
+        Ok(unsafe { &*this })
+    }
+}
+
+/// A [`struct drm_encoder`] without a known [`DriverEncoder`] implementation.
+///
+/// This is mainly for situations where our bindings can't infer the [`DriverEncoder`] implementation
+/// for a [`struct drm_encoder`] automatically. It is identical to [`Encoder`], except that it does not
+/// provide access to the driver's private data.
+///
+/// # Invariants
+///
+/// Same as [`Encoder`].
+#[repr(transparent)]
+pub struct OpaqueEncoder<T: KmsDriver> {
+    encoder: Opaque<bindings::drm_encoder>,
+    _p: PhantomData<T>,
+}
+
+impl<T: KmsDriver> Sealed for OpaqueEncoder<T> {}
+
+// SAFETY: All of our encoder interfaces are thread-safe
+unsafe impl<T: KmsDriver> Send for OpaqueEncoder<T> {}
+
+// SAFETY: All of our encoder interfaces are thread-safe
+unsafe impl<T: KmsDriver> Sync for OpaqueEncoder<T> {}
+
+// SAFETY: We don't expose OpaqueEncoder<T> to users before `base` is initialized in
+// OpaqueEncoder::new(), so `raw_mode_obj` always returns a valid poiner to a
+// bindings::drm_mode_object.
+unsafe impl<T: KmsDriver> ModeObject for OpaqueEncoder<T> {
+    type Driver = T;
+
+    fn drm_dev(&self) -> &Device<Self::Driver> {
+        // SAFETY: DRM encoders exist for as long as the device does, so this pointer is always
+        // valid
+        unsafe { Device::from_raw((*self.encoder.get()).dev) }
+    }
+
+    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
+        // SAFETY: We don't expose Encoder<T> to users before it's initialized, so `base` is always
+        // initialized
+        unsafe { &raw mut (*self.encoder.get()).base }
+    }
+}
+
+// SAFETY: Encoders do not have a refcount
+unsafe impl<T: KmsDriver> StaticModeObject for OpaqueEncoder<T> {}
+
+// SAFETY:
+// - Via our type variants our data layout is identical to  with `drm_encoder`
+// - Since we don't expose `Encoder` to users before it has been initialized, this and our data
+//   layout ensure that `as_raw()` always returns a valid pointer to a `drm_encoder`.
+unsafe impl<T: KmsDriver> AsRawEncoder for OpaqueEncoder<T> {
+    fn as_raw(&self) -> *mut bindings::drm_encoder {
+        self.encoder.get()
+    }
+
+    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encoder) -> &'a Self {
+        // SAFETY: Our data layout is identical to `bindings::drm_encoder`
+        unsafe { &*ptr.cast() }
+    }
+}
+
+// SAFETY: `funcs` is initialized when the encoder is allocated
+unsafe impl<T: KmsDriver> ModeObjectVtable for OpaqueEncoder<T> {
+    type Vtable = bindings::drm_encoder_funcs;
+
+    fn vtable(&self) -> *const Self::Vtable {
+        // SAFETY: `as_raw()` always returns a valid pointer to an encoder
+        unsafe { *self.as_raw() }.funcs
+    }
+}
+
+unsafe extern "C" fn encoder_destroy_callback<T: DriverEncoder>(
+    encoder: *mut bindings::drm_encoder,
+) {
+    // SAFETY: DRM guarantees that `encoder` points to a valid initialized `drm_encoder`.
+    unsafe { bindings::drm_encoder_cleanup(encoder) };
+
+    // SAFETY:
+    // - DRM guarantees we are now the only one with access to this [`drm_encoder`].
+    // - This cast is safe via `DriverEncoder`s type invariants.
+    unsafe { drop(KBox::from_raw(encoder as *mut Encoder<T>)) };
+}
diff --git a/rust/kernel/drm/kms/framebuffer.rs b/rust/kernel/drm/kms/framebuffer.rs
new file mode 100644
index 000000000000..54d0391388a9
--- /dev/null
+++ b/rust/kernel/drm/kms/framebuffer.rs
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! DRM framebuffers.
+//!
+//! C header: [`include/drm/drm_framebuffer.h`](srctree/include/drm/drm_framebuffer.h)
+
+use super::{KmsDriver, ModeObject, Sealed};
+use crate::{drm::device::Device, types::*};
+use bindings;
+use core::{marker::*, ptr};
+
+/// The main interface for [`struct drm_framebuffer`].
+///
+/// # Invariants
+///
+/// - `self.0` is initialized for as long as this object is exposed to users.
+/// - This type has an identical data layout to [`struct drm_framebuffer`]
+///
+/// [`struct drm_framebuffer`]: srctree/include/drm/drm_framebuffer.h
+#[repr(transparent)]
+pub struct Framebuffer<T: KmsDriver>(Opaque<bindings::drm_framebuffer>, PhantomData<T>);
+
+// SAFETY:
+// - `self.0` is initialized for as long as this object is exposed to users
+// - `base` is initialized by DRM when `self.0` is initialized, thus `raw_mode_obj()` always returns
+//   a valid pointer.
+unsafe impl<T: KmsDriver> ModeObject for Framebuffer<T> {
+    type Driver = T;
+
+    fn drm_dev(&self) -> &Device<Self::Driver> {
+        // SAFETY: `dev` points to an initialized `struct drm_device` for as long as this type is
+        // initialized
+        unsafe { Device::from_raw((*self.0.get()).dev) }
+    }
+
+    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
+        // SAFETY: We don't expose Framebuffer<T> to users before its initialized, so `base` is
+        // always initialized
+        unsafe { &raw mut (*self.0.get()).base }
+    }
+}
+
+// SAFETY: References to framebuffers are safe to be accessed from any thread
+unsafe impl<T: KmsDriver> Send for Framebuffer<T> {}
+// SAFETY: References to framebuffers are safe to be accessed from any thread
+unsafe impl<T: KmsDriver> Sync for Framebuffer<T> {}
+
+// For implementing ModeObject
+impl<T: KmsDriver> Sealed for Framebuffer<T> {}
+
+impl<T: KmsDriver> PartialEq for Framebuffer<T> {
+    fn eq(&self, other: &Self) -> bool {
+        ptr::eq(self.0.get(), other.0.get())
+    }
+}
+impl<T: KmsDriver> Eq for Framebuffer<T> {}
+
+impl<T: KmsDriver> Framebuffer<T> {
+    /// Convert a raw pointer to a `struct drm_framebuffer` into a [`Framebuffer`]
+    ///
+    /// # Safety
+    ///
+    /// The caller guarantews that `ptr` points to a initialized `struct drm_framebuffer` for at
+    /// least the entire lifetime of `'a`.
+    #[inline]
+    pub(super) unsafe fn from_raw<'a>(ptr: *const bindings::drm_framebuffer) -> &'a Self {
+        // SAFETY: Our data layout is identical to drm_framebuffer
+        unsafe { &*ptr.cast() }
+    }
+}
diff --git a/rust/kernel/drm/kms/modes.rs b/rust/kernel/drm/kms/modes.rs
new file mode 100644
index 000000000000..0e8dc434487d
--- /dev/null
+++ b/rust/kernel/drm/kms/modes.rs
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+use bindings;
+
+use crate::types::Opaque;
+
+/// DRM kernel-internal display mode structure.
+///
+/// This structure contains various resolution and timing information for a given display mode in
+/// DRM.
+///
+/// # Invariants
+///
+/// - The data layout of this structure is guaranteed to be equivalent to that of `struct
+///   drm_display_mode`.
+/// - We ensure through our bindings that rust's data aliasing rules are maintained, ensuring it is
+///   safe to read any fields inside of `self.inner`.
+#[repr(transparent)]
+pub struct DisplayMode {
+    inner: Opaque<bindings::drm_display_mode>,
+}
+
+// SAFETY: Our bindings are thread-safe via our type invariants.
+unsafe impl Send for DisplayMode {}
+// SAFETY: Our bindings are thread-safe via our type invariants.
+unsafe impl Sync for DisplayMode {}
+
+impl DisplayMode {
+    /// Convert a raw pointer to a `struct drm_display_mode` into an immutable [`DisplayMode`] ref.
+    ///
+    /// # SAFETY
+    ///
+    /// - The caller guarantees that `self_ptr` points to a valid initialized `struct
+    ///   drm_display_mode`.
+    /// - The caller must ensure that rust's data aliasing rules will not be broken for the lifetime
+    ///   of `'a`, e.g. no mutable references may exist while immutable references exist to Self.
+    #[inline]
+    pub(crate) unsafe fn as_ref<'a>(self_ptr: *const bindings::drm_display_mode) -> &'a Self {
+        // SAFETY: The pointer is valid via our safety contract, and the data layout of this struct
+        // is equivalent to `Self` via our type invariants.
+        unsafe { &*self_ptr.cast() }
+    }
+
+    /// Return a raw pointer to the `struct drm_display_mode` contained within this [`DisplayMode`].
+    #[inline]
+    pub(crate) fn as_raw(&self) -> *const bindings::drm_display_mode {
+        self.inner.get().cast_const()
+    }
+
+    /// Retrieve the pixel clock for the adjusted display mode in kHz.
+    #[inline]
+    pub fn crtc_clock(&self) -> i32 {
+        // SAFETY: Reading these fields is safe via our type invariants
+        unsafe { (*self.as_raw()).crtc_clock }
+    }
+
+    /// Retrieve the start of the vertical sync period for the adjusted display mode.
+    #[inline]
+    pub fn crtc_vblank_start(&self) -> u16 {
+        unsafe { (*self.as_raw()).crtc_vblank_start }
+    }
+
+    /// Retrieve the end of the vertical sync period for the adjusted display mode.
+    #[inline]
+    pub fn crtc_vblank_end(&self) -> u16 {
+        // SAFETY: Reading these fields is safe via our type invariants
+        unsafe { (*self.as_raw()).crtc_vblank_end }
+    }
+
+    /// Retrieve the number of vertical scanlines for a full scanout frame in this adjusted display
+    /// mode.
+    #[inline]
+    pub fn crtc_vtotal(&self) -> u16 {
+        // SAFETY: Reading these fields is safe via our type invariants
+        unsafe { (*self.as_raw()).crtc_vtotal }
+    }
+}
diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs
new file mode 100644
index 000000000000..661d82273099
--- /dev/null
+++ b/rust/kernel/drm/kms/plane.rs
@@ -0,0 +1,1095 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! DRM display planes.
+//!
+//! C header: [`include/drm/drm_plane.h`](srctree/include/drm/drm_plane.h)
+
+use super::{
+    atomic::*, crtc::*, framebuffer::*, KmsDriver, ModeObject, ModeObjectVtable, StaticModeObject,
+    NewKmsDevice, Probing, Sealed
+};
+use crate::{
+    alloc::KBox,
+    bindings,
+    drm::{device::Device, fourcc::*},
+    error::{from_result, to_result, Error},
+    prelude::*,
+    types::{NotThreadSafe, Opaque},
+};
+use core::{
+    cell::Cell,
+    marker::*,
+    mem::{self, ManuallyDrop},
+    ops::*,
+    pin::Pin,
+    ptr::{null, null_mut, NonNull},
+};
+
+/// The main trait for implementing the [`struct drm_plane`] API for [`Plane`].
+///
+/// Any KMS driver should have at least one implementation of this type, which allows them to create
+/// [`Plane`] objects. Additionally, a driver may store driver-private data within the type that
+/// implements [`DriverPlane`] - and it will be made available when using a fully typed [`Plane`]
+/// object.
+///
+/// # Invariants
+///
+/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in
+///   [`struct drm_plane`] pointers are contained within a [`Plane<Self>`].
+/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in
+///   [`struct drm_plane_state`] pointers are contained within a [`PlaneState<Self::State>`].
+///
+/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h
+/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
+#[vtable]
+pub trait DriverPlane: Send + Sync + Sized {
+    /// The generated C vtable for this [`DriverPlane`] implementation.
+    const OPS: &'static DriverPlaneOps = &DriverPlaneOps {
+        funcs: bindings::drm_plane_funcs {
+            update_plane: Some(bindings::drm_atomic_helper_update_plane),
+            disable_plane: Some(bindings::drm_atomic_helper_disable_plane),
+            destroy: Some(plane_destroy_callback::<Self>),
+            reset: Some(plane_reset_callback::<Self>),
+            set_property: None,
+            atomic_duplicate_state: Some(atomic_duplicate_state_callback::<Self::State>),
+            atomic_destroy_state: Some(atomic_destroy_state_callback::<Self::State>),
+            atomic_set_property: None,
+            atomic_get_property: None,
+            late_register: None,
+            early_unregister: None,
+            atomic_print_state: None,
+            format_mod_supported: None,
+            format_mod_supported_async: None,
+        },
+
+        helper_funcs: bindings::drm_plane_helper_funcs {
+            prepare_fb: None,
+            cleanup_fb: None,
+            begin_fb_access: None,
+            end_fb_access: None,
+            atomic_check: if Self::HAS_ATOMIC_CHECK {
+                Some(atomic_check_callback::<Self>)
+            } else {
+                None
+            },
+            atomic_update: if Self::HAS_ATOMIC_UPDATE {
+                Some(atomic_update_callback::<Self>)
+            } else {
+                None
+            },
+            atomic_enable: None,
+            atomic_disable: None,
+            atomic_async_check: None,
+            atomic_async_update: None,
+            panic_flush: None,
+            get_scanout_buffer: None,
+        },
+    };
+
+    /// The type to pass to the `args` field of [`UnregisteredPlane::new`].
+    ///
+    /// This type will be made available in in the `args` argument of [`Self::new`]. Drivers which
+    /// don't need this can simply pass [`()`] here.
+    type Args;
+
+    /// The parent [`KmsDriver`] implementation.
+    type Driver: KmsDriver;
+
+    /// The [`DriverPlaneState`] implementation for this [`DriverPlane`].
+    ///
+    /// See [`DriverPlaneState`] for more info.
+    type State: DriverPlaneState;
+
+    /// The constructor for creating a [`Plane`] using this [`DriverPlane`] implementation.
+    ///
+    /// Drivers may use this to instantiate their [`DriverPlane`] object.
+    fn new(device: &Device<Self::Driver>, args: Self::Args) -> impl PinInit<Self, Error>;
+
+    /// The optional [`drm_plane_helper_funcs.atomic_update`] hook for this plane.
+    ///
+    /// Drivers may use this to customize the atomic update phase of their [`Plane`] objects. If not
+    /// specified, this function is a no-op.
+    ///
+    /// [`drm_plane_helper_funcs.atomic_update`]: srctree/include/drm/drm_modeset_helper_vtables.h
+    fn atomic_update(_commit: PlaneAtomicCommit<'_, Self>) {
+        build_error::build_error("This should not be reachable")
+    }
+
+    /// The optional [`drm_plane_helper_funcs.atomic_check`] hook for this plane.
+    ///
+    /// Drivers may use this to customize the atomic check phase of their [`Plane`] objects. The
+    /// result of this function determines whether the atomic check passed or failed.
+    ///
+    /// [`drm_plane_helper_funcs.atomic_check`]: srctree/include/drm/drm_modeset_helper_vtables.h
+    fn atomic_check(_check: PlaneAtomicCheck<'_, Self>) -> Result {
+        build_error::build_error("This should not be reachable")
+    }
+}
+
+/// The generated C vtable for a [`DriverPlane`].
+///
+/// This type is created internally by DRM.
+pub struct DriverPlaneOps {
+    funcs: bindings::drm_plane_funcs,
+    helper_funcs: bindings::drm_plane_helper_funcs,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(u32)]
+/// An enumerator describing a type of [`Plane`].
+///
+/// This is mainly just relevant for DRM legacy drivers.
+///
+/// # Invariants
+///
+/// This type is identical to [`enum drm_plane_type`].
+///
+/// [`enum drm_plane_type`]: srctree/include/drm/drm_plane.h
+pub enum Type {
+    /// Overlay planes represent all non-primary, non-cursor planes. Some drivers refer to these
+    /// types of planes as "sprites" internally.
+    Overlay = bindings::drm_plane_type_DRM_PLANE_TYPE_OVERLAY,
+
+    /// A primary plane attached to a CRTC that is the most likely to be able to light up the CRTC
+    /// when no scaling/cropping is used, and the plane covers the whole CRTC.
+    Primary = bindings::drm_plane_type_DRM_PLANE_TYPE_PRIMARY,
+
+    /// A cursor plane attached to a CRTC that is more likely to be enabled when no scaling/cropping
+    /// is used, and the framebuffer has the size indicated by [`ModeConfigInfo::max_cursor`].
+    ///
+    /// [`ModeConfigInfo::max_cursor`]: crate::drm::kms::ModeConfigInfo
+    Cursor = bindings::drm_plane_type_DRM_PLANE_TYPE_CURSOR,
+}
+
+/// The main interface for a [`struct drm_plane`].
+///
+/// This type is the main interface for dealing with DRM planes. In addition, it also allows
+/// immutable access to whatever private data is contained within an implementor's [`DriverPlane`]
+/// type.
+///
+/// # Invariants
+///
+/// - `plane` and `inner` are initialized for as long as this object is made available to users.
+/// - The data layout of this structure begins with [`struct drm_plane`].
+/// - The atomic state for this type can always be assumed to be of type [`PlaneState<T::State>`].
+///
+/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h
+#[repr(C)]
+#[pin_data]
+pub struct Plane<T: DriverPlane> {
+    /// The FFI drm_plane object
+    plane: Opaque<bindings::drm_plane>,
+    /// The driver's private inner data
+    #[pin]
+    inner: T,
+    #[pin]
+    _p: PhantomPinned,
+}
+
+impl<T: DriverPlane> Sealed for Plane<T> {}
+
+impl<T: DriverPlane> Deref for Plane<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+// SAFETY: `funcs` is initialized when the plane is allocated
+unsafe impl<T: DriverPlane> ModeObjectVtable for Plane<T> {
+    type Vtable = bindings::drm_plane_funcs;
+
+    fn vtable(&self) -> *const Self::Vtable {
+        // SAFETY: `as_raw()` always returns a valid plane pointer
+        unsafe { *self.as_raw() }.funcs
+    }
+}
+
+impl<T: DriverPlane> Plane<T> {
+    super::impl_from_opaque_mode_obj! {
+        fn <'a, D>(&'a OpaquePlane<D>) -> &'a Self;
+        use
+            T as DriverPlane,
+            D as KmsDriver<Plane = ...>
+    }
+}
+
+/// A [`Plane`] that has not yet been registered with userspace.
+///
+/// KMS registration is single-threaded, so this object is not thread-safe.
+///
+/// # Invariants
+///
+/// - This object can only exist before its respective KMS device has been registered.
+/// - Otherwise, it inherits all invariants of [`Plane`] and has an identical data layout.
+pub struct UnregisteredPlane<T: DriverPlane>(Plane<T>, NotThreadSafe);
+
+// SAFETY: We share the invariants of `Plane`
+unsafe impl<T: DriverPlane> AsRawPlane for UnregisteredPlane<T> {
+    fn as_raw(&self) -> *mut bindings::drm_plane {
+        self.0.as_raw()
+    }
+
+    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane) -> &'a Self {
+        // SAFETY: This is another from_raw() call, so this function shares the same safety contract
+        let plane = unsafe { Plane::<T>::from_raw(ptr) };
+
+        // SAFETY: Our data layout is identical via our type invariants.
+        unsafe { mem::transmute(plane) }
+    }
+}
+
+impl<T: DriverPlane> Deref for UnregisteredPlane<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0.inner
+    }
+}
+
+impl<T: DriverPlane> UnregisteredPlane<T> {
+    /// Construct a new [`UnregisteredPlane`].
+    ///
+    /// A driver may use this from their [`KmsDriver::create_objects`] callback in order to
+    /// construct new [`UnregisteredPlane`] objects.
+    ///
+    /// [`KmsDriver::create_objects`]: kernel::drm::kms::KmsDriver::create_objects
+    pub fn new<'a, 'b: 'a>(
+        dev: &'a NewKmsDevice<'a, T::Driver, Probing>,
+        possible_crtcs: u32,
+        formats: &[u32],
+        format_modifiers: Option<&[u64]>,
+        type_: Type,
+        name: Option<&CStr>,
+        args: T::Args,
+    ) -> Result<&'b Self> {
+        let this: Pin<KBox<Plane<T>>> = KBox::try_pin_init(
+            try_pin_init!(Plane {
+                plane: Opaque::new(bindings::drm_plane {
+                    helper_private: &T::OPS.helper_funcs,
+                    ..Default::default()
+                }),
+                inner <- T::new(dev, args),
+                _p: PhantomPinned
+            }),
+            GFP_KERNEL,
+        )?;
+
+        // TODO: Move this over to using collect() someday
+        // Create a modifiers array with the sentinel for passing to DRM
+        let format_modifiers_raw;
+        if let Some(modifiers) = format_modifiers {
+            let mut raw = KVec::with_capacity(modifiers.len() + 1, GFP_KERNEL)?;
+            for modifier in modifiers {
+                raw.push(*modifier, GFP_KERNEL)?;
+            }
+            raw.push(FORMAT_MOD_INVALID, GFP_KERNEL)?;
+
+            format_modifiers_raw = Some(raw);
+        } else {
+            format_modifiers_raw = None;
+        }
+
+        // SAFETY:
+        // - `dev` handles destroying the plane, and thus will outlive us and always be valid.
+        // - We just allocated `this`, and we won't move it since it's pinned
+        // - We just allocated the `format_modifiers_raw` vec and added the sentinel DRM expects
+        //   above
+        // - `drm_universal_plane_init` will memcpy() the following parameters into its own storage,
+        //   so it's safe for them to become inaccessible after this call returns:
+        //   - `formats`
+        //   - `format_modifiers_raw`
+        //   - `name`
+        // - `type_` is equivalent to `drm_plane_type` via its type invariants.
+        to_result(unsafe {
+            bindings::drm_universal_plane_init(
+                dev.as_raw(),
+                this.as_raw(),
+                possible_crtcs,
+                &T::OPS.funcs,
+                formats.as_ptr(),
+                formats.len() as _,
+                format_modifiers_raw.map_or(null(), |f| f.as_ptr()),
+                type_ as _,
+                name.map_or(null(), |n| n.as_char_ptr()),
+            )
+        })?;
+
+        // SAFETY: We don't move anything
+        let this = unsafe { Pin::into_inner_unchecked(this) };
+
+        // We'll re-assemble the box in plane_destroy_callback()
+        let this = KBox::into_raw(this);
+
+        // UnregisteredPlane has an equivalent data layout
+        let this: *mut Self = this.cast();
+
+        // SAFETY: We just allocated the plane above, so this pointer must be valid
+        Ok(unsafe { &*this })
+    }
+}
+
+/// A trait implemented by any type that acts as a [`struct drm_plane`] interface.
+///
+/// This is implemented internally by DRM.
+///
+/// # Safety
+///
+/// [`as_raw()`] must always return a valid pointer to an initialized [`struct drm_plane`].
+///
+/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h
+/// [`as_raw()`]: AsRawPlane::as_raw()
+pub unsafe trait AsRawPlane {
+    /// Return the raw `bindings::drm_plane` for this DRM plane.
+    ///
+    /// Drivers should never use this directly.
+    fn as_raw(&self) -> *mut bindings::drm_plane;
+
+    /// Convert a raw `bindings::drm_plane` pointer into an object of this type.
+    ///
+    /// # Safety
+    ///
+    /// Callers promise that `ptr` points to a valid instance of this type
+    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane) -> &'a Self;
+}
+
+// SAFETY:
+// - Via our type variants our data layout starts with `drm_plane`
+// - Since we don't expose `plane` to users before it has been initialized, this and our data
+//   layout ensure that `as_raw()` always returns a valid pointer to a `drm_plane`.
+unsafe impl<T: DriverPlane> AsRawPlane for Plane<T> {
+    fn as_raw(&self) -> *mut bindings::drm_plane {
+        self.plane.get()
+    }
+
+    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane) -> &'a Self {
+        // Our data layout start with `bindings::drm_plane`.
+        let ptr: *mut Self = ptr.cast();
+
+        // SAFETY: Our safety contract requires that `ptr` point to a valid intance of `Self`.
+        unsafe { &*ptr }
+    }
+}
+
+// SAFETY: We only expose this object to users directly after KmsDriver::create_objects has been
+// called.
+unsafe impl<T: DriverPlane> ModesettablePlane for Plane<T> {
+    type State = PlaneState<T::State>;
+}
+
+// SAFETY: We don't expose Plane<T> to users before `base` is initialized in ::new(), so
+// `raw_mode_obj` always returns a valid pointer to a bindings::drm_mode_object.
+unsafe impl<T: DriverPlane> ModeObject for Plane<T> {
+    type Driver = T::Driver;
+
+    fn drm_dev(&self) -> &Device<Self::Driver> {
+        // SAFETY: DRM planes exist for as long as the device does, so this pointer is always valid
+        unsafe { Device::from_raw((*self.as_raw()).dev) }
+    }
+
+    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
+        // SAFETY: We don't expose DRM planes to users before `base` is initialized
+        unsafe { &raw mut (*self.as_raw()).base }
+    }
+}
+
+// SAFETY: Planes do not have a refcount
+unsafe impl<T: DriverPlane> StaticModeObject for Plane<T> {}
+
+// SAFETY: Our interface is thread-safe.
+unsafe impl<T: DriverPlane> Send for Plane<T> {}
+
+// SAFETY: Our interface is thread-safe.
+unsafe impl<T: DriverPlane> Sync for Plane<T> {}
+
+/// A supertrait of [`AsRawPlane`] for [`struct drm_plane`] interfaces that can perform modesets.
+///
+/// This is implemented internally by DRM.
+///
+/// # Safety
+///
+/// Any object implementing this trait must only be made directly available to the user after
+/// [`create_objects`] has completed.
+///
+/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h
+/// [`create_objects`]: KmsDriver::create_objects
+pub unsafe trait ModesettablePlane: AsRawPlane {
+    /// The type that should be returned for a plane state acquired using this plane interface
+    type State: FromRawPlaneState;
+}
+
+/// Common methods available on any type which implements [`AsRawPlane`].
+///
+/// This is implemented internally by DRM, and provides many of the basic methods for working with
+/// planes.
+pub trait RawPlane: AsRawPlane {
+    /// Return the index of this DRM plane
+    #[inline]
+    fn index(&self) -> u32 {
+        // SAFETY:
+        // - The index is initialized by the time we expose planes to users, and does not change
+        //   throughout its lifetime
+        // - `.as_raw()` always returns a valid poiinter.
+        unsafe { *self.as_raw() }.index
+    }
+
+    /// Return the index of this DRM plane in the form of a bitmask
+    #[inline]
+    fn mask(&self) -> u32 {
+        1 << self.index()
+    }
+}
+impl<T: AsRawPlane> RawPlane for T {}
+
+/// A [`struct drm_plane`] without a known [`DriverPlane`] implementation.
+///
+/// This is mainly for situations where our bindings can't infer the [`DriverPlane`] implementation
+/// for a [`struct drm_plane`] automatically. It is identical to [`Plane`], except that it does not
+/// provide access to the driver's private data.
+///
+/// It may be upcasted to a full [`Plane`] using [`Plane::from_opaque`] or
+/// [`Plane::try_from_opaque`].
+///
+/// # Invariants
+///
+/// - `plane` is initialized for as long as this object is made available to users.
+/// - The data layout of this structure is equivalent to [`struct drm_plane`].
+///
+/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h
+#[repr(transparent)]
+pub struct OpaquePlane<T: KmsDriver> {
+    plane: Opaque<bindings::drm_plane>,
+    _p: PhantomData<T>,
+}
+
+impl<T: KmsDriver> Sealed for OpaquePlane<T> {}
+
+// SAFETY:
+// * Via our type variants our data layout is identical to `drm_plane`
+// * Since we don't expose `plane` to users before it has been initialized, this and our data
+//   layout ensure that `as_raw()` always returns a valid pointer to a `drm_plane`.
+unsafe impl<T: KmsDriver> AsRawPlane for OpaquePlane<T> {
+    fn as_raw(&self) -> *mut bindings::drm_plane {
+        self.plane.get()
+    }
+
+    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane) -> &'a Self {
+        // SAFETY: Our data layout is identical to `bindings::drm_plane`
+        unsafe { &*ptr.cast() }
+    }
+}
+
+// SAFETY: We only expose this object to users directly after KmsDriver::create_objects has been
+// called.
+unsafe impl<T: KmsDriver> ModesettablePlane for OpaquePlane<T> {
+    type State = OpaquePlaneState<T>;
+}
+
+// SAFETY: We don't expose OpaquePlane<T> to users before `base` is initialized in
+// Plane::<T>::new(), so `raw_mode_obj` always returns a valid pointer to a
+// bindings::drm_mode_object.
+unsafe impl<T: KmsDriver> ModeObject for OpaquePlane<T> {
+    type Driver = T;
+
+    fn drm_dev(&self) -> &Device<Self::Driver> {
+        // SAFETY: DRM planes exist for as long as the device does, so this pointer is always valid
+        unsafe { Device::from_raw((*self.as_raw()).dev) }
+    }
+
+    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
+        // SAFETY: We don't expose DRM planes to users before `base` is initialized
+        unsafe { &raw mut (*self.as_raw()).base }
+    }
+}
+
+// SAFETY: Planes do not have a refcount
+unsafe impl<T: KmsDriver> StaticModeObject for OpaquePlane<T> {}
+
+// SAFETY: `funcs` is initialized when the plane is allocated
+unsafe impl<T: KmsDriver> ModeObjectVtable for OpaquePlane<T> {
+    type Vtable = bindings::drm_plane_funcs;
+
+    fn vtable(&self) -> *const Self::Vtable {
+        // SAFETY: `as_raw()` always returns a valid pointer to a plane
+        unsafe { *self.as_raw() }.funcs
+    }
+}
+
+// SAFETY: Our plane interfaces are guaranteed to be thread-safe
+unsafe impl<T: KmsDriver> Send for OpaquePlane<T> {}
+unsafe impl<T: KmsDriver> Sync for OpaquePlane<T> {}
+
+/// A trait implemented by any type which can produce a reference to a [`struct drm_plane_state`].
+///
+/// This is implemented internally by DRM.
+///
+/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
+pub trait AsRawPlaneState: private::AsRawPlaneState {
+    /// The type that this plane state interface returns to represent the parent DRM plane
+    type Plane: ModesettablePlane;
+}
+
+pub(crate) mod private {
+    /// Trait for retrieving references to the base plane state contained within any plane state
+    /// compatible type
+    #[allow(unreachable_pub)]
+    pub trait AsRawPlaneState {
+        /// Return an immutable reference to the raw plane state
+        fn as_raw(&self) -> &bindings::drm_plane_state;
+
+        /// Get a mutable reference to the raw `bindings::drm_plane_state` contained within this
+        /// type.
+        ///
+        /// # Safety
+        ///
+        /// The caller promises this mutable reference will not be used to modify any contents of
+        /// `bindings::drm_plane_state` which DRM would consider to be static - like the backpointer
+        /// to the DRM plane that owns this state. This also means the mutable reference should
+        /// never be exposed outside of this crate.
+        unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_plane_state;
+    }
+}
+
+pub(crate) use private::AsRawPlaneState as AsRawPlaneStatePrivate;
+
+/// A trait implemented for any type which can be constructed directly from a
+/// [`struct drm_plane_state`] pointer.
+///
+/// This is implemented internally by DRM.
+///
+/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
+pub trait FromRawPlaneState: AsRawPlaneState {
+    /// Get an immutable reference to this type from the given raw [`struct drm_plane_state`]
+    /// pointer.
+    ///
+    /// # Safety
+    ///
+    /// - The caller guarantees `ptr` is contained within a valid instance of `Self`
+    /// - The caller guarantees that `ptr` cannot not be modified for the lifetime of `'a`.
+    ///
+    /// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
+    unsafe fn from_raw<'a>(ptr: *const bindings::drm_plane_state) -> &'a Self;
+
+    /// Get a mutable reference to this type from the given raw [`struct drm_plane_state`] pointer.
+    ///
+    /// # Safety
+    ///
+    /// - The caller guarantees that `ptr` is contained within a valid instance of `Self`
+    /// - The caller guarantees that `ptr` cannot have any other references taken out for the
+    ///   lifetime of `'a`.
+    ///
+    /// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
+    unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_plane_state) -> &'a mut Self;
+}
+
+/// Common methods available on any type which implements [`AsRawPlane`].
+///
+/// This is implemented internally by DRM, and provides many of the basic methods for working with
+/// the atomic state of [`Plane`]s.
+pub trait RawPlaneState: AsRawPlaneState {
+    /// Return the plane that this plane state belongs to.
+    fn plane(&self) -> &Self::Plane {
+        // SAFETY: The index is initialized by the time we expose Plane objects to users, and is
+        // invariant throughout the lifetime of the Plane
+        unsafe { Self::Plane::from_raw(self.as_raw().plane) }
+    }
+
+    /// Return the current [`OpaqueCrtc`] assigned to this plane, if there is one.
+    fn crtc<'a, 'b: 'a, D>(&'a self) -> Option<&'b OpaqueCrtc<D>>
+    where
+        Self::Plane: ModeObject<Driver = D>,
+        D: KmsDriver,
+    {
+        // SAFETY: This cast is guaranteed safe by `OpaqueCrtc`s invariants.
+        NonNull::new(self.as_raw().crtc).map(|c| unsafe { OpaqueCrtc::from_raw(c.as_ptr()) })
+    }
+
+    /// Run the atomic check helper for this plane and the given CRTC state.
+    fn atomic_helper_check<S, D>(
+        &mut self,
+        crtc_state: &CrtcStateMutator<'_, S>,
+        can_position: bool,
+        can_update_disabled: bool,
+    ) -> Result
+    where
+        D: KmsDriver,
+        S: FromRawCrtcState,
+        S::Crtc: ModesettableCrtc + ModeObject<Driver = D>,
+        Self::Plane: ModeObject<Driver = D>,
+    {
+        // SAFETY: We're passing the mutable reference from `self.as_raw_mut()` directly to DRM,
+        // which is safe.
+        to_result(unsafe {
+            bindings::drm_atomic_helper_check_plane_state(
+                self.as_raw_mut(),
+                crtc_state.as_raw(),
+                bindings::DRM_PLANE_NO_SCALING as _, // TODO: add parameters for scaling
+                bindings::DRM_PLANE_NO_SCALING as _,
+                can_position,
+                can_update_disabled,
+            )
+        })
+    }
+
+    /// Return the framebuffer currently set for this plane state
+    #[inline]
+    fn framebuffer<D>(&self) -> Option<&Framebuffer<D>>
+    where
+        Self::Plane: ModeObject<Driver = D>,
+        D: KmsDriver,
+    {
+        // SAFETY: The layout of Framebuffer<T> is identical to `fb`
+        unsafe {
+            self.as_raw()
+                .fb
+                .as_ref()
+                .map(|fb| Framebuffer::from_raw(fb))
+        }
+    }
+}
+impl<T: AsRawPlaneState + ?Sized> RawPlaneState for T {}
+
+/// The main interface for a [`struct drm_plane_state`].
+///
+/// This type is the main interface for dealing with the atomic state of DRM planes. In addition, it
+/// allows access to whatever private data is contained within an implementor's [`DriverPlaneState`]
+/// type.
+///
+/// # Invariants
+///
+/// - The DRM C API and our interface guarantees that only the user has mutable access to `state`,
+///   up until [`drm_atomic_helper_commit_hw_done`] is called. Therefore, `plane` follows rust's
+///   data aliasing rules and does not need to be behind an [`Opaque`] type.
+/// - `state` and `inner` initialized for as long as this object is exposed to users.
+/// - The data layout of this structure begins with [`struct drm_plane_state`].
+/// - The plane for this atomic state can always be assumed to be of type [`Plane<T::Plane>`].
+///
+/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
+/// [`drm_atomic_helper_commit_hw_done`]: srctree/include/drm/drm_atomic_helper.h
+#[derive(Default)]
+#[repr(C)]
+pub struct PlaneState<T: DriverPlaneState> {
+    state: bindings::drm_plane_state,
+    inner: T,
+}
+
+/// The main trait for implementing the [`struct drm_plane_state`] API for a [`Plane`].
+///
+/// A driver may store driver-private data within the implementor's type, which will be available
+/// when using a full typed [`PlaneState`] object.
+///
+/// # Invariants
+///
+/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in
+///   [`struct drm_plane`] pointers are contained within a [`Plane<Self::Plane>`].
+/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in
+///   [`struct drm_plane_state`] pointers are contained within a [`PlaneState<Self>`].
+///
+/// [`struct drm_plane`]: srctree/include/drm_plane.h
+/// [`struct drm_plane_state`]: srctree/include/drm_plane.h
+pub trait DriverPlaneState: Clone + Default + Sized {
+    /// The type for this driver's drm_plane implementation
+    type Plane: DriverPlane;
+}
+
+impl<T: DriverPlaneState> Sealed for PlaneState<T> {}
+
+impl<T: DriverPlaneState> AsRawPlaneState for PlaneState<T> {
+    type Plane = Plane<T::Plane>;
+}
+
+impl<T: DriverPlaneState> private::AsRawPlaneState for PlaneState<T> {
+    fn as_raw(&self) -> &bindings::drm_plane_state {
+        &self.state
+    }
+
+    unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_plane_state {
+        &mut self.state
+    }
+}
+
+impl<T: DriverPlaneState> FromRawPlaneState for PlaneState<T> {
+    unsafe fn from_raw<'a>(ptr: *const bindings::drm_plane_state) -> &'a Self {
+        // Our data layout starts with `bindings::drm_plane_state`.
+        let ptr: *const Self = ptr.cast();
+
+        // SAFETY:
+        // - Our safety contract requires that `ptr` be contained within `Self`.
+        // - Our safety contract requires the caller ensure that it is safe for us to take an
+        //   immutable reference.
+        unsafe { &*ptr }
+    }
+
+    unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_plane_state) -> &'a mut Self {
+        // Our data layout starts with `bindings::drm_plane_state`.
+        let ptr: *mut Self = ptr.cast();
+
+        // SAFETY:
+        // - Our safety contract requires that `ptr` be contained within `Self`.
+        // - Our safety contract requires the caller ensure it is safe for us to take a mutable
+        //   reference.
+        unsafe { &mut *ptr }
+    }
+}
+
+impl<T: DriverPlaneState> Deref for PlaneState<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl<T: DriverPlaneState> DerefMut for PlaneState<T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.inner
+    }
+}
+
+// SAFETY: Shares the safety guarantee of Plane<T>'s vtable impl
+unsafe impl<T: DriverPlaneState> ModeObjectVtable for PlaneState<T> {
+    type Vtable = bindings::drm_plane_funcs;
+
+    fn vtable(&self) -> *const Self::Vtable {
+        self.plane().vtable()
+    }
+}
+
+impl<T: DriverPlaneState> PlaneState<T> {
+    super::impl_from_opaque_mode_obj! {
+        fn <'a, D, P>(&'a OpaquePlaneState<D>) -> &'a Self
+        where
+            T: DriverPlaneState<Plane = P>;
+        use
+            P as DriverPlane,
+            D as KmsDriver<Plane = ...>
+    }
+}
+
+/// A [`struct drm_plane_state`] without a known [`DriverPlaneState`] implementation.
+///
+/// This is mainly for situations where our bindings can't infer the [`DriverPlaneState`]
+/// implementation for a [`struct drm_plane_state`] automatically. It is identical to [`Plane`],
+/// except that it does not provide access to the driver's private data.
+///
+/// # Invariants
+///
+/// - The DRM C API and our interface guarantees that only the user has mutable access to `state`,
+///   up until [`drm_atomic_helper_commit_hw_done`] is called. Therefore, `plane` follows rust's
+///   data aliasing rules and does not need to be behind an [`Opaque`] type.
+/// - `state` is initialized for as long as this object is exposed to users.
+/// - The data layout of this structure is identical to [`struct drm_plane_state`].
+///
+/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
+/// [`drm_atomic_helper_commit_hw_done`]: srctree/include/drm/drm_atomic_helper.h
+#[repr(transparent)]
+pub struct OpaquePlaneState<T: KmsDriver> {
+    state: bindings::drm_plane_state,
+    _p: PhantomData<T>,
+}
+
+impl<T: KmsDriver> AsRawPlaneState for OpaquePlaneState<T> {
+    type Plane = OpaquePlane<T>;
+}
+
+impl<T: KmsDriver> private::AsRawPlaneState for OpaquePlaneState<T> {
+    fn as_raw(&self) -> &bindings::drm_plane_state {
+        &self.state
+    }
+
+    unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_plane_state {
+        &mut self.state
+    }
+}
+
+impl<T: KmsDriver> FromRawPlaneState for OpaquePlaneState<T> {
+    unsafe fn from_raw<'a>(ptr: *const bindings::drm_plane_state) -> &'a Self {
+        // SAFETY: Our data layout is identical to `ptr`
+        unsafe { &*ptr.cast() }
+    }
+
+    unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_plane_state) -> &'a mut Self {
+        // SAFETY: Our data layout is identical to `ptr`
+        unsafe { &mut *ptr.cast() }
+    }
+}
+
+// SAFETY: Shares the safety guarantee of OpaquePlane<T>'s vtable impl
+unsafe impl<T: KmsDriver> ModeObjectVtable for OpaquePlaneState<T> {
+    type Vtable = bindings::drm_plane_funcs;
+
+    fn vtable(&self) -> *const Self::Vtable {
+        self.plane().vtable()
+    }
+}
+
+/// An interface for mutating a [`Plane`]s atomic state.
+///
+/// This type is typically returned by an [`AtomicStateMutator`] within contexts where it is
+/// possible to safely mutate a plane's state. In order to uphold rust's data-aliasing rules, only
+/// [`PlaneStateMutator`] may exist at a time.
+pub struct PlaneStateMutator<'a, T: FromRawPlaneState> {
+    state: &'a mut T,
+    mask: &'a Cell<u32>,
+}
+
+impl<'a, T: FromRawPlaneState> PlaneStateMutator<'a, T> {
+    pub(super) fn new<D: KmsDriver>(
+        mutator: &'a AtomicStateMutator<D>,
+        state: NonNull<bindings::drm_plane_state>,
+    ) -> Option<Self> {
+        // SAFETY: `plane` is invariant throughout the lifetime of the atomic state, is
+        // initialized by this point, and we're guaranteed it is of type `AsRawPlane` by type
+        // invariance
+        let plane = unsafe { T::Plane::from_raw((*state.as_ptr()).plane) };
+        let plane_mask = plane.mask();
+        let borrowed_mask = mutator.borrowed_planes.get();
+
+        if borrowed_mask & plane_mask == 0 {
+            mutator.borrowed_planes.set(borrowed_mask | plane_mask);
+            Some(Self {
+                mask: &mutator.borrowed_planes,
+                // SAFETY: We're guaranteed `state` is of `FromRawPlaneState` by type invariance,
+                // and we just confirmed by checking `borrowed_planes` that no other mutable borrows
+                // have been taken out for `state`
+                state: unsafe { T::from_raw_mut(state.as_ptr()) },
+            })
+        } else {
+            None
+        }
+    }
+}
+
+impl<'a, T: FromRawPlaneState> Drop for PlaneStateMutator<'a, T> {
+    fn drop(&mut self) {
+        let mask = self.state.plane().mask();
+        self.mask.set(self.mask.get() & !mask);
+    }
+}
+
+impl<'a, T: FromRawPlaneState> AsRawPlaneState for PlaneStateMutator<'a, T> {
+    type Plane = T::Plane;
+}
+
+impl<'a, T: FromRawPlaneState> private::AsRawPlaneState for PlaneStateMutator<'a, T> {
+    fn as_raw(&self) -> &bindings::drm_plane_state {
+        self.state.as_raw()
+    }
+
+    unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_plane_state {
+        // SAFETY: This function is bound by the same safety contract as `self.inner.as_raw_mut()`
+        unsafe { self.state.as_raw_mut() }
+    }
+}
+
+impl<'a, T: FromRawPlaneState> Sealed for PlaneStateMutator<'a, T> {}
+
+impl<'a, T: DriverPlaneState> Deref for PlaneStateMutator<'a, PlaneState<T>> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.state.inner
+    }
+}
+
+impl<'a, T: DriverPlaneState> DerefMut for PlaneStateMutator<'a, PlaneState<T>> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.state.inner
+    }
+}
+
+// SAFETY: Shares the safety guarantees of `T`'s ModeObjectVtable impl
+unsafe impl<'a, T: FromRawPlaneState> ModeObjectVtable for PlaneStateMutator<'a, T>
+where
+    T: FromRawPlaneState + ModeObjectVtable,
+{
+    type Vtable = T::Vtable;
+
+    fn vtable(&self) -> *const Self::Vtable {
+        self.state.vtable()
+    }
+}
+
+impl<'a, T: DriverPlaneState> PlaneStateMutator<'a, PlaneState<T>> {
+    super::impl_from_opaque_mode_obj! {
+        fn <D, P>(PlaneStateMutator<'a, OpaquePlaneState<D>>) -> Self
+        where
+            T: DriverPlaneState<Plane = P>;
+        use
+            P as DriverPlane,
+            D as KmsDriver<Plane = ...>
+    }
+}
+
+/// A token provided during [`atomic_check`] callbacks for accessing the plane, atomic state, and
+/// the old and new states of the plane.
+///
+/// [`atomic_check`]: DriverPlane::atomic_check
+pub struct PlaneAtomicCheck<'a, T: DriverPlane> {
+    state: &'a AtomicStateComposer<T::Driver>,
+    plane: &'a Plane<T>,
+}
+
+impl<'a, T: DriverPlane> PlaneAtomicCheck<'a, T> {
+    impl_atomic_state_token_ops!(
+        PlaneAtomicCheck,
+        AtomicStateComposer,
+        Plane,
+        use <'a, T>
+    );
+}
+
+/// A token provided to [`DriverPlane`] callbacks during the atomic commit phase for accessing the
+/// plane, atomic state, new and old states of the plane.
+///
+/// # Invariants
+///
+/// This token is proof that the old and new atomic state of `plane` are present in `state` and do
+/// not have any mutators taken out.
+pub struct PlaneAtomicCommit<'a, T: DriverPlane> {
+    state: &'a AtomicStateMutator<T::Driver>,
+    plane: &'a Plane<T>,
+}
+
+impl<'a, T: DriverPlane> PlaneAtomicCommit<'a, T> {
+    impl_atomic_state_token_ops!(
+        PlaneAtomicCommit,
+        AtomicStateMutator,
+        Plane,
+        use <'a, T>
+    );
+}
+
+unsafe extern "C" fn plane_destroy_callback<T: DriverPlane>(plane: *mut bindings::drm_plane) {
+    // SAFETY: DRM guarantees that `plane` points to a valid initialized `drm_plane`.
+    unsafe { bindings::drm_plane_cleanup(plane) };
+
+    // SAFETY:
+    // - DRM guarantees we are now the only one with access to this [`drm_plane`].
+    // - This cast is safe via `DriverPlane`s type invariants.
+    drop(unsafe { KBox::from_raw(plane as *mut Plane<T>) });
+}
+
+unsafe extern "C" fn atomic_duplicate_state_callback<T: DriverPlaneState>(
+    plane: *mut bindings::drm_plane,
+) -> *mut bindings::drm_plane_state {
+    // SAFETY: DRM guarantees that `plane` points to a valid initialized `drm_plane`.
+    let state = unsafe { (*plane).state };
+    if state.is_null() {
+        return null_mut();
+    }
+
+    // SAFETY: This cast is safe via `DriverPlaneState`s type invariants.
+    let state = unsafe { PlaneState::<T>::from_raw(state) };
+
+    let new: Result<KBox<_>> = KBox::try_init(
+        try_init!(PlaneState {
+            inner: state.inner.clone(),
+            state: bindings::drm_plane_state {
+                ..Default::default()
+            },
+        }),
+        GFP_KERNEL,
+    );
+
+    if let Ok(mut new) = new {
+        // SAFETY:
+        // - `new` provides a valid pointer to a newly allocated `drm_plane_state` via type
+        //   invariants
+        // - This initializes `new` via memcpy()
+        unsafe { bindings::__drm_atomic_helper_plane_duplicate_state(plane, new.as_raw_mut()) };
+
+        KBox::into_raw(new).cast()
+    } else {
+        null_mut()
+    }
+}
+
+unsafe extern "C" fn atomic_destroy_state_callback<T: DriverPlaneState>(
+    _plane: *mut bindings::drm_plane,
+    state: *mut bindings::drm_plane_state,
+) {
+    // SAFETY: DRM guarantees that `state` points to a valid instance of `drm_plane_state`
+    unsafe { bindings::__drm_atomic_helper_plane_destroy_state(state) };
+
+    // SAFETY:
+    // * DRM guarantees we are the only one with access to this `drm_plane_state`
+    // * This cast is safe via our type invariants.
+    drop(unsafe { KBox::from_raw(state.cast::<PlaneState<T>>()) });
+}
+
+unsafe extern "C" fn plane_reset_callback<T: DriverPlane>(plane: *mut bindings::drm_plane) {
+    // SAFETY: DRM guarantees that `state` points to a valid instance of `drm_plane_state`
+    let state = unsafe { (*plane).state };
+    if !state.is_null() {
+        // SAFETY:
+        // - We're guaranteed `plane` is `Plane<T>` via type invariants
+        // - We're guaranteed `state` is `PlaneState<T>` via type invariants.
+        unsafe { atomic_destroy_state_callback::<T::State>(plane, state) }
+
+        // SAFETY: No special requirements here, DRM expects this to be NULL
+        unsafe {
+            (*plane).state = null_mut();
+        }
+    }
+
+    // Unfortunately, this is the best we can do at the moment as this FFI callback was mistakenly
+    // presumed to be infallible :(
+    let new =
+        KBox::new(PlaneState::<T::State>::default(), GFP_KERNEL).expect("Blame the API, sorry!");
+
+    // DRM takes ownership of the state from here, resets it, and then assigns it to the plane
+    // SAFETY:
+    // - DRM guarantees that `plane` points to a valid instance of `drm_plane`.
+    // - The cast to `drm_plane_state` is safe via `PlaneState`s type invariants.
+    unsafe { bindings::__drm_atomic_helper_plane_reset(plane, KBox::into_raw(new).cast()) };
+}
+
+unsafe extern "C" fn atomic_update_callback<T: DriverPlane>(
+    plane: *mut bindings::drm_plane,
+    state: *mut bindings::drm_atomic_state,
+) {
+    // SAFETY:
+    // - We're guaranteed `plane` is of type `Plane<T>` via type invariants.
+    // - We're guaranteed by DRM that `plane` is pointing to a valid initialized state.
+    let plane = unsafe { Plane::from_raw(plane) };
+
+    // SAFETY: DRM guarantees `state` points to a valid `drm_atomic_state`
+    let state = unsafe { AtomicStateMutator::new(NonNull::new_unchecked(state)) };
+
+    // SAFETY:
+    // - Since we're in the atomic_update callback, we're guaranteed by DRM that both the old and new
+    //   plane state are resent in this atomic state.
+    // - We just created the state mutator above, so other mutators cannot be taken out on the plane
+    //   state yet.
+    let commit = unsafe { PlaneAtomicCommit::new(plane, &state) };
+
+    T::atomic_update(commit);
+}
+
+unsafe extern "C" fn atomic_check_callback<T: DriverPlane>(
+    plane: *mut bindings::drm_plane,
+    state: *mut bindings::drm_atomic_state,
+) -> i32 {
+    // SAFETY:
+    // - We're guaranteed `plane` is of type `Plane<T>` via type invariants.
+    // - We're guaranteed by DRM that `plane` is pointing to a valid initialized state.
+    let plane = unsafe { Plane::from_raw(plane) };
+
+    // SAFETY: We're guaranteed by DRM that `state` points to a valid instance of `drm_atomic_state`
+    // We use ManuallyDrop here since AtomicStateComposer would otherwise drop a owned reference to
+    // the atomic state upon finishing this callback.
+    let state = ManuallyDrop::new(unsafe {
+        AtomicStateComposer::<T::Driver>::new(NonNull::new_unchecked(state))
+    });
+
+    // SAFETY:
+    // - Since we're in the atomic check callback, we're guaranteed by DRM that both the old and
+    //   new plane state are present in this atomic state
+    // - We just created the state composer above, so other composers cannot be taken out on the
+    //   plane state yet.
+    let check = unsafe { PlaneAtomicCheck::new(plane, &state) };
+
+    from_result(|| T::atomic_check(check).map(|_| 0))
+}
diff --git a/rust/kernel/drm/kms/vblank.rs b/rust/kernel/drm/kms/vblank.rs
new file mode 100644
index 000000000000..dc34e02e8ccb
--- /dev/null
+++ b/rust/kernel/drm/kms/vblank.rs
@@ -0,0 +1,461 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! DRM KMS vblank support.
+//!
+//! C header: [`include/drm/drm_vblank.h`](srcfree/include/drm/drm_vblank.h)
+
+use super::{crtc::*, ModeObject, modes::*, Sealed};
+use bindings;
+use core::{
+    marker::*,
+    mem::{self, ManuallyDrop},
+    ops::{Deref, Drop},
+    ptr::null_mut,
+};
+use kernel::{
+    drm::device::Device,
+    error::{from_result, to_result},
+    interrupt::LocalInterruptDisabled,
+    prelude::*,
+    time::Delta,
+    types::Opaque,
+};
+
+/// The main trait for a driver to implement hardware vblank support for a [`Crtc`].
+///
+/// # Invariants
+///
+/// C FFI callbacks generated using this trait can safely assume that input pointers to
+/// [`struct drm_crtc`] are always contained within a [`Crtc<Self::Crtc>`].
+///
+/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
+pub trait VblankSupport: Sized {
+    /// The parent [`DriverCrtc`].
+    type Crtc: VblankDriverCrtc<VblankImpl = Self>;
+
+    /// Enable vblank interrupts for this [`DriverCrtc`].
+    fn enable_vblank(
+        crtc: &Crtc<Self::Crtc>,
+        vblank_guard: &VblankGuard<'_, Self::Crtc>,
+        irq: &LocalInterruptDisabled,
+    ) -> Result;
+
+    /// Disable vblank interrupts for this [`DriverCrtc`].
+    fn disable_vblank(
+        crtc: &Crtc<Self::Crtc>,
+        vblank_guard: &VblankGuard<'_, Self::Crtc>,
+        irq: &LocalInterruptDisabled,
+    );
+
+    /// Retrieve the current vblank timestamp for this [`Crtc`]
+    ///
+    /// If this function is being called from the driver's vblank interrupt handler,
+    /// `handling_vblank_irq` will be `true`.
+    fn get_vblank_timestamp(
+        crtc: &Crtc<Self::Crtc>,
+        in_vblank_irq: bool,
+    ) -> Option<VblankTimestamp>;
+}
+
+/// Trait used for CRTC vblank (or lack there-of) implementations. Implemented internally.
+///
+/// Drivers interested in implementing vblank support should refer to [`VblankSupport`], drivers
+/// that don't have vblank support can use [`PhantomData`].
+pub trait VblankImpl {
+    /// The parent [`DriverCrtc`].
+    type Crtc: DriverCrtc<VblankImpl = Self>;
+
+    /// The generated [`VblankOps`].
+    const VBLANK_OPS: VblankOps;
+}
+
+/// C FFI callbacks for vblank management.
+///
+/// Created internally by DRM.
+#[derive(Default)]
+pub struct VblankOps {
+    pub(crate) enable_vblank: Option<unsafe extern "C" fn(crtc: *mut bindings::drm_crtc) -> i32>,
+    pub(crate) disable_vblank: Option<unsafe extern "C" fn(crtc: *mut bindings::drm_crtc)>,
+    pub(crate) get_vblank_timestamp: Option<
+        unsafe extern "C" fn(
+            crtc: *mut bindings::drm_crtc,
+            max_error: *mut i32,
+            vblank_time: *mut bindings::ktime_t,
+            in_vblank_irq: bool,
+        ) -> bool,
+    >,
+}
+
+impl<T: VblankSupport> VblankImpl for T {
+    type Crtc = T::Crtc;
+
+    const VBLANK_OPS: VblankOps = VblankOps {
+        enable_vblank: Some(enable_vblank_callback::<T>),
+        disable_vblank: Some(disable_vblank_callback::<T>),
+        get_vblank_timestamp: Some(get_vblank_timestamp_callback::<T>),
+    };
+}
+
+impl<T> VblankImpl for PhantomData<T>
+where
+    T: DriverCrtc<VblankImpl = PhantomData<T>>,
+{
+    type Crtc = T;
+
+    const VBLANK_OPS: VblankOps = VblankOps {
+        enable_vblank: None,
+        disable_vblank: None,
+        get_vblank_timestamp: None,
+    };
+}
+
+unsafe extern "C" fn enable_vblank_callback<T: VblankSupport>(
+    crtc: *mut bindings::drm_crtc,
+) -> i32 {
+    // SAFETY: We're guaranteed that `crtc` is of type `Crtc<T::Crtc>` by type invariants.
+    let crtc = unsafe { Crtc::<T::Crtc>::from_raw(crtc) };
+
+    // SAFETY: This callback happens with IRQs disabled
+    let irq = unsafe { LocalInterruptDisabled::assume_disabled() };
+
+    // SAFETY: This callback happens with `vbl_lock` already held
+    // We don't want to drop `vbl_lock` when this callback completes since DRM will do this for us,
+    // so wrap the `VblankGuard` in a `ManuallyDrop`
+    let vblank_guard = ManuallyDrop::new(unsafe { VblankGuard::new(crtc, irq) });
+
+    from_result(|| T::enable_vblank(crtc, &vblank_guard, irq).map(|_| 0))
+}
+
+unsafe extern "C" fn disable_vblank_callback<T: VblankSupport>(crtc: *mut bindings::drm_crtc) {
+    // SAFETY: We're guaranteed that `crtc` is of type `Crtc<T::Crtc>` by type invariants.
+    let crtc = unsafe { Crtc::<T::Crtc>::from_raw(crtc) };
+
+    // SAFETY: This callback happens with IRQs disabled
+    let irq = unsafe { LocalInterruptDisabled::assume_disabled() };
+
+    // SAFETY: This call happens with `vbl_lock` already held
+    // We don't want to drop `vbl_lock` when this callback completes since DRM will do this for us,
+    // so wrap the `VblankGuard` in a `ManuallyDrop`
+    let vblank_guard = ManuallyDrop::new(unsafe { VblankGuard::new(crtc, irq) });
+
+    T::disable_vblank(crtc, &vblank_guard, irq);
+}
+
+unsafe extern "C" fn get_vblank_timestamp_callback<T: VblankSupport>(
+    crtc: *mut bindings::drm_crtc,
+    max_error: *mut i32,
+    vblank_time: *mut bindings::ktime_t,
+    in_vblank_irq: bool,
+) -> bool {
+    // SAFETY: We're guaranteed `crtc` is of type `Crtc<T::Crtc>` by type invariance
+    let crtc = unsafe { Crtc::<T::Crtc>::from_raw(crtc) };
+
+    if let Some(timestamp) = T::get_vblank_timestamp(crtc, in_vblank_irq) {
+        // SAFETY: Both of these pointers are guaranteed by the C API to be valid
+        unsafe {
+            (*max_error) = timestamp.max_error;
+            (*vblank_time) = timestamp.time.as_nanos();
+        };
+
+        true
+    } else {
+        false
+    }
+}
+
+/// A vblank timestamp.
+///
+/// This type is used by [`VblankSupport::get_vblank_timestamp`] for the implementor to return the
+/// current vblank timestamp for the hardware.
+#[derive(Copy, Clone)]
+pub struct VblankTimestamp {
+    /// The actual vblank timestamp in nanoseconds, accuracy to within [`Self::max_error`]
+    /// nanoseconds.
+    pub time: Delta,
+
+    /// Maximum allowable timestamp error in nanoseconds
+    pub max_error: i32,
+}
+
+/// A trait for [`DriverCrtc`] implementations with hardware vblank support.
+///
+/// This trait is implemented internally by DRM for any [`DriverCrtc`] implementation that
+/// implements [`VblankSupport`]. It is used to expose hardware-vblank driver exclusive methods and
+/// data to users.
+pub trait VblankDriverCrtc: DriverCrtc {}
+
+impl<T, V> VblankDriverCrtc for T
+where
+    T: DriverCrtc<VblankImpl = V>,
+    V: VblankSupport<Crtc = T>,
+{
+}
+
+impl<T: VblankDriverCrtc> Crtc<T> {
+    /// Retrieve a reference to the [`VblankCrtc`] for this [`Crtc`].
+    pub(crate) fn vblank_crtc(&self) -> &VblankCrtc<T> {
+        // SAFETY:
+        // - The data layouts of these types are equivalent via `VblankCrtc`s type invariants
+        // - We don't expose any way of calling `vblank_crtc()` before `drm_vblank_init()` has been
+        //   called.
+        unsafe { VblankCrtc::from_raw(self.get_vblank_ptr()) }
+    }
+
+    /// Access vblank related infrastructure for a [`Crtc`].
+    ///
+    /// This function explicitly locks the device's vblank lock, and allows access to controlling
+    /// the vblank configuration for this CRTC. The lock is dropped once [`VblankGuard`] is
+    /// dropped.
+    pub fn vblank_lock<'a>(&'a self, irq: &'a LocalInterruptDisabled) -> VblankGuard<'a, T> {
+        // SAFETY: `vbl_lock` is initialized for as long as `Crtc` is available to users
+        // INVARIANT: We just acquired `vbl_lock`, fulfilling the invariants of `VblankGuard`
+        unsafe { bindings::spin_lock(&raw mut (*self.drm_dev().as_raw()).vbl_lock) };
+
+        // SAFETY: We just acquired vbl_lock above
+        unsafe { VblankGuard::new(self, irq) }
+    }
+
+    /// Trigger a vblank event on this [`Crtc`].
+    ///
+    /// Drivers should use this in their vblank interrupt handlers to update the vblank counter and
+    /// send any signals that may be pending.
+    ///
+    /// Returns whether or not the vblank event was handled.
+    #[inline]
+    pub fn handle_vblank(&self) -> bool {
+        // SAFETY: `as_raw()` always returns a valid pointer to an initialized drm_crtc.
+        unsafe { bindings::drm_crtc_handle_vblank(self.as_raw()) }
+    }
+
+    /// Forbid vblank events for a [`Crtc`].
+    ///
+    /// This function disables vblank events for a [`Crtc`], even if [`VblankRef`] objects exist.
+    #[inline]
+    pub fn vblank_off(&self) {
+        // SAFETY: `as_raw()` always returns a valid pointer to an initialized drm_crtc.
+        unsafe { bindings::drm_crtc_vblank_off(self.as_raw()) }
+    }
+
+    /// Allow vblank events for a [`Crtc`].
+    ///
+    /// This function allows users to enable vblank events and acquire [`VblankRef`] objects again.
+    #[inline]
+    pub fn vblank_on(&self) {
+        // SAFETY: `as_raw()` always returns a valid pointer to an initialized drm_crtc.
+        unsafe { bindings::drm_crtc_vblank_on(self.as_raw()) }
+    }
+
+    /// Enable vblank events for a [`Crtc`].
+    ///
+    /// Returns a [`VblankRef`] which will allow vblank events to be sent until it is dropped. Note
+    /// that vblank events may still be disabled by [`Self::vblank_off`].
+    #[must_use = "Vblanks are only enabled until the result from this function is dropped"]
+    pub fn vblank_get(&self) -> Result<VblankRef<'_, T>> {
+        VblankRef::new(self)
+    }
+}
+
+/// Common methods available on any [`CrtcState`] whose [`Crtc`] implements [`VblankSupport`].
+///
+/// This trait is implemented automatically by DRM for any [`DriverCrtc`] implementation that
+/// implements [`VblankSupport`].
+pub trait RawVblankCrtcState: AsRawCrtcState {
+    /// Return the [`PendingVblankEvent`] for this CRTC state, if there is one.
+    fn get_pending_vblank_event(&mut self) -> Option<PendingVblankEvent<'_, Self>>
+    where
+        Self: Sized,
+    {
+        // SAFETY: The driver is the only one that will ever modify this data, and since our
+        // interface follows rust's data aliasing rules that means this is safe to read
+        let event_ptr = unsafe { *self.as_raw() }.event;
+
+        (!event_ptr.is_null()).then_some(PendingVblankEvent(self))
+    }
+}
+
+impl<T, C> RawVblankCrtcState for T
+where
+    T: AsRawCrtcState<Crtc = Crtc<C>>,
+    C: VblankDriverCrtc,
+{
+}
+
+/// A pending vblank event from an atomic state
+pub struct PendingVblankEvent<'a, T: RawVblankCrtcState>(&'a mut T);
+
+impl<'a, T: RawVblankCrtcState> PendingVblankEvent<'a, T> {
+    /// Send this [`PendingVblankEvent`].
+    ///
+    /// A [`PendingVblankEvent`] can only be sent once, so this function consumes the
+    /// [`PendingVblankEvent`].
+    pub fn send<C>(self)
+    where
+        T: RawVblankCrtcState<Crtc = Crtc<C>>,
+        C: VblankDriverCrtc,
+    {
+        let crtc: &Crtc<C> = self.0.crtc();
+        let event_lock = crtc.drm_dev().event_lock();
+        let _guard = event_lock.lock();
+
+        // SAFETY:
+        // - We now hold the appropriate lock to call this function
+        // - Vblanks are enabled as proved by `vbl_ref`, as per the C api requirements
+        // - Our interface is proof that `event` is non-null
+        unsafe { bindings::drm_crtc_send_vblank_event(crtc.as_raw(), (*self.0.as_raw()).event) };
+
+        // SAFETY: The mutable reference in `self.state` is proof that it is safe to mutate this,
+        // and DRM expects us to set this to NULL once we've sent the vblank event.
+        unsafe { (*self.0.as_raw()).event = null_mut() };
+    }
+
+    /// Arm this [`PendingVblankEvent`] to be sent later by the CRTC's vblank interrupt handler.
+    ///
+    /// A [`PendingVblankEvent`] can only be armed once, so this function consumes the
+    /// [`PendingVblankEvent`]. As well, it requires a [`VblankRef`] so that vblank interrupts
+    /// remain enabled until the [`PendingVblankEvent`] has been sent out by the driver's vblank
+    /// interrupt handler.
+    pub fn arm<C>(self, vbl_ref: VblankRef<'_, C>)
+    where
+        T: RawVblankCrtcState<Crtc = Crtc<C>>,
+        C: VblankDriverCrtc,
+    {
+        let crtc: &Crtc<C> = self.0.crtc();
+        let event_lock = crtc.drm_dev().event_lock();
+        let _guard = event_lock.lock();
+
+        // SAFETY:
+        // - We now hold the appropriate lock to call this function
+        // - Vblanks are enabled as proved by `vbl_ref`, as per the C api requirements
+        // - Our interface is proof that `event` is non-null
+        unsafe { bindings::drm_crtc_arm_vblank_event(crtc.as_raw(), (*self.0.as_raw()).event) };
+
+        // SAFETY: The mutable reference in `self.state` is proof that it is safe to mutate this,
+        // and DRM expects us to set this to NULL once we've armed the vblank event.
+        unsafe { (*self.0.as_raw()).event = null_mut() };
+
+        // DRM took ownership of `vbl_ref` after we called `drm_crtc_arm_vblank_event`
+        mem::forget(vbl_ref);
+    }
+}
+
+/// A borrowed vblank reference.
+///
+/// This object keeps the vblank reference count for a [`Crtc`] incremented for as long as it
+/// exists, enabling vblank interrupts for said [`Crtc`] until all references are dropped, or
+/// [`Crtc::vblank_off`] is called - whichever comes first.
+pub struct VblankRef<'a, T: VblankDriverCrtc>(&'a Crtc<T>);
+
+impl<T: VblankDriverCrtc> Drop for VblankRef<'_, T> {
+    fn drop(&mut self) {
+        // SAFETY: as_raw() returns a valid pointer to an initialized drm_crtc
+        unsafe { bindings::drm_crtc_vblank_put(self.0.as_raw()) };
+    }
+}
+
+impl<'a, T: VblankDriverCrtc> VblankRef<'a, T> {
+    fn new(crtc: &'a Crtc<T>) -> Result<Self> {
+        // SAFETY: as_raw() returns a valid pointer to an initialized drm_crtc
+        to_result(unsafe { bindings::drm_crtc_vblank_get(crtc.as_raw()) })?;
+
+        Ok(Self(crtc))
+    }
+}
+
+/// The base wrapper for [`drm_vblank_crtc`].
+///
+/// Users will rarely interact with this object directly, it is a simple wrapper around
+/// [`drm_vblank_crtc`] which provides access to methods and data that is not protected by a lock.
+///
+/// # Invariants
+///
+/// This type has an identical data layout to [`drm_vblank_crtc`].
+///
+/// [`drm_vblank_crtc`]: srctree/include/drm/drm_vblank.h
+#[repr(transparent)]
+pub struct VblankCrtc<T>(Opaque<bindings::drm_vblank_crtc>, PhantomData<T>);
+
+impl<T: VblankDriverCrtc> VblankCrtc<T> {
+    pub(crate) fn as_raw(&self) -> *mut bindings::drm_vblank_crtc {
+        self.0.get()
+    }
+
+    // SAFETY: The caller promises that `ptr` points to a valid instance of
+    // `bindings::drm_vblank_crtc`, and that access to this structure has been properly serialized
+    pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::drm_vblank_crtc) -> &'a Self {
+        // SAFETY: Our data layouts are identical via #[repr(transparent)]
+        unsafe { &*ptr.cast() }
+    }
+
+    /// Returns the [`Device`] for this [`VblankGuard`]
+    pub fn drm_dev(&self) -> &Device<T::Driver> {
+        // SAFETY: `drm` is initialized, invariant and valid throughout our lifetime
+        unsafe { Device::from_raw((*self.as_raw()).dev) }
+    }
+}
+
+// NOTE: This type does not use a `Guard` because the mutex is not contained within the same
+// structure as the relevant CRTC
+/// An interface for accessing and controlling vblank related state for a [`Crtc`].
+///
+/// This type may be returned from some [`VblankSupport`] callbacks, or manually via
+/// [`Crtc::vblank_lock`]. It provides access to methods and data which require
+/// [`drm_device.vbl_lock`] be held.
+///
+/// # Invariants
+///
+/// - [`drm_device.vbl_lock`] is acquired whenever an instance of this type exists.
+/// - Shares the invariants of [`VblankCrtc`].
+///
+/// [`drm_device.vbl_lock`]: srctree/include/drm/drm_device.h
+#[repr(transparent)]
+pub struct VblankGuard<'a, T: VblankDriverCrtc>(&'a VblankCrtc<T>);
+
+impl<'a, T: VblankDriverCrtc> VblankGuard<'a, T> {
+    /// Construct a new [`VblankGuard`]
+    ///
+    /// # Safety
+    ///
+    /// The caller must have already acquired [`drm_device.vbl_lock`].
+    ///
+    /// [`drm_device.vbl_lock`]: srctree/include/drm/drm_device.h
+    pub(crate) unsafe fn new(crtc: &'a Crtc<T>, _irq: &'a LocalInterruptDisabled) -> Self {
+        // INVARIANT: The caller promises that we've acquired `vbl_lock`
+        Self(crtc.vblank_crtc())
+    }
+
+    /// Returns the duration of a single scanout frame in ns.
+    pub fn frame_duration(&self) -> i32 {
+        // SAFETY: We hold the appropriate lock for this read via our type invariants.
+        unsafe { *self.as_raw() }.framedur_ns
+    }
+
+    /// Return the vblank core's cached copy of the currently set display mode.
+    ///
+    /// If the display is disabled, this will return `None`.
+    pub fn hwmode(&self) -> Option<&DisplayMode> {
+        // SAFETY: We hold the appropriate lock for this read via our type invariants.
+        let ptr = unsafe { &raw const (*self.as_raw()).hwmode };
+
+        // SAFETY: We check here if the cached DisplayMode is Null, which means the only other
+        // possibility is that the pointer points to a valid initialized drm_display_mode.
+        (!ptr.is_null()).then(|| unsafe { DisplayMode::as_ref(ptr) })
+    }
+}
+
+impl<T: VblankDriverCrtc> Deref for VblankGuard<'_, T> {
+    type Target = VblankCrtc<T>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<T: VblankDriverCrtc> Drop for VblankGuard<'_, T> {
+    fn drop(&mut self) {
+        // SAFETY:
+        // - We acquired this spinlock when creating this object
+        // - This lock is guaranteed to be initialized for as long as our DRM device is exposed to
+        //   users.
+        unsafe { bindings::spin_unlock(&raw mut (*self.drm_dev().as_raw()).vbl_lock) }
+    }
+}
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 02/18] rust: drm: kms: adapt the port to current drm-next
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 01/18] rust: drm: kms: forward-port the safe mode-object layer onto the typestate device Mike Lothian
@ 2026-07-03  3:00   ` Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 03/18] rust: drm: kms: break the Driver* trait well-formedness cycle Mike Lothian
                     ` (15 subsequent siblings)
  17 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:00 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

Three mechanical fixups needed to build the forward-ported layer
against this tree, rather than the rvkms-slim/rust-stuck source
branches it came from:

- wire the KMS bindings/helpers/aref imports the port assumes into
  bindings_helper.h and rust/helpers/drm/{atomic,drm,vblank}.c, and fix
  the impl_aref_for_mode_object! macro path
- rename drm_atomic_state -> drm_atomic_commit, renamed upstream since
  rvkms-slim was last synced
- pin KmsDriver's associated types to Driver=Self and tidy the
  probe_kms stub so it type-checks against the current Driver trait

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-sonnet-5 [Claude-Code]
---
 rust/bindings/bindings_helper.h |  8 +++++++
 rust/helpers/drm/atomic.c       | 32 +++++++++++++++++++++++++
 rust/helpers/drm/drm.c          |  2 ++
 rust/helpers/drm/vblank.c       |  8 +++++++
 rust/kernel/drm/kms.rs          | 21 ++++++++---------
 rust/kernel/drm/kms/atomic.rs   | 41 +++++++++++++++++----------------
 rust/kernel/drm/kms/crtc.rs     | 16 ++++++-------
 rust/kernel/drm/kms/plane.rs    |  8 +++----
 8 files changed, 93 insertions(+), 43 deletions(-)
 create mode 100644 rust/helpers/drm/atomic.c
 create mode 100644 rust/helpers/drm/vblank.c

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index a1ea23714bdd..e41d2716fb2e 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -36,6 +36,14 @@
 #include <linux/gpu_buddy.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
 #include <drm/clients/drm_client_setup.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
diff --git a/rust/helpers/drm/atomic.c b/rust/helpers/drm/atomic.c
new file mode 100644
index 000000000000..ed5e49b73c81
--- /dev/null
+++ b/rust/helpers/drm/atomic.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <drm/drm_atomic.h>
+
+void rust_helper_drm_atomic_commit_get(struct drm_atomic_commit *state)
+{
+	drm_atomic_commit_get(state);
+}
+
+void rust_helper_drm_atomic_commit_put(struct drm_atomic_commit *state)
+{
+	drm_atomic_commit_put(state);
+}
+
+// Macros for generating one repetitive atomic state accessors (like drm_atomic_get_new_plane_state)
+#define STATE_FUNC(type, tense)                                                                     \
+	struct drm_ ## type ## _state *rust_helper_drm_atomic_get_ ## tense ## _ ## type ## _state( \
+		const struct drm_atomic_commit *state,                                               \
+		struct drm_ ## type *type                                                           \
+	) {                                                                                         \
+		return drm_atomic_get_## tense ## _ ## type ## _state(state, type);                 \
+	}
+#define STATE_FUNCS(type) \
+	STATE_FUNC(type, new); \
+	STATE_FUNC(type, old);
+
+STATE_FUNCS(plane);
+STATE_FUNCS(crtc);
+STATE_FUNCS(connector);
+
+#undef STATE_FUNCS
+#undef STATE_FUNC
diff --git a/rust/helpers/drm/drm.c b/rust/helpers/drm/drm.c
index b8700413a2b8..5d700d75c0c9 100644
--- a/rust/helpers/drm/drm.c
+++ b/rust/helpers/drm/drm.c
@@ -1,4 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 
+#include "atomic.c"
 #include "gem.c"
 #include "vma_manager.c"
+#include "vblank.c"
diff --git a/rust/helpers/drm/vblank.c b/rust/helpers/drm/vblank.c
new file mode 100644
index 000000000000..165db7ac5b4d
--- /dev/null
+++ b/rust/helpers/drm/vblank.c
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <drm/drm_vblank.h>
+
+struct drm_vblank_crtc *rust_helper_drm_crtc_vblank_crtc(struct drm_crtc *crtc)
+{
+	return drm_crtc_vblank_crtc(crtc);
+}
diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs
index 11b09d2175db..a147276b3412 100644
--- a/rust/kernel/drm/kms.rs
+++ b/rust/kernel/drm/kms.rs
@@ -12,8 +12,7 @@
     },
     error::to_result,
     prelude::*,
-    sync::{Mutex, MutexGuard},
-    types::*,
+    sync::{aref::ARef, Mutex, MutexGuard},
 };
 use bindings;
 use core::{
@@ -69,10 +68,10 @@ pub trait KmsImpl {
         ///
         /// This function may only be called once.
         unsafe fn probe_kms(_drm: &Device<Self::Driver, Uninit>) -> Result<ModeConfigInfo> {
-            // TODO before submission: There is definitely a proper way of doing this, but I don't
-            // remember what it is.
-            unimplemented!()
-            //build_error!("This should never be reachable")
+            // This default is only reachable for the `PhantomData<T>` (non-KMS) implementor, whose
+            // KMS setup is never invoked. KMS drivers override it via the `KmsDriver` blanket impl
+            // below, so reaching this is a build-time bug.
+            build_error::build_error("probe_kms called on a device without KMS support")
         }
     }
 }
@@ -126,22 +125,22 @@ pub trait KmsDriver: Driver {
     /// The driver's [`DriverConnector`](connector::DriverConnector) implementation.
     ///
     /// TODO: This will be unneeded once we support multiple `DriverConnector` implementations.
-    type Connector: connector::DriverConnector;
+    type Connector: connector::DriverConnector<Driver = Self>;
 
     /// The driver's [`DriverPlane`](plane::DriverPlane) implementation.
     ///
     /// TODO: This will be unneeded once we support multiple `DriverPlane` implementations.
-    type Plane: plane::DriverPlane;
+    type Plane: plane::DriverPlane<Driver = Self>;
 
     /// The driver's [`DriverCrtc`](crtc::DriverCrtc) implementation.
     ///
     /// TODO: This will be unneeded once we support multiple `DriverCrtc` implementations.
-    type Crtc: crtc::DriverCrtc;
+    type Crtc: crtc::DriverCrtc<Driver = Self>;
 
     /// The driver's [`DriverEncoder`](encoder::DriverEncoder) implementation.
     ///
     /// TODO: This will be unneeded once we support multiple `DriverEncoder` implementations.
-    type Encoder: encoder::DriverEncoder;
+    type Encoder: encoder::DriverEncoder<Driver = Self>;
 
     /// Return a [`ModeConfigInfo`] structure for this [`device::Device`].
     fn mode_config_info(dev: &Device<Self, Uninit>) -> Result<ModeConfigInfo>
@@ -486,7 +485,7 @@ macro_rules! impl_aref_for_mode_object {
     (impl $( < $( $param:ident: $bound:ident ),+ > )? for $type:ty) => {
         // SAFETY: drm_mode_object_get()/put() ensure the type is ref-counted according to the
         // safety contract
-        unsafe impl $( < $( $param: $bound ),+ > )? kernel::types::AlwaysRefCounted for $type {
+        unsafe impl $( < $( $param: $bound ),+ > )? kernel::sync::aref::AlwaysRefCounted for $type {
             #[inline]
             fn inc_ref(&self) {
                 // SAFETY: We're guaranteed by the safety contract of `ModeObject` that
diff --git a/rust/kernel/drm/kms/atomic.rs b/rust/kernel/drm/kms/atomic.rs
index cc14bff47abd..18dc136940f3 100644
--- a/rust/kernel/drm/kms/atomic.rs
+++ b/rust/kernel/drm/kms/atomic.rs
@@ -1,31 +1,32 @@
 // SPDX-License-Identifier: GPL-2.0 OR MIT
 
-//! [`struct drm_atomic_state`] related bindings for rust.
+//! [`struct drm_atomic_commit`] related bindings for rust.
 //!
-//! [`struct drm_atomic_state`]: srctree/include/drm/drm_atomic.h
+//! [`struct drm_atomic_commit`]: srctree/include/drm/drm_atomic.h
 use super::{connector::*, crtc::*, plane::*, KmsDriver, ModeObject};
 use crate::{
     bindings,
     drm::device::Device,
     error::{from_err_ptr, to_result},
     prelude::*,
+    sync::aref::{ARef, AlwaysRefCounted},
     types::*,
 };
 use core::{cell::Cell, marker::*, mem::ManuallyDrop, ops::*, ptr::NonNull};
 
-/// The main wrapper around [`struct drm_atomic_state`].
+/// The main wrapper around [`struct drm_atomic_commit`].
 ///
 /// This type is usually embedded within another interface such as an [`AtomicStateMutator`].
 ///
 /// # Invariants
 ///
-/// - The data layout of this type is identical to [`struct drm_atomic_state`].
+/// - The data layout of this type is identical to [`struct drm_atomic_commit`].
 /// - `state` is initialized for as long as this type is exposed to users.
 ///
-/// [`struct drm_atomic_state`]: srctree/include/drm/drm_atomic.h
+/// [`struct drm_atomic_commit`]: srctree/include/drm/drm_atomic.h
 #[repr(transparent)]
 pub struct AtomicState<T: KmsDriver> {
-    pub(super) state: Opaque<bindings::drm_atomic_state>,
+    pub(super) state: Opaque<bindings::drm_atomic_commit>,
     _p: PhantomData<T>,
 }
 
@@ -34,18 +35,18 @@ impl<T: KmsDriver> AtomicState<T> {
     ///
     /// # Safety
     ///
-    /// `ptr` must point to a valid initialized instance of [`struct drm_atomic_state`].
+    /// `ptr` must point to a valid initialized instance of [`struct drm_atomic_commit`].
     ///
-    /// [`struct drm_atomic_state`]: srctree/include/drm/drm_atomic.h
+    /// [`struct drm_atomic_commit`]: srctree/include/drm/drm_atomic.h
     #[allow(dead_code)]
-    pub(super) unsafe fn from_raw<'a>(ptr: *const bindings::drm_atomic_state) -> &'a Self {
+    pub(super) unsafe fn from_raw<'a>(ptr: *const bindings::drm_atomic_commit) -> &'a Self {
         // SAFETY: Our data layout is identical
         // INVARIANT: Our safety contract upholds the guarantee that `state` is initialized for as
         // long as this type is exposed to users.
         unsafe { &*ptr.cast() }
     }
 
-    pub(crate) fn as_raw(&self) -> *mut bindings::drm_atomic_state {
+    pub(crate) fn as_raw(&self) -> *mut bindings::drm_atomic_commit {
         self.state.get()
     }
 
@@ -102,12 +103,12 @@ pub fn get_old_connector_state<C>(&self, connector: &C) -> Option<&C::State>
 unsafe impl<T: KmsDriver> AlwaysRefCounted for AtomicState<T> {
     fn inc_ref(&self) {
         // SAFETY: `state` is initialized for as long as this type is exposed to users
-        unsafe { bindings::drm_atomic_state_get(self.state.get()) }
+        unsafe { bindings::drm_atomic_commit_get(self.state.get()) }
     }
 
     unsafe fn dec_ref(obj: NonNull<Self>) {
         // SAFETY: `obj` contains a valid non-null pointer to an initialized `Self`.
-        unsafe { bindings::drm_atomic_state_put(obj.as_ptr().cast()) }
+        unsafe { bindings::drm_atomic_commit_put(obj.as_ptr().cast()) }
     }
 }
 
@@ -141,11 +142,11 @@ impl<T: KmsDriver> AtomicStateMutator<T> {
     ///
     /// # Safety
     ///
-    /// `ptr` must point to a valid `drm_atomic_state`
+    /// `ptr` must point to a valid `drm_atomic_commit`
     #[allow(dead_code)]
-    pub(super) unsafe fn new(ptr: NonNull<bindings::drm_atomic_state>) -> Self {
+    pub(super) unsafe fn new(ptr: NonNull<bindings::drm_atomic_commit>) -> Self {
         Self {
-            // SAFETY: The data layout of `AtomicState<T>` is identical to drm_atomic_state
+            // SAFETY: The data layout of `AtomicState<T>` is identical to drm_atomic_commit
             // We use `ManuallyDrop` because `AtomicStateMutator` is only ever provided to users in
             // the context of KMS callbacks. As such, skipping ref inc/dec for the atomic state is
             // convienent for our bindings.
@@ -156,7 +157,7 @@ pub(super) unsafe fn new(ptr: NonNull<bindings::drm_atomic_state>) -> Self {
         }
     }
 
-    pub(crate) fn as_raw(&self) -> *mut bindings::drm_atomic_state {
+    pub(crate) fn as_raw(&self) -> *mut bindings::drm_atomic_commit {
         self.state.as_raw()
     }
 
@@ -273,8 +274,8 @@ fn drop(&mut self) {
 impl<T: KmsDriver> AtomicStateComposer<T> {
     /// # Safety
     ///
-    /// The caller guarantees that `ptr` points to a valid instance of `drm_atomic_state`.
-    pub(crate) unsafe fn new(ptr: NonNull<bindings::drm_atomic_state>) -> Self {
+    /// The caller guarantees that `ptr` points to a valid instance of `drm_atomic_commit`.
+    pub(crate) unsafe fn new(ptr: NonNull<bindings::drm_atomic_commit>) -> Self {
         // SAFETY: see `AtomicStateMutator::from_raw()`
         Self(unsafe { AtomicStateMutator::new(ptr) })
     }
@@ -681,11 +682,11 @@ pub fn commit_hw_done<'b>(
 
 // The actual raw C callback for custom atomic commit tail implementations
 pub(crate) unsafe extern "C" fn commit_tail_callback<T: KmsDriver>(
-    state: *mut bindings::drm_atomic_state,
+    state: *mut bindings::drm_atomic_commit,
 ) {
     // SAFETY:
     // - We're guaranteed by DRM that `state` always points to a valid instance of
-    //   `bindings::drm_atomic_state`
+    //   `bindings::drm_atomic_commit`
     // - This conversion is safe via the type invariants
     let state = unsafe { AtomicState::from_raw(state.cast_const()) };
 
diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs
index b9d095854ba6..0f579dc4ec75 100644
--- a/rust/kernel/drm/kms/crtc.rs
+++ b/rust/kernel/drm/kms/crtc.rs
@@ -995,14 +995,14 @@ impl<'a, T: DriverCrtc> CrtcAtomicCommit<'a, T> {
 
 unsafe extern "C" fn atomic_check_callback<T: DriverCrtc>(
     crtc: *mut bindings::drm_crtc,
-    state: *mut bindings::drm_atomic_state,
+    state: *mut bindings::drm_atomic_commit,
 ) -> i32 {
     // SAFETY:
     // - We're guaranteed `crtc` is of type `Crtc<T>` via type invariants.
     // - We're guaranteed by DRM that `crtc` is pointing to a valid initialized state.
     let crtc = unsafe { Crtc::from_raw(crtc) };
 
-    // SAFETY: DRM guarantees `state` points to a valid `drm_atomic_state`
+    // SAFETY: DRM guarantees `state` points to a valid `drm_atomic_commit`
     // We use a ManuallyDrop here to avoid AtomicStateComposer dropping an owned reference we never
     // acquired.
     let state =
@@ -1023,14 +1023,14 @@ impl<'a, T: DriverCrtc> CrtcAtomicCommit<'a, T> {
 
 unsafe extern "C" fn atomic_begin_callback<T: DriverCrtc>(
     crtc: *mut bindings::drm_crtc,
-    state: *mut bindings::drm_atomic_state,
+    state: *mut bindings::drm_atomic_commit,
 ) {
     // SAFETY:
     // * We're guaranteed `crtc` is of type `Crtc<T>` via type invariants.
     // * We're guaranteed by DRM that `crtc` is pointing to a valid initialized state.
     let crtc = unsafe { Crtc::from_raw(crtc) };
 
-    // SAFETY: We're guaranteed by DRM that `state` points to a valid instance of `drm_atomic_state`
+    // SAFETY: We're guaranteed by DRM that `state` points to a valid instance of `drm_atomic_commit`
     let state = unsafe { AtomicStateMutator::new(NonNull::new_unchecked(state)) };
 
     // SAFETY:
@@ -1045,14 +1045,14 @@ impl<'a, T: DriverCrtc> CrtcAtomicCommit<'a, T> {
 
 unsafe extern "C" fn atomic_flush_callback<T: DriverCrtc>(
     crtc: *mut bindings::drm_crtc,
-    state: *mut bindings::drm_atomic_state,
+    state: *mut bindings::drm_atomic_commit,
 ) {
     // SAFETY:
     // - We're guaranteed `crtc` is of type `Crtc<T>` via type invariants.
     // - We're guaranteed by DRM that `crtc` is pointing to a valid initialized state.
     let crtc = unsafe { Crtc::from_raw(crtc) };
 
-    // SAFETY: We're guaranteed by DRM that `state` points to a valid instance of `drm_atomic_state`
+    // SAFETY: We're guaranteed by DRM that `state` points to a valid instance of `drm_atomic_commit`
     let state = unsafe { AtomicStateMutator::new(NonNull::new_unchecked(state)) };
 
     // SAFETY:
@@ -1067,7 +1067,7 @@ impl<'a, T: DriverCrtc> CrtcAtomicCommit<'a, T> {
 
 unsafe extern "C" fn atomic_enable_callback<T: DriverCrtc>(
     crtc: *mut bindings::drm_crtc,
-    state: *mut bindings::drm_atomic_state,
+    state: *mut bindings::drm_atomic_commit,
 ) {
     // SAFETY:
     // - We're guaranteed `crtc` is of type `Crtc<T>` via type invariants.
@@ -1089,7 +1089,7 @@ impl<'a, T: DriverCrtc> CrtcAtomicCommit<'a, T> {
 
 unsafe extern "C" fn atomic_disable_callback<T: DriverCrtc>(
     crtc: *mut bindings::drm_crtc,
-    state: *mut bindings::drm_atomic_state,
+    state: *mut bindings::drm_atomic_commit,
 ) {
     // SAFETY:
     // - We're guaranteed `crtc` points to a valid instance of `drm_crtc`
diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs
index 661d82273099..6f547adcfdc9 100644
--- a/rust/kernel/drm/kms/plane.rs
+++ b/rust/kernel/drm/kms/plane.rs
@@ -1048,14 +1048,14 @@ impl<'a, T: DriverPlane> PlaneAtomicCommit<'a, T> {
 
 unsafe extern "C" fn atomic_update_callback<T: DriverPlane>(
     plane: *mut bindings::drm_plane,
-    state: *mut bindings::drm_atomic_state,
+    state: *mut bindings::drm_atomic_commit,
 ) {
     // SAFETY:
     // - We're guaranteed `plane` is of type `Plane<T>` via type invariants.
     // - We're guaranteed by DRM that `plane` is pointing to a valid initialized state.
     let plane = unsafe { Plane::from_raw(plane) };
 
-    // SAFETY: DRM guarantees `state` points to a valid `drm_atomic_state`
+    // SAFETY: DRM guarantees `state` points to a valid `drm_atomic_commit`
     let state = unsafe { AtomicStateMutator::new(NonNull::new_unchecked(state)) };
 
     // SAFETY:
@@ -1070,14 +1070,14 @@ impl<'a, T: DriverPlane> PlaneAtomicCommit<'a, T> {
 
 unsafe extern "C" fn atomic_check_callback<T: DriverPlane>(
     plane: *mut bindings::drm_plane,
-    state: *mut bindings::drm_atomic_state,
+    state: *mut bindings::drm_atomic_commit,
 ) -> i32 {
     // SAFETY:
     // - We're guaranteed `plane` is of type `Plane<T>` via type invariants.
     // - We're guaranteed by DRM that `plane` is pointing to a valid initialized state.
     let plane = unsafe { Plane::from_raw(plane) };
 
-    // SAFETY: We're guaranteed by DRM that `state` points to a valid instance of `drm_atomic_state`
+    // SAFETY: We're guaranteed by DRM that `state` points to a valid instance of `drm_atomic_commit`
     // We use ManuallyDrop here since AtomicStateComposer would otherwise drop a owned reference to
     // the atomic state upon finishing this callback.
     let state = ManuallyDrop::new(unsafe {
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 03/18] rust: drm: kms: break the Driver* trait well-formedness cycle
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 01/18] rust: drm: kms: forward-port the safe mode-object layer onto the typestate device Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 02/18] rust: drm: kms: adapt the port to current drm-next Mike Lothian
@ 2026-07-03  3:00   ` Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 04/18] rust: drm: kms: build the kernel crate clean under -Znext-solver Mike Lothian
                     ` (14 subsequent siblings)
  17 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:00 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

The ported mode-object trait web (KmsDriver / DriverConnector /
DriverConnectorState / VblankImpl, ...) is mutually recursive: a
Modesettable* impl for Crtc<T::Crtc> needs <T::Crtc::State>::Crtc:
DriverCrtc, which needs <T::Crtc::State::Crtc::State>::Crtc, and so on
-- an infinite projection regress the trait solver can't terminate on
unless the associated-state types are pinned to State<Assoc = Self>.

Pin them, add the where-clauses the *StateMutator Deref/DerefMut impls
now need once callers pass through the pinned types, and fix a
from_opaque copy-paste bug in CrtcStateMutator that was reading Plane's
fields instead of Crtc's.

This still doesn't build clean: pinning trades the projection regress
for a coinductive cycle in the from_opaque-on-state impls, which the
current (non-next-gen) trait solver exhausts memory on instead of
resolving. See the following commit.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-sonnet-5 [Claude-Code]
---
 rust/kernel/drm/kms.rs           | 28 +++++++++++++---------------
 rust/kernel/drm/kms/connector.rs | 27 ++++++++++++++++++++-------
 rust/kernel/drm/kms/crtc.rs      | 31 +++++++++++++++++++++----------
 rust/kernel/drm/kms/plane.rs     | 27 ++++++++++++++++++++-------
 rust/kernel/drm/kms/vblank.rs    |  5 +++--
 5 files changed, 77 insertions(+), 41 deletions(-)

diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs
index a147276b3412..d07389fd510c 100644
--- a/rust/kernel/drm/kms.rs
+++ b/rust/kernel/drm/kms.rs
@@ -122,25 +122,23 @@ fn deref(&self) -> &Self::Target {
 /// [`PhantomData<Self>`]: PhantomData
 #[vtable]
 pub trait KmsDriver: Driver {
-    /// The driver's [`DriverConnector`](connector::DriverConnector) implementation.
-    ///
-    /// TODO: This will be unneeded once we support multiple `DriverConnector` implementations.
-    type Connector: connector::DriverConnector<Driver = Self>;
+    // The driver's mode-object implementations. Left *unbounded* to break a trait
+    // well-formedness cycle: the natural bound (`type Crtc: DriverCrtc<Driver = Self>`, etc.)
+    // makes every `T: KmsDriver` obligation pull in the mutually-recursive Driver* web, which
+    // the compiler cannot evaluate (it loops in obligation processing and exhausts memory). The
+    // `DriverConnector`/`DriverCrtc`/... bounds are required at the use sites as `where`-clauses
+    // instead (an assumption, not an eagerly WF-checked trait-definition obligation).
+    //
+    // TODO: unneeded once we support multiple `Driver*` implementations per driver.
 
+    /// The driver's [`DriverConnector`](connector::DriverConnector) implementation.
+    type Connector;
     /// The driver's [`DriverPlane`](plane::DriverPlane) implementation.
-    ///
-    /// TODO: This will be unneeded once we support multiple `DriverPlane` implementations.
-    type Plane: plane::DriverPlane<Driver = Self>;
-
+    type Plane;
     /// The driver's [`DriverCrtc`](crtc::DriverCrtc) implementation.
-    ///
-    /// TODO: This will be unneeded once we support multiple `DriverCrtc` implementations.
-    type Crtc: crtc::DriverCrtc<Driver = Self>;
-
+    type Crtc;
     /// The driver's [`DriverEncoder`](encoder::DriverEncoder) implementation.
-    ///
-    /// TODO: This will be unneeded once we support multiple `DriverEncoder` implementations.
-    type Encoder: encoder::DriverEncoder<Driver = Self>;
+    type Encoder;
 
     /// Return a [`ModeConfigInfo`] structure for this [`device::Device`].
     fn mode_config_info(dev: &Device<Self, Uninit>) -> Result<ModeConfigInfo>
diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs
index 05ec64cf6fa2..aca7be50e583 100644
--- a/rust/kernel/drm/kms/connector.rs
+++ b/rust/kernel/drm/kms/connector.rs
@@ -139,7 +139,7 @@ pub trait DriverConnector: Send + Sync + Sized {
     /// The [`DriverConnectorState`] implementation for this [`DriverConnector`].
     ///
     /// See [`DriverConnectorState`] for more info.
-    type State: DriverConnectorState;
+    type State: DriverConnectorState<Connector = Self>;
 
     /// The constructor for creating a [`Connector`] using this [`DriverConnector`] implementation.
     ///
@@ -700,13 +700,17 @@ pub struct ConnectorState<T: DriverConnectorState> {
 /// [`struct drm_connector`]: srctree/include/drm_connector.h
 /// [`struct drm_connector_state`]: srctree/include/drm_connector.h
 pub trait DriverConnectorState: Clone + Default + Sized {
-    /// The parent [`DriverConnector`].
-    type Connector: DriverConnector;
+    /// The parent [`DriverConnector`]. Unbounded to break the trait WF cycle; the
+    /// `DriverConnector` bound is required at use sites instead.
+    type Connector;
 }
 
 impl<T: DriverConnectorState> Sealed for ConnectorState<T> {}
 
-impl<T: DriverConnectorState> AsRawConnectorState for ConnectorState<T> {
+impl<T: DriverConnectorState> AsRawConnectorState for ConnectorState<T>
+where
+    T::Connector: DriverConnector,
+{
     type Connector = Connector<T::Connector>;
 }
 
@@ -720,7 +724,10 @@ unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_connector_state {
     }
 }
 
-impl<T: DriverConnectorState> FromRawConnectorState for ConnectorState<T> {
+impl<T: DriverConnectorState> FromRawConnectorState for ConnectorState<T>
+where
+    T::Connector: DriverConnector,
+{
     unsafe fn from_raw<'a>(ptr: *const bindings::drm_connector_state) -> &'a Self {
         // Our data layout starts with `bindings::drm_connector_state`.
         let ptr: *const Self = ptr.cast();
@@ -859,7 +866,10 @@ pub(super) fn new<D: KmsDriver>(
     }
 }
 
-impl<'a, T: DriverConnectorState> Deref for ConnectorStateMutator<'a, ConnectorState<T>> {
+impl<'a, T: DriverConnectorState> Deref for ConnectorStateMutator<'a, ConnectorState<T>>
+where
+    T::Connector: DriverConnector,
+{
     type Target = T;
 
     fn deref(&self) -> &Self::Target {
@@ -867,7 +877,10 @@ fn deref(&self) -> &Self::Target {
     }
 }
 
-impl<'a, T: DriverConnectorState> DerefMut for ConnectorStateMutator<'a, ConnectorState<T>> {
+impl<'a, T: DriverConnectorState> DerefMut for ConnectorStateMutator<'a, ConnectorState<T>>
+where
+    T::Connector: DriverConnector,
+{
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.state.inner
     }
diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs
index 0f579dc4ec75..4c6cb6bbdcb8 100644
--- a/rust/kernel/drm/kms/crtc.rs
+++ b/rust/kernel/drm/kms/crtc.rs
@@ -124,7 +124,7 @@ pub trait DriverCrtc: Send + Sync + Sized {
     /// The [`DriverCrtcState`] implementation for this [`DriverCrtc`].
     ///
     /// See [`DriverCrtcState`] for more info.
-    type State: DriverCrtcState;
+    type State: DriverCrtcState<Crtc = Self>;
 
     /// The driver's optional hardware vblank implementation
     ///
@@ -561,10 +561,9 @@ impl<T: DriverCrtcState> Sealed for CrtcState<T> {}
 /// [`struct drm_crtc`]: srctree/include/drm_crtc.h
 /// [`struct drm_crtc_state`]: srctree/include/drm_crtc.h
 pub trait DriverCrtcState: Clone + Default + Unpin {
-    /// The parent CRTC driver for this CRTC state
-    type Crtc: DriverCrtc<State = Self>
-    where
-        Self: Sized;
+    /// The parent CRTC driver for this CRTC state. Unbounded to break the trait WF cycle;
+    /// the `DriverCrtc` bound is required at use sites instead.
+    type Crtc;
 }
 
 /// The main interface for a [`struct drm_crtc_state`].
@@ -702,11 +701,17 @@ fn as_raw(&self) -> *mut bindings::drm_crtc_state {
     }
 }
 
-impl<T: DriverCrtcState> AsRawCrtcState for CrtcState<T> {
+impl<T: DriverCrtcState> AsRawCrtcState for CrtcState<T>
+where
+    T::Crtc: DriverCrtc,
+{
     type Crtc = Crtc<T::Crtc>;
 }
 
-impl<T: DriverCrtcState> FromRawCrtcState for CrtcState<T> {
+impl<T: DriverCrtcState> FromRawCrtcState for CrtcState<T>
+where
+    T::Crtc: DriverCrtc,
+{
     unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc_state) -> &'a Self {
         // SAFETY: Our data layout starts with `bindings::drm_crtc_state`
         unsafe { &*(ptr.cast()) }
@@ -800,7 +805,7 @@ fn <D, C>(CrtcStateMutator<'a, OpaqueCrtcState<D>>) -> Self
         where
             T: DriverCrtcState<Crtc = C>;
         use
-            T as DriverCrtc,
+            C as DriverCrtc,
             D as KmsDriver<Crtc = ...>
     }
 }
@@ -813,7 +818,10 @@ fn drop(&mut self) {
     }
 }
 
-impl<'a, T: DriverCrtcState> Deref for CrtcStateMutator<'a, CrtcState<T>> {
+impl<'a, T: DriverCrtcState> Deref for CrtcStateMutator<'a, CrtcState<T>>
+where
+    T::Crtc: DriverCrtc,
+{
     type Target = T;
 
     fn deref(&self) -> &Self::Target {
@@ -823,7 +831,10 @@ fn deref(&self) -> &Self::Target {
     }
 }
 
-impl<'a, T: DriverCrtcState> DerefMut for CrtcStateMutator<'a, CrtcState<T>> {
+impl<'a, T: DriverCrtcState> DerefMut for CrtcStateMutator<'a, CrtcState<T>>
+where
+    T::Crtc: DriverCrtc,
+{
     fn deref_mut(&mut self) -> &mut Self::Target {
         // SAFETY: Our interface ensures that `self.state.inner` follows rust's data-aliasing rules,
         // so this is safe
diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs
index 6f547adcfdc9..47e05fe6bee1 100644
--- a/rust/kernel/drm/kms/plane.rs
+++ b/rust/kernel/drm/kms/plane.rs
@@ -98,7 +98,7 @@ pub trait DriverPlane: Send + Sync + Sized {
     /// The [`DriverPlaneState`] implementation for this [`DriverPlane`].
     ///
     /// See [`DriverPlaneState`] for more info.
-    type State: DriverPlaneState;
+    type State: DriverPlaneState<Plane = Self>;
 
     /// The constructor for creating a [`Plane`] using this [`DriverPlane`] implementation.
     ///
@@ -689,13 +689,17 @@ pub struct PlaneState<T: DriverPlaneState> {
 /// [`struct drm_plane`]: srctree/include/drm_plane.h
 /// [`struct drm_plane_state`]: srctree/include/drm_plane.h
 pub trait DriverPlaneState: Clone + Default + Sized {
-    /// The type for this driver's drm_plane implementation
-    type Plane: DriverPlane;
+    /// The type for this driver's drm_plane implementation. Unbounded to break the trait WF
+    /// cycle; the `DriverPlane` bound is required at use sites instead.
+    type Plane;
 }
 
 impl<T: DriverPlaneState> Sealed for PlaneState<T> {}
 
-impl<T: DriverPlaneState> AsRawPlaneState for PlaneState<T> {
+impl<T: DriverPlaneState> AsRawPlaneState for PlaneState<T>
+where
+    T::Plane: DriverPlane,
+{
     type Plane = Plane<T::Plane>;
 }
 
@@ -709,7 +713,10 @@ unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_plane_state {
     }
 }
 
-impl<T: DriverPlaneState> FromRawPlaneState for PlaneState<T> {
+impl<T: DriverPlaneState> FromRawPlaneState for PlaneState<T>
+where
+    T::Plane: DriverPlane,
+{
     unsafe fn from_raw<'a>(ptr: *const bindings::drm_plane_state) -> &'a Self {
         // Our data layout starts with `bindings::drm_plane_state`.
         let ptr: *const Self = ptr.cast();
@@ -885,7 +892,10 @@ unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_plane_state {
 
 impl<'a, T: FromRawPlaneState> Sealed for PlaneStateMutator<'a, T> {}
 
-impl<'a, T: DriverPlaneState> Deref for PlaneStateMutator<'a, PlaneState<T>> {
+impl<'a, T: DriverPlaneState> Deref for PlaneStateMutator<'a, PlaneState<T>>
+where
+    T::Plane: DriverPlane,
+{
     type Target = T;
 
     fn deref(&self) -> &Self::Target {
@@ -893,7 +903,10 @@ fn deref(&self) -> &Self::Target {
     }
 }
 
-impl<'a, T: DriverPlaneState> DerefMut for PlaneStateMutator<'a, PlaneState<T>> {
+impl<'a, T: DriverPlaneState> DerefMut for PlaneStateMutator<'a, PlaneState<T>>
+where
+    T::Plane: DriverPlane,
+{
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.state.inner
     }
diff --git a/rust/kernel/drm/kms/vblank.rs b/rust/kernel/drm/kms/vblank.rs
index dc34e02e8ccb..534fd18f63b4 100644
--- a/rust/kernel/drm/kms/vblank.rs
+++ b/rust/kernel/drm/kms/vblank.rs
@@ -62,8 +62,9 @@ fn get_vblank_timestamp(
 /// Drivers interested in implementing vblank support should refer to [`VblankSupport`], drivers
 /// that don't have vblank support can use [`PhantomData`].
 pub trait VblankImpl {
-    /// The parent [`DriverCrtc`].
-    type Crtc: DriverCrtc<VblankImpl = Self>;
+    /// The parent [`DriverCrtc`]. Unbounded to break the trait WF cycle; the `DriverCrtc`
+    /// bound is required at use sites instead.
+    type Crtc;
 
     /// The generated [`VblankOps`].
     const VBLANK_OPS: VblankOps;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 04/18] rust: drm: kms: build the kernel crate clean under -Znext-solver
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
                     ` (2 preceding siblings ...)
  2026-07-03  3:00   ` [RFC PATCH v2 03/18] rust: drm: kms: break the Driver* trait well-formedness cycle Mike Lothian
@ 2026-07-03  3:00   ` Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 05/18] rust: drm: expose <drm/display/drm_hdcp.h> HDCP 2.2 message definitions Mike Lothian
                     ` (13 subsequent siblings)
  17 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:00 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

The coinductive cycle from the previous commit isn't a bug in this
port -- it's inherent to the mutual type Driver / type State / type
Crtc references in the mode-object traits, and the current trait
solver can't evaluate a coinductive obligation without unbounded
memory, regardless of where the bounds are placed. Disabling the
unused from_opaque-on-state impls doesn't help either: their errors
were masking the same explosion one level down.

The next-generation trait solver (-Znext-solver) does handle it. Scope
it to the kernel crate only in rust/Makefile rather than globally, so
core/alloc/etc. stay on the old solver -- core.o ICEs under
-Znext-solver on this toolchain, and there's no reason to pay that
cost crate-wide when only kernel/ needs the new solver's coinductive
support.

rust/kernel.o now builds with 0 errors, safe KMS layer included. Fixes
needed to get there once the solver could actually evaluate the web:
restore the pinned associated-type bounds (no back-edge cuts needed
under the new solver); add Device::event_lock(), ModeConfigGuard::new();
import AlwaysRefCounted/SpinLockIrq; Driver*::new() takes
&Device<.., Uninit> since objects are created during probe; base drift
in drm_crtc_helper_funcs (drop mode_set_base_atomic, add
handle_vblank_timeout); device.rs T::Object::<Uninit>::ALLOC_OPS and a
probe_kms Option rewrite; driver.rs captures preferred_fourcc and uses
the registered device after mem::forget(drm); unused imports dropped;
allow(dead_code) on the not-yet-used ModeObjectVtable.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-sonnet-5 [Claude-Code]
---
 rust/Makefile                    |  6 ++--
 rust/kernel/drm/device.rs        | 12 ++++----
 rust/kernel/drm/driver.rs        |  8 ++---
 rust/kernel/drm/kms.rs           | 51 +++++++++++++++++++++++---------
 rust/kernel/drm/kms/connector.rs | 31 ++++++-------------
 rust/kernel/drm/kms/crtc.rs      | 35 ++++++++--------------
 rust/kernel/drm/kms/encoder.rs   |  4 +--
 rust/kernel/drm/kms/modes.rs     |  5 ++++
 rust/kernel/drm/kms/plane.rs     | 31 ++++++-------------
 rust/kernel/drm/kms/vblank.rs    |  7 ++---
 10 files changed, 91 insertions(+), 99 deletions(-)

diff --git a/rust/Makefile b/rust/Makefile
index a870d1616c71..9aa985e21905 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -270,7 +270,7 @@ rustdoc-kernel: private is-kernel-object := y
 rustdoc-kernel: private rustc_target_flags = --extern ffi --extern pin_init \
     --extern build_error --extern macros \
     --extern bindings --extern uapi \
-    --extern zerocopy --extern zerocopy_derive
+    --extern zerocopy --extern zerocopy_derive -Znext-solver
 rustdoc-kernel: $(src)/kernel/lib.rs rustdoc-core rustdoc-ffi rustdoc-macros \
     rustdoc-pin_init rustdoc-compiler_builtins $(obj)/$(libmacros_name) \
     $(obj)/bindings.o FORCE
@@ -342,7 +342,7 @@ rusttestlib-pin_init: $(src)/pin-init/src/lib.rs rusttestlib-macros \
 rusttestlib-kernel: private rustc_target_flags = --extern ffi \
     --extern build_error --extern macros --extern pin_init \
     --extern bindings --extern uapi \
-    --extern zerocopy --extern zerocopy_derive
+    --extern zerocopy --extern zerocopy_derive -Znext-solver
 rusttestlib-kernel: $(src)/kernel/lib.rs rusttestlib-bindings rusttestlib-uapi \
     rusttestlib-build_error rusttestlib-pin_init $(obj)/$(libmacros_name) \
     $(obj)/bindings.o rusttestlib-zerocopy rusttestlib-zerocopy_derive FORCE
@@ -764,7 +764,7 @@ $(obj)/uapi.o: $(src)/uapi/lib.rs \
 
 $(obj)/kernel.o: private rustc_target_flags = --extern ffi --extern pin_init \
     --extern build_error --extern macros --extern bindings --extern uapi \
-    --extern zerocopy --extern zerocopy_derive
+    --extern zerocopy --extern zerocopy_derive -Znext-solver
 $(obj)/kernel.o: $(src)/kernel/lib.rs $(obj)/build_error.o $(obj)/pin_init.o \
     $(obj)/$(libmacros_name) $(obj)/bindings.o $(obj)/uapi.o \
     $(obj)/zerocopy.o $(obj)/$(libzerocopy_derive_name) FORCE
diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs
index 9641102bd87f..5b84ce18fb29 100644
--- a/rust/kernel/drm/device.rs
+++ b/rust/kernel/drm/device.rs
@@ -229,7 +229,7 @@ const fn compute_features() -> u32 {
         dumb_map_offset: T::Object::<Uninit>::ALLOC_OPS.dumb_map_offset,
 
         show_fdinfo: None,
-        fbdev_probe: T::Object::ALLOC_OPS.fbdev_probe,
+        fbdev_probe: T::Object::<Uninit>::ALLOC_OPS.fbdev_probe,
 
         major: T::INFO.major,
         minor: T::INFO.minor,
@@ -306,15 +306,17 @@ pub fn new(
         // won't be called during its lifetime and that the device is unregistered.
         let drm_dev = unsafe { ARef::from_raw(raw_drm) };
 
-        let mode_config_info = drm::Device::<T>::has_kms().then(|| {
+        let mode_config_info = if drm::Device::<T>::has_kms() {
             // SAFETY: This is the only place that we call probe_kms
-            unsafe { T::Kms::probe_kms(&drm_dev)? }
-        });
+            Some(unsafe { T::Kms::probe_kms(&drm_dev)? })
+        } else {
+            None
+        };
 
         Ok(Self {
             // SAFETY: We just completed all of the initialization steps for this device.
             dev: unsafe { mem::transmute(drm_dev) },
-            preferred_fourcc: mode_config_info.preferred_fourcc,
+            preferred_fourcc: mode_config_info.and_then(|i| i.preferred_fourcc),
             _p: NotThreadSafe,
         })
     }
diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs
index c7716afabcb0..b7e80df6289e 100644
--- a/rust/kernel/drm/driver.rs
+++ b/rust/kernel/drm/driver.rs
@@ -10,7 +10,6 @@
     devres,
     drm::{
         self,
-        kms::private::KmsImpl as KmsImplPrivate,
     },
     error::to_result,
     prelude::*,
@@ -214,6 +213,7 @@ fn new<'bound, E>(
             return Err(e);
         }
 
+        let preferred_fourcc = drm.preferred_fourcc;
         // SAFETY: We just called `drm_dev_register` above
         let new = NonNull::from(unsafe { drm.assume_ctx() });
 
@@ -226,12 +226,12 @@ fn new<'bound, E>(
 
         #[cfg(CONFIG_DRM_CLIENT = "y")]
         if drm::Device::<T>::has_kms() {
-            if let Some(fourcc) = drm.preferred_fourcc {
+            if let Some(fourcc) = preferred_fourcc {
                 // SAFETY: We just registered `drm` above, fulfilling the C API requirements
-                unsafe { bindings::drm_client_setup_with_fourcc(drm.as_raw(), fourcc) }
+                unsafe { bindings::drm_client_setup_with_fourcc(new.as_raw(), fourcc) }
             } else {
                 // SAFETY: We just registered `drm` above, fulfilling the C API requirements
-                unsafe { bindings::drm_client_setup(drm.as_raw(), ptr::null()) }
+                unsafe { bindings::drm_client_setup(new.as_raw(), ptr::null()) }
             }
         }
 
diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs
index d07389fd510c..7c7e1d59c892 100644
--- a/rust/kernel/drm/kms.rs
+++ b/rust/kernel/drm/kms.rs
@@ -12,7 +12,7 @@
     },
     error::to_result,
     prelude::*,
-    sync::{aref::ARef, Mutex, MutexGuard},
+    sync::{aref::{ARef, AlwaysRefCounted}, Mutex, MutexGuard, SpinLockIrq},
 };
 use bindings;
 use core::{
@@ -122,23 +122,25 @@ fn deref(&self) -> &Self::Target {
 /// [`PhantomData<Self>`]: PhantomData
 #[vtable]
 pub trait KmsDriver: Driver {
-    // The driver's mode-object implementations. Left *unbounded* to break a trait
-    // well-formedness cycle: the natural bound (`type Crtc: DriverCrtc<Driver = Self>`, etc.)
-    // makes every `T: KmsDriver` obligation pull in the mutually-recursive Driver* web, which
-    // the compiler cannot evaluate (it loops in obligation processing and exhausts memory). The
-    // `DriverConnector`/`DriverCrtc`/... bounds are required at the use sites as `where`-clauses
-    // instead (an assumption, not an eagerly WF-checked trait-definition obligation).
-    //
-    // TODO: unneeded once we support multiple `Driver*` implementations per driver.
-
     /// The driver's [`DriverConnector`](connector::DriverConnector) implementation.
-    type Connector;
+    ///
+    /// TODO: This will be unneeded once we support multiple `DriverConnector` implementations.
+    type Connector: connector::DriverConnector<Driver = Self>;
+
     /// The driver's [`DriverPlane`](plane::DriverPlane) implementation.
-    type Plane;
+    ///
+    /// TODO: This will be unneeded once we support multiple `DriverPlane` implementations.
+    type Plane: plane::DriverPlane<Driver = Self>;
+
     /// The driver's [`DriverCrtc`](crtc::DriverCrtc) implementation.
-    type Crtc;
+    ///
+    /// TODO: This will be unneeded once we support multiple `DriverCrtc` implementations.
+    type Crtc: crtc::DriverCrtc<Driver = Self>;
+
     /// The driver's [`DriverEncoder`](encoder::DriverEncoder) implementation.
-    type Encoder;
+    ///
+    /// TODO: This will be unneeded once we support multiple `DriverEncoder` implementations.
+    type Encoder: encoder::DriverEncoder<Driver = Self>;
 
     /// Return a [`ModeConfigInfo`] structure for this [`device::Device`].
     fn mode_config_info(dev: &Device<Self, Uninit>) -> Result<ModeConfigInfo>
@@ -372,6 +374,13 @@ pub fn num_crtcs(&self) -> u32 {
         // * num_crtc is always >= 0, so casting to u32 is fine
         unsafe { (*self.as_raw()).mode_config.num_crtc as u32 }
     }
+
+    /// Returns a reference to the `event` spinlock for this [`Device`].
+    #[inline]
+    pub(crate) fn event_lock(&self) -> &SpinLockIrq<()> {
+        // SAFETY: `event_lock` is initialized for as long as `self` is exposed to users.
+        unsafe { SpinLockIrq::from_raw(addr_of_mut!((*self.as_raw()).event_lock)) }
+    }
 }
 
 impl<T: KmsDriver> Device<T> {
@@ -512,6 +521,7 @@ unsafe fn dec_ref(obj: core::ptr::NonNull<Self>) {
 ///
 /// `ModeObjectVtable::vtable()` must always return a valid pointer to the relevant mode object's
 /// vtable.
+#[allow(dead_code)] // Scaffolding for future vtable-pointer comparison; see doc above.
 pub(crate) unsafe trait ModeObjectVtable {
     /// The type for the auto-generated vtable.
     type Vtable;
@@ -539,6 +549,19 @@ pub(crate) unsafe trait ModeObjectVtable {
 pub struct ModeConfigGuard<'a, T: KmsDriver>(MutexGuard<'a, ()>, PhantomData<T>);
 
 impl<'a, T: KmsDriver> ModeConfigGuard<'a, T> {
+    /// Construct a new [`ModeConfigGuard`].
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that `drm_device.mode_config.mutex` is acquired.
+    pub(crate) unsafe fn new(drm: &'a Device<T>) -> Self {
+        // SAFETY: Our safety contract fulfills the requirements of `MutexGuard::new()`.
+        Self(
+            unsafe { MutexGuard::new(drm.mode_config_mutex(), ()) },
+            PhantomData,
+        )
+    }
+
     /// Return the [`Device`] that this [`ModeConfigGuard`] belongs to.
     pub fn drm_dev(&self) -> &'a Device<T> {
         let lock: *mut bindings::mutex = ptr::from_ref(self.0.lock_ref()).cast_mut().cast();
diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs
index aca7be50e583..1c7c7b586340 100644
--- a/rust/kernel/drm/kms/connector.rs
+++ b/rust/kernel/drm/kms/connector.rs
@@ -10,7 +10,7 @@
 use crate::{
     alloc::KBox,
     bindings,
-    drm::{device::Device, kms::{NewKmsDevice, Probing}},
+    drm::{device::{Device, Uninit}, kms::{NewKmsDevice, Probing}},
     error::to_result,
     prelude::*,
     types::{NotThreadSafe, Opaque},
@@ -139,12 +139,12 @@ pub trait DriverConnector: Send + Sync + Sized {
     /// The [`DriverConnectorState`] implementation for this [`DriverConnector`].
     ///
     /// See [`DriverConnectorState`] for more info.
-    type State: DriverConnectorState<Connector = Self>;
+    type State: DriverConnectorState;
 
     /// The constructor for creating a [`Connector`] using this [`DriverConnector`] implementation.
     ///
     /// Drivers may use this to instantiate their [`DriverConnector`] object.
-    fn new(device: &Device<Self::Driver>, args: Self::Args) -> impl PinInit<Self, Error>;
+    fn new(device: &Device<Self::Driver, Uninit>, args: Self::Args) -> impl PinInit<Self, Error>;
 
     /// Retrieve a list of available display modes for this [`Connector`].
     fn get_modes<'a>(
@@ -700,17 +700,13 @@ pub struct ConnectorState<T: DriverConnectorState> {
 /// [`struct drm_connector`]: srctree/include/drm_connector.h
 /// [`struct drm_connector_state`]: srctree/include/drm_connector.h
 pub trait DriverConnectorState: Clone + Default + Sized {
-    /// The parent [`DriverConnector`]. Unbounded to break the trait WF cycle; the
-    /// `DriverConnector` bound is required at use sites instead.
-    type Connector;
+    /// The parent [`DriverConnector`].
+    type Connector: DriverConnector;
 }
 
 impl<T: DriverConnectorState> Sealed for ConnectorState<T> {}
 
-impl<T: DriverConnectorState> AsRawConnectorState for ConnectorState<T>
-where
-    T::Connector: DriverConnector,
-{
+impl<T: DriverConnectorState> AsRawConnectorState for ConnectorState<T> {
     type Connector = Connector<T::Connector>;
 }
 
@@ -724,10 +720,7 @@ unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_connector_state {
     }
 }
 
-impl<T: DriverConnectorState> FromRawConnectorState for ConnectorState<T>
-where
-    T::Connector: DriverConnector,
-{
+impl<T: DriverConnectorState> FromRawConnectorState for ConnectorState<T> {
     unsafe fn from_raw<'a>(ptr: *const bindings::drm_connector_state) -> &'a Self {
         // Our data layout starts with `bindings::drm_connector_state`.
         let ptr: *const Self = ptr.cast();
@@ -866,10 +859,7 @@ pub(super) fn new<D: KmsDriver>(
     }
 }
 
-impl<'a, T: DriverConnectorState> Deref for ConnectorStateMutator<'a, ConnectorState<T>>
-where
-    T::Connector: DriverConnector,
-{
+impl<'a, T: DriverConnectorState> Deref for ConnectorStateMutator<'a, ConnectorState<T>> {
     type Target = T;
 
     fn deref(&self) -> &Self::Target {
@@ -877,10 +867,7 @@ fn deref(&self) -> &Self::Target {
     }
 }
 
-impl<'a, T: DriverConnectorState> DerefMut for ConnectorStateMutator<'a, ConnectorState<T>>
-where
-    T::Connector: DriverConnector,
-{
+impl<'a, T: DriverConnectorState> DerefMut for ConnectorStateMutator<'a, ConnectorState<T>> {
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.state.inner
     }
diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs
index 4c6cb6bbdcb8..9b43501bfb58 100644
--- a/rust/kernel/drm/kms/crtc.rs
+++ b/rust/kernel/drm/kms/crtc.rs
@@ -11,7 +11,7 @@
 use crate::{
     alloc::KBox,
     bindings,
-    drm::device::Device,
+    drm::device::{Device, Uninit},
     error::{from_result, to_result},
     prelude::*,
     types::{NotThreadSafe, Opaque},
@@ -107,8 +107,8 @@ pub trait DriverCrtc: Send + Sync + Sized {
             },
             mode_set_nofb: None,
             mode_set_base: None,
-            mode_set_base_atomic: None,
             get_scanout_position: None,
+            handle_vblank_timeout: None,
         },
     };
 
@@ -124,7 +124,7 @@ pub trait DriverCrtc: Send + Sync + Sized {
     /// The [`DriverCrtcState`] implementation for this [`DriverCrtc`].
     ///
     /// See [`DriverCrtcState`] for more info.
-    type State: DriverCrtcState<Crtc = Self>;
+    type State: DriverCrtcState;
 
     /// The driver's optional hardware vblank implementation
     ///
@@ -135,7 +135,7 @@ pub trait DriverCrtc: Send + Sync + Sized {
     /// The constructor for creating a [`Crtc`] using this [`DriverCrtc`] implementation.
     ///
     /// Drivers may use this to instantiate their [`DriverCrtc`] object.
-    fn new(device: &Device<Self::Driver>, args: &Self::Args) -> impl PinInit<Self, Error>;
+    fn new(device: &Device<Self::Driver, Uninit>, args: &Self::Args) -> impl PinInit<Self, Error>;
 
     /// The optional [`drm_crtc_helper_funcs.atomic_check`] hook for this crtc.
     ///
@@ -561,9 +561,10 @@ impl<T: DriverCrtcState> Sealed for CrtcState<T> {}
 /// [`struct drm_crtc`]: srctree/include/drm_crtc.h
 /// [`struct drm_crtc_state`]: srctree/include/drm_crtc.h
 pub trait DriverCrtcState: Clone + Default + Unpin {
-    /// The parent CRTC driver for this CRTC state. Unbounded to break the trait WF cycle;
-    /// the `DriverCrtc` bound is required at use sites instead.
-    type Crtc;
+    /// The parent CRTC driver for this CRTC state
+    type Crtc: DriverCrtc<State = Self>
+    where
+        Self: Sized;
 }
 
 /// The main interface for a [`struct drm_crtc_state`].
@@ -701,17 +702,11 @@ fn as_raw(&self) -> *mut bindings::drm_crtc_state {
     }
 }
 
-impl<T: DriverCrtcState> AsRawCrtcState for CrtcState<T>
-where
-    T::Crtc: DriverCrtc,
-{
+impl<T: DriverCrtcState> AsRawCrtcState for CrtcState<T> {
     type Crtc = Crtc<T::Crtc>;
 }
 
-impl<T: DriverCrtcState> FromRawCrtcState for CrtcState<T>
-where
-    T::Crtc: DriverCrtc,
-{
+impl<T: DriverCrtcState> FromRawCrtcState for CrtcState<T> {
     unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc_state) -> &'a Self {
         // SAFETY: Our data layout starts with `bindings::drm_crtc_state`
         unsafe { &*(ptr.cast()) }
@@ -818,10 +813,7 @@ fn drop(&mut self) {
     }
 }
 
-impl<'a, T: DriverCrtcState> Deref for CrtcStateMutator<'a, CrtcState<T>>
-where
-    T::Crtc: DriverCrtc,
-{
+impl<'a, T: DriverCrtcState> Deref for CrtcStateMutator<'a, CrtcState<T>> {
     type Target = T;
 
     fn deref(&self) -> &Self::Target {
@@ -831,10 +823,7 @@ fn deref(&self) -> &Self::Target {
     }
 }
 
-impl<'a, T: DriverCrtcState> DerefMut for CrtcStateMutator<'a, CrtcState<T>>
-where
-    T::Crtc: DriverCrtc,
-{
+impl<'a, T: DriverCrtcState> DerefMut for CrtcStateMutator<'a, CrtcState<T>> {
     fn deref_mut(&mut self) -> &mut Self::Target {
         // SAFETY: Our interface ensures that `self.state.inner` follows rust's data-aliasing rules,
         // so this is safe
diff --git a/rust/kernel/drm/kms/encoder.rs b/rust/kernel/drm/kms/encoder.rs
index 5f860faf8b61..47fd223a4d3a 100644
--- a/rust/kernel/drm/kms/encoder.rs
+++ b/rust/kernel/drm/kms/encoder.rs
@@ -9,7 +9,7 @@
 };
 use crate::{
     alloc::KBox,
-    drm::device::Device,
+    drm::device::{Device, Uninit},
     error::to_result,
     prelude::*,
     types::{NotThreadSafe, Opaque},
@@ -105,7 +105,7 @@ pub trait DriverEncoder: Send + Sync + Sized {
     /// The constructor for creating a [`Encoder`] using this [`DriverEncoder`] implementation.
     ///
     /// Drivers may use this to instantiate their [`DriverEncoder`] object.
-    fn new(device: &Device<Self::Driver>, args: Self::Args) -> impl PinInit<Self, Error>;
+    fn new(device: &Device<Self::Driver, Uninit>, args: Self::Args) -> impl PinInit<Self, Error>;
 }
 
 /// The generated C vtable for a [`DriverEncoder`].
diff --git a/rust/kernel/drm/kms/modes.rs b/rust/kernel/drm/kms/modes.rs
index 0e8dc434487d..88ea27729212 100644
--- a/rust/kernel/drm/kms/modes.rs
+++ b/rust/kernel/drm/kms/modes.rs
@@ -1,4 +1,9 @@
 // SPDX-License-Identifier: GPL-2.0
+
+//! DRM display mode definitions ([`struct drm_display_mode`]).
+//!
+//! [`struct drm_display_mode`]: srctree/include/drm/drm_modes.h
+
 use bindings;
 
 use crate::types::Opaque;
diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs
index 47e05fe6bee1..375f79bd3ceb 100644
--- a/rust/kernel/drm/kms/plane.rs
+++ b/rust/kernel/drm/kms/plane.rs
@@ -11,7 +11,7 @@
 use crate::{
     alloc::KBox,
     bindings,
-    drm::{device::Device, fourcc::*},
+    drm::{device::{Device, Uninit}, fourcc::*},
     error::{from_result, to_result, Error},
     prelude::*,
     types::{NotThreadSafe, Opaque},
@@ -98,12 +98,12 @@ pub trait DriverPlane: Send + Sync + Sized {
     /// The [`DriverPlaneState`] implementation for this [`DriverPlane`].
     ///
     /// See [`DriverPlaneState`] for more info.
-    type State: DriverPlaneState<Plane = Self>;
+    type State: DriverPlaneState;
 
     /// The constructor for creating a [`Plane`] using this [`DriverPlane`] implementation.
     ///
     /// Drivers may use this to instantiate their [`DriverPlane`] object.
-    fn new(device: &Device<Self::Driver>, args: Self::Args) -> impl PinInit<Self, Error>;
+    fn new(device: &Device<Self::Driver, Uninit>, args: Self::Args) -> impl PinInit<Self, Error>;
 
     /// The optional [`drm_plane_helper_funcs.atomic_update`] hook for this plane.
     ///
@@ -689,17 +689,13 @@ pub struct PlaneState<T: DriverPlaneState> {
 /// [`struct drm_plane`]: srctree/include/drm_plane.h
 /// [`struct drm_plane_state`]: srctree/include/drm_plane.h
 pub trait DriverPlaneState: Clone + Default + Sized {
-    /// The type for this driver's drm_plane implementation. Unbounded to break the trait WF
-    /// cycle; the `DriverPlane` bound is required at use sites instead.
-    type Plane;
+    /// The type for this driver's drm_plane implementation
+    type Plane: DriverPlane;
 }
 
 impl<T: DriverPlaneState> Sealed for PlaneState<T> {}
 
-impl<T: DriverPlaneState> AsRawPlaneState for PlaneState<T>
-where
-    T::Plane: DriverPlane,
-{
+impl<T: DriverPlaneState> AsRawPlaneState for PlaneState<T> {
     type Plane = Plane<T::Plane>;
 }
 
@@ -713,10 +709,7 @@ unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_plane_state {
     }
 }
 
-impl<T: DriverPlaneState> FromRawPlaneState for PlaneState<T>
-where
-    T::Plane: DriverPlane,
-{
+impl<T: DriverPlaneState> FromRawPlaneState for PlaneState<T> {
     unsafe fn from_raw<'a>(ptr: *const bindings::drm_plane_state) -> &'a Self {
         // Our data layout starts with `bindings::drm_plane_state`.
         let ptr: *const Self = ptr.cast();
@@ -892,10 +885,7 @@ unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_plane_state {
 
 impl<'a, T: FromRawPlaneState> Sealed for PlaneStateMutator<'a, T> {}
 
-impl<'a, T: DriverPlaneState> Deref for PlaneStateMutator<'a, PlaneState<T>>
-where
-    T::Plane: DriverPlane,
-{
+impl<'a, T: DriverPlaneState> Deref for PlaneStateMutator<'a, PlaneState<T>> {
     type Target = T;
 
     fn deref(&self) -> &Self::Target {
@@ -903,10 +893,7 @@ fn deref(&self) -> &Self::Target {
     }
 }
 
-impl<'a, T: DriverPlaneState> DerefMut for PlaneStateMutator<'a, PlaneState<T>>
-where
-    T::Plane: DriverPlane,
-{
+impl<'a, T: DriverPlaneState> DerefMut for PlaneStateMutator<'a, PlaneState<T>> {
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.state.inner
     }
diff --git a/rust/kernel/drm/kms/vblank.rs b/rust/kernel/drm/kms/vblank.rs
index 534fd18f63b4..84e5cbbe923d 100644
--- a/rust/kernel/drm/kms/vblank.rs
+++ b/rust/kernel/drm/kms/vblank.rs
@@ -4,7 +4,7 @@
 //!
 //! C header: [`include/drm/drm_vblank.h`](srcfree/include/drm/drm_vblank.h)
 
-use super::{crtc::*, ModeObject, modes::*, Sealed};
+use super::{crtc::*, ModeObject, modes::*};
 use bindings;
 use core::{
     marker::*,
@@ -62,9 +62,8 @@ fn get_vblank_timestamp(
 /// Drivers interested in implementing vblank support should refer to [`VblankSupport`], drivers
 /// that don't have vblank support can use [`PhantomData`].
 pub trait VblankImpl {
-    /// The parent [`DriverCrtc`]. Unbounded to break the trait WF cycle; the `DriverCrtc`
-    /// bound is required at use sites instead.
-    type Crtc;
+    /// The parent [`DriverCrtc`].
+    type Crtc: DriverCrtc<VblankImpl = Self>;
 
     /// The generated [`VblankOps`].
     const VBLANK_OPS: VblankOps;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 05/18] rust: drm: expose <drm/display/drm_hdcp.h> HDCP 2.2 message definitions
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
                     ` (3 preceding siblings ...)
  2026-07-03  3:00   ` [RFC PATCH v2 04/18] rust: drm: kms: build the kernel crate clean under -Znext-solver Mike Lothian
@ 2026-07-03  3:00   ` Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 06/18] rust: drm: kms: add a Framebuffer::vmap() guard Mike Lothian
                     ` (12 subsequent siblings)
  17 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:00 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

Add <drm/display/drm_hdcp.h> to the bindings so Rust DRM drivers can use
the canonical HDCP 2.2 message IDs (HDCP_2_2_AKE_INIT ..
HDCP_2_2_REP_STREAM_READY), length constants and hdcp2_* message structs
instead of redefining them. The header only pulls in <linux/types.h>, so
it adds no new heavy dependencies.

The Vino DisplayLink DL3 driver uses these for its HDCP 2.2 AKE/LC/SKE
handshake (carried over a vendor USB transport rather than the DP/HDMI
HDCP transport, so only the message definitions are reused, not the
drm_hdcp_helper state machine).

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-sonnet-5 [Claude-Code]
---
 rust/bindings/bindings_helper.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index e41d2716fb2e..e5cfda610781 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -34,6 +34,7 @@
 
 #include <linux/acpi.h>
 #include <linux/gpu_buddy.h>
+#include <drm/display/drm_hdcp.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_connector.h>
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 06/18] rust: drm: kms: add a Framebuffer::vmap() guard
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
                     ` (4 preceding siblings ...)
  2026-07-03  3:00   ` [RFC PATCH v2 05/18] rust: drm: expose <drm/display/drm_hdcp.h> HDCP 2.2 message definitions Mike Lothian
@ 2026-07-03  3:00   ` Mike Lothian
  2026-07-07 21:51     ` lyude
  2026-07-03  3:00   ` [RFC PATCH v2 07/18] rust: drm: kms: add safe accessors for common state and connector modes Mike Lothian
                     ` (11 subsequent siblings)
  17 siblings, 1 reply; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:00 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

Framebuffer::vmap() maps a framebuffer's plane-0 backing pages into the
kernel address space via drm_gem_fb_vmap()/vunmap(), returning an RAII
FramebufferVmap guard that unmaps on drop. Only single-CPU-visible-plane
framebuffers are supported (packed formats backed by GEM-shmem/CMA;
multi-plane YUV or an unmapped imported dma-buf return EINVAL). This is
the driver-side FbVmap guard the v1 series carried, moved into the layer
proper so every KMS driver gets it instead of hand-rolling the same
map/use/unmap dance.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-sonnet-5 [Claude-Code]
---
 rust/kernel/drm/kms/framebuffer.rs | 67 +++++++++++++++++++++++++++++-
 1 file changed, 66 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/drm/kms/framebuffer.rs b/rust/kernel/drm/kms/framebuffer.rs
index 54d0391388a9..1ec6779ba7de 100644
--- a/rust/kernel/drm/kms/framebuffer.rs
+++ b/rust/kernel/drm/kms/framebuffer.rs
@@ -5,7 +5,12 @@
 //! C header: [`include/drm/drm_framebuffer.h`](srctree/include/drm/drm_framebuffer.h)
 
 use super::{KmsDriver, ModeObject, Sealed};
-use crate::{drm::device::Device, types::*};
+use crate::{
+    drm::device::Device,
+    error::{code::EINVAL, to_result},
+    prelude::*,
+    types::*,
+};
 use bindings;
 use core::{marker::*, ptr};
 
@@ -67,4 +72,64 @@ pub(super) unsafe fn from_raw<'a>(ptr: *const bindings::drm_framebuffer) -> &'a
         // SAFETY: Our data layout is identical to drm_framebuffer
         unsafe { &*ptr.cast() }
     }
+
+    /// Return the raw `bindings::drm_framebuffer` for this framebuffer.
+    #[inline]
+    pub(crate) fn as_raw(&self) -> *mut bindings::drm_framebuffer {
+        self.0.get()
+    }
+
+    /// Map this framebuffer's plane-0 backing pages into the kernel address space for CPU
+    /// access, for the duration of the returned guard.
+    ///
+    /// Only framebuffers with a single, CPU-visible plane are supported (i.e. packed formats
+    /// backed by GEM-shmem/CMA memory, not multi-plane YUV or an IMPORTED dma-buf without a
+    /// CPU mapping); other cases return `EINVAL`.
+    pub fn vmap(&self) -> Result<FramebufferVmap<'_, T>> {
+        // SAFETY: `iosys_map` is POD (a pointer union plus a bool); all-zero is a valid
+        // "not mapped" value that `drm_gem_fb_vmap` overwrites for present planes.
+        let mut map: [bindings::iosys_map; 4] = unsafe { core::mem::zeroed() };
+        let mut data_map: [bindings::iosys_map; 4] = unsafe { core::mem::zeroed() };
+        // SAFETY: `self.as_raw()` is a valid, GEM-backed framebuffer for the lifetime of `self`.
+        to_result(unsafe {
+            bindings::drm_gem_fb_vmap(self.as_raw(), map.as_mut_ptr(), data_map.as_mut_ptr())
+        })?;
+        // SAFETY: `map[0]` was just filled in by `drm_gem_fb_vmap` above.
+        let vaddr = unsafe { map[0].__bindgen_anon_1.vaddr };
+        if vaddr.is_null() {
+            // SAFETY: balances the vmap just done, with the same `map`.
+            unsafe { bindings::drm_gem_fb_vunmap(self.as_raw(), map.as_mut_ptr()) };
+            return Err(EINVAL);
+        }
+        Ok(FramebufferVmap { fb: self, map, _p: PhantomData })
+    }
+}
+
+/// An RAII guard over a CPU mapping of a [`Framebuffer`]'s plane-0 backing pages, created by
+/// [`Framebuffer::vmap`].
+///
+/// The mapping is torn down when this guard is dropped, so an early return between mapping and
+/// use can never leak it.
+pub struct FramebufferVmap<'a, T: KmsDriver> {
+    fb: &'a Framebuffer<T>,
+    map: [bindings::iosys_map; 4],
+    _p: PhantomData<T>,
+}
+
+impl<'a, T: KmsDriver> FramebufferVmap<'a, T> {
+    /// Plane 0's CPU virtual base address (guaranteed non-null for the guard's lifetime).
+    #[inline]
+    pub fn as_ptr(&self) -> *const u8 {
+        // SAFETY: set non-null in `Framebuffer::vmap`; `vaddr` is the active union member.
+        (unsafe { self.map[0].__bindgen_anon_1.vaddr }) as *const u8
+    }
+}
+
+impl<'a, T: KmsDriver> Drop for FramebufferVmap<'a, T> {
+    fn drop(&mut self) {
+        // SAFETY: `self.fb.as_raw()`/`self.map` are exactly the pair passed to
+        // `drm_gem_fb_vmap` in `Framebuffer::vmap`, and the mapping has not been released
+        // since.
+        unsafe { bindings::drm_gem_fb_vunmap(self.fb.as_raw(), self.map.as_mut_ptr()) };
+    }
 }
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 07/18] rust: drm: kms: add safe accessors for common state and connector modes
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
                     ` (5 preceding siblings ...)
  2026-07-03  3:00   ` [RFC PATCH v2 06/18] rust: drm: kms: add a Framebuffer::vmap() guard Mike Lothian
@ 2026-07-03  3:00   ` Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 08/18] rust: drm: tyr: add the Kms associated type Mike Lothian
                     ` (10 subsequent siblings)
  17 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:00 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

Add safe wrappers for the handful of raw drm_kms fields and helpers a
real KMS driver reaches for, so drivers need no raw escape hatch:

  - drm::Device::hotplug_event() wraps drm_kms_helper_hotplug_event().
  - RawCrtcState::mode() returns the state's programmed DisplayMode.
  - DisplayMode gains hdisplay()/hsync_start()/hsync_end()/htotal()/
    vdisplay()/vsync_start()/vsync_end()/vtotal()/clock()/vrefresh().
  - RawPlaneState::crtc_w()/crtc_h() return the destination rectangle.
  - ConnectorGuard::add_edid_modes() parses an EDID blob, updates the
    connector and adds its modes (the safe counterpart to the existing
    add_modes_noedid()).

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-sonnet-5 [Claude-Code]
---
 rust/kernel/drm/kms.rs           | 12 ++++++
 rust/kernel/drm/kms/connector.rs | 20 +++++++++
 rust/kernel/drm/kms/crtc.rs      | 13 +++++-
 rust/kernel/drm/kms/modes.rs     | 70 ++++++++++++++++++++++++++++++++
 rust/kernel/drm/kms/plane.rs     | 10 +++++
 5 files changed, 123 insertions(+), 2 deletions(-)

diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs
index 7c7e1d59c892..7207466094e7 100644
--- a/rust/kernel/drm/kms.rs
+++ b/rust/kernel/drm/kms.rs
@@ -114,6 +114,18 @@ fn deref(&self) -> &Self::Target {
     }
 }
 
+impl<T: KmsDriver, C: DeviceContext> Device<T, C> {
+    /// Send a hotplug uevent to userspace, prompting it to re-probe connector state.
+    ///
+    /// A thin safe wrapper over `drm_kms_helper_hotplug_event()`, for drivers that detect a
+    /// connection change out of band (for example a dock delivering an EDID after bring-up).
+    pub fn hotplug_event(&self) {
+        // SAFETY: `self.as_raw()` is our live `drm_device`; a KMS driver has the KMS helper
+        // state this call requires.
+        unsafe { bindings::drm_kms_helper_hotplug_event(self.as_raw()) };
+    }
+}
+
 /// A trait which must be implemented by drivers that wish to support KMS
 ///
 /// It should be implemented for the same type that implements [`Driver`]. Drivers which don't
diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs
index 1c7c7b586340..591153de7ab4 100644
--- a/rust/kernel/drm/kms/connector.rs
+++ b/rust/kernel/drm/kms/connector.rs
@@ -574,6 +574,26 @@ pub fn set_preferred_mode(&self, (h_pref, w_pref): (u32, u32)) {
         // SAFETY: We hold the locks required to call this via our type invariants.
         unsafe { bindings::drm_set_preferred_mode(self.as_raw(), h_pref, w_pref) }
     }
+
+    /// Parse a raw EDID blob, update the connector's display information from it, and add the
+    /// modes it advertises to the connector's mode list.
+    ///
+    /// Returns how many modes were added (0 if the blob is rejected).
+    pub fn add_edid_modes(&self, edid: &[u8]) -> i32 {
+        // SAFETY: `edid` is a valid byte buffer of `edid.len()` bytes; `drm_edid_alloc` copies it.
+        let drm_edid = unsafe { bindings::drm_edid_alloc(edid.as_ptr().cast(), edid.len()) };
+        if drm_edid.is_null() {
+            return 0;
+        }
+        // SAFETY: We hold the locks required via our type invariants; `drm_edid` was just
+        // allocated above.
+        unsafe { bindings::drm_edid_connector_update(self.as_raw(), drm_edid) };
+        // SAFETY: Same invariants; adds the EDID-derived modes and returns the count.
+        let n = unsafe { bindings::drm_edid_connector_add_modes(self.as_raw()) };
+        // SAFETY: `drm_edid` was allocated by `drm_edid_alloc` above and is no longer needed.
+        unsafe { bindings::drm_edid_free(drm_edid) };
+        n
+    }
 }
 
 /// A trait implemented by any type which can produce a reference to a
diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs
index 9b43501bfb58..fbe113676e2f 100644
--- a/rust/kernel/drm/kms/crtc.rs
+++ b/rust/kernel/drm/kms/crtc.rs
@@ -5,8 +5,8 @@
 //! C header: [`include/drm/drm_crtc.h`](srctree/include/drm/drm_crtc.h)
 
 use super::{
-    atomic::*, plane::*, vblank::*, KmsDriver, ModeObject, ModeObjectVtable, StaticModeObject,
-    NewKmsDevice, Probing, Sealed,
+    atomic::*, modes::DisplayMode, plane::*, vblank::*, KmsDriver, ModeObject, ModeObjectVtable,
+    StaticModeObject, NewKmsDevice, Probing, Sealed,
 };
 use crate::{
     alloc::KBox,
@@ -643,6 +643,7 @@ pub trait AsRawCrtcState: private::AsRawCrtcState {
 pub(crate) mod private {
     use super::*;
 
+    /// The raw-pointer half of [`AsRawCrtcState`](super::AsRawCrtcState).
     #[allow(unreachable_pub)]
     pub trait AsRawCrtcState {
         /// Return a raw pointer to the DRM CRTC state
@@ -677,6 +678,14 @@ fn active(&self) -> bool {
         // this access is serialized
         unsafe { (*self.as_raw()).active }
     }
+
+    /// Returns the display mode programmed into this CRTC state.
+    fn mode(&self) -> &DisplayMode {
+        // SAFETY: `mode` is embedded in `drm_crtc_state`, so it shares the state's lifetime, and
+        // is only mutated from the atomic check context - invariant beyond that point, so our
+        // interface can ensure this access is serialized.
+        unsafe { DisplayMode::as_ref(&(*self.as_raw()).mode) }
+    }
 }
 impl<T: AsRawCrtcState> RawCrtcState for T {}
 
diff --git a/rust/kernel/drm/kms/modes.rs b/rust/kernel/drm/kms/modes.rs
index 88ea27729212..16024855c32f 100644
--- a/rust/kernel/drm/kms/modes.rs
+++ b/rust/kernel/drm/kms/modes.rs
@@ -78,4 +78,74 @@ pub fn crtc_vtotal(&self) -> u16 {
         // SAFETY: Reading these fields is safe via our type invariants
         unsafe { (*self.as_raw()).crtc_vtotal }
     }
+
+    /// Horizontal active pixels (`hdisplay`).
+    #[inline]
+    pub fn hdisplay(&self) -> u16 {
+        // SAFETY: Reading these fields is safe via our type invariants
+        unsafe { (*self.as_raw()).hdisplay }
+    }
+
+    /// Horizontal sync pulse start (`hsync_start`).
+    #[inline]
+    pub fn hsync_start(&self) -> u16 {
+        // SAFETY: Reading these fields is safe via our type invariants
+        unsafe { (*self.as_raw()).hsync_start }
+    }
+
+    /// Horizontal sync pulse end (`hsync_end`).
+    #[inline]
+    pub fn hsync_end(&self) -> u16 {
+        // SAFETY: Reading these fields is safe via our type invariants
+        unsafe { (*self.as_raw()).hsync_end }
+    }
+
+    /// Horizontal total pixels including blanking (`htotal`).
+    #[inline]
+    pub fn htotal(&self) -> u16 {
+        // SAFETY: Reading these fields is safe via our type invariants
+        unsafe { (*self.as_raw()).htotal }
+    }
+
+    /// Vertical active scanlines (`vdisplay`).
+    #[inline]
+    pub fn vdisplay(&self) -> u16 {
+        // SAFETY: Reading these fields is safe via our type invariants
+        unsafe { (*self.as_raw()).vdisplay }
+    }
+
+    /// Vertical sync pulse start (`vsync_start`).
+    #[inline]
+    pub fn vsync_start(&self) -> u16 {
+        // SAFETY: Reading these fields is safe via our type invariants
+        unsafe { (*self.as_raw()).vsync_start }
+    }
+
+    /// Vertical sync pulse end (`vsync_end`).
+    #[inline]
+    pub fn vsync_end(&self) -> u16 {
+        // SAFETY: Reading these fields is safe via our type invariants
+        unsafe { (*self.as_raw()).vsync_end }
+    }
+
+    /// Vertical total scanlines including blanking (`vtotal`).
+    #[inline]
+    pub fn vtotal(&self) -> u16 {
+        // SAFETY: Reading these fields is safe via our type invariants
+        unsafe { (*self.as_raw()).vtotal }
+    }
+
+    /// Pixel clock for this display mode in kHz (`clock`).
+    #[inline]
+    pub fn clock(&self) -> i32 {
+        // SAFETY: Reading these fields is safe via our type invariants
+        unsafe { (*self.as_raw()).clock }
+    }
+
+    /// The refresh rate of this mode in Hz, as computed by `drm_mode_vrefresh()`.
+    #[inline]
+    pub fn vrefresh(&self) -> i32 {
+        // SAFETY: `drm_mode_vrefresh` only reads the mode, which is valid via our invariants.
+        unsafe { bindings::drm_mode_vrefresh(self.as_raw()) }
+    }
 }
diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs
index 375f79bd3ceb..3863dfe1b84c 100644
--- a/rust/kernel/drm/kms/plane.rs
+++ b/rust/kernel/drm/kms/plane.rs
@@ -595,6 +595,16 @@ fn plane(&self) -> &Self::Plane {
         unsafe { Self::Plane::from_raw(self.as_raw().plane) }
     }
 
+    /// The width of this plane's destination rectangle within the CRTC, in pixels (`crtc_w`).
+    fn crtc_w(&self) -> u32 {
+        self.as_raw().crtc_w
+    }
+
+    /// The height of this plane's destination rectangle within the CRTC, in pixels (`crtc_h`).
+    fn crtc_h(&self) -> u32 {
+        self.as_raw().crtc_h
+    }
+
     /// Return the current [`OpaqueCrtc`] assigned to this plane, if there is one.
     fn crtc<'a, 'b: 'a, D>(&'a self) -> Option<&'b OpaqueCrtc<D>>
     where
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 08/18] rust: drm: tyr: add the Kms associated type
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
                     ` (6 preceding siblings ...)
  2026-07-03  3:00   ` [RFC PATCH v2 07/18] rust: drm: kms: add safe accessors for common state and connector modes Mike Lothian
@ 2026-07-03  3:00   ` Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 09/18] rust: drm: add drm_event delivery Mike Lothian
                     ` (9 subsequent siblings)
  17 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:00 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

drm::Driver::Kms became a required associated type when the safe KMS
mode-object layer landed; tyr doesn't do KMS, so PhantomData<Self> is
the correct value (same as nova's). Base drift: this tree's tyr driver
predates that requirement.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-sonnet-5 [Claude-Code]
---
 drivers/gpu/drm/tyr/driver.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index 360f57cb0bd7..a598c61162cf 100644
--- a/drivers/gpu/drm/tyr/driver.rs
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -186,6 +186,7 @@ impl drm::Driver for TyrDrmDriver {
     type File = TyrDrmFileData;
     type Object<R: drm::DeviceContext> = drm::gem::shmem::Object<BoData, R>;
     type ParentDevice<Ctx: DeviceContext> = platform::Device<Ctx>;
+    type Kms = core::marker::PhantomData<Self>;
 
     const INFO: drm::DriverInfo = INFO;
     const FEAT_RENDER: bool = true;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 09/18] rust: drm: add drm_event delivery
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
                     ` (7 preceding siblings ...)
  2026-07-03  3:00   ` [RFC PATCH v2 08/18] rust: drm: tyr: add the Kms associated type Mike Lothian
@ 2026-07-03  3:00   ` Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 10/18] rust: drm: allow drivers to declare ioctls from their own uAPI module Mike Lothian
                     ` (8 subsequent siblings)
  17 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:00 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

DRM drivers deliver asynchronous events to the userspace client that owns a
struct drm_file (vblank completions, and driver-defined events such as the
DisplayLink evdi driver's mode/cursor/dpms/ddcci notifications), which userspace
reaps through the poll(2)/read(2) interface of the DRM character device. The
Rust DRM abstractions had no binding for this at all.

Add a drm::event module providing:

  - EventPayload, an unsafe marker trait for a #[repr(C)] event struct whose
    first field is a drm_event header (filled in by the binding);

  - EventChannel, which stores the receiver drm_file under the device's
    event_lock -- the same lock drm_event_reserve_init_locked() and
    drm_send_event_locked() require held -- so registering, clearing and sending
    are serialized against file close. The classic bug of reading the target
    file pointer outside the lock and delivering to a client that just
    disconnected (a NULL/UAF oops) is therefore unrepresentable.

event_lock() moves from the KMS Device impl to the core drm::Device impl, since
events are a DRM-core concept, not a KMS one; the vblank callers are unaffected.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/kernel/drm/device.rs |  19 +++-
 rust/kernel/drm/event.rs  | 215 ++++++++++++++++++++++++++++++++++++++
 rust/kernel/drm/kms.rs    |  11 +-
 rust/kernel/drm/mod.rs    |   1 +
 4 files changed, 236 insertions(+), 10 deletions(-)
 create mode 100644 rust/kernel/drm/event.rs

diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs
index 5b84ce18fb29..7088db9420fb 100644
--- a/rust/kernel/drm/device.rs
+++ b/rust/kernel/drm/device.rs
@@ -19,9 +19,12 @@
     },
     error::from_err_ptr,
     prelude::*,
-    sync::aref::{
-        ARef,
-        AlwaysRefCounted, //
+    sync::{
+        aref::{
+            ARef,
+            AlwaysRefCounted, //
+        },
+        SpinLockIrq,
     },
     types::{
         ForLt,
@@ -353,6 +356,16 @@ pub(crate) fn as_raw(&self) -> *mut bindings::drm_device {
         self.dev.get()
     }
 
+    /// Returns a reference to the `event` spinlock of this [`Device`].
+    ///
+    /// This is the `struct drm_device::event_lock` that the DRM core requires to be held while
+    /// reserving and sending events to userspace (see the [`event`](crate::drm::event) module).
+    #[inline]
+    pub(crate) fn event_lock(&self) -> &SpinLockIrq<()> {
+        // SAFETY: `event_lock` is initialized for as long as `self` is exposed to users.
+        unsafe { SpinLockIrq::from_raw(&raw mut (*self.as_raw()).event_lock) }
+    }
+
     /// Returns a reference to the registration data with lifetime shortened
     /// from `'static`.
     ///
diff --git a/rust/kernel/drm/event.rs b/rust/kernel/drm/event.rs
new file mode 100644
index 000000000000..39d65d2a8fb2
--- /dev/null
+++ b/rust/kernel/drm/event.rs
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! DRM event delivery to userspace.
+//!
+//! A DRM driver can deliver asynchronous events to the userspace client that owns a
+//! [`File`][crate::drm::File]. Userspace reaps them through the `poll(2)`/`read(2)` interface of
+//! the DRM character device. The C core calls this mechanism "events" and drivers such as
+//! `vkms`/`vc4`/DisplayLink's `evdi` use it to hand back vblank completions, mode-change
+//! notifications, cursor updates and so on.
+//!
+//! The C API (`drm_event_reserve_init_locked()` + `drm_send_event_locked()`) has one non-obvious
+//! rule: the `struct drm_device::event_lock` must be held while reserving and sending, and the
+//! target `struct drm_file` must be valid for that whole critical section. Reading the target file
+//! pointer outside the lock is a classic source of use-after-free/NULL-deref crashes (a driver's
+//! flush worker racing a client disconnect). [`EventChannel`] encodes that rule in its API: the
+//! receiver file is only ever touched while `event_lock` is held, so registration, teardown and
+//! delivery cannot race.
+//!
+//! C header: [`include/drm/drm_file.h`](srctree/include/drm/drm_file.h)
+
+use crate::{
+    bindings,
+    drm::{
+        self,
+        device::DeviceContext,
+        file::{DriverFile, File},
+        Device,
+    },
+    error::to_result,
+    interrupt,
+    prelude::*,
+};
+use core::{cell::UnsafeCell, mem};
+
+/// A driver-defined DRM event payload.
+///
+/// This is the fixed-size structure copied verbatim to userspace when the event is read. It must
+/// begin with a [`bindings::drm_event`] header; [`EventChannel::send`] fills that header's `type`
+/// and `length` for you from [`EventPayload::TYPE`] and `size_of::<Self>()`.
+///
+/// # Safety
+///
+/// Implementers must guarantee that:
+/// - `Self` is `#[repr(C)]` and its first field is a [`bindings::drm_event`], so that a
+///   `*mut Self` is also a valid `*mut drm_event`;
+/// - `Self` is plain-old-data: it has no [`Drop`] glue and every bit pattern written by the driver
+///   is safe to copy to userspace (do not place kernel pointers or padding holding secrets in it).
+pub unsafe trait EventPayload: Copy + 'static {
+    /// The `struct drm_event::type` code identifying this event to userspace.
+    const TYPE: u32;
+}
+
+/// Backing allocation for one in-flight event.
+///
+/// The [`bindings::drm_pending_event`] is deliberately the first field: once handed to the DRM
+/// core, the whole allocation is freed by a single `kfree()` of the `drm_pending_event` pointer
+/// (on delivery or on file close), which is only correct if it sits at offset 0.
+#[repr(C)]
+struct EventStorage<T: EventPayload> {
+    pending: bindings::drm_pending_event,
+    event: T,
+}
+
+/// A per-device channel that delivers driver events to the [`File`] registered as the receiver.
+///
+/// The receiver pointer is stored under the owning device's `event_lock` — the same lock the DRM
+/// core requires held while queuing events — so [`connect`](Self::connect),
+/// [`disconnect`](Self::disconnect), [`notify_close`](Self::notify_close) and [`send`](Self::send)
+/// are all serialized against each other and against the core's own event handling. This makes the
+/// "send to a file that just disconnected" race unrepresentable.
+///
+/// A channel is bound to exactly one [`Device`]: always pass the same device to every method.
+/// Drivers must call [`notify_close`](Self::notify_close) from their `postclose` hook (and/or
+/// [`disconnect`](Self::disconnect) when tearing the logical connection down) so the receiver
+/// pointer never outlives the file it refers to.
+pub struct EventChannel {
+    /// The receiver `drm_file`, or null when no client is connected.
+    ///
+    /// Only ever accessed while the owning device's `event_lock` is held.
+    receiver: UnsafeCell<*mut bindings::drm_file>,
+}
+
+// SAFETY: `receiver` is only accessed while holding the owning device's `event_lock`, which
+// serializes all access across threads.
+unsafe impl Send for EventChannel {}
+// SAFETY: See `Send`.
+unsafe impl Sync for EventChannel {}
+
+impl EventChannel {
+    /// Create a new, disconnected channel.
+    pub const fn new() -> Self {
+        Self {
+            receiver: UnsafeCell::new(core::ptr::null_mut()),
+        }
+    }
+
+    /// Register `file` as the receiver for events sent through this channel.
+    ///
+    /// Any previously registered file is replaced. Runs under `dev`'s `event_lock`.
+    pub fn connect<D: drm::Driver, C: DeviceContext, F: DriverFile>(
+        &self,
+        dev: &Device<D, C>,
+        file: &File<F>,
+    ) {
+        let irq = interrupt::local_interrupt_disable();
+        let _guard = dev.event_lock().lock_with(&irq);
+        // SAFETY: `receiver` is only accessed under `event_lock`, which is held here.
+        unsafe { *self.receiver.get() = file.as_raw() };
+    }
+
+    /// Clear the receiver, dropping any future events on the floor until the next
+    /// [`connect`](Self::connect). Runs under `dev`'s `event_lock`.
+    pub fn disconnect<D: drm::Driver, C: DeviceContext>(&self, dev: &Device<D, C>) {
+        let irq = interrupt::local_interrupt_disable();
+        let _guard = dev.event_lock().lock_with(&irq);
+        // SAFETY: `receiver` is only accessed under `event_lock`, which is held here.
+        unsafe { *self.receiver.get() = core::ptr::null_mut() };
+    }
+
+    /// Clear the receiver if it is `file`.
+    ///
+    /// Intended to be called from the driver's `postclose` hook so the stored pointer never
+    /// outlives the file. Runs under `dev`'s `event_lock`.
+    pub fn notify_close<D: drm::Driver, C: DeviceContext, F: DriverFile>(
+        &self,
+        dev: &Device<D, C>,
+        file: &File<F>,
+    ) {
+        let irq = interrupt::local_interrupt_disable();
+        let _guard = dev.event_lock().lock_with(&irq);
+        // SAFETY: `receiver` is only accessed under `event_lock`, which is held here.
+        unsafe {
+            if *self.receiver.get() == file.as_raw() {
+                *self.receiver.get() = core::ptr::null_mut();
+            }
+        }
+    }
+
+    /// Whether a receiver is currently connected. Runs under `dev`'s `event_lock`.
+    pub fn is_connected<D: drm::Driver, C: DeviceContext>(&self, dev: &Device<D, C>) -> bool {
+        let irq = interrupt::local_interrupt_disable();
+        let _guard = dev.event_lock().lock_with(&irq);
+        // SAFETY: `receiver` is only accessed under `event_lock`, which is held here.
+        !unsafe { *self.receiver.get() }.is_null()
+    }
+
+    /// Deliver `payload` to the connected receiver.
+    ///
+    /// Allocates the backing event, fills its `drm_event` header, and hands it to the DRM core
+    /// under `event_lock`. If no receiver is connected the event is silently dropped and `Ok(())`
+    /// is returned (mirroring the C drivers, which cannot deliver to a closed client). If the
+    /// client has no space left in its event queue this returns `Err(ENOMEM)` and the event is
+    /// dropped; the caller may retry later.
+    pub fn send<D: drm::Driver, C: DeviceContext, T: EventPayload>(
+        &self,
+        dev: &Device<D, C>,
+        payload: T,
+    ) -> Result {
+        let mut storage = KBox::new(
+            EventStorage {
+                pending: bindings::drm_pending_event::default(),
+                event: payload,
+            },
+            GFP_KERNEL,
+        )?;
+
+        // Fill the `drm_event` header that begins `event`.
+        // SAFETY: `EventPayload` guarantees `T` begins with a `drm_event`.
+        let ev_ptr: *mut bindings::drm_event = (&raw mut storage.event).cast();
+        // SAFETY: `ev_ptr` points to the live `drm_event` header inside `storage`.
+        unsafe {
+            (*ev_ptr).type_ = T::TYPE;
+            (*ev_ptr).length = mem::size_of::<T>() as u32;
+        }
+        storage.pending.event = ev_ptr;
+        let pending: *mut bindings::drm_pending_event = &raw mut storage.pending;
+
+        let irq = interrupt::local_interrupt_disable();
+        let guard = dev.event_lock().lock_with(&irq);
+
+        // SAFETY: `receiver` is only accessed under `event_lock`, which is held here.
+        let receiver = unsafe { *self.receiver.get() };
+        if receiver.is_null() {
+            drop(guard);
+            // `storage` is dropped here, freeing the allocation.
+            return Ok(());
+        }
+
+        let dev_raw = dev.as_raw();
+        // SAFETY: `dev_raw` and `receiver` are valid; `pending`/`ev_ptr` point into a live
+        // allocation; `event_lock` is held as required by the C API.
+        let ret = unsafe {
+            bindings::drm_event_reserve_init_locked(dev_raw, receiver, pending, ev_ptr)
+        };
+        if ret != 0 {
+            drop(guard);
+            // Reservation failed, so `pending` was never queued: `storage` frees the allocation.
+            return to_result(ret);
+        }
+
+        // SAFETY: The event is reserved against `receiver`; hand ownership of the allocation to the
+        // DRM core, which frees it (via `kfree` of the `drm_pending_event` at offset 0) once the
+        // event is delivered or the file is closed. `event_lock` is held as required.
+        unsafe { bindings::drm_send_event_locked(dev_raw, pending) };
+        let _ = KBox::into_raw(storage);
+        drop(guard);
+        Ok(())
+    }
+}
+
+impl Default for EventChannel {
+    fn default() -> Self {
+        Self::new()
+    }
+}
diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs
index 7207466094e7..874a2ebc7887 100644
--- a/rust/kernel/drm/kms.rs
+++ b/rust/kernel/drm/kms.rs
@@ -12,7 +12,7 @@
     },
     error::to_result,
     prelude::*,
-    sync::{aref::{ARef, AlwaysRefCounted}, Mutex, MutexGuard, SpinLockIrq},
+    sync::{aref::{ARef, AlwaysRefCounted}, Mutex, MutexGuard},
 };
 use bindings;
 use core::{
@@ -377,6 +377,9 @@ pub(crate) fn mode_config_mutex(&self) -> &Mutex<()> {
         unsafe { Mutex::from_raw(addr_of_mut!((*self.as_raw()).mode_config.mutex)) }
     }
 
+    // NOTE: `event_lock()` now lives in `drm::device` since events are a DRM-core concept, not a
+    // KMS one. See [`crate::drm::Device::event_lock`] and the [`crate::drm::event`] module.
+
     /// Return the number of registered [`Crtc`](crtc::Crtc) objects on this [`Device`].
     #[inline]
     pub fn num_crtcs(&self) -> u32 {
@@ -387,12 +390,6 @@ pub fn num_crtcs(&self) -> u32 {
         unsafe { (*self.as_raw()).mode_config.num_crtc as u32 }
     }
 
-    /// Returns a reference to the `event` spinlock for this [`Device`].
-    #[inline]
-    pub(crate) fn event_lock(&self) -> &SpinLockIrq<()> {
-        // SAFETY: `event_lock` is initialized for as long as `self` is exposed to users.
-        unsafe { SpinLockIrq::from_raw(addr_of_mut!((*self.as_raw()).event_lock)) }
-    }
 }
 
 impl<T: KmsDriver> Device<T> {
diff --git a/rust/kernel/drm/mod.rs b/rust/kernel/drm/mod.rs
index 7503f298db08..77067fc00d2a 100644
--- a/rust/kernel/drm/mod.rs
+++ b/rust/kernel/drm/mod.rs
@@ -4,6 +4,7 @@
 
 pub mod device;
 pub mod driver;
+pub mod event;
 pub mod file;
 pub mod fourcc;
 pub mod gem;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 10/18] rust: drm: allow drivers to declare ioctls from their own uAPI module
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
                     ` (8 preceding siblings ...)
  2026-07-03  3:00   ` [RFC PATCH v2 09/18] rust: drm: add drm_event delivery Mike Lothian
@ 2026-07-03  3:00   ` Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 11/18] rust: platform: add runtime platform device creation Mike Lothian
                     ` (7 subsequent siblings)
  17 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:00 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

declare_drm_ioctls! resolves each ioctl's argument_type and DRM_IOCTL_* number
from the in-tree kernel::uapi crate. An out-of-tree driver with its own private
DRM ioctl range (e.g. DisplayLink's evdi) cannot extend kernel::uapi, so it
cannot use the macro at all.

Add declare_drm_ioctls_ext!, which takes the argument type and the fully-formed
ioctl number directly per entry:

    (handler_name, argument_type, ioctl_number, flags, user_callback)

so a driver can keep its own #[repr(C)] uAPI mirror in its own crate and pass
crate::uapi::Foo / crate::uapi::DRM_IOCTL_FOO. The callback prototype, the
compile-time argument-size assertion against the ioctl number, and the
drm_dev_enter/exit critical section are identical to declare_drm_ioctls!, which
is left untouched (a caller-provided :path module cannot be glued to ::* / ::Ty
in a macro transcriber, so the two macros do not share an implementation).

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/kernel/drm/ioctl.rs | 86 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 86 insertions(+)

diff --git a/rust/kernel/drm/ioctl.rs b/rust/kernel/drm/ioctl.rs
index 181918303f2f..78169798bbbc 100644
--- a/rust/kernel/drm/ioctl.rs
+++ b/rust/kernel/drm/ioctl.rs
@@ -176,3 +176,89 @@ macro_rules! declare_drm_ioctls {
         };
     };
 }
+
+/// Declare the DRM ioctls for a driver whose uAPI lives outside `kernel::uapi`.
+///
+/// [`declare_drm_ioctls!`] resolves each `argument_type` and `DRM_IOCTL_*` number from the
+/// in-tree `kernel::uapi` crate, which an out-of-tree driver with its own private ioctl range
+/// cannot extend. This variant takes, per entry, the argument **type** and the fully-formed ioctl
+/// **number** directly, so the driver can keep its own `#[repr(C)]` uAPI mirror inside its own
+/// crate. Each entry is:
+///
+/// `(handler_name, argument_type, ioctl_number, flags, user_callback)`
+///
+/// `handler_name` is any identifier (used to name the generated trampoline and as the debug name);
+/// `argument_type` is the `#[repr(C)]` argument struct (any path, e.g. `crate::uapi::Foo`);
+/// `ioctl_number` is the full command number (e.g. `crate::uapi::DRM_IOCTL_FOO`, typically built
+/// with [`IOWR`]/[`IOR`]/[`IOW`]/[`IO`]). `user_callback` has the exact same prototype as for
+/// [`declare_drm_ioctls!`], and the handler still runs inside a `drm_dev_enter/exit` critical
+/// section (returning `ENODEV` if the device has been unplugged).
+///
+/// The ioctl array is indexed by the DRM core as `number - DRM_COMMAND_BASE`, so entries must be
+/// listed in ascending, contiguous order starting at the driver's command base.
+///
+/// # Examples
+///
+/// ```ignore
+/// kernel::declare_drm_ioctls_ext! {
+///     (EVDI_CONNECT, crate::uapi::DrmEvdiConnect, crate::uapi::DRM_IOCTL_EVDI_CONNECT,
+///         0, evdi_connect_ioctl),
+/// }
+/// ```
+#[macro_export]
+macro_rules! declare_drm_ioctls_ext {
+    ( $(($cmd:ident, $arg_ty:ty, $num:expr, $flags:expr, $func:expr)),* $(,)? ) => {
+        const IOCTLS: &'static [$crate::drm::ioctl::DrmIoctlDescriptor] = {
+            const _:() = {
+                // The argument struct size must match what the ioctl number encodes, so the DRM
+                // core copies exactly `size_of::<argument_type>()` bytes to/from userspace.
+                $(
+                    ::core::assert!(::core::mem::size_of::<$arg_ty>() ==
+                                    $crate::ioctl::_IOC_SIZE($num));
+                )*
+            };
+
+            let ioctls = &[$(
+                $crate::drm::ioctl::internal::drm_ioctl_desc {
+                    cmd: $num as u32,
+                    func: {
+                        #[allow(non_snake_case)]
+                        unsafe extern "C" fn $cmd(
+                                raw_dev: *mut $crate::drm::ioctl::internal::drm_device,
+                                raw_data: *mut ::core::ffi::c_void,
+                                raw_file: *mut $crate::drm::ioctl::internal::drm_file,
+                        ) -> core::ffi::c_int {
+                            // SAFETY: The DRM core keeps the device alive across the callback and
+                            // only dispatches here on a registered device.
+                            let dev = unsafe {
+                                $crate::drm::device::Device::from_raw(raw_dev)
+                            };
+                            let guard = match $crate::drm::device::unbind_guard(dev) {
+                                Some(g) => g,
+                                None => return $crate::error::code::ENODEV.to_errno(),
+                            };
+                            // SAFETY: The ioctl argument has size `_IOC_SIZE($num)`, asserted above
+                            // to match `size_of::<$arg_ty>()`; `drm_ioctl()` guarantees the buffer
+                            // is valid and exclusively owned for the duration of this call.
+                            let data = unsafe { &mut *(raw_data.cast::<$arg_ty>()) };
+                            // SAFETY: This is just the DRM file structure.
+                            let file = unsafe { $crate::drm::File::from_raw(raw_file) };
+
+                            match $func(dev, &*guard, guard.registration_data(), data, file) {
+                                Err(e) => e.to_errno(),
+                                Ok(i) => i.try_into()
+                                            .unwrap_or($crate::error::code::ERANGE.to_errno()),
+                            }
+                        }
+                        Some($cmd)
+                    },
+                    flags: $flags,
+                    name: $crate::str::as_char_ptr_in_const_context(
+                        $crate::c_str!(::core::stringify!($cmd)),
+                    ),
+                }
+            ),*];
+            ioctls
+        };
+    };
+}
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 11/18] rust: platform: add runtime platform device creation
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
                     ` (9 preceding siblings ...)
  2026-07-03  3:00   ` [RFC PATCH v2 10/18] rust: drm: allow drivers to declare ioctls from their own uAPI module Mike Lothian
@ 2026-07-03  3:00   ` Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 12/18] rust: drm: framebuffer: add geometry accessors, refcounting and a byte-slice vmap Mike Lothian
                     ` (6 subsequent siblings)
  17 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:00 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

The platform abstraction can register a driver (platform::Driver) but not create
a device for it to bind to. Virtual drivers that spawn their own devices -- for
instance DisplayLink's evdi, which creates one platform device per virtual
display card from a sysfs attribute or at module load -- need the provider side.

Add platform::RegisteredDevice, a thin owner around
platform_device_register_full() whose Drop calls platform_device_unregister()
(which unbinds any bound driver first), plus DEVID_AUTO/DEVID_NONE constants and
a name/id/parent/dma_mask constructor. The non-generic methods are #[inline] so
out-of-tree modules can use them without an exported symbol.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/kernel/platform.rs | 77 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 77 insertions(+)

diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index 9b362e0495d3..4c2ded2e264f 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -571,3 +571,80 @@ unsafe impl Sync for Device {}
 // SAFETY: Same as `Device<Normal>` -- the underlying `struct platform_device` is the same;
 // `Bound` is a zero-sized type-state marker that does not affect thread safety.
 unsafe impl Sync for Device<device::Bound> {}
+
+/// Auto-assign a platform device instance id (`PLATFORM_DEVID_AUTO`).
+pub const DEVID_AUTO: i32 = bindings::PLATFORM_DEVID_AUTO;
+/// Create a platform device with no instance id (`PLATFORM_DEVID_NONE`).
+pub const DEVID_NONE: i32 = bindings::PLATFORM_DEVID_NONE;
+
+/// An owned platform device created and registered at runtime.
+///
+/// This is the *provider* side of the platform bus: whereas [`Driver`] binds to devices the bus
+/// hands it, [`RegisteredDevice`] *creates* a `struct platform_device` and adds it to the bus,
+/// which then binds any matching [`Driver`] (calling its `probe`). Virtual drivers such as
+/// DisplayLink's `evdi` use this to spawn their DRM card devices on demand (from a sysfs `add`
+/// attribute or at module init).
+///
+/// [`RegisteredDevice::new`] wraps `platform_device_register_full()`; dropping the handle calls
+/// `platform_device_unregister()`, which unbinds the bound driver and releases the device.
+pub struct RegisteredDevice {
+    pdev: NonNull<bindings::platform_device>,
+}
+
+// SAFETY: `platform_device_register_full()`/`platform_device_unregister()` are internally
+// synchronized by the driver core and safe to call from any thread; the handle only unregisters.
+unsafe impl Send for RegisteredDevice {}
+// SAFETY: See `Send`; `&RegisteredDevice` exposes only the thread-safe `Device<Normal>`.
+unsafe impl Sync for RegisteredDevice {}
+
+impl RegisteredDevice {
+    /// Create and register a new platform device.
+    ///
+    /// `name` is the platform device name, which the platform bus also uses to match a [`Driver`].
+    /// `id` is the instance id: [`DEVID_AUTO`] to auto-assign a unique one, [`DEVID_NONE`] for an
+    /// unnumbered device, or a fixed non-negative value. If `parent` is given, the new device is
+    /// created as its child. `dma_mask` requests a coherent DMA mask of that many bits (pass `0`
+    /// to leave the platform default).
+    #[inline]
+    pub fn new(
+        name: &CStr,
+        id: i32,
+        parent: Option<&device::Device>,
+        dma_mask: u64,
+    ) -> Result<Self> {
+        let info = bindings::platform_device_info {
+            parent: parent.map_or(core::ptr::null_mut(), |p| p.as_raw()),
+            name: name.as_char_ptr(),
+            id,
+            dma_mask,
+            ..Default::default()
+        };
+
+        // SAFETY: `info` is fully initialized (all remaining fields zeroed) and only borrowed for
+        // the duration of the call, which copies out the fields it needs.
+        let pdev = unsafe { bindings::platform_device_register_full(&info) };
+        let pdev = crate::error::from_err_ptr(pdev)?;
+
+        Ok(Self {
+            // A successful `platform_device_register_full()` returns a valid, non-null pointer.
+            pdev: NonNull::new(pdev).ok_or(ENOMEM)?,
+        })
+    }
+
+    /// Return a reference to the created platform [`Device`].
+    #[inline]
+    pub fn device(&self) -> &Device {
+        // SAFETY: `self.pdev` points to a valid, registered `struct platform_device` owned by
+        // `self`; `Device` is a transparent wrapper over `struct platform_device`.
+        unsafe { &*self.pdev.as_ptr().cast::<Device>() }
+    }
+}
+
+impl Drop for RegisteredDevice {
+    #[inline]
+    fn drop(&mut self) {
+        // SAFETY: `self.pdev` was returned by `platform_device_register_full()` and has not been
+        // unregistered yet; this consumes that registration exactly once.
+        unsafe { bindings::platform_device_unregister(self.pdev.as_ptr()) };
+    }
+}
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 12/18] rust: drm: framebuffer: add geometry accessors, refcounting and a byte-slice vmap
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
                     ` (10 preceding siblings ...)
  2026-07-03  3:00   ` [RFC PATCH v2 11/18] rust: platform: add runtime platform device creation Mike Lothian
@ 2026-07-03  3:00   ` Mike Lothian
  2026-07-03  3:01   ` [RFC PATCH v2 13/18] rust: i2c: add adapter-provider (bus controller) registration Mike Lothian
                     ` (5 subsequent siblings)
  17 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:00 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

A driver that reads back a framebuffer's pixels (e.g. DisplayLink's evdi, whose
scanout is pulled by userspace via a GRABPIX ioctl rather than pushed to
hardware) needs three things the Framebuffer binding did not provide:

  - width()/height()/pitch() geometry accessors;

  - reference counting, so the framebuffer flipped in during an atomic commit can
    be held (ARef<Framebuffer>) and mapped later from an ioctl -- add
    AlwaysRefCounted backed by new drm_framebuffer_get()/put() helpers;

  - a way to read the mapping without unsafe in the driver -- add
    FramebufferVmap::as_bytes(), returning the plane-0 mapping as a &[u8] of
    height * pitch(0) bytes, valid for the guard's lifetime.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/helpers/drm/drm.c             |  1 +
 rust/helpers/drm/framebuffer.c     | 13 +++++++
 rust/kernel/drm/kms/framebuffer.rs | 54 ++++++++++++++++++++++++++++++
 3 files changed, 68 insertions(+)
 create mode 100644 rust/helpers/drm/framebuffer.c

diff --git a/rust/helpers/drm/drm.c b/rust/helpers/drm/drm.c
index 5d700d75c0c9..24c531841ba1 100644
--- a/rust/helpers/drm/drm.c
+++ b/rust/helpers/drm/drm.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 
 #include "atomic.c"
+#include "framebuffer.c"
 #include "gem.c"
 #include "vma_manager.c"
 #include "vblank.c"
diff --git a/rust/helpers/drm/framebuffer.c b/rust/helpers/drm/framebuffer.c
new file mode 100644
index 000000000000..280538ffb82c
--- /dev/null
+++ b/rust/helpers/drm/framebuffer.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <drm/drm_framebuffer.h>
+
+void rust_helper_drm_framebuffer_get(struct drm_framebuffer *fb)
+{
+	drm_framebuffer_get(fb);
+}
+
+void rust_helper_drm_framebuffer_put(struct drm_framebuffer *fb)
+{
+	drm_framebuffer_put(fb);
+}
diff --git a/rust/kernel/drm/kms/framebuffer.rs b/rust/kernel/drm/kms/framebuffer.rs
index 1ec6779ba7de..c7089b12d0d1 100644
--- a/rust/kernel/drm/kms/framebuffer.rs
+++ b/rust/kernel/drm/kms/framebuffer.rs
@@ -9,6 +9,7 @@
     drm::device::Device,
     error::{code::EINVAL, to_result},
     prelude::*,
+    sync::aref::AlwaysRefCounted,
     types::*,
 };
 use bindings;
@@ -46,6 +47,23 @@ fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
 }
 
 // SAFETY: References to framebuffers are safe to be accessed from any thread
+// SAFETY: `struct drm_framebuffer` is refcounted via `drm_framebuffer_get()`/`drm_framebuffer_put()`,
+// which are the correct increment/decrement operations for it.
+unsafe impl<T: KmsDriver> AlwaysRefCounted for Framebuffer<T> {
+    #[inline]
+    fn inc_ref(&self) {
+        // SAFETY: The existence of a shared reference guarantees the refcount is non-zero.
+        unsafe { bindings::drm_framebuffer_get(self.as_raw()) };
+    }
+
+    #[inline]
+    unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
+        // SAFETY: The safety requirements guarantee the refcount is non-zero; `Framebuffer<T>` has
+        // the same layout as `struct drm_framebuffer`.
+        unsafe { bindings::drm_framebuffer_put(obj.as_ptr().cast()) };
+    }
+}
+
 unsafe impl<T: KmsDriver> Send for Framebuffer<T> {}
 // SAFETY: References to framebuffers are safe to be accessed from any thread
 unsafe impl<T: KmsDriver> Sync for Framebuffer<T> {}
@@ -79,6 +97,30 @@ pub(crate) fn as_raw(&self) -> *mut bindings::drm_framebuffer {
         self.0.get()
     }
 
+    /// The framebuffer's width in pixels.
+    #[inline]
+    pub fn width(&self) -> u32 {
+        // SAFETY: `as_raw()` is a valid framebuffer; `width` is set at creation and immutable.
+        unsafe { (*self.as_raw()).width }
+    }
+
+    /// The framebuffer's height in pixels.
+    #[inline]
+    pub fn height(&self) -> u32 {
+        // SAFETY: `as_raw()` is a valid framebuffer; `height` is set at creation and immutable.
+        unsafe { (*self.as_raw()).height }
+    }
+
+    /// The byte stride (pitch) of colour plane `index`.
+    ///
+    /// A `drm_framebuffer` has at most four colour planes; `index` is masked into `0..=3`.
+    #[inline]
+    pub fn pitch(&self, index: usize) -> u32 {
+        // SAFETY: `pitches` has four elements; `index & 3` keeps the access in bounds. The pitches
+        // are set at creation and immutable.
+        unsafe { (*self.as_raw()).pitches[index & 3] }
+    }
+
     /// Map this framebuffer's plane-0 backing pages into the kernel address space for CPU
     /// access, for the duration of the returned guard.
     ///
@@ -123,6 +165,18 @@ pub fn as_ptr(&self) -> *const u8 {
         // SAFETY: set non-null in `Framebuffer::vmap`; `vaddr` is the active union member.
         (unsafe { self.map[0].__bindgen_anon_1.vaddr }) as *const u8
     }
+
+    /// Plane 0's mapped pixels as a byte slice.
+    ///
+    /// The slice covers `height * pitch(0)` bytes — the full extent of colour plane 0 — and is
+    /// valid for the guard's lifetime. This lets callers read the scanout buffer without `unsafe`.
+    #[inline]
+    pub fn as_bytes(&self) -> &[u8] {
+        let len = self.fb.height() as usize * self.fb.pitch(0) as usize;
+        // SAFETY: `drm_gem_fb_vmap` mapped colour plane 0 contiguously at `as_ptr()` for at least
+        // `height * pitch(0)` bytes, and the mapping is live for `self`'s lifetime.
+        unsafe { core::slice::from_raw_parts(self.as_ptr(), len) }
+    }
 }
 
 impl<'a, T: KmsDriver> Drop for FramebufferVmap<'a, T> {
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 13/18] rust: i2c: add adapter-provider (bus controller) registration
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
                     ` (11 preceding siblings ...)
  2026-07-03  3:00   ` [RFC PATCH v2 12/18] rust: drm: framebuffer: add geometry accessors, refcounting and a byte-slice vmap Mike Lothian
@ 2026-07-03  3:01   ` Mike Lothian
  2026-07-03  3:01   ` [RFC PATCH v2 14/18] rust: add sysfs device attribute groups Mike Lothian
                     ` (4 subsequent siblings)
  17 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:01 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

The I2C abstraction can register an I2C *client* driver (i2c::Driver), but not
provide a bus. A driver that presents a virtual I2C bus and services the
transfers itself -- e.g. DisplayLink's evdi, which exposes a DDC/CI channel so
userspace monitor-control tools can reach the display -- needs the controller
side.

Add i2c::BusController (a trait with master_xfer/functionality over a
driver-supplied Context) and i2c::BusAdapter, which fills a struct i2c_adapter
whose i2c_algorithm dispatches to the trait via trampolines, calls
i2c_add_adapter() on creation and i2c_del_adapter() on drop. i2c::Msg is a
#[repr(transparent)] view over struct i2c_msg exposing addr/flags/is_read and
the payload as a (mutable) byte slice, so a transfer routine needs no unsafe.
FUNC_I2C is exported for the common functionality() return.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/kernel/i2c.rs | 192 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 192 insertions(+)

diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs
index 624b971ca8b0..5cde59a3ce46 100644
--- a/rust/kernel/i2c.rs
+++ b/rust/kernel/i2c.rs
@@ -601,3 +601,195 @@ unsafe impl Send for Registration {}
 // SAFETY: `Registration` offers no interior mutability (no mutation through &self
 // and no mutable access is exposed)
 unsafe impl Sync for Registration {}
+
+// ===========================================================================
+// I2C adapter (bus controller / provider) side
+// ===========================================================================
+
+/// A single I2C message presented to a [`BusController`]'s transfer routine.
+///
+/// Wraps a `struct i2c_msg`; `#[repr(transparent)]` so a C `i2c_msg` array can be viewed directly
+/// as a `[Msg]`.
+#[repr(transparent)]
+pub struct Msg(Opaque<bindings::i2c_msg>);
+
+impl Msg {
+    /// The 7-bit (or 10-bit) target address.
+    #[inline]
+    pub fn addr(&self) -> u16 {
+        // SAFETY: `self.0` is a valid `i2c_msg` for the callback's duration.
+        unsafe { (*self.0.get()).addr }
+    }
+
+    /// The message flags (`I2C_M_*`).
+    #[inline]
+    pub fn flags(&self) -> u16 {
+        // SAFETY: as above.
+        unsafe { (*self.0.get()).flags }
+    }
+
+    /// Whether this is a read (`I2C_M_RD`) rather than a write.
+    #[inline]
+    pub fn is_read(&self) -> bool {
+        self.flags() & (bindings::I2C_M_RD as u16) != 0
+    }
+
+    /// The message length in bytes.
+    #[inline]
+    pub fn len(&self) -> usize {
+        // SAFETY: as above.
+        unsafe { (*self.0.get()).len as usize }
+    }
+
+    /// Whether the message is empty.
+    #[inline]
+    pub fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+
+    /// The message payload of a write, as a read-only slice.
+    #[inline]
+    pub fn buf(&self) -> &[u8] {
+        // SAFETY: `buf` points to `len` bytes valid for the callback's duration.
+        unsafe { core::slice::from_raw_parts((*self.0.get()).buf, self.len()) }
+    }
+
+    /// The message buffer of a read, as a mutable slice to fill with the reply.
+    #[inline]
+    pub fn buf_mut(&mut self) -> &mut [u8] {
+        // SAFETY: `buf` points to `len` writable bytes valid for the callback's duration.
+        unsafe { core::slice::from_raw_parts_mut((*self.0.get()).buf, self.len()) }
+    }
+}
+
+/// `I2C_FUNC_I2C`: the adapter supports plain I2C (not just SMBus) transfers.
+pub const FUNC_I2C: u32 = bindings::I2C_FUNC_I2C;
+
+/// A Rust-implemented I2C bus adapter (the controller/provider side).
+///
+/// This is the counterpart to the [`Driver`] trait above: rather than binding to an I2C client on
+/// an existing bus, an implementer *is* a bus and services transfers. Virtual buses -- e.g. the
+/// DDC/CI channel DisplayLink's evdi exposes so userspace monitor-control tools can reach the
+/// display -- use this.
+pub trait BusController: 'static {
+    /// Per-adapter driver context, made available to the transfer callbacks.
+    type Context: Send + Sync;
+
+    /// Transfer `msgs` on the bus, returning the number of messages successfully transferred
+    /// (as the C `master_xfer` does), or an error.
+    fn master_xfer(ctx: &Self::Context, msgs: &mut [Msg]) -> Result<usize>;
+
+    /// Report the bus functionality bitmask (`I2C_FUNC_*`).
+    fn functionality(ctx: &Self::Context) -> u32;
+}
+
+/// An owned, registered I2C adapter driven by a [`BusController`].
+///
+/// [`BusAdapter::new`] fills a `struct i2c_adapter` (pointing its algorithm at trampolines that
+/// dispatch to `T`) and calls `i2c_add_adapter()`; dropping it calls `i2c_del_adapter()`.
+#[pin_data(PinnedDrop)]
+pub struct BusAdapter<T: BusController> {
+    #[pin]
+    adapter: Opaque<bindings::i2c_adapter>,
+    #[pin]
+    algo: Opaque<bindings::i2c_algorithm>,
+    ctx: T::Context,
+    registered: core::sync::atomic::AtomicBool,
+}
+
+// SAFETY: the adapter is internally synchronized by the I2C core; `ctx` is `Send + Sync`.
+unsafe impl<T: BusController> Send for BusAdapter<T> {}
+// SAFETY: see `Send`.
+unsafe impl<T: BusController> Sync for BusAdapter<T> {}
+
+impl<T: BusController> BusAdapter<T> {
+    /// # Safety
+    /// `adap` is a valid adapter whose `algo_data` points to a live `T::Context`.
+    unsafe extern "C" fn xfer_trampoline(
+        adap: *mut bindings::i2c_adapter,
+        msgs: *mut bindings::i2c_msg,
+        num: crate::ffi::c_int,
+    ) -> crate::ffi::c_int {
+        // SAFETY: by the invariant established in `new`, `algo_data` points to the `T::Context`.
+        let ctx = unsafe { &*((*adap).algo_data as *const T::Context) };
+        // SAFETY: the I2C core passes `num` valid `i2c_msg`s; `Msg` is `#[repr(transparent)]`.
+        let slice = unsafe { core::slice::from_raw_parts_mut(msgs.cast::<Msg>(), num as usize) };
+        match T::master_xfer(ctx, slice) {
+            Ok(n) => n as crate::ffi::c_int,
+            Err(e) => e.to_errno(),
+        }
+    }
+
+    /// # Safety
+    /// `adap` is a valid adapter whose `algo_data` points to a live `T::Context`.
+    unsafe extern "C" fn func_trampoline(adap: *mut bindings::i2c_adapter) -> u32 {
+        // SAFETY: by the invariant established in `new`, `algo_data` points to the `T::Context`.
+        let ctx = unsafe { &*((*adap).algo_data as *const T::Context) };
+        T::functionality(ctx)
+    }
+
+    /// Create and register a new I2C adapter named `name`, parented to `parent`, with driver
+    /// context `ctx`.
+    pub fn new(
+        name: &CStr,
+        parent: &device::Device,
+        ctx: T::Context,
+    ) -> Result<Pin<KBox<Self>>> {
+        let this = KBox::try_pin_init(
+            try_pin_init!(Self {
+                adapter <- Opaque::ffi_init(|slot: *mut bindings::i2c_adapter| {
+                    // SAFETY: `slot` is valid, uninitialized storage; a zeroed adapter is a valid
+                    // starting point (self-referential fields are set below, after pinning).
+                    unsafe { *slot = bindings::i2c_adapter::default() };
+                }),
+                algo <- Opaque::ffi_init(|slot: *mut bindings::i2c_algorithm| {
+                    // SAFETY: `slot` is valid, uninitialized storage.
+                    unsafe {
+                        *slot = bindings::i2c_algorithm::default();
+                        (*slot).__bindgen_anon_1.master_xfer = Some(Self::xfer_trampoline);
+                        (*slot).functionality = Some(Self::func_trampoline);
+                    }
+                }),
+                ctx: ctx,
+                registered: core::sync::atomic::AtomicBool::new(false),
+            }),
+            GFP_KERNEL,
+        )?;
+
+        // `this` now has a stable address; wire the self-referential adapter fields and register.
+        let adap = this.adapter.get();
+        // Copy the name without its NUL into the fixed 48-byte array (capped at 47 so the
+        // already-zeroed array stays NUL-terminated even if the name is over-long).
+        let name_bytes = name.to_bytes();
+        let n = core::cmp::min(name_bytes.len(), 47);
+        // SAFETY: `adap`/`algo`/`ctx` live for `this`'s (pinned) lifetime; `name_bytes` is valid
+        // for `n <= 47` bytes; `parent` is a valid device.
+        unsafe {
+            (*adap).algo = this.algo.get();
+            (*adap).algo_data = (&this.ctx as *const T::Context).cast_mut().cast();
+            core::ptr::copy_nonoverlapping(
+                name_bytes.as_ptr(),
+                (*adap).name.as_mut_ptr().cast::<u8>(),
+                n,
+            );
+            (*adap).dev.parent = parent.as_raw();
+        }
+
+        // SAFETY: `adap` is a fully-initialized adapter.
+        to_result(unsafe { bindings::i2c_add_adapter(adap) })?;
+        this.registered
+            .store(true, core::sync::atomic::Ordering::Release);
+        Ok(this)
+    }
+}
+
+#[pinned_drop]
+impl<T: BusController> PinnedDrop for BusAdapter<T> {
+    fn drop(self: Pin<&mut Self>) {
+        if self.registered.load(core::sync::atomic::Ordering::Acquire) {
+            // SAFETY: the adapter was successfully registered with `i2c_add_adapter` and has not
+            // been deleted yet.
+            unsafe { bindings::i2c_del_adapter(self.adapter.get()) };
+        }
+    }
+}
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 14/18] rust: add sysfs device attribute groups
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
                     ` (12 preceding siblings ...)
  2026-07-03  3:01   ` [RFC PATCH v2 13/18] rust: i2c: add adapter-provider (bus controller) registration Mike Lothian
@ 2026-07-03  3:01   ` Mike Lothian
  2026-07-03  3:01   ` [RFC PATCH v2 15/18] rust: drm: support hardware cursor planes with sleepable event delivery Mike Lothian
                     ` (3 subsequent siblings)
  17 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:01 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

A driver that exposes control files under sysfs -- for instance DisplayLink's
evdi, whose /sys/devices/evdi/{count,add,remove_all} files let the daemon create
and destroy virtual display cards -- had no safe way to do so from Rust.

Add a sysfs module: a driver implements DeviceAttributes (an ATTRS list plus
show()/store(), dispatched by attribute name) for the type it stores as a
device's driver data, and AttributeGroup::register_root() creates a standalone
root device (root_device_register(), under /sys/devices/<name>) hosting the
files. Reads and writes trampoline to show()/store() with the driver data
recovered from the device; the file mode (Attr::ro/wo/rw) gates access. The group
removes the files, reclaims the driver data and unregisters the device on drop.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/kernel/lib.rs   |   1 +
 rust/kernel/sysfs.rs | 245 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 246 insertions(+)
 create mode 100644 rust/kernel/sysfs.rs

diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 8baa13079071..13729ec06333 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -128,6 +128,7 @@
 pub mod std_vendor;
 pub mod str;
 pub mod sync;
+pub mod sysfs;
 pub mod task;
 pub mod time;
 pub mod tracepoint;
diff --git a/rust/kernel/sysfs.rs b/rust/kernel/sysfs.rs
new file mode 100644
index 000000000000..f6df17c5013d
--- /dev/null
+++ b/rust/kernel/sysfs.rs
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Sysfs device attributes.
+//!
+//! A driver can expose a set of named sysfs files on a device by implementing [`DeviceAttributes`]
+//! for the type it stores as that device's driver data, and registering an [`AttributeGroup`].
+//! Reads and writes are dispatched by attribute name to the type's [`show`](DeviceAttributes::show)
+//! and [`store`](DeviceAttributes::store) methods; the file mode (see [`Attr`]) controls which are
+//! reachable from userspace.
+//!
+//! [`AttributeGroup::register_root`] additionally creates a standalone "root" device
+//! (`root_device_register()`, appearing under `/sys/devices/<name>`) to host the group -- the shape
+//! DisplayLink's evdi uses for its `add`/`remove`/`count` control files.
+//!
+//! C headers: [`include/linux/sysfs.h`](srctree/include/linux/sysfs.h),
+//! [`include/linux/device.h`](srctree/include/linux/device.h)
+
+use crate::{
+    bindings,
+    error::{from_err_ptr, to_result},
+    page::PAGE_SIZE,
+    prelude::*,
+    types::ForeignOwnable,
+    ThisModule,
+};
+
+/// Definition of one sysfs attribute file: its name and permission mode (e.g. `0o444` read-only,
+/// `0o200` write-only, `0o644` read-write).
+pub struct Attr {
+    /// The file name.
+    pub name: &'static CStr,
+    /// The permission bits (`umode_t`).
+    pub mode: u16,
+}
+
+impl Attr {
+    /// A read-only (`0o444`) attribute.
+    pub const fn ro(name: &'static CStr) -> Self {
+        Self { name, mode: 0o444 }
+    }
+    /// A write-only (`0o200`) attribute.
+    pub const fn wo(name: &'static CStr) -> Self {
+        Self { name, mode: 0o200 }
+    }
+    /// A read-write (`0o644`) attribute.
+    pub const fn rw(name: &'static CStr) -> Self {
+        Self { name, mode: 0o644 }
+    }
+}
+
+/// The show/store behaviour of a device's sysfs attribute group.
+///
+/// Implemented by the type a device stores as its driver data. A shared reference to it is handed
+/// to [`show`](Self::show)/[`store`](Self::store), dispatched by the attribute `name`.
+pub trait DeviceAttributes: Send + Sync + 'static {
+    /// The attributes exposed by this group.
+    const ATTRS: &'static [Attr];
+
+    /// Handle a read of attribute `name`, writing up to one page into `buf` and returning the
+    /// number of bytes written. Only called for readable (`ro`/`rw`) attributes.
+    fn show(&self, name: &CStr, buf: &mut [u8]) -> Result<usize> {
+        let _ = (name, buf);
+        Err(EINVAL)
+    }
+
+    /// Handle a write of `buf` to attribute `name`. Only called for writable (`wo`/`rw`)
+    /// attributes.
+    fn store(&self, name: &CStr, buf: &[u8]) -> Result {
+        let _ = (name, buf);
+        Err(EINVAL)
+    }
+}
+
+/// A registered sysfs attribute group hosted on a `root_device_register()` device.
+///
+/// Dropping it removes the files, reclaims the driver data, and unregisters the root device.
+pub struct AttributeGroup<T: DeviceAttributes> {
+    root: *mut bindings::device,
+    /// Backing storage for the `device_attribute`s handed to `device_create_file()`; must stay
+    /// alive (and not move) for as long as the files exist, so it lives in this heap allocation.
+    attrs: KVec<bindings::device_attribute>,
+    _p: core::marker::PhantomData<T>,
+}
+
+// SAFETY: the root device and its attributes are internally synchronized by the driver core; `T`
+// is `Send + Sync`.
+unsafe impl<T: DeviceAttributes> Send for AttributeGroup<T> {}
+// SAFETY: see `Send`.
+unsafe impl<T: DeviceAttributes> Sync for AttributeGroup<T> {}
+
+impl<T: DeviceAttributes> AttributeGroup<T> {
+    /// # Safety
+    /// `dev`'s driver data is a live `T` set via [`device::Device::set_drvdata`].
+    unsafe extern "C" fn show_trampoline(
+        dev: *mut bindings::device,
+        attr: *mut bindings::device_attribute,
+        buf: *mut crate::ffi::c_char,
+    ) -> isize {
+        // SAFETY: `dev` is a valid device with `T` driver data (invariant of `register_root`).
+        let ctx = unsafe { borrow_ctx::<T>(dev) };
+        // SAFETY: the attribute name is a valid C string for the callback's duration.
+        let name = unsafe { as_cstr((*attr).attr.name) };
+        // SAFETY: sysfs guarantees `buf` is a writable page.
+        let slice = unsafe { core::slice::from_raw_parts_mut(buf.cast::<u8>(), PAGE_SIZE) };
+        match T::show(ctx, name, slice) {
+            Ok(n) => core::cmp::min(n, PAGE_SIZE) as isize,
+            Err(e) => e.to_errno() as isize,
+        }
+    }
+
+    /// # Safety
+    /// `dev`'s driver data is a live `T` set via [`device::Device::set_drvdata`].
+    unsafe extern "C" fn store_trampoline(
+        dev: *mut bindings::device,
+        attr: *mut bindings::device_attribute,
+        buf: *const crate::ffi::c_char,
+        count: usize,
+    ) -> isize {
+        // SAFETY: as in `show_trampoline`.
+        let ctx = unsafe { borrow_ctx::<T>(dev) };
+        // SAFETY: the attribute name is a valid C string for the callback's duration.
+        let name = unsafe { as_cstr((*attr).attr.name) };
+        // SAFETY: sysfs guarantees `buf` holds `count` readable bytes.
+        let slice = unsafe { core::slice::from_raw_parts(buf.cast::<u8>(), count) };
+        match T::store(ctx, name, slice) {
+            Ok(()) => count as isize,
+            Err(e) => e.to_errno() as isize,
+        }
+    }
+
+    fn make_attr(a: &Attr) -> bindings::device_attribute {
+        let mut da: bindings::device_attribute = bindings::device_attribute::default();
+        da.attr.name = a.name.as_char_ptr();
+        da.attr.mode = a.mode;
+        da.__bindgen_anon_1.show = Some(Self::show_trampoline);
+        da.__bindgen_anon_2.store = Some(Self::store_trampoline);
+        da
+    }
+
+    /// Create a root device named `name` and expose `T`'s attributes on it.
+    ///
+    /// `T` becomes the device's driver data; the attribute callbacks recover it by name-dispatch.
+    pub fn register_root(
+        name: &CStr,
+        module: &'static ThisModule,
+        ctx: impl PinInit<T, Error>,
+    ) -> Result<KBox<Self>> {
+        // Build the attribute array up front (no external resources yet, so a failure here needs
+        // no cleanup).
+        let mut attrs: KVec<bindings::device_attribute> =
+            KVec::with_capacity(T::ATTRS.len(), GFP_KERNEL)?;
+        for a in T::ATTRS {
+            attrs.push(Self::make_attr(a), GFP_KERNEL)?;
+        }
+        // Box the context; its foreign pointer becomes the device's driver data.
+        let ctx_ptr = KBox::pin_init(ctx, GFP_KERNEL)?.into_foreign();
+
+        // SAFETY: `name` is a valid C string; `module` is this module.
+        let root = match from_err_ptr(unsafe {
+            bindings::__root_device_register(name.as_char_ptr(), module.as_ptr())
+        }) {
+            Ok(r) => r,
+            Err(e) => {
+                // Reclaim the context box we have not attached anywhere yet.
+                // SAFETY: `ctx_ptr` came from `into_foreign()` above and was not consumed.
+                drop(unsafe { <Pin<KBox<T>> as ForeignOwnable>::from_foreign(ctx_ptr) });
+                return Err(e);
+            }
+        };
+        // SAFETY: `root` is a freshly-registered, valid device; `ctx_ptr` is its driver data now.
+        unsafe { bindings::dev_set_drvdata(root, ctx_ptr) };
+
+        // Register each file against its stable slot in `attrs`.
+        for da in attrs.iter() {
+            if let Err(e) = to_result(unsafe { bindings::device_create_file(root, da) }) {
+                // `root_device_unregister` (device_del) removes any files created so far;
+                // reclaim the context box first.
+                Self::teardown(root);
+                return Err(e);
+            }
+        }
+
+        match KBox::new(
+            Self {
+                root,
+                attrs,
+                _p: core::marker::PhantomData,
+            },
+            GFP_KERNEL,
+        ) {
+            Ok(group) => Ok(group),
+            Err(e) => {
+                Self::teardown(root);
+                Err(e.into())
+            }
+        }
+    }
+
+    /// Reclaim the driver-data box and unregister the root device (which removes its files).
+    fn teardown(root: *mut bindings::device) {
+        // SAFETY: `root` is a valid registered device whose driver data is a `Pin<KBox<T>>` (or
+        // NULL); `dev_get_drvdata` returns the pointer stashed in `register_root`.
+        let ptr = unsafe { bindings::dev_get_drvdata(root) };
+        if !ptr.is_null() {
+            // SAFETY: `ptr` came from `Pin::<KBox<T>>::into_foreign`.
+            drop(unsafe { <Pin<KBox<T>> as ForeignOwnable>::from_foreign(ptr) });
+        }
+        // SAFETY: `root` was created by `__root_device_register` and not yet unregistered.
+        unsafe { bindings::root_device_unregister(root) };
+    }
+}
+
+impl<T: DeviceAttributes> Drop for AttributeGroup<T> {
+    fn drop(&mut self) {
+        for da in self.attrs.iter() {
+            // SAFETY: each `da` was passed to `device_create_file` on `self.root` and not removed
+            // since.
+            unsafe { bindings::device_remove_file(self.root, da) };
+        }
+        // Reclaim the driver-data box and unregister the root device.
+        Self::teardown(self.root);
+    }
+}
+
+/// Borrow the `T` driver data of the device `ptr`.
+///
+/// # Safety
+/// `ptr` is a valid `struct device` whose driver data is a `Pin<KBox<T>>` stashed by
+/// [`AttributeGroup::register_root`], valid for the returned reference's lifetime.
+unsafe fn borrow_ctx<'a, T>(ptr: *mut bindings::device) -> &'a T {
+    // SAFETY: `ptr` is a valid device by the contract.
+    let data = unsafe { bindings::dev_get_drvdata(ptr) };
+    // SAFETY: `data` is the pointer `Pin<KBox<T>>::into_foreign` returned, i.e. a valid `*mut T`
+    // live for the borrow.
+    unsafe { &*(data as *const T) }
+}
+
+/// # Safety
+/// `ptr` is a valid, NUL-terminated C string for the returned reference's lifetime.
+#[inline]
+unsafe fn as_cstr<'a>(ptr: *const crate::ffi::c_char) -> &'a CStr {
+    // SAFETY: by the contract, `ptr` is a valid NUL-terminated C string. `crate::ffi::c_char` is
+    // `u8` while `core::ffi::CStr::from_ptr` takes `*const i8`, so cast the pointer.
+    unsafe { CStr::from_ptr(ptr.cast()) }
+}
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 15/18] rust: drm: support hardware cursor planes with sleepable event delivery
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
                     ` (13 preceding siblings ...)
  2026-07-03  3:01   ` [RFC PATCH v2 14/18] rust: add sysfs device attribute groups Mike Lothian
@ 2026-07-03  3:01   ` Mike Lothian
  2026-07-03  3:01   ` [RFC PATCH v2 16/18] rust: drm: add CRTC gamma LUT and plane rotation property bindings Mike Lothian
                     ` (2 subsequent siblings)
  17 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:01 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

A driver that forwards a hardware cursor plane's contents to userspace (e.g.
DisplayLink's evdi, whose daemon composites the cursor itself) needs to hand the
client a GEM handle for the cursor bitmap on each update. drm_gem_handle_create()
may sleep, so the connected file must be reachable from sleepable context -- but
drm::event::EventChannel stored the receiver under the device event_lock (a
spinlock).

Rework EventChannel to guard the receiver with a Mutex instead: send() holds it
across the event_lock-protected reserve+queue (so delivery still cannot race a
close), and a new with_receiver() runs a closure with the connected File while
holding the mutex, allowing a sleepable GEM-handle creation without the file
closing underneath it (notify_close() takes the same mutex). new() now takes a
lock class so the class lives in the driver module. connect/disconnect/
notify_close/is_connected no longer need the device.

Add the accessors such a cursor consumer needs:
  - plane state crtc_x()/crtc_y() (cursor position) and hotspot_x()/hotspot_y();
  - Framebuffer::format() (FourCC) and Framebuffer::create_handle() (a GEM handle
    for plane-0 in a given file).

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/kernel/drm/event.rs           | 179 +++++++++++++++--------------
 rust/kernel/drm/kms/framebuffer.rs |  28 +++++
 rust/kernel/drm/kms/plane.rs       |  20 ++++
 3 files changed, 138 insertions(+), 89 deletions(-)

diff --git a/rust/kernel/drm/event.rs b/rust/kernel/drm/event.rs
index 39d65d2a8fb2..dadb38c7df20 100644
--- a/rust/kernel/drm/event.rs
+++ b/rust/kernel/drm/event.rs
@@ -13,8 +13,8 @@
 //! target `struct drm_file` must be valid for that whole critical section. Reading the target file
 //! pointer outside the lock is a classic source of use-after-free/NULL-deref crashes (a driver's
 //! flush worker racing a client disconnect). [`EventChannel`] encodes that rule in its API: the
-//! receiver file is only ever touched while `event_lock` is held, so registration, teardown and
-//! delivery cannot race.
+//! receiver file lives behind a mutex and [`send`](EventChannel::send) holds it across the whole
+//! `event_lock`-protected reserve+queue, so registration, teardown and delivery cannot race.
 //!
 //! C header: [`include/drm/drm_file.h`](srctree/include/drm/drm_file.h)
 
@@ -29,8 +29,9 @@
     error::to_result,
     interrupt,
     prelude::*,
+    sync::{LockClassKey, Mutex},
 };
-use core::{cell::UnsafeCell, mem};
+use core::mem;
 
 /// A driver-defined DRM event payload.
 ///
@@ -63,94 +64,101 @@ struct EventStorage<T: EventPayload> {
 
 /// A per-device channel that delivers driver events to the [`File`] registered as the receiver.
 ///
-/// The receiver pointer is stored under the owning device's `event_lock` — the same lock the DRM
-/// core requires held while queuing events — so [`connect`](Self::connect),
-/// [`disconnect`](Self::disconnect), [`notify_close`](Self::notify_close) and [`send`](Self::send)
-/// are all serialized against each other and against the core's own event handling. This makes the
-/// "send to a file that just disconnected" race unrepresentable.
+/// The receiver `drm_file` is stored behind a [`Mutex`], not the device `event_lock`, so it can be
+/// touched from sleepable context — in particular so a driver can create a GEM handle for the
+/// connected client (`drm_gem_handle_create` may sleep) via [`with_receiver`](Self::with_receiver).
+/// [`send`](Self::send) still acquires the device's `event_lock` (as the DRM core requires) while
+/// holding the mutex, so the receiver pointer is valid for the whole reserve+queue and cannot race
+/// [`disconnect`](Self::disconnect)/[`notify_close`](Self::notify_close). This makes the classic
+/// "deliver to a file that just closed" use-after-free unrepresentable.
 ///
-/// A channel is bound to exactly one [`Device`]: always pass the same device to every method.
+/// A channel is bound to exactly one [`Device`]: always pass the same device to [`send`](Self::send).
 /// Drivers must call [`notify_close`](Self::notify_close) from their `postclose` hook (and/or
 /// [`disconnect`](Self::disconnect) when tearing the logical connection down) so the receiver
 /// pointer never outlives the file it refers to.
+#[pin_data]
 pub struct EventChannel {
-    /// The receiver `drm_file`, or null when no client is connected.
+    /// The receiver `drm_file` address, or `0` when no client is connected.
     ///
-    /// Only ever accessed while the owning device's `event_lock` is held.
-    receiver: UnsafeCell<*mut bindings::drm_file>,
+    /// A `usize` (not a raw pointer) so the `Mutex` is `Send`/`Sync` on its own; the value is only
+    /// ever turned back into a `*mut drm_file` while the mutex is held.
+    #[pin]
+    receiver: Mutex<usize>,
 }
 
-// SAFETY: `receiver` is only accessed while holding the owning device's `event_lock`, which
-// serializes all access across threads.
-unsafe impl Send for EventChannel {}
-// SAFETY: See `Send`.
-unsafe impl Sync for EventChannel {}
-
 impl EventChannel {
     /// Create a new, disconnected channel.
-    pub const fn new() -> Self {
-        Self {
-            receiver: UnsafeCell::new(core::ptr::null_mut()),
-        }
+    ///
+    /// `key` is the lock class for the receiver mutex; pass
+    /// [`static_lock_class!()`](crate::sync::static_lock_class) from the driver so the class lives
+    /// in the driver module.
+    #[inline]
+    pub fn new(key: Pin<&'static LockClassKey>) -> impl PinInit<Self> {
+        pin_init!(Self {
+            receiver <- Mutex::new(0, c"EventChannel::receiver", key),
+        })
     }
 
     /// Register `file` as the receiver for events sent through this channel.
     ///
-    /// Any previously registered file is replaced. Runs under `dev`'s `event_lock`.
-    pub fn connect<D: drm::Driver, C: DeviceContext, F: DriverFile>(
-        &self,
-        dev: &Device<D, C>,
-        file: &File<F>,
-    ) {
-        let irq = interrupt::local_interrupt_disable();
-        let _guard = dev.event_lock().lock_with(&irq);
-        // SAFETY: `receiver` is only accessed under `event_lock`, which is held here.
-        unsafe { *self.receiver.get() = file.as_raw() };
+    /// Any previously registered file is replaced.
+    pub fn connect<F: DriverFile>(&self, file: &File<F>) {
+        *self.receiver.lock() = file.as_raw() as usize;
     }
 
     /// Clear the receiver, dropping any future events on the floor until the next
-    /// [`connect`](Self::connect). Runs under `dev`'s `event_lock`.
-    pub fn disconnect<D: drm::Driver, C: DeviceContext>(&self, dev: &Device<D, C>) {
-        let irq = interrupt::local_interrupt_disable();
-        let _guard = dev.event_lock().lock_with(&irq);
-        // SAFETY: `receiver` is only accessed under `event_lock`, which is held here.
-        unsafe { *self.receiver.get() = core::ptr::null_mut() };
+    /// [`connect`](Self::connect).
+    #[inline]
+    pub fn disconnect(&self) {
+        *self.receiver.lock() = 0;
     }
 
     /// Clear the receiver if it is `file`.
     ///
     /// Intended to be called from the driver's `postclose` hook so the stored pointer never
-    /// outlives the file. Runs under `dev`'s `event_lock`.
-    pub fn notify_close<D: drm::Driver, C: DeviceContext, F: DriverFile>(
-        &self,
-        dev: &Device<D, C>,
-        file: &File<F>,
-    ) {
-        let irq = interrupt::local_interrupt_disable();
-        let _guard = dev.event_lock().lock_with(&irq);
-        // SAFETY: `receiver` is only accessed under `event_lock`, which is held here.
-        unsafe {
-            if *self.receiver.get() == file.as_raw() {
-                *self.receiver.get() = core::ptr::null_mut();
-            }
+    /// outlives the file.
+    pub fn notify_close<F: DriverFile>(&self, file: &File<F>) {
+        let mut recv = self.receiver.lock();
+        if *recv == file.as_raw() as usize {
+            *recv = 0;
         }
     }
 
-    /// Whether a receiver is currently connected. Runs under `dev`'s `event_lock`.
-    pub fn is_connected<D: drm::Driver, C: DeviceContext>(&self, dev: &Device<D, C>) -> bool {
-        let irq = interrupt::local_interrupt_disable();
-        let _guard = dev.event_lock().lock_with(&irq);
-        // SAFETY: `receiver` is only accessed under `event_lock`, which is held here.
-        !unsafe { *self.receiver.get() }.is_null()
+    /// Whether a receiver is currently connected.
+    #[inline]
+    pub fn is_connected(&self) -> bool {
+        *self.receiver.lock() != 0
+    }
+
+    /// Run `f` with the connected receiver [`File`], if any, returning its result.
+    ///
+    /// The receiver mutex is held for the duration, so `f` may sleep (e.g. to create a GEM handle
+    /// in the client's file with [`Framebuffer::create_handle`](crate::drm::kms::framebuffer::Framebuffer::create_handle))
+    /// without the file being closed underneath it: [`notify_close`](Self::notify_close) takes the
+    /// same mutex and so blocks until `f` returns. Returns [`None`] if no client is connected.
+    ///
+    /// # Type parameter
+    ///
+    /// `F` must be the same [`DriverFile`] type the receiver was [`connect`](Self::connect)ed with.
+    pub fn with_receiver<F: DriverFile, R>(&self, f: impl FnOnce(&File<F>) -> R) -> Option<R> {
+        let recv = self.receiver.lock();
+        let raw = *recv as *mut bindings::drm_file;
+        if raw.is_null() {
+            return None;
+        }
+        // SAFETY: `raw` was stored from a live `File<F>` by `connect`; it stays valid because
+        // `notify_close`/`disconnect` (which clear it) take this same mutex, held here.
+        let file = unsafe { File::<F>::from_raw(raw) };
+        Some(f(file))
     }
 
     /// Deliver `payload` to the connected receiver.
     ///
     /// Allocates the backing event, fills its `drm_event` header, and hands it to the DRM core
-    /// under `event_lock`. If no receiver is connected the event is silently dropped and `Ok(())`
-    /// is returned (mirroring the C drivers, which cannot deliver to a closed client). If the
-    /// client has no space left in its event queue this returns `Err(ENOMEM)` and the event is
-    /// dropped; the caller may retry later.
+    /// under `event_lock` (acquired while the receiver mutex is held). If no receiver is connected
+    /// the event is silently dropped and `Ok(())` is returned (mirroring the C drivers, which
+    /// cannot deliver to a closed client). If the client has no space left in its event queue this
+    /// returns `Err(ENOMEM)` and the event is dropped; the caller may retry later.
     pub fn send<D: drm::Driver, C: DeviceContext, T: EventPayload>(
         &self,
         dev: &Device<D, C>,
@@ -175,41 +183,34 @@ pub fn send<D: drm::Driver, C: DeviceContext, T: EventPayload>(
         storage.pending.event = ev_ptr;
         let pending: *mut bindings::drm_pending_event = &raw mut storage.pending;
 
-        let irq = interrupt::local_interrupt_disable();
-        let guard = dev.event_lock().lock_with(&irq);
-
-        // SAFETY: `receiver` is only accessed under `event_lock`, which is held here.
-        let receiver = unsafe { *self.receiver.get() };
+        // Hold the receiver mutex across the whole reserve+queue so the file can't close underneath
+        // us, then take `event_lock` as the C API requires.
+        let recv = self.receiver.lock();
+        let receiver = *recv as *mut bindings::drm_file;
         if receiver.is_null() {
-            drop(guard);
-            // `storage` is dropped here, freeing the allocation.
+            // No client; `storage` is dropped here, freeing the allocation.
             return Ok(());
         }
 
         let dev_raw = dev.as_raw();
-        // SAFETY: `dev_raw` and `receiver` are valid; `pending`/`ev_ptr` point into a live
-        // allocation; `event_lock` is held as required by the C API.
-        let ret = unsafe {
-            bindings::drm_event_reserve_init_locked(dev_raw, receiver, pending, ev_ptr)
-        };
-        if ret != 0 {
-            drop(guard);
-            // Reservation failed, so `pending` was never queued: `storage` frees the allocation.
-            return to_result(ret);
+        let irq = interrupt::local_interrupt_disable();
+        {
+            let _ev = dev.event_lock().lock_with(&irq);
+            // SAFETY: `dev_raw`/`receiver` are valid; `pending`/`ev_ptr` point into a live
+            // allocation; `event_lock` is held as required by the C API.
+            let ret = unsafe {
+                bindings::drm_event_reserve_init_locked(dev_raw, receiver, pending, ev_ptr)
+            };
+            if ret != 0 {
+                // Reservation failed, so `pending` was never queued: `storage` frees it.
+                return to_result(ret);
+            }
+            // SAFETY: The event is reserved against `receiver`; hand ownership of the allocation to
+            // the DRM core, which frees it (via `kfree` of the `drm_pending_event` at offset 0)
+            // once delivered or the file closes. `event_lock` is held as required.
+            unsafe { bindings::drm_send_event_locked(dev_raw, pending) };
         }
-
-        // SAFETY: The event is reserved against `receiver`; hand ownership of the allocation to the
-        // DRM core, which frees it (via `kfree` of the `drm_pending_event` at offset 0) once the
-        // event is delivered or the file is closed. `event_lock` is held as required.
-        unsafe { bindings::drm_send_event_locked(dev_raw, pending) };
         let _ = KBox::into_raw(storage);
-        drop(guard);
         Ok(())
     }
 }
-
-impl Default for EventChannel {
-    fn default() -> Self {
-        Self::new()
-    }
-}
diff --git a/rust/kernel/drm/kms/framebuffer.rs b/rust/kernel/drm/kms/framebuffer.rs
index c7089b12d0d1..e12fa7a24e15 100644
--- a/rust/kernel/drm/kms/framebuffer.rs
+++ b/rust/kernel/drm/kms/framebuffer.rs
@@ -121,6 +121,34 @@ pub fn pitch(&self, index: usize) -> u32 {
         unsafe { (*self.as_raw()).pitches[index & 3] }
     }
 
+    /// The framebuffer's pixel format as a FourCC code (`DRM_FORMAT_*`).
+    #[inline]
+    pub fn format(&self) -> u32 {
+        // SAFETY: a valid framebuffer always has a non-null, immutable `format` descriptor.
+        unsafe { (*(*self.as_raw()).format).format }
+    }
+
+    /// Create a GEM handle for this framebuffer's plane-0 backing object in `file`'s handle space,
+    /// returning it.
+    ///
+    /// This lets a driver hand a userspace client (e.g. via an event) a handle to a buffer the
+    /// client did not create itself -- for instance a cursor bitmap set by a compositor that a
+    /// separate control daemon needs to read. May sleep.
+    pub fn create_handle<F: crate::drm::file::DriverFile>(
+        &self,
+        file: &crate::drm::File<F>,
+    ) -> Result<u32> {
+        // SAFETY: `as_raw()` is a valid framebuffer; `obj[0]` is its plane-0 GEM object.
+        let obj = unsafe { (*self.as_raw()).obj[0] };
+        if obj.is_null() {
+            return Err(EINVAL);
+        }
+        let mut handle = 0u32;
+        // SAFETY: `file` and `obj` are valid; `drm_gem_handle_create` initializes `handle`.
+        to_result(unsafe { bindings::drm_gem_handle_create(file.as_raw().cast(), obj, &mut handle) })?;
+        Ok(handle)
+    }
+
     /// Map this framebuffer's plane-0 backing pages into the kernel address space for CPU
     /// access, for the duration of the returned guard.
     ///
diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs
index 3863dfe1b84c..fcaa8ba550fe 100644
--- a/rust/kernel/drm/kms/plane.rs
+++ b/rust/kernel/drm/kms/plane.rs
@@ -605,6 +605,26 @@ fn crtc_h(&self) -> u32 {
         self.as_raw().crtc_h
     }
 
+    /// The X position of this plane's destination rectangle within the CRTC (`crtc_x`).
+    fn crtc_x(&self) -> i32 {
+        self.as_raw().crtc_x
+    }
+
+    /// The Y position of this plane's destination rectangle within the CRTC (`crtc_y`).
+    fn crtc_y(&self) -> i32 {
+        self.as_raw().crtc_y
+    }
+
+    /// The cursor hotspot X offset (`hotspot_x`), for a virtualized cursor plane.
+    fn hotspot_x(&self) -> i32 {
+        self.as_raw().hotspot_x
+    }
+
+    /// The cursor hotspot Y offset (`hotspot_y`), for a virtualized cursor plane.
+    fn hotspot_y(&self) -> i32 {
+        self.as_raw().hotspot_y
+    }
+
     /// Return the current [`OpaqueCrtc`] assigned to this plane, if there is one.
     fn crtc<'a, 'b: 'a, D>(&'a self) -> Option<&'b OpaqueCrtc<D>>
     where
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 16/18] rust: drm: add CRTC gamma LUT and plane rotation property bindings
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
                     ` (14 preceding siblings ...)
  2026-07-03  3:01   ` [RFC PATCH v2 15/18] rust: drm: support hardware cursor planes with sleepable event delivery Mike Lothian
@ 2026-07-03  3:01   ` Mike Lothian
  2026-07-03  3:01   ` [RFC PATCH v2 17/18] rust: drm: kms: add connector detect() and mode_valid() hooks Mike Lothian
  2026-07-03  3:01   ` [RFC PATCH v2 18/18] rust: drm: kms: add plane damage-clip accessors Mike Lothian
  17 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:01 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

Two more mode-object properties a real driver reaches for, and the last
the DisplayLink vino driver needs to match its pre-safe-KMS feature set:

  - CRTC gamma: UnregisteredCrtc::enable_gamma()
    (drm_crtc_enable_color_mgmt, creating a GAMMA_LUT property) and
    RawCrtcState::gamma_lut() to read the set ramp back as a
    &[drm_color_lut].

  - Plane rotation: UnregisteredPlane::create_rotation_property()
    (drm_plane_create_rotation_property) and RawPlaneState::rotation() /
    crtc_x()/crtc_y()/hotspot_x()/hotspot_y() to read the plane's
    placement and orientation.

drm_plane_create_rotation_property lives in <drm/drm_blend.h>, so add
that to bindings_helper.h.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/bindings/bindings_helper.h |  1 +
 rust/kernel/drm/kms/crtc.rs     | 33 +++++++++++++++++++++++++++++++++
 rust/kernel/drm/kms/plane.rs    | 29 +++++++++++++++++++++++++++++
 3 files changed, 63 insertions(+)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index e5cfda610781..7d04abec1215 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -39,6 +39,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_connector.h>
 #include <drm/drm_crtc.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_encoder.h>
 #include <drm/drm_framebuffer.h>
diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs
index fbe113676e2f..b5128ae1ca95 100644
--- a/rust/kernel/drm/kms/crtc.rs
+++ b/rust/kernel/drm/kms/crtc.rs
@@ -356,6 +356,17 @@ pub fn new<'a, 'b: 'a, PrimaryData, CursorData>(
         // SAFETY: We just allocated the crtc above, so this pointer must be valid
         Ok(unsafe { &*this })
     }
+
+    /// Enable colour management on this CRTC, creating a `GAMMA_LUT` property of `gamma_size`
+    /// entries that userspace can program (no degamma LUT, no CTM). The set LUT is then readable
+    /// from the CRTC state via [`RawCrtcState::gamma_lut`].
+    ///
+    /// Call this during [`KmsDriver::probe`](crate::drm::kms::KmsDriver::probe), before the device
+    /// is registered.
+    pub fn enable_gamma(&self, gamma_size: u32) {
+        // SAFETY: `as_raw()` is a valid, not-yet-registered CRTC.
+        unsafe { bindings::drm_crtc_enable_color_mgmt(self.as_raw(), 0, false, gamma_size) };
+    }
 }
 
 // SAFETY: We inherit all relevant invariants of `Crtc`
@@ -686,6 +697,28 @@ fn mode(&self) -> &DisplayMode {
         // interface can ensure this access is serialized.
         unsafe { DisplayMode::as_ref(&(*self.as_raw()).mode) }
     }
+
+    /// Returns the CRTC's gamma LUT for this state as an array of [`drm_color_lut`] entries, or
+    /// [`None`] if no gamma LUT is programmed. Requires gamma to have been enabled on the CRTC
+    /// (see [`UnregisteredCrtc::enable_gamma`]).
+    ///
+    /// [`drm_color_lut`]: bindings::drm_color_lut
+    fn gamma_lut(&self) -> Option<&[bindings::drm_color_lut]> {
+        // SAFETY: `as_raw()` is a valid `drm_crtc_state`.
+        let blob = unsafe { (*self.as_raw()).gamma_lut };
+        if blob.is_null() {
+            return None;
+        }
+        // SAFETY: a non-null gamma_lut blob is valid for the state's lifetime.
+        let (data, length) = unsafe { ((*blob).data, (*blob).length) };
+        let n = length / core::mem::size_of::<bindings::drm_color_lut>();
+        if data.is_null() || n == 0 {
+            return None;
+        }
+        // SAFETY: the blob's `data` holds `n` contiguous `drm_color_lut` entries (the blob length
+        // is a multiple of the entry size), valid for the state's lifetime.
+        Some(unsafe { core::slice::from_raw_parts(data.cast::<bindings::drm_color_lut>(), n) })
+    }
 }
 impl<T: AsRawCrtcState> RawCrtcState for T {}
 
diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs
index fcaa8ba550fe..ead875316829 100644
--- a/rust/kernel/drm/kms/plane.rs
+++ b/rust/kernel/drm/kms/plane.rs
@@ -328,6 +328,28 @@ pub fn new<'a, 'b: 'a>(
         // SAFETY: We just allocated the plane above, so this pointer must be valid
         Ok(unsafe { &*this })
     }
+
+    /// Attach a rotation property to this plane, advertising `supported_rotations` (a bitmask of
+    /// `DRM_MODE_ROTATE_*` | `DRM_MODE_REFLECT_*`) with initial value `default_rotation`. The
+    /// selected value is then readable from the plane state via
+    /// [`RawPlaneState::rotation`](crate::drm::kms::plane::RawPlaneState::rotation).
+    ///
+    /// Call this during [`KmsDriver::probe`](crate::drm::kms::KmsDriver::probe), before the device
+    /// is registered.
+    pub fn create_rotation_property(
+        &self,
+        default_rotation: u32,
+        supported_rotations: u32,
+    ) -> Result {
+        // SAFETY: `as_raw()` is a valid, not-yet-registered plane.
+        to_result(unsafe {
+            bindings::drm_plane_create_rotation_property(
+                self.as_raw(),
+                default_rotation,
+                supported_rotations,
+            )
+        })
+    }
 }
 
 /// A trait implemented by any type that acts as a [`struct drm_plane`] interface.
@@ -625,6 +647,13 @@ fn hotspot_y(&self) -> i32 {
         self.as_raw().hotspot_y
     }
 
+    /// The plane's rotation/reflection (`DRM_MODE_ROTATE_*` | `DRM_MODE_REFLECT_*` bitmask), for a
+    /// plane with a rotation property (see
+    /// [`UnregisteredPlane::create_rotation_property`]). Defaults to `DRM_MODE_ROTATE_0`.
+    fn rotation(&self) -> u32 {
+        self.as_raw().rotation
+    }
+
     /// Return the current [`OpaqueCrtc`] assigned to this plane, if there is one.
     fn crtc<'a, 'b: 'a, D>(&'a self) -> Option<&'b OpaqueCrtc<D>>
     where
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 17/18] rust: drm: kms: add connector detect() and mode_valid() hooks
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
                     ` (15 preceding siblings ...)
  2026-07-03  3:01   ` [RFC PATCH v2 16/18] rust: drm: add CRTC gamma LUT and plane rotation property bindings Mike Lothian
@ 2026-07-03  3:01   ` Mike Lothian
  2026-07-03  3:01   ` [RFC PATCH v2 18/18] rust: drm: kms: add plane damage-clip accessors Mike Lothian
  17 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:01 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

The safe connector layer only exposed get_modes(), so a driver couldn't
report hotplug state or prune modes it can't drive. Add two optional
DriverConnector hooks, gated the same way as the plane/CRTC optionals
(registered in the vtable only when the driver overrides them):

  - detect(&Connector, force) -> Status, wired to
    drm_connector_funcs.detect, returning a new Status enum
    (Connected/Disconnected/Unknown mapping to enum drm_connector_status).

  - mode_valid(&Connector, &DisplayMode) -> ModeStatus, wired to
    drm_connector_helper_funcs.mode_valid, returning a new ModeStatus enum
    (Ok/Bad/ClockHigh/ClockLow, a common subset of enum drm_mode_status).

Both leave DRM's default behaviour (always connected, all modes valid)
unchanged for connectors that don't implement them.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/kernel/drm/kms/connector.rs | 98 +++++++++++++++++++++++++++++++-
 1 file changed, 95 insertions(+), 3 deletions(-)

diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs
index 591153de7ab4..a66243db8868 100644
--- a/rust/kernel/drm/kms/connector.rs
+++ b/rust/kernel/drm/kms/connector.rs
@@ -5,7 +5,8 @@
 //! C header: [`include/drm/drm_connector.h`](srctree/include/drm/drm_connector.h)
 
 use super::{
-    atomic::*, encoder::*, KmsDriver, ModeConfigGuard, ModeObject, ModeObjectVtable, Sealed
+    atomic::*, encoder::*, modes::DisplayMode, KmsDriver, ModeConfigGuard, ModeObject,
+    ModeObjectVtable, Sealed,
 };
 use crate::{
     alloc::KBox,
@@ -72,6 +73,43 @@ pub enum Type {
     USB         as Usb
 }
 
+/// The connection status of a [`Connector`], as returned by [`DriverConnector::detect`].
+///
+/// This is identical to [`enum drm_connector_status`].
+///
+/// [`enum drm_connector_status`]: srctree/include/drm/drm_connector.h
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(u32)]
+pub enum Status {
+    /// The connector is connected to a display and a mode list can be retrieved.
+    Connected = bindings::drm_connector_status_connector_status_connected,
+    /// The connector has no display attached.
+    Disconnected = bindings::drm_connector_status_connector_status_disconnected,
+    /// The connection state could not be determined (treated as connected for probing).
+    Unknown = bindings::drm_connector_status_connector_status_unknown,
+}
+
+/// The result of validating a display mode against a [`Connector`], as returned by
+/// [`DriverConnector::mode_valid`].
+///
+/// This mirrors a small, commonly-used subset of [`enum drm_mode_status`]; use [`ModeStatus::Bad`]
+/// for a generic rejection, or the clock-specific variants when a mode is out of the driver's
+/// pixel-clock range.
+///
+/// [`enum drm_mode_status`]: srctree/include/drm/drm_modes.h
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(i32)]
+pub enum ModeStatus {
+    /// The mode is usable.
+    Ok = bindings::drm_mode_status_MODE_OK,
+    /// The mode is rejected for an unspecified reason.
+    Bad = bindings::drm_mode_status_MODE_BAD,
+    /// The mode's pixel clock is above what the driver can drive.
+    ClockHigh = bindings::drm_mode_status_MODE_CLOCK_HIGH,
+    /// The mode's pixel clock is below what the driver can drive.
+    ClockLow = bindings::drm_mode_status_MODE_CLOCK_LOW,
+}
+
 /// The main trait for implementing the [`struct drm_connector`] API for [`Connector`].
 ///
 /// Any KMS driver should have at least one implementation of this type, which allows them to create
@@ -105,14 +143,22 @@ pub trait DriverConnector: Send + Sync + Sized {
             atomic_destroy_state: Some(atomic_destroy_state_callback::<Self::State>),
             destroy: Some(connector_destroy_callback::<Self>),
             force: None,
-            detect: None,
+            detect: if Self::HAS_DETECT {
+                Some(detect_callback::<Self>)
+            } else {
+                None
+            },
             fill_modes: Some(bindings::drm_helper_probe_single_connector_modes),
             debugfs_init: None,
             oob_hotplug_event: None,
             atomic_duplicate_state: Some(atomic_duplicate_state_callback::<Self::State>),
         },
         helper_funcs: bindings::drm_connector_helper_funcs {
-            mode_valid: None,
+            mode_valid: if Self::HAS_MODE_VALID {
+                Some(mode_valid_callback::<Self>)
+            } else {
+                None
+            },
             atomic_check: None,
             get_modes: Some(get_modes_callback::<Self>),
             detect_ctx: None,
@@ -151,6 +197,28 @@ fn get_modes<'a>(
         connector: ConnectorGuard<'a, Self>,
         guard: &ModeConfigGuard<'a, Self::Driver>,
     ) -> i32;
+
+    /// The optional [`drm_connector_funcs.detect`] hook for this connector.
+    ///
+    /// Drivers may implement this to report whether a display is currently attached. If not
+    /// implemented, the connector is always considered connected (DRM's default with no `detect`
+    /// hook). `force` is set when userspace explicitly requested a forced probe.
+    ///
+    /// [`drm_connector_funcs.detect`]: srctree/include/drm/drm_connector.h
+    fn detect(_connector: &Connector<Self>, _force: bool) -> Status {
+        build_error::build_error("This should not be reachable")
+    }
+
+    /// The optional [`drm_connector_helper_funcs.mode_valid`] hook for this connector.
+    ///
+    /// Drivers may implement this to reject modes they cannot drive (for example, a mode whose
+    /// pixel clock exceeds the hardware's budget). Returning anything other than [`ModeStatus::Ok`]
+    /// prunes the mode from the probed list. If not implemented, every mode is accepted.
+    ///
+    /// [`drm_connector_helper_funcs.mode_valid`]: srctree/include/drm/drm_modeset_helper_vtables.h
+    fn mode_valid(_connector: &Connector<Self>, _mode: &DisplayMode) -> ModeStatus {
+        build_error::build_error("This should not be reachable")
+    }
 }
 
 /// The generated C vtable for a [`DriverConnector`].
@@ -464,6 +532,30 @@ impl<T: AsRawConnector> RawConnector for T {}
     T::get_modes(connector.guard(&guard), &guard)
 }
 
+unsafe extern "C" fn detect_callback<T: DriverConnector>(
+    connector: *mut bindings::drm_connector,
+    force: bool,
+) -> bindings::drm_connector_status {
+    // SAFETY: This is safe via `DriverConnector`s type invariants.
+    let connector = unsafe { Connector::<T>::from_raw(connector) };
+
+    T::detect(connector, force) as bindings::drm_connector_status
+}
+
+unsafe extern "C" fn mode_valid_callback<T: DriverConnector>(
+    connector: *mut bindings::drm_connector,
+    mode: *const bindings::drm_display_mode,
+) -> bindings::drm_mode_status {
+    // SAFETY: This is safe via `DriverConnector`s type invariants.
+    let connector = unsafe { Connector::<T>::from_raw(connector) };
+
+    // SAFETY: DRM guarantees `mode` points to a valid `drm_display_mode` for the duration of this
+    // callback, and only passes us shared access to it.
+    let mode = unsafe { DisplayMode::as_ref(mode) };
+
+    T::mode_valid(connector, mode) as bindings::drm_mode_status
+}
+
 /// A [`struct drm_connector`] without a known [`DriverConnector`] implementation.
 ///
 /// This is mainly for situations where our bindings can't infer the [`DriverConnector`]
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [RFC PATCH v2 18/18] rust: drm: kms: add plane damage-clip accessors
  2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
                     ` (16 preceding siblings ...)
  2026-07-03  3:01   ` [RFC PATCH v2 17/18] rust: drm: kms: add connector detect() and mode_valid() hooks Mike Lothian
@ 2026-07-03  3:01   ` Mike Lothian
  17 siblings, 0 replies; 29+ messages in thread
From: Mike Lothian @ 2026-07-03  3:01 UTC (permalink / raw)
  To: rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Lyude Paul, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, linux-kernel, Mike Lothian

A driver whose "scanout" is an expensive encode-and-send (like a USB
display) wants to repaint only the region a client marked dirty rather
than the whole framebuffer every flip. Add two RawPlaneState accessors
over the client's FB_DAMAGE_CLIPS (relative to the old plane state), both
intersected with the visible source area:

  - damage_merged(), wrapping drm_atomic_helper_damage_merged(): merges the
    clips into one rectangle and returns it as a new Rect type (with
    width()/height() helpers). None means nothing to repaint; if the client
    supplied no clips, the full plane rectangle is returned, so a driver can
    always treat Some as "repaint this box".

  - for_each_damage_clip(), driving drm_atomic_helper_damage_iter: invokes a
    closure once per individual clip (the rectangles damage_merged() collapses
    into one), so a driver that forwards each changed region separately -- e.g.
    over USB -- can repaint just those rectangles instead of their bounding box.

drm_atomic_helper_damage_{merged,iter_*} live in <drm/drm_damage_helper.h>,
so add that to bindings_helper.h.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/bindings/bindings_helper.h |   1 +
 rust/kernel/drm/kms/plane.rs    | 105 ++++++++++++++++++++++++++++++++
 2 files changed, 106 insertions(+)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 7d04abec1215..b8675642c1a8 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -39,6 +39,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_connector.h>
 #include <drm/drm_crtc.h>
+#include <drm/drm_damage_helper.h>
 #include <drm/drm_blend.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_encoder.h>
diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs
index ead875316829..4ae11cec88a4 100644
--- a/rust/kernel/drm/kms/plane.rs
+++ b/rust/kernel/drm/kms/plane.rs
@@ -605,6 +605,49 @@ pub trait FromRawPlaneState: AsRawPlaneState {
     unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_plane_state) -> &'a mut Self;
 }
 
+/// A rectangle in a plane's source (pixel) space, as produced by
+/// [`RawPlaneState::damage_merged`].
+///
+/// The box is inclusive on the top-left and exclusive on the bottom-right (`[x1, x2)` by
+/// `[y1, y2)`), matching [`struct drm_rect`].
+///
+/// [`struct drm_rect`]: srctree/include/drm/drm_rect.h
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct Rect {
+    /// Left edge, inclusive.
+    pub x1: i32,
+    /// Top edge, inclusive.
+    pub y1: i32,
+    /// Right edge, exclusive.
+    pub x2: i32,
+    /// Bottom edge, exclusive.
+    pub y2: i32,
+}
+
+impl Rect {
+    #[inline]
+    fn from_raw(r: &bindings::drm_rect) -> Self {
+        Self {
+            x1: r.x1,
+            y1: r.y1,
+            x2: r.x2,
+            y2: r.y2,
+        }
+    }
+
+    /// The width of the rectangle in pixels.
+    #[inline]
+    pub fn width(&self) -> i32 {
+        self.x2 - self.x1
+    }
+
+    /// The height of the rectangle in pixels.
+    #[inline]
+    pub fn height(&self) -> i32 {
+        self.y2 - self.y1
+    }
+}
+
 /// Common methods available on any type which implements [`AsRawPlane`].
 ///
 /// This is implemented internally by DRM, and provides many of the basic methods for working with
@@ -691,6 +734,68 @@ fn atomic_helper_check<S, D>(
         })
     }
 
+    /// Merge all frame-damage clips on this (new) plane state -- relative to `old` -- into a
+    /// single bounding rectangle, intersected with the plane's visible source area.
+    ///
+    /// Returns [`None`] when the plane is not visible or there is nothing to update. If the client
+    /// supplied no explicit damage clips, the full plane rectangle is returned, so a driver can
+    /// always treat [`Some`] as "repaint this rectangle" and fall back to a full-frame update.
+    /// Coordinates are integer pixels in the plane's source space.
+    ///
+    /// [`drm_atomic_helper_damage_merged`]: srctree/include/drm/drm_damage_helper.h
+    fn damage_merged(&self, old: &impl AsRawPlaneState) -> Option<Rect> {
+        let mut rect = bindings::drm_rect {
+            x1: 0,
+            y1: 0,
+            x2: 0,
+            y2: 0,
+        };
+
+        // SAFETY:
+        // - `old` and `self` are valid initialized `drm_plane_state`s via their type invariants.
+        // - `drm_atomic_helper_damage_merged` only reads the two states (to gather the damage
+        //   clips and the source rectangle) and writes the merged result into `rect`; it does not
+        //   mutate the plane state, so deriving a `*mut` from our shared reference is sound.
+        let visible = unsafe {
+            bindings::drm_atomic_helper_damage_merged(
+                core::ptr::from_ref(old.as_raw()),
+                core::ptr::from_ref(self.as_raw()).cast_mut(),
+                &mut rect,
+            )
+        };
+
+        visible.then(|| Rect::from_raw(&rect))
+    }
+
+    /// Invoke `f` once per frame-damage clip on this (new) plane state relative to `old`, each
+    /// intersected with the plane's visible source area -- i.e. the individual rectangles that
+    /// [`Self::damage_merged`] collapses into one. If the client supplied no explicit damage clips,
+    /// `f` is called once with the full plane rectangle. Coordinates are integer pixels in the
+    /// plane's source space.
+    ///
+    /// This lets a driver forward each changed region separately (e.g. to a remote display) instead
+    /// of the bounding box of them all.
+    ///
+    /// [`drm_atomic_helper_damage_iter`]: srctree/include/drm/drm_damage_helper.h
+    fn for_each_damage_clip(&self, old: &impl AsRawPlaneState, mut f: impl FnMut(Rect)) {
+        let mut iter = bindings::drm_atomic_helper_damage_iter::default();
+        let mut clip = bindings::drm_rect::default();
+        // SAFETY:
+        // - `old` and `self` are valid initialized `drm_plane_state`s via their type invariants.
+        // - `drm_atomic_helper_damage_iter_init` only reads the two states to set up `iter`, and
+        //   `_next` only reads `iter` and writes `clip`; neither escapes a pointer.
+        unsafe {
+            bindings::drm_atomic_helper_damage_iter_init(
+                &mut iter,
+                core::ptr::from_ref(old.as_raw()),
+                core::ptr::from_ref(self.as_raw()),
+            );
+            while bindings::drm_atomic_helper_damage_iter_next(&mut iter, &mut clip) {
+                f(Rect::from_raw(&clip));
+            }
+        }
+    }
+
     /// Return the framebuffer currently set for this plane state
     #[inline]
     fn framebuffer<D>(&self) -> Option<&Framebuffer<D>>
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v2 01/18] rust: drm: kms: forward-port the safe mode-object layer onto the typestate device
  2026-07-03  3:00   ` [RFC PATCH v2 01/18] rust: drm: kms: forward-port the safe mode-object layer onto the typestate device Mike Lothian
@ 2026-07-07 21:46     ` lyude
  2026-07-07 22:21     ` lyude
  1 sibling, 0 replies; 29+ messages in thread
From: lyude @ 2026-07-07 21:46 UTC (permalink / raw)
  To: Mike Lothian, rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	linux-kernel

NAK.

You are resubmitting my work with an entirely different authorship tag,
squashing all of the actual development history from this patch series,
adding a claude tag on something I have been working on myself over the
course of two years trying to get upstream.

The last time you showed me this was on a gitlab branch that instead
had me as a co-author and added claude as a co-author. Stop.

On Fri, 2026-07-03 at 04:00 +0100, Mike Lothian wrote:
> Port Lyude Paul's rvkms-slim safe mode-object layer
> (CRTC/plane/connector/
> encoder/vblank/atomic state) onto her newer rust-stuck typestate
> Device
> design (ParentDevice/DeviceContext/RegistrationData), which the
> preceding
> cherry-picked commits bring in. Source: lyude/rvkms-slim +
> lyude/rust/rust-stuck.
> 
> Same module split as rvkms-slim (kms/{atomic,connector,crtc,encoder,
> framebuffer,modes,plane,vblank}.rs), same trait shape, adjusted to
> the
> typestate device's generic Ctx/State parameters. This is a mechanical
> type-level port, not new design -- diff against lyude/rvkms-slim to
> see
> exactly what moved.
> 
> Does not build yet. The port's mode-object traits are mutually
> recursive in a way the current trait solver can't evaluate; the next
> few commits get it to a clean build.
> 
> Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
> Assisted-by: Claude:claude-sonnet-5 [Claude-Code]
> ---
>  rust/kernel/drm/kms.rs             |  395 +++++++++-
>  rust/kernel/drm/kms/atomic.rs      |  864 ++++++++++++++++++++++
>  rust/kernel/drm/kms/connector.rs   |  997 +++++++++++++++++++++++++
>  rust/kernel/drm/kms/crtc.rs        | 1110
> ++++++++++++++++++++++++++++
>  rust/kernel/drm/kms/encoder.rs     |  409 ++++++++++
>  rust/kernel/drm/kms/framebuffer.rs |   70 ++
>  rust/kernel/drm/kms/modes.rs       |   76 ++
>  rust/kernel/drm/kms/plane.rs       | 1095
> +++++++++++++++++++++++++++
>  rust/kernel/drm/kms/vblank.rs      |  461 ++++++++++++
>  9 files changed, 5469 insertions(+), 8 deletions(-)
>  create mode 100644 rust/kernel/drm/kms/atomic.rs
>  create mode 100644 rust/kernel/drm/kms/connector.rs
>  create mode 100644 rust/kernel/drm/kms/crtc.rs
>  create mode 100644 rust/kernel/drm/kms/encoder.rs
>  create mode 100644 rust/kernel/drm/kms/framebuffer.rs
>  create mode 100644 rust/kernel/drm/kms/modes.rs
>  create mode 100644 rust/kernel/drm/kms/plane.rs
>  create mode 100644 rust/kernel/drm/kms/vblank.rs
> 
> diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs
> index 084ed0aebd0e..11b09d2175db 100644
> --- a/rust/kernel/drm/kms.rs
> +++ b/rust/kernel/drm/kms.rs
> @@ -3,17 +3,37 @@
>  //! KMS driver abstractions for rust.
>  
>  use crate::{
> +    container_of,
>      drm::{
> -        device::Device,
> +        device::{Device, DeviceContext},
>          driver::Driver,
>          private::Sealed,
>          Uninit, //
>      },
>      error::to_result,
> -    prelude::*, //
> +    prelude::*,
> +    sync::{Mutex, MutexGuard},
> +    types::*,
>  };
>  use bindings;
> -use core::{marker::PhantomData, ops::Deref};
> +use core::{
> +    cell::Cell,
> +    marker::PhantomData,
> +    ops::Deref,
> +    ptr::{self, addr_of_mut, NonNull},
> +};
> +
> +// Forward-ported mode-object layer (see
> Documentation/gpu/rust/vino-kms-forward-port.md).
> +// Modules are declared here as each is grafted onto the rust-stuck
> typestate design.
> +// `modes` is self-contained and ported as-is; the rest follow per
> the plan's order.
> +pub mod atomic;
> +pub mod connector;
> +pub mod crtc;
> +pub mod encoder;
> +pub mod framebuffer;
> +pub mod modes;
> +pub mod plane;
> +pub mod vblank;
>  
>  /// The C vtable for a [`Device`].
>  ///
> @@ -77,13 +97,21 @@ impl KmsContext for Probing {}
>  /// This type is guaranteed to represent a [`Device`] that has not
> been registered with userspace,
>  /// and is in the process of setting up KMS support. It carries a
> [`KmsDeviceContext`] to indicate
>  /// which stage of the KMS setup process this [`Device`] is
> currently in.
> -pub struct NewKmsDevice<'a, T: Driver, C: KmsContext>(&'a Device<T,
> Uninit>, PhantomData<C>);
> +pub struct NewKmsDevice<'a, T: Driver, C: KmsContext> {
> +    drm: &'a Device<T, Uninit>,
> +    /// Tracks whether any CRTC created during probe requested
> vblank support, so that
> +    /// [`drm_vblank_init()`] can be called afterwards. Set by
> [`crtc::Crtc::new()`].
> +    ///
> +    /// [`drm_vblank_init()`]: srctree/include/drm/drm_vblank.h
> +    pub(crate) has_vblanks: Cell<bool>,
> +    _ctx: PhantomData<C>,
> +}
>  
>  impl<'a, T: Driver, C: KmsContext> Deref for NewKmsDevice<'a, T, C>
> {
>      type Target = Device<T, Uninit>;
>  
>      fn deref(&self) -> &Self::Target {
> -        self.0
> +        self.drm
>      }
>  }
>  
> @@ -95,15 +123,56 @@ fn deref(&self) -> &Self::Target {
>  /// [`PhantomData<Self>`]: PhantomData
>  #[vtable]
>  pub trait KmsDriver: Driver {
> +    /// The driver's [`DriverConnector`](connector::DriverConnector)
> implementation.
> +    ///
> +    /// TODO: This will be unneeded once we support multiple
> `DriverConnector` implementations.
> +    type Connector: connector::DriverConnector;
> +
> +    /// The driver's [`DriverPlane`](plane::DriverPlane)
> implementation.
> +    ///
> +    /// TODO: This will be unneeded once we support multiple
> `DriverPlane` implementations.
> +    type Plane: plane::DriverPlane;
> +
> +    /// The driver's [`DriverCrtc`](crtc::DriverCrtc)
> implementation.
> +    ///
> +    /// TODO: This will be unneeded once we support multiple
> `DriverCrtc` implementations.
> +    type Crtc: crtc::DriverCrtc;
> +
> +    /// The driver's [`DriverEncoder`](encoder::DriverEncoder)
> implementation.
> +    ///
> +    /// TODO: This will be unneeded once we support multiple
> `DriverEncoder` implementations.
> +    type Encoder: encoder::DriverEncoder;
> +
>      /// Return a [`ModeConfigInfo`] structure for this
> [`device::Device`].
>      fn mode_config_info(dev: &Device<Self, Uninit>) ->
> Result<ModeConfigInfo>
>      where
>          Self: Sized;
>  
>      /// Create mode objects like [`crtc::Crtc`], [`plane::Plane`],
> etc. for this device
> -    fn probe(drm: NewKmsDevice<'_, Self, Probing>) -> Result
> +    fn probe(drm: &NewKmsDevice<'_, Self, Probing>) -> Result
>      where
>          Self: Sized;
> +
> +    /// The optional [`atomic_commit_tail`] callback for this
> [`Device`].
> +    ///
> +    /// It must return a
> [`CommittedAtomicState`](atomic::CommittedAtomicState) to prove that
> it has
> +    /// signaled completion of the hw commit phase. Drivers may use
> this function to customize the
> +    /// order in which commits are performed during the atomic
> commit phase.
> +    ///
> +    /// If not provided, DRM will use its own default atomic commit
> tail helper
> +    /// `drm_atomic_helper_commit_tail`.
> +    ///
> +    /// [`atomic_commit_tail`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +    fn atomic_commit_tail<'a>(
> +        _state: atomic::AtomicCommitTail<'a, Self>,
> +        _modeset_token: atomic::ModesetsReadyToken<'_>,
> +        _plane_update_token: atomic::PlaneUpdatesReadyToken<'_>,
> +    ) -> atomic::CommittedAtomicState<'a, Self>
> +    where
> +        Self: Sized,
> +    {
> +        build_error::build_error("This function should not be
> reachable")
> +    }
>  }
>  
>  impl<T: KmsDriver> private::KmsImpl for T {
> @@ -123,7 +192,11 @@ impl<T: KmsDriver> private::KmsImpl for T {
>  
>          kms_helper_vtable: bindings::drm_mode_config_helper_funcs {
>              atomic_commit_setup: None,
> -            atomic_commit_tail: None,
> +            atomic_commit_tail: if T::HAS_ATOMIC_COMMIT_TAIL {
> +                Some(atomic::commit_tail_callback::<T>)
> +            } else {
> +                None
> +            },
>          },
>      });
>  
> @@ -156,7 +229,19 @@ unsafe fn probe_kms(drm: &Device<Self::Driver,
> Uninit>) -> Result<ModeConfigInfo
>          // SAFETY: We just setup all of the info required to call
> this function above.
>          to_result(unsafe {
> bindings::drmm_mode_config_init(drm.as_raw()) })?;
>  
> -        T::probe(NewKmsDevice(&drm, PhantomData))?;
> +        let new_kms = NewKmsDevice {
> +            drm: &drm,
> +            has_vblanks: Cell::new(false),
> +            _ctx: PhantomData,
> +        };
> +        T::probe(&new_kms)?;
> +
> +        if new_kms.has_vblanks.get() {
> +            // SAFETY: `has_vblanks` is only set when CRTCs with
> vblank support were created during
> +            // probe (the only place static mode objects are
> created), so the vblank state is ready
> +            // to be initialized.
> +            to_result(unsafe {
> bindings::drm_vblank_init(drm.as_raw(), drm.num_crtcs()) })?;
> +        }
>  
>          // TODO: In the future, we should add a hook here for
> initializing each state via hardware
>          // state readback.
> @@ -192,3 +277,297 @@ pub struct ModeConfigInfo {
>      /// An optional default fourcc format code to be preferred for
> clients.
>      pub preferred_fourcc: Option<u32>,
>  }
> +
> +// -----------------------------------------------------------------
> ----------
> +// Mode-object framework (forward-ported from lyude/rvkms-slim's
> kms.rs).
> +//
> +// Adapted to rust-stuck's typestate device: `Device<T, C>` defaults
> `C` to
> +// `Registered`, so most `Device<T>` references port unchanged; only
> creation
> +// paths (during probe) use the `Uninit` context, reached via
> `NewKmsDevice`.
> +// -----------------------------------------------------------------
> ----------
> +
> +/// Implement the repetitive from_opaque/try_from_opaque methods for
> all mode object and state
> +/// types.
> +///
> +/// Because there are so many different ways of accessing mode
> objects, their states, etc. we need a
> +/// macro that we can use for consistently implementing
> try_from_opaque()/from_opaque() functions to
> +/// convert from Opaque mode objects to fully typed mode objects.
> This macro handles that, and can
> +/// generate said functions for any kind of type which the original
> mode object driver trait can be
> +/// derived from.
> +macro_rules! impl_from_opaque_mode_obj {
> +    (
> +        fn <
> +            $( $lifetime:lifetime, )?
> +            $( $decl_bound_id:ident ),*
> +        > ($opaque:ty) -> $inner_ret_ty:ty
> +        $(
> +            where
> +                $( $extra_bound_id:ident : $extra_trait:ident<$(
> $extra_assoc:ident = $extra_param_match:ident ),+> ),+
> +        )? ;
> +        use
> +            $obj_trait_param:ident as $obj_trait:ident,
> +            $drv_trait_param:ident as
> KmsDriver<$drv_assoc_trait:ident = ...>
> +    ) => {
> +        #[doc = "Try to convert `opaque` into a fully qualified
> `Self`."]
> +        #[doc = ""]
> +        #[doc = concat!("This will try to convert `opaque` into
> `Self` if it shares the same [`",
> +                        stringify!($obj_trait), "`] implementation
> as `Self`.")]
> +        pub fn try_from_opaque<$( $lifetime, )? $( $decl_bound_id
> ),* >(
> +            opaque: $opaque
> +        ) -> Result<$inner_ret_ty, $opaque>
> +        where
> +            $drv_trait_param: KmsDriver<$drv_assoc_trait =
> $obj_trait_param>,
> +            $obj_trait_param: $obj_trait<Driver = $drv_trait_param>
> +            $( , $( $extra_bound_id: $extra_trait<$( $extra_assoc =
> $extra_param_match ),+> ),+ )?
> +        {
> +            // FIXME: What we really want to be doing here is
> comparing vtable pointers, but this is
> +            // currently blocked on getting unique vtable macros to
> ensure that each vtable has a
> +            // consistent memory pointer.
> +            // For the time being, we simply restrict things to one
> object type per driver and do a
> +            // transmutation based on that assumption holding true.
> +            // SAFETY: We currently only allow one object type per-
> driver, so this transmute is
> +            // always safe.
> +            Ok(unsafe { core::mem::transmute(opaque) })
> +        }
> +
> +        #[doc = "Convert `opaque` into a fully qualified `Self`."]
> +        #[doc = ""]
> +        #[doc = concat!("This is an infallible version of
> [`Self::try_from_opaque`]. This ",
> +                        "function is mainly useful for drivers where
> only a single [`",
> +                        stringify!($obj_trait), "`] implementation
> exists.")]
> +        #[doc = ""]
> +        #[doc = "# Panics"]
> +        #[doc = ""]
> +        #[doc = concat!("This function will panic if `opaque`
> belongs to a different [`",
> +                        stringify!($obj_trait), "`]
> implementation.")]
> +        pub fn from_opaque<$( $lifetime, )? $( $decl_bound_id ),* >(
> +            opaque: $opaque
> +        ) -> $inner_ret_ty
> +        where
> +            $drv_trait_param: KmsDriver<$drv_assoc_trait =
> $obj_trait_param>,
> +            $obj_trait_param: $obj_trait<Driver = $drv_trait_param>
> +            $( , $( $extra_bound_id: $extra_trait<$( $extra_assoc =
> $extra_param_match ),+> ),+ )?
> +        {
> +            Self::try_from_opaque(opaque)
> +                .map_or(None, |o| Some(o))
> +                .expect(concat!("Passed ", stringify!($opaque), "
> does not share this ",
> +                                stringify!($obj_trait), "
> implementation."))
> +        }
> +    };
> +}
> +
> +pub(crate) use impl_from_opaque_mode_obj;
> +
> +impl<T: KmsDriver, C: DeviceContext> Device<T, C> {
> +    /// Retrieve a pointer to the mode_config mutex
> +    #[inline]
> +    pub(crate) fn mode_config_mutex(&self) -> &Mutex<()> {
> +        // SAFETY: This lock is initialized for as long as
> `Device<T>` is exposed to users
> +        unsafe {
> Mutex::from_raw(addr_of_mut!((*self.as_raw()).mode_config.mutex)) }
> +    }
> +
> +    /// Return the number of registered [`Crtc`](crtc::Crtc) objects
> on this [`Device`].
> +    #[inline]
> +    pub fn num_crtcs(&self) -> u32 {
> +        // SAFETY:
> +        // * This can only be modified during the single-threaded
> context before registration, so
> +        //   this is safe
> +        // * num_crtc is always >= 0, so casting to u32 is fine
> +        unsafe { (*self.as_raw()).mode_config.num_crtc as u32 }
> +    }
> +}
> +
> +impl<T: KmsDriver> Device<T> {
> +    /// Acquire the [`mode_config.mutex`] for this [`Device`].
> +    #[inline]
> +    pub fn mode_config_lock(&self) -> ModeConfigGuard<'_, T> {
> +        // INVARIANT: We're locking mode_config.mutex, fulfilling
> our invariant that this lock is
> +        // held throughout ModeConfigGuard's lifetime.
> +        ModeConfigGuard(self.mode_config_mutex().lock(),
> PhantomData)
> +    }
> +}
> +
> +/// A modesetting object in DRM.
> +///
> +/// This is any type of object where the underlying C object
> contains a [`struct drm_mode_object`].
> +/// This type requires [`Send`] + [`Sync`] as all modesetting
> objects in DRM are able to be sent
> +/// between threads.
> +///
> +/// This type is only implemented by the DRM crate itself.
> +///
> +/// # Safety
> +///
> +/// [`raw_mode_obj()`] must always return a valid pointer to an
> initialized
> +/// [`struct drm_mode_object`].
> +///
> +/// [`struct drm_mode_object`]:
> srctree/include/drm/drm_mode_object.h
> +/// [`raw_mode_obj()`]: ModeObject::raw_mode_obj()
> +pub unsafe trait ModeObject: Sealed + Send + Sync {
> +    /// The parent driver for this [`ModeObject`].
> +    type Driver: KmsDriver;
> +
> +    /// Return the [`Device`] for this [`ModeObject`].
> +    fn drm_dev(&self) -> &Device<Self::Driver>;
> +
> +    /// Return a pointer to the [`struct drm_mode_object`] for this
> [`ModeObject`].
> +    ///
> +    /// [`struct drm_mode_object`]:
> (srctree/include/drm/drm_mode_object.h)
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object;
> +}
> +
> +/// A trait for modesetting objects which don't come with their own
> reference-counting.
> +///
> +/// Some [`ModeObject`] types in DRM do not have a reference count.
> These types are considered
> +/// "static" and share the lifetime of their parent [`Device`]. To
> retrieve an owned reference to
> +/// such types, see [`KmsRef`].
> +///
> +/// # Safety
> +///
> +/// This trait must only be implemented for modesetting objects
> which do not have a refcount within
> +/// their [`struct drm_mode_object`], otherwise [`KmsRef`] can't
> guarantee the object will stay
> +/// alive.
> +///
> +/// [`struct drm_mode_object`]:
> (srctree/include/drm/drm_mode_object.h)
> +pub unsafe trait StaticModeObject: ModeObject {}
> +
> +/// An owned reference to a [`StaticModeObject`].
> +///
> +/// Note that since [`StaticModeObject`] types share the lifetime of
> their parent [`Device`], the
> +/// parent [`Device`] will stay alive as long as this type exists.
> Thus, users should be aware that
> +/// storing a [`KmsRef`] within a [`ModeObject`] is a circular
> reference.
> +///
> +/// # Invariants
> +///
> +/// `self.0` points to a valid instance of `T` throughout the
> lifetime of this type.
> +pub struct KmsRef<T: StaticModeObject>(NonNull<T>);
> +
> +// SAFETY: Owned references to DRM device are thread-safe.
> +unsafe impl<T: StaticModeObject> Send for KmsRef<T> {}
> +// SAFETY: Owned references to DRM device are thread-safe.
> +unsafe impl<T: StaticModeObject> Sync for KmsRef<T> {}
> +
> +impl<T: StaticModeObject> From<&T> for KmsRef<T> {
> +    fn from(value: &T) -> Self {
> +        // INVARIANT: Because the lifetime of the StaticModeObject
> is the same as the lifetime of
> +        // its parent device, we can ensure that `value` remains
> alive by incrementing the device's
> +        // reference count. The device will only disappear once we
> drop this reference in `Drop`.
> +        value.drm_dev().inc_ref();
> +
> +        Self(value.into())
> +    }
> +}
> +
> +impl<T: StaticModeObject> Drop for KmsRef<T> {
> +    fn drop(&mut self) {
> +        // SAFETY: We're reclaiming the reference we leaked in
> From<&T>
> +        drop(unsafe { ARef::from_raw(self.drm_dev().into()) })
> +    }
> +}
> +
> +impl<T: StaticModeObject> Deref for KmsRef<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        // SAFETY: We're guaranteed object will point to a valid
> object as long as we hold dev
> +        unsafe { self.0.as_ref() }
> +    }
> +}
> +
> +impl<T: StaticModeObject> Clone for KmsRef<T> {
> +    fn clone(&self) -> Self {
> +        // INVARIANT: See `From<&T>`; we keep the parent device
> alive for this new reference.
> +        self.drm_dev().inc_ref();
> +
> +        Self(self.0)
> +    }
> +}
> +
> +macro_rules! impl_aref_for_mode_object {
> +    (impl $( < $( $param:ident: $bound:ident ),+ > )? for $type:ty)
> => {
> +        // SAFETY: drm_mode_object_get()/put() ensure the type is
> ref-counted according to the
> +        // safety contract
> +        unsafe impl $( < $( $param: $bound ),+ > )?
> kernel::types::AlwaysRefCounted for $type {
> +            #[inline]
> +            fn inc_ref(&self) {
> +                // SAFETY: We're guaranteed by the safety contract
> of `ModeObject` that
> +                // `raw_mode_obj()` always returns a pointer to an
> initialized `drm_mode_object`.
> +                unsafe {
> kernel::bindings::drm_mode_object_get(self.raw_mode_obj()) }
> +            }
> +
> +            #[inline]
> +            unsafe fn dec_ref(obj: core::ptr::NonNull<Self>) {
> +                // SAFETY: We're guaranteed by the safety contract
> of `ModeObject` that
> +                // `raw_mode_obj()` always returns a pointer to an
> initialized `drm_mode_object`.
> +                unsafe {
> kernel::bindings::drm_mode_object_put(obj.as_ref().raw_mode_obj()) }
> +            }
> +        }
> +    };
> +}
> +
> +pub(super) use impl_aref_for_mode_object;
> +
> +/// A trait for any object related to a [`ModeObject`] that can
> return its vtable.
> +///
> +/// This reference will be used for checking whether an opaque
> representation of a mode object uses a
> +/// specific driver trait implementation.
> +///
> +/// # Safety
> +///
> +/// `ModeObjectVtable::vtable()` must always return a valid pointer
> to the relevant mode object's
> +/// vtable.
> +pub(crate) unsafe trait ModeObjectVtable {
> +    /// The type for the auto-generated vtable.
> +    type Vtable;
> +
> +    /// Return a static reference to the auto-generated vtable for
> the relevant mode object.
> +    fn vtable(&self) -> *const Self::Vtable;
> +}
> +
> +/// A mode config guard.
> +///
> +/// This is an exclusive primitive that represents when
> [`drm_device.mode_config.mutex`] is held - as
> +/// some modesetting operations (particularly ones related to
> [`connectors`](connector)) are still
> +/// protected under this single lock. The lock will be dropped once
> this object is dropped.
> +///
> +/// # Invariants
> +///
> +/// - `self.0` is contained within a [`struct drm_mode_config`],
> which is contained within a
> +///   [`struct drm_device`].
> +/// - The [`KmsDriver`] implementation of that [`struct drm_device`]
> is always `T`.
> +/// - This type proves that [`drm_device.mode_config.mutex`] is
> acquired.
> +///
> +/// [`struct drm_mode_config`]: (srctree/include/drm/drm_device.h)
> +/// [`drm_device.mode_config.mutex`]:
> (srctree/include/drm/drm_device.h)
> +/// [`struct drm_device`]: (srctree/include/drm/drm_device.h)
> +pub struct ModeConfigGuard<'a, T: KmsDriver>(MutexGuard<'a, ()>,
> PhantomData<T>);
> +
> +impl<'a, T: KmsDriver> ModeConfigGuard<'a, T> {
> +    /// Return the [`Device`] that this [`ModeConfigGuard`] belongs
> to.
> +    pub fn drm_dev(&self) -> &'a Device<T> {
> +        let lock: *mut bindings::mutex =
> ptr::from_ref(self.0.lock_ref()).cast_mut().cast();
> +
> +        // SAFETY:
> +        // - `self` is embedded within a `drm_mode_config` via our
> type invariants
> +        // - `self.0.lock` has an equivalent data type to `mutex`
> via its type invariants.
> +        let mode_config = unsafe { container_of!(lock,
> bindings::drm_mode_config, mutex) };
> +
> +        // SAFETY: And that `drm_mode_config` lives in a
> `drm_device` via type invariants.
> +        unsafe {
> +            Device::from_raw(container_of!(
> +                mode_config,
> +                bindings::drm_device,
> +                mode_config
> +            ))
> +        }
> +    }
> +
> +    /// Assert that the given device is the owner of this mode
> config guard.
> +    ///
> +    /// # Panics
> +    ///
> +    /// Panics if `dev` is different from the owning device for this
> mode config guard.
> +    #[inline]
> +    pub(crate) fn assert_owner(&self, dev: &Device<T>) {
> +        assert!(ptr::eq(self.drm_dev(), dev));
> +    }
> +}
> diff --git a/rust/kernel/drm/kms/atomic.rs
> b/rust/kernel/drm/kms/atomic.rs
> new file mode 100644
> index 000000000000..cc14bff47abd
> --- /dev/null
> +++ b/rust/kernel/drm/kms/atomic.rs
> @@ -0,0 +1,864 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! [`struct drm_atomic_state`] related bindings for rust.
> +//!
> +//! [`struct drm_atomic_state`]: srctree/include/drm/drm_atomic.h
> +use super::{connector::*, crtc::*, plane::*, KmsDriver, ModeObject};
> +use crate::{
> +    bindings,
> +    drm::device::Device,
> +    error::{from_err_ptr, to_result},
> +    prelude::*,
> +    types::*,
> +};
> +use core::{cell::Cell, marker::*, mem::ManuallyDrop, ops::*,
> ptr::NonNull};
> +
> +/// The main wrapper around [`struct drm_atomic_state`].
> +///
> +/// This type is usually embedded within another interface such as
> an [`AtomicStateMutator`].
> +///
> +/// # Invariants
> +///
> +/// - The data layout of this type is identical to [`struct
> drm_atomic_state`].
> +/// - `state` is initialized for as long as this type is exposed to
> users.
> +///
> +/// [`struct drm_atomic_state`]: srctree/include/drm/drm_atomic.h
> +#[repr(transparent)]
> +pub struct AtomicState<T: KmsDriver> {
> +    pub(super) state: Opaque<bindings::drm_atomic_state>,
> +    _p: PhantomData<T>,
> +}
> +
> +impl<T: KmsDriver> AtomicState<T> {
> +    /// Reconstruct an immutable reference to an atomic state from
> the given pointer
> +    ///
> +    /// # Safety
> +    ///
> +    /// `ptr` must point to a valid initialized instance of [`struct
> drm_atomic_state`].
> +    ///
> +    /// [`struct drm_atomic_state`]:
> srctree/include/drm/drm_atomic.h
> +    #[allow(dead_code)]
> +    pub(super) unsafe fn from_raw<'a>(ptr: *const
> bindings::drm_atomic_state) -> &'a Self {
> +        // SAFETY: Our data layout is identical
> +        // INVARIANT: Our safety contract upholds the guarantee that
> `state` is initialized for as
> +        // long as this type is exposed to users.
> +        unsafe { &*ptr.cast() }
> +    }
> +
> +    pub(crate) fn as_raw(&self) -> *mut bindings::drm_atomic_state {
> +        self.state.get()
> +    }
> +
> +    /// Return the [`Device`] associated with this [`AtomicState`].
> +    pub fn drm_dev(&self) -> &Device<T> {
> +        // SAFETY:
> +        // - `state` is initialized via our type invariants.
> +        // - `dev` is invariant throughout the lifetime of
> `AtomicState`
> +        unsafe { Device::from_raw((*self.state.get()).dev) }
> +    }
> +
> +    /// Return the old atomic state for `crtc`, if it is present
> within this [`AtomicState`].
> +    pub fn get_old_crtc_state<C>(&self, crtc: &C) ->
> Option<&C::State>
> +    where
> +        C: ModesettableCrtc + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: This function either returns NULL or a valid
> pointer to a `drm_crtc_state`
> +        unsafe {
> +            bindings::drm_atomic_get_old_crtc_state(self.as_raw(),
> crtc.as_raw())
> +                .as_ref()
> +                .map(|p| C::State::from_raw(p))
> +        }
> +    }
> +
> +    /// Return the old atomic state for `plane`, if it is present
> within this [`AtomicState`].
> +    pub fn get_old_plane_state<P>(&self, plane: &P) ->
> Option<&P::State>
> +    where
> +        P: ModesettablePlane + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: This function either returns NULL or a valid
> pointer to a `drm_plane_state`
> +        unsafe {
> +            bindings::drm_atomic_get_old_plane_state(self.as_raw(),
> plane.as_raw())
> +                .as_ref()
> +                .map(|p| P::State::from_raw(p))
> +        }
> +    }
> +
> +    /// Return the old atomic state for `connector` if it is present
> within this [`AtomicState`].
> +    pub fn get_old_connector_state<C>(&self, connector: &C) ->
> Option<&C::State>
> +    where
> +        C: ModesettableConnector + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: This function either returns NULL or a valid
> pointer to a `drm_connector_state`.
> +        unsafe {
> +           
> bindings::drm_atomic_get_old_connector_state(self.as_raw(),
> connector.as_raw())
> +                .as_ref()
> +                .map(|p| C::State::from_raw(p))
> +        }
> +    }
> +}
> +
> +// SAFETY: DRM atomic state objects are always reference counted and
> the get/put functions satisfy
> +// the requirements.
> +unsafe impl<T: KmsDriver> AlwaysRefCounted for AtomicState<T> {
> +    fn inc_ref(&self) {
> +        // SAFETY: `state` is initialized for as long as this type
> is exposed to users
> +        unsafe { bindings::drm_atomic_state_get(self.state.get()) }
> +    }
> +
> +    unsafe fn dec_ref(obj: NonNull<Self>) {
> +        // SAFETY: `obj` contains a valid non-null pointer to an
> initialized `Self`.
> +        unsafe { bindings::drm_atomic_state_put(obj.as_ptr().cast())
> }
> +    }
> +}
> +
> +/// A smart-pointer for modifying the contents of an atomic state.
> +///
> +/// As it's not unreasonable for a modesetting driver to want to
> have references to the state of
> +/// multiple modesetting objects at once, along with mutating
> multiple states for unique modesetting
> +/// objects at once, this type provides a mechanism for safely doing
> both of these things.
> +///
> +/// To honor Rust's aliasing rules regarding mutable references,
> this structure ensures only one
> +/// mutable reference to a mode object's atomic state may exist at a
> time - and refuses to provide
> +/// another if one has already been taken out using runtime checks.
> +pub struct AtomicStateMutator<T: KmsDriver> {
> +    /// The state being mutated. Note that the use of `ManuallyDrop`
> here is because mutators are
> +    /// only constructed in FFI callbacks and thus borrow their
> references to the atomic state from
> +    /// DRM. Composers, which make use of mutators internally, can
> potentially be owned by rust code
> +    /// if a driver is performing an atomic commit internally - and
> thus will call the drop
> +    /// implementation here.
> +    state: ManuallyDrop<ARef<AtomicState<T>>>,
> +
> +    /// Bitmask of borrowed CRTC state objects
> +    pub(super) borrowed_crtcs: Cell<u32>,
> +    /// Bitmask of borrowed plane state objects
> +    pub(super) borrowed_planes: Cell<u32>,
> +    /// Bitmask of borrowed connector state objects
> +    pub(super) borrowed_connectors: Cell<u32>,
> +}
> +
> +impl<T: KmsDriver> AtomicStateMutator<T> {
> +    /// Construct a new [`AtomicStateMutator`]
> +    ///
> +    /// # Safety
> +    ///
> +    /// `ptr` must point to a valid `drm_atomic_state`
> +    #[allow(dead_code)]
> +    pub(super) unsafe fn new(ptr:
> NonNull<bindings::drm_atomic_state>) -> Self {
> +        Self {
> +            // SAFETY: The data layout of `AtomicState<T>` is
> identical to drm_atomic_state
> +            // We use `ManuallyDrop` because `AtomicStateMutator` is
> only ever provided to users in
> +            // the context of KMS callbacks. As such, skipping ref
> inc/dec for the atomic state is
> +            // convienent for our bindings.
> +            state: ManuallyDrop::new(unsafe {
> ARef::from_raw(ptr.cast()) }),
> +            borrowed_planes: Cell::default(),
> +            borrowed_crtcs: Cell::default(),
> +            borrowed_connectors: Cell::default(),
> +        }
> +    }
> +
> +    pub(crate) fn as_raw(&self) -> *mut bindings::drm_atomic_state {
> +        self.state.as_raw()
> +    }
> +
> +    /// Return the [`Device`] for this [`AtomicStateMutator`].
> +    pub fn drm_dev(&self) -> &Device<T> {
> +        self.state.drm_dev()
> +    }
> +
> +    /// Retrieve the last committed atomic state for `crtc` if
> `crtc` has already been added to the
> +    /// atomic state being composed.
> +    ///
> +    /// Returns `None` otherwise.
> +    pub fn get_old_crtc_state<C>(&self, crtc: &C) ->
> Option<&C::State>
> +    where
> +        C: ModesettableCrtc + ModeObject<Driver = T>,
> +    {
> +        self.state.get_old_crtc_state(crtc)
> +    }
> +
> +    /// Retrieve the last committed atomic state for `connector` if
> `connector` has already been
> +    /// added to the atomic state being composed.
> +    ///
> +    /// Returns `None` otherwise.
> +    pub fn get_old_connector_state<C>(&self, connector: &C) ->
> Option<&C::State>
> +    where
> +        C: ModesettableConnector + ModeObject<Driver = T>,
> +    {
> +        self.state.get_old_connector_state(connector)
> +    }
> +
> +    /// Retrieve the last committed atomic state for `plane` if
> `plane` has already been added to
> +    /// the atomic state being composed.
> +    ///
> +    /// Returns `None` otherwise.
> +    pub fn get_old_plane_state<P>(&self, plane: &P) ->
> Option<&P::State>
> +    where
> +        P: ModesettablePlane + ModeObject<Driver = T>,
> +    {
> +        self.state.get_old_plane_state(plane)
> +    }
> +
> +    /// Return a composer for `plane`s new atomic state if it was
> previously added to the atomic
> +    /// state being composed.
> +    ///
> +    /// Returns `None` otherwise, or if another mutator still exists
> for this state.
> +    pub fn get_new_crtc_state<C>(&self, crtc: &C) ->
> Option<CrtcStateMutator<'_, C::State>>
> +    where
> +        C: ModesettableCrtc + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: DRM either returns NULL or a valid pointer to a
> `drm_crtc_state`
> +        let state =
> +            unsafe {
> bindings::drm_atomic_get_new_crtc_state(self.as_raw(), crtc.as_raw())
> };
> +
> +        CrtcStateMutator::<C::State>::new(self,
> NonNull::new(state)?)
> +    }
> +
> +    /// Return a composer for `plane`s new atomic state if it was
> previously added to the atomic
> +    /// state being composed.
> +    ///
> +    /// Returns `None` otherwise, or if another mutator still exists
> for this state.
> +    pub fn get_new_plane_state<P>(&self, plane: &P) ->
> Option<PlaneStateMutator<'_, P::State>>
> +    where
> +        P: ModesettablePlane + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: DRM either returns NULL or a valid pointer to a
> `drm_plane_state`.
> +        let state =
> +            unsafe {
> bindings::drm_atomic_get_new_plane_state(self.as_raw(),
> plane.as_raw()) };
> +
> +        PlaneStateMutator::<P::State>::new(self,
> NonNull::new(state)?)
> +    }
> +
> +    /// Return a composer for `crtc`s new atomic state if it was
> previously added to the atomic
> +    /// state being composed.
> +    ///
> +    /// Returns `None` otherwise, or if another mutator still exists
> for this state.
> +    pub fn get_new_connector_state<C>(
> +        &self,
> +        connector: &C,
> +    ) -> Option<ConnectorStateMutator<'_, C::State>>
> +    where
> +        C: ModesettableConnector + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: DRM either returns NULL or a valid pointer to a
> `drm_connector_state`
> +        let state = unsafe {
> +           
> bindings::drm_atomic_get_new_connector_state(self.as_raw(),
> connector.as_raw())
> +        };
> +
> +        ConnectorStateMutator::<C::State>::new(self,
> NonNull::new(state)?)
> +    }
> +}
> +
> +/// An [`AtomicStateMutator`] wrapper which is not yet part of any
> commit operation.
> +///
> +/// Since it's not yet part of a commit operation, new mode objects
> may be added to the state. It
> +/// also holds a reference to the underlying [`AtomicState`] that
> will be released when this object
> +/// is dropped.
> +pub struct AtomicStateComposer<T: KmsDriver>(AtomicStateMutator<T>);
> +
> +impl<T: KmsDriver> Deref for AtomicStateComposer<T> {
> +    type Target = AtomicStateMutator<T>;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.0
> +    }
> +}
> +
> +impl<T: KmsDriver> Drop for AtomicStateComposer<T> {
> +    fn drop(&mut self) {
> +        // SAFETY: We're in drop, so this is guaranteed to be the
> last possible reference
> +        unsafe { ManuallyDrop::drop(&mut self.0.state) }
> +    }
> +}
> +
> +impl<T: KmsDriver> AtomicStateComposer<T> {
> +    /// # Safety
> +    ///
> +    /// The caller guarantees that `ptr` points to a valid instance
> of `drm_atomic_state`.
> +    pub(crate) unsafe fn new(ptr:
> NonNull<bindings::drm_atomic_state>) -> Self {
> +        // SAFETY: see `AtomicStateMutator::from_raw()`
> +        Self(unsafe { AtomicStateMutator::new(ptr) })
> +    }
> +
> +    /// Attempt to add the state for `crtc` to the atomic state for
> this composer if it hasn't
> +    /// already been added, and create a mutator for it.
> +    ///
> +    /// If a composer already exists for this `crtc`, this function
> returns `Error(EBUSY)`. If
> +    /// attempting to add the state fails, another error code will
> be returned.
> +    pub fn add_crtc_state<C>(&self, crtc: &C) ->
> Result<CrtcStateMutator<'_, C::State>>
> +    where
> +        C: ModesettableCrtc + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: DRM will only return a valid pointer to a
> `drm_crtc_state` - or an error.
> +        let state = unsafe {
> +            from_err_ptr(bindings::drm_atomic_get_crtc_state(
> +                self.as_raw(),
> +                crtc.as_raw(),
> +            ))
> +            .map(|c| NonNull::new_unchecked(c))
> +        }?;
> +
> +        CrtcStateMutator::<C::State>::new(self, state).ok_or(EBUSY)
> +    }
> +
> +    /// Attempt to add the state for `plane` to the atomic state for
> this composer if it hasn't
> +    /// already been added, and create a mutator for it.
> +    ///
> +    /// If a composer already exists for this `plane`, this function
> returns `Error(EBUSY)`. If
> +    /// attempting to add the state fails, another error code will
> be returned.
> +    pub fn add_plane_state<P>(&self, plane: &P) ->
> Result<PlaneStateMutator<'_, P::State>>
> +    where
> +        P: ModesettablePlane + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: DRM will only return a valid pointer to a
> `drm_plane_state` - or an error.
> +        let state = unsafe {
> +            from_err_ptr(bindings::drm_atomic_get_plane_state(
> +                self.as_raw(),
> +                plane.as_raw(),
> +            ))
> +            .map(|p| NonNull::new_unchecked(p))
> +        }?;
> +
> +        PlaneStateMutator::<P::State>::new(self, state).ok_or(EBUSY)
> +    }
> +
> +    /// Attempt to add the state for `connector` to the atomic state
> for this composer if it hasn't
> +    /// already been added, and create a mutator for it.
> +    ///
> +    /// If a composer already exists for this `connector`, this
> function returns `Error(EBUSY)`. If
> +    /// attempting to add the state fails, another error code will
> be returned.
> +    pub fn add_connector_state<C>(
> +        &self,
> +        connector: &C,
> +    ) -> Result<ConnectorStateMutator<'_, C::State>>
> +    where
> +        C: ModesettableConnector + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: DRM will only return a valid pointer to a
> `drm_plane_state` - or an error.
> +        let state = unsafe {
> +            from_err_ptr(bindings::drm_atomic_get_connector_state(
> +                self.as_raw(),
> +                connector.as_raw(),
> +            ))
> +            .map(|c| NonNull::new_unchecked(c))
> +        }?;
> +
> +        ConnectorStateMutator::<C::State>::new(self,
> state).ok_or(EBUSY)
> +    }
> +
> +    /// Attempt to add any planes affected by changes on `crtc` to
> this [`AtomicStateComposer`].
> +    ///
> +    /// Will return an [`Error`] if this fails.
> +    pub fn add_affected_planes<C>(&self, crtc: &C) -> Result
> +    where
> +        C: ModesettableCrtc + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: Both .as_raw() values are guaranteed to return a
> valid pointer
> +        to_result(unsafe {
> bindings::drm_atomic_add_affected_planes(self.as_raw(),
> crtc.as_raw()) })
> +    }
> +}
> +
> +/// A macro for declaring the repetitive take_all(), take_state(),
> etc. methods for atomic state
> +/// token types.
> +///
> +/// It is assumed that $token_name refers to a struct that contains
> two members:
> +///
> +/// - `state`: This should be the atomic state type to use
> +/// - The object in question. The name of this member is generated
> by converting $obj to lowercase.
> +///
> +/// The struct should have one lifetime ($lifetime_a) declared, and
> one meta-variable ($meta) which
> +/// should be bound to the Driver* trait for the given mode object.
> +macro_rules! impl_atomic_state_token_ops {
> +    (
> +        $token_name:ident,
> +        $state:ident,
> +        $obj:ident,
> +        use <$lifetime_a:lifetime, $meta:ident>
> +    ) => {
> +        kernel::macros::paste! {
> +            /// Create a new token.
> +            ///
> +            /// # Safety
> +            ///
> +            /// To use this function it must be known in the current
> context that:
> +            ///
> +            /// - The object has had its atomic states added to
> `state`.
> +            /// - No state mutator can possibly be taken out for the
> objects new state.
> +            pub(crate) unsafe fn new(
> +                [<$obj:lower>]: &$lifetime_a $obj<$meta>,
> +                state: &$lifetime_a $state<$meta::Driver>,
> +            ) -> Self {
> +                Self { [<$obj:lower>], state }
> +            }
> +
> +            #[doc = concat!("Get the [`", stringify!($obj), "`]
> associated with this",
> +                            " [`", stringify!($token_name), "`].")]
> +            pub fn [<$obj:lower>](&self) -> &$lifetime_a $obj<$meta>
> {
> +                self.[<$obj:lower>]
> +            }
> +
> +            /// Exchange this token for a (atomic_state, old_state,
> new_state) tuple.
> +            pub fn take_all(self) -> (
> +                &$lifetime_a $state<$meta::Driver>,
> +                &$lifetime_a [<$obj State>]<$meta::State>,
> +                [<$obj StateMutator>]<$lifetime_a, [<$obj
> State>]<$meta::State>>,
> +            ) {
> +                let (old_state, new_state) = (
> +                    self.state.[<get_old_ $obj:lower
> _state>](self.[<$obj:lower>]),
> +                    self.state.[<get_new_ $obj:lower
> _state>](self.[<$obj:lower>]),
> +                );
> +
> +                // SAFETY:
> +                // - Both the old and new object state are present
> in `state` via our type
> +                //   invariants.
> +                // - The new state is guaranteed to have no mutators
> taken out via our type
> +                //   invariants.
> +                let (old_state, new_state) = unsafe {
> +                    (old_state.unwrap_unchecked(),
> new_state.unwrap_unchecked())
> +                };
> +
> +                (self.state, old_state, new_state)
> +            }
> +
> +            #[doc = concat!("Exchange this token for the old [`",
> stringify!($obj), "State`].")]
> +            pub fn take_old_state(self) -> &$lifetime_a [<$obj
> State>]<$meta::State> {
> +                let old = self.state.[<get_old_ $obj:lower
> _state>](self.[<$obj:lower>]);
> +
> +                // SAFETY: The old state is guaranteed to be present
> in `state` via our type
> +                // invariants.
> +                unsafe { old.unwrap_unchecked() }
> +            }
> +
> +            #[doc = concat!("Exchange this token for the new [`",
> stringify!($obj), "State`].")]
> +            pub fn take_new_state(
> +                self
> +            ) -> [<$obj StateMutator>]<$lifetime_a, [<$obj
> State>]<$meta::State>> {
> +                let new = self.state.[<get_new_ $obj:lower
> _state>](self.[<$obj:lower>]);
> +
> +                // SAFETY:
> +                // - The new state is guaranteed to be present in
> our `state` via our type
> +                //   invariants.
> +                // - The new state is guaranteed not to have any
> mutators taken out for it via our
> +                //   type invariants.
> +                unsafe { new.unwrap_unchecked() }
> +            }
> +
> +            #[doc = concat!("Exchange this token for both the old
> and new [`",
> +                            stringify!($obj), "State`].")]
> +            pub fn take_old_new_state(self) -> (
> +                &$lifetime_a [<$obj State>]<$meta::State>,
> +                [<$obj StateMutator>]<$lifetime_a, [<$obj
> State>]<$meta::State>>,
> +            ) {
> +                let (old_state, new_state) = (
> +                    self.state.[<get_old_ $obj:lower
> _state>](self.[<$obj:lower>]),
> +                    self.state.[<get_new_ $obj:lower
> _state>](self.[<$obj:lower>]),
> +                );
> +
> +                // SAFETY:
> +                // - Both the old and new object state are present
> in `state` via our type
> +                //   invariants.
> +                // - The new state is guaranteed to have no mutators
> taken out via our type
> +                //   invariants.
> +                let (old_state, new_state) = unsafe {
> +                    (old_state.unwrap_unchecked(),
> new_state.unwrap_unchecked())
> +                };
> +
> +                (old_state, new_state)
> +            }
> +
> +            #[doc = concat!("Exchange this token for both the [`",
> stringify!($state),
> +                            "`] and the old [`", stringify!($obj),
> "State`].")]
> +            pub fn take_state_old_state(self) -> (
> +                &$lifetime_a $state<$meta::Driver>,
> +                &$lifetime_a [<$obj State>]<$meta::State>,
> +            ) {
> +                let old = self.state.[<get_old_ $obj:lower
> _state>](self.[<$obj:lower>]);
> +
> +                // SAFETY: The old state is guaranteed to be present
> in `state` via our type
> +                // invariants.
> +                (self.state, unsafe { old.unwrap_unchecked() })
> +            }
> +
> +            #[doc = concat!("Exchange this token for both the [`",
> stringify!($state),
> +                            "`] and the new [`", stringify!($obj),
> "State`].")]
> +            pub fn take_state_new_state(self) -> (
> +                &$lifetime_a $state<$meta::Driver>,
> +                [<$obj StateMutator>]<$lifetime_a, [<$obj
> State>]<$meta::State>>,
> +            ) {
> +                let new = self.state.[<get_new_ $obj:lower
> _state>](self.[<$obj:lower>]);
> +
> +                // SAFETY:
> +                // - The new state is guaranteed to be present in
> `state` via our type
> +                //   invariants.
> +                // - The new state is guaranteed to have no mutators
> taken out via our type
> +                //   invariants.
> +                (self.state, unsafe { new.unwrap_unchecked() })
> +            }
> +        }
> +
> +        #[doc = concat!("Exchange this token for the [`",
> stringify!($state), "`].")]
> +        pub fn take_state(self) -> &$lifetime_a
> $state<$meta::Driver> {
> +            self.state
> +        }
> +    };
> +}
> +
> +pub(crate) use impl_atomic_state_token_ops;
> +
> +/// A token proving that no modesets for a commit have completed.
> +///
> +/// This token is proof that no commits have yet completed, and is
> provided as an argument to
> +/// [`KmsDriver::atomic_commit_tail`]. This may be used with
> +/// [`AtomicCommitTail::commit_modeset_disables`].
> +pub struct ModesetsReadyToken<'a>(PhantomData<&'a ()>);
> +
> +/// A token proving that modeset disables for a commit have
> completed.
> +///
> +/// This token is proof that an implementor's
> [`KmsDriver::atomic_commit_tail`] phase has finished
> +/// committing any operations which disable mode objects. It is
> returned by
> +/// [`AtomicCommitTail::commit_modeset_disables`], and can be used
> with
> +/// [`AtomicCommitTail::commit_modeset_enables`] to acquire a
> [`EnablesCommittedToken`].
> +pub struct DisablesCommittedToken<'a>(PhantomData<&'a ()>);
> +
> +/// A token proving that modeset enables for a commit have
> completed.
> +///
> +/// This token is proof that an implementor's
> [`KmsDriver::atomic_commit_tail`] phase has finished
> +/// committing any operations which enable mode objects. It is
> returned by
> +/// [`AtomicCommitTail::commit_modeset_enables`].
> +pub struct EnablesCommittedToken<'a>(PhantomData<&'a ()>);
> +
> +/// A token proving that no plane updates for a commit have
> completed.
> +///
> +/// This token is proof that no plane updates have yet been
> completed within an implementor's
> +/// [`KmsDriver::atomic_commit_tail`] implementation, and that we
> are ready to begin updating planes. It
> +/// is provided as an argument to [`KmsDriver::atomic_commit_tail`].
> +pub struct PlaneUpdatesReadyToken<'a>(PhantomData<&'a ()>);
> +
> +/// A token proving that all plane updates for a commit have
> completed.
> +///
> +/// This token is proof that all plane updates within an
> implementor's [`KmsDriver::atomic_commit_tail`]
> +/// implementation have completed. It is returned by
> [`AtomicCommitTail::commit_planes`].
> +pub struct PlaneUpdatesCommittedToken<'a>(PhantomData<&'a ()>);
> +
> +/// An [`AtomicState`] interface that allows a driver to control the
> [`atomic_commit_tail`]
> +/// callback.
> +///
> +/// This object is provided as an argument to
> [`KmsDriver::atomic_commit_tail`], and represents an atomic
> +/// state within the commit tail phase which is still in the process
> of being committed to hardware.
> +/// It may be used to control the order in which the commit process
> happens.
> +///
> +/// # Invariants
> +///
> +/// Same as [`AtomicState`].
> +///
> +/// [`atomic_commit_tail`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +pub struct AtomicCommitTail<'a, T: KmsDriver>(&'a AtomicState<T>);
> +
> +impl<'a, T: KmsDriver> AtomicCommitTail<'a, T> {
> +    /// Commit modesets which would disable outputs.
> +    ///
> +    /// This function commits any modesets which would shut down
> outputs, along with preparing them
> +    /// for a new mode (if needed).
> +    ///
> +    /// Since it is physically impossible to disable an output
> multiple times, and since it is
> +    /// logically unsound to disable an output within an atomic
> commit after the output was enabled
> +    /// in the same commit - this function requires a
> [`ModesetsReadyToken`] to consume and returns
> +    /// a [`DisablesCommittedToken`].
> +    ///
> +    /// If compatibility with legacy CRTC helpers is desired, this
> +    /// should be called before [`commit_planes`] which is what the
> default commit function does.
> +    /// But drivers with different needs can group the modeset
> commits tgether and do the plane
> +    /// commits at the end. This is useful for drivers doing runtime
> PM since then plane updates
> +    /// only happen when the CRTC is actually enabled.
> +    ///
> +    /// [`commit_planes`]: AtomicCommitTail::commit_planes
> +    #[inline]
> +    #[must_use]
> +    pub fn commit_modeset_disables<'b>(
> +        &mut self,
> +        _token: ModesetsReadyToken<'_>,
> +    ) -> DisablesCommittedToken<'b> {
> +        // SAFETY: Both `as_raw()` calls are guaranteed to return
> valid pointers
> +        unsafe {
> +            bindings::drm_atomic_helper_commit_modeset_disables(
> +                self.0.drm_dev().as_raw(),
> +                self.0.as_raw(),
> +            )
> +        }
> +
> +        DisablesCommittedToken(PhantomData)
> +    }
> +
> +    /// Commit all plane updates.
> +    ///
> +    /// This function performs all plane updates for the given
> [`AtomicCommitTail`]. Since it is
> +    /// logically unsound to perform the same plane update more then
> once in a given atomic commit,
> +    /// this function requires a [`PlaneUpdatesReadyToken`] to
> consume and returns a
> +    /// [`PlaneUpdatesCommittedToken`] to prove that plane updates
> for the state have completed.
> +    #[inline]
> +    #[must_use]
> +    pub fn commit_planes<'b>(
> +        &mut self,
> +        _token: PlaneUpdatesReadyToken<'_>,
> +        flags: PlaneCommitFlags,
> +    ) -> PlaneUpdatesCommittedToken<'b> {
> +        // SAFETY: Both `as_raw()` calls are guaranteed to return
> valid pointers
> +        unsafe {
> +            bindings::drm_atomic_helper_commit_planes(
> +                self.0.drm_dev().as_raw(),
> +                self.0.as_raw(),
> +                flags.into(),
> +            )
> +        }
> +
> +        PlaneUpdatesCommittedToken(PhantomData)
> +    }
> +
> +    /// Commit modesets which would enable outputs.
> +    ///
> +    /// This function commits any modesets in the given
> [`AtomicCommitTail`] which would enable
> +    /// outputs, along with preparing them for their new modes (if
> needed).
> +    ///
> +    /// Since it is logically unsound to enable an output before any
> disabling modesets within the
> +    /// same atomic commit have been performed, and physically
> impossible to enable the same output
> +    /// multiple times - this function requires a
> [`DisablesCommittedToken`] to consume and returns
> +    /// a [`EnablesCommittedToken`] which may be used as proof that
> all modesets in the state have
> +    /// been completed.
> +    #[inline]
> +    #[must_use]
> +    pub fn commit_modeset_enables<'b>(
> +        &mut self,
> +        _token: DisablesCommittedToken<'_>,
> +    ) -> EnablesCommittedToken<'b> {
> +        // SAFETY: Both `as_raw()` calls are guaranteed to return
> valid pointers
> +        unsafe {
> +            bindings::drm_atomic_helper_commit_modeset_enables(
> +                self.0.drm_dev().as_raw(),
> +                self.0.as_raw(),
> +            )
> +        }
> +
> +        EnablesCommittedToken(PhantomData)
> +    }
> +
> +    /// Fake vblank events if needed.
> +    ///
> +    /// Note that this is still relevant to drivers which don't
> implement [`VblankSupport`] for any
> +    /// of their CRTCs.
> +    ///
> +    /// TODO: more doc
> +    ///
> +    /// [`VblankSupport`]: super::vblank::VblankSupport
> +    pub fn fake_vblank(&mut self) {
> +        // SAFETY: `as_raw()` is guaranteed to always return a valid
> pointer
> +        unsafe {
> bindings::drm_atomic_helper_fake_vblank(self.0.as_raw()) }
> +    }
> +
> +    /// Signal completion of the hardware commit step.
> +    ///
> +    /// This swaps the atomic state into the relevant atomic state
> pointers and marks the hardware
> +    /// commit step as completed. Since this step can only happen
> after all plane updates and
> +    /// modesets within an [`AtomicCommitTail`] have been completed,
> it requires both a
> +    /// [`EnablesCommittedToken`] and a
> [`PlaneUpdatesCommittedToken`] to consume. After this
> +    /// function is called, the caller no longer has exclusive
> access to the underlying atomic
> +    /// state. As such, this function consumes the
> [`AtomicCommitTail`] object and returns a
> +    /// [`CommittedAtomicState`] accessor for performing post-hw
> commit tasks.
> +    pub fn commit_hw_done<'b>(
> +        self,
> +        _modeset_token: EnablesCommittedToken<'_>,
> +        _plane_updates_token: PlaneUpdatesCommittedToken<'_>,
> +    ) -> CommittedAtomicState<'b, T>
> +    where
> +        'a: 'b,
> +    {
> +        // SAFETY: we consume the `AtomicCommitTail` object, making
> it impossible for the user to
> +        // mutate the state after this function has been called -
> which upholds the safety
> +        // requirements of the C API allowing us to safely call this
> function
> +        unsafe {
> bindings::drm_atomic_helper_commit_hw_done(self.0.as_raw()) };
> +
> +        CommittedAtomicState(self.0)
> +    }
> +}
> +
> +// The actual raw C callback for custom atomic commit tail
> implementations
> +pub(crate) unsafe extern "C" fn commit_tail_callback<T: KmsDriver>(
> +    state: *mut bindings::drm_atomic_state,
> +) {
> +    // SAFETY:
> +    // - We're guaranteed by DRM that `state` always points to a
> valid instance of
> +    //   `bindings::drm_atomic_state`
> +    // - This conversion is safe via the type invariants
> +    let state = unsafe { AtomicState::from_raw(state.cast_const())
> };
> +
> +    T::atomic_commit_tail(
> +        AtomicCommitTail(state),
> +        ModesetsReadyToken(PhantomData),
> +        PlaneUpdatesReadyToken(PhantomData),
> +    );
> +}
> +
> +/// An [`AtomicState`] which was just committed with
> [`AtomicCommitTail::commit_hw_done`].
> +///
> +/// This object represents an [`AtomicState`] which has been fully
> committed to hardware, and as
> +/// such may no longer be mutated as it is visible to userspace. It
> may be used to control what
> +/// happens immediately after an atomic commit finishes within the
> [`atomic_commit_tail`] callback.
> +///
> +/// Since acquiring this object means that all modesetting locks
> have been dropped, a non-blocking
> +/// commit could happen at the same time an [`atomic_commit_tail`]
> implementer has access to this
> +/// object. Thus, it cannot be assumed that this object represents
> the current hardware state - and
> +/// instead only represents the final result of the
> [`AtomicCommitTail`] that was just committed.
> +///
> +/// # Invariants
> +///
> +/// It may be assumed that [`drm_atomic_helper_commit_hw_done`] has
> been called as long as this type
> +/// exists.
> +///
> +/// [`atomic_commit_tail`]: KmsDriver::atomic_commit_tail
> +/// [`drm_atomic_helper_commit_hw_done`]:
> srctree/include/drm/drm_atomic_helper.h
> +pub struct CommittedAtomicState<'a, T: KmsDriver>(&'a
> AtomicState<T>);
> +
> +impl<'a, T: KmsDriver> CommittedAtomicState<'a, T> {
> +    /// Wait for page flips on this state to complete
> +    pub fn wait_for_flip_done(&self) {
> +        // SAFETY: `drm_atomic_helper_commit_hw_done` has been
> called via our invariants
> +        unsafe {
> +            bindings::drm_atomic_helper_wait_for_flip_done(
> +                self.0.drm_dev().as_raw(),
> +                self.0.as_raw(),
> +            )
> +        }
> +    }
> +}
> +
> +impl<'a, T: KmsDriver> Drop for CommittedAtomicState<'a, T> {
> +    fn drop(&mut self) {
> +        // SAFETY:
> +        // * This interface represents the last atomic state
> accessor which could be affected as a
> +        //   result of resources from an atomic commit being cleaned
> up.
> +        unsafe {
> +           
> bindings::drm_atomic_helper_cleanup_planes(self.0.drm_dev().as_raw(),
> self.0.as_raw())
> +        }
> +    }
> +}
> +
> +/// An enumator representing a single flag in [`PlaneCommitFlags`].
> +///
> +/// This is a non-exhaustive list, as the C side could add more
> later.
> +#[derive(Copy, Clone, PartialEq, Eq)]
> +#[repr(u32)]
> +#[non_exhaustive]
> +pub enum PlaneCommitFlag {
> +    /// Don't notify applications of plane updates for newly-
> disabled planes. Drivers are encouraged
> +    /// to set this flag by default, as otherwise they need to
> ignore plane updates for disabled
> +    /// planes by hand.
> +    ActiveOnly = (1 << 0),
> +    /// Tell the DRM core that the display hardware requires that a
> [`Crtc`]'s planes must be
> +    /// disabled when the [`Crtc`] is disabled. When not specified,
> +    /// [`AtomicCommitTail::commit_planes`] will skip the atomic
> disable callbacks for a plane if
> +    /// the [`Crtc`] in the old [`PlaneState`] needs a modesetting
> operation. It is still up to the
> +    /// driver to disable said planes in their
> [`DriverCrtc::atomic_disable`] callback.
> +    NoDisableAfterModeset = (1 << 1),
> +}
> +
> +impl BitOr for PlaneCommitFlag {
> +    type Output = PlaneCommitFlags;
> +
> +    fn bitor(self, rhs: Self) -> Self::Output {
> +        PlaneCommitFlags(self as u32 | rhs as u32)
> +    }
> +}
> +
> +impl BitOr<PlaneCommitFlags> for PlaneCommitFlag {
> +    type Output = PlaneCommitFlags;
> +
> +    fn bitor(self, rhs: PlaneCommitFlags) -> Self::Output {
> +        PlaneCommitFlags(self as u32 | rhs.0)
> +    }
> +}
> +
> +/// A bitmask for controlling the behavior of
> [`AtomicCommitTail::commit_planes`].
> +///
> +/// This corresponds to the `DRM_PLANE_COMMIT_*` flags on the C
> side. Note that this bitmask does
> +/// not discard unknown values in order to ensure that adding new
> flags on the C side of things does
> +/// not break anything in the future.
> +#[derive(Copy, Clone, Default, PartialEq, Eq)]
> +pub struct PlaneCommitFlags(u32);
> +
> +impl From<PlaneCommitFlag> for PlaneCommitFlags {
> +    fn from(value: PlaneCommitFlag) -> Self {
> +        Self(value as u32)
> +    }
> +}
> +
> +impl From<PlaneCommitFlags> for u32 {
> +    fn from(value: PlaneCommitFlags) -> Self {
> +        value.0
> +    }
> +}
> +
> +impl BitOr for PlaneCommitFlags {
> +    type Output = Self;
> +
> +    fn bitor(self, rhs: Self) -> Self::Output {
> +        Self(self.0 | rhs.0)
> +    }
> +}
> +
> +impl BitOrAssign for PlaneCommitFlags {
> +    fn bitor_assign(&mut self, rhs: Self) {
> +        *self = *self | rhs
> +    }
> +}
> +
> +impl BitAnd for PlaneCommitFlags {
> +    type Output = PlaneCommitFlags;
> +
> +    fn bitand(self, rhs: Self) -> Self::Output {
> +        Self(self.0 & rhs.0)
> +    }
> +}
> +
> +impl BitAndAssign for PlaneCommitFlags {
> +    fn bitand_assign(&mut self, rhs: Self) {
> +        *self = *self & rhs
> +    }
> +}
> +
> +impl BitOr<PlaneCommitFlag> for PlaneCommitFlags {
> +    type Output = Self;
> +
> +    fn bitor(self, rhs: PlaneCommitFlag) -> Self::Output {
> +        self | Self::from(rhs)
> +    }
> +}
> +
> +impl BitOrAssign<PlaneCommitFlag> for PlaneCommitFlags {
> +    fn bitor_assign(&mut self, rhs: PlaneCommitFlag) {
> +        *self = *self | rhs
> +    }
> +}
> +
> +impl BitAnd<PlaneCommitFlag> for PlaneCommitFlags {
> +    type Output = PlaneCommitFlags;
> +
> +    fn bitand(self, rhs: PlaneCommitFlag) -> Self::Output {
> +        self & Self::from(rhs)
> +    }
> +}
> +
> +impl BitAndAssign<PlaneCommitFlag> for PlaneCommitFlags {
> +    fn bitand_assign(&mut self, rhs: PlaneCommitFlag) {
> +        *self = *self & rhs
> +    }
> +}
> +
> +impl PlaneCommitFlags {
> +    /// Create a new bitmask.
> +    pub fn new() -> Self {
> +        Self::default()
> +    }
> +
> +    /// Check if the bitmask has the given commit flag set.
> +    pub fn has(&self, flag: PlaneCommitFlag) -> bool {
> +        *self & flag == flag.into()
> +    }
> +}
> diff --git a/rust/kernel/drm/kms/connector.rs
> b/rust/kernel/drm/kms/connector.rs
> new file mode 100644
> index 000000000000..05ec64cf6fa2
> --- /dev/null
> +++ b/rust/kernel/drm/kms/connector.rs
> @@ -0,0 +1,997 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! DRM display connectors.
> +//!
> +//! C header:
> [`include/drm/drm_connector.h`](srctree/include/drm/drm_connector.h)
> +
> +use super::{
> +    atomic::*, encoder::*, KmsDriver, ModeConfigGuard, ModeObject,
> ModeObjectVtable, Sealed
> +};
> +use crate::{
> +    alloc::KBox,
> +    bindings,
> +    drm::{device::Device, kms::{NewKmsDevice, Probing}},
> +    error::to_result,
> +    prelude::*,
> +    types::{NotThreadSafe, Opaque},
> +};
> +use core::{
> +    cell::Cell,
> +    marker::*,
> +    mem::{self, ManuallyDrop},
> +    ops::*,
> +    ptr::{null_mut, NonNull},
> +    stringify,
> +};
> +use macros::paste;
> +
> +/// A macro for generating our type ID enumerator.
> +macro_rules! declare_conn_types {
> +    ($( $oldname:ident as $newname:ident ),+) => {
> +        /// An enumerator for all possible [`Connector`] type IDs.
> +        #[repr(i32)]
> +        #[non_exhaustive]
> +        #[derive(Copy, Clone, PartialEq, Eq)]
> +        pub enum Type {
> +            // Note: bindgen defaults the macro values to u32 and
> not i32, but DRM takes them as an
> +            // i32 - so just do the conversion here
> +            $(
> +                #[doc = concat!("The connector type ID for a ",
> stringify!($newname), " connector.")]
> +                $newname =
> paste!(crate::bindings::[<DRM_MODE_CONNECTOR_ $oldname>]) as i32
> +            ),+,
> +
> +            // 9PinDIN is special because of the 9, making it an
> invalid ident. Just define it here
> +            // manually since it's the only one
> +
> +            /// The connector type ID for a 9PinDIN connector.
> +            _9PinDin = crate::bindings::DRM_MODE_CONNECTOR_9PinDIN
> as i32
> +        }
> +    };
> +}
> +
> +declare_conn_types! {
> +    Unknown     as Unknown,
> +    Composite   as Composite,
> +    Component   as Component,
> +    DisplayPort as DisplayPort,
> +    VGA         as Vga,
> +    DVII        as DviI,
> +    DVID        as DviD,
> +    DVIA        as DviA,
> +    SVIDEO      as SVideo,
> +    LVDS        as Lvds,
> +    HDMIA       as HdmiA,
> +    HDMIB       as HdmiB,
> +    TV          as Tv,
> +    eDP         as Edp,
> +    VIRTUAL     as Virtual,
> +    DSI         as Dsi,
> +    DPI         as Dpi,
> +    WRITEBACK   as Writeback,
> +    SPI         as Spi,
> +    USB         as Usb
> +}
> +
> +/// The main trait for implementing the [`struct drm_connector`] API
> for [`Connector`].
> +///
> +/// Any KMS driver should have at least one implementation of this
> type, which allows them to create
> +/// [`Connector`] objects. Additionally, a driver may store driver-
> private data within the type that
> +/// implements [`DriverConnector`] - and it will be made available
> when using a fully typed
> +/// [`Connector`] object.
> +///
> +/// # Invariants
> +///
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_connector`] pointers are contained within a
> [`Connector<Self>`].
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_connector_state`] pointers are contained within a
> +///   [`ConnectorState<Self::State>`].
> +///
> +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h
> +/// [`struct drm_connector_state`]:
> srctree/include/drm/drm_connector.h
> +#[vtable]
> +pub trait DriverConnector: Send + Sync + Sized {
> +    /// The generated C vtable for this [`DriverConnector`]
> implementation
> +    const OPS: &'static DriverConnectorOps = &DriverConnectorOps {
> +        funcs: bindings::drm_connector_funcs {
> +            dpms: None,
> +            atomic_get_property: None,
> +            atomic_set_property: None,
> +            early_unregister: None,
> +            late_register: None,
> +            set_property: None,
> +            reset: Some(connector_reset_callback::<Self::State>),
> +            atomic_print_state: None,
> +            atomic_destroy_state:
> Some(atomic_destroy_state_callback::<Self::State>),
> +            destroy: Some(connector_destroy_callback::<Self>),
> +            force: None,
> +            detect: None,
> +            fill_modes:
> Some(bindings::drm_helper_probe_single_connector_modes),
> +            debugfs_init: None,
> +            oob_hotplug_event: None,
> +            atomic_duplicate_state:
> Some(atomic_duplicate_state_callback::<Self::State>),
> +        },
> +        helper_funcs: bindings::drm_connector_helper_funcs {
> +            mode_valid: None,
> +            atomic_check: None,
> +            get_modes: Some(get_modes_callback::<Self>),
> +            detect_ctx: None,
> +            enable_hpd: None,
> +            disable_hpd: None,
> +            best_encoder: None,
> +            atomic_commit: None,
> +            mode_valid_ctx: None,
> +            atomic_best_encoder: None,
> +            prepare_writeback_job: None,
> +            cleanup_writeback_job: None,
> +        },
> +    };
> +
> +    /// The type to pass to the `args` field of
> [`UnregisteredConnector::new`].
> +    ///
> +    /// This type will be made available in in the `args` argument
> of [`Self::new`]. Drivers which
> +    /// don't need this can simply pass [`()`] here.
> +    type Args;
> +
> +    /// The parent [`KmsDriver`] implementation.
> +    type Driver: KmsDriver;
> +
> +    /// The [`DriverConnectorState`] implementation for this
> [`DriverConnector`].
> +    ///
> +    /// See [`DriverConnectorState`] for more info.
> +    type State: DriverConnectorState;
> +
> +    /// The constructor for creating a [`Connector`] using this
> [`DriverConnector`] implementation.
> +    ///
> +    /// Drivers may use this to instantiate their
> [`DriverConnector`] object.
> +    fn new(device: &Device<Self::Driver>, args: Self::Args) -> impl
> PinInit<Self, Error>;
> +
> +    /// Retrieve a list of available display modes for this
> [`Connector`].
> +    fn get_modes<'a>(
> +        connector: ConnectorGuard<'a, Self>,
> +        guard: &ModeConfigGuard<'a, Self::Driver>,
> +    ) -> i32;
> +}
> +
> +/// The generated C vtable for a [`DriverConnector`].
> +///
> +/// This type is created internally by DRM.
> +pub struct DriverConnectorOps {
> +    funcs: bindings::drm_connector_funcs,
> +    helper_funcs: bindings::drm_connector_helper_funcs,
> +}
> +
> +/// The main interface for a [`struct drm_connector`].
> +///
> +/// This type is the main interface for dealing with DRM connectors.
> In addition, it also allows
> +/// immutable access to whatever private data is contained within an
> implementor's
> +/// [`DriverConnector`] type.
> +///
> +/// # Invariants
> +///
> +/// - The DRM C API and our interface guarantees that only the user
> has mutable access to `state`,
> +///   up until [`drm_atomic_helper_commit_hw_done`] is called.
> Therefore, `connector` follows rust's
> +///   data aliasing rules and does not need to be behind an
> [`Opaque`] type.
> +/// - `connector` and `inner` are initialized for as long as this
> object is made available to users.
> +/// - The data layout of this structure begins with [`struct
> drm_connector`].
> +/// - The atomic state for this type can always be assumed to be of
> type
> +///   [`ConnectorState<T::State>`].
> +///
> +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h
> +/// [`drm_atomic_helper_commit_hw_done`]:
> srctree/include/drm/drm_atomic_helper.h
> +#[repr(C)]
> +#[pin_data]
> +pub struct Connector<T: DriverConnector> {
> +    connector: Opaque<bindings::drm_connector>,
> +    #[pin]
> +    inner: T,
> +    #[pin]
> +    _p: PhantomPinned,
> +}
> +
> +impl<T: DriverConnector> Sealed for Connector<T> {}
> +
> +impl<T: DriverConnector> Deref for Connector<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.inner
> +    }
> +}
> +
> +impl<T: DriverConnector> Connector<T> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <'a, D>(&'a OpaqueConnector<D>) -> &'a Self;
> +        use
> +            T as DriverConnector,
> +            D as KmsDriver<Connector = ...>
> +    }
> +
> +    /// Acquire a [`ConnectorGuard`] for this connector from a
> [`ModeConfigGuard`].
> +    ///
> +    /// This verifies using the provided reference that the given
> guard is actually for the same
> +    /// device as this connector's parent.
> +    ///
> +    /// # Panics
> +    ///
> +    /// Panics if `guard` is not a [`ModeConfigGuard`] for this
> connector's parent [`Device`].
> +    pub fn guard<'a>(&'a self, guard: &ModeConfigGuard<'a,
> T::Driver>) -> ConnectorGuard<'a, T> {
> +        guard.assert_owner(self.drm_dev());
> +        ConnectorGuard(self)
> +    }
> +}
> +
> +/// A trait implemented by any type that acts as a [`struct
> drm_connector`] interface.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// # Safety
> +///
> +/// [`as_raw()`] must always return a pointer to a valid initialized
> [`struct drm_connector`].
> +///
> +/// [`as_raw()`]: AsRawConnector::as_raw()
> +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h
> +pub unsafe trait AsRawConnector {
> +    /// Return the raw [`struct drm_connector`] for this DRM
> connector.
> +    ///
> +    /// Drivers should never use this directly
> +    ///
> +    /// [`struct drm_Connector`]:
> srctree/include/drm/drm_connector.h
> +    fn as_raw(&self) -> *mut bindings::drm_connector;
> +
> +    /// Convert a raw `bindings::drm_connector` pointer into an
> object of this type.
> +    ///
> +    /// # Safety
> +    ///
> +    /// Callers promise that `ptr` points to a valid instance of
> this type.
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a
> Self;
> +}
> +
> +/// A supertrait of [`AsRawConnector`] for [`struct drm_connector`]
> interfaces that can perform
> +/// modesets.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// # Safety
> +///
> +/// Any object implementing this trait must only be made directly
> available to the user after
> +/// [`create_objects`] has completed.
> +///
> +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h
> +/// [`create_objects`]: KmsDriver::create_objects
> +pub unsafe trait ModesettableConnector: AsRawConnector {
> +    /// The type that should be returned for a plane state acquired
> using this plane interface
> +    type State: FromRawConnectorState;
> +}
> +
> +// SAFETY: Our connector interfaces are guaranteed to be thread-safe
> +unsafe impl<T: DriverConnector> Send for Connector<T> {}
> +
> +// SAFETY: Our connector interfaces are guaranteed to be thread-safe
> +unsafe impl<T: DriverConnector> Sync for Connector<T> {}
> +
> +// SAFETY: We don't expose Connector<T> to users before `base` is
> initialized in ::new(), so
> +// `raw_mode_obj` always returns a valid pointer to a
> bindings::drm_mode_object.
> +unsafe impl<T: DriverConnector> ModeObject for Connector<T> {
> +    type Driver = T::Driver;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: The parent device for a DRM connector will never
> outlive the connector, and this
> +        // pointer is invariant through the lifetime of the
> connector
> +        unsafe { Device::from_raw((*self.as_raw()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose DRM connectors to users before
> `base` is initialized
> +        unsafe { &raw mut (*self.as_raw()).base }
> +    }
> +}
> +
> +// Connectors are refcounted objects.
> +super::impl_aref_for_mode_object! {
> +    impl<T: DriverConnector> for Connector<T>
> +}
> +
> +// SAFETY: `funcs` is initialized by DRM when the connector is
> allocated
> +unsafe impl<T: DriverConnector> ModeObjectVtable for Connector<T> {
> +    type Vtable = bindings::drm_connector_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        // SAFETY: `funcs` is initialized by DRM when the connector
> is allocated
> +        unsafe { *self.as_raw() }.funcs
> +    }
> +}
> +
> +// SAFETY:
> +// * Via our type variants our data layout starts with
> `drm_connector`
> +// * Since we don't expose `Connector` to users before it has been
> initialized, this and our data
> +//   layout ensure that `as_raw()` always returns a valid pointer to
> a `drm_connector`.
> +unsafe impl<T: DriverConnector> AsRawConnector for Connector<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_connector {
> +        self.connector.get()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a
> Self {
> +        // SAFETY: Our data layout starts with
> `bindings::drm_connector`
> +        unsafe { &*ptr.cast() }
> +    }
> +}
> +
> +// SAFETY: We only expose this object to users directly after
> KmsDriver::create_objects has been
> +// called.
> +unsafe impl<T: DriverConnector> ModesettableConnector for
> Connector<T> {
> +    type State = ConnectorState<T::State>;
> +}
> +
> +/// A [`Connector`] that has not yet been registered with userspace.
> +///
> +/// KMS registration is single-threaded, so this object is not
> thread-safe.
> +///
> +/// # Invariants
> +///
> +/// - This object can only exist before its respective KMS device
> has been registered.
> +/// - Otherwise, it inherits all invariants of [`Connector`] and has
> an identical data layout.
> +pub struct UnregisteredConnector<T: DriverConnector>(Connector<T>,
> NotThreadSafe);
> +
> +// SAFETY: We share the invariants of `Connector`
> +unsafe impl<T: DriverConnector> AsRawConnector for
> UnregisteredConnector<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_connector {
> +        self.0.as_raw()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a
> Self {
> +        // SAFETY: This is another from_raw() call, so this function
> shares the same safety contract
> +        let connector = unsafe { Connector::<T>::from_raw(ptr) };
> +
> +        // SAFETY: Our data layout is identical via our type
> invariants.
> +        unsafe { mem::transmute(connector) }
> +    }
> +}
> +
> +impl<T: DriverConnector> Deref for UnregisteredConnector<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.0.inner
> +    }
> +}
> +
> +impl<T: DriverConnector> UnregisteredConnector<T> {
> +    /// Construct a new [`UnregisteredConnector`].
> +    ///
> +    /// A driver may use this to create new
> [`UnregisteredConnector`] objects.
> +    ///
> +    /// [`KmsDriver::create_objects`]:
> kernel::drm::kms::KmsDriver::create_objects
> +    pub fn new<'a>(
> +        dev: &'a NewKmsDevice<'a, T::Driver, Probing>,
> +        type_: Type,
> +        args: T::Args,
> +    ) -> Result<&'a Self> {
> +        let new: Pin<KBox<Connector<T>>> = KBox::try_pin_init(
> +            try_pin_init!(Connector {
> +                connector: Opaque::new(bindings::drm_connector {
> +                    helper_private: &T::OPS.helper_funcs,
> +                    ..Default::default()
> +                }),
> +                inner <- T::new(dev, args),
> +                _p: PhantomPinned,
> +            }),
> +            GFP_KERNEL,
> +        )?;
> +
> +        // SAFETY:
> +        // - `dev` will hold a reference to the new connector, and
> thus outlives us.
> +        // - We just allocated `new` above
> +        // - `new` starts with `drm_connector` via its type
> invariants.
> +        to_result(unsafe {
> +            bindings::drm_connector_init(dev.as_raw(), new.as_raw(),
> &T::OPS.funcs, type_ as i32)
> +        })?;
> +
> +        // SAFETY: We don't move anything
> +        let this = unsafe { Pin::into_inner_unchecked(new) };
> +
> +        // We'll re-assemble the box in connector_destroy_callback()
> +        let this = KBox::into_raw(this);
> +
> +        // UnregisteredConnector has an equivalent data layout
> +        let this: *mut Self = this.cast();
> +
> +        // SAFETY: We just allocated the connector above, so this
> pointer must be valid
> +        Ok(unsafe { &*this })
> +    }
> +
> +    /// Attach an encoder to this [`Connector`].
> +    #[must_use]
> +    pub fn attach_encoder(&self, encoder: &impl AsRawEncoder) ->
> Result {
> +        // SAFETY:
> +        // - Both as_raw() calls are guaranteed to return a valid
> pointer
> +        // - We're guaranteed this connector is not registered via
> our type invariants, thus this
> +        //   function is safe to call
> +        to_result(unsafe {
> +            bindings::drm_connector_attach_encoder(self.as_raw(),
> encoder.as_raw())
> +        })
> +    }
> +}
> +
> +/// Common methods available on any type which implements
> [`AsRawConnector`].
> +///
> +/// This is implemented internally by DRM, and provides many of the
> basic methods for working with
> +/// connectors.
> +pub trait RawConnector: AsRawConnector {
> +    /// Return the index of this DRM connector
> +    #[inline]
> +    fn index(&self) -> u32 {
> +        // SAFETY: The index is initialized by the time we expose
> DRM connector objects to users,
> +        // and is invariant throughout the lifetime of the connector
> +        unsafe { (*self.as_raw()).index }
> +    }
> +
> +    /// Return the bitmask derived from this DRM connector's index
> +    #[inline]
> +    fn mask(&self) -> u32 {
> +        1 << self.index()
> +    }
> +}
> +impl<T: AsRawConnector> RawConnector for T {}
> +
> +unsafe extern "C" fn connector_destroy_callback<T: DriverConnector>(
> +    connector: *mut bindings::drm_connector,
> +) {
> +    // SAFETY: DRM guarantees that `connector` points to a valid
> initialized `drm_connector`.
> +    unsafe {
> +        bindings::drm_connector_unregister(connector);
> +        bindings::drm_connector_cleanup(connector);
> +    };
> +
> +    // SAFETY:
> +    // - We originally created the connector in a `Box`
> +    // - We are guaranteed to hold the last remaining reference to
> this connector
> +    // - This cast is safe via `DriverConnector`s type invariants.
> +    drop(unsafe { KBox::from_raw(connector as *mut Connector<T>) });
> +}
> +
> +unsafe extern "C" fn get_modes_callback<T: DriverConnector>(
> +    connector: *mut bindings::drm_connector,
> +) -> core::ffi::c_int {
> +    // SAFETY: This is safe via `DriverConnector`s type invariants.
> +    let connector = unsafe { Connector::<T>::from_raw(connector) };
> +
> +    // SAFETY: This FFI callback is only called while
> `mode_config.lock` is held
> +    // We use ManuallyDrop here to prevent the lock from being
> released after the callback
> +    // completes, as that should be handled by DRM.
> +    let guard = ManuallyDrop::new(unsafe {
> ModeConfigGuard::new(connector.drm_dev()) });
> +
> +    T::get_modes(connector.guard(&guard), &guard)
> +}
> +
> +/// A [`struct drm_connector`] without a known [`DriverConnector`]
> implementation.
> +///
> +/// This is mainly for situations where our bindings can't infer the
> [`DriverConnector`]
> +/// implementation for a [`struct drm_connector`] automatically. It
> is identical to [`Connector`],
> +/// except that it does not provide access to the driver's private
> data.
> +///
> +/// # Invariants
> +///
> +/// - `connector` is initialized for as long as this object is
> exposed to users.
> +/// - The data layout of this type is equivalent to [`struct
> drm_connector`].
> +///
> +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h
> +#[repr(transparent)]
> +pub struct OpaqueConnector<T: KmsDriver> {
> +    connector: Opaque<bindings::drm_connector>,
> +    _p: PhantomData<T>,
> +}
> +
> +impl<T: KmsDriver> Sealed for OpaqueConnector<T> {}
> +
> +// SAFETY:
> +// - Via our type variants our data layout starts is identical to
> `drm_connector`
> +// - Since we don't expose `OpaqueConnector` to users before it has
> been initialized, this and our
> +//   data layout ensure that `as_raw()` always returns a valid
> pointer to a `drm_connector`.
> +unsafe impl<T: KmsDriver> AsRawConnector for OpaqueConnector<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_connector {
> +        self.connector.get()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a
> Self {
> +        // SAFETY: Our data layout is identical to
> `bindings::drm_connector`
> +        unsafe { &*ptr.cast() }
> +    }
> +}
> +
> +// SAFETY: We only expose this object to users directly after
> KmsDriver::create_objects has been
> +// called.
> +unsafe impl<T: KmsDriver> ModesettableConnector for
> OpaqueConnector<T> {
> +    type State = OpaqueConnectorState<T>;
> +}
> +
> +// SAFETY: We don't expose OpaqueConnector<T> to users before `base`
> is initialized in
> +// Connector::new(), so `raw_mode_obj` always returns a valid
> pointer to a bindings::drm_mode_object.
> +unsafe impl<T: KmsDriver> ModeObject for OpaqueConnector<T> {
> +    type Driver = T;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: The parent device for a DRM connector will never
> outlive the connector, and this
> +        // pointer is invariant through the lifetime of the
> connector
> +        unsafe { Device::from_raw((*self.as_raw()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose DRM connectors to users before
> `base` is initialized
> +        unsafe { &mut (*self.as_raw()).base }
> +    }
> +}
> +
> +super::impl_aref_for_mode_object! {
> +    impl<T: KmsDriver> for OpaqueConnector<T>
> +}
> +
> +// SAFETY: `funcs` is initialized by DRM when the connector is
> allocated
> +unsafe impl<T: KmsDriver> ModeObjectVtable for OpaqueConnector<T> {
> +    type Vtable = bindings::drm_connector_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        // SAFETY: `funcs` is initialized by DRM when the connector
> is allocated
> +        unsafe { *self.as_raw() }.funcs
> +    }
> +}
> +
> +// SAFETY: Our connector interfaces are guaranteed to be thread-safe
> +unsafe impl<T: KmsDriver> Send for OpaqueConnector<T> {}
> +unsafe impl<T: KmsDriver> Sync for OpaqueConnector<T> {}
> +
> +/// A privileged [`Connector`] obtained while holding a
> [`ModeConfigGuard`].
> +///
> +/// This provides access to various methods for [`Connector`] that
> must happen under lock, such as
> +/// setting resolution preferences and adding display modes.
> +///
> +/// # Invariants
> +///
> +/// Shares the invariants of [`ModeConfigGuard`].
> +#[derive(Copy, Clone)]
> +pub struct ConnectorGuard<'a, T: DriverConnector>(&'a Connector<T>);
> +
> +impl<T: DriverConnector> Deref for ConnectorGuard<'_, T> {
> +    type Target = Connector<T>;
> +
> +    fn deref(&self) -> &Self::Target {
> +        self.0
> +    }
> +}
> +
> +impl<'a, T: DriverConnector> ConnectorGuard<'a, T> {
> +    /// Add modes for a [`ConnectorGuard`] without an EDID.
> +    ///
> +    /// Add the specified modes to the connector's mode list up to
> the given maximum resultion.
> +    /// Returns how many modes were added.
> +    pub fn add_modes_noedid(&self, (max_h, max_v): (u32, u32)) ->
> i32 {
> +        // SAFETY: We hold the locks required to call this via our
> type invariants.
> +        unsafe { bindings::drm_add_modes_noedid(self.as_raw(),
> max_h, max_v) }
> +    }
> +
> +    /// Set the preferred display mode for the underlying
> [`Connector`].
> +    pub fn set_preferred_mode(&self, (h_pref, w_pref): (u32, u32)) {
> +        // SAFETY: We hold the locks required to call this via our
> type invariants.
> +        unsafe { bindings::drm_set_preferred_mode(self.as_raw(),
> h_pref, w_pref) }
> +    }
> +}
> +
> +/// A trait implemented by any type which can produce a reference to
> a
> +/// [`struct drm_connector_state`].
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// [`struct drm_connector_state`]:
> srctree/include/drm/drm_connector.h
> +pub trait AsRawConnectorState: private::AsRawConnectorState {
> +    /// The type that represents this connector state's DRM
> connector.
> +    type Connector: AsRawConnector;
> +}
> +
> +pub(super) mod private {
> +    use super::*;
> +
> +    /// Trait for retrieving references to the base connector state
> contained within any connector
> +    /// state compatible type
> +    #[allow(unreachable_pub)]
> +    pub trait AsRawConnectorState {
> +        /// Return an immutable reference to the raw connector
> state.
> +        fn as_raw(&self) -> &bindings::drm_connector_state;
> +
> +        /// Get a mutable reference to the raw [`struct
> drm_connector_state`] contained within this
> +        /// type.
> +        ///
> +        ///
> +        /// # Safety
> +        ///
> +        /// The caller promises this mutable reference will not be
> used to modify any contents of
> +        /// [`struct drm_connector_state`] which DRM would consider
> to be static - like the
> +        /// backpointer to the DRM connector that owns this state.
> This also means the mutable
> +        /// reference should never be exposed outside of this crate.
> +        ///
> +        /// [`struct drm_connector_state`]:
> srctree/include/drm/drm_connector.h
> +        unsafe fn as_raw_mut(&mut self) -> &mut
> bindings::drm_connector_state;
> +    }
> +}
> +
> +pub(super) use private::AsRawConnectorState as
> AsRawConnectorStatePrivate;
> +
> +/// A trait implemented for any type which can be constructed
> directly from a
> +/// [`struct drm_connector_state`] pointer.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// [`struct drm_connector_state`]:
> srctree/include/drm/drm_connector.h
> +pub trait FromRawConnectorState: AsRawConnectorState {
> +    /// Get an immutable reference to this type from the given raw
> [`struct drm_connector_state`]
> +    /// pointer.
> +    ///
> +    /// # Safety
> +    ///
> +    /// - The caller guarantees `ptr` is contained within a valid
> instance of `Self`.
> +    /// - The caller guarantees that `ptr` cannot not be modified
> for the lifetime of `'a`.
> +    ///
> +    /// [`struct drm_connector_state`]:
> srctree/include/drm/drm_connector.h
> +    unsafe fn from_raw<'a>(ptr: *const
> bindings::drm_connector_state) -> &'a Self;
> +
> +    /// Get a mutable reference to this type from the given raw
> [`struct drm_connector_state`]
> +    /// pointer.
> +    ///
> +    /// # Safety
> +    ///
> +    /// - The caller guarantees that `ptr` is contained within a
> valid instance of `Self`.
> +    /// - The caller guarantees that `ptr` cannot have any other
> references taken out for the
> +    ///   lifetime of `'a`.
> +    ///
> +    /// [`struct drm_connector_state`]:
> srctree/include/drm/drm_connector.h
> +    unsafe fn from_raw_mut<'a>(ptr: *mut
> bindings::drm_connector_state) -> &'a mut Self;
> +}
> +
> +/// Common methods available on any type which implements
> [`AsRawConnectorState`].
> +///
> +/// This is implemented internally by DRM, and provides many of the
> basic methods for working with
> +/// the atomic state of [`Connector`]s.
> +pub trait RawConnectorState: AsRawConnectorState {
> +    /// Return the connector that this atomic state belongs to.
> +    fn connector(&self) -> &Self::Connector {
> +        // SAFETY: This is guaranteed safe by type invariance, and
> we're guaranteed by DRM that
> +        // `self.state.connector` points to a valid instance of a
> `Connector<T>`
> +        unsafe {
> Self::Connector::from_raw((*self.as_raw()).connector) }
> +    }
> +}
> +impl<T: AsRawConnectorState> RawConnectorState for T {}
> +
> +/// The main interface for a [`struct drm_connector_state`].
> +///
> +/// This type is the main interface for dealing with the atomic
> state of DRM connectors. In
> +/// addition, it allows access to whatever private data is contained
> within an implementor's
> +/// [`DriverConnectorState`] type.
> +///
> +/// # Invariants
> +///
> +/// - The DRM C API and our interface guarantees that only the user
> has mutable access to `state`,
> +///   up until [`drm_atomic_helper_commit_hw_done`] is called.
> Therefore, `connector` follows rust's
> +///   data aliasing rules and does not need to be behind an
> [`Opaque`] type.
> +/// - `state` and `inner` initialized for as long as this object is
> exposed to users.
> +/// - The data layout of this structure begins with [`struct
> drm_connector_state`].
> +/// - The connector for this atomic state can always be assumed to
> be of type
> +///   [`Connector<T::Connector>`].
> +///
> +/// [`struct drm_connector_state`]:
> srctree/include/drm/drm_connector.h
> +/// [`drm_atomic_helper_commit_hw_done`]:
> srctree/include/drm/drm_atomic_helper.h
> +#[derive(Default)]
> +#[repr(C)]
> +pub struct ConnectorState<T: DriverConnectorState> {
> +    state: bindings::drm_connector_state,
> +    inner: T,
> +}
> +
> +/// The main trait for implementing the [`struct
> drm_connector_state`] API for a [`Connector`].
> +///
> +/// A driver may store driver-private data within the implementor's
> type, which will be available
> +/// when using a full typed [`ConnectorState`] object.
> +///
> +/// # Invariants
> +///
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_connector`] pointers are contained within a
> [`Connector<Self::Connector>`].
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_connector_state`] pointers are contained within a
> [`ConnectorState<Self>`].
> +///
> +/// [`struct drm_connector`]: srctree/include/drm_connector.h
> +/// [`struct drm_connector_state`]: srctree/include/drm_connector.h
> +pub trait DriverConnectorState: Clone + Default + Sized {
> +    /// The parent [`DriverConnector`].
> +    type Connector: DriverConnector;
> +}
> +
> +impl<T: DriverConnectorState> Sealed for ConnectorState<T> {}
> +
> +impl<T: DriverConnectorState> AsRawConnectorState for
> ConnectorState<T> {
> +    type Connector = Connector<T::Connector>;
> +}
> +
> +impl<T: DriverConnectorState> private::AsRawConnectorState for
> ConnectorState<T> {
> +    fn as_raw(&self) -> &bindings::drm_connector_state {
> +        &self.state
> +    }
> +
> +    unsafe fn as_raw_mut(&mut self) -> &mut
> bindings::drm_connector_state {
> +        &mut self.state
> +    }
> +}
> +
> +impl<T: DriverConnectorState> FromRawConnectorState for
> ConnectorState<T> {
> +    unsafe fn from_raw<'a>(ptr: *const
> bindings::drm_connector_state) -> &'a Self {
> +        // Our data layout starts with
> `bindings::drm_connector_state`.
> +        let ptr: *const Self = ptr.cast();
> +
> +        // SAFETY:
> +        // - Our safety contract requires that `ptr` be contained
> within `Self`.
> +        // - Our safety contract requires the caller ensure that it
> is safe for us to take an
> +        //   immutable reference.
> +        unsafe { &*ptr }
> +    }
> +
> +    unsafe fn from_raw_mut<'a>(ptr: *mut
> bindings::drm_connector_state) -> &'a mut Self {
> +        // Our data layout starts with
> `bindings::drm_connector_state`.
> +        let ptr: *mut Self = ptr.cast();
> +
> +        // SAFETY:
> +        // - Our safety contract requires that `ptr` be contained
> within `Self`.
> +        // - Our safety contract requires the caller ensure it is
> safe for us to take a mutable
> +        //   reference.
> +        unsafe { &mut *ptr }
> +    }
> +}
> +
> +// SAFETY: `funcs` is initialized by DRM when the connector is
> allocated
> +unsafe impl<T: DriverConnectorState> ModeObjectVtable for
> ConnectorState<T> {
> +    type Vtable = bindings::drm_connector_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.connector().vtable()
> +    }
> +}
> +
> +impl<T: DriverConnectorState> ConnectorState<T> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <'a, D, C>(&'a OpaqueConnectorState<D>) -> &'a Self
> +        where
> +            T: DriverConnectorState<Connector = C>;
> +        use
> +            C as DriverConnector,
> +            D as KmsDriver<Connector = ...>
> +    }
> +}
> +
> +/// A [`struct drm_connector_state`] without a known
> [`DriverConnectorState`] implementation.
> +///
> +/// This is mainly for situations where our bindings can't infer the
> [`DriverConnectorState`]
> +/// implementation for a [`struct drm_connector_state`]
> automatically. It is identical to
> +/// [`Connector`], except that it does not provide access to the
> driver's private data.
> +///
> +/// # Invariants
> +///
> +/// - `state` is initialized for as long as this object is exposed
> to users.
> +/// - The data layout of this type is identical to [`struct
> drm_connector_state`].
> +/// - The DRM C API and our interface guarantees that only the user
> has mutable access to `state`,
> +///   up until [`drm_atomic_helper_commit_hw_done`] is called.
> Therefore, `connector` follows rust's
> +///   data aliasing rules and does not need to be behind an
> [`Opaque`] type.
> +///
> +/// [`struct drm_connector_state`]:
> srctree/include/drm/drm_connector.h
> +/// [`drm_atomic_helper_commit_hw_done`]:
> srctree/include/drm/drm_atomic_helper.h
> +#[repr(transparent)]
> +pub struct OpaqueConnectorState<T: KmsDriver> {
> +    state: bindings::drm_connector_state,
> +    _p: PhantomData<T>,
> +}
> +
> +impl<T: KmsDriver> AsRawConnectorState for OpaqueConnectorState<T> {
> +    type Connector = OpaqueConnector<T>;
> +}
> +
> +impl<T: KmsDriver> private::AsRawConnectorState for
> OpaqueConnectorState<T> {
> +    fn as_raw(&self) -> &bindings::drm_connector_state {
> +        &self.state
> +    }
> +
> +    unsafe fn as_raw_mut(&mut self) -> &mut
> bindings::drm_connector_state {
> +        &mut self.state
> +    }
> +}
> +
> +impl<T: KmsDriver> FromRawConnectorState for OpaqueConnectorState<T>
> {
> +    unsafe fn from_raw<'a>(ptr: *const
> bindings::drm_connector_state) -> &'a Self {
> +        // SAFETY: Our data layout is identical to
> `bindings::drm_connector_state`
> +        unsafe { &*ptr.cast() }
> +    }
> +
> +    unsafe fn from_raw_mut<'a>(ptr: *mut
> bindings::drm_connector_state) -> &'a mut Self {
> +        // SAFETY: Our data layout is identical to
> `bindings::drm_connector_state`
> +        unsafe { &mut *ptr.cast() }
> +    }
> +}
> +
> +// SAFETY: See OpaqueConnector's ModeObjectVtable implementation
> +unsafe impl<T: KmsDriver> ModeObjectVtable for
> OpaqueConnectorState<T> {
> +    type Vtable = bindings::drm_connector_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.connector().vtable()
> +    }
> +}
> +
> +/// An interface for mutating a [`Connector`]s atomic state.
> +///
> +/// This type is typically returned by an [`AtomicStateMutator`]
> within contexts where it is
> +/// possible to safely mutate a connector's state. In order to
> uphold rust's data-aliasing rules,
> +/// only [`ConnectorStateMutator`] may exist at a time.
> +pub struct ConnectorStateMutator<'a, T: FromRawConnectorState> {
> +    state: &'a mut T,
> +    mask: &'a Cell<u32>,
> +}
> +
> +impl<'a, T: FromRawConnectorState> ConnectorStateMutator<'a, T> {
> +    pub(super) fn new<D: KmsDriver>(
> +        mutator: &'a AtomicStateMutator<D>,
> +        state: NonNull<bindings::drm_connector_state>,
> +    ) -> Option<Self> {
> +        // SAFETY:
> +        // - `connector` is invariant throughout the lifetime of the
> atomic state.
> +        // - `state` is initialized by the time it is passed to this
> function.
> +        // - We're guaranteed that `state` is compatible with
> `drm_connector` by type invariants.
> +        let connector = unsafe {
> T::Connector::from_raw((*state.as_ptr()).connector) };
> +        let conn_mask = connector.mask();
> +        let borrowed_mask = mutator.borrowed_connectors.get();
> +
> +        if borrowed_mask & conn_mask == 0 {
> +            mutator.borrowed_connectors.set(borrowed_mask |
> conn_mask);
> +            Some(Self {
> +                mask: &mutator.borrowed_connectors,
> +                // SAFETY: We're guaranteed `state` is of `T` by
> type invariance, and we just
> +                // confirmed by checking `borrowed_connectors` that
> no other mutable borrows have
> +                // been taken out for `state`
> +                state: unsafe { T::from_raw_mut(state.as_ptr()) },
> +            })
> +        } else {
> +            None
> +        }
> +    }
> +}
> +
> +impl<'a, T: DriverConnectorState> Deref for
> ConnectorStateMutator<'a, ConnectorState<T>> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.state.inner
> +    }
> +}
> +
> +impl<'a, T: DriverConnectorState> DerefMut for
> ConnectorStateMutator<'a, ConnectorState<T>> {
> +    fn deref_mut(&mut self) -> &mut Self::Target {
> +        &mut self.state.inner
> +    }
> +}
> +
> +impl<'a, T: FromRawConnectorState> Drop for
> ConnectorStateMutator<'a, T> {
> +    fn drop(&mut self) {
> +        let mask = self.state.connector().mask();
> +        self.mask.set(self.mask.get() & !mask);
> +    }
> +}
> +
> +impl<'a, T: FromRawConnectorState> AsRawConnectorState for
> ConnectorStateMutator<'a, T> {
> +    type Connector = T::Connector;
> +}
> +
> +impl<'a, T: FromRawConnectorState> private::AsRawConnectorState for
> ConnectorStateMutator<'a, T> {
> +    fn as_raw(&self) -> &bindings::drm_connector_state {
> +        self.state.as_raw()
> +    }
> +
> +    unsafe fn as_raw_mut(&mut self) -> &mut
> bindings::drm_connector_state {
> +        // SAFETY: We're bound by the same safety contract as this
> function
> +        unsafe { self.state.as_raw_mut() }
> +    }
> +}
> +
> +// SAFETY: we inherit the safety guarantees of `T`
> +unsafe impl<'a, T> ModeObjectVtable for ConnectorStateMutator<'a, T>
> +where
> +    T: FromRawConnectorState + ModeObjectVtable,
> +{
> +    type Vtable = T::Vtable;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.state.vtable()
> +    }
> +}
> +
> +impl<'a, T: DriverConnectorState> ConnectorStateMutator<'a,
> ConnectorState<T>> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <D, C>(ConnectorStateMutator<'a,
> OpaqueConnectorState<D>>) -> Self
> +        where
> +            T: DriverConnectorState<Connector = C>;
> +        use
> +            C as DriverConnector,
> +            D as KmsDriver<Connector = ...>
> +    }
> +}
> +
> +unsafe extern "C" fn atomic_duplicate_state_callback<T:
> DriverConnectorState>(
> +    connector: *mut bindings::drm_connector,
> +) -> *mut bindings::drm_connector_state {
> +    // SAFETY: DRM guarantees that `connector` points to a valid
> initialized `drm_connector`.
> +    let state = unsafe { (*connector).state };
> +    if state.is_null() {
> +        return null_mut();
> +    }
> +
> +    // SAFETY:
> +    // - We just verified that `state` is non-null
> +    // - This cast is guaranteed to be safe via our type invariants.
> +    let state = unsafe { ConnectorState::<T>::from_raw(state) };
> +
> +    let new: Result<KBox<_>> = KBox::init(
> +        init!(ConnectorState::<T> {
> +            inner: state.inner.clone(),
> +            state: bindings::drm_connector_state {
> +                ..Default::default()
> +            },
> +        }),
> +        GFP_KERNEL,
> +    );
> +
> +    if let Ok(mut new) = new {
> +        // SAFETY:
> +        // - `new` provides a valid pointer to a newly allocated
> `drm_plane_state` via type
> +        //   invariants
> +        // - This initializes `new` via memcpy()
> +        unsafe {
> +           
> bindings::__drm_atomic_helper_connector_duplicate_state(connector,
> new.as_raw_mut())
> +        };
> +
> +        KBox::into_raw(new).cast()
> +    } else {
> +        null_mut()
> +    }
> +}
> +
> +unsafe extern "C" fn atomic_destroy_state_callback<T:
> DriverConnectorState>(
> +    _connector: *mut bindings::drm_connector,
> +    connector_state: *mut bindings::drm_connector_state,
> +) {
> +    // SAFETY: DRM guarantees that `state` points to a valid
> instance of `drm_connector_state`
> +    unsafe {
> bindings::__drm_atomic_helper_connector_destroy_state(connector_state
> ) };
> +
> +    // SAFETY:
> +    // - DRM guarantees we are the only one with access to this
> `drm_connector_state`
> +    // - This cast is safe via our type invariants.
> +    drop(unsafe {
> KBox::from_raw(connector_state.cast::<ConnectorState<T>>()) });
> +}
> +
> +unsafe extern "C" fn connector_reset_callback<T:
> DriverConnectorState>(
> +    connector: *mut bindings::drm_connector,
> +) {
> +    // SAFETY: DRM guarantees that `state` points to a valid
> instance of `drm_connector_state`
> +    let state = unsafe { (*connector).state };
> +    if !state.is_null() {
> +        // SAFETY:
> +        // - We're guaranteed `connector` is `Connector<T>` via type
> invariants
> +        // - We're guaranteed `state` is `ConnectorState<T>` via
> type invariants.
> +        unsafe { atomic_destroy_state_callback::<T>(connector,
> state) }
> +
> +        // SAFETY: No special requirements here, DRM expects this to
> be NULL
> +        unsafe { (*connector).state = null_mut() };
> +    }
> +
> +    // Unfortunately, this is the best we can do at the moment as
> this FFI callback was mistakenly
> +    // presumed to be infallible :(
> +    let new = KBox::new(ConnectorState::<T>::default(),
> GFP_KERNEL).expect("Blame the API, sorry!");
> +
> +    // DRM takes ownership of the state from here, resets it, and
> then assigns it to the connector
> +    // SAFETY:
> +    // - DRM guarantees that `connector` points to a valid instance
> of `drm_connector`.
> +    // - The cast to `drm_connector_state` is safe via
> `ConnectorState`s type invariants.
> +    unsafe {
> bindings::__drm_atomic_helper_connector_reset(connector,
> Box::into_raw(new).cast()) };
> +}
> diff --git a/rust/kernel/drm/kms/crtc.rs
> b/rust/kernel/drm/kms/crtc.rs
> new file mode 100644
> index 000000000000..b9d095854ba6
> --- /dev/null
> +++ b/rust/kernel/drm/kms/crtc.rs
> @@ -0,0 +1,1110 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! DRM CRTCs.
> +//!
> +//! C header:
> [`include/drm/drm_crtc.h`](srctree/include/drm/drm_crtc.h)
> +
> +use super::{
> +    atomic::*, plane::*, vblank::*, KmsDriver, ModeObject,
> ModeObjectVtable, StaticModeObject,
> +    NewKmsDevice, Probing, Sealed,
> +};
> +use crate::{
> +    alloc::KBox,
> +    bindings,
> +    drm::device::Device,
> +    error::{from_result, to_result},
> +    prelude::*,
> +    types::{NotThreadSafe, Opaque},
> +};
> +use core::{
> +    cell::{Cell, UnsafeCell},
> +    marker::*,
> +    mem::{self, ManuallyDrop},
> +    ops::{Deref, DerefMut},
> +    ptr::{null, null_mut, NonNull},
> +};
> +use macros::vtable;
> +
> +/// The main trait for implementing the [`struct drm_crtc`] API for
> [`Crtc`].
> +///
> +/// Any KMS driver should have at least one implementation of this
> type, which allows them to create
> +/// [`Crtc`] objects. Additionally, a driver may store driver-
> private data within the type that
> +/// implements [`DriverCrtc`] - and it will be made available when
> using a fully typed [`Crtc`]
> +/// object.
> +///
> +/// # Invariants
> +///
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_crtc`] pointers are contained within a
> [`Crtc<Self>`].
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_crtc_state`] pointers are contained within a
> [`CrtcState<Self::State>`].
> +///
> +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
> +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h
> +#[vtable]
> +pub trait DriverCrtc: Send + Sync + Sized {
> +    /// The generated C vtable for this [`DriverCrtc`]
> implementation.
> +    const OPS: &'static DriverCrtcOps = &DriverCrtcOps {
> +        funcs: bindings::drm_crtc_funcs {
> +            atomic_destroy_state:
> Some(atomic_destroy_state_callback::<Self::State>),
> +            atomic_duplicate_state:
> Some(atomic_duplicate_state_callback::<Self::State>),
> +            atomic_get_property: None,
> +            atomic_print_state: None,
> +            atomic_set_property: None,
> +            cursor_move: None,
> +            cursor_set2: None,
> +            cursor_set: None,
> +            destroy: Some(crtc_destroy_callback::<Self>),
> +            disable_vblank: <Self::VblankImpl as
> VblankImpl>::VBLANK_OPS.disable_vblank,
> +            early_unregister: None,
> +            enable_vblank: <Self::VblankImpl as
> VblankImpl>::VBLANK_OPS.enable_vblank,
> +            gamma_set: None,
> +            get_crc_sources: None,
> +            get_vblank_counter: None,
> +            get_vblank_timestamp: <Self::VblankImpl as
> VblankImpl>::VBLANK_OPS.get_vblank_timestamp,
> +            late_register: None,
> +            page_flip: Some(bindings::drm_atomic_helper_page_flip),
> +            page_flip_target: None,
> +            reset: Some(crtc_reset_callback::<Self::State>),
> +            set_config:
> Some(bindings::drm_atomic_helper_set_config),
> +            set_crc_source: None,
> +            set_property: None,
> +            verify_crc_source: None,
> +        },
> +
> +        helper_funcs: bindings::drm_crtc_helper_funcs {
> +            atomic_disable: if Self::HAS_ATOMIC_DISABLE {
> +                Some(atomic_disable_callback::<Self>)
> +            } else {
> +                None
> +            },
> +            atomic_enable: if Self::HAS_ATOMIC_ENABLE {
> +                Some(atomic_enable_callback::<Self>)
> +            } else {
> +                None
> +            },
> +            atomic_check: if Self::HAS_ATOMIC_CHECK {
> +                Some(atomic_check_callback::<Self>)
> +            } else {
> +                None
> +            },
> +            dpms: None,
> +            commit: None,
> +            prepare: None,
> +            disable: None,
> +            mode_set: None,
> +            mode_valid: None,
> +            mode_fixup: None,
> +            atomic_begin: if Self::HAS_ATOMIC_BEGIN {
> +                Some(atomic_begin_callback::<Self>)
> +            } else {
> +                None
> +            },
> +            atomic_flush: if Self::HAS_ATOMIC_FLUSH {
> +                Some(atomic_flush_callback::<Self>)
> +            } else {
> +                None
> +            },
> +            mode_set_nofb: None,
> +            mode_set_base: None,
> +            mode_set_base_atomic: None,
> +            get_scanout_position: None,
> +        },
> +    };
> +
> +    /// The type to pass to the `args` field of
> [`UnregisteredCrtc::new`].
> +    ///
> +    /// This type will be made available in in the `args` argument
> of [`Self::new`]. Drivers which
> +    /// don't need this can simply pass [`()`] here.
> +    type Args;
> +
> +    /// The parent [`KmsDriver`] implementation.
> +    type Driver: KmsDriver;
> +
> +    /// The [`DriverCrtcState`] implementation for this
> [`DriverCrtc`].
> +    ///
> +    /// See [`DriverCrtcState`] for more info.
> +    type State: DriverCrtcState;
> +
> +    /// The driver's optional hardware vblank implementation
> +    ///
> +    /// See [`VblankSupport`] for more info. Drivers that don't care
> about this can just pass
> +    /// [`PhantomData<Self>`].
> +    type VblankImpl: VblankImpl<Crtc = Self>;
> +
> +    /// The constructor for creating a [`Crtc`] using this
> [`DriverCrtc`] implementation.
> +    ///
> +    /// Drivers may use this to instantiate their [`DriverCrtc`]
> object.
> +    fn new(device: &Device<Self::Driver>, args: &Self::Args) -> impl
> PinInit<Self, Error>;
> +
> +    /// The optional [`drm_crtc_helper_funcs.atomic_check`] hook for
> this crtc.
> +    ///
> +    /// Drivers may use this to customize the atomic check phase of
> their [`Crtc`] objects. The
> +    /// result of this function determines whether the atomic check
> passed or failed.
> +    ///
> +    /// [`drm_crtc_helper_funcs.atomic_check`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +    fn atomic_check(_check: CrtcAtomicCheck<'_, Self>) -> Result {
> +        build_error::build_error("This should not be reachable")
> +    }
> +
> +    /// The optional [`drm_crtc_helper_funcs.atomic_begin`] hook.
> +    ///
> +    /// This hook will be called before a set of [`Plane`] updates
> are performed for the given
> +    /// [`Crtc`].
> +    ///
> +    /// [`drm_crtc_helper_funcs.atomic_begin`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +    fn atomic_begin(_commit: CrtcAtomicCommit<'_, Self>) {
> +        build_error::build_error("This should not be reachable")
> +    }
> +
> +    /// The optional [`drm_crtc_helper_funcs.atomic_flush`] hook.
> +    ///
> +    /// This hook will be called after a set of [`Plane`] updates
> are performed for the given
> +    /// [`Crtc`].
> +    ///
> +    /// [`drm_crtc_helper_funcs.atomic_flush`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +    fn atomic_flush(_commit: CrtcAtomicCommit<'_, Self>) {
> +        build_error::build_error("This should never be reachable")
> +    }
> +
> +    /// The optional [`drm_crtc_helper_funcs.atomic_enable`] hook.
> +    ///
> +    /// This hook will be called before enabling a [`Crtc`] in an
> atomic commit.
> +    ///
> +    /// [`drm_crtc_helper_funcs.atomic_enable`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +    fn atomic_enable(_commit: CrtcAtomicCommit<'_, Self>) {
> +        build_error::build_error("This should never be reachable")
> +    }
> +
> +    /// The optional [`drm_crtc_helper_funcs.atomic_disable`] hook.
> +    ///
> +    /// This hook will be called before disabling a [`Crtc`] in an
> atomic commit.
> +    ///
> +    /// [`drm_crtc_helper_funcs.atomic_disable`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +    fn atomic_disable(_commit: CrtcAtomicCommit<'_, Self>) {
> +        build_error::build_error("This should never be reachable")
> +    }
> +}
> +
> +/// The generated C vtable for a [`DriverCrtc`].
> +///
> +/// This type is created internally by DRM.
> +pub struct DriverCrtcOps {
> +    funcs: bindings::drm_crtc_funcs,
> +    helper_funcs: bindings::drm_crtc_helper_funcs,
> +}
> +
> +/// The main interface for a [`struct drm_crtc`].
> +///
> +/// This type is the main interface for dealing with DRM CRTCs. In
> addition, it also allows
> +/// immutable access to whatever private data is contained within an
> implementor's [`DriverCrtc`]
> +/// type.
> +///
> +/// # Invariants
> +///
> +/// - `crtc` and `inner` are initialized for as long as this object
> is made available to users.
> +/// - The data layout of this structure begins with [`struct
> drm_crtc`].
> +/// - The atomic state for this type can always be assumed to be of
> type [`CrtcState<T::State>`].
> +///
> +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
> +#[repr(C)]
> +#[pin_data]
> +pub struct Crtc<T: DriverCrtc> {
> +    // The FFI drm_crtc object
> +    crtc: Opaque<bindings::drm_crtc>,
> +    /// The driver's private inner data
> +    #[pin]
> +    inner: T,
> +    #[pin]
> +    _p: PhantomPinned,
> +}
> +
> +impl<T: DriverCrtc> Sealed for Crtc<T> {}
> +
> +// SAFETY: Our CRTC interfaces are guaranteed to be thread-safe
> +unsafe impl<T: DriverCrtc> Send for Crtc<T> {}
> +
> +// SAFETY: Our CRTC interfaces are guaranteed to be thread-safe
> +unsafe impl<T: DriverCrtc> Sync for Crtc<T> {}
> +
> +impl<T: DriverCrtc> Deref for Crtc<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.inner
> +    }
> +}
> +
> +// SAFETY: We don't expose Crtc<T> to users before `base` is
> initialized in ::new(), so
> +// `raw_mode_obj` always returns a valid pointer to a
> bindings::drm_mode_object.
> +unsafe impl<T: DriverCrtc> ModeObject for Crtc<T> {
> +    type Driver = T::Driver;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: DRM connectors exist for as long as the device
> does, so this pointer is always
> +        // valid
> +        unsafe { Device::from_raw((*self.as_raw()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose Crtc<T> to users before it's
> initialized, so `base` is always
> +        // initialized
> +        unsafe { &raw mut (*self.as_raw()).base }
> +    }
> +}
> +
> +// SAFETY: CRTCs are non-refcounted modesetting objects
> +unsafe impl<T: DriverCrtc> StaticModeObject for Crtc<T> {}
> +
> +// SAFETY: `funcs` is initialized when the crtc is allocated
> +unsafe impl<T: DriverCrtc> ModeObjectVtable for Crtc<T> {
> +    type Vtable = bindings::drm_crtc_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        // SAFETY: `as_raw()` always returns a valid pointer to a
> CRTC
> +        unsafe { *self.as_raw() }.funcs
> +    }
> +}
> +
> +impl<T: DriverCrtc> Crtc<T> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <'a, D>(&'a OpaqueCrtc<D>) -> &'a Self;
> +        use
> +            T as DriverCrtc,
> +            D as KmsDriver<Crtc = ...>
> +    }
> +
> +    pub(crate) fn get_vblank_ptr(&self) -> *mut
> bindings::drm_vblank_crtc {
> +        // SAFETY: FFI Call with no special requirements
> +        unsafe { bindings::drm_crtc_vblank_crtc(self.as_raw()) }
> +    }
> +
> +    pub(crate) const fn has_vblank() -> bool {
> +        T::OPS.funcs.enable_vblank.is_some()
> +    }
> +}
> +
> +/// A [`Crtc`] that has not yet been registered with userspace.
> +///
> +/// KMS registration is single-threaded, so this object is not
> thread-safe.
> +///
> +/// # Invariants
> +///
> +/// - This object can only exist before its respective KMS device
> has been registered.
> +/// - Otherwise, it inherits all invariants of [`Crtc`] and has an
> identical data layout.
> +pub struct UnregisteredCrtc<T: DriverCrtc>(Crtc<T>, NotThreadSafe);
> +
> +impl<T: DriverCrtc> UnregisteredCrtc<T> {
> +    /// Construct a new [`UnregisteredCrtc`].
> +    ///
> +    /// A driver may use this from their
> [`KmsDriver::create_objects`] callback in order to
> +    /// construct new [`UnregisteredCrtc`] objects.
> +    ///
> +    /// [`KmsDriver::create_objects`]:
> kernel::drm::kms::KmsDriver::create_objects
> +    pub fn new<'a, 'b: 'a, PrimaryData, CursorData>(
> +        dev: &'a NewKmsDevice<'a, T::Driver, Probing>,
> +        primary: &'a UnregisteredPlane<PrimaryData>,
> +        cursor: Option<&'a UnregisteredPlane<CursorData>>,
> +        name: Option<&CStr>,
> +        args: T::Args,
> +    ) -> Result<&'a Self>
> +    where
> +        PrimaryData: DriverPlane<Driver = T::Driver>,
> +        CursorData: DriverPlane<Driver = T::Driver>,
> +    {
> +        if Crtc::<T>::has_vblank() {
> +            dev.has_vblanks.set(true)
> +        }
> +
> +        let this: Pin<KBox<Crtc<T>>> = KBox::try_pin_init(
> +            try_pin_init!(Crtc {
> +                crtc: Opaque::new(bindings::drm_crtc {
> +                    helper_private: &T::OPS.helper_funcs,
> +                    ..Default::default()
> +                }),
> +                inner <- T::new(dev, &args),
> +                _p: PhantomPinned,
> +            }),
> +            GFP_KERNEL,
> +        )?;
> +
> +        // SAFETY:
> +        // - `dev` handles destroying the CRTC and thus will outlive
> us.
> +        // - We just allocated `this`, and we won't move it since
> it's pinned
> +        // - `primary` and `cursor` share the lifetime 'a with `dev`
> +        // - This function will memcpy the contents of `name` into
> its own storage.
> +        to_result(unsafe {
> +            bindings::drm_crtc_init_with_planes(
> +                dev.as_raw(),
> +                this.as_raw(),
> +                primary.as_raw(),
> +                cursor.map_or(null_mut(), |c| c.as_raw()),
> +                &T::OPS.funcs,
> +                name.map_or(null(), |n| n.as_char_ptr()),
> +            )
> +        })?;
> +
> +        // SAFETY: We don't move anything
> +        let this = unsafe { Pin::into_inner_unchecked(this) };
> +
> +        // We'll re-assemble the box in crtc_destroy_callback()
> +        let this = KBox::into_raw(this);
> +
> +        // UnregisteredCrtc has an equivalent data layout
> +        let this: *mut Self = this.cast();
> +
> +        // SAFETY: We just allocated the crtc above, so this pointer
> must be valid
> +        Ok(unsafe { &*this })
> +    }
> +}
> +
> +// SAFETY: We inherit all relevant invariants of `Crtc`
> +unsafe impl<T: DriverCrtc> AsRawCrtc for UnregisteredCrtc<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_crtc {
> +        self.0.as_raw()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) -> &'a Self
> {
> +        // SAFETY: This is another from_raw() call, so this function
> shares the same safety contract
> +        let crtc = unsafe { Crtc::<T>::from_raw(ptr) };
> +
> +        // SAFETY: Our data layout is identical via our type
> invariants.
> +        unsafe { mem::transmute(crtc) }
> +    }
> +}
> +
> +impl<T: DriverCrtc> Deref for UnregisteredCrtc<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.0.inner
> +    }
> +}
> +
> +/// A trait implemented by any type that acts as a [`struct
> drm_crtc`] interface.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// # Safety
> +///
> +/// [`as_raw()`] must always return a valid pointer to a [`struct
> drm_crtc`].
> +///
> +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
> +/// [`as_raw()`]: AsRawCrtc::as_raw()
> +pub unsafe trait AsRawCrtc {
> +    /// Return a raw pointer to the `bindings::drm_crtc` for this
> object
> +    fn as_raw(&self) -> *mut bindings::drm_crtc;
> +
> +    /// Convert a raw [`struct drm_crtc`] pointer into an object of
> this type.
> +    ///
> +    /// # Safety
> +    ///
> +    /// Callers promise that `ptr` points to a valid instance of
> this type
> +    ///
> +    /// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) -> &'a
> Self;
> +}
> +
> +// SAFETY:
> +// - Via our type variants our data layout starts with `drm_crtc`
> +// - Since we don't expose `crtc` to users before it has been
> initialized, this and our data
> +//   layout ensure that `as_raw()` always returns a valid pointer to
> a `drm_crtc`.
> +unsafe impl<T: DriverCrtc> AsRawCrtc for Crtc<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_crtc {
> +        self.crtc.get()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) -> &'a Self
> {
> +        // Our data layout start with `bindings::drm_crtc`.
> +        let ptr: *mut Self = ptr.cast();
> +
> +        // SAFETY: Our safety contract requires that `ptr` point to
> a valid intance of `Self`.
> +        unsafe { &*ptr }
> +    }
> +}
> +
> +// SAFETY: We only expose this object to users directly after
> KmsDriver::create_objects has been
> +// called.
> +unsafe impl<T: DriverCrtc> ModesettableCrtc for Crtc<T> {
> +    type State = CrtcState<T::State>;
> +}
> +
> +/// A supertrait of [`AsRawCrtc`] for [`struct drm_crtc`] interfaces
> that can perform modesets.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// # Safety
> +///
> +/// Any object implementing this trait must only be made directly
> available to the user after
> +/// [`create_objects`] has completed.
> +///
> +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
> +/// [`create_objects`]: KmsDriver::create_objects
> +pub unsafe trait ModesettableCrtc: AsRawCrtc {
> +    /// The type that should be returned for a CRTC state acquired
> using this CRTC interface
> +    type State: FromRawCrtcState;
> +}
> +
> +/// Common methods available on any type which implements
> [`AsRawCrtc`].
> +///
> +/// This is implemented internally by DRM, and provides many of the
> basic methods for working with
> +/// CRTCs.
> +pub trait RawCrtc: AsRawCrtc {
> +    /// Return the index of this CRTC.
> +    fn index(&self) -> u32 {
> +        // SAFETY: The index is initialized by the time we expose
> Crtc objects to users, and is
> +        // invariant throughout the lifetime of the Crtc
> +        unsafe { (*self.as_raw()).index }
> +    }
> +
> +    /// Return the index of this DRM CRTC in the form of a bitmask.
> +    fn mask(&self) -> u32 {
> +        1 << self.index()
> +    }
> +}
> +impl<T: AsRawCrtc> RawCrtc for T {}
> +
> +/// A [`struct drm_crtc`] without a known [`DriverCrtc`]
> implementation.
> +///
> +/// This is mainly for situations where our bindings can't infer the
> [`DriverCrtc`] implementation
> +/// for a [`struct drm_crtc`] automatically. It is identical to
> [`Crtc`], except that it does not
> +/// provide access to the driver's private data.
> +///
> +/// It may be upcasted to a full [`Crtc`] using
> [`Crtc::from_opaque`] or
> +/// [`Crtc::try_from_opaque`].
> +///
> +/// # Invariants
> +///
> +/// - `crtc` is initialized for as long as this object is made
> available to users.
> +/// - The data layout of this structure is equivalent to [`struct
> drm_crtc`].
> +///
> +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
> +#[repr(transparent)]
> +pub struct OpaqueCrtc<T: KmsDriver> {
> +    crtc: Opaque<bindings::drm_crtc>,
> +    _p: PhantomData<T>,
> +}
> +
> +impl<T: KmsDriver> Sealed for OpaqueCrtc<T> {}
> +
> +// SAFETY:
> +// - Via our type variants our data layout is identical to
> `drm_crtc`
> +// - Since we don't expose `OpaqueCrtc` to users before it has been
> initialized, this and our data
> +//   layout ensure that `as_raw()` always returns a valid pointer to
> a `drm_crtc`.
> +unsafe impl<T: KmsDriver> AsRawCrtc for OpaqueCrtc<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_crtc {
> +        self.crtc.get()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) -> &'a Self
> {
> +        // SAFETY: Our data layout starts with `bindings::drm_crtc`
> +        unsafe { &*ptr.cast() }
> +    }
> +}
> +
> +// SAFETY: We only expose this object to users directly after
> KmsDriver::create_objects has been
> +// called.
> +unsafe impl<T: KmsDriver> ModesettableCrtc for OpaqueCrtc<T> {
> +    type State = OpaqueCrtcState<T>;
> +}
> +
> +// SAFETY: We don't expose OpaqueCrtc<T> to users before `base` is
> initialized in Crtc::<T>::new(),
> +// so `raw_mode_obj` always returns a valid pointer to a
> bindings::drm_mode_object.
> +unsafe impl<T: KmsDriver> ModeObject for OpaqueCrtc<T> {
> +    type Driver = T;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: The parent device for a DRM connector will never
> outlive the connector, and this
> +        // pointer is invariant through the lifetime of the
> connector
> +        unsafe { Device::from_raw((*self.as_raw()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose DRM connectors to users before
> `base` is initialized
> +        unsafe { &raw mut (*self.as_raw()).base }
> +    }
> +}
> +
> +// SAFETY: CRTCs are non-refcounted modesetting objects
> +unsafe impl<T: KmsDriver> StaticModeObject for OpaqueCrtc<T> {}
> +
> +// SAFETY: Our CRTC interface is guaranteed to be thread-safe
> +unsafe impl<T: KmsDriver> Send for OpaqueCrtc<T> {}
> +
> +// SAFETY: Our CRTC interface is guaranteed to be thread-safe
> +unsafe impl<T: KmsDriver> Sync for OpaqueCrtc<T> {}
> +
> +// SAFETY: `funcs` is initialized when the CRTC is allocated
> +unsafe impl<T: KmsDriver> ModeObjectVtable for OpaqueCrtc<T> {
> +    type Vtable = bindings::drm_crtc_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        // SAFETY: `as_raw()` always returns a valid pointer to a
> crtc
> +        unsafe { (*self.as_raw()).funcs }
> +    }
> +}
> +
> +impl<T: DriverCrtcState> Sealed for CrtcState<T> {}
> +
> +/// The main trait for implementing the [`struct drm_crtc_state`]
> API for a [`Crtc`].
> +///
> +/// A driver may store driver-private data within the implementor's
> type, which will be available
> +/// when using a full typed [`CrtcState`] object.
> +///
> +/// # Invariants
> +///
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_crtc`] pointers are contained within a
> [`Crtc<Self::Crtc>`].
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_crtc_state`] pointers are contained within a
> [`CrtcState<Self>`].
> +///
> +/// [`struct drm_crtc`]: srctree/include/drm_crtc.h
> +/// [`struct drm_crtc_state`]: srctree/include/drm_crtc.h
> +pub trait DriverCrtcState: Clone + Default + Unpin {
> +    /// The parent CRTC driver for this CRTC state
> +    type Crtc: DriverCrtc<State = Self>
> +    where
> +        Self: Sized;
> +}
> +
> +/// The main interface for a [`struct drm_crtc_state`].
> +///
> +/// This type is the main interface for dealing with the atomic
> state of DRM crtcs. In addition, it
> +/// allows access to whatever private data is contained within an
> implementor's [`DriverCrtcState`]
> +/// type.
> +///
> +/// # Invariants
> +///
> +/// - `state` and `inner` initialized for as long as this object is
> exposed to users.
> +/// - The data layout of this structure begins with [`struct
> drm_crtc_state`].
> +/// - The CRTC for this type can always be assumed to be of type
> [`Crtc<T::Crtc>`].
> +///
> +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h
> +#[repr(C)]
> +pub struct CrtcState<T: DriverCrtcState> {
> +    // It should be noted that CrtcState is a bit of an oddball -
> it's the only atomic state
> +    // structure that can be modified after it has been swapped in,
> which is why we need to have
> +    // `state` within an `Opaque<>`…
> +    state: Opaque<bindings::drm_crtc_state>,
> +
> +    // …it is also one of the few atomic states that some drivers
> will embed work structures into,
> +    // which means there's a good chance in the future we may have
> pinned data here - making it
> +    // impossible for us to hold a mutable or immutable reference to
> the CrtcState. In preparation
> +    // for that possibility, we keep `T` in an UnsafeCell.
> +    inner: UnsafeCell<T>,
> +}
> +
> +impl<T: DriverCrtcState> Deref for CrtcState<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        // SAFETY: Our interface ensures that `inner` will not be
> modified unless only a single
> +        // mutable reference exists to `inner`, so this is safe
> +        unsafe { &*self.inner.get() }
> +    }
> +}
> +
> +impl<T: DriverCrtcState> DerefMut for CrtcState<T> {
> +    fn deref_mut(&mut self) -> &mut Self::Target {
> +        self.inner.get_mut()
> +    }
> +}
> +
> +// SAFETY: Shares the safety guarantee of Crtc<T>'s ModeObjectVtable
> impl
> +unsafe impl<T: DriverCrtcState> ModeObjectVtable for CrtcState<T> {
> +    type Vtable = bindings::drm_crtc_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.crtc().vtable()
> +    }
> +}
> +
> +impl<T: DriverCrtcState> CrtcState<T> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <'a, D, C>(&'a OpaqueCrtcState<D>) -> &'a Self
> +        where
> +            T: DriverCrtcState<Crtc = C>;
> +        use
> +            C as DriverCrtc,
> +            D as KmsDriver<Crtc = ...>
> +    }
> +}
> +
> +/// A trait implemented by any type which can produce a reference to
> a [`struct drm_crtc_state`].
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h
> +pub trait AsRawCrtcState: private::AsRawCrtcState {
> +    /// The type that this CRTC state interface returns to represent
> the parent CRTC
> +    type Crtc: ModesettableCrtc;
> +}
> +
> +pub(crate) mod private {
> +    use super::*;
> +
> +    #[allow(unreachable_pub)]
> +    pub trait AsRawCrtcState {
> +        /// Return a raw pointer to the DRM CRTC state
> +        ///
> +        /// Note that CRTC states are the only atomic state in KMS
> which don't nicely follow rust's
> +        /// data aliasing rules already.
> +        fn as_raw(&self) -> *mut bindings::drm_crtc_state;
> +    }
> +}
> +
> +pub(super) use private::AsRawCrtcState as AsRawCrtcStatePrivate;
> +
> +/// Common methods available on any type which implements
> [`AsRawCrtcState`].
> +///
> +/// This is implemented internally by DRM, and provides many of the
> basic methods for working with
> +/// the atomic state of [`Crtc`]s.
> +pub trait RawCrtcState: AsRawCrtcState {
> +    /// Return the CRTC that owns this state.
> +    fn crtc(&self) -> &Self::Crtc {
> +        // SAFETY:
> +        // - This type conversion is guaranteed by type invariance
> +        // - Our interface ensures that this access follows rust's
> data-aliasing rules
> +        // - `crtc` is guaranteed to never be NULL and is invariant
> throughout the lifetime of the
> +        //   state
> +        unsafe { <Self::Crtc as
> AsRawCrtc>::from_raw((*self.as_raw()).crtc) }
> +    }
> +
> +    /// Returns whether or not the CRTC is active in this atomic
> state.
> +    fn active(&self) -> bool {
> +        // SAFETY: `active` and the rest of its containing bitfield
> can only be modified from the
> +        // atomic check context, and are invariant beyond that point
> - so our interface can ensure
> +        // this access is serialized
> +        unsafe { (*self.as_raw()).active }
> +    }
> +}
> +impl<T: AsRawCrtcState> RawCrtcState for T {}
> +
> +/// A trait implemented for any type which can be constructed
> directly from a
> +/// [`struct drm_crtc_state`] pointer.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h
> +pub trait FromRawCrtcState: AsRawCrtcState {
> +    /// Obtain a reference back to this type from a raw DRM crtc
> state pointer
> +    ///
> +    /// # Safety
> +    ///
> +    /// Callers must ensure that ptr contains a valid instance of
> this type.
> +    unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc_state) ->
> &'a Self;
> +}
> +
> +impl<T: DriverCrtcState> private::AsRawCrtcState for CrtcState<T> {
> +    #[inline]
> +    fn as_raw(&self) -> *mut bindings::drm_crtc_state {
> +        self.state.get()
> +    }
> +}
> +
> +impl<T: DriverCrtcState> AsRawCrtcState for CrtcState<T> {
> +    type Crtc = Crtc<T::Crtc>;
> +}
> +
> +impl<T: DriverCrtcState> FromRawCrtcState for CrtcState<T> {
> +    unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc_state) ->
> &'a Self {
> +        // SAFETY: Our data layout starts with
> `bindings::drm_crtc_state`
> +        unsafe { &*(ptr.cast()) }
> +    }
> +}
> +
> +/// A [`struct drm_crtc_state`] without a known [`DriverCrtcState`]
> implementation.
> +///
> +/// This is mainly for situations where our bindings can't infer the
> [`DriverCrtcState`]
> +/// implementation for a [`struct drm_crtc_state`] automatically. It
> is identical to [`Crtc`],
> +/// except that it does not provide access to the driver's private
> data.
> +///
> +/// # Invariants
> +///
> +/// - `state` is initialized for as long as this object is exposed
> to users.
> +/// - The data layout of this type is identical to [`struct
> drm_crtc_state`].
> +///
> +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h
> +#[repr(transparent)]
> +pub struct OpaqueCrtcState<T: KmsDriver> {
> +    state: Opaque<bindings::drm_crtc_state>,
> +    _p: PhantomData<T>,
> +}
> +
> +impl<T: KmsDriver> AsRawCrtcState for OpaqueCrtcState<T> {
> +    type Crtc = OpaqueCrtc<T>;
> +}
> +
> +impl<T: KmsDriver> private::AsRawCrtcState for OpaqueCrtcState<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_crtc_state {
> +        self.state.get()
> +    }
> +}
> +
> +impl<T: KmsDriver> FromRawCrtcState for OpaqueCrtcState<T> {
> +    unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc_state) ->
> &'a Self {
> +        // SAFETY: Our data layout is identical to
> `bindings::drm_crtc_state`
> +        unsafe { &*(ptr.cast()) }
> +    }
> +}
> +
> +// SAFETY: Shares the safety guarantees of OpaqueCrtc<T>'s
> ModeObjectVtable impl
> +unsafe impl<T: KmsDriver> ModeObjectVtable for OpaqueCrtcState<T> {
> +    type Vtable = bindings::drm_crtc_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.crtc().vtable()
> +    }
> +}
> +
> +/// An interface for mutating a [`Crtc`]s atomic state.
> +///
> +/// This type is typically returned by an [`AtomicStateMutator`]
> within contexts where it is
> +/// possible to safely mutate a plane's state. In order to uphold
> rust's data-aliasing rules, only
> +/// [`CrtcStateMutator`] may exist at a time.
> +///
> +/// # Invariants
> +///
> +/// `self.state` always points to a valid instance of a
> [`FromRawCrtcState`] object.
> +pub struct CrtcStateMutator<'a, T: FromRawCrtcState> {
> +    state: NonNull<T>,
> +    mask: &'a Cell<u32>,
> +}
> +
> +impl<'a, T: FromRawCrtcState> CrtcStateMutator<'a, T> {
> +    pub(super) fn new<D: KmsDriver>(
> +        mutator: &'a AtomicStateMutator<D>,
> +        state: NonNull<bindings::drm_crtc_state>,
> +    ) -> Option<Self> {
> +        // SAFETY: `crtc` is invariant throughout the lifetime of
> the atomic state, and always
> +        // points to a valid `Crtc<T::Crtc>`
> +        let crtc = unsafe {
> T::Crtc::from_raw((*state.as_ptr()).crtc) };
> +        let crtc_mask = crtc.mask();
> +        let borrowed_mask = mutator.borrowed_crtcs.get();
> +
> +        if borrowed_mask & crtc_mask == 0 {
> +            mutator.borrowed_crtcs.set(borrowed_mask | crtc_mask);
> +            Some(Self {
> +                mask: &mutator.borrowed_crtcs,
> +                state: state.cast(),
> +            })
> +        } else {
> +            None
> +        }
> +    }
> +}
> +
> +impl<'a, T: DriverCrtcState> CrtcStateMutator<'a, CrtcState<T>> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <D, C>(CrtcStateMutator<'a, OpaqueCrtcState<D>>) -> Self
> +        where
> +            T: DriverCrtcState<Crtc = C>;
> +        use
> +            T as DriverCrtc,
> +            D as KmsDriver<Crtc = ...>
> +    }
> +}
> +
> +impl<'a, T: FromRawCrtcState> Drop for CrtcStateMutator<'a, T> {
> +    fn drop(&mut self) {
> +        // SAFETY: Our interface is proof that we are the only ones
> with a reference to this data
> +        let mask = unsafe { self.state.as_ref() }.crtc().mask();
> +        self.mask.set(self.mask.get() & !mask);
> +    }
> +}
> +
> +impl<'a, T: DriverCrtcState> Deref for CrtcStateMutator<'a,
> CrtcState<T>> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        // SAFETY: Our interface ensures that `self.state.inner`
> follows rust's data-aliasing rules,
> +        // so this is safe
> +        unsafe { &*(*self.state.as_ptr()).inner.get() }
> +    }
> +}
> +
> +impl<'a, T: DriverCrtcState> DerefMut for CrtcStateMutator<'a,
> CrtcState<T>> {
> +    fn deref_mut(&mut self) -> &mut Self::Target {
> +        // SAFETY: Our interface ensures that `self.state.inner`
> follows rust's data-aliasing rules,
> +        // so this is safe
> +        unsafe { (*self.state.as_ptr()).inner.get_mut() }
> +    }
> +}
> +
> +impl<'a, T: FromRawCrtcState> AsRawCrtcState for
> CrtcStateMutator<'a, T> {
> +    type Crtc = T::Crtc;
> +}
> +
> +impl<'a, T: FromRawCrtcState> private::AsRawCrtcState for
> CrtcStateMutator<'a, T> {
> +    fn as_raw(&self) -> *mut bindings::drm_crtc_state {
> +        self.state.as_ptr().cast()
> +    }
> +}
> +
> +// SAFETY: Shares the safety guarantees of T::Crtc's
> ModeObjectVtable impl
> +unsafe impl<'a, T: FromRawCrtcState> ModeObjectVtable for
> CrtcStateMutator<'a, T>
> +where
> +    T::Crtc: ModeObjectVtable,
> +{
> +    type Vtable = <T::Crtc as ModeObjectVtable>::Vtable;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.crtc().vtable()
> +    }
> +}
> +
> +/// A token provided during [`atomic_check`] callbacks for accessing
> the crtc, atomic state, and new
> +/// and old states of the crtc.
> +///
> +/// # Invariants
> +///
> +/// This token is proof that the old and new atomic state of `crtc`
> are present in `state` and do
> +/// not have any mutators taken out.
> +///
> +/// [`atomic_check`]: DriverCrtc::atomic_check
> +pub struct CrtcAtomicCheck<'a, T: DriverCrtc> {
> +    state: &'a AtomicStateComposer<T::Driver>,
> +    crtc: &'a Crtc<T>,
> +}
> +
> +impl<'a, T: DriverCrtc> CrtcAtomicCheck<'a, T> {
> +    impl_atomic_state_token_ops!(
> +        CrtcAtomicCheck,
> +        AtomicStateComposer,
> +        Crtc,
> +        use <'a, T>
> +    );
> +}
> +
> +/// A token provided to [`DriverCrtc`] callbacks during the atomic
> commit phase for accessing the
> +/// crtc, atomic state, new and old states of the crtc.
> +///
> +/// # Invariants
> +///
> +/// This token is proof that the old and new atomic state of `crtc`
> are present in `state` and do
> +/// not have any mutators taken out.
> +pub struct CrtcAtomicCommit<'a, T: DriverCrtc> {
> +    state: &'a AtomicStateMutator<T::Driver>,
> +    crtc: &'a Crtc<T>,
> +}
> +
> +impl<'a, T: DriverCrtc> CrtcAtomicCommit<'a, T> {
> +    impl_atomic_state_token_ops!(
> +        CrtcAtomicCommit,
> +        AtomicStateMutator,
> +        Crtc,
> +        use <'a, T>
> +    );
> +}
> +
> +unsafe extern "C" fn crtc_destroy_callback<T: DriverCrtc>(crtc: *mut
> bindings::drm_crtc) {
> +    // SAFETY: DRM guarantees that `crtc` points to a valid
> initialized `drm_crtc`.
> +    unsafe { bindings::drm_crtc_cleanup(crtc) };
> +
> +    // SAFETY:
> +    // - DRM guarantees we are now the only one with access to this
> [`drm_crtc`].
> +    // - This cast is safe via `DriverCrtc`s type invariants.
> +    // - We created this as a pinned type originally
> +    drop(unsafe { Pin::new_unchecked(KBox::from_raw(crtc as *mut
> Crtc<T>)) });
> +}
> +
> +unsafe extern "C" fn atomic_duplicate_state_callback<T:
> DriverCrtcState>(
> +    crtc: *mut bindings::drm_crtc,
> +) -> *mut bindings::drm_crtc_state {
> +    // SAFETY: DRM guarantees that `crtc` points to a valid
> initialized `drm_crtc`.
> +    let state = unsafe { (*crtc).state };
> +    if state.is_null() {
> +        return null_mut();
> +    }
> +
> +    // SAFETY: This cast is safe via `DriverCrtcState`s type
> invariants.
> +    let crtc = unsafe { Crtc::<T::Crtc>::from_raw(crtc) };
> +
> +    // SAFETY: This cast is safe via `DriverCrtcState`s type
> invariants.
> +    let state = unsafe { CrtcState::<T>::from_raw(state) };
> +
> +    let new: Result<KBox<_>> = KBox::try_init(
> +        try_init!(CrtcState {
> +            inner: UnsafeCell::new((*state).clone()),
> +            state: Opaque::new(Default::default()),
> +        }),
> +        GFP_KERNEL,
> +    );
> +
> +    if let Ok(new) = new {
> +        let new = KBox::into_raw(new).cast();
> +
> +        // SAFETY:
> +        // - `new` provides a valid pointer to a newly allocated
> `drm_crtc_state` via type
> +        // invariants
> +        // - This initializes `new` via memcpy()
> +        unsafe {
> bindings::__drm_atomic_helper_crtc_duplicate_state(crtc.as_raw(),
> new) }
> +
> +        new
> +    } else {
> +        null_mut()
> +    }
> +}
> +
> +unsafe extern "C" fn atomic_destroy_state_callback<T:
> DriverCrtcState>(
> +    _crtc: *mut bindings::drm_crtc,
> +    crtc_state: *mut bindings::drm_crtc_state,
> +) {
> +    // SAFETY: DRM guarantees that `state` points to a valid
> instance of `drm_crtc_state`
> +    unsafe {
> bindings::__drm_atomic_helper_crtc_destroy_state(crtc_state) };
> +
> +    // SAFETY:
> +    // - DRM guarantees we are the only one with access to this
> `drm_crtc_state`
> +    // - This cast is safe via our type invariants.
> +    // - All data in `CrtcState` is either Unpin, or pinned
> +    drop(unsafe { KBox::from_raw(crtc_state as *mut CrtcState<T>)
> });
> +}
> +
> +unsafe extern "C" fn crtc_reset_callback<T: DriverCrtcState>(crtc:
> *mut bindings::drm_crtc) {
> +    // SAFETY: DRM guarantees that `state` points to a valid
> instance of `drm_crtc_state`
> +    let state = unsafe { (*crtc).state };
> +    if !state.is_null() {
> +        // SAFETY:
> +        // - We're guaranteed `crtc` is `Crtc<T>` via type
> invariants
> +        // - We're guaranteed `state` is `CrtcState<T>` via type
> invariants.
> +        unsafe { atomic_destroy_state_callback::<T>(crtc, state) }
> +
> +        // SAFETY: No special requirements here, DRM expects this to
> be NULL
> +        unsafe {
> +            (*crtc).state = null_mut();
> +        }
> +    }
> +
> +    // SAFETY: `crtc` is guaranteed to be of type `Crtc<T::Crtc>` by
> type invariance
> +    let crtc = unsafe { Crtc::<T::Crtc>::from_raw(crtc) };
> +
> +    // Unfortunately, this is the best we can do at the moment as
> this FFI callback was mistakenly
> +    // presumed to be infallible :(
> +    let new: KBox<CrtcState<T>> = KBox::try_init(
> +        try_init!(CrtcState {
> +            state: Opaque::new(Default::default()),
> +            inner: UnsafeCell::new(Default::default()),
> +        }),
> +        GFP_KERNEL,
> +    )
> +    .expect("Unfortunately, this API was presumed infallible");
> +
> +    // SAFETY: DRM takes ownership of the state from here, and will
> never move it
> +    unsafe { bindings::__drm_atomic_helper_crtc_reset(crtc.as_raw(),
> KBox::into_raw(new).cast()) };
> +}
> +
> +unsafe extern "C" fn atomic_check_callback<T: DriverCrtc>(
> +    crtc: *mut bindings::drm_crtc,
> +    state: *mut bindings::drm_atomic_state,
> +) -> i32 {
> +    // SAFETY:
> +    // - We're guaranteed `crtc` is of type `Crtc<T>` via type
> invariants.
> +    // - We're guaranteed by DRM that `crtc` is pointing to a valid
> initialized state.
> +    let crtc = unsafe { Crtc::from_raw(crtc) };
> +
> +    // SAFETY: DRM guarantees `state` points to a valid
> `drm_atomic_state`
> +    // We use a ManuallyDrop here to avoid AtomicStateComposer
> dropping an owned reference we never
> +    // acquired.
> +    let state =
> +        unsafe {
> ManuallyDrop::new(AtomicStateComposer::new(NonNull::new_unchecked(sta
> te))) };
> +
> +    // SAFETY:
> +    // - Since we're in the atomic check callback, we're guaranteed
> by DRM that both the old and
> +    //   new crtc state are present in this atomic state
> +    // - We just created the state composer above, so other
> composers cannot be taken out on the
> +    //   crtc state yet.
> +    let check = unsafe { CrtcAtomicCheck::new(crtc, &state) };
> +
> +    from_result(|| {
> +        T::atomic_check(check)?;
> +        Ok(0)
> +    })
> +}
> +
> +unsafe extern "C" fn atomic_begin_callback<T: DriverCrtc>(
> +    crtc: *mut bindings::drm_crtc,
> +    state: *mut bindings::drm_atomic_state,
> +) {
> +    // SAFETY:
> +    // * We're guaranteed `crtc` is of type `Crtc<T>` via type
> invariants.
> +    // * We're guaranteed by DRM that `crtc` is pointing to a valid
> initialized state.
> +    let crtc = unsafe { Crtc::from_raw(crtc) };
> +
> +    // SAFETY: We're guaranteed by DRM that `state` points to a
> valid instance of `drm_atomic_state`
> +    let state = unsafe {
> AtomicStateMutator::new(NonNull::new_unchecked(state)) };
> +
> +    // SAFETY:
> +    // - Since we're in the atomic_begin callback, we're guaranteed
> by DRM that both the old and new
> +    //   crtc state are resent in this atomic state.
> +    // - We just created the state mutator above, so other mutators
> cannot be taken out on the crtc
> +    //   state yet.
> +    let commit = unsafe { CrtcAtomicCommit::new(crtc, &state) };
> +
> +    T::atomic_begin(commit);
> +}
> +
> +unsafe extern "C" fn atomic_flush_callback<T: DriverCrtc>(
> +    crtc: *mut bindings::drm_crtc,
> +    state: *mut bindings::drm_atomic_state,
> +) {
> +    // SAFETY:
> +    // - We're guaranteed `crtc` is of type `Crtc<T>` via type
> invariants.
> +    // - We're guaranteed by DRM that `crtc` is pointing to a valid
> initialized state.
> +    let crtc = unsafe { Crtc::from_raw(crtc) };
> +
> +    // SAFETY: We're guaranteed by DRM that `state` points to a
> valid instance of `drm_atomic_state`
> +    let state = unsafe {
> AtomicStateMutator::new(NonNull::new_unchecked(state)) };
> +
> +    // SAFETY:
> +    // - Since we're in the atomic_flush callback, we're guaranteed
> by DRM that both the old and new
> +    //   crtc state are resent in this atomic state.
> +    // - We just created the state mutator above, so other mutators
> cannot be taken out on the crtc
> +    //   state yet.
> +    let commit = unsafe { CrtcAtomicCommit::new(crtc, &state) };
> +
> +    T::atomic_flush(commit);
> +}
> +
> +unsafe extern "C" fn atomic_enable_callback<T: DriverCrtc>(
> +    crtc: *mut bindings::drm_crtc,
> +    state: *mut bindings::drm_atomic_state,
> +) {
> +    // SAFETY:
> +    // - We're guaranteed `crtc` is of type `Crtc<T>` via type
> invariants.
> +    // - We're guaranteed by DRM that `crtc` is pointing to a valid
> initialized state.
> +    let crtc = unsafe { Crtc::from_raw(crtc) };
> +
> +    // SAFETY: DRM never passes an invalid ptr for `state`
> +    let state = unsafe {
> AtomicStateMutator::new(NonNull::new_unchecked(state)) };
> +
> +    // SAFETY:
> +    // - Since we're in the atomic_enable callback, we're guaranteed
> by DRM that both the old and
> +    //   new crtc state are present in this atomic state.
> +    // - We just created the state mutator above, so other mutators
> cannot be taken out on the crtc
> +    //   state yet.
> +    let commit = unsafe { CrtcAtomicCommit::new(crtc, &state) };
> +
> +    T::atomic_enable(commit);
> +}
> +
> +unsafe extern "C" fn atomic_disable_callback<T: DriverCrtc>(
> +    crtc: *mut bindings::drm_crtc,
> +    state: *mut bindings::drm_atomic_state,
> +) {
> +    // SAFETY:
> +    // - We're guaranteed `crtc` points to a valid instance of
> `drm_crtc`
> +    // - We're guaranteed that `crtc` is of type `Plane<T>` by
> `DriverPlane`s type invariants.
> +    let crtc = unsafe { Crtc::from_raw(crtc) };
> +
> +    // SAFETY: We're guaranteed that `state` points to a valid
> `drm_crtc_state` by DRM
> +    let state = unsafe {
> AtomicStateMutator::new(NonNull::new_unchecked(state)) };
> +
> +    // SAFETY:
> +    // - Since we're in the atomic_disable callback, we're
> guaranteed by DRM that both the old and
> +    //   new crtc state are present in this atomic state.
> +    // - We just created the state mutator above, so other mutators
> cannot be taken out on the crtc
> +    //   state yet.
> +    let commit = unsafe { CrtcAtomicCommit::new(crtc, &state) };
> +
> +    T::atomic_disable(commit);
> +}
> diff --git a/rust/kernel/drm/kms/encoder.rs
> b/rust/kernel/drm/kms/encoder.rs
> new file mode 100644
> index 000000000000..5f860faf8b61
> --- /dev/null
> +++ b/rust/kernel/drm/kms/encoder.rs
> @@ -0,0 +1,409 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! DRM encoders.
> +//!
> +//! C header:
> [`include/drm/drm_encoder.h`](srctree/include/drm/drm_encoder.h)
> +
> +use super::{
> +    KmsDriver, ModeObject, ModeObjectVtable, NewKmsDevice, Probing,
> StaticModeObject, Sealed
> +};
> +use crate::{
> +    alloc::KBox,
> +    drm::device::Device,
> +    error::to_result,
> +    prelude::*,
> +    types::{NotThreadSafe, Opaque},
> +};
> +use bindings;
> +use core::{
> +    marker::*,
> +    mem,
> +    ops::Deref,
> +    ptr::null,
> +};
> +use macros::paste;
> +
> +/// A macro for generating our type ID enumerator.
> +macro_rules! declare_encoder_types {
> +    ($( $oldname:ident as $newname:ident ),+) => {
> +        #[repr(i32)]
> +        #[non_exhaustive]
> +        #[derive(Copy, Clone, PartialEq, Eq)]
> +        /// An enumerator for all possible [`Encoder`] type IDs.
> +        pub enum Type {
> +            // Note: bindgen defaults the macro values to u32 and
> not i32, but DRM takes them as an
> +            // i32 - so just do the conversion here
> +            $(
> +                #[doc = concat!("The encoder type ID for a ",
> stringify!($newname), " encoder.")]
> +                $newname =
> paste!(crate::bindings::[<DRM_MODE_ENCODER_ $oldname>]) as i32
> +            ),+
> +        }
> +    };
> +}
> +
> +declare_encoder_types! {
> +    NONE     as None,
> +    DAC      as Dac,
> +    TMDS     as Tmds,
> +    LVDS     as Lvds,
> +    VIRTUAL  as Virtual,
> +    DSI      as Dsi,
> +    DPMST    as DpMst,
> +    DPI      as Dpi
> +}
> +
> +/// The main trait for implementing the [`struct drm_encoder`] API
> for [`Encoder`].
> +///
> +/// Any KMS driver should have at least one implementation of this
> type, which allows them to create
> +/// [`Encoder`] objects. Additionally, a driver may store driver-
> private data within the type that
> +/// implements [`DriverEncoder`] - and it will be made available
> when using a fully typed
> +/// [`Encoder`] object.
> +///
> +/// # Invariants
> +///
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_encoder`] pointers are contained within a
> [`Encoder<Self>`].
> +///
> +/// [`struct drm_encoder`]: srctree/include/drm/drm_encoder.h
> +#[vtable]
> +pub trait DriverEncoder: Send + Sync + Sized {
> +    /// The generated C vtable for this [`DriverEncoder`]
> implementation.
> +    const OPS: &'static DriverEncoderOps = &DriverEncoderOps {
> +        funcs: bindings::drm_encoder_funcs {
> +            reset: None,
> +            destroy: Some(encoder_destroy_callback::<Self>),
> +            late_register: None,
> +            early_unregister: None,
> +            debugfs_init: None,
> +        },
> +        helper_funcs: bindings::drm_encoder_helper_funcs {
> +            dpms: None,
> +            mode_valid: None,
> +            mode_fixup: None,
> +            prepare: None,
> +            mode_set: None,
> +            commit: None,
> +            detect: None,
> +            enable: None,
> +            disable: None,
> +            atomic_check: None,
> +            atomic_enable: None,
> +            atomic_disable: None,
> +            atomic_mode_set: None,
> +        },
> +    };
> +
> +    /// The parent driver for this drm_encoder implementation
> +    type Driver: KmsDriver;
> +
> +    /// The type to pass to the `args` field of
> [`UnregisteredEncoder::new`].
> +    ///
> +    /// This type will be made available in in the `args` argument
> of [`Self::new`]. Drivers which
> +    /// don't need this can simply pass [`()`] here.
> +    type Args;
> +
> +    /// The constructor for creating a [`Encoder`] using this
> [`DriverEncoder`] implementation.
> +    ///
> +    /// Drivers may use this to instantiate their [`DriverEncoder`]
> object.
> +    fn new(device: &Device<Self::Driver>, args: Self::Args) -> impl
> PinInit<Self, Error>;
> +}
> +
> +/// The generated C vtable for a [`DriverEncoder`].
> +///
> +/// This type is created internally by DRM.
> +pub struct DriverEncoderOps {
> +    funcs: bindings::drm_encoder_funcs,
> +    helper_funcs: bindings::drm_encoder_helper_funcs,
> +}
> +
> +/// A trait implemented by any type that acts as a [`struct
> drm_encoder`] interface.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// # Safety
> +///
> +/// [`as_raw()`] must always return a valid pointer to a [`struct
> drm_encoder`].
> +///
> +/// [`struct drm_encoder`]: srctree/include/drm/drm_encoder.h
> +/// [`as_raw()`]: AsRawEncoder::as_raw()
> +pub unsafe trait AsRawEncoder {
> +    /// Return the raw `bindings::drm_encoder` for this DRM encoder.
> +    ///
> +    /// Drivers should never use this directly
> +    fn as_raw(&self) -> *mut bindings::drm_encoder;
> +
> +    /// Convert a raw `bindings::drm_encoder` pointer into an object
> of this type.
> +    ///
> +    /// # Safety
> +    ///
> +    /// Callers promise that `ptr` points to a valid instance of
> this type
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encoder) -> &'a
> Self;
> +}
> +
> +/// The main interface for a [`struct drm_encoder`].
> +///
> +/// This type is the main interface for dealing with DRM encoders.
> In addition, it also allows
> +/// immutable access to whatever private data is contained within an
> implementor's
> +/// [`DriverEncoder`] type.
> +///
> +/// # Invariants
> +///
> +/// - `encoder` and `inner` are initialized for as long as this
> object is made available to users.
> +/// - The data layout of this structure begins with [`struct
> drm_encoder`].
> +///
> +/// [`struct drm_encoder`]: srctree/include/drm/drm_encoder.h
> +#[repr(C)]
> +#[pin_data]
> +pub struct Encoder<T: DriverEncoder> {
> +    /// The FFI drm_encoder object
> +    encoder: Opaque<bindings::drm_encoder>,
> +    /// The driver's private inner data
> +    #[pin]
> +    inner: T,
> +    #[pin]
> +    _p: PhantomPinned,
> +}
> +
> +impl<T: DriverEncoder> Sealed for Encoder<T> {}
> +
> +// SAFETY: Our interface is thread-safe.
> +unsafe impl<T: DriverEncoder> Send for Encoder<T> {}
> +// SAFETY: Our interface is thread-safe.
> +unsafe impl<T: DriverEncoder> Sync for Encoder<T> {}
> +
> +// SAFETY: We don't expose Encoder<T> to users before `base` is
> initialized in ::new(), so
> +// `raw_mode_obj` always returns a valid pointer to a
> bindings::drm_mode_object.
> +unsafe impl<T: DriverEncoder> ModeObject for Encoder<T> {
> +    type Driver = T::Driver;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: DRM encoders exist for as long as the device
> does, so this pointer is always
> +        // valid
> +        unsafe { Device::from_raw((*self.encoder.get()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose Encoder<T> to users before it's
> initialized, so `base` is always
> +        // initialized
> +        unsafe { &raw mut (*self.encoder.get()).base }
> +    }
> +}
> +
> +// SAFETY: Encoders do not have a refcount
> +unsafe impl<T: DriverEncoder> StaticModeObject for Encoder<T> {}
> +
> +impl<T: DriverEncoder> Deref for Encoder<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.inner
> +    }
> +}
> +
> +// SAFETY:
> +// - Via our type invariants our data layout starts with
> `drm_encoder`.
> +// - Since we don't expose `Encoder` to users befre it has been
> initialized, this and our data
> +//   layout ensure that `as_raw()` always returns a valid pointer to
> a `drm_encoder`.
> +unsafe impl<T: DriverEncoder> AsRawEncoder for Encoder<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_encoder {
> +        self.encoder.get()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encoder) -> &'a
> Self {
> +        // SAFETY: Our data layout is starts with to
> `bindings::drm_encoder`
> +        unsafe { &*ptr.cast() }
> +    }
> +}
> +
> +// SAFETY: `funcs` is initialized when the encoder is allocated
> +unsafe impl<T: DriverEncoder> ModeObjectVtable for Encoder<T> {
> +    type Vtable = bindings::drm_encoder_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        // SAFETY: `as_raw()` always returns a valid pointer to an
> encoder
> +        unsafe { *self.as_raw() }.funcs
> +    }
> +}
> +
> +impl<T: DriverEncoder> Encoder<T> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <'a, D>(&'a OpaqueEncoder<D>) -> &'a Self;
> +        use
> +            T as DriverEncoder,
> +            D as KmsDriver<Encoder = ...>
> +    }
> +}
> +
> +/// A [`Encoder`] that has not yet been registered with userspace.
> +///
> +/// KMS registration is single-threaded, so this object is not
> thread-safe.
> +///
> +/// # Invariants
> +///
> +/// - This object can only exist before its respective KMS device
> has been registered.
> +/// - Otherwise, it inherits all invariants of [`Encoder`] and has
> an identical data layout.
> +pub struct UnregisteredEncoder<T: DriverEncoder>(Encoder<T>,
> NotThreadSafe);
> +
> +// SAFETY: We inherit all relevant invariants of `Encoder`
> +unsafe impl<T: DriverEncoder> AsRawEncoder for
> UnregisteredEncoder<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_encoder {
> +        self.0.as_raw()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encoder) -> &'a
> Self {
> +        // SAFETY: This is another from_raw() call, so this function
> shares the same safety contract
> +        let encoder = unsafe { Encoder::<T>::from_raw(ptr) };
> +
> +        // SAFETY: Our data layout is identical via our type
> invariants.
> +        unsafe { mem::transmute(encoder) }
> +    }
> +}
> +
> +impl<T: DriverEncoder> Deref for UnregisteredEncoder<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.0.inner
> +    }
> +}
> +
> +impl<T: DriverEncoder> UnregisteredEncoder<T> {
> +    /// Construct a new [`UnregisteredEncoder`].
> +    ///
> +    /// A driver may use this from their
> [`KmsDriver::create_objects`] callback in order to
> +    /// construct new [`UnregisteredEncoder`] objects.
> +    ///
> +    /// [`KmsDriver::create_objects`]:
> kernel::drm::kms::KmsDriver::create_objects
> +    pub fn new<'a, 'b: 'a>(
> +        dev: &'a NewKmsDevice<'a, T::Driver, Probing>,
> +        type_: Type,
> +        possible_crtcs: u32,
> +        possible_clones: u32,
> +        name: Option<&CStr>,
> +        args: T::Args,
> +    ) -> Result<&'b Self> {
> +        let this: Pin<KBox<Encoder<T>>> = KBox::try_pin_init(
> +            try_pin_init!(Encoder {
> +                encoder: Opaque::new(bindings::drm_encoder {
> +                    helper_private: &T::OPS.helper_funcs,
> +                    possible_crtcs,
> +                    possible_clones,
> +                    ..Default::default()
> +                }),
> +                inner <- T::new(dev, args),
> +                _p: PhantomPinned
> +            }),
> +            GFP_KERNEL,
> +        )?;
> +
> +        // SAFETY:
> +        // - `dev` is responsible for destroying the encoder and
> thus outlives us.
> +        // - as_raw() returns valid pointers for each type here
> +        // - This initializes `this`
> +        // - Our type is proof that this is being called before KMS
> device registration
> +        // - `name` is optional and will be auto-generated by DRM if
> passed as NULL
> +        to_result(unsafe {
> +            bindings::drm_encoder_init(
> +                dev.as_raw(),
> +                this.as_raw(),
> +                &T::OPS.funcs,
> +                type_ as _,
> +                name.map_or(null(), |n| n.as_char_ptr()),
> +            )
> +        })?;
> +
> +        // SAFETY: We don't move anything
> +        let this = unsafe { Pin::into_inner_unchecked(this) };
> +
> +        // We'll re-assemble the box in encoder_destroy_callback()
> +        let this = KBox::into_raw(this);
> +
> +        // UnregisteredEncoder has an equivalent data layout
> +        let this: *mut Self = this.cast();
> +
> +        // SAFETY: We just allocated the encoder above, so this
> pointer must be valid
> +        Ok(unsafe { &*this })
> +    }
> +}
> +
> +/// A [`struct drm_encoder`] without a known [`DriverEncoder`]
> implementation.
> +///
> +/// This is mainly for situations where our bindings can't infer the
> [`DriverEncoder`] implementation
> +/// for a [`struct drm_encoder`] automatically. It is identical to
> [`Encoder`], except that it does not
> +/// provide access to the driver's private data.
> +///
> +/// # Invariants
> +///
> +/// Same as [`Encoder`].
> +#[repr(transparent)]
> +pub struct OpaqueEncoder<T: KmsDriver> {
> +    encoder: Opaque<bindings::drm_encoder>,
> +    _p: PhantomData<T>,
> +}
> +
> +impl<T: KmsDriver> Sealed for OpaqueEncoder<T> {}
> +
> +// SAFETY: All of our encoder interfaces are thread-safe
> +unsafe impl<T: KmsDriver> Send for OpaqueEncoder<T> {}
> +
> +// SAFETY: All of our encoder interfaces are thread-safe
> +unsafe impl<T: KmsDriver> Sync for OpaqueEncoder<T> {}
> +
> +// SAFETY: We don't expose OpaqueEncoder<T> to users before `base`
> is initialized in
> +// OpaqueEncoder::new(), so `raw_mode_obj` always returns a valid
> poiner to a
> +// bindings::drm_mode_object.
> +unsafe impl<T: KmsDriver> ModeObject for OpaqueEncoder<T> {
> +    type Driver = T;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: DRM encoders exist for as long as the device
> does, so this pointer is always
> +        // valid
> +        unsafe { Device::from_raw((*self.encoder.get()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose Encoder<T> to users before it's
> initialized, so `base` is always
> +        // initialized
> +        unsafe { &raw mut (*self.encoder.get()).base }
> +    }
> +}
> +
> +// SAFETY: Encoders do not have a refcount
> +unsafe impl<T: KmsDriver> StaticModeObject for OpaqueEncoder<T> {}
> +
> +// SAFETY:
> +// - Via our type variants our data layout is identical to  with
> `drm_encoder`
> +// - Since we don't expose `Encoder` to users before it has been
> initialized, this and our data
> +//   layout ensure that `as_raw()` always returns a valid pointer to
> a `drm_encoder`.
> +unsafe impl<T: KmsDriver> AsRawEncoder for OpaqueEncoder<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_encoder {
> +        self.encoder.get()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encoder) -> &'a
> Self {
> +        // SAFETY: Our data layout is identical to
> `bindings::drm_encoder`
> +        unsafe { &*ptr.cast() }
> +    }
> +}
> +
> +// SAFETY: `funcs` is initialized when the encoder is allocated
> +unsafe impl<T: KmsDriver> ModeObjectVtable for OpaqueEncoder<T> {
> +    type Vtable = bindings::drm_encoder_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        // SAFETY: `as_raw()` always returns a valid pointer to an
> encoder
> +        unsafe { *self.as_raw() }.funcs
> +    }
> +}
> +
> +unsafe extern "C" fn encoder_destroy_callback<T: DriverEncoder>(
> +    encoder: *mut bindings::drm_encoder,
> +) {
> +    // SAFETY: DRM guarantees that `encoder` points to a valid
> initialized `drm_encoder`.
> +    unsafe { bindings::drm_encoder_cleanup(encoder) };
> +
> +    // SAFETY:
> +    // - DRM guarantees we are now the only one with access to this
> [`drm_encoder`].
> +    // - This cast is safe via `DriverEncoder`s type invariants.
> +    unsafe { drop(KBox::from_raw(encoder as *mut Encoder<T>)) };
> +}
> diff --git a/rust/kernel/drm/kms/framebuffer.rs
> b/rust/kernel/drm/kms/framebuffer.rs
> new file mode 100644
> index 000000000000..54d0391388a9
> --- /dev/null
> +++ b/rust/kernel/drm/kms/framebuffer.rs
> @@ -0,0 +1,70 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! DRM framebuffers.
> +//!
> +//! C header:
> [`include/drm/drm_framebuffer.h`](srctree/include/drm/drm_framebuffer
> .h)
> +
> +use super::{KmsDriver, ModeObject, Sealed};
> +use crate::{drm::device::Device, types::*};
> +use bindings;
> +use core::{marker::*, ptr};
> +
> +/// The main interface for [`struct drm_framebuffer`].
> +///
> +/// # Invariants
> +///
> +/// - `self.0` is initialized for as long as this object is exposed
> to users.
> +/// - This type has an identical data layout to [`struct
> drm_framebuffer`]
> +///
> +/// [`struct drm_framebuffer`]:
> srctree/include/drm/drm_framebuffer.h
> +#[repr(transparent)]
> +pub struct Framebuffer<T:
> KmsDriver>(Opaque<bindings::drm_framebuffer>, PhantomData<T>);
> +
> +// SAFETY:
> +// - `self.0` is initialized for as long as this object is exposed
> to users
> +// - `base` is initialized by DRM when `self.0` is initialized, thus
> `raw_mode_obj()` always returns
> +//   a valid pointer.
> +unsafe impl<T: KmsDriver> ModeObject for Framebuffer<T> {
> +    type Driver = T;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: `dev` points to an initialized `struct
> drm_device` for as long as this type is
> +        // initialized
> +        unsafe { Device::from_raw((*self.0.get()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose Framebuffer<T> to users before
> its initialized, so `base` is
> +        // always initialized
> +        unsafe { &raw mut (*self.0.get()).base }
> +    }
> +}
> +
> +// SAFETY: References to framebuffers are safe to be accessed from
> any thread
> +unsafe impl<T: KmsDriver> Send for Framebuffer<T> {}
> +// SAFETY: References to framebuffers are safe to be accessed from
> any thread
> +unsafe impl<T: KmsDriver> Sync for Framebuffer<T> {}
> +
> +// For implementing ModeObject
> +impl<T: KmsDriver> Sealed for Framebuffer<T> {}
> +
> +impl<T: KmsDriver> PartialEq for Framebuffer<T> {
> +    fn eq(&self, other: &Self) -> bool {
> +        ptr::eq(self.0.get(), other.0.get())
> +    }
> +}
> +impl<T: KmsDriver> Eq for Framebuffer<T> {}
> +
> +impl<T: KmsDriver> Framebuffer<T> {
> +    /// Convert a raw pointer to a `struct drm_framebuffer` into a
> [`Framebuffer`]
> +    ///
> +    /// # Safety
> +    ///
> +    /// The caller guarantews that `ptr` points to a initialized
> `struct drm_framebuffer` for at
> +    /// least the entire lifetime of `'a`.
> +    #[inline]
> +    pub(super) unsafe fn from_raw<'a>(ptr: *const
> bindings::drm_framebuffer) -> &'a Self {
> +        // SAFETY: Our data layout is identical to drm_framebuffer
> +        unsafe { &*ptr.cast() }
> +    }
> +}
> diff --git a/rust/kernel/drm/kms/modes.rs
> b/rust/kernel/drm/kms/modes.rs
> new file mode 100644
> index 000000000000..0e8dc434487d
> --- /dev/null
> +++ b/rust/kernel/drm/kms/modes.rs
> @@ -0,0 +1,76 @@
> +// SPDX-License-Identifier: GPL-2.0
> +use bindings;
> +
> +use crate::types::Opaque;
> +
> +/// DRM kernel-internal display mode structure.
> +///
> +/// This structure contains various resolution and timing
> information for a given display mode in
> +/// DRM.
> +///
> +/// # Invariants
> +///
> +/// - The data layout of this structure is guaranteed to be
> equivalent to that of `struct
> +///   drm_display_mode`.
> +/// - We ensure through our bindings that rust's data aliasing rules
> are maintained, ensuring it is
> +///   safe to read any fields inside of `self.inner`.
> +#[repr(transparent)]
> +pub struct DisplayMode {
> +    inner: Opaque<bindings::drm_display_mode>,
> +}
> +
> +// SAFETY: Our bindings are thread-safe via our type invariants.
> +unsafe impl Send for DisplayMode {}
> +// SAFETY: Our bindings are thread-safe via our type invariants.
> +unsafe impl Sync for DisplayMode {}
> +
> +impl DisplayMode {
> +    /// Convert a raw pointer to a `struct drm_display_mode` into an
> immutable [`DisplayMode`] ref.
> +    ///
> +    /// # SAFETY
> +    ///
> +    /// - The caller guarantees that `self_ptr` points to a valid
> initialized `struct
> +    ///   drm_display_mode`.
> +    /// - The caller must ensure that rust's data aliasing rules
> will not be broken for the lifetime
> +    ///   of `'a`, e.g. no mutable references may exist while
> immutable references exist to Self.
> +    #[inline]
> +    pub(crate) unsafe fn as_ref<'a>(self_ptr: *const
> bindings::drm_display_mode) -> &'a Self {
> +        // SAFETY: The pointer is valid via our safety contract, and
> the data layout of this struct
> +        // is equivalent to `Self` via our type invariants.
> +        unsafe { &*self_ptr.cast() }
> +    }
> +
> +    /// Return a raw pointer to the `struct drm_display_mode`
> contained within this [`DisplayMode`].
> +    #[inline]
> +    pub(crate) fn as_raw(&self) -> *const bindings::drm_display_mode
> {
> +        self.inner.get().cast_const()
> +    }
> +
> +    /// Retrieve the pixel clock for the adjusted display mode in
> kHz.
> +    #[inline]
> +    pub fn crtc_clock(&self) -> i32 {
> +        // SAFETY: Reading these fields is safe via our type
> invariants
> +        unsafe { (*self.as_raw()).crtc_clock }
> +    }
> +
> +    /// Retrieve the start of the vertical sync period for the
> adjusted display mode.
> +    #[inline]
> +    pub fn crtc_vblank_start(&self) -> u16 {
> +        unsafe { (*self.as_raw()).crtc_vblank_start }
> +    }
> +
> +    /// Retrieve the end of the vertical sync period for the
> adjusted display mode.
> +    #[inline]
> +    pub fn crtc_vblank_end(&self) -> u16 {
> +        // SAFETY: Reading these fields is safe via our type
> invariants
> +        unsafe { (*self.as_raw()).crtc_vblank_end }
> +    }
> +
> +    /// Retrieve the number of vertical scanlines for a full scanout
> frame in this adjusted display
> +    /// mode.
> +    #[inline]
> +    pub fn crtc_vtotal(&self) -> u16 {
> +        // SAFETY: Reading these fields is safe via our type
> invariants
> +        unsafe { (*self.as_raw()).crtc_vtotal }
> +    }
> +}
> diff --git a/rust/kernel/drm/kms/plane.rs
> b/rust/kernel/drm/kms/plane.rs
> new file mode 100644
> index 000000000000..661d82273099
> --- /dev/null
> +++ b/rust/kernel/drm/kms/plane.rs
> @@ -0,0 +1,1095 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! DRM display planes.
> +//!
> +//! C header:
> [`include/drm/drm_plane.h`](srctree/include/drm/drm_plane.h)
> +
> +use super::{
> +    atomic::*, crtc::*, framebuffer::*, KmsDriver, ModeObject,
> ModeObjectVtable, StaticModeObject,
> +    NewKmsDevice, Probing, Sealed
> +};
> +use crate::{
> +    alloc::KBox,
> +    bindings,
> +    drm::{device::Device, fourcc::*},
> +    error::{from_result, to_result, Error},
> +    prelude::*,
> +    types::{NotThreadSafe, Opaque},
> +};
> +use core::{
> +    cell::Cell,
> +    marker::*,
> +    mem::{self, ManuallyDrop},
> +    ops::*,
> +    pin::Pin,
> +    ptr::{null, null_mut, NonNull},
> +};
> +
> +/// The main trait for implementing the [`struct drm_plane`] API for
> [`Plane`].
> +///
> +/// Any KMS driver should have at least one implementation of this
> type, which allows them to create
> +/// [`Plane`] objects. Additionally, a driver may store driver-
> private data within the type that
> +/// implements [`DriverPlane`] - and it will be made available when
> using a fully typed [`Plane`]
> +/// object.
> +///
> +/// # Invariants
> +///
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_plane`] pointers are contained within a
> [`Plane<Self>`].
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_plane_state`] pointers are contained within a
> [`PlaneState<Self::State>`].
> +///
> +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h
> +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
> +#[vtable]
> +pub trait DriverPlane: Send + Sync + Sized {
> +    /// The generated C vtable for this [`DriverPlane`]
> implementation.
> +    const OPS: &'static DriverPlaneOps = &DriverPlaneOps {
> +        funcs: bindings::drm_plane_funcs {
> +            update_plane:
> Some(bindings::drm_atomic_helper_update_plane),
> +            disable_plane:
> Some(bindings::drm_atomic_helper_disable_plane),
> +            destroy: Some(plane_destroy_callback::<Self>),
> +            reset: Some(plane_reset_callback::<Self>),
> +            set_property: None,
> +            atomic_duplicate_state:
> Some(atomic_duplicate_state_callback::<Self::State>),
> +            atomic_destroy_state:
> Some(atomic_destroy_state_callback::<Self::State>),
> +            atomic_set_property: None,
> +            atomic_get_property: None,
> +            late_register: None,
> +            early_unregister: None,
> +            atomic_print_state: None,
> +            format_mod_supported: None,
> +            format_mod_supported_async: None,
> +        },
> +
> +        helper_funcs: bindings::drm_plane_helper_funcs {
> +            prepare_fb: None,
> +            cleanup_fb: None,
> +            begin_fb_access: None,
> +            end_fb_access: None,
> +            atomic_check: if Self::HAS_ATOMIC_CHECK {
> +                Some(atomic_check_callback::<Self>)
> +            } else {
> +                None
> +            },
> +            atomic_update: if Self::HAS_ATOMIC_UPDATE {
> +                Some(atomic_update_callback::<Self>)
> +            } else {
> +                None
> +            },
> +            atomic_enable: None,
> +            atomic_disable: None,
> +            atomic_async_check: None,
> +            atomic_async_update: None,
> +            panic_flush: None,
> +            get_scanout_buffer: None,
> +        },
> +    };
> +
> +    /// The type to pass to the `args` field of
> [`UnregisteredPlane::new`].
> +    ///
> +    /// This type will be made available in in the `args` argument
> of [`Self::new`]. Drivers which
> +    /// don't need this can simply pass [`()`] here.
> +    type Args;
> +
> +    /// The parent [`KmsDriver`] implementation.
> +    type Driver: KmsDriver;
> +
> +    /// The [`DriverPlaneState`] implementation for this
> [`DriverPlane`].
> +    ///
> +    /// See [`DriverPlaneState`] for more info.
> +    type State: DriverPlaneState;
> +
> +    /// The constructor for creating a [`Plane`] using this
> [`DriverPlane`] implementation.
> +    ///
> +    /// Drivers may use this to instantiate their [`DriverPlane`]
> object.
> +    fn new(device: &Device<Self::Driver>, args: Self::Args) -> impl
> PinInit<Self, Error>;
> +
> +    /// The optional [`drm_plane_helper_funcs.atomic_update`] hook
> for this plane.
> +    ///
> +    /// Drivers may use this to customize the atomic update phase of
> their [`Plane`] objects. If not
> +    /// specified, this function is a no-op.
> +    ///
> +    /// [`drm_plane_helper_funcs.atomic_update`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +    fn atomic_update(_commit: PlaneAtomicCommit<'_, Self>) {
> +        build_error::build_error("This should not be reachable")
> +    }
> +
> +    /// The optional [`drm_plane_helper_funcs.atomic_check`] hook
> for this plane.
> +    ///
> +    /// Drivers may use this to customize the atomic check phase of
> their [`Plane`] objects. The
> +    /// result of this function determines whether the atomic check
> passed or failed.
> +    ///
> +    /// [`drm_plane_helper_funcs.atomic_check`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +    fn atomic_check(_check: PlaneAtomicCheck<'_, Self>) -> Result {
> +        build_error::build_error("This should not be reachable")
> +    }
> +}
> +
> +/// The generated C vtable for a [`DriverPlane`].
> +///
> +/// This type is created internally by DRM.
> +pub struct DriverPlaneOps {
> +    funcs: bindings::drm_plane_funcs,
> +    helper_funcs: bindings::drm_plane_helper_funcs,
> +}
> +
> +#[derive(Copy, Clone, Debug, PartialEq, Eq)]
> +#[repr(u32)]
> +/// An enumerator describing a type of [`Plane`].
> +///
> +/// This is mainly just relevant for DRM legacy drivers.
> +///
> +/// # Invariants
> +///
> +/// This type is identical to [`enum drm_plane_type`].
> +///
> +/// [`enum drm_plane_type`]: srctree/include/drm/drm_plane.h
> +pub enum Type {
> +    /// Overlay planes represent all non-primary, non-cursor planes.
> Some drivers refer to these
> +    /// types of planes as "sprites" internally.
> +    Overlay = bindings::drm_plane_type_DRM_PLANE_TYPE_OVERLAY,
> +
> +    /// A primary plane attached to a CRTC that is the most likely
> to be able to light up the CRTC
> +    /// when no scaling/cropping is used, and the plane covers the
> whole CRTC.
> +    Primary = bindings::drm_plane_type_DRM_PLANE_TYPE_PRIMARY,
> +
> +    /// A cursor plane attached to a CRTC that is more likely to be
> enabled when no scaling/cropping
> +    /// is used, and the framebuffer has the size indicated by
> [`ModeConfigInfo::max_cursor`].
> +    ///
> +    /// [`ModeConfigInfo::max_cursor`]:
> crate::drm::kms::ModeConfigInfo
> +    Cursor = bindings::drm_plane_type_DRM_PLANE_TYPE_CURSOR,
> +}
> +
> +/// The main interface for a [`struct drm_plane`].
> +///
> +/// This type is the main interface for dealing with DRM planes. In
> addition, it also allows
> +/// immutable access to whatever private data is contained within an
> implementor's [`DriverPlane`]
> +/// type.
> +///
> +/// # Invariants
> +///
> +/// - `plane` and `inner` are initialized for as long as this object
> is made available to users.
> +/// - The data layout of this structure begins with [`struct
> drm_plane`].
> +/// - The atomic state for this type can always be assumed to be of
> type [`PlaneState<T::State>`].
> +///
> +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h
> +#[repr(C)]
> +#[pin_data]
> +pub struct Plane<T: DriverPlane> {
> +    /// The FFI drm_plane object
> +    plane: Opaque<bindings::drm_plane>,
> +    /// The driver's private inner data
> +    #[pin]
> +    inner: T,
> +    #[pin]
> +    _p: PhantomPinned,
> +}
> +
> +impl<T: DriverPlane> Sealed for Plane<T> {}
> +
> +impl<T: DriverPlane> Deref for Plane<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.inner
> +    }
> +}
> +
> +// SAFETY: `funcs` is initialized when the plane is allocated
> +unsafe impl<T: DriverPlane> ModeObjectVtable for Plane<T> {
> +    type Vtable = bindings::drm_plane_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        // SAFETY: `as_raw()` always returns a valid plane pointer
> +        unsafe { *self.as_raw() }.funcs
> +    }
> +}
> +
> +impl<T: DriverPlane> Plane<T> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <'a, D>(&'a OpaquePlane<D>) -> &'a Self;
> +        use
> +            T as DriverPlane,
> +            D as KmsDriver<Plane = ...>
> +    }
> +}
> +
> +/// A [`Plane`] that has not yet been registered with userspace.
> +///
> +/// KMS registration is single-threaded, so this object is not
> thread-safe.
> +///
> +/// # Invariants
> +///
> +/// - This object can only exist before its respective KMS device
> has been registered.
> +/// - Otherwise, it inherits all invariants of [`Plane`] and has an
> identical data layout.
> +pub struct UnregisteredPlane<T: DriverPlane>(Plane<T>,
> NotThreadSafe);
> +
> +// SAFETY: We share the invariants of `Plane`
> +unsafe impl<T: DriverPlane> AsRawPlane for UnregisteredPlane<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_plane {
> +        self.0.as_raw()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane) -> &'a
> Self {
> +        // SAFETY: This is another from_raw() call, so this function
> shares the same safety contract
> +        let plane = unsafe { Plane::<T>::from_raw(ptr) };
> +
> +        // SAFETY: Our data layout is identical via our type
> invariants.
> +        unsafe { mem::transmute(plane) }
> +    }
> +}
> +
> +impl<T: DriverPlane> Deref for UnregisteredPlane<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.0.inner
> +    }
> +}
> +
> +impl<T: DriverPlane> UnregisteredPlane<T> {
> +    /// Construct a new [`UnregisteredPlane`].
> +    ///
> +    /// A driver may use this from their
> [`KmsDriver::create_objects`] callback in order to
> +    /// construct new [`UnregisteredPlane`] objects.
> +    ///
> +    /// [`KmsDriver::create_objects`]:
> kernel::drm::kms::KmsDriver::create_objects
> +    pub fn new<'a, 'b: 'a>(
> +        dev: &'a NewKmsDevice<'a, T::Driver, Probing>,
> +        possible_crtcs: u32,
> +        formats: &[u32],
> +        format_modifiers: Option<&[u64]>,
> +        type_: Type,
> +        name: Option<&CStr>,
> +        args: T::Args,
> +    ) -> Result<&'b Self> {
> +        let this: Pin<KBox<Plane<T>>> = KBox::try_pin_init(
> +            try_pin_init!(Plane {
> +                plane: Opaque::new(bindings::drm_plane {
> +                    helper_private: &T::OPS.helper_funcs,
> +                    ..Default::default()
> +                }),
> +                inner <- T::new(dev, args),
> +                _p: PhantomPinned
> +            }),
> +            GFP_KERNEL,
> +        )?;
> +
> +        // TODO: Move this over to using collect() someday
> +        // Create a modifiers array with the sentinel for passing to
> DRM
> +        let format_modifiers_raw;
> +        if let Some(modifiers) = format_modifiers {
> +            let mut raw = KVec::with_capacity(modifiers.len() + 1,
> GFP_KERNEL)?;
> +            for modifier in modifiers {
> +                raw.push(*modifier, GFP_KERNEL)?;
> +            }
> +            raw.push(FORMAT_MOD_INVALID, GFP_KERNEL)?;
> +
> +            format_modifiers_raw = Some(raw);
> +        } else {
> +            format_modifiers_raw = None;
> +        }
> +
> +        // SAFETY:
> +        // - `dev` handles destroying the plane, and thus will
> outlive us and always be valid.
> +        // - We just allocated `this`, and we won't move it since
> it's pinned
> +        // - We just allocated the `format_modifiers_raw` vec and
> added the sentinel DRM expects
> +        //   above
> +        // - `drm_universal_plane_init` will memcpy() the following
> parameters into its own storage,
> +        //   so it's safe for them to become inaccessible after this
> call returns:
> +        //   - `formats`
> +        //   - `format_modifiers_raw`
> +        //   - `name`
> +        // - `type_` is equivalent to `drm_plane_type` via its type
> invariants.
> +        to_result(unsafe {
> +            bindings::drm_universal_plane_init(
> +                dev.as_raw(),
> +                this.as_raw(),
> +                possible_crtcs,
> +                &T::OPS.funcs,
> +                formats.as_ptr(),
> +                formats.len() as _,
> +                format_modifiers_raw.map_or(null(), |f| f.as_ptr()),
> +                type_ as _,
> +                name.map_or(null(), |n| n.as_char_ptr()),
> +            )
> +        })?;
> +
> +        // SAFETY: We don't move anything
> +        let this = unsafe { Pin::into_inner_unchecked(this) };
> +
> +        // We'll re-assemble the box in plane_destroy_callback()
> +        let this = KBox::into_raw(this);
> +
> +        // UnregisteredPlane has an equivalent data layout
> +        let this: *mut Self = this.cast();
> +
> +        // SAFETY: We just allocated the plane above, so this
> pointer must be valid
> +        Ok(unsafe { &*this })
> +    }
> +}
> +
> +/// A trait implemented by any type that acts as a [`struct
> drm_plane`] interface.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// # Safety
> +///
> +/// [`as_raw()`] must always return a valid pointer to an
> initialized [`struct drm_plane`].
> +///
> +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h
> +/// [`as_raw()`]: AsRawPlane::as_raw()
> +pub unsafe trait AsRawPlane {
> +    /// Return the raw `bindings::drm_plane` for this DRM plane.
> +    ///
> +    /// Drivers should never use this directly.
> +    fn as_raw(&self) -> *mut bindings::drm_plane;
> +
> +    /// Convert a raw `bindings::drm_plane` pointer into an object
> of this type.
> +    ///
> +    /// # Safety
> +    ///
> +    /// Callers promise that `ptr` points to a valid instance of
> this type
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane) -> &'a
> Self;
> +}
> +
> +// SAFETY:
> +// - Via our type variants our data layout starts with `drm_plane`
> +// - Since we don't expose `plane` to users before it has been
> initialized, this and our data
> +//   layout ensure that `as_raw()` always returns a valid pointer to
> a `drm_plane`.
> +unsafe impl<T: DriverPlane> AsRawPlane for Plane<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_plane {
> +        self.plane.get()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane) -> &'a
> Self {
> +        // Our data layout start with `bindings::drm_plane`.
> +        let ptr: *mut Self = ptr.cast();
> +
> +        // SAFETY: Our safety contract requires that `ptr` point to
> a valid intance of `Self`.
> +        unsafe { &*ptr }
> +    }
> +}
> +
> +// SAFETY: We only expose this object to users directly after
> KmsDriver::create_objects has been
> +// called.
> +unsafe impl<T: DriverPlane> ModesettablePlane for Plane<T> {
> +    type State = PlaneState<T::State>;
> +}
> +
> +// SAFETY: We don't expose Plane<T> to users before `base` is
> initialized in ::new(), so
> +// `raw_mode_obj` always returns a valid pointer to a
> bindings::drm_mode_object.
> +unsafe impl<T: DriverPlane> ModeObject for Plane<T> {
> +    type Driver = T::Driver;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: DRM planes exist for as long as the device does,
> so this pointer is always valid
> +        unsafe { Device::from_raw((*self.as_raw()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose DRM planes to users before `base`
> is initialized
> +        unsafe { &raw mut (*self.as_raw()).base }
> +    }
> +}
> +
> +// SAFETY: Planes do not have a refcount
> +unsafe impl<T: DriverPlane> StaticModeObject for Plane<T> {}
> +
> +// SAFETY: Our interface is thread-safe.
> +unsafe impl<T: DriverPlane> Send for Plane<T> {}
> +
> +// SAFETY: Our interface is thread-safe.
> +unsafe impl<T: DriverPlane> Sync for Plane<T> {}
> +
> +/// A supertrait of [`AsRawPlane`] for [`struct drm_plane`]
> interfaces that can perform modesets.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// # Safety
> +///
> +/// Any object implementing this trait must only be made directly
> available to the user after
> +/// [`create_objects`] has completed.
> +///
> +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h
> +/// [`create_objects`]: KmsDriver::create_objects
> +pub unsafe trait ModesettablePlane: AsRawPlane {
> +    /// The type that should be returned for a plane state acquired
> using this plane interface
> +    type State: FromRawPlaneState;
> +}
> +
> +/// Common methods available on any type which implements
> [`AsRawPlane`].
> +///
> +/// This is implemented internally by DRM, and provides many of the
> basic methods for working with
> +/// planes.
> +pub trait RawPlane: AsRawPlane {
> +    /// Return the index of this DRM plane
> +    #[inline]
> +    fn index(&self) -> u32 {
> +        // SAFETY:
> +        // - The index is initialized by the time we expose planes
> to users, and does not change
> +        //   throughout its lifetime
> +        // - `.as_raw()` always returns a valid poiinter.
> +        unsafe { *self.as_raw() }.index
> +    }
> +
> +    /// Return the index of this DRM plane in the form of a bitmask
> +    #[inline]
> +    fn mask(&self) -> u32 {
> +        1 << self.index()
> +    }
> +}
> +impl<T: AsRawPlane> RawPlane for T {}
> +
> +/// A [`struct drm_plane`] without a known [`DriverPlane`]
> implementation.
> +///
> +/// This is mainly for situations where our bindings can't infer the
> [`DriverPlane`] implementation
> +/// for a [`struct drm_plane`] automatically. It is identical to
> [`Plane`], except that it does not
> +/// provide access to the driver's private data.
> +///
> +/// It may be upcasted to a full [`Plane`] using
> [`Plane::from_opaque`] or
> +/// [`Plane::try_from_opaque`].
> +///
> +/// # Invariants
> +///
> +/// - `plane` is initialized for as long as this object is made
> available to users.
> +/// - The data layout of this structure is equivalent to [`struct
> drm_plane`].
> +///
> +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h
> +#[repr(transparent)]
> +pub struct OpaquePlane<T: KmsDriver> {
> +    plane: Opaque<bindings::drm_plane>,
> +    _p: PhantomData<T>,
> +}
> +
> +impl<T: KmsDriver> Sealed for OpaquePlane<T> {}
> +
> +// SAFETY:
> +// * Via our type variants our data layout is identical to
> `drm_plane`
> +// * Since we don't expose `plane` to users before it has been
> initialized, this and our data
> +//   layout ensure that `as_raw()` always returns a valid pointer to
> a `drm_plane`.
> +unsafe impl<T: KmsDriver> AsRawPlane for OpaquePlane<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_plane {
> +        self.plane.get()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane) -> &'a
> Self {
> +        // SAFETY: Our data layout is identical to
> `bindings::drm_plane`
> +        unsafe { &*ptr.cast() }
> +    }
> +}
> +
> +// SAFETY: We only expose this object to users directly after
> KmsDriver::create_objects has been
> +// called.
> +unsafe impl<T: KmsDriver> ModesettablePlane for OpaquePlane<T> {
> +    type State = OpaquePlaneState<T>;
> +}
> +
> +// SAFETY: We don't expose OpaquePlane<T> to users before `base` is
> initialized in
> +// Plane::<T>::new(), so `raw_mode_obj` always returns a valid
> pointer to a
> +// bindings::drm_mode_object.
> +unsafe impl<T: KmsDriver> ModeObject for OpaquePlane<T> {
> +    type Driver = T;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: DRM planes exist for as long as the device does,
> so this pointer is always valid
> +        unsafe { Device::from_raw((*self.as_raw()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose DRM planes to users before `base`
> is initialized
> +        unsafe { &raw mut (*self.as_raw()).base }
> +    }
> +}
> +
> +// SAFETY: Planes do not have a refcount
> +unsafe impl<T: KmsDriver> StaticModeObject for OpaquePlane<T> {}
> +
> +// SAFETY: `funcs` is initialized when the plane is allocated
> +unsafe impl<T: KmsDriver> ModeObjectVtable for OpaquePlane<T> {
> +    type Vtable = bindings::drm_plane_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        // SAFETY: `as_raw()` always returns a valid pointer to a
> plane
> +        unsafe { *self.as_raw() }.funcs
> +    }
> +}
> +
> +// SAFETY: Our plane interfaces are guaranteed to be thread-safe
> +unsafe impl<T: KmsDriver> Send for OpaquePlane<T> {}
> +unsafe impl<T: KmsDriver> Sync for OpaquePlane<T> {}
> +
> +/// A trait implemented by any type which can produce a reference to
> a [`struct drm_plane_state`].
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
> +pub trait AsRawPlaneState: private::AsRawPlaneState {
> +    /// The type that this plane state interface returns to
> represent the parent DRM plane
> +    type Plane: ModesettablePlane;
> +}
> +
> +pub(crate) mod private {
> +    /// Trait for retrieving references to the base plane state
> contained within any plane state
> +    /// compatible type
> +    #[allow(unreachable_pub)]
> +    pub trait AsRawPlaneState {
> +        /// Return an immutable reference to the raw plane state
> +        fn as_raw(&self) -> &bindings::drm_plane_state;
> +
> +        /// Get a mutable reference to the raw
> `bindings::drm_plane_state` contained within this
> +        /// type.
> +        ///
> +        /// # Safety
> +        ///
> +        /// The caller promises this mutable reference will not be
> used to modify any contents of
> +        /// `bindings::drm_plane_state` which DRM would consider to
> be static - like the backpointer
> +        /// to the DRM plane that owns this state. This also means
> the mutable reference should
> +        /// never be exposed outside of this crate.
> +        unsafe fn as_raw_mut(&mut self) -> &mut
> bindings::drm_plane_state;
> +    }
> +}
> +
> +pub(crate) use private::AsRawPlaneState as AsRawPlaneStatePrivate;
> +
> +/// A trait implemented for any type which can be constructed
> directly from a
> +/// [`struct drm_plane_state`] pointer.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
> +pub trait FromRawPlaneState: AsRawPlaneState {
> +    /// Get an immutable reference to this type from the given raw
> [`struct drm_plane_state`]
> +    /// pointer.
> +    ///
> +    /// # Safety
> +    ///
> +    /// - The caller guarantees `ptr` is contained within a valid
> instance of `Self`
> +    /// - The caller guarantees that `ptr` cannot not be modified
> for the lifetime of `'a`.
> +    ///
> +    /// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
> +    unsafe fn from_raw<'a>(ptr: *const bindings::drm_plane_state) ->
> &'a Self;
> +
> +    /// Get a mutable reference to this type from the given raw
> [`struct drm_plane_state`] pointer.
> +    ///
> +    /// # Safety
> +    ///
> +    /// - The caller guarantees that `ptr` is contained within a
> valid instance of `Self`
> +    /// - The caller guarantees that `ptr` cannot have any other
> references taken out for the
> +    ///   lifetime of `'a`.
> +    ///
> +    /// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
> +    unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_plane_state)
> -> &'a mut Self;
> +}
> +
> +/// Common methods available on any type which implements
> [`AsRawPlane`].
> +///
> +/// This is implemented internally by DRM, and provides many of the
> basic methods for working with
> +/// the atomic state of [`Plane`]s.
> +pub trait RawPlaneState: AsRawPlaneState {
> +    /// Return the plane that this plane state belongs to.
> +    fn plane(&self) -> &Self::Plane {
> +        // SAFETY: The index is initialized by the time we expose
> Plane objects to users, and is
> +        // invariant throughout the lifetime of the Plane
> +        unsafe { Self::Plane::from_raw(self.as_raw().plane) }
> +    }
> +
> +    /// Return the current [`OpaqueCrtc`] assigned to this plane, if
> there is one.
> +    fn crtc<'a, 'b: 'a, D>(&'a self) -> Option<&'b OpaqueCrtc<D>>
> +    where
> +        Self::Plane: ModeObject<Driver = D>,
> +        D: KmsDriver,
> +    {
> +        // SAFETY: This cast is guaranteed safe by `OpaqueCrtc`s
> invariants.
> +        NonNull::new(self.as_raw().crtc).map(|c| unsafe {
> OpaqueCrtc::from_raw(c.as_ptr()) })
> +    }
> +
> +    /// Run the atomic check helper for this plane and the given
> CRTC state.
> +    fn atomic_helper_check<S, D>(
> +        &mut self,
> +        crtc_state: &CrtcStateMutator<'_, S>,
> +        can_position: bool,
> +        can_update_disabled: bool,
> +    ) -> Result
> +    where
> +        D: KmsDriver,
> +        S: FromRawCrtcState,
> +        S::Crtc: ModesettableCrtc + ModeObject<Driver = D>,
> +        Self::Plane: ModeObject<Driver = D>,
> +    {
> +        // SAFETY: We're passing the mutable reference from
> `self.as_raw_mut()` directly to DRM,
> +        // which is safe.
> +        to_result(unsafe {
> +            bindings::drm_atomic_helper_check_plane_state(
> +                self.as_raw_mut(),
> +                crtc_state.as_raw(),
> +                bindings::DRM_PLANE_NO_SCALING as _, // TODO: add
> parameters for scaling
> +                bindings::DRM_PLANE_NO_SCALING as _,
> +                can_position,
> +                can_update_disabled,
> +            )
> +        })
> +    }
> +
> +    /// Return the framebuffer currently set for this plane state
> +    #[inline]
> +    fn framebuffer<D>(&self) -> Option<&Framebuffer<D>>
> +    where
> +        Self::Plane: ModeObject<Driver = D>,
> +        D: KmsDriver,
> +    {
> +        // SAFETY: The layout of Framebuffer<T> is identical to `fb`
> +        unsafe {
> +            self.as_raw()
> +                .fb
> +                .as_ref()
> +                .map(|fb| Framebuffer::from_raw(fb))
> +        }
> +    }
> +}
> +impl<T: AsRawPlaneState + ?Sized> RawPlaneState for T {}
> +
> +/// The main interface for a [`struct drm_plane_state`].
> +///
> +/// This type is the main interface for dealing with the atomic
> state of DRM planes. In addition, it
> +/// allows access to whatever private data is contained within an
> implementor's [`DriverPlaneState`]
> +/// type.
> +///
> +/// # Invariants
> +///
> +/// - The DRM C API and our interface guarantees that only the user
> has mutable access to `state`,
> +///   up until [`drm_atomic_helper_commit_hw_done`] is called.
> Therefore, `plane` follows rust's
> +///   data aliasing rules and does not need to be behind an
> [`Opaque`] type.
> +/// - `state` and `inner` initialized for as long as this object is
> exposed to users.
> +/// - The data layout of this structure begins with [`struct
> drm_plane_state`].
> +/// - The plane for this atomic state can always be assumed to be of
> type [`Plane<T::Plane>`].
> +///
> +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
> +/// [`drm_atomic_helper_commit_hw_done`]:
> srctree/include/drm/drm_atomic_helper.h
> +#[derive(Default)]
> +#[repr(C)]
> +pub struct PlaneState<T: DriverPlaneState> {
> +    state: bindings::drm_plane_state,
> +    inner: T,
> +}
> +
> +/// The main trait for implementing the [`struct drm_plane_state`]
> API for a [`Plane`].
> +///
> +/// A driver may store driver-private data within the implementor's
> type, which will be available
> +/// when using a full typed [`PlaneState`] object.
> +///
> +/// # Invariants
> +///
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_plane`] pointers are contained within a
> [`Plane<Self::Plane>`].
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_plane_state`] pointers are contained within a
> [`PlaneState<Self>`].
> +///
> +/// [`struct drm_plane`]: srctree/include/drm_plane.h
> +/// [`struct drm_plane_state`]: srctree/include/drm_plane.h
> +pub trait DriverPlaneState: Clone + Default + Sized {
> +    /// The type for this driver's drm_plane implementation
> +    type Plane: DriverPlane;
> +}
> +
> +impl<T: DriverPlaneState> Sealed for PlaneState<T> {}
> +
> +impl<T: DriverPlaneState> AsRawPlaneState for PlaneState<T> {
> +    type Plane = Plane<T::Plane>;
> +}
> +
> +impl<T: DriverPlaneState> private::AsRawPlaneState for PlaneState<T>
> {
> +    fn as_raw(&self) -> &bindings::drm_plane_state {
> +        &self.state
> +    }
> +
> +    unsafe fn as_raw_mut(&mut self) -> &mut
> bindings::drm_plane_state {
> +        &mut self.state
> +    }
> +}
> +
> +impl<T: DriverPlaneState> FromRawPlaneState for PlaneState<T> {
> +    unsafe fn from_raw<'a>(ptr: *const bindings::drm_plane_state) ->
> &'a Self {
> +        // Our data layout starts with `bindings::drm_plane_state`.
> +        let ptr: *const Self = ptr.cast();
> +
> +        // SAFETY:
> +        // - Our safety contract requires that `ptr` be contained
> within `Self`.
> +        // - Our safety contract requires the caller ensure that it
> is safe for us to take an
> +        //   immutable reference.
> +        unsafe { &*ptr }
> +    }
> +
> +    unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_plane_state)
> -> &'a mut Self {
> +        // Our data layout starts with `bindings::drm_plane_state`.
> +        let ptr: *mut Self = ptr.cast();
> +
> +        // SAFETY:
> +        // - Our safety contract requires that `ptr` be contained
> within `Self`.
> +        // - Our safety contract requires the caller ensure it is
> safe for us to take a mutable
> +        //   reference.
> +        unsafe { &mut *ptr }
> +    }
> +}
> +
> +impl<T: DriverPlaneState> Deref for PlaneState<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.inner
> +    }
> +}
> +
> +impl<T: DriverPlaneState> DerefMut for PlaneState<T> {
> +    fn deref_mut(&mut self) -> &mut Self::Target {
> +        &mut self.inner
> +    }
> +}
> +
> +// SAFETY: Shares the safety guarantee of Plane<T>'s vtable impl
> +unsafe impl<T: DriverPlaneState> ModeObjectVtable for PlaneState<T>
> {
> +    type Vtable = bindings::drm_plane_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.plane().vtable()
> +    }
> +}
> +
> +impl<T: DriverPlaneState> PlaneState<T> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <'a, D, P>(&'a OpaquePlaneState<D>) -> &'a Self
> +        where
> +            T: DriverPlaneState<Plane = P>;
> +        use
> +            P as DriverPlane,
> +            D as KmsDriver<Plane = ...>
> +    }
> +}
> +
> +/// A [`struct drm_plane_state`] without a known
> [`DriverPlaneState`] implementation.
> +///
> +/// This is mainly for situations where our bindings can't infer the
> [`DriverPlaneState`]
> +/// implementation for a [`struct drm_plane_state`] automatically.
> It is identical to [`Plane`],
> +/// except that it does not provide access to the driver's private
> data.
> +///
> +/// # Invariants
> +///
> +/// - The DRM C API and our interface guarantees that only the user
> has mutable access to `state`,
> +///   up until [`drm_atomic_helper_commit_hw_done`] is called.
> Therefore, `plane` follows rust's
> +///   data aliasing rules and does not need to be behind an
> [`Opaque`] type.
> +/// - `state` is initialized for as long as this object is exposed
> to users.
> +/// - The data layout of this structure is identical to [`struct
> drm_plane_state`].
> +///
> +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
> +/// [`drm_atomic_helper_commit_hw_done`]:
> srctree/include/drm/drm_atomic_helper.h
> +#[repr(transparent)]
> +pub struct OpaquePlaneState<T: KmsDriver> {
> +    state: bindings::drm_plane_state,
> +    _p: PhantomData<T>,
> +}
> +
> +impl<T: KmsDriver> AsRawPlaneState for OpaquePlaneState<T> {
> +    type Plane = OpaquePlane<T>;
> +}
> +
> +impl<T: KmsDriver> private::AsRawPlaneState for OpaquePlaneState<T>
> {
> +    fn as_raw(&self) -> &bindings::drm_plane_state {
> +        &self.state
> +    }
> +
> +    unsafe fn as_raw_mut(&mut self) -> &mut
> bindings::drm_plane_state {
> +        &mut self.state
> +    }
> +}
> +
> +impl<T: KmsDriver> FromRawPlaneState for OpaquePlaneState<T> {
> +    unsafe fn from_raw<'a>(ptr: *const bindings::drm_plane_state) ->
> &'a Self {
> +        // SAFETY: Our data layout is identical to `ptr`
> +        unsafe { &*ptr.cast() }
> +    }
> +
> +    unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_plane_state)
> -> &'a mut Self {
> +        // SAFETY: Our data layout is identical to `ptr`
> +        unsafe { &mut *ptr.cast() }
> +    }
> +}
> +
> +// SAFETY: Shares the safety guarantee of OpaquePlane<T>'s vtable
> impl
> +unsafe impl<T: KmsDriver> ModeObjectVtable for OpaquePlaneState<T> {
> +    type Vtable = bindings::drm_plane_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.plane().vtable()
> +    }
> +}
> +
> +/// An interface for mutating a [`Plane`]s atomic state.
> +///
> +/// This type is typically returned by an [`AtomicStateMutator`]
> within contexts where it is
> +/// possible to safely mutate a plane's state. In order to uphold
> rust's data-aliasing rules, only
> +/// [`PlaneStateMutator`] may exist at a time.
> +pub struct PlaneStateMutator<'a, T: FromRawPlaneState> {
> +    state: &'a mut T,
> +    mask: &'a Cell<u32>,
> +}
> +
> +impl<'a, T: FromRawPlaneState> PlaneStateMutator<'a, T> {
> +    pub(super) fn new<D: KmsDriver>(
> +        mutator: &'a AtomicStateMutator<D>,
> +        state: NonNull<bindings::drm_plane_state>,
> +    ) -> Option<Self> {
> +        // SAFETY: `plane` is invariant throughout the lifetime of
> the atomic state, is
> +        // initialized by this point, and we're guaranteed it is of
> type `AsRawPlane` by type
> +        // invariance
> +        let plane = unsafe {
> T::Plane::from_raw((*state.as_ptr()).plane) };
> +        let plane_mask = plane.mask();
> +        let borrowed_mask = mutator.borrowed_planes.get();
> +
> +        if borrowed_mask & plane_mask == 0 {
> +            mutator.borrowed_planes.set(borrowed_mask | plane_mask);
> +            Some(Self {
> +                mask: &mutator.borrowed_planes,
> +                // SAFETY: We're guaranteed `state` is of
> `FromRawPlaneState` by type invariance,
> +                // and we just confirmed by checking
> `borrowed_planes` that no other mutable borrows
> +                // have been taken out for `state`
> +                state: unsafe { T::from_raw_mut(state.as_ptr()) },
> +            })
> +        } else {
> +            None
> +        }
> +    }
> +}
> +
> +impl<'a, T: FromRawPlaneState> Drop for PlaneStateMutator<'a, T> {
> +    fn drop(&mut self) {
> +        let mask = self.state.plane().mask();
> +        self.mask.set(self.mask.get() & !mask);
> +    }
> +}
> +
> +impl<'a, T: FromRawPlaneState> AsRawPlaneState for
> PlaneStateMutator<'a, T> {
> +    type Plane = T::Plane;
> +}
> +
> +impl<'a, T: FromRawPlaneState> private::AsRawPlaneState for
> PlaneStateMutator<'a, T> {
> +    fn as_raw(&self) -> &bindings::drm_plane_state {
> +        self.state.as_raw()
> +    }
> +
> +    unsafe fn as_raw_mut(&mut self) -> &mut
> bindings::drm_plane_state {
> +        // SAFETY: This function is bound by the same safety
> contract as `self.inner.as_raw_mut()`
> +        unsafe { self.state.as_raw_mut() }
> +    }
> +}
> +
> +impl<'a, T: FromRawPlaneState> Sealed for PlaneStateMutator<'a, T>
> {}
> +
> +impl<'a, T: DriverPlaneState> Deref for PlaneStateMutator<'a,
> PlaneState<T>> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.state.inner
> +    }
> +}
> +
> +impl<'a, T: DriverPlaneState> DerefMut for PlaneStateMutator<'a,
> PlaneState<T>> {
> +    fn deref_mut(&mut self) -> &mut Self::Target {
> +        &mut self.state.inner
> +    }
> +}
> +
> +// SAFETY: Shares the safety guarantees of `T`'s ModeObjectVtable
> impl
> +unsafe impl<'a, T: FromRawPlaneState> ModeObjectVtable for
> PlaneStateMutator<'a, T>
> +where
> +    T: FromRawPlaneState + ModeObjectVtable,
> +{
> +    type Vtable = T::Vtable;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.state.vtable()
> +    }
> +}
> +
> +impl<'a, T: DriverPlaneState> PlaneStateMutator<'a, PlaneState<T>> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <D, P>(PlaneStateMutator<'a, OpaquePlaneState<D>>) ->
> Self
> +        where
> +            T: DriverPlaneState<Plane = P>;
> +        use
> +            P as DriverPlane,
> +            D as KmsDriver<Plane = ...>
> +    }
> +}
> +
> +/// A token provided during [`atomic_check`] callbacks for accessing
> the plane, atomic state, and
> +/// the old and new states of the plane.
> +///
> +/// [`atomic_check`]: DriverPlane::atomic_check
> +pub struct PlaneAtomicCheck<'a, T: DriverPlane> {
> +    state: &'a AtomicStateComposer<T::Driver>,
> +    plane: &'a Plane<T>,
> +}
> +
> +impl<'a, T: DriverPlane> PlaneAtomicCheck<'a, T> {
> +    impl_atomic_state_token_ops!(
> +        PlaneAtomicCheck,
> +        AtomicStateComposer,
> +        Plane,
> +        use <'a, T>
> +    );
> +}
> +
> +/// A token provided to [`DriverPlane`] callbacks during the atomic
> commit phase for accessing the
> +/// plane, atomic state, new and old states of the plane.
> +///
> +/// # Invariants
> +///
> +/// This token is proof that the old and new atomic state of `plane`
> are present in `state` and do
> +/// not have any mutators taken out.
> +pub struct PlaneAtomicCommit<'a, T: DriverPlane> {
> +    state: &'a AtomicStateMutator<T::Driver>,
> +    plane: &'a Plane<T>,
> +}
> +
> +impl<'a, T: DriverPlane> PlaneAtomicCommit<'a, T> {
> +    impl_atomic_state_token_ops!(
> +        PlaneAtomicCommit,
> +        AtomicStateMutator,
> +        Plane,
> +        use <'a, T>
> +    );
> +}
> +
> +unsafe extern "C" fn plane_destroy_callback<T: DriverPlane>(plane:
> *mut bindings::drm_plane) {
> +    // SAFETY: DRM guarantees that `plane` points to a valid
> initialized `drm_plane`.
> +    unsafe { bindings::drm_plane_cleanup(plane) };
> +
> +    // SAFETY:
> +    // - DRM guarantees we are now the only one with access to this
> [`drm_plane`].
> +    // - This cast is safe via `DriverPlane`s type invariants.
> +    drop(unsafe { KBox::from_raw(plane as *mut Plane<T>) });
> +}
> +
> +unsafe extern "C" fn atomic_duplicate_state_callback<T:
> DriverPlaneState>(
> +    plane: *mut bindings::drm_plane,
> +) -> *mut bindings::drm_plane_state {
> +    // SAFETY: DRM guarantees that `plane` points to a valid
> initialized `drm_plane`.
> +    let state = unsafe { (*plane).state };
> +    if state.is_null() {
> +        return null_mut();
> +    }
> +
> +    // SAFETY: This cast is safe via `DriverPlaneState`s type
> invariants.
> +    let state = unsafe { PlaneState::<T>::from_raw(state) };
> +
> +    let new: Result<KBox<_>> = KBox::try_init(
> +        try_init!(PlaneState {
> +            inner: state.inner.clone(),
> +            state: bindings::drm_plane_state {
> +                ..Default::default()
> +            },
> +        }),
> +        GFP_KERNEL,
> +    );
> +
> +    if let Ok(mut new) = new {
> +        // SAFETY:
> +        // - `new` provides a valid pointer to a newly allocated
> `drm_plane_state` via type
> +        //   invariants
> +        // - This initializes `new` via memcpy()
> +        unsafe {
> bindings::__drm_atomic_helper_plane_duplicate_state(plane,
> new.as_raw_mut()) };
> +
> +        KBox::into_raw(new).cast()
> +    } else {
> +        null_mut()
> +    }
> +}
> +
> +unsafe extern "C" fn atomic_destroy_state_callback<T:
> DriverPlaneState>(
> +    _plane: *mut bindings::drm_plane,
> +    state: *mut bindings::drm_plane_state,
> +) {
> +    // SAFETY: DRM guarantees that `state` points to a valid
> instance of `drm_plane_state`
> +    unsafe {
> bindings::__drm_atomic_helper_plane_destroy_state(state) };
> +
> +    // SAFETY:
> +    // * DRM guarantees we are the only one with access to this
> `drm_plane_state`
> +    // * This cast is safe via our type invariants.
> +    drop(unsafe { KBox::from_raw(state.cast::<PlaneState<T>>()) });
> +}
> +
> +unsafe extern "C" fn plane_reset_callback<T: DriverPlane>(plane:
> *mut bindings::drm_plane) {
> +    // SAFETY: DRM guarantees that `state` points to a valid
> instance of `drm_plane_state`
> +    let state = unsafe { (*plane).state };
> +    if !state.is_null() {
> +        // SAFETY:
> +        // - We're guaranteed `plane` is `Plane<T>` via type
> invariants
> +        // - We're guaranteed `state` is `PlaneState<T>` via type
> invariants.
> +        unsafe { atomic_destroy_state_callback::<T::State>(plane,
> state) }
> +
> +        // SAFETY: No special requirements here, DRM expects this to
> be NULL
> +        unsafe {
> +            (*plane).state = null_mut();
> +        }
> +    }
> +
> +    // Unfortunately, this is the best we can do at the moment as
> this FFI callback was mistakenly
> +    // presumed to be infallible :(
> +    let new =
> +        KBox::new(PlaneState::<T::State>::default(),
> GFP_KERNEL).expect("Blame the API, sorry!");
> +
> +    // DRM takes ownership of the state from here, resets it, and
> then assigns it to the plane
> +    // SAFETY:
> +    // - DRM guarantees that `plane` points to a valid instance of
> `drm_plane`.
> +    // - The cast to `drm_plane_state` is safe via `PlaneState`s
> type invariants.
> +    unsafe { bindings::__drm_atomic_helper_plane_reset(plane,
> KBox::into_raw(new).cast()) };
> +}
> +
> +unsafe extern "C" fn atomic_update_callback<T: DriverPlane>(
> +    plane: *mut bindings::drm_plane,
> +    state: *mut bindings::drm_atomic_state,
> +) {
> +    // SAFETY:
> +    // - We're guaranteed `plane` is of type `Plane<T>` via type
> invariants.
> +    // - We're guaranteed by DRM that `plane` is pointing to a valid
> initialized state.
> +    let plane = unsafe { Plane::from_raw(plane) };
> +
> +    // SAFETY: DRM guarantees `state` points to a valid
> `drm_atomic_state`
> +    let state = unsafe {
> AtomicStateMutator::new(NonNull::new_unchecked(state)) };
> +
> +    // SAFETY:
> +    // - Since we're in the atomic_update callback, we're guaranteed
> by DRM that both the old and new
> +    //   plane state are resent in this atomic state.
> +    // - We just created the state mutator above, so other mutators
> cannot be taken out on the plane
> +    //   state yet.
> +    let commit = unsafe { PlaneAtomicCommit::new(plane, &state) };
> +
> +    T::atomic_update(commit);
> +}
> +
> +unsafe extern "C" fn atomic_check_callback<T: DriverPlane>(
> +    plane: *mut bindings::drm_plane,
> +    state: *mut bindings::drm_atomic_state,
> +) -> i32 {
> +    // SAFETY:
> +    // - We're guaranteed `plane` is of type `Plane<T>` via type
> invariants.
> +    // - We're guaranteed by DRM that `plane` is pointing to a valid
> initialized state.
> +    let plane = unsafe { Plane::from_raw(plane) };
> +
> +    // SAFETY: We're guaranteed by DRM that `state` points to a
> valid instance of `drm_atomic_state`
> +    // We use ManuallyDrop here since AtomicStateComposer would
> otherwise drop a owned reference to
> +    // the atomic state upon finishing this callback.
> +    let state = ManuallyDrop::new(unsafe {
> +       
> AtomicStateComposer::<T::Driver>::new(NonNull::new_unchecked(state))
> +    });
> +
> +    // SAFETY:
> +    // - Since we're in the atomic check callback, we're guaranteed
> by DRM that both the old and
> +    //   new plane state are present in this atomic state
> +    // - We just created the state composer above, so other
> composers cannot be taken out on the
> +    //   plane state yet.
> +    let check = unsafe { PlaneAtomicCheck::new(plane, &state) };
> +
> +    from_result(|| T::atomic_check(check).map(|_| 0))
> +}
> diff --git a/rust/kernel/drm/kms/vblank.rs
> b/rust/kernel/drm/kms/vblank.rs
> new file mode 100644
> index 000000000000..dc34e02e8ccb
> --- /dev/null
> +++ b/rust/kernel/drm/kms/vblank.rs
> @@ -0,0 +1,461 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! DRM KMS vblank support.
> +//!
> +//! C header:
> [`include/drm/drm_vblank.h`](srcfree/include/drm/drm_vblank.h)
> +
> +use super::{crtc::*, ModeObject, modes::*, Sealed};
> +use bindings;
> +use core::{
> +    marker::*,
> +    mem::{self, ManuallyDrop},
> +    ops::{Deref, Drop},
> +    ptr::null_mut,
> +};
> +use kernel::{
> +    drm::device::Device,
> +    error::{from_result, to_result},
> +    interrupt::LocalInterruptDisabled,
> +    prelude::*,
> +    time::Delta,
> +    types::Opaque,
> +};
> +
> +/// The main trait for a driver to implement hardware vblank support
> for a [`Crtc`].
> +///
> +/// # Invariants
> +///
> +/// C FFI callbacks generated using this trait can safely assume
> that input pointers to
> +/// [`struct drm_crtc`] are always contained within a
> [`Crtc<Self::Crtc>`].
> +///
> +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
> +pub trait VblankSupport: Sized {
> +    /// The parent [`DriverCrtc`].
> +    type Crtc: VblankDriverCrtc<VblankImpl = Self>;
> +
> +    /// Enable vblank interrupts for this [`DriverCrtc`].
> +    fn enable_vblank(
> +        crtc: &Crtc<Self::Crtc>,
> +        vblank_guard: &VblankGuard<'_, Self::Crtc>,
> +        irq: &LocalInterruptDisabled,
> +    ) -> Result;
> +
> +    /// Disable vblank interrupts for this [`DriverCrtc`].
> +    fn disable_vblank(
> +        crtc: &Crtc<Self::Crtc>,
> +        vblank_guard: &VblankGuard<'_, Self::Crtc>,
> +        irq: &LocalInterruptDisabled,
> +    );
> +
> +    /// Retrieve the current vblank timestamp for this [`Crtc`]
> +    ///
> +    /// If this function is being called from the driver's vblank
> interrupt handler,
> +    /// `handling_vblank_irq` will be `true`.
> +    fn get_vblank_timestamp(
> +        crtc: &Crtc<Self::Crtc>,
> +        in_vblank_irq: bool,
> +    ) -> Option<VblankTimestamp>;
> +}
> +
> +/// Trait used for CRTC vblank (or lack there-of) implementations.
> Implemented internally.
> +///
> +/// Drivers interested in implementing vblank support should refer
> to [`VblankSupport`], drivers
> +/// that don't have vblank support can use [`PhantomData`].
> +pub trait VblankImpl {
> +    /// The parent [`DriverCrtc`].
> +    type Crtc: DriverCrtc<VblankImpl = Self>;
> +
> +    /// The generated [`VblankOps`].
> +    const VBLANK_OPS: VblankOps;
> +}
> +
> +/// C FFI callbacks for vblank management.
> +///
> +/// Created internally by DRM.
> +#[derive(Default)]
> +pub struct VblankOps {
> +    pub(crate) enable_vblank: Option<unsafe extern "C" fn(crtc: *mut
> bindings::drm_crtc) -> i32>,
> +    pub(crate) disable_vblank: Option<unsafe extern "C" fn(crtc:
> *mut bindings::drm_crtc)>,
> +    pub(crate) get_vblank_timestamp: Option<
> +        unsafe extern "C" fn(
> +            crtc: *mut bindings::drm_crtc,
> +            max_error: *mut i32,
> +            vblank_time: *mut bindings::ktime_t,
> +            in_vblank_irq: bool,
> +        ) -> bool,
> +    >,
> +}
> +
> +impl<T: VblankSupport> VblankImpl for T {
> +    type Crtc = T::Crtc;
> +
> +    const VBLANK_OPS: VblankOps = VblankOps {
> +        enable_vblank: Some(enable_vblank_callback::<T>),
> +        disable_vblank: Some(disable_vblank_callback::<T>),
> +        get_vblank_timestamp:
> Some(get_vblank_timestamp_callback::<T>),
> +    };
> +}
> +
> +impl<T> VblankImpl for PhantomData<T>
> +where
> +    T: DriverCrtc<VblankImpl = PhantomData<T>>,
> +{
> +    type Crtc = T;
> +
> +    const VBLANK_OPS: VblankOps = VblankOps {
> +        enable_vblank: None,
> +        disable_vblank: None,
> +        get_vblank_timestamp: None,
> +    };
> +}
> +
> +unsafe extern "C" fn enable_vblank_callback<T: VblankSupport>(
> +    crtc: *mut bindings::drm_crtc,
> +) -> i32 {
> +    // SAFETY: We're guaranteed that `crtc` is of type
> `Crtc<T::Crtc>` by type invariants.
> +    let crtc = unsafe { Crtc::<T::Crtc>::from_raw(crtc) };
> +
> +    // SAFETY: This callback happens with IRQs disabled
> +    let irq = unsafe { LocalInterruptDisabled::assume_disabled() };
> +
> +    // SAFETY: This callback happens with `vbl_lock` already held
> +    // We don't want to drop `vbl_lock` when this callback completes
> since DRM will do this for us,
> +    // so wrap the `VblankGuard` in a `ManuallyDrop`
> +    let vblank_guard = ManuallyDrop::new(unsafe {
> VblankGuard::new(crtc, irq) });
> +
> +    from_result(|| T::enable_vblank(crtc, &vblank_guard,
> irq).map(|_| 0))
> +}
> +
> +unsafe extern "C" fn disable_vblank_callback<T: VblankSupport>(crtc:
> *mut bindings::drm_crtc) {
> +    // SAFETY: We're guaranteed that `crtc` is of type
> `Crtc<T::Crtc>` by type invariants.
> +    let crtc = unsafe { Crtc::<T::Crtc>::from_raw(crtc) };
> +
> +    // SAFETY: This callback happens with IRQs disabled
> +    let irq = unsafe { LocalInterruptDisabled::assume_disabled() };
> +
> +    // SAFETY: This call happens with `vbl_lock` already held
> +    // We don't want to drop `vbl_lock` when this callback completes
> since DRM will do this for us,
> +    // so wrap the `VblankGuard` in a `ManuallyDrop`
> +    let vblank_guard = ManuallyDrop::new(unsafe {
> VblankGuard::new(crtc, irq) });
> +
> +    T::disable_vblank(crtc, &vblank_guard, irq);
> +}
> +
> +unsafe extern "C" fn get_vblank_timestamp_callback<T:
> VblankSupport>(
> +    crtc: *mut bindings::drm_crtc,
> +    max_error: *mut i32,
> +    vblank_time: *mut bindings::ktime_t,
> +    in_vblank_irq: bool,
> +) -> bool {
> +    // SAFETY: We're guaranteed `crtc` is of type `Crtc<T::Crtc>` by
> type invariance
> +    let crtc = unsafe { Crtc::<T::Crtc>::from_raw(crtc) };
> +
> +    if let Some(timestamp) = T::get_vblank_timestamp(crtc,
> in_vblank_irq) {
> +        // SAFETY: Both of these pointers are guaranteed by the C
> API to be valid
> +        unsafe {
> +            (*max_error) = timestamp.max_error;
> +            (*vblank_time) = timestamp.time.as_nanos();
> +        };
> +
> +        true
> +    } else {
> +        false
> +    }
> +}
> +
> +/// A vblank timestamp.
> +///
> +/// This type is used by [`VblankSupport::get_vblank_timestamp`] for
> the implementor to return the
> +/// current vblank timestamp for the hardware.
> +#[derive(Copy, Clone)]
> +pub struct VblankTimestamp {
> +    /// The actual vblank timestamp in nanoseconds, accuracy to
> within [`Self::max_error`]
> +    /// nanoseconds.
> +    pub time: Delta,
> +
> +    /// Maximum allowable timestamp error in nanoseconds
> +    pub max_error: i32,
> +}
> +
> +/// A trait for [`DriverCrtc`] implementations with hardware vblank
> support.
> +///
> +/// This trait is implemented internally by DRM for any
> [`DriverCrtc`] implementation that
> +/// implements [`VblankSupport`]. It is used to expose hardware-
> vblank driver exclusive methods and
> +/// data to users.
> +pub trait VblankDriverCrtc: DriverCrtc {}
> +
> +impl<T, V> VblankDriverCrtc for T
> +where
> +    T: DriverCrtc<VblankImpl = V>,
> +    V: VblankSupport<Crtc = T>,
> +{
> +}
> +
> +impl<T: VblankDriverCrtc> Crtc<T> {
> +    /// Retrieve a reference to the [`VblankCrtc`] for this
> [`Crtc`].
> +    pub(crate) fn vblank_crtc(&self) -> &VblankCrtc<T> {
> +        // SAFETY:
> +        // - The data layouts of these types are equivalent via
> `VblankCrtc`s type invariants
> +        // - We don't expose any way of calling `vblank_crtc()`
> before `drm_vblank_init()` has been
> +        //   called.
> +        unsafe { VblankCrtc::from_raw(self.get_vblank_ptr()) }
> +    }
> +
> +    /// Access vblank related infrastructure for a [`Crtc`].
> +    ///
> +    /// This function explicitly locks the device's vblank lock, and
> allows access to controlling
> +    /// the vblank configuration for this CRTC. The lock is dropped
> once [`VblankGuard`] is
> +    /// dropped.
> +    pub fn vblank_lock<'a>(&'a self, irq: &'a
> LocalInterruptDisabled) -> VblankGuard<'a, T> {
> +        // SAFETY: `vbl_lock` is initialized for as long as `Crtc`
> is available to users
> +        // INVARIANT: We just acquired `vbl_lock`, fulfilling the
> invariants of `VblankGuard`
> +        unsafe { bindings::spin_lock(&raw mut
> (*self.drm_dev().as_raw()).vbl_lock) };
> +
> +        // SAFETY: We just acquired vbl_lock above
> +        unsafe { VblankGuard::new(self, irq) }
> +    }
> +
> +    /// Trigger a vblank event on this [`Crtc`].
> +    ///
> +    /// Drivers should use this in their vblank interrupt handlers
> to update the vblank counter and
> +    /// send any signals that may be pending.
> +    ///
> +    /// Returns whether or not the vblank event was handled.
> +    #[inline]
> +    pub fn handle_vblank(&self) -> bool {
> +        // SAFETY: `as_raw()` always returns a valid pointer to an
> initialized drm_crtc.
> +        unsafe { bindings::drm_crtc_handle_vblank(self.as_raw()) }
> +    }
> +
> +    /// Forbid vblank events for a [`Crtc`].
> +    ///
> +    /// This function disables vblank events for a [`Crtc`], even if
> [`VblankRef`] objects exist.
> +    #[inline]
> +    pub fn vblank_off(&self) {
> +        // SAFETY: `as_raw()` always returns a valid pointer to an
> initialized drm_crtc.
> +        unsafe { bindings::drm_crtc_vblank_off(self.as_raw()) }
> +    }
> +
> +    /// Allow vblank events for a [`Crtc`].
> +    ///
> +    /// This function allows users to enable vblank events and
> acquire [`VblankRef`] objects again.
> +    #[inline]
> +    pub fn vblank_on(&self) {
> +        // SAFETY: `as_raw()` always returns a valid pointer to an
> initialized drm_crtc.
> +        unsafe { bindings::drm_crtc_vblank_on(self.as_raw()) }
> +    }
> +
> +    /// Enable vblank events for a [`Crtc`].
> +    ///
> +    /// Returns a [`VblankRef`] which will allow vblank events to be
> sent until it is dropped. Note
> +    /// that vblank events may still be disabled by
> [`Self::vblank_off`].
> +    #[must_use = "Vblanks are only enabled until the result from
> this function is dropped"]
> +    pub fn vblank_get(&self) -> Result<VblankRef<'_, T>> {
> +        VblankRef::new(self)
> +    }
> +}
> +
> +/// Common methods available on any [`CrtcState`] whose [`Crtc`]
> implements [`VblankSupport`].
> +///
> +/// This trait is implemented automatically by DRM for any
> [`DriverCrtc`] implementation that
> +/// implements [`VblankSupport`].
> +pub trait RawVblankCrtcState: AsRawCrtcState {
> +    /// Return the [`PendingVblankEvent`] for this CRTC state, if
> there is one.
> +    fn get_pending_vblank_event(&mut self) ->
> Option<PendingVblankEvent<'_, Self>>
> +    where
> +        Self: Sized,
> +    {
> +        // SAFETY: The driver is the only one that will ever modify
> this data, and since our
> +        // interface follows rust's data aliasing rules that means
> this is safe to read
> +        let event_ptr = unsafe { *self.as_raw() }.event;
> +
> +        (!event_ptr.is_null()).then_some(PendingVblankEvent(self))
> +    }
> +}
> +
> +impl<T, C> RawVblankCrtcState for T
> +where
> +    T: AsRawCrtcState<Crtc = Crtc<C>>,
> +    C: VblankDriverCrtc,
> +{
> +}
> +
> +/// A pending vblank event from an atomic state
> +pub struct PendingVblankEvent<'a, T: RawVblankCrtcState>(&'a mut T);
> +
> +impl<'a, T: RawVblankCrtcState> PendingVblankEvent<'a, T> {
> +    /// Send this [`PendingVblankEvent`].
> +    ///
> +    /// A [`PendingVblankEvent`] can only be sent once, so this
> function consumes the
> +    /// [`PendingVblankEvent`].
> +    pub fn send<C>(self)
> +    where
> +        T: RawVblankCrtcState<Crtc = Crtc<C>>,
> +        C: VblankDriverCrtc,
> +    {
> +        let crtc: &Crtc<C> = self.0.crtc();
> +        let event_lock = crtc.drm_dev().event_lock();
> +        let _guard = event_lock.lock();
> +
> +        // SAFETY:
> +        // - We now hold the appropriate lock to call this function
> +        // - Vblanks are enabled as proved by `vbl_ref`, as per the
> C api requirements
> +        // - Our interface is proof that `event` is non-null
> +        unsafe { bindings::drm_crtc_send_vblank_event(crtc.as_raw(),
> (*self.0.as_raw()).event) };
> +
> +        // SAFETY: The mutable reference in `self.state` is proof
> that it is safe to mutate this,
> +        // and DRM expects us to set this to NULL once we've sent
> the vblank event.
> +        unsafe { (*self.0.as_raw()).event = null_mut() };
> +    }
> +
> +    /// Arm this [`PendingVblankEvent`] to be sent later by the
> CRTC's vblank interrupt handler.
> +    ///
> +    /// A [`PendingVblankEvent`] can only be armed once, so this
> function consumes the
> +    /// [`PendingVblankEvent`]. As well, it requires a [`VblankRef`]
> so that vblank interrupts
> +    /// remain enabled until the [`PendingVblankEvent`] has been
> sent out by the driver's vblank
> +    /// interrupt handler.
> +    pub fn arm<C>(self, vbl_ref: VblankRef<'_, C>)
> +    where
> +        T: RawVblankCrtcState<Crtc = Crtc<C>>,
> +        C: VblankDriverCrtc,
> +    {
> +        let crtc: &Crtc<C> = self.0.crtc();
> +        let event_lock = crtc.drm_dev().event_lock();
> +        let _guard = event_lock.lock();
> +
> +        // SAFETY:
> +        // - We now hold the appropriate lock to call this function
> +        // - Vblanks are enabled as proved by `vbl_ref`, as per the
> C api requirements
> +        // - Our interface is proof that `event` is non-null
> +        unsafe { bindings::drm_crtc_arm_vblank_event(crtc.as_raw(),
> (*self.0.as_raw()).event) };
> +
> +        // SAFETY: The mutable reference in `self.state` is proof
> that it is safe to mutate this,
> +        // and DRM expects us to set this to NULL once we've armed
> the vblank event.
> +        unsafe { (*self.0.as_raw()).event = null_mut() };
> +
> +        // DRM took ownership of `vbl_ref` after we called
> `drm_crtc_arm_vblank_event`
> +        mem::forget(vbl_ref);
> +    }
> +}
> +
> +/// A borrowed vblank reference.
> +///
> +/// This object keeps the vblank reference count for a [`Crtc`]
> incremented for as long as it
> +/// exists, enabling vblank interrupts for said [`Crtc`] until all
> references are dropped, or
> +/// [`Crtc::vblank_off`] is called - whichever comes first.
> +pub struct VblankRef<'a, T: VblankDriverCrtc>(&'a Crtc<T>);
> +
> +impl<T: VblankDriverCrtc> Drop for VblankRef<'_, T> {
> +    fn drop(&mut self) {
> +        // SAFETY: as_raw() returns a valid pointer to an
> initialized drm_crtc
> +        unsafe { bindings::drm_crtc_vblank_put(self.0.as_raw()) };
> +    }
> +}
> +
> +impl<'a, T: VblankDriverCrtc> VblankRef<'a, T> {
> +    fn new(crtc: &'a Crtc<T>) -> Result<Self> {
> +        // SAFETY: as_raw() returns a valid pointer to an
> initialized drm_crtc
> +        to_result(unsafe {
> bindings::drm_crtc_vblank_get(crtc.as_raw()) })?;
> +
> +        Ok(Self(crtc))
> +    }
> +}
> +
> +/// The base wrapper for [`drm_vblank_crtc`].
> +///
> +/// Users will rarely interact with this object directly, it is a
> simple wrapper around
> +/// [`drm_vblank_crtc`] which provides access to methods and data
> that is not protected by a lock.
> +///
> +/// # Invariants
> +///
> +/// This type has an identical data layout to [`drm_vblank_crtc`].
> +///
> +/// [`drm_vblank_crtc`]: srctree/include/drm/drm_vblank.h
> +#[repr(transparent)]
> +pub struct VblankCrtc<T>(Opaque<bindings::drm_vblank_crtc>,
> PhantomData<T>);
> +
> +impl<T: VblankDriverCrtc> VblankCrtc<T> {
> +    pub(crate) fn as_raw(&self) -> *mut bindings::drm_vblank_crtc {
> +        self.0.get()
> +    }
> +
> +    // SAFETY: The caller promises that `ptr` points to a valid
> instance of
> +    // `bindings::drm_vblank_crtc`, and that access to this
> structure has been properly serialized
> +    pub(crate) unsafe fn from_raw<'a>(ptr: *mut
> bindings::drm_vblank_crtc) -> &'a Self {
> +        // SAFETY: Our data layouts are identical via
> #[repr(transparent)]
> +        unsafe { &*ptr.cast() }
> +    }
> +
> +    /// Returns the [`Device`] for this [`VblankGuard`]
> +    pub fn drm_dev(&self) -> &Device<T::Driver> {
> +        // SAFETY: `drm` is initialized, invariant and valid
> throughout our lifetime
> +        unsafe { Device::from_raw((*self.as_raw()).dev) }
> +    }
> +}
> +
> +// NOTE: This type does not use a `Guard` because the mutex is not
> contained within the same
> +// structure as the relevant CRTC
> +/// An interface for accessing and controlling vblank related state
> for a [`Crtc`].
> +///
> +/// This type may be returned from some [`VblankSupport`] callbacks,
> or manually via
> +/// [`Crtc::vblank_lock`]. It provides access to methods and data
> which require
> +/// [`drm_device.vbl_lock`] be held.
> +///
> +/// # Invariants
> +///
> +/// - [`drm_device.vbl_lock`] is acquired whenever an instance of
> this type exists.
> +/// - Shares the invariants of [`VblankCrtc`].
> +///
> +/// [`drm_device.vbl_lock`]: srctree/include/drm/drm_device.h
> +#[repr(transparent)]
> +pub struct VblankGuard<'a, T: VblankDriverCrtc>(&'a VblankCrtc<T>);
> +
> +impl<'a, T: VblankDriverCrtc> VblankGuard<'a, T> {
> +    /// Construct a new [`VblankGuard`]
> +    ///
> +    /// # Safety
> +    ///
> +    /// The caller must have already acquired
> [`drm_device.vbl_lock`].
> +    ///
> +    /// [`drm_device.vbl_lock`]: srctree/include/drm/drm_device.h
> +    pub(crate) unsafe fn new(crtc: &'a Crtc<T>, _irq: &'a
> LocalInterruptDisabled) -> Self {
> +        // INVARIANT: The caller promises that we've acquired
> `vbl_lock`
> +        Self(crtc.vblank_crtc())
> +    }
> +
> +    /// Returns the duration of a single scanout frame in ns.
> +    pub fn frame_duration(&self) -> i32 {
> +        // SAFETY: We hold the appropriate lock for this read via
> our type invariants.
> +        unsafe { *self.as_raw() }.framedur_ns
> +    }
> +
> +    /// Return the vblank core's cached copy of the currently set
> display mode.
> +    ///
> +    /// If the display is disabled, this will return `None`.
> +    pub fn hwmode(&self) -> Option<&DisplayMode> {
> +        // SAFETY: We hold the appropriate lock for this read via
> our type invariants.
> +        let ptr = unsafe { &raw const (*self.as_raw()).hwmode };
> +
> +        // SAFETY: We check here if the cached DisplayMode is Null,
> which means the only other
> +        // possibility is that the pointer points to a valid
> initialized drm_display_mode.
> +        (!ptr.is_null()).then(|| unsafe { DisplayMode::as_ref(ptr)
> })
> +    }
> +}
> +
> +impl<T: VblankDriverCrtc> Deref for VblankGuard<'_, T> {
> +    type Target = VblankCrtc<T>;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.0
> +    }
> +}
> +
> +impl<T: VblankDriverCrtc> Drop for VblankGuard<'_, T> {
> +    fn drop(&mut self) {
> +        // SAFETY:
> +        // - We acquired this spinlock when creating this object
> +        // - This lock is guaranteed to be initialized for as long
> as our DRM device is exposed to
> +        //   users.
> +        unsafe { bindings::spin_unlock(&raw mut
> (*self.drm_dev().as_raw()).vbl_lock) }
> +    }
> +}


^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v2 06/18] rust: drm: kms: add a Framebuffer::vmap() guard
  2026-07-03  3:00   ` [RFC PATCH v2 06/18] rust: drm: kms: add a Framebuffer::vmap() guard Mike Lothian
@ 2026-07-07 21:51     ` lyude
  0 siblings, 0 replies; 29+ messages in thread
From: lyude @ 2026-07-07 21:51 UTC (permalink / raw)
  To: Mike Lothian, rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	linux-kernel

We have VMap at home already, please (with your eyes) search the rest
of the kernel for it. The work for this was pushed to drm-rust-next
recently.

On Fri, 2026-07-03 at 04:00 +0100, Mike Lothian wrote:
> Framebuffer::vmap() maps a framebuffer's plane-0 backing pages into
> the
> kernel address space via drm_gem_fb_vmap()/vunmap(), returning an
> RAII
> FramebufferVmap guard that unmaps on drop. Only single-CPU-visible-
> plane
> framebuffers are supported (packed formats backed by GEM-shmem/CMA;
> multi-plane YUV or an unmapped imported dma-buf return EINVAL). This
> is
> the driver-side FbVmap guard the v1 series carried, moved into the
> layer
> proper so every KMS driver gets it instead of hand-rolling the same
> map/use/unmap dance.
> 
> Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
> Assisted-by: Claude:claude-sonnet-5 [Claude-Code]
> ---
>  rust/kernel/drm/kms/framebuffer.rs | 67
> +++++++++++++++++++++++++++++-
>  1 file changed, 66 insertions(+), 1 deletion(-)
> 
> diff --git a/rust/kernel/drm/kms/framebuffer.rs
> b/rust/kernel/drm/kms/framebuffer.rs
> index 54d0391388a9..1ec6779ba7de 100644
> --- a/rust/kernel/drm/kms/framebuffer.rs
> +++ b/rust/kernel/drm/kms/framebuffer.rs
> @@ -5,7 +5,12 @@
>  //! C header:
> [`include/drm/drm_framebuffer.h`](srctree/include/drm/drm_framebuffer
> .h)
>  
>  use super::{KmsDriver, ModeObject, Sealed};
> -use crate::{drm::device::Device, types::*};
> +use crate::{
> +    drm::device::Device,
> +    error::{code::EINVAL, to_result},
> +    prelude::*,
> +    types::*,
> +};
>  use bindings;
>  use core::{marker::*, ptr};
>  
> @@ -67,4 +72,64 @@ pub(super) unsafe fn from_raw<'a>(ptr: *const
> bindings::drm_framebuffer) -> &'a
>          // SAFETY: Our data layout is identical to drm_framebuffer
>          unsafe { &*ptr.cast() }
>      }
> +
> +    /// Return the raw `bindings::drm_framebuffer` for this
> framebuffer.
> +    #[inline]
> +    pub(crate) fn as_raw(&self) -> *mut bindings::drm_framebuffer {
> +        self.0.get()
> +    }
> +
> +    /// Map this framebuffer's plane-0 backing pages into the kernel
> address space for CPU
> +    /// access, for the duration of the returned guard.
> +    ///
> +    /// Only framebuffers with a single, CPU-visible plane are
> supported (i.e. packed formats
> +    /// backed by GEM-shmem/CMA memory, not multi-plane YUV or an
> IMPORTED dma-buf without a
> +    /// CPU mapping); other cases return `EINVAL`.
> +    pub fn vmap(&self) -> Result<FramebufferVmap<'_, T>> {
> +        // SAFETY: `iosys_map` is POD (a pointer union plus a bool);
> all-zero is a valid
> +        // "not mapped" value that `drm_gem_fb_vmap` overwrites for
> present planes.
> +        let mut map: [bindings::iosys_map; 4] = unsafe {
> core::mem::zeroed() };
> +        let mut data_map: [bindings::iosys_map; 4] = unsafe {
> core::mem::zeroed() };
> +        // SAFETY: `self.as_raw()` is a valid, GEM-backed
> framebuffer for the lifetime of `self`.
> +        to_result(unsafe {
> +            bindings::drm_gem_fb_vmap(self.as_raw(),
> map.as_mut_ptr(), data_map.as_mut_ptr())
> +        })?;
> +        // SAFETY: `map[0]` was just filled in by `drm_gem_fb_vmap`
> above.
> +        let vaddr = unsafe { map[0].__bindgen_anon_1.vaddr };
> +        if vaddr.is_null() {
> +            // SAFETY: balances the vmap just done, with the same
> `map`.
> +            unsafe { bindings::drm_gem_fb_vunmap(self.as_raw(),
> map.as_mut_ptr()) };
> +            return Err(EINVAL);
> +        }
> +        Ok(FramebufferVmap { fb: self, map, _p: PhantomData })
> +    }
> +}
> +
> +/// An RAII guard over a CPU mapping of a [`Framebuffer`]'s plane-0
> backing pages, created by
> +/// [`Framebuffer::vmap`].
> +///
> +/// The mapping is torn down when this guard is dropped, so an early
> return between mapping and
> +/// use can never leak it.
> +pub struct FramebufferVmap<'a, T: KmsDriver> {
> +    fb: &'a Framebuffer<T>,
> +    map: [bindings::iosys_map; 4],
> +    _p: PhantomData<T>,
> +}
> +
> +impl<'a, T: KmsDriver> FramebufferVmap<'a, T> {
> +    /// Plane 0's CPU virtual base address (guaranteed non-null for
> the guard's lifetime).
> +    #[inline]
> +    pub fn as_ptr(&self) -> *const u8 {
> +        // SAFETY: set non-null in `Framebuffer::vmap`; `vaddr` is
> the active union member.
> +        (unsafe { self.map[0].__bindgen_anon_1.vaddr }) as *const u8
> +    }
> +}
> +
> +impl<'a, T: KmsDriver> Drop for FramebufferVmap<'a, T> {
> +    fn drop(&mut self) {
> +        // SAFETY: `self.fb.as_raw()`/`self.map` are exactly the
> pair passed to
> +        // `drm_gem_fb_vmap` in `Framebuffer::vmap`, and the mapping
> has not been released
> +        // since.
> +        unsafe { bindings::drm_gem_fb_vunmap(self.fb.as_raw(),
> self.map.as_mut_ptr()) };
> +    }
>  }


^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RFC PATCH v2 01/18] rust: drm: kms: forward-port the safe mode-object layer onto the typestate device
  2026-07-03  3:00   ` [RFC PATCH v2 01/18] rust: drm: kms: forward-port the safe mode-object layer onto the typestate device Mike Lothian
  2026-07-07 21:46     ` lyude
@ 2026-07-07 22:21     ` lyude
  1 sibling, 0 replies; 29+ messages in thread
From: lyude @ 2026-07-07 22:21 UTC (permalink / raw)
  To: Mike Lothian, rust-for-linux
  Cc: dri-devel, David Airlie, Simona Vetter, Danilo Krummrich,
	Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	linux-kernel

NAK on the whole series.

The first patch starts with taking all of my work, changing the
authorship, adding a claude tag on the thing I spent 2 years of my life
trying to get upstream, on top of the dozens of other issues with this
patch series:

 * You, again, changed/removed my authorship on work that I wrote. In
   this case, you copied my entire git branch, re-authored it under
   yourself and added a claude assisted-by tag, and have tried
   submitting it to the kernel mailing list. That is absolutely not OK
   by any stretch, even for an RFC. The next time I see this, I am
   giving an immediate NAK and just pointing back at this email.
 * Rebasing means actually going through the entire patch series and
   modifying the patches so they apply. It doesn't mean changing the
   authorship on patches, squashing them together, and then adding a
   rebase patch at the end. If you need to have a hacked-together
   branch to make things work, that can't be part of the actual series
   you submitting.
 * Multiple import items on a single line.
 * I get that this is a RFC patch, but there are dozens of patches here
   that seem to do things that are entirely out of scope of KMS:
    - Changing the build system to workaround issues without any
      description as to the actual issues that were hit. It took me
      multiple tries to understand what you were even trying to fix, and
      regardless: it shouldn't be part of this patch series.
    - Blobs of text that appear to be copied straight from AI output,
      especially in the git commit message. Maintainers aren't here to
      clean this up for you, people read this and use this to understand
      what is going on.
    - More reimplementations of things that exist elsewhere, dozens of
      comments across patches that clearly didn't get looked at by a
      person, etc. etc.

I've left comments, but there are so many to leave here and it is very
obvious that a lot of this code was generated and not actually looked
over by a person. I get it, RFCs can be messy, but not like this. It's
much different when you ask a machine to generate a large amount of
code, don't actually look over it, and then ask for others to go
through and vet it.

I'm going to ask: if you're not willing to write the code here yourself
(as in, without claude), please simply don't submit another version.
I've received AI generated patches, they're usually fine, but that's
because they're generally used as large search replaces with limited
scope such that it's obvious the person who made the patch has
understood it. I don't see evidence that the vast majority of this was
even read before it was submitted. It's not a maintainer's job to take
slop and turn it into something legible. That's not the purpose of an
RFC either.

On Fri, 2026-07-03 at 04:00 +0100, Mike Lothian wrote:
> Port Lyude Paul's rvkms-slim safe mode-object layer
> (CRTC/plane/connector/
> encoder/vblank/atomic state) onto her newer rust-stuck typestate
> Device
> design (ParentDevice/DeviceContext/RegistrationData), which the
> preceding
> cherry-picked commits bring in. Source: lyude/rvkms-slim +
> lyude/rust/rust-stuck.
> 
> Same module split as rvkms-slim (kms/{atomic,connector,crtc,encoder,
> framebuffer,modes,plane,vblank}.rs), same trait shape, adjusted to
> the
> typestate device's generic Ctx/State parameters. This is a mechanical
> type-level port, not new design -- diff against lyude/rvkms-slim to
> see
> exactly what moved.
> 
> Does not build yet. The port's mode-object traits are mutually
> recursive in a way the current trait solver can't evaluate; the next
> few commits get it to a clean build.
> 
> Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
> Assisted-by: Claude:claude-sonnet-5 [Claude-Code]
> ---
>  rust/kernel/drm/kms.rs             |  395 +++++++++-
>  rust/kernel/drm/kms/atomic.rs      |  864 ++++++++++++++++++++++
>  rust/kernel/drm/kms/connector.rs   |  997 +++++++++++++++++++++++++
>  rust/kernel/drm/kms/crtc.rs        | 1110
> ++++++++++++++++++++++++++++
>  rust/kernel/drm/kms/encoder.rs     |  409 ++++++++++
>  rust/kernel/drm/kms/framebuffer.rs |   70 ++
>  rust/kernel/drm/kms/modes.rs       |   76 ++
>  rust/kernel/drm/kms/plane.rs       | 1095
> +++++++++++++++++++++++++++
>  rust/kernel/drm/kms/vblank.rs      |  461 ++++++++++++
>  9 files changed, 5469 insertions(+), 8 deletions(-)
>  create mode 100644 rust/kernel/drm/kms/atomic.rs
>  create mode 100644 rust/kernel/drm/kms/connector.rs
>  create mode 100644 rust/kernel/drm/kms/crtc.rs
>  create mode 100644 rust/kernel/drm/kms/encoder.rs
>  create mode 100644 rust/kernel/drm/kms/framebuffer.rs
>  create mode 100644 rust/kernel/drm/kms/modes.rs
>  create mode 100644 rust/kernel/drm/kms/plane.rs
>  create mode 100644 rust/kernel/drm/kms/vblank.rs
> 
> diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs
> index 084ed0aebd0e..11b09d2175db 100644
> --- a/rust/kernel/drm/kms.rs
> +++ b/rust/kernel/drm/kms.rs
> @@ -3,17 +3,37 @@
>  //! KMS driver abstractions for rust.
>  
>  use crate::{
> +    container_of,
>      drm::{
> -        device::Device,
> +        device::{Device, DeviceContext},
>          driver::Driver,
>          private::Sealed,
>          Uninit, //
>      },
>      error::to_result,
> -    prelude::*, //
> +    prelude::*,
> +    sync::{Mutex, MutexGuard},
> +    types::*,
>  };
>  use bindings;
> -use core::{marker::PhantomData, ops::Deref};
> +use core::{
> +    cell::Cell,
> +    marker::PhantomData,
> +    ops::Deref,
> +    ptr::{self, addr_of_mut, NonNull},
> +};
> +
> +// Forward-ported mode-object layer (see
> Documentation/gpu/rust/vino-kms-forward-port.md).
> +// Modules are declared here as each is grafted onto the rust-stuck
> typestate design.
> +// `modes` is self-contained and ported as-is; the rest follow per
> the plan's order.
> +pub mod atomic;
> +pub mod connector;
> +pub mod crtc;
> +pub mod encoder;
> +pub mod framebuffer;
> +pub mod modes;
> +pub mod plane;
> +pub mod vblank;
>  
>  /// The C vtable for a [`Device`].
>  ///
> @@ -77,13 +97,21 @@ impl KmsContext for Probing {}
>  /// This type is guaranteed to represent a [`Device`] that has not
> been registered with userspace,
>  /// and is in the process of setting up KMS support. It carries a
> [`KmsDeviceContext`] to indicate
>  /// which stage of the KMS setup process this [`Device`] is
> currently in.
> -pub struct NewKmsDevice<'a, T: Driver, C: KmsContext>(&'a Device<T,
> Uninit>, PhantomData<C>);
> +pub struct NewKmsDevice<'a, T: Driver, C: KmsContext> {
> +    drm: &'a Device<T, Uninit>,
> +    /// Tracks whether any CRTC created during probe requested
> vblank support, so that
> +    /// [`drm_vblank_init()`] can be called afterwards. Set by
> [`crtc::Crtc::new()`].
> +    ///
> +    /// [`drm_vblank_init()`]: srctree/include/drm/drm_vblank.h
> +    pub(crate) has_vblanks: Cell<bool>,
> +    _ctx: PhantomData<C>,
> +}
>  
>  impl<'a, T: Driver, C: KmsContext> Deref for NewKmsDevice<'a, T, C>
> {
>      type Target = Device<T, Uninit>;
>  
>      fn deref(&self) -> &Self::Target {
> -        self.0
> +        self.drm
>      }
>  }
>  
> @@ -95,15 +123,56 @@ fn deref(&self) -> &Self::Target {
>  /// [`PhantomData<Self>`]: PhantomData
>  #[vtable]
>  pub trait KmsDriver: Driver {
> +    /// The driver's [`DriverConnector`](connector::DriverConnector)
> implementation.
> +    ///
> +    /// TODO: This will be unneeded once we support multiple
> `DriverConnector` implementations.
> +    type Connector: connector::DriverConnector;
> +
> +    /// The driver's [`DriverPlane`](plane::DriverPlane)
> implementation.
> +    ///
> +    /// TODO: This will be unneeded once we support multiple
> `DriverPlane` implementations.
> +    type Plane: plane::DriverPlane;
> +
> +    /// The driver's [`DriverCrtc`](crtc::DriverCrtc)
> implementation.
> +    ///
> +    /// TODO: This will be unneeded once we support multiple
> `DriverCrtc` implementations.
> +    type Crtc: crtc::DriverCrtc;
> +
> +    /// The driver's [`DriverEncoder`](encoder::DriverEncoder)
> implementation.
> +    ///
> +    /// TODO: This will be unneeded once we support multiple
> `DriverEncoder` implementations.
> +    type Encoder: encoder::DriverEncoder;
> +
>      /// Return a [`ModeConfigInfo`] structure for this
> [`device::Device`].
>      fn mode_config_info(dev: &Device<Self, Uninit>) ->
> Result<ModeConfigInfo>
>      where
>          Self: Sized;
>  
>      /// Create mode objects like [`crtc::Crtc`], [`plane::Plane`],
> etc. for this device
> -    fn probe(drm: NewKmsDevice<'_, Self, Probing>) -> Result
> +    fn probe(drm: &NewKmsDevice<'_, Self, Probing>) -> Result
>      where
>          Self: Sized;
> +
> +    /// The optional [`atomic_commit_tail`] callback for this
> [`Device`].
> +    ///
> +    /// It must return a
> [`CommittedAtomicState`](atomic::CommittedAtomicState) to prove that
> it has
> +    /// signaled completion of the hw commit phase. Drivers may use
> this function to customize the
> +    /// order in which commits are performed during the atomic
> commit phase.
> +    ///
> +    /// If not provided, DRM will use its own default atomic commit
> tail helper
> +    /// `drm_atomic_helper_commit_tail`.
> +    ///
> +    /// [`atomic_commit_tail`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +    fn atomic_commit_tail<'a>(
> +        _state: atomic::AtomicCommitTail<'a, Self>,
> +        _modeset_token: atomic::ModesetsReadyToken<'_>,
> +        _plane_update_token: atomic::PlaneUpdatesReadyToken<'_>,
> +    ) -> atomic::CommittedAtomicState<'a, Self>
> +    where
> +        Self: Sized,
> +    {
> +        build_error::build_error("This function should not be
> reachable")
> +    }
>  }
>  
>  impl<T: KmsDriver> private::KmsImpl for T {
> @@ -123,7 +192,11 @@ impl<T: KmsDriver> private::KmsImpl for T {
>  
>          kms_helper_vtable: bindings::drm_mode_config_helper_funcs {
>              atomic_commit_setup: None,
> -            atomic_commit_tail: None,
> +            atomic_commit_tail: if T::HAS_ATOMIC_COMMIT_TAIL {
> +                Some(atomic::commit_tail_callback::<T>)
> +            } else {
> +                None
> +            },
>          },
>      });
>  
> @@ -156,7 +229,19 @@ unsafe fn probe_kms(drm: &Device<Self::Driver,
> Uninit>) -> Result<ModeConfigInfo
>          // SAFETY: We just setup all of the info required to call
> this function above.
>          to_result(unsafe {
> bindings::drmm_mode_config_init(drm.as_raw()) })?;
>  
> -        T::probe(NewKmsDevice(&drm, PhantomData))?;
> +        let new_kms = NewKmsDevice {
> +            drm: &drm,
> +            has_vblanks: Cell::new(false),
> +            _ctx: PhantomData,
> +        };
> +        T::probe(&new_kms)?;
> +
> +        if new_kms.has_vblanks.get() {
> +            // SAFETY: `has_vblanks` is only set when CRTCs with
> vblank support were created during
> +            // probe (the only place static mode objects are
> created), so the vblank state is ready
> +            // to be initialized.
> +            to_result(unsafe {
> bindings::drm_vblank_init(drm.as_raw(), drm.num_crtcs()) })?;
> +        }
>  
>          // TODO: In the future, we should add a hook here for
> initializing each state via hardware
>          // state readback.
> @@ -192,3 +277,297 @@ pub struct ModeConfigInfo {
>      /// An optional default fourcc format code to be preferred for
> clients.
>      pub preferred_fourcc: Option<u32>,
>  }
> +
> +// -----------------------------------------------------------------
> ----------
> +// Mode-object framework (forward-ported from lyude/rvkms-slim's
> kms.rs).
> +//
> +// Adapted to rust-stuck's typestate device: `Device<T, C>` defaults
> `C` to
> +// `Registered`, so most `Device<T>` references port unchanged; only
> creation
> +// paths (during probe) use the `Uninit` context, reached via
> `NewKmsDevice`.
> +// -----------------------------------------------------------------
> ----------
> +
> +/// Implement the repetitive from_opaque/try_from_opaque methods for
> all mode object and state
> +/// types.
> +///
> +/// Because there are so many different ways of accessing mode
> objects, their states, etc. we need a
> +/// macro that we can use for consistently implementing
> try_from_opaque()/from_opaque() functions to
> +/// convert from Opaque mode objects to fully typed mode objects.
> This macro handles that, and can
> +/// generate said functions for any kind of type which the original
> mode object driver trait can be
> +/// derived from.
> +macro_rules! impl_from_opaque_mode_obj {
> +    (
> +        fn <
> +            $( $lifetime:lifetime, )?
> +            $( $decl_bound_id:ident ),*
> +        > ($opaque:ty) -> $inner_ret_ty:ty
> +        $(
> +            where
> +                $( $extra_bound_id:ident : $extra_trait:ident<$(
> $extra_assoc:ident = $extra_param_match:ident ),+> ),+
> +        )? ;
> +        use
> +            $obj_trait_param:ident as $obj_trait:ident,
> +            $drv_trait_param:ident as
> KmsDriver<$drv_assoc_trait:ident = ...>
> +    ) => {
> +        #[doc = "Try to convert `opaque` into a fully qualified
> `Self`."]
> +        #[doc = ""]
> +        #[doc = concat!("This will try to convert `opaque` into
> `Self` if it shares the same [`",
> +                        stringify!($obj_trait), "`] implementation
> as `Self`.")]
> +        pub fn try_from_opaque<$( $lifetime, )? $( $decl_bound_id
> ),* >(
> +            opaque: $opaque
> +        ) -> Result<$inner_ret_ty, $opaque>
> +        where
> +            $drv_trait_param: KmsDriver<$drv_assoc_trait =
> $obj_trait_param>,
> +            $obj_trait_param: $obj_trait<Driver = $drv_trait_param>
> +            $( , $( $extra_bound_id: $extra_trait<$( $extra_assoc =
> $extra_param_match ),+> ),+ )?
> +        {
> +            // FIXME: What we really want to be doing here is
> comparing vtable pointers, but this is
> +            // currently blocked on getting unique vtable macros to
> ensure that each vtable has a
> +            // consistent memory pointer.
> +            // For the time being, we simply restrict things to one
> object type per driver and do a
> +            // transmutation based on that assumption holding true.
> +            // SAFETY: We currently only allow one object type per-
> driver, so this transmute is
> +            // always safe.
> +            Ok(unsafe { core::mem::transmute(opaque) })
> +        }
> +
> +        #[doc = "Convert `opaque` into a fully qualified `Self`."]
> +        #[doc = ""]
> +        #[doc = concat!("This is an infallible version of
> [`Self::try_from_opaque`]. This ",
> +                        "function is mainly useful for drivers where
> only a single [`",
> +                        stringify!($obj_trait), "`] implementation
> exists.")]
> +        #[doc = ""]
> +        #[doc = "# Panics"]
> +        #[doc = ""]
> +        #[doc = concat!("This function will panic if `opaque`
> belongs to a different [`",
> +                        stringify!($obj_trait), "`]
> implementation.")]
> +        pub fn from_opaque<$( $lifetime, )? $( $decl_bound_id ),* >(
> +            opaque: $opaque
> +        ) -> $inner_ret_ty
> +        where
> +            $drv_trait_param: KmsDriver<$drv_assoc_trait =
> $obj_trait_param>,
> +            $obj_trait_param: $obj_trait<Driver = $drv_trait_param>
> +            $( , $( $extra_bound_id: $extra_trait<$( $extra_assoc =
> $extra_param_match ),+> ),+ )?
> +        {
> +            Self::try_from_opaque(opaque)
> +                .map_or(None, |o| Some(o))
> +                .expect(concat!("Passed ", stringify!($opaque), "
> does not share this ",
> +                                stringify!($obj_trait), "
> implementation."))
> +        }
> +    };
> +}
> +
> +pub(crate) use impl_from_opaque_mode_obj;
> +
> +impl<T: KmsDriver, C: DeviceContext> Device<T, C> {
> +    /// Retrieve a pointer to the mode_config mutex
> +    #[inline]
> +    pub(crate) fn mode_config_mutex(&self) -> &Mutex<()> {
> +        // SAFETY: This lock is initialized for as long as
> `Device<T>` is exposed to users
> +        unsafe {
> Mutex::from_raw(addr_of_mut!((*self.as_raw()).mode_config.mutex)) }
> +    }
> +
> +    /// Return the number of registered [`Crtc`](crtc::Crtc) objects
> on this [`Device`].
> +    #[inline]
> +    pub fn num_crtcs(&self) -> u32 {
> +        // SAFETY:
> +        // * This can only be modified during the single-threaded
> context before registration, so
> +        //   this is safe
> +        // * num_crtc is always >= 0, so casting to u32 is fine
> +        unsafe { (*self.as_raw()).mode_config.num_crtc as u32 }
> +    }
> +}
> +
> +impl<T: KmsDriver> Device<T> {
> +    /// Acquire the [`mode_config.mutex`] for this [`Device`].
> +    #[inline]
> +    pub fn mode_config_lock(&self) -> ModeConfigGuard<'_, T> {
> +        // INVARIANT: We're locking mode_config.mutex, fulfilling
> our invariant that this lock is
> +        // held throughout ModeConfigGuard's lifetime.
> +        ModeConfigGuard(self.mode_config_mutex().lock(),
> PhantomData)
> +    }
> +}
> +
> +/// A modesetting object in DRM.
> +///
> +/// This is any type of object where the underlying C object
> contains a [`struct drm_mode_object`].
> +/// This type requires [`Send`] + [`Sync`] as all modesetting
> objects in DRM are able to be sent
> +/// between threads.
> +///
> +/// This type is only implemented by the DRM crate itself.
> +///
> +/// # Safety
> +///
> +/// [`raw_mode_obj()`] must always return a valid pointer to an
> initialized
> +/// [`struct drm_mode_object`].
> +///
> +/// [`struct drm_mode_object`]:
> srctree/include/drm/drm_mode_object.h
> +/// [`raw_mode_obj()`]: ModeObject::raw_mode_obj()
> +pub unsafe trait ModeObject: Sealed + Send + Sync {
> +    /// The parent driver for this [`ModeObject`].
> +    type Driver: KmsDriver;
> +
> +    /// Return the [`Device`] for this [`ModeObject`].
> +    fn drm_dev(&self) -> &Device<Self::Driver>;
> +
> +    /// Return a pointer to the [`struct drm_mode_object`] for this
> [`ModeObject`].
> +    ///
> +    /// [`struct drm_mode_object`]:
> (srctree/include/drm/drm_mode_object.h)
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object;
> +}
> +
> +/// A trait for modesetting objects which don't come with their own
> reference-counting.
> +///
> +/// Some [`ModeObject`] types in DRM do not have a reference count.
> These types are considered
> +/// "static" and share the lifetime of their parent [`Device`]. To
> retrieve an owned reference to
> +/// such types, see [`KmsRef`].
> +///
> +/// # Safety
> +///
> +/// This trait must only be implemented for modesetting objects
> which do not have a refcount within
> +/// their [`struct drm_mode_object`], otherwise [`KmsRef`] can't
> guarantee the object will stay
> +/// alive.
> +///
> +/// [`struct drm_mode_object`]:
> (srctree/include/drm/drm_mode_object.h)
> +pub unsafe trait StaticModeObject: ModeObject {}
> +
> +/// An owned reference to a [`StaticModeObject`].
> +///
> +/// Note that since [`StaticModeObject`] types share the lifetime of
> their parent [`Device`], the
> +/// parent [`Device`] will stay alive as long as this type exists.
> Thus, users should be aware that
> +/// storing a [`KmsRef`] within a [`ModeObject`] is a circular
> reference.
> +///
> +/// # Invariants
> +///
> +/// `self.0` points to a valid instance of `T` throughout the
> lifetime of this type.
> +pub struct KmsRef<T: StaticModeObject>(NonNull<T>);
> +
> +// SAFETY: Owned references to DRM device are thread-safe.
> +unsafe impl<T: StaticModeObject> Send for KmsRef<T> {}
> +// SAFETY: Owned references to DRM device are thread-safe.
> +unsafe impl<T: StaticModeObject> Sync for KmsRef<T> {}
> +
> +impl<T: StaticModeObject> From<&T> for KmsRef<T> {
> +    fn from(value: &T) -> Self {
> +        // INVARIANT: Because the lifetime of the StaticModeObject
> is the same as the lifetime of
> +        // its parent device, we can ensure that `value` remains
> alive by incrementing the device's
> +        // reference count. The device will only disappear once we
> drop this reference in `Drop`.
> +        value.drm_dev().inc_ref();
> +
> +        Self(value.into())
> +    }
> +}
> +
> +impl<T: StaticModeObject> Drop for KmsRef<T> {
> +    fn drop(&mut self) {
> +        // SAFETY: We're reclaiming the reference we leaked in
> From<&T>
> +        drop(unsafe { ARef::from_raw(self.drm_dev().into()) })
> +    }
> +}
> +
> +impl<T: StaticModeObject> Deref for KmsRef<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        // SAFETY: We're guaranteed object will point to a valid
> object as long as we hold dev
> +        unsafe { self.0.as_ref() }
> +    }
> +}
> +
> +impl<T: StaticModeObject> Clone for KmsRef<T> {
> +    fn clone(&self) -> Self {
> +        // INVARIANT: See `From<&T>`; we keep the parent device
> alive for this new reference.
> +        self.drm_dev().inc_ref();
> +
> +        Self(self.0)
> +    }
> +}
> +
> +macro_rules! impl_aref_for_mode_object {
> +    (impl $( < $( $param:ident: $bound:ident ),+ > )? for $type:ty)
> => {
> +        // SAFETY: drm_mode_object_get()/put() ensure the type is
> ref-counted according to the
> +        // safety contract
> +        unsafe impl $( < $( $param: $bound ),+ > )?
> kernel::types::AlwaysRefCounted for $type {
> +            #[inline]
> +            fn inc_ref(&self) {
> +                // SAFETY: We're guaranteed by the safety contract
> of `ModeObject` that
> +                // `raw_mode_obj()` always returns a pointer to an
> initialized `drm_mode_object`.
> +                unsafe {
> kernel::bindings::drm_mode_object_get(self.raw_mode_obj()) }
> +            }
> +
> +            #[inline]
> +            unsafe fn dec_ref(obj: core::ptr::NonNull<Self>) {
> +                // SAFETY: We're guaranteed by the safety contract
> of `ModeObject` that
> +                // `raw_mode_obj()` always returns a pointer to an
> initialized `drm_mode_object`.
> +                unsafe {
> kernel::bindings::drm_mode_object_put(obj.as_ref().raw_mode_obj()) }
> +            }
> +        }
> +    };
> +}
> +
> +pub(super) use impl_aref_for_mode_object;
> +
> +/// A trait for any object related to a [`ModeObject`] that can
> return its vtable.
> +///
> +/// This reference will be used for checking whether an opaque
> representation of a mode object uses a
> +/// specific driver trait implementation.
> +///
> +/// # Safety
> +///
> +/// `ModeObjectVtable::vtable()` must always return a valid pointer
> to the relevant mode object's
> +/// vtable.
> +pub(crate) unsafe trait ModeObjectVtable {
> +    /// The type for the auto-generated vtable.
> +    type Vtable;
> +
> +    /// Return a static reference to the auto-generated vtable for
> the relevant mode object.
> +    fn vtable(&self) -> *const Self::Vtable;
> +}
> +
> +/// A mode config guard.
> +///
> +/// This is an exclusive primitive that represents when
> [`drm_device.mode_config.mutex`] is held - as
> +/// some modesetting operations (particularly ones related to
> [`connectors`](connector)) are still
> +/// protected under this single lock. The lock will be dropped once
> this object is dropped.
> +///
> +/// # Invariants
> +///
> +/// - `self.0` is contained within a [`struct drm_mode_config`],
> which is contained within a
> +///   [`struct drm_device`].
> +/// - The [`KmsDriver`] implementation of that [`struct drm_device`]
> is always `T`.
> +/// - This type proves that [`drm_device.mode_config.mutex`] is
> acquired.
> +///
> +/// [`struct drm_mode_config`]: (srctree/include/drm/drm_device.h)
> +/// [`drm_device.mode_config.mutex`]:
> (srctree/include/drm/drm_device.h)
> +/// [`struct drm_device`]: (srctree/include/drm/drm_device.h)
> +pub struct ModeConfigGuard<'a, T: KmsDriver>(MutexGuard<'a, ()>,
> PhantomData<T>);
> +
> +impl<'a, T: KmsDriver> ModeConfigGuard<'a, T> {
> +    /// Return the [`Device`] that this [`ModeConfigGuard`] belongs
> to.
> +    pub fn drm_dev(&self) -> &'a Device<T> {
> +        let lock: *mut bindings::mutex =
> ptr::from_ref(self.0.lock_ref()).cast_mut().cast();
> +
> +        // SAFETY:
> +        // - `self` is embedded within a `drm_mode_config` via our
> type invariants
> +        // - `self.0.lock` has an equivalent data type to `mutex`
> via its type invariants.
> +        let mode_config = unsafe { container_of!(lock,
> bindings::drm_mode_config, mutex) };
> +
> +        // SAFETY: And that `drm_mode_config` lives in a
> `drm_device` via type invariants.
> +        unsafe {
> +            Device::from_raw(container_of!(
> +                mode_config,
> +                bindings::drm_device,
> +                mode_config
> +            ))
> +        }
> +    }
> +
> +    /// Assert that the given device is the owner of this mode
> config guard.
> +    ///
> +    /// # Panics
> +    ///
> +    /// Panics if `dev` is different from the owning device for this
> mode config guard.
> +    #[inline]
> +    pub(crate) fn assert_owner(&self, dev: &Device<T>) {
> +        assert!(ptr::eq(self.drm_dev(), dev));
> +    }
> +}
> diff --git a/rust/kernel/drm/kms/atomic.rs
> b/rust/kernel/drm/kms/atomic.rs
> new file mode 100644
> index 000000000000..cc14bff47abd
> --- /dev/null
> +++ b/rust/kernel/drm/kms/atomic.rs
> @@ -0,0 +1,864 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! [`struct drm_atomic_state`] related bindings for rust.
> +//!
> +//! [`struct drm_atomic_state`]: srctree/include/drm/drm_atomic.h
> +use super::{connector::*, crtc::*, plane::*, KmsDriver, ModeObject};
> +use crate::{
> +    bindings,
> +    drm::device::Device,
> +    error::{from_err_ptr, to_result},
> +    prelude::*,
> +    types::*,
> +};
> +use core::{cell::Cell, marker::*, mem::ManuallyDrop, ops::*,
> ptr::NonNull};
> +
> +/// The main wrapper around [`struct drm_atomic_state`].
> +///
> +/// This type is usually embedded within another interface such as
> an [`AtomicStateMutator`].
> +///
> +/// # Invariants
> +///
> +/// - The data layout of this type is identical to [`struct
> drm_atomic_state`].
> +/// - `state` is initialized for as long as this type is exposed to
> users.
> +///
> +/// [`struct drm_atomic_state`]: srctree/include/drm/drm_atomic.h
> +#[repr(transparent)]
> +pub struct AtomicState<T: KmsDriver> {
> +    pub(super) state: Opaque<bindings::drm_atomic_state>,
> +    _p: PhantomData<T>,
> +}
> +
> +impl<T: KmsDriver> AtomicState<T> {
> +    /// Reconstruct an immutable reference to an atomic state from
> the given pointer
> +    ///
> +    /// # Safety
> +    ///
> +    /// `ptr` must point to a valid initialized instance of [`struct
> drm_atomic_state`].
> +    ///
> +    /// [`struct drm_atomic_state`]:
> srctree/include/drm/drm_atomic.h
> +    #[allow(dead_code)]
> +    pub(super) unsafe fn from_raw<'a>(ptr: *const
> bindings::drm_atomic_state) -> &'a Self {
> +        // SAFETY: Our data layout is identical
> +        // INVARIANT: Our safety contract upholds the guarantee that
> `state` is initialized for as
> +        // long as this type is exposed to users.
> +        unsafe { &*ptr.cast() }
> +    }
> +
> +    pub(crate) fn as_raw(&self) -> *mut bindings::drm_atomic_state {
> +        self.state.get()
> +    }
> +
> +    /// Return the [`Device`] associated with this [`AtomicState`].
> +    pub fn drm_dev(&self) -> &Device<T> {
> +        // SAFETY:
> +        // - `state` is initialized via our type invariants.
> +        // - `dev` is invariant throughout the lifetime of
> `AtomicState`
> +        unsafe { Device::from_raw((*self.state.get()).dev) }
> +    }
> +
> +    /// Return the old atomic state for `crtc`, if it is present
> within this [`AtomicState`].
> +    pub fn get_old_crtc_state<C>(&self, crtc: &C) ->
> Option<&C::State>
> +    where
> +        C: ModesettableCrtc + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: This function either returns NULL or a valid
> pointer to a `drm_crtc_state`
> +        unsafe {
> +            bindings::drm_atomic_get_old_crtc_state(self.as_raw(),
> crtc.as_raw())
> +                .as_ref()
> +                .map(|p| C::State::from_raw(p))
> +        }
> +    }
> +
> +    /// Return the old atomic state for `plane`, if it is present
> within this [`AtomicState`].
> +    pub fn get_old_plane_state<P>(&self, plane: &P) ->
> Option<&P::State>
> +    where
> +        P: ModesettablePlane + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: This function either returns NULL or a valid
> pointer to a `drm_plane_state`
> +        unsafe {
> +            bindings::drm_atomic_get_old_plane_state(self.as_raw(),
> plane.as_raw())
> +                .as_ref()
> +                .map(|p| P::State::from_raw(p))
> +        }
> +    }
> +
> +    /// Return the old atomic state for `connector` if it is present
> within this [`AtomicState`].
> +    pub fn get_old_connector_state<C>(&self, connector: &C) ->
> Option<&C::State>
> +    where
> +        C: ModesettableConnector + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: This function either returns NULL or a valid
> pointer to a `drm_connector_state`.
> +        unsafe {
> +           
> bindings::drm_atomic_get_old_connector_state(self.as_raw(),
> connector.as_raw())
> +                .as_ref()
> +                .map(|p| C::State::from_raw(p))
> +        }
> +    }
> +}
> +
> +// SAFETY: DRM atomic state objects are always reference counted and
> the get/put functions satisfy
> +// the requirements.
> +unsafe impl<T: KmsDriver> AlwaysRefCounted for AtomicState<T> {
> +    fn inc_ref(&self) {
> +        // SAFETY: `state` is initialized for as long as this type
> is exposed to users
> +        unsafe { bindings::drm_atomic_state_get(self.state.get()) }
> +    }
> +
> +    unsafe fn dec_ref(obj: NonNull<Self>) {
> +        // SAFETY: `obj` contains a valid non-null pointer to an
> initialized `Self`.
> +        unsafe { bindings::drm_atomic_state_put(obj.as_ptr().cast())
> }
> +    }
> +}
> +
> +/// A smart-pointer for modifying the contents of an atomic state.
> +///
> +/// As it's not unreasonable for a modesetting driver to want to
> have references to the state of
> +/// multiple modesetting objects at once, along with mutating
> multiple states for unique modesetting
> +/// objects at once, this type provides a mechanism for safely doing
> both of these things.
> +///
> +/// To honor Rust's aliasing rules regarding mutable references,
> this structure ensures only one
> +/// mutable reference to a mode object's atomic state may exist at a
> time - and refuses to provide
> +/// another if one has already been taken out using runtime checks.
> +pub struct AtomicStateMutator<T: KmsDriver> {
> +    /// The state being mutated. Note that the use of `ManuallyDrop`
> here is because mutators are
> +    /// only constructed in FFI callbacks and thus borrow their
> references to the atomic state from
> +    /// DRM. Composers, which make use of mutators internally, can
> potentially be owned by rust code
> +    /// if a driver is performing an atomic commit internally - and
> thus will call the drop
> +    /// implementation here.
> +    state: ManuallyDrop<ARef<AtomicState<T>>>,
> +
> +    /// Bitmask of borrowed CRTC state objects
> +    pub(super) borrowed_crtcs: Cell<u32>,
> +    /// Bitmask of borrowed plane state objects
> +    pub(super) borrowed_planes: Cell<u32>,
> +    /// Bitmask of borrowed connector state objects
> +    pub(super) borrowed_connectors: Cell<u32>,
> +}
> +
> +impl<T: KmsDriver> AtomicStateMutator<T> {
> +    /// Construct a new [`AtomicStateMutator`]
> +    ///
> +    /// # Safety
> +    ///
> +    /// `ptr` must point to a valid `drm_atomic_state`
> +    #[allow(dead_code)]
> +    pub(super) unsafe fn new(ptr:
> NonNull<bindings::drm_atomic_state>) -> Self {
> +        Self {
> +            // SAFETY: The data layout of `AtomicState<T>` is
> identical to drm_atomic_state
> +            // We use `ManuallyDrop` because `AtomicStateMutator` is
> only ever provided to users in
> +            // the context of KMS callbacks. As such, skipping ref
> inc/dec for the atomic state is
> +            // convienent for our bindings.
> +            state: ManuallyDrop::new(unsafe {
> ARef::from_raw(ptr.cast()) }),
> +            borrowed_planes: Cell::default(),
> +            borrowed_crtcs: Cell::default(),
> +            borrowed_connectors: Cell::default(),
> +        }
> +    }
> +
> +    pub(crate) fn as_raw(&self) -> *mut bindings::drm_atomic_state {
> +        self.state.as_raw()
> +    }
> +
> +    /// Return the [`Device`] for this [`AtomicStateMutator`].
> +    pub fn drm_dev(&self) -> &Device<T> {
> +        self.state.drm_dev()
> +    }
> +
> +    /// Retrieve the last committed atomic state for `crtc` if
> `crtc` has already been added to the
> +    /// atomic state being composed.
> +    ///
> +    /// Returns `None` otherwise.
> +    pub fn get_old_crtc_state<C>(&self, crtc: &C) ->
> Option<&C::State>
> +    where
> +        C: ModesettableCrtc + ModeObject<Driver = T>,
> +    {
> +        self.state.get_old_crtc_state(crtc)
> +    }
> +
> +    /// Retrieve the last committed atomic state for `connector` if
> `connector` has already been
> +    /// added to the atomic state being composed.
> +    ///
> +    /// Returns `None` otherwise.
> +    pub fn get_old_connector_state<C>(&self, connector: &C) ->
> Option<&C::State>
> +    where
> +        C: ModesettableConnector + ModeObject<Driver = T>,
> +    {
> +        self.state.get_old_connector_state(connector)
> +    }
> +
> +    /// Retrieve the last committed atomic state for `plane` if
> `plane` has already been added to
> +    /// the atomic state being composed.
> +    ///
> +    /// Returns `None` otherwise.
> +    pub fn get_old_plane_state<P>(&self, plane: &P) ->
> Option<&P::State>
> +    where
> +        P: ModesettablePlane + ModeObject<Driver = T>,
> +    {
> +        self.state.get_old_plane_state(plane)
> +    }
> +
> +    /// Return a composer for `plane`s new atomic state if it was
> previously added to the atomic
> +    /// state being composed.
> +    ///
> +    /// Returns `None` otherwise, or if another mutator still exists
> for this state.
> +    pub fn get_new_crtc_state<C>(&self, crtc: &C) ->
> Option<CrtcStateMutator<'_, C::State>>
> +    where
> +        C: ModesettableCrtc + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: DRM either returns NULL or a valid pointer to a
> `drm_crtc_state`
> +        let state =
> +            unsafe {
> bindings::drm_atomic_get_new_crtc_state(self.as_raw(), crtc.as_raw())
> };
> +
> +        CrtcStateMutator::<C::State>::new(self,
> NonNull::new(state)?)
> +    }
> +
> +    /// Return a composer for `plane`s new atomic state if it was
> previously added to the atomic
> +    /// state being composed.
> +    ///
> +    /// Returns `None` otherwise, or if another mutator still exists
> for this state.
> +    pub fn get_new_plane_state<P>(&self, plane: &P) ->
> Option<PlaneStateMutator<'_, P::State>>
> +    where
> +        P: ModesettablePlane + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: DRM either returns NULL or a valid pointer to a
> `drm_plane_state`.
> +        let state =
> +            unsafe {
> bindings::drm_atomic_get_new_plane_state(self.as_raw(),
> plane.as_raw()) };
> +
> +        PlaneStateMutator::<P::State>::new(self,
> NonNull::new(state)?)
> +    }
> +
> +    /// Return a composer for `crtc`s new atomic state if it was
> previously added to the atomic
> +    /// state being composed.
> +    ///
> +    /// Returns `None` otherwise, or if another mutator still exists
> for this state.
> +    pub fn get_new_connector_state<C>(
> +        &self,
> +        connector: &C,
> +    ) -> Option<ConnectorStateMutator<'_, C::State>>
> +    where
> +        C: ModesettableConnector + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: DRM either returns NULL or a valid pointer to a
> `drm_connector_state`
> +        let state = unsafe {
> +           
> bindings::drm_atomic_get_new_connector_state(self.as_raw(),
> connector.as_raw())
> +        };
> +
> +        ConnectorStateMutator::<C::State>::new(self,
> NonNull::new(state)?)
> +    }
> +}
> +
> +/// An [`AtomicStateMutator`] wrapper which is not yet part of any
> commit operation.
> +///
> +/// Since it's not yet part of a commit operation, new mode objects
> may be added to the state. It
> +/// also holds a reference to the underlying [`AtomicState`] that
> will be released when this object
> +/// is dropped.
> +pub struct AtomicStateComposer<T: KmsDriver>(AtomicStateMutator<T>);
> +
> +impl<T: KmsDriver> Deref for AtomicStateComposer<T> {
> +    type Target = AtomicStateMutator<T>;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.0
> +    }
> +}
> +
> +impl<T: KmsDriver> Drop for AtomicStateComposer<T> {
> +    fn drop(&mut self) {
> +        // SAFETY: We're in drop, so this is guaranteed to be the
> last possible reference
> +        unsafe { ManuallyDrop::drop(&mut self.0.state) }
> +    }
> +}
> +
> +impl<T: KmsDriver> AtomicStateComposer<T> {
> +    /// # Safety
> +    ///
> +    /// The caller guarantees that `ptr` points to a valid instance
> of `drm_atomic_state`.
> +    pub(crate) unsafe fn new(ptr:
> NonNull<bindings::drm_atomic_state>) -> Self {
> +        // SAFETY: see `AtomicStateMutator::from_raw()`
> +        Self(unsafe { AtomicStateMutator::new(ptr) })
> +    }
> +
> +    /// Attempt to add the state for `crtc` to the atomic state for
> this composer if it hasn't
> +    /// already been added, and create a mutator for it.
> +    ///
> +    /// If a composer already exists for this `crtc`, this function
> returns `Error(EBUSY)`. If
> +    /// attempting to add the state fails, another error code will
> be returned.
> +    pub fn add_crtc_state<C>(&self, crtc: &C) ->
> Result<CrtcStateMutator<'_, C::State>>
> +    where
> +        C: ModesettableCrtc + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: DRM will only return a valid pointer to a
> `drm_crtc_state` - or an error.
> +        let state = unsafe {
> +            from_err_ptr(bindings::drm_atomic_get_crtc_state(
> +                self.as_raw(),
> +                crtc.as_raw(),
> +            ))
> +            .map(|c| NonNull::new_unchecked(c))
> +        }?;
> +
> +        CrtcStateMutator::<C::State>::new(self, state).ok_or(EBUSY)
> +    }
> +
> +    /// Attempt to add the state for `plane` to the atomic state for
> this composer if it hasn't
> +    /// already been added, and create a mutator for it.
> +    ///
> +    /// If a composer already exists for this `plane`, this function
> returns `Error(EBUSY)`. If
> +    /// attempting to add the state fails, another error code will
> be returned.
> +    pub fn add_plane_state<P>(&self, plane: &P) ->
> Result<PlaneStateMutator<'_, P::State>>
> +    where
> +        P: ModesettablePlane + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: DRM will only return a valid pointer to a
> `drm_plane_state` - or an error.
> +        let state = unsafe {
> +            from_err_ptr(bindings::drm_atomic_get_plane_state(
> +                self.as_raw(),
> +                plane.as_raw(),
> +            ))
> +            .map(|p| NonNull::new_unchecked(p))
> +        }?;
> +
> +        PlaneStateMutator::<P::State>::new(self, state).ok_or(EBUSY)
> +    }
> +
> +    /// Attempt to add the state for `connector` to the atomic state
> for this composer if it hasn't
> +    /// already been added, and create a mutator for it.
> +    ///
> +    /// If a composer already exists for this `connector`, this
> function returns `Error(EBUSY)`. If
> +    /// attempting to add the state fails, another error code will
> be returned.
> +    pub fn add_connector_state<C>(
> +        &self,
> +        connector: &C,
> +    ) -> Result<ConnectorStateMutator<'_, C::State>>
> +    where
> +        C: ModesettableConnector + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: DRM will only return a valid pointer to a
> `drm_plane_state` - or an error.
> +        let state = unsafe {
> +            from_err_ptr(bindings::drm_atomic_get_connector_state(
> +                self.as_raw(),
> +                connector.as_raw(),
> +            ))
> +            .map(|c| NonNull::new_unchecked(c))
> +        }?;
> +
> +        ConnectorStateMutator::<C::State>::new(self,
> state).ok_or(EBUSY)
> +    }
> +
> +    /// Attempt to add any planes affected by changes on `crtc` to
> this [`AtomicStateComposer`].
> +    ///
> +    /// Will return an [`Error`] if this fails.
> +    pub fn add_affected_planes<C>(&self, crtc: &C) -> Result
> +    where
> +        C: ModesettableCrtc + ModeObject<Driver = T>,
> +    {
> +        // SAFETY: Both .as_raw() values are guaranteed to return a
> valid pointer
> +        to_result(unsafe {
> bindings::drm_atomic_add_affected_planes(self.as_raw(),
> crtc.as_raw()) })
> +    }
> +}
> +
> +/// A macro for declaring the repetitive take_all(), take_state(),
> etc. methods for atomic state
> +/// token types.
> +///
> +/// It is assumed that $token_name refers to a struct that contains
> two members:
> +///
> +/// - `state`: This should be the atomic state type to use
> +/// - The object in question. The name of this member is generated
> by converting $obj to lowercase.
> +///
> +/// The struct should have one lifetime ($lifetime_a) declared, and
> one meta-variable ($meta) which
> +/// should be bound to the Driver* trait for the given mode object.
> +macro_rules! impl_atomic_state_token_ops {
> +    (
> +        $token_name:ident,
> +        $state:ident,
> +        $obj:ident,
> +        use <$lifetime_a:lifetime, $meta:ident>
> +    ) => {
> +        kernel::macros::paste! {
> +            /// Create a new token.
> +            ///
> +            /// # Safety
> +            ///
> +            /// To use this function it must be known in the current
> context that:
> +            ///
> +            /// - The object has had its atomic states added to
> `state`.
> +            /// - No state mutator can possibly be taken out for the
> objects new state.
> +            pub(crate) unsafe fn new(
> +                [<$obj:lower>]: &$lifetime_a $obj<$meta>,
> +                state: &$lifetime_a $state<$meta::Driver>,
> +            ) -> Self {
> +                Self { [<$obj:lower>], state }
> +            }
> +
> +            #[doc = concat!("Get the [`", stringify!($obj), "`]
> associated with this",
> +                            " [`", stringify!($token_name), "`].")]
> +            pub fn [<$obj:lower>](&self) -> &$lifetime_a $obj<$meta>
> {
> +                self.[<$obj:lower>]
> +            }
> +
> +            /// Exchange this token for a (atomic_state, old_state,
> new_state) tuple.
> +            pub fn take_all(self) -> (
> +                &$lifetime_a $state<$meta::Driver>,
> +                &$lifetime_a [<$obj State>]<$meta::State>,
> +                [<$obj StateMutator>]<$lifetime_a, [<$obj
> State>]<$meta::State>>,
> +            ) {
> +                let (old_state, new_state) = (
> +                    self.state.[<get_old_ $obj:lower
> _state>](self.[<$obj:lower>]),
> +                    self.state.[<get_new_ $obj:lower
> _state>](self.[<$obj:lower>]),
> +                );
> +
> +                // SAFETY:
> +                // - Both the old and new object state are present
> in `state` via our type
> +                //   invariants.
> +                // - The new state is guaranteed to have no mutators
> taken out via our type
> +                //   invariants.
> +                let (old_state, new_state) = unsafe {
> +                    (old_state.unwrap_unchecked(),
> new_state.unwrap_unchecked())
> +                };
> +
> +                (self.state, old_state, new_state)
> +            }
> +
> +            #[doc = concat!("Exchange this token for the old [`",
> stringify!($obj), "State`].")]
> +            pub fn take_old_state(self) -> &$lifetime_a [<$obj
> State>]<$meta::State> {
> +                let old = self.state.[<get_old_ $obj:lower
> _state>](self.[<$obj:lower>]);
> +
> +                // SAFETY: The old state is guaranteed to be present
> in `state` via our type
> +                // invariants.
> +                unsafe { old.unwrap_unchecked() }
> +            }
> +
> +            #[doc = concat!("Exchange this token for the new [`",
> stringify!($obj), "State`].")]
> +            pub fn take_new_state(
> +                self
> +            ) -> [<$obj StateMutator>]<$lifetime_a, [<$obj
> State>]<$meta::State>> {
> +                let new = self.state.[<get_new_ $obj:lower
> _state>](self.[<$obj:lower>]);
> +
> +                // SAFETY:
> +                // - The new state is guaranteed to be present in
> our `state` via our type
> +                //   invariants.
> +                // - The new state is guaranteed not to have any
> mutators taken out for it via our
> +                //   type invariants.
> +                unsafe { new.unwrap_unchecked() }
> +            }
> +
> +            #[doc = concat!("Exchange this token for both the old
> and new [`",
> +                            stringify!($obj), "State`].")]
> +            pub fn take_old_new_state(self) -> (
> +                &$lifetime_a [<$obj State>]<$meta::State>,
> +                [<$obj StateMutator>]<$lifetime_a, [<$obj
> State>]<$meta::State>>,
> +            ) {
> +                let (old_state, new_state) = (
> +                    self.state.[<get_old_ $obj:lower
> _state>](self.[<$obj:lower>]),
> +                    self.state.[<get_new_ $obj:lower
> _state>](self.[<$obj:lower>]),
> +                );
> +
> +                // SAFETY:
> +                // - Both the old and new object state are present
> in `state` via our type
> +                //   invariants.
> +                // - The new state is guaranteed to have no mutators
> taken out via our type
> +                //   invariants.
> +                let (old_state, new_state) = unsafe {
> +                    (old_state.unwrap_unchecked(),
> new_state.unwrap_unchecked())
> +                };
> +
> +                (old_state, new_state)
> +            }
> +
> +            #[doc = concat!("Exchange this token for both the [`",
> stringify!($state),
> +                            "`] and the old [`", stringify!($obj),
> "State`].")]
> +            pub fn take_state_old_state(self) -> (
> +                &$lifetime_a $state<$meta::Driver>,
> +                &$lifetime_a [<$obj State>]<$meta::State>,
> +            ) {
> +                let old = self.state.[<get_old_ $obj:lower
> _state>](self.[<$obj:lower>]);
> +
> +                // SAFETY: The old state is guaranteed to be present
> in `state` via our type
> +                // invariants.
> +                (self.state, unsafe { old.unwrap_unchecked() })
> +            }
> +
> +            #[doc = concat!("Exchange this token for both the [`",
> stringify!($state),
> +                            "`] and the new [`", stringify!($obj),
> "State`].")]
> +            pub fn take_state_new_state(self) -> (
> +                &$lifetime_a $state<$meta::Driver>,
> +                [<$obj StateMutator>]<$lifetime_a, [<$obj
> State>]<$meta::State>>,
> +            ) {
> +                let new = self.state.[<get_new_ $obj:lower
> _state>](self.[<$obj:lower>]);
> +
> +                // SAFETY:
> +                // - The new state is guaranteed to be present in
> `state` via our type
> +                //   invariants.
> +                // - The new state is guaranteed to have no mutators
> taken out via our type
> +                //   invariants.
> +                (self.state, unsafe { new.unwrap_unchecked() })
> +            }
> +        }
> +
> +        #[doc = concat!("Exchange this token for the [`",
> stringify!($state), "`].")]
> +        pub fn take_state(self) -> &$lifetime_a
> $state<$meta::Driver> {
> +            self.state
> +        }
> +    };
> +}
> +
> +pub(crate) use impl_atomic_state_token_ops;
> +
> +/// A token proving that no modesets for a commit have completed.
> +///
> +/// This token is proof that no commits have yet completed, and is
> provided as an argument to
> +/// [`KmsDriver::atomic_commit_tail`]. This may be used with
> +/// [`AtomicCommitTail::commit_modeset_disables`].
> +pub struct ModesetsReadyToken<'a>(PhantomData<&'a ()>);
> +
> +/// A token proving that modeset disables for a commit have
> completed.
> +///
> +/// This token is proof that an implementor's
> [`KmsDriver::atomic_commit_tail`] phase has finished
> +/// committing any operations which disable mode objects. It is
> returned by
> +/// [`AtomicCommitTail::commit_modeset_disables`], and can be used
> with
> +/// [`AtomicCommitTail::commit_modeset_enables`] to acquire a
> [`EnablesCommittedToken`].
> +pub struct DisablesCommittedToken<'a>(PhantomData<&'a ()>);
> +
> +/// A token proving that modeset enables for a commit have
> completed.
> +///
> +/// This token is proof that an implementor's
> [`KmsDriver::atomic_commit_tail`] phase has finished
> +/// committing any operations which enable mode objects. It is
> returned by
> +/// [`AtomicCommitTail::commit_modeset_enables`].
> +pub struct EnablesCommittedToken<'a>(PhantomData<&'a ()>);
> +
> +/// A token proving that no plane updates for a commit have
> completed.
> +///
> +/// This token is proof that no plane updates have yet been
> completed within an implementor's
> +/// [`KmsDriver::atomic_commit_tail`] implementation, and that we
> are ready to begin updating planes. It
> +/// is provided as an argument to [`KmsDriver::atomic_commit_tail`].
> +pub struct PlaneUpdatesReadyToken<'a>(PhantomData<&'a ()>);
> +
> +/// A token proving that all plane updates for a commit have
> completed.
> +///
> +/// This token is proof that all plane updates within an
> implementor's [`KmsDriver::atomic_commit_tail`]
> +/// implementation have completed. It is returned by
> [`AtomicCommitTail::commit_planes`].
> +pub struct PlaneUpdatesCommittedToken<'a>(PhantomData<&'a ()>);
> +
> +/// An [`AtomicState`] interface that allows a driver to control the
> [`atomic_commit_tail`]
> +/// callback.
> +///
> +/// This object is provided as an argument to
> [`KmsDriver::atomic_commit_tail`], and represents an atomic
> +/// state within the commit tail phase which is still in the process
> of being committed to hardware.
> +/// It may be used to control the order in which the commit process
> happens.
> +///
> +/// # Invariants
> +///
> +/// Same as [`AtomicState`].
> +///
> +/// [`atomic_commit_tail`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +pub struct AtomicCommitTail<'a, T: KmsDriver>(&'a AtomicState<T>);
> +
> +impl<'a, T: KmsDriver> AtomicCommitTail<'a, T> {
> +    /// Commit modesets which would disable outputs.
> +    ///
> +    /// This function commits any modesets which would shut down
> outputs, along with preparing them
> +    /// for a new mode (if needed).
> +    ///
> +    /// Since it is physically impossible to disable an output
> multiple times, and since it is
> +    /// logically unsound to disable an output within an atomic
> commit after the output was enabled
> +    /// in the same commit - this function requires a
> [`ModesetsReadyToken`] to consume and returns
> +    /// a [`DisablesCommittedToken`].
> +    ///
> +    /// If compatibility with legacy CRTC helpers is desired, this
> +    /// should be called before [`commit_planes`] which is what the
> default commit function does.
> +    /// But drivers with different needs can group the modeset
> commits tgether and do the plane
> +    /// commits at the end. This is useful for drivers doing runtime
> PM since then plane updates
> +    /// only happen when the CRTC is actually enabled.
> +    ///
> +    /// [`commit_planes`]: AtomicCommitTail::commit_planes
> +    #[inline]
> +    #[must_use]
> +    pub fn commit_modeset_disables<'b>(
> +        &mut self,
> +        _token: ModesetsReadyToken<'_>,
> +    ) -> DisablesCommittedToken<'b> {
> +        // SAFETY: Both `as_raw()` calls are guaranteed to return
> valid pointers
> +        unsafe {
> +            bindings::drm_atomic_helper_commit_modeset_disables(
> +                self.0.drm_dev().as_raw(),
> +                self.0.as_raw(),
> +            )
> +        }
> +
> +        DisablesCommittedToken(PhantomData)
> +    }
> +
> +    /// Commit all plane updates.
> +    ///
> +    /// This function performs all plane updates for the given
> [`AtomicCommitTail`]. Since it is
> +    /// logically unsound to perform the same plane update more then
> once in a given atomic commit,
> +    /// this function requires a [`PlaneUpdatesReadyToken`] to
> consume and returns a
> +    /// [`PlaneUpdatesCommittedToken`] to prove that plane updates
> for the state have completed.
> +    #[inline]
> +    #[must_use]
> +    pub fn commit_planes<'b>(
> +        &mut self,
> +        _token: PlaneUpdatesReadyToken<'_>,
> +        flags: PlaneCommitFlags,
> +    ) -> PlaneUpdatesCommittedToken<'b> {
> +        // SAFETY: Both `as_raw()` calls are guaranteed to return
> valid pointers
> +        unsafe {
> +            bindings::drm_atomic_helper_commit_planes(
> +                self.0.drm_dev().as_raw(),
> +                self.0.as_raw(),
> +                flags.into(),
> +            )
> +        }
> +
> +        PlaneUpdatesCommittedToken(PhantomData)
> +    }
> +
> +    /// Commit modesets which would enable outputs.
> +    ///
> +    /// This function commits any modesets in the given
> [`AtomicCommitTail`] which would enable
> +    /// outputs, along with preparing them for their new modes (if
> needed).
> +    ///
> +    /// Since it is logically unsound to enable an output before any
> disabling modesets within the
> +    /// same atomic commit have been performed, and physically
> impossible to enable the same output
> +    /// multiple times - this function requires a
> [`DisablesCommittedToken`] to consume and returns
> +    /// a [`EnablesCommittedToken`] which may be used as proof that
> all modesets in the state have
> +    /// been completed.
> +    #[inline]
> +    #[must_use]
> +    pub fn commit_modeset_enables<'b>(
> +        &mut self,
> +        _token: DisablesCommittedToken<'_>,
> +    ) -> EnablesCommittedToken<'b> {
> +        // SAFETY: Both `as_raw()` calls are guaranteed to return
> valid pointers
> +        unsafe {
> +            bindings::drm_atomic_helper_commit_modeset_enables(
> +                self.0.drm_dev().as_raw(),
> +                self.0.as_raw(),
> +            )
> +        }
> +
> +        EnablesCommittedToken(PhantomData)
> +    }
> +
> +    /// Fake vblank events if needed.
> +    ///
> +    /// Note that this is still relevant to drivers which don't
> implement [`VblankSupport`] for any
> +    /// of their CRTCs.
> +    ///
> +    /// TODO: more doc
> +    ///
> +    /// [`VblankSupport`]: super::vblank::VblankSupport
> +    pub fn fake_vblank(&mut self) {
> +        // SAFETY: `as_raw()` is guaranteed to always return a valid
> pointer
> +        unsafe {
> bindings::drm_atomic_helper_fake_vblank(self.0.as_raw()) }
> +    }
> +
> +    /// Signal completion of the hardware commit step.
> +    ///
> +    /// This swaps the atomic state into the relevant atomic state
> pointers and marks the hardware
> +    /// commit step as completed. Since this step can only happen
> after all plane updates and
> +    /// modesets within an [`AtomicCommitTail`] have been completed,
> it requires both a
> +    /// [`EnablesCommittedToken`] and a
> [`PlaneUpdatesCommittedToken`] to consume. After this
> +    /// function is called, the caller no longer has exclusive
> access to the underlying atomic
> +    /// state. As such, this function consumes the
> [`AtomicCommitTail`] object and returns a
> +    /// [`CommittedAtomicState`] accessor for performing post-hw
> commit tasks.
> +    pub fn commit_hw_done<'b>(
> +        self,
> +        _modeset_token: EnablesCommittedToken<'_>,
> +        _plane_updates_token: PlaneUpdatesCommittedToken<'_>,
> +    ) -> CommittedAtomicState<'b, T>
> +    where
> +        'a: 'b,
> +    {
> +        // SAFETY: we consume the `AtomicCommitTail` object, making
> it impossible for the user to
> +        // mutate the state after this function has been called -
> which upholds the safety
> +        // requirements of the C API allowing us to safely call this
> function
> +        unsafe {
> bindings::drm_atomic_helper_commit_hw_done(self.0.as_raw()) };
> +
> +        CommittedAtomicState(self.0)
> +    }
> +}
> +
> +// The actual raw C callback for custom atomic commit tail
> implementations
> +pub(crate) unsafe extern "C" fn commit_tail_callback<T: KmsDriver>(
> +    state: *mut bindings::drm_atomic_state,
> +) {
> +    // SAFETY:
> +    // - We're guaranteed by DRM that `state` always points to a
> valid instance of
> +    //   `bindings::drm_atomic_state`
> +    // - This conversion is safe via the type invariants
> +    let state = unsafe { AtomicState::from_raw(state.cast_const())
> };
> +
> +    T::atomic_commit_tail(
> +        AtomicCommitTail(state),
> +        ModesetsReadyToken(PhantomData),
> +        PlaneUpdatesReadyToken(PhantomData),
> +    );
> +}
> +
> +/// An [`AtomicState`] which was just committed with
> [`AtomicCommitTail::commit_hw_done`].
> +///
> +/// This object represents an [`AtomicState`] which has been fully
> committed to hardware, and as
> +/// such may no longer be mutated as it is visible to userspace. It
> may be used to control what
> +/// happens immediately after an atomic commit finishes within the
> [`atomic_commit_tail`] callback.
> +///
> +/// Since acquiring this object means that all modesetting locks
> have been dropped, a non-blocking
> +/// commit could happen at the same time an [`atomic_commit_tail`]
> implementer has access to this
> +/// object. Thus, it cannot be assumed that this object represents
> the current hardware state - and
> +/// instead only represents the final result of the
> [`AtomicCommitTail`] that was just committed.
> +///
> +/// # Invariants
> +///
> +/// It may be assumed that [`drm_atomic_helper_commit_hw_done`] has
> been called as long as this type
> +/// exists.
> +///
> +/// [`atomic_commit_tail`]: KmsDriver::atomic_commit_tail
> +/// [`drm_atomic_helper_commit_hw_done`]:
> srctree/include/drm/drm_atomic_helper.h
> +pub struct CommittedAtomicState<'a, T: KmsDriver>(&'a
> AtomicState<T>);
> +
> +impl<'a, T: KmsDriver> CommittedAtomicState<'a, T> {
> +    /// Wait for page flips on this state to complete
> +    pub fn wait_for_flip_done(&self) {
> +        // SAFETY: `drm_atomic_helper_commit_hw_done` has been
> called via our invariants
> +        unsafe {
> +            bindings::drm_atomic_helper_wait_for_flip_done(
> +                self.0.drm_dev().as_raw(),
> +                self.0.as_raw(),
> +            )
> +        }
> +    }
> +}
> +
> +impl<'a, T: KmsDriver> Drop for CommittedAtomicState<'a, T> {
> +    fn drop(&mut self) {
> +        // SAFETY:
> +        // * This interface represents the last atomic state
> accessor which could be affected as a
> +        //   result of resources from an atomic commit being cleaned
> up.
> +        unsafe {
> +           
> bindings::drm_atomic_helper_cleanup_planes(self.0.drm_dev().as_raw(),
> self.0.as_raw())
> +        }
> +    }
> +}
> +
> +/// An enumator representing a single flag in [`PlaneCommitFlags`].
> +///
> +/// This is a non-exhaustive list, as the C side could add more
> later.
> +#[derive(Copy, Clone, PartialEq, Eq)]
> +#[repr(u32)]
> +#[non_exhaustive]
> +pub enum PlaneCommitFlag {
> +    /// Don't notify applications of plane updates for newly-
> disabled planes. Drivers are encouraged
> +    /// to set this flag by default, as otherwise they need to
> ignore plane updates for disabled
> +    /// planes by hand.
> +    ActiveOnly = (1 << 0),
> +    /// Tell the DRM core that the display hardware requires that a
> [`Crtc`]'s planes must be
> +    /// disabled when the [`Crtc`] is disabled. When not specified,
> +    /// [`AtomicCommitTail::commit_planes`] will skip the atomic
> disable callbacks for a plane if
> +    /// the [`Crtc`] in the old [`PlaneState`] needs a modesetting
> operation. It is still up to the
> +    /// driver to disable said planes in their
> [`DriverCrtc::atomic_disable`] callback.
> +    NoDisableAfterModeset = (1 << 1),
> +}
> +
> +impl BitOr for PlaneCommitFlag {
> +    type Output = PlaneCommitFlags;
> +
> +    fn bitor(self, rhs: Self) -> Self::Output {
> +        PlaneCommitFlags(self as u32 | rhs as u32)
> +    }
> +}
> +
> +impl BitOr<PlaneCommitFlags> for PlaneCommitFlag {
> +    type Output = PlaneCommitFlags;
> +
> +    fn bitor(self, rhs: PlaneCommitFlags) -> Self::Output {
> +        PlaneCommitFlags(self as u32 | rhs.0)
> +    }
> +}
> +
> +/// A bitmask for controlling the behavior of
> [`AtomicCommitTail::commit_planes`].
> +///
> +/// This corresponds to the `DRM_PLANE_COMMIT_*` flags on the C
> side. Note that this bitmask does
> +/// not discard unknown values in order to ensure that adding new
> flags on the C side of things does
> +/// not break anything in the future.
> +#[derive(Copy, Clone, Default, PartialEq, Eq)]
> +pub struct PlaneCommitFlags(u32);
> +
> +impl From<PlaneCommitFlag> for PlaneCommitFlags {
> +    fn from(value: PlaneCommitFlag) -> Self {
> +        Self(value as u32)
> +    }
> +}
> +
> +impl From<PlaneCommitFlags> for u32 {
> +    fn from(value: PlaneCommitFlags) -> Self {
> +        value.0
> +    }
> +}
> +
> +impl BitOr for PlaneCommitFlags {
> +    type Output = Self;
> +
> +    fn bitor(self, rhs: Self) -> Self::Output {
> +        Self(self.0 | rhs.0)
> +    }
> +}
> +
> +impl BitOrAssign for PlaneCommitFlags {
> +    fn bitor_assign(&mut self, rhs: Self) {
> +        *self = *self | rhs
> +    }
> +}
> +
> +impl BitAnd for PlaneCommitFlags {
> +    type Output = PlaneCommitFlags;
> +
> +    fn bitand(self, rhs: Self) -> Self::Output {
> +        Self(self.0 & rhs.0)
> +    }
> +}
> +
> +impl BitAndAssign for PlaneCommitFlags {
> +    fn bitand_assign(&mut self, rhs: Self) {
> +        *self = *self & rhs
> +    }
> +}
> +
> +impl BitOr<PlaneCommitFlag> for PlaneCommitFlags {
> +    type Output = Self;
> +
> +    fn bitor(self, rhs: PlaneCommitFlag) -> Self::Output {
> +        self | Self::from(rhs)
> +    }
> +}
> +
> +impl BitOrAssign<PlaneCommitFlag> for PlaneCommitFlags {
> +    fn bitor_assign(&mut self, rhs: PlaneCommitFlag) {
> +        *self = *self | rhs
> +    }
> +}
> +
> +impl BitAnd<PlaneCommitFlag> for PlaneCommitFlags {
> +    type Output = PlaneCommitFlags;
> +
> +    fn bitand(self, rhs: PlaneCommitFlag) -> Self::Output {
> +        self & Self::from(rhs)
> +    }
> +}
> +
> +impl BitAndAssign<PlaneCommitFlag> for PlaneCommitFlags {
> +    fn bitand_assign(&mut self, rhs: PlaneCommitFlag) {
> +        *self = *self & rhs
> +    }
> +}
> +
> +impl PlaneCommitFlags {
> +    /// Create a new bitmask.
> +    pub fn new() -> Self {
> +        Self::default()
> +    }
> +
> +    /// Check if the bitmask has the given commit flag set.
> +    pub fn has(&self, flag: PlaneCommitFlag) -> bool {
> +        *self & flag == flag.into()
> +    }
> +}
> diff --git a/rust/kernel/drm/kms/connector.rs
> b/rust/kernel/drm/kms/connector.rs
> new file mode 100644
> index 000000000000..05ec64cf6fa2
> --- /dev/null
> +++ b/rust/kernel/drm/kms/connector.rs
> @@ -0,0 +1,997 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! DRM display connectors.
> +//!
> +//! C header:
> [`include/drm/drm_connector.h`](srctree/include/drm/drm_connector.h)
> +
> +use super::{
> +    atomic::*, encoder::*, KmsDriver, ModeConfigGuard, ModeObject,
> ModeObjectVtable, Sealed
> +};
> +use crate::{
> +    alloc::KBox,
> +    bindings,
> +    drm::{device::Device, kms::{NewKmsDevice, Probing}},
> +    error::to_result,
> +    prelude::*,
> +    types::{NotThreadSafe, Opaque},
> +};
> +use core::{
> +    cell::Cell,
> +    marker::*,
> +    mem::{self, ManuallyDrop},
> +    ops::*,
> +    ptr::{null_mut, NonNull},
> +    stringify,
> +};
> +use macros::paste;
> +
> +/// A macro for generating our type ID enumerator.
> +macro_rules! declare_conn_types {
> +    ($( $oldname:ident as $newname:ident ),+) => {
> +        /// An enumerator for all possible [`Connector`] type IDs.
> +        #[repr(i32)]
> +        #[non_exhaustive]
> +        #[derive(Copy, Clone, PartialEq, Eq)]
> +        pub enum Type {
> +            // Note: bindgen defaults the macro values to u32 and
> not i32, but DRM takes them as an
> +            // i32 - so just do the conversion here
> +            $(
> +                #[doc = concat!("The connector type ID for a ",
> stringify!($newname), " connector.")]
> +                $newname =
> paste!(crate::bindings::[<DRM_MODE_CONNECTOR_ $oldname>]) as i32
> +            ),+,
> +
> +            // 9PinDIN is special because of the 9, making it an
> invalid ident. Just define it here
> +            // manually since it's the only one
> +
> +            /// The connector type ID for a 9PinDIN connector.
> +            _9PinDin = crate::bindings::DRM_MODE_CONNECTOR_9PinDIN
> as i32
> +        }
> +    };
> +}
> +
> +declare_conn_types! {
> +    Unknown     as Unknown,
> +    Composite   as Composite,
> +    Component   as Component,
> +    DisplayPort as DisplayPort,
> +    VGA         as Vga,
> +    DVII        as DviI,
> +    DVID        as DviD,
> +    DVIA        as DviA,
> +    SVIDEO      as SVideo,
> +    LVDS        as Lvds,
> +    HDMIA       as HdmiA,
> +    HDMIB       as HdmiB,
> +    TV          as Tv,
> +    eDP         as Edp,
> +    VIRTUAL     as Virtual,
> +    DSI         as Dsi,
> +    DPI         as Dpi,
> +    WRITEBACK   as Writeback,
> +    SPI         as Spi,
> +    USB         as Usb
> +}
> +
> +/// The main trait for implementing the [`struct drm_connector`] API
> for [`Connector`].
> +///
> +/// Any KMS driver should have at least one implementation of this
> type, which allows them to create
> +/// [`Connector`] objects. Additionally, a driver may store driver-
> private data within the type that
> +/// implements [`DriverConnector`] - and it will be made available
> when using a fully typed
> +/// [`Connector`] object.
> +///
> +/// # Invariants
> +///
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_connector`] pointers are contained within a
> [`Connector<Self>`].
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_connector_state`] pointers are contained within a
> +///   [`ConnectorState<Self::State>`].
> +///
> +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h
> +/// [`struct drm_connector_state`]:
> srctree/include/drm/drm_connector.h
> +#[vtable]
> +pub trait DriverConnector: Send + Sync + Sized {
> +    /// The generated C vtable for this [`DriverConnector`]
> implementation
> +    const OPS: &'static DriverConnectorOps = &DriverConnectorOps {
> +        funcs: bindings::drm_connector_funcs {
> +            dpms: None,
> +            atomic_get_property: None,
> +            atomic_set_property: None,
> +            early_unregister: None,
> +            late_register: None,
> +            set_property: None,
> +            reset: Some(connector_reset_callback::<Self::State>),
> +            atomic_print_state: None,
> +            atomic_destroy_state:
> Some(atomic_destroy_state_callback::<Self::State>),
> +            destroy: Some(connector_destroy_callback::<Self>),
> +            force: None,
> +            detect: None,
> +            fill_modes:
> Some(bindings::drm_helper_probe_single_connector_modes),
> +            debugfs_init: None,
> +            oob_hotplug_event: None,
> +            atomic_duplicate_state:
> Some(atomic_duplicate_state_callback::<Self::State>),
> +        },
> +        helper_funcs: bindings::drm_connector_helper_funcs {
> +            mode_valid: None,
> +            atomic_check: None,
> +            get_modes: Some(get_modes_callback::<Self>),
> +            detect_ctx: None,
> +            enable_hpd: None,
> +            disable_hpd: None,
> +            best_encoder: None,
> +            atomic_commit: None,
> +            mode_valid_ctx: None,
> +            atomic_best_encoder: None,
> +            prepare_writeback_job: None,
> +            cleanup_writeback_job: None,
> +        },
> +    };
> +
> +    /// The type to pass to the `args` field of
> [`UnregisteredConnector::new`].
> +    ///
> +    /// This type will be made available in in the `args` argument
> of [`Self::new`]. Drivers which
> +    /// don't need this can simply pass [`()`] here.
> +    type Args;
> +
> +    /// The parent [`KmsDriver`] implementation.
> +    type Driver: KmsDriver;
> +
> +    /// The [`DriverConnectorState`] implementation for this
> [`DriverConnector`].
> +    ///
> +    /// See [`DriverConnectorState`] for more info.
> +    type State: DriverConnectorState;
> +
> +    /// The constructor for creating a [`Connector`] using this
> [`DriverConnector`] implementation.
> +    ///
> +    /// Drivers may use this to instantiate their
> [`DriverConnector`] object.
> +    fn new(device: &Device<Self::Driver>, args: Self::Args) -> impl
> PinInit<Self, Error>;
> +
> +    /// Retrieve a list of available display modes for this
> [`Connector`].
> +    fn get_modes<'a>(
> +        connector: ConnectorGuard<'a, Self>,
> +        guard: &ModeConfigGuard<'a, Self::Driver>,
> +    ) -> i32;
> +}
> +
> +/// The generated C vtable for a [`DriverConnector`].
> +///
> +/// This type is created internally by DRM.
> +pub struct DriverConnectorOps {
> +    funcs: bindings::drm_connector_funcs,
> +    helper_funcs: bindings::drm_connector_helper_funcs,
> +}
> +
> +/// The main interface for a [`struct drm_connector`].
> +///
> +/// This type is the main interface for dealing with DRM connectors.
> In addition, it also allows
> +/// immutable access to whatever private data is contained within an
> implementor's
> +/// [`DriverConnector`] type.
> +///
> +/// # Invariants
> +///
> +/// - The DRM C API and our interface guarantees that only the user
> has mutable access to `state`,
> +///   up until [`drm_atomic_helper_commit_hw_done`] is called.
> Therefore, `connector` follows rust's
> +///   data aliasing rules and does not need to be behind an
> [`Opaque`] type.
> +/// - `connector` and `inner` are initialized for as long as this
> object is made available to users.
> +/// - The data layout of this structure begins with [`struct
> drm_connector`].
> +/// - The atomic state for this type can always be assumed to be of
> type
> +///   [`ConnectorState<T::State>`].
> +///
> +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h
> +/// [`drm_atomic_helper_commit_hw_done`]:
> srctree/include/drm/drm_atomic_helper.h
> +#[repr(C)]
> +#[pin_data]
> +pub struct Connector<T: DriverConnector> {
> +    connector: Opaque<bindings::drm_connector>,
> +    #[pin]
> +    inner: T,
> +    #[pin]
> +    _p: PhantomPinned,
> +}
> +
> +impl<T: DriverConnector> Sealed for Connector<T> {}
> +
> +impl<T: DriverConnector> Deref for Connector<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.inner
> +    }
> +}
> +
> +impl<T: DriverConnector> Connector<T> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <'a, D>(&'a OpaqueConnector<D>) -> &'a Self;
> +        use
> +            T as DriverConnector,
> +            D as KmsDriver<Connector = ...>
> +    }
> +
> +    /// Acquire a [`ConnectorGuard`] for this connector from a
> [`ModeConfigGuard`].
> +    ///
> +    /// This verifies using the provided reference that the given
> guard is actually for the same
> +    /// device as this connector's parent.
> +    ///
> +    /// # Panics
> +    ///
> +    /// Panics if `guard` is not a [`ModeConfigGuard`] for this
> connector's parent [`Device`].
> +    pub fn guard<'a>(&'a self, guard: &ModeConfigGuard<'a,
> T::Driver>) -> ConnectorGuard<'a, T> {
> +        guard.assert_owner(self.drm_dev());
> +        ConnectorGuard(self)
> +    }
> +}
> +
> +/// A trait implemented by any type that acts as a [`struct
> drm_connector`] interface.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// # Safety
> +///
> +/// [`as_raw()`] must always return a pointer to a valid initialized
> [`struct drm_connector`].
> +///
> +/// [`as_raw()`]: AsRawConnector::as_raw()
> +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h
> +pub unsafe trait AsRawConnector {
> +    /// Return the raw [`struct drm_connector`] for this DRM
> connector.
> +    ///
> +    /// Drivers should never use this directly
> +    ///
> +    /// [`struct drm_Connector`]:
> srctree/include/drm/drm_connector.h
> +    fn as_raw(&self) -> *mut bindings::drm_connector;
> +
> +    /// Convert a raw `bindings::drm_connector` pointer into an
> object of this type.
> +    ///
> +    /// # Safety
> +    ///
> +    /// Callers promise that `ptr` points to a valid instance of
> this type.
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a
> Self;
> +}
> +
> +/// A supertrait of [`AsRawConnector`] for [`struct drm_connector`]
> interfaces that can perform
> +/// modesets.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// # Safety
> +///
> +/// Any object implementing this trait must only be made directly
> available to the user after
> +/// [`create_objects`] has completed.
> +///
> +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h
> +/// [`create_objects`]: KmsDriver::create_objects
> +pub unsafe trait ModesettableConnector: AsRawConnector {
> +    /// The type that should be returned for a plane state acquired
> using this plane interface
> +    type State: FromRawConnectorState;
> +}
> +
> +// SAFETY: Our connector interfaces are guaranteed to be thread-safe
> +unsafe impl<T: DriverConnector> Send for Connector<T> {}
> +
> +// SAFETY: Our connector interfaces are guaranteed to be thread-safe
> +unsafe impl<T: DriverConnector> Sync for Connector<T> {}
> +
> +// SAFETY: We don't expose Connector<T> to users before `base` is
> initialized in ::new(), so
> +// `raw_mode_obj` always returns a valid pointer to a
> bindings::drm_mode_object.
> +unsafe impl<T: DriverConnector> ModeObject for Connector<T> {
> +    type Driver = T::Driver;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: The parent device for a DRM connector will never
> outlive the connector, and this
> +        // pointer is invariant through the lifetime of the
> connector
> +        unsafe { Device::from_raw((*self.as_raw()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose DRM connectors to users before
> `base` is initialized
> +        unsafe { &raw mut (*self.as_raw()).base }
> +    }
> +}
> +
> +// Connectors are refcounted objects.
> +super::impl_aref_for_mode_object! {
> +    impl<T: DriverConnector> for Connector<T>
> +}
> +
> +// SAFETY: `funcs` is initialized by DRM when the connector is
> allocated
> +unsafe impl<T: DriverConnector> ModeObjectVtable for Connector<T> {
> +    type Vtable = bindings::drm_connector_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        // SAFETY: `funcs` is initialized by DRM when the connector
> is allocated
> +        unsafe { *self.as_raw() }.funcs
> +    }
> +}
> +
> +// SAFETY:
> +// * Via our type variants our data layout starts with
> `drm_connector`
> +// * Since we don't expose `Connector` to users before it has been
> initialized, this and our data
> +//   layout ensure that `as_raw()` always returns a valid pointer to
> a `drm_connector`.
> +unsafe impl<T: DriverConnector> AsRawConnector for Connector<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_connector {
> +        self.connector.get()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a
> Self {
> +        // SAFETY: Our data layout starts with
> `bindings::drm_connector`
> +        unsafe { &*ptr.cast() }
> +    }
> +}
> +
> +// SAFETY: We only expose this object to users directly after
> KmsDriver::create_objects has been
> +// called.
> +unsafe impl<T: DriverConnector> ModesettableConnector for
> Connector<T> {
> +    type State = ConnectorState<T::State>;
> +}
> +
> +/// A [`Connector`] that has not yet been registered with userspace.
> +///
> +/// KMS registration is single-threaded, so this object is not
> thread-safe.
> +///
> +/// # Invariants
> +///
> +/// - This object can only exist before its respective KMS device
> has been registered.
> +/// - Otherwise, it inherits all invariants of [`Connector`] and has
> an identical data layout.
> +pub struct UnregisteredConnector<T: DriverConnector>(Connector<T>,
> NotThreadSafe);
> +
> +// SAFETY: We share the invariants of `Connector`
> +unsafe impl<T: DriverConnector> AsRawConnector for
> UnregisteredConnector<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_connector {
> +        self.0.as_raw()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a
> Self {
> +        // SAFETY: This is another from_raw() call, so this function
> shares the same safety contract
> +        let connector = unsafe { Connector::<T>::from_raw(ptr) };
> +
> +        // SAFETY: Our data layout is identical via our type
> invariants.
> +        unsafe { mem::transmute(connector) }
> +    }
> +}
> +
> +impl<T: DriverConnector> Deref for UnregisteredConnector<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.0.inner
> +    }
> +}
> +
> +impl<T: DriverConnector> UnregisteredConnector<T> {
> +    /// Construct a new [`UnregisteredConnector`].
> +    ///
> +    /// A driver may use this to create new
> [`UnregisteredConnector`] objects.
> +    ///
> +    /// [`KmsDriver::create_objects`]:
> kernel::drm::kms::KmsDriver::create_objects
> +    pub fn new<'a>(
> +        dev: &'a NewKmsDevice<'a, T::Driver, Probing>,
> +        type_: Type,
> +        args: T::Args,
> +    ) -> Result<&'a Self> {
> +        let new: Pin<KBox<Connector<T>>> = KBox::try_pin_init(
> +            try_pin_init!(Connector {
> +                connector: Opaque::new(bindings::drm_connector {
> +                    helper_private: &T::OPS.helper_funcs,
> +                    ..Default::default()
> +                }),
> +                inner <- T::new(dev, args),
> +                _p: PhantomPinned,
> +            }),
> +            GFP_KERNEL,
> +        )?;
> +
> +        // SAFETY:
> +        // - `dev` will hold a reference to the new connector, and
> thus outlives us.
> +        // - We just allocated `new` above
> +        // - `new` starts with `drm_connector` via its type
> invariants.
> +        to_result(unsafe {
> +            bindings::drm_connector_init(dev.as_raw(), new.as_raw(),
> &T::OPS.funcs, type_ as i32)
> +        })?;
> +
> +        // SAFETY: We don't move anything
> +        let this = unsafe { Pin::into_inner_unchecked(new) };
> +
> +        // We'll re-assemble the box in connector_destroy_callback()
> +        let this = KBox::into_raw(this);
> +
> +        // UnregisteredConnector has an equivalent data layout
> +        let this: *mut Self = this.cast();
> +
> +        // SAFETY: We just allocated the connector above, so this
> pointer must be valid
> +        Ok(unsafe { &*this })
> +    }
> +
> +    /// Attach an encoder to this [`Connector`].
> +    #[must_use]
> +    pub fn attach_encoder(&self, encoder: &impl AsRawEncoder) ->
> Result {
> +        // SAFETY:
> +        // - Both as_raw() calls are guaranteed to return a valid
> pointer
> +        // - We're guaranteed this connector is not registered via
> our type invariants, thus this
> +        //   function is safe to call
> +        to_result(unsafe {
> +            bindings::drm_connector_attach_encoder(self.as_raw(),
> encoder.as_raw())
> +        })
> +    }
> +}
> +
> +/// Common methods available on any type which implements
> [`AsRawConnector`].
> +///
> +/// This is implemented internally by DRM, and provides many of the
> basic methods for working with
> +/// connectors.
> +pub trait RawConnector: AsRawConnector {
> +    /// Return the index of this DRM connector
> +    #[inline]
> +    fn index(&self) -> u32 {
> +        // SAFETY: The index is initialized by the time we expose
> DRM connector objects to users,
> +        // and is invariant throughout the lifetime of the connector
> +        unsafe { (*self.as_raw()).index }
> +    }
> +
> +    /// Return the bitmask derived from this DRM connector's index
> +    #[inline]
> +    fn mask(&self) -> u32 {
> +        1 << self.index()
> +    }
> +}
> +impl<T: AsRawConnector> RawConnector for T {}
> +
> +unsafe extern "C" fn connector_destroy_callback<T: DriverConnector>(
> +    connector: *mut bindings::drm_connector,
> +) {
> +    // SAFETY: DRM guarantees that `connector` points to a valid
> initialized `drm_connector`.
> +    unsafe {
> +        bindings::drm_connector_unregister(connector);
> +        bindings::drm_connector_cleanup(connector);
> +    };
> +
> +    // SAFETY:
> +    // - We originally created the connector in a `Box`
> +    // - We are guaranteed to hold the last remaining reference to
> this connector
> +    // - This cast is safe via `DriverConnector`s type invariants.
> +    drop(unsafe { KBox::from_raw(connector as *mut Connector<T>) });
> +}
> +
> +unsafe extern "C" fn get_modes_callback<T: DriverConnector>(
> +    connector: *mut bindings::drm_connector,
> +) -> core::ffi::c_int {
> +    // SAFETY: This is safe via `DriverConnector`s type invariants.
> +    let connector = unsafe { Connector::<T>::from_raw(connector) };
> +
> +    // SAFETY: This FFI callback is only called while
> `mode_config.lock` is held
> +    // We use ManuallyDrop here to prevent the lock from being
> released after the callback
> +    // completes, as that should be handled by DRM.
> +    let guard = ManuallyDrop::new(unsafe {
> ModeConfigGuard::new(connector.drm_dev()) });
> +
> +    T::get_modes(connector.guard(&guard), &guard)
> +}
> +
> +/// A [`struct drm_connector`] without a known [`DriverConnector`]
> implementation.
> +///
> +/// This is mainly for situations where our bindings can't infer the
> [`DriverConnector`]
> +/// implementation for a [`struct drm_connector`] automatically. It
> is identical to [`Connector`],
> +/// except that it does not provide access to the driver's private
> data.
> +///
> +/// # Invariants
> +///
> +/// - `connector` is initialized for as long as this object is
> exposed to users.
> +/// - The data layout of this type is equivalent to [`struct
> drm_connector`].
> +///
> +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h
> +#[repr(transparent)]
> +pub struct OpaqueConnector<T: KmsDriver> {
> +    connector: Opaque<bindings::drm_connector>,
> +    _p: PhantomData<T>,
> +}
> +
> +impl<T: KmsDriver> Sealed for OpaqueConnector<T> {}
> +
> +// SAFETY:
> +// - Via our type variants our data layout starts is identical to
> `drm_connector`
> +// - Since we don't expose `OpaqueConnector` to users before it has
> been initialized, this and our
> +//   data layout ensure that `as_raw()` always returns a valid
> pointer to a `drm_connector`.
> +unsafe impl<T: KmsDriver> AsRawConnector for OpaqueConnector<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_connector {
> +        self.connector.get()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a
> Self {
> +        // SAFETY: Our data layout is identical to
> `bindings::drm_connector`
> +        unsafe { &*ptr.cast() }
> +    }
> +}
> +
> +// SAFETY: We only expose this object to users directly after
> KmsDriver::create_objects has been
> +// called.
> +unsafe impl<T: KmsDriver> ModesettableConnector for
> OpaqueConnector<T> {
> +    type State = OpaqueConnectorState<T>;
> +}
> +
> +// SAFETY: We don't expose OpaqueConnector<T> to users before `base`
> is initialized in
> +// Connector::new(), so `raw_mode_obj` always returns a valid
> pointer to a bindings::drm_mode_object.
> +unsafe impl<T: KmsDriver> ModeObject for OpaqueConnector<T> {
> +    type Driver = T;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: The parent device for a DRM connector will never
> outlive the connector, and this
> +        // pointer is invariant through the lifetime of the
> connector
> +        unsafe { Device::from_raw((*self.as_raw()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose DRM connectors to users before
> `base` is initialized
> +        unsafe { &mut (*self.as_raw()).base }
> +    }
> +}
> +
> +super::impl_aref_for_mode_object! {
> +    impl<T: KmsDriver> for OpaqueConnector<T>
> +}
> +
> +// SAFETY: `funcs` is initialized by DRM when the connector is
> allocated
> +unsafe impl<T: KmsDriver> ModeObjectVtable for OpaqueConnector<T> {
> +    type Vtable = bindings::drm_connector_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        // SAFETY: `funcs` is initialized by DRM when the connector
> is allocated
> +        unsafe { *self.as_raw() }.funcs
> +    }
> +}
> +
> +// SAFETY: Our connector interfaces are guaranteed to be thread-safe
> +unsafe impl<T: KmsDriver> Send for OpaqueConnector<T> {}
> +unsafe impl<T: KmsDriver> Sync for OpaqueConnector<T> {}
> +
> +/// A privileged [`Connector`] obtained while holding a
> [`ModeConfigGuard`].
> +///
> +/// This provides access to various methods for [`Connector`] that
> must happen under lock, such as
> +/// setting resolution preferences and adding display modes.
> +///
> +/// # Invariants
> +///
> +/// Shares the invariants of [`ModeConfigGuard`].
> +#[derive(Copy, Clone)]
> +pub struct ConnectorGuard<'a, T: DriverConnector>(&'a Connector<T>);
> +
> +impl<T: DriverConnector> Deref for ConnectorGuard<'_, T> {
> +    type Target = Connector<T>;
> +
> +    fn deref(&self) -> &Self::Target {
> +        self.0
> +    }
> +}
> +
> +impl<'a, T: DriverConnector> ConnectorGuard<'a, T> {
> +    /// Add modes for a [`ConnectorGuard`] without an EDID.
> +    ///
> +    /// Add the specified modes to the connector's mode list up to
> the given maximum resultion.
> +    /// Returns how many modes were added.
> +    pub fn add_modes_noedid(&self, (max_h, max_v): (u32, u32)) ->
> i32 {
> +        // SAFETY: We hold the locks required to call this via our
> type invariants.
> +        unsafe { bindings::drm_add_modes_noedid(self.as_raw(),
> max_h, max_v) }
> +    }
> +
> +    /// Set the preferred display mode for the underlying
> [`Connector`].
> +    pub fn set_preferred_mode(&self, (h_pref, w_pref): (u32, u32)) {
> +        // SAFETY: We hold the locks required to call this via our
> type invariants.
> +        unsafe { bindings::drm_set_preferred_mode(self.as_raw(),
> h_pref, w_pref) }
> +    }
> +}
> +
> +/// A trait implemented by any type which can produce a reference to
> a
> +/// [`struct drm_connector_state`].
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// [`struct drm_connector_state`]:
> srctree/include/drm/drm_connector.h
> +pub trait AsRawConnectorState: private::AsRawConnectorState {
> +    /// The type that represents this connector state's DRM
> connector.
> +    type Connector: AsRawConnector;
> +}
> +
> +pub(super) mod private {
> +    use super::*;
> +
> +    /// Trait for retrieving references to the base connector state
> contained within any connector
> +    /// state compatible type
> +    #[allow(unreachable_pub)]
> +    pub trait AsRawConnectorState {
> +        /// Return an immutable reference to the raw connector
> state.
> +        fn as_raw(&self) -> &bindings::drm_connector_state;
> +
> +        /// Get a mutable reference to the raw [`struct
> drm_connector_state`] contained within this
> +        /// type.
> +        ///
> +        ///
> +        /// # Safety
> +        ///
> +        /// The caller promises this mutable reference will not be
> used to modify any contents of
> +        /// [`struct drm_connector_state`] which DRM would consider
> to be static - like the
> +        /// backpointer to the DRM connector that owns this state.
> This also means the mutable
> +        /// reference should never be exposed outside of this crate.
> +        ///
> +        /// [`struct drm_connector_state`]:
> srctree/include/drm/drm_connector.h
> +        unsafe fn as_raw_mut(&mut self) -> &mut
> bindings::drm_connector_state;
> +    }
> +}
> +
> +pub(super) use private::AsRawConnectorState as
> AsRawConnectorStatePrivate;
> +
> +/// A trait implemented for any type which can be constructed
> directly from a
> +/// [`struct drm_connector_state`] pointer.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// [`struct drm_connector_state`]:
> srctree/include/drm/drm_connector.h
> +pub trait FromRawConnectorState: AsRawConnectorState {
> +    /// Get an immutable reference to this type from the given raw
> [`struct drm_connector_state`]
> +    /// pointer.
> +    ///
> +    /// # Safety
> +    ///
> +    /// - The caller guarantees `ptr` is contained within a valid
> instance of `Self`.
> +    /// - The caller guarantees that `ptr` cannot not be modified
> for the lifetime of `'a`.
> +    ///
> +    /// [`struct drm_connector_state`]:
> srctree/include/drm/drm_connector.h
> +    unsafe fn from_raw<'a>(ptr: *const
> bindings::drm_connector_state) -> &'a Self;
> +
> +    /// Get a mutable reference to this type from the given raw
> [`struct drm_connector_state`]
> +    /// pointer.
> +    ///
> +    /// # Safety
> +    ///
> +    /// - The caller guarantees that `ptr` is contained within a
> valid instance of `Self`.
> +    /// - The caller guarantees that `ptr` cannot have any other
> references taken out for the
> +    ///   lifetime of `'a`.
> +    ///
> +    /// [`struct drm_connector_state`]:
> srctree/include/drm/drm_connector.h
> +    unsafe fn from_raw_mut<'a>(ptr: *mut
> bindings::drm_connector_state) -> &'a mut Self;
> +}
> +
> +/// Common methods available on any type which implements
> [`AsRawConnectorState`].
> +///
> +/// This is implemented internally by DRM, and provides many of the
> basic methods for working with
> +/// the atomic state of [`Connector`]s.
> +pub trait RawConnectorState: AsRawConnectorState {
> +    /// Return the connector that this atomic state belongs to.
> +    fn connector(&self) -> &Self::Connector {
> +        // SAFETY: This is guaranteed safe by type invariance, and
> we're guaranteed by DRM that
> +        // `self.state.connector` points to a valid instance of a
> `Connector<T>`
> +        unsafe {
> Self::Connector::from_raw((*self.as_raw()).connector) }
> +    }
> +}
> +impl<T: AsRawConnectorState> RawConnectorState for T {}
> +
> +/// The main interface for a [`struct drm_connector_state`].
> +///
> +/// This type is the main interface for dealing with the atomic
> state of DRM connectors. In
> +/// addition, it allows access to whatever private data is contained
> within an implementor's
> +/// [`DriverConnectorState`] type.
> +///
> +/// # Invariants
> +///
> +/// - The DRM C API and our interface guarantees that only the user
> has mutable access to `state`,
> +///   up until [`drm_atomic_helper_commit_hw_done`] is called.
> Therefore, `connector` follows rust's
> +///   data aliasing rules and does not need to be behind an
> [`Opaque`] type.
> +/// - `state` and `inner` initialized for as long as this object is
> exposed to users.
> +/// - The data layout of this structure begins with [`struct
> drm_connector_state`].
> +/// - The connector for this atomic state can always be assumed to
> be of type
> +///   [`Connector<T::Connector>`].
> +///
> +/// [`struct drm_connector_state`]:
> srctree/include/drm/drm_connector.h
> +/// [`drm_atomic_helper_commit_hw_done`]:
> srctree/include/drm/drm_atomic_helper.h
> +#[derive(Default)]
> +#[repr(C)]
> +pub struct ConnectorState<T: DriverConnectorState> {
> +    state: bindings::drm_connector_state,
> +    inner: T,
> +}
> +
> +/// The main trait for implementing the [`struct
> drm_connector_state`] API for a [`Connector`].
> +///
> +/// A driver may store driver-private data within the implementor's
> type, which will be available
> +/// when using a full typed [`ConnectorState`] object.
> +///
> +/// # Invariants
> +///
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_connector`] pointers are contained within a
> [`Connector<Self::Connector>`].
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_connector_state`] pointers are contained within a
> [`ConnectorState<Self>`].
> +///
> +/// [`struct drm_connector`]: srctree/include/drm_connector.h
> +/// [`struct drm_connector_state`]: srctree/include/drm_connector.h
> +pub trait DriverConnectorState: Clone + Default + Sized {
> +    /// The parent [`DriverConnector`].
> +    type Connector: DriverConnector;
> +}
> +
> +impl<T: DriverConnectorState> Sealed for ConnectorState<T> {}
> +
> +impl<T: DriverConnectorState> AsRawConnectorState for
> ConnectorState<T> {
> +    type Connector = Connector<T::Connector>;
> +}
> +
> +impl<T: DriverConnectorState> private::AsRawConnectorState for
> ConnectorState<T> {
> +    fn as_raw(&self) -> &bindings::drm_connector_state {
> +        &self.state
> +    }
> +
> +    unsafe fn as_raw_mut(&mut self) -> &mut
> bindings::drm_connector_state {
> +        &mut self.state
> +    }
> +}
> +
> +impl<T: DriverConnectorState> FromRawConnectorState for
> ConnectorState<T> {
> +    unsafe fn from_raw<'a>(ptr: *const
> bindings::drm_connector_state) -> &'a Self {
> +        // Our data layout starts with
> `bindings::drm_connector_state`.
> +        let ptr: *const Self = ptr.cast();
> +
> +        // SAFETY:
> +        // - Our safety contract requires that `ptr` be contained
> within `Self`.
> +        // - Our safety contract requires the caller ensure that it
> is safe for us to take an
> +        //   immutable reference.
> +        unsafe { &*ptr }
> +    }
> +
> +    unsafe fn from_raw_mut<'a>(ptr: *mut
> bindings::drm_connector_state) -> &'a mut Self {
> +        // Our data layout starts with
> `bindings::drm_connector_state`.
> +        let ptr: *mut Self = ptr.cast();
> +
> +        // SAFETY:
> +        // - Our safety contract requires that `ptr` be contained
> within `Self`.
> +        // - Our safety contract requires the caller ensure it is
> safe for us to take a mutable
> +        //   reference.
> +        unsafe { &mut *ptr }
> +    }
> +}
> +
> +// SAFETY: `funcs` is initialized by DRM when the connector is
> allocated
> +unsafe impl<T: DriverConnectorState> ModeObjectVtable for
> ConnectorState<T> {
> +    type Vtable = bindings::drm_connector_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.connector().vtable()
> +    }
> +}
> +
> +impl<T: DriverConnectorState> ConnectorState<T> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <'a, D, C>(&'a OpaqueConnectorState<D>) -> &'a Self
> +        where
> +            T: DriverConnectorState<Connector = C>;
> +        use
> +            C as DriverConnector,
> +            D as KmsDriver<Connector = ...>
> +    }
> +}
> +
> +/// A [`struct drm_connector_state`] without a known
> [`DriverConnectorState`] implementation.
> +///
> +/// This is mainly for situations where our bindings can't infer the
> [`DriverConnectorState`]
> +/// implementation for a [`struct drm_connector_state`]
> automatically. It is identical to
> +/// [`Connector`], except that it does not provide access to the
> driver's private data.
> +///
> +/// # Invariants
> +///
> +/// - `state` is initialized for as long as this object is exposed
> to users.
> +/// - The data layout of this type is identical to [`struct
> drm_connector_state`].
> +/// - The DRM C API and our interface guarantees that only the user
> has mutable access to `state`,
> +///   up until [`drm_atomic_helper_commit_hw_done`] is called.
> Therefore, `connector` follows rust's
> +///   data aliasing rules and does not need to be behind an
> [`Opaque`] type.
> +///
> +/// [`struct drm_connector_state`]:
> srctree/include/drm/drm_connector.h
> +/// [`drm_atomic_helper_commit_hw_done`]:
> srctree/include/drm/drm_atomic_helper.h
> +#[repr(transparent)]
> +pub struct OpaqueConnectorState<T: KmsDriver> {
> +    state: bindings::drm_connector_state,
> +    _p: PhantomData<T>,
> +}
> +
> +impl<T: KmsDriver> AsRawConnectorState for OpaqueConnectorState<T> {
> +    type Connector = OpaqueConnector<T>;
> +}
> +
> +impl<T: KmsDriver> private::AsRawConnectorState for
> OpaqueConnectorState<T> {
> +    fn as_raw(&self) -> &bindings::drm_connector_state {
> +        &self.state
> +    }
> +
> +    unsafe fn as_raw_mut(&mut self) -> &mut
> bindings::drm_connector_state {
> +        &mut self.state
> +    }
> +}
> +
> +impl<T: KmsDriver> FromRawConnectorState for OpaqueConnectorState<T>
> {
> +    unsafe fn from_raw<'a>(ptr: *const
> bindings::drm_connector_state) -> &'a Self {
> +        // SAFETY: Our data layout is identical to
> `bindings::drm_connector_state`
> +        unsafe { &*ptr.cast() }
> +    }
> +
> +    unsafe fn from_raw_mut<'a>(ptr: *mut
> bindings::drm_connector_state) -> &'a mut Self {
> +        // SAFETY: Our data layout is identical to
> `bindings::drm_connector_state`
> +        unsafe { &mut *ptr.cast() }
> +    }
> +}
> +
> +// SAFETY: See OpaqueConnector's ModeObjectVtable implementation
> +unsafe impl<T: KmsDriver> ModeObjectVtable for
> OpaqueConnectorState<T> {
> +    type Vtable = bindings::drm_connector_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.connector().vtable()
> +    }
> +}
> +
> +/// An interface for mutating a [`Connector`]s atomic state.
> +///
> +/// This type is typically returned by an [`AtomicStateMutator`]
> within contexts where it is
> +/// possible to safely mutate a connector's state. In order to
> uphold rust's data-aliasing rules,
> +/// only [`ConnectorStateMutator`] may exist at a time.
> +pub struct ConnectorStateMutator<'a, T: FromRawConnectorState> {
> +    state: &'a mut T,
> +    mask: &'a Cell<u32>,
> +}
> +
> +impl<'a, T: FromRawConnectorState> ConnectorStateMutator<'a, T> {
> +    pub(super) fn new<D: KmsDriver>(
> +        mutator: &'a AtomicStateMutator<D>,
> +        state: NonNull<bindings::drm_connector_state>,
> +    ) -> Option<Self> {
> +        // SAFETY:
> +        // - `connector` is invariant throughout the lifetime of the
> atomic state.
> +        // - `state` is initialized by the time it is passed to this
> function.
> +        // - We're guaranteed that `state` is compatible with
> `drm_connector` by type invariants.
> +        let connector = unsafe {
> T::Connector::from_raw((*state.as_ptr()).connector) };
> +        let conn_mask = connector.mask();
> +        let borrowed_mask = mutator.borrowed_connectors.get();
> +
> +        if borrowed_mask & conn_mask == 0 {
> +            mutator.borrowed_connectors.set(borrowed_mask |
> conn_mask);
> +            Some(Self {
> +                mask: &mutator.borrowed_connectors,
> +                // SAFETY: We're guaranteed `state` is of `T` by
> type invariance, and we just
> +                // confirmed by checking `borrowed_connectors` that
> no other mutable borrows have
> +                // been taken out for `state`
> +                state: unsafe { T::from_raw_mut(state.as_ptr()) },
> +            })
> +        } else {
> +            None
> +        }
> +    }
> +}
> +
> +impl<'a, T: DriverConnectorState> Deref for
> ConnectorStateMutator<'a, ConnectorState<T>> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.state.inner
> +    }
> +}
> +
> +impl<'a, T: DriverConnectorState> DerefMut for
> ConnectorStateMutator<'a, ConnectorState<T>> {
> +    fn deref_mut(&mut self) -> &mut Self::Target {
> +        &mut self.state.inner
> +    }
> +}
> +
> +impl<'a, T: FromRawConnectorState> Drop for
> ConnectorStateMutator<'a, T> {
> +    fn drop(&mut self) {
> +        let mask = self.state.connector().mask();
> +        self.mask.set(self.mask.get() & !mask);
> +    }
> +}
> +
> +impl<'a, T: FromRawConnectorState> AsRawConnectorState for
> ConnectorStateMutator<'a, T> {
> +    type Connector = T::Connector;
> +}
> +
> +impl<'a, T: FromRawConnectorState> private::AsRawConnectorState for
> ConnectorStateMutator<'a, T> {
> +    fn as_raw(&self) -> &bindings::drm_connector_state {
> +        self.state.as_raw()
> +    }
> +
> +    unsafe fn as_raw_mut(&mut self) -> &mut
> bindings::drm_connector_state {
> +        // SAFETY: We're bound by the same safety contract as this
> function
> +        unsafe { self.state.as_raw_mut() }
> +    }
> +}
> +
> +// SAFETY: we inherit the safety guarantees of `T`
> +unsafe impl<'a, T> ModeObjectVtable for ConnectorStateMutator<'a, T>
> +where
> +    T: FromRawConnectorState + ModeObjectVtable,
> +{
> +    type Vtable = T::Vtable;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.state.vtable()
> +    }
> +}
> +
> +impl<'a, T: DriverConnectorState> ConnectorStateMutator<'a,
> ConnectorState<T>> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <D, C>(ConnectorStateMutator<'a,
> OpaqueConnectorState<D>>) -> Self
> +        where
> +            T: DriverConnectorState<Connector = C>;
> +        use
> +            C as DriverConnector,
> +            D as KmsDriver<Connector = ...>
> +    }
> +}
> +
> +unsafe extern "C" fn atomic_duplicate_state_callback<T:
> DriverConnectorState>(
> +    connector: *mut bindings::drm_connector,
> +) -> *mut bindings::drm_connector_state {
> +    // SAFETY: DRM guarantees that `connector` points to a valid
> initialized `drm_connector`.
> +    let state = unsafe { (*connector).state };
> +    if state.is_null() {
> +        return null_mut();
> +    }
> +
> +    // SAFETY:
> +    // - We just verified that `state` is non-null
> +    // - This cast is guaranteed to be safe via our type invariants.
> +    let state = unsafe { ConnectorState::<T>::from_raw(state) };
> +
> +    let new: Result<KBox<_>> = KBox::init(
> +        init!(ConnectorState::<T> {
> +            inner: state.inner.clone(),
> +            state: bindings::drm_connector_state {
> +                ..Default::default()
> +            },
> +        }),
> +        GFP_KERNEL,
> +    );
> +
> +    if let Ok(mut new) = new {
> +        // SAFETY:
> +        // - `new` provides a valid pointer to a newly allocated
> `drm_plane_state` via type
> +        //   invariants
> +        // - This initializes `new` via memcpy()
> +        unsafe {
> +           
> bindings::__drm_atomic_helper_connector_duplicate_state(connector,
> new.as_raw_mut())
> +        };
> +
> +        KBox::into_raw(new).cast()
> +    } else {
> +        null_mut()
> +    }
> +}
> +
> +unsafe extern "C" fn atomic_destroy_state_callback<T:
> DriverConnectorState>(
> +    _connector: *mut bindings::drm_connector,
> +    connector_state: *mut bindings::drm_connector_state,
> +) {
> +    // SAFETY: DRM guarantees that `state` points to a valid
> instance of `drm_connector_state`
> +    unsafe {
> bindings::__drm_atomic_helper_connector_destroy_state(connector_state
> ) };
> +
> +    // SAFETY:
> +    // - DRM guarantees we are the only one with access to this
> `drm_connector_state`
> +    // - This cast is safe via our type invariants.
> +    drop(unsafe {
> KBox::from_raw(connector_state.cast::<ConnectorState<T>>()) });
> +}
> +
> +unsafe extern "C" fn connector_reset_callback<T:
> DriverConnectorState>(
> +    connector: *mut bindings::drm_connector,
> +) {
> +    // SAFETY: DRM guarantees that `state` points to a valid
> instance of `drm_connector_state`
> +    let state = unsafe { (*connector).state };
> +    if !state.is_null() {
> +        // SAFETY:
> +        // - We're guaranteed `connector` is `Connector<T>` via type
> invariants
> +        // - We're guaranteed `state` is `ConnectorState<T>` via
> type invariants.
> +        unsafe { atomic_destroy_state_callback::<T>(connector,
> state) }
> +
> +        // SAFETY: No special requirements here, DRM expects this to
> be NULL
> +        unsafe { (*connector).state = null_mut() };
> +    }
> +
> +    // Unfortunately, this is the best we can do at the moment as
> this FFI callback was mistakenly
> +    // presumed to be infallible :(
> +    let new = KBox::new(ConnectorState::<T>::default(),
> GFP_KERNEL).expect("Blame the API, sorry!");
> +
> +    // DRM takes ownership of the state from here, resets it, and
> then assigns it to the connector
> +    // SAFETY:
> +    // - DRM guarantees that `connector` points to a valid instance
> of `drm_connector`.
> +    // - The cast to `drm_connector_state` is safe via
> `ConnectorState`s type invariants.
> +    unsafe {
> bindings::__drm_atomic_helper_connector_reset(connector,
> Box::into_raw(new).cast()) };
> +}
> diff --git a/rust/kernel/drm/kms/crtc.rs
> b/rust/kernel/drm/kms/crtc.rs
> new file mode 100644
> index 000000000000..b9d095854ba6
> --- /dev/null
> +++ b/rust/kernel/drm/kms/crtc.rs
> @@ -0,0 +1,1110 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! DRM CRTCs.
> +//!
> +//! C header:
> [`include/drm/drm_crtc.h`](srctree/include/drm/drm_crtc.h)
> +
> +use super::{
> +    atomic::*, plane::*, vblank::*, KmsDriver, ModeObject,
> ModeObjectVtable, StaticModeObject,
> +    NewKmsDevice, Probing, Sealed,
> +};
> +use crate::{
> +    alloc::KBox,
> +    bindings,
> +    drm::device::Device,
> +    error::{from_result, to_result},
> +    prelude::*,
> +    types::{NotThreadSafe, Opaque},
> +};
> +use core::{
> +    cell::{Cell, UnsafeCell},
> +    marker::*,
> +    mem::{self, ManuallyDrop},
> +    ops::{Deref, DerefMut},
> +    ptr::{null, null_mut, NonNull},
> +};
> +use macros::vtable;
> +
> +/// The main trait for implementing the [`struct drm_crtc`] API for
> [`Crtc`].
> +///
> +/// Any KMS driver should have at least one implementation of this
> type, which allows them to create
> +/// [`Crtc`] objects. Additionally, a driver may store driver-
> private data within the type that
> +/// implements [`DriverCrtc`] - and it will be made available when
> using a fully typed [`Crtc`]
> +/// object.
> +///
> +/// # Invariants
> +///
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_crtc`] pointers are contained within a
> [`Crtc<Self>`].
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_crtc_state`] pointers are contained within a
> [`CrtcState<Self::State>`].
> +///
> +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
> +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h
> +#[vtable]
> +pub trait DriverCrtc: Send + Sync + Sized {
> +    /// The generated C vtable for this [`DriverCrtc`]
> implementation.
> +    const OPS: &'static DriverCrtcOps = &DriverCrtcOps {
> +        funcs: bindings::drm_crtc_funcs {
> +            atomic_destroy_state:
> Some(atomic_destroy_state_callback::<Self::State>),
> +            atomic_duplicate_state:
> Some(atomic_duplicate_state_callback::<Self::State>),
> +            atomic_get_property: None,
> +            atomic_print_state: None,
> +            atomic_set_property: None,
> +            cursor_move: None,
> +            cursor_set2: None,
> +            cursor_set: None,
> +            destroy: Some(crtc_destroy_callback::<Self>),
> +            disable_vblank: <Self::VblankImpl as
> VblankImpl>::VBLANK_OPS.disable_vblank,
> +            early_unregister: None,
> +            enable_vblank: <Self::VblankImpl as
> VblankImpl>::VBLANK_OPS.enable_vblank,
> +            gamma_set: None,
> +            get_crc_sources: None,
> +            get_vblank_counter: None,
> +            get_vblank_timestamp: <Self::VblankImpl as
> VblankImpl>::VBLANK_OPS.get_vblank_timestamp,
> +            late_register: None,
> +            page_flip: Some(bindings::drm_atomic_helper_page_flip),
> +            page_flip_target: None,
> +            reset: Some(crtc_reset_callback::<Self::State>),
> +            set_config:
> Some(bindings::drm_atomic_helper_set_config),
> +            set_crc_source: None,
> +            set_property: None,
> +            verify_crc_source: None,
> +        },
> +
> +        helper_funcs: bindings::drm_crtc_helper_funcs {
> +            atomic_disable: if Self::HAS_ATOMIC_DISABLE {
> +                Some(atomic_disable_callback::<Self>)
> +            } else {
> +                None
> +            },
> +            atomic_enable: if Self::HAS_ATOMIC_ENABLE {
> +                Some(atomic_enable_callback::<Self>)
> +            } else {
> +                None
> +            },
> +            atomic_check: if Self::HAS_ATOMIC_CHECK {
> +                Some(atomic_check_callback::<Self>)
> +            } else {
> +                None
> +            },
> +            dpms: None,
> +            commit: None,
> +            prepare: None,
> +            disable: None,
> +            mode_set: None,
> +            mode_valid: None,
> +            mode_fixup: None,
> +            atomic_begin: if Self::HAS_ATOMIC_BEGIN {
> +                Some(atomic_begin_callback::<Self>)
> +            } else {
> +                None
> +            },
> +            atomic_flush: if Self::HAS_ATOMIC_FLUSH {
> +                Some(atomic_flush_callback::<Self>)
> +            } else {
> +                None
> +            },
> +            mode_set_nofb: None,
> +            mode_set_base: None,
> +            mode_set_base_atomic: None,
> +            get_scanout_position: None,
> +        },
> +    };
> +
> +    /// The type to pass to the `args` field of
> [`UnregisteredCrtc::new`].
> +    ///
> +    /// This type will be made available in in the `args` argument
> of [`Self::new`]. Drivers which
> +    /// don't need this can simply pass [`()`] here.
> +    type Args;
> +
> +    /// The parent [`KmsDriver`] implementation.
> +    type Driver: KmsDriver;
> +
> +    /// The [`DriverCrtcState`] implementation for this
> [`DriverCrtc`].
> +    ///
> +    /// See [`DriverCrtcState`] for more info.
> +    type State: DriverCrtcState;
> +
> +    /// The driver's optional hardware vblank implementation
> +    ///
> +    /// See [`VblankSupport`] for more info. Drivers that don't care
> about this can just pass
> +    /// [`PhantomData<Self>`].
> +    type VblankImpl: VblankImpl<Crtc = Self>;
> +
> +    /// The constructor for creating a [`Crtc`] using this
> [`DriverCrtc`] implementation.
> +    ///
> +    /// Drivers may use this to instantiate their [`DriverCrtc`]
> object.
> +    fn new(device: &Device<Self::Driver>, args: &Self::Args) -> impl
> PinInit<Self, Error>;
> +
> +    /// The optional [`drm_crtc_helper_funcs.atomic_check`] hook for
> this crtc.
> +    ///
> +    /// Drivers may use this to customize the atomic check phase of
> their [`Crtc`] objects. The
> +    /// result of this function determines whether the atomic check
> passed or failed.
> +    ///
> +    /// [`drm_crtc_helper_funcs.atomic_check`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +    fn atomic_check(_check: CrtcAtomicCheck<'_, Self>) -> Result {
> +        build_error::build_error("This should not be reachable")
> +    }
> +
> +    /// The optional [`drm_crtc_helper_funcs.atomic_begin`] hook.
> +    ///
> +    /// This hook will be called before a set of [`Plane`] updates
> are performed for the given
> +    /// [`Crtc`].
> +    ///
> +    /// [`drm_crtc_helper_funcs.atomic_begin`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +    fn atomic_begin(_commit: CrtcAtomicCommit<'_, Self>) {
> +        build_error::build_error("This should not be reachable")
> +    }
> +
> +    /// The optional [`drm_crtc_helper_funcs.atomic_flush`] hook.
> +    ///
> +    /// This hook will be called after a set of [`Plane`] updates
> are performed for the given
> +    /// [`Crtc`].
> +    ///
> +    /// [`drm_crtc_helper_funcs.atomic_flush`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +    fn atomic_flush(_commit: CrtcAtomicCommit<'_, Self>) {
> +        build_error::build_error("This should never be reachable")
> +    }
> +
> +    /// The optional [`drm_crtc_helper_funcs.atomic_enable`] hook.
> +    ///
> +    /// This hook will be called before enabling a [`Crtc`] in an
> atomic commit.
> +    ///
> +    /// [`drm_crtc_helper_funcs.atomic_enable`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +    fn atomic_enable(_commit: CrtcAtomicCommit<'_, Self>) {
> +        build_error::build_error("This should never be reachable")
> +    }
> +
> +    /// The optional [`drm_crtc_helper_funcs.atomic_disable`] hook.
> +    ///
> +    /// This hook will be called before disabling a [`Crtc`] in an
> atomic commit.
> +    ///
> +    /// [`drm_crtc_helper_funcs.atomic_disable`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +    fn atomic_disable(_commit: CrtcAtomicCommit<'_, Self>) {
> +        build_error::build_error("This should never be reachable")
> +    }
> +}
> +
> +/// The generated C vtable for a [`DriverCrtc`].
> +///
> +/// This type is created internally by DRM.
> +pub struct DriverCrtcOps {
> +    funcs: bindings::drm_crtc_funcs,
> +    helper_funcs: bindings::drm_crtc_helper_funcs,
> +}
> +
> +/// The main interface for a [`struct drm_crtc`].
> +///
> +/// This type is the main interface for dealing with DRM CRTCs. In
> addition, it also allows
> +/// immutable access to whatever private data is contained within an
> implementor's [`DriverCrtc`]
> +/// type.
> +///
> +/// # Invariants
> +///
> +/// - `crtc` and `inner` are initialized for as long as this object
> is made available to users.
> +/// - The data layout of this structure begins with [`struct
> drm_crtc`].
> +/// - The atomic state for this type can always be assumed to be of
> type [`CrtcState<T::State>`].
> +///
> +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
> +#[repr(C)]
> +#[pin_data]
> +pub struct Crtc<T: DriverCrtc> {
> +    // The FFI drm_crtc object
> +    crtc: Opaque<bindings::drm_crtc>,
> +    /// The driver's private inner data
> +    #[pin]
> +    inner: T,
> +    #[pin]
> +    _p: PhantomPinned,
> +}
> +
> +impl<T: DriverCrtc> Sealed for Crtc<T> {}
> +
> +// SAFETY: Our CRTC interfaces are guaranteed to be thread-safe
> +unsafe impl<T: DriverCrtc> Send for Crtc<T> {}
> +
> +// SAFETY: Our CRTC interfaces are guaranteed to be thread-safe
> +unsafe impl<T: DriverCrtc> Sync for Crtc<T> {}
> +
> +impl<T: DriverCrtc> Deref for Crtc<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.inner
> +    }
> +}
> +
> +// SAFETY: We don't expose Crtc<T> to users before `base` is
> initialized in ::new(), so
> +// `raw_mode_obj` always returns a valid pointer to a
> bindings::drm_mode_object.
> +unsafe impl<T: DriverCrtc> ModeObject for Crtc<T> {
> +    type Driver = T::Driver;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: DRM connectors exist for as long as the device
> does, so this pointer is always
> +        // valid
> +        unsafe { Device::from_raw((*self.as_raw()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose Crtc<T> to users before it's
> initialized, so `base` is always
> +        // initialized
> +        unsafe { &raw mut (*self.as_raw()).base }
> +    }
> +}
> +
> +// SAFETY: CRTCs are non-refcounted modesetting objects
> +unsafe impl<T: DriverCrtc> StaticModeObject for Crtc<T> {}
> +
> +// SAFETY: `funcs` is initialized when the crtc is allocated
> +unsafe impl<T: DriverCrtc> ModeObjectVtable for Crtc<T> {
> +    type Vtable = bindings::drm_crtc_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        // SAFETY: `as_raw()` always returns a valid pointer to a
> CRTC
> +        unsafe { *self.as_raw() }.funcs
> +    }
> +}
> +
> +impl<T: DriverCrtc> Crtc<T> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <'a, D>(&'a OpaqueCrtc<D>) -> &'a Self;
> +        use
> +            T as DriverCrtc,
> +            D as KmsDriver<Crtc = ...>
> +    }
> +
> +    pub(crate) fn get_vblank_ptr(&self) -> *mut
> bindings::drm_vblank_crtc {
> +        // SAFETY: FFI Call with no special requirements
> +        unsafe { bindings::drm_crtc_vblank_crtc(self.as_raw()) }
> +    }
> +
> +    pub(crate) const fn has_vblank() -> bool {
> +        T::OPS.funcs.enable_vblank.is_some()
> +    }
> +}
> +
> +/// A [`Crtc`] that has not yet been registered with userspace.
> +///
> +/// KMS registration is single-threaded, so this object is not
> thread-safe.
> +///
> +/// # Invariants
> +///
> +/// - This object can only exist before its respective KMS device
> has been registered.
> +/// - Otherwise, it inherits all invariants of [`Crtc`] and has an
> identical data layout.
> +pub struct UnregisteredCrtc<T: DriverCrtc>(Crtc<T>, NotThreadSafe);
> +
> +impl<T: DriverCrtc> UnregisteredCrtc<T> {
> +    /// Construct a new [`UnregisteredCrtc`].
> +    ///
> +    /// A driver may use this from their
> [`KmsDriver::create_objects`] callback in order to
> +    /// construct new [`UnregisteredCrtc`] objects.
> +    ///
> +    /// [`KmsDriver::create_objects`]:
> kernel::drm::kms::KmsDriver::create_objects
> +    pub fn new<'a, 'b: 'a, PrimaryData, CursorData>(
> +        dev: &'a NewKmsDevice<'a, T::Driver, Probing>,
> +        primary: &'a UnregisteredPlane<PrimaryData>,
> +        cursor: Option<&'a UnregisteredPlane<CursorData>>,
> +        name: Option<&CStr>,
> +        args: T::Args,
> +    ) -> Result<&'a Self>
> +    where
> +        PrimaryData: DriverPlane<Driver = T::Driver>,
> +        CursorData: DriverPlane<Driver = T::Driver>,
> +    {
> +        if Crtc::<T>::has_vblank() {
> +            dev.has_vblanks.set(true)
> +        }
> +
> +        let this: Pin<KBox<Crtc<T>>> = KBox::try_pin_init(
> +            try_pin_init!(Crtc {
> +                crtc: Opaque::new(bindings::drm_crtc {
> +                    helper_private: &T::OPS.helper_funcs,
> +                    ..Default::default()
> +                }),
> +                inner <- T::new(dev, &args),
> +                _p: PhantomPinned,
> +            }),
> +            GFP_KERNEL,
> +        )?;
> +
> +        // SAFETY:
> +        // - `dev` handles destroying the CRTC and thus will outlive
> us.
> +        // - We just allocated `this`, and we won't move it since
> it's pinned
> +        // - `primary` and `cursor` share the lifetime 'a with `dev`
> +        // - This function will memcpy the contents of `name` into
> its own storage.
> +        to_result(unsafe {
> +            bindings::drm_crtc_init_with_planes(
> +                dev.as_raw(),
> +                this.as_raw(),
> +                primary.as_raw(),
> +                cursor.map_or(null_mut(), |c| c.as_raw()),
> +                &T::OPS.funcs,
> +                name.map_or(null(), |n| n.as_char_ptr()),
> +            )
> +        })?;
> +
> +        // SAFETY: We don't move anything
> +        let this = unsafe { Pin::into_inner_unchecked(this) };
> +
> +        // We'll re-assemble the box in crtc_destroy_callback()
> +        let this = KBox::into_raw(this);
> +
> +        // UnregisteredCrtc has an equivalent data layout
> +        let this: *mut Self = this.cast();
> +
> +        // SAFETY: We just allocated the crtc above, so this pointer
> must be valid
> +        Ok(unsafe { &*this })
> +    }
> +}
> +
> +// SAFETY: We inherit all relevant invariants of `Crtc`
> +unsafe impl<T: DriverCrtc> AsRawCrtc for UnregisteredCrtc<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_crtc {
> +        self.0.as_raw()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) -> &'a Self
> {
> +        // SAFETY: This is another from_raw() call, so this function
> shares the same safety contract
> +        let crtc = unsafe { Crtc::<T>::from_raw(ptr) };
> +
> +        // SAFETY: Our data layout is identical via our type
> invariants.
> +        unsafe { mem::transmute(crtc) }
> +    }
> +}
> +
> +impl<T: DriverCrtc> Deref for UnregisteredCrtc<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.0.inner
> +    }
> +}
> +
> +/// A trait implemented by any type that acts as a [`struct
> drm_crtc`] interface.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// # Safety
> +///
> +/// [`as_raw()`] must always return a valid pointer to a [`struct
> drm_crtc`].
> +///
> +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
> +/// [`as_raw()`]: AsRawCrtc::as_raw()
> +pub unsafe trait AsRawCrtc {
> +    /// Return a raw pointer to the `bindings::drm_crtc` for this
> object
> +    fn as_raw(&self) -> *mut bindings::drm_crtc;
> +
> +    /// Convert a raw [`struct drm_crtc`] pointer into an object of
> this type.
> +    ///
> +    /// # Safety
> +    ///
> +    /// Callers promise that `ptr` points to a valid instance of
> this type
> +    ///
> +    /// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) -> &'a
> Self;
> +}
> +
> +// SAFETY:
> +// - Via our type variants our data layout starts with `drm_crtc`
> +// - Since we don't expose `crtc` to users before it has been
> initialized, this and our data
> +//   layout ensure that `as_raw()` always returns a valid pointer to
> a `drm_crtc`.
> +unsafe impl<T: DriverCrtc> AsRawCrtc for Crtc<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_crtc {
> +        self.crtc.get()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) -> &'a Self
> {
> +        // Our data layout start with `bindings::drm_crtc`.
> +        let ptr: *mut Self = ptr.cast();
> +
> +        // SAFETY: Our safety contract requires that `ptr` point to
> a valid intance of `Self`.
> +        unsafe { &*ptr }
> +    }
> +}
> +
> +// SAFETY: We only expose this object to users directly after
> KmsDriver::create_objects has been
> +// called.
> +unsafe impl<T: DriverCrtc> ModesettableCrtc for Crtc<T> {
> +    type State = CrtcState<T::State>;
> +}
> +
> +/// A supertrait of [`AsRawCrtc`] for [`struct drm_crtc`] interfaces
> that can perform modesets.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// # Safety
> +///
> +/// Any object implementing this trait must only be made directly
> available to the user after
> +/// [`create_objects`] has completed.
> +///
> +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
> +/// [`create_objects`]: KmsDriver::create_objects
> +pub unsafe trait ModesettableCrtc: AsRawCrtc {
> +    /// The type that should be returned for a CRTC state acquired
> using this CRTC interface
> +    type State: FromRawCrtcState;
> +}
> +
> +/// Common methods available on any type which implements
> [`AsRawCrtc`].
> +///
> +/// This is implemented internally by DRM, and provides many of the
> basic methods for working with
> +/// CRTCs.
> +pub trait RawCrtc: AsRawCrtc {
> +    /// Return the index of this CRTC.
> +    fn index(&self) -> u32 {
> +        // SAFETY: The index is initialized by the time we expose
> Crtc objects to users, and is
> +        // invariant throughout the lifetime of the Crtc
> +        unsafe { (*self.as_raw()).index }
> +    }
> +
> +    /// Return the index of this DRM CRTC in the form of a bitmask.
> +    fn mask(&self) -> u32 {
> +        1 << self.index()
> +    }
> +}
> +impl<T: AsRawCrtc> RawCrtc for T {}
> +
> +/// A [`struct drm_crtc`] without a known [`DriverCrtc`]
> implementation.
> +///
> +/// This is mainly for situations where our bindings can't infer the
> [`DriverCrtc`] implementation
> +/// for a [`struct drm_crtc`] automatically. It is identical to
> [`Crtc`], except that it does not
> +/// provide access to the driver's private data.
> +///
> +/// It may be upcasted to a full [`Crtc`] using
> [`Crtc::from_opaque`] or
> +/// [`Crtc::try_from_opaque`].
> +///
> +/// # Invariants
> +///
> +/// - `crtc` is initialized for as long as this object is made
> available to users.
> +/// - The data layout of this structure is equivalent to [`struct
> drm_crtc`].
> +///
> +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
> +#[repr(transparent)]
> +pub struct OpaqueCrtc<T: KmsDriver> {
> +    crtc: Opaque<bindings::drm_crtc>,
> +    _p: PhantomData<T>,
> +}
> +
> +impl<T: KmsDriver> Sealed for OpaqueCrtc<T> {}
> +
> +// SAFETY:
> +// - Via our type variants our data layout is identical to
> `drm_crtc`
> +// - Since we don't expose `OpaqueCrtc` to users before it has been
> initialized, this and our data
> +//   layout ensure that `as_raw()` always returns a valid pointer to
> a `drm_crtc`.
> +unsafe impl<T: KmsDriver> AsRawCrtc for OpaqueCrtc<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_crtc {
> +        self.crtc.get()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) -> &'a Self
> {
> +        // SAFETY: Our data layout starts with `bindings::drm_crtc`
> +        unsafe { &*ptr.cast() }
> +    }
> +}
> +
> +// SAFETY: We only expose this object to users directly after
> KmsDriver::create_objects has been
> +// called.
> +unsafe impl<T: KmsDriver> ModesettableCrtc for OpaqueCrtc<T> {
> +    type State = OpaqueCrtcState<T>;
> +}
> +
> +// SAFETY: We don't expose OpaqueCrtc<T> to users before `base` is
> initialized in Crtc::<T>::new(),
> +// so `raw_mode_obj` always returns a valid pointer to a
> bindings::drm_mode_object.
> +unsafe impl<T: KmsDriver> ModeObject for OpaqueCrtc<T> {
> +    type Driver = T;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: The parent device for a DRM connector will never
> outlive the connector, and this
> +        // pointer is invariant through the lifetime of the
> connector
> +        unsafe { Device::from_raw((*self.as_raw()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose DRM connectors to users before
> `base` is initialized
> +        unsafe { &raw mut (*self.as_raw()).base }
> +    }
> +}
> +
> +// SAFETY: CRTCs are non-refcounted modesetting objects
> +unsafe impl<T: KmsDriver> StaticModeObject for OpaqueCrtc<T> {}
> +
> +// SAFETY: Our CRTC interface is guaranteed to be thread-safe
> +unsafe impl<T: KmsDriver> Send for OpaqueCrtc<T> {}
> +
> +// SAFETY: Our CRTC interface is guaranteed to be thread-safe
> +unsafe impl<T: KmsDriver> Sync for OpaqueCrtc<T> {}
> +
> +// SAFETY: `funcs` is initialized when the CRTC is allocated
> +unsafe impl<T: KmsDriver> ModeObjectVtable for OpaqueCrtc<T> {
> +    type Vtable = bindings::drm_crtc_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        // SAFETY: `as_raw()` always returns a valid pointer to a
> crtc
> +        unsafe { (*self.as_raw()).funcs }
> +    }
> +}
> +
> +impl<T: DriverCrtcState> Sealed for CrtcState<T> {}
> +
> +/// The main trait for implementing the [`struct drm_crtc_state`]
> API for a [`Crtc`].
> +///
> +/// A driver may store driver-private data within the implementor's
> type, which will be available
> +/// when using a full typed [`CrtcState`] object.
> +///
> +/// # Invariants
> +///
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_crtc`] pointers are contained within a
> [`Crtc<Self::Crtc>`].
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_crtc_state`] pointers are contained within a
> [`CrtcState<Self>`].
> +///
> +/// [`struct drm_crtc`]: srctree/include/drm_crtc.h
> +/// [`struct drm_crtc_state`]: srctree/include/drm_crtc.h
> +pub trait DriverCrtcState: Clone + Default + Unpin {
> +    /// The parent CRTC driver for this CRTC state
> +    type Crtc: DriverCrtc<State = Self>
> +    where
> +        Self: Sized;
> +}
> +
> +/// The main interface for a [`struct drm_crtc_state`].
> +///
> +/// This type is the main interface for dealing with the atomic
> state of DRM crtcs. In addition, it
> +/// allows access to whatever private data is contained within an
> implementor's [`DriverCrtcState`]
> +/// type.
> +///
> +/// # Invariants
> +///
> +/// - `state` and `inner` initialized for as long as this object is
> exposed to users.
> +/// - The data layout of this structure begins with [`struct
> drm_crtc_state`].
> +/// - The CRTC for this type can always be assumed to be of type
> [`Crtc<T::Crtc>`].
> +///
> +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h
> +#[repr(C)]
> +pub struct CrtcState<T: DriverCrtcState> {
> +    // It should be noted that CrtcState is a bit of an oddball -
> it's the only atomic state
> +    // structure that can be modified after it has been swapped in,
> which is why we need to have
> +    // `state` within an `Opaque<>`…
> +    state: Opaque<bindings::drm_crtc_state>,
> +
> +    // …it is also one of the few atomic states that some drivers
> will embed work structures into,
> +    // which means there's a good chance in the future we may have
> pinned data here - making it
> +    // impossible for us to hold a mutable or immutable reference to
> the CrtcState. In preparation
> +    // for that possibility, we keep `T` in an UnsafeCell.
> +    inner: UnsafeCell<T>,
> +}
> +
> +impl<T: DriverCrtcState> Deref for CrtcState<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        // SAFETY: Our interface ensures that `inner` will not be
> modified unless only a single
> +        // mutable reference exists to `inner`, so this is safe
> +        unsafe { &*self.inner.get() }
> +    }
> +}
> +
> +impl<T: DriverCrtcState> DerefMut for CrtcState<T> {
> +    fn deref_mut(&mut self) -> &mut Self::Target {
> +        self.inner.get_mut()
> +    }
> +}
> +
> +// SAFETY: Shares the safety guarantee of Crtc<T>'s ModeObjectVtable
> impl
> +unsafe impl<T: DriverCrtcState> ModeObjectVtable for CrtcState<T> {
> +    type Vtable = bindings::drm_crtc_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.crtc().vtable()
> +    }
> +}
> +
> +impl<T: DriverCrtcState> CrtcState<T> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <'a, D, C>(&'a OpaqueCrtcState<D>) -> &'a Self
> +        where
> +            T: DriverCrtcState<Crtc = C>;
> +        use
> +            C as DriverCrtc,
> +            D as KmsDriver<Crtc = ...>
> +    }
> +}
> +
> +/// A trait implemented by any type which can produce a reference to
> a [`struct drm_crtc_state`].
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h
> +pub trait AsRawCrtcState: private::AsRawCrtcState {
> +    /// The type that this CRTC state interface returns to represent
> the parent CRTC
> +    type Crtc: ModesettableCrtc;
> +}
> +
> +pub(crate) mod private {
> +    use super::*;
> +
> +    #[allow(unreachable_pub)]
> +    pub trait AsRawCrtcState {
> +        /// Return a raw pointer to the DRM CRTC state
> +        ///
> +        /// Note that CRTC states are the only atomic state in KMS
> which don't nicely follow rust's
> +        /// data aliasing rules already.
> +        fn as_raw(&self) -> *mut bindings::drm_crtc_state;
> +    }
> +}
> +
> +pub(super) use private::AsRawCrtcState as AsRawCrtcStatePrivate;
> +
> +/// Common methods available on any type which implements
> [`AsRawCrtcState`].
> +///
> +/// This is implemented internally by DRM, and provides many of the
> basic methods for working with
> +/// the atomic state of [`Crtc`]s.
> +pub trait RawCrtcState: AsRawCrtcState {
> +    /// Return the CRTC that owns this state.
> +    fn crtc(&self) -> &Self::Crtc {
> +        // SAFETY:
> +        // - This type conversion is guaranteed by type invariance
> +        // - Our interface ensures that this access follows rust's
> data-aliasing rules
> +        // - `crtc` is guaranteed to never be NULL and is invariant
> throughout the lifetime of the
> +        //   state
> +        unsafe { <Self::Crtc as
> AsRawCrtc>::from_raw((*self.as_raw()).crtc) }
> +    }
> +
> +    /// Returns whether or not the CRTC is active in this atomic
> state.
> +    fn active(&self) -> bool {
> +        // SAFETY: `active` and the rest of its containing bitfield
> can only be modified from the
> +        // atomic check context, and are invariant beyond that point
> - so our interface can ensure
> +        // this access is serialized
> +        unsafe { (*self.as_raw()).active }
> +    }
> +}
> +impl<T: AsRawCrtcState> RawCrtcState for T {}
> +
> +/// A trait implemented for any type which can be constructed
> directly from a
> +/// [`struct drm_crtc_state`] pointer.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h
> +pub trait FromRawCrtcState: AsRawCrtcState {
> +    /// Obtain a reference back to this type from a raw DRM crtc
> state pointer
> +    ///
> +    /// # Safety
> +    ///
> +    /// Callers must ensure that ptr contains a valid instance of
> this type.
> +    unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc_state) ->
> &'a Self;
> +}
> +
> +impl<T: DriverCrtcState> private::AsRawCrtcState for CrtcState<T> {
> +    #[inline]
> +    fn as_raw(&self) -> *mut bindings::drm_crtc_state {
> +        self.state.get()
> +    }
> +}
> +
> +impl<T: DriverCrtcState> AsRawCrtcState for CrtcState<T> {
> +    type Crtc = Crtc<T::Crtc>;
> +}
> +
> +impl<T: DriverCrtcState> FromRawCrtcState for CrtcState<T> {
> +    unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc_state) ->
> &'a Self {
> +        // SAFETY: Our data layout starts with
> `bindings::drm_crtc_state`
> +        unsafe { &*(ptr.cast()) }
> +    }
> +}
> +
> +/// A [`struct drm_crtc_state`] without a known [`DriverCrtcState`]
> implementation.
> +///
> +/// This is mainly for situations where our bindings can't infer the
> [`DriverCrtcState`]
> +/// implementation for a [`struct drm_crtc_state`] automatically. It
> is identical to [`Crtc`],
> +/// except that it does not provide access to the driver's private
> data.
> +///
> +/// # Invariants
> +///
> +/// - `state` is initialized for as long as this object is exposed
> to users.
> +/// - The data layout of this type is identical to [`struct
> drm_crtc_state`].
> +///
> +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h
> +#[repr(transparent)]
> +pub struct OpaqueCrtcState<T: KmsDriver> {
> +    state: Opaque<bindings::drm_crtc_state>,
> +    _p: PhantomData<T>,
> +}
> +
> +impl<T: KmsDriver> AsRawCrtcState for OpaqueCrtcState<T> {
> +    type Crtc = OpaqueCrtc<T>;
> +}
> +
> +impl<T: KmsDriver> private::AsRawCrtcState for OpaqueCrtcState<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_crtc_state {
> +        self.state.get()
> +    }
> +}
> +
> +impl<T: KmsDriver> FromRawCrtcState for OpaqueCrtcState<T> {
> +    unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc_state) ->
> &'a Self {
> +        // SAFETY: Our data layout is identical to
> `bindings::drm_crtc_state`
> +        unsafe { &*(ptr.cast()) }
> +    }
> +}
> +
> +// SAFETY: Shares the safety guarantees of OpaqueCrtc<T>'s
> ModeObjectVtable impl
> +unsafe impl<T: KmsDriver> ModeObjectVtable for OpaqueCrtcState<T> {
> +    type Vtable = bindings::drm_crtc_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.crtc().vtable()
> +    }
> +}
> +
> +/// An interface for mutating a [`Crtc`]s atomic state.
> +///
> +/// This type is typically returned by an [`AtomicStateMutator`]
> within contexts where it is
> +/// possible to safely mutate a plane's state. In order to uphold
> rust's data-aliasing rules, only
> +/// [`CrtcStateMutator`] may exist at a time.
> +///
> +/// # Invariants
> +///
> +/// `self.state` always points to a valid instance of a
> [`FromRawCrtcState`] object.
> +pub struct CrtcStateMutator<'a, T: FromRawCrtcState> {
> +    state: NonNull<T>,
> +    mask: &'a Cell<u32>,
> +}
> +
> +impl<'a, T: FromRawCrtcState> CrtcStateMutator<'a, T> {
> +    pub(super) fn new<D: KmsDriver>(
> +        mutator: &'a AtomicStateMutator<D>,
> +        state: NonNull<bindings::drm_crtc_state>,
> +    ) -> Option<Self> {
> +        // SAFETY: `crtc` is invariant throughout the lifetime of
> the atomic state, and always
> +        // points to a valid `Crtc<T::Crtc>`
> +        let crtc = unsafe {
> T::Crtc::from_raw((*state.as_ptr()).crtc) };
> +        let crtc_mask = crtc.mask();
> +        let borrowed_mask = mutator.borrowed_crtcs.get();
> +
> +        if borrowed_mask & crtc_mask == 0 {
> +            mutator.borrowed_crtcs.set(borrowed_mask | crtc_mask);
> +            Some(Self {
> +                mask: &mutator.borrowed_crtcs,
> +                state: state.cast(),
> +            })
> +        } else {
> +            None
> +        }
> +    }
> +}
> +
> +impl<'a, T: DriverCrtcState> CrtcStateMutator<'a, CrtcState<T>> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <D, C>(CrtcStateMutator<'a, OpaqueCrtcState<D>>) -> Self
> +        where
> +            T: DriverCrtcState<Crtc = C>;
> +        use
> +            T as DriverCrtc,
> +            D as KmsDriver<Crtc = ...>
> +    }
> +}
> +
> +impl<'a, T: FromRawCrtcState> Drop for CrtcStateMutator<'a, T> {
> +    fn drop(&mut self) {
> +        // SAFETY: Our interface is proof that we are the only ones
> with a reference to this data
> +        let mask = unsafe { self.state.as_ref() }.crtc().mask();
> +        self.mask.set(self.mask.get() & !mask);
> +    }
> +}
> +
> +impl<'a, T: DriverCrtcState> Deref for CrtcStateMutator<'a,
> CrtcState<T>> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        // SAFETY: Our interface ensures that `self.state.inner`
> follows rust's data-aliasing rules,
> +        // so this is safe
> +        unsafe { &*(*self.state.as_ptr()).inner.get() }
> +    }
> +}
> +
> +impl<'a, T: DriverCrtcState> DerefMut for CrtcStateMutator<'a,
> CrtcState<T>> {
> +    fn deref_mut(&mut self) -> &mut Self::Target {
> +        // SAFETY: Our interface ensures that `self.state.inner`
> follows rust's data-aliasing rules,
> +        // so this is safe
> +        unsafe { (*self.state.as_ptr()).inner.get_mut() }
> +    }
> +}
> +
> +impl<'a, T: FromRawCrtcState> AsRawCrtcState for
> CrtcStateMutator<'a, T> {
> +    type Crtc = T::Crtc;
> +}
> +
> +impl<'a, T: FromRawCrtcState> private::AsRawCrtcState for
> CrtcStateMutator<'a, T> {
> +    fn as_raw(&self) -> *mut bindings::drm_crtc_state {
> +        self.state.as_ptr().cast()
> +    }
> +}
> +
> +// SAFETY: Shares the safety guarantees of T::Crtc's
> ModeObjectVtable impl
> +unsafe impl<'a, T: FromRawCrtcState> ModeObjectVtable for
> CrtcStateMutator<'a, T>
> +where
> +    T::Crtc: ModeObjectVtable,
> +{
> +    type Vtable = <T::Crtc as ModeObjectVtable>::Vtable;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.crtc().vtable()
> +    }
> +}
> +
> +/// A token provided during [`atomic_check`] callbacks for accessing
> the crtc, atomic state, and new
> +/// and old states of the crtc.
> +///
> +/// # Invariants
> +///
> +/// This token is proof that the old and new atomic state of `crtc`
> are present in `state` and do
> +/// not have any mutators taken out.
> +///
> +/// [`atomic_check`]: DriverCrtc::atomic_check
> +pub struct CrtcAtomicCheck<'a, T: DriverCrtc> {
> +    state: &'a AtomicStateComposer<T::Driver>,
> +    crtc: &'a Crtc<T>,
> +}
> +
> +impl<'a, T: DriverCrtc> CrtcAtomicCheck<'a, T> {
> +    impl_atomic_state_token_ops!(
> +        CrtcAtomicCheck,
> +        AtomicStateComposer,
> +        Crtc,
> +        use <'a, T>
> +    );
> +}
> +
> +/// A token provided to [`DriverCrtc`] callbacks during the atomic
> commit phase for accessing the
> +/// crtc, atomic state, new and old states of the crtc.
> +///
> +/// # Invariants
> +///
> +/// This token is proof that the old and new atomic state of `crtc`
> are present in `state` and do
> +/// not have any mutators taken out.
> +pub struct CrtcAtomicCommit<'a, T: DriverCrtc> {
> +    state: &'a AtomicStateMutator<T::Driver>,
> +    crtc: &'a Crtc<T>,
> +}
> +
> +impl<'a, T: DriverCrtc> CrtcAtomicCommit<'a, T> {
> +    impl_atomic_state_token_ops!(
> +        CrtcAtomicCommit,
> +        AtomicStateMutator,
> +        Crtc,
> +        use <'a, T>
> +    );
> +}
> +
> +unsafe extern "C" fn crtc_destroy_callback<T: DriverCrtc>(crtc: *mut
> bindings::drm_crtc) {
> +    // SAFETY: DRM guarantees that `crtc` points to a valid
> initialized `drm_crtc`.
> +    unsafe { bindings::drm_crtc_cleanup(crtc) };
> +
> +    // SAFETY:
> +    // - DRM guarantees we are now the only one with access to this
> [`drm_crtc`].
> +    // - This cast is safe via `DriverCrtc`s type invariants.
> +    // - We created this as a pinned type originally
> +    drop(unsafe { Pin::new_unchecked(KBox::from_raw(crtc as *mut
> Crtc<T>)) });
> +}
> +
> +unsafe extern "C" fn atomic_duplicate_state_callback<T:
> DriverCrtcState>(
> +    crtc: *mut bindings::drm_crtc,
> +) -> *mut bindings::drm_crtc_state {
> +    // SAFETY: DRM guarantees that `crtc` points to a valid
> initialized `drm_crtc`.
> +    let state = unsafe { (*crtc).state };
> +    if state.is_null() {
> +        return null_mut();
> +    }
> +
> +    // SAFETY: This cast is safe via `DriverCrtcState`s type
> invariants.
> +    let crtc = unsafe { Crtc::<T::Crtc>::from_raw(crtc) };
> +
> +    // SAFETY: This cast is safe via `DriverCrtcState`s type
> invariants.
> +    let state = unsafe { CrtcState::<T>::from_raw(state) };
> +
> +    let new: Result<KBox<_>> = KBox::try_init(
> +        try_init!(CrtcState {
> +            inner: UnsafeCell::new((*state).clone()),
> +            state: Opaque::new(Default::default()),
> +        }),
> +        GFP_KERNEL,
> +    );
> +
> +    if let Ok(new) = new {
> +        let new = KBox::into_raw(new).cast();
> +
> +        // SAFETY:
> +        // - `new` provides a valid pointer to a newly allocated
> `drm_crtc_state` via type
> +        // invariants
> +        // - This initializes `new` via memcpy()
> +        unsafe {
> bindings::__drm_atomic_helper_crtc_duplicate_state(crtc.as_raw(),
> new) }
> +
> +        new
> +    } else {
> +        null_mut()
> +    }
> +}
> +
> +unsafe extern "C" fn atomic_destroy_state_callback<T:
> DriverCrtcState>(
> +    _crtc: *mut bindings::drm_crtc,
> +    crtc_state: *mut bindings::drm_crtc_state,
> +) {
> +    // SAFETY: DRM guarantees that `state` points to a valid
> instance of `drm_crtc_state`
> +    unsafe {
> bindings::__drm_atomic_helper_crtc_destroy_state(crtc_state) };
> +
> +    // SAFETY:
> +    // - DRM guarantees we are the only one with access to this
> `drm_crtc_state`
> +    // - This cast is safe via our type invariants.
> +    // - All data in `CrtcState` is either Unpin, or pinned
> +    drop(unsafe { KBox::from_raw(crtc_state as *mut CrtcState<T>)
> });
> +}
> +
> +unsafe extern "C" fn crtc_reset_callback<T: DriverCrtcState>(crtc:
> *mut bindings::drm_crtc) {
> +    // SAFETY: DRM guarantees that `state` points to a valid
> instance of `drm_crtc_state`
> +    let state = unsafe { (*crtc).state };
> +    if !state.is_null() {
> +        // SAFETY:
> +        // - We're guaranteed `crtc` is `Crtc<T>` via type
> invariants
> +        // - We're guaranteed `state` is `CrtcState<T>` via type
> invariants.
> +        unsafe { atomic_destroy_state_callback::<T>(crtc, state) }
> +
> +        // SAFETY: No special requirements here, DRM expects this to
> be NULL
> +        unsafe {
> +            (*crtc).state = null_mut();
> +        }
> +    }
> +
> +    // SAFETY: `crtc` is guaranteed to be of type `Crtc<T::Crtc>` by
> type invariance
> +    let crtc = unsafe { Crtc::<T::Crtc>::from_raw(crtc) };
> +
> +    // Unfortunately, this is the best we can do at the moment as
> this FFI callback was mistakenly
> +    // presumed to be infallible :(
> +    let new: KBox<CrtcState<T>> = KBox::try_init(
> +        try_init!(CrtcState {
> +            state: Opaque::new(Default::default()),
> +            inner: UnsafeCell::new(Default::default()),
> +        }),
> +        GFP_KERNEL,
> +    )
> +    .expect("Unfortunately, this API was presumed infallible");
> +
> +    // SAFETY: DRM takes ownership of the state from here, and will
> never move it
> +    unsafe { bindings::__drm_atomic_helper_crtc_reset(crtc.as_raw(),
> KBox::into_raw(new).cast()) };
> +}
> +
> +unsafe extern "C" fn atomic_check_callback<T: DriverCrtc>(
> +    crtc: *mut bindings::drm_crtc,
> +    state: *mut bindings::drm_atomic_state,
> +) -> i32 {
> +    // SAFETY:
> +    // - We're guaranteed `crtc` is of type `Crtc<T>` via type
> invariants.
> +    // - We're guaranteed by DRM that `crtc` is pointing to a valid
> initialized state.
> +    let crtc = unsafe { Crtc::from_raw(crtc) };
> +
> +    // SAFETY: DRM guarantees `state` points to a valid
> `drm_atomic_state`
> +    // We use a ManuallyDrop here to avoid AtomicStateComposer
> dropping an owned reference we never
> +    // acquired.
> +    let state =
> +        unsafe {
> ManuallyDrop::new(AtomicStateComposer::new(NonNull::new_unchecked(sta
> te))) };
> +
> +    // SAFETY:
> +    // - Since we're in the atomic check callback, we're guaranteed
> by DRM that both the old and
> +    //   new crtc state are present in this atomic state
> +    // - We just created the state composer above, so other
> composers cannot be taken out on the
> +    //   crtc state yet.
> +    let check = unsafe { CrtcAtomicCheck::new(crtc, &state) };
> +
> +    from_result(|| {
> +        T::atomic_check(check)?;
> +        Ok(0)
> +    })
> +}
> +
> +unsafe extern "C" fn atomic_begin_callback<T: DriverCrtc>(
> +    crtc: *mut bindings::drm_crtc,
> +    state: *mut bindings::drm_atomic_state,
> +) {
> +    // SAFETY:
> +    // * We're guaranteed `crtc` is of type `Crtc<T>` via type
> invariants.
> +    // * We're guaranteed by DRM that `crtc` is pointing to a valid
> initialized state.
> +    let crtc = unsafe { Crtc::from_raw(crtc) };
> +
> +    // SAFETY: We're guaranteed by DRM that `state` points to a
> valid instance of `drm_atomic_state`
> +    let state = unsafe {
> AtomicStateMutator::new(NonNull::new_unchecked(state)) };
> +
> +    // SAFETY:
> +    // - Since we're in the atomic_begin callback, we're guaranteed
> by DRM that both the old and new
> +    //   crtc state are resent in this atomic state.
> +    // - We just created the state mutator above, so other mutators
> cannot be taken out on the crtc
> +    //   state yet.
> +    let commit = unsafe { CrtcAtomicCommit::new(crtc, &state) };
> +
> +    T::atomic_begin(commit);
> +}
> +
> +unsafe extern "C" fn atomic_flush_callback<T: DriverCrtc>(
> +    crtc: *mut bindings::drm_crtc,
> +    state: *mut bindings::drm_atomic_state,
> +) {
> +    // SAFETY:
> +    // - We're guaranteed `crtc` is of type `Crtc<T>` via type
> invariants.
> +    // - We're guaranteed by DRM that `crtc` is pointing to a valid
> initialized state.
> +    let crtc = unsafe { Crtc::from_raw(crtc) };
> +
> +    // SAFETY: We're guaranteed by DRM that `state` points to a
> valid instance of `drm_atomic_state`
> +    let state = unsafe {
> AtomicStateMutator::new(NonNull::new_unchecked(state)) };
> +
> +    // SAFETY:
> +    // - Since we're in the atomic_flush callback, we're guaranteed
> by DRM that both the old and new
> +    //   crtc state are resent in this atomic state.
> +    // - We just created the state mutator above, so other mutators
> cannot be taken out on the crtc
> +    //   state yet.
> +    let commit = unsafe { CrtcAtomicCommit::new(crtc, &state) };
> +
> +    T::atomic_flush(commit);
> +}
> +
> +unsafe extern "C" fn atomic_enable_callback<T: DriverCrtc>(
> +    crtc: *mut bindings::drm_crtc,
> +    state: *mut bindings::drm_atomic_state,
> +) {
> +    // SAFETY:
> +    // - We're guaranteed `crtc` is of type `Crtc<T>` via type
> invariants.
> +    // - We're guaranteed by DRM that `crtc` is pointing to a valid
> initialized state.
> +    let crtc = unsafe { Crtc::from_raw(crtc) };
> +
> +    // SAFETY: DRM never passes an invalid ptr for `state`
> +    let state = unsafe {
> AtomicStateMutator::new(NonNull::new_unchecked(state)) };
> +
> +    // SAFETY:
> +    // - Since we're in the atomic_enable callback, we're guaranteed
> by DRM that both the old and
> +    //   new crtc state are present in this atomic state.
> +    // - We just created the state mutator above, so other mutators
> cannot be taken out on the crtc
> +    //   state yet.
> +    let commit = unsafe { CrtcAtomicCommit::new(crtc, &state) };
> +
> +    T::atomic_enable(commit);
> +}
> +
> +unsafe extern "C" fn atomic_disable_callback<T: DriverCrtc>(
> +    crtc: *mut bindings::drm_crtc,
> +    state: *mut bindings::drm_atomic_state,
> +) {
> +    // SAFETY:
> +    // - We're guaranteed `crtc` points to a valid instance of
> `drm_crtc`
> +    // - We're guaranteed that `crtc` is of type `Plane<T>` by
> `DriverPlane`s type invariants.
> +    let crtc = unsafe { Crtc::from_raw(crtc) };
> +
> +    // SAFETY: We're guaranteed that `state` points to a valid
> `drm_crtc_state` by DRM
> +    let state = unsafe {
> AtomicStateMutator::new(NonNull::new_unchecked(state)) };
> +
> +    // SAFETY:
> +    // - Since we're in the atomic_disable callback, we're
> guaranteed by DRM that both the old and
> +    //   new crtc state are present in this atomic state.
> +    // - We just created the state mutator above, so other mutators
> cannot be taken out on the crtc
> +    //   state yet.
> +    let commit = unsafe { CrtcAtomicCommit::new(crtc, &state) };
> +
> +    T::atomic_disable(commit);
> +}
> diff --git a/rust/kernel/drm/kms/encoder.rs
> b/rust/kernel/drm/kms/encoder.rs
> new file mode 100644
> index 000000000000..5f860faf8b61
> --- /dev/null
> +++ b/rust/kernel/drm/kms/encoder.rs
> @@ -0,0 +1,409 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! DRM encoders.
> +//!
> +//! C header:
> [`include/drm/drm_encoder.h`](srctree/include/drm/drm_encoder.h)
> +
> +use super::{
> +    KmsDriver, ModeObject, ModeObjectVtable, NewKmsDevice, Probing,
> StaticModeObject, Sealed
> +};
> +use crate::{
> +    alloc::KBox,
> +    drm::device::Device,
> +    error::to_result,
> +    prelude::*,
> +    types::{NotThreadSafe, Opaque},
> +};
> +use bindings;
> +use core::{
> +    marker::*,
> +    mem,
> +    ops::Deref,
> +    ptr::null,
> +};
> +use macros::paste;
> +
> +/// A macro for generating our type ID enumerator.
> +macro_rules! declare_encoder_types {
> +    ($( $oldname:ident as $newname:ident ),+) => {
> +        #[repr(i32)]
> +        #[non_exhaustive]
> +        #[derive(Copy, Clone, PartialEq, Eq)]
> +        /// An enumerator for all possible [`Encoder`] type IDs.
> +        pub enum Type {
> +            // Note: bindgen defaults the macro values to u32 and
> not i32, but DRM takes them as an
> +            // i32 - so just do the conversion here
> +            $(
> +                #[doc = concat!("The encoder type ID for a ",
> stringify!($newname), " encoder.")]
> +                $newname =
> paste!(crate::bindings::[<DRM_MODE_ENCODER_ $oldname>]) as i32
> +            ),+
> +        }
> +    };
> +}
> +
> +declare_encoder_types! {
> +    NONE     as None,
> +    DAC      as Dac,
> +    TMDS     as Tmds,
> +    LVDS     as Lvds,
> +    VIRTUAL  as Virtual,
> +    DSI      as Dsi,
> +    DPMST    as DpMst,
> +    DPI      as Dpi
> +}
> +
> +/// The main trait for implementing the [`struct drm_encoder`] API
> for [`Encoder`].
> +///
> +/// Any KMS driver should have at least one implementation of this
> type, which allows them to create
> +/// [`Encoder`] objects. Additionally, a driver may store driver-
> private data within the type that
> +/// implements [`DriverEncoder`] - and it will be made available
> when using a fully typed
> +/// [`Encoder`] object.
> +///
> +/// # Invariants
> +///
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_encoder`] pointers are contained within a
> [`Encoder<Self>`].
> +///
> +/// [`struct drm_encoder`]: srctree/include/drm/drm_encoder.h
> +#[vtable]
> +pub trait DriverEncoder: Send + Sync + Sized {
> +    /// The generated C vtable for this [`DriverEncoder`]
> implementation.
> +    const OPS: &'static DriverEncoderOps = &DriverEncoderOps {
> +        funcs: bindings::drm_encoder_funcs {
> +            reset: None,
> +            destroy: Some(encoder_destroy_callback::<Self>),
> +            late_register: None,
> +            early_unregister: None,
> +            debugfs_init: None,
> +        },
> +        helper_funcs: bindings::drm_encoder_helper_funcs {
> +            dpms: None,
> +            mode_valid: None,
> +            mode_fixup: None,
> +            prepare: None,
> +            mode_set: None,
> +            commit: None,
> +            detect: None,
> +            enable: None,
> +            disable: None,
> +            atomic_check: None,
> +            atomic_enable: None,
> +            atomic_disable: None,
> +            atomic_mode_set: None,
> +        },
> +    };
> +
> +    /// The parent driver for this drm_encoder implementation
> +    type Driver: KmsDriver;
> +
> +    /// The type to pass to the `args` field of
> [`UnregisteredEncoder::new`].
> +    ///
> +    /// This type will be made available in in the `args` argument
> of [`Self::new`]. Drivers which
> +    /// don't need this can simply pass [`()`] here.
> +    type Args;
> +
> +    /// The constructor for creating a [`Encoder`] using this
> [`DriverEncoder`] implementation.
> +    ///
> +    /// Drivers may use this to instantiate their [`DriverEncoder`]
> object.
> +    fn new(device: &Device<Self::Driver>, args: Self::Args) -> impl
> PinInit<Self, Error>;
> +}
> +
> +/// The generated C vtable for a [`DriverEncoder`].
> +///
> +/// This type is created internally by DRM.
> +pub struct DriverEncoderOps {
> +    funcs: bindings::drm_encoder_funcs,
> +    helper_funcs: bindings::drm_encoder_helper_funcs,
> +}
> +
> +/// A trait implemented by any type that acts as a [`struct
> drm_encoder`] interface.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// # Safety
> +///
> +/// [`as_raw()`] must always return a valid pointer to a [`struct
> drm_encoder`].
> +///
> +/// [`struct drm_encoder`]: srctree/include/drm/drm_encoder.h
> +/// [`as_raw()`]: AsRawEncoder::as_raw()
> +pub unsafe trait AsRawEncoder {
> +    /// Return the raw `bindings::drm_encoder` for this DRM encoder.
> +    ///
> +    /// Drivers should never use this directly
> +    fn as_raw(&self) -> *mut bindings::drm_encoder;
> +
> +    /// Convert a raw `bindings::drm_encoder` pointer into an object
> of this type.
> +    ///
> +    /// # Safety
> +    ///
> +    /// Callers promise that `ptr` points to a valid instance of
> this type
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encoder) -> &'a
> Self;
> +}
> +
> +/// The main interface for a [`struct drm_encoder`].
> +///
> +/// This type is the main interface for dealing with DRM encoders.
> In addition, it also allows
> +/// immutable access to whatever private data is contained within an
> implementor's
> +/// [`DriverEncoder`] type.
> +///
> +/// # Invariants
> +///
> +/// - `encoder` and `inner` are initialized for as long as this
> object is made available to users.
> +/// - The data layout of this structure begins with [`struct
> drm_encoder`].
> +///
> +/// [`struct drm_encoder`]: srctree/include/drm/drm_encoder.h
> +#[repr(C)]
> +#[pin_data]
> +pub struct Encoder<T: DriverEncoder> {
> +    /// The FFI drm_encoder object
> +    encoder: Opaque<bindings::drm_encoder>,
> +    /// The driver's private inner data
> +    #[pin]
> +    inner: T,
> +    #[pin]
> +    _p: PhantomPinned,
> +}
> +
> +impl<T: DriverEncoder> Sealed for Encoder<T> {}
> +
> +// SAFETY: Our interface is thread-safe.
> +unsafe impl<T: DriverEncoder> Send for Encoder<T> {}
> +// SAFETY: Our interface is thread-safe.
> +unsafe impl<T: DriverEncoder> Sync for Encoder<T> {}
> +
> +// SAFETY: We don't expose Encoder<T> to users before `base` is
> initialized in ::new(), so
> +// `raw_mode_obj` always returns a valid pointer to a
> bindings::drm_mode_object.
> +unsafe impl<T: DriverEncoder> ModeObject for Encoder<T> {
> +    type Driver = T::Driver;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: DRM encoders exist for as long as the device
> does, so this pointer is always
> +        // valid
> +        unsafe { Device::from_raw((*self.encoder.get()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose Encoder<T> to users before it's
> initialized, so `base` is always
> +        // initialized
> +        unsafe { &raw mut (*self.encoder.get()).base }
> +    }
> +}
> +
> +// SAFETY: Encoders do not have a refcount
> +unsafe impl<T: DriverEncoder> StaticModeObject for Encoder<T> {}
> +
> +impl<T: DriverEncoder> Deref for Encoder<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.inner
> +    }
> +}
> +
> +// SAFETY:
> +// - Via our type invariants our data layout starts with
> `drm_encoder`.
> +// - Since we don't expose `Encoder` to users befre it has been
> initialized, this and our data
> +//   layout ensure that `as_raw()` always returns a valid pointer to
> a `drm_encoder`.
> +unsafe impl<T: DriverEncoder> AsRawEncoder for Encoder<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_encoder {
> +        self.encoder.get()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encoder) -> &'a
> Self {
> +        // SAFETY: Our data layout is starts with to
> `bindings::drm_encoder`
> +        unsafe { &*ptr.cast() }
> +    }
> +}
> +
> +// SAFETY: `funcs` is initialized when the encoder is allocated
> +unsafe impl<T: DriverEncoder> ModeObjectVtable for Encoder<T> {
> +    type Vtable = bindings::drm_encoder_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        // SAFETY: `as_raw()` always returns a valid pointer to an
> encoder
> +        unsafe { *self.as_raw() }.funcs
> +    }
> +}
> +
> +impl<T: DriverEncoder> Encoder<T> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <'a, D>(&'a OpaqueEncoder<D>) -> &'a Self;
> +        use
> +            T as DriverEncoder,
> +            D as KmsDriver<Encoder = ...>
> +    }
> +}
> +
> +/// A [`Encoder`] that has not yet been registered with userspace.
> +///
> +/// KMS registration is single-threaded, so this object is not
> thread-safe.
> +///
> +/// # Invariants
> +///
> +/// - This object can only exist before its respective KMS device
> has been registered.
> +/// - Otherwise, it inherits all invariants of [`Encoder`] and has
> an identical data layout.
> +pub struct UnregisteredEncoder<T: DriverEncoder>(Encoder<T>,
> NotThreadSafe);
> +
> +// SAFETY: We inherit all relevant invariants of `Encoder`
> +unsafe impl<T: DriverEncoder> AsRawEncoder for
> UnregisteredEncoder<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_encoder {
> +        self.0.as_raw()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encoder) -> &'a
> Self {
> +        // SAFETY: This is another from_raw() call, so this function
> shares the same safety contract
> +        let encoder = unsafe { Encoder::<T>::from_raw(ptr) };
> +
> +        // SAFETY: Our data layout is identical via our type
> invariants.
> +        unsafe { mem::transmute(encoder) }
> +    }
> +}
> +
> +impl<T: DriverEncoder> Deref for UnregisteredEncoder<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.0.inner
> +    }
> +}
> +
> +impl<T: DriverEncoder> UnregisteredEncoder<T> {
> +    /// Construct a new [`UnregisteredEncoder`].
> +    ///
> +    /// A driver may use this from their
> [`KmsDriver::create_objects`] callback in order to
> +    /// construct new [`UnregisteredEncoder`] objects.
> +    ///
> +    /// [`KmsDriver::create_objects`]:
> kernel::drm::kms::KmsDriver::create_objects
> +    pub fn new<'a, 'b: 'a>(
> +        dev: &'a NewKmsDevice<'a, T::Driver, Probing>,
> +        type_: Type,
> +        possible_crtcs: u32,
> +        possible_clones: u32,
> +        name: Option<&CStr>,
> +        args: T::Args,
> +    ) -> Result<&'b Self> {
> +        let this: Pin<KBox<Encoder<T>>> = KBox::try_pin_init(
> +            try_pin_init!(Encoder {
> +                encoder: Opaque::new(bindings::drm_encoder {
> +                    helper_private: &T::OPS.helper_funcs,
> +                    possible_crtcs,
> +                    possible_clones,
> +                    ..Default::default()
> +                }),
> +                inner <- T::new(dev, args),
> +                _p: PhantomPinned
> +            }),
> +            GFP_KERNEL,
> +        )?;
> +
> +        // SAFETY:
> +        // - `dev` is responsible for destroying the encoder and
> thus outlives us.
> +        // - as_raw() returns valid pointers for each type here
> +        // - This initializes `this`
> +        // - Our type is proof that this is being called before KMS
> device registration
> +        // - `name` is optional and will be auto-generated by DRM if
> passed as NULL
> +        to_result(unsafe {
> +            bindings::drm_encoder_init(
> +                dev.as_raw(),
> +                this.as_raw(),
> +                &T::OPS.funcs,
> +                type_ as _,
> +                name.map_or(null(), |n| n.as_char_ptr()),
> +            )
> +        })?;
> +
> +        // SAFETY: We don't move anything
> +        let this = unsafe { Pin::into_inner_unchecked(this) };
> +
> +        // We'll re-assemble the box in encoder_destroy_callback()
> +        let this = KBox::into_raw(this);
> +
> +        // UnregisteredEncoder has an equivalent data layout
> +        let this: *mut Self = this.cast();
> +
> +        // SAFETY: We just allocated the encoder above, so this
> pointer must be valid
> +        Ok(unsafe { &*this })
> +    }
> +}
> +
> +/// A [`struct drm_encoder`] without a known [`DriverEncoder`]
> implementation.
> +///
> +/// This is mainly for situations where our bindings can't infer the
> [`DriverEncoder`] implementation
> +/// for a [`struct drm_encoder`] automatically. It is identical to
> [`Encoder`], except that it does not
> +/// provide access to the driver's private data.
> +///
> +/// # Invariants
> +///
> +/// Same as [`Encoder`].
> +#[repr(transparent)]
> +pub struct OpaqueEncoder<T: KmsDriver> {
> +    encoder: Opaque<bindings::drm_encoder>,
> +    _p: PhantomData<T>,
> +}
> +
> +impl<T: KmsDriver> Sealed for OpaqueEncoder<T> {}
> +
> +// SAFETY: All of our encoder interfaces are thread-safe
> +unsafe impl<T: KmsDriver> Send for OpaqueEncoder<T> {}
> +
> +// SAFETY: All of our encoder interfaces are thread-safe
> +unsafe impl<T: KmsDriver> Sync for OpaqueEncoder<T> {}
> +
> +// SAFETY: We don't expose OpaqueEncoder<T> to users before `base`
> is initialized in
> +// OpaqueEncoder::new(), so `raw_mode_obj` always returns a valid
> poiner to a
> +// bindings::drm_mode_object.
> +unsafe impl<T: KmsDriver> ModeObject for OpaqueEncoder<T> {
> +    type Driver = T;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: DRM encoders exist for as long as the device
> does, so this pointer is always
> +        // valid
> +        unsafe { Device::from_raw((*self.encoder.get()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose Encoder<T> to users before it's
> initialized, so `base` is always
> +        // initialized
> +        unsafe { &raw mut (*self.encoder.get()).base }
> +    }
> +}
> +
> +// SAFETY: Encoders do not have a refcount
> +unsafe impl<T: KmsDriver> StaticModeObject for OpaqueEncoder<T> {}
> +
> +// SAFETY:
> +// - Via our type variants our data layout is identical to  with
> `drm_encoder`
> +// - Since we don't expose `Encoder` to users before it has been
> initialized, this and our data
> +//   layout ensure that `as_raw()` always returns a valid pointer to
> a `drm_encoder`.
> +unsafe impl<T: KmsDriver> AsRawEncoder for OpaqueEncoder<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_encoder {
> +        self.encoder.get()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encoder) -> &'a
> Self {
> +        // SAFETY: Our data layout is identical to
> `bindings::drm_encoder`
> +        unsafe { &*ptr.cast() }
> +    }
> +}
> +
> +// SAFETY: `funcs` is initialized when the encoder is allocated
> +unsafe impl<T: KmsDriver> ModeObjectVtable for OpaqueEncoder<T> {
> +    type Vtable = bindings::drm_encoder_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        // SAFETY: `as_raw()` always returns a valid pointer to an
> encoder
> +        unsafe { *self.as_raw() }.funcs
> +    }
> +}
> +
> +unsafe extern "C" fn encoder_destroy_callback<T: DriverEncoder>(
> +    encoder: *mut bindings::drm_encoder,
> +) {
> +    // SAFETY: DRM guarantees that `encoder` points to a valid
> initialized `drm_encoder`.
> +    unsafe { bindings::drm_encoder_cleanup(encoder) };
> +
> +    // SAFETY:
> +    // - DRM guarantees we are now the only one with access to this
> [`drm_encoder`].
> +    // - This cast is safe via `DriverEncoder`s type invariants.
> +    unsafe { drop(KBox::from_raw(encoder as *mut Encoder<T>)) };
> +}
> diff --git a/rust/kernel/drm/kms/framebuffer.rs
> b/rust/kernel/drm/kms/framebuffer.rs
> new file mode 100644
> index 000000000000..54d0391388a9
> --- /dev/null
> +++ b/rust/kernel/drm/kms/framebuffer.rs
> @@ -0,0 +1,70 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! DRM framebuffers.
> +//!
> +//! C header:
> [`include/drm/drm_framebuffer.h`](srctree/include/drm/drm_framebuffer
> .h)
> +
> +use super::{KmsDriver, ModeObject, Sealed};
> +use crate::{drm::device::Device, types::*};
> +use bindings;
> +use core::{marker::*, ptr};
> +
> +/// The main interface for [`struct drm_framebuffer`].
> +///
> +/// # Invariants
> +///
> +/// - `self.0` is initialized for as long as this object is exposed
> to users.
> +/// - This type has an identical data layout to [`struct
> drm_framebuffer`]
> +///
> +/// [`struct drm_framebuffer`]:
> srctree/include/drm/drm_framebuffer.h
> +#[repr(transparent)]
> +pub struct Framebuffer<T:
> KmsDriver>(Opaque<bindings::drm_framebuffer>, PhantomData<T>);
> +
> +// SAFETY:
> +// - `self.0` is initialized for as long as this object is exposed
> to users
> +// - `base` is initialized by DRM when `self.0` is initialized, thus
> `raw_mode_obj()` always returns
> +//   a valid pointer.
> +unsafe impl<T: KmsDriver> ModeObject for Framebuffer<T> {
> +    type Driver = T;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: `dev` points to an initialized `struct
> drm_device` for as long as this type is
> +        // initialized
> +        unsafe { Device::from_raw((*self.0.get()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose Framebuffer<T> to users before
> its initialized, so `base` is
> +        // always initialized
> +        unsafe { &raw mut (*self.0.get()).base }
> +    }
> +}
> +
> +// SAFETY: References to framebuffers are safe to be accessed from
> any thread
> +unsafe impl<T: KmsDriver> Send for Framebuffer<T> {}
> +// SAFETY: References to framebuffers are safe to be accessed from
> any thread
> +unsafe impl<T: KmsDriver> Sync for Framebuffer<T> {}
> +
> +// For implementing ModeObject
> +impl<T: KmsDriver> Sealed for Framebuffer<T> {}
> +
> +impl<T: KmsDriver> PartialEq for Framebuffer<T> {
> +    fn eq(&self, other: &Self) -> bool {
> +        ptr::eq(self.0.get(), other.0.get())
> +    }
> +}
> +impl<T: KmsDriver> Eq for Framebuffer<T> {}
> +
> +impl<T: KmsDriver> Framebuffer<T> {
> +    /// Convert a raw pointer to a `struct drm_framebuffer` into a
> [`Framebuffer`]
> +    ///
> +    /// # Safety
> +    ///
> +    /// The caller guarantews that `ptr` points to a initialized
> `struct drm_framebuffer` for at
> +    /// least the entire lifetime of `'a`.
> +    #[inline]
> +    pub(super) unsafe fn from_raw<'a>(ptr: *const
> bindings::drm_framebuffer) -> &'a Self {
> +        // SAFETY: Our data layout is identical to drm_framebuffer
> +        unsafe { &*ptr.cast() }
> +    }
> +}
> diff --git a/rust/kernel/drm/kms/modes.rs
> b/rust/kernel/drm/kms/modes.rs
> new file mode 100644
> index 000000000000..0e8dc434487d
> --- /dev/null
> +++ b/rust/kernel/drm/kms/modes.rs
> @@ -0,0 +1,76 @@
> +// SPDX-License-Identifier: GPL-2.0
> +use bindings;
> +
> +use crate::types::Opaque;
> +
> +/// DRM kernel-internal display mode structure.
> +///
> +/// This structure contains various resolution and timing
> information for a given display mode in
> +/// DRM.
> +///
> +/// # Invariants
> +///
> +/// - The data layout of this structure is guaranteed to be
> equivalent to that of `struct
> +///   drm_display_mode`.
> +/// - We ensure through our bindings that rust's data aliasing rules
> are maintained, ensuring it is
> +///   safe to read any fields inside of `self.inner`.
> +#[repr(transparent)]
> +pub struct DisplayMode {
> +    inner: Opaque<bindings::drm_display_mode>,
> +}
> +
> +// SAFETY: Our bindings are thread-safe via our type invariants.
> +unsafe impl Send for DisplayMode {}
> +// SAFETY: Our bindings are thread-safe via our type invariants.
> +unsafe impl Sync for DisplayMode {}
> +
> +impl DisplayMode {
> +    /// Convert a raw pointer to a `struct drm_display_mode` into an
> immutable [`DisplayMode`] ref.
> +    ///
> +    /// # SAFETY
> +    ///
> +    /// - The caller guarantees that `self_ptr` points to a valid
> initialized `struct
> +    ///   drm_display_mode`.
> +    /// - The caller must ensure that rust's data aliasing rules
> will not be broken for the lifetime
> +    ///   of `'a`, e.g. no mutable references may exist while
> immutable references exist to Self.
> +    #[inline]
> +    pub(crate) unsafe fn as_ref<'a>(self_ptr: *const
> bindings::drm_display_mode) -> &'a Self {
> +        // SAFETY: The pointer is valid via our safety contract, and
> the data layout of this struct
> +        // is equivalent to `Self` via our type invariants.
> +        unsafe { &*self_ptr.cast() }
> +    }
> +
> +    /// Return a raw pointer to the `struct drm_display_mode`
> contained within this [`DisplayMode`].
> +    #[inline]
> +    pub(crate) fn as_raw(&self) -> *const bindings::drm_display_mode
> {
> +        self.inner.get().cast_const()
> +    }
> +
> +    /// Retrieve the pixel clock for the adjusted display mode in
> kHz.
> +    #[inline]
> +    pub fn crtc_clock(&self) -> i32 {
> +        // SAFETY: Reading these fields is safe via our type
> invariants
> +        unsafe { (*self.as_raw()).crtc_clock }
> +    }
> +
> +    /// Retrieve the start of the vertical sync period for the
> adjusted display mode.
> +    #[inline]
> +    pub fn crtc_vblank_start(&self) -> u16 {
> +        unsafe { (*self.as_raw()).crtc_vblank_start }
> +    }
> +
> +    /// Retrieve the end of the vertical sync period for the
> adjusted display mode.
> +    #[inline]
> +    pub fn crtc_vblank_end(&self) -> u16 {
> +        // SAFETY: Reading these fields is safe via our type
> invariants
> +        unsafe { (*self.as_raw()).crtc_vblank_end }
> +    }
> +
> +    /// Retrieve the number of vertical scanlines for a full scanout
> frame in this adjusted display
> +    /// mode.
> +    #[inline]
> +    pub fn crtc_vtotal(&self) -> u16 {
> +        // SAFETY: Reading these fields is safe via our type
> invariants
> +        unsafe { (*self.as_raw()).crtc_vtotal }
> +    }
> +}
> diff --git a/rust/kernel/drm/kms/plane.rs
> b/rust/kernel/drm/kms/plane.rs
> new file mode 100644
> index 000000000000..661d82273099
> --- /dev/null
> +++ b/rust/kernel/drm/kms/plane.rs
> @@ -0,0 +1,1095 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! DRM display planes.
> +//!
> +//! C header:
> [`include/drm/drm_plane.h`](srctree/include/drm/drm_plane.h)
> +
> +use super::{
> +    atomic::*, crtc::*, framebuffer::*, KmsDriver, ModeObject,
> ModeObjectVtable, StaticModeObject,
> +    NewKmsDevice, Probing, Sealed
> +};
> +use crate::{
> +    alloc::KBox,
> +    bindings,
> +    drm::{device::Device, fourcc::*},
> +    error::{from_result, to_result, Error},
> +    prelude::*,
> +    types::{NotThreadSafe, Opaque},
> +};
> +use core::{
> +    cell::Cell,
> +    marker::*,
> +    mem::{self, ManuallyDrop},
> +    ops::*,
> +    pin::Pin,
> +    ptr::{null, null_mut, NonNull},
> +};
> +
> +/// The main trait for implementing the [`struct drm_plane`] API for
> [`Plane`].
> +///
> +/// Any KMS driver should have at least one implementation of this
> type, which allows them to create
> +/// [`Plane`] objects. Additionally, a driver may store driver-
> private data within the type that
> +/// implements [`DriverPlane`] - and it will be made available when
> using a fully typed [`Plane`]
> +/// object.
> +///
> +/// # Invariants
> +///
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_plane`] pointers are contained within a
> [`Plane<Self>`].
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_plane_state`] pointers are contained within a
> [`PlaneState<Self::State>`].
> +///
> +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h
> +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
> +#[vtable]
> +pub trait DriverPlane: Send + Sync + Sized {
> +    /// The generated C vtable for this [`DriverPlane`]
> implementation.
> +    const OPS: &'static DriverPlaneOps = &DriverPlaneOps {
> +        funcs: bindings::drm_plane_funcs {
> +            update_plane:
> Some(bindings::drm_atomic_helper_update_plane),
> +            disable_plane:
> Some(bindings::drm_atomic_helper_disable_plane),
> +            destroy: Some(plane_destroy_callback::<Self>),
> +            reset: Some(plane_reset_callback::<Self>),
> +            set_property: None,
> +            atomic_duplicate_state:
> Some(atomic_duplicate_state_callback::<Self::State>),
> +            atomic_destroy_state:
> Some(atomic_destroy_state_callback::<Self::State>),
> +            atomic_set_property: None,
> +            atomic_get_property: None,
> +            late_register: None,
> +            early_unregister: None,
> +            atomic_print_state: None,
> +            format_mod_supported: None,
> +            format_mod_supported_async: None,
> +        },
> +
> +        helper_funcs: bindings::drm_plane_helper_funcs {
> +            prepare_fb: None,
> +            cleanup_fb: None,
> +            begin_fb_access: None,
> +            end_fb_access: None,
> +            atomic_check: if Self::HAS_ATOMIC_CHECK {
> +                Some(atomic_check_callback::<Self>)
> +            } else {
> +                None
> +            },
> +            atomic_update: if Self::HAS_ATOMIC_UPDATE {
> +                Some(atomic_update_callback::<Self>)
> +            } else {
> +                None
> +            },
> +            atomic_enable: None,
> +            atomic_disable: None,
> +            atomic_async_check: None,
> +            atomic_async_update: None,
> +            panic_flush: None,
> +            get_scanout_buffer: None,
> +        },
> +    };
> +
> +    /// The type to pass to the `args` field of
> [`UnregisteredPlane::new`].
> +    ///
> +    /// This type will be made available in in the `args` argument
> of [`Self::new`]. Drivers which
> +    /// don't need this can simply pass [`()`] here.
> +    type Args;
> +
> +    /// The parent [`KmsDriver`] implementation.
> +    type Driver: KmsDriver;
> +
> +    /// The [`DriverPlaneState`] implementation for this
> [`DriverPlane`].
> +    ///
> +    /// See [`DriverPlaneState`] for more info.
> +    type State: DriverPlaneState;
> +
> +    /// The constructor for creating a [`Plane`] using this
> [`DriverPlane`] implementation.
> +    ///
> +    /// Drivers may use this to instantiate their [`DriverPlane`]
> object.
> +    fn new(device: &Device<Self::Driver>, args: Self::Args) -> impl
> PinInit<Self, Error>;
> +
> +    /// The optional [`drm_plane_helper_funcs.atomic_update`] hook
> for this plane.
> +    ///
> +    /// Drivers may use this to customize the atomic update phase of
> their [`Plane`] objects. If not
> +    /// specified, this function is a no-op.
> +    ///
> +    /// [`drm_plane_helper_funcs.atomic_update`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +    fn atomic_update(_commit: PlaneAtomicCommit<'_, Self>) {
> +        build_error::build_error("This should not be reachable")
> +    }
> +
> +    /// The optional [`drm_plane_helper_funcs.atomic_check`] hook
> for this plane.
> +    ///
> +    /// Drivers may use this to customize the atomic check phase of
> their [`Plane`] objects. The
> +    /// result of this function determines whether the atomic check
> passed or failed.
> +    ///
> +    /// [`drm_plane_helper_funcs.atomic_check`]:
> srctree/include/drm/drm_modeset_helper_vtables.h
> +    fn atomic_check(_check: PlaneAtomicCheck<'_, Self>) -> Result {
> +        build_error::build_error("This should not be reachable")
> +    }
> +}
> +
> +/// The generated C vtable for a [`DriverPlane`].
> +///
> +/// This type is created internally by DRM.
> +pub struct DriverPlaneOps {
> +    funcs: bindings::drm_plane_funcs,
> +    helper_funcs: bindings::drm_plane_helper_funcs,
> +}
> +
> +#[derive(Copy, Clone, Debug, PartialEq, Eq)]
> +#[repr(u32)]
> +/// An enumerator describing a type of [`Plane`].
> +///
> +/// This is mainly just relevant for DRM legacy drivers.
> +///
> +/// # Invariants
> +///
> +/// This type is identical to [`enum drm_plane_type`].
> +///
> +/// [`enum drm_plane_type`]: srctree/include/drm/drm_plane.h
> +pub enum Type {
> +    /// Overlay planes represent all non-primary, non-cursor planes.
> Some drivers refer to these
> +    /// types of planes as "sprites" internally.
> +    Overlay = bindings::drm_plane_type_DRM_PLANE_TYPE_OVERLAY,
> +
> +    /// A primary plane attached to a CRTC that is the most likely
> to be able to light up the CRTC
> +    /// when no scaling/cropping is used, and the plane covers the
> whole CRTC.
> +    Primary = bindings::drm_plane_type_DRM_PLANE_TYPE_PRIMARY,
> +
> +    /// A cursor plane attached to a CRTC that is more likely to be
> enabled when no scaling/cropping
> +    /// is used, and the framebuffer has the size indicated by
> [`ModeConfigInfo::max_cursor`].
> +    ///
> +    /// [`ModeConfigInfo::max_cursor`]:
> crate::drm::kms::ModeConfigInfo
> +    Cursor = bindings::drm_plane_type_DRM_PLANE_TYPE_CURSOR,
> +}
> +
> +/// The main interface for a [`struct drm_plane`].
> +///
> +/// This type is the main interface for dealing with DRM planes. In
> addition, it also allows
> +/// immutable access to whatever private data is contained within an
> implementor's [`DriverPlane`]
> +/// type.
> +///
> +/// # Invariants
> +///
> +/// - `plane` and `inner` are initialized for as long as this object
> is made available to users.
> +/// - The data layout of this structure begins with [`struct
> drm_plane`].
> +/// - The atomic state for this type can always be assumed to be of
> type [`PlaneState<T::State>`].
> +///
> +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h
> +#[repr(C)]
> +#[pin_data]
> +pub struct Plane<T: DriverPlane> {
> +    /// The FFI drm_plane object
> +    plane: Opaque<bindings::drm_plane>,
> +    /// The driver's private inner data
> +    #[pin]
> +    inner: T,
> +    #[pin]
> +    _p: PhantomPinned,
> +}
> +
> +impl<T: DriverPlane> Sealed for Plane<T> {}
> +
> +impl<T: DriverPlane> Deref for Plane<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.inner
> +    }
> +}
> +
> +// SAFETY: `funcs` is initialized when the plane is allocated
> +unsafe impl<T: DriverPlane> ModeObjectVtable for Plane<T> {
> +    type Vtable = bindings::drm_plane_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        // SAFETY: `as_raw()` always returns a valid plane pointer
> +        unsafe { *self.as_raw() }.funcs
> +    }
> +}
> +
> +impl<T: DriverPlane> Plane<T> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <'a, D>(&'a OpaquePlane<D>) -> &'a Self;
> +        use
> +            T as DriverPlane,
> +            D as KmsDriver<Plane = ...>
> +    }
> +}
> +
> +/// A [`Plane`] that has not yet been registered with userspace.
> +///
> +/// KMS registration is single-threaded, so this object is not
> thread-safe.
> +///
> +/// # Invariants
> +///
> +/// - This object can only exist before its respective KMS device
> has been registered.
> +/// - Otherwise, it inherits all invariants of [`Plane`] and has an
> identical data layout.
> +pub struct UnregisteredPlane<T: DriverPlane>(Plane<T>,
> NotThreadSafe);
> +
> +// SAFETY: We share the invariants of `Plane`
> +unsafe impl<T: DriverPlane> AsRawPlane for UnregisteredPlane<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_plane {
> +        self.0.as_raw()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane) -> &'a
> Self {
> +        // SAFETY: This is another from_raw() call, so this function
> shares the same safety contract
> +        let plane = unsafe { Plane::<T>::from_raw(ptr) };
> +
> +        // SAFETY: Our data layout is identical via our type
> invariants.
> +        unsafe { mem::transmute(plane) }
> +    }
> +}
> +
> +impl<T: DriverPlane> Deref for UnregisteredPlane<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.0.inner
> +    }
> +}
> +
> +impl<T: DriverPlane> UnregisteredPlane<T> {
> +    /// Construct a new [`UnregisteredPlane`].
> +    ///
> +    /// A driver may use this from their
> [`KmsDriver::create_objects`] callback in order to
> +    /// construct new [`UnregisteredPlane`] objects.
> +    ///
> +    /// [`KmsDriver::create_objects`]:
> kernel::drm::kms::KmsDriver::create_objects
> +    pub fn new<'a, 'b: 'a>(
> +        dev: &'a NewKmsDevice<'a, T::Driver, Probing>,
> +        possible_crtcs: u32,
> +        formats: &[u32],
> +        format_modifiers: Option<&[u64]>,
> +        type_: Type,
> +        name: Option<&CStr>,
> +        args: T::Args,
> +    ) -> Result<&'b Self> {
> +        let this: Pin<KBox<Plane<T>>> = KBox::try_pin_init(
> +            try_pin_init!(Plane {
> +                plane: Opaque::new(bindings::drm_plane {
> +                    helper_private: &T::OPS.helper_funcs,
> +                    ..Default::default()
> +                }),
> +                inner <- T::new(dev, args),
> +                _p: PhantomPinned
> +            }),
> +            GFP_KERNEL,
> +        )?;
> +
> +        // TODO: Move this over to using collect() someday
> +        // Create a modifiers array with the sentinel for passing to
> DRM
> +        let format_modifiers_raw;
> +        if let Some(modifiers) = format_modifiers {
> +            let mut raw = KVec::with_capacity(modifiers.len() + 1,
> GFP_KERNEL)?;
> +            for modifier in modifiers {
> +                raw.push(*modifier, GFP_KERNEL)?;
> +            }
> +            raw.push(FORMAT_MOD_INVALID, GFP_KERNEL)?;
> +
> +            format_modifiers_raw = Some(raw);
> +        } else {
> +            format_modifiers_raw = None;
> +        }
> +
> +        // SAFETY:
> +        // - `dev` handles destroying the plane, and thus will
> outlive us and always be valid.
> +        // - We just allocated `this`, and we won't move it since
> it's pinned
> +        // - We just allocated the `format_modifiers_raw` vec and
> added the sentinel DRM expects
> +        //   above
> +        // - `drm_universal_plane_init` will memcpy() the following
> parameters into its own storage,
> +        //   so it's safe for them to become inaccessible after this
> call returns:
> +        //   - `formats`
> +        //   - `format_modifiers_raw`
> +        //   - `name`
> +        // - `type_` is equivalent to `drm_plane_type` via its type
> invariants.
> +        to_result(unsafe {
> +            bindings::drm_universal_plane_init(
> +                dev.as_raw(),
> +                this.as_raw(),
> +                possible_crtcs,
> +                &T::OPS.funcs,
> +                formats.as_ptr(),
> +                formats.len() as _,
> +                format_modifiers_raw.map_or(null(), |f| f.as_ptr()),
> +                type_ as _,
> +                name.map_or(null(), |n| n.as_char_ptr()),
> +            )
> +        })?;
> +
> +        // SAFETY: We don't move anything
> +        let this = unsafe { Pin::into_inner_unchecked(this) };
> +
> +        // We'll re-assemble the box in plane_destroy_callback()
> +        let this = KBox::into_raw(this);
> +
> +        // UnregisteredPlane has an equivalent data layout
> +        let this: *mut Self = this.cast();
> +
> +        // SAFETY: We just allocated the plane above, so this
> pointer must be valid
> +        Ok(unsafe { &*this })
> +    }
> +}
> +
> +/// A trait implemented by any type that acts as a [`struct
> drm_plane`] interface.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// # Safety
> +///
> +/// [`as_raw()`] must always return a valid pointer to an
> initialized [`struct drm_plane`].
> +///
> +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h
> +/// [`as_raw()`]: AsRawPlane::as_raw()
> +pub unsafe trait AsRawPlane {
> +    /// Return the raw `bindings::drm_plane` for this DRM plane.
> +    ///
> +    /// Drivers should never use this directly.
> +    fn as_raw(&self) -> *mut bindings::drm_plane;
> +
> +    /// Convert a raw `bindings::drm_plane` pointer into an object
> of this type.
> +    ///
> +    /// # Safety
> +    ///
> +    /// Callers promise that `ptr` points to a valid instance of
> this type
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane) -> &'a
> Self;
> +}
> +
> +// SAFETY:
> +// - Via our type variants our data layout starts with `drm_plane`
> +// - Since we don't expose `plane` to users before it has been
> initialized, this and our data
> +//   layout ensure that `as_raw()` always returns a valid pointer to
> a `drm_plane`.
> +unsafe impl<T: DriverPlane> AsRawPlane for Plane<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_plane {
> +        self.plane.get()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane) -> &'a
> Self {
> +        // Our data layout start with `bindings::drm_plane`.
> +        let ptr: *mut Self = ptr.cast();
> +
> +        // SAFETY: Our safety contract requires that `ptr` point to
> a valid intance of `Self`.
> +        unsafe { &*ptr }
> +    }
> +}
> +
> +// SAFETY: We only expose this object to users directly after
> KmsDriver::create_objects has been
> +// called.
> +unsafe impl<T: DriverPlane> ModesettablePlane for Plane<T> {
> +    type State = PlaneState<T::State>;
> +}
> +
> +// SAFETY: We don't expose Plane<T> to users before `base` is
> initialized in ::new(), so
> +// `raw_mode_obj` always returns a valid pointer to a
> bindings::drm_mode_object.
> +unsafe impl<T: DriverPlane> ModeObject for Plane<T> {
> +    type Driver = T::Driver;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: DRM planes exist for as long as the device does,
> so this pointer is always valid
> +        unsafe { Device::from_raw((*self.as_raw()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose DRM planes to users before `base`
> is initialized
> +        unsafe { &raw mut (*self.as_raw()).base }
> +    }
> +}
> +
> +// SAFETY: Planes do not have a refcount
> +unsafe impl<T: DriverPlane> StaticModeObject for Plane<T> {}
> +
> +// SAFETY: Our interface is thread-safe.
> +unsafe impl<T: DriverPlane> Send for Plane<T> {}
> +
> +// SAFETY: Our interface is thread-safe.
> +unsafe impl<T: DriverPlane> Sync for Plane<T> {}
> +
> +/// A supertrait of [`AsRawPlane`] for [`struct drm_plane`]
> interfaces that can perform modesets.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// # Safety
> +///
> +/// Any object implementing this trait must only be made directly
> available to the user after
> +/// [`create_objects`] has completed.
> +///
> +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h
> +/// [`create_objects`]: KmsDriver::create_objects
> +pub unsafe trait ModesettablePlane: AsRawPlane {
> +    /// The type that should be returned for a plane state acquired
> using this plane interface
> +    type State: FromRawPlaneState;
> +}
> +
> +/// Common methods available on any type which implements
> [`AsRawPlane`].
> +///
> +/// This is implemented internally by DRM, and provides many of the
> basic methods for working with
> +/// planes.
> +pub trait RawPlane: AsRawPlane {
> +    /// Return the index of this DRM plane
> +    #[inline]
> +    fn index(&self) -> u32 {
> +        // SAFETY:
> +        // - The index is initialized by the time we expose planes
> to users, and does not change
> +        //   throughout its lifetime
> +        // - `.as_raw()` always returns a valid poiinter.
> +        unsafe { *self.as_raw() }.index
> +    }
> +
> +    /// Return the index of this DRM plane in the form of a bitmask
> +    #[inline]
> +    fn mask(&self) -> u32 {
> +        1 << self.index()
> +    }
> +}
> +impl<T: AsRawPlane> RawPlane for T {}
> +
> +/// A [`struct drm_plane`] without a known [`DriverPlane`]
> implementation.
> +///
> +/// This is mainly for situations where our bindings can't infer the
> [`DriverPlane`] implementation
> +/// for a [`struct drm_plane`] automatically. It is identical to
> [`Plane`], except that it does not
> +/// provide access to the driver's private data.
> +///
> +/// It may be upcasted to a full [`Plane`] using
> [`Plane::from_opaque`] or
> +/// [`Plane::try_from_opaque`].
> +///
> +/// # Invariants
> +///
> +/// - `plane` is initialized for as long as this object is made
> available to users.
> +/// - The data layout of this structure is equivalent to [`struct
> drm_plane`].
> +///
> +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h
> +#[repr(transparent)]
> +pub struct OpaquePlane<T: KmsDriver> {
> +    plane: Opaque<bindings::drm_plane>,
> +    _p: PhantomData<T>,
> +}
> +
> +impl<T: KmsDriver> Sealed for OpaquePlane<T> {}
> +
> +// SAFETY:
> +// * Via our type variants our data layout is identical to
> `drm_plane`
> +// * Since we don't expose `plane` to users before it has been
> initialized, this and our data
> +//   layout ensure that `as_raw()` always returns a valid pointer to
> a `drm_plane`.
> +unsafe impl<T: KmsDriver> AsRawPlane for OpaquePlane<T> {
> +    fn as_raw(&self) -> *mut bindings::drm_plane {
> +        self.plane.get()
> +    }
> +
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane) -> &'a
> Self {
> +        // SAFETY: Our data layout is identical to
> `bindings::drm_plane`
> +        unsafe { &*ptr.cast() }
> +    }
> +}
> +
> +// SAFETY: We only expose this object to users directly after
> KmsDriver::create_objects has been
> +// called.
> +unsafe impl<T: KmsDriver> ModesettablePlane for OpaquePlane<T> {
> +    type State = OpaquePlaneState<T>;
> +}
> +
> +// SAFETY: We don't expose OpaquePlane<T> to users before `base` is
> initialized in
> +// Plane::<T>::new(), so `raw_mode_obj` always returns a valid
> pointer to a
> +// bindings::drm_mode_object.
> +unsafe impl<T: KmsDriver> ModeObject for OpaquePlane<T> {
> +    type Driver = T;
> +
> +    fn drm_dev(&self) -> &Device<Self::Driver> {
> +        // SAFETY: DRM planes exist for as long as the device does,
> so this pointer is always valid
> +        unsafe { Device::from_raw((*self.as_raw()).dev) }
> +    }
> +
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object {
> +        // SAFETY: We don't expose DRM planes to users before `base`
> is initialized
> +        unsafe { &raw mut (*self.as_raw()).base }
> +    }
> +}
> +
> +// SAFETY: Planes do not have a refcount
> +unsafe impl<T: KmsDriver> StaticModeObject for OpaquePlane<T> {}
> +
> +// SAFETY: `funcs` is initialized when the plane is allocated
> +unsafe impl<T: KmsDriver> ModeObjectVtable for OpaquePlane<T> {
> +    type Vtable = bindings::drm_plane_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        // SAFETY: `as_raw()` always returns a valid pointer to a
> plane
> +        unsafe { *self.as_raw() }.funcs
> +    }
> +}
> +
> +// SAFETY: Our plane interfaces are guaranteed to be thread-safe
> +unsafe impl<T: KmsDriver> Send for OpaquePlane<T> {}
> +unsafe impl<T: KmsDriver> Sync for OpaquePlane<T> {}
> +
> +/// A trait implemented by any type which can produce a reference to
> a [`struct drm_plane_state`].
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
> +pub trait AsRawPlaneState: private::AsRawPlaneState {
> +    /// The type that this plane state interface returns to
> represent the parent DRM plane
> +    type Plane: ModesettablePlane;
> +}
> +
> +pub(crate) mod private {
> +    /// Trait for retrieving references to the base plane state
> contained within any plane state
> +    /// compatible type
> +    #[allow(unreachable_pub)]
> +    pub trait AsRawPlaneState {
> +        /// Return an immutable reference to the raw plane state
> +        fn as_raw(&self) -> &bindings::drm_plane_state;
> +
> +        /// Get a mutable reference to the raw
> `bindings::drm_plane_state` contained within this
> +        /// type.
> +        ///
> +        /// # Safety
> +        ///
> +        /// The caller promises this mutable reference will not be
> used to modify any contents of
> +        /// `bindings::drm_plane_state` which DRM would consider to
> be static - like the backpointer
> +        /// to the DRM plane that owns this state. This also means
> the mutable reference should
> +        /// never be exposed outside of this crate.
> +        unsafe fn as_raw_mut(&mut self) -> &mut
> bindings::drm_plane_state;
> +    }
> +}
> +
> +pub(crate) use private::AsRawPlaneState as AsRawPlaneStatePrivate;
> +
> +/// A trait implemented for any type which can be constructed
> directly from a
> +/// [`struct drm_plane_state`] pointer.
> +///
> +/// This is implemented internally by DRM.
> +///
> +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
> +pub trait FromRawPlaneState: AsRawPlaneState {
> +    /// Get an immutable reference to this type from the given raw
> [`struct drm_plane_state`]
> +    /// pointer.
> +    ///
> +    /// # Safety
> +    ///
> +    /// - The caller guarantees `ptr` is contained within a valid
> instance of `Self`
> +    /// - The caller guarantees that `ptr` cannot not be modified
> for the lifetime of `'a`.
> +    ///
> +    /// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
> +    unsafe fn from_raw<'a>(ptr: *const bindings::drm_plane_state) ->
> &'a Self;
> +
> +    /// Get a mutable reference to this type from the given raw
> [`struct drm_plane_state`] pointer.
> +    ///
> +    /// # Safety
> +    ///
> +    /// - The caller guarantees that `ptr` is contained within a
> valid instance of `Self`
> +    /// - The caller guarantees that `ptr` cannot have any other
> references taken out for the
> +    ///   lifetime of `'a`.
> +    ///
> +    /// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
> +    unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_plane_state)
> -> &'a mut Self;
> +}
> +
> +/// Common methods available on any type which implements
> [`AsRawPlane`].
> +///
> +/// This is implemented internally by DRM, and provides many of the
> basic methods for working with
> +/// the atomic state of [`Plane`]s.
> +pub trait RawPlaneState: AsRawPlaneState {
> +    /// Return the plane that this plane state belongs to.
> +    fn plane(&self) -> &Self::Plane {
> +        // SAFETY: The index is initialized by the time we expose
> Plane objects to users, and is
> +        // invariant throughout the lifetime of the Plane
> +        unsafe { Self::Plane::from_raw(self.as_raw().plane) }
> +    }
> +
> +    /// Return the current [`OpaqueCrtc`] assigned to this plane, if
> there is one.
> +    fn crtc<'a, 'b: 'a, D>(&'a self) -> Option<&'b OpaqueCrtc<D>>
> +    where
> +        Self::Plane: ModeObject<Driver = D>,
> +        D: KmsDriver,
> +    {
> +        // SAFETY: This cast is guaranteed safe by `OpaqueCrtc`s
> invariants.
> +        NonNull::new(self.as_raw().crtc).map(|c| unsafe {
> OpaqueCrtc::from_raw(c.as_ptr()) })
> +    }
> +
> +    /// Run the atomic check helper for this plane and the given
> CRTC state.
> +    fn atomic_helper_check<S, D>(
> +        &mut self,
> +        crtc_state: &CrtcStateMutator<'_, S>,
> +        can_position: bool,
> +        can_update_disabled: bool,
> +    ) -> Result
> +    where
> +        D: KmsDriver,
> +        S: FromRawCrtcState,
> +        S::Crtc: ModesettableCrtc + ModeObject<Driver = D>,
> +        Self::Plane: ModeObject<Driver = D>,
> +    {
> +        // SAFETY: We're passing the mutable reference from
> `self.as_raw_mut()` directly to DRM,
> +        // which is safe.
> +        to_result(unsafe {
> +            bindings::drm_atomic_helper_check_plane_state(
> +                self.as_raw_mut(),
> +                crtc_state.as_raw(),
> +                bindings::DRM_PLANE_NO_SCALING as _, // TODO: add
> parameters for scaling
> +                bindings::DRM_PLANE_NO_SCALING as _,
> +                can_position,
> +                can_update_disabled,
> +            )
> +        })
> +    }
> +
> +    /// Return the framebuffer currently set for this plane state
> +    #[inline]
> +    fn framebuffer<D>(&self) -> Option<&Framebuffer<D>>
> +    where
> +        Self::Plane: ModeObject<Driver = D>,
> +        D: KmsDriver,
> +    {
> +        // SAFETY: The layout of Framebuffer<T> is identical to `fb`
> +        unsafe {
> +            self.as_raw()
> +                .fb
> +                .as_ref()
> +                .map(|fb| Framebuffer::from_raw(fb))
> +        }
> +    }
> +}
> +impl<T: AsRawPlaneState + ?Sized> RawPlaneState for T {}
> +
> +/// The main interface for a [`struct drm_plane_state`].
> +///
> +/// This type is the main interface for dealing with the atomic
> state of DRM planes. In addition, it
> +/// allows access to whatever private data is contained within an
> implementor's [`DriverPlaneState`]
> +/// type.
> +///
> +/// # Invariants
> +///
> +/// - The DRM C API and our interface guarantees that only the user
> has mutable access to `state`,
> +///   up until [`drm_atomic_helper_commit_hw_done`] is called.
> Therefore, `plane` follows rust's
> +///   data aliasing rules and does not need to be behind an
> [`Opaque`] type.
> +/// - `state` and `inner` initialized for as long as this object is
> exposed to users.
> +/// - The data layout of this structure begins with [`struct
> drm_plane_state`].
> +/// - The plane for this atomic state can always be assumed to be of
> type [`Plane<T::Plane>`].
> +///
> +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
> +/// [`drm_atomic_helper_commit_hw_done`]:
> srctree/include/drm/drm_atomic_helper.h
> +#[derive(Default)]
> +#[repr(C)]
> +pub struct PlaneState<T: DriverPlaneState> {
> +    state: bindings::drm_plane_state,
> +    inner: T,
> +}
> +
> +/// The main trait for implementing the [`struct drm_plane_state`]
> API for a [`Plane`].
> +///
> +/// A driver may store driver-private data within the implementor's
> type, which will be available
> +/// when using a full typed [`PlaneState`] object.
> +///
> +/// # Invariants
> +///
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_plane`] pointers are contained within a
> [`Plane<Self::Plane>`].
> +/// - Any C FFI callbacks generated using this trait are guaranteed
> that passed-in
> +///   [`struct drm_plane_state`] pointers are contained within a
> [`PlaneState<Self>`].
> +///
> +/// [`struct drm_plane`]: srctree/include/drm_plane.h
> +/// [`struct drm_plane_state`]: srctree/include/drm_plane.h
> +pub trait DriverPlaneState: Clone + Default + Sized {
> +    /// The type for this driver's drm_plane implementation
> +    type Plane: DriverPlane;
> +}
> +
> +impl<T: DriverPlaneState> Sealed for PlaneState<T> {}
> +
> +impl<T: DriverPlaneState> AsRawPlaneState for PlaneState<T> {
> +    type Plane = Plane<T::Plane>;
> +}
> +
> +impl<T: DriverPlaneState> private::AsRawPlaneState for PlaneState<T>
> {
> +    fn as_raw(&self) -> &bindings::drm_plane_state {
> +        &self.state
> +    }
> +
> +    unsafe fn as_raw_mut(&mut self) -> &mut
> bindings::drm_plane_state {
> +        &mut self.state
> +    }
> +}
> +
> +impl<T: DriverPlaneState> FromRawPlaneState for PlaneState<T> {
> +    unsafe fn from_raw<'a>(ptr: *const bindings::drm_plane_state) ->
> &'a Self {
> +        // Our data layout starts with `bindings::drm_plane_state`.
> +        let ptr: *const Self = ptr.cast();
> +
> +        // SAFETY:
> +        // - Our safety contract requires that `ptr` be contained
> within `Self`.
> +        // - Our safety contract requires the caller ensure that it
> is safe for us to take an
> +        //   immutable reference.
> +        unsafe { &*ptr }
> +    }
> +
> +    unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_plane_state)
> -> &'a mut Self {
> +        // Our data layout starts with `bindings::drm_plane_state`.
> +        let ptr: *mut Self = ptr.cast();
> +
> +        // SAFETY:
> +        // - Our safety contract requires that `ptr` be contained
> within `Self`.
> +        // - Our safety contract requires the caller ensure it is
> safe for us to take a mutable
> +        //   reference.
> +        unsafe { &mut *ptr }
> +    }
> +}
> +
> +impl<T: DriverPlaneState> Deref for PlaneState<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.inner
> +    }
> +}
> +
> +impl<T: DriverPlaneState> DerefMut for PlaneState<T> {
> +    fn deref_mut(&mut self) -> &mut Self::Target {
> +        &mut self.inner
> +    }
> +}
> +
> +// SAFETY: Shares the safety guarantee of Plane<T>'s vtable impl
> +unsafe impl<T: DriverPlaneState> ModeObjectVtable for PlaneState<T>
> {
> +    type Vtable = bindings::drm_plane_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.plane().vtable()
> +    }
> +}
> +
> +impl<T: DriverPlaneState> PlaneState<T> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <'a, D, P>(&'a OpaquePlaneState<D>) -> &'a Self
> +        where
> +            T: DriverPlaneState<Plane = P>;
> +        use
> +            P as DriverPlane,
> +            D as KmsDriver<Plane = ...>
> +    }
> +}
> +
> +/// A [`struct drm_plane_state`] without a known
> [`DriverPlaneState`] implementation.
> +///
> +/// This is mainly for situations where our bindings can't infer the
> [`DriverPlaneState`]
> +/// implementation for a [`struct drm_plane_state`] automatically.
> It is identical to [`Plane`],
> +/// except that it does not provide access to the driver's private
> data.
> +///
> +/// # Invariants
> +///
> +/// - The DRM C API and our interface guarantees that only the user
> has mutable access to `state`,
> +///   up until [`drm_atomic_helper_commit_hw_done`] is called.
> Therefore, `plane` follows rust's
> +///   data aliasing rules and does not need to be behind an
> [`Opaque`] type.
> +/// - `state` is initialized for as long as this object is exposed
> to users.
> +/// - The data layout of this structure is identical to [`struct
> drm_plane_state`].
> +///
> +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h
> +/// [`drm_atomic_helper_commit_hw_done`]:
> srctree/include/drm/drm_atomic_helper.h
> +#[repr(transparent)]
> +pub struct OpaquePlaneState<T: KmsDriver> {
> +    state: bindings::drm_plane_state,
> +    _p: PhantomData<T>,
> +}
> +
> +impl<T: KmsDriver> AsRawPlaneState for OpaquePlaneState<T> {
> +    type Plane = OpaquePlane<T>;
> +}
> +
> +impl<T: KmsDriver> private::AsRawPlaneState for OpaquePlaneState<T>
> {
> +    fn as_raw(&self) -> &bindings::drm_plane_state {
> +        &self.state
> +    }
> +
> +    unsafe fn as_raw_mut(&mut self) -> &mut
> bindings::drm_plane_state {
> +        &mut self.state
> +    }
> +}
> +
> +impl<T: KmsDriver> FromRawPlaneState for OpaquePlaneState<T> {
> +    unsafe fn from_raw<'a>(ptr: *const bindings::drm_plane_state) ->
> &'a Self {
> +        // SAFETY: Our data layout is identical to `ptr`
> +        unsafe { &*ptr.cast() }
> +    }
> +
> +    unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_plane_state)
> -> &'a mut Self {
> +        // SAFETY: Our data layout is identical to `ptr`
> +        unsafe { &mut *ptr.cast() }
> +    }
> +}
> +
> +// SAFETY: Shares the safety guarantee of OpaquePlane<T>'s vtable
> impl
> +unsafe impl<T: KmsDriver> ModeObjectVtable for OpaquePlaneState<T> {
> +    type Vtable = bindings::drm_plane_funcs;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.plane().vtable()
> +    }
> +}
> +
> +/// An interface for mutating a [`Plane`]s atomic state.
> +///
> +/// This type is typically returned by an [`AtomicStateMutator`]
> within contexts where it is
> +/// possible to safely mutate a plane's state. In order to uphold
> rust's data-aliasing rules, only
> +/// [`PlaneStateMutator`] may exist at a time.
> +pub struct PlaneStateMutator<'a, T: FromRawPlaneState> {
> +    state: &'a mut T,
> +    mask: &'a Cell<u32>,
> +}
> +
> +impl<'a, T: FromRawPlaneState> PlaneStateMutator<'a, T> {
> +    pub(super) fn new<D: KmsDriver>(
> +        mutator: &'a AtomicStateMutator<D>,
> +        state: NonNull<bindings::drm_plane_state>,
> +    ) -> Option<Self> {
> +        // SAFETY: `plane` is invariant throughout the lifetime of
> the atomic state, is
> +        // initialized by this point, and we're guaranteed it is of
> type `AsRawPlane` by type
> +        // invariance
> +        let plane = unsafe {
> T::Plane::from_raw((*state.as_ptr()).plane) };
> +        let plane_mask = plane.mask();
> +        let borrowed_mask = mutator.borrowed_planes.get();
> +
> +        if borrowed_mask & plane_mask == 0 {
> +            mutator.borrowed_planes.set(borrowed_mask | plane_mask);
> +            Some(Self {
> +                mask: &mutator.borrowed_planes,
> +                // SAFETY: We're guaranteed `state` is of
> `FromRawPlaneState` by type invariance,
> +                // and we just confirmed by checking
> `borrowed_planes` that no other mutable borrows
> +                // have been taken out for `state`
> +                state: unsafe { T::from_raw_mut(state.as_ptr()) },
> +            })
> +        } else {
> +            None
> +        }
> +    }
> +}
> +
> +impl<'a, T: FromRawPlaneState> Drop for PlaneStateMutator<'a, T> {
> +    fn drop(&mut self) {
> +        let mask = self.state.plane().mask();
> +        self.mask.set(self.mask.get() & !mask);
> +    }
> +}
> +
> +impl<'a, T: FromRawPlaneState> AsRawPlaneState for
> PlaneStateMutator<'a, T> {
> +    type Plane = T::Plane;
> +}
> +
> +impl<'a, T: FromRawPlaneState> private::AsRawPlaneState for
> PlaneStateMutator<'a, T> {
> +    fn as_raw(&self) -> &bindings::drm_plane_state {
> +        self.state.as_raw()
> +    }
> +
> +    unsafe fn as_raw_mut(&mut self) -> &mut
> bindings::drm_plane_state {
> +        // SAFETY: This function is bound by the same safety
> contract as `self.inner.as_raw_mut()`
> +        unsafe { self.state.as_raw_mut() }
> +    }
> +}
> +
> +impl<'a, T: FromRawPlaneState> Sealed for PlaneStateMutator<'a, T>
> {}
> +
> +impl<'a, T: DriverPlaneState> Deref for PlaneStateMutator<'a,
> PlaneState<T>> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.state.inner
> +    }
> +}
> +
> +impl<'a, T: DriverPlaneState> DerefMut for PlaneStateMutator<'a,
> PlaneState<T>> {
> +    fn deref_mut(&mut self) -> &mut Self::Target {
> +        &mut self.state.inner
> +    }
> +}
> +
> +// SAFETY: Shares the safety guarantees of `T`'s ModeObjectVtable
> impl
> +unsafe impl<'a, T: FromRawPlaneState> ModeObjectVtable for
> PlaneStateMutator<'a, T>
> +where
> +    T: FromRawPlaneState + ModeObjectVtable,
> +{
> +    type Vtable = T::Vtable;
> +
> +    fn vtable(&self) -> *const Self::Vtable {
> +        self.state.vtable()
> +    }
> +}
> +
> +impl<'a, T: DriverPlaneState> PlaneStateMutator<'a, PlaneState<T>> {
> +    super::impl_from_opaque_mode_obj! {
> +        fn <D, P>(PlaneStateMutator<'a, OpaquePlaneState<D>>) ->
> Self
> +        where
> +            T: DriverPlaneState<Plane = P>;
> +        use
> +            P as DriverPlane,
> +            D as KmsDriver<Plane = ...>
> +    }
> +}
> +
> +/// A token provided during [`atomic_check`] callbacks for accessing
> the plane, atomic state, and
> +/// the old and new states of the plane.
> +///
> +/// [`atomic_check`]: DriverPlane::atomic_check
> +pub struct PlaneAtomicCheck<'a, T: DriverPlane> {
> +    state: &'a AtomicStateComposer<T::Driver>,
> +    plane: &'a Plane<T>,
> +}
> +
> +impl<'a, T: DriverPlane> PlaneAtomicCheck<'a, T> {
> +    impl_atomic_state_token_ops!(
> +        PlaneAtomicCheck,
> +        AtomicStateComposer,
> +        Plane,
> +        use <'a, T>
> +    );
> +}
> +
> +/// A token provided to [`DriverPlane`] callbacks during the atomic
> commit phase for accessing the
> +/// plane, atomic state, new and old states of the plane.
> +///
> +/// # Invariants
> +///
> +/// This token is proof that the old and new atomic state of `plane`
> are present in `state` and do
> +/// not have any mutators taken out.
> +pub struct PlaneAtomicCommit<'a, T: DriverPlane> {
> +    state: &'a AtomicStateMutator<T::Driver>,
> +    plane: &'a Plane<T>,
> +}
> +
> +impl<'a, T: DriverPlane> PlaneAtomicCommit<'a, T> {
> +    impl_atomic_state_token_ops!(
> +        PlaneAtomicCommit,
> +        AtomicStateMutator,
> +        Plane,
> +        use <'a, T>
> +    );
> +}
> +
> +unsafe extern "C" fn plane_destroy_callback<T: DriverPlane>(plane:
> *mut bindings::drm_plane) {
> +    // SAFETY: DRM guarantees that `plane` points to a valid
> initialized `drm_plane`.
> +    unsafe { bindings::drm_plane_cleanup(plane) };
> +
> +    // SAFETY:
> +    // - DRM guarantees we are now the only one with access to this
> [`drm_plane`].
> +    // - This cast is safe via `DriverPlane`s type invariants.
> +    drop(unsafe { KBox::from_raw(plane as *mut Plane<T>) });
> +}
> +
> +unsafe extern "C" fn atomic_duplicate_state_callback<T:
> DriverPlaneState>(
> +    plane: *mut bindings::drm_plane,
> +) -> *mut bindings::drm_plane_state {
> +    // SAFETY: DRM guarantees that `plane` points to a valid
> initialized `drm_plane`.
> +    let state = unsafe { (*plane).state };
> +    if state.is_null() {
> +        return null_mut();
> +    }
> +
> +    // SAFETY: This cast is safe via `DriverPlaneState`s type
> invariants.
> +    let state = unsafe { PlaneState::<T>::from_raw(state) };
> +
> +    let new: Result<KBox<_>> = KBox::try_init(
> +        try_init!(PlaneState {
> +            inner: state.inner.clone(),
> +            state: bindings::drm_plane_state {
> +                ..Default::default()
> +            },
> +        }),
> +        GFP_KERNEL,
> +    );
> +
> +    if let Ok(mut new) = new {
> +        // SAFETY:
> +        // - `new` provides a valid pointer to a newly allocated
> `drm_plane_state` via type
> +        //   invariants
> +        // - This initializes `new` via memcpy()
> +        unsafe {
> bindings::__drm_atomic_helper_plane_duplicate_state(plane,
> new.as_raw_mut()) };
> +
> +        KBox::into_raw(new).cast()
> +    } else {
> +        null_mut()
> +    }
> +}
> +
> +unsafe extern "C" fn atomic_destroy_state_callback<T:
> DriverPlaneState>(
> +    _plane: *mut bindings::drm_plane,
> +    state: *mut bindings::drm_plane_state,
> +) {
> +    // SAFETY: DRM guarantees that `state` points to a valid
> instance of `drm_plane_state`
> +    unsafe {
> bindings::__drm_atomic_helper_plane_destroy_state(state) };
> +
> +    // SAFETY:
> +    // * DRM guarantees we are the only one with access to this
> `drm_plane_state`
> +    // * This cast is safe via our type invariants.
> +    drop(unsafe { KBox::from_raw(state.cast::<PlaneState<T>>()) });
> +}
> +
> +unsafe extern "C" fn plane_reset_callback<T: DriverPlane>(plane:
> *mut bindings::drm_plane) {
> +    // SAFETY: DRM guarantees that `state` points to a valid
> instance of `drm_plane_state`
> +    let state = unsafe { (*plane).state };
> +    if !state.is_null() {
> +        // SAFETY:
> +        // - We're guaranteed `plane` is `Plane<T>` via type
> invariants
> +        // - We're guaranteed `state` is `PlaneState<T>` via type
> invariants.
> +        unsafe { atomic_destroy_state_callback::<T::State>(plane,
> state) }
> +
> +        // SAFETY: No special requirements here, DRM expects this to
> be NULL
> +        unsafe {
> +            (*plane).state = null_mut();
> +        }
> +    }
> +
> +    // Unfortunately, this is the best we can do at the moment as
> this FFI callback was mistakenly
> +    // presumed to be infallible :(
> +    let new =
> +        KBox::new(PlaneState::<T::State>::default(),
> GFP_KERNEL).expect("Blame the API, sorry!");
> +
> +    // DRM takes ownership of the state from here, resets it, and
> then assigns it to the plane
> +    // SAFETY:
> +    // - DRM guarantees that `plane` points to a valid instance of
> `drm_plane`.
> +    // - The cast to `drm_plane_state` is safe via `PlaneState`s
> type invariants.
> +    unsafe { bindings::__drm_atomic_helper_plane_reset(plane,
> KBox::into_raw(new).cast()) };
> +}
> +
> +unsafe extern "C" fn atomic_update_callback<T: DriverPlane>(
> +    plane: *mut bindings::drm_plane,
> +    state: *mut bindings::drm_atomic_state,
> +) {
> +    // SAFETY:
> +    // - We're guaranteed `plane` is of type `Plane<T>` via type
> invariants.
> +    // - We're guaranteed by DRM that `plane` is pointing to a valid
> initialized state.
> +    let plane = unsafe { Plane::from_raw(plane) };
> +
> +    // SAFETY: DRM guarantees `state` points to a valid
> `drm_atomic_state`
> +    let state = unsafe {
> AtomicStateMutator::new(NonNull::new_unchecked(state)) };
> +
> +    // SAFETY:
> +    // - Since we're in the atomic_update callback, we're guaranteed
> by DRM that both the old and new
> +    //   plane state are resent in this atomic state.
> +    // - We just created the state mutator above, so other mutators
> cannot be taken out on the plane
> +    //   state yet.
> +    let commit = unsafe { PlaneAtomicCommit::new(plane, &state) };
> +
> +    T::atomic_update(commit);
> +}
> +
> +unsafe extern "C" fn atomic_check_callback<T: DriverPlane>(
> +    plane: *mut bindings::drm_plane,
> +    state: *mut bindings::drm_atomic_state,
> +) -> i32 {
> +    // SAFETY:
> +    // - We're guaranteed `plane` is of type `Plane<T>` via type
> invariants.
> +    // - We're guaranteed by DRM that `plane` is pointing to a valid
> initialized state.
> +    let plane = unsafe { Plane::from_raw(plane) };
> +
> +    // SAFETY: We're guaranteed by DRM that `state` points to a
> valid instance of `drm_atomic_state`
> +    // We use ManuallyDrop here since AtomicStateComposer would
> otherwise drop a owned reference to
> +    // the atomic state upon finishing this callback.
> +    let state = ManuallyDrop::new(unsafe {
> +       
> AtomicStateComposer::<T::Driver>::new(NonNull::new_unchecked(state))
> +    });
> +
> +    // SAFETY:
> +    // - Since we're in the atomic check callback, we're guaranteed
> by DRM that both the old and
> +    //   new plane state are present in this atomic state
> +    // - We just created the state composer above, so other
> composers cannot be taken out on the
> +    //   plane state yet.
> +    let check = unsafe { PlaneAtomicCheck::new(plane, &state) };
> +
> +    from_result(|| T::atomic_check(check).map(|_| 0))
> +}
> diff --git a/rust/kernel/drm/kms/vblank.rs
> b/rust/kernel/drm/kms/vblank.rs
> new file mode 100644
> index 000000000000..dc34e02e8ccb
> --- /dev/null
> +++ b/rust/kernel/drm/kms/vblank.rs
> @@ -0,0 +1,461 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! DRM KMS vblank support.
> +//!
> +//! C header:
> [`include/drm/drm_vblank.h`](srcfree/include/drm/drm_vblank.h)
> +
> +use super::{crtc::*, ModeObject, modes::*, Sealed};
> +use bindings;
> +use core::{
> +    marker::*,
> +    mem::{self, ManuallyDrop},
> +    ops::{Deref, Drop},
> +    ptr::null_mut,
> +};
> +use kernel::{
> +    drm::device::Device,
> +    error::{from_result, to_result},
> +    interrupt::LocalInterruptDisabled,
> +    prelude::*,
> +    time::Delta,
> +    types::Opaque,
> +};
> +
> +/// The main trait for a driver to implement hardware vblank support
> for a [`Crtc`].
> +///
> +/// # Invariants
> +///
> +/// C FFI callbacks generated using this trait can safely assume
> that input pointers to
> +/// [`struct drm_crtc`] are always contained within a
> [`Crtc<Self::Crtc>`].
> +///
> +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h
> +pub trait VblankSupport: Sized {
> +    /// The parent [`DriverCrtc`].
> +    type Crtc: VblankDriverCrtc<VblankImpl = Self>;
> +
> +    /// Enable vblank interrupts for this [`DriverCrtc`].
> +    fn enable_vblank(
> +        crtc: &Crtc<Self::Crtc>,
> +        vblank_guard: &VblankGuard<'_, Self::Crtc>,
> +        irq: &LocalInterruptDisabled,
> +    ) -> Result;
> +
> +    /// Disable vblank interrupts for this [`DriverCrtc`].
> +    fn disable_vblank(
> +        crtc: &Crtc<Self::Crtc>,
> +        vblank_guard: &VblankGuard<'_, Self::Crtc>,
> +        irq: &LocalInterruptDisabled,
> +    );
> +
> +    /// Retrieve the current vblank timestamp for this [`Crtc`]
> +    ///
> +    /// If this function is being called from the driver's vblank
> interrupt handler,
> +    /// `handling_vblank_irq` will be `true`.
> +    fn get_vblank_timestamp(
> +        crtc: &Crtc<Self::Crtc>,
> +        in_vblank_irq: bool,
> +    ) -> Option<VblankTimestamp>;
> +}
> +
> +/// Trait used for CRTC vblank (or lack there-of) implementations.
> Implemented internally.
> +///
> +/// Drivers interested in implementing vblank support should refer
> to [`VblankSupport`], drivers
> +/// that don't have vblank support can use [`PhantomData`].
> +pub trait VblankImpl {
> +    /// The parent [`DriverCrtc`].
> +    type Crtc: DriverCrtc<VblankImpl = Self>;
> +
> +    /// The generated [`VblankOps`].
> +    const VBLANK_OPS: VblankOps;
> +}
> +
> +/// C FFI callbacks for vblank management.
> +///
> +/// Created internally by DRM.
> +#[derive(Default)]
> +pub struct VblankOps {
> +    pub(crate) enable_vblank: Option<unsafe extern "C" fn(crtc: *mut
> bindings::drm_crtc) -> i32>,
> +    pub(crate) disable_vblank: Option<unsafe extern "C" fn(crtc:
> *mut bindings::drm_crtc)>,
> +    pub(crate) get_vblank_timestamp: Option<
> +        unsafe extern "C" fn(
> +            crtc: *mut bindings::drm_crtc,
> +            max_error: *mut i32,
> +            vblank_time: *mut bindings::ktime_t,
> +            in_vblank_irq: bool,
> +        ) -> bool,
> +    >,
> +}
> +
> +impl<T: VblankSupport> VblankImpl for T {
> +    type Crtc = T::Crtc;
> +
> +    const VBLANK_OPS: VblankOps = VblankOps {
> +        enable_vblank: Some(enable_vblank_callback::<T>),
> +        disable_vblank: Some(disable_vblank_callback::<T>),
> +        get_vblank_timestamp:
> Some(get_vblank_timestamp_callback::<T>),
> +    };
> +}
> +
> +impl<T> VblankImpl for PhantomData<T>
> +where
> +    T: DriverCrtc<VblankImpl = PhantomData<T>>,
> +{
> +    type Crtc = T;
> +
> +    const VBLANK_OPS: VblankOps = VblankOps {
> +        enable_vblank: None,
> +        disable_vblank: None,
> +        get_vblank_timestamp: None,
> +    };
> +}
> +
> +unsafe extern "C" fn enable_vblank_callback<T: VblankSupport>(
> +    crtc: *mut bindings::drm_crtc,
> +) -> i32 {
> +    // SAFETY: We're guaranteed that `crtc` is of type
> `Crtc<T::Crtc>` by type invariants.
> +    let crtc = unsafe { Crtc::<T::Crtc>::from_raw(crtc) };
> +
> +    // SAFETY: This callback happens with IRQs disabled
> +    let irq = unsafe { LocalInterruptDisabled::assume_disabled() };
> +
> +    // SAFETY: This callback happens with `vbl_lock` already held
> +    // We don't want to drop `vbl_lock` when this callback completes
> since DRM will do this for us,
> +    // so wrap the `VblankGuard` in a `ManuallyDrop`
> +    let vblank_guard = ManuallyDrop::new(unsafe {
> VblankGuard::new(crtc, irq) });
> +
> +    from_result(|| T::enable_vblank(crtc, &vblank_guard,
> irq).map(|_| 0))
> +}
> +
> +unsafe extern "C" fn disable_vblank_callback<T: VblankSupport>(crtc:
> *mut bindings::drm_crtc) {
> +    // SAFETY: We're guaranteed that `crtc` is of type
> `Crtc<T::Crtc>` by type invariants.
> +    let crtc = unsafe { Crtc::<T::Crtc>::from_raw(crtc) };
> +
> +    // SAFETY: This callback happens with IRQs disabled
> +    let irq = unsafe { LocalInterruptDisabled::assume_disabled() };
> +
> +    // SAFETY: This call happens with `vbl_lock` already held
> +    // We don't want to drop `vbl_lock` when this callback completes
> since DRM will do this for us,
> +    // so wrap the `VblankGuard` in a `ManuallyDrop`
> +    let vblank_guard = ManuallyDrop::new(unsafe {
> VblankGuard::new(crtc, irq) });
> +
> +    T::disable_vblank(crtc, &vblank_guard, irq);
> +}
> +
> +unsafe extern "C" fn get_vblank_timestamp_callback<T:
> VblankSupport>(
> +    crtc: *mut bindings::drm_crtc,
> +    max_error: *mut i32,
> +    vblank_time: *mut bindings::ktime_t,
> +    in_vblank_irq: bool,
> +) -> bool {
> +    // SAFETY: We're guaranteed `crtc` is of type `Crtc<T::Crtc>` by
> type invariance
> +    let crtc = unsafe { Crtc::<T::Crtc>::from_raw(crtc) };
> +
> +    if let Some(timestamp) = T::get_vblank_timestamp(crtc,
> in_vblank_irq) {
> +        // SAFETY: Both of these pointers are guaranteed by the C
> API to be valid
> +        unsafe {
> +            (*max_error) = timestamp.max_error;
> +            (*vblank_time) = timestamp.time.as_nanos();
> +        };
> +
> +        true
> +    } else {
> +        false
> +    }
> +}
> +
> +/// A vblank timestamp.
> +///
> +/// This type is used by [`VblankSupport::get_vblank_timestamp`] for
> the implementor to return the
> +/// current vblank timestamp for the hardware.
> +#[derive(Copy, Clone)]
> +pub struct VblankTimestamp {
> +    /// The actual vblank timestamp in nanoseconds, accuracy to
> within [`Self::max_error`]
> +    /// nanoseconds.
> +    pub time: Delta,
> +
> +    /// Maximum allowable timestamp error in nanoseconds
> +    pub max_error: i32,
> +}
> +
> +/// A trait for [`DriverCrtc`] implementations with hardware vblank
> support.
> +///
> +/// This trait is implemented internally by DRM for any
> [`DriverCrtc`] implementation that
> +/// implements [`VblankSupport`]. It is used to expose hardware-
> vblank driver exclusive methods and
> +/// data to users.
> +pub trait VblankDriverCrtc: DriverCrtc {}
> +
> +impl<T, V> VblankDriverCrtc for T
> +where
> +    T: DriverCrtc<VblankImpl = V>,
> +    V: VblankSupport<Crtc = T>,
> +{
> +}
> +
> +impl<T: VblankDriverCrtc> Crtc<T> {
> +    /// Retrieve a reference to the [`VblankCrtc`] for this
> [`Crtc`].
> +    pub(crate) fn vblank_crtc(&self) -> &VblankCrtc<T> {
> +        // SAFETY:
> +        // - The data layouts of these types are equivalent via
> `VblankCrtc`s type invariants
> +        // - We don't expose any way of calling `vblank_crtc()`
> before `drm_vblank_init()` has been
> +        //   called.
> +        unsafe { VblankCrtc::from_raw(self.get_vblank_ptr()) }
> +    }
> +
> +    /// Access vblank related infrastructure for a [`Crtc`].
> +    ///
> +    /// This function explicitly locks the device's vblank lock, and
> allows access to controlling
> +    /// the vblank configuration for this CRTC. The lock is dropped
> once [`VblankGuard`] is
> +    /// dropped.
> +    pub fn vblank_lock<'a>(&'a self, irq: &'a
> LocalInterruptDisabled) -> VblankGuard<'a, T> {
> +        // SAFETY: `vbl_lock` is initialized for as long as `Crtc`
> is available to users
> +        // INVARIANT: We just acquired `vbl_lock`, fulfilling the
> invariants of `VblankGuard`
> +        unsafe { bindings::spin_lock(&raw mut
> (*self.drm_dev().as_raw()).vbl_lock) };
> +
> +        // SAFETY: We just acquired vbl_lock above
> +        unsafe { VblankGuard::new(self, irq) }
> +    }
> +
> +    /// Trigger a vblank event on this [`Crtc`].
> +    ///
> +    /// Drivers should use this in their vblank interrupt handlers
> to update the vblank counter and
> +    /// send any signals that may be pending.
> +    ///
> +    /// Returns whether or not the vblank event was handled.
> +    #[inline]
> +    pub fn handle_vblank(&self) -> bool {
> +        // SAFETY: `as_raw()` always returns a valid pointer to an
> initialized drm_crtc.
> +        unsafe { bindings::drm_crtc_handle_vblank(self.as_raw()) }
> +    }
> +
> +    /// Forbid vblank events for a [`Crtc`].
> +    ///
> +    /// This function disables vblank events for a [`Crtc`], even if
> [`VblankRef`] objects exist.
> +    #[inline]
> +    pub fn vblank_off(&self) {
> +        // SAFETY: `as_raw()` always returns a valid pointer to an
> initialized drm_crtc.
> +        unsafe { bindings::drm_crtc_vblank_off(self.as_raw()) }
> +    }
> +
> +    /// Allow vblank events for a [`Crtc`].
> +    ///
> +    /// This function allows users to enable vblank events and
> acquire [`VblankRef`] objects again.
> +    #[inline]
> +    pub fn vblank_on(&self) {
> +        // SAFETY: `as_raw()` always returns a valid pointer to an
> initialized drm_crtc.
> +        unsafe { bindings::drm_crtc_vblank_on(self.as_raw()) }
> +    }
> +
> +    /// Enable vblank events for a [`Crtc`].
> +    ///
> +    /// Returns a [`VblankRef`] which will allow vblank events to be
> sent until it is dropped. Note
> +    /// that vblank events may still be disabled by
> [`Self::vblank_off`].
> +    #[must_use = "Vblanks are only enabled until the result from
> this function is dropped"]
> +    pub fn vblank_get(&self) -> Result<VblankRef<'_, T>> {
> +        VblankRef::new(self)
> +    }
> +}
> +
> +/// Common methods available on any [`CrtcState`] whose [`Crtc`]
> implements [`VblankSupport`].
> +///
> +/// This trait is implemented automatically by DRM for any
> [`DriverCrtc`] implementation that
> +/// implements [`VblankSupport`].
> +pub trait RawVblankCrtcState: AsRawCrtcState {
> +    /// Return the [`PendingVblankEvent`] for this CRTC state, if
> there is one.
> +    fn get_pending_vblank_event(&mut self) ->
> Option<PendingVblankEvent<'_, Self>>
> +    where
> +        Self: Sized,
> +    {
> +        // SAFETY: The driver is the only one that will ever modify
> this data, and since our
> +        // interface follows rust's data aliasing rules that means
> this is safe to read
> +        let event_ptr = unsafe { *self.as_raw() }.event;
> +
> +        (!event_ptr.is_null()).then_some(PendingVblankEvent(self))
> +    }
> +}
> +
> +impl<T, C> RawVblankCrtcState for T
> +where
> +    T: AsRawCrtcState<Crtc = Crtc<C>>,
> +    C: VblankDriverCrtc,
> +{
> +}
> +
> +/// A pending vblank event from an atomic state
> +pub struct PendingVblankEvent<'a, T: RawVblankCrtcState>(&'a mut T);
> +
> +impl<'a, T: RawVblankCrtcState> PendingVblankEvent<'a, T> {
> +    /// Send this [`PendingVblankEvent`].
> +    ///
> +    /// A [`PendingVblankEvent`] can only be sent once, so this
> function consumes the
> +    /// [`PendingVblankEvent`].
> +    pub fn send<C>(self)
> +    where
> +        T: RawVblankCrtcState<Crtc = Crtc<C>>,
> +        C: VblankDriverCrtc,
> +    {
> +        let crtc: &Crtc<C> = self.0.crtc();
> +        let event_lock = crtc.drm_dev().event_lock();
> +        let _guard = event_lock.lock();
> +
> +        // SAFETY:
> +        // - We now hold the appropriate lock to call this function
> +        // - Vblanks are enabled as proved by `vbl_ref`, as per the
> C api requirements
> +        // - Our interface is proof that `event` is non-null
> +        unsafe { bindings::drm_crtc_send_vblank_event(crtc.as_raw(),
> (*self.0.as_raw()).event) };
> +
> +        // SAFETY: The mutable reference in `self.state` is proof
> that it is safe to mutate this,
> +        // and DRM expects us to set this to NULL once we've sent
> the vblank event.
> +        unsafe { (*self.0.as_raw()).event = null_mut() };
> +    }
> +
> +    /// Arm this [`PendingVblankEvent`] to be sent later by the
> CRTC's vblank interrupt handler.
> +    ///
> +    /// A [`PendingVblankEvent`] can only be armed once, so this
> function consumes the
> +    /// [`PendingVblankEvent`]. As well, it requires a [`VblankRef`]
> so that vblank interrupts
> +    /// remain enabled until the [`PendingVblankEvent`] has been
> sent out by the driver's vblank
> +    /// interrupt handler.
> +    pub fn arm<C>(self, vbl_ref: VblankRef<'_, C>)
> +    where
> +        T: RawVblankCrtcState<Crtc = Crtc<C>>,
> +        C: VblankDriverCrtc,
> +    {
> +        let crtc: &Crtc<C> = self.0.crtc();
> +        let event_lock = crtc.drm_dev().event_lock();
> +        let _guard = event_lock.lock();
> +
> +        // SAFETY:
> +        // - We now hold the appropriate lock to call this function
> +        // - Vblanks are enabled as proved by `vbl_ref`, as per the
> C api requirements
> +        // - Our interface is proof that `event` is non-null
> +        unsafe { bindings::drm_crtc_arm_vblank_event(crtc.as_raw(),
> (*self.0.as_raw()).event) };
> +
> +        // SAFETY: The mutable reference in `self.state` is proof
> that it is safe to mutate this,
> +        // and DRM expects us to set this to NULL once we've armed
> the vblank event.
> +        unsafe { (*self.0.as_raw()).event = null_mut() };
> +
> +        // DRM took ownership of `vbl_ref` after we called
> `drm_crtc_arm_vblank_event`
> +        mem::forget(vbl_ref);
> +    }
> +}
> +
> +/// A borrowed vblank reference.
> +///
> +/// This object keeps the vblank reference count for a [`Crtc`]
> incremented for as long as it
> +/// exists, enabling vblank interrupts for said [`Crtc`] until all
> references are dropped, or
> +/// [`Crtc::vblank_off`] is called - whichever comes first.
> +pub struct VblankRef<'a, T: VblankDriverCrtc>(&'a Crtc<T>);
> +
> +impl<T: VblankDriverCrtc> Drop for VblankRef<'_, T> {
> +    fn drop(&mut self) {
> +        // SAFETY: as_raw() returns a valid pointer to an
> initialized drm_crtc
> +        unsafe { bindings::drm_crtc_vblank_put(self.0.as_raw()) };
> +    }
> +}
> +
> +impl<'a, T: VblankDriverCrtc> VblankRef<'a, T> {
> +    fn new(crtc: &'a Crtc<T>) -> Result<Self> {
> +        // SAFETY: as_raw() returns a valid pointer to an
> initialized drm_crtc
> +        to_result(unsafe {
> bindings::drm_crtc_vblank_get(crtc.as_raw()) })?;
> +
> +        Ok(Self(crtc))
> +    }
> +}
> +
> +/// The base wrapper for [`drm_vblank_crtc`].
> +///
> +/// Users will rarely interact with this object directly, it is a
> simple wrapper around
> +/// [`drm_vblank_crtc`] which provides access to methods and data
> that is not protected by a lock.
> +///
> +/// # Invariants
> +///
> +/// This type has an identical data layout to [`drm_vblank_crtc`].
> +///
> +/// [`drm_vblank_crtc`]: srctree/include/drm/drm_vblank.h
> +#[repr(transparent)]
> +pub struct VblankCrtc<T>(Opaque<bindings::drm_vblank_crtc>,
> PhantomData<T>);
> +
> +impl<T: VblankDriverCrtc> VblankCrtc<T> {
> +    pub(crate) fn as_raw(&self) -> *mut bindings::drm_vblank_crtc {
> +        self.0.get()
> +    }
> +
> +    // SAFETY: The caller promises that `ptr` points to a valid
> instance of
> +    // `bindings::drm_vblank_crtc`, and that access to this
> structure has been properly serialized
> +    pub(crate) unsafe fn from_raw<'a>(ptr: *mut
> bindings::drm_vblank_crtc) -> &'a Self {
> +        // SAFETY: Our data layouts are identical via
> #[repr(transparent)]
> +        unsafe { &*ptr.cast() }
> +    }
> +
> +    /// Returns the [`Device`] for this [`VblankGuard`]
> +    pub fn drm_dev(&self) -> &Device<T::Driver> {
> +        // SAFETY: `drm` is initialized, invariant and valid
> throughout our lifetime
> +        unsafe { Device::from_raw((*self.as_raw()).dev) }
> +    }
> +}
> +
> +// NOTE: This type does not use a `Guard` because the mutex is not
> contained within the same
> +// structure as the relevant CRTC
> +/// An interface for accessing and controlling vblank related state
> for a [`Crtc`].
> +///
> +/// This type may be returned from some [`VblankSupport`] callbacks,
> or manually via
> +/// [`Crtc::vblank_lock`]. It provides access to methods and data
> which require
> +/// [`drm_device.vbl_lock`] be held.
> +///
> +/// # Invariants
> +///
> +/// - [`drm_device.vbl_lock`] is acquired whenever an instance of
> this type exists.
> +/// - Shares the invariants of [`VblankCrtc`].
> +///
> +/// [`drm_device.vbl_lock`]: srctree/include/drm/drm_device.h
> +#[repr(transparent)]
> +pub struct VblankGuard<'a, T: VblankDriverCrtc>(&'a VblankCrtc<T>);
> +
> +impl<'a, T: VblankDriverCrtc> VblankGuard<'a, T> {
> +    /// Construct a new [`VblankGuard`]
> +    ///
> +    /// # Safety
> +    ///
> +    /// The caller must have already acquired
> [`drm_device.vbl_lock`].
> +    ///
> +    /// [`drm_device.vbl_lock`]: srctree/include/drm/drm_device.h
> +    pub(crate) unsafe fn new(crtc: &'a Crtc<T>, _irq: &'a
> LocalInterruptDisabled) -> Self {
> +        // INVARIANT: The caller promises that we've acquired
> `vbl_lock`
> +        Self(crtc.vblank_crtc())
> +    }
> +
> +    /// Returns the duration of a single scanout frame in ns.
> +    pub fn frame_duration(&self) -> i32 {
> +        // SAFETY: We hold the appropriate lock for this read via
> our type invariants.
> +        unsafe { *self.as_raw() }.framedur_ns
> +    }
> +
> +    /// Return the vblank core's cached copy of the currently set
> display mode.
> +    ///
> +    /// If the display is disabled, this will return `None`.
> +    pub fn hwmode(&self) -> Option<&DisplayMode> {
> +        // SAFETY: We hold the appropriate lock for this read via
> our type invariants.
> +        let ptr = unsafe { &raw const (*self.as_raw()).hwmode };
> +
> +        // SAFETY: We check here if the cached DisplayMode is Null,
> which means the only other
> +        // possibility is that the pointer points to a valid
> initialized drm_display_mode.
> +        (!ptr.is_null()).then(|| unsafe { DisplayMode::as_ref(ptr)
> })
> +    }
> +}
> +
> +impl<T: VblankDriverCrtc> Deref for VblankGuard<'_, T> {
> +    type Target = VblankCrtc<T>;
> +
> +    fn deref(&self) -> &Self::Target {
> +        &self.0
> +    }
> +}
> +
> +impl<T: VblankDriverCrtc> Drop for VblankGuard<'_, T> {
> +    fn drop(&mut self) {
> +        // SAFETY:
> +        // - We acquired this spinlock when creating this object
> +        // - This lock is guaranteed to be initialized for as long
> as our DRM device is exposed to
> +        //   users.
> +        unsafe { bindings::spin_unlock(&raw mut
> (*self.drm_dev().as_raw()).vbl_lock) }
> +    }
> +}


^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2026-07-07 22:21 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-17 15:02 [RFC PATCH 0/5] rust: drm: minimal KMS bindings, EDID read, rotation, HDCP defs Mike Lothian
2026-06-17 15:02 ` [RFC PATCH 1/4] rust: drm: add minimal KMS bindings for simple-display-pipe drivers Mike Lothian
2026-06-17 15:02 ` [RFC PATCH 2/4] rust: drm: expose drm_edid.h for reading connector EDID Mike Lothian
2026-06-17 15:02 ` [RFC PATCH 3/4] rust: drm: expose drm::Device::as_raw() Mike Lothian
2026-06-17 15:02 ` [RFC PATCH 4/4] rust: drm: expose drm_blend.h and the atomic new-CRTC-state accessor Mike Lothian
2026-06-17 15:11 ` [RFC PATCH 0/5] rust: drm: minimal KMS bindings, EDID read, rotation, HDCP defs Miguel Ojeda
2026-06-17 15:29   ` Mike Lothian
2026-07-03  3:00 ` [RFC PATCH v2 00/18] rust: drm: safe KMS mode-object layer + evdi bindings Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 01/18] rust: drm: kms: forward-port the safe mode-object layer onto the typestate device Mike Lothian
2026-07-07 21:46     ` lyude
2026-07-07 22:21     ` lyude
2026-07-03  3:00   ` [RFC PATCH v2 02/18] rust: drm: kms: adapt the port to current drm-next Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 03/18] rust: drm: kms: break the Driver* trait well-formedness cycle Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 04/18] rust: drm: kms: build the kernel crate clean under -Znext-solver Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 05/18] rust: drm: expose <drm/display/drm_hdcp.h> HDCP 2.2 message definitions Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 06/18] rust: drm: kms: add a Framebuffer::vmap() guard Mike Lothian
2026-07-07 21:51     ` lyude
2026-07-03  3:00   ` [RFC PATCH v2 07/18] rust: drm: kms: add safe accessors for common state and connector modes Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 08/18] rust: drm: tyr: add the Kms associated type Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 09/18] rust: drm: add drm_event delivery Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 10/18] rust: drm: allow drivers to declare ioctls from their own uAPI module Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 11/18] rust: platform: add runtime platform device creation Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 12/18] rust: drm: framebuffer: add geometry accessors, refcounting and a byte-slice vmap Mike Lothian
2026-07-03  3:01   ` [RFC PATCH v2 13/18] rust: i2c: add adapter-provider (bus controller) registration Mike Lothian
2026-07-03  3:01   ` [RFC PATCH v2 14/18] rust: add sysfs device attribute groups Mike Lothian
2026-07-03  3:01   ` [RFC PATCH v2 15/18] rust: drm: support hardware cursor planes with sleepable event delivery Mike Lothian
2026-07-03  3:01   ` [RFC PATCH v2 16/18] rust: drm: add CRTC gamma LUT and plane rotation property bindings Mike Lothian
2026-07-03  3:01   ` [RFC PATCH v2 17/18] rust: drm: kms: add connector detect() and mode_valid() hooks Mike Lothian
2026-07-03  3:01   ` [RFC PATCH v2 18/18] rust: drm: kms: add plane damage-clip accessors Mike Lothian

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox