mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-05 10:31:46 +09:00
ASoC: rockchip: spdifrx: fix issue where reset causes underrun in app
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 <shengquan.zhong@rock-chips.com>
This commit is contained in:
committed by
Tao Huang
parent
eae70e5ad3
commit
89360f8db2
@@ -16,6 +16,7 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user