1 // SPDX-License-Identifier: GPL-2.0 2 3 use kernel::{ 4 auxiliary, 5 device::Core, 6 dma::Device, 7 dma::DmaMask, 8 pci, 9 pci::{ 10 Class, 11 ClassMask, 12 Vendor, // 13 }, 14 prelude::*, 15 sizes::SZ_16M, 16 sync::{ 17 atomic::{ 18 Atomic, 19 Relaxed, // 20 }, 21 Arc, 22 }, 23 types::ForLt, 24 }; 25 26 use crate::gpu::Gpu; 27 28 /// Counter for generating unique auxiliary device IDs. 29 static AUXILIARY_ID_COUNTER: Atomic<u32> = Atomic::new(0); 30 31 #[pin_data] 32 pub(crate) struct NovaCore<'bound> { 33 #[pin] 34 pub(crate) gpu: Gpu, 35 #[allow(clippy::type_complexity)] 36 _reg: auxiliary::Registration<'bound, ForLt!(())>, 37 } 38 39 pub(crate) struct NovaCoreDriver; 40 41 const BAR0_SIZE: usize = SZ_16M; 42 43 // For now we only support Ampere which can use up to 47-bit DMA addresses. 44 // 45 // TODO: Add an abstraction for this to support newer GPUs which may support 46 // larger DMA addresses. Limiting these GPUs to smaller address widths won't 47 // have any adverse affects, unless installed on systems which require larger 48 // DMA addresses. These systems should be quite rare. 49 const GPU_DMA_BITS: u32 = 47; 50 51 pub(crate) type Bar0 = pci::Bar<'static, BAR0_SIZE>; 52 53 kernel::pci_device_table!( 54 PCI_TABLE, 55 MODULE_PCI_TABLE, 56 <NovaCoreDriver as pci::Driver>::IdInfo, 57 [ 58 // Modern NVIDIA GPUs will show up as either VGA or 3D controllers. 59 ( 60 pci::DeviceId::from_class_and_vendor( 61 Class::DISPLAY_VGA, 62 ClassMask::ClassSubclass, 63 Vendor::NVIDIA 64 ), 65 () 66 ), 67 ( 68 pci::DeviceId::from_class_and_vendor( 69 Class::DISPLAY_3D, 70 ClassMask::ClassSubclass, 71 Vendor::NVIDIA 72 ), 73 () 74 ), 75 ] 76 ); 77 78 impl pci::Driver for NovaCoreDriver { 79 type IdInfo = (); 80 type Data<'bound> = NovaCore<'bound>; 81 const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE; 82 83 fn probe<'bound>( 84 pdev: &'bound pci::Device<Core<'_>>, 85 _info: &'bound Self::IdInfo, 86 ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound { 87 pin_init::pin_init_scope(move || { 88 dev_dbg!(pdev, "Probe Nova Core GPU driver.\n"); 89 90 pdev.enable_device_mem()?; 91 pdev.set_master(); 92 93 // SAFETY: No concurrent DMA allocations or mappings can be made because 94 // the device is still being probed and therefore isn't being used by 95 // other threads of execution. 96 unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::<GPU_DMA_BITS>())? }; 97 98 let bar = Arc::new( 99 pdev.iomap_region_sized::<BAR0_SIZE>(0, c"nova-core/bar0")? 100 .into_devres()?, 101 GFP_KERNEL, 102 )?; 103 104 Ok(try_pin_init!(NovaCore { 105 gpu <- Gpu::new(pdev, bar.clone(), bar.access(pdev.as_ref())?), 106 _reg: auxiliary::Registration::new( 107 pdev.as_ref(), 108 c"nova-drm", 109 // TODO[XARR]: Use XArray or perhaps IDA for proper ID allocation/recycling. For 110 // now, use a simple atomic counter that never recycles IDs. 111 AUXILIARY_ID_COUNTER.fetch_add(1, Relaxed), 112 crate::MODULE_NAME, 113 (), 114 )?, 115 })) 116 }) 117 } 118 119 fn unbind<'bound>(pdev: &'bound pci::Device<Core<'_>>, this: Pin<&Self::Data<'bound>>) { 120 this.gpu.unbind(pdev.as_ref()); 121 } 122 } 123