]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00307835-3 ASoC: fsl: implement the ESAI xrun handler.
authorShengjiu Wang <b02247@freescale.com>
Mon, 14 Apr 2014 07:19:54 +0000 (15:19 +0800)
committerNitin Garg <nitin.garg@freescale.com>
Fri, 16 Jan 2015 03:17:58 +0000 (21:17 -0600)
When esai xrun happened, there is possibility of channel swap. So ESAI
need to be reset.

Signed-off-by: Shengjiu Wang <b02247@freescale.com>
(cherry picked from commit 440fbeee5bba4d4356a392ff35fe262774c48ea1)

sound/soc/fsl/fsl_esai.c
sound/soc/fsl/imx-pcm-dma.c

index 878a24de6e8aef7312ee10290363b3129536b608..cf193c08af156ccccbac27d11478705d9541bf23 100644 (file)
@@ -49,6 +49,7 @@
 struct fsl_esai {
        struct snd_dmaengine_dai_dma_data dma_params_rx;
        struct snd_dmaengine_dai_dma_data dma_params_tx;
+       struct snd_pcm_substream *substream[2];
        struct platform_device *pdev;
        struct regmap *regmap;
        struct clk *coreclk;
@@ -497,6 +498,8 @@ static int fsl_esai_startup(struct snd_pcm_substream *substream,
                                   ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2));
        }
 
+       esai_priv->substream[substream->stream] = substream;
+
        return 0;
 
 err_fsysclk:
@@ -560,6 +563,8 @@ static void fsl_esai_shutdown(struct snd_pcm_substream *substream,
 {
        struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
 
+       esai_priv->substream[substream->stream] = NULL;
+
        if (!IS_ERR(esai_priv->fsysclk))
                clk_disable_unprepare(esai_priv->fsysclk);
        if (!IS_ERR(esai_priv->extalclk))
@@ -726,6 +731,107 @@ static struct regmap_config fsl_esai_regmap_config = {
        .writeable_reg = fsl_esai_writeable_reg,
 };
 
+static bool fsl_esai_check_xrun(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(cpu_dai);
+       u32 saisr;
+
+       regmap_read(esai_priv->regmap, REG_ESAI_SAISR, &saisr);
+
+       return saisr & (ESAI_SAISR_TUE | ESAI_SAISR_ROE) ;
+}
+
+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;
+}
+
+/*
+ *Here is ESAI underrun reset step:
+ *1. Read "TUE" and got TUE=1
+ *2. stop DMA.
+ *3. stop ESAI TX section.
+ *4. Set the transmitter section individual reset "TPR=1"
+ *5. Reset the ESAI Transmit FIFO (set ESAI_TFCR[1]=1).
+ *6. Config the control registers ESAI_TCCR and ESAI_TCR.config the Transmit FIFO register.
+ *7. clear "TPR"
+ *8. read "TUE"
+ *9. Prefill ESAI TX FIFO.
+ *10.Start DMA.
+ *11 Enable the ESAI
+ */
+static void fsl_esai_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_esai *esai_priv = snd_soc_dai_get_drvdata(cpu_dai);
+       u32 saisr;
+
+       if (stop) {
+               stop_lock_stream(esai_priv->substream[0]);
+               stop_lock_stream(esai_priv->substream[1]);
+       }
+
+
+       regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
+                               ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK,
+                               ESAI_ECR_ESAIEN | ESAI_ECR_ERST);
+       regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
+                               ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK,
+                               ESAI_ECR_ESAIEN);
+
+       regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, ESAI_xCR_xPR_MASK, ESAI_xCR_xPR);
+       regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, ESAI_xCR_xPR_MASK, ESAI_xCR_xPR);
+
+       regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, ESAI_PRRC_PDC_MASK, 0);
+       regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, ESAI_PCRC_PC_MASK, 0);
+
+       /*
+        * Add fifo reset here, because the regcache_sync will write one more data to ETDR.
+        * Which will cause channel shift.
+        */
+       regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR_MASK, ESAI_xFCR_xFR);
+       regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR_MASK, ESAI_xFCR_xFR);
+
+       regcache_mark_dirty(esai_priv->regmap);
+       regcache_sync(esai_priv->regmap);
+
+       regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR_MASK, 0);
+       regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR_MASK, 0);
+
+       regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, ESAI_xCR_xPR_MASK, 0);
+       regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, ESAI_xCR_xPR_MASK, 0);
+
+       regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
+                                  ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO));
+       regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,
+                                  ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO));
+
+       regmap_read(esai_priv->regmap, REG_ESAI_SAISR, &saisr);
+
+       if (stop) {
+               start_unlock_stream(esai_priv->substream[1]);
+               start_unlock_stream(esai_priv->substream[0]);
+       }
+}
+
 static int fsl_esai_probe(struct platform_device *pdev)
 {
        struct device_node *np = pdev->dev.of_node;
@@ -817,6 +923,11 @@ static int fsl_esai_probe(struct platform_device *pdev)
        esai_priv->dma_params_tx.addr = res->start + REG_ESAI_ETDR;
        esai_priv->dma_params_rx.addr = res->start + REG_ESAI_ERDR;
 
+       esai_priv->dma_params_tx.check_xrun = fsl_esai_check_xrun;
+       esai_priv->dma_params_rx.check_xrun = fsl_esai_check_xrun;
+       esai_priv->dma_params_tx.device_reset = fsl_esai_reset;
+       esai_priv->dma_params_rx.device_reset = fsl_esai_reset;
+
        esai_priv->synchronous =
                of_property_read_bool(np, "fsl,esai-synchronous");
 
index 1fc01ed3279dbee0991a343c082aa6c9ea5f042b..cb1eba73342a5956457236e34d4014e7caee8667 100644 (file)
@@ -48,9 +48,49 @@ static const struct snd_pcm_hardware imx_pcm_hardware = {
        .fifo_size = 0,
 };
 
+static void imx_pcm_dma_complete(void *arg)
+{
+       struct snd_pcm_substream *substream = arg;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct dmaengine_pcm_runtime_data *prtd = substream->runtime->private_data;
+       struct snd_dmaengine_dai_dma_data *dma_data;
+
+       prtd->pos += snd_pcm_lib_period_bytes(substream);
+       if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream))
+               prtd->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 imx_pcm_dma_prepare_slave_config(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_dmaengine_dai_dma_data *dma_data;
+       struct dmaengine_pcm_runtime_data *prtd = substream->runtime->private_data;
+       int ret;
+
+       dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+       prtd->callback = imx_pcm_dma_complete;
+
+       ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config);
+       if (ret)
+               return ret;
+
+       snd_dmaengine_pcm_set_config_from_dai_data(substream, dma_data,
+               slave_config);
+
+       return 0;
+
+}
+
 static const struct snd_dmaengine_pcm_config imx_dmaengine_pcm_config = {
        .pcm_hardware = &imx_pcm_hardware,
-       .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+       .prepare_slave_config = imx_pcm_dma_prepare_slave_config,
        .compat_filter_fn = filter,
        .prealloc_buffer_size = IMX_DEFAULT_DMABUF_SIZE,
 };