Skip to content

Commit 12043ed

Browse files
committed
drivers: i2s: esp32: improve TX robustness and trigger handling
Treat TX underrun as READY instead of ERROR, so DMA can be restarted. Make START/STOP non-fatal when already in the target state. Auto-start and flush if data is queued, no-op if idle. Allow write() to auto-kick TX when in READY. Signed-off-by: Sylvio Alves <[email protected]>
1 parent 6e1ff6a commit 12043ed

File tree

1 file changed

+106
-6
lines changed

1 file changed

+106
-6
lines changed

drivers/i2s/i2s_esp32.c

Lines changed: 106 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -403,9 +403,13 @@ static void i2s_esp32_tx_callback(void *arg, int status)
403403

404404
err = k_msgq_get(&stream->data->queue, &item, K_NO_WAIT);
405405
if (err < 0) {
406-
stream->data->state = I2S_STATE_ERROR;
407-
LOG_ERR("TX queue empty: %d", err);
408-
goto tx_disable;
406+
/*
407+
* Stop DMA and transition to READY, so the next write()
408+
* can auto-kick TX without requiring a new START trigger.
409+
*/
410+
stream->data->state = I2S_STATE_READY;
411+
stream->conf->stop_transfer(dev);
412+
return;
409413
}
410414

411415
mem_block_tmp = stream->data->mem_block;
@@ -459,7 +463,13 @@ static int i2s_esp32_tx_start_transfer(const struct device *dev)
459463

460464
err = k_msgq_get(&stream->data->queue, &item, K_NO_WAIT);
461465
if (err < 0) {
462-
return -ENOMEM;
466+
/*
467+
* No data queued yet. Leave mem_block NULL and return success.
468+
* START will keep TX in READY; next write() will auto-kick TX.
469+
*/
470+
stream->data->mem_block = NULL;
471+
stream->data->mem_block_len = 0;
472+
return 0;
463473
}
464474

465475
stream->data->mem_block = item.buffer;
@@ -1193,6 +1203,11 @@ static int i2s_esp32_trigger_stream(const struct device *dev, const struct i2s_e
11931203

11941204
switch (cmd) {
11951205
case I2S_TRIGGER_START:
1206+
/* if already streaming, treat as success */
1207+
if (stream->data->state == I2S_STATE_RUNNING) {
1208+
LOG_DBG("START ignored: already RUNNING");
1209+
return 0;
1210+
}
11961211
if (stream->data->state != I2S_STATE_READY) {
11971212
LOG_ERR("START - Invalid state: %d", (int)stream->data->state);
11981213
return -EIO;
@@ -1223,12 +1238,27 @@ static int i2s_esp32_trigger_stream(const struct device *dev, const struct i2s_e
12231238
return -EIO;
12241239
}
12251240
stream->data->last_block = false;
1241+
/*
1242+
* If no mem_block was available (queue empty), stay READY.
1243+
* The next write() will auto-kick TX into RUNNING.
1244+
*/
1245+
if (dir == I2S_DIR_TX && stream->data->mem_block == NULL &&
1246+
!stream->data->dma_pending) {
1247+
stream->data->state = I2S_STATE_READY;
1248+
irq_unlock(key);
1249+
return 0;
1250+
}
12261251
stream->data->state = I2S_STATE_RUNNING;
12271252
irq_unlock(key);
12281253
break;
12291254

12301255
case I2S_TRIGGER_STOP:
12311256
key = irq_lock();
1257+
if (stream->data->state == I2S_STATE_READY) {
1258+
/* Already idle; treat STOP as success */
1259+
irq_unlock(key);
1260+
return 0;
1261+
}
12321262
if (stream->data->state != I2S_STATE_RUNNING) {
12331263
irq_unlock(key);
12341264
LOG_ERR("STOP - Invalid state: %d", (int)stream->data->state);
@@ -1249,9 +1279,49 @@ static int i2s_esp32_trigger_stream(const struct device *dev, const struct i2s_e
12491279

12501280
case I2S_TRIGGER_DRAIN:
12511281
key = irq_lock();
1252-
if (stream->data->state != I2S_STATE_RUNNING) {
1282+
int32_t st = stream->data->state;
1283+
1284+
if (st == I2S_STATE_READY) {
1285+
#if I2S_ESP32_IS_DIR_EN(tx)
1286+
if (dir == I2S_DIR_TX) {
1287+
bool have_data = (k_msgq_num_used_get(&stream->data->queue) > 0);
1288+
1289+
/* If there's data queued and TX is idle, kick once and drain */
1290+
if (have_data && !stream->data->dma_pending) {
1291+
int kick = stream->conf->start_transfer(dev);
1292+
1293+
if (kick < 0) {
1294+
irq_unlock(key);
1295+
LOG_ERR("DRAIN - autostart failed: %d", kick);
1296+
return -EIO;
1297+
}
1298+
stream->data->last_block = false;
1299+
stream->data->stop_without_draining = false;
1300+
stream->data->state = I2S_STATE_STOPPING;
1301+
irq_unlock(key);
1302+
break;
1303+
}
1304+
1305+
/* Nothing to drain (idle and no queued blocks) */
1306+
if (!have_data && !stream->data->dma_pending) {
1307+
irq_unlock(key);
1308+
return 0;
1309+
}
1310+
}
1311+
#endif /* I2S_ESP32_IS_DIR_EN(tx) */
1312+
1313+
#if I2S_ESP32_IS_DIR_EN(rx)
1314+
if (dir == I2S_DIR_RX) {
1315+
/* If RX is idle and no DMA pending, nothing to drain */
1316+
if (!stream->data->dma_pending) {
1317+
irq_unlock(key);
1318+
return 0;
1319+
}
1320+
}
1321+
#endif /* I2S_ESP32_IS_DIR_EN(rx) */
1322+
} else if (st != I2S_STATE_RUNNING) {
12531323
irq_unlock(key);
1254-
LOG_ERR("DRAIN - Invalid state: %d", (int)stream->data->state);
1324+
LOG_ERR("DRAIN - Invalid state: %d", (int)st);
12551325
return -EIO;
12561326
}
12571327

@@ -1460,6 +1530,36 @@ static int i2s_esp32_write(const struct device *dev, void *mem_block, size_t siz
14601530
return err;
14611531
}
14621532

1533+
/*
1534+
* Auto-restart TX after an underrun or a START with no data:
1535+
* If TX is in READY and no DMA is pending and there's no current block,
1536+
* kick DMA immediately so we don't require a new START trigger.
1537+
*/
1538+
if (stream->data->state == I2S_STATE_READY && !stream->data->dma_pending &&
1539+
stream->data->mem_block == NULL) {
1540+
unsigned int key = irq_lock();
1541+
1542+
if (stream->data->state == I2S_STATE_READY && !stream->data->dma_pending &&
1543+
stream->data->mem_block == NULL) {
1544+
int kick = stream->conf->start_transfer(dev);
1545+
1546+
if (kick < 0) {
1547+
irq_unlock(key);
1548+
LOG_ERR("TX auto-restart failed: %d", kick);
1549+
return kick;
1550+
}
1551+
stream->data->last_block = false;
1552+
/*
1553+
* If start_transfer pulled from the queue, we are RUNNING;
1554+
* if not (shouldn't happen here), stay READY.
1555+
*/
1556+
if (stream->data->mem_block != NULL || stream->data->dma_pending) {
1557+
stream->data->state = I2S_STATE_RUNNING;
1558+
}
1559+
}
1560+
irq_unlock(key);
1561+
}
1562+
14631563
return 0;
14641564
#else
14651565
LOG_ERR("I2S_DIR_TX not enabled");

0 commit comments

Comments
 (0)