dri-devel.lists.freedesktop.org archive mirror
 help / color / mirror / Atom feed
* [RFC WIP 0/4] Rust bindings for KMS + RVKMS
@ 2024-03-22 22:03 Lyude Paul
  2024-03-22 22:03 ` [PATCH 1/4] WIP: rust: Add basic KMS bindings Lyude Paul
                   ` (3 more replies)
  0 siblings, 4 replies; 12+ messages in thread
From: Lyude Paul @ 2024-03-22 22:03 UTC (permalink / raw)
  To: dri-devel
  Cc: Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, open list:RUST:Keyword:b(?i:rust)b

Hi everyone! I mentioned a little while ago that I've been working on
porting vkms over to rust so that we could come up with a set of rust
KMS bindings for the nova driver to be able to have a modesetting driver
written in rust. This driver currently doesn't really do much, but it
does load and register a modesetting device!

I wanted to send this early on so that people could take a look and tell
me if there's anything I've overlooked so far. As well, I've written up
a high level overview of how the interface works - along with my
reasoning for a lot of the design choices here. I'm sure things will
change a bit as I figure out what we really need while porting rvkms,
but I'm hoping this is pretty close to what the final set of bindings
are going to look like.

Note: not all of the patches required for building this have been
included. I've just included the kms/rvkms bits here, but the full
branch can be found here:

https://gitlab.freedesktop.org/lyudess/linux/-/commits/rvkms-03222024

So, the overview!

# Modesetting objects and atomic states

A general overview of the interface: so far we have the following traits
for drivers to implement:

  * DriverConnector
  * DriverCrtc
  * DriverPlane
  * DriverEncoder

Each one of these modesetting objects also has a typed container that
can be used to refer to it by drivers: Connector<T>, Plane<T>, etc. Each
typed container has only two members (not counting phantoms). Using
Crtc<T> as an example:

/// A typed KMS CRTC with a specific driver.
#[repr(C)]
#[pin_data]
pub struct Crtc<T: DriverCrtc> {
    // The FFI drm_crtc object
    pub(super) crtc: Opaque<bindings::drm_crtc>,
    /// The driver's private inner data
    #[pin]
    inner: T,
    #[pin]
    _p: PhantomPinned,
}

The first member is an Opaque<> container containing the FFI struct for
the modesetting object, and the second `inner` contains the implementing
driver's private data for the modesetting object. Each modesetting
object container can be coerced (through Deref<>) into an immutable
reference to inner to make accessing driver-private data easy. This
allows subclassing of modesetting objects in a similar manner to what we
currently do in C.

The main difference is that modesetting objects are immutable to drivers
(as in, drivers can only get immutable references to these objects). The
reason for this is that while it's common for drivers to stick their own
private data in objects, pretty much all of this data falls into one of
these categories:

* Static driver data which is assigned during driver init. In our
  abstraction, this assignment will happen in each modesetting object's
  T::new() function - where the driver can use each modesetting object
  trait's Args type to specify a data type containing such data which
  will be passed to T::new(). This should always be available through
  immutable references.
* Modesetting state for drivers which needs to be kept track of
  independently of the atomic state. As the name entails, all data in
  this category is assumed to only be accessible within a modesetting
  context - which itself ensures synchronization. At some point we will
  implement a container type for this, which only provides a &mut
  reference once the caller provides proof that we're in an atomic
  modesetting context and thus - are the only ones who could possibly
  hold a mutable reference.
* Miscellaneous driver state, which may need to be accessed both inside
  and outside of the atomic modesetting context. We will provide no
  container type for this, and it will be up to the driver to provide an
  appropriate container that ensures synchronization and hands out
  mutable references as needed. A simple example of such a container
  would be Mutex<T>.

# Opaque modesetting objects

This is something I haven't written up code for yet because we haven't
run into any use for it, but presumably we will eventually. Basically:
since we have driver subclassing through types like Connector<T> which
the main DRM device trait doesn't need to know about - it is technically
possible for us to have multiple different implementations of a
modesetting object (for instance, a driver could have two
DriverConnector implementations for different types of display
connectors). At first I thought this might be too complicated, but I
don't think it is! To support this, we would use a similar solution to
what the GEM bindings currently do - by providing an opaque version of
each modesetting object which only can be used for operations common
across different implementations of that object. This Opaque struct
could then be fallibly converted to its more qualified type, and we can
ensure type safety here by simply using each implementation's vtable
pointer (all of which should be 'static) to tell whether an opaque
object matches the qualified type the driver is expecting. Here's some
example psuedocode to give you an idea:

```
let drm_dev: drm::device::Device<Driver>; /* initialized elsewhere */

type Plane = drm::kms::plane::Plane<DriverPlane>;

for plane in drm_dev.iter_planes().filter_map(Plane::from_opaque) {
  // ... do something
}
```

We'd only really need to use these opaque types in situations where we
can't guarantee type invariance, like iterating a list of multiple impls
of modesetting objects. So, that more or less just means "anywhere
outside of a DRM callback" (callbacks can ensure type invariance since
each type has its own vtable).

# Atomic states for modesetting objects

For objects with atomic states, these traits are also implemented by the
driver and allow subclassing as well - but with a few differences:

* ConnectorState
* CrtcState
* PlaneState

The main difference is that unlike with modesetting objects, atomic
states are initially* considered mutable objects. This is mainly since
contexts like atomic_check can satisfy rust's data aliasing rules, and
it's just a lot easier for drivers to be able to get &mut references to
these easily. It is likely that we may expose these objects later on
through Pin<&mut> - since we don't want to allow them to move as certain
drivers will do things like storing work structs in their states. I
haven't gotten this far in the code yet though!

# Helpers

VKMS does not actually use drm_plane_state! Instead, it actually uses a
GEM provided atomic state helper and the drm_shadow_plane_struct - which
contains a drm_plane_state. And this is only a single example of such a
helper, as a number of other ones which change the struct type a driver
uses exist. So, how do we actually handle this in our bindings? Well, by
shamelessly copying what the GEM bindings do!

For modesetting objects where we want to also support the use of a
helper that comes with it's own modesetting object/atomic state struct,
we can follow another trait pattern. Using PlaneState<T> and shadow
planes as an example: we can introduce a sealed trait named
IntoPlaneState. This trait will represent any object in rust which
contains a drm_plane_state - along with providing a set of functions
that can be implemented to tell rust how to translate the object into a
drm_plane_state, along with providing methods for invoking any other DRM
functions that might be different for a helper. In the patch series I've
posted, I've included some of the WIP code for handling shadow planes in
this manner.

# The initially* mutable global atomic state

I have some WIP code written up for this, but nothing very complete
quite yet as we haven't yet needed to manually instantiate an atomic
state yet. But in some of the code I wrote up, I noticed some funny
gotchas.

The main drm_atomic_state structure is refcounted - which means multiple
references to an atomic state can exist at once. This complicates things
just a bit - as rust's data aliasing rules only allow for one mutable
reference to an object to exist at a time. My current plan for handling
this is to extend the kernel's ARef<T> type to also include a
UniqueARef<T> type. This is basically the same as the kernel's
UniqueArc<T> type, but for already refcounted objects - which will allow
a caller mutable access to the underlying object so long as it's the
only one holding a ref to it. Sharing references to the atomic state
requires will require converting away from the UniqueARef. I think this
should be enough, since drivers shouldn't really be making any changes
to a state after the atomic check phase - so only having immutable
references after that point should be good enough to make everyone
happy.

# Modesetting object lifetimes, ModesetObject and StaticModesetObject

I think sima may have tried to warn me about this, because this was
definitely a strange bit to figure out. There are two main types of
modesetting objects in the kernel: refcounted modesetting objects, and
non-refcounted modesetting objects. Reference counted objects are easy:
we just implement the kernel's AlwaysRefCounted trait for them.
Non-refcounted modesetting objects are weirder, as they share the
lifetime of the device. This was one of the other big reasons we have to
always expose modesetting objects through immutable references giving
driver ownership over these structs would imply the driver could drop
them before the drm device has been dropped which would cause UB. I
originally considered having modesetting objects live in Arc<>
internally to workaround this, but decided against it as this would mean
we could make a non-refcounted modesetting object outlive it's device -
which could also cause UB if a driver tried to do a modesetting
operation with said object.

I'd _really_ love to figure out how to handle this with references and
lifetimes, as right now we currently tie the lifetime of a modesetting
object's reference to the lifetime of the reference to the DRM device
that created it - but this isn't currently enough to store those
references in a driver's private data as I can't figure out how to tell
the compiler "this reference is valid for as long as you hold an ARef
for the DRM device". Which brings us to ModesetObject and
StaticModesetObject. ModesetObject is a simple trait implemented by all
DRM modesetting objects, which provides access to functions common
across them (such as fetching the DRM device associated with a
modesetting object). StaticModesetObject is a supertrait of
ModesetObject which essentially marks a modesetting object as not having
a refcount. We then provide KmsRef<T> which can be used to retrieve a
"owned" reference to a modesetting object which can be stored in device
state. This owned reference is simply a pointer to the modesetting
object, along with an owned refcount for that object's parent device -
which should ensure that the modesetting object is initialized and valid
for as long as a KmsRef<> is held. I'm open for better ideas to solve
this though :).

# BFLs

Unfortunately KMS has a few BFLs. In particular,
drm_device.mode_config.mutex is one such BFL that protects anything
related to hotplugging state (and probably a few other things?). In
rust, data races are considered UB - so we need to be careful to ensure
that any safe bindings we expose take care to be thread safe. Since rust
usually represents locks as container types (e.g. Mutex<T>), but we have
to deal with a BFL, we need a different solution that we can use to
represent critical sections under this BFL. This brings us to
ModeConfigGuard and ConnectorGuard.

In rust, upon acquiring access to a mutex - you're given a "Guard"
object which will release its respective lock once its dropped, and
ModeConfigGuard is our version of this. ModeConfigGuards can either be
"owned" or "borrowed". "Owned" means the rust code acquired the lock
itself and thus - the lock must be dropped when the guard goes out of
scope. "Borrowed" guards can only be created using unsafe functions, and
are basically simply a promise that the mode config lock is held for the
entire scope in which the ModeConfigGuard is alive. Borrowed guards are
really only something that will be used on the FFI side of things for
callbacks like drm_connector.get_modes() - where we know that the
mode_config_lock was acquired prior to our get_modes() callback being
invoked by DRM.

ConnectorGuard is basically just a helpful extension of this that can be
instantiated cheaply from a ModeConfigGuard, and it basically just
provides access to methods for individual connectors that can only
happen under the mode_config lock. You can see an example of this in
rust/kernel/drm/kms/connector.rs and drivers/gpu/drm/rvkms/connector.rs.
Keep in mind we currently don't have an implementation of an owned
ModeConfigGuard, as no use for one has arisen in rvkms quite yet.

Note: I did (and still am) considering using the kernel's already-made
LockedBy<> type - however, this seems rather difficult to do considering
that the BFL and connector live in different structs - and those structs
are FFI structs. I shoud look a bit closer at some point soon though. As
well, I expect the semantics around how this type works might change
slightly - as I'd also like to see if we can represent the difference
between owned and borrowed guards such that no checks need to be done at
runtime.

# Actually registering a modesetting device

This part is still very much in flux I think, and it's worth explaining
why. Currently from the DRM bindings we inherited from Asahi, the
lifetime of a DRM device's registration is represented by
drm::drv::Registration<T>. Unsurprisingly, the purpose of this is that
once the Registration<T> object is dropped - the DRM device is
unregistered. There's some complications around this though.

The big one is that modesetting initialization is single threaded. This
should be fine, but, currently (following how Asahi's driver does this)
we ensure automatic reg/unreg by sticking the Registration<T> object in
our registration device data (see `device::Data<T, U, V>` for an
explanation of what this means). And that would also be fine, except for
the fact that said device data has to be thread safe. This means it
needs Send/Sync, which also means we basically have to treat the
registration as multi-threaded despite the fact it really should never
actually be happening in multiple threads. We also have a
RegistrationInfo struct i had to come up with, which provides access to
an atomic boolean that tracks whether or not we've actually registered
the device. This part was only needed because while DRM does have a
variable for tracking this in drm_device already, the Registration<T>
object is created before registration actually happens - so it needs a
thread-safe way of knowing whether or not it needs to unregister the
device upon being dropped.

The other thing I haven't been able to figure out: a way of safely
gating global modesetting device functions so that they only can be used
on DRM devices that support KMS (an example of one such function -
drm_mode_config_reset()). Since the callbacks for modesetting devices
and the rest of DRM live in the same struct, the same struct is used for
both cases - even though it's entirely possible to have a drm_device
without KMS support and thus without an initialized mode_config struct.
This would be very nice to figure out, because I assume there's likely
to be UB if a non-KMS device attempts to do KMS-like operations on
itself. Currently, a modesetting device indicates it has KMS in my
branch by doing two things:

* Setting FEAT_MODESET and FEAT_ATOMIC in drm::drv::Driver::FEATURES
* Passing a ModeConfigInfo struct to drm::drv::Registration::register(),
  containing various misc. information drivers usually populate in
  mode_config

Figuring out how to gate these to only KMS-supporting devices would
likely mean moving the global modesetting callbacks we need to support
into a different trait that's only implemented by KMS drivers - but I'm
not quite sure how to do that cleanly yet.

# Other issues/hacks

* Currently, a DRM driver's vtable and file operations table are not
  static. I totally think we can (and should) make this static by making
  drm::gem::create_fops() a const fn, and also turning DriverOps's
  constructors into const fns. The current blocker for this is that
  Default::default() is not const, along with mem::zeroed() - giving us
  no way of creating a zero-initialized struct at compile-time.
  Coincidentally, mem::zeroed() actually becomes const in rust 1.75 - so
  once the kernel updates its rust version we should be able to fix
  this.
* There is a leak somewhere? Unloading rvkms currently leaves behind a
  few DRI directories, I'm not totally sure why yet - but I think this
  may be an issue with the DRM bindings themselves.
* bindgen doesn't understand fourcc, and probably a number of other
  similar files. So we're going to need some nasty hacks to expose
  these.
* I'm sure there's bits of code that need cleaning up, but I figured it
  was more important to start getting feedback on all of this first :).

Lyude Paul (4):
  WIP: rust: Add basic KMS bindings
  WIP: drm: Introduce rvkms
  rust/drm/kms: Extract PlaneState<T> into IntoPlaneState
  WIP: rust/drm/kms: Add ShadowPlaneState<T>

 drivers/gpu/drm/Kconfig                  |   2 +
 drivers/gpu/drm/Makefile                 |   1 +
 drivers/gpu/drm/rvkms/Kconfig            |   3 +
 drivers/gpu/drm/rvkms/Makefile           |   1 +
 drivers/gpu/drm/rvkms/connector.rs       |  55 +++
 drivers/gpu/drm/rvkms/crtc.rs            |  40 +++
 drivers/gpu/drm/rvkms/encoder.rs         |  26 ++
 drivers/gpu/drm/rvkms/file.rs            |  22 ++
 drivers/gpu/drm/rvkms/gem.rs             |  32 ++
 drivers/gpu/drm/rvkms/output.rs          |  72 ++++
 drivers/gpu/drm/rvkms/plane.rs           |  42 +++
 drivers/gpu/drm/rvkms/rvkms.rs           | 146 ++++++++
 rust/bindings/bindings_helper.h          |   6 +
 rust/helpers.c                           |  17 +
 rust/kernel/drm/device.rs                |   2 +
 rust/kernel/drm/drv.rs                   | 115 ++++++-
 rust/kernel/drm/kms.rs                   | 147 +++++++++
 rust/kernel/drm/kms/connector.rs         | 404 +++++++++++++++++++++++
 rust/kernel/drm/kms/crtc.rs              | 300 +++++++++++++++++
 rust/kernel/drm/kms/encoder.rs           | 175 ++++++++++
 rust/kernel/drm/kms/gem_atomic_helper.rs |  48 +++
 rust/kernel/drm/kms/plane.rs             | 339 +++++++++++++++++++
 rust/kernel/drm/mod.rs                   |   1 +
 23 files changed, 1980 insertions(+), 16 deletions(-)
 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
 create mode 100644 rust/kernel/drm/kms.rs
 create mode 100644 rust/kernel/drm/kms/connector.rs
 create mode 100644 rust/kernel/drm/kms/crtc.rs
 create mode 100644 rust/kernel/drm/kms/encoder.rs
 create mode 100644 rust/kernel/drm/kms/gem_atomic_helper.rs
 create mode 100644 rust/kernel/drm/kms/plane.rs

-- 
2.43.0


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 1/4] WIP: rust: Add basic KMS bindings
  2024-03-22 22:03 [RFC WIP 0/4] Rust bindings for KMS + RVKMS Lyude Paul
