diff options
author | Jon Smirl <jonsmirl@gmail.com> | 2009-05-26 08:34:08 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-05-26 16:12:50 -0400 |
commit | dbcc34756234596993a3b1304636f032e66d401f (patch) | |
tree | f1188f0c20ce402f778257bb0d905456ee213b14 /sound/soc | |
parent | 0f89bdcac61536c5cb2a095a514657019573afb4 (diff) |
ASoC: Main rewite of the mpc5200 audio DMA code
Rewrite the mpc5200 audio DMA code to support both I2S and AC97.
Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc')
-rw-r--r-- | sound/soc/fsl/Kconfig | 1 | ||||
-rw-r--r-- | sound/soc/fsl/mpc5200_dma.c | 442 | ||||
-rw-r--r-- | sound/soc/fsl/mpc5200_dma.h | 33 | ||||
-rw-r--r-- | sound/soc/fsl/mpc5200_psc_i2s.c | 247 | ||||
-rw-r--r-- | sound/soc/fsl/mpc5200_psc_i2s.h | 12 |
5 files changed, 344 insertions, 391 deletions
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index dc79bdf33692..1918c78b858e 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig | |||
@@ -25,7 +25,6 @@ config SND_SOC_MPC8610_HPCD | |||
25 | config SND_SOC_MPC5200_I2S | 25 | config SND_SOC_MPC5200_I2S |
26 | tristate "Freescale MPC5200 PSC in I2S mode driver" | 26 | tristate "Freescale MPC5200 PSC in I2S mode driver" |
27 | depends on PPC_MPC52xx && PPC_BESTCOMM | 27 | depends on PPC_MPC52xx && PPC_BESTCOMM |
28 | select SND_SOC_OF_SIMPLE | ||
29 | select SND_MPC52xx_DMA | 28 | select SND_MPC52xx_DMA |
30 | select PPC_BESTCOMM_GEN_BD | 29 | select PPC_BESTCOMM_GEN_BD |
31 | help | 30 | help |
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 685039213f05..efec33a1c5bd 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c | |||
@@ -3,23 +3,13 @@ | |||
3 | * ALSA SoC Platform driver | 3 | * ALSA SoC Platform 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> | ||
15 | #include <linux/dma-mapping.h> | ||
16 | 11 | ||
17 | #include <sound/core.h> | ||
18 | #include <sound/pcm.h> | ||
19 | #include <sound/pcm_params.h> | ||
20 | #include <sound/initval.h> | ||
21 | #include <sound/soc.h> | 12 | #include <sound/soc.h> |
22 | #include <sound/soc-of-simple.h> | ||
23 | 13 | ||
24 | #include <sysdev/bestcomm/bestcomm.h> | 14 | #include <sysdev/bestcomm/bestcomm.h> |
25 | #include <sysdev/bestcomm/gen_bd.h> | 15 | #include <sysdev/bestcomm/gen_bd.h> |
@@ -27,10 +17,6 @@ | |||
27 | 17 | ||
28 | #include "mpc5200_dma.h" | 18 | #include "mpc5200_dma.h" |
29 | 19 | ||
30 | MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); | ||
31 | MODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver"); | ||
32 | MODULE_LICENSE("GPL"); | ||
33 | |||
34 | /* | 20 | /* |
35 | * Interrupt handlers | 21 | * Interrupt handlers |
36 | */ | 22 | */ |
@@ -50,7 +36,7 @@ static irqreturn_t psc_dma_status_irq(int irq, void *_psc_dma) | |||
50 | if (psc_dma->capture.active && (isr & MPC52xx_PSC_IMR_ORERR)) | 36 | if (psc_dma->capture.active && (isr & MPC52xx_PSC_IMR_ORERR)) |
51 | psc_dma->stats.overrun_count++; | 37 | psc_dma->stats.overrun_count++; |
52 | 38 | ||
53 | out_8(®s->command, 4 << 4); /* reset the error status */ | 39 | out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); |
54 | 40 | ||
55 | return IRQ_HANDLED; | 41 | return IRQ_HANDLED; |
56 | } | 42 | } |
@@ -81,21 +67,36 @@ static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s) | |||
81 | s->period_next_pt = s->period_start; | 67 | s->period_next_pt = s->period_start; |
82 | } | 68 | } |
83 | 69 | ||
70 | static void psc_dma_bcom_enqueue_tx(struct psc_dma_stream *s) | ||
71 | { | ||
72 | while (s->appl_ptr < s->runtime->control->appl_ptr) { | ||
73 | |||
74 | if (bcom_queue_full(s->bcom_task)) | ||
75 | return; | ||
76 | |||
77 | s->appl_ptr += s->period_size; | ||
78 | |||
79 | psc_dma_bcom_enqueue_next_buffer(s); | ||
80 | } | ||
81 | } | ||
82 | |||
84 | /* Bestcomm DMA irq handler */ | 83 | /* Bestcomm DMA irq handler */ |
85 | static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) | 84 | static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream) |
86 | { | 85 | { |
87 | struct psc_dma_stream *s = _psc_dma_stream; | 86 | struct psc_dma_stream *s = _psc_dma_stream; |
88 | 87 | ||
88 | spin_lock(&s->psc_dma->lock); | ||
89 | /* For each finished period, dequeue the completed period buffer | 89 | /* For each finished period, dequeue the completed period buffer |
90 | * and enqueue a new one in it's place. */ | 90 | * and enqueue a new one in it's place. */ |
91 | while (bcom_buffer_done(s->bcom_task)) { | 91 | while (bcom_buffer_done(s->bcom_task)) { |
92 | bcom_retrieve_buffer(s->bcom_task, NULL, NULL); | 92 | bcom_retrieve_buffer(s->bcom_task, NULL, NULL); |
93 | |||
93 | s->period_current_pt += s->period_bytes; | 94 | s->period_current_pt += s->period_bytes; |
94 | if (s->period_current_pt >= s->period_end) | 95 | if (s->period_current_pt >= s->period_end) |
95 | s->period_current_pt = s->period_start; | 96 | s->period_current_pt = s->period_start; |
96 | psc_dma_bcom_enqueue_next_buffer(s); | ||
97 | bcom_enable(s->bcom_task); | ||
98 | } | 97 | } |
98 | psc_dma_bcom_enqueue_tx(s); | ||
99 | spin_unlock(&s->psc_dma->lock); | ||
99 | 100 | ||
100 | /* If the stream is active, then also inform the PCM middle layer | 101 | /* If the stream is active, then also inform the PCM middle layer |
101 | * of the period finished event. */ | 102 | * of the period finished event. */ |
@@ -105,49 +106,33 @@ static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) | |||
105 | return IRQ_HANDLED; | 106 | return IRQ_HANDLED; |
106 | } | 107 | } |
107 | 108 | ||
108 | /** | 109 | static irqreturn_t psc_dma_bcom_irq_rx(int irq, void *_psc_dma_stream) |
109 | * psc_dma_startup: create a new substream | ||
110 | * | ||
111 | * This is the first function called when a stream is opened. | ||
112 | * | ||
113 | * If this is the first stream open, then grab the IRQ and program most of | ||
114 | * the PSC registers. | ||
115 | */ | ||
116 | int psc_dma_startup(struct snd_pcm_substream *substream, | ||
117 | struct snd_soc_dai *dai) | ||
118 | { | 110 | { |
119 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 111 | struct psc_dma_stream *s = _psc_dma_stream; |
120 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | ||
121 | int rc; | ||
122 | 112 | ||
123 | dev_dbg(psc_dma->dev, "psc_dma_startup(substream=%p)\n", substream); | 113 | spin_lock(&s->psc_dma->lock); |
114 | /* For each finished period, dequeue the completed period buffer | ||
115 | * and enqueue a new one in it's place. */ | ||
116 | while (bcom_buffer_done(s->bcom_task)) { | ||
117 | bcom_retrieve_buffer(s->bcom_task, NULL, NULL); | ||
124 | 118 | ||
125 | if (!psc_dma->playback.active && | 119 | s->period_current_pt += s->period_bytes; |
126 | !psc_dma->capture.active) { | 120 | if (s->period_current_pt >= s->period_end) |
127 | /* Setup the IRQs */ | 121 | s->period_current_pt = s->period_start; |
128 | rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, | 122 | |
129 | "psc-dma-status", psc_dma); | 123 | psc_dma_bcom_enqueue_next_buffer(s); |
130 | rc |= request_irq(psc_dma->capture.irq, | ||
131 | &psc_dma_bcom_irq, IRQF_SHARED, | ||
132 | "psc-dma-capture", &psc_dma->capture); | ||
133 | rc |= request_irq(psc_dma->playback.irq, | ||
134 | &psc_dma_bcom_irq, IRQF_SHARED, | ||
135 | "psc-dma-playback", &psc_dma->playback); | ||
136 | if (rc) { | ||
137 | free_irq(psc_dma->irq, psc_dma); | ||
138 | free_irq(psc_dma->capture.irq, | ||
139 | &psc_dma->capture); | ||
140 | free_irq(psc_dma->playback.irq, | ||
141 | &psc_dma->playback); | ||
142 | return -ENODEV; | ||
143 | } | ||
144 | } | 124 | } |
125 | spin_unlock(&s->psc_dma->lock); | ||
145 | 126 | ||
146 | return 0; | 127 | /* If the stream is active, then also inform the PCM middle layer |
128 | * of the period finished event. */ | ||
129 | if (s->active) | ||
130 | snd_pcm_period_elapsed(s->stream); | ||
131 | |||
132 | return IRQ_HANDLED; | ||
147 | } | 133 | } |
148 | 134 | ||
149 | int psc_dma_hw_free(struct snd_pcm_substream *substream, | 135 | static int psc_dma_hw_free(struct snd_pcm_substream *substream) |
150 | struct snd_soc_dai *dai) | ||
151 | { | 136 | { |
152 | snd_pcm_set_runtime_buffer(substream, NULL); | 137 | snd_pcm_set_runtime_buffer(substream, NULL); |
153 | return 0; | 138 | return 0; |
@@ -159,8 +144,7 @@ int psc_dma_hw_free(struct snd_pcm_substream *substream, | |||
159 | * This function is called by ALSA to start, stop, pause, and resume the DMA | 144 | * This function is called by ALSA to start, stop, pause, and resume the DMA |
160 | * transfer of data. | 145 | * transfer of data. |
161 | */ | 146 | */ |
162 | int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd, | 147 | static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) |
163 | struct snd_soc_dai *dai) | ||
164 | { | 148 | { |
165 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 149 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
166 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | 150 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; |
@@ -168,8 +152,8 @@ int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd, | |||
168 | struct psc_dma_stream *s; | 152 | struct psc_dma_stream *s; |
169 | struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; | 153 | struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; |
170 | u16 imr; | 154 | u16 imr; |
171 | u8 psc_cmd; | ||
172 | unsigned long flags; | 155 | unsigned long flags; |
156 | int i; | ||
173 | 157 | ||
174 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | 158 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) |
175 | s = &psc_dma->capture; | 159 | s = &psc_dma->capture; |
@@ -189,68 +173,48 @@ int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd, | |||
189 | (s->period_bytes * runtime->periods); | 173 | (s->period_bytes * runtime->periods); |
190 | s->period_next_pt = s->period_start; | 174 | s->period_next_pt = s->period_start; |
191 | s->period_current_pt = s->period_start; | 175 | s->period_current_pt = s->period_start; |
176 | s->period_size = runtime->period_size; | ||
192 | s->active = 1; | 177 | s->active = 1; |
193 | 178 | ||
194 | /* First; reset everything */ | 179 | /* track appl_ptr so that we have a better chance of detecting |
180 | * end of stream and not over running it. | ||
181 | */ | ||
182 | s->runtime = runtime; | ||
183 | s->appl_ptr = s->runtime->control->appl_ptr - | ||
184 | (runtime->period_size * runtime->periods); | ||
185 | |||
186 | /* Fill up the bestcomm bd queue and enable DMA. | ||
187 | * This will begin filling the PSC's fifo. | ||
188 | */ | ||
189 | spin_lock_irqsave(&psc_dma->lock, flags); | ||
190 | |||
195 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { | 191 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { |
196 | out_8(®s->command, MPC52xx_PSC_RST_RX); | 192 | bcom_gen_bd_rx_reset(s->bcom_task); |
197 | out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); | 193 | for (i = 0; i < runtime->periods; i++) |
194 | if (!bcom_queue_full(s->bcom_task)) | ||
195 | psc_dma_bcom_enqueue_next_buffer(s); | ||
198 | } else { | 196 | } else { |
199 | out_8(®s->command, MPC52xx_PSC_RST_TX); | 197 | bcom_gen_bd_tx_reset(s->bcom_task); |
200 | out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); | 198 | psc_dma_bcom_enqueue_tx(s); |
201 | } | 199 | } |
202 | 200 | ||
203 | /* Next, fill up the bestcomm bd queue and enable DMA. | ||
204 | * This will begin filling the PSC's fifo. */ | ||
205 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
206 | bcom_gen_bd_rx_reset(s->bcom_task); | ||
207 | else | ||
208 | bcom_gen_bd_tx_reset(s->bcom_task); | ||
209 | while (!bcom_queue_full(s->bcom_task)) | ||
210 | psc_dma_bcom_enqueue_next_buffer(s); | ||
211 | bcom_enable(s->bcom_task); | 201 | bcom_enable(s->bcom_task); |
212 | |||
213 | /* Due to errata in the dma mode; need to line up enabling | ||
214 | * the transmitter with a transition on the frame sync | ||
215 | * line */ | ||
216 | |||
217 | spin_lock_irqsave(&psc_dma->lock, flags); | ||
218 | /* first make sure it is low */ | ||
219 | while ((in_8(®s->ipcr_acr.ipcr) & 0x80) != 0) | ||
220 | ; | ||
221 | /* then wait for the transition to high */ | ||
222 | while ((in_8(®s->ipcr_acr.ipcr) & 0x80) == 0) | ||
223 | ; | ||
224 | /* Finally, enable the PSC. | ||
225 | * Receiver must always be enabled; even when we only want | ||
226 | * transmit. (see 15.3.2.3 of MPC5200B User's Guide) */ | ||
227 | psc_cmd = MPC52xx_PSC_RX_ENABLE; | ||
228 | if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
229 | psc_cmd |= MPC52xx_PSC_TX_ENABLE; | ||
230 | out_8(®s->command, psc_cmd); | ||
231 | spin_unlock_irqrestore(&psc_dma->lock, flags); | 202 | spin_unlock_irqrestore(&psc_dma->lock, flags); |
232 | 203 | ||
204 | out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); | ||
205 | |||
233 | break; | 206 | break; |
234 | 207 | ||
235 | case SNDRV_PCM_TRIGGER_STOP: | 208 | case SNDRV_PCM_TRIGGER_STOP: |
236 | /* Turn off the PSC */ | ||
237 | s->active = 0; | 209 | s->active = 0; |
238 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
239 | if (!psc_dma->playback.active) { | ||
240 | out_8(®s->command, 2 << 4); /* reset rx */ | ||
241 | out_8(®s->command, 3 << 4); /* reset tx */ | ||
242 | out_8(®s->command, 4 << 4); /* reset err */ | ||
243 | } | ||
244 | } else { | ||
245 | out_8(®s->command, 3 << 4); /* reset tx */ | ||
246 | out_8(®s->command, 4 << 4); /* reset err */ | ||
247 | if (!psc_dma->capture.active) | ||
248 | out_8(®s->command, 2 << 4); /* reset rx */ | ||
249 | } | ||
250 | 210 | ||
211 | spin_lock_irqsave(&psc_dma->lock, flags); | ||
251 | bcom_disable(s->bcom_task); | 212 | bcom_disable(s->bcom_task); |
252 | while (!bcom_queue_empty(s->bcom_task)) | 213 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) |
253 | bcom_retrieve_buffer(s->bcom_task, NULL, NULL); | 214 | bcom_gen_bd_rx_reset(s->bcom_task); |
215 | else | ||
216 | bcom_gen_bd_tx_reset(s->bcom_task); | ||
217 | spin_unlock_irqrestore(&psc_dma->lock, flags); | ||
254 | 218 | ||
255 | break; | 219 | break; |
256 | 220 | ||
@@ -265,44 +229,11 @@ int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd, | |||
265 | imr |= MPC52xx_PSC_IMR_TXEMP; | 229 | imr |= MPC52xx_PSC_IMR_TXEMP; |
266 | if (psc_dma->capture.active) | 230 | if (psc_dma->capture.active) |
267 | imr |= MPC52xx_PSC_IMR_ORERR; | 231 | imr |= MPC52xx_PSC_IMR_ORERR; |
268 | out_be16(®s->isr_imr.imr, imr); | 232 | out_be16(®s->isr_imr.imr, psc_dma->imr | imr); |
269 | 233 | ||
270 | return 0; | 234 | return 0; |
271 | } | 235 | } |
272 | 236 | ||
273 | /** | ||
274 | * psc_dma_shutdown: shutdown the data transfer on a stream | ||
275 | * | ||
276 | * Shutdown the PSC if there are no other substreams open. | ||
277 | */ | ||
278 | void psc_dma_shutdown(struct snd_pcm_substream *substream, | ||
279 | struct snd_soc_dai *dai) | ||
280 | { | ||
281 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
282 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | ||
283 | |||
284 | dev_dbg(psc_dma->dev, "psc_dma_shutdown(substream=%p)\n", substream); | ||
285 | |||
286 | /* | ||
287 | * If this is the last active substream, disable the PSC and release | ||
288 | * the IRQ. | ||
289 | */ | ||
290 | if (!psc_dma->playback.active && | ||
291 | !psc_dma->capture.active) { | ||
292 | |||
293 | /* Disable all interrupts and reset the PSC */ | ||
294 | out_be16(&psc_dma->psc_regs->isr_imr.imr, 0); | ||
295 | out_8(&psc_dma->psc_regs->command, 3 << 4); /* reset tx */ | ||
296 | out_8(&psc_dma->psc_regs->command, 2 << 4); /* reset rx */ | ||
297 | out_8(&psc_dma->psc_regs->command, 1 << 4); /* reset mode */ | ||
298 | out_8(&psc_dma->psc_regs->command, 4 << 4); /* reset error */ | ||
299 | |||
300 | /* Release irqs */ | ||
301 | free_irq(psc_dma->irq, psc_dma); | ||
302 | free_irq(psc_dma->capture.irq, &psc_dma->capture); | ||
303 | free_irq(psc_dma->playback.irq, &psc_dma->playback); | ||
304 | } | ||
305 | } | ||
306 | 237 | ||
307 | /* --------------------------------------------------------------------- | 238 | /* --------------------------------------------------------------------- |
308 | * The PSC DMA 'ASoC platform' driver | 239 | * The PSC DMA 'ASoC platform' driver |
@@ -312,62 +243,78 @@ void psc_dma_shutdown(struct snd_pcm_substream *substream, | |||
312 | * interaction with the attached codec | 243 | * interaction with the attached codec |
313 | */ | 244 | */ |
314 | 245 | ||
315 | static const struct snd_pcm_hardware psc_dma_pcm_hardware = { | 246 | static const struct snd_pcm_hardware psc_dma_hardware = { |
316 | .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | | 247 | .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | |
317 | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | | 248 | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
318 | SNDRV_PCM_INFO_BATCH, | 249 | SNDRV_PCM_INFO_BATCH, |
319 | .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | | 250 | .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | |
320 | SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, | 251 | SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, |
321 | .rate_min = 8000, | 252 | .rate_min = 8000, |
322 | .rate_max = 48000, | 253 | .rate_max = 48000, |
323 | .channels_min = 2, | 254 | .channels_min = 1, |
324 | .channels_max = 2, | 255 | .channels_max = 2, |
325 | .period_bytes_max = 1024 * 1024, | 256 | .period_bytes_max = 1024 * 1024, |
326 | .period_bytes_min = 32, | 257 | .period_bytes_min = 32, |
327 | .periods_min = 2, | 258 | .periods_min = 2, |
328 | .periods_max = 256, | 259 | .periods_max = 256, |
329 | .buffer_bytes_max = 2 * 1024 * 1024, | 260 | .buffer_bytes_max = 2 * 1024 * 1024, |
330 | .fifo_size = 0, | 261 | .fifo_size = 512, |
331 | }; | 262 | }; |
332 | 263 | ||
333 | static int psc_dma_pcm_open(struct snd_pcm_substream *substream) | 264 | static int psc_dma_open(struct snd_pcm_substream *substream) |
334 | { | 265 | { |
266 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
335 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 267 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
336 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | 268 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; |
337 | struct psc_dma_stream *s; | 269 | struct psc_dma_stream *s; |
270 | int rc; | ||
338 | 271 | ||
339 | dev_dbg(psc_dma->dev, "psc_dma_pcm_open(substream=%p)\n", substream); | 272 | dev_dbg(psc_dma->dev, "psc_dma_open(substream=%p)\n", substream); |
340 | 273 | ||
341 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | 274 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) |
342 | s = &psc_dma->capture; | 275 | s = &psc_dma->capture; |
343 | else | 276 | else |
344 | s = &psc_dma->playback; | 277 | s = &psc_dma->playback; |
345 | 278 | ||
346 | snd_soc_set_runtime_hwparams(substream, &psc_dma_pcm_hardware); | 279 | snd_soc_set_runtime_hwparams(substream, &psc_dma_hardware); |
280 | |||
281 | rc = snd_pcm_hw_constraint_integer(runtime, | ||
282 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
283 | if (rc < 0) { | ||
284 | dev_err(substream->pcm->card->dev, "invalid buffer size\n"); | ||
285 | return rc; | ||
286 | } | ||
347 | 287 | ||
348 | s->stream = substream; | 288 | s->stream = substream; |
349 | return 0; | 289 | return 0; |
350 | } | 290 | } |
351 | 291 | ||
352 | static int psc_dma_pcm_close(struct snd_pcm_substream *substream) | 292 | static int psc_dma_close(struct snd_pcm_substream *substream) |
353 | { | 293 | { |
354 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 294 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
355 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | 295 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; |
356 | struct psc_dma_stream *s; | 296 | struct psc_dma_stream *s; |
357 | 297 | ||
358 | dev_dbg(psc_dma->dev, "psc_dma_pcm_close(substream=%p)\n", substream); | 298 | dev_dbg(psc_dma->dev, "psc_dma_close(substream=%p)\n", substream); |
359 | 299 | ||
360 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | 300 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) |
361 | s = &psc_dma->capture; | 301 | s = &psc_dma->capture; |
362 | else | 302 | else |
363 | s = &psc_dma->playback; | 303 | s = &psc_dma->playback; |
364 | 304 | ||
305 | if (!psc_dma->playback.active && | ||
306 | !psc_dma->capture.active) { | ||
307 | |||
308 | /* Disable all interrupts and reset the PSC */ | ||
309 | out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); | ||
310 | out_8(&psc_dma->psc_regs->command, 4 << 4); /* reset error */ | ||
311 | } | ||
365 | s->stream = NULL; | 312 | s->stream = NULL; |
366 | return 0; | 313 | return 0; |
367 | } | 314 | } |
368 | 315 | ||
369 | static snd_pcm_uframes_t | 316 | static snd_pcm_uframes_t |
370 | psc_dma_pcm_pointer(struct snd_pcm_substream *substream) | 317 | psc_dma_pointer(struct snd_pcm_substream *substream) |
371 | { | 318 | { |
372 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 319 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
373 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | 320 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; |
@@ -384,60 +331,78 @@ psc_dma_pcm_pointer(struct snd_pcm_substream *substream) | |||
384 | return bytes_to_frames(substream->runtime, count); | 331 | return bytes_to_frames(substream->runtime, count); |
385 | } | 332 | } |
386 | 333 | ||
387 | static struct snd_pcm_ops psc_dma_pcm_ops = { | 334 | static int |
388 | .open = psc_dma_pcm_open, | 335 | psc_dma_hw_params(struct snd_pcm_substream *substream, |
389 | .close = psc_dma_pcm_close, | 336 | struct snd_pcm_hw_params *params) |
337 | { | ||
338 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
339 | |||
340 | return 0; | ||
341 | } | ||
342 | |||
343 | static struct snd_pcm_ops psc_dma_ops = { | ||
344 | .open = psc_dma_open, | ||
345 | .close = psc_dma_close, | ||
346 | .hw_free = psc_dma_hw_free, | ||
390 | .ioctl = snd_pcm_lib_ioctl, | 347 | .ioctl = snd_pcm_lib_ioctl, |
391 | .pointer = psc_dma_pcm_pointer, | 348 | .pointer = psc_dma_pointer, |
349 | .trigger = psc_dma_trigger, | ||
350 | .hw_params = psc_dma_hw_params, | ||
392 | }; | 351 | }; |
393 | 352 | ||
394 | static u64 psc_dma_pcm_dmamask = 0xffffffff; | 353 | static u64 psc_dma_dmamask = 0xffffffff; |
395 | static int psc_dma_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, | 354 | static int psc_dma_new(struct snd_card *card, struct snd_soc_dai *dai, |
396 | struct snd_pcm *pcm) | 355 | struct snd_pcm *pcm) |
397 | { | 356 | { |
398 | struct snd_soc_pcm_runtime *rtd = pcm->private_data; | 357 | struct snd_soc_pcm_runtime *rtd = pcm->private_data; |
399 | size_t size = psc_dma_pcm_hardware.buffer_bytes_max; | 358 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; |
359 | size_t size = psc_dma_hardware.buffer_bytes_max; | ||
400 | int rc = 0; | 360 | int rc = 0; |
401 | 361 | ||
402 | dev_dbg(rtd->socdev->dev, "psc_dma_pcm_new(card=%p, dai=%p, pcm=%p)\n", | 362 | dev_dbg(rtd->socdev->dev, "psc_dma_new(card=%p, dai=%p, pcm=%p)\n", |
403 | card, dai, pcm); | 363 | card, dai, pcm); |
404 | 364 | ||
405 | if (!card->dev->dma_mask) | 365 | if (!card->dev->dma_mask) |
406 | card->dev->dma_mask = &psc_dma_pcm_dmamask; | 366 | card->dev->dma_mask = &psc_dma_dmamask; |
407 | if (!card->dev->coherent_dma_mask) | 367 | if (!card->dev->coherent_dma_mask) |
408 | card->dev->coherent_dma_mask = 0xffffffff; | 368 | card->dev->coherent_dma_mask = 0xffffffff; |
409 | 369 | ||
410 | if (pcm->streams[0].substream) { | 370 | if (pcm->streams[0].substream) { |
411 | rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, | 371 | rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, |
412 | &pcm->streams[0].substream->dma_buffer); | 372 | size, &pcm->streams[0].substream->dma_buffer); |
413 | if (rc) | 373 | if (rc) |
414 | goto playback_alloc_err; | 374 | goto playback_alloc_err; |
415 | } | 375 | } |
416 | 376 | ||
417 | if (pcm->streams[1].substream) { | 377 | if (pcm->streams[1].substream) { |
418 | rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, | 378 | rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, |
419 | &pcm->streams[1].substream->dma_buffer); | 379 | size, &pcm->streams[1].substream->dma_buffer); |
420 | if (rc) | 380 | if (rc) |
421 | goto capture_alloc_err; | 381 | goto capture_alloc_err; |
422 | } | 382 | } |
423 | 383 | ||
384 | if (rtd->socdev->card->codec->ac97) | ||
385 | rtd->socdev->card->codec->ac97->private_data = psc_dma; | ||
386 | |||
424 | return 0; | 387 | return 0; |
425 | 388 | ||
426 | capture_alloc_err: | 389 | capture_alloc_err: |
427 | if (pcm->streams[0].substream) | 390 | if (pcm->streams[0].substream) |
428 | snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); | 391 | snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); |
392 | |||
429 | playback_alloc_err: | 393 | playback_alloc_err: |
430 | dev_err(card->dev, "Cannot allocate buffer(s)\n"); | 394 | dev_err(card->dev, "Cannot allocate buffer(s)\n"); |
395 | |||
431 | return -ENOMEM; | 396 | return -ENOMEM; |
432 | } | 397 | } |
433 | 398 | ||
434 | static void psc_dma_pcm_free(struct snd_pcm *pcm) | 399 | static void psc_dma_free(struct snd_pcm *pcm) |
435 | { | 400 | { |
436 | struct snd_soc_pcm_runtime *rtd = pcm->private_data; | 401 | struct snd_soc_pcm_runtime *rtd = pcm->private_data; |
437 | struct snd_pcm_substream *substream; | 402 | struct snd_pcm_substream *substream; |
438 | int stream; | 403 | int stream; |
439 | 404 | ||
440 | dev_dbg(rtd->socdev->dev, "psc_dma_pcm_free(pcm=%p)\n", pcm); | 405 | dev_dbg(rtd->socdev->dev, "psc_dma_free(pcm=%p)\n", pcm); |
441 | 406 | ||
442 | for (stream = 0; stream < 2; stream++) { | 407 | for (stream = 0; stream < 2; stream++) { |
443 | substream = pcm->streams[stream].substream; | 408 | substream = pcm->streams[stream].substream; |
@@ -449,10 +414,151 @@ static void psc_dma_pcm_free(struct snd_pcm *pcm) | |||
449 | } | 414 | } |
450 | } | 415 | } |
451 | 416 | ||
452 | struct snd_soc_platform psc_dma_pcm_soc_platform = { | 417 | struct snd_soc_platform mpc5200_audio_dma_platform = { |
453 | .name = "mpc5200-psc-audio", | 418 | .name = "mpc5200-psc-audio", |
454 | .pcm_ops = &psc_dma_pcm_ops, | 419 | .pcm_ops = &psc_dma_ops, |
455 | .pcm_new = &psc_dma_pcm_new, | 420 | .pcm_new = &psc_dma_new, |
456 | .pcm_free = &psc_dma_pcm_free, | 421 | .pcm_free = &psc_dma_free, |
457 | }; | 422 | }; |
423 | EXPORT_SYMBOL_GPL(mpc5200_audio_dma_platform); | ||
424 | |||
425 | int mpc5200_audio_dma_create(struct of_device *op) | ||
426 | { | ||
427 | phys_addr_t fifo; | ||
428 | struct psc_dma *psc_dma; | ||
429 | struct resource res; | ||
430 | int size, irq, rc; | ||
431 | const __be32 *prop; | ||
432 | void __iomem *regs; | ||
433 | |||
434 | /* Fetch the registers and IRQ of the PSC */ | ||
435 | irq = irq_of_parse_and_map(op->node, 0); | ||
436 | if (of_address_to_resource(op->node, 0, &res)) { | ||
437 | dev_err(&op->dev, "Missing reg property\n"); | ||
438 | return -ENODEV; | ||
439 | } | ||
440 | regs = ioremap(res.start, 1 + res.end - res.start); | ||
441 | if (!regs) { | ||
442 | dev_err(&op->dev, "Could not map registers\n"); | ||
443 | return -ENODEV; | ||
444 | } | ||
445 | |||
446 | /* Allocate and initialize the driver private data */ | ||
447 | psc_dma = kzalloc(sizeof *psc_dma, GFP_KERNEL); | ||
448 | if (!psc_dma) { | ||
449 | iounmap(regs); | ||
450 | return -ENOMEM; | ||
451 | } | ||
452 | |||
453 | /* Get the PSC ID */ | ||
454 | prop = of_get_property(op->node, "cell-index", &size); | ||
455 | if (!prop || size < sizeof *prop) | ||
456 | return -ENODEV; | ||
457 | |||
458 | spin_lock_init(&psc_dma->lock); | ||
459 | psc_dma->id = be32_to_cpu(*prop); | ||
460 | psc_dma->irq = irq; | ||
461 | psc_dma->psc_regs = regs; | ||
462 | psc_dma->fifo_regs = regs + sizeof *psc_dma->psc_regs; | ||
463 | psc_dma->dev = &op->dev; | ||
464 | psc_dma->playback.psc_dma = psc_dma; | ||
465 | psc_dma->capture.psc_dma = psc_dma; | ||
466 | snprintf(psc_dma->name, sizeof psc_dma->name, "PSC%u", psc_dma->id); | ||
467 | |||
468 | /* Find the address of the fifo data registers and setup the | ||
469 | * DMA tasks */ | ||
470 | fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32); | ||
471 | psc_dma->capture.bcom_task = | ||
472 | bcom_psc_gen_bd_rx_init(psc_dma->id, 10, fifo, 512); | ||
473 | psc_dma->playback.bcom_task = | ||
474 | bcom_psc_gen_bd_tx_init(psc_dma->id, 10, fifo); | ||
475 | if (!psc_dma->capture.bcom_task || | ||
476 | !psc_dma->playback.bcom_task) { | ||
477 | dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); | ||
478 | iounmap(regs); | ||
479 | kfree(psc_dma); | ||
480 | return -ENODEV; | ||
481 | } | ||
482 | |||
483 | /* Disable all interrupts and reset the PSC */ | ||
484 | out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); | ||
485 | /* reset receiver */ | ||
486 | out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_RX); | ||
487 | /* reset transmitter */ | ||
488 | out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_TX); | ||
489 | /* reset error */ | ||
490 | out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_ERR_STAT); | ||
491 | /* reset mode */ | ||
492 | out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_SEL_MODE_REG_1); | ||
493 | |||
494 | /* Set up mode register; | ||
495 | * First write: RxRdy (FIFO Alarm) generates rx FIFO irq | ||
496 | * Second write: register Normal mode for non loopback | ||
497 | */ | ||
498 | out_8(&psc_dma->psc_regs->mode, 0); | ||
499 | out_8(&psc_dma->psc_regs->mode, 0); | ||
500 | |||
501 | /* Set the TX and RX fifo alarm thresholds */ | ||
502 | out_be16(&psc_dma->fifo_regs->rfalarm, 0x100); | ||
503 | out_8(&psc_dma->fifo_regs->rfcntl, 0x4); | ||
504 | out_be16(&psc_dma->fifo_regs->tfalarm, 0x100); | ||
505 | out_8(&psc_dma->fifo_regs->tfcntl, 0x7); | ||
506 | |||
507 | /* Lookup the IRQ numbers */ | ||
508 | psc_dma->playback.irq = | ||
509 | bcom_get_task_irq(psc_dma->playback.bcom_task); | ||
510 | psc_dma->capture.irq = | ||
511 | bcom_get_task_irq(psc_dma->capture.bcom_task); | ||
512 | |||
513 | rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, | ||
514 | "psc-dma-status", psc_dma); | ||
515 | rc |= request_irq(psc_dma->capture.irq, | ||
516 | &psc_dma_bcom_irq_rx, IRQF_SHARED, | ||
517 | "psc-dma-capture", &psc_dma->capture); | ||
518 | rc |= request_irq(psc_dma->playback.irq, | ||
519 | &psc_dma_bcom_irq_tx, IRQF_SHARED, | ||
520 | "psc-dma-playback", &psc_dma->playback); | ||
521 | if (rc) { | ||
522 | free_irq(psc_dma->irq, psc_dma); | ||
523 | free_irq(psc_dma->capture.irq, | ||
524 | &psc_dma->capture); | ||
525 | free_irq(psc_dma->playback.irq, | ||
526 | &psc_dma->playback); | ||
527 | return -ENODEV; | ||
528 | } | ||
458 | 529 | ||
530 | /* Save what we've done so it can be found again later */ | ||
531 | dev_set_drvdata(&op->dev, psc_dma); | ||
532 | |||
533 | /* Tell the ASoC OF helpers about it */ | ||
534 | return snd_soc_register_platform(&mpc5200_audio_dma_platform); | ||
535 | } | ||
536 | EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create); | ||
537 | |||
538 | int mpc5200_audio_dma_destroy(struct of_device *op) | ||
539 | { | ||
540 | struct psc_dma *psc_dma = dev_get_drvdata(&op->dev); | ||
541 | |||
542 | dev_dbg(&op->dev, "mpc5200_audio_dma_destroy()\n"); | ||
543 | |||
544 | snd_soc_unregister_platform(&mpc5200_audio_dma_platform); | ||
545 | |||
546 | bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); | ||
547 | bcom_gen_bd_tx_release(psc_dma->playback.bcom_task); | ||
548 | |||
549 | /* Release irqs */ | ||
550 | free_irq(psc_dma->irq, psc_dma); | ||
551 | free_irq(psc_dma->capture.irq, &psc_dma->capture); | ||
552 | free_irq(psc_dma->playback.irq, &psc_dma->playback); | ||
553 | |||
554 | iounmap(psc_dma->psc_regs); | ||
555 | kfree(psc_dma); | ||
556 | dev_set_drvdata(&op->dev, NULL); | ||
557 | |||
558 | return 0; | ||
559 | } | ||
560 | EXPORT_SYMBOL_GPL(mpc5200_audio_dma_destroy); | ||
561 | |||
562 | MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); | ||
563 | MODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver"); | ||
564 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h index a33232c0c59e..2000803f06a7 100644 --- a/sound/soc/fsl/mpc5200_dma.h +++ b/sound/soc/fsl/mpc5200_dma.h | |||
@@ -5,8 +5,10 @@ | |||
5 | #ifndef __SOUND_SOC_FSL_MPC5200_DMA_H__ | 5 | #ifndef __SOUND_SOC_FSL_MPC5200_DMA_H__ |
6 | #define __SOUND_SOC_FSL_MPC5200_DMA_H__ | 6 | #define __SOUND_SOC_FSL_MPC5200_DMA_H__ |
7 | 7 | ||
8 | #define PSC_STREAM_NAME_LEN 32 | ||
9 | |||
8 | /** | 10 | /** |
9 | * psc_dma_stream - Data specific to a single stream (playback or capture) | 11 | * psc_ac97_stream - Data specific to a single stream (playback or capture) |
10 | * @active: flag indicating if the stream is active | 12 | * @active: flag indicating if the stream is active |
11 | * @psc_dma: pointer back to parent psc_dma data structure | 13 | * @psc_dma: pointer back to parent psc_dma data structure |
12 | * @bcom_task: bestcomm task structure | 14 | * @bcom_task: bestcomm task structure |
@@ -17,6 +19,9 @@ | |||
17 | * @period_bytes: size of DMA period in bytes | 19 | * @period_bytes: size of DMA period in bytes |
18 | */ | 20 | */ |
19 | struct psc_dma_stream { | 21 | struct psc_dma_stream { |
22 | struct snd_pcm_runtime *runtime; | ||
23 | snd_pcm_uframes_t appl_ptr; | ||
24 | |||
20 | int active; | 25 | int active; |
21 | struct psc_dma *psc_dma; | 26 | struct psc_dma *psc_dma; |
22 | struct bcom_task *bcom_task; | 27 | struct bcom_task *bcom_task; |
@@ -27,6 +32,7 @@ struct psc_dma_stream { | |||
27 | dma_addr_t period_next_pt; | 32 | dma_addr_t period_next_pt; |
28 | dma_addr_t period_current_pt; | 33 | dma_addr_t period_current_pt; |
29 | int period_bytes; | 34 | int period_bytes; |
35 | int period_size; | ||
30 | }; | 36 | }; |
31 | 37 | ||
32 | /** | 38 | /** |
@@ -48,9 +54,12 @@ struct psc_dma { | |||
48 | struct mpc52xx_psc_fifo __iomem *fifo_regs; | 54 | struct mpc52xx_psc_fifo __iomem *fifo_regs; |
49 | unsigned int irq; | 55 | unsigned int irq; |
50 | struct device *dev; | 56 | struct device *dev; |
51 | struct snd_soc_dai dai; | ||
52 | spinlock_t lock; | 57 | spinlock_t lock; |
53 | u32 sicr; | 58 | u32 sicr; |
59 | uint sysclk; | ||
60 | int imr; | ||
61 | int id; | ||
62 | unsigned int slots; | ||
54 | 63 | ||
55 | /* per-stream data */ | 64 | /* per-stream data */ |
56 | struct psc_dma_stream playback; | 65 | struct psc_dma_stream playback; |
@@ -58,24 +67,14 @@ struct psc_dma { | |||
58 | 67 | ||
59 | /* Statistics */ | 68 | /* Statistics */ |
60 | struct { | 69 | struct { |
61 | int overrun_count; | 70 | unsigned long overrun_count; |
62 | int underrun_count; | 71 | unsigned long underrun_count; |
63 | } stats; | 72 | } stats; |
64 | }; | 73 | }; |
65 | 74 | ||
75 | int mpc5200_audio_dma_create(struct of_device *op); | ||
76 | int mpc5200_audio_dma_destroy(struct of_device *op); | ||
66 | 77 | ||
67 | int psc_dma_startup(struct snd_pcm_substream *substream, | 78 | extern struct snd_soc_platform mpc5200_audio_dma_platform; |
68 | struct snd_soc_dai *dai); | ||
69 | |||
70 | int psc_dma_hw_free(struct snd_pcm_substream *substream, | ||
71 | struct snd_soc_dai *dai); | ||
72 | |||
73 | void psc_dma_shutdown(struct snd_pcm_substream *substream, | ||
74 | struct snd_soc_dai *dai); | ||
75 | |||
76 | int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd, | ||
77 | struct snd_soc_dai *dai); | ||
78 | |||
79 | extern struct snd_soc_platform psc_dma_pcm_soc_platform; | ||
80 | 79 | ||
81 | #endif /* __SOUND_SOC_FSL_MPC5200_DMA_H__ */ | 80 | #endif /* __SOUND_SOC_FSL_MPC5200_DMA_H__ */ |
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 12a7917b57b0..ce8de90fb94a 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c | |||
@@ -3,34 +3,22 @@ | |||
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 | ||
19 | #include "mpc5200_psc_i2s.h" | ||
28 | #include "mpc5200_dma.h" | 20 | #include "mpc5200_dma.h" |
29 | 21 | ||
30 | MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); | ||
31 | MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver"); | ||
32 | MODULE_LICENSE("GPL"); | ||
33 | |||
34 | /** | 22 | /** |
35 | * PSC_I2S_RATES: sample rates supported by the I2S | 23 | * PSC_I2S_RATES: sample rates supported by the I2S |
36 | * | 24 | * |
@@ -46,8 +34,7 @@ MODULE_LICENSE("GPL"); | |||
46 | * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode | 34 | * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode |
47 | */ | 35 | */ |
48 | #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 | \ |
49 | SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE | \ | 37 | SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE) |
50 | SNDRV_PCM_FMTBIT_S32_BE) | ||
51 | 38 | ||
52 | static int psc_i2s_hw_params(struct snd_pcm_substream *substream, | 39 | static int psc_i2s_hw_params(struct snd_pcm_substream *substream, |
53 | struct snd_pcm_hw_params *params, | 40 | struct snd_pcm_hw_params *params, |
@@ -82,8 +69,6 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, | |||
82 | } | 69 | } |
83 | out_be32(&psc_dma->psc_regs->sicr, psc_dma->sicr | mode); | 70 | out_be32(&psc_dma->psc_regs->sicr, psc_dma->sicr | mode); |
84 | 71 | ||
85 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
86 | |||
87 | return 0; | 72 | return 0; |
88 | } | 73 | } |
89 | 74 | ||
@@ -140,16 +125,13 @@ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) | |||
140 | * psc_i2s_dai_template: template CPU Digital Audio Interface | 125 | * psc_i2s_dai_template: template CPU Digital Audio Interface |
141 | */ | 126 | */ |
142 | static struct snd_soc_dai_ops psc_i2s_dai_ops = { | 127 | static struct snd_soc_dai_ops psc_i2s_dai_ops = { |
143 | .startup = psc_dma_startup, | ||
144 | .hw_params = psc_i2s_hw_params, | 128 | .hw_params = psc_i2s_hw_params, |
145 | .hw_free = psc_dma_hw_free, | ||
146 | .shutdown = psc_dma_shutdown, | ||
147 | .trigger = psc_dma_trigger, | ||
148 | .set_sysclk = psc_i2s_set_sysclk, | 129 | .set_sysclk = psc_i2s_set_sysclk, |
149 | .set_fmt = psc_i2s_set_fmt, | 130 | .set_fmt = psc_i2s_set_fmt, |
150 | }; | 131 | }; |
151 | 132 | ||
152 | static struct snd_soc_dai psc_i2s_dai_template = { | 133 | struct snd_soc_dai psc_i2s_dai[] = {{ |
134 | .name = "I2S", | ||
153 | .playback = { | 135 | .playback = { |
154 | .channels_min = 2, | 136 | .channels_min = 2, |
155 | .channels_max = 2, | 137 | .channels_max = 2, |
@@ -163,71 +145,8 @@ static struct snd_soc_dai psc_i2s_dai_template = { | |||
163 | .formats = PSC_I2S_FORMATS, | 145 | .formats = PSC_I2S_FORMATS, |
164 | }, | 146 | }, |
165 | .ops = &psc_i2s_dai_ops, | 147 | .ops = &psc_i2s_dai_ops, |
166 | }; | 148 | } }; |
167 | 149 | EXPORT_SYMBOL_GPL(psc_i2s_dai); | |
168 | /* --------------------------------------------------------------------- | ||
169 | * Sysfs attributes for debugging | ||
170 | */ | ||
171 | |||
172 | static ssize_t psc_i2s_status_show(struct device *dev, | ||
173 | struct device_attribute *attr, char *buf) | ||
174 | { | ||
175 | struct psc_dma *psc_dma = dev_get_drvdata(dev); | ||
176 | |||
177 | return sprintf(buf, "status=%.4x sicr=%.8x rfnum=%i rfstat=0x%.4x " | ||
178 | "tfnum=%i tfstat=0x%.4x\n", | ||
179 | in_be16(&psc_dma->psc_regs->sr_csr.status), | ||
180 | in_be32(&psc_dma->psc_regs->sicr), | ||
181 | in_be16(&psc_dma->fifo_regs->rfnum) & 0x1ff, | ||
182 | in_be16(&psc_dma->fifo_regs->rfstat), | ||
183 | in_be16(&psc_dma->fifo_regs->tfnum) & 0x1ff, | ||
184 | in_be16(&psc_dma->fifo_regs->tfstat)); | ||
185 | } | ||
186 | |||
187 | static int *psc_i2s_get_stat_attr(struct psc_dma *psc_dma, const char *name) | ||
188 | { | ||
189 | if (strcmp(name, "playback_underrun") == 0) | ||
190 | return &psc_dma->stats.underrun_count; | ||
191 | if (strcmp(name, "capture_overrun") == 0) | ||
192 | return &psc_dma->stats.overrun_count; | ||
193 | |||
194 | return NULL; | ||
195 | } | ||
196 | |||
197 | static ssize_t psc_i2s_stat_show(struct device *dev, | ||
198 | struct device_attribute *attr, char *buf) | ||
199 | { | ||
200 | struct psc_dma *psc_dma = dev_get_drvdata(dev); | ||
201 | int *attrib; | ||
202 | |||
203 | attrib = psc_i2s_get_stat_attr(psc_dma, attr->attr.name); | ||
204 | if (!attrib) | ||
205 | return 0; | ||
206 | |||
207 | return sprintf(buf, "%i\n", *attrib); | ||
208 | } | ||
209 | |||
210 | static ssize_t psc_i2s_stat_store(struct device *dev, | ||
211 | struct device_attribute *attr, | ||
212 | const char *buf, | ||
213 | size_t count) | ||
214 | { | ||
215 | struct psc_dma *psc_dma = dev_get_drvdata(dev); | ||
216 | int *attrib; | ||
217 | |||
218 | attrib = psc_i2s_get_stat_attr(psc_dma, attr->attr.name); | ||
219 | if (!attrib) | ||
220 | return 0; | ||
221 | |||
222 | *attrib = simple_strtoul(buf, NULL, 0); | ||
223 | return count; | ||
224 | } | ||
225 | |||
226 | static DEVICE_ATTR(status, 0644, psc_i2s_status_show, NULL); | ||
227 | static DEVICE_ATTR(playback_underrun, 0644, psc_i2s_stat_show, | ||
228 | psc_i2s_stat_store); | ||
229 | static DEVICE_ATTR(capture_overrun, 0644, psc_i2s_stat_show, | ||
230 | psc_i2s_stat_store); | ||
231 | 150 | ||
232 | /* --------------------------------------------------------------------- | 151 | /* --------------------------------------------------------------------- |
233 | * OF platform bus binding code: | 152 | * OF platform bus binding code: |
@@ -237,82 +156,26 @@ static DEVICE_ATTR(capture_overrun, 0644, psc_i2s_stat_show, | |||
237 | static int __devinit psc_i2s_of_probe(struct of_device *op, | 156 | static int __devinit psc_i2s_of_probe(struct of_device *op, |
238 | const struct of_device_id *match) | 157 | const struct of_device_id *match) |
239 | { | 158 | { |
240 | phys_addr_t fifo; | 159 | int rc; |
241 | struct psc_dma *psc_dma; | 160 | struct psc_dma *psc_dma; |
242 | struct resource res; | 161 | struct mpc52xx_psc __iomem *regs; |
243 | int size, psc_id, irq, rc; | ||
244 | const __be32 *prop; | ||
245 | void __iomem *regs; | ||
246 | |||
247 | dev_dbg(&op->dev, "probing psc i2s device\n"); | ||
248 | |||
249 | /* Get the PSC ID */ | ||
250 | prop = of_get_property(op->node, "cell-index", &size); | ||
251 | if (!prop || size < sizeof *prop) | ||
252 | return -ENODEV; | ||
253 | psc_id = be32_to_cpu(*prop); | ||
254 | |||
255 | /* Fetch the registers and IRQ of the PSC */ | ||
256 | irq = irq_of_parse_and_map(op->node, 0); | ||
257 | if (of_address_to_resource(op->node, 0, &res)) { | ||
258 | dev_err(&op->dev, "Missing reg property\n"); | ||
259 | return -ENODEV; | ||
260 | } | ||
261 | regs = ioremap(res.start, 1 + res.end - res.start); | ||
262 | if (!regs) { | ||
263 | dev_err(&op->dev, "Could not map registers\n"); | ||
264 | return -ENODEV; | ||
265 | } | ||
266 | 162 | ||
267 | /* Allocate and initialize the driver private data */ | 163 | rc = mpc5200_audio_dma_create(op); |
268 | psc_dma = kzalloc(sizeof *psc_dma, GFP_KERNEL); | 164 | if (rc != 0) |
269 | if (!psc_dma) { | 165 | return rc; |
270 | iounmap(regs); | 166 | |
271 | return -ENOMEM; | 167 | rc = snd_soc_register_dais(psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai)); |
272 | } | 168 | if (rc != 0) { |
273 | spin_lock_init(&psc_dma->lock); | 169 | pr_err("Failed to register DAI\n"); |
274 | psc_dma->irq = irq; | 170 | return 0; |
275 | psc_dma->psc_regs = regs; | ||
276 | psc_dma->fifo_regs = regs + sizeof *psc_dma->psc_regs; | ||
277 | psc_dma->dev = &op->dev; | ||
278 | psc_dma->playback.psc_dma = psc_dma; | ||
279 | psc_dma->capture.psc_dma = psc_dma; | ||
280 | snprintf(psc_dma->name, sizeof psc_dma->name, "PSC%u", psc_id+1); | ||
281 | |||
282 | /* Fill out the CPU DAI structure */ | ||
283 | memcpy(&psc_dma->dai, &psc_i2s_dai_template, sizeof psc_dma->dai); | ||
284 | psc_dma->dai.private_data = psc_dma; | ||
285 | psc_dma->dai.name = psc_dma->name; | ||
286 | psc_dma->dai.id = psc_id; | ||
287 | |||
288 | /* Find the address of the fifo data registers and setup the | ||
289 | * DMA tasks */ | ||
290 | fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32); | ||
291 | psc_dma->capture.bcom_task = | ||
292 | bcom_psc_gen_bd_rx_init(psc_id, 10, fifo, 512); | ||
293 | psc_dma->playback.bcom_task = | ||
294 | bcom_psc_gen_bd_tx_init(psc_id, 10, fifo); | ||
295 | if (!psc_dma->capture.bcom_task || | ||
296 | !psc_dma->playback.bcom_task) { | ||
297 | dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); | ||
298 | iounmap(regs); | ||
299 | kfree(psc_dma); | ||
300 | return -ENODEV; | ||
301 | } | 171 | } |
302 | 172 | ||
303 | /* Disable all interrupts and reset the PSC */ | 173 | psc_dma = dev_get_drvdata(&op->dev); |
304 | out_be16(&psc_dma->psc_regs->isr_imr.imr, 0); | 174 | regs = psc_dma->psc_regs; |
305 | out_8(&psc_dma->psc_regs->command, 3 << 4); /* reset transmitter */ | ||
306 | out_8(&psc_dma->psc_regs->command, 2 << 4); /* reset receiver */ | ||
307 | out_8(&psc_dma->psc_regs->command, 1 << 4); /* reset mode */ | ||
308 | out_8(&psc_dma->psc_regs->command, 4 << 4); /* reset error */ | ||
309 | 175 | ||
310 | /* Configure the serial interface mode; defaulting to CODEC8 mode */ | 176 | /* Configure the serial interface mode; defaulting to CODEC8 mode */ |
311 | psc_dma->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S | | 177 | psc_dma->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S | |
312 | MPC52xx_PSC_SICR_CLKPOL; | 178 | MPC52xx_PSC_SICR_CLKPOL; |
313 | if (of_get_property(op->node, "fsl,cellslave", NULL)) | ||
314 | psc_dma->sicr |= MPC52xx_PSC_SICR_CELLSLAVE | | ||
315 | MPC52xx_PSC_SICR_GENCLK; | ||
316 | out_be32(&psc_dma->psc_regs->sicr, | 179 | out_be32(&psc_dma->psc_regs->sicr, |
317 | psc_dma->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8); | 180 | psc_dma->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8); |
318 | 181 | ||
@@ -321,66 +184,37 @@ static int __devinit psc_i2s_of_probe(struct of_device *op, | |||
321 | if (!of_get_property(op->node, "codec-handle", NULL)) | 184 | if (!of_get_property(op->node, "codec-handle", NULL)) |
322 | return 0; | 185 | return 0; |
323 | 186 | ||
324 | /* Set up mode register; | 187 | /* Due to errata in the dma mode; need to line up enabling |
325 | * First write: RxRdy (FIFO Alarm) generates rx FIFO irq | 188 | * the transmitter with a transition on the frame sync |
326 | * Second write: register Normal mode for non loopback | 189 | * line */ |
327 | */ | 190 | |
328 | out_8(&psc_dma->psc_regs->mode, 0); | 191 | /* first make sure it is low */ |
329 | out_8(&psc_dma->psc_regs->mode, 0); | 192 | while ((in_8(®s->ipcr_acr.ipcr) & 0x80) != 0) |
330 | 193 | ; | |
331 | /* Set the TX and RX fifo alarm thresholds */ | 194 | /* then wait for the transition to high */ |
332 | out_be16(&psc_dma->fifo_regs->rfalarm, 0x100); | 195 | while ((in_8(®s->ipcr_acr.ipcr) & 0x80) == 0) |
333 | out_8(&psc_dma->fifo_regs->rfcntl, 0x4); | 196 | ; |
334 | out_be16(&psc_dma->fifo_regs->tfalarm, 0x100); | 197 | /* Finally, enable the PSC. |
335 | out_8(&psc_dma->fifo_regs->tfcntl, 0x7); | 198 | * Receiver must always be enabled; even when we only want |
336 | 199 | * transmit. (see 15.3.2.3 of MPC5200B User's Guide) */ | |
337 | /* Lookup the IRQ numbers */ | 200 | |
338 | psc_dma->playback.irq = | 201 | /* Go */ |
339 | bcom_get_task_irq(psc_dma->playback.bcom_task); | 202 | out_8(&psc_dma->psc_regs->command, |
340 | psc_dma->capture.irq = | 203 | MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); |
341 | bcom_get_task_irq(psc_dma->capture.bcom_task); | ||
342 | |||
343 | /* Save what we've done so it can be found again later */ | ||
344 | dev_set_drvdata(&op->dev, psc_dma); | ||
345 | |||
346 | /* Register the SYSFS files */ | ||
347 | rc = device_create_file(psc_dma->dev, &dev_attr_status); | ||
348 | rc |= device_create_file(psc_dma->dev, &dev_attr_capture_overrun); | ||
349 | rc |= device_create_file(psc_dma->dev, &dev_attr_playback_underrun); | ||
350 | if (rc) | ||
351 | dev_info(psc_dma->dev, "error creating sysfs files\n"); | ||
352 | |||
353 | snd_soc_register_platform(&psc_dma_pcm_soc_platform); | ||
354 | |||
355 | /* Tell the ASoC OF helpers about it */ | ||
356 | of_snd_soc_register_platform(&psc_dma_pcm_soc_platform, op->node, | ||
357 | &psc_dma->dai); | ||
358 | 204 | ||
359 | return 0; | 205 | return 0; |
206 | |||
360 | } | 207 | } |
361 | 208 | ||
362 | static int __devexit psc_i2s_of_remove(struct of_device *op) | 209 | static int __devexit psc_i2s_of_remove(struct of_device *op) |
363 | { | 210 | { |
364 | struct psc_dma *psc_dma = dev_get_drvdata(&op->dev); | 211 | return mpc5200_audio_dma_destroy(op); |
365 | |||
366 | dev_dbg(&op->dev, "psc_i2s_remove()\n"); | ||
367 | |||
368 | snd_soc_unregister_platform(&psc_dma_pcm_soc_platform); | ||
369 | |||
370 | bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); | ||
371 | bcom_gen_bd_tx_release(psc_dma->playback.bcom_task); | ||
372 | |||
373 | iounmap(psc_dma->psc_regs); | ||
374 | iounmap(psc_dma->fifo_regs); | ||
375 | kfree(psc_dma); | ||
376 | dev_set_drvdata(&op->dev, NULL); | ||
377 | |||
378 | return 0; | ||
379 | } | 212 | } |
380 | 213 | ||
381 | /* Match table for of_platform binding */ | 214 | /* Match table for of_platform binding */ |
382 | static struct of_device_id psc_i2s_match[] __devinitdata = { | 215 | static struct of_device_id psc_i2s_match[] __devinitdata = { |
383 | { .compatible = "fsl,mpc5200-psc-i2s", }, | 216 | { .compatible = "fsl,mpc5200-psc-i2s", }, |
217 | { .compatible = "fsl,mpc5200b-psc-i2s", }, | ||
384 | {} | 218 | {} |
385 | }; | 219 | }; |
386 | MODULE_DEVICE_TABLE(of, psc_i2s_match); | 220 | MODULE_DEVICE_TABLE(of, psc_i2s_match); |
@@ -411,4 +245,7 @@ static void __exit psc_i2s_exit(void) | |||
411 | } | 245 | } |
412 | module_exit(psc_i2s_exit); | 246 | module_exit(psc_i2s_exit); |
413 | 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"); | ||
414 | 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__ */ | ||