From 991dfd04a381babf61caf391fa881857c82f31e6 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Fri, 5 May 2023 15:46:55 +0300 Subject: [PATCH 1/4] intel: ssp: do not read data directly if DMA is active If DMA is active, do not read data directly from the SSP RX fifo. Signed-off-by: Kai Vehmanen (cherry picked from commit e94071fb822b98d03b5ee9687bd4e4f3d2d736bb) (cherry picked from commit 736af0180388ea03a4b5e3bed26fc4850aa785fa) --- src/drivers/intel/ssp/ssp.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/drivers/intel/ssp/ssp.c b/src/drivers/intel/ssp/ssp.c index 93627beccf13..037b64ca38c0 100644 --- a/src/drivers/intel/ssp/ssp.c +++ b/src/drivers/intel/ssp/ssp.c @@ -71,9 +71,18 @@ static void ssp_empty_rx_fifo(struct dai *dai) uint64_t sample_ticks = clock_ticks_per_sample(PLATFORM_DEFAULT_CLOCK, ssp->params.fsync_rate); uint32_t retry = SSP_RX_FLUSH_RETRY_MAX; + bool direct_reads = ssp->state[DAI_DIR_CAPTURE] <= COMP_STATE_PREPARE; uint32_t entries; uint32_t i; +#if CONFIG_DMA_SUSPEND_DRAIN + /* + * In drain mode, DMA is stopped before DAI, so flush must be + * always done with direct register read. + */ + direct_reads = true; +#endif + /* * To make sure all the RX FIFO entries are read out for the flushing, * we need to wait a minimal SSP port delay after entries are all read, @@ -84,9 +93,12 @@ static void ssp_empty_rx_fifo(struct dai *dai) while ((ssp_read(dai, SSSR) & SSSR_RNE) && retry--) { entries = SSCR3_RFL_VAL(ssp_read(dai, SSCR3)); dai_dbg(dai, "ssp_empty_rx_fifo(), before flushing, entries %d", entries); - for (i = 0; i < entries + 1; i++) - /* read to try empty fifo */ - ssp_read(dai, SSDR); + + /* let DMA consume data or read RX FIFO directly */ + if (direct_reads) { + for (i = 0; i < entries + 1; i++) + ssp_read(dai, SSDR); + } /* wait to get valid fifo status and re-check */ wait_delay(sample_ticks); From 8eabff7aeba6b950c5b45a279ce5e92ee8bd97d1 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Fri, 5 May 2023 15:37:22 +0300 Subject: [PATCH 2/4] intel: ssp: drain RX fifo when starting If DMA transaction is ongoing when RX is enabled, this can lead to stuck communication between DMA and SSP (DMA service request not seen by the DMA). To avoid this, flush the RX fifo before enabling SSP RX. Link: https://github.com/thesofproject/sof/issues/7548 Suggested-by: Peter Ujfalusi Signed-off-by: Kai Vehmanen (cherry picked from commit 4a4d8d261907bd900c91d0e64d7458bdb32e4cb3) (cherry picked from commit 2c3f3702a53d4f280ca81619026eecf20514c396) --- src/drivers/intel/ssp/ssp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/drivers/intel/ssp/ssp.c b/src/drivers/intel/ssp/ssp.c index 037b64ca38c0..cf99c1e453fe 100644 --- a/src/drivers/intel/ssp/ssp.c +++ b/src/drivers/intel/ssp/ssp.c @@ -913,6 +913,10 @@ static void ssp_start(struct dai *dai, int direction) spin_lock(&dai->lock); + /* RX fifo must be cleared before start */ + if (direction == DAI_DIR_CAPTURE) + ssp_empty_rx_fifo(dai); + /* request mclk/bclk */ ssp_pre_start(dai); From d2d85c29635a96ca934e0f865c1062788c1b4edc Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 31 May 2023 11:32:46 +0300 Subject: [PATCH 3/4] intel: ssp: Revise receive FIFO draining The receive FIFO needs to be drained in a different way depending when it is done. - before start If the RX FIFO is in overflow state then we must read all the entries out to empty it (it was after all full). - before stop The DMA might be already running to read out data. Check the FIFO level change in one sample time which gives us the needed information to decide to wait for another loop for the DMA burst to finish, wait for the DMA to start it's burst (DMA request was asserted) or drain the FIFO directly. No need to drain the RX fifo at probe time. Signed-off-by: Peter Ujfalusi (cherry picked from commit 69d56a757740274a6f2ccf17fc77c4188f17fc01) (cherry picked from commit 6571598b22e07ed23b9ddbef078422bbb0f2df59) --- src/drivers/intel/ssp/ssp.c | 91 +++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 34 deletions(-) diff --git a/src/drivers/intel/ssp/ssp.c b/src/drivers/intel/ssp/ssp.c index cf99c1e453fe..570bb3c91277 100644 --- a/src/drivers/intel/ssp/ssp.c +++ b/src/drivers/intel/ssp/ssp.c @@ -64,49 +64,74 @@ static void ssp_empty_tx_fifo(struct dai *dai) ssp_write(dai, SSSR, sssr); } -/* empty SSP receive FIFO */ -static void ssp_empty_rx_fifo(struct dai *dai) +static void ssp_empty_rx_fifo_on_start(struct dai *dai) +{ + uint32_t retry = SSP_RX_FLUSH_RETRY_MAX; + uint32_t i, sssr; + + sssr = ssp_read(dai, SSSR); + + if (sssr & SSSR_ROR) { + /* The RX FIFO is in overflow condition, empty it */ + for (i = 0; i < SSP_FIFO_DEPTH; i++) + ssp_read(dai, SSDR); + + /* Clear the overflow status */ + ssp_update_bits(dai, SSSR, SSSR_ROR, SSSR_ROR); + /* Re-read the SSSR register */ + sssr = ssp_read(dai, SSSR); + } + + while ((sssr & SSSR_RNE) && retry--) { + uint32_t entries = SSCR3_RFL_VAL(ssp_read(dai, SSCR3)); + + /* Empty the RX FIFO (the DMA is not running at this point) */ + for (i = 0; i < entries + 1; i++) + ssp_read(dai, SSDR); + + sssr = ssp_read(dai, SSSR); + } +} + +static void ssp_empty_rx_fifo_on_stop(struct dai *dai) { struct ssp_pdata *ssp = dai_get_drvdata(dai); uint64_t sample_ticks = clock_ticks_per_sample(PLATFORM_DEFAULT_CLOCK, ssp->params.fsync_rate); uint32_t retry = SSP_RX_FLUSH_RETRY_MAX; - bool direct_reads = ssp->state[DAI_DIR_CAPTURE] <= COMP_STATE_PREPARE; - uint32_t entries; - uint32_t i; + uint32_t entries[2]; + uint32_t i, sssr; -#if CONFIG_DMA_SUSPEND_DRAIN - /* - * In drain mode, DMA is stopped before DAI, so flush must be - * always done with direct register read. - */ - direct_reads = true; -#endif + sssr = ssp_read(dai, SSSR); + entries[0] = SSCR3_RFL_VAL(ssp_read(dai, SSCR3)); - /* - * To make sure all the RX FIFO entries are read out for the flushing, - * we need to wait a minimal SSP port delay after entries are all read, - * and then re-check to see if there is any subsequent entries written - * to the FIFO. This will help to make sure there is no sample mismatched - * issue for the next run with the SSP RX. - */ - while ((ssp_read(dai, SSSR) & SSSR_RNE) && retry--) { - entries = SSCR3_RFL_VAL(ssp_read(dai, SSCR3)); - dai_dbg(dai, "ssp_empty_rx_fifo(), before flushing, entries %d", entries); + while ((sssr & SSSR_RNE) && retry--) { + /* Wait one sample time */ + wait_delay(sample_ticks); - /* let DMA consume data or read RX FIFO directly */ - if (direct_reads) { - for (i = 0; i < entries + 1; i++) + entries[1] = SSCR3_RFL_VAL(ssp_read(dai, SSCR3)); + sssr = ssp_read(dai, SSSR); + + if (entries[0] > entries[1]) { + /* + * The DMA is reading the FIFO, check the status in the + * next loop + */ + entries[0] = entries[1]; + } else if (!(sssr & SSSR_RFS)) { + /* + * The DMA request is not asserted, read the FIFO + * directly, otherwise let the next loop iteration to + * check the status + */ + for (i = 0; i < entries[1] + 1; i++) ssp_read(dai, SSDR); } - /* wait to get valid fifo status and re-check */ - wait_delay(sample_ticks); - entries = SSCR3_RFL_VAL(ssp_read(dai, SSCR3)); - dai_dbg(dai, "ssp_empty_rx_fifo(), after flushing, entries %d", entries); + sssr = ssp_read(dai, SSSR); } - /* clear interrupt */ + /* Just in case clear the overflow status */ ssp_update_bits(dai, SSSR, SSSR_ROR, SSSR_ROR); } @@ -915,7 +940,7 @@ static void ssp_start(struct dai *dai, int direction) /* RX fifo must be cleared before start */ if (direction == DAI_DIR_CAPTURE) - ssp_empty_rx_fifo(dai); + ssp_empty_rx_fifo_on_start(dai); /* request mclk/bclk */ ssp_pre_start(dai); @@ -968,7 +993,7 @@ static void ssp_stop(struct dai *dai, int direction) if (direction == DAI_DIR_CAPTURE && ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_PREPARE) { ssp_update_bits(dai, SSRSA, SSRSA_RXEN, 0); - ssp_empty_rx_fifo(dai); + ssp_empty_rx_fifo_on_stop(dai); ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PREPARE; dai_info(dai, "ssp_stop(), RX stop"); } @@ -1078,8 +1103,6 @@ static int ssp_probe(struct dai *dai) /* Disable dynamic clock gating before touching any register */ pm_runtime_get_sync(SSP_CLK, dai->index); - ssp_empty_rx_fifo(dai); - return 0; } From 149aa210871ec7378369746ffe4944f86d78f886 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Fri, 17 Nov 2023 11:51:10 +0800 Subject: [PATCH 4/4] intel: ssp: check mclk always-on quirk in ssp_remove Dynamic pipeline breaks mclk always-on feature by freeing dai component in HW_FREE stage; the ssp_remove() will be called and disables mclk unconditionally. Signed-off-by: Brent Lu (cherry picked from commit 6dc474c2e7031f2ad0534174d7d4c3caad7b58c3) (cherry picked from commit d5e3c70d81f8143f4f47f6b404571d7ae8130a8b) --- src/drivers/intel/ssp/ssp.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/drivers/intel/ssp/ssp.c b/src/drivers/intel/ssp/ssp.c index 570bb3c91277..ecaa7c85f602 100644 --- a/src/drivers/intel/ssp/ssp.c +++ b/src/drivers/intel/ssp/ssp.c @@ -1108,9 +1108,18 @@ static int ssp_probe(struct dai *dai) static int ssp_remove(struct dai *dai) { + struct ssp_pdata *ssp = dai_get_drvdata(dai); + pm_runtime_put_sync(SSP_CLK, dai->index); - ssp_mclk_disable_unprepare(dai); + /* + * dai comp will be freed during HW_FREE stage if dynamic pipeline is + * enabled; need to check the MCLK_AON quirk to see if we need to keep + * MCLK on even when dai comp is going to be freed + */ + if (!(ssp->params.clks_control & SOF_DAI_INTEL_SSP_CLKCTRL_MCLK_AON)) + ssp_mclk_disable_unprepare(dai); + ssp_bclk_disable_unprepare(dai); /* Disable SSP power */