aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/imx
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/imx')
-rw-r--r--sound/soc/imx/Kconfig20
-rw-r--r--sound/soc/imx/Makefile14
-rw-r--r--sound/soc/imx/imx-pcm-dma-mx2.c313
-rw-r--r--sound/soc/imx/imx-pcm-fiq.c297
-rw-r--r--sound/soc/imx/imx-ssi.c758
-rw-r--r--sound/soc/imx/imx-ssi.h237
-rw-r--r--sound/soc/imx/mx1_mx2-pcm.c488
-rw-r--r--sound/soc/imx/mx1_mx2-pcm.h26
-rw-r--r--sound/soc/imx/mx27vis_wm8974.c318
-rw-r--r--sound/soc/imx/mxc-ssi.c860
-rw-r--r--sound/soc/imx/mxc-ssi.h238
-rw-r--r--sound/soc/imx/phycore-ac97.c90
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 @@
1config SND_MX1_MX2_SOC 1config 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
9config SND_MXC_SOC_SSI 11config SND_MXC_SOC_SSI
10 tristate 12 tristate
11 13
12config 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
2snd-soc-mx1_mx2-objs := mx1_mx2-pcm.o 2snd-soc-imx-objs := imx-ssi.o imx-pcm-fiq.o
3snd-soc-mxc-ssi-objs := mxc-ssi.o
4 3
5obj-$(CONFIG_SND_MX1_MX2_SOC) += snd-soc-mx1_mx2.o 4ifdef CONFIG_MACH_MX27
6obj-$(CONFIG_SND_MXC_SOC_SSI) += snd-soc-mxc-ssi.o 5snd-soc-imx-objs += imx-pcm-dma-mx2.o
6endif
7
8obj-$(CONFIG_SND_IMX_SOC) += snd-soc-imx.o
7 9
8# i.MX Machine Support 10# i.MX Machine Support
9snd-soc-mx27vis-wm8974-objs := mx27vis_wm8974.o 11snd-soc-phycore-ac97-objs := phycore-ac97.o
10obj-$(CONFIG_SND_SOC_MX27VIS_WM8974) += snd-soc-mx27vis-wm8974.o 12obj-$(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
33struct 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 */
49static 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
66static void imx_ssi_dma_callback(int channel, void *data)
67{
68 pr_err("%s shouldn't be called\n", __func__);
69}
70
71static 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
83static 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;
122out:
123 imx_dma_free(iprtd->dma);
124 return ret;
125}
126
127static 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
175static 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
191static 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
216static 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
242static 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
250static 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
269static 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
287static 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
298static 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
305struct 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
35struct 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
45static inline void imx_ssi_set_next_poll(struct imx_pcm_runtime_data *iprtd)
46{
47 iprtd->timer.expires = jiffies + iprtd->poll_time;
48}
49
50static 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(&regs);
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
86static struct fiq_handler fh = {
87 .name = DRV_NAME,
88};
89
90static 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
108static 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(&regs);
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(&regs);
121
122 return 0;
123}
124
125static int fiq_enable;
126static int imx_pcm_fiq;
127
128static 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
160static 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
168static 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
187static 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
209static 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
220static 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
231static 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
262static 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
268struct 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
291void 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 */
60static 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 */
88static 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 */
156static 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 */
184static 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 */
232static 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
268static 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
333static 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
342static 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
358int 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
374static 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
392static u64 imx_pcm_dmamask = DMA_BIT_MASK(32);
393
394int 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
418out:
419 return ret;
420}
421
422void 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
443struct snd_soc_platform imx_soc_platform = {
444 .name = "imx-audio",
445};
446EXPORT_SYMBOL_GPL(imx_soc_platform);
447
448static 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
468static 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
498static struct imx_ssi *ac97_ssi;
499
500static 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
523static 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
545static 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
553static 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
561struct 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};
567EXPORT_SYMBOL_GPL(soc_ac97_ops);
568
569struct snd_soc_dai imx_ssi_pcm_dai[2];
570EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai);
571
572static 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
679failed_register:
680failed_ac97:
681 iounmap(ssi->base);
682failed_ioremap:
683 release_mem_region(res->start, resource_size(res));
684failed_get_resource:
685 clk_disable(ssi->clk);
686 clk_put(ssi->clk);
687failed_clk:
688 kfree(ssi);
689
690 return ret;
691}
692
693static 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
716static 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
726static 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
745static void __exit imx_ssi_exit(void)
746{
747 platform_driver_unregister(&imx_ssi_driver);
748 snd_soc_unregister_platform(&imx_soc_platform);
749}
750
751module_init(imx_ssi_init);
752module_exit(imx_ssi_exit);
753
754/* Module information */
755MODULE_AUTHOR("Sascha Hauer, <s.hauer@pengutronix.de>");
756MODULE_DESCRIPTION("i.MX I2S/ac97 SoC Interface");
757MODULE_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
186extern struct snd_soc_dai imx_ssi_pcm_dai[2];
187extern struct snd_soc_platform imx_soc_platform;
188
189#define DRV_NAME "imx-ssi"
190
191struct imx_pcm_dma_params {
192 int dma;
193 unsigned long dma_addr;
194 int burstsize;
195};
196
197struct 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
218struct snd_soc_platform *imx_ssi_fiq_init(struct platform_device *pdev,
219 struct imx_ssi *ssi);
220void imx_ssi_fiq_exit(struct platform_device *pdev, struct imx_ssi *ssi);
221struct snd_soc_platform *imx_ssi_dma_mx2_init(struct platform_device *pdev,
222 struct imx_ssi *ssi);
223
224int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma);
225int imx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
226 struct snd_pcm *pcm);
227void 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
33static 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
47struct 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 */
65static 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 */
96static 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 */
155static 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 */
198static int
199snd_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
210static 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
234static 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
246static 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
272static snd_pcm_uframes_t
273mx1_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
295static 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
359static 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
369static 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
379static 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
391static u64 mx1_mx2_pcm_dmamask = 0xffffffff;
392
393static 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
416static 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
437static 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
466struct 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};
472EXPORT_SYMBOL_GPL(mx1_mx2_soc_platform);
473
474static int __init mx1_mx2_soc_platform_init(void)
475{
476 return snd_soc_register_platform(&mx1_mx2_soc_platform);
477}
478module_init(mx1_mx2_soc_platform_init);
479
480static void __exit mx1_mx2_soc_platform_exit(void)
481{
482 snd_soc_unregister_platform(&mx1_mx2_soc_platform);
483}
484module_exit(mx1_mx2_soc_platform_exit);
485
486MODULE_AUTHOR("Javier Martin, javier.martin@vista-silicon.com");
487MODULE_DESCRIPTION("Freescale i.MX2x, i.MX1x PCM DMA module");
488MODULE_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 */
13struct 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 */
24extern 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
34static 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 */
42void 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
66static 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
177static 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 */
190static struct snd_soc_ops mx27vis_hifi_ops = {
191 .hw_params = mx27vis_hifi_hw_params,
192 .hw_free = mx27vis_hifi_hw_free,
193};
194
195
196static int mx27vis_suspend(struct platform_device *pdev, pm_message_t state)
197{
198 return 0;
199}
200
201static int mx27vis_resume(struct platform_device *pdev)
202{
203 return 0;
204}
205
206static 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
221static int mx27vis_remove(struct platform_device *pdev)
222{
223 put_ssi_clk(0);
224 return 0;
225}
226
227static 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
237static 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
248static struct snd_soc_device mx27vis_snd_devdata = {
249 .card = &mx27vis,
250 .codec_dev = &soc_codec_dev_wm8974,
251};
252
253static struct platform_device *mx27vis_snd_device;
254
255/* Temporal definition of board specific behaviour */
256void 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
283static 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
307static void __exit mx27vis_exit(void)
308{
309 /* We should call some "ssi_gpio_inactive()" properly */
310}
311
312module_init(mx27vis_init);
313module_exit(mx27vis_exit);
314
315
316MODULE_AUTHOR("Javier Martin, javier.martin@vista-silicon.com");
317MODULE_DESCRIPTION("ALSA SoC WM8974 mx27vis");
318MODULE_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
42static int ssi_active[2] = {0, 0};
43
44/* DMA information for mx1_mx2 platforms */
45static 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
55static 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
65static 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
75static 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
85static 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
95static 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
105static 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
115static 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
125static struct clk *ssi_clk0, *ssi_clk1;
126
127int 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}
144EXPORT_SYMBOL(get_ssi_clk);
145
146void 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}
159EXPORT_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 */
165static 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 */
212static 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 */
273static 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 */
315static 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
413static 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
502int 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
554int 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 */
610int 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
645int 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
668static 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
709static 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
736static int imx_ssi_suspend(struct platform_device *dev,
737 struct snd_soc_dai *dai)
738{
739 return 0;
740}
741
742static 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
764static 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
776struct 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};
842EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai);
843
844static 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
850static 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
856module_init(imx_ssi_init);
857module_exit(imx_ssi_exit);
858MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com");
859MODULE_DESCRIPTION("i.MX ASoC I2S driver");
860MODULE_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
235extern struct snd_soc_dai imx_ssi_pcm_dai[4];
236extern int get_ssi_clk(int ssi, struct device *dev);
237extern 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
26static struct snd_soc_card imx_phycore;
27
28static struct snd_soc_ops imx_phycore_hifi_ops = {
29};
30
31static 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
40static 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
47static struct snd_soc_device imx_phycore_snd_devdata = {
48 .card = &imx_phycore,
49 .codec_dev = &soc_codec_dev_wm9712,
50};
51
52static struct platform_device *imx_phycore_snd_device;
53
54static 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
80static void __exit imx_phycore_exit(void)
81{
82 platform_device_unregister(imx_phycore_snd_device);
83}
84
85late_initcall(imx_phycore_init);
86module_exit(imx_phycore_exit);
87
88MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
89MODULE_DESCRIPTION("PhyCORE ALSA SoC driver");
90MODULE_LICENSE("GPL");