2 * imx-pcm-fiq.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>
36 struct imx_pcm_runtime_data
{
40 unsigned long last_offset
;
42 struct timer_list timer
;
46 static inline void imx_ssi_set_next_poll(struct imx_pcm_runtime_data
*iprtd
)
48 iprtd
->timer
.expires
= jiffies
+ iprtd
->poll_time
;
51 static void imx_ssi_timer_callback(unsigned long data
)
53 struct snd_pcm_substream
*substream
= (void *)data
;
54 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
55 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
61 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
62 iprtd
->offset
= regs
.ARM_r8
& 0xffff;
64 iprtd
->offset
= regs
.ARM_r9
& 0xffff;
66 /* How much data have we transferred since the last period report? */
67 if (iprtd
->offset
>= iprtd
->last_offset
)
68 delta
= iprtd
->offset
- iprtd
->last_offset
;
70 delta
= runtime
->buffer_size
+ iprtd
->offset
73 /* If we've transferred at least a period then report it and
74 * reset our poll time */
75 if (delta
>= runtime
->period_size
) {
76 snd_pcm_period_elapsed(substream
);
77 iprtd
->last_offset
= iprtd
->offset
;
79 imx_ssi_set_next_poll(iprtd
);
82 /* Restart the timer; if we didn't report we'll run on the next tick */
83 add_timer(&iprtd
->timer
);
87 static struct fiq_handler fh
= {
91 static int snd_imx_pcm_hw_params(struct snd_pcm_substream
*substream
,
92 struct snd_pcm_hw_params
*params
)
94 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
95 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
97 iprtd
->size
= params_buffer_bytes(params
);
98 iprtd
->periods
= params_periods(params
);
99 iprtd
->period
= params_period_bytes(params
) ;
101 iprtd
->last_offset
= 0;
102 iprtd
->poll_time
= HZ
/ (params_rate(params
) / params_period_size(params
));
104 snd_pcm_set_runtime_buffer(substream
, &substream
->dma_buffer
);
109 static int snd_imx_pcm_prepare(struct snd_pcm_substream
*substream
)
111 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
112 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
116 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
117 regs
.ARM_r8
= (iprtd
->period
* iprtd
->periods
- 1) << 16;
119 regs
.ARM_r9
= (iprtd
->period
* iprtd
->periods
- 1) << 16;
126 static int fiq_enable
;
127 static int imx_pcm_fiq
;
129 static int snd_imx_pcm_trigger(struct snd_pcm_substream
*substream
, int cmd
)
131 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
132 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
135 case SNDRV_PCM_TRIGGER_START
:
136 case SNDRV_PCM_TRIGGER_RESUME
:
137 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
138 imx_ssi_set_next_poll(iprtd
);
139 add_timer(&iprtd
->timer
);
140 if (++fiq_enable
== 1)
141 enable_fiq(imx_pcm_fiq
);
145 case SNDRV_PCM_TRIGGER_STOP
:
146 case SNDRV_PCM_TRIGGER_SUSPEND
:
147 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
148 del_timer(&iprtd
->timer
);
149 if (--fiq_enable
== 0)
150 disable_fiq(imx_pcm_fiq
);
161 static snd_pcm_uframes_t
snd_imx_pcm_pointer(struct snd_pcm_substream
*substream
)
163 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
164 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
166 return bytes_to_frames(substream
->runtime
, iprtd
->offset
);
169 static struct snd_pcm_hardware snd_imx_hardware
= {
170 .info
= SNDRV_PCM_INFO_INTERLEAVED
|
171 SNDRV_PCM_INFO_BLOCK_TRANSFER
|
172 SNDRV_PCM_INFO_MMAP
|
173 SNDRV_PCM_INFO_MMAP_VALID
|
174 SNDRV_PCM_INFO_PAUSE
|
175 SNDRV_PCM_INFO_RESUME
,
176 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,
180 .buffer_bytes_max
= IMX_SSI_DMABUF_SIZE
,
181 .period_bytes_min
= 128,
182 .period_bytes_max
= 16 * 1024,
188 static int snd_imx_open(struct snd_pcm_substream
*substream
)
190 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
191 struct imx_pcm_runtime_data
*iprtd
;
194 iprtd
= kzalloc(sizeof(*iprtd
), GFP_KERNEL
);
195 runtime
->private_data
= iprtd
;
197 init_timer(&iprtd
->timer
);
198 iprtd
->timer
.data
= (unsigned long)substream
;
199 iprtd
->timer
.function
= imx_ssi_timer_callback
;
201 ret
= snd_pcm_hw_constraint_integer(substream
->runtime
,
202 SNDRV_PCM_HW_PARAM_PERIODS
);
206 snd_soc_set_runtime_hwparams(substream
, &snd_imx_hardware
);
210 static int snd_imx_close(struct snd_pcm_substream
*substream
)
212 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
213 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
215 del_timer_sync(&iprtd
->timer
);
221 static struct snd_pcm_ops imx_pcm_ops
= {
222 .open
= snd_imx_open
,
223 .close
= snd_imx_close
,
224 .ioctl
= snd_pcm_lib_ioctl
,
225 .hw_params
= snd_imx_pcm_hw_params
,
226 .prepare
= snd_imx_pcm_prepare
,
227 .trigger
= snd_imx_pcm_trigger
,
228 .pointer
= snd_imx_pcm_pointer
,
229 .mmap
= snd_imx_pcm_mmap
,
232 static int imx_pcm_fiq_new(struct snd_card
*card
, struct snd_soc_dai
*dai
,
237 ret
= imx_pcm_new(card
, dai
, pcm
);
241 if (dai
->playback
.channels_min
) {
242 struct snd_pcm_substream
*substream
=
243 pcm
->streams
[SNDRV_PCM_STREAM_PLAYBACK
].substream
;
244 struct snd_dma_buffer
*buf
= &substream
->dma_buffer
;
246 imx_ssi_fiq_tx_buffer
= (unsigned long)buf
->area
;
249 if (dai
->capture
.channels_min
) {
250 struct snd_pcm_substream
*substream
=
251 pcm
->streams
[SNDRV_PCM_STREAM_CAPTURE
].substream
;
252 struct snd_dma_buffer
*buf
= &substream
->dma_buffer
;
254 imx_ssi_fiq_rx_buffer
= (unsigned long)buf
->area
;
257 set_fiq_handler(&imx_ssi_fiq_start
,
258 &imx_ssi_fiq_end
- &imx_ssi_fiq_start
);
263 static struct snd_soc_platform imx_soc_platform_fiq
= {
264 .pcm_ops
= &imx_pcm_ops
,
265 .pcm_new
= imx_pcm_fiq_new
,
266 .pcm_free
= imx_pcm_free
,
269 struct snd_soc_platform
*imx_ssi_fiq_init(struct platform_device
*pdev
,
274 ret
= claim_fiq(&fh
);
276 dev_err(&pdev
->dev
, "failed to claim fiq: %d", ret
);
280 mxc_set_irq_fiq(ssi
->irq
, 1);
282 imx_pcm_fiq
= ssi
->irq
;
284 imx_ssi_fiq_base
= (unsigned long)ssi
->base
;
286 ssi
->dma_params_tx
.burstsize
= 4;
287 ssi
->dma_params_rx
.burstsize
= 6;
289 return &imx_soc_platform_fiq
;
292 void imx_ssi_fiq_exit(struct platform_device
*pdev
,
295 mxc_set_irq_fiq(ssi
->irq
, 0);