From 96b57692f2b1d7e244b85e80037321c7d851f9dc Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 10 Apr 2014 10:11:00 +0800 Subject: ENGR00307835-4 ASoC: fsl: implement ASRC P2P xrun handler When ASRC P2P is working, it will check the xrun status of cpu dai in the back end bistream. then will do Whole route stop and restart. Signed-off-by: Shengjiu Wang (cherry picked from commit 28d18f23fe7577f613ad8cfb4699bba26178304b) --- sound/soc/fsl/fsl_asrc.c | 103 +++++++++++++++++++++++++++++++++++++++++++ sound/soc/fsl/fsl_asrc.h | 1 + sound/soc/fsl/fsl_asrc_dma.c | 7 +++ 3 files changed, 111 insertions(+) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 29adf261e986..832e822d1c9f 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -537,7 +537,26 @@ static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } +static int fsl_asrc_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(cpu_dai); + + asrc_priv->substream[substream->stream] = substream; + return 0; +} + +static void fsl_asrc_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(cpu_dai); + + asrc_priv->substream[substream->stream] = NULL; +} + static struct snd_soc_dai_ops fsl_asrc_dai_ops = { + .startup = fsl_asrc_dai_startup, + .shutdown = fsl_asrc_dai_shutdown, .hw_params = fsl_asrc_dai_hw_params, .hw_free = fsl_asrc_dai_hw_free, .trigger = fsl_asrc_dai_trigger, @@ -700,6 +719,85 @@ static struct regmap_config fsl_asrc_regmap_config = { #include "fsl_asrc_m2m.c" +static bool fsl_asrc_check_xrun(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_dmaengine_dai_dma_data *dma_params_be = NULL; + struct snd_pcm_substream *be_substream; + struct snd_soc_dpcm *dpcm; + int ret = 0; + + /* find the be for this fe stream */ + list_for_each_entry(dpcm, &rtd->dpcm[substream->stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_soc_dai *dai = be->cpu_dai; + + if (dpcm->fe != rtd) + continue; + + be_substream = snd_soc_dpcm_get_substream(be, substream->stream); + dma_params_be = snd_soc_dai_get_dma_data(dai, be_substream); + if (dma_params_be->check_xrun && dma_params_be->check_xrun(be_substream)) + ret = 1; + } + + return ret; +} + +static int stop_lock_stream(struct snd_pcm_substream *substream) +{ + if (substream) { + snd_pcm_stream_lock_irq(substream); + if (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING) + substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP); + } + return 0; +} + +static int start_unlock_stream(struct snd_pcm_substream *substream) +{ + if (substream) { + if (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING) + substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START); + snd_pcm_stream_unlock_irq(substream); + } + return 0; +} + +static void fsl_asrc_reset(struct snd_pcm_substream *substream, bool stop) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct snd_dmaengine_dai_dma_data *dma_params_be = NULL; + struct snd_soc_dpcm *dpcm; + struct snd_pcm_substream *be_substream; + + if (stop) { + stop_lock_stream(asrc_priv->substream[0]); + stop_lock_stream(asrc_priv->substream[1]); + } + + /* find the be for this fe stream */ + list_for_each_entry(dpcm, &rtd->dpcm[substream->stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_soc_dai *dai = be->cpu_dai; + + if (dpcm->fe != rtd) + continue; + + be_substream = snd_soc_dpcm_get_substream(be, substream->stream); + dma_params_be = snd_soc_dai_get_dma_data(dai, be_substream); + dma_params_be->device_reset(be_substream, 0); + break; + } + + if (stop) { + start_unlock_stream(asrc_priv->substream[1]); + start_unlock_stream(asrc_priv->substream[0]); + } +} + /** * Initialize ASRC registers with a default configurations */ @@ -889,6 +987,11 @@ static int fsl_asrc_probe(struct platform_device *pdev) return -EINVAL; } + asrc_priv->dma_params_tx.check_xrun = fsl_asrc_check_xrun; + asrc_priv->dma_params_rx.check_xrun = fsl_asrc_check_xrun; + asrc_priv->dma_params_tx.device_reset = fsl_asrc_reset; + asrc_priv->dma_params_rx.device_reset = fsl_asrc_reset; + if (asrc_priv->asrc_width != 16 && asrc_priv->asrc_width != 24) { dev_warn(&pdev->dev, "unsupported width, switching to 24bit\n"); asrc_priv->asrc_width = 24; diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h index 9cb1689b1b95..5bf8831089df 100644 --- a/sound/soc/fsl/fsl_asrc.h +++ b/sound/soc/fsl/fsl_asrc.h @@ -354,6 +354,7 @@ struct fsl_asrc { struct clk *asrck_clk[ASRC_CLK_MAX_NUM]; spinlock_t lock; + struct snd_pcm_substream *substream[2]; struct fsl_asrc_pair *pair[ASRC_PAIR_MAX_NUM]; unsigned int channel_bits; unsigned int channel_avail; diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c index ffc000bc1f15..28bc57f897a4 100644 --- a/sound/soc/fsl/fsl_asrc_dma.c +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -50,12 +50,19 @@ static void fsl_asrc_dma_complete(void *arg) struct snd_pcm_substream *substream = arg; struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_asrc_pair *pair = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_dmaengine_dai_dma_data *dma_data; pair->pos += snd_pcm_lib_period_bytes(substream); if (pair->pos >= snd_pcm_lib_buffer_bytes(substream)) pair->pos = 0; snd_pcm_period_elapsed(substream); + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + if (dma_data->check_xrun && dma_data->check_xrun(substream)) + dma_data->device_reset(substream, 1); + } static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream) -- cgit v1.2.3