From 89360f8db226167d37f2f09129386842cf7afd0a Mon Sep 17 00:00:00 2001 From: Zhong Shengquan Date: Thu, 31 Jul 2025 15:11:04 +0800 Subject: [PATCH] ASoC: rockchip: spdifrx: fix issue where reset causes underrun in app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When sample rate changes, a reset operation is performed, and another reset occurs after debouncing. This results in less data being read by upper layer, thus triggering an underrun condition. Therefore, remove reset operation in rk_spdifrx_trigger. Meanwhile, when sample rate changes, ​​disable dma enable​​, and after debouncing completes, notify upper layer via stop_xrun to restart​​, then ​​enable dma to resume data transfer. ​​ Change-Id: Ie112ed83018722d069bd85d181f609c4bf865027 Signed-off-by: Zhong Shengquan --- sound/soc/rockchip/rockchip_spdifrx.c | 67 +++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/sound/soc/rockchip/rockchip_spdifrx.c b/sound/soc/rockchip/rockchip_spdifrx.c index 066de3a388c0..0a6137041ccc 100644 --- a/sound/soc/rockchip/rockchip_spdifrx.c +++ b/sound/soc/rockchip/rockchip_spdifrx.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -46,9 +47,11 @@ struct rk_spdifrx_dev { struct reset_control *reset; struct rk_spdifrx_info info; struct snd_soc_dai *dai; + struct snd_pcm_substream *substream; struct timer_list debounce_timer; struct timer_list non_liner_timer; struct timer_list fifo_timer; + struct work_struct xrun_work; unsigned int mclk_rate; int irq; bool cdr_count_avg; @@ -129,6 +132,7 @@ static int rk_spdifrx_hw_params(struct snd_pcm_substream *substream, spdifrx->info.sample_rate_src_last = 0; spdifrx->info.sample_width_last = 0; spdifrx->info.liner_pcm_last = 1; + spdifrx->substream = substream; if (params_rate(params) >= 44100) spdifrx->cdr_count_avg = true; @@ -155,7 +159,7 @@ static int rk_spdifrx_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - rk_spdifrx_reset(spdifrx); + regmap_write(spdifrx->regmap, SPDIFRX_CLR, SPDIFRX_CLR_RXSC); ret = regmap_update_bits(spdifrx->regmap, SPDIFRX_DMACR, SPDIFRX_DMACR_RDE_MASK, SPDIFRX_DMACR_RDE_ENABLE); @@ -166,6 +170,9 @@ static int rk_spdifrx_trigger(struct snd_pcm_substream *substream, ret = regmap_update_bits(spdifrx->regmap, SPDIFRX_CFGR, SPDIFRX_EN_MASK, SPDIFRX_EN); + + mod_timer(&spdifrx->fifo_timer, jiffies + msecs_to_jiffies(1000)); + dev_dbg(spdifrx->dev, "start fifo timer\n"); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: @@ -391,6 +398,7 @@ static int rk_spdifrx_dai_probe(struct snd_soc_dai *dai) ARRAY_SIZE(rk_spdifrx_controls)); rk_spdifrx_parse_quirks(spdifrx); + spdifrx->need_reset = true; return 0; } @@ -618,6 +626,24 @@ static unsigned int rk_spdifrx_convert_sample_rate(unsigned int mclk, unsigned i return 0; } +static int rk_spdifrx_disable_dma(struct rk_spdifrx_dev *spdifrx) +{ + int ret; + + ret = regmap_update_bits(spdifrx->regmap, SPDIFRX_DMACR, + SPDIFRX_DMACR_RDE_MASK, + SPDIFRX_DMACR_RDE_DISABLE); + + if (ret != 0) { + dev_err(spdifrx->dev, "Failed to disable rxdma\n"); + return ret; + } + + dev_dbg(spdifrx->dev, "rxdma disabled\n"); + + return ret; +} + static irqreturn_t rk_spdifrx_isr(int irq, void *dev_id) { struct rk_spdifrx_dev *spdifrx = dev_id; @@ -642,6 +668,7 @@ static irqreturn_t rk_spdifrx_isr(int irq, void *dev_id) regmap_write(spdifrx->regmap, SPDIFRX_INTCLR, SPDIFRX_INTCLR_NVLDICLR); rk_spdifrx_reset(spdifrx); spdifrx->need_reset = true; + rk_spdifrx_disable_dma(spdifrx); regmap_update_bits(spdifrx->regmap, SPDIFRX_INTEN, SPDIFRX_INTEN_NVLDIE_MASK, SPDIFRX_INTEN_NVLDIE_DIS); } @@ -658,6 +685,7 @@ static irqreturn_t rk_spdifrx_isr(int irq, void *dev_id) regmap_write(spdifrx->regmap, SPDIFRX_INTCLR, SPDIFRX_INTCLR_PEICLR); rk_spdifrx_reset(spdifrx); spdifrx->need_reset = true; + rk_spdifrx_disable_dma(spdifrx); } if (intsr & SPDIFRX_INTSR_NPSPISR_ACTIVE) { @@ -679,6 +707,7 @@ static irqreturn_t rk_spdifrx_isr(int irq, void *dev_id) regmap_write(spdifrx->regmap, SPDIFRX_INTCLR, SPDIFRX_INTCLR_BMDEICLR); rk_spdifrx_reset(spdifrx); spdifrx->need_reset = true; + rk_spdifrx_disable_dma(spdifrx); } if (intsr & SPDIFRX_INTSR_NSYNCISR_ACTIVE) { @@ -690,7 +719,7 @@ static irqreturn_t rk_spdifrx_isr(int irq, void *dev_id) regmap_update_bits(spdifrx->regmap, SPDIFRX_INTEN, SPDIFRX_INTEN_NSYNCIE_MASK, SPDIFRX_INTEN_NSYNCIE_DIS); regmap_write(spdifrx->regmap, SPDIFRX_INTCLR, SPDIFRX_INTCLR_NSYNCICLR); - regmap_write(spdifrx->regmap, SPDIFRX_CLR, 0x1); + regmap_write(spdifrx->regmap, SPDIFRX_CLR, SPDIFRX_CLR_RXSC); } if (intsr & SPDIFRX_INTSR_BTEISR_ACTIVE) { @@ -784,8 +813,18 @@ static void rk_spdifrx_fifo_timer_isr(struct timer_list *timer) ktime_t start, end; int ret; - if (spdifrx->info.sync == 0 || spdifrx->need_reset || spdifrx->info.sample_rate_src == 0) + if (spdifrx->info.sync == 0 || spdifrx->need_reset || spdifrx->info.sample_rate_src == 0) { + dev_dbg(spdifrx->dev, "exit fifo timer\n"); + dev_dbg(spdifrx->dev, "sync: %d, need_reset: %d, sample_rate_src: %u\n", + spdifrx->info.sync, spdifrx->need_reset, spdifrx->info.sample_rate_src); return; + } + + regmap_read(spdifrx->regmap, SPDIFRX_DMACR, &val); + if ((val & SPDIFRX_DMACR_RDE_MASK) == 0) { + dev_dbg(spdifrx->dev, "exit fifo timer: rxdma disabled\n"); + return; + } timeout_us = DIV_ROUND_UP(500000, spdifrx->info.sample_rate_src); @@ -802,6 +841,7 @@ static void rk_spdifrx_fifo_timer_isr(struct timer_list *timer) dev_info(spdifrx->dev, "no data to fifo, reset\n"); rk_spdifrx_reset(spdifrx); spdifrx->need_reset = true; + rk_spdifrx_disable_dma(spdifrx); return; } } @@ -839,6 +879,7 @@ static void rk_spdifrx_debounce_timer_isr(struct timer_list *timer) if (spdifrx->need_reset) { rk_spdifrx_reset(spdifrx); spdifrx->need_reset = false; + schedule_work(&spdifrx->xrun_work); } else { regmap_read(spdifrx->regmap, SPDIFRX_CDRST, &val); if (spdifrx->cdr_count_avg) @@ -858,7 +899,6 @@ static void rk_spdifrx_debounce_timer_isr(struct timer_list *timer) snd_ctl_notify(dai->component->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &sync_kctl->id); spdifrx->info.sample_rate_cal_last = spdifrx->info.sample_rate_cal; - mod_timer(&spdifrx->fifo_timer, jiffies + msecs_to_jiffies(1000)); if (spdifrx->info.liner_pcm == 1) regmap_update_bits(spdifrx->regmap, SPDIFRX_INTEN, SPDIFRX_INTEN_NVLDIE_MASK, @@ -873,6 +913,24 @@ static void rk_spdifrx_debounce_timer_isr(struct timer_list *timer) } } +static void rk_spdifrx_xrun_work(struct work_struct *work) +{ + struct rk_spdifrx_dev *spdifrx = container_of(work, struct rk_spdifrx_dev, xrun_work); + int ret; + u32 val; + + ret = regmap_read_poll_timeout(spdifrx->regmap, SPDIFRX_CDR, val, + ((val & SPDIFRX_CDR_CS_MASK) >> 9) == 0x3, 300, 3000); + if (!ret) { + if (spdifrx->substream) { + snd_pcm_stop_xrun(spdifrx->substream); + dev_dbg(spdifrx->dev, "stop xrun\n"); + } + } else { + dev_dbg(spdifrx->dev, "reset enter sync failed\n"); + } +} + static int rk_spdifrx_probe(struct platform_device *pdev) { struct rk_spdifrx_dev *spdifrx; @@ -911,6 +969,7 @@ static int rk_spdifrx_probe(struct platform_device *pdev) timer_setup(&spdifrx->debounce_timer, rk_spdifrx_debounce_timer_isr, 0); timer_setup(&spdifrx->non_liner_timer, rk_spdifrx_non_liner_timer_isr, 0); timer_setup(&spdifrx->fifo_timer, rk_spdifrx_fifo_timer_isr, 0); + INIT_WORK(&spdifrx->xrun_work, rk_spdifrx_xrun_work); ret = devm_request_threaded_irq(&pdev->dev, spdifrx->irq, NULL, rk_spdifrx_isr,