diff options
-rw-r--r-- | include/sound/spear_dma.h | 35 | ||||
-rw-r--r-- | sound/soc/spear/spear_pcm.c | 214 |
2 files changed, 249 insertions, 0 deletions
diff --git a/include/sound/spear_dma.h b/include/sound/spear_dma.h new file mode 100644 index 000000000000..1b365bfdfb37 --- /dev/null +++ b/include/sound/spear_dma.h | |||
@@ -0,0 +1,35 @@ | |||
1 | /* | ||
2 | * linux/spear_dma.h | ||
3 | * | ||
4 | * Copyright (ST) 2012 Rajeev Kumar (rajeev-dlh.kumar@st.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #ifndef SPEAR_DMA_H | ||
23 | #define SPEAR_DMA_H | ||
24 | |||
25 | #include <linux/dmaengine.h> | ||
26 | |||
27 | struct spear_dma_data { | ||
28 | void *data; | ||
29 | dma_addr_t addr; | ||
30 | u32 max_burst; | ||
31 | enum dma_slave_buswidth addr_width; | ||
32 | bool (*filter)(struct dma_chan *chan, void *slave); | ||
33 | }; | ||
34 | |||
35 | #endif /* SPEAR_DMA_H */ | ||
diff --git a/sound/soc/spear/spear_pcm.c b/sound/soc/spear/spear_pcm.c new file mode 100644 index 000000000000..97c2cac8e92c --- /dev/null +++ b/sound/soc/spear/spear_pcm.c | |||
@@ -0,0 +1,214 @@ | |||
1 | /* | ||
2 | * ALSA PCM interface for ST SPEAr Processors | ||
3 | * | ||
4 | * sound/soc/spear/spear_pcm.c | ||
5 | * | ||
6 | * Copyright (C) 2012 ST Microelectronics | ||
7 | * Rajeev Kumar<rajeev-dlh.kumar@st.com> | ||
8 | * | ||
9 | * This file is licensed under the terms of the GNU General Public | ||
10 | * License version 2. This program is licensed "as is" without any | ||
11 | * warranty of any kind, whether express or implied. | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/dmaengine.h> | ||
16 | #include <linux/dma-mapping.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/scatterlist.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <sound/core.h> | ||
22 | #include <sound/dmaengine_pcm.h> | ||
23 | #include <sound/pcm.h> | ||
24 | #include <sound/pcm_params.h> | ||
25 | #include <sound/soc.h> | ||
26 | #include <sound/spear_dma.h> | ||
27 | |||
28 | struct snd_pcm_hardware spear_pcm_hardware = { | ||
29 | .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
30 | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | | ||
31 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), | ||
32 | .buffer_bytes_max = 16 * 1024, /* max buffer size */ | ||
33 | .period_bytes_min = 2 * 1024, /* 1 msec data minimum period size */ | ||
34 | .period_bytes_max = 2 * 1024, /* maximum period size */ | ||
35 | .periods_min = 1, /* min # periods */ | ||
36 | .periods_max = 8, /* max # of periods */ | ||
37 | .fifo_size = 0, /* fifo size in bytes */ | ||
38 | }; | ||
39 | |||
40 | static int spear_pcm_hw_params(struct snd_pcm_substream *substream, | ||
41 | struct snd_pcm_hw_params *params) | ||
42 | { | ||
43 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
44 | |||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | static int spear_pcm_hw_free(struct snd_pcm_substream *substream) | ||
49 | { | ||
50 | snd_pcm_set_runtime_buffer(substream, NULL); | ||
51 | |||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | static int spear_pcm_open(struct snd_pcm_substream *substream) | ||
56 | { | ||
57 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
58 | |||
59 | struct spear_dma_data *dma_data = (struct spear_dma_data *) | ||
60 | snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | ||
61 | int ret; | ||
62 | |||
63 | ret = snd_soc_set_runtime_hwparams(substream, &spear_pcm_hardware); | ||
64 | if (ret) | ||
65 | return ret; | ||
66 | |||
67 | ret = snd_dmaengine_pcm_open(substream, dma_data->filter, dma_data); | ||
68 | if (ret) | ||
69 | return ret; | ||
70 | |||
71 | snd_dmaengine_pcm_set_data(substream, dma_data); | ||
72 | |||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static int spear_pcm_close(struct snd_pcm_substream *substream) | ||
77 | { | ||
78 | |||
79 | snd_dmaengine_pcm_close(substream); | ||
80 | |||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | static int spear_pcm_mmap(struct snd_pcm_substream *substream, | ||
85 | struct vm_area_struct *vma) | ||
86 | { | ||
87 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
88 | |||
89 | return dma_mmap_writecombine(substream->pcm->card->dev, vma, | ||
90 | runtime->dma_area, runtime->dma_addr, | ||
91 | runtime->dma_bytes); | ||
92 | } | ||
93 | |||
94 | static struct snd_pcm_ops spear_pcm_ops = { | ||
95 | .open = spear_pcm_open, | ||
96 | .close = spear_pcm_close, | ||
97 | .ioctl = snd_pcm_lib_ioctl, | ||
98 | .hw_params = spear_pcm_hw_params, | ||
99 | .hw_free = spear_pcm_hw_free, | ||
100 | .trigger = snd_dmaengine_pcm_trigger, | ||
101 | .pointer = snd_dmaengine_pcm_pointer, | ||
102 | .mmap = spear_pcm_mmap, | ||
103 | }; | ||
104 | |||
105 | static int | ||
106 | spear_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream, | ||
107 | size_t size) | ||
108 | { | ||
109 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; | ||
110 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
111 | |||
112 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | ||
113 | buf->dev.dev = pcm->card->dev; | ||
114 | buf->private_data = NULL; | ||
115 | |||
116 | buf->area = dma_alloc_writecombine(pcm->card->dev, size, | ||
117 | &buf->addr, GFP_KERNEL); | ||
118 | if (!buf->area) | ||
119 | return -ENOMEM; | ||
120 | |||
121 | dev_info(buf->dev.dev, | ||
122 | " preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", | ||
123 | (void *)buf->area, (void *)buf->addr, size); | ||
124 | |||
125 | buf->bytes = size; | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static void spear_pcm_free(struct snd_pcm *pcm) | ||
130 | { | ||
131 | struct snd_pcm_substream *substream; | ||
132 | struct snd_dma_buffer *buf; | ||
133 | int stream; | ||
134 | |||
135 | for (stream = 0; stream < 2; stream++) { | ||
136 | substream = pcm->streams[stream].substream; | ||
137 | if (!substream) | ||
138 | continue; | ||
139 | |||
140 | buf = &substream->dma_buffer; | ||
141 | if (!buf && !buf->area) | ||
142 | continue; | ||
143 | |||
144 | dma_free_writecombine(pcm->card->dev, buf->bytes, | ||
145 | buf->area, buf->addr); | ||
146 | buf->area = NULL; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | static u64 spear_pcm_dmamask = DMA_BIT_MASK(32); | ||
151 | |||
152 | static int spear_pcm_new(struct snd_card *card, | ||
153 | struct snd_soc_dai *dai, struct snd_pcm *pcm) | ||
154 | { | ||
155 | int ret; | ||
156 | |||
157 | if (!card->dev->dma_mask) | ||
158 | card->dev->dma_mask = &spear_pcm_dmamask; | ||
159 | if (!card->dev->coherent_dma_mask) | ||
160 | card->dev->coherent_dma_mask = DMA_BIT_MASK(32); | ||
161 | |||
162 | if (dai->driver->playback.channels_min) { | ||
163 | ret = spear_pcm_preallocate_dma_buffer(pcm, | ||
164 | SNDRV_PCM_STREAM_PLAYBACK, | ||
165 | spear_pcm_hardware.buffer_bytes_max); | ||
166 | if (ret) | ||
167 | return ret; | ||
168 | } | ||
169 | |||
170 | if (dai->driver->capture.channels_min) { | ||
171 | ret = spear_pcm_preallocate_dma_buffer(pcm, | ||
172 | SNDRV_PCM_STREAM_CAPTURE, | ||
173 | spear_pcm_hardware.buffer_bytes_max); | ||
174 | if (ret) | ||
175 | return ret; | ||
176 | } | ||
177 | |||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | struct snd_soc_platform_driver spear_soc_platform = { | ||
182 | .ops = &spear_pcm_ops, | ||
183 | .pcm_new = spear_pcm_new, | ||
184 | .pcm_free = spear_pcm_free, | ||
185 | }; | ||
186 | |||
187 | static int __devinit spear_soc_platform_probe(struct platform_device *pdev) | ||
188 | { | ||
189 | return snd_soc_register_platform(&pdev->dev, &spear_soc_platform); | ||
190 | } | ||
191 | |||
192 | static int __devexit spear_soc_platform_remove(struct platform_device *pdev) | ||
193 | { | ||
194 | snd_soc_unregister_platform(&pdev->dev); | ||
195 | |||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | static struct platform_driver spear_pcm_driver = { | ||
200 | .driver = { | ||
201 | .name = "spear-pcm-audio", | ||
202 | .owner = THIS_MODULE, | ||
203 | }, | ||
204 | |||
205 | .probe = spear_soc_platform_probe, | ||
206 | .remove = __devexit_p(spear_soc_platform_remove), | ||
207 | }; | ||
208 | |||
209 | module_platform_driver(spear_pcm_driver); | ||
210 | |||
211 | MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>"); | ||
212 | MODULE_DESCRIPTION("SPEAr PCM DMA module"); | ||
213 | MODULE_LICENSE("GPL"); | ||
214 | MODULE_ALIAS("platform:spear-pcm-audio"); | ||