Commit | Line | Data |
---|---|---|
3e7cc3d3 LG |
1 | /* |
2 | * pxa2xx-i2s.c -- ALSA Soc Audio Layer | |
3 | * | |
4 | * Copyright 2005 Wolfson Microelectronics PLC. | |
5 | * Author: Liam Girdwood | |
d331124d | 6 | * lrg@slimlogic.co.uk |
3e7cc3d3 LG |
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. | |
3e7cc3d3 LG |
12 | */ |
13 | ||
14 | #include <linux/init.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/device.h> | |
17 | #include <linux/delay.h> | |
5a2cc50f | 18 | #include <linux/clk.h> |
6e5ea701 | 19 | #include <linux/platform_device.h> |
3e7cc3d3 LG |
20 | #include <sound/core.h> |
21 | #include <sound/pcm.h> | |
22 | #include <sound/initval.h> | |
23 | #include <sound/soc.h> | |
a6d77317 | 24 | #include <sound/pxa2xx-lib.h> |
3e7cc3d3 | 25 | |
a09e64fb RK |
26 | #include <mach/hardware.h> |
27 | #include <mach/pxa-regs.h> | |
28 | #include <mach/pxa2xx-gpio.h> | |
29 | #include <mach/audio.h> | |
3e7cc3d3 LG |
30 | |
31 | #include "pxa2xx-pcm.h" | |
eaff2ae7 | 32 | #include "pxa2xx-i2s.h" |
3e7cc3d3 | 33 | |
a6d77317 DB |
34 | struct pxa2xx_gpio { |
35 | u32 sys; | |
36 | u32 rx; | |
37 | u32 tx; | |
38 | u32 clk; | |
39 | u32 frm; | |
40 | }; | |
41 | ||
52358ba3 EM |
42 | /* |
43 | * I2S Controller Register and Bit Definitions | |
44 | */ | |
45 | #define SACR0 __REG(0x40400000) /* Global Control Register */ | |
46 | #define SACR1 __REG(0x40400004) /* Serial Audio I 2 S/MSB-Justified Control Register */ | |
47 | #define SASR0 __REG(0x4040000C) /* Serial Audio I 2 S/MSB-Justified Interface and FIFO Status Register */ | |
48 | #define SAIMR __REG(0x40400014) /* Serial Audio Interrupt Mask Register */ | |
49 | #define SAICR __REG(0x40400018) /* Serial Audio Interrupt Clear Register */ | |
50 | #define SADIV __REG(0x40400060) /* Audio Clock Divider Register. */ | |
51 | #define SADR __REG(0x40400080) /* Serial Audio Data Register (TX and RX FIFO access Register). */ | |
52 | ||
53 | #define SACR0_RFTH(x) ((x) << 12) /* Rx FIFO Interrupt or DMA Trigger Threshold */ | |
54 | #define SACR0_TFTH(x) ((x) << 8) /* Tx FIFO Interrupt or DMA Trigger Threshold */ | |
55 | #define SACR0_STRF (1 << 5) /* FIFO Select for EFWR Special Function */ | |
56 | #define SACR0_EFWR (1 << 4) /* Enable EFWR Function */ | |
57 | #define SACR0_RST (1 << 3) /* FIFO, i2s Register Reset */ | |
58 | #define SACR0_BCKD (1 << 2) /* Bit Clock Direction */ | |
59 | #define SACR0_ENB (1 << 0) /* Enable I2S Link */ | |
60 | #define SACR1_ENLBF (1 << 5) /* Enable Loopback */ | |
61 | #define SACR1_DRPL (1 << 4) /* Disable Replaying Function */ | |
62 | #define SACR1_DREC (1 << 3) /* Disable Recording Function */ | |
63 | #define SACR1_AMSL (1 << 0) /* Specify Alternate Mode */ | |
64 | ||
65 | #define SASR0_I2SOFF (1 << 7) /* Controller Status */ | |
66 | #define SASR0_ROR (1 << 6) /* Rx FIFO Overrun */ | |
67 | #define SASR0_TUR (1 << 5) /* Tx FIFO Underrun */ | |
68 | #define SASR0_RFS (1 << 4) /* Rx FIFO Service Request */ | |
69 | #define SASR0_TFS (1 << 3) /* Tx FIFO Service Request */ | |
70 | #define SASR0_BSY (1 << 2) /* I2S Busy */ | |
71 | #define SASR0_RNE (1 << 1) /* Rx FIFO Not Empty */ | |
72 | #define SASR0_TNF (1 << 0) /* Tx FIFO Not Empty */ | |
73 | ||
74 | #define SAICR_ROR (1 << 6) /* Clear Rx FIFO Overrun Interrupt */ | |
75 | #define SAICR_TUR (1 << 5) /* Clear Tx FIFO Underrun Interrupt */ | |
76 | ||
77 | #define SAIMR_ROR (1 << 6) /* Enable Rx FIFO Overrun Condition Interrupt */ | |
78 | #define SAIMR_TUR (1 << 5) /* Enable Tx FIFO Underrun Condition Interrupt */ | |
79 | #define SAIMR_RFS (1 << 4) /* Enable Rx FIFO Service Interrupt */ | |
80 | #define SAIMR_TFS (1 << 3) /* Enable Tx FIFO Service Interrupt */ | |
a6d77317 | 81 | |
3e7cc3d3 LG |
82 | struct pxa_i2s_port { |
83 | u32 sadiv; | |
84 | u32 sacr0; | |
85 | u32 sacr1; | |
86 | u32 saimr; | |
87 | int master; | |
eaff2ae7 | 88 | u32 fmt; |
3e7cc3d3 LG |
89 | }; |
90 | static struct pxa_i2s_port pxa_i2s; | |
5a2cc50f | 91 | static struct clk *clk_i2s; |
3e7cc3d3 | 92 | |
3e7cc3d3 LG |
93 | static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = { |
94 | .name = "I2S PCM Stereo out", | |
95 | .dev_addr = __PREG(SADR), | |
87f3dd77 | 96 | .drcmr = &DRCMR(3), |
3e7cc3d3 LG |
97 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | |
98 | DCMD_BURST32 | DCMD_WIDTH4, | |
99 | }; | |
100 | ||
101 | static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = { | |
102 | .name = "I2S PCM Stereo in", | |
103 | .dev_addr = __PREG(SADR), | |
87f3dd77 | 104 | .drcmr = &DRCMR(2), |
3e7cc3d3 LG |
105 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | |
106 | DCMD_BURST32 | DCMD_WIDTH4, | |
107 | }; | |
108 | ||
109 | static struct pxa2xx_gpio gpio_bus[] = { | |
110 | { /* I2S SoC Slave */ | |
111 | .rx = GPIO29_SDATA_IN_I2S_MD, | |
112 | .tx = GPIO30_SDATA_OUT_I2S_MD, | |
113 | .clk = GPIO28_BITCLK_IN_I2S_MD, | |
114 | .frm = GPIO31_SYNC_I2S_MD, | |
115 | }, | |
116 | { /* I2S SoC Master */ | |
3e7cc3d3 LG |
117 | .rx = GPIO29_SDATA_IN_I2S_MD, |
118 | .tx = GPIO30_SDATA_OUT_I2S_MD, | |
119 | .clk = GPIO28_BITCLK_OUT_I2S_MD, | |
120 | .frm = GPIO31_SYNC_I2S_MD, | |
121 | }, | |
122 | }; | |
123 | ||
dee89c4d MB |
124 | static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream, |
125 | struct snd_soc_dai *dai) | |
3e7cc3d3 LG |
126 | { |
127 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
917f93ac | 128 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
3e7cc3d3 | 129 | |
5a2cc50f | 130 | if (IS_ERR(clk_i2s)) |
131 | return PTR_ERR(clk_i2s); | |
132 | ||
eaff2ae7 | 133 | if (!cpu_dai->active) { |
3e7cc3d3 LG |
134 | SACR0 |= SACR0_RST; |
135 | SACR0 = 0; | |
136 | } | |
137 | ||
138 | return 0; | |
139 | } | |
140 | ||
141 | /* wait for I2S controller to be ready */ | |
142 | static int pxa_i2s_wait(void) | |
143 | { | |
144 | int i; | |
145 | ||
146 | /* flush the Rx FIFO */ | |
147 | for(i = 0; i < 16; i++) | |
148 | SADR; | |
149 | return 0; | |
150 | } | |
151 | ||
917f93ac | 152 | static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, |
eaff2ae7 | 153 | unsigned int fmt) |
3e7cc3d3 | 154 | { |
eaff2ae7 PZ |
155 | /* interface format */ |
156 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | |
157 | case SND_SOC_DAIFMT_I2S: | |
158 | pxa_i2s.fmt = 0; | |
159 | break; | |
160 | case SND_SOC_DAIFMT_LEFT_J: | |
161 | pxa_i2s.fmt = SACR1_AMSL; | |
162 | break; | |
163 | } | |
3e7cc3d3 | 164 | |
eaff2ae7 PZ |
165 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
166 | case SND_SOC_DAIFMT_CBS_CFS: | |
3e7cc3d3 | 167 | pxa_i2s.master = 1; |
eaff2ae7 PZ |
168 | break; |
169 | case SND_SOC_DAIFMT_CBM_CFS: | |
170 | pxa_i2s.master = 0; | |
171 | break; | |
172 | default: | |
173 | break; | |
174 | } | |
175 | return 0; | |
176 | } | |
3e7cc3d3 | 177 | |
917f93ac | 178 | static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai, |
eaff2ae7 PZ |
179 | int clk_id, unsigned int freq, int dir) |
180 | { | |
181 | if (clk_id != PXA2XX_I2S_SYSCLK) | |
182 | return -ENODEV; | |
183 | ||
184 | if (pxa_i2s.master && dir == SND_SOC_CLOCK_OUT) | |
3e7cc3d3 LG |
185 | pxa_gpio_mode(gpio_bus[pxa_i2s.master].sys); |
186 | ||
eaff2ae7 PZ |
187 | return 0; |
188 | } | |
189 | ||
190 | static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, | |
dee89c4d MB |
191 | struct snd_pcm_hw_params *params, |
192 | struct snd_soc_dai *dai) | |
eaff2ae7 PZ |
193 | { |
194 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
917f93ac | 195 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
eaff2ae7 | 196 | |
3e7cc3d3 LG |
197 | pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx); |
198 | pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx); | |
199 | pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm); | |
200 | pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk); | |
6e5ea701 | 201 | BUG_ON(IS_ERR(clk_i2s)); |
5a2cc50f | 202 | clk_enable(clk_i2s); |
3e7cc3d3 LG |
203 | pxa_i2s_wait(); |
204 | ||
205 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
eaff2ae7 | 206 | cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_out; |
3e7cc3d3 | 207 | else |
eaff2ae7 | 208 | cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_in; |
3e7cc3d3 LG |
209 | |
210 | /* is port used by another stream */ | |
211 | if (!(SACR0 & SACR0_ENB)) { | |
212 | ||
213 | SACR0 = 0; | |
214 | SACR1 = 0; | |
215 | if (pxa_i2s.master) | |
216 | SACR0 |= SACR0_BCKD; | |
217 | ||
218 | SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1); | |
eaff2ae7 | 219 | SACR1 |= pxa_i2s.fmt; |
3e7cc3d3 LG |
220 | } |
221 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
222 | SAIMR |= SAIMR_TFS; | |
223 | else | |
224 | SAIMR |= SAIMR_RFS; | |
225 | ||
eaff2ae7 PZ |
226 | switch (params_rate(params)) { |
227 | case 8000: | |
228 | SADIV = 0x48; | |
229 | break; | |
230 | case 11025: | |
231 | SADIV = 0x34; | |
232 | break; | |
233 | case 16000: | |
234 | SADIV = 0x24; | |
235 | break; | |
236 | case 22050: | |
237 | SADIV = 0x1a; | |
238 | break; | |
239 | case 44100: | |
240 | SADIV = 0xd; | |
241 | break; | |
242 | case 48000: | |
243 | SADIV = 0xc; | |
244 | break; | |
245 | case 96000: /* not in manual and possibly slightly inaccurate */ | |
246 | SADIV = 0x6; | |
247 | break; | |
248 | } | |
249 | ||
3e7cc3d3 LG |
250 | return 0; |
251 | } | |
252 | ||
dee89c4d MB |
253 | static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, |
254 | struct snd_soc_dai *dai) | |
3e7cc3d3 LG |
255 | { |
256 | int ret = 0; | |
257 | ||
258 | switch (cmd) { | |
259 | case SNDRV_PCM_TRIGGER_START: | |
260 | SACR0 |= SACR0_ENB; | |
261 | break; | |
262 | case SNDRV_PCM_TRIGGER_RESUME: | |
263 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
264 | case SNDRV_PCM_TRIGGER_STOP: | |
265 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
266 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
267 | break; | |
268 | default: | |
269 | ret = -EINVAL; | |
270 | } | |
271 | ||
272 | return ret; | |
273 | } | |
274 | ||
dee89c4d MB |
275 | static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream, |
276 | struct snd_soc_dai *dai) | |
3e7cc3d3 LG |
277 | { |
278 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
279 | SACR1 |= SACR1_DRPL; | |
280 | SAIMR &= ~SAIMR_TFS; | |
281 | } else { | |
282 | SACR1 |= SACR1_DREC; | |
283 | SAIMR &= ~SAIMR_RFS; | |
284 | } | |
285 | ||
286 | if (SACR1 & (SACR1_DREC | SACR1_DRPL)) { | |
287 | SACR0 &= ~SACR0_ENB; | |
288 | pxa_i2s_wait(); | |
5a2cc50f | 289 | clk_disable(clk_i2s); |
3e7cc3d3 | 290 | } |
5a2cc50f | 291 | |
292 | clk_put(clk_i2s); | |
3e7cc3d3 LG |
293 | } |
294 | ||
295 | #ifdef CONFIG_PM | |
296 | static int pxa2xx_i2s_suspend(struct platform_device *dev, | |
917f93ac | 297 | struct snd_soc_dai *dai) |
3e7cc3d3 LG |
298 | { |
299 | if (!dai->active) | |
300 | return 0; | |
301 | ||
302 | /* store registers */ | |
303 | pxa_i2s.sacr0 = SACR0; | |
304 | pxa_i2s.sacr1 = SACR1; | |
305 | pxa_i2s.saimr = SAIMR; | |
306 | pxa_i2s.sadiv = SADIV; | |
307 | ||
308 | /* deactivate link */ | |
309 | SACR0 &= ~SACR0_ENB; | |
310 | pxa_i2s_wait(); | |
311 | return 0; | |
312 | } | |
313 | ||
314 | static int pxa2xx_i2s_resume(struct platform_device *pdev, | |
917f93ac | 315 | struct snd_soc_dai *dai) |
3e7cc3d3 LG |
316 | { |
317 | if (!dai->active) | |
318 | return 0; | |
319 | ||
320 | pxa_i2s_wait(); | |
321 | ||
322 | SACR0 = pxa_i2s.sacr0 &= ~SACR0_ENB; | |
323 | SACR1 = pxa_i2s.sacr1; | |
324 | SAIMR = pxa_i2s.saimr; | |
325 | SADIV = pxa_i2s.sadiv; | |
326 | SACR0 |= SACR0_ENB; | |
327 | ||
328 | return 0; | |
329 | } | |
330 | ||
331 | #else | |
332 | #define pxa2xx_i2s_suspend NULL | |
333 | #define pxa2xx_i2s_resume NULL | |
334 | #endif | |
335 | ||
eaff2ae7 PZ |
336 | #define PXA2XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ |
337 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ | |
338 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) | |
3e7cc3d3 | 339 | |
917f93ac | 340 | struct snd_soc_dai pxa_i2s_dai = { |
3e7cc3d3 LG |
341 | .name = "pxa2xx-i2s", |
342 | .id = 0, | |
3e7cc3d3 LG |
343 | .suspend = pxa2xx_i2s_suspend, |
344 | .resume = pxa2xx_i2s_resume, | |
3e7cc3d3 LG |
345 | .playback = { |
346 | .channels_min = 2, | |
eaff2ae7 PZ |
347 | .channels_max = 2, |
348 | .rates = PXA2XX_I2S_RATES, | |
349 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | |
3e7cc3d3 LG |
350 | .capture = { |
351 | .channels_min = 2, | |
eaff2ae7 PZ |
352 | .channels_max = 2, |
353 | .rates = PXA2XX_I2S_RATES, | |
354 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | |
3e7cc3d3 LG |
355 | .ops = { |
356 | .startup = pxa2xx_i2s_startup, | |
357 | .shutdown = pxa2xx_i2s_shutdown, | |
358 | .trigger = pxa2xx_i2s_trigger, | |
dee89c4d | 359 | .hw_params = pxa2xx_i2s_hw_params, |
eaff2ae7 PZ |
360 | .set_fmt = pxa2xx_i2s_set_dai_fmt, |
361 | .set_sysclk = pxa2xx_i2s_set_dai_sysclk, | |
362 | }, | |
3e7cc3d3 LG |
363 | }; |
364 | ||
365 | EXPORT_SYMBOL_GPL(pxa_i2s_dai); | |
366 | ||
6e5ea701 DB |
367 | static int pxa2xx_i2s_probe(struct platform_device *dev) |
368 | { | |
369 | clk_i2s = clk_get(&dev->dev, "I2SCLK"); | |
370 | return IS_ERR(clk_i2s) ? PTR_ERR(clk_i2s) : 0; | |
371 | } | |
372 | ||
373 | static int __devexit pxa2xx_i2s_remove(struct platform_device *dev) | |
374 | { | |
375 | clk_put(clk_i2s); | |
376 | clk_i2s = ERR_PTR(-ENOENT); | |
377 | return 0; | |
378 | } | |
379 | ||
380 | static struct platform_driver pxa2xx_i2s_driver = { | |
381 | .probe = pxa2xx_i2s_probe, | |
382 | .remove = __devexit_p(pxa2xx_i2s_remove), | |
383 | ||
384 | .driver = { | |
385 | .name = "pxa2xx-i2s", | |
386 | .owner = THIS_MODULE, | |
387 | }, | |
388 | }; | |
389 | ||
390 | static int __init pxa2xx_i2s_init(void) | |
391 | { | |
081b355d DB |
392 | if (cpu_is_pxa27x()) |
393 | gpio_bus[1].sys = GPIO113_I2S_SYSCLK_MD; | |
394 | else | |
395 | gpio_bus[1].sys = GPIO32_SYSCLK_I2S_MD; | |
396 | ||
6e5ea701 DB |
397 | clk_i2s = ERR_PTR(-ENOENT); |
398 | return platform_driver_register(&pxa2xx_i2s_driver); | |
399 | } | |
400 | ||
401 | static void __exit pxa2xx_i2s_exit(void) | |
402 | { | |
403 | platform_driver_unregister(&pxa2xx_i2s_driver); | |
404 | } | |
405 | ||
406 | module_init(pxa2xx_i2s_init); | |
407 | module_exit(pxa2xx_i2s_exit); | |
408 | ||
3e7cc3d3 | 409 | /* Module information */ |
d331124d | 410 | MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); |
3e7cc3d3 LG |
411 | MODULE_DESCRIPTION("pxa2xx I2S SoC Interface"); |
412 | MODULE_LICENSE("GPL"); |