Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ep93xx-pcm.c
Go to the documentation of this file.
1 /*
2  * linux/sound/arm/ep93xx-pcm.c - EP93xx ALSA PCM interface
3  *
4  * Copyright (C) 2006 Lennert Buytenhek <[email protected]>
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
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/dmaengine.h>
20 #include <linux/dma-mapping.h>
21 
22 #include <sound/core.h>
23 #include <sound/pcm.h>
24 #include <sound/pcm_params.h>
25 #include <sound/soc.h>
26 #include <sound/dmaengine_pcm.h>
27 
29 #include <mach/hardware.h>
30 #include <mach/ep93xx-regs.h>
31 
32 #include "ep93xx-pcm.h"
33 
34 static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
35  .info = (SNDRV_PCM_INFO_MMAP |
39 
43 
47 
48  .buffer_bytes_max = 131072,
49  .period_bytes_min = 32,
50  .period_bytes_max = 32768,
51  .periods_min = 1,
52  .periods_max = 32,
53  .fifo_size = 32,
54 };
55 
56 static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param)
57 {
58  struct ep93xx_dma_data *data = filter_param;
59 
60  if (data->direction == ep93xx_dma_chan_direction(chan)) {
61  chan->private = data;
62  return true;
63  }
64 
65  return false;
66 }
67 
68 static int ep93xx_pcm_open(struct snd_pcm_substream *substream)
69 {
70  struct snd_soc_pcm_runtime *rtd = substream->private_data;
71  struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
72  struct ep93xx_pcm_dma_params *dma_params;
73  struct ep93xx_dma_data *dma_data;
74  int ret;
75 
76  snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware);
77 
78  dma_data = kmalloc(sizeof(*dma_data), GFP_KERNEL);
79  if (!dma_data)
80  return -ENOMEM;
81 
82  dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream);
83  dma_data->port = dma_params->dma_port;
84  dma_data->name = dma_params->name;
85  dma_data->direction = snd_pcm_substream_to_dma_direction(substream);
86 
87  ret = snd_dmaengine_pcm_open(substream, ep93xx_pcm_dma_filter, dma_data);
88  if (ret) {
89  kfree(dma_data);
90  return ret;
91  }
92 
93  snd_dmaengine_pcm_set_data(substream, dma_data);
94 
95  return 0;
96 }
97 
98 static int ep93xx_pcm_close(struct snd_pcm_substream *substream)
99 {
100  struct dma_data *dma_data = snd_dmaengine_pcm_get_data(substream);
101 
102  snd_dmaengine_pcm_close(substream);
103  kfree(dma_data);
104  return 0;
105 }
106 
107 static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream,
108  struct snd_pcm_hw_params *params)
109 {
110  snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
111 
112  return 0;
113 }
114 
115 static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
116 {
117  snd_pcm_set_runtime_buffer(substream, NULL);
118  return 0;
119 }
120 
121 static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream,
122  struct vm_area_struct *vma)
123 {
124  struct snd_pcm_runtime *runtime = substream->runtime;
125 
126  return dma_mmap_writecombine(substream->pcm->card->dev, vma,
127  runtime->dma_area,
128  runtime->dma_addr,
129  runtime->dma_bytes);
130 }
131 
132 static struct snd_pcm_ops ep93xx_pcm_ops = {
133  .open = ep93xx_pcm_open,
134  .close = ep93xx_pcm_close,
135  .ioctl = snd_pcm_lib_ioctl,
136  .hw_params = ep93xx_pcm_hw_params,
137  .hw_free = ep93xx_pcm_hw_free,
138  .trigger = snd_dmaengine_pcm_trigger,
140  .mmap = ep93xx_pcm_mmap,
141 };
142 
143 static int ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
144 {
145  struct snd_pcm_substream *substream = pcm->streams[stream].substream;
146  struct snd_dma_buffer *buf = &substream->dma_buffer;
147  size_t size = ep93xx_pcm_hardware.buffer_bytes_max;
148 
149  buf->dev.type = SNDRV_DMA_TYPE_DEV;
150  buf->dev.dev = pcm->card->dev;
151  buf->private_data = NULL;
152  buf->area = dma_alloc_writecombine(pcm->card->dev, size,
153  &buf->addr, GFP_KERNEL);
154  buf->bytes = size;
155 
156  return (buf->area == NULL) ? -ENOMEM : 0;
157 }
158 
159 static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
160 {
161  struct snd_pcm_substream *substream;
162  struct snd_dma_buffer *buf;
163  int stream;
164 
165  for (stream = 0; stream < 2; stream++) {
166  substream = pcm->streams[stream].substream;
167  if (!substream)
168  continue;
169 
170  buf = &substream->dma_buffer;
171  if (!buf->area)
172  continue;
173 
174  dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area,
175  buf->addr);
176  buf->area = NULL;
177  }
178 }
179 
180 static u64 ep93xx_pcm_dmamask = DMA_BIT_MASK(32);
181 
182 static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd)
183 {
184  struct snd_card *card = rtd->card->snd_card;
185  struct snd_pcm *pcm = rtd->pcm;
186  int ret = 0;
187 
188  if (!card->dev->dma_mask)
189  card->dev->dma_mask = &ep93xx_pcm_dmamask;
190  if (!card->dev->coherent_dma_mask)
191  card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
192 
193  if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
194  ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
196  if (ret)
197  return ret;
198  }
199 
200  if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
201  ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
203  if (ret)
204  return ret;
205  }
206 
207  return 0;
208 }
209 
210 static struct snd_soc_platform_driver ep93xx_soc_platform = {
211  .ops = &ep93xx_pcm_ops,
212  .pcm_new = &ep93xx_pcm_new,
213  .pcm_free = &ep93xx_pcm_free_dma_buffers,
214 };
215 
216 static int __devinit ep93xx_soc_platform_probe(struct platform_device *pdev)
217 {
218  return snd_soc_register_platform(&pdev->dev, &ep93xx_soc_platform);
219 }
220 
221 static int __devexit ep93xx_soc_platform_remove(struct platform_device *pdev)
222 {
224  return 0;
225 }
226 
227 static struct platform_driver ep93xx_pcm_driver = {
228  .driver = {
229  .name = "ep93xx-pcm-audio",
230  .owner = THIS_MODULE,
231  },
232 
233  .probe = ep93xx_soc_platform_probe,
234  .remove = __devexit_p(ep93xx_soc_platform_remove),
235 };
236 
237 module_platform_driver(ep93xx_pcm_driver);
238 
239 MODULE_AUTHOR("Ryan Mallon");
240 MODULE_DESCRIPTION("EP93xx ALSA PCM interface");
241 MODULE_LICENSE("GPL");
242 MODULE_ALIAS("platform:ep93xx-pcm-audio");