drivers/graphics: Introduce DrmObjects and track connectors and encoders using it

This commit is contained in:
bjorn3
2025-12-20 13:48:48 +01:00
parent 40510aa759
commit 0e98a36495
6 changed files with 346 additions and 77 deletions
+77 -60
View File
@@ -8,6 +8,7 @@ use std::mem;
use std::mem::transmute;
use std::sync::Arc;
use drm_sys::drm_mode_modeinfo;
use graphics_ipc::v1::CursorDamage;
use graphics_ipc::v2::Damage;
use inputd::{VtEvent, VtEventKind};
@@ -17,13 +18,21 @@ use redox_scheme::{CallerCtx, OpenResult, RequestKind, SignalBehavior, Socket};
use syscall::schemev2::NewFdFlags;
use syscall::{Error, MapFlags, Result, EAGAIN, EBADF, EINVAL, ENOENT, EOPNOTSUPP};
pub trait GraphicsAdapter {
use crate::objects::{DrmObjectId, DrmObjects};
pub mod objects;
pub trait GraphicsAdapter: Sized {
type Connector;
type Framebuffer: Framebuffer;
type Cursor: CursorFramebuffer;
fn name(&self) -> &'static [u8];
fn desc(&self) -> &'static [u8];
fn init(&mut self, objects: &mut DrmObjects<Self>);
fn get_cap(&self, cap: u32) -> Result<u64>;
fn set_client_cap(&self, cap: u32, value: u64) -> Result<()>;
@@ -65,6 +74,7 @@ pub struct GraphicsScheme<T: GraphicsAdapter> {
scheme_name: String,
disable_graphical_debug: Option<File>,
socket: Socket,
objects: DrmObjects<T>,
next_id: usize,
handles: BTreeMap<usize, Handle<T>>,
@@ -90,7 +100,7 @@ enum Handle<T: GraphicsAdapter> {
}
impl<T: GraphicsAdapter> GraphicsScheme<T> {
pub fn new(adapter: T, scheme_name: String) -> Self {
pub fn new(mut adapter: T, scheme_name: String) -> Self {
assert!(scheme_name.starts_with("display"));
let socket = Socket::nonblock(&scheme_name).expect("failed to create graphics scheme");
@@ -99,11 +109,15 @@ impl<T: GraphicsAdapter> GraphicsScheme<T> {
.expect("vesad: Failed to open /scheme/debug/disable-graphical-debug"),
);
let mut objects = DrmObjects::new();
adapter.init(&mut objects);
GraphicsScheme {
adapter,
scheme_name,
disable_graphical_debug,
socket,
objects,
next_id: 0,
handles: BTreeMap::new(),
active_vt: 0,
@@ -123,6 +137,18 @@ impl<T: GraphicsAdapter> GraphicsScheme<T> {
&mut self.adapter
}
pub fn objects(&self) -> &DrmObjects<T> {
&self.objects
}
pub fn objects_mut(&mut self) -> &mut DrmObjects<T> {
&mut self.objects
}
pub fn adapter_and_objects_mut(&mut self) -> (&mut T, &mut DrmObjects<T>) {
(&mut self.adapter, &mut self.objects)
}
pub fn handle_vt_event(&mut self, vt_event: VtEvent) {
match vt_event.kind {
VtEventKind::Activate => {
@@ -440,18 +466,10 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
id & 0xFF
}
fn conn_id(i: u32) -> u32 {
id_index(i) | (1 << 8)
}
fn crtc_id(i: u32) -> u32 {
id_index(i) | (1 << 9)
}
fn enc_id(i: u32) -> u32 {
id_index(i) | (1 << 10)
}
fn fb_id(i: u32) -> u32 {
id_index(i) | (1 << 11)
}
@@ -468,40 +486,6 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
id_index(i) | (1 << 14)
}
fn modeinfo_for_size(width: u32, height: u32) -> drm_sys::drm_mode_modeinfo {
let mut modeinfo = drm_sys::drm_mode_modeinfo {
// The actual visible display size
hdisplay: width as u16,
vdisplay: height as u16,
// These are used to calculate the refresh rate
clock: 60 * width * height / 1000,
htotal: width as u16,
vtotal: height as u16,
vscan: 0,
vrefresh: 60,
type_: drm_sys::DRM_MODE_TYPE_PREFERRED | drm_sys::DRM_MODE_TYPE_DRIVER,
name: [0; 32],
// These only matter when modesetting physical display adapters. For
// those we should be able to parse the EDID blob.
hsync_start: width as u16,
hsync_end: width as u16,
hskew: 0,
vsync_start: height as u16,
vsync_end: height as u16,
flags: 0,
};
let name = format!("{width}x{height}").into_bytes();
for (to, from) in modeinfo.name.iter_mut().zip(name) {
*to = from as c_char;
}
modeinfo
}
match self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::V1Screen { .. } => {
return Err(Error::new(EOPNOTSUPP));
@@ -539,14 +523,12 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
}),
ipc::MODE_CARD_RES => ipc::DrmModeCardRes::with(payload, |mut data| {
let count = self.adapter.display_count();
let mut conn_ids = Vec::with_capacity(count);
let conn_ids = self.objects.connectors().map(|id| id.0).collect::<Vec<_>>();
let mut crtc_ids = Vec::with_capacity(count);
let mut enc_ids = Vec::with_capacity(count);
let enc_ids = self.objects.encoders().map(|id| id.0).collect::<Vec<_>>();
let mut fb_ids = Vec::with_capacity(count);
for i in 0..(count as u32) {
conn_ids.push(conn_id(i));
crtc_ids.push(crtc_id(i));
enc_ids.push(enc_id(i));
fb_ids.push(fb_id(i));
}
data.set_fb_id_ptr(&fb_ids);
@@ -561,7 +543,6 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
}),
ipc::MODE_GET_CRTC => ipc::DrmModeCrtc::with(payload, |mut data| {
let i = id_index(data.crtc_id());
let (width, height) = self.adapter.display_size(i as usize);
//TOOD: connectors
data.set_fb_id(fb_id(i));
data.set_x(0);
@@ -573,21 +554,24 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
Ok(0)
}),
ipc::MODE_GET_ENCODER => ipc::DrmModeGetEncoder::with(payload, |mut data| {
let i = id_index(data.encoder_id());
let (width, height) = self.adapter.display_size(i as usize);
data.set_crtc_id(crtc_id(i));
data.set_possible_crtcs(1 << i);
data.set_possible_clones(1 << i);
let encoder = self.objects.get_encoder(DrmObjectId(data.encoder_id()))?;
data.set_crtc_id(encoder.crtc_id.0);
data.set_possible_crtcs(encoder.possible_crtcs);
data.set_possible_clones(encoder.possible_clones);
Ok(0)
}),
ipc::MODE_GET_CONNECTOR => ipc::DrmModeGetConnector::with(payload, |mut data| {
let i = id_index(data.connector_id());
let (width, height) = self.adapter.display_size(i as usize);
data.set_connection(1 /* connected */);
data.set_modes_ptr(&[modeinfo_for_size(width, height)]);
let connector = self
.objects
.get_connector(DrmObjectId(data.connector_id()))?;
data.set_connection(connector.connection as u32);
data.set_modes_ptr(&connector.modes);
data.set_mm_width(connector.mm_width);
data.set_mm_height(connector.mm_width);
data.set_subpixel(connector.subpixel as u32);
data.set_props_ptr(&[]);
data.set_prop_values_ptr(&[]);
data.set_encoders_ptr(&[enc_id(i)]);
data.set_encoders_ptr(&[connector.encoder_id.0]);
Ok(0)
}),
ipc::MODE_GET_FB => ipc::DrmModeFbCmd::with(payload, |mut data| {
@@ -656,7 +640,6 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
}),
ipc::MODE_GET_PLANE => ipc::DrmModeGetPlane::with(payload, |mut data| {
let i = id_index(data.plane_id());
let (width, height) = self.adapter.display_size(i as usize);
data.set_crtc_id(crtc_id(i));
data.set_fb_id(fb_id(i));
data.set_possible_crtcs(1 << i);
@@ -748,3 +731,37 @@ impl<T: GraphicsAdapter> GraphicsScheme<T> {
self.handles.remove(&id);
}
}
pub fn modeinfo_for_size(width: u32, height: u32) -> drm_mode_modeinfo {
let mut modeinfo = drm_mode_modeinfo {
// The actual visible display size
hdisplay: width as u16,
vdisplay: height as u16,
// These are used to calculate the refresh rate
clock: 60 * width * height / 1000,
htotal: width as u16,
vtotal: height as u16,
vscan: 0,
vrefresh: 60,
type_: drm_sys::DRM_MODE_TYPE_PREFERRED | drm_sys::DRM_MODE_TYPE_DRIVER,
name: [0; 32],
// These only matter when modesetting physical display adapters. For
// those we should be able to parse the EDID blob.
hsync_start: width as u16,
hsync_end: width as u16,
hskew: 0,
vsync_start: height as u16,
vsync_end: height as u16,
flags: 0,
};
let name = format!("{width}x{height}").into_bytes();
for (to, from) in modeinfo.name.iter_mut().zip(name) {
*to = from as c_char;
}
modeinfo
}
@@ -0,0 +1,153 @@
use std::collections::HashMap;
use drm_sys::drm_mode_modeinfo;
use syscall::{Error, Result, EINVAL};
use crate::GraphicsAdapter;
pub struct DrmObjects<T: GraphicsAdapter> {
next_id: DrmObjectId,
objects: HashMap<DrmObjectId, DrmObject<T>>,
}
impl<T: GraphicsAdapter> DrmObjects<T> {
pub(crate) fn new() -> Self {
DrmObjects {
next_id: DrmObjectId(1),
objects: HashMap::new(),
}
}
pub fn add_connector(&mut self, connector: DrmConnector<T>) -> DrmObjectId {
let connector_id = self.next_id;
self.objects.insert(
connector_id,
DrmObject {
kind: DrmObjectKind::Connector(connector),
},
);
self.next_id.0 += 1;
let encoder_id = self.next_id;
self.objects.insert(
encoder_id,
DrmObject {
kind: DrmObjectKind::Encoder(DrmEncoder {
crtc_id: DrmObjectId::INVALID,
possible_crtcs: 0,
possible_clones: 0,
}),
},
);
self.next_id.0 += 1;
// Attach encoder to connector
self.get_connector_mut(connector_id).unwrap().encoder_id = encoder_id;
connector_id
}
pub fn connectors(&self) -> impl Iterator<Item = DrmObjectId> + use<'_, T> {
self.objects
.iter()
.filter_map(|(&id, object)| match object.kind {
DrmObjectKind::Connector(_) => Some(id),
_ => None,
})
}
pub fn get_connector(&self, id: DrmObjectId) -> Result<&DrmConnector<T>> {
let object = self.objects.get(&id).ok_or(Error::new(EINVAL))?;
match &object.kind {
DrmObjectKind::Connector(drm_connector) => Ok(drm_connector),
_ => Err(Error::new(EINVAL)),
}
}
pub fn get_connector_mut(&mut self, id: DrmObjectId) -> Result<&mut DrmConnector<T>> {
let object = self.objects.get_mut(&id).ok_or(Error::new(EINVAL))?;
match &mut object.kind {
DrmObjectKind::Connector(drm_connector) => Ok(drm_connector),
_ => Err(Error::new(EINVAL)),
}
}
pub fn encoders(&self) -> impl Iterator<Item = DrmObjectId> + use<'_, T> {
self.objects
.iter()
.filter_map(|(&id, object)| match object.kind {
DrmObjectKind::Encoder(_) => Some(id),
_ => None,
})
}
pub fn get_encoder(&self, id: DrmObjectId) -> Result<&DrmEncoder> {
let object = self.objects.get(&id).ok_or(Error::new(EINVAL))?;
match &object.kind {
DrmObjectKind::Encoder(drm_encoder) => Ok(drm_encoder),
_ => Err(Error::new(EINVAL)),
}
}
pub fn get_encoder_mut(&mut self, id: DrmObjectId) -> Result<&mut DrmEncoder> {
let object = self.objects.get_mut(&id).ok_or(Error::new(EINVAL))?;
match &mut object.kind {
DrmObjectKind::Encoder(drm_encoder) => Ok(drm_encoder),
_ => Err(Error::new(EINVAL)),
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct DrmObjectId(pub(crate) u32);
impl DrmObjectId {
pub const INVALID: DrmObjectId = DrmObjectId(0);
}
struct DrmObject<T: GraphicsAdapter> {
kind: DrmObjectKind<T>,
}
enum DrmObjectKind<T: GraphicsAdapter> {
Connector(DrmConnector<T>),
Encoder(DrmEncoder),
}
pub struct DrmConnector<T: GraphicsAdapter> {
pub modes: Vec<drm_mode_modeinfo>,
pub encoder_id: DrmObjectId,
pub connector_type: u32,
pub connector_type_id: u32,
pub connection: DrmConnectorStatus,
pub mm_width: u32,
pub mm_height: u32,
pub subpixel: DrmSubpixelOrder,
pub driver_data: T::Connector,
}
#[derive(Copy, Clone)]
#[repr(u32)]
pub enum DrmConnectorStatus {
Disconnected = 0,
Connected = 1,
Unknown = 2,
}
#[derive(Copy, Clone)]
#[repr(u32)]
pub enum DrmSubpixelOrder {
Unknown = 0,
HorizontalRGB,
HorizontalBGR,
VerticalRGB,
VerticalBGR,
None,
}
// FIXME can we represent connector and encoder using a single struct?
pub struct DrmEncoder {
pub crtc_id: DrmObjectId,
pub possible_crtcs: u32,
pub possible_clones: u32,
}
+28 -1
View File
@@ -4,7 +4,12 @@ use std::alloc::{self, Layout};
use std::convert::TryInto;
use std::ptr::{self, NonNull};
use driver_graphics::{CursorFramebuffer, CursorPlane, Framebuffer, GraphicsAdapter};
use driver_graphics::objects::{
DrmConnector, DrmConnectorStatus, DrmObjectId, DrmObjects, DrmSubpixelOrder,
};
use driver_graphics::{
modeinfo_for_size, CursorFramebuffer, CursorPlane, Framebuffer, GraphicsAdapter,
};
use graphics_ipc::v1::Damage;
use graphics_ipc::v2::ipc::{DRM_CAP_DUMB_BUFFER, DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT};
use syscall::{error::EINVAL, PAGE_SIZE};
@@ -17,6 +22,8 @@ pub enum Cursor {}
impl CursorFramebuffer for Cursor {}
impl GraphicsAdapter for Device {
type Connector = ();
type Framebuffer = DumbFb;
type Cursor = Cursor;
@@ -28,6 +35,26 @@ impl GraphicsAdapter for Device {
b"Intel HD Graphics"
}
fn init(&mut self, objects: &mut DrmObjects<Self>) {
// FIXME enumerate actual connectors
for framebuffer in &self.framebuffers {
objects.add_connector(DrmConnector {
modes: vec![modeinfo_for_size(
framebuffer.width as u32,
framebuffer.height as u32,
)],
encoder_id: DrmObjectId::INVALID,
connector_type: 0,
connector_type_id: 0,
connection: DrmConnectorStatus::Connected,
mm_width: 0,
mm_height: 0,
subpixel: DrmSubpixelOrder::Unknown,
driver_data: (),
});
}
}
fn get_cap(&self, cap: u32) -> syscall::Result<u64> {
match cap {
DRM_CAP_DUMB_BUFFER => Ok(1),
+27 -1
View File
@@ -2,7 +2,12 @@ use std::alloc::{self, Layout};
use std::convert::TryInto;
use std::ptr::{self, NonNull};
use driver_graphics::{CursorFramebuffer, CursorPlane, Framebuffer, GraphicsAdapter};
use driver_graphics::objects::{
DrmConnector, DrmConnectorStatus, DrmObjectId, DrmObjects, DrmSubpixelOrder,
};
use driver_graphics::{
modeinfo_for_size, CursorFramebuffer, CursorPlane, Framebuffer, GraphicsAdapter,
};
use graphics_ipc::v1::Damage;
use graphics_ipc::v2::ipc::{DRM_CAP_DUMB_BUFFER, DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT};
use syscall::{EINVAL, PAGE_SIZE};
@@ -16,6 +21,8 @@ pub enum VesadCursor {}
impl CursorFramebuffer for VesadCursor {}
impl GraphicsAdapter for FbAdapter {
type Connector = ();
type Framebuffer = GraphicScreen;
type Cursor = VesadCursor;
@@ -27,6 +34,25 @@ impl GraphicsAdapter for FbAdapter {
b"VESA"
}
fn init(&mut self, objects: &mut DrmObjects<Self>) {
for framebuffer in &self.framebuffers {
objects.add_connector(DrmConnector {
modes: vec![modeinfo_for_size(
framebuffer.width as u32,
framebuffer.height as u32,
)],
encoder_id: DrmObjectId::INVALID,
connector_type: 0,
connector_type_id: 0,
connection: DrmConnectorStatus::Connected,
mm_width: 0,
mm_height: 0,
subpixel: DrmSubpixelOrder::Unknown,
driver_data: (),
});
}
}
fn get_cap(&self, cap: u32) -> syscall::Result<u64> {
match cap {
DRM_CAP_DUMB_BUFFER => Ok(1),
+10 -6
View File
@@ -477,12 +477,12 @@ fn deamon(deamon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow:
device.transport.run_device();
deamon.ready();
let (mut scheme, mut inputd_handle) = futures::executor::block_on(scheme::GpuScheme::new(
let (mut scheme, mut inputd_handle) = scheme::GpuScheme::new(
config,
control_queue.clone(),
cursor_queue.clone(),
device.transport.clone(),
))?;
)?;
user_data! {
enum Source {
@@ -538,13 +538,17 @@ fn deamon(deamon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow:
Source::Interrupt => loop {
let before_gen = device.transport.config_generation();
let events = config.events_read.get();
let events = scheme.adapter().config.events_read.get();
if events & VIRTIO_GPU_EVENT_DISPLAY != 0 {
futures::executor::block_on(scheme.adapter_mut().update_displays(config))
.unwrap();
let (adapter, objects) = scheme.adapter_and_objects_mut();
futures::executor::block_on(adapter.update_displays(objects)).unwrap();
scheme.notify_displays_changed();
config.events_clear.set(VIRTIO_GPU_EVENT_DISPLAY);
scheme
.adapter_mut()
.config
.events_clear
.set(VIRTIO_GPU_EVENT_DISPLAY);
}
let after_gen = device.transport.config_generation();
+51 -9
View File
@@ -1,8 +1,11 @@
use std::sync::Arc;
use common::{dma::Dma, sgl};
use driver_graphics::objects::{
DrmConnector, DrmConnectorStatus, DrmObjectId, DrmObjects, DrmSubpixelOrder,
};
use driver_graphics::{
CursorFramebuffer, CursorPlane, Framebuffer, GraphicsAdapter, GraphicsScheme,
modeinfo_for_size, CursorFramebuffer, CursorPlane, Framebuffer, GraphicsAdapter, GraphicsScheme,
};
use graphics_ipc::v1::Damage;
use graphics_ipc::v2::ipc::{DRM_CAP_DUMB_BUFFER, DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT};
@@ -26,6 +29,10 @@ impl Into<GpuRect> for Damage {
}
}
pub struct VirtGpuConnector {
display_id: u32,
}
pub struct VirtGpuFramebuffer<'a> {
queue: Arc<Queue<'a>>,
id: ResourceId,
@@ -75,6 +82,7 @@ pub struct Display {
}
pub struct VirtGpuAdapter<'a> {
pub config: &'a mut GpuConfig,
control_queue: Arc<Queue<'a>>,
cursor_queue: Arc<Queue<'a>>,
transport: Arc<dyn Transport>,
@@ -82,9 +90,9 @@ pub struct VirtGpuAdapter<'a> {
}
impl VirtGpuAdapter<'_> {
pub async fn update_displays(&mut self, config: &mut GpuConfig) -> Result<(), Error> {
let mut display_info = self.get_display_info().await?;
let raw_displays = &mut display_info.display_info[..config.num_scanouts() as usize];
pub async fn update_displays(&mut self, objects: &mut DrmObjects<Self>) -> Result<(), Error> {
let display_info = self.get_display_info().await?;
let raw_displays = &display_info.display_info[..self.config.num_scanouts() as usize];
self.displays.resize(
raw_displays.len(),
@@ -113,6 +121,19 @@ impl VirtGpuAdapter<'_> {
}
}
for connector in objects.connectors().collect::<Vec<_>>() {
let connector = objects.get_connector_mut(connector).unwrap();
let display = &self.displays[connector.driver_data.display_id as usize];
connector.modes = vec![modeinfo_for_size(display.width, display.height)];
connector.connection =
if raw_displays[connector.driver_data.display_id as usize].enabled != 0 {
DrmConnectorStatus::Connected
} else {
DrmConnectorStatus::Disconnected
};
}
Ok(())
}
@@ -198,6 +219,8 @@ impl VirtGpuAdapter<'_> {
}
impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> {
type Connector = VirtGpuConnector;
type Framebuffer = VirtGpuFramebuffer<'a>;
type Cursor = VirtGpuCursor;
@@ -209,6 +232,26 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> {
b"VirtIO GPU"
}
fn init(&mut self, objects: &mut DrmObjects<Self>) {
for display_id in 0..self.config.num_scanouts.get() {
objects.add_connector(DrmConnector {
modes: vec![],
encoder_id: DrmObjectId::INVALID,
connector_type: 0,
connector_type_id: 0,
connection: DrmConnectorStatus::Disconnected,
mm_width: 0,
mm_height: 0,
subpixel: DrmSubpixelOrder::Unknown,
driver_data: VirtGpuConnector { display_id },
});
}
futures::executor::block_on(async {
self.update_displays(objects).await.unwrap();
});
}
fn get_cap(&self, cap: u32) -> syscall::Result<u64> {
match cap {
DRM_CAP_DUMB_BUFFER => Ok(1),
@@ -425,21 +468,20 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> {
pub struct GpuScheme {}
impl<'a> GpuScheme {
pub async fn new(
config: &mut GpuConfig,
pub fn new(
config: &'a mut GpuConfig,
control_queue: Arc<Queue<'a>>,
cursor_queue: Arc<Queue<'a>>,
transport: Arc<dyn Transport>,
) -> Result<(GraphicsScheme<VirtGpuAdapter<'a>>, DisplayHandle), Error> {
let mut adapter = VirtGpuAdapter {
let adapter = VirtGpuAdapter {
config,
control_queue,
cursor_queue,
transport,
displays: vec![],
};
adapter.update_displays(config).await?;
let scheme = GraphicsScheme::new(adapter, "display.virtio-gpu".to_owned());
let handle = DisplayHandle::new("virtio-gpu").unwrap();
Ok((scheme, handle))