xref: /linux/drivers/gpu/nova-core/fb.rs (revision 82b78182eacf82c1847c6f1fd93d91c15efb69cf)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 use core::ops::{
4     Deref,
5     Range, //
6 };
7 
8 use kernel::{
9     device,
10     dma::CoherentHandle,
11     fmt,
12     io::Io,
13     prelude::*,
14     ptr::{
15         Alignable,
16         Alignment, //
17     },
18     sizes::*,
19     sync::aref::ARef, //
20 };
21 
22 use crate::{
23     driver::Bar0,
24     firmware::gsp::GspFirmware,
25     gpu::Chipset,
26     gsp,
27     num::FromSafeCast,
28     regs, //
29 };
30 
31 mod hal;
32 
33 /// Type holding the sysmem flush memory page, a page of memory to be written into the
34 /// `NV_PFB_NISO_FLUSH_SYSMEM_ADDR*` registers and used to maintain memory coherency.
35 ///
36 /// A system memory page is required for `sysmembar`, which is a GPU-initiated hardware
37 /// memory-barrier operation that flushes all pending GPU-side memory writes that were done through
38 /// PCIE to system memory. It is required for falcons to be reset as the reset operation involves a
39 /// reset handshake. When the falcon acknowledges a reset, it writes into system memory. To ensure
40 /// this write is visible to the host and prevent driver timeouts, the falcon must perform a
41 /// sysmembar operation to flush its writes.
42 ///
43 /// Because of this, the sysmem flush memory page must be registered as early as possible during
44 /// driver initialization, and before any falcon is reset.
45 ///
46 /// Users are responsible for manually calling [`Self::unregister`] before dropping this object,
47 /// otherwise the GPU might still use it even after it has been freed.
48 pub(crate) struct SysmemFlush {
49     /// Chipset we are operating on.
50     chipset: Chipset,
51     device: ARef<device::Device>,
52     /// Keep the page alive as long as we need it.
53     page: CoherentHandle,
54 }
55 
56 impl SysmemFlush {
57     /// Allocate a memory page and register it as the sysmem flush page.
58     pub(crate) fn register(
59         dev: &device::Device<device::Bound>,
60         bar: &Bar0,
61         chipset: Chipset,
62     ) -> Result<Self> {
63         let page = CoherentHandle::alloc(dev, kernel::page::PAGE_SIZE, GFP_KERNEL)?;
64 
65         hal::fb_hal(chipset).write_sysmem_flush_page(bar, page.dma_handle())?;
66 
67         Ok(Self {
68             chipset,
69             device: dev.into(),
70             page,
71         })
72     }
73 
74     /// Unregister the managed sysmem flush page.
75     ///
76     /// In order to gracefully tear down the GPU, users must make sure to call this method before
77     /// dropping the object.
78     pub(crate) fn unregister(&self, bar: &Bar0) {
79         let hal = hal::fb_hal(self.chipset);
80 
81         if hal.read_sysmem_flush_page(bar) == self.page.dma_handle() {
82             let _ = hal.write_sysmem_flush_page(bar, 0).inspect_err(|e| {
83                 dev_warn!(
84                     &self.device,
85                     "failed to unregister sysmem flush page: {:?}\n",
86                     e
87                 )
88             });
89         } else {
90             // Another page has been registered after us for some reason - warn as this is a bug.
91             dev_warn!(
92                 &self.device,
93                 "attempt to unregister a sysmem flush page that is not active\n"
94             );
95         }
96     }
97 }
98 
99 pub(crate) struct FbRange(Range<u64>);
100 
101 impl FbRange {
102     pub(crate) fn len(&self) -> u64 {
103         self.0.end - self.0.start
104     }
105 }
106 
107 impl From<Range<u64>> for FbRange {
108     fn from(range: Range<u64>) -> Self {
109         Self(range)
110     }
111 }
112 
113 impl Deref for FbRange {
114     type Target = Range<u64>;
115 
116     fn deref(&self) -> &Self::Target {
117         &self.0
118     }
119 }
120 
121 impl fmt::Debug for FbRange {
122     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123         // Use alternate format ({:#?}) to include size, compact format ({:?}) for just the range.
124         if f.alternate() {
125             let size = self.len();
126 
127             if size < u64::SZ_1M {
128                 let size_kib = size / u64::SZ_1K;
129                 f.write_fmt(fmt!(
130                     "{:#x}..{:#x} ({} KiB)",
131                     self.0.start,
132                     self.0.end,
133                     size_kib
134                 ))
135             } else {
136                 let size_mib = size / u64::SZ_1M;
137                 f.write_fmt(fmt!(
138                     "{:#x}..{:#x} ({} MiB)",
139                     self.0.start,
140                     self.0.end,
141                     size_mib
142                 ))
143             }
144         } else {
145             f.write_fmt(fmt!("{:#x}..{:#x}", self.0.start, self.0.end))
146         }
147     }
148 }
149 
150 /// Layout of the GPU framebuffer memory.
151 ///
152 /// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process.
153 #[derive(Debug)]
154 pub(crate) struct FbLayout {
155     /// Range of the framebuffer. Starts at `0`.
156     pub(crate) fb: FbRange,
157     /// VGA workspace, small area of reserved memory at the end of the framebuffer.
158     pub(crate) vga_workspace: FbRange,
159     /// FRTS range.
160     pub(crate) frts: FbRange,
161     /// Memory area containing the GSP bootloader image.
162     pub(crate) boot: FbRange,
163     /// Memory area containing the GSP firmware image.
164     pub(crate) elf: FbRange,
165     /// WPR2 heap.
166     pub(crate) wpr2_heap: FbRange,
167     /// WPR2 region range, starting with an instance of `GspFwWprMeta`.
168     pub(crate) wpr2: FbRange,
169     pub(crate) heap: FbRange,
170     pub(crate) vf_partition_count: u8,
171 }
172 
173 impl FbLayout {
174     /// Computes the FB layout for `chipset` required to run the `gsp_fw` GSP firmware.
175     pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result<Self> {
176         let hal = hal::fb_hal(chipset);
177 
178         let fb = {
179             let fb_size = hal.vidmem_size(bar);
180 
181             FbRange(0..fb_size)
182         };
183 
184         let vga_workspace = {
185             let vga_base = {
186                 const NV_PRAMIN_SIZE: u64 = u64::SZ_1M;
187                 let base = fb.end - NV_PRAMIN_SIZE;
188 
189                 if hal.supports_display(bar) {
190                     match bar
191                         .read(regs::NV_PDISP_VGA_WORKSPACE_BASE)
192                         .vga_workspace_addr()
193                     {
194                         Some(addr) => {
195                             if addr < base {
196                                 const VBIOS_WORKSPACE_SIZE: u64 = u64::SZ_128K;
197 
198                                 // Point workspace address to end of framebuffer.
199                                 fb.end - VBIOS_WORKSPACE_SIZE
200                             } else {
201                                 addr
202                             }
203                         }
204                         None => base,
205                     }
206                 } else {
207                     base
208                 }
209             };
210 
211             FbRange(vga_base..fb.end)
212         };
213 
214         let frts = {
215             const FRTS_DOWN_ALIGN: Alignment = Alignment::new::<SZ_128K>();
216             let frts_size: u64 = hal.frts_size();
217             let frts_base = vga_workspace.start.align_down(FRTS_DOWN_ALIGN) - frts_size;
218 
219             FbRange(frts_base..frts_base + frts_size)
220         };
221 
222         let boot = {
223             const BOOTLOADER_DOWN_ALIGN: Alignment = Alignment::new::<SZ_4K>();
224             let bootloader_size = u64::from_safe_cast(gsp_fw.bootloader.ucode.size());
225             let bootloader_base = (frts.start - bootloader_size).align_down(BOOTLOADER_DOWN_ALIGN);
226 
227             FbRange(bootloader_base..bootloader_base + bootloader_size)
228         };
229 
230         let elf = {
231             const ELF_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>();
232             let elf_size = u64::from_safe_cast(gsp_fw.size);
233             let elf_addr = (boot.start - elf_size).align_down(ELF_DOWN_ALIGN);
234 
235             FbRange(elf_addr..elf_addr + elf_size)
236         };
237 
238         let wpr2_heap = {
239             const WPR2_HEAP_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
240             let wpr2_heap_size =
241                 gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb.end)?;
242             let wpr2_heap_addr = (elf.start - wpr2_heap_size).align_down(WPR2_HEAP_DOWN_ALIGN);
243 
244             FbRange(wpr2_heap_addr..(elf.start).align_down(WPR2_HEAP_DOWN_ALIGN))
245         };
246 
247         let wpr2 = {
248             const WPR2_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
249             let wpr2_addr = (wpr2_heap.start - u64::from_safe_cast(size_of::<gsp::GspFwWprMeta>()))
250                 .align_down(WPR2_DOWN_ALIGN);
251 
252             FbRange(wpr2_addr..frts.end)
253         };
254 
255         let heap = {
256             const HEAP_SIZE: u64 = u64::SZ_1M;
257 
258             FbRange(wpr2.start - HEAP_SIZE..wpr2.start)
259         };
260 
261         Ok(Self {
262             fb,
263             vga_workspace,
264             frts,
265             boot,
266             elf,
267             wpr2_heap,
268             wpr2,
269             heap,
270             vf_partition_count: 0,
271         })
272     }
273 }
274