From: Mike Lothian <mike@fireburn.co.uk>
To: dri-devel@lists.freedesktop.org
Cc: "Mike Lothian" <mike@fireburn.co.uk>,
"David Airlie" <airlied@gmail.com>,
"Simona Vetter" <simona@ffwll.ch>,
"Danilo Krummrich" <dakr@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <lossin@kernel.org>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Trevor Gross" <tmgross@umich.edu>,
"Daniel Almeida" <daniel.almeida@collabora.com>,
"Tamir Duberstein" <tamird@kernel.org>,
"Alexandre Courbot" <acourbot@nvidia.com>,
"Onur Özkan" <work@onurozkan.dev>,
"Lyude Paul" <lyude@redhat.com>,
rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v3 19/23] rust: drm: expose CRTC mode changes
Date: Wed, 26 Aug 2026 17:31:50 +0100 [thread overview]
Message-ID: <20260826163359.4998-20-mike@fireburn.co.uk> (raw)
In-Reply-To: <20260826163359.4998-1-mike@fireburn.co.uk>
Allow Rust DRM drivers to distinguish a modeset or enable transition
from an ordinary atomic page flip. This avoids repeating mode-specific
validation on the page-flip hot path.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
---
rust/kernel/drm/kms/crtc.rs | 139 +++++++++++++++++++++++++++++++++++-
1 file changed, 138 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs
index 892f04f04e22..9e888c4e2f68 100644
--- a/rust/kernel/drm/kms/crtc.rs
+++ b/rust/kernel/drm/kms/crtc.rs
@@ -31,6 +31,17 @@
pub struct ColorLut(bindings::drm_color_lut);
impl ColorLut {
+ /// Build an entry. Mainly useful for tests and for drivers synthesising a ramp.
+ #[inline]
+ pub const fn new(red: u16, green: u16, blue: u16) -> Self {
+ Self(bindings::drm_color_lut {
+ red,
+ green,
+ blue,
+ reserved: 0,
+ })
+ }
+
/// Red channel value.
pub fn red(&self) -> u16 {
self.0.red
@@ -47,6 +58,65 @@ pub fn blue(&self) -> u16 {
}
}
+/// A colour transformation matrix, as programmed through the CRTC's `CTM` property.
+///
+/// The matrix is applied to the pixel values that the degamma LUT produced, before the gamma LUT:
+///
+/// ```text
+/// out matrix in
+/// |R| |0 1 2| |R|
+/// |G| = |3 4 5| x |G|
+/// |B| |6 7 8| |B|
+/// ```
+#[repr(transparent)]
+pub struct ColorCtm(bindings::drm_color_ctm);
+
+impl ColorCtm {
+ /// Build a matrix from raw S31.32 **sign-magnitude** entries. Mainly useful for tests.
+ #[inline]
+ pub const fn from_raw(matrix: [u64; 9]) -> Self {
+ Self(bindings::drm_color_ctm { matrix })
+ }
+
+ /// The raw matrix, in the UAPI's S31.32 **sign-magnitude** encoding.
+ ///
+ /// Prefer [`Self::coefficient`], which decodes an entry into an ordinary signed value.
+ #[inline]
+ pub fn raw(&self) -> &[u64; 9] {
+ &self.0.matrix
+ }
+
+ /// Return matrix entry `i` as a two's-complement S31.32 fixed-point value, or [`None`] if `i`
+ /// is out of range.
+ ///
+ /// The UAPI encodes these in **sign-magnitude, not two's complement** (bit 63 is the sign and
+ /// the remaining 63 bits are the magnitude), so reading the `u64` as an `i64` silently turns
+ /// every negative coefficient into a huge positive one. Decoding here means no driver has to
+ /// remember that.
+ #[inline]
+ pub fn coefficient(&self, i: usize) -> Option<i64> {
+ let raw = *self.0.matrix.get(i)?;
+ // The magnitude is capped so it always fits a positive i64.
+ let magnitude = (raw & !(1u64 << 63)) as i64;
+ Some(if raw & (1u64 << 63) != 0 {
+ -magnitude
+ } else {
+ magnitude
+ })
+ }
+
+ /// Return all nine coefficients decoded by [`Self::coefficient`].
+ #[inline]
+ pub fn coefficients(&self) -> [i64; 9] {
+ let mut out = [0i64; 9];
+ for (i, o) in out.iter_mut().enumerate() {
+ // The index is in range by construction, so the fallback is unreachable.
+ *o = self.coefficient(i).unwrap_or(0);
+ }
+ out
+ }
+}
+
/// 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
@@ -440,8 +510,28 @@ pub fn new<'a, PrimaryData, CursorData>(
/// Call this during [`KmsDriver::probe`](crate::drm::kms::KmsDriver::probe), before the device
/// is registered.
pub fn enable_gamma(&self, gamma_size: u32) {
+ self.enable_color_mgmt(0, false, gamma_size)
+ }
+
+ /// Enable colour management on this CRTC, creating the `DEGAMMA_LUT`, `CTM` and `GAMMA_LUT`
+ /// properties that userspace can program.
+ ///
+ /// A size of zero suppresses the corresponding LUT property, and `has_ctm` selects whether the
+ /// `CTM` property is created. The programmed values are then readable from the CRTC state via
+ /// [`RawCrtcState::degamma_lut`], [`RawCrtcState::ctm`] and [`RawCrtcState::gamma_lut`].
+ ///
+ /// A driver with no colour hardware can still advertise these and apply them in software while
+ /// it has the pixels; compositors that colour-correct through the CRTC properties (rather than
+ /// by rewriting the framebuffer) otherwise have nowhere to put the correction on such an
+ /// output.
+ ///
+ /// Call this during [`KmsDriver::probe`](crate::drm::kms::KmsDriver::probe), before the device
+ /// is registered.
+ pub fn enable_color_mgmt(&self, degamma_size: u32, has_ctm: bool, gamma_size: u32) {
// SAFETY: `as_raw()` is a valid, not-yet-registered CRTC.
- unsafe { bindings::drm_crtc_enable_color_mgmt(self.as_raw(), 0, false, gamma_size) };
+ unsafe {
+ bindings::drm_crtc_enable_color_mgmt(self.as_raw(), degamma_size, has_ctm, gamma_size)
+ };
}
}
@@ -766,6 +856,12 @@ fn active(&self) -> bool {
unsafe { (*self.as_raw()).active }
}
+ /// Returns whether the mode or enable state changed in this atomic state.
+ fn mode_changed(&self) -> bool {
+ // SAFETY: The atomic-state API serializes access to this state, including its bitfields.
+ unsafe { (*self.as_raw()).mode_changed() }
+ }
+
/// Return the display mode programmed into this CRTC state.
fn mode(&self) -> &DisplayMode {
// SAFETY: `mode` is embedded in the CRTC state and therefore has the same lifetime. The
@@ -793,6 +889,47 @@ fn gamma_lut(&self) -> Option<&[ColorLut]> {
// entries valid for the state's lifetime.
Some(unsafe { core::slice::from_raw_parts(data.cast::<ColorLut>(), n) })
}
+
+ /// Returns the CRTC's degamma LUT for this state as an array of [`ColorLut`] entries, or
+ /// [`None`] if none is programmed. Requires a non-zero `degamma_size` to have been passed to
+ /// [`UnregisteredCrtc::enable_color_mgmt`].
+ fn degamma_lut(&self) -> Option<&[ColorLut]> {
+ // SAFETY: `as_raw()` is a valid `drm_crtc_state`.
+ let blob = unsafe { (*self.as_raw()).degamma_lut };
+ if blob.is_null() {
+ return None;
+ }
+ // SAFETY: a non-null degamma_lut blob is valid for the state's lifetime.
+ let (data, length) = unsafe { ((*blob).data, (*blob).length) };
+ let n = length / core::mem::size_of::<ColorLut>();
+ if data.is_null() || n == 0 {
+ return None;
+ }
+ // SAFETY: `ColorLut` is transparent over `drm_color_lut`; the blob holds `n` contiguous
+ // entries valid for the state's lifetime.
+ Some(unsafe { core::slice::from_raw_parts(data.cast::<ColorLut>(), n) })
+ }
+
+ /// Returns the CRTC's colour transformation matrix for this state, or [`None`] if none is
+ /// programmed. Requires `has_ctm` to have been passed to
+ /// [`UnregisteredCrtc::enable_color_mgmt`].
+ fn ctm(&self) -> Option<&ColorCtm> {
+ // SAFETY: `as_raw()` is a valid `drm_crtc_state`.
+ let blob = unsafe { (*self.as_raw()).ctm };
+ if blob.is_null() {
+ return None;
+ }
+ // SAFETY: a non-null ctm blob is valid for the state's lifetime.
+ let (data, length) = unsafe { ((*blob).data, (*blob).length) };
+ // DRM validates the blob length when the property is set, but this is the boundary where
+ // a short blob would become an out-of-bounds read of nine u64s.
+ if data.is_null() || length < core::mem::size_of::<ColorCtm>() {
+ return None;
+ }
+ // SAFETY: `ColorCtm` is transparent over `drm_color_ctm`, and the blob is at least that
+ // long and valid for the state's lifetime.
+ Some(unsafe { &*data.cast::<ColorCtm>() })
+ }
}
impl<T: AsRawCrtcState> RawCrtcState for T {}
next prev parent reply other threads:[~2026-08-26 16:36 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-26 16:31 [PATCH v3 0/23] rust: drm: KMS abstractions for a Rust display driver Mike Lothian
2026-08-26 16:31 ` [PATCH v3 1/23] rust: drm: kms: adapt Lyude's KMS series to current DRM APIs Mike Lothian
2026-08-26 16:31 ` [PATCH v3 2/23] rust: drm: kms: tie mode-object references to their owners Mike Lothian
2026-08-26 16:31 ` [PATCH v3 3/23] rust: drm: kms: constrain connector encoder attachment Mike Lothian
2026-08-26 16:31 ` [PATCH v3 4/23] rust: drm: reject cross-device GEM handle creation Mike Lothian
2026-08-26 16:31 ` [PATCH v3 5/23] rust: drm: kms: add common state and connector helpers Mike Lothian
2026-08-26 16:31 ` [PATCH v3 6/23] rust: drm: expose HDCP 2.2 message identifiers Mike Lothian
2026-08-26 16:31 ` [PATCH v3 7/23] rust: drm: kms: add typed color and rotation properties Mike Lothian
2026-08-26 16:31 ` [PATCH v3 8/23] rust: drm: kms: add connector detect() and mode_valid() hooks Mike Lothian
2026-08-26 16:31 ` [PATCH v3 9/23] rust: drm: kms: add plane damage-clip accessors Mike Lothian
2026-08-26 16:31 ` [PATCH v3 10/23] rust: drm: framebuffer: add validated shmem scanout views Mike Lothian
2026-08-26 16:31 ` [PATCH v3 11/23] rust: drm: kms: expose checked plane geometry Mike Lothian
2026-08-26 16:31 ` [PATCH v3 12/23] rust: drm: kms: add owned CRTC and vblank references Mike Lothian
2026-08-26 16:31 ` [PATCH v3 13/23] rust: drm: kms: plane: add FB_DAMAGE_CLIPS property support Mike Lothian
2026-08-26 16:31 ` [PATCH v3 14/23] rust: drm: add a safe constructor for owned registration data Mike Lothian
2026-08-26 16:31 ` [PATCH v3 15/23] rust: drm: pin the owner while DRM files remain open Mike Lothian
2026-08-26 16:31 ` [PATCH v3 16/23] rust: drm: kms: add the plane blend-mode property Mike Lothian
2026-08-26 16:31 ` [PATCH v3 17/23] rust: drm: add an owned display mode constructor Mike Lothian
2026-08-26 16:31 ` [PATCH v3 18/23] rust: drm: expose mode flags and CTA VIC matching Mike Lothian
2026-08-26 16:31 ` Mike Lothian [this message]
2026-08-26 16:31 ` [PATCH v3 20/23] rust: drm: kms: add synthesized CVT connector modes Mike Lothian
2026-08-26 16:31 ` [PATCH v3 21/23] rust: drm: kms: read a connector's colorimetry and HDR metadata Mike Lothian
2026-08-26 16:31 ` [PATCH v3 22/23] rust: drm: kms: walk the CRTCs an atomic commit carries Mike Lothian
2026-08-26 16:31 ` [PATCH v3 23/23] rust: drm: kms: expose a connector's requested link depth 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=20260826163359.4998-20-mike@fireburn.co.uk \
--to=mike@fireburn.co.uk \
--cc=a.hindborg@kernel.org \
--cc=acourbot@nvidia.com \
--cc=airlied@gmail.com \
--cc=aliceryhl@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=gary@garyguo.net \
--cc=linux-kernel@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=lyude@redhat.com \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=simona@ffwll.ch \
--cc=tamird@kernel.org \
--cc=tmgross@umich.edu \
--cc=work@onurozkan.dev \
/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;
as well as URLs for NNTP newsgroup(s).