Rust for Linux List
 help / color / mirror / Atom feed
From: Mike Lothian <mike@fireburn.co.uk>
To: dri-devel@lists.freedesktop.org
Cc: "Mike Lothian" <mike@fireburn.co.uk>,
	"Danilo Krummrich" <dakr@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"David Airlie" <airlied@gmail.com>,
	"Simona Vetter" <simona@ffwll.ch>,
	"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 8/23] rust: drm: kms: add connector detect() and mode_valid() hooks
Date: Wed, 26 Aug 2026 17:31:39 +0100	[thread overview]
Message-ID: <20260826163359.4998-9-mike@fireburn.co.uk> (raw)
In-Reply-To: <20260826163359.4998-1-mike@fireburn.co.uk>

Add optional connector detect() and mode_valid() callbacks to the
safe KMS API.

Map connector status and common mode-validation results through typed
enums. Connectors which do not implement the callbacks retain the
DRM defaults of connected and mode-valid.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
---
 rust/kernel/drm/kms/connector.rs | 141 ++++++++++++++++++++++++++++++-
 1 file changed, 138 insertions(+), 3 deletions(-)

diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs
index 793a7bb5bfef..dd126469788f 100644
--- a/rust/kernel/drm/kms/connector.rs
+++ b/rust/kernel/drm/kms/connector.rs
@@ -5,7 +5,8 @@
 //! C header: [`include/drm/drm_connector.h`](srctree/include/drm/drm_connector.h)
 
 use super::{
-    atomic::*, encoder::*, KmsDriver, ModeConfigGuard, ModeObject, ModeObjectVtable, Sealed,
+    atomic::*, encoder::*, modes::DisplayMode, KmsDriver, ModeConfigGuard, ModeObject,
+    ModeObjectVtable, Sealed,
 };
 use crate::{
     alloc::KBox,
@@ -72,6 +73,43 @@ pub enum Type {
     USB         as Usb
 }
 
+/// The connection status of a [`Connector`], as returned by [`DriverConnector::detect`].
+///
+/// This is identical to [`enum drm_connector_status`].
+///
+/// [`enum drm_connector_status`]: srctree/include/drm/drm_connector.h
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(u32)]
+pub enum Status {
+    /// The connector is connected to a display and a mode list can be retrieved.
+    Connected = bindings::drm_connector_status_connector_status_connected,
+    /// The connector has no display attached.
+    Disconnected = bindings::drm_connector_status_connector_status_disconnected,
+    /// The connection state could not be determined (treated as connected for probing).
+    Unknown = bindings::drm_connector_status_connector_status_unknown,
+}
+
+/// The result of validating a display mode against a [`Connector`], as returned by
+/// [`DriverConnector::mode_valid`].
+///
+/// This mirrors a small, commonly-used subset of [`enum drm_mode_status`]; use [`ModeStatus::Bad`]
+/// for a generic rejection, or the clock-specific variants when a mode is out of the driver's
+/// pixel-clock range.
+///
+/// [`enum drm_mode_status`]: srctree/include/drm/drm_modes.h
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(i32)]
+pub enum ModeStatus {
+    /// The mode is usable.
+    Ok = bindings::drm_mode_status_MODE_OK,
+    /// The mode is rejected for an unspecified reason.
+    Bad = bindings::drm_mode_status_MODE_BAD,
+    /// The mode's pixel clock is above what the driver can drive.
+    ClockHigh = bindings::drm_mode_status_MODE_CLOCK_HIGH,
+    /// The mode's pixel clock is below what the driver can drive.
+    ClockLow = bindings::drm_mode_status_MODE_CLOCK_LOW,
+}
+
 /// 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
