Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
01c97ac
i2s_out: one line of i2s_out_dma_buffer() DEBUG
SpComb Oct 28, 2025
549b021
i2s_out: unset dma eof desc next loop to fix OUT_DSCR_ERR interrupt
SpComb Oct 28, 2025
24006d0
i2s_out: use I2S_OUT_TOTAL_EOF_INT
SpComb Oct 28, 2025
59a5213
i2s_out: simplify i2s_out_dma_buffer(), use eof isr to detect free dm…
SpComb Oct 28, 2025
10bc014
i2s_out: rename dma rx -> out
SpComb Oct 28, 2025
57ea1cc
i2s_out: rename dma eof -> end
SpComb Oct 28, 2025
aa7bb0e
i2s_out: refactor i2s_out_dma_desc/next()
SpComb Oct 28, 2025
ee9c74a
leds: optional i2s interface setup() for async operation
SpComb Oct 28, 2025
4a6a9ac
main: leds setup_interface
SpComb Oct 28, 2025
4dd0579
fixup! i2s_out: rename dma rx -> out
SpComb Oct 28, 2025
39f80fa
i2s_out: implement async start() mode
SpComb Oct 28, 2025
e81ea7b
main: fix i2s_data_copies ordering
SpComb Oct 28, 2025
d6049af
main: USER_ALERT_ERROR_LEDS on leds_setup() errors
SpComb Oct 28, 2025
de8b602
logging: optional ISR WARN
SpComb Oct 28, 2025
9b92c39
i2s_out: cleanup i2s_out_intr_setup() DEBUG logging
SpComb Oct 28, 2025
0dd0271
i2s_out: i2s_out_dma_wait() -> DEBUG
SpComb Oct 28, 2025
b4aa11a
logging: extra TRACE level
SpComb Oct 28, 2025
a3f490e
i2s_out: dma TRACE logging
SpComb Oct 28, 2025
9f4d54b
leds: bump TEST_FRAME_RATE -> 30
SpComb Oct 28, 2025
745bc17
i2s_out: fix goto error -> locking
SpComb Oct 28, 2025
6b120af
i2s_out: timeouts
SpComb Oct 28, 2025
47f2dbc
i2s_out: enforce setup
SpComb Oct 28, 2025
197df52
leds_interface_i2s->options
SpComb Oct 28, 2025
1b8a427
main: reset leds interface on update_leds() errors
SpComb Oct 28, 2025
77f5a84
i2s_out: do not set I2S_OUT_EVENT_GROUP_BIT_DMA_EOF event bit on I2S_…
SpComb Oct 28, 2025
6e7b87b
i2s_out: workaround unreliable I2S_OUT_TOTAL_EOF_INT
SpComb Oct 28, 2025
f712c61
main: bump LEDS_I2S_TIMEOUT -> 5000ms to survive config save -> flash…
SpComb Oct 28, 2025
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
482 changes: 300 additions & 182 deletions components/i2s_out/esp32/dma.c

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions components/i2s_out/esp32/dma.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,15 @@ struct dma_desc {
// linked list
struct dma_desc *next;
};

/* Prepare desc for re-use after DMA EOF */
static inline void i2s_dma_desc_reset(struct dma_desc *eof_desc)
{
eof_desc->len = 0;
eof_desc->owner = 0;
}

static inline void i2s_dma_tx_get_des_addr(i2s_dev_t *hw, uint32_t *dscr_addr)
{
*dscr_addr = hw->out_link_dscr;
}
21 changes: 17 additions & 4 deletions components/i2s_out/esp32/i2s.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ int i2s_out_i2s_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt
i2s_out->dev->sample_rate_conf.val
);

// reset eof state
// reset event state
xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF);

return 0;
Expand All @@ -126,6 +126,9 @@ void i2s_out_i2s_start(struct i2s_out *i2s_out)
{
LOG_DEBUG("");

// reset event state
xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF);

taskENTER_CRITICAL(&i2s_out->mux);

// NOTE: there seems to always be three extra BCK cycles at the start of TX
Expand All @@ -137,7 +140,7 @@ void i2s_out_i2s_start(struct i2s_out *i2s_out)
taskEXIT_CRITICAL(&i2s_out->mux);
}

int i2s_out_i2s_flush(struct i2s_out *i2s_out)
int i2s_out_i2s_flush(struct i2s_out *i2s_out, TickType_t timeout)
{
EventBits_t bits;

Expand All @@ -152,9 +155,16 @@ int i2s_out_i2s_flush(struct i2s_out *i2s_out)

taskEXIT_CRITICAL(&i2s_out->mux);

LOG_DEBUG("wait event_group bits=%08x", I2S_OUT_EVENT_GROUP_BIT_I2S_EOF);
LOG_DEBUG("...");

bits = xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF, false, false, timeout);

xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF, false, false, portMAX_DELAY);
if (!(bits & I2S_OUT_EVENT_GROUP_BIT_I2S_EOF)) {
LOG_ERROR("timeout -> bits=%08x", bits);
return -1;
} else {
LOG_DEBUG("wait -> bits=%08x", bits);
}
}

return 0;
Expand All @@ -169,4 +179,7 @@ void i2s_out_i2s_stop(struct i2s_out *i2s_out)
i2s_ll_tx_stop(i2s_out->dev);

taskEXIT_CRITICAL(&i2s_out->mux);

