Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
nuc900-pcm.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010 Nuvoton technology corporation.
3  *
4  * Wan ZongShun <[email protected]>
5  *
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.
9  *
10  */
11 
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/io.h>
15 #include <linux/platform_device.h>
16 #include <linux/slab.h>
17 #include <linux/dma-mapping.h>
18 
19 #include <sound/core.h>
20 #include <sound/pcm.h>
21 #include <sound/pcm_params.h>
22 #include <sound/soc.h>
23 
24 #include <mach/hardware.h>
25 
26 #include "nuc900-audio.h"
27 
28 static const struct snd_pcm_hardware nuc900_pcm_hardware = {
35  .formats = SNDRV_PCM_FMTBIT_S16_LE,
36  .channels_min = 1,
37  .channels_max = 2,
38  .buffer_bytes_max = 4*1024,
39  .period_bytes_min = 1*1024,
40  .period_bytes_max = 4*1024,
41  .periods_min = 1,
42  .periods_max = 1024,
43 };
44 
45 static int nuc900_dma_hw_params(struct snd_pcm_substream *substream,
46  struct snd_pcm_hw_params *params)
47 {
48  struct snd_pcm_runtime *runtime = substream->runtime;
49  struct nuc900_audio *nuc900_audio = runtime->private_data;
50  unsigned long flags;
51  int ret = 0;
52 
53  ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
54  if (ret < 0)
55  return ret;
56 
57  spin_lock_irqsave(&nuc900_audio->lock, flags);
58 
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);
63 
64  spin_unlock_irqrestore(&nuc900_audio->lock, flags);
65 
66  return ret;
67 }
68 
69 static void nuc900_update_dma_register(struct snd_pcm_substream *substream,
70  dma_addr_t dma_addr, size_t count)
71 {
72  struct snd_pcm_runtime *runtime = substream->runtime;
73  struct nuc900_audio *nuc900_audio = runtime->private_data;
74  void __iomem *mmio_addr, *mmio_len;
75 
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;
79  } else {
80  mmio_addr = nuc900_audio->mmio + ACTL_RDSTB;
81  mmio_len = nuc900_audio->mmio + ACTL_RDST_LENGTH;
82  }
83 
84  AUDIO_WRITE(mmio_addr, dma_addr);
85  AUDIO_WRITE(mmio_len, count);
86 }
87 
88 static void nuc900_dma_start(struct snd_pcm_substream *substream)
89 {
90  struct snd_pcm_runtime *runtime = substream->runtime;
91  struct nuc900_audio *nuc900_audio = runtime->private_data;
92  unsigned long val;
93 
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);
97 }
98 
99 static void nuc900_dma_stop(struct snd_pcm_substream *substream)
100 {
101  struct snd_pcm_runtime *runtime = substream->runtime;
102  struct nuc900_audio *nuc900_audio = runtime->private_data;
103  unsigned long val;
104 
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);
108 }
109 
110 static irqreturn_t nuc900_dma_interrupt(int irq, void *dev_id)
111 {
112  struct snd_pcm_substream *substream = dev_id;
113  struct nuc900_audio *nuc900_audio = substream->runtime->private_data;
114  unsigned long val;
115 
116  spin_lock(&nuc900_audio->lock);
117 
118  val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
119 
120  if (val & R_DMA_IRQ) {
121  AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | R_DMA_IRQ);
122 
123  val = AUDIO_READ(nuc900_audio->mmio + ACTL_RSR);
124 
125  if (val & R_DMA_MIDDLE_IRQ) {
126  val |= R_DMA_MIDDLE_IRQ;
127  AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
128  }
129 
130  if (val & R_DMA_END_IRQ) {
131  val |= R_DMA_END_IRQ;
132  AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
133  }
134  } else if (val & T_DMA_IRQ) {
135  AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | T_DMA_IRQ);
136 
137  val = AUDIO_READ(nuc900_audio->mmio + ACTL_PSR);
138 
139  if (val & P_DMA_MIDDLE_IRQ) {
140  val |= P_DMA_MIDDLE_IRQ;
141  AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
142  }
143 
144  if (val & P_DMA_END_IRQ) {
145  val |= P_DMA_END_IRQ;
146  AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
147  }
148  } else {
149  dev_err(nuc900_audio->dev, "Wrong DMA interrupt status!\n");
150  spin_unlock(&nuc900_audio->lock);
151  return IRQ_HANDLED;
152  }
153 
154  spin_unlock(&nuc900_audio->lock);
155 
156  snd_pcm_period_elapsed(substream);
157 
158  return IRQ_HANDLED;
159 }
160 
161 static int nuc900_dma_hw_free(struct snd_pcm_substream *substream)
162 {
163  snd_pcm_lib_free_pages(substream);
164  return 0;
165 }
166 
167 static int nuc900_dma_prepare(struct snd_pcm_substream *substream)
168 {
169  struct snd_pcm_runtime *runtime = substream->runtime;
170  struct nuc900_audio *nuc900_audio = runtime->private_data;
171  unsigned long flags, val;
172  int ret = 0;
173 
174  spin_lock_irqsave(&nuc900_audio->lock, flags);
175 
176  nuc900_update_dma_register(substream,
177  nuc900_audio->dma_addr[substream->stream],
178  nuc900_audio->buffersize[substream->stream]);
179 
180  val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
181 
182  switch (runtime->channels) {
183  case 1:
184  if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
186  val |= PLAY_RIGHT_CHNNEL;
187  } else {
189  val |= RECORD_RIGHT_CHNNEL;
190  }
191  AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
192  break;
193  case 2:
194  if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
196  else
198  AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
199  break;
200  default:
201  ret = -EINVAL;
202  }
203  spin_unlock_irqrestore(&nuc900_audio->lock, flags);
204  return ret;
205 }
206 
207 static int nuc900_dma_trigger(struct snd_pcm_substream *substream, int cmd)
208 {
209  int ret = 0;
210 
211  switch (cmd) {
214  nuc900_dma_start(substream);
215  break;
216 
219  nuc900_dma_stop(substream);
220  break;
221 
222  default:
223  ret = -EINVAL;
224  break;
225  }
226 
227  return ret;
228 }
229 
230 static int nuc900_dma_getposition(struct snd_pcm_substream *substream,
232 {
233  struct snd_pcm_runtime *runtime = substream->runtime;
234  struct nuc900_audio *nuc900_audio = runtime->private_data;
235 
236  if (src != NULL)
237  *src = AUDIO_READ(nuc900_audio->mmio + ACTL_PDSTC);
238 
239  if (dst != NULL)
240  *dst = AUDIO_READ(nuc900_audio->mmio + ACTL_RDSTC);
241 
242  return 0;
243 }
244 
245 static snd_pcm_uframes_t nuc900_dma_pointer(struct snd_pcm_substream *substream)
246 {
247  struct snd_pcm_runtime *runtime = substream->runtime;
248  dma_addr_t src, dst;
249  unsigned long res;
250 
251  nuc900_dma_getposition(substream, &src, &dst);
252 
253  if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
254  res = dst - runtime->dma_addr;
255  else
256  res = src - runtime->dma_addr;
257 
258  return bytes_to_frames(substream->runtime, res);
259 }
260 
261 static int nuc900_dma_open(struct snd_pcm_substream *substream)
262 {
263  struct snd_pcm_runtime *runtime = substream->runtime;
264  struct nuc900_audio *nuc900_audio;
265 
266  snd_soc_set_runtime_hwparams(substream, &nuc900_pcm_hardware);
267 
268  nuc900_audio = nuc900_ac97_data;
269 
270  if (request_irq(nuc900_audio->irq_num, nuc900_dma_interrupt,
271  0, "nuc900-dma", substream))
272  return -EBUSY;
273 
274  runtime->private_data = nuc900_audio;
275 
276  return 0;
277 }
278 
279 static int nuc900_dma_close(struct snd_pcm_substream *substream)
280 {
281  struct snd_pcm_runtime *runtime = substream->runtime;
282  struct nuc900_audio *nuc900_audio = runtime->private_data;
283 
284  free_irq(nuc900_audio->irq_num, substream);
285 
286  return 0;
287 }
288 
289 static int nuc900_dma_mmap(struct snd_pcm_substream *substream,
290  struct vm_area_struct *vma)
291 {
292  struct snd_pcm_runtime *runtime = substream->runtime;
293 
294  return dma_mmap_writecombine(substream->pcm->card->dev, vma,
295  runtime->dma_area,
296  runtime->dma_addr,
297  runtime->dma_bytes);
298 }
299 
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,
310 };
311 
312 static void nuc900_dma_free_dma_buffers(struct snd_pcm *pcm)
313 {
315 }
316 
317 static u64 nuc900_pcm_dmamask = DMA_BIT_MASK(32);
318 static int nuc900_dma_new(struct snd_soc_pcm_runtime *rtd)
319 {
320  struct snd_card *card = rtd->card->snd_card;
321  struct snd_pcm *pcm = rtd->pcm;
322 
323  if (!card->dev->dma_mask)
324  card->dev->dma_mask = &nuc900_pcm_dmamask;
325  if (!card->dev->coherent_dma_mask)
326  card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
327 
329  card->dev, 4 * 1024, (4 * 1024) - 1);
330 
331  return 0;
332 }
333 
334 static struct snd_soc_platform_driver nuc900_soc_platform = {
335  .ops = &nuc900_dma_ops,
336  .pcm_new = nuc900_dma_new,
337  .pcm_free = nuc900_dma_free_dma_buffers,
338 };
339 
340 static int __devinit nuc900_soc_platform_probe(struct platform_device *pdev)
341 {
342  return snd_soc_register_platform(&pdev->dev, &nuc900_soc_platform);
343 }
344 
345 static int __devexit nuc900_soc_platform_remove(struct platform_device *pdev)
346 {
348  return 0;
349 }
350 
351 static struct platform_driver nuc900_pcm_driver = {
352  .driver = {
353  .name = "nuc900-pcm-audio",
354  .owner = THIS_MODULE,
355  },
356 
357  .probe = nuc900_soc_platform_probe,
358  .remove = __devexit_p(nuc900_soc_platform_remove),
359 };
360 
361 module_platform_driver(nuc900_pcm_driver);
362 
363 MODULE_AUTHOR("Wan ZongShun, <[email protected]>");
364 MODULE_DESCRIPTION("nuc900 Audio DMA module");
365 MODULE_LICENSE("GPL");