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 .buffer_bytes_max
= 4*1024,
36 .period_bytes_min
= 1*1024,
37 .period_bytes_max
= 4*1024,
42 static int nuc900_dma_hw_params(struct snd_pcm_substream
*substream
,
43 struct snd_pcm_hw_params
*params
)
45 return snd_pcm_lib_malloc_pages(substream
, params_buffer_bytes(params
));
48 static void nuc900_update_dma_register(struct snd_pcm_substream
*substream
)
50 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
51 struct nuc900_audio
*nuc900_audio
= runtime
->private_data
;
52 void __iomem
*mmio_addr
, *mmio_len
;
54 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
55 mmio_addr
= nuc900_audio
->mmio
+ ACTL_PDSTB
;
56 mmio_len
= nuc900_audio
->mmio
+ ACTL_PDST_LENGTH
;
58 mmio_addr
= nuc900_audio
->mmio
+ ACTL_RDSTB
;
59 mmio_len
= nuc900_audio
->mmio
+ ACTL_RDST_LENGTH
;
62 AUDIO_WRITE(mmio_addr
, runtime
->dma_addr
);
63 AUDIO_WRITE(mmio_len
, runtime
->dma_bytes
);
66 static void nuc900_dma_start(struct snd_pcm_substream
*substream
)
68 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
69 struct nuc900_audio
*nuc900_audio
= runtime
->private_data
;
72 val
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_CON
);
73 val
|= (T_DMA_IRQ
| R_DMA_IRQ
);
74 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_CON
, val
);
77 static void nuc900_dma_stop(struct snd_pcm_substream
*substream
)
79 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
80 struct nuc900_audio
*nuc900_audio
= runtime
->private_data
;
83 val
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_CON
);
84 val
&= ~(T_DMA_IRQ
| R_DMA_IRQ
);
85 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_CON
, val
);
88 static irqreturn_t
nuc900_dma_interrupt(int irq
, void *dev_id
)
90 struct snd_pcm_substream
*substream
= dev_id
;
91 struct nuc900_audio
*nuc900_audio
= substream
->runtime
->private_data
;
94 spin_lock(&nuc900_audio
->lock
);
96 val
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_CON
);
98 if (val
& R_DMA_IRQ
) {
99 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_CON
, val
| R_DMA_IRQ
);
101 val
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_RSR
);
103 if (val
& R_DMA_MIDDLE_IRQ
) {
104 val
|= R_DMA_MIDDLE_IRQ
;
105 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_RSR
, val
);
108 if (val
& R_DMA_END_IRQ
) {
109 val
|= R_DMA_END_IRQ
;
110 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_RSR
, val
);
112 } else if (val
& T_DMA_IRQ
) {
113 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_CON
, val
| T_DMA_IRQ
);
115 val
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_PSR
);
117 if (val
& P_DMA_MIDDLE_IRQ
) {
118 val
|= P_DMA_MIDDLE_IRQ
;
119 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_PSR
, val
);
122 if (val
& P_DMA_END_IRQ
) {
123 val
|= P_DMA_END_IRQ
;
124 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_PSR
, val
);
127 dev_err(nuc900_audio
->dev
, "Wrong DMA interrupt status!\n");
128 spin_unlock(&nuc900_audio
->lock
);
132 spin_unlock(&nuc900_audio
->lock
);
134 snd_pcm_period_elapsed(substream
);
139 static int nuc900_dma_hw_free(struct snd_pcm_substream
*substream
)
141 snd_pcm_lib_free_pages(substream
);
145 static int nuc900_dma_prepare(struct snd_pcm_substream
*substream
)
147 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
148 struct nuc900_audio
*nuc900_audio
= runtime
->private_data
;
149 unsigned long flags
, val
;
152 spin_lock_irqsave(&nuc900_audio
->lock
, flags
);
154 nuc900_update_dma_register(substream
);
156 val
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_RESET
);
158 switch (runtime
->channels
) {
160 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
161 val
&= ~(PLAY_LEFT_CHNNEL
| PLAY_RIGHT_CHNNEL
);
162 val
|= PLAY_RIGHT_CHNNEL
;
164 val
&= ~(RECORD_LEFT_CHNNEL
| RECORD_RIGHT_CHNNEL
);
165 val
|= RECORD_RIGHT_CHNNEL
;
167 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_RESET
, val
);
170 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
171 val
|= (PLAY_LEFT_CHNNEL
| PLAY_RIGHT_CHNNEL
);
173 val
|= (RECORD_LEFT_CHNNEL
| RECORD_RIGHT_CHNNEL
);
174 AUDIO_WRITE(nuc900_audio
->mmio
+ ACTL_RESET
, val
);
179 spin_unlock_irqrestore(&nuc900_audio
->lock
, flags
);
183 static int nuc900_dma_trigger(struct snd_pcm_substream
*substream
, int cmd
)
188 case SNDRV_PCM_TRIGGER_START
:
189 case SNDRV_PCM_TRIGGER_RESUME
:
190 nuc900_dma_start(substream
);
193 case SNDRV_PCM_TRIGGER_STOP
:
194 case SNDRV_PCM_TRIGGER_SUSPEND
:
195 nuc900_dma_stop(substream
);
206 static int nuc900_dma_getposition(struct snd_pcm_substream
*substream
,
207 dma_addr_t
*src
, dma_addr_t
*dst
)
209 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
210 struct nuc900_audio
*nuc900_audio
= runtime
->private_data
;
213 *src
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_PDSTC
);
216 *dst
= AUDIO_READ(nuc900_audio
->mmio
+ ACTL_RDSTC
);
221 static snd_pcm_uframes_t
nuc900_dma_pointer(struct snd_pcm_substream
*substream
)
223 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
227 nuc900_dma_getposition(substream
, &src
, &dst
);
229 if (substream
->stream
== SNDRV_PCM_STREAM_CAPTURE
)
230 res
= dst
- runtime
->dma_addr
;
232 res
= src
- runtime
->dma_addr
;
234 return bytes_to_frames(substream
->runtime
, res
);
237 static int nuc900_dma_open(struct snd_pcm_substream
*substream
)
239 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
240 struct nuc900_audio
*nuc900_audio
;
242 snd_soc_set_runtime_hwparams(substream
, &nuc900_pcm_hardware
);
244 nuc900_audio
= nuc900_ac97_data
;
246 if (request_irq(nuc900_audio
->irq_num
, nuc900_dma_interrupt
,
247 0, "nuc900-dma", substream
))
250 runtime
->private_data
= nuc900_audio
;
255 static int nuc900_dma_close(struct snd_pcm_substream
*substream
)
257 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
258 struct nuc900_audio
*nuc900_audio
= runtime
->private_data
;
260 free_irq(nuc900_audio
->irq_num
, substream
);
265 static int nuc900_dma_mmap(struct snd_pcm_substream
*substream
,
266 struct vm_area_struct
*vma
)
268 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
270 return dma_mmap_writecombine(substream
->pcm
->card
->dev
, vma
,
276 static struct snd_pcm_ops nuc900_dma_ops
= {
277 .open
= nuc900_dma_open
,
278 .close
= nuc900_dma_close
,
279 .ioctl
= snd_pcm_lib_ioctl
,
280 .hw_params
= nuc900_dma_hw_params
,
281 .hw_free
= nuc900_dma_hw_free
,
282 .prepare
= nuc900_dma_prepare
,
283 .trigger
= nuc900_dma_trigger
,
284 .pointer
= nuc900_dma_pointer
,
285 .mmap
= nuc900_dma_mmap
,
288 static int nuc900_dma_new(struct snd_soc_pcm_runtime
*rtd
)
290 struct snd_card
*card
= rtd
->card
->snd_card
;
291 struct snd_pcm
*pcm
= rtd
->pcm
;
294 ret
= dma_coerce_mask_and_coherent(card
->dev
, DMA_BIT_MASK(32));
298 snd_pcm_lib_preallocate_pages_for_all(pcm
, SNDRV_DMA_TYPE_DEV
,
299 card
->dev
, 4 * 1024, (4 * 1024) - 1);
304 static struct snd_soc_platform_driver nuc900_soc_platform
= {
305 .ops
= &nuc900_dma_ops
,
306 .pcm_new
= nuc900_dma_new
,
309 static int nuc900_soc_platform_probe(struct platform_device
*pdev
)
311 return devm_snd_soc_register_platform(&pdev
->dev
, &nuc900_soc_platform
);
314 static struct platform_driver nuc900_pcm_driver
= {
316 .name
= "nuc900-pcm-audio",
319 .probe
= nuc900_soc_platform_probe
,
322 module_platform_driver(nuc900_pcm_driver
);
324 MODULE_AUTHOR("Wan ZongShun, <mcuos.com@gmail.com>");
325 MODULE_DESCRIPTION("nuc900 Audio DMA module");
326 MODULE_LICENSE("GPL");