16554ad65SAlexandre Courbot // SPDX-License-Identifier: GPL-2.0 26554ad65SAlexandre Courbot 380213934SAlexandre Courbot use core::ops::Range; 480213934SAlexandre Courbot 56554ad65SAlexandre Courbot use kernel::prelude::*; 680213934SAlexandre Courbot use kernel::sizes::*; 76554ad65SAlexandre Courbot use kernel::types::ARef; 86554ad65SAlexandre Courbot use kernel::{dev_warn, device}; 96554ad65SAlexandre Courbot 106554ad65SAlexandre Courbot use crate::dma::DmaObject; 116554ad65SAlexandre Courbot use crate::driver::Bar0; 126554ad65SAlexandre Courbot use crate::gpu::Chipset; 1380213934SAlexandre Courbot use crate::regs; 146554ad65SAlexandre Courbot 156554ad65SAlexandre Courbot mod hal; 166554ad65SAlexandre Courbot 176554ad65SAlexandre Courbot /// Type holding the sysmem flush memory page, a page of memory to be written into the 186554ad65SAlexandre Courbot /// `NV_PFB_NISO_FLUSH_SYSMEM_ADDR*` registers and used to maintain memory coherency. 196554ad65SAlexandre Courbot /// 206554ad65SAlexandre Courbot /// Users are responsible for manually calling [`Self::unregister`] before dropping this object, 216554ad65SAlexandre Courbot /// otherwise the GPU might still use it even after it has been freed. 226554ad65SAlexandre Courbot pub(crate) struct SysmemFlush { 236554ad65SAlexandre Courbot /// Chipset we are operating on. 246554ad65SAlexandre Courbot chipset: Chipset, 256554ad65SAlexandre Courbot device: ARef<device::Device>, 266554ad65SAlexandre Courbot /// Keep the page alive as long as we need it. 276554ad65SAlexandre Courbot page: DmaObject, 286554ad65SAlexandre Courbot } 296554ad65SAlexandre Courbot 306554ad65SAlexandre Courbot impl SysmemFlush { 316554ad65SAlexandre Courbot /// Allocate a memory page and register it as the sysmem flush page. 326554ad65SAlexandre Courbot pub(crate) fn register( 336554ad65SAlexandre Courbot dev: &device::Device<device::Bound>, 346554ad65SAlexandre Courbot bar: &Bar0, 356554ad65SAlexandre Courbot chipset: Chipset, 366554ad65SAlexandre Courbot ) -> Result<Self> { 376554ad65SAlexandre Courbot let page = DmaObject::new(dev, kernel::page::PAGE_SIZE)?; 386554ad65SAlexandre Courbot 396554ad65SAlexandre Courbot hal::fb_hal(chipset).write_sysmem_flush_page(bar, page.dma_handle())?; 406554ad65SAlexandre Courbot 416554ad65SAlexandre Courbot Ok(Self { 426554ad65SAlexandre Courbot chipset, 436554ad65SAlexandre Courbot device: dev.into(), 446554ad65SAlexandre Courbot page, 456554ad65SAlexandre Courbot }) 466554ad65SAlexandre Courbot } 476554ad65SAlexandre Courbot 486554ad65SAlexandre Courbot /// Unregister the managed sysmem flush page. 496554ad65SAlexandre Courbot /// 506554ad65SAlexandre Courbot /// In order to gracefully tear down the GPU, users must make sure to call this method before 516554ad65SAlexandre Courbot /// dropping the object. 526554ad65SAlexandre Courbot pub(crate) fn unregister(&self, bar: &Bar0) { 536554ad65SAlexandre Courbot let hal = hal::fb_hal(self.chipset); 546554ad65SAlexandre Courbot 556554ad65SAlexandre Courbot if hal.read_sysmem_flush_page(bar) == self.page.dma_handle() { 566554ad65SAlexandre Courbot let _ = hal.write_sysmem_flush_page(bar, 0).inspect_err(|e| { 576554ad65SAlexandre Courbot dev_warn!( 586554ad65SAlexandre Courbot &self.device, 596554ad65SAlexandre Courbot "failed to unregister sysmem flush page: {:?}", 606554ad65SAlexandre Courbot e 616554ad65SAlexandre Courbot ) 626554ad65SAlexandre Courbot }); 636554ad65SAlexandre Courbot } else { 646554ad65SAlexandre Courbot // Another page has been registered after us for some reason - warn as this is a bug. 656554ad65SAlexandre Courbot dev_warn!( 666554ad65SAlexandre Courbot &self.device, 676554ad65SAlexandre Courbot "attempt to unregister a sysmem flush page that is not active\n" 686554ad65SAlexandre Courbot ); 696554ad65SAlexandre Courbot } 706554ad65SAlexandre Courbot } 716554ad65SAlexandre Courbot } 7280213934SAlexandre Courbot 7380213934SAlexandre Courbot /// Layout of the GPU framebuffer memory. 7480213934SAlexandre Courbot /// 7580213934SAlexandre Courbot /// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process. 7680213934SAlexandre Courbot #[derive(Debug)] 7780213934SAlexandre Courbot #[expect(dead_code)] 7880213934SAlexandre Courbot pub(crate) struct FbLayout { 7980213934SAlexandre Courbot pub(crate) fb: Range<u64>, 8080213934SAlexandre Courbot pub(crate) vga_workspace: Range<u64>, 8180213934SAlexandre Courbot pub(crate) frts: Range<u64>, 8280213934SAlexandre Courbot } 8380213934SAlexandre Courbot 8480213934SAlexandre Courbot impl FbLayout { 8580213934SAlexandre Courbot /// Computes the FB layout. 8680213934SAlexandre Courbot pub(crate) fn new(chipset: Chipset, bar: &Bar0) -> Result<Self> { 8780213934SAlexandre Courbot let hal = hal::fb_hal(chipset); 8880213934SAlexandre Courbot 8980213934SAlexandre Courbot let fb = { 9080213934SAlexandre Courbot let fb_size = hal.vidmem_size(bar); 9180213934SAlexandre Courbot 9280213934SAlexandre Courbot 0..fb_size 9380213934SAlexandre Courbot }; 9480213934SAlexandre Courbot 9580213934SAlexandre Courbot let vga_workspace = { 9680213934SAlexandre Courbot let vga_base = { 9780213934SAlexandre Courbot const NV_PRAMIN_SIZE: u64 = SZ_1M as u64; 9880213934SAlexandre Courbot let base = fb.end - NV_PRAMIN_SIZE; 9980213934SAlexandre Courbot 10080213934SAlexandre Courbot if hal.supports_display(bar) { 10180213934SAlexandre Courbot match regs::NV_PDISP_VGA_WORKSPACE_BASE::read(bar).vga_workspace_addr() { 10280213934SAlexandre Courbot Some(addr) => { 10380213934SAlexandre Courbot if addr < base { 10480213934SAlexandre Courbot const VBIOS_WORKSPACE_SIZE: u64 = SZ_128K as u64; 10580213934SAlexandre Courbot 10680213934SAlexandre Courbot // Point workspace address to end of framebuffer. 10780213934SAlexandre Courbot fb.end - VBIOS_WORKSPACE_SIZE 10880213934SAlexandre Courbot } else { 10980213934SAlexandre Courbot addr 11080213934SAlexandre Courbot } 11180213934SAlexandre Courbot } 11280213934SAlexandre Courbot None => base, 11380213934SAlexandre Courbot } 11480213934SAlexandre Courbot } else { 11580213934SAlexandre Courbot base 11680213934SAlexandre Courbot } 11780213934SAlexandre Courbot }; 11880213934SAlexandre Courbot 11980213934SAlexandre Courbot vga_base..fb.end 12080213934SAlexandre Courbot }; 12180213934SAlexandre Courbot 12280213934SAlexandre Courbot let frts = { 12380213934SAlexandre Courbot const FRTS_DOWN_ALIGN: u64 = SZ_128K as u64; 12480213934SAlexandre Courbot const FRTS_SIZE: u64 = SZ_1M as u64; 125*3606620bSAlexandre Courbot // TODO[NUMM]: replace with `align_down` once it lands. 12680213934SAlexandre Courbot let frts_base = (vga_workspace.start & !(FRTS_DOWN_ALIGN - 1)) - FRTS_SIZE; 12780213934SAlexandre Courbot 12880213934SAlexandre Courbot frts_base..frts_base + FRTS_SIZE 12980213934SAlexandre Courbot }; 13080213934SAlexandre Courbot 13180213934SAlexandre Courbot Ok(Self { 13280213934SAlexandre Courbot fb, 13380213934SAlexandre Courbot vga_workspace, 13480213934SAlexandre Courbot frts, 13580213934SAlexandre Courbot }) 13680213934SAlexandre Courbot } 13780213934SAlexandre Courbot } 138