Skip to content

Commit

Permalink
drivers: i2s: Fix TX transfer stopping issue in some corner cases
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
Raymond0225 authored and kartben committed Jan 28, 2025
1 parent e8a3ca6 commit f96b78b
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 5 deletions.
38 changes: 34 additions & 4 deletions drivers/i2s/i2s_mcux_sai.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

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 disabled_exit_no_drop;
goto enabled_exit;


disabled_exit_no_drop:
i2s_tx_stream_disable(dev, false);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion west.yml
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ manifest:
groups:
- hal
- name: hal_nxp
revision: b7bd6f67785aee302c1573dababc278e9da00569
revision: 49ff7e33f848e4b59da59369a77da63e346fb1a3
path: modules/hal/nxp
groups:
- hal
Expand Down

0 comments on commit f96b78b

Please sign in to comment.