Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm: nova-drm: add initial driver skeleton

Add the initial nova-drm driver skeleton.

nova-drm is connected to nova-core through the auxiliary bus and
implements the DRM parts of the nova driver stack.

For now, it implements the fundamental DRM abstractions, i.e. creates a
DRM device and registers it, exposing a three sample IOCTLs.

DRM_IOCTL_NOVA_GETPARAM
- provides the PCI bar size from the bar that maps the GPUs VRAM
from nova-core

DRM_IOCTL_NOVA_GEM_CREATE
- creates a new dummy DRM GEM object and returns a handle

DRM_IOCTL_NOVA_GEM_INFO
- provides metadata for the DRM GEM object behind a given handle

I implemented a small userspace test suite [1] that utilizes this
interface.

Link: https://gitlab.freedesktop.org/dakr/drm-test [1]
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Acked-by: Dave Airlie <airlied@redhat.com>
Link: https://lore.kernel.org/r/20250424160452.8070-3-dakr@kernel.org
[ Kconfig: depend on DRM=y rather than just DRM. - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>

+405
+12
MAINTAINERS
··· 7594 7594 F: Documentation/gpu/nova/ 7595 7595 F: drivers/gpu/nova-core/ 7596 7596 7597 + DRM DRIVER FOR NVIDIA GPUS [RUST] 7598 + M: Danilo Krummrich <dakr@kernel.org> 7599 + L: nouveau@lists.freedesktop.org 7600 + S: Supported 7601 + Q: https://patchwork.freedesktop.org/project/nouveau/ 7602 + B: https://gitlab.freedesktop.org/drm/nova/-/issues 7603 + C: irc://irc.oftc.net/nouveau 7604 + T: git https://gitlab.freedesktop.org/drm/nova.git nova-next 7605 + F: Documentation/gpu/nova/ 7606 + F: drivers/gpu/drm/nova/ 7607 + F: include/uapi/drm/nova_drm.h 7608 + 7597 7609 DRM DRIVER FOR OLIMEX LCD-OLINUXINO PANELS 7598 7610 M: Stefan Mavrodiev <stefan@olimex.com> 7599 7611 S: Maintained
+2
drivers/gpu/drm/Kconfig
··· 343 343 344 344 source "drivers/gpu/drm/nouveau/Kconfig" 345 345 346 + source "drivers/gpu/drm/nova/Kconfig" 347 + 346 348 source "drivers/gpu/drm/i915/Kconfig" 347 349 348 350 source "drivers/gpu/drm/xe/Kconfig"
+1
drivers/gpu/drm/Makefile
··· 176 176 obj-$(CONFIG_DRM_VGEM) += vgem/ 177 177 obj-$(CONFIG_DRM_VKMS) += vkms/ 178 178 obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/ 179 + obj-$(CONFIG_DRM_NOVA) += nova/ 179 180 obj-$(CONFIG_DRM_EXYNOS) +=exynos/ 180 181 obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/ 181 182 obj-$(CONFIG_DRM_GMA500) += gma500/
+14
drivers/gpu/drm/nova/Kconfig
··· 1 + config DRM_NOVA 2 + tristate "Nova DRM driver" 3 + depends on AUXILIARY_BUS 4 + depends on DRM=y 5 + depends on PCI 6 + depends on RUST 7 + default n 8 + help 9 + Choose this if you want to build the Nova DRM driver for Nvidia 10 + GSP-based GPUs. 11 + 12 + This driver is work in progress and may not be functional. 13 + 14 + If M is selected, the module will be called nova.
+3
drivers/gpu/drm/nova/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + obj-$(CONFIG_DRM_NOVA) += nova.o
+69
drivers/gpu/drm/nova/driver.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + use kernel::{auxiliary, c_str, device::Core, drm, drm::gem, drm::ioctl, prelude::*, types::ARef}; 4 + 5 + use crate::file::File; 6 + use crate::gem::NovaObject; 7 + 8 + pub(crate) struct NovaDriver { 9 + #[expect(unused)] 10 + drm: ARef<drm::Device<Self>>, 11 + } 12 + 13 + /// Convienence type alias for the DRM device type for this driver 14 + pub(crate) type NovaDevice = drm::Device<NovaDriver>; 15 + 16 + #[pin_data] 17 + pub(crate) struct NovaData { 18 + pub(crate) adev: ARef<auxiliary::Device>, 19 + } 20 + 21 + const INFO: drm::DriverInfo = drm::DriverInfo { 22 + major: 0, 23 + minor: 0, 24 + patchlevel: 0, 25 + name: c_str!("nova"), 26 + desc: c_str!("Nvidia Graphics"), 27 + }; 28 + 29 + const NOVA_CORE_MODULE_NAME: &CStr = c_str!("NovaCore"); 30 + const AUXILIARY_NAME: &CStr = c_str!("nova-drm"); 31 + 32 + kernel::auxiliary_device_table!( 33 + AUX_TABLE, 34 + MODULE_AUX_TABLE, 35 + <NovaDriver as auxiliary::Driver>::IdInfo, 36 + [( 37 + auxiliary::DeviceId::new(NOVA_CORE_MODULE_NAME, AUXILIARY_NAME), 38 + () 39 + )] 40 + ); 41 + 42 + impl auxiliary::Driver for NovaDriver { 43 + type IdInfo = (); 44 + const ID_TABLE: auxiliary::IdTable<Self::IdInfo> = &AUX_TABLE; 45 + 46 + fn probe(adev: &auxiliary::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> { 47 + let data = try_pin_init!(NovaData { adev: adev.into() }); 48 + 49 + let drm = drm::Device::<Self>::new(adev.as_ref(), data)?; 50 + drm::Registration::new_foreign_owned(&drm, adev.as_ref(), 0)?; 51 + 52 + Ok(KBox::new(Self { drm }, GFP_KERNEL)?.into()) 53 + } 54 + } 55 + 56 + #[vtable] 57 + impl drm::Driver for NovaDriver { 58 + type Data = NovaData; 59 + type File = File; 60 + type Object = gem::Object<NovaObject>; 61 + 62 + const INFO: drm::DriverInfo = INFO; 63 + 64 + kernel::declare_drm_ioctls! { 65 + (NOVA_GETPARAM, drm_nova_getparam, ioctl::RENDER_ALLOW, File::get_param), 66 + (NOVA_GEM_CREATE, drm_nova_gem_create, ioctl::AUTH | ioctl::RENDER_ALLOW, File::gem_create), 67 + (NOVA_GEM_INFO, drm_nova_gem_info, ioctl::AUTH | ioctl::RENDER_ALLOW, File::gem_info), 68 + } 69 + }
+74
drivers/gpu/drm/nova/file.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + use crate::driver::{NovaDevice, NovaDriver}; 4 + use crate::gem::NovaObject; 5 + use crate::uapi::{GemCreate, GemInfo, Getparam}; 6 + use kernel::{ 7 + alloc::flags::*, 8 + drm::{self, gem::BaseObject}, 9 + pci, 10 + prelude::*, 11 + types::Opaque, 12 + uapi, 13 + }; 14 + 15 + pub(crate) struct File; 16 + 17 + impl drm::file::DriverFile for File { 18 + type Driver = NovaDriver; 19 + 20 + fn open(_dev: &NovaDevice) -> Result<Pin<KBox<Self>>> { 21 + Ok(KBox::new(Self, GFP_KERNEL)?.into()) 22 + } 23 + } 24 + 25 + impl File { 26 + /// IOCTL: get_param: Query GPU / driver metadata. 27 + pub(crate) fn get_param( 28 + dev: &NovaDevice, 29 + getparam: &Opaque<uapi::drm_nova_getparam>, 30 + _file: &drm::File<File>, 31 + ) -> Result<u32> { 32 + let adev = &dev.adev; 33 + let parent = adev.parent().ok_or(ENOENT)?; 34 + let pdev: &pci::Device = parent.try_into()?; 35 + let getparam: &Getparam = getparam.into(); 36 + 37 + let value = match getparam.param() as u32 { 38 + uapi::NOVA_GETPARAM_VRAM_BAR_SIZE => pdev.resource_len(1)?, 39 + _ => return Err(EINVAL), 40 + }; 41 + 42 + getparam.set_value(value); 43 + 44 + Ok(0) 45 + } 46 + 47 + /// IOCTL: gem_create: Create a new DRM GEM object. 48 + pub(crate) fn gem_create( 49 + dev: &NovaDevice, 50 + req: &Opaque<uapi::drm_nova_gem_create>, 51 + file: &drm::File<File>, 52 + ) -> Result<u32> { 53 + let req: &GemCreate = req.into(); 54 + let obj = NovaObject::new(dev, req.size().try_into()?)?; 55 + 56 + req.set_handle(obj.create_handle(file)?); 57 + 58 + Ok(0) 59 + } 60 + 61 + /// IOCTL: gem_info: Query GEM metadata. 62 + pub(crate) fn gem_info( 63 + _dev: &NovaDevice, 64 + req: &Opaque<uapi::drm_nova_gem_info>, 65 + file: &drm::File<File>, 66 + ) -> Result<u32> { 67 + let req: &GemInfo = req.into(); 68 + let bo = NovaObject::lookup_handle(file, req.handle())?; 69 + 70 + req.set_size(bo.size().try_into()?); 71 + 72 + Ok(0) 73 + } 74 + }
+49
drivers/gpu/drm/nova/gem.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + use kernel::{ 4 + drm, 5 + drm::{gem, gem::BaseObject}, 6 + prelude::*, 7 + types::ARef, 8 + }; 9 + 10 + use crate::{ 11 + driver::{NovaDevice, NovaDriver}, 12 + file::File, 13 + }; 14 + 15 + /// GEM Object inner driver data 16 + #[pin_data] 17 + pub(crate) struct NovaObject {} 18 + 19 + impl gem::BaseDriverObject<gem::Object<NovaObject>> for NovaObject { 20 + fn new(_dev: &NovaDevice, _size: usize) -> impl PinInit<Self, Error> { 21 + try_pin_init!(NovaObject {}) 22 + } 23 + } 24 + 25 + impl gem::DriverObject for NovaObject { 26 + type Driver = NovaDriver; 27 + } 28 + 29 + impl NovaObject { 30 + /// Create a new DRM GEM object. 31 + pub(crate) fn new(dev: &NovaDevice, size: usize) -> Result<ARef<gem::Object<Self>>> { 32 + let aligned_size = size.next_multiple_of(1 << 12); 33 + 34 + if size == 0 || size > aligned_size { 35 + return Err(EINVAL); 36 + } 37 + 38 + gem::Object::new(dev, aligned_size) 39 + } 40 + 41 + /// Look up a GEM object handle for a `File` and return an `ObjectRef` for it. 42 + #[inline] 43 + pub(crate) fn lookup_handle( 44 + file: &drm::File<File>, 45 + handle: u32, 46 + ) -> Result<ARef<gem::Object<Self>>> { 47 + gem::Object::lookup_handle(file, handle) 48 + } 49 + }
+18
drivers/gpu/drm/nova/nova.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + //! Nova DRM Driver 4 + 5 + mod driver; 6 + mod file; 7 + mod gem; 8 + mod uapi; 9 + 10 + use crate::driver::NovaDriver; 11 + 12 + kernel::module_auxiliary_driver! { 13 + type: NovaDriver, 14 + name: "Nova", 15 + author: "Danilo Krummrich", 16 + description: "Nova GPU driver", 17 + license: "GPL v2", 18 + }
+61
drivers/gpu/drm/nova/uapi.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + use kernel::uapi; 4 + 5 + // TODO Work out some common infrastructure to avoid boilerplate code for uAPI abstractions. 6 + 7 + macro_rules! define_uapi_abstraction { 8 + ($name:ident <= $inner:ty) => { 9 + #[repr(transparent)] 10 + pub struct $name(::kernel::types::Opaque<$inner>); 11 + 12 + impl ::core::convert::From<&::kernel::types::Opaque<$inner>> for &$name { 13 + fn from(value: &::kernel::types::Opaque<$inner>) -> Self { 14 + // SAFETY: `Self` is a transparent wrapper of `$inner`. 15 + unsafe { ::core::mem::transmute(value) } 16 + } 17 + } 18 + }; 19 + } 20 + 21 + define_uapi_abstraction!(Getparam <= uapi::drm_nova_getparam); 22 + 23 + impl Getparam { 24 + pub fn param(&self) -> u64 { 25 + // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_getparam`. 26 + unsafe { (*self.0.get()).param } 27 + } 28 + 29 + pub fn set_value(&self, v: u64) { 30 + // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_getparam`. 31 + unsafe { (*self.0.get()).value = v }; 32 + } 33 + } 34 + 35 + define_uapi_abstraction!(GemCreate <= uapi::drm_nova_gem_create); 36 + 37 + impl GemCreate { 38 + pub fn size(&self) -> u64 { 39 + // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_gem_create`. 40 + unsafe { (*self.0.get()).size } 41 + } 42 + 43 + pub fn set_handle(&self, handle: u32) { 44 + // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_gem_create`. 45 + unsafe { (*self.0.get()).handle = handle }; 46 + } 47 + } 48 + 49 + define_uapi_abstraction!(GemInfo <= uapi::drm_nova_gem_info); 50 + 51 + impl GemInfo { 52 + pub fn handle(&self) -> u32 { 53 + // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_gem_info`. 54 + unsafe { (*self.0.get()).handle } 55 + } 56 + 57 + pub fn set_size(&self, size: u64) { 58 + // SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_gem_info`. 59 + unsafe { (*self.0.get()).size = size }; 60 + } 61 + }
+101
include/uapi/drm/nova_drm.h
··· 1 + /* SPDX-License-Identifier: MIT */ 2 + 3 + #ifndef __NOVA_DRM_H__ 4 + #define __NOVA_DRM_H__ 5 + 6 + #include "drm.h" 7 + 8 + /* DISCLAIMER: Do not use, this is not a stable uAPI. 9 + * 10 + * This uAPI serves only testing purposes as long as this driver is still in 11 + * development. It is required to implement and test infrastructure which is 12 + * upstreamed in the context of this driver. See also [1]. 13 + * 14 + * [1] https://lore.kernel.org/dri-devel/Zfsj0_tb-0-tNrJy@cassiopeiae/T/#u 15 + */ 16 + 17 + #if defined(__cplusplus) 18 + extern "C" { 19 + #endif 20 + 21 + /* 22 + * NOVA_GETPARAM_VRAM_BAR_SIZE 23 + * 24 + * Query the VRAM BAR size in bytes. 25 + */ 26 + #define NOVA_GETPARAM_VRAM_BAR_SIZE 0x1 27 + 28 + /** 29 + * struct drm_nova_getparam - query GPU and driver metadata 30 + */ 31 + struct drm_nova_getparam { 32 + /** 33 + * @param: The identifier of the parameter to query. 34 + */ 35 + __u64 param; 36 + 37 + /** 38 + * @value: The value for the specified parameter. 39 + */ 40 + __u64 value; 41 + }; 42 + 43 + /** 44 + * struct drm_nova_gem_create - create a new DRM GEM object 45 + */ 46 + struct drm_nova_gem_create { 47 + /** 48 + * @handle: The handle of the new DRM GEM object. 49 + */ 50 + __u32 handle; 51 + 52 + /** 53 + * @pad: 32 bit padding, should be 0. 54 + */ 55 + __u32 pad; 56 + 57 + /** 58 + * @size: The size of the new DRM GEM object. 59 + */ 60 + __u64 size; 61 + }; 62 + 63 + /** 64 + * struct drm_nova_gem_info - query DRM GEM object metadata 65 + */ 66 + struct drm_nova_gem_info { 67 + /** 68 + * @handle: The handle of the DRM GEM object to query. 69 + */ 70 + __u32 handle; 71 + 72 + /** 73 + * @pad: 32 bit padding, should be 0. 74 + */ 75 + __u32 pad; 76 + 77 + /** 78 + * @size: The size of the DRM GEM obejct. 79 + */ 80 + __u64 size; 81 + }; 82 + 83 + #define DRM_NOVA_GETPARAM 0x00 84 + #define DRM_NOVA_GEM_CREATE 0x01 85 + #define DRM_NOVA_GEM_INFO 0x02 86 + 87 + /* Note: this is an enum so that it can be resolved by Rust bindgen. */ 88 + enum { 89 + DRM_IOCTL_NOVA_GETPARAM = DRM_IOWR(DRM_COMMAND_BASE + DRM_NOVA_GETPARAM, 90 + struct drm_nova_getparam), 91 + DRM_IOCTL_NOVA_GEM_CREATE = DRM_IOWR(DRM_COMMAND_BASE + DRM_NOVA_GEM_CREATE, 92 + struct drm_nova_gem_create), 93 + DRM_IOCTL_NOVA_GEM_INFO = DRM_IOWR(DRM_COMMAND_BASE + DRM_NOVA_GEM_INFO, 94 + struct drm_nova_gem_info), 95 + }; 96 + 97 + #if defined(__cplusplus) 98 + } 99 + #endif 100 + 101 + #endif /* __NOVA_DRM_H__ */
+1
rust/uapi/uapi_helper.h
··· 8 8 9 9 #include <uapi/asm-generic/ioctl.h> 10 10 #include <uapi/drm/drm.h> 11 + #include <uapi/drm/nova_drm.h> 11 12 #include <uapi/linux/mdio.h> 12 13 #include <uapi/linux/mii.h> 13 14 #include <uapi/linux/ethtool.h>