510d3576df
And support fetching the EDID in virtio-gpud.
255 lines
7.1 KiB
Rust
255 lines
7.1 KiB
Rust
use std::alloc::{self, Layout};
|
|
use std::convert::TryInto;
|
|
use std::ptr::{self, NonNull};
|
|
|
|
use driver_graphics::objects::{DrmConnectorStatus, DrmObjectId, DrmObjects};
|
|
use driver_graphics::{
|
|
modeinfo_for_size, CursorFramebuffer, CursorPlane, Framebuffer, GraphicsAdapter,
|
|
StandardProperties,
|
|
};
|
|
use drm_sys::DRM_MODE_DPMS_ON;
|
|
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};
|
|
|
|
#[derive(Debug)]
|
|
pub struct FbAdapter {
|
|
pub framebuffers: Vec<FrameBuffer>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Connector {
|
|
width: u32,
|
|
height: u32,
|
|
}
|
|
|
|
pub enum VesadCursor {}
|
|
|
|
impl CursorFramebuffer for VesadCursor {}
|
|
|
|
impl GraphicsAdapter for FbAdapter {
|
|
type Connector = Connector;
|
|
|
|
type Framebuffer = GraphicScreen;
|
|
type Cursor = VesadCursor;
|
|
|
|
fn name(&self) -> &'static [u8] {
|
|
b"vesad"
|
|
}
|
|
|
|
fn desc(&self) -> &'static [u8] {
|
|
b"VESA"
|
|
}
|
|
|
|
fn init(&mut self, objects: &mut DrmObjects<Self>, standard_properties: &StandardProperties) {
|
|
for framebuffer in &self.framebuffers {
|
|
let connector = objects.add_connector(Connector {
|
|
width: framebuffer.width as u32,
|
|
height: framebuffer.height as u32,
|
|
});
|
|
objects.add_object_property(
|
|
connector,
|
|
standard_properties.dpms,
|
|
DRM_MODE_DPMS_ON.into(),
|
|
);
|
|
}
|
|
}
|
|
|
|
fn get_cap(&self, cap: u32) -> syscall::Result<u64> {
|
|
match cap {
|
|
DRM_CAP_DUMB_BUFFER => Ok(1),
|
|
_ => Err(syscall::Error::new(EINVAL)),
|
|
}
|
|
}
|
|
|
|
fn set_client_cap(&self, cap: u32, _value: u64) -> syscall::Result<()> {
|
|
match cap {
|
|
DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT => Ok(()),
|
|
_ => Err(syscall::Error::new(EINVAL)),
|
|
}
|
|
}
|
|
|
|
fn probe_connector(
|
|
&mut self,
|
|
objects: &mut DrmObjects<Self>,
|
|
_standard_properties: &StandardProperties,
|
|
id: DrmObjectId,
|
|
) {
|
|
let connector = objects.get_connector_mut(id).unwrap();
|
|
connector.modes = vec![modeinfo_for_size(
|
|
connector.driver_data.width,
|
|
connector.driver_data.height,
|
|
)];
|
|
connector.connection = DrmConnectorStatus::Connected;
|
|
}
|
|
|
|
fn display_count(&self) -> usize {
|
|
self.framebuffers.len()
|
|
}
|
|
|
|
fn display_size(&self, display_id: usize) -> (u32, u32) {
|
|
(
|
|
self.framebuffers[display_id].width as u32,
|
|
self.framebuffers[display_id].height as u32,
|
|
)
|
|
}
|
|
|
|
fn create_dumb_framebuffer(&mut self, width: u32, height: u32) -> Self::Framebuffer {
|
|
GraphicScreen::new(width as usize, height as usize)
|
|
}
|
|
|
|
fn map_dumb_framebuffer(&mut self, framebuffer: &Self::Framebuffer) -> *mut u8 {
|
|
framebuffer.ptr.as_ptr().cast::<u8>()
|
|
}
|
|
|
|
fn update_plane(&mut self, display_id: usize, framebuffer: &Self::Framebuffer, damage: Damage) {
|
|
framebuffer.sync(&mut self.framebuffers[display_id], damage)
|
|
}
|
|
|
|
fn supports_hw_cursor(&self) -> bool {
|
|
false
|
|
}
|
|
|
|
fn create_cursor_framebuffer(&mut self) -> VesadCursor {
|
|
unimplemented!("Vesad does not support this function");
|
|
}
|
|
|
|
fn map_cursor_framebuffer(&mut self, _cursor: &Self::Cursor) -> *mut u8 {
|
|
unimplemented!("Vesad does not support this function");
|
|
}
|
|
|
|
fn handle_cursor(&mut self, _cursor: &CursorPlane<VesadCursor>, _dirty_fb: bool) {
|
|
unimplemented!("Vesad does not support this function");
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct FrameBuffer {
|
|
pub onscreen: *mut [u32],
|
|
pub phys: usize,
|
|
pub width: usize,
|
|
pub height: usize,
|
|
pub stride: usize,
|
|
}
|
|
|
|
impl FrameBuffer {
|
|
pub unsafe fn new(phys: usize, width: usize, height: usize, stride: usize) -> Self {
|
|
let size = stride * height;
|
|
let virt = common::physmap(
|
|
phys,
|
|
size * 4,
|
|
common::Prot {
|
|
read: true,
|
|
write: true,
|
|
},
|
|
common::MemoryType::WriteCombining,
|
|
)
|
|
.expect("vesad: failed to map framebuffer") as *mut u32;
|
|
|
|
let onscreen = ptr::slice_from_raw_parts_mut(virt, size);
|
|
|
|
Self {
|
|
onscreen,
|
|
phys,
|
|
width,
|
|
height,
|
|
stride,
|
|
}
|
|
}
|
|
|
|
pub unsafe fn parse(var: &str) -> Option<Self> {
|
|
fn parse_number(part: &str) -> Option<usize> {
|
|
let (start, radix) = if part.starts_with("0x") {
|
|
(2, 16)
|
|
} else {
|
|
(0, 10)
|
|
};
|
|
match usize::from_str_radix(&part[start..], radix) {
|
|
Ok(ok) => Some(ok),
|
|
Err(err) => {
|
|
eprintln!("vesad: failed to parse '{}': {}", part, err);
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
let mut parts = var.split(',');
|
|
let phys = parse_number(parts.next()?)?;
|
|
let width = parse_number(parts.next()?)?;
|
|
let height = parse_number(parts.next()?)?;
|
|
let stride = parse_number(parts.next()?)?;
|
|
Some(Self::new(phys, width, height, stride))
|
|
}
|
|
}
|
|
|
|
pub struct GraphicScreen {
|
|
width: usize,
|
|
height: usize,
|
|
ptr: NonNull<[u32]>,
|
|
}
|
|
|
|
impl GraphicScreen {
|
|
fn new(width: usize, height: usize) -> GraphicScreen {
|
|
let len = width * height;
|
|
let layout = Self::layout(len);
|
|
let ptr = unsafe { alloc::alloc_zeroed(layout) };
|
|
let ptr = ptr::slice_from_raw_parts_mut(ptr.cast(), len);
|
|
let ptr = NonNull::new(ptr).unwrap_or_else(|| alloc::handle_alloc_error(layout));
|
|
|
|
GraphicScreen { width, height, ptr }
|
|
}
|
|
|
|
#[inline]
|
|
fn layout(len: usize) -> Layout {
|
|
// optimizes to an integer mul
|
|
Layout::array::<u32>(len)
|
|
.unwrap()
|
|
.align_to(PAGE_SIZE)
|
|
.unwrap()
|
|
}
|
|
}
|
|
|
|
impl Drop for GraphicScreen {
|
|
fn drop(&mut self) {
|
|
let layout = Self::layout(self.ptr.len());
|
|
unsafe { alloc::dealloc(self.ptr.as_ptr().cast(), layout) };
|
|
}
|
|
}
|
|
|
|
impl Framebuffer for GraphicScreen {
|
|
fn width(&self) -> u32 {
|
|
self.width as u32
|
|
}
|
|
|
|
fn height(&self) -> u32 {
|
|
self.height as u32
|
|
}
|
|
}
|
|
|
|
impl GraphicScreen {
|
|
fn sync(&self, framebuffer: &mut FrameBuffer, sync_rect: Damage) {
|
|
let sync_rect = sync_rect.clip(
|
|
self.width.try_into().unwrap(),
|
|
self.height.try_into().unwrap(),
|
|
);
|
|
|
|
let start_x: usize = sync_rect.x.try_into().unwrap();
|
|
let start_y: usize = sync_rect.y.try_into().unwrap();
|
|
let w: usize = sync_rect.width.try_into().unwrap();
|
|
let h: usize = sync_rect.height.try_into().unwrap();
|
|
|
|
let offscreen_ptr = self.ptr.as_ptr() as *mut u32;
|
|
let onscreen_ptr = framebuffer.onscreen as *mut u32; // FIXME use as_mut_ptr once stable
|
|
|
|
for row in start_y..start_y + h {
|
|
unsafe {
|
|
ptr::copy(
|
|
offscreen_ptr.add(row * self.width + start_x),
|
|
onscreen_ptr.add(row * framebuffer.stride + start_x),
|
|
w,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|