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