diff options
Diffstat (limited to 'drivers/gpu/drm/nova')
-rw-r--r-- | drivers/gpu/drm/nova/Kconfig | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/nova/Makefile | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/nova/driver.rs | 103 | ||||
-rw-r--r-- | drivers/gpu/drm/nova/file.rs | 70 | ||||
-rw-r--r-- | drivers/gpu/drm/nova/gem.rs | 50 | ||||
-rw-r--r-- | drivers/gpu/drm/nova/gpu.rs | 173 | ||||
-rw-r--r-- | drivers/gpu/drm/nova/nova.rs | 18 |
7 files changed, 429 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nova/Kconfig b/drivers/gpu/drm/nova/Kconfig new file mode 100644 index 000000000000..3c15593e054b --- /dev/null +++ b/drivers/gpu/drm/nova/Kconfig @@ -0,0 +1,12 @@ +config DRM_NOVA_STUB + tristate "Nova GPU driver stub" + depends on DRM + depends on PCI + depends on RUST + depends on RUST_FW_LOADER_ABSTRACTIONS + default n + help + Choose this if you want to build the Nova stub driver for Nvidia + GSP-based GPUs. + + If M is selected, the module will be called nova. diff --git a/drivers/gpu/drm/nova/Makefile b/drivers/gpu/drm/nova/Makefile new file mode 100644 index 000000000000..733ac5fb9f4f --- /dev/null +++ b/drivers/gpu/drm/nova/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_DRM_NOVA_STUB) += nova.o diff --git a/drivers/gpu/drm/nova/driver.rs b/drivers/gpu/drm/nova/driver.rs new file mode 100644 index 000000000000..346998fe934f --- /dev/null +++ b/drivers/gpu/drm/nova/driver.rs @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 + +use kernel::{ + bindings, c_str, drm, + drm::{drv, ioctl}, + pci, + prelude::*, + sync::Arc, + types::ARef, +}; + +use crate::{file::File, gpu::Gpu}; + +pub(crate) struct NovaDriver(ARef<NovaDevice>); + +/// Convienence type alias for the DRM device type for this driver +pub(crate) type NovaDevice = drm::device::Device<NovaDriver>; + +#[allow(dead_code)] +#[pin_data] +pub(crate) struct NovaData { + #[pin] + pub(crate) gpu: Gpu, + pub(crate) pdev: pci::Device, +} + +const BAR0_SIZE: usize = 8; +pub(crate) type Bar0 = pci::Bar<BAR0_SIZE>; + +const INFO: drm::drv::DriverInfo = drm::drv::DriverInfo { + major: 0, + minor: 0, + patchlevel: 0, + name: c_str!("nova"), + desc: c_str!("Nvidia Graphics"), + date: c_str!("20240227"), +}; + +kernel::pci_device_table!( + PCI_TABLE, + MODULE_PCI_TABLE, + <NovaDriver as pci::Driver>::IdInfo, + [( + pci::DeviceId::new(bindings::PCI_VENDOR_ID_NVIDIA, bindings::PCI_ANY_ID as _), + () + )] +); + +impl pci::Driver for NovaDriver { + type IdInfo = (); + const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE; + + fn probe( + pdev: &mut pci::Device, + _id: &pci::DeviceId, + _info: &Self::IdInfo, + ) -> Result<Pin<KBox<Self>>> { + dev_dbg!(pdev.as_ref(), "Probe Nova GPU driver.\n"); + + pdev.enable_device_mem()?; + pdev.set_master(); + + let bar = pdev.iomap_region_sized::<BAR0_SIZE>(0, c_str!("nova"))?; + + let p = pdev.clone(); + let data = Arc::pin_init( + try_pin_init!(NovaData { + gpu <- Gpu::new(&p, bar)?, + pdev: p, + }), + GFP_KERNEL, + )?; + + let drm = drm::device::Device::<NovaDriver>::new(pdev.as_ref(), data.clone())?; + drm::drv::Registration::new_foreign_owned(drm.clone(), 0)?; + + Ok(KBox::new(Self(drm), GFP_KERNEL)?.into()) + } +} + +impl Drop for NovaDriver { + fn drop(&mut self) { + let data = self.0.data(); + + dev_dbg!(data.pdev.as_ref(), "Remove Nova GPU driver.\n"); + } +} + +#[vtable] +impl drm::drv::Driver for NovaDriver { + type Data = Arc<NovaData>; + type File = File; + type Object = crate::gem::Object; + + const INFO: drm::drv::DriverInfo = INFO; + const FEATURES: u32 = drv::FEAT_GEM; + + kernel::declare_drm_ioctls! { + (NOVA_GETPARAM, drm_nova_getparam, ioctl::RENDER_ALLOW, File::get_param), + (NOVA_GEM_CREATE, drm_nova_gem_create, ioctl::AUTH | ioctl::RENDER_ALLOW, File::gem_create), + (NOVA_GEM_INFO, drm_nova_gem_info, ioctl::AUTH | ioctl::RENDER_ALLOW, File::gem_info), + } +} diff --git a/drivers/gpu/drm/nova/file.rs b/drivers/gpu/drm/nova/file.rs new file mode 100644 index 000000000000..0cabaee327bb --- /dev/null +++ b/drivers/gpu/drm/nova/file.rs @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0 + +use crate::driver::{NovaDevice, NovaDriver}; +use crate::gem; +use kernel::{ + alloc::flags::*, + drm::{self, device::Device as DrmDevice, gem::BaseObject}, + prelude::*, + uapi, +}; + +pub(crate) struct File(); + +/// Convenience type alias for our DRM `File` type +pub(crate) type DrmFile = drm::file::File<File>; + +impl drm::file::DriverFile for File { + type Driver = NovaDriver; + + fn open(dev: &DrmDevice<Self::Driver>) -> Result<Pin<KBox<Self>>> { + dev_dbg!(dev.as_ref(), "drm::device::Device::open\n"); + + Ok(KBox::new(Self(), GFP_KERNEL)?.into()) + } +} + +impl File { + /// IOCTL: get_param: Query GPU / driver metadata. + pub(crate) fn get_param( + dev: &NovaDevice, + getparam: &mut uapi::drm_nova_getparam, + _file: &DrmFile, + ) -> Result<u32> { + let pdev = &dev.data().pdev; + + getparam.value = match getparam.param as u32 { + uapi::NOVA_GETPARAM_VRAM_BAR_SIZE => pdev.resource_len(1)?, + _ => return Err(EINVAL), + }; + + Ok(0) + } + + /// IOCTL: gem_create: Create a new DRM GEM object. + pub(crate) fn gem_create( + dev: &NovaDevice, + req: &mut uapi::drm_nova_gem_create, + file: &DrmFile, + ) -> Result<u32> { + let obj = gem::object_new(dev, req.size.try_into()?)?; + + let handle = obj.create_handle(file)?; + req.handle = handle; + + Ok(0) + } + + /// IOCTL: gem_info: Query GEM metadata. + pub(crate) fn gem_info( + _dev: &NovaDevice, + req: &mut uapi::drm_nova_gem_info, + file: &DrmFile, + ) -> Result<u32> { + let bo = gem::lookup_handle(file, req.handle)?; + + req.size = bo.size().try_into()?; + + Ok(0) + } +} diff --git a/drivers/gpu/drm/nova/gem.rs b/drivers/gpu/drm/nova/gem.rs new file mode 100644 index 000000000000..51bc30c226e2 --- /dev/null +++ b/drivers/gpu/drm/nova/gem.rs @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 + +use kernel::{ + drm::{ + gem, + gem::{BaseObject, ObjectRef}, + }, + prelude::*, +}; + +use crate::{ + driver::{NovaDevice, NovaDriver}, + file::DrmFile, +}; + +/// GEM Object inner driver data +#[pin_data] +pub(crate) struct DriverObject {} + +/// Type alias for the GEM object tyoe for this driver. +pub(crate) type Object = gem::Object<DriverObject>; + +impl gem::BaseDriverObject<Object> for DriverObject { + fn new(dev: &NovaDevice, _size: usize) -> impl PinInit<Self, Error> { + dev_dbg!(dev.as_ref(), "DriverObject::new\n"); + DriverObject {} + } +} + +impl gem::DriverObject for DriverObject { + type Driver = NovaDriver; +} + +/// Create a new DRM GEM object. +pub(crate) fn object_new(dev: &NovaDevice, size: usize) -> Result<ObjectRef<Object>> { + let aligned_size = size.next_multiple_of(1 << 12); + + if size == 0 || size > aligned_size { + return Err(EINVAL); + } + + let gem = Object::new(dev, aligned_size)?; + + Ok(ObjectRef::from_pinned_unique(gem)) +} + +/// Look up a GEM object handle for a `File` and return an `ObjectRef` for it. +pub(crate) fn lookup_handle(file: &DrmFile, handle: u32) -> Result<ObjectRef<Object>> { + Object::lookup_handle(file, handle) +} diff --git a/drivers/gpu/drm/nova/gpu.rs b/drivers/gpu/drm/nova/gpu.rs new file mode 100644 index 000000000000..2b8f34ac2198 --- /dev/null +++ b/drivers/gpu/drm/nova/gpu.rs @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0 + +use kernel::{ + device, devres::Devres, error::code::*, firmware, fmt, pci, prelude::*, str::CString, +}; + +use crate::driver::Bar0; +use core::fmt::Debug; + +/// Enum representing the GPU chipset. +#[derive(Debug)] +pub(crate) enum Chipset { + TU102 = 0x162, + TU104 = 0x164, + TU106 = 0x166, + TU117 = 0x167, + TU116 = 0x168, + GA102 = 0x172, + GA103 = 0x173, + GA104 = 0x174, + GA106 = 0x176, + GA107 = 0x177, + AD102 = 0x192, + AD103 = 0x193, + AD104 = 0x194, + AD106 = 0x196, + AD107 = 0x197, +} + +/// Enum representing the GPU generation. +#[derive(Debug)] +pub(crate) enum CardType { + /// Turing + TU100 = 0x160, + /// Ampere + GA100 = 0x170, + /// Ada Lovelace + AD100 = 0x190, +} + +/// Structure holding the metadata of the GPU. +#[allow(dead_code)] +pub(crate) struct GpuSpec { + /// Contents of the boot0 register. + boot0: u64, + card_type: CardType, + chipset: Chipset, + /// The revision of the chipset. + chiprev: u8, +} + +/// Structure encapsulating the firmware blobs required for the GPU to operate. +#[allow(dead_code)] +pub(crate) struct Firmware { + booter_load: firmware::Firmware, + booter_unload: firmware::Firmware, + gsp: firmware::Firmware, +} + +/// Structure holding the resources required to operate the GPU. +#[allow(dead_code)] +#[pin_data] +pub(crate) struct Gpu { + spec: GpuSpec, + /// MMIO mapping of PCI BAR 0 + bar: Devres<Bar0>, + fw: Firmware, +} + +// TODO replace with something like derive(FromPrimitive) +impl Chipset { + fn from_u32(value: u32) -> Option<Chipset> { + match value { + 0x162 => Some(Chipset::TU102), + 0x164 => Some(Chipset::TU104), + 0x166 => Some(Chipset::TU106), + 0x167 => Some(Chipset::TU117), + 0x168 => Some(Chipset::TU116), + 0x172 => Some(Chipset::GA102), + 0x173 => Some(Chipset::GA103), + 0x174 => Some(Chipset::GA104), + 0x176 => Some(Chipset::GA106), + 0x177 => Some(Chipset::GA107), + 0x192 => Some(Chipset::AD102), + 0x193 => Some(Chipset::AD103), + 0x194 => Some(Chipset::AD104), + 0x196 => Some(Chipset::AD106), + 0x197 => Some(Chipset::AD107), + _ => None, + } + } +} + +// TODO replace with something like derive(FromPrimitive) +impl CardType { + fn from_u32(value: u32) -> Option<CardType> { + match value { + 0x160 => Some(CardType::TU100), + 0x170 => Some(CardType::GA100), + 0x190 => Some(CardType::AD100), + _ => None, + } + } +} + +impl GpuSpec { + fn new(bar: &Devres<Bar0>) -> Result<GpuSpec> { + let bar = bar.try_access().ok_or(ENXIO)?; + let boot0 = u64::from_le(bar.readq(0)); + let chip = ((boot0 & 0x1ff00000) >> 20) as u32; + + if boot0 & 0x1f000000 == 0 { + return Err(ENODEV); + } + + let chipset = match Chipset::from_u32(chip) { + Some(x) => x, + None => return Err(ENODEV), + }; + + let card_type = match CardType::from_u32(chip & 0x1f0) { + Some(x) => x, + None => return Err(ENODEV), + }; + + Ok(Self { + boot0, + card_type, + chipset, + chiprev: (boot0 & 0xff) as u8, + }) + } +} + +impl Firmware { + fn new(dev: &device::Device, spec: &GpuSpec, ver: &str) -> Result<Firmware> { + let mut chip_name = CString::try_from_fmt(fmt!("{:?}", spec.chipset))?; + chip_name.make_ascii_lowercase(); + + let fw_booter_load_path = + CString::try_from_fmt(fmt!("nvidia/{}/gsp/booter_load-{}.bin", &*chip_name, ver,))?; + let fw_booter_unload_path = + CString::try_from_fmt(fmt!("nvidia/{}/gsp/booter_unload-{}.bin", &*chip_name, ver))?; + let fw_gsp_path = + CString::try_from_fmt(fmt!("nvidia/{}/gsp/gsp-{}.bin", &*chip_name, ver))?; + + let booter_load = firmware::Firmware::request(&fw_booter_load_path, dev)?; + let booter_unload = firmware::Firmware::request(&fw_booter_unload_path, dev)?; + let gsp = firmware::Firmware::request(&fw_gsp_path, dev)?; + + Ok(Firmware { + booter_load, + booter_unload, + gsp, + }) + } +} + +impl Gpu { + pub(crate) fn new(pdev: &pci::Device, bar: Devres<Bar0>) -> Result<impl PinInit<Self>> { + let spec = GpuSpec::new(&bar)?; + let fw = Firmware::new(pdev.as_ref(), &spec, "535.113.01")?; + + dev_info!( + pdev.as_ref(), + "NVIDIA {:?} ({:#x})", + spec.chipset, + spec.boot0 + ); + + Ok(pin_init!(Self { spec, bar, fw })) + } +} diff --git a/drivers/gpu/drm/nova/nova.rs b/drivers/gpu/drm/nova/nova.rs new file mode 100644 index 000000000000..c675be404d9b --- /dev/null +++ b/drivers/gpu/drm/nova/nova.rs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Nova GPU Driver + +mod driver; +mod file; +mod gem; +mod gpu; + +use crate::driver::NovaDriver; + +kernel::module_pci_driver! { + type: NovaDriver, + name: "Nova", + author: "Danilo Krummrich", + description: "Nova GPU driver", + license: "GPL v2", +} |