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