diff options
author | Shengjiu Wang <b02247@freescale.com> | 2014-04-14 15:19:54 +0800 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2014-04-16 08:58:20 -0500 |
commit | 440fbeee5bba4d4356a392ff35fe262774c48ea1 (patch) | |
tree | 39bdb1ee74dffff73fc6b96ee8e91b832ce3eab0 /sound | |
parent | 405588f31e432e5c6634a0817e4d804ead87882b (diff) |
ENGR00307835-3 ASoC: fsl: implement the ESAI xrun handler.
When esai xrun happened, there is possibility of channel swap. So ESAI
need to be reset.
Signed-off-by: Shengjiu Wang <b02247@freescale.com>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/fsl/fsl_esai.c | 136 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_esai.h | 5 | ||||
-rw-r--r-- | sound/soc/fsl/imx-pcm-dma.c | 20 |
3 files changed, 160 insertions, 1 deletions
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 6f6adcec7d41..762ac316fa32 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -35,6 +35,13 @@ #define IMX_ESAI_NET (1 << 0) #define IMX_ESAI_SYN (1 << 1) +static inline void write_esai_mask(u32 __iomem *addr, u32 clear, u32 set) +{ + u32 val = readl(addr); + val = (val & ~clear) | set; + writel(val, addr); +} + static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int dir) { @@ -308,6 +315,8 @@ static int fsl_esai_startup(struct snd_pcm_substream *substream, clk_enable(esai->clk); clk_prepare_enable(esai->dmaclk); + esai->substream[substream->stream] = substream; + ESAI_DUMP(); return 0; } @@ -463,6 +472,8 @@ static void fsl_esai_shutdown(struct snd_pcm_substream *substream, writel(0, esai->base + ESAI_PCRC); } + esai->substream[substream->stream] = NULL; + clk_disable_unprepare(esai->dmaclk); clk_disable(esai->clk); } @@ -578,6 +589,126 @@ static const struct snd_soc_component_driver fsl_esai_component = { .name = "fsl-esai", }; +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 = snd_soc_dai_get_drvdata(cpu_dai); + u32 saisr = readl(esai->base + ESAI_SAISR); + + return saisr & (ESAI_SAISR_TUE | ESAI_SAISR_ROE) ; +} + +static int store_reg(struct snd_soc_dai *cpu_dai) +{ + struct fsl_esai *esai = snd_soc_dai_get_drvdata(cpu_dai); + + esai->reg_cache[0] = readl(esai->base + ESAI_ECR); + esai->reg_cache[2] = readl(esai->base + ESAI_TFCR); + esai->reg_cache[4] = readl(esai->base + ESAI_RFCR); + esai->reg_cache[8] = readl(esai->base + ESAI_SAICR); + esai->reg_cache[9] = readl(esai->base + ESAI_TCR); + esai->reg_cache[10] = readl(esai->base + ESAI_TCCR); + esai->reg_cache[11] = readl(esai->base + ESAI_RCR); + esai->reg_cache[12] = readl(esai->base + ESAI_RCCR); + esai->reg_cache[13] = readl(esai->base + ESAI_TSMA); + esai->reg_cache[14] = readl(esai->base + ESAI_TSMB); + esai->reg_cache[15] = readl(esai->base + ESAI_RSMA); + esai->reg_cache[16] = readl(esai->base + ESAI_RSMB); + return 0; +} + +static int restore_reg(struct snd_soc_dai *cpu_dai) +{ + struct fsl_esai *esai = snd_soc_dai_get_drvdata(cpu_dai); + + writel(esai->reg_cache[0], esai->base + ESAI_ECR); + writel(esai->reg_cache[2] & ~ESAI_TFCR_TFEN, esai->base + ESAI_TFCR); + writel(esai->reg_cache[4] & ~ESAI_RFCR_RFEN, esai->base + ESAI_RFCR); + writel(esai->reg_cache[8], esai->base + ESAI_SAICR); + writel(esai->reg_cache[9] & ~ESAI_TCR_TE(12), esai->base + ESAI_TCR); + writel(esai->reg_cache[10], esai->base + ESAI_TCCR); + writel(esai->reg_cache[11] & ~ESAI_RCR_RE(8), esai->base + ESAI_RCR); + writel(esai->reg_cache[12], esai->base + ESAI_RCCR); + writel(esai->reg_cache[13], esai->base + ESAI_TSMA); + writel(esai->reg_cache[14], esai->base + ESAI_TSMB); + writel(esai->reg_cache[15], esai->base + ESAI_RSMA); + writel(esai->reg_cache[16], esai->base + ESAI_RSMB); + return 0; +} + +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 = snd_soc_dai_get_drvdata(cpu_dai); + ESAI_DUMP(); + + if (stop) { + stop_lock_stream(esai->substream[0]); + stop_lock_stream(esai->substream[1]); + } + + store_reg(cpu_dai); + + writel(ESAI_ECR_ESAIEN | ESAI_ECR_ERST, esai->base + ESAI_ECR); + writel(ESAI_ECR_ESAIEN, esai->base + ESAI_ECR); + + write_esai_mask(esai->base+ESAI_TCR, 0, ESAI_TCR_TPR); + write_esai_mask(esai->base+ESAI_RCR, 0, ESAI_RCR_RPR); + + restore_reg(cpu_dai); + + write_esai_mask(esai->base+ESAI_TCR, ESAI_TCR_TPR, 0); + write_esai_mask(esai->base+ESAI_RCR, ESAI_RCR_RPR, 0); + + writel(ESAI_GPIO_ESAI, esai->base + ESAI_PRRC); + writel(ESAI_GPIO_ESAI, esai->base + ESAI_PCRC); + + /* read "TUE" flag.*/ + readl(esai->base + ESAI_SAISR); + + if (stop) { + start_unlock_stream(esai->substream[1]); + start_unlock_stream(esai->substream[0]); + } + ESAI_DUMP(); +} + static int fsl_esai_reg_init(struct fsl_esai *esai) { u32 xccr, slots = 2; @@ -674,6 +805,11 @@ static int fsl_esai_probe(struct platform_device *pdev) esai->dma_params_tx.filter_data = &esai->filter_data_tx; esai->dma_params_rx.filter_data = &esai->filter_data_rx; + esai->dma_params_tx.check_xrun = fsl_esai_check_xrun; + esai->dma_params_rx.check_xrun = fsl_esai_check_xrun; + esai->dma_params_tx.device_reset = fsl_esai_reset; + esai->dma_params_rx.device_reset = fsl_esai_reset; + ret = of_property_read_u32_array(pdev->dev.of_node, "fsl,esai-dma-events", dma_events, 2); if (ret) { diff --git a/sound/soc/fsl/fsl_esai.h b/sound/soc/fsl/fsl_esai.h index f0b6198a69a6..daf080641277 100644 --- a/sound/soc/fsl/fsl_esai.h +++ b/sound/soc/fsl/fsl_esai.h @@ -1,7 +1,7 @@ /* * imx-esai.h -- ESAI driver header file for Freescale IMX * - * Copyright 2008-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008-2014 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -315,6 +315,7 @@ #define IMX_DAI_ESAI_TX 0x04 #define IMX_DAI_ESAI_RX 0x08 #define IMX_DAI_ESAI_TXRX (IMX_DAI_ESAI_TX | IMX_DAI_ESAI_RX) +#define REG_CACHE_NUM 20 struct fsl_esai { struct clk *clk; @@ -328,8 +329,10 @@ struct fsl_esai { struct snd_dmaengine_dai_dma_data dma_params_tx; struct imx_dma_data filter_data_tx; struct imx_dma_data filter_data_rx; + struct snd_pcm_substream *substream[2]; char name[32]; + u32 reg_cache[REG_CACHE_NUM]; }; #endif diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index 33752da05482..aa74383daa82 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -94,14 +94,34 @@ static void imx_pcm_dma_set_config_from_dai_data( } } +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) |