From patchwork Mon Sep 30 23:09:44 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817254 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 05858CEB2CB for ; Mon, 30 Sep 2024 23:37:34 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 8110710E2BC; Mon, 30 Sep 2024 23:37:33 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="ZkXgA1dc"; 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 A863010E2BC for ; Mon, 30 Sep 2024 23:37:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739450; 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=DdC7V+RGCNsUxoQc10gW35F78meSMQbe3+j1HDfYD9I=; b=ZkXgA1dcWoNiOxsgjU81+6EDMpMu8SNG+lzq1Fo+PQOpQg7B4rxGVDCqnZJBco9QyucMYJ QroKZmKQlMnV0jM8asMGjevKL0t+vF4X1k0o6kTmS0gZ4aNX8w0z7R9v2PCKXu6Fr5kWwd tNeTK3BEKx3OO8zDi3V6ClXn/006cf0= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-164-3RGTdnYgNwGDm4k27PGtmQ-1; Mon, 30 Sep 2024 19:37:25 -0400 X-MC-Unique: 3RGTdnYgNwGDm4k27PGtmQ-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 51A4619626D0; Mon, 30 Sep 2024 23:37:18 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id EF92A3003DEC; Mon, 30 Sep 2024 23:37:12 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , Danilo Krummrich , Mika Westerberg , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 01/35] WIP: rust/drm: Add fourcc bindings Date: Mon, 30 Sep 2024 19:09:44 -0400 Message-ID: <20240930233257.1189730-2-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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 adds some very basic rust bindings for fourcc. We only have a single format code added for the moment, but this is enough to get a driver registered. TODO: * Write up something to automatically generate constants from the fourcc headers Signed-off-by: Lyude Paul --- rust/bindings/bindings_helper.h | 1 + rust/kernel/drm/fourcc.rs | 127 ++++++++++++++++++++++++++++++++ rust/kernel/drm/mod.rs | 1 + 3 files changed, 129 insertions(+) create mode 100644 rust/kernel/drm/fourcc.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index b2e05f8c2ee7d..04898f70ef1b8 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/drm/fourcc.rs b/rust/kernel/drm/fourcc.rs new file mode 100644 index 0000000000000..b80eba99aa7e4 --- /dev/null +++ b/rust/kernel/drm/fourcc.rs @@ -0,0 +1,127 @@ +use bindings; +use core::{ops::*, slice, ptr}; + +const fn fourcc_code(a: u8, b: u8, c: u8, d: u8) -> u32 { + (a as u32) | (b as u32) << 8 | (c as u32) << 16 | (d as u32) << 24 +} + +// TODO: Figure out a more automated way of importing this +pub const XRGB888: u32 = fourcc_code(b'X', b'R', b'2', b'4'); + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct FormatList { + list: [u32; COUNT], + _sentinel: u32, +} + +impl FormatList { + /// Create a new [`FormatList`] + pub const fn new(list: [u32; COUNT]) -> Self { + Self { + list, + _sentinel: 0 + } + } + + /// Returns the number of entries in the list, including the sentinel. + /// + /// This is generally only useful for passing [`FormatList`] to C bindings. + pub const fn raw_len(&self) -> usize { + COUNT + 1 + } +} + +impl Deref for FormatList { + type Target = [u32; COUNT]; + + fn deref(&self) -> &Self::Target { + &self.list + } +} + +impl DerefMut for FormatList { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.list + } +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct ModifierList { + list: [u64; COUNT], + _sentinel: u64 +} + +impl ModifierList { + /// Create a new [`ModifierList`] + pub const fn new(list: [u64; COUNT]) -> Self { + Self { + list, + _sentinel: 0 + } + } +} + +impl Deref for ModifierList { + type Target = [u64; COUNT]; + + fn deref(&self) -> &Self::Target { + &self.list + } +} + +impl DerefMut for ModifierList { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.list + } +} + +#[repr(transparent)] +#[derive(Copy, Clone)] +pub struct FormatInfo { + inner: bindings::drm_format_info, +} + +impl FormatInfo { + // SAFETY: `ptr` must point to a valid instance of a `bindings::drm_format_info` + pub(super) unsafe fn from_raw<'a>(ptr: *const bindings::drm_format_info) -> &'a Self { + // SAFETY: Our data layout is identical + unsafe { &*ptr.cast() } + } + + /// The number of color planes (1 to 3) + pub const fn num_planes(&self) -> u8 { + self.inner.num_planes + } + + /// Does the format embed an alpha component? + pub const fn has_alpha(&self) -> bool { + self.inner.has_alpha + } + + /// The total number of components (color planes + alpha channel, if there is one) + pub const fn num_components(&self) -> u8 { + self.num_planes() + self.has_alpha() as u8 + } + + /// Number of bytes per block (per plane), where blocks are defined as a rectangle of pixels + /// which are stored next to each other in a byte aligned memory region. + pub fn char_per_block(&self) -> &[u8] { + // SAFETY: The union we access here is just for descriptive purposes on the C side, both + // members are identical in data layout + unsafe { &self.inner.__bindgen_anon_1.char_per_block[..self.num_components() as _] } + } +} + +impl AsRef for FormatInfo { + fn as_ref(&self) -> &bindings::drm_format_info { + &self.inner + } +} + +impl From for FormatInfo { + fn from(value: bindings::drm_format_info) -> Self { + Self { inner: value } + } +} diff --git a/rust/kernel/drm/mod.rs b/rust/kernel/drm/mod.rs index c44760a1332fa..2c12dbd181997 100644 --- a/rust/kernel/drm/mod.rs +++ b/rust/kernel/drm/mod.rs @@ -5,5 +5,6 @@ pub mod device; pub mod drv; pub mod file; +pub mod fourcc; pub mod gem; pub mod ioctl; From patchwork Mon Sep 30 23:09:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817255 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 69E80CEB2CD for ; Mon, 30 Sep 2024 23:37:39 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id EB82410E300; Mon, 30 Sep 2024 23:37:38 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="XQynRX+K"; 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 F042F10E300 for ; Mon, 30 Sep 2024 23:37:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739457; 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=ACftoemmwJyFTrC4hxNed1Z9He1Jfe5sVp07orpoTOA=; b=XQynRX+KrAGcd9gYDaJB0d3A3ZPcdnw0iLTqNGeFg9Sq4pT5UAetcnSSnHvc/1JrsiRhcj ZkxuYFEzxYPF8KX8uMQOuMJL34tcIqx/+vWXa/FOHWZyzTHjDPisrppdaS3+afizHtZ6gl 0LDmd85u4VJlO9sLvHHM4nLd4QhYSIk= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-270-UwIA5MDvP625sjJaVLOC9Q-1; Mon, 30 Sep 2024 19:37:35 -0400 X-MC-Unique: UwIA5MDvP625sjJaVLOC9Q-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 24B38196A415; Mon, 30 Sep 2024 23:37:32 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id C70673003DEC; Mon, 30 Sep 2024 23:37:26 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , Danilo Krummrich , Mika Westerberg , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 02/35] WIP: rust: drm: Add traits for registering KMS devices Date: Mon, 30 Sep 2024 19:09:45 -0400 Message-ID: <20240930233257.1189730-3-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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 commit adds some traits for registering DRM devices with KMS support, implemented through the kernel::drm::kms::Kms trait. Devices which don't have KMS support can simply use PhantomData. Signed-off-by: Lyude Paul --- TODO: * Generate feature flags automatically, these shouldn't need to be specified by the user Signed-off-by: Lyude Paul --- rust/bindings/bindings_helper.h | 4 + rust/kernel/drm/device.rs | 18 ++- rust/kernel/drm/drv.rs | 45 ++++++- rust/kernel/drm/kms.rs | 230 ++++++++++++++++++++++++++++++++ rust/kernel/drm/kms/fbdev.rs | 45 +++++++ rust/kernel/drm/mod.rs | 1 + 6 files changed, 335 insertions(+), 8 deletions(-) create mode 100644 rust/kernel/drm/kms.rs create mode 100644 rust/kernel/drm/kms/fbdev.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 04898f70ef1b8..4a8e44e11c96a 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -6,11 +6,15 @@ * Sorted alphabetically. */ +#include +#include #include #include #include #include +#include #include +#include #include #include #include diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs index 2b687033caa2d..d4d6b1185f6a6 100644 --- a/rust/kernel/drm/device.rs +++ b/rust/kernel/drm/device.rs @@ -5,14 +5,22 @@ //! C header: [`include/linux/drm/drm_device.h`](srctree/include/linux/drm/drm_device.h) use crate::{ - bindings, device, drm, - drm::drv::AllocImpl, + bindings, device, + drm::{ + drv::AllocImpl, + self, + kms::{KmsImpl, private::KmsImpl as KmsImplPrivate} + }, error::code::*, error::from_err_ptr, error::Result, types::{ARef, AlwaysRefCounted, ForeignOwnable, Opaque}, }; -use core::{ffi::c_void, marker::PhantomData, ptr::NonNull}; +use core::{ + ffi::c_void, + marker::PhantomData, + ptr::NonNull +}; #[cfg(CONFIG_DRM_LEGACY)] macro_rules! drm_legacy_fields { @@ -150,6 +158,10 @@ pub fn data(&self) -> ::Borrowed<'_> { // SAFETY: `Self::data` is always converted and set on device creation. unsafe { ::from_foreign(drm.raw_data()) }; } + + pub(crate) const fn has_kms() -> bool { + ::MODE_CONFIG_OPS.is_some() + } } // SAFETY: DRM device objects are always reference counted and the get/put functions diff --git a/rust/kernel/drm/drv.rs b/rust/kernel/drm/drv.rs index 0cf3fb1cea53c..6b61f2755ba79 100644 --- a/rust/kernel/drm/drv.rs +++ b/rust/kernel/drm/drv.rs @@ -8,7 +8,15 @@ alloc::flags::*, bindings, devres::Devres, - drm, + drm::{ + self, + kms::{ + KmsImpl, + private::KmsImpl as KmsImplPrivate, + Kms + } + }, + device, error::{Error, Result}, private::Sealed, str::CStr, @@ -142,6 +150,12 @@ pub trait Driver { /// The type used to represent a DRM File (client) type File: drm::file::DriverFile; + /// The KMS implementation for this driver. + /// + /// Drivers that wish to support KMS should pass their implementation of `drm::kms::KmsDriver` + /// here. Drivers which do not have KMS support can simply pass `drm::kms::NoKms` here. + type Kms: drm::kms::KmsImpl where Self: Sized; + /// Driver metadata const INFO: DriverInfo; @@ -159,21 +173,36 @@ pub trait Driver { impl Registration { /// Creates a new [`Registration`] and registers it. - pub fn new(drm: ARef>, flags: usize) -> Result { + pub fn new(dev: &device::Device, data: T::Data, flags: usize) -> Result { + let drm = drm::device::Device::::new(dev, data)?; + let has_kms = drm::device::Device::::has_kms(); + + let mode_config_info = if has_kms { + // SAFETY: We have yet to register this device + Some(unsafe { T::Kms::setup_kms(&drm)? }) + } else { + None + }; + // SAFETY: Safe by the invariants of `drm::device::Device`. let ret = unsafe { bindings::drm_dev_register(drm.as_raw(), flags as u64) }; if ret < 0 { return Err(Error::from_errno(ret)); } + if let Some(ref info) = mode_config_info { + // SAFETY: We just registered the device above + unsafe { T::Kms::setup_fbdev(&drm, info) }; + } + Ok(Self(drm)) } /// Same as [`Registration::new`}, but transfers ownership of the [`Registration`] to `Devres`. - pub fn new_foreign_owned(drm: ARef>, flags: usize) -> Result { - let reg = Registration::::new(drm.clone(), flags)?; + pub fn new_foreign_owned(dev: &device::Device, data: T::Data, flags: usize) -> Result { + let reg = Registration::::new(dev, data, flags)?; - Devres::new_foreign_owned(drm.as_ref(), reg, GFP_KERNEL) + Devres::new_foreign_owned(dev, reg, GFP_KERNEL) } /// Returns a reference to the `Device` instance for this registration. @@ -195,5 +224,11 @@ fn drop(&mut self) { // SAFETY: Safe by the invariant of `ARef>`. The existance of this // `Registration` also guarantees the this `drm::device::Device` is actually registered. unsafe { bindings::drm_dev_unregister(self.0.as_raw()) }; + + if drm::device::Device::::has_kms() { + // SAFETY: We just checked above that KMS was setup for this device, so this is safe to + // call + unsafe { bindings::drm_atomic_helper_shutdown(self.0.as_raw()) } + } } } diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs new file mode 100644 index 0000000000000..d3558a5eccc54 --- /dev/null +++ b/rust/kernel/drm/kms.rs @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +//! KMS driver abstractions for rust. + +pub mod fbdev; + +use crate::{ + drm::{ + drv::Driver, + device::Device + }, + device, + prelude::*, + types::*, + error::to_result, + private::Sealed, +}; +use core::{ + ops::Deref, + ptr::{self, NonNull}, + mem::{self, ManuallyDrop}, + marker::PhantomData, +}; +use bindings; + +/// The C vtable for a [`Device`]. +/// +/// This is created internally by DRM. +pub(crate) struct ModeConfigOps { + pub(crate) kms_vtable: bindings::drm_mode_config_funcs, + pub(crate) kms_helper_vtable: bindings::drm_mode_config_helper_funcs +} + +/// A trait representing a type that can be used for setting up KMS, or a stub. +/// +/// For drivers which don't have KMS support, the methods provided by this trait may be stubs. It is +/// implemented internally by DRM. +pub trait KmsImpl: private::KmsImpl {} + +pub(crate) mod private { + use super::*; + + /// Private callback implemented internally by DRM for setting up KMS on a device, or stubbing + /// the KMS setup for devices which don't have KMS support can just use [`PhantomData`]. + pub trait KmsImpl { + /// The parent driver for this KMS implementation + type Driver: Driver; + + /// The optional KMS callback operations for this driver. + const MODE_CONFIG_OPS: Option; + + /// The callback for setting up KMS on a device + /// + /// # Safety + /// + /// `drm` must be unregistered. + unsafe fn setup_kms(drm: &Device) -> Result { + build_error::build_error("This should never be reachable") + } + + /// The callback for setting up fbdev emulation on a KMS device. + /// + /// # Safety + /// + /// `drm` must be registered. + unsafe fn setup_fbdev(drm: &Device, mode_config_info: &ModeConfigInfo) { + build_error::build_error("This should never be reachable") + } + } +} + +/// A [`Device`] with KMS initialized that has not been registered with userspace. +/// +/// This type is identical to [`Device`], except that it is able to create new static KMS resources. +/// It represents a KMS device that is not yet visible to userspace, and also contains miscellaneous +/// state required during the initialization process of a [`Device`]. +pub struct UnregisteredKmsDevice<'a, T: Driver> { + drm: &'a Device, +} + +impl<'a, T: Driver> Deref for UnregisteredKmsDevice<'a, T> { + type Target = Device; + + fn deref(&self) -> &Self::Target { + self.drm + } +} + +impl<'a, T: Driver> UnregisteredKmsDevice<'a, T> { + /// Construct a new [`UnregisteredKmsDevice`]. + /// + /// # Safety + /// + /// The caller promises that `drm` is an unregistered [`Device`]. + pub(crate) unsafe fn new(drm: &'a Device) -> Self { + Self { + drm, + } + } +} + +/// A trait which must be implemented by drivers that wish to support KMS +/// +/// It should be implemented for the same type that implements [`Driver`]. Drivers which don't +/// support KMS should use [`PhantomData`]. +/// +/// [`PhantomData`]: PhantomData +#[vtable] +pub trait Kms { + /// The parent [`Driver`] for this [`Device`]. + type Driver: KmsDriver; + + /// The fbdev implementation to use for this [`Device`]. + /// + /// Which implementation may be used here depends on the GEM implementation specified in + /// [`Driver::Object`]. See [`fbdev`] for more information. + type Fbdev: fbdev::FbdevImpl; + + /// Return a [`ModeConfigInfo`] structure for this [`device::Device`]. + fn mode_config_info( + dev: &device::Device, + drm_data: <::Data as ForeignOwnable>::Borrowed<'_>, + ) -> Result; + + /// Create mode objects like [`crtc::Crtc`], [`plane::Plane`], etc. for this device + fn create_objects(drm: &UnregisteredKmsDevice<'_, Self::Driver>) -> Result; +} + +impl private::KmsImpl for T { + type Driver = T::Driver; + + const MODE_CONFIG_OPS: Option = Some(ModeConfigOps { + kms_vtable: bindings::drm_mode_config_funcs { + atomic_check: Some(bindings::drm_atomic_helper_check), + // 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, + }, + + kms_helper_vtable: bindings::drm_mode_config_helper_funcs { + atomic_commit_setup: None, // TODO + atomic_commit_tail: None, // TODO + }, + }); + + unsafe fn setup_kms(drm: &Device) -> Result { + let mode_config_info = T::mode_config_info(drm.as_ref(), drm.data())?; + + // SAFETY: `MODE_CONFIG_OPS` is always Some() in this implementation + let ops = unsafe { T::MODE_CONFIG_OPS.as_ref().unwrap_unchecked() }; + + // SAFETY: + // - This function can only be called before registration via our safety contract. + // - Before registration, we are the only ones with access to this device. + unsafe { + (*drm.as_raw()).mode_config = bindings::drm_mode_config { + funcs: &ops.kms_vtable, + helper_private: &ops.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: We just setup all of the required info this function needs in `drm_device` + to_result(unsafe { bindings::drmm_mode_config_init(drm.as_raw()) })?; + + // SAFETY: `drm` is guaranteed to be unregistered via our safety contract. + let drm = unsafe { UnregisteredKmsDevice::new(drm) }; + + T::create_objects(&drm)?; + + // TODO: Eventually add a hook to customize how state readback happens, for now just reset + // SAFETY: Since all static modesetting objects were created in `T::create_objects()`, and + // that is the only place they can be created, this fulfills the C API requirements. + unsafe { bindings::drm_mode_config_reset(drm.as_raw()) }; + + Ok(mode_config_info) + } + + unsafe fn setup_fbdev(drm: &Device, mode_config_info: &ModeConfigInfo) { + <::Fbdev as fbdev::private::FbdevImpl>::setup_fbdev(drm, mode_config_info) + } +} + +impl KmsImpl for T {} + +impl private::KmsImpl for PhantomData { + type Driver = T; + + const MODE_CONFIG_OPS: Option = None; +} + +impl KmsImpl for PhantomData {} + +/// Various device-wide information for a [`Device`] that is provided during initialization. +#[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, +} + +/// A [`Driver`] with [`Kms`] implemented. +/// +/// This is implemented internally by DRM for any [`Device`] whose [`Driver`] type implements +/// [`Kms`], and provides access to methods which are only safe to use with KMS devices. +pub trait KmsDriver: Driver {} + +impl KmsDriver for T +where + T: Driver, + K: Kms {} diff --git a/rust/kernel/drm/kms/fbdev.rs b/rust/kernel/drm/kms/fbdev.rs new file mode 100644 index 0000000000000..bdf97500137d8 --- /dev/null +++ b/rust/kernel/drm/kms/fbdev.rs @@ -0,0 +1,45 @@ +//! Fbdev helper implementations for rust. +//! +//! This module provides the various Fbdev implementations that can be used by Rust KMS drivers. +use core::marker::*; +use crate::{private::Sealed, drm::{kms::*, device::Device, gem}}; +use bindings; + +pub(crate) mod private { + use super::*; + + pub trait FbdevImpl { + /// Setup the fbdev implementation for this KMS driver. + fn setup_fbdev(drm: &Device, mode_config_info: &ModeConfigInfo); + } +} + +/// The main trait for a driver's DRM implementation. +/// +/// Drivers are expected not to implement this directly, and to instead use one of the objects +/// provided by this module such as [`FbdevDma`]. +pub trait FbdevImpl: private::FbdevImpl {} + +/// The fbdev implementation for drivers using the gem DMA helpers. +/// +/// Drivers which use the gem DMA helpers ([`gem::Object`]) should use this for their [`Kms::Fbdev`] +/// type. +pub struct FbdevDma(PhantomData); + +impl private::FbdevImpl for FbdevDma +where + T: Driver>, + G: gem::DriverObject +{ + #[inline] + fn setup_fbdev(drm: &Device, mode_config_info: &ModeConfigInfo) { + // SAFETY: Our implementation bounds re proof that this driver is using the gem dma helpers + unsafe { bindings::drm_fbdev_dma_setup(drm.as_raw(), mode_config_info.preferred_depth) }; + } +} + +impl FbdevImpl for FbdevDma +where + T: Driver>, + G: gem::DriverObject +{} 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 Mon Sep 30 23:09:46 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817256 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 2940ECEB2CB for ; Mon, 30 Sep 2024 23:37:49 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id A06FD10E301; Mon, 30 Sep 2024 23:37:48 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="PRdexgwx"; 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 1000810E301 for ; Mon, 30 Sep 2024 23:37:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739465; 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=6Zb0rBlFBS6DnNXycQrkSdH1hnOzRIIqb3zhPvtIptc=; b=PRdexgwxr8Ff5JiHik4JwvmKQ26HWj0b4csdcC/Mb+FvVxrEureomUqMyaRCG5Gn7e0WjC 4cl/hmBwg6mDEtKqFbvsiet+QqWK3l6ar9STq/Y3n0S0cOiaDo2mv8r+KEkPWAHDc5dHNX tBV52YLu9gXrKG6F9LNUg+o2opL8yfk= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-478-OoxvmIacM-WG0ZEoppEFHw-1; Mon, 30 Sep 2024 19:37:42 -0400 X-MC-Unique: OoxvmIacM-WG0ZEoppEFHw-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id E4735196A40C; Mon, 30 Sep 2024 23:37:39 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 738DD3003DEC; Mon, 30 Sep 2024 23:37:35 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , Danilo Krummrich , Mika Westerberg , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 03/35] rust: drm/kms/fbdev: Add FbdevShmem Date: Mon, 30 Sep 2024 19:09:46 -0400 Message-ID: <20240930233257.1189730-4-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" For drivers which use the shmem based GEM helpers, they'll want to use the relevant drm_fbdev_shmem_setup() functions instead of the drm_fbdev_dma_setup() functions. To allow for this, introduce another FbdevImpl that such drivers can use instead of FbdevDma. Signed-off-by: Lyude Paul --- rust/bindings/bindings_helper.h | 1 + rust/kernel/drm/kms/fbdev.rs | 8 +++++++- rust/kernel/drm/kms/fbdev/shmem.rs | 33 ++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 rust/kernel/drm/kms/fbdev/shmem.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 4a8e44e11c96a..9803e0ecac7c1 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/drm/kms/fbdev.rs b/rust/kernel/drm/kms/fbdev.rs index bdf97500137d8..a1035d948949a 100644 --- a/rust/kernel/drm/kms/fbdev.rs +++ b/rust/kernel/drm/kms/fbdev.rs @@ -5,6 +5,12 @@ use crate::{private::Sealed, drm::{kms::*, device::Device, gem}}; use bindings; +#[cfg(CONFIG_DRM_GEM_SHMEM_HELPER = "y")] +mod shmem; + +#[cfg(CONFIG_DRM_GEM_SHMEM_HELPER = "y")] +pub use shmem::FbdevShmem; + pub(crate) mod private { use super::*; @@ -17,7 +23,7 @@ pub trait FbdevImpl { /// The main trait for a driver's DRM implementation. /// /// Drivers are expected not to implement this directly, and to instead use one of the objects -/// provided by this module such as [`FbdevDma`]. +/// provided by this module such as [`FbdevDma`] and [`FbdevShmem`]. pub trait FbdevImpl: private::FbdevImpl {} /// The fbdev implementation for drivers using the gem DMA helpers. diff --git a/rust/kernel/drm/kms/fbdev/shmem.rs b/rust/kernel/drm/kms/fbdev/shmem.rs new file mode 100644 index 0000000000000..16c3533ad2a0f --- /dev/null +++ b/rust/kernel/drm/kms/fbdev/shmem.rs @@ -0,0 +1,33 @@ +//! The GEM shmem fbdev implementation for rust. +//! +//! This module provides an Fbdev implementation that can be used by Rust KMS drivers using the GEM +//! shmem helpers provided by [`shmem`]. + +use core::marker::*; +use crate::drm::{gem::shmem, kms::*, device::Device}; +use super::{private::FbdevImpl as FbdevImplPrivate, FbdevImpl}; +use bindings; + +/// The fbdev implementation for drivers using the gem shmem helpers. +/// +/// KMS Drivers which use the GEM helpers provided by [`shmem`] should use this for [`Kms::Fbdev`]. +pub struct FbdevShmem(PhantomData); + +impl FbdevImplPrivate for FbdevShmem +where + T: Driver>, + G: shmem::DriverObject +{ + #[inline] + fn setup_fbdev(drm: &Device, mode_config_info: &ModeConfigInfo) { + // SAFETY: Our implementation bounds are proof that this driver is using the gem shmem + // helpers + unsafe { bindings::drm_fbdev_shmem_setup(drm.as_raw(), mode_config_info.preferred_depth) }; + } +} + +impl FbdevImpl for FbdevShmem +where + T: Driver>, + G: shmem::DriverObject +{} From patchwork Mon Sep 30 23:09:47 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817257 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 29CCDCEB2CD for ; Mon, 30 Sep 2024 23:37:55 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id AEB8B10E303; Mon, 30 Sep 2024 23:37:54 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="KJiIeoll"; 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 6CA6910E303 for ; Mon, 30 Sep 2024 23:37:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739472; 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=2YOF0nj/JdsgddKgtxQOhYZS7F2Gd3au2zY7Q0sSNhM=; b=KJiIeollnapJ+X2+OrudVIDuOql2PNJwma3cyFEDgY1+vXGg6tlJtHGAhDfjfUrv/pMRl6 Lcy5DvbZm3/8KIJ8EoEsnGcN9BzAjch4VWT2O+X/33R4yrUN2LOU8sOj1cTU8bO6Qh1Tkn ifPgNCWrDKtbL1/90nrqr2ckuC7JzQY= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-395-L7kSxfCBMXKWZJN1lUUehw-1; Mon, 30 Sep 2024 19:37:49 -0400 X-MC-Unique: L7kSxfCBMXKWZJN1lUUehw-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id D979919626D4; Mon, 30 Sep 2024 23:37:46 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id D62F23003DEC; Mon, 30 Sep 2024 23:37:42 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , Danilo Krummrich , Mika Westerberg , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 04/35] rust: drm/kms: Introduce the main ModeConfigObject traits Date: Mon, 30 Sep 2024 19:09:47 -0400 Message-ID: <20240930233257.1189730-5-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" The KMS API has a very consistent idea of a "mode config object", which includes any object with a drm_mode_object struct embedded in it. These objects have their own object IDs which DRM exposes to userspace, and we introduce the ModeConfigObject trait to represent any object matching these characteristics. One slightly less consistent trait of these objects however: some mode objects have a reference count, while others don't. Since rust requires that we are able to define the lifetime of an object up-front, we introduce two other super-traits of ModeConfigObject for this: * StaticModeObject - this trait represents any mode object which does not have a reference count of its own. Such objects can be considered to share the lifetime of their parent KMS device * RcModeObject - this trait represents any mode object which does have its own reference count. Objects implementing this trait get a free blanket implementation of AlwaysRefCounted, and as such can be used with the ARef container without us having to implement AlwaysRefCounted for each individual mode object. This will be able to handle most lifetimes we'll need with one exception: it's entirely possible a driver may want to hold a "owned" reference to a static mode object. We allow for this by introducing the KmsRef container, which grabs an owned refcount to the parent KMS device of a StaticModeObject and holds a pointer to said object - essentially allowing it to act identically to an owned refcount by preventing the device's lifetime from ending until the KmsRef is dropped. I choose not to use AlwaysRefCounted for this as holding a refcount to the device has its own set of implications since if you forget to drop the KmsRef the device will never be destroyed. Signed-off-by: Lyude Paul --- rust/bindings/bindings_helper.h | 1 + rust/kernel/drm/kms.rs | 107 ++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 9803e0ecac7c1..ba1871b05b7fa 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs index d3558a5eccc54..f1a8ba4b7e296 100644 --- a/rust/kernel/drm/kms.rs +++ b/rust/kernel/drm/kms.rs @@ -228,3 +228,110 @@ impl KmsDriver for T where T: Driver, K: Kms {} + +/// A modesetting object in DRM. +/// +/// This is any type of object where the underlying C object contains a [`struct drm_mode_object`]. +/// +/// [`struct drm_mode_object`]: srctree/include/drm/drm_mode_object.h +pub trait ModeObject: Sealed + Send + Sync { + /// The parent driver for this [`ModeObject`]. + type Driver: KmsDriver; + + /// Return the [`Device`] for this [`ModeObject`]. + fn drm_dev(&self) -> &Device; + + /// Return a pointer to the [`struct drm_mode_object`] for this [`ModeObject`]. + /// + /// [`struct drm_mode_object`]: (srctree/include/drm/drm_mode_object.h) + fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object; +} + +/// A trait for modesetting objects which don't come with their own reference-counting. +/// +/// Some [`ModeObject`] types in DRM do not have a reference count. These types are considered +/// "static" and share the lifetime of their parent [`Device`]. To retrieve an owned reference to +/// such types, see [`KmsRef`]. +/// +/// # Safety +/// +/// This trait must only be implemented for modesetting objects which do not have a refcount within +/// their [`struct drm_mode_object`], otherwise [`KmsRef`] can't guarantee the object will stay +/// alive. +/// +/// [`struct drm_mode_object`]: (srctree/include/drm/drm_mode_object.h) +pub unsafe trait StaticModeObject: ModeObject {} + +/// An owned reference to a [`StaticModeObject`]. +/// +/// Note that since [`StaticModeObject`] types share the lifetime of their parent [`Device`], the +/// parent [`Device`] will stay alive as long as this type exists. Thus, users should be aware that +/// storing a [`KmsRef`] within a [`ModeObject`] is a circular reference. +/// +/// # Invariants +/// +/// `self.0` points to a valid instance of `T` throughout the lifetime of this type. +pub struct KmsRef(NonNull); + +// SAFETY: Owned references to DRM device are thread-safe. +unsafe impl Send for KmsRef {} +unsafe impl Sync for KmsRef {} + +impl From<&T> for KmsRef { + fn from(value: &T) -> Self { + // We will drop the reference we leak here in Drop + value.drm_dev().inc_ref(); + + Self(value.into()) + } +} + +impl Drop for KmsRef { + fn drop(&mut self) { + // SAFETY: We're reclaiming the reference we leaked in From<&T> + drop(unsafe { ARef::from_raw(self.drm_dev().into()) }) + } +} + +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.0.as_ref() } + } +} + +impl Clone for KmsRef { + fn clone(&self) -> Self { + self.drm_dev().inc_ref(); + + Self(self.0) + } +} + +/// A trait for [`ModeObject`] which is reference counted. +/// +/// This trait is implemented by DRM for any [`ModeObject`] which has a reference count provided by +/// [`struct drm_mode_object`]. It provides a common implementation of [`AlwaysRefCounted`], since +/// all [`RcModeObject`] types use the same functions for refcounting. +/// +/// # Safety +/// +/// The [`ModeObject`] must initialize the refcount in its [`struct drm_mode_object`] field. +/// +/// [`struct drm_mode_object`]: (srctree/include/drm/drm_mode_object.h) +pub unsafe trait RcModeObject: ModeObject {} + +unsafe impl AlwaysRefCounted for T { + fn inc_ref(&self) { + // SAFETY: FFI call with no special requirements + unsafe { bindings::drm_mode_object_get(self.raw_mode_obj()) } + } + + unsafe fn dec_ref(obj: ptr::NonNull) { + // SAFETY: We never expose modesetting objects in our interfaces to users before they're + // initialized + unsafe { bindings::drm_mode_object_put(obj.as_ref().raw_mode_obj()) } + } +} From patchwork Mon Sep 30 23:09:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817258 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 4C3BBCEB2CD for ; Mon, 30 Sep 2024 23:38:05 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id C58E310E302; Mon, 30 Sep 2024 23:38:04 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="MNnujE+H"; 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 01B4E10E302 for ; Mon, 30 Sep 2024 23:38:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739482; 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=I+9wfJ/uruhUx/dSaxcJTr6CZ1x1jlWlTi1id3rLICQ=; b=MNnujE+H6a2XukpVtNQAxcSBRBAgBHeTtb3xlt5xR3MhnipakRU74WgqrPGkDqUwN4Zeii T46RjFm6/mBYdfbo8G1q8GkGKF8ya4f9p+qruvypU/OHN08iEriUk5udo8eJRjH/HLE3+f QqtMW9sjuAuB6XlAgbIIVQdtXOwQZOU= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-608-8oUkX7nGM3ewj14vrOCuNA-1; Mon, 30 Sep 2024 19:37:58 -0400 X-MC-Unique: 8oUkX7nGM3ewj14vrOCuNA-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 2282B193EB0E; Mon, 30 Sep 2024 23:37:56 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 6FE5E3003FD1; Mon, 30 Sep 2024 23:37:52 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 05/35] rust: drm/kms: Add bindings for drm_connector Date: Mon, 30 Sep 2024 19:09:48 -0400 Message-ID: <20240930233257.1189730-6-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" We start off by introducing wrappers for the first important type of mode object: a DRM display connector. This introduces Connector and ConnectorState. Both DriverConnector and DriverConnectorState must be implemented by KMS drivers, and a driver may have as many implementations of these two traits as it needs. This also introduces the general data pattern we'll be using for all of the core mode objects that can be used in atomic commits. It's important to note that both Connector and ConnectorState are intended to be "subclassable". To explain what this means, we need to look at how a DRM driver normally uses objects like DRM connectors. Typically, a driver in C will define its connectors like so: struct foo_connector { struct drm_connector base; int bar; } Note that we have a drm_connector struct embedded in foo_connector, but we have data which comes after it which is defined by the driver. This is important for a number of reasons: connectors can have their own mutexes and various other hardware-specific information that a driver may want access to at any time. The same goes for drm_connector_state, where drivers will subclass this struct in the same way. It's worth noting as well that it isn't uncommon for a driver to have multiple types of connectors, but we'll handle in a later commit. As a result, we've designed Connector and ConnectorState so that for both types: a DRM driver can add custom data into the T. As well, there's some basic limitations on how this data may be accessed: * Data within the `DriverConnector` struct is pinned in order to allow mutexes and other structs which need pinning to be stored within it. As well, it is impossible to get a direct mutable reference to the data within DriverConnector - as there's no locks for doing so which would cause a race condition. * Data within the `DriverConnectorState` struct is currently not pinned. While it's not unheard of for a driver to put something like a mutex in its atomic states, (VKMS actually does this in some spots) this quickly complicates things especially with nonblocking modesets - and doesn't really fit into the philosophy of an atomic state anyway. We may add support for this in the future later if this does end up being needed, but for now we hold back in order to make it much easier for drivers to access private data within the atomic state. As well, the functions we provide for converting to/from raw connector state pointers are notably different from many other rust types in the kernel. Instead of converting raw state pointers to raw ConnectorState pointers, we allow for direct immutable and mutable references. The reason for this is that it makes accessing private driver data in the state much easier, and unlike Connector - we can actually uphold all of the required data aliasing rules thanks to states only being mutable by a single thread before they've been swapped in. Note that currently, we don't provide a way to access said private data for ConnectorState since allowing direct access to a &mut ConnectorState could allow a caller to modify portions of drm_connector_state which are meant to be invariant throughout the lifetime of the connector state. We'll address this in the next few commits when we introduce the global atomic state type. And finally - we introduce the following internal traits for the crate side of things: * AsRawConnector - any type which can spit out a *mut bindings::drm_connector or be recovered from one * AsRawConnectorState - any type which can return a reference to a bindings::drm_connector_state * private::AsRawConnectorState - just methods for AsRawConnectorState that we don't want to be accessible to our users (since they could be used to introduce UB) * FromRawConnectorState - any type which can be recovered from a raw pointer to a bindings::drm_connector_state The reason for having AsRawConnectorState and FromRawConnectorState as separate traits unlike AsRawConnector is due to the fact that we'll introduce objects later on which can be used as DRM connector states, but cannot be directly derived from a *mut bindings::drm_connector_state because they hold additional state or have additional side-effects. Likewise, we'll also have other objects which can be used as raw DRM connectors - hence AsRawConnector. Signed-off-by: Lyude Paul --- rust/kernel/drm/kms.rs | 1 + rust/kernel/drm/kms/connector.rs | 531 +++++++++++++++++++++++++++++++ 2 files changed, 532 insertions(+) create mode 100644 rust/kernel/drm/kms/connector.rs diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs index f1a8ba4b7e296..0138e6830b48c 100644 --- a/rust/kernel/drm/kms.rs +++ b/rust/kernel/drm/kms.rs @@ -2,6 +2,7 @@ //! KMS driver abstractions for rust. +pub mod connector; pub mod fbdev; use crate::{ diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs new file mode 100644 index 0000000000000..0fa927a3743b2 --- /dev/null +++ b/rust/kernel/drm/kms/connector.rs @@ -0,0 +1,531 @@ +// 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, + private::Sealed, +}; +use core::{ + marker::*, + ptr::null_mut, + mem, + ptr::{self, NonNull, addr_of_mut}, + ffi::*, + ops::*, + cell::Cell, +}; +use super::{ + ModeObject, + RcModeObject, + 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, +}; + +/// The main trait for implementing the [`struct drm_connector`] API for [`Connector`]. +/// +/// Any KMS driver should have at least one implementation of this type, which allows them to create +/// [`Connector`] objects. Additionally, a driver may store driver-private data within the type that +/// implements [`DriverConnector`] - and it will be made available when using a fully typed +/// [`Connector`] object. +/// +/// # Invariants +/// +/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in +/// [`struct drm_connector`] pointers are contained within a [`Connector`]. +/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in +/// [`struct drm_connector_state`] pointers are contained within a +/// [`ConnectorState`]. +/// +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h +/// [`struct drm_connector_state`]: srctree/include/drm/drm_connector.h +#[vtable] +pub trait DriverConnector: Send + Sync + Sized { + /// The generated C vtable for this [`DriverConnector`] implementation + #[unique] + const OPS: &'static DriverConnectorOps = &DriverConnectorOps { + 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: None, + debugfs_init: None, + oob_hotplug_event: None, + atomic_duplicate_state: Some(atomic_duplicate_state_callback::), + }, + helper_funcs: bindings::drm_connector_helper_funcs { + mode_valid: None, + atomic_check: None, + get_modes: None, + 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, + } + }; + + /// The type to pass to the `args` field of [`Connector::new`]. + /// + /// This type will be made available in in the `args` argument of [`Self::new`]. Drivers which + /// don't need this can simply pass [`()`] here. + type Args; + + /// The parent [`Driver`] implementation. + type Driver: KmsDriver; + + /// The [`DriverConnectorState`] implementation for this [`DriverConnector`]. + /// + /// See [`DriverConnectorState`] for more info. + type State: DriverConnectorState; + + /// The constructor for creating a [`Connector`] using this [`DriverConnector`] implementation. + /// + /// Drivers may use this to instantiate their [`DriverConnector`] object. + fn new(device: &Device, args: Self::Args) -> impl PinInit; +} + +/// The generated C vtable for a [`DriverConnector`]. +/// +/// This type is created internally by DRM. +pub struct DriverConnectorOps { + funcs: bindings::drm_connector_funcs, + helper_funcs: bindings::drm_connector_helper_funcs, +} + +/// The main interface for a [`struct drm_connector`]. +/// +/// This type is the main interface for dealing with DRM connectors. In addition, it also allows +/// immutable access to whatever private data is contained within an implementor's +/// [`DriverConnector`] type. +/// +/// # Invariants +/// +/// - The DRM C API and our interface guarantees that only the user has mutable access to `state`, +/// up until [`drm_atomic_helper_commit_hw_done`] is called. Therefore, `plane` follows rust's +/// data aliasing rules and does not need to be behind an [`Opaque`] type. +/// - `connector` and `inner` are initialized for as long as this object is made available to users. +/// - The data layout of this structure begins with [`struct drm_connector`]. +/// - The atomic state for this type can always be assumed to be of type +/// [`ConnectorState`]. +/// +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h +/// [`drm_atomic_helper_commit_hw_done`]: srctree/include/drm/drm_atomic_helper.h +#[repr(C)] +#[pin_data] +pub struct Connector { + connector: Opaque, + #[pin] + inner: T, + #[pin] + _p: PhantomPinned +} + +impl Sealed for Connector { } + +// SAFETY: DRM expects this struct to be zero-initialized +unsafe impl Zeroable for bindings::drm_connector { } + +impl Deref for Connector { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl Connector { + /// Construct a new [`Connector`]. + /// + /// A driver may use this to create new [`Connector`] objects. + /// + /// TODO: Add a way to handle creating connectors after device registration. Also, that's why we + /// don't ask for a UnregisteredKms device here. + /// + /// [`Kms::create_objects`]: kernel::drm::kms::Kms::create_objects + 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: &T::OPS.helper_funcs, + ..Default::default() + }), + inner <- T::new(dev, args), + _p: PhantomPinned + }), + GFP_KERNEL + )?; + + // SAFETY: FFI call with no special safety requirements + to_result(unsafe { + bindings::drm_connector_init( + dev.as_raw(), + new.as_raw(), + &T::OPS.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)))) + }) + } +} + +/// A trait implemented by any type that acts as a [`struct drm_connector`] interface. +/// +/// This is implemented internally by DRM. +/// +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h +pub trait AsRawConnector: RcModeObject { + /// The parent [`Driver`] for this [`Connector`] interface + /// + /// TODO: get rid of this + type Driver: KmsDriver; + + /// The type this connector interface returns for its atomic state + type State: FromRawConnectorState; + + /// Return the raw [`struct drm_connector`] for this DRM connector. + /// + /// Drivers should never use this directly + /// + /// [`struct drm_Connector`]: srctree/include/drm/drm_connector.h + fn as_raw(&self) -> *mut bindings::drm_connector; + + /// Convert a raw `bindings::drm_connector` pointer into an object of this type. + /// + /// # Safety + /// + /// Callers promise that `ptr` points to a valid instance of this type. + unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a Self; +} + +// SAFETY: Our connector interfaces are guaranteed to be thread-safe +unsafe impl Send for Connector {} + +// SAFETY: Our connector interfaces are guaranteed to be thread-safe +unsafe impl Sync for Connector {} + +impl ModeObject for Connector { + type Driver = T::Driver; + + fn drm_dev(&self) -> &Device { + // SAFETY: The parent device for a DRM connector will never outlive the connector, and this + // pointer is invariant through the lifetime of the connector + unsafe { Device::borrow((*self.as_raw()).dev) } + } + + fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object { + // SAFETY: We don't expose DRM connectors to users before `base` is initialized + unsafe { &mut (*self.as_raw()).base } + } +} + +// SAFETY: DRM connectors are refcounted mode objects +unsafe impl RcModeObject for Connector {} + +impl AsRawConnector for Connector { + type Driver = T::Driver; + type State = ConnectorState; + + fn as_raw(&self) -> *mut bindings::drm_connector { + self.connector.get() + } + + unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a Self { + // SAFETY: Our data layout starts with `bindings::drm_connector` + unsafe { &*ptr.cast() } + } +} + +unsafe extern "C" fn connector_destroy_callback( + connector: *mut bindings::drm_connector, +) { + // SAFETY: DRM guarantees that `connector` points to a valid initialized `drm_connector`. + unsafe { + bindings::drm_connector_unregister(connector); + bindings::drm_connector_cleanup(connector); + }; + + // SAFETY: + // * We originally created the connector in a `Box` + // * We are guaranteed to hold the last remaining reference to this connector + // * This cast is safe via `DriverConnector`s type invariants. + drop(unsafe { Box::from_raw(connector as *mut Connector) }); +} + +// SAFETY: DRM expects this struct to be zero-initialized +unsafe impl Zeroable for bindings::drm_connector_state {} + +/// A trait implemented by any type which can produce a reference to a [`struct drm_connector_state`]. +/// +/// This is implemented internally by DRM. +/// +/// [`struct drm_connector_state`]: srctree/include/drm/drm_connector.h +pub trait AsRawConnectorState: private::AsRawConnectorState { + /// The type that represents this connector state's DRM connector. + type Connector: AsRawConnector; +} + +pub(super) mod private { + use super::*; + + /// Trait for retrieving references to the base connector state contained within any connector + /// state compatible type + #[doc(hidden)] + pub trait AsRawConnectorState { + /// Return an immutable reference to the raw connector state. + fn as_raw(&self) -> &bindings::drm_connector_state; + + /// Get a mutable reference to the raw [`struct drm_connector_state`] contained within this + /// type. + /// + /// + /// # Safety + /// + /// The caller promises this mutable reference will not be used to modify any contents of + /// [`struct drm_connector_state`] which DRM would consider to be static - like the + /// backpointer to the DRM connector that owns this state. This also means the mutable + /// reference should never be exposed outside of this crate. + /// + /// [`struct drm_connector_state`]: srctree/include/drm/drm_connector.h + unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_connector_state; + } +} + +pub(super) use private::AsRawConnectorState as AsRawConnectorStatePrivate; + +/// A trait implemented for any type which can be constructed directly from a +/// [`struct drm_connector_state`] pointer. +/// +/// This is implemented internally by DRM. +/// +/// [`struct drm_connector_state`]: srctree/include/drm/drm_connector.h +pub trait FromRawConnectorState: AsRawConnectorState { + /// Get an immutable reference to this type from the given raw [`struct drm_connector_state`] + /// pointer + /// + /// # Safety + /// + /// The caller guarantees `ptr` is contained within a valid instance of `Self` + /// + /// [`struct drm_connector_state`]: srctree/include/drm/drm_connector.h + unsafe fn from_raw<'a>(ptr: *const bindings::drm_connector_state) -> &'a Self; + + /// Get a mutable reference to this type from the given raw [`struct drm_connector_state`] + /// pointer + /// + /// # Safety + /// + /// The caller guarantees `ptr` is contained within a valid instance of `Self`, and that no + /// other references (mutable or immutable) to `ptr` exist. + /// + /// [`struct drm_connector_state`]: srctree/include/drm/drm_connector.h + unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_connector_state) -> &'a mut Self; +} + +/// The main interface for a [`struct drm_connector_state`]. +/// +/// This type is the main interface for dealing with the atomic state of DRM connectors. In +/// addition, it allows access to whatever private data is contained within an implementor's +/// [`DriverConnectorState`] type. +/// +/// # Invariants +/// +/// - The DRM C API and our interface guarantees that only the user has mutable access to `state`, +/// up until [`drm_atomic_helper_commit_hw_done`] is called. Therefore, `connector` follows rust's +/// data aliasing rules and does not need to be behind an [`Opaque`] type. +/// - `state` and `inner` initialized for as long as this object is exposed to users. +/// - The data layout of this structure begins with [`struct drm_connector_state`]. +/// - The connector for this atomic state can always be assumed to be of type +/// [`Connector`]. +/// +/// [`struct drm_connector_state`]: srctree/include/drm/drm_connector.h +/// [`drm_atomic_helper_commit_hw_done`]: srctree/include/drm/drm_atomic_helper.h +#[derive(Default)] +#[repr(C)] +pub struct ConnectorState { + state: bindings::drm_connector_state, + inner: T, +} + +/// The main trait for implementing the [`struct drm_connector_state`] API for a [`Connector`]. +/// +/// A driver may store driver-private data within the implementor's type, which will be available +/// when using a full typed [`ConnectorState`] object. +/// +/// # Invariants +/// +/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in +/// [`struct drm_connector`] pointers are contained within a [`Connector`]. +/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in +/// [`struct drm_connector_state`] pointers are contained within a [`ConnectorState`]. +/// +/// [`struct drm_connector`]: srctree/include/drm_connector.h +/// [`struct drm_connector_state`]: srctree/include/drm_connector.h +pub trait DriverConnectorState: Clone + Default + Sized { + /// The parent [`DriverConnector`]. + type Connector: DriverConnector; +} + +impl Sealed for ConnectorState {} + +impl AsRawConnectorState for ConnectorState { + type Connector = Connector; +} + +impl private::AsRawConnectorState for ConnectorState { + fn as_raw(&self) -> &bindings::drm_connector_state { + &self.state + } + + unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_connector_state { + &mut self.state + } +} + +impl FromRawConnectorState for ConnectorState { + unsafe fn from_raw<'a>(ptr: *const bindings::drm_connector_state) -> &'a Self { + // SAFETY: Our data layout starts with `bindings::drm_connector_state` + unsafe { &*ptr.cast() } + } + + unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_connector_state) -> &'a mut Self { + // SAFETY: Our data layout starts with `bindings::drm_connector_state` + unsafe { &mut *ptr.cast() } + } +} + +unsafe extern "C" fn atomic_duplicate_state_callback( + connector: *mut bindings::drm_connector +) -> *mut bindings::drm_connector_state +{ + // SAFETY: DRM guarantees that `connector` points to a valid initialized `drm_connector`. + let state = unsafe { (*connector).state }; + if state.is_null() { + return null_mut(); + } + + // SAFETY: + // - We just verified that `state` is non-null + // * This cast is guaranteed to be safe via our type invariants. + let state = unsafe { ConnectorState::::from_raw(state) }; + + let new = Box::try_init( + try_init!(ConnectorState:: { + state: bindings::drm_connector_state { ..Default::default() }, + inner: state.inner.clone() + }), + GFP_KERNEL + ); + + 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_raw_mut()) + }; + + Box::into_raw(new).cast() + } else { + null_mut() + } +} + +unsafe extern "C" fn atomic_destroy_state_callback( + _connector: *mut bindings::drm_connector, + connector_state: *mut bindings::drm_connector_state +) { + // SAFETY: DRM guarantees that `state` points to a valid instance of `drm_connector_state` + unsafe { bindings::__drm_atomic_helper_connector_destroy_state(connector_state) }; + + // SAFETY: + // * DRM guarantees we are the only one with access to this `drm_connector_state` + // * This cast is safe via our type invariants. + drop(unsafe { Box::from_raw(connector_state.cast::>()) }); +} + +unsafe extern "C" fn connector_reset_callback( + connector: *mut bindings::drm_connector, +) { + // SAFETY: DRM guarantees that `state` points to a valid instance of `drm_connector_state` + let state = unsafe { (*connector).state }; + if !state.is_null() { + // SAFETY: + // * We're guaranteed `connector` is `Connector` via type invariants + // * We're guaranteed `state` is `ConnectorState` via type invariants. + unsafe { atomic_destroy_state_callback::(connector, state) } + + // SAFETY: No special requirements here, DRM expects this to be NULL + unsafe { (*connector).state = null_mut(); } + } + + // Unfortunately, this is the best we can do at the moment as this FFI callback was mistakenly + // presumed to be infallible :( + let new = Box::new(ConnectorState::::default(), GFP_KERNEL).expect("Blame the API, sorry!"); + + // DRM takes ownership of the state from here, resets it, and then assigns it to the connector + // SAFETY: + // - DRM guarantees that `connector` points to a valid instance of `drm_connector`. + // - The cast to `drm_connector_state` is safe via `ConnectorState`s type invariants. + unsafe { + bindings::__drm_atomic_helper_connector_reset(connector, Box::into_raw(new).cast()) + }; +} From patchwork Mon Sep 30 23:09:49 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817259 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 6AE38CEB2CD for ; Mon, 30 Sep 2024 23:38:14 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id DD44310E305; Mon, 30 Sep 2024 23:38:13 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="I7evTY6Q"; 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 4169410E46F for ; Mon, 30 Sep 2024 23:38:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739491; 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=010Ls1W8y6QITg3CHwheGXq/I8nr5NOipHNCDl02V5w=; b=I7evTY6QybCXpwGkYt0WOZWJyYO+h/lx1Qjj6SzLkBs8bd6qWC4YTV5sDEOKYjfYtQDspi 6tlVfWJXCgf5jvIC/P6+w/GQAB7RHJy+CsJrS/A3lIoYS/R8k044obtJFrV6WiiXxepPkm l33uLtcYYbRwL6jrag/LUmSROwW6CEE= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-345-ychX1yp_NsSGqMKAUBcSag-1; Mon, 30 Sep 2024 19:38:07 -0400 X-MC-Unique: ychX1yp_NsSGqMKAUBcSag-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 890E5196A119; Mon, 30 Sep 2024 23:38:05 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id B05E53003DEC; Mon, 30 Sep 2024 23:38:01 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 06/35] rust: drm/kms: Add drm_plane bindings Date: Mon, 30 Sep 2024 19:09:49 -0400 Message-ID: <20240930233257.1189730-7-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" The next step is adding a set of basic bindings to create a plane, which has to happen before we can create a CRTC (since we need to be able to at least specify a primary plane for a CRTC upon creation). This mostly follows the same general pattern as connectors (AsRawPlane, AsRawPlaneState, etc.). There is one major difference with planes vs. other types of atomic mode objects: drm_plane_state isn't the only base plane struct used in DRM drivers, as some drivers will use helpers like drm_shadow_plane_state which have a drm_plane_state embedded within them. Since we'll eventually be adding bindings for shadow planes, we introduce a PlaneStateHelper trait - which represents any data type which can be used as the main wrapping structure around a drm_plane_state - and we implement this trait for PlaneState. This trait can be used in our C callbacks to allow for drivers to use different wrapping structures without needing to implement a separate set of FFI callbacks for each type. Currently planes are the only type I'm aware of which do this. Signed-off-by: Lyude Paul --- V2: * Start using Gerry Guo's updated #[vtable] function so that our driver operations table has a static location in memory Signed-off-by: Lyude Paul --- rust/kernel/drm/kms.rs | 1 + rust/kernel/drm/kms/plane.rs | 504 +++++++++++++++++++++++++++++++++++ 2 files changed, 505 insertions(+) create mode 100644 rust/kernel/drm/kms/plane.rs diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs index 0138e6830b48c..5b075794a1155 100644 --- a/rust/kernel/drm/kms.rs +++ b/rust/kernel/drm/kms.rs @@ -4,6 +4,7 @@ pub mod connector; pub mod fbdev; +pub mod plane; use crate::{ drm::{ diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs new file mode 100644 index 0000000000000..3040c4546b121 --- /dev/null +++ b/rust/kernel/drm/kms/plane.rs @@ -0,0 +1,504 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +//! Bindings for [`struct drm_plane`] and friends. +//! +//! [`struct drm_plane`]: srctree/include/drm/drm_plane.h + +use alloc::boxed::Box; +use crate::{ + bindings, + drm::{device::Device, drv::Driver, fourcc::*}, + error::{to_result, from_result, Error}, + init::Zeroable, + prelude::*, + types::{ARef, Opaque}, + private::Sealed, +}; +use core::{ + cell::Cell, + pin::Pin, + slice, + mem::{self, size_of, ManuallyDrop}, + ptr::{self, null, null_mut, NonNull}, + marker::*, + ops::*, +}; +use macros::pin_data; +use super::{ + KmsDriver, + UnregisteredKmsDevice, + ModeObject, + StaticModeObject, +}; + +/// The main trait for implementing the [`struct drm_plane`] API for [`Plane`] +/// +/// Any KMS driver should have at least one implementation of this type, which allows them to create +/// [`Plane`] objects. Additionally, a driver may store driver-private data within the type that +/// implements [`DriverPlane`] - and it will be made available when using a fully typed [`Plane`] +/// object. +/// +/// # Invariants +/// +/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in +/// [`struct drm_plane`] pointers are contained within a [`Plane`]. +/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in +/// [`struct drm_plane_state`] pointers are contained within a [`PlaneState`]. +/// +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h +#[vtable] +pub trait DriverPlane: Send + Sync + Sized { + /// The generated C vtable for this [`DriverPlane`] implementation. + #[unique] + const OPS: &'static DriverPlaneOps = &DriverPlaneOps { + 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 + }, + + helper_funcs: bindings::drm_plane_helper_funcs { + prepare_fb: None, + cleanup_fb: None, + begin_fb_access: None, // TODO: someday? + end_fb_access: None, // TODO: someday? + atomic_check: None, + atomic_update: None, + atomic_enable: None, // TODO + atomic_disable: None, // TODO + atomic_async_check: None, // TODO + atomic_async_update: None, // TODO + panic_flush: None, + get_scanout_buffer: None + } + }; + + /// The type to pass to the `args` field of [`Plane::new`]. + /// + /// This type will be made available in in the `args` argument of [`Self::new`]. Drivers which + /// don't need this can simply pass [`()`] here. + type Args; + + /// The parent [`Driver`] implementation. + type Driver: KmsDriver; + + /// The [`DriverPlaneState`] implementation for this [`DriverPlane`]. + /// + /// See [`DriverPlaneState`] for more info. + type State: DriverPlaneState; + + /// The constructor for creating a [`Plane`] using this [`DriverPlane`] implementation. + /// + /// Drivers may use this to instantiate their [`DriverPlane`] object. + fn new(device: &Device, args: Self::Args) -> impl PinInit; +} + +/// The generated C vtable for a [`DriverPlane`]. +/// +/// This type is created internally by DRM. +pub struct DriverPlaneOps { + funcs: bindings::drm_plane_funcs, + helper_funcs: bindings::drm_plane_helper_funcs, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +/// An enumerator describing a type of [`Plane`]. +/// +/// This is mainly just relevant for DRM legacy drivers. +pub enum PlaneType { + /// Overlay planes represent all non-primary, non-cursor planes. Some drivers refer to these + /// types of planes as "sprites" internally. + OVERLAY = bindings::drm_plane_type_DRM_PLANE_TYPE_OVERLAY, + + /// A primary plane attached to a CRTC that is the most likely to be able to light up the CRTC + /// when no scaling/cropping is used, and the plane covers the whole CRTC. + PRIMARY = bindings::drm_plane_type_DRM_PLANE_TYPE_PRIMARY, + + /// A cursor plane attached to a CRTC that is more likely to be enabled when no scaling/cropping + /// is used, and the framebuffer has the size indicated by [`ModeConfigInfo::max_cursor`]. + /// + /// [`ModeConfigInfo::max_cursor`]: crate::drm::kms::ModeConfigInfo + CURSOR = bindings::drm_plane_type_DRM_PLANE_TYPE_CURSOR, +} + +/// The main interface for a [`struct drm_plane`]. +/// +/// This type is the main interface for dealing with DRM planes. In addition, it also allows +/// immutable access to whatever private data is contained within an implementor's [`DriverPlane`] +/// type. +/// +/// # Invariants +/// +/// - `plane` and `inner` are initialized for as long as this object is made available to users. +/// - The data layout of this structure begins with [`struct drm_plane`]. +/// - The atomic state for this type can always be assumed to be of type [`PlaneState`]. +/// +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h +#[repr(C)] +#[pin_data] +pub struct Plane { + /// The FFI drm_plane object + plane: Opaque, + /// The driver's private inner data + #[pin] + inner: T, + #[pin] + _p: PhantomPinned, +} + +unsafe impl Zeroable for bindings::drm_plane {} + +impl Sealed for Plane {} + +impl Deref for Plane { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl Plane { + /// Construct a new [`Plane`]. + /// + /// A driver may use this from their [`Kms::create_objects`] callback in order to construct new + /// [`Plane`] objects. + /// + /// [`Kms::create_objects`]: kernel::drm::kms::Kms::create_objects + pub fn new<'a, 'b: 'a, const FMT_COUNT: usize, const MOD_COUNT: usize>( + dev: &'a UnregisteredKmsDevice<'a, T::Driver>, + possible_crtcs: u32, + formats: &'static FormatList, + format_modifiers: Option<&'static ModifierList>, + type_: PlaneType, + name: Option<&CStr>, + args: T::Args, + ) -> Result<&'b Self> { + let this: Pin> = Box::try_pin_init( + try_pin_init!(Self { + plane: Opaque::new(bindings::drm_plane { + helper_private: &T::OPS.helper_funcs, + ..Default::default() + }), + inner <- T::new(dev, args), + _p: PhantomPinned + }), + GFP_KERNEL + )?; + + // SAFETY: FFI call with no special requirements + to_result(unsafe { + bindings::drm_universal_plane_init( + dev.as_raw(), + this.as_raw(), + possible_crtcs, + &T::OPS.funcs, + formats.as_ptr(), + formats.raw_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)) }) + } +} + +/// A trait implemented by any type that acts as a [`struct drm_plane`] interface. +/// +/// This is implemented internally by DRM. +/// +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h +pub trait AsRawPlane: StaticModeObject { + /// The type that should be used to represent an atomic state for this plane interface. + type State: FromRawPlaneState; + + /// Return the raw `bindings::drm_plane` for this DRM plane. + /// + /// Drivers should never use this directly. + fn as_raw(&self) -> *mut bindings::drm_plane; + + /// Convert a raw `bindings::drm_plane` pointer into an object of this type. + /// + /// SAFETY: Callers promise that `ptr` points to a valid instance of this type + unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane) -> &'a Self; +} + +impl AsRawPlane for Plane { + type State = PlaneState; + + fn as_raw(&self) -> *mut bindings::drm_plane { + self.plane.get() + } + + unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane) -> &'a Self { + // SAFETY: Our data layout starts with `bindings::drm_plane` + unsafe { &*ptr.cast() } + } +} + +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 { Device::borrow((*self.as_raw()).dev) } + } + + fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object { + // SAFETY: We don't expose DRM planes to users before `base` is initialized + unsafe { &mut ((*self.as_raw()).base) } + } +} + +// SAFETY: Planes do not have a refcount +unsafe impl StaticModeObject for Plane {} + +// SAFETY: Our interface is thread-safe. +unsafe impl Send for Plane {} + +// SAFETY: Our interface is thread-safe. +unsafe impl Sync for Plane {} + +/// A trait implemented by any type which can produce a reference to a [`struct drm_plane_state`]. +/// +/// This is implemented internally by DRM. +/// +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h +pub trait AsRawPlaneState: private::AsRawPlaneState { + /// The type that this plane state interface returns to represent the parent DRM plane + type Plane: AsRawPlane; +} + +pub(crate) mod private { + /// Trait for retrieving references to the base plane state contained within any plane state + /// compatible type + #[doc(hidden)] + pub trait AsRawPlaneState { + /// Return an immutable reference to the raw plane state + fn as_raw(&self) -> &bindings::drm_plane_state; + + /// Get a mutable reference to the raw `bindings::drm_plane_state` contained within this + /// type. + /// + /// # Safety + /// + /// The caller promises this mutable reference will not be used to modify any contents of + /// `bindings::drm_plane_state` which DRM would consider to be static - like the backpointer + /// to the DRM plane that owns this state. This also means the mutable reference should + /// never be exposed outside of this crate. + unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_plane_state; + } +} + +pub(crate) use private::AsRawPlaneState as AsRawPlaneStatePrivate; + +/// A trait implemented for any type which can be constructed directly from a +/// [`struct drm_plane_state`] pointer. +/// +/// This is implemented internally by DRM. +/// +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h +pub trait FromRawPlaneState: AsRawPlaneState { + /// Get an immutable 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 from_raw<'a>(ptr: *const bindings::drm_plane_state) -> &'a Self; + + /// 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`, and that no + /// other references (mutable or immutable) to `ptr` exist. + unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_plane_state) -> &'a mut Self; +} + +/// The main interface for a [`struct drm_plane_state`]. +/// +/// This type is the main interface for dealing with the atomic state of DRM planes. In addition, it +/// allows access to whatever private data is contained within an implementor's [`DriverPlaneState`] +/// type. +/// +/// # Invariants +/// +/// - The DRM C API and our interface guarantees that only the user has mutable access to `state`, +/// up until [`drm_atomic_helper_commit_hw_done`] is called. Therefore, `plane` follows rust's +/// data aliasing rules and does not need to be behind an [`Opaque`] type. +/// - `state` and `inner` initialized for as long as this object is exposed to users. +/// - The data layout of this structure begins with [`struct drm_plane_state`]. +/// - The plane for this atomic state can always be assumed to be of type [`Plane`]. +/// +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h +/// [`drm_atomic_helper_commit_hw_done`]: srctree/include/drm/drm_atomic_helper.h +#[derive(Default)] +#[repr(C)] +pub struct PlaneState { + state: bindings::drm_plane_state, + inner: T, +} + +/// The main trait for implementing the [`struct drm_plane_state`] API for a [`Plane`]. +/// +/// A driver may store driver-private data within the implementor's type, which will be available +/// when using a full typed [`PlaneState`] object. +/// +/// # Invariants +/// +/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in +/// [`struct drm_plane`] pointers are contained within a [`Plane`]. +/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in +/// [`struct drm_plane_state`] pointers are contained within a [`PlaneState`]. +/// +/// [`struct drm_plane`]: srctree/include/drm_plane.h +/// [`struct drm_plane_state`]: srctree/include/drm_plane.h +pub trait DriverPlaneState: Clone + Default + Sized { + /// The type for this driver's drm_plane implementation + type Plane: DriverPlane; +} + +impl Sealed for PlaneState {} + +impl AsRawPlaneState for PlaneState { + type Plane = Plane; +} + +impl private::AsRawPlaneState for PlaneState { + fn as_raw(&self) -> &bindings::drm_plane_state { + &self.state + } + + unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_plane_state { + &mut self.state + } +} + +impl FromRawPlaneState for PlaneState { + unsafe fn from_raw<'a>(ptr: *const bindings::drm_plane_state) -> &'a Self { + // SAFETY: Our data layout starts with `bindings::drm_plane_state` + unsafe { &*ptr.cast() } + } + + unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_plane_state) -> &'a mut Self { + // SAFETY: Our data layout starts with `bindings::drm_plane_state` + unsafe { &mut *ptr.cast() } + } +} + +unsafe impl Zeroable for bindings::drm_plane_state {} + +impl Deref for PlaneState { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for PlaneState { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +unsafe extern "C" fn plane_destroy_callback( + plane: *mut bindings::drm_plane +) { + // SAFETY: DRM guarantees that `plane` points to a valid initialized `drm_plane`. + unsafe { bindings::drm_plane_cleanup(plane) }; + + // SAFETY: + // - DRM guarantees we are now the only one with access to this [`drm_plane`]. + // - This cast is safe via `DriverPlane`s type invariants. + drop(unsafe { Box::from_raw(plane as *mut Plane) }); +} + +unsafe extern "C" fn atomic_duplicate_state_callback( + plane: *mut bindings::drm_plane +) -> *mut bindings::drm_plane_state { + // SAFETY: DRM guarantees that `plane` points to a valid initialized `drm_plane`. + let state = unsafe { (*plane).state }; + if state.is_null() { + return null_mut(); + } + + // SAFETY: This cast is safe via `DriverPlaneState`s type invariants. + let state = unsafe { PlaneState::::from_raw(state) }; + + let new = Box::try_init( + try_init!(PlaneState:: { + state: bindings::drm_plane_state { ..Default::default() }, + inner: state.inner.clone() + }), + GFP_KERNEL + ); + + 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_raw_mut()) + }; + + Box::into_raw(new).cast() + } else { + null_mut() + } +} + +unsafe extern "C" fn atomic_destroy_state_callback( + _plane: *mut bindings::drm_plane, + state: *mut bindings::drm_plane_state +) { + // SAFETY: DRM guarantees that `state` points to a valid instance of `drm_plane_state` + unsafe { bindings::__drm_atomic_helper_plane_destroy_state(state) }; + + // SAFETY: + // * DRM guarantees we are the only one with access to this `drm_plane_state` + // * This cast is safe via our type invariants. + drop(unsafe { Box::from_raw(state.cast::>()) }); +} + +unsafe extern "C" fn plane_reset_callback( + plane: *mut bindings::drm_plane, +) { + // SAFETY: DRM guarantees that `state` points to a valid instance of `drm_plane_state` + let state = unsafe { (*plane).state }; + if !state.is_null() { + // SAFETY: + // * We're guaranteed `plane` is `Plane` via type invariants + // * We're guaranteed `state` is `PlaneState` via type invariants. + unsafe { atomic_destroy_state_callback::(plane, state) } + + // SAFETY: No special requirements here, DRM expects this to be NULL + unsafe { (*plane).state = null_mut(); } + } + + // Unfortunately, this is the best we can do at the moment as this FFI callback was mistakenly + // presumed to be infallible :( + let new = Box::new(PlaneState::::default(), GFP_KERNEL) + .expect("Blame the API, sorry!"); + + // DRM takes ownership of the state from here, resets it, and then assigns it to the plane + // SAFETY: + // - DRM guarantees that `plane` points to a valid instance of `drm_plane`. + // - The cast to `drm_plane_state` is safe via `PlaneState`s type invariants. + unsafe { bindings::__drm_atomic_helper_plane_reset(plane, Box::into_raw(new).cast()) }; +} From patchwork Mon Sep 30 23:09:50 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817260 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 74857CEB2CB for ; Mon, 30 Sep 2024 23:38:23 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id EAE3E10E46F; Mon, 30 Sep 2024 23:38:22 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="ZZlVBMOt"; 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 355EB10E46F for ; Mon, 30 Sep 2024 23:38:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739501; 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=Zsg6b5UNPhHsmvqymPCPnflKtWQ+9pnWvRyUUcC31Ac=; b=ZZlVBMOtZtzZczYOYeEjo7rxKMOyydq2LzihfabliEcwVEiXgd071MzdZOVQir4JZC2l/B AtLzXSA63KS+42yvM/k0cCZGoD+7LQdWExwsNNYRgtjlYMWYjGD46ku6dwYzgJK0WaLZ9R 8/ZRww6crjpmG2cnfFQxDTLQs+8CjyY= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-617-PlY6vkyENYeWN7BvNNgSPA-1; Mon, 30 Sep 2024 19:38:17 -0400 X-MC-Unique: PlY6vkyENYeWN7BvNNgSPA-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id D8230193EB0B; Mon, 30 Sep 2024 23:38:14 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 0B7593003DEC; Mon, 30 Sep 2024 23:38:10 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 07/35] WIP: rust: drm/kms: Add drm_crtc bindings Date: Mon, 30 Sep 2024 19:09:50 -0400 Message-ID: <20240930233257.1189730-8-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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 basic bindings for DRM CRTCs which follow the same general pattern as connectors and planes (e.g. AsRawCrtc, AsRawCrtcState, etc.). There is one big difference though - drm_crtc_state appears to be the one atomic state that actually has data which can be mutated from outside of the atomic commit phase - which means we can't keep rust referencs to it, and instead need to use the Opaque type and implement things through pointers instead. This should be the last mode object we're introducing for the time being with its own atomic state. Note that we've not added bindings for private modesetting objects yet, but I don't think those will be needed for rvkms - and the same general patterns we're using here should work for adding private modesetting objects. Signed-off-by: Lyude Paul --- TODO: * Add commit data in the future Signed-off-by: Lyude Paul --- rust/kernel/drm/kms.rs | 1 + rust/kernel/drm/kms/crtc.rs | 501 ++++++++++++++++++++++++++++++++++++ 2 files changed, 502 insertions(+) create mode 100644 rust/kernel/drm/kms/crtc.rs diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs index 5b075794a1155..4b54611fdba8b 100644 --- a/rust/kernel/drm/kms.rs +++ b/rust/kernel/drm/kms.rs @@ -3,6 +3,7 @@ //! KMS driver abstractions for rust. pub mod connector; +pub mod crtc; pub mod fbdev; pub mod plane; diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs new file mode 100644 index 0000000000000..d84db49948380 --- /dev/null +++ b/rust/kernel/drm/kms/crtc.rs @@ -0,0 +1,501 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +//! KMS driver abstractions for rust. + +use super::{ + plane::*, + ModeObject, + StaticModeObject, + KmsDriver, + UnregisteredKmsDevice +}; +use crate::{ + bindings, + drm::device::Device, + device, + prelude::*, + private::Sealed, + error::from_result, + types::Opaque, + init::Zeroable, + sync::Arc, + error::to_result, +}; +use core::{ + cell::{Cell, UnsafeCell}, + marker::*, + ptr::{NonNull, null, null_mut, addr_of_mut, self}, + ops::{Deref, DerefMut}, + mem, +}; +use macros::vtable; + +/// The main trait for implementing the [`struct drm_crtc`] API for [`Crtc`]. +/// +/// Any KMS driver should have at least one implementation of this type, which allows them to create +/// [`Crtc`] objects. Additionally, a driver may store driver-private data within the type that +/// implements [`DriverCrtc`] - and it will be made available when using a fully typed [`Crtc`] +/// object. +/// +/// # Invariants +/// +/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in +/// [`struct drm_crtc`] pointers are contained within a [`Crtc`]. +/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in +/// [`struct drm_crtc_state`] pointers are contained within a [`CrtcState`]. +/// +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h +#[vtable] +pub trait DriverCrtc: Send + Sync + Sized { + /// The generated C vtable for this [`DriverCrtc`] implementation. + #[unique] + const OPS: &'static DriverCrtcOps = &DriverCrtcOps { + 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, + }, + + 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, + }, + }; + + /// The type to pass to the `args` field of [`Crtc::new`]. + /// + /// This type will be made available in in the `args` argument of [`Self::new`]. Drivers which + /// don't need this can simply pass [`()`] here. + type Args; + + /// The parent [`KmsDriver`] implementation. + type Driver: KmsDriver; + + /// The [`DriverCrtcState`] implementation for this [`DriverCrtc`]. + /// + /// See [`DriverCrtcState`] for more info. + type State: DriverCrtcState; + + /// The constructor for creating a [`Crtc`] using this [`DriverCrtc`] implementation. + /// + /// Drivers may use this to instantiate their [`DriverCrtc`] object. + fn new(device: &Device, args: &Self::Args) -> impl PinInit; +} + +/// The generated C vtable for a [`DriverCrtc`]. +/// +/// This type is created internally by DRM. +pub struct DriverCrtcOps { + funcs: bindings::drm_crtc_funcs, + helper_funcs: bindings::drm_crtc_helper_funcs, +} + +/// The main interface for a [`struct drm_crtc`]. +/// +/// This type is the main interface for dealing with DRM CRTCs. In addition, it also allows +/// immutable access to whatever private data is contained within an implementor's [`DriverCrtc`] +/// type. +/// +/// # Invariants +/// +/// - `crtc` and `inner` are initialized for as long as this object is made available to users. +/// - The data layout of this structure begins with [`struct drm_crtc`]. +/// - The atomic state for this type can always be assumed to be of type [`CrtcState`]. +/// +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h +#[repr(C)] +#[pin_data] +pub struct Crtc { + // The FFI drm_crtc object + crtc: Opaque, + /// The driver's private inner data + #[pin] + inner: T, + #[pin] + _p: PhantomPinned, +} + +// SAFETY: DRM expects this struct to be zero-initialized +unsafe impl Zeroable for bindings::drm_crtc { } + +impl Sealed for Crtc {} + +// SAFETY: Our CRTC interfaces are guaranteed to be thread-safe +unsafe impl Send for Crtc { } + +// SAFETY: Our CRTC interfaces are guaranteed to be thread-safe +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 { Device::borrow((*self.as_raw()).dev) } + } + + fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object { + // SAFETY: We don't expose Crtc to users before it's initialized, so `base` is always + // initialized + unsafe { addr_of_mut!((*self.as_raw()).base) } + } +} + +// SAFETY: CRTCs are non-refcounted modesetting objects +unsafe impl StaticModeObject for Crtc { } + +impl Crtc { + /// Construct a new [`Crtc`]. + /// + /// A driver may use this from their [`Kms::create_objects`] callback in order to construct new + /// [`Crtc`] objects. + /// + /// [`Kms::create_objects`]: kernel::drm::kms::Kms::create_objects + pub fn new<'a, 'b: 'a, P, C>( + dev: &'a UnregisteredKmsDevice<'a, T::Driver>, + primary: &'a Plane

