Rust for Linux List
 help / color / mirror / Atom feed
From: Mike Lothian <mike@fireburn.co.uk>
To: rust-for-linux@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org,
	"David Airlie" <airlied@gmail.com>,
	"Simona Vetter" <simona@ffwll.ch>,
	"Danilo Krummrich" <dakr@kernel.org>,
	"Lyude Paul" <lyude@redhat.com>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <lossin@kernel.org>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Trevor Gross" <tmgross@umich.edu>,
	linux-kernel@vger.kernel.org,
	"Mike Lothian" <mike@fireburn.co.uk>
Subject: [RFC PATCH v2 01/18] rust: drm: kms: forward-port the safe mode-object layer onto the typestate device
Date: Fri,  3 Jul 2026 04:00:48 +0100	[thread overview]
Message-ID: <20260703030123.2814-2-mike@fireburn.co.uk> (raw)
In-Reply-To: <20260703030123.2814-1-mike@fireburn.co.uk>

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


  reply	other threads:[~2026-07-03  3:01 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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   ` Mike Lothian [this message]
2026-07-07 21:46     ` [RFC PATCH v2 01/18] rust: drm: kms: forward-port the safe mode-object layer onto the typestate device 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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260703030123.2814-2-mike@fireburn.co.uk \
    --to=mike@fireburn.co.uk \
    --cc=a.hindborg@kernel.org \
    --cc=airlied@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun@kernel.org \
    --cc=dakr@kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=gary@garyguo.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=lyude@redhat.com \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=simona@ffwll.ch \
    --cc=tmgross@umich.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox