Skip to content

Commit 5cf7f29

Browse files
committed
Tweak audio playback on RP2
This will hopefully make it more reliable. I think the failure to play was due to a latent interrupt from the previously interrupted playback.
1 parent a1e2b0b commit 5cf7f29

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy