2 * imx-pcm-dma-mx2.c -- ALSA Soc Audio Layer
4 * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
6 * This code is based on code copyrighted by Freescale,
7 * Liam Girdwood, Javier Martin and probably others.
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
14 #include <linux/clk.h>
15 #include <linux/delay.h>
16 #include <linux/device.h>
17 #include <linux/dma-mapping.h>
18 #include <linux/init.h>
19 #include <linux/interrupt.h>
20 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/slab.h>
24 #include <sound/core.h>
25 #include <sound/initval.h>
26 #include <sound/pcm.h>
27 #include <sound/pcm_params.h>
28 #include <sound/soc.h>
30 #include <mach/dma-mx1-mx2.h>
34 struct imx_pcm_runtime_data
{
36 struct scatterlist
*sg_list
;
39 unsigned long dma_addr
;
41 struct snd_pcm_substream
*substream
;
44 unsigned long period_cnt
;
49 /* Called by the DMA framework when a period has elapsed */
50 static void imx_ssi_dma_progression(int channel
, void *data
,
51 struct scatterlist
*sg
)
53 struct snd_pcm_substream
*substream
= data
;
54 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
55 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
60 runtime
= iprtd
->substream
->runtime
;
62 iprtd
->offset
= sg
->dma_address
- runtime
->dma_addr
;
64 snd_pcm_period_elapsed(iprtd
->substream
);
67 static void imx_ssi_dma_callback(int channel
, void *data
)
69 pr_err("%s shouldn't be called\n", __func__
);
72 static void snd_imx_dma_err_callback(int channel
, void *data
, int err
)
74 struct snd_pcm_substream
*substream
= data
;
75 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
76 struct imx_pcm_dma_params
*dma_params
=
77 snd_soc_dai_get_dma_data(rtd
->dai
->cpu_dai
, substream
);
78 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
79 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
82 pr_err("DMA timeout on channel %d -%s%s%s%s\n",
84 err
& IMX_DMA_ERR_BURST
? " burst" : "",
85 err
& IMX_DMA_ERR_REQUEST
? " request" : "",
86 err
& IMX_DMA_ERR_TRANSFER
? " transfer" : "",
87 err
& IMX_DMA_ERR_BUFFER
? " buffer" : "");
89 imx_dma_disable(iprtd
->dma
);
90 ret
= imx_dma_setup_sg(iprtd
->dma
, iprtd
->sg_list
, iprtd
->sg_count
,
91 IMX_DMA_LENGTH_LOOP
, dma_params
->dma_addr
,
92 substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
?
93 DMA_MODE_WRITE
: DMA_MODE_READ
);
95 imx_dma_enable(iprtd
->dma
);
98 static int imx_ssi_dma_alloc(struct snd_pcm_substream
*substream
)
100 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
101 struct imx_pcm_dma_params
*dma_params
;
102 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
103 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
106 dma_params
= snd_soc_dai_get_dma_data(rtd
->dai
->cpu_dai
, substream
);
108 iprtd
->dma
= imx_dma_request_by_prio(DRV_NAME
, DMA_PRIO_HIGH
);
109 if (iprtd
->dma
< 0) {
110 pr_err("Failed to claim the audio DMA\n");
114 ret
= imx_dma_setup_handlers(iprtd
->dma
,
115 imx_ssi_dma_callback
,
116 snd_imx_dma_err_callback
, substream
);
120 ret
= imx_dma_setup_progression_handler(iprtd
->dma
,
121 imx_ssi_dma_progression
);
123 pr_err("Failed to setup the DMA handler\n");
127 ret
= imx_dma_config_channel(iprtd
->dma
,
128 IMX_DMA_MEMSIZE_16
| IMX_DMA_TYPE_FIFO
,
129 IMX_DMA_MEMSIZE_32
| IMX_DMA_TYPE_LINEAR
,
132 pr_err("Cannot configure DMA channel: %d\n", ret
);
136 imx_dma_config_burstlen(iprtd
->dma
, dma_params
->burstsize
* 2);
140 imx_dma_free(iprtd
->dma
);
144 static int snd_imx_pcm_hw_params(struct snd_pcm_substream
*substream
,
145 struct snd_pcm_hw_params
*params
)
147 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
148 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
150 unsigned long dma_addr
;
152 imx_ssi_dma_alloc(substream
);
154 iprtd
->size
= params_buffer_bytes(params
);
155 iprtd
->periods
= params_periods(params
);
156 iprtd
->period
= params_period_bytes(params
);
158 iprtd
->period_time
= HZ
/ (params_rate(params
) /
159 params_period_size(params
));
161 snd_pcm_set_runtime_buffer(substream
, &substream
->dma_buffer
);
163 if (iprtd
->sg_count
!= iprtd
->periods
) {
164 kfree(iprtd
->sg_list
);
166 iprtd
->sg_list
= kcalloc(iprtd
->periods
+ 1,
167 sizeof(struct scatterlist
), GFP_KERNEL
);
170 iprtd
->sg_count
= iprtd
->periods
+ 1;
173 sg_init_table(iprtd
->sg_list
, iprtd
->sg_count
);
174 dma_addr
= runtime
->dma_addr
;
176 for (i
= 0; i
< iprtd
->periods
; i
++) {
177 iprtd
->sg_list
[i
].page_link
= 0;
178 iprtd
->sg_list
[i
].offset
= 0;
179 iprtd
->sg_list
[i
].dma_address
= dma_addr
;
180 iprtd
->sg_list
[i
].length
= iprtd
->period
;
181 dma_addr
+= iprtd
->period
;
185 iprtd
->sg_list
[iprtd
->sg_count
- 1].offset
= 0;
186 iprtd
->sg_list
[iprtd
->sg_count
- 1].length
= 0;
187 iprtd
->sg_list
[iprtd
->sg_count
- 1].page_link
=
188 ((unsigned long) iprtd
->sg_list
| 0x01) & ~0x02;
192 static int snd_imx_pcm_hw_free(struct snd_pcm_substream
*substream
)
194 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
195 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
197 if (iprtd
->dma
>= 0) {
198 imx_dma_free(iprtd
->dma
);
199 iprtd
->dma
= -EINVAL
;
202 kfree(iprtd
->sg_list
);
203 iprtd
->sg_list
= NULL
;
208 static int snd_imx_pcm_prepare(struct snd_pcm_substream
*substream
)
210 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
211 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
212 struct imx_pcm_dma_params
*dma_params
;
213 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
216 dma_params
= snd_soc_dai_get_dma_data(rtd
->dai
->cpu_dai
, substream
);
218 iprtd
->substream
= substream
;
219 iprtd
->buf
= (unsigned int *)substream
->dma_buffer
.area
;
220 iprtd
->period_cnt
= 0;
222 pr_debug("%s: buf: %p period: %d periods: %d\n",
223 __func__
, iprtd
->buf
, iprtd
->period
, iprtd
->periods
);
225 err
= imx_dma_setup_sg(iprtd
->dma
, iprtd
->sg_list
, iprtd
->sg_count
,
226 IMX_DMA_LENGTH_LOOP
, dma_params
->dma_addr
,
227 substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
?
228 DMA_MODE_WRITE
: DMA_MODE_READ
);
235 static int snd_imx_pcm_trigger(struct snd_pcm_substream
*substream
, int cmd
)
237 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
238 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
241 case SNDRV_PCM_TRIGGER_START
:
242 case SNDRV_PCM_TRIGGER_RESUME
:
243 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
244 imx_dma_enable(iprtd
->dma
);
248 case SNDRV_PCM_TRIGGER_STOP
:
249 case SNDRV_PCM_TRIGGER_SUSPEND
:
250 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
251 imx_dma_disable(iprtd
->dma
);
261 static snd_pcm_uframes_t
snd_imx_pcm_pointer(struct snd_pcm_substream
*substream
)
263 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
264 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
266 return bytes_to_frames(substream
->runtime
, iprtd
->offset
);
269 static struct snd_pcm_hardware snd_imx_hardware
= {
270 .info
= SNDRV_PCM_INFO_INTERLEAVED
|
271 SNDRV_PCM_INFO_BLOCK_TRANSFER
|
272 SNDRV_PCM_INFO_MMAP
|
273 SNDRV_PCM_INFO_MMAP_VALID
|
274 SNDRV_PCM_INFO_PAUSE
|
275 SNDRV_PCM_INFO_RESUME
,
276 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,
280 .buffer_bytes_max
= IMX_SSI_DMABUF_SIZE
,
281 .period_bytes_min
= 128,
282 .period_bytes_max
= 16 * 1024,
288 static int snd_imx_open(struct snd_pcm_substream
*substream
)
290 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
291 struct imx_pcm_runtime_data
*iprtd
;
294 iprtd
= kzalloc(sizeof(*iprtd
), GFP_KERNEL
);
295 runtime
->private_data
= iprtd
;
297 ret
= snd_pcm_hw_constraint_integer(substream
->runtime
,
298 SNDRV_PCM_HW_PARAM_PERIODS
);
302 snd_soc_set_runtime_hwparams(substream
, &snd_imx_hardware
);
306 static struct snd_pcm_ops imx_pcm_ops
= {
307 .open
= snd_imx_open
,
308 .ioctl
= snd_pcm_lib_ioctl
,
309 .hw_params
= snd_imx_pcm_hw_params
,
310 .hw_free
= snd_imx_pcm_hw_free
,
311 .prepare
= snd_imx_pcm_prepare
,
312 .trigger
= snd_imx_pcm_trigger
,
313 .pointer
= snd_imx_pcm_pointer
,
314 .mmap
= snd_imx_pcm_mmap
,
317 static struct snd_soc_platform imx_soc_platform_dma
= {
319 .pcm_ops
= &imx_pcm_ops
,
320 .pcm_new
= imx_pcm_new
,
321 .pcm_free
= imx_pcm_free
,
324 struct snd_soc_platform
*imx_ssi_dma_mx2_init(struct platform_device
*pdev
,
327 ssi
->dma_params_tx
.burstsize
= DMA_TXFIFO_BURST
;
328 ssi
->dma_params_rx
.burstsize
= DMA_RXFIFO_BURST
;
330 return &imx_soc_platform_dma
;