diff options
author | Shengjiu Wang <b02247@freescale.com> | 2014-04-14 03:19:54 -0400 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2014-04-16 09:58:20 -0400 |
commit | 440fbeee5bba4d4356a392ff35fe262774c48ea1 (patch) | |
tree | 39bdb1ee74dffff73fc6b96ee8e91b832ce3eab0 | |
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>
-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 @@ | |||
35 | #define IMX_ESAI_NET (1 << 0) | 35 | #define IMX_ESAI_NET (1 << 0) |
36 | #define IMX_ESAI_SYN (1 << 1) | 36 | #define IMX_ESAI_SYN (1 << 1) |
37 | 37 | ||
38 | static inline void write_esai_mask(u32 __iomem *addr, u32 clear, u32 set) | ||
39 | { | ||
40 | u32 val = readl(addr); | ||
41 | val = (val & ~clear) | set; | ||
42 | writel(val, addr); | ||
43 | } | ||
44 | |||
38 | static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, | 45 | static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, |
39 | int clk_id, unsigned int freq, int dir) | 46 | int clk_id, unsigned int freq, int dir) |
40 | { | 47 | { |
@@ -308,6 +315,8 @@ static int fsl_esai_startup(struct snd_pcm_substream *substream, | |||
308 | clk_enable(esai->clk); | 315 | clk_enable(esai->clk); |
309 | clk_prepare_enable(esai->dmaclk); | 316 | clk_prepare_enable(esai->dmaclk); |
310 | 317 | ||
318 | esai->substream[substream->stream] = substream; | ||
319 | |||
311 | ESAI_DUMP(); | 320 | ESAI_DUMP(); |
312 | return 0; | 321 | return 0; |
313 | } | 322 | } |
@@ -463,6 +472,8 @@ static void fsl_esai_shutdown(struct snd_pcm_substream *substream, | |||
463 | writel(0, esai->base + ESAI_PCRC); | 472 | writel(0, esai->base + ESAI_PCRC); |
464 | } | 473 | } |
465 | 474 | ||
475 | esai->substream[substream->stream] = NULL; | ||
476 | |||
466 | clk_disable_unprepare(esai->dmaclk); | 477 | clk_disable_unprepare(esai->dmaclk); |
467 | clk_disable(esai->clk); | 478 | clk_disable(esai->clk); |
468 | } | 479 | } |
@@ -578,6 +589,126 @@ static const struct snd_soc_component_driver fsl_esai_component = { | |||
578 | .name = "fsl-esai", | 589 | .name = "fsl-esai", |
579 | }; | 590 | }; |
580 | 591 | ||
592 | static bool fsl_esai_check_xrun(struct snd_pcm_substream *substream) | ||
593 | { | ||
594 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
595 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
596 | struct fsl_esai *esai = snd_soc_dai_get_drvdata(cpu_dai); | ||
597 | u32 saisr = readl(esai->base + ESAI_SAISR); | ||
598 | |||
599 | return saisr & (ESAI_SAISR_TUE | ESAI_SAISR_ROE) ; | ||
600 | } | ||
601 | |||
602 | static int store_reg(struct snd_soc_dai *cpu_dai) | ||
603 | { | ||
604 | struct fsl_esai *esai = snd_soc_dai_get_drvdata(cpu_dai); | ||
605 | |||
606 | esai->reg_cache[0] = readl(esai->base + ESAI_ECR); | ||
607 | esai->reg_cache[2] = readl(esai->base + ESAI_TFCR); | ||
608 | esai->reg_cache[4] = readl(esai->base + ESAI_RFCR); | ||
609 | esai->reg_cache[8] = readl(esai->base + ESAI_SAICR); | ||
610 | esai->reg_cache[9] = readl(esai->base + ESAI_TCR); | ||
611 | esai->reg_cache[10] = readl(esai->base + ESAI_TCCR); | ||
612 | esai->reg_cache[11] = readl(esai->base + ESAI_RCR); | ||
613 | esai->reg_cache[12] = readl(esai->base + ESAI_RCCR); | ||
614 | esai->reg_cache[13] = readl(esai->base + ESAI_TSMA); | ||
615 | esai->reg_cache[14] = readl(esai->base + ESAI_TSMB); | ||
616 | esai->reg_cache[15] = readl(esai->base + ESAI_RSMA); | ||
617 | esai->reg_cache[16] = readl(esai->base + ESAI_RSMB); | ||
618 | return 0; | ||
619 | } | ||
620 | |||
621 | static int restore_reg(struct snd_soc_dai *cpu_dai) | ||
622 | { | ||
623 | struct fsl_esai *esai = snd_soc_dai_get_drvdata(cpu_dai); | ||
624 | |||
625 | writel(esai->reg_cache[0], esai->base + ESAI_ECR); | ||
626 | writel(esai->reg_cache[2] & ~ESAI_TFCR_TFEN, esai->base + ESAI_TFCR); | ||
627 | writel(esai->reg_cache[4] & ~ESAI_RFCR_RFEN, esai->base + ESAI_RFCR); | ||
628 | writel(esai->reg_cache[8], esai->base + ESAI_SAICR); | ||
629 | writel(esai->reg_cache[9] & ~ESAI_TCR_TE(12), esai->base + ESAI_TCR); | ||
630 | writel(esai->reg_cache[10], esai->base + ESAI_TCCR); | ||
631 | writel(esai->reg_cache[11] & ~ESAI_RCR_RE(8), esai->base + ESAI_RCR); | ||
632 | writel(esai->reg_cache[12], esai->base + ESAI_RCCR); | ||
633 | writel(esai->reg_cache[13], esai->base + ESAI_TSMA); | ||
634 | writel(esai->reg_cache[14], esai->base + ESAI_TSMB); | ||
635 | writel(esai->reg_cache[15], esai->base + ESAI_RSMA); | ||
636 | writel(esai->reg_cache[16], esai->base + ESAI_RSMB); | ||
637 | return 0; | ||
638 | } | ||
639 | |||
640 | static int stop_lock_stream(struct snd_pcm_substream *substream) | ||
641 | { | ||
642 | if (substream) { | ||
643 | snd_pcm_stream_lock_irq(substream); | ||
644 | if (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING) | ||
645 | substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP); | ||
646 | } | ||
647 | return 0; | ||
648 | } | ||
649 | |||
650 | static int start_unlock_stream(struct snd_pcm_substream *substream) | ||
651 | { | ||
652 | if (substream) { | ||
653 | if (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING) | ||
654 | substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START); | ||
655 | snd_pcm_stream_unlock_irq(substream); | ||
656 | } | ||
657 | return 0; | ||
658 | } | ||
659 | |||
660 | /* | ||
661 | *Here is ESAI underrun reset step: | ||
662 | *1. Read "TUE" and got TUE=1 | ||
663 | *2. stop DMA. | ||
664 | *3. stop ESAI TX section. | ||
665 | *4. Set the transmitter section individual reset "TPR=1" | ||
666 | *5. Reset the ESAI Transmit FIFO (set ESAI_TFCR[1]=1). | ||
667 | *6. Config the control registers ESAI_TCCR and ESAI_TCR.config the Transmit FIFO register. | ||
668 | *7. clear "TPR" | ||
669 | *8. read "TUE" | ||
670 | *9. Prefill ESAI TX FIFO. | ||
671 | *10.Start DMA. | ||
672 | *11 Enable the ESAI | ||
673 | */ | ||
674 | static void fsl_esai_reset(struct snd_pcm_substream *substream, bool stop) | ||
675 | { | ||
676 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
677 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
678 | struct fsl_esai *esai = snd_soc_dai_get_drvdata(cpu_dai); | ||
679 | ESAI_DUMP(); | ||
680 | |||
681 | if (stop) { | ||
682 | stop_lock_stream(esai->substream[0]); | ||
683 | stop_lock_stream(esai->substream[1]); | ||
684 | } | ||
685 | |||
686 | store_reg(cpu_dai); | ||
687 | |||
688 | writel(ESAI_ECR_ESAIEN | ESAI_ECR_ERST, esai->base + ESAI_ECR); | ||
689 | writel(ESAI_ECR_ESAIEN, esai->base + ESAI_ECR); | ||
690 | |||
691 | write_esai_mask(esai->base+ESAI_TCR, 0, ESAI_TCR_TPR); | ||
692 | write_esai_mask(esai->base+ESAI_RCR, 0, ESAI_RCR_RPR); | ||
693 | |||
694 | restore_reg(cpu_dai); | ||
695 | |||
696 | write_esai_mask(esai->base+ESAI_TCR, ESAI_TCR_TPR, 0); | ||
697 | write_esai_mask(esai->base+ESAI_RCR, ESAI_RCR_RPR, 0); | ||
698 | |||
699 | writel(ESAI_GPIO_ESAI, esai->base + ESAI_PRRC); | ||
700 | writel(ESAI_GPIO_ESAI, esai->base + ESAI_PCRC); | ||
701 | |||
702 | /* read "TUE" flag.*/ | ||
703 | readl(esai->base + ESAI_SAISR); | ||
704 | |||
705 | if (stop) { | ||
706 | start_unlock_stream(esai->substream[1]); | ||
707 | start_unlock_stream(esai->substream[0]); | ||
708 | } | ||
709 | ESAI_DUMP(); | ||
710 | } | ||
711 | |||
581 | static int fsl_esai_reg_init(struct fsl_esai *esai) | 712 | static int fsl_esai_reg_init(struct fsl_esai *esai) |
582 | { | 713 | { |
583 | u32 xccr, slots = 2; | 714 | u32 xccr, slots = 2; |
@@ -674,6 +805,11 @@ static int fsl_esai_probe(struct platform_device *pdev) | |||
674 | esai->dma_params_tx.filter_data = &esai->filter_data_tx; | 805 | esai->dma_params_tx.filter_data = &esai->filter_data_tx; |
675 | esai->dma_params_rx.filter_data = &esai->filter_data_rx; | 806 | esai->dma_params_rx.filter_data = &esai->filter_data_rx; |
676 | 807 | ||
808 | esai->dma_params_tx.check_xrun = fsl_esai_check_xrun; | ||
809 | esai->dma_params_rx.check_xrun = fsl_esai_check_xrun; | ||
810 | esai->dma_params_tx.device_reset = fsl_esai_reset; | ||
811 | esai->dma_params_rx.device_reset = fsl_esai_reset; | ||
812 | |||
677 | ret = of_property_read_u32_array(pdev->dev.of_node, | 813 | ret = of_property_read_u32_array(pdev->dev.of_node, |
678 | "fsl,esai-dma-events", dma_events, 2); | 814 | "fsl,esai-dma-events", dma_events, 2); |
679 | if (ret) { | 815 | 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 @@ | |||
1 | /* | 1 | /* |
2 | * imx-esai.h -- ESAI driver header file for Freescale IMX | 2 | * imx-esai.h -- ESAI driver header file for Freescale IMX |
3 | * | 3 | * |
4 | * Copyright 2008-2013 Freescale Semiconductor, Inc. All Rights Reserved. | 4 | * Copyright 2008-2014 Freescale Semiconductor, Inc. All Rights Reserved. |
5 | */ | 5 | */ |
6 | 6 | ||
7 | /* | 7 | /* |
@@ -315,6 +315,7 @@ | |||
315 | #define IMX_DAI_ESAI_TX 0x04 | 315 | #define IMX_DAI_ESAI_TX 0x04 |
316 | #define IMX_DAI_ESAI_RX 0x08 | 316 | #define IMX_DAI_ESAI_RX 0x08 |
317 | #define IMX_DAI_ESAI_TXRX (IMX_DAI_ESAI_TX | IMX_DAI_ESAI_RX) | 317 | #define IMX_DAI_ESAI_TXRX (IMX_DAI_ESAI_TX | IMX_DAI_ESAI_RX) |
318 | #define REG_CACHE_NUM 20 | ||
318 | 319 | ||
319 | struct fsl_esai { | 320 | struct fsl_esai { |
320 | struct clk *clk; | 321 | struct clk *clk; |
@@ -328,8 +329,10 @@ struct fsl_esai { | |||
328 | struct snd_dmaengine_dai_dma_data dma_params_tx; | 329 | struct snd_dmaengine_dai_dma_data dma_params_tx; |
329 | struct imx_dma_data filter_data_tx; | 330 | struct imx_dma_data filter_data_tx; |
330 | struct imx_dma_data filter_data_rx; | 331 | struct imx_dma_data filter_data_rx; |
332 | struct snd_pcm_substream *substream[2]; | ||
331 | 333 | ||
332 | char name[32]; | 334 | char name[32]; |
335 | u32 reg_cache[REG_CACHE_NUM]; | ||
333 | }; | 336 | }; |
334 | 337 | ||
335 | #endif | 338 | #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( | |||
94 | } | 94 | } |
95 | } | 95 | } |
96 | 96 | ||
97 | static void imx_pcm_dma_complete(void *arg) | ||
98 | { | ||
99 | struct snd_pcm_substream *substream = arg; | ||
100 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
101 | struct dmaengine_pcm_runtime_data *prtd = substream->runtime->private_data; | ||
102 | struct snd_dmaengine_dai_dma_data *dma_data; | ||
103 | |||
104 | prtd->pos += snd_pcm_lib_period_bytes(substream); | ||
105 | if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream)) | ||
106 | prtd->pos = 0; | ||
107 | |||
108 | snd_pcm_period_elapsed(substream); | ||
109 | |||
110 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
111 | if (dma_data->check_xrun && dma_data->check_xrun(substream)) | ||
112 | dma_data->device_reset(substream, 1); | ||
113 | } | ||
114 | |||
97 | static int imx_pcm_dma_prepare_slave_config(struct snd_pcm_substream *substream, | 115 | static int imx_pcm_dma_prepare_slave_config(struct snd_pcm_substream *substream, |
98 | struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config) | 116 | struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config) |
99 | { | 117 | { |
100 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 118 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
101 | struct snd_dmaengine_dai_dma_data *dma_data; | 119 | struct snd_dmaengine_dai_dma_data *dma_data; |
120 | struct dmaengine_pcm_runtime_data *prtd = substream->runtime->private_data; | ||
102 | int ret; | 121 | int ret; |
103 | 122 | ||
104 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | 123 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); |
124 | prtd->callback = imx_pcm_dma_complete; | ||
105 | 125 | ||
106 | ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config); | 126 | ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config); |
107 | if (ret) | 127 | if (ret) |