From f96b78b0303ccf26a718e641e2d76e30436db39f Mon Sep 17 00:00:00 2001 From: Raymond Lei Date: Mon, 13 Jan 2025 18:18:27 -0500 Subject: [PATCH] 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 --- drivers/i2s/i2s_mcux_sai.c | 38 ++++++++++++++++++++++++++++++++++---- west.yml | 2 +- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/drivers/i2s/i2s_mcux_sai.c b/drivers/i2s/i2s_mcux_sai.c index acb5415a943..a6982a055ca 100644 --- a/drivers/i2s/i2s_mcux_sai.c +++ b/drivers/i2s/i2s_mcux_sai.c @@ -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; } diff --git a/west.yml b/west.yml index d3887834ca3..0c17ee71197 100644 --- a/west.yml +++ b/west.yml @@ -203,7 +203,7 @@ manifest: groups: - hal - name: hal_nxp - revision: b7bd6f67785aee302c1573dababc278e9da00569 + revision: 49ff7e33f848e4b59da59369a77da63e346fb1a3 path: modules/hal/nxp groups: - hal