From ae47cbcc0b25f26343fc47744ff71fdfd12bc7cc Mon Sep 17 00:00:00 2001
From: Lyude Paul <lyude@redhat.com>
Date: Thu, 23 May 2024 17:26:06 -0400
Subject: [PATCH] drm: Introduce RVKMS!

Now that we've added all of the bits that we need for the KMS API, it's
time to Bring The Beef and introduce RVKMS!

TODO: write something more here I guess?

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      |  84 ++++++++++++++++
 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    |  90 ++++++++++++++++++
 drivers/gpu/drm/rvkms/plane.rs     | 124 ++++++++++++++++++++++++
 drivers/gpu/drm/rvkms/rvkms.rs     | 148 +++++++++++++++++++++++++++++
 12 files changed, 588 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..551422803b9a6
--- /dev/null
+++ b/drivers/gpu/drm/rvkms/Kconfig
@@ -0,0 +1,3 @@
+config DRM_RVKMS
+        tristate "Rust VKMS PoC driver (EXPERIMENTAL)"
+        depends on RUST && DRM && DRM_GEM_SHMEM_HELPER=y
diff --git a/drivers/gpu/drm/rvkms/Makefile b/drivers/gpu/drm/rvkms/Makefile
new file mode 100644
index 0000000000000..18e06fc3343c6
--- /dev/null
+++ b/drivers/gpu/drm/rvkms/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DRM_RVKMS) += rvkms.o
diff --git a/drivers/gpu/drm/rvkms/connector.rs b/drivers/gpu/drm/rvkms/connector.rs
new file mode 100644
index 0000000000000..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..dd55ef2ebc72d
--- /dev/null
+++ b/drivers/gpu/drm/rvkms/crtc.rs
@@ -0,0 +1,84 @@
+// TODO: License and stuff
+// Contain's rvkms's drm_crtc implementation
+use core::marker::PhantomPinned;
+use super::{RvkmsDriver, plane::*};
+use kernel::{
+    prelude::*,
+    drm::{
+        device::Device,
+        kms::{
+            atomic::*,
+            crtc,
+            ModeObject,
+        }
+    },
+    device::RawDevice
+};
+
+pub(crate) type Crtc = crtc::Crtc<DriverCrtc>;
+
+#[pin_data]
+pub(crate) struct DriverCrtc {
+    #[pin]
+    _p: PhantomPinned
+}
+
+#[vtable]
+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 })
+    }
+
+    fn atomic_check(crtc: &crtc::Crtc<Self>, state: &AtomicStateComposer<Self::Driver>) -> Result {
+        let mut crtc_state = state.get_new_crtc_state(crtc).ok_or(EINVAL)?;
+
+        //if !crtc_state.active_planes.is_empty() {
+            //return Ok(());
+        //}
+
+        state.add_affected_planes(crtc)
+    }
+
+        //crtc_state.active_planes.try_reserve_exact(crtc.drm_dev().num_plane() as _)?;
+
+        //for plane in state.iter_planes().filter_map(|p| Plane::from_opaque(p)) {
+            //let old_plane_state = state.get_old_plane_state(plane).ok_or(EINVAL)?;
+
+            //if old_plane_state.state.base.visible {
+                //crtc.drm_dev().pr_info(format_args!("loop\n"));
+
+                //crtc_state.active_planes.try_push(
+                    //state.save_old_plane_state(plane).ok_or(EINVAL)?
+                //)?;
+            //}
+        //}
+
+        //Ok(())
+    //}
+}
+
+#[derive(Default)]
+pub(crate) struct CrtcState {
+    //active_planes: Vec<PrevAtomicStateRef<PlaneState>>
+}
+
+impl Clone for CrtcState {
+    fn clone(&self) -> Self {
+        Self {
+            // We don't want to bring the prior value of `active_planes` into new states
+            //active_planes: Vec::new(),
+        }
+    }
+}
+
+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..30005392cfed8
--- /dev/null
+++ b/drivers/gpu/drm/rvkms/output.rs
@@ -0,0 +1,90 @@
+use crate::{
+    crtc::Crtc,
+    plane::Plane,
+    connector::Connector,
+    encoder::Encoder,
+    RvkmsDevice,
+    RvkmsDriver
+};
+use kernel::{
+    drm::{
+        fourcc::*,
+        rect::Rect,
+        kms::{
+            connector::DRM_MODE_CONNECTOR_VIRTUAL,
+            encoder::DRM_MODE_ENCODER_VIRTUAL,
+            plane::PlaneType,
+            framebuffer::*,
+        },
+    },
+    sync::Arc,
+    prelude::*,
+    types::ARef,
+    drm_format_list,
+};
+
+pub(crate) struct Output<'a> {
+    crtc: &'a Crtc,
+    primary: &'a Plane,
+    // TODO: overlay, cursor
+    connector: ARef<Connector>,
+    encoder: &'a Encoder,
+}
+
+impl<'a, 'b: 'a> Output<'b> {
+    pub(crate) fn new(dev: &'a 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,
+            primary,
+            connector,
+            encoder
+        })
+    }
+}
+
+// TODO: Finish this, we still need something to represent the mapping of the Framebuffer - and as
+// well we need to add any information from the atomic state that we need for the composer
+
+#[derive(Clone)]
+pub(crate) struct FrameInfo {
+    pub(crate) fb: ARef<Framebuffer<RvkmsDriver>>,
+    pub(crate) src: Rect,
+    pub(crate) dst: Rect,
+    pub(crate) rotated: Rect,
+    pub(crate) rotation: u32,
+    pub(crate) cpp: u8, // We only support linear formats at the moment so it ok
+    pub(crate) offset: u32,
+    pub(crate) pitch: u32,
+    // TODO: mapping
+}
diff --git a/drivers/gpu/drm/rvkms/plane.rs b/drivers/gpu/drm/rvkms/plane.rs
new file mode 100644
index 0000000000000..821b91eeb6b03
--- /dev/null
+++ b/drivers/gpu/drm/rvkms/plane.rs
@@ -0,0 +1,124 @@
+use core::marker::PhantomPinned;
+use super::{
+    RvkmsDriver,
+    crtc::DriverCrtc,
+    output::FrameInfo,
+};
+use kernel::{
+    prelude::*,
+    device::RawDevice,
+    drm::{
+        device::Device,
+        kms::{
+            atomic::*,
+            blend::rotation_simplify,
+            mode::*,
+            plane::{self, AsRawPlaneState, FromRawPlaneState, DriverPlaneState, RawPlane, RawPlaneState, BorrowedPlaneState},
+            gem_atomic_helper::*,
+            ModeObject
+        }
+    },
+};
+
+#[pin_data]
+pub(crate) struct DriverPlane {
+    #[pin]
+    _p: PhantomPinned,
+}
+
+pub(crate) type Plane = plane::Plane<DriverPlane>;
+pub(crate) type PlaneState = ShadowPlaneState<RvkmsPlaneState>;
+
+#[vtable]
+impl plane::DriverPlane for DriverPlane {
+    type Initializer = impl PinInit<Self, Error>;
+
+    type State = PlaneState;
+
+    type Driver = RvkmsDriver;
+
+    type Crtc = DriverCrtc;
+
+    type Args = ();
+
+    fn new(device: &Device<Self::Driver>, args: Self::Args) -> Self::Initializer {
+        try_pin_init!(Self { _p: PhantomPinned })
+    }
+
+    fn atomic_check(
+        plane: &plane::Plane<Self>,
+        state: &AtomicStateComposer<Self::Driver>
+    ) -> Result {
+        let mut new_plane_state = state.get_new_plane_state(plane).ok_or(EINVAL)?;
+        if new_plane_state.framebuffer().is_none() {
+            return Ok(());
+        }
+
+        if let Some(crtc) = new_plane_state.crtc() {
+            let crtc_state = state.add_crtc_state(crtc)?;
+            new_plane_state.atomic_helper_check(&crtc_state, true, true)
+        } else {
+            // TODO: We should be printing a warning here if we have no CRTC but do have an fb
+            Ok(())
+        }
+    }
+
+    fn atomic_update(plane: &plane::Plane<Self>, state: &AtomicStateMutator<Self::Driver>) {
+        let mut plane_state = state.get_new_plane_state(plane).unwrap();
+
+        if let Some(fb) = plane_state.framebuffer() {
+            let dst = plane_state.dst();
+            let rotation = rotation_simplify(
+                plane_state.rotation(),
+                ROTATE_0 | ROTATE_90 | ROTATE_270 | REFLECT_X | REFLECT_Y
+            );
+
+            plane_state.frame_info = Some(FrameInfo {
+                fb: fb.into(),
+                src: *plane_state.src(),
+                dst: *dst,
+                rotated: dst.rotate(dst.width(), dst.height(), rotation),
+                rotation,
+                cpp: fb.format().char_per_block()[0],
+                pitch: fb.pitches()[0],
+                offset: fb.offsets()[0],
+            });
+        }
+    }
+
+    fn prepare_fb(_plane: &plane::Plane<Self>, state: &mut Self::State) -> Result {
+        if state.framebuffer().is_none() {
+            return Ok(());
+        }
+
+        state.gem_plane_helper_prepare_fb()?;
+
+        // We want our FB access to last beyond just this commit
+        state.gem_plane_helper_begin_fb_access()
+    }
+
+    fn cleanup_fb(_plane: &plane::Plane<Self>, state: &mut Self::State) {
+        if state.framebuffer().is_some() {
+            // Our FB access lasts beyond just a single commit
+            state.gem_plane_helper_end_fb_access()
+        }
+    }
+}
+
+#[derive(Default)]
+pub(crate) struct RvkmsPlaneState {
+    frame_info: Option<FrameInfo>,
+}
+
+impl Clone for RvkmsPlaneState {
+    fn clone(&self) -> Self {
+        Self {
+            // We don't want to carry the frame info over
+            frame_info: None,
+        }
+    }
+}
+
+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..a575bae4de698
--- /dev/null
+++ b/drivers/gpu/drm/rvkms/rvkms.rs
@@ -0,0 +1,148 @@
+// 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<'a> {
+    output: Output<'a>,
+    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<'a> kernel::Module for Rvkms<'a> {
+    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)?;
+
+        drm_dev.fbdev_generic_setup();
+
+        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",
+}
-- 
GitLab