parent
f394f0fccb
commit
0acb65ff74
@ -0,0 +1,232 @@
|
|||||||
|
//! Framebuffer
|
||||||
|
|
||||||
|
use super::mailbox;
|
||||||
|
use alloc::string::String;
|
||||||
|
use core::fmt;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use log::*;
|
||||||
|
use once::*;
|
||||||
|
use spin::Mutex;
|
||||||
|
|
||||||
|
/// Framebuffer information
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct FramebufferInfo {
|
||||||
|
/// visible width
|
||||||
|
pub xres: u32,
|
||||||
|
/// visible height
|
||||||
|
pub yres: u32,
|
||||||
|
/// virtual width
|
||||||
|
pub xres_virtual: u32,
|
||||||
|
/// virtual height
|
||||||
|
pub yres_virtual: u32,
|
||||||
|
/// virtual offset x
|
||||||
|
pub xoffset: u32,
|
||||||
|
/// virtual offset y
|
||||||
|
pub yoffset: u32,
|
||||||
|
|
||||||
|
/// bits per pixel
|
||||||
|
pub depth: u32,
|
||||||
|
/// bytes per line
|
||||||
|
pub pitch: u32,
|
||||||
|
|
||||||
|
/// bus address, starts from 0xC0000000/0x40000000
|
||||||
|
/// (see https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes)
|
||||||
|
pub bus_addr: u32,
|
||||||
|
/// screen buffer size
|
||||||
|
pub screen_size: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum ColorDepth {
|
||||||
|
ColorDepth16 = 16,
|
||||||
|
ColorDepth32 = 32,
|
||||||
|
}
|
||||||
|
use self::ColorDepth::*;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
union ColorBuffer {
|
||||||
|
base_addr: usize,
|
||||||
|
buf16: &'static mut [u16],
|
||||||
|
buf32: &'static mut [u32],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColorBuffer {
|
||||||
|
fn new(color_depth: ColorDepth, base_addr: usize, size: usize) -> ColorBuffer {
|
||||||
|
unsafe {
|
||||||
|
match color_depth {
|
||||||
|
ColorDepth16 => ColorBuffer {
|
||||||
|
buf16: core::slice::from_raw_parts_mut(base_addr as *mut u16, size / 2),
|
||||||
|
},
|
||||||
|
ColorDepth32 => ColorBuffer {
|
||||||
|
buf32: core::slice::from_raw_parts_mut(base_addr as *mut u32, size / 4),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read16(&self, index: u32) -> u16 {
|
||||||
|
unsafe { self.buf16[index as usize] }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read32(&self, index: u32) -> u32 {
|
||||||
|
unsafe { self.buf32[index as usize] }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write16(&mut self, index: u32, pixel: u16) {
|
||||||
|
unsafe { self.buf16[index as usize] = pixel }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write32(&mut self, index: u32, pixel: u32) {
|
||||||
|
unsafe { self.buf32[index as usize] = pixel }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Frambuffer structure
|
||||||
|
pub struct Framebuffer {
|
||||||
|
pub fb_info: FramebufferInfo,
|
||||||
|
pub color_depth: ColorDepth,
|
||||||
|
buf: ColorBuffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Framebuffer {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let mut f = f.debug_struct("Framebuffer");
|
||||||
|
f.field("fb_info", &self.fb_info);
|
||||||
|
f.field("color_depth", &self.color_depth);
|
||||||
|
f.field("base_addr", unsafe { &self.buf.base_addr });
|
||||||
|
f.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Framebuffer {
|
||||||
|
fn new(width: u32, height: u32, depth: u32) -> Result<Framebuffer, String> {
|
||||||
|
assert_has_not_been_called!("Framebuffer::new must be called only once");
|
||||||
|
|
||||||
|
let (width, height) = if width == 0 || height == 0 {
|
||||||
|
mailbox::framebuffer_get_physical_size()?
|
||||||
|
} else {
|
||||||
|
(width, height)
|
||||||
|
};
|
||||||
|
let depth = if depth == 0 {
|
||||||
|
mailbox::framebuffer_get_depth()?
|
||||||
|
} else {
|
||||||
|
depth
|
||||||
|
};
|
||||||
|
|
||||||
|
let info = mailbox::framebuffer_alloc(width, height, depth)?;
|
||||||
|
let color_depth = match info.depth {
|
||||||
|
16 => ColorDepth16,
|
||||||
|
32 => ColorDepth32,
|
||||||
|
_ => Err(format!("unsupported color depth {}", info.depth))?,
|
||||||
|
};
|
||||||
|
|
||||||
|
if info.bus_addr == 0 || info.screen_size == 0 {
|
||||||
|
Err(format!("mailbox call returned an invalid address/size"))?;
|
||||||
|
}
|
||||||
|
if info.pitch == 0 || info.pitch != info.xres * info.depth / 8 {
|
||||||
|
Err(format!(
|
||||||
|
"mailbox call returned an invalid pitch value {}",
|
||||||
|
info.pitch
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
use crate::arch::memory;
|
||||||
|
let paddr = info.bus_addr & !0xC0000000;
|
||||||
|
let vaddr = memory::ioremap(paddr as usize, info.screen_size as usize, "fb");
|
||||||
|
if vaddr == 0 {
|
||||||
|
Err(format!(
|
||||||
|
"cannot remap memory range [{:#x?}..{:#x?}]",
|
||||||
|
paddr,
|
||||||
|
paddr + info.screen_size
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
Ok(Framebuffer {
|
||||||
|
buf: ColorBuffer::new(color_depth, vaddr, info.screen_size as usize),
|
||||||
|
color_depth,
|
||||||
|
fb_info: info,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn base_addr(&self) -> usize {
|
||||||
|
unsafe { self.buf.base_addr }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read pixel at `(x, y)`.
|
||||||
|
#[inline]
|
||||||
|
pub fn read(&self, x: u32, y: u32) -> u32 {
|
||||||
|
match self.color_depth {
|
||||||
|
ColorDepth16 => self.buf.read16(y * self.fb_info.xres + x) as u32,
|
||||||
|
ColorDepth32 => self.buf.read32(y * self.fb_info.xres + x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write pixel at `(x, y)`.
|
||||||
|
#[inline]
|
||||||
|
pub fn write(&mut self, x: u32, y: u32, pixel: u32) {
|
||||||
|
match self.color_depth {
|
||||||
|
ColorDepth16 => self.buf.write16(y * self.fb_info.xres + x, pixel as u16),
|
||||||
|
ColorDepth32 => self.buf.write32(y * self.fb_info.xres + x, pixel),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy buffer `[src_off .. src_off + size]` to `[dst_off .. dst_off + size]`.
|
||||||
|
/// `dst_off`, `src_off` and `size` must be aligned with `usize`.
|
||||||
|
pub fn copy(&mut self, dst_off: usize, src_off: usize, size: usize) {
|
||||||
|
const USIZE: usize = core::mem::size_of::<usize>();
|
||||||
|
let mut dst = self.base_addr() + dst_off;
|
||||||
|
let mut src = self.base_addr() + src_off;
|
||||||
|
let src_end = src + size;
|
||||||
|
while src < src_end {
|
||||||
|
unsafe { *(dst as *mut usize) = *(src as *mut usize) }
|
||||||
|
dst += USIZE;
|
||||||
|
src += USIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fill buffer `[offset .. offset + size]` with `pixel`.
|
||||||
|
/// `offset` and `size` must be aligned with `usize`.
|
||||||
|
pub fn fill(&mut self, offset: usize, size: usize, pixel: u32) {
|
||||||
|
const USIZE: usize = core::mem::size_of::<usize>();
|
||||||
|
let mut value: usize = 0;
|
||||||
|
let repeat = USIZE * 8 / self.fb_info.depth as usize;
|
||||||
|
let mask = ((1u64 << self.fb_info.depth) - 1) as usize;
|
||||||
|
for _i in 0..repeat {
|
||||||
|
value <<= self.fb_info.depth;
|
||||||
|
value += pixel as usize & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut start = self.base_addr() + offset;
|
||||||
|
let end = start + size;
|
||||||
|
while start < end {
|
||||||
|
unsafe { *(start as *mut usize) = value }
|
||||||
|
start += USIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fill the entire buffer with `0`.
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.fill(0, self.fb_info.screen_size as usize, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref FRAME_BUFFER: Mutex<Option<Framebuffer>> = Mutex::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize framebuffer
|
||||||
|
pub fn init() {
|
||||||
|
match Framebuffer::new(0, 0, 0) {
|
||||||
|
Ok(fb) => {
|
||||||
|
info!("framebuffer: init end\n{:#x?}", fb);
|
||||||
|
*FRAME_BUFFER.lock() = Some(fb);
|
||||||
|
}
|
||||||
|
Err(err) => warn!("framebuffer init failed: {}", err),
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
//! naive serial adapter driver for thinpad
|
||||||
|
|
||||||
|
use core::fmt::{Write, Result, Arguments};
|
||||||
|
use core::ptr::{read_volatile, write_volatile};
|
||||||
|
use spin::Mutex;
|
||||||
|
|
||||||
|
struct SerialPort {
|
||||||
|
base: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
const UART_STATUS: usize = 0;
|
||||||
|
const UART_DATA: usize = 0;
|
||||||
|
|
||||||
|
const UART_STATUS_CTS: u8 = 0x1; // clear to send signal
|
||||||
|
const UART_STATUS_DR: u8 = 0x2; // data ready signal
|
||||||
|
|
||||||
|
|
||||||
|
impl SerialPort {
|
||||||
|
fn new() -> SerialPort {
|
||||||
|
SerialPort {
|
||||||
|
base: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(&mut self, base: usize) {
|
||||||
|
self.base = base;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// non-blocking version of putchar()
|
||||||
|
fn putchar(&mut self, c: u8) {
|
||||||
|
write(self.base + UART_DATA, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// blocking version of getchar()
|
||||||
|
pub fn getchar(&mut self) -> char {
|
||||||
|
loop {
|
||||||
|
if (read::<u8>(self.base + UART_STATUS) & UART_STATUS_DR) == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let c = read::<u8>(self.base + UART_DATA);
|
||||||
|
match c {
|
||||||
|
255 => '\0', // null
|
||||||
|
c => c as char,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// non-blocking version of getchar()
|
||||||
|
pub fn getchar_option(&mut self) -> Option<char> {
|
||||||
|
match read::<u8>(self.base + UART_STATUS) & UART_STATUS_DR {
|
||||||
|
0 => None,
|
||||||
|
_ => Some(read::<u8>(self.base + UART_DATA) as u8 as char),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn putfmt(&mut self, fmt: Arguments) {
|
||||||
|
self.write_fmt(fmt).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for SerialPort {
|
||||||
|
fn write_str(&mut self, s: &str) -> Result {
|
||||||
|
for c in s.bytes() {
|
||||||
|
if c == 127 {
|
||||||
|
self.putchar(8);
|
||||||
|
self.putchar(b' ');
|
||||||
|
self.putchar(8);
|
||||||
|
} else {
|
||||||
|
self.putchar(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<T>(addr: usize, content: T) {
|
||||||
|
let cell = (addr) as *mut T;
|
||||||
|
write_volatile(cell, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read<T>(addr: usize) -> T {
|
||||||
|
let cell = (addr) as *const T;
|
||||||
|
read_volatile(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref SERIAL_PORT: Mutex<SerialPort> = Mutex::new(SerialPort::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(base: usize) {
|
||||||
|
SERIAL_PORT.lock().init(base);
|
||||||
|
}
|
Loading…
Reference in new issue