diff --git a/kernel/src/arch/mipsel/board/thinpad/fb.rs b/kernel/src/arch/mipsel/board/thinpad/fb.rs new file mode 100644 index 0000000..ff3b31f --- /dev/null +++ b/kernel/src/arch/mipsel/board/thinpad/fb.rs @@ -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 { + 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::(); + 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::(); + 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> = 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), + } +} diff --git a/kernel/src/arch/mipsel/board/thinpad/mod.rs b/kernel/src/arch/mipsel/board/thinpad/mod.rs index 1277ed7..dbfbb84 100644 --- a/kernel/src/arch/mipsel/board/thinpad/mod.rs +++ b/kernel/src/arch/mipsel/board/thinpad/mod.rs @@ -8,7 +8,7 @@ pub mod console; /// Initialize serial port first pub fn init_serial_early() { assert_has_not_been_called!("board::init must be called only once"); - serial::init(); + serial::init(0xa3000000); println!("Hello ThinPad!"); } @@ -16,5 +16,4 @@ pub fn init_serial_early() { pub fn init_driver() { // TODO: add possibly more drivers // timer::init(); - console::init(); } \ No newline at end of file diff --git a/kernel/src/arch/mipsel/board/thinpad/serial.rs b/kernel/src/arch/mipsel/board/thinpad/serial.rs new file mode 100644 index 0000000..3c59a0d --- /dev/null +++ b/kernel/src/arch/mipsel/board/thinpad/serial.rs @@ -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::(self.base + UART_STATUS) & UART_STATUS_DR) == 0 { + break; + } + } + let c = read::(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 { + match read::(self.base + UART_STATUS) & UART_STATUS_DR { + 0 => None, + _ => Some(read::(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(addr: usize, content: T) { + let cell = (addr) as *mut T; + write_volatile(cell, content); +} + +fn read(addr: usize) -> T { + let cell = (addr) as *const T; + read_volatile(cell); +} + + +lazy_static! { + pub static ref SERIAL_PORT: Mutex = Mutex::new(SerialPort::new()); +} + +pub fn init(base: usize) { + SERIAL_PORT.lock().init(base); +}