, + cursor: Option<&'a Plane>, + name: Option<&CStr>, + args: T::Args, + ) -> Result<&'b Self> + where + P: DriverPlane, + C: DriverPlane + { + let this = Box::try_pin_init( + try_pin_init!(Self { + crtc: Opaque::new(bindings::drm_crtc { + helper_private: &T::OPS.helper_funcs, + ..Default::default() + }), + inner <- T::new(dev, &args), + _p: PhantomPinned, + }), + GFP_KERNEL + )?; + + to_result(unsafe { + bindings::drm_crtc_init_with_planes( + dev.as_raw(), + this.as_raw(), + primary.as_raw(), + cursor.map_or(null_mut(), |c| c.as_raw()), + &T::OPS.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)) }) + } +} + +/// A trait implemented by any type that acts as a [`struct drm_crtc`] interface. +/// +/// This is implemented internally by DRM. +/// +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h +pub trait AsRawCrtc: StaticModeObject { + /// The type that should be returned for a CRTC state acquired using this CRTC interface + type State: FromRawCrtcState; + + /// Return a raw pointer to the `bindings::drm_crtc` for this object + fn as_raw(&self) -> *mut bindings::drm_crtc; + + /// Convert a raw `bindings::drm_crtc` pointer into an object of this type. + /// + /// SAFETY: Callers promise that `ptr` points to a valid instance of this type + unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) -> &'a Self; +} + +impl AsRawCrtc for Crtc { + type State = CrtcState; + + fn as_raw(&self) -> *mut bindings::drm_crtc { + self.crtc.get() + } + + unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) -> &'a Self { + // SAFETY: Our data layout starts with `bindings::drm_crtc` + unsafe { &*ptr.cast() } + } +} + +unsafe impl Zeroable for bindings::drm_crtc_state { } + +impl Sealed for CrtcState {} + +/// The main trait for implementing the [`struct drm_crtc_state`] API for a [`Crtc`]. +/// +/// A driver may store driver-private data within the implementor's type, which will be available +/// when using a full typed [`CrtcState`] object. +/// +/// # Invariants +/// +/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in +/// [`struct drm_crtc`] pointers are contained within a [`Crtc`]. +/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in +/// [`struct drm_crtc_state`] pointers are contained within a [`CrtcState`]. +/// +/// [`struct drm_crtc`]: srctree/include/drm_crtc.h +/// [`struct drm_crtc_state`]: srctree/include/drm_crtc.h +pub trait DriverCrtcState: Clone + Default + Unpin { + /// The parent CRTC driver for this CRTC state + type Crtc: DriverCrtc where Self: Sized; +} + +/// The main interface for a [`struct drm_crtc_state`]. +/// +/// This type is the main interface for dealing with the atomic state of DRM crtcs. In addition, it +/// allows access to whatever private data is contained within an implementor's [`DriverCrtcState`] +/// type. +/// +/// # Invariants +/// +/// - `state` and `inner` initialized for as long as this object is exposed to users. +/// - The data layout of this structure begins with [`struct drm_crtc_state`]. +/// - The CRTC for this type can always be assumed to be of type [`Crtc`]. +/// +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h +#[repr(C)] +pub struct CrtcState { + state: Opaque, + inner: UnsafeCell, +} + +impl Deref for CrtcState { + type Target = T; + + fn deref(&self) -> &Self::Target { + // SAFETY: Our interface ensures that `inner` will not be modified unless only a single + // mutable reference exists to `inner`, so this is safe + unsafe { &*self.inner.get() } + } +} + +impl DerefMut for CrtcState { + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: Our interfaces ensures that we either have one mutable reference to the state + // (this one), or multiple immutable references + unsafe { self.inner.get_mut() } + } +} + +/// A trait implemented by any type which can produce a reference to a [`struct drm_crtc_state`]. +/// +/// This is implemented internally by DRM. +/// +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h +pub trait AsRawCrtcState: private::AsRawCrtcState { + /// The type that this CRTC state interface returns to represent the parent CRTC + type Crtc: AsRawCrtc; +} + +pub(crate) mod private { + use super::*; + + #[doc(hidden)] + pub trait AsRawCrtcState { + /// Return a raw pointer to the DRM CRTC state + /// + /// Note that CRTC states are the only atomic state in KMS which don't nicely follow rust's + /// data aliasing rules already. + fn as_raw(&self) -> *mut bindings::drm_crtc_state; + } +} + +pub(super) use private::AsRawCrtcState as AsRawCrtcStatePrivate; + +/// A trait for providing common methods which can be used on any type that can be used as an atomic +/// CRTC state. +pub trait RawCrtcState: AsRawCrtcState { + /// Return the CRTC that owns this state. + fn crtc(&self) -> &Self::Crtc { + // SAFETY: + // * This type conversion is guaranteed by type invariance + // * Our interface ensures that this access follows rust's data-aliasing rules + // * `crtc` is guaranteed to never be NULL and is invariant throughout the lifetime of the + // state + unsafe { ::from_raw((*self.as_raw()).crtc) } + } +} +impl RawCrtcState for T {} + +/// A trait implemented for any type which can be constructed directly from a +/// [`struct drm_crtc_state`] pointer. +/// +/// This is implemented internally by DRM. +/// +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h +pub trait FromRawCrtcState: AsRawCrtcState { + /// Obtain a reference back to this type from a raw DRM crtc state pointer + /// + /// # Safety + /// + /// Callers must ensure that ptr contains a valid instance of this type. + unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc_state) -> &'a Self; +} + +impl private::AsRawCrtcState for CrtcState { + #[inline] + fn as_raw(&self) -> *mut bindings::drm_crtc_state { + self.state.get() + } +} + +impl AsRawCrtcState for CrtcState { + type Crtc = Crtc; +} + +impl FromRawCrtcState for CrtcState { + unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc_state) -> &'a Self { + // SAFETY: Our data layout starts with `bindings::drm_crtc_state` + unsafe { &*(ptr.cast()) } + } +} + +unsafe extern "C" fn crtc_destroy_callback( + crtc: *mut bindings::drm_crtc +) { + // SAFETY: DRM guarantees that `crtc` points to a valid initialized `drm_crtc`. + unsafe { bindings::drm_crtc_cleanup(crtc) }; + + // SAFETY: + // - DRM guarantees we are now the only one with access to this [`drm_crtc`]. + // - This cast is safe via `DriverCrtc`s type invariants. + // - We created this as a pinned type originally + drop(unsafe { Pin::new_unchecked(Box::from_raw(crtc as *mut Crtc)) }); +} + +unsafe extern "C" fn atomic_duplicate_state_callback( + crtc: *mut bindings::drm_crtc +) -> *mut bindings::drm_crtc_state { + // SAFETY: DRM guarantees that `crtc` points to a valid initialized `drm_crtc`. + let state = unsafe { (*crtc).state }; + if state.is_null() { + return null_mut(); + } + + // SAFETY: This cast is safe via `DriverCrtcState`s type invariants. + let crtc = unsafe { Crtc::::from_raw(crtc) }; + + // SAFETY: This cast is safe via `DriverCrtcState`s type invariants. + let state = unsafe { CrtcState::::from_raw(state) }; + + let mut new = Box::try_init( + try_init!(CrtcState:: { + state: Opaque::new(Default::default()), + inner: UnsafeCell::new((*state).clone()), + }), + GFP_KERNEL + ); + + if let Ok(mut new) = new { + let new = Box::into_raw(new).cast(); + + // SAFETY: DRM simply copies the data from the previous base DRM state here and does not + // move the contents of `ptr` + unsafe { bindings::__drm_atomic_helper_crtc_duplicate_state(crtc.as_raw(), new) } + + new + } else { + null_mut() + } +} + +unsafe extern "C" fn atomic_destroy_state_callback( + _crtc: *mut bindings::drm_crtc, + crtc_state: *mut bindings::drm_crtc_state, +) { + // SAFETY: DRM guarantees that `state` points to a valid instance of `drm_crtc_state` + unsafe { bindings::__drm_atomic_helper_crtc_destroy_state(crtc_state) }; + + // SAFETY: + // * DRM guarantees we are the only one with access to this `drm_crtc_state` + // * This cast is safe via our type invariants. + // * All data in `CrtcState` is either Unpin, or pinned + drop(unsafe { Box::from_raw(crtc_state as *mut CrtcState) }); +} + +unsafe extern "C" fn crtc_reset_callback( + crtc: *mut bindings::drm_crtc, +) { + // SAFETY: DRM guarantees that `state` points to a valid instance of `drm_crtc_state` + let state = unsafe { (*crtc).state }; + if !state.is_null() { + // SAFETY: + // * We're guaranteed `crtc` is `Crtc` via type invariants + // * We're guaranteed `state` is `CrtcState` via type invariants. + unsafe { atomic_destroy_state_callback::(crtc, state) } + + // SAFETY: No special requirements here, DRM expects this to be NULL + unsafe { (*crtc).state = null_mut(); } + } + + // SAFETY: `crtc` is guaranteed to be of type `Crtc` by type invariance + let crtc = unsafe { Crtc::::from_raw(crtc) }; + + // 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_init( + try_init!(CrtcState:: { + state: Opaque::new(Default::default()), + inner: UnsafeCell::new(Default::default()), + }), + GFP_KERNEL + ).expect("Unfortunately, this API was presumed infallible"); + + // SAFETY: DRM takes ownership of the state from here, and will never move it + unsafe { + bindings::__drm_atomic_helper_crtc_reset( + crtc.as_raw(), + Box::into_raw(new).cast() + ) + }; +} From patchwork Mon Sep 30 23:09:51 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817261 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 E355ECEB2CD for ; Mon, 30 Sep 2024 23:38:32 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 69F2C10E47A; Mon, 30 Sep 2024 23:38:32 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="OVKfYNFl"; 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 927C810E47A for ; Mon, 30 Sep 2024 23:38:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739509; 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=D4rfjRqkU+rOu5xBWMssWwYCDhyFvRa9kZUvIuwzUoI=; b=OVKfYNFlc6KP+Q6l49gVRPrfmNb2OIVlzLN//FfdMpM7CHtrbMZlHiVw+kg9Dk/f3GeBFS WbVjHdvmOc5k0P/zdsj0RjslbH/fQkVxmAaVQh4QyaF+YsmPBAZNNYw9t9dB+beQePpMYE OFctzTlI7/6vKXZYs+xabkdiudYc1o4= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-303-iVxDa2flN7a3QiNfhk8r-g-1; Mon, 30 Sep 2024 19:38:26 -0400 X-MC-Unique: iVxDa2flN7a3QiNfhk8r-g-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 40D96193EB26; Mon, 30 Sep 2024 23:38:24 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 25D923003DEC; Mon, 30 Sep 2024 23:38:19 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 08/35] rust: drm/kms: Add bindings for drm_encoder Date: Mon, 30 Sep 2024 19:09:51 -0400 Message-ID: <20240930233257.1189730-9-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" The last thing we need to be able to register a KMS driver is the ability to create DRM encoders, so let's add bindings for that. Again, these bindings follow the same general pattern as CRTCs, planes, and connector with one difference: encoders don't have an atomic state. Note that not having an atomic state doesn't mean there aren't plenty of valid usecases for a driver to stick private data within a DRM encoder, hence why we reuse the aforementioned pattern. Signed-off-by: Lyude Paul --- rust/kernel/drm/kms.rs | 1 + rust/kernel/drm/kms/encoder.rs | 248 +++++++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+) create mode 100644 rust/kernel/drm/kms/encoder.rs diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs index 4b54611fdba8b..d5cad598f016f 100644 --- a/rust/kernel/drm/kms.rs +++ b/rust/kernel/drm/kms.rs @@ -4,6 +4,7 @@ pub mod connector; pub mod crtc; +pub mod encoder; pub mod fbdev; pub mod plane; diff --git a/rust/kernel/drm/kms/encoder.rs b/rust/kernel/drm/kms/encoder.rs new file mode 100644 index 0000000000000..3ae597093645e --- /dev/null +++ b/rust/kernel/drm/kms/encoder.rs @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +//! drm_encoder abstractions for rust + +use crate::{ + drm::{ + device::Device, + drv::Driver, + }, + prelude::*, + sync::Arc, + types::Opaque, + init::Zeroable, + error::to_result, + private::Sealed, +}; +use core::{ + marker::*, + ptr::{null, addr_of_mut}, + ops::Deref, +}; +use super::{ModeObject, StaticModeObject, KmsDriver, UnregisteredKmsDevice}; +use bindings; + +// TODO: We should alias all of these types with shorter names +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, +}; + +/// The main trait for implementing the [`struct drm_encoder`] API for [`Encoder`]. +/// +/// Any KMS driver should have at least one implementation of this type, which allows them to create +/// [`Encoder`] objects. Additionally, a driver may store driver-private data within the type that +/// implements [`DriverEncoder`] - and it will be made available when using a fully typed +/// [`Encoder`] object. +/// +/// # Invariants +/// +/// - Any C FFI callbacks generated using this trait are guaranteed that passed-in +/// [`struct drm_encoder`] pointers are contained within a [`Encoder`]. +/// +/// [`struct drm_encoder`]: srctree/include/drm/drm_encoder.h +#[vtable] +pub trait DriverEncoder: Send + Sync + Sized { + /// The generated C vtable for this [`DriverEncoder`] implementation. + #[unique] + const OPS: &'static DriverEncoderOps = &DriverEncoderOps { + funcs: bindings::drm_encoder_funcs { + reset: None, + destroy: Some(encoder_destroy_callback::), + late_register: None, + early_unregister: None, + debugfs_init: None, + }, + 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, + }, + }; + + /// The parent driver for this drm_encoder implementation + type Driver: KmsDriver; + + /// The type to pass to the `args` field of [`Encoder::new`]. + /// + /// This type will be made available in in the `args` argument of [`Self::new`]. Drivers which + /// don't need this can simply pass [`()`] here. + type Args; + + /// The constructor for creating a [`Encoder`] using this [`DriverEncoder`] implementation. + /// + /// Drivers may use this to instantiate their [`DriverEncoder`] object. + fn new(device: &Device, args: Self::Args) -> impl PinInit; +} + +/// The generated C vtable for a [`DriverEncoder`]. +/// +/// This type is created internally by DRM. +pub struct DriverEncoderOps { + funcs: bindings::drm_encoder_funcs, + helper_funcs: bindings::drm_encoder_helper_funcs, +} + +/// A trait implemented by any type that acts as a [`struct drm_encoder`] interface. +/// +/// This is implemented internally by DRM. +/// +/// [`struct drm_encoder`]: srctree/include/drm/drm_encoder.h +pub trait AsRawEncoder: StaticModeObject { + /// Return the raw `bindings::drm_encoder` for this DRM encoder. + /// + /// Drivers should never use this directly + fn as_raw(&self) -> *mut bindings::drm_encoder; + + /// Convert a raw `bindings::drm_encoder` pointer into an object of this type. + /// + /// SAFETY: Callers promise that `ptr` points to a valid instance of this type + unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encoder) -> &'a Self; +} + +/// The main interface for a [`struct drm_encoder`]. +/// +/// This type is the main interface for dealing with DRM encoders. In addition, it also allows +/// immutable access to whatever private data is contained within an implementor's +/// [`DriverEncoder`] type. +/// +/// # Invariants +/// +/// - `encoder` and `inner` are initialized for as long as this object is made available to users. +/// - The data layout of this structure begins with [`struct drm_encoder`]. +/// +/// [`struct drm_encoder`]: srctree/include/drm/drm_encoder.h +#[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, +} + +impl Sealed for Encoder {} + +// SAFETY: DRM expects this to be zero-initialized +unsafe impl Zeroable for bindings::drm_encoder {} + +// SAFETY: Our interface is thread-safe. +unsafe impl Send for Encoder { } +// SAFETY: Our interface is thread-safe. +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 { Device::borrow((*self.encoder.get()).dev) } + } + + fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object { + // SAFETY: We don't expose Encoder to users before it's initialized, so `base` is always + // initialized + unsafe { &mut (*self.encoder.get()).base } + } +} + +// 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 AsRawEncoder for Encoder { + fn as_raw(&self) -> *mut bindings::drm_encoder { + self.encoder.get() + } + + unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encoder) -> &'a Self { + // SAFETY: Our data layout is starts with to `bindings::drm_encoder` + unsafe { &*ptr.cast() } + } +} + +impl Encoder { + /// Construct a new [`Encoder`]. + /// + /// A driver may use this from their [`Kms::create_objects`] callback in order to construct new + /// [`Encoder`] objects. + /// + /// [`Kms::create_objects`]: kernel::drm::kms::Kms::create_objects + pub fn new<'a, 'b: 'a>( + dev: &'a UnregisteredKmsDevice<'a, T::Driver>, + type_: u32, + possible_crtcs: u32, + possible_clones: u32, + name: Option<&CStr>, + args: T::Args, + ) -> Result<&'b Self> { + let this: Pin> = Box::try_pin_init( + try_pin_init!(Self { + encoder: Opaque::new(bindings::drm_encoder { + helper_private: &T::OPS.helper_funcs, + possible_crtcs, + possible_clones, + ..Default::default() + }), + inner <- T::new(dev, args), + _p: PhantomPinned + }), + GFP_KERNEL + )?; + + // SAFETY: FFI call with no special requirements + to_result(unsafe { + bindings::drm_encoder_init( + dev.as_raw(), + this.as_raw(), + &T::OPS.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)) }) + } +} + +unsafe extern "C" fn encoder_destroy_callback( + encoder: *mut bindings::drm_encoder +) { + // SAFETY: DRM guarantees that `encoder` points to a valid initialized `drm_encoder`. + unsafe { bindings::drm_encoder_cleanup(encoder) }; + + // SAFETY: + // - DRM guarantees we are now the only one with access to this [`drm_encoder`]. + // - This cast is safe via `DriverEncoder`s type invariants. + unsafe { drop(Box::from_raw(encoder as *mut Encoder)) }; +} From patchwork Mon Sep 30 23:09:52 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817262 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 E637ECEB2CD for ; Mon, 30 Sep 2024 23:38:41 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 6971C10E487; Mon, 30 Sep 2024 23:38:41 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="WbaAr0ZM"; 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 009F210E487 for ; Mon, 30 Sep 2024 23:38:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739519; 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=PJ3TeIAd9cs6+pu/8iyKEZ6SOjd6owM+icv2Rkamtr8=; b=WbaAr0ZMJONEqyZEyYkN51RUty/B0BG5+JPBiROXp3DQ/LOFM+oY6LloQND+vVVc7aYrln U5znVjqsda+IkvyI1kkqymTFcqDP8iowQAKpJZLdnji112OMVZSEKkLo7aEA6VFGHlNkjO t4QUy7o4OzRfqc4ExSbB5Asq7er6pYQ= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-540-w3nDE2ciOuyU1w2ZEh1kUw-1; Mon, 30 Sep 2024 19:38:34 -0400 X-MC-Unique: w3nDE2ciOuyU1w2ZEh1kUw-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 9D37519560B7; Mon, 30 Sep 2024 23:38:31 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 36C723003FD1; Mon, 30 Sep 2024 23:38:27 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 09/35] WIP: rust: drm/kms: Add Connector.attach_encoder() Date: Mon, 30 Sep 2024 19:09:52 -0400 Message-ID: <20240930233257.1189730-10-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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 adds a simple binding for completing the last step of creating a DRM connector - attaching its encoder. This function should only be called before the connector is registered, and DRM should enforce this itself by returning an error if a driver tries to add an encoder to an already-registered DRM connector. Note that unlike most of the methods we'll be adding to DRM mode objects, this is directly implemented on the Connector type since I don't really think it would make sense for us to allow this operation on an OpaqueConnector (a DRM connector without a known DriverConnector implementation, something we'll be adding in the next few commits). Signed-off-by: Lyude Paul --- TODO: * Figure out a solution for making sure that this can only be called when a Connector is unregistered, probably via an UnregisteredConnector type. Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/connector.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs index 0fa927a3743b2..54457b327c365 100644 --- a/rust/kernel/drm/kms/connector.rs +++ b/rust/kernel/drm/kms/connector.rs @@ -28,6 +28,7 @@ use super::{ ModeObject, RcModeObject, + encoder::*, KmsDriver, }; use macros::pin_data; @@ -227,6 +228,20 @@ pub fn new( ARef::from_raw(NonNull::new_unchecked(Box::into_raw(Pin::into_inner_unchecked(new)))) }) } + + /// Attach an encoder to this [`Connector`]. + /// + /// TODO: Move this to an `UnregisteredConnector` interface somehow… + #[must_use] + pub fn attach_encoder(&self, encoder: &Encoder) -> Result + where + E: DriverEncoder + { + // SAFETY: FFI call with no special requirements + to_result(unsafe { + bindings::drm_connector_attach_encoder(self.as_raw(), encoder.as_raw()) + }) + } } /// A trait implemented by any type that acts as a [`struct drm_connector`] interface. From patchwork Mon Sep 30 23:09:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817263 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 9D79FCEB2CD for ; Mon, 30 Sep 2024 23:38:48 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 25D8F10E498; Mon, 30 Sep 2024 23:38:48 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="CnAN1Cck"; 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 622B310E498 for ; Mon, 30 Sep 2024 23:38:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739526; 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=JlrhV03R2ar0qDWbNL3ocjtNNVJ3Ps0MyvSIOpYYekY=; b=CnAN1Cck4bloD3jmRGEF9xu4XhHB3rOyy8fqUogdkTu0Z1IZwnk0bm+Zx62xffHpLihWGE OK314QfaOUssM5Q8Tfvc9Qd7Ktun606fAGQx5WObdy0sNkPYdxImAXP651Ox46Lw1UPv4o 9R/XB3a8wCpTk1lyMOT4S6AL2R2MYjM= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-628-B90LovqUMrGEpOIDcW1CJw-1; Mon, 30 Sep 2024 19:38:43 -0400 X-MC-Unique: B90LovqUMrGEpOIDcW1CJw-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 48B401955F09; Mon, 30 Sep 2024 23:38:40 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 9E0E33003E4D; Mon, 30 Sep 2024 23:38:36 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 10/35] rust: drm/kms: Add DriverConnector::get_mode callback Date: Mon, 30 Sep 2024 19:09:53 -0400 Message-ID: <20240930233257.1189730-11-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" Next up is filling out some of the basic connector hotplugging callbacks - which we'll need for setting up the fbdev helpers for KMS devices. Note that connector hotplugging in DRM follows a BFL scheme: pretty much all probing is protected under the mighty drm_device->mode_config.lock, which of course is a bit counter-intuitive to rust's locking schemes where data is always associated with its lock. Since that lock is embedded in an FFI type and not a rust type, we need to introduce our own wrapper type that acts as a lock acquisition for this. This brings us to introducing a few new types: * ModeConfigGuard - the most basic lock guard, as long as this object is alive we are guaranteed to be holding drm_device->mode_config.lock. This object doesn't do much else on its own currently. * ConnectorGuard - an object which corresponds to a specific typed DRM connector. This can only be acquired with a ModeConfigGuard, and will be used to allow calling methods that are only safe to call with drm_device->mode_config.lock held. Since it implements Deref> as well, it can also be used for any other operations that would normally be available on a DRM connector. And finally, we add the DriverConnector::get_modes() trait method which drivers can use to implement the drm_connector_helper_funcs.get_modes callback. Note that while we make this trait method mandatory, we only do so for the time being since VKMS doesn't do very much with DRM connectors - and as such we have no need yet to implement alternative connector probing schemes outside of get_modes(). Signed-off-by: Lyude Paul --- rust/kernel/drm/kms.rs | 70 ++++++++++++++++++++++++++++++++ rust/kernel/drm/kms/connector.rs | 57 ++++++++++++++++++++++++-- 2 files changed, 124 insertions(+), 3 deletions(-) diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs index d5cad598f016f..d74267c78864f 100644 --- a/rust/kernel/drm/kms.rs +++ b/rust/kernel/drm/kms.rs @@ -18,6 +18,8 @@ types::*, error::to_result, private::Sealed, + sync::{Mutex, MutexGuard}, + container_of }; use core::{ ops::Deref, @@ -233,6 +235,21 @@ impl KmsDriver for T T: Driver, K: Kms {} +impl Device { + /// Retrieve a pointer to the mode_config mutex + #[inline] + pub(crate) fn mode_config_mutex(&self) -> &Mutex<()> { + // SAFETY: This lock is initialized for as long as `Device` is exposed to users + unsafe { Mutex::from_raw(&mut (*self.as_raw()).mode_config.mutex) } + } + + /// Acquire the [`mode_config.mutex`] for this [`Device`]. + #[inline] + pub fn mode_config_lock(&self) -> ModeConfigGuard<'_, T> { + ModeConfigGuard(self.mode_config_mutex().lock(), PhantomData) + } +} + /// A modesetting object in DRM. /// /// This is any type of object where the underlying C object contains a [`struct drm_mode_object`]. @@ -339,3 +356,56 @@ unsafe fn dec_ref(obj: ptr::NonNull) { unsafe { bindings::drm_mode_object_put(obj.as_ref().raw_mode_obj()) } } } + +/// A mode config guard. +/// +/// This is an exclusive primitive that represents when [`drm_device.mode_config.mutex`] is held - as +/// some modesetting operations (particularly ones related to [`connectors`](connector)) are still +/// protected under this single lock. The lock will be dropped once this object is dropped. +/// +/// # Invariants +/// +/// - `self.0` is contained within a [`struct drm_mode_config`], which is contained within a +/// [`struct drm_device`]. +/// - The [`KmsDriver`] implementation of that [`struct drm_device`] is always `T`. +/// - This type proves that [`drm_device.mode_config.mutex`] is acquired. +/// +/// [`struct drm_mode_config`]: (srctree/include/drm/drm_device.h) +/// [`drm_device.mode_config.mutex`]: (srctree/include/drm/drm_device.h) +/// [`struct drm_device`]: (srctree/include/drm/drm_device.h) +pub struct ModeConfigGuard<'a, T: KmsDriver>(MutexGuard<'a, ()>, PhantomData); + +impl<'a, T: KmsDriver> ModeConfigGuard<'a, T> { + /// Construct a new [`ModeConfigGuard`]. + /// + /// # Safety + /// + /// The caller must ensure that [`drm_device.mode_config.mutex`] is acquired. + /// + /// [`drm_device.mode_config.mutex`]: (srctree/include/drm/drm_device.h) + pub(crate) unsafe fn new(drm: &'a Device) -> Self { + // SAFETY: Our safety contract fulfills the requirements of `MutexGuard::new()` + Self(unsafe { MutexGuard::new(drm.mode_config_mutex(), ()) }, PhantomData) + } + + /// Return the [`Device`] that this [`ModeConfigGuard`] belongs to. + pub fn drm_dev(&self) -> &'a Device { + // SAFETY: + // - `self` is embedded within a `drm_mode_config` via our type invariants + // - `self.0.lock` has an equivalent data type to `mutex` via its type invariants. + let mode_config = unsafe { container_of!(self.0.lock, bindings::drm_mode_config, mutex) }; + + // SAFETY: And that `drm_mode_config` lives in a `drm_device` via type invariants. + unsafe { Device::borrow(container_of!(mode_config, bindings::drm_device, mode_config)) } + } + + /// 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. + #[inline] + pub(crate) fn assert_owner(&self, dev: &Device) { + assert!(ptr::eq(self.drm_dev(), dev)); + } +} diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs index 54457b327c365..57ab29473c344 100644 --- a/rust/kernel/drm/kms/connector.rs +++ b/rust/kernel/drm/kms/connector.rs @@ -19,7 +19,7 @@ use core::{ marker::*, ptr::null_mut, - mem, + mem::{self, ManuallyDrop}, ptr::{self, NonNull, addr_of_mut}, ffi::*, ops::*, @@ -28,6 +28,7 @@ use super::{ ModeObject, RcModeObject, + ModeConfigGuard, encoder::*, KmsDriver, }; @@ -93,7 +94,7 @@ pub trait DriverConnector: Send + Sync + Sized { destroy: Some(connector_destroy_callback::), force: None, detect: None, - fill_modes: 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::), @@ -101,7 +102,7 @@ pub trait DriverConnector: Send + Sync + Sized { helper_funcs: bindings::drm_connector_helper_funcs { mode_valid: None, atomic_check: None, - get_modes: None, + get_modes: Some(get_modes_callback::), detect_ctx: None, enable_hpd: None, disable_hpd: None, @@ -132,6 +133,12 @@ pub trait DriverConnector: Send + Sync + Sized { /// /// Drivers may use this to instantiate their [`DriverConnector`] object. fn new(device: &Device, args: Self::Args) -> impl PinInit; + + /// Retrieve a list of available display modes for this [`Connector`]. + fn get_modes<'a>( + connector: ConnectorGuard<'a, Self>, + guard: &ModeConfigGuard<'a, Self::Driver> + ) -> i32; } /// The generated C vtable for a [`DriverConnector`]. @@ -229,6 +236,19 @@ pub fn new( }) } + /// 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 [`ModeConfigGuard`] for this connector's parent [`Device`]. + pub fn guard<'a>(&'a self, guard: &ModeConfigGuard<'a, T::Driver>) -> ConnectorGuard<'a, T> { + guard.assert_owner(self.drm_dev()); + ConnectorGuard(self) + } + /// Attach an encoder to this [`Connector`]. /// /// TODO: Move this to an `UnregisteredConnector` interface somehow… @@ -327,6 +347,37 @@ unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a Self { drop(unsafe { Box::from_raw(connector as *mut Connector) }); } +unsafe extern "C" fn get_modes_callback( + connector: *mut bindings::drm_connector, +) -> c_int { + // SAFETY: This is safe via `DriverConnector`s type invariants. + let connector = unsafe { Connector::::from_raw(connector) }; + + // SAFETY: This FFI callback is only called while `mode_config.lock` is held + let guard = ManuallyDrop::new(unsafe { ModeConfigGuard::new(connector.drm_dev()) }); + + T::get_modes(connector.guard(&guard), &guard) +} + +/// A privileged [`Connector`] obtained while holding a [`ModeConfigGuard`]. +/// +/// This provides access to various methods for [`Connector`] that must happen under lock, such as +/// setting resolution preferences and adding display modes. +/// +/// # Invariants +/// +/// Shares the invariants of [`ModeConfigGuard`]. +#[derive(Copy, Clone)] +pub struct ConnectorGuard<'a, T: DriverConnector>(&'a Connector); + +impl Deref for ConnectorGuard<'_, T> { + type Target = Connector; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + // SAFETY: DRM expects this struct to be zero-initialized unsafe impl Zeroable for bindings::drm_connector_state {} From patchwork Mon Sep 30 23:09:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817264 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 33EF8CEB2CB for ; Mon, 30 Sep 2024 23:38:57 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id A8BE910E570; Mon, 30 Sep 2024 23:38:56 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="Oky8VVVR"; 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 026B610E570 for ; Mon, 30 Sep 2024 23:38:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739534; 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=jWkQB1pyBirQ1KP5U86Ct+4qpRkAEhXPzsNN9WOdD3Q=; b=Oky8VVVRoysLy8e30uNxv4WBF2qhXVAgIK5coBxNZeCS4gKI/tlr6MHtNW2VkWNpno26EX oXDif5I+YWxRebKLvm+XjKhycgJUuCLErX051ZTJx/dmZt9YpoXKR2xzFNeAy/lQqKcSYr TFJrWTQXqjTQqZYl5bvv7I1GgwgRIlM= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-564-tXS1bctpM0iGvAUk1iodGg-1; Mon, 30 Sep 2024 19:38:50 -0400 X-MC-Unique: tXS1bctpM0iGvAUk1iodGg-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 5983B1955D47; Mon, 30 Sep 2024 23:38:48 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 993753003DEC; Mon, 30 Sep 2024 23:38:43 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , Danilo Krummrich , Mika Westerberg , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 11/35] rust: drm/kms: Add ConnectorGuard::add_modes_noedid() Date: Mon, 30 Sep 2024 19:09:54 -0400 Message-ID: <20240930233257.1189730-12-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" A simple binding for drm_add_modes_noedid() using the ConnectorGuard type we just added. Signed-off-by: Lyude Paul --- rust/bindings/bindings_helper.h | 2 ++ rust/kernel/drm/kms/connector.rs | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index ba1871b05b7fa..7695f11f4363c 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs index 57ab29473c344..7d0f67c3bdaf8 100644 --- a/rust/kernel/drm/kms/connector.rs +++ b/rust/kernel/drm/kms/connector.rs @@ -378,6 +378,17 @@ fn deref(&self) -> &Self::Target { } } +impl<'a, T: DriverConnector> ConnectorGuard<'a, T> { + /// Add modes for a [`ConnectorGuard`] without an EDID. + /// + /// Add the specified modes to the connector's mode list up to the given maximum resultion. + /// Returns how many modes were added. + pub fn add_modes_noedid(&self, (max_h, max_v): (i32, i32)) -> i32 { + // SAFETY: We hold the locks required to call this via our type invariants. + unsafe { bindings::drm_add_modes_noedid(self.as_raw(), max_h, max_v) } + } +} + // SAFETY: DRM expects this struct to be zero-initialized unsafe impl Zeroable for bindings::drm_connector_state {} From patchwork Mon Sep 30 23:09:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817265 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 C1750CEB2CB for ; Mon, 30 Sep 2024 23:39:06 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 4793410E597; Mon, 30 Sep 2024 23:39:06 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="TKEcwO0R"; 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 A71D410E597 for ; Mon, 30 Sep 2024 23:39:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739543; 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=xibrkd8yzwA0Iqd6dDSkdoojf7lApu96rDOOBdAdKcQ=; b=TKEcwO0RQ9F2Wun6B+ITbJPt4b6AZ94SLj4MJFB+JLP8qNVObnJL7ftDvu7MGxI7d1Hi68 pJ2RCsK2qbsfqM+VyO79luA/oD7DdveO6kgIDpDLUteEZOZEdqS8hJIedXJTmCxS9guY2p XLHnMIrcQRyzJMakEUaYen8mzSoQ58g= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-83-bjN3m5KvN1iMrZyVKWqy7w-1; Mon, 30 Sep 2024 19:38:58 -0400 X-MC-Unique: bjN3m5KvN1iMrZyVKWqy7w-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 37682193EF57; Mon, 30 Sep 2024 23:38:56 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 140803003DEC; Mon, 30 Sep 2024 23:38:50 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 12/35] rust: drm/kms: Add ConnectorGuard::set_preferred_mode Date: Mon, 30 Sep 2024 19:09:55 -0400 Message-ID: <20240930233257.1189730-13-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" Add a wrapper for `drm_set_preferred_mode()` for our new `ConnectorGuard` type so we can set the preferred mode for RVKMS connectors. Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/connector.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs index 7d0f67c3bdaf8..ec842ebc111ae 100644 --- a/rust/kernel/drm/kms/connector.rs +++ b/rust/kernel/drm/kms/connector.rs @@ -387,6 +387,12 @@ pub fn add_modes_noedid(&self, (max_h, max_v): (i32, i32)) -> i32 { // SAFETY: We hold the locks required to call this via our type invariants. unsafe { bindings::drm_add_modes_noedid(self.as_raw(), max_h, max_v) } } + + /// Set the preferred display mode for the underlying [`Connector`]. + pub fn set_preferred_mode(&self, (h_pref, w_pref): (i32, i32)) { + // SAFETY: We hold the locks required to call this via our type invariants. + unsafe { bindings::drm_set_preferred_mode(self.as_raw(), h_pref, w_pref) } + } } // SAFETY: DRM expects this struct to be zero-initialized From patchwork Mon Sep 30 23:09:56 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817266 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 DBE33CEB2CD for ; Mon, 30 Sep 2024 23:39:11 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 60E8110E598; Mon, 30 Sep 2024 23:39:11 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="AtrJZ9xb"; 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 830EF10E598 for ; Mon, 30 Sep 2024 23:39:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739546; 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=j6rI5OuJT5Uhwr5YsNsmFqxJRozJhSEgscnGrLzTJHQ=; b=AtrJZ9xb7t4DElm5VkHqkW+K9yIcTDPWmPdcOoqQQbSM3rSZv6WqHlkc8E8DYjF1sCogQE lHriig8jURirmrhU5XBJMpOT1P8hZ2kiXQNXLIUJoeQaWMNyJPcqTYAenPOtqbLjztWnGJ DaNgZxmNWBlmWJILfYXpWfj5NRIgZkE= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-54-T22mXw6dOM2PCD_MgYanjg-1; Mon, 30 Sep 2024 19:39:05 -0400 X-MC-Unique: T22mXw6dOM2PCD_MgYanjg-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id E91D6196A122; Mon, 30 Sep 2024 23:39:02 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 25EDD3003DEC; Mon, 30 Sep 2024 23:38:58 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 13/35] WIP: rust: drm/kms: Add OpaqueConnector and OpaqueConnectorState Date: Mon, 30 Sep 2024 19:09:56 -0400 Message-ID: <20240930233257.1189730-14-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" Since we allow drivers to have multiple implementations of DriverConnector and DriverConnectorState (in C, the equivalent of this is having multiple structs which embed drm_connector) - there are some situations we will run into where it's not possible for us to know the corresponding DriverConnector or DriverConnectorState for a given connector. The most obvious one is iterating through all connectors on a KMS device. So, take advantage of the various connector traits we added to introduce OpaqueConnector<> and OpaqueConnectorState<> which both can be used as a DRM connector and connector state respectively without needing to know the corresponding traits. Signed-off-by: Lyude Paul --- TODO: * Add upcast functions for these types Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/connector.rs | 108 +++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs index ec842ebc111ae..98ac7fb781d4e 100644 --- a/rust/kernel/drm/kms/connector.rs +++ b/rust/kernel/drm/kms/connector.rs @@ -359,6 +359,64 @@ unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a Self { T::get_modes(connector.guard(&guard), &guard) } +/// A [`struct drm_connector`] without a known [`DriverConnector`] implementation. +/// +/// This is mainly for situations where our bindings can't infer the [`DriverConnector`] +/// implementation for a [`struct drm_connector`] automatically. It is identical to [`Connector`], +/// except that it does not provide access to the driver's private data. +/// +/// TODO: Add upcast methods for this +/// +/// # Invariants +/// +/// - `connector` is initialized for as long as this object is exposed to users. +/// - The data layout of this type is equivalent to [`struct drm_connector`]. +/// +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h +#[repr(transparent)] +pub struct OpaqueConnector { + connector: Opaque, + _p: PhantomData +} + +impl Sealed for OpaqueConnector {} + +impl AsRawConnector for OpaqueConnector { + type Driver = T; + type State = OpaqueConnectorState; + + fn as_raw(&self) -> *mut bindings::drm_connector { + self.connector.get() + } + + unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a Self { + // SAFETY: Our data layout is identical to `bindings::drm_connector` + unsafe { &*ptr.cast() } + } +} + +impl ModeObject for OpaqueConnector { + type Driver = T; + + fn drm_dev(&self) -> &Device { + // SAFETY: The parent device for a DRM connector will never outlive the connector, and this + // pointer is invariant through the lifetime of the connector + unsafe { Device::borrow((*self.as_raw()).dev) } + } + + fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object { + // SAFETY: We don't expose DRM connectors to users before `base` is initialized + unsafe { &mut (*self.as_raw()).base } + } +} + +// SAFETY: Connectors are reference counted mode objects +unsafe impl RcModeObject for OpaqueConnector {} + +// SAFETY: Our connector interfaces are guaranteed to be thread-safe +unsafe impl Send for OpaqueConnector {} +unsafe impl Sync for OpaqueConnector {} + /// A privileged [`Connector`] obtained while holding a [`ModeConfigGuard`]. /// /// This provides access to various methods for [`Connector`] that must happen under lock, such as @@ -537,6 +595,56 @@ unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_connector_state) -> &'a mut S } } +/// A [`struct drm_connector_state`] without a known [`DriverConnectorState`] implementation. +/// +/// This is mainly for situations where our bindings can't infer the [`DriverConnectorState`] +/// implementation for a [`struct drm_connector_state`] automatically. It is identical to +/// [`Connector`], except that it does not provide access to the driver's private data. +/// +/// TODO: Add upcast functions +/// +/// # Invariants +/// +/// - `state` is initialized for as long as this object is exposed to users. +/// - The data layout of this type is identical to [`struct drm_connector_state`]. +/// - The DRM C API and our interface guarantees that only the user has mutable access to `state`, +/// up until [`drm_atomic_helper_commit_hw_done`] is called. Therefore, `connector` follows rust's +/// data aliasing rules and does not need to be behind an [`Opaque`] type. +/// +/// [`struct drm_connector_state`]: srctree/include/drm/drm_connector.h +/// [`drm_atomic_helper_commit_hw_done`]: srctree/include/drm/drm_atomic_helper.h +#[repr(transparent)] +pub struct OpaqueConnectorState { + state: bindings::drm_connector_state, + _p: PhantomData +} + +impl AsRawConnectorState for OpaqueConnectorState { + type Connector = OpaqueConnector; +} + +impl private::AsRawConnectorState for OpaqueConnectorState { + fn as_raw(&self) -> &bindings::drm_connector_state { + &self.state + } + + unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_connector_state { + &mut self.state + } +} + +impl FromRawConnectorState for OpaqueConnectorState { + unsafe fn from_raw<'a>(ptr: *const bindings::drm_connector_state) -> &'a Self { + // SAFETY: Our data layout is identical to `bindings::drm_connector_state` + unsafe { &*ptr.cast() } + } + + unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_connector_state) -> &'a mut Self { + // SAFETY: Our data layout is identical to `bindings::drm_connector_state` + unsafe { &mut *ptr.cast() } + } +} + unsafe extern "C" fn atomic_duplicate_state_callback( connector: *mut bindings::drm_connector ) -> *mut bindings::drm_connector_state From patchwork Mon Sep 30 23:09:57 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817267 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 B6974CEB2CD for ; Mon, 30 Sep 2024 23:39:18 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 3B24D10E599; Mon, 30 Sep 2024 23:39:18 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="YF6zFD5z"; 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 4644710E599 for ; Mon, 30 Sep 2024 23:39:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739555; 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=rNcosQLumm6LvLMU8qHzc4TOTdZH/IRXYAw7EHjI1/s=; b=YF6zFD5zIXD/gpNzTcPX9Ep/3i8rIRGyuPI4pCw5wqmq/6VFyjiWPPYro+HeRwngiBjZf7 55NQ6APzqAyAK0QJ7S6dXnGmnVdWN6/dYWyc2PzjhfEl8/JfofcmQct+XLuJg1HjJuf9FK Z2APIoWrRCFOUybSoTxhisUYd58D994= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-653--VkizbrXMPaIfd66sLcc4w-1; Mon, 30 Sep 2024 19:39:12 -0400 X-MC-Unique: -VkizbrXMPaIfd66sLcc4w-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id A33B019560B7; Mon, 30 Sep 2024 23:39:09 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id DBBEE3003DEC; Mon, 30 Sep 2024 23:39:05 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 14/35] WIP: rust: drm/kms: Add OpaqueCrtc and OpaqueCrtcState Date: Mon, 30 Sep 2024 19:09:57 -0400 Message-ID: <20240930233257.1189730-15-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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 is the same thing as OpaqueConnector and OpaqueConnectorState, but for CRTCs now. Signed-off-by: Lyude Paul --- TODO: * Add upcast functions Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/crtc.rs | 131 ++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs index d84db49948380..1a3c9c448afcc 100644 --- a/rust/kernel/drm/kms/crtc.rs +++ b/rust/kernel/drm/kms/crtc.rs @@ -234,6 +234,41 @@ pub fn new<'a, 'b: 'a, P, C>( // SAFETY: We don't move anything Ok(unsafe { &*Box::into_raw(Pin::into_inner_unchecked(this)) }) } + + /// Attempt to convert an [`OpaqueCrtc`] into a fully qualified [`Crtc`]. + /// + /// This checks if the given [`OpaqueCrtc`] uses the same [`DriverCrtc`] implementation, and + /// returns the [`OpaqueCrtc`] as a [`Crtc`] object if so. + pub fn try_from_opaque<'a, D>(opaque: &'a OpaqueCrtc) -> Option<&'a Self> + where + D: KmsDriver, + T: DriverCrtc + { + // SAFETY: The vtables for a `Crtc` are initialized throughout the lifetime of the object + let funcs = unsafe { (*opaque.crtc.get()).funcs }; + + // SAFETY: We only perform this transmutation if the opaque CRTC shares our vtable pointers, + // so the underlying `Crtc` must share our data layout. + ptr::eq(funcs, &T::OPS.funcs).then(|| unsafe { mem::transmute(opaque) }) + } + + /// Convert a [`OpaqueCrtc`] into its fully qualified [`Crtc`]. + /// + /// This is an infallible version of [`Self::try_from_opaque`]. This function is mainly useful + /// for drivers where only a single [`DriverCrtc`] implementation exists. + /// + /// # Panics + /// + /// This function will panic if the underlying CRTC in the provided [`OpaqueCrtc`] does not + /// belong to the same [`DriverCrtc`] implementation. + pub fn from_opaque<'a, D>(opaque: &'a OpaqueCrtc) -> &'a Self + where + D: KmsDriver, + T: DriverCrtc + { + Self::try_from_opaque(opaque) + .expect("Passed OpaqueCrtc does not share this DriverCrtc implementation") + } } /// A trait implemented by any type that acts as a [`struct drm_crtc`] interface. @@ -267,6 +302,66 @@ unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) -> &'a Self { } } +/// A [`struct drm_crtc`] without a known [`DriverCrtc`] implementation. +/// +/// This is mainly for situations where our bindings can't infer the [`DriverCrtc`] implementation +/// for a [`struct drm_crtc`] automatically. It is identical to [`Crtc`], except that it does not +/// provide access to the driver's private data. +/// +/// It may be upcasted to a full [`Crtc`] using [`Crtc::from_opaque`] or +/// [`Crtc::try_from_opaque`]. +/// +/// # Invariants +/// +/// - `crtc` is initialized for as long as this object is made available to users. +/// - The data layout of this structure is equivalent to [`struct drm_crtc`]. +/// +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h +#[repr(transparent)] +pub struct OpaqueCrtc { + crtc: Opaque, + _p: PhantomData +} + +impl Sealed for OpaqueCrtc {} + +impl AsRawCrtc for OpaqueCrtc { + type State = OpaqueCrtcState; + + fn as_raw(&self) -> *mut bindings::drm_crtc { + self.crtc.get() + } + + unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) -> &'a Self { + // SAFETY: Our data layout starts with `bindings::drm_crtc` + unsafe { &*ptr.cast() } + } +} + +impl ModeObject for OpaqueCrtc { + type Driver = T; + + fn drm_dev(&self) -> &Device { + // SAFETY: The parent device for a DRM connector will never outlive the connector, and this + // pointer is invariant through the lifetime of the connector + unsafe { Device::borrow((*self.as_raw()).dev) } + } + + fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object { + // SAFETY: We don't expose DRM connectors to users before `base` is initialized + unsafe { addr_of_mut!((*self.as_raw()).base) } + } +} + +// SAFETY: CRTCs are non-refcounted modesetting objects +unsafe impl StaticModeObject for OpaqueCrtc {} + +// SAFETY: Our CRTC interface is guaranteed to be thread-safe +unsafe impl Send for OpaqueCrtc {} + +// SAFETY: Our CRTC interface is guaranteed to be thread-safe +unsafe impl Sync for OpaqueCrtc {} + unsafe impl Zeroable for bindings::drm_crtc_state { } impl Sealed for CrtcState {} @@ -400,6 +495,42 @@ unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc_state) -> &'a Self { } } +/// A [`struct drm_crtc_state`] without a known [`DriverCrtcState`] implementation. +/// +/// This is mainly for situations where our bindings can't infer the [`DriverCrtcState`] +/// implementation for a [`struct drm_crtc_state`] automatically. It is identical to [`Crtc`], +/// except that it does not provide access to the driver's private data. +/// +/// TODO: Add upcast functions +/// +/// # Invariants +/// +/// - `state` is initialized for as long as this object is exposed to users. +/// - The data layout of this type is identical to [`struct drm_crtc_state`]. +/// +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h +#[repr(transparent)] +pub struct OpaqueCrtcState { + state: Opaque, + _p: PhantomData +} + +impl AsRawCrtcState for OpaqueCrtcState { + type Crtc = OpaqueCrtc; +} + +impl private::AsRawCrtcState for OpaqueCrtcState { + fn as_raw(&self) -> *mut bindings::drm_crtc_state { + self.state.get() + } +} + +impl FromRawCrtcState for OpaqueCrtcState { + unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc_state) -> &'a Self { + // SAFETY: Our data layout is identical to `bindings::drm_crtc_state` + unsafe { &*(ptr.cast()) } + } +} unsafe extern "C" fn crtc_destroy_callback( crtc: *mut bindings::drm_crtc ) { From patchwork Mon Sep 30 23:09:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817268 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 B7117CEB2CD for ; Mon, 30 Sep 2024 23:39:25 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 3857710E59A; Mon, 30 Sep 2024 23:39:25 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="cPgS2KWI"; 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 704D810E59A for ; Mon, 30 Sep 2024 23:39:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739562; 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=ZR+Oy8+dZBZO6+3VRxhqxljsi2SG5GmfYhJRwKT9SBE=; b=cPgS2KWIpuYjMogBGaG2rLjIGYGv3Gt5DxluI1vU7B1e8CqVlkRKptgkz7b1zJo0LXXY+4 AeMWusaZyor5FQv+Lg7KzbUkh53U3t/PG90Y8bVPByAbank5wU1Gfnhpv7bMCrprGb7+Eu DS/ld4vr1Bp3czRL9/Hb8EaQ3G1mG6A= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-20-rkaPSSVnPBibG5lQR8OeMA-1; Mon, 30 Sep 2024 19:39:19 -0400 X-MC-Unique: rkaPSSVnPBibG5lQR8OeMA-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id BD146192DE39; Mon, 30 Sep 2024 23:39:16 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id A94FB3003DEC; Mon, 30 Sep 2024 23:39:12 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 15/35] WIP: rust: drm/kms: Add OpaquePlane and OpaquePlaneState Date: Mon, 30 Sep 2024 19:09:58 -0400 Message-ID: <20240930233257.1189730-16-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" Same thing as OpaqueCrtc and OpaqueCrtcState, but for plane states now. Signed-off-by: Lyude Paul --- TODO: * Finish adding upcast functions. Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/plane.rs | 143 +++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs index 3040c4546b121..3ace487316d46 100644 --- a/rust/kernel/drm/kms/plane.rs +++ b/rust/kernel/drm/kms/plane.rs @@ -217,6 +217,43 @@ pub fn new<'a, 'b: 'a, const FMT_COUNT: usize, const MOD_COUNT: usize>( // SAFETY: We don't move anything Ok(unsafe { &*Box::into_raw(Pin::into_inner_unchecked(this)) }) } + + /// Attempt to convert an [`OpaquePlane`] into a fully qualified [`Plane`]. + /// + /// This checks if the given [`OpaquePlane`] uses the same [`DriverPlane`] implementation, and + /// returns the [`OpaquePlane`] as a [`Plane`] object if so. + pub fn try_from_opaque<'a, D>(opaque: &'a OpaquePlane) -> Option<&'a Self> + where + D: KmsDriver, + T: DriverPlane + { + // SAFETY: The vtables for a `Plane` are initialized by the time that we expose `Plane` + // objects to users, and their values are invariant throughout the lifetime of the device. + let funcs = unsafe { (*opaque.plane.get()).funcs }; + + // SAFETY: We just guaranteed that the opaque plane shares our vtable pointers, which means + // it must belong to our `DriverPlane` implementation. As well, all `Plane` + // objects start with an identical data layout to `OpaquePlane` + ptr::eq(funcs, &T::OPS.funcs).then(|| unsafe { mem::transmute(opaque) }) + } + + /// Convert a [`OpaquePlane`] into its fully qualified [`Plane`]. + /// + /// This is an infallible version of [`Self::try_from_opaque`]. This function is mainly useful + /// for drivers where only a single [`DriverPlane`] implementation exists. + /// + /// # Panics + /// + /// This function will panic if the underlying [`Plane`] which contains the provided + /// [`OpaquePlane`] does not belong to the same [`DriverPlane`] implementation. + pub fn from_opaque<'a, D>(opaque: &'a OpaquePlane) -> &'a Self + where + D: KmsDriver, + T: DriverPlane + { + Self::try_from_opaque(opaque) + .expect("Passed OpaquePlane does not share this DriverPlane implementation") + } } /// A trait implemented by any type that acts as a [`struct drm_plane`] interface. @@ -275,6 +312,63 @@ unsafe impl Send for Plane {} // SAFETY: Our interface is thread-safe. unsafe impl Sync for Plane {} +/// A [`struct drm_plane`] without a known [`DriverPlane`] implementation. +/// +/// This is mainly for situations where our bindings can't infer the [`DriverPlane`] implementation +/// for a [`struct drm_plane`] automatically. It is identical to [`Plane`], except that it does not +/// provide access to the driver's private data. +/// +/// It may be upcasted to a full [`Plane`] using [`Plane::from_opaque`] or +/// [`Plane::try_from_opaque`]. +/// +/// # Invariants +/// +/// - `plane` is initialized for as long as this object is made available to users. +/// - The data layout of this structure is equivalent to [`struct drm_plane`]. +/// +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h +#[repr(transparent)] +pub struct OpaquePlane { + plane: Opaque, + _p: PhantomData, +} + +impl Sealed for OpaquePlane {} + +impl AsRawPlane for OpaquePlane { + type State = OpaquePlaneState; + + fn as_raw(&self) -> *mut bindings::drm_plane { + self.plane.get() + } + + unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane) -> &'a Self { + // SAFETY: Our data layout is identical to `bindings::drm_plane` + unsafe { &*ptr.cast() } + } +} + +impl ModeObject for OpaquePlane { + type Driver = T; + + fn drm_dev(&self) -> &Device { + // SAFETY: DRM planes exist for as long as the device does, so this pointer is always valid + unsafe { Device::borrow((*self.as_raw()).dev) } + } + + fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object { + // SAFETY: We don't expose DRM planes to users before `base` is initialized + unsafe { &mut ((*self.as_raw()).base) } + } +} + +// SAFETY: Planes do not have a refcount +unsafe impl StaticModeObject for OpaquePlane {} + +// SAFETY: Our plane interfaces are guaranteed to be thread-safe +unsafe impl Send for OpaquePlane {} +unsafe impl Sync for OpaquePlane {} + /// A trait implemented by any type which can produce a reference to a [`struct drm_plane_state`]. /// /// This is implemented internally by DRM. @@ -419,6 +513,55 @@ fn deref_mut(&mut self) -> &mut Self::Target { } } +/// A [`struct drm_plane_state`] without a known [`DriverPlaneState`] implementation. +/// +/// This is mainly for situations where our bindings can't infer the [`DriverPlaneState`] +/// implementation for a [`struct drm_plane_state`] automatically. It is identical to [`Plane`], +/// except that it does not provide access to the driver's private data. +/// +/// TODO: Add upcast functions +/// +/// # Invariants +/// +/// - The DRM C API and our interface guarantees that only the user has mutable access to `state`, +/// up until [`drm_atomic_helper_commit_hw_done`] is called. Therefore, `plane` follows rust's +/// data aliasing rules and does not need to be behind an [`Opaque`] type. +/// - `state` is initialized for as long as this object is exposed to users. +/// - The data layout of this structure is identical to [`struct drm_plane_state`]. +/// +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h +/// [`drm_atomic_helper_commit_hw_done`]: srctree/include/drm/drm_atomic_helper.h +#[repr(transparent)] +pub struct OpaquePlaneState { + state: bindings::drm_plane_state, + _p: PhantomData, +} + +impl AsRawPlaneState for OpaquePlaneState { + type Plane = OpaquePlane; +} + +impl private::AsRawPlaneState for OpaquePlaneState { + fn as_raw(&self) -> &bindings::drm_plane_state { + &self.state + } + + unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_plane_state { + &mut self.state + } +} + +impl FromRawPlaneState for OpaquePlaneState { + unsafe fn from_raw<'a>(ptr: *const bindings::drm_plane_state) -> &'a Self { + // SAFETY: Our data layout is identical to `ptr` + unsafe { &*ptr.cast() } + } + + unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_plane_state) -> &'a mut Self { + // SAFETY: Our data layout is identical to `ptr` + unsafe { &mut *ptr.cast() } + } +} unsafe extern "C" fn plane_destroy_callback( plane: *mut bindings::drm_plane ) { From patchwork Mon Sep 30 23:09:59 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817269 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 718F7CEB2CD for ; Mon, 30 Sep 2024 23:39:33 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id EB9D510E59B; Mon, 30 Sep 2024 23:39:32 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="ZFqv+c9L"; 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 4D80310E59B for ; Mon, 30 Sep 2024 23:39:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739571; 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=kQUtNu2ymV8+ePIE/+uRZptH2R5q5/TOrvKyouxuFHU=; b=ZFqv+c9LpGjOXPwt7OM9yuwt416Wr2tLucpdtaD5NVjsw+8wTBhWnQTcryPGF+R/uKO4Yw TC3Ijuh7P3jU8Og+R5AAw5mTeANR/UTWkSf3Mkl1/g2LFsy0kT9eIVWk7uO3VAqy05OpNa JhER8aWsvBw4wq07qJi+IzipXQvuwPo= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-250-iOcJtWYwNDCzdFpW_N7r0Q-1; Mon, 30 Sep 2024 19:39:26 -0400 X-MC-Unique: iOcJtWYwNDCzdFpW_N7r0Q-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id A0B15196A111; Mon, 30 Sep 2024 23:39:23 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 98B8B3003DEC; Mon, 30 Sep 2024 23:39:19 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 16/35] rust: drm/kms: Add RawConnector and RawConnectorState Date: Mon, 30 Sep 2024 19:09:59 -0400 Message-ID: <20240930233257.1189730-17-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" Now that we have more then one way to refer to connectors, we also want to ensure that any methods which are common to any kind of connector type can be used on all connector representations. This is where RawConnector and RawConnectorState come in: we implement these traits for any type which implements AsRawConnector or AsRawConnectorState respectively. Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/connector.rs | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs index 98ac7fb781d4e..f62740d7f6469 100644 --- a/rust/kernel/drm/kms/connector.rs +++ b/rust/kernel/drm/kms/connector.rs @@ -331,6 +331,27 @@ unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connector) -> &'a Self { } } +/// Common methods available on any type which implements [`AsRawConnector`]. +/// +/// This is implemented internally by DRM, and provides many of the basic methods for working with +/// connectors. +pub trait RawConnector: AsRawConnector { + /// Return the index of this DRM connector + #[inline] + fn index(&self) -> u32 { + // SAFETY: The index is initialized by the time we expose DRM connector objects to users, + // and is invariant throughout the lifetime of the connector + unsafe { (*self.as_raw()).index } + } + + /// Return the bitmask derived from this DRM connector's index + #[inline] + fn mask(&self) -> u32 { + 1 << self.index() + } +} +impl RawConnector for T {} + unsafe extern "C" fn connector_destroy_callback( connector: *mut bindings::drm_connector, ) { @@ -523,6 +544,19 @@ pub trait FromRawConnectorState: AsRawConnectorState { unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_connector_state) -> &'a mut Self; } +/// Common methods available on any type which implements [`AsRawConnectorState`]. +/// +/// This is implemented internally by DRM, and provides many of the basic methods for working with +/// the atomic state of [`Connector`]s. +pub trait RawConnectorState: AsRawConnectorState { + fn connector(&self) -> &Self::Connector { + // SAFETY: This is guaranteed safe by type invariance, and we're guaranteed by DRM that + // `self.state.connector` points to a valid instance of a `Connector` + unsafe { Self::Connector::from_raw((*self.as_raw()).connector) } + } +} +impl RawConnectorState for T {} + /// The main interface for a [`struct drm_connector_state`]. /// /// This type is the main interface for dealing with the atomic state of DRM connectors. In From patchwork Mon Sep 30 23:10:00 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817270 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 B7DC8CEB2CB for ; Mon, 30 Sep 2024 23:39:38 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 3A26410E59C; Mon, 30 Sep 2024 23:39:38 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="J+BXSl+p"; 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 B6F5210E59C for ; Mon, 30 Sep 2024 23:39:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739576; 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=l2ZqNS7fpaukLWUM4ISWNMCXuLZuLhXZJI56Y+VCkes=; b=J+BXSl+pf2iIGcztwG+3bvg41upB8CJlUvBtSwZdQOmApCGJKiIkF/k+N8y51DXZrSVmuk x+ePwGC75aPenSzw5II5LKXEKiE2KvcQuTV0UJX3sjlVkpFR0A+EKbdsPt/uqBiKZxBinu IGGa4IEWRdlY+vc6pYr+xit90l3zclc= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-323-Nr2P90v3PYGPy62zTVK3og-1; Mon, 30 Sep 2024 19:39:32 -0400 X-MC-Unique: Nr2P90v3PYGPy62zTVK3og-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 0E21B19626D4; Mon, 30 Sep 2024 23:39:30 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 7C0EF3003DEC; Mon, 30 Sep 2024 23:39:26 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 17/35] rust: drm/kms: Add RawCrtc and RawCrtcState Date: Mon, 30 Sep 2024 19:10:00 -0400 Message-ID: <20240930233257.1189730-18-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" Same thing as RawConnector and RawConnectorState, just for CRTCs now. Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/crtc.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs index 1a3c9c448afcc..246d15a15c14d 100644 --- a/rust/kernel/drm/kms/crtc.rs +++ b/rust/kernel/drm/kms/crtc.rs @@ -302,6 +302,25 @@ unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) -> &'a Self { } } +/// Common methods available on any type which implements [`AsRawCrtc`]. +/// +/// This is implemented internally by DRM, and provides many of the basic methods for working with +/// CRTCs. +pub trait RawCrtc: AsRawCrtc { + /// Return the index of this CRTC. + fn index(&self) -> u32 { + // SAFETY: The index is initialized by the time we expose Crtc objects to users, and is + // invariant throughout the lifetime of the Crtc + unsafe { (*self.as_raw()).index } + } + + /// Return the index of this DRM CRTC in the form of a bitmask. + fn mask(&self) -> u32 { + 1 << self.index() + } +} +impl RawCrtc for T {} + /// A [`struct drm_crtc`] without a known [`DriverCrtc`] implementation. /// /// This is mainly for situations where our bindings can't infer the [`DriverCrtc`] implementation @@ -447,8 +466,10 @@ pub trait AsRawCrtcState { pub(super) use private::AsRawCrtcState as AsRawCrtcStatePrivate; -/// A trait for providing common methods which can be used on any type that can be used as an atomic -/// CRTC state. +/// Common methods available on any type which implements [`AsRawCrtcState`]. +/// +/// This is implemented internally by DRM, and provides many of the basic methods for working with +/// the atomic state of [`Crtc`]s. pub trait RawCrtcState: AsRawCrtcState { /// Return the CRTC that owns this state. fn crtc(&self) -> &Self::Crtc { From patchwork Mon Sep 30 23:10:01 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817272 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 861DACEB2CD for ; Mon, 30 Sep 2024 23:39:57 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 1814410E59E; Mon, 30 Sep 2024 23:39:57 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="HdpFb6xr"; 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 7554E10E59E for ; Mon, 30 Sep 2024 23:39:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739594; 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=O9gbDuyNEQzpSC0vAhmZzo+JcWQOY6GTcF09aFJMUz8=; b=HdpFb6xrOKICs3tcyobSwgfSGVEdk7NOmqSuYT1yerJgg+EhI28plsiJPH16/gzlr/72cY rFy/8DJP+vGrwrbj4HVXAjL46HJ1nNKbq+XxmM/Ef+Y5QGunrMNy10FqmfTcCB3ZkRbfR0 +rTlpSnMN5E+NGEpGLhfks4np7/FGVA= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-199-8xusCUW9P3-5hWO4SgwbBw-1; Mon, 30 Sep 2024 19:39:38 -0400 X-MC-Unique: 8xusCUW9P3-5hWO4SgwbBw-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id E2E5C196A40C; Mon, 30 Sep 2024 23:39:35 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id E9A5F3003DEC; Mon, 30 Sep 2024 23:39:32 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 18/35] rust: drm/kms: Add RawPlane and RawPlaneState Date: Mon, 30 Sep 2024 19:10:01 -0400 Message-ID: <20240930233257.1189730-19-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" Same thing as RawCrtc and RawCrtcState, but for DRM planes now Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/plane.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs index 3ace487316d46..1c151ae3b3dcc 100644 --- a/rust/kernel/drm/kms/plane.rs +++ b/rust/kernel/drm/kms/plane.rs @@ -312,6 +312,27 @@ unsafe impl Send for Plane {} // SAFETY: Our interface is thread-safe. unsafe impl Sync for Plane {} +/// Common methods available on any type which implements [`AsRawPlane`]. +/// +/// This is implemented internally by DRM, and provides many of the basic methods for working with +/// planes. +pub trait RawPlane: AsRawPlane { + /// Return the index of this DRM plane + #[inline] + fn index(&self) -> u32 { + // SAFETY: The index is initialized by the time we expose `Plane` objects to users, and is + // invariant throughout the lifetime of the `Plane` + unsafe { (*self.as_raw()).index } + } + + /// Return the index of this DRM plane in the form of a bitmask + #[inline] + fn mask(&self) -> u32 { + 1 << self.index() + } +} +impl RawPlane for T {} + /// A [`struct drm_plane`] without a known [`DriverPlane`] implementation. /// /// This is mainly for situations where our bindings can't infer the [`DriverPlane`] implementation @@ -426,6 +447,20 @@ pub trait FromRawPlaneState: AsRawPlaneState { unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_plane_state) -> &'a mut Self; } +/// Common methods available on any type which implements [`AsRawPlane`]. +/// +/// This is implemented internally by DRM, and provides many of the basic methods for working with +/// the atomic state of [`Plane`]s. +pub trait RawPlaneState: AsRawPlaneState { + /// Return the plane that this plane state belongs to. + fn plane(&self) -> &Self::Plane { + // SAFETY: The index is initialized by the time we expose Plane objects to users, and is + // invariant throughout the lifetime of the Plane + unsafe { Self::Plane::from_raw(self.as_raw().plane) } + } +} +impl RawPlaneState for T {} + /// The main interface for a [`struct drm_plane_state`]. /// /// This type is the main interface for dealing with the atomic state of DRM planes. In addition, it From patchwork Mon Sep 30 23:10:02 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817271 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 8590DCEB2CD for ; Mon, 30 Sep 2024 23:39:49 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 06B1110E59D; Mon, 30 Sep 2024 23:39:49 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="huJZ+wm1"; 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 E306410E59D for ; Mon, 30 Sep 2024 23:39:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739587; 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=RqJVQxO6y8ZjBezncHzbNZEVz53Yon6RGUEUFxdh8z0=; b=huJZ+wm1SRV2n9YXtyBSao1VjZGO6xu+fldVq5/h/yV/uThIJ433iSKREJxOxt/qfuHqmz ghyQ8XjEqxnYTe27P+ZnwA2sbh7R0A/wHULWUyd3AEY6iZWW4hyPPhSBusFxzHE1dc8jVU vUev31RkVJnUii2Fj9rCA/rmzYfKtk0= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-553-kU9cTNeWP_i06bB4I9LCDg-1; Mon, 30 Sep 2024 19:39:43 -0400 X-MC-Unique: kU9cTNeWP_i06bB4I9LCDg-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 69712193EB0E; Mon, 30 Sep 2024 23:39:41 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 53C1E3003DEC; Mon, 30 Sep 2024 23:39:38 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 19/35] WIP: rust: drm/kms: Add OpaqueEncoder Date: Mon, 30 Sep 2024 19:10:02 -0400 Message-ID: <20240930233257.1189730-20-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" Same thing as OpaquePlane, but for encoders now. Signed-off-by: Lyude Paul --- TODO: * Add upcast functions for this Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/encoder.rs | 55 ++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/rust/kernel/drm/kms/encoder.rs b/rust/kernel/drm/kms/encoder.rs index 3ae597093645e..71fae45d2d651 100644 --- a/rust/kernel/drm/kms/encoder.rs +++ b/rust/kernel/drm/kms/encoder.rs @@ -235,6 +235,61 @@ pub fn new<'a, 'b: 'a>( } } +/// A [`struct drm_encoder`] without a known [`DriverEncoder`] implementation. +/// +/// This is mainly for situations where our bindings can't infer the [`DriverEncoder`] implementation +/// for a [`struct drm_encoder`] automatically. It is identical to [`Encoder`], except that it does not +/// provide access to the driver's private data. +/// +/// TODO: Add functions for upcasting. +/// +/// # Invariants +/// +/// Same as [`Encoder`]. +#[repr(transparent)] +pub struct OpaqueEncoder { + encoder: Opaque, + _p: PhantomData, +} + +impl Sealed for OpaqueEncoder {} + +// SAFETY: All of our encoder interfaces are thread-safe +unsafe impl Send for OpaqueEncoder {} + +// SAFETY: All of our encoder interfaces are thread-safe +unsafe impl Sync for OpaqueEncoder {} + +impl ModeObject for OpaqueEncoder { + type Driver = T; + + fn drm_dev(&self) -> &Device { + // SAFETY: DRM encoders exist for as long as the device does, so this pointer is always + // valid + unsafe { Device::borrow((*self.encoder.get()).dev) } + } + + fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object { + // SAFETY: We don't expose Encoder to users before it's initialized, so `base` is always + // initialized + unsafe { addr_of_mut!((*self.encoder.get()).base) } + } +} + +// SAFETY: Encoders do not have a refcount +unsafe impl StaticModeObject for OpaqueEncoder {} + +impl AsRawEncoder for OpaqueEncoder { + fn as_raw(&self) -> *mut bindings::drm_encoder { + self.encoder.get() + } + + unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encoder) -> &'a Self { + // SAFETY: Our data layout is identical to `bindings::drm_encoder` + unsafe { &*ptr.cast() } + } +} + unsafe extern "C" fn encoder_destroy_callback( encoder: *mut bindings::drm_encoder ) { From patchwork Mon Sep 30 23:10:03 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817273 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 18D23CEB2CB for ; Mon, 30 Sep 2024 23:40:08 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 7E99410E59F; Mon, 30 Sep 2024 23:40:07 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="JBkvlL4s"; 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 1663410E59F for ; Mon, 30 Sep 2024 23:40:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739605; 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=KGIYCGCzhFhNPsWqkLs0PuZAHIENDZzRREJuybADa7I=; b=JBkvlL4sbT1jH2k+dgsweFkoNnxO3FkEKrhtY7OZsgBL0ExBSceTxjsCyJLFMo/VMIz/ev dbbUICRDtfnAxf8o8YTngWkiIayICr+mQD3G3+eCeg2mB7BY3NGMElW73rK3kuDLUchhuv 7n9Ma1DHbx6RBTtHW8CePx29Z8ipl+s= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-187-p4OGm-VROpiTE1wUgeqX1g-1; Mon, 30 Sep 2024 19:40:02 -0400 X-MC-Unique: p4OGm-VROpiTE1wUgeqX1g-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id AAFEB197700A; Mon, 30 Sep 2024 23:39:59 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id D21AF3003DEC; Mon, 30 Sep 2024 23:39:55 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 20/35] WIP: rust: drm/kms: Add drm_atomic_state bindings Date: Mon, 30 Sep 2024 19:10:03 -0400 Message-ID: <20240930233257.1189730-21-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" Next up is introducing bindings that we can use to represent the global DRM atomic state, along with all of the various object states contained within. We do this by introducing a few new concepts: borrowed states, atomic state mutators, and atomic state composers. To understand these, we need to quickly touch upon the general life of an atomic commit. Assuming a driver does its own internal atomic commit, the procedure looks something like this: * Allocate a new atomic state * Duplicate the atomic state of each mode object we want to mutate, and add the duplicated state to the new atomic state * Check (possibly more then once) the atomic state, possibly modifying it along the way * Commit the atomic state to software (we'll call this commit time). At this point no new objects can be added to the state * Finish committing the atomic state to hardware asynchronously With this in mind, we introduce AtomicStateMutator and AtomicStateComposer (along with leaky variants intended for uses in FFI calls). An AtomicStateMutator allows mutating an atomic state but does not allow for adding new objects to the state. Subsequently, an AtomicStateComposer allows for both mutating an atomic state and adding new mode objects. We control when we expose each of these types in order to implement the limitations required by the aforementioned example. Note as well that AtomicStateComposer is intended to eventually be usable directly by drivers. In this scenario, a driver will be able to create an AtomicStateComposer (the equivalent of allocating an atomic state in C) and then commit it by passing it to our DRM bindings by-value, insuring that once the commit process begins it is impossible to keep using the AtomicStateComposer. The next part of this is allowing users to modify the atomic states of all of the objects contained within an atomic state. Since it's an extremely common usecase for objects to mutate the atomic state of multiple objects at once in an unpredictable order, we need a mechanism that will allow us to hand out &mut references to each state while ensuring at runtime that we do not break rust's data aliasing rules (which disallow us from ever having more then one &mut reference to the same piece of data). We do this by introducing the concept of a "borrowed" state. This is a very similar concept to RefCell, where it is ensured during runtime that when a &mut reference is taken out another one cannot be created until the corresponding Ref object has been dropped. Our equivalent Ref types are BorrowedConnectorState, BorrowedCrtcState, and BorrowedPlaneState. Each one of these types can be used in the same manner as a Ref - no additional borrows for an atomic state may be taken until the existing one has been dropped. Subsequently, all of these types implement their respective AsRaw* and FromRaw* counter-parts - and allow dereferencing to each driver-private data structure for fully qualified borrows (like BorrowedCrtcState<'a, CrtcState>. This allows a pretty clean way of mutating multiple states at once without ever breaking rust's mutability rules. We'll use all of these types over the next few commits to begin introducing various atomic modeset callbacks to each mode object type. Signed-off-by: Lyude Paul --- TODO: * Finish adding state iterators We only have one iterator for planes right now, but the plan is to have iterators for all types and have two different kind of iterators: * State object iterators Basically, these just iterate through all of the mode objects of a specific type present in an atomic state. Currently this is what our plane iterator does. * State mutator iterators With the existence of AtomicStateMutator and friends, it makes sense to have a type of iterator that: * Only iterates through unborrowed atomic states, removing the need to deal with the Option<> that get_new_*_state() functions return * Returns each (object, old_state, new_state) triplet as a dedicated type (PlaneUpdate, CrtcUpdate, ConnectorUpdate) that can be upcasted from an Opaque type using a single call. This is desirable, as it would make iterating through objects with a specific Driver* implementation as easy as just adding a .filter_map() call to the iterator. * Upcast functions for the Borrowed* types Signed-off-by: Lyude Paul --- rust/helpers/drm/atomic.c | 32 +++ rust/helpers/drm/drm.c | 3 + rust/kernel/drm/kms.rs | 9 + rust/kernel/drm/kms/atomic.rs | 419 +++++++++++++++++++++++++++++++ rust/kernel/drm/kms/connector.rs | 75 ++++++ rust/kernel/drm/kms/crtc.rs | 75 ++++++ rust/kernel/drm/kms/plane.rs | 77 ++++++ 7 files changed, 690 insertions(+) create mode 100644 rust/helpers/drm/atomic.c create mode 100644 rust/kernel/drm/kms/atomic.rs diff --git a/rust/helpers/drm/atomic.c b/rust/helpers/drm/atomic.c new file mode 100644 index 0000000000000..fff70053f6943 --- /dev/null +++ b/rust/helpers/drm/atomic.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +void rust_helper_drm_atomic_state_get(struct drm_atomic_state *state) +{ + drm_atomic_state_get(state); +} + +void rust_helper_drm_atomic_state_put(struct drm_atomic_state *state) +{ + drm_atomic_state_put(state); +} + +// Macros for generating one repetitive atomic state accessors (like drm_atomic_get_new_plane_state) +#define STATE_FUNC(type, tense) \ + struct drm_ ## type ## _state *rust_helper_drm_atomic_get_ ## tense ## _ ## type ## _state( \ + const struct drm_atomic_state *state, \ + struct drm_ ## type *type \ + ) { \ + return drm_atomic_get_## tense ## _ ## type ## _state(state, type); \ + } +#define STATE_FUNCS(type) \ + STATE_FUNC(type, new); \ + STATE_FUNC(type, old); + +STATE_FUNCS(plane); +STATE_FUNCS(crtc); +STATE_FUNCS(connector); + +#undef STATE_FUNCS +#undef STATE_FUNC diff --git a/rust/helpers/drm/drm.c b/rust/helpers/drm/drm.c index 028b8ab429572..365f6807774d4 100644 --- a/rust/helpers/drm/drm.c +++ b/rust/helpers/drm/drm.c @@ -1,5 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 +#ifdef CONFIG_DRM_KMS_HELPER +#include "atomic.c" +#endif #include "gem.c" #ifdef CONFIG_DRM_GEM_SHMEM_HELPER #include "gem_shmem_helper.c" diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs index d74267c78864f..4ab039d67352e 100644 --- a/rust/kernel/drm/kms.rs +++ b/rust/kernel/drm/kms.rs @@ -2,6 +2,7 @@ //! KMS driver abstractions for rust. +pub mod atomic; pub mod connector; pub mod crtc; pub mod encoder; @@ -248,6 +249,14 @@ pub(crate) fn mode_config_mutex(&self) -> &Mutex<()> { pub fn mode_config_lock(&self) -> ModeConfigGuard<'_, T> { ModeConfigGuard(self.mode_config_mutex().lock(), PhantomData) } + + /// Return the number of registered [`Plane`](plane::Plane) objects on this [`Device`]. + #[inline] + pub fn num_plane(&self) -> i32 { + // SAFETY: The only context which this could change is before registration, which must be + // single-threaded anyway - so it's safe to just read this value + unsafe { (*self.as_raw()).mode_config.num_total_plane } + } } /// A modesetting object in DRM. diff --git a/rust/kernel/drm/kms/atomic.rs b/rust/kernel/drm/kms/atomic.rs new file mode 100644 index 0000000000000..a4354b89b07cc --- /dev/null +++ b/rust/kernel/drm/kms/atomic.rs @@ -0,0 +1,419 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +//! [`struct drm_atomic_state`] related bindings for rust. +//! +//! [`struct drm_atomic_state`]: srctree/include/drm/drm_atomic.h +use alloc::boxed::Box; +use crate::{ + bindings, + drm::{device::Device, drv::Driver}, + types::*, + error::{from_err_ptr, to_result, from_result}, + init::Zeroable, + prelude::*, + private::Sealed +}; +use core::{ + marker::*, + ptr::NonNull, + cell::Cell, + ffi::*, + slice, + ops::*, + mem::ManuallyDrop, + iter::Iterator, +}; +use super::{ + crtc::*, + connector::*, + plane::*, + Kms, + KmsDriver, + ModeObject +}; + +/// The main wrapper around [`struct drm_atomic_state`]. +/// +/// This type is usually embedded within another interface such as an [`AtomicStateMutator`]. +/// +/// # Invariants +/// +/// - The data layout of this type is identical to [`struct drm_atomic_state`]. +/// - `state` is initialized for as long as this type is exposed to users. +/// +/// [`struct drm_atomic_state`]: srctree/include/drm/drm_atomic.h +#[repr(transparent)] +pub struct AtomicState { + pub(super) state: Opaque, + _p: PhantomData, +} + +impl AtomicState { + /// Reconstruct an immutable reference to an atomic state from the given pointer + /// + /// # Safety + /// + /// `ptr` must point to a valid initialized instance of [`struct drm_atomic_state`]. + /// + /// [`struct drm_atomic_state`]: srctree/include/drm/drm_atomic.h + pub(super) unsafe fn from_raw<'a>(ptr: *const bindings::drm_atomic_state) -> &'a Self { + // SAFETY: Our data layout is identical + unsafe { &*ptr.cast() } + } + + pub(crate) fn as_raw(&self) -> *mut bindings::drm_atomic_state { + self.state.get() + } + + /// Return the [`Device`] associated with this [`AtomicState`]. + pub fn drm_dev(&self) -> &Device { + // SAFETY: + // * `state` is initialized via our type invariants. + // * `dev` is invariant throughout the lifetime of `AtomicState` + unsafe { Device::borrow((*self.state.get()).dev) } + } + + /// Return the old atomic state for `crtc`, if it is present within this [`AtomicState`]. + pub fn get_old_crtc_state(&self, crtc: &C) -> Option<&C::State> + where + C: AsRawCrtc, + { + // SAFETY: This function either returns NULL or a valid pointer to a `drm_crtc_state` + unsafe { + bindings::drm_atomic_get_old_crtc_state( + self.as_raw(), + crtc.as_raw() + ).as_ref().map(|p| unsafe { C::State::from_raw(p) }) + } + } + + /// Return the old atomic state for `plane`, if it is present within this [`AtomicState`]. + pub fn get_old_plane_state

