[PATCH v3 8/23] rust: drm: kms: add connector detect() and mode_valid() hooks

From: Mike Lothian

Date: Wed Aug 26 2026 - 12:40:57 EST


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@xxxxxxxxxxxxxx>
---
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.
///