1 // SPDX-License-Identifier: GPL-2.0 2 3 use core::ops::Range; 4 5 use kernel::{ 6 device, 7 prelude::*, 8 ptr::{ 9 Alignable, 10 Alignment, // 11 }, 12 sizes::*, 13 sync::aref::ARef, // 14 }; 15 16 use crate::{ 17 dma::DmaObject, 18 driver::Bar0, 19 gpu::Chipset, 20 num::usize_as_u64, 21 regs, // 22 }; 23 24 mod hal; 25 26 /// Type holding the sysmem flush memory page, a page of memory to be written into the 27 /// `NV_PFB_NISO_FLUSH_SYSMEM_ADDR*` registers and used to maintain memory coherency. 28 /// 29 /// A system memory page is required for `sysmembar`, which is a GPU-initiated hardware 30 /// memory-barrier operation that flushes all pending GPU-side memory writes that were done through 31 /// PCIE to system memory. It is required for falcons to be reset as the reset operation involves a 32 /// reset handshake. When the falcon acknowledges a reset, it writes into system memory. To ensure 33 /// this write is visible to the host and prevent driver timeouts, the falcon must perform a 34 /// sysmembar operation to flush its writes. 35 /// 36 /// Because of this, the sysmem flush memory page must be registered as early as possible during 37 /// driver initialization, and before any falcon is reset. 38 /// 39 /// Users are responsible for manually calling [`Self::unregister`] before dropping this object, 40 /// otherwise the GPU might still use it even after it has been freed. 41 pub(crate) struct SysmemFlush { 42 /// Chipset we are operating on. 43 chipset: Chipset, 44 device: ARef<device::Device>, 45 /// Keep the page alive as long as we need it. 46 page: DmaObject, 47 } 48 49 impl SysmemFlush { 50 /// Allocate a memory page and register it as the sysmem flush page. 51 pub(crate) fn register( 52 dev: &device::Device<device::Bound>, 53 bar: &Bar0, 54 chipset: Chipset, 55 ) -> Result<Self> { 56 let page = DmaObject::new(dev, kernel::page::PAGE_SIZE)?; 57 58 hal::fb_hal(chipset).write_sysmem_flush_page(bar, page.dma_handle())?; 59 60 Ok(Self { 61 chipset, 62 device: dev.into(), 63 page, 64 }) 65 } 66 67 /// Unregister the managed sysmem flush page. 68 /// 69 /// In order to gracefully tear down the GPU, users must make sure to call this method before 70 /// dropping the object. 71 pub(crate) fn unregister(&self, bar: &Bar0) { 72 let hal = hal::fb_hal(self.chipset); 73 74 if hal.read_sysmem_flush_page(bar) == self.page.dma_handle() { 75 let _ = hal.write_sysmem_flush_page(bar, 0).inspect_err(|e| { 76 dev_warn!( 77 &self.device, 78 "failed to unregister sysmem flush page: {:?}", 79 e 80 ) 81 }); 82 } else { 83 // Another page has been registered after us for some reason - warn as this is a bug. 84 dev_warn!( 85 &self.device, 86 "attempt to unregister a sysmem flush page that is not active\n" 87 ); 88 } 89 } 90 } 91 92 /// Layout of the GPU framebuffer memory. 93 /// 94 /// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process. 95 #[derive(Debug)] 96 #[expect(dead_code)] 97 pub(crate) struct FbLayout { 98 pub(crate) fb: Range<u64>, 99 pub(crate) vga_workspace: Range<u64>, 100 pub(crate) frts: Range<u64>, 101 } 102 103 impl FbLayout { 104 /// Computes the FB layout. 105 pub(crate) fn new(chipset: Chipset, bar: &Bar0) -> Result<Self> { 106 let hal = hal::fb_hal(chipset); 107 108 let fb = { 109 let fb_size = hal.vidmem_size(bar); 110 111 0..fb_size 112 }; 113 114 let vga_workspace = { 115 let vga_base = { 116 const NV_PRAMIN_SIZE: u64 = usize_as_u64(SZ_1M); 117 let base = fb.end - NV_PRAMIN_SIZE; 118 119 if hal.supports_display(bar) { 120 match regs::NV_PDISP_VGA_WORKSPACE_BASE::read(bar).vga_workspace_addr() { 121 Some(addr) => { 122 if addr < base { 123 const VBIOS_WORKSPACE_SIZE: u64 = usize_as_u64(SZ_128K); 124 125 // Point workspace address to end of framebuffer. 126 fb.end - VBIOS_WORKSPACE_SIZE 127 } else { 128 addr 129 } 130 } 131 None => base, 132 } 133 } else { 134 base 135 } 136 }; 137 138 vga_base..fb.end 139 }; 140 141 let frts = { 142 const FRTS_DOWN_ALIGN: Alignment = Alignment::new::<SZ_128K>(); 143 const FRTS_SIZE: u64 = usize_as_u64(SZ_1M); 144 let frts_base = vga_workspace.start.align_down(FRTS_DOWN_ALIGN) - FRTS_SIZE; 145 146 frts_base..frts_base + FRTS_SIZE 147 }; 148 149 Ok(Self { 150 fb, 151 vga_workspace, 152 frts, 153 }) 154 } 155 } 156