use esp_hal::dma::DmaDescriptor;
use esp_hal::dma::DmaTxBuffer;
use esp_hal::dma::Preparation;
#[cfg(feature = "iram")]
use esp_hal::ram;
use crate::framebuffer::FrameBuffer;
pub(crate) struct BcmBuf {
descriptors: &'static mut [DmaDescriptor],
descriptor_count: usize,
#[cfg(esp32c6)]
total_len: usize,
}
impl BcmBuf {
pub(crate) fn new(descriptors: &'static mut [DmaDescriptor]) -> Self {
Self {
descriptors,
descriptor_count: 0,
#[cfg(esp32c6)]
total_len: 0,
}
}
pub(crate) fn build<FB: FrameBuffer>(&mut self, fb: &'static FB) {
let total_descs = crate::dma_descriptor_count::<FB>(crate::MAX_DMA_CHUNK_SIZE);
debug_assert!(
self.descriptors.len() >= total_descs,
"not enough DMA descriptors: have {}, need {}",
self.descriptors.len(),
total_descs,
);
let (last_next, last_suc_eof) = cfg_select! {
feature = "circular-dma" => (self.descriptors.as_mut_ptr(), false),
_ => (core::ptr::null_mut(), true),
};
super::fill_descriptor_chain(
&mut self.descriptors[..total_descs],
fb.bcm_segment_count(),
|i| {
let seg = fb.bcm_segment(i);
debug_assert!(!seg.ptr.is_null(), "segment {i} returned a null pointer");
seg
},
total_descs,
last_next,
last_suc_eof,
);
self.descriptor_count = total_descs;
#[cfg(esp32c6)]
{
let mut total = 0;
for i in 0..fb.bcm_segment_count() {
let seg = fb.bcm_segment(i);
total += seg.len * seg.reps;
}
self.total_len = total;
}
}
pub(crate) fn descriptors_ptr(&mut self) -> *mut DmaDescriptor {
self.descriptors.as_mut_ptr()
}
pub(crate) fn descriptor_count(&self) -> usize {
self.descriptor_count
}
#[cfg(esp32c6)]
#[cfg_attr(feature = "iram", ram)]
pub(crate) fn current_transfer_len(&self) -> usize {
self.total_len
}
}
#[cfg(feature = "circular-dma")]
pub(crate) static mut BOUNDARY_DESCRIPTOR: DmaDescriptor = DmaDescriptor::EMPTY;
#[cfg(feature = "circular-dma")]
#[cfg_attr(feature = "iram", ram)]
pub(crate) fn arm_boundary(descriptors: *mut DmaDescriptor, descriptor_count: usize) {
debug_assert!(
descriptor_count >= 2,
"the circular chain needs at least two descriptors"
);
unsafe {
let last = &*descriptors.add(descriptor_count - 1);
let spare = &raw mut BOUNDARY_DESCRIPTOR;
(*spare).flags = last.flags;
(*spare).flags.set_suc_eof(true);
(*spare).buffer = last.buffer;
(*spare).next = core::ptr::null_mut();
(*descriptors.add(descriptor_count - 2)).next = spare;
}
}
#[cfg(feature = "circular-dma")]
#[cfg_attr(feature = "iram", ram)]
pub(crate) fn disarm_boundary(descriptors: *mut DmaDescriptor, descriptor_count: usize) {
debug_assert!(
descriptor_count >= 2,
"the circular chain needs at least two descriptors"
);
unsafe {
(*descriptors.add(descriptor_count - 2)).next = descriptors.add(descriptor_count - 1);
}
}
#[cfg_attr(feature = "iram", ram)]
pub(crate) fn apply_delta(descriptors: *mut DmaDescriptor, descriptor_count: usize, delta: isize) {
unsafe {
for i in 0..descriptor_count {
let desc = &mut *descriptors.add(i);
desc.buffer = desc.buffer.wrapping_byte_offset(delta);
}
}
}
unsafe impl Send for BcmBuf {}
unsafe impl DmaTxBuffer for BcmBuf {
type View = Self;
type Final = Self;
#[cfg_attr(feature = "iram", ram)]
fn prepare(&mut self) -> Preparation {
super::make_preparation(self.descriptors)
}
#[cfg_attr(feature = "iram", ram)]
fn into_view(self) -> Self::View {
self
}
#[cfg_attr(feature = "iram", ram)]
fn from_view(view: Self::View) -> Self::Final {
view
}
}