diff options
Diffstat (limited to 'sound/soc/fsl')
-rw-r--r-- | sound/soc/fsl/Kconfig | 32 | ||||
-rw-r--r-- | sound/soc/fsl/Makefile | 7 | ||||
-rw-r--r-- | sound/soc/fsl/efika-audio-fabric.c | 90 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_ssi.c | 11 | ||||
-rw-r--r-- | sound/soc/fsl/mpc5200_dma.c | 582 | ||||
-rw-r--r-- | sound/soc/fsl/mpc5200_dma.h | 81 | ||||
-rw-r--r-- | sound/soc/fsl/mpc5200_psc_ac97.c | 345 | ||||
-rw-r--r-- | sound/soc/fsl/mpc5200_psc_ac97.h | 15 | ||||
-rw-r--r-- | sound/soc/fsl/mpc5200_psc_i2s.c | 754 | ||||
-rw-r--r-- | sound/soc/fsl/mpc5200_psc_i2s.h | 12 | ||||
-rw-r--r-- | sound/soc/fsl/pcm030-audio-fabric.c | 90 |
11 files changed, 1312 insertions, 707 deletions
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 9fc908283371..8cb65ccad35f 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig | |||
@@ -1,5 +1,8 @@ | |||
1 | config SND_SOC_OF_SIMPLE | 1 | config SND_SOC_OF_SIMPLE |
2 | tristate | 2 | tristate |
3 | |||
4 | config SND_MPC52xx_DMA | ||
5 | tristate | ||
3 | 6 | ||
4 | # ASoC platform support for the Freescale MPC8610 SOC. This compiles drivers | 7 | # ASoC platform support for the Freescale MPC8610 SOC. This compiles drivers |
5 | # for the SSI and the Elo DMA controller. You will still need to select | 8 | # for the SSI and the Elo DMA controller. You will still need to select |
@@ -22,7 +25,34 @@ config SND_SOC_MPC8610_HPCD | |||
22 | config SND_SOC_MPC5200_I2S | 25 | config SND_SOC_MPC5200_I2S |
23 | tristate "Freescale MPC5200 PSC in I2S mode driver" | 26 | tristate "Freescale MPC5200 PSC in I2S mode driver" |
24 | depends on PPC_MPC52xx && PPC_BESTCOMM | 27 | depends on PPC_MPC52xx && PPC_BESTCOMM |
25 | select SND_SOC_OF_SIMPLE | 28 | select SND_MPC52xx_DMA |
26 | select PPC_BESTCOMM_GEN_BD | 29 | select PPC_BESTCOMM_GEN_BD |
27 | help | 30 | help |
28 | Say Y here to support the MPC5200 PSCs in I2S mode. | 31 | Say Y here to support the MPC5200 PSCs in I2S mode. |
32 | |||
33 | config SND_SOC_MPC5200_AC97 | ||
34 | tristate "Freescale MPC5200 PSC in AC97 mode driver" | ||
35 | depends on PPC_MPC52xx && PPC_BESTCOMM | ||
36 | select SND_SOC_AC97_BUS | ||
37 | select SND_MPC52xx_DMA | ||
38 | select PPC_BESTCOMM_GEN_BD | ||
39 | help | ||
40 | Say Y here to support the MPC5200 PSCs in AC97 mode. | ||
41 | |||
42 | config SND_MPC52xx_SOC_PCM030 | ||
43 | tristate "SoC AC97 Audio support for Phytec pcm030 and WM9712" | ||
44 | depends on PPC_MPC5200_SIMPLE | ||
45 | select SND_SOC_MPC5200_AC97 | ||
46 | select SND_SOC_WM9712 | ||
47 | help | ||
48 | Say Y if you want to add support for sound on the Phytec pcm030 | ||
49 | baseboard. | ||
50 | |||
51 | config SND_MPC52xx_SOC_EFIKA | ||
52 | tristate "SoC AC97 Audio support for bbplan Efika and STAC9766" | ||
53 | depends on PPC_EFIKA | ||
54 | select SND_SOC_MPC5200_AC97 | ||
55 | select SND_SOC_STAC9766 | ||
56 | help | ||
57 | Say Y if you want to add support for sound on the Efika. | ||
58 | |||
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index f85134c86387..a83a73967ec6 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile | |||
@@ -10,5 +10,12 @@ snd-soc-fsl-ssi-objs := fsl_ssi.o | |||
10 | snd-soc-fsl-dma-objs := fsl_dma.o | 10 | snd-soc-fsl-dma-objs := fsl_dma.o |
11 | obj-$(CONFIG_SND_SOC_MPC8610) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o | 11 | obj-$(CONFIG_SND_SOC_MPC8610) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o |
12 | 12 | ||
13 | # MPC5200 Platform Support | ||
14 | obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o | ||
13 | obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o | 15 | obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o |
16 | obj-$(CONFIG_SND_SOC_MPC5200_AC97) += mpc5200_psc_ac97.o | ||
17 | |||
18 | # MPC5200 Machine Support | ||
19 | obj-$(CONFIG_SND_MPC52xx_SOC_PCM030) += pcm030-audio-fabric.o | ||
20 | obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o | ||
14 | 21 | ||
diff --git a/sound/soc/fsl/efika-audio-fabric.c b/sound/soc/fsl/efika-audio-fabric.c new file mode 100644 index 000000000000..85b0e7569504 --- /dev/null +++ b/sound/soc/fsl/efika-audio-fabric.c | |||
@@ -0,0 +1,90 @@ | |||
1 | /* | ||
2 | * Efika driver for the PSC of the Freescale MPC52xx | ||
3 | * configured as AC97 interface | ||
4 | * | ||
5 | * Copyright 2008 Jon Smirl, Digispeaker | ||
6 | * Author: Jon Smirl <jonsmirl@gmail.com> | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public License | ||
9 | * version 2. This program is licensed "as is" without any warranty of any | ||
10 | * kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #include <linux/init.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/device.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/of_device.h> | ||
19 | #include <linux/of_platform.h> | ||
20 | #include <linux/dma-mapping.h> | ||
21 | |||
22 | #include <sound/core.h> | ||
23 | #include <sound/pcm.h> | ||
24 | #include <sound/pcm_params.h> | ||
25 | #include <sound/initval.h> | ||
26 | #include <sound/soc.h> | ||
27 | #include <sound/soc-of-simple.h> | ||
28 | |||
29 | #include "mpc5200_dma.h" | ||
30 | #include "mpc5200_psc_ac97.h" | ||
31 | #include "../codecs/stac9766.h" | ||
32 | |||
33 | static struct snd_soc_device device; | ||
34 | static struct snd_soc_card card; | ||
35 | |||
36 | static struct snd_soc_dai_link efika_fabric_dai[] = { | ||
37 | { | ||
38 | .name = "AC97", | ||
39 | .stream_name = "AC97 Analog", | ||
40 | .codec_dai = &stac9766_dai[STAC9766_DAI_AC97_ANALOG], | ||
41 | .cpu_dai = &psc_ac97_dai[MPC5200_AC97_NORMAL], | ||
42 | }, | ||
43 | { | ||
44 | .name = "AC97", | ||
45 | .stream_name = "AC97 IEC958", | ||
46 | .codec_dai = &stac9766_dai[STAC9766_DAI_AC97_DIGITAL], | ||
47 | .cpu_dai = &psc_ac97_dai[MPC5200_AC97_SPDIF], | ||
48 | }, | ||
49 | }; | ||
50 | |||
51 | static __init int efika_fabric_init(void) | ||
52 | { | ||
53 | struct platform_device *pdev; | ||
54 | int rc; | ||
55 | |||
56 | if (!machine_is_compatible("bplan,efika")) | ||
57 | return -ENODEV; | ||
58 | |||
59 | card.platform = &mpc5200_audio_dma_platform; | ||
60 | card.name = "Efika"; | ||
61 | card.dai_link = efika_fabric_dai; | ||
62 | card.num_links = ARRAY_SIZE(efika_fabric_dai); | ||
63 | |||
64 | device.card = &card; | ||
65 | device.codec_dev = &soc_codec_dev_stac9766; | ||
66 | |||
67 | pdev = platform_device_alloc("soc-audio", 1); | ||
68 | if (!pdev) { | ||
69 | pr_err("efika_fabric_init: platform_device_alloc() failed\n"); | ||
70 | return -ENODEV; | ||
71 | } | ||
72 | |||
73 | platform_set_drvdata(pdev, &device); | ||
74 | device.dev = &pdev->dev; | ||
75 | |||
76 | rc = platform_device_add(pdev); | ||
77 | if (rc) { | ||
78 | pr_err("efika_fabric_init: platform_device_add() failed\n"); | ||
79 | return -ENODEV; | ||
80 | } | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | module_init(efika_fabric_init); | ||
85 | |||
86 | |||
87 | MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>"); | ||
88 | MODULE_DESCRIPTION(DRV_NAME ": mpc5200 Efika fabric driver"); | ||
89 | MODULE_LICENSE("GPL"); | ||
90 | |||
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 3711d8454d96..93f0f38a32c9 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c | |||
@@ -375,18 +375,14 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, | |||
375 | struct snd_pcm_runtime *first_runtime = | 375 | struct snd_pcm_runtime *first_runtime = |
376 | ssi_private->first_stream->runtime; | 376 | ssi_private->first_stream->runtime; |
377 | 377 | ||
378 | if (!first_runtime->rate || !first_runtime->sample_bits) { | 378 | if (!first_runtime->sample_bits) { |
379 | dev_err(substream->pcm->card->dev, | 379 | dev_err(substream->pcm->card->dev, |
380 | "set sample rate and size in %s stream first\n", | 380 | "set sample size in %s stream first\n", |
381 | substream->stream == SNDRV_PCM_STREAM_PLAYBACK | 381 | substream->stream == SNDRV_PCM_STREAM_PLAYBACK |
382 | ? "capture" : "playback"); | 382 | ? "capture" : "playback"); |
383 | return -EAGAIN; | 383 | return -EAGAIN; |
384 | } | 384 | } |
385 | 385 | ||
386 | snd_pcm_hw_constraint_minmax(substream->runtime, | ||
387 | SNDRV_PCM_HW_PARAM_RATE, | ||
388 | first_runtime->rate, first_runtime->rate); | ||
389 | |||
390 | /* If we're in synchronous mode, then we need to constrain | 386 | /* If we're in synchronous mode, then we need to constrain |
391 | * the sample size as well. We don't support independent sample | 387 | * the sample size as well. We don't support independent sample |
392 | * rates in asynchronous mode. | 388 | * rates in asynchronous mode. |
@@ -674,7 +670,7 @@ struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info) | |||
674 | ssi_private->dev = ssi_info->dev; | 670 | ssi_private->dev = ssi_info->dev; |
675 | ssi_private->asynchronous = ssi_info->asynchronous; | 671 | ssi_private->asynchronous = ssi_info->asynchronous; |
676 | 672 | ||
677 | ssi_private->dev->driver_data = fsl_ssi_dai; | 673 | dev_set_drvdata(ssi_private->dev, fsl_ssi_dai); |
678 | 674 | ||
679 | /* Initialize the the device_attribute structure */ | 675 | /* Initialize the the device_attribute structure */ |
680 | dev_attr->attr.name = "ssi-stats"; | 676 | dev_attr->attr.name = "ssi-stats"; |
@@ -693,6 +689,7 @@ struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info) | |||
693 | fsl_ssi_dai->name = ssi_private->name; | 689 | fsl_ssi_dai->name = ssi_private->name; |
694 | fsl_ssi_dai->id = ssi_info->id; | 690 | fsl_ssi_dai->id = ssi_info->id; |
695 | fsl_ssi_dai->dev = ssi_info->dev; | 691 | fsl_ssi_dai->dev = ssi_info->dev; |
692 | fsl_ssi_dai->symmetric_rates = 1; | ||
696 | 693 | ||
697 | ret = snd_soc_register_dai(fsl_ssi_dai); | 694 | ret = snd_soc_register_dai(fsl_ssi_dai); |
698 | if (ret != 0) { | 695 | if (ret != 0) { |
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c new file mode 100644 index 000000000000..9ff62e3a9b1d --- /dev/null +++ b/sound/soc/fsl/mpc5200_dma.c | |||
@@ -0,0 +1,582 @@ | |||
1 | /* | ||
2 | * Freescale MPC5200 PSC DMA | ||
3 | * ALSA SoC Platform driver | ||
4 | * | ||
5 | * Copyright (C) 2008 Secret Lab Technologies Ltd. | ||
6 | * Copyright (C) 2009 Jon Smirl, Digispeaker | ||
7 | */ | ||
8 | |||
9 | #include <linux/module.h> | ||
10 | #include <linux/of_device.h> | ||
11 | |||
12 | #include <sound/soc.h> | ||
13 | |||
14 | #include <sysdev/bestcomm/bestcomm.h> | ||
15 | #include <sysdev/bestcomm/gen_bd.h> | ||
16 | #include <asm/mpc52xx_psc.h> | ||
17 | |||
18 | #include "mpc5200_dma.h" | ||
19 | |||
20 | /* | ||
21 | * Interrupt handlers | ||
22 | */ | ||
23 | static irqreturn_t psc_dma_status_irq(int irq, void *_psc_dma) | ||
24 | { | ||
25 | struct psc_dma *psc_dma = _psc_dma; | ||
26 | struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; | ||
27 | u16 isr; | ||
28 | |||
29 | isr = in_be16(®s->mpc52xx_psc_isr); | ||
30 | |||
31 | /* Playback underrun error */ | ||
32 | if (psc_dma->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP)) | ||
33 | psc_dma->stats.underrun_count++; | ||
34 | |||
35 | /* Capture overrun error */ | ||
36 | if (psc_dma->capture.active && (isr & MPC52xx_PSC_IMR_ORERR)) | ||
37 | psc_dma->stats.overrun_count++; | ||
38 | |||
39 | out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); | ||
40 | |||
41 | return IRQ_HANDLED; | ||
42 | } | ||
43 | |||
44 | /** | ||
45 | * psc_dma_bcom_enqueue_next_buffer - Enqueue another audio buffer | ||
46 | * @s: pointer to stream private data structure | ||
47 | * | ||
48 | * Enqueues another audio period buffer into the bestcomm queue. | ||
49 | * | ||
50 | * Note: The routine must only be called when there is space available in | ||
51 | * the queue. Otherwise the enqueue will fail and the audio ring buffer | ||
52 | * will get out of sync | ||
53 | */ | ||
54 | static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s) | ||
55 | { | ||
56 | struct bcom_bd *bd; | ||
57 | |||
58 | /* Prepare and enqueue the next buffer descriptor */ | ||
59 | bd = bcom_prepare_next_buffer(s->bcom_task); | ||
60 | bd->status = s->period_bytes; | ||
61 | bd->data[0] = s->period_next_pt; | ||
62 | bcom_submit_next_buffer(s->bcom_task, NULL); | ||
63 | |||
64 | /* Update for next period */ | ||
65 | s->period_next_pt += s->period_bytes; | ||
66 | if (s->period_next_pt >= s->period_end) | ||
67 | s->period_next_pt = s->period_start; | ||
68 | } | ||
69 | |||
70 | static void psc_dma_bcom_enqueue_tx(struct psc_dma_stream *s) | ||
71 | { | ||
72 | if (s->appl_ptr > s->runtime->control->appl_ptr) { | ||
73 | /* | ||
74 | * In this case s->runtime->control->appl_ptr has wrapped around. | ||
75 | * Play the data to the end of the boundary, then wrap our own | ||
76 | * appl_ptr back around. | ||
77 | */ | ||
78 | while (s->appl_ptr < s->runtime->boundary) { | ||
79 | if (bcom_queue_full(s->bcom_task)) | ||
80 | return; | ||
81 | |||
82 | s->appl_ptr += s->period_size; | ||
83 | |||
84 | psc_dma_bcom_enqueue_next_buffer(s); | ||
85 | } | ||
86 | s->appl_ptr -= s->runtime->boundary; | ||
87 | } | ||
88 | |||
89 | while (s->appl_ptr < s->runtime->control->appl_ptr) { | ||
90 | |||
91 | if (bcom_queue_full(s->bcom_task)) | ||
92 | return; | ||
93 | |||
94 | s->appl_ptr += s->period_size; | ||
95 | |||
96 | psc_dma_bcom_enqueue_next_buffer(s); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | /* Bestcomm DMA irq handler */ | ||
101 | static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream) | ||
102 | { | ||
103 | struct psc_dma_stream *s = _psc_dma_stream; | ||
104 | |||
105 | spin_lock(&s->psc_dma->lock); | ||
106 | /* For each finished period, dequeue the completed period buffer | ||
107 | * and enqueue a new one in it's place. */ | ||
108 | while (bcom_buffer_done(s->bcom_task)) { | ||
109 | bcom_retrieve_buffer(s->bcom_task, NULL, NULL); | ||
110 | |||
111 | s->period_current_pt += s->period_bytes; | ||
112 | if (s->period_current_pt >= s->period_end) | ||
113 | s->period_current_pt = s->period_start; | ||
114 | } | ||
115 | psc_dma_bcom_enqueue_tx(s); | ||
116 | spin_unlock(&s->psc_dma->lock); | ||
117 | |||
118 | /* If the stream is active, then also inform the PCM middle layer | ||
119 | * of the period finished event. */ | ||
120 | if (s->active) | ||
121 | snd_pcm_period_elapsed(s->stream); | ||
122 | |||
123 | return IRQ_HANDLED; | ||
124 | } | ||
125 | |||
126 | static irqreturn_t psc_dma_bcom_irq_rx(int irq, void *_psc_dma_stream) | ||
127 | { | ||
128 | struct psc_dma_stream *s = _psc_dma_stream; | ||
129 | |||
130 | spin_lock(&s->psc_dma->lock); | ||
131 | /* For each finished period, dequeue the completed period buffer | ||
132 | * and enqueue a new one in it's place. */ | ||
133 | while (bcom_buffer_done(s->bcom_task)) { | ||
134 | bcom_retrieve_buffer(s->bcom_task, NULL, NULL); | ||
135 | |||
136 | s->period_current_pt += s->period_bytes; | ||
137 | if (s->period_current_pt >= s->period_end) | ||
138 | s->period_current_pt = s->period_start; | ||
139 | |||
140 | psc_dma_bcom_enqueue_next_buffer(s); | ||
141 | } | ||
142 | spin_unlock(&s->psc_dma->lock); | ||
143 | |||
144 | /* If the stream is active, then also inform the PCM middle layer | ||
145 | * of the period finished event. */ | ||
146 | if (s->active) | ||
147 | snd_pcm_period_elapsed(s->stream); | ||
148 | |||
149 | return IRQ_HANDLED; | ||
150 | } | ||
151 | |||
152 | static int psc_dma_hw_free(struct snd_pcm_substream *substream) | ||
153 | { | ||
154 | snd_pcm_set_runtime_buffer(substream, NULL); | ||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | /** | ||
159 | * psc_dma_trigger: start and stop the DMA transfer. | ||
160 | * | ||
161 | * This function is called by ALSA to start, stop, pause, and resume the DMA | ||
162 | * transfer of data. | ||
163 | */ | ||
164 | static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) | ||
165 | { | ||
166 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
167 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | ||
168 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
169 | struct psc_dma_stream *s; | ||
170 | struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; | ||
171 | u16 imr; | ||
172 | unsigned long flags; | ||
173 | int i; | ||
174 | |||
175 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
176 | s = &psc_dma->capture; | ||
177 | else | ||
178 | s = &psc_dma->playback; | ||
179 | |||
180 | dev_dbg(psc_dma->dev, "psc_dma_trigger(substream=%p, cmd=%i)" | ||
181 | " stream_id=%i\n", | ||
182 | substream, cmd, substream->pstr->stream); | ||
183 | |||
184 | switch (cmd) { | ||
185 | case SNDRV_PCM_TRIGGER_START: | ||
186 | s->period_bytes = frames_to_bytes(runtime, | ||
187 | runtime->period_size); | ||
188 | s->period_start = virt_to_phys(runtime->dma_area); | ||
189 | s->period_end = s->period_start + | ||
190 | (s->period_bytes * runtime->periods); | ||
191 | s->period_next_pt = s->period_start; | ||
192 | s->period_current_pt = s->period_start; | ||
193 | s->period_size = runtime->period_size; | ||
194 | s->active = 1; | ||
195 | |||
196 | /* track appl_ptr so that we have a better chance of detecting | ||
197 | * end of stream and not over running it. | ||
198 | */ | ||
199 | s->runtime = runtime; | ||
200 | s->appl_ptr = s->runtime->control->appl_ptr - | ||
201 | (runtime->period_size * runtime->periods); | ||
202 | |||
203 | /* Fill up the bestcomm bd queue and enable DMA. | ||
204 | * This will begin filling the PSC's fifo. | ||
205 | */ | ||
206 | spin_lock_irqsave(&psc_dma->lock, flags); | ||
207 | |||
208 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
209 | bcom_gen_bd_rx_reset(s->bcom_task); | ||
210 | for (i = 0; i < runtime->periods; i++) | ||
211 | if (!bcom_queue_full(s->bcom_task)) | ||
212 | psc_dma_bcom_enqueue_next_buffer(s); | ||
213 | } else { | ||
214 | bcom_gen_bd_tx_reset(s->bcom_task); | ||
215 | psc_dma_bcom_enqueue_tx(s); | ||
216 | } | ||
217 | |||
218 | bcom_enable(s->bcom_task); | ||
219 | spin_unlock_irqrestore(&psc_dma->lock, flags); | ||
220 | |||
221 | out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); | ||
222 | |||
223 | break; | ||
224 | |||
225 | case SNDRV_PCM_TRIGGER_STOP: | ||
226 | s->active = 0; | ||
227 | |||
228 | spin_lock_irqsave(&psc_dma->lock, flags); | ||
229 | bcom_disable(s->bcom_task); | ||
230 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
231 | bcom_gen_bd_rx_reset(s->bcom_task); | ||
232 | else | ||
233 | bcom_gen_bd_tx_reset(s->bcom_task); | ||
234 | spin_unlock_irqrestore(&psc_dma->lock, flags); | ||
235 | |||
236 | break; | ||
237 | |||
238 | default: | ||
239 | dev_dbg(psc_dma->dev, "invalid command\n"); | ||
240 | return -EINVAL; | ||
241 | } | ||
242 | |||
243 | /* Update interrupt enable settings */ | ||
244 | imr = 0; | ||
245 | if (psc_dma->playback.active) | ||
246 | imr |= MPC52xx_PSC_IMR_TXEMP; | ||
247 | if (psc_dma->capture.active) | ||
248 | imr |= MPC52xx_PSC_IMR_ORERR; | ||
249 | out_be16(®s->isr_imr.imr, psc_dma->imr | imr); | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | |||
255 | /* --------------------------------------------------------------------- | ||
256 | * The PSC DMA 'ASoC platform' driver | ||
257 | * | ||
258 | * Can be referenced by an 'ASoC machine' driver | ||
259 | * This driver only deals with the audio bus; it doesn't have any | ||
260 | * interaction with the attached codec | ||
261 | */ | ||
262 | |||
263 | static const struct snd_pcm_hardware psc_dma_hardware = { | ||
264 | .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | | ||
265 | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
266 | SNDRV_PCM_INFO_BATCH, | ||
267 | .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | | ||
268 | SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, | ||
269 | .rate_min = 8000, | ||
270 | .rate_max = 48000, | ||
271 | .channels_min = 1, | ||
272 | .channels_max = 2, | ||
273 | .period_bytes_max = 1024 * 1024, | ||
274 | .period_bytes_min = 32, | ||
275 | .periods_min = 2, | ||
276 | .periods_max = 256, | ||
277 | .buffer_bytes_max = 2 * 1024 * 1024, | ||
278 | .fifo_size = 512, | ||
279 | }; | ||
280 | |||
281 | static int psc_dma_open(struct snd_pcm_substream *substream) | ||
282 | { | ||
283 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
284 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
285 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | ||
286 | struct psc_dma_stream *s; | ||
287 | int rc; | ||
288 | |||
289 | dev_dbg(psc_dma->dev, "psc_dma_open(substream=%p)\n", substream); | ||
290 | |||
291 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
292 | s = &psc_dma->capture; | ||
293 | else | ||
294 | s = &psc_dma->playback; | ||
295 | |||
296 | snd_soc_set_runtime_hwparams(substream, &psc_dma_hardware); | ||
297 | |||
298 | rc = snd_pcm_hw_constraint_integer(runtime, | ||
299 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
300 | if (rc < 0) { | ||
301 | dev_err(substream->pcm->card->dev, "invalid buffer size\n"); | ||
302 | return rc; | ||
303 | } | ||
304 | |||
305 | s->stream = substream; | ||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | static int psc_dma_close(struct snd_pcm_substream *substream) | ||
310 | { | ||
311 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
312 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | ||
313 | struct psc_dma_stream *s; | ||
314 | |||
315 | dev_dbg(psc_dma->dev, "psc_dma_close(substream=%p)\n", substream); | ||
316 | |||
317 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
318 | s = &psc_dma->capture; | ||
319 | else | ||
320 | s = &psc_dma->playback; | ||
321 | |||
322 | if (!psc_dma->playback.active && | ||
323 | !psc_dma->capture.active) { | ||
324 | |||
325 | /* Disable all interrupts and reset the PSC */ | ||
326 | out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); | ||
327 | out_8(&psc_dma->psc_regs->command, 4 << 4); /* reset error */ | ||
328 | } | ||
329 | s->stream = NULL; | ||
330 | return 0; | ||
331 | } | ||
332 | |||
333 | static snd_pcm_uframes_t | ||
334 | psc_dma_pointer(struct snd_pcm_substream *substream) | ||
335 | { | ||
336 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
337 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | ||
338 | struct psc_dma_stream *s; | ||
339 | dma_addr_t count; | ||
340 | |||
341 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
342 | s = &psc_dma->capture; | ||
343 | else | ||
344 | s = &psc_dma->playback; | ||
345 | |||
346 | count = s->period_current_pt - s->period_start; | ||
347 | |||
348 | return bytes_to_frames(substream->runtime, count); | ||
349 | } | ||
350 | |||
351 | static int | ||
352 | psc_dma_hw_params(struct snd_pcm_substream *substream, | ||
353 | struct snd_pcm_hw_params *params) | ||
354 | { | ||
355 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
356 | |||
357 | return 0; | ||
358 | } | ||
359 | |||
360 | static struct snd_pcm_ops psc_dma_ops = { | ||
361 | .open = psc_dma_open, | ||
362 | .close = psc_dma_close, | ||
363 | .hw_free = psc_dma_hw_free, | ||
364 | .ioctl = snd_pcm_lib_ioctl, | ||
365 | .pointer = psc_dma_pointer, | ||
366 | .trigger = psc_dma_trigger, | ||
367 | .hw_params = psc_dma_hw_params, | ||
368 | }; | ||
369 | |||
370 | static u64 psc_dma_dmamask = 0xffffffff; | ||
371 | static int psc_dma_new(struct snd_card *card, struct snd_soc_dai *dai, | ||
372 | struct snd_pcm *pcm) | ||
373 | { | ||
374 | struct snd_soc_pcm_runtime *rtd = pcm->private_data; | ||
375 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | ||
376 | size_t size = psc_dma_hardware.buffer_bytes_max; | ||
377 | int rc = 0; | ||
378 | |||
379 | dev_dbg(rtd->socdev->dev, "psc_dma_new(card=%p, dai=%p, pcm=%p)\n", | ||
380 | card, dai, pcm); | ||
381 | |||
382 | if (!card->dev->dma_mask) | ||
383 | card->dev->dma_mask = &psc_dma_dmamask; | ||
384 | if (!card->dev->coherent_dma_mask) | ||
385 | card->dev->coherent_dma_mask = 0xffffffff; | ||
386 | |||
387 | if (pcm->streams[0].substream) { | ||
388 | rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, | ||
389 | size, &pcm->streams[0].substream->dma_buffer); | ||
390 | if (rc) | ||
391 | goto playback_alloc_err; | ||
392 | } | ||
393 | |||
394 | if (pcm->streams[1].substream) { | ||
395 | rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, | ||
396 | size, &pcm->streams[1].substream->dma_buffer); | ||
397 | if (rc) | ||
398 | goto capture_alloc_err; | ||
399 | } | ||
400 | |||
401 | if (rtd->socdev->card->codec->ac97) | ||
402 | rtd->socdev->card->codec->ac97->private_data = psc_dma; | ||
403 | |||
404 | return 0; | ||
405 | |||
406 | capture_alloc_err: | ||
407 | if (pcm->streams[0].substream) | ||
408 | snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); | ||
409 | |||
410 | playback_alloc_err: | ||
411 | dev_err(card->dev, "Cannot allocate buffer(s)\n"); | ||
412 | |||
413 | return -ENOMEM; | ||
414 | } | ||
415 | |||
416 | static void psc_dma_free(struct snd_pcm *pcm) | ||
417 | { | ||
418 | struct snd_soc_pcm_runtime *rtd = pcm->private_data; | ||
419 | struct snd_pcm_substream *substream; | ||
420 | int stream; | ||
421 | |||
422 | dev_dbg(rtd->socdev->dev, "psc_dma_free(pcm=%p)\n", pcm); | ||
423 | |||
424 | for (stream = 0; stream < 2; stream++) { | ||
425 | substream = pcm->streams[stream].substream; | ||
426 | if (substream) { | ||
427 | snd_dma_free_pages(&substream->dma_buffer); | ||
428 | substream->dma_buffer.area = NULL; | ||
429 | substream->dma_buffer.addr = 0; | ||
430 | } | ||
431 | } | ||
432 | } | ||
433 | |||
434 | struct snd_soc_platform mpc5200_audio_dma_platform = { | ||
435 | .name = "mpc5200-psc-audio", | ||
436 | .pcm_ops = &psc_dma_ops, | ||
437 | .pcm_new = &psc_dma_new, | ||
438 | .pcm_free = &psc_dma_free, | ||
439 | }; | ||
440 | EXPORT_SYMBOL_GPL(mpc5200_audio_dma_platform); | ||
441 | |||
442 | int mpc5200_audio_dma_create(struct of_device *op) | ||
443 | { | ||
444 | phys_addr_t fifo; | ||
445 | struct psc_dma *psc_dma; | ||
446 | struct resource res; | ||
447 | int size, irq, rc; | ||
448 | const __be32 *prop; | ||
449 | void __iomem *regs; | ||
450 | |||
451 | /* Fetch the registers and IRQ of the PSC */ | ||
452 | irq = irq_of_parse_and_map(op->node, 0); | ||
453 | if (of_address_to_resource(op->node, 0, &res)) { | ||
454 | dev_err(&op->dev, "Missing reg property\n"); | ||
455 | return -ENODEV; | ||
456 | } | ||
457 | regs = ioremap(res.start, 1 + res.end - res.start); | ||
458 | if (!regs) { | ||
459 | dev_err(&op->dev, "Could not map registers\n"); | ||
460 | return -ENODEV; | ||
461 | } | ||
462 | |||
463 | /* Allocate and initialize the driver private data */ | ||
464 | psc_dma = kzalloc(sizeof *psc_dma, GFP_KERNEL); | ||
465 | if (!psc_dma) { | ||
466 | iounmap(regs); | ||
467 | return -ENOMEM; | ||
468 | } | ||
469 | |||
470 | /* Get the PSC ID */ | ||
471 | prop = of_get_property(op->node, "cell-index", &size); | ||
472 | if (!prop || size < sizeof *prop) | ||
473 | return -ENODEV; | ||
474 | |||
475 | spin_lock_init(&psc_dma->lock); | ||
476 | mutex_init(&psc_dma->mutex); | ||
477 | psc_dma->id = be32_to_cpu(*prop); | ||
478 | psc_dma->irq = irq; | ||
479 | psc_dma->psc_regs = regs; | ||
480 | psc_dma->fifo_regs = regs + sizeof *psc_dma->psc_regs; | ||
481 | psc_dma->dev = &op->dev; | ||
482 | psc_dma->playback.psc_dma = psc_dma; | ||
483 | psc_dma->capture.psc_dma = psc_dma; | ||
484 | snprintf(psc_dma->name, sizeof psc_dma->name, "PSC%u", psc_dma->id); | ||
485 | |||
486 | /* Find the address of the fifo data registers and setup the | ||
487 | * DMA tasks */ | ||
488 | fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32); | ||
489 | psc_dma->capture.bcom_task = | ||
490 | bcom_psc_gen_bd_rx_init(psc_dma->id, 10, fifo, 512); | ||
491 | psc_dma->playback.bcom_task = | ||
492 | bcom_psc_gen_bd_tx_init(psc_dma->id, 10, fifo); | ||
493 | if (!psc_dma->capture.bcom_task || | ||
494 | !psc_dma->playback.bcom_task) { | ||
495 | dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); | ||
496 | iounmap(regs); | ||
497 | kfree(psc_dma); | ||
498 | return -ENODEV; | ||
499 | } | ||
500 | |||
501 | /* Disable all interrupts and reset the PSC */ | ||
502 | out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); | ||
503 | /* reset receiver */ | ||
504 | out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_RX); | ||
505 | /* reset transmitter */ | ||
506 | out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_TX); | ||
507 | /* reset error */ | ||
508 | out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_ERR_STAT); | ||
509 | /* reset mode */ | ||
510 | out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_SEL_MODE_REG_1); | ||
511 | |||
512 | /* Set up mode register; | ||
513 | * First write: RxRdy (FIFO Alarm) generates rx FIFO irq | ||
514 | * Second write: register Normal mode for non loopback | ||
515 | */ | ||
516 | out_8(&psc_dma->psc_regs->mode, 0); | ||
517 | out_8(&psc_dma->psc_regs->mode, 0); | ||
518 | |||
519 | /* Set the TX and RX fifo alarm thresholds */ | ||
520 | out_be16(&psc_dma->fifo_regs->rfalarm, 0x100); | ||
521 | out_8(&psc_dma->fifo_regs->rfcntl, 0x4); | ||
522 | out_be16(&psc_dma->fifo_regs->tfalarm, 0x100); | ||
523 | out_8(&psc_dma->fifo_regs->tfcntl, 0x7); | ||
524 | |||
525 | /* Lookup the IRQ numbers */ | ||
526 | psc_dma->playback.irq = | ||
527 | bcom_get_task_irq(psc_dma->playback.bcom_task); | ||
528 | psc_dma->capture.irq = | ||
529 | bcom_get_task_irq(psc_dma->capture.bcom_task); | ||
530 | |||
531 | rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, | ||
532 | "psc-dma-status", psc_dma); | ||
533 | rc |= request_irq(psc_dma->capture.irq, | ||
534 | &psc_dma_bcom_irq_rx, IRQF_SHARED, | ||
535 | "psc-dma-capture", &psc_dma->capture); | ||
536 | rc |= request_irq(psc_dma->playback.irq, | ||
537 | &psc_dma_bcom_irq_tx, IRQF_SHARED, | ||
538 | "psc-dma-playback", &psc_dma->playback); | ||
539 | if (rc) { | ||
540 | free_irq(psc_dma->irq, psc_dma); | ||
541 | free_irq(psc_dma->capture.irq, | ||
542 | &psc_dma->capture); | ||
543 | free_irq(psc_dma->playback.irq, | ||
544 | &psc_dma->playback); | ||
545 | return -ENODEV; | ||
546 | } | ||
547 | |||
548 | /* Save what we've done so it can be found again later */ | ||
549 | dev_set_drvdata(&op->dev, psc_dma); | ||
550 | |||
551 | /* Tell the ASoC OF helpers about it */ | ||
552 | return snd_soc_register_platform(&mpc5200_audio_dma_platform); | ||
553 | } | ||
554 | EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create); | ||
555 | |||
556 | int mpc5200_audio_dma_destroy(struct of_device *op) | ||
557 | { | ||
558 | struct psc_dma *psc_dma = dev_get_drvdata(&op->dev); | ||
559 | |||
560 | dev_dbg(&op->dev, "mpc5200_audio_dma_destroy()\n"); | ||
561 | |||
562 | snd_soc_unregister_platform(&mpc5200_audio_dma_platform); | ||
563 | |||
564 | bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); | ||
565 | bcom_gen_bd_tx_release(psc_dma->playback.bcom_task); | ||
566 | |||
567 | /* Release irqs */ | ||
568 | free_irq(psc_dma->irq, psc_dma); | ||
569 | free_irq(psc_dma->capture.irq, &psc_dma->capture); | ||
570 | free_irq(psc_dma->playback.irq, &psc_dma->playback); | ||
571 | |||
572 | iounmap(psc_dma->psc_regs); | ||
573 | kfree(psc_dma); | ||
574 | dev_set_drvdata(&op->dev, NULL); | ||
575 | |||
576 | return 0; | ||
577 | } | ||
578 | EXPORT_SYMBOL_GPL(mpc5200_audio_dma_destroy); | ||
579 | |||
580 | MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); | ||
581 | MODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver"); | ||
582 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h new file mode 100644 index 000000000000..8d396bb9d9fe --- /dev/null +++ b/sound/soc/fsl/mpc5200_dma.h | |||
@@ -0,0 +1,81 @@ | |||
1 | /* | ||
2 | * Freescale MPC5200 Audio DMA driver | ||
3 | */ | ||
4 | |||
5 | #ifndef __SOUND_SOC_FSL_MPC5200_DMA_H__ | ||
6 | #define __SOUND_SOC_FSL_MPC5200_DMA_H__ | ||
7 | |||
8 | #define PSC_STREAM_NAME_LEN 32 | ||
9 | |||
10 | /** | ||
11 | * psc_ac97_stream - Data specific to a single stream (playback or capture) | ||
12 | * @active: flag indicating if the stream is active | ||
13 | * @psc_dma: pointer back to parent psc_dma data structure | ||
14 | * @bcom_task: bestcomm task structure | ||
15 | * @irq: irq number for bestcomm task | ||
16 | * @period_start: physical address of start of DMA region | ||
17 | * @period_end: physical address of end of DMA region | ||
18 | * @period_next_pt: physical address of next DMA buffer to enqueue | ||
19 | * @period_bytes: size of DMA period in bytes | ||
20 | */ | ||
21 | struct psc_dma_stream { | ||
22 | struct snd_pcm_runtime *runtime; | ||
23 | snd_pcm_uframes_t appl_ptr; | ||
24 | |||
25 | int active; | ||
26 | struct psc_dma *psc_dma; | ||
27 | struct bcom_task *bcom_task; | ||
28 | int irq; | ||
29 | struct snd_pcm_substream *stream; | ||
30 | dma_addr_t period_start; | ||
31 | dma_addr_t period_end; | ||
32 | dma_addr_t period_next_pt; | ||
33 | dma_addr_t period_current_pt; | ||
34 | int period_bytes; | ||
35 | int period_size; | ||
36 | }; | ||
37 | |||
38 | /** | ||
39 | * psc_dma - Private driver data | ||
40 | * @name: short name for this device ("PSC0", "PSC1", etc) | ||
41 | * @psc_regs: pointer to the PSC's registers | ||
42 | * @fifo_regs: pointer to the PSC's FIFO registers | ||
43 | * @irq: IRQ of this PSC | ||
44 | * @dev: struct device pointer | ||
45 | * @dai: the CPU DAI for this device | ||
46 | * @sicr: Base value used in serial interface control register; mode is ORed | ||
47 | * with this value. | ||
48 | * @playback: Playback stream context data | ||
49 | * @capture: Capture stream context data | ||
50 | */ | ||
51 | struct psc_dma { | ||
52 | char name[32]; | ||
53 | struct mpc52xx_psc __iomem *psc_regs; | ||
54 | struct mpc52xx_psc_fifo __iomem *fifo_regs; | ||
55 | unsigned int irq; | ||
56 | struct device *dev; | ||
57 | spinlock_t lock; | ||
58 | struct mutex mutex; | ||
59 | u32 sicr; | ||
60 | uint sysclk; | ||
61 | int imr; | ||
62 | int id; | ||
63 | unsigned int slots; | ||
64 | |||
65 | /* per-stream data */ | ||
66 | struct psc_dma_stream playback; | ||
67 | struct psc_dma_stream capture; | ||
68 | |||
69 | /* Statistics */ | ||
70 | struct { | ||
71 | unsigned long overrun_count; | ||
72 | unsigned long underrun_count; | ||
73 | } stats; | ||
74 | }; | ||
75 | |||
76 | int mpc5200_audio_dma_create(struct of_device *op); | ||
77 | int mpc5200_audio_dma_destroy(struct of_device *op); | ||
78 | |||
79 | extern struct snd_soc_platform mpc5200_audio_dma_platform; | ||
80 | |||
81 | #endif /* __SOUND_SOC_FSL_MPC5200_DMA_H__ */ | ||
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c new file mode 100644 index 000000000000..c4ae3e096bb9 --- /dev/null +++ b/sound/soc/fsl/mpc5200_psc_ac97.c | |||
@@ -0,0 +1,345 @@ | |||
1 | /* | ||
2 | * linux/sound/mpc5200-ac97.c -- AC97 support for the Freescale MPC52xx chip. | ||
3 | * | ||
4 | * Copyright (C) 2009 Jon Smirl, Digispeaker | ||
5 | * Author: Jon Smirl <jonsmirl@gmail.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/of_device.h> | ||
14 | #include <linux/of_platform.h> | ||
15 | #include <linux/delay.h> | ||
16 | |||
17 | #include <sound/pcm.h> | ||
18 | #include <sound/pcm_params.h> | ||
19 | #include <sound/soc.h> | ||
20 | |||
21 | #include <asm/time.h> | ||
22 | #include <asm/delay.h> | ||
23 | #include <asm/mpc52xx_psc.h> | ||
24 | |||
25 | #include "mpc5200_dma.h" | ||
26 | #include "mpc5200_psc_ac97.h" | ||
27 | |||
28 | #define DRV_NAME "mpc5200-psc-ac97" | ||
29 | |||
30 | /* ALSA only supports a single AC97 device so static is recommend here */ | ||
31 | static struct psc_dma *psc_dma; | ||
32 | |||
33 | static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) | ||
34 | { | ||
35 | int status; | ||
36 | unsigned int val; | ||
37 | |||
38 | mutex_lock(&psc_dma->mutex); | ||
39 | |||
40 | /* Wait for command send status zero = ready */ | ||
41 | status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) & | ||
42 | MPC52xx_PSC_SR_CMDSEND), 100, 0); | ||
43 | if (status == 0) { | ||
44 | pr_err("timeout on ac97 bus (rdy)\n"); | ||
45 | mutex_unlock(&psc_dma->mutex); | ||
46 | return -ENODEV; | ||
47 | } | ||
48 | |||
49 | /* Force clear the data valid bit */ | ||
50 | in_be32(&psc_dma->psc_regs->ac97_data); | ||
51 | |||
52 | /* Send the read */ | ||
53 | out_be32(&psc_dma->psc_regs->ac97_cmd, (1<<31) | ((reg & 0x7f) << 24)); | ||
54 | |||
55 | /* Wait for the answer */ | ||
56 | status = spin_event_timeout((in_be16(&psc_dma->psc_regs->sr_csr.status) & | ||
57 | MPC52xx_PSC_SR_DATA_VAL), 100, 0); | ||
58 | if (status == 0) { | ||
59 | pr_err("timeout on ac97 read (val) %x\n", | ||
60 | in_be16(&psc_dma->psc_regs->sr_csr.status)); | ||
61 | mutex_unlock(&psc_dma->mutex); | ||
62 | return -ENODEV; | ||
63 | } | ||
64 | /* Get the data */ | ||
65 | val = in_be32(&psc_dma->psc_regs->ac97_data); | ||
66 | if (((val >> 24) & 0x7f) != reg) { | ||
67 | pr_err("reg echo error on ac97 read\n"); | ||
68 | mutex_unlock(&psc_dma->mutex); | ||
69 | return -ENODEV; | ||
70 | } | ||
71 | val = (val >> 8) & 0xffff; | ||
72 | |||
73 | mutex_unlock(&psc_dma->mutex); | ||
74 | return (unsigned short) val; | ||
75 | } | ||
76 | |||
77 | static void psc_ac97_write(struct snd_ac97 *ac97, | ||
78 | unsigned short reg, unsigned short val) | ||
79 | { | ||
80 | int status; | ||
81 | |||
82 | mutex_lock(&psc_dma->mutex); | ||
83 | |||
84 | /* Wait for command status zero = ready */ | ||
85 | status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) & | ||
86 | MPC52xx_PSC_SR_CMDSEND), 100, 0); | ||
87 | if (status == 0) { | ||
88 | pr_err("timeout on ac97 bus (write)\n"); | ||
89 | goto out; | ||
90 | } | ||
91 | /* Write data */ | ||
92 | out_be32(&psc_dma->psc_regs->ac97_cmd, | ||
93 | ((reg & 0x7f) << 24) | (val << 8)); | ||
94 | |||
95 | out: | ||
96 | mutex_unlock(&psc_dma->mutex); | ||
97 | } | ||
98 | |||
99 | static void psc_ac97_warm_reset(struct snd_ac97 *ac97) | ||
100 | { | ||
101 | struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; | ||
102 | |||
103 | out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_AWR); | ||
104 | udelay(3); | ||
105 | out_be32(®s->sicr, psc_dma->sicr); | ||
106 | } | ||
107 | |||
108 | static void psc_ac97_cold_reset(struct snd_ac97 *ac97) | ||
109 | { | ||
110 | struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; | ||
111 | |||
112 | /* Do a cold reset */ | ||
113 | out_8(®s->op1, MPC52xx_PSC_OP_RES); | ||
114 | udelay(10); | ||
115 | out_8(®s->op0, MPC52xx_PSC_OP_RES); | ||
116 | msleep(1); | ||
117 | psc_ac97_warm_reset(ac97); | ||
118 | } | ||
119 | |||
120 | struct snd_ac97_bus_ops soc_ac97_ops = { | ||
121 | .read = psc_ac97_read, | ||
122 | .write = psc_ac97_write, | ||
123 | .reset = psc_ac97_cold_reset, | ||
124 | .warm_reset = psc_ac97_warm_reset, | ||
125 | }; | ||
126 | EXPORT_SYMBOL_GPL(soc_ac97_ops); | ||
127 | |||
128 | static int psc_ac97_hw_analog_params(struct snd_pcm_substream *substream, | ||
129 | struct snd_pcm_hw_params *params, | ||
130 | struct snd_soc_dai *cpu_dai) | ||
131 | { | ||
132 | struct psc_dma *psc_dma = cpu_dai->private_data; | ||
133 | |||
134 | dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i" | ||
135 | " periods=%i buffer_size=%i buffer_bytes=%i channels=%i" | ||
136 | " rate=%i format=%i\n", | ||
137 | __func__, substream, params_period_size(params), | ||
138 | params_period_bytes(params), params_periods(params), | ||
139 | params_buffer_size(params), params_buffer_bytes(params), | ||
140 | params_channels(params), params_rate(params), | ||
141 | params_format(params)); | ||
142 | |||
143 | |||
144 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
145 | if (params_channels(params) == 1) | ||
146 | psc_dma->slots |= 0x00000100; | ||
147 | else | ||
148 | psc_dma->slots |= 0x00000300; | ||
149 | } else { | ||
150 | if (params_channels(params) == 1) | ||
151 | psc_dma->slots |= 0x01000000; | ||
152 | else | ||
153 | psc_dma->slots |= 0x03000000; | ||
154 | } | ||
155 | out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots); | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static int psc_ac97_hw_digital_params(struct snd_pcm_substream *substream, | ||
161 | struct snd_pcm_hw_params *params, | ||
162 | struct snd_soc_dai *cpu_dai) | ||
163 | { | ||
164 | struct psc_dma *psc_dma = cpu_dai->private_data; | ||
165 | |||
166 | if (params_channels(params) == 1) | ||
167 | out_be32(&psc_dma->psc_regs->ac97_slots, 0x01000000); | ||
168 | else | ||
169 | out_be32(&psc_dma->psc_regs->ac97_slots, 0x03000000); | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static int psc_ac97_trigger(struct snd_pcm_substream *substream, int cmd, | ||
175 | struct snd_soc_dai *dai) | ||
176 | { | ||
177 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
178 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | ||
179 | |||
180 | switch (cmd) { | ||
181 | case SNDRV_PCM_TRIGGER_STOP: | ||
182 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
183 | psc_dma->slots &= 0xFFFF0000; | ||
184 | else | ||
185 | psc_dma->slots &= 0x0000FFFF; | ||
186 | |||
187 | out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots); | ||
188 | break; | ||
189 | } | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static int psc_ac97_probe(struct platform_device *pdev, | ||
194 | struct snd_soc_dai *cpu_dai) | ||
195 | { | ||
196 | struct psc_dma *psc_dma = cpu_dai->private_data; | ||
197 | struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; | ||
198 | |||
199 | /* Go */ | ||
200 | out_8(®s->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | /* --------------------------------------------------------------------- | ||
205 | * ALSA SoC Bindings | ||
206 | * | ||
207 | * - Digital Audio Interface (DAI) template | ||
208 | * - create/destroy dai hooks | ||
209 | */ | ||
210 | |||
211 | /** | ||
212 | * psc_ac97_dai_template: template CPU Digital Audio Interface | ||
213 | */ | ||
214 | static struct snd_soc_dai_ops psc_ac97_analog_ops = { | ||
215 | .hw_params = psc_ac97_hw_analog_params, | ||
216 | .trigger = psc_ac97_trigger, | ||
217 | }; | ||
218 | |||
219 | static struct snd_soc_dai_ops psc_ac97_digital_ops = { | ||
220 | .hw_params = psc_ac97_hw_digital_params, | ||
221 | }; | ||
222 | |||
223 | struct snd_soc_dai psc_ac97_dai[] = { | ||
224 | { | ||
225 | .name = "AC97", | ||
226 | .ac97_control = 1, | ||
227 | .probe = psc_ac97_probe, | ||
228 | .playback = { | ||
229 | .channels_min = 1, | ||
230 | .channels_max = 6, | ||
231 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
232 | .formats = SNDRV_PCM_FMTBIT_S32_BE, | ||
233 | }, | ||
234 | .capture = { | ||
235 | .channels_min = 1, | ||
236 | .channels_max = 2, | ||
237 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
238 | .formats = SNDRV_PCM_FMTBIT_S32_BE, | ||
239 | }, | ||
240 | .ops = &psc_ac97_analog_ops, | ||
241 | }, | ||
242 | { | ||
243 | .name = "SPDIF", | ||
244 | .ac97_control = 1, | ||
245 | .playback = { | ||
246 | .channels_min = 1, | ||
247 | .channels_max = 2, | ||
248 | .rates = SNDRV_PCM_RATE_32000 | \ | ||
249 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, | ||
250 | .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE, | ||
251 | }, | ||
252 | .ops = &psc_ac97_digital_ops, | ||
253 | } }; | ||
254 | EXPORT_SYMBOL_GPL(psc_ac97_dai); | ||
255 | |||
256 | |||
257 | |||
258 | /* --------------------------------------------------------------------- | ||
259 | * OF platform bus binding code: | ||
260 | * - Probe/remove operations | ||
261 | * - OF device match table | ||
262 | */ | ||
263 | static int __devinit psc_ac97_of_probe(struct of_device *op, | ||
264 | const struct of_device_id *match) | ||
265 | { | ||
266 | int rc, i; | ||
267 | struct snd_ac97 ac97; | ||
268 | struct mpc52xx_psc __iomem *regs; | ||
269 | |||
270 | rc = mpc5200_audio_dma_create(op); | ||
271 | if (rc != 0) | ||
272 | return rc; | ||
273 | |||
274 | for (i = 0; i < ARRAY_SIZE(psc_ac97_dai); i++) | ||
275 | psc_ac97_dai[i].dev = &op->dev; | ||
276 | |||
277 | rc = snd_soc_register_dais(psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai)); | ||
278 | if (rc != 0) { | ||
279 | dev_err(&op->dev, "Failed to register DAI\n"); | ||
280 | return rc; | ||
281 | } | ||
282 | |||
283 | psc_dma = dev_get_drvdata(&op->dev); | ||
284 | regs = psc_dma->psc_regs; | ||
285 | ac97.private_data = psc_dma; | ||
286 | |||
287 | for (i = 0; i < ARRAY_SIZE(psc_ac97_dai); i++) | ||
288 | psc_ac97_dai[i].private_data = psc_dma; | ||
289 | |||
290 | psc_dma->imr = 0; | ||
291 | out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); | ||
292 | |||
293 | /* Configure the serial interface mode to AC97 */ | ||
294 | psc_dma->sicr = MPC52xx_PSC_SICR_SIM_AC97 | MPC52xx_PSC_SICR_ENAC97; | ||
295 | out_be32(®s->sicr, psc_dma->sicr); | ||
296 | |||
297 | /* No slots active */ | ||
298 | out_be32(®s->ac97_slots, 0x00000000); | ||
299 | |||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static int __devexit psc_ac97_of_remove(struct of_device *op) | ||
304 | { | ||
305 | return mpc5200_audio_dma_destroy(op); | ||
306 | } | ||
307 | |||
308 | /* Match table for of_platform binding */ | ||
309 | static struct of_device_id psc_ac97_match[] __devinitdata = { | ||
310 | { .compatible = "fsl,mpc5200-psc-ac97", }, | ||
311 | { .compatible = "fsl,mpc5200b-psc-ac97", }, | ||
312 | {} | ||
313 | }; | ||
314 | MODULE_DEVICE_TABLE(of, psc_ac97_match); | ||
315 | |||
316 | static struct of_platform_driver psc_ac97_driver = { | ||
317 | .match_table = psc_ac97_match, | ||
318 | .probe = psc_ac97_of_probe, | ||
319 | .remove = __devexit_p(psc_ac97_of_remove), | ||
320 | .driver = { | ||
321 | .name = "mpc5200-psc-ac97", | ||
322 | .owner = THIS_MODULE, | ||
323 | }, | ||
324 | }; | ||
325 | |||
326 | /* --------------------------------------------------------------------- | ||
327 | * Module setup and teardown; simply register the of_platform driver | ||
328 | * for the PSC in AC97 mode. | ||
329 | */ | ||
330 | static int __init psc_ac97_init(void) | ||
331 | { | ||
332 | return of_register_platform_driver(&psc_ac97_driver); | ||
333 | } | ||
334 | module_init(psc_ac97_init); | ||
335 | |||
336 | static void __exit psc_ac97_exit(void) | ||
337 | { | ||
338 | of_unregister_platform_driver(&psc_ac97_driver); | ||
339 | } | ||
340 | module_exit(psc_ac97_exit); | ||
341 | |||
342 | MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>"); | ||
343 | MODULE_DESCRIPTION("mpc5200 AC97 module"); | ||
344 | MODULE_LICENSE("GPL"); | ||
345 | |||
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.h b/sound/soc/fsl/mpc5200_psc_ac97.h new file mode 100644 index 000000000000..4bc18c35c369 --- /dev/null +++ b/sound/soc/fsl/mpc5200_psc_ac97.h | |||
@@ -0,0 +1,15 @@ | |||
1 | /* | ||
2 | * Freescale MPC5200 PSC in AC97 mode | ||
3 | * ALSA SoC Digital Audio Interface (DAI) driver | ||
4 | * | ||
5 | */ | ||
6 | |||
7 | #ifndef __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ | ||
8 | #define __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ | ||
9 | |||
10 | extern struct snd_soc_dai psc_ac97_dai[]; | ||
11 | |||
12 | #define MPC5200_AC97_NORMAL 0 | ||
13 | #define MPC5200_AC97_SPDIF 1 | ||
14 | |||
15 | #endif /* __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ */ | ||
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 1111c710118a..ce8de90fb94a 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c | |||
@@ -3,31 +3,21 @@ | |||
3 | * ALSA SoC Digital Audio Interface (DAI) driver | 3 | * ALSA SoC Digital Audio Interface (DAI) driver |
4 | * | 4 | * |
5 | * Copyright (C) 2008 Secret Lab Technologies Ltd. | 5 | * Copyright (C) 2008 Secret Lab Technologies Ltd. |
6 | * Copyright (C) 2009 Jon Smirl, Digispeaker | ||
6 | */ | 7 | */ |
7 | 8 | ||
8 | #include <linux/init.h> | ||
9 | #include <linux/module.h> | 9 | #include <linux/module.h> |
10 | #include <linux/interrupt.h> | ||
11 | #include <linux/device.h> | ||
12 | #include <linux/delay.h> | ||
13 | #include <linux/of_device.h> | 10 | #include <linux/of_device.h> |
14 | #include <linux/of_platform.h> | 11 | #include <linux/of_platform.h> |
15 | #include <linux/dma-mapping.h> | ||
16 | 12 | ||
17 | #include <sound/core.h> | ||
18 | #include <sound/pcm.h> | 13 | #include <sound/pcm.h> |
19 | #include <sound/pcm_params.h> | 14 | #include <sound/pcm_params.h> |
20 | #include <sound/initval.h> | ||
21 | #include <sound/soc.h> | 15 | #include <sound/soc.h> |
22 | #include <sound/soc-of-simple.h> | ||
23 | 16 | ||
24 | #include <sysdev/bestcomm/bestcomm.h> | ||
25 | #include <sysdev/bestcomm/gen_bd.h> | ||
26 | #include <asm/mpc52xx_psc.h> | 17 | #include <asm/mpc52xx_psc.h> |
27 | 18 | ||
28 | MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); | 19 | #include "mpc5200_psc_i2s.h" |
29 | MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver"); | 20 | #include "mpc5200_dma.h" |
30 | MODULE_LICENSE("GPL"); | ||
31 | 21 | ||
32 | /** | 22 | /** |
33 | * PSC_I2S_RATES: sample rates supported by the I2S | 23 | * PSC_I2S_RATES: sample rates supported by the I2S |
@@ -44,191 +34,17 @@ MODULE_LICENSE("GPL"); | |||
44 | * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode | 34 | * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode |
45 | */ | 35 | */ |
46 | #define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \ | 36 | #define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \ |
47 | SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE | \ | 37 | SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE) |
48 | SNDRV_PCM_FMTBIT_S32_BE) | ||
49 | |||
50 | /** | ||
51 | * psc_i2s_stream - Data specific to a single stream (playback or capture) | ||
52 | * @active: flag indicating if the stream is active | ||
53 | * @psc_i2s: pointer back to parent psc_i2s data structure | ||
54 | * @bcom_task: bestcomm task structure | ||
55 | * @irq: irq number for bestcomm task | ||
56 | * @period_start: physical address of start of DMA region | ||
57 | * @period_end: physical address of end of DMA region | ||
58 | * @period_next_pt: physical address of next DMA buffer to enqueue | ||
59 | * @period_bytes: size of DMA period in bytes | ||
60 | */ | ||
61 | struct psc_i2s_stream { | ||
62 | int active; | ||
63 | struct psc_i2s *psc_i2s; | ||
64 | struct bcom_task *bcom_task; | ||
65 | int irq; | ||
66 | struct snd_pcm_substream *stream; | ||
67 | dma_addr_t period_start; | ||
68 | dma_addr_t period_end; | ||
69 | dma_addr_t period_next_pt; | ||
70 | dma_addr_t period_current_pt; | ||
71 | int period_bytes; | ||
72 | }; | ||
73 | |||
74 | /** | ||
75 | * psc_i2s - Private driver data | ||
76 | * @name: short name for this device ("PSC0", "PSC1", etc) | ||
77 | * @psc_regs: pointer to the PSC's registers | ||
78 | * @fifo_regs: pointer to the PSC's FIFO registers | ||
79 | * @irq: IRQ of this PSC | ||
80 | * @dev: struct device pointer | ||
81 | * @dai: the CPU DAI for this device | ||
82 | * @sicr: Base value used in serial interface control register; mode is ORed | ||
83 | * with this value. | ||
84 | * @playback: Playback stream context data | ||
85 | * @capture: Capture stream context data | ||
86 | */ | ||
87 | struct psc_i2s { | ||
88 | char name[32]; | ||
89 | struct mpc52xx_psc __iomem *psc_regs; | ||
90 | struct mpc52xx_psc_fifo __iomem *fifo_regs; | ||
91 | unsigned int irq; | ||
92 | struct device *dev; | ||
93 | struct snd_soc_dai dai; | ||
94 | spinlock_t lock; | ||
95 | u32 sicr; | ||
96 | |||
97 | /* per-stream data */ | ||
98 | struct psc_i2s_stream playback; | ||
99 | struct psc_i2s_stream capture; | ||
100 | |||
101 | /* Statistics */ | ||
102 | struct { | ||
103 | int overrun_count; | ||
104 | int underrun_count; | ||
105 | } stats; | ||
106 | }; | ||
107 | |||
108 | /* | ||
109 | * Interrupt handlers | ||
110 | */ | ||
111 | static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s) | ||
112 | { | ||
113 | struct psc_i2s *psc_i2s = _psc_i2s; | ||
114 | struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs; | ||
115 | u16 isr; | ||
116 | |||
117 | isr = in_be16(®s->mpc52xx_psc_isr); | ||
118 | |||
119 | /* Playback underrun error */ | ||
120 | if (psc_i2s->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP)) | ||
121 | psc_i2s->stats.underrun_count++; | ||
122 | |||
123 | /* Capture overrun error */ | ||
124 | if (psc_i2s->capture.active && (isr & MPC52xx_PSC_IMR_ORERR)) | ||
125 | psc_i2s->stats.overrun_count++; | ||
126 | |||
127 | out_8(®s->command, 4 << 4); /* reset the error status */ | ||
128 | |||
129 | return IRQ_HANDLED; | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * psc_i2s_bcom_enqueue_next_buffer - Enqueue another audio buffer | ||
134 | * @s: pointer to stream private data structure | ||
135 | * | ||
136 | * Enqueues another audio period buffer into the bestcomm queue. | ||
137 | * | ||
138 | * Note: The routine must only be called when there is space available in | ||
139 | * the queue. Otherwise the enqueue will fail and the audio ring buffer | ||
140 | * will get out of sync | ||
141 | */ | ||
142 | static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s) | ||
143 | { | ||
144 | struct bcom_bd *bd; | ||
145 | |||
146 | /* Prepare and enqueue the next buffer descriptor */ | ||
147 | bd = bcom_prepare_next_buffer(s->bcom_task); | ||
148 | bd->status = s->period_bytes; | ||
149 | bd->data[0] = s->period_next_pt; | ||
150 | bcom_submit_next_buffer(s->bcom_task, NULL); | ||
151 | |||
152 | /* Update for next period */ | ||
153 | s->period_next_pt += s->period_bytes; | ||
154 | if (s->period_next_pt >= s->period_end) | ||
155 | s->period_next_pt = s->period_start; | ||
156 | } | ||
157 | |||
158 | /* Bestcomm DMA irq handler */ | ||
159 | static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream) | ||
160 | { | ||
161 | struct psc_i2s_stream *s = _psc_i2s_stream; | ||
162 | |||
163 | /* For each finished period, dequeue the completed period buffer | ||
164 | * and enqueue a new one in it's place. */ | ||
165 | while (bcom_buffer_done(s->bcom_task)) { | ||
166 | bcom_retrieve_buffer(s->bcom_task, NULL, NULL); | ||
167 | s->period_current_pt += s->period_bytes; | ||
168 | if (s->period_current_pt >= s->period_end) | ||
169 | s->period_current_pt = s->period_start; | ||
170 | psc_i2s_bcom_enqueue_next_buffer(s); | ||
171 | bcom_enable(s->bcom_task); | ||
172 | } | ||
173 | |||
174 | /* If the stream is active, then also inform the PCM middle layer | ||
175 | * of the period finished event. */ | ||
176 | if (s->active) | ||
177 | snd_pcm_period_elapsed(s->stream); | ||
178 | |||
179 | return IRQ_HANDLED; | ||
180 | } | ||
181 | |||
182 | /** | ||
183 | * psc_i2s_startup: create a new substream | ||
184 | * | ||
185 | * This is the first function called when a stream is opened. | ||
186 | * | ||
187 | * If this is the first stream open, then grab the IRQ and program most of | ||
188 | * the PSC registers. | ||
189 | */ | ||
190 | static int psc_i2s_startup(struct snd_pcm_substream *substream, | ||
191 | struct snd_soc_dai *dai) | ||
192 | { | ||
193 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
194 | struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; | ||
195 | int rc; | ||
196 | |||
197 | dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream); | ||
198 | |||
199 | if (!psc_i2s->playback.active && | ||
200 | !psc_i2s->capture.active) { | ||
201 | /* Setup the IRQs */ | ||
202 | rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED, | ||
203 | "psc-i2s-status", psc_i2s); | ||
204 | rc |= request_irq(psc_i2s->capture.irq, | ||
205 | &psc_i2s_bcom_irq, IRQF_SHARED, | ||
206 | "psc-i2s-capture", &psc_i2s->capture); | ||
207 | rc |= request_irq(psc_i2s->playback.irq, | ||
208 | &psc_i2s_bcom_irq, IRQF_SHARED, | ||
209 | "psc-i2s-playback", &psc_i2s->playback); | ||
210 | if (rc) { | ||
211 | free_irq(psc_i2s->irq, psc_i2s); | ||
212 | free_irq(psc_i2s->capture.irq, | ||
213 | &psc_i2s->capture); | ||
214 | free_irq(psc_i2s->playback.irq, | ||
215 | &psc_i2s->playback); | ||
216 | return -ENODEV; | ||
217 | } | ||
218 | } | ||
219 | |||
220 | return 0; | ||
221 | } | ||
222 | 38 | ||
223 | static int psc_i2s_hw_params(struct snd_pcm_substream *substream, | 39 | static int psc_i2s_hw_params(struct snd_pcm_substream *substream, |
224 | struct snd_pcm_hw_params *params, | 40 | struct snd_pcm_hw_params *params, |
225 | struct snd_soc_dai *dai) | 41 | struct snd_soc_dai *dai) |
226 | { | 42 | { |
227 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 43 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
228 | struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; | 44 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; |
229 | u32 mode; | 45 | u32 mode; |
230 | 46 | ||
231 | dev_dbg(psc_i2s->dev, "%s(substream=%p) p_size=%i p_bytes=%i" | 47 | dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i" |
232 | " periods=%i buffer_size=%i buffer_bytes=%i\n", | 48 | " periods=%i buffer_size=%i buffer_bytes=%i\n", |
233 | __func__, substream, params_period_size(params), | 49 | __func__, substream, params_period_size(params), |
234 | params_period_bytes(params), params_periods(params), | 50 | params_period_bytes(params), params_periods(params), |
@@ -248,175 +64,15 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, | |||
248 | mode = MPC52xx_PSC_SICR_SIM_CODEC_32; | 64 | mode = MPC52xx_PSC_SICR_SIM_CODEC_32; |
249 | break; | 65 | break; |
250 | default: | 66 | default: |
251 | dev_dbg(psc_i2s->dev, "invalid format\n"); | 67 | dev_dbg(psc_dma->dev, "invalid format\n"); |
252 | return -EINVAL; | ||
253 | } | ||
254 | out_be32(&psc_i2s->psc_regs->sicr, psc_i2s->sicr | mode); | ||
255 | |||
256 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
257 | |||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static int psc_i2s_hw_free(struct snd_pcm_substream *substream, | ||
262 | struct snd_soc_dai *dai) | ||
263 | { | ||
264 | snd_pcm_set_runtime_buffer(substream, NULL); | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | /** | ||
269 | * psc_i2s_trigger: start and stop the DMA transfer. | ||
270 | * | ||
271 | * This function is called by ALSA to start, stop, pause, and resume the DMA | ||
272 | * transfer of data. | ||
273 | */ | ||
274 | static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | ||
275 | struct snd_soc_dai *dai) | ||
276 | { | ||
277 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
278 | struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; | ||
279 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
280 | struct psc_i2s_stream *s; | ||
281 | struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs; | ||
282 | u16 imr; | ||
283 | u8 psc_cmd; | ||
284 | unsigned long flags; | ||
285 | |||
286 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
287 | s = &psc_i2s->capture; | ||
288 | else | ||
289 | s = &psc_i2s->playback; | ||
290 | |||
291 | dev_dbg(psc_i2s->dev, "psc_i2s_trigger(substream=%p, cmd=%i)" | ||
292 | " stream_id=%i\n", | ||
293 | substream, cmd, substream->pstr->stream); | ||
294 | |||
295 | switch (cmd) { | ||
296 | case SNDRV_PCM_TRIGGER_START: | ||
297 | s->period_bytes = frames_to_bytes(runtime, | ||
298 | runtime->period_size); | ||
299 | s->period_start = virt_to_phys(runtime->dma_area); | ||
300 | s->period_end = s->period_start + | ||
301 | (s->period_bytes * runtime->periods); | ||
302 | s->period_next_pt = s->period_start; | ||
303 | s->period_current_pt = s->period_start; | ||
304 | s->active = 1; | ||
305 | |||
306 | /* First; reset everything */ | ||
307 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
308 | out_8(®s->command, MPC52xx_PSC_RST_RX); | ||
309 | out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); | ||
310 | } else { | ||
311 | out_8(®s->command, MPC52xx_PSC_RST_TX); | ||
312 | out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); | ||
313 | } | ||
314 | |||
315 | /* Next, fill up the bestcomm bd queue and enable DMA. | ||
316 | * This will begin filling the PSC's fifo. */ | ||
317 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
318 | bcom_gen_bd_rx_reset(s->bcom_task); | ||
319 | else | ||
320 | bcom_gen_bd_tx_reset(s->bcom_task); | ||
321 | while (!bcom_queue_full(s->bcom_task)) | ||
322 | psc_i2s_bcom_enqueue_next_buffer(s); | ||
323 | bcom_enable(s->bcom_task); | ||
324 | |||
325 | /* Due to errata in the i2s mode; need to line up enabling | ||
326 | * the transmitter with a transition on the frame sync | ||
327 | * line */ | ||
328 | |||
329 | spin_lock_irqsave(&psc_i2s->lock, flags); | ||
330 | /* first make sure it is low */ | ||
331 | while ((in_8(®s->ipcr_acr.ipcr) & 0x80) != 0) | ||
332 | ; | ||
333 | /* then wait for the transition to high */ | ||
334 | while ((in_8(®s->ipcr_acr.ipcr) & 0x80) == 0) | ||
335 | ; | ||
336 | /* Finally, enable the PSC. | ||
337 | * Receiver must always be enabled; even when we only want | ||
338 | * transmit. (see 15.3.2.3 of MPC5200B User's Guide) */ | ||
339 | psc_cmd = MPC52xx_PSC_RX_ENABLE; | ||
340 | if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
341 | psc_cmd |= MPC52xx_PSC_TX_ENABLE; | ||
342 | out_8(®s->command, psc_cmd); | ||
343 | spin_unlock_irqrestore(&psc_i2s->lock, flags); | ||
344 | |||
345 | break; | ||
346 | |||
347 | case SNDRV_PCM_TRIGGER_STOP: | ||
348 | /* Turn off the PSC */ | ||
349 | s->active = 0; | ||
350 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
351 | if (!psc_i2s->playback.active) { | ||
352 | out_8(®s->command, 2 << 4); /* reset rx */ | ||
353 | out_8(®s->command, 3 << 4); /* reset tx */ | ||
354 | out_8(®s->command, 4 << 4); /* reset err */ | ||
355 | } | ||
356 | } else { | ||
357 | out_8(®s->command, 3 << 4); /* reset tx */ | ||
358 | out_8(®s->command, 4 << 4); /* reset err */ | ||
359 | if (!psc_i2s->capture.active) | ||
360 | out_8(®s->command, 2 << 4); /* reset rx */ | ||
361 | } | ||
362 | |||
363 | bcom_disable(s->bcom_task); | ||
364 | while (!bcom_queue_empty(s->bcom_task)) | ||
365 | bcom_retrieve_buffer(s->bcom_task, NULL, NULL); | ||
366 | |||
367 | break; | ||
368 | |||
369 | default: | ||
370 | dev_dbg(psc_i2s->dev, "invalid command\n"); | ||
371 | return -EINVAL; | 68 | return -EINVAL; |
372 | } | 69 | } |
373 | 70 | out_be32(&psc_dma->psc_regs->sicr, psc_dma->sicr | mode); | |
374 | /* Update interrupt enable settings */ | ||
375 | imr = 0; | ||
376 | if (psc_i2s->playback.active) | ||
377 | imr |= MPC52xx_PSC_IMR_TXEMP; | ||
378 | if (psc_i2s->capture.active) | ||
379 | imr |= MPC52xx_PSC_IMR_ORERR; | ||
380 | out_be16(®s->isr_imr.imr, imr); | ||
381 | 71 | ||
382 | return 0; | 72 | return 0; |
383 | } | 73 | } |
384 | 74 | ||
385 | /** | 75 | /** |
386 | * psc_i2s_shutdown: shutdown the data transfer on a stream | ||
387 | * | ||
388 | * Shutdown the PSC if there are no other substreams open. | ||
389 | */ | ||
390 | static void psc_i2s_shutdown(struct snd_pcm_substream *substream, | ||
391 | struct snd_soc_dai *dai) | ||
392 | { | ||
393 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
394 | struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; | ||
395 | |||
396 | dev_dbg(psc_i2s->dev, "psc_i2s_shutdown(substream=%p)\n", substream); | ||
397 | |||
398 | /* | ||
399 | * If this is the last active substream, disable the PSC and release | ||
400 | * the IRQ. | ||
401 | */ | ||
402 | if (!psc_i2s->playback.active && | ||
403 | !psc_i2s->capture.active) { | ||
404 | |||
405 | /* Disable all interrupts and reset the PSC */ | ||
406 | out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0); | ||
407 | out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset tx */ | ||
408 | out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset rx */ | ||
409 | out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */ | ||
410 | out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */ | ||
411 | |||
412 | /* Release irqs */ | ||
413 | free_irq(psc_i2s->irq, psc_i2s); | ||
414 | free_irq(psc_i2s->capture.irq, &psc_i2s->capture); | ||
415 | free_irq(psc_i2s->playback.irq, &psc_i2s->playback); | ||
416 | } | ||
417 | } | ||
418 | |||
419 | /** | ||
420 | * psc_i2s_set_sysclk: set the clock frequency and direction | 76 | * psc_i2s_set_sysclk: set the clock frequency and direction |
421 | * | 77 | * |
422 | * This function is called by the machine driver to tell us what the clock | 78 | * This function is called by the machine driver to tell us what the clock |
@@ -433,8 +89,8 @@ static void psc_i2s_shutdown(struct snd_pcm_substream *substream, | |||
433 | static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, | 89 | static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, |
434 | int clk_id, unsigned int freq, int dir) | 90 | int clk_id, unsigned int freq, int dir) |
435 | { | 91 | { |
436 | struct psc_i2s *psc_i2s = cpu_dai->private_data; | 92 | struct psc_dma *psc_dma = cpu_dai->private_data; |
437 | dev_dbg(psc_i2s->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n", | 93 | dev_dbg(psc_dma->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n", |
438 | cpu_dai, dir); | 94 | cpu_dai, dir); |
439 | return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL; | 95 | return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL; |
440 | } | 96 | } |
@@ -452,8 +108,8 @@ static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, | |||
452 | */ | 108 | */ |
453 | static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) | 109 | static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) |
454 | { | 110 | { |
455 | struct psc_i2s *psc_i2s = cpu_dai->private_data; | 111 | struct psc_dma *psc_dma = cpu_dai->private_data; |
456 | dev_dbg(psc_i2s->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n", | 112 | dev_dbg(psc_dma->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n", |
457 | cpu_dai, format); | 113 | cpu_dai, format); |
458 | return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL; | 114 | return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL; |
459 | } | 115 | } |
@@ -469,16 +125,13 @@ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) | |||
469 | * psc_i2s_dai_template: template CPU Digital Audio Interface | 125 | * psc_i2s_dai_template: template CPU Digital Audio Interface |
470 | */ | 126 | */ |
471 | static struct snd_soc_dai_ops psc_i2s_dai_ops = { | 127 | static struct snd_soc_dai_ops psc_i2s_dai_ops = { |
472 | .startup = psc_i2s_startup, | ||
473 | .hw_params = psc_i2s_hw_params, | 128 | .hw_params = psc_i2s_hw_params, |
474 | .hw_free = psc_i2s_hw_free, | ||
475 | .shutdown = psc_i2s_shutdown, | ||
476 | .trigger = psc_i2s_trigger, | ||
477 | .set_sysclk = psc_i2s_set_sysclk, | 129 | .set_sysclk = psc_i2s_set_sysclk, |
478 | .set_fmt = psc_i2s_set_fmt, | 130 | .set_fmt = psc_i2s_set_fmt, |
479 | }; | 131 | }; |
480 | 132 | ||
481 | static struct snd_soc_dai psc_i2s_dai_template = { | 133 | struct snd_soc_dai psc_i2s_dai[] = {{ |
134 | .name = "I2S", | ||
482 | .playback = { | 135 | .playback = { |
483 | .channels_min = 2, | 136 | .channels_min = 2, |
484 | .channels_max = 2, | 137 | .channels_max = 2, |
@@ -492,223 +145,8 @@ static struct snd_soc_dai psc_i2s_dai_template = { | |||
492 | .formats = PSC_I2S_FORMATS, | 145 | .formats = PSC_I2S_FORMATS, |
493 | }, | 146 | }, |
494 | .ops = &psc_i2s_dai_ops, | 147 | .ops = &psc_i2s_dai_ops, |
495 | }; | 148 | } }; |
496 | 149 | EXPORT_SYMBOL_GPL(psc_i2s_dai); | |
497 | /* --------------------------------------------------------------------- | ||
498 | * The PSC I2S 'ASoC platform' driver | ||
499 | * | ||
500 | * Can be referenced by an 'ASoC machine' driver | ||
501 | * This driver only deals with the audio bus; it doesn't have any | ||
502 | * interaction with the attached codec | ||
503 | */ | ||
504 | |||
505 | static const struct snd_pcm_hardware psc_i2s_pcm_hardware = { | ||
506 | .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | | ||
507 | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
508 | SNDRV_PCM_INFO_BATCH, | ||
509 | .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | | ||
510 | SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, | ||
511 | .rate_min = 8000, | ||
512 | .rate_max = 48000, | ||
513 | .channels_min = 2, | ||
514 | .channels_max = 2, | ||
515 | .period_bytes_max = 1024 * 1024, | ||
516 | .period_bytes_min = 32, | ||
517 | .periods_min = 2, | ||
518 | .periods_max = 256, | ||
519 | .buffer_bytes_max = 2 * 1024 * 1024, | ||
520 | .fifo_size = 0, | ||
521 | }; | ||
522 | |||
523 | static int psc_i2s_pcm_open(struct snd_pcm_substream *substream) | ||
524 | { | ||
525 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
526 | struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; | ||
527 | struct psc_i2s_stream *s; | ||
528 | |||
529 | dev_dbg(psc_i2s->dev, "psc_i2s_pcm_open(substream=%p)\n", substream); | ||
530 | |||
531 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
532 | s = &psc_i2s->capture; | ||
533 | else | ||
534 | s = &psc_i2s->playback; | ||
535 | |||
536 | snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware); | ||
537 | |||
538 | s->stream = substream; | ||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | static int psc_i2s_pcm_close(struct snd_pcm_substream *substream) | ||
543 | { | ||
544 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
545 | struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; | ||
546 | struct psc_i2s_stream *s; | ||
547 | |||
548 | dev_dbg(psc_i2s->dev, "psc_i2s_pcm_close(substream=%p)\n", substream); | ||
549 | |||
550 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
551 | s = &psc_i2s->capture; | ||
552 | else | ||
553 | s = &psc_i2s->playback; | ||
554 | |||
555 | s->stream = NULL; | ||
556 | return 0; | ||
557 | } | ||
558 | |||
559 | static snd_pcm_uframes_t | ||
560 | psc_i2s_pcm_pointer(struct snd_pcm_substream *substream) | ||
561 | { | ||
562 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
563 | struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; | ||
564 | struct psc_i2s_stream *s; | ||
565 | dma_addr_t count; | ||
566 | |||
567 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
568 | s = &psc_i2s->capture; | ||
569 | else | ||
570 | s = &psc_i2s->playback; | ||
571 | |||
572 | count = s->period_current_pt - s->period_start; | ||
573 | |||
574 | return bytes_to_frames(substream->runtime, count); | ||
575 | } | ||
576 | |||
577 | static struct snd_pcm_ops psc_i2s_pcm_ops = { | ||
578 | .open = psc_i2s_pcm_open, | ||
579 | .close = psc_i2s_pcm_close, | ||
580 | .ioctl = snd_pcm_lib_ioctl, | ||
581 | .pointer = psc_i2s_pcm_pointer, | ||
582 | }; | ||
583 | |||
584 | static u64 psc_i2s_pcm_dmamask = 0xffffffff; | ||
585 | static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, | ||
586 | struct snd_pcm *pcm) | ||
587 | { | ||
588 | struct snd_soc_pcm_runtime *rtd = pcm->private_data; | ||
589 | size_t size = psc_i2s_pcm_hardware.buffer_bytes_max; | ||
590 | int rc = 0; | ||
591 | |||
592 | dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_new(card=%p, dai=%p, pcm=%p)\n", | ||
593 | card, dai, pcm); | ||
594 | |||
595 | if (!card->dev->dma_mask) | ||
596 | card->dev->dma_mask = &psc_i2s_pcm_dmamask; | ||
597 | if (!card->dev->coherent_dma_mask) | ||
598 | card->dev->coherent_dma_mask = 0xffffffff; | ||
599 | |||
600 | if (pcm->streams[0].substream) { | ||
601 | rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, | ||
602 | &pcm->streams[0].substream->dma_buffer); | ||
603 | if (rc) | ||
604 | goto playback_alloc_err; | ||
605 | } | ||
606 | |||
607 | if (pcm->streams[1].substream) { | ||
608 | rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, | ||
609 | &pcm->streams[1].substream->dma_buffer); | ||
610 | if (rc) | ||
611 | goto capture_alloc_err; | ||
612 | } | ||
613 | |||
614 | return 0; | ||
615 | |||
616 | capture_alloc_err: | ||
617 | if (pcm->streams[0].substream) | ||
618 | snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); | ||
619 | playback_alloc_err: | ||
620 | dev_err(card->dev, "Cannot allocate buffer(s)\n"); | ||
621 | return -ENOMEM; | ||
622 | } | ||
623 | |||
624 | static void psc_i2s_pcm_free(struct snd_pcm *pcm) | ||
625 | { | ||
626 | struct snd_soc_pcm_runtime *rtd = pcm->private_data; | ||
627 | struct snd_pcm_substream *substream; | ||
628 | int stream; | ||
629 | |||
630 | dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_free(pcm=%p)\n", pcm); | ||
631 | |||
632 | for (stream = 0; stream < 2; stream++) { | ||
633 | substream = pcm->streams[stream].substream; | ||
634 | if (substream) { | ||
635 | snd_dma_free_pages(&substream->dma_buffer); | ||
636 | substream->dma_buffer.area = NULL; | ||
637 | substream->dma_buffer.addr = 0; | ||
638 | } | ||
639 | } | ||
640 | } | ||
641 | |||
642 | struct snd_soc_platform psc_i2s_pcm_soc_platform = { | ||
643 | .name = "mpc5200-psc-audio", | ||
644 | .pcm_ops = &psc_i2s_pcm_ops, | ||
645 | .pcm_new = &psc_i2s_pcm_new, | ||
646 | .pcm_free = &psc_i2s_pcm_free, | ||
647 | }; | ||
648 | |||
649 | /* --------------------------------------------------------------------- | ||
650 | * Sysfs attributes for debugging | ||
651 | */ | ||
652 | |||
653 | static ssize_t psc_i2s_status_show(struct device *dev, | ||
654 | struct device_attribute *attr, char *buf) | ||
655 | { | ||
656 | struct psc_i2s *psc_i2s = dev_get_drvdata(dev); | ||
657 | |||
658 | return sprintf(buf, "status=%.4x sicr=%.8x rfnum=%i rfstat=0x%.4x " | ||
659 | "tfnum=%i tfstat=0x%.4x\n", | ||
660 | in_be16(&psc_i2s->psc_regs->sr_csr.status), | ||
661 | in_be32(&psc_i2s->psc_regs->sicr), | ||
662 | in_be16(&psc_i2s->fifo_regs->rfnum) & 0x1ff, | ||
663 | in_be16(&psc_i2s->fifo_regs->rfstat), | ||
664 | in_be16(&psc_i2s->fifo_regs->tfnum) & 0x1ff, | ||
665 | in_be16(&psc_i2s->fifo_regs->tfstat)); | ||
666 | } | ||
667 | |||
668 | static int *psc_i2s_get_stat_attr(struct psc_i2s *psc_i2s, const char *name) | ||
669 | { | ||
670 | if (strcmp(name, "playback_underrun") == 0) | ||
671 | return &psc_i2s->stats.underrun_count; | ||
672 | if (strcmp(name, "capture_overrun") == 0) | ||
673 | return &psc_i2s->stats.overrun_count; | ||
674 | |||
675 | return NULL; | ||
676 | } | ||
677 | |||
678 | static ssize_t psc_i2s_stat_show(struct device *dev, | ||
679 | struct device_attribute *attr, char *buf) | ||
680 | { | ||
681 | struct psc_i2s *psc_i2s = dev_get_drvdata(dev); | ||
682 | int *attrib; | ||
683 | |||
684 | attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name); | ||
685 | if (!attrib) | ||
686 | return 0; | ||
687 | |||
688 | return sprintf(buf, "%i\n", *attrib); | ||
689 | } | ||
690 | |||
691 | static ssize_t psc_i2s_stat_store(struct device *dev, | ||
692 | struct device_attribute *attr, | ||
693 | const char *buf, | ||
694 | size_t count) | ||
695 | { | ||
696 | struct psc_i2s *psc_i2s = dev_get_drvdata(dev); | ||
697 | int *attrib; | ||
698 | |||
699 | attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name); | ||
700 | if (!attrib) | ||
701 | return 0; | ||
702 | |||
703 | *attrib = simple_strtoul(buf, NULL, 0); | ||
704 | return count; | ||
705 | } | ||
706 | |||
707 | static DEVICE_ATTR(status, 0644, psc_i2s_status_show, NULL); | ||
708 | static DEVICE_ATTR(playback_underrun, 0644, psc_i2s_stat_show, | ||
709 | psc_i2s_stat_store); | ||
710 | static DEVICE_ATTR(capture_overrun, 0644, psc_i2s_stat_show, | ||
711 | psc_i2s_stat_store); | ||
712 | 150 | ||
713 | /* --------------------------------------------------------------------- | 151 | /* --------------------------------------------------------------------- |
714 | * OF platform bus binding code: | 152 | * OF platform bus binding code: |
@@ -718,150 +156,65 @@ static DEVICE_ATTR(capture_overrun, 0644, psc_i2s_stat_show, | |||
718 | static int __devinit psc_i2s_of_probe(struct of_device *op, | 156 | static int __devinit psc_i2s_of_probe(struct of_device *op, |
719 | const struct of_device_id *match) | 157 | const struct of_device_id *match) |
720 | { | 158 | { |
721 | phys_addr_t fifo; | 159 | int rc; |
722 | struct psc_i2s *psc_i2s; | 160 | struct psc_dma *psc_dma; |
723 | struct resource res; | 161 | struct mpc52xx_psc __iomem *regs; |
724 | int size, psc_id, irq, rc; | ||
725 | const __be32 *prop; | ||
726 | void __iomem *regs; | ||
727 | |||
728 | dev_dbg(&op->dev, "probing psc i2s device\n"); | ||
729 | |||
730 | /* Get the PSC ID */ | ||
731 | prop = of_get_property(op->node, "cell-index", &size); | ||
732 | if (!prop || size < sizeof *prop) | ||
733 | return -ENODEV; | ||
734 | psc_id = be32_to_cpu(*prop); | ||
735 | |||
736 | /* Fetch the registers and IRQ of the PSC */ | ||
737 | irq = irq_of_parse_and_map(op->node, 0); | ||
738 | if (of_address_to_resource(op->node, 0, &res)) { | ||
739 | dev_err(&op->dev, "Missing reg property\n"); | ||
740 | return -ENODEV; | ||
741 | } | ||
742 | regs = ioremap(res.start, 1 + res.end - res.start); | ||
743 | if (!regs) { | ||
744 | dev_err(&op->dev, "Could not map registers\n"); | ||
745 | return -ENODEV; | ||
746 | } | ||
747 | 162 | ||
748 | /* Allocate and initialize the driver private data */ | 163 | rc = mpc5200_audio_dma_create(op); |
749 | psc_i2s = kzalloc(sizeof *psc_i2s, GFP_KERNEL); | 164 | if (rc != 0) |
750 | if (!psc_i2s) { | 165 | return rc; |
751 | iounmap(regs); | 166 | |
752 | return -ENOMEM; | 167 | rc = snd_soc_register_dais(psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai)); |
753 | } | 168 | if (rc != 0) { |
754 | spin_lock_init(&psc_i2s->lock); | 169 | pr_err("Failed to register DAI\n"); |
755 | psc_i2s->irq = irq; | 170 | return 0; |
756 | psc_i2s->psc_regs = regs; | ||
757 | psc_i2s->fifo_regs = regs + sizeof *psc_i2s->psc_regs; | ||
758 | psc_i2s->dev = &op->dev; | ||
759 | psc_i2s->playback.psc_i2s = psc_i2s; | ||
760 | psc_i2s->capture.psc_i2s = psc_i2s; | ||
761 | snprintf(psc_i2s->name, sizeof psc_i2s->name, "PSC%u", psc_id+1); | ||
762 | |||
763 | /* Fill out the CPU DAI structure */ | ||
764 | memcpy(&psc_i2s->dai, &psc_i2s_dai_template, sizeof psc_i2s->dai); | ||
765 | psc_i2s->dai.private_data = psc_i2s; | ||
766 | psc_i2s->dai.name = psc_i2s->name; | ||
767 | psc_i2s->dai.id = psc_id; | ||
768 | |||
769 | /* Find the address of the fifo data registers and setup the | ||
770 | * DMA tasks */ | ||
771 | fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32); | ||
772 | psc_i2s->capture.bcom_task = | ||
773 | bcom_psc_gen_bd_rx_init(psc_id, 10, fifo, 512); | ||
774 | psc_i2s->playback.bcom_task = | ||
775 | bcom_psc_gen_bd_tx_init(psc_id, 10, fifo); | ||
776 | if (!psc_i2s->capture.bcom_task || | ||
777 | !psc_i2s->playback.bcom_task) { | ||
778 | dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); | ||
779 | iounmap(regs); | ||
780 | kfree(psc_i2s); | ||
781 | return -ENODEV; | ||
782 | } | 171 | } |
783 | 172 | ||
784 | /* Disable all interrupts and reset the PSC */ | 173 | psc_dma = dev_get_drvdata(&op->dev); |
785 | out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0); | 174 | regs = psc_dma->psc_regs; |
786 | out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset transmitter */ | ||
787 | out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset receiver */ | ||
788 | out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */ | ||
789 | out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */ | ||
790 | 175 | ||
791 | /* Configure the serial interface mode; defaulting to CODEC8 mode */ | 176 | /* Configure the serial interface mode; defaulting to CODEC8 mode */ |
792 | psc_i2s->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S | | 177 | psc_dma->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S | |
793 | MPC52xx_PSC_SICR_CLKPOL; | 178 | MPC52xx_PSC_SICR_CLKPOL; |
794 | if (of_get_property(op->node, "fsl,cellslave", NULL)) | 179 | out_be32(&psc_dma->psc_regs->sicr, |
795 | psc_i2s->sicr |= MPC52xx_PSC_SICR_CELLSLAVE | | 180 | psc_dma->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8); |
796 | MPC52xx_PSC_SICR_GENCLK; | ||
797 | out_be32(&psc_i2s->psc_regs->sicr, | ||
798 | psc_i2s->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8); | ||
799 | 181 | ||
800 | /* Check for the codec handle. If it is not present then we | 182 | /* Check for the codec handle. If it is not present then we |
801 | * are done */ | 183 | * are done */ |
802 | if (!of_get_property(op->node, "codec-handle", NULL)) | 184 | if (!of_get_property(op->node, "codec-handle", NULL)) |
803 | return 0; | 185 | return 0; |
804 | 186 | ||
805 | /* Set up mode register; | 187 | /* Due to errata in the dma mode; need to line up enabling |
806 | * First write: RxRdy (FIFO Alarm) generates rx FIFO irq | 188 | * the transmitter with a transition on the frame sync |
807 | * Second write: register Normal mode for non loopback | 189 | * line */ |
808 | */ | 190 | |
809 | out_8(&psc_i2s->psc_regs->mode, 0); | 191 | /* first make sure it is low */ |
810 | out_8(&psc_i2s->psc_regs->mode, 0); | 192 | while ((in_8(®s->ipcr_acr.ipcr) & 0x80) != 0) |
811 | 193 | ; | |
812 | /* Set the TX and RX fifo alarm thresholds */ | 194 | /* then wait for the transition to high */ |
813 | out_be16(&psc_i2s->fifo_regs->rfalarm, 0x100); | 195 | while ((in_8(®s->ipcr_acr.ipcr) & 0x80) == 0) |
814 | out_8(&psc_i2s->fifo_regs->rfcntl, 0x4); | 196 | ; |
815 | out_be16(&psc_i2s->fifo_regs->tfalarm, 0x100); | 197 | /* Finally, enable the PSC. |
816 | out_8(&psc_i2s->fifo_regs->tfcntl, 0x7); | 198 | * Receiver must always be enabled; even when we only want |
817 | 199 | * transmit. (see 15.3.2.3 of MPC5200B User's Guide) */ | |
818 | /* Lookup the IRQ numbers */ | 200 | |
819 | psc_i2s->playback.irq = | 201 | /* Go */ |
820 | bcom_get_task_irq(psc_i2s->playback.bcom_task); | 202 | out_8(&psc_dma->psc_regs->command, |
821 | psc_i2s->capture.irq = | 203 | MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); |
822 | bcom_get_task_irq(psc_i2s->capture.bcom_task); | ||
823 | |||
824 | /* Save what we've done so it can be found again later */ | ||
825 | dev_set_drvdata(&op->dev, psc_i2s); | ||
826 | |||
827 | /* Register the SYSFS files */ | ||
828 | rc = device_create_file(psc_i2s->dev, &dev_attr_status); | ||
829 | rc |= device_create_file(psc_i2s->dev, &dev_attr_capture_overrun); | ||
830 | rc |= device_create_file(psc_i2s->dev, &dev_attr_playback_underrun); | ||
831 | if (rc) | ||
832 | dev_info(psc_i2s->dev, "error creating sysfs files\n"); | ||
833 | |||
834 | snd_soc_register_platform(&psc_i2s_pcm_soc_platform); | ||
835 | |||
836 | /* Tell the ASoC OF helpers about it */ | ||
837 | of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node, | ||
838 | &psc_i2s->dai); | ||
839 | 204 | ||
840 | return 0; | 205 | return 0; |
206 | |||
841 | } | 207 | } |
842 | 208 | ||
843 | static int __devexit psc_i2s_of_remove(struct of_device *op) | 209 | static int __devexit psc_i2s_of_remove(struct of_device *op) |
844 | { | 210 | { |
845 | struct psc_i2s *psc_i2s = dev_get_drvdata(&op->dev); | 211 | return mpc5200_audio_dma_destroy(op); |
846 | |||
847 | dev_dbg(&op->dev, "psc_i2s_remove()\n"); | ||
848 | |||
849 | snd_soc_unregister_platform(&psc_i2s_pcm_soc_platform); | ||
850 | |||
851 | bcom_gen_bd_rx_release(psc_i2s->capture.bcom_task); | ||
852 | bcom_gen_bd_tx_release(psc_i2s->playback.bcom_task); | ||
853 | |||
854 | iounmap(psc_i2s->psc_regs); | ||
855 | iounmap(psc_i2s->fifo_regs); | ||
856 | kfree(psc_i2s); | ||
857 | dev_set_drvdata(&op->dev, NULL); | ||
858 | |||
859 | return 0; | ||
860 | } | 212 | } |
861 | 213 | ||
862 | /* Match table for of_platform binding */ | 214 | /* Match table for of_platform binding */ |
863 | static struct of_device_id psc_i2s_match[] __devinitdata = { | 215 | static struct of_device_id psc_i2s_match[] __devinitdata = { |
864 | { .compatible = "fsl,mpc5200-psc-i2s", }, | 216 | { .compatible = "fsl,mpc5200-psc-i2s", }, |
217 | { .compatible = "fsl,mpc5200b-psc-i2s", }, | ||
865 | {} | 218 | {} |
866 | }; | 219 | }; |
867 | MODULE_DEVICE_TABLE(of, psc_i2s_match); | 220 | MODULE_DEVICE_TABLE(of, psc_i2s_match); |
@@ -892,4 +245,7 @@ static void __exit psc_i2s_exit(void) | |||
892 | } | 245 | } |
893 | module_exit(psc_i2s_exit); | 246 | module_exit(psc_i2s_exit); |
894 | 247 | ||
248 | MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); | ||
249 | MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver"); | ||
250 | MODULE_LICENSE("GPL"); | ||
895 | 251 | ||
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.h b/sound/soc/fsl/mpc5200_psc_i2s.h new file mode 100644 index 000000000000..ce55e070fdf3 --- /dev/null +++ b/sound/soc/fsl/mpc5200_psc_i2s.h | |||
@@ -0,0 +1,12 @@ | |||
1 | /* | ||
2 | * Freescale MPC5200 PSC in I2S mode | ||
3 | * ALSA SoC Digital Audio Interface (DAI) driver | ||
4 | * | ||
5 | */ | ||
6 | |||
7 | #ifndef __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__ | ||
8 | #define __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__ | ||
9 | |||
10 | extern struct snd_soc_dai psc_i2s_dai[]; | ||
11 | |||
12 | #endif /* __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__ */ | ||
diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c new file mode 100644 index 000000000000..8766f7a3893d --- /dev/null +++ b/sound/soc/fsl/pcm030-audio-fabric.c | |||
@@ -0,0 +1,90 @@ | |||
1 | /* | ||
2 | * Phytec pcm030 driver for the PSC of the Freescale MPC52xx | ||
3 | * configured as AC97 interface | ||
4 | * | ||
5 | * Copyright 2008 Jon Smirl, Digispeaker | ||
6 | * Author: Jon Smirl <jonsmirl@gmail.com> | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public License | ||
9 | * version 2. This program is licensed "as is" without any warranty of any | ||
10 | * kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #include <linux/init.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/device.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/of_device.h> | ||
19 | #include <linux/of_platform.h> | ||
20 | #include <linux/dma-mapping.h> | ||
21 | |||
22 | #include <sound/core.h> | ||
23 | #include <sound/pcm.h> | ||
24 | #include <sound/pcm_params.h> | ||
25 | #include <sound/initval.h> | ||
26 | #include <sound/soc.h> | ||
27 | #include <sound/soc-of-simple.h> | ||
28 | |||
29 | #include "mpc5200_dma.h" | ||
30 | #include "mpc5200_psc_ac97.h" | ||
31 | #include "../codecs/wm9712.h" | ||
32 | |||
33 | static struct snd_soc_device device; | ||
34 | static struct snd_soc_card card; | ||
35 | |||
36 | static struct snd_soc_dai_link pcm030_fabric_dai[] = { | ||
37 | { | ||
38 | .name = "AC97", | ||
39 | .stream_name = "AC97 Analog", | ||
40 | .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI], | ||
41 | .cpu_dai = &psc_ac97_dai[MPC5200_AC97_NORMAL], | ||
42 | }, | ||
43 | { | ||
44 | .name = "AC97", | ||
45 | .stream_name = "AC97 IEC958", | ||
46 | .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], | ||
47 | .cpu_dai = &psc_ac97_dai[MPC5200_AC97_SPDIF], | ||
48 | }, | ||
49 | }; | ||
50 | |||
51 | static __init int pcm030_fabric_init(void) | ||
52 | { | ||
53 | struct platform_device *pdev; | ||
54 | int rc; | ||
55 | |||
56 | if (!machine_is_compatible("phytec,pcm030")) | ||
57 | return -ENODEV; | ||
58 | |||
59 | card.platform = &mpc5200_audio_dma_platform; | ||
60 | card.name = "pcm030"; | ||
61 | card.dai_link = pcm030_fabric_dai; | ||
62 | card.num_links = ARRAY_SIZE(pcm030_fabric_dai); | ||
63 | |||
64 | device.card = &card; | ||
65 | device.codec_dev = &soc_codec_dev_wm9712; | ||
66 | |||
67 | pdev = platform_device_alloc("soc-audio", 1); | ||
68 | if (!pdev) { | ||
69 | pr_err("pcm030_fabric_init: platform_device_alloc() failed\n"); | ||
70 | return -ENODEV; | ||
71 | } | ||
72 | |||
73 | platform_set_drvdata(pdev, &device); | ||
74 | device.dev = &pdev->dev; | ||
75 | |||
76 | rc = platform_device_add(pdev); | ||
77 | if (rc) { | ||
78 | pr_err("pcm030_fabric_init: platform_device_add() failed\n"); | ||
79 | return -ENODEV; | ||
80 | } | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | module_init(pcm030_fabric_init); | ||
85 | |||
86 | |||
87 | MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>"); | ||
88 | MODULE_DESCRIPTION(DRV_NAME ": mpc5200 pcm030 fabric driver"); | ||
89 | MODULE_LICENSE("GPL"); | ||
90 | |||