use core::ptr::null;
use esp_hal::dma::BurstConfig;
use esp_hal::dma::DmaDescriptor;
use esp_hal::dma::DmaTxBuffer;
use esp_hal::dma::EmptyBuf;
#[cfg(any(feature = "full-chain-dma", feature = "circular-dma"))]
use esp_hal::dma::Owner;
use esp_hal::dma::Preparation;
use esp_hal::dma::TransferDirection;
use crate::framebuffer::FrameBuffer;
#[cfg(any(feature = "full-chain-dma", feature = "circular-dma"))]
use crate::MAX_DMA_CHUNK_SIZE;
#[cfg(feature = "circular-dma")]
pub(crate) mod circular;
#[cfg(not(feature = "circular-dma"))]
pub(crate) mod linear;
pub(crate) const MAX_PLANES: usize = 8;
pub(crate) type PlaneInfo = [(*const u8, usize); MAX_PLANES];
pub(crate) fn planes_from_fb(fb: &impl FrameBuffer) -> PlaneInfo {
let plane_count = fb.plane_count();
assert!(
plane_count <= MAX_PLANES,
"plane_count {plane_count} exceeds MAX_PLANES"
);
let mut planes: PlaneInfo = [(null::<u8>(), 0usize); MAX_PLANES];
for (i, slot) in planes.iter_mut().enumerate().take(plane_count) {
*slot = fb.plane_ptr_len(i);
debug_assert!(!slot.0.is_null(), "plane {i} returned a null pointer");
}
planes
}
pub(super) fn make_preparation(descriptors: &mut [DmaDescriptor]) -> Preparation {
let mut empty = EmptyBuf;
let mut prep: Preparation = empty.prepare();
prep.start = descriptors.as_mut_ptr();
prep.direction = TransferDirection::Out;
prep.burst_transfer = BurstConfig::default();
prep.check_owner = Some(false);
prep.auto_write_back = false;
prep
}
#[cfg(any(feature = "full-chain-dma", feature = "circular-dma"))]
pub(super) fn fill_full_chain(
descriptors: &mut [DmaDescriptor],
planes: &PlaneInfo,
plane_count: usize,
total_descs: usize,
last_next: *mut DmaDescriptor,
) {
let base_ptr = descriptors.as_mut_ptr();
let mut desc_idx = 0;
for (plane_idx, &(plane_ptr, plane_bytes)) in planes.iter().enumerate().take(plane_count) {
let reps = 1usize << (plane_count - 1 - plane_idx);
for _ in 0..reps {
let mut remaining = plane_bytes;
let mut offset = 0;
while remaining > 0 {
let chunk = remaining.min(MAX_DMA_CHUNK_SIZE);
let is_last = desc_idx + 1 == total_descs;
let next = if is_last {
last_next
} else {
unsafe { base_ptr.add(desc_idx + 1) }
};
let desc = &mut descriptors[desc_idx];
desc.buffer = unsafe { plane_ptr.add(offset) as *mut u8 };
desc.set_size(chunk);
desc.set_length(chunk);
desc.set_owner(Owner::Dma);
desc.set_suc_eof(is_last);
desc.next = next;
remaining -= chunk;
offset += chunk;
desc_idx += 1;
}
}
}
}