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