Make cyclic descriptors in bounce buffers work

This commit is contained in:
Jakub Hlusička 2026-02-22 16:31:12 +01:00
parent 8f55f23840
commit 735d0a48bb

View file

@ -185,6 +185,9 @@ impl DmaBounce {
Self::linear_descriptors_for_buffer(window_size, burst_config, |_| {});
let bounce_src_descs = if cyclic {
Self::bounce_descriptors_for_buffer_cyclic(
row_front_porch_bytes,
row_width_bytes,
window_size_rows,
unsafe {
(
&mut *(bounce_buffer_dst as *mut _),
@ -260,7 +263,105 @@ impl DmaBounce {
descriptors
}
fn prepare_descriptors_window(
bounce_buffer: &mut [u8],
descriptors_window: &mut [DmaDescriptor],
row_front_porch_bytes: usize,
row_width_bytes: usize,
window_size_rows: usize,
max_chunk_size: usize,
descriptors_per_row: usize,
descriptors_per_row_front_porch: usize,
) {
for (row_index_in_window, descriptors_row) in descriptors_window
.chunks_mut(descriptors_per_row)
.enumerate()
{
// let row_index = row_index_in_window + window_index * window_size_rows;
let (descriptors_row_front_porch, descriptors_row_stored) =
descriptors_row.split_at_mut(descriptors_per_row_front_porch);
// Prepare front porch descriptors.
{
let mut descriptors_it = descriptors_row_front_porch.iter_mut();
let mut remaining_front_porch = row_front_porch_bytes;
while remaining_front_porch > 0 {
let desc = descriptors_it.next().unwrap();
let chunk_size = core::cmp::min(max_chunk_size, remaining_front_porch);
remaining_front_porch -= chunk_size;
// Just make it point at a bounce buffer.
// It is guaranteed to have enough bytes by `DmaBounce::new`.
desc.buffer = bounce_buffer.as_mut_ptr();
desc.set_size(chunk_size);
desc.set_length(chunk_size);
desc.reset_for_tx(false);
}
assert!(
descriptors_it.next().is_none(),
"front porch descriptors must be used up"
);
assert_eq!(
descriptors_row_front_porch
.iter()
.map(|desc| desc.size())
.sum::<usize>(),
row_front_porch_bytes
);
}
// Prepare window descriptors.
{
let mut remaining_bounce_buffer =
&mut bounce_buffer[row_index_in_window * row_width_bytes..][..row_width_bytes];
// if remaining_bounce_buffer.len() > row_width_bytes {
// remaining_bounce_buffer = &mut remaining_bounce_buffer[..row_width_bytes];
// }
for desc in &mut *descriptors_row_stored {
let chunk_size = core::cmp::min(max_chunk_size, remaining_bounce_buffer.len());
desc.buffer = remaining_bounce_buffer.as_mut_ptr();
remaining_bounce_buffer = &mut remaining_bounce_buffer[chunk_size..];
desc.set_size(chunk_size);
desc.set_length(chunk_size);
desc.reset_for_tx(false);
}
assert!(
remaining_bounce_buffer.is_empty(),
"bounce buffer must be used up"
);
assert_eq!(
descriptors_row_stored
.iter()
.map(|desc| desc.size())
.sum::<usize>(),
row_width_bytes
);
}
}
// Set EOF bit on the last descriptor of the window, to signal
// that the bounce buffer is done being read from.
if let Some(last_desc) = descriptors_window.last_mut() {
last_desc.reset_for_tx(true);
}
assert_eq!(
descriptors_window
.iter()
.map(|desc| desc.size())
.sum::<usize>(),
window_size_rows * (row_front_porch_bytes + row_width_bytes)
);
}
fn bounce_descriptors_for_buffer_cyclic(
row_front_porch_bytes: usize,
row_width_bytes: usize,
window_size_rows: usize,
bounce_buffers: (&'static mut [u8], &'static mut [u8]),
burst_config: BurstConfig,
) -> &'static mut [DmaDescriptor] {
@ -271,17 +372,23 @@ impl DmaBounce {
);
let buffer_len = bounce_buffers.0.len();
let max_chunk_size = burst_config.max_compatible_chunk_size();
let descriptors_len = dma::descriptor_count(
assert_eq!(
buffer_len,
max_chunk_size,
// TODO: This might need to be set to true?
// I don't know why cyclic descriptor lists must be at least 3 descriptors long.
false,
row_width_bytes * window_size_rows,
"the provided bounce buffers have an invalid size"
);
let max_chunk_size = burst_config.max_compatible_chunk_size();
let descriptors_per_row_front_porch =
dma::descriptor_count(row_front_porch_bytes, max_chunk_size, false);
let descriptors_per_row_stored =
dma::descriptor_count(row_width_bytes, max_chunk_size, false);
let descriptors_per_row = descriptors_per_row_stored + descriptors_per_row_front_porch;
let descriptors_per_window = window_size_rows * descriptors_per_row;
let descriptors_combined =
Box::leak(vec![DmaDescriptor::EMPTY; 2 * descriptors_len].into_boxed_slice());
let descriptors_pair = descriptors_combined.split_at_mut(descriptors_len);
Box::leak(vec![DmaDescriptor::EMPTY; 2 * descriptors_per_window].into_boxed_slice());
let descriptors_pair = descriptors_combined.split_at_mut(descriptors_per_window);
// Link up the descriptors.
fn link_up_descriptors(
@ -303,19 +410,16 @@ impl DmaBounce {
(bounce_buffers.0, descriptors_pair.0),
(bounce_buffers.1, descriptors_pair.1),
] {
let mut descriptors_it = descriptors.iter_mut();
let mut remaining_bounce_buffer = bounce_buffer;
while !remaining_bounce_buffer.is_empty() {
let chunk_size = core::cmp::min(max_chunk_size, remaining_bounce_buffer.len());
let desc = descriptors_it.next().unwrap();
desc.buffer = remaining_bounce_buffer.as_mut_ptr();
remaining_bounce_buffer = &mut remaining_bounce_buffer[chunk_size..];
let is_last = remaining_bounce_buffer.is_empty();
desc.set_size(chunk_size);
desc.set_length(chunk_size);
desc.reset_for_tx(is_last);
}
Self::prepare_descriptors_window(
bounce_buffer,
descriptors,
row_front_porch_bytes,
row_width_bytes,
window_size_rows,
max_chunk_size,
descriptors_per_row,
descriptors_per_row_front_porch,
);
}
descriptors_combined
@ -345,10 +449,6 @@ impl DmaBounce {
"the provided bounce buffers have an invalid size"
);
warn!(
"windows_len: {windows_len}\nrow_front_porch_bytes: {row_front_porch_bytes}\nrow_width_bytes: {row_width_bytes}\nwindow_size_rows: {window_size_rows}\nbuffer_len: {buffer_len}",
);
let max_chunk_size = burst_config.max_compatible_chunk_size();
let descriptors_per_row_front_porch =
dma::descriptor_count(row_front_porch_bytes, max_chunk_size, false);
@ -376,92 +476,16 @@ impl DmaBounce {
{
let bounce_buffer_index = window_index % 2;
let bounce_buffer = &mut *bounce_buffers[bounce_buffer_index];
// let bounce_buffer_ptr = bounce_buffers[bounce_buffer_index].as_mut_ptr();
// let mut remaining_bounce_buffer = &mut *bounce_buffers[bounce_buffer_index];
for (row_index_in_window, descriptors_row) in descriptors_window
.chunks_mut(descriptors_per_row)
.enumerate()
{
// let row_index = row_index_in_window + window_index * window_size_rows;
let (descriptors_row_front_porch, descriptors_row_stored) =
descriptors_row.split_at_mut(descriptors_per_row_front_porch);
// Prepare front porch descriptors.
{
let mut descriptors_it = descriptors_row_front_porch.iter_mut();
let mut remaining_front_porch = row_front_porch_bytes;
while remaining_front_porch > 0 {
let desc = descriptors_it.next().unwrap();
let chunk_size = core::cmp::min(max_chunk_size, remaining_front_porch);
remaining_front_porch -= chunk_size;
// Just make it point at a bounce buffer.
// It is guaranteed to have enough bytes by `DmaBounce::new`.
desc.buffer = bounce_buffer.as_mut_ptr();
desc.set_size(chunk_size);
desc.set_length(chunk_size);
desc.reset_for_tx(false);
}
assert!(
descriptors_it.next().is_none(),
"front porch descriptors must be used up"
);
assert_eq!(
descriptors_row_front_porch
.iter()
.map(|desc| desc.size())
.sum::<usize>(),
row_front_porch_bytes
);
}
// Prepare window descriptors.
{
let mut remaining_bounce_buffer = &mut bounce_buffer
[row_index_in_window * row_width_bytes..][..row_width_bytes];
// if remaining_bounce_buffer.len() > row_width_bytes {
// remaining_bounce_buffer = &mut remaining_bounce_buffer[..row_width_bytes];
// }
for desc in &mut *descriptors_row_stored {
let chunk_size =
core::cmp::min(max_chunk_size, remaining_bounce_buffer.len());
desc.buffer = remaining_bounce_buffer.as_mut_ptr();
remaining_bounce_buffer = &mut remaining_bounce_buffer[chunk_size..];
desc.set_size(chunk_size);
desc.set_length(chunk_size);
desc.reset_for_tx(false);
}
assert!(
remaining_bounce_buffer.is_empty(),
"bounce buffer must be used up"
);
assert_eq!(
descriptors_row_stored
.iter()
.map(|desc| desc.size())
.sum::<usize>(),
row_width_bytes
);
}
}
// Set EOF bit on the last descriptor of the window, to signal
// that the bounce buffer is done being read from.
if let Some(last_desc) = descriptors_window.last_mut() {
last_desc.reset_for_tx(true);
}
assert_eq!(
descriptors_window
.iter()
.map(|desc| desc.size())
.sum::<usize>(),
window_size_rows * (row_front_porch_bytes + row_width_bytes)
Self::prepare_descriptors_window(
bounce_buffer,
descriptors_window,
row_front_porch_bytes,
row_width_bytes,
window_size_rows,
max_chunk_size,
descriptors_per_row,
descriptors_per_row_front_porch,
);
}