4 * (c) 2010 Arnaud Patard <apatard@mandriva.com>
5 * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
13 #include <linux/init.h>
14 #include <linux/module.h>
15 #include <linux/device.h>
17 #include <linux/slab.h>
18 #include <linux/interrupt.h>
19 #include <linux/dma-mapping.h>
20 #include <linux/mbus.h>
21 #include <sound/soc.h>
24 #define KIRKWOOD_RATES \
25 (SNDRV_PCM_RATE_8000_192000 | \
26 SNDRV_PCM_RATE_CONTINUOUS | \
29 #define KIRKWOOD_FORMATS \
30 (SNDRV_PCM_FMTBIT_S16_LE | \
31 SNDRV_PCM_FMTBIT_S24_LE | \
32 SNDRV_PCM_FMTBIT_S32_LE | \
33 SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE | \
34 SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE)
36 struct kirkwood_dma_priv
{
37 struct snd_pcm_substream
*play_stream
;
38 struct snd_pcm_substream
*rec_stream
;
39 struct kirkwood_dma_data
*data
;
42 static struct snd_pcm_hardware kirkwood_dma_snd_hw
= {
43 .info
= (SNDRV_PCM_INFO_INTERLEAVED
|
45 SNDRV_PCM_INFO_MMAP_VALID
|
46 SNDRV_PCM_INFO_BLOCK_TRANSFER
|
47 SNDRV_PCM_INFO_PAUSE
),
48 .formats
= KIRKWOOD_FORMATS
,
49 .rates
= KIRKWOOD_RATES
,
54 .buffer_bytes_max
= KIRKWOOD_SND_MAX_PERIOD_BYTES
* KIRKWOOD_SND_MAX_PERIODS
,
55 .period_bytes_min
= KIRKWOOD_SND_MIN_PERIOD_BYTES
,
56 .period_bytes_max
= KIRKWOOD_SND_MAX_PERIOD_BYTES
,
57 .periods_min
= KIRKWOOD_SND_MIN_PERIODS
,
58 .periods_max
= KIRKWOOD_SND_MAX_PERIODS
,
62 static u64 kirkwood_dma_dmamask
= DMA_BIT_MASK(32);
64 static irqreturn_t
kirkwood_dma_irq(int irq
, void *dev_id
)
66 struct kirkwood_dma_priv
*prdata
= dev_id
;
67 struct kirkwood_dma_data
*priv
= prdata
->data
;
68 unsigned long mask
, status
, cause
;
70 mask
= readl(priv
->io
+ KIRKWOOD_INT_MASK
);
71 status
= readl(priv
->io
+ KIRKWOOD_INT_CAUSE
) & mask
;
73 cause
= readl(priv
->io
+ KIRKWOOD_ERR_CAUSE
);
74 if (unlikely(cause
)) {
75 printk(KERN_WARNING
"%s: got err interrupt 0x%lx\n",
77 writel(cause
, priv
->io
+ KIRKWOOD_ERR_CAUSE
);
80 /* we've enabled only bytes interrupts ... */
81 if (status
& ~(KIRKWOOD_INT_CAUSE_PLAY_BYTES
| \
82 KIRKWOOD_INT_CAUSE_REC_BYTES
)) {
83 printk(KERN_WARNING
"%s: unexpected interrupt %lx\n",
89 writel(status
, priv
->io
+ KIRKWOOD_INT_CAUSE
);
91 if (status
& KIRKWOOD_INT_CAUSE_PLAY_BYTES
)
92 snd_pcm_period_elapsed(prdata
->play_stream
);
94 if (status
& KIRKWOOD_INT_CAUSE_REC_BYTES
)
95 snd_pcm_period_elapsed(prdata
->rec_stream
);
101 kirkwood_dma_conf_mbus_windows(void __iomem
*base
, int win
,
103 const struct mbus_dram_target_info
*dram
)
107 /* First disable and clear windows */
108 writel(0, base
+ KIRKWOOD_AUDIO_WIN_CTRL_REG(win
));
109 writel(0, base
+ KIRKWOOD_AUDIO_WIN_BASE_REG(win
));
111 /* try to find matching cs for current dma address */
112 for (i
= 0; i
< dram
->num_cs
; i
++) {
113 const struct mbus_dram_window
*cs
= dram
->cs
+ i
;
114 if ((cs
->base
& 0xffff0000) < (dma
& 0xffff0000)) {
115 writel(cs
->base
& 0xffff0000,
116 base
+ KIRKWOOD_AUDIO_WIN_BASE_REG(win
));
117 writel(((cs
->size
- 1) & 0xffff0000) |
118 (cs
->mbus_attr
<< 8) |
119 (dram
->mbus_dram_target_id
<< 4) | 1,
120 base
+ KIRKWOOD_AUDIO_WIN_CTRL_REG(win
));
125 static int kirkwood_dma_open(struct snd_pcm_substream
*substream
)
128 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
129 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
130 struct snd_soc_platform
*platform
= soc_runtime
->platform
;
131 struct snd_soc_dai
*cpu_dai
= soc_runtime
->cpu_dai
;
132 struct kirkwood_dma_data
*priv
;
133 struct kirkwood_dma_priv
*prdata
= snd_soc_platform_get_drvdata(platform
);
134 const struct mbus_dram_target_info
*dram
;
137 priv
= snd_soc_dai_get_dma_data(cpu_dai
, substream
);
138 snd_soc_set_runtime_hwparams(substream
, &kirkwood_dma_snd_hw
);
140 /* Ensure that all constraints linked to dma burst are fulfilled */
141 err
= snd_pcm_hw_constraint_minmax(runtime
,
142 SNDRV_PCM_HW_PARAM_BUFFER_BYTES
,
144 KIRKWOOD_AUDIO_BUF_MAX
-1);
148 err
= snd_pcm_hw_constraint_step(runtime
, 0,
149 SNDRV_PCM_HW_PARAM_BUFFER_BYTES
,
154 err
= snd_pcm_hw_constraint_step(substream
->runtime
, 0,
155 SNDRV_PCM_HW_PARAM_PERIOD_BYTES
,
160 if (prdata
== NULL
) {
161 prdata
= kzalloc(sizeof(struct kirkwood_dma_priv
), GFP_KERNEL
);
167 err
= request_irq(priv
->irq
, kirkwood_dma_irq
, IRQF_SHARED
,
168 "kirkwood-i2s", prdata
);
174 snd_soc_platform_set_drvdata(platform
, prdata
);
177 * Enable Error interrupts. We're only ack'ing them but
178 * it's useful for diagnostics
180 writel((unsigned long)-1, priv
->io
+ KIRKWOOD_ERR_MASK
);
183 dram
= mv_mbus_dram_info();
184 addr
= substream
->dma_buffer
.addr
;
185 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
186 prdata
->play_stream
= substream
;
187 kirkwood_dma_conf_mbus_windows(priv
->io
,
188 KIRKWOOD_PLAYBACK_WIN
, addr
, dram
);
190 prdata
->rec_stream
= substream
;
191 kirkwood_dma_conf_mbus_windows(priv
->io
,
192 KIRKWOOD_RECORD_WIN
, addr
, dram
);
198 static int kirkwood_dma_close(struct snd_pcm_substream
*substream
)
200 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
201 struct snd_soc_dai
*cpu_dai
= soc_runtime
->cpu_dai
;
202 struct snd_soc_platform
*platform
= soc_runtime
->platform
;
203 struct kirkwood_dma_priv
*prdata
= snd_soc_platform_get_drvdata(platform
);
204 struct kirkwood_dma_data
*priv
;
206 priv
= snd_soc_dai_get_dma_data(cpu_dai
, substream
);
208 if (!prdata
|| !priv
)
211 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
212 prdata
->play_stream
= NULL
;
214 prdata
->rec_stream
= NULL
;
216 if (!prdata
->play_stream
&& !prdata
->rec_stream
) {
217 writel(0, priv
->io
+ KIRKWOOD_ERR_MASK
);
218 free_irq(priv
->irq
, prdata
);
220 snd_soc_platform_set_drvdata(platform
, NULL
);
226 static int kirkwood_dma_hw_params(struct snd_pcm_substream
*substream
,
227 struct snd_pcm_hw_params
*params
)
229 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
231 snd_pcm_set_runtime_buffer(substream
, &substream
->dma_buffer
);
232 runtime
->dma_bytes
= params_buffer_bytes(params
);
237 static int kirkwood_dma_hw_free(struct snd_pcm_substream
*substream
)
239 snd_pcm_set_runtime_buffer(substream
, NULL
);
243 static int kirkwood_dma_prepare(struct snd_pcm_substream
*substream
)
245 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
246 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
247 struct snd_soc_dai
*cpu_dai
= soc_runtime
->cpu_dai
;
248 struct kirkwood_dma_data
*priv
;
249 unsigned long size
, count
;
251 priv
= snd_soc_dai_get_dma_data(cpu_dai
, substream
);
253 /* compute buffer size in term of "words" as requested in specs */
254 size
= frames_to_bytes(runtime
, runtime
->buffer_size
);
256 count
= snd_pcm_lib_period_bytes(substream
);
258 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
259 writel(count
, priv
->io
+ KIRKWOOD_PLAY_BYTE_INT_COUNT
);
260 writel(runtime
->dma_addr
, priv
->io
+ KIRKWOOD_PLAY_BUF_ADDR
);
261 writel(size
, priv
->io
+ KIRKWOOD_PLAY_BUF_SIZE
);
263 writel(count
, priv
->io
+ KIRKWOOD_REC_BYTE_INT_COUNT
);
264 writel(runtime
->dma_addr
, priv
->io
+ KIRKWOOD_REC_BUF_ADDR
);
265 writel(size
, priv
->io
+ KIRKWOOD_REC_BUF_SIZE
);
272 static snd_pcm_uframes_t
kirkwood_dma_pointer(struct snd_pcm_substream
275 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
276 struct snd_soc_dai
*cpu_dai
= soc_runtime
->cpu_dai
;
277 struct kirkwood_dma_data
*priv
;
278 snd_pcm_uframes_t count
;
280 priv
= snd_soc_dai_get_dma_data(cpu_dai
, substream
);
282 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
283 count
= bytes_to_frames(substream
->runtime
,
284 readl(priv
->io
+ KIRKWOOD_PLAY_BYTE_COUNT
));
286 count
= bytes_to_frames(substream
->runtime
,
287 readl(priv
->io
+ KIRKWOOD_REC_BYTE_COUNT
));
292 struct snd_pcm_ops kirkwood_dma_ops
= {
293 .open
= kirkwood_dma_open
,
294 .close
= kirkwood_dma_close
,
295 .ioctl
= snd_pcm_lib_ioctl
,
296 .hw_params
= kirkwood_dma_hw_params
,
297 .hw_free
= kirkwood_dma_hw_free
,
298 .prepare
= kirkwood_dma_prepare
,
299 .pointer
= kirkwood_dma_pointer
,
302 static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm
*pcm
,
305 struct snd_pcm_substream
*substream
= pcm
->streams
[stream
].substream
;
306 struct snd_dma_buffer
*buf
= &substream
->dma_buffer
;
307 size_t size
= kirkwood_dma_snd_hw
.buffer_bytes_max
;
309 buf
->dev
.type
= SNDRV_DMA_TYPE_DEV
;
310 buf
->dev
.dev
= pcm
->card
->dev
;
311 buf
->area
= dma_alloc_coherent(pcm
->card
->dev
, size
,
312 &buf
->addr
, GFP_KERNEL
);
316 buf
->private_data
= NULL
;
321 static int kirkwood_dma_new(struct snd_soc_pcm_runtime
*rtd
)
323 struct snd_card
*card
= rtd
->card
->snd_card
;
324 struct snd_pcm
*pcm
= rtd
->pcm
;
327 if (!card
->dev
->dma_mask
)
328 card
->dev
->dma_mask
= &kirkwood_dma_dmamask
;
329 if (!card
->dev
->coherent_dma_mask
)
330 card
->dev
->coherent_dma_mask
= DMA_BIT_MASK(32);
332 if (pcm
->streams
[SNDRV_PCM_STREAM_PLAYBACK
].substream
) {
333 ret
= kirkwood_dma_preallocate_dma_buffer(pcm
,
334 SNDRV_PCM_STREAM_PLAYBACK
);
339 if (pcm
->streams
[SNDRV_PCM_STREAM_CAPTURE
].substream
) {
340 ret
= kirkwood_dma_preallocate_dma_buffer(pcm
,
341 SNDRV_PCM_STREAM_CAPTURE
);
349 static void kirkwood_dma_free_dma_buffers(struct snd_pcm
*pcm
)
351 struct snd_pcm_substream
*substream
;
352 struct snd_dma_buffer
*buf
;
355 for (stream
= 0; stream
< 2; stream
++) {
356 substream
= pcm
->streams
[stream
].substream
;
359 buf
= &substream
->dma_buffer
;
363 dma_free_coherent(pcm
->card
->dev
, buf
->bytes
,
364 buf
->area
, buf
->addr
);
369 static struct snd_soc_platform_driver kirkwood_soc_platform
= {
370 .ops
= &kirkwood_dma_ops
,
371 .pcm_new
= kirkwood_dma_new
,
372 .pcm_free
= kirkwood_dma_free_dma_buffers
,
375 static int kirkwood_soc_platform_probe(struct platform_device
*pdev
)
377 return snd_soc_register_platform(&pdev
->dev
, &kirkwood_soc_platform
);
380 static int kirkwood_soc_platform_remove(struct platform_device
*pdev
)
382 snd_soc_unregister_platform(&pdev
->dev
);
386 static struct platform_driver kirkwood_pcm_driver
= {
388 .name
= "kirkwood-pcm-audio",
389 .owner
= THIS_MODULE
,
392 .probe
= kirkwood_soc_platform_probe
,
393 .remove
= kirkwood_soc_platform_remove
,
396 module_platform_driver(kirkwood_pcm_driver
);
398 MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
399 MODULE_DESCRIPTION("Marvell Kirkwood Audio DMA module");
400 MODULE_LICENSE("GPL");
401 MODULE_ALIAS("platform:kirkwood-pcm-audio");