drivers: i2s: Fix TX transfer stopping issue in some corner cases

In previous implementions, when there is no buffers in TX queue in a TX
callback context, TX will be stopped immediately. It is not correct
because there may still TX blocks in DMA TX transfer queue.
TX should only be stopped when DMA queue is empty and no more in I2S TX
queue.
Another modification is to set correct TX water FIFO level.

Signed-off-by: Raymond Lei <raymond.lei@nxp.com>
This commit is contained in:
Raymond Lei 2025-01-13 18:18:27 -05:00 committed by Benjamin Cabé
commit f96b78b030
2 changed files with 35 additions and 5 deletions

View file

@ -308,11 +308,23 @@ static void i2s_dma_tx_callback(const struct device *dma_dev, void *arg, uint32_
/* TX queue has drained */
strm->state = I2S_STATE_READY;
LOG_DBG("TX stream has stopped");
} else {
strm->state = I2S_STATE_ERROR;
LOG_ERR("TX Failed to reload DMA");
goto disabled_exit_no_drop;
}
goto disabled_exit_no_drop;
LOG_WRN("TX input queue empty!");
if (strm->free_tx_dma_blocks >= MAX_TX_DMA_BLOCKS) {
/* In running state, no TX blocks for transferring, so stop
* TX (This will disable bit clock to avoid dummy bits
* received in RX side.
*/
const struct i2s_mcux_config *dev_cfg = dev->config;
I2S_Type *base = (I2S_Type *)dev_cfg->base;
SAI_TxEnable(base, false);
LOG_WRN("TX is paused.");
}
goto enabled_exit;
disabled_exit_no_drop:
i2s_tx_stream_disable(dev, false);
@ -597,6 +609,7 @@ static int i2s_mcux_config(const struct device *dev, enum i2s_dir dir,
LOG_DBG("tx slab block_size = %d", (uint32_t)i2s_cfg->mem_slab->info.block_size);
LOG_DBG("tx slab buffer = 0x%x", (uint32_t)i2s_cfg->mem_slab->buffer);
config.fifo.fifoWatermark = (uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) - 1;
/* set bit clock divider */
SAI_TxSetConfig(base, &config);
dev_data->tx.start_channel = config.startChannel;
@ -976,6 +989,23 @@ static int i2s_mcux_write(const struct device *dev, void *mem_block, size_t size
return ret;
}
if (strm->state == I2S_STATE_RUNNING && strm->free_tx_dma_blocks >= MAX_TX_DMA_BLOCKS) {
uint8_t blocks_queued = 0;
const struct i2s_mcux_config *dev_cfg = dev->config;
I2S_Type *base = (I2S_Type *)dev_cfg->base;
/* As DMA has been stopped because reloading failure in TX callback,
* here is a good place to reload it and resume TX.
*/
ret = i2s_tx_reload_multiple_dma_blocks(dev, &blocks_queued);
if (ret == 0 && blocks_queued > 0) {
SAI_TxEnable(base, true);
LOG_WRN("TX is resumed");
} else {
LOG_ERR("TX block reload err, TX is not resumed");
return ret;
}
}
return ret;
}

View file

@ -203,7 +203,7 @@ manifest:
groups:
- hal
- name: hal_nxp
revision: b7bd6f67785aee302c1573dababc278e9da00569
revision: 49ff7e33f848e4b59da59369a77da63e346fb1a3
path: modules/hal/nxp
groups:
- hal