use crate::{peripherals::VDMA, private::Sealed, reg_access::VolatileCell};
pub trait VdmaDmaChannel: Sealed {
fn channel_id(&self) -> u8;
}
for_each_dma_channel! {
("VDMA", $ch:ident, $num:literal, compatible = [$($compatible:ident),*]) => {
impl VdmaDmaChannel for crate::peripherals::$ch<'_> {
#[inline]
fn channel_id(&self) -> u8 { $num }
}
};
}
pub(super) const DSI_BRG_MEM_BASE: u32 = 0x5010_5000;
const PORT_MEMORY: u32 = 1;
const PORT_DSI: u32 = 0;
const CTRL_LO: u32 = PORT_MEMORY | (PORT_DSI << 2) | (1 << 6) | (3 << 8) | (3 << 11) | (8 << 14) | (7 << 18);
const CTRL_HI_BASE: u32 = (1 << 6) | (16 << 7) | (1 << 15) | (16 << 16);
const LLI_VALID: u32 = 1 << 31;
#[repr(C)]
pub(super) struct VdmaLinkItem {
sar_lo: VolatileCell<u32>, sar_hi: VolatileCell<u32>, dar_lo: VolatileCell<u32>, dar_hi: VolatileCell<u32>, block_ts: VolatileCell<u32>, _res1: VolatileCell<u32>, llp_lo: VolatileCell<u32>, llp_hi: VolatileCell<u32>, ctrl_lo: VolatileCell<u32>, ctrl_hi: VolatileCell<u32>, sstat: VolatileCell<u32>, dstat: VolatileCell<u32>, status_lo: VolatileCell<u32>, status_hi: VolatileCell<u32>, _res2: VolatileCell<u32>, _res3: VolatileCell<u32>, }
impl VdmaLinkItem {
pub(super) const fn zeroed() -> Self {
Self {
sar_lo: VolatileCell::new(0),
sar_hi: VolatileCell::new(0),
dar_lo: VolatileCell::new(0),
dar_hi: VolatileCell::new(0),
block_ts: VolatileCell::new(0),
_res1: VolatileCell::new(0),
llp_lo: VolatileCell::new(0),
llp_hi: VolatileCell::new(0),
ctrl_lo: VolatileCell::new(0),
ctrl_hi: VolatileCell::new(0),
sstat: VolatileCell::new(0),
dstat: VolatileCell::new(0),
status_lo: VolatileCell::new(0),
status_hi: VolatileCell::new(0),
_res2: VolatileCell::new(0),
_res3: VolatileCell::new(0),
}
}
pub(super) fn configure(&self, src_addr: u32, fb_size: usize, next: *const VdmaLinkItem) {
debug_assert!(fb_size.is_multiple_of(8), "fb_size must be a multiple of 8");
let block_ts = (fb_size / 8) as u32 - 1;
let ctrl_hi = CTRL_HI_BASE | LLI_VALID; self.sar_lo.set(src_addr);
self.sar_hi.set(0);
self.dar_lo.set(DSI_BRG_MEM_BASE);
self.dar_hi.set(0);
self.block_ts.set(block_ts);
self._res1.set(0);
self.llp_lo.set(next as u32 | PORT_MEMORY);
self.llp_hi.set(0);
self.ctrl_lo.set(CTRL_LO);
self.ctrl_hi.set(ctrl_hi);
self.sstat.set(0);
self.dstat.set(0);
self.status_lo.set(0);
self.status_hi.set(0);
self._res2.set(0);
self._res3.set(0);
}
pub(super) fn set_source(&self, src_addr: u32) {
self.sar_lo.set(src_addr);
}
pub(super) fn rearm(&self) {
self.ctrl_hi.set(CTRL_HI_BASE | LLI_VALID);
}
}
pub(super) struct VdmaChannel {
channel_id: u8,
}
impl VdmaChannel {
pub(super) fn new(channel_id: u8) -> Self {
VDMA::regs().reset0().write(|w| w.dmac_rst().set_bit());
while VDMA::regs().reset0().read().dmac_rst().bit_is_set() {}
VDMA::regs().cfg0().modify(|_, w| {
w.dmac_en().set_bit();
w.int_en().set_bit()
});
let ch = VDMA::regs().ch(channel_id as usize);
ch.cfg0().write(|w| unsafe {
w.ch1_src_multblk_type().bits(3);
w.ch1_dst_multblk_type().bits(3)
});
ch.cfg1().write(|w| unsafe {
w.ch1_tt_fc().bits(1);
w.ch1_hs_sel_src().clear_bit();
w.ch1_hs_sel_dst().clear_bit();
w.ch1_dst_per().bits(0);
w.ch1_ch_prior().bits(1);
w.ch1_src_osr_lmt().bits(4);
w.ch1_dst_osr_lmt().bits(1)
});
Self { channel_id }
}
pub(super) fn start(&mut self, item: &VdmaLinkItem) {
let addr = item as *const VdmaLinkItem as u32;
debug_assert_eq!(addr & 0x3F, 0, "LLI must be 64-byte aligned");
let dma = VDMA::regs();
let ch = dma.ch(self.channel_id as usize);
ch.llp0()
.write(|w| unsafe { w.ch1_lms().bit(PORT_MEMORY != 0).ch1_loc0().bits(addr >> 6) });
unsafe { ch.llp1().write_with_zero(|w| w) };
self.ch_enable(true);
}
fn ch_enable(&self, en: bool) {
let shift = self.channel_id;
let val: u32 = if en {
0x0101 << shift } else {
0x0100 << shift };
unsafe { VDMA::regs().chen0().write(|w| w.bits(val)) };
}
}