diff options
Diffstat (limited to 'sound/soc/imx')
-rw-r--r-- | sound/soc/imx/Kconfig | 20 | ||||
-rw-r--r-- | sound/soc/imx/Makefile | 14 | ||||
-rw-r--r-- | sound/soc/imx/imx-pcm-dma-mx2.c | 313 | ||||
-rw-r--r-- | sound/soc/imx/imx-pcm-fiq.c | 297 | ||||
-rw-r--r-- | sound/soc/imx/imx-ssi.c | 758 | ||||
-rw-r--r-- | sound/soc/imx/imx-ssi.h | 237 | ||||
-rw-r--r-- | sound/soc/imx/mx1_mx2-pcm.c | 488 | ||||
-rw-r--r-- | sound/soc/imx/mx1_mx2-pcm.h | 26 | ||||
-rw-r--r-- | sound/soc/imx/mx27vis_wm8974.c | 318 | ||||
-rw-r--r-- | sound/soc/imx/mxc-ssi.c | 860 | ||||
-rw-r--r-- | sound/soc/imx/mxc-ssi.h | 238 | ||||
-rw-r--r-- | sound/soc/imx/phycore-ac97.c | 90 |
12 files changed, 1709 insertions, 1950 deletions
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig index a700562e8692..c7d0fd9b7de8 100644 --- a/sound/soc/imx/Kconfig +++ b/sound/soc/imx/Kconfig | |||
@@ -1,21 +1,13 @@ | |||
1 | config SND_MX1_MX2_SOC | 1 | config SND_IMX_SOC |
2 | tristate "SoC Audio for Freecale i.MX1x i.MX2x CPUs" | 2 | tristate "SoC Audio for Freescale i.MX CPUs" |
3 | depends on ARCH_MX2 || ARCH_MX1 | 3 | depends on ARCH_MXC && BROKEN |
4 | select SND_PCM | 4 | select SND_PCM |
5 | select FIQ | ||
6 | select SND_SOC_AC97_BUS | ||
5 | help | 7 | help |
6 | Say Y or M if you want to add support for codecs attached to | 8 | Say Y or M if you want to add support for codecs attached to |
7 | the MX1 or MX2 SSI interface. | 9 | the i.MX SSI interface. |
8 | 10 | ||
9 | config SND_MXC_SOC_SSI | 11 | config SND_MXC_SOC_SSI |
10 | tristate | 12 | tristate |
11 | 13 | ||
12 | config SND_SOC_MX27VIS_WM8974 | ||
13 | tristate "SoC Audio support for MX27 - WM8974 Visstrim_sm10 board" | ||
14 | depends on SND_MX1_MX2_SOC && MACH_MX27 && MACH_IMX27_VISSTRIM_M10 | ||
15 | select SND_MXC_SOC_SSI | ||
16 | select SND_SOC_WM8974 | ||
17 | help | ||
18 | Say Y if you want to add support for SoC audio on Visstrim SM10 | ||
19 | board with WM8974. | ||
20 | |||
21 | |||
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile index c2ffd2c8df5a..9f8bb92ddfcc 100644 --- a/sound/soc/imx/Makefile +++ b/sound/soc/imx/Makefile | |||
@@ -1,10 +1,12 @@ | |||
1 | # i.MX Platform Support | 1 | # i.MX Platform Support |
2 | snd-soc-mx1_mx2-objs := mx1_mx2-pcm.o | 2 | snd-soc-imx-objs := imx-ssi.o imx-pcm-fiq.o |
3 | snd-soc-mxc-ssi-objs := mxc-ssi.o | ||
4 | 3 | ||
5 | obj-$(CONFIG_SND_MX1_MX2_SOC) += snd-soc-mx1_mx2.o | 4 | ifdef CONFIG_MACH_MX27 |
6 | obj-$(CONFIG_SND_MXC_SOC_SSI) += snd-soc-mxc-ssi.o | 5 | snd-soc-imx-objs += imx-pcm-dma-mx2.o |
6 | endif | ||
7 | |||
8 | obj-$(CONFIG_SND_IMX_SOC) += snd-soc-imx.o | ||
7 | 9 | ||
8 | # i.MX Machine Support | 10 | # i.MX Machine Support |
9 | snd-soc-mx27vis-wm8974-objs := mx27vis_wm8974.o | 11 | snd-soc-phycore-ac97-objs := phycore-ac97.o |
10 | obj-$(CONFIG_SND_SOC_MX27VIS_WM8974) += snd-soc-mx27vis-wm8974.o | 12 | obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o |
diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c new file mode 100644 index 000000000000..19452e44afdc --- /dev/null +++ b/sound/soc/imx/imx-pcm-dma-mx2.c | |||
@@ -0,0 +1,313 @@ | |||
1 | /* | ||
2 | * imx-pcm-dma-mx2.c -- ALSA Soc Audio Layer | ||
3 | * | ||
4 | * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de> | ||
5 | * | ||
6 | * This code is based on code copyrighted by Freescale, | ||
7 | * Liam Girdwood, Javier Martin and probably others. | ||
8 | * | ||
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. | ||
13 | */ | ||
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 | |||
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> | ||
28 | |||
29 | #include <mach/dma-mx1-mx2.h> | ||
30 | |||
31 | #include "imx-ssi.h" | ||
32 | |||
33 | struct imx_pcm_runtime_data { | ||
34 | int sg_count; | ||
35 | struct scatterlist *sg_list; | ||
36 | int period; | ||
37 | int periods; | ||
38 | unsigned long dma_addr; | ||
39 | int dma; | ||
40 | struct snd_pcm_substream *substream; | ||
41 | unsigned long offset; | ||
42 | unsigned long size; | ||
43 | unsigned long period_cnt; | ||
44 | void *buf; | ||
45 | int period_time; | ||
46 | }; | ||
47 | |||
48 | /* Called by the DMA framework when a period has elapsed */ | ||
49 | static void imx_ssi_dma_progression(int channel, void *data, | ||
50 | struct scatterlist *sg) | ||
51 | { | ||
52 | struct snd_pcm_substream *substream = data; | ||
53 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
54 | struct imx_pcm_runtime_data *iprtd = runtime->private_data; | ||
55 | |||
56 | if (!sg) | ||
57 | return; | ||
58 | |||
59 | runtime = iprtd->substream->runtime; | ||
60 | |||
61 | iprtd->offset = sg->dma_address - runtime->dma_addr; | ||
62 | |||
63 | snd_pcm_period_elapsed(iprtd->substream); | ||
64 | } | ||
65 | |||
66 | static void imx_ssi_dma_callback(int channel, void *data) | ||
67 | { | ||
68 | pr_err("%s shouldn't be called\n", __func__); | ||
69 | } | ||
70 | |||
71 | static void snd_imx_dma_err_callback(int channel, void *data, int err) | ||
72 | { | ||
73 | pr_err("DMA error callback called\n"); | ||
74 | |||
75 | pr_err("DMA timeout on channel %d -%s%s%s%s\n", | ||
76 | channel, | ||
77 | err & IMX_DMA_ERR_BURST ? " burst" : "", | ||
78 | err & IMX_DMA_ERR_REQUEST ? " request" : "", | ||
79 | err & IMX_DMA_ERR_TRANSFER ? " transfer" : "", | ||
80 | err & IMX_DMA_ERR_BUFFER ? " buffer" : ""); | ||
81 | } | ||
82 | |||
83 | static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream) | ||
84 | { | ||
85 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
86 | struct imx_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data; | ||
87 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
88 | struct imx_pcm_runtime_data *iprtd = runtime->private_data; | ||
89 | int ret; | ||
90 | |||
91 | iprtd->dma = imx_dma_request_by_prio(DRV_NAME, DMA_PRIO_HIGH); | ||
92 | if (iprtd->dma < 0) { | ||
93 | pr_err("Failed to claim the audio DMA\n"); | ||
94 | return -ENODEV; | ||
95 | } | ||
96 | |||
97 | ret = imx_dma_setup_handlers(iprtd->dma, | ||
98 | imx_ssi_dma_callback, | ||
99 | snd_imx_dma_err_callback, substream); | ||
100 | if (ret) | ||
101 | goto out; | ||
102 | |||
103 | ret = imx_dma_setup_progression_handler(iprtd->dma, | ||
104 | imx_ssi_dma_progression); | ||
105 | if (ret) { | ||
106 | pr_err("Failed to setup the DMA handler\n"); | ||
107 | goto out; | ||
108 | } | ||
109 | |||
110 | ret = imx_dma_config_channel(iprtd->dma, | ||
111 | IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, | ||
112 | IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | ||
113 | dma_params->dma, 1); | ||
114 | if (ret < 0) { | ||
115 | pr_err("Cannot configure DMA channel: %d\n", ret); | ||
116 | goto out; | ||
117 | } | ||
118 | |||
119 | imx_dma_config_burstlen(iprtd->dma, dma_params->burstsize * 2); | ||
120 | |||
121 | return 0; | ||
122 | out: | ||
123 | imx_dma_free(iprtd->dma); | ||
124 | return ret; | ||
125 | } | ||
126 | |||
127 | static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, | ||
128 | struct snd_pcm_hw_params *params) | ||
129 | { | ||
130 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
131 | struct imx_pcm_runtime_data *iprtd = runtime->private_data; | ||
132 | int i; | ||
133 | unsigned long dma_addr; | ||
134 | |||
135 | imx_ssi_dma_alloc(substream); | ||
136 | |||
137 | iprtd->size = params_buffer_bytes(params); | ||
138 | iprtd->periods = params_periods(params); | ||
139 | iprtd->period = params_period_bytes(params); | ||
140 | iprtd->offset = 0; | ||
141 | iprtd->period_time = HZ / (params_rate(params) / | ||
142 | params_period_size(params)); | ||
143 | |||
144 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
145 | |||
146 | if (iprtd->sg_count != iprtd->periods) { | ||
147 | kfree(iprtd->sg_list); | ||
148 | |||
149 | iprtd->sg_list = kcalloc(iprtd->periods + 1, | ||
150 | sizeof(struct scatterlist), GFP_KERNEL); | ||
151 | if (!iprtd->sg_list) | ||
152 | return -ENOMEM; | ||
153 | iprtd->sg_count = iprtd->periods + 1; | ||
154 | } | ||
155 | |||
156 | sg_init_table(iprtd->sg_list, iprtd->sg_count); | ||
157 | dma_addr = runtime->dma_addr; | ||
158 | |||
159 | for (i = 0; i < iprtd->periods; i++) { | ||
160 | iprtd->sg_list[i].page_link = 0; | ||
161 | iprtd->sg_list[i].offset = 0; | ||
162 | iprtd->sg_list[i].dma_address = dma_addr; | ||
163 | iprtd->sg_list[i].length = iprtd->period; | ||
164 | dma_addr += iprtd->period; | ||
165 | } | ||
166 | |||
167 | /* close the loop */ | ||
168 | iprtd->sg_list[iprtd->sg_count - 1].offset = 0; | ||
169 | iprtd->sg_list[iprtd->sg_count - 1].length = 0; | ||
170 | iprtd->sg_list[iprtd->sg_count - 1].page_link = | ||
171 | ((unsigned long) iprtd->sg_list | 0x01) & ~0x02; | ||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | static int snd_imx_pcm_hw_free(struct snd_pcm_substream *substream) | ||
176 | { | ||
177 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
178 | struct imx_pcm_runtime_data *iprtd = runtime->private_data; | ||
179 | |||
180 | if (iprtd->dma >= 0) { | ||
181 | imx_dma_free(iprtd->dma); | ||
182 | iprtd->dma = -EINVAL; | ||
183 | } | ||
184 | |||
185 | kfree(iprtd->sg_list); | ||
186 | iprtd->sg_list = NULL; | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream) | ||
192 | { | ||
193 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
194 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
195 | struct imx_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data; | ||
196 | struct imx_pcm_runtime_data *iprtd = runtime->private_data; | ||
197 | int err; | ||
198 | |||
199 | iprtd->substream = substream; | ||
200 | iprtd->buf = (unsigned int *)substream->dma_buffer.area; | ||
201 | iprtd->period_cnt = 0; | ||
202 | |||
203 | pr_debug("%s: buf: %p period: %d periods: %d\n", | ||
204 | __func__, iprtd->buf, iprtd->period, iprtd->periods); | ||
205 | |||
206 | err = imx_dma_setup_sg(iprtd->dma, iprtd->sg_list, iprtd->sg_count, | ||
207 | IMX_DMA_LENGTH_LOOP, dma_params->dma_addr, | ||
208 | substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? | ||
209 | DMA_MODE_WRITE : DMA_MODE_READ); | ||
210 | if (err) | ||
211 | return err; | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
217 | { | ||
218 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
219 | struct imx_pcm_runtime_data *iprtd = runtime->private_data; | ||
220 | |||
221 | switch (cmd) { | ||
222 | case SNDRV_PCM_TRIGGER_START: | ||
223 | case SNDRV_PCM_TRIGGER_RESUME: | ||
224 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
225 | imx_dma_enable(iprtd->dma); | ||
226 | |||
227 | break; | ||
228 | |||
229 | case SNDRV_PCM_TRIGGER_STOP: | ||
230 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
231 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
232 | imx_dma_disable(iprtd->dma); | ||
233 | |||
234 | break; | ||
235 | default: | ||
236 | return -EINVAL; | ||
237 | } | ||
238 | |||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream) | ||
243 | { | ||
244 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
245 | struct imx_pcm_runtime_data *iprtd = runtime->private_data; | ||
246 | |||
247 | return bytes_to_frames(substream->runtime, iprtd->offset); | ||
248 | } | ||
249 | |||
250 | static struct snd_pcm_hardware snd_imx_hardware = { | ||
251 | .info = SNDRV_PCM_INFO_INTERLEAVED | | ||
252 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
253 | SNDRV_PCM_INFO_MMAP | | ||
254 | SNDRV_PCM_INFO_MMAP_VALID | | ||
255 | SNDRV_PCM_INFO_PAUSE | | ||
256 | SNDRV_PCM_INFO_RESUME, | ||
257 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
258 | .rate_min = 8000, | ||
259 | .channels_min = 2, | ||
260 | .channels_max = 2, | ||
261 | .buffer_bytes_max = IMX_SSI_DMABUF_SIZE, | ||
262 | .period_bytes_min = 128, | ||
263 | .period_bytes_max = 16 * 1024, | ||
264 | .periods_min = 2, | ||
265 | .periods_max = 255, | ||
266 | .fifo_size = 0, | ||
267 | }; | ||
268 | |||
269 | static int snd_imx_open(struct snd_pcm_substream *substream) | ||
270 | { | ||
271 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
272 | struct imx_pcm_runtime_data *iprtd; | ||
273 | int ret; | ||
274 | |||
275 | iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL); | ||
276 | runtime->private_data = iprtd; | ||
277 | |||
278 | ret = snd_pcm_hw_constraint_integer(substream->runtime, | ||
279 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
280 | if (ret < 0) | ||
281 | return ret; | ||
282 | |||
283 | snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); | ||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | static struct snd_pcm_ops imx_pcm_ops = { | ||
288 | .open = snd_imx_open, | ||
289 | .ioctl = snd_pcm_lib_ioctl, | ||
290 | .hw_params = snd_imx_pcm_hw_params, | ||
291 | .hw_free = snd_imx_pcm_hw_free, | ||
292 | .prepare = snd_imx_pcm_prepare, | ||
293 | .trigger = snd_imx_pcm_trigger, | ||
294 | .pointer = snd_imx_pcm_pointer, | ||
295 | .mmap = snd_imx_pcm_mmap, | ||
296 | }; | ||
297 | |||
298 | static struct snd_soc_platform imx_soc_platform_dma = { | ||
299 | .name = "imx-audio", | ||
300 | .pcm_ops = &imx_pcm_ops, | ||
301 | .pcm_new = imx_pcm_new, | ||
302 | .pcm_free = imx_pcm_free, | ||
303 | }; | ||
304 | |||
305 | struct snd_soc_platform *imx_ssi_dma_mx2_init(struct platform_device *pdev, | ||
306 | struct imx_ssi *ssi) | ||
307 | { | ||
308 | ssi->dma_params_tx.burstsize = DMA_TXFIFO_BURST; | ||
309 | ssi->dma_params_rx.burstsize = DMA_RXFIFO_BURST; | ||
310 | |||
311 | return &imx_soc_platform_dma; | ||
312 | } | ||
313 | |||
diff --git a/sound/soc/imx/imx-pcm-fiq.c b/sound/soc/imx/imx-pcm-fiq.c new file mode 100644 index 000000000000..d9cb9849b033 --- /dev/null +++ b/sound/soc/imx/imx-pcm-fiq.c | |||
@@ -0,0 +1,297 @@ | |||
1 | /* | ||
2 | * imx-pcm-fiq.c -- ALSA Soc Audio Layer | ||
3 | * | ||
4 | * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de> | ||
5 | * | ||
6 | * This code is based on code copyrighted by Freescale, | ||
7 | * Liam Girdwood, Javier Martin and probably others. | ||
8 | * | ||
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. | ||
13 | */ | ||
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 | |||
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> | ||
28 | |||
29 | #include <asm/fiq.h> | ||
30 | |||
31 | #include <mach/ssi.h> | ||
32 | |||
33 | #include "imx-ssi.h" | ||
34 | |||
35 | struct imx_pcm_runtime_data { | ||
36 | int period; | ||
37 | int periods; | ||
38 | unsigned long offset; | ||
39 | unsigned long last_offset; | ||
40 | unsigned long size; | ||
41 | struct timer_list timer; | ||
42 | int poll_time; | ||
43 | }; | ||
44 | |||
45 | static inline void imx_ssi_set_next_poll(struct imx_pcm_runtime_data *iprtd) | ||
46 | { | ||
47 | iprtd->timer.expires = jiffies + iprtd->poll_time; | ||
48 | } | ||
49 | |||
50 | static void imx_ssi_timer_callback(unsigned long data) | ||
51 | { | ||
52 | struct snd_pcm_substream *substream = (void *)data; | ||
53 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
54 | struct imx_pcm_runtime_data *iprtd = runtime->private_data; | ||
55 | struct pt_regs regs; | ||
56 | unsigned long delta; | ||
57 | |||
58 | get_fiq_regs(®s); | ||
59 | |||
60 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
61 | iprtd->offset = regs.ARM_r8 & 0xffff; | ||
62 | else | ||
63 | iprtd->offset = regs.ARM_r9 & 0xffff; | ||
64 | |||
65 | /* How much data have we transferred since the last period report? */ | ||
66 | if (iprtd->offset >= iprtd->last_offset) | ||
67 | delta = iprtd->offset - iprtd->last_offset; | ||
68 | else | ||
69 | delta = runtime->buffer_size + iprtd->offset | ||
70 | - iprtd->last_offset; | ||
71 | |||
72 | /* If we've transferred at least a period then report it and | ||
73 | * reset our poll time */ | ||
74 | if (delta >= runtime->period_size) { | ||
75 | snd_pcm_period_elapsed(substream); | ||
76 | iprtd->last_offset = iprtd->offset; | ||
77 | |||
78 | imx_ssi_set_next_poll(iprtd); | ||
79 | } | ||
80 | |||
81 | /* Restart the timer; if we didn't report we'll run on the next tick */ | ||
82 | add_timer(&iprtd->timer); | ||
83 | |||
84 | } | ||
85 | |||
86 | static struct fiq_handler fh = { | ||
87 | .name = DRV_NAME, | ||
88 | }; | ||
89 | |||
90 | static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, | ||
91 | struct snd_pcm_hw_params *params) | ||
92 | { | ||
93 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
94 | struct imx_pcm_runtime_data *iprtd = runtime->private_data; | ||
95 | |||
96 | iprtd->size = params_buffer_bytes(params); | ||
97 | iprtd->periods = params_periods(params); | ||
98 | iprtd->period = params_period_bytes(params) ; | ||
99 | iprtd->offset = 0; | ||
100 | iprtd->last_offset = 0; | ||
101 | iprtd->poll_time = HZ / (params_rate(params) / params_period_size(params)); | ||
102 | |||
103 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream) | ||
109 | { | ||
110 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
111 | struct imx_pcm_runtime_data *iprtd = runtime->private_data; | ||
112 | struct pt_regs regs; | ||
113 | |||
114 | get_fiq_regs(®s); | ||
115 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
116 | regs.ARM_r8 = (iprtd->period * iprtd->periods - 1) << 16; | ||
117 | else | ||
118 | regs.ARM_r9 = (iprtd->period * iprtd->periods - 1) << 16; | ||
119 | |||
120 | set_fiq_regs(®s); | ||
121 | |||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | static int fiq_enable; | ||
126 | static int imx_pcm_fiq; | ||
127 | |||
128 | static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
129 | { | ||
130 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
131 | struct imx_pcm_runtime_data *iprtd = runtime->private_data; | ||
132 | |||
133 | switch (cmd) { | ||
134 | case SNDRV_PCM_TRIGGER_START: | ||
135 | case SNDRV_PCM_TRIGGER_RESUME: | ||
136 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
137 | imx_ssi_set_next_poll(iprtd); | ||
138 | add_timer(&iprtd->timer); | ||
139 | if (++fiq_enable == 1) | ||
140 | enable_fiq(imx_pcm_fiq); | ||
141 | |||
142 | break; | ||
143 | |||
144 | case SNDRV_PCM_TRIGGER_STOP: | ||
145 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
146 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
147 | del_timer(&iprtd->timer); | ||
148 | if (--fiq_enable == 0) | ||
149 | disable_fiq(imx_pcm_fiq); | ||
150 | |||
151 | |||
152 | break; | ||
153 | default: | ||
154 | return -EINVAL; | ||
155 | } | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream) | ||
161 | { | ||
162 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
163 | struct imx_pcm_runtime_data *iprtd = runtime->private_data; | ||
164 | |||
165 | return bytes_to_frames(substream->runtime, iprtd->offset); | ||
166 | } | ||
167 | |||
168 | static struct snd_pcm_hardware snd_imx_hardware = { | ||
169 | .info = SNDRV_PCM_INFO_INTERLEAVED | | ||
170 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
171 | SNDRV_PCM_INFO_MMAP | | ||
172 | SNDRV_PCM_INFO_MMAP_VALID | | ||
173 | SNDRV_PCM_INFO_PAUSE | | ||
174 | SNDRV_PCM_INFO_RESUME, | ||
175 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
176 | .rate_min = 8000, | ||
177 | .channels_min = 2, | ||
178 | .channels_max = 2, | ||
179 | .buffer_bytes_max = IMX_SSI_DMABUF_SIZE, | ||
180 | .period_bytes_min = 128, | ||
181 | .period_bytes_max = 16 * 1024, | ||
182 | .periods_min = 2, | ||
183 | .periods_max = 255, | ||
184 | .fifo_size = 0, | ||
185 | }; | ||
186 | |||
187 | static int snd_imx_open(struct snd_pcm_substream *substream) | ||
188 | { | ||
189 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
190 | struct imx_pcm_runtime_data *iprtd; | ||
191 | int ret; | ||
192 | |||
193 | iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL); | ||
194 | runtime->private_data = iprtd; | ||
195 | |||
196 | init_timer(&iprtd->timer); | ||
197 | iprtd->timer.data = (unsigned long)substream; | ||
198 | iprtd->timer.function = imx_ssi_timer_callback; | ||
199 | |||
200 | ret = snd_pcm_hw_constraint_integer(substream->runtime, | ||
201 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
202 | if (ret < 0) | ||
203 | return ret; | ||
204 | |||
205 | snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); | ||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | static int snd_imx_close(struct snd_pcm_substream *substream) | ||
210 | { | ||
211 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
212 | struct imx_pcm_runtime_data *iprtd = runtime->private_data; | ||
213 | |||
214 | del_timer_sync(&iprtd->timer); | ||
215 | kfree(iprtd); | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static struct snd_pcm_ops imx_pcm_ops = { | ||
221 | .open = snd_imx_open, | ||
222 | .close = snd_imx_close, | ||
223 | .ioctl = snd_pcm_lib_ioctl, | ||
224 | .hw_params = snd_imx_pcm_hw_params, | ||
225 | .prepare = snd_imx_pcm_prepare, | ||
226 | .trigger = snd_imx_pcm_trigger, | ||
227 | .pointer = snd_imx_pcm_pointer, | ||
228 | .mmap = snd_imx_pcm_mmap, | ||
229 | }; | ||
230 | |||
231 | static int imx_pcm_fiq_new(struct snd_card *card, struct snd_soc_dai *dai, | ||
232 | struct snd_pcm *pcm) | ||
233 | { | ||
234 | int ret; | ||
235 | |||
236 | ret = imx_pcm_new(card, dai, pcm); | ||
237 | if (ret) | ||
238 | return ret; | ||
239 | |||
240 | if (dai->playback.channels_min) { | ||
241 | struct snd_pcm_substream *substream = | ||
242 | pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; | ||
243 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
244 | |||
245 | imx_ssi_fiq_tx_buffer = (unsigned long)buf->area; | ||
246 | } | ||
247 | |||
248 | if (dai->capture.channels_min) { | ||
249 | struct snd_pcm_substream *substream = | ||
250 | pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; | ||
251 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
252 | |||
253 | imx_ssi_fiq_rx_buffer = (unsigned long)buf->area; | ||
254 | } | ||
255 | |||
256 | set_fiq_handler(&imx_ssi_fiq_start, | ||
257 | &imx_ssi_fiq_end - &imx_ssi_fiq_start); | ||
258 | |||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | static struct snd_soc_platform imx_soc_platform_fiq = { | ||
263 | .pcm_ops = &imx_pcm_ops, | ||
264 | .pcm_new = imx_pcm_fiq_new, | ||
265 | .pcm_free = imx_pcm_free, | ||
266 | }; | ||
267 | |||
268 | struct snd_soc_platform *imx_ssi_fiq_init(struct platform_device *pdev, | ||
269 | struct imx_ssi *ssi) | ||
270 | { | ||
271 | int ret = 0; | ||
272 | |||
273 | ret = claim_fiq(&fh); | ||
274 | if (ret) { | ||
275 | dev_err(&pdev->dev, "failed to claim fiq: %d", ret); | ||
276 | return ERR_PTR(ret); | ||
277 | } | ||
278 | |||
279 | mxc_set_irq_fiq(ssi->irq, 1); | ||
280 | |||
281 | imx_pcm_fiq = ssi->irq; | ||
282 | |||
283 | imx_ssi_fiq_base = (unsigned long)ssi->base; | ||
284 | |||
285 | ssi->dma_params_tx.burstsize = 4; | ||
286 | ssi->dma_params_rx.burstsize = 6; | ||
287 | |||
288 | return &imx_soc_platform_fiq; | ||
289 | } | ||
290 | |||
291 | void imx_ssi_fiq_exit(struct platform_device *pdev, | ||
292 | struct imx_ssi *ssi) | ||
293 | { | ||
294 | mxc_set_irq_fiq(ssi->irq, 0); | ||
295 | release_fiq(&fh); | ||
296 | } | ||
297 | |||
diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c new file mode 100644 index 000000000000..56f46a75d297 --- /dev/null +++ b/sound/soc/imx/imx-ssi.c | |||
@@ -0,0 +1,758 @@ | |||
1 | /* | ||
2 | * imx-ssi.c -- ALSA Soc Audio Layer | ||
3 | * | ||
4 | * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de> | ||
5 | * | ||
6 | * This code is based on code copyrighted by Freescale, | ||
7 | * Liam Girdwood, Javier Martin and probably others. | ||
8 | * | ||
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. | ||
13 | * | ||
14 | * | ||
15 | * The i.MX SSI core has some nasty limitations in AC97 mode. While most | ||
16 | * sane processor vendors have a FIFO per AC97 slot, the i.MX has only | ||
17 | * one FIFO which combines all valid receive slots. We cannot even select | ||
18 | * which slots we want to receive. The WM9712 with which this driver | ||
19 | * was developped with always sends GPIO status data in slot 12 which | ||
20 | * we receive in our (PCM-) data stream. The only chance we have is to | ||
21 | * manually skip this data in the FIQ handler. With sampling rates different | ||
22 | * from 48000Hz not every frame has valid receive data, so the ratio | ||
23 | * between pcm data and GPIO status data changes. Our FIQ handler is not | ||
24 | * able to handle this, hence this driver only works with 48000Hz sampling | ||
25 | * rate. | ||
26 | * Reading and writing AC97 registers is another challange. The core | ||
27 | * provides us status bits when the read register is updated with *another* | ||
28 | * value. When we read the same register two times (and the register still | ||
29 | * contains the same value) these status bits are not set. We work | ||
30 | * around this by not polling these bits but only wait a fixed delay. | ||
31 | * | ||
32 | */ | ||
33 | |||
34 | #include <linux/clk.h> | ||
35 | #include <linux/delay.h> | ||
36 | #include <linux/device.h> | ||
37 | #include <linux/dma-mapping.h> | ||
38 | #include <linux/init.h> | ||
39 | #include <linux/interrupt.h> | ||
40 | #include <linux/module.h> | ||
41 | #include <linux/platform_device.h> | ||
42 | |||
43 | #include <sound/core.h> | ||
44 | #include <sound/initval.h> | ||
45 | #include <sound/pcm.h> | ||
46 | #include <sound/pcm_params.h> | ||
47 | #include <sound/soc.h> | ||
48 | |||
49 | #include <mach/ssi.h> | ||
50 | #include <mach/hardware.h> | ||
51 | |||
52 | #include "imx-ssi.h" | ||
53 | |||
54 | #define SSI_SACNT_DEFAULT (SSI_SACNT_AC97EN | SSI_SACNT_FV) | ||
55 | |||
56 | /* | ||
57 | * SSI Network Mode or TDM slots configuration. | ||
58 | * Should only be called when port is inactive (i.e. SSIEN = 0). | ||
59 | */ | ||
60 | static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, | ||
61 | unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) | ||
62 | { | ||
63 | struct imx_ssi *ssi = cpu_dai->private_data; | ||
64 | u32 sccr; | ||
65 | |||
66 | sccr = readl(ssi->base + SSI_STCCR); | ||
67 | sccr &= ~SSI_STCCR_DC_MASK; | ||
68 | sccr |= SSI_STCCR_DC(slots - 1); | ||
69 | writel(sccr, ssi->base + SSI_STCCR); | ||
70 | |||
71 | sccr = readl(ssi->base + SSI_SRCCR); | ||
72 | sccr &= ~SSI_STCCR_DC_MASK; | ||
73 | sccr |= SSI_STCCR_DC(slots - 1); | ||
74 | writel(sccr, ssi->base + SSI_SRCCR); | ||
75 | |||
76 | writel(tx_mask, ssi->base + SSI_STMSK); | ||
77 | writel(rx_mask, ssi->base + SSI_SRMSK); | ||
78 | |||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | /* | ||
83 | * SSI DAI format configuration. | ||
84 | * Should only be called when port is inactive (i.e. SSIEN = 0). | ||
85 | * Note: We don't use the I2S modes but instead manually configure the | ||
86 | * SSI for I2S because the I2S mode is only a register preset. | ||
87 | */ | ||
88 | static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) | ||
89 | { | ||
90 | struct imx_ssi *ssi = cpu_dai->private_data; | ||
91 | u32 strcr = 0, scr; | ||
92 | |||
93 | scr = readl(ssi->base + SSI_SCR) & ~(SSI_SCR_SYN | SSI_SCR_NET); | ||
94 | |||
95 | /* DAI mode */ | ||
96 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
97 | case SND_SOC_DAIFMT_I2S: | ||
98 | /* data on rising edge of bclk, frame low 1clk before data */ | ||
99 | strcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0; | ||
100 | scr |= SSI_SCR_NET; | ||
101 | break; | ||
102 | case SND_SOC_DAIFMT_LEFT_J: | ||
103 | /* data on rising edge of bclk, frame high with data */ | ||
104 | strcr |= SSI_STCR_TXBIT0; | ||
105 | break; | ||
106 | case SND_SOC_DAIFMT_DSP_B: | ||
107 | /* data on rising edge of bclk, frame high with data */ | ||
108 | strcr |= SSI_STCR_TFSL; | ||
109 | break; | ||
110 | case SND_SOC_DAIFMT_DSP_A: | ||
111 | /* data on rising edge of bclk, frame high 1clk before data */ | ||
112 | strcr |= SSI_STCR_TFSL | SSI_STCR_TEFS; | ||
113 | break; | ||
114 | } | ||
115 | |||
116 | /* DAI clock inversion */ | ||
117 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
118 | case SND_SOC_DAIFMT_IB_IF: | ||
119 | strcr |= SSI_STCR_TFSI; | ||
120 | strcr &= ~SSI_STCR_TSCKP; | ||
121 | break; | ||
122 | case SND_SOC_DAIFMT_IB_NF: | ||
123 | strcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI); | ||
124 | break; | ||
125 | case SND_SOC_DAIFMT_NB_IF: | ||
126 | strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP; | ||
127 | break; | ||
128 | case SND_SOC_DAIFMT_NB_NF: | ||
129 | strcr &= ~SSI_STCR_TFSI; | ||
130 | strcr |= SSI_STCR_TSCKP; | ||
131 | break; | ||
132 | } | ||
133 | |||
134 | /* DAI clock master masks */ | ||
135 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
136 | case SND_SOC_DAIFMT_CBM_CFM: | ||
137 | break; | ||
138 | default: | ||
139 | /* Master mode not implemented, needs handling of clocks. */ | ||
140 | return -EINVAL; | ||
141 | } | ||
142 | |||
143 | strcr |= SSI_STCR_TFEN0; | ||
144 | |||
145 | writel(strcr, ssi->base + SSI_STCR); | ||
146 | writel(strcr, ssi->base + SSI_SRCR); | ||
147 | writel(scr, ssi->base + SSI_SCR); | ||
148 | |||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | /* | ||
153 | * SSI system clock configuration. | ||
154 | * Should only be called when port is inactive (i.e. SSIEN = 0). | ||
155 | */ | ||
156 | static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai, | ||
157 | int clk_id, unsigned int freq, int dir) | ||
158 | { | ||
159 | struct imx_ssi *ssi = cpu_dai->private_data; | ||
160 | u32 scr; | ||
161 | |||
162 | scr = readl(ssi->base + SSI_SCR); | ||
163 | |||
164 | switch (clk_id) { | ||
165 | case IMX_SSP_SYS_CLK: | ||
166 | if (dir == SND_SOC_CLOCK_OUT) | ||
167 | scr |= SSI_SCR_SYS_CLK_EN; | ||
168 | else | ||
169 | scr &= ~SSI_SCR_SYS_CLK_EN; | ||
170 | break; | ||
171 | default: | ||
172 | return -EINVAL; | ||
173 | } | ||
174 | |||
175 | writel(scr, ssi->base + SSI_SCR); | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | /* | ||
181 | * SSI Clock dividers | ||
182 | * Should only be called when port is inactive (i.e. SSIEN = 0). | ||
183 | */ | ||
184 | static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, | ||
185 | int div_id, int div) | ||
186 | { | ||
187 | struct imx_ssi *ssi = cpu_dai->private_data; | ||
188 | u32 stccr, srccr; | ||
189 | |||
190 | stccr = readl(ssi->base + SSI_STCCR); | ||
191 | srccr = readl(ssi->base + SSI_SRCCR); | ||
192 | |||
193 | switch (div_id) { | ||
194 | case IMX_SSI_TX_DIV_2: | ||
195 | stccr &= ~SSI_STCCR_DIV2; | ||
196 | stccr |= div; | ||
197 | break; | ||
198 | case IMX_SSI_TX_DIV_PSR: | ||
199 | stccr &= ~SSI_STCCR_PSR; | ||
200 | stccr |= div; | ||
201 | break; | ||
202 | case IMX_SSI_TX_DIV_PM: | ||
203 | stccr &= ~0xff; | ||
204 | stccr |= SSI_STCCR_PM(div); | ||
205 | break; | ||
206 | case IMX_SSI_RX_DIV_2: | ||
207 | stccr &= ~SSI_STCCR_DIV2; | ||
208 | stccr |= div; | ||
209 | break; | ||
210 | case IMX_SSI_RX_DIV_PSR: | ||
211 | stccr &= ~SSI_STCCR_PSR; | ||
212 | stccr |= div; | ||
213 | break; | ||
214 | case IMX_SSI_RX_DIV_PM: | ||
215 | stccr &= ~0xff; | ||
216 | stccr |= SSI_STCCR_PM(div); | ||
217 | break; | ||
218 | default: | ||
219 | return -EINVAL; | ||
220 | } | ||
221 | |||
222 | writel(stccr, ssi->base + SSI_STCCR); | ||
223 | writel(srccr, ssi->base + SSI_SRCCR); | ||
224 | |||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | /* | ||
229 | * Should only be called when port is inactive (i.e. SSIEN = 0), | ||
230 | * although can be called multiple times by upper layers. | ||
231 | */ | ||
232 | static int imx_ssi_hw_params(struct snd_pcm_substream *substream, | ||
233 | struct snd_pcm_hw_params *params, | ||
234 | struct snd_soc_dai *cpu_dai) | ||
235 | { | ||
236 | struct imx_ssi *ssi = cpu_dai->private_data; | ||
237 | u32 reg, sccr; | ||
238 | |||
239 | /* Tx/Rx config */ | ||
240 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
241 | reg = SSI_STCCR; | ||
242 | cpu_dai->dma_data = &ssi->dma_params_tx; | ||
243 | } else { | ||
244 | reg = SSI_SRCCR; | ||
245 | cpu_dai->dma_data = &ssi->dma_params_rx; | ||
246 | } | ||
247 | |||
248 | sccr = readl(ssi->base + reg) & ~SSI_STCCR_WL_MASK; | ||
249 | |||
250 | /* DAI data (word) size */ | ||
251 | switch (params_format(params)) { | ||
252 | case SNDRV_PCM_FORMAT_S16_LE: | ||
253 | sccr |= SSI_SRCCR_WL(16); | ||
254 | break; | ||
255 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
256 | sccr |= SSI_SRCCR_WL(20); | ||
257 | break; | ||
258 | case SNDRV_PCM_FORMAT_S24_LE: | ||
259 | sccr |= SSI_SRCCR_WL(24); | ||
260 | break; | ||
261 | } | ||
262 | |||
263 | writel(sccr, ssi->base + reg); | ||
264 | |||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd, | ||
269 | struct snd_soc_dai *dai) | ||
270 | { | ||
271 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
272 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
273 | struct imx_ssi *ssi = cpu_dai->private_data; | ||
274 | unsigned int sier_bits, sier; | ||
275 | unsigned int scr; | ||
276 | |||
277 | scr = readl(ssi->base + SSI_SCR); | ||
278 | sier = readl(ssi->base + SSI_SIER); | ||
279 | |||
280 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
281 | if (ssi->flags & IMX_SSI_DMA) | ||
282 | sier_bits = SSI_SIER_TDMAE; | ||
283 | else | ||
284 | sier_bits = SSI_SIER_TIE | SSI_SIER_TFE0_EN; | ||
285 | } else { | ||
286 | if (ssi->flags & IMX_SSI_DMA) | ||
287 | sier_bits = SSI_SIER_RDMAE; | ||
288 | else | ||
289 | sier_bits = SSI_SIER_RIE | SSI_SIER_RFF0_EN; | ||
290 | } | ||
291 | |||
292 | switch (cmd) { | ||
293 | case SNDRV_PCM_TRIGGER_START: | ||
294 | case SNDRV_PCM_TRIGGER_RESUME: | ||
295 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
296 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
297 | scr |= SSI_SCR_TE; | ||
298 | else | ||
299 | scr |= SSI_SCR_RE; | ||
300 | sier |= sier_bits; | ||
301 | |||
302 | if (++ssi->enabled == 1) | ||
303 | scr |= SSI_SCR_SSIEN; | ||
304 | |||
305 | break; | ||
306 | |||
307 | case SNDRV_PCM_TRIGGER_STOP: | ||
308 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
309 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
310 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
311 | scr &= ~SSI_SCR_TE; | ||
312 | else | ||
313 | scr &= ~SSI_SCR_RE; | ||
314 | sier &= ~sier_bits; | ||
315 | |||
316 | if (--ssi->enabled == 0) | ||
317 | scr &= ~SSI_SCR_SSIEN; | ||
318 | |||
319 | break; | ||
320 | default: | ||
321 | return -EINVAL; | ||
322 | } | ||
323 | |||
324 | if (!(ssi->flags & IMX_SSI_USE_AC97)) | ||
325 | /* rx/tx are always enabled to access ac97 registers */ | ||
326 | writel(scr, ssi->base + SSI_SCR); | ||
327 | |||
328 | writel(sier, ssi->base + SSI_SIER); | ||
329 | |||
330 | return 0; | ||
331 | } | ||
332 | |||
333 | static struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = { | ||
334 | .hw_params = imx_ssi_hw_params, | ||
335 | .set_fmt = imx_ssi_set_dai_fmt, | ||
336 | .set_clkdiv = imx_ssi_set_dai_clkdiv, | ||
337 | .set_sysclk = imx_ssi_set_dai_sysclk, | ||
338 | .set_tdm_slot = imx_ssi_set_dai_tdm_slot, | ||
339 | .trigger = imx_ssi_trigger, | ||
340 | }; | ||
341 | |||
342 | static struct snd_soc_dai imx_ssi_dai = { | ||
343 | .playback = { | ||
344 | .channels_min = 2, | ||
345 | .channels_max = 2, | ||
346 | .rates = SNDRV_PCM_RATE_8000_96000, | ||
347 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
348 | }, | ||
349 | .capture = { | ||
350 | .channels_min = 2, | ||
351 | .channels_max = 2, | ||
352 | .rates = SNDRV_PCM_RATE_8000_96000, | ||
353 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
354 | }, | ||
355 | .ops = &imx_ssi_pcm_dai_ops, | ||
356 | }; | ||
357 | |||
358 | int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, | ||
359 | struct vm_area_struct *vma) | ||
360 | { | ||
361 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
362 | int ret; | ||
363 | |||
364 | ret = dma_mmap_coherent(NULL, vma, runtime->dma_area, | ||
365 | runtime->dma_addr, runtime->dma_bytes); | ||
366 | |||
367 | pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret, | ||
368 | runtime->dma_area, | ||
369 | runtime->dma_addr, | ||
370 | runtime->dma_bytes); | ||
371 | return ret; | ||
372 | } | ||
373 | |||
374 | static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) | ||
375 | { | ||
376 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; | ||
377 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
378 | size_t size = IMX_SSI_DMABUF_SIZE; | ||
379 | |||
380 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | ||
381 | buf->dev.dev = pcm->card->dev; | ||
382 | buf->private_data = NULL; | ||
383 | buf->area = dma_alloc_writecombine(pcm->card->dev, size, | ||
384 | &buf->addr, GFP_KERNEL); | ||
385 | if (!buf->area) | ||
386 | return -ENOMEM; | ||
387 | buf->bytes = size; | ||
388 | |||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | static u64 imx_pcm_dmamask = DMA_BIT_MASK(32); | ||
393 | |||
394 | int imx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, | ||
395 | struct snd_pcm *pcm) | ||
396 | { | ||
397 | |||
398 | int ret = 0; | ||
399 | |||
400 | if (!card->dev->dma_mask) | ||
401 | card->dev->dma_mask = &imx_pcm_dmamask; | ||
402 | if (!card->dev->coherent_dma_mask) | ||
403 | card->dev->coherent_dma_mask = DMA_BIT_MASK(32); | ||
404 | if (dai->playback.channels_min) { | ||
405 | ret = imx_pcm_preallocate_dma_buffer(pcm, | ||
406 | SNDRV_PCM_STREAM_PLAYBACK); | ||
407 | if (ret) | ||
408 | goto out; | ||
409 | } | ||
410 | |||
411 | if (dai->capture.channels_min) { | ||
412 | ret = imx_pcm_preallocate_dma_buffer(pcm, | ||
413 | SNDRV_PCM_STREAM_CAPTURE); | ||
414 | if (ret) | ||
415 | goto out; | ||
416 | } | ||
417 | |||
418 | out: | ||
419 | return ret; | ||
420 | } | ||
421 | |||
422 | void imx_pcm_free(struct snd_pcm *pcm) | ||
423 | { | ||
424 | struct snd_pcm_substream *substream; | ||
425 | struct snd_dma_buffer *buf; | ||
426 | int stream; | ||
427 | |||
428 | for (stream = 0; stream < 2; stream++) { | ||
429 | substream = pcm->streams[stream].substream; | ||
430 | if (!substream) | ||
431 | continue; | ||
432 | |||
433 | buf = &substream->dma_buffer; | ||
434 | if (!buf->area) | ||
435 | continue; | ||
436 | |||
437 | dma_free_writecombine(pcm->card->dev, buf->bytes, | ||
438 | buf->area, buf->addr); | ||
439 | buf->area = NULL; | ||
440 | } | ||
441 | } | ||
442 | |||
443 | struct snd_soc_platform imx_soc_platform = { | ||
444 | .name = "imx-audio", | ||
445 | }; | ||
446 | EXPORT_SYMBOL_GPL(imx_soc_platform); | ||
447 | |||
448 | static struct snd_soc_dai imx_ac97_dai = { | ||
449 | .name = "AC97", | ||
450 | .ac97_control = 1, | ||
451 | .playback = { | ||
452 | .stream_name = "AC97 Playback", | ||
453 | .channels_min = 2, | ||
454 | .channels_max = 2, | ||
455 | .rates = SNDRV_PCM_RATE_48000, | ||
456 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
457 | }, | ||
458 | .capture = { | ||
459 | .stream_name = "AC97 Capture", | ||
460 | .channels_min = 2, | ||
461 | .channels_max = 2, | ||
462 | .rates = SNDRV_PCM_RATE_48000, | ||
463 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
464 | }, | ||
465 | .ops = &imx_ssi_pcm_dai_ops, | ||
466 | }; | ||
467 | |||
468 | static void setup_channel_to_ac97(struct imx_ssi *imx_ssi) | ||
469 | { | ||
470 | void __iomem *base = imx_ssi->base; | ||
471 | |||
472 | writel(0x0, base + SSI_SCR); | ||
473 | writel(0x0, base + SSI_STCR); | ||
474 | writel(0x0, base + SSI_SRCR); | ||
475 | |||
476 | writel(SSI_SCR_SYN | SSI_SCR_NET, base + SSI_SCR); | ||
477 | |||
478 | writel(SSI_SFCSR_RFWM0(8) | | ||
479 | SSI_SFCSR_TFWM0(8) | | ||
480 | SSI_SFCSR_RFWM1(8) | | ||
481 | SSI_SFCSR_TFWM1(8), base + SSI_SFCSR); | ||
482 | |||
483 | writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_STCCR); | ||
484 | writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_SRCCR); | ||
485 | |||
486 | writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN, base + SSI_SCR); | ||
487 | writel(SSI_SOR_WAIT(3), base + SSI_SOR); | ||
488 | |||
489 | writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN | | ||
490 | SSI_SCR_TE | SSI_SCR_RE, | ||
491 | base + SSI_SCR); | ||
492 | |||
493 | writel(SSI_SACNT_DEFAULT, base + SSI_SACNT); | ||
494 | writel(0xff, base + SSI_SACCDIS); | ||
495 | writel(0x300, base + SSI_SACCEN); | ||
496 | } | ||
497 | |||
498 | static struct imx_ssi *ac97_ssi; | ||
499 | |||
500 | static void imx_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, | ||
501 | unsigned short val) | ||
502 | { | ||
503 | struct imx_ssi *imx_ssi = ac97_ssi; | ||
504 | void __iomem *base = imx_ssi->base; | ||
505 | unsigned int lreg; | ||
506 | unsigned int lval; | ||
507 | |||
508 | if (reg > 0x7f) | ||
509 | return; | ||
510 | |||
511 | pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val); | ||
512 | |||
513 | lreg = reg << 12; | ||
514 | writel(lreg, base + SSI_SACADD); | ||
515 | |||
516 | lval = val << 4; | ||
517 | writel(lval , base + SSI_SACDAT); | ||
518 | |||
519 | writel(SSI_SACNT_DEFAULT | SSI_SACNT_WR, base + SSI_SACNT); | ||
520 | udelay(100); | ||
521 | } | ||
522 | |||
523 | static unsigned short imx_ssi_ac97_read(struct snd_ac97 *ac97, | ||
524 | unsigned short reg) | ||
525 | { | ||
526 | struct imx_ssi *imx_ssi = ac97_ssi; | ||
527 | void __iomem *base = imx_ssi->base; | ||
528 | |||
529 | unsigned short val = -1; | ||
530 | unsigned int lreg; | ||
531 | |||
532 | lreg = (reg & 0x7f) << 12 ; | ||
533 | writel(lreg, base + SSI_SACADD); | ||
534 | writel(SSI_SACNT_DEFAULT | SSI_SACNT_RD, base + SSI_SACNT); | ||
535 | |||
536 | udelay(100); | ||
537 | |||
538 | val = (readl(base + SSI_SACDAT) >> 4) & 0xffff; | ||
539 | |||
540 | pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val); | ||
541 | |||
542 | return val; | ||
543 | } | ||
544 | |||
545 | static void imx_ssi_ac97_reset(struct snd_ac97 *ac97) | ||
546 | { | ||
547 | struct imx_ssi *imx_ssi = ac97_ssi; | ||
548 | |||
549 | if (imx_ssi->ac97_reset) | ||
550 | imx_ssi->ac97_reset(ac97); | ||
551 | } | ||
552 | |||
553 | static void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97) | ||
554 | { | ||
555 | struct imx_ssi *imx_ssi = ac97_ssi; | ||
556 | |||
557 | if (imx_ssi->ac97_warm_reset) | ||
558 | imx_ssi->ac97_warm_reset(ac97); | ||
559 | } | ||
560 | |||
561 | struct snd_ac97_bus_ops soc_ac97_ops = { | ||
562 | .read = imx_ssi_ac97_read, | ||
563 | .write = imx_ssi_ac97_write, | ||
564 | .reset = imx_ssi_ac97_reset, | ||
565 | .warm_reset = imx_ssi_ac97_warm_reset | ||
566 | }; | ||
567 | EXPORT_SYMBOL_GPL(soc_ac97_ops); | ||
568 | |||
569 | struct snd_soc_dai imx_ssi_pcm_dai[2]; | ||
570 | EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai); | ||
571 | |||
572 | static int imx_ssi_probe(struct platform_device *pdev) | ||
573 | { | ||
574 | struct resource *res; | ||
575 | struct imx_ssi *ssi; | ||
576 | struct imx_ssi_platform_data *pdata = pdev->dev.platform_data; | ||
577 | struct snd_soc_platform *platform; | ||
578 | int ret = 0; | ||
579 | unsigned int val; | ||
580 | struct snd_soc_dai *dai = &imx_ssi_pcm_dai[pdev->id]; | ||
581 | |||
582 | if (dai->id >= ARRAY_SIZE(imx_ssi_pcm_dai)) | ||
583 | return -EINVAL; | ||
584 | |||
585 | ssi = kzalloc(sizeof(*ssi), GFP_KERNEL); | ||
586 | if (!ssi) | ||
587 | return -ENOMEM; | ||
588 | |||
589 | if (pdata) { | ||
590 | ssi->ac97_reset = pdata->ac97_reset; | ||
591 | ssi->ac97_warm_reset = pdata->ac97_warm_reset; | ||
592 | ssi->flags = pdata->flags; | ||
593 | } | ||
594 | |||
595 | ssi->irq = platform_get_irq(pdev, 0); | ||
596 | |||
597 | ssi->clk = clk_get(&pdev->dev, NULL); | ||
598 | if (IS_ERR(ssi->clk)) { | ||
599 | ret = PTR_ERR(ssi->clk); | ||
600 | dev_err(&pdev->dev, "Cannot get the clock: %d\n", | ||
601 | ret); | ||
602 | goto failed_clk; | ||
603 | } | ||
604 | clk_enable(ssi->clk); | ||
605 | |||
606 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
607 | if (!res) { | ||
608 | ret = -ENODEV; | ||
609 | goto failed_get_resource; | ||
610 | } | ||
611 | |||
612 | if (!request_mem_region(res->start, resource_size(res), DRV_NAME)) { | ||
613 | dev_err(&pdev->dev, "request_mem_region failed\n"); | ||
614 | ret = -EBUSY; | ||
615 | goto failed_get_resource; | ||
616 | } | ||
617 | |||
618 | ssi->base = ioremap(res->start, resource_size(res)); | ||
619 | if (!ssi->base) { | ||
620 | dev_err(&pdev->dev, "ioremap failed\n"); | ||
621 | ret = -ENODEV; | ||
622 | goto failed_ioremap; | ||
623 | } | ||
624 | |||
625 | if (ssi->flags & IMX_SSI_USE_AC97) { | ||
626 | if (ac97_ssi) { | ||
627 | ret = -EBUSY; | ||
628 | goto failed_ac97; | ||
629 | } | ||
630 | ac97_ssi = ssi; | ||
631 | setup_channel_to_ac97(ssi); | ||
632 | memcpy(dai, &imx_ac97_dai, sizeof(imx_ac97_dai)); | ||
633 | } else | ||
634 | memcpy(dai, &imx_ssi_dai, sizeof(imx_ssi_dai)); | ||
635 | |||
636 | writel(0x0, ssi->base + SSI_SIER); | ||
637 | |||
638 | ssi->dma_params_rx.dma_addr = res->start + SSI_SRX0; | ||
639 | ssi->dma_params_tx.dma_addr = res->start + SSI_STX0; | ||
640 | |||
641 | res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0"); | ||
642 | if (res) | ||
643 | ssi->dma_params_tx.dma = res->start; | ||
644 | |||
645 | res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx0"); | ||
646 | if (res) | ||
647 | ssi->dma_params_rx.dma = res->start; | ||
648 | |||
649 | dai->id = pdev->id; | ||
650 | dai->dev = &pdev->dev; | ||
651 | dai->name = kasprintf(GFP_KERNEL, "imx-ssi.%d", pdev->id); | ||
652 | dai->private_data = ssi; | ||
653 | |||
654 | if ((cpu_is_mx27() || cpu_is_mx21()) && | ||
655 | !(ssi->flags & IMX_SSI_USE_AC97)) { | ||
656 | ssi->flags |= IMX_SSI_DMA; | ||
657 | platform = imx_ssi_dma_mx2_init(pdev, ssi); | ||
658 | } else | ||
659 | platform = imx_ssi_fiq_init(pdev, ssi); | ||
660 | |||
661 | imx_soc_platform.pcm_ops = platform->pcm_ops; | ||
662 | imx_soc_platform.pcm_new = platform->pcm_new; | ||
663 | imx_soc_platform.pcm_free = platform->pcm_free; | ||
664 | |||
665 | val = SSI_SFCSR_TFWM0(ssi->dma_params_tx.burstsize) | | ||
666 | SSI_SFCSR_RFWM0(ssi->dma_params_rx.burstsize); | ||
667 | writel(val, ssi->base + SSI_SFCSR); | ||
668 | |||
669 | ret = snd_soc_register_dai(dai); | ||
670 | if (ret) { | ||
671 | dev_err(&pdev->dev, "register DAI failed\n"); | ||
672 | goto failed_register; | ||
673 | } | ||
674 | |||
675 | platform_set_drvdata(pdev, ssi); | ||
676 | |||
677 | return 0; | ||
678 | |||
679 | failed_register: | ||
680 | failed_ac97: | ||
681 | iounmap(ssi->base); | ||
682 | failed_ioremap: | ||
683 | release_mem_region(res->start, resource_size(res)); | ||
684 | failed_get_resource: | ||
685 | clk_disable(ssi->clk); | ||
686 | clk_put(ssi->clk); | ||
687 | failed_clk: | ||
688 | kfree(ssi); | ||
689 | |||
690 | return ret; | ||
691 | } | ||
692 | |||
693 | static int __devexit imx_ssi_remove(struct platform_device *pdev) | ||
694 | { | ||
695 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
696 | struct imx_ssi *ssi = platform_get_drvdata(pdev); | ||
697 | struct snd_soc_dai *dai = &imx_ssi_pcm_dai[pdev->id]; | ||
698 | |||
699 | snd_soc_unregister_dai(dai); | ||
700 | |||
701 | if (ssi->flags & IMX_SSI_USE_AC97) | ||
702 | ac97_ssi = NULL; | ||
703 | |||
704 | if (!(ssi->flags & IMX_SSI_DMA)) | ||
705 | imx_ssi_fiq_exit(pdev, ssi); | ||
706 | |||
707 | iounmap(ssi->base); | ||
708 | release_mem_region(res->start, resource_size(res)); | ||
709 | clk_disable(ssi->clk); | ||
710 | clk_put(ssi->clk); | ||
711 | kfree(ssi); | ||
712 | |||
713 | return 0; | ||
714 | } | ||
715 | |||
716 | static struct platform_driver imx_ssi_driver = { | ||
717 | .probe = imx_ssi_probe, | ||
718 | .remove = __devexit_p(imx_ssi_remove), | ||
719 | |||
720 | .driver = { | ||
721 | .name = DRV_NAME, | ||
722 | .owner = THIS_MODULE, | ||
723 | }, | ||
724 | }; | ||
725 | |||
726 | static int __init imx_ssi_init(void) | ||
727 | { | ||
728 | int ret; | ||
729 | |||
730 | ret = snd_soc_register_platform(&imx_soc_platform); | ||
731 | if (ret) { | ||
732 | pr_err("failed to register soc platform: %d\n", ret); | ||
733 | return ret; | ||
734 | } | ||
735 | |||
736 | ret = platform_driver_register(&imx_ssi_driver); | ||
737 | if (ret) { | ||
738 | snd_soc_unregister_platform(&imx_soc_platform); | ||
739 | return ret; | ||
740 | } | ||
741 | |||
742 | return 0; | ||
743 | } | ||
744 | |||
745 | static void __exit imx_ssi_exit(void) | ||
746 | { | ||
747 | platform_driver_unregister(&imx_ssi_driver); | ||
748 | snd_soc_unregister_platform(&imx_soc_platform); | ||
749 | } | ||
750 | |||
751 | module_init(imx_ssi_init); | ||
752 | module_exit(imx_ssi_exit); | ||
753 | |||
754 | /* Module information */ | ||
755 | MODULE_AUTHOR("Sascha Hauer, <s.hauer@pengutronix.de>"); | ||
756 | MODULE_DESCRIPTION("i.MX I2S/ac97 SoC Interface"); | ||
757 | MODULE_LICENSE("GPL"); | ||
758 | |||
diff --git a/sound/soc/imx/imx-ssi.h b/sound/soc/imx/imx-ssi.h new file mode 100644 index 000000000000..55f26ebcd8c2 --- /dev/null +++ b/sound/soc/imx/imx-ssi.h | |||
@@ -0,0 +1,237 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or modify | ||
3 | * it under the terms of the GNU General Public License version 2 as | ||
4 | * published by the Free Software Foundation. | ||
5 | */ | ||
6 | |||
7 | #ifndef _IMX_SSI_H | ||
8 | #define _IMX_SSI_H | ||
9 | |||
10 | #define SSI_STX0 0x00 | ||
11 | #define SSI_STX1 0x04 | ||
12 | #define SSI_SRX0 0x08 | ||
13 | #define SSI_SRX1 0x0c | ||
14 | |||
15 | #define SSI_SCR 0x10 | ||
16 | #define SSI_SCR_CLK_IST (1 << 9) | ||
17 | #define SSI_SCR_CLK_IST_SHIFT 9 | ||
18 | #define SSI_SCR_TCH_EN (1 << 8) | ||
19 | #define SSI_SCR_SYS_CLK_EN (1 << 7) | ||
20 | #define SSI_SCR_I2S_MODE_NORM (0 << 5) | ||
21 | #define SSI_SCR_I2S_MODE_MSTR (1 << 5) | ||
22 | #define SSI_SCR_I2S_MODE_SLAVE (2 << 5) | ||
23 | #define SSI_I2S_MODE_MASK (3 << 5) | ||
24 | #define SSI_SCR_SYN (1 << 4) | ||
25 | #define SSI_SCR_NET (1 << 3) | ||
26 | #define SSI_SCR_RE (1 << 2) | ||
27 | #define SSI_SCR_TE (1 << 1) | ||
28 | #define SSI_SCR_SSIEN (1 << 0) | ||
29 | |||
30 | #define SSI_SISR 0x14 | ||
31 | #define SSI_SISR_MASK ((1 << 19) - 1) | ||
32 | #define SSI_SISR_CMDAU (1 << 18) | ||
33 | #define SSI_SISR_CMDDU (1 << 17) | ||
34 | #define SSI_SISR_RXT (1 << 16) | ||
35 | #define SSI_SISR_RDR1 (1 << 15) | ||
36 | #define SSI_SISR_RDR0 (1 << 14) | ||
37 | #define SSI_SISR_TDE1 (1 << 13) | ||
38 | #define SSI_SISR_TDE0 (1 << 12) | ||
39 | #define SSI_SISR_ROE1 (1 << 11) | ||
40 | #define SSI_SISR_ROE0 (1 << 10) | ||
41 | #define SSI_SISR_TUE1 (1 << 9) | ||
42 | #define SSI_SISR_TUE0 (1 << 8) | ||
43 | #define SSI_SISR_TFS (1 << 7) | ||
44 | #define SSI_SISR_RFS (1 << 6) | ||
45 | #define SSI_SISR_TLS (1 << 5) | ||
46 | #define SSI_SISR_RLS (1 << 4) | ||
47 | #define SSI_SISR_RFF1 (1 << 3) | ||
48 | #define SSI_SISR_RFF0 (1 << 2) | ||
49 | #define SSI_SISR_TFE1 (1 << 1) | ||
50 | #define SSI_SISR_TFE0 (1 << 0) | ||
51 | |||
52 | #define SSI_SIER 0x18 | ||
53 | #define SSI_SIER_RDMAE (1 << 22) | ||
54 | #define SSI_SIER_RIE (1 << 21) | ||
55 | #define SSI_SIER_TDMAE (1 << 20) | ||
56 | #define SSI_SIER_TIE (1 << 19) | ||
57 | #define SSI_SIER_CMDAU_EN (1 << 18) | ||
58 | #define SSI_SIER_CMDDU_EN (1 << 17) | ||
59 | #define SSI_SIER_RXT_EN (1 << 16) | ||
60 | #define SSI_SIER_RDR1_EN (1 << 15) | ||
61 | #define SSI_SIER_RDR0_EN (1 << 14) | ||
62 | #define SSI_SIER_TDE1_EN (1 << 13) | ||
63 | #define SSI_SIER_TDE0_EN (1 << 12) | ||
64 | #define SSI_SIER_ROE1_EN (1 << 11) | ||
65 | #define SSI_SIER_ROE0_EN (1 << 10) | ||
66 | #define SSI_SIER_TUE1_EN (1 << 9) | ||
67 | #define SSI_SIER_TUE0_EN (1 << 8) | ||
68 | #define SSI_SIER_TFS_EN (1 << 7) | ||
69 | #define SSI_SIER_RFS_EN (1 << 6) | ||
70 | #define SSI_SIER_TLS_EN (1 << 5) | ||
71 | #define SSI_SIER_RLS_EN (1 << 4) | ||
72 | #define SSI_SIER_RFF1_EN (1 << 3) | ||
73 | #define SSI_SIER_RFF0_EN (1 << 2) | ||
74 | #define SSI_SIER_TFE1_EN (1 << 1) | ||
75 | #define SSI_SIER_TFE0_EN (1 << 0) | ||
76 | |||
77 | #define SSI_STCR 0x1c | ||
78 | #define SSI_STCR_TXBIT0 (1 << 9) | ||
79 | #define SSI_STCR_TFEN1 (1 << 8) | ||
80 | #define SSI_STCR_TFEN0 (1 << 7) | ||
81 | #define SSI_FIFO_ENABLE_0_SHIFT 7 | ||
82 | #define SSI_STCR_TFDIR (1 << 6) | ||
83 | #define SSI_STCR_TXDIR (1 << 5) | ||
84 | #define SSI_STCR_TSHFD (1 << 4) | ||
85 | #define SSI_STCR_TSCKP (1 << 3) | ||
86 | #define SSI_STCR_TFSI (1 << 2) | ||
87 | #define SSI_STCR_TFSL (1 << 1) | ||
88 | #define SSI_STCR_TEFS (1 << 0) | ||
89 | |||
90 | #define SSI_SRCR 0x20 | ||
91 | #define SSI_SRCR_RXBIT0 (1 << 9) | ||
92 | #define SSI_SRCR_RFEN1 (1 << 8) | ||
93 | #define SSI_SRCR_RFEN0 (1 << 7) | ||
94 | #define SSI_FIFO_ENABLE_0_SHIFT 7 | ||
95 | #define SSI_SRCR_RFDIR (1 << 6) | ||
96 | #define SSI_SRCR_RXDIR (1 << 5) | ||
97 | #define SSI_SRCR_RSHFD (1 << 4) | ||
98 | #define SSI_SRCR_RSCKP (1 << 3) | ||
99 | #define SSI_SRCR_RFSI (1 << 2) | ||
100 | #define SSI_SRCR_RFSL (1 << 1) | ||
101 | #define SSI_SRCR_REFS (1 << 0) | ||
102 | |||
103 | #define SSI_SRCCR 0x28 | ||
104 | #define SSI_SRCCR_DIV2 (1 << 18) | ||
105 | #define SSI_SRCCR_PSR (1 << 17) | ||
106 | #define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13) | ||
107 | #define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8) | ||
108 | #define SSI_SRCCR_PM(x) (((x) & 0xff) << 0) | ||
109 | #define SSI_SRCCR_WL_MASK (0xf << 13) | ||
110 | #define SSI_SRCCR_DC_MASK (0x1f << 8) | ||
111 | #define SSI_SRCCR_PM_MASK (0xff << 0) | ||
112 | |||
113 | #define SSI_STCCR 0x24 | ||
114 | #define SSI_STCCR_DIV2 (1 << 18) | ||
115 | #define SSI_STCCR_PSR (1 << 17) | ||
116 | #define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13) | ||
117 | #define SSI_STCCR_DC(x) (((x) & 0x1f) << 8) | ||
118 | #define SSI_STCCR_PM(x) (((x) & 0xff) << 0) | ||
119 | #define SSI_STCCR_WL_MASK (0xf << 13) | ||
120 | #define SSI_STCCR_DC_MASK (0x1f << 8) | ||
121 | #define SSI_STCCR_PM_MASK (0xff << 0) | ||
122 | |||
123 | #define SSI_SFCSR 0x2c | ||
124 | #define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28) | ||
125 | #define SSI_RX_FIFO_1_COUNT_SHIFT 28 | ||
126 | #define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24) | ||
127 | #define SSI_TX_FIFO_1_COUNT_SHIFT 24 | ||
128 | #define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20) | ||
129 | #define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16) | ||
130 | #define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12) | ||
131 | #define SSI_RX_FIFO_0_COUNT_SHIFT 12 | ||
132 | #define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8) | ||
133 | #define SSI_TX_FIFO_0_COUNT_SHIFT 8 | ||
134 | #define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4) | ||
135 | #define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0) | ||
136 | #define SSI_SFCSR_RFWM0_MASK (0xf << 4) | ||
137 | #define SSI_SFCSR_TFWM0_MASK (0xf << 0) | ||
138 | |||
139 | #define SSI_STR 0x30 | ||
140 | #define SSI_STR_TEST (1 << 15) | ||
141 | #define SSI_STR_RCK2TCK (1 << 14) | ||
142 | #define SSI_STR_RFS2TFS (1 << 13) | ||
143 | #define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8) | ||
144 | #define SSI_STR_TXD2RXD (1 << 7) | ||
145 | #define SSI_STR_TCK2RCK (1 << 6) | ||
146 | #define SSI_STR_TFS2RFS (1 << 5) | ||
147 | #define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0) | ||
148 | |||
149 | #define SSI_SOR 0x34 | ||
150 | #define SSI_SOR_CLKOFF (1 << 6) | ||
151 | #define SSI_SOR_RX_CLR (1 << 5) | ||
152 | #define SSI_SOR_TX_CLR (1 << 4) | ||
153 | #define SSI_SOR_INIT (1 << 3) | ||
154 | #define SSI_SOR_WAIT(x) (((x) & 0x3) << 1) | ||
155 | #define SSI_SOR_WAIT_MASK (0x3 << 1) | ||
156 | #define SSI_SOR_SYNRST (1 << 0) | ||
157 | |||
158 | #define SSI_SACNT 0x38 | ||
159 | #define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5) | ||
160 | #define SSI_SACNT_WR (1 << 4) | ||
161 | #define SSI_SACNT_RD (1 << 3) | ||
162 | #define SSI_SACNT_TIF (1 << 2) | ||
163 | #define SSI_SACNT_FV (1 << 1) | ||
164 | #define SSI_SACNT_AC97EN (1 << 0) | ||
165 | |||
166 | #define SSI_SACADD 0x3c | ||
167 | #define SSI_SACDAT 0x40 | ||
168 | #define SSI_SATAG 0x44 | ||
169 | #define SSI_STMSK 0x48 | ||
170 | #define SSI_SRMSK 0x4c | ||
171 | #define SSI_SACCST 0x50 | ||
172 | #define SSI_SACCEN 0x54 | ||
173 | #define SSI_SACCDIS 0x58 | ||
174 | |||
175 | /* SSI clock sources */ | ||
176 | #define IMX_SSP_SYS_CLK 0 | ||
177 | |||
178 | /* SSI audio dividers */ | ||
179 | #define IMX_SSI_TX_DIV_2 0 | ||
180 | #define IMX_SSI_TX_DIV_PSR 1 | ||
181 | #define IMX_SSI_TX_DIV_PM 2 | ||
182 | #define IMX_SSI_RX_DIV_2 3 | ||
183 | #define IMX_SSI_RX_DIV_PSR 4 | ||
184 | #define IMX_SSI_RX_DIV_PM 5 | ||
185 | |||
186 | extern struct snd_soc_dai imx_ssi_pcm_dai[2]; | ||
187 | extern struct snd_soc_platform imx_soc_platform; | ||
188 | |||
189 | #define DRV_NAME "imx-ssi" | ||
190 | |||
191 | struct imx_pcm_dma_params { | ||
192 | int dma; | ||
193 | unsigned long dma_addr; | ||
194 | int burstsize; | ||
195 | }; | ||
196 | |||
197 | struct imx_ssi { | ||
198 | struct platform_device *ac97_dev; | ||
199 | |||
200 | struct snd_soc_device imx_ac97; | ||
201 | struct clk *clk; | ||
202 | void __iomem *base; | ||
203 | int irq; | ||
204 | int fiq_enable; | ||
205 | unsigned int offset; | ||
206 | |||
207 | unsigned int flags; | ||
208 | |||
209 | void (*ac97_reset) (struct snd_ac97 *ac97); | ||
210 | void (*ac97_warm_reset)(struct snd_ac97 *ac97); | ||
211 | |||
212 | struct imx_pcm_dma_params dma_params_rx; | ||
213 | struct imx_pcm_dma_params dma_params_tx; | ||
214 | |||
215 | int enabled; | ||
216 | }; | ||
217 | |||
218 | struct snd_soc_platform *imx_ssi_fiq_init(struct platform_device *pdev, | ||
219 | struct imx_ssi *ssi); | ||
220 | void imx_ssi_fiq_exit(struct platform_device *pdev, struct imx_ssi *ssi); | ||
221 | struct snd_soc_platform *imx_ssi_dma_mx2_init(struct platform_device *pdev, | ||
222 | struct imx_ssi *ssi); | ||
223 | |||
224 | int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma); | ||
225 | int imx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, | ||
226 | struct snd_pcm *pcm); | ||
227 | void imx_pcm_free(struct snd_pcm *pcm); | ||
228 | |||
229 | /* | ||
230 | * Do not change this as the FIQ handler depends on this size | ||
231 | */ | ||
232 | #define IMX_SSI_DMABUF_SIZE (64 * 1024) | ||
233 | |||
234 | #define DMA_RXFIFO_BURST 0x4 | ||
235 | #define DMA_TXFIFO_BURST 0x6 | ||
236 | |||
237 | #endif /* _IMX_SSI_H */ | ||
diff --git a/sound/soc/imx/mx1_mx2-pcm.c b/sound/soc/imx/mx1_mx2-pcm.c deleted file mode 100644 index bffffcd5ff34..000000000000 --- a/sound/soc/imx/mx1_mx2-pcm.c +++ /dev/null | |||
@@ -1,488 +0,0 @@ | |||
1 | /* | ||
2 | * mx1_mx2-pcm.c -- ALSA SoC interface for Freescale i.MX1x, i.MX2x CPUs | ||
3 | * | ||
4 | * Copyright 2009 Vista Silicon S.L. | ||
5 | * Author: Javier Martin | ||
6 | * javier.martin@vista-silicon.com | ||
7 | * | ||
8 | * Based on mxc-pcm.c by Liam Girdwood. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | * | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/dma-mapping.h> | ||
22 | #include <sound/core.h> | ||
23 | #include <sound/pcm.h> | ||
24 | #include <sound/pcm_params.h> | ||
25 | #include <sound/soc.h> | ||
26 | #include <asm/dma.h> | ||
27 | #include <mach/hardware.h> | ||
28 | #include <mach/dma-mx1-mx2.h> | ||
29 | |||
30 | #include "mx1_mx2-pcm.h" | ||
31 | |||
32 | |||
33 | static const struct snd_pcm_hardware mx1_mx2_pcm_hardware = { | ||
34 | .info = (SNDRV_PCM_INFO_INTERLEAVED | | ||
35 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
36 | SNDRV_PCM_INFO_MMAP | | ||
37 | SNDRV_PCM_INFO_MMAP_VALID), | ||
38 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
39 | .buffer_bytes_max = 32 * 1024, | ||
40 | .period_bytes_min = 64, | ||
41 | .period_bytes_max = 8 * 1024, | ||
42 | .periods_min = 2, | ||
43 | .periods_max = 255, | ||
44 | .fifo_size = 0, | ||
45 | }; | ||
46 | |||
47 | struct mx1_mx2_runtime_data { | ||
48 | int dma_ch; | ||
49 | int active; | ||
50 | unsigned int period; | ||
51 | unsigned int periods; | ||
52 | int tx_spin; | ||
53 | spinlock_t dma_lock; | ||
54 | struct mx1_mx2_pcm_dma_params *dma_params; | ||
55 | }; | ||
56 | |||
57 | |||
58 | /** | ||
59 | * This function stops the current dma transfer for playback | ||
60 | * and clears the dma pointers. | ||
61 | * | ||
62 | * @param substream pointer to the structure of the current stream. | ||
63 | * | ||
64 | */ | ||
65 | static int audio_stop_dma(struct snd_pcm_substream *substream) | ||
66 | { | ||
67 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
68 | struct mx1_mx2_runtime_data *prtd = runtime->private_data; | ||
69 | unsigned long flags; | ||
70 | |||
71 | spin_lock_irqsave(&prtd->dma_lock, flags); | ||
72 | |||
73 | pr_debug("%s\n", __func__); | ||
74 | |||
75 | prtd->active = 0; | ||
76 | prtd->period = 0; | ||
77 | prtd->periods = 0; | ||
78 | |||
79 | /* this stops the dma channel and clears the buffer ptrs */ | ||
80 | |||
81 | imx_dma_disable(prtd->dma_ch); | ||
82 | |||
83 | spin_unlock_irqrestore(&prtd->dma_lock, flags); | ||
84 | |||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * This function is called whenever a new audio block needs to be | ||
90 | * transferred to the codec. The function receives the address and the size | ||
91 | * of the new block and start a new DMA transfer. | ||
92 | * | ||
93 | * @param substream pointer to the structure of the current stream. | ||
94 | * | ||
95 | */ | ||
96 | static int dma_new_period(struct snd_pcm_substream *substream) | ||
97 | { | ||
98 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
99 | struct mx1_mx2_runtime_data *prtd = runtime->private_data; | ||
100 | unsigned int dma_size; | ||
101 | unsigned int offset; | ||
102 | int ret = 0; | ||
103 | dma_addr_t mem_addr; | ||
104 | unsigned int dev_addr; | ||
105 | |||
106 | if (prtd->active) { | ||
107 | dma_size = frames_to_bytes(runtime, runtime->period_size); | ||
108 | offset = dma_size * prtd->period; | ||
109 | |||
110 | pr_debug("%s: period (%d) out of (%d)\n", __func__, | ||
111 | prtd->period, | ||
112 | runtime->periods); | ||
113 | pr_debug("period_size %d frames\n offset %d bytes\n", | ||
114 | (unsigned int)runtime->period_size, | ||
115 | offset); | ||
116 | pr_debug("dma_size %d bytes\n", dma_size); | ||
117 | |||
118 | snd_BUG_ON(dma_size > mx1_mx2_pcm_hardware.period_bytes_max); | ||
119 | |||
120 | mem_addr = (dma_addr_t)(runtime->dma_addr + offset); | ||
121 | dev_addr = prtd->dma_params->per_address; | ||
122 | pr_debug("%s: mem_addr is %x\n dev_addr is %x\n", | ||
123 | __func__, mem_addr, dev_addr); | ||
124 | |||
125 | ret = imx_dma_setup_single(prtd->dma_ch, mem_addr, | ||
126 | dma_size, dev_addr, | ||
127 | prtd->dma_params->transfer_type); | ||
128 | if (ret < 0) { | ||
129 | printk(KERN_ERR "Error %d configuring DMA\n", ret); | ||
130 | return ret; | ||
131 | } | ||
132 | imx_dma_enable(prtd->dma_ch); | ||
133 | |||
134 | pr_debug("%s: transfer enabled\nmem_addr = %x\n", | ||
135 | __func__, (unsigned int) mem_addr); | ||
136 | pr_debug("dev_addr = %x\ndma_size = %d\n", | ||
137 | (unsigned int) dev_addr, dma_size); | ||
138 | |||
139 | prtd->tx_spin = 1; /* FGA little trick to retrieve DMA pos */ | ||
140 | prtd->period++; | ||
141 | prtd->period %= runtime->periods; | ||
142 | } | ||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | |||
147 | /** | ||
148 | * This is a callback which will be called | ||
149 | * when a TX transfer finishes. The call occurs | ||
150 | * in interrupt context. | ||
151 | * | ||
152 | * @param dat pointer to the structure of the current stream. | ||
153 | * | ||
154 | */ | ||
155 | static void audio_dma_irq(int channel, void *data) | ||
156 | { | ||
157 | struct snd_pcm_substream *substream; | ||
158 | struct snd_pcm_runtime *runtime; | ||
159 | struct mx1_mx2_runtime_data *prtd; | ||
160 | unsigned int dma_size; | ||
161 | unsigned int previous_period; | ||
162 | unsigned int offset; | ||
163 | |||
164 | substream = data; | ||
165 | runtime = substream->runtime; | ||
166 | prtd = runtime->private_data; | ||
167 | previous_period = prtd->periods; | ||
168 | dma_size = frames_to_bytes(runtime, runtime->period_size); | ||
169 | offset = dma_size * previous_period; | ||
170 | |||
171 | prtd->tx_spin = 0; | ||
172 | prtd->periods++; | ||
173 | prtd->periods %= runtime->periods; | ||
174 | |||
175 | pr_debug("%s: irq per %d offset %x\n", __func__, prtd->periods, offset); | ||
176 | |||
177 | /* | ||
178 | * If we are getting a callback for an active stream then we inform | ||
179 | * the PCM middle layer we've finished a period | ||
180 | */ | ||
181 | if (prtd->active) | ||
182 | snd_pcm_period_elapsed(substream); | ||
183 | |||
184 | /* | ||
185 | * Trig next DMA transfer | ||
186 | */ | ||
187 | dma_new_period(substream); | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * This function configures the hardware to allow audio | ||
192 | * playback operations. It is called by ALSA framework. | ||
193 | * | ||
194 | * @param substream pointer to the structure of the current stream. | ||
195 | * | ||
196 | * @return 0 on success, -1 otherwise. | ||
197 | */ | ||
198 | static int | ||
199 | snd_mx1_mx2_prepare(struct snd_pcm_substream *substream) | ||
200 | { | ||
201 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
202 | struct mx1_mx2_runtime_data *prtd = runtime->private_data; | ||
203 | |||
204 | prtd->period = 0; | ||
205 | prtd->periods = 0; | ||
206 | |||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | static int mx1_mx2_pcm_hw_params(struct snd_pcm_substream *substream, | ||
211 | struct snd_pcm_hw_params *hw_params) | ||
212 | { | ||
213 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
214 | int ret; | ||
215 | |||
216 | ret = snd_pcm_lib_malloc_pages(substream, | ||
217 | params_buffer_bytes(hw_params)); | ||
218 | if (ret < 0) { | ||
219 | printk(KERN_ERR "%s: Error %d failed to malloc pcm pages \n", | ||
220 | __func__, ret); | ||
221 | return ret; | ||
222 | } | ||
223 | |||
224 | pr_debug("%s: snd_imx1_mx2_audio_hw_params runtime->dma_addr 0x(%x)\n", | ||
225 | __func__, (unsigned int)runtime->dma_addr); | ||
226 | pr_debug("%s: snd_imx1_mx2_audio_hw_params runtime->dma_area 0x(%x)\n", | ||
227 | __func__, (unsigned int)runtime->dma_area); | ||
228 | pr_debug("%s: snd_imx1_mx2_audio_hw_params runtime->dma_bytes 0x(%x)\n", | ||
229 | __func__, (unsigned int)runtime->dma_bytes); | ||
230 | |||
231 | return ret; | ||
232 | } | ||
233 | |||
234 | static int mx1_mx2_pcm_hw_free(struct snd_pcm_substream *substream) | ||
235 | { | ||
236 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
237 | struct mx1_mx2_runtime_data *prtd = runtime->private_data; | ||
238 | |||
239 | imx_dma_free(prtd->dma_ch); | ||
240 | |||
241 | snd_pcm_lib_free_pages(substream); | ||
242 | |||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | static int mx1_mx2_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
247 | { | ||
248 | struct mx1_mx2_runtime_data *prtd = substream->runtime->private_data; | ||
249 | int ret = 0; | ||
250 | |||
251 | switch (cmd) { | ||
252 | case SNDRV_PCM_TRIGGER_START: | ||
253 | prtd->tx_spin = 0; | ||
254 | /* requested stream startup */ | ||
255 | prtd->active = 1; | ||
256 | pr_debug("%s: starting dma_new_period\n", __func__); | ||
257 | ret = dma_new_period(substream); | ||
258 | break; | ||
259 | case SNDRV_PCM_TRIGGER_STOP: | ||
260 | /* requested stream shutdown */ | ||
261 | pr_debug("%s: stopping dma transfer\n", __func__); | ||
262 | ret = audio_stop_dma(substream); | ||
263 | break; | ||
264 | default: | ||
265 | ret = -EINVAL; | ||
266 | break; | ||
267 | } | ||
268 | |||
269 | return ret; | ||
270 | } | ||
271 | |||
272 | static snd_pcm_uframes_t | ||
273 | mx1_mx2_pcm_pointer(struct snd_pcm_substream *substream) | ||
274 | { | ||
275 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
276 | struct mx1_mx2_runtime_data *prtd = runtime->private_data; | ||
277 | unsigned int offset = 0; | ||
278 | |||
279 | /* tx_spin value is used here to check if a transfer is active */ | ||
280 | if (prtd->tx_spin) { | ||
281 | offset = (runtime->period_size * (prtd->periods)) + | ||
282 | (runtime->period_size >> 1); | ||
283 | if (offset >= runtime->buffer_size) | ||
284 | offset = runtime->period_size >> 1; | ||
285 | } else { | ||
286 | offset = (runtime->period_size * (prtd->periods)); | ||
287 | if (offset >= runtime->buffer_size) | ||
288 | offset = 0; | ||
289 | } | ||
290 | pr_debug("%s: pointer offset %x\n", __func__, offset); | ||
291 | |||
292 | return offset; | ||
293 | } | ||
294 | |||
295 | static int mx1_mx2_pcm_open(struct snd_pcm_substream *substream) | ||
296 | { | ||
297 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
298 | struct mx1_mx2_runtime_data *prtd; | ||
299 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
300 | struct mx1_mx2_pcm_dma_params *dma_data = rtd->dai->cpu_dai->dma_data; | ||
301 | int ret; | ||
302 | |||
303 | snd_soc_set_runtime_hwparams(substream, &mx1_mx2_pcm_hardware); | ||
304 | |||
305 | ret = snd_pcm_hw_constraint_integer(runtime, | ||
306 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
307 | if (ret < 0) | ||
308 | return ret; | ||
309 | |||
310 | prtd = kzalloc(sizeof(struct mx1_mx2_runtime_data), GFP_KERNEL); | ||
311 | if (prtd == NULL) { | ||
312 | ret = -ENOMEM; | ||
313 | goto out; | ||
314 | } | ||
315 | |||
316 | runtime->private_data = prtd; | ||
317 | |||
318 | if (!dma_data) | ||
319 | return -ENODEV; | ||
320 | |||
321 | prtd->dma_params = dma_data; | ||
322 | |||
323 | pr_debug("%s: Requesting dma channel (%s)\n", __func__, | ||
324 | prtd->dma_params->name); | ||
325 | ret = imx_dma_request_by_prio(prtd->dma_params->name, DMA_PRIO_HIGH); | ||
326 | if (ret < 0) { | ||
327 | printk(KERN_ERR "Error %d requesting dma channel\n", ret); | ||
328 | return ret; | ||
329 | } | ||
330 | prtd->dma_ch = ret; | ||
331 | imx_dma_config_burstlen(prtd->dma_ch, | ||
332 | prtd->dma_params->watermark_level); | ||
333 | |||
334 | ret = imx_dma_config_channel(prtd->dma_ch, | ||
335 | prtd->dma_params->per_config, | ||
336 | prtd->dma_params->mem_config, | ||
337 | prtd->dma_params->event_id, 0); | ||
338 | |||
339 | if (ret) { | ||
340 | pr_debug(KERN_ERR "Error %d configuring dma channel %d\n", | ||
341 | ret, prtd->dma_ch); | ||
342 | return ret; | ||
343 | } | ||
344 | |||
345 | pr_debug("%s: Setting tx dma callback function\n", __func__); | ||
346 | ret = imx_dma_setup_handlers(prtd->dma_ch, | ||
347 | audio_dma_irq, NULL, | ||
348 | (void *)substream); | ||
349 | if (ret < 0) { | ||
350 | printk(KERN_ERR "Error %d setting dma callback function\n", ret); | ||
351 | return ret; | ||
352 | } | ||
353 | return 0; | ||
354 | |||
355 | out: | ||
356 | return ret; | ||
357 | } | ||
358 | |||
359 | static int mx1_mx2_pcm_close(struct snd_pcm_substream *substream) | ||
360 | { | ||
361 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
362 | struct mx1_mx2_runtime_data *prtd = runtime->private_data; | ||
363 | |||
364 | kfree(prtd); | ||
365 | |||
366 | return 0; | ||
367 | } | ||
368 | |||
369 | static int mx1_mx2_pcm_mmap(struct snd_pcm_substream *substream, | ||
370 | struct vm_area_struct *vma) | ||
371 | { | ||
372 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
373 | return dma_mmap_writecombine(substream->pcm->card->dev, vma, | ||
374 | runtime->dma_area, | ||
375 | runtime->dma_addr, | ||
376 | runtime->dma_bytes); | ||
377 | } | ||
378 | |||
379 | static struct snd_pcm_ops mx1_mx2_pcm_ops = { | ||
380 | .open = mx1_mx2_pcm_open, | ||
381 | .close = mx1_mx2_pcm_close, | ||
382 | .ioctl = snd_pcm_lib_ioctl, | ||
383 | .hw_params = mx1_mx2_pcm_hw_params, | ||
384 | .hw_free = mx1_mx2_pcm_hw_free, | ||
385 | .prepare = snd_mx1_mx2_prepare, | ||
386 | .trigger = mx1_mx2_pcm_trigger, | ||
387 | .pointer = mx1_mx2_pcm_pointer, | ||
388 | .mmap = mx1_mx2_pcm_mmap, | ||
389 | }; | ||
390 | |||
391 | static u64 mx1_mx2_pcm_dmamask = 0xffffffff; | ||
392 | |||
393 | static int mx1_mx2_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) | ||
394 | { | ||
395 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; | ||
396 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
397 | size_t size = mx1_mx2_pcm_hardware.buffer_bytes_max; | ||
398 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | ||
399 | buf->dev.dev = pcm->card->dev; | ||
400 | buf->private_data = NULL; | ||
401 | |||
402 | /* Reserve uncached-buffered memory area for DMA */ | ||
403 | buf->area = dma_alloc_writecombine(pcm->card->dev, size, | ||
404 | &buf->addr, GFP_KERNEL); | ||
405 | |||
406 | pr_debug("%s: preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", | ||
407 | __func__, (void *) buf->area, (void *) buf->addr, size); | ||
408 | |||
409 | if (!buf->area) | ||
410 | return -ENOMEM; | ||
411 | |||
412 | buf->bytes = size; | ||
413 | return 0; | ||
414 | } | ||
415 | |||
416 | static void mx1_mx2_pcm_free_dma_buffers(struct snd_pcm *pcm) | ||
417 | { | ||
418 | struct snd_pcm_substream *substream; | ||
419 | struct snd_dma_buffer *buf; | ||
420 | int stream; | ||
421 | |||
422 | for (stream = 0; stream < 2; stream++) { | ||
423 | substream = pcm->streams[stream].substream; | ||
424 | if (!substream) | ||
425 | continue; | ||
426 | |||
427 | buf = &substream->dma_buffer; | ||
428 | if (!buf->area) | ||
429 | continue; | ||
430 | |||
431 | dma_free_writecombine(pcm->card->dev, buf->bytes, | ||
432 | buf->area, buf->addr); | ||
433 | buf->area = NULL; | ||
434 | } | ||
435 | } | ||
436 | |||
437 | static int mx1_mx2_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, | ||
438 | struct snd_pcm *pcm) | ||
439 | { | ||
440 | int ret = 0; | ||
441 | |||
442 | if (!card->dev->dma_mask) | ||
443 | card->dev->dma_mask = &mx1_mx2_pcm_dmamask; | ||
444 | if (!card->dev->coherent_dma_mask) | ||
445 | card->dev->coherent_dma_mask = 0xffffffff; | ||
446 | |||
447 | if (dai->playback.channels_min) { | ||
448 | ret = mx1_mx2_pcm_preallocate_dma_buffer(pcm, | ||
449 | SNDRV_PCM_STREAM_PLAYBACK); | ||
450 | pr_debug("%s: preallocate playback buffer\n", __func__); | ||
451 | if (ret) | ||
452 | goto out; | ||
453 | } | ||
454 | |||
455 | if (dai->capture.channels_min) { | ||
456 | ret = mx1_mx2_pcm_preallocate_dma_buffer(pcm, | ||
457 | SNDRV_PCM_STREAM_CAPTURE); | ||
458 | pr_debug("%s: preallocate capture buffer\n", __func__); | ||
459 | if (ret) | ||
460 | goto out; | ||
461 | } | ||
462 | out: | ||
463 | return ret; | ||
464 | } | ||
465 | |||
466 | struct snd_soc_platform mx1_mx2_soc_platform = { | ||
467 | .name = "mx1_mx2-audio", | ||
468 | .pcm_ops = &mx1_mx2_pcm_ops, | ||
469 | .pcm_new = mx1_mx2_pcm_new, | ||
470 | .pcm_free = mx1_mx2_pcm_free_dma_buffers, | ||
471 | }; | ||
472 | EXPORT_SYMBOL_GPL(mx1_mx2_soc_platform); | ||
473 | |||
474 | static int __init mx1_mx2_soc_platform_init(void) | ||
475 | { | ||
476 | return snd_soc_register_platform(&mx1_mx2_soc_platform); | ||
477 | } | ||
478 | module_init(mx1_mx2_soc_platform_init); | ||
479 | |||
480 | static void __exit mx1_mx2_soc_platform_exit(void) | ||
481 | { | ||
482 | snd_soc_unregister_platform(&mx1_mx2_soc_platform); | ||
483 | } | ||
484 | module_exit(mx1_mx2_soc_platform_exit); | ||
485 | |||
486 | MODULE_AUTHOR("Javier Martin, javier.martin@vista-silicon.com"); | ||
487 | MODULE_DESCRIPTION("Freescale i.MX2x, i.MX1x PCM DMA module"); | ||
488 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/imx/mx1_mx2-pcm.h b/sound/soc/imx/mx1_mx2-pcm.h deleted file mode 100644 index 2e528106570b..000000000000 --- a/sound/soc/imx/mx1_mx2-pcm.h +++ /dev/null | |||
@@ -1,26 +0,0 @@ | |||
1 | /* | ||
2 | * mx1_mx2-pcm.h :- ASoC platform header for Freescale i.MX1x, i.MX2x | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef _MX1_MX2_PCM_H | ||
10 | #define _MX1_MX2_PCM_H | ||
11 | |||
12 | /* DMA information for mx1_mx2 platforms */ | ||
13 | struct mx1_mx2_pcm_dma_params { | ||
14 | char *name; /* stream identifier */ | ||
15 | unsigned int transfer_type; /* READ or WRITE DMA transfer */ | ||
16 | dma_addr_t per_address; /* physical address of SSI fifo */ | ||
17 | int event_id; /* fixed DMA number for SSI fifo */ | ||
18 | int watermark_level; /* SSI fifo watermark level */ | ||
19 | int per_config; /* DMA Config flags for peripheral */ | ||
20 | int mem_config; /* DMA Config flags for RAM */ | ||
21 | }; | ||
22 | |||
23 | /* platform data */ | ||
24 | extern struct snd_soc_platform mx1_mx2_soc_platform; | ||
25 | |||
26 | #endif | ||
diff --git a/sound/soc/imx/mx27vis_wm8974.c b/sound/soc/imx/mx27vis_wm8974.c deleted file mode 100644 index 07d2a248438c..000000000000 --- a/sound/soc/imx/mx27vis_wm8974.c +++ /dev/null | |||
@@ -1,318 +0,0 @@ | |||
1 | /* | ||
2 | * mx27vis_wm8974.c -- SoC audio for mx27vis | ||
3 | * | ||
4 | * Copyright 2009 Vista Silicon S.L. | ||
5 | * Author: Javier Martin | ||
6 | * javier.martin@vista-silicon.com | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/moduleparam.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/i2c.h> | ||
19 | #include <sound/core.h> | ||
20 | #include <sound/pcm.h> | ||
21 | #include <sound/soc.h> | ||
22 | #include <sound/soc-dapm.h> | ||
23 | |||
24 | |||
25 | #include "../codecs/wm8974.h" | ||
26 | #include "mx1_mx2-pcm.h" | ||
27 | #include "mxc-ssi.h" | ||
28 | #include <mach/gpio.h> | ||
29 | #include <mach/iomux.h> | ||
30 | |||
31 | #define IGNORED_ARG 0 | ||
32 | |||
33 | |||
34 | static struct snd_soc_card mx27vis; | ||
35 | |||
36 | /** | ||
37 | * This function connects SSI1 (HPCR1) as slave to | ||
38 | * SSI1 external signals (PPCR1) | ||
39 | * As slave, HPCR1 must set TFSDIR and TCLKDIR as inputs from | ||
40 | * port 4 | ||
41 | */ | ||
42 | void audmux_connect_1_4(void) | ||
43 | { | ||
44 | pr_debug("AUDMUX: normal operation mode\n"); | ||
45 | /* Reset HPCR1 and PPCR1 */ | ||
46 | |||
47 | DAM_HPCR1 = 0x00000000; | ||
48 | DAM_PPCR1 = 0x00000000; | ||
49 | |||
50 | /* set to synchronous */ | ||
51 | DAM_HPCR1 |= AUDMUX_HPCR_SYN; | ||
52 | DAM_PPCR1 |= AUDMUX_PPCR_SYN; | ||
53 | |||
54 | |||
55 | /* set Rx sources 1 <--> 4 */ | ||
56 | DAM_HPCR1 |= AUDMUX_HPCR_RXDSEL(3); /* port 4 */ | ||
57 | DAM_PPCR1 |= AUDMUX_PPCR_RXDSEL(0); /* port 1 */ | ||
58 | |||
59 | /* set Tx frame and Clock direction and source 4 --> 1 output */ | ||
60 | DAM_HPCR1 |= AUDMUX_HPCR_TFSDIR | AUDMUX_HPCR_TCLKDIR; | ||
61 | DAM_HPCR1 |= AUDMUX_HPCR_TFCSEL(3); /* TxDS and TxCclk from port 4 */ | ||
62 | |||
63 | return; | ||
64 | } | ||
65 | |||
66 | static int mx27vis_hifi_hw_params(struct snd_pcm_substream *substream, | ||
67 | struct snd_pcm_hw_params *params) | ||
68 | { | ||
69 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
70 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
71 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
72 | unsigned int pll_out = 0, bclk = 0, fmt = 0, mclk = 0; | ||
73 | int ret = 0; | ||
74 | |||
75 | /* | ||
76 | * The WM8974 is better at generating accurate audio clocks than the | ||
77 | * MX27 SSI controller, so we will use it as master when we can. | ||
78 | */ | ||
79 | switch (params_rate(params)) { | ||
80 | case 8000: | ||
81 | fmt = SND_SOC_DAIFMT_CBM_CFM; | ||
82 | mclk = WM8974_MCLKDIV_12; | ||
83 | pll_out = 24576000; | ||
84 | break; | ||
85 | case 16000: | ||
86 | fmt = SND_SOC_DAIFMT_CBM_CFM; | ||
87 | pll_out = 12288000; | ||
88 | break; | ||
89 | case 48000: | ||
90 | fmt = SND_SOC_DAIFMT_CBM_CFM; | ||
91 | bclk = WM8974_BCLKDIV_4; | ||
92 | pll_out = 12288000; | ||
93 | break; | ||
94 | case 96000: | ||
95 | fmt = SND_SOC_DAIFMT_CBM_CFM; | ||
96 | bclk = WM8974_BCLKDIV_2; | ||
97 | pll_out = 12288000; | ||
98 | break; | ||
99 | case 11025: | ||
100 | fmt = SND_SOC_DAIFMT_CBM_CFM; | ||
101 | bclk = WM8974_BCLKDIV_16; | ||
102 | pll_out = 11289600; | ||
103 | break; | ||
104 | case 22050: | ||
105 | fmt = SND_SOC_DAIFMT_CBM_CFM; | ||
106 | bclk = WM8974_BCLKDIV_8; | ||
107 | pll_out = 11289600; | ||
108 | break; | ||
109 | case 44100: | ||
110 | fmt = SND_SOC_DAIFMT_CBM_CFM; | ||
111 | bclk = WM8974_BCLKDIV_4; | ||
112 | mclk = WM8974_MCLKDIV_2; | ||
113 | pll_out = 11289600; | ||
114 | break; | ||
115 | case 88200: | ||
116 | fmt = SND_SOC_DAIFMT_CBM_CFM; | ||
117 | bclk = WM8974_BCLKDIV_2; | ||
118 | pll_out = 11289600; | ||
119 | break; | ||
120 | } | ||
121 | |||
122 | /* set codec DAI configuration */ | ||
123 | ret = codec_dai->ops->set_fmt(codec_dai, | ||
124 | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF | | ||
125 | SND_SOC_DAIFMT_SYNC | fmt); | ||
126 | if (ret < 0) { | ||
127 | printk(KERN_ERR "Error from codec DAI configuration\n"); | ||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | /* set cpu DAI configuration */ | ||
132 | ret = cpu_dai->ops->set_fmt(cpu_dai, | ||
133 | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | ||
134 | SND_SOC_DAIFMT_SYNC | fmt); | ||
135 | if (ret < 0) { | ||
136 | printk(KERN_ERR "Error from cpu DAI configuration\n"); | ||
137 | return ret; | ||
138 | } | ||
139 | |||
140 | /* Put DC field of STCCR to 1 (not zero) */ | ||
141 | ret = cpu_dai->ops->set_tdm_slot(cpu_dai, 0, 2); | ||
142 | |||
143 | /* set the SSI system clock as input */ | ||
144 | ret = cpu_dai->ops->set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, | ||
145 | SND_SOC_CLOCK_IN); | ||
146 | if (ret < 0) { | ||
147 | printk(KERN_ERR "Error when setting system SSI clk\n"); | ||
148 | return ret; | ||
149 | } | ||
150 | |||
151 | /* set codec BCLK division for sample rate */ | ||
152 | ret = codec_dai->ops->set_clkdiv(codec_dai, WM8974_BCLKDIV, bclk); | ||
153 | if (ret < 0) { | ||
154 | printk(KERN_ERR "Error when setting BCLK division\n"); | ||
155 | return ret; | ||
156 | } | ||
157 | |||
158 | |||
159 | /* codec PLL input is 25 MHz */ | ||
160 | ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, IGNORED_ARG, | ||
161 | 25000000, pll_out); | ||
162 | if (ret < 0) { | ||
163 | printk(KERN_ERR "Error when setting PLL input\n"); | ||
164 | return ret; | ||
165 | } | ||
166 | |||
167 | /*set codec MCLK division for sample rate */ | ||
168 | ret = codec_dai->ops->set_clkdiv(codec_dai, WM8974_MCLKDIV, mclk); | ||
169 | if (ret < 0) { | ||
170 | printk(KERN_ERR "Error when setting MCLK division\n"); | ||
171 | return ret; | ||
172 | } | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static int mx27vis_hifi_hw_free(struct snd_pcm_substream *substream) | ||
178 | { | ||
179 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
180 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
181 | |||
182 | /* disable the PLL */ | ||
183 | return codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, IGNORED_ARG, | ||
184 | 0, 0); | ||
185 | } | ||
186 | |||
187 | /* | ||
188 | * mx27vis WM8974 HiFi DAI opserations. | ||
189 | */ | ||
190 | static struct snd_soc_ops mx27vis_hifi_ops = { | ||
191 | .hw_params = mx27vis_hifi_hw_params, | ||
192 | .hw_free = mx27vis_hifi_hw_free, | ||
193 | }; | ||
194 | |||
195 | |||
196 | static int mx27vis_suspend(struct platform_device *pdev, pm_message_t state) | ||
197 | { | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static int mx27vis_resume(struct platform_device *pdev) | ||
202 | { | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static int mx27vis_probe(struct platform_device *pdev) | ||
207 | { | ||
208 | int ret = 0; | ||
209 | |||
210 | ret = get_ssi_clk(0, &pdev->dev); | ||
211 | |||
212 | if (ret < 0) { | ||
213 | printk(KERN_ERR "%s: cant get ssi clock\n", __func__); | ||
214 | return ret; | ||
215 | } | ||
216 | |||
217 | |||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | static int mx27vis_remove(struct platform_device *pdev) | ||
222 | { | ||
223 | put_ssi_clk(0); | ||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | static struct snd_soc_dai_link mx27vis_dai[] = { | ||
228 | { /* Hifi Playback*/ | ||
229 | .name = "WM8974", | ||
230 | .stream_name = "WM8974 HiFi", | ||
231 | .cpu_dai = &imx_ssi_pcm_dai[0], | ||
232 | .codec_dai = &wm8974_dai, | ||
233 | .ops = &mx27vis_hifi_ops, | ||
234 | }, | ||
235 | }; | ||
236 | |||
237 | static struct snd_soc_card mx27vis = { | ||
238 | .name = "mx27vis", | ||
239 | .platform = &mx1_mx2_soc_platform, | ||
240 | .probe = mx27vis_probe, | ||
241 | .remove = mx27vis_remove, | ||
242 | .suspend_pre = mx27vis_suspend, | ||
243 | .resume_post = mx27vis_resume, | ||
244 | .dai_link = mx27vis_dai, | ||
245 | .num_links = ARRAY_SIZE(mx27vis_dai), | ||
246 | }; | ||
247 | |||
248 | static struct snd_soc_device mx27vis_snd_devdata = { | ||
249 | .card = &mx27vis, | ||
250 | .codec_dev = &soc_codec_dev_wm8974, | ||
251 | }; | ||
252 | |||
253 | static struct platform_device *mx27vis_snd_device; | ||
254 | |||
255 | /* Temporal definition of board specific behaviour */ | ||
256 | void gpio_ssi_active(int ssi_num) | ||
257 | { | ||
258 | int ret = 0; | ||
259 | |||
260 | unsigned int ssi1_pins[] = { | ||
261 | PC20_PF_SSI1_FS, | ||
262 | PC21_PF_SSI1_RXD, | ||
263 | PC22_PF_SSI1_TXD, | ||
264 | PC23_PF_SSI1_CLK, | ||
265 | }; | ||
266 | unsigned int ssi2_pins[] = { | ||
267 | PC24_PF_SSI2_FS, | ||
268 | PC25_PF_SSI2_RXD, | ||
269 | PC26_PF_SSI2_TXD, | ||
270 | PC27_PF_SSI2_CLK, | ||
271 | }; | ||
272 | if (ssi_num == 0) | ||
273 | ret = mxc_gpio_setup_multiple_pins(ssi1_pins, | ||
274 | ARRAY_SIZE(ssi1_pins), "USB OTG"); | ||
275 | else | ||
276 | ret = mxc_gpio_setup_multiple_pins(ssi2_pins, | ||
277 | ARRAY_SIZE(ssi2_pins), "USB OTG"); | ||
278 | if (ret) | ||
279 | printk(KERN_ERR "Error requesting ssi %x pins\n", ssi_num); | ||
280 | } | ||
281 | |||
282 | |||
283 | static int __init mx27vis_init(void) | ||
284 | { | ||
285 | int ret; | ||
286 | |||
287 | mx27vis_snd_device = platform_device_alloc("soc-audio", -1); | ||
288 | if (!mx27vis_snd_device) | ||
289 | return -ENOMEM; | ||
290 | |||
291 | platform_set_drvdata(mx27vis_snd_device, &mx27vis_snd_devdata); | ||
292 | mx27vis_snd_devdata.dev = &mx27vis_snd_device->dev; | ||
293 | ret = platform_device_add(mx27vis_snd_device); | ||
294 | |||
295 | if (ret) { | ||
296 | printk(KERN_ERR "ASoC: Platform device allocation failed\n"); | ||
297 | platform_device_put(mx27vis_snd_device); | ||
298 | } | ||
299 | |||
300 | /* WM8974 uses SSI1 (HPCR1) via AUDMUX port 4 for audio (PPCR1) */ | ||
301 | gpio_ssi_active(0); | ||
302 | audmux_connect_1_4(); | ||
303 | |||
304 | return ret; | ||
305 | } | ||
306 | |||
307 | static void __exit mx27vis_exit(void) | ||
308 | { | ||
309 | /* We should call some "ssi_gpio_inactive()" properly */ | ||
310 | } | ||
311 | |||
312 | module_init(mx27vis_init); | ||
313 | module_exit(mx27vis_exit); | ||
314 | |||
315 | |||
316 | MODULE_AUTHOR("Javier Martin, javier.martin@vista-silicon.com"); | ||
317 | MODULE_DESCRIPTION("ALSA SoC WM8974 mx27vis"); | ||
318 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/imx/mxc-ssi.c b/sound/soc/imx/mxc-ssi.c deleted file mode 100644 index ccdefe60e752..000000000000 --- a/sound/soc/imx/mxc-ssi.c +++ /dev/null | |||
@@ -1,860 +0,0 @@ | |||
1 | /* | ||
2 | * mxc-ssi.c -- SSI driver for Freescale IMX | ||
3 | * | ||
4 | * Copyright 2006 Wolfson Microelectronics PLC. | ||
5 | * Author: Liam Girdwood | ||
6 | * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com | ||
7 | * | ||
8 | * Based on mxc-alsa-mc13783 (C) 2006 Freescale. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | * | ||
15 | * TODO: | ||
16 | * Need to rework SSI register defs when new defs go into mainline. | ||
17 | * Add support for TDM and FIFO 1. | ||
18 | * Add support for i.mx3x DMA interface. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | |||
23 | #include <linux/module.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/platform_device.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/dma-mapping.h> | ||
28 | #include <linux/clk.h> | ||
29 | #include <sound/core.h> | ||
30 | #include <sound/pcm.h> | ||
31 | #include <sound/pcm_params.h> | ||
32 | #include <sound/soc.h> | ||
33 | #include <mach/dma-mx1-mx2.h> | ||
34 | #include <asm/mach-types.h> | ||
35 | |||
36 | #include "mxc-ssi.h" | ||
37 | #include "mx1_mx2-pcm.h" | ||
38 | |||
39 | #define SSI1_PORT 0 | ||
40 | #define SSI2_PORT 1 | ||
41 | |||
42 | static int ssi_active[2] = {0, 0}; | ||
43 | |||
44 | /* DMA information for mx1_mx2 platforms */ | ||
45 | static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_out0 = { | ||
46 | .name = "SSI1 PCM Stereo out 0", | ||
47 | .transfer_type = DMA_MODE_WRITE, | ||
48 | .per_address = SSI1_BASE_ADDR + STX0, | ||
49 | .event_id = DMA_REQ_SSI1_TX0, | ||
50 | .watermark_level = TXFIFO_WATERMARK, | ||
51 | .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, | ||
52 | .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | ||
53 | }; | ||
54 | |||
55 | static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_out1 = { | ||
56 | .name = "SSI1 PCM Stereo out 1", | ||
57 | .transfer_type = DMA_MODE_WRITE, | ||
58 | .per_address = SSI1_BASE_ADDR + STX1, | ||
59 | .event_id = DMA_REQ_SSI1_TX1, | ||
60 | .watermark_level = TXFIFO_WATERMARK, | ||
61 | .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, | ||
62 | .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | ||
63 | }; | ||
64 | |||
65 | static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_in0 = { | ||
66 | .name = "SSI1 PCM Stereo in 0", | ||
67 | .transfer_type = DMA_MODE_READ, | ||
68 | .per_address = SSI1_BASE_ADDR + SRX0, | ||
69 | .event_id = DMA_REQ_SSI1_RX0, | ||
70 | .watermark_level = RXFIFO_WATERMARK, | ||
71 | .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, | ||
72 | .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | ||
73 | }; | ||
74 | |||
75 | static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_in1 = { | ||
76 | .name = "SSI1 PCM Stereo in 1", | ||
77 | .transfer_type = DMA_MODE_READ, | ||
78 | .per_address = SSI1_BASE_ADDR + SRX1, | ||
79 | .event_id = DMA_REQ_SSI1_RX1, | ||
80 | .watermark_level = RXFIFO_WATERMARK, | ||
81 | .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, | ||
82 | .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | ||
83 | }; | ||
84 | |||
85 | static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_out0 = { | ||
86 | .name = "SSI2 PCM Stereo out 0", | ||
87 | .transfer_type = DMA_MODE_WRITE, | ||
88 | .per_address = SSI2_BASE_ADDR + STX0, | ||
89 | .event_id = DMA_REQ_SSI2_TX0, | ||
90 | .watermark_level = TXFIFO_WATERMARK, | ||
91 | .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, | ||
92 | .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | ||
93 | }; | ||
94 | |||
95 | static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_out1 = { | ||
96 | .name = "SSI2 PCM Stereo out 1", | ||
97 | .transfer_type = DMA_MODE_WRITE, | ||
98 | .per_address = SSI2_BASE_ADDR + STX1, | ||
99 | .event_id = DMA_REQ_SSI2_TX1, | ||
100 | .watermark_level = TXFIFO_WATERMARK, | ||
101 | .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, | ||
102 | .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | ||
103 | }; | ||
104 | |||
105 | static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_in0 = { | ||
106 | .name = "SSI2 PCM Stereo in 0", | ||
107 | .transfer_type = DMA_MODE_READ, | ||
108 | .per_address = SSI2_BASE_ADDR + SRX0, | ||
109 | .event_id = DMA_REQ_SSI2_RX0, | ||
110 | .watermark_level = RXFIFO_WATERMARK, | ||
111 | .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, | ||
112 | .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | ||
113 | }; | ||
114 | |||
115 | static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_in1 = { | ||
116 | .name = "SSI2 PCM Stereo in 1", | ||
117 | .transfer_type = DMA_MODE_READ, | ||
118 | .per_address = SSI2_BASE_ADDR + SRX1, | ||
119 | .event_id = DMA_REQ_SSI2_RX1, | ||
120 | .watermark_level = RXFIFO_WATERMARK, | ||
121 | .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, | ||
122 | .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | ||
123 | }; | ||
124 | |||
125 | static struct clk *ssi_clk0, *ssi_clk1; | ||
126 | |||
127 | int get_ssi_clk(int ssi, struct device *dev) | ||
128 | { | ||
129 | switch (ssi) { | ||
130 | case 0: | ||
131 | ssi_clk0 = clk_get(dev, "ssi1"); | ||
132 | if (IS_ERR(ssi_clk0)) | ||
133 | return PTR_ERR(ssi_clk0); | ||
134 | return 0; | ||
135 | case 1: | ||
136 | ssi_clk1 = clk_get(dev, "ssi2"); | ||
137 | if (IS_ERR(ssi_clk1)) | ||
138 | return PTR_ERR(ssi_clk1); | ||
139 | return 0; | ||
140 | default: | ||
141 | return -EINVAL; | ||
142 | } | ||
143 | } | ||
144 | EXPORT_SYMBOL(get_ssi_clk); | ||
145 | |||
146 | void put_ssi_clk(int ssi) | ||
147 | { | ||
148 | switch (ssi) { | ||
149 | case 0: | ||
150 | clk_put(ssi_clk0); | ||
151 | ssi_clk0 = NULL; | ||
152 | break; | ||
153 | case 1: | ||
154 | clk_put(ssi_clk1); | ||
155 | ssi_clk1 = NULL; | ||
156 | break; | ||
157 | } | ||
158 | } | ||
159 | EXPORT_SYMBOL(put_ssi_clk); | ||
160 | |||
161 | /* | ||
162 | * SSI system clock configuration. | ||
163 | * Should only be called when port is inactive (i.e. SSIEN = 0). | ||
164 | */ | ||
165 | static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai, | ||
166 | int clk_id, unsigned int freq, int dir) | ||
167 | { | ||
168 | u32 scr; | ||
169 | |||
170 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
171 | scr = SSI1_SCR; | ||
172 | pr_debug("%s: SCR for SSI1 is %x\n", __func__, scr); | ||
173 | } else { | ||
174 | scr = SSI2_SCR; | ||
175 | pr_debug("%s: SCR for SSI2 is %x\n", __func__, scr); | ||
176 | } | ||
177 | |||
178 | if (scr & SSI_SCR_SSIEN) { | ||
179 | printk(KERN_WARNING "Warning ssi already enabled\n"); | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | switch (clk_id) { | ||
184 | case IMX_SSP_SYS_CLK: | ||
185 | if (dir == SND_SOC_CLOCK_OUT) { | ||
186 | scr |= SSI_SCR_SYS_CLK_EN; | ||
187 | pr_debug("%s: clk of is output\n", __func__); | ||
188 | } else { | ||
189 | scr &= ~SSI_SCR_SYS_CLK_EN; | ||
190 | pr_debug("%s: clk of is input\n", __func__); | ||
191 | } | ||
192 | break; | ||
193 | default: | ||
194 | return -EINVAL; | ||
195 | } | ||
196 | |||
197 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
198 | pr_debug("%s: writeback of SSI1_SCR\n", __func__); | ||
199 | SSI1_SCR = scr; | ||
200 | } else { | ||
201 | pr_debug("%s: writeback of SSI2_SCR\n", __func__); | ||
202 | SSI2_SCR = scr; | ||
203 | } | ||
204 | |||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | * SSI Clock dividers | ||
210 | * Should only be called when port is inactive (i.e. SSIEN = 0). | ||
211 | */ | ||
212 | static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, | ||
213 | int div_id, int div) | ||
214 | { | ||
215 | u32 stccr, srccr; | ||
216 | |||
217 | pr_debug("%s\n", __func__); | ||
218 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
219 | if (SSI1_SCR & SSI_SCR_SSIEN) | ||
220 | return 0; | ||
221 | srccr = SSI1_STCCR; | ||
222 | stccr = SSI1_STCCR; | ||
223 | } else { | ||
224 | if (SSI2_SCR & SSI_SCR_SSIEN) | ||
225 | return 0; | ||
226 | srccr = SSI2_STCCR; | ||
227 | stccr = SSI2_STCCR; | ||
228 | } | ||
229 | |||
230 | switch (div_id) { | ||
231 | case IMX_SSI_TX_DIV_2: | ||
232 | stccr &= ~SSI_STCCR_DIV2; | ||
233 | stccr |= div; | ||
234 | break; | ||
235 | case IMX_SSI_TX_DIV_PSR: | ||
236 | stccr &= ~SSI_STCCR_PSR; | ||
237 | stccr |= div; | ||
238 | break; | ||
239 | case IMX_SSI_TX_DIV_PM: | ||
240 | stccr &= ~0xff; | ||
241 | stccr |= SSI_STCCR_PM(div); | ||
242 | break; | ||
243 | case IMX_SSI_RX_DIV_2: | ||
244 | stccr &= ~SSI_STCCR_DIV2; | ||
245 | stccr |= div; | ||
246 | break; | ||
247 | case IMX_SSI_RX_DIV_PSR: | ||
248 | stccr &= ~SSI_STCCR_PSR; | ||
249 | stccr |= div; | ||
250 | break; | ||
251 | case IMX_SSI_RX_DIV_PM: | ||
252 | stccr &= ~0xff; | ||
253 | stccr |= SSI_STCCR_PM(div); | ||
254 | break; | ||
255 | default: | ||
256 | return -EINVAL; | ||
257 | } | ||
258 | |||
259 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
260 | SSI1_STCCR = stccr; | ||
261 | SSI1_SRCCR = srccr; | ||
262 | } else { | ||
263 | SSI2_STCCR = stccr; | ||
264 | SSI2_SRCCR = srccr; | ||
265 | } | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | /* | ||
270 | * SSI Network Mode or TDM slots configuration. | ||
271 | * Should only be called when port is inactive (i.e. SSIEN = 0). | ||
272 | */ | ||
273 | static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, | ||
274 | unsigned int mask, int slots) | ||
275 | { | ||
276 | u32 stmsk, srmsk, stccr; | ||
277 | |||
278 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
279 | if (SSI1_SCR & SSI_SCR_SSIEN) { | ||
280 | printk(KERN_WARNING "Warning ssi already enabled\n"); | ||
281 | return 0; | ||
282 | } | ||
283 | stccr = SSI1_STCCR; | ||
284 | } else { | ||
285 | if (SSI2_SCR & SSI_SCR_SSIEN) { | ||
286 | printk(KERN_WARNING "Warning ssi already enabled\n"); | ||
287 | return 0; | ||
288 | } | ||
289 | stccr = SSI2_STCCR; | ||
290 | } | ||
291 | |||
292 | stmsk = srmsk = mask; | ||
293 | stccr &= ~SSI_STCCR_DC_MASK; | ||
294 | stccr |= SSI_STCCR_DC(slots - 1); | ||
295 | |||
296 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
297 | SSI1_STMSK = stmsk; | ||
298 | SSI1_SRMSK = srmsk; | ||
299 | SSI1_SRCCR = SSI1_STCCR = stccr; | ||
300 | } else { | ||
301 | SSI2_STMSK = stmsk; | ||
302 | SSI2_SRMSK = srmsk; | ||
303 | SSI2_SRCCR = SSI2_STCCR = stccr; | ||
304 | } | ||
305 | |||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | /* | ||
310 | * SSI DAI format configuration. | ||
311 | * Should only be called when port is inactive (i.e. SSIEN = 0). | ||
312 | * Note: We don't use the I2S modes but instead manually configure the | ||
313 | * SSI for I2S. | ||
314 | */ | ||
315 | static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, | ||
316 | unsigned int fmt) | ||
317 | { | ||
318 | u32 stcr = 0, srcr = 0, scr; | ||
319 | |||
320 | /* | ||
321 | * This is done to avoid this function to modify | ||
322 | * previous set values in stcr | ||
323 | */ | ||
324 | stcr = SSI1_STCR; | ||
325 | |||
326 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) | ||
327 | scr = SSI1_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET); | ||
328 | else | ||
329 | scr = SSI2_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET); | ||
330 | |||
331 | if (scr & SSI_SCR_SSIEN) { | ||
332 | printk(KERN_WARNING "Warning ssi already enabled\n"); | ||
333 | return 0; | ||
334 | } | ||
335 | |||
336 | /* DAI mode */ | ||
337 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
338 | case SND_SOC_DAIFMT_I2S: | ||
339 | /* data on rising edge of bclk, frame low 1clk before data */ | ||
340 | stcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0; | ||
341 | srcr |= SSI_SRCR_RFSI | SSI_SRCR_REFS | SSI_SRCR_RXBIT0; | ||
342 | break; | ||
343 | case SND_SOC_DAIFMT_LEFT_J: | ||
344 | /* data on rising edge of bclk, frame high with data */ | ||
345 | stcr |= SSI_STCR_TXBIT0; | ||
346 | srcr |= SSI_SRCR_RXBIT0; | ||
347 | break; | ||
348 | case SND_SOC_DAIFMT_DSP_B: | ||
349 | /* data on rising edge of bclk, frame high with data */ | ||
350 | stcr |= SSI_STCR_TFSL; | ||
351 | srcr |= SSI_SRCR_RFSL; | ||
352 | break; | ||
353 | case SND_SOC_DAIFMT_DSP_A: | ||
354 | /* data on rising edge of bclk, frame high 1clk before data */ | ||
355 | stcr |= SSI_STCR_TFSL | SSI_STCR_TEFS; | ||
356 | srcr |= SSI_SRCR_RFSL | SSI_SRCR_REFS; | ||
357 | break; | ||
358 | } | ||
359 | |||
360 | /* DAI clock inversion */ | ||
361 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
362 | case SND_SOC_DAIFMT_IB_IF: | ||
363 | stcr |= SSI_STCR_TFSI; | ||
364 | stcr &= ~SSI_STCR_TSCKP; | ||
365 | srcr |= SSI_SRCR_RFSI; | ||
366 | srcr &= ~SSI_SRCR_RSCKP; | ||
367 | break; | ||
368 | case SND_SOC_DAIFMT_IB_NF: | ||
369 | stcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI); | ||
370 | srcr &= ~(SSI_SRCR_RSCKP | SSI_SRCR_RFSI); | ||
371 | break; | ||
372 | case SND_SOC_DAIFMT_NB_IF: | ||
373 | stcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP; | ||
374 | srcr |= SSI_SRCR_RFSI | SSI_SRCR_RSCKP; | ||
375 | break; | ||
376 | case SND_SOC_DAIFMT_NB_NF: | ||
377 | stcr &= ~SSI_STCR_TFSI; | ||
378 | stcr |= SSI_STCR_TSCKP; | ||
379 | srcr &= ~SSI_SRCR_RFSI; | ||
380 | srcr |= SSI_SRCR_RSCKP; | ||
381 | break; | ||
382 | } | ||
383 | |||
384 | /* DAI clock master masks */ | ||
385 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
386 | case SND_SOC_DAIFMT_CBS_CFS: | ||
387 | stcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR; | ||
388 | srcr |= SSI_SRCR_RFDIR | SSI_SRCR_RXDIR; | ||
389 | break; | ||
390 | case SND_SOC_DAIFMT_CBM_CFS: | ||
391 | stcr |= SSI_STCR_TFDIR; | ||
392 | srcr |= SSI_SRCR_RFDIR; | ||
393 | break; | ||
394 | case SND_SOC_DAIFMT_CBS_CFM: | ||
395 | stcr |= SSI_STCR_TXDIR; | ||
396 | srcr |= SSI_SRCR_RXDIR; | ||
397 | break; | ||
398 | } | ||
399 | |||
400 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
401 | SSI1_STCR = stcr; | ||
402 | SSI1_SRCR = srcr; | ||
403 | SSI1_SCR = scr; | ||
404 | } else { | ||
405 | SSI2_STCR = stcr; | ||
406 | SSI2_SRCR = srcr; | ||
407 | SSI2_SCR = scr; | ||
408 | } | ||
409 | |||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | static int imx_ssi_startup(struct snd_pcm_substream *substream, | ||
414 | struct snd_soc_dai *dai) | ||
415 | { | ||
416 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
417 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
418 | |||
419 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
420 | /* set up TX DMA params */ | ||
421 | switch (cpu_dai->id) { | ||
422 | case IMX_DAI_SSI0: | ||
423 | cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out0; | ||
424 | break; | ||
425 | case IMX_DAI_SSI1: | ||
426 | cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out1; | ||
427 | break; | ||
428 | case IMX_DAI_SSI2: | ||
429 | cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out0; | ||
430 | break; | ||
431 | case IMX_DAI_SSI3: | ||
432 | cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out1; | ||
433 | } | ||
434 | pr_debug("%s: (playback)\n", __func__); | ||
435 | } else { | ||
436 | /* set up RX DMA params */ | ||
437 | switch (cpu_dai->id) { | ||
438 | case IMX_DAI_SSI0: | ||
439 | cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in0; | ||
440 | break; | ||
441 | case IMX_DAI_SSI1: | ||
442 | cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in1; | ||
443 | break; | ||
444 | case IMX_DAI_SSI2: | ||
445 | cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in0; | ||
446 | break; | ||
447 | case IMX_DAI_SSI3: | ||
448 | cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in1; | ||
449 | } | ||
450 | pr_debug("%s: (capture)\n", __func__); | ||
451 | } | ||
452 | |||
453 | /* | ||
454 | * we cant really change any SSI values after SSI is enabled | ||
455 | * need to fix in software for max flexibility - lrg | ||
456 | */ | ||
457 | if (cpu_dai->active) { | ||
458 | printk(KERN_WARNING "Warning ssi already enabled\n"); | ||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | /* reset the SSI port - Sect 45.4.4 */ | ||
463 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
464 | |||
465 | if (!ssi_clk0) | ||
466 | return -EINVAL; | ||
467 | |||
468 | if (ssi_active[SSI1_PORT]++) { | ||
469 | pr_debug("%s: exit before reset\n", __func__); | ||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | /* SSI1 Reset */ | ||
474 | SSI1_SCR = 0; | ||
475 | |||
476 | SSI1_SFCSR = SSI_SFCSR_RFWM1(RXFIFO_WATERMARK) | | ||
477 | SSI_SFCSR_RFWM0(RXFIFO_WATERMARK) | | ||
478 | SSI_SFCSR_TFWM1(TXFIFO_WATERMARK) | | ||
479 | SSI_SFCSR_TFWM0(TXFIFO_WATERMARK); | ||
480 | } else { | ||
481 | |||
482 | if (!ssi_clk1) | ||
483 | return -EINVAL; | ||
484 | |||
485 | if (ssi_active[SSI2_PORT]++) { | ||
486 | pr_debug("%s: exit before reset\n", __func__); | ||
487 | return 0; | ||
488 | } | ||
489 | |||
490 | /* SSI2 Reset */ | ||
491 | SSI2_SCR = 0; | ||
492 | |||
493 | SSI2_SFCSR = SSI_SFCSR_RFWM1(RXFIFO_WATERMARK) | | ||
494 | SSI_SFCSR_RFWM0(RXFIFO_WATERMARK) | | ||
495 | SSI_SFCSR_TFWM1(TXFIFO_WATERMARK) | | ||
496 | SSI_SFCSR_TFWM0(TXFIFO_WATERMARK); | ||
497 | } | ||
498 | |||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | int imx_ssi_hw_tx_params(struct snd_pcm_substream *substream, | ||
503 | struct snd_pcm_hw_params *params) | ||
504 | { | ||
505 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
506 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
507 | u32 stccr, stcr, sier; | ||
508 | |||
509 | pr_debug("%s\n", __func__); | ||
510 | |||
511 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
512 | stccr = SSI1_STCCR & ~SSI_STCCR_WL_MASK; | ||
513 | stcr = SSI1_STCR; | ||
514 | sier = SSI1_SIER; | ||
515 | } else { | ||
516 | stccr = SSI2_STCCR & ~SSI_STCCR_WL_MASK; | ||
517 | stcr = SSI2_STCR; | ||
518 | sier = SSI2_SIER; | ||
519 | } | ||
520 | |||
521 | /* DAI data (word) size */ | ||
522 | switch (params_format(params)) { | ||
523 | case SNDRV_PCM_FORMAT_S16_LE: | ||
524 | stccr |= SSI_STCCR_WL(16); | ||
525 | break; | ||
526 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
527 | stccr |= SSI_STCCR_WL(20); | ||
528 | break; | ||
529 | case SNDRV_PCM_FORMAT_S24_LE: | ||
530 | stccr |= SSI_STCCR_WL(24); | ||
531 | break; | ||
532 | } | ||
533 | |||
534 | /* enable interrupts */ | ||
535 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) | ||
536 | stcr |= SSI_STCR_TFEN0; | ||
537 | else | ||
538 | stcr |= SSI_STCR_TFEN1; | ||
539 | sier |= SSI_SIER_TDMAE; | ||
540 | |||
541 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
542 | SSI1_STCR = stcr; | ||
543 | SSI1_STCCR = stccr; | ||
544 | SSI1_SIER = sier; | ||
545 | } else { | ||
546 | SSI2_STCR = stcr; | ||
547 | SSI2_STCCR = stccr; | ||
548 | SSI2_SIER = sier; | ||
549 | } | ||
550 | |||
551 | return 0; | ||
552 | } | ||
553 | |||
554 | int imx_ssi_hw_rx_params(struct snd_pcm_substream *substream, | ||
555 | struct snd_pcm_hw_params *params) | ||
556 | { | ||
557 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
558 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
559 | u32 srccr, srcr, sier; | ||
560 | |||
561 | pr_debug("%s\n", __func__); | ||
562 | |||
563 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
564 | srccr = SSI1_SRCCR & ~SSI_SRCCR_WL_MASK; | ||
565 | srcr = SSI1_SRCR; | ||
566 | sier = SSI1_SIER; | ||
567 | } else { | ||
568 | srccr = SSI2_SRCCR & ~SSI_SRCCR_WL_MASK; | ||
569 | srcr = SSI2_SRCR; | ||
570 | sier = SSI2_SIER; | ||
571 | } | ||
572 | |||
573 | /* DAI data (word) size */ | ||
574 | switch (params_format(params)) { | ||
575 | case SNDRV_PCM_FORMAT_S16_LE: | ||
576 | srccr |= SSI_SRCCR_WL(16); | ||
577 | break; | ||
578 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
579 | srccr |= SSI_SRCCR_WL(20); | ||
580 | break; | ||
581 | case SNDRV_PCM_FORMAT_S24_LE: | ||
582 | srccr |= SSI_SRCCR_WL(24); | ||
583 | break; | ||
584 | } | ||
585 | |||
586 | /* enable interrupts */ | ||
587 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) | ||
588 | srcr |= SSI_SRCR_RFEN0; | ||
589 | else | ||
590 | srcr |= SSI_SRCR_RFEN1; | ||
591 | sier |= SSI_SIER_RDMAE; | ||
592 | |||
593 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
594 | SSI1_SRCR = srcr; | ||
595 | SSI1_SRCCR = srccr; | ||
596 | SSI1_SIER = sier; | ||
597 | } else { | ||
598 | SSI2_SRCR = srcr; | ||
599 | SSI2_SRCCR = srccr; | ||
600 | SSI2_SIER = sier; | ||
601 | } | ||
602 | |||
603 | return 0; | ||
604 | } | ||
605 | |||
606 | /* | ||
607 | * Should only be called when port is inactive (i.e. SSIEN = 0), | ||
608 | * although can be called multiple times by upper layers. | ||
609 | */ | ||
610 | int imx_ssi_hw_params(struct snd_pcm_substream *substream, | ||
611 | struct snd_pcm_hw_params *params, | ||
612 | struct snd_soc_dai *dai) | ||
613 | { | ||
614 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
615 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
616 | |||
617 | int ret; | ||
618 | |||
619 | /* cant change any parameters when SSI is running */ | ||
620 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
621 | if (SSI1_SCR & SSI_SCR_SSIEN) { | ||
622 | printk(KERN_WARNING "Warning ssi already enabled\n"); | ||
623 | return 0; | ||
624 | } | ||
625 | } else { | ||
626 | if (SSI2_SCR & SSI_SCR_SSIEN) { | ||
627 | printk(KERN_WARNING "Warning ssi already enabled\n"); | ||
628 | return 0; | ||
629 | } | ||
630 | } | ||
631 | |||
632 | /* | ||
633 | * Configure both tx and rx params with the same settings. This is | ||
634 | * really a harware restriction because SSI must be disabled until | ||
635 | * we can change those values. If there is an active audio stream in | ||
636 | * one direction, enabling the other direction with different | ||
637 | * settings would mean disturbing the running one. | ||
638 | */ | ||
639 | ret = imx_ssi_hw_tx_params(substream, params); | ||
640 | if (ret < 0) | ||
641 | return ret; | ||
642 | return imx_ssi_hw_rx_params(substream, params); | ||
643 | } | ||
644 | |||
645 | int imx_ssi_prepare(struct snd_pcm_substream *substream, | ||
646 | struct snd_soc_dai *dai) | ||
647 | { | ||
648 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
649 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
650 | int ret; | ||
651 | |||
652 | pr_debug("%s\n", __func__); | ||
653 | |||
654 | /* Enable clks here to follow SSI recommended init sequence */ | ||
655 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
656 | ret = clk_enable(ssi_clk0); | ||
657 | if (ret < 0) | ||
658 | printk(KERN_ERR "Unable to enable ssi_clk0\n"); | ||
659 | } else { | ||
660 | ret = clk_enable(ssi_clk1); | ||
661 | if (ret < 0) | ||
662 | printk(KERN_ERR "Unable to enable ssi_clk1\n"); | ||
663 | } | ||
664 | |||
665 | return 0; | ||
666 | } | ||
667 | |||
668 | static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd, | ||
669 | struct snd_soc_dai *dai) | ||
670 | { | ||
671 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
672 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
673 | u32 scr; | ||
674 | |||
675 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) | ||
676 | scr = SSI1_SCR; | ||
677 | else | ||
678 | scr = SSI2_SCR; | ||
679 | |||
680 | switch (cmd) { | ||
681 | case SNDRV_PCM_TRIGGER_START: | ||
682 | case SNDRV_PCM_TRIGGER_RESUME: | ||
683 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
684 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
685 | scr |= SSI_SCR_TE | SSI_SCR_SSIEN; | ||
686 | else | ||
687 | scr |= SSI_SCR_RE | SSI_SCR_SSIEN; | ||
688 | break; | ||
689 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
690 | case SNDRV_PCM_TRIGGER_STOP: | ||
691 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
692 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
693 | scr &= ~SSI_SCR_TE; | ||
694 | else | ||
695 | scr &= ~SSI_SCR_RE; | ||
696 | break; | ||
697 | default: | ||
698 | return -EINVAL; | ||
699 | } | ||
700 | |||
701 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) | ||
702 | SSI1_SCR = scr; | ||
703 | else | ||
704 | SSI2_SCR = scr; | ||
705 | |||
706 | return 0; | ||
707 | } | ||
708 | |||
709 | static void imx_ssi_shutdown(struct snd_pcm_substream *substream, | ||
710 | struct snd_soc_dai *dai) | ||
711 | { | ||
712 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
713 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
714 | |||
715 | /* shutdown SSI if neither Tx or Rx is active */ | ||
716 | if (!cpu_dai->active) { | ||
717 | |||
718 | if (cpu_dai->id == IMX_DAI_SSI0 || | ||
719 | cpu_dai->id == IMX_DAI_SSI2) { | ||
720 | |||
721 | if (--ssi_active[SSI1_PORT] > 1) | ||
722 | return; | ||
723 | |||
724 | SSI1_SCR = 0; | ||
725 | clk_disable(ssi_clk0); | ||
726 | } else { | ||
727 | if (--ssi_active[SSI2_PORT]) | ||
728 | return; | ||
729 | SSI2_SCR = 0; | ||
730 | clk_disable(ssi_clk1); | ||
731 | } | ||
732 | } | ||
733 | } | ||
734 | |||
735 | #ifdef CONFIG_PM | ||
736 | static int imx_ssi_suspend(struct platform_device *dev, | ||
737 | struct snd_soc_dai *dai) | ||
738 | { | ||
739 | return 0; | ||
740 | } | ||
741 | |||
742 | static int imx_ssi_resume(struct platform_device *pdev, | ||
743 | struct snd_soc_dai *dai) | ||
744 | { | ||
745 | return 0; | ||
746 | } | ||
747 | |||
748 | #else | ||
749 | #define imx_ssi_suspend NULL | ||
750 | #define imx_ssi_resume NULL | ||
751 | #endif | ||
752 | |||
753 | #define IMX_SSI_RATES \ | ||
754 | (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \ | ||
755 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ | ||
756 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ | ||
757 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \ | ||
758 | SNDRV_PCM_RATE_96000) | ||
759 | |||
760 | #define IMX_SSI_BITS \ | ||
761 | (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ | ||
762 | SNDRV_PCM_FMTBIT_S24_LE) | ||
763 | |||
764 | static struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = { | ||
765 | .startup = imx_ssi_startup, | ||
766 | .shutdown = imx_ssi_shutdown, | ||
767 | .trigger = imx_ssi_trigger, | ||
768 | .prepare = imx_ssi_prepare, | ||
769 | .hw_params = imx_ssi_hw_params, | ||
770 | .set_sysclk = imx_ssi_set_dai_sysclk, | ||
771 | .set_clkdiv = imx_ssi_set_dai_clkdiv, | ||
772 | .set_fmt = imx_ssi_set_dai_fmt, | ||
773 | .set_tdm_slot = imx_ssi_set_dai_tdm_slot, | ||
774 | }; | ||
775 | |||
776 | struct snd_soc_dai imx_ssi_pcm_dai[] = { | ||
777 | { | ||
778 | .name = "imx-i2s-1-0", | ||
779 | .id = IMX_DAI_SSI0, | ||
780 | .suspend = imx_ssi_suspend, | ||
781 | .resume = imx_ssi_resume, | ||
782 | .playback = { | ||
783 | .channels_min = 1, | ||
784 | .channels_max = 2, | ||
785 | .formats = IMX_SSI_BITS, | ||
786 | .rates = IMX_SSI_RATES,}, | ||
787 | .capture = { | ||
788 | .channels_min = 1, | ||
789 | .channels_max = 2, | ||
790 | .formats = IMX_SSI_BITS, | ||
791 | .rates = IMX_SSI_RATES,}, | ||
792 | .ops = &imx_ssi_pcm_dai_ops, | ||
793 | }, | ||
794 | { | ||
795 | .name = "imx-i2s-2-0", | ||
796 | .id = IMX_DAI_SSI1, | ||
797 | .playback = { | ||
798 | .channels_min = 1, | ||
799 | .channels_max = 2, | ||
800 | .formats = IMX_SSI_BITS, | ||
801 | .rates = IMX_SSI_RATES,}, | ||
802 | .capture = { | ||
803 | .channels_min = 1, | ||
804 | .channels_max = 2, | ||
805 | .formats = IMX_SSI_BITS, | ||
806 | .rates = IMX_SSI_RATES,}, | ||
807 | .ops = &imx_ssi_pcm_dai_ops, | ||
808 | }, | ||
809 | { | ||
810 | .name = "imx-i2s-1-1", | ||
811 | .id = IMX_DAI_SSI2, | ||
812 | .suspend = imx_ssi_suspend, | ||
813 | .resume = imx_ssi_resume, | ||
814 | .playback = { | ||
815 | .channels_min = 1, | ||
816 | .channels_max = 2, | ||
817 | .formats = IMX_SSI_BITS, | ||
818 | .rates = IMX_SSI_RATES,}, | ||
819 | .capture = { | ||
820 | .channels_min = 1, | ||
821 | .channels_max = 2, | ||
822 | .formats = IMX_SSI_BITS, | ||
823 | .rates = IMX_SSI_RATES,}, | ||
824 | .ops = &imx_ssi_pcm_dai_ops, | ||
825 | }, | ||
826 | { | ||
827 | .name = "imx-i2s-2-1", | ||
828 | .id = IMX_DAI_SSI3, | ||
829 | .playback = { | ||
830 | .channels_min = 1, | ||
831 | .channels_max = 2, | ||
832 | .formats = IMX_SSI_BITS, | ||
833 | .rates = IMX_SSI_RATES,}, | ||
834 | .capture = { | ||
835 | .channels_min = 1, | ||
836 | .channels_max = 2, | ||
837 | .formats = IMX_SSI_BITS, | ||
838 | .rates = IMX_SSI_RATES,}, | ||
839 | .ops = &imx_ssi_pcm_dai_ops, | ||
840 | }, | ||
841 | }; | ||
842 | EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai); | ||
843 | |||
844 | static int __init imx_ssi_init(void) | ||
845 | { | ||
846 | return snd_soc_register_dais(imx_ssi_pcm_dai, | ||
847 | ARRAY_SIZE(imx_ssi_pcm_dai)); | ||
848 | } | ||
849 | |||
850 | static void __exit imx_ssi_exit(void) | ||
851 | { | ||
852 | snd_soc_unregister_dais(imx_ssi_pcm_dai, | ||
853 | ARRAY_SIZE(imx_ssi_pcm_dai)); | ||
854 | } | ||
855 | |||
856 | module_init(imx_ssi_init); | ||
857 | module_exit(imx_ssi_exit); | ||
858 | MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com"); | ||
859 | MODULE_DESCRIPTION("i.MX ASoC I2S driver"); | ||
860 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/imx/mxc-ssi.h b/sound/soc/imx/mxc-ssi.h deleted file mode 100644 index 12bbdc9c7ecd..000000000000 --- a/sound/soc/imx/mxc-ssi.h +++ /dev/null | |||
@@ -1,238 +0,0 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or modify | ||
3 | * it under the terms of the GNU General Public License version 2 as | ||
4 | * published by the Free Software Foundation. | ||
5 | */ | ||
6 | |||
7 | #ifndef _IMX_SSI_H | ||
8 | #define _IMX_SSI_H | ||
9 | |||
10 | #include <mach/hardware.h> | ||
11 | |||
12 | /* SSI regs definition - MOVE to /arch/arm/plat-mxc/include/mach/ when stable */ | ||
13 | #define SSI1_IO_BASE_ADDR IO_ADDRESS(SSI1_BASE_ADDR) | ||
14 | #define SSI2_IO_BASE_ADDR IO_ADDRESS(SSI2_BASE_ADDR) | ||
15 | |||
16 | #define STX0 0x00 | ||
17 | #define STX1 0x04 | ||
18 | #define SRX0 0x08 | ||
19 | #define SRX1 0x0c | ||
20 | #define SCR 0x10 | ||
21 | #define SISR 0x14 | ||
22 | #define SIER 0x18 | ||
23 | #define STCR 0x1c | ||
24 | #define SRCR 0x20 | ||
25 | #define STCCR 0x24 | ||
26 | #define SRCCR 0x28 | ||
27 | #define SFCSR 0x2c | ||
28 | #define STR 0x30 | ||
29 | #define SOR 0x34 | ||
30 | #define SACNT 0x38 | ||
31 | #define SACADD 0x3c | ||
32 | #define SACDAT 0x40 | ||
33 | #define SATAG 0x44 | ||
34 | #define STMSK 0x48 | ||
35 | #define SRMSK 0x4c | ||
36 | |||
37 | #define SSI1_STX0 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STX0))) | ||
38 | #define SSI1_STX1 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STX1))) | ||
39 | #define SSI1_SRX0 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRX0))) | ||
40 | #define SSI1_SRX1 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRX1))) | ||
41 | #define SSI1_SCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SCR))) | ||
42 | #define SSI1_SISR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SISR))) | ||
43 | #define SSI1_SIER (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SIER))) | ||
44 | #define SSI1_STCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STCR))) | ||
45 | #define SSI1_SRCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRCR))) | ||
46 | #define SSI1_STCCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STCCR))) | ||
47 | #define SSI1_SRCCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRCCR))) | ||
48 | #define SSI1_SFCSR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SFCSR))) | ||
49 | #define SSI1_STR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STR))) | ||
50 | #define SSI1_SOR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SOR))) | ||
51 | #define SSI1_SACNT (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACNT))) | ||
52 | #define SSI1_SACADD (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACADD))) | ||
53 | #define SSI1_SACDAT (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACDAT))) | ||
54 | #define SSI1_SATAG (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SATAG))) | ||
55 | #define SSI1_STMSK (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STMSK))) | ||
56 | #define SSI1_SRMSK (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRMSK))) | ||
57 | |||
58 | |||
59 | #define SSI2_STX0 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STX0))) | ||
60 | #define SSI2_STX1 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STX1))) | ||
61 | #define SSI2_SRX0 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRX0))) | ||
62 | #define SSI2_SRX1 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRX1))) | ||
63 | #define SSI2_SCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SCR))) | ||
64 | #define SSI2_SISR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SISR))) | ||
65 | #define SSI2_SIER (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SIER))) | ||
66 | #define SSI2_STCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STCR))) | ||
67 | #define SSI2_SRCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRCR))) | ||
68 | #define SSI2_STCCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STCCR))) | ||
69 | #define SSI2_SRCCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRCCR))) | ||
70 | #define SSI2_SFCSR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SFCSR))) | ||
71 | #define SSI2_STR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STR))) | ||
72 | #define SSI2_SOR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SOR))) | ||
73 | #define SSI2_SACNT (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACNT))) | ||
74 | #define SSI2_SACADD (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACADD))) | ||
75 | #define SSI2_SACDAT (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACDAT))) | ||
76 | #define SSI2_SATAG (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SATAG))) | ||
77 | #define SSI2_STMSK (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STMSK))) | ||
78 | #define SSI2_SRMSK (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRMSK))) | ||
79 | |||
80 | #define SSI_SCR_CLK_IST (1 << 9) | ||
81 | #define SSI_SCR_TCH_EN (1 << 8) | ||
82 | #define SSI_SCR_SYS_CLK_EN (1 << 7) | ||
83 | #define SSI_SCR_I2S_MODE_NORM (0 << 5) | ||
84 | #define SSI_SCR_I2S_MODE_MSTR (1 << 5) | ||
85 | #define SSI_SCR_I2S_MODE_SLAVE (2 << 5) | ||
86 | #define SSI_SCR_SYN (1 << 4) | ||
87 | #define SSI_SCR_NET (1 << 3) | ||
88 | #define SSI_SCR_RE (1 << 2) | ||
89 | #define SSI_SCR_TE (1 << 1) | ||
90 | #define SSI_SCR_SSIEN (1 << 0) | ||
91 | |||
92 | #define SSI_SISR_CMDAU (1 << 18) | ||
93 | #define SSI_SISR_CMDDU (1 << 17) | ||
94 | #define SSI_SISR_RXT (1 << 16) | ||
95 | #define SSI_SISR_RDR1 (1 << 15) | ||
96 | #define SSI_SISR_RDR0 (1 << 14) | ||
97 | #define SSI_SISR_TDE1 (1 << 13) | ||
98 | #define SSI_SISR_TDE0 (1 << 12) | ||
99 | #define SSI_SISR_ROE1 (1 << 11) | ||
100 | #define SSI_SISR_ROE0 (1 << 10) | ||
101 | #define SSI_SISR_TUE1 (1 << 9) | ||
102 | #define SSI_SISR_TUE0 (1 << 8) | ||
103 | #define SSI_SISR_TFS (1 << 7) | ||
104 | #define SSI_SISR_RFS (1 << 6) | ||
105 | #define SSI_SISR_TLS (1 << 5) | ||
106 | #define SSI_SISR_RLS (1 << 4) | ||
107 | #define SSI_SISR_RFF1 (1 << 3) | ||
108 | #define SSI_SISR_RFF0 (1 << 2) | ||
109 | #define SSI_SISR_TFE1 (1 << 1) | ||
110 | #define SSI_SISR_TFE0 (1 << 0) | ||
111 | |||
112 | #define SSI_SIER_RDMAE (1 << 22) | ||
113 | #define SSI_SIER_RIE (1 << 21) | ||
114 | #define SSI_SIER_TDMAE (1 << 20) | ||
115 | #define SSI_SIER_TIE (1 << 19) | ||
116 | #define SSI_SIER_CMDAU_EN (1 << 18) | ||
117 | #define SSI_SIER_CMDDU_EN (1 << 17) | ||
118 | #define SSI_SIER_RXT_EN (1 << 16) | ||
119 | #define SSI_SIER_RDR1_EN (1 << 15) | ||
120 | #define SSI_SIER_RDR0_EN (1 << 14) | ||
121 | #define SSI_SIER_TDE1_EN (1 << 13) | ||
122 | #define SSI_SIER_TDE0_EN (1 << 12) | ||
123 | #define SSI_SIER_ROE1_EN (1 << 11) | ||
124 | #define SSI_SIER_ROE0_EN (1 << 10) | ||
125 | #define SSI_SIER_TUE1_EN (1 << 9) | ||
126 | #define SSI_SIER_TUE0_EN (1 << 8) | ||
127 | #define SSI_SIER_TFS_EN (1 << 7) | ||
128 | #define SSI_SIER_RFS_EN (1 << 6) | ||
129 | #define SSI_SIER_TLS_EN (1 << 5) | ||
130 | #define SSI_SIER_RLS_EN (1 << 4) | ||
131 | #define SSI_SIER_RFF1_EN (1 << 3) | ||
132 | #define SSI_SIER_RFF0_EN (1 << 2) | ||
133 | #define SSI_SIER_TFE1_EN (1 << 1) | ||
134 | #define SSI_SIER_TFE0_EN (1 << 0) | ||
135 | |||
136 | #define SSI_STCR_TXBIT0 (1 << 9) | ||
137 | #define SSI_STCR_TFEN1 (1 << 8) | ||
138 | #define SSI_STCR_TFEN0 (1 << 7) | ||
139 | #define SSI_STCR_TFDIR (1 << 6) | ||
140 | #define SSI_STCR_TXDIR (1 << 5) | ||
141 | #define SSI_STCR_TSHFD (1 << 4) | ||
142 | #define SSI_STCR_TSCKP (1 << 3) | ||
143 | #define SSI_STCR_TFSI (1 << 2) | ||
144 | #define SSI_STCR_TFSL (1 << 1) | ||
145 | #define SSI_STCR_TEFS (1 << 0) | ||
146 | |||
147 | #define SSI_SRCR_RXBIT0 (1 << 9) | ||
148 | #define SSI_SRCR_RFEN1 (1 << 8) | ||
149 | #define SSI_SRCR_RFEN0 (1 << 7) | ||
150 | #define SSI_SRCR_RFDIR (1 << 6) | ||
151 | #define SSI_SRCR_RXDIR (1 << 5) | ||
152 | #define SSI_SRCR_RSHFD (1 << 4) | ||
153 | #define SSI_SRCR_RSCKP (1 << 3) | ||
154 | #define SSI_SRCR_RFSI (1 << 2) | ||
155 | #define SSI_SRCR_RFSL (1 << 1) | ||
156 | #define SSI_SRCR_REFS (1 << 0) | ||
157 | |||
158 | #define SSI_STCCR_DIV2 (1 << 18) | ||
159 | #define SSI_STCCR_PSR (1 << 15) | ||
160 | #define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13) | ||
161 | #define SSI_STCCR_DC(x) (((x) & 0x1f) << 8) | ||
162 | #define SSI_STCCR_PM(x) (((x) & 0xff) << 0) | ||
163 | #define SSI_STCCR_WL_MASK (0xf << 13) | ||
164 | #define SSI_STCCR_DC_MASK (0x1f << 8) | ||
165 | #define SSI_STCCR_PM_MASK (0xff << 0) | ||
166 | |||
167 | #define SSI_SRCCR_DIV2 (1 << 18) | ||
168 | #define SSI_SRCCR_PSR (1 << 15) | ||
169 | #define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13) | ||
170 | #define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8) | ||
171 | #define SSI_SRCCR_PM(x) (((x) & 0xff) << 0) | ||
172 | #define SSI_SRCCR_WL_MASK (0xf << 13) | ||
173 | #define SSI_SRCCR_DC_MASK (0x1f << 8) | ||
174 | #define SSI_SRCCR_PM_MASK (0xff << 0) | ||
175 | |||
176 | |||
177 | #define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28) | ||
178 | #define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24) | ||
179 | #define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20) | ||
180 | #define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16) | ||
181 | #define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12) | ||
182 | #define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8) | ||
183 | #define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4) | ||
184 | #define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0) | ||
185 | |||
186 | #define SSI_STR_TEST (1 << 15) | ||
187 | #define SSI_STR_RCK2TCK (1 << 14) | ||
188 | #define SSI_STR_RFS2TFS (1 << 13) | ||
189 | #define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8) | ||
190 | #define SSI_STR_TXD2RXD (1 << 7) | ||
191 | #define SSI_STR_TCK2RCK (1 << 6) | ||
192 | #define SSI_STR_TFS2RFS (1 << 5) | ||
193 | #define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0) | ||
194 | |||
195 | #define SSI_SOR_CLKOFF (1 << 6) | ||
196 | #define SSI_SOR_RX_CLR (1 << 5) | ||
197 | #define SSI_SOR_TX_CLR (1 << 4) | ||
198 | #define SSI_SOR_INIT (1 << 3) | ||
199 | #define SSI_SOR_WAIT(x) (((x) & 0x3) << 1) | ||
200 | #define SSI_SOR_SYNRST (1 << 0) | ||
201 | |||
202 | #define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5) | ||
203 | #define SSI_SACNT_WR (x << 4) | ||
204 | #define SSI_SACNT_RD (x << 3) | ||
205 | #define SSI_SACNT_TIF (x << 2) | ||
206 | #define SSI_SACNT_FV (x << 1) | ||
207 | #define SSI_SACNT_AC97EN (x << 0) | ||
208 | |||
209 | /* Watermarks for FIFO's */ | ||
210 | #define TXFIFO_WATERMARK 0x4 | ||
211 | #define RXFIFO_WATERMARK 0x4 | ||
212 | |||
213 | /* i.MX DAI SSP ID's */ | ||
214 | #define IMX_DAI_SSI0 0 /* SSI1 FIFO 0 */ | ||
215 | #define IMX_DAI_SSI1 1 /* SSI1 FIFO 1 */ | ||
216 | #define IMX_DAI_SSI2 2 /* SSI2 FIFO 0 */ | ||
217 | #define IMX_DAI_SSI3 3 /* SSI2 FIFO 1 */ | ||
218 | |||
219 | /* SSI clock sources */ | ||
220 | #define IMX_SSP_SYS_CLK 0 | ||
221 | |||
222 | /* SSI audio dividers */ | ||
223 | #define IMX_SSI_TX_DIV_2 0 | ||
224 | #define IMX_SSI_TX_DIV_PSR 1 | ||
225 | #define IMX_SSI_TX_DIV_PM 2 | ||
226 | #define IMX_SSI_RX_DIV_2 3 | ||
227 | #define IMX_SSI_RX_DIV_PSR 4 | ||
228 | #define IMX_SSI_RX_DIV_PM 5 | ||
229 | |||
230 | |||
231 | /* SSI Div 2 */ | ||
232 | #define IMX_SSI_DIV_2_OFF (~SSI_STCCR_DIV2) | ||
233 | #define IMX_SSI_DIV_2_ON SSI_STCCR_DIV2 | ||
234 | |||
235 | extern struct snd_soc_dai imx_ssi_pcm_dai[4]; | ||
236 | extern int get_ssi_clk(int ssi, struct device *dev); | ||
237 | extern void put_ssi_clk(int ssi); | ||
238 | #endif | ||
diff --git a/sound/soc/imx/phycore-ac97.c b/sound/soc/imx/phycore-ac97.c new file mode 100644 index 000000000000..a8307d55c70e --- /dev/null +++ b/sound/soc/imx/phycore-ac97.c | |||
@@ -0,0 +1,90 @@ | |||
1 | /* | ||
2 | * phycore-ac97.c -- SoC audio for imx_phycore in AC97 mode | ||
3 | * | ||
4 | * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> | ||
5 | * | ||
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. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/moduleparam.h> | ||
15 | #include <linux/device.h> | ||
16 | #include <linux/i2c.h> | ||
17 | #include <sound/core.h> | ||
18 | #include <sound/pcm.h> | ||
19 | #include <sound/soc.h> | ||
20 | #include <sound/soc-dapm.h> | ||
21 | #include <asm/mach-types.h> | ||
22 | |||
23 | #include "../codecs/wm9712.h" | ||
24 | #include "imx-ssi.h" | ||
25 | |||
26 | static struct snd_soc_card imx_phycore; | ||
27 | |||
28 | static struct snd_soc_ops imx_phycore_hifi_ops = { | ||
29 | }; | ||
30 | |||
31 | static struct snd_soc_dai_link imx_phycore_dai_ac97[] = { | ||
32 | { | ||
33 | .name = "HiFi", | ||
34 | .stream_name = "HiFi", | ||
35 | .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI], | ||
36 | .ops = &imx_phycore_hifi_ops, | ||
37 | }, | ||
38 | }; | ||
39 | |||
40 | static struct snd_soc_card imx_phycore = { | ||
41 | .name = "PhyCORE-audio", | ||
42 | .platform = &imx_soc_platform, | ||
43 | .dai_link = imx_phycore_dai_ac97, | ||
44 | .num_links = ARRAY_SIZE(imx_phycore_dai_ac97), | ||
45 | }; | ||
46 | |||
47 | static struct snd_soc_device imx_phycore_snd_devdata = { | ||
48 | .card = &imx_phycore, | ||
49 | .codec_dev = &soc_codec_dev_wm9712, | ||
50 | }; | ||
51 | |||
52 | static struct platform_device *imx_phycore_snd_device; | ||
53 | |||
54 | static int __init imx_phycore_init(void) | ||
55 | { | ||
56 | int ret; | ||
57 | |||
58 | if (!machine_is_pcm043() && !machine_is_pca100()) | ||
59 | /* return happy. We might run on a totally different machine */ | ||
60 | return 0; | ||
61 | |||
62 | imx_phycore_snd_device = platform_device_alloc("soc-audio", -1); | ||
63 | if (!imx_phycore_snd_device) | ||
64 | return -ENOMEM; | ||
65 | |||
66 | imx_phycore_dai_ac97[0].cpu_dai = &imx_ssi_pcm_dai[0]; | ||
67 | |||
68 | platform_set_drvdata(imx_phycore_snd_device, &imx_phycore_snd_devdata); | ||
69 | imx_phycore_snd_devdata.dev = &imx_phycore_snd_device->dev; | ||
70 | ret = platform_device_add(imx_phycore_snd_device); | ||
71 | |||
72 | if (ret) { | ||
73 | printk(KERN_ERR "ASoC: Platform device allocation failed\n"); | ||
74 | platform_device_put(imx_phycore_snd_device); | ||
75 | } | ||
76 | |||
77 | return ret; | ||
78 | } | ||
79 | |||
80 | static void __exit imx_phycore_exit(void) | ||
81 | { | ||
82 | platform_device_unregister(imx_phycore_snd_device); | ||
83 | } | ||
84 | |||
85 | late_initcall(imx_phycore_init); | ||
86 | module_exit(imx_phycore_exit); | ||
87 | |||
88 | MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); | ||
89 | MODULE_DESCRIPTION("PhyCORE ALSA SoC driver"); | ||
90 | MODULE_LICENSE("GPL"); | ||