use super::command_word::CommandWord;
use super::strfmt;
use crate::commands::options;
use crate::commands::waiter::{PollingWaiter, Waiter, WaiterError};
use crate::interface::Interface;
use crate::low_level::LowLevel;
use crate::memory::{Ptr, Slice};
use crate::models::Model;
use crate::registers::Register;
pub type Result<T, M, I, W> = core::result::Result<T, Error<M, I, W>>;
pub struct Coprocessor<M: Model, I: Interface, W: Waiter<M, I>> {
ll: LowLevel<M, I>,
wait: W,
known_space: u16,
}
impl<M: Model, I: Interface, W: Waiter<M, I>> Coprocessor<M, I, W> {
pub fn ram_ptr(&self, offset: u32) -> Ptr<M::MainMem> {
use crate::memory::MemoryRegion;
M::MainMem::ptr(offset)
}
pub fn new_display_list<F>(&mut self, f: F) -> Result<(), M, I, W>
where
F: FnOnce(&mut Self) -> Result<(), M, I, W>,
{
self.start_display_list()?;
f(self)?;
self.display_list_swap()
}
pub fn trigger_cmdflag_interrupt(
&mut self,
delay: core::time::Duration,
) -> Result<(), M, I, W> {
self.write_stream(8, |cp| {
let delay = delay.as_millis();
let delay = if delay > (core::u32::MAX as u128) {
core::u32::MAX
} else {
delay as u32
};
cp.write_to_buffer(0xFFFFFF02 as u32)?;
cp.write_to_buffer(delay)
})
}
pub fn cold_start(&mut self) -> Result<(), M, I, W> {
self.write_stream(4, |cp| cp.write_to_buffer(0xFFFFFF32 as u32))
}
pub fn append_display_list_from_main_mem<S: Into<Slice<M::MainMem>>>(
&mut self,
slice: S,
) -> Result<(), M, I, W> {
let slice: Slice<M::MainMem> = slice.into();
let start = slice.start().to_raw();
let count = slice.len();
self.write_stream(12, |cp| {
cp.write_to_buffer(0xFFFFFF1E as u32)?;
cp.write_to_buffer(start)?;
cp.write_to_buffer(count)
})
}
pub fn write_register(&mut self, reg: Register, v: u32) -> Result<(), M, I, W> {
let ptr_raw = reg.ptr::<M>().to_raw();
self.write_stream(16, |cp| {
cp.write_to_buffer(0xFFFFFF1A as u32)?;
cp.write_to_buffer(ptr_raw)?;
cp.write_to_buffer(4)?;
cp.write_to_buffer(v)
})
}
pub fn write_memory<'a, IntoIter, R>(
&mut self,
to: Ptr<R>,
from: IntoIter,
) -> Result<(), M, I, W>
where
IntoIter: core::iter::IntoIterator<Item = &'a u8>,
IntoIter::IntoIter: core::iter::Iterator<Item = &'a u8> + core::iter::ExactSizeIterator,
R: crate::memory::MemoryRegion + crate::memory::HostAccessible,
{
let ptr_raw = to.to_raw();
let iter = from.into_iter();
let len = iter.len() as u32;
self.write_stream(12, |cp| {
cp.write_to_buffer(0xFFFFFF1A as u32)?;
cp.write_to_buffer(ptr_raw)?;
cp.write_to_buffer(len)
})?;
self.write_bytes_chunked(iter)
}
pub fn write_memory_inflate<'a, IntoIter, R>(
&mut self,
to: Ptr<R>,
from: IntoIter,
) -> Result<(), M, I, W>
where
IntoIter: core::iter::IntoIterator<Item = &'a u8>,
IntoIter::IntoIter: core::iter::Iterator<Item = &'a u8>,
R: crate::memory::MemoryRegion + crate::memory::HostAccessible,
{
let ptr_raw = to.to_raw();
let iter = from.into_iter();
self.write_stream(8, |cp| {
cp.write_to_buffer(0xFFFFFF22 as u32)?;
cp.write_to_buffer(ptr_raw)
})?;
self.write_bytes_chunked(iter)
}
pub fn write_memory_image<'a, IntoIter, R>(
&mut self,
to: Ptr<R>,
from: IntoIter,
opts: options::LoadImage,
) -> Result<(), M, I, W>
where
IntoIter: core::iter::IntoIterator<Item = &'a u8>,
IntoIter::IntoIter: core::iter::Iterator<Item = &'a u8>,
R: crate::memory::MemoryRegion + crate::memory::HostAccessible,
{
let ptr_raw = to.to_raw();
let iter = from.into_iter();
self.write_stream(12, |cp| {
cp.write_to_buffer(0xFFFFFF24 as u32)?;
cp.write_to_buffer(ptr_raw)?;
cp.write_to_buffer(opts.to_raw())
})?;
self.write_bytes_chunked(iter)
}
pub fn show_testcard(&mut self) -> Result<(), M, I, W> {
self.write_stream(4, |cp| cp.write_to_buffer(0xFFFFFF61 as u32))
}
pub fn show_manufacturer_logo(&mut self) -> Result<(), M, I, W> {
self.write_stream(4, |cp| cp.write_to_buffer(0xFFFFFF31 as u32))
}
pub fn start_spinner(&mut self) -> Result<(), M, I, W> {
self.write_stream(20, |cp| {
cp.write_to_buffer(0xFFFFFF16 as u32)?;
cp.write_to_buffer(1000)?;
cp.write_to_buffer(1000)?;
cp.write_to_buffer(0)?;
cp.write_to_buffer(0)
})
}
pub fn start_display_list(&mut self) -> Result<(), M, I, W> {
self.write_stream(4, |cp| cp.write_to_buffer(0xFFFFFF00 as u32))
}
pub fn display_list_swap(&mut self) -> Result<(), M, I, W> {
self.write_stream(4, |cp| cp.write_to_buffer(0xFFFFFF01 as u32))
}
pub fn draw_button<Rect: Into<crate::graphics::WidgetRect>>(
&mut self,
rect: Rect,
msg: strfmt::Message<M::MainMem>,
font: options::FontRef,
options: options::Button,
) -> Result<(), M, I, W> {
let rect: crate::graphics::WidgetRect = rect.into();
self.write_stream(28, |cp| {
cp.write_to_buffer(0xFFFFFF0D as u32)?;
cp.write_to_buffer((rect.x, rect.y))?;
cp.write_to_buffer((rect.w, rect.h))?;
let font_raw = font.to_raw() as u16;
let opts_raw = maybe_opt_format(options.to_raw(), &msg) as u16;
cp.write_to_buffer((font_raw, opts_raw))
})?;
self.write_fmt_message(&msg)
}
pub fn draw_text<Pos: Into<crate::graphics::WidgetPos>>(
&mut self,
pos: Pos,
msg: strfmt::Message<M::MainMem>,
font: options::FontRef,
options: options::Text,
) -> Result<(), M, I, W> {
let pos: crate::graphics::WidgetPos = pos.into();
self.write_stream(28, |cp| {
cp.write_to_buffer(0xFFFFFF0C as u32)?;
cp.write_to_buffer((pos.x, pos.y))?;
let font_raw = font.to_raw() as u16;
let opts_raw = maybe_opt_format(options.to_raw(), &msg) as u16;
cp.write_to_buffer((font_raw, opts_raw))
})?;
self.write_fmt_message(&msg)
}
pub fn append_display_list(&mut self, cmd: crate::display_list::DLCmd) -> Result<(), M, I, W> {
self.write_stream(4, |cp| cp.write_to_buffer(cmd.as_raw()))
}
pub fn append_raw_word(&mut self, word: u32) -> Result<(), M, I, W> {
self.write_stream(4, |cp| cp.write_to_buffer(word))
}
pub fn wait_microseconds(&mut self, delay: u32) -> Result<(), M, I, W> {
self.write_stream(8, |cp| {
cp.write_to_buffer(0xFFFFFF65 as u32)?;
cp.write_to_buffer(delay)
})
}
pub fn wait_video_scanout(&mut self) -> Result<(), M, I, W> {
self.write_stream(4, |cp| cp.write_to_buffer(0xFFFFFF42 as u32))
}
}
impl<M: Model, I: Interface, W: Waiter<M, I>> Coprocessor<M, I, W> {
#[inline]
pub fn space_when_empty() -> u16 {
4092
}
pub fn block_until_idle(&mut self) -> Result<(), M, I, W> {
self.ensure_space(Self::space_when_empty())
}
pub fn block_until_video_scanout(&mut self) -> Result<(), M, I, W> {
self.wait_video_scanout()?;
self.block_until_idle()
}
pub fn block_read_register(&mut self, reg: crate::registers::Register) -> Result<u32, M, I, W> {
let ptr = M::reg_ptr(reg);
self.write_stream(12, |cp| {
cp.write_to_buffer(0xFFFFFF19 as u32)?;
cp.write_to_buffer(ptr.to_raw())?;
cp.write_to_buffer(0xf0f0f0f0 as u32) })?;
self.block_for_output_values(|ll, addr| {
let result_ptr = addr - 4;
ll.rd32(result_ptr)
})
}
pub fn block_for_memory_crc<R, S>(&mut self, region: S) -> Result<u32, M, I, W>
where
R: crate::memory::MemoryRegion,
S: Into<Slice<R>>,
{
let region: Slice<R> = region.into();
let ptr = region.start();
let len = region.len();
self.write_stream(12, |cp| {
cp.write_to_buffer(0xFFFFFF18 as u32)?;
cp.write_to_buffer(ptr.to_raw())?;
cp.write_to_buffer(len)?;
cp.write_to_buffer(0xf0f0f0f0 as u32) })?;
self.block_for_output_values(|ll, addr| {
let result_ptr = addr - 4;
ll.rd32(result_ptr)
})
}
fn block_for_output_values<F, R>(&mut self, f: F) -> Result<R, M, I, W>
where
R: Sized,
F: FnOnce(
&mut LowLevel<M, I>,
Ptr<M::DisplayListMem>,
) -> core::result::Result<R, crate::error::Error<I>>,
{
let ptr_reg = crate::registers::Register::CMD_WRITE;
let stopped = self.stop_stream()?;
let write_addr = {
let ll = self.borrow_low_level(&stopped);
ll.rd32(M::reg_ptr(ptr_reg))?
};
self.ensure_space_stopped(&stopped, Self::space_when_empty())?;
let result = {
let ll = self.borrow_low_level(&stopped);
f(ll, Ptr::new(write_addr))
};
self.start_stream(stopped)?;
Error::general_result(result)
}
}
impl<M: Model, I: Interface, W: Waiter<M, I>> Coprocessor<M, I, W> {
pub fn new(ei: I, wait: W) -> Result<Self, M, I, W> {
let mut ll = crate::low_level::LowLevel::new(ei);
ll.wr8(ll.reg_ptr(Register::CPURESET), 0b001)?;
ll.wr8(ll.reg_ptr(Register::CPURESET), 0b000)?;
let mut ret = Self {
ll: ll,
wait: wait,
known_space: 0,
};
let stopped = StoppedStream;
ret.synchronize(&stopped)?;
ret.start_stream(stopped)?;
Ok(ret)
}
pub fn with_new_waiter<W2, F>(self, f: F) -> Coprocessor<M, I, W2>
where
W2: Waiter<M, I>,
F: FnOnce(W) -> W2,
{
let ll = self.ll;
let old_wait = self.wait;
let old_known_space = self.known_space;
let new_wait = f(old_wait);
Coprocessor {
ll: ll,
wait: new_wait,
known_space: old_known_space,
}
}
pub fn take_interface(mut self) -> Result<I, M, I, W> {
self.stop_stream()?;
return Ok(self.ll.take_interface());
}
pub fn with_interface<R, F: FnOnce(&mut I) -> Result<R, M, I, W>>(
&mut self,
f: F,
) -> Result<R, M, I, W> {
let stopped = self.stop_stream()?;
let result = {
let ei = self.ll.borrow_interface();
f(ei)
};
self.synchronize(&stopped)?;
self.start_stream(stopped)?;
result
}
fn synchronize(&mut self, _stopped: &StoppedStream) -> Result<(), M, I, W> {
let known_space = self.ll.rd16(self.ll.reg_ptr(Register::CMDB_SPACE))?;
self.known_space = known_space;
Ok(())
}
fn borrow_interface<'a>(&'a mut self, stopped: &StoppedStream) -> &'a mut I {
let ll = self.borrow_low_level(stopped);
ll.borrow_interface()
}
fn borrow_low_level<'a>(&'a mut self, _stopped: &StoppedStream) -> &'a mut LowLevel<M, I> {
&mut self.ll
}
fn borrow_low_level_and_waiter<'a>(
&'a mut self,
_stopped: &StoppedStream,
) -> (&'a mut LowLevel<M, I>, &'a mut W) {
(&mut self.ll, &mut self.wait)
}
fn start_stream(&mut self, stopped: StoppedStream) -> Result<(), M, I, W> {
let ei = self.borrow_interface(&stopped);
Self::interface_result(ei.begin_write(M::reg_ptr(Register::CMDB_WRITE).to_raw()))
}
fn stop_stream(&mut self) -> Result<StoppedStream, M, I, W> {
let ei = self.ll.borrow_interface();
Self::interface_result(ei.end_write())?;
Ok(StoppedStream)
}
fn write_stream<F: FnOnce(&mut Self) -> Result<(), M, I, W>>(
&mut self,
len: u16,
f: F,
) -> Result<(), M, I, W> {
self.ensure_space(len)?;
f(self)?;
Ok(())
}
fn ensure_space(&mut self, need: u16) -> Result<(), M, I, W> {
if self.known_space >= need {
return Ok(());
}
let stopped = self.stop_stream()?;
self.ensure_space_stopped(&stopped, need)?;
self.start_stream(stopped)?;
Ok(())
}
fn ensure_space_stopped(&mut self, stopped: &StoppedStream, need: u16) -> Result<(), M, I, W> {
if self.known_space >= need {
return Ok(());
}
let (ll, wait) = self.borrow_low_level_and_waiter(&stopped);
match wait.wait_for_space(ll, need) {
Ok(known_space) => {
self.known_space = known_space;
}
Err(err) => {
self.known_space = 0;
return Err(match err {
WaiterError::Comm(err) => Error::Waiter(err),
WaiterError::Fault => Error::Fault,
});
}
}
Ok(())
}
fn write_to_buffer<V: Into<CommandWord>>(&mut self, v: V) -> Result<(), M, I, W> {
let v: CommandWord = v.into();
let v = v.to_raw();
let data: [u8; 4] = [v as u8, (v >> 8) as u8, (v >> 16) as u8, (v >> 24) as u8];
let ei = self.ll.borrow_interface();
let result = Self::interface_result(ei.continue_write(&data));
if self.known_space >= 4 {
self.known_space -= 4;
}
result
}
fn write_bytes_chunked<'a, Iter>(&mut self, v: Iter) -> Result<(), M, I, W>
where
Iter: core::iter::Iterator<Item = &'a u8>,
{
for word in super::command_word::command_words_for_bytes_iter(v) {
self.ensure_space(4)?;
self.write_to_buffer(word.to_raw())?;
}
Ok(())
}
fn write_fmt_message<R: crate::memory::MainMem>(
&mut self,
msg: &strfmt::Message<'_, '_, R>,
) -> Result<(), M, I, W> {
use strfmt::Argument::*;
self.write_bytes_chunked(msg.fmt.into_iter())?;
if let Some(args) = msg.args {
let arg_space = (args.len() * 4) as u16;
self.ensure_space(arg_space)?;
for arg in args {
let raw: u32 = match *arg {
Int(v) => unsafe { core::mem::transmute(v) },
UInt(v) => v,
Char(v) => v as u32,
String(ptr) => ptr.to_raw(),
};
self.write_to_buffer(raw)?;
}
}
Ok(())
}
fn interface_result<T>(result: core::result::Result<T, I::Error>) -> Result<T, M, I, W> {
match result {
Ok(v) => Ok(v),
Err(err) => Err(Error::Interface(err)),
}
}
}
impl<M: Model, I: Interface> Coprocessor<M, I, PollingWaiter<M, I>> {
pub fn new_polling(ei: I) -> Result<Self, M, I, PollingWaiter<M, I>> {
let w: PollingWaiter<M, I> = PollingWaiter::new();
Self::new(ei, w)
}
}
impl<M, I, W> Coprocessor<M, I, W>
where
M: Model + crate::models::WithCommandErrMem,
I: Interface,
W: Waiter<M, I>,
{
pub fn coprocessor_fault_msg(&mut self) -> Result<FaultMessage<M::CommandErrMem>, M, I, W> {
use crate::memory::{CommandErrMem, MemoryRegion};
use crate::models::WithCommandErrMem;
let stopped = self.stop_stream()?;
let mut raw = <<M as WithCommandErrMem>::CommandErrMem as CommandErrMem>::RawMessage::new();
{
let into = raw.as_storage_bytes();
let ll = self.borrow_low_level(&stopped);
let addr = <<M as WithCommandErrMem>::CommandErrMem as MemoryRegion>::ptr(0);
ll.rd8s(addr, into)?;
}
self.start_stream(stopped)?;
Ok(FaultMessage::new(raw))
}
}
impl<M, I, W> Coprocessor<M, I, W>
where
M: Model + crate::models::WithCommandErrMem,
I: Interface,
W: Waiter<M, I>,
{
pub fn use_api_level_1(&mut self) -> Result<(), M, I, W> {
self.write_stream(8, |cp| {
cp.write_to_buffer(0xFFFFFF63 as u32)?;
cp.write_to_buffer(1 as u32)
})
}
}
impl<M, I, W> Coprocessor<M, I, W>
where
M: Model + crate::models::WithCommandErrMem,
I: Interface,
W: Waiter<M, I>,
{
pub fn use_api_level_2(&mut self) -> Result<(), M, I, W> {
self.write_stream(8, |cp| {
cp.write_to_buffer(0xFFFFFF63 as u32)?;
cp.write_to_buffer(2 as u32)
})
}
}
#[doc(inline)]
pub use crate::error::CoprocessorError as Error;
#[derive(Debug, Clone)]
pub struct FaultMessage<R: crate::memory::CommandErrMem>(R::RawMessage);
impl<R: crate::memory::CommandErrMem> FaultMessage<R> {
fn new(raw: R::RawMessage) -> Self {
Self(raw)
}
pub fn as_bytes<'a>(&'a self) -> &'a [u8] {
self.0.as_bytes()
}
}
#[doc(hide)]
pub trait FaultMessageRaw {
fn new() -> Self;
fn as_bytes<'a>(&'a self) -> &'a [u8];
fn as_storage_bytes<'a>(&'a mut self) -> &'a mut [u8];
}
impl FaultMessageRaw for [u8; 128] {
fn new() -> Self {
[0; 128]
}
fn as_bytes<'a>(&'a self) -> &'a [u8] {
let all = &self[..];
for (i, b) in all.iter().enumerate() {
if *b == 0 {
return &all[0..i];
}
}
all
}
fn as_storage_bytes<'a>(&'a mut self) -> &'a mut [u8] {
&mut self[..]
}
}
struct StoppedStream;
impl<M, I, W> crate::display_list::Builder for Coprocessor<M, I, W>
where
M: Model,
I: Interface,
W: Waiter<M, I>,
{
type Error = Error<M, I, W>;
type Model = M;
fn append_raw_command(&mut self, raw: u32) -> core::result::Result<(), Self::Error> {
self.append_raw_word(raw)
}
fn append_command(
&mut self,
cmd: crate::display_list::DLCmd,
) -> core::result::Result<(), Self::Error> {
self.append_display_list(cmd)
}
}
fn maybe_opt_format<R: crate::memory::MainMem>(
given: u32,
msg: &strfmt::Message<'_, '_, R>,
) -> u32 {
const OPT_FORMAT: u32 = 4096;
if msg.needs_format() {
given | OPT_FORMAT
} else {
given
}
}