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