Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/nova-core/fb.rs
26426 views
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
/// A system memory page is required for `sysmembar`, which is a GPU-initiated hardware
21
/// memory-barrier operation that flushes all pending GPU-side memory writes that were done through
22
/// PCIE to system memory. It is required for falcons to be reset as the reset operation involves a
23
/// reset handshake. When the falcon acknowledges a reset, it writes into system memory. To ensure
24
/// this write is visible to the host and prevent driver timeouts, the falcon must perform a
25
/// sysmembar operation to flush its writes.
26
///
27
/// Because of this, the sysmem flush memory page must be registered as early as possible during
28
/// driver initialization, and before any falcon is reset.
29
///
30
/// Users are responsible for manually calling [`Self::unregister`] before dropping this object,
31
/// otherwise the GPU might still use it even after it has been freed.
32
pub(crate) struct SysmemFlush {
33
/// Chipset we are operating on.
34
chipset: Chipset,
35
device: ARef<device::Device>,
36
/// Keep the page alive as long as we need it.
37
page: DmaObject,
38
}
39
40
impl SysmemFlush {
41
/// Allocate a memory page and register it as the sysmem flush page.
42
pub(crate) fn register(
43
dev: &device::Device<device::Bound>,
44
bar: &Bar0,
45
chipset: Chipset,
46
) -> Result<Self> {
47
let page = DmaObject::new(dev, kernel::page::PAGE_SIZE)?;
48
49
hal::fb_hal(chipset).write_sysmem_flush_page(bar, page.dma_handle())?;
50
51
Ok(Self {
52
chipset,
53
device: dev.into(),
54
page,
55
})
56
}
57
58
/// Unregister the managed sysmem flush page.
59
///
60
/// In order to gracefully tear down the GPU, users must make sure to call this method before
61
/// dropping the object.
62
pub(crate) fn unregister(&self, bar: &Bar0) {
63
let hal = hal::fb_hal(self.chipset);
64
65
if hal.read_sysmem_flush_page(bar) == self.page.dma_handle() {
66
let _ = hal.write_sysmem_flush_page(bar, 0).inspect_err(|e| {
67
dev_warn!(
68
&self.device,
69
"failed to unregister sysmem flush page: {:?}",
70
e
71
)
72
});
73
} else {
74
// Another page has been registered after us for some reason - warn as this is a bug.
75
dev_warn!(
76
&self.device,
77
"attempt to unregister a sysmem flush page that is not active\n"
78
);
79
}
80
}
81
}
82
83
/// Layout of the GPU framebuffer memory.
84
///
85
/// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process.
86
#[derive(Debug)]
87
#[expect(dead_code)]
88
pub(crate) struct FbLayout {
89
pub(crate) fb: Range<u64>,
90
pub(crate) vga_workspace: Range<u64>,
91
pub(crate) frts: Range<u64>,
92
}
93
94
impl FbLayout {
95
/// Computes the FB layout.
96
pub(crate) fn new(chipset: Chipset, bar: &Bar0) -> Result<Self> {
97
let hal = hal::fb_hal(chipset);
98
99
let fb = {
100
let fb_size = hal.vidmem_size(bar);
101
102
0..fb_size
103
};
104
105
let vga_workspace = {
106
let vga_base = {
107
const NV_PRAMIN_SIZE: u64 = SZ_1M as u64;
108
let base = fb.end - NV_PRAMIN_SIZE;
109
110
if hal.supports_display(bar) {
111
match regs::NV_PDISP_VGA_WORKSPACE_BASE::read(bar).vga_workspace_addr() {
112
Some(addr) => {
113
if addr < base {
114
const VBIOS_WORKSPACE_SIZE: u64 = SZ_128K as u64;
115
116
// Point workspace address to end of framebuffer.
117
fb.end - VBIOS_WORKSPACE_SIZE
118
} else {
119
addr
120
}
121
}
122
None => base,
123
}
124
} else {
125
base
126
}
127
};
128
129
vga_base..fb.end
130
};
131
132
let frts = {
133
const FRTS_DOWN_ALIGN: u64 = SZ_128K as u64;
134
const FRTS_SIZE: u64 = SZ_1M as u64;
135
// TODO[NUMM]: replace with `align_down` once it lands.
136
let frts_base = (vga_workspace.start & !(FRTS_DOWN_ALIGN - 1)) - FRTS_SIZE;
137
138
frts_base..frts_base + FRTS_SIZE
139
};
140
141
Ok(Self {
142
fb,
143
vga_workspace,
144
frts,
145
})
146
}
147
}
148
149