The safe connector layer only exposed get_modes(), so a driver couldn't
report hotplug state or prune modes it can't drive. Add two optional
DriverConnector hooks, gated the same way as the plane/CRTC optionals
(registered in the vtable only when the driver overrides them):

  - detect(&Connector, force) -> Status, wired to
    drm_connector_funcs.detect, returning a new Status enum
    (Connected/Disconnected/Unknown mapping to enum drm_connector_status).

  - mode_valid(&Connector, &DisplayMode) -> ModeStatus, wired to
    drm_connector_helper_funcs.mode_valid, returning a new ModeStatus enum
    (Ok/Bad/ClockHigh/ClockLow, a common subset of enum drm_mode_status).

Both leave DRM's default behaviour (always connected, all modes valid)
unchanged for connectors that don't implement them.

Signed-off-by: Mike Lothian <[email protected]>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/kernel/drm/kms/connector.rs | 98 +++++++++++++++++++++++++++++++-
 1 file changed, 95 insertions(+), 3 deletions(-)

diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs
index 591153de7ab4..a66243db8868 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
@@ -105,14 +143,22 @@ 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,
             atomic_duplicate_state: 
Some(atomic_duplicate_state_callback::<Self::State>),
         },
         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,
@@ -151,6 +197,28 @@ 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: &Connector<Self>, _mode: &DisplayMode) -> 
ModeStatus {
+        build_error::build_error("This should not be reachable")
+    }
 }
 
 /// The generated C vtable for a [`DriverConnector`].
@@ -464,6 +532,30 @@ 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) };
+
+    T::mode_valid(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`]
-- 
2.55.0

Reply via email to