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