@@ -106,7 +144,11 @@ pub trait DriverConnector: Send + Sync + Sized {
             atomic_destroy_state: Some(atomic_destroy_state_callback::<Self::State>),
             destroy: Some(connector_destroy_callback::<Self>),
             force: None,
-            detect: None,
+            detect: if Self::HAS_DETECT {
+                Some(detect_callback::<Self>)
+            } else {
+                None
+            },
             fill_modes: Some(bindings::drm_helper_probe_single_connector_modes),
             debugfs_init: None,
             oob_hotplug_event: None,
@@ -114,7 +156,11 @@ pub trait DriverConnector: Send + Sync + Sized {
             color_format: None,
         },
         helper_funcs: bindings::drm_connector_helper_funcs {
-            mode_valid: None,
+            mode_valid: if Self::HAS_MODE_VALID {
+                Some(mode_valid_callback::<Self>)
+            } else {
+                None
+            },
             atomic_check: None,
             get_modes: Some(get_modes_callback::<Self>),
             detect_ctx: None,
@@ -153,6 +199,31 @@ fn get_modes<'a>(
         connector: ConnectorGuard<'a, Self>,
         guard: &ModeConfigGuard<'a, Self::Driver>,
     ) -> i32;
+
+    /// The optional [`drm_connector_funcs.detect`] hook for this connector.
+    ///
+    /// Drivers may implement this to report whether a display is currently attached. If not
+    /// implemented, the connector is always considered connected (DRM's default with no `detect`
+    /// hook). `force` is set when userspace explicitly requested a forced probe.
+    ///
+    /// [`drm_connector_funcs.detect`]: srctree/include/drm/drm_connector.h
+    fn detect(_connector: &Connector<Self>, _force: bool) -> Status {
+        build_error::build_error("This should not be reachable")
+    }
+
+    /// The optional [`drm_connector_helper_funcs.mode_valid`] hook for this connector.
+    ///
+    /// Drivers may implement this to reject modes they cannot drive (for example, a mode whose
+    /// pixel clock exceeds the hardware's budget). Returning anything other than [`ModeStatus::Ok`]
+    /// prunes the mode from the probed list. If not implemented, every mode is accepted.
+    ///
+    /// [`drm_connector_helper_funcs.mode_valid`]: srctree/include/drm/drm_modeset_helper_vtables.h
+    fn mode_valid(
+        _connector: ConnectorModeValidation<'_, Self>,
+        _mode: &DisplayMode,
+    ) -> ModeStatus {
+        build_error::build_error("This should not be reachable")
+    }
 }
 
 /// The generated C vtable for a [`DriverConnector`].
@@ -477,6 +548,32 @@ impl<T: AsRawConnector> RawConnector for T {}
     T::get_modes(connector.guard(&guard), &guard)
 }
 
+unsafe extern "C" fn detect_callback<T: DriverConnector>(
+    connector: *mut bindings::drm_connector,
+    force: bool,
+) -> bindings::drm_connector_status {
+    // SAFETY: This is safe via `DriverConnector`s type invariants.
+    let connector = unsafe { Connector::<T>::from_raw(connector) };
+
+    T::detect(connector, force) as bindings::drm_connector_status
+}
+
+unsafe extern "C" fn mode_valid_callback<T: DriverConnector>(
+    connector: *mut bindings::drm_connector,
+    mode: *const bindings::drm_display_mode,
+) -> bindings::drm_mode_status {
+    // SAFETY: This is safe via `DriverConnector`s type invariants.
+    let connector = unsafe { Connector::<T>::from_raw(connector) };
+
+    // SAFETY: DRM guarantees `mode` points to a valid `drm_display_mode` for the duration of this
+    // callback, and only passes us shared access to it.
+    let mode = unsafe { DisplayMode::as_ref(mode) };
+
+    // DRM invokes the connector helper while the mode list is stable. Keep that guarantee in a
+    // capability type so drivers can safely compare this mode with the other probed modes.
+    T::mode_valid(ConnectorModeValidation(connector), mode) as bindings::drm_mode_status
+}
+
 /// A [`struct drm_connector`] without a known [`DriverConnector`] implementation.
 ///
 /// This is mainly for situations where our bindings can't infer the [`DriverConnector`]
@@ -572,6 +669,44 @@ fn deref(&self) -> &Self::Target {
     }
 }
 
+/// A connector being validated while its mode list is stable.
+///
+/// This is only constructed by the DRM connector-helper callback. It permits read-only iteration
+/// over the connector's modes without exposing list pointers or extending a mode reference beyond
+/// the callback.
+#[derive(Copy, Clone)]
+pub struct ConnectorModeValidation<'a, T: DriverConnector>(&'a Connector<T>);
+
+impl<T: DriverConnector> Deref for ConnectorModeValidation<'_, T> {
+    type Target = Connector<T>;
+
+    fn deref(&self) -> &Self::Target {
+        self.0
+    }
+}
+
+impl<T: DriverConnector> ConnectorModeValidation<'_, T> {
+    /// Return whether any mode currently on this connector satisfies `predicate`.
+    pub fn any_mode(&self, mut predicate: impl FnMut(&DisplayMode) -> bool) -> bool {
+        let raw = self.as_raw();
+        // SAFETY: DRM only constructs this capability while `connector->modes` is stable. Each
+        // list entry is an initialized `drm_display_mode`, and the shared reference is confined to
+        // this callback invocation.
+        unsafe {
+            let head: *mut bindings::list_head = &raw mut (*raw).modes;
+            let mut node = (*head).next;
+            while node != head {
+                let mode = crate::container_of!(node, bindings::drm_display_mode, head);
+                if predicate(DisplayMode::as_ref(mode)) {
+                    return true;
+                }
+                node = (*node).next;
+            }
+        }
+        false
+    }
+}
+
 impl<'a, T: DriverConnector> ConnectorGuard<'a, T> {
     /// Add modes for a [`ConnectorGuard`] without an EDID.
     ///

  parent reply	other threads:[~2026-08-26 16:35 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 ` Mike Lothian [this message]
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 ` [PATCH v3 19/23] rust: drm: expose CRTC mode changes Mike Lothian
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-9-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