1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // ALSA SoC Audio Layer - Samsung I2S Controller driver |
4 | // |
5 | // Copyright (c) 2010 Samsung Electronics Co. Ltd. |
6 | // Jaswinder Singh <jassisinghbrar@gmail.com> |
7 | |
8 | #include <dt-bindings/sound/samsung-i2s.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/slab.h> |
11 | #include <linux/clk.h> |
12 | #include <linux/clk-provider.h> |
13 | #include <linux/io.h> |
14 | #include <linux/module.h> |
15 | #include <linux/of.h> |
16 | #include <linux/pm_runtime.h> |
17 | |
18 | #include <sound/soc.h> |
19 | #include <sound/pcm_params.h> |
20 | |
21 | #include <linux/platform_data/asoc-s3c.h> |
22 | |
23 | #include "dma.h" |
24 | #include "idma.h" |
25 | #include "i2s.h" |
26 | #include "i2s-regs.h" |
27 | |
28 | #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) |
29 | |
30 | #define SAMSUNG_I2S_ID_PRIMARY 1 |
31 | #define SAMSUNG_I2S_ID_SECONDARY 2 |
32 | |
33 | struct samsung_i2s_variant_regs { |
34 | unsigned int bfs_off; |
35 | unsigned int rfs_off; |
36 | unsigned int sdf_off; |
37 | unsigned int txr_off; |
38 | unsigned int rclksrc_off; |
39 | unsigned int mss_off; |
40 | unsigned int cdclkcon_off; |
41 | unsigned int lrp_off; |
42 | unsigned int bfs_mask; |
43 | unsigned int rfs_mask; |
44 | unsigned int ftx0cnt_off; |
45 | }; |
46 | |
47 | struct samsung_i2s_dai_data { |
48 | u32 quirks; |
49 | unsigned int pcm_rates; |
50 | const struct samsung_i2s_variant_regs *i2s_variant_regs; |
51 | void (*fixup_early)(struct snd_pcm_substream *substream, |
52 | struct snd_soc_dai *dai); |
53 | void (*fixup_late)(struct snd_pcm_substream *substream, |
54 | struct snd_soc_dai *dai); |
55 | }; |
56 | |
57 | struct i2s_dai { |
58 | /* Platform device for this DAI */ |
59 | struct platform_device *pdev; |
60 | |
61 | /* Frame clock */ |
62 | unsigned frmclk; |
63 | /* |
64 | * Specifically requested RCLK, BCLK by machine driver. |
65 | * 0 indicates CPU driver is free to choose any value. |
66 | */ |
67 | unsigned rfs, bfs; |
68 | /* Pointer to the Primary_Fifo if this is Sec_Fifo, NULL otherwise */ |
69 | struct i2s_dai *pri_dai; |
70 | /* Pointer to the Secondary_Fifo if it has one, NULL otherwise */ |
71 | struct i2s_dai *sec_dai; |
72 | |
73 | #define DAI_OPENED (1 << 0) /* DAI is opened */ |
74 | #define DAI_MANAGER (1 << 1) /* DAI is the manager */ |
75 | unsigned mode; |
76 | |
77 | /* Driver for this DAI */ |
78 | struct snd_soc_dai_driver *drv; |
79 | |
80 | /* DMA parameters */ |
81 | struct snd_dmaengine_dai_dma_data dma_playback; |
82 | struct snd_dmaengine_dai_dma_data dma_capture; |
83 | struct snd_dmaengine_dai_dma_data idma_playback; |
84 | dma_filter_fn filter; |
85 | |
86 | struct samsung_i2s_priv *priv; |
87 | }; |
88 | |
89 | struct samsung_i2s_priv { |
90 | struct platform_device *pdev; |
91 | struct platform_device *pdev_sec; |
92 | |
93 | /* Lock for cross interface checks */ |
94 | spinlock_t pcm_lock; |
95 | |
96 | /* CPU DAIs and their corresponding drivers */ |
97 | struct i2s_dai *dai; |
98 | struct snd_soc_dai_driver *dai_drv; |
99 | int num_dais; |
100 | |
101 | /* The I2S controller's core clock */ |
102 | struct clk *clk; |
103 | |
104 | /* Clock for generating I2S signals */ |
105 | struct clk *op_clk; |
106 | |
107 | /* Rate of RCLK source clock */ |
108 | unsigned long rclk_srcrate; |
109 | |
110 | /* Cache of selected I2S registers for system suspend */ |
111 | u32 suspend_i2smod; |
112 | u32 suspend_i2scon; |
113 | u32 suspend_i2spsr; |
114 | |
115 | const struct samsung_i2s_variant_regs *variant_regs; |
116 | void (*fixup_early)(struct snd_pcm_substream *substream, |
117 | struct snd_soc_dai *dai); |
118 | void (*fixup_late)(struct snd_pcm_substream *substream, |
119 | struct snd_soc_dai *dai); |
120 | u32 quirks; |
121 | |
122 | /* The clock provider's data */ |
123 | struct clk *clk_table[3]; |
124 | struct clk_onecell_data clk_data; |
125 | |
126 | /* Spinlock protecting member fields below */ |
127 | spinlock_t lock; |
128 | |
129 | /* Memory mapped SFR region */ |
130 | void __iomem *addr; |
131 | |
132 | /* A flag indicating the I2S slave mode operation */ |
133 | bool slave_mode; |
134 | }; |
135 | |
136 | /* Returns true if this is the 'overlay' stereo DAI */ |
137 | static inline bool is_secondary(struct i2s_dai *i2s) |
138 | { |
139 | return i2s->drv->id == SAMSUNG_I2S_ID_SECONDARY; |
140 | } |
141 | |
142 | /* If this interface of the controller is transmitting data */ |
143 | static inline bool tx_active(struct i2s_dai *i2s) |
144 | { |
145 | u32 active; |
146 | |
147 | if (!i2s) |
148 | return false; |
149 | |
150 | active = readl(addr: i2s->priv->addr + I2SCON); |
151 | |
152 | if (is_secondary(i2s)) |
153 | active &= CON_TXSDMA_ACTIVE; |
154 | else |
155 | active &= CON_TXDMA_ACTIVE; |
156 | |
157 | return active ? true : false; |
158 | } |
159 | |
160 | /* Return pointer to the other DAI */ |
161 | static inline struct i2s_dai *get_other_dai(struct i2s_dai *i2s) |
162 | { |
163 | return i2s->pri_dai ? : i2s->sec_dai; |
164 | } |
165 | |
166 | /* If the other interface of the controller is transmitting data */ |
167 | static inline bool other_tx_active(struct i2s_dai *i2s) |
168 | { |
169 | struct i2s_dai *other = get_other_dai(i2s); |
170 | |
171 | return tx_active(i2s: other); |
172 | } |
173 | |
174 | /* If any interface of the controller is transmitting data */ |
175 | static inline bool any_tx_active(struct i2s_dai *i2s) |
176 | { |
177 | return tx_active(i2s) || other_tx_active(i2s); |
178 | } |
179 | |
180 | /* If this interface of the controller is receiving data */ |
181 | static inline bool rx_active(struct i2s_dai *i2s) |
182 | { |
183 | u32 active; |
184 | |
185 | if (!i2s) |
186 | return false; |
187 | |
188 | active = readl(addr: i2s->priv->addr + I2SCON) & CON_RXDMA_ACTIVE; |
189 | |
190 | return active ? true : false; |
191 | } |
192 | |
193 | /* If the other interface of the controller is receiving data */ |
194 | static inline bool other_rx_active(struct i2s_dai *i2s) |
195 | { |
196 | struct i2s_dai *other = get_other_dai(i2s); |
197 | |
198 | return rx_active(i2s: other); |
199 | } |
200 | |
201 | /* If any interface of the controller is receiving data */ |
202 | static inline bool any_rx_active(struct i2s_dai *i2s) |
203 | { |
204 | return rx_active(i2s) || other_rx_active(i2s); |
205 | } |
206 | |
207 | /* If the other DAI is transmitting or receiving data */ |
208 | static inline bool other_active(struct i2s_dai *i2s) |
209 | { |
210 | return other_rx_active(i2s) || other_tx_active(i2s); |
211 | } |
212 | |
213 | /* If this DAI is transmitting or receiving data */ |
214 | static inline bool this_active(struct i2s_dai *i2s) |
215 | { |
216 | return tx_active(i2s) || rx_active(i2s); |
217 | } |
218 | |
219 | /* If the controller is active anyway */ |
220 | static inline bool any_active(struct i2s_dai *i2s) |
221 | { |
222 | return this_active(i2s) || other_active(i2s); |
223 | } |
224 | |
225 | static inline struct i2s_dai *to_info(struct snd_soc_dai *dai) |
226 | { |
227 | struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); |
228 | |
229 | return &priv->dai[dai->id - 1]; |
230 | } |
231 | |
232 | static inline bool is_opened(struct i2s_dai *i2s) |
233 | { |
234 | if (i2s && (i2s->mode & DAI_OPENED)) |
235 | return true; |
236 | else |
237 | return false; |
238 | } |
239 | |
240 | static inline bool is_manager(struct i2s_dai *i2s) |
241 | { |
242 | if (is_opened(i2s) && (i2s->mode & DAI_MANAGER)) |
243 | return true; |
244 | else |
245 | return false; |
246 | } |
247 | |
248 | /* Read RCLK of I2S (in multiples of LRCLK) */ |
249 | static inline unsigned get_rfs(struct i2s_dai *i2s) |
250 | { |
251 | struct samsung_i2s_priv *priv = i2s->priv; |
252 | u32 rfs; |
253 | |
254 | rfs = readl(addr: priv->addr + I2SMOD) >> priv->variant_regs->rfs_off; |
255 | rfs &= priv->variant_regs->rfs_mask; |
256 | |
257 | switch (rfs) { |
258 | case 7: return 192; |
259 | case 6: return 96; |
260 | case 5: return 128; |
261 | case 4: return 64; |
262 | case 3: return 768; |
263 | case 2: return 384; |
264 | case 1: return 512; |
265 | default: return 256; |
266 | } |
267 | } |
268 | |
269 | /* Write RCLK of I2S (in multiples of LRCLK) */ |
270 | static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) |
271 | { |
272 | struct samsung_i2s_priv *priv = i2s->priv; |
273 | u32 mod = readl(addr: priv->addr + I2SMOD); |
274 | int rfs_shift = priv->variant_regs->rfs_off; |
275 | |
276 | mod &= ~(priv->variant_regs->rfs_mask << rfs_shift); |
277 | |
278 | switch (rfs) { |
279 | case 192: |
280 | mod |= (EXYNOS7_MOD_RCLK_192FS << rfs_shift); |
281 | break; |
282 | case 96: |
283 | mod |= (EXYNOS7_MOD_RCLK_96FS << rfs_shift); |
284 | break; |
285 | case 128: |
286 | mod |= (EXYNOS7_MOD_RCLK_128FS << rfs_shift); |
287 | break; |
288 | case 64: |
289 | mod |= (EXYNOS7_MOD_RCLK_64FS << rfs_shift); |
290 | break; |
291 | case 768: |
292 | mod |= (MOD_RCLK_768FS << rfs_shift); |
293 | break; |
294 | case 512: |
295 | mod |= (MOD_RCLK_512FS << rfs_shift); |
296 | break; |
297 | case 384: |
298 | mod |= (MOD_RCLK_384FS << rfs_shift); |
299 | break; |
300 | default: |
301 | mod |= (MOD_RCLK_256FS << rfs_shift); |
302 | break; |
303 | } |
304 | |
305 | writel(val: mod, addr: priv->addr + I2SMOD); |
306 | } |
307 | |
308 | /* Read bit-clock of I2S (in multiples of LRCLK) */ |
309 | static inline unsigned get_bfs(struct i2s_dai *i2s) |
310 | { |
311 | struct samsung_i2s_priv *priv = i2s->priv; |
312 | u32 bfs; |
313 | |
314 | bfs = readl(addr: priv->addr + I2SMOD) >> priv->variant_regs->bfs_off; |
315 | bfs &= priv->variant_regs->bfs_mask; |
316 | |
317 | switch (bfs) { |
318 | case 8: return 256; |
319 | case 7: return 192; |
320 | case 6: return 128; |
321 | case 5: return 96; |
322 | case 4: return 64; |
323 | case 3: return 24; |
324 | case 2: return 16; |
325 | case 1: return 48; |
326 | default: return 32; |
327 | } |
328 | } |
329 | |
330 | /* Write bit-clock of I2S (in multiples of LRCLK) */ |
331 | static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) |
332 | { |
333 | struct samsung_i2s_priv *priv = i2s->priv; |
334 | u32 mod = readl(addr: priv->addr + I2SMOD); |
335 | int tdm = priv->quirks & QUIRK_SUPPORTS_TDM; |
336 | int bfs_shift = priv->variant_regs->bfs_off; |
337 | |
338 | /* Non-TDM I2S controllers do not support BCLK > 48 * FS */ |
339 | if (!tdm && bfs > 48) { |
340 | dev_err(&i2s->pdev->dev, "Unsupported BCLK divider\n" ); |
341 | return; |
342 | } |
343 | |
344 | mod &= ~(priv->variant_regs->bfs_mask << bfs_shift); |
345 | |
346 | switch (bfs) { |
347 | case 48: |
348 | mod |= (MOD_BCLK_48FS << bfs_shift); |
349 | break; |
350 | case 32: |
351 | mod |= (MOD_BCLK_32FS << bfs_shift); |
352 | break; |
353 | case 24: |
354 | mod |= (MOD_BCLK_24FS << bfs_shift); |
355 | break; |
356 | case 16: |
357 | mod |= (MOD_BCLK_16FS << bfs_shift); |
358 | break; |
359 | case 64: |
360 | mod |= (EXYNOS5420_MOD_BCLK_64FS << bfs_shift); |
361 | break; |
362 | case 96: |
363 | mod |= (EXYNOS5420_MOD_BCLK_96FS << bfs_shift); |
364 | break; |
365 | case 128: |
366 | mod |= (EXYNOS5420_MOD_BCLK_128FS << bfs_shift); |
367 | break; |
368 | case 192: |
369 | mod |= (EXYNOS5420_MOD_BCLK_192FS << bfs_shift); |
370 | break; |
371 | case 256: |
372 | mod |= (EXYNOS5420_MOD_BCLK_256FS << bfs_shift); |
373 | break; |
374 | default: |
375 | dev_err(&i2s->pdev->dev, "Wrong BCLK Divider!\n" ); |
376 | return; |
377 | } |
378 | |
379 | writel(val: mod, addr: priv->addr + I2SMOD); |
380 | } |
381 | |
382 | /* Sample size */ |
383 | static inline int get_blc(struct i2s_dai *i2s) |
384 | { |
385 | int blc = readl(addr: i2s->priv->addr + I2SMOD); |
386 | |
387 | blc = (blc >> 13) & 0x3; |
388 | |
389 | switch (blc) { |
390 | case 2: return 24; |
391 | case 1: return 8; |
392 | default: return 16; |
393 | } |
394 | } |
395 | |
396 | /* TX channel control */ |
397 | static void i2s_txctrl(struct i2s_dai *i2s, int on) |
398 | { |
399 | struct samsung_i2s_priv *priv = i2s->priv; |
400 | void __iomem *addr = priv->addr; |
401 | int txr_off = priv->variant_regs->txr_off; |
402 | u32 con = readl(addr: addr + I2SCON); |
403 | u32 mod = readl(addr: addr + I2SMOD) & ~(3 << txr_off); |
404 | |
405 | if (on) { |
406 | con |= CON_ACTIVE; |
407 | con &= ~CON_TXCH_PAUSE; |
408 | |
409 | if (is_secondary(i2s)) { |
410 | con |= CON_TXSDMA_ACTIVE; |
411 | con &= ~CON_TXSDMA_PAUSE; |
412 | } else { |
413 | con |= CON_TXDMA_ACTIVE; |
414 | con &= ~CON_TXDMA_PAUSE; |
415 | } |
416 | |
417 | if (any_rx_active(i2s)) |
418 | mod |= 2 << txr_off; |
419 | else |
420 | mod |= 0 << txr_off; |
421 | } else { |
422 | if (is_secondary(i2s)) { |
423 | con |= CON_TXSDMA_PAUSE; |
424 | con &= ~CON_TXSDMA_ACTIVE; |
425 | } else { |
426 | con |= CON_TXDMA_PAUSE; |
427 | con &= ~CON_TXDMA_ACTIVE; |
428 | } |
429 | |
430 | if (other_tx_active(i2s)) { |
431 | writel(val: con, addr: addr + I2SCON); |
432 | return; |
433 | } |
434 | |
435 | con |= CON_TXCH_PAUSE; |
436 | |
437 | if (any_rx_active(i2s)) |
438 | mod |= 1 << txr_off; |
439 | else |
440 | con &= ~CON_ACTIVE; |
441 | } |
442 | |
443 | writel(val: mod, addr: addr + I2SMOD); |
444 | writel(val: con, addr: addr + I2SCON); |
445 | } |
446 | |
447 | /* RX Channel Control */ |
448 | static void i2s_rxctrl(struct i2s_dai *i2s, int on) |
449 | { |
450 | struct samsung_i2s_priv *priv = i2s->priv; |
451 | void __iomem *addr = priv->addr; |
452 | int txr_off = priv->variant_regs->txr_off; |
453 | u32 con = readl(addr: addr + I2SCON); |
454 | u32 mod = readl(addr: addr + I2SMOD) & ~(3 << txr_off); |
455 | |
456 | if (on) { |
457 | con |= CON_RXDMA_ACTIVE | CON_ACTIVE; |
458 | con &= ~(CON_RXDMA_PAUSE | CON_RXCH_PAUSE); |
459 | |
460 | if (any_tx_active(i2s)) |
461 | mod |= 2 << txr_off; |
462 | else |
463 | mod |= 1 << txr_off; |
464 | } else { |
465 | con |= CON_RXDMA_PAUSE | CON_RXCH_PAUSE; |
466 | con &= ~CON_RXDMA_ACTIVE; |
467 | |
468 | if (any_tx_active(i2s)) |
469 | mod |= 0 << txr_off; |
470 | else |
471 | con &= ~CON_ACTIVE; |
472 | } |
473 | |
474 | writel(val: mod, addr: addr + I2SMOD); |
475 | writel(val: con, addr: addr + I2SCON); |
476 | } |
477 | |
478 | /* Flush FIFO of an interface */ |
479 | static inline void i2s_fifo(struct i2s_dai *i2s, u32 flush) |
480 | { |
481 | void __iomem *fic; |
482 | u32 val; |
483 | |
484 | if (!i2s) |
485 | return; |
486 | |
487 | if (is_secondary(i2s)) |
488 | fic = i2s->priv->addr + I2SFICS; |
489 | else |
490 | fic = i2s->priv->addr + I2SFIC; |
491 | |
492 | /* Flush the FIFO */ |
493 | writel(readl(addr: fic) | flush, addr: fic); |
494 | |
495 | /* Be patient */ |
496 | val = msecs_to_loops(1) / 1000; /* 1 usec */ |
497 | while (--val) |
498 | cpu_relax(); |
499 | |
500 | writel(readl(addr: fic) & ~flush, addr: fic); |
501 | } |
502 | |
503 | static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, |
504 | int dir) |
505 | { |
506 | struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); |
507 | struct i2s_dai *i2s = to_info(dai); |
508 | struct i2s_dai *other = get_other_dai(i2s); |
509 | const struct samsung_i2s_variant_regs *i2s_regs = priv->variant_regs; |
510 | unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off; |
511 | unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off; |
512 | u32 mod, mask, val = 0; |
513 | unsigned long flags; |
514 | int ret = 0; |
515 | |
516 | pm_runtime_get_sync(dev: dai->dev); |
517 | |
518 | spin_lock_irqsave(&priv->lock, flags); |
519 | mod = readl(addr: priv->addr + I2SMOD); |
520 | spin_unlock_irqrestore(lock: &priv->lock, flags); |
521 | |
522 | switch (clk_id) { |
523 | case SAMSUNG_I2S_OPCLK: |
524 | mask = MOD_OPCLK_MASK; |
525 | val = (dir << MOD_OPCLK_SHIFT) & MOD_OPCLK_MASK; |
526 | break; |
527 | case SAMSUNG_I2S_CDCLK: |
528 | mask = 1 << i2s_regs->cdclkcon_off; |
529 | /* Shouldn't matter in GATING(CLOCK_IN) mode */ |
530 | if (dir == SND_SOC_CLOCK_IN) |
531 | rfs = 0; |
532 | |
533 | if ((rfs && other && other->rfs && (other->rfs != rfs)) || |
534 | (any_active(i2s) && |
535 | (((dir == SND_SOC_CLOCK_IN) |
536 | && !(mod & cdcon_mask)) || |
537 | ((dir == SND_SOC_CLOCK_OUT) |
538 | && (mod & cdcon_mask))))) { |
539 | dev_err(&i2s->pdev->dev, |
540 | "%s:%d Other DAI busy\n" , __func__, __LINE__); |
541 | ret = -EAGAIN; |
542 | goto err; |
543 | } |
544 | |
545 | if (dir == SND_SOC_CLOCK_IN) |
546 | val = 1 << i2s_regs->cdclkcon_off; |
547 | |
548 | i2s->rfs = rfs; |
549 | break; |
550 | |
551 | case SAMSUNG_I2S_RCLKSRC_0: /* clock corrsponding to IISMOD[10] := 0 */ |
552 | case SAMSUNG_I2S_RCLKSRC_1: /* clock corrsponding to IISMOD[10] := 1 */ |
553 | mask = 1 << i2s_regs->rclksrc_off; |
554 | |
555 | if ((priv->quirks & QUIRK_NO_MUXPSR) |
556 | || (clk_id == SAMSUNG_I2S_RCLKSRC_0)) |
557 | clk_id = 0; |
558 | else |
559 | clk_id = 1; |
560 | |
561 | if (!any_active(i2s)) { |
562 | if (priv->op_clk && !IS_ERR(ptr: priv->op_clk)) { |
563 | if ((clk_id && !(mod & rsrc_mask)) || |
564 | (!clk_id && (mod & rsrc_mask))) { |
565 | clk_disable_unprepare(clk: priv->op_clk); |
566 | clk_put(clk: priv->op_clk); |
567 | } else { |
568 | priv->rclk_srcrate = |
569 | clk_get_rate(clk: priv->op_clk); |
570 | goto done; |
571 | } |
572 | } |
573 | |
574 | if (clk_id) |
575 | priv->op_clk = clk_get(dev: &i2s->pdev->dev, |
576 | id: "i2s_opclk1" ); |
577 | else |
578 | priv->op_clk = clk_get(dev: &i2s->pdev->dev, |
579 | id: "i2s_opclk0" ); |
580 | |
581 | if (WARN_ON(IS_ERR(priv->op_clk))) { |
582 | ret = PTR_ERR(ptr: priv->op_clk); |
583 | priv->op_clk = NULL; |
584 | goto err; |
585 | } |
586 | |
587 | ret = clk_prepare_enable(clk: priv->op_clk); |
588 | if (ret) { |
589 | clk_put(clk: priv->op_clk); |
590 | priv->op_clk = NULL; |
591 | goto err; |
592 | } |
593 | priv->rclk_srcrate = clk_get_rate(clk: priv->op_clk); |
594 | |
595 | } else if ((!clk_id && (mod & rsrc_mask)) |
596 | || (clk_id && !(mod & rsrc_mask))) { |
597 | dev_err(&i2s->pdev->dev, |
598 | "%s:%d Other DAI busy\n" , __func__, __LINE__); |
599 | ret = -EAGAIN; |
600 | goto err; |
601 | } else { |
602 | /* Call can't be on the active DAI */ |
603 | goto done; |
604 | } |
605 | |
606 | if (clk_id == 1) |
607 | val = 1 << i2s_regs->rclksrc_off; |
608 | break; |
609 | default: |
610 | dev_err(&i2s->pdev->dev, "We don't serve that!\n" ); |
611 | ret = -EINVAL; |
612 | goto err; |
613 | } |
614 | |
615 | spin_lock_irqsave(&priv->lock, flags); |
616 | mod = readl(addr: priv->addr + I2SMOD); |
617 | mod = (mod & ~mask) | val; |
618 | writel(val: mod, addr: priv->addr + I2SMOD); |
619 | spin_unlock_irqrestore(lock: &priv->lock, flags); |
620 | done: |
621 | pm_runtime_put(dev: dai->dev); |
622 | |
623 | return 0; |
624 | err: |
625 | pm_runtime_put(dev: dai->dev); |
626 | return ret; |
627 | } |
628 | |
629 | static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
630 | { |
631 | struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); |
632 | struct i2s_dai *i2s = to_info(dai); |
633 | int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave; |
634 | u32 mod, tmp = 0; |
635 | unsigned long flags; |
636 | |
637 | lrp_shift = priv->variant_regs->lrp_off; |
638 | sdf_shift = priv->variant_regs->sdf_off; |
639 | mod_slave = 1 << priv->variant_regs->mss_off; |
640 | |
641 | sdf_mask = MOD_SDF_MASK << sdf_shift; |
642 | lrp_rlow = MOD_LR_RLOW << lrp_shift; |
643 | |
644 | /* Format is priority */ |
645 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
646 | case SND_SOC_DAIFMT_RIGHT_J: |
647 | tmp |= lrp_rlow; |
648 | tmp |= (MOD_SDF_MSB << sdf_shift); |
649 | break; |
650 | case SND_SOC_DAIFMT_LEFT_J: |
651 | tmp |= lrp_rlow; |
652 | tmp |= (MOD_SDF_LSB << sdf_shift); |
653 | break; |
654 | case SND_SOC_DAIFMT_I2S: |
655 | tmp |= (MOD_SDF_IIS << sdf_shift); |
656 | break; |
657 | default: |
658 | dev_err(&i2s->pdev->dev, "Format not supported\n" ); |
659 | return -EINVAL; |
660 | } |
661 | |
662 | /* |
663 | * INV flag is relative to the FORMAT flag - if set it simply |
664 | * flips the polarity specified by the Standard |
665 | */ |
666 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
667 | case SND_SOC_DAIFMT_NB_NF: |
668 | break; |
669 | case SND_SOC_DAIFMT_NB_IF: |
670 | if (tmp & lrp_rlow) |
671 | tmp &= ~lrp_rlow; |
672 | else |
673 | tmp |= lrp_rlow; |
674 | break; |
675 | default: |
676 | dev_err(&i2s->pdev->dev, "Polarity not supported\n" ); |
677 | return -EINVAL; |
678 | } |
679 | |
680 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
681 | case SND_SOC_DAIFMT_BC_FC: |
682 | tmp |= mod_slave; |
683 | break; |
684 | case SND_SOC_DAIFMT_BP_FP: |
685 | /* |
686 | * Set default source clock in Master mode, only when the |
687 | * CLK_I2S_RCLK_SRC clock is not exposed so we ensure any |
688 | * clock configuration assigned in DT is not overwritten. |
689 | */ |
690 | if (priv->rclk_srcrate == 0 && priv->clk_data.clks == NULL) |
691 | i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0, |
692 | rfs: 0, SND_SOC_CLOCK_IN); |
693 | break; |
694 | default: |
695 | dev_err(&i2s->pdev->dev, "master/slave format not supported\n" ); |
696 | return -EINVAL; |
697 | } |
698 | |
699 | pm_runtime_get_sync(dev: dai->dev); |
700 | spin_lock_irqsave(&priv->lock, flags); |
701 | mod = readl(addr: priv->addr + I2SMOD); |
702 | /* |
703 | * Don't change the I2S mode if any controller is active on this |
704 | * channel. |
705 | */ |
706 | if (any_active(i2s) && |
707 | ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) { |
708 | spin_unlock_irqrestore(lock: &priv->lock, flags); |
709 | pm_runtime_put(dev: dai->dev); |
710 | dev_err(&i2s->pdev->dev, |
711 | "%s:%d Other DAI busy\n" , __func__, __LINE__); |
712 | return -EAGAIN; |
713 | } |
714 | |
715 | mod &= ~(sdf_mask | lrp_rlow | mod_slave); |
716 | mod |= tmp; |
717 | writel(val: mod, addr: priv->addr + I2SMOD); |
718 | priv->slave_mode = (mod & mod_slave); |
719 | spin_unlock_irqrestore(lock: &priv->lock, flags); |
720 | pm_runtime_put(dev: dai->dev); |
721 | |
722 | return 0; |
723 | } |
724 | |
725 | static int i2s_hw_params(struct snd_pcm_substream *substream, |
726 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) |
727 | { |
728 | struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); |
729 | struct i2s_dai *i2s = to_info(dai); |
730 | u32 mod, mask = 0, val = 0; |
731 | struct clk *rclksrc; |
732 | unsigned long flags; |
733 | |
734 | WARN_ON(!pm_runtime_active(dai->dev)); |
735 | |
736 | if (!is_secondary(i2s)) |
737 | mask |= (MOD_DC2_EN | MOD_DC1_EN); |
738 | |
739 | switch (params_channels(p: params)) { |
740 | case 6: |
741 | val |= MOD_DC2_EN; |
742 | fallthrough; |
743 | case 4: |
744 | val |= MOD_DC1_EN; |
745 | break; |
746 | case 2: |
747 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
748 | i2s->dma_playback.addr_width = 4; |
749 | else |
750 | i2s->dma_capture.addr_width = 4; |
751 | break; |
752 | case 1: |
753 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
754 | i2s->dma_playback.addr_width = 2; |
755 | else |
756 | i2s->dma_capture.addr_width = 2; |
757 | |
758 | break; |
759 | default: |
760 | dev_err(&i2s->pdev->dev, "%d channels not supported\n" , |
761 | params_channels(params)); |
762 | return -EINVAL; |
763 | } |
764 | |
765 | if (is_secondary(i2s)) |
766 | mask |= MOD_BLCS_MASK; |
767 | else |
768 | mask |= MOD_BLCP_MASK; |
769 | |
770 | if (is_manager(i2s)) |
771 | mask |= MOD_BLC_MASK; |
772 | |
773 | switch (params_width(p: params)) { |
774 | case 8: |
775 | if (is_secondary(i2s)) |
776 | val |= MOD_BLCS_8BIT; |
777 | else |
778 | val |= MOD_BLCP_8BIT; |
779 | if (is_manager(i2s)) |
780 | val |= MOD_BLC_8BIT; |
781 | break; |
782 | case 16: |
783 | if (is_secondary(i2s)) |
784 | val |= MOD_BLCS_16BIT; |
785 | else |
786 | val |= MOD_BLCP_16BIT; |
787 | if (is_manager(i2s)) |
788 | val |= MOD_BLC_16BIT; |
789 | break; |
790 | case 24: |
791 | if (is_secondary(i2s)) |
792 | val |= MOD_BLCS_24BIT; |
793 | else |
794 | val |= MOD_BLCP_24BIT; |
795 | if (is_manager(i2s)) |
796 | val |= MOD_BLC_24BIT; |
797 | break; |
798 | default: |
799 | dev_err(&i2s->pdev->dev, "Format(%d) not supported\n" , |
800 | params_format(params)); |
801 | return -EINVAL; |
802 | } |
803 | |
804 | spin_lock_irqsave(&priv->lock, flags); |
805 | mod = readl(addr: priv->addr + I2SMOD); |
806 | mod = (mod & ~mask) | val; |
807 | writel(val: mod, addr: priv->addr + I2SMOD); |
808 | spin_unlock_irqrestore(lock: &priv->lock, flags); |
809 | |
810 | snd_soc_dai_init_dma_data(dai, playback: &i2s->dma_playback, capture: &i2s->dma_capture); |
811 | |
812 | i2s->frmclk = params_rate(p: params); |
813 | |
814 | rclksrc = priv->clk_table[CLK_I2S_RCLK_SRC]; |
815 | if (rclksrc && !IS_ERR(ptr: rclksrc)) |
816 | priv->rclk_srcrate = clk_get_rate(clk: rclksrc); |
817 | |
818 | return 0; |
819 | } |
820 | |
821 | /* We set constraints on the substream according to the version of I2S */ |
822 | static int i2s_startup(struct snd_pcm_substream *substream, |
823 | struct snd_soc_dai *dai) |
824 | { |
825 | struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); |
826 | struct i2s_dai *i2s = to_info(dai); |
827 | struct i2s_dai *other = get_other_dai(i2s); |
828 | unsigned long flags; |
829 | |
830 | pm_runtime_get_sync(dev: dai->dev); |
831 | |
832 | spin_lock_irqsave(&priv->pcm_lock, flags); |
833 | |
834 | i2s->mode |= DAI_OPENED; |
835 | |
836 | if (is_manager(i2s: other)) |
837 | i2s->mode &= ~DAI_MANAGER; |
838 | else |
839 | i2s->mode |= DAI_MANAGER; |
840 | |
841 | if (!any_active(i2s) && (priv->quirks & QUIRK_NEED_RSTCLR)) |
842 | writel(CON_RSTCLR, addr: i2s->priv->addr + I2SCON); |
843 | |
844 | spin_unlock_irqrestore(lock: &priv->pcm_lock, flags); |
845 | |
846 | return 0; |
847 | } |
848 | |
849 | static void i2s_shutdown(struct snd_pcm_substream *substream, |
850 | struct snd_soc_dai *dai) |
851 | { |
852 | struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); |
853 | struct i2s_dai *i2s = to_info(dai); |
854 | struct i2s_dai *other = get_other_dai(i2s); |
855 | unsigned long flags; |
856 | |
857 | spin_lock_irqsave(&priv->pcm_lock, flags); |
858 | |
859 | i2s->mode &= ~DAI_OPENED; |
860 | i2s->mode &= ~DAI_MANAGER; |
861 | |
862 | if (is_opened(i2s: other)) |
863 | other->mode |= DAI_MANAGER; |
864 | |
865 | /* Reset any constraint on RFS and BFS */ |
866 | i2s->rfs = 0; |
867 | i2s->bfs = 0; |
868 | |
869 | spin_unlock_irqrestore(lock: &priv->pcm_lock, flags); |
870 | |
871 | pm_runtime_put(dev: dai->dev); |
872 | } |
873 | |
874 | static int config_setup(struct i2s_dai *i2s) |
875 | { |
876 | struct samsung_i2s_priv *priv = i2s->priv; |
877 | struct i2s_dai *other = get_other_dai(i2s); |
878 | unsigned rfs, bfs, blc; |
879 | u32 psr; |
880 | |
881 | blc = get_blc(i2s); |
882 | |
883 | bfs = i2s->bfs; |
884 | |
885 | if (!bfs && other) |
886 | bfs = other->bfs; |
887 | |
888 | /* Select least possible multiple(2) if no constraint set */ |
889 | if (!bfs) |
890 | bfs = blc * 2; |
891 | |
892 | rfs = i2s->rfs; |
893 | |
894 | if (!rfs && other) |
895 | rfs = other->rfs; |
896 | |
897 | if ((rfs == 256 || rfs == 512) && (blc == 24)) { |
898 | dev_err(&i2s->pdev->dev, |
899 | "%d-RFS not supported for 24-blc\n" , rfs); |
900 | return -EINVAL; |
901 | } |
902 | |
903 | if (!rfs) { |
904 | if (bfs == 16 || bfs == 32) |
905 | rfs = 256; |
906 | else |
907 | rfs = 384; |
908 | } |
909 | |
910 | /* If already setup and running */ |
911 | if (any_active(i2s) && (get_rfs(i2s) != rfs || get_bfs(i2s) != bfs)) { |
912 | dev_err(&i2s->pdev->dev, |
913 | "%s:%d Other DAI busy\n" , __func__, __LINE__); |
914 | return -EAGAIN; |
915 | } |
916 | |
917 | set_bfs(i2s, bfs); |
918 | set_rfs(i2s, rfs); |
919 | |
920 | /* Don't bother with PSR in Slave mode */ |
921 | if (priv->slave_mode) |
922 | return 0; |
923 | |
924 | if (!(priv->quirks & QUIRK_NO_MUXPSR)) { |
925 | psr = priv->rclk_srcrate / i2s->frmclk / rfs; |
926 | writel(val: ((psr - 1) << 8) | PSR_PSREN, addr: priv->addr + I2SPSR); |
927 | dev_dbg(&i2s->pdev->dev, |
928 | "RCLK_SRC=%luHz PSR=%u, RCLK=%dfs, BCLK=%dfs\n" , |
929 | priv->rclk_srcrate, psr, rfs, bfs); |
930 | } |
931 | |
932 | return 0; |
933 | } |
934 | |
935 | static int i2s_trigger(struct snd_pcm_substream *substream, |
936 | int cmd, struct snd_soc_dai *dai) |
937 | { |
938 | struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); |
939 | int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); |
940 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
941 | struct i2s_dai *i2s = to_info(snd_soc_rtd_to_cpu(rtd, 0)); |
942 | unsigned long flags; |
943 | |
944 | switch (cmd) { |
945 | case SNDRV_PCM_TRIGGER_START: |
946 | case SNDRV_PCM_TRIGGER_RESUME: |
947 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
948 | pm_runtime_get_sync(dev: dai->dev); |
949 | |
950 | if (priv->fixup_early) |
951 | priv->fixup_early(substream, dai); |
952 | |
953 | spin_lock_irqsave(&priv->lock, flags); |
954 | |
955 | if (config_setup(i2s)) { |
956 | spin_unlock_irqrestore(lock: &priv->lock, flags); |
957 | return -EINVAL; |
958 | } |
959 | |
960 | if (priv->fixup_late) |
961 | priv->fixup_late(substream, dai); |
962 | |
963 | if (capture) |
964 | i2s_rxctrl(i2s, on: 1); |
965 | else |
966 | i2s_txctrl(i2s, on: 1); |
967 | |
968 | spin_unlock_irqrestore(lock: &priv->lock, flags); |
969 | break; |
970 | case SNDRV_PCM_TRIGGER_STOP: |
971 | case SNDRV_PCM_TRIGGER_SUSPEND: |
972 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
973 | spin_lock_irqsave(&priv->lock, flags); |
974 | |
975 | if (capture) { |
976 | i2s_rxctrl(i2s, on: 0); |
977 | i2s_fifo(i2s, FIC_RXFLUSH); |
978 | } else { |
979 | i2s_txctrl(i2s, on: 0); |
980 | i2s_fifo(i2s, FIC_TXFLUSH); |
981 | } |
982 | |
983 | spin_unlock_irqrestore(lock: &priv->lock, flags); |
984 | pm_runtime_put(dev: dai->dev); |
985 | break; |
986 | } |
987 | |
988 | return 0; |
989 | } |
990 | |
991 | static int i2s_set_clkdiv(struct snd_soc_dai *dai, |
992 | int div_id, int div) |
993 | { |
994 | struct i2s_dai *i2s = to_info(dai); |
995 | struct i2s_dai *other = get_other_dai(i2s); |
996 | |
997 | switch (div_id) { |
998 | case SAMSUNG_I2S_DIV_BCLK: |
999 | pm_runtime_get_sync(dev: dai->dev); |
1000 | if ((any_active(i2s) && div && (get_bfs(i2s) != div)) |
1001 | || (other && other->bfs && (other->bfs != div))) { |
1002 | pm_runtime_put(dev: dai->dev); |
1003 | dev_err(&i2s->pdev->dev, |
1004 | "%s:%d Other DAI busy\n" , __func__, __LINE__); |
1005 | return -EAGAIN; |
1006 | } |
1007 | i2s->bfs = div; |
1008 | pm_runtime_put(dev: dai->dev); |
1009 | break; |
1010 | default: |
1011 | dev_err(&i2s->pdev->dev, |
1012 | "Invalid clock divider(%d)\n" , div_id); |
1013 | return -EINVAL; |
1014 | } |
1015 | |
1016 | return 0; |
1017 | } |
1018 | |
1019 | static snd_pcm_sframes_t |
1020 | i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) |
1021 | { |
1022 | struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); |
1023 | struct i2s_dai *i2s = to_info(dai); |
1024 | u32 reg = readl(addr: priv->addr + I2SFIC); |
1025 | snd_pcm_sframes_t delay; |
1026 | |
1027 | WARN_ON(!pm_runtime_active(dai->dev)); |
1028 | |
1029 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) |
1030 | delay = FIC_RXCOUNT(reg); |
1031 | else if (is_secondary(i2s)) |
1032 | delay = FICS_TXCOUNT(readl(priv->addr + I2SFICS)); |
1033 | else |
1034 | delay = (reg >> priv->variant_regs->ftx0cnt_off) & 0x7f; |
1035 | |
1036 | return delay; |
1037 | } |
1038 | |
1039 | #ifdef CONFIG_PM |
1040 | static int i2s_suspend(struct snd_soc_component *component) |
1041 | { |
1042 | return pm_runtime_force_suspend(dev: component->dev); |
1043 | } |
1044 | |
1045 | static int i2s_resume(struct snd_soc_component *component) |
1046 | { |
1047 | return pm_runtime_force_resume(dev: component->dev); |
1048 | } |
1049 | #else |
1050 | #define i2s_suspend NULL |
1051 | #define i2s_resume NULL |
1052 | #endif |
1053 | |
1054 | static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) |
1055 | { |
1056 | struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); |
1057 | struct i2s_dai *i2s = to_info(dai); |
1058 | struct i2s_dai *other = get_other_dai(i2s); |
1059 | unsigned long flags; |
1060 | |
1061 | pm_runtime_get_sync(dev: dai->dev); |
1062 | |
1063 | if (is_secondary(i2s)) { |
1064 | /* If this is probe on the secondary DAI */ |
1065 | snd_soc_dai_init_dma_data(dai, playback: &i2s->dma_playback, NULL); |
1066 | } else { |
1067 | snd_soc_dai_init_dma_data(dai, playback: &i2s->dma_playback, |
1068 | capture: &i2s->dma_capture); |
1069 | |
1070 | if (priv->quirks & QUIRK_NEED_RSTCLR) |
1071 | writel(CON_RSTCLR, addr: priv->addr + I2SCON); |
1072 | |
1073 | if (priv->quirks & QUIRK_SUPPORTS_IDMA) |
1074 | idma_reg_addr_init(regs: priv->addr, |
1075 | addr: other->idma_playback.addr); |
1076 | } |
1077 | |
1078 | /* Reset any constraint on RFS and BFS */ |
1079 | i2s->rfs = 0; |
1080 | i2s->bfs = 0; |
1081 | |
1082 | spin_lock_irqsave(&priv->lock, flags); |
1083 | i2s_txctrl(i2s, on: 0); |
1084 | i2s_rxctrl(i2s, on: 0); |
1085 | i2s_fifo(i2s, FIC_TXFLUSH); |
1086 | i2s_fifo(i2s: other, FIC_TXFLUSH); |
1087 | i2s_fifo(i2s, FIC_RXFLUSH); |
1088 | spin_unlock_irqrestore(lock: &priv->lock, flags); |
1089 | |
1090 | /* Gate CDCLK by default */ |
1091 | if (!is_opened(i2s: other)) |
1092 | i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK, |
1093 | rfs: 0, SND_SOC_CLOCK_IN); |
1094 | pm_runtime_put(dev: dai->dev); |
1095 | |
1096 | return 0; |
1097 | } |
1098 | |
1099 | static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) |
1100 | { |
1101 | struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); |
1102 | struct i2s_dai *i2s = to_info(dai); |
1103 | unsigned long flags; |
1104 | |
1105 | pm_runtime_get_sync(dev: dai->dev); |
1106 | |
1107 | if (!is_secondary(i2s)) { |
1108 | if (priv->quirks & QUIRK_NEED_RSTCLR) { |
1109 | spin_lock_irqsave(&priv->lock, flags); |
1110 | writel(val: 0, addr: priv->addr + I2SCON); |
1111 | spin_unlock_irqrestore(lock: &priv->lock, flags); |
1112 | } |
1113 | } |
1114 | |
1115 | pm_runtime_put(dev: dai->dev); |
1116 | |
1117 | return 0; |
1118 | } |
1119 | |
1120 | static const struct snd_soc_dai_ops samsung_i2s_dai_ops = { |
1121 | .probe = samsung_i2s_dai_probe, |
1122 | .remove = samsung_i2s_dai_remove, |
1123 | .trigger = i2s_trigger, |
1124 | .hw_params = i2s_hw_params, |
1125 | .set_fmt = i2s_set_fmt, |
1126 | .set_clkdiv = i2s_set_clkdiv, |
1127 | .set_sysclk = i2s_set_sysclk, |
1128 | .startup = i2s_startup, |
1129 | .shutdown = i2s_shutdown, |
1130 | .delay = i2s_delay, |
1131 | }; |
1132 | |
1133 | static const struct snd_soc_dapm_widget samsung_i2s_widgets[] = { |
1134 | /* Backend DAI */ |
1135 | SND_SOC_DAPM_AIF_OUT("Mixer DAI TX" , NULL, 0, SND_SOC_NOPM, 0, 0), |
1136 | SND_SOC_DAPM_AIF_IN("Mixer DAI RX" , NULL, 0, SND_SOC_NOPM, 0, 0), |
1137 | |
1138 | /* Playback Mixer */ |
1139 | SND_SOC_DAPM_MIXER("Playback Mixer" , SND_SOC_NOPM, 0, 0, NULL, 0), |
1140 | }; |
1141 | |
1142 | static const struct snd_soc_dapm_route samsung_i2s_dapm_routes[] = { |
1143 | { "Playback Mixer" , NULL, "Primary Playback" }, |
1144 | { "Playback Mixer" , NULL, "Secondary Playback" }, |
1145 | |
1146 | { "Mixer DAI TX" , NULL, "Playback Mixer" }, |
1147 | { "Primary Capture" , NULL, "Mixer DAI RX" }, |
1148 | }; |
1149 | |
1150 | static const struct snd_soc_component_driver samsung_i2s_component = { |
1151 | .name = "samsung-i2s" , |
1152 | |
1153 | .dapm_widgets = samsung_i2s_widgets, |
1154 | .num_dapm_widgets = ARRAY_SIZE(samsung_i2s_widgets), |
1155 | |
1156 | .dapm_routes = samsung_i2s_dapm_routes, |
1157 | .num_dapm_routes = ARRAY_SIZE(samsung_i2s_dapm_routes), |
1158 | |
1159 | .suspend = i2s_suspend, |
1160 | .resume = i2s_resume, |
1161 | |
1162 | .legacy_dai_naming = 1, |
1163 | }; |
1164 | |
1165 | #define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ |
1166 | SNDRV_PCM_FMTBIT_S24_LE) |
1167 | |
1168 | static int i2s_alloc_dais(struct samsung_i2s_priv *priv, |
1169 | const struct samsung_i2s_dai_data *i2s_dai_data, |
1170 | int num_dais) |
1171 | { |
1172 | static const char *dai_names[] = { "samsung-i2s" , "samsung-i2s-sec" }; |
1173 | static const char *stream_names[] = { "Primary Playback" , |
1174 | "Secondary Playback" }; |
1175 | struct snd_soc_dai_driver *dai_drv; |
1176 | int i; |
1177 | |
1178 | priv->dai = devm_kcalloc(dev: &priv->pdev->dev, n: num_dais, |
1179 | size: sizeof(struct i2s_dai), GFP_KERNEL); |
1180 | if (!priv->dai) |
1181 | return -ENOMEM; |
1182 | |
1183 | priv->dai_drv = devm_kcalloc(dev: &priv->pdev->dev, n: num_dais, |
1184 | size: sizeof(*dai_drv), GFP_KERNEL); |
1185 | if (!priv->dai_drv) |
1186 | return -ENOMEM; |
1187 | |
1188 | for (i = 0; i < num_dais; i++) { |
1189 | dai_drv = &priv->dai_drv[i]; |
1190 | |
1191 | dai_drv->symmetric_rate = 1; |
1192 | dai_drv->ops = &samsung_i2s_dai_ops; |
1193 | |
1194 | dai_drv->playback.channels_min = 1; |
1195 | dai_drv->playback.channels_max = 2; |
1196 | dai_drv->playback.rates = i2s_dai_data->pcm_rates; |
1197 | dai_drv->playback.formats = SAMSUNG_I2S_FMTS; |
1198 | dai_drv->playback.stream_name = stream_names[i]; |
1199 | |
1200 | dai_drv->id = i + 1; |
1201 | dai_drv->name = dai_names[i]; |
1202 | |
1203 | priv->dai[i].drv = &priv->dai_drv[i]; |
1204 | priv->dai[i].pdev = priv->pdev; |
1205 | } |
1206 | |
1207 | /* Initialize capture only for the primary DAI */ |
1208 | dai_drv = &priv->dai_drv[SAMSUNG_I2S_ID_PRIMARY - 1]; |
1209 | |
1210 | dai_drv->capture.channels_min = 1; |
1211 | dai_drv->capture.channels_max = 2; |
1212 | dai_drv->capture.rates = i2s_dai_data->pcm_rates; |
1213 | dai_drv->capture.formats = SAMSUNG_I2S_FMTS; |
1214 | dai_drv->capture.stream_name = "Primary Capture" ; |
1215 | |
1216 | return 0; |
1217 | } |
1218 | |
1219 | #ifdef CONFIG_PM |
1220 | static int i2s_runtime_suspend(struct device *dev) |
1221 | { |
1222 | struct samsung_i2s_priv *priv = dev_get_drvdata(dev); |
1223 | |
1224 | priv->suspend_i2smod = readl(addr: priv->addr + I2SMOD); |
1225 | priv->suspend_i2scon = readl(addr: priv->addr + I2SCON); |
1226 | priv->suspend_i2spsr = readl(addr: priv->addr + I2SPSR); |
1227 | |
1228 | clk_disable_unprepare(clk: priv->op_clk); |
1229 | clk_disable_unprepare(clk: priv->clk); |
1230 | |
1231 | return 0; |
1232 | } |
1233 | |
1234 | static int i2s_runtime_resume(struct device *dev) |
1235 | { |
1236 | struct samsung_i2s_priv *priv = dev_get_drvdata(dev); |
1237 | int ret; |
1238 | |
1239 | ret = clk_prepare_enable(clk: priv->clk); |
1240 | if (ret) |
1241 | return ret; |
1242 | |
1243 | if (priv->op_clk) { |
1244 | ret = clk_prepare_enable(clk: priv->op_clk); |
1245 | if (ret) { |
1246 | clk_disable_unprepare(clk: priv->clk); |
1247 | return ret; |
1248 | } |
1249 | } |
1250 | |
1251 | writel(val: priv->suspend_i2scon, addr: priv->addr + I2SCON); |
1252 | writel(val: priv->suspend_i2smod, addr: priv->addr + I2SMOD); |
1253 | writel(val: priv->suspend_i2spsr, addr: priv->addr + I2SPSR); |
1254 | |
1255 | return 0; |
1256 | } |
1257 | #endif /* CONFIG_PM */ |
1258 | |
1259 | static void i2s_unregister_clocks(struct samsung_i2s_priv *priv) |
1260 | { |
1261 | int i; |
1262 | |
1263 | for (i = 0; i < priv->clk_data.clk_num; i++) { |
1264 | if (!IS_ERR(ptr: priv->clk_table[i])) |
1265 | clk_unregister(clk: priv->clk_table[i]); |
1266 | } |
1267 | } |
1268 | |
1269 | static void i2s_unregister_clock_provider(struct samsung_i2s_priv *priv) |
1270 | { |
1271 | of_clk_del_provider(np: priv->pdev->dev.of_node); |
1272 | i2s_unregister_clocks(priv); |
1273 | } |
1274 | |
1275 | |
1276 | static int i2s_register_clock_provider(struct samsung_i2s_priv *priv) |
1277 | { |
1278 | |
1279 | const char * const i2s_clk_desc[] = { "cdclk" , "rclk_src" , "prescaler" }; |
1280 | const char *clk_name[2] = { "i2s_opclk0" , "i2s_opclk1" }; |
1281 | const char *p_names[2] = { NULL }; |
1282 | struct device *dev = &priv->pdev->dev; |
1283 | const struct samsung_i2s_variant_regs *reg_info = priv->variant_regs; |
1284 | const char *i2s_clk_name[ARRAY_SIZE(i2s_clk_desc)]; |
1285 | struct clk *rclksrc; |
1286 | int ret, i; |
1287 | |
1288 | /* Register the clock provider only if it's expected in the DTB */ |
1289 | if (!of_property_present(np: dev->of_node, propname: "#clock-cells" )) |
1290 | return 0; |
1291 | |
1292 | /* Get the RCLKSRC mux clock parent clock names */ |
1293 | for (i = 0; i < ARRAY_SIZE(p_names); i++) { |
1294 | rclksrc = clk_get(dev, id: clk_name[i]); |
1295 | if (IS_ERR(ptr: rclksrc)) |
1296 | continue; |
1297 | p_names[i] = __clk_get_name(clk: rclksrc); |
1298 | clk_put(clk: rclksrc); |
1299 | } |
1300 | |
1301 | for (i = 0; i < ARRAY_SIZE(i2s_clk_desc); i++) { |
1302 | i2s_clk_name[i] = devm_kasprintf(dev, GFP_KERNEL, fmt: "%s_%s" , |
1303 | dev_name(dev), i2s_clk_desc[i]); |
1304 | if (!i2s_clk_name[i]) |
1305 | return -ENOMEM; |
1306 | } |
1307 | |
1308 | if (!(priv->quirks & QUIRK_NO_MUXPSR)) { |
1309 | /* Activate the prescaler */ |
1310 | u32 val = readl(addr: priv->addr + I2SPSR); |
1311 | writel(val: val | PSR_PSREN, addr: priv->addr + I2SPSR); |
1312 | |
1313 | priv->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(dev, |
1314 | i2s_clk_name[CLK_I2S_RCLK_SRC], p_names, |
1315 | ARRAY_SIZE(p_names), |
1316 | CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, |
1317 | priv->addr + I2SMOD, reg_info->rclksrc_off, |
1318 | 1, 0, &priv->lock); |
1319 | |
1320 | priv->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(dev, |
1321 | i2s_clk_name[CLK_I2S_RCLK_PSR], |
1322 | i2s_clk_name[CLK_I2S_RCLK_SRC], |
1323 | CLK_SET_RATE_PARENT, |
1324 | priv->addr + I2SPSR, 8, 6, 0, &priv->lock); |
1325 | |
1326 | p_names[0] = i2s_clk_name[CLK_I2S_RCLK_PSR]; |
1327 | priv->clk_data.clk_num = 2; |
1328 | } |
1329 | |
1330 | priv->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev, |
1331 | name: i2s_clk_name[CLK_I2S_CDCLK], parent_name: p_names[0], |
1332 | CLK_SET_RATE_PARENT, |
1333 | reg: priv->addr + I2SMOD, bit_idx: reg_info->cdclkcon_off, |
1334 | CLK_GATE_SET_TO_DISABLE, lock: &priv->lock); |
1335 | |
1336 | priv->clk_data.clk_num += 1; |
1337 | priv->clk_data.clks = priv->clk_table; |
1338 | |
1339 | ret = of_clk_add_provider(np: dev->of_node, clk_src_get: of_clk_src_onecell_get, |
1340 | data: &priv->clk_data); |
1341 | if (ret < 0) { |
1342 | dev_err(dev, "failed to add clock provider: %d\n" , ret); |
1343 | i2s_unregister_clocks(priv); |
1344 | } |
1345 | |
1346 | return ret; |
1347 | } |
1348 | |
1349 | /* Create platform device for the secondary PCM */ |
1350 | static int i2s_create_secondary_device(struct samsung_i2s_priv *priv) |
1351 | { |
1352 | struct platform_device *pdev_sec; |
1353 | const char *devname; |
1354 | int ret; |
1355 | |
1356 | devname = devm_kasprintf(dev: &priv->pdev->dev, GFP_KERNEL, fmt: "%s-sec" , |
1357 | dev_name(dev: &priv->pdev->dev)); |
1358 | if (!devname) |
1359 | return -ENOMEM; |
1360 | |
1361 | pdev_sec = platform_device_alloc(name: devname, id: -1); |
1362 | if (!pdev_sec) |
1363 | return -ENOMEM; |
1364 | |
1365 | pdev_sec->driver_override = kstrdup(s: "samsung-i2s" , GFP_KERNEL); |
1366 | if (!pdev_sec->driver_override) { |
1367 | platform_device_put(pdev: pdev_sec); |
1368 | return -ENOMEM; |
1369 | } |
1370 | |
1371 | ret = platform_device_add(pdev: pdev_sec); |
1372 | if (ret < 0) { |
1373 | platform_device_put(pdev: pdev_sec); |
1374 | return ret; |
1375 | } |
1376 | |
1377 | ret = device_attach(dev: &pdev_sec->dev); |
1378 | if (ret <= 0) { |
1379 | platform_device_unregister(priv->pdev_sec); |
1380 | dev_info(&pdev_sec->dev, "device_attach() failed\n" ); |
1381 | return ret; |
1382 | } |
1383 | |
1384 | priv->pdev_sec = pdev_sec; |
1385 | |
1386 | return 0; |
1387 | } |
1388 | |
1389 | static void i2s_delete_secondary_device(struct samsung_i2s_priv *priv) |
1390 | { |
1391 | platform_device_unregister(priv->pdev_sec); |
1392 | priv->pdev_sec = NULL; |
1393 | } |
1394 | |
1395 | static int samsung_i2s_probe(struct platform_device *pdev) |
1396 | { |
1397 | struct i2s_dai *pri_dai, *sec_dai = NULL; |
1398 | struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data; |
1399 | u32 regs_base, idma_addr = 0; |
1400 | struct device_node *np = pdev->dev.of_node; |
1401 | const struct samsung_i2s_dai_data *i2s_dai_data; |
1402 | const struct platform_device_id *id; |
1403 | struct samsung_i2s_priv *priv; |
1404 | struct resource *res; |
1405 | int num_dais, ret; |
1406 | |
1407 | if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { |
1408 | i2s_dai_data = of_device_get_match_data(dev: &pdev->dev); |
1409 | } else { |
1410 | id = platform_get_device_id(pdev); |
1411 | |
1412 | /* Nothing to do if it is the secondary device probe */ |
1413 | if (!id) |
1414 | return 0; |
1415 | |
1416 | i2s_dai_data = (struct samsung_i2s_dai_data *)id->driver_data; |
1417 | } |
1418 | |
1419 | priv = devm_kzalloc(dev: &pdev->dev, size: sizeof(*priv), GFP_KERNEL); |
1420 | if (!priv) |
1421 | return -ENOMEM; |
1422 | |
1423 | if (np) { |
1424 | priv->quirks = i2s_dai_data->quirks; |
1425 | priv->fixup_early = i2s_dai_data->fixup_early; |
1426 | priv->fixup_late = i2s_dai_data->fixup_late; |
1427 | } else { |
1428 | if (!i2s_pdata) { |
1429 | dev_err(&pdev->dev, "Missing platform data\n" ); |
1430 | return -EINVAL; |
1431 | } |
1432 | priv->quirks = i2s_pdata->type.quirks; |
1433 | } |
1434 | |
1435 | num_dais = (priv->quirks & QUIRK_SEC_DAI) ? 2 : 1; |
1436 | priv->pdev = pdev; |
1437 | priv->variant_regs = i2s_dai_data->i2s_variant_regs; |
1438 | |
1439 | ret = i2s_alloc_dais(priv, i2s_dai_data, num_dais); |
1440 | if (ret < 0) |
1441 | return ret; |
1442 | |
1443 | pri_dai = &priv->dai[SAMSUNG_I2S_ID_PRIMARY - 1]; |
1444 | |
1445 | spin_lock_init(&priv->lock); |
1446 | spin_lock_init(&priv->pcm_lock); |
1447 | |
1448 | if (!np) { |
1449 | pri_dai->dma_playback.filter_data = i2s_pdata->dma_playback; |
1450 | pri_dai->dma_capture.filter_data = i2s_pdata->dma_capture; |
1451 | pri_dai->filter = i2s_pdata->dma_filter; |
1452 | |
1453 | idma_addr = i2s_pdata->type.idma_addr; |
1454 | } else { |
1455 | if (of_property_read_u32(np, propname: "samsung,idma-addr" , |
1456 | out_value: &idma_addr)) { |
1457 | if (priv->quirks & QUIRK_SUPPORTS_IDMA) { |
1458 | dev_info(&pdev->dev, "idma address is not" \ |
1459 | "specified" ); |
1460 | } |
1461 | } |
1462 | } |
1463 | |
1464 | priv->addr = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res); |
1465 | if (IS_ERR(ptr: priv->addr)) |
1466 | return PTR_ERR(ptr: priv->addr); |
1467 | |
1468 | regs_base = res->start; |
1469 | |
1470 | priv->clk = devm_clk_get(dev: &pdev->dev, id: "iis" ); |
1471 | if (IS_ERR(ptr: priv->clk)) { |
1472 | dev_err(&pdev->dev, "Failed to get iis clock\n" ); |
1473 | return PTR_ERR(ptr: priv->clk); |
1474 | } |
1475 | |
1476 | ret = clk_prepare_enable(clk: priv->clk); |
1477 | if (ret != 0) { |
1478 | dev_err(&pdev->dev, "failed to enable clock: %d\n" , ret); |
1479 | return ret; |
1480 | } |
1481 | pri_dai->dma_playback.addr = regs_base + I2STXD; |
1482 | pri_dai->dma_capture.addr = regs_base + I2SRXD; |
1483 | pri_dai->dma_playback.chan_name = "tx" ; |
1484 | pri_dai->dma_capture.chan_name = "rx" ; |
1485 | pri_dai->dma_playback.addr_width = 4; |
1486 | pri_dai->dma_capture.addr_width = 4; |
1487 | pri_dai->priv = priv; |
1488 | |
1489 | if (priv->quirks & QUIRK_PRI_6CHAN) |
1490 | pri_dai->drv->playback.channels_max = 6; |
1491 | |
1492 | ret = samsung_asoc_dma_platform_register(dev: &pdev->dev, filter: pri_dai->filter, |
1493 | tx: "tx" , rx: "rx" , NULL); |
1494 | if (ret < 0) |
1495 | goto err_disable_clk; |
1496 | |
1497 | if (priv->quirks & QUIRK_SEC_DAI) { |
1498 | sec_dai = &priv->dai[SAMSUNG_I2S_ID_SECONDARY - 1]; |
1499 | |
1500 | sec_dai->dma_playback.addr = regs_base + I2STXDS; |
1501 | sec_dai->dma_playback.chan_name = "tx-sec" ; |
1502 | |
1503 | if (!np) { |
1504 | sec_dai->dma_playback.filter_data = i2s_pdata->dma_play_sec; |
1505 | sec_dai->filter = i2s_pdata->dma_filter; |
1506 | } |
1507 | |
1508 | sec_dai->dma_playback.addr_width = 4; |
1509 | sec_dai->idma_playback.addr = idma_addr; |
1510 | sec_dai->pri_dai = pri_dai; |
1511 | sec_dai->priv = priv; |
1512 | pri_dai->sec_dai = sec_dai; |
1513 | |
1514 | ret = i2s_create_secondary_device(priv); |
1515 | if (ret < 0) |
1516 | goto err_disable_clk; |
1517 | |
1518 | ret = samsung_asoc_dma_platform_register(dev: &priv->pdev_sec->dev, |
1519 | filter: sec_dai->filter, tx: "tx-sec" , NULL, |
1520 | dma_dev: &pdev->dev); |
1521 | if (ret < 0) |
1522 | goto err_del_sec; |
1523 | |
1524 | } |
1525 | |
1526 | if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { |
1527 | dev_err(&pdev->dev, "Unable to configure gpio\n" ); |
1528 | ret = -EINVAL; |
1529 | goto err_del_sec; |
1530 | } |
1531 | |
1532 | dev_set_drvdata(dev: &pdev->dev, data: priv); |
1533 | |
1534 | ret = devm_snd_soc_register_component(dev: &pdev->dev, |
1535 | component_driver: &samsung_i2s_component, |
1536 | dai_drv: priv->dai_drv, num_dai: num_dais); |
1537 | if (ret < 0) |
1538 | goto err_del_sec; |
1539 | |
1540 | pm_runtime_set_active(dev: &pdev->dev); |
1541 | pm_runtime_enable(dev: &pdev->dev); |
1542 | |
1543 | ret = i2s_register_clock_provider(priv); |
1544 | if (ret < 0) |
1545 | goto err_disable_pm; |
1546 | |
1547 | priv->op_clk = clk_get_parent(clk: priv->clk_table[CLK_I2S_RCLK_SRC]); |
1548 | |
1549 | return 0; |
1550 | |
1551 | err_disable_pm: |
1552 | pm_runtime_disable(dev: &pdev->dev); |
1553 | err_del_sec: |
1554 | i2s_delete_secondary_device(priv); |
1555 | err_disable_clk: |
1556 | clk_disable_unprepare(clk: priv->clk); |
1557 | return ret; |
1558 | } |
1559 | |
1560 | static void samsung_i2s_remove(struct platform_device *pdev) |
1561 | { |
1562 | struct samsung_i2s_priv *priv = dev_get_drvdata(dev: &pdev->dev); |
1563 | |
1564 | /* The secondary device has no driver data assigned */ |
1565 | if (!priv) |
1566 | return; |
1567 | |
1568 | pm_runtime_get_sync(dev: &pdev->dev); |
1569 | pm_runtime_disable(dev: &pdev->dev); |
1570 | |
1571 | i2s_unregister_clock_provider(priv); |
1572 | i2s_delete_secondary_device(priv); |
1573 | clk_disable_unprepare(clk: priv->clk); |
1574 | |
1575 | pm_runtime_put_noidle(dev: &pdev->dev); |
1576 | } |
1577 | |
1578 | static void fsd_i2s_fixup_early(struct snd_pcm_substream *substream, |
1579 | struct snd_soc_dai *dai) |
1580 | { |
1581 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
1582 | struct i2s_dai *i2s = to_info(snd_soc_rtd_to_cpu(rtd, 0)); |
1583 | struct i2s_dai *other = get_other_dai(i2s); |
1584 | |
1585 | if (!is_opened(i2s: other)) { |
1586 | i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK, rfs: 0, SND_SOC_CLOCK_OUT); |
1587 | i2s_set_sysclk(dai, SAMSUNG_I2S_OPCLK, rfs: 0, MOD_OPCLK_PCLK); |
1588 | } |
1589 | } |
1590 | |
1591 | static void fsd_i2s_fixup_late(struct snd_pcm_substream *substream, |
1592 | struct snd_soc_dai *dai) |
1593 | { |
1594 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
1595 | struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); |
1596 | struct i2s_dai *i2s = to_info(snd_soc_rtd_to_cpu(rtd, 0)); |
1597 | struct i2s_dai *other = get_other_dai(i2s); |
1598 | |
1599 | if (!is_opened(i2s: other)) |
1600 | writel(PSR_PSVAL(2) | PSR_PSREN, addr: priv->addr + I2SPSR); |
1601 | } |
1602 | |
1603 | static const struct samsung_i2s_variant_regs i2sv3_regs = { |
1604 | .bfs_off = 1, |
1605 | .rfs_off = 3, |
1606 | .sdf_off = 5, |
1607 | .txr_off = 8, |
1608 | .rclksrc_off = 10, |
1609 | .mss_off = 11, |
1610 | .cdclkcon_off = 12, |
1611 | .lrp_off = 7, |
1612 | .bfs_mask = 0x3, |
1613 | .rfs_mask = 0x3, |
1614 | .ftx0cnt_off = 8, |
1615 | }; |
1616 | |
1617 | static const struct samsung_i2s_variant_regs i2sv6_regs = { |
1618 | .bfs_off = 0, |
1619 | .rfs_off = 4, |
1620 | .sdf_off = 6, |
1621 | .txr_off = 8, |
1622 | .rclksrc_off = 10, |
1623 | .mss_off = 11, |
1624 | .cdclkcon_off = 12, |
1625 | .lrp_off = 15, |
1626 | .bfs_mask = 0xf, |
1627 | .rfs_mask = 0x3, |
1628 | .ftx0cnt_off = 8, |
1629 | }; |
1630 | |
1631 | static const struct samsung_i2s_variant_regs i2sv7_regs = { |
1632 | .bfs_off = 0, |
1633 | .rfs_off = 4, |
1634 | .sdf_off = 7, |
1635 | .txr_off = 9, |
1636 | .rclksrc_off = 11, |
1637 | .mss_off = 12, |
1638 | .cdclkcon_off = 22, |
1639 | .lrp_off = 15, |
1640 | .bfs_mask = 0xf, |
1641 | .rfs_mask = 0x7, |
1642 | .ftx0cnt_off = 0, |
1643 | }; |
1644 | |
1645 | static const struct samsung_i2s_variant_regs i2sv5_i2s1_regs = { |
1646 | .bfs_off = 0, |
1647 | .rfs_off = 3, |
1648 | .sdf_off = 6, |
1649 | .txr_off = 8, |
1650 | .rclksrc_off = 10, |
1651 | .mss_off = 11, |
1652 | .cdclkcon_off = 12, |
1653 | .lrp_off = 15, |
1654 | .bfs_mask = 0x7, |
1655 | .rfs_mask = 0x7, |
1656 | .ftx0cnt_off = 8, |
1657 | }; |
1658 | |
1659 | static const struct samsung_i2s_dai_data i2sv3_dai_type = { |
1660 | .quirks = QUIRK_NO_MUXPSR, |
1661 | .pcm_rates = SNDRV_PCM_RATE_8000_96000, |
1662 | .i2s_variant_regs = &i2sv3_regs, |
1663 | }; |
1664 | |
1665 | static const struct samsung_i2s_dai_data i2sv5_dai_type __maybe_unused = { |
1666 | .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | |
1667 | QUIRK_SUPPORTS_IDMA, |
1668 | .pcm_rates = SNDRV_PCM_RATE_8000_96000, |
1669 | .i2s_variant_regs = &i2sv3_regs, |
1670 | }; |
1671 | |
1672 | static const struct samsung_i2s_dai_data i2sv6_dai_type __maybe_unused = { |
1673 | .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | |
1674 | QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA, |
1675 | .pcm_rates = SNDRV_PCM_RATE_8000_96000, |
1676 | .i2s_variant_regs = &i2sv6_regs, |
1677 | }; |
1678 | |
1679 | static const struct samsung_i2s_dai_data i2sv7_dai_type __maybe_unused = { |
1680 | .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | |
1681 | QUIRK_SUPPORTS_TDM, |
1682 | .pcm_rates = SNDRV_PCM_RATE_8000_192000, |
1683 | .i2s_variant_regs = &i2sv7_regs, |
1684 | }; |
1685 | |
1686 | static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 __maybe_unused = { |
1687 | .quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR, |
1688 | .pcm_rates = SNDRV_PCM_RATE_8000_96000, |
1689 | .i2s_variant_regs = &i2sv5_i2s1_regs, |
1690 | }; |
1691 | |
1692 | static const struct samsung_i2s_dai_data fsd_dai_type __maybe_unused = { |
1693 | .quirks = QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM, |
1694 | .pcm_rates = SNDRV_PCM_RATE_8000_192000, |
1695 | .i2s_variant_regs = &i2sv7_regs, |
1696 | .fixup_early = fsd_i2s_fixup_early, |
1697 | .fixup_late = fsd_i2s_fixup_late, |
1698 | }; |
1699 | |
1700 | static const struct platform_device_id samsung_i2s_driver_ids[] = { |
1701 | { |
1702 | .name = "samsung-i2s" , |
1703 | .driver_data = (kernel_ulong_t)&i2sv3_dai_type, |
1704 | }, |
1705 | {}, |
1706 | }; |
1707 | MODULE_DEVICE_TABLE(platform, samsung_i2s_driver_ids); |
1708 | |
1709 | #ifdef CONFIG_OF |
1710 | static const struct of_device_id exynos_i2s_match[] = { |
1711 | { |
1712 | .compatible = "samsung,s3c6410-i2s" , |
1713 | .data = &i2sv3_dai_type, |
1714 | }, { |
1715 | .compatible = "samsung,s5pv210-i2s" , |
1716 | .data = &i2sv5_dai_type, |
1717 | }, { |
1718 | .compatible = "samsung,exynos5420-i2s" , |
1719 | .data = &i2sv6_dai_type, |
1720 | }, { |
1721 | .compatible = "samsung,exynos7-i2s" , |
1722 | .data = &i2sv7_dai_type, |
1723 | }, { |
1724 | .compatible = "samsung,exynos7-i2s1" , |
1725 | .data = &i2sv5_dai_type_i2s1, |
1726 | }, { |
1727 | .compatible = "tesla,fsd-i2s" , |
1728 | .data = &fsd_dai_type, |
1729 | }, |
1730 | {}, |
1731 | }; |
1732 | MODULE_DEVICE_TABLE(of, exynos_i2s_match); |
1733 | #endif |
1734 | |
1735 | static const struct dev_pm_ops samsung_i2s_pm = { |
1736 | SET_RUNTIME_PM_OPS(i2s_runtime_suspend, |
1737 | i2s_runtime_resume, NULL) |
1738 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, |
1739 | pm_runtime_force_resume) |
1740 | }; |
1741 | |
1742 | static struct platform_driver samsung_i2s_driver = { |
1743 | .probe = samsung_i2s_probe, |
1744 | .remove_new = samsung_i2s_remove, |
1745 | .id_table = samsung_i2s_driver_ids, |
1746 | .driver = { |
1747 | .name = "samsung-i2s" , |
1748 | .of_match_table = of_match_ptr(exynos_i2s_match), |
1749 | .pm = &samsung_i2s_pm, |
1750 | }, |
1751 | }; |
1752 | |
1753 | module_platform_driver(samsung_i2s_driver); |
1754 | |
1755 | /* Module information */ |
1756 | MODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>" ); |
1757 | MODULE_DESCRIPTION("Samsung I2S Interface" ); |
1758 | MODULE_ALIAS("platform:samsung-i2s" ); |
1759 | MODULE_LICENSE("GPL" ); |
1760 | |