Add Rust abstraction for the DRM panel subsystem, covering
the consumer side of the panel API.

`Panel` wraps a reference-counted `struct drm_panel` and
implements `AlwaysRefCounted`. It exposes consumer-side lifecycle
operations and provides `from_of_node()` associated method for
looking up a registered panel by device tree node.

`PanelOrientation` wraps `enum drm_panel_orientation` and
provides `from_of_node()` to read "rotation" property from
the device tree.

`Registration` manages the panel's presence in the global
registry, decoupling its lifetime from reference count lifetime.

Following patches will handle the producer side.

Signed-off-by: Albert Esteve <[email protected]>
---
 rust/bindings/bindings_helper.h |   1 +
 rust/helpers/drm.c              |  15 +++
 rust/kernel/drm/mod.rs          |   1 +
 rust/kernel/drm/panel.rs        | 227 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 244 insertions(+)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 7f3030ac0feb6..0f23d323cbb24 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -33,6 +33,7 @@
 #include <drm/drm_connector.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_panel.h>
 #include <drm/drm_file.h>
 #include <drm/drm_gem.h>
 #include <drm/drm_gem_shmem_helper.h>
diff --git a/rust/helpers/drm.c b/rust/helpers/drm.c
index 65f3f22b0e1d2..fc5b9af6e4d23 100644
--- a/rust/helpers/drm.c
+++ b/rust/helpers/drm.c
@@ -2,6 +2,7 @@
 
 #include <drm/drm_gem.h>
 #include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_panel.h>
 #include <drm/drm_vma_manager.h>
 
 #ifdef CONFIG_DRM
@@ -75,4 +76,18 @@ rust_helper_drm_gem_shmem_object_mmap(struct drm_gem_object 
*obj, struct vm_area
 }
 
 #endif /* CONFIG_DRM_GEM_SHMEM_HELPER */
+
+__rust_helper struct drm_panel *
+rust_helper_of_drm_find_panel(const struct device_node *np)
+{
+       return of_drm_find_panel(np);
+}
+
+__rust_helper int
+rust_helper_of_drm_get_panel_orientation(const struct device_node *np,
+                                        enum drm_panel_orientation 
*orientation)
+{
+       return of_drm_get_panel_orientation(np, orientation);
+}
+
 #endif /* CONFIG_DRM */
diff --git a/rust/kernel/drm/mod.rs b/rust/kernel/drm/mod.rs
index 672ea8728e1c3..d1de5d448a1a5 100644
--- a/rust/kernel/drm/mod.rs
+++ b/rust/kernel/drm/mod.rs
@@ -9,6 +9,7 @@
 pub mod gem;
 pub mod gpuvm;
 pub mod ioctl;
+pub mod panel;
 
 pub use self::device::Device;
 pub use self::device::DeviceContext;
