[WIP RFC v2 35/35] WIP: drm: Introduce RVKMS!

Daniel Almeida daniel.almeida at collabora.com
Thu Dec 5 16:36:40 UTC 2024


Hi Lyude,

> On 30 Sep 2024, at 20:10, Lyude Paul <lyude at redhat.com> wrote:
> 
> Now that we've added all of the bits that we need for the KMS API, it's
> time to introduce rvkms! This is a port of the VKMS driver to rust, with
> the intent of acting as an example usecase of the KMS bindings that we've
> come up with so far in preparation for writing a display driver for nova.
> 
> Currently RVKMS is an extremely bear bones driver - it only registers a

nit: typo in `bare-bones`
 
> device and emulates vblanking, but it exercises a good portion of the API
> that we've introduced so far! Eventually I hope to introduce CRC generation
> and maybe writeback connectors like.
> 
> Signed-off-by: Lyude Paul <lyude at 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 |  53 ++++++
> drivers/gpu/drm/rvkms/crtc.rs      | 253 +++++++++++++++++++++++++++++
> drivers/gpu/drm/rvkms/encoder.rs   |  33 ++++
> drivers/gpu/drm/rvkms/file.rs      |  22 +++
> drivers/gpu/drm/rvkms/gem.rs       |  30 ++++
> drivers/gpu/drm/rvkms/output.rs    |  55 +++++++
> drivers/gpu/drm/rvkms/plane.rs     |  81 +++++++++
> drivers/gpu/drm/rvkms/rvkms.rs     | 168 +++++++++++++++++++
> 12 files changed, 702 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 6b2c6b91f9625..525e2e1615ca2 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -323,6 +323,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 68cc9258ffc4b..ebedcab4dece0 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -172,6 +172,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..97b94054fbe1f
> --- /dev/null
> +++ b/drivers/gpu/drm/rvkms/connector.rs
> @@ -0,0 +1,53 @@
> +// SPDX-License-Identifier: GPL-2.0
> +use super::{Rvkms, RvkmsDevice, MAX_RES, DEFAULT_RES};
> +use kernel::{
> +    prelude::*,
> +    drm::{
> +        device::Device,
> +        kms::{
> +            connector::{self, ConnectorGuard, DriverConnectorOps},
> +            ModeConfigGuard
> +        }
> +    },
> +    prelude::*
> +};
> +use core::marker::PhantomPinned;
> +
> +#[pin_data]
> +pub(crate) struct DriverConnector {

I assume a real driver would have some data in here, right?

If so, you should add a comment highlighting that.

> +    #[pin]
> +    _p: PhantomPinned
> +}
> +
> +pub(crate) type Connector = connector::Connector<DriverConnector>;

> +
> +#[vtable]
> +impl connector::DriverConnector for DriverConnector {
> +    #[unique]
> +    const OPS: &'static DriverConnectorOps;
> +
> +    type State = ConnectorState;
> +    type Driver = Rvkms;
> +    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;
> +

Same here. What would a real driver have in here?


> +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..c3a90c76e4a5e
> --- /dev/null
> +++ b/drivers/gpu/drm/rvkms/crtc.rs
> @@ -0,0 +1,253 @@
> +// TODO: License and stuff
> +// Contain's rvkms's drm_crtc implementation
> +use core::marker::*;
> +use super::{Rvkms, plane::*};
> +use kernel::{
> +    prelude::*,
> +    drm::{
> +        device::Device,
> +        kms::{
> +            atomic::*,
> +            crtc::{self, RawCrtcState, DriverCrtcOps},
> +            ModeObject,
> +            KmsRef,
> +            vblank::*,
> +        }
> +    },
> +    sync::{
> +        lock::Guard,
> +        SpinLockIrq,
> +        LockedBy,
> +    },
> +    hrtimer::*,
> +    time::*,
> +    irq::*,
> +    sync::Arc,
> +    new_spinlock_irq,
> +    impl_has_timer
> +};
> +
> +pub(crate) type Crtc = crtc::Crtc<RvkmsCrtc>;
> +pub(crate) type CrtcState = crtc::CrtcState<RvkmsCrtcState>;

I think you should expand a bit on the differences between Crtc and CrtcState now that you
have a user.

Same for Plane and PlaneState, and for Connector and ConnectorState.


> +
> +#[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<ArcTimerHandle<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 = Rvkms;
> +    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::BorrowedCrtcState<'_, 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::BorrowedCrtcState<'_, 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::BorrowedCrtcState<'_, CrtcState>,
> +        _state: &AtomicStateMutator<Self::Driver>
> +    ) {
> +        with_irqs_disabled(|irq| {
> +            // Store a reference to the newly created vblank timer for this CRTC
> +            crtc.vblank_state.lock_with(irq).timer = new_state.vblank_timer.clone()
> +        });
> +
> +        crtc.vblank_on();
> +    }
> +
> +    fn atomic_disable(
> +        crtc: &Crtc,
> +        _old_state: &CrtcState,
> +        _new_state: crtc::BorrowedCrtcState<'_, CrtcState>,
> +        _state: &AtomicStateMutator<Self::Driver>
> +    ) {
> +        crtc.vblank_off();
> +
> +        // Since we just explicitly disabled vblanks, destroy the vblank state to resolve circular
> +        // reference to this CRTC that it holds. Note that dropping the handle will cause us to wait
> +        // for the timer to finish, so we return it from with_irqs_disabled so that it is only
> +        // dropped once the vblank_state lock has been released
> +        drop(with_irqs_disabled(|irq| {
> +            let mut state = crtc.vblank_state.lock_with(irq);
> +
> +            (state.timer.take(), state.handle.take())
> +        }));
> +    }
> +}
> +
> +impl VblankSupport for RvkmsCrtc {
> +    type Crtc = Self;
> +
> +    fn enable_vblank(
> +        crtc: &Crtc,
> +        vblank: &VblankGuard<'_, Self::Crtc>,
> +        irq: IrqDisabled<'_>,
> +    ) -> 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.schedule(period_ns as _));
> +        }
> +
> +        Ok(())
> +    }
> +
> +    fn disable_vblank(crtc: &Crtc, _vbl_guard: &VblankGuard<'_, Self::Crtc>, irq: IrqDisabled<'_>) {
> +        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);

