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