Run background tasks during display refresh

This allows audio buffers to be filled during display refresh.
Howevere, this only works during explicit refreshes though because
background tasks cannot be recursive.

Also, on RP2, disable a finished audio DMA so it isn't accidentally
triggered and restart the channels if needed.
This commit is contained in:
Scott Shawcroft 2025-04-21 14:26:36 -07:00
parent dc325a3f42
commit 952bea23f8
No known key found for this signature in database
4 changed files with 28 additions and 6 deletions

View file

@ -159,9 +159,13 @@ static void audio_dma_load_next_block(audio_dma_t *dma, size_t buffer_idx) {
!dma_channel_is_busy(dma->channel[1])) {
// No data has been read, and both DMA channels have now finished, so it's safe to stop.
audio_dma_stop(dma);
dma->dma_result = AUDIO_DMA_OK;
return;
}
}
}
// Enable the channel so that it can be played.
dma_hw->ch[dma_channel].al1_ctrl |= DMA_CH1_CTRL_TRIG_EN_BITS;
dma->dma_result = AUDIO_DMA_OK;
}
@ -462,18 +466,26 @@ static void dma_callback_fun(void *arg) {
// Load the blocks for the requested channels.
uint32_t channel = 0;
size_t filled_count = 0;
while (channels_to_load_mask) {
if (channels_to_load_mask & 1) {
if (dma->channel[0] == channel) {
audio_dma_load_next_block(dma, 0);
filled_count++;
}
if (dma->channel[1] == channel) {
audio_dma_load_next_block(dma, 1);
filled_count++;
}
}
channels_to_load_mask >>= 1;
channel++;
}
// If we had to fill both buffers, then we missed the trigger from the other
// buffer. So restart the DMA.
if (filled_count == 2) {
dma_channel_start(dma->channel[0]);
}
}
void __not_in_flash_func(isr_dma_0)(void) {
@ -491,6 +503,8 @@ void __not_in_flash_func(isr_dma_0)(void) {
audio_dma_t *dma = MP_STATE_PORT(playing_audio)[i];
// Record all channels whose DMA has completed; they need loading.
dma->channels_to_load_mask |= mask;
// Disable the channel so that we don't play it without filling it.
dma_hw->ch[i].al1_ctrl &= ~DMA_CH0_CTRL_TRIG_EN_BITS;
background_callback_add(&dma->callback, dma_callback_fun, (void *)dma);
}
if (MP_STATE_PORT(background_pio_read)[i] != NULL) {

View file

@ -296,8 +296,11 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are
_send_pixels(self, (uint8_t *)buffer, subrectangle_size_bytes);
displayio_display_bus_end_transaction(&self->bus);
// TODO(tannewt): Make refresh displays faster so we don't starve other
// background tasks.
// Run background tasks so they can run during an explicit refresh.
// Auto-refresh won't run background tasks here because it is a background task itself.
RUN_BACKGROUND_TASKS;
// Run USB background tasks so they can run during an implicit refresh.
#if CIRCUITPY_TINYUSB
usb_background();
#endif

View file

@ -360,8 +360,11 @@ static bool epaperdisplay_epaperdisplay_refresh_area(epaperdisplay_epaperdisplay
self->bus.send(self->bus.bus, DISPLAY_DATA, self->chip_select, (uint8_t *)buffer, subrectangle_size_bytes);
displayio_display_bus_end_transaction(&self->bus);
// TODO(tannewt): Make refresh displays faster so we don't starve other
// background tasks.
// Run background tasks so they can run during an explicit refresh.
// Auto-refresh won't run background tasks here because it is a background task itself.
RUN_BACKGROUND_TASKS;
// Run USB background tasks so they can run during an implicit refresh.
#if CIRCUITPY_TINYUSB
usb_background();
#endif

View file

@ -200,9 +200,11 @@ static bool _refresh_area(framebufferio_framebufferdisplay_obj_t *self, const di
dest += rowstride;
src += rowsize;
}
// Run background tasks so they can run during an explicit refresh.
// Auto-refresh won't run background tasks here because it is a background task itself.
RUN_BACKGROUND_TASKS;
// TODO(tannewt): Make refresh displays faster so we don't starve other
// background tasks.
// Run USB background tasks so they can run during an implicit refresh.
#if CIRCUITPY_TINYUSB
usb_background();
#endif