.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> |
---|
3 | | - * |
---|
4 | | - * This program is free software; you can redistribute it and/or modify it |
---|
5 | | - * under the terms of the GNU General Public License as published by the |
---|
6 | | - * Free Software Foundation; either version 2 of the License, or (at your |
---|
7 | | - * option) any later version. |
---|
8 | | - * |
---|
9 | | - * You should have received a copy of the GNU General Public License along |
---|
10 | | - * with this program; if not, write to the Free Software Foundation, Inc., |
---|
11 | | - * 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
12 | | - * |
---|
13 | 4 | */ |
---|
14 | 5 | |
---|
15 | 6 | #include <linux/init.h> |
---|
.. | .. |
---|
58 | 49 | |
---|
59 | 50 | #define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12 |
---|
60 | 51 | #define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8 |
---|
61 | | -#define JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 24 |
---|
62 | | -#define JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 16 |
---|
63 | | -#define JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_MASK \ |
---|
64 | | - (0xf << JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
---|
65 | | -#define JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_MASK \ |
---|
66 | | - (0x1f << JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
---|
| 52 | +#define JZ4760_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 24 |
---|
| 53 | +#define JZ4760_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 16 |
---|
67 | 54 | |
---|
68 | 55 | #define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19) |
---|
69 | 56 | #define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16) |
---|
.. | .. |
---|
72 | 59 | #define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11) |
---|
73 | 60 | #define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10) |
---|
74 | 61 | #define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9) |
---|
75 | | -#define JZ_AIC_CTRL_FLUSH BIT(8) |
---|
| 62 | +#define JZ_AIC_CTRL_TFLUSH BIT(8) |
---|
| 63 | +#define JZ_AIC_CTRL_RFLUSH BIT(7) |
---|
76 | 64 | #define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6) |
---|
77 | 65 | #define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5) |
---|
78 | 66 | #define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4) |
---|
.. | .. |
---|
99 | 87 | |
---|
100 | 88 | enum jz47xx_i2s_version { |
---|
101 | 89 | JZ_I2S_JZ4740, |
---|
| 90 | + JZ_I2S_JZ4760, |
---|
| 91 | + JZ_I2S_JZ4770, |
---|
102 | 92 | JZ_I2S_JZ4780, |
---|
| 93 | +}; |
---|
| 94 | + |
---|
| 95 | +struct i2s_soc_info { |
---|
| 96 | + enum jz47xx_i2s_version version; |
---|
| 97 | + struct snd_soc_dai_driver *dai; |
---|
| 98 | + |
---|
| 99 | + bool shared_fifo_flush; |
---|
103 | 100 | }; |
---|
104 | 101 | |
---|
105 | 102 | struct jz4740_i2s { |
---|
.. | .. |
---|
113 | 110 | struct snd_dmaengine_dai_dma_data playback_dma_data; |
---|
114 | 111 | struct snd_dmaengine_dai_dma_data capture_dma_data; |
---|
115 | 112 | |
---|
116 | | - enum jz47xx_i2s_version version; |
---|
| 113 | + const struct i2s_soc_info *soc_info; |
---|
117 | 114 | }; |
---|
118 | 115 | |
---|
119 | 116 | static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s, |
---|
.. | .. |
---|
128 | 125 | writel(value, i2s->base + reg); |
---|
129 | 126 | } |
---|
130 | 127 | |
---|
| 128 | +static inline void jz4740_i2s_set_bits(const struct jz4740_i2s *i2s, |
---|
| 129 | + unsigned int reg, uint32_t bits) |
---|
| 130 | +{ |
---|
| 131 | + uint32_t value = jz4740_i2s_read(i2s, reg); |
---|
| 132 | + value |= bits; |
---|
| 133 | + jz4740_i2s_write(i2s, reg, value); |
---|
| 134 | +} |
---|
| 135 | + |
---|
131 | 136 | static int jz4740_i2s_startup(struct snd_pcm_substream *substream, |
---|
132 | 137 | struct snd_soc_dai *dai) |
---|
133 | 138 | { |
---|
134 | 139 | struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
---|
135 | | - uint32_t conf, ctrl; |
---|
| 140 | + uint32_t conf; |
---|
136 | 141 | int ret; |
---|
137 | 142 | |
---|
138 | | - if (dai->active) |
---|
| 143 | + /* |
---|
| 144 | + * When we can flush FIFOs independently, only flush the FIFO |
---|
| 145 | + * that is starting up. We can do this when the DAI is active |
---|
| 146 | + * because it does not disturb other active substreams. |
---|
| 147 | + */ |
---|
| 148 | + if (!i2s->soc_info->shared_fifo_flush) { |
---|
| 149 | + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
---|
| 150 | + jz4740_i2s_set_bits(i2s, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_TFLUSH); |
---|
| 151 | + else |
---|
| 152 | + jz4740_i2s_set_bits(i2s, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_RFLUSH); |
---|
| 153 | + } |
---|
| 154 | + |
---|
| 155 | + if (snd_soc_dai_active(dai)) |
---|
139 | 156 | return 0; |
---|
140 | 157 | |
---|
141 | | - ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL); |
---|
142 | | - ctrl |= JZ_AIC_CTRL_FLUSH; |
---|
143 | | - jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl); |
---|
| 158 | + /* |
---|
| 159 | + * When there is a shared flush bit for both FIFOs, the TFLUSH |
---|
| 160 | + * bit flushes both FIFOs. Flushing while the DAI is active would |
---|
| 161 | + * cause FIFO underruns in other active substreams so we have to |
---|
| 162 | + * guard this behind the snd_soc_dai_active() check. |
---|
| 163 | + */ |
---|
| 164 | + if (i2s->soc_info->shared_fifo_flush) |
---|
| 165 | + jz4740_i2s_set_bits(i2s, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_TFLUSH); |
---|
144 | 166 | |
---|
145 | 167 | ret = clk_prepare_enable(i2s->clk_i2s); |
---|
146 | 168 | if (ret) |
---|
.. | .. |
---|
159 | 181 | struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
---|
160 | 182 | uint32_t conf; |
---|
161 | 183 | |
---|
162 | | - if (dai->active) |
---|
| 184 | + if (snd_soc_dai_active(dai)) |
---|
163 | 185 | return; |
---|
164 | 186 | |
---|
165 | 187 | conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); |
---|
.. | .. |
---|
293 | 315 | ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK; |
---|
294 | 316 | ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET; |
---|
295 | 317 | |
---|
296 | | - if (i2s->version >= JZ_I2S_JZ4780) { |
---|
| 318 | + if (i2s->soc_info->version >= JZ_I2S_JZ4770) { |
---|
297 | 319 | div_reg &= ~I2SDIV_IDV_MASK; |
---|
298 | 320 | div_reg |= (div - 1) << I2SDIV_IDV_SHIFT; |
---|
299 | 321 | } else { |
---|
.. | .. |
---|
337 | 359 | return ret; |
---|
338 | 360 | } |
---|
339 | 361 | |
---|
340 | | -static int jz4740_i2s_suspend(struct snd_soc_dai *dai) |
---|
| 362 | +static int jz4740_i2s_suspend(struct snd_soc_component *component) |
---|
341 | 363 | { |
---|
342 | | - struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
---|
| 364 | + struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component); |
---|
343 | 365 | uint32_t conf; |
---|
344 | 366 | |
---|
345 | | - if (dai->active) { |
---|
| 367 | + if (snd_soc_component_active(component)) { |
---|
346 | 368 | conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); |
---|
347 | 369 | conf &= ~JZ_AIC_CONF_ENABLE; |
---|
348 | 370 | jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); |
---|
.. | .. |
---|
355 | 377 | return 0; |
---|
356 | 378 | } |
---|
357 | 379 | |
---|
358 | | -static int jz4740_i2s_resume(struct snd_soc_dai *dai) |
---|
| 380 | +static int jz4740_i2s_resume(struct snd_soc_component *component) |
---|
359 | 381 | { |
---|
360 | | - struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
---|
| 382 | + struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component); |
---|
361 | 383 | uint32_t conf; |
---|
362 | 384 | int ret; |
---|
363 | 385 | |
---|
.. | .. |
---|
365 | 387 | if (ret) |
---|
366 | 388 | return ret; |
---|
367 | 389 | |
---|
368 | | - if (dai->active) { |
---|
| 390 | + if (snd_soc_component_active(component)) { |
---|
369 | 391 | ret = clk_prepare_enable(i2s->clk_i2s); |
---|
370 | 392 | if (ret) { |
---|
371 | 393 | clk_disable_unprepare(i2s->clk_aic); |
---|
.. | .. |
---|
411 | 433 | snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, |
---|
412 | 434 | &i2s->capture_dma_data); |
---|
413 | 435 | |
---|
414 | | - if (i2s->version >= JZ_I2S_JZ4780) { |
---|
415 | | - conf = (7 << JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | |
---|
416 | | - (8 << JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | |
---|
| 436 | + if (i2s->soc_info->version >= JZ_I2S_JZ4760) { |
---|
| 437 | + conf = (7 << JZ4760_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | |
---|
| 438 | + (8 << JZ4760_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | |
---|
417 | 439 | JZ_AIC_CONF_OVERFLOW_PLAY_LAST | |
---|
418 | 440 | JZ_AIC_CONF_I2S | |
---|
419 | 441 | JZ_AIC_CONF_INTERNAL_CODEC; |
---|
.. | .. |
---|
468 | 490 | }, |
---|
469 | 491 | .symmetric_rates = 1, |
---|
470 | 492 | .ops = &jz4740_i2s_dai_ops, |
---|
471 | | - .suspend = jz4740_i2s_suspend, |
---|
472 | | - .resume = jz4740_i2s_resume, |
---|
473 | 493 | }; |
---|
474 | 494 | |
---|
475 | | -static struct snd_soc_dai_driver jz4780_i2s_dai = { |
---|
| 495 | +static const struct i2s_soc_info jz4740_i2s_soc_info = { |
---|
| 496 | + .version = JZ_I2S_JZ4740, |
---|
| 497 | + .dai = &jz4740_i2s_dai, |
---|
| 498 | + .shared_fifo_flush = true, |
---|
| 499 | +}; |
---|
| 500 | + |
---|
| 501 | +static const struct i2s_soc_info jz4760_i2s_soc_info = { |
---|
| 502 | + .version = JZ_I2S_JZ4760, |
---|
| 503 | + .dai = &jz4740_i2s_dai, |
---|
| 504 | +}; |
---|
| 505 | + |
---|
| 506 | +static struct snd_soc_dai_driver jz4770_i2s_dai = { |
---|
476 | 507 | .probe = jz4740_i2s_dai_probe, |
---|
477 | 508 | .remove = jz4740_i2s_dai_remove, |
---|
478 | 509 | .playback = { |
---|
.. | .. |
---|
488 | 519 | .formats = JZ4740_I2S_FMTS, |
---|
489 | 520 | }, |
---|
490 | 521 | .ops = &jz4740_i2s_dai_ops, |
---|
491 | | - .suspend = jz4740_i2s_suspend, |
---|
492 | | - .resume = jz4740_i2s_resume, |
---|
| 522 | +}; |
---|
| 523 | + |
---|
| 524 | +static const struct i2s_soc_info jz4770_i2s_soc_info = { |
---|
| 525 | + .version = JZ_I2S_JZ4770, |
---|
| 526 | + .dai = &jz4770_i2s_dai, |
---|
| 527 | +}; |
---|
| 528 | + |
---|
| 529 | +static const struct i2s_soc_info jz4780_i2s_soc_info = { |
---|
| 530 | + .version = JZ_I2S_JZ4780, |
---|
| 531 | + .dai = &jz4770_i2s_dai, |
---|
493 | 532 | }; |
---|
494 | 533 | |
---|
495 | 534 | static const struct snd_soc_component_driver jz4740_i2s_component = { |
---|
496 | 535 | .name = "jz4740-i2s", |
---|
| 536 | + .suspend = jz4740_i2s_suspend, |
---|
| 537 | + .resume = jz4740_i2s_resume, |
---|
497 | 538 | }; |
---|
498 | 539 | |
---|
499 | | -#ifdef CONFIG_OF |
---|
500 | 540 | static const struct of_device_id jz4740_of_matches[] = { |
---|
501 | | - { .compatible = "ingenic,jz4740-i2s", .data = (void *)JZ_I2S_JZ4740 }, |
---|
502 | | - { .compatible = "ingenic,jz4780-i2s", .data = (void *)JZ_I2S_JZ4780 }, |
---|
| 541 | + { .compatible = "ingenic,jz4740-i2s", .data = &jz4740_i2s_soc_info }, |
---|
| 542 | + { .compatible = "ingenic,jz4760-i2s", .data = &jz4760_i2s_soc_info }, |
---|
| 543 | + { .compatible = "ingenic,jz4770-i2s", .data = &jz4770_i2s_soc_info }, |
---|
| 544 | + { .compatible = "ingenic,jz4780-i2s", .data = &jz4780_i2s_soc_info }, |
---|
503 | 545 | { /* sentinel */ } |
---|
504 | 546 | }; |
---|
505 | 547 | MODULE_DEVICE_TABLE(of, jz4740_of_matches); |
---|
506 | | -#endif |
---|
507 | 548 | |
---|
508 | 549 | static int jz4740_i2s_dev_probe(struct platform_device *pdev) |
---|
509 | 550 | { |
---|
| 551 | + struct device *dev = &pdev->dev; |
---|
510 | 552 | struct jz4740_i2s *i2s; |
---|
511 | 553 | struct resource *mem; |
---|
512 | 554 | int ret; |
---|
513 | | - const struct of_device_id *match; |
---|
514 | 555 | |
---|
515 | | - i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); |
---|
| 556 | + i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL); |
---|
516 | 557 | if (!i2s) |
---|
517 | 558 | return -ENOMEM; |
---|
518 | 559 | |
---|
519 | | - match = of_match_device(jz4740_of_matches, &pdev->dev); |
---|
520 | | - if (match) |
---|
521 | | - i2s->version = (enum jz47xx_i2s_version)match->data; |
---|
| 560 | + i2s->soc_info = device_get_match_data(dev); |
---|
522 | 561 | |
---|
523 | 562 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
524 | | - i2s->base = devm_ioremap_resource(&pdev->dev, mem); |
---|
| 563 | + i2s->base = devm_ioremap_resource(dev, mem); |
---|
525 | 564 | if (IS_ERR(i2s->base)) |
---|
526 | 565 | return PTR_ERR(i2s->base); |
---|
527 | 566 | |
---|
528 | 567 | i2s->phys_base = mem->start; |
---|
529 | 568 | |
---|
530 | | - i2s->clk_aic = devm_clk_get(&pdev->dev, "aic"); |
---|
| 569 | + i2s->clk_aic = devm_clk_get(dev, "aic"); |
---|
531 | 570 | if (IS_ERR(i2s->clk_aic)) |
---|
532 | 571 | return PTR_ERR(i2s->clk_aic); |
---|
533 | 572 | |
---|
534 | | - i2s->clk_i2s = devm_clk_get(&pdev->dev, "i2s"); |
---|
| 573 | + i2s->clk_i2s = devm_clk_get(dev, "i2s"); |
---|
535 | 574 | if (IS_ERR(i2s->clk_i2s)) |
---|
536 | 575 | return PTR_ERR(i2s->clk_i2s); |
---|
537 | 576 | |
---|
538 | 577 | platform_set_drvdata(pdev, i2s); |
---|
539 | 578 | |
---|
540 | | - if (i2s->version == JZ_I2S_JZ4780) |
---|
541 | | - ret = devm_snd_soc_register_component(&pdev->dev, |
---|
542 | | - &jz4740_i2s_component, &jz4780_i2s_dai, 1); |
---|
543 | | - else |
---|
544 | | - ret = devm_snd_soc_register_component(&pdev->dev, |
---|
545 | | - &jz4740_i2s_component, &jz4740_i2s_dai, 1); |
---|
546 | | - |
---|
| 579 | + ret = devm_snd_soc_register_component(dev, &jz4740_i2s_component, |
---|
| 580 | + i2s->soc_info->dai, 1); |
---|
547 | 581 | if (ret) |
---|
548 | 582 | return ret; |
---|
549 | 583 | |
---|
550 | | - return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, |
---|
| 584 | + return devm_snd_dmaengine_pcm_register(dev, NULL, |
---|
551 | 585 | SND_DMAENGINE_PCM_FLAG_COMPAT); |
---|
552 | 586 | } |
---|
553 | 587 | |
---|
.. | .. |
---|
555 | 589 | .probe = jz4740_i2s_dev_probe, |
---|
556 | 590 | .driver = { |
---|
557 | 591 | .name = "jz4740-i2s", |
---|
558 | | - .of_match_table = of_match_ptr(jz4740_of_matches) |
---|
| 592 | + .of_match_table = jz4740_of_matches, |
---|
559 | 593 | }, |
---|
560 | 594 | }; |
---|
561 | 595 | |
---|