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

TOMOYO Linux Cross Reference
Linux/sound/soc/samsung/dma.c

Version: ~ [ linux-5.10-rc5 ] ~ [ linux-5.9.10 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.79 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.159 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.208 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.245 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.245 ] ~ [ 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 /*
  2  * dma.c  --  ALSA Soc Audio Layer
  3  *
  4  * (c) 2006 Wolfson Microelectronics PLC.
  5  * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
  6  *
  7  * Copyright 2004-2005 Simtec Electronics
  8  *      http://armlinux.simtec.co.uk/
  9  *      Ben Dooks <ben@simtec.co.uk>
 10  *
 11  *  This program is free software; you can redistribute  it and/or modify it
 12  *  under  the terms of  the GNU General  Public License as published by the
 13  *  Free Software Foundation;  either version 2 of the  License, or (at your
 14  *  option) any later version.
 15  */
 16 
 17 #include <linux/slab.h>
 18 #include <linux/dma-mapping.h>
 19 #include <linux/module.h>
 20 
 21 #include <sound/soc.h>
 22 #include <sound/pcm_params.h>
 23 
 24 #include <asm/dma.h>
 25 #include <mach/hardware.h>
 26 #include <mach/dma.h>
 27 
 28 #include "dma.h"
 29 
 30 #define ST_RUNNING              (1<<0)
 31 #define ST_OPENED               (1<<1)
 32 
 33 static const struct snd_pcm_hardware dma_hardware = {
 34         .info                   = SNDRV_PCM_INFO_INTERLEAVED |
 35                                     SNDRV_PCM_INFO_BLOCK_TRANSFER |
 36                                     SNDRV_PCM_INFO_MMAP |
 37                                     SNDRV_PCM_INFO_MMAP_VALID,
 38         .formats                = SNDRV_PCM_FMTBIT_S16_LE |
 39                                     SNDRV_PCM_FMTBIT_U16_LE |
 40                                     SNDRV_PCM_FMTBIT_U8 |
 41                                     SNDRV_PCM_FMTBIT_S8,
 42         .channels_min           = 2,
 43         .channels_max           = 2,
 44         .buffer_bytes_max       = 128*1024,
 45         .period_bytes_min       = PAGE_SIZE,
 46         .period_bytes_max       = PAGE_SIZE*2,
 47         .periods_min            = 2,
 48         .periods_max            = 128,
 49         .fifo_size              = 32,
 50 };
 51 
 52 struct runtime_data {
 53         spinlock_t lock;
 54         int state;
 55         unsigned int dma_loaded;
 56         unsigned int dma_period;
 57         dma_addr_t dma_start;
 58         dma_addr_t dma_pos;
 59         dma_addr_t dma_end;
 60         struct s3c_dma_params *params;
 61 };
 62 
 63 static void audio_buffdone(void *data);
 64 
 65 /* dma_enqueue
 66  *
 67  * place a dma buffer onto the queue for the dma system
 68  * to handle.
 69  */
 70 static void dma_enqueue(struct snd_pcm_substream *substream)
 71 {
 72         struct runtime_data *prtd = substream->runtime->private_data;
 73         dma_addr_t pos = prtd->dma_pos;
 74         unsigned int limit;
 75         struct samsung_dma_prep dma_info;
 76 
 77         pr_debug("Entered %s\n", __func__);
 78 
 79         limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period;
 80 
 81         pr_debug("%s: loaded %d, limit %d\n",
 82                                 __func__, prtd->dma_loaded, limit);
 83 
 84         dma_info.cap = (samsung_dma_has_circular() ? DMA_CYCLIC : DMA_SLAVE);
 85         dma_info.direction =
 86                 (substream->stream == SNDRV_PCM_STREAM_PLAYBACK
 87                 ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM);
 88         dma_info.fp = audio_buffdone;
 89         dma_info.fp_param = substream;
 90         dma_info.period = prtd->dma_period;
 91         dma_info.len = prtd->dma_period*limit;
 92 
 93         if (dma_info.cap == DMA_CYCLIC) {
 94                 dma_info.buf = pos;
 95                 prtd->params->ops->prepare(prtd->params->ch, &dma_info);
 96                 prtd->dma_loaded += limit;
 97                 return;
 98         }
 99 
100         while (prtd->dma_loaded < limit) {
101                 pr_debug("dma_loaded: %d\n", prtd->dma_loaded);
102 
103                 if ((pos + dma_info.period) > prtd->dma_end) {
104                         dma_info.period  = prtd->dma_end - pos;
105                         pr_debug("%s: corrected dma len %ld\n",
106                                         __func__, dma_info.period);
107                 }
108 
109                 dma_info.buf = pos;
110                 prtd->params->ops->prepare(prtd->params->ch, &dma_info);
111 
112                 prtd->dma_loaded++;
113                 pos += prtd->dma_period;
114                 if (pos >= prtd->dma_end)
115                         pos = prtd->dma_start;
116         }
117 
118         prtd->dma_pos = pos;
119 }
120 
121 static void audio_buffdone(void *data)
122 {
123         struct snd_pcm_substream *substream = data;
124         struct runtime_data *prtd = substream->runtime->private_data;
125 
126         pr_debug("Entered %s\n", __func__);
127 
128         if (prtd->state & ST_RUNNING) {
129                 prtd->dma_pos += prtd->dma_period;
130                 if (prtd->dma_pos >= prtd->dma_end)
131                         prtd->dma_pos = prtd->dma_start;
132 
133                 if (substream)
134                         snd_pcm_period_elapsed(substream);
135 
136                 spin_lock(&prtd->lock);
137                 if (!samsung_dma_has_circular()) {
138                         prtd->dma_loaded--;
139                         dma_enqueue(substream);
140                 }
141                 spin_unlock(&prtd->lock);
142         }
143 }
144 
145 static int dma_hw_params(struct snd_pcm_substream *substream,
146         struct snd_pcm_hw_params *params)
147 {
148         struct snd_pcm_runtime *runtime = substream->runtime;
149         struct runtime_data *prtd = runtime->private_data;
150         struct snd_soc_pcm_runtime *rtd = substream->private_data;
151         unsigned long totbytes = params_buffer_bytes(params);
152         struct s3c_dma_params *dma =
153                 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
154         struct samsung_dma_req req;
155         struct samsung_dma_config config;
156 
157         pr_debug("Entered %s\n", __func__);
158 
159         /* return if this is a bufferless transfer e.g.
160          * codec <--> BT codec or GSM modem -- lg FIXME */
161         if (!dma)
162                 return 0;
163 
164         /* this may get called several times by oss emulation
165          * with different params -HW */
166         if (prtd->params == NULL) {
167                 /* prepare DMA */
168                 prtd->params = dma;
169 
170                 pr_debug("params %p, client %p, channel %d\n", prtd->params,
171                         prtd->params->client, prtd->params->channel);
172 
173                 prtd->params->ops = samsung_dma_get_ops();
174 
175                 req.cap = (samsung_dma_has_circular() ?
176                         DMA_CYCLIC : DMA_SLAVE);
177                 req.client = prtd->params->client;
178                 config.direction =
179                         (substream->stream == SNDRV_PCM_STREAM_PLAYBACK
180                         ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM);
181                 config.width = prtd->params->dma_size;
182                 config.fifo = prtd->params->dma_addr;
183                 prtd->params->ch = prtd->params->ops->request(
184                                 prtd->params->channel, &req, rtd->cpu_dai->dev,
185                                 prtd->params->ch_name);
186                 if (!prtd->params->ch) {
187                         pr_err("Failed to allocate DMA channel\n");
188                         return -ENXIO;
189                 }
190                 prtd->params->ops->config(prtd->params->ch, &config);
191         }
192 
193         snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
194 
195         runtime->dma_bytes = totbytes;
196 
197         spin_lock_irq(&prtd->lock);
198         prtd->dma_loaded = 0;
199         prtd->dma_period = params_period_bytes(params);
200         prtd->dma_start = runtime->dma_addr;
201         prtd->dma_pos = prtd->dma_start;
202         prtd->dma_end = prtd->dma_start + totbytes;
203         spin_unlock_irq(&prtd->lock);
204 
205         return 0;
206 }
207 
208 static int dma_hw_free(struct snd_pcm_substream *substream)
209 {
210         struct runtime_data *prtd = substream->runtime->private_data;
211 
212         pr_debug("Entered %s\n", __func__);
213 
214         snd_pcm_set_runtime_buffer(substream, NULL);
215 
216         if (prtd->params) {
217                 prtd->params->ops->flush(prtd->params->ch);
218                 prtd->params->ops->release(prtd->params->ch,
219                                         prtd->params->client);
220                 prtd->params = NULL;
221         }
222 
223         return 0;
224 }
225 
226 static int dma_prepare(struct snd_pcm_substream *substream)
227 {
228         struct runtime_data *prtd = substream->runtime->private_data;
229         int ret = 0;
230 
231         pr_debug("Entered %s\n", __func__);
232 
233         /* return if this is a bufferless transfer e.g.
234          * codec <--> BT codec or GSM modem -- lg FIXME */
235         if (!prtd->params)
236                 return 0;
237 
238         /* flush the DMA channel */
239         prtd->params->ops->flush(prtd->params->ch);
240 
241         prtd->dma_loaded = 0;
242         prtd->dma_pos = prtd->dma_start;
243 
244         /* enqueue dma buffers */
245         dma_enqueue(substream);
246 
247         return ret;
248 }
249 
250 static int dma_trigger(struct snd_pcm_substream *substream, int cmd)
251 {
252         struct runtime_data *prtd = substream->runtime->private_data;
253         int ret = 0;
254 
255         pr_debug("Entered %s\n", __func__);
256 
257         spin_lock(&prtd->lock);
258 
259         switch (cmd) {
260         case SNDRV_PCM_TRIGGER_START:
261                 prtd->state |= ST_RUNNING;
262                 prtd->params->ops->trigger(prtd->params->ch);
263                 break;
264 
265         case SNDRV_PCM_TRIGGER_STOP:
266                 prtd->state &= ~ST_RUNNING;
267                 prtd->params->ops->stop(prtd->params->ch);
268                 break;
269 
270         default:
271                 ret = -EINVAL;
272                 break;
273         }
274 
275         spin_unlock(&prtd->lock);
276 
277         return ret;
278 }
279 
280 static snd_pcm_uframes_t
281 dma_pointer(struct snd_pcm_substream *substream)
282 {
283         struct snd_pcm_runtime *runtime = substream->runtime;
284         struct runtime_data *prtd = runtime->private_data;
285         unsigned long res;
286 
287         pr_debug("Entered %s\n", __func__);
288 
289         res = prtd->dma_pos - prtd->dma_start;
290 
291         pr_debug("Pointer offset: %lu\n", res);
292 
293         /* we seem to be getting the odd error from the pcm library due
294          * to out-of-bounds pointers. this is maybe due to the dma engine
295          * not having loaded the new values for the channel before being
296          * called... (todo - fix )
297          */
298 
299         if (res >= snd_pcm_lib_buffer_bytes(substream)) {
300                 if (res == snd_pcm_lib_buffer_bytes(substream))
301                         res = 0;
302         }
303 
304         return bytes_to_frames(substream->runtime, res);
305 }
306 
307 static int dma_open(struct snd_pcm_substream *substream)
308 {
309         struct snd_pcm_runtime *runtime = substream->runtime;
310         struct runtime_data *prtd;
311 
312         pr_debug("Entered %s\n", __func__);
313 
314         snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
315         snd_soc_set_runtime_hwparams(substream, &dma_hardware);
316 
317         prtd = kzalloc(sizeof(struct runtime_data), GFP_KERNEL);
318         if (prtd == NULL)
319                 return -ENOMEM;
320 
321         spin_lock_init(&prtd->lock);
322 
323         runtime->private_data = prtd;
324         return 0;
325 }
326 
327 static int dma_close(struct snd_pcm_substream *substream)
328 {
329         struct snd_pcm_runtime *runtime = substream->runtime;
330         struct runtime_data *prtd = runtime->private_data;
331 
332         pr_debug("Entered %s\n", __func__);
333 
334         if (!prtd)
335                 pr_debug("dma_close called with prtd == NULL\n");
336 
337         kfree(prtd);
338 
339         return 0;
340 }
341 
342 static int dma_mmap(struct snd_pcm_substream *substream,
343         struct vm_area_struct *vma)
344 {
345         struct snd_pcm_runtime *runtime = substream->runtime;
346 
347         pr_debug("Entered %s\n", __func__);
348 
349         return dma_mmap_writecombine(substream->pcm->card->dev, vma,
350                                      runtime->dma_area,
351                                      runtime->dma_addr,
352                                      runtime->dma_bytes);
353 }
354 
355 static struct snd_pcm_ops dma_ops = {
356         .open           = dma_open,
357         .close          = dma_close,
358         .ioctl          = snd_pcm_lib_ioctl,
359         .hw_params      = dma_hw_params,
360         .hw_free        = dma_hw_free,
361         .prepare        = dma_prepare,
362         .trigger        = dma_trigger,
363         .pointer        = dma_pointer,
364         .mmap           = dma_mmap,
365 };
366 
367 static int preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
368 {
369         struct snd_pcm_substream *substream = pcm->streams[stream].substream;
370         struct snd_dma_buffer *buf = &substream->dma_buffer;
371         size_t size = dma_hardware.buffer_bytes_max;
372 
373         pr_debug("Entered %s\n", __func__);
374 
375         buf->dev.type = SNDRV_DMA_TYPE_DEV;
376         buf->dev.dev = pcm->card->dev;
377         buf->private_data = NULL;
378         buf->area = dma_alloc_writecombine(pcm->card->dev, size,
379                                            &buf->addr, GFP_KERNEL);
380         if (!buf->area)
381                 return -ENOMEM;
382         buf->bytes = size;
383         return 0;
384 }
385 
386 static void dma_free_dma_buffers(struct snd_pcm *pcm)
387 {
388         struct snd_pcm_substream *substream;
389         struct snd_dma_buffer *buf;
390         int stream;
391 
392         pr_debug("Entered %s\n", __func__);
393 
394         for (stream = 0; stream < 2; stream++) {
395                 substream = pcm->streams[stream].substream;
396                 if (!substream)
397                         continue;
398 
399                 buf = &substream->dma_buffer;
400                 if (!buf->area)
401                         continue;
402 
403                 dma_free_writecombine(pcm->card->dev, buf->bytes,
404                                       buf->area, buf->addr);
405                 buf->area = NULL;
406         }
407 }
408 
409 static u64 dma_mask = DMA_BIT_MASK(32);
410 
411 static int dma_new(struct snd_soc_pcm_runtime *rtd)
412 {
413         struct snd_card *card = rtd->card->snd_card;
414         struct snd_pcm *pcm = rtd->pcm;
415         int ret = 0;
416 
417         pr_debug("Entered %s\n", __func__);
418 
419         if (!card->dev->dma_mask)
420                 card->dev->dma_mask = &dma_mask;
421         if (!card->dev->coherent_dma_mask)
422                 card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
423 
424         if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
425                 ret = preallocate_dma_buffer(pcm,
426                         SNDRV_PCM_STREAM_PLAYBACK);
427                 if (ret)
428                         goto out;
429         }
430 
431         if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
432                 ret = preallocate_dma_buffer(pcm,
433                         SNDRV_PCM_STREAM_CAPTURE);
434                 if (ret)
435                         goto out;
436         }
437 out:
438         return ret;
439 }
440 
441 static struct snd_soc_platform_driver samsung_asoc_platform = {
442         .ops            = &dma_ops,
443         .pcm_new        = dma_new,
444         .pcm_free       = dma_free_dma_buffers,
445 };
446 
447 int samsung_asoc_dma_platform_register(struct device *dev)
448 {
449         return snd_soc_register_platform(dev, &samsung_asoc_platform);
450 }
451 EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register);
452 
453 void samsung_asoc_dma_platform_unregister(struct device *dev)
454 {
455         snd_soc_unregister_platform(dev);
456 }
457 EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_unregister);
458 
459 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
460 MODULE_DESCRIPTION("Samsung ASoC DMA Driver");
461 MODULE_LICENSE("GPL");
462 

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