xref: /linux/drivers/gpu/nova-core/fb.rs (revision 7c01dc25f5c828401a5807307c4f7dda6555469f)
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     firmware::gsp::GspFirmware,
20     gpu::Chipset,
21     gsp,
22     num::{
23         usize_as_u64,
24         FromSafeCast, //
25     },
26     regs,
27 };
28 
29 mod hal;
30 
31 /// Type holding the sysmem flush memory page, a page of memory to be written into the
32 /// `NV_PFB_NISO_FLUSH_SYSMEM_ADDR*` registers and used to maintain memory coherency.
33 ///
34 /// A system memory page is required for `sysmembar`, which is a GPU-initiated hardware
35 /// memory-barrier operation that flushes all pending GPU-side memory writes that were done through
36 /// PCIE to system memory. It is required for falcons to be reset as the reset operation involves a
37 /// reset handshake. When the falcon acknowledges a reset, it writes into system memory. To ensure
38 /// this write is visible to the host and prevent driver timeouts, the falcon must perform a
39 /// sysmembar operation to flush its writes.
40 ///
41 /// Because of this, the sysmem flush memory page must be registered as early as possible during
42 /// driver initialization, and before any falcon is reset.
43 ///
44 /// Users are responsible for manually calling [`Self::unregister`] before dropping this object,
45 /// otherwise the GPU might still use it even after it has been freed.
46 pub(crate) struct SysmemFlush {
47     /// Chipset we are operating on.
48     chipset: Chipset,
49     device: ARef<device::Device>,
50     /// Keep the page alive as long as we need it.
51     page: DmaObject,
52 }
53 
54 impl SysmemFlush {
55     /// Allocate a memory page and register it as the sysmem flush page.
56     pub(crate) fn register(
57         dev: &device::Device<device::Bound>,
58         bar: &Bar0,
59         chipset: Chipset,
60     ) -> Result<Self> {
61         let page = DmaObject::new(dev, kernel::page::PAGE_SIZE)?;
62 
63         hal::fb_hal(chipset).write_sysmem_flush_page(bar, page.dma_handle())?;
64 
65         Ok(Self {
66             chipset,
67             device: dev.into(),
68             page,
69         })
70     }
71 
72     /// Unregister the managed sysmem flush page.
73     ///
74     /// In order to gracefully tear down the GPU, users must make sure to call this method before
75     /// dropping the object.
76     pub(crate) fn unregister(&self, bar: &Bar0) {
77         let hal = hal::fb_hal(self.chipset);
78 
79         if hal.read_sysmem_flush_page(bar) == self.page.dma_handle() {
80             let _ = hal.write_sysmem_flush_page(bar, 0).inspect_err(|e| {
81                 dev_warn!(
82                     &self.device,
83                     "failed to unregister sysmem flush page: {:?}",
84                     e
85                 )
86             });
87         } else {
88             // Another page has been registered after us for some reason - warn as this is a bug.
89             dev_warn!(
90                 &self.device,
91                 "attempt to unregister a sysmem flush page that is not active\n"
92             );
93         }
94     }
95 }
96 
97 /// Layout of the GPU framebuffer memory.
98 ///
99 /// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process.
100 #[derive(Debug)]
101 #[expect(dead_code)]
102 pub(crate) struct FbLayout {
103     /// Range of the framebuffer. Starts at `0`.
104     pub(crate) fb: Range<u64>,
105     /// VGA workspace, small area of reserved memory at the end of the framebuffer.
106     pub(crate) vga_workspace: Range<u64>,
107     /// FRTS range.
108     pub(crate) frts: Range<u64>,
109     /// Memory area containing the GSP bootloader image.
110     pub(crate) boot: Range<u64>,
111     /// Memory area containing the GSP firmware image.
112     pub(crate) elf: Range<u64>,
113     /// WPR2 heap.
114     pub(crate) wpr2_heap: Range<u64>,
115     /// WPR2 region range, starting with an instance of `GspFwWprMeta`.
116     pub(crate) wpr2: Range<u64>,
117     pub(crate) heap: Range<u64>,
118     pub(crate) vf_partition_count: u8,
119 }
120 
121 impl FbLayout {
122     /// Computes the FB layout for `chipset` required to run the `gsp_fw` GSP firmware.
123     pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result<Self> {
124         let hal = hal::fb_hal(chipset);
125 
126         let fb = {
127             let fb_size = hal.vidmem_size(bar);
128 
129             0..fb_size
130         };
131 
132         let vga_workspace = {
133             let vga_base = {
134                 const NV_PRAMIN_SIZE: u64 = usize_as_u64(SZ_1M);
135                 let base = fb.end - NV_PRAMIN_SIZE;
136 
137                 if hal.supports_display(bar) {
138                     match regs::NV_PDISP_VGA_WORKSPACE_BASE::read(bar).vga_workspace_addr() {
139                         Some(addr) => {
140                             if addr < base {
141                                 const VBIOS_WORKSPACE_SIZE: u64 = usize_as_u64(SZ_128K);
142 
143                                 // Point workspace address to end of framebuffer.
144                                 fb.end - VBIOS_WORKSPACE_SIZE
145                             } else {
146                                 addr
147                             }
148                         }
149                         None => base,
150                     }
151                 } else {
152                     base
153                 }
154             };
155 
156             vga_base..fb.end
157         };
158 
159         let frts = {
160             const FRTS_DOWN_ALIGN: Alignment = Alignment::new::<SZ_128K>();
161             const FRTS_SIZE: u64 = usize_as_u64(SZ_1M);
162             let frts_base = vga_workspace.start.align_down(FRTS_DOWN_ALIGN) - FRTS_SIZE;
163 
164             frts_base..frts_base + FRTS_SIZE
165         };
166 
167         let boot = {
168             const BOOTLOADER_DOWN_ALIGN: Alignment = Alignment::new::<SZ_4K>();
169             let bootloader_size = u64::from_safe_cast(gsp_fw.bootloader.ucode.size());
170             let bootloader_base = (frts.start - bootloader_size).align_down(BOOTLOADER_DOWN_ALIGN);
171 
172             bootloader_base..bootloader_base + bootloader_size
173         };
174 
175         let elf = {
176             const ELF_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>();
177             let elf_size = u64::from_safe_cast(gsp_fw.size);
178             let elf_addr = (boot.start - elf_size).align_down(ELF_DOWN_ALIGN);
179 
180             elf_addr..elf_addr + elf_size
181         };
182 
183         let wpr2_heap = {
184             const WPR2_HEAP_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
185             let wpr2_heap_size =
186                 gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb.end);
187             let wpr2_heap_addr = (elf.start - wpr2_heap_size).align_down(WPR2_HEAP_DOWN_ALIGN);
188 
189             wpr2_heap_addr..(elf.start).align_down(WPR2_HEAP_DOWN_ALIGN)
190         };
191 
192         let wpr2 = {
193             const WPR2_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
194             let wpr2_addr = (wpr2_heap.start - u64::from_safe_cast(size_of::<gsp::GspFwWprMeta>()))
195                 .align_down(WPR2_DOWN_ALIGN);
196 
197             wpr2_addr..frts.end
198         };
199 
200         let heap = {
201             const HEAP_SIZE: u64 = usize_as_u64(SZ_1M);
202 
203             wpr2.start - HEAP_SIZE..wpr2.start
204         };
205 
206         Ok(Self {
207             fb,
208             vga_workspace,
209             frts,
210             boot,
211             elf,
212             wpr2_heap,
213             wpr2,
214             heap,
215             vf_partition_count: 0,
216         })
217     }
218 }
219