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