[PATCH 3/5] rust: drm: add panel consumer abstractions
Albert Esteve <[email protected]>
| Newsgroups | org.kernel.vger.rust-for-linux,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
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