[PATCH 2/4] WIP: drm: Introduce rvkms
From: Lyude Paul
Date: Fri Mar 22 2024 - 18:15:45 EST
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@xxxxxxxxxx>
---
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