Add a safe constructor for standalone display modes used by drivers
and tests.

Accept the essential timing fields and reject invalid active, sync,
and total ordering before exposing the owned mode.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Mike Lothian <[email protected]>
---
 rust/kernel/drm/kms/modes.rs | 62 +++++++++++++++++++++++++++++++++++-
 1 file changed, 61 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/drm/kms/modes.rs b/rust/kernel/drm/kms/modes.rs
index cc3c486eecf1..b48a02edfc13 100644
--- a/rust/kernel/drm/kms/modes.rs
+++ b/rust/kernel/drm/kms/modes.rs
@@ -6,7 +6,33 @@
 
 use bindings;
 
-use crate::types::Opaque;
+use crate::{
+    error::{code::EINVAL, Result},
+    types::Opaque,
+};
+
+/// The essential timing fields of a display mode.
+#[derive(Clone, Copy)]
+pub struct ModeTimings {
+    /// Pixel clock in kHz.
+    pub clock_khz: i32,
+    /// Horizontal active pixels.
+    pub hdisplay: u16,
+    /// Start of the horizontal sync pulse.
+    pub hsync_start: u16,
+    /// End of the horizontal sync pulse.
+    pub hsync_end: u16,
+    /// Total horizontal pixels including blanking.
+    pub htotal: u16,
+    /// Vertical active lines.
+    pub vdisplay: u16,
+    /// Start of the vertical sync pulse.
+    pub vsync_start: u16,
+    /// End of the vertical sync pulse.
+    pub vsync_end: u16,
+    /// Total vertical lines including blanking.
+    pub vtotal: u16,
+}
 
 /// DRM kernel-internal display mode structure.
 ///
@@ -30,6 +56,40 @@ unsafe impl Send for DisplayMode {}
 unsafe impl Sync for DisplayMode {}
 
 impl DisplayMode {
+    /// Creates a standalone display mode from validated timings.
+    ///
+    /// This is useful when a driver needs an owned mode for validation or 
tests rather than a
+    /// reference to a mode owned by the DRM core.
+    pub fn from_timings(t: ModeTimings) -> Result<Self> {
+        if t.clock_khz <= 0
+            || t.hdisplay == 0
+            || t.hdisplay > t.hsync_start
+            || t.hsync_start > t.hsync_end
+            || t.hsync_end > t.htotal
+            || t.vdisplay == 0
+            || t.vdisplay > t.vsync_start
+            || t.vsync_start > t.vsync_end
+            || t.vsync_end > t.vtotal
+        {
+            return Err(EINVAL);
+        }
+
+        let mut mode = bindings::drm_display_mode::default();
+        mode.clock = t.clock_khz;
+        mode.hdisplay = t.hdisplay;
+        mode.hsync_start = t.hsync_start;
+        mode.hsync_end = t.hsync_end;
+        mode.htotal = t.htotal;
+        mode.vdisplay = t.vdisplay;
+        mode.vsync_start = t.vsync_start;
+        mode.vsync_end = t.vsync_end;
+        mode.vtotal = t.vtotal;
+
+        Ok(Self {
+            inner: Opaque::new(mode),
+        })
+    }
+
     /// Convert a raw pointer to a `struct drm_display_mode` into an immutable 
[`DisplayMode`] ref.
     ///
     /// # SAFETY

Reply via email to