Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion src/audiopump/audiopump.c
Original file line number Diff line number Diff line change
Expand Up @@ -1627,7 +1627,9 @@ static void audiopump_teardown(bool release_guard) {
audiopump_ctx.stop = true;
audiopump_ctx.park_req = false;
if (audiopump_ctx.service_mode) {
audiodsp_pump_lock_acquire();
audiopump_tail_drop(&audiopump_ctx, false);
audiodsp_pump_lock_release();
}
audiodsp_pump_set_active(false);
const audiodsp_port_ops_t *port = audiodsp_port();
Expand Down Expand Up @@ -1854,9 +1856,13 @@ static int audiopump_launch(int core, int prio, int stack, bool psram) {
// -2, not -1: a caller that prints what spawn() returned should be
// able to tell "no core affinity" from "no thread at all", and on a
// build that should have had one that number is the symptom.
// Under the lock, for a driver that renders from an interrupt: the
// first render must find the loop begun.
audiodsp_pump_lock_acquire();
audiopump_ctx.service_mode = true;
audiopump_live = true;
audiopump_run_begin(&audiopump_ctx);
audiodsp_pump_lock_release();
return -2;
}

Expand Down Expand Up @@ -2001,7 +2007,9 @@ void audiopump_c_stop(void) {
audiopump_ctx.stop = true;
audiopump_ctx.park_req = false;
if (audiopump_ctx.service_mode) {
audiodsp_pump_lock_acquire();
audiopump_tail_drop(&audiopump_ctx, false);
audiodsp_pump_lock_release();
}
const audiodsp_port_ops_t *port = audiodsp_port();
if (port->thread_wake != NULL) {
Expand Down Expand Up @@ -2035,10 +2043,21 @@ void audiopump_c_service(uint64_t budget) {
|| audiopump_ctx.finished) {
return;
}
// A driver that renders from an interrupt calls this from it, where the
// lock is nothing; a call from the interpreter (an output's stop()
// finishing the loop) holds the interrupt off while it runs, so the two
// never pull at once. Without render_kick the lock isn't taken, as before.
const bool hold = audiodsp_port()->render_kick != NULL;
if (hold) {
audiodsp_pump_lock_acquire();
}
if (audiopump_run_blocks(&audiopump_ctx, budget ? budget : UINT64_MAX)
== AUDIOPUMP_SERVICE_DONE) {
audiopump_run_end(&audiopump_ctx);
}
if (hold) {
audiodsp_pump_lock_release();
}
}

// --- the park protocol ----------------------------------------------------
Expand Down Expand Up @@ -2115,7 +2134,9 @@ static bool audiopump_c_park_fade(uint32_t timeout_us, bool fade) {
}
}
if (audiopump_ctx.service_mode) {
audiodsp_pump_lock_acquire();
audiopump_tail_drop(&audiopump_ctx, true);
audiodsp_pump_lock_release();
// The loop is not running -- this call IS the only thread -- so the
// pump is already at a block boundary by construction and park() is a
// store. The wait below would spin until the timeout and then report
Expand Down Expand Up @@ -2178,7 +2199,17 @@ static mp_obj_t audiopump_service(size_t n_args, const mp_obj_t *args) {
}
int why = AUDIOPUMP_SERVICE_MORE;
uint64_t before = audiopump_ctx.acc_blocks;
if (audiopump_ctx.service_mode) {
void (*kick)(void) = audiodsp_port()->render_kick;
if (kick != NULL) {
// The driver renders from an interrupt: running the pump here as
// well would be two pumps at once. Ask it to render, and report no
// blocks, as a threaded port does.
if (audiopump_live && !audiopump_ctx.finished) {
kick();
} else {
why = AUDIOPUMP_SERVICE_DONE;
}
} else if (audiopump_ctx.service_mode) {
if (audiopump_live && !audiopump_ctx.finished) {
why = audiopump_run_blocks(&audiopump_ctx, budget);
if (why == AUDIOPUMP_SERVICE_DONE) {
Expand Down
15 changes: 15 additions & 0 deletions src/shared/audiodsp_port.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,23 @@ typedef struct {
// AUDIODSP_PORT_MEM_HOT_HOOKS.
void *(*mem_alloc_hot)(size_t size);
void (*mem_free_hot)(void *ptr);

// --- a port that renders from an interrupt -------------------------------
//
// A port with no threads can still render away from the interpreter, from
// an interrupt the driver raises (the STM32H7's DAC does). The interrupt
// is then the pump's thread: audiopump.service() on the interpreter calls
// render_kick() to ask the driver to render, instead of rendering there,
// and the interpreter's changes to the pump take the lock, which such a
// driver implements by holding its interrupt off. NULL everywhere else,
// which changes nothing. A driver that also builds against an older
// audiodsp fills it only #ifdef AUDIODSP_PORT_IRQ_HOOKS.
void (*render_kick)(void);
} audiodsp_port_ops_t;

// This header has render_kick.
#define AUDIODSP_PORT_IRQ_HOOKS (1)

// This header has the mem_* hooks above.
#define AUDIODSP_PORT_MEM_HOOKS (1)
// And the mem_*_hot pair.
Expand Down
Loading