File Index
Files and directories
Sign in
FILE

fb.rs

File preview with syntax highlighting.

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