drivers/graphics/ihdgd: Implement support for mapping memory to the GPU
This is done by adding entries to the global GTT page table. At startup it only contains entries for a small chunk of memory reserved for the GPU. This small chunk is rarely enough to contain all framebuffers we need to allocate.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
use std::{ptr, slice};
|
||||
use std::{mem, ptr, slice};
|
||||
|
||||
use range_alloc::RangeAllocator;
|
||||
use syscall::{Error, EIO};
|
||||
use common::dma::Dma;
|
||||
|
||||
use crate::device::ggtt::GlobalGtt;
|
||||
use crate::device::MmioRegion;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -28,35 +28,21 @@ impl GpuBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alloc(
|
||||
gm: &MmioRegion,
|
||||
alloc_surfaces: &mut RangeAllocator<u32>,
|
||||
size: u32,
|
||||
) -> syscall::Result<Self> {
|
||||
let surf_size = size.next_multiple_of(4096);
|
||||
let gm_offset = alloc_surfaces
|
||||
.allocate_range(surf_size)
|
||||
.map_err(|err| {
|
||||
log::warn!("failed to allocate buffer of size {}: {:?}", surf_size, err);
|
||||
Error::new(EIO)
|
||||
})?
|
||||
.start;
|
||||
pub fn alloc(gm: &MmioRegion, ggtt: &mut GlobalGtt, size: u32) -> syscall::Result<Self> {
|
||||
let gm_offset = ggtt.alloc_phys_mem(size)?;
|
||||
|
||||
Ok(unsafe { GpuBuffer::new(gm, gm_offset, size, true) })
|
||||
}
|
||||
|
||||
pub fn alloc_dumb(
|
||||
gm: &MmioRegion,
|
||||
alloc_surfaces: &mut RangeAllocator<u32>,
|
||||
ggtt: &mut GlobalGtt,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> syscall::Result<(Self, u32)> {
|
||||
//TODO: documentation on this is not great
|
||||
let stride = (width * 4).next_multiple_of(64);
|
||||
|
||||
Ok((
|
||||
GpuBuffer::alloc(gm, alloc_surfaces, stride * height)?,
|
||||
stride,
|
||||
))
|
||||
Ok((GpuBuffer::alloc(gm, ggtt, stride * height)?, stride))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
use std::sync::Arc;
|
||||
use std::{mem, ptr};
|
||||
|
||||
use pcid_interface::PciFunctionHandle;
|
||||
use range_alloc::RangeAllocator;
|
||||
use syscall::{Error, EIO};
|
||||
|
||||
use crate::device::MmioRegion;
|
||||
|
||||
/// Global Graphics Translation Table (global GTT)
|
||||
///
|
||||
/// The global GTT is a page table used by all parts of the GPU that don't use
|
||||
/// the PPGTT (Per-Process GTT). This includes the display engine and the GM
|
||||
/// aperture that the CPU can access.
|
||||
///
|
||||
/// The global GTT is located in the GTTMM BAR at offset 8MiB, is up to 8MiB big
|
||||
/// and consists of 64bit entries. Each entry has a present bit as LSB and the
|
||||
/// address of the frame at bits 12 through 38. The rest of the bits are ignored.
|
||||
///
|
||||
/// Source: Pages 6 and 75 of intel-gfx-prm-osrc-kbl-vol05-memory_views.pdf
|
||||
pub struct GlobalGtt {
|
||||
gttmm: Arc<MmioRegion>,
|
||||
/// Base the GTT
|
||||
gtt_base: *mut u64,
|
||||
/// Size of the GTT
|
||||
gtt_size: usize,
|
||||
|
||||
/// Allocator for GM aperture pages
|
||||
gm_alloc: RangeAllocator<u32>,
|
||||
|
||||
// FIXME reuse DSM memory for something useful
|
||||
/// Base Data of Stolen Memory (DSM)
|
||||
base_dsm: *mut (),
|
||||
/// Size of DSM
|
||||
size_data_stolen_memory: usize,
|
||||
}
|
||||
|
||||
const GTT_PAGE_SIZE: u32 = 4096;
|
||||
|
||||
impl GlobalGtt {
|
||||
pub unsafe fn new(
|
||||
pcid_handle: &mut PciFunctionHandle,
|
||||
gttmm: Arc<MmioRegion>,
|
||||
gm_size: u32,
|
||||
) -> Self {
|
||||
let gtt_offset = 8 * 1024 * 1024;
|
||||
let gtt_base = ptr::with_exposed_provenance_mut(gttmm.virt + gtt_offset);
|
||||
|
||||
let base_dsm = unsafe { pcid_handle.read_config(0x5C) };
|
||||
let ggc = unsafe { pcid_handle.read_config(0x50) };
|
||||
|
||||
let dsm_size = match (ggc >> 8) & 0xFF {
|
||||
size if size & 0xF0 == 0 => size * 32 * 1024 * 1024,
|
||||
size => (size & !0xF0) * 4 * 1024 * 1024,
|
||||
} as usize;
|
||||
let gtt_size = match (ggc >> 6) & 0x3 {
|
||||
0 => 0,
|
||||
1 => 2 * 1024 * 1024,
|
||||
2 => 4 * 1024 * 1024,
|
||||
3 => 8 * 1024 * 1024,
|
||||
_ => unreachable!(),
|
||||
} as usize;
|
||||
|
||||
log::info!("Base DSM: {:X}", base_dsm);
|
||||
log::info!(
|
||||
"GGC: {:X} => global GTT size: {}MiB; DSM size: {}MiB",
|
||||
ggc,
|
||||
gtt_size / 1024 / 1024,
|
||||
dsm_size / 1024 / 1024,
|
||||
);
|
||||
|
||||
let gm_alloc = RangeAllocator::new(0..gm_size / 4096);
|
||||
|
||||
GlobalGtt {
|
||||
gttmm,
|
||||
gtt_base,
|
||||
gtt_size,
|
||||
gm_alloc,
|
||||
base_dsm: core::ptr::with_exposed_provenance_mut(base_dsm as usize),
|
||||
size_data_stolen_memory: dsm_size,
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset the global GTT by clearing out all existing mappings.
|
||||
pub unsafe fn reset(&mut self) {
|
||||
for i in 0..self.gtt_size / 8 {
|
||||
unsafe { *self.gtt_base.add(i) = 0 };
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reserve(&mut self, surf: u32, surf_size: u32) {
|
||||
assert!(surf.is_multiple_of(GTT_PAGE_SIZE));
|
||||
assert!(surf_size.is_multiple_of(GTT_PAGE_SIZE));
|
||||
|
||||
self.gm_alloc
|
||||
.allocate_exact_range(
|
||||
surf / GTT_PAGE_SIZE..surf / GTT_PAGE_SIZE + surf_size / GTT_PAGE_SIZE,
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"failed to allocate pre-existing surface at 0x{:x} of size {}: {:?}",
|
||||
surf, surf_size, err
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn alloc_phys_mem(&mut self, size: u32) -> syscall::Result<u32> {
|
||||
let size = size.next_multiple_of(GTT_PAGE_SIZE);
|
||||
|
||||
let sgl = common::sgl::Sgl::new(size as usize)?;
|
||||
|
||||
let range = self
|
||||
.gm_alloc
|
||||
.allocate_range(size / GTT_PAGE_SIZE)
|
||||
.map_err(|err| {
|
||||
log::warn!("failed to allocate buffer of size {}: {:?}", size, err);
|
||||
Error::new(EIO)
|
||||
})?;
|
||||
|
||||
for chunk in sgl.chunks() {
|
||||
for i in 0..chunk.length / GTT_PAGE_SIZE as usize {
|
||||
unsafe {
|
||||
*self
|
||||
.gtt_base
|
||||
.add(range.start as usize + chunk.offset / GTT_PAGE_SIZE as usize + i) =
|
||||
chunk.phys as u64 + i as u64 * u64::from(GTT_PAGE_SIZE) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
mem::forget(sgl);
|
||||
|
||||
Ok(range.start * 4096)
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ mod gmbus;
|
||||
pub use self::gmbus::*;
|
||||
mod gpio;
|
||||
pub use self::gpio::*;
|
||||
mod ggtt;
|
||||
use ggtt::*;
|
||||
mod hal;
|
||||
pub use self::hal::*;
|
||||
mod pipe;
|
||||
@@ -153,7 +155,6 @@ enum VideoInput {
|
||||
pub struct Device {
|
||||
kind: DeviceKind,
|
||||
alloc_buffers: RangeAllocator<u32>,
|
||||
alloc_surfaces: RangeAllocator<u32>,
|
||||
bios: Option<Bios>,
|
||||
ddis: Vec<Ddi>,
|
||||
dpclka_cfgcr0: Option<MmioPtr<u32>>,
|
||||
@@ -162,6 +163,7 @@ pub struct Device {
|
||||
framebuffers: Vec<DeviceFb>,
|
||||
int: Interrupter,
|
||||
gttmm: Arc<MmioRegion>,
|
||||
ggtt: GlobalGtt,
|
||||
gm: MmioRegion,
|
||||
gmbus: Gmbus,
|
||||
pipes: Vec<Pipe>,
|
||||
@@ -175,7 +177,6 @@ impl fmt::Debug for Device {
|
||||
f.debug_struct("Device")
|
||||
.field("kind", &self.kind)
|
||||
.field("alloc_buffers", &self.alloc_buffers)
|
||||
.field("alloc_surfaces", &self.alloc_surfaces)
|
||||
.field("gttmm", &self.gttmm)
|
||||
.field("gm", &self.gm)
|
||||
.field("ref_freq", &self.ref_freq)
|
||||
@@ -290,6 +291,16 @@ impl Device {
|
||||
None
|
||||
};
|
||||
|
||||
let ggtt = unsafe {
|
||||
GlobalGtt::new(
|
||||
pcid_handle,
|
||||
gttmm.clone(),
|
||||
//TODO: how to use 64-bit surface addresses?
|
||||
gm.size.min(u32::MAX as usize) as u32,
|
||||
)
|
||||
};
|
||||
//unsafe { ggtt.reset() };
|
||||
|
||||
// GMBUS seems to be stable for all generations
|
||||
let gmbus = unsafe { Gmbus::new(>tmm)? };
|
||||
|
||||
@@ -402,12 +413,9 @@ impl Device {
|
||||
|
||||
//TODO: get number of available buffers
|
||||
let buffers = 1024;
|
||||
//TODO: how to use 64-bit surface addresses?
|
||||
let surface_memory = gm.size.min(u32::max_value() as usize) as u32;
|
||||
Ok(Self {
|
||||
kind,
|
||||
alloc_buffers: RangeAllocator::new(0..buffers),
|
||||
alloc_surfaces: RangeAllocator::new(0..surface_memory),
|
||||
bios,
|
||||
ddis,
|
||||
dpclka_cfgcr0,
|
||||
@@ -416,6 +424,7 @@ impl Device {
|
||||
framebuffers: Vec::new(),
|
||||
int,
|
||||
gttmm,
|
||||
ggtt,
|
||||
gm,
|
||||
gmbus,
|
||||
pipes,
|
||||
@@ -428,7 +437,6 @@ impl Device {
|
||||
pub fn init_inner(&mut self) {
|
||||
// Discover current framebuffers
|
||||
self.alloc_buffers.reset();
|
||||
self.alloc_surfaces.reset();
|
||||
self.framebuffers.clear();
|
||||
for pipe in self.pipes.iter() {
|
||||
for plane in pipe.planes.iter() {
|
||||
@@ -436,7 +444,7 @@ impl Device {
|
||||
plane.fetch_modeset(&mut self.alloc_buffers);
|
||||
|
||||
self.framebuffers
|
||||
.push(plane.fetch_framebuffer(&self.gm, &mut self.alloc_surfaces));
|
||||
.push(plane.fetch_framebuffer(&self.gm, &mut self.ggtt));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -720,7 +728,7 @@ impl Device {
|
||||
let width = timing.horizontal_active_pixels as u32;
|
||||
let height = timing.vertical_active_lines as u32;
|
||||
|
||||
let fb = DeviceFb::alloc(&self.gm, &mut self.alloc_surfaces, width, height)?;
|
||||
let fb = DeviceFb::alloc(&self.gm, &mut self.ggtt, width, height)?;
|
||||
|
||||
plane.modeset(&mut self.alloc_buffers)?;
|
||||
plane.set_framebuffer(&fb);
|
||||
|
||||
@@ -4,7 +4,7 @@ use syscall::error::Result;
|
||||
use syscall::{Error, EIO};
|
||||
|
||||
use super::buffer::GpuBuffer;
|
||||
use super::MmioRegion;
|
||||
use super::{GlobalGtt, MmioRegion};
|
||||
|
||||
pub const PLANE_CTL_ENABLE: u32 = 1 << 31;
|
||||
|
||||
@@ -38,11 +38,11 @@ impl DeviceFb {
|
||||
|
||||
pub fn alloc(
|
||||
gm: &MmioRegion,
|
||||
alloc_surfaces: &mut RangeAllocator<u32>,
|
||||
ggtt: &mut GlobalGtt,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> syscall::Result<Self> {
|
||||
let (buffer, stride) = GpuBuffer::alloc_dumb(gm, alloc_surfaces, width, height)?;
|
||||
let (buffer, stride) = GpuBuffer::alloc_dumb(gm, ggtt, width, height)?;
|
||||
|
||||
Ok(DeviceFb {
|
||||
buffer,
|
||||
@@ -112,11 +112,7 @@ impl Plane {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fetch_framebuffer(
|
||||
&self,
|
||||
gm: &MmioRegion,
|
||||
alloc_surfaces: &mut RangeAllocator<u32>,
|
||||
) -> DeviceFb {
|
||||
pub fn fetch_framebuffer(&self, gm: &MmioRegion, ggtt: &mut GlobalGtt) -> DeviceFb {
|
||||
let size = self.size.read();
|
||||
let width = (size & 0xFFFF) + 1;
|
||||
let height = ((size >> 16) & 0xFFFF) + 1;
|
||||
@@ -126,14 +122,7 @@ impl Plane {
|
||||
let surf = self.surf.read() & 0xFFFFF000;
|
||||
//TODO: read bits per pixel
|
||||
let surf_size = (stride * height).next_multiple_of(4096);
|
||||
alloc_surfaces
|
||||
.allocate_exact_range(surf..(surf + surf_size))
|
||||
.unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"failed to allocate pre-existing surface at 0x{:x} of size {}: {:?}",
|
||||
surf, surf_size, err
|
||||
);
|
||||
});
|
||||
ggtt.reserve(surf, surf_size);
|
||||
|
||||
unsafe { DeviceFb::new(gm, surf, width, height, stride, true) }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user