// reset event state
xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF);
}
98 changes: 86 additions & 12 deletions components/i2s_out/esp32/intr.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,27 @@ static const int i2s_irq[I2S_PORT_MAX] = {
[I2S_PORT_1] = ETS_I2S1_INTR_SOURCE,
};

void IRAM_ATTR i2s_intr_out_total_eof_handler(struct i2s_out *i2s_out, BaseType_t *task_wokenp)
{
uint32_t eof_addr;

i2s_ll_tx_get_eof_des_addr(i2s_out->dev, &eof_addr);

LOG_ISR_DEBUG("desc=%p", eof_addr);

// unblock flush() tasks
xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF, task_wokenp);

i2s_intr_clear(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_CLR);
}

void IRAM_ATTR i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out, BaseType_t *task_wokenp)
{
LOG_ISR_WARN("");
uint32_t dscr_addr;

i2s_dma_tx_get_des_addr(i2s_out->dev, &dscr_addr);

LOG_ISR_WARN("desc=%p", dscr_addr);

i2s_intr_clear(i2s_out->dev, I2S_OUT_DSCR_ERR_INT_CLR);
}
Expand All @@ -32,14 +50,66 @@ void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *tas

struct dma_desc *eof_desc = (struct dma_desc *) eof_addr;

LOG_ISR_DEBUG("desc=%p", eof_desc);
// only handle normal out_desc, not repeat_desc or end_desc
if (eof_desc >= i2s_out->dma_out_desc && eof_desc < i2s_out->dma_out_desc + i2s_out->dma_out_count) {
LOG_ISR_DEBUG("eof desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len);

// NOTE: this is unlikely to stop DMA before this repeats at least once
i2s_ll_tx_stop_link(i2s_out->dev);
i2s_ll_rx_stop_link(i2s_out->dev);
// speculation: we may miss sone EOF ISRs
if (i2s_out->dma_eof_desc) {
for (struct dma_desc *desc = eof_desc; desc > i2s_out->dma_eof_desc; desc--) {
if (desc != eof_desc) {
LOG_ISR_WARN("miss desc=%p owner=%u len=%u", desc, desc->owner, desc->len);
}

// unblock flush() task
xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, task_wokenp);
i2s_dma_desc_reset(desc);
}
} else {
i2s_dma_desc_reset(eof_desc);
}

i2s_out->dma_eof_desc = eof_desc;

// unblock get() task
xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, task_wokenp);

} else if (eof_desc == i2s_out->dma_end_desc) {

// speculation: we might miss EOF ISR for an intermediate DMA descriptor, and hit TOTAL_EOF with eof_addr at the end_desc
// resulting in wait() deadlocking on dma_eof_desc not being updated
eof_desc = i2s_out->dma_out_desc + i2s_out->dma_out_count - 1;

if (!i2s_out->dma_eof_desc) {
LOG_ISR_WARN("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc);

for (struct dma_desc *desc = eof_desc; desc >= i2s_out->dma_out_desc; desc--) {
i2s_dma_desc_reset(desc);
}
} else if (i2s_out->dma_eof_desc != eof_desc) {
LOG_ISR_WARN("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc);

for (struct dma_desc *desc = eof_desc; desc > i2s_out->dma_eof_desc; desc--) {
i2s_dma_desc_reset(desc);
}
} else {
LOG_ISR_DEBUG("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc);
}

i2s_out->dma_eof_desc = eof_desc;

// unblock get() task
xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, task_wokenp);

// XXX: e.g. flash writes seem to drop I2S_OUT_TOTAL_EOF_INT_ST?
if (!(xEventGroupGetBitsFromISR(i2s_out->event_group) & (I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF))) {
LOG_ISR_WARN("end -> total_eof, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc);

// unblock flush() tasks
xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF, task_wokenp);
}

} else {
LOG_ISR_DEBUG("ignore desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len);
}

i2s_intr_clear(i2s_out->dev, I2S_OUT_EOF_INT_CLR);
}
Expand Down Expand Up @@ -68,6 +138,9 @@ void IRAM_ATTR i2s_intr_handler(void *arg)

taskENTER_CRITICAL_ISR(&i2s_out->mux);

if (int_st & I2S_OUT_TOTAL_EOF_INT_ST) {
i2s_intr_out_total_eof_handler(i2s_out, &task_woken);
}
if (int_st & I2S_OUT_DSCR_ERR_INT_ST) {
i2s_intr_out_dscr_err_handler(i2s_out, &task_woken);
}
Expand All @@ -89,21 +162,22 @@ int i2s_out_intr_setup(struct i2s_out *i2s_out, const struct i2s_out_options *op
{
esp_err_t err = 0;

LOG_DEBUG("intr=%p", i2s_out->intr);
if (i2s_out->intr) {
return 0;
}

taskENTER_CRITICAL(&i2s_out->mux);

i2s_intr_disable_all(i2s_out->dev);

if (!i2s_out->intr) {
err = esp_intr_alloc(i2s_irq[i2s_out->port], I2S_INTR_ALLOC_FLAGS, i2s_intr_handler, i2s_out, &i2s_out->intr);
}
err = esp_intr_alloc(i2s_irq[i2s_out->port], I2S_INTR_ALLOC_FLAGS, i2s_intr_handler, i2s_out, &i2s_out->intr);

taskEXIT_CRITICAL(&i2s_out->mux);

if (err) {
LOG_ERROR("esp_intr_alloc: %s", esp_err_to_name(err));
return -1;
} else {
LOG_DEBUG("intr=%p", i2s_out->intr);
}

return 0;
Expand Down
Loading
Loading