2 * Copyright (c) 2010 Nuvoton technology corporation.
4 * Wan ZongShun <mcuos.com@gmail.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation;version 2 of the License.
12 #include <linux/module.h>
13 #include <linux/init.h>
15 #include <linux/platform_device.h>
16 #include <linux/slab.h>
17 #include <linux/dma-mapping.h>
19 #include <sound/core.h>
20 #include <sound/pcm.h>
21 #include <sound/pcm_params.h>
22 #include <sound/soc.h>
24 #include <mach/hardware.h>
26 #include "nuc900-audio.h"
28 static const struct snd_pcm_hardware nuc900_pcm_hardware
= {
29 .info
= SNDRV_PCM_INFO_INTERLEAVED
|
30 SNDRV_PCM_INFO_BLOCK_TRANSFER
|
32 SNDRV_PCM_INFO_MMAP_VALID
|
33 SNDRV_PCM_INFO_PAUSE
|
34 SNDRV_PCM_INFO_RESUME
,
35 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,
38 .buffer_bytes_max
= 4*1024,
39 .period_bytes_min
= 1*1024,
40 .period_bytes_max
= 4*1024,
45 static int nuc900_dma_hw_params(struct snd_pcm_substream
*substream
,
46 struct snd_pcm_hw_params
*params
)
48 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
49 struct nuc900_audio
*nuc900_audio
= runtime
->private_data
;
53 ret
= snd_pcm_lib_malloc_pages(substream
, params_buffer_bytes(params
));
57 spin_lock_irqsave(&nuc900_audio
->lock
, flags
);
59 nuc900_audio
->substream
= substream
;
60 nuc900_audio
->dma_addr
[substream
->stream
] = runtime
->dma_addr
;
61 nuc900_audio
->buffersize
[substream
->stream
] =
62 params_buffer_bytes(params
);
64 spin_unlock_irqrestore(&nuc900_audio
->lock
, flags
);
69 static void nuc900_update_dma_register(struct snd_pcm_substream
*substream
,
70 dma_addr_t dma_addr
, size_t count
)
72 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
73 struct nuc900_audio
*nuc900_audio
= runtime
->private_data
;
74 void __iomem
*mmio_addr
, *mmio_len
;
76 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
77 mmio_addr
= nuc900_audio
->mmio
+ ACTL_PDSTB
;
78 mmio_len
= nuc900_audio
->mmio
+ ACTL_PDST_LENGTH
;
80 mmio_addr
= nuc900_audio
->mmio
+ ACTL_RDSTB
;
81 mmio_len
= nuc900_audio
->mmio
+ ACTL_RDST_LENGTH
;
84 AUDIO_WRITE(mmio_addr
, dma_addr
);
85 AUDIO_WRITE(mmio_len
, count
);
88 static void nuc900_dma_start(struct snd_pcm_substream
*substream
)
90 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
91 struct nuc900_audio
*nuc900_audio
= runtime
->private_data
;
94 val
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_CON
);
95 val
|= (T_DMA_IRQ
| R_DMA_IRQ
);
96 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_CON
, val
);
99 static void nuc900_dma_stop(struct snd_pcm_substream
*substream
)
101 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
102 struct nuc900_audio
*nuc900_audio
= runtime
->private_data
;
105 val
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_CON
);
106 val
&= ~(T_DMA_IRQ
| R_DMA_IRQ
);
107 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_CON
, val
);
110 static irqreturn_t
nuc900_dma_interrupt(int irq
, void *dev_id
)
112 struct snd_pcm_substream
*substream
= dev_id
;
113 struct nuc900_audio
*nuc900_audio
= substream
->runtime
->private_data
;
116 spin_lock(&nuc900_audio
->lock
);
118 val
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_CON
);
120 if (val
& R_DMA_IRQ
) {
121 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_CON
, val
| R_DMA_IRQ
);
123 val
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_RSR
);
125 if (val
& R_DMA_MIDDLE_IRQ
) {
126 val
|= R_DMA_MIDDLE_IRQ
;
127 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_RSR
, val
);
130 if (val
& R_DMA_END_IRQ
) {
131 val
|= R_DMA_END_IRQ
;
132 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_RSR
, val
);
134 } else if (val
& T_DMA_IRQ
) {
135 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_CON
, val
| T_DMA_IRQ
);
137 val
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_PSR
);
139 if (val
& P_DMA_MIDDLE_IRQ
) {
140 val
|= P_DMA_MIDDLE_IRQ
;
141 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_PSR
, val
);
144 if (val
& P_DMA_END_IRQ
) {
145 val
|= P_DMA_END_IRQ
;
146 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_PSR
, val
);
149 dev_err(nuc900_audio
->dev
, "Wrong DMA interrupt status!\n");
150 spin_unlock(&nuc900_audio
->lock
);
154 spin_unlock(&nuc900_audio
->lock
);
156 snd_pcm_period_elapsed(substream
);
161 static int nuc900_dma_hw_free(struct snd_pcm_substream
*substream
)
163 snd_pcm_lib_free_pages(substream
);
167 static int nuc900_dma_prepare(struct snd_pcm_substream
*substream
)
169 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
170 struct nuc900_audio
*nuc900_audio
= runtime
->private_data
;
171 unsigned long flags
, val
;
174 spin_lock_irqsave(&nuc900_audio
->lock
, flags
);
176 nuc900_update_dma_register(substream
,
177 nuc900_audio
->dma_addr
[substream
->stream
],
178 nuc900_audio
->buffersize
[substream
->stream
]);
180 val
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_RESET
);
182 switch (runtime
->channels
) {
184 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
185 val
&= ~(PLAY_LEFT_CHNNEL
| PLAY_RIGHT_CHNNEL
);
186 val
|= PLAY_RIGHT_CHNNEL
;
188 val
&= ~(RECORD_LEFT_CHNNEL
| RECORD_RIGHT_CHNNEL
);
189 val
|= RECORD_RIGHT_CHNNEL
;
191 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_RESET
, val
);
194 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
195 val
|= (PLAY_LEFT_CHNNEL
| PLAY_RIGHT_CHNNEL
);
197 val
|= (RECORD_LEFT_CHNNEL
| RECORD_RIGHT_CHNNEL
);
198 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_RESET
, val
);
203 spin_unlock_irqrestore(&nuc900_audio
->lock
, flags
);
207 static int nuc900_dma_trigger(struct snd_pcm_substream
*substream
, int cmd
)
212 case SNDRV_PCM_TRIGGER_START
:
213 case SNDRV_PCM_TRIGGER_RESUME
:
214 nuc900_dma_start(substream
);
217 case SNDRV_PCM_TRIGGER_STOP
:
218 case SNDRV_PCM_TRIGGER_SUSPEND
:
219 nuc900_dma_stop(substream
);
230 int nuc900_dma_getposition(struct snd_pcm_substream
*substream
,
231 dma_addr_t
*src
, dma_addr_t
*dst
)
233 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
234 struct nuc900_audio
*nuc900_audio
= runtime
->private_data
;
237 *src
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_PDSTC
);
240 *dst
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_RDSTC
);
245 static snd_pcm_uframes_t
nuc900_dma_pointer(struct snd_pcm_substream
*substream
)
247 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
251 nuc900_dma_getposition(substream
, &src
, &dst
);
253 if (substream
->stream
== SNDRV_PCM_STREAM_CAPTURE
)
254 res
= dst
- runtime
->dma_addr
;
256 res
= src
- runtime
->dma_addr
;
258 return bytes_to_frames(substream
->runtime
, res
);
261 static int nuc900_dma_open(struct snd_pcm_substream
*substream
)
263 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
264 struct nuc900_audio
*nuc900_audio
;
266 snd_soc_set_runtime_hwparams(substream
, &nuc900_pcm_hardware
);
268 nuc900_audio
= nuc900_ac97_data
;
270 if (request_irq(nuc900_audio
->irq_num
, nuc900_dma_interrupt
,
271 IRQF_DISABLED
, "nuc900-dma", substream
))
274 runtime
->private_data
= nuc900_audio
;
279 static int nuc900_dma_close(struct snd_pcm_substream
*substream
)
281 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
282 struct nuc900_audio
*nuc900_audio
= runtime
->private_data
;
284 free_irq(nuc900_audio
->irq_num
, substream
);
289 static int nuc900_dma_mmap(struct snd_pcm_substream
*substream
,
290 struct vm_area_struct
*vma
)
292 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
294 return dma_mmap_writecombine(substream
->pcm
->card
->dev
, vma
,
300 static struct snd_pcm_ops nuc900_dma_ops
= {
301 .open
= nuc900_dma_open
,
302 .close
= nuc900_dma_close
,
303 .ioctl
= snd_pcm_lib_ioctl
,
304 .hw_params
= nuc900_dma_hw_params
,
305 .hw_free
= nuc900_dma_hw_free
,
306 .prepare
= nuc900_dma_prepare
,
307 .trigger
= nuc900_dma_trigger
,
308 .pointer
= nuc900_dma_pointer
,
309 .mmap
= nuc900_dma_mmap
,
312 static void nuc900_dma_free_dma_buffers(struct snd_pcm
*pcm
)
314 snd_pcm_lib_preallocate_free_for_all(pcm
);
317 static u64 nuc900_pcm_dmamask
= DMA_BIT_MASK(32);
318 static int nuc900_dma_new(struct snd_card
*card
,
319 struct snd_soc_dai
*dai
, struct snd_pcm
*pcm
)
321 if (!card
->dev
->dma_mask
)
322 card
->dev
->dma_mask
= &nuc900_pcm_dmamask
;
323 if (!card
->dev
->coherent_dma_mask
)
324 card
->dev
->coherent_dma_mask
= DMA_BIT_MASK(32);
326 snd_pcm_lib_preallocate_pages_for_all(pcm
, SNDRV_DMA_TYPE_DEV
,
327 card
->dev
, 4 * 1024, (4 * 1024) - 1);
332 static struct snd_soc_platform_driver nuc900_soc_platform
= {
333 .ops
= &nuc900_dma_ops
,
334 .pcm_new
= nuc900_dma_new
,
335 .pcm_free
= nuc900_dma_free_dma_buffers
,
338 static int __devinit
nuc900_soc_platform_probe(struct platform_device
*pdev
)
340 return snd_soc_register_platform(&pdev
->dev
, &nuc900_soc_platform
);
343 static int __devexit
nuc900_soc_platform_remove(struct platform_device
*pdev
)
345 snd_soc_unregister_platform(&pdev
->dev
);
349 static struct platform_driver nuc900_pcm_driver
= {
351 .name
= "nuc900-pcm-audio",
352 .owner
= THIS_MODULE
,
355 .probe
= nuc900_soc_platform_probe
,
356 .remove
= __devexit_p(nuc900_soc_platform_remove
),
359 static int __init
nuc900_pcm_init(void)
361 return platform_driver_register(&nuc900_pcm_driver
);
363 module_init(nuc900_pcm_init
);
365 static void __exit
nuc900_pcm_exit(void)
367 platform_driver_unregister(&nuc900_pcm_driver
);
369 module_exit(nuc900_pcm_exit
);
371 MODULE_AUTHOR("Wan ZongShun, <mcuos.com@gmail.com>");
372 MODULE_DESCRIPTION("nuc900 Audio DMA module");
373 MODULE_LICENSE("GPL");