Files
RedBear-OS/drivers/graphics/vesad/src/scheme.rs
T
bjorn3 510d3576df driver/graphics: Add support for blob properties
And support fetching the EDID in virtio-gpud.
2025-12-21 21:56:35 +01:00

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,
);
}
}
}
}