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