@ 2024-03-22 22:03 ` Lyude Paul
  2024-03-27 20:50   ` Benno Lossin
  2024-03-22 22:03 ` [PATCH 2/4] WIP: drm: Introduce rvkms Lyude Paul
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 12+ messages in thread
From: Lyude Paul @ 2024-03-22 22:03 UTC (permalink / raw)
  To: dri-devel
  Cc: Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Asahi Lina, Martin Rodriguez Reboredo,
	FUJITA Tomonori, Danilo Krummrich, open list, open list:RUST

Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 rust/bindings/bindings_helper.h  |   4 +
 rust/helpers.c                   |  17 ++
 rust/kernel/drm/device.rs        |   2 +
 rust/kernel/drm/drv.rs           | 115 +++++++--
 rust/kernel/drm/kms.rs           | 146 +++++++++++
 rust/kernel/drm/kms/connector.rs | 404 +++++++++++++++++++++++++++++++
 rust/kernel/drm/kms/crtc.rs      | 300 +++++++++++++++++++++++
 rust/kernel/drm/kms/encoder.rs   | 175 +++++++++++++
 rust/kernel/drm/kms/plane.rs     | 300 +++++++++++++++++++++++
 rust/kernel/drm/mod.rs           |   1 +
 10 files changed, 1448 insertions(+), 16 deletions(-)
 create mode 100644 rust/kernel/drm/kms.rs
 create mode 100644 rust/kernel/drm/kms/connector.rs
 create mode 100644 rust/kernel/drm/kms/crtc.rs
 create mode 100644 rust/kernel/drm/kms/encoder.rs
 create mode 100644 rust/kernel/drm/kms/plane.rs

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index a712efecdb1a9..5856afbe6e8f6 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -6,12 +6,16 @@
  * Sorted alphabetically.
  */
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_file.h>
 #include <drm/drm_gem.h>
+#include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_gem_shmem_helper.h>
 #include <drm/drm_ioctl.h>
+#include <drm/drm_probe_helper.h>
 #include <kunit/test.h>
 #include <linux/device.h>
 #include <linux/dma-resv.h>
diff --git a/rust/helpers.c b/rust/helpers.c
index 69fc66164c785..bf9b299f4597f 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -20,6 +20,7 @@
  * Sorted alphabetically.
  */
 
+#include <drm/drm_connector.h>
 #include <drm/drm_gem.h>
 #include <drm/drm_gem_shmem_helper.h>
 #include <kunit/test-bug.h>
@@ -284,6 +285,22 @@ int rust_helper_drm_gem_shmem_object_mmap(struct drm_gem_object *obj, struct vm_
 EXPORT_SYMBOL_GPL(rust_helper_drm_gem_shmem_object_mmap);
 
 #endif
+
+#ifdef CONFIG_DRM_KMS_HELPER
+
+void rust_helper_drm_connector_get(struct drm_connector *connector)
+{
+	drm_connector_get(connector);
+}
+EXPORT_SYMBOL_GPL(rust_helper_drm_connector_get);
+
+void rust_helper_drm_connector_put(struct drm_connector *connector)
+{
+	drm_connector_put(connector);
+}
+EXPORT_SYMBOL_GPL(rust_helper_drm_connector_put);
+
+#endif /* CONFIG_DRM_KMS_HELPER */
 #endif
 
 void rust_helper_pci_set_drvdata(struct pci_dev *pdev, void *data)
diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs
index 6176e2e879d0b..07bc8ed50eae0 100644
--- a/rust/kernel/drm/device.rs
+++ b/rust/kernel/drm/device.rs
@@ -20,6 +20,8 @@ pub struct Device<T: drm::drv::Driver> {
 }
 
 impl<T: drm::drv::Driver> Device<T> {
+    pub const HAS_KMS: bool = T::FEATURES & drm::drv::FEAT_MODESET != 0;
+
     #[allow(dead_code, clippy::mut_from_ref)]
     pub(crate) unsafe fn raw_mut(&self) -> &mut bindings::drm_device {
         unsafe { &mut *self.drm.get() }
diff --git a/rust/kernel/drm/drv.rs b/rust/kernel/drm/drv.rs
index fa9ce64a5080c..308f0a117f546 100644
--- a/rust/kernel/drm/drv.rs
+++ b/rust/kernel/drm/drv.rs
@@ -5,9 +5,13 @@
 //! C header: [`include/linux/drm/drm_drv.h`](../../../../include/linux/drm/drm_drv.h)
 
 use crate::{
-    bindings, device, drm,
+    bindings, device,
+    drm::{
+        self,
+        kms,
+    },
     error::code::*,
-    error::from_err_ptr,
+    error::{from_err_ptr, to_result},
     error::{Error, Result},
     prelude::*,
     private::Sealed,
@@ -15,6 +19,7 @@
     types::{ARef, ForeignOwnable},
     ThisModule,
     sync::Arc,
+    init::Zeroable,
 };
 use core::{
     marker::{PhantomData, PhantomPinned},
@@ -150,7 +155,11 @@ pub trait Driver {
 /// The struct which contains both the driver's fops and vtable
 ///
 /// These live in the same structure since it needs to be self-referential, so having them in their
-/// own structure allows us to pin this struct without pinning the [`Registration`] object
+/// own structure allows us to pin this struct without pinning the [`Registration`] object.
+///
+/// Drivers should not need to create this structure themselves, as it will be created for them by
+/// DRM. As well: this object is a temporary holdover until we can generate the DRM fops and vtable
+/// in a const function (which should be possible once const mem::zeroed becomes stable).
 #[pin_data]
 pub struct DriverOps {
     #[pin]
@@ -225,8 +234,10 @@ macro_rules! drm_legacy_fields {
 #[allow(clippy::crate_in_macro_def)]
 #[macro_export]
 macro_rules! new_drm_registration {
-    ($type:ty, $parent:expr) => {{
-        $crate::drm::drv::Registration::<$type>::new($parent, &crate::THIS_MODULE)
+    ($type:ty, $parent:expr, $mode_config_info:expr) => {{
+        $crate::drm::drv::Registration::<$type>::new(
+            $parent, $mode_config_info, &crate::THIS_MODULE
+        )
     }};
 }
 
@@ -249,6 +260,8 @@ pub struct RegistrationInfo<T: Driver> {
     drm: ARef<drm::device::Device<T>>,
 }
 
+unsafe impl Zeroable for bindings::drm_mode_config { }
+
 impl<T: Driver> Registration<T> {
     const VTABLE: bindings::drm_driver = drm_legacy_fields! {
         load: None,
@@ -282,28 +295,89 @@ impl<T: Driver> Registration<T> {
         fops: core::ptr::null_mut(),
     };
 
+    const KMS_VTABLE: bindings::drm_mode_config_funcs = bindings::drm_mode_config_funcs {
+        atomic_check: None, // TODO
+        // TODO TODO: There are other possibilities then this function, but we need
+        // to write up more bindings before we can support those
+        fb_create: Some(bindings::drm_gem_fb_create),
+        mode_valid: None, // TODO
+        atomic_commit: Some(bindings::drm_atomic_helper_commit),
+        get_format_info: None,
+        atomic_state_free: None,
+        atomic_state_alloc: None,
+        atomic_state_clear: None,
+        output_poll_changed: None,
+    };
+
+    const KMS_HELPER_VTABLE: bindings::drm_mode_config_helper_funcs =
+        bindings::drm_mode_config_helper_funcs {
+            atomic_commit_setup: None, // TODO
+            atomic_commit_tail: None, // TODO
+        };
+
+    pub const HAS_KMS: bool = T::FEATURES & FEAT_MODESET != 0;
+
     /// Creates a new [`Registration`] but does not register it yet.
     ///
-    /// It is allowed to move.
-    /// XXX: Write up a macro for calling this, since we now handle as much init here as possible to
-    /// avoid having to handle it after we've moved away the Registration object
+    /// It is allowed to move. Note that `mode_confg_info` must be provided for a device to be
+    /// initialized with KMS.
     pub fn new(
         parent: &dyn device::RawDevice,
+        mode_config_info: Option<drm::kms::ModeConfigInfo>,
         module: &'static ThisModule,
     ) -> Result<Self> {
-        let registered = Arc::try_new(AtomicBool::new(false))?;
-        let ops = DriverOps::try_new(Self::VTABLE, module)?;
+        // mode_config_info must be passed for KMS drivers. We do this check up here so we don't
+        // have to worry about leaking raw_drm
+        // XXX: Would love to know a way to do this at compile-time instead…
+        if Self::HAS_KMS != mode_config_info.is_some() {
+            parent.pr_err(
+                if Self::HAS_KMS {
+                    format_args!("KMS drivers must specify mode_config_info for new devices")
+                } else {
+                    format_args!("mode_config_info is only for KMS drivers (see drm::drv::Driver::FEATURES)")
+                }
+            );
 
-        let raw_drm = unsafe { bindings::drm_dev_alloc(&ops.vtable, parent.raw_device()) };
-        if T::FEATURES & FEAT_MODESET != 0 {
-            unsafe { bindings::drmm_mode_config_init(raw_drm) };
+            return Err(EINVAL);
         }
 
-        let raw_drm = NonNull::new(from_err_ptr(raw_drm)? as *mut _).ok_or(ENOMEM)?;
+        let registered = Arc::try_new(AtomicBool::new(false))?;
+        let ops = DriverOps::try_new(Self::VTABLE, module)?;
+
+        // SAFETY: FFI call with no special requirements
+        let raw_drm: NonNull<Self> =
+            from_err_ptr(unsafe { bindings::drm_dev_alloc(&ops.vtable, parent.raw_device()) })
+            .and_then(|p| NonNull::new(p).ok_or(ENOMEM))?
+            .cast();
 
         // The reference count is one, and now we take ownership of that reference as a
         // drm::device::Device.
-        let drm = unsafe { ARef::from_raw(raw_drm) };
+        let drm: ARef<drm::device::Device<T>> = unsafe { ARef::from_raw(raw_drm.cast()) };
+
+        // Finally, setup KMS - we do this at the end to avoid leaking raw_drm if something fails
+        if Self::HAS_KMS {
+            // SAFETY: We made sure at the start of this function that mode_config_info is Some
+            let mode_config_info = unsafe { mode_config_info.unwrap_unchecked() };
+
+            // SAFETY: We just allocated the device, and it's safe to zero-initialize this
+            unsafe {
+                (*drm.drm.get()).mode_config = bindings::drm_mode_config {
+                    funcs: &Self::KMS_VTABLE,
+                    helper_private: &Self::KMS_HELPER_VTABLE,
+                    min_width: mode_config_info.min_resolution.0,
+                    min_height: mode_config_info.min_resolution.1,
+                    max_width: mode_config_info.max_resolution.0,
+                    max_height: mode_config_info.max_resolution.1,
+                    cursor_width: mode_config_info.max_cursor.0,
+                    cursor_height: mode_config_info.max_cursor.1,
+                    preferred_depth: mode_config_info.preferred_depth,
+                    ..Default::default()
+                }
+            };
+
+            // SAFETY: FFI call with no special requirements
+            unsafe { to_result(bindings::drmm_mode_config_init(drm.drm.get())) }?;
+        }
 
         Ok(Self {
             drm,
@@ -324,6 +398,8 @@ pub fn registration_info(&self) -> RegistrationInfo<T> {
 
     /// Registers a DRM device with the rest of the kernel.
     ///
+    /// For KMS drivers, this also calls `bindings::drm_mode_config_reset()` before registration.
+    ///
     /// Users are encouraged to use the [`drm_device_register!()`] macro because it automatically
     /// picks up the current module.
     pub fn register(
@@ -390,7 +466,14 @@ fn drop(&mut self) {
             let data_pointer = unsafe { self.drm.raw_mut().dev_private };
 
             // SAFETY: Since `registered` is true, `self.drm` is both valid and registered.
-            unsafe { bindings::drm_dev_unregister(self.drm.raw_mut()) };
+            unsafe {
+                let raw_drm = self.drm.raw_mut();
+
+                bindings::drm_dev_unregister(raw_drm);
+                if Self::HAS_KMS {
+                    bindings::drm_atomic_helper_shutdown(raw_drm);
+                }
+            };
 
             // Free data as well.
             // SAFETY: `data_pointer` was returned by `into_foreign` during registration.
diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs
new file mode 100644
index 0000000000000..b55d14415367a
--- /dev/null
+++ b/rust/kernel/drm/kms.rs
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! KMS driver abstractions for rust.
+
+pub mod connector;
+pub mod crtc;
+pub mod encoder;
+pub mod plane;
+
+use crate::{
+    drm::{drv, device::Device},
+    prelude::*,
+    types::ARef,
+    private::Sealed
+};
+use core::{
+    ops::Deref,
+    ptr,
+};
+use bindings;
+
+#[derive(Copy, Clone)]
+pub struct ModeConfigInfo {
+    /// The minimum (w, h) resolution this driver can support
+    pub min_resolution: (i32, i32),
+    /// The maximum (w, h) resolution this driver can support
+    pub max_resolution: (i32, i32),
+    /// The maximum (w, h) cursor size this driver can support
+    pub max_cursor: (u32, u32),
+    /// The preferred depth for dumb ioctls
+    pub preferred_depth: u32,
+}
+
+// TODO: I am not totally sure about this. Ideally, I'd like a nice way of hiding KMS-specific
+// functions for DRM drivers which don't implement KMS - so that we don't have to have a bunch of
+// random modesetting functions all over the DRM device trait. But, unfortunately I don't know of
+// any nice way of doing that yet :(
+
+/// An atomic KMS driver implementation
+pub trait KmsDriver: drv::Driver { }
+
+impl<T: KmsDriver> Device<T> {
+    pub fn mode_config_reset(&self) {
+        // SAFETY: The previous build assertion ensures this can only be called for devices with KMS
+        // support, which means mode_config is initialized
+        unsafe { bindings::drm_mode_config_reset(self.drm.get()) }
+    }
+}
+
+/// Main trait for a modesetting object in DRM
+pub trait ModeObject: Sealed + Send + Sync {
+    /// The parent driver for this ModeObject
+    type Driver: KmsDriver;
+
+    /// Return the `drv::Device` for this `ModeObject`
+    fn drm_dev(&self) -> &Device<Self::Driver>;
+}
+
+/// A trait for modesetting objects which don't come with their own reference-counting.
+///
+/// Objects without a reference count share the lifetime of their parent DRM device
+///
+/// SAFETY: This trait must not be implemented for modesetting objects which have a refcount
+/// already, as otherwise `KmsRef` can't safely guarantee the object will stay alive.
+pub unsafe trait StaticModeObject: ModeObject {}
+
+/// An owned reference to a non-reference counted modesetting object.
+///
+/// In KMS: some modesetting objects aren't reference counted and instead share the drm device's
+/// lifetime. In order to allow rust drivers access to "owned" references to objects which are
+/// guaranteed to remain valid, we provide a smart-pointer that holds both a pointer to the
+/// modesetting object, and an owned refcount from its owning device - ensuring that the object
+/// remains valid for as long as this reference exists.
+pub struct KmsRef<T: StaticModeObject> {
+    dev: ARef<Device<T::Driver>>,
+    object: *const T,
+}
+
+// SAFETY: Owned references to DRM device are thread-safe, and object will survive as long as we
+// have said owned references
+unsafe impl<T: StaticModeObject> Send for KmsRef<T> {}
+unsafe impl<T: StaticModeObject> Sync for KmsRef<T> {}
+
+impl<T: StaticModeObject> From<&T> for KmsRef<T> {
+    fn from(value: &T) -> Self {
+        Self {
+            dev: value.drm_dev().into(),
+            object: value,
+        }
+    }
+}
+
+impl<T: StaticModeObject> Deref for KmsRef<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        // SAFETY: We're guaranteed object will point to a valid object as long as we hold dev
+        unsafe { &(*self.object) }
+    }
+}
+
+impl<T: StaticModeObject> Clone for KmsRef<T> {
+    fn clone(&self) -> Self {
+        Self {
+            dev: self.dev.clone(),
+            object: self.object
+        }
+    }
+}
+
+/// A mode config guard.
+///
+/// This is an exclusive primitive that represents when `bindings::drm_device.mode_config.lock` is
+/// held - as some modesetting operations (particularly ones related to connectors) are still
+/// protected under this single lock. So long as this object is held, it is guaranteed that the lock
+/// is held.
+pub struct ModeConfigGuard<'a, T: KmsDriver> {
+    owner: &'a Device<T>,
+    owned: bool,
+}
+
+impl<'a, T: KmsDriver> ModeConfigGuard<'a, T> {
+    /// Create a "borrowed" mode config guard.
+    ///
+    /// This is primarily for situations in the DRM bindings where we know that the mode_config lock
+    /// is held, but we aren't the ones who initially acquired it. Dropping this mode config guard
+    /// is a no-op.
+    ///
+    /// SAFETY: The caller must ensure that the mode_config lock is acquired throughout the lifetime
+    /// of this object.
+    unsafe fn new_borrowed(dev: &'a Device<T>) -> Self {
+        Self {
+            owner: dev,
+            owned: false,
+        }
+    }
+
+    /// Assert that the given device is the owner of this mode config guard.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `dev` is different from the owning device for this mode config guard.
+    pub(crate) fn assert_owner(&self, dev: &Device<T>) {
+        assert!(ptr::eq(self.owner, dev))
+    }
+}
diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs
new file mode 100644
index 0000000000000..88dfa946d306b
--- /dev/null
+++ b/rust/kernel/drm/kms/connector.rs
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! Rust bindings for DRM connectors
+
+use crate::{
+    bindings,
+    sync::ArcBorrow,
+    drm::{
+        drv::{Driver, FEAT_MODESET},
+        device::Device,
+    },
+    types::{AlwaysRefCounted, Opaque, ARef},
+    prelude::*,
+    init::Zeroable,
+    error::{to_result, from_result},
+    build_error,
+};
+use core::{
+    marker::PhantomPinned,
+    ptr::null_mut,
+    mem,
+    ptr::{self, NonNull},
+    ffi::*,
+    ops::Deref,
+};
+use super::{
+    ModeObject,
+    ModeConfigGuard,
+    encoder::{Encoder, DriverEncoder},
+    KmsDriver,
+};
+use macros::pin_data;
+
+// XXX: This is :\, figure out a better way at some point?
+pub use bindings::{
+    DRM_MODE_CONNECTOR_Unknown,
+    DRM_MODE_CONNECTOR_VGA,
+    DRM_MODE_CONNECTOR_DVII,
+    DRM_MODE_CONNECTOR_DVID,
+    DRM_MODE_CONNECTOR_DVIA,
+    DRM_MODE_CONNECTOR_Composite,
+    DRM_MODE_CONNECTOR_SVIDEO,
+    DRM_MODE_CONNECTOR_LVDS,
+    DRM_MODE_CONNECTOR_Component,
+    DRM_MODE_CONNECTOR_9PinDIN,
+    DRM_MODE_CONNECTOR_DisplayPort,
+    DRM_MODE_CONNECTOR_HDMIA,
+    DRM_MODE_CONNECTOR_HDMIB,
+    DRM_MODE_CONNECTOR_TV,
+    DRM_MODE_CONNECTOR_eDP,
+    DRM_MODE_CONNECTOR_VIRTUAL,
+    DRM_MODE_CONNECTOR_DSI,
+    DRM_MODE_CONNECTOR_DPI,
+    DRM_MODE_CONNECTOR_WRITEBACK,
+    DRM_MODE_CONNECTOR_SPI,
+    DRM_MODE_CONNECTOR_USB,
+};
+
+/// A DRM connector implementation
+pub trait DriverConnector: Send + Sync + Sized {
+    /// The return type of the new() function. Should be `impl PinInit<Self, Error>`.
+    /// TODO: Remove this when return_position_impl_trait_in_trait is stable.
+    type Initializer: PinInit<Self, Error>;
+
+    /// The data type to use for passing incoming arguments for new `Connector<T>` instances
+    /// Drivers which don't care about this can just use `()`
+    type Args;
+
+    /// The parent driver for this DRM connector implementation
+    type Driver: KmsDriver;
+
+    /// The atomic state implementation for this DRM connector implementation
+    type State: DriverConnectorState;
+
+    /// Create a new instance of the private driver data struct for this connector in-place
+    fn new(dev: &Device<Self::Driver>, args: Self::Args) -> Self::Initializer;
+
+    /// Retrieve a list of available display modes for this connector
+    fn get_modes<'a>(
+        connector: ConnectorGuard<'a, Self>,
+        guard: &ModeConfigGuard<'a, Self::Driver>
+    ) -> i32;
+}
+
+/// A DRM connector
+#[repr(C)]
+#[pin_data]
+pub struct Connector<T: DriverConnector> {
+    connector: Opaque<bindings::drm_connector>,
+    #[pin]
+    inner: T,
+    #[pin]
+    _p: PhantomPinned
+}
+
+impl<T: DriverConnector> crate::private::Sealed for Connector<T> { }
+
+// SAFETY: DRM expects this struct to be zero-initialized
+unsafe impl Zeroable for bindings::drm_connector { }
+
+// SAFETY: Connector.connector is not exposed to users by default, and our accessors ensure we only
+// perform thread-safe operations for this object
+unsafe impl<T: DriverConnector> Send for Connector<T> { }
+unsafe impl<T: DriverConnector> Sync for Connector<T> { }
+
+unsafe impl<T: DriverConnector> AlwaysRefCounted for Connector<T> {
+    fn inc_ref(&self) {
+        unsafe { bindings::drm_connector_get(self.raw_mut_ptr()) }
+    }
+
+    unsafe fn dec_ref(obj: core::ptr::NonNull<Self>) {
+        unsafe { bindings::drm_connector_put(obj.as_ref().raw_mut_ptr()) }
+    }
+}
+
+impl<T: DriverConnector> Deref for Connector<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl<T: DriverConnector> ModeObject for Connector<T> {
+    type Driver = T::Driver;
+
+    fn drm_dev(&self) -> &Device<Self::Driver> {
+        // SAFETY: DRM connectors exist for as long as the device does, so this pointer is always
+        // valid
+        unsafe { &*((*self.raw_mut_ptr()).dev.cast()) }
+    }
+}
+
+impl<T: DriverConnector> Connector<T> {
+    const FUNCS: bindings::drm_connector_funcs = bindings::drm_connector_funcs {
+        dpms: None,
+        atomic_get_property: None,
+        atomic_set_property: None,
+        early_unregister: None,
+        late_register: None,
+        set_property: None,
+        reset: Some(connector_reset_callback::<T::State>),
+        atomic_print_state: None,
+        atomic_destroy_state: Some(atomic_destroy_state_callback::<T::State>),
+        destroy: Some(connector_destroy_callback::<T>),
+        force: None,
+        detect: None,
+        fill_modes: Some(bindings::drm_helper_probe_single_connector_modes),
+        debugfs_init: None,
+        oob_hotplug_event: None,
+        atomic_duplicate_state: Some(atomic_duplicate_state_callback::<T::State>),
+    };
+
+    const HELPER_FUNCS: bindings::drm_connector_helper_funcs = bindings::drm_connector_helper_funcs {
+        mode_valid: None,
+        atomic_check: None,
+        get_modes: Some(get_modes_callback::<T>),
+        detect_ctx: None,
+        enable_hpd: None,
+        disable_hpd: None,
+        best_encoder: None,
+        atomic_commit: None,
+        mode_valid_ctx: None,
+        atomic_best_encoder: None,
+        prepare_writeback_job: None,
+        cleanup_writeback_job: None,
+    };
+
+    pub fn new(
+        dev: &Device<T::Driver>,
+        type_: u32,
+        args: T::Args,
+    ) -> Result<ARef<Self>> {
+        let new: Pin<Box<Self>> = Box::try_pin_init(try_pin_init!(Self {
+            connector: Opaque::new(bindings::drm_connector {
+                helper_private: &Self::HELPER_FUNCS,
+                ..Default::default()
+            }),
+            inner <- T::new(dev, args),
+            _p: PhantomPinned
+        }))?;
+
+        // SAFETY: FFI call with no special safety requirements
+        to_result(unsafe {
+            bindings::drm_connector_init(
+                dev.drm.get(),
+                new.raw_mut_ptr(),
+                &Self::FUNCS,
+                type_ as i32
+            )
+        })?;
+
+        // Convert the connector into an ARef so the caller has proper ownership over a refcount to
+        // it. Also, the Box we consume here will be reconstructed in connector_destroy_callback()
+        // once the connector's refcount drops to zero.
+        // SAFETY: We currently hold ownership of the Box containing the connector and it's
+        // refcount. As well, this operation will not move the contents of the Box.
+        Ok(unsafe {
+            ARef::from_raw(NonNull::new_unchecked(Box::into_raw(Pin::into_inner_unchecked(new))))
+        })
+    }
+
+    pub(super) unsafe fn raw_mut_ptr(&self) -> *mut bindings::drm_connector {
+        self.connector.get()
+    }
+
+    pub(super) unsafe fn from_raw_ptr<'a>(ptr: *const bindings::drm_connector) -> &'a Self {
+        unsafe { &*(ptr as *mut Self) }
+    }
+
+    /// Acquire a `ConnectorGuard` for this connector from a `ModeConfigGuard`.
+    ///
+    /// This verifies using the provided reference that the given guard is actually for the same
+    /// device as this connector's parent.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `guard` is not a mode config guard for this connector's parent DRM device
+    pub fn guard<'a>(&'a self, guard: &ModeConfigGuard<'a, T::Driver>) -> ConnectorGuard<'a, T> {
+        guard.assert_owner(self.drm_dev());
+        ConnectorGuard { connector: self }
+    }
+
+    /// Attach an encoder to this connector, this should only be done before registration
+    #[must_use]
+    pub fn attach_encoder<E: DriverEncoder>(&self, encoder: &Encoder<E>) -> Result {
+        // SAFETY: FFI call with no special requirements
+        to_result(unsafe {
+            bindings::drm_connector_attach_encoder(self.raw_mut_ptr(), encoder.raw_mut_ptr())
+        })
+    }
+}
+
+unsafe extern "C" fn connector_destroy_callback<T: DriverConnector>(
+    connector: *mut bindings::drm_connector,
+) {
+    // SAFETY: Connector has to point to a valid drm_connector, as its function table is the only
+    // existing entrypoint to this function
+    unsafe {
+        bindings::drm_connector_unregister(connector);
+        bindings::drm_connector_cleanup(connector);
+    };
+
+    // SAFETY: We always create connectors in boxes, and since we are running from this connector's
+    // destructor we are guaranteed to have the last remaining reference. Furthermore, we're
+    // guaranteed by type invariance that the contents of the box are of type Connector<T>.
+    unsafe { drop(Box::from_raw(connector as *mut Connector<T>)) };
+}
+
+unsafe extern "C" fn get_modes_callback<T: DriverConnector>(
+    connector: *mut bindings::drm_connector,
+) -> c_int {
+    // SAFETY: We're guaranteed by type invariants that connector is of type Connector<T>, and
+    // connector must point to a valid instance of Connector<T> as it's the only entry-point to this
+    // callback.
+    let connector = unsafe { Connector::<T>::from_raw_ptr(connector) };
+
+    // SAFETY: This FFI callback is only called while the mode config lock is held
+    let guard = unsafe { ModeConfigGuard::new_borrowed(connector.drm_dev()) };
+
+    T::get_modes(connector.guard(&guard), &guard)
+}
+
+/// A privileged smart-pointer for `Connector<T>` which proves that the owner currently is protected
+/// under the `bindings::drm_device.mode_config.mutex` lock and provides access to data and methods
+/// protected under said lock.
+#[derive(Copy, Clone)]
+pub struct ConnectorGuard<'a, T: DriverConnector> {
+    connector: &'a Connector<T>,
+}
+
+impl<T: DriverConnector> Deref for ConnectorGuard<'_, T> {
+    type Target = Connector<T>;
+
+    fn deref(&self) -> &Self::Target {
+        self.connector
+    }
+}
+
+impl<'a, T: DriverConnector> ConnectorGuard<'a, T> {
+    pub fn add_modes_noedid(&self, (max_h, max_v): (i32, i32)) -> i32 {
+        unsafe { bindings::drm_add_modes_noedid(self.raw_mut_ptr(), max_h, max_v) }
+    }
+
+    pub fn set_preferred_mode(&self, (h_pref, w_pref): (i32, i32)) {
+        unsafe { bindings::drm_set_preferred_mode(self.raw_mut_ptr(), h_pref, w_pref) }
+    }
+}
+
+#[derive(Default)]
+#[repr(C)]
+pub struct ConnectorState<T: DriverConnectorState> {
+    state: bindings::drm_connector_state,
+    inner: T,
+}
+
+/// The trait for a driver's atomic DRM connector state
+pub trait DriverConnectorState: Clone + Default + Sized {
+    type Connector: DriverConnector;
+}
+
+impl<T: DriverConnectorState> ConnectorState<T> {
+    /// Consume this struct without dropping it, and return a pointer to its base
+    /// `drm_connector_state` which can be handed off to DRM.
+    fn into_raw(self: Box<Self>) -> *mut bindings::drm_connector_state {
+        let this = Box::into_raw(self);
+
+        unsafe { &mut (*this).state }
+    }
+
+    /// Consume a raw pointer and recover the original `Box<ConnectorState<T>>`
+    ///
+    /// SAFETY: Callers must ensure that ptr contains a valid instance of `ConnectorState<T>`
+    unsafe fn from_raw(ptr: *mut bindings::drm_connector_state) -> Box<Self> {
+        unsafe { Box::from_raw(ptr as *mut _) }
+    }
+
+    /// Obtain a reference back to the `ConnectorState<T>`
+    ///
+    /// SAFETY: Callers must ensure that ptr contains a valid instance of `ConnectorState<T>`.
+    unsafe fn as_ref<'a>(ptr: *const bindings::drm_connector_state) -> &'a Self {
+        unsafe { &*(ptr as *const _) }
+    }
+
+    /// Obtain a mutable reference back to the ConnectorState<T>
+    ///
+    /// SAFETY: Callers must ensure that ptr contains a valid instance of `ConnectorState<T>`, and
+    /// that no other references to this `ConnectorState<T>` exist for the lifetime of this
+    /// reference
+    unsafe fn as_mut_ref<'a>(ptr: *mut bindings::drm_connector_state) -> &'a mut Self {
+        unsafe { &mut *(ptr as *mut _) }
+    }
+
+    /// Obtain a mutable pointer to the base connector state, for use in FFI calls
+    unsafe fn as_mut_ptr(&mut self) -> *mut bindings::drm_connector_state {
+        &mut self.state
+    }
+}
+
+unsafe impl Zeroable for bindings::drm_connector_state {}
+
+unsafe extern "C" fn atomic_duplicate_state_callback<T: DriverConnectorState>(
+    connector: *mut bindings::drm_connector
+) -> *mut bindings::drm_connector_state
+{
+    // SAFETY: `connector` has to point to a valid instance of drm_connector, since it holds the vtable for
+    // this function - which is the only possible entrypoint the caller could have used
+    let state = unsafe { (*connector).state };
+    if state.is_null() {
+        return null_mut();
+    }
+
+    // SAFETY: We just verified that `state` is non-null, and we're guaranteed by our bindings that
+    // `state` is of type `ConnectorState<T>`.
+    let state = unsafe { ConnectorState::<T>::as_ref(state) };
+
+    let mut new: Result<Box<ConnectorState<T>>> = Box::try_init(try_init!(ConnectorState::<T> {
+        state: bindings::drm_connector_state { ..Default::default() },
+        inner: state.inner.clone()
+    }));
+
+    if let Ok(mut new) = new {
+        // SAFETY: Just a lil' FFI call, nothing special here
+        unsafe {
+            bindings::__drm_atomic_helper_connector_duplicate_state(connector, new.as_mut_ptr())
+        };
+
+        new.into_raw()
+    } else {
+        null_mut()
+    }
+}
+
+unsafe extern "C" fn atomic_destroy_state_callback<T: DriverConnectorState>(
+    _connector: *mut bindings::drm_connector,
+    connector_state: *mut bindings::drm_connector_state
+) {
+    // SAFETY: This callback wouldn't be called unless there a connector state to destroy
+    unsafe { bindings::__drm_atomic_helper_connector_destroy_state(connector_state) };
+
+    // SAFETY: We're guaranteed by type invariants that connector_state is of type
+    // ConnectorState<T>, and since this is the destructor callback for DRM - we're guaranteed to
+    // hold the only remaining reference to this state
+    unsafe { drop(ConnectorState::<T>::from_raw(connector_state)) };
+}
+
+unsafe extern "C" fn connector_reset_callback<T: DriverConnectorState>(
+    connector: *mut bindings::drm_connector,
+) {
+    // SAFETY: The only entrypoint to this function lives in `connector` so it must be valid, and
+    let state = unsafe { (*connector).state };
+    if !state.is_null() {
+        // SAFETY: We're guaranteed by type invariance that this connector's state is of type
+        // DriverConnectorState<T>
+        unsafe { atomic_destroy_state_callback::<T>(connector, state) }
+    }
+
+    // Unfortunately, this is the best we can do at the moment as this FFI callback was mistakenly
+    // presumed to be infallible :(
+    let new = Box::try_new(ConnectorState::<T>::default()).expect("Blame the API, sorry!");
+
+    // SAFETY: DRM takes ownership of the state from here and assigns it to the connector
+    unsafe { bindings::__drm_atomic_helper_connector_reset(connector, new.into_raw()) };
+}
diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs
new file mode 100644
index 0000000000000..3d072028a4884
--- /dev/null
+++ b/rust/kernel/drm/kms/crtc.rs
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! KMS driver abstractions for rust.
+
+use super::{
+    plane::*,
+    ModeObject,
+    StaticModeObject,
+    KmsDriver
+};
+use crate::{
+    bindings,
+    drm::{drv::Driver, device::Device},
+    device,
+    prelude::*,
+    types::Opaque,
+    init::Zeroable,
+    sync::Arc,
+    error::to_result,
+};
+use core::{
+    cell::UnsafeCell,
+    marker::PhantomPinned,
+    ptr::{null, null_mut},
+    ops::Deref,
+};
+use macros::vtable;
+
+/// A typed KMS CRTC with a specific driver.
+#[repr(C)]
+#[pin_data]
+pub struct Crtc<T: DriverCrtc> {
+    // The FFI drm_crtc object
+    pub(super) crtc: Opaque<bindings::drm_crtc>,
+    /// The driver's private inner data
+    #[pin]
+    inner: T,
+    #[pin]
+    _p: PhantomPinned,
+}
+
+/// KMS CRTC object functions, which must be implemented by drivers.
+pub trait DriverCrtc: Send + Sync + Sized {
+    /// The return type of the new() function. Should be `impl PinInit<Self, Error>`.
+    /// TODO: Remove this when return_position_impl_trait_in_trait is stable.
+    type Initializer: PinInit<Self, Error>;
+
+    /// The data type to use for passing incoming arguments for new `Crtc<T>` instances
+    /// Drivers which don't care about this can just use `()`
+    type Args;
+
+    /// The parent driver implementation
+    type Driver: KmsDriver;
+
+    /// The type for this driver's drm_crtc_state implementation
+    type State: DriverCrtcState;
+
+    /// Create a new CRTC for this driver
+    fn new(device: &Device<Self::Driver>, args: Self::Args) -> Self::Initializer;
+}
+
+unsafe impl Zeroable for bindings::drm_crtc { }
+
+impl<T: DriverCrtc> crate::private::Sealed for Crtc<T> {}
+
+// SAFETY: Crtc.crtc is not exposed to users by default, and our accessors ensure we only perform
+// thread-safe operations for this object
+unsafe impl<T: DriverCrtc> Send for Crtc<T> { }
+unsafe impl<T: DriverCrtc> Sync for Crtc<T> { }
+
+impl<T: DriverCrtc> Deref for Crtc<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl<T: DriverCrtc> ModeObject for Crtc<T> {
+    type Driver = T::Driver;
+
+    fn drm_dev(&self) -> &Device<Self::Driver> {
+        // SAFETY: DRM connectors exist for as long as the device does, so this pointer is always
+        // valid
+        unsafe { &*((*self.raw_mut_ptr()).dev as *const _) }
+    }
+}
+
+// SAFETY: CRTCs are non-refcounted modesetting objects
+unsafe impl<T: DriverCrtc> StaticModeObject for Crtc<T> { }
+
+impl<T: DriverCrtc> Crtc<T> {
+    /// The actual C vtable for drm_crtc_funcs
+    const FUNCS: bindings::drm_crtc_funcs = bindings::drm_crtc_funcs {
+        atomic_destroy_state: Some(atomic_destroy_state_callback::<T::State>),
+        atomic_duplicate_state: Some(atomic_duplicate_state_callback::<T::State>),
+        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::<T>),
+        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::<T::State>),
+        set_config: Some(bindings::drm_atomic_helper_set_config),
+        set_crc_source: None,
+        set_property: None,
+        verify_crc_source: None,
+    };
+
+    /// The actual C vtable for drm_crtc_helper_funcs
+    const HELPER_FUNCS: bindings::drm_crtc_helper_funcs = bindings::drm_crtc_helper_funcs {
+        atomic_disable: None,
+        atomic_enable: None,
+        atomic_check: None,
+        dpms: None,
+        commit: None,
+        prepare: None,
+        disable: None,
+        mode_set: None,
+        mode_valid: None,
+        mode_fixup: None,
+        atomic_begin: None,
+        atomic_flush: None,
+        mode_set_nofb: None,
+        mode_set_base: None,
+        mode_set_base_atomic: None,
+        get_scanout_position: None,
+    };
+
+    pub fn new<'a, P, C>(
+        dev: &'a Device<T::Driver>,
+        primary: &'a Plane<P>,
+        cursor: Option<&'a Plane<C>>,
+        name: Option<&CStr>,
+        args: T::Args,
+    ) -> Result<&'a Self>
+    where
+        P: DriverPlane,
+        C: DriverPlane
+    {
+        let this = Box::try_pin_init(try_pin_init!(Self {
+            crtc: Opaque::new(bindings::drm_crtc {
+                helper_private: &Self::HELPER_FUNCS,
+                ..Default::default()
+            }),
+            inner <- T::new(dev, args),
+            _p: PhantomPinned,
+        }))?;
+
+        to_result(unsafe {
+            bindings::drm_crtc_init_with_planes(
+                dev.drm.get(),
+                this.raw_mut_ptr(),
+                primary.raw_mut_ptr(),
+                cursor.map_or(null_mut(), |c| c.raw_mut_ptr()),
+                &Self::FUNCS,
+                name.map_or(null(), |n| n.as_char_ptr())
+            )
+        })?;
+
+        // Convert the box into a raw pointer, we'll re-assemble it in crtc_destroy_callback()
+        // SAFETY: We don't move anything
+        Ok(unsafe { &*Box::into_raw(Pin::into_inner_unchecked(this)) })
+    }
+
+    pub(super) fn raw_mut_ptr(&self) -> *mut bindings::drm_crtc {
+        self.crtc.get()
+    }
+}
+
+unsafe extern "C" fn crtc_destroy_callback<T: DriverCrtc>(
+    crtc: *mut bindings::drm_crtc
+) {
+    // SAFETY: FFI call with no special requirements
+    unsafe { bindings::drm_crtc_cleanup(crtc) };
+
+    // SAFETY: We're guaranteed by type invariants this plane is contained within an Box<Crtc<T>>
+    unsafe { drop(Box::from_raw(crtc as *mut Crtc<T>)) };
+}
+
+unsafe impl Zeroable for bindings::drm_crtc_state { }
+
+pub trait DriverCrtcState: Clone + Default + Sized {
+    type Crtc: DriverCrtc;
+}
+
+#[derive(Default)]
+#[repr(C)]
+pub struct CrtcState<T: DriverCrtcState> {
+    state: bindings::drm_crtc_state,
+    inner: T,
+}
+
+impl<T: DriverCrtcState> CrtcState<T> {
+    /// Consume this struct without dropping it, and return a pointer to its base `drm_crtc_state`
+    /// which can be handed off to DRM.
+    fn into_raw(self: Box<Self>) -> *mut bindings::drm_crtc_state {
+        let this = Box::into_raw(self);
+
+        unsafe { &mut (*this).state }
+    }
+
+    /// Consume a raw pointer and recover the original `Box<CrtcState<T>>`
+    ///
+    /// SAFETY: Callers must ensure that ptr contains a valid instance of `CrtcState<T>`
+    unsafe fn from_raw(ptr: *mut bindings::drm_crtc_state) -> Box<Self> {
+        unsafe { Box::from_raw(ptr as *mut _) }
+    }
+
+    /// Obtain a reference back to the `CrtcState<T>`
+    ///
+    /// SAFETY: Callers must ensure that ptr contains a valid instance of `CrtcState<T>`.
+    unsafe fn as_ref<'a>(ptr: *const bindings::drm_crtc_state) -> &'a Self {
+        unsafe { &*(ptr as *const _) }
+    }
+
+    /// Obtain a mutable reference back to the CrtcState<T>
+    ///
+    /// SAFETY: Callers must ensure that ptr contains a valid instance of `CrtcState<T>`, and that
+    /// no other references to this `CrtcState<T>` exist for the lifetime of this reference
+    unsafe fn as_mut_ref<'a>(ptr: *mut bindings::drm_crtc_state) -> &'a mut Self {
+        unsafe { &mut *(ptr as *mut _) }
+    }
+
+    /// Obtain a mutable pointer to the base plane state, for use in FFI calls
+    unsafe fn as_mut_ptr(&mut self) -> *mut bindings::drm_crtc_state {
+        &mut self.state
+    }
+}
+
+unsafe extern "C" fn atomic_duplicate_state_callback<T: DriverCrtcState>(
+    crtc: *mut bindings::drm_crtc
+) -> *mut bindings::drm_crtc_state {
+    // SAFETY: `crtc` has to point to a valid instance of drm_crtc, since it holds the vtable for
+    // this function - which is the only possible entrypoint the caller could have used
+    let state = unsafe { (*crtc).state };
+    if state.is_null() {
+        return null_mut();
+    }
+
+    // SAFETY: We just verified that `state` is non-null, and we're guaranteed by our bindings that
+    // `state` is of type `CrtcState<T>`.
+    let state = unsafe { CrtcState::<T>::as_ref(state) };
+
+    let mut new: Result<Box<CrtcState<T>>> = Box::try_init(try_init!(CrtcState::<T> {
+        state: bindings::drm_crtc_state { ..Default::default() },
+        inner: state.inner.clone()
+    }));
+
+    if let Ok(mut new) = new {
+        unsafe { bindings::__drm_atomic_helper_crtc_duplicate_state(crtc, new.as_mut_ptr()) }
+
+        new.into_raw()
+    } else {
+        null_mut()
+    }
+}
+
+unsafe extern "C" fn atomic_destroy_state_callback<T: DriverCrtcState>(
+    _crtc: *mut bindings::drm_crtc,
+    crtc_state: *mut bindings::drm_crtc_state,
+) {
+    // SAFETY: This callback wouldn't be called unless there a CRTC state to destroy
+    unsafe { bindings::__drm_atomic_helper_crtc_destroy_state(crtc_state) };
+    //
+    // SAFETY: We're guaranteed by type invariants that crtc_state is of type CrtcState<T>, and
+    // since this is the destructor callback for DRM - we're guaranteed to hold the only remaining
+    // reference to this state
+    drop(unsafe { CrtcState::<T>::from_raw(crtc_state) });
+}
+
+unsafe extern "C" fn crtc_reset_callback<T: DriverCrtcState>(
+    crtc: *mut bindings::drm_crtc,
+) {
+    // SAFETY: The only entrypoint to this function lives in `crtc` so it must be valid, and
+    let state = unsafe { (*crtc).state };
+    if !state.is_null() {
+        // SAFETY: We're guaranteed by type invariance that this crtc's state is of type
+        // DriverConnectorState<T>
+        unsafe { atomic_destroy_state_callback::<T>(crtc, state) }
+    }
+
+    // Unfortunately, this is the best we can do at the moment as this FFI callback was mistakenly
+    // presumed to be infallible :(
+    let new = Box::try_new(CrtcState::<T>::default()).expect("Blame the API, sorry!");
+
+    // SAFETY: DRM takes ownership of the state from here and assigns it to the crtc
+    unsafe { bindings::__drm_atomic_helper_crtc_reset(crtc, new.into_raw()) };
+}
diff --git a/rust/kernel/drm/kms/encoder.rs b/rust/kernel/drm/kms/encoder.rs
new file mode 100644
index 0000000000000..7a5bc0ca1577b
--- /dev/null
+++ b/rust/kernel/drm/kms/encoder.rs
@@ -0,0 +1,175 @@
+// TODO: License stuff
+
+//! drm_encoder abstractions for rust
+
+use crate::{
+    drm::{
+        device::Device,
+        drv::Driver,
+    },
+    prelude::*,
+    sync::Arc,
+    types::Opaque,
+    init::Zeroable,
+    error::to_result,
+};
+use core::{
+    marker::PhantomPinned,
+    ptr::null,
+    ops::Deref,
+};
+use super::{ModeObject, StaticModeObject, KmsDriver};
+use bindings;
+
+pub use bindings::{
+    DRM_MODE_ENCODER_NONE,
+    DRM_MODE_ENCODER_DAC,
+    DRM_MODE_ENCODER_TMDS,
+    DRM_MODE_ENCODER_LVDS,
+    DRM_MODE_ENCODER_TVDAC,
+    DRM_MODE_ENCODER_VIRTUAL,
+    DRM_MODE_ENCODER_DSI,
+    DRM_MODE_ENCODER_DPMST,
+    DRM_MODE_ENCODER_DPI,
+};
+
+/// A DRM encoder (`drm_encoder`)
+///
+/// This is the main struct for DRM encoders, which may also hold any private data specified by the
+/// driver.
+#[repr(C)]
+#[pin_data]
+pub struct Encoder<T: DriverEncoder> {
+    /// The FFI drm_encoder object
+    encoder: Opaque<bindings::drm_encoder>,
+    /// The driver's private inner data
+    #[pin]
+    inner: T,
+    #[pin]
+    _p: PhantomPinned,
+}
+
+/// The main trait for KMS drivers to implement for their display encoders.
+pub trait DriverEncoder: Send + Sync + Sized {
+    /// The return type of the new() function. Should be `impl PinInit<Self, Error>`.
+    /// TODO: Remove this when return_position_impl_trait_in_trait is stable.
+    type Initializer: PinInit<Self, Error>;
+
+    /// The parent driver for this drm_encoder implementation
+    type Driver: KmsDriver;
+
+    /// The type used for passing arguments to the driver's constructor
+    /// Drivers which don't care about this can just use `()`
+    type Args;
+
+    /// Create a new encoder for this driver
+    fn new(device: &Device<Self::Driver>, args: Self::Args) -> Self::Initializer;
+}
+
+impl<T: DriverEncoder> crate::private::Sealed for Encoder<T> {}
+
+unsafe impl Zeroable for bindings::drm_encoder {}
+
+// SAFETY: Encoder.encoder is not exposed to users by default, and our accessors ensure we only
+// perform thread-safe operations for this object
+unsafe impl<T: DriverEncoder> Send for Encoder<T> { }
+unsafe impl<T: DriverEncoder> Sync for Encoder<T> { }
+
+impl<T: DriverEncoder> ModeObject for Encoder<T> {
+    type Driver = T::Driver;
+
+    fn drm_dev(&self) -> &Device<Self::Driver> {
+        // SAFETY: DRM encoders exist for as long as the device does, so this pointer is always
+        // valid
+        unsafe { &*((*self.raw_mut_ptr()).dev.cast()) }
+    }
+}
+
+// SAFETY: Encoders do not have a refcount
+unsafe impl<T: DriverEncoder> StaticModeObject for Encoder<T> { }
+
+impl<T: DriverEncoder> Deref for Encoder<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl<T: DriverEncoder> Encoder<T> {
+    const FUNCS: bindings::drm_encoder_funcs = bindings::drm_encoder_funcs {
+        reset: None,
+        destroy: Some(encoder_destroy_callback::<T>),
+        late_register: None,
+        early_unregister: None,
+        debugfs_init: None,
+    };
+
+    const HELPER_FUNCS: bindings::drm_encoder_helper_funcs = bindings::drm_encoder_helper_funcs {
+        dpms: None,
+        mode_valid: None,
+        mode_fixup: None,
+        prepare: None,
+        mode_set: None,
+        commit: None,
+        detect: None,
+        enable: None,
+        disable: None,
+        atomic_check: None,
+        atomic_enable: None,
+        atomic_disable: None,
+        atomic_mode_set: None,
+    };
+
+    pub fn new<'a>(
+        dev: &'a Device<T::Driver>,
+        type_: u32,
+        possible_crtcs: u32,
+        possible_clones: u32,
+        name: Option<&CStr>,
+        args: T::Args,
+    ) -> Result<&'a Self> {
+        let this: Pin<Box<Self>> = Box::try_pin_init(try_pin_init!(Self {
+            encoder: Opaque::new(bindings::drm_encoder {
+                helper_private: &Self::HELPER_FUNCS,
+                possible_crtcs,
+                possible_clones,
+                ..Default::default()
+            }),
+            inner <- T::new(dev, args),
+            _p: PhantomPinned
+        }))?;
+
+        // SAFETY: FFI call with no special requirements
+        to_result(unsafe {
+            bindings::drm_encoder_init(
+                dev.drm.get(),
+                this.raw_mut_ptr(),
+                &Self::FUNCS,
+                type_ as _,
+                name.map_or(null(), |n| n.as_char_ptr())
+            )
+        })?;
+
+        // Convert the box into a raw pointer, we'll re-assemble it in encoder_destroy_callback()
+        // SAFETY: We don't move anything
+        Ok(unsafe { &*Box::into_raw(Pin::into_inner_unchecked(this)) })
+    }
+
+    pub(crate) unsafe fn raw_mut_ptr(&self) -> *mut bindings::drm_encoder {
+        self.encoder.get()
+    }
+}
+
+unsafe extern "C" fn encoder_destroy_callback<T: DriverEncoder>(
+    encoder: *mut bindings::drm_encoder
+) {
+    // SAFETY: encoder contains the only possible entrypoint to this function, so the pointer must
+    // be valid
+    unsafe { bindings::drm_encoder_cleanup(encoder) };
+
+    // Reclaim ownership of the reference we took in Encoder::<T>::new() so we can drop it
+    // SAFETY: We always create encoders in Arc<T>, and we're guaranteed by type invariants that
+    // this encoder is a Encoder<T>
+    unsafe { drop(Box::from_raw(encoder as *mut Encoder<T>)) };
+}
diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs
new file mode 100644
index 0000000000000..78c8e370b997c
--- /dev/null
+++ b/rust/kernel/drm/kms/plane.rs
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+//! KMS atomic plane abstractions for rust.
+
+use alloc::boxed::Box;
+use crate::{
+    bindings,
+    drm::{device::Device, drv::Driver},
+    error::{to_result, from_err_ptr, Error},
+    init::Zeroable,
+    prelude::*,
+    types::{ARef, Opaque},
+    sync::{Arc, ArcBorrow},
+    init::InPlaceInit,
+    offset_of,
+};
+use core::{
+    cell::UnsafeCell,
+    mem::{self, size_of, MaybeUninit},
+    ptr::{NonNull, null, null_mut, addr_of_mut},
+    marker::PhantomPinned,
+    ops::Deref,
+    ffi::c_int,
+};
+use macros::pin_data;
+use super::{KmsDriver, ModeObject, StaticModeObject};
+
+/// The main structure containing a drm_plane that is exposed to callers. It is intentionally
+/// impossible to acquire a mutable reference to this structure, and as such this structure should
+/// only be exposed through immutable references.
+#[repr(C)]
+#[pin_data]
+pub struct Plane<T: DriverPlane> {
+    /// The FFI drm_plane object
+    pub(super) plane: Opaque<bindings::drm_plane>,
+    /// The driver's private inner data
+    #[pin]
+    inner: T,
+    #[pin]
+    _p: PhantomPinned,
+}
+
+unsafe impl Zeroable for bindings::drm_plane {}
+
+// SAFETY: Plane.plane is not exposed to users by default, and our accessors ensure we only
+// perform thread-safe operations for this object
+unsafe impl<T: DriverPlane> Send for Plane<T> { }
+unsafe impl<T: DriverPlane> Sync for Plane<T> { }
+
+/// The main trait for implementing the drm_plane API. This contains the various trait methods that
+/// need to be implemented by a driver. The private driver data for the plane is contained in
+/// whatever struct the driver defines which implements this trait.
+pub trait DriverPlane: Send + Sync + Sized {
+    /// The return type of the new() function. Should be `impl PinInit<Self, Error>`.
+    /// TODO: Remove this when return_position_impl_trait_in_trait is stable.
+    type Initializer: PinInit<Self, Error>;
+
+    /// The data type to use for passing incoming arguments for new `Plane<T>` instances
+    /// Drivers which don't care about this can just use `()`
+    type Args;
+
+    /// The parent driver implementation
+    type Driver: KmsDriver;
+
+    /// The type for this driver's drm_plane_state implementation
+    type State: DriverPlaneState;
+
+    /// Create a new plane for this driver
+    fn new(device: &Device<Self::Driver>, args: Self::Args) -> Self::Initializer;
+}
+
+impl<T: DriverPlane> crate::private::Sealed for Plane<T> {}
+
+impl<T: DriverPlane> ModeObject for Plane<T> {
+    type Driver = T::Driver;
+
+    fn drm_dev(&self) -> &Device<Self::Driver> {
+        // SAFETY: DRM planes exist for as long as the device does, so this pointer is always valid
+        unsafe { &*((*self.raw_mut_ptr()).dev as *const _) }
+    }
+}
+
+// SAFETY: Planes do not have a refcount
+unsafe impl<T: DriverPlane> StaticModeObject for Plane<T> { }
+
+impl<T: DriverPlane> Deref for Plane<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl<T: DriverPlane> Plane<T> {
+    /// The actual C vtable for drm_plane_funcs
+    const FUNCS: bindings::drm_plane_funcs = bindings::drm_plane_funcs {
+        update_plane: Some(bindings::drm_atomic_helper_update_plane),
+        disable_plane: Some(bindings::drm_atomic_helper_disable_plane),
+        destroy: Some(plane_destroy_callback::<T>),
+        reset: Some(plane_reset_callback::<T::State>),
+        set_property: None,
+        atomic_duplicate_state: Some(atomic_duplicate_state_callback::<T::State>),
+        atomic_destroy_state: Some(atomic_destroy_state_callback::<T::State>),
+        atomic_set_property: None, // TODO someday
+        atomic_get_property: None, // TODO someday
+        late_register: None, // TODO someday
+        early_unregister: None, // TODO someday
+        atomic_print_state: None, // TODO: Display someday???
+        format_mod_supported: None // TODO someday
+    };
+
+    const HELPER_FUNCS: bindings::drm_plane_helper_funcs = bindings::drm_plane_helper_funcs {
+        prepare_fb: None, // TODO someday?
+        cleanup_fb: None, // TODO someday?
+        begin_fb_access: None, // TODO: someday?
+        end_fb_access: None, // TODO: someday?
+        atomic_check: None, // TODO
+        atomic_update: None, // TODO
+        atomic_enable: None, // TODO
+        atomic_disable: None, // TODO
+        atomic_async_check: None, // TODO
+        atomic_async_update: None, // TODO
+    };
+
+    pub fn new<'a>(
+        dev: &'a Device<T::Driver>,
+        possible_crtcs: u32,
+        formats: &'static [u32],
+        format_modifiers: Option<&'static [u64]>,
+        type_: PlaneType,
+        name: Option<&CStr>,
+        args: T::Args,
+    ) -> Result<&'a Self> {
+        let this: Pin<Box<Self>> = Box::try_pin_init(try_pin_init!(Self {
+            plane: Opaque::new(bindings::drm_plane {
+                helper_private: &Self::HELPER_FUNCS,
+                ..Default::default()
+            }),
+            inner <- T::new(dev, args),
+            _p: PhantomPinned
+        }))?;
+
+        // SAFETY: FFI call with no special requirements
+        to_result(unsafe {
+            bindings::drm_universal_plane_init(
+                dev.drm.get(),
+                this.raw_mut_ptr(),
+                possible_crtcs,
+                &Self::FUNCS,
+                formats.as_ptr(),
+                formats.len() as _,
+                format_modifiers.map_or(null(), |f| f.as_ptr()),
+                type_ as _,
+                name.map_or(null(), |n| n.as_char_ptr())
+            )
+        })?;
+
+        // Convert the box into a raw pointer, we'll re-assemble it in plane_destroy_callback()
+        // SAFETY: We don't move anything
+        Ok(unsafe { &*Box::into_raw(Pin::into_inner_unchecked(this)) })
+    }
+
+    pub(super) fn raw_mut_ptr(&self) -> *mut bindings::drm_plane {
+        self.plane.get()
+    }
+}
+
+unsafe extern "C" fn plane_destroy_callback<T: DriverPlane>(
+    plane: *mut bindings::drm_plane
+) {
+    // SAFETY: plane contains the only possible entrypoint to this function, so the pointer must be
+    // valid
+    unsafe { bindings::drm_plane_cleanup(plane) };
+
+    // Reclaim ownership of the reference we took in Plane::<T>::new() so we can drop it
+    // SAFETY: We're guaranteed by type invariants this plane is contained within an Box<Plane<T>>
+    unsafe { drop(Box::from_raw(plane as *mut Plane<T>)) };
+}
+
+#[derive(Default)]
+#[repr(C)]
+pub struct PlaneState<T: DriverPlaneState> {
+    state: bindings::drm_plane_state,
+    inner: T,
+}
+
+/// Traits which must be implemented by KMS drivers for DRM planes.
+pub trait DriverPlaneState: Clone + Default + Sized {
+    /// The type for this driver's drm_plane implementation
+    type Plane: DriverPlane;
+}
+
+impl<T: DriverPlaneState> PlaneState<T> {
+    /// Consume this struct without dropping it, and return a pointer to its base `drm_plane_state`
+    /// which can be handed off to DRM.
+    fn into_raw(self: Box<Self>) -> *mut bindings::drm_plane_state {
+        let this = Box::into_raw(self);
+
+        unsafe { &mut (*this).state }
+    }
+
+    /// Consume a raw pointer and recover the original `Box<PlaneState<T>>`
+    ///
+    /// SAFETY: Callers must ensure that ptr contains a valid instance of `PlaneState<T>`
+    unsafe fn from_raw(ptr: *mut bindings::drm_plane_state) -> Box<Self> {
+        unsafe { Box::from_raw(ptr as *mut _) }
+    }
+
+    /// Obtain a reference back to the `PlaneState<T>`
+    ///
+    /// SAFETY: Callers must ensure that ptr contains a valid instance of `PlaneState<T>`.
+    unsafe fn as_ref<'a>(ptr: *const bindings::drm_plane_state) -> &'a Self {
+        unsafe { &*(ptr as *const _) }
+    }
+
+    /// Obtain a mutable reference back to the PlaneState<T>
+    ///
+    /// SAFETY: Callers must ensure that ptr contains a valid instance of `PlaneState<T>`, and that
+    /// no other references to this `PlaneState<T>` exist for the lifetime of this reference
+    unsafe fn as_mut_ref<'a>(ptr: *mut bindings::drm_plane_state) -> &'a mut Self {
+        unsafe { &mut *(ptr as *mut _) }
+    }
+
+    /// Obtain a mutable pointer to the base plane state, for use in FFI calls
+    unsafe fn as_mut_ptr(&mut self) -> *mut bindings::drm_plane_state {
+        &mut self.state
+    }
+}
+
+unsafe impl Zeroable for bindings::drm_plane_state { }
+
+unsafe extern "C" fn atomic_duplicate_state_callback<T: DriverPlaneState>(
+    plane: *mut bindings::drm_plane
+) -> *mut bindings::drm_plane_state
+{
+    // SAFETY: `plane` has to point to a valid instance of drm_plane, since it holds the vtable for
+    // this function - which is the only possible entrypoint the caller could have used
+    let state = unsafe { (*plane).state };
+    if state.is_null() {
+        return null_mut();
+    }
+
+    // SAFETY: We just verified that `state` is non-null, and we're guaranteed by our bindings that
+    // `state` is of type `PlaneState<T>`.
+    let state = unsafe { PlaneState::<T>::as_ref(state) };
+
+    let mut new: Result<Box<PlaneState<T>>> = Box::try_init(try_init!(PlaneState::<T> {
+        state: bindings::drm_plane_state { ..Default::default() },
+        inner: state.inner.clone()
+    }));
+
+    if let Ok(mut new) = new {
+        // SAFETY: Just a lil' FFI call, nothing special here
+        unsafe { bindings::__drm_atomic_helper_plane_duplicate_state(plane, new.as_mut_ptr()) };
+
+        new.into_raw()
+    } else {
+        null_mut()
+    }
+}
+
+unsafe extern "C" fn atomic_destroy_state_callback<T: DriverPlaneState>(
+    _plane: *mut bindings::drm_plane,
+    plane_state: *mut bindings::drm_plane_state
+) {
+    // SAFETY: This callback wouldn't be called unless there a plane state to destroy
+    unsafe { bindings::__drm_atomic_helper_plane_destroy_state(plane_state) };
+
+    // SAFETY: We're guaranteed by type invariants that plane_state is of type PlaneState<T>, and
+    // since this is the destructor callback for DRM - we're guaranteed to hold the only remaining
+    // reference to this state
+    unsafe { drop(PlaneState::<T>::from_raw(plane_state)) };
+}
+
+unsafe extern "C" fn plane_reset_callback<T: DriverPlaneState>(
+    plane: *mut bindings::drm_plane,
+) {
+    // SAFETY: The only entrypoint to this function lives in `plane` so it must be valid, and
+    let state = unsafe { (*plane).state };
+    if !state.is_null() {
+        // SAFETY: We're guaranteed by type invariance that this plane's state is of type
+        // DriverPlaneState<T>
+        unsafe { atomic_destroy_state_callback::<T>(plane, state) }
+    }
+
+    // Unfortunately, this is the best we can do at the moment as this FFI callback was mistakenly
+    // presumed to be infallible :(
+    let new = Box::try_new(PlaneState::<T>::default()).expect("Blame the API, sorry!");
+
+    // SAFETY: DRM takes ownership of the state from here and assigns it to the plane
+    unsafe { bindings::__drm_atomic_helper_plane_reset(plane, new.into_raw()) };
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(u32)]
+pub enum PlaneType {
+    OVERLAY = bindings::drm_plane_type_DRM_PLANE_TYPE_OVERLAY,
+    PRIMARY = bindings::drm_plane_type_DRM_PLANE_TYPE_PRIMARY,
+    CURSOR = bindings::drm_plane_type_DRM_PLANE_TYPE_CURSOR,
+}
diff --git a/rust/kernel/drm/mod.rs b/rust/kernel/drm/mod.rs
index 2c12dbd181997..049ae675cb9b1 100644
--- a/rust/kernel/drm/mod.rs
+++ b/rust/kernel/drm/mod.rs
@@ -8,3 +8,4 @@
 pub mod fourcc;
 pub mod gem;
 pub mod ioctl;
+pub mod kms;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 2/4] WIP: drm: Introduce rvkms
  2024-03-22 22:03 [RFC WIP 0/4] Rust bindings for KMS + RVKMS Lyude Paul
  2024-03-22 22:03 ` [PATCH 1/4] WIP: rust: Add basic KMS bindings Lyude Paul
