use esp_hal::Blocking;
#[cfg(feature = "iram")]
use esp_hal::ram;
use super::BcmBuf;
use crate::Hub75Error;
#[cfg(hub75_use_lcd_cam)]
use crate::framebuffer::WordSize;
cfg_select! {
hub75_use_i2s_parallel => {
use esp_hal::i2s::parallel::{I2sParallel, I2sParallelTransfer};
pub(crate) type TxDriver = I2sParallel<'static, Blocking>;
pub(crate) type TxXfer<B> = I2sParallelTransfer<'static, B, Blocking>;
}
hub75_use_parl_io => {
use esp_hal::parl_io::{ParlIoTx, ParlIoTxTransfer};
pub(crate) type TxDriver = ParlIoTx<'static, Blocking>;
pub(crate) type TxXfer<B> = ParlIoTxTransfer<'static, B, Blocking>;
}
hub75_use_lcd_cam => {
use esp_hal::lcd_cam::lcd::i8080::{I8080, I8080Transfer};
pub(crate) type TxDriver = I8080<'static, Blocking>;
pub(crate) type TxXfer<B> = I8080Transfer<'static, B, Blocking>;
}
_ => {
compile_error!("no HUB75 backend selected: enable exactly one chip feature");
}
}
pub(crate) type TxTransfer = TxXfer<BcmBuf>;
#[cfg(all(hub75_use_parl_io, esp32c5))]
pub(crate) const PARL_IO_DUMMY_TRANSFER_LEN: usize = 0;
enum TransferPhase {
Idle(TxDriver, BcmBuf),
InFlight(TxTransfer),
Error(Hub75Error, TxDriver, BcmBuf),
Transitioning,
}
pub(crate) struct Transfer {
phase: TransferPhase,
#[cfg(hub75_use_lcd_cam)]
word_size: WordSize,
}
impl Transfer {
pub(crate) fn new(
tx: TxDriver,
buf: BcmBuf,
#[cfg(hub75_use_lcd_cam)] word_size: WordSize,
) -> Self {
Self {
phase: TransferPhase::Idle(tx, buf),
#[cfg(hub75_use_lcd_cam)]
word_size,
}
}
pub(crate) fn is_in_flight(&self) -> bool {
matches!(self.phase, TransferPhase::InFlight(..))
}
pub(crate) fn is_idle(&self) -> bool {
matches!(
self.phase,
TransferPhase::Idle(..) | TransferPhase::Error(..)
)
}
pub(crate) fn error(&self) -> Option<Hub75Error> {
match &self.phase {
TransferPhase::Error(err, _, _) => Some(*err),
_ => None,
}
}
pub(crate) fn buf_mut(&mut self) -> &mut BcmBuf {
match &mut self.phase {
TransferPhase::Idle(_, buf) | TransferPhase::Error(_, _, buf) => buf,
TransferPhase::InFlight(_) | TransferPhase::Transitioning => {
panic!("Transfer::buf_mut() called while a transfer is in flight")
}
}
}
#[cfg_attr(feature = "iram", ram)]
pub(crate) fn start(&mut self) -> Result<(), Hub75Error> {
#[cfg(hub75_use_lcd_cam)]
use esp_hal::lcd_cam::lcd::i8080::Command;
let (tx, buf) = match core::mem::replace(&mut self.phase, TransferPhase::Transitioning) {
TransferPhase::Idle(tx, buf) | TransferPhase::Error(_, tx, buf) => (tx, buf),
other => {
self.phase = other;
return Err(Hub75Error::AlreadyRunning);
}
};
#[cfg(hub75_use_parl_io)]
let transfer_len = cfg_select! {
any(feature = "circular-dma", esp32c5) => PARL_IO_DUMMY_TRANSFER_LEN,
_ => buf.current_transfer_len(),
};
let xfer_result = cfg_select! {
hub75_use_i2s_parallel => {
tx.send(buf)
.map_err(|(err, tx, buf)| (Hub75Error::Dma(err), tx, buf))
}
hub75_use_parl_io => {
tx.write(transfer_len, buf)
.map_err(|(err, tx, buf)| (Hub75Error::ParlIo(err), tx, buf))
}
hub75_use_lcd_cam => {
match self.word_size {
WordSize::Eight => tx.send(Command::<u8>::None, 0, buf),
WordSize::Sixteen => tx.send(Command::<u16>::None, 0, buf),
}
.map_err(|(err, tx, buf)| (Hub75Error::Dma(err), tx, buf))
}
_ => {
unreachable!()
}
};
match xfer_result {
Ok(xfer) => {
self.phase = TransferPhase::InFlight(xfer);
Ok(())
}
Err((err, tx, buf)) => {
self.phase = TransferPhase::Error(err, tx, buf);
Err(err)
}
}
}
#[cfg_attr(feature = "iram", ram)]
pub(crate) fn finish(&mut self) -> Result<(), Hub75Error> {
let xfer = match core::mem::replace(&mut self.phase, TransferPhase::Transitioning) {
TransferPhase::InFlight(xfer) => xfer,
other => {
self.phase = other;
return Ok(());
}
};
#[cfg(feature = "circular-dma")]
cfg_select! {
hub75_use_i2s_parallel => {}
_ => {
assert!(
xfer.is_done(),
"circular boundary ISR: transfer not complete at the pass boundary"
);
}
}
let (result, tx, buf) = Self::wait(xfer);
match result {
Ok(()) => {
self.phase = TransferPhase::Idle(tx, buf);
Ok(())
}
Err(err) => {
self.phase = TransferPhase::Error(err, tx, buf);
Err(err)
}
}
}
#[cfg_attr(feature = "iram", ram)]
fn wait(xfer: TxTransfer) -> (Result<(), Hub75Error>, TxDriver, BcmBuf) {
cfg_select! {
hub75_use_i2s_parallel => {
let (tx, buf) = xfer.wait();
(Ok(()), tx, buf)
}
_ => {
let (result, tx, buf) = xfer.wait();
(result.map_err(Hub75Error::Dma), tx, buf)
}
}
}
#[cfg(feature = "circular-dma")]
#[cfg_attr(feature = "iram", ram)]
pub(crate) fn clear_frame_interrupt() {
crate::driver::clear_frame_interrupt();
}
}