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