Merge branch 'gpu_drm45' into 'main'

drivers/graphics/driver-graphics: Implement cursor planes

See merge request redox-os/base!296
This commit is contained in:
Jeremy Soller
2026-07-02 11:55:22 -06:00
8 changed files with 156 additions and 103 deletions
@@ -1,18 +1,25 @@
use std::collections::HashMap;
use std::sync::atomic::AtomicBool;
use drm_fourcc::DrmFourcc;
use drm_sys::{DRM_MODE_CURSOR_BO, DRM_MODE_CURSOR_MOVE};
use syscall::{ENOENT, ENXIO, Error};
use syscall::{EINVAL, ENXIO, Error};
use crate::{DrmHandle, GraphicsAdapter, VtState};
use crate::kms::objects::{KmsFramebuffer, KmsObjectId, KmsObjects, KmsRect};
use crate::{Damage, DrmHandle, GraphicsAdapter, VtState};
pub(super) fn mode_cursor<T: GraphicsAdapter>(
adapter: &mut T,
objects: &mut KmsObjects<T>,
active_vt: usize,
vts: &mut HashMap<usize, VtState<T>>,
handle: &mut DrmHandle<T>,
data: redox_ioctl::drm::DrmModeCursor<'_>,
) -> Result<usize, Error> {
cursor_inner(
adapter,
objects,
active_vt,
vts,
handle,
data.flags(),
@@ -29,12 +36,16 @@ pub(super) fn mode_cursor<T: GraphicsAdapter>(
pub(super) fn mode_cursor2<T: GraphicsAdapter>(
adapter: &mut T,
objects: &mut KmsObjects<T>,
active_vt: usize,
vts: &mut HashMap<usize, VtState<T>>,
handle: &mut DrmHandle<T>,
data: redox_ioctl::drm::DrmModeCursor2<'_>,
) -> Result<usize, Error> {
cursor_inner(
adapter,
objects,
active_vt,
vts,
handle,
data.flags(),
@@ -51,44 +62,84 @@ pub(super) fn mode_cursor2<T: GraphicsAdapter>(
fn cursor_inner<T: GraphicsAdapter>(
adapter: &mut T,
objects: &mut KmsObjects<T>,
active_vt: usize,
vts: &mut HashMap<usize, VtState<T>>,
handle: &mut DrmHandle<T>,
flags: u32,
_crtc_id: u32,
crtc_id: u32,
x: i32,
y: i32,
_width: u32,
_height: u32,
width: u32,
height: u32,
handle_id: u32,
hot_x: i32,
hot_y: i32,
) -> Result<usize, Error> {
let vt_state = vts.get_mut(&handle.vt).unwrap();
let Some(cursor_plane) = &mut vt_state.cursor_plane else {
let crtc_id = KmsObjectId(crtc_id);
let Some(plane) = objects.get_crtc(crtc_id)?.lock().unwrap().cursor_plane else {
return Err(Error::new(ENXIO));
};
let mut new_state = objects.get_plane(plane)?.lock().unwrap().state.clone();
let old_fb_id = new_state.fb_id;
new_state.crtc_id = Some(crtc_id);
let update_buffer = flags & DRM_MODE_CURSOR_BO != 0;
if update_buffer {
cursor_plane.buffer = if handle_id == 0 {
None
} else if let Some(buffer) = handle.buffers.get(&handle_id) {
Some(buffer.clone())
if flags & DRM_MODE_CURSOR_BO != 0 {
if handle_id == 0 {
new_state.fb_id = None;
} else {
return Err(Error::new(ENOENT));
};
cursor_plane.hot_x = hot_x;
cursor_plane.hot_y = hot_y;
let buffer = handle.buffers.get(&handle_id).ok_or(Error::new(EINVAL))?;
let fb = adapter.create_framebuffer(buffer);
let fb_id = objects.add_framebuffer(KmsFramebuffer {
closed: AtomicBool::new(true),
width,
height,
pixel_format: DrmFourcc::Argb8888,
pitch: width * 4,
buffer: buffer.clone(),
driver_data: fb,
});
new_state.fb_id = Some(fb_id);
new_state.src_rect = KmsRect {
x: 0,
y: 0,
width,
height,
};
new_state.crtc_rect.width = width;
new_state.crtc_rect.height = height;
if let Some(hotspot) = &mut new_state.hotspot {
*hotspot = (hot_x, hot_y);
}
}
}
if flags & DRM_MODE_CURSOR_MOVE != 0 {
cursor_plane.x = x;
cursor_plane.y = y;
new_state.crtc_rect.x = x;
new_state.crtc_rect.y = y;
}
adapter.handle_cursor(cursor_plane, update_buffer);
let plane = objects.get_plane(plane).unwrap();
if handle.vt == active_vt {
#[rustfmt::skip]
let damage = if flags & DRM_MODE_CURSOR_BO != 0 {
Damage { x: 0, y: 0, width, height }
} else {
Damage { x: 0, y: 0, width: 0, height: 0 }
};
adapter.set_plane(&objects, plane, new_state.clone(), damage)?;
}
vts.get_mut(&handle.vt).unwrap().plane_state[plane.lock().unwrap().plane_index as usize] =
new_state;
if let Some(old_fb_id) = old_fb_id {
if !VtState::fb_has_any_use(vts, old_fb_id) {
objects.remove_framebuffer_if_closed(old_fb_id);
}
}
Ok(0)
}
@@ -202,7 +202,7 @@ pub(crate) fn call_ioctl<T: GraphicsAdapter>(
Ok(0)
}),
ipc::MODE_CURSOR => ipc::DrmModeCursor::with(payload, |data| {
cursor::mode_cursor(adapter, vts, handle, data)
cursor::mode_cursor(adapter, objects, active_vt, vts, handle, data)
}),
ipc::MODE_GET_ENCODER => ipc::DrmModeGetEncoder::with(payload, |mut data| {
let encoder = objects.get_encoder(KmsObjectId(data.encoder_id()))?;
@@ -345,8 +345,8 @@ pub(crate) fn call_ioctl<T: GraphicsAdapter>(
Damage {
x: 0,
y: 0,
width: 0,
height: 0,
width: data.src_w(),
height: data.src_h(),
},
)?;
}
@@ -380,7 +380,7 @@ pub(crate) fn call_ioctl<T: GraphicsAdapter>(
property::mode_obj_get_properties(objects, data)
}),
ipc::MODE_CURSOR2 => ipc::DrmModeCursor2::with(payload, |data| {
cursor::mode_cursor2(adapter, vts, handle, data)
cursor::mode_cursor2(adapter, objects, active_vt, vts, handle, data)
}),
ipc::MODE_GET_FB2 => ipc::DrmModeFbCmd2::with(payload, |data| {
framebuffer::mode_get_fb2(objects, handle, data)
@@ -88,8 +88,13 @@ impl<T: GraphicsAdapter> KmsObjects<T> {
plane_data: T::Plane,
plane_data_state: <T::Plane as KmsPlaneDriver>::State,
) -> (KmsObjectId, KmsObjectId) {
let primary_plane =
self.add_plane(&[], KmsPlaneType::Primary, plane_data, plane_data_state);
let primary_plane = self.add_plane(
&[],
KmsPlaneType::Primary,
false,
plane_data,
plane_data_state,
);
let crtc_index = self.crtcs.len() as u32;
let id = self.add(Mutex::new(KmsCrtc {
@@ -97,6 +102,7 @@ impl<T: GraphicsAdapter> KmsObjects<T> {
gamma_size: 0,
properties: KmsCrtc::base_properties(),
primary_plane,
cursor_plane: None,
state: KmsCrtcState {
mode: None,
driver_data: driver_data_state,
@@ -132,9 +138,14 @@ impl<T: GraphicsAdapter> KmsObjects<T> {
&mut self,
crtcs: &[KmsObjectId],
plane_type: KmsPlaneType,
has_hotspot: bool,
driver_data: T::Plane,
driver_data_state: <T::Plane as KmsPlaneDriver>::State,
) -> KmsObjectId {
if has_hotspot {
assert_eq!(plane_type, KmsPlaneType::Cursor);
}
let mut possible_crtcs = 0u32;
for &crtc in crtcs {
possible_crtcs |= 1 << self.get_crtc(crtc).unwrap().lock().unwrap().crtc_index
@@ -160,6 +171,7 @@ impl<T: GraphicsAdapter> KmsObjects<T> {
width: 0,
height: 0,
},
hotspot: has_hotspot.then_some((0, 0)),
driver_data: driver_data_state,
},
driver_data,
@@ -297,6 +309,7 @@ pub struct KmsCrtc<T: GraphicsAdapter> {
pub gamma_size: u32,
pub properties: Vec<KmsPropertyData<Self>>,
pub primary_plane: KmsObjectId,
pub cursor_plane: Option<KmsObjectId>,
pub state: KmsCrtcState<T>,
pub driver_data: T::Crtc,
}
@@ -346,6 +359,7 @@ pub struct KmsPlaneState<T: GraphicsAdapter> {
pub crtc_id: Option<KmsObjectId>,
pub src_rect: KmsRect<u32>,
pub crtc_rect: KmsRect<i32>,
pub hotspot: Option<(i32, i32)>,
pub driver_data: <T::Plane as KmsPlaneDriver>::State,
}
@@ -356,6 +370,7 @@ impl<T: GraphicsAdapter> Clone for KmsPlaneState<T> {
crtc_id: self.crtc_id.clone(),
src_rect: self.src_rect.clone(),
crtc_rect: self.crtc_rect.clone(),
hotspot: self.hotspot,
driver_data: self.driver_data.clone(),
}
}
@@ -395,6 +410,7 @@ define_object_props!(object, KmsPlane<T: GraphicsAdapter> {
SRC_H {
get => u64::from(object.state.src_rect.height),
}
// FIXME HOTSPOT_X and HOTSPOT_Y if supported by graphics card
});
#[derive(Copy, Clone, Debug, PartialEq)]
+2 -29
View File
@@ -112,11 +112,6 @@ pub trait GraphicsAdapter: Sized + Debug {
new_plane_state: KmsPlaneState<Self>,
damage: Damage,
) -> syscall::Result<()>;
// FIXME replace this with an actual plane marked as cursor plane once we
// support planes internally.
fn has_cursor_plane(&self) -> bool;
fn handle_cursor(&mut self, cursor: &CursorPlane<Self::Buffer>, dirty_fb: bool);
}
pub trait Buffer: Debug {
@@ -127,14 +122,6 @@ pub trait Framebuffer: Debug {}
impl Framebuffer for () {}
pub struct CursorPlane<C: Buffer> {
pub x: i32,
pub y: i32,
pub hot_x: i32,
pub hot_y: i32,
pub buffer: Option<Arc<C>>,
}
pub struct GraphicsScheme<T: GraphicsAdapter> {
inner: GraphicsSchemeInner<T>,
inputd_handle: DisplayHandle,
@@ -281,7 +268,6 @@ struct VtState<T: GraphicsAdapter> {
connector_state: Vec<KmsConnectorState<T>>,
crtc_state: Vec<KmsCrtcState<T>>,
plane_state: Vec<KmsPlaneState<T>>,
cursor_plane: Option<CursorPlane<T::Buffer>>,
}
impl<T: GraphicsAdapter> VtState<T> {
@@ -313,7 +299,6 @@ struct DrmHandle<T: GraphicsAdapter> {
impl<T: GraphicsAdapter> GraphicsSchemeInner<T> {
fn get_or_create_vt<'a>(
adapter: &T,
objects: &KmsObjects<T>,
vts: &'a mut HashMap<usize, VtState<T>>,
vt: usize,
@@ -331,13 +316,6 @@ impl<T: GraphicsAdapter> GraphicsSchemeInner<T> {
.planes()
.map(|plane| plane.lock().unwrap().state.clone())
.collect(),
cursor_plane: adapter.has_cursor_plane().then(|| CursorPlane {
x: 0,
y: 0,
hot_x: 0,
hot_y: 0,
buffer: None,
}),
})
}
@@ -354,8 +332,7 @@ impl<T: GraphicsAdapter> GraphicsSchemeInner<T> {
self.active_vt = vt;
let vt_state =
GraphicsSchemeInner::get_or_create_vt(&self.adapter, &self.objects, &mut self.vts, vt);
let vt_state = GraphicsSchemeInner::get_or_create_vt(&self.objects, &mut self.vts, vt);
for (connector_idx, connector_state) in vt_state.connector_state.iter().enumerate() {
let connector_id = self.objects.connector_ids()[connector_idx];
@@ -410,10 +387,6 @@ impl<T: GraphicsAdapter> GraphicsSchemeInner<T> {
)
.unwrap();
}
if let Some(cursor_plane) = &vt_state.cursor_plane {
self.adapter.handle_cursor(cursor_plane, true);
}
}
}
@@ -447,7 +420,7 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsSchemeInner<T> {
.map_err(|_| Error::new(EINVAL))?;
// Ensure the VT exists such that the rest of the methods can freely access it.
Self::get_or_create_vt(&self.adapter, &self.objects, &mut self.vts, vt);
Self::get_or_create_vt(&self.objects, &mut self.vts, vt);
Handle::V2(DrmHandle {
vt,
+2
View File
@@ -460,6 +460,8 @@ impl Device {
(),
);
// FIXME add cursor plane
let (width, height) = (fb.width, fb.height);
let fb_id = objects.add_framebuffer(fb);
+1 -9
View File
@@ -5,7 +5,7 @@ use driver_graphics::kms::objects::{
KmsCrtc, KmsCrtcDriver, KmsCrtcState, KmsObjectId, KmsObjects, KmsPlane, KmsPlaneDriver,
KmsPlaneState,
};
use driver_graphics::{Buffer, CursorPlane, Damage, GraphicsAdapter};
use driver_graphics::{Buffer, Damage, GraphicsAdapter};
use drm_sys::{
DRM_CAP_DUMB_BUFFER, DRM_CAP_DUMB_PREFERRED_DEPTH, DRM_CAP_DUMB_PREFER_SHADOW,
DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT,
@@ -135,12 +135,4 @@ impl GraphicsAdapter for Device {
Ok(())
}
fn has_cursor_plane(&self) -> bool {
false
}
fn handle_cursor(&mut self, _cursor: &CursorPlane<Self::Buffer>, _dirty_fb: bool) {
unimplemented!("ihdgd does not support this function");
}
}
+1 -9
View File
@@ -7,7 +7,7 @@ use driver_graphics::kms::connector::{KmsConnectorDriver, KmsConnectorStatus};
use driver_graphics::kms::objects::{
KmsCrtc, KmsCrtcState, KmsObjectId, KmsObjects, KmsPlane, KmsPlaneState,
};
use driver_graphics::{Buffer, CursorPlane, Damage, GraphicsAdapter};
use driver_graphics::{Buffer, Damage, GraphicsAdapter};
use drm_sys::{
DRM_CAP_DUMB_BUFFER, DRM_CAP_DUMB_PREFERRED_DEPTH, DRM_CAP_DUMB_PREFER_SHADOW,
DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT,
@@ -163,14 +163,6 @@ impl GraphicsAdapter for FbAdapter {
Ok(())
}
fn has_cursor_plane(&self) -> bool {
false
}
fn handle_cursor(&mut self, _cursor: &CursorPlane<Self::Buffer>, _dirty_fb: bool) {
unimplemented!("Vesad does not support this function");
}
}
#[derive(Debug)]
+56 -29
View File
@@ -4,9 +4,10 @@ use std::sync::{Arc, Mutex};
use common::{dma::Dma, sgl};
use driver_graphics::kms::connector::{KmsConnectorDriver, KmsConnectorStatus};
use driver_graphics::kms::objects::{
KmsCrtc, KmsCrtcState, KmsObjectId, KmsObjects, KmsPlane, KmsPlaneState,
KmsCrtc, KmsCrtcState, KmsObjectId, KmsObjects, KmsPlane, KmsPlaneDriver, KmsPlaneState,
KmsPlaneType,
};
use driver_graphics::{Buffer as DrmBuffer, CursorPlane, Damage, GraphicsAdapter, GraphicsScheme};
use driver_graphics::{Buffer as DrmBuffer, Damage, GraphicsAdapter, GraphicsScheme};
use drm_sys::{
DRM_CAP_CURSOR_HEIGHT, DRM_CAP_CURSOR_WIDTH, DRM_CAP_DUMB_BUFFER, DRM_CAP_DUMB_PREFERRED_DEPTH,
DRM_CAP_DUMB_PREFER_SHADOW, DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT,
@@ -39,6 +40,15 @@ impl KmsConnectorDriver for VirtGpuConnector {
type State = ();
}
#[derive(Debug)]
pub struct VirtGpuPlane {
is_cursor: bool,
}
impl KmsPlaneDriver for VirtGpuPlane {
type State = ();
}
pub struct VirtGpuFramebuffer<'a> {
queue: Arc<Queue<'a>>,
id: ResourceId,
@@ -343,7 +353,7 @@ impl<'a> VirtGpuAdapter<'a> {
impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> {
type Connector = VirtGpuConnector;
type Crtc = ();
type Plane = ();
type Plane = VirtGpuPlane;
type Buffer = VirtGpuFramebuffer<'a>;
type Framebuffer = ();
@@ -362,7 +372,18 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> {
});
for display_id in 0..self.config.num_scanouts.get() {
let (crtc, _primary_plane_id) = objects.add_crtc((), (), (), ());
let (crtc, _primary_plane_id) =
objects.add_crtc((), (), VirtGpuPlane { is_cursor: false }, ());
let cursor_plane = objects.add_plane(
&[crtc],
KmsPlaneType::Cursor,
true,
VirtGpuPlane { is_cursor: true },
(),
);
objects.get_crtc(crtc).unwrap().lock().unwrap().cursor_plane = Some(cursor_plane);
objects.add_connector(VirtGpuConnector { display_id }, (), &[crtc]);
}
@@ -444,10 +465,6 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> {
damage: Damage,
) -> syscall::Result<()> {
futures::executor::block_on(async {
let Some(crtc_id) = new_plane_state.crtc_id else {
return Ok(());
};
let crtc = objects.get_crtc(crtc_id).unwrap().lock().unwrap();
let mut plane = plane.lock().unwrap();
let framebuffer = new_plane_state
@@ -455,6 +472,37 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> {
.map(|fb_id| objects.get_framebuffer_maybe_closed(fb_id))
.transpose()?;
if plane.driver_data.is_cursor {
if let Some(framebuffer) = framebuffer {
if damage.width != 0 || damage.height != 0 {
self.update_cursor(
&framebuffer.buffer,
new_plane_state.crtc_rect.x,
new_plane_state.crtc_rect.y,
new_plane_state.hotspot.unwrap().0,
new_plane_state.hotspot.unwrap().1,
)
.await;
} else {
self.move_cursor(new_plane_state.crtc_rect.x, new_plane_state.crtc_rect.y)
.await;
}
} else {
if plane.state.fb_id.is_some() {
self.disable_cursor().await;
}
}
plane.state = new_plane_state;
return Ok(());
}
let Some(crtc_id) = new_plane_state.crtc_id else {
return Ok(());
};
let crtc = objects.get_crtc(crtc_id).unwrap().lock().unwrap();
plane.state = new_plane_state;
for connector in objects.connectors() {
@@ -519,27 +567,6 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> {
Ok(())
})
}
fn has_cursor_plane(&self) -> bool {
true
}
fn handle_cursor(&mut self, cursor: &CursorPlane<Self::Buffer>, dirty_fb: bool) {
futures::executor::block_on(async {
if let Some(buffer) = &cursor.buffer {
if dirty_fb {
self.update_cursor(buffer, cursor.x, cursor.y, cursor.hot_x, cursor.hot_y)
.await;
} else {
self.move_cursor(cursor.x, cursor.y).await;
}
} else {
if dirty_fb {
self.disable_cursor().await;
}
}
});
}
}
pub struct GpuScheme {}