@@ -336,6 +336,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"
@@ -176,6 +176,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/
new file mode 100644
@@ -0,0 +1,3 @@
+config DRM_RVKMS
+ tristate "Rust VKMS PoC driver (EXPERIMENTAL)"
+ depends on RUST && DRM && DRM_GEM_SHMEM_HELPER=y
new file mode 100644
@@ -0,0 +1 @@
+obj-$(CONFIG_DRM_RVKMS) += rvkms.o
new file mode 100644
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0
+//! RVKMS's DRM Connector implementation
+use super::{RvkmsDriver, DEFAULT_RES, MAX_RES};
+use core::marker::PhantomPinned;
+use kernel::{
+ drm::{
+ device::Device,
+ kms::{
+ connector::{self, ConnectorGuard, DriverConnectorOps},
+ ModeConfigGuard,
+ },
+ },
+ prelude::*,
+};
+
+#[pin_data]
+pub(crate) struct DriverConnector {
+ #[pin]
+ _p: PhantomPinned,
+}
+
+#[allow(unused)]
+pub(crate) type Connector = connector::Connector<DriverConnector>;
+pub(crate) type UnregisteredConnector = connector::UnregisteredConnector<DriverConnector>;
+
+#[vtable]
+impl connector::DriverConnector for DriverConnector {
+ #[unique]
+ const OPS: &'static DriverConnectorOps;
+
+ type State = ConnectorState;
+ type Driver = RvkmsDriver;
+ type Args = ();
+
+ fn new(_dev: &Device<Self::Driver>, _args: Self::Args) -> impl PinInit<Self, Error> {
+ 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;
+}
new file mode 100644
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0
+//! RVKMS's DRM CRTC implementation.
+use super::RvkmsDriver;
+use core::marker::*;
+use kernel::{
+ drm::{
+ device::Device,
+ kms::{
+ atomic::*,
+ crtc::{self, DriverCrtcOps, RawCrtcState},
+ vblank::*,
+ KmsRef, ModeObject,
+ },
+ },
+ impl_has_hr_timer,
+ interrupt::LocalInterruptDisabled,
+ new_spinlock_irq,
+ prelude::*,
+ sync::{Arc, SpinLockIrq},
+ time::{hrtimer::*, Ktime},
+};
+
+pub(crate) type Crtc = crtc::Crtc<RvkmsCrtc>;
+pub(crate) type UnregisteredCrtc = crtc::UnregisteredCrtc<RvkmsCrtc>;
+pub(crate) type CrtcState = crtc::CrtcState<RvkmsCrtcState>;
+
+#[derive(Default)]
+pub(crate) struct VblankState {
+ /// A reference to the current VblankTimer
+ timer: Option<Arc<VblankTimer>>,
+
+ /// A reference to a handle for the current VblankTimer
+ handle: Option<ArcHrTimerHandle<VblankTimer>>,
+
+ /// The current frame duration in ns
+ ///
+ /// Stored separately here so it can be read safely without the vblank lock
+ period_ns: i32,
+}
+
+#[pin_data]
+pub(crate) struct RvkmsCrtc {
+ /// The current vblank emulation state
+ ///
+ /// This is uninitalized when the CRTC is disabled to prevent circular references
+ #[pin]
+ vblank_state: SpinLockIrq<VblankState>,
+}
+
+#[vtable]
+impl crtc::DriverCrtc for RvkmsCrtc {
+ #[unique]
+ const OPS: &'static DriverCrtcOps;
+
+ type Args = ();
+ type State = RvkmsCrtcState;
+ type Driver = RvkmsDriver;
+ type VblankImpl = Self;
+
+ fn new(_device: &Device<Self::Driver>, _args: &Self::Args) -> impl PinInit<Self, Error> {
+ try_pin_init!(Self {
+ vblank_state <- new_spinlock_irq!(VblankState::default(), "vblank_handle_lock")
+ })
+ }
+
+ fn atomic_check(
+ crtc: &Crtc,
+ old_state: &CrtcState,
+ mut new_state: crtc::CrtcStateMutator<'_, CrtcState>,
+ state: &AtomicStateComposer<Self::Driver>,
+ ) -> Result {
+ state.add_affected_planes(crtc)?;
+
+ // Create a vblank timer when enabling a CRTC, and destroy said timer when disabling to
+ // resolve the circular reference to CRTC it creates
+ if old_state.active() != new_state.active() {
+ new_state.vblank_timer = if new_state.active() {
+ Some(VblankTimer::new(crtc)?)
+ } else {
+ None
+ };
+ }
+
+ Ok(())
+ }
+
+ fn atomic_flush(
+ crtc: &Crtc,
+ _old_state: &CrtcState,
+ mut new_state: crtc::CrtcStateMutator<'_, CrtcState>,
+ _state: &AtomicStateMutator<Self::Driver>,
+ ) {
+ if let Some(event) = new_state.get_pending_vblank_event() {
+ if let Ok(vbl_ref) = crtc.vblank_get() {
+ event.arm(vbl_ref);
+ } else {
+ event.send();
+ }
+ }
+ }
+
+ fn atomic_enable(
+ crtc: &Crtc,
+ _old_state: &CrtcState,
+ new_state: crtc::CrtcStateMutator<'_, CrtcState>,
+ _state: &AtomicStateMutator<Self::Driver>,
+ ) {
+ // Store a reference to the newly created vblank timer for this CRTC
+ crtc.vblank_state.lock().timer = new_state.vblank_timer.clone();
+
+ crtc.vblank_on();
+ }
+
+ fn atomic_disable(
+ crtc: &Crtc,
+ _old_state: &CrtcState,
+ _new_state: crtc::CrtcStateMutator<'_, CrtcState>,
+ _state: &AtomicStateMutator<Self::Driver>,
+ ) {
+ crtc.vblank_off();
+
+ let mut vbl_state = crtc.vblank_state.lock();
+ let handles = (vbl_state.timer.take(), vbl_state.handle.take());
+
+ // Since we just explicitly disabled vblanks, destroy the vblank state to resolve circular
+ // reference to this CRTC that it holds.
+ // Note that since dropping the handle will cause us to wait for the timer to finish, we
+ // must drop the lock before we do so.
+ drop(vbl_state);
+ drop(handles);
+ }
+}
+
+impl VblankSupport for RvkmsCrtc {
+ type Crtc = Self;
+
+ fn enable_vblank(
+ crtc: &Crtc,
+ vblank: &VblankGuard<'_, Self::Crtc>,
+ irq: &LocalInterruptDisabled,
+ ) -> Result {
+ let period_ns = vblank.frame_duration();
+ let mut vbl_state = crtc.vblank_state.lock_with(irq);
+
+ if let Some(timer) = vbl_state.timer.clone() {
+ vbl_state.period_ns = period_ns;
+ vbl_state.handle = Some(timer.start(Ktime::from_raw(period_ns as _)));
+ }
+
+ Ok(())
+ }
+
+ fn disable_vblank(
+ crtc: &Crtc,
+ _vbl_guard: &VblankGuard<'_, Self::Crtc>,
+ irq: &LocalInterruptDisabled,
+ ) {
+ let handle = crtc.vblank_state.lock_with(irq).handle.take();
+
+ // Now that we're outside of the vblank lock, we can safely drop the handle
+ drop(handle);
+ }
+
+ fn get_vblank_timestamp(crtc: &Crtc, _handling_vblank_irq: bool) -> Option<VblankTimestamp> {
+ let vbl_state = crtc.vblank_state.lock();
+
+ // Return the expiration of our vblank timer if we have one (if not, vblanks are disabled)
+ let time = vbl_state.timer.as_ref().map(|t| {
+ // To prevent races, we roll the hrtimer forward before we do any interrupt
+ // processing - this is how real hw works (the interrupt is only generated after all
+ // the vblank registers are updated) and what the vblank core expects. Therefore we
+ // need to always correct the timestamps by one frame.
+ t.timer.expires() - Ktime::from_nanos(vbl_state.period_ns)
+ });
+
+ Some(VblankTimestamp {
+ // …otherwise, just use the current time
+ time: time.unwrap_or_else(|| Ktime::ktime_get()),
+ max_error: 0,
+ })
+ }
+}
+
+#[derive(Clone, Default)]
+pub(crate) struct RvkmsCrtcState {
+ /// The current hrtimer used for emulating vblank events, if there is one.
+ vblank_timer: Option<Arc<VblankTimer>>,
+}
+
+impl crtc::DriverCrtcState for RvkmsCrtcState {
+ type Crtc = RvkmsCrtc;
+}
+
+/// The main hrtimer structure for emulating vblanks.
+#[pin_data]
+pub(crate) struct VblankTimer {
+ /// The actual hrtimer used for sending out vblanks
+ #[pin]
+ timer: HrTimer<Self>,
+
+ /// An owned reference to the CRTC that this [`VblankTimer`] belongs to
+ crtc: KmsRef<Crtc>,
+}
+
+impl_has_hr_timer! {
+ impl HasHrTimer<Self> for VblankTimer { self.timer }
+}
+
+impl VblankTimer {
+ pub(crate) fn new(crtc: &Crtc) -> Result<Arc<Self>> {
+ Arc::pin_init(
+ pin_init!(Self {
+ timer <- HrTimer::<Self>::new(HrTimerMode::Relative, ClockSource::Monotonic),
+ crtc: crtc.into(),
+ }),
+ GFP_KERNEL,
+ )
+ }
+}
+
+impl HrTimerCallback for VblankTimer {
+ type Pointer<'a> = Arc<Self>;
+
+ fn run<T>(
+ this: <Self::Pointer<'_> as RawHrTimerCallback>::CallbackTarget<'_>,
+ context: HrTimerCallbackContext<'_, T>,
+ ) -> HrTimerRestart
+ where
+ Self: Sized,
+ {
+ let period_ns = this.crtc.vblank_state.lock().period_ns;
+
+ let overrun = context.forward_now(Ktime::from_nanos(period_ns));
+ if overrun != 1 {
+ dev_warn!(
+ this.crtc.drm_dev().as_ref(),
+ "vblank timer overrun (expected 1, got {overrun})\n"
+ );
+ }
+
+ this.crtc.handle_vblank();
+
+ HrTimerRestart::Restart
+ }
+}
new file mode 100644
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+//! RVKMS's DRM Encoder implementation
+use crate::RvkmsDriver;
+use core::marker::PhantomPinned;
+use kernel::{
+ drm::{device::Device, kms::encoder},
+ prelude::*,
+};
+
+#[pin_data]
+pub(crate) struct DriverEncoder {
+ #[pin]
+ _p: PhantomPinned,
+}
+
+#[allow(unused)]
+pub(crate) type Encoder = encoder::Encoder<DriverEncoder>;
+pub(crate) type UnregisteredEncoder = encoder::UnregisteredEncoder<DriverEncoder>;
+
+#[vtable]
+impl encoder::DriverEncoder for DriverEncoder {
+ #[unique]
+ const OPS: &'static encoder::DriverEncoderOps;
+
+ type Driver = RvkmsDriver;
+ type Args = ();
+
+ fn new(_device: &Device<Self::Driver>, _args: Self::Args) -> impl PinInit<Self, Error> {
+ try_pin_init!(Self { _p: PhantomPinned })
+ }
+}
new file mode 100644
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
+//! RVKMS's DRM File implementation.
+use super::RvkmsDriver;
+
+use kernel::{
+ alloc::*,
+ drm::{self, device::Device as DrmDevice},
+ prelude::*,
+};
+
+pub(crate) struct File;
+
+impl drm::file::DriverFile for File {
+ type Driver = RvkmsDriver;
+
+ fn open(_device: &DrmDevice<Self::Driver>) -> Result<Pin<KBox<Self>>> {
+ Box::pin_init(init!(File {}), GFP_KERNEL)
+ }
+}
new file mode 100644
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+//! RVKMS's DRM GEM implementation
+use crate::{RvkmsDevice, RvkmsDriver};
+use core::sync::atomic::{AtomicU64, Ordering};
+use kernel::{drm::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 {
+ fn new(_dev: &RvkmsDevice, _size: usize) -> impl PinInit<Self, Error> {
+ 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;
+}
new file mode 100644
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+//! RVKMS's display outputs.
+use crate::{
+ connector::UnregisteredConnector, crtc::UnregisteredCrtc, encoder::UnregisteredEncoder,
+ plane::UnregisteredPlane, RvkmsDriver,
+};
+use kernel::{
+ drm::{
+ fourcc::*,
+ kms::{connector, encoder, plane, UnregisteredKmsDevice},
+ },
+ prelude::*,
+};
+
+pub(crate) fn create_output(dev: &UnregisteredKmsDevice<'_, RvkmsDriver>, index: u8) -> Result {
+ let possible_crtcs = 1 << index;
+
+ let primary = UnregisteredPlane::new(
+ dev,
+ possible_crtcs,
+ &[XRGB888],
+ None,
+ plane::Type::Primary,
+ None,
+ (),
+ )?;
+
+ let _crtc = UnregisteredCrtc::new(dev, primary, Option::<&UnregisteredPlane>::None, None, ())?;
+
+ let connector = UnregisteredConnector::new(dev, connector::Type::Virtual, ())?;
+
+ let encoder =
+ UnregisteredEncoder::new(dev, encoder::Type::Virtual, possible_crtcs, 0, None, ())?;
+
+ connector.attach_encoder(encoder)
+}
new file mode 100644
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0
+//! RVKMS's DRM plane implementation
+use super::RvkmsDriver;
+use core::marker::PhantomPinned;
+use kernel::{
+ drm::{
+ device::Device,
+ kms::{
+ atomic::*,
+ plane::{self, DriverPlaneOps, DriverPlaneState, PlaneStateMutator, RawPlaneState},
+ },
+ },
+ prelude::*,
+};
+
+#[pin_data]
+pub(crate) struct RvkmsPlane {
+ #[pin]
+ _p: PhantomPinned,
+}
+
+pub(crate) type Plane = plane::Plane<RvkmsPlane>;
+pub(crate) type UnregisteredPlane = plane::UnregisteredPlane<RvkmsPlane>;
+pub(crate) type PlaneState = plane::PlaneState<RvkmsPlaneState>;
+
+#[vtable]
+impl plane::DriverPlane for RvkmsPlane {
+ #[unique]
+ const OPS: &'static DriverPlaneOps;
+
+ type State = RvkmsPlaneState;
+ type Driver = RvkmsDriver;
+ type Args = ();
+
+ fn new(_device: &Device<Self::Driver>, _args: Self::Args) -> impl PinInit<Self, Error> {
+ try_pin_init!(Self { _p: PhantomPinned })
+ }
+
+ fn atomic_check(
+ _plane: &Plane,
+ mut new_state: PlaneStateMutator<'_, PlaneState>,
+ _old_state: &PlaneState,
+ state: &AtomicStateComposer<Self::Driver>,
+ ) -> Result {
+ if new_state.framebuffer().is_none() {
+ return Ok(());
+ }
+
+ if let Some(crtc) = new_state.crtc() {
+ let crtc_state = state.add_crtc_state(crtc)?;
+ new_state.atomic_helper_check(&crtc_state, true, true)
+ } else {
+ // TODO: We should be printing a warning here if we have no CRTC but do have an fb
+ return Ok(());
+ }
+ }
+
+ fn atomic_update(
+ _plane: &Plane,
+ _new_state: PlaneStateMutator<'_, PlaneState>,
+ _old_state: &PlaneState,
+ _state: &AtomicStateMutator<Self::Driver>,
+ ) {
+ // TODO, no-op for now
+ }
+}
+
+#[derive(Clone, Default)]
+pub(crate) struct RvkmsPlaneState;
+
+impl DriverPlaneState for RvkmsPlaneState {
+ type Plane = RvkmsPlane;
+}
new file mode 100644
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0
+//! A port of the VKMS driver to rust.
+pub(crate) mod connector;
+pub(crate) mod crtc;
+pub(crate) mod encoder;
+pub(crate) mod file;
+pub(crate) mod gem;
+pub(crate) mod output;
+pub(crate) mod plane;
+
+use core::marker::*;
+
+use kernel::{
+ c_str, device,
+ drm::{
+ self, drv,
+ kms::{atomic::*, KmsDriver, ModeConfigInfo, UnregisteredKmsDevice},
+ },
+ faux,
+ prelude::*,
+ str::CStr,
+};
+
+/// Convienence type alias for the DRM device type for this driver
+pub(crate) type RvkmsDevice = drm::device::Device<RvkmsDriver>;
+
+/// The name of the driver
+const NAME: &'static CStr = c_str!("rvkms");
+
+/// Driver metadata
+const INFO: drv::DriverInfo = drv::DriverInfo {
+ major: 0,
+ minor: 0,
+ patchlevel: 0,
+ name: &NAME,
+ desc: c_str!("Rust VKMS PoC"),
+ date: c_str!("20240115"),
+};
+
+/// The minimum supported resolution
+const MIN_RES: (i32, i32) = (10, 10);
+
+/// The maximum supported resolution
+const MAX_RES: (i32, i32) = (8192, 8192);
+
+/// The "preferred" resolution
+const DEFAULT_RES: (i32, i32) = (1024, 768);
+
+/// DRM Driver implementation for `RvkmsDriver`
+#[vtable]
+impl drv::Driver for RvkmsDriver {
+ type Data = ();
+ type Object = gem::Object;
+ type File = file::File;
+ type Kms = Self;
+
+ const INFO: drv::DriverInfo = INFO;
+ const FEATURES: u32 = drv::FEAT_GEM | drv::FEAT_MODESET | drv::FEAT_ATOMIC;
+
+ kernel::declare_drm_ioctls! {}
+}
+
+#[vtable]
+impl KmsDriver for RvkmsDriver {
+ fn mode_config_info(
+ _dev: &device::Device,
+ _drm_data: <Self::Data as kernel::types::ForeignOwnable>::Borrowed<'_>,
+ ) -> Result<ModeConfigInfo> {
+ Ok(MODE_CONFIG_INFO)
+ }
+
+ fn create_objects(drm: &UnregisteredKmsDevice<'_, Self>) -> Result
+ where
+ Self: Sized,
+ {
+ output::create_output(drm, 0)
+ }
+
+ fn atomic_commit_tail<'a>(
+ mut state: AtomicCommitTail<'a, Self>,
+ modeset_token: ModesetsReadyToken<'_>,
+ plane_update_token: PlaneUpdatesReadyToken<'_>,
+ ) -> CommittedAtomicState<'a, Self>
+ where
+ Self: Sized,
+ {
+ let modeset_token = state.commit_modeset_disables(modeset_token);
+
+ let plane_update_token = state.commit_planes(plane_update_token, Default::default());
+
+ let modeset_token = state.commit_modeset_enables(modeset_token);
+
+ state.fake_vblank();
+
+ let state = state.commit_hw_done(modeset_token, plane_update_token);
+
+ state.wait_for_flip_done();
+
+ state
+ }
+}
+
+pub(crate) struct RvkmsDriver;
+
+const MODE_CONFIG_INFO: ModeConfigInfo = ModeConfigInfo {
+ min_resolution: MIN_RES,
+ max_resolution: MAX_RES,
+ max_cursor: (512, 512),
+ preferred_depth: 24,
+ preferred_fourcc: None,
+};
+
+pub(crate) struct RvkmsModule {
+ _dev: faux::Registration,
+ _drm: drv::Registration<RvkmsDriver>,
+}
+
+impl kernel::Module for RvkmsModule {
+ fn init(_module: &'static ThisModule) -> Result<Self> {
+ pr_info!("RVKMS Loaded\n");
+
+ let dev = faux::Registration::new(NAME, None)?;
+ dev_info!(dev.as_ref(), "Setting up DRM\n");
+
+ let drm = drv::Registration::<RvkmsDriver>::new(dev.as_ref(), (), 0)?;
+
+ Ok(Self {
+ _dev: dev,
+ _drm: drm,
+ })
+ }
+}
+
+module! {
+ type: RvkmsModule,
+ name: "rvkms",
+ author: "Lyude Paul",
+ description: "Rust VKMS Proof of Concept driver",
+ license: "GPL v2",
+}