| .. | .. |
|---|
| 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 | |
|---|