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

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

Version: ~ [ linux-5.1-rc2 ] ~ [ linux-5.0.4 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.31 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.108 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.165 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.177 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.137 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.63 ] ~ [ 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-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ 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 /*
  2  * linux/sound/arm/ep93xx-pcm.c - EP93xx ALSA PCM interface
  3  *
  4  * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
  5  * Copyright (C) 2006 Applied Data Systems
  6  *
  7  * Rewritten for the SoC audio subsystem (Based on PXA2xx code):
  8  *   Copyright (c) 2008 Ryan Mallon <ryan@bluewatersys.com>
  9  *
 10  * This program is free software; you can redistribute it and/or modify
 11  * it under the terms of the GNU General Public License version 2 as
 12  * published by the Free Software Foundation.
 13  */
 14 
 15 #include <linux/module.h>
 16 #include <linux/init.h>
 17 #include <linux/device.h>
 18 #include <linux/slab.h>
 19 #include <linux/dma-mapping.h>
 20 
 21 #include <sound/core.h>
 22 #include <sound/pcm.h>
 23 #include <sound/pcm_params.h>
 24 #include <sound/soc.h>
 25 
 26 #include <mach/dma.h>
 27 #include <mach/hardware.h>
 28 #include <mach/ep93xx-regs.h>
 29 
 30 #include "ep93xx-pcm.h"
 31 
 32 static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
 33         .info                   = (SNDRV_PCM_INFO_MMAP          |
 34                                    SNDRV_PCM_INFO_MMAP_VALID    |
 35                                    SNDRV_PCM_INFO_INTERLEAVED   |
 36                                    SNDRV_PCM_INFO_BLOCK_TRANSFER),
 37                                    
 38         .rates                  = SNDRV_PCM_RATE_8000_192000,
 39         .rate_min               = SNDRV_PCM_RATE_8000,
 40         .rate_max               = SNDRV_PCM_RATE_192000,
 41         
 42         .formats                = (SNDRV_PCM_FMTBIT_S16_LE |
 43                                    SNDRV_PCM_FMTBIT_S24_LE |
 44                                    SNDRV_PCM_FMTBIT_S32_LE),
 45         
 46         .buffer_bytes_max       = 131072,
 47         .period_bytes_min       = 32,
 48         .period_bytes_max       = 32768,
 49         .periods_min            = 1,
 50         .periods_max            = 32,
 51         .fifo_size              = 32,
 52 };
 53 
 54 struct ep93xx_runtime_data
 55 {
 56         struct ep93xx_dma_m2p_client    cl;
 57         struct ep93xx_pcm_dma_params    *params;
 58         int                             pointer_bytes;
 59         struct tasklet_struct           period_tasklet;
 60         int                             periods;
 61         struct ep93xx_dma_buffer        buf[32];
 62 };
 63 
 64 static void ep93xx_pcm_period_elapsed(unsigned long data)
 65 {
 66         struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data;
 67         snd_pcm_period_elapsed(substream);
 68 }
 69 
 70 static void ep93xx_pcm_buffer_started(void *cookie,
 71                                       struct ep93xx_dma_buffer *buf)
 72 {
 73 }
 74 
 75 static void ep93xx_pcm_buffer_finished(void *cookie, 
 76                                        struct ep93xx_dma_buffer *buf, 
 77                                        int bytes, int error)
 78 {
 79         struct snd_pcm_substream *substream = cookie;
 80         struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
 81 
 82         if (buf == rtd->buf + rtd->periods - 1)
 83                 rtd->pointer_bytes = 0;
 84         else
 85                 rtd->pointer_bytes += buf->size;
 86 
 87         if (!error) {
 88                 ep93xx_dma_m2p_submit_recursive(&rtd->cl, buf);
 89                 tasklet_schedule(&rtd->period_tasklet);
 90         } else {
 91                 snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
 92         }
 93 }
 94 
 95 static int ep93xx_pcm_open(struct snd_pcm_substream *substream)
 96 {
 97         struct snd_soc_pcm_runtime *soc_rtd = substream->private_data;
 98         struct snd_soc_dai *cpu_dai = soc_rtd->cpu_dai;
 99         struct ep93xx_pcm_dma_params *dma_params;
100         struct ep93xx_runtime_data *rtd;    
101         int ret;
102 
103         dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream);
104         snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware);
105 
106         rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
107         if (!rtd) 
108                 return -ENOMEM;
109 
110         memset(&rtd->period_tasklet, 0, sizeof(rtd->period_tasklet));
111         rtd->period_tasklet.func = ep93xx_pcm_period_elapsed;
112         rtd->period_tasklet.data = (unsigned long)substream;
113 
114         rtd->cl.name = dma_params->name;
115         rtd->cl.flags = dma_params->dma_port | EP93XX_DMA_M2P_IGNORE_ERROR |
116                 ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
117                  EP93XX_DMA_M2P_TX : EP93XX_DMA_M2P_RX);
118         rtd->cl.cookie = substream;
119         rtd->cl.buffer_started = ep93xx_pcm_buffer_started;
120         rtd->cl.buffer_finished = ep93xx_pcm_buffer_finished;
121         ret = ep93xx_dma_m2p_client_register(&rtd->cl);
122         if (ret < 0) {
123                 kfree(rtd);
124                 return ret;
125         }
126         
127         substream->runtime->private_data = rtd;
128         return 0;
129 }
130 
131 static int ep93xx_pcm_close(struct snd_pcm_substream *substream)
132 {
133         struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
134 
135         ep93xx_dma_m2p_client_unregister(&rtd->cl);
136         kfree(rtd);
137         return 0;
138 }
139 
140 static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream,
141                                 struct snd_pcm_hw_params *params)
142 {
143         struct snd_pcm_runtime *runtime = substream->runtime;
144         struct ep93xx_runtime_data *rtd = runtime->private_data;
145         size_t totsize = params_buffer_bytes(params);
146         size_t period = params_period_bytes(params);
147         int i;
148 
149         snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
150         runtime->dma_bytes = totsize;
151 
152         rtd->periods = (totsize + period - 1) / period;
153         for (i = 0; i < rtd->periods; i++) {
154                 rtd->buf[i].bus_addr = runtime->dma_addr + (i * period);
155                 rtd->buf[i].size = period;
156                 if ((i + 1) * period > totsize)
157                         rtd->buf[i].size = totsize - (i * period);
158         }
159 
160         return 0;
161 }
162 
163 static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
164 {
165         snd_pcm_set_runtime_buffer(substream, NULL);
166         return 0;
167 }
168 
169 static int ep93xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
170 {
171         struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
172         int ret;
173         int i;
174 
175         ret = 0;
176         switch (cmd) {
177         case SNDRV_PCM_TRIGGER_START:
178         case SNDRV_PCM_TRIGGER_RESUME:
179         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
180                 rtd->pointer_bytes = 0;
181                 for (i = 0; i < rtd->periods; i++)
182                         ep93xx_dma_m2p_submit(&rtd->cl, rtd->buf + i);
183                 break;
184 
185         case SNDRV_PCM_TRIGGER_STOP:
186         case SNDRV_PCM_TRIGGER_SUSPEND:
187         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
188                 ep93xx_dma_m2p_flush(&rtd->cl);
189                 break;
190 
191         default:
192                 ret = -EINVAL;
193                 break;
194         }
195 
196         return ret;
197 }
198 
199 static snd_pcm_uframes_t ep93xx_pcm_pointer(struct snd_pcm_substream *substream)
200 {
201         struct snd_pcm_runtime *runtime = substream->runtime;
202         struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
203 
204         /* FIXME: implement this with sub-period granularity */
205         return bytes_to_frames(runtime, rtd->pointer_bytes);
206 }
207 
208 static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream,
209                            struct vm_area_struct *vma)
210 {
211         struct snd_pcm_runtime *runtime = substream->runtime;
212 
213         return dma_mmap_writecombine(substream->pcm->card->dev, vma,
214                                      runtime->dma_area,
215                                      runtime->dma_addr,
216                                      runtime->dma_bytes);
217 }
218 
219 static struct snd_pcm_ops ep93xx_pcm_ops = {
220         .open           = ep93xx_pcm_open,
221         .close          = ep93xx_pcm_close,
222         .ioctl          = snd_pcm_lib_ioctl,
223         .hw_params      = ep93xx_pcm_hw_params,
224         .hw_free        = ep93xx_pcm_hw_free,
225         .trigger        = ep93xx_pcm_trigger,
226         .pointer        = ep93xx_pcm_pointer,
227         .mmap           = ep93xx_pcm_mmap,
228 };
229 
230 static int ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
231 {
232         struct snd_pcm_substream *substream = pcm->streams[stream].substream;
233         struct snd_dma_buffer *buf = &substream->dma_buffer;
234         size_t size = ep93xx_pcm_hardware.buffer_bytes_max;
235 
236         buf->dev.type = SNDRV_DMA_TYPE_DEV;
237         buf->dev.dev = pcm->card->dev;
238         buf->private_data = NULL;
239         buf->area = dma_alloc_writecombine(pcm->card->dev, size,
240                                            &buf->addr, GFP_KERNEL);
241         buf->bytes = size;
242 
243         return (buf->area == NULL) ? -ENOMEM : 0;
244 }
245 
246 static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
247 {
248         struct snd_pcm_substream *substream;
249         struct snd_dma_buffer *buf;
250         int stream;
251 
252         for (stream = 0; stream < 2; stream++) {                
253                 substream = pcm->streams[stream].substream;
254                 if (!substream)
255                         continue;
256                 
257                 buf = &substream->dma_buffer;
258                 if (!buf->area)
259                         continue;
260 
261                 dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area,
262                                       buf->addr);
263                 buf->area = NULL;
264         }
265 }
266 
267 static u64 ep93xx_pcm_dmamask = 0xffffffff;
268 
269 static int ep93xx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
270                           struct snd_pcm *pcm)
271 {
272         int ret = 0;
273 
274         if (!card->dev->dma_mask)
275                 card->dev->dma_mask = &ep93xx_pcm_dmamask;
276         if (!card->dev->coherent_dma_mask)
277                 card->dev->coherent_dma_mask = 0xffffffff;
278 
279         if (dai->driver->playback.channels_min) {
280                 ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
281                                         SNDRV_PCM_STREAM_PLAYBACK);
282                 if (ret)
283                         return ret;
284         }
285 
286         if (dai->driver->capture.channels_min) {
287                 ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
288                                         SNDRV_PCM_STREAM_CAPTURE);
289                 if (ret)
290                         return ret;
291         }
292 
293         return 0;
294 }
295 
296 static struct snd_soc_platform_driver ep93xx_soc_platform = {
297         .ops            = &ep93xx_pcm_ops,
298         .pcm_new        = &ep93xx_pcm_new,
299         .pcm_free       = &ep93xx_pcm_free_dma_buffers,
300 };
301 
302 static int __devinit ep93xx_soc_platform_probe(struct platform_device *pdev)
303 {
304         return snd_soc_register_platform(&pdev->dev, &ep93xx_soc_platform);
305 }
306 
307 static int __devexit ep93xx_soc_platform_remove(struct platform_device *pdev)
308 {
309         snd_soc_unregister_platform(&pdev->dev);
310         return 0;
311 }
312 
313 static struct platform_driver ep93xx_pcm_driver = {
314         .driver = {
315                         .name = "ep93xx-pcm-audio",
316                         .owner = THIS_MODULE,
317         },
318 
319         .probe = ep93xx_soc_platform_probe,
320         .remove = __devexit_p(ep93xx_soc_platform_remove),
321 };
322 
323 static int __init ep93xx_soc_platform_init(void)
324 {
325         return platform_driver_register(&ep93xx_pcm_driver);
326 }
327 
328 static void __exit ep93xx_soc_platform_exit(void)
329 {
330         platform_driver_unregister(&ep93xx_pcm_driver);
331 }
332 
333 module_init(ep93xx_soc_platform_init);
334 module_exit(ep93xx_soc_platform_exit);
335 
336 MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>");
337 MODULE_DESCRIPTION("EP93xx ALSA PCM interface");
338 MODULE_LICENSE("GPL");
339 

~ [ 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