mirror of
https://github.com/rcore-os/rCore.git
synced 2024-11-23 00:16:17 +04:00
aarch64/fb: add framebuffer initialization
This commit is contained in:
parent
d7511d8120
commit
6ba9e34f77
190
kernel/src/arch/aarch64/board/raspi3/fb.rs
Normal file
190
kernel/src/arch/aarch64/board/raspi3/fb.rs
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
//! 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)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum ColorFormat {
|
||||||
|
BGR565 = 16,
|
||||||
|
RGBA8888 = 32,
|
||||||
|
}
|
||||||
|
use self::ColorFormat::*;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
union ColorBuffer {
|
||||||
|
base_addr: u32,
|
||||||
|
buf16: &'static mut [u16],
|
||||||
|
buf32: &'static mut [u32],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColorBuffer {
|
||||||
|
fn new(color_format: ColorFormat, bus_addr: u32, size: u32) -> ColorBuffer {
|
||||||
|
unsafe {
|
||||||
|
match color_format {
|
||||||
|
BGR565 => ColorBuffer {
|
||||||
|
buf16: core::slice::from_raw_parts_mut(
|
||||||
|
bus_addr as *mut u16,
|
||||||
|
(size / 2) as usize,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
RGBA8888 => ColorBuffer {
|
||||||
|
buf32: core::slice::from_raw_parts_mut(
|
||||||
|
bus_addr as *mut u32,
|
||||||
|
(size / 4) as usize,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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_format: ColorFormat,
|
||||||
|
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_format", &self.color_format);
|
||||||
|
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_format = match info.depth {
|
||||||
|
16 => BGR565,
|
||||||
|
32 => RGBA8888,
|
||||||
|
_ => 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
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let paddr = info.bus_addr & !0xC0000000;
|
||||||
|
Ok(Framebuffer {
|
||||||
|
buf: ColorBuffer::new(color_format, paddr, info.screen_size),
|
||||||
|
color_format,
|
||||||
|
fb_info: info,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn base_addr(&self) -> u32 {
|
||||||
|
unsafe { self.buf.base_addr }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn read(&self, x: u32, y: u32) -> u32 {
|
||||||
|
match self.color_format {
|
||||||
|
BGR565 => self.buf.read16(y * self.fb_info.xres + x) as u32,
|
||||||
|
RGBA8888 => self.buf.read32(y * self.fb_info.xres + x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn write(&mut self, x: u32, y: u32, pixel: u32) {
|
||||||
|
match self.color_format {
|
||||||
|
BGR565 => self.buf.write16(y * self.fb_info.xres + x, pixel as u16),
|
||||||
|
RGBA8888 => self.buf.write32(y * self.fb_info.xres + x, pixel),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
let info = fb.fb_info;
|
||||||
|
info!("framebuffer: init end\n{:#x?}", fb);
|
||||||
|
*FRAME_BUFFER.lock() = Some(fb);
|
||||||
|
}
|
||||||
|
Err(err) => error!("framebuffer init failed: {}", err),
|
||||||
|
}
|
||||||
|
}
|
@ -2,8 +2,10 @@
|
|||||||
//!
|
//!
|
||||||
//! (ref: https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface)
|
//! (ref: https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface)
|
||||||
|
|
||||||
|
use super::fb::FramebufferInfo;
|
||||||
use bcm2837::mailbox::{Mailbox, MailboxChannel};
|
use bcm2837::mailbox::{Mailbox, MailboxChannel};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use alloc::string::String;
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use spin::Mutex;
|
use spin::Mutex;
|
||||||
use aarch64::barrier;
|
use aarch64::barrier;
|
||||||
@ -16,6 +18,12 @@ lazy_static! {
|
|||||||
pub struct PropertyMailboxError(u32);
|
pub struct PropertyMailboxError(u32);
|
||||||
pub type PropertyMailboxResult<T> = Result<T, PropertyMailboxError>;
|
pub type PropertyMailboxResult<T> = Result<T, PropertyMailboxError>;
|
||||||
|
|
||||||
|
impl From<PropertyMailboxError> for String {
|
||||||
|
fn from(error: PropertyMailboxError) -> Self {
|
||||||
|
format!("{:x?}", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Buffer request/response code.
|
/// Buffer request/response code.
|
||||||
/// Copied from `linux/include/soc/bcm2835/raspberrypi-firmware.h`
|
/// Copied from `linux/include/soc/bcm2835/raspberrypi-firmware.h`
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
@ -167,11 +175,11 @@ struct Align16<T: Sized>(PropertyMailboxRequest<T>);
|
|||||||
|
|
||||||
/// Pack a sequence of concatenated tags into a request, and send the address
|
/// Pack a sequence of concatenated tags into a request, and send the address
|
||||||
/// to the mailbox.
|
/// to the mailbox.
|
||||||
/// Returns PropertyMailboxResult<typeof($tags)>.
|
/// Returns `PropertyMailboxResult<typeof($tags)>`.
|
||||||
macro_rules! send_request {
|
macro_rules! send_request {
|
||||||
($tags: ident) => {{
|
($tags: ident) => {{
|
||||||
let req = Align16(PropertyMailboxRequest {
|
let req = Align16(PropertyMailboxRequest {
|
||||||
buf_size: mem::size_of_val(&$tags) as u32,
|
buf_size: mem::size_of_val(&$tags) as u32 + 12,
|
||||||
req_resp_code: RPI_FIRMWARE_STATUS_REQUEST,
|
req_resp_code: RPI_FIRMWARE_STATUS_REQUEST,
|
||||||
buf: $tags,
|
buf: $tags,
|
||||||
end_tag: RPI_FIRMWARE_PROPERTY_END,
|
end_tag: RPI_FIRMWARE_PROPERTY_END,
|
||||||
@ -179,8 +187,9 @@ macro_rules! send_request {
|
|||||||
|
|
||||||
unsafe { barrier::wmb() }
|
unsafe { barrier::wmb() }
|
||||||
{
|
{
|
||||||
|
let addr = &req as *const _ as u32;
|
||||||
let mut mbox = MAILBOX.lock();
|
let mut mbox = MAILBOX.lock();
|
||||||
mbox.write(MailboxChannel::Property, &req as *const _ as u32);
|
mbox.write(MailboxChannel::Property, addr);
|
||||||
mbox.read(MailboxChannel::Property);
|
mbox.read(MailboxChannel::Property);
|
||||||
}
|
}
|
||||||
unsafe { barrier::rmb() }
|
unsafe { barrier::rmb() }
|
||||||
@ -193,7 +202,7 @@ macro_rules! send_request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Send a tag to mailbox. Will call `send_request!`.
|
/// Send a tag to mailbox. Will call `send_request!`.
|
||||||
/// Returns PropertyMailboxResult<typeof(buf)>.
|
/// Returns `PropertyMailboxResult<typeof(buf)>`.
|
||||||
macro_rules! send_one_tag {
|
macro_rules! send_one_tag {
|
||||||
($id: expr, [$($arg: expr),*]) => {{
|
($id: expr, [$($arg: expr),*]) => {{
|
||||||
let buf = [$($arg),*];
|
let buf = [$($arg),*];
|
||||||
@ -264,3 +273,79 @@ pub fn framebuffer_set_virtual_offset(xoffset: u32, yoffset: u32) -> PropertyMai
|
|||||||
)?;
|
)?;
|
||||||
Ok((ret[0], ret[1]))
|
Ok((ret[0], ret[1]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allocate framebuffer on GPU and try to set width/height/depth.
|
||||||
|
/// Returns `FramebufferInfo`.
|
||||||
|
pub fn framebuffer_alloc(width: u32, height: u32, depth: u32) -> PropertyMailboxResult<FramebufferInfo> {
|
||||||
|
#[repr(C, packed)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct FramebufferAllocTag {
|
||||||
|
set_physical_size: PropertyMailboxTag<[u32; 2]>,
|
||||||
|
set_virtual_size: PropertyMailboxTag<[u32; 2]>,
|
||||||
|
set_depth: PropertyMailboxTag<[u32; 1]>,
|
||||||
|
set_virtual_offset: PropertyMailboxTag<[u32; 2]>,
|
||||||
|
allocate: PropertyMailboxTag<[u32; 2]>,
|
||||||
|
get_pitch: PropertyMailboxTag<[u32; 1]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let tags = FramebufferAllocTag {
|
||||||
|
// Set physical (buffer) width/height. Returns `(width, height)` in pixel.
|
||||||
|
set_physical_size: PropertyMailboxTag {
|
||||||
|
id: RPI_FIRMWARE_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT,
|
||||||
|
buf_size: 8,
|
||||||
|
req_resp_size: 0,
|
||||||
|
buf: [width, height],
|
||||||
|
},
|
||||||
|
// Set virtual (buffer) width/height. Returns `(width, height)` in pixel.
|
||||||
|
set_virtual_size: PropertyMailboxTag {
|
||||||
|
id: RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT,
|
||||||
|
buf_size: 8,
|
||||||
|
req_resp_size: 0,
|
||||||
|
buf: [width, height],
|
||||||
|
},
|
||||||
|
// Set depth; Returns bits per pixel.
|
||||||
|
set_depth: PropertyMailboxTag {
|
||||||
|
id: RPI_FIRMWARE_FRAMEBUFFER_SET_DEPTH,
|
||||||
|
buf_size: 4,
|
||||||
|
req_resp_size: 0,
|
||||||
|
buf: [depth],
|
||||||
|
},
|
||||||
|
// Set virtual offset. Returns `(X, Y)` in pixel.
|
||||||
|
set_virtual_offset: PropertyMailboxTag {
|
||||||
|
id: RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_OFFSET,
|
||||||
|
buf_size: 8,
|
||||||
|
req_resp_size: 0,
|
||||||
|
buf: [0, 0],
|
||||||
|
},
|
||||||
|
// Allocate buffer. Returns `(base_address, size)` in bytes.
|
||||||
|
allocate: PropertyMailboxTag {
|
||||||
|
id: RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE,
|
||||||
|
buf_size: 8,
|
||||||
|
req_resp_size: 0,
|
||||||
|
buf: [0x1000, 0],
|
||||||
|
},
|
||||||
|
// Get pitch. Return bytes per line.
|
||||||
|
get_pitch: PropertyMailboxTag {
|
||||||
|
id: RPI_FIRMWARE_FRAMEBUFFER_GET_PITCH,
|
||||||
|
buf_size: 4,
|
||||||
|
req_resp_size: 0,
|
||||||
|
buf: [0],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let ret = send_request!(tags)?;
|
||||||
|
Ok(FramebufferInfo {
|
||||||
|
xres: ret.set_physical_size.buf[0],
|
||||||
|
yres: ret.set_physical_size.buf[1],
|
||||||
|
xres_virtual: ret.set_virtual_size.buf[0],
|
||||||
|
yres_virtual: ret.set_virtual_size.buf[1],
|
||||||
|
xoffset: ret.set_virtual_offset.buf[0],
|
||||||
|
yoffset: ret.set_virtual_offset.buf[1],
|
||||||
|
|
||||||
|
depth: ret.set_depth.buf[0],
|
||||||
|
pitch: ret.get_pitch.buf[0],
|
||||||
|
|
||||||
|
bus_addr: ret.allocate.buf[0],
|
||||||
|
screen_size: ret.allocate.buf[1],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use once::*;
|
use once::*;
|
||||||
|
|
||||||
|
pub mod fb;
|
||||||
pub mod irq;
|
pub mod irq;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
pub mod serial;
|
pub mod serial;
|
||||||
@ -10,16 +11,17 @@ pub mod mailbox;
|
|||||||
pub const IO_REMAP_BASE: usize = bcm2837::IO_BASE;
|
pub const IO_REMAP_BASE: usize = bcm2837::IO_BASE;
|
||||||
pub const IO_REMAP_END: usize = 0x40001000;
|
pub const IO_REMAP_END: usize = 0x40001000;
|
||||||
|
|
||||||
/// Some initializations must be done before other initializations.
|
/// Initialize serial port before other initializations.
|
||||||
pub fn init_early() {
|
pub fn init_serial_early() {
|
||||||
assert_has_not_been_called!("board::init must be called only once");
|
assert_has_not_been_called!("board::init must be called only once");
|
||||||
|
|
||||||
serial::SERIAL_PORT.lock().init();
|
serial::init();
|
||||||
|
|
||||||
println!("Hello Raspberry Pi!");
|
println!("Hello Raspberry Pi!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize raspi3 drivers
|
/// Initialize raspi3 drivers
|
||||||
pub fn init_driver() {
|
pub fn init_driver() {
|
||||||
|
fb::init();
|
||||||
timer::init();
|
timer::init();
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ use bcm2837::mini_uart::{MiniUart, MiniUartInterruptId};
|
|||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use spin::Mutex;
|
use spin::Mutex;
|
||||||
|
use once::*;
|
||||||
|
|
||||||
/// Struct to get a global SerialPort interface
|
/// Struct to get a global SerialPort interface
|
||||||
pub struct SerialPort {
|
pub struct SerialPort {
|
||||||
@ -22,6 +23,8 @@ impl SerialPort {
|
|||||||
|
|
||||||
/// Init a newly created SerialPort, can only be called once.
|
/// Init a newly created SerialPort, can only be called once.
|
||||||
pub fn init(&mut self) {
|
pub fn init(&mut self) {
|
||||||
|
assert_has_not_been_called!("SerialPort::init must be called only once");
|
||||||
|
|
||||||
self.mu.init();
|
self.mu.init();
|
||||||
super::irq::register_irq(super::irq::Interrupt::Aux, handle_serial_irq);
|
super::irq::register_irq(super::irq::Interrupt::Aux, handle_serial_irq);
|
||||||
}
|
}
|
||||||
@ -78,6 +81,11 @@ fn handle_serial_irq() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static!{
|
lazy_static! {
|
||||||
pub static ref SERIAL_PORT: Mutex<SerialPort> = Mutex::new(SerialPort::new());
|
pub static ref SERIAL_PORT: Mutex<SerialPort> = Mutex::new(SerialPort::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn init() {
|
||||||
|
SERIAL_PORT.lock().init();
|
||||||
|
}
|
||||||
|
@ -18,15 +18,13 @@ global_asm!(include_str!("boot/boot.S"));
|
|||||||
#[no_mangle] // don't mangle the name of this function
|
#[no_mangle] // don't mangle the name of this function
|
||||||
pub extern "C" fn rust_main() -> ! {
|
pub extern "C" fn rust_main() -> ! {
|
||||||
memory::init_mmu_early(); // Enable mmu and paging
|
memory::init_mmu_early(); // Enable mmu and paging
|
||||||
|
board::init_serial_early();
|
||||||
|
|
||||||
crate::logging::init();
|
crate::logging::init();
|
||||||
|
|
||||||
board::init_early();
|
|
||||||
println!("{}", LOGO);
|
|
||||||
|
|
||||||
interrupt::init();
|
interrupt::init();
|
||||||
memory::init();
|
memory::init();
|
||||||
driver::init();
|
driver::init();
|
||||||
|
println!("{}", LOGO);
|
||||||
|
|
||||||
crate::process::init();
|
crate::process::init();
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#![feature(lang_items)]
|
#![feature(lang_items)]
|
||||||
#![feature(alloc)]
|
#![feature(alloc)]
|
||||||
#![feature(naked_functions)]
|
#![feature(naked_functions)]
|
||||||
|
#![feature(untagged_unions)]
|
||||||
#![feature(asm)]
|
#![feature(asm)]
|
||||||
#![feature(optin_builtin_traits)]
|
#![feature(optin_builtin_traits)]
|
||||||
#![feature(panic_info_message)]
|
#![feature(panic_info_message)]
|
||||||
@ -8,6 +9,7 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
// just keep it ...
|
// just keep it ...
|
||||||
|
#[macro_use]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
pub use crate::process::{processor, new_kernel_context};
|
pub use crate::process::{processor, new_kernel_context};
|
||||||
|
Loading…
Reference in New Issue
Block a user