diff --git a/drivers/graphics/driver-graphics/src/lib.rs b/drivers/graphics/driver-graphics/src/lib.rs index 29bece881b..cefa71bc2b 100644 --- a/drivers/graphics/driver-graphics/src/lib.rs +++ b/drivers/graphics/driver-graphics/src/lib.rs @@ -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); + fn get_cap(&self, cap: u32) -> Result; fn set_client_cap(&self, cap: u32, value: u64) -> Result<()>; @@ -65,6 +74,7 @@ pub struct GraphicsScheme { scheme_name: String, disable_graphical_debug: Option, socket: Socket, + objects: DrmObjects, next_id: usize, handles: BTreeMap>, @@ -90,7 +100,7 @@ enum Handle { } impl GraphicsScheme { - 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 GraphicsScheme { .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 GraphicsScheme { &mut self.adapter } + pub fn objects(&self) -> &DrmObjects { + &self.objects + } + + pub fn objects_mut(&mut self) -> &mut DrmObjects { + &mut self.objects + } + + pub fn adapter_and_objects_mut(&mut self) -> (&mut T, &mut DrmObjects) { + (&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 SchemeSync for GraphicsScheme { 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 SchemeSync for GraphicsScheme { 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 SchemeSync for GraphicsScheme { }), 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::>(); 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::>(); 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 SchemeSync for GraphicsScheme { }), 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 SchemeSync for GraphicsScheme { 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 SchemeSync for GraphicsScheme { }), 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 GraphicsScheme { 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 +} diff --git a/drivers/graphics/driver-graphics/src/objects.rs b/drivers/graphics/driver-graphics/src/objects.rs new file mode 100644 index 0000000000..b65dc41044 --- /dev/null +++ b/drivers/graphics/driver-graphics/src/objects.rs @@ -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 { + next_id: DrmObjectId, + objects: HashMap>, +} + +impl DrmObjects { + pub(crate) fn new() -> Self { + DrmObjects { + next_id: DrmObjectId(1), + objects: HashMap::new(), + } + } + + pub fn add_connector(&mut self, connector: DrmConnector) -> 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 + 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> { + 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> { + 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 + 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 { + kind: DrmObjectKind, +} + +enum DrmObjectKind { + Connector(DrmConnector), + Encoder(DrmEncoder), +} + +pub struct DrmConnector { + pub modes: Vec, + 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, +} diff --git a/drivers/graphics/ihdgd/src/device/scheme.rs b/drivers/graphics/ihdgd/src/device/scheme.rs index da49004474..7fd2a22636 100644 --- a/drivers/graphics/ihdgd/src/device/scheme.rs +++ b/drivers/graphics/ihdgd/src/device/scheme.rs @@ -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) { + // 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 { match cap { DRM_CAP_DUMB_BUFFER => Ok(1), diff --git a/drivers/graphics/vesad/src/scheme.rs b/drivers/graphics/vesad/src/scheme.rs index e77458411f..644e6b9b3f 100644 --- a/drivers/graphics/vesad/src/scheme.rs +++ b/drivers/graphics/vesad/src/scheme.rs @@ -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) { + 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 { match cap { DRM_CAP_DUMB_BUFFER => Ok(1), diff --git a/drivers/graphics/virtio-gpud/src/main.rs b/drivers/graphics/virtio-gpud/src/main.rs index 5852efaebb..cbf43e482c 100644 --- a/drivers/graphics/virtio-gpud/src/main.rs +++ b/drivers/graphics/virtio-gpud/src/main.rs @@ -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(); diff --git a/drivers/graphics/virtio-gpud/src/scheme.rs b/drivers/graphics/virtio-gpud/src/scheme.rs index 19e048f07d..4ab7b88ec4 100644 --- a/drivers/graphics/virtio-gpud/src/scheme.rs +++ b/drivers/graphics/virtio-gpud/src/scheme.rs @@ -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 for Damage { } } +pub struct VirtGpuConnector { + display_id: u32, +} + pub struct VirtGpuFramebuffer<'a> { queue: Arc>, id: ResourceId, @@ -75,6 +82,7 @@ pub struct Display { } pub struct VirtGpuAdapter<'a> { + pub config: &'a mut GpuConfig, control_queue: Arc>, cursor_queue: Arc>, transport: Arc, @@ -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) -> 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::>() { + 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) { + 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 { 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>, cursor_queue: Arc>, transport: Arc, ) -> Result<(GraphicsScheme>, 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))