diff --git a/rust/helpers/drm/atomic.c b/rust/helpers/drm/atomic.c new file mode 100644 index 0000000000000000000000000000000000000000..fff70053f6943a899b972bf51d1732ca10609914 --- /dev/null +++ b/rust/helpers/drm/atomic.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <drm/drm_atomic.h> + +void rust_helper_drm_atomic_state_get(struct drm_atomic_state *state) +{ + drm_atomic_state_get(state); +} + +void rust_helper_drm_atomic_state_put(struct drm_atomic_state *state) +{ + drm_atomic_state_put(state); +} + +// Macros for generating one repetitive atomic state accessors (like drm_atomic_get_new_plane_state) +#define STATE_FUNC(type, tense) \ + struct drm_ ## type ## _state *rust_helper_drm_atomic_get_ ## tense ## _ ## type ## _state( \ + const struct drm_atomic_state *state, \ + struct drm_ ## type *type \ + ) { \ + return drm_atomic_get_## tense ## _ ## type ## _state(state, type); \ + } +#define STATE_FUNCS(type) \ + STATE_FUNC(type, new); \ + STATE_FUNC(type, old); + +STATE_FUNCS(plane); +STATE_FUNCS(crtc); +STATE_FUNCS(connector); + +#undef STATE_FUNCS +#undef STATE_FUNC diff --git a/rust/helpers/drm/drm.c b/rust/helpers/drm/drm.c index 028b8ab429572483da25a38712e11605869b5c29..365f6807774d4984d03a5d6348e6cf2c444daaf4 100644 --- a/rust/helpers/drm/drm.c +++ b/rust/helpers/drm/drm.c @@ -1,5 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 +#ifdef CONFIG_DRM_KMS_HELPER +#include "atomic.c" +#endif #include "gem.c" #ifdef CONFIG_DRM_GEM_SHMEM_HELPER #include "gem_shmem_helper.c" diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs index d74267c78864f93442c77492dee62589afd6ac25..4ab039d67352ecb2e14c33f690ad911eff10760e 100644 --- a/rust/kernel/drm/kms.rs +++ b/rust/kernel/drm/kms.rs @@ -2,6 +2,7 @@ //! KMS driver abstractions for rust. +pub mod atomic; pub mod connector; pub mod crtc; pub mod encoder; @@ -248,6 +249,14 @@ impl<T: KmsDriver> Device<T> { pub fn mode_config_lock(&self) -> ModeConfigGuard<'_, T> { ModeConfigGuard(self.mode_config_mutex().lock(), PhantomData) } + + /// Return the number of registered [`Plane`](plane::Plane) objects on this [`Device`]. + #[inline] + pub fn num_plane(&self) -> i32 { + // SAFETY: The only context which this could change is before registration, which must be + // single-threaded anyway - so it's safe to just read this value + unsafe { (*self.as_raw()).mode_config.num_total_plane } + } } /// A modesetting object in DRM. diff --git a/rust/kernel/drm/kms/atomic.rs b/rust/kernel/drm/kms/atomic.rs new file mode 100644 index 0000000000000000000000000000000000000000..a4354b89b07cc4a1b29bf9ca2abbf304e518f30a --- /dev/null +++ b/rust/kernel/drm/kms/atomic.rs @@ -0,0 +1,419 @@ +// 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 alloc::boxed::Box; +use crate::{ + bindings, + drm::{device::Device, drv::Driver}, + types::*, + error::{from_err_ptr, to_result, from_result}, + init::Zeroable, + prelude::*, + private::Sealed +}; +use core::{ + marker::*, + ptr::NonNull, + cell::Cell, + ffi::*, + slice, + ops::*, + mem::ManuallyDrop, + iter::Iterator, +}; +use super::{ + crtc::*, + connector::*, + plane::*, + Kms, + KmsDriver, + ModeObject +}; + +/// 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 + pub(super) unsafe fn from_raw<'a>(ptr: *const bindings::drm_atomic_state) -> &'a Self { + // SAFETY: Our data layout is identical + 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::borrow((*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: AsRawCrtc<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| unsafe { 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: AsRawPlane<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| unsafe { 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: AsRawConnector<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| unsafe { 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: FFI call with no special requirements + unsafe { bindings::drm_atomic_state_get(self.state.get()) } + } + + unsafe fn dec_ref(obj: NonNull<Self>) { + // SAFETY: FFI calls with no special requirements + 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` + 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 + 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: AsRawCrtc<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: AsRawConnector<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: AsRawPlane<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 a composer still exists for this state. + pub fn get_new_crtc_state<C>(&self, crtc: &C) -> Option<BorrowedCrtcState<'_, C::State>> + where + C: AsRawCrtc<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()) + }; + + BorrowedCrtcState::<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 a composer still exists for this state. + pub fn get_new_plane_state<P>(&self, plane: &P) -> Option<BorrowedPlaneState<'_, P::State>> + where + P: AsRawPlane<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()) + }; + + BorrowedPlaneState::<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 a composer still exists for this state. + pub fn get_new_connector_state<C>( + &self, + connector: &C + ) -> Option<BorrowedConnectorState<'_, C::State>> + where + C: AsRawConnector<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()) + }; + + BorrowedConnectorState::<C::State>::new(self, NonNull::new(state)?) + } + + /// Iterate through each of the planes (regardless of type) currently in this atomic state. + pub fn iter_planes(&self) -> AtomicStatePlaneIter<'_, T> { + AtomicStatePlaneIter::new(&self.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<BorrowedCrtcState<'_, C::State>> + where + C: AsRawCrtc<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)) + }?; + + BorrowedCrtcState::<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<BorrowedPlaneState<'_, P::State>> + where + P: AsRawPlane<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)) + }?; + + BorrowedPlaneState::<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<BorrowedConnectorState<'_, C::State>> + where + C: AsRawConnector<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)) + }?; + + BorrowedConnectorState::<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(&self, crtc: &impl AsRawCrtc<Driver = T>) -> Result { + // SAFETY: FFI call with no special safety requirements + to_result(unsafe { + bindings::drm_atomic_add_affected_planes(self.as_raw(), crtc.as_raw()) + }) + } +} + +/// An iterator which goes through each DRM plane currently in an atomic state. +/// +/// Note that this iterator will return [`OpaquePlane`]s, because it's entirely possible for a +/// driver to have multiple implementations of [`DriverPlane`] - so we don't know what the fully +/// qualified type of each plane is. +pub struct AtomicStatePlaneIter<'a, T: KmsDriver> { + state: &'a AtomicState<T>, + current_idx: u8, +} + +impl<'a, T: KmsDriver> Iterator for AtomicStatePlaneIter<'a, T> { + type Item = &'a OpaquePlane<T>; + + fn next(&mut self) -> Option<Self::Item> { + let ptr = self.state.state.get(); + + // SAFETY: `planes` is initialized by the time we expose AtomicState<T> through any form to + // users. And because we don't allow state mutation outside of mutators, which are single + // threaded, the contents of this struct are at least guaranteed not to change through the + // duration of this borrow. + let planes: &[bindings::__drm_planes_state] = unsafe { + slice::from_raw_parts((*ptr).planes.cast_const(), self.state.drm_dev().num_plane() as _) + }; + + for plane_states in &planes[self.current_idx as _..] { + self.current_idx += 1; + if !plane_states.ptr.is_null() { + // SAFETY: OpaquePlane has an identical data layout, and its only possible values + // are NULL or pointing at a valid drm_plane + return Some(unsafe { OpaquePlane::from_raw(plane_states.ptr) }); + } + } + + None + } +} + +impl<'a, T: KmsDriver> AtomicStatePlaneIter<'a, T> { + fn new(state: &'a AtomicState<T>) -> AtomicStatePlaneIter<'a, T> { + Self { + current_idx: 0, + state + } + } +} diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs index f62740d7f6469b58a26e4fb27e62bd3c9b6e9930..6fcfce8b48c64de9619a8a580c37a5ba24bbc484 100644 --- a/rust/kernel/drm/kms/connector.rs +++ b/rust/kernel/drm/kms/connector.rs @@ -31,6 +31,7 @@ use super::{ ModeConfigGuard, encoder::*, KmsDriver, + atomic::*, }; use macros::pin_data; @@ -679,6 +680,80 @@ impl<T: KmsDriver> FromRawConnectorState for OpaqueConnectorState<T> { } } +/// 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 [`BorrowedConnectorState`] may exist at a time. +pub struct BorrowedConnectorState<'a, T: FromRawConnectorState> { + state: &'a mut T, + mask: &'a Cell<u32> +} + +impl<'a, T: FromRawConnectorState> BorrowedConnectorState<'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, is + // initialized by this point, and we're guaranteed it is of type `OpaqueConnector<T>` by + // type invariance + 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 `ConnectorState<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 { + // TODO: Print a kernel warning here, this is a user error + None + } + } +} + +impl<'a, T: DriverConnectorState> Deref for BorrowedConnectorState<'a, ConnectorState<T>> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.state.inner + } +} + +impl<'a, T: DriverConnectorState> DerefMut for BorrowedConnectorState<'a, ConnectorState<T>> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.state.inner + } +} + +impl<'a, T: FromRawConnectorState> Drop for BorrowedConnectorState<'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 BorrowedConnectorState<'a, T> { + type Connector = T::Connector; +} + +impl<'a, T: FromRawConnectorState> private::AsRawConnectorState for BorrowedConnectorState<'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() } + } +} + unsafe extern "C" fn atomic_duplicate_state_callback<T: DriverConnectorState>( connector: *mut bindings::drm_connector ) -> *mut bindings::drm_connector_state diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs index 246d15a15c14d91c5ef1974116ed94e8171c55bf..7864540705f76ce676950f33e5b153f89680d257 100644 --- a/rust/kernel/drm/kms/crtc.rs +++ b/rust/kernel/drm/kms/crtc.rs @@ -3,6 +3,7 @@ //! KMS driver abstractions for rust. use super::{ + atomic::*, plane::*, ModeObject, StaticModeObject, @@ -552,6 +553,80 @@ impl<T: KmsDriver> FromRawCrtcState for OpaqueCrtcState<T> { unsafe { &*(ptr.cast()) } } } + +/// 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 +/// [`BorrowedCrtcState`] may exist at a time. +/// +/// # Invariants +/// +/// `self.state` always points to a valid instance of a [`FromRawCrtcState`] object. +pub struct BorrowedCrtcState<'a, T: FromRawCrtcState> { + state: NonNull<T>, + mask: &'a Cell<u32>, +} + +impl<'a, T: FromRawCrtcState> BorrowedCrtcState<'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: FromRawCrtcState> Drop for BorrowedCrtcState<'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 BorrowedCrtcState<'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 BorrowedCrtcState<'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 BorrowedCrtcState<'a, T> { + type Crtc = T::Crtc; +} + +impl<'a, T: FromRawCrtcState> private::AsRawCrtcState for BorrowedCrtcState<'a, T> { + fn as_raw(&self) -> *mut bindings::drm_crtc_state { + self.state.as_ptr().cast() + } +} + unsafe extern "C" fn crtc_destroy_callback<T: DriverCrtc>( crtc: *mut bindings::drm_crtc ) { diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs index 1c151ae3b3dcc97b8c13971ca3c4c3245e644f5a..d6e11a65cc1017a20ad8d7eb646423078cdd525d 100644 --- a/rust/kernel/drm/kms/plane.rs +++ b/rust/kernel/drm/kms/plane.rs @@ -29,6 +29,7 @@ use super::{ UnregisteredKmsDevice, ModeObject, StaticModeObject, + atomic::*, }; /// The main trait for implementing the [`struct drm_plane`] API for [`Plane`] @@ -597,6 +598,82 @@ impl<T: KmsDriver> FromRawPlaneState for OpaquePlaneState<T> { unsafe { &mut *ptr.cast() } } } + +/// 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 +/// [`BorrowedPlaneState`] may exist at a time. +pub struct BorrowedPlaneState<'a, T: FromRawPlaneState> { + state: &'a mut T, + mask: &'a Cell<u32> +} + +impl<'a, T: FromRawPlaneState> BorrowedPlaneState<'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 BorrowedPlaneState<'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 BorrowedPlaneState<'a, T> { + type Plane = T::Plane; +} + +impl<'a, T: FromRawPlaneState> private::AsRawPlaneState for BorrowedPlaneState<'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 BorrowedPlaneState<'a, T> {} + +impl<'a, T: DriverPlaneState> Deref for BorrowedPlaneState<'a, PlaneState<T>> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.state.inner + } +} + +impl<'a, T: DriverPlaneState> DerefMut for BorrowedPlaneState<'a, PlaneState<T>> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.state.inner + } +} + unsafe extern "C" fn plane_destroy_callback<T: DriverPlane>( plane: *mut bindings::drm_plane ) {