Commit | Line | Data |
---|---|---|
0cbbec09 FM |
1 | /* |
2 | * at91rm9200-i2s.c -- ALSA Soc Audio Layer Platform driver and DMA engine | |
3 | * | |
4 | * Author: Frank Mandarino <fmandarino@endrelia.com> | |
5 | * Endrelia Technologies Inc. | |
6 | * | |
7 | * Based on pxa2xx Platform drivers by | |
8 | * Liam Girdwood <liam.girdwood@wolfsonmicro.com> | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License as published by the | |
12 | * Free Software Foundation; either version 2 of the License, or (at your | |
13 | * option) any later version. | |
14 | * | |
15 | * Revision history | |
16 | * 3rd Mar 2006 Initial version. | |
17 | */ | |
18 | ||
19 | #include <linux/init.h> | |
20 | #include <linux/module.h> | |
21 | #include <linux/interrupt.h> | |
22 | #include <linux/device.h> | |
23 | #include <linux/delay.h> | |
24 | #include <linux/clk.h> | |
25 | #include <sound/driver.h> | |
26 | #include <sound/core.h> | |
27 | #include <sound/pcm.h> | |
28 | #include <sound/initval.h> | |
29 | #include <sound/soc.h> | |
30 | ||
31 | #include <asm/arch/at91rm9200.h> | |
32 | #include <asm/arch/at91rm9200_ssc.h> | |
33 | #include <asm/arch/at91rm9200_pdc.h> | |
34 | #include <asm/arch/hardware.h> | |
35 | ||
36 | #include "at91rm9200-pcm.h" | |
37 | ||
38 | #if 0 | |
39 | #define DBG(x...) printk(KERN_DEBUG "at91rm9200-i2s:" x) | |
40 | #else | |
41 | #define DBG(x...) | |
42 | #endif | |
43 | ||
44 | #define AT91RM9200_I2S_DAIFMT \ | |
45 | (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_NB_NF) | |
46 | ||
47 | #define AT91RM9200_I2S_DIR \ | |
48 | (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) | |
49 | ||
50 | /* priv is (SSC_CMR.DIV << 16 | SSC_TCMR.PERIOD ) */ | |
51 | static struct snd_soc_dai_mode at91rm9200_i2s[] = { | |
52 | ||
53 | /* 8k: BCLK = (MCLK/10) = (60MHz/50) = 1.2MHz */ | |
527541f9 | 54 | { |
b3b9c1cb TI |
55 | .fmt = AT91RM9200_I2S_DAIFMT, |
56 | .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, | |
57 | .pcmrate = SNDRV_PCM_RATE_8000, | |
527541f9 LG |
58 | .pcmdir = AT91RM9200_I2S_DIR, |
59 | .flags = SND_SOC_DAI_BFS_DIV, | |
60 | .fs = 1500, | |
61 | .bfs = SND_SOC_FSBD(10), | |
62 | .priv = (25 << 16 | 74), | |
63 | }, | |
0cbbec09 FM |
64 | |
65 | /* 16k: BCLK = (MCLK/3) ~= (60MHz/14) = 4.285714MHz */ | |
b3b9c1cb | 66 | { |
527541f9 LG |
67 | .fmt = AT91RM9200_I2S_DAIFMT, |
68 | .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, | |
69 | .pcmrate = SNDRV_PCM_RATE_16000, | |
70 | .pcmdir = AT91RM9200_I2S_DIR, | |
b3b9c1cb TI |
71 | .flags = SND_SOC_DAI_BFS_DIV, |
72 | .fs = 750, | |
527541f9 | 73 | .bfs = SND_SOC_FSBD(3), |
543a0fbe | 74 | .priv = (7 << 16 | 133), |
527541f9 | 75 | }, |
0cbbec09 | 76 | |
543a0fbe | 77 | /* 32k: BCLK = (MCLK/3) ~= (60MHz/14) = 4.285714MHz */ |
b3b9c1cb | 78 | { |
527541f9 | 79 | .fmt = AT91RM9200_I2S_DAIFMT, |
b3b9c1cb | 80 | .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, |
543a0fbe | 81 | .pcmrate = SNDRV_PCM_RATE_32000, |
527541f9 | 82 | .pcmdir = AT91RM9200_I2S_DIR, |
b3b9c1cb | 83 | .flags = SND_SOC_DAI_BFS_DIV, |
543a0fbe FM |
84 | .fs = 375, |
85 | .bfs = SND_SOC_FSBD(3), | |
86 | .priv = (7 << 16 | 66), | |
527541f9 | 87 | }, |
0cbbec09 | 88 | |
543a0fbe | 89 | /* 48k: BCLK = (MCLK/5) ~= (60MHz/26) = 2.3076923MHz */ |
b3b9c1cb | 90 | { |
527541f9 LG |
91 | .fmt = AT91RM9200_I2S_DAIFMT, |
92 | .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE, | |
b3b9c1cb | 93 | .pcmrate = SNDRV_PCM_RATE_48000, |
527541f9 | 94 | .pcmdir = AT91RM9200_I2S_DIR, |
b3b9c1cb TI |
95 | .flags = SND_SOC_DAI_BFS_DIV, |
96 | .fs = 250, | |
97 | .bfs SND_SOC_FSBD(5), | |
527541f9 LG |
98 | .priv = (13 << 16 | 23), |
99 | }, | |
0cbbec09 FM |
100 | }; |
101 | ||
102 | ||
103 | /* | |
104 | * SSC registers required by the PCM DMA engine. | |
105 | */ | |
106 | static struct at91rm9200_ssc_regs ssc_reg[3] = { | |
107 | { | |
108 | .cr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_SSC_CR), | |
109 | .ier = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_SSC_IER), | |
110 | .idr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_SSC_IDR), | |
111 | }, | |
112 | { | |
113 | .cr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_SSC_CR), | |
114 | .ier = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_SSC_IER), | |
115 | .idr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_SSC_IDR), | |
116 | }, | |
117 | { | |
118 | .cr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_SSC_CR), | |
119 | .ier = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_SSC_IER), | |
120 | .idr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_SSC_IDR), | |
121 | }, | |
122 | }; | |
123 | ||
124 | static struct at91rm9200_pdc_regs pdc_tx_reg[3] = { | |
125 | { | |
126 | .xpr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_TPR), | |
127 | .xcr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_TCR), | |
128 | .xnpr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_TNPR), | |
129 | .xncr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_TNCR), | |
130 | .ptcr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_PTCR), | |
131 | }, | |
132 | { | |
133 | .xpr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_TPR), | |
134 | .xcr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_TCR), | |
135 | .xnpr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_TNPR), | |
136 | .xncr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_TNCR), | |
137 | .ptcr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_PTCR), | |
138 | }, | |
139 | { | |
140 | .xpr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_TPR), | |
141 | .xcr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_TCR), | |
142 | .xnpr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_TNPR), | |
143 | .xncr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_TNCR), | |
144 | .ptcr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_PTCR), | |
145 | }, | |
146 | }; | |
147 | ||
148 | static struct at91rm9200_pdc_regs pdc_rx_reg[3] = { | |
149 | { | |
150 | .xpr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_RPR), | |
151 | .xcr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_RCR), | |
152 | .xnpr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_RNPR), | |
153 | .xncr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_RNCR), | |
154 | .ptcr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_PTCR), | |
155 | }, | |
156 | { | |
157 | .xpr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_RPR), | |
158 | .xcr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_RCR), | |
159 | .xnpr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_RNPR), | |
160 | .xncr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_RNCR), | |
161 | .ptcr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_PTCR), | |
162 | }, | |
163 | { | |
164 | .xpr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_RPR), | |
165 | .xcr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_RCR), | |
166 | .xnpr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_RNPR), | |
167 | .xncr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_RNCR), | |
168 | .ptcr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_PTCR), | |
169 | }, | |
170 | }; | |
171 | ||
172 | /* | |
173 | * SSC & PDC status bits for transmit and receive. | |
174 | */ | |
175 | static struct at91rm9200_ssc_mask ssc_tx_mask = { | |
176 | .ssc_enable = AT91_SSC_TXEN, | |
177 | .ssc_disable = AT91_SSC_TXDIS, | |
178 | .ssc_endx = AT91_SSC_ENDTX, | |
179 | .ssc_endbuf = AT91_SSC_TXBUFE, | |
180 | .pdc_enable = AT91_PDC_TXTEN, | |
181 | .pdc_disable = AT91_PDC_TXTDIS, | |
182 | }; | |
183 | ||
184 | static struct at91rm9200_ssc_mask ssc_rx_mask = { | |
185 | .ssc_enable = AT91_SSC_RXEN, | |
186 | .ssc_disable = AT91_SSC_RXDIS, | |
187 | .ssc_endx = AT91_SSC_ENDRX, | |
188 | .ssc_endbuf = AT91_SSC_RXBUFF, | |
189 | .pdc_enable = AT91_PDC_RXTEN, | |
190 | .pdc_disable = AT91_PDC_RXTDIS, | |
191 | }; | |
192 | ||
193 | /* | |
194 | * A MUTEX is used to protect an SSC initialzed flag which allows | |
195 | * the substream hw_params() call to initialize the SSC only if | |
196 | * there are no other substreams open. If there are other | |
197 | * substreams open, the hw_param() call can only check that | |
198 | * it is using the same format and rate. | |
199 | */ | |
200 | static DECLARE_MUTEX(ssc0_mutex); | |
201 | static DECLARE_MUTEX(ssc1_mutex); | |
202 | static DECLARE_MUTEX(ssc2_mutex); | |
203 | ||
204 | /* | |
205 | * DMA parameters. | |
206 | */ | |
207 | static at91rm9200_pcm_dma_params_t ssc_dma_params[3][2] = { | |
208 | {{ | |
209 | .name = "SSC0/I2S PCM Stereo out", | |
210 | .ssc = &ssc_reg[0], | |
211 | .pdc = &pdc_tx_reg[0], | |
212 | .mask = &ssc_tx_mask, | |
213 | }, | |
214 | { | |
215 | .name = "SSC0/I2S PCM Stereo in", | |
216 | .ssc = &ssc_reg[0], | |
217 | .pdc = &pdc_rx_reg[0], | |
218 | .mask = &ssc_rx_mask, | |
219 | }}, | |
220 | {{ | |
221 | .name = "SSC1/I2S PCM Stereo out", | |
222 | .ssc = &ssc_reg[1], | |
223 | .pdc = &pdc_tx_reg[1], | |
224 | .mask = &ssc_tx_mask, | |
225 | }, | |
226 | { | |
227 | .name = "SSC1/I2S PCM Stereo in", | |
228 | .ssc = &ssc_reg[1], | |
229 | .pdc = &pdc_rx_reg[1], | |
230 | .mask = &ssc_rx_mask, | |
231 | }}, | |
232 | {{ | |
233 | .name = "SSC2/I2S PCM Stereo out", | |
234 | .ssc = &ssc_reg[2], | |
235 | .pdc = &pdc_tx_reg[2], | |
236 | .mask = &ssc_tx_mask, | |
237 | }, | |
238 | { | |
239 | .name = "SSC1/I2S PCM Stereo in", | |
240 | .ssc = &ssc_reg[2], | |
241 | .pdc = &pdc_rx_reg[2], | |
242 | .mask = &ssc_rx_mask, | |
243 | }}, | |
244 | }; | |
245 | ||
246 | ||
247 | struct at91rm9200_ssc_state { | |
248 | u32 ssc_cmr; | |
249 | u32 ssc_rcmr; | |
250 | u32 ssc_rfmr; | |
251 | u32 ssc_tcmr; | |
252 | u32 ssc_tfmr; | |
253 | u32 ssc_sr; | |
254 | u32 ssc_imr; | |
255 | }; | |
256 | ||
257 | static struct at91rm9200_ssc_info { | |
258 | char *name; | |
259 | void __iomem *ssc_base; | |
260 | u32 pid; | |
261 | spinlock_t lock; /* lock for dir_mask */ | |
262 | int dir_mask; /* 0=unused, 1=playback, 2=capture */ | |
263 | struct semaphore *mutex; | |
264 | int initialized; | |
265 | int pcmfmt; | |
266 | int rate; | |
267 | at91rm9200_pcm_dma_params_t *dma_params[2]; | |
268 | struct at91rm9200_ssc_state ssc_state; | |
269 | ||
270 | } ssc_info[3] = { | |
271 | { | |
272 | .name = "ssc0", | |
273 | .ssc_base = (void __iomem *) AT91_VA_BASE_SSC0, | |
274 | .pid = AT91_ID_SSC0, | |
275 | .lock = SPIN_LOCK_UNLOCKED, | |
276 | .dir_mask = 0, | |
277 | .mutex = &ssc0_mutex, | |
278 | .initialized = 0, | |
279 | }, | |
280 | { | |
281 | .name = "ssc1", | |
282 | .ssc_base = (void __iomem *) AT91_VA_BASE_SSC1, | |
283 | .pid = AT91_ID_SSC1, | |
284 | .lock = SPIN_LOCK_UNLOCKED, | |
285 | .dir_mask = 0, | |
286 | .mutex = &ssc1_mutex, | |
287 | .initialized = 0, | |
288 | }, | |
289 | { | |
290 | .name = "ssc2", | |
291 | .ssc_base = (void __iomem *) AT91_VA_BASE_SSC2, | |
292 | .pid = AT91_ID_SSC2, | |
293 | .lock = SPIN_LOCK_UNLOCKED, | |
294 | .dir_mask = 0, | |
295 | .mutex = &ssc2_mutex, | |
296 | .initialized = 0, | |
297 | }, | |
298 | }; | |
299 | ||
300 | ||
0f71e8b9 | 301 | static irqreturn_t at91rm9200_i2s_interrupt(int irq, void *dev_id) |
0cbbec09 FM |
302 | { |
303 | struct at91rm9200_ssc_info *ssc_p = dev_id; | |
304 | at91rm9200_pcm_dma_params_t *dma_params; | |
305 | u32 ssc_sr; | |
306 | int i; | |
307 | ||
308 | ssc_sr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_SR) | |
309 | & at91_ssc_read(ssc_p->ssc_base + AT91_SSC_IMR); | |
310 | ||
311 | /* | |
312 | * Loop through the substreams attached to this SSC. If | |
313 | * a DMA-related interrupt occurred on that substream, call | |
314 | * the DMA interrupt handler function, if one has been | |
315 | * registered in the dma_params structure by the PCM driver. | |
316 | */ | |
317 | for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) { | |
318 | dma_params = ssc_p->dma_params[i]; | |
319 | ||
320 | if (dma_params != NULL && dma_params->dma_intr_handler != NULL && | |
321 | (ssc_sr & | |
322 | (dma_params->mask->ssc_endx | dma_params->mask->ssc_endbuf))) | |
323 | ||
324 | dma_params->dma_intr_handler(ssc_sr, dma_params->substream); | |
325 | } | |
326 | ||
327 | return IRQ_HANDLED; | |
328 | } | |
329 | ||
330 | static int at91rm9200_i2s_startup(struct snd_pcm_substream *substream) | |
331 | { | |
332 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
333 | struct at91rm9200_ssc_info *ssc_p = &ssc_info[rtd->cpu_dai->id]; | |
334 | int dir_mask; | |
335 | ||
336 | DBG("i2s_startup: SSC_SR=0x%08lx\n", | |
337 | at91_ssc_read(ssc_p->ssc_base + AT91_SSC_SR)); | |
338 | dir_mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0x1 : 0x2; | |
339 | ||
340 | spin_lock_irq(&ssc_p->lock); | |
341 | if (ssc_p->dir_mask & dir_mask) { | |
342 | spin_unlock_irq(&ssc_p->lock); | |
343 | return -EBUSY; | |
344 | } | |
345 | ssc_p->dir_mask |= dir_mask; | |
346 | spin_unlock_irq(&ssc_p->lock); | |
347 | ||
348 | return 0; | |
349 | } | |
350 | ||
351 | static void at91rm9200_i2s_shutdown(struct snd_pcm_substream *substream) | |
352 | { | |
353 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
354 | struct at91rm9200_ssc_info *ssc_p = &ssc_info[rtd->cpu_dai->id]; | |
355 | at91rm9200_pcm_dma_params_t *dma_params = rtd->cpu_dai->dma_data; | |
356 | int dir, dir_mask; | |
357 | ||
358 | dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; | |
359 | ||
360 | if (dma_params != NULL) { | |
361 | at91_ssc_write(dma_params->ssc->cr, dma_params->mask->ssc_disable); | |
362 | DBG("%s disabled SSC_SR=0x%08lx\n", (dir ? "receive" : "transmit"), | |
363 | at91_ssc_read(ssc_p->ssc_base + AT91_SSC_SR)); | |
364 | ||
365 | dma_params->substream = NULL; | |
366 | ssc_p->dma_params[dir] = NULL; | |
367 | } | |
368 | ||
369 | dir_mask = 1 << dir; | |
370 | ||
371 | spin_lock_irq(&ssc_p->lock); | |
372 | ssc_p->dir_mask &= ~dir_mask; | |
373 | if (!ssc_p->dir_mask) { | |
374 | /* Shutdown the SSC clock. */ | |
375 | DBG("Stopping pid %d clock\n", ssc_p->pid); | |
219e281f | 376 | at91_sys_write(AT91_PMC_PCDR, 1<<ssc_p->pid); |
0cbbec09 FM |
377 | |
378 | if (ssc_p->initialized) | |
379 | free_irq(ssc_p->pid, ssc_p); | |
380 | ||
381 | /* Reset the SSC */ | |
382 | at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CR, AT91_SSC_SWRST); | |
383 | ||
384 | /* Force a re-init on the next hw_params() call. */ | |
385 | ssc_p->initialized = 0; | |
386 | } | |
387 | spin_unlock_irq(&ssc_p->lock); | |
388 | } | |
389 | ||
390 | #ifdef CONFIG_PM | |
391 | static int at91rm9200_i2s_suspend(struct platform_device *pdev, | |
392 | struct snd_soc_cpu_dai *dai) | |
393 | { | |
394 | struct at91rm9200_ssc_info *ssc_p; | |
395 | ||
396 | if(!dai->active) | |
397 | return 0; | |
398 | ||
399 | ssc_p = &ssc_info[dai->id]; | |
400 | ||
401 | /* Save the status register before disabling transmit and receive. */ | |
402 | ssc_p->state->ssc_sr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_SR); | |
403 | at91_ssc_write(ssc_p->ssc_base + | |
404 | AT91_SSC_CR, AT91_SSC_TXDIS | AT91_SSC_RXDIS); | |
405 | ||
406 | /* Save the current interrupt mask, then disable unmasked interrupts. */ | |
407 | ssc_p->state->ssc_imr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_IMR); | |
408 | at91_ssc_write(ssc_p->ssc_base + AT91_SSC_IDR, ssc_p->state->ssc_imr); | |
409 | ||
410 | ssc_p->state->ssc_cmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_CMR); | |
411 | ssc_p->state->ssc_rcmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_RCMR); | |
412 | ssc_p->state->ssc_rfmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_RCMR); | |
413 | ssc_p->state->ssc_tcmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_RCMR); | |
414 | ssc_p->state->ssc_tfmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_RCMR); | |
415 | ||
416 | return 0; | |
417 | } | |
418 | ||
419 | static int at91rm9200_i2s_resume(struct platform_device *pdev, | |
420 | struct snd_soc_cpu_dai *dai) | |
421 | { | |
422 | struct at91rm9200_ssc_info *ssc_p; | |
423 | u32 cr_mask; | |
424 | ||
425 | if(!dai->active) | |
426 | return 0; | |
427 | ||
428 | ssc_p = &ssc_info[dai->id]; | |
429 | ||
430 | at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, ssc_p->state->ssc_tfmr); | |
431 | at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, ssc_p->state->ssc_tcmr); | |
432 | at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, ssc_p->state->ssc_rfmr); | |
433 | at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, ssc_p->state->ssc_rcmr); | |
434 | at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CMR, ssc_p->state->ssc_cmr); | |
435 | ||
436 | at91_ssc_write(ssc_p->ssc_base + AT91_SSC_IER, ssc_p->state->ssc_imr); | |
437 | ||
438 | at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CR, | |
439 | ((ssc_p->state->ssc_sr & AT91_SSC_RXENA) ? AT91_SSC_RXEN : 0) | | |
440 | ((ssc_p->state->ssc_sr & AT91_SSC_TXENA) ? AT91_SSC_TXEN : 0)); | |
441 | ||
442 | return 0; | |
443 | } | |
444 | ||
445 | #else | |
446 | #define at91rm9200_i2s_suspend NULL | |
447 | #define at91rm9200_i2s_resume NULL | |
448 | #endif | |
449 | ||
450 | static unsigned int at91rm9200_i2s_config_sysclk( | |
451 | struct snd_soc_cpu_dai *iface, struct snd_soc_clock_info *info, | |
452 | unsigned int clk) | |
453 | { | |
454 | /* Currently, there is only support for USB (12Mhz) mode */ | |
455 | if (clk != 12000000) | |
456 | return 0; | |
457 | return 12000000; | |
458 | } | |
459 | ||
460 | static int at91rm9200_i2s_hw_params(struct snd_pcm_substream *substream, | |
461 | struct snd_pcm_hw_params *params) | |
462 | { | |
463 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
464 | int id = rtd->cpu_dai->id; | |
465 | struct at91rm9200_ssc_info *ssc_p = &ssc_info[id]; | |
466 | at91rm9200_pcm_dma_params_t *dma_params; | |
467 | unsigned int pcmfmt, rate; | |
468 | int dir, channels, bits; | |
469 | struct clk *mck_clk; | |
470 | unsigned long bclk; | |
471 | u32 div, period, tfmr, rfmr, tcmr, rcmr; | |
472 | int ret; | |
473 | ||
474 | /* | |
475 | * Currently, there is only one set of dma params for | |
476 | * each direction. If more are added, this code will | |
477 | * have to be changed to select the proper set. | |
478 | */ | |
479 | dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; | |
480 | ||
481 | dma_params = &ssc_dma_params[id][dir]; | |
482 | dma_params->substream = substream; | |
483 | ||
484 | ssc_p->dma_params[dir] = dma_params; | |
485 | rtd->cpu_dai->dma_data = dma_params; | |
486 | ||
487 | rate = params_rate(params); | |
488 | channels = params_channels(params); | |
489 | ||
490 | pcmfmt = rtd->cpu_dai->dai_runtime.pcmfmt; | |
491 | switch (pcmfmt) { | |
492 | case SNDRV_PCM_FMTBIT_S16_LE: | |
493 | /* likely this is all we'll ever support, but ... */ | |
494 | bits = 16; | |
495 | dma_params->pdc_xfer_size = 2; | |
496 | break; | |
497 | default: | |
498 | printk(KERN_WARNING "at91rm9200-i2s: unsupported format %x\n", | |
499 | pcmfmt); | |
500 | return -EINVAL; | |
501 | } | |
502 | ||
503 | /* Don't allow both SSC substreams to initialize at the same time. */ | |
504 | down(ssc_p->mutex); | |
505 | ||
506 | /* | |
507 | * If this SSC is alreadly initialized, then this substream must use | |
508 | * the same format and rate. | |
509 | */ | |
510 | if (ssc_p->initialized) { | |
511 | if (pcmfmt != ssc_p->pcmfmt || rate != ssc_p->rate) { | |
512 | printk(KERN_WARNING "at91rm9200-i2s: " | |
513 | "incompatible substream in other direction\n"); | |
514 | up(ssc_p->mutex); | |
515 | return -EINVAL; | |
516 | } | |
517 | } else { | |
518 | /* Enable PMC peripheral clock for this SSC */ | |
519 | DBG("Starting pid %d clock\n", ssc_p->pid); | |
520 | at91_sys_write(AT91_PMC_PCER, 1<<ssc_p->pid); | |
521 | ||
522 | /* Reset the SSC */ | |
523 | at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CR, AT91_SSC_SWRST); | |
524 | ||
525 | at91_ssc_write(ssc_p->ssc_base + AT91_PDC_RPR, 0); | |
526 | at91_ssc_write(ssc_p->ssc_base + AT91_PDC_RCR, 0); | |
527 | at91_ssc_write(ssc_p->ssc_base + AT91_PDC_RNPR, 0); | |
528 | at91_ssc_write(ssc_p->ssc_base + AT91_PDC_RNCR, 0); | |
529 | at91_ssc_write(ssc_p->ssc_base + AT91_PDC_TPR, 0); | |
530 | at91_ssc_write(ssc_p->ssc_base + AT91_PDC_TCR, 0); | |
531 | at91_ssc_write(ssc_p->ssc_base + AT91_PDC_TNPR, 0); | |
532 | at91_ssc_write(ssc_p->ssc_base + AT91_PDC_TNCR, 0); | |
533 | ||
534 | mck_clk = clk_get(NULL, "mck"); | |
535 | ||
536 | div = rtd->cpu_dai->dai_runtime.priv >> 16; | |
537 | period = rtd->cpu_dai->dai_runtime.priv & 0xffff; | |
538 | bclk = 60000000 / (2 * div); | |
539 | ||
540 | DBG("mck %ld fsbd %d bfs %d bfs_real %d bclk %ld div %d period %d\n", | |
541 | clk_get_rate(mck_clk), | |
542 | SND_SOC_FSBD(6), | |
543 | rtd->cpu_dai->dai_runtime.bfs, | |
544 | SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs), | |
545 | bclk, | |
546 | div, | |
547 | period); | |
548 | ||
549 | clk_put(mck_clk); | |
550 | ||
551 | at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CMR, div); | |
552 | ||
553 | /* | |
554 | * Setup the TFMR and RFMR for the proper data format. | |
555 | */ | |
556 | tfmr = | |
557 | (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) | |
558 | | (( 0 << 23) & AT91_SSC_FSDEN) | |
559 | | (( AT91_SSC_FSOS_NEGATIVE ) & AT91_SSC_FSOS) | |
560 | | (((bits - 1) << 16) & AT91_SSC_FSLEN) | |
561 | | (((channels - 1) << 8) & AT91_SSC_DATNB) | |
562 | | (( 1 << 7) & AT91_SSC_MSBF) | |
563 | | (( 0 << 5) & AT91_SSC_DATDEF) | |
564 | | (((bits - 1) << 0) & AT91_SSC_DATALEN); | |
565 | DBG("SSC_TFMR=0x%08x\n", tfmr); | |
566 | at91_ssc_write(ssc_p->ssc_base + AT91_SSC_TFMR, tfmr); | |
567 | ||
568 | rfmr = | |
569 | (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE) | |
570 | | (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS) | |
571 | | (( 0 << 16) & AT91_SSC_FSLEN) | |
572 | | (((channels - 1) << 8) & AT91_SSC_DATNB) | |
573 | | (( 1 << 7) & AT91_SSC_MSBF) | |
574 | | (( 0 << 5) & AT91_SSC_LOOP) | |
575 | | (((bits - 1) << 0) & AT91_SSC_DATALEN); | |
576 | ||
577 | DBG("SSC_RFMR=0x%08x\n", rfmr); | |
578 | at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RFMR, rfmr); | |
579 | ||
580 | /* | |
581 | * Setup the TCMR and RCMR to generate the proper BCLK | |
582 | * and LRC signals. | |
583 | */ | |
584 | tcmr = | |
585 | (( period << 24) & AT91_SSC_PERIOD) | |
586 | | (( 1 << 16) & AT91_SSC_STTDLY) | |
587 | | (( AT91_SSC_START_FALLING_RF ) & AT91_SSC_START) | |
588 | | (( AT91_SSC_CKI_FALLING ) & AT91_SSC_CKI) | |
589 | | (( AT91_SSC_CKO_CONTINUOUS ) & AT91_SSC_CKO) | |
590 | | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS); | |
591 | ||
592 | DBG("SSC_TCMR=0x%08x\n", tcmr); | |
593 | at91_ssc_write(ssc_p->ssc_base + AT91_SSC_TCMR, tcmr); | |
594 | ||
595 | rcmr = | |
596 | (( 0 << 24) & AT91_SSC_PERIOD) | |
597 | | (( 1 << 16) & AT91_SSC_STTDLY) | |
598 | | (( AT91_SSC_START_TX_RX ) & AT91_SSC_START) | |
599 | | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI) | |
600 | | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO) | |
601 | | (( AT91_SSC_CKS_CLOCK ) & AT91_SSC_CKS); | |
602 | ||
603 | DBG("SSC_RCMR=0x%08x\n", rcmr); | |
604 | at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, rcmr); | |
605 | ||
606 | if ((ret = request_irq(ssc_p->pid, at91rm9200_i2s_interrupt, | |
607 | 0, ssc_p->name, ssc_p)) < 0) { | |
608 | printk(KERN_WARNING "at91rm9200-i2s: request_irq failure\n"); | |
609 | return ret; | |
610 | } | |
611 | ||
612 | /* | |
613 | * Save the current substream parameters in order to check | |
614 | * that the substream in the opposite direction uses the | |
615 | * same parameters. | |
616 | */ | |
617 | ssc_p->pcmfmt = pcmfmt; | |
618 | ssc_p->rate = rate; | |
619 | ssc_p->initialized = 1; | |
620 | ||
621 | DBG("hw_params: SSC initialized\n"); | |
622 | } | |
623 | ||
624 | up(ssc_p->mutex); | |
625 | ||
626 | return 0; | |
627 | } | |
628 | ||
629 | ||
630 | static int at91rm9200_i2s_prepare(struct snd_pcm_substream *substream) | |
631 | { | |
632 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
633 | at91rm9200_pcm_dma_params_t *dma_params = rtd->cpu_dai->dma_data; | |
634 | ||
635 | at91_ssc_write(dma_params->ssc->cr, dma_params->mask->ssc_enable); | |
636 | ||
637 | DBG("%s enabled SSC_SR=0x%08lx\n", | |
638 | substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "transmit" : "receive", | |
639 | at91_ssc_read(ssc_info[rtd->cpu_dai->id].ssc_base + AT91_SSC_SR)); | |
640 | return 0; | |
641 | } | |
642 | ||
643 | ||
644 | struct snd_soc_cpu_dai at91rm9200_i2s_dai[] = { | |
645 | { .name = "at91rm9200-ssc0/i2s", | |
646 | .id = 0, | |
647 | .type = SND_SOC_DAI_I2S, | |
648 | .suspend = at91rm9200_i2s_suspend, | |
649 | .resume = at91rm9200_i2s_resume, | |
650 | .config_sysclk = at91rm9200_i2s_config_sysclk, | |
651 | .playback = { | |
652 | .channels_min = 1, | |
653 | .channels_max = 2,}, | |
654 | .capture = { | |
655 | .channels_min = 1, | |
656 | .channels_max = 2,}, | |
657 | .ops = { | |
658 | .startup = at91rm9200_i2s_startup, | |
659 | .shutdown = at91rm9200_i2s_shutdown, | |
660 | .prepare = at91rm9200_i2s_prepare, | |
661 | .hw_params = at91rm9200_i2s_hw_params,}, | |
662 | .caps = { | |
663 | .mode = &at91rm9200_i2s[0], | |
664 | .num_modes = ARRAY_SIZE(at91rm9200_i2s),}, | |
665 | }, | |
666 | { .name = "at91rm9200-ssc1/i2s", | |
667 | .id = 1, | |
668 | .type = SND_SOC_DAI_I2S, | |
669 | .suspend = at91rm9200_i2s_suspend, | |
670 | .resume = at91rm9200_i2s_resume, | |
671 | .config_sysclk = at91rm9200_i2s_config_sysclk, | |
672 | .playback = { | |
673 | .channels_min = 1, | |
674 | .channels_max = 2,}, | |
675 | .capture = { | |
676 | .channels_min = 1, | |
677 | .channels_max = 2,}, | |
678 | .ops = { | |
679 | .startup = at91rm9200_i2s_startup, | |
680 | .shutdown = at91rm9200_i2s_shutdown, | |
681 | .prepare = at91rm9200_i2s_prepare, | |
682 | .hw_params = at91rm9200_i2s_hw_params,}, | |
683 | .caps = { | |
684 | .mode = &at91rm9200_i2s[0], | |
685 | .num_modes = ARRAY_SIZE(at91rm9200_i2s),}, | |
686 | }, | |
687 | { .name = "at91rm9200-ssc2/i2s", | |
688 | .id = 2, | |
689 | .type = SND_SOC_DAI_I2S, | |
690 | .suspend = at91rm9200_i2s_suspend, | |
691 | .resume = at91rm9200_i2s_resume, | |
692 | .config_sysclk = at91rm9200_i2s_config_sysclk, | |
693 | .playback = { | |
694 | .channels_min = 1, | |
695 | .channels_max = 2,}, | |
696 | .capture = { | |
697 | .channels_min = 1, | |
698 | .channels_max = 2,}, | |
699 | .ops = { | |
700 | .startup = at91rm9200_i2s_startup, | |
701 | .shutdown = at91rm9200_i2s_shutdown, | |
702 | .prepare = at91rm9200_i2s_prepare, | |
703 | .hw_params = at91rm9200_i2s_hw_params,}, | |
704 | .caps = { | |
705 | .mode = &at91rm9200_i2s[0], | |
706 | .num_modes = ARRAY_SIZE(at91rm9200_i2s),}, | |
707 | }, | |
708 | }; | |
709 | ||
710 | EXPORT_SYMBOL_GPL(at91rm9200_i2s_dai); | |
711 | ||
712 | /* Module information */ | |
713 | MODULE_AUTHOR("Frank Mandarino, fmandarino@endrelia.com, www.endrelia.com"); | |
714 | MODULE_DESCRIPTION("AT91RM9200 I2S ASoC Interface"); | |
715 | MODULE_LICENSE("GPL"); |