Skip to content

Commit 88f6117

Browse files
authored
Merge pull request #10299 from tannewt/fix_audio_buffer_retrigger
Tweak audio playback on RP2
2 parents 1e4d766 + 5cf7f29 commit 88f6117

File tree

2 files changed

+39
-25
lines changed

2 files changed

+39
-25
lines changed

ports/raspberrypi/audio_dma.c

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,9 @@ static void audio_dma_load_next_block(audio_dma_t *dma, size_t buffer_idx) {
165165
}
166166
}
167167
// Enable the channel so that it can be played.
168-
dma_hw->ch[dma_channel].al1_ctrl |= DMA_CH1_CTRL_TRIG_EN_BITS;
168+
if (!dma->paused) {
169+
dma_hw->ch[dma_channel].al1_ctrl |= DMA_CH1_CTRL_TRIG_EN_BITS;
170+
}
169171
dma->dma_result = AUDIO_DMA_OK;
170172
}
171173

@@ -301,6 +303,8 @@ audio_dma_result audio_dma_setup_playback(
301303
MP_STATE_PORT(playing_audio)[dma->channel[0]] = dma;
302304
MP_STATE_PORT(playing_audio)[dma->channel[1]] = dma;
303305

306+
dma->paused = false;
307+
304308
// Load the first two blocks up front.
305309
audio_dma_load_next_block(dma, 0);
306310
if (dma->dma_result != AUDIO_DMA_OK) {
@@ -331,6 +335,8 @@ audio_dma_result audio_dma_setup_playback(
331335
1, // transaction count
332336
false); // trigger
333337
} else {
338+
// Clear any latent interrupts so that we don't immediately disable channels.
339+
dma_hw->ints0 |= (1 << dma->channel[0]) | (1 << dma->channel[1]);
334340
// Enable our DMA channels on DMA_IRQ_0 to the CPU. This will wake us up when
335341
// we're WFI.
336342
dma_hw->inte0 |= (1 << dma->channel[0]) | (1 << dma->channel[1]);
@@ -344,6 +350,8 @@ audio_dma_result audio_dma_setup_playback(
344350
}
345351

346352
void audio_dma_stop(audio_dma_t *dma) {
353+
dma->paused = true;
354+
347355
// Disable our interrupts.
348356
uint32_t channel_mask = 0;
349357
if (dma->channel[0] < NUM_DMA_CHANNELS) {
@@ -363,12 +371,13 @@ void audio_dma_stop(audio_dma_t *dma) {
363371

364372
for (size_t i = 0; i < 2; i++) {
365373
size_t channel = dma->channel[i];
374+
dma->channel[i] = NUM_DMA_CHANNELS;
366375
if (channel == NUM_DMA_CHANNELS) {
367376
// Channel not in use.
368377
continue;
369378
}
370379

371-
dma_channel_config c = dma_channel_get_default_config(dma->channel[i]);
380+
dma_channel_config c = dma_channel_get_default_config(channel);
372381
channel_config_set_enable(&c, false);
373382
dma_channel_set_config(channel, &c, false /* trigger */);
374383

@@ -381,7 +390,6 @@ void audio_dma_stop(audio_dma_t *dma) {
381390
dma_channel_set_trans_count(channel, 0, false /* trigger */);
382391
dma_channel_unclaim(channel);
383392
MP_STATE_PORT(playing_audio)[channel] = NULL;
384-
dma->channel[i] = NUM_DMA_CHANNELS;
385393
}
386394
dma->playing_in_progress = false;
387395

@@ -393,6 +401,7 @@ void audio_dma_stop(audio_dma_t *dma) {
393401
void audio_dma_pause(audio_dma_t *dma) {
394402
dma_hw->ch[dma->channel[0]].al1_ctrl &= ~DMA_CH0_CTRL_TRIG_EN_BITS;
395403
dma_hw->ch[dma->channel[1]].al1_ctrl &= ~DMA_CH1_CTRL_TRIG_EN_BITS;
404+
dma->paused = true;
396405
}
397406

398407
void audio_dma_resume(audio_dma_t *dma) {
@@ -405,15 +414,14 @@ void audio_dma_resume(audio_dma_t *dma) {
405414
dma_hw->ch[dma->channel[0]].al1_ctrl |= DMA_CH0_CTRL_TRIG_EN_BITS;
406415
dma_hw->ch[dma->channel[1]].al1_ctrl |= DMA_CH1_CTRL_TRIG_EN_BITS;
407416
}
417+
dma->paused = false;
408418
}
409419

410420
bool audio_dma_get_paused(audio_dma_t *dma) {
411421
if (dma->channel[0] >= NUM_DMA_CHANNELS) {
412422
return false;
413423
}
414-
uint32_t control = dma_hw->ch[dma->channel[0]].ctrl_trig;
415-
416-
return (control & DMA_CH0_CTRL_TRIG_EN_BITS) == 0;
424+
return dma->playing_in_progress && dma->paused;
417425
}
418426

419427
uint32_t audio_dma_pause_all(void) {
@@ -446,6 +454,9 @@ void audio_dma_init(audio_dma_t *dma) {
446454

447455
dma->channel[0] = NUM_DMA_CHANNELS;
448456
dma->channel[1] = NUM_DMA_CHANNELS;
457+
458+
dma->playing_in_progress = false;
459+
dma->paused = false;
449460
}
450461

451462
void audio_dma_deinit(audio_dma_t *dma) {
@@ -486,37 +497,38 @@ bool audio_dma_get_playing(audio_dma_t *dma) {
486497
// NOTE(dhalbert): I successfully printed from here while debugging.
487498
// So it's possible, but be careful.
488499
static void dma_callback_fun(void *arg) {
500+
// Any audio interrupts that happen below will requeue the background task
501+
// after updating channels_to_load_mask.
489502
audio_dma_t *dma = arg;
490503
if (dma == NULL) {
491504
return;
492505
}
493506

494507
common_hal_mcu_disable_interrupts();
495508
uint32_t channels_to_load_mask = dma->channels_to_load_mask;
509+
// This can be 0 if the background task was queued between the call to
510+
// dma_callback_fun and the above read of channels_to_load_mask.
496511
dma->channels_to_load_mask = 0;
497512
common_hal_mcu_enable_interrupts();
498513

499-
// Load the blocks for the requested channels.
500-
uint32_t channel = 0;
514+
uint8_t first_filled_channel = NUM_DMA_CHANNELS;
501515
size_t filled_count = 0;
502-
while (channels_to_load_mask) {
503-
if (channels_to_load_mask & 1) {
504-
if (dma->channel[0] == channel) {
505-
audio_dma_load_next_block(dma, 0);
506-
filled_count++;
507-
}
508-
if (dma->channel[1] == channel) {
509-
audio_dma_load_next_block(dma, 1);
510-
filled_count++;
511-
}
512-
}
513-
channels_to_load_mask >>= 1;
514-
channel++;
516+
if (dma->channel[0] != NUM_DMA_CHANNELS && (channels_to_load_mask & (1 << dma->channel[0]))) {
517+
audio_dma_load_next_block(dma, 0);
518+
first_filled_channel = dma->channel[0];
519+
filled_count++;
515520
}
516-
// If we had to fill both buffers, then we missed the trigger from the other
517-
// buffer. So restart the DMA.
518-
if (filled_count == 2) {
519-
dma_channel_start(dma->channel[0]);
521+
if (dma->channel[1] != NUM_DMA_CHANNELS && (channels_to_load_mask & (1 << dma->channel[1]))) {
522+
audio_dma_load_next_block(dma, 1);
523+
first_filled_channel = dma->channel[1];
524+
filled_count++;
525+
}
526+
527+
// Restart if the other channel has been queued while we were filling the first or we filled two
528+
// now. (Two get filled if the second buffer completes while the first is waiting in the
529+
// background task queue.)
530+
if (first_filled_channel != NUM_DMA_CHANNELS && (dma->channels_to_load_mask != 0 || filled_count == 2)) {
531+
dma_channel_start(first_filled_channel);
520532
}
521533
}
522534

@@ -537,6 +549,7 @@ void __not_in_flash_func(isr_dma_0)(void) {
537549
dma->channels_to_load_mask |= mask;
538550
// Disable the channel so that we don't play it without filling it.
539551
dma_hw->ch[i].al1_ctrl &= ~DMA_CH0_CTRL_TRIG_EN_BITS;
552+
// This is a noop if the callback is already queued.
540553
background_callback_add(&dma->callback, dma_callback_fun, (void *)dma);
541554
}
542555
if (MP_STATE_PORT(background_pio_read)[i] != NULL) {

ports/raspberrypi/audio_dma.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ typedef struct {
3838
bool unsigned_to_signed;
3939
bool output_signed;
4040
bool playing_in_progress;
41+
bool paused;
4142
bool swap_channel;
4243
} audio_dma_t;
4344

0 commit comments

Comments
 (0)