4 * (c) 2010 Arnaud Patard <apatard@mandriva.com>
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
12 #include <linux/init.h>
13 #include <linux/module.h>
14 #include <linux/device.h>
16 #include <linux/slab.h>
17 #include <linux/interrupt.h>
18 #include <linux/dma-mapping.h>
19 #include <linux/mbus.h>
20 #include <sound/soc.h>
21 #include "kirkwood-dma.h"
24 #define KIRKWOOD_RATES \
25 (SNDRV_PCM_RATE_44100 | \
26 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
27 #define KIRKWOOD_FORMATS \
28 (SNDRV_PCM_FMTBIT_S16_LE | \
29 SNDRV_PCM_FMTBIT_S24_LE | \
30 SNDRV_PCM_FMTBIT_S32_LE)
32 struct kirkwood_dma_priv
{
33 struct snd_pcm_substream
*play_stream
;
34 struct snd_pcm_substream
*rec_stream
;
35 struct kirkwood_dma_data
*data
;
38 static struct snd_pcm_hardware kirkwood_dma_snd_hw
= {
39 .info
= (SNDRV_PCM_INFO_INTERLEAVED
|
41 SNDRV_PCM_INFO_MMAP_VALID
|
42 SNDRV_PCM_INFO_BLOCK_TRANSFER
|
43 SNDRV_PCM_INFO_PAUSE
),
44 .formats
= KIRKWOOD_FORMATS
,
45 .rates
= KIRKWOOD_RATES
,
50 .buffer_bytes_max
= KIRKWOOD_SND_MAX_PERIOD_BYTES
* KIRKWOOD_SND_MAX_PERIODS
,
51 .period_bytes_min
= KIRKWOOD_SND_MIN_PERIOD_BYTES
,
52 .period_bytes_max
= KIRKWOOD_SND_MAX_PERIOD_BYTES
,
53 .periods_min
= KIRKWOOD_SND_MIN_PERIODS
,
54 .periods_max
= KIRKWOOD_SND_MAX_PERIODS
,
58 static u64 kirkwood_dma_dmamask
= 0xFFFFFFFFUL
;
60 static irqreturn_t
kirkwood_dma_irq(int irq
, void *dev_id
)
62 struct kirkwood_dma_priv
*prdata
= dev_id
;
63 struct kirkwood_dma_data
*priv
= prdata
->data
;
64 unsigned long mask
, status
, cause
;
66 mask
= readl(priv
->io
+ KIRKWOOD_INT_MASK
);
67 status
= readl(priv
->io
+ KIRKWOOD_INT_CAUSE
) & mask
;
69 cause
= readl(priv
->io
+ KIRKWOOD_ERR_CAUSE
);
70 if (unlikely(cause
)) {
71 printk(KERN_WARNING
"%s: got err interrupt 0x%lx\n",
73 writel(cause
, priv
->io
+ KIRKWOOD_ERR_CAUSE
);
77 /* we've enabled only bytes interrupts ... */
78 if (status
& ~(KIRKWOOD_INT_CAUSE_PLAY_BYTES
| \
79 KIRKWOOD_INT_CAUSE_REC_BYTES
)) {
80 printk(KERN_WARNING
"%s: unexpected interrupt %lx\n",
86 writel(status
, priv
->io
+ KIRKWOOD_INT_CAUSE
);
88 if (status
& KIRKWOOD_INT_CAUSE_PLAY_BYTES
)
89 snd_pcm_period_elapsed(prdata
->play_stream
);
91 if (status
& KIRKWOOD_INT_CAUSE_REC_BYTES
)
92 snd_pcm_period_elapsed(prdata
->rec_stream
);
97 static void kirkwood_dma_conf_mbus_windows(void __iomem
*base
, int win
,
99 struct mbus_dram_target_info
*dram
)
103 /* First disable and clear windows */
104 writel(0, base
+ KIRKWOOD_AUDIO_WIN_CTRL_REG(win
));
105 writel(0, base
+ KIRKWOOD_AUDIO_WIN_BASE_REG(win
));
107 /* try to find matching cs for current dma address */
108 for (i
= 0; i
< dram
->num_cs
; i
++) {
109 struct mbus_dram_window
*cs
= dram
->cs
+ i
;
110 if ((cs
->base
& 0xffff0000) < (dma
& 0xffff0000)) {
111 writel(cs
->base
& 0xffff0000,
112 base
+ KIRKWOOD_AUDIO_WIN_BASE_REG(win
));
113 writel(((cs
->size
- 1) & 0xffff0000) |
114 (cs
->mbus_attr
<< 8) |
115 (dram
->mbus_dram_target_id
<< 4) | 1,
116 base
+ KIRKWOOD_AUDIO_WIN_CTRL_REG(win
));
121 static int kirkwood_dma_open(struct snd_pcm_substream
*substream
)
124 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
125 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
126 struct snd_soc_dai
*cpu_dai
= soc_runtime
->dai
->cpu_dai
;
127 struct kirkwood_dma_data
*priv
;
128 struct kirkwood_dma_priv
*prdata
= cpu_dai
->private_data
;
131 priv
= snd_soc_dai_get_dma_data(cpu_dai
, substream
);
132 snd_soc_set_runtime_hwparams(substream
, &kirkwood_dma_snd_hw
);
134 /* Ensure that all constraints linked to dma burst are fullfilled */
135 err
= snd_pcm_hw_constraint_minmax(runtime
,
136 SNDRV_PCM_HW_PARAM_BUFFER_BYTES
,
138 KIRKWOOD_AUDIO_BUF_MAX
-1);
142 err
= snd_pcm_hw_constraint_step(runtime
, 0,
143 SNDRV_PCM_HW_PARAM_BUFFER_BYTES
,
148 err
= snd_pcm_hw_constraint_step(substream
->runtime
, 0,
149 SNDRV_PCM_HW_PARAM_PERIOD_BYTES
,
154 if (soc_runtime
->dai
->cpu_dai
->private_data
== NULL
) {
155 prdata
= kzalloc(sizeof(struct kirkwood_dma_priv
), GFP_KERNEL
);
161 err
= request_irq(priv
->irq
, kirkwood_dma_irq
, IRQF_SHARED
,
162 "kirkwood-i2s", prdata
);
168 soc_runtime
->dai
->cpu_dai
->private_data
= prdata
;
171 * Enable Error interrupts. We're only ack'ing them but
172 * it's usefull for diagnostics
174 writel((unsigned long)-1, priv
->io
+ KIRKWOOD_ERR_MASK
);
177 addr
= virt_to_phys(substream
->dma_buffer
.area
);
178 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
179 prdata
->play_stream
= substream
;
180 kirkwood_dma_conf_mbus_windows(priv
->io
,
181 KIRKWOOD_PLAYBACK_WIN
, addr
, priv
->dram
);
183 prdata
->rec_stream
= substream
;
184 kirkwood_dma_conf_mbus_windows(priv
->io
,
185 KIRKWOOD_RECORD_WIN
, addr
, priv
->dram
);
191 static int kirkwood_dma_close(struct snd_pcm_substream
*substream
)
193 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
194 struct snd_soc_dai
*cpu_dai
= soc_runtime
->dai
->cpu_dai
;
195 struct kirkwood_dma_priv
*prdata
= cpu_dai
->private_data
;
196 struct kirkwood_dma_data
*priv
;
198 priv
= snd_soc_dai_get_dma_data(cpu_dai
, substream
);
200 if (!prdata
|| !priv
)
203 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
204 prdata
->play_stream
= NULL
;
206 prdata
->rec_stream
= NULL
;
208 if (!prdata
->play_stream
&& !prdata
->rec_stream
) {
209 writel(0, priv
->io
+ KIRKWOOD_ERR_MASK
);
210 free_irq(priv
->irq
, prdata
);
212 soc_runtime
->dai
->cpu_dai
->private_data
= NULL
;
218 static int kirkwood_dma_hw_params(struct snd_pcm_substream
*substream
,
219 struct snd_pcm_hw_params
*params
)
221 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
223 snd_pcm_set_runtime_buffer(substream
, &substream
->dma_buffer
);
224 runtime
->dma_bytes
= params_buffer_bytes(params
);
229 static int kirkwood_dma_hw_free(struct snd_pcm_substream
*substream
)
231 snd_pcm_set_runtime_buffer(substream
, NULL
);
235 static int kirkwood_dma_prepare(struct snd_pcm_substream
*substream
)
237 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
238 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
239 struct snd_soc_dai
*cpu_dai
= soc_runtime
->dai
->cpu_dai
;
240 struct kirkwood_dma_data
*priv
;
241 unsigned long size
, count
;
243 priv
= snd_soc_dai_get_dma_data(cpu_dai
, substream
);
245 /* compute buffer size in term of "words" as requested in specs */
246 size
= frames_to_bytes(runtime
, runtime
->buffer_size
);
248 count
= snd_pcm_lib_period_bytes(substream
);
250 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
251 writel(count
, priv
->io
+ KIRKWOOD_PLAY_BYTE_INT_COUNT
);
252 writel(runtime
->dma_addr
, priv
->io
+ KIRKWOOD_PLAY_BUF_ADDR
);
253 writel(size
, priv
->io
+ KIRKWOOD_PLAY_BUF_SIZE
);
255 writel(count
, priv
->io
+ KIRKWOOD_REC_BYTE_INT_COUNT
);
256 writel(runtime
->dma_addr
, priv
->io
+ KIRKWOOD_REC_BUF_ADDR
);
257 writel(size
, priv
->io
+ KIRKWOOD_REC_BUF_SIZE
);
264 static snd_pcm_uframes_t
kirkwood_dma_pointer(struct snd_pcm_substream
267 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
268 struct snd_soc_dai
*cpu_dai
= soc_runtime
->dai
->cpu_dai
;
269 struct kirkwood_dma_data
*priv
;
270 snd_pcm_uframes_t count
;
272 priv
= snd_soc_dai_get_dma_data(cpu_dai
, substream
);
274 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
275 count
= bytes_to_frames(substream
->runtime
,
276 readl(priv
->io
+ KIRKWOOD_PLAY_BYTE_COUNT
));
278 count
= bytes_to_frames(substream
->runtime
,
279 readl(priv
->io
+ KIRKWOOD_REC_BYTE_COUNT
));
284 struct snd_pcm_ops kirkwood_dma_ops
= {
285 .open
= kirkwood_dma_open
,
286 .close
= kirkwood_dma_close
,
287 .ioctl
= snd_pcm_lib_ioctl
,
288 .hw_params
= kirkwood_dma_hw_params
,
289 .hw_free
= kirkwood_dma_hw_free
,
290 .prepare
= kirkwood_dma_prepare
,
291 .pointer
= kirkwood_dma_pointer
,
294 static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm
*pcm
,
297 struct snd_pcm_substream
*substream
= pcm
->streams
[stream
].substream
;
298 struct snd_dma_buffer
*buf
= &substream
->dma_buffer
;
299 size_t size
= kirkwood_dma_snd_hw
.buffer_bytes_max
;
301 buf
->dev
.type
= SNDRV_DMA_TYPE_DEV
;
302 buf
->dev
.dev
= pcm
->card
->dev
;
303 buf
->area
= dma_alloc_coherent(pcm
->card
->dev
, size
,
304 &buf
->addr
, GFP_KERNEL
);
308 buf
->private_data
= NULL
;
313 static int kirkwood_dma_new(struct snd_card
*card
,
314 struct snd_soc_dai
*dai
, struct snd_pcm
*pcm
)
318 if (!card
->dev
->dma_mask
)
319 card
->dev
->dma_mask
= &kirkwood_dma_dmamask
;
320 if (!card
->dev
->coherent_dma_mask
)
321 card
->dev
->coherent_dma_mask
= 0xffffffff;
323 if (dai
->playback
.channels_min
) {
324 ret
= kirkwood_dma_preallocate_dma_buffer(pcm
,
325 SNDRV_PCM_STREAM_PLAYBACK
);
330 if (dai
->capture
.channels_min
) {
331 ret
= kirkwood_dma_preallocate_dma_buffer(pcm
,
332 SNDRV_PCM_STREAM_CAPTURE
);
340 static void kirkwood_dma_free_dma_buffers(struct snd_pcm
*pcm
)
342 struct snd_pcm_substream
*substream
;
343 struct snd_dma_buffer
*buf
;
346 for (stream
= 0; stream
< 2; stream
++) {
347 substream
= pcm
->streams
[stream
].substream
;
350 buf
= &substream
->dma_buffer
;
354 dma_free_coherent(pcm
->card
->dev
, buf
->bytes
,
355 buf
->area
, buf
->addr
);
360 struct snd_soc_platform kirkwood_soc_platform
= {
361 .name
= "kirkwood-dma",
362 .pcm_ops
= &kirkwood_dma_ops
,
363 .pcm_new
= kirkwood_dma_new
,
364 .pcm_free
= kirkwood_dma_free_dma_buffers
,
366 EXPORT_SYMBOL_GPL(kirkwood_soc_platform
);
368 static int __init
kirkwood_soc_platform_init(void)
370 return snd_soc_register_platform(&kirkwood_soc_platform
);
372 module_init(kirkwood_soc_platform_init
);
374 static void __exit
kirkwood_soc_platform_exit(void)
376 snd_soc_unregister_platform(&kirkwood_soc_platform
);
378 module_exit(kirkwood_soc_platform_exit
);
380 MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>");
381 MODULE_DESCRIPTION("Marvell Kirkwood Audio DMA module");
382 MODULE_LICENSE("GPL");