~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/nuc900/nuc900-pcm.c

Version: ~ [ linux-5.10-rc1 ] ~ [ linux-5.9.1 ] ~ [ linux-5.8.16 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.72 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.152 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.202 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.240 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.240 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * Copyright (c) 2010 Nuvoton technology corporation.
  4  *
  5  * Wan ZongShun <mcuos.com@gmail.com>
  6  */
  7 
  8 #include <linux/module.h>
  9 #include <linux/init.h>
 10 #include <linux/io.h>
 11 #include <linux/platform_device.h>
 12 #include <linux/slab.h>
 13 #include <linux/dma-mapping.h>
 14 
 15 #include <sound/core.h>
 16 #include <sound/pcm.h>
 17 #include <sound/pcm_params.h>
 18 #include <sound/soc.h>
 19 
 20 #include <mach/hardware.h>
 21 
 22 #include "nuc900-audio.h"
 23 
 24 static const struct snd_pcm_hardware nuc900_pcm_hardware = {
 25         .info                   = SNDRV_PCM_INFO_INTERLEAVED |
 26                                         SNDRV_PCM_INFO_BLOCK_TRANSFER |
 27                                         SNDRV_PCM_INFO_MMAP |
 28                                         SNDRV_PCM_INFO_MMAP_VALID |
 29                                         SNDRV_PCM_INFO_PAUSE |
 30                                         SNDRV_PCM_INFO_RESUME,
 31         .buffer_bytes_max       = 4*1024,
 32         .period_bytes_min       = 1*1024,
 33         .period_bytes_max       = 4*1024,
 34         .periods_min            = 1,
 35         .periods_max            = 1024,
 36 };
 37 
 38 static int nuc900_dma_hw_params(struct snd_pcm_substream *substream,
 39         struct snd_pcm_hw_params *params)
 40 {
 41         return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
 42 }
 43 
 44 static void nuc900_update_dma_register(struct snd_pcm_substream *substream)
 45 {
 46         struct snd_pcm_runtime *runtime = substream->runtime;
 47         struct nuc900_audio *nuc900_audio = runtime->private_data;
 48         void __iomem *mmio_addr, *mmio_len;
 49 
 50         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 51                 mmio_addr = nuc900_audio->mmio + ACTL_PDSTB;
 52                 mmio_len = nuc900_audio->mmio + ACTL_PDST_LENGTH;
 53         } else {
 54                 mmio_addr = nuc900_audio->mmio + ACTL_RDSTB;
 55                 mmio_len = nuc900_audio->mmio + ACTL_RDST_LENGTH;
 56         }
 57 
 58         AUDIO_WRITE(mmio_addr, runtime->dma_addr);
 59         AUDIO_WRITE(mmio_len, runtime->dma_bytes);
 60 }
 61 
 62 static void nuc900_dma_start(struct snd_pcm_substream *substream)
 63 {
 64         struct snd_pcm_runtime *runtime = substream->runtime;
 65         struct nuc900_audio *nuc900_audio = runtime->private_data;
 66         unsigned long val;
 67 
 68         val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
 69         val |= (T_DMA_IRQ | R_DMA_IRQ);
 70         AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
 71 }
 72 
 73 static void nuc900_dma_stop(struct snd_pcm_substream *substream)
 74 {
 75         struct snd_pcm_runtime *runtime = substream->runtime;
 76         struct nuc900_audio *nuc900_audio = runtime->private_data;
 77         unsigned long val;
 78 
 79         val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
 80         val &= ~(T_DMA_IRQ | R_DMA_IRQ);
 81         AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
 82 }
 83 
 84 static irqreturn_t nuc900_dma_interrupt(int irq, void *dev_id)
 85 {
 86         struct snd_pcm_substream *substream = dev_id;
 87         struct nuc900_audio *nuc900_audio = substream->runtime->private_data;
 88         unsigned long val;
 89 
 90         spin_lock(&nuc900_audio->lock);
 91 
 92         val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
 93 
 94         if (val & R_DMA_IRQ) {
 95                 AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | R_DMA_IRQ);
 96 
 97                 val = AUDIO_READ(nuc900_audio->mmio + ACTL_RSR);
 98 
 99                 if (val & R_DMA_MIDDLE_IRQ) {
100                         val |= R_DMA_MIDDLE_IRQ;
101                         AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
102                 }
103 
104                 if (val & R_DMA_END_IRQ) {
105                         val |= R_DMA_END_IRQ;
106                         AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
107                 }
108         } else if (val & T_DMA_IRQ) {
109                 AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | T_DMA_IRQ);
110 
111                 val = AUDIO_READ(nuc900_audio->mmio + ACTL_PSR);
112 
113                 if (val & P_DMA_MIDDLE_IRQ) {
114                         val |= P_DMA_MIDDLE_IRQ;
115                         AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
116                 }
117 
118                 if (val & P_DMA_END_IRQ) {
119                         val |= P_DMA_END_IRQ;
120                         AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
121                 }
122         } else {
123                 dev_err(nuc900_audio->dev, "Wrong DMA interrupt status!\n");
124                 spin_unlock(&nuc900_audio->lock);
125                 return IRQ_HANDLED;
126         }
127 
128         spin_unlock(&nuc900_audio->lock);
129 
130         snd_pcm_period_elapsed(substream);
131 
132         return IRQ_HANDLED;
133 }
134 
135 static int nuc900_dma_hw_free(struct snd_pcm_substream *substream)
136 {
137         snd_pcm_lib_free_pages(substream);
138         return 0;
139 }
140 
141 static int nuc900_dma_prepare(struct snd_pcm_substream *substream)
142 {
143         struct snd_pcm_runtime *runtime = substream->runtime;
144         struct nuc900_audio *nuc900_audio = runtime->private_data;
145         unsigned long flags, val;
146         int ret = 0;
147 
148         spin_lock_irqsave(&nuc900_audio->lock, flags);
149 
150         nuc900_update_dma_register(substream);
151 
152         val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
153 
154         switch (runtime->channels) {
155         case 1:
156                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
157                         val &= ~(PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
158                         val |= PLAY_RIGHT_CHNNEL;
159                 } else {
160                         val &= ~(RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
161                         val |= RECORD_RIGHT_CHNNEL;
162                 }
163                 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
164                 break;
165         case 2:
166                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
167                         val |= (PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
168                 else
169                         val |= (RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
170                 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
171                 break;
172         default:
173                 ret = -EINVAL;
174         }
175         spin_unlock_irqrestore(&nuc900_audio->lock, flags);
176         return ret;
177 }
178 
179 static int nuc900_dma_trigger(struct snd_pcm_substream *substream, int cmd)
180 {
181         int ret = 0;
182 
183         switch (cmd) {
184         case SNDRV_PCM_TRIGGER_START:
185         case SNDRV_PCM_TRIGGER_RESUME:
186                 nuc900_dma_start(substream);
187                 break;
188 
189         case SNDRV_PCM_TRIGGER_STOP:
190         case SNDRV_PCM_TRIGGER_SUSPEND:
191                 nuc900_dma_stop(substream);
192                 break;
193 
194         default:
195                 ret = -EINVAL;
196                 break;
197         }
198 
199         return ret;
200 }
201 
202 static int nuc900_dma_getposition(struct snd_pcm_substream *substream,
203                                         dma_addr_t *src, dma_addr_t *dst)
204 {
205         struct snd_pcm_runtime *runtime = substream->runtime;
206         struct nuc900_audio *nuc900_audio = runtime->private_data;
207 
208         if (src != NULL)
209                 *src = AUDIO_READ(nuc900_audio->mmio + ACTL_PDSTC);
210 
211         if (dst != NULL)
212                 *dst = AUDIO_READ(nuc900_audio->mmio + ACTL_RDSTC);
213 
214         return 0;
215 }
216 
217 static snd_pcm_uframes_t nuc900_dma_pointer(struct snd_pcm_substream *substream)
218 {
219         struct snd_pcm_runtime *runtime = substream->runtime;
220         dma_addr_t src, dst;
221         unsigned long res;
222 
223         nuc900_dma_getposition(substream, &src, &dst);
224 
225         if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
226                 res = dst - runtime->dma_addr;
227         else
228                 res = src - runtime->dma_addr;
229 
230         return bytes_to_frames(substream->runtime, res);
231 }
232 
233 static int nuc900_dma_open(struct snd_pcm_substream *substream)
234 {
235         struct snd_pcm_runtime *runtime = substream->runtime;
236         struct nuc900_audio *nuc900_audio;
237 
238         snd_soc_set_runtime_hwparams(substream, &nuc900_pcm_hardware);
239 
240         nuc900_audio = nuc900_ac97_data;
241 
242         if (request_irq(nuc900_audio->irq_num, nuc900_dma_interrupt,
243                         0, "nuc900-dma", substream))
244                 return -EBUSY;
245 
246         runtime->private_data = nuc900_audio;
247 
248         return 0;
249 }
250 
251 static int nuc900_dma_close(struct snd_pcm_substream *substream)
252 {
253         struct snd_pcm_runtime *runtime = substream->runtime;
254         struct nuc900_audio *nuc900_audio = runtime->private_data;
255 
256         free_irq(nuc900_audio->irq_num, substream);
257 
258         return 0;
259 }
260 
261 static int nuc900_dma_mmap(struct snd_pcm_substream *substream,
262         struct vm_area_struct *vma)
263 {
264         struct snd_pcm_runtime *runtime = substream->runtime;
265 
266         return dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area,
267                            runtime->dma_addr, runtime->dma_bytes);
268 }
269 
270 static const struct snd_pcm_ops nuc900_dma_ops = {
271         .open           = nuc900_dma_open,
272         .close          = nuc900_dma_close,
273         .ioctl          = snd_pcm_lib_ioctl,
274         .hw_params      = nuc900_dma_hw_params,
275         .hw_free        = nuc900_dma_hw_free,
276         .prepare        = nuc900_dma_prepare,
277         .trigger        = nuc900_dma_trigger,
278         .pointer        = nuc900_dma_pointer,
279         .mmap           = nuc900_dma_mmap,
280 };
281 
282 static int nuc900_dma_new(struct snd_soc_pcm_runtime *rtd)
283 {
284         struct snd_card *card = rtd->card->snd_card;
285         struct snd_pcm *pcm = rtd->pcm;
286         int ret;
287 
288         ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
289         if (ret)
290                 return ret;
291 
292         snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
293                 card->dev, 4 * 1024, (4 * 1024) - 1);
294 
295         return 0;
296 }
297 
298 static const struct snd_soc_component_driver nuc900_soc_component = {
299         .ops            = &nuc900_dma_ops,
300         .pcm_new        = nuc900_dma_new,
301 };
302 
303 static int nuc900_soc_platform_probe(struct platform_device *pdev)
304 {
305         return devm_snd_soc_register_component(&pdev->dev, &nuc900_soc_component,
306                                                NULL, 0);
307 }
308 
309 static struct platform_driver nuc900_pcm_driver = {
310         .driver = {
311                         .name = "nuc900-pcm-audio",
312         },
313 
314         .probe = nuc900_soc_platform_probe,
315 };
316 
317 module_platform_driver(nuc900_pcm_driver);
318 
319 MODULE_AUTHOR("Wan ZongShun, <mcuos.com@gmail.com>");
320 MODULE_DESCRIPTION("nuc900 Audio DMA module");
321 MODULE_LICENSE("GPL");
322 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp