From patchwork Fri Mar 22 22:03:33 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13600439 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 9550EC47DD9 for ; Fri, 22 Mar 2024 22:15:10 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id DAC3511282E; Fri, 22 Mar 2024 22:15:09 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="edPEkYa4"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by gabe.freedesktop.org (Postfix) with ESMTPS id 3C5B711282F for ; Fri, 22 Mar 2024 22:15:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1711145707; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=wB343G/URjvp9S/F5TMB5/43KB5zndfAK18UMcVICOA=; b=edPEkYa43sVcORYu7JQwEYSp8NycUVgbzxURnqO6EzLEu8yINksmtNX2aObDtYAdBNdzt/ wB/waICbbl09FtgJLUgwMqKvTIFDKVeAjQf6x7fmNHj2iCIUhWqQk4kaCtZrrrZI+KFgAR VLYnE31chdOMOVcMnwjbZFTyuStDxx0= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-444-7FBJ-4FAOeW-Wk7awtYhhQ-1; Fri, 22 Mar 2024 18:15:02 -0400 X-MC-Unique: 7FBJ-4FAOeW-Wk7awtYhhQ-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id D3B888007AF; Fri, 22 Mar 2024 22:15:01 +0000 (UTC) Received: from emerald.redhat.com (unknown [10.22.8.130]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8CE928173; Fri, 22 Mar 2024 22:15:00 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org Cc: Miguel Ojeda , Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Asahi Lina , Martin Rodriguez Reboredo , FUJITA Tomonori , Danilo Krummrich , linux-kernel@vger.kernel.org (open list), rust-for-linux@vger.kernel.org (open list:RUST) Subject: [PATCH 1/4] WIP: rust: Add basic KMS bindings Date: Fri, 22 Mar 2024 18:03:33 -0400 Message-ID: <20240322221305.1403600-2-lyude@redhat.com> In-Reply-To: <20240322221305.1403600-1-lyude@redhat.com> References: <20240322221305.1403600-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.5 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Signed-off-by: Lyude Paul --- rust/bindings/bindings_helper.h | 4 + rust/helpers.c | 17 ++ rust/kernel/drm/device.rs | 2 + rust/kernel/drm/drv.rs | 115 +++++++-- rust/kernel/drm/kms.rs | 146 +++++++++++ rust/kernel/drm/kms/connector.rs | 404 +++++++++++++++++++++++++++++++ rust/kernel/drm/kms/crtc.rs | 300 +++++++++++++++++++++++ rust/kernel/drm/kms/encoder.rs | 175 +++++++++++++ rust/kernel/drm/kms/plane.rs | 300 +++++++++++++++++++++++ rust/kernel/drm/mod.rs | 1 + 10 files changed, 1448 insertions(+), 16 deletions(-) create mode 100644 rust/kernel/drm/kms.rs create mode 100644 rust/kernel/drm/kms/connector.rs create mode 100644 rust/kernel/drm/kms/crtc.rs create mode 100644 rust/kernel/drm/kms/encoder.rs create mode 100644 rust/kernel/drm/kms/plane.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index a712efecdb1a9..5856afbe6e8f6 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -6,12 +6,16 @@ * Sorted alphabetically. */ +#include #include #include +#include #include #include +#include #include #include +#include #include #include #include diff --git a/rust/helpers.c b/rust/helpers.c index 69fc66164c785..bf9b299f4597f 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -20,6 +20,7 @@ * Sorted alphabetically. */ +#include #include #include #include @@ -284,6 +285,22 @@ int rust_helper_drm_gem_shmem_object_mmap(struct drm_gem_object *obj, struct vm_ EXPORT_SYMBOL_GPL(rust_helper_drm_gem_shmem_object_mmap); #endif + +#ifdef CONFIG_DRM_KMS_HELPER + +void rust_helper_drm_connector_get(struct drm_connector *connector) +{ + drm_connector_get(connector); +} +EXPORT_SYMBOL_GPL(rust_helper_drm_connector_get); + +void rust_helper_drm_connector_put(struct drm_connector *connector) +{ + drm_connector_put(connector); +} +EXPORT_SYMBOL_GPL(rust_helper_drm_connector_put); + +#endif /* CONFIG_DRM_KMS_HELPER */ #endif void rust_helper_pci_set_drvdata(struct pci_dev *pdev, void *data) diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs index 6176e2e879d0b..07bc8ed50eae0 100644 --- a/rust/kernel/drm/device.rs +++ b/rust/kernel/drm/device.rs @@ -20,6 +20,8 @@ pub struct Device { } impl Device { + pub const HAS_KMS: bool = T::FEATURES & drm::drv::FEAT_MODESET != 0; + #[allow(dead_code, clippy::mut_from_ref)] pub(crate) unsafe fn raw_mut(&self) -> &mut bindings::drm_device { unsafe { &mut *self.drm.get() } diff --git a/rust/kernel/drm/drv.rs b/rust/kernel/drm/drv.rs index fa9ce64a5080c..308f0a117f546 100644 --- a/rust/kernel/drm/drv.rs +++ b/rust/kernel/drm/drv.rs @@ -5,9 +5,13 @@ //! C header: [`include/linux/drm/drm_drv.h`](../../../../include/linux/drm/drm_drv.h) use crate::{ - bindings, device, drm, + bindings, device, + drm::{ + self, + kms, + }, error::code::*, - error::from_err_ptr, + error::{from_err_ptr, to_result}, error::{Error, Result}, prelude::*, private::Sealed, @@ -15,6 +19,7 @@ types::{ARef, ForeignOwnable}, ThisModule, sync::Arc, + init::Zeroable, }; use core::{ marker::{PhantomData, PhantomPinned}, @@ -150,7 +155,11 @@ pub trait Driver { /// The struct which contains both the driver's fops and vtable /// /// These live in the same structure since it needs to be self-referential, so having them in their -/// own structure allows us to pin this struct without pinning the [`Registration`] object +/// own structure allows us to pin this struct without pinning the [`Registration`] object. +/// +/// Drivers should not need to create this structure themselves, as it will be created for them by +/// DRM. As well: this object is a temporary holdover until we can generate the DRM fops and vtable +/// in a const function (which should be possible once const mem::zeroed becomes stable). #[pin_data] pub struct DriverOps { #[pin] @@ -225,8 +234,10 @@ macro_rules! drm_legacy_fields { #[allow(clippy::crate_in_macro_def)] #[macro_export] macro_rules! new_drm_registration { - ($type:ty, $parent:expr) => {{ - $crate::drm::drv::Registration::<$type>::new($parent, &crate::THIS_MODULE) + ($type:ty, $parent:expr, $mode_config_info:expr) => {{ + $crate::drm::drv::Registration::<$type>::new( + $parent, $mode_config_info, &crate::THIS_MODULE + ) }}; } @@ -249,6 +260,8 @@ pub struct RegistrationInfo { drm: ARef>, } +unsafe impl Zeroable for bindings::drm_mode_config { } + impl Registration { const VTABLE: bindings::drm_driver = drm_legacy_fields! { load: None, @@ -282,28 +295,89 @@ impl Registration { fops: core::ptr::null_mut(), }; + const KMS_VTABLE: bindings::drm_mode_config_funcs = bindings::drm_mode_config_funcs { + atomic_check: None, // TODO + // TODO TODO: There are other possibilities then this function, but we need + // to write up more bindings before we can support those + fb_create: Some(bindings::drm_gem_fb_create), + mode_valid: None, // TODO + atomic_commit: Some(bindings::drm_atomic_helper_commit), + get_format_info: None, + atomic_state_free: None, + atomic_state_alloc: None, + atomic_state_clear: None, + output_poll_changed: None, + }; + + const KMS_HELPER_VTABLE: bindings::drm_mode_config_helper_funcs = + bindings::drm_mode_config_helper_funcs { + atomic_commit_setup: None, // TODO + atomic_commit_tail: None, // TODO + }; + + pub const HAS_KMS: bool = T::FEATURES & FEAT_MODESET != 0; + /// Creates a new [`Registration`] but does not register it yet. /// - /// It is allowed to move. - /// XXX: Write up a macro for calling this, since we now handle as much init here as possible to - /// avoid having to handle it after we've moved away the Registration object + /// It is allowed to move. Note that `mode_confg_info` must be provided for a device to be + /// initialized with KMS. pub fn new( parent: &dyn device::RawDevice, + mode_config_info: Option, module: &'static ThisModule, ) -> Result { - let registered = Arc::try_new(AtomicBool::new(false))?; - let ops = DriverOps::try_new(Self::VTABLE, module)?; + // mode_config_info must be passed for KMS drivers. We do this check up here so we don't + // have to worry about leaking raw_drm + // XXX: Would love to know a way to do this at compile-time instead… + if Self::HAS_KMS != mode_config_info.is_some() { + parent.pr_err( + if Self::HAS_KMS { + format_args!("KMS drivers must specify mode_config_info for new devices") + } else { + format_args!("mode_config_info is only for KMS drivers (see drm::drv::Driver::FEATURES)") + } + ); - let raw_drm = unsafe { bindings::drm_dev_alloc(&ops.vtable, parent.raw_device()) }; - if T::FEATURES & FEAT_MODESET != 0 { - unsafe { bindings::drmm_mode_config_init(raw_drm) }; + return Err(EINVAL); } - let raw_drm = NonNull::new(from_err_ptr(raw_drm)? as *mut _).ok_or(ENOMEM)?; + let registered = Arc::try_new(AtomicBool::new(false))?; + let ops = DriverOps::try_new(Self::VTABLE, module)?; + + // SAFETY: FFI call with no special requirements + let raw_drm: NonNull = + from_err_ptr(unsafe { bindings::drm_dev_alloc(&ops.vtable, parent.raw_device()) }) + .and_then(|p| NonNull::new(p).ok_or(ENOMEM))? + .cast(); // The reference count is one, and now we take ownership of that reference as a // drm::device::Device. - let drm = unsafe { ARef::from_raw(raw_drm) }; + let drm: ARef> = unsafe { ARef::from_raw(raw_drm.cast()) }; + + // Finally, setup KMS - we do this at the end to avoid leaking raw_drm if something fails + if Self::HAS_KMS { + // SAFETY: We made sure at the start of this function that mode_config_info is Some + let mode_config_info = unsafe { mode_config_info.unwrap_unchecked() }; + + // SAFETY: We just allocated the device, and it's safe to zero-initialize this + unsafe { + (*drm.drm.get()).mode_config = bindings::drm_mode_config { + funcs: &Self::KMS_VTABLE, + helper_private: &Self::KMS_HELPER_VTABLE, + min_width: mode_config_info.min_resolution.0, + min_height: mode_config_info.min_resolution.1, + max_width: mode_config_info.max_resolution.0, + max_height: mode_config_info.max_resolution.1, + cursor_width: mode_config_info.max_cursor.0, + cursor_height: mode_config_info.max_cursor.1, + preferred_depth: mode_config_info.preferred_depth, + ..Default::default() + } + }; + + // SAFETY: FFI call with no special requirements + unsafe { to_result(bindings::drmm_mode_config_init(drm.drm.get())) }?; + } Ok(Self { drm, @@ -324,6 +398,8 @@ pub fn registration_info(&self) -> RegistrationInfo { /// Registers a DRM device with the rest of the kernel. /// + /// For KMS drivers, this also calls `bindings::drm_mode_config_reset()` before registration. + /// /// Users are encouraged to use the [`drm_device_register!()`] macro because it automatically /// picks up the current module. pub fn register( @@ -390,7 +466,14 @@ fn drop(&mut self) { let data_pointer = unsafe { self.drm.raw_mut().dev_private }; // SAFETY: Since `registered` is true, `self.drm` is both valid and registered. - unsafe { bindings::drm_dev_unregister(self.drm.raw_mut()) }; + unsafe { + let raw_drm = self.drm.raw_mut(); + + bindings::drm_dev_unregister(raw_drm); + if Self::HAS_KMS { + bindings::drm_atomic_helper_shutdown(raw_drm); + } + }; // Free data as well. // SAFETY: `data_pointer` was returned by `into_foreign` during registration. diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs new file mode 100644 index 0000000000000..b55d14415367a --- /dev/null +++ b/rust/kernel/drm/kms.rs @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +//! KMS driver abstractions for rust. + +pub mod connector; +pub mod crtc; +pub mod encoder; +pub mod plane; + +use crate::{ + drm::{drv, device::Device}, + prelude::*, + types::ARef, + private::Sealed +}; +use core::{ + ops::Deref, + ptr, +}; +use bindings; + +#[derive(Copy, Clone)] +pub struct ModeConfigInfo { + /// The minimum (w, h) resolution this driver can support + pub min_resolution: (i32, i32), + /// The maximum (w, h) resolution this driver can support + pub max_resolution: (i32, i32), + /// The maximum (w, h) cursor size this driver can support + pub max_cursor: (u32, u32), + /// The preferred depth for dumb ioctls + pub preferred_depth: u32, +} + +// TODO: I am not totally sure about this. Ideally, I'd like a nice way of hiding KMS-specific +// functions for DRM drivers which don't implement KMS - so that we don't have to have a bunch of +// random modesetting functions all over the DRM device trait. But, unfortunately I don't know of +// any nice way of doing that yet :( + +/// An atomic KMS driver implementation +pub trait KmsDriver: drv::Driver { } + +impl Device { + pub fn mode_config_reset(&self) { + // SAFETY: The previous build assertion ensures this can only be called for devices with KMS + // support, which means mode_config is initialized + unsafe { bindings::drm_mode_config_reset(self.drm.get()) } + } +} + +/// Main trait for a modesetting object in DRM +pub trait ModeObject: Sealed + Send + Sync { + /// The parent driver for this ModeObject + type Driver: KmsDriver; + + /// Return the `drv::Device` for this `ModeObject` + fn drm_dev(&self) -> &Device; +} + +/// A trait for modesetting objects which don't come with their own reference-counting. +/// +/// Objects without a reference count share the lifetime of their parent DRM device +/// +/// SAFETY: This trait must not be implemented for modesetting objects which have a refcount +/// already, as otherwise `KmsRef` can't safely guarantee the object will stay alive. +pub unsafe trait StaticModeObject: ModeObject {} + +/// An owned reference to a non-reference counted modesetting object. +/// +/// In KMS: some modesetting objects aren't reference counted and instead share the drm device's +/// lifetime. In order to allow rust drivers access to "owned" references to objects which are +/// guaranteed to remain valid, we provide a smart-pointer that holds both a pointer to the +/// modesetting object, and an owned refcount from its owning device - ensuring that the object +/// remains valid for as long as this reference exists. +pub struct KmsRef { + dev: ARef>, + object: *const T, +} + +// SAFETY: Owned references to DRM device are thread-safe, and object will survive as long as we +// have said owned references +unsafe impl Send for KmsRef {} +unsafe impl Sync for KmsRef {} + +impl From<&T> for KmsRef { + fn from(value: &T) -> Self { + Self { + dev: value.drm_dev().into(), + object: value, + } + } +} + +impl Deref for KmsRef { + type Target = T; + + fn deref(&self) -> &Self::Target { + // SAFETY: We're guaranteed object will point to a valid object as long as we hold dev + unsafe { &(*self.object) } + } +} + +impl Clone for KmsRef { + fn clone(&self) -> Self { + Self { + dev: self.dev.clone(), + object: self.object + } + } +} + +/// A mode config guard. +/// +/// This is an exclusive primitive that represents when `bindings::drm_device.mode_config.lock` is +/// held - as some modesetting operations (particularly ones related to connectors) are still +/// protected under this single lock. So long as this object is held, it is guaranteed that the lock +/// is held. +pub struct ModeConfigGuard<'a, T: KmsDriver> { + owner: &'a Device, + owned: bool, +} + +impl<'a, T: KmsDriver> ModeConfigGuard<'a, T> { + /// Create a "borrowed" mode config guard. + /// + /// This is primarily for situations in the DRM bindings where we know that the mode_config lock + /// is held, but we aren't the ones who initially acquired it. Dropping this mode config guard + /// is a no-op. + /// + /// SAFETY: The caller must ensure that the mode_config lock is acquired throughout the lifetime + /// of this object. + unsafe fn new_borrowed(dev: &'a Device) -> Self { + Self { + owner: dev, + owned: false, + } + } + + /// Assert that the given device is the owner of this mode config guard. + /// + /// # Panics + /// + /// Panics if `dev` is different from the owning device for this mode config guard. + pub(crate) fn assert_owner(&self, dev: &Device) { + assert!(ptr::eq(self.owner, dev)) + } +} diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs new file mode 100644 index 0000000000000..88dfa946d306b --- /dev/null +++ b/rust/kernel/drm/kms/connector.rs @@ -0,0 +1,404 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +//! Rust bindings for DRM connectors + +use crate::{ + bindings, + sync::ArcBorrow, + drm::{ + drv::{Driver, FEAT_MODESET}, + device::Device, + }, + types::{AlwaysRefCounted, Opaque, ARef}, + prelude::*, + init::Zeroable, + error::{to_result, from_result}, + build_error, +}; +use core::{ + marker::PhantomPinned, + ptr::null_mut, + mem, + ptr::{self, NonNull}, + ffi::*, + ops::Deref, +}; +use super::{ + ModeObject, + ModeConfigGuard, + encoder::{Encoder, DriverEncoder}, + KmsDriver, +}; +use macros::pin_data; + +// XXX: This is :\, figure out a better way at some point? +pub use bindings::{ + DRM_MODE_CONNECTOR_Unknown, + DRM_MODE_CONNECTOR_VGA, + DRM_MODE_CONNECTOR_DVII, + DRM_MODE_CONNECTOR_DVID, + DRM_MODE_CONNECTOR_DVIA, + DRM_MODE_CONNECTOR_Composite, + DRM_MODE_CONNECTOR_SVIDEO, + DRM_MODE_CONNECTOR_LVDS, + DRM_MODE_CONNECTOR_Component, + DRM_MODE_CONNECTOR_9PinDIN, + DRM_MODE_CONNECTOR_DisplayPort, + DRM_MODE_CONNECTOR_HDMIA, + DRM_MODE_CONNECTOR_HDMIB, + DRM_MODE_CONNECTOR_TV, + DRM_MODE_CONNECTOR_eDP, + DRM_MODE_CONNECTOR_VIRTUAL, + DRM_MODE_CONNECTOR_DSI, + DRM_MODE_CONNECTOR_DPI, + DRM_MODE_CONNECTOR_WRITEBACK, + DRM_MODE_CONNECTOR_SPI, + DRM_MODE_CONNECTOR_USB, +}; + +/// A DRM connector implementation +pub trait DriverConnector: Send + Sync + Sized { + /// The return type of the new() function. Should be `impl PinInit`. + /// TODO: Remove this when return_position_impl_trait_in_trait is stable. + type Initializer: PinInit; + + /// The data type to use for passing incoming arguments for new `Connector` instances + /// Drivers which don't care about this can just use `()` + type Args; + + /// The parent driver for this DRM connector implementation + type Driver: KmsDriver; + + /// The atomic state implementation for this DRM connector implementation + type State: DriverConnectorState; + + /// Create a new instance of the private driver data struct for this connector in-place + fn new(dev: &Device, args: Self::Args) -> Self::Initializer; + + /// Retrieve a list of available display modes for this connector + fn get_modes<'a>( + connector: ConnectorGuard<'a, Self>, + guard: &ModeConfigGuard<'a, Self::Driver> + ) -> i32; +} + +/// A DRM connector +#[repr(C)] +#[pin_data] +pub struct Connector { + connector: Opaque, + #[pin] + inner: T, + #[pin] + _p: PhantomPinned +} + +impl crate::private::Sealed for Connector { } + +// SAFETY: DRM expects this struct to be zero-initialized +unsafe impl Zeroable for bindings::drm_connector { } + +// SAFETY: Connector.connector is not exposed to users by default, and our accessors ensure we only +// perform thread-safe operations for this object +unsafe impl Send for Connector { } +unsafe impl Sync for Connector { } + +unsafe impl AlwaysRefCounted for Connector { + fn inc_ref(&self) { + unsafe { bindings::drm_connector_get(self.raw_mut_ptr()) } + } + + unsafe fn dec_ref(obj: core::ptr::NonNull) { + unsafe { bindings::drm_connector_put(obj.as_ref().raw_mut_ptr()) } + } +} + +impl Deref for Connector { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl ModeObject for Connector { + type Driver = T::Driver; + + fn drm_dev(&self) -> &Device { + // SAFETY: DRM connectors exist for as long as the device does, so this pointer is always + // valid + unsafe { &*((*self.raw_mut_ptr()).dev.cast()) } + } +} + +impl Connector { + const FUNCS: bindings::drm_connector_funcs = bindings::drm_connector_funcs { + dpms: None, + atomic_get_property: None, + atomic_set_property: None, + early_unregister: None, + late_register: None, + set_property: None, + reset: Some(connector_reset_callback::), + atomic_print_state: None, + atomic_destroy_state: Some(atomic_destroy_state_callback::), + destroy: Some(connector_destroy_callback::), + force: None, + detect: 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::), + }; + + const HELPER_FUNCS: bindings::drm_connector_helper_funcs = bindings::drm_connector_helper_funcs { + mode_valid: None, + atomic_check: None, + get_modes: Some(get_modes_callback::), + detect_ctx: None, + enable_hpd: None, + disable_hpd: None, + best_encoder: None, + atomic_commit: None, + mode_valid_ctx: None, + atomic_best_encoder: None, + prepare_writeback_job: None, + cleanup_writeback_job: None, + }; + + pub fn new( + dev: &Device, + type_: u32, + args: T::Args, + ) -> Result> { + let new: Pin> = Box::try_pin_init(try_pin_init!(Self { + connector: Opaque::new(bindings::drm_connector { + helper_private: &Self::HELPER_FUNCS, + ..Default::default() + }), + inner <- T::new(dev, args), + _p: PhantomPinned + }))?; + + // SAFETY: FFI call with no special safety requirements + to_result(unsafe { + bindings::drm_connector_init( + dev.drm.get(), + new.raw_mut_ptr(), + &Self::FUNCS, + type_ as i32 + ) + })?; + + // Convert the connector into an ARef so the caller has proper ownership over a refcount to + // it. Also, the Box we consume here will be reconstructed in connector_destroy_callback() + // once the connector's refcount drops to zero. + // SAFETY: We currently hold ownership of the Box containing the connector and it's + // refcount. As well, this operation will not move the contents of the Box. + Ok(unsafe { + ARef::from_raw(NonNull::new_unchecked(Box::into_raw(Pin::into_inner_unchecked(new)))) + }) + } + + pub(super) unsafe fn raw_mut_ptr(&self) -> *mut bindings::drm_connector { + self.connector.get() + } + + pub(super) unsafe fn from_raw_ptr<'a>(ptr: *const bindings::drm_connector) -> &'a Self { + unsafe { &*(ptr as *mut Self) } + } + + /// Acquire a `ConnectorGuard` for this connector from a `ModeConfigGuard`. + /// + /// This verifies using the provided reference that the given guard is actually for the same + /// device as this connector's parent. + /// + /// # Panics + /// + /// Panics if `guard` is not a mode config guard for this connector's parent DRM device + pub fn guard<'a>(&'a self, guard: &ModeConfigGuard<'a, T::Driver>) -> ConnectorGuard<'a, T> { + guard.assert_owner(self.drm_dev()); + ConnectorGuard { connector: self } + } + + /// Attach an encoder to this connector, this should only be done before registration + #[must_use] + pub fn attach_encoder(&self, encoder: &Encoder) -> Result { + // SAFETY: FFI call with no special requirements + to_result(unsafe { + bindings::drm_connector_attach_encoder(self.raw_mut_ptr(), encoder.raw_mut_ptr()) + }) + } +} + +unsafe extern "C" fn connector_destroy_callback( + connector: *mut bindings::drm_connector, +) { + // SAFETY: Connector has to point to a valid drm_connector, as its function table is the only + // existing entrypoint to this function + unsafe { + bindings::drm_connector_unregister(connector); + bindings::drm_connector_cleanup(connector); + }; + + // SAFETY: We always create connectors in boxes, and since we are running from this connector's + // destructor we are guaranteed to have the last remaining reference. Furthermore, we're + // guaranteed by type invariance that the contents of the box are of type Connector. + unsafe { drop(Box::from_raw(connector as *mut Connector)) }; +} + +unsafe extern "C" fn get_modes_callback( + connector: *mut bindings::drm_connector, +) -> c_int { + // SAFETY: We're guaranteed by type invariants that connector is of type Connector, and + // connector must point to a valid instance of Connector as it's the only entry-point to this + // callback. + let connector = unsafe { Connector::::from_raw_ptr(connector) }; + + // SAFETY: This FFI callback is only called while the mode config lock is held + let guard = unsafe { ModeConfigGuard::new_borrowed(connector.drm_dev()) }; + + T::get_modes(connector.guard(&guard), &guard) +} + +/// A privileged smart-pointer for `Connector` which proves that the owner currently is protected +/// under the `bindings::drm_device.mode_config.mutex` lock and provides access to data and methods +/// protected under said lock. +#[derive(Copy, Clone)] +pub struct ConnectorGuard<'a, T: DriverConnector> { + connector: &'a Connector, +} + +impl Deref for ConnectorGuard<'_, T> { + type Target = Connector; + + fn deref(&self) -> &Self::Target { + self.connector + } +} + +impl<'a, T: DriverConnector> ConnectorGuard<'a, T> { + pub fn add_modes_noedid(&self, (max_h, max_v): (i32, i32)) -> i32 { + unsafe { bindings::drm_add_modes_noedid(self.raw_mut_ptr(), max_h, max_v) } + } + + pub fn set_preferred_mode(&self, (h_pref, w_pref): (i32, i32)) { + unsafe { bindings::drm_set_preferred_mode(self.raw_mut_ptr(), h_pref, w_pref) } + } +} + +#[derive(Default)] +#[repr(C)] +pub struct ConnectorState { + state: bindings::drm_connector_state, + inner: T, +} + +/// The trait for a driver's atomic DRM connector state +pub trait DriverConnectorState: Clone + Default + Sized { + type Connector: DriverConnector; +} + +impl ConnectorState { + /// Consume this struct without dropping it, and return a pointer to its base + /// `drm_connector_state` which can be handed off to DRM. + fn into_raw(self: Box) -> *mut bindings::drm_connector_state { + let this = Box::into_raw(self); + + unsafe { &mut (*this).state } + } + + /// Consume a raw pointer and recover the original `Box>` + /// + /// SAFETY: Callers must ensure that ptr contains a valid instance of `ConnectorState` + unsafe fn from_raw(ptr: *mut bindings::drm_connector_state) -> Box { + unsafe { Box::from_raw(ptr as *mut _) } + } + + /// Obtain a reference back to the `ConnectorState` + /// + /// SAFETY: Callers must ensure that ptr contains a valid instance of `ConnectorState`. + unsafe fn as_ref<'a>(ptr: *const bindings::drm_connector_state) -> &'a Self { + unsafe { &*(ptr as *const _) } + } + + /// Obtain a mutable reference back to the ConnectorState + /// + /// SAFETY: Callers must ensure that ptr contains a valid instance of `ConnectorState`, and + /// that no other references to this `ConnectorState` exist for the lifetime of this + /// reference + unsafe fn as_mut_ref<'a>(ptr: *mut bindings::drm_connector_state) -> &'a mut Self { + unsafe { &mut *(ptr as *mut _) } + } + + /// Obtain a mutable pointer to the base connector state, for use in FFI calls + unsafe fn as_mut_ptr(&mut self) -> *mut bindings::drm_connector_state { + &mut self.state + } +} + +unsafe impl Zeroable for bindings::drm_connector_state {} + +unsafe extern "C" fn atomic_duplicate_state_callback( + connector: *mut bindings::drm_connector +) -> *mut bindings::drm_connector_state +{ + // SAFETY: `connector` has to point to a valid instance of drm_connector, since it holds the vtable for + // this function - which is the only possible entrypoint the caller could have used + let state = unsafe { (*connector).state }; + if state.is_null() { + return null_mut(); + } + + // SAFETY: We just verified that `state` is non-null, and we're guaranteed by our bindings that + // `state` is of type `ConnectorState`. + let state = unsafe { ConnectorState::::as_ref(state) }; + + let mut new: Result>> = Box::try_init(try_init!(ConnectorState:: { + state: bindings::drm_connector_state { ..Default::default() }, + inner: state.inner.clone() + })); + + if let Ok(mut new) = new { + // SAFETY: Just a lil' FFI call, nothing special here + unsafe { + bindings::__drm_atomic_helper_connector_duplicate_state(connector, new.as_mut_ptr()) + }; + + new.into_raw() + } else { + null_mut() + } +} + +unsafe extern "C" fn atomic_destroy_state_callback( + _connector: *mut bindings::drm_connector, + connector_state: *mut bindings::drm_connector_state +) { + // SAFETY: This callback wouldn't be called unless there a connector state to destroy + unsafe { bindings::__drm_atomic_helper_connector_destroy_state(connector_state) }; + + // SAFETY: We're guaranteed by type invariants that connector_state is of type + // ConnectorState, and since this is the destructor callback for DRM - we're guaranteed to + // hold the only remaining reference to this state + unsafe { drop(ConnectorState::::from_raw(connector_state)) }; +} + +unsafe extern "C" fn connector_reset_callback( + connector: *mut bindings::drm_connector, +) { + // SAFETY: The only entrypoint to this function lives in `connector` so it must be valid, and + let state = unsafe { (*connector).state }; + if !state.is_null() { + // SAFETY: We're guaranteed by type invariance that this connector's state is of type + // DriverConnectorState + unsafe { atomic_destroy_state_callback::(connector, state) } + } + + // Unfortunately, this is the best we can do at the moment as this FFI callback was mistakenly + // presumed to be infallible :( + let new = Box::try_new(ConnectorState::::default()).expect("Blame the API, sorry!"); + + // SAFETY: DRM takes ownership of the state from here and assigns it to the connector + unsafe { bindings::__drm_atomic_helper_connector_reset(connector, new.into_raw()) }; +} diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs new file mode 100644 index 0000000000000..3d072028a4884 --- /dev/null +++ b/rust/kernel/drm/kms/crtc.rs @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +//! KMS driver abstractions for rust. + +use super::{ + plane::*, + ModeObject, + StaticModeObject, + KmsDriver +}; +use crate::{ + bindings, + drm::{drv::Driver, device::Device}, + device, + prelude::*, + types::Opaque, + init::Zeroable, + sync::Arc, + error::to_result, +}; +use core::{ + cell::UnsafeCell, + marker::PhantomPinned, + ptr::{null, null_mut}, + ops::Deref, +}; +use macros::vtable; + +/// A typed KMS CRTC with a specific driver. +#[repr(C)] +#[pin_data] +pub struct Crtc { + // The FFI drm_crtc object + pub(super) crtc: Opaque, + /// The driver's private inner data + #[pin] + inner: T, + #[pin] + _p: PhantomPinned, +} + +/// KMS CRTC object functions, which must be implemented by drivers. +pub trait DriverCrtc: Send + Sync + Sized { + /// The return type of the new() function. Should be `impl PinInit`. + /// TODO: Remove this when return_position_impl_trait_in_trait is stable. + type Initializer: PinInit; + + /// The data type to use for passing incoming arguments for new `Crtc` instances + /// Drivers which don't care about this can just use `()` + type Args; + + /// The parent driver implementation + type Driver: KmsDriver; + + /// The type for this driver's drm_crtc_state implementation + type State: DriverCrtcState; + + /// Create a new CRTC for this driver + fn new(device: &Device, args: Self::Args) -> Self::Initializer; +} + +unsafe impl Zeroable for bindings::drm_crtc { } + +impl crate::private::Sealed for Crtc {} + +// SAFETY: Crtc.crtc is not exposed to users by default, and our accessors ensure we only perform +// thread-safe operations for this object +unsafe impl Send for Crtc { } +unsafe impl Sync for Crtc { } + +impl Deref for Crtc { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl ModeObject for Crtc { + type Driver = T::Driver; + + fn drm_dev(&self) -> &Device { + // SAFETY: DRM connectors exist for as long as the device does, so this pointer is always + // valid + unsafe { &*((*self.raw_mut_ptr()).dev as *const _) } + } +} + +// SAFETY: CRTCs are non-refcounted modesetting objects +unsafe impl StaticModeObject for Crtc { } + +impl Crtc { + /// The actual C vtable for drm_crtc_funcs + const FUNCS: bindings::drm_crtc_funcs = bindings::drm_crtc_funcs { + atomic_destroy_state: Some(atomic_destroy_state_callback::), + atomic_duplicate_state: Some(atomic_duplicate_state_callback::), + atomic_get_property: None, + atomic_print_state: None, + atomic_set_property: None, + cursor_move: None, + cursor_set2: None, + cursor_set: None, + destroy: Some(crtc_destroy_callback::), + disable_vblank: None, + early_unregister: None, + enable_vblank: None, + gamma_set: None, // TODO + get_crc_sources: None, + get_vblank_counter: None, + get_vblank_timestamp: None, + late_register: None, + page_flip: Some(bindings::drm_atomic_helper_page_flip), + page_flip_target: None, + reset: Some(crtc_reset_callback::), + set_config: Some(bindings::drm_atomic_helper_set_config), + set_crc_source: None, + set_property: None, + verify_crc_source: None, + }; + + /// The actual C vtable for drm_crtc_helper_funcs + const HELPER_FUNCS: bindings::drm_crtc_helper_funcs = bindings::drm_crtc_helper_funcs { + atomic_disable: None, + atomic_enable: None, + atomic_check: None, + dpms: None, + commit: None, + prepare: None, + disable: None, + mode_set: None, + mode_valid: None, + mode_fixup: None, + atomic_begin: None, + atomic_flush: None, + mode_set_nofb: None, + mode_set_base: None, + mode_set_base_atomic: None, + get_scanout_position: None, + }; + + pub fn new<'a, P, C>( + dev: &'a Device, + primary: &'a Plane

, + cursor: Option<&'a Plane>, + name: Option<&CStr>, + args: T::Args, + ) -> Result<&'a Self> + where + P: DriverPlane, + C: DriverPlane + { + let this = Box::try_pin_init(try_pin_init!(Self { + crtc: Opaque::new(bindings::drm_crtc { + helper_private: &Self::HELPER_FUNCS, + ..Default::default() + }), + inner <- T::new(dev, args), + _p: PhantomPinned, + }))?; + + to_result(unsafe { + bindings::drm_crtc_init_with_planes( + dev.drm.get(), + this.raw_mut_ptr(), + primary.raw_mut_ptr(), + cursor.map_or(null_mut(), |c| c.raw_mut_ptr()), + &Self::FUNCS, + name.map_or(null(), |n| n.as_char_ptr()) + ) + })?; + + // Convert the box into a raw pointer, we'll re-assemble it in crtc_destroy_callback() + // SAFETY: We don't move anything + Ok(unsafe { &*Box::into_raw(Pin::into_inner_unchecked(this)) }) + } + + pub(super) fn raw_mut_ptr(&self) -> *mut bindings::drm_crtc { + self.crtc.get() + } +} + +unsafe extern "C" fn crtc_destroy_callback( + crtc: *mut bindings::drm_crtc +) { + // SAFETY: FFI call with no special requirements + unsafe { bindings::drm_crtc_cleanup(crtc) }; + + // SAFETY: We're guaranteed by type invariants this plane is contained within an Box> + unsafe { drop(Box::from_raw(crtc as *mut Crtc)) }; +} + +unsafe impl Zeroable for bindings::drm_crtc_state { } + +pub trait DriverCrtcState: Clone + Default + Sized { + type Crtc: DriverCrtc; +} + +#[derive(Default)] +#[repr(C)] +pub struct CrtcState { + state: bindings::drm_crtc_state, + inner: T, +} + +impl CrtcState { + /// Consume this struct without dropping it, and return a pointer to its base `drm_crtc_state` + /// which can be handed off to DRM. + fn into_raw(self: Box) -> *mut bindings::drm_crtc_state { + let this = Box::into_raw(self); + + unsafe { &mut (*this).state } + } + + /// Consume a raw pointer and recover the original `Box>` + /// + /// SAFETY: Callers must ensure that ptr contains a valid instance of `CrtcState` + unsafe fn from_raw(ptr: *mut bindings::drm_crtc_state) -> Box { + unsafe { Box::from_raw(ptr as *mut _) } + } + + /// Obtain a reference back to the `CrtcState` + /// + /// SAFETY: Callers must ensure that ptr contains a valid instance of `CrtcState`. + unsafe fn as_ref<'a>(ptr: *const bindings::drm_crtc_state) -> &'a Self { + unsafe { &*(ptr as *const _) } + } + + /// Obtain a mutable reference back to the CrtcState + /// + /// SAFETY: Callers must ensure that ptr contains a valid instance of `CrtcState`, and that + /// no other references to this `CrtcState` exist for the lifetime of this reference + unsafe fn as_mut_ref<'a>(ptr: *mut bindings::drm_crtc_state) -> &'a mut Self { + unsafe { &mut *(ptr as *mut _) } + } + + /// Obtain a mutable pointer to the base plane state, for use in FFI calls + unsafe fn as_mut_ptr(&mut self) -> *mut bindings::drm_crtc_state { + &mut self.state + } +} + +unsafe extern "C" fn atomic_duplicate_state_callback( + crtc: *mut bindings::drm_crtc +) -> *mut bindings::drm_crtc_state { + // SAFETY: `crtc` has to point to a valid instance of drm_crtc, since it holds the vtable for + // this function - which is the only possible entrypoint the caller could have used + let state = unsafe { (*crtc).state }; + if state.is_null() { + return null_mut(); + } + + // SAFETY: We just verified that `state` is non-null, and we're guaranteed by our bindings that + // `state` is of type `CrtcState`. + let state = unsafe { CrtcState::::as_ref(state) }; + + let mut new: Result>> = Box::try_init(try_init!(CrtcState:: { + state: bindings::drm_crtc_state { ..Default::default() }, + inner: state.inner.clone() + })); + + if let Ok(mut new) = new { + unsafe { bindings::__drm_atomic_helper_crtc_duplicate_state(crtc, new.as_mut_ptr()) } + + new.into_raw() + } else { + null_mut() + } +} + +unsafe extern "C" fn atomic_destroy_state_callback( + _crtc: *mut bindings::drm_crtc, + crtc_state: *mut bindings::drm_crtc_state, +) { + // SAFETY: This callback wouldn't be called unless there a CRTC state to destroy + unsafe { bindings::__drm_atomic_helper_crtc_destroy_state(crtc_state) }; + // + // SAFETY: We're guaranteed by type invariants that crtc_state is of type CrtcState, and + // since this is the destructor callback for DRM - we're guaranteed to hold the only remaining + // reference to this state + drop(unsafe { CrtcState::::from_raw(crtc_state) }); +} + +unsafe extern "C" fn crtc_reset_callback( + crtc: *mut bindings::drm_crtc, +) { + // SAFETY: The only entrypoint to this function lives in `crtc` so it must be valid, and + let state = unsafe { (*crtc).state }; + if !state.is_null() { + // SAFETY: We're guaranteed by type invariance that this crtc's state is of type + // DriverConnectorState + unsafe { atomic_destroy_state_callback::(crtc, state) } + } + + // Unfortunately, this is the best we can do at the moment as this FFI callback was mistakenly + // presumed to be infallible :( + let new = Box::try_new(CrtcState::::default()).expect("Blame the API, sorry!"); + + // SAFETY: DRM takes ownership of the state from here and assigns it to the crtc + unsafe { bindings::__drm_atomic_helper_crtc_reset(crtc, new.into_raw()) }; +} diff --git a/rust/kernel/drm/kms/encoder.rs b/rust/kernel/drm/kms/encoder.rs new file mode 100644 index 0000000000000..7a5bc0ca1577b --- /dev/null +++ b/rust/kernel/drm/kms/encoder.rs @@ -0,0 +1,175 @@ +// TODO: License stuff + +//! drm_encoder abstractions for rust + +use crate::{ + drm::{ + device::Device, + drv::Driver, + }, + prelude::*, + sync::Arc, + types::Opaque, + init::Zeroable, + error::to_result, +}; +use core::{ + marker::PhantomPinned, + ptr::null, + ops::Deref, +}; +use super::{ModeObject, StaticModeObject, KmsDriver}; +use bindings; + +pub use bindings::{ + DRM_MODE_ENCODER_NONE, + DRM_MODE_ENCODER_DAC, + DRM_MODE_ENCODER_TMDS, + DRM_MODE_ENCODER_LVDS, + DRM_MODE_ENCODER_TVDAC, + DRM_MODE_ENCODER_VIRTUAL, + DRM_MODE_ENCODER_DSI, + DRM_MODE_ENCODER_DPMST, + DRM_MODE_ENCODER_DPI, +}; + +/// A DRM encoder (`drm_encoder`) +/// +/// This is the main struct for DRM encoders, which may also hold any private data specified by the +/// driver. +#[repr(C)] +#[pin_data] +pub struct Encoder { + /// The FFI drm_encoder object + encoder: Opaque, + /// The driver's private inner data + #[pin] + inner: T, + #[pin] + _p: PhantomPinned, +} + +/// The main trait for KMS drivers to implement for their display encoders. +pub trait DriverEncoder: Send + Sync + Sized { + /// The return type of the new() function. Should be `impl PinInit`. + /// TODO: Remove this when return_position_impl_trait_in_trait is stable. + type Initializer: PinInit; + + /// The parent driver for this drm_encoder implementation + type Driver: KmsDriver; + + /// The type used for passing arguments to the driver's constructor + /// Drivers which don't care about this can just use `()` + type Args; + + /// Create a new encoder for this driver + fn new(device: &Device, args: Self::Args) -> Self::Initializer; +} + +impl crate::private::Sealed for Encoder {} + +unsafe impl Zeroable for bindings::drm_encoder {} + +// SAFETY: Encoder.encoder is not exposed to users by default, and our accessors ensure we only +// perform thread-safe operations for this object +unsafe impl Send for Encoder { } +unsafe impl Sync for Encoder { } + +impl ModeObject for Encoder { + type Driver = T::Driver; + + fn drm_dev(&self) -> &Device { + // SAFETY: DRM encoders exist for as long as the device does, so this pointer is always + // valid + unsafe { &*((*self.raw_mut_ptr()).dev.cast()) } + } +} + +// SAFETY: Encoders do not have a refcount +unsafe impl StaticModeObject for Encoder { } + +impl Deref for Encoder { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl Encoder { + const FUNCS: bindings::drm_encoder_funcs = bindings::drm_encoder_funcs { + reset: None, + destroy: Some(encoder_destroy_callback::), + late_register: None, + early_unregister: None, + debugfs_init: None, + }; + + const HELPER_FUNCS: bindings::drm_encoder_helper_funcs = bindings::drm_encoder_helper_funcs { + dpms: None, + mode_valid: None, + mode_fixup: None, + prepare: None, + mode_set: None, + commit: None, + detect: None, + enable: None, + disable: None, + atomic_check: None, + atomic_enable: None, + atomic_disable: None, + atomic_mode_set: None, + }; + + pub fn new<'a>( + dev: &'a Device, + type_: u32, + possible_crtcs: u32, + possible_clones: u32, + name: Option<&CStr>, + args: T::Args, + ) -> Result<&'a Self> { + let this: Pin> = Box::try_pin_init(try_pin_init!(Self { + encoder: Opaque::new(bindings::drm_encoder { + helper_private: &Self::HELPER_FUNCS, + possible_crtcs, + possible_clones, + ..Default::default() + }), + inner <- T::new(dev, args), + _p: PhantomPinned + }))?; + + // SAFETY: FFI call with no special requirements + to_result(unsafe { + bindings::drm_encoder_init( + dev.drm.get(), + this.raw_mut_ptr(), + &Self::FUNCS, + type_ as _, + name.map_or(null(), |n| n.as_char_ptr()) + ) + })?; + + // Convert the box into a raw pointer, we'll re-assemble it in encoder_destroy_callback() + // SAFETY: We don't move anything + Ok(unsafe { &*Box::into_raw(Pin::into_inner_unchecked(this)) }) + } + + pub(crate) unsafe fn raw_mut_ptr(&self) -> *mut bindings::drm_encoder { + self.encoder.get() + } +} + +unsafe extern "C" fn encoder_destroy_callback( + encoder: *mut bindings::drm_encoder +) { + // SAFETY: encoder contains the only possible entrypoint to this function, so the pointer must + // be valid + unsafe { bindings::drm_encoder_cleanup(encoder) }; + + // Reclaim ownership of the reference we took in Encoder::::new() so we can drop it + // SAFETY: We always create encoders in Arc, and we're guaranteed by type invariants that + // this encoder is a Encoder + unsafe { drop(Box::from_raw(encoder as *mut Encoder)) }; +} diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs new file mode 100644 index 0000000000000..78c8e370b997c --- /dev/null +++ b/rust/kernel/drm/kms/plane.rs @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +//! KMS atomic plane abstractions for rust. + +use alloc::boxed::Box; +use crate::{ + bindings, + drm::{device::Device, drv::Driver}, + error::{to_result, from_err_ptr, Error}, + init::Zeroable, + prelude::*, + types::{ARef, Opaque}, + sync::{Arc, ArcBorrow}, + init::InPlaceInit, + offset_of, +}; +use core::{ + cell::UnsafeCell, + mem::{self, size_of, MaybeUninit}, + ptr::{NonNull, null, null_mut, addr_of_mut}, + marker::PhantomPinned, + ops::Deref, + ffi::c_int, +}; +use macros::pin_data; +use super::{KmsDriver, ModeObject, StaticModeObject}; + +/// The main structure containing a drm_plane that is exposed to callers. It is intentionally +/// impossible to acquire a mutable reference to this structure, and as such this structure should +/// only be exposed through immutable references. +#[repr(C)] +#[pin_data] +pub struct Plane { + /// The FFI drm_plane object + pub(super) plane: Opaque, + /// The driver's private inner data + #[pin] + inner: T, + #[pin] + _p: PhantomPinned, +} + +unsafe impl Zeroable for bindings::drm_plane {} + +// SAFETY: Plane.plane is not exposed to users by default, and our accessors ensure we only +// perform thread-safe operations for this object +unsafe impl Send for Plane { } +unsafe impl Sync for Plane { } + +/// The main trait for implementing the drm_plane API. This contains the various trait methods that +/// need to be implemented by a driver. The private driver data for the plane is contained in +/// whatever struct the driver defines which implements this trait. +pub trait DriverPlane: Send + Sync + Sized { + /// The return type of the new() function. Should be `impl PinInit`. + /// TODO: Remove this when return_position_impl_trait_in_trait is stable. + type Initializer: PinInit; + + /// The data type to use for passing incoming arguments for new `Plane` instances + /// Drivers which don't care about this can just use `()` + type Args; + + /// The parent driver implementation + type Driver: KmsDriver; + + /// The type for this driver's drm_plane_state implementation + type State: DriverPlaneState; + + /// Create a new plane for this driver + fn new(device: &Device, args: Self::Args) -> Self::Initializer; +} + +impl crate::private::Sealed for Plane {} + +impl ModeObject for Plane { + type Driver = T::Driver; + + fn drm_dev(&self) -> &Device { + // SAFETY: DRM planes exist for as long as the device does, so this pointer is always valid + unsafe { &*((*self.raw_mut_ptr()).dev as *const _) } + } +} + +// SAFETY: Planes do not have a refcount +unsafe impl StaticModeObject for Plane { } + +impl Deref for Plane { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl Plane { + /// The actual C vtable for drm_plane_funcs + const FUNCS: bindings::drm_plane_funcs = bindings::drm_plane_funcs { + update_plane: Some(bindings::drm_atomic_helper_update_plane), + disable_plane: Some(bindings::drm_atomic_helper_disable_plane), + destroy: Some(plane_destroy_callback::), + reset: Some(plane_reset_callback::), + set_property: None, + atomic_duplicate_state: Some(atomic_duplicate_state_callback::), + atomic_destroy_state: Some(atomic_destroy_state_callback::), + atomic_set_property: None, // TODO someday + atomic_get_property: None, // TODO someday + late_register: None, // TODO someday + early_unregister: None, // TODO someday + atomic_print_state: None, // TODO: Display someday??? + format_mod_supported: None // TODO someday + }; + + const HELPER_FUNCS: bindings::drm_plane_helper_funcs = bindings::drm_plane_helper_funcs { + prepare_fb: None, // TODO someday? + cleanup_fb: None, // TODO someday? + begin_fb_access: None, // TODO: someday? + end_fb_access: None, // TODO: someday? + atomic_check: None, // TODO + atomic_update: None, // TODO + atomic_enable: None, // TODO + atomic_disable: None, // TODO + atomic_async_check: None, // TODO + atomic_async_update: None, // TODO + }; + + pub fn new<'a>( + dev: &'a Device, + possible_crtcs: u32, + formats: &'static [u32], + format_modifiers: Option<&'static [u64]>, + type_: PlaneType, + name: Option<&CStr>, + args: T::Args, + ) -> Result<&'a Self> { + let this: Pin> = Box::try_pin_init(try_pin_init!(Self { + plane: Opaque::new(bindings::drm_plane { + helper_private: &Self::HELPER_FUNCS, + ..Default::default() + }), + inner <- T::new(dev, args), + _p: PhantomPinned + }))?; + + // SAFETY: FFI call with no special requirements + to_result(unsafe { + bindings::drm_universal_plane_init( + dev.drm.get(), + this.raw_mut_ptr(), + possible_crtcs, + &Self::FUNCS, + formats.as_ptr(), + formats.len() as _, + format_modifiers.map_or(null(), |f| f.as_ptr()), + type_ as _, + name.map_or(null(), |n| n.as_char_ptr()) + ) + })?; + + // Convert the box into a raw pointer, we'll re-assemble it in plane_destroy_callback() + // SAFETY: We don't move anything + Ok(unsafe { &*Box::into_raw(Pin::into_inner_unchecked(this)) }) + } + + pub(super) fn raw_mut_ptr(&self) -> *mut bindings::drm_plane { + self.plane.get() + } +} + +unsafe extern "C" fn plane_destroy_callback( + plane: *mut bindings::drm_plane +) { + // SAFETY: plane contains the only possible entrypoint to this function, so the pointer must be + // valid + unsafe { bindings::drm_plane_cleanup(plane) }; + + // Reclaim ownership of the reference we took in Plane::::new() so we can drop it + // SAFETY: We're guaranteed by type invariants this plane is contained within an Box> + unsafe { drop(Box::from_raw(plane as *mut Plane)) }; +} + +#[derive(Default)] +#[repr(C)] +pub struct PlaneState { + state: bindings::drm_plane_state, + inner: T, +} + +/// Traits which must be implemented by KMS drivers for DRM planes. +pub trait DriverPlaneState: Clone + Default + Sized { + /// The type for this driver's drm_plane implementation + type Plane: DriverPlane; +} + +impl PlaneState { + /// Consume this struct without dropping it, and return a pointer to its base `drm_plane_state` + /// which can be handed off to DRM. + fn into_raw(self: Box) -> *mut bindings::drm_plane_state { + let this = Box::into_raw(self); + + unsafe { &mut (*this).state } + } + + /// Consume a raw pointer and recover the original `Box>` + /// + /// SAFETY: Callers must ensure that ptr contains a valid instance of `PlaneState` + unsafe fn from_raw(ptr: *mut bindings::drm_plane_state) -> Box { + unsafe { Box::from_raw(ptr as *mut _) } + } + + /// Obtain a reference back to the `PlaneState` + /// + /// SAFETY: Callers must ensure that ptr contains a valid instance of `PlaneState`. + unsafe fn as_ref<'a>(ptr: *const bindings::drm_plane_state) -> &'a Self { + unsafe { &*(ptr as *const _) } + } + + /// Obtain a mutable reference back to the PlaneState + /// + /// SAFETY: Callers must ensure that ptr contains a valid instance of `PlaneState`, and that + /// no other references to this `PlaneState` exist for the lifetime of this reference + unsafe fn as_mut_ref<'a>(ptr: *mut bindings::drm_plane_state) -> &'a mut Self { + unsafe { &mut *(ptr as *mut _) } + } + + /// Obtain a mutable pointer to the base plane state, for use in FFI calls + unsafe fn as_mut_ptr(&mut self) -> *mut bindings::drm_plane_state { + &mut self.state + } +} + +unsafe impl Zeroable for bindings::drm_plane_state { } + +unsafe extern "C" fn atomic_duplicate_state_callback( + plane: *mut bindings::drm_plane +) -> *mut bindings::drm_plane_state +{ + // SAFETY: `plane` has to point to a valid instance of drm_plane, since it holds the vtable for + // this function - which is the only possible entrypoint the caller could have used + let state = unsafe { (*plane).state }; + if state.is_null() { + return null_mut(); + } + + // SAFETY: We just verified that `state` is non-null, and we're guaranteed by our bindings that + // `state` is of type `PlaneState`. + let state = unsafe { PlaneState::::as_ref(state) }; + + let mut new: Result>> = Box::try_init(try_init!(PlaneState:: { + state: bindings::drm_plane_state { ..Default::default() }, + inner: state.inner.clone() + })); + + if let Ok(mut new) = new { + // SAFETY: Just a lil' FFI call, nothing special here + unsafe { bindings::__drm_atomic_helper_plane_duplicate_state(plane, new.as_mut_ptr()) }; + + new.into_raw() + } else { + null_mut() + } +} + +unsafe extern "C" fn atomic_destroy_state_callback( + _plane: *mut bindings::drm_plane, + plane_state: *mut bindings::drm_plane_state +) { + // SAFETY: This callback wouldn't be called unless there a plane state to destroy + unsafe { bindings::__drm_atomic_helper_plane_destroy_state(plane_state) }; + + // SAFETY: We're guaranteed by type invariants that plane_state is of type PlaneState, and + // since this is the destructor callback for DRM - we're guaranteed to hold the only remaining + // reference to this state + unsafe { drop(PlaneState::::from_raw(plane_state)) }; +} + +unsafe extern "C" fn plane_reset_callback( + plane: *mut bindings::drm_plane, +) { + // SAFETY: The only entrypoint to this function lives in `plane` so it must be valid, and + let state = unsafe { (*plane).state }; + if !state.is_null() { + // SAFETY: We're guaranteed by type invariance that this plane's state is of type + // DriverPlaneState + unsafe { atomic_destroy_state_callback::(plane, state) } + } + + // Unfortunately, this is the best we can do at the moment as this FFI callback was mistakenly + // presumed to be infallible :( + let new = Box::try_new(PlaneState::::default()).expect("Blame the API, sorry!"); + + // SAFETY: DRM takes ownership of the state from here and assigns it to the plane + unsafe { bindings::__drm_atomic_helper_plane_reset(plane, new.into_raw()) }; +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum PlaneType { + OVERLAY = bindings::drm_plane_type_DRM_PLANE_TYPE_OVERLAY, + PRIMARY = bindings::drm_plane_type_DRM_PLANE_TYPE_PRIMARY, + CURSOR = bindings::drm_plane_type_DRM_PLANE_TYPE_CURSOR, +} diff --git a/rust/kernel/drm/mod.rs b/rust/kernel/drm/mod.rs index 2c12dbd181997..049ae675cb9b1 100644 --- a/rust/kernel/drm/mod.rs +++ b/rust/kernel/drm/mod.rs @@ -8,3 +8,4 @@ pub mod fourcc; pub mod gem; pub mod ioctl; +pub mod kms; From patchwork Fri Mar 22 22:03:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13600440 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id C3EE4C47DD9 for ; Fri, 22 Mar 2024 22:15:30 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 42C6C112825; Fri, 22 Mar 2024 22:15:30 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="htc2MAU0"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by gabe.freedesktop.org (Postfix) with ESMTPS id 9CC25112825 for ; Fri, 22 Mar 2024 22:15:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1711145727; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=/2rbFK3Zkwkpa0zSjV5S8wegkn2R9j6nsE8sHWQ00u4=; b=htc2MAU0ZcdvOAlb+IQpo6hYZTBbBKD1mT29wzuFQMc+U5u9WYeAWHgagIX4I6huKau6Ac QkrAf9okTL+CdMLSfFtkXIY6qk5umDVoCISdzfBYZ9ms/iBvHIsHrEaUIMc0I3db/cJlEe VBBuZA4Epkco2VqVSxt3/e+xoC+9P80= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-64-7wnjtKkHPcy09wf6XsHwfg-1; Fri, 22 Mar 2024 18:15:22 -0400 X-MC-Unique: 7wnjtKkHPcy09wf6XsHwfg-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 33F7F29AA3B6; Fri, 22 Mar 2024 22:15:21 +0000 (UTC) Received: from emerald.redhat.com (unknown [10.22.8.130]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1B60D8173; Fri, 22 Mar 2024 22:15:20 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org Cc: Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Daniel Vetter , Miguel Ojeda , Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , "GitAuthor: Lyude Paul" , linux-kernel@vger.kernel.org (open list), rust-for-linux@vger.kernel.org (open list:RUST:Keyword:\b(?i:rust)\b) Subject: [PATCH 2/4] WIP: drm: Introduce rvkms Date: Fri, 22 Mar 2024 18:03:34 -0400 Message-ID: <20240322221305.1403600-3-lyude@redhat.com> In-Reply-To: <20240322221305.1403600-1-lyude@redhat.com> References: <20240322221305.1403600-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.5 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" This introduces a work in progress port of the VKMS driver to rust to provide a user of the kernel's new rust bindings for KMS drivers! This driver is very incomplete, I'm not even sure if it loads right now without crashing (but it did at one point, and I'll be checking very soon!). Squash into rvkms introduction Signed-off-by: Lyude Paul --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/rvkms/Kconfig | 3 + drivers/gpu/drm/rvkms/Makefile | 1 + drivers/gpu/drm/rvkms/connector.rs | 55 +++++++++++ drivers/gpu/drm/rvkms/crtc.rs | 40 ++++++++ drivers/gpu/drm/rvkms/encoder.rs | 26 +++++ drivers/gpu/drm/rvkms/file.rs | 22 +++++ drivers/gpu/drm/rvkms/gem.rs | 32 +++++++ drivers/gpu/drm/rvkms/output.rs | 72 ++++++++++++++ drivers/gpu/drm/rvkms/plane.rs | 39 ++++++++ drivers/gpu/drm/rvkms/rvkms.rs | 146 +++++++++++++++++++++++++++++ 12 files changed, 439 insertions(+) create mode 100644 drivers/gpu/drm/rvkms/Kconfig create mode 100644 drivers/gpu/drm/rvkms/Makefile create mode 100644 drivers/gpu/drm/rvkms/connector.rs create mode 100644 drivers/gpu/drm/rvkms/crtc.rs create mode 100644 drivers/gpu/drm/rvkms/encoder.rs create mode 100644 drivers/gpu/drm/rvkms/file.rs create mode 100644 drivers/gpu/drm/rvkms/gem.rs create mode 100644 drivers/gpu/drm/rvkms/output.rs create mode 100644 drivers/gpu/drm/rvkms/plane.rs create mode 100644 drivers/gpu/drm/rvkms/rvkms.rs diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index c7edba18a6f09..48c140f943356 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -275,6 +275,8 @@ source "drivers/gpu/drm/amd/amdgpu/Kconfig" source "drivers/gpu/drm/nouveau/Kconfig" +source "drivers/gpu/drm/rvkms/Kconfig" + source "drivers/gpu/drm/i915/Kconfig" source "drivers/gpu/drm/xe/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 104b42df2e956..91a18c8290fa4 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -143,6 +143,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/ obj-$(CONFIG_DRM_VGEM) += vgem/ obj-$(CONFIG_DRM_VKMS) += vkms/ obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/ +obj-$(CONFIG_DRM_RVKMS) += rvkms/ obj-$(CONFIG_DRM_EXYNOS) +=exynos/ obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/ obj-$(CONFIG_DRM_GMA500) += gma500/ diff --git a/drivers/gpu/drm/rvkms/Kconfig b/drivers/gpu/drm/rvkms/Kconfig new file mode 100644 index 0000000000000..d888aa4ffc84c --- /dev/null +++ b/drivers/gpu/drm/rvkms/Kconfig @@ -0,0 +1,3 @@ +config DRM_RVKMS + tristate "Rust VKMS PoC driver (EXPERIMENTAL)" + depends on RUST && DRM && DRM_GEM_SHMEM_HELPER diff --git a/drivers/gpu/drm/rvkms/Makefile b/drivers/gpu/drm/rvkms/Makefile new file mode 100644 index 0000000000000..18e06fc3343c6 --- /dev/null +++ b/drivers/gpu/drm/rvkms/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_DRM_RVKMS) += rvkms.o diff --git a/drivers/gpu/drm/rvkms/connector.rs b/drivers/gpu/drm/rvkms/connector.rs new file mode 100644 index 0000000000000..40f84d38437ee --- /dev/null +++ b/drivers/gpu/drm/rvkms/connector.rs @@ -0,0 +1,55 @@ +// TODO: License and stuff +// Contain's rvkms's drm_connector implementation + +use super::{RvkmsDriver, RvkmsDevice, MAX_RES, DEFAULT_RES}; +use kernel::{ + prelude::*, + drm::{ + device::Device, + kms::{ + connector::{self, ConnectorGuard}, + ModeConfigGuard + } + }, + prelude::* +}; +use core::marker::PhantomPinned; + +#[pin_data] +pub(crate) struct DriverConnector { + #[pin] + _p: PhantomPinned +} + +pub(crate) type Connector = connector::Connector; + +impl connector::DriverConnector for DriverConnector { + type Initializer = impl PinInit; + + type State = ConnectorState; + + type Driver = RvkmsDriver; + + type Args = (); + + fn new(dev: &Device, args: Self::Args) -> Self::Initializer { + try_pin_init!(Self { _p: PhantomPinned }) + } + + fn get_modes( + connector: ConnectorGuard<'_, Self>, + _guard: &ModeConfigGuard<'_, Self::Driver> + ) -> i32 { + let count = connector.add_modes_noedid(MAX_RES); + + connector.set_preferred_mode(DEFAULT_RES); + count + } +} + +#[derive(Clone, Default)] +pub(crate) struct ConnectorState; + +impl connector::DriverConnectorState for ConnectorState { + type Connector = DriverConnector; +} diff --git a/drivers/gpu/drm/rvkms/crtc.rs b/drivers/gpu/drm/rvkms/crtc.rs new file mode 100644 index 0000000000000..2998f288b88e6 --- /dev/null +++ b/drivers/gpu/drm/rvkms/crtc.rs @@ -0,0 +1,40 @@ +// TODO: License and stuff +// Contain's rvkms's drm_crtc implementation +use core::marker::PhantomPinned; +use super::RvkmsDriver; +use kernel::{ + prelude::*, + drm::{ + device::Device, + kms::crtc, + }, +}; + +pub(crate) type Crtc = crtc::Crtc; + +#[pin_data] +pub(crate) struct DriverCrtc { + #[pin] + _p: PhantomPinned +} + +impl crtc::DriverCrtc for DriverCrtc { + type Initializer = impl PinInit; + + type Args = (); + + type State = CrtcState; + + type Driver = RvkmsDriver; + + fn new(device: &Device, args: Self::Args) -> Self::Initializer { + try_pin_init!(Self { _p: PhantomPinned }) + } +} + +#[derive(Clone, Default)] +pub(crate) struct CrtcState; + +impl crtc::DriverCrtcState for CrtcState { + type Crtc = DriverCrtc; +} diff --git a/drivers/gpu/drm/rvkms/encoder.rs b/drivers/gpu/drm/rvkms/encoder.rs new file mode 100644 index 0000000000000..72d8b43d9107e --- /dev/null +++ b/drivers/gpu/drm/rvkms/encoder.rs @@ -0,0 +1,26 @@ +use core::marker::PhantomPinned; +use kernel::{ + drm::{device::Device, kms}, + prelude::* +}; +use crate::RvkmsDriver; + +#[pin_data] +pub(crate) struct DriverEncoder { + #[pin] + _p: PhantomPinned, +} + +pub(crate) type Encoder = kms::encoder::Encoder; + +impl kms::encoder::DriverEncoder for DriverEncoder { + type Initializer = impl PinInit; + + type Driver = RvkmsDriver; + + type Args = (); + + fn new(device: &Device, args: Self::Args) -> Self::Initializer { + try_pin_init!(Self { _p: PhantomPinned }) + } +} diff --git a/drivers/gpu/drm/rvkms/file.rs b/drivers/gpu/drm/rvkms/file.rs new file mode 100644 index 0000000000000..24b1b53b78238 --- /dev/null +++ b/drivers/gpu/drm/rvkms/file.rs @@ -0,0 +1,22 @@ +use super::RvkmsDriver; + +use kernel::{ + drm::{ + self, + device::Device as DrmDevice + }, + prelude::*, +}; +use core::option::*; + +pub(crate) struct File(); + +impl drm::file::DriverFile for File { + type Driver = RvkmsDriver; + + fn open(device: &DrmDevice) -> Result>> { + pr_info!("Someone opened a file! But I do not yet know which one...\n"); + + Ok(Box::into_pin(Box::try_new(Self())?)) + } +} diff --git a/drivers/gpu/drm/rvkms/gem.rs b/drivers/gpu/drm/rvkms/gem.rs new file mode 100644 index 0000000000000..b789a1c2170c7 --- /dev/null +++ b/drivers/gpu/drm/rvkms/gem.rs @@ -0,0 +1,32 @@ +use crate::{RvkmsDriver, RvkmsDevice}; +use core::sync::atomic::{AtomicU64, Ordering}; +use kernel::{ + drm::{self, gem}, + prelude::*, +}; + +static GEM_ID: AtomicU64 = AtomicU64::new(0); + +/// GEM Object implementation +#[pin_data] +pub(crate) struct DriverObject { + /// ID for debugging + id: u64, +} + +pub(crate) type Object = gem::shmem::Object; + +impl gem::BaseDriverObject for DriverObject { + type Initializer = impl PinInit; + + fn new(dev: &RvkmsDevice, size: usize) -> Self::Initializer { + let id = GEM_ID.fetch_add(1, Ordering::Relaxed); + + pr_debug!("DriverObject::new id={id}\n"); + DriverObject { id } + } +} + +impl gem::shmem::DriverObject for DriverObject { + type Driver = RvkmsDriver; +} diff --git a/drivers/gpu/drm/rvkms/output.rs b/drivers/gpu/drm/rvkms/output.rs new file mode 100644 index 0000000000000..227d6c3d719a0 --- /dev/null +++ b/drivers/gpu/drm/rvkms/output.rs @@ -0,0 +1,72 @@ +use crate::{ + crtc::Crtc, + plane::Plane, + connector::Connector, + encoder::Encoder, + RvkmsDevice +}; +use kernel::{ + drm::{ + fourcc::Format, + kms::{ + connector::DRM_MODE_CONNECTOR_VIRTUAL, + encoder::DRM_MODE_ENCODER_VIRTUAL, + plane::PlaneType, + KmsRef + }, + }, + sync::Arc, + prelude::*, + types::ARef, + drm_format_list, +}; + +pub(crate) struct Output { + crtc: KmsRef, + primary: KmsRef, + // TODO: overlay, cursor + connector: ARef, + encoder: KmsRef, +} + +impl Output { + pub(crate) fn new(dev: &RvkmsDevice, index: u8) -> Result { + let primary = Plane::new( + dev, + 1 << index, + &drm_format_list![Format::XRGB888], + None, + PlaneType::PRIMARY, + None, + () + )?; + + let crtc = Crtc::new( + dev, + &primary, + Option::<&Plane>::None, + None, + () + )?; + + let connector = Connector::new(dev, DRM_MODE_CONNECTOR_VIRTUAL, ())?; + + let encoder = Encoder::new( + dev, + DRM_MODE_ENCODER_VIRTUAL, + 1 << index, + 0, + None, + () + )?; + + connector.attach_encoder(&encoder)?; + + Ok(Self { + crtc: crtc.into(), + primary: primary.into(), + connector, + encoder: encoder.into() + }) + } +} diff --git a/drivers/gpu/drm/rvkms/plane.rs b/drivers/gpu/drm/rvkms/plane.rs new file mode 100644 index 0000000000000..54c4bbda64d8e --- /dev/null +++ b/drivers/gpu/drm/rvkms/plane.rs @@ -0,0 +1,39 @@ +use core::marker::PhantomPinned; +use super::RvkmsDriver; +use kernel::{ + prelude::*, + drm::{ + device::Device, + kms::plane::{self, DriverPlaneState}, + }, +}; + +#[pin_data] +pub(crate) struct DriverPlane { + #[pin] + _p: PhantomPinned, +} + +pub(crate) type Plane = plane::Plane; +pub(crate) type PlaneState = plane::PlaneState; + +impl plane::DriverPlane for DriverPlane { + type Initializer = impl PinInit; + + type State = RvkmsPlaneState; + + type Driver = RvkmsDriver; + + type Args = (); + + fn new(device: &Device, args: Self::Args) -> Self::Initializer { + try_pin_init!(Self { _p: PhantomPinned }) + } +} + +#[derive(Clone, Default)] +pub(crate) struct RvkmsPlaneState; + +impl DriverPlaneState for RvkmsPlaneState { + type Plane = DriverPlane; +} diff --git a/drivers/gpu/drm/rvkms/rvkms.rs b/drivers/gpu/drm/rvkms/rvkms.rs new file mode 100644 index 0000000000000..0b14aca7f77d4 --- /dev/null +++ b/drivers/gpu/drm/rvkms/rvkms.rs @@ -0,0 +1,146 @@ +// Rust VKMS Proof of Concept driver +// Written by Lyude Paul +// Enormous thanks to: +// - Maira Canal +// - Asahi Lina +// - Probably lots of other wonderful people whose names aren't here + +mod connector; +mod crtc; +mod file; +mod gem; +mod plane; +mod output; +mod encoder; + +use alloc::boxed::Box; + +use core::option::*; + +use kernel::{ + c_str, + str::CStr, + device::{self, RawDevice}, + drm::{ + self, + drv, + kms::{ + KmsDriver, + ModeConfigInfo, + }, + }, + platform, + prelude::*, + sync::Arc, + types::ARef, +}; + +use crate::output::Output; + +pub(crate) struct RvkmsDriver; +pub(crate) struct Resources; + +pub(crate) type DeviceData = device::Data, Resources, Data>; + +/// Convienence type alias for the DRM device type for this driver +pub(crate) type RvkmsDevice = drm::device::Device; + +/// Driver metadata +const INFO: drv::DriverInfo = drv::DriverInfo { + major: 0, + minor: 0, + patchlevel: 0, + name: c_str!("rvkms"), + desc: c_str!("Rust VKMS PoC"), + date: c_str!("20240115"), +}; + +/// The minimum supported resolution +const MIN_RES: (i32, i32) = (10, 10); + +/// The maximum supported resolution +const MAX_RES: (i32, i32) = (8192, 8192); + +/// The "preferred" resolution +const DEFAULT_RES: (i32, i32) = (1024, 768); + +pub(crate) struct Data { +} + +/// DRM Driver implementation for `RvkmsDriver` +#[vtable] +impl drv::Driver for RvkmsDriver { + type Data = Arc; + type Object = gem::Object; + type File = file::File; + + const INFO: drv::DriverInfo = INFO; + const FEATURES:u32 = drv::FEAT_GEM | drv::FEAT_MODESET | drv::FEAT_ATOMIC; + + kernel::declare_drm_ioctls! {} +} + +impl KmsDriver for RvkmsDriver {} + +pub(crate) struct Rvkms { + output: Output, + drm: ARef, + _resource: device::Resource, + _pdev: platform::Device, +} + +const MODE_CONFIG_INFO: ModeConfigInfo = ModeConfigInfo { + min_resolution: MIN_RES, + max_resolution: MAX_RES, + max_cursor: (512, 512), + preferred_depth: 0, +}; + +impl kernel::Module for Rvkms { + fn init(name: &'static CStr, module: &'static ThisModule) -> Result { + // We need to handle all of this from the true module_init, as we're a platform driver that + // binds to nothing - so we have to handle that portion of init ourselves like the real vkms + let mut pdev = platform::Device::register(c_str!("rvkms"), -1)?; + let dev = device::Device::from_dev(&pdev); + + dev.pr_info(format_args!("Initializing RVKMS\n")); + + // TODO: I wonder if there's any way we could get rid of having to pass raw pointers to + // this? + // no idea + let resource = dev.open_group(core::ptr::null_mut() as *mut core::ffi::c_void)?; + + let reg: drv::Registration = + drv::Registration::new(&dev, Option::Some(MODE_CONFIG_INFO), module)?; + let drm_dev: ARef = ARef::from(reg.device()); + let reg_info = reg.registration_info(); + let output = Output::new(&drm_dev, 0)?; + let data = kernel::new_device_data!( + reg, + Resources, + Data { }, + "RvkmsDeviceData" + )?; + + drm_dev.mode_config_reset(); + + dev.dma_coerce_mask_and_coherent(kernel::dma::dma_bit_mask(64))?; + + drv::Registration::register(reg_info, data.into(), 0, module)?; + + Ok(Self { + drm: drm_dev, + output, + _resource: resource, + _pdev: pdev, + }) + } +} + +module! { + type: Rvkms, + name: "rvkms", + author: "Lyude Paul", + description: "Rust VKMS Proof of Concept driver", + license: "GPL v2", +} From patchwork Fri Mar 22 22:03:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13600441 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 3D94DC54E64 for ; Fri, 22 Mar 2024 22:15:38 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 586D5112827; Fri, 22 Mar 2024 22:15:37 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="YWx3bPHd"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by gabe.freedesktop.org (Postfix) with ESMTPS id E4EB4112827 for ; Fri, 22 Mar 2024 22:15:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1711145735; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=g7GQ3fPHiJFc4OvZ+/3sVtCU8QWBFnwHWiIOmeagJEg=; b=YWx3bPHd3qglprymwy/hgBr6qGJgWTvJxJgONAuDwK9FFBYWRaxrlLvV1qPvxMVaqn6NTY jLsoYdT2bKOkiV9WoVh59jVZqKq4wrFkDETbMhhgUzhDOsybYB+jC7nCpDBsDcC7sgXN7N vZ+E11lPcI1Z9XyMPJ6JpmM3MUKyWNs= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-176-g5IWk4NQOAK7b87urW2VgQ-1; Fri, 22 Mar 2024 18:15:27 -0400 X-MC-Unique: g5IWk4NQOAK7b87urW2VgQ-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id BD0CD3C02454; Fri, 22 Mar 2024 22:15:26 +0000 (UTC) Received: from emerald.redhat.com (unknown [10.22.8.130]) by smtp.corp.redhat.com (Postfix) with ESMTP id 28DA81074E; Fri, 22 Mar 2024 22:15:26 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org Cc: Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Daniel Vetter , Miguel Ojeda , Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , linux-kernel@vger.kernel.org (open list), rust-for-linux@vger.kernel.org (open list:RUST) Subject: [PATCH 3/4] rust/drm/kms: Extract PlaneState into IntoPlaneState Date: Fri, 22 Mar 2024 18:03:35 -0400 Message-ID: <20240322221305.1403600-4-lyude@redhat.com> In-Reply-To: <20240322221305.1403600-1-lyude@redhat.com> References: <20240322221305.1403600-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.5 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" DRM actually has a number of helpers that wrap drm_plane_state, one of which is actually needed by VKMS - drm_shadow_plane_state. So, let's start preparing to write bindings for this by first extracting PlaneState into the IntoPlaneState trait - which all DRM structs which wrap drm_plane_state will implement. This is basically the same idea as the GEM ops - but for plane states. Signed-off-by: Lyude Paul --- drivers/gpu/drm/rvkms/plane.rs | 2 +- rust/kernel/drm/kms/plane.rs | 165 ++++++++++++++++++++------------- 2 files changed, 103 insertions(+), 64 deletions(-) diff --git a/drivers/gpu/drm/rvkms/plane.rs b/drivers/gpu/drm/rvkms/plane.rs index 54c4bbda64d8e..d98a1f7bf79e2 100644 --- a/drivers/gpu/drm/rvkms/plane.rs +++ b/drivers/gpu/drm/rvkms/plane.rs @@ -20,7 +20,7 @@ pub(crate) struct DriverPlane { impl plane::DriverPlane for DriverPlane { type Initializer = impl PinInit; - type State = RvkmsPlaneState; + type State = PlaneState; type Driver = RvkmsDriver; diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs index 78c8e370b997c..73c285445be63 100644 --- a/rust/kernel/drm/kms/plane.rs +++ b/rust/kernel/drm/kms/plane.rs @@ -13,6 +13,7 @@ sync::{Arc, ArcBorrow}, init::InPlaceInit, offset_of, + private::Sealed, }; use core::{ cell::UnsafeCell, @@ -62,14 +63,16 @@ pub trait DriverPlane: Send + Sync + Sized { /// The parent driver implementation type Driver: KmsDriver; - /// The type for this driver's drm_plane_state implementation - type State: DriverPlaneState; + /// The full type for this driver's drm_plane_state implementation. Drivers which don't need + /// special DRM helpers for their plane states may just use `PlaneState` here, where `T` is + /// their private state struct which implements `DriverPlaneState` + type State: IntoPlaneState; /// Create a new plane for this driver fn new(device: &Device, args: Self::Args) -> Self::Initializer; } -impl crate::private::Sealed for Plane {} +impl Sealed for Plane {} impl ModeObject for Plane { type Driver = T::Driver; @@ -177,6 +180,70 @@ pub(super) fn raw_mut_ptr(&self) -> *mut bindings::drm_plane { unsafe { drop(Box::from_raw(plane as *mut Plane)) }; } +/// Operations implemented by any base atomic plane state. These are implemented by DRM to provide +/// wrappers around the generic atomic plane state, along with various plane state helpers. +/// +/// SAFETY: Incorrect implementation of these methods will result in UB, users should take care to +/// read through the documentation of each method - especially the provided methods. +pub unsafe trait IntoPlaneState: Default + Sealed { + /// Consume the box for this plane state without dropping its contents, and return a reference + /// to it's base plane state to hand off to DRM + /// + /// Implementors must override this if their data layout does not start with + /// `bindings::drm_plane_state`. + fn into_raw(self: Box) -> *mut bindings::drm_plane_state { + // Our data layout starts with drm_plane_state + Box::into_raw(self).cast() + } + + /// Reconstruct the box for this plate state for deallocation + /// + /// Implementors must override this if their data layout does not start with + /// `bindings::drm_plane_state`. + unsafe fn from_raw(ptr: *mut bindings::drm_plane_state) -> Box { + // SAFETY: Users of this default promise their data layout starts with drm_plane_state + unsafe { Box::from_raw(ptr.cast()) } + } + + /// Get a mutable reference to the raw `bindings::drm_plane_state` contained within this type + /// that we can pass to DRM + fn as_raw_mut(&mut self) -> &mut bindings::drm_plane_state { + // SAFETY: Users of this default promise their data layout starts with drm_plane_state + unsafe { mem::transmute(self) } + } + + /// Get an immutable reference to this type from the given raw `bindings::drm_plane_state` + /// pointer + /// + /// Implementors must override this if their data layout does not start with + /// `bindings::drm_plane_state`. + /// + /// SAFETY: The caller guarantees `ptr` is contained within a valid instance of `Self` + unsafe fn ref_from_raw<'a>(ptr: *const bindings::drm_plane_state) -> &'a Self { + // SAFETY: Users of this default promise their data layout starts with drm_plane_state + unsafe { &*ptr.cast() } + } + + /// Get a mutable reference to this type from the given raw `bindings::drm_plane_state` pointer + /// + /// SAFETY: The caller guarantees `ptr` is contained within a valid instance of `Self` + unsafe fn ref_from_raw_mut<'a>(ptr: *mut bindings::drm_plane_state) -> &'a mut Self { + // SAFETY: Users of this default promise their data layout starts with drm_plane_state + unsafe { &mut (*ptr.cast()) } + } + + // Allocate a "duplicated" copy of this state, which usually involves calling DRM's helper + // function for this plane state type + fn __duplicate_state(&self, plane: *mut bindings::drm_plane) -> Result>; + + // Call DRM's destroy helper for this type of plane state. Note this only cleans up the base DRM + // state struct and does not de-allocate its `Box`. + fn __destroy_state(state: *mut bindings::drm_plane_state); + + // Call DRM's reset helper for this type of plane state. + fn __reset_state(plane: *mut bindings::drm_plane, state: *mut bindings::drm_plane_state); +} + #[derive(Default)] #[repr(C)] pub struct PlaneState { @@ -190,46 +257,35 @@ pub trait DriverPlaneState: Clone + Default + Sized { type Plane: DriverPlane; } -impl PlaneState { - /// Consume this struct without dropping it, and return a pointer to its base `drm_plane_state` - /// which can be handed off to DRM. - fn into_raw(self: Box) -> *mut bindings::drm_plane_state { - let this = Box::into_raw(self); +impl Sealed for PlaneState {} - unsafe { &mut (*this).state } - } +// SAFETY: Our data layout starts with drm_plane_state +unsafe impl IntoPlaneState for PlaneState { + fn __duplicate_state(&self, plane: *mut bindings::drm_plane) -> Result> { + let mut new: Box = Box::try_init(try_init!(Self { + state: bindings::drm_plane_state { ..Default::default() }, + inner: self.inner.clone() + }))?; - /// Consume a raw pointer and recover the original `Box>` - /// - /// SAFETY: Callers must ensure that ptr contains a valid instance of `PlaneState` - unsafe fn from_raw(ptr: *mut bindings::drm_plane_state) -> Box { - unsafe { Box::from_raw(ptr as *mut _) } - } + // SAFETY: FFI call with no special requirements + unsafe { bindings::__drm_atomic_helper_plane_duplicate_state(plane, new.as_raw_mut()) }; - /// Obtain a reference back to the `PlaneState` - /// - /// SAFETY: Callers must ensure that ptr contains a valid instance of `PlaneState`. - unsafe fn as_ref<'a>(ptr: *const bindings::drm_plane_state) -> &'a Self { - unsafe { &*(ptr as *const _) } + Ok(new) } - /// Obtain a mutable reference back to the PlaneState - /// - /// SAFETY: Callers must ensure that ptr contains a valid instance of `PlaneState`, and that - /// no other references to this `PlaneState` exist for the lifetime of this reference - unsafe fn as_mut_ref<'a>(ptr: *mut bindings::drm_plane_state) -> &'a mut Self { - unsafe { &mut *(ptr as *mut _) } + fn __destroy_state(state: *mut bindings::drm_plane_state) { + // SAFETY: This would not be called without a plane state to destroy + unsafe { bindings::__drm_atomic_helper_plane_destroy_state(state) }; } - /// Obtain a mutable pointer to the base plane state, for use in FFI calls - unsafe fn as_mut_ptr(&mut self) -> *mut bindings::drm_plane_state { - &mut self.state + fn __reset_state(plane: *mut bindings::drm_plane, state: *mut bindings::drm_plane_state) { + unsafe { bindings::__drm_atomic_helper_plane_reset(plane, state) } } } unsafe impl Zeroable for bindings::drm_plane_state { } -unsafe extern "C" fn atomic_duplicate_state_callback( +unsafe extern "C" fn atomic_duplicate_state_callback( plane: *mut bindings::drm_plane ) -> *mut bindings::drm_plane_state { @@ -240,55 +296,38 @@ unsafe impl Zeroable for bindings::drm_plane_state { } return null_mut(); } - // SAFETY: We just verified that `state` is non-null, and we're guaranteed by our bindings that - // `state` is of type `PlaneState`. - let state = unsafe { PlaneState::::as_ref(state) }; - - let mut new: Result>> = Box::try_init(try_init!(PlaneState:: { - state: bindings::drm_plane_state { ..Default::default() }, - inner: state.inner.clone() - })); - - if let Ok(mut new) = new { - // SAFETY: Just a lil' FFI call, nothing special here - unsafe { bindings::__drm_atomic_helper_plane_duplicate_state(plane, new.as_mut_ptr()) }; - - new.into_raw() - } else { - null_mut() - } + // SAFETY: We just checked that the state is non-null, and we're guaranteed this operation is + // safe via type invariance + unsafe { T::ref_from_raw(state) }.__duplicate_state(plane).map_or(null_mut(), T::into_raw) } -unsafe extern "C" fn atomic_destroy_state_callback( +unsafe extern "C" fn atomic_destroy_state_callback( _plane: *mut bindings::drm_plane, - plane_state: *mut bindings::drm_plane_state + state: *mut bindings::drm_plane_state ) { - // SAFETY: This callback wouldn't be called unless there a plane state to destroy - unsafe { bindings::__drm_atomic_helper_plane_destroy_state(plane_state) }; + T::__destroy_state(state); - // SAFETY: We're guaranteed by type invariants that plane_state is of type PlaneState, and - // since this is the destructor callback for DRM - we're guaranteed to hold the only remaining - // reference to this state - unsafe { drop(PlaneState::::from_raw(plane_state)) }; + // SAFETY: We're guaranteed by type invariance that state of type T, and __destroy_state() does + // not perform any deallocations + drop(unsafe { T::from_raw(state) }); } -unsafe extern "C" fn plane_reset_callback( +unsafe extern "C" fn plane_reset_callback( plane: *mut bindings::drm_plane, ) { - // SAFETY: The only entrypoint to this function lives in `plane` so it must be valid, and + // SAFETY: The only entrypoint to this function lives in `plane` so it must be valid let state = unsafe { (*plane).state }; if !state.is_null() { - // SAFETY: We're guaranteed by type invariance that this plane's state is of type - // DriverPlaneState + // SAFETY: We're guaranteed by type invariance that this planes state is of type T unsafe { atomic_destroy_state_callback::(plane, state) } } // Unfortunately, this is the best we can do at the moment as this FFI callback was mistakenly // presumed to be infallible :( - let new = Box::try_new(PlaneState::::default()).expect("Blame the API, sorry!"); + let new = Box::try_new(T::default()).expect("Blame the API, sorry!"); - // SAFETY: DRM takes ownership of the state from here and assigns it to the plane - unsafe { bindings::__drm_atomic_helper_plane_reset(plane, new.into_raw()) }; + // DRM takes ownership of the state from here, resets it, and then assigns it to the plane + T::__reset_state(plane, new.into_raw()); } #[derive(Copy, Clone, Debug, PartialEq, Eq)] From patchwork Fri Mar 22 22:03:36 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13600442 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 7D2D7C47DD9 for ; Fri, 22 Mar 2024 22:15:48 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id B6C6C11282F; Fri, 22 Mar 2024 22:15:47 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="Sh+jLXNE"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by gabe.freedesktop.org (Postfix) with ESMTPS id B7A3311282F for ; Fri, 22 Mar 2024 22:15:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1711145744; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=t+o/hq/vRaAlqZorXgz61wZ5x9VfNVOFlmD2hAnuwQc=; b=Sh+jLXNEBJAp5e+n6Sph14X7feQT8DmOln9Tn/pDyNqpqpi9ClzLZP52fmTIHYpvWwfDNe tO/BhzpHh2fcAECqrhSqjvPWu4BUruwz1zIh6GmneL5Eo7/OmFdoxd2XR/e18HYQHSUKDb s8M9haG6HqdBVgmEnsWJazdn+Fdt7gw= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-159-JjZz40PEMPe23vRDS-XaXg-1; Fri, 22 Mar 2024 18:15:38 -0400 X-MC-Unique: JjZz40PEMPe23vRDS-XaXg-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id E5C3D3C02454; Fri, 22 Mar 2024 22:15:36 +0000 (UTC) Received: from emerald.redhat.com (unknown [10.22.8.130]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3BA338173; Fri, 22 Mar 2024 22:15:36 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org Cc: Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Daniel Vetter , Miguel Ojeda , Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Asahi Lina , Martin Rodriguez Reboredo , FUJITA Tomonori , linux-kernel@vger.kernel.org (open list), rust-for-linux@vger.kernel.org (open list:RUST) Subject: [PATCH 4/4] WIP: rust/drm/kms: Add ShadowPlaneState Date: Fri, 22 Mar 2024 18:03:36 -0400 Message-ID: <20240322221305.1403600-5-lyude@redhat.com> In-Reply-To: <20240322221305.1403600-1-lyude@redhat.com> References: <20240322221305.1403600-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.5 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Signed-off-by: Lyude Paul --- drivers/gpu/drm/rvkms/plane.rs | 7 +++- rust/bindings/bindings_helper.h | 2 + rust/kernel/drm/kms.rs | 1 + rust/kernel/drm/kms/gem_atomic_helper.rs | 48 ++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/drm/kms/gem_atomic_helper.rs diff --git a/drivers/gpu/drm/rvkms/plane.rs b/drivers/gpu/drm/rvkms/plane.rs index d98a1f7bf79e2..5fb1b63842929 100644 --- a/drivers/gpu/drm/rvkms/plane.rs +++ b/drivers/gpu/drm/rvkms/plane.rs @@ -4,7 +4,10 @@ prelude::*, drm::{ device::Device, - kms::plane::{self, DriverPlaneState}, + kms::{ + plane::{self, DriverPlaneState}, + gem_atomic_helper::ShadowPlaneState, + } }, }; @@ -15,7 +18,7 @@ pub(crate) struct DriverPlane { } pub(crate) type Plane = plane::Plane; -pub(crate) type PlaneState = plane::PlaneState; +pub(crate) type PlaneState = ShadowPlaneState; impl plane::DriverPlane for DriverPlane { type Initializer = impl PinInit; diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 5856afbe6e8f6..73a5eb00e8625 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs index b55d14415367a..14f4c3842ada0 100644 --- a/rust/kernel/drm/kms.rs +++ b/rust/kernel/drm/kms.rs @@ -6,6 +6,7 @@ pub mod crtc; pub mod encoder; pub mod plane; +pub mod gem_atomic_helper; use crate::{ drm::{drv, device::Device}, diff --git a/rust/kernel/drm/kms/gem_atomic_helper.rs b/rust/kernel/drm/kms/gem_atomic_helper.rs new file mode 100644 index 0000000000000..85bc3df32d8b7 --- /dev/null +++ b/rust/kernel/drm/kms/gem_atomic_helper.rs @@ -0,0 +1,48 @@ +use crate::{ + prelude::*, + private::Sealed, + bindings, + init::Zeroable, +}; +use super::plane::{IntoPlaneState, DriverPlaneState}; + +unsafe impl Zeroable for bindings::drm_shadow_plane_state {} + +#[derive(Default)] +#[repr(C)] +pub struct ShadowPlaneState { + shadow_state: bindings::drm_shadow_plane_state, + inner: T, +} + +impl Sealed for ShadowPlaneState {} + +static_assert!(crate::offset_of!(bindings::drm_shadow_plane_state, base) == 0); + +// SAFETY: Our data layout starts with drm_plane_state (contained at the start of +// drm_shadow_plane_state) +unsafe impl IntoPlaneState for ShadowPlaneState { + fn __duplicate_state(&self, plane: *mut bindings::drm_plane) -> Result> { + let mut new: Box = Box::try_init(try_init!(Self { + shadow_state: bindings::drm_shadow_plane_state { ..Default::default() }, + inner: self.inner.clone() + }))?; + + // SAFETY: FFI call with no special requirements + unsafe { bindings::__drm_gem_duplicate_shadow_plane_state(plane, &mut new.shadow_state) }; + + Ok(new) + } + + fn __destroy_state(state: *mut bindings::drm_plane_state) { + // SAFETY: This would not be called without a plane state to destroy, and our data layout + // starts with `bindings::drm_plane_state` + unsafe { bindings::__drm_gem_destroy_shadow_plane_state(state.cast()) }; + } + + fn __reset_state(plane: *mut bindings::drm_plane, state: *mut bindings::drm_plane_state) { + // SAFETY: This would not be called without a plane state to reset, and our data layout + // starts with `bindings::drm_plane_state` + unsafe { bindings::__drm_gem_reset_shadow_plane(plane, state.cast()) } + } +}