[PATCH v5 0/2] ASoC: nau8325: Modify driver code and dtschema.
Revise properties description and use standard units in dtschema. The unit conversion driver based on the attribute must also be changed accordingly.
Change: V1 -> V2: - Revise the driver description part for the C++ comment. - In the nau8325_clkdet_put function, modify the max variable to hard code. - Removed "Clock Detection" switch control. - modify the "ALC Enable" switch name. - Revise the dtschema for "nuvoton,dac-vref".
V2 -> V3: - Properties use standard unit suffixes. - Modify the enum definition. - Driver code should be used dev_dbg().
V3 -> V4: - Properties use standard unit suffixes. - Modify the enum definition.
V4->V5: - Properties use standard unit suffixes for vref-impedance and dac-vref. - Revise the enum definition. - Modify the code related to properties usage in the NAU8325 codec driver. - Re-arrange header files in alphabetical order.
Seven Lee (2): ASoC: dt-bindings: Added schema for "nuvoton,nau8325" ASoC: nau8325: new driver
.../bindings/sound/nuvoton,nau8325.yaml | 80 ++ sound/soc/codecs/nau8325.c | 893 ++++++++++++++++++ sound/soc/codecs/nau8325.h | 391 ++++++++ 3 files changed, 1364 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nuvoton,nau8325.yaml create mode 100644 sound/soc/codecs/nau8325.c create mode 100644 sound/soc/codecs/nau8325.h
Add a DT schema for describing nau8325 audio amplifiers. The key features are as follows: - Low SPK_VDD Quiescent Current - Gain Setting with 2-wire interface - Powerful Stereo Class-D Amplifier - Low Output Noise - Low Current Shutdown Mode - Click-and Pop Suppression
More resources : https://www.nuvoton.com/products/smart-home-audio/audio-amplifiers/class-d-s...
Signed-off-by: Seven Lee wtli@nuvoton.com --- .../bindings/sound/nuvoton,nau8325.yaml | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nuvoton,nau8325.yaml
diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8325.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8325.yaml new file mode 100644 index 000000000000..979be0d336da --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nuvoton,nau8325.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nuvoton,nau8325.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NAU8325 audio Amplifier + +maintainers: + - Seven Lee WTLI@nuvoton.com + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: nuvoton,nau8325 + + reg: + maxItems: 1 + + nuvoton,vref-impedance-ohms: + description: + The vref impedance to be used in ohms. Middle of voltage enables + Tie-Off selection options. Due to the high impedance of the VREF + pin, it is important to use a low-leakage capacitor. + + enum: [0, 25000, 125000, 2500] + + nuvoton,dac-vref-microvolt: + description: + The DAC vref to be used in voltage. DAC reference voltage setting. Can + be used for minor tuning of the output level. Since the VDDA is range + between 1.62 to 1.98 voltage, the typical value for design is 1.8V. After + the minor tuning, the final microvolt are as the below. + + enum: [1800000, 2700000, 2880000, 3060000] + + nuvoton,alc-enable: + description: + Enable digital automatic level control (ALC) function. + type: boolean + + nuvoton,clock-detection-disable: + description: + When clock detection is enabled, it will detect whether MCLK + and FS are within the range. MCLK range is from 2.048MHz to 24.576MHz. + FS range is from 8kHz to 96kHz. And also needs to detect the ratio + MCLK_SRC/LRCK of 256, 400 or 500, and needs to detect the BCLK + to make sure data is present. There needs to be at least 8 BCLK + cycles per Frame Sync. + type: boolean + + nuvoton,clock-det-data: + description: + Request clock detection to require 2048 non-zero samples before enabling + the audio paths. If set then non-zero samples is required, otherwise it + doesn't matter. + type: boolean + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec@21 { + compatible = "nuvoton,nau8325"; + reg = <0x21>; + nuvoton,vref-impedance-ohms = <125000>; + nuvoton,dac-vref-microvolt = <2880000>; + nuvoton,alc-enable; + nuvoton,clock-det-data; + }; + };
On Wed, 27 Mar 2024 15:57:54 +0800, Seven Lee wrote:
Add a DT schema for describing nau8325 audio amplifiers. The key features are as follows:
- Low SPK_VDD Quiescent Current
- Gain Setting with 2-wire interface
- Powerful Stereo Class-D Amplifier
- Low Output Noise
- Low Current Shutdown Mode
- Click-and Pop Suppression
More resources : https://www.nuvoton.com/products/smart-home-audio/audio-amplifiers/class-d-s...
Signed-off-by: Seven Lee wtli@nuvoton.com
.../bindings/sound/nuvoton,nau8325.yaml | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nuvoton,nau8325.yaml
Reviewed-by: Rob Herring robh@kernel.org
The driver is for amplifiers NAU8325 of Nuvoton Technology Corporation. The NAU8325 is a stereo high efficiency filter-free Class-D audio amplifier, which is capable of driving a 4ohm load with up to 3W output power.
Signed-off-by: Seven Lee wtli@nuvoton.com --- sound/soc/codecs/nau8325.c | 893 +++++++++++++++++++++++++++++++++++++ sound/soc/codecs/nau8325.h | 391 ++++++++++++++++ 2 files changed, 1284 insertions(+) create mode 100644 sound/soc/codecs/nau8325.c create mode 100644 sound/soc/codecs/nau8325.h
diff --git a/sound/soc/codecs/nau8325.c b/sound/soc/codecs/nau8325.c new file mode 100644 index 000000000000..a00a30364c89 --- /dev/null +++ b/sound/soc/codecs/nau8325.c @@ -0,0 +1,893 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// nau8325.c -- Nuvoton NAU8325 audio codec driver +// +// Copyright 2023 Nuvoton Technology Crop. +// Author: Seven Lee WTLI@nuvoton.com +// David Lin CTLIN0@nuvoton.com +// + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include "nau8325.h" + +/* Range of Master Clock MCLK (Hz) */ +#define MASTER_CLK_MAX 49152000 +#define MASTER_CLK_MIN 2048000 + +/* scaling for MCLK source */ +#define CLK_PROC_BYPASS (-1) + +/* the maximum CLK_DAC */ +#define CLK_DA_AD_MAX 6144000 + +/* from MCLK input */ +#define MCLK_SRC 4 + +static const struct nau8325_src_attr mclk_n1_div[] = { + { 1, 0x0 }, + { 2, 0x1 }, + { 3, 0x2 }, +}; + +/* over sampling rate */ +static const struct nau8325_osr_attr osr_dac_sel[] = { + { 64, 2 }, /* OSR 64, SRC 1/4 */ + { 256, 0 }, /* OSR 256, SRC 1 */ + { 128, 1 }, /* OSR 128, SRC 1/2 */ + { 0, 0 }, + { 32, 3 }, /* OSR 32, SRC 1/8 */ +}; + +static const struct nau8325_src_attr mclk_n2_div[] = { + { 0, 0x0 }, + { 1, 0x1 }, + { 2, 0x2 }, + { 3, 0x3 }, + { 4, 0x4 }, +}; + +static const struct nau8325_src_attr mclk_n3_mult[] = { + { 0, 0x1 }, + { 1, 0x2 }, + { 2, 0x3 }, + { 3, 0x4 }, +}; + +/* Sample Rate and MCLK_SRC selections */ +static const struct nau8325_srate_attr target_srate_table[] = { + /* { FS, range, max, { MCLK source }} */ + { 48000, 2, true, { 12288000, 19200000, 24000000 } }, + { 16000, 1, false, { 4096000, 6400000, 8000000 } }, + { 8000, 0, false, { 2048000, 3200000, 4000000 }}, + { 44100, 2, true, { 11289600, 17640000, 22050000 }}, + { 64000, 3, false, { 16384000, 25600000, 32000000 } }, + { 96000, 3, true, { 24576000, 38400000, 48000000 } }, + { 12000, 0, true, { 3072000, 4800000, 6000000 } }, + { 24000, 1, true, { 6144000, 9600000, 12000000 } }, + { 32000, 2, false, { 8192000, 12800000, 16000000 } }, +}; + +static const struct reg_default nau8325_reg_defaults[] = { + { NAU8325_R00_HARDWARE_RST, 0x0000 }, + { NAU8325_R01_SOFTWARE_RST, 0x0000 }, + { NAU8325_R03_CLK_CTRL, 0x0000 }, + { NAU8325_R04_ENA_CTRL, 0x0000 }, + { NAU8325_R05_INTERRUPT_CTRL, 0x007f }, + { NAU8325_R09_IRQOUT, 0x0000 }, + { NAU8325_R0A_IO_CTRL, 0x0000 }, + { NAU8325_R0B_PDM_CTRL, 0x0000 }, + { NAU8325_R0C_TDM_CTRL, 0x0000 }, + { NAU8325_R0D_I2S_PCM_CTRL1, 0x000a }, + { NAU8325_R0E_I2S_PCM_CTRL2, 0x0000 }, + { NAU8325_R0F_L_TIME_SLOT, 0x0000 }, + { NAU8325_R10_R_TIME_SLOT, 0x0000 }, + { NAU8325_R11_HPF_CTRL, 0x0000 }, + { NAU8325_R12_MUTE_CTRL, 0x0000 }, + { NAU8325_R13_DAC_VOLUME, 0xf3f3 }, + { NAU8325_R29_DAC_CTRL1, 0x0081 }, + { NAU8325_R2A_DAC_CTRL2, 0x0000 }, + { NAU8325_R2C_ALC_CTRL1, 0x000e }, + { NAU8325_R2D_ALC_CTRL2, 0x8400 }, + { NAU8325_R2E_ALC_CTRL3, 0x0000 }, + { NAU8325_R2F_ALC_CTRL4, 0x003f }, + { NAU8325_R40_CLK_DET_CTRL, 0xa801 }, + { NAU8325_R50_MIXER_CTRL, 0x0000 }, + { NAU8325_R55_MISC_CTRL, 0x0000 }, + { NAU8325_R60_BIAS_ADJ, 0x0000 }, + { NAU8325_R61_ANALOG_CONTROL_1, 0x0000 }, + { NAU8325_R62_ANALOG_CONTROL_2, 0x0000 }, + { NAU8325_R63_ANALOG_CONTROL_3, 0x0000 }, + { NAU8325_R64_ANALOG_CONTROL_4, 0x0000 }, + { NAU8325_R65_ANALOG_CONTROL_5, 0x0000 }, + { NAU8325_R66_ANALOG_CONTROL_6, 0x0000 }, + { NAU8325_R69_CLIP_CTRL, 0x0000 }, + { NAU8325_R73_RDAC, 0x0008 }, +}; + +static bool nau8325_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8325_R02_DEVICE_ID ... NAU8325_R06_INT_CLR_STATUS: + case NAU8325_R09_IRQOUT ... NAU8325_R13_DAC_VOLUME: + case NAU8325_R1D_DEBUG_READ1: + case NAU8325_R1F_DEBUG_READ2: + case NAU8325_R22_DEBUG_READ3: + case NAU8325_R29_DAC_CTRL1 ... NAU8325_R2A_DAC_CTRL2: + case NAU8325_R2C_ALC_CTRL1 ... NAU8325_R2F_ALC_CTRL4: + case NAU8325_R40_CLK_DET_CTRL: + case NAU8325_R49_TEST_STATUS ... NAU8325_R4A_ANALOG_READ: + case NAU8325_R50_MIXER_CTRL: + case NAU8325_R55_MISC_CTRL: + case NAU8325_R60_BIAS_ADJ ... NAU8325_R66_ANALOG_CONTROL_6: + case NAU8325_R69_CLIP_CTRL: + case NAU8325_R73_RDAC: + return true; + default: + return false; + } +} + +static bool nau8325_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8325_R00_HARDWARE_RST: + case NAU8325_R03_CLK_CTRL ... NAU8325_R06_INT_CLR_STATUS: + case NAU8325_R09_IRQOUT ... NAU8325_R13_DAC_VOLUME: + case NAU8325_R29_DAC_CTRL1 ... NAU8325_R2A_DAC_CTRL2: + case NAU8325_R2C_ALC_CTRL1 ... NAU8325_R2F_ALC_CTRL4: + case NAU8325_R40_CLK_DET_CTRL: + case NAU8325_R50_MIXER_CTRL: + case NAU8325_R55_MISC_CTRL: + case NAU8325_R60_BIAS_ADJ ... NAU8325_R66_ANALOG_CONTROL_6: + case NAU8325_R69_CLIP_CTRL: + case NAU8325_R73_RDAC: + return true; + default: + return false; + } +} + +static bool nau8325_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8325_R00_HARDWARE_RST ... NAU8325_R02_DEVICE_ID: + case NAU8325_R06_INT_CLR_STATUS: + case NAU8325_R1D_DEBUG_READ1: + case NAU8325_R1F_DEBUG_READ2: + case NAU8325_R22_DEBUG_READ3: + case NAU8325_R4A_ANALOG_READ: + return true; + default: + return false; + } +} + +static const char * const nau8325_dac_oversampl[] = { + "64", "256", "128", "", "32" }; + +static const struct soc_enum nau8325_dac_oversampl_enum = + SOC_ENUM_SINGLE(NAU8325_R29_DAC_CTRL1, NAU8325_DAC_OVERSAMPLE_SFT, + ARRAY_SIZE(nau8325_dac_oversampl), + nau8325_dac_oversampl); + +static const DECLARE_TLV_DB_MINMAX_MUTE(dac_vol_tlv, -8000, 600); + +static const struct snd_kcontrol_new nau8325_snd_controls[] = { + SOC_ENUM("DAC Oversampling Rate", nau8325_dac_oversampl_enum), + SOC_DOUBLE_TLV("Speaker Volume", NAU8325_R13_DAC_VOLUME, + NAU8325_DAC_VOLUME_L_SFT, NAU8325_DAC_VOLUME_R_SFT, + NAU8325_DAC_VOLUME_R_EN, 0, dac_vol_tlv), + SOC_SINGLE("ALC Max Gain", NAU8325_R2C_ALC_CTRL1, + NAU8325_ALC_MAXGAIN_SFT, NAU8325_ALC_MAXGAIN_MAX, 0), + SOC_SINGLE("ALC Min Gain", NAU8325_R2C_ALC_CTRL1, + NAU8325_ALC_MINGAIN_SFT, NAU8325_ALC_MINGAIN_MAX, 0), + SOC_SINGLE("ALC Decay Timer", NAU8325_R2D_ALC_CTRL2, + NAU8325_ALC_DCY_SFT, NAU8325_ALC_DCY_MAX, 0), + SOC_SINGLE("ALC Attack Timer", NAU8325_R2D_ALC_CTRL2, + NAU8325_ALC_ATK_SFT, NAU8325_ALC_ATK_MAX, 0), + SOC_SINGLE("ALC Hold Time", NAU8325_R2D_ALC_CTRL2, + NAU8325_ALC_HLD_SFT, NAU8325_ALC_HLD_MAX, 0), + SOC_SINGLE("ALC Target Level", NAU8325_R2D_ALC_CTRL2, + NAU8325_ALC_LVL_SFT, NAU8325_ALC_LVL_MAX, 0), + SOC_SINGLE("ALC Enable Switch", NAU8325_R2E_ALC_CTRL3, + NAU8325_ALC_EN_SFT, 1, 0), +}; + +static int nau8325_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(nau8325->regmap, NAU8325_R12_MUTE_CTRL, + NAU8325_SOFT_MUTE, 0); + msleep(30); + break; + case SND_SOC_DAPM_PRE_PMD: + /* Soft mute the output to prevent the pop noise. */ + regmap_update_bits(nau8325->regmap, NAU8325_R12_MUTE_CTRL, + NAU8325_SOFT_MUTE, NAU8325_SOFT_MUTE); + msleep(30); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int nau8325_powerup_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component); + + if (nau8325->clock_detection) + return 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(nau8325->regmap, NAU8325_R40_CLK_DET_CTRL, + NAU8325_PWRUP_DFT, NAU8325_PWRUP_DFT); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(nau8325->regmap, NAU8325_R40_CLK_DET_CTRL, + NAU8325_PWRUP_DFT, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dapm_widget nau8325_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("Power Up", SND_SOC_NOPM, 0, 0, + nau8325_powerup_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("DACL", NULL, NAU8325_R04_ENA_CTRL, + NAU8325_DAC_LEFT_CH_EN_SFT, 0, nau8325_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC_E("DACR", NULL, NAU8325_R04_ENA_CTRL, + NAU8325_DAC_RIGHT_CH_EN_SFT, 0, nau8325_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_AIF_IN("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("SPKL"), + SND_SOC_DAPM_OUTPUT("SPKR"), +}; + +static const struct snd_soc_dapm_route nau8325_dapm_routes[] = { + { "DACL", NULL, "Power Up" }, + { "DACR", NULL, "Power Up" }, + + { "DACL", NULL, "AIFRX" }, + { "DACR", NULL, "AIFRX" }, + { "SPKL", NULL, "DACL" }, + { "SPKR", NULL, "DACR" }, +}; + +static int nau8325_srate_clk_apply(struct nau8325 *nau8325, + const struct nau8325_srate_attr *srate_table, + int n1_sel, int mclk_mult_sel, int n2_sel) +{ + if (!srate_table || n2_sel < 0 || n2_sel >= ARRAY_SIZE(mclk_n2_div) || + n1_sel < 0 || n1_sel >= ARRAY_SIZE(mclk_n1_div)) { + dev_dbg(nau8325->dev, "The CLK isn't supported."); + return -EINVAL; + } + + regmap_update_bits(nau8325->regmap, NAU8325_R40_CLK_DET_CTRL, + NAU8325_REG_SRATE_MASK | NAU8325_REG_DIV_MAX, + (srate_table->range << NAU8325_REG_SRATE_SFT) | + (srate_table->max ? NAU8325_REG_DIV_MAX : 0)); + regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL, + NAU8325_MCLK_SRC_MASK, mclk_n2_div[n2_sel].val); + regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL, + NAU8325_CLK_MUL_SRC_MASK, + mclk_n1_div[n1_sel].val << NAU8325_CLK_MUL_SRC_SFT); + + if (mclk_mult_sel != CLK_PROC_BYPASS) { + regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL, + NAU8325_MCLK_SEL_MASK, + mclk_n3_mult[mclk_mult_sel].val << + NAU8325_MCLK_SEL_SFT); + } else { + regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL, + NAU8325_MCLK_SEL_MASK, 0); + } + + switch (mclk_mult_sel) { + case 2: + regmap_update_bits(nau8325->regmap, NAU8325_R65_ANALOG_CONTROL_5, + NAU8325_MCLK4XEN_EN, NAU8325_MCLK4XEN_EN); + break; + case 3: + regmap_update_bits(nau8325->regmap, NAU8325_R65_ANALOG_CONTROL_5, + NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN, + NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN); + break; + default: + regmap_update_bits(nau8325->regmap, NAU8325_R65_ANALOG_CONTROL_5, + NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN, 0); + break; + } + + return 0; +} + +static int nau8325_clksrc_n2(struct nau8325 *nau8325, + const struct nau8325_srate_attr *srate_table, + int mclk, int *n2_sel) +{ + int i, mclk_src, ratio; + + ratio = NAU8325_MCLK_FS_RATIO_NUM; + for (i = 0; i < ARRAY_SIZE(mclk_n2_div); i++) { + mclk_src = mclk >> mclk_n2_div[i].param; + if (srate_table->mclk_src[NAU8325_MCLK_FS_RATIO_256] == mclk_src) { + ratio = NAU8325_MCLK_FS_RATIO_256; + break; + } else if (srate_table->mclk_src[NAU8325_MCLK_FS_RATIO_400] == mclk_src) { + ratio = NAU8325_MCLK_FS_RATIO_400; + break; + } else if (srate_table->mclk_src[NAU8325_MCLK_FS_RATIO_500] == mclk_src) { + ratio = NAU8325_MCLK_FS_RATIO_500; + break; + } + } + if (ratio != NAU8325_MCLK_FS_RATIO_NUM) + *n2_sel = i; + + return ratio; +} + +static const struct nau8325_srate_attr *target_srate_attribute(int srate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(target_srate_table); i++) + if (target_srate_table[i].fs == srate) + break; + + if (i == ARRAY_SIZE(target_srate_table)) + goto proc_err; + + return &target_srate_table[i]; + +proc_err: + return NULL; +} + +static int nau8325_clksrc_choose(struct nau8325 *nau8325, + const struct nau8325_srate_attr **srate_table, + int *n1_sel, int *mult_sel, int *n2_sel) +{ + int i, j, mclk, mclk_max, ratio, ratio_sel, n2_max; + + if (!nau8325->mclk || !nau8325->fs) + goto proc_err; + + /* select sampling rate and MCLK_SRC */ + *srate_table = target_srate_attribute(nau8325->fs); + if (!*srate_table) + goto proc_err; + + /* First check clock from MCLK directly, decide N2 for MCLK_SRC. + * If not good, consider 1/N1 and Multiplier. + */ + ratio = nau8325_clksrc_n2(nau8325, *srate_table, nau8325->mclk, n2_sel); + if (ratio != NAU8325_MCLK_FS_RATIO_NUM) { + *n1_sel = 0; + *mult_sel = CLK_PROC_BYPASS; + *n2_sel = MCLK_SRC; + goto proc_done; + } + + /* Get MCLK_SRC through 1/N, Multiplier, and then 1/N2. */ + mclk_max = 0; + for (i = 0; i < ARRAY_SIZE(mclk_n1_div); i++) { + for (j = 0; j < ARRAY_SIZE(mclk_n3_mult); j++) { + mclk = nau8325->mclk << mclk_n3_mult[j].param; + mclk = mclk / mclk_n1_div[i].param; + ratio = nau8325_clksrc_n2(nau8325, + *srate_table, mclk, n2_sel); + if (ratio != NAU8325_MCLK_FS_RATIO_NUM && + (mclk_max < mclk || i > *n1_sel)) { + mclk_max = mclk; + n2_max = *n2_sel; + *n1_sel = i; + *mult_sel = j; + ratio_sel = ratio; + goto proc_done; + } + } + } + if (mclk_max) { + *n2_sel = n2_max; + ratio = ratio_sel; + goto proc_done; + } + +proc_err: + dev_dbg(nau8325->dev, "The MCLK %d is invalid. It can't get MCLK_SRC of 256/400/500 FS (%d)", + nau8325->mclk, nau8325->fs); + return -EINVAL; +proc_done: + dev_dbg(nau8325->dev, "nau8325->fs=%d,range=0x%x, %s, (n1,mu,n2,dmu):(%d,%d,%d), MCLK_SRC=%uHz (%d)", + nau8325->fs, (*srate_table)->range, + (*srate_table)->max ? "MAX" : "MIN", + *n1_sel == CLK_PROC_BYPASS ? + CLK_PROC_BYPASS : mclk_n1_div[*n1_sel].param, + *mult_sel == CLK_PROC_BYPASS ? + CLK_PROC_BYPASS : 1 << mclk_n3_mult[*mult_sel].param, + 1 << mclk_n2_div[*n2_sel].param, + (*srate_table)->mclk_src[ratio], + (*srate_table)->mclk_src[ratio] / nau8325->fs); + + return 0; +} + +static int nau8325_clock_config(struct nau8325 *nau8325) +{ + const struct nau8325_srate_attr *srate_table; + int ret, n1_sel, mult_sel, n2_sel; + + ret = nau8325_clksrc_choose(nau8325, &srate_table, + &n1_sel, &mult_sel, &n2_sel); + if (ret) + goto err; + + ret = nau8325_srate_clk_apply(nau8325, srate_table, + n1_sel, mult_sel, n2_sel); + if (ret) + goto err; + + return 0; +err: + return ret; +} + +static const struct nau8325_osr_attr *nau8325_get_osr(struct nau8325 *nau8325) +{ + unsigned int osr; + + regmap_read(nau8325->regmap, NAU8325_R29_DAC_CTRL1, &osr); + osr &= NAU8325_DAC_OVERSAMPLE_MASK; + if (osr >= ARRAY_SIZE(osr_dac_sel)) + return NULL; + + return &osr_dac_sel[osr]; +} + +static int nau8325_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component); + const struct nau8325_osr_attr *osr; + + osr = nau8325_get_osr(nau8325); + if (!osr || !osr->osr) + return -EINVAL; + + return snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, + 0, CLK_DA_AD_MAX / osr->osr); +} + +static int nau8325_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0; + const struct nau8325_osr_attr *osr; + int ret; + + nau8325->fs = params_rate(params); + osr = nau8325_get_osr(nau8325); + if (!osr || !osr->osr || nau8325->fs * osr->osr > CLK_DA_AD_MAX) { + ret = -EINVAL; + goto err; + } + regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL, + NAU8325_CLK_DAC_SRC_MASK, + osr->clk_src << NAU8325_CLK_DAC_SRC_SFT); + + ret = nau8325_clock_config(nau8325); + if (ret) + goto err; + + switch (params_width(params)) { + case 16: + val_len |= NAU8325_I2S_DL_16; + break; + case 20: + val_len |= NAU8325_I2S_DL_20; + break; + case 24: + val_len |= NAU8325_I2S_DL_24; + break; + case 32: + val_len |= NAU8325_I2S_DL_32; + break; + default: + ret = -EINVAL; + goto err; + } + + regmap_update_bits(nau8325->regmap, NAU8325_R0D_I2S_PCM_CTRL1, + NAU8325_I2S_DL_MASK, val_len); + + return 0; + +err: + return ret; +} + +static int nau8325_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component); + unsigned int ctrl1_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl1_val |= NAU8325_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl1_val |= NAU8325_I2S_DF_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1_val |= NAU8325_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl1_val |= NAU8325_I2S_DF_RIGTH; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1_val |= NAU8325_I2S_DF_PCM_AB; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl1_val |= NAU8325_I2S_DF_PCM_AB; + ctrl1_val |= NAU8325_I2S_PCMB_EN; + break; + default: + return -EINVAL; + } + + regmap_update_bits(nau8325->regmap, NAU8325_R0D_I2S_PCM_CTRL1, + NAU8325_I2S_DF_MASK | NAU8325_I2S_BP_MASK | + NAU8325_I2S_PCMB_EN, ctrl1_val); + + return 0; +} + +static int nau8325_set_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir) +{ + struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component); + + if (freq < MASTER_CLK_MIN || freq > MASTER_CLK_MAX) { + dev_dbg(nau8325->dev, "MCLK exceeds the range, MCLK:%d", freq); + return -EINVAL; + } + + nau8325->mclk = freq; + dev_dbg(nau8325->dev, "MCLK %dHz", nau8325->mclk); + + return 0; +} + +static const struct snd_soc_component_driver nau8325_component_driver = { + .set_sysclk = nau8325_set_sysclk, + .suspend_bias_off = true, + .controls = nau8325_snd_controls, + .num_controls = ARRAY_SIZE(nau8325_snd_controls), + .dapm_widgets = nau8325_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(nau8325_dapm_widgets), + .dapm_routes = nau8325_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(nau8325_dapm_routes), +}; + +static const struct snd_soc_dai_ops nau8325_dai_ops = { + .startup = nau8325_dai_startup, + .hw_params = nau8325_hw_params, + .set_fmt = nau8325_set_fmt, +}; + +#define NAU8325_RATES SNDRV_PCM_RATE_8000_96000 +#define NAU8325_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE) + +static struct snd_soc_dai_driver nau8325_dai = { + .name = NAU8325_CODEC_DAI, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = NAU8325_RATES, + .formats = NAU8325_FORMATS, + }, + .ops = &nau8325_dai_ops, +}; + +static const struct regmap_config nau8325_regmap_config = { + .reg_bits = NAU8325_REG_ADDR_LEN, + .val_bits = NAU8325_REG_DATA_LEN, + + .max_register = NAU8325_REG_MAX, + .readable_reg = nau8325_readable_reg, + .writeable_reg = nau8325_writeable_reg, + .volatile_reg = nau8325_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = nau8325_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(nau8325_reg_defaults), +}; + +static void nau8325_reset_chip(struct regmap *regmap) +{ + regmap_write(regmap, NAU8325_R00_HARDWARE_RST, 0x0001); + regmap_write(regmap, NAU8325_R00_HARDWARE_RST, 0x0000); +} + +static void nau8325_init_regs(struct nau8325 *nau8325) +{ + struct regmap *regmap = nau8325->regmap; + struct device *dev = nau8325->dev; + + /* set ALC parameters */ + regmap_update_bits(regmap, NAU8325_R2C_ALC_CTRL1, + NAU8325_ALC_MAXGAIN_MASK, + 0x7 << NAU8325_ALC_MAXGAIN_SFT); + regmap_update_bits(regmap, NAU8325_R2D_ALC_CTRL2, + NAU8325_ALC_DCY_MASK | NAU8325_ALC_ATK_MASK | + NAU8325_ALC_HLD_MASK, (0x5 << NAU8325_ALC_DCY_SFT) | + (0x3 << NAU8325_ALC_ATK_SFT) | + (0x5 << NAU8325_ALC_HLD_SFT)); + /* Enable ALC to avoid signal distortion when battery low. */ + if (nau8325->alc_enable) + regmap_update_bits(regmap, NAU8325_R2E_ALC_CTRL3, + NAU8325_ALC_EN, NAU8325_ALC_EN); + if (nau8325->clock_detection) + regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL, + NAU8325_CLKPWRUP_DIS | + NAU8325_PWRUP_DFT, 0); + else + regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL, + NAU8325_CLKPWRUP_DIS | NAU8325_PWRUP_DFT, + NAU8325_CLKPWRUP_DIS); + if (nau8325->clock_det_data) + regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL, + NAU8325_APWRUP_EN, NAU8325_APWRUP_EN); + else + regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL, + NAU8325_APWRUP_EN, 0); + + /* DAC Reference Voltage Setting */ + switch (nau8325->dac_vref_microvolt) { + case 1800000: + regmap_update_bits(regmap, NAU8325_R73_RDAC, + NAU8325_DACVREFSEL_MASK, 0 << NAU8325_DACVREFSEL_SFT); + break; + case 2700000: + regmap_update_bits(regmap, NAU8325_R73_RDAC, + NAU8325_DACVREFSEL_MASK, 1 << NAU8325_DACVREFSEL_SFT); + break; + case 2880000: + regmap_update_bits(regmap, NAU8325_R73_RDAC, + NAU8325_DACVREFSEL_MASK, 2 << NAU8325_DACVREFSEL_SFT); + break; + case 3060000: + regmap_update_bits(regmap, NAU8325_R73_RDAC, + NAU8325_DACVREFSEL_MASK, 3 << NAU8325_DACVREFSEL_SFT); + break; + default: + dev_dbg(dev, "Invalid dac-vref-microvolt %d", nau8325->dac_vref_microvolt); + + } + + /* DAC Reference Voltage Decoupling Capacitors. */ + regmap_update_bits(regmap, NAU8325_R63_ANALOG_CONTROL_3, + NAU8325_CLASSD_COARSE_GAIN_MASK, 0x4); + /* Auto-Att Min Gain 0dB, Class-D N Driver Slew Rate -25%. */ + regmap_update_bits(regmap, NAU8325_R64_ANALOG_CONTROL_4, + NAU8325_CLASSD_SLEWN_MASK, 0x7); + + /* VMID Tieoff (VMID Resistor Selection) */ + switch (nau8325->vref_impedance_ohms) { + case 0: + regmap_update_bits(regmap, NAU8325_R60_BIAS_ADJ, + NAU8325_BIAS_VMID_SEL_MASK, 0 << NAU8325_BIAS_VMID_SEL_SFT); + break; + case 25000: + regmap_update_bits(regmap, NAU8325_R60_BIAS_ADJ, + NAU8325_BIAS_VMID_SEL_MASK, 1 << NAU8325_BIAS_VMID_SEL_SFT); + break; + case 125000: + regmap_update_bits(regmap, NAU8325_R60_BIAS_ADJ, + NAU8325_BIAS_VMID_SEL_MASK, 2 << NAU8325_BIAS_VMID_SEL_SFT); + break; + case 2500: + regmap_update_bits(regmap, NAU8325_R60_BIAS_ADJ, + NAU8325_BIAS_VMID_SEL_MASK, 3 << NAU8325_BIAS_VMID_SEL_SFT); + break; + default: + dev_dbg(dev, "Invalid vref-impedance-ohms %d", nau8325->vref_impedance_ohms); + } + + + /* enable VMID, BIAS, DAC, DCA CLOCK, Voltage/Current Amps + */ + regmap_update_bits(regmap, NAU8325_R61_ANALOG_CONTROL_1, + NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK | + NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK | + NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK | + NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK, + (0x1 << NAU8325_DACEN_SFT) | + (0x1 << NAU8325_DACCLKEN_SFT) | + (0x1 << NAU8325_DACEN_R_SFT) | + (0x1 << NAU8325_DACCLKEN_R_SFT) | + (0x1 << NAU8325_CLASSDEN_SFT) | + (0x1 << NAU8325_VMDFSTENB_SFT) | + (0x1 << NAU8325_BIASEN_SFT) | 0x3); + + /* Enable ALC to avoid signal distortion when battery low. */ + if (nau8325->alc_enable) + regmap_update_bits(regmap, NAU8325_R2E_ALC_CTRL3, + NAU8325_ALC_EN, NAU8325_ALC_EN); + if (nau8325->clock_det_data) + regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL, + NAU8325_APWRUP_EN, NAU8325_APWRUP_EN); + else + regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL, + NAU8325_APWRUP_EN, 0); + if (nau8325->clock_detection) + regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL, + NAU8325_CLKPWRUP_DIS | + NAU8325_PWRUP_DFT, 0); + else + regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL, + NAU8325_CLKPWRUP_DIS | NAU8325_PWRUP_DFT, + NAU8325_CLKPWRUP_DIS); + regmap_update_bits(regmap, NAU8325_R29_DAC_CTRL1, + NAU8325_DAC_OVERSAMPLE_MASK, + NAU8325_DAC_OVERSAMPLE_128); +} + +static void nau8325_print_device_properties(struct nau8325 *nau8325) +{ + struct device *dev = nau8325->dev; + + dev_dbg(dev, "vref-impedance-ohms: %d", nau8325->vref_impedance_ohms); + dev_dbg(dev, "dac-vref-microvolt: %d", nau8325->dac_vref_microvolt); + dev_dbg(dev, "alc-enable: %d", nau8325->alc_enable); + dev_dbg(dev, "clock-det-data: %d", nau8325->clock_det_data); + dev_dbg(dev, "clock-detection-disable: %d", nau8325->clock_detection); +} + +static int nau8325_read_device_properties(struct device *dev, + struct nau8325 *nau8325) +{ + int ret; + + nau8325->alc_enable = + device_property_read_bool(dev, "nuvoton,alc-enable"); + nau8325->clock_det_data = + device_property_read_bool(dev, "nuvoton,clock-det-data"); + nau8325->clock_detection = + !device_property_read_bool(dev, "nuvoton,clock-detection-disable"); + + ret = device_property_read_u32(dev, "nuvoton,vref-impedance-ohms", + &nau8325->vref_impedance_ohms); + if (ret) + nau8325->vref_impedance_ohms = 125000; + ret = device_property_read_u32(dev, "nuvoton,dac-vref-microvolt", + &nau8325->dac_vref_microvolt); + if (ret) + nau8325->dac_vref_microvolt = 2880000; + + return 0; +} + +static int nau8325_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct nau8325 *nau8325 = dev_get_platdata(dev); + int ret, value; + + if (!nau8325) { + nau8325 = devm_kzalloc(dev, sizeof(*nau8325), GFP_KERNEL); + if (!nau8325) { + ret = -ENOMEM; + goto err; + } + ret = nau8325_read_device_properties(dev, nau8325); + if (ret) + goto err; + } + i2c_set_clientdata(i2c, nau8325); + + nau8325->regmap = devm_regmap_init_i2c(i2c, &nau8325_regmap_config); + if (IS_ERR(nau8325->regmap)) { + ret = PTR_ERR(nau8325->regmap); + goto err; + } + nau8325->dev = dev; + nau8325_print_device_properties(nau8325); + + nau8325_reset_chip(nau8325->regmap); + ret = regmap_read(nau8325->regmap, NAU8325_R02_DEVICE_ID, &value); + if (ret) { + dev_dbg(dev, "Failed to read device id (%d)", ret); + goto err; + } + nau8325_init_regs(nau8325); + + ret = devm_snd_soc_register_component(dev, &nau8325_component_driver, + &nau8325_dai, 1); +err: + return ret; +} + +static const struct i2c_device_id nau8325_i2c_ids[] = { + { "nau8325", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nau8325_i2c_ids); + +#ifdef CONFIG_OF +static const struct of_device_id nau8325_of_ids[] = { + { .compatible = "nuvoton,nau8325", }, + {} +}; +MODULE_DEVICE_TABLE(of, nau8325_of_ids); +#endif + +static struct i2c_driver nau8325_i2c_driver = { + .driver = { + .name = "nau8325", + .of_match_table = of_match_ptr(nau8325_of_ids), + }, + .probe = nau8325_i2c_probe, + .id_table = nau8325_i2c_ids, +}; +module_i2c_driver(nau8325_i2c_driver); + +MODULE_DESCRIPTION("ASoC NAU8325 driver"); +MODULE_AUTHOR("Seven Lee WTLI@nuvoton.com"); +MODULE_AUTHOR("David Lin CTLIN0@nuvoton.com"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/nau8325.h b/sound/soc/codecs/nau8325.h new file mode 100644 index 000000000000..0d173b66a4d4 --- /dev/null +++ b/sound/soc/codecs/nau8325.h @@ -0,0 +1,391 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * nau8325.h -- Nuvoton NAU8325 audio codec driver + * + * Copyright 2023 Nuvoton Technology Crop. + * Author: Seven Lee WTLI@nuvoton.com + * David Lin CTLIN0@nuvoton.com + */ + +#ifndef __NAU8325_H__ +#define __NAU8325_H__ + +#define NAU8325_R00_HARDWARE_RST 0x00 +#define NAU8325_R01_SOFTWARE_RST 0x01 +#define NAU8325_R02_DEVICE_ID 0x02 +#define NAU8325_R03_CLK_CTRL 0x03 +#define NAU8325_R04_ENA_CTRL 0x04 +#define NAU8325_R05_INTERRUPT_CTRL 0x05 +#define NAU8325_R06_INT_CLR_STATUS 0x06 +#define NAU8325_R09_IRQOUT 0x09 +#define NAU8325_R0A_IO_CTRL 0x0a +#define NAU8325_R0B_PDM_CTRL 0x0b +#define NAU8325_R0C_TDM_CTRL 0x0c +#define NAU8325_R0D_I2S_PCM_CTRL1 0x0d +#define NAU8325_R0E_I2S_PCM_CTRL2 0x0e +#define NAU8325_R0F_L_TIME_SLOT 0x0f +#define NAU8325_R10_R_TIME_SLOT 0x10 +#define NAU8325_R11_HPF_CTRL 0x11 +#define NAU8325_R12_MUTE_CTRL 0x12 +#define NAU8325_R13_DAC_VOLUME 0x13 +#define NAU8325_R1D_DEBUG_READ1 0x1d +#define NAU8325_R1F_DEBUG_READ2 0x1f +#define NAU8325_R22_DEBUG_READ3 0x22 +#define NAU8325_R29_DAC_CTRL1 0x29 +#define NAU8325_R2A_DAC_CTRL2 0x2a +#define NAU8325_R2C_ALC_CTRL1 0x2c +#define NAU8325_R2D_ALC_CTRL2 0x2d +#define NAU8325_R2E_ALC_CTRL3 0x2e +#define NAU8325_R2F_ALC_CTRL4 0x2f +#define NAU8325_R40_CLK_DET_CTRL 0x40 +#define NAU8325_R49_TEST_STATUS 0x49 +#define NAU8325_R4A_ANALOG_READ 0x4a +#define NAU8325_R50_MIXER_CTRL 0x50 +#define NAU8325_R55_MISC_CTRL 0x55 +#define NAU8325_R60_BIAS_ADJ 0x60 +#define NAU8325_R61_ANALOG_CONTROL_1 0x61 +#define NAU8325_R62_ANALOG_CONTROL_2 0x62 +#define NAU8325_R63_ANALOG_CONTROL_3 0x63 +#define NAU8325_R64_ANALOG_CONTROL_4 0x64 +#define NAU8325_R65_ANALOG_CONTROL_5 0x65 +#define NAU8325_R66_ANALOG_CONTROL_6 0x66 +#define NAU8325_R69_CLIP_CTRL 0x69 +#define NAU8325_R73_RDAC 0x73 +#define NAU8325_REG_MAX NAU8325_R73_RDAC + +/* 16-bit control register address, and 16-bits control register data */ +#define NAU8325_REG_ADDR_LEN 16 +#define NAU8325_REG_DATA_LEN 16 + +/* CLK_CTRL (0x03) */ +#define NAU8325_CLK_DAC_SRC_SFT 12 +#define NAU8325_CLK_DAC_SRC_MASK (0x3 << NAU8325_CLK_DAC_SRC_SFT) +#define NAU8325_CLK_MUL_SRC_SFT 6 +#define NAU8325_CLK_MUL_SRC_MASK (0x3 << NAU8325_CLK_MUL_SRC_SFT) +#define NAU8325_MCLK_SEL_SFT 3 +#define NAU8325_MCLK_SEL_MASK (0x7 << NAU8325_MCLK_SEL_SFT) +#define NAU8325_MCLK_SRC_MASK 0x7 + +/* ENA_CTRL (0x04) */ +#define NAU8325_DAC_LEFT_CH_EN_SFT 3 +#define NAU8325_DAC_LEFT_CH_EN (0x1 << NAU8325_DAC_LEFT_CH_EN_SFT) +#define NAU8325_DAC_RIGHT_CH_EN_SFT 2 +#define NAU8325_DAC_RIGHT_CH_EN (0x1 << NAU8325_DAC_RIGHT_CH_EN_SFT) + +/* INTERRUPT_CTRL (0x05) */ +#define NAU8325_ARP_DWN_INT_SFT 12 +#define NAU8325_ARP_DWN_INT_MASK (0x1 << NAU8325_ARP_DWN_INT_SFT) +#define NAU8325_CLIP_INT_SFT 11 +#define NAU8325_CLIP_INT_MASK (0x1 << NAU8325_CLIP_INT_SFT) +#define NAU8325_LVD_INT_SFT 10 +#define NAU8325_LVD_INT_MASK (0x1 << NAU8325_LVD_INT_SFT) +#define NAU8325_PWR_INT_DIS_SFT 8 +#define NAU8325_PWR_INT_DIS (0x1 << NAU8325_PWR_INT_DIS_SFT) +#define NAU8325_OCP_OTP_SHTDWN_INT_SFT 4 +#define NAU8325_OCP_OTP_SHTDWN_INT_MASK (0x1 << NAU8325_OCP_OTP_SHTDWN_INT_SFT) +#define NAU8325_CLIP_INT_DIS_SFT 3 +#define NAU8325_CLIP_INT_DIS (0x1 << NAU8325_CLIP_INT_DIS_SFT) +#define NAU8325_LVD_INT_DIS_SFT 2 +#define NAU8325_LVD_INT_DIS (0x1 << NAU8325_LVD_INT_DIS_SFT) +#define NAU8325_PWR_INT_MASK 0x1 + +/* INT_CLR_STATUS (0x06) */ +#define NAU8325_INT_STATUS_MASK 0x7f + +/* IRQOUT (0x9) */ +#define NAU8325_IRQOUT_SEL_SEF 12 +#define NAU8325_IRQOUT_SEL_MASK (0xf << NAU8325_IRQOUT_SEL_SEF) +#define NAU8325_DEM_DITH_SFT 7 +#define NAU8325_DEM_DITH_EN (0x1 << NAU8325_DEM_DITH_SFT) +#define NAU8325_GAINZI3_SFT 5 +#define NAU8325_GAINZI3_MASK (0x1 << NAU8325_GAINZI3_SFT) +#define NAU8325_GAINZI2_MASK 0x1f + +/* IO_CTRL (0x0a) */ +#define NAU8325_IRQ_PL_SFT 15 +#define NAU8325_IRQ_PL_ACT_HIGH (0x1 << NAU8325_IRQ_PL_SFT) +#define NAU8325_IRQ_PS_SFT 14 +#define NAU8325_IRQ_PS_UP (0x1 << NAU8325_IRQ_PS_SFT) +#define NAU8325_IRQ_PE_SFT 13 +#define NAU8325_IRQ_PE_EN (0x1 << NAU8325_IRQ_PE_SFT) +#define NAU8325_IRQ_DS_SFT 12 +#define NAU8325_IRQ_DS_HIGH (0x1 << NAU8325_IRQ_DS_SFT) +#define NAU8325_IRQ_OUTPUT_SFT 11 +#define NAU8325_IRQ_OUTPUT_EN (0x1 << NAU8325_IRQ_OUTPUT_SFT) +#define NAU8325_IRQ_PIN_DEBUG_SFT 10 +#define NAU8325_IRQ_PIN_DEBUG_EN (0x1 << NAU8325_IRQ_PIN_DEBUG_SFT) + +/* PDM_CTRL (0x0b) */ +#define NAU8325_PDM_LCH_EDGE_SFT 1 +#define NAU8325_PDM_LCH_EDGE__MASK (0x1 << NAU8325_PDM_LCH_EDGE_SFT) +#define NAU8325_PDM_MODE_EN 0x1 + +/* TDM_CTRL (0x0c) */ +#define NAU8325_TDM_SFT 15 +#define NAU8325_TDM_EN (0x1 << NAU8325_TDM_SFT) +#define NAU8325_PCM_OFFSET_CTRL_SFT 14 +#define NAU8325_PCM_OFFSET_CTRL_EN (0x1 << NAU8325_PCM_OFFSET_CTRL_SFT) +#define NAU8325_DAC_LEFT_SFT 6 +#define NAU8325_NAU8325_DAC_LEFT_MASK (0x7 << NAU8325_DAC_LEFT_SFT) +#define NAU8325_DAC_RIGHT_SFT 3 +#define NAU8325_DAC_RIGHT_MASK (0x7 << NAU8325_DAC_RIGHT_SFT) + +/* I2S_PCM_CTRL1 (0x0d) */ +#define NAU8325_DACCM_CTL_SFT 14 +#define NAU8325_DACCM_CTL_MASK (0x3 << NAU8325_DACCM_CTL_SFT) +#define NAU8325_CMB8_0_SFT 10 +#define NAU8325_CMB8_0_MASK (0x1 << NAU8325_CMB8_0_SFT) +#define NAU8325_UA_OFFSET_SFT 9 +#define NAU8325_UA_OFFSET_MASK (0x1 << NAU8325_UA_OFFSET_SFT) +#define NAU8325_I2S_BP_SFT 7 +#define NAU8325_I2S_BP_MASK (0x1 << NAU8325_I2S_BP_SFT) +#define NAU8325_I2S_BP_INV (0x1 << NAU8325_I2S_BP_SFT) +#define NAU8325_I2S_PCMB_SFT 6 +#define NAU8325_I2S_PCMB_EN (0x1 << NAU8325_I2S_PCMB_SFT) +#define NAU8325_I2S_DACPSHS0_SFT 5 +#define NAU8325_I2S_DACPSHS0_MASK (0x1 << NAU8325_I2S_DACPSHS0_SFT) +#define NAU8325_I2S_DL_SFT 2 +#define NAU8325_I2S_DL_MASK (0x3 << NAU8325_I2S_DL_SFT) +#define NAU8325_I2S_DL_32 (0x3 << NAU8325_I2S_DL_SFT) +#define NAU8325_I2S_DL_24 (0x2 << NAU8325_I2S_DL_SFT) +#define NAU8325_I2S_DL_20 (0x1 << NAU8325_I2S_DL_SFT) +#define NAU8325_I2S_DL_16 (0x0 << NAU8325_I2S_DL_SFT) +#define NAU8325_I2S_DF_MASK 0x3 +#define NAU8325_I2S_DF_RIGTH 0x0 +#define NAU8325_I2S_DF_LEFT 0x1 +#define NAU8325_I2S_DF_I2S 0x2 +#define NAU8325_I2S_DF_PCM_AB 0x3 + +/* I2S_PCM_CTRL2 (0x0e) */ +#define NAU8325_PCM_TS_SFT 10 +#define NAU8325_PCM_TS_EN (0x1 << NAU8325_PCM_TS_SFT) +#define NAU8325_PCM8BIT0_SFT 8 +#define NAU8325_PCM8BIT0_MASK (0x1 << NAU8325_PCM8BIT0_SFT) + +/* L_TIME_SLOT (0x0f)*/ +#define NAU8325_SHORT_FS_DET_SFT 13 +#define NAU8325_SHORT_FS_DET_DIS (0x1 << NAU8325_SHORT_FS_DET_SFT) +#define NAU8325_TSLOT_L0_MASK 0x3ff + +/* R_TIME_SLOT (0x10)*/ +#define NAU8325_TSLOT_R0_MASK 0x3ff + +/* HPF_CTRL (0x11)*/ +#define NAU8325_DAC_HPF_SFT 15 +#define NAU8325_DAC_HPF_EN (0x1 << NAU8325_DAC_HPF_SFT) +#define NAU8325_DAC_HPF_APP_SFT 14 +#define NAU8325_DAC_HPF_APP_MASK (0x1 << NAU8325_DAC_HPF_APP_SFT) +#define NAU8325_DAC_HPF_FCUT_SFT 11 +#define NAU8325_DAC_HPF_FCUT_MASK (0x7 << NAU8325_DAC_HPF_FCUT_SFT) + +/* MUTE_CTRL (0x12)*/ +#define NAU8325_SOFT_MUTE_SFT 15 +#define NAU8325_SOFT_MUTE (0x1 << NAU8325_SOFT_MUTE_SFT) +#define NAU8325_DAC_ZC_SFT 8 +#define NAU8325_DAC_ZC_EN (0x1 << NAU8325_DAC_ZC_SFT) +#define NAU8325_UNMUTE_CTL_SFT 6 +#define NAU8325_UNMUTE_CTL_MASK (0x3 << NAU8325_UNMUTE_CTL_SFT) +#define NAU8325_ANA_MUTE_SFT 4 +#define NAU8325_ANA_MUTE_MASK (0x3 << NAU8325_ANA_MUTE_SFT) +#define NAU8325_AUTO_MUTE_SFT 3 +#define NAU8325_AUTO_MUTE_DIS (0x1 << NAU8325_AUTO_MUTE_SFT) + +/* DAC_VOLUME (0x13) */ +#define NAU8325_DAC_VOLUME_L_SFT 8 +#define NAU8325_DAC_VOLUME_L_EN (0xff << NAU8325_DAC_VOLUME_L_SFT) +#define NAU8325_DAC_VOLUME_R_SFT 0 +#define NAU8325_DAC_VOLUME_R_EN (0xff << NAU8325_DAC_VOLUME_R_SFT) +#define NAU8325_DAC_VOL_MAX 0xff + +/* DEBUG_READ1 (0x1d)*/ +#define NAU8325_OSR100_MASK (0x1 << 6) +#define NAU8325_MIPS500_MASK (0x1 << 5) +#define NAU8325_SHUTDWNDRVR_R_MASK (0x1 << 4) +#define NAU8325_SHUTDWNDRVR_L_MASK (0x1 << 3) +#define NAU8325_MUTEB_MASK (0x1 << 2) +#define NAU8325_PDOSCB_MASK (0x1 << 1) +#define NAU8325_POWERDOWN1B_D_MASK 0x1 + +/* DEBUG_READ2 (0x1f)*/ +#define NAU8325_R_CHANNEL_Vol_SFT 8 +#define NAU8325_R_CHANNEL_Vol_MASK (0xff << NAU8325_R_CHANNEL_Vol_SFT) +#define NAU8325_L_CHANNEL_Vol_MASK 0xff + +/* DEBUG_READ3(0x22)*/ +#define NAU8325_PGAL_GAIN_MASK (0x3f << 7) +#define NAU8325_CLIP_MASK (0x1 << 6) +#define NAU8325_SCAN_MODE_MASK (0x1 << 5) +#define NAU8325_SDB_MASK (0x1 << 4) +#define NAU8325_TALARM_MASK (0x1 << 3) +#define NAU8325_SHORTR_MASK (0x1 << 2) +#define NAU8325_SHORTL_MASK (0x1 << 1) +#define NAU8325_TMDET_MASK 0x1 + +/* DAC_CTRL1 (0x29) */ +#define NAU8325_DAC_OVERSAMPLE_SFT 0 +#define NAU8325_DAC_OVERSAMPLE_MASK 0x7 +#define NAU8325_DAC_OVERSAMPLE_256 1 +#define NAU8325_DAC_OVERSAMPLE_128 2 +#define NAU8325_DAC_OVERSAMPLE_64 0 +#define NAU8325_DAC_OVERSAMPLE_32 4 + +/* ALC_CTRL1 (0x2c) */ +#define NAU8325_ALC_MAXGAIN_SFT 5 +#define NAU8325_ALC_MAXGAIN_MAX 0x7 +#define NAU8325_ALC_MAXGAIN_MASK (0x7 << NAU8325_ALC_MAXGAIN_SFT) +#define NAU8325_ALC_MINGAIN_MAX 4 +#define NAU8325_ALC_MINGAIN_SFT 1 +#define NAU8325_ALC_MINGAIN_MASK (0x7 << NAU8325_ALC_MINGAIN_SFT) + +/* ALC_CTRL2 (0x2d) */ +#define NAU8325_ALC_DCY_SFT 12 +#define NAU8325_ALC_DCY_MAX 0xb +#define NAU8325_ALC_DCY_MASK (0xf << NAU8325_ALC_DCY_SFT) +#define NAU8325_ALC_ATK_SFT 8 +#define NAU8325_ALC_ATK_MAX 0xb +#define NAU8325_ALC_ATK_MASK (0xf << NAU8325_ALC_ATK_SFT) +#define NAU8325_ALC_HLD_SFT 4 +#define NAU8325_ALC_HLD_MAX 0xa +#define NAU8325_ALC_HLD_MASK (0xf << NAU8325_ALC_HLD_SFT) +#define NAU8325_ALC_LVL_SFT 0 +#define NAU8325_ALC_LVL_MAX 0xf +#define NAU8325_ALC_LVL_MASK 0xf + +/* ALC_CTRL3 (0x2e) */ +#define NAU8325_ALC_EN_SFT 15 +#define NAU8325_ALC_EN (0x1 << NAU8325_ALC_EN_SFT) + +/* TEMP_COMP_CTRL (0x30) */ +#define NAU8325_TEMP_COMP_ACT2_MASK 0xff + +/* LPF_CTRL (0x33) */ +#define NAU8325_LPF_IN1_EN_SFT 15 +#define NAU8325_LPF_IN1_EN (0x1 << NAU8325_LPF_IN1_EN_SFT) +#define NAU8325_LPF_IN1_TC_SFT 11 +#define NAU8325_LPF_IN1_TC_MASK (0xf << NAU8325_LPF_IN1_TC_SFT) +#define NAU8325_LPF_IN2_EN_SFT 10 +#define NAU8325_LPF_IN2_EN (0x1 << NAU8325_LPF_IN2_EN_SFT) +#define NAU8325_LPF_IN2_TC_SFT 6 +#define NAU8325_LPF_IN2_TC_MASK (0xf << NAU8325_LPF_IN2_TC_SFT) + +/* CLK_DET_CTRL (0x40) */ +#define NAU8325_APWRUP_SFT 15 +#define NAU8325_APWRUP_EN (0x1 << NAU8325_APWRUP_SFT) +#define NAU8325_CLKPWRUP_SFT 14 +#define NAU8325_CLKPWRUP_DIS (0x1 << NAU8325_CLKPWRUP_SFT) +#define NAU8325_PWRUP_DFT_SFT 13 +#define NAU8325_PWRUP_DFT (0x1 << NAU8325_PWRUP_DFT_SFT) +#define NAU8325_REG_SRATE_SFT 10 +#define NAU8325_REG_SRATE_MASK (0x7 << NAU8325_REG_SRATE_SFT) +#define NAU8325_REG_ALT_SRATE_SFT 9 +#define NAU8325_REG_ALT_SRATE_EN (0x1 << NAU8325_REG_ALT_SRATE_SFT) +#define NAU8325_REG_DIV_MAX 0x1 + +/* BIAS_ADJ (0x60) */ +#define NAU8325_BIAS_VMID_SEL_SFT 4 +#define NAU8325_BIAS_VMID_SEL_MASK (0x3 << NAU8325_BIAS_VMID_SEL_SFT) + +/* ANALOG_CONTROL_1 (0x61) */ +#define NAU8325_VMDFSTENB_SFT 14 +#define NAU8325_VMDFSTENB_MASK (0x3 << NAU8325_VMDFSTENB_SFT) +#define NAU8325_CLASSDEN_SFT 12 +#define NAU8325_CLASSDEN_MASK (0x3 << NAU8325_CLASSDEN_SFT) +#define NAU8325_DACCLKEN_R_SFT 10 +#define NAU8325_DACCLKEN_R_MASK (0x3 << NAU8325_DACCLKEN_R_SFT) +#define NAU8325_DACEN_R_SFT 8 +#define NAU8325_DACEN_R_MASK (0x3 << NAU8325_DACEN_R_SFT) +#define NAU8325_DACCLKEN_SFT 6 +#define NAU8325_DACCLKEN_MASK (0x3 << NAU8325_DACCLKEN_SFT) +#define NAU8325_DACEN_SFT 4 +#define NAU8325_DACEN_MASK (0x3 << NAU8325_DACEN_SFT) +#define NAU8325_BIASEN_SFT 2 +#define NAU8325_BIASEN_MASK (0x3 << NAU8325_BIASEN_SFT) +#define NAU8325_VMIDEN_MASK 0x3 + +/* ANALOG_CONTROL_2 (0x62) */ +#define NAU8325_PWMMOD_SFT 14 +#define NAU8325_PWMMOD_MASK (0x1 << NAU8325_PWMMOD_SFT) +#define NAU8325_DACTEST_SFT 6 +#define NAU8325_DACTEST_MASK (0x3 << NAU8325_DACTEST_SFT) +#define NAU8325_DACREFCAP_SFT 4 +#define NAU8325_DACREFCAP_MASK (0x3 << NAU8325_DACREFCAP_SFT) + +/* ANALOG_CONTROL_3 (0x63) */ +#define NAU8325_POWER_DOWN_L_SFT 12 +#define NAU8325_POWER_DOWN_L_MASK (0x3 << NAU8325_POWER_DOWN_L_SFT) +#define NAU8325_POWER_DOWN_R_SFT 11 +#define NAU8325_POWER_DOWN_R_MASK (0x3 << NAU8325_DACREFCAP_SFT) +#define NAU8325_CLASSD_FINE_SFT 5 +#define NAU8325_CLASSD_FINE_MASK (0x3 << NAU8325_CLASSD_FINE_SFT) +#define NAU8325_CLASSD_COARSE_GAIN_MASK 0xf + +/* ANALOG_CONTROL_4 (0x64) */ +#define NAU8325_CLASSD_OCPN_SFT 12 +#define NAU8325_CLASSD_OCPN_MASK (0xf << NAU8325_CLASSD_OCPN_SFT) +#define NAU8325_CLASSD_OCPP_SFT 8 +#define NAU8325_CLASSD_OCPP_MASK (0xf << NAU8325_CLASSD_OCPP_SFT) +#define NAU8325_CLASSD_SLEWN_MASK 0xff + +/* ANALOG_CONTROL_5 (0x65) */ +#define NAU8325_MCLK_RANGE_SFT 2 +#define NAU8325_MCLK_RANGE_EN (0x1 << NAU8325_MCLK_RANGE_SFT) +#define NAU8325_MCLK8XEN_SFT 1 +#define NAU8325_MCLK8XEN_EN (0x1 << NAU8325_MCLK8XEN_SFT) +#define NAU8325_MCLK4XEN_EN 0x1 + +/* ANALOG_CONTROL_6 (0x66) */ +#define NAU8325_VBATLOW_SFT 4 +#define NAU8325_VBATLOW_MASK (0x1 << NAU8325_VBATLOW_SFT) +#define NAU8325_VDDSPK_LIM_SFT 3 +#define NAU8325_VDDSPK_LIM_EN (0x1 << NAU8325_VDDSPK_LIM_SFT) +#define NAU8325_VDDSPK_LIM_MASK 0x7 + +/* CLIP_CTRL (0x69)*/ +#define NAU8325_ANTI_CLIP_SFT 4 +#define NAU8325_ANTI_CLIP_EN (0x1 << NAU8325_ANTI_CLIP_SFT) + +/* RDAC (0x73) */ +#define NAU8325_CLK_DAC_DELAY_SFT 4 +#define NAU8325_CLK_DAC_DELAY_EN (0x7 << NAU8325_CLK_DAC_DELAY_SFT) +#define NAU8325_DACVREFSEL_SFT 2 +#define NAU8325_DACVREFSEL_MASK (0x3 << NAU8325_DACVREFSEL_SFT) + +#define NAU8325_CODEC_DAI "nau8325-hifi" + +struct nau8325 { + struct device *dev; + struct regmap *regmap; + int mclk; + int fs; + int vref_impedance_ohms; + int dac_vref_microvolt; + int clock_detection; + int clock_det_data; + int alc_enable; +}; + +struct nau8325_src_attr { + int param; + unsigned int val; +}; + +enum { + NAU8325_MCLK_FS_RATIO_256, + NAU8325_MCLK_FS_RATIO_400, + NAU8325_MCLK_FS_RATIO_500, + NAU8325_MCLK_FS_RATIO_NUM, +}; + +struct nau8325_srate_attr { + int fs; + int range; + bool max; + unsigned int mclk_src[NAU8325_MCLK_FS_RATIO_NUM]; +}; + +struct nau8325_osr_attr { + unsigned int osr; + unsigned int clk_src; +}; + +#endif /* __NAU8325_H__ */
On Wed, Mar 27, 2024 at 03:57:55PM +0800, Seven Lee wrote:
+static const char * const nau8325_dac_oversampl[] = {
- "64", "256", "128", "", "32" };
+static const struct soc_enum nau8325_dac_oversampl_enum =
- SOC_ENUM_SINGLE(NAU8325_R29_DAC_CTRL1, NAU8325_DAC_OVERSAMPLE_SFT,
ARRAY_SIZE(nau8325_dac_oversampl),
nau8325_dac_oversampl);
This should really be a SOC_VALUE_ENUM so you can just hide the fourth value rather than having the empty (presumably invalid) option. Please send an incremental patch doing this.
Mark Brown 於 3/28/2024 11:22 PM 寫道:
On Wed, Mar 27, 2024 at 03:57:55PM +0800, Seven Lee wrote:
+static const char * const nau8325_dac_oversampl[] = {
- "64", "256", "128", "", "32" };
+static const struct soc_enum nau8325_dac_oversampl_enum =
- SOC_ENUM_SINGLE(NAU8325_R29_DAC_CTRL1, NAU8325_DAC_OVERSAMPLE_SFT,
ARRAY_SIZE(nau8325_dac_oversampl),
nau8325_dac_oversampl);
This should really be a SOC_VALUE_ENUM so you can just hide the fourth value rather than having the empty (presumably invalid) option. Please send an incremental patch doing this.
okay, thanks for reminding.
________________________________ ________________________________ The privileged confidential information contained in this email is intended for use only by the addressees as indicated by the original sender of this email. If you are not the addressee indicated in this email or are not responsible for delivery of the email to such a person, please kindly reply to the sender indicating this fact and delete all copies of it from your computer and network server immediately. Your cooperation is highly appreciated. It is advised that any unauthorized use of confidential information of Nuvoton is strictly prohibited; and any information in this email irrelevant to the official business of Nuvoton shall be deemed as neither given nor endorsed by Nuvoton.
On Wed, 27 Mar 2024 15:57:53 +0800, Seven Lee wrote:
Revise properties description and use standard units in dtschema. The unit conversion driver based on the attribute must also be changed accordingly.
Change: V1 -> V2:
- Revise the driver description part for the C++ comment.
- In the nau8325_clkdet_put function, modify the max variable to hard code.
- Removed "Clock Detection" switch control.
- modify the "ALC Enable" switch name.
- Revise the dtschema for "nuvoton,dac-vref".
[...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
Thanks!
[1/2] ASoC: dt-bindings: Added schema for "nuvoton,nau8325" commit: 2e4c003b3672085267a358ebf82129b4e08dd64f [2/2] ASoC: nau8325: new driver commit: c0a3873b9938bfaa77bd337cad33266a50a6583f
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
participants (4)
-
Mark Brown
-
Rob Herring
-
Seven Lee
-
WTLI