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>
23 #include <sound/core.h>
24 #include <sound/initval.h>
25 #include <sound/pcm.h>
26 #include <sound/pcm_params.h>
27 #include <sound/soc.h>
35 struct imx_pcm_runtime_data
{
38 unsigned long dma_addr
;
42 unsigned long period_cnt
;
44 struct timer_list timer
;
48 static void imx_ssi_timer_callback(unsigned long data
)
50 struct snd_pcm_substream
*substream
= (void *)data
;
51 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
52 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
57 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
58 iprtd
->offset
= regs
.ARM_r8
& 0xffff;
60 iprtd
->offset
= regs
.ARM_r9
& 0xffff;
62 iprtd
->timer
.expires
= jiffies
+ iprtd
->period_time
;
63 add_timer(&iprtd
->timer
);
64 snd_pcm_period_elapsed(substream
);
67 static struct fiq_handler fh
= {
71 static int snd_imx_pcm_hw_params(struct snd_pcm_substream
*substream
,
72 struct snd_pcm_hw_params
*params
)
74 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
75 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
77 iprtd
->size
= params_buffer_bytes(params
);
78 iprtd
->periods
= params_periods(params
);
79 iprtd
->period
= params_period_bytes(params
);
81 iprtd
->period_time
= HZ
/ (params_rate(params
) / params_period_size(params
));
83 snd_pcm_set_runtime_buffer(substream
, &substream
->dma_buffer
);
88 static int snd_imx_pcm_prepare(struct snd_pcm_substream
*substream
)
90 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
91 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
95 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
96 regs
.ARM_r8
= (iprtd
->period
* iprtd
->periods
- 1) << 16;
98 regs
.ARM_r9
= (iprtd
->period
* iprtd
->periods
- 1) << 16;
105 static int fiq_enable
;
106 static int imx_pcm_fiq
;
108 static int snd_imx_pcm_trigger(struct snd_pcm_substream
*substream
, int cmd
)
110 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
111 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
114 case SNDRV_PCM_TRIGGER_START
:
115 case SNDRV_PCM_TRIGGER_RESUME
:
116 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
117 iprtd
->timer
.expires
= jiffies
+ iprtd
->period_time
;
118 add_timer(&iprtd
->timer
);
119 if (++fiq_enable
== 1)
120 enable_fiq(imx_pcm_fiq
);
124 case SNDRV_PCM_TRIGGER_STOP
:
125 case SNDRV_PCM_TRIGGER_SUSPEND
:
126 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
127 del_timer(&iprtd
->timer
);
128 if (--fiq_enable
== 0)
129 disable_fiq(imx_pcm_fiq
);
140 static snd_pcm_uframes_t
snd_imx_pcm_pointer(struct snd_pcm_substream
*substream
)
142 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
143 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
145 return bytes_to_frames(substream
->runtime
, iprtd
->offset
);
148 static struct snd_pcm_hardware snd_imx_hardware
= {
149 .info
= SNDRV_PCM_INFO_INTERLEAVED
|
150 SNDRV_PCM_INFO_BLOCK_TRANSFER
|
151 SNDRV_PCM_INFO_MMAP
|
152 SNDRV_PCM_INFO_MMAP_VALID
|
153 SNDRV_PCM_INFO_PAUSE
|
154 SNDRV_PCM_INFO_RESUME
,
155 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,
159 .buffer_bytes_max
= IMX_SSI_DMABUF_SIZE
,
160 .period_bytes_min
= 128,
161 .period_bytes_max
= 16 * 1024,
167 static int snd_imx_open(struct snd_pcm_substream
*substream
)
169 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
170 struct imx_pcm_runtime_data
*iprtd
;
173 iprtd
= kzalloc(sizeof(*iprtd
), GFP_KERNEL
);
174 runtime
->private_data
= iprtd
;
176 init_timer(&iprtd
->timer
);
177 iprtd
->timer
.data
= (unsigned long)substream
;
178 iprtd
->timer
.function
= imx_ssi_timer_callback
;
180 ret
= snd_pcm_hw_constraint_integer(substream
->runtime
,
181 SNDRV_PCM_HW_PARAM_PERIODS
);
185 snd_soc_set_runtime_hwparams(substream
, &snd_imx_hardware
);
189 static int snd_imx_close(struct snd_pcm_substream
*substream
)
191 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
192 struct imx_pcm_runtime_data
*iprtd
= runtime
->private_data
;
194 del_timer_sync(&iprtd
->timer
);
200 static struct snd_pcm_ops imx_pcm_ops
= {
201 .open
= snd_imx_open
,
202 .close
= snd_imx_close
,
203 .ioctl
= snd_pcm_lib_ioctl
,
204 .hw_params
= snd_imx_pcm_hw_params
,
205 .prepare
= snd_imx_pcm_prepare
,
206 .trigger
= snd_imx_pcm_trigger
,
207 .pointer
= snd_imx_pcm_pointer
,
208 .mmap
= snd_imx_pcm_mmap
,
211 static int imx_pcm_fiq_new(struct snd_card
*card
, struct snd_soc_dai
*dai
,
216 ret
= imx_pcm_new(card
, dai
, pcm
);
220 if (dai
->playback
.channels_min
) {
221 struct snd_pcm_substream
*substream
=
222 pcm
->streams
[SNDRV_PCM_STREAM_PLAYBACK
].substream
;
223 struct snd_dma_buffer
*buf
= &substream
->dma_buffer
;
225 imx_ssi_fiq_tx_buffer
= (unsigned long)buf
->area
;
228 if (dai
->capture
.channels_min
) {
229 struct snd_pcm_substream
*substream
=
230 pcm
->streams
[SNDRV_PCM_STREAM_CAPTURE
].substream
;
231 struct snd_dma_buffer
*buf
= &substream
->dma_buffer
;
233 imx_ssi_fiq_rx_buffer
= (unsigned long)buf
->area
;
236 set_fiq_handler(&imx_ssi_fiq_start
,
237 &imx_ssi_fiq_end
- &imx_ssi_fiq_start
);
242 static struct snd_soc_platform imx_soc_platform_fiq
= {
243 .pcm_ops
= &imx_pcm_ops
,
244 .pcm_new
= imx_pcm_fiq_new
,
245 .pcm_free
= imx_pcm_free
,
248 struct snd_soc_platform
*imx_ssi_fiq_init(struct platform_device
*pdev
,
253 ret
= claim_fiq(&fh
);
255 dev_err(&pdev
->dev
, "failed to claim fiq: %d", ret
);
259 mxc_set_irq_fiq(ssi
->irq
, 1);
261 imx_pcm_fiq
= ssi
->irq
;
263 imx_ssi_fiq_base
= (unsigned long)ssi
->base
;
265 ssi
->dma_params_tx
.burstsize
= 4;
266 ssi
->dma_params_rx
.burstsize
= 6;
268 return &imx_soc_platform_fiq
;
271 void imx_ssi_fiq_exit(struct platform_device
*pdev
,
274 mxc_set_irq_fiq(ssi
->irq
, 0);