use crate::mmio::MmioAccess; use crate::queue::{Descriptor, VirtioQueue}; use libkos::Window; use types::address::{ByteOffset, PhysicalAddress}; use types::display::Dimension; /// The command page: commands go out from its start, responses come back /// at `RESPONSE`. pub struct GpuDmaAddrs { pub cmd_pa: PhysicalAddress, pub cmd: Window, pub resp_pa: PhysicalAddress, } pub const RESPONSE: ByteOffset = ByteOffset::new(512); /// Command fields (virtio spec 5.7.6.7): a 24-byte header, then the body. const TYPE: ByteOffset = ByteOffset::new(0); const BODY: ByteOffset = ByteOffset::new(24); /// Most bodies start with a rectangle: x, y, width, height. const RECT_X: ByteOffset = ByteOffset::new(24); const RECT_Y: ByteOffset = ByteOffset::new(28); const RECT_W: ByteOffset = ByteOffset::new(32); const RECT_H: ByteOffset = ByteOffset::new(36); /// The 24 bytes of header and 16 of rectangle in a display-info response. const DISPLAY_W: ByteOffset = ByteOffset::new(24 + 8); const DISPLAY_H: ByteOffset = ByteOffset::new(24 + 12); pub fn create_2d(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, width: Dimension, height: Dimension) -> bool { write_cmd_header(&dma.cmd, 0x0101); // Resource id, format (134: R8G8B8X8, a window's own byte order, so // its rows are copied as they are), width, height. dma.cmd.write(BODY, resource_id); dma.cmd.write(BODY + 4, 134u32); dma.cmd.write(BODY + 8, width.raw() as u32); dma.cmd.write(BODY + 12, height.raw() as u32); submit_cmd(mmio, queue, dma, 40) } pub fn attach_backing(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, fb_pa: PhysicalAddress, fb_size: u32) -> bool { write_cmd_header(&dma.cmd, 0x0106); // Resource id, one memory entry: its address and length. dma.cmd.write(BODY, resource_id); dma.cmd.write(BODY + 4, 1u32); dma.cmd.write(BODY + 8, fb_pa.raw() as u64); dma.cmd.write(BODY + 16, fb_size); submit_cmd(mmio, queue, dma, 48) } pub fn set_scanout(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, scanout_id: u32, resource_id: u32, width: Dimension, height: Dimension) -> bool { write_cmd_header(&dma.cmd, 0x0103); // The rectangle (from 0, 0), then scanout and resource ids. dma.cmd.write(RECT_W, width.raw() as u32); dma.cmd.write(RECT_H, height.raw() as u32); dma.cmd.write(BODY + 16, scanout_id); dma.cmd.write(BODY + 20, resource_id); submit_cmd(mmio, queue, dma, 48) } /// A rectangle of a resource, in pixels. #[derive(Clone, Copy, Debug)] pub struct Rect { pub x: u32, pub y: u32, pub w: u32, pub h: u32, } /// Copies `rect` of the backing to the host; `offset` is where its top /// left pixel is in the backing, in bytes. pub fn transfer_to_host(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, rect: Rect, offset: u64) -> bool { write_cmd_header(&dma.cmd, 0x0105); put_rect(&dma.cmd, rect); // Offset into the resource's memory, then the resource id. dma.cmd.write(BODY + 16, offset); dma.cmd.write(BODY + 24, resource_id); submit_cmd(mmio, queue, dma, 56) } pub fn resource_flush(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, rect: Rect) -> bool { write_cmd_header(&dma.cmd, 0x0104); put_rect(&dma.cmd, rect); dma.cmd.write(BODY + 16, resource_id); submit_cmd(mmio, queue, dma, 48) } fn write_cmd_header(cmd: &Window, cmd_type: u32) { cmd.fill_u32(ByteOffset::ZERO, 128, 0); cmd.write(TYPE, cmd_type); } fn put_rect(cmd: &Window, r: Rect) { cmd.write(RECT_X, r.x); cmd.write(RECT_Y, r.y); cmd.write(RECT_W, r.w); cmd.write(RECT_H, r.h); } pub fn get_display_info(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs) -> Option<(u32, u32)> { write_cmd_header(&dma.cmd, 0x0100); let descs = [ Descriptor { addr: dma.cmd_pa.raw() as u64, len: 24, writable: false }, Descriptor { addr: dma.resp_pa.raw() as u64, len: 408, writable: true }, ]; if !queue.submit(mmio, &descs) { return None; } if dma.cmd.read::(RESPONSE) != 0x1101 { return None; } let w: u32 = dma.cmd.read(RESPONSE + DISPLAY_W.raw()); let h: u32 = dma.cmd.read(RESPONSE + DISPLAY_H.raw()); if w > 0 && h > 0 { Some((w, h)) } else { None } } fn submit_cmd(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, cmd_len: u32) -> bool { let descs = [ Descriptor { addr: dma.cmd_pa.raw() as u64, len: cmd_len, writable: false }, Descriptor { addr: dma.resp_pa.raw() as u64, len: 24, writable: true }, ]; queue.submit(mmio, &descs) }