| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* Atmel ALSA SoC Audio Class D Amplifier (CLASSD) driver |
|---|
| 2 | 3 | * |
|---|
| 3 | 4 | * Copyright (C) 2015 Atmel |
|---|
| 4 | 5 | * |
|---|
| 5 | 6 | * Author: Songjun Wu <songjun.wu@atmel.com> |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | | - * it under the terms of the GNU General Public License version 2 or later |
|---|
| 9 | | - * as published by the Free Software Foundation. |
|---|
| 10 | 7 | */ |
|---|
| 11 | 8 | |
|---|
| 12 | 9 | #include <linux/of.h> |
|---|
| .. | .. |
|---|
| 121 | 118 | static int atmel_classd_cpu_dai_startup(struct snd_pcm_substream *substream, |
|---|
| 122 | 119 | struct snd_soc_dai *cpu_dai) |
|---|
| 123 | 120 | { |
|---|
| 124 | | - struct snd_soc_pcm_runtime *rtd = substream->private_data; |
|---|
| 121 | + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); |
|---|
| 125 | 122 | struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); |
|---|
| 123 | + int err; |
|---|
| 126 | 124 | |
|---|
| 127 | 125 | regmap_write(dd->regmap, CLASSD_THR, 0x0); |
|---|
| 128 | 126 | |
|---|
| 129 | | - return clk_prepare_enable(dd->pclk); |
|---|
| 127 | + err = clk_prepare_enable(dd->pclk); |
|---|
| 128 | + if (err) |
|---|
| 129 | + return err; |
|---|
| 130 | + err = clk_prepare_enable(dd->gclk); |
|---|
| 131 | + if (err) { |
|---|
| 132 | + clk_disable_unprepare(dd->pclk); |
|---|
| 133 | + return err; |
|---|
| 134 | + } |
|---|
| 135 | + return 0; |
|---|
| 130 | 136 | } |
|---|
| 131 | | - |
|---|
| 132 | | -static void atmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream, |
|---|
| 133 | | - struct snd_soc_dai *cpu_dai) |
|---|
| 134 | | -{ |
|---|
| 135 | | - struct snd_soc_pcm_runtime *rtd = substream->private_data; |
|---|
| 136 | | - struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); |
|---|
| 137 | | - |
|---|
| 138 | | - clk_disable_unprepare(dd->pclk); |
|---|
| 139 | | -} |
|---|
| 140 | | - |
|---|
| 141 | | -static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = { |
|---|
| 142 | | - .startup = atmel_classd_cpu_dai_startup, |
|---|
| 143 | | - .shutdown = atmel_classd_cpu_dai_shutdown, |
|---|
| 144 | | -}; |
|---|
| 145 | | - |
|---|
| 146 | | -static struct snd_soc_dai_driver atmel_classd_cpu_dai = { |
|---|
| 147 | | - .playback = { |
|---|
| 148 | | - .channels_min = 1, |
|---|
| 149 | | - .channels_max = 2, |
|---|
| 150 | | - .rates = ATMEL_CLASSD_RATES, |
|---|
| 151 | | - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, |
|---|
| 152 | | - .ops = &atmel_classd_cpu_dai_ops, |
|---|
| 153 | | -}; |
|---|
| 154 | | - |
|---|
| 155 | | -static const struct snd_soc_component_driver atmel_classd_cpu_dai_component = { |
|---|
| 156 | | - .name = "atmel-classd", |
|---|
| 157 | | -}; |
|---|
| 158 | 137 | |
|---|
| 159 | 138 | /* platform */ |
|---|
| 160 | 139 | static int |
|---|
| .. | .. |
|---|
| 162 | 141 | struct snd_pcm_hw_params *params, |
|---|
| 163 | 142 | struct dma_slave_config *slave_config) |
|---|
| 164 | 143 | { |
|---|
| 165 | | - struct snd_soc_pcm_runtime *rtd = substream->private_data; |
|---|
| 144 | + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); |
|---|
| 166 | 145 | struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); |
|---|
| 167 | 146 | |
|---|
| 168 | 147 | if (params_physical_width(params) != 16) { |
|---|
| .. | .. |
|---|
| 309 | 288 | return regcache_sync(dd->regmap); |
|---|
| 310 | 289 | } |
|---|
| 311 | 290 | |
|---|
| 312 | | -static struct snd_soc_component_driver soc_component_dev_classd = { |
|---|
| 313 | | - .probe = atmel_classd_component_probe, |
|---|
| 314 | | - .resume = atmel_classd_component_resume, |
|---|
| 315 | | - .controls = atmel_classd_snd_controls, |
|---|
| 316 | | - .num_controls = ARRAY_SIZE(atmel_classd_snd_controls), |
|---|
| 317 | | - .idle_bias_on = 1, |
|---|
| 318 | | - .use_pmdown_time = 1, |
|---|
| 319 | | - .endianness = 1, |
|---|
| 320 | | - .non_legacy_dai_naming = 1, |
|---|
| 321 | | -}; |
|---|
| 322 | | - |
|---|
| 323 | | -/* codec dai component */ |
|---|
| 324 | | -static int atmel_classd_codec_dai_startup(struct snd_pcm_substream *substream, |
|---|
| 325 | | - struct snd_soc_dai *codec_dai) |
|---|
| 291 | +static int atmel_classd_cpu_dai_mute_stream(struct snd_soc_dai *cpu_dai, |
|---|
| 292 | + int mute, int direction) |
|---|
| 326 | 293 | { |
|---|
| 327 | | - struct snd_soc_pcm_runtime *rtd = substream->private_data; |
|---|
| 328 | | - struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); |
|---|
| 329 | | - |
|---|
| 330 | | - return clk_prepare_enable(dd->gclk); |
|---|
| 331 | | -} |
|---|
| 332 | | - |
|---|
| 333 | | -static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai *codec_dai, |
|---|
| 334 | | - int mute) |
|---|
| 335 | | -{ |
|---|
| 336 | | - struct snd_soc_component *component = codec_dai->component; |
|---|
| 294 | + struct snd_soc_component *component = cpu_dai->component; |
|---|
| 337 | 295 | u32 mask, val; |
|---|
| 338 | 296 | |
|---|
| 339 | 297 | mask = CLASSD_MR_LMUTE_MASK | CLASSD_MR_RMUTE_MASK; |
|---|
| .. | .. |
|---|
| 376 | 334 | }; |
|---|
| 377 | 335 | |
|---|
| 378 | 336 | static int |
|---|
| 379 | | -atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream, |
|---|
| 380 | | - struct snd_pcm_hw_params *params, |
|---|
| 381 | | - struct snd_soc_dai *codec_dai) |
|---|
| 337 | +atmel_classd_cpu_dai_hw_params(struct snd_pcm_substream *substream, |
|---|
| 338 | + struct snd_pcm_hw_params *params, |
|---|
| 339 | + struct snd_soc_dai *cpu_dai) |
|---|
| 382 | 340 | { |
|---|
| 383 | | - struct snd_soc_pcm_runtime *rtd = substream->private_data; |
|---|
| 341 | + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); |
|---|
| 384 | 342 | struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); |
|---|
| 385 | | - struct snd_soc_component *component = codec_dai->component; |
|---|
| 343 | + struct snd_soc_component *component = cpu_dai->component; |
|---|
| 386 | 344 | int fs; |
|---|
| 387 | 345 | int i, best, best_val, cur_val, ret; |
|---|
| 388 | 346 | u32 mask, val; |
|---|
| .. | .. |
|---|
| 420 | 378 | } |
|---|
| 421 | 379 | |
|---|
| 422 | 380 | static void |
|---|
| 423 | | -atmel_classd_codec_dai_shutdown(struct snd_pcm_substream *substream, |
|---|
| 424 | | - struct snd_soc_dai *codec_dai) |
|---|
| 381 | +atmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream, |
|---|
| 382 | + struct snd_soc_dai *cpu_dai) |
|---|
| 425 | 383 | { |
|---|
| 426 | | - struct snd_soc_pcm_runtime *rtd = substream->private_data; |
|---|
| 384 | + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); |
|---|
| 427 | 385 | struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); |
|---|
| 428 | 386 | |
|---|
| 429 | 387 | clk_disable_unprepare(dd->gclk); |
|---|
| 430 | 388 | } |
|---|
| 431 | 389 | |
|---|
| 432 | | -static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream *substream, |
|---|
| 433 | | - struct snd_soc_dai *codec_dai) |
|---|
| 390 | +static int atmel_classd_cpu_dai_prepare(struct snd_pcm_substream *substream, |
|---|
| 391 | + struct snd_soc_dai *cpu_dai) |
|---|
| 434 | 392 | { |
|---|
| 435 | | - struct snd_soc_component *component = codec_dai->component; |
|---|
| 393 | + struct snd_soc_component *component = cpu_dai->component; |
|---|
| 436 | 394 | |
|---|
| 437 | 395 | snd_soc_component_update_bits(component, CLASSD_MR, |
|---|
| 438 | 396 | CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK, |
|---|
| .. | .. |
|---|
| 442 | 400 | return 0; |
|---|
| 443 | 401 | } |
|---|
| 444 | 402 | |
|---|
| 445 | | -static int atmel_classd_codec_dai_trigger(struct snd_pcm_substream *substream, |
|---|
| 446 | | - int cmd, struct snd_soc_dai *codec_dai) |
|---|
| 403 | +static int atmel_classd_cpu_dai_trigger(struct snd_pcm_substream *substream, |
|---|
| 404 | + int cmd, struct snd_soc_dai *cpu_dai) |
|---|
| 447 | 405 | { |
|---|
| 448 | | - struct snd_soc_component *component = codec_dai->component; |
|---|
| 406 | + struct snd_soc_component *component = cpu_dai->component; |
|---|
| 449 | 407 | u32 mask, val; |
|---|
| 450 | 408 | |
|---|
| 451 | 409 | mask = CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK; |
|---|
| .. | .. |
|---|
| 471 | 429 | return 0; |
|---|
| 472 | 430 | } |
|---|
| 473 | 431 | |
|---|
| 474 | | -static const struct snd_soc_dai_ops atmel_classd_codec_dai_ops = { |
|---|
| 475 | | - .digital_mute = atmel_classd_codec_dai_digital_mute, |
|---|
| 476 | | - .startup = atmel_classd_codec_dai_startup, |
|---|
| 477 | | - .shutdown = atmel_classd_codec_dai_shutdown, |
|---|
| 478 | | - .hw_params = atmel_classd_codec_dai_hw_params, |
|---|
| 479 | | - .prepare = atmel_classd_codec_dai_prepare, |
|---|
| 480 | | - .trigger = atmel_classd_codec_dai_trigger, |
|---|
| 432 | +static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = { |
|---|
| 433 | + .startup = atmel_classd_cpu_dai_startup, |
|---|
| 434 | + .shutdown = atmel_classd_cpu_dai_shutdown, |
|---|
| 435 | + .mute_stream = atmel_classd_cpu_dai_mute_stream, |
|---|
| 436 | + .hw_params = atmel_classd_cpu_dai_hw_params, |
|---|
| 437 | + .prepare = atmel_classd_cpu_dai_prepare, |
|---|
| 438 | + .trigger = atmel_classd_cpu_dai_trigger, |
|---|
| 439 | + .no_capture_mute = 1, |
|---|
| 481 | 440 | }; |
|---|
| 482 | 441 | |
|---|
| 483 | | -#define ATMEL_CLASSD_CODEC_DAI_NAME "atmel-classd-hifi" |
|---|
| 484 | | - |
|---|
| 485 | | -static struct snd_soc_dai_driver atmel_classd_codec_dai = { |
|---|
| 486 | | - .name = ATMEL_CLASSD_CODEC_DAI_NAME, |
|---|
| 442 | +static struct snd_soc_dai_driver atmel_classd_cpu_dai = { |
|---|
| 487 | 443 | .playback = { |
|---|
| 488 | 444 | .stream_name = "Playback", |
|---|
| 489 | 445 | .channels_min = 1, |
|---|
| .. | .. |
|---|
| 491 | 447 | .rates = ATMEL_CLASSD_RATES, |
|---|
| 492 | 448 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
|---|
| 493 | 449 | }, |
|---|
| 494 | | - .ops = &atmel_classd_codec_dai_ops, |
|---|
| 450 | + .ops = &atmel_classd_cpu_dai_ops, |
|---|
| 451 | +}; |
|---|
| 452 | + |
|---|
| 453 | +static const struct snd_soc_component_driver atmel_classd_cpu_dai_component = { |
|---|
| 454 | + .name = "atmel-classd", |
|---|
| 455 | + .probe = atmel_classd_component_probe, |
|---|
| 456 | + .resume = atmel_classd_component_resume, |
|---|
| 457 | + .controls = atmel_classd_snd_controls, |
|---|
| 458 | + .num_controls = ARRAY_SIZE(atmel_classd_snd_controls), |
|---|
| 459 | + .idle_bias_on = 1, |
|---|
| 460 | + .use_pmdown_time = 1, |
|---|
| 495 | 461 | }; |
|---|
| 496 | 462 | |
|---|
| 497 | 463 | /* ASoC sound card */ |
|---|
| .. | .. |
|---|
| 500 | 466 | { |
|---|
| 501 | 467 | struct snd_soc_dai_link *dai_link; |
|---|
| 502 | 468 | struct atmel_classd *dd = snd_soc_card_get_drvdata(card); |
|---|
| 469 | + struct snd_soc_dai_link_component *comp; |
|---|
| 503 | 470 | |
|---|
| 504 | 471 | dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL); |
|---|
| 505 | 472 | if (!dai_link) |
|---|
| 506 | 473 | return -ENOMEM; |
|---|
| 507 | 474 | |
|---|
| 475 | + comp = devm_kzalloc(dev, 3 * sizeof(*comp), GFP_KERNEL); |
|---|
| 476 | + if (!comp) |
|---|
| 477 | + return -ENOMEM; |
|---|
| 478 | + |
|---|
| 479 | + dai_link->cpus = &comp[0]; |
|---|
| 480 | + dai_link->codecs = &comp[1]; |
|---|
| 481 | + dai_link->platforms = &comp[2]; |
|---|
| 482 | + |
|---|
| 483 | + dai_link->num_cpus = 1; |
|---|
| 484 | + dai_link->num_codecs = 1; |
|---|
| 485 | + dai_link->num_platforms = 1; |
|---|
| 486 | + |
|---|
| 508 | 487 | dai_link->name = "CLASSD"; |
|---|
| 509 | 488 | dai_link->stream_name = "CLASSD PCM"; |
|---|
| 510 | | - dai_link->codec_dai_name = ATMEL_CLASSD_CODEC_DAI_NAME; |
|---|
| 511 | | - dai_link->cpu_dai_name = dev_name(dev); |
|---|
| 512 | | - dai_link->codec_name = dev_name(dev); |
|---|
| 513 | | - dai_link->platform_name = dev_name(dev); |
|---|
| 489 | + dai_link->codecs->dai_name = "snd-soc-dummy-dai"; |
|---|
| 490 | + dai_link->cpus->dai_name = dev_name(dev); |
|---|
| 491 | + dai_link->codecs->name = "snd-soc-dummy"; |
|---|
| 492 | + dai_link->platforms->name = dev_name(dev); |
|---|
| 514 | 493 | |
|---|
| 515 | 494 | card->dai_link = dai_link; |
|---|
| 516 | 495 | card->num_links = 1; |
|---|
| .. | .. |
|---|
| 561 | 540 | dd->pdata = pdata; |
|---|
| 562 | 541 | |
|---|
| 563 | 542 | dd->irq = platform_get_irq(pdev, 0); |
|---|
| 564 | | - if (dd->irq < 0) { |
|---|
| 565 | | - ret = dd->irq; |
|---|
| 566 | | - dev_err(dev, "failed to could not get irq: %d\n", ret); |
|---|
| 567 | | - return ret; |
|---|
| 568 | | - } |
|---|
| 543 | + if (dd->irq < 0) |
|---|
| 544 | + return dd->irq; |
|---|
| 569 | 545 | |
|---|
| 570 | 546 | dd->pclk = devm_clk_get(dev, "pclk"); |
|---|
| 571 | 547 | if (IS_ERR(dd->pclk)) { |
|---|
| .. | .. |
|---|
| 610 | 586 | 0); |
|---|
| 611 | 587 | if (ret) { |
|---|
| 612 | 588 | dev_err(dev, "could not register platform: %d\n", ret); |
|---|
| 613 | | - return ret; |
|---|
| 614 | | - } |
|---|
| 615 | | - |
|---|
| 616 | | - ret = devm_snd_soc_register_component(dev, &soc_component_dev_classd, |
|---|
| 617 | | - &atmel_classd_codec_dai, 1); |
|---|
| 618 | | - if (ret) { |
|---|
| 619 | | - dev_err(dev, "could not register component: %d\n", ret); |
|---|
| 620 | 589 | return ret; |
|---|
| 621 | 590 | } |
|---|
| 622 | 591 | |
|---|