[PATCH v3 22/23] rust: drm: kms: walk the CRTCs an atomic commit carries
Mike Lothian <[email protected]>
| Newsgroups | org.kernel.vger.rust-for-linux,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
A driver enforcing a constraint its CRTCs share -- a bandwidth budget, a clock source, a fixed pool of scanout engines -- has to weigh what every head will be once the commit lands, and a per-CRTC callback can only reach its own new state. Reaching the others through committed state answers a different question: it prices each sibling at what it is leaving rather than what it is taking, so two heads that both rise in one commit each find the other still low, pass individually, and break the shared limit together. Add the walk over the CRTCs an atomic state carries, the Rust spelling of for_each_new_crtc_in_state(), on both the state and its mutator. The CRTC is handed over alongside its new state because the driver-private data that names what a CRTC drives hangs off the CRTC, not off the state. Assisted-by: Claude:claude-opus-5 Signed-off-by: Mike Lothian <[email protected]> --- rust/kernel/drm/kms/atomic.rs | 52 +++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/rust/kernel/drm/kms/atomic.rs b/rust/kernel/drm/kms/atomic.rs index f9f91edc89c3..24daeaeca075 100644 --- a/rust/kernel/drm/kms/atomic.rs +++ b/rust/kernel/drm/kms/atomic.rs @@ -142,6 +142,48 @@ pub fn new_connector_state_for_crtc<C>(&self, crtc: &C) -> Option<&OpaqueConnect None } + /// Invoke `f` for every CRTC this [`AtomicState`] carries a new state for, passing the CRTC + /// and that state. + /// + /// This is the Rust spelling of walking `for_each_new_crtc_in_state()`. + /// + /// A driver enforcing a constraint its heads share -- a bandwidth budget, a clock source, a + /// fixed pool of scanout engines -- has to weigh what every head will be once the commit + /// lands. A per-CRTC callback that consults committed state instead sees each sibling at its + /// old value, so two heads that both rise in one commit each find the other still low, pass + /// individually, and break the shared limit together. + pub fn for_each_new_crtc_state<F>(&self, mut f: F) + where + F: FnMut(&Crtc<T::Crtc>, &OpaqueCrtcState<T>), + { + // SAFETY: `state` is initialized via our type invariants, and `crtcs` together with the + // device's `num_crtc` are invariant for as long as we hold a reference to it. + let (crtcs, num) = unsafe { + let raw = self.as_raw(); + ((*raw).crtcs, (*(*raw).dev).mode_config.num_crtc) + }; + if crtcs.is_null() || num <= 0 { + return; + } + for i in 0..num as usize { + // SAFETY: `crtcs` points to `num_crtc` initialized entries. + let (ptr, new_state) = unsafe { ((*crtcs.add(i)).ptr, (*crtcs.add(i)).new_state) }; + if ptr.is_null() || new_state.is_null() { + continue; + } + // SAFETY: every CRTC of a `KmsDriver` device is a `Crtc<T::Crtc>`, and a non-null + // `new_state` is a valid `drm_crtc_state`. Both borrow from `self`, so neither can + // outlive the atomic state owning them. + let (crtc, state) = unsafe { + ( + Crtc::<T::Crtc>::from_raw(ptr), + OpaqueCrtcState::<T>::from_raw(new_state), + ) + }; + f(crtc, state); + } + } + /// Return the old state of the first connector routed to `crtc` in this [`AtomicState`], if /// any. /// @@ -282,6 +324,16 @@ pub fn new_connector_state_for_crtc<C>(&self, crtc: &C) -> Option<&OpaqueConnect self.state.new_connector_state_for_crtc(crtc) } + /// Invoke `f` for every CRTC this state carries a new state for. + /// + /// See [`AtomicState::for_each_new_crtc_state`]. + pub fn for_each_new_crtc_state<F>(&self, f: F) + where + F: FnMut(&Crtc<T::Crtc>, &OpaqueCrtcState<T>), + { + self.state.for_each_new_crtc_state(f) + } + /// Return the old state of the first connector routed to `crtc`, if any. /// /// See [`AtomicState::old_connector_state_for_crtc`].