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-auido.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 spin_lock_irqsave(&nuc900_audio
->lock
, flags
);
55 ret
= snd_pcm_lib_malloc_pages(substream
, params_buffer_bytes(params
));
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
;
173 spin_lock_irqsave(&nuc900_audio
->lock
, flags
);
175 nuc900_update_dma_register(substream
,
176 nuc900_audio
->dma_addr
[substream
->stream
],
177 nuc900_audio
->buffersize
[substream
->stream
]);
179 val
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_RESET
);
181 switch (runtime
->channels
) {
183 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
184 val
&= ~(PLAY_LEFT_CHNNEL
| PLAY_RIGHT_CHNNEL
);
185 val
|= PLAY_RIGHT_CHNNEL
;
187 val
&= ~(RECORD_LEFT_CHNNEL
| RECORD_RIGHT_CHNNEL
);
188 val
|= RECORD_RIGHT_CHNNEL
;
190 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_RESET
, val
);
193 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
194 val
|= (PLAY_LEFT_CHNNEL
| PLAY_RIGHT_CHNNEL
);
196 val
|= (RECORD_LEFT_CHNNEL
| RECORD_RIGHT_CHNNEL
);
197 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_RESET
, val
);
202 spin_unlock_irqrestore(&nuc900_audio
->lock
, flags
);
206 static int nuc900_dma_trigger(struct snd_pcm_substream
*substream
, int cmd
)
211 case SNDRV_PCM_TRIGGER_START
:
212 case SNDRV_PCM_TRIGGER_RESUME
:
213 nuc900_dma_start(substream
);
216 case SNDRV_PCM_TRIGGER_STOP
:
217 case SNDRV_PCM_TRIGGER_SUSPEND
:
218 nuc900_dma_stop(substream
);
229 int nuc900_dma_getposition(struct snd_pcm_substream
*substream
,
230 dma_addr_t
*src
, dma_addr_t
*dst
)
232 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
233 struct nuc900_audio
*nuc900_audio
= runtime
->private_data
;
236 *src
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_PDSTC
);
239 *dst
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_RDSTC
);
244 static snd_pcm_uframes_t
nuc900_dma_pointer(struct snd_pcm_substream
*substream
)
246 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
250 nuc900_dma_getposition(substream
, &src
, &dst
);
252 if (substream
->stream
== SNDRV_PCM_STREAM_CAPTURE
)
253 res
= dst
- runtime
->dma_addr
;
255 res
= src
- runtime
->dma_addr
;
257 return bytes_to_frames(substream
->runtime
, res
);
260 static int nuc900_dma_open(struct snd_pcm_substream
*substream
)
262 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
263 struct nuc900_audio
*nuc900_audio
;
265 snd_soc_set_runtime_hwparams(substream
, &nuc900_pcm_hardware
);
267 nuc900_audio
= nuc900_ac97_data
;
269 if (request_irq(nuc900_audio
->irq_num
, nuc900_dma_interrupt
,
270 IRQF_DISABLED
, "nuc900-dma", substream
))
273 runtime
->private_data
= nuc900_audio
;
278 static int nuc900_dma_close(struct snd_pcm_substream
*substream
)
280 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
281 struct nuc900_audio
*nuc900_audio
= runtime
->private_data
;
283 free_irq(nuc900_audio
->irq_num
, substream
);
288 static int nuc900_dma_mmap(struct snd_pcm_substream
*substream
,
289 struct vm_area_struct
*vma
)
291 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
293 return dma_mmap_writecombine(substream
->pcm
->card
->dev
, vma
,
299 static struct snd_pcm_ops nuc900_dma_ops
= {
300 .open
= nuc900_dma_open
,
301 .close
= nuc900_dma_close
,
302 .ioctl
= snd_pcm_lib_ioctl
,
303 .hw_params
= nuc900_dma_hw_params
,
304 .hw_free
= nuc900_dma_hw_free
,
305 .prepare
= nuc900_dma_prepare
,
306 .trigger
= nuc900_dma_trigger
,
307 .pointer
= nuc900_dma_pointer
,
308 .mmap
= nuc900_dma_mmap
,
311 static void nuc900_dma_free_dma_buffers(struct snd_pcm
*pcm
)
313 snd_pcm_lib_preallocate_free_for_all(pcm
);
316 static u64 nuc900_pcm_dmamask
= DMA_BIT_MASK(32);
317 static int nuc900_dma_new(struct snd_card
*card
,
318 struct snd_soc_dai
*dai
, struct snd_pcm
*pcm
)
320 if (!card
->dev
->dma_mask
)
321 card
->dev
->dma_mask
= &nuc900_pcm_dmamask
;
322 if (!card
->dev
->coherent_dma_mask
)
323 card
->dev
->coherent_dma_mask
= DMA_BIT_MASK(32);
325 snd_pcm_lib_preallocate_pages_for_all(pcm
, SNDRV_DMA_TYPE_DEV
,
326 card
->dev
, 4 * 1024, (4 * 1024) - 1);
331 struct snd_soc_platform nuc900_soc_platform
= {
332 .name
= "nuc900-dma",
333 .pcm_ops
= &nuc900_dma_ops
,
334 .pcm_new
= nuc900_dma_new
,
335 .pcm_free
= nuc900_dma_free_dma_buffers
,
337 EXPORT_SYMBOL_GPL(nuc900_soc_platform
);
339 static int __init
nuc900_soc_platform_init(void)
341 return snd_soc_register_platform(&nuc900_soc_platform
);
344 static void __exit
nuc900_soc_platform_exit(void)
346 snd_soc_unregister_platform(&nuc900_soc_platform
);
349 module_init(nuc900_soc_platform_init
);
350 module_exit(nuc900_soc_platform_exit
);
352 MODULE_AUTHOR("Wan ZongShun, <mcuos.com@gmail.com>");
353 MODULE_DESCRIPTION("nuc900 Audio DMA module");
354 MODULE_LICENSE("GPL");