1
2
3
4
5
6
7
8
9
10
11
12#include <linux/module.h>
13#include <linux/init.h>
14#include <linux/platform_device.h>
15#include <linux/slab.h>
16#include <linux/dma-mapping.h>
17
18#include <sound/core.h>
19#include <sound/pcm.h>
20#include <sound/pcm_params.h>
21#include <sound/soc.h>
22
23#include <asm/mach-au1x00/au1000.h>
24#include <asm/mach-au1x00/au1xxx_dbdma.h>
25#include <asm/mach-au1x00/au1xxx_psc.h>
26
27#include "psc.h"
28
29
30
31#define DRV_NAME "dbdma2"
32
33#define MSG(x...) printk(KERN_INFO "au1xpsc_pcm: " x)
34#ifdef PCM_DEBUG
35#define DBG MSG
36#else
37#define DBG(x...) do {} while (0)
38#endif
39
40struct au1xpsc_audio_dmadata {
41
42 unsigned int ddma_id;
43 u32 ddma_chan;
44
45
46 struct snd_pcm_substream *substream;
47 unsigned long curr_period;
48 unsigned long q_period;
49 dma_addr_t dma_area;
50 dma_addr_t dma_area_s;
51 unsigned long pos;
52 unsigned long periods;
53 unsigned long period_bytes;
54
55
56 int msbits;
57};
58
59
60
61
62
63#define AU1XPSC_PERIOD_MIN_BYTES 1024
64#define AU1XPSC_BUFFER_MIN_BYTES 65536
65
66
67static const struct snd_pcm_hardware au1xpsc_pcm_hardware = {
68 .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
69 SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BATCH,
70 .period_bytes_min = AU1XPSC_PERIOD_MIN_BYTES,
71 .period_bytes_max = 4096 * 1024 - 1,
72 .periods_min = 2,
73 .periods_max = 4096,
74 .buffer_bytes_max = 4096 * 1024 - 1,
75 .fifo_size = 16,
76};
77
78static void au1x_pcm_queue_tx(struct au1xpsc_audio_dmadata *cd)
79{
80 au1xxx_dbdma_put_source(cd->ddma_chan, cd->dma_area,
81 cd->period_bytes, DDMA_FLAGS_IE);
82
83
84 ++cd->q_period;
85 cd->dma_area += cd->period_bytes;
86 if (cd->q_period >= cd->periods) {
87 cd->q_period = 0;
88 cd->dma_area = cd->dma_area_s;
89 }
90}
91
92static void au1x_pcm_queue_rx(struct au1xpsc_audio_dmadata *cd)
93{
94 au1xxx_dbdma_put_dest(cd->ddma_chan, cd->dma_area,
95 cd->period_bytes, DDMA_FLAGS_IE);
96
97
98 ++cd->q_period;
99 cd->dma_area += cd->period_bytes;
100 if (cd->q_period >= cd->periods) {
101 cd->q_period = 0;
102 cd->dma_area = cd->dma_area_s;
103 }
104}
105
106static void au1x_pcm_dmatx_cb(int irq, void *dev_id)
107{
108 struct au1xpsc_audio_dmadata *cd = dev_id;
109
110 cd->pos += cd->period_bytes;
111 if (++cd->curr_period >= cd->periods) {
112 cd->pos = 0;
113 cd->curr_period = 0;
114 }
115 snd_pcm_period_elapsed(cd->substream);
116 au1x_pcm_queue_tx(cd);
117}
118
119static void au1x_pcm_dmarx_cb(int irq, void *dev_id)
120{
121 struct au1xpsc_audio_dmadata *cd = dev_id;
122
123 cd->pos += cd->period_bytes;
124 if (++cd->curr_period >= cd->periods) {
125 cd->pos = 0;
126 cd->curr_period = 0;
127 }
128 snd_pcm_period_elapsed(cd->substream);
129 au1x_pcm_queue_rx(cd);
130}
131
132static void au1x_pcm_dbdma_free(struct au1xpsc_audio_dmadata *pcd)
133{
134 if (pcd->ddma_chan) {
135 au1xxx_dbdma_stop(pcd->ddma_chan);
136 au1xxx_dbdma_reset(pcd->ddma_chan);
137 au1xxx_dbdma_chan_free(pcd->ddma_chan);
138 pcd->ddma_chan = 0;
139 pcd->msbits = 0;
140 }
141}
142
143
144
145
146
147
148static int au1x_pcm_dbdma_realloc(struct au1xpsc_audio_dmadata *pcd,
149 int stype, int msbits)
150{
151
152 if (msbits == 24)
153 msbits = 32;
154
155
156 if ((pcd->ddma_chan) && (msbits == pcd->msbits))
157 goto out;
158
159 au1x_pcm_dbdma_free(pcd);
160
161 if (stype == SNDRV_PCM_STREAM_CAPTURE)
162 pcd->ddma_chan = au1xxx_dbdma_chan_alloc(pcd->ddma_id,
163 DSCR_CMD0_ALWAYS,
164 au1x_pcm_dmarx_cb, (void *)pcd);
165 else
166 pcd->ddma_chan = au1xxx_dbdma_chan_alloc(DSCR_CMD0_ALWAYS,
167 pcd->ddma_id,
168 au1x_pcm_dmatx_cb, (void *)pcd);
169
170 if (!pcd->ddma_chan)
171 return -ENOMEM;
172
173 au1xxx_dbdma_set_devwidth(pcd->ddma_chan, msbits);
174 au1xxx_dbdma_ring_alloc(pcd->ddma_chan, 2);
175
176 pcd->msbits = msbits;
177
178 au1xxx_dbdma_stop(pcd->ddma_chan);
179 au1xxx_dbdma_reset(pcd->ddma_chan);
180
181out:
182 return 0;
183}
184
185static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream *ss)
186{
187 struct snd_soc_pcm_runtime *rtd = ss->private_data;
188 struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
189 struct au1xpsc_audio_dmadata *pcd = snd_soc_component_get_drvdata(component);
190 return &pcd[ss->stream];
191}
192
193static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
194 struct snd_pcm_hw_params *params)
195{
196 struct snd_pcm_runtime *runtime = substream->runtime;
197 struct au1xpsc_audio_dmadata *pcd;
198 int stype, ret;
199
200 ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
201 if (ret < 0)
202 goto out;
203
204 stype = substream->stream;
205 pcd = to_dmadata(substream);
206
207 DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %zu "
208 "runtime->min_align %lu\n",
209 (unsigned long)runtime->dma_area,
210 (unsigned long)runtime->dma_addr, runtime->dma_bytes,
211 runtime->min_align);
212
213 DBG("bits %d frags %d frag_bytes %d is_rx %d\n", params->msbits,
214 params_periods(params), params_period_bytes(params), stype);
215
216 ret = au1x_pcm_dbdma_realloc(pcd, stype, params->msbits);
217 if (ret) {
218 MSG("DDMA channel (re)alloc failed!\n");
219 goto out;
220 }
221
222 pcd->substream = substream;
223 pcd->period_bytes = params_period_bytes(params);
224 pcd->periods = params_periods(params);
225 pcd->dma_area_s = pcd->dma_area = runtime->dma_addr;
226 pcd->q_period = 0;
227 pcd->curr_period = 0;
228 pcd->pos = 0;
229
230 ret = 0;
231out:
232 return ret;
233}
234
235static int au1xpsc_pcm_hw_free(struct snd_pcm_substream *substream)
236{
237 snd_pcm_lib_free_pages(substream);
238 return 0;
239}
240
241static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
242{
243 struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream);
244
245 au1xxx_dbdma_reset(pcd->ddma_chan);
246
247 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
248 au1x_pcm_queue_rx(pcd);
249 au1x_pcm_queue_rx(pcd);
250 } else {
251 au1x_pcm_queue_tx(pcd);
252 au1x_pcm_queue_tx(pcd);
253 }
254
255 return 0;
256}
257
258static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
259{
260 u32 c = to_dmadata(substream)->ddma_chan;
261
262 switch (cmd) {
263 case SNDRV_PCM_TRIGGER_START:
264 case SNDRV_PCM_TRIGGER_RESUME:
265 au1xxx_dbdma_start(c);
266 break;
267 case SNDRV_PCM_TRIGGER_STOP:
268 case SNDRV_PCM_TRIGGER_SUSPEND:
269 au1xxx_dbdma_stop(c);
270 break;
271 default:
272 return -EINVAL;
273 }
274 return 0;
275}
276
277static snd_pcm_uframes_t
278au1xpsc_pcm_pointer(struct snd_pcm_substream *substream)
279{
280 return bytes_to_frames(substream->runtime, to_dmadata(substream)->pos);
281}
282
283static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
284{
285 struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream);
286 struct snd_soc_pcm_runtime *rtd = substream->private_data;
287 int stype = substream->stream, *dmaids;
288
289 dmaids = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
290 if (!dmaids)
291 return -ENODEV;
292
293 pcd->ddma_id = dmaids[stype];
294
295 snd_soc_set_runtime_hwparams(substream, &au1xpsc_pcm_hardware);
296 return 0;
297}
298
299static int au1xpsc_pcm_close(struct snd_pcm_substream *substream)
300{
301 au1x_pcm_dbdma_free(to_dmadata(substream));
302 return 0;
303}
304
305static const struct snd_pcm_ops au1xpsc_pcm_ops = {
306 .open = au1xpsc_pcm_open,
307 .close = au1xpsc_pcm_close,
308 .ioctl = snd_pcm_lib_ioctl,
309 .hw_params = au1xpsc_pcm_hw_params,
310 .hw_free = au1xpsc_pcm_hw_free,
311 .prepare = au1xpsc_pcm_prepare,
312 .trigger = au1xpsc_pcm_trigger,
313 .pointer = au1xpsc_pcm_pointer,
314};
315
316static int au1xpsc_pcm_new(struct snd_soc_pcm_runtime *rtd)
317{
318 struct snd_card *card = rtd->card->snd_card;
319 struct snd_pcm *pcm = rtd->pcm;
320
321 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
322 card->dev, AU1XPSC_BUFFER_MIN_BYTES, (4096 * 1024) - 1);
323
324 return 0;
325}
326
327
328static struct snd_soc_component_driver au1xpsc_soc_component = {
329 .name = DRV_NAME,
330 .ops = &au1xpsc_pcm_ops,
331 .pcm_new = au1xpsc_pcm_new,
332};
333
334static int au1xpsc_pcm_drvprobe(struct platform_device *pdev)
335{
336 struct au1xpsc_audio_dmadata *dmadata;
337
338 dmadata = devm_kcalloc(&pdev->dev,
339 2, sizeof(struct au1xpsc_audio_dmadata),
340 GFP_KERNEL);
341 if (!dmadata)
342 return -ENOMEM;
343
344 platform_set_drvdata(pdev, dmadata);
345
346 return devm_snd_soc_register_component(&pdev->dev,
347 &au1xpsc_soc_component, NULL, 0);
348}
349
350static struct platform_driver au1xpsc_pcm_driver = {
351 .driver = {
352 .name = "au1xpsc-pcm",
353 },
354 .probe = au1xpsc_pcm_drvprobe,
355};
356
357module_platform_driver(au1xpsc_pcm_driver);
358
359MODULE_LICENSE("GPL");
360MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver");
361MODULE_AUTHOR("Manuel Lauss");
362