xref: /linux/drivers/gpu/nova-core/fb.rs (revision 3606620b316c29e3de8ff87b40828c722086a9c9)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 use core::ops::Range;
4 
5 use kernel::prelude::*;
6 use kernel::sizes::*;
7 use kernel::types::ARef;
8 use kernel::{dev_warn, device};
9 
10 use crate::dma::DmaObject;
11 use crate::driver::Bar0;
12 use crate::gpu::Chipset;
13 use crate::regs;
14 
15 mod hal;
16 
17 /// Type holding the sysmem flush memory page, a page of memory to be written into the
18 /// `NV_PFB_NISO_FLUSH_SYSMEM_ADDR*` registers and used to maintain memory coherency.
19 ///
20 /// Users are responsible for manually calling [`Self::unregister`] before dropping this object,
21 /// otherwise the GPU might still use it even after it has been freed.
22 pub(crate) struct SysmemFlush {
23     /// Chipset we are operating on.
24     chipset: Chipset,
25     device: ARef<device::Device>,
26     /// Keep the page alive as long as we need it.
27     page: DmaObject,
28 }
29 
30 impl SysmemFlush {
31     /// Allocate a memory page and register it as the sysmem flush page.
32     pub(crate) fn register(
33         dev: &device::Device<device::Bound>,
34         bar: &Bar0,
35         chipset: Chipset,
36     ) -> Result<Self> {
37         let page = DmaObject::new(dev, kernel::page::PAGE_SIZE)?;
38 
39         hal::fb_hal(chipset).write_sysmem_flush_page(bar, page.dma_handle())?;
40 
41         Ok(Self {
42             chipset,
43             device: dev.into(),
44             page,
45         })
46     }
47 
48     /// Unregister the managed sysmem flush page.
49     ///
50     /// In order to gracefully tear down the GPU, users must make sure to call this method before
51     /// dropping the object.
52     pub(crate) fn unregister(&self, bar: &Bar0) {
53         let hal = hal::fb_hal(self.chipset);
54 
55         if hal.read_sysmem_flush_page(bar) == self.page.dma_handle() {
56             let _ = hal.write_sysmem_flush_page(bar, 0).inspect_err(|e| {
57                 dev_warn!(
58                     &self.device,
59                     "failed to unregister sysmem flush page: {:?}",
60                     e
61                 )
62             });
63         } else {
64             // Another page has been registered after us for some reason - warn as this is a bug.
65             dev_warn!(
66                 &self.device,
67                 "attempt to unregister a sysmem flush page that is not active\n"
68             );
69         }
70     }
71 }
72 
73 /// Layout of the GPU framebuffer memory.
74 ///
75 /// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process.
76 #[derive(Debug)]
77 #[expect(dead_code)]
78 pub(crate) struct FbLayout {
79     pub(crate) fb: Range<u64>,
80     pub(crate) vga_workspace: Range<u64>,
81     pub(crate) frts: Range<u64>,
82 }
83 
84 impl FbLayout {
85     /// Computes the FB layout.
86     pub(crate) fn new(chipset: Chipset, bar: &Bar0) -> Result<Self> {
87         let hal = hal::fb_hal(chipset);
88 
89         let fb = {
90             let fb_size = hal.vidmem_size(bar);
91 
92             0..fb_size
93         };
94 
95         let vga_workspace = {
96             let vga_base = {
97                 const NV_PRAMIN_SIZE: u64 = SZ_1M as u64;
98                 let base = fb.end - NV_PRAMIN_SIZE;
99 
100                 if hal.supports_display(bar) {
101                     match regs::NV_PDISP_VGA_WORKSPACE_BASE::read(bar).vga_workspace_addr() {
102                         Some(addr) => {
103                             if addr < base {
104                                 const VBIOS_WORKSPACE_SIZE: u64 = SZ_128K as u64;
105 
106                                 // Point workspace address to end of framebuffer.
107                                 fb.end - VBIOS_WORKSPACE_SIZE
108                             } else {
109                                 addr
110                             }
111                         }
112                         None => base,
113                     }
114                 } else {
115                     base
116                 }
117             };
118 
119             vga_base..fb.end
120         };
121 
122         let frts = {
123             const FRTS_DOWN_ALIGN: u64 = SZ_128K as u64;
124             const FRTS_SIZE: u64 = SZ_1M as u64;
125             // TODO[NUMM]: replace with `align_down` once it lands.
126             let frts_base = (vga_workspace.start & !(FRTS_DOWN_ALIGN - 1)) - FRTS_SIZE;
127 
128             frts_base..frts_base + FRTS_SIZE
129         };
130 
131         Ok(Self {
132             fb,
133             vga_workspace,
134             frts,
135         })
136     }
137 }
138