diff options
-rw-r--r-- | sound/soc/imx/Kconfig | 2 | ||||
-rw-r--r-- | sound/soc/imx/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/imx/mxc-ssi.c | 868 | ||||
-rw-r--r-- | sound/soc/imx/mxc-ssi.h | 238 |
4 files changed, 1110 insertions, 0 deletions
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig index a1bf053bf462..886dadd76bb2 100644 --- a/sound/soc/imx/Kconfig +++ b/sound/soc/imx/Kconfig | |||
@@ -6,6 +6,8 @@ config SND_MX1_MX2_SOC | |||
6 | Say Y or M if you want to add support for codecs attached to | 6 | Say Y or M if you want to add support for codecs attached to |
7 | the MX1 or MX2 SSI interface. | 7 | the MX1 or MX2 SSI interface. |
8 | 8 | ||
9 | config SND_MXC_SOC_SSI | ||
10 | tristate | ||
9 | 11 | ||
10 | 12 | ||
11 | 13 | ||
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile index c390f0f6960d..6552cb202bcc 100644 --- a/sound/soc/imx/Makefile +++ b/sound/soc/imx/Makefile | |||
@@ -1,4 +1,6 @@ | |||
1 | # i.MX Platform Support | 1 | # i.MX Platform Support |
2 | snd-soc-mx1_mx2-objs := mx1_mx2-pcm.o | 2 | snd-soc-mx1_mx2-objs := mx1_mx2-pcm.o |
3 | snd-soc-mxc-ssi-objs := mxc-ssi.o | ||
3 | 4 | ||
4 | obj-$(CONFIG_SND_MX1_MX2_SOC) += snd-soc-mx1_mx2.o | 5 | obj-$(CONFIG_SND_MX1_MX2_SOC) += snd-soc-mx1_mx2.o |
6 | obj-$(CONFIG_SND_MXC_SOC_SSI) += snd-soc-mxc-ssi.o | ||
diff --git a/sound/soc/imx/mxc-ssi.c b/sound/soc/imx/mxc-ssi.c new file mode 100644 index 000000000000..3806ff2c0cd4 --- /dev/null +++ b/sound/soc/imx/mxc-ssi.c | |||
@@ -0,0 +1,868 @@ | |||
1 | /* | ||
2 | * mxc-ssi.c -- SSI driver for Freescale IMX | ||
3 | * | ||
4 | * Copyright 2006 Wolfson Microelectronics PLC. | ||
5 | * Author: Liam Girdwood | ||
6 | * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com | ||
7 | * | ||
8 | * Based on mxc-alsa-mc13783 (C) 2006 Freescale. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | * | ||
15 | * TODO: | ||
16 | * Need to rework SSI register defs when new defs go into mainline. | ||
17 | * Add support for TDM and FIFO 1. | ||
18 | * Add support for i.mx3x DMA interface. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | |||
23 | #include <linux/module.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/platform_device.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/dma-mapping.h> | ||
28 | #include <linux/clk.h> | ||
29 | #include <sound/core.h> | ||
30 | #include <sound/pcm.h> | ||
31 | #include <sound/pcm_params.h> | ||
32 | #include <sound/soc.h> | ||
33 | #include <mach/dma-mx1-mx2.h> | ||
34 | #include <asm/mach-types.h> | ||
35 | |||
36 | #include "mxc-ssi.h" | ||
37 | #include "mx1_mx2-pcm.h" | ||
38 | |||
39 | #define SSI1_PORT 0 | ||
40 | #define SSI2_PORT 1 | ||
41 | |||
42 | static int ssi_active[2] = {0, 0}; | ||
43 | |||
44 | /* DMA information for mx1_mx2 platforms */ | ||
45 | static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_out0 = { | ||
46 | .name = "SSI1 PCM Stereo out 0", | ||
47 | .transfer_type = DMA_MODE_WRITE, | ||
48 | .per_address = SSI1_BASE_ADDR + STX0, | ||
49 | .event_id = DMA_REQ_SSI1_TX0, | ||
50 | .watermark_level = TXFIFO_WATERMARK, | ||
51 | .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, | ||
52 | .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | ||
53 | }; | ||
54 | |||
55 | static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_out1 = { | ||
56 | .name = "SSI1 PCM Stereo out 1", | ||
57 | .transfer_type = DMA_MODE_WRITE, | ||
58 | .per_address = SSI1_BASE_ADDR + STX1, | ||
59 | .event_id = DMA_REQ_SSI1_TX1, | ||
60 | .watermark_level = TXFIFO_WATERMARK, | ||
61 | .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, | ||
62 | .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | ||
63 | }; | ||
64 | |||
65 | static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_in0 = { | ||
66 | .name = "SSI1 PCM Stereo in 0", | ||
67 | .transfer_type = DMA_MODE_READ, | ||
68 | .per_address = SSI1_BASE_ADDR + SRX0, | ||
69 | .event_id = DMA_REQ_SSI1_RX0, | ||
70 | .watermark_level = RXFIFO_WATERMARK, | ||
71 | .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, | ||
72 | .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | ||
73 | }; | ||
74 | |||
75 | static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_in1 = { | ||
76 | .name = "SSI1 PCM Stereo in 1", | ||
77 | .transfer_type = DMA_MODE_READ, | ||
78 | .per_address = SSI1_BASE_ADDR + SRX1, | ||
79 | .event_id = DMA_REQ_SSI1_RX1, | ||
80 | .watermark_level = RXFIFO_WATERMARK, | ||
81 | .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, | ||
82 | .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | ||
83 | }; | ||
84 | |||
85 | static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_out0 = { | ||
86 | .name = "SSI2 PCM Stereo out 0", | ||
87 | .transfer_type = DMA_MODE_WRITE, | ||
88 | .per_address = SSI2_BASE_ADDR + STX0, | ||
89 | .event_id = DMA_REQ_SSI2_TX0, | ||
90 | .watermark_level = TXFIFO_WATERMARK, | ||
91 | .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, | ||
92 | .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | ||
93 | }; | ||
94 | |||
95 | static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_out1 = { | ||
96 | .name = "SSI2 PCM Stereo out 1", | ||
97 | .transfer_type = DMA_MODE_WRITE, | ||
98 | .per_address = SSI2_BASE_ADDR + STX1, | ||
99 | .event_id = DMA_REQ_SSI2_TX1, | ||
100 | .watermark_level = TXFIFO_WATERMARK, | ||
101 | .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, | ||
102 | .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | ||
103 | }; | ||
104 | |||
105 | static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_in0 = { | ||
106 | .name = "SSI2 PCM Stereo in 0", | ||
107 | .transfer_type = DMA_MODE_READ, | ||
108 | .per_address = SSI2_BASE_ADDR + SRX0, | ||
109 | .event_id = DMA_REQ_SSI2_RX0, | ||
110 | .watermark_level = RXFIFO_WATERMARK, | ||
111 | .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, | ||
112 | .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | ||
113 | }; | ||
114 | |||
115 | static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_in1 = { | ||
116 | .name = "SSI2 PCM Stereo in 1", | ||
117 | .transfer_type = DMA_MODE_READ, | ||
118 | .per_address = SSI2_BASE_ADDR + SRX1, | ||
119 | .event_id = DMA_REQ_SSI2_RX1, | ||
120 | .watermark_level = RXFIFO_WATERMARK, | ||
121 | .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, | ||
122 | .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, | ||
123 | }; | ||
124 | |||
125 | static struct clk *ssi_clk0, *ssi_clk1; | ||
126 | |||
127 | int get_ssi_clk(int ssi, struct device *dev) | ||
128 | { | ||
129 | switch (ssi) { | ||
130 | case 0: | ||
131 | ssi_clk0 = clk_get(dev, "ssi1"); | ||
132 | if (IS_ERR(ssi_clk0)) | ||
133 | return PTR_ERR(ssi_clk0); | ||
134 | return 0; | ||
135 | case 1: | ||
136 | ssi_clk1 = clk_get(dev, "ssi2"); | ||
137 | if (IS_ERR(ssi_clk1)) | ||
138 | return PTR_ERR(ssi_clk1); | ||
139 | return 0; | ||
140 | default: | ||
141 | return -EINVAL; | ||
142 | } | ||
143 | } | ||
144 | EXPORT_SYMBOL(get_ssi_clk); | ||
145 | |||
146 | void put_ssi_clk(int ssi) | ||
147 | { | ||
148 | switch (ssi) { | ||
149 | case 0: | ||
150 | clk_put(ssi_clk0); | ||
151 | ssi_clk0 = NULL; | ||
152 | break; | ||
153 | case 1: | ||
154 | clk_put(ssi_clk1); | ||
155 | ssi_clk1 = NULL; | ||
156 | break; | ||
157 | } | ||
158 | } | ||
159 | EXPORT_SYMBOL(put_ssi_clk); | ||
160 | |||
161 | /* | ||
162 | * SSI system clock configuration. | ||
163 | * Should only be called when port is inactive (i.e. SSIEN = 0). | ||
164 | */ | ||
165 | static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai, | ||
166 | int clk_id, unsigned int freq, int dir) | ||
167 | { | ||
168 | u32 scr; | ||
169 | |||
170 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
171 | scr = SSI1_SCR; | ||
172 | pr_debug("%s: SCR for SSI1 is %x\n", __func__, scr); | ||
173 | } else { | ||
174 | scr = SSI2_SCR; | ||
175 | pr_debug("%s: SCR for SSI2 is %x\n", __func__, scr); | ||
176 | } | ||
177 | |||
178 | if (scr & SSI_SCR_SSIEN) { | ||
179 | printk(KERN_WARNING "Warning ssi already enabled\n"); | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | switch (clk_id) { | ||
184 | case IMX_SSP_SYS_CLK: | ||
185 | if (dir == SND_SOC_CLOCK_OUT) { | ||
186 | scr |= SSI_SCR_SYS_CLK_EN; | ||
187 | pr_debug("%s: clk of is output\n", __func__); | ||
188 | } else { | ||
189 | scr &= ~SSI_SCR_SYS_CLK_EN; | ||
190 | pr_debug("%s: clk of is input\n", __func__); | ||
191 | } | ||
192 | break; | ||
193 | default: | ||
194 | return -EINVAL; | ||
195 | } | ||
196 | |||
197 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
198 | pr_debug("%s: writeback of SSI1_SCR\n", __func__); | ||
199 | SSI1_SCR = scr; | ||
200 | } else { | ||
201 | pr_debug("%s: writeback of SSI2_SCR\n", __func__); | ||
202 | SSI2_SCR = scr; | ||
203 | } | ||
204 | |||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | * SSI Clock dividers | ||
210 | * Should only be called when port is inactive (i.e. SSIEN = 0). | ||
211 | */ | ||
212 | static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, | ||
213 | int div_id, int div) | ||
214 | { | ||
215 | u32 stccr, srccr; | ||
216 | |||
217 | pr_debug("%s\n", __func__); | ||
218 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
219 | if (SSI1_SCR & SSI_SCR_SSIEN) | ||
220 | return 0; | ||
221 | srccr = SSI1_STCCR; | ||
222 | stccr = SSI1_STCCR; | ||
223 | } else { | ||
224 | if (SSI2_SCR & SSI_SCR_SSIEN) | ||
225 | return 0; | ||
226 | srccr = SSI2_STCCR; | ||
227 | stccr = SSI2_STCCR; | ||
228 | } | ||
229 | |||
230 | switch (div_id) { | ||
231 | case IMX_SSI_TX_DIV_2: | ||
232 | stccr &= ~SSI_STCCR_DIV2; | ||
233 | stccr |= div; | ||
234 | break; | ||
235 | case IMX_SSI_TX_DIV_PSR: | ||
236 | stccr &= ~SSI_STCCR_PSR; | ||
237 | stccr |= div; | ||
238 | break; | ||
239 | case IMX_SSI_TX_DIV_PM: | ||
240 | stccr &= ~0xff; | ||
241 | stccr |= SSI_STCCR_PM(div); | ||
242 | break; | ||
243 | case IMX_SSI_RX_DIV_2: | ||
244 | stccr &= ~SSI_STCCR_DIV2; | ||
245 | stccr |= div; | ||
246 | break; | ||
247 | case IMX_SSI_RX_DIV_PSR: | ||
248 | stccr &= ~SSI_STCCR_PSR; | ||
249 | stccr |= div; | ||
250 | break; | ||
251 | case IMX_SSI_RX_DIV_PM: | ||
252 | stccr &= ~0xff; | ||
253 | stccr |= SSI_STCCR_PM(div); | ||
254 | break; | ||
255 | default: | ||
256 | return -EINVAL; | ||
257 | } | ||
258 | |||
259 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
260 | SSI1_STCCR = stccr; | ||
261 | SSI1_SRCCR = srccr; | ||
262 | } else { | ||
263 | SSI2_STCCR = stccr; | ||
264 | SSI2_SRCCR = srccr; | ||
265 | } | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | /* | ||
270 | * SSI Network Mode or TDM slots configuration. | ||
271 | * Should only be called when port is inactive (i.e. SSIEN = 0). | ||
272 | */ | ||
273 | static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, | ||
274 | unsigned int mask, int slots) | ||
275 | { | ||
276 | u32 stmsk, srmsk, stccr; | ||
277 | |||
278 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
279 | if (SSI1_SCR & SSI_SCR_SSIEN) { | ||
280 | printk(KERN_WARNING "Warning ssi already enabled\n"); | ||
281 | return 0; | ||
282 | } | ||
283 | stccr = SSI1_STCCR; | ||
284 | } else { | ||
285 | if (SSI2_SCR & SSI_SCR_SSIEN) { | ||
286 | printk(KERN_WARNING "Warning ssi already enabled\n"); | ||
287 | return 0; | ||
288 | } | ||
289 | stccr = SSI2_STCCR; | ||
290 | } | ||
291 | |||
292 | stmsk = srmsk = mask; | ||
293 | stccr &= ~SSI_STCCR_DC_MASK; | ||
294 | stccr |= SSI_STCCR_DC(slots - 1); | ||
295 | |||
296 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
297 | SSI1_STMSK = stmsk; | ||
298 | SSI1_SRMSK = srmsk; | ||
299 | SSI1_SRCCR = SSI1_STCCR = stccr; | ||
300 | } else { | ||
301 | SSI2_STMSK = stmsk; | ||
302 | SSI2_SRMSK = srmsk; | ||
303 | SSI2_SRCCR = SSI2_STCCR = stccr; | ||
304 | } | ||
305 | |||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | /* | ||
310 | * SSI DAI format configuration. | ||
311 | * Should only be called when port is inactive (i.e. SSIEN = 0). | ||
312 | * Note: We don't use the I2S modes but instead manually configure the | ||
313 | * SSI for I2S. | ||
314 | */ | ||
315 | static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, | ||
316 | unsigned int fmt) | ||
317 | { | ||
318 | u32 stcr = 0, srcr = 0, scr; | ||
319 | |||
320 | /* | ||
321 | * This is done to avoid this function to modify | ||
322 | * previous set values in stcr | ||
323 | */ | ||
324 | stcr = SSI1_STCR; | ||
325 | |||
326 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) | ||
327 | scr = SSI1_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET); | ||
328 | else | ||
329 | scr = SSI2_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET); | ||
330 | |||
331 | if (scr & SSI_SCR_SSIEN) { | ||
332 | printk(KERN_WARNING "Warning ssi already enabled\n"); | ||
333 | return 0; | ||
334 | } | ||
335 | |||
336 | /* DAI mode */ | ||
337 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
338 | case SND_SOC_DAIFMT_I2S: | ||
339 | /* data on rising edge of bclk, frame low 1clk before data */ | ||
340 | stcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0; | ||
341 | srcr |= SSI_SRCR_RFSI | SSI_SRCR_REFS | SSI_SRCR_RXBIT0; | ||
342 | break; | ||
343 | case SND_SOC_DAIFMT_LEFT_J: | ||
344 | /* data on rising edge of bclk, frame high with data */ | ||
345 | stcr |= SSI_STCR_TXBIT0; | ||
346 | srcr |= SSI_SRCR_RXBIT0; | ||
347 | break; | ||
348 | case SND_SOC_DAIFMT_DSP_B: | ||
349 | /* data on rising edge of bclk, frame high with data */ | ||
350 | stcr |= SSI_STCR_TFSL; | ||
351 | srcr |= SSI_SRCR_RFSL; | ||
352 | break; | ||
353 | case SND_SOC_DAIFMT_DSP_A: | ||
354 | /* data on rising edge of bclk, frame high 1clk before data */ | ||
355 | stcr |= SSI_STCR_TFSL | SSI_STCR_TEFS; | ||
356 | srcr |= SSI_SRCR_RFSL | SSI_SRCR_REFS; | ||
357 | break; | ||
358 | } | ||
359 | |||
360 | /* DAI clock inversion */ | ||
361 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
362 | case SND_SOC_DAIFMT_IB_IF: | ||
363 | stcr |= SSI_STCR_TFSI; | ||
364 | stcr &= ~SSI_STCR_TSCKP; | ||
365 | srcr |= SSI_SRCR_RFSI; | ||
366 | srcr &= ~SSI_SRCR_RSCKP; | ||
367 | break; | ||
368 | case SND_SOC_DAIFMT_IB_NF: | ||
369 | stcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI); | ||
370 | srcr &= ~(SSI_SRCR_RSCKP | SSI_SRCR_RFSI); | ||
371 | break; | ||
372 | case SND_SOC_DAIFMT_NB_IF: | ||
373 | stcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP; | ||
374 | srcr |= SSI_SRCR_RFSI | SSI_SRCR_RSCKP; | ||
375 | break; | ||
376 | case SND_SOC_DAIFMT_NB_NF: | ||
377 | stcr &= ~SSI_STCR_TFSI; | ||
378 | stcr |= SSI_STCR_TSCKP; | ||
379 | srcr &= ~SSI_SRCR_RFSI; | ||
380 | srcr |= SSI_SRCR_RSCKP; | ||
381 | break; | ||
382 | } | ||
383 | |||
384 | /* DAI clock master masks */ | ||
385 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
386 | case SND_SOC_DAIFMT_CBS_CFS: | ||
387 | stcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR; | ||
388 | srcr |= SSI_SRCR_RFDIR | SSI_SRCR_RXDIR; | ||
389 | break; | ||
390 | case SND_SOC_DAIFMT_CBM_CFS: | ||
391 | stcr |= SSI_STCR_TFDIR; | ||
392 | srcr |= SSI_SRCR_RFDIR; | ||
393 | break; | ||
394 | case SND_SOC_DAIFMT_CBS_CFM: | ||
395 | stcr |= SSI_STCR_TXDIR; | ||
396 | srcr |= SSI_SRCR_RXDIR; | ||
397 | break; | ||
398 | } | ||
399 | |||
400 | /* sync */ | ||
401 | if (!(fmt & SND_SOC_DAIFMT_ASYNC)) | ||
402 | scr |= SSI_SCR_SYN; | ||
403 | |||
404 | /* tdm - only for stereo atm */ | ||
405 | if (fmt & SND_SOC_DAIFMT_TDM) | ||
406 | scr |= SSI_SCR_NET; | ||
407 | |||
408 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
409 | SSI1_STCR = stcr; | ||
410 | SSI1_SRCR = srcr; | ||
411 | SSI1_SCR = scr; | ||
412 | } else { | ||
413 | SSI2_STCR = stcr; | ||
414 | SSI2_SRCR = srcr; | ||
415 | SSI2_SCR = scr; | ||
416 | } | ||
417 | |||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | static int imx_ssi_startup(struct snd_pcm_substream *substream, | ||
422 | struct snd_soc_dai *dai) | ||
423 | { | ||
424 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
425 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
426 | |||
427 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
428 | /* set up TX DMA params */ | ||
429 | switch (cpu_dai->id) { | ||
430 | case IMX_DAI_SSI0: | ||
431 | cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out0; | ||
432 | break; | ||
433 | case IMX_DAI_SSI1: | ||
434 | cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out1; | ||
435 | break; | ||
436 | case IMX_DAI_SSI2: | ||
437 | cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out0; | ||
438 | break; | ||
439 | case IMX_DAI_SSI3: | ||
440 | cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out1; | ||
441 | } | ||
442 | pr_debug("%s: (playback)\n", __func__); | ||
443 | } else { | ||
444 | /* set up RX DMA params */ | ||
445 | switch (cpu_dai->id) { | ||
446 | case IMX_DAI_SSI0: | ||
447 | cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in0; | ||
448 | break; | ||
449 | case IMX_DAI_SSI1: | ||
450 | cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in1; | ||
451 | break; | ||
452 | case IMX_DAI_SSI2: | ||
453 | cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in0; | ||
454 | break; | ||
455 | case IMX_DAI_SSI3: | ||
456 | cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in1; | ||
457 | } | ||
458 | pr_debug("%s: (capture)\n", __func__); | ||
459 | } | ||
460 | |||
461 | /* | ||
462 | * we cant really change any SSI values after SSI is enabled | ||
463 | * need to fix in software for max flexibility - lrg | ||
464 | */ | ||
465 | if (cpu_dai->active) { | ||
466 | printk(KERN_WARNING "Warning ssi already enabled\n"); | ||
467 | return 0; | ||
468 | } | ||
469 | |||
470 | /* reset the SSI port - Sect 45.4.4 */ | ||
471 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
472 | |||
473 | if (!ssi_clk0) | ||
474 | return -EINVAL; | ||
475 | |||
476 | if (ssi_active[SSI1_PORT]++) { | ||
477 | pr_debug("%s: exit before reset\n", __func__); | ||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | /* SSI1 Reset */ | ||
482 | SSI1_SCR = 0; | ||
483 | |||
484 | SSI1_SFCSR = SSI_SFCSR_RFWM1(RXFIFO_WATERMARK) | | ||
485 | SSI_SFCSR_RFWM0(RXFIFO_WATERMARK) | | ||
486 | SSI_SFCSR_TFWM1(TXFIFO_WATERMARK) | | ||
487 | SSI_SFCSR_TFWM0(TXFIFO_WATERMARK); | ||
488 | } else { | ||
489 | |||
490 | if (!ssi_clk1) | ||
491 | return -EINVAL; | ||
492 | |||
493 | if (ssi_active[SSI2_PORT]++) { | ||
494 | pr_debug("%s: exit before reset\n", __func__); | ||
495 | return 0; | ||
496 | } | ||
497 | |||
498 | /* SSI2 Reset */ | ||
499 | SSI2_SCR = 0; | ||
500 | |||
501 | SSI2_SFCSR = SSI_SFCSR_RFWM1(RXFIFO_WATERMARK) | | ||
502 | SSI_SFCSR_RFWM0(RXFIFO_WATERMARK) | | ||
503 | SSI_SFCSR_TFWM1(TXFIFO_WATERMARK) | | ||
504 | SSI_SFCSR_TFWM0(TXFIFO_WATERMARK); | ||
505 | } | ||
506 | |||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | int imx_ssi_hw_tx_params(struct snd_pcm_substream *substream, | ||
511 | struct snd_pcm_hw_params *params) | ||
512 | { | ||
513 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
514 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
515 | u32 stccr, stcr, sier; | ||
516 | |||
517 | pr_debug("%s\n", __func__); | ||
518 | |||
519 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
520 | stccr = SSI1_STCCR & ~SSI_STCCR_WL_MASK; | ||
521 | stcr = SSI1_STCR; | ||
522 | sier = SSI1_SIER; | ||
523 | } else { | ||
524 | stccr = SSI2_STCCR & ~SSI_STCCR_WL_MASK; | ||
525 | stcr = SSI2_STCR; | ||
526 | sier = SSI2_SIER; | ||
527 | } | ||
528 | |||
529 | /* DAI data (word) size */ | ||
530 | switch (params_format(params)) { | ||
531 | case SNDRV_PCM_FORMAT_S16_LE: | ||
532 | stccr |= SSI_STCCR_WL(16); | ||
533 | break; | ||
534 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
535 | stccr |= SSI_STCCR_WL(20); | ||
536 | break; | ||
537 | case SNDRV_PCM_FORMAT_S24_LE: | ||
538 | stccr |= SSI_STCCR_WL(24); | ||
539 | break; | ||
540 | } | ||
541 | |||
542 | /* enable interrupts */ | ||
543 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) | ||
544 | stcr |= SSI_STCR_TFEN0; | ||
545 | else | ||
546 | stcr |= SSI_STCR_TFEN1; | ||
547 | sier |= SSI_SIER_TDMAE; | ||
548 | |||
549 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
550 | SSI1_STCR = stcr; | ||
551 | SSI1_STCCR = stccr; | ||
552 | SSI1_SIER = sier; | ||
553 | } else { | ||
554 | SSI2_STCR = stcr; | ||
555 | SSI2_STCCR = stccr; | ||
556 | SSI2_SIER = sier; | ||
557 | } | ||
558 | |||
559 | return 0; | ||
560 | } | ||
561 | |||
562 | int imx_ssi_hw_rx_params(struct snd_pcm_substream *substream, | ||
563 | struct snd_pcm_hw_params *params) | ||
564 | { | ||
565 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
566 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
567 | u32 srccr, srcr, sier; | ||
568 | |||
569 | pr_debug("%s\n", __func__); | ||
570 | |||
571 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
572 | srccr = SSI1_SRCCR & ~SSI_SRCCR_WL_MASK; | ||
573 | srcr = SSI1_SRCR; | ||
574 | sier = SSI1_SIER; | ||
575 | } else { | ||
576 | srccr = SSI2_SRCCR & ~SSI_SRCCR_WL_MASK; | ||
577 | srcr = SSI2_SRCR; | ||
578 | sier = SSI2_SIER; | ||
579 | } | ||
580 | |||
581 | /* DAI data (word) size */ | ||
582 | switch (params_format(params)) { | ||
583 | case SNDRV_PCM_FORMAT_S16_LE: | ||
584 | srccr |= SSI_SRCCR_WL(16); | ||
585 | break; | ||
586 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
587 | srccr |= SSI_SRCCR_WL(20); | ||
588 | break; | ||
589 | case SNDRV_PCM_FORMAT_S24_LE: | ||
590 | srccr |= SSI_SRCCR_WL(24); | ||
591 | break; | ||
592 | } | ||
593 | |||
594 | /* enable interrupts */ | ||
595 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) | ||
596 | srcr |= SSI_SRCR_RFEN0; | ||
597 | else | ||
598 | srcr |= SSI_SRCR_RFEN1; | ||
599 | sier |= SSI_SIER_RDMAE; | ||
600 | |||
601 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
602 | SSI1_SRCR = srcr; | ||
603 | SSI1_SRCCR = srccr; | ||
604 | SSI1_SIER = sier; | ||
605 | } else { | ||
606 | SSI2_SRCR = srcr; | ||
607 | SSI2_SRCCR = srccr; | ||
608 | SSI2_SIER = sier; | ||
609 | } | ||
610 | |||
611 | return 0; | ||
612 | } | ||
613 | |||
614 | /* | ||
615 | * Should only be called when port is inactive (i.e. SSIEN = 0), | ||
616 | * although can be called multiple times by upper layers. | ||
617 | */ | ||
618 | int imx_ssi_hw_params(struct snd_pcm_substream *substream, | ||
619 | struct snd_pcm_hw_params *params, | ||
620 | struct snd_soc_dai *dai) | ||
621 | { | ||
622 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
623 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
624 | |||
625 | int ret; | ||
626 | |||
627 | /* cant change any parameters when SSI is running */ | ||
628 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
629 | if (SSI1_SCR & SSI_SCR_SSIEN) { | ||
630 | printk(KERN_WARNING "Warning ssi already enabled\n"); | ||
631 | return 0; | ||
632 | } | ||
633 | } else { | ||
634 | if (SSI2_SCR & SSI_SCR_SSIEN) { | ||
635 | printk(KERN_WARNING "Warning ssi already enabled\n"); | ||
636 | return 0; | ||
637 | } | ||
638 | } | ||
639 | |||
640 | /* | ||
641 | * Configure both tx and rx params with the same settings. This is | ||
642 | * really a harware restriction because SSI must be disabled until | ||
643 | * we can change those values. If there is an active audio stream in | ||
644 | * one direction, enabling the other direction with different | ||
645 | * settings would mean disturbing the running one. | ||
646 | */ | ||
647 | ret = imx_ssi_hw_tx_params(substream, params); | ||
648 | if (ret < 0) | ||
649 | return ret; | ||
650 | return imx_ssi_hw_rx_params(substream, params); | ||
651 | } | ||
652 | |||
653 | int imx_ssi_prepare(struct snd_pcm_substream *substream, | ||
654 | struct snd_soc_dai *dai) | ||
655 | { | ||
656 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
657 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
658 | int ret; | ||
659 | |||
660 | pr_debug("%s\n", __func__); | ||
661 | |||
662 | /* Enable clks here to follow SSI recommended init sequence */ | ||
663 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) { | ||
664 | ret = clk_enable(ssi_clk0); | ||
665 | if (ret < 0) | ||
666 | printk(KERN_ERR "Unable to enable ssi_clk0\n"); | ||
667 | } else { | ||
668 | ret = clk_enable(ssi_clk1); | ||
669 | if (ret < 0) | ||
670 | printk(KERN_ERR "Unable to enable ssi_clk1\n"); | ||
671 | } | ||
672 | |||
673 | return 0; | ||
674 | } | ||
675 | |||
676 | static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd, | ||
677 | struct snd_soc_dai *dai) | ||
678 | { | ||
679 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
680 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
681 | u32 scr; | ||
682 | |||
683 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) | ||
684 | scr = SSI1_SCR; | ||
685 | else | ||
686 | scr = SSI2_SCR; | ||
687 | |||
688 | switch (cmd) { | ||
689 | case SNDRV_PCM_TRIGGER_START: | ||
690 | case SNDRV_PCM_TRIGGER_RESUME: | ||
691 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
692 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
693 | scr |= SSI_SCR_TE | SSI_SCR_SSIEN; | ||
694 | else | ||
695 | scr |= SSI_SCR_RE | SSI_SCR_SSIEN; | ||
696 | break; | ||
697 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
698 | case SNDRV_PCM_TRIGGER_STOP: | ||
699 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
700 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
701 | scr &= ~SSI_SCR_TE; | ||
702 | else | ||
703 | scr &= ~SSI_SCR_RE; | ||
704 | break; | ||
705 | default: | ||
706 | return -EINVAL; | ||
707 | } | ||
708 | |||
709 | if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) | ||
710 | SSI1_SCR = scr; | ||
711 | else | ||
712 | SSI2_SCR = scr; | ||
713 | |||
714 | return 0; | ||
715 | } | ||
716 | |||
717 | static void imx_ssi_shutdown(struct snd_pcm_substream *substream, | ||
718 | struct snd_soc_dai *dai) | ||
719 | { | ||
720 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
721 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
722 | |||
723 | /* shutdown SSI if neither Tx or Rx is active */ | ||
724 | if (!cpu_dai->active) { | ||
725 | |||
726 | if (cpu_dai->id == IMX_DAI_SSI0 || | ||
727 | cpu_dai->id == IMX_DAI_SSI2) { | ||
728 | |||
729 | if (--ssi_active[SSI1_PORT] > 1) | ||
730 | return; | ||
731 | |||
732 | SSI1_SCR = 0; | ||
733 | clk_disable(ssi_clk0); | ||
734 | } else { | ||
735 | if (--ssi_active[SSI2_PORT]) | ||
736 | return; | ||
737 | SSI2_SCR = 0; | ||
738 | clk_disable(ssi_clk1); | ||
739 | } | ||
740 | } | ||
741 | } | ||
742 | |||
743 | #ifdef CONFIG_PM | ||
744 | static int imx_ssi_suspend(struct platform_device *dev, | ||
745 | struct snd_soc_dai *dai) | ||
746 | { | ||
747 | return 0; | ||
748 | } | ||
749 | |||
750 | static int imx_ssi_resume(struct platform_device *pdev, | ||
751 | struct snd_soc_dai *dai) | ||
752 | { | ||
753 | return 0; | ||
754 | } | ||
755 | |||
756 | #else | ||
757 | #define imx_ssi_suspend NULL | ||
758 | #define imx_ssi_resume NULL | ||
759 | #endif | ||
760 | |||
761 | #define IMX_SSI_RATES \ | ||
762 | (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \ | ||
763 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ | ||
764 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ | ||
765 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \ | ||
766 | SNDRV_PCM_RATE_96000) | ||
767 | |||
768 | #define IMX_SSI_BITS \ | ||
769 | (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ | ||
770 | SNDRV_PCM_FMTBIT_S24_LE) | ||
771 | |||
772 | static struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = { | ||
773 | .startup = imx_ssi_startup, | ||
774 | .shutdown = imx_ssi_shutdown, | ||
775 | .trigger = imx_ssi_trigger, | ||
776 | .prepare = imx_ssi_prepare, | ||
777 | .hw_params = imx_ssi_hw_params, | ||
778 | .set_sysclk = imx_ssi_set_dai_sysclk, | ||
779 | .set_clkdiv = imx_ssi_set_dai_clkdiv, | ||
780 | .set_fmt = imx_ssi_set_dai_fmt, | ||
781 | .set_tdm_slot = imx_ssi_set_dai_tdm_slot, | ||
782 | }; | ||
783 | |||
784 | struct snd_soc_dai imx_ssi_pcm_dai[] = { | ||
785 | { | ||
786 | .name = "imx-i2s-1-0", | ||
787 | .id = IMX_DAI_SSI0, | ||
788 | .suspend = imx_ssi_suspend, | ||
789 | .resume = imx_ssi_resume, | ||
790 | .playback = { | ||
791 | .channels_min = 1, | ||
792 | .channels_max = 2, | ||
793 | .formats = IMX_SSI_BITS, | ||
794 | .rates = IMX_SSI_RATES,}, | ||
795 | .capture = { | ||
796 | .channels_min = 1, | ||
797 | .channels_max = 2, | ||
798 | .formats = IMX_SSI_BITS, | ||
799 | .rates = IMX_SSI_RATES,}, | ||
800 | .ops = &imx_ssi_pcm_dai_ops, | ||
801 | }, | ||
802 | { | ||
803 | .name = "imx-i2s-2-0", | ||
804 | .id = IMX_DAI_SSI1, | ||
805 | .playback = { | ||
806 | .channels_min = 1, | ||
807 | .channels_max = 2, | ||
808 | .formats = IMX_SSI_BITS, | ||
809 | .rates = IMX_SSI_RATES,}, | ||
810 | .capture = { | ||
811 | .channels_min = 1, | ||
812 | .channels_max = 2, | ||
813 | .formats = IMX_SSI_BITS, | ||
814 | .rates = IMX_SSI_RATES,}, | ||
815 | .ops = &imx_ssi_pcm_dai_ops, | ||
816 | }, | ||
817 | { | ||
818 | .name = "imx-i2s-1-1", | ||
819 | .id = IMX_DAI_SSI2, | ||
820 | .suspend = imx_ssi_suspend, | ||
821 | .resume = imx_ssi_resume, | ||
822 | .playback = { | ||
823 | .channels_min = 1, | ||
824 | .channels_max = 2, | ||
825 | .formats = IMX_SSI_BITS, | ||
826 | .rates = IMX_SSI_RATES,}, | ||
827 | .capture = { | ||
828 | .channels_min = 1, | ||
829 | .channels_max = 2, | ||
830 | .formats = IMX_SSI_BITS, | ||
831 | .rates = IMX_SSI_RATES,}, | ||
832 | .ops = &imx_ssi_pcm_dai_ops, | ||
833 | }, | ||
834 | { | ||
835 | .name = "imx-i2s-2-1", | ||
836 | .id = IMX_DAI_SSI3, | ||
837 | .playback = { | ||
838 | .channels_min = 1, | ||
839 | .channels_max = 2, | ||
840 | .formats = IMX_SSI_BITS, | ||
841 | .rates = IMX_SSI_RATES,}, | ||
842 | .capture = { | ||
843 | .channels_min = 1, | ||
844 | .channels_max = 2, | ||
845 | .formats = IMX_SSI_BITS, | ||
846 | .rates = IMX_SSI_RATES,}, | ||
847 | .ops = &imx_ssi_pcm_dai_ops, | ||
848 | }, | ||
849 | }; | ||
850 | EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai); | ||
851 | |||
852 | static int __init imx_ssi_init(void) | ||
853 | { | ||
854 | return snd_soc_register_dais(imx_ssi_pcm_dai, | ||
855 | ARRAY_SIZE(imx_ssi_pcm_dai)); | ||
856 | } | ||
857 | |||
858 | static void __exit imx_ssi_exit(void) | ||
859 | { | ||
860 | snd_soc_unregister_dais(imx_ssi_pcm_dai, | ||
861 | ARRAY_SIZE(imx_ssi_pcm_dai)); | ||
862 | } | ||
863 | |||
864 | module_init(imx_ssi_init); | ||
865 | module_exit(imx_ssi_exit); | ||
866 | MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com"); | ||
867 | MODULE_DESCRIPTION("i.MX ASoC I2S driver"); | ||
868 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/imx/mxc-ssi.h b/sound/soc/imx/mxc-ssi.h new file mode 100644 index 000000000000..12bbdc9c7ecd --- /dev/null +++ b/sound/soc/imx/mxc-ssi.h | |||
@@ -0,0 +1,238 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or modify | ||
3 | * it under the terms of the GNU General Public License version 2 as | ||
4 | * published by the Free Software Foundation. | ||
5 | */ | ||
6 | |||
7 | #ifndef _IMX_SSI_H | ||
8 | #define _IMX_SSI_H | ||
9 | |||
10 | #include <mach/hardware.h> | ||
11 | |||
12 | /* SSI regs definition - MOVE to /arch/arm/plat-mxc/include/mach/ when stable */ | ||
13 | #define SSI1_IO_BASE_ADDR IO_ADDRESS(SSI1_BASE_ADDR) | ||
14 | #define SSI2_IO_BASE_ADDR IO_ADDRESS(SSI2_BASE_ADDR) | ||
15 | |||
16 | #define STX0 0x00 | ||
17 | #define STX1 0x04 | ||
18 | #define SRX0 0x08 | ||
19 | #define SRX1 0x0c | ||
20 | #define SCR 0x10 | ||
21 | #define SISR 0x14 | ||
22 | #define SIER 0x18 | ||
23 | #define STCR 0x1c | ||
24 | #define SRCR 0x20 | ||
25 | #define STCCR 0x24 | ||
26 | #define SRCCR 0x28 | ||
27 | #define SFCSR 0x2c | ||
28 | #define STR 0x30 | ||
29 | #define SOR 0x34 | ||
30 | #define SACNT 0x38 | ||
31 | #define SACADD 0x3c | ||
32 | #define SACDAT 0x40 | ||
33 | #define SATAG 0x44 | ||
34 | #define STMSK 0x48 | ||
35 | #define SRMSK 0x4c | ||
36 | |||
37 | #define SSI1_STX0 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STX0))) | ||
38 | #define SSI1_STX1 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STX1))) | ||
39 | #define SSI1_SRX0 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRX0))) | ||
40 | #define SSI1_SRX1 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRX1))) | ||
41 | #define SSI1_SCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SCR))) | ||
42 | #define SSI1_SISR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SISR))) | ||
43 | #define SSI1_SIER (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SIER))) | ||
44 | #define SSI1_STCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STCR))) | ||
45 | #define SSI1_SRCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRCR))) | ||
46 | #define SSI1_STCCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STCCR))) | ||
47 | #define SSI1_SRCCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRCCR))) | ||
48 | #define SSI1_SFCSR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SFCSR))) | ||
49 | #define SSI1_STR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STR))) | ||
50 | #define SSI1_SOR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SOR))) | ||
51 | #define SSI1_SACNT (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACNT))) | ||
52 | #define SSI1_SACADD (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACADD))) | ||
53 | #define SSI1_SACDAT (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACDAT))) | ||
54 | #define SSI1_SATAG (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SATAG))) | ||
55 | #define SSI1_STMSK (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STMSK))) | ||
56 | #define SSI1_SRMSK (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRMSK))) | ||
57 | |||
58 | |||
59 | #define SSI2_STX0 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STX0))) | ||
60 | #define SSI2_STX1 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STX1))) | ||
61 | #define SSI2_SRX0 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRX0))) | ||
62 | #define SSI2_SRX1 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRX1))) | ||
63 | #define SSI2_SCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SCR))) | ||
64 | #define SSI2_SISR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SISR))) | ||
65 | #define SSI2_SIER (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SIER))) | ||
66 | #define SSI2_STCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STCR))) | ||
67 | #define SSI2_SRCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRCR))) | ||
68 | #define SSI2_STCCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STCCR))) | ||
69 | #define SSI2_SRCCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRCCR))) | ||
70 | #define SSI2_SFCSR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SFCSR))) | ||
71 | #define SSI2_STR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STR))) | ||
72 | #define SSI2_SOR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SOR))) | ||
73 | #define SSI2_SACNT (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACNT))) | ||
74 | #define SSI2_SACADD (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACADD))) | ||
75 | #define SSI2_SACDAT (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACDAT))) | ||
76 | #define SSI2_SATAG (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SATAG))) | ||
77 | #define SSI2_STMSK (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STMSK))) | ||
78 | #define SSI2_SRMSK (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRMSK))) | ||
79 | |||
80 | #define SSI_SCR_CLK_IST (1 << 9) | ||
81 | #define SSI_SCR_TCH_EN (1 << 8) | ||
82 | #define SSI_SCR_SYS_CLK_EN (1 << 7) | ||
83 | #define SSI_SCR_I2S_MODE_NORM (0 << 5) | ||
84 | #define SSI_SCR_I2S_MODE_MSTR (1 << 5) | ||
85 | #define SSI_SCR_I2S_MODE_SLAVE (2 << 5) | ||
86 | #define SSI_SCR_SYN (1 << 4) | ||
87 | #define SSI_SCR_NET (1 << 3) | ||
88 | #define SSI_SCR_RE (1 << 2) | ||
89 | #define SSI_SCR_TE (1 << 1) | ||
90 | #define SSI_SCR_SSIEN (1 << 0) | ||
91 | |||
92 | #define SSI_SISR_CMDAU (1 << 18) | ||
93 | #define SSI_SISR_CMDDU (1 << 17) | ||
94 | #define SSI_SISR_RXT (1 << 16) | ||
95 | #define SSI_SISR_RDR1 (1 << 15) | ||
96 | #define SSI_SISR_RDR0 (1 << 14) | ||
97 | #define SSI_SISR_TDE1 (1 << 13) | ||
98 | #define SSI_SISR_TDE0 (1 << 12) | ||
99 | #define SSI_SISR_ROE1 (1 << 11) | ||
100 | #define SSI_SISR_ROE0 (1 << 10) | ||
101 | #define SSI_SISR_TUE1 (1 << 9) | ||
102 | #define SSI_SISR_TUE0 (1 << 8) | ||
103 | #define SSI_SISR_TFS (1 << 7) | ||
104 | #define SSI_SISR_RFS (1 << 6) | ||
105 | #define SSI_SISR_TLS (1 << 5) | ||
106 | #define SSI_SISR_RLS (1 << 4) | ||
107 | #define SSI_SISR_RFF1 (1 << 3) | ||
108 | #define SSI_SISR_RFF0 (1 << 2) | ||
109 | #define SSI_SISR_TFE1 (1 << 1) | ||
110 | #define SSI_SISR_TFE0 (1 << 0) | ||
111 | |||
112 | #define SSI_SIER_RDMAE (1 << 22) | ||
113 | #define SSI_SIER_RIE (1 << 21) | ||
114 | #define SSI_SIER_TDMAE (1 << 20) | ||
115 | #define SSI_SIER_TIE (1 << 19) | ||
116 | #define SSI_SIER_CMDAU_EN (1 << 18) | ||
117 | #define SSI_SIER_CMDDU_EN (1 << 17) | ||
118 | #define SSI_SIER_RXT_EN (1 << 16) | ||
119 | #define SSI_SIER_RDR1_EN (1 << 15) | ||
120 | #define SSI_SIER_RDR0_EN (1 << 14) | ||
121 | #define SSI_SIER_TDE1_EN (1 << 13) | ||
122 | #define SSI_SIER_TDE0_EN (1 << 12) | ||
123 | #define SSI_SIER_ROE1_EN (1 << 11) | ||
124 | #define SSI_SIER_ROE0_EN (1 << 10) | ||
125 | #define SSI_SIER_TUE1_EN (1 << 9) | ||
126 | #define SSI_SIER_TUE0_EN (1 << 8) | ||
127 | #define SSI_SIER_TFS_EN (1 << 7) | ||
128 | #define SSI_SIER_RFS_EN (1 << 6) | ||
129 | #define SSI_SIER_TLS_EN (1 << 5) | ||
130 | #define SSI_SIER_RLS_EN (1 << 4) | ||
131 | #define SSI_SIER_RFF1_EN (1 << 3) | ||
132 | #define SSI_SIER_RFF0_EN (1 << 2) | ||
133 | #define SSI_SIER_TFE1_EN (1 << 1) | ||
134 | #define SSI_SIER_TFE0_EN (1 << 0) | ||
135 | |||
136 | #define SSI_STCR_TXBIT0 (1 << 9) | ||
137 | #define SSI_STCR_TFEN1 (1 << 8) | ||
138 | #define SSI_STCR_TFEN0 (1 << 7) | ||
139 | #define SSI_STCR_TFDIR (1 << 6) | ||
140 | #define SSI_STCR_TXDIR (1 << 5) | ||
141 | #define SSI_STCR_TSHFD (1 << 4) | ||
142 | #define SSI_STCR_TSCKP (1 << 3) | ||
143 | #define SSI_STCR_TFSI (1 << 2) | ||
144 | #define SSI_STCR_TFSL (1 << 1) | ||
145 | #define SSI_STCR_TEFS (1 << 0) | ||
146 | |||
147 | #define SSI_SRCR_RXBIT0 (1 << 9) | ||
148 | #define SSI_SRCR_RFEN1 (1 << 8) | ||
149 | #define SSI_SRCR_RFEN0 (1 << 7) | ||
150 | #define SSI_SRCR_RFDIR (1 << 6) | ||
151 | #define SSI_SRCR_RXDIR (1 << 5) | ||
152 | #define SSI_SRCR_RSHFD (1 << 4) | ||
153 | #define SSI_SRCR_RSCKP (1 << 3) | ||
154 | #define SSI_SRCR_RFSI (1 << 2) | ||
155 | #define SSI_SRCR_RFSL (1 << 1) | ||
156 | #define SSI_SRCR_REFS (1 << 0) | ||
157 | |||
158 | #define SSI_STCCR_DIV2 (1 << 18) | ||
159 | #define SSI_STCCR_PSR (1 << 15) | ||
160 | #define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13) | ||
161 | #define SSI_STCCR_DC(x) (((x) & 0x1f) << 8) | ||
162 | #define SSI_STCCR_PM(x) (((x) & 0xff) << 0) | ||
163 | #define SSI_STCCR_WL_MASK (0xf << 13) | ||
164 | #define SSI_STCCR_DC_MASK (0x1f << 8) | ||
165 | #define SSI_STCCR_PM_MASK (0xff << 0) | ||
166 | |||
167 | #define SSI_SRCCR_DIV2 (1 << 18) | ||
168 | #define SSI_SRCCR_PSR (1 << 15) | ||
169 | #define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13) | ||
170 | #define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8) | ||
171 | #define SSI_SRCCR_PM(x) (((x) & 0xff) << 0) | ||
172 | #define SSI_SRCCR_WL_MASK (0xf << 13) | ||
173 | #define SSI_SRCCR_DC_MASK (0x1f << 8) | ||
174 | #define SSI_SRCCR_PM_MASK (0xff << 0) | ||
175 | |||
176 | |||
177 | #define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28) | ||
178 | #define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24) | ||
179 | #define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20) | ||
180 | #define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16) | ||
181 | #define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12) | ||
182 | #define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8) | ||
183 | #define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4) | ||
184 | #define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0) | ||
185 | |||
186 | #define SSI_STR_TEST (1 << 15) | ||
187 | #define SSI_STR_RCK2TCK (1 << 14) | ||
188 | #define SSI_STR_RFS2TFS (1 << 13) | ||
189 | #define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8) | ||
190 | #define SSI_STR_TXD2RXD (1 << 7) | ||
191 | #define SSI_STR_TCK2RCK (1 << 6) | ||
192 | #define SSI_STR_TFS2RFS (1 << 5) | ||
193 | #define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0) | ||
194 | |||
195 | #define SSI_SOR_CLKOFF (1 << 6) | ||
196 | #define SSI_SOR_RX_CLR (1 << 5) | ||
197 | #define SSI_SOR_TX_CLR (1 << 4) | ||
198 | #define SSI_SOR_INIT (1 << 3) | ||
199 | #define SSI_SOR_WAIT(x) (((x) & 0x3) << 1) | ||
200 | #define SSI_SOR_SYNRST (1 << 0) | ||
201 | |||
202 | #define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5) | ||
203 | #define SSI_SACNT_WR (x << 4) | ||
204 | #define SSI_SACNT_RD (x << 3) | ||
205 | #define SSI_SACNT_TIF (x << 2) | ||
206 | #define SSI_SACNT_FV (x << 1) | ||
207 | #define SSI_SACNT_AC97EN (x << 0) | ||
208 | |||
209 | /* Watermarks for FIFO's */ | ||
210 | #define TXFIFO_WATERMARK 0x4 | ||
211 | #define RXFIFO_WATERMARK 0x4 | ||
212 | |||
213 | /* i.MX DAI SSP ID's */ | ||
214 | #define IMX_DAI_SSI0 0 /* SSI1 FIFO 0 */ | ||
215 | #define IMX_DAI_SSI1 1 /* SSI1 FIFO 1 */ | ||
216 | #define IMX_DAI_SSI2 2 /* SSI2 FIFO 0 */ | ||
217 | #define IMX_DAI_SSI3 3 /* SSI2 FIFO 1 */ | ||
218 | |||
219 | /* SSI clock sources */ | ||
220 | #define IMX_SSP_SYS_CLK 0 | ||
221 | |||
222 | /* SSI audio dividers */ | ||
223 | #define IMX_SSI_TX_DIV_2 0 | ||
224 | #define IMX_SSI_TX_DIV_PSR 1 | ||
225 | #define IMX_SSI_TX_DIV_PM 2 | ||
226 | #define IMX_SSI_RX_DIV_2 3 | ||
227 | #define IMX_SSI_RX_DIV_PSR 4 | ||
228 | #define IMX_SSI_RX_DIV_PM 5 | ||
229 | |||
230 | |||
231 | /* SSI Div 2 */ | ||
232 | #define IMX_SSI_DIV_2_OFF (~SSI_STCCR_DIV2) | ||
233 | #define IMX_SSI_DIV_2_ON SSI_STCCR_DIV2 | ||
234 | |||
235 | extern struct snd_soc_dai imx_ssi_pcm_dai[4]; | ||
236 | extern int get_ssi_clk(int ssi, struct device *dev); | ||
237 | extern void put_ssi_clk(int ssi); | ||
238 | #endif | ||