Merge branch 'gpu_drm15' into 'main'

drivers/graphics: Add framebuffer indirection to the DRM interface

See merge request redox-os/base!154
This commit is contained in:
Jeremy Soller
2026-03-11 18:05:43 -06:00
6 changed files with 126 additions and 82 deletions
+11 -7
View File
@@ -6,27 +6,31 @@ use std::{cmp, io, mem, ptr};
use drm::buffer::{Buffer, DrmFourcc};
use drm::control::dumbbuffer::{DumbBuffer, DumbMapping};
use drm::control::Device;
use drm::control::{framebuffer, Device};
use graphics_ipc::v1::Damage;
use graphics_ipc::v2::V2GraphicsHandle;
use orbclient::FONT;
pub struct V2DisplayMap {
pub display_handle: V2GraphicsHandle,
pub fb: DumbBuffer,
pub fb: framebuffer::Handle,
pub buffer: DumbBuffer,
mapping: DumbMapping<'static>,
}
impl V2DisplayMap {
pub fn new(display_handle: V2GraphicsHandle, width: u32, height: u32) -> io::Result<Self> {
let mut fb = display_handle.create_dumb_buffer((width, height), DrmFourcc::Argb8888, 32)?;
let mut buffer =
display_handle.create_dumb_buffer((width, height), DrmFourcc::Argb8888, 32)?;
let fb = display_handle.add_framebuffer(&buffer, 24, 32)?;
let map = display_handle.map_dumb_buffer(&mut fb)?;
let map = display_handle.map_dumb_buffer(&mut buffer)?;
let map = unsafe { mem::transmute::<DumbMapping<'_>, DumbMapping<'static>>(map) };
Ok(Self {
display_handle,
fb,
buffer,
mapping: map,
})
}
@@ -37,8 +41,8 @@ impl V2DisplayMap {
self.mapping.as_mut_ptr() as *mut u32,
self.mapping.len() / 4,
),
width: self.fb.size().0 as usize,
height: self.fb.size().1 as usize,
width: self.buffer.size().0 as usize,
height: self.buffer.size().1 as usize,
}
}
}
@@ -324,7 +328,7 @@ impl TextScreen {
}
}
let old_fb = mem::replace(&mut map.fb, new_fb);
let old_fb = mem::replace(&mut map.buffer, new_fb);
map.mapping = new_mapping;
let _ = map.display_handle.destroy_dumb_buffer(old_fb);
+87 -49
View File
@@ -349,6 +349,67 @@ impl<T: GraphicsAdapter> GraphicsSchemeInner<T> {
},
);
}
fn handle_cursor_update(
&mut self,
vt: usize,
cursor_damage: &graphics_ipc::v2::ipc::UpdateCursor,
) -> Result<()> {
let vt_state = self.vts.get_mut(&vt).unwrap();
let Some(cursor_plane) = &mut vt_state.cursor_plane else {
// Hardware cursor not supported
return Err(Error::new(EINVAL));
};
cursor_plane.x = cursor_damage.x;
cursor_plane.y = cursor_damage.y;
if cursor_damage.header == 0 {
if vt != self.active_vt {
return Ok(());
}
self.adapter.handle_cursor(cursor_plane, false);
} else {
cursor_plane.hot_x = cursor_damage.hot_x;
cursor_plane.hot_y = cursor_damage.hot_y;
let w: i32 = cursor_damage.width;
let h: i32 = cursor_damage.height;
let cursor_image = cursor_damage.cursor_img_bytes;
let cursor_ptr = self
.adapter
.map_cursor_framebuffer(&cursor_plane.framebuffer);
//Clear previous image from backing storage
unsafe {
core::ptr::write_bytes(cursor_ptr as *mut u8, 0, 64 * 64 * 4);
}
//Write image to backing storage
for row in 0..h {
let start: usize = (w * row) as usize;
let end: usize = (w * row + w) as usize;
unsafe {
core::ptr::copy_nonoverlapping(
cursor_image[start..end].as_ptr(),
cursor_ptr.cast::<u32>().offset(64 * row as isize),
w as usize,
);
}
}
if vt != self.active_vt {
return Ok(());
}
self.adapter.handle_cursor(cursor_plane, true);
}
return Ok(());
}
}
const MAP_FAKE_OFFSET_MULTIPLIER: usize = 0x10_000_000;
@@ -495,6 +556,14 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsSchemeInner<T> {
) -> Result<usize> {
match self.handles.get(&id).ok_or(Error::new(EBADF))? {
Handle::V1Screen { vt, screen } => {
if size_of_val(buf) == std::mem::size_of::<CursorDamage>() {
let cursor_damage = unsafe { &*buf.as_ptr().cast::<CursorDamage>() };
self.handle_cursor_update(*vt, cursor_damage)?;
return Ok(buf.len());
}
if *vt != self.active_vt {
// This is a protection against background VT's spamming us with flush requests. We will
// flush the framebuffer on the next VT switch anyway
@@ -503,55 +572,6 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsSchemeInner<T> {
let vt_state = self.vts.get_mut(vt).unwrap();
if size_of_val(buf) == std::mem::size_of::<CursorDamage>() {
let Some(cursor_plane) = &mut vt_state.cursor_plane else {
// Hardware cursor not supported
return Err(Error::new(EINVAL));
};
let cursor_damage = unsafe { *buf.as_ptr().cast::<CursorDamage>() };
cursor_plane.x = cursor_damage.x;
cursor_plane.y = cursor_damage.y;
if cursor_damage.header == 0 {
self.adapter.handle_cursor(cursor_plane, false);
} else {
cursor_plane.hot_x = cursor_damage.hot_x;
cursor_plane.hot_y = cursor_damage.hot_y;
let w: i32 = cursor_damage.width;
let h: i32 = cursor_damage.height;
let cursor_image = cursor_damage.cursor_img_bytes;
let cursor_ptr = self
.adapter
.map_cursor_framebuffer(&cursor_plane.framebuffer);
//Clear previous image from backing storage
unsafe {
core::ptr::write_bytes(cursor_ptr as *mut u8, 0, 64 * 64 * 4);
}
//Write image to backing storage
for row in 0..h {
let start: usize = (w * row) as usize;
let end: usize = (w * row + w) as usize;
unsafe {
core::ptr::copy_nonoverlapping(
cursor_image[start..end].as_ptr(),
cursor_ptr.cast::<u32>().offset(64 * row as isize),
w as usize,
);
}
}
self.adapter.handle_cursor(cursor_plane, true);
}
return Ok(buf.len());
}
assert_eq!(buf.len(), std::mem::size_of::<Damage>());
let damage = unsafe { *buf.as_ptr().cast::<Damage>() };
@@ -810,6 +830,10 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsSchemeInner<T> {
data.set_handle(fb_handle_id(i));
Ok(0)
}),
ipc::MODE_ADD_FB => ipc::DrmModeFbCmd::with(payload, |mut data| {
data.set_fb_id(fb_handle_id(id_index(data.handle())));
Ok(0)
}),
ipc::MODE_CREATE_DUMB => ipc::DrmModeCreateDumb::with(payload, |mut data| {
if data.bpp() != 32 {
return Err(Error::new(EINVAL));
@@ -931,6 +955,20 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsSchemeInner<T> {
Ok(size_of::<ipc::UpdatePlane>())
}
ipc::UPDATE_CURSOR => {
if payload.len() < size_of::<ipc::UpdateCursor>() {
return Err(Error::new(EINVAL));
}
let payload = unsafe {
transmute::<&mut [u8; size_of::<ipc::UpdateCursor>()], &mut ipc::UpdateCursor>(
payload.as_mut_array().unwrap(),
)
};
let vt = *vt;
self.handle_cursor_update(vt, payload)?;
Ok(size_of::<ipc::UpdateCursor>())
}
_ => return Err(Error::new(EINVAL)),
},
}
+7 -7
View File
@@ -122,7 +122,7 @@ impl FbbootlogScheme {
self.is_scrollback = true;
self.scrollback_offset = cmp::min(
self.scrollback_offset,
buffer_len - map.fb.size().1 as usize / 16 + spare_lines,
buffer_len - map.buffer.size().1 as usize / 16 + spare_lines,
);
let mut i = self.scrollback_offset;
self.text_screen
@@ -135,9 +135,9 @@ impl FbbootlogScheme {
.write(map, &self.text_buffer.lines[i][..], &mut VecDeque::new());
i += 1;
let yd = (damage.y + damage.height) as usize;
if i == buffer_len || yd + spare_lines * 16 > map.fb.size().1 as usize {
if i == buffer_len || yd + spare_lines * 16 > map.buffer.size().1 as usize {
// render until end of screen
damage.height = map.fb.size().1 - damage.y;
damage.height = map.buffer.size().1 - damage.y;
total_damage = total_damage.merge(damage);
self.is_scrollback = i < buffer_len;
break;
@@ -146,7 +146,7 @@ impl FbbootlogScheme {
}
}
map.display_handle
.update_plane(0, u32::from(map.fb.handle()), total_damage)
.update_plane(0, u32::from(map.buffer.handle()), total_damage)
.unwrap();
}
@@ -157,11 +157,11 @@ impl FbbootlogScheme {
Ok((width, height)) => (width.into(), height.into()),
Err(err) => {
eprintln!("fbbootlogd: failed to get display size: {}", err);
map.fb.size()
map.buffer.size()
}
};
if (width, height) != map.fb.size() {
if (width, height) != map.buffer.size() {
match text_screen.resize(map, width, height) {
Ok(()) => eprintln!("fbbootlogd: mapped display"),
Err(err) => {
@@ -248,7 +248,7 @@ impl SchemeSync for FbbootlogScheme {
if let Some(map) = &self.display_map {
map.display_handle
.update_plane(0, u32::from(map.fb.handle()), damage)
.update_plane(0, u32::from(map.buffer.handle()), damage)
.unwrap();
}
}
+5 -5
View File
@@ -45,8 +45,8 @@ impl Display {
Ok(map) => {
log::debug!(
"fbcond: Mapped new display with size {}x{}",
map.fb.size().0,
map.fb.size().1,
map.buffer.size().0,
map.buffer.size().1,
);
self.map = Some(map)
}
@@ -64,11 +64,11 @@ impl Display {
Ok((width, height)) => (width.into(), height.into()),
Err(err) => {
log::error!("fbcond: failed to get display size: {}", err);
map.fb.size()
map.buffer.size()
}
};
if (width, height) != map.fb.size() {
if (width, height) != map.buffer.size() {
match text_screen.resize(map, width, height) {
Ok(()) => eprintln!("fbcond: mapped display"),
Err(err) => {
@@ -82,7 +82,7 @@ impl Display {
pub fn sync_rect(&mut self, damage: Damage) {
if let Some(map) = &self.map {
map.display_handle
.update_plane(0, u32::from(map.fb.handle()), damage)
.update_plane(0, u32::from(map.buffer.handle()), damage)
.unwrap();
}
}
+1 -13
View File
@@ -1,14 +1,2 @@
pub use crate::common::Damage;
#[derive(Debug, Copy, Clone)]
#[repr(C, packed)]
pub struct CursorDamage {
pub header: u32,
pub x: i32,
pub y: i32,
pub hot_x: i32,
pub hot_y: i32,
pub width: i32,
pub height: i32,
pub cursor_img_bytes: [u32; 4096],
}
pub use crate::v2::ipc::UpdateCursor as CursorDamage;
+15 -1
View File
@@ -69,11 +69,25 @@ pub mod ipc {
pub use redox_ioctl::drm::*;
// FIXME replace these with proper drm interfaces
pub const UPDATE_PLANE: u64 = 6;
pub const UPDATE_PLANE: u64 = 0x12345670;
#[repr(C, packed)]
pub struct UpdatePlane {
pub display_id: usize,
pub fb_id: u32,
pub damage: Damage,
}
pub const UPDATE_CURSOR: u64 = 0x12345671;
#[derive(Debug, Copy, Clone)]
#[repr(C, packed)]
pub struct UpdateCursor {
pub header: u32,
pub x: i32,
pub y: i32,
pub hot_x: i32,
pub hot_y: i32,
pub width: i32,
pub height: i32,
pub cursor_img_bytes: [u32; 4096],
}
}