use crate::cache::{clean_range, invalidate_range};
use crate::pac::VCMAILBOX;
const CHANNEL_PROPERTY_TAGS: u32 = 8;
const MAX_VALUE_WORDS: usize = 4;
const BUFFER_WORDS: usize = 2 + 3 + MAX_VALUE_WORDS + 1;
const RESPONSE_BIT: u32 = 1 << 31;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
AddressMismatch,
RequestFailed,
TagNotAnswered,
ValueTooLarge,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum ClockId {
Emmc = 1,
Uart = 2,
Arm = 3,
Core = 4,
V3d = 5,
H264 = 6,
Isp = 7,
Sdram = 8,
Pixel = 9,
Pwm = 10,
#[cfg(feature = "bcm2711")]
Emmc2 = 12,
}
pub const THROTTLED_UNDER_VOLTAGE: u32 = 1 << 0;
pub const THROTTLED_ARM_FREQ_CAPPED: u32 = 1 << 1;
pub const THROTTLED_NOW: u32 = 1 << 2;
pub const THROTTLED_SOFT_TEMP_LIMIT: u32 = 1 << 3;
pub const THROTTLED_UNDER_VOLTAGE_EVER: u32 = 1 << 16;
pub const THROTTLED_ARM_FREQ_CAPPED_EVER: u32 = 1 << 17;
pub const THROTTLED_EVER: u32 = 1 << 18;
pub const THROTTLED_SOFT_TEMP_LIMIT_EVER: u32 = 1 << 19;
pub const EXPANDER_WL_ON: u32 = 129;
pub const EXPANDER_BT_ON: u32 = 128;
pub const EXPANDER_CAM_GPIO0: u32 = 133;
pub const EXPANDER_CAM_GPIO1: u32 = 134;
pub const POWER_DOMAIN_UNICAM1: u32 = 13;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum PowerDeviceId {
SdCard = 0,
Uart0 = 1,
Uart1 = 2,
UsbHcd = 3,
I2c0 = 4,
I2c1 = 5,
I2c2 = 6,
Spi = 7,
Ccp2tx = 8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MemoryRegion {
pub base_address: u32,
pub size_bytes: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DisplaySize {
pub width: u32,
pub height: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Overscan {
pub top: u32,
pub bottom: u32,
pub left: u32,
pub right: u32,
}
impl Overscan {
pub fn is_zero(&self) -> bool {
self.top == 0 && self.bottom == 0 && self.left == 0 && self.right == 0
}
}
pub const EDID_BLOCK_LEN: usize = 128;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum PixelOrder {
Bgr = 0,
Rgb = 1,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Framebuffer {
pub address: u32,
pub size_bytes: u32,
pub pitch_bytes: u32,
pub width: u32,
pub height: u32,
pub virtual_height: u32,
pub depth_bits: u32,
}
impl Framebuffer {
pub fn pages(&self) -> u32 {
self.virtual_height / self.height
}
pub fn page_offset_bytes(&self, page: u32) -> u32 {
page * self.height * self.pitch_bytes
}
pub fn flush(&self) {
clean_range(self.address, self.size_bytes as usize);
}
pub fn flush_page(&self, page: u32) {
if page >= self.pages() {
return;
}
clean_range(
self.address + self.page_offset_bytes(page),
(self.height * self.pitch_bytes) as usize,
);
}
}
pub struct Mailbox {
vcmailbox: VCMAILBOX,
}
impl Mailbox {
pub fn new(vcmailbox: VCMAILBOX) -> Self {
Self { vcmailbox }
}
pub fn firmware_revision(&mut self) -> Result<u32, Error> {
let response = self.property_call(0x0000_0001, &[], 1)?;
Ok(response[0])
}
pub fn board_revision(&mut self) -> Result<u32, Error> {
let response = self.property_call(0x0001_0002, &[], 1)?;
Ok(response[0])
}
pub fn board_serial(&mut self) -> Result<u64, Error> {
let response = self.property_call(0x0001_0004, &[], 2)?;
Ok((response[0] as u64) | ((response[1] as u64) << 32))
}
pub fn mac_address(&mut self) -> Result<[u8; 6], Error> {
let response = self.property_call(0x0001_0003, &[], 2)?;
let low = response[0].to_le_bytes();
let high = response[1].to_le_bytes();
Ok([low[0], low[1], low[2], low[3], high[0], high[1]])
}
pub fn arm_memory(&mut self) -> Result<MemoryRegion, Error> {
self.memory_region(0x0001_0005)
}
pub fn vc_memory(&mut self) -> Result<MemoryRegion, Error> {
self.memory_region(0x0001_0006)
}
pub fn clock_rate_hz(&mut self, clock: ClockId) -> Result<u32, Error> {
let response = self.property_call(0x0003_0002, &[clock as u32], 2)?;
Ok(response[1])
}
pub fn max_clock_rate_hz(&mut self, clock: ClockId) -> Result<u32, Error> {
let response = self.property_call(0x0003_0004, &[clock as u32], 2)?;
Ok(response[1])
}
pub fn set_clock_rate_hz(&mut self, clock: ClockId, rate_hz: u32) -> Result<u32, Error> {
let response = self.property_call(0x0003_8002, &[clock as u32, rate_hz, 0], 3)?;
Ok(response[1])
}
pub fn set_clock_state(&mut self, clock: ClockId, on: bool) -> Result<bool, Error> {
let response = self.property_call(0x0003_8001, &[clock as u32, on as u32], 2)?;
Ok(response[1] & 0b10 == 0)
}
pub fn temperature_millicelsius(&mut self) -> Result<u32, Error> {
let response = self.property_call(0x0003_0006, &[0], 2)?;
Ok(response[1])
}
pub fn throttled(&mut self) -> Result<u32, Error> {
let response = self.property_call(0x0003_0046, &[0], 2)?;
Ok(response[1])
}
pub fn set_power_domain(&mut self, domain: u32, on: bool) -> Result<bool, Error> {
let response = self.property_call(0x0003_8030, &[domain, on as u32], 2)?;
Ok(response[1] != 0)
}
pub fn set_enable_qpu(&mut self, enable: bool) -> Result<(), Error> {
self.property_call(0x0003_0012, &[enable as u32], 1)?;
Ok(())
}
pub fn set_power_state(&mut self, device: PowerDeviceId, on: bool) -> Result<bool, Error> {
let state = if on { 0b11 } else { 0b10 };
let response = self.property_call(0x0002_8001, &[device as u32, state], 2)?;
Ok(response[1] & 0b10 == 0 && response[1] & 0b1 == if on { 1 } else { 0 })
}
pub fn set_expander_gpio(&mut self, pin: u32, on: bool) -> Result<(), Error> {
self.property_call(0x0003_8041, &[pin, on as u32], 2)?;
Ok(())
}
pub fn get_expander_gpio(&mut self, pin: u32) -> Result<bool, Error> {
let response = self.property_call(0x0003_0041, &[pin, 0], 2)?;
Ok(response[1] != 0)
}
pub fn display_size(&mut self) -> Result<DisplaySize, Error> {
let response = self.property_call(0x0004_0003, &[], 2)?;
Ok(DisplaySize {
width: response[0],
height: response[1],
})
}
pub fn overscan(&mut self) -> Result<Overscan, Error> {
let response = self.property_call(0x0004_000A, &[], 4)?;
Ok(Overscan {
top: response[0],
bottom: response[1],
left: response[2],
right: response[3],
})
}
pub fn set_overscan(&mut self, overscan: Overscan) -> Result<Overscan, Error> {
let response = self.property_call(
0x0004_800A,
&[overscan.top, overscan.bottom, overscan.left, overscan.right],
4,
)?;
Ok(Overscan {
top: response[0],
bottom: response[1],
left: response[2],
right: response[3],
})
}
pub fn edid_block(&mut self, block: u32) -> Result<Option<[u8; EDID_BLOCK_LEN]>, Error> {
const VALUE_WORDS: usize = 2 + EDID_BLOCK_LEN / 4;
const WORDS: usize = 2 + 3 + VALUE_WORDS + 1;
#[repr(C, align(16))]
struct Buffer {
words: [u32; WORDS],
}
let mut buffer = Buffer { words: [0; WORDS] };
buffer.words[0] = (WORDS * 4) as u32;
buffer.words[1] = 0;
buffer.words[2] = 0x0003_0020;
buffer.words[3] = (VALUE_WORDS * 4) as u32;
buffer.words[4] = 0;
buffer.words[5] = block;
let address = &buffer as *const Buffer as u32;
clean_range(address, WORDS * 4);
let echoed = self.call_raw(CHANNEL_PROPERTY_TAGS, address);
if echoed != address {
return Err(Error::AddressMismatch);
}
invalidate_range(address, WORDS * 4);
if buffer.words[1] & RESPONSE_BIT == 0 {
return Err(Error::RequestFailed);
}
if buffer.words[4] & RESPONSE_BIT == 0 {
return Err(Error::TagNotAnswered);
}
if buffer.words[6] != 0 {
return Ok(None);
}
let mut bytes = [0u8; EDID_BLOCK_LEN];
for (word, chunk) in buffer.words[7..7 + EDID_BLOCK_LEN / 4]
.iter()
.zip(bytes.as_chunks_mut::<4>().0)
{
*chunk = word.to_le_bytes();
}
Ok(Some(bytes))
}
pub fn allocate_framebuffer(
&mut self,
width: u32,
height: u32,
depth_bits: u32,
pixel_order: PixelOrder,
) -> Result<Framebuffer, Error> {
self.allocate_framebuffer_paged(width, height, 1, depth_bits, pixel_order)
}
pub fn allocate_framebuffer_paged(
&mut self,
width: u32,
height: u32,
pages: u32,
depth_bits: u32,
pixel_order: PixelOrder,
) -> Result<Framebuffer, Error> {
const ALLOCATE_ALIGNMENT: u32 = 4096;
const WORDS: usize = 2 + (5 + 5 + 4 + 4 + 5 + 4) + 1;
#[repr(C, align(16))]
struct Buffer {
words: [u32; WORDS],
}
let virtual_height = height * pages.max(1);
let mut buffer = Buffer { words: [0; WORDS] };
buffer.words[0] = (WORDS * 4) as u32;
buffer.words[1] = 0;
buffer.words[2] = 0x0004_8003;
buffer.words[3] = 8;
buffer.words[4] = 0;
buffer.words[5] = width;
buffer.words[6] = height;
buffer.words[7] = 0x0004_8004;
buffer.words[8] = 8;
buffer.words[9] = 0;
buffer.words[10] = width;
buffer.words[11] = virtual_height;
buffer.words[12] = 0x0004_8005;
buffer.words[13] = 4;
buffer.words[14] = 0;
buffer.words[15] = depth_bits;
buffer.words[16] = 0x0004_8006;
buffer.words[17] = 4;
buffer.words[18] = 0;
buffer.words[19] = pixel_order as u32;
buffer.words[20] = 0x0004_0001;
buffer.words[21] = 8;
buffer.words[22] = 0;
buffer.words[23] = ALLOCATE_ALIGNMENT;
buffer.words[24] = 0;
buffer.words[25] = 0x0004_0008;
buffer.words[26] = 4;
buffer.words[27] = 0;
buffer.words[28] = 0;
let address = &buffer as *const Buffer as u32;
clean_range(address, WORDS * 4);
let echoed = self.call_raw(CHANNEL_PROPERTY_TAGS, address);
if echoed != address {
return Err(Error::AddressMismatch);
}
invalidate_range(address, WORDS * 4);
if buffer.words[1] & RESPONSE_BIT == 0 {
return Err(Error::RequestFailed);
}
for &code_index in &[4, 9, 14, 18, 22, 27] {
if buffer.words[code_index] & RESPONSE_BIT == 0 {
return Err(Error::TagNotAnswered);
}
}
Ok(Framebuffer {
address: from_vc_bus_address(buffer.words[23]),
size_bytes: buffer.words[24],
pitch_bytes: buffer.words[28],
width: buffer.words[5],
height: buffer.words[6],
virtual_height: buffer.words[11],
depth_bits: buffer.words[15],
})
}
pub fn set_virtual_offset(&mut self, x: u32, y: u32) -> Result<(), Error> {
self.property_call(0x0004_8009, &[x, y], 2)?;
Ok(())
}
pub fn wait_for_vsync(&mut self) -> Result<(), Error> {
self.property_call(0x0004_800e, &[0], 1)?;
Ok(())
}
pub fn vchiq_init(&mut self, slot_bus_address: u32) -> Result<u32, Error> {
let response = self.property_call(0x0004_8010, &[slot_bus_address], 1)?;
Ok(response[0])
}
pub fn set_touch_buffer_address(&mut self, address: u32) -> Result<(), Error> {
self.property_call(0x0004_801f, &[to_vc_bus_address(address)], 1)?;
Ok(())
}
fn memory_region(&mut self, tag: u32) -> Result<MemoryRegion, Error> {
let response = self.property_call(tag, &[], 2)?;
Ok(MemoryRegion {
base_address: response[0],
size_bytes: response[1],
})
}
fn property_call(
&mut self,
tag: u32,
request: &[u32],
response_words: usize,
) -> Result<[u32; MAX_VALUE_WORDS], Error> {
if request.len() > MAX_VALUE_WORDS || response_words > MAX_VALUE_WORDS {
return Err(Error::ValueTooLarge);
}
#[repr(C, align(16))]
struct Buffer {
words: [u32; BUFFER_WORDS],
}
let mut buffer = Buffer {
words: [0; BUFFER_WORDS],
};
buffer.words[0] = (BUFFER_WORDS * 4) as u32;
buffer.words[1] = 0;
buffer.words[2] = tag;
buffer.words[3] = (MAX_VALUE_WORDS * 4) as u32;
buffer.words[4] = 0;
buffer.words[5..5 + request.len()].copy_from_slice(request);
let address = &buffer as *const Buffer as u32;
clean_range(address, BUFFER_WORDS * 4);
let echoed = self.call_raw(CHANNEL_PROPERTY_TAGS, address);
if echoed != address {
return Err(Error::AddressMismatch);
}
invalidate_range(address, BUFFER_WORDS * 4);
if buffer.words[1] & RESPONSE_BIT == 0 {
return Err(Error::RequestFailed);
}
if buffer.words[4] & RESPONSE_BIT == 0 {
return Err(Error::TagNotAnswered);
}
let mut response = [0u32; MAX_VALUE_WORDS];
response[..response_words].copy_from_slice(&buffer.words[5..5 + response_words]);
Ok(response)
}
fn call_raw(&mut self, channel: u32, buffer_address: u32) -> u32 {
let bus_address = to_vc_bus_address(buffer_address);
let message = (bus_address & !0xF) | (channel & 0xF);
while self.vcmailbox.status1().read().bits() & (1 << 31) != 0 {}
unsafe {
self.vcmailbox.write().write_with_zero(|w| w.bits(message));
}
loop {
while self.vcmailbox.status0().read().empty().bit_is_set() {}
let response = self.vcmailbox.read().read().bits();
if response & 0xF == channel {
return (response & !0xF) - 0xC000_0000;
}
}
}
}
fn to_vc_bus_address(physical_address: u32) -> u32 {
(physical_address & 0x3FFF_FFFF) | 0xC000_0000
}
fn from_vc_bus_address(bus_address: u32) -> u32 {
bus_address & 0x3FFF_FFFF
}