(&self, plane: &P) -> Option<&P::State> + where + P: AsRawPlane, + { + // SAFETY: This function either returns NULL or a valid pointer to a `drm_plane_state` + unsafe { + bindings::drm_atomic_get_old_plane_state( + self.as_raw(), + plane.as_raw() + ).as_ref().map(|p| unsafe { P::State::from_raw(p) }) + } + } + + /// Return the old atomic state for `connector` if it is present within this [`AtomicState`]. + pub fn get_old_connector_state(&self, connector: &C) -> Option<&C::State> + where + C: AsRawConnector + { + // SAFETY: THis function either returns NULL or a valid pointer to a `drm_connector_state`. + unsafe { + bindings::drm_atomic_get_old_connector_state( + self.as_raw(), + connector.as_raw() + ).as_ref().map(|p| unsafe { C::State::from_raw(p) }) + } + } +} + +// SAFETY: DRM atomic state objects are always reference counted and the get/put functions satisfy +// the requirements. +unsafe impl AlwaysRefCounted for AtomicState { + fn inc_ref(&self) { + // SAFETY: FFI call with no special requirements + unsafe { bindings::drm_atomic_state_get(self.state.get()) } + } + + unsafe fn dec_ref(obj: NonNull) { + // SAFETY: FFI calls with no special requirements + unsafe { bindings::drm_atomic_state_put(obj.as_ptr().cast()) } + } +} + +/// A smart-pointer for modifying the contents of an atomic state. +/// +/// As it's not unreasonable for a modesetting driver to want to have references to the state of +/// multiple modesetting objects at once, along with mutating multiple states for unique modesetting +/// objects at once, this type provides a mechanism for safely doing both of these things. +/// +/// To honor Rust's aliasing rules regarding mutable references, this structure ensures only one +/// mutable reference to a mode object's atomic state may exist at a time - and refuses to provide +/// another if one has already been taken out using runtime checks. +pub struct AtomicStateMutator { + /// The state being mutated. Note that the use of `ManuallyDrop` here is because mutators are + /// only constructed in FFI callbacks and thus borrow their references to the atomic state from + /// DRM. Composers, which make use of mutators internally, can potentially be owned by rust code + /// if a driver is performing an atomic commit internally - and thus will call the drop + /// implementation here. + state: ManuallyDrop>>, + + /// Bitmask of borrowed CRTC state objects + pub(super) borrowed_crtcs: Cell, + /// Bitmask of borrowed plane state objects + pub(super) borrowed_planes: Cell, + /// Bitmask of borrowed connector state objects + pub(super) borrowed_connectors: Cell, +} + +impl AtomicStateMutator { + /// Construct a new [`AtomicStateMutator`] + /// + /// # Safety + /// + /// `ptr` must point to a valid `drm_atomic_state` + pub(super) unsafe fn new(ptr: NonNull) -> Self { + Self { + // SAFETY: The data layout of `AtomicState` is identical to drm_atomic_state + state: ManuallyDrop::new(unsafe { ARef::from_raw(ptr.cast()) }), + borrowed_planes: Cell::default(), + borrowed_crtcs: Cell::default(), + borrowed_connectors: Cell::default(), + } + } + + pub(crate) fn as_raw(&self) -> *mut bindings::drm_atomic_state { + self.state.as_raw() + } + + /// Return the [`Device`] for this [`AtomicStateMutator`] + pub fn drm_dev(&self) -> &Device { + self.state.drm_dev() + } + + /// Retrieve the last committed atomic state for `crtc` if `crtc` has already been added to the + /// atomic state being composed. + /// + /// Returns `None` otherwise. + pub fn get_old_crtc_state(&self, crtc: &C) -> Option<&C::State> + where + C: AsRawCrtc + { + self.state.get_old_crtc_state(crtc) + } + + /// Retrieve the last committed atomic state for `connector` if `connector` has already been + /// added to the atomic state being composed. + /// + /// Returns `None` otherwise. + pub fn get_old_connector_state(&self, connector: &C) -> Option<&C::State> + where + C: AsRawConnector + { + self.state.get_old_connector_state(connector) + } + + /// Retrieve the last committed atomic state for `plane` if `plane` has already been added to + /// the atomic state being composed. + /// + /// Returns `None` otherwise. + pub fn get_old_plane_state

(&self, plane: &P) -> Option<&P::State> + where + P: AsRawPlane, + { + self.state.get_old_plane_state(plane) + } + + /// Return a composer for `plane`s new atomic state if it was previously added to the atomic + /// state being composed. + /// + /// Returns `None` otherwise, or if a composer still exists for this state. + pub fn get_new_crtc_state(&self, crtc: &C) -> Option> + where + C: AsRawCrtc + { + // SAFETY: DRM either returns NULL or a valid pointer to a `drm_crtc_state` + let state = unsafe { + bindings::drm_atomic_get_new_crtc_state(self.as_raw(), crtc.as_raw()) + }; + + BorrowedCrtcState::::new(self, NonNull::new(state)?) + } + + /// Return a composer for `plane`s new atomic state if it was previously added to the atomic + /// state being composed. + /// + /// Returns `None` otherwise, or if a composer still exists for this state. + pub fn get_new_plane_state

(&self, plane: &P) -> Option> + where + P: AsRawPlane, + { + // SAFETY: DRM either returns NULL or a valid pointer to a `drm_plane_state`. + let state = unsafe { + bindings::drm_atomic_get_new_plane_state(self.as_raw(), plane.as_raw()) + }; + + BorrowedPlaneState::::new(self, NonNull::new(state)?) + } + + /// Return a composer for `crtc`s new atomic state if it was previously added to the atomic + /// state being composed. + /// + /// Returns `None` otherwise, or if a composer still exists for this state. + pub fn get_new_connector_state( + &self, + connector: &C + ) -> Option> + where + C: AsRawConnector, + { + // SAFETY: DRM either returns NULL or a valid pointer to a `drm_connector_state` + let state = unsafe { + bindings::drm_atomic_get_new_connector_state(self.as_raw(), connector.as_raw()) + }; + + BorrowedConnectorState::::new(self, NonNull::new(state)?) + } + + /// Iterate through each of the planes (regardless of type) currently in this atomic state. + pub fn iter_planes(&self) -> AtomicStatePlaneIter<'_, T> { + AtomicStatePlaneIter::new(&self.state) + } +} + +/// An [`AtomicStateMutator`] wrapper which is not yet part of any commit operation. +/// +/// Since it's not yet part of a commit operation, new mode objects may be added to the state. It +/// also holds a reference to the underlying [`AtomicState`] that will be released when this object +/// is dropped. +pub struct AtomicStateComposer(AtomicStateMutator); + +impl Deref for AtomicStateComposer { + type Target = AtomicStateMutator; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Drop for AtomicStateComposer { + fn drop(&mut self) { + // SAFETY: We're in drop, so this is guaranteed to be the last possible reference + unsafe { ManuallyDrop::drop(&mut self.0.state) } + } +} + +impl AtomicStateComposer { + /// # Safety + /// + /// The caller guarantees that `ptr` points to a valid instance of `drm_atomic_state`. + pub(crate) unsafe fn new(ptr: NonNull) -> Self { + // SAFETY: see `AtomicStateMutator::from_raw()` + Self(unsafe { AtomicStateMutator::new(ptr) }) + } + + /// Attempt to add the state for `crtc` to the atomic state for this composer if it hasn't + /// already been added, and create a mutator for it. + /// + /// If a composer already exists for this `crtc`, this function returns `Error(EBUSY)`. If + /// attempting to add the state fails, another error code will be returned. + pub fn add_crtc_state(&self, crtc: &C) -> Result> + where + C: AsRawCrtc + { + // SAFETY: DRM will only return a valid pointer to a [`drm_crtc_state`] - or an error. + let state = unsafe { + from_err_ptr( + bindings::drm_atomic_get_crtc_state(self.as_raw(), crtc.as_raw()) + ).map(|c| NonNull::new_unchecked(c)) + }?; + + BorrowedCrtcState::::new(self, state).ok_or(EBUSY) + } + + /// Attempt to add the state for `plane` to the atomic state for this composer if it hasn't + /// already been added, and create a mutator for it. + /// + /// If a composer already exists for this `plane`, this function returns `Error(EBUSY)`. If + /// attempting to add the state fails, another error code will be returned. + pub fn add_plane_state

(&self, plane: &P) -> Result> + where + P: AsRawPlane, + { + // SAFETY: DRM will only return a valid pointer to a [`drm_plane_state`] - or an error. + let state = unsafe { + from_err_ptr( + bindings::drm_atomic_get_plane_state(self.as_raw(), plane.as_raw()) + ).map(|p| NonNull::new_unchecked(p)) + }?; + + BorrowedPlaneState::::new(self, state).ok_or(EBUSY) + } + + /// Attempt to add the state for `connector` to the atomic state for this composer if it hasn't + /// already been added, and create a mutator for it. + /// + /// If a composer already exists for this `connector`, this function returns `Error(EBUSY)`. If + /// attempting to add the state fails, another error code will be returned. + pub fn add_connector_state( + &self, + connector: &C + ) -> Result> + where + C: AsRawConnector, + { + // SAFETY: DRM will only return a valid pointer to a [`drm_plane_state`] - or an error. + let state = unsafe { + from_err_ptr( + bindings::drm_atomic_get_connector_state(self.as_raw(), connector.as_raw()) + ).map(|c| NonNull::new_unchecked(c)) + }?; + + BorrowedConnectorState::::new(self, state).ok_or(EBUSY) + } + + /// Attempt to add any planes affected by changes on `crtc` to this [`AtomicStateComposer`]. + /// + /// Will return an [`Error`] if this fails. + pub fn add_affected_planes(&self, crtc: &impl AsRawCrtc) -> Result { + // SAFETY: FFI call with no special safety requirements + to_result(unsafe { + bindings::drm_atomic_add_affected_planes(self.as_raw(), crtc.as_raw()) + }) + } +} + +/// An iterator which goes through each DRM plane currently in an atomic state. +/// +/// Note that this iterator will return [`OpaquePlane`]s, because it's entirely possible for a +/// driver to have multiple implementations of [`DriverPlane`] - so we don't know what the fully +/// qualified type of each plane is. +pub struct AtomicStatePlaneIter<'a, T: KmsDriver> { + state: &'a AtomicState, + current_idx: u8, +} + +impl<'a, T: KmsDriver> Iterator for AtomicStatePlaneIter<'a, T> { + type Item = &'a OpaquePlane; + + fn next(&mut self) -> Option { + let ptr = self.state.state.get(); + + // SAFETY: `planes` is initialized by the time we expose AtomicState through any form to + // users. And because we don't allow state mutation outside of mutators, which are single + // threaded, the contents of this struct are at least guaranteed not to change through the + // duration of this borrow. + let planes: &[bindings::__drm_planes_state] = unsafe { + slice::from_raw_parts((*ptr).planes.cast_const(), self.state.drm_dev().num_plane() as _) + }; + + for plane_states in &planes[self.current_idx as _..] { + self.current_idx += 1; + if !plane_states.ptr.is_null() { + // SAFETY: OpaquePlane has an identical data layout, and its only possible values + // are NULL or pointing at a valid drm_plane + return Some(unsafe { OpaquePlane::from_raw(plane_states.ptr) }); + } + } + + None + } +} + +impl<'a, T: KmsDriver> AtomicStatePlaneIter<'a, T> { + fn new(state: &'a AtomicState) -> AtomicStatePlaneIter<'a, T> { + Self { + current_idx: 0, + state + } + } +} diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs index f62740d7f6469..6fcfce8b48c64 100644 --- a/rust/kernel/drm/kms/connector.rs +++ b/rust/kernel/drm/kms/connector.rs @@ -31,6 +31,7 @@ ModeConfigGuard, encoder::*, KmsDriver, + atomic::*, }; use macros::pin_data; @@ -679,6 +680,80 @@ unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_connector_state) -> &'a mut S } } +/// An interface for mutating a [`Connector`]s atomic state. +/// +/// This type is typically returned by an [`AtomicStateMutator`] within contexts where it is +/// possible to safely mutate a connector's state. In order to uphold rust's data-aliasing rules, +/// only [`BorrowedConnectorState`] may exist at a time. +pub struct BorrowedConnectorState<'a, T: FromRawConnectorState> { + state: &'a mut T, + mask: &'a Cell +} + +impl<'a, T: FromRawConnectorState> BorrowedConnectorState<'a, T> { + pub(super) fn new( + mutator: &'a AtomicStateMutator, + state: NonNull + ) -> Option { + // SAFETY: `connector` is invariant throughout the lifetime of the atomic state, is + // initialized by this point, and we're guaranteed it is of type `OpaqueConnector` by + // type invariance + let connector = unsafe { T::Connector::from_raw((*state.as_ptr()).connector) }; + let conn_mask = connector.mask(); + let borrowed_mask = mutator.borrowed_connectors.get(); + + if borrowed_mask & conn_mask == 0 { + mutator.borrowed_connectors.set(borrowed_mask | conn_mask); + Some(Self { + mask: &mutator.borrowed_connectors, + // SAFETY: We're guaranteed `state` is of `ConnectorState` by type invariance, + // and we just confirmed by checking `borrowed_connectors` that no other mutable + // borrows have been taken out for `state` + state: unsafe { T::from_raw_mut(state.as_ptr()) }, + }) + } else { + // TODO: Print a kernel warning here, this is a user error + None + } + } +} + +impl<'a, T: DriverConnectorState> Deref for BorrowedConnectorState<'a, ConnectorState> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.state.inner + } +} + +impl<'a, T: DriverConnectorState> DerefMut for BorrowedConnectorState<'a, ConnectorState> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.state.inner + } +} + +impl<'a, T: FromRawConnectorState> Drop for BorrowedConnectorState<'a, T> { + fn drop(&mut self) { + let mask = self.state.connector().mask(); + self.mask.set(self.mask.get() & !mask); + } +} + +impl<'a, T: FromRawConnectorState> AsRawConnectorState for BorrowedConnectorState<'a, T> { + type Connector = T::Connector; +} + +impl<'a, T: FromRawConnectorState> private::AsRawConnectorState for BorrowedConnectorState<'a, T> { + fn as_raw(&self) -> &bindings::drm_connector_state { + self.state.as_raw() + } + + unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_connector_state { + // SAFETY: We're bound by the same safety contract as this function + unsafe { self.state.as_raw_mut() } + } +} + unsafe extern "C" fn atomic_duplicate_state_callback( connector: *mut bindings::drm_connector ) -> *mut bindings::drm_connector_state diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs index 246d15a15c14d..7864540705f76 100644 --- a/rust/kernel/drm/kms/crtc.rs +++ b/rust/kernel/drm/kms/crtc.rs @@ -3,6 +3,7 @@ //! KMS driver abstractions for rust. use super::{ + atomic::*, plane::*, ModeObject, StaticModeObject, @@ -552,6 +553,80 @@ unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc_state) -> &'a Self { unsafe { &*(ptr.cast()) } } } + +/// An interface for mutating a [`Crtc`]s atomic state. +/// +/// This type is typically returned by an [`AtomicStateMutator`] within contexts where it is +/// possible to safely mutate a plane's state. In order to uphold rust's data-aliasing rules, only +/// [`BorrowedCrtcState`] may exist at a time. +/// +/// # Invariants +/// +/// `self.state` always points to a valid instance of a [`FromRawCrtcState`] object. +pub struct BorrowedCrtcState<'a, T: FromRawCrtcState> { + state: NonNull, + mask: &'a Cell, +} + +impl<'a, T: FromRawCrtcState> BorrowedCrtcState<'a, T> { + pub(super) fn new( + mutator: &'a AtomicStateMutator, + state: NonNull + ) -> Option { + // SAFETY: `crtc` is invariant throughout the lifetime of the atomic state, and always + // points to a valid `Crtc` + let crtc = unsafe { T::Crtc::from_raw((*state.as_ptr()).crtc) }; + let crtc_mask = crtc.mask(); + let borrowed_mask = mutator.borrowed_crtcs.get(); + + if borrowed_mask & crtc_mask == 0 { + mutator.borrowed_crtcs.set(borrowed_mask | crtc_mask); + Some(Self { + mask: &mutator.borrowed_crtcs, + state: state.cast() + }) + } else { + None + } + } +} + +impl<'a, T: FromRawCrtcState> Drop for BorrowedCrtcState<'a, T> { + fn drop(&mut self) { + // SAFETY: Our interface is proof that we are the only ones with a reference to this data + let mask = unsafe { self.state.as_ref() }.crtc().mask(); + self.mask.set(self.mask.get() & !mask); + } +} + +impl<'a, T: DriverCrtcState> Deref for BorrowedCrtcState<'a, CrtcState> { + type Target = T; + + fn deref(&self) -> &Self::Target { + // SAFETY: Our interface ensures that `self.state.inner` follows rust's data-aliasing rules, + // so this is safe + unsafe { &*(*self.state.as_ptr()).inner.get() } + } +} + +impl<'a, T: DriverCrtcState> DerefMut for BorrowedCrtcState<'a, CrtcState> { + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: Our interface ensures that `self.state.inner` follows rust's data-aliasing rules, + // so this is safe + unsafe { (*self.state.as_ptr()).inner.get_mut() } + } +} + +impl<'a, T: FromRawCrtcState> AsRawCrtcState for BorrowedCrtcState<'a, T> { + type Crtc = T::Crtc; +} + +impl<'a, T: FromRawCrtcState> private::AsRawCrtcState for BorrowedCrtcState<'a, T> { + fn as_raw(&self) -> *mut bindings::drm_crtc_state { + self.state.as_ptr().cast() + } +} + unsafe extern "C" fn crtc_destroy_callback( crtc: *mut bindings::drm_crtc ) { diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs index 1c151ae3b3dcc..d6e11a65cc101 100644 --- a/rust/kernel/drm/kms/plane.rs +++ b/rust/kernel/drm/kms/plane.rs @@ -29,6 +29,7 @@ UnregisteredKmsDevice, ModeObject, StaticModeObject, + atomic::*, }; /// The main trait for implementing the [`struct drm_plane`] API for [`Plane`] @@ -597,6 +598,82 @@ unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_plane_state) -> &'a mut Self unsafe { &mut *ptr.cast() } } } + +/// An interface for mutating a [`Plane`]s atomic state. +/// +/// This type is typically returned by an [`AtomicStateMutator`] within contexts where it is +/// possible to safely mutate a plane's state. In order to uphold rust's data-aliasing rules, only +/// [`BorrowedPlaneState`] may exist at a time. +pub struct BorrowedPlaneState<'a, T: FromRawPlaneState> { + state: &'a mut T, + mask: &'a Cell +} + +impl<'a, T: FromRawPlaneState> BorrowedPlaneState<'a, T> { + pub(super) fn new( + mutator: &'a AtomicStateMutator, + state: NonNull + ) -> Option { + // SAFETY: `plane` is invariant throughout the lifetime of the atomic state, is + // initialized by this point, and we're guaranteed it is of type `AsRawPlane` by type + // invariance + let plane = unsafe { T::Plane::from_raw((*state.as_ptr()).plane) }; + let plane_mask = plane.mask(); + let borrowed_mask = mutator.borrowed_planes.get(); + + if borrowed_mask & plane_mask == 0 { + mutator.borrowed_planes.set(borrowed_mask | plane_mask); + Some(Self { + mask: &mutator.borrowed_planes, + // SAFETY: We're guaranteed `state` is of `FromRawPlaneState` by type invariance, + // and we just confirmed by checking `borrowed_planes` that no other mutable borrows + // have been taken out for `state` + state: unsafe { T::from_raw_mut(state.as_ptr()) }, + }) + } else { + None + } + } +} + +impl<'a, T: FromRawPlaneState> Drop for BorrowedPlaneState<'a, T> { + fn drop(&mut self) { + let mask = self.state.plane().mask(); + self.mask.set(self.mask.get() & !mask); + } +} + +impl<'a, T: FromRawPlaneState> AsRawPlaneState for BorrowedPlaneState<'a, T> { + type Plane = T::Plane; +} + +impl<'a, T: FromRawPlaneState> private::AsRawPlaneState for BorrowedPlaneState<'a, T> { + fn as_raw(&self) -> &bindings::drm_plane_state { + self.state.as_raw() + } + + unsafe fn as_raw_mut(&mut self) -> &mut bindings::drm_plane_state { + // SAFETY: This function is bound by the same safety contract as `self.inner.as_raw_mut()` + unsafe { self.state.as_raw_mut() } + } +} + +impl<'a, T: FromRawPlaneState> Sealed for BorrowedPlaneState<'a, T> {} + +impl<'a, T: DriverPlaneState> Deref for BorrowedPlaneState<'a, PlaneState> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.state.inner + } +} + +impl<'a, T: DriverPlaneState> DerefMut for BorrowedPlaneState<'a, PlaneState> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.state.inner + } +} + unsafe extern "C" fn plane_destroy_callback( plane: *mut bindings::drm_plane ) { From patchwork Mon Sep 30 23:10:04 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817274 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 5AD9CCEB2CD for ; Mon, 30 Sep 2024 23:40:16 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id D0AF810E5A0; Mon, 30 Sep 2024 23:40:15 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="fbIk37o4"; 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 6E69710E5A0 for ; Mon, 30 Sep 2024 23:40:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739613; 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=fXYRkES5IMRSEWy/SdffRUBHVVSOxpQy/XQZZbTshqY=; b=fbIk37o4uUCywcXOq95gDkAMzprMwGOxUD7ZMuTfDmbdZNMXtT5+5N9aeMHJWvB8qYmX8u Z+BQ68TQZJ1GuwtJXK/X6xNIcUlK601Es5BH0MF0rtMp0I1ozllXDW45mp7QZUEqJLbbbz qLEUqu8tUSpTJcV1dKSf+P+O0gPAjJQ= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-331-ZZjrKZJOO3uRfv2nR5MVhg-1; Mon, 30 Sep 2024 19:40:08 -0400 X-MC-Unique: ZZjrKZJOO3uRfv2nR5MVhg-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 17FEF192DE39; Mon, 30 Sep 2024 23:40:06 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 4928D3003E4D; Mon, 30 Sep 2024 23:40:02 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 21/35] rust: drm/kms: Introduce DriverCrtc::atomic_check() Date: Mon, 30 Sep 2024 19:10:04 -0400 Message-ID: <20240930233257.1189730-22-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" An optional trait method for implementing a CRTC's atomic state check. Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/crtc.rs | 46 +++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs index 7864540705f76..43c7264402b07 100644 --- a/rust/kernel/drm/kms/crtc.rs +++ b/rust/kernel/drm/kms/crtc.rs @@ -27,7 +27,7 @@ marker::*, ptr::{NonNull, null, null_mut, addr_of_mut, self}, ops::{Deref, DerefMut}, - mem, + mem::{self, ManuallyDrop}, }; use macros::vtable; @@ -82,7 +82,7 @@ pub trait DriverCrtc: Send + Sync + Sized { helper_funcs: bindings::drm_crtc_helper_funcs { atomic_disable: None, atomic_enable: None, - atomic_check: None, + atomic_check: if Self::HAS_ATOMIC_CHECK { Some(atomic_check_callback::) } else { None }, dpms: None, commit: None, prepare: None, @@ -117,6 +117,21 @@ pub trait DriverCrtc: Send + Sync + Sized { /// /// Drivers may use this to instantiate their [`DriverCrtc`] object. fn new(device: &Device, args: &Self::Args) -> impl PinInit; + + /// The optional [`drm_crtc_helper_funcs.atomic_check`] hook for this crtc. + /// + /// Drivers may use this to customize the atomic check phase of their [`Crtc`] objects. The + /// result of this function determines whether the atomic check passed or failed. + /// + /// [`drm_crtc_helper_funcs.atomic_check`]: srctree/include/drm/drm_modeset_helper_vtables.h + fn atomic_check( + crtc: &Crtc, + old_state: &CrtcState, + new_state: BorrowedCrtcState<'_, CrtcState>, + state: &AtomicStateComposer + ) -> Result { + build_error::build_error("This should not be reachable") + } } /// The generated C vtable for a [`DriverCrtc`]. @@ -726,3 +741,30 @@ fn as_raw(&self) -> *mut bindings::drm_crtc_state { ) }; } + +unsafe extern "C" fn atomic_check_callback( + crtc: *mut bindings::drm_crtc, + state: *mut bindings::drm_atomic_state, +) -> i32 { + // SAFETY: + // * We're guaranteed `crtc` is of type `Crtc` via type invariants. + // * We're guaranteed by DRM that `crtc` is pointing to a valid initialized state. + let crtc = unsafe { Crtc::from_raw(crtc) }; + + // SAFETY: DRM guarantees `state` points to a valid `drm_atomic_state` + let state = unsafe { + ManuallyDrop::new(AtomicStateComposer::new(NonNull::new_unchecked(state))) + }; + + // SAFETY: Since we are in the atomic update callback, we're guaranteed by DRM that both the old + // and new atomic state are present within `state` + let (old_state, new_state) = unsafe {( + state.get_old_crtc_state(crtc).unwrap_unchecked(), + state.get_new_crtc_state(crtc).unwrap_unchecked(), + )}; + + from_result(|| { + T::atomic_check(crtc, old_state, new_state, &state)?; + Ok(0) + }) +} From patchwork Mon Sep 30 23:10:05 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817275 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 26199CEB2CD for ; Mon, 30 Sep 2024 23:40:22 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id A4BA210E5A1; Mon, 30 Sep 2024 23:40:21 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="KUOh7f6k"; 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 AE84310E5A1 for ; Mon, 30 Sep 2024 23:40:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739618; 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=HfyGQAtdmrvZOPwaMinkiOwAP5GbXOwQ1SJfQzgrIws=; b=KUOh7f6k8VnT+SPLmdngOee9Obg9pNI8LThDnKS0hSsw2f7QmxEczfZaYLVFWjhhEn9hbR aj1pkhzpHoks6Ys+cQSPgdbp6heOTwqF2U4KPo/C0g4FnvIerJ2VUYGihvDJ9I5iSV3md1 bjADv6fg83BneDPCxey+3Y/07t4I4n4= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-491-xZpGV_G_OmSZbwHMYykISQ-1; Mon, 30 Sep 2024 19:40:15 -0400 X-MC-Unique: xZpGV_G_OmSZbwHMYykISQ-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 071AE19560B7; Mon, 30 Sep 2024 23:40:13 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id DD4DA3003DEC; Mon, 30 Sep 2024 23:40:08 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 22/35] rust: drm/kms: Add DriverPlane::atomic_update() Date: Mon, 30 Sep 2024 19:10:05 -0400 Message-ID: <20240930233257.1189730-23-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" A mandatory trait method used for implementing DRM's atomic plane update callback. Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/plane.rs | 39 +++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs index d6e11a65cc101..506ed5ced1270 100644 --- a/rust/kernel/drm/kms/plane.rs +++ b/rust/kernel/drm/kms/plane.rs @@ -75,7 +75,7 @@ pub trait DriverPlane: Send + Sync + Sized { begin_fb_access: None, // TODO: someday? end_fb_access: None, // TODO: someday? atomic_check: None, - atomic_update: None, + atomic_update: if Self::HAS_ATOMIC_UPDATE { Some(atomic_update_callback::) } else { None }, atomic_enable: None, // TODO atomic_disable: None, // TODO atomic_async_check: None, // TODO @@ -103,6 +103,21 @@ pub trait DriverPlane: Send + Sync + Sized { /// /// Drivers may use this to instantiate their [`DriverPlane`] object. fn new(device: &Device, args: Self::Args) -> impl PinInit; + + /// The optional [`drm_plane_helper_funcs.atomic_update`] hook for this plane. + /// + /// Drivers may use this to customize the atomic update phase of their [`Plane`] objects. If not + /// specified, this function is a no-op. + /// + /// [`drm_plane_helper_funcs.atomic_update`]: srctree/include/drm/drm_modeset_helper_vtables.h + fn atomic_update( + plane: &Plane, + new_state: BorrowedPlaneState<'_, PlaneState>, + old_state: &PlaneState, + state: &AtomicStateMutator + ) { + build_error::build_error("This should not be reachable") + } } /// The generated C vtable for a [`DriverPlane`]. @@ -757,3 +772,25 @@ fn deref_mut(&mut self) -> &mut Self::Target { // - The cast to `drm_plane_state` is safe via `PlaneState`s type invariants. unsafe { bindings::__drm_atomic_helper_plane_reset(plane, Box::into_raw(new).cast()) }; } + +unsafe extern "C" fn atomic_update_callback( + plane: *mut bindings::drm_plane, + state: *mut bindings::drm_atomic_state, +) { + // SAFETY: + // * We're guaranteed `plane` is of type `Plane` via type invariants. + // * We're guaranteed by DRM that `plane` is pointing to a valid initialized state. + let plane = unsafe { Plane::from_raw(plane) }; + + // SAFETY: DRM guarantees `state` points to a valid `drm_atomic_state` + let state = unsafe { AtomicStateMutator::new(NonNull::new_unchecked(state)) }; + + // SAFETY: Since we are in the atomic update callback, we're guaranteed by DRM that both the old + // and new atomic state are present within `state` + let (old_state, new_state) = unsafe {( + state.get_old_plane_state(plane).unwrap_unchecked(), + state.get_new_plane_state(plane).unwrap_unchecked(), + )}; + + T::atomic_update(plane, new_state, old_state, &state); +} From patchwork Mon Sep 30 23:10:06 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817276 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 C93F3CEB2CB for ; Mon, 30 Sep 2024 23:40:32 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 5D3C010E5A6; Mon, 30 Sep 2024 23:40:32 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="daFccErm"; 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 F054710E5A2 for ; Mon, 30 Sep 2024 23:40:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739627; 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=Ks/AeHTgPHpVhwmTJF1Of6G8oz5pXr9wZPJOSOab2EM=; b=daFccErm2uPDhJwTn8HeF6oLx0Fmxw8kO1AzHkdeIF0bYG4nJqe3PHsr2LpWn7LWSXKd5m Ap7lrMTnuPUAq3yTPPueX/dgI7nN2qx4YH9PPb9YDOZHg2gPdPGlGDsBDxvIkhaY67wih2 tCg+omBPsBK+s3Szp+0iWCO8iqzcr5E= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-351-ZiOOdnYzM3yS3u0JqSaZCg-1; Mon, 30 Sep 2024 19:40:24 -0400 X-MC-Unique: ZiOOdnYzM3yS3u0JqSaZCg-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 2DAD2195395A; Mon, 30 Sep 2024 23:40:20 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 16F4A3003DEC; Mon, 30 Sep 2024 23:40:15 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 23/35] rust: drm/kms: Add DriverPlane::atomic_check() Date: Mon, 30 Sep 2024 19:10:06 -0400 Message-ID: <20240930233257.1189730-24-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" Optional trait method for implementing a plane's atomic_check(). Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/plane.rs | 41 +++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs index 506ed5ced1270..04f1bdfbb1ea2 100644 --- a/rust/kernel/drm/kms/plane.rs +++ b/rust/kernel/drm/kms/plane.rs @@ -74,7 +74,7 @@ pub trait DriverPlane: Send + Sync + Sized { cleanup_fb: None, begin_fb_access: None, // TODO: someday? end_fb_access: None, // TODO: someday? - atomic_check: None, + atomic_check: if Self::HAS_ATOMIC_CHECK { Some(atomic_check_callback::) } else { None }, atomic_update: if Self::HAS_ATOMIC_UPDATE { Some(atomic_update_callback::) } else { None }, atomic_enable: None, // TODO atomic_disable: None, // TODO @@ -118,6 +118,21 @@ fn atomic_update( ) { build_error::build_error("This should not be reachable") } + + /// The optional [`drm_plane_helper_funcs.atomic_check`] hook for this plane. + /// + /// Drivers may use this to customize the atomic check phase of their [`Plane`] objects. The + /// result of this function determines whether the atomic check passed or failed. + /// + /// [`drm_plane_helper_funcs.atomic_check`]: srctree/include/drm/drm_modeset_helper_vtables.h + fn atomic_check( + plane: &Plane, + new_state: BorrowedPlaneState<'_, PlaneState>, + old_state: &PlaneState, + state: &AtomicStateComposer + ) -> Result { + build_error::build_error("This should not be reachable") + } } /// The generated C vtable for a [`DriverPlane`]. @@ -794,3 +809,27 @@ fn deref_mut(&mut self) -> &mut Self::Target { T::atomic_update(plane, new_state, old_state, &state); } + +unsafe extern "C" fn atomic_check_callback( + plane: *mut bindings::drm_plane, + state: *mut bindings::drm_atomic_state, +) -> i32 { + // SAFETY: + // * We're guaranteed `plane` is of type `Plane` via type invariants. + // * We're guaranteed by DRM that `plane` is pointing to a valid initialized state. + let plane = unsafe { Plane::from_raw(plane) }; + + // SAFETY: We're guaranteed by DRM that `state` points to a valid instance of `drm_atomic_state` + let state = ManuallyDrop::new(unsafe { + AtomicStateComposer::::new(NonNull::new_unchecked(state)) + }); + + // SAFETY: We're guaranteed by DRM that both the old and new atomic state are present within + // this `drm_atomic_state` + let (old_state, new_state) = unsafe {( + state.get_old_plane_state(plane).unwrap_unchecked(), + state.get_new_plane_state(plane).unwrap_unchecked(), + )}; + + from_result(|| T::atomic_check(plane, new_state, old_state, &state).map(|_| 0)) +} From patchwork Mon Sep 30 23:10:07 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817277 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 911A2CEB2CB for ; Mon, 30 Sep 2024 23:40:35 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 11C7310E5AA; Mon, 30 Sep 2024 23:40:35 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="FA9mF/D2"; 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 8230C10E5A3 for ; Mon, 30 Sep 2024 23:40:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739632; 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=60V41hOGujHu5sr/jwQK4i2saVJBJYoo6sOpK9L71eA=; b=FA9mF/D20FIxcEGGKV6ciNwFrk3dppNFvhfEs5S/i/VcjOAbGID4k2E2IT+lSBHC+5SGDZ kRB+4s0PaUEIOGavIeiSgVwxaXGZ0GjQPdN1Fzsq/C4adiupp9ORsX4M9wfK/i0jruRwQz FcY+EdB++zJlMmJWvntxkXtui11bjSw= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-231-QVcBuQXJPZC4PTHFCIUINA-1; Mon, 30 Sep 2024 19:40:29 -0400 X-MC-Unique: QVcBuQXJPZC4PTHFCIUINA-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id CE0DC19626D7; Mon, 30 Sep 2024 23:40:26 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 22E813003E4D; Mon, 30 Sep 2024 23:40:22 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 24/35] rust: drm/kms: Add RawCrtcState::active() Date: Mon, 30 Sep 2024 19:10:07 -0400 Message-ID: <20240930233257.1189730-25-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" A binding for checking drm_crtc_state.active. Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/crtc.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs index 43c7264402b07..ec9b58763dcca 100644 --- a/rust/kernel/drm/kms/crtc.rs +++ b/rust/kernel/drm/kms/crtc.rs @@ -496,6 +496,14 @@ fn crtc(&self) -> &Self::Crtc { // state unsafe { ::from_raw((*self.as_raw()).crtc) } } + + /// Returns whether or not the CRTC is active in this atomic state. + fn active(&self) -> bool { + // SAFETY: `active` and the rest of its containing bitfield can only be modified from the + // atomic check context, and are invariant beyond that point - so our interface can ensure + // this access is serialized + unsafe { (*self.as_raw()).active } + } } impl RawCrtcState for T {} From patchwork Mon Sep 30 23:10:08 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817278 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 0FF21CEB2CD for ; Mon, 30 Sep 2024 23:40:47 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 7D30710E5A2; Mon, 30 Sep 2024 23:40:46 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="hsWXC2TC"; 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 F218710E5A2 for ; Mon, 30 Sep 2024 23:40:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739644; 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=OPpcmKbNeNbUSddRK0GJ79bGRqnGtk4IaTVQGxA5MmY=; b=hsWXC2TCaqMA9IhHR6pY97ISeMuNqJZhcImQwKqdiaysJ0wGdDEdSNpuKdAzE+pHbPOoQp 2MZSVW3wdMdvzecRZ6J2l13ihYaDGq2ZF6xcEYl/moHm3OSdfg5ojQWUDEP9QWwKBlOL8k kOZqCAw28W3TUoNnkaTjkGX7GJ9yjvc= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-477-f4W_Q1TUOOinP8t2E5kuOw-1; Mon, 30 Sep 2024 19:40:40 -0400 X-MC-Unique: f4W_Q1TUOOinP8t2E5kuOw-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id DC66F191961D; Mon, 30 Sep 2024 23:40:37 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id A11393004AB3; Mon, 30 Sep 2024 23:40:29 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 25/35] rust: drm/kms: Add RawPlaneState::crtc() Date: Mon, 30 Sep 2024 19:10:08 -0400 Message-ID: <20240930233257.1189730-26-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" Add a binding for checking drm_plane_state.crtc. Note that we don't have a way of knowing what DriverCrtc implementation would be used here (and want to make this function also available on OpaquePlaneState types), so we return an OpaqueCrtc. Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/plane.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs index 04f1bdfbb1ea2..4d16d53179fca 100644 --- a/rust/kernel/drm/kms/plane.rs +++ b/rust/kernel/drm/kms/plane.rs @@ -30,6 +30,7 @@ ModeObject, StaticModeObject, atomic::*, + crtc::*, }; /// The main trait for implementing the [`struct drm_plane`] API for [`Plane`] @@ -489,6 +490,12 @@ fn plane(&self) -> &Self::Plane { // invariant throughout the lifetime of the Plane unsafe { Self::Plane::from_raw(self.as_raw().plane) } } + + /// Return the current [`OpaqueCrtc`] assigned to this plane, if there is one. + fn crtc<'a, 'b: 'a>(&'a self) -> Option<&'b OpaqueCrtc<::Driver>> { + // SAFETY: This cast is guaranteed safe by `OpaqueCrtc`s invariants. + NonNull::new(self.as_raw().crtc).map(|c| unsafe { OpaqueCrtc::from_raw(c.as_ptr()) }) + } } impl RawPlaneState for T {} From patchwork Mon Sep 30 23:10:09 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817279 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 B886DCEB2CD for ; Mon, 30 Sep 2024 23:40:53 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 3A25F10E5A4; Mon, 30 Sep 2024 23:40:53 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="ZAJPtupn"; 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 3B40F10E5A3 for ; Mon, 30 Sep 2024 23:40:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739651; 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=r65mS9fGP6QHa8ZJthkFLot5TCFWUe3W7tgNCeGtS+Y=; b=ZAJPtupnGZIRn6kFb/hSnqk6BaDeDJx0PWg2dwPEzWAx1FWjI+Wx3ZJQIMknSvS+GUFA7Z h5+n3biRPbbwZ6JVYrwFbsOZQba1Qquk1Con8O1nNJqSOJYz2FlcClvSBDA+FoYg1hi2Mo DvkKCRx96m811MaDY9YaUE4uC0fjh5U= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-375-YGuzUkxzNLKTlLsoR2qN7w-1; Mon, 30 Sep 2024 19:40:47 -0400 X-MC-Unique: YGuzUkxzNLKTlLsoR2qN7w-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id EC143196A433; Mon, 30 Sep 2024 23:40:44 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id C03833003DEC; Mon, 30 Sep 2024 23:40:40 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 26/35] WIP: rust: drm/kms: Add RawPlaneState::atomic_helper_check() Date: Mon, 30 Sep 2024 19:10:09 -0400 Message-ID: <20240930233257.1189730-27-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" Add a binding for drm_atomic_helper_check_plane_state(). Since we want to make sure that the user is passing in the new state for a Crtc instead of an old state, we explicitly ask for a reference to a BorrowedCrtcState. Signed-off-by: Lyude Paul --- TODO: * Add support for scaling options Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/plane.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs index 4d16d53179fca..cd5167e6441f1 100644 --- a/rust/kernel/drm/kms/plane.rs +++ b/rust/kernel/drm/kms/plane.rs @@ -496,6 +496,31 @@ fn crtc<'a, 'b: 'a>(&'a self) -> Option<&'b OpaqueCrtc<( + &mut self, + crtc_state: &BorrowedCrtcState<'_, S>, + can_position: bool, + can_update_disabled: bool + ) -> Result + where + S: FromRawCrtcState, + S::Crtc: AsRawCrtc::Driver> + { + // SAFETY: We're passing the mutable reference from `self.as_raw_mut()` directly to DRM, + // which is safe. + to_result(unsafe { + bindings::drm_atomic_helper_check_plane_state( + self.as_raw_mut(), + crtc_state.as_raw(), + bindings::DRM_PLANE_NO_SCALING as _, // TODO: add parameters for scaling + bindings::DRM_PLANE_NO_SCALING as _, + can_position, + can_update_disabled + ) + }) + } } impl RawPlaneState for T {} From patchwork Mon Sep 30 23:10:10 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817280 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 D2F06CEB2CD for ; Mon, 30 Sep 2024 23:41:04 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 5DF6D10E5A3; Mon, 30 Sep 2024 23:41:04 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="X+J+XuJZ"; 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 9EF4510E5A5 for ; Mon, 30 Sep 2024 23:41:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739661; 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=JS1c0OCCFpRlXiQX91H1LkrlyJCKWK3k0GLCh/QD2fY=; b=X+J+XuJZbyp6oUJg++dE3ETdEYznByeiitLdjYbJTsY8YDJFGkmpLieXdX32ProahJD12Y fw7MxTHDE18VEHt3gbIHcoNtEjywNB7xlXHUjTOc2YYQuREXw0dS9RagZq19Ven2DrAM7m +ZyzHMrq4xaXNHC2b9Ripq41uJNUW6I= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-570-mojEH0EhNHi2ABqV4vN0Nw-1; Mon, 30 Sep 2024 19:40:56 -0400 X-MC-Unique: mojEH0EhNHi2ABqV4vN0Nw-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id A954819626FA; Mon, 30 Sep 2024 23:40:53 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 3E4333003DEC; Mon, 30 Sep 2024 23:40:49 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , Danilo Krummrich , Mika Westerberg , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 27/35] rust: drm/kms: Add drm_framebuffer bindings Date: Mon, 30 Sep 2024 19:10:10 -0400 Message-ID: <20240930233257.1189730-28-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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 adds some very simple bindings for drm_framebuffer. We don't use them much yet, but we'll eventually be using them when rvkms eventually gets CRC and writeback support. Just like Connector objects, these use RcModeObject. Signed-off-by: Lyude Paul --- rust/bindings/bindings_helper.h | 1 + rust/kernel/drm/kms.rs | 1 + rust/kernel/drm/kms/framebuffer.rs | 58 ++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 rust/kernel/drm/kms/framebuffer.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 7695f11f4363c..7c324003c3885 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs index 4ab039d67352e..3edd90bc0025a 100644 --- a/rust/kernel/drm/kms.rs +++ b/rust/kernel/drm/kms.rs @@ -7,6 +7,7 @@ pub mod crtc; pub mod encoder; pub mod fbdev; +pub mod framebuffer; pub mod plane; use crate::{ diff --git a/rust/kernel/drm/kms/framebuffer.rs b/rust/kernel/drm/kms/framebuffer.rs new file mode 100644 index 0000000000000..bbe408c187670 --- /dev/null +++ b/rust/kernel/drm/kms/framebuffer.rs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +//! DRM Framebuffer related bindings + +use crate::{ + drm::{ + device::Device, + fourcc::*, + }, + types::*, + prelude::*, +}; +use bindings; +use core::{ + marker::*, + slice, +}; +use super::{ModeObject, RcModeObject, KmsDriver}; + +/// The main interface for [`struct drm_framebuffer`]. +/// +/// [`struct drm_framebuffer`]: srctree/include/drm/drm_framebuffer.h +#[repr(transparent)] +pub struct Framebuffer { + inner: Opaque, + _p: PhantomData, +} + +impl ModeObject for Framebuffer { + type Driver = T; + + fn drm_dev(&self) -> &Device { + unsafe { Device::borrow((*self.inner.get()).dev) } + } + + fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object { + // SAFETY: We don't expose Framebuffer to users before it's initialized, so `base` is + // always initialized + unsafe { &mut (*self.inner.get()).base } + } +} + +// SAFETY: Framebuffers are refcounted mode objects. +unsafe impl RcModeObject for Framebuffer {} + +// SAFETY: References to framebuffers are safe to be accessed from any thread +unsafe impl Send for Framebuffer {} +// SAFETY: References to framebuffers are safe to be accessed from any thread +unsafe impl Sync for Framebuffer {} + +impl crate::private::Sealed for Framebuffer {} + +impl Framebuffer { + #[inline] + pub(super) unsafe fn from_raw<'a>(ptr: *const bindings::drm_framebuffer) -> &'a Self { + // SAFETY: Our data layout is identical to drm_framebuffer + unsafe { &*ptr.cast() } + } +} From patchwork Mon Sep 30 23:10:11 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817281 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 DC3BACEB2CB for ; Mon, 30 Sep 2024 23:41:08 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 5F70010E5A5; Mon, 30 Sep 2024 23:41:08 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="THdhzqhA"; 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 C427710E5A5 for ; Mon, 30 Sep 2024 23:41:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739666; 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=XAGxt+7QcVJP+8MJM9swQ0ElPs58C/YHAc6dcv4hB6c=; b=THdhzqhAUckIViLBCBLG1mIZaKwxllWAlavqC6MNTYjZuJxfMKGAZjDHOkbOJnEx5TtPIj y/AAK3XT77vm2aY3St7IwL2MdEJTQT9EGY3ehgFqbuztozBQkwLnxqjYlRAMpJeKQXE3Gp puyzar8LzArR6YGTTI8JASYoeUvdtCQ= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-427-T8aDwVKTNQu0bBm4d0eZrg-1; Mon, 30 Sep 2024 19:41:02 -0400 X-MC-Unique: T8aDwVKTNQu0bBm4d0eZrg-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 7FCB719192B1; Mon, 30 Sep 2024 23:41:00 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 7F3933003E4D; Mon, 30 Sep 2024 23:40:56 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 28/35] rust: drm/kms: Add RawPlane::framebuffer() Date: Mon, 30 Sep 2024 19:10:11 -0400 Message-ID: <20240930233257.1189730-29-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" Returns the Framebuffer currently assigned in an atomic plane state. Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/plane.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs index cd5167e6441f1..15efa53fda8d3 100644 --- a/rust/kernel/drm/kms/plane.rs +++ b/rust/kernel/drm/kms/plane.rs @@ -31,6 +31,7 @@ StaticModeObject, atomic::*, crtc::*, + framebuffer::*, }; /// The main trait for implementing the [`struct drm_plane`] API for [`Plane`] @@ -521,6 +522,13 @@ fn atomic_helper_check( ) }) } + + /// Return the framebuffer currently set for this plane state + #[inline] + fn framebuffer(&self) -> Option<&Framebuffer<::Driver>> { + // SAFETY: The layout of Framebuffer is identical to `fb` + unsafe { self.as_raw().fb.as_ref().map(|fb| Framebuffer::from_raw(fb)) } + } } impl RawPlaneState for T {} From patchwork Mon Sep 30 23:10:12 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817282 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 DEA8DCEB2CB for ; Mon, 30 Sep 2024 23:41:14 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 6750E10E5A7; Mon, 30 Sep 2024 23:41:14 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="eUK9edg5"; 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 426A210E5A7 for ; Mon, 30 Sep 2024 23:41:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739671; 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=Wwd9KyXQl0BylRT4eiU/cc5fZo+yn5dpVQ2DRtqGoyA=; b=eUK9edg5UixKjMmgrCDxUCtSQTz24Ts4j6ewnEx4cuoLblwyhoqy/jm6MYGQs4JD5dYTVi pYJAKTcoT3SSiOqQbYsxVj0mNHqN/vO133RP6nxZGb49Y4jmkaJ0/jGbNEtdXbJpbv0kSR fC5M2n1U2ojKNQkhfmw7d0rgcxCRWyE= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-684-ql9qTW94PYqZ09md1Du9_Q-1; Mon, 30 Sep 2024 19:41:09 -0400 X-MC-Unique: ql9qTW94PYqZ09md1Du9_Q-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 79796193E8CD; Mon, 30 Sep 2024 23:41:07 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 5DC423003DEC; Mon, 30 Sep 2024 23:41:03 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 29/35] rust: drm/kms: Add DriverCrtc::atomic_begin() and atomic_flush() Date: Mon, 30 Sep 2024 19:10:12 -0400 Message-ID: <20240930233257.1189730-30-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" Optional trait methods for implementing the atomic_begin and atomic_flush callbacks for a CRTC. Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/crtc.rs | 78 ++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs index ec9b58763dcca..a4e955364bd8c 100644 --- a/rust/kernel/drm/kms/crtc.rs +++ b/rust/kernel/drm/kms/crtc.rs @@ -90,8 +90,8 @@ pub trait DriverCrtc: Send + Sync + Sized { mode_set: None, mode_valid: None, mode_fixup: None, - atomic_begin: None, - atomic_flush: None, + atomic_begin: if Self::HAS_ATOMIC_BEGIN { Some(atomic_begin_callback::) } else { None }, + atomic_flush: if Self::HAS_ATOMIC_FLUSH { Some(atomic_flush_callback::) } else { None }, mode_set_nofb: None, mode_set_base: None, mode_set_base_atomic: None, @@ -132,6 +132,36 @@ fn atomic_check( ) -> Result { build_error::build_error("This should not be reachable") } + + /// The optional [`drm_crtc_helper_funcs.atomic_begin`] hook. + /// + /// This hook will be called before a set of [`Plane`] updates are performed for the given + /// [`Crtc`]. + /// + /// [`drm_crtc_helper_funcs.atomic_begin`]: srctree/include/drm/drm_modeset_helper_vtables.h + fn atomic_begin( + crtc: &Crtc, + old_state: &CrtcState, + new_state: BorrowedCrtcState<'_, CrtcState>, + state: &AtomicStateMutator + ) { + build_error::build_error("This should not be reachable") + } + + /// The optional [`drm_crtc_helper_funcs.atomic_flush`] hook. + /// + /// This hook will be called after a set of [`Plane`] updates are performed for the given + /// [`Crtc`]. + /// + /// [`drm_crtc_helper_funcs.atomic_flush`]: srctree/include/drm/drm_modeset_helper_vtables.h + fn atomic_flush( + crtc: &Crtc, + old_state: &CrtcState, + new_state: BorrowedCrtcState<'_, CrtcState>, + state: &AtomicStateMutator + ) { + build_error::build_error("This should never be reachable") + } } /// The generated C vtable for a [`DriverCrtc`]. @@ -776,3 +806,47 @@ fn as_raw(&self) -> *mut bindings::drm_crtc_state { Ok(0) }) } + +unsafe extern "C" fn atomic_begin_callback( + crtc: *mut bindings::drm_crtc, + state: *mut bindings::drm_atomic_state, +) { + // SAFETY: + // * We're guaranteed `crtc` is of type `Crtc` via type invariants. + // * We're guaranteed by DRM that `crtc` is pointing to a valid initialized state. + let crtc = unsafe { Crtc::from_raw(crtc) }; + + // SAFETY: We're guaranteed by DRM that `state` points to a valid instance of `drm_atomic_state` + let state = unsafe { AtomicStateMutator::new(NonNull::new_unchecked(state)) }; + + // SAFETY: We're guaranteed by DRM that both the old and new atomic state are present within + // this `drm_atomic_state` + let (old_state, new_state) = unsafe {( + state.get_old_crtc_state(crtc).unwrap_unchecked(), + state.get_new_crtc_state(crtc).unwrap_unchecked(), + )}; + + T::atomic_begin(crtc, old_state, new_state, &state); +} + +unsafe extern "C" fn atomic_flush_callback( + crtc: *mut bindings::drm_crtc, + state: *mut bindings::drm_atomic_state, +) { + // SAFETY: + // * We're guaranteed `crtc` is of type `Crtc` via type invariants. + // * We're guaranteed by DRM that `crtc` is pointing to a valid initialized state. + let crtc = unsafe { Crtc::from_raw(crtc) }; + + // SAFETY: We're guaranteed by DRM that `state` points to a valid instance of `drm_atomic_state` + let state = unsafe { AtomicStateMutator::new(NonNull::new_unchecked(state)) }; + + // SAFETY: We're in an atomic flush callback, so we know that both the new and old state are + // present + let (old_state, new_state) = unsafe {( + state.get_old_crtc_state(crtc).unwrap_unchecked(), + state.get_new_crtc_state(crtc).unwrap_unchecked(), + )}; + + T::atomic_flush(crtc, old_state, new_state, &state); +} From patchwork Mon Sep 30 23:10:13 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817283 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 EBF87CEB2CD for ; Mon, 30 Sep 2024 23:41:21 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 799F510E5A8; Mon, 30 Sep 2024 23:41:21 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="QalZnnmw"; 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 6DFF910E5A8 for ; Mon, 30 Sep 2024 23:41:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739679; 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=Hf2PwgqqoorfI7tgNpCFOlFnV5DzwsS0y6C7e13QCQQ=; b=QalZnnmw+f213HUQlvJoMt+dMkgcMB/K7ooPUMN40JDIpj8yckQ0ubEppYpJC5GRg7IL70 mI7tYyktrX4jVZmimmn92hJzANRVxiq8ba5PgG5RojgRegPlVQUtEF7geuL11MWEv6oN2f IflL4Uc1OBNkYMT4J2d52UmSHiaTgIE= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-617-_xPnOvVNMX-5Pb_SaZ2qGQ-1; Mon, 30 Sep 2024 19:41:16 -0400 X-MC-Unique: _xPnOvVNMX-5Pb_SaZ2qGQ-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 583F8196D18E; Mon, 30 Sep 2024 23:41:14 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 6EB443003DEC; Mon, 30 Sep 2024 23:41:10 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 30/35] rust: drm/kms: Add DriverCrtc::atomic_enable() and atomic_disable() Date: Mon, 30 Sep 2024 19:10:13 -0400 Message-ID: <20240930233257.1189730-31-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" Optional trait methods for implementing the atomic_enable and atomic_disable callbacks of a CRTC. Signed-off-by: Lyude Paul --- rust/kernel/drm/kms/crtc.rs | 76 ++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs index a4e955364bd8c..ef1b331f742d0 100644 --- a/rust/kernel/drm/kms/crtc.rs +++ b/rust/kernel/drm/kms/crtc.rs @@ -80,8 +80,8 @@ pub trait DriverCrtc: Send + Sync + Sized { }, helper_funcs: bindings::drm_crtc_helper_funcs { - atomic_disable: None, - atomic_enable: None, + atomic_disable: if Self::HAS_ATOMIC_DISABLE { Some(atomic_disable_callback::) } else { None }, + atomic_enable: if Self::HAS_ATOMIC_ENABLE { Some(atomic_enable_callback::) } else { None }, atomic_check: if Self::HAS_ATOMIC_CHECK { Some(atomic_check_callback::) } else { None }, dpms: None, commit: None, @@ -162,6 +162,34 @@ fn atomic_flush( ) { build_error::build_error("This should never be reachable") } + + /// The optional [`drm_crtc_helper_funcs.atomic_enable`] hook. + /// + /// This hook will be called before enabling a [`Crtc`] in an atomic commit. + /// + /// [`drm_crtc_helper_funcs.atomic_enable`]: srctree/include/drm/drm_modeset_helper_vtables.h + fn atomic_enable( + crtc: &Crtc, + old_state: &CrtcState, + new_state: BorrowedCrtcState<'_, CrtcState>, + state: &AtomicStateMutator + ) { + build_error::build_error("This should never be reachable") + } + + /// The optional [`drm_crtc_helper_funcs.atomic_disable`] hook. + /// + /// This hook will be called before disabling a [`Crtc`] in an atomic commit. + /// + /// [`drm_crtc_helper_funcs.atomic_disable`]: srctree/include/drm/drm_modeset_helper_vtables.h + fn atomic_disable( + crtc: &Crtc, + old_state: &CrtcState, + new_state: BorrowedCrtcState<'_, CrtcState>, + state: &AtomicStateMutator + ) { + build_error::build_error("This should never be reachable") + } } /// The generated C vtable for a [`DriverCrtc`]. @@ -850,3 +878,47 @@ fn as_raw(&self) -> *mut bindings::drm_crtc_state { T::atomic_flush(crtc, old_state, new_state, &state); } + +unsafe extern "C" fn atomic_enable_callback( + crtc: *mut bindings::drm_crtc, + state: *mut bindings::drm_atomic_state, +) { + // SAFETY: + // * We're guaranteed `crtc` is of type `Crtc` via type invariants. + // * We're guaranteed by DRM that `crtc` is pointing to a valid initialized state. + let crtc = unsafe { Crtc::from_raw(crtc) }; + + // SAFETY: DRM never passes an invalid ptr for `state` + let state = unsafe { AtomicStateMutator::new(NonNull::new_unchecked(state)) }; + + // SAFETY: We're in an atomic enable callback, so we know that both the new and old state are + // present + let (old_state, new_state) = unsafe {( + state.get_old_crtc_state(crtc).unwrap_unchecked(), + state.get_new_crtc_state(crtc).unwrap_unchecked(), + )}; + + T::atomic_enable(crtc, old_state, new_state, &state); +} + +unsafe extern "C" fn atomic_disable_callback( + crtc: *mut bindings::drm_crtc, + state: *mut bindings::drm_atomic_state, +) { + // SAFETY: + // * We're guaranteed `crtc` points to a valid instance of `drm_crtc` + // * We're guaranteed that `crtc` is of type `Plane` by `DriverPlane`s type invariants. + let crtc = unsafe { Crtc::from_raw(crtc) }; + + // SAFETY: We're guaranteed that `state` points to a valid `drm_crtc_state` by DRM + let state = unsafe { AtomicStateMutator::new(NonNull::new_unchecked(state)) }; + + // SAFETY: We're in an atomic commit callback, so we know that both the new and old state are + // present + let (old_state, new_state) = unsafe {( + state.get_old_crtc_state(crtc).unwrap_unchecked(), + state.get_new_crtc_state(crtc).unwrap_unchecked(), + )}; + + T::atomic_disable(crtc, old_state, new_state, &state); +} From patchwork Mon Sep 30 23:10:14 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817284 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 BB009CEB2CD for ; Mon, 30 Sep 2024 23:41:28 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 38A3B10E5A9; Mon, 30 Sep 2024 23:41:28 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="d5RkNiXC"; 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 098E710E5A9 for ; Mon, 30 Sep 2024 23:41:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739686; 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=xhLeLvV27GzVDMIsND1hL5s5tE+smtYvQODvZdGLp1k=; b=d5RkNiXCjA6yEhZI6EmQCcKs05346j94IThos2kAb0a5fsl46/EhiVVITRLdn48EvVxSCe SI5fiWTtkEAkm/PgJLie6jLzVRXvK9eQq8zDvot7dBxc9FNVEgSN/GCFJRgqyvaUl5GLrR FqhmQX/98vnbV4VUowi4p0vBwd1sgas= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-450-JvvOGLywPfqNFpiwe3YSlg-1; Mon, 30 Sep 2024 19:41:22 -0400 X-MC-Unique: JvvOGLywPfqNFpiwe3YSlg-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 800D1194511A; Mon, 30 Sep 2024 23:41:20 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id AA03A3003DEC; Mon, 30 Sep 2024 23:41:16 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , Danilo Krummrich , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 31/35] rust: drm: Add Device::event_lock() Date: Mon, 30 Sep 2024 19:10:14 -0400 Message-ID: <20240930233257.1189730-32-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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 is just a crate-private helper to use Lock::from_raw() to provide an immutable reference to the DRM event_lock, so that it can be used like a normal rust spinlock. We'll need this for adding vblank related bindings. Signed-off-by: Lyude Paul --- rust/kernel/drm/device.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs index d4d6b1185f6a6..207e7ea87cf8f 100644 --- a/rust/kernel/drm/device.rs +++ b/rust/kernel/drm/device.rs @@ -15,6 +15,7 @@ error::from_err_ptr, error::Result, types::{ARef, AlwaysRefCounted, ForeignOwnable, Opaque}, + sync::*, }; use core::{ ffi::c_void, @@ -159,6 +160,12 @@ pub fn data(&self) -> ::Borrowed<'_> { unsafe { ::from_foreign(drm.raw_data()) }; } + /// Returns a reference to the `event` spinlock + pub(crate) fn event_lock(&self) -> &SpinLockIrq<()> { + // SAFETY: `event_lock` is initialized for as long as `self` is exposed to users + unsafe { SpinLockIrq::from_raw(&mut (*self.as_raw()).event_lock) } + } + pub(crate) const fn has_kms() -> bool { ::MODE_CONFIG_OPS.is_some() } From patchwork Mon Sep 30 23:10:15 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817285 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 BBF3ECEB2CB for ; Mon, 30 Sep 2024 23:41:39 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 4940110E5AB; Mon, 30 Sep 2024 23:41:39 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="PTowA6Wa"; 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 0482E10E5AB for ; Mon, 30 Sep 2024 23:41:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739697; 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=7t/iPh52Or/uRW+fztwWU5+XnAVyTy6rcl2tUGiZv4A=; b=PTowA6Wav4fccU0ARyTItRjaNdPNH+N4nqzl1oHcMunuidCbuK0kVWlIN80EqrDr36OIEP 0Syr+eLak5VshFODQYpl95zoFqybvduUvBi0XMKKwZ0BbEOdLV+YN6H2ZwJbde3E3C8dqd MfH5WqPa6qlHbmjnHpDSNBsKnr11yzU= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-3-OWGYhbbOPTi0_jOaBzYfLg-1; Mon, 30 Sep 2024 19:41:35 -0400 X-MC-Unique: OWGYhbbOPTi0_jOaBzYfLg-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 3A46919192AC; Mon, 30 Sep 2024 23:41:27 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id E539D3003DEC; Mon, 30 Sep 2024 23:41:22 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 32/35] rust: drm/kms: Add Device::num_crtcs() Date: Mon, 30 Sep 2024 19:10:15 -0400 Message-ID: <20240930233257.1189730-33-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" A binding for checking drm_device.num_crtcs. We'll need this in a moment for vblank support, since setting it up requires knowing the number of CRTCs that a driver has initialized. Signed-off-by: Lyude Paul --- rust/kernel/drm/kms.rs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs index 3edd90bc0025a..d0745b44ba9b6 100644 --- a/rust/kernel/drm/kms.rs +++ b/rust/kernel/drm/kms.rs @@ -253,10 +253,27 @@ pub fn mode_config_lock(&self) -> ModeConfigGuard<'_, T> { /// Return the number of registered [`Plane`](plane::Plane) objects on this [`Device`]. #[inline] - pub fn num_plane(&self) -> i32 { - // SAFETY: The only context which this could change is before registration, which must be - // single-threaded anyway - so it's safe to just read this value - unsafe { (*self.as_raw()).mode_config.num_total_plane } + pub fn num_plane(&self) -> u32 { + // SAFETY: + // * This can only be modified during the single-threaded context before registration, so + // this is safe + // * num_total_plane could be >= 0, but no less - so casting to u32 is fine (and better to + // prevent errors) + unsafe { (*self.as_raw()).mode_config.num_total_plane as u32 } + } + + /// Return the number of registered CRTCs + /// TODO: while `num_crtc` is of i32, that type actually makes literally no sense here and just + /// causes problems and unecessary casts. Same for num_plane(). So, fix that at some point (we + /// will never get n < 0 anyway) + #[inline] + pub fn num_crtcs(&self) -> u32 { + // SAFETY: + // * This can only be modified during the single-threaded context before registration, so + // this is safe + // * num_crtc could be >= 0, but no less - so casting to u32 is fine (and better to prevent + // errors) + unsafe { (*self.as_raw()).mode_config.num_crtc as u32 } } } From patchwork Mon Sep 30 23:10:16 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817286 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 87CFCCEB2CD for ; Mon, 30 Sep 2024 23:41:49 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 13BD810E5AD; Mon, 30 Sep 2024 23:41:49 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="PlJ46QGA"; 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 9190C10E5AC for ; Mon, 30 Sep 2024 23:41:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739706; 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=hITimVXnjHjjr9o0HF6Jy1biV6iCDZf6yj0L2qZFPDM=; b=PlJ46QGAzqxY3M96lch1jHK74QfKMXYvZaynle8myyivpj0nDRwtRI9TFxZMOWSaKOt7Nk YcLxrAfXmVazF2LWwzo6TzbhASh5kJS7a/JXELvB6DgvIn6kyYB1C6ZTwS/KEETUHksleC YhFNM8QqZFPiwjZSGkvzvYYDVNiuKrE= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-373-fZxbSOUZNNWtdz8XhUXiaw-1; Mon, 30 Sep 2024 19:41:43 -0400 X-MC-Unique: fZxbSOUZNNWtdz8XhUXiaw-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id D951719192AB; Mon, 30 Sep 2024 23:41:40 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 6F9C43003DEC; Mon, 30 Sep 2024 23:41:36 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , Danilo Krummrich , Mika Westerberg , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 33/35] WIP: rust: drm/kms: Add VblankSupport Date: Mon, 30 Sep 2024 19:10:16 -0400 Message-ID: <20240930233257.1189730-34-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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 commit adds bindings for implementing vblank support for a driver's CRTCs. These bindings are optional, to account for the fact that not all drivers have dedicated hardware vblanks. In order to accomplish this, we introduce the VblankSupport trait which can be implemented on DriverCrtc by drivers which support vblanks. This works in the same way as the main Kms trait - drivers which don't support hardware vblanks can simply pass PhantomData to the associated type on DriverCrtc. If a driver chooses to implement VblankSupport, VblankImpl will be implemented by DRM automatically - and can be passed to the VblankImpl associated type on DriverCrtc. Additionally, we gate methods which only apply to vblank-supporting drivers by introducing a VblankDriverCrtc trait that is automatically implemented by DRM for CRTC drivers implementing VblankSupport. This works basically in the same way as Kms and KmsDriver, but for CRTCs. Signed-off-by: Lyude Paul --- Notes: * One thing to keep in mind: this trait is implemented on the CRTC as opposed to the KMS driver due to the possibility that a driver may have multiple different types of CRTCs. As a result, it's not impossible that there could potentially be differences in each type's vblank hardware implementation. In theory this could lead to a driver mistakenly only implementing VblankSupport for some CRTCs and not others, which isn't really defined behavior in DRM. As such, one of the dependencies in the branch for this patch series preview is a fix to ensure that DRM disallows registering drivers that make this mistake. TODO: * Technically this patch introduces a soundness issue. We currently allow access to a kernel::drm::Device (through UnregisteredKmsDevice's Deref implementation) within the kernel::drm::kms::Kms::create_objects trait method. A caller could potentially access an uninitialized mutex by calling Crtc::vblank_lock() within this context. My solution for this is likely going to be adding unregistered variants of Crtc and other mode-objects that don't have access to the full set of methods on mode objects. Signed-off-by: Lyude Paul --- rust/bindings/bindings_helper.h | 1 + rust/helpers/drm/drm.c | 2 + rust/helpers/drm/vblank.c | 8 + rust/kernel/drm/kms.rs | 20 +- rust/kernel/drm/kms/crtc.rs | 28 +- rust/kernel/drm/kms/vblank.rs | 454 ++++++++++++++++++++++++++++++++ 6 files changed, 505 insertions(+), 8 deletions(-) create mode 100644 rust/helpers/drm/vblank.c create mode 100644 rust/kernel/drm/kms/vblank.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 7c324003c3885..191eb24b32234 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/drm/drm.c b/rust/helpers/drm/drm.c index 365f6807774d4..5d4498e01fd3e 100644 --- a/rust/helpers/drm/drm.c +++ b/rust/helpers/drm/drm.c @@ -2,7 +2,9 @@ #ifdef CONFIG_DRM_KMS_HELPER #include "atomic.c" +#include "vblank.c" #endif + #include "gem.c" #ifdef CONFIG_DRM_GEM_SHMEM_HELPER #include "gem_shmem_helper.c" diff --git a/rust/helpers/drm/vblank.c b/rust/helpers/drm/vblank.c new file mode 100644 index 0000000000000..165db7ac5b4da --- /dev/null +++ b/rust/helpers/drm/vblank.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +struct drm_vblank_crtc *rust_helper_drm_crtc_vblank_crtc(struct drm_crtc *crtc) +{ + return drm_crtc_vblank_crtc(crtc); +} diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs index d0745b44ba9b6..e13f35d9e223f 100644 --- a/rust/kernel/drm/kms.rs +++ b/rust/kernel/drm/kms.rs @@ -9,6 +9,7 @@ pub mod fbdev; pub mod framebuffer; pub mod plane; +pub mod vblank; use crate::{ drm::{ @@ -28,6 +29,7 @@ ptr::{self, NonNull}, mem::{self, ManuallyDrop}, marker::PhantomData, + cell::Cell, }; use bindings; @@ -84,6 +86,13 @@ unsafe fn setup_fbdev(drm: &Device, mode_config_info: &ModeConfigI /// state required during the initialization process of a [`Device`]. pub struct UnregisteredKmsDevice<'a, T: Driver> { drm: &'a Device, + // TODO: Get rid of this - I think the solution we came up on the C side to just make it so that + // DRM is a bit more consistent with verifying whether all CRTCs have this implemented or not - + // meaning we don't need to keep track of this and can just make the vblank setup conditional on + // the implementation of `VblankSupport`. + // Note that this also applies to headless devices - those are literally the same but + // `dev.num_crtc()` = 0 + pub(crate) has_vblanks: Cell, } impl<'a, T: Driver> Deref for UnregisteredKmsDevice<'a, T> { @@ -103,6 +112,7 @@ impl<'a, T: Driver> UnregisteredKmsDevice<'a, T> { pub(crate) unsafe fn new(drm: &'a Device) -> Self { Self { drm, + has_vblanks: Cell::new(false) } } } @@ -190,6 +200,11 @@ unsafe fn setup_kms(drm: &Device) -> Result { T::create_objects(&drm)?; + if drm.has_vblanks.get() { + // SAFETY: `has_vblank` is only true if CRTCs with vblank support were registered + to_result(unsafe { bindings::drm_vblank_init(drm.as_raw(), drm.num_crtcs()) })?; + } + // TODO: Eventually add a hook to customize how state readback happens, for now just reset // SAFETY: Since all static modesetting objects were created in `T::create_objects()`, and // that is the only place they can be created, this fulfills the C API requirements. @@ -262,10 +277,7 @@ pub fn num_plane(&self) -> u32 { unsafe { (*self.as_raw()).mode_config.num_total_plane as u32 } } - /// Return the number of registered CRTCs - /// TODO: while `num_crtc` is of i32, that type actually makes literally no sense here and just - /// causes problems and unecessary casts. Same for num_plane(). So, fix that at some point (we - /// will never get n < 0 anyway) + /// Return the number of registered [`Crtc`](crtc::Crtc) objects on this [`Device`]. #[inline] pub fn num_crtcs(&self) -> u32 { // SAFETY: diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs index ef1b331f742d0..74e7b666d828c 100644 --- a/rust/kernel/drm/kms/crtc.rs +++ b/rust/kernel/drm/kms/crtc.rs @@ -8,7 +8,8 @@ ModeObject, StaticModeObject, KmsDriver, - UnregisteredKmsDevice + UnregisteredKmsDevice, + vblank::* }; use crate::{ bindings, @@ -62,13 +63,13 @@ pub trait DriverCrtc: Send + Sync + Sized { cursor_set2: None, cursor_set: None, destroy: Some(crtc_destroy_callback::), - disable_vblank: None, + disable_vblank: ::VBLANK_OPS.disable_vblank, early_unregister: None, - enable_vblank: None, + enable_vblank: ::VBLANK_OPS.enable_vblank, gamma_set: None, // TODO get_crc_sources: None, get_vblank_counter: None, - get_vblank_timestamp: None, + get_vblank_timestamp: ::VBLANK_OPS.get_vblank_timestamp, late_register: None, page_flip: Some(bindings::drm_atomic_helper_page_flip), page_flip_target: None, @@ -113,6 +114,12 @@ pub trait DriverCrtc: Send + Sync + Sized { /// See [`DriverCrtcState`] for more info. type State: DriverCrtcState; + /// The driver's optional hardware vblank implementation + /// + /// See [`VblankSupport`] for more info. Drivers that don't care about this can just pass + /// [`PhantomData`]. + type VblankImpl: VblankImpl; + /// The constructor for creating a [`Crtc`] using this [`DriverCrtc`] implementation. /// /// Drivers may use this to instantiate their [`DriverCrtc`] object. @@ -281,6 +288,10 @@ pub fn new<'a, 'b: 'a, P, C>( P: DriverPlane, C: DriverPlane { + if Self::has_vblank() { + dev.has_vblanks.set(true) + } + let this = Box::try_pin_init( try_pin_init!(Self { crtc: Opaque::new(bindings::drm_crtc { @@ -343,6 +354,15 @@ pub fn from_opaque<'a, D>(opaque: &'a OpaqueCrtc) -> &'a Self Self::try_from_opaque(opaque) .expect("Passed OpaqueCrtc does not share this DriverCrtc implementation") } + + pub(crate) fn get_vblank_ptr(&self) -> *mut bindings::drm_vblank_crtc { + // SAFETY: FFI Call with no special requirements + unsafe { bindings::drm_crtc_vblank_crtc(self.as_raw()) } + } + + pub(crate) const fn has_vblank() -> bool { + T::OPS.funcs.enable_vblank.is_some() + } } /// A trait implemented by any type that acts as a [`struct drm_crtc`] interface. diff --git a/rust/kernel/drm/kms/vblank.rs b/rust/kernel/drm/kms/vblank.rs new file mode 100644 index 0000000000000..29dce38053a49 --- /dev/null +++ b/rust/kernel/drm/kms/vblank.rs @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +//! Rust bindings for KMS vblank support + +use core::{ + marker::*, + mem::{self, ManuallyDrop}, + ops::{Drop, Deref}, + ptr::{self, NonNull, null_mut} +}; +use kernel::{ + time::Ktime, + types::Opaque, + error::{from_result, to_result}, + prelude::*, + drm::device::Device, + irq::* +}; +use super::{ + crtc::*, + KmsRef, + ModeObject, + KmsDriver, +}; +use bindings; + +/// The main trait for a driver to implement hardware vblank support for a [`Crtc`]. +/// +/// # Invariants +/// +/// C FFI callbacks generated using this trait can safely assume that input pointers to +/// [`struct drm_crtc`] are always contained within a [`Crtc`]. +/// +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h +pub trait VblankSupport: Sized { + /// The parent [`DriverCrtc`]. + type Crtc: VblankDriverCrtc; + + /// Enable vblank interrupts for this [`DriverCrtc`]. + fn enable_vblank( + crtc: &Crtc, + vblank_guard: &VblankGuard<'_, Self::Crtc>, + irq: IrqDisabled<'_> + ) -> Result; + + /// Disable vblank interrupts for this [`DriverCrtc`]. + fn disable_vblank( + crtc: &Crtc, + vblank_guard: &VblankGuard<'_, Self::Crtc>, + irq: IrqDisabled<'_> + ); + + /// Retrieve the current vblank timestamp for this [`Crtc`] + /// + /// If this function is being called from the driver's vblank interrupt handler, + /// `handling_vblank_irq` will be [`Some`]. + fn get_vblank_timestamp( + crtc: &Crtc, + in_vblank_irq: bool, + ) -> Option; +} + +/// Trait used for CRTC vblank (or lack there-of) implementations. Implemented internally. +/// +/// Drivers interested in implementing vblank support should refer to [`VblankSupport`], drivers +/// that don't have vblank support can use [`PhantomData`]. +pub trait VblankImpl { + /// The parent [`DriverCrtc`]. + type Crtc: DriverCrtc; + + /// The generated [`VblankOps`]. + const VBLANK_OPS: VblankOps; +} + +/// C FFI callbacks for vblank management. +/// +/// Created internally by DRM. +#[derive(Default)] +pub struct VblankOps { + pub(crate) enable_vblank: Option i32>, + pub(crate) disable_vblank: Option, + pub(crate) get_vblank_timestamp: Option< + unsafe extern "C" fn( + crtc: *mut bindings::drm_crtc, + max_error: *mut i32, + vblank_time: *mut bindings::ktime_t, + in_vblank_irq: bool, + ) -> bool + > +} + +impl VblankImpl for T { + type Crtc = T::Crtc; + + const VBLANK_OPS: VblankOps = VblankOps { + enable_vblank: Some(enable_vblank_callback::), + disable_vblank: Some(disable_vblank_callback::), + get_vblank_timestamp: Some(get_vblank_timestamp_callback::) + }; +} + +impl VblankImpl for PhantomData +where + T: DriverCrtc> +{ + type Crtc = T; + + const VBLANK_OPS: VblankOps = VblankOps { + enable_vblank: None, + disable_vblank: None, + get_vblank_timestamp: None, + }; +} + +unsafe extern "C" fn enable_vblank_callback( + crtc: *mut bindings::drm_crtc, +) -> i32 { + // SAFETY: We're guaranteed that `crtc` is of type `Crtc` by type invariants. + let crtc = unsafe { Crtc::::from_raw(crtc) }; + + // SAFETY: This callback happens with IRQs disabled + let irq = unsafe { IrqDisabled::new() }; + + // SAFETY: This callback happens with `vbl_lock` already held + let vblank_guard = ManuallyDrop::new(unsafe { VblankGuard::new(crtc, irq) }); + + from_result(|| T::enable_vblank(crtc, &vblank_guard, irq).map(|_| 0)) +} + +unsafe extern "C" fn disable_vblank_callback( + crtc: *mut bindings::drm_crtc, +) { + // SAFETY: We're guaranteed that `crtc` is of type `Crtc` by type invariants. + let crtc = unsafe { Crtc::::from_raw(crtc) }; + + // SAFETY: This callback happens with IRQs disabled + let irq = unsafe { IrqDisabled::new() }; + + // SAFETY: This call happens with `vbl_lock` already held + let vblank_guard = ManuallyDrop::new(unsafe { VblankGuard::new(crtc, irq) }); + + T::disable_vblank(crtc, &vblank_guard, irq); +} + +unsafe extern "C" fn get_vblank_timestamp_callback( + crtc: *mut bindings::drm_crtc, + max_error: *mut i32, + vblank_time: *mut bindings::ktime_t, + in_vblank_irq: bool +) -> bool { + // SAFETY: We're guaranteed `crtc` is of type `Crtc` by type invariance + let crtc = unsafe { Crtc::::from_raw(crtc) }; + + if let Some(timestamp) = T::get_vblank_timestamp(crtc, in_vblank_irq) { + // SAFETY: Both of these pointers are guaranteed by the C API to be valid + unsafe { + (*max_error) = timestamp.max_error; + (*vblank_time) = timestamp.time.to_ns(); + }; + + true + } else { + false + } +} + +/// A vblank timestamp. +/// +/// This type is used by [`VblankSupport::get_vblank_timestamp`] for the implementor to return the +/// current vblank timestamp for the hardware. +#[derive(Copy, Clone)] +pub struct VblankTimestamp { + /// The actual vblank timestamp, accuracy to within [`Self::max_error`] nanoseconds + pub time: Ktime, + + /// Maximum allowable timestamp error in nanoseconds + pub max_error: i32, +} + +/// A trait for [`DriverCrtc`] implementations with hardware vblank support. +/// +/// This trait is implemented internally by DRM for any [`DriverCrtc`] implementation that +/// implements [`VblankSupport`]. It is used to expose hardware-vblank driver exclusive methods and +/// data to users. +pub trait VblankDriverCrtc: DriverCrtc {} + +impl VblankDriverCrtc for T +where + T: DriverCrtc, + V: VblankSupport {} + +impl Crtc { + /// Retrieve a reference to the [`VblankCrtc`] for this [`Crtc`]. + pub(crate) fn vblank_crtc(&self) -> &VblankCrtc { + // SAFETY: Casting is safe via `VblankCrtc`s type invariants. + unsafe { &*self.get_vblank_ptr().cast() } + } + + /// Access vblank related infrastructure for a [`Crtc`]. + /// + /// This function explicitly locks the device's vblank lock, and allows access to controlling + /// the vblank configuration for this CRTC. The lock is dropped once [`VblankGuard`] is + /// dropped. + pub fn vblank_lock<'a>(&'a self, irq: IrqDisabled<'a>) -> VblankGuard<'a, T> { + // SAFETY: `vbl_lock` is initialized for as long as `Crtc` is available to users + unsafe { bindings::spin_lock(&mut (*self.drm_dev().as_raw()).vbl_lock) }; + + // SAFETY: We just acquired vbl_lock above + unsafe { VblankGuard::new(self, irq) } + } + + /// Trigger a vblank event on this [`Crtc`]. + /// + /// Drivers should use this in their vblank interrupt handlers to update the vblank counter and + /// send any signals that may be pending. + /// + /// Returns whether or not the vblank event was handled. + #[inline] + pub fn handle_vblank(&self) -> bool { + // SAFETY: FFI call with no special requirements + unsafe { bindings::drm_crtc_handle_vblank(self.as_raw()) } + } + + /// Forbid vblank events for a [`Crtc`]. + /// + /// This function disables vblank events for a [`Crtc`], even if [`VblankRef`] objects exist. + #[inline] + pub fn vblank_off(&self) { + // SAFETY: FFI call with no special requirements + unsafe { bindings::drm_crtc_vblank_off(self.as_raw()) } + } + + /// Allow vblank events for a [`Crtc`]. + /// + /// This function allows users to enable vblank events and acquire [`VblankRef`] objects again. + #[inline] + pub fn vblank_on(&self) { + // SAFETY: FFI call with no special requirements + unsafe { bindings::drm_crtc_vblank_on(self.as_raw()) } + } + + /// Enable vblank events for a [`Crtc`]. + /// + /// Returns a [`VblankRef`] which will allow vblank events to be sent until it is dropped. Note + /// that vblank events may still be disabled by [`Self::vblank_off`]. + #[must_use = "Vblanks are only enabled until the result from this function is dropped"] + pub fn vblank_get(&self) -> Result> { + VblankRef::new(self) + } +} + +/// Common methods available on any [`CrtcState`] whose [`Crtc`] implements [`VblankSupport`]. +/// +/// This trait is implemented automatically by DRM for any [`DriverCrtc`] implementation that +/// implements [`VblankSupport`]. +pub trait RawVblankCrtcState: AsRawCrtcState { + /// Return the [`PendingVblankEvent`] for this CRTC state, if there is one. + fn get_pending_vblank_event(&mut self) -> Option> + where + Self: Sized, + { + // SAFETY: The driver is the only one that will ever modify this data, and since our + // interface follows rust's data aliasing rules that means this is safe to read + let event_ptr = unsafe { (*self.as_raw()).event }; + + (!event_ptr.is_null()).then_some(PendingVblankEvent(self)) + } +} + +impl RawVblankCrtcState for T +where + T: AsRawCrtcState>, + C: VblankDriverCrtc {} + +/// A pending vblank event from an atomic state +pub struct PendingVblankEvent<'a, T: RawVblankCrtcState>(&'a mut T); + +impl<'a, T: RawVblankCrtcState> PendingVblankEvent<'a, T> { + /// Send this [`PendingVblankEvent`]. + /// + /// A [`PendingVblankEvent`] can only be sent once, so this function consumes the + /// [`PendingVblankEvent`]. + pub fn send(self) + where + T: RawVblankCrtcState>, + C: VblankDriverCrtc + { + let crtc: &Crtc = self.0.crtc(); + let event_lock = crtc.drm_dev().event_lock(); + + with_irqs_disabled(|irq| { + let _guard = event_lock.lock_with(irq); + + // SAFETY: + // * We now hold the appropriate lock to call this function + // * Vblanks are enabled as proved by `vbl_ref`, as per the C api requirements + // * Our interface is proof that `event` is non-null + unsafe { + bindings::drm_crtc_send_vblank_event(crtc.as_raw(), (*self.0.as_raw()).event) + }; + }); + + // SAFETY: The mutable reference in `self.state` is proof that it is safe to mutate this, + // and DRM expects us to set this to NULL once we've sent the vblank event. + unsafe { (*self.0.as_raw()).event = null_mut() }; + } + + /// Arm this [`PendingVblankEvent`] to be sent later by the CRTC's vblank interrupt handler. + /// + /// A [`PendingVblankEvent`] can only be armed once, so this function consumes the + /// [`PendingVblankEvent`]. As well, it requires a [`VblankRef`] so that vblank interrupts + /// remain enabled until the [`PendingVblankEvent`] has been sent out by the driver's vblank + /// interrupt handler. + pub fn arm(self, vbl_ref: VblankRef<'_, C>) + where + T: RawVblankCrtcState>, + C: VblankDriverCrtc + { + let crtc: &Crtc = self.0.crtc(); + let event_lock = crtc.drm_dev().event_lock(); + + with_irqs_disabled(|irq| { + let _guard = event_lock.lock_with(irq); + + // SAFETY: + // * We now hold the appropriate lock to call this function + // * Vblanks are enabled as proved by `vbl_ref`, as per the C api requirements + // * Our interface is proof that `event` is non-null + unsafe { + bindings::drm_crtc_arm_vblank_event(crtc.as_raw(), (*self.0.as_raw()).event) + }; + }); + + // SAFETY: The mutable reference in `self.state` is proof that it is safe to mutate this, + // and DRM expects us to set this to NULL once we've armed the vblank event. + unsafe { (*self.0.as_raw()).event = null_mut() }; + + // DRM took ownership of `vbl_ref` after we called `drm_crtc_arm_vblank_event` + mem::forget(vbl_ref); + } +} + +/// A borrowed vblank reference. +/// +/// This object keeps the vblank reference count for a [`Crtc`] incremented for as long as it +/// exists, enabling vblank interrupts for said [`Crtc`] until all references are dropped, or +/// [`Crtc::vblank_off`] is called - whichever comes first. +pub struct VblankRef<'a, T: VblankDriverCrtc>(&'a Crtc); + +impl Drop for VblankRef<'_, T> { + fn drop(&mut self) { + // SAFETY: FFI call with no special requirements + unsafe { bindings::drm_crtc_vblank_put(self.0.as_raw()) }; + } +} + +impl<'a, T: VblankDriverCrtc> VblankRef<'a, T> { + fn new(crtc: &'a Crtc) -> Result { + // SAFETY: FFI call with no special requirements + to_result(unsafe { bindings::drm_crtc_vblank_get(crtc.as_raw()) })?; + + Ok(Self(crtc)) + } +} + +/// The base wrapper for [`drm_vblank_crtc`]. +/// +/// Users will rarely interact with this object directly, it is a simple wrapper around +/// [`drm_vblank_crtc`] which provides access to methods and data that is not protected by a lock. +/// +/// # Invariants +/// +/// This type has an identical data layout to [`drm_vblank_crtc`]. +/// +/// [`drm_vblank_crtc`]: srctree/include/drm/drm_vblank.h +#[repr(transparent)] +pub struct VblankCrtc(Opaque, PhantomData); + +impl VblankCrtc { + pub(crate) fn as_raw(&self) -> *mut bindings::drm_vblank_crtc { + // SAFETY: Our data layouts are identical via #[repr(transparent)] + unsafe { self.0.get() } + } + + // SAFETY: The caller promises that `ptr` points to a valid instance of + // `bindings::drm_vblank_crtc`, and that access to this structure has been properly serialized + pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::drm_vblank_crtc) -> &'a Self { + // SAFETY: Our data layouts are identical via #[repr(transparent)] + unsafe { &*ptr.cast() } + } + + /// Returns the [`Device`] for this [`VblankGuard`] + pub fn drm_dev(&self) -> &Device { + // SAFETY: `drm` is initialized, invariant and valid throughout our lifetime + unsafe { Device::borrow((*self.as_raw()).dev) } + } +} + +// NOTE: This type does not use a `Guard` because the mutex is not contained within the same +// structure as the relevant CRTC +/// An interface for accessing and controlling vblank related state for a [`Crtc`]. +/// +/// This type may be returned from some [`VblankSupport`] callbacks, or manually via +/// [`Crtc::vblank_lock`]. It provides access to methods and data which require +/// [`drm_device.vbl_lock`] be held. +/// +/// # Invariants +/// +/// - [`drm_device.vbl_lock`] is acquired whenever an instance of this type exists. +/// - Shares the invariants of [`VblankCrtc`]. +/// +/// [`drm_device.vbl_lock`]: srctree/include/drm/drm_device.h +#[repr(transparent)] +pub struct VblankGuard<'a, T: VblankDriverCrtc>(&'a VblankCrtc); + +impl<'a, T: VblankDriverCrtc> VblankGuard<'a, T> { + /// Construct a new [`VblankGuard`] + /// + /// # Safety + /// + /// The caller must have already acquired [`drm_device.vbl_lock`]. + /// + /// [`drm_device.vbl_lock`]: srctree/include/drm/drm_device.h + pub(crate) unsafe fn new( + crtc: &'a Crtc, + _irq: IrqDisabled<'a> + ) -> Self { + Self(crtc.vblank_crtc()) + } + + /// Returns the duration of a single scanout frame in ns + pub fn frame_duration(&self) -> i32 { + // SAFETY: We hold the appropriate lock for this read via our type invariants. + unsafe { (*self.as_raw()).framedur_ns } + } +} + +impl Deref for VblankGuard<'_, T> { + type Target = VblankCrtc; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Drop for VblankGuard<'_, T> { + fn drop(&mut self) { + // SAFETY: + // - We acquired this spinlock when creating this object + // - This lock is guaranteed to be initialized for as long as our DRM device is exposed to + // users. + unsafe { bindings::spin_unlock(&mut (*self.drm_dev().as_raw()).vbl_lock) } + } +} From patchwork Mon Sep 30 23:10:17 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817287 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 83744CEB2CD for ; Mon, 30 Sep 2024 23:41:57 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 1442210E5AC; Mon, 30 Sep 2024 23:41:57 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="d23S7xwH"; 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 A1BC410E5AC for ; Mon, 30 Sep 2024 23:41:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739715; 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=Hb064is8joXzoyCGmxX+mPb868GRiJ43Hq2b6TnYY6k=; b=d23S7xwHQINGWoPQu53UUNMkNB5D8go9h52l0w1GVc5W/LSJAySvtqEqnOBDktpGnH3Kt2 p1v9Actn8aj/P188uv2vAUBpAY9vmu4rv+7ua4MeulVfeHT3RpyheCtIgpWDX8GRzDMp64 c/90X9Q1idBjR/yZLLHSUumrs9MaQrU= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-180-rT-mv8fEP5C8UfCkbOntLA-1; Mon, 30 Sep 2024 19:41:52 -0400 X-MC-Unique: rT-mv8fEP5C8UfCkbOntLA-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 1D5B0196D18E; Mon, 30 Sep 2024 23:41:50 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id D6F2D3003DEC; Mon, 30 Sep 2024 23:41:45 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 34/35] WIP: rust: drm/kms: Add Kms::atomic_commit_tail Date: Mon, 30 Sep 2024 19:10:17 -0400 Message-ID: <20240930233257.1189730-35-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" A quick note: this is one of my favorite bindings so far :). It sounds way overly complicated, but so far actually writing implementations of this in rust has been a breeze. Anyway: RVKMS has a slightly different atomic_commit_tail than normal, which means we need to write up some bindings for atomic_commit_tail. This is a lot more interesting then it might seem on the surface as implementing atomic_commit_tail incorrectly could result in UB. And in general, DRM has up until now relied entirely on the programmer to do this correctly through implicit ordering requirements. In the universe of rust though, we want no UB at all! To ensure this, we need to make sure that all atomic commit callbacks follow all of these requirements: * Disable/enable modeset commits must happen exactly once * A disable modeset must be committed for a resource before an enable modeset may be committed for a resource * Plane updates must happen exactly once * drm_atomic_commit_hw_done() must be called exactly once, and only after all commits have been completed. * The state may not be mutated after drm_atomic_commit_hw_done() is called * Access to the prior atomic states are revoked after drm_atomic_commit_hw_done() is called (and our "new" states become "old" states) To handle this, we introduce a number of new objects and types: tokens: * AtomicCommitTail Main object for controlling the commit_tail process * ModesetsReadyToken A single use token indicating that no modesets have been committed with the AtomicCommitTail yet * commit_modeset_disables() -> DisablesCommittedToken This function consumes the ModesetsReadyToken, commits modeset disables, and then returns a DisablesCommittedToken * commit_modeset_enables() -> EnablesCommittedToken This function consumes a DisablesCommittedToken, commits modeset enables, and then returns a EnablesCommittedToken EnablesCommittedToken - enforcing the disables -> enables order. * commit_planes() -> PlaneUpdatesCommittedToken Consumes a PlaneUpdatesReadyToken and returns a PlaneUpdatesCommittedToken. * commit_hw_done() -> CommittedAtomicState Revokes access to the AtomicCommitTailObject, and consumes both the EnablesCommittedToken and PlaneUpdatesCommitted tokens. This ensures that all modesets and plane updates have occurred exactly once. * CommittedAtomicState - main object for controlling the atomic_commit_tail after the state has been swapped in. This must be returned from the atomic_commit_tail function to prove that all of the required commits have occurred. Signed-off-by: Lyude Paul --- TODO: * Currently this solution wouldn't be sufficient for drivers that need precise control over the order of each individual modeset or plane update. However, this should be very easy to add. * Figure out something better for enforcing the plane cleanup then what we have right now (e.g. cleaning up planes in the destructor for CommittedAtomicState). * Add iterator functions that take mutable references to the atomic state objects here. This will prevent functions like commit_modeset_disables() from being called while a state borrow is taken out, while still allowing easy access to the contents of the atomic state at any portion of the atomic commit tail. * Actually add some macros for generating bitmasks like we do with PlaneCommitFlags - right now we just do this by hand. Signed-off-by: Lyude Paul --- rust/kernel/drm/kms.rs | 27 ++- rust/kernel/drm/kms/atomic.rs | 365 +++++++++++++++++++++++++++++++++- 2 files changed, 386 insertions(+), 6 deletions(-) diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs index e13f35d9e223f..117c97a9e7165 100644 --- a/rust/kernel/drm/kms.rs +++ b/rust/kernel/drm/kms.rs @@ -142,6 +142,26 @@ fn mode_config_info( /// Create mode objects like [`crtc::Crtc`], [`plane::Plane`], etc. for this device fn create_objects(drm: &UnregisteredKmsDevice<'_, Self::Driver>) -> Result; + + /// The optional [`atomic_commit_tail`] callback for this [`Device`]. + /// + /// It must return a [`CommittedAtomicState`] to prove that it has signaled completion of the hw + /// commit phase. Drivers may use this function to customize the order in which commits are + /// performed during the atomic commit phase. + /// + /// If not provided, DRM will use its own default atomic commit tail helper + /// [`drm_atomic_helper_commit_tail`]. + /// + /// [`CommittedAtomicState`]: atomic::CommittedAtomicState + /// [`atomic_commit_tail`]: srctree/include/drm/drm_modeset_helper_vtables.h + /// [`drm_atomic_helper_commit_tail`]: srctree/include/drm/drm_atomic_helpers.h + fn atomic_commit_tail<'a>( + state: atomic::AtomicCommitTail<'a, Self::Driver>, + _modeset_token: atomic::ModesetsReadyToken<'_>, + _plane_update_token: atomic::PlaneUpdatesReadyToken<'_> + ) -> atomic::CommittedAtomicState<'a, Self::Driver> { + build_error::build_error("This function should not be reachable") + } } impl private::KmsImpl for T { @@ -164,7 +184,12 @@ impl private::KmsImpl for T { kms_helper_vtable: bindings::drm_mode_config_helper_funcs { atomic_commit_setup: None, // TODO - atomic_commit_tail: None, // TODO + atomic_commit_tail: + if Self::HAS_ATOMIC_COMMIT_TAIL { + Some(atomic::commit_tail_callback::) + } else { + None + }, }, }); diff --git a/rust/kernel/drm/kms/atomic.rs b/rust/kernel/drm/kms/atomic.rs index a4354b89b07cc..f9398edbca3d6 100644 --- a/rust/kernel/drm/kms/atomic.rs +++ b/rust/kernel/drm/kms/atomic.rs @@ -14,14 +14,14 @@ private::Sealed }; use core::{ - marker::*, - ptr::NonNull, cell::Cell, ffi::*, - slice, - ops::*, - mem::ManuallyDrop, iter::Iterator, + marker::*, + mem::ManuallyDrop, + ops::*, + ptr::NonNull, + slice }; use super::{ crtc::*, @@ -372,6 +372,361 @@ pub fn add_affected_planes(&self, crtc: &impl AsRawCrtc) -> Result { } } +/// A token proving that no modesets for a commit have completed. +/// +/// This token is proof that no commits have yet completed, and is provided as an argument to +/// [`Kms::atomic_commit_tail`]. This may be used with +/// [`AtomicCommitTail::commit_modeset_disables`]. +pub struct ModesetsReadyToken<'a>(PhantomData<&'a ()>); + +/// A token proving that modeset disables for a commit have completed. +/// +/// This token is proof that an implementor's [`Kms::atomic_commit_tail`] phase has finished +/// committing any operations which disable mode objects. It is returned by +/// [`AtomicCommitTail::commit_modeset_disables`], and can be used with +/// [`AtomicCommitTail::commit_modeset_enables`] to acquire a [`EnablesCommittedToken`]. +pub struct DisablesCommittedToken<'a>(PhantomData<&'a ()>); + +/// A token proving that modeset enables for a commit have completed. +/// +/// This token is proof that an implementor's [`Kms::atomic_commit_tail`] phase has finished +/// committing any operations which enable mode objects. It is returned by +/// [`AtomicCommitTail::commit_modeset_enables`]. +pub struct EnablesCommittedToken<'a>(PhantomData<&'a ()>); + +/// A token proving that no plane updates for a commit have completed. +/// +/// This token is proof that no plane updates have yet been completed within an implementor's +/// [`Kms::atomic_commit_tail`] implementation, and that we are ready to begin updating planes. It +/// is provided as an argument to [`Kms::atomic_commit_tail`]. +pub struct PlaneUpdatesReadyToken<'a>(PhantomData<&'a ()>); + +/// A token proving that all plane updates for a commit have completed. +/// +/// This token is proof that all plane updates within an implementor's [`Kms::atomic_commit_tail`] +/// implementation have completed. It is returned by [`AtomicCommitTail::commit_planes`]. +pub struct PlaneUpdatesCommittedToken<'a>(PhantomData<&'a ()>); + +/// An [`AtomicState`] interface that allows a driver to control the [`atomic_commit_tail`] +/// callback. +/// +/// This object is provided as an argument to [`Kms::atomic_commit_tail`], and represents an atomic +/// state within the commit tail phase which is still in the process of being committed to hardware. +/// It may be used to control the order in which the commit process happens. +/// +/// # Invariants +/// +/// Same as [`AtomicState`]. +/// +/// [`atomic_commit_tail`]: srctree/include/drm/drm_modeset_helper_vtables.h +pub struct AtomicCommitTail<'a, T: KmsDriver>(&'a AtomicState); + +impl<'a, T: KmsDriver> AtomicCommitTail<'a, T> { + /// Commit modesets which would disable outputs. + /// + /// This function commits any modesets which would shut down outputs, along with preparing them + /// for a new mode (if needed). + /// + /// Since it is physically impossible to disable an output multiple times, and since it is + /// logically unsound to disable an output within an atomic commit after the output was enabled + /// in the same commit - this function requires a [`ModesetsReadyToken`] to consume and returns + /// a [`DisablesCommittedToken`]. + /// + /// If compatibility with legacy CRTC helpers is desired, this + /// should be called before [`commit_planes`] which is what the default commit function does. + /// But drivers with different needs can group the modeset commits tgether and do the plane + /// commits at the end. This is useful for drivers doing runtime PM since then plane updates + /// only happen when the CRTC is actually enabled. + /// + /// [`commit_planes`]: AtomicCommitTail::commit_planes + #[inline] + #[must_use] + pub fn commit_modeset_disables<'b>( + &mut self, + _token: ModesetsReadyToken<'_>, + ) -> DisablesCommittedToken<'b> { + // SAFETY: Both `as_raw()` calls are guaranteed to return valid pointers + unsafe { + bindings::drm_atomic_helper_commit_modeset_disables( + self.0.drm_dev().as_raw(), + self.0.as_raw() + ) + } + + DisablesCommittedToken(PhantomData) + } + + /// Commit all plane updates. + /// + /// This function performs all plane updates for the given [`AtomicCommitTail`]. Since it is + /// logically unsound to perform the same plane update more then once in a given atomic commit, + /// this function requires a [`PlaneUpdatesReadyToken`] to consume and returns a + /// [`PlaneUpdatesCommittedToken`] to prove that plane updates for the state have completed. + #[inline] + #[must_use] + pub fn commit_planes<'b>( + &mut self, + _token: PlaneUpdatesReadyToken<'_>, + flags: PlaneCommitFlags + ) -> PlaneUpdatesCommittedToken<'b> { + // SAFETY: Both `as_raw()` calls are guaranteed to return valid pointers + unsafe { + bindings::drm_atomic_helper_commit_planes( + self.0.drm_dev().as_raw(), + self.0.as_raw(), + flags.into() + ) + } + + PlaneUpdatesCommittedToken(PhantomData) + } + + /// Commit modesets which would enable outputs. + /// + /// This function commits any modesets in the given [`AtomicCommitTail`] which would enable + /// outputs, along with preparing them for their new modes (if needed). + /// + /// Since it is logically unsound to enable an output before any disabling modesets within the + /// same atomic commit have been performed, and physically impossible to enable the same output + /// multiple times - this function requires a [`DisablesCommittedToken`] to consume and returns + /// a [`EnablesCommittedToken`] which may be used as proof that all modesets in the state have + /// been completed. + #[inline] + #[must_use] + pub fn commit_modeset_enables<'b>( + &mut self, + _token: DisablesCommittedToken<'_> + ) -> EnablesCommittedToken<'b> { + // SAFETY: Both `as_raw()` calls are guaranteed to return valid pointers + unsafe { + bindings::drm_atomic_helper_commit_modeset_enables( + self.0.drm_dev().as_raw(), + self.0.as_raw() + ) + } + + EnablesCommittedToken(PhantomData) + } + + /// Fake VBLANK events if needed + /// + /// Note that this is still relevant to drivers which don't implement [`VblankSupport`] for any + /// of their CRTCs. + /// + /// TODO: more doc + /// + /// [`VblankSupport`]: super::vblank::VblankSupport + pub fn fake_vblank(&mut self) { + // SAFETY: `as_raw()` is guaranteed to always return a valid pointer + unsafe { bindings::drm_atomic_helper_fake_vblank(self.0.as_raw()) } + } + + /// Signal completion of the hardware commit step. + /// + /// This swaps the atomic state into the relevant atomic state pointers and marks the hardware + /// commit step as completed. Since this step can only happen after all plane updates and + /// modesets within an [`AtomicCommitTail`] have been completed, it requires both a + /// [`EnablesCommittedToken`] and a [`PlaneUpdatesCommittedToken`] to consume. After this + /// function is called, the caller no longer has exclusive access to the underlying atomic + /// state. As such, this function consumes the [`AtomicCommitTail`] object and returns a + /// [`CommittedAtomicState`] accessor for performing post-hw commit tasks. + pub fn commit_hw_done<'b>( + self, + _modeset_token: EnablesCommittedToken<'_>, + _plane_updates_token: PlaneUpdatesCommittedToken<'_>, + ) -> CommittedAtomicState<'b, T> + where + 'a: 'b + { + // SAFETY: we consume the `AtomicCommitTail` object, making it impossible for the user to + // mutate the state after this function has been called - which upholds the safety + // requirements of the C API allowing us to safely call this function + unsafe { bindings::drm_atomic_helper_commit_hw_done(self.0.as_raw()) }; + + CommittedAtomicState(self.0) + } +} + +// The actual raw C callback for custom atomic commit tail implementations +pub(crate) unsafe extern "C" fn commit_tail_callback( + state: *mut bindings::drm_atomic_state +) { + // SAFETY: + // * We're guaranteed by DRM that `state` always points to a valid instance of + // `bindings::drm_atomic_state` + // * This conversion is safe via the type invariants + let state = unsafe { AtomicState::::from_raw(state.cast_const()) }; + + T::atomic_commit_tail( + AtomicCommitTail(state), + ModesetsReadyToken(PhantomData), + PlaneUpdatesReadyToken(PhantomData), + ); +} + +/// An [`AtomicState`] which was just committed with [`AtomicCommitTail::commit_hw_done`]. +/// +/// This object represents an [`AtomicState`] which has been fully committed to hardware, and as +/// such may no longer be mutated as it is visible to userspace. It may be used to control what +/// happens immediately after an atomic commit finishes within the [`atomic_commit_tail`] callback. +/// +/// Since acquiring this object means that all modesetting locks have been dropped, a non-blocking +/// commit could happen at the same time an [`atomic_commit_tail`] implementer has access to this +/// object. Thus, it cannot be assumed that this object represents the current hardware state - and +/// instead only represents the final result of the [`AtomicCommitTail`] that was just committed. +/// +/// # Invariants +/// +/// It may be assumed that [`drm_atomic_helper_commit_hw_done`] has been called as long as this type +/// exists. +/// +/// [`atomic_commit_tail`]: Kms::atomic_commit_tail +/// [`drm_atomic_helper_commit_hw_done`]: srctree/include/drm/drm_atomic_helper.h +pub struct CommittedAtomicState<'a, T: KmsDriver>(&'a AtomicState); + +impl<'a, T: KmsDriver> CommittedAtomicState<'a, T> { + /// Wait for page flips on this state to complete + pub fn wait_for_flip_done(&self) { + // SAFETY: `drm_atomic_helper_commit_hw_done` has been called via our invariants + unsafe { + bindings::drm_atomic_helper_wait_for_flip_done( + self.0.drm_dev().as_raw(), + self.0.as_raw() + ) + } + } +} + +impl<'a, T: KmsDriver> Drop for CommittedAtomicState<'a, T> { + fn drop(&mut self) { + // SAFETY: + // * This interface represents the last atomic state accessor which could be affected as a + // result of resources from an atomic commit being cleaned up. + unsafe { + bindings::drm_atomic_helper_cleanup_planes( + self.0.drm_dev().as_raw(), + self.0.as_raw() + ) + } + } +} + +/// An enumerator representing all the possible flags in a [`PlaneCommitFlags`] mask +/// +/// This is a non-exhaustive list, as the C side could add more later. +/// +/// TODO: this idea kinda sick we should add some macros for this :3c +#[derive(Copy, Clone, PartialEq, Eq)] +#[repr(u32)] +pub enum PlaneCommitFlag { + ActiveOnly = (1 << 0), + NoDisableAfterModeset = (1 << 1), +} + +impl BitOr for PlaneCommitFlag { + type Output = PlaneCommitFlags; + + fn bitor(self, rhs: Self) -> Self::Output { + PlaneCommitFlags(self as u32 | rhs as u32) + } +} + +impl BitOr for PlaneCommitFlag { + type Output = PlaneCommitFlags; + + fn bitor(self, rhs: PlaneCommitFlags) -> Self::Output { + PlaneCommitFlags(self as u32 | rhs.0) + } +} + +/// A bitmask for controlling the behavior of [`AtomicCommitTail::commit_planes`] +/// +/// This corresponds to the `DRM_PLANE_COMMIT_*` flags on the C side. Note that this bitmask does +/// not discard unknown values in order to ensure that adding new flags on the C side of things does +/// not break anything in the future. +#[derive(Copy, Clone, Default, PartialEq, Eq)] +pub struct PlaneCommitFlags(u32); + +impl From for PlaneCommitFlags { + fn from(value: PlaneCommitFlag) -> Self { + Self(value as u32) + } +} + +impl From for u32 { + fn from(value: PlaneCommitFlags) -> Self { + value.0 + } +} + +impl BitOr for PlaneCommitFlags { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } +} + +impl BitOrAssign for PlaneCommitFlags { + fn bitor_assign(&mut self, rhs: Self) { + *self = *self | rhs + } +} + +impl BitAnd for PlaneCommitFlags { + type Output = PlaneCommitFlags; + + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & rhs.0) + } +} + +impl BitAndAssign for PlaneCommitFlags { + fn bitand_assign(&mut self, rhs: Self) { + *self = *self & rhs + } +} + +impl BitOr for PlaneCommitFlags { + type Output = Self; + + fn bitor(self, rhs: PlaneCommitFlag) -> Self::Output { + self | Self::from(rhs) + } +} + +impl BitOrAssign for PlaneCommitFlags { + fn bitor_assign(&mut self, rhs: PlaneCommitFlag) { + *self = *self | rhs + } +} + +impl BitAnd for PlaneCommitFlags { + type Output = PlaneCommitFlags; + + fn bitand(self, rhs: PlaneCommitFlag) -> Self::Output { + self & Self::from(rhs) + } +} + +impl BitAndAssign for PlaneCommitFlags { + fn bitand_assign(&mut self, rhs: PlaneCommitFlag) { + *self = *self & rhs + } +} + +impl PlaneCommitFlags { + /// Create a new bitmask + fn new() -> Self { + Self::default() + } + + /// Check if the bitmask has the given commit flag set + fn has(&self, flag: PlaneCommitFlag) -> bool { + *self & flag == flag.into() + } +} + /// An iterator which goes through each DRM plane currently in an atomic state. /// /// Note that this iterator will return [`OpaquePlane`]s, because it's entirely possible for a From patchwork Mon Sep 30 23:10:18 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Lyude Paul X-Patchwork-Id: 13817288 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 1FF1ACEB2CB for ; Mon, 30 Sep 2024 23:42:19 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 8ED7C10E5AE; Mon, 30 Sep 2024 23:42:18 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="h8AWc+6C"; 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 1E6D510E5AE for ; Mon, 30 Sep 2024 23:42:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727739736; 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=8RNAHaATLEjnd+TmtggNdwyEYacgXK5CVqJ+rc7+KF8=; b=h8AWc+6Cq95vH58t1BQVJufVk5XWDhQvDCHEw03P70EsA1sXvZSPpoEEuEE18a2NMk+71I eypyLJivBaTyBLjzH15kqPxdVG8gpNcYCqW/ytEdEhLnk3BUJqLFDrV38opxDlyMQw+Nbb 2OF0bASWHbcx5gtdhFHqmMPx4seUNv8= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-567-7PQZjkj7OLaZ95TSrO84_A-1; Mon, 30 Sep 2024 19:42:12 -0400 X-MC-Unique: 7PQZjkj7OLaZ95TSrO84_A-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (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 mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id AD179196C429; Mon, 30 Sep 2024 23:42:09 +0000 (UTC) Received: from chopper.redhat.com (unknown [10.22.32.36]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 96C673003DEC; Mon, 30 Sep 2024 23:42:04 +0000 (UTC) From: Lyude Paul To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Cc: Asahi Lina , Danilo Krummrich , mcanal@igalia.com, airlied@redhat.com, zhiw@nvidia.com, cjia@nvidia.com, jhubbard@nvidia.com, 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 , Trevor Gross , linux-kernel@vger.kernel.org (open list) Subject: [WIP RFC v2 35/35] WIP: drm: Introduce RVKMS! Date: Mon, 30 Sep 2024 19:10:18 -0400 Message-ID: <20240930233257.1189730-36-lyude@redhat.com> In-Reply-To: <20240930233257.1189730-1-lyude@redhat.com> References: <20240930233257.1189730-1-lyude@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 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" Now that we've added all of the bits that we need for the KMS API, it's time to introduce rvkms! This is a port of the VKMS driver to rust, with the intent of acting as an example usecase of the KMS bindings that we've come up with so far in preparation for writing a display driver for nova. Currently RVKMS is an extremely bear bones driver - it only registers a device and emulates vblanking, but it exercises a good portion of the API that we've introduced so far! Eventually I hope to introduce CRC generation and maybe writeback connectors like. 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 | 53 ++++++ drivers/gpu/drm/rvkms/crtc.rs | 253 +++++++++++++++++++++++++++++ drivers/gpu/drm/rvkms/encoder.rs | 33 ++++ drivers/gpu/drm/rvkms/file.rs | 22 +++ drivers/gpu/drm/rvkms/gem.rs | 30 ++++ drivers/gpu/drm/rvkms/output.rs | 55 +++++++ drivers/gpu/drm/rvkms/plane.rs | 81 +++++++++ drivers/gpu/drm/rvkms/rvkms.rs | 168 +++++++++++++++++++ 12 files changed, 702 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 6b2c6b91f9625..525e2e1615ca2 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -323,6 +323,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 68cc9258ffc4b..ebedcab4dece0 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -172,6 +172,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..551422803b9a6 --- /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=y 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..97b94054fbe1f --- /dev/null +++ b/drivers/gpu/drm/rvkms/connector.rs @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0 +use super::{Rvkms, RvkmsDevice, MAX_RES, DEFAULT_RES}; +use kernel::{ + prelude::*, + drm::{ + device::Device, + kms::{ + connector::{self, ConnectorGuard, DriverConnectorOps}, + ModeConfigGuard + } + }, + prelude::* +}; +use core::marker::PhantomPinned; + +#[pin_data] +pub(crate) struct DriverConnector { + #[pin] + _p: PhantomPinned +} + +pub(crate) type Connector = connector::Connector; + +#[vtable] +impl connector::DriverConnector for DriverConnector { + #[unique] + const OPS: &'static DriverConnectorOps; + + type State = ConnectorState; + type Driver = Rvkms; + type Args = (); + + fn new(dev: &Device, args: Self::Args) -> impl PinInit { + 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..c3a90c76e4a5e --- /dev/null +++ b/drivers/gpu/drm/rvkms/crtc.rs @@ -0,0 +1,253 @@ +// TODO: License and stuff +// Contain's rvkms's drm_crtc implementation +use core::marker::*; +use super::{Rvkms, plane::*}; +use kernel::{ + prelude::*, + drm::{ + device::Device, + kms::{ + atomic::*, + crtc::{self, RawCrtcState, DriverCrtcOps}, + ModeObject, + KmsRef, + vblank::*, + } + }, + sync::{ + lock::Guard, + SpinLockIrq, + LockedBy, + }, + hrtimer::*, + time::*, + irq::*, + sync::Arc, + new_spinlock_irq, + impl_has_timer +}; + +pub(crate) type Crtc = crtc::Crtc; +pub(crate) type CrtcState = crtc::CrtcState; + +#[derive(Default)] +pub(crate) struct VblankState { + /// A reference to the current VblankTimer + timer: Option>, + + /// A reference to a handle for the current VblankTimer + handle: Option>, + + /// The current frame duration in ns + /// + /// Stored separately here so it can be read safely without the vblank lock + period_ns: i32, +} + +#[pin_data] +pub(crate) struct RvkmsCrtc { + /// The current vblank emulation state + /// + /// This is uninitalized when the CRTC is disabled to prevent circular references + #[pin] + vblank_state: SpinLockIrq +} + +#[vtable] +impl crtc::DriverCrtc for RvkmsCrtc { + #[unique] + const OPS: &'static DriverCrtcOps; + + type Args = (); + type State = RvkmsCrtcState; + type Driver = Rvkms; + type VblankImpl = Self; + + fn new(device: &Device, args: &Self::Args) -> impl PinInit { + try_pin_init!(Self { + vblank_state <- new_spinlock_irq!(VblankState::default(), "vblank_handle_lock") + }) + } + + fn atomic_check( + crtc: &Crtc, + old_state: &CrtcState, + mut new_state: crtc::BorrowedCrtcState<'_, CrtcState>, + state: &AtomicStateComposer + ) -> Result { + state.add_affected_planes(crtc)?; + + // Create a vblank timer when enabling a CRTC, and destroy said timer when disabling to + // resolve the circular reference to CRTC it creates + if old_state.active() != new_state.active() { + new_state.vblank_timer = if new_state.active() { + Some(VblankTimer::new(crtc)?) + } else { + None + }; + } + + Ok(()) + } + + fn atomic_flush( + crtc: &Crtc, + _old_state: &CrtcState, + mut new_state: crtc::BorrowedCrtcState<'_, CrtcState>, + _state: &AtomicStateMutator + ) { + if let Some(event) = new_state.get_pending_vblank_event() { + if let Ok(vbl_ref) = crtc.vblank_get() { + event.arm(vbl_ref); + } else { + event.send(); + } + } + } + + fn atomic_enable( + crtc: &Crtc, + old_state: &CrtcState, + new_state: crtc::BorrowedCrtcState<'_, CrtcState>, + _state: &AtomicStateMutator + ) { + with_irqs_disabled(|irq| { + // Store a reference to the newly created vblank timer for this CRTC + crtc.vblank_state.lock_with(irq).timer = new_state.vblank_timer.clone() + }); + + crtc.vblank_on(); + } + + fn atomic_disable( + crtc: &Crtc, + _old_state: &CrtcState, + _new_state: crtc::BorrowedCrtcState<'_, CrtcState>, + _state: &AtomicStateMutator + ) { + crtc.vblank_off(); + + // Since we just explicitly disabled vblanks, destroy the vblank state to resolve circular + // reference to this CRTC that it holds. Note that dropping the handle will cause us to wait + // for the timer to finish, so we return it from with_irqs_disabled so that it is only + // dropped once the vblank_state lock has been released + drop(with_irqs_disabled(|irq| { + let mut state = crtc.vblank_state.lock_with(irq); + + (state.timer.take(), state.handle.take()) + })); + } +} + +impl VblankSupport for RvkmsCrtc { + type Crtc = Self; + + fn enable_vblank( + crtc: &Crtc, + vblank: &VblankGuard<'_, Self::Crtc>, + irq: IrqDisabled<'_>, + ) -> Result { + let period_ns = vblank.frame_duration(); + let mut vbl_state = crtc.vblank_state.lock_with(irq); + + if let Some(timer) = vbl_state.timer.clone() { + vbl_state.period_ns = period_ns; + vbl_state.handle = Some(timer.schedule(period_ns as _)); + } + + Ok(()) + } + + fn disable_vblank(crtc: &Crtc, _vbl_guard: &VblankGuard<'_, Self::Crtc>, irq: IrqDisabled<'_>) { + let handle = crtc.vblank_state.lock_with(irq).handle.take(); + + // Now that we're outside of the vblank lock, we can safely drop the handle + drop(handle); + } + + fn get_vblank_timestamp(crtc: &Crtc, _handling_vblank_irq: bool) -> Option { + let time = with_irqs_disabled(|irq| { + let vbl_state = crtc.vblank_state.lock_with(irq); + + // Return the expiration of our vblank timer if we have one (if not, vblanks are + // disabled) + vbl_state.timer.as_ref().map(|t| { + // To prevent races, we roll the hrtimer forward before we do any interrupt + // processing - this is how real hw works (the interrupt is only generated after all + // the vblank registers are updated) and what the vblank core expects. Therefore we + // need to always correct the timestamps by one frame. + t.timer.expires() - Ktime::from_ns(vbl_state.period_ns) + }) + }); + + Some(VblankTimestamp { + // …otherwise, just use the current time + time: time.unwrap_or_else(|| Ktime::ktime_get()), + max_error: 0 + }) + } +} + +#[derive(Clone, Default)] +pub(crate) struct RvkmsCrtcState { + vblank_timer: Option> +} + +impl crtc::DriverCrtcState for RvkmsCrtcState { + type Crtc = RvkmsCrtc; +} + +/// The main hrtimer structure for emulating vblanks. +#[pin_data] +pub(crate) struct VblankTimer { + /// The actual hrtimer used for sending out vblanks + #[pin] + timer: Timer, + + /// An owned reference to the CRTC that this [`VblankTimer`] belongs to + crtc: KmsRef, +} + +impl_has_timer! { + impl HasTimer for VblankTimer { self.timer } +} + +impl VblankTimer { + pub(crate) fn new(crtc: &Crtc) -> Result> { + Arc::pin_init( + pin_init!(Self { + timer <- Timer::::new::>(), + crtc: crtc.into(), + }), + GFP_KERNEL + ) + } +} + +impl TimerCallback for VblankTimer { + type CallbackTarget<'a> = Arc; + + fn run( + this: Self::CallbackTarget<'_>, + context: TimerCallbackContext<'_, Self> + ) -> TimerRestart + where + Self: Sized + { + with_irqs_disabled(|irq| { + let period_ns = this.crtc.vblank_state.lock_with(irq).period_ns; + + let overrun = context.forward_now(Ktime::from_ns(period_ns)); + if overrun != 1 { + dev_warn!( + this.crtc.drm_dev().as_ref(), + "vblank timer overrun (expected 1, got {overrun})\n" + ); + } + + this.crtc.handle_vblank(); + }); + + TimerRestart::Restart + } +} diff --git a/drivers/gpu/drm/rvkms/encoder.rs b/drivers/gpu/drm/rvkms/encoder.rs new file mode 100644 index 0000000000000..f426ef10bcd7e --- /dev/null +++ b/drivers/gpu/drm/rvkms/encoder.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 +use core::marker::PhantomPinned; +use kernel::{ + drm::{device::Device, kms::encoder}, + prelude::*, + types::ARef, +}; +use crate::{Rvkms, connector::Connector}; + +#[pin_data] +pub(crate) struct DriverEncoder { + connector: ARef, + #[pin] + _p: PhantomPinned, +} + +pub(crate) type Encoder = encoder::Encoder; + +#[vtable] +impl encoder::DriverEncoder for DriverEncoder { + #[unique] + const OPS: &'static encoder::DriverEncoderOps; + + type Driver = Rvkms; + type Args = ARef; + + fn new(device: &Device, args: Self::Args) -> impl PinInit { + try_pin_init!(Self { + connector: args, + _p: PhantomPinned + }) + } +} diff --git a/drivers/gpu/drm/rvkms/file.rs b/drivers/gpu/drm/rvkms/file.rs new file mode 100644 index 0000000000000..baa9297673ecc --- /dev/null +++ b/drivers/gpu/drm/rvkms/file.rs @@ -0,0 +1,22 @@ +use super::Rvkms; + +use kernel::{ + drm::{ + self, + device::Device as DrmDevice + }, + prelude::*, +}; +use core::option::*; + +pub(crate) struct File; + +impl drm::file::DriverFile for File { + type Driver = Rvkms; + + fn open(device: &DrmDevice) -> Result>> { + pr_info!("Someone opened a file! But I do not yet know which one...\n"); + + Box::pin_init(init!(File { }), GFP_KERNEL) + } +} diff --git a/drivers/gpu/drm/rvkms/gem.rs b/drivers/gpu/drm/rvkms/gem.rs new file mode 100644 index 0000000000000..950ef33758657 --- /dev/null +++ b/drivers/gpu/drm/rvkms/gem.rs @@ -0,0 +1,30 @@ +use crate::{Rvkms, 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 { + fn new(dev: &RvkmsDevice, size: usize) -> impl PinInit { + 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 = Rvkms; +} diff --git a/drivers/gpu/drm/rvkms/output.rs b/drivers/gpu/drm/rvkms/output.rs new file mode 100644 index 0000000000000..b110e2d5d8a8b --- /dev/null +++ b/drivers/gpu/drm/rvkms/output.rs @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0 +use crate::{ + crtc::Crtc, + plane::Plane, + connector::Connector, + encoder::Encoder, + RvkmsDevice, + Rvkms +}; +use kernel::{ + drm::{ + fourcc::*, + kms::{ + connector::DRM_MODE_CONNECTOR_VIRTUAL, + encoder::DRM_MODE_ENCODER_VIRTUAL, + plane::{self, PlaneType}, + framebuffer::*, + UnregisteredKmsDevice, + }, + }, + sync::Arc, + prelude::*, + types::ARef, +}; + +const FORMATS: FormatList<1> = FormatList::new([XRGB888]); + +pub(crate) fn create_output(dev: &UnregisteredKmsDevice<'_, Rvkms>, index: u8) -> Result { + let possible_crtcs = 1 << index; + + let primary = Plane::new( + dev, + possible_crtcs, + &FORMATS, + Option::<&ModifierList<0>>::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, + possible_crtcs, + 0, + None, + connector.clone() + )?; + + connector.attach_encoder(encoder) +} diff --git a/drivers/gpu/drm/rvkms/plane.rs b/drivers/gpu/drm/rvkms/plane.rs new file mode 100644 index 0000000000000..2722845a32e9a --- /dev/null +++ b/drivers/gpu/drm/rvkms/plane.rs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 +use core::marker::PhantomPinned; +use super::{Rvkms, crtc::{RvkmsCrtc, Crtc}}; +use kernel::{ + prelude::*, + drm::{ + device::Device, + kms::{ + atomic::*, + plane::{ + self, + AsRawPlaneState, + FromRawPlaneState, + DriverPlaneState, + RawPlane, + RawPlaneState, + BorrowedPlaneState, + DriverPlaneOps, + }, + ModeObject + } + }, +}; + +#[pin_data] +pub(crate) struct RvkmsPlane { + #[pin] + _p: PhantomPinned, +} + +pub(crate) type Plane = plane::Plane; +pub(crate) type PlaneState = plane::PlaneState; + +#[vtable] +impl plane::DriverPlane for RvkmsPlane { + #[unique] + const OPS: &'static DriverPlaneOps; + + type State = RvkmsPlaneState; + type Driver = Rvkms; + type Args = (); + + fn new(device: &Device, args: Self::Args) -> impl PinInit { + try_pin_init!(Self { _p: PhantomPinned }) + } + + fn atomic_check( + plane: &Plane, + mut new_state: BorrowedPlaneState<'_, PlaneState>, + _old_state: &PlaneState, + state: &AtomicStateComposer + ) -> Result { + if new_state.framebuffer().is_none() { + return Ok(()); + } + + if let Some(crtc) = new_state.crtc() { + let crtc_state = state.add_crtc_state(crtc)?; + new_state.atomic_helper_check(&crtc_state, true, true) + } else { + // TODO: We should be printing a warning here if we have no CRTC but do have an fb + return Ok(()); + } + } + + fn atomic_update( + _plane: &Plane, + _new_state: BorrowedPlaneState<'_, PlaneState>, + _old_state: &PlaneState, + _state: &AtomicStateMutator, + ) { + // TODO, no-op for now + } +} + +#[derive(Clone, Default)] +pub(crate) struct RvkmsPlaneState; + +impl DriverPlaneState for RvkmsPlaneState { + type Plane = RvkmsPlane; +} diff --git a/drivers/gpu/drm/rvkms/rvkms.rs b/drivers/gpu/drm/rvkms/rvkms.rs new file mode 100644 index 0000000000000..2c72c0ec6989d --- /dev/null +++ b/drivers/gpu/drm/rvkms/rvkms.rs @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +mod connector; +mod crtc; +mod file; +mod gem; +mod plane; +mod output; +mod encoder; + +use alloc::boxed::Box; + +use core::{option::*, marker::*}; + +use kernel::{ + c_str, + str::CStr, + device, + driver, + drm::{ + self, + drv, + kms::{ + Kms, + ModeConfigInfo, + UnregisteredKmsDevice, + atomic::*, + fbdev::*, + }, + }, + platform, + prelude::*, + sync::Arc, +}; + +/// Convienence type alias for the DRM device type for this driver +pub(crate) type RvkmsDevice = drm::device::Device; + +/// The name of the driver +const NAME: &'static CStr = c_str!("rvkms"); + +/// Driver metadata +const INFO: drv::DriverInfo = drv::DriverInfo { + major: 0, + minor: 0, + patchlevel: 0, + name: &NAME, + 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 Rvkms { + type Data = Arc; + type Object = gem::Object; + type File = file::File; + type Kms = Self; + + const INFO: drv::DriverInfo = INFO; + const FEATURES:u32 = drv::FEAT_GEM | drv::FEAT_MODESET | drv::FEAT_ATOMIC; + + kernel::declare_drm_ioctls! {} +} + +#[vtable] +impl Kms for Rvkms { + type Driver = Self; + type Fbdev = FbdevShmem; + + fn mode_config_info( + _dev: &device::Device, + _drm_data: <::Data as kernel::types::ForeignOwnable>::Borrowed<'_>, + ) -> Result { + Ok(MODE_CONFIG_INFO) + } + + fn create_objects(drm: &UnregisteredKmsDevice<'_, Self::Driver>) -> Result { + output::create_output(drm, 0) + } + + fn atomic_commit_tail<'a>( + mut state: AtomicCommitTail<'a, Self::Driver>, + modeset_token: ModesetsReadyToken<'_>, + plane_update_token: PlaneUpdatesReadyToken<'_>, + ) -> CommittedAtomicState<'a, Self::Driver> { + let modeset_token = state.commit_modeset_disables(modeset_token); + + let plane_update_token = state.commit_planes(plane_update_token, Default::default()); + + let modeset_token = state.commit_modeset_enables(modeset_token); + + state.fake_vblank(); + + let state = state.commit_hw_done(modeset_token, plane_update_token); + + state.wait_for_flip_done(); + + state + } +} + +impl platform::Driver for Rvkms { + type Data = Arc; + type IdInfo = (); + + fn probe(pdev: &mut platform::Device, id_info: Option<&Self::IdInfo>) -> Result { + // XXX: do not fret, the mutable reference here is temporary (poke dakr if it isn't) + let dev: &device::Device = pdev.as_ref(); + dev.pr_info(format_args!("RVKMS probing\n")); + + let data = Arc::new(Data { }, GFP_KERNEL)?; + let drm = drv::Registration::::new_foreign_owned(dev, data.clone(), 0)?; + + Ok(data) + } +} + +pub(crate) struct Rvkms { + drv_reg: Pin>>, + 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) -> kernel::error::Result { + pr_info!("RVKMS module loaded\n"); + + // Register the driver (FIXME: this should be static + let drv_reg = Box::try_pin_init( + platform::Registration::::new(name, module), + GFP_KERNEL + )?; + + let pdev = platform::Device::create_simple(&NAME, 0)?; + let dev: &device::Device = pdev.as_ref(); + + Ok(Self { + drv_reg, + pdev, + }) + } +} + +module! { + type: Rvkms, + name: "rvkms", + author: "Lyude Paul", + description: "Rust VKMS Proof of Concept driver", + license: "GPL v2", +}