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