diff --git a/rust/kernel/drm/panel.rs b/rust/kernel/drm/panel.rs
new file mode 100644
index 0000000000000..fd21cc2236685
--- /dev/null
+++ b/rust/kernel/drm/panel.rs
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! DRM panel abstractions.
+//!
+//! C header: [`include/drm/drm_panel.h`](srctree/include/drm/drm_panel.h)
+
+use crate::drm::connector::Connector;
+use crate::{
+    bindings, error, of,
+    prelude::*,
+    sync::aref::{ARef, AlwaysRefCounted},
+    types::Opaque,
+};
+use core::ptr::NonNull;
+
+/// A DRM panel object.
+///
+/// Wraps `struct drm_panel`. Instances are reference-counted via 
[`drm_panel_get`] and
+/// [`drm_panel_put`]; use [`ARef<Panel>`] to hold an owned reference.
+///
+/// The DRM panel methods allow drivers to register panel objects with a
+/// central registry and provide functions to retrieve those panels in display
+/// drivers.
+///
+/// # Invariants
+///
+/// The inner pointer is always a valid, non-null pointer to a `struct 
drm_panel` with a
+/// positive reference count.
+///
+/// [`drm_panel_get`]: srctree/include/drm/drm_panel.h
+/// [`drm_panel_put`]: srctree/include/drm/drm_panel.h
+#[repr(transparent)]
+pub struct Panel(Opaque<bindings::drm_panel>);
+
+impl Panel {
+    /// Creates a reference from a raw pointer.
+    ///
+    /// # Safety
+    ///
+    /// `ptr` must be a valid, non-null `struct drm_panel` pointer that remains
+    /// valid for the lifetime `'a`.
+    pub unsafe fn from_raw<'a>(ptr: *const bindings::drm_panel) -> &'a Self {
+        // SAFETY: Caller guarantees `ptr` is valid and lives for `'a`.
+        unsafe { &*ptr.cast() }
+    }
+
+    /// Returns the raw pointer to the underlying `struct drm_panel`.
+    pub fn as_raw(&self) -> *mut bindings::drm_panel {
+        self.0.get()
+    }
+
+    /// Power on a panel.
+    ///
+    /// Calling this function will enable power and deassert any reset signals 
to
+    /// the panel. After this has completed it is possible to communicate with 
any
+    /// integrated circuitry via a command bus. This function cannot fail (as 
it is
+    /// called from the pre_enable call chain). There will always be a call to
+    /// [`Panel::disable`] afterwards.
+    pub fn prepare(&self) {
+        // SAFETY: The type invariant guarantees the pointer is valid.
+        unsafe { bindings::drm_panel_prepare(self.as_raw()) }
+    }
+
+    /// Power off a panel.
+    ///
+    /// Calling this function will completely power off a panel (assert the 
panel's
+    /// reset, turn off power supplies, ...). After this function has 
completed, it
+    /// is usually no longer possible to communicate with the panel until 
another
+    /// call to [`Panel::prepare`].
+    pub fn unprepare(&self) {
+        // SAFETY: The type invariant guarantees the pointer is valid.
+        unsafe { bindings::drm_panel_unprepare(self.as_raw()) }
+    }
+
+    /// Enable a panel.
+    ///
+    /// Calling this function will cause the panel display drivers to be 
turned on
+    /// and the backlight to be enabled. Content will be visible on screen 
after
+    /// this call completes. This function cannot fail (as it is called from 
the
+    /// enable call chain). There will always be a call to [`Panel::disable`]
+    /// afterwards.
+    pub fn enable(&self) {
+        // SAFETY: The type invariant guarantees the pointer is valid.
+        unsafe { bindings::drm_panel_enable(self.as_raw()) }
+    }
+
+    /// Disable a panel.
+    ///
+    /// This will typically turn off the panel's backlight or disable the 
display
+    /// drivers. For smart panels it should still be possible to communicate 
with
+    /// the integrated circuitry via any command bus after this call.
+    pub fn disable(&self) {
+        // SAFETY: The type invariant guarantees the pointer is valid.
+        unsafe { bindings::drm_panel_disable(self.as_raw()) }
+    }
+
+    /// Probe the available display modes of a panel.
+    ///
+    /// The modes probed from the panel are automatically added to the 
connector
+    /// that the panel is attached to.
+    ///
+    /// Return: The number of modes available from the panel on success, or 0 
on
+    /// failure (no modes).
+    pub fn get_modes(&self, connector: &Connector) -> i32 {
+        // SAFETY: The type invariants guarantee the pointers are valid.
+        unsafe { bindings::drm_panel_get_modes(self.as_raw(), 
connector.as_raw()) } as i32
+    }
+
+    /// Use backlight device node for backlight.
+    ///
+    /// Use this function to enable backlight handling if your panel
+    /// uses device tree and has a backlight phandle.
+    ///
+    /// When the panel is enabled backlight will be enabled after a
+    /// successful call to [`Panel::enable`].
+    ///
+    /// When the panel is disabled backlight will be disabled before the
+    /// call to [`Panel::disable`].
+    ///
+    /// A typical implementation for a panel driver supporting device tree
+    /// will call this function at probe time. Backlight will then be handled
+    /// transparently without requiring any intervention from the driver.
+    #[cfg(CONFIG_BACKLIGHT_CLASS_DEVICE)]
+    pub fn of_backlight(&self) -> Result<()> {
+        // SAFETY: The type invariant guarantees the pointer is valid.
+        error::to_result(unsafe { 
bindings::drm_panel_of_backlight(self.as_raw()) })?;
+        Ok(())
+    }
+
+    /// Look up the panel associated with the given device tree node.
+    ///
+    /// Searches the set of registered panels for one that matches the given 
device
+    /// tree node. If a matching panel is found, return a pointer to it.
+    pub fn from_of_node(node: &of::Node) -> Result<ARef<Self>> {
+        // SAFETY: `node.as_raw()` is a valid device_node pointer.
+        let panel = error::from_err_ptr(unsafe { 
bindings::of_drm_find_panel(node.as_raw()) })?;
+
+        // SAFETY: `from_err_ptr` guarantees a non-null pointer on success.
+        // `of_drm_find_panel` returns a kref-incremented reference.
+        Ok(unsafe { ARef::from_raw(NonNull::new_unchecked(panel).cast()) })
+    }
+}
+
+// SAFETY: By the type invariants, this type is always refcounted.
+unsafe impl AlwaysRefCounted for Panel {
+    fn inc_ref(&self) {
+        // SAFETY: The type invariant guarantees the pointer is valid.
+        unsafe { bindings::drm_panel_get(self.as_raw()) };
+    }
+
+    unsafe fn dec_ref(obj: NonNull<Self>) {
+        // SAFETY: The existence of `obj` guarantees the refcount is positive.
+        unsafe { bindings::drm_panel_put(obj.cast().as_ptr()) };
+    }
+}
+
+/// This enum is used to track the (LCD) panel orientation.
+///
+/// C header: 
[`include/drm/drm_connector.h`](srctree/include/drm/drm_connector.h)
+#[repr(i32)]
+pub enum PanelOrientation {
+    /// The drm driver has not provided any panel orientation information.
+    Unknown = -1,
+    /// The top side of the panel matches the top side of the device's casing.
+    Normal = 0,
+    /// The top side of the panel matches the bottom side of the device's 
casing.
+    BottomUp = 1,
+    /// The left side of the panel matches the top side of the device's casing.
+    LeftUp = 2,
+    /// The right side of the panel matches the top side of the device's 
casing.
+    RightUp = 3,
+}
+
+impl TryFrom<i32> for PanelOrientation {
+    type Error = Error;
+    fn try_from(v: i32) -> Result<Self> {
+        match v {
+            -1 => Ok(Self::Unknown),
+            0 => Ok(Self::Normal),
+            1 => Ok(Self::BottomUp),
+            2 => Ok(Self::LeftUp),
+            3 => Ok(Self::RightUp),
+            _ => Err(EINVAL),
+        }
+    }
+}
+
+impl PanelOrientation {
+    /// Look up the orientation of the panel through the "rotation" binding
+    /// from a device tree node
+    ///
+    /// Looks up the rotation of a panel in the device tree. The orientation 
of the
+    /// panel is expressed as a property name "rotation" in the device tree. 
The
+    /// rotation in the device tree is counter clockwise.
+    pub fn from_of_node(node: &of::Node) -> Result<Self> {
+        let mut orientation = 0i32;
+        // SAFETY: `node.as_raw()` is a valid device_node pointer.
+        error::to_result(unsafe {
+            bindings::of_drm_get_panel_orientation(node.as_raw(), &mut 
orientation)
+        })?;
+        Ok(PanelOrientation::try_from(orientation)?)
+    }
+}
+
+/// A registration of a panel to the global panel registry.
+pub struct Registration(ARef<Panel>);
+
+impl Registration {
+    /// Registers a panel with the global panel registry.
+    pub fn register(panel: ARef<Panel>) -> Self {
+        // SAFETY: The type invariant guarantees the pointer is valid.
+        unsafe { bindings::drm_panel_add(panel.as_raw()) };
+        Self(panel)
+    }
+
+    /// Returns a reference to the panel.
+    pub fn panel(&self) -> &Panel {
+        &self.0
+    }
+}
+
+impl Drop for Registration {
+    fn drop(&mut self) {
+        // SAFETY: The type invariant guarantees the pointer is valid.
+        unsafe { bindings::drm_panel_remove(self.0.as_raw()) };
+    }
+}

-- 
2.55.0

Reply via email to