diff --git a/src/audio/dai.c b/src/audio/dai.c index 45e19e0f2166..d12d68cbc4cd 100644 --- a/src/audio/dai.c +++ b/src/audio/dai.c @@ -651,11 +651,11 @@ static int dai_comp_trigger_internal(struct comp_dev *dev, int cmd) /* only start the DAI if we are not XRUN handling */ if (dd->xrun == 0) { - /* start the DAI */ - dai_trigger(dd->dai, cmd, dev->direction); ret = dma_start(dd->chan); if (ret < 0) return ret; + /* start the DAI */ + dai_trigger(dd->dai, cmd, dev->direction); } else { dd->xrun = 0; } @@ -695,8 +695,21 @@ static int dai_comp_trigger_internal(struct comp_dev *dev, int cmd) COMPILER_FALLTHROUGH; case COMP_TRIGGER_STOP: comp_dbg(dev, "dai_comp_trigger_internal(), STOP"); +/* + * Some platforms cannot just simple disable + * DMA channel during the transfer, + * because it will hang the whole DMA controller. + * Therefore, stop the DMA first and let the DAI + * drain the FIFO in order to stop the channel + * as soon as possible. + */ +#if CONFIG_DMA_SUSPEND_DRAIN ret = dma_stop(dd->chan); dai_trigger(dd->dai, cmd, dev->direction); +#else + dai_trigger(dd->dai, cmd, dev->direction); + ret = dma_stop(dd->chan); +#endif break; case COMP_TRIGGER_PAUSE: comp_dbg(dev, "dai_comp_trigger_internal(), PAUSE"); diff --git a/src/drivers/imx/sai.c b/src/drivers/imx/sai.c index 76a225d742d2..6ea552106c94 100644 --- a/src/drivers/imx/sai.c +++ b/src/drivers/imx/sai.c @@ -33,13 +33,46 @@ static void sai_start(struct dai *dai, int direction) uint32_t xcsr = 0U; - /* enable DMA requests */ + if (direction == DAI_DIR_CAPTURE) { + /* Software Reset */ + dai_update_bits(dai, REG_SAI_XCSR(DAI_DIR_CAPTURE), + REG_SAI_CSR_SR, REG_SAI_CSR_SR); + /* Clear SR bit to finish the reset */ + dai_update_bits(dai, REG_SAI_XCSR(DAI_DIR_CAPTURE), + REG_SAI_CSR_SR, 0U); + /* Check if the opposite direction is also disabled */ + xcsr = dai_read(dai, REG_SAI_XCSR(DAI_DIR_PLAYBACK)); + if (!(xcsr & REG_SAI_CSR_FRDE)) { + /* Software Reset */ + dai_update_bits(dai, REG_SAI_XCSR(DAI_DIR_PLAYBACK), + REG_SAI_CSR_SR, REG_SAI_CSR_SR); + /* Clear SR bit to finish the reset */ + dai_update_bits(dai, REG_SAI_XCSR(DAI_DIR_PLAYBACK), + REG_SAI_CSR_SR, 0U); + /* Transmitter enable */ + dai_update_bits(dai, REG_SAI_XCSR(DAI_DIR_PLAYBACK), + REG_SAI_CSR_TERE, REG_SAI_CSR_TERE); + } + } else { + /* Check if the opposite direction is also disabled */ + xcsr = dai_read(dai, REG_SAI_XCSR(DAI_DIR_CAPTURE)); + if (!(xcsr & REG_SAI_CSR_FRDE)) { + /* Software Reset */ + dai_update_bits(dai, REG_SAI_XCSR(DAI_DIR_PLAYBACK), + REG_SAI_CSR_SR, REG_SAI_CSR_SR); + /* Clear SR bit to finish the reset */ + dai_update_bits(dai, REG_SAI_XCSR(DAI_DIR_PLAYBACK), + REG_SAI_CSR_SR, 0U); + } + } + + /* W1C */ dai_update_bits(dai, REG_SAI_XCSR(direction), - REG_SAI_CSR_FRDE, REG_SAI_CSR_FRDE); -#ifdef CONFIG_IMX8M - dai_update_bits(dai, REG_SAI_MCTL, REG_SAI_MCTL_MCLK_EN, - REG_SAI_MCTL_MCLK_EN); -#endif + REG_SAI_CSR_FEF, 1); + dai_update_bits(dai, REG_SAI_XCSR(direction), + REG_SAI_CSR_SEF, 1); + dai_update_bits(dai, REG_SAI_XCSR(direction), + REG_SAI_CSR_WSF, 1); /* add one word to FIFO before TRCE is enabled */ if (direction == DAI_DIR_PLAYBACK) @@ -47,27 +80,21 @@ static void sai_start(struct dai *dai, int direction) else dai_write(dai, REG_SAI_RDR0, 0x0); - /* transmitter enable */ + /* enable DMA requests */ dai_update_bits(dai, REG_SAI_XCSR(direction), - REG_SAI_CSR_TERE, REG_SAI_CSR_TERE); - /* TODO: for the time being use half FIFO size as watermark */ - dai_update_bits(dai, REG_SAI_XCR1(direction), - REG_SAI_CR1_RFW_MASK, SAI_FIFO_WORD_SIZE / 2); + REG_SAI_CSR_FRDE, REG_SAI_CSR_FRDE); +#ifdef CONFIG_IMX8M + dai_update_bits(dai, REG_SAI_MCTL, REG_SAI_MCTL_MCLK_EN, + REG_SAI_MCTL_MCLK_EN); +#endif + + /* transmit/receive data channel enable */ dai_update_bits(dai, REG_SAI_XCR3(direction), REG_SAI_CR3_TRCE_MASK, REG_SAI_CR3_TRCE(1)); - if (direction == DAI_DIR_CAPTURE) { - xcsr = dai_read(dai, REG_SAI_XCSR(DAI_DIR_PLAYBACK)); - if (!(xcsr & REG_SAI_CSR_FRDE)) { - - dai_update_bits(dai, REG_SAI_XCSR(DAI_DIR_PLAYBACK), - REG_SAI_CSR_TERE, REG_SAI_CSR_TERE); - dai_update_bits(dai, REG_SAI_XCR3(DAI_DIR_PLAYBACK), - REG_SAI_CR3_TRCE_MASK, - REG_SAI_CR3_TRCE(1)); - } - } - + /* transmitter/receiver enable */ + dai_update_bits(dai, REG_SAI_XCSR(direction), + REG_SAI_CSR_TERE, REG_SAI_CSR_TERE); } static void sai_stop(struct dai *dai, int direction) @@ -76,32 +103,39 @@ static void sai_stop(struct dai *dai, int direction) uint32_t xcsr = 0U; + /* Disable DMA request */ dai_update_bits(dai, REG_SAI_XCSR(direction), REG_SAI_CSR_FRDE, 0); + + /* Transmit/Receive data channel disable */ + dai_update_bits(dai, REG_SAI_XCR3(direction), + REG_SAI_CR3_TRCE_MASK, + REG_SAI_CR3_TRCE(0)); + + /* Disable interrupts */ dai_update_bits(dai, REG_SAI_XCSR(direction), REG_SAI_CSR_XIE_MASK, 0); - /* Check if the opposite direction is also disabled */ - xcsr = dai_read(dai, REG_SAI_XCSR(!direction)); - if (!(xcsr & REG_SAI_CSR_FRDE)) { - /* Disable both directions and reset their FIFOs */ - dai_update_bits(dai, REG_SAI_TCSR, REG_SAI_CSR_TERE, 0); - poll_for_register_delay(dai_base(dai) + REG_SAI_TCSR, - REG_SAI_CSR_TERE, 0, 100); - - dai_update_bits(dai, REG_SAI_RCSR, REG_SAI_CSR_TERE, 0); - poll_for_register_delay(dai_base(dai) + REG_SAI_RCSR, + /* Disable transmitter/receiver */ + if (direction == DAI_DIR_CAPTURE) { + dai_update_bits(dai, REG_SAI_XCSR(DAI_DIR_CAPTURE), REG_SAI_CSR_TERE, 0); + poll_for_register_delay(dai_base(dai) + REG_SAI_XCSR(DAI_DIR_CAPTURE), REG_SAI_CSR_TERE, 0, 100); - - /* Software Reset for both Tx and Rx */ - dai_update_bits(dai, REG_SAI_TCSR, REG_SAI_CSR_SR, - REG_SAI_CSR_SR); - dai_update_bits(dai, REG_SAI_RCSR, REG_SAI_CSR_SR, - REG_SAI_CSR_SR); - - /* Clear SR bit to finish the reset */ - dai_update_bits(dai, REG_SAI_TCSR, REG_SAI_CSR_SR, 0U); - dai_update_bits(dai, REG_SAI_RCSR, REG_SAI_CSR_SR, 0U); + /* Check if the opposite direction is also disabled */ + xcsr = dai_read(dai, REG_SAI_XCSR(DAI_DIR_PLAYBACK)); + if (!(xcsr & REG_SAI_CSR_FRDE)) { + dai_update_bits(dai, REG_SAI_XCSR(DAI_DIR_PLAYBACK), REG_SAI_CSR_TERE, 0); + poll_for_register_delay(dai_base(dai) + REG_SAI_XCSR(DAI_DIR_PLAYBACK), + REG_SAI_CSR_TERE, 0, 100); + } + } else { + /* Check if the opposite direction is also disabled */ + xcsr = dai_read(dai, REG_SAI_XCSR(DAI_DIR_CAPTURE)); + if (!(xcsr & REG_SAI_CSR_FRDE)) { + dai_update_bits(dai, REG_SAI_XCSR(DAI_DIR_PLAYBACK), REG_SAI_CSR_TERE, 0); + poll_for_register_delay(dai_base(dai) + REG_SAI_XCSR(DAI_DIR_PLAYBACK), + REG_SAI_CSR_TERE, 0, 100); + } } } @@ -255,6 +289,9 @@ static inline int sai_set_config(struct dai *dai, mask_cr5 = REG_SAI_CR5_WNW_MASK | REG_SAI_CR5_W0W_MASK | REG_SAI_CR5_FBT_MASK; + /* TODO: for the time being use half FIFO size as watermark */ + dai_update_bits(dai, REG_SAI_XCR1(REG_TX_DIR), + REG_SAI_CR1_RFW_MASK, SAI_FIFO_WORD_SIZE / 2); dai_update_bits(dai, REG_SAI_XCR2(REG_TX_DIR), mask_cr2, val_cr2); dai_update_bits(dai, REG_SAI_XCR4(REG_TX_DIR), mask_cr4, val_cr4); dai_update_bits(dai, REG_SAI_XCR5(REG_TX_DIR), mask_cr5, val_cr5); @@ -264,6 +301,10 @@ static inline int sai_set_config(struct dai *dai, val_cr2 |= REG_SAI_CR2_SYNC; mask_cr2 |= REG_SAI_CR2_SYNC_MASK; + + /* TODO: for the time being use half FIFO size as watermark */ + dai_update_bits(dai, REG_SAI_XCR1(REG_RX_DIR), + REG_SAI_CR1_RFW_MASK, SAI_FIFO_WORD_SIZE / 2); dai_update_bits(dai, REG_SAI_XCR2(REG_RX_DIR), mask_cr2, val_cr2); dai_update_bits(dai, REG_SAI_XCR4(REG_RX_DIR), mask_cr4, val_cr4); dai_update_bits(dai, REG_SAI_XCR5(REG_RX_DIR), mask_cr5, val_cr5);