#![no_std]
#![no_main]
use core::fmt::Write;
use rpi_hal::halt;
use rpi_hal::mailbox::{DisplayId, Mailbox, Outcome, PixelOrder};
use rpi_hal::{pac, uart::Uart};
const FALLBACK_WIDTH: u32 = 800;
const FALLBACK_HEIGHT: u32 = 480;
const DEPTH_BITS: u32 = 32;
const PREFERENCE: &[DisplayId] = &[DisplayId::MainLcd, DisplayId::Hdmi0];
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
let peripherals = unsafe { pac::Peripherals::steal() };
let mut uart = Uart::init(&peripherals.GPIO, peripherals.UART0);
let _ = writeln!(uart, "PANIC: {info}");
halt();
}
#[no_mangle]
pub extern "C" fn kmain() -> ! {
let peripherals = unsafe { pac::Peripherals::steal() };
let mut uart = Uart::init(&peripherals.GPIO, peripherals.UART0);
let mut mailbox = Mailbox::new(peripherals.VCMAILBOX);
report_displays(&mut mailbox, &mut uart);
let (width, height) = display_resolution(&mut mailbox, &mut uart);
let framebuffer = match mailbox.allocate_framebuffer(width, height, DEPTH_BITS, PixelOrder::Bgr)
{
Ok(framebuffer) => framebuffer,
Err(e) => {
let _ = writeln!(uart, "framebuffer allocation failed: {e:?}");
halt();
}
};
let _ = writeln!(
uart,
"framebuffer: {}x{} @ {}bpp, address 0x{:08x}, pitch {} bytes, size {} bytes",
framebuffer.width,
framebuffer.height,
framebuffer.depth_bits,
framebuffer.address,
framebuffer.pitch_bytes,
framebuffer.size_bytes
);
draw_color_bars(&framebuffer);
framebuffer.flush();
let _ = writeln!(uart, "pattern drawn -- check the display");
halt();
}
fn report_displays(mailbox: &mut Mailbox, uart: &mut Uart) {
let selection = mailbox.select_display(PREFERENCE);
if selection.outcome() == Outcome::Sole {
let _ = match selection.reported_count() {
Some(count) => writeln!(
uart,
"display: firmware reports {count}, using {:?}",
selection.chosen()
),
None => writeln!(
uart,
"display: firmware does not report a display count, using {:?}",
selection.chosen()
),
};
return;
}
let _ = writeln!(
uart,
"display: firmware reports {:?} displays",
selection.reported_count()
);
for (number, id) in selection.attached() {
let _ = writeln!(uart, "display: {number} is {id:?}");
}
let _ = match selection.outcome() {
Outcome::Preferred => writeln!(uart, "display: drawing on {:?}", selection.chosen()),
Outcome::Firmwares => writeln!(
uart,
"display: no preference set, using whichever the firmware selected ({:?})",
selection.chosen()
),
Outcome::FellBack => writeln!(
uart,
"display: none of {PREFERENCE:?} is attached, falling back to display 0 ({:?})",
selection.chosen()
),
Outcome::Sole => unreachable!("handled above"),
};
}
fn display_resolution(mailbox: &mut Mailbox, uart: &mut Uart) -> (u32, u32) {
let mode = match mailbox.display_mode() {
Ok(mode) => mode,
Err(rpi_hal::mailbox::Error::NoDisplayMode) => {
let _ = writeln!(
uart,
"display: firmware has no mode configured, \
falling back to {FALLBACK_WIDTH}x{FALLBACK_HEIGHT}"
);
return (FALLBACK_WIDTH, FALLBACK_HEIGHT);
}
Err(e) => {
let _ = writeln!(
uart,
"display: no size from firmware ({e:?}), \
falling back to {FALLBACK_WIDTH}x{FALLBACK_HEIGHT}"
);
return (FALLBACK_WIDTH, FALLBACK_HEIGHT);
}
};
if !mode.overscan.is_zero() {
let _ = writeln!(
uart,
"overscan: top {} bottom {} left {} right {}",
mode.overscan.top, mode.overscan.bottom, mode.overscan.left, mode.overscan.right
);
}
if mode.overscan.is_zero() {
let _ = writeln!(uart, "display: full mode is {}x{}", mode.width, mode.height);
} else if mode.overscan_cleared {
let _ = writeln!(
uart,
"overscan: cleared, full mode is {}x{}",
mode.width, mode.height
);
} else {
let _ = writeln!(
uart,
"overscan: border stayed, drawing inside it at {}x{}",
mode.width, mode.height
);
}
(mode.width, mode.height)
}
fn draw_color_bars(framebuffer: &rpi_hal::mailbox::Framebuffer) {
const COLORS: [u32; 8] = [
0x00FF_FFFF, 0x00FF_FF00, 0x0000_FFFF, 0x0000_FF00, 0x00FF_00FF, 0x00FF_0000, 0x0000_00FF, 0x0000_0000, ];
let base = framebuffer.address as *mut u32;
let pitch_pixels = framebuffer.pitch_bytes / 4;
let bar_width = framebuffer.width / COLORS.len() as u32;
for y in 0..framebuffer.height {
for x in 0..framebuffer.width {
let bar = (x / bar_width).min(COLORS.len() as u32 - 1) as usize;
let offset = y * pitch_pixels + x;
unsafe { base.add(offset as usize).write_volatile(COLORS[bar]) };
}
}
}