16554ad65SAlexandre Courbot // SPDX-License-Identifier: GPL-2.0 26554ad65SAlexandre Courbot 3*80213934SAlexandre Courbot use core::ops::Range; 4*80213934SAlexandre Courbot 56554ad65SAlexandre Courbot use kernel::prelude::*; 6*80213934SAlexandre 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; 13*80213934SAlexandre 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 } 72*80213934SAlexandre Courbot 73*80213934SAlexandre Courbot /// Layout of the GPU framebuffer memory. 74*80213934SAlexandre Courbot /// 75*80213934SAlexandre Courbot /// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process. 76*80213934SAlexandre Courbot #[derive(Debug)] 77*80213934SAlexandre Courbot #[expect(dead_code)] 78*80213934SAlexandre Courbot pub(crate) struct FbLayout { 79*80213934SAlexandre Courbot pub(crate) fb: Range<u64>, 80*80213934SAlexandre Courbot pub(crate) vga_workspace: Range<u64>, 81*80213934SAlexandre Courbot pub(crate) frts: Range<u64>, 82*80213934SAlexandre Courbot } 83*80213934SAlexandre Courbot 84*80213934SAlexandre Courbot impl FbLayout { 85*80213934SAlexandre Courbot /// Computes the FB layout. 86*80213934SAlexandre Courbot pub(crate) fn new(chipset: Chipset, bar: &Bar0) -> Result<Self> { 87*80213934SAlexandre Courbot let hal = hal::fb_hal(chipset); 88*80213934SAlexandre Courbot 89*80213934SAlexandre Courbot let fb = { 90*80213934SAlexandre Courbot let fb_size = hal.vidmem_size(bar); 91*80213934SAlexandre Courbot 92*80213934SAlexandre Courbot 0..fb_size 93*80213934SAlexandre Courbot }; 94*80213934SAlexandre Courbot 95*80213934SAlexandre Courbot let vga_workspace = { 96*80213934SAlexandre Courbot let vga_base = { 97*80213934SAlexandre Courbot const NV_PRAMIN_SIZE: u64 = SZ_1M as u64; 98*80213934SAlexandre Courbot let base = fb.end - NV_PRAMIN_SIZE; 99*80213934SAlexandre Courbot 100*80213934SAlexandre Courbot if hal.supports_display(bar) { 101*80213934SAlexandre Courbot match regs::NV_PDISP_VGA_WORKSPACE_BASE::read(bar).vga_workspace_addr() { 102*80213934SAlexandre Courbot Some(addr) => { 103*80213934SAlexandre Courbot if addr < base { 104*80213934SAlexandre Courbot const VBIOS_WORKSPACE_SIZE: u64 = SZ_128K as u64; 105*80213934SAlexandre Courbot 106*80213934SAlexandre Courbot // Point workspace address to end of framebuffer. 107*80213934SAlexandre Courbot fb.end - VBIOS_WORKSPACE_SIZE 108*80213934SAlexandre Courbot } else { 109*80213934SAlexandre Courbot addr 110*80213934SAlexandre Courbot } 111*80213934SAlexandre Courbot } 112*80213934SAlexandre Courbot None => base, 113*80213934SAlexandre Courbot } 114*80213934SAlexandre Courbot } else { 115*80213934SAlexandre Courbot base 116*80213934SAlexandre Courbot } 117*80213934SAlexandre Courbot }; 118*80213934SAlexandre Courbot 119*80213934SAlexandre Courbot vga_base..fb.end 120*80213934SAlexandre Courbot }; 121*80213934SAlexandre Courbot 122*80213934SAlexandre Courbot let frts = { 123*80213934SAlexandre Courbot const FRTS_DOWN_ALIGN: u64 = SZ_128K as u64; 124*80213934SAlexandre Courbot const FRTS_SIZE: u64 = SZ_1M as u64; 125*80213934SAlexandre Courbot // TODO: replace with `align_down` once it lands. 126*80213934SAlexandre Courbot let frts_base = (vga_workspace.start & !(FRTS_DOWN_ALIGN - 1)) - FRTS_SIZE; 127*80213934SAlexandre Courbot 128*80213934SAlexandre Courbot frts_base..frts_base + FRTS_SIZE 129*80213934SAlexandre Courbot }; 130*80213934SAlexandre Courbot 131*80213934SAlexandre Courbot Ok(Self { 132*80213934SAlexandre Courbot fb, 133*80213934SAlexandre Courbot vga_workspace, 134*80213934SAlexandre Courbot frts, 135*80213934SAlexandre Courbot }) 136*80213934SAlexandre Courbot } 137*80213934SAlexandre Courbot } 138