Root/
Source at commit e836ae0a9e7a76bb1a8a776851cb0b32b458d24f created 10 years 11 months ago. By Lars-Peter Clausen, ASoC: dmaengine-pcm: Make requesting the DMA channel at PCM open optional | |
---|---|
1 | /* |
2 | * omap-pcm.c -- ALSA PCM interface for the OMAP SoC |
3 | * |
4 | * Copyright (C) 2008 Nokia Corporation |
5 | * |
6 | * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com> |
7 | * Peter Ujfalusi <peter.ujfalusi@ti.com> |
8 | * |
9 | * This program is free software; you can redistribute it and/or |
10 | * modify it under the terms of the GNU General Public License |
11 | * version 2 as published by the Free Software Foundation. |
12 | * |
13 | * This program is distributed in the hope that it will be useful, but |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program; if not, write to the Free Software |
20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
21 | * 02110-1301 USA |
22 | * |
23 | */ |
24 | |
25 | #include <linux/dma-mapping.h> |
26 | #include <linux/slab.h> |
27 | #include <linux/module.h> |
28 | #include <linux/omap-dma.h> |
29 | #include <sound/core.h> |
30 | #include <sound/pcm.h> |
31 | #include <sound/pcm_params.h> |
32 | #include <sound/dmaengine_pcm.h> |
33 | #include <sound/soc.h> |
34 | |
35 | #ifdef CONFIG_ARCH_OMAP1 |
36 | #define pcm_omap1510() cpu_is_omap1510() |
37 | #else |
38 | #define pcm_omap1510() 0 |
39 | #endif |
40 | |
41 | static const struct snd_pcm_hardware omap_pcm_hardware = { |
42 | .info = SNDRV_PCM_INFO_MMAP | |
43 | SNDRV_PCM_INFO_MMAP_VALID | |
44 | SNDRV_PCM_INFO_INTERLEAVED | |
45 | SNDRV_PCM_INFO_PAUSE | |
46 | SNDRV_PCM_INFO_RESUME | |
47 | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, |
48 | .formats = SNDRV_PCM_FMTBIT_S16_LE | |
49 | SNDRV_PCM_FMTBIT_S32_LE, |
50 | .period_bytes_min = 32, |
51 | .period_bytes_max = 64 * 1024, |
52 | .periods_min = 2, |
53 | .periods_max = 255, |
54 | .buffer_bytes_max = 128 * 1024, |
55 | }; |
56 | |
57 | /* this may get called several times by oss emulation */ |
58 | static int omap_pcm_hw_params(struct snd_pcm_substream *substream, |
59 | struct snd_pcm_hw_params *params) |
60 | { |
61 | struct snd_pcm_runtime *runtime = substream->runtime; |
62 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
63 | struct omap_pcm_dma_data *dma_data; |
64 | struct dma_slave_config config; |
65 | struct dma_chan *chan; |
66 | int err = 0; |
67 | |
68 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); |
69 | |
70 | /* return if this is a bufferless transfer e.g. |
71 | * codec <--> BT codec or GSM modem -- lg FIXME */ |
72 | if (!dma_data) |
73 | return 0; |
74 | |
75 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); |
76 | runtime->dma_bytes = params_buffer_bytes(params); |
77 | |
78 | chan = snd_dmaengine_pcm_get_chan(substream); |
79 | if (!chan) |
80 | return -EINVAL; |
81 | |
82 | /* fills in addr_width and direction */ |
83 | err = snd_hwparams_to_dma_slave_config(substream, params, &config); |
84 | if (err) |
85 | return err; |
86 | |
87 | snd_dmaengine_pcm_set_config_from_dai_data(substream, |
88 | snd_soc_dai_get_dma_data(rtd->cpu_dai, substream), |
89 | &config); |
90 | |
91 | return dmaengine_slave_config(chan, &config); |
92 | } |
93 | |
94 | static int omap_pcm_hw_free(struct snd_pcm_substream *substream) |
95 | { |
96 | snd_pcm_set_runtime_buffer(substream, NULL); |
97 | return 0; |
98 | } |
99 | |
100 | static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream) |
101 | { |
102 | snd_pcm_uframes_t offset; |
103 | |
104 | if (pcm_omap1510()) |
105 | offset = snd_dmaengine_pcm_pointer_no_residue(substream); |
106 | else |
107 | offset = snd_dmaengine_pcm_pointer(substream); |
108 | |
109 | return offset; |
110 | } |
111 | |
112 | static int omap_pcm_open(struct snd_pcm_substream *substream) |
113 | { |
114 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
115 | struct snd_dmaengine_dai_dma_data *dma_data; |
116 | |
117 | snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware); |
118 | |
119 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); |
120 | |
121 | return snd_dmaengine_pcm_open_request_chan(substream, |
122 | omap_dma_filter_fn, |
123 | dma_data->filter_data); |
124 | } |
125 | |
126 | static int omap_pcm_mmap(struct snd_pcm_substream *substream, |
127 | struct vm_area_struct *vma) |
128 | { |
129 | struct snd_pcm_runtime *runtime = substream->runtime; |
130 | |
131 | return dma_mmap_writecombine(substream->pcm->card->dev, vma, |
132 | runtime->dma_area, |
133 | runtime->dma_addr, |
134 | runtime->dma_bytes); |
135 | } |
136 | |
137 | static struct snd_pcm_ops omap_pcm_ops = { |
138 | .open = omap_pcm_open, |
139 | .close = snd_dmaengine_pcm_close_release_chan, |
140 | .ioctl = snd_pcm_lib_ioctl, |
141 | .hw_params = omap_pcm_hw_params, |
142 | .hw_free = omap_pcm_hw_free, |
143 | .trigger = snd_dmaengine_pcm_trigger, |
144 | .pointer = omap_pcm_pointer, |
145 | .mmap = omap_pcm_mmap, |
146 | }; |
147 | |
148 | static u64 omap_pcm_dmamask = DMA_BIT_MASK(64); |
149 | |
150 | static int omap_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, |
151 | int stream) |
152 | { |
153 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; |
154 | struct snd_dma_buffer *buf = &substream->dma_buffer; |
155 | size_t size = omap_pcm_hardware.buffer_bytes_max; |
156 | |
157 | buf->dev.type = SNDRV_DMA_TYPE_DEV; |
158 | buf->dev.dev = pcm->card->dev; |
159 | buf->private_data = NULL; |
160 | buf->area = dma_alloc_writecombine(pcm->card->dev, size, |
161 | &buf->addr, GFP_KERNEL); |
162 | if (!buf->area) |
163 | return -ENOMEM; |
164 | |
165 | buf->bytes = size; |
166 | return 0; |
167 | } |
168 | |
169 | static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm) |
170 | { |
171 | struct snd_pcm_substream *substream; |
172 | struct snd_dma_buffer *buf; |
173 | int stream; |
174 | |
175 | for (stream = 0; stream < 2; stream++) { |
176 | substream = pcm->streams[stream].substream; |
177 | if (!substream) |
178 | continue; |
179 | |
180 | buf = &substream->dma_buffer; |
181 | if (!buf->area) |
182 | continue; |
183 | |
184 | dma_free_writecombine(pcm->card->dev, buf->bytes, |
185 | buf->area, buf->addr); |
186 | buf->area = NULL; |
187 | } |
188 | } |
189 | |
190 | static int omap_pcm_new(struct snd_soc_pcm_runtime *rtd) |
191 | { |
192 | struct snd_card *card = rtd->card->snd_card; |
193 | struct snd_pcm *pcm = rtd->pcm; |
194 | int ret = 0; |
195 | |
196 | if (!card->dev->dma_mask) |
197 | card->dev->dma_mask = &omap_pcm_dmamask; |
198 | if (!card->dev->coherent_dma_mask) |
199 | card->dev->coherent_dma_mask = DMA_BIT_MASK(64); |
200 | |
201 | if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { |
202 | ret = omap_pcm_preallocate_dma_buffer(pcm, |
203 | SNDRV_PCM_STREAM_PLAYBACK); |
204 | if (ret) |
205 | goto out; |
206 | } |
207 | |
208 | if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { |
209 | ret = omap_pcm_preallocate_dma_buffer(pcm, |
210 | SNDRV_PCM_STREAM_CAPTURE); |
211 | if (ret) |
212 | goto out; |
213 | } |
214 | |
215 | out: |
216 | /* free preallocated buffers in case of error */ |
217 | if (ret) |
218 | omap_pcm_free_dma_buffers(pcm); |
219 | |
220 | return ret; |
221 | } |
222 | |
223 | static struct snd_soc_platform_driver omap_soc_platform = { |
224 | .ops = &omap_pcm_ops, |
225 | .pcm_new = omap_pcm_new, |
226 | .pcm_free = omap_pcm_free_dma_buffers, |
227 | }; |
228 | |
229 | static int omap_pcm_probe(struct platform_device *pdev) |
230 | { |
231 | return snd_soc_register_platform(&pdev->dev, |
232 | &omap_soc_platform); |
233 | } |
234 | |
235 | static int omap_pcm_remove(struct platform_device *pdev) |
236 | { |
237 | snd_soc_unregister_platform(&pdev->dev); |
238 | return 0; |
239 | } |
240 | |
241 | static struct platform_driver omap_pcm_driver = { |
242 | .driver = { |
243 | .name = "omap-pcm-audio", |
244 | .owner = THIS_MODULE, |
245 | }, |
246 | |
247 | .probe = omap_pcm_probe, |
248 | .remove = omap_pcm_remove, |
249 | }; |
250 | |
251 | module_platform_driver(omap_pcm_driver); |
252 | |
253 | MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>"); |
254 | MODULE_DESCRIPTION("OMAP PCM DMA module"); |
255 | MODULE_LICENSE("GPL"); |
256 | MODULE_ALIAS("platform:omap-pcm-audio"); |
257 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9