.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * nau8810.c -- NAU8810 ALSA Soc Audio driver |
---|
3 | 4 | * |
---|
.. | .. |
---|
6 | 7 | * Author: David Lin <ctlin0@nuvoton.com> |
---|
7 | 8 | * |
---|
8 | 9 | * Based on WM8974.c |
---|
9 | | - * |
---|
10 | | - * This program is free software; you can redistribute it and/or modify |
---|
11 | | - * it under the terms of the GNU General Public License version 2 as |
---|
12 | | - * published by the Free Software Foundation. |
---|
13 | 10 | */ |
---|
14 | 11 | |
---|
15 | 12 | #include <linux/module.h> |
---|
.. | .. |
---|
358 | 355 | |
---|
359 | 356 | /* Speaker Output Mixer */ |
---|
360 | 357 | static const struct snd_kcontrol_new nau8810_speaker_mixer_controls[] = { |
---|
| 358 | + SOC_DAPM_SINGLE("AUX Bypass Switch", NAU8810_REG_SPKMIX, |
---|
| 359 | + NAU8810_AUXSPK_SFT, 1, 0), |
---|
361 | 360 | SOC_DAPM_SINGLE("Line Bypass Switch", NAU8810_REG_SPKMIX, |
---|
362 | 361 | NAU8810_BYPSPK_SFT, 1, 0), |
---|
363 | 362 | SOC_DAPM_SINGLE("PCM Playback Switch", NAU8810_REG_SPKMIX, |
---|
.. | .. |
---|
366 | 365 | |
---|
367 | 366 | /* Mono Output Mixer */ |
---|
368 | 367 | static const struct snd_kcontrol_new nau8810_mono_mixer_controls[] = { |
---|
| 368 | + SOC_DAPM_SINGLE("AUX Bypass Switch", NAU8810_REG_MONOMIX, |
---|
| 369 | + NAU8810_AUXMOUT_SFT, 1, 0), |
---|
369 | 370 | SOC_DAPM_SINGLE("Line Bypass Switch", NAU8810_REG_MONOMIX, |
---|
370 | 371 | NAU8810_BYPMOUT_SFT, 1, 0), |
---|
371 | 372 | SOC_DAPM_SINGLE("PCM Playback Switch", NAU8810_REG_MONOMIX, |
---|
.. | .. |
---|
374 | 375 | |
---|
375 | 376 | /* PGA Mute */ |
---|
376 | 377 | static const struct snd_kcontrol_new nau8810_pgaboost_mixer_controls[] = { |
---|
| 378 | + SOC_DAPM_SINGLE("AUX PGA Switch", NAU8810_REG_ADCBOOST, |
---|
| 379 | + NAU8810_AUXBSTGAIN_SFT, 0x7, 0), |
---|
377 | 380 | SOC_DAPM_SINGLE("PGA Mute Switch", NAU8810_REG_PGAGAIN, |
---|
378 | 381 | NAU8810_PGAMT_SFT, 1, 1), |
---|
379 | 382 | SOC_DAPM_SINGLE("PMIC PGA Switch", NAU8810_REG_ADCBOOST, |
---|
.. | .. |
---|
382 | 385 | |
---|
383 | 386 | /* Input PGA */ |
---|
384 | 387 | static const struct snd_kcontrol_new nau8810_inpga[] = { |
---|
| 388 | + SOC_DAPM_SINGLE("AUX Switch", NAU8810_REG_INPUT_SIGNAL, |
---|
| 389 | + NAU8810_AUXPGA_SFT, 1, 0), |
---|
385 | 390 | SOC_DAPM_SINGLE("MicN Switch", NAU8810_REG_INPUT_SIGNAL, |
---|
386 | 391 | NAU8810_NMICPGA_SFT, 1, 0), |
---|
387 | 392 | SOC_DAPM_SINGLE("MicP Switch", NAU8810_REG_INPUT_SIGNAL, |
---|
.. | .. |
---|
402 | 407 | |
---|
403 | 408 | regmap_read(nau8810->regmap, NAU8810_REG_CLOCK, &value); |
---|
404 | 409 | return (value & NAU8810_CLKM_MASK); |
---|
| 410 | +} |
---|
| 411 | + |
---|
| 412 | +static int check_mic_enabled(struct snd_soc_dapm_widget *source, |
---|
| 413 | + struct snd_soc_dapm_widget *sink) |
---|
| 414 | +{ |
---|
| 415 | + struct snd_soc_component *component = |
---|
| 416 | + snd_soc_dapm_to_component(source->dapm); |
---|
| 417 | + struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component); |
---|
| 418 | + unsigned int value; |
---|
| 419 | + |
---|
| 420 | + regmap_read(nau8810->regmap, NAU8810_REG_INPUT_SIGNAL, &value); |
---|
| 421 | + if (value & NAU8810_PMICPGA_EN || value & NAU8810_NMICPGA_EN) |
---|
| 422 | + return 1; |
---|
| 423 | + regmap_read(nau8810->regmap, NAU8810_REG_ADCBOOST, &value); |
---|
| 424 | + if (value & NAU8810_PMICBSTGAIN_MASK) |
---|
| 425 | + return 1; |
---|
| 426 | + return 0; |
---|
405 | 427 | } |
---|
406 | 428 | |
---|
407 | 429 | static const struct snd_soc_dapm_widget nau8810_dapm_widgets[] = { |
---|
.. | .. |
---|
428 | 450 | SND_SOC_DAPM_MIXER("Input Boost Stage", NAU8810_REG_POWER2, |
---|
429 | 451 | NAU8810_BST_EN_SFT, 0, nau8810_pgaboost_mixer_controls, |
---|
430 | 452 | ARRAY_SIZE(nau8810_pgaboost_mixer_controls)), |
---|
| 453 | + SND_SOC_DAPM_PGA("AUX Input", NAU8810_REG_POWER1, |
---|
| 454 | + NAU8810_AUX_EN_SFT, 0, NULL, 0), |
---|
431 | 455 | |
---|
432 | 456 | SND_SOC_DAPM_SUPPLY("Mic Bias", NAU8810_REG_POWER1, |
---|
433 | 457 | NAU8810_MICBIAS_EN_SFT, 0, NULL, 0), |
---|
.. | .. |
---|
437 | 461 | SND_SOC_DAPM_SWITCH("Digital Loopback", SND_SOC_NOPM, 0, 0, |
---|
438 | 462 | &nau8810_loopback), |
---|
439 | 463 | |
---|
| 464 | + SND_SOC_DAPM_INPUT("AUX"), |
---|
440 | 465 | SND_SOC_DAPM_INPUT("MICN"), |
---|
441 | 466 | SND_SOC_DAPM_INPUT("MICP"), |
---|
442 | 467 | SND_SOC_DAPM_OUTPUT("MONOOUT"), |
---|
.. | .. |
---|
448 | 473 | {"DAC", NULL, "PLL", check_mclk_select_pll}, |
---|
449 | 474 | |
---|
450 | 475 | /* Mono output mixer */ |
---|
| 476 | + {"Mono Mixer", "AUX Bypass Switch", "AUX Input"}, |
---|
451 | 477 | {"Mono Mixer", "PCM Playback Switch", "DAC"}, |
---|
452 | 478 | {"Mono Mixer", "Line Bypass Switch", "Input Boost Stage"}, |
---|
453 | 479 | |
---|
454 | 480 | /* Speaker output mixer */ |
---|
| 481 | + {"Speaker Mixer", "AUX Bypass Switch", "AUX Input"}, |
---|
455 | 482 | {"Speaker Mixer", "PCM Playback Switch", "DAC"}, |
---|
456 | 483 | {"Speaker Mixer", "Line Bypass Switch", "Input Boost Stage"}, |
---|
457 | 484 | |
---|
.. | .. |
---|
466 | 493 | /* Input Boost Stage */ |
---|
467 | 494 | {"ADC", NULL, "Input Boost Stage"}, |
---|
468 | 495 | {"ADC", NULL, "PLL", check_mclk_select_pll}, |
---|
| 496 | + {"Input Boost Stage", "AUX PGA Switch", "AUX Input"}, |
---|
469 | 497 | {"Input Boost Stage", "PGA Mute Switch", "Input PGA"}, |
---|
470 | 498 | {"Input Boost Stage", "PMIC PGA Switch", "MICP"}, |
---|
471 | 499 | |
---|
472 | 500 | /* Input PGA */ |
---|
473 | | - {"Input PGA", NULL, "Mic Bias"}, |
---|
| 501 | + {"Input PGA", NULL, "Mic Bias", check_mic_enabled}, |
---|
| 502 | + {"Input PGA", "AUX Switch", "AUX Input"}, |
---|
474 | 503 | {"Input PGA", "MicN Switch", "MICN"}, |
---|
475 | 504 | {"Input PGA", "MicP Switch", "MICP"}, |
---|
| 505 | + {"AUX Input", NULL, "AUX"}, |
---|
476 | 506 | |
---|
477 | 507 | /* Digital Looptack */ |
---|
478 | 508 | {"Digital Loopback", "Switch", "ADC"}, |
---|
.. | .. |
---|
493 | 523 | return 0; |
---|
494 | 524 | } |
---|
495 | 525 | |
---|
496 | | -static int nau88l0_calc_pll(unsigned int pll_in, |
---|
| 526 | +static int nau8810_calc_pll(unsigned int pll_in, |
---|
497 | 527 | unsigned int fs, struct nau8810_pll *pll_param) |
---|
498 | 528 | { |
---|
499 | 529 | u64 f2, f2_max, pll_ratio; |
---|
.. | .. |
---|
505 | 535 | f2_max = 0; |
---|
506 | 536 | scal_sel = ARRAY_SIZE(nau8810_mclk_scaler); |
---|
507 | 537 | for (i = 0; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) { |
---|
508 | | - f2 = 256 * fs * 4 * nau8810_mclk_scaler[i] / 10; |
---|
| 538 | + f2 = 256ULL * fs * 4 * nau8810_mclk_scaler[i]; |
---|
| 539 | + f2 = div_u64(f2, 10); |
---|
509 | 540 | if (f2 > NAU_PLL_FREQ_MIN && f2 < NAU_PLL_FREQ_MAX && |
---|
510 | 541 | f2_max < f2) { |
---|
511 | 542 | f2_max = f2; |
---|
.. | .. |
---|
542 | 573 | int ret, fs; |
---|
543 | 574 | |
---|
544 | 575 | fs = freq_out / 256; |
---|
545 | | - ret = nau88l0_calc_pll(freq_in, fs, pll_param); |
---|
| 576 | + ret = nau8810_calc_pll(freq_in, fs, pll_param); |
---|
546 | 577 | if (ret < 0) { |
---|
547 | 578 | dev_err(nau8810->dev, "Unsupported input clock %d\n", freq_in); |
---|
548 | 579 | return ret; |
---|
.. | .. |
---|
667 | 698 | struct snd_soc_component *component = dai->component; |
---|
668 | 699 | struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component); |
---|
669 | 700 | int val_len = 0, val_rate = 0, ret = 0; |
---|
| 701 | + unsigned int ctrl_val, bclk_fs, bclk_div; |
---|
| 702 | + |
---|
| 703 | + /* Select BCLK configuration if the codec as master. */ |
---|
| 704 | + regmap_read(nau8810->regmap, NAU8810_REG_CLOCK, &ctrl_val); |
---|
| 705 | + if (ctrl_val & NAU8810_CLKIO_MASTER) { |
---|
| 706 | + /* get the bclk and fs ratio */ |
---|
| 707 | + bclk_fs = snd_soc_params_to_bclk(params) / params_rate(params); |
---|
| 708 | + if (bclk_fs <= 32) |
---|
| 709 | + bclk_div = NAU8810_BCLKDIV_8; |
---|
| 710 | + else if (bclk_fs <= 64) |
---|
| 711 | + bclk_div = NAU8810_BCLKDIV_4; |
---|
| 712 | + else if (bclk_fs <= 128) |
---|
| 713 | + bclk_div = NAU8810_BCLKDIV_2; |
---|
| 714 | + else |
---|
| 715 | + return -EINVAL; |
---|
| 716 | + regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK, |
---|
| 717 | + NAU8810_BCLKSEL_MASK, bclk_div); |
---|
| 718 | + } |
---|
670 | 719 | |
---|
671 | 720 | switch (params_width(params)) { |
---|
672 | 721 | case 16: |
---|
.. | .. |
---|
846 | 895 | |
---|
847 | 896 | static const struct i2c_device_id nau8810_i2c_id[] = { |
---|
848 | 897 | { "nau8810", 0 }, |
---|
| 898 | + { "nau8812", 0 }, |
---|
| 899 | + { "nau8814", 0 }, |
---|
849 | 900 | { } |
---|
850 | 901 | }; |
---|
851 | 902 | MODULE_DEVICE_TABLE(i2c, nau8810_i2c_id); |
---|
.. | .. |
---|
853 | 904 | #ifdef CONFIG_OF |
---|
854 | 905 | static const struct of_device_id nau8810_of_match[] = { |
---|
855 | 906 | { .compatible = "nuvoton,nau8810", }, |
---|
| 907 | + { .compatible = "nuvoton,nau8812", }, |
---|
| 908 | + { .compatible = "nuvoton,nau8814", }, |
---|
856 | 909 | { } |
---|
857 | 910 | }; |
---|
858 | 911 | MODULE_DEVICE_TABLE(of, nau8810_of_match); |
---|