@ 2024-03-22 22:03 ` Lyude Paul
  2024-03-27 21:06   ` Benno Lossin
  2024-03-22 22:03 ` [PATCH 3/4] rust/drm/kms: Extract PlaneState<T> into IntoPlaneState Lyude Paul
  2024-03-22 22:03 ` [PATCH 4/4] WIP: rust/drm/kms: Add ShadowPlaneState<T> Lyude Paul
  3 siblings, 1 reply; 12+ messages in thread
From: Lyude Paul @ 2024-03-22 22:03 UTC (permalink / raw)
  To: dri-devel
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Miguel Ojeda, Alex Gaynor,
	Wedson Almeida Filho, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl,
	GitAuthor: Lyude Paul, open list,
	open list:RUST:Keyword:b(?i:rust)b

This introduces a work in progress port of the VKMS driver to rust to
provide a user of the kernel's new rust bindings for KMS drivers! This
driver is very incomplete, I'm not even sure if it loads right now without
crashing (but it did at one point, and I'll be checking very soon!).

Squash into rvkms introduction

Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/Kconfig            |   2 +
 drivers/gpu/drm/Makefile           |   1 +
 drivers/gpu/drm/rvkms/Kconfig      |   3 +
 drivers/gpu/drm/rvkms/Makefile     |   1 +
 drivers/gpu/drm/rvkms/connector.rs |  55 +++++++++++
 drivers/gpu/drm/rvkms/crtc.rs      |  40 ++++++++
 drivers/gpu/drm/rvkms/encoder.rs   |  26 +++++
 drivers/gpu/drm/rvkms/file.rs      |  22 +++++
 drivers/gpu/drm/rvkms/gem.rs       |  32 +++++++
 drivers/gpu/drm/rvkms/output.rs    |  72 ++++++++++++++
 drivers/gpu/drm/rvkms/plane.rs     |  39 ++++++++
 drivers/gpu/drm/rvkms/rvkms.rs     | 146 +++++++++++++++++++++++++++++
 12 files changed, 439 insertions(+)
 create mode 100644 drivers/gpu/drm/rvkms/Kconfig
 create mode 100644 drivers/gpu/drm/rvkms/Makefile
 create mode 100644 drivers/gpu/drm/rvkms/connector.rs
 create mode 100644 drivers/gpu/drm/rvkms/crtc.rs
 create mode 100644 drivers/gpu/drm/rvkms/encoder.rs
 create mode 100644 drivers/gpu/drm/rvkms/file.rs
 create mode 100644 drivers/gpu/drm/rvkms/gem.rs
 create mode 100644 drivers/gpu/drm/rvkms/output.rs
 create mode 100644 drivers/gpu/drm/rvkms/plane.rs
 create mode 100644 drivers/gpu/drm/rvkms/rvkms.rs

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index c7edba18a6f09..48c140f943356 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -275,6 +275,8 @@ source "drivers/gpu/drm/amd/amdgpu/Kconfig"
 
 source "drivers/gpu/drm/nouveau/Kconfig"
 
+source "drivers/gpu/drm/rvkms/Kconfig"
+
 source "drivers/gpu/drm/i915/Kconfig"
 
 source "drivers/gpu/drm/xe/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 104b42df2e956..91a18c8290fa4 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -143,6 +143,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
 obj-$(CONFIG_DRM_VGEM)	+= vgem/
 obj-$(CONFIG_DRM_VKMS)	+= vkms/
 obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
+obj-$(CONFIG_DRM_RVKMS) += rvkms/
 obj-$(CONFIG_DRM_EXYNOS) +=exynos/
 obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
 obj-$(CONFIG_DRM_GMA500) += gma500/
diff --git a/drivers/gpu/drm/rvkms/Kconfig b/drivers/gpu/drm/rvkms/Kconfig
new file mode 100644
index 0000000000000..d888aa4ffc84c
--- /dev/null
+++ b/drivers/gpu/drm/rvkms/Kconfig
@@ -0,0 +1,3 @@
+config DRM_RVKMS
+        tristate "Rust VKMS PoC driver (EXPERIMENTAL)"
+        depends on RUST && DRM && DRM_GEM_SHMEM_HELPER
diff --git a/drivers/gpu/drm/rvkms/Makefile b/drivers/gpu/drm/rvkms/Makefile
new file mode 100644
index 0000000000000..18e06fc3343c6
--- /dev/null
+++ b/drivers/gpu/drm/rvkms/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DRM_RVKMS) += rvkms.o
diff --git a/drivers/gpu/drm/rvkms/connector.rs b/drivers/gpu/drm/rvkms/connector.rs
new file mode 100644
index 0000000000000..40f84d38437ee
--- /dev/null
+++ b/drivers/gpu/drm/rvkms/connector.rs
@@ -0,0 +1,55 @@
+// TODO: License and stuff
+// Contain's rvkms's drm_connector implementation
+
+use super::{RvkmsDriver, RvkmsDevice, MAX_RES, DEFAULT_RES};
+use kernel::{
+    prelude::*,
+    drm::{
+        device::Device,
+        kms::{
+            connector::{self, ConnectorGuard},
+            ModeConfigGuard
+        }
+    },
+    prelude::*
+};
+use core::marker::PhantomPinned;
+
+#[pin_data]
+pub(crate) struct DriverConnector {
+    #[pin]
+    _p: PhantomPinned
+}
+
+pub(crate) type Connector = connector::Connector<DriverConnector>;
+
+impl connector::DriverConnector for DriverConnector {
+    type Initializer = impl PinInit<Self, Error>;
+
+    type State = ConnectorState;
+
+    type Driver = RvkmsDriver;
+
+    type Args = ();
+
+    fn new(dev: &Device<Self::Driver>, args: Self::Args) -> Self::Initializer {
+        try_pin_init!(Self { _p: PhantomPinned })
+    }
+
+    fn get_modes(
+        connector: ConnectorGuard<'_, Self>,
+        _guard: &ModeConfigGuard<'_, Self::Driver>
+    ) -> i32 {
+        let count = connector.add_modes_noedid(MAX_RES);
+
+        connector.set_preferred_mode(DEFAULT_RES);
+        count
+    }
+}
+
+#[derive(Clone, Default)]
+pub(crate) struct ConnectorState;
+
+impl connector::DriverConnectorState for ConnectorState {
+    type Connector = DriverConnector;
+}
diff --git a/drivers/gpu/drm/rvkms/crtc.rs b/drivers/gpu/drm/rvkms/crtc.rs
new file mode 100644
index 0000000000000..2998f288b88e6
--- /dev/null
+++ b/drivers/gpu/drm/rvkms/crtc.rs
@@ -0,0 +1,40 @@
+// TODO: License and stuff
+// Contain's rvkms's drm_crtc implementation
+use core::marker::PhantomPinned;
+use super::RvkmsDriver;
+use kernel::{
+    prelude::*,
+    drm::{
+        device::Device,
+        kms::crtc,
+    },
+};
+
+pub(crate) type Crtc = crtc::Crtc<DriverCrtc>;
+
+#[pin_data]
+pub(crate) struct DriverCrtc {
+    #[pin]
+    _p: PhantomPinned
+}
+
+impl crtc::DriverCrtc for DriverCrtc {
+    type Initializer = impl PinInit<Self, Error>;
+
+    type Args = ();
+
+    type State = CrtcState;
+
+    type Driver = RvkmsDriver;
+
+    fn new(device: &Device<Self::Driver>, args: Self::Args) -> Self::Initializer {
+        try_pin_init!(Self { _p: PhantomPinned })
+    }
+}
+
+#[derive(Clone, Default)]
+pub(crate) struct CrtcState;
+
+impl crtc::DriverCrtcState for CrtcState {
+    type Crtc = DriverCrtc;
+}
diff --git a/drivers/gpu/drm/rvkms/encoder.rs b/drivers/gpu/drm/rvkms/encoder.rs
new file mode 100644
index 0000000000000..72d8b43d9107e
--- /dev/null
+++ b/drivers/gpu/drm/rvkms/encoder.rs
@@ -0,0 +1,26 @@
+use core::marker::PhantomPinned;
+use kernel::{
+    drm::{device::Device, kms},
+    prelude::*
+};
+use crate::RvkmsDriver;
+
+#[pin_data]
+pub(crate) struct DriverEncoder {
+    #[pin]
+    _p: PhantomPinned,
+}
+
+pub(crate) type Encoder = kms::encoder::Encoder<DriverEncoder>;
+
+impl kms::encoder::DriverEncoder for DriverEncoder {
+    type Initializer = impl PinInit<Self, Error>;
+
+    type Driver = RvkmsDriver;
+
+    type Args = ();
+
+    fn new(device: &Device<Self::Driver>, args: Self::Args) -> Self::Initializer {
+        try_pin_init!(Self { _p: PhantomPinned })
+    }
+}
diff --git a/drivers/gpu/drm/rvkms/file.rs b/drivers/gpu/drm/rvkms/file.rs
new file mode 100644
index 0000000000000..24b1b53b78238
--- /dev/null
+++ b/drivers/gpu/drm/rvkms/file.rs
@@ -0,0 +1,22 @@
+use super::RvkmsDriver;
+
+use kernel::{
+    drm::{
+        self,
+        device::Device as DrmDevice
+    },
+    prelude::*,
+};
+use core::option::*;
+
+pub(crate) struct File();
+
+impl drm::file::DriverFile for File {
+    type Driver = RvkmsDriver;
+
+    fn open(device: &DrmDevice<Self::Driver>) -> Result<Pin<Box<Self>>> {
+        pr_info!("Someone opened a file! But I do not yet know which one...\n");
+
+        Ok(Box::into_pin(Box::try_new(Self())?))
+    }
+}
diff --git a/drivers/gpu/drm/rvkms/gem.rs b/drivers/gpu/drm/rvkms/gem.rs
new file mode 100644
index 0000000000000..b789a1c2170c7
--- /dev/null
+++ b/drivers/gpu/drm/rvkms/gem.rs
@@ -0,0 +1,32 @@
+use crate::{RvkmsDriver, RvkmsDevice};
+use core::sync::atomic::{AtomicU64, Ordering};
+use kernel::{
+    drm::{self, gem},
+    prelude::*,
+};
+
+static GEM_ID: AtomicU64 = AtomicU64::new(0);
+
+/// GEM Object implementation
+#[pin_data]
+pub(crate) struct DriverObject {
+    /// ID for debugging
+    id: u64,
+}
+
+pub(crate) type Object = gem::shmem::Object<DriverObject>;
+
+impl gem::BaseDriverObject<Object> for DriverObject {
+    type Initializer = impl PinInit<Self, Error>;
+
+    fn new(dev: &RvkmsDevice, size: usize) -> Self::Initializer {
+        let id = GEM_ID.fetch_add(1, Ordering::Relaxed);
+
+        pr_debug!("DriverObject::new id={id}\n");
+        DriverObject { id }
+    }
+}
+
+impl gem::shmem::DriverObject for DriverObject {
+    type Driver = RvkmsDriver;
+}
diff --git a/drivers/gpu/drm/rvkms/output.rs b/drivers/gpu/drm/rvkms/output.rs
new file mode 100644
index 0000000000000..227d6c3d719a0
--- /dev/null
+++ b/drivers/gpu/drm/rvkms/output.rs
@@ -0,0 +1,72 @@
+use crate::{
+    crtc::Crtc,
+    plane::Plane,
+    connector::Connector,
+    encoder::Encoder,
+    RvkmsDevice
+};
+use kernel::{
+    drm::{
+        fourcc::Format,
+        kms::{
+            connector::DRM_MODE_CONNECTOR_VIRTUAL,
+            encoder::DRM_MODE_ENCODER_VIRTUAL,
+            plane::PlaneType,
+            KmsRef
+        },
+    },
+    sync::Arc,
+    prelude::*,
+    types::ARef,
+    drm_format_list,
+};
+
+pub(crate) struct Output {
+    crtc: KmsRef<Crtc>,
+    primary: KmsRef<Plane>,
+    // TODO: overlay, cursor
+    connector: ARef<Connector>,
+    encoder: KmsRef<Encoder>,
+}
+
+impl Output {
+    pub(crate) fn new(dev: &RvkmsDevice, index: u8) -> Result<Self> {
+        let primary = Plane::new(
+            dev,
+            1 << index,
+            &drm_format_list![Format::XRGB888],
+            None,
+            PlaneType::PRIMARY,
+            None,
+            ()
+        )?;
+
+        let crtc = Crtc::new(
+            dev,
+            &primary,
+            Option::<&Plane>::None,
+            None,
+            ()
+        )?;
+
+        let connector = Connector::new(dev, DRM_MODE_CONNECTOR_VIRTUAL, ())?;
+
+        let encoder = Encoder::new(
+            dev,
+            DRM_MODE_ENCODER_VIRTUAL,
+            1 << index,
+            0,
+            None,
+            ()
+        )?;
+
+        connector.attach_encoder(&encoder)?;
+
+        Ok(Self {
+            crtc: crtc.into(),
+            primary: primary.into(),
+            connector,
+            encoder: encoder.into()
+        })
+    }
+}
diff --git a/drivers/gpu/drm/rvkms/plane.rs b/drivers/gpu/drm/rvkms/plane.rs
new file mode 100644
index 0000000000000..54c4bbda64d8e
--- /dev/null
+++ b/drivers/gpu/drm/rvkms/plane.rs
@@ -0,0 +1,39 @@
+use core::marker::PhantomPinned;
+use super::RvkmsDriver;
+use kernel::{
+    prelude::*,
+    drm::{
+        device::Device,
+        kms::plane::{self, DriverPlaneState},
+    },
+};
+
+#[pin_data]
+pub(crate) struct DriverPlane {
+    #[pin]
+    _p: PhantomPinned,
+}
+
+pub(crate) type Plane = plane::Plane<DriverPlane>;
+pub(crate) type PlaneState = plane::PlaneState<RvkmsPlaneState>;
+
+impl plane::DriverPlane for DriverPlane {
+    type Initializer = impl PinInit<Self, Error>;
+
+    type State = RvkmsPlaneState;
+
+    type Driver = RvkmsDriver;
+
+    type Args = ();
+
+    fn new(device: &Device<Self::Driver>, args: Self::Args) -> Self::Initializer {
+        try_pin_init!(Self { _p: PhantomPinned })
+    }
+}
+
+#[derive(Clone, Default)]
+pub(crate) struct RvkmsPlaneState;
+
+impl DriverPlaneState for RvkmsPlaneState {
+    type Plane = DriverPlane;
+}
diff --git a/drivers/gpu/drm/rvkms/rvkms.rs b/drivers/gpu/drm/rvkms/rvkms.rs
new file mode 100644
index 0000000000000..0b14aca7f77d4
--- /dev/null
+++ b/drivers/gpu/drm/rvkms/rvkms.rs
@@ -0,0 +1,146 @@
+// Rust VKMS Proof of Concept driver
+// Written by Lyude Paul
+// Enormous thanks to:
+// - Maira Canal
+// - Asahi Lina
+// - Probably lots of other wonderful people whose names aren't here
+
+mod connector;
+mod crtc;
+mod file;
+mod gem;
+mod plane;
+mod output;
+mod encoder;
+
+use alloc::boxed::Box;
+
+use core::option::*;
+
+use kernel::{
+    c_str,
+    str::CStr,
+    device::{self, RawDevice},
+    drm::{
+        self,
+        drv,
+        kms::{
+            KmsDriver,
+            ModeConfigInfo,
+        },
+    },
+    platform,
+    prelude::*,
+    sync::Arc,
+    types::ARef,
+};
+
+use crate::output::Output;
+
+pub(crate) struct RvkmsDriver;
+pub(crate) struct Resources;
+
+pub(crate) type DeviceData = device::Data<drv::Registration<RvkmsDriver>, Resources, Data>;
+
+/// Convienence type alias for the DRM device type for this driver
+pub(crate) type RvkmsDevice = drm::device::Device<RvkmsDriver>;
+
+/// Driver metadata
+const INFO: drv::DriverInfo = drv::DriverInfo {
+    major: 0,
+    minor: 0,
+    patchlevel: 0,
+    name: c_str!("rvkms"),
+    desc: c_str!("Rust VKMS PoC"),
+    date: c_str!("20240115"),
+};
+
+/// The minimum supported resolution
+const MIN_RES: (i32, i32) = (10, 10);
+
+/// The maximum supported resolution
+const MAX_RES: (i32, i32) = (8192, 8192);
+
+/// The "preferred" resolution
+const DEFAULT_RES: (i32, i32) = (1024, 768);
+
+pub(crate) struct Data {
+}
+
+/// DRM Driver implementation for `RvkmsDriver`
+#[vtable]
+impl drv::Driver for RvkmsDriver {
+    type Data = Arc<DeviceData>;
+    type Object = gem::Object;
+    type File = file::File;
+
+    const INFO: drv::DriverInfo = INFO;
+    const FEATURES:u32 = drv::FEAT_GEM | drv::FEAT_MODESET | drv::FEAT_ATOMIC;
+
+    kernel::declare_drm_ioctls! {}
+}
+
+impl KmsDriver for RvkmsDriver {}
+
+pub(crate) struct Rvkms {
+    output: Output,
+    drm: ARef<RvkmsDevice>,
+    _resource: device::Resource,
+    _pdev: platform::Device,
+}
+
+const MODE_CONFIG_INFO: ModeConfigInfo = ModeConfigInfo {
+    min_resolution: MIN_RES,
+    max_resolution: MAX_RES,
+    max_cursor: (512, 512),
+    preferred_depth: 0,
+};
+
+impl kernel::Module for Rvkms {
+    fn init(name: &'static CStr, module: &'static ThisModule) -> Result<Self> {
+        // We need to handle all of this from the true module_init, as we're a platform driver that
+        // binds to nothing - so we have to handle that portion of init ourselves like the real vkms
+        let mut pdev = platform::Device::register(c_str!("rvkms"), -1)?;
+        let dev = device::Device::from_dev(&pdev);
+
+        dev.pr_info(format_args!("Initializing RVKMS\n"));
+
+        // TODO: I wonder if there's any way we could get rid of having to pass raw pointers to
+        // this?
+        // no idea
+        let resource = dev.open_group(core::ptr::null_mut() as *mut core::ffi::c_void)?;
+
+        let reg: drv::Registration<RvkmsDriver> =
+            drv::Registration::new(&dev, Option::Some(MODE_CONFIG_INFO), module)?;
+        let drm_dev: ARef<RvkmsDevice> = ARef::from(reg.device());
+        let reg_info = reg.registration_info();
+        let output = Output::new(&drm_dev, 0)?;
+        let data = kernel::new_device_data!(
+            reg,
+            Resources,
+            Data { },
+            "RvkmsDeviceData"
+        )?;
+
+        drm_dev.mode_config_reset();
+
+        dev.dma_coerce_mask_and_coherent(kernel::dma::dma_bit_mask(64))?;
+
+        drv::Registration::register(reg_info, data.into(), 0, module)?;
+
+        Ok(Self {
+            drm: drm_dev,
+            output,
+            _resource: resource,
+            _pdev: pdev,
+        })
+    }
+}
+
+module! {
+    type: Rvkms,
+    name: "rvkms",
+    author: "Lyude Paul",
+    description: "Rust VKMS Proof of Concept driver",
+    license: "GPL v2",
+}
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 3/4] rust/drm/kms: Extract PlaneState<T> into IntoPlaneState
  2024-03-22 22:03 [RFC WIP 0/4] Rust bindings for KMS + RVKMS Lyude Paul
  2024-03-22 22:03 ` [PATCH 1/4] WIP: rust: Add basic KMS bindings Lyude Paul
  2024-03-22 22:03 ` [PATCH 2/4] WIP: drm: Introduce rvkms Lyude Paul