A variable that gets immediately dropped is a bit suspect.

> +    }
> +
> +    fn get_vblank_timestamp(crtc: &Crtc, _handling_vblank_irq: bool) -> Option<VblankTimestamp> {
> +        let time = with_irqs_disabled(|irq| {
> +            let vbl_state = crtc.vblank_state.lock_with(irq);
> +
> +            // Return the expiration of our vblank timer if we have one (if not, vblanks are
> +            // disabled)
> +            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_ns(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 {
> +    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: Timer<Self>,
> +
> +    /// An owned reference to the CRTC that this [`VblankTimer`] belongs to
> +    crtc: KmsRef<Crtc>,
> +}
> +
> +impl_has_timer! {
> +    impl HasTimer<Self> for VblankTimer { self.timer }
> +}
> +
> +impl VblankTimer {
> +    pub(crate) fn new(crtc: &Crtc) -> Result<Arc<Self>> {
> +        Arc::pin_init(
> +            pin_init!(Self {
> +                timer <- Timer::<Self>::new::<Arc<Self>>(),
> +                crtc: crtc.into(),
> +            }),
> +            GFP_KERNEL
> +        )
> +    }
> +}
> +
> +impl TimerCallback for VblankTimer {
> +    type CallbackTarget<'a> = Arc<Self>;
> +
> +    fn run(
> +        this: Self::CallbackTarget<'_>,
> +        context: TimerCallbackContext<'_, Self>
> +    ) -> TimerRestart
> +    where
> +        Self: Sized
> +    {
> +        with_irqs_disabled(|irq| {
> +            let period_ns = this.crtc.vblank_state.lock_with(irq).period_ns;
> +
> +            let overrun = context.forward_now(Ktime::from_ns(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();
> +        });
> +
> +        TimerRestart::Restart
> +    }
> +}
> diff --git a/drivers/gpu/drm/rvkms/encoder.rs b/drivers/gpu/drm/rvkms/encoder.rs
> new file mode 100644
> index 0000000000000..f426ef10bcd7e
> --- /dev/null
> +++ b/drivers/gpu/drm/rvkms/encoder.rs
> @@ -0,0 +1,33 @@
> +// SPDX-License-Identifier: GPL-2.0
> +use core::marker::PhantomPinned;
> +use kernel::{
> +    drm::{device::Device, kms::encoder},
> +    prelude::*,
> +    types::ARef,
> +};
> +use crate::{Rvkms, connector::Connector};
> +
> +#[pin_data]
> +pub(crate) struct DriverEncoder {
> +    connector: ARef<Connector>,
> +    #[pin]
> +    _p: PhantomPinned,
> +}
> +
> +pub(crate) type Encoder = encoder::Encoder<DriverEncoder>;
> +
> +#[vtable]
> +impl encoder::DriverEncoder for DriverEncoder {
> +    #[unique]
> +    const OPS: &'static encoder::DriverEncoderOps;
> +
> +    type Driver = Rvkms;
> +    type Args = ARef<Connector>;
> +
> +    fn new(device: &Device<Self::Driver>, args: Self::Args) -> impl PinInit<Self, Error> {
> +        try_pin_init!(Self {
> +            connector: args,
> +            _p: PhantomPinned
> +        })
> +    }
> +}
> diff --git a/drivers/gpu/drm/rvkms/file.rs b/drivers/gpu/drm/rvkms/file.rs
> new file mode 100644
> index 0000000000000..baa9297673ecc
> --- /dev/null
> +++ b/drivers/gpu/drm/rvkms/file.rs
> @@ -0,0 +1,22 @@
> +use super::Rvkms;
> +
> +use kernel::{
> +    drm::{
> +        self,
> +        device::Device as DrmDevice
> +    },
> +    prelude::*,
> +};
> +use core::option::*;
> +
> +pub(crate) struct File;
> +
> +impl drm::file::DriverFile for File {
> +    type Driver = Rvkms;
> +
> +    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");
> +
> +        Box::pin_init(init!(File { }), GFP_KERNEL)
> +    }
> +}
> diff --git a/drivers/gpu/drm/rvkms/gem.rs b/drivers/gpu/drm/rvkms/gem.rs
> new file mode 100644
> index 0000000000000..950ef33758657
> --- /dev/null
> +++ b/drivers/gpu/drm/rvkms/gem.rs
> @@ -0,0 +1,30 @@
> +use crate::{Rvkms, 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 {
> +    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 = Rvkms;
> +}
> diff --git a/drivers/gpu/drm/rvkms/output.rs b/drivers/gpu/drm/rvkms/output.rs
> new file mode 100644
> index 0000000000000..b110e2d5d8a8b
> --- /dev/null
> +++ b/drivers/gpu/drm/rvkms/output.rs
> @@ -0,0 +1,55 @@
> +// SPDX-License-Identifier: GPL-2.0
> +use crate::{
> +    crtc::Crtc,
> +    plane::Plane,
> +    connector::Connector,
> +    encoder::Encoder,
> +    RvkmsDevice,
> +    Rvkms
> +};
> +use kernel::{
> +    drm::{
> +        fourcc::*,
> +        kms::{
> +            connector::DRM_MODE_CONNECTOR_VIRTUAL,
> +            encoder::DRM_MODE_ENCODER_VIRTUAL,
> +            plane::{self, PlaneType},
> +            framebuffer::*,
> +            UnregisteredKmsDevice,
> +        },
> +    },
> +    sync::Arc,
> +    prelude::*,
> +    types::ARef,
> +};
> +
> +const FORMATS: FormatList<1> = FormatList::new([XRGB888]);
> +
> +pub(crate) fn create_output(dev: &UnregisteredKmsDevice<'_, Rvkms>, index: u8) -> Result {
> +    let possible_crtcs = 1 << index;
> +
> +    let primary = Plane::new(
> +        dev,
> +        possible_crtcs,
> +        &FORMATS,
> +        Option::<&ModifierList<0>>::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,
> +        possible_crtcs,
> +        0,
> +        None,
> +        connector.clone()
> +    )?;
> +
> +    connector.attach_encoder(encoder)
> +}
> diff --git a/drivers/gpu/drm/rvkms/plane.rs b/drivers/gpu/drm/rvkms/plane.rs
> new file mode 100644
> index 0000000000000..2722845a32e9a
> --- /dev/null
> +++ b/drivers/gpu/drm/rvkms/plane.rs
> @@ -0,0 +1,81 @@
> +// SPDX-License-Identifier: GPL-2.0
> +use core::marker::PhantomPinned;
> +use super::{Rvkms, crtc::{RvkmsCrtc, Crtc}};
> +use kernel::{
> +    prelude::*,
> +    drm::{
> +        device::Device,
> +        kms::{
> +            atomic::*,
> +            plane::{
> +                self,
> +                AsRawPlaneState,
> +                FromRawPlaneState,
> +                DriverPlaneState,
> +                RawPlane,
> +                RawPlaneState,
> +                BorrowedPlaneState,
> +                DriverPlaneOps,
> +            },
> +            ModeObject
> +        }
> +    },
> +};
> +
> +#[pin_data]
> +pub(crate) struct RvkmsPlane {
> +    #[pin]
> +    _p: PhantomPinned,
> +}
> +
> +pub(crate) type Plane = plane::Plane<RvkmsPlane>;
> +pub(crate) type PlaneState = plane::PlaneState<RvkmsPlaneState>;
> +
> +#[vtable]
> +impl plane::DriverPlane for RvkmsPlane {
> +    #[unique]
> +    const OPS: &'static DriverPlaneOps;
> +
> +    type State = RvkmsPlaneState;
> +    type Driver = Rvkms;
> +    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: BorrowedPlaneState<'_, 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: BorrowedPlaneState<'_, 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;
> +}
> diff --git a/drivers/gpu/drm/rvkms/rvkms.rs b/drivers/gpu/drm/rvkms/rvkms.rs
> new file mode 100644
> index 0000000000000..2c72c0ec6989d
> --- /dev/null
> +++ b/drivers/gpu/drm/rvkms/rvkms.rs
> @@ -0,0 +1,168 @@
> +// SPDX-License-Identifier: GPL-2.0
> +mod connector;
> +mod crtc;
> +mod file;
> +mod gem;
> +mod plane;
> +mod output;
> +mod encoder;
> +
> +use alloc::boxed::Box;
> +
> +use core::{option::*, marker::*};
> +
> +use kernel::{
> +    c_str,
> +    str::CStr,
> +    device,
> +    driver,
> +    drm::{
> +        self,
> +        drv,
> +        kms::{
> +            Kms,
> +            ModeConfigInfo,
> +            UnregisteredKmsDevice,
> +            atomic::*,
> +            fbdev::*,
> +        },
> +    },
> +    platform,
> +    prelude::*,
> +    sync::Arc,
> +};
> +
> +/// Convienence type alias for the DRM device type for this driver
> +pub(crate) type RvkmsDevice = drm::device::Device<Rvkms>;
> +
> +/// 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);
> +
> +pub(crate) struct Data {
> +}
> +
> +/// DRM Driver implementation for `RvkmsDriver`
> +#[vtable]
> +impl drv::Driver for Rvkms {
> +    type Data = Arc<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 Kms for Rvkms {
> +    type Driver = Self;
> +    type Fbdev = FbdevShmem<Self>;
> +
> +    fn mode_config_info(
> +        _dev: &device::Device,
> +        _drm_data: <<Self::Driver as drv::Driver>::Data as kernel::types::ForeignOwnable>::Borrowed<'_>,
> +    ) -> Result<ModeConfigInfo> {
> +        Ok(MODE_CONFIG_INFO)
> +    }
> +
> +    fn create_objects(drm: &UnregisteredKmsDevice<'_, Self::Driver>) -> Result {
> +        output::create_output(drm, 0)
> +    }
> +
> +    fn atomic_commit_tail<'a>(
> +        mut state: AtomicCommitTail<'a, Self::Driver>,
> +        modeset_token: ModesetsReadyToken<'_>,
> +        plane_update_token: PlaneUpdatesReadyToken<'_>,
> +    ) -> CommittedAtomicState<'a, Self::Driver> {
> +        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
> +    }
> +}
> +
> +impl platform::Driver for Rvkms {
> +    type Data = Arc<Data>;
> +    type IdInfo = ();
> +
> +    fn probe(pdev: &mut platform::Device, id_info: Option<&Self::IdInfo>) -> Result<Self::Data> {
> +        // XXX: do not fret, the mutable reference here is temporary (poke dakr if it isn't)
> +        let dev: &device::Device = pdev.as_ref();

FYI: IIRC this now changed to `as_dev()`.

> +        dev.pr_info(format_args!("RVKMS probing\n"));
> +
> +        let data = Arc::new(Data { }, GFP_KERNEL)?;
> +        let drm = drv::Registration::<Rvkms>::new_foreign_owned(dev, data.clone(), 0)?;
> +
> +        Ok(data)
> +    }
> +}
> +
> +pub(crate) struct Rvkms {
> +    drv_reg: Pin<Box<platform::Registration<Self>>>,
> +    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) -> kernel::error::Result<Self> {
> +        pr_info!("RVKMS module loaded\n");
> +
> +        // Register the driver (FIXME: this should be static
> +        let drv_reg = Box::try_pin_init(
> +            platform::Registration::<Self>::new(name, module),
> +            GFP_KERNEL
> +        )?;
> +
> +        let pdev = platform::Device::create_simple(&NAME, 0)?;
> +        let dev: &device::Device = pdev.as_ref();
> +
> +        Ok(Self {
> +            drv_reg,
> +            pdev,
> +        })
> +    }
> +}
> +
> +module! {
> +    type: Rvkms,
> +    name: "rvkms",
> +    author: "Lyude Paul",
> +    description: "Rust VKMS Proof of Concept driver",
> +    license: "GPL v2",
> +}
> -- 
> 2.46.1
> 
> 

Lyude, you and your colleagues managed to advance quite a lot here. That’s a staggering amount of
work, and I liked the way you designed the abstractions in general.

Overall, this series is in good shape given its RFC nature.

If you give me some instructions I will test it for you.

— Daniel.




More information about the dri-devel mailing list