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