@ 2024-03-22 22:03 ` Lyude Paul
  2024-03-22 22:03 ` [PATCH 4/4] WIP: rust/drm/kms: Add ShadowPlaneState<T> Lyude Paul
  3 siblings, 0 replies; 12+ messages in thread
From: Lyude Paul @ 2024-03-22 22:03 UTC (permalink / raw)
  To: dri-devel
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Miguel Ojeda, Alex Gaynor,
	Wedson Almeida Filho, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, open list,
	open list:RUST

DRM actually has a number of helpers that wrap drm_plane_state, one of
which is actually needed by VKMS - drm_shadow_plane_state.

So, let's start preparing to write bindings for this by first extracting
PlaneState<T> into the IntoPlaneState trait - which all DRM structs which
wrap drm_plane_state will implement. This is basically the same idea as the
GEM ops - but for plane states.

Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/rvkms/plane.rs |   2 +-
 rust/kernel/drm/kms/plane.rs   | 165 ++++++++++++++++++++-------------
 2 files changed, 103 insertions(+), 64 deletions(-)

diff --git a/drivers/gpu/drm/rvkms/plane.rs b/drivers/gpu/drm/rvkms/plane.rs
index 54c4bbda64d8e..d98a1f7bf79e2 100644
--- a/drivers/gpu/drm/rvkms/plane.rs
+++ b/drivers/gpu/drm/rvkms/plane.rs
@@ -20,7 +20,7 @@ pub(crate) struct DriverPlane {
 impl plane::DriverPlane for DriverPlane {
     type Initializer = impl PinInit<Self, Error>;
 
-    type State = RvkmsPlaneState;
+    type State = PlaneState;
 
     type Driver = RvkmsDriver;
 
diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs
index 78c8e370b997c..73c285445be63 100644
--- a/rust/kernel/drm/kms/plane.rs
+++ b/rust/kernel/drm/kms/plane.rs
@@ -13,6 +13,7 @@
     sync::{Arc, ArcBorrow},
     init::InPlaceInit,
     offset_of,
+    private::Sealed,
 };
 use core::{
     cell::UnsafeCell,
@@ -62,14 +63,16 @@ pub trait DriverPlane: Send + Sync + Sized {
     /// The parent driver implementation
     type Driver: KmsDriver;
 
-    /// The type for this driver's drm_plane_state implementation
-    type State: DriverPlaneState;
+    /// The full type for this driver's drm_plane_state implementation. Drivers which don't need
+    /// special DRM helpers for their plane states may just use `PlaneState<T>` here, where `T` is
+    /// their private state struct which implements `DriverPlaneState`
+    type State: IntoPlaneState;
 
     /// Create a new plane for this driver
     fn new(device: &Device<Self::Driver>, args: Self::Args) -> Self::Initializer;
 }
 
-impl<T: DriverPlane> crate::private::Sealed for Plane<T> {}
+impl<T: DriverPlane> Sealed for Plane<T> {}
 
 impl<T: DriverPlane> ModeObject for Plane<T> {
     type Driver = T::Driver;
@@ -177,6 +180,70 @@ pub(super) fn raw_mut_ptr(&self) -> *mut bindings::drm_plane {
     unsafe { drop(Box::from_raw(plane as *mut Plane<T>)) };
 }
 
+/// Operations implemented by any base atomic plane state. These are implemented by DRM to provide
+/// wrappers around the generic atomic plane state, along with various plane state helpers.
+///
+/// SAFETY: Incorrect implementation of these methods will result in UB, users should take care to
+/// read through the documentation of each method - especially the provided methods.
+pub unsafe trait IntoPlaneState: Default + Sealed {
+    /// Consume the box for this plane state without dropping its contents, and return a reference
+    /// to it's base plane state to hand off to DRM
+    ///
+    /// Implementors must override this if their data layout does not start with
+    /// `bindings::drm_plane_state`.
+    fn into_raw(self: Box<Self>) -> *mut bindings::drm_plane_state {
+        // Our data layout starts with drm_plane_state
+        Box::into_raw(self).cast()
+    }
+
+    /// Reconstruct the box for this plate state for deallocation
+    ///
+    /// Implementors must override this if their data layout does not start with
+    /// `bindings::drm_plane_state`.
+    unsafe fn from_raw(ptr: *mut bindings::drm_plane_state) -> Box<Self> {
+        // SAFETY: Users of this default promise their data layout starts with drm_plane_state
+        unsafe { Box::from_raw(ptr.cast()) }
+    }
+
+    /// Get a mutable reference to the raw `bindings::drm_plane_state` contained within this type
+    /// that we can pass to DRM
+    fn as_raw_mut(&mut self) -> &mut bindings::drm_plane_state {
+        // SAFETY: Users of this default promise their data layout starts with drm_plane_state
+        unsafe { mem::transmute(self) }
+    }
+
+    /// Get an immutable reference to this type from the given raw `bindings::drm_plane_state`
+    /// pointer
+    ///
+    /// Implementors must override this if their data layout does not start with
+    /// `bindings::drm_plane_state`.
+    ///
+    /// SAFETY: The caller guarantees `ptr` is contained within a valid instance of `Self`
+    unsafe fn ref_from_raw<'a>(ptr: *const bindings::drm_plane_state) -> &'a Self {
+        // SAFETY: Users of this default promise their data layout starts with drm_plane_state
+        unsafe { &*ptr.cast() }
+    }
+
+    /// Get a mutable reference to this type from the given raw `bindings::drm_plane_state` pointer
+    ///
+    /// SAFETY: The caller guarantees `ptr` is contained within a valid instance of `Self`
+    unsafe fn ref_from_raw_mut<'a>(ptr: *mut bindings::drm_plane_state) -> &'a mut Self {
+        // SAFETY: Users of this default promise their data layout starts with drm_plane_state
+        unsafe { &mut (*ptr.cast()) }
+    }
+
+    // Allocate a "duplicated" copy of this state, which usually involves calling DRM's helper
+    // function for this plane state type
+    fn __duplicate_state(&self, plane: *mut bindings::drm_plane) -> Result<Box<Self>>;
+
+    // Call DRM's destroy helper for this type of plane state. Note this only cleans up the base DRM
+    // state struct and does not de-allocate its `Box`.
+    fn __destroy_state(state: *mut bindings::drm_plane_state);
+
+    // Call DRM's reset helper for this type of plane state.
+    fn __reset_state(plane: *mut bindings::drm_plane, state: *mut bindings::drm_plane_state);
+}
+
 #[derive(Default)]
 #[repr(C)]
 pub struct PlaneState<T: DriverPlaneState> {
@@ -190,46 +257,35 @@ pub trait DriverPlaneState: Clone + Default + Sized {
     type Plane: DriverPlane;
 }
 
-impl<T: DriverPlaneState> PlaneState<T> {
-    /// Consume this struct without dropping it, and return a pointer to its base `drm_plane_state`
-    /// which can be handed off to DRM.
-    fn into_raw(self: Box<Self>) -> *mut bindings::drm_plane_state {
-        let this = Box::into_raw(self);
+impl<T: DriverPlaneState> Sealed for PlaneState<T> {}
 
-        unsafe { &mut (*this).state }
-    }
+// SAFETY: Our data layout starts with drm_plane_state
+unsafe impl<T: DriverPlaneState> IntoPlaneState for PlaneState<T> {
+    fn __duplicate_state(&self, plane: *mut bindings::drm_plane) -> Result<Box<Self>> {
+        let mut new: Box<Self> = Box::try_init(try_init!(Self {
+            state: bindings::drm_plane_state { ..Default::default() },
+            inner: self.inner.clone()
+        }))?;
 
-    /// Consume a raw pointer and recover the original `Box<PlaneState<T>>`
-    ///
-    /// SAFETY: Callers must ensure that ptr contains a valid instance of `PlaneState<T>`
-    unsafe fn from_raw(ptr: *mut bindings::drm_plane_state) -> Box<Self> {
-        unsafe { Box::from_raw(ptr as *mut _) }
-    }
+        // SAFETY: FFI call with no special requirements
+        unsafe { bindings::__drm_atomic_helper_plane_duplicate_state(plane, new.as_raw_mut()) };
 
-    /// Obtain a reference back to the `PlaneState<T>`
-    ///
-    /// SAFETY: Callers must ensure that ptr contains a valid instance of `PlaneState<T>`.
-    unsafe fn as_ref<'a>(ptr: *const bindings::drm_plane_state) -> &'a Self {
-        unsafe { &*(ptr as *const _) }
+        Ok(new)
     }
 
-    /// Obtain a mutable reference back to the PlaneState<T>
-    ///
-    /// SAFETY: Callers must ensure that ptr contains a valid instance of `PlaneState<T>`, and that
-    /// no other references to this `PlaneState<T>` exist for the lifetime of this reference
-    unsafe fn as_mut_ref<'a>(ptr: *mut bindings::drm_plane_state) -> &'a mut Self {
-        unsafe { &mut *(ptr as *mut _) }
+    fn __destroy_state(state: *mut bindings::drm_plane_state) {
+        // SAFETY: This would not be called without a plane state to destroy
+        unsafe { bindings::__drm_atomic_helper_plane_destroy_state(state) };
     }
 
-    /// Obtain a mutable pointer to the base plane state, for use in FFI calls
-    unsafe fn as_mut_ptr(&mut self) -> *mut bindings::drm_plane_state {
-        &mut self.state
+    fn __reset_state(plane: *mut bindings::drm_plane, state: *mut bindings::drm_plane_state) {
+        unsafe { bindings::__drm_atomic_helper_plane_reset(plane, state) }
     }
 }
 
 unsafe impl Zeroable for bindings::drm_plane_state { }
 
-unsafe extern "C" fn atomic_duplicate_state_callback<T: DriverPlaneState>(
+unsafe extern "C" fn atomic_duplicate_state_callback<T: IntoPlaneState>(
     plane: *mut bindings::drm_plane
 ) -> *mut bindings::drm_plane_state
 {
@@ -240,55 +296,38 @@ unsafe impl Zeroable for bindings::drm_plane_state { }
         return null_mut();
     }
 
-    // SAFETY: We just verified that `state` is non-null, and we're guaranteed by our bindings that
-    // `state` is of type `PlaneState<T>`.
-    let state = unsafe { PlaneState::<T>::as_ref(state) };
-
-    let mut new: Result<Box<PlaneState<T>>> = Box::try_init(try_init!(PlaneState::<T> {
-        state: bindings::drm_plane_state { ..Default::default() },
-        inner: state.inner.clone()
-    }));
-
-    if let Ok(mut new) = new {
-        // SAFETY: Just a lil' FFI call, nothing special here
-        unsafe { bindings::__drm_atomic_helper_plane_duplicate_state(plane, new.as_mut_ptr()) };
-
-        new.into_raw()
-    } else {
-        null_mut()
-    }
+    // SAFETY: We just checked that the state is non-null, and we're guaranteed this operation is
+    // safe via type invariance
+    unsafe { T::ref_from_raw(state) }.__duplicate_state(plane).map_or(null_mut(), T::into_raw)
 }
 
-unsafe extern "C" fn atomic_destroy_state_callback<T: DriverPlaneState>(
+unsafe extern "C" fn atomic_destroy_state_callback<T: IntoPlaneState>(
     _plane: *mut bindings::drm_plane,
-    plane_state: *mut bindings::drm_plane_state
+    state: *mut bindings::drm_plane_state
 ) {
-    // SAFETY: This callback wouldn't be called unless there a plane state to destroy
-    unsafe { bindings::__drm_atomic_helper_plane_destroy_state(plane_state) };
+    T::__destroy_state(state);
 
-    // SAFETY: We're guaranteed by type invariants that plane_state is of type PlaneState<T>, and
-    // since this is the destructor callback for DRM - we're guaranteed to hold the only remaining
-    // reference to this state
-    unsafe { drop(PlaneState::<T>::from_raw(plane_state)) };
+    // SAFETY: We're guaranteed by type invariance that state of type T, and __destroy_state() does
+    // not perform any deallocations
+    drop(unsafe { T::from_raw(state) });
 }
 
-unsafe extern "C" fn plane_reset_callback<T: DriverPlaneState>(
+unsafe extern "C" fn plane_reset_callback<T: IntoPlaneState>(
     plane: *mut bindings::drm_plane,
 ) {
-    // SAFETY: The only entrypoint to this function lives in `plane` so it must be valid, and
+    // SAFETY: The only entrypoint to this function lives in `plane` so it must be valid
     let state = unsafe { (*plane).state };
     if !state.is_null() {
-        // SAFETY: We're guaranteed by type invariance that this plane's state is of type
-        // DriverPlaneState<T>
+        // SAFETY: We're guaranteed by type invariance that this planes state is of type T
         unsafe { atomic_destroy_state_callback::<T>(plane, state) }
     }
 
     // Unfortunately, this is the best we can do at the moment as this FFI callback was mistakenly
     // presumed to be infallible :(
-    let new = Box::try_new(PlaneState::<T>::default()).expect("Blame the API, sorry!");
+    let new = Box::try_new(T::default()).expect("Blame the API, sorry!");
 
-    // SAFETY: DRM takes ownership of the state from here and assigns it to the plane
-    unsafe { bindings::__drm_atomic_helper_plane_reset(plane, new.into_raw()) };
+    // DRM takes ownership of the state from here, resets it, and then assigns it to the plane
+    T::__reset_state(plane, new.into_raw());
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 4/4] WIP: rust/drm/kms: Add ShadowPlaneState<T>
  2024-03-22 22:03 [RFC WIP 0/4] Rust bindings for KMS + RVKMS Lyude Paul
                   ` (2 preceding siblings ...)
  2024-03-22 22:03 ` [PATCH 3/4] rust/drm/kms: Extract PlaneState<T> into IntoPlaneState Lyude Paul
@ 2024-03-22 22:03 ` Lyude Paul
  3 siblings, 0 replies; 12+ messages in thread
From: Lyude Paul @ 2024-03-22 22:03 UTC (permalink / raw)
  To: dri-devel
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Miguel Ojeda, Alex Gaynor,
	Wedson Almeida Filho, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Asahi Lina,
	Martin Rodriguez Reboredo, FUJITA Tomonori, open list,
	open list:RUST

Signed-off-by: Lyude Paul <lyude@redhat.com>
---
 drivers/gpu/drm/rvkms/plane.rs           |  7 +++-
 rust/bindings/bindings_helper.h          |  2 +
 rust/kernel/drm/kms.rs                   |  1 +
 rust/kernel/drm/kms/gem_atomic_helper.rs | 48 ++++++++++++++++++++++++
 4 files changed, 56 insertions(+), 2 deletions(-)
 create mode 100644 rust/kernel/drm/kms/gem_atomic_helper.rs

diff --git a/drivers/gpu/drm/rvkms/plane.rs b/drivers/gpu/drm/rvkms/plane.rs
index d98a1f7bf79e2..5fb1b63842929 100644
--- a/drivers/gpu/drm/rvkms/plane.rs
+++ b/drivers/gpu/drm/rvkms/plane.rs
@@ -4,7 +4,10 @@
     prelude::*,
     drm::{
         device::Device,
-        kms::plane::{self, DriverPlaneState},
+        kms::{
+            plane::{self, DriverPlaneState},
+            gem_atomic_helper::ShadowPlaneState,
+        }
     },
 };
 
@@ -15,7 +18,7 @@ pub(crate) struct DriverPlane {
 }
 
 pub(crate) type Plane = plane::Plane<DriverPlane>;
-pub(crate) type PlaneState = plane::PlaneState<RvkmsPlaneState>;
+pub(crate) type PlaneState = ShadowPlaneState<RvkmsPlaneState>;
 
 impl plane::DriverPlane for DriverPlane {
     type Initializer = impl PinInit<Self, Error>;
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 5856afbe6e8f6..73a5eb00e8625 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -12,6 +12,8 @@
 #include <drm/drm_edid.h>
 #include <drm/drm_file.h>
 #include <drm/drm_gem.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_gem_shmem_helper.h>
 #include <drm/drm_ioctl.h>
diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs
index b55d14415367a..14f4c3842ada0 100644
--- a/rust/kernel/drm/kms.rs
+++ b/rust/kernel/drm/kms.rs
@@ -6,6 +6,7 @@
 pub mod crtc;
 pub mod encoder;
 pub mod plane;
+pub mod gem_atomic_helper;
 
 use crate::{
     drm::{drv, device::Device},
diff --git a/rust/kernel/drm/kms/gem_atomic_helper.rs b/rust/kernel/drm/kms/gem_atomic_helper.rs
new file mode 100644
index 0000000000000..85bc3df32d8b7
--- /dev/null
+++ b/rust/kernel/drm/kms/gem_atomic_helper.rs
@@ -0,0 +1,48 @@
+use crate::{
+    prelude::*,
+    private::Sealed,
+    bindings,
+    init::Zeroable,
+};
+use super::plane::{IntoPlaneState, DriverPlaneState};
+
+unsafe impl Zeroable for bindings::drm_shadow_plane_state {}
+
+#[derive(Default)]
+#[repr(C)]
+pub struct ShadowPlaneState<T: DriverPlaneState> {
+    shadow_state: bindings::drm_shadow_plane_state,
+    inner: T,
+}
+
+impl<T: DriverPlaneState> Sealed for ShadowPlaneState<T> {}
+
+static_assert!(crate::offset_of!(bindings::drm_shadow_plane_state, base) == 0);
+
+// SAFETY: Our data layout starts with drm_plane_state (contained at the start of
+// drm_shadow_plane_state)
+unsafe impl<T: DriverPlaneState> IntoPlaneState for ShadowPlaneState<T> {
+    fn __duplicate_state(&self, plane: *mut bindings::drm_plane) -> Result<Box<Self>> {
+        let mut new: Box<Self> = Box::try_init(try_init!(Self {
+            shadow_state: bindings::drm_shadow_plane_state { ..Default::default() },
+            inner: self.inner.clone()
+        }))?;
+
+        // SAFETY: FFI call with no special requirements
+        unsafe { bindings::__drm_gem_duplicate_shadow_plane_state(plane, &mut new.shadow_state) };
+
+        Ok(new)
+    }
+
+    fn __destroy_state(state: *mut bindings::drm_plane_state) {
+        // SAFETY: This would not be called without a plane state to destroy, and our data layout
+        // starts with `bindings::drm_plane_state`
+        unsafe { bindings::__drm_gem_destroy_shadow_plane_state(state.cast()) };
+    }
+
+    fn __reset_state(plane: *mut bindings::drm_plane, state: *mut bindings::drm_plane_state) {
+        // SAFETY: This would not be called without a plane state to reset, and our data layout
+        // starts with `bindings::drm_plane_state`
+        unsafe { bindings::__drm_gem_reset_shadow_plane(plane, state.cast()) }
+    }
+}
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/4] WIP: rust: Add basic KMS bindings
  2024-03-22 22:03 ` [PATCH 1/4] WIP: rust: Add basic KMS bindings Lyude Paul
@ 2024-03-27 20:50   ` Benno Lossin
  2024-04-22  1:47     ` Lyude Paul
  0 siblings, 1 reply; 12+ messages in thread
From: Benno Lossin @ 2024-03-27 20:50 UTC (permalink / raw)
  To: Lyude Paul, dri-devel
  Cc: Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho, Boqun Feng,
	Gary Guo, Björn Roy Baron, Andreas Hindborg, Alice Ryhl,
	Asahi Lina, Martin Rodriguez Reboredo, FUJITA Tomonori,
	Danilo Krummrich, linux-kernel, rust-for-linux

Hi,

I just took a quick look and commented on the things that stuck
out to me. Some general things:
- several `unsafe` blocks have missing SAFETY comments,
- missing documentation and examples.

On 22.03.24 23:03, Lyude Paul wrote:
> Signed-off-by: Lyude Paul <lyude@redhat.com>
> ---
>  rust/bindings/bindings_helper.h  |   4 +
>  rust/helpers.c                   |  17 ++
>  rust/kernel/drm/device.rs        |   2 +
>  rust/kernel/drm/drv.rs           | 115 +++++++--
>  rust/kernel/drm/kms.rs           | 146 +++++++++++
>  rust/kernel/drm/kms/connector.rs | 404 +++++++++++++++++++++++++++++++
>  rust/kernel/drm/kms/crtc.rs      | 300 +++++++++++++++++++++++
>  rust/kernel/drm/kms/encoder.rs   | 175 +++++++++++++
>  rust/kernel/drm/kms/plane.rs     | 300 +++++++++++++++++++++++
>  rust/kernel/drm/mod.rs           |   1 +
>  10 files changed, 1448 insertions(+), 16 deletions(-)

Please try to break this up into smaller patches. It makes review
a lot easier!

[...]

> diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs
> new file mode 100644
> index 0000000000000..b55d14415367a
> --- /dev/null
> +++ b/rust/kernel/drm/kms.rs
> @@ -0,0 +1,146 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! KMS driver abstractions for rust.
> +
> +pub mod connector;
> +pub mod crtc;
> +pub mod encoder;
> +pub mod plane;
> +
> +use crate::{
> +    drm::{drv, device::Device},
> +    prelude::*,
> +    types::ARef,
> +    private::Sealed
> +};
> +use core::{
> +    ops::Deref,
> +    ptr,
> +};
> +use bindings;
> +
> +#[derive(Copy, Clone)]
> +pub struct ModeConfigInfo {
> +    /// The minimum (w, h) resolution this driver can support
> +    pub min_resolution: (i32, i32),
> +    /// The maximum (w, h) resolution this driver can support
> +    pub max_resolution: (i32, i32),
> +    /// The maximum (w, h) cursor size this driver can support
> +    pub max_cursor: (u32, u32),
> +    /// The preferred depth for dumb ioctls
> +    pub preferred_depth: u32,
> +}
> +
> +// TODO: I am not totally sure about this. Ideally, I'd like a nice way of hiding KMS-specific
> +// functions for DRM drivers which don't implement KMS - so that we don't have to have a bunch of
> +// random modesetting functions all over the DRM device trait. But, unfortunately I don't know of
> +// any nice way of doing that yet :(

I don't follow, can't you put the KMS specific functions into the
KmsDriver trait?

> +
> +/// An atomic KMS driver implementation
> +pub trait KmsDriver: drv::Driver { }
> +
> +impl<T: KmsDriver> Device<T> {
> +    pub fn mode_config_reset(&self) {
> +        // SAFETY: The previous build assertion ensures this can only be called for devices with KMS
> +        // support, which means mode_config is initialized
> +        unsafe { bindings::drm_mode_config_reset(self.drm.get()) }
> +    }
> +}
> +
> +/// Main trait for a modesetting object in DRM
> +pub trait ModeObject: Sealed + Send + Sync {
> +    /// The parent driver for this ModeObject
> +    type Driver: KmsDriver;
> +
> +    /// Return the `drv::Device` for this `ModeObject`
> +    fn drm_dev(&self) -> &Device<Self::Driver>;
> +}

[...]

> diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs
> new file mode 100644
> index 0000000000000..88dfa946d306b
> --- /dev/null
> +++ b/rust/kernel/drm/kms/connector.rs
> @@ -0,0 +1,404 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! Rust bindings for DRM connectors
> +
> +use crate::{
> +    bindings,
> +    sync::ArcBorrow,
> +    drm::{
> +        drv::{Driver, FEAT_MODESET},
> +        device::Device,
> +    },
> +    types::{AlwaysRefCounted, Opaque, ARef},
> +    prelude::*,
> +    init::Zeroable,
> +    error::{to_result, from_result},
> +    build_error,
> +};
> +use core::{
> +    marker::PhantomPinned,
> +    ptr::null_mut,
> +    mem,
> +    ptr::{self, NonNull},
> +    ffi::*,
> +    ops::Deref,
> +};
> +use super::{
> +    ModeObject,
> +    ModeConfigGuard,
> +    encoder::{Encoder, DriverEncoder},
> +    KmsDriver,
> +};
> +use macros::pin_data;
> +
> +// XXX: This is :\, figure out a better way at some point?
> +pub use bindings::{
> +    DRM_MODE_CONNECTOR_Unknown,
> +    DRM_MODE_CONNECTOR_VGA,
> +    DRM_MODE_CONNECTOR_DVII,
> +    DRM_MODE_CONNECTOR_DVID,
> +    DRM_MODE_CONNECTOR_DVIA,
> +    DRM_MODE_CONNECTOR_Composite,
> +    DRM_MODE_CONNECTOR_SVIDEO,
> +    DRM_MODE_CONNECTOR_LVDS,
> +    DRM_MODE_CONNECTOR_Component,
> +    DRM_MODE_CONNECTOR_9PinDIN,
> +    DRM_MODE_CONNECTOR_DisplayPort,
> +    DRM_MODE_CONNECTOR_HDMIA,
> +    DRM_MODE_CONNECTOR_HDMIB,
> +    DRM_MODE_CONNECTOR_TV,
> +    DRM_MODE_CONNECTOR_eDP,
> +    DRM_MODE_CONNECTOR_VIRTUAL,
> +    DRM_MODE_CONNECTOR_DSI,
> +    DRM_MODE_CONNECTOR_DPI,
> +    DRM_MODE_CONNECTOR_WRITEBACK,
> +    DRM_MODE_CONNECTOR_SPI,
> +    DRM_MODE_CONNECTOR_USB,
> +};
> +
> +/// A DRM connector implementation
> +pub trait DriverConnector: Send + Sync + Sized {
> +    /// The return type of the new() function. Should be `impl PinInit<Self, Error>`.
> +    /// TODO: Remove this when return_position_impl_trait_in_trait is stable.
> +    type Initializer: PinInit<Self, Error>;

This has been stabilized in 1.75.0, so now you should be able to write

     fn new(dev: &Device<Self::Driver>, args: Self::Args) -> impl PinInit<Self, Error>;

> +
> +    /// The data type to use for passing incoming arguments for new `Connector<T>` instances
> +    /// Drivers which don't care about this can just use `()`
> +    type Args;
> +
> +    /// The parent driver for this DRM connector implementation
> +    type Driver: KmsDriver;
> +
> +    /// The atomic state implementation for this DRM connector implementation
> +    type State: DriverConnectorState;
> +
> +    /// Create a new instance of the private driver data struct for this connector in-place
> +    fn new(dev: &Device<Self::Driver>, args: Self::Args) -> Self::Initializer;
> +
> +    /// Retrieve a list of available display modes for this connector
> +    fn get_modes<'a>(
> +        connector: ConnectorGuard<'a, Self>,
> +        guard: &ModeConfigGuard<'a, Self::Driver>
> +    ) -> i32;
> +}

[...]

> diff --git a/rust/kernel/drm/kms/crtc.rs b/rust/kernel/drm/kms/crtc.rs
> new file mode 100644
> index 0000000000000..3d072028a4884
> --- /dev/null
> +++ b/rust/kernel/drm/kms/crtc.rs
> @@ -0,0 +1,300 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +
> +//! KMS driver abstractions for rust.
> +
> +use super::{
> +    plane::*,
> +    ModeObject,
> +    StaticModeObject,
> +    KmsDriver
> +};
> +use crate::{
> +    bindings,
> +    drm::{drv::Driver, device::Device},
> +    device,
> +    prelude::*,
> +    types::Opaque,
> +    init::Zeroable,
> +    sync::Arc,
> +    error::to_result,
> +};
> +use core::{
> +    cell::UnsafeCell,
> +    marker::PhantomPinned,
> +    ptr::{null, null_mut},
> +    ops::Deref,
> +};
> +use macros::vtable;
> +
> +/// A typed KMS CRTC with a specific driver.
> +#[repr(C)]
> +#[pin_data]
> +pub struct Crtc<T: DriverCrtc> {
> +    // The FFI drm_crtc object
> +    pub(super) crtc: Opaque<bindings::drm_crtc>,
> +    /// The driver's private inner data
> +    #[pin]
> +    inner: T,
> +    #[pin]
> +    _p: PhantomPinned,

Instead of adding this field, you can mark the `crtc` field above as
`#[pin]`. This is because of 0b4e3b6f6b79 ("rust: types: make `Opaque`
be `!Unpin`").

-- 
Cheers,
Benno

> +}
> +
> +/// KMS CRTC object functions, which must be implemented by drivers.
> +pub trait DriverCrtc: Send + Sync + Sized {
> +    /// The return type of the new() function. Should be `impl PinInit<Self, Error>`.
> +    /// TODO: Remove this when return_position_impl_trait_in_trait is stable.
> +    type Initializer: PinInit<Self, Error>;
> +
> +    /// The data type to use for passing incoming arguments for new `Crtc<T>` instances
> +    /// Drivers which don't care about this can just use `()`
> +    type Args;
> +
> +    /// The parent driver implementation
> +    type Driver: KmsDriver;
> +
> +    /// The type for this driver's drm_crtc_state implementation
> +    type State: DriverCrtcState;
> +
> +    /// Create a new CRTC for this driver
> +    fn new(device: &Device<Self::Driver>, args: Self::Args) -> Self::Initializer;
> +}


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/4] WIP: drm: Introduce rvkms
  2024-03-22 22:03 ` [PATCH 2/4] WIP: drm: Introduce rvkms Lyude Paul
@ 2024-03-27 21:06   ` Benno Lossin
  2024-04-22  1:54     ` Lyude Paul
  0 siblings, 1 reply; 12+ messages in thread
From: Benno Lossin @ 2024-03-27 21:06 UTC (permalink / raw)
  To: Lyude Paul, dri-devel
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Miguel Ojeda, Alex Gaynor,
	Wedson Almeida Filho, Boqun Feng, Gary Guo, Björn Roy Baron,
	Andreas Hindborg, Alice Ryhl, linux-kernel, rust-for-linux

On 22.03.24 23:03, Lyude Paul wrote:
> diff --git a/drivers/gpu/drm/rvkms/connector.rs b/drivers/gpu/drm/rvkms/connector.rs
> new file mode 100644
> index 0000000000000..40f84d38437ee
> --- /dev/null
> +++ b/drivers/gpu/drm/rvkms/connector.rs
> @@ -0,0 +1,55 @@
> +// TODO: License and stuff
> +// Contain's rvkms's drm_connector implementation
> +
> +use super::{RvkmsDriver, RvkmsDevice, MAX_RES, DEFAULT_RES};
> +use kernel::{
> +    prelude::*,
> +    drm::{
> +        device::Device,
> +        kms::{
> +            connector::{self, ConnectorGuard},
> +            ModeConfigGuard
> +        }
> +    },
> +    prelude::*
> +};
> +use core::marker::PhantomPinned;
> +
> +#[pin_data]
> +pub(crate) struct DriverConnector {
> +    #[pin]
> +    _p: PhantomPinned
> +}

This struct does not need to be annotated with `#[pin_data]`, this
should just work:

pub(crate) struct DriverConnector;

> +
> +pub(crate) type Connector = connector::Connector<DriverConnector>;
> +
> +impl connector::DriverConnector for DriverConnector {
> +    type Initializer = impl PinInit<Self, Error>;
> +
> +    type State = ConnectorState;
> +
> +    type Driver = RvkmsDriver;
> +
> +    type Args = ();
> +
> +    fn new(dev: &Device<Self::Driver>, args: Self::Args) -> Self::Initializer {

And then here just return `Self`.

This works, since there is a blanket impl `PinInit<T, E> for T`.

Looking at how you use this API, I am not sure if you actually need
pin-init for the type that implements `DriverConnector`.
Do you need to store eg `Mutex<T>` or something else that needs
pin-init in here in a more complex driver?

-- 
Cheers,
Benno

> +        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;
> +}


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/4] WIP: rust: Add basic KMS bindings
  2024-03-27 20:50   ` Benno Lossin
@ 2024-04-22  1:47     ` Lyude Paul
  2024-04-25 15:42       ` Benno Lossin
  0 siblings, 1 reply; 12+ messages in thread
From: Lyude Paul @ 2024-04-22  1:47 UTC (permalink / raw)
  To: Benno Lossin, dri-devel
  Cc: Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho, Boqun Feng,
	Gary Guo, Björn Roy Baron, Andreas Hindborg, Alice Ryhl,
	Asahi Lina, Martin Rodriguez Reboredo, FUJITA Tomonori,
	Danilo Krummrich, linux-kernel, rust-for-linux

On Wed, 2024-03-27 at 20:50 +0000, Benno Lossin wrote:
> Hi,
> 
> I just took a quick look and commented on the things that stuck
> out to me. Some general things:
> - several `unsafe` blocks have missing SAFETY comments,
> - missing documentation and examples.

This is really early on - so I had wanted to post a WIP before I
actually wrote up everything to make sure I'm going in the right
direction (I'm certainly not planning on leaving things undocumented
when this is actually ready for submission :).

> 
> On 22.03.24 23:03, Lyude Paul wrote:
> > Signed-off-by: Lyude Paul <lyude@redhat.com>
> > ---
> >  rust/bindings/bindings_helper.h  |   4 +
> >  rust/helpers.c                   |  17 ++
> >  rust/kernel/drm/device.rs        |   2 +
> >  rust/kernel/drm/drv.rs           | 115 +++++++--
> >  rust/kernel/drm/kms.rs           | 146 +++++++++++
> >  rust/kernel/drm/kms/connector.rs | 404
> > +++++++++++++++++++++++++++++++
> >  rust/kernel/drm/kms/crtc.rs      | 300 +++++++++++++++++++++++
> >  rust/kernel/drm/kms/encoder.rs   | 175 +++++++++++++
> >  rust/kernel/drm/kms/plane.rs     | 300 +++++++++++++++++++++++
> >  rust/kernel/drm/mod.rs           |   1 +
> >  10 files changed, 1448 insertions(+), 16 deletions(-)
> 
> Please try to break this up into smaller patches. It makes review
> a lot easier!

I'll definitely try to do that next time!

> 
> [...]
> 
> > diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs
> > new file mode 100644
> > index 0000000000000..b55d14415367a
> > --- /dev/null
> > +++ b/rust/kernel/drm/kms.rs
> > @@ -0,0 +1,146 @@
> > +// SPDX-License-Identifier: GPL-2.0 OR MIT
> > +
> > +//! KMS driver abstractions for rust.
> > +
> > +pub mod connector;
> > +pub mod crtc;
> > +pub mod encoder;
> > +pub mod plane;
> > +
> > +use crate::{
> > +    drm::{drv, device::Device},
> > +    prelude::*,
> > +    types::ARef,
> > +    private::Sealed
> > +};
> > +use core::{
> > +    ops::Deref,
> > +    ptr,
> > +};
> > +use bindings;
> > +
> > +#[derive(Copy, Clone)]
> > +pub struct ModeConfigInfo {
> > +    /// The minimum (w, h) resolution this driver can support
> > +    pub min_resolution: (i32, i32),
> > +    /// The maximum (w, h) resolution this driver can support
> > +    pub max_resolution: (i32, i32),
> > +    /// The maximum (w, h) cursor size this driver can support
> > +    pub max_cursor: (u32, u32),
> > +    /// The preferred depth for dumb ioctls
> > +    pub preferred_depth: u32,
> > +}
> > +
> > +// TODO: I am not totally sure about this. Ideally, I'd like a
> > nice way of hiding KMS-specific
> > +// functions for DRM drivers which don't implement KMS - so that
> > we don't have to have a bunch of
> > +// random modesetting functions all over the DRM device trait.
> > But, unfortunately I don't know of
> > +// any nice way of doing that yet :(
> 
> I don't follow, can't you put the KMS specific functions into the
> KmsDriver trait?

I can, lol. I realized how that would work a little while after writing
this, so I'm not quite sure where my confusion was with this - so I'll
fix this on the next version I send out.

> 
> > +
> > +/// An atomic KMS driver implementation
> > +pub trait KmsDriver: drv::Driver { }
> > +
> > +impl<T: KmsDriver> Device<T> {
> > +    pub fn mode_config_reset(&self) {
> > +        // SAFETY: The previous build assertion ensures this can
> > only be called for devices with KMS
> > +        // support, which means mode_config is initialized
> > +        unsafe { bindings::drm_mode_config_reset(self.drm.get()) }
> > +    }
> > +}
> > +
> > +/// Main trait for a modesetting object in DRM
> > +pub trait ModeObject: Sealed + Send + Sync {
> > +    /// The parent driver for this ModeObject
> > +    type Driver: KmsDriver;
> > +
> > +    /// Return the `drv::Device` for this `ModeObject`
> > +    fn drm_dev(&self) -> &Device<Self::Driver>;
> > +}
> 
> [...]
> 
> > diff --git a/rust/kernel/drm/kms/connector.rs
> > b/rust/kernel/drm/kms/connector.rs
> > new file mode 100644
> > index 0000000000000..88dfa946d306b
> > --- /dev/null
> > +++ b/rust/kernel/drm/kms/connector.rs
> > @@ -0,0 +1,404 @@
> > +// SPDX-License-Identifier: GPL-2.0 OR MIT
> > +
> > +//! Rust bindings for DRM connectors
> > +
> > +use crate::{
> > +    bindings,
> > +    sync::ArcBorrow,
> > +    drm::{
> > +        drv::{Driver, FEAT_MODESET},
> > +        device::Device,
> > +    },
> > +    types::{AlwaysRefCounted, Opaque, ARef},
> > +    prelude::*,
> > +    init::Zeroable,
> > +    error::{to_result, from_result},
> > +    build_error,
> > +};
> > +use core::{
> > +    marker::PhantomPinned,
> > +    ptr::null_mut,
> > +    mem,
> > +    ptr::{self, NonNull},
> > +    ffi::*,
> > +    ops::Deref,
> > +};
> > +use super::{
> > +    ModeObject,
> > +    ModeConfigGuard,
> > +    encoder::{Encoder, DriverEncoder},
> > +    KmsDriver,
> > +};
> > +use macros::pin_data;
> > +
> > +// XXX: This is :\, figure out a better way at some point?
> > +pub use bindings::{
> > +    DRM_MODE_CONNECTOR_Unknown,
> > +    DRM_MODE_CONNECTOR_VGA,
> > +    DRM_MODE_CONNECTOR_DVII,
> > +    DRM_MODE_CONNECTOR_DVID,
> > +    DRM_MODE_CONNECTOR_DVIA,
> > +    DRM_MODE_CONNECTOR_Composite,
> > +    DRM_MODE_CONNECTOR_SVIDEO,
> > +    DRM_MODE_CONNECTOR_LVDS,
> > +    DRM_MODE_CONNECTOR_Component,
> > +    DRM_MODE_CONNECTOR_9PinDIN,
> > +    DRM_MODE_CONNECTOR_DisplayPort,
> > +    DRM_MODE_CONNECTOR_HDMIA,
> > +    DRM_MODE_CONNECTOR_HDMIB,
> > +    DRM_MODE_CONNECTOR_TV,
> > +    DRM_MODE_CONNECTOR_eDP,
> > +    DRM_MODE_CONNECTOR_VIRTUAL,
> > +    DRM_MODE_CONNECTOR_DSI,
> > +    DRM_MODE_CONNECTOR_DPI,
> > +    DRM_MODE_CONNECTOR_WRITEBACK,
> > +    DRM_MODE_CONNECTOR_SPI,
> > +    DRM_MODE_CONNECTOR_USB,
> > +};
> > +
> > +/// A DRM connector implementation
> > +pub trait DriverConnector: Send + Sync + Sized {
> > +    /// The return type of the new() function. Should be `impl
> > PinInit<Self, Error>`.
> > +    /// TODO: Remove this when return_position_impl_trait_in_trait
> > is stable.
> > +    type Initializer: PinInit<Self, Error>;
> 
> This has been stabilized in 1.75.0, so now you should be able to
> write
> 
>      fn new(dev: &Device<Self::Driver>, args: Self::Args) -> impl
> PinInit<Self, Error>;

Ack for this and the below comment as well!

> 
> > +
> > +    /// The data type to use for passing incoming arguments for
> > new `Connector<T>` instances
> > +    /// Drivers which don't care about this can just use `()`
> > +    type Args;
> > +
> > +    /// The parent driver for this DRM connector implementation
> > +    type Driver: KmsDriver;
> > +
> > +    /// The atomic state implementation for this DRM connector
> > implementation
> > +    type State: DriverConnectorState;
> > +
> > +    /// Create a new instance of the private driver data struct
> > for this connector in-place
> > +    fn new(dev: &Device<Self::Driver>, args: Self::Args) ->
> > Self::Initializer;
> > +
> > +    /// Retrieve a list of available display modes for this
> > connector
> > +    fn get_modes<'a>(
> > +        connector: ConnectorGuard<'a, Self>,
> > +        guard: &ModeConfigGuard<'a, Self::Driver>
> > +    ) -> i32;
> > +}
> 
> [...]
> 
> > diff --git a/rust/kernel/drm/kms/crtc.rs
> > b/rust/kernel/drm/kms/crtc.rs
> > new file mode 100644
> > index 0000000000000..3d072028a4884
> > --- /dev/null
> > +++ b/rust/kernel/drm/kms/crtc.rs
> > @@ -0,0 +1,300 @@
> > +// SPDX-License-Identifier: GPL-2.0 OR MIT
> > +
> > +//! KMS driver abstractions for rust.
> > +
> > +use super::{
> > +    plane::*,
> > +    ModeObject,
> > +    StaticModeObject,
> > +    KmsDriver
> > +};
> > +use crate::{
> > +    bindings,
> > +    drm::{drv::Driver, device::Device},
> > +    device,
> > +    prelude::*,
> > +    types::Opaque,
> > +    init::Zeroable,
> > +    sync::Arc,
> > +    error::to_result,
> > +};
> > +use core::{
> > +    cell::UnsafeCell,
> > +    marker::PhantomPinned,
> > +    ptr::{null, null_mut},
> > +    ops::Deref,
> > +};
> > +use macros::vtable;
> > +
> > +/// A typed KMS CRTC with a specific driver.
> > +#[repr(C)]
> > +#[pin_data]
> > +pub struct Crtc<T: DriverCrtc> {
> > +    // The FFI drm_crtc object
> > +    pub(super) crtc: Opaque<bindings::drm_crtc>,
> > +    /// The driver's private inner data
> > +    #[pin]
> > +    inner: T,
> > +    #[pin]
> > +    _p: PhantomPinned,
> 
> Instead of adding this field, you can mark the `crtc` field above as
> `#[pin]`. This is because of 0b4e3b6f6b79 ("rust: types: make
> `Opaque`
> be `!Unpin`").
> 

-- 
Cheers,
 Lyude Paul (she/her)
 Software Engineer at Red Hat


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/4] WIP: drm: Introduce rvkms
  2024-03-27 21:06   ` Benno Lossin
@ 2024-04-22  1:54     ` Lyude Paul
  2024-04-25 15:46       ` Benno Lossin
  0 siblings, 1 reply; 12+ messages in thread
From: Lyude Paul @ 2024-04-22  1:54 UTC (permalink / raw)
  To: Benno Lossin, dri-devel
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Miguel Ojeda, Alex Gaynor,
	Wedson Almeida Filho, Boqun Feng, Gary Guo, Björn Roy Baron,
	Andreas Hindborg, Alice Ryhl, linux-kernel, rust-for-linux

On Wed, 2024-03-27 at 21:06 +0000, Benno Lossin wrote:
> On 22.03.24 23:03, Lyude Paul wrote:
> > diff --git a/drivers/gpu/drm/rvkms/connector.rs
> > b/drivers/gpu/drm/rvkms/connector.rs
> > new file mode 100644
> > index 0000000000000..40f84d38437ee
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rvkms/connector.rs
> > @@ -0,0 +1,55 @@
> > +// TODO: License and stuff
> > +// Contain's rvkms's drm_connector implementation
> > +
> > +use super::{RvkmsDriver, RvkmsDevice, MAX_RES, DEFAULT_RES};
> > +use kernel::{
> > +    prelude::*,
> > +    drm::{
> > +        device::Device,
> > +        kms::{
> > +            connector::{self, ConnectorGuard},
> > +            ModeConfigGuard
> > +        }
> > +    },
> > +    prelude::*
> > +};
> > +use core::marker::PhantomPinned;
> > +
> > +#[pin_data]
> > +pub(crate) struct DriverConnector {
> > +    #[pin]
> > +    _p: PhantomPinned
> > +}
> 
> This struct does not need to be annotated with `#[pin_data]`, this
> should just work:
> 
> pub(crate) struct DriverConnector;
> 
> > +
> > +pub(crate) type Connector = connector::Connector<DriverConnector>;
> > +
> > +impl connector::DriverConnector for DriverConnector {
> > +    type Initializer = impl PinInit<Self, Error>;
> > +
> > +    type State = ConnectorState;
> > +
> > +    type Driver = RvkmsDriver;
> > +
> > +    type Args = ();
> > +
> > +    fn new(dev: &Device<Self::Driver>, args: Self::Args) ->
> > Self::Initializer {
> 
> And then here just return `Self`.
> 
> This works, since there is a blanket impl `PinInit<T, E> for T`.
> 
> Looking at how you use this API, I am not sure if you actually need
> pin-init for the type that implements `DriverConnector`.
> Do you need to store eg `Mutex<T>` or something else that needs
> pin-init in here in a more complex driver?

Most likely yes - a lot of drivers have various private locks contained
within their subclassed mode objects. I'm not sure we will in rvkms's
connector since vkms doesn't really do much with connectors - but we at
a minimum be using pinned types (spinlocks and hrtimers) in our
DriverCrtc implementation once I've started implementing support for
vblanks[1]

[1]
https://www.kernel.org/doc/html/v6.9-rc5/gpu/drm-kms.html?highlight=vblank#vertical-blanking

In nova (the main reason I'm working on rvkms in the first place),
we'll definitely have locks in our connectors and possibly other types.

> 

-- 
Cheers,
 Lyude Paul (she/her)
 Software Engineer at Red Hat


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/4] WIP: rust: Add basic KMS bindings
  2024-04-22  1:47     ` Lyude Paul
@ 2024-04-25 15:42       ` Benno Lossin
  0 siblings, 0 replies; 12+ messages in thread
From: Benno Lossin @ 2024-04-25 15:42 UTC (permalink / raw)
  To: Lyude Paul, dri-devel
  Cc: Miguel Ojeda, Alex Gaynor, Wedson Almeida Filho, Boqun Feng,
	Gary Guo, Björn Roy Baron, Andreas Hindborg, Alice Ryhl,
	Asahi Lina, Martin Rodriguez Reboredo, FUJITA Tomonori,
	Danilo Krummrich, linux-kernel, rust-for-linux

On 22.04.24 03:47, Lyude Paul wrote:
> On Wed, 2024-03-27 at 20:50 +0000, Benno Lossin wrote:
>> Hi,
>>
>> I just took a quick look and commented on the things that stuck
>> out to me. Some general things:
>> - several `unsafe` blocks have missing SAFETY comments,
>> - missing documentation and examples.
> 
> This is really early on - so I had wanted to post a WIP before I
> actually wrote up everything to make sure I'm going in the right
> direction (I'm certainly not planning on leaving things undocumented
> when this is actually ready for submission :).

No worries, I just wanted to point out everything that I found.

One thing that I missed was your "RFC WIP" in your cover letter. I think
that it's a good idea to put "RFC" onto every patch, that way people
without context immediately know that it is not yet ready.

-- 
Cheers,
Benno


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/4] WIP: drm: Introduce rvkms
  2024-04-22  1:54     ` Lyude Paul
@ 2024-04-25 15:46       ` Benno Lossin
  2024-04-29 19:54         ` Lyude Paul
  0 siblings, 1 reply; 12+ messages in thread
From: Benno Lossin @ 2024-04-25 15:46 UTC (permalink / raw)
  To: Lyude Paul, dri-devel
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Miguel Ojeda, Alex Gaynor,
	Wedson Almeida Filho, Boqun Feng, Gary Guo, Björn Roy Baron,
	Andreas Hindborg, Alice Ryhl, linux-kernel, rust-for-linux

On 22.04.24 03:54, Lyude Paul wrote:
> On Wed, 2024-03-27 at 21:06 +0000, Benno Lossin wrote:
>> On 22.03.24 23:03, Lyude Paul wrote:
>>> +
>>> +pub(crate) type Connector = connector::Connector<DriverConnector>;
>>> +
>>> +impl connector::DriverConnector for DriverConnector {
>>> +    type Initializer = impl PinInit<Self, Error>;
>>> +
>>> +    type State = ConnectorState;
>>> +
>>> +    type Driver = RvkmsDriver;
>>> +
>>> +    type Args = ();
>>> +
>>> +    fn new(dev: &Device<Self::Driver>, args: Self::Args) ->
>>> Self::Initializer {
>>
>> And then here just return `Self`.
>>
>> This works, since there is a blanket impl `PinInit<T, E> for T`.
>>
>> Looking at how you use this API, I am not sure if you actually need
>> pin-init for the type that implements `DriverConnector`.
>> Do you need to store eg `Mutex<T>` or something else that needs
>> pin-init in here in a more complex driver?
> 
> Most likely yes - a lot of drivers have various private locks contained
> within their subclassed mode objects. I'm not sure we will in rvkms's
> connector since vkms doesn't really do much with connectors - but we at
> a minimum be using pinned types (spinlocks and hrtimers) in our
> DriverCrtc implementation once I've started implementing support for
> vblanks[1]
> 
> [1]
> https://www.kernel.org/doc/html/v6.9-rc5/gpu/drm-kms.html?highlight=vblank#vertical-blanking
> 
> In nova (the main reason I'm working on rvkms in the first place),
> we'll definitely have locks in our connectors and possibly other types.

I see, in that case it would be a good idea to either have an RFC of
the nova driver (or something else that needs pinned types) as
motivation for why it needs to be pin-initialized.

-- 
Cheers,
Benno


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/4] WIP: drm: Introduce rvkms
  2024-04-25 15:46       ` Benno Lossin
@ 2024-04-29 19:54         ` Lyude Paul
  0 siblings, 0 replies; 12+ messages in thread
From: Lyude Paul @ 2024-04-29 19:54 UTC (permalink / raw)
  To: Benno Lossin, dri-devel
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Daniel Vetter, Miguel Ojeda, Alex Gaynor,
	Wedson Almeida Filho, Boqun Feng, Gary Guo, Björn Roy Baron,
	Andreas Hindborg, Alice Ryhl, linux-kernel, rust-for-linux

On Thu, 2024-04-25 at 15:46 +0000, Benno Lossin wrote:
> On 22.04.24 03:54, Lyude Paul wrote:
> > On Wed, 2024-03-27 at 21:06 +0000, Benno Lossin wrote:
> > > On 22.03.24 23:03, Lyude Paul wrote:
> > > > +
> > > > +pub(crate) type Connector =
> > > > connector::Connector<DriverConnector>;
> > > > +
> > > > +impl connector::DriverConnector for DriverConnector {
> > > > +    type Initializer = impl PinInit<Self, Error>;
> > > > +
> > > > +    type State = ConnectorState;
> > > > +
> > > > +    type Driver = RvkmsDriver;
> > > > +
> > > > +    type Args = ();
> > > > +
> > > > +    fn new(dev: &Device<Self::Driver>, args: Self::Args) ->
> > > > Self::Initializer {
> > > 
> > > And then here just return `Self`.
> > > 
> > > This works, since there is a blanket impl `PinInit<T, E> for T`.
> > > 
> > > Looking at how you use this API, I am not sure if you actually
> > > need
> > > pin-init for the type that implements `DriverConnector`.
> > > Do you need to store eg `Mutex<T>` or something else that needs
> > > pin-init in here in a more complex driver?
> > 
> > Most likely yes - a lot of drivers have various private locks
> > contained
> > within their subclassed mode objects. I'm not sure we will in
> > rvkms's
> > connector since vkms doesn't really do much with connectors - but
> > we at
> > a minimum be using pinned types (spinlocks and hrtimers) in our
> > DriverCrtc implementation once I've started implementing support
> > for
> > vblanks[1]
> > 
> > [1]
> > https://www.kernel.org/doc/html/v6.9-rc5/gpu/drm-kms.html?highlight=vblank#vertical-blanking
> > 
> > In nova (the main reason I'm working on rvkms in the first place),
> > we'll definitely have locks in our connectors and possibly other
> > types.
> 
> I see, in that case it would be a good idea to either have an RFC of
> the nova driver (or something else that needs pinned types) as
> motivation for why it needs to be pin-initialized.

I mean - I'll happily include this with the RFC of nova if nova is
ready at that point, but the purpose of rvkms is to exercise enough of
this API to justify merging it :P - and I think it's a lot likely rvkm
is probably going to be ready well before nova gets to the point of
modesetting. And we will definitely have some uses of pinned types in
rvkms once the driver's ready for submission.

> 

-- 
Cheers,
 Lyude Paul (she/her)
 Software Engineer at Red Hat


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2024-04-29 19:54 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-22 22:03 [RFC WIP 0/4] Rust bindings for KMS + RVKMS Lyude Paul
2024-03-22 22:03 ` [PATCH 1/4] WIP: rust: Add basic KMS bindings Lyude Paul
2024-03-27 20:50   ` Benno Lossin
2024-04-22  1:47     ` Lyude Paul
2024-04-25 15:42       ` Benno Lossin
2024-03-22 22:03 ` [PATCH 2/4] WIP: drm: Introduce rvkms Lyude Paul
2024-03-27 21:06   ` Benno Lossin
2024-04-22  1:54     ` Lyude Paul
2024-04-25 15:46       ` Benno Lossin
2024-04-29 19:54         ` Lyude Paul
2024-03-22 22:03 ` [PATCH 3/4] rust/drm/kms: Extract PlaneState<T> into IntoPlaneState Lyude Paul
2024-03-22 22:03 ` [PATCH 4/4] WIP: rust/drm/kms: Add ShadowPlaneState<T> Lyude Paul

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).