All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] ASoC: Add support for NAU8825 codec to ASoC
@ 2015-04-16  9:56 ` Chih-Chiang Chang
  0 siblings, 0 replies; 8+ messages in thread
From: Chih-Chiang Chang @ 2015-04-16  9:56 UTC (permalink / raw)
  To: Mark Brown; +Cc: tiwai, lgirdwood, Lars-Peter Clausen, alsa-devel, linux-kernel

The NAU88L25 is an ultra-low power high performance audio codec designed
for smartphone, tablet PC, and other portable devices by Nuvoton, now
add linux driver support for it.

Signed-off-by: Chih-Chiang Chang <ccchang12@nuvoton.com>
---
v2->v1:
   - fixes according to Lars-Peter Clausen's review comments
   - removes unused platform data file
   - corrects the naming of DAPM input widget
   - fixes some wrong coding of SOC widgets and other codes
   - adds definition and remark for config FLL clock
   - moves the code of reset hardware registers from codec_probe() to i2c_probe()
   - removes unused codes

 sound/soc/codecs/Kconfig   |   5 +
 sound/soc/codecs/Makefile  |   2 +
 sound/soc/codecs/nau8825.c | 563 +++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/nau8825.h | 170 ++++++++++++++
 4 files changed, 740 insertions(+)
 create mode 100644 sound/soc/codecs/nau8825.c
 create mode 100644 sound/soc/codecs/nau8825.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 061c465..ad7e205 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -76,6 +76,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_MAX9877 if I2C
 	select SND_SOC_MC13783 if MFD_MC13XXX
 	select SND_SOC_ML26124 if I2C
+	select SND_SOC_NAU8825 if I2C
 	select SND_SOC_HDMI_CODEC
 	select SND_SOC_PCM1681 if I2C
 	select SND_SOC_PCM1792A if SPI_MASTER
@@ -468,6 +469,10 @@ config SND_SOC_MAX98925
 config SND_SOC_MAX9850
 	tristate
 
+config SND_SOC_NAU8825
+	tristate "Nuvoton NAU8825 CODEC"
+	depends on I2C
+
 config SND_SOC_PCM1681
 	tristate "Texas Instruments PCM1681 CODEC"
 	depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index abe2d7e..9663e1c 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -69,6 +69,7 @@ snd-soc-max98925-objs := max98925.o
 snd-soc-max9850-objs := max9850.o
 snd-soc-mc13783-objs := mc13783.o
 snd-soc-ml26124-objs := ml26124.o
+snd-soc-nau8825-objs := nau8825.o
 snd-soc-hdmi-codec-objs := hdmi.o
 snd-soc-pcm1681-objs := pcm1681.o
 snd-soc-pcm1792a-codec-objs := pcm1792a.o
@@ -254,6 +255,7 @@ obj-$(CONFIG_SND_SOC_MAX98925)	+= snd-soc-max98925.o
 obj-$(CONFIG_SND_SOC_MAX9850)	+= snd-soc-max9850.o
 obj-$(CONFIG_SND_SOC_MC13783)	+= snd-soc-mc13783.o
 obj-$(CONFIG_SND_SOC_ML26124)	+= snd-soc-ml26124.o
+obj-$(CONFIG_SND_SOC_NAU8825)	+= snd-soc-nau8825.o
 obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
 obj-$(CONFIG_SND_SOC_PCM1681)	+= snd-soc-pcm1681.o
 obj-$(CONFIG_SND_SOC_PCM1792A)	+= snd-soc-pcm1792a-codec.o
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
new file mode 100644
index 0000000..f19c117
--- /dev/null
+++ b/sound/soc/codecs/nau8825.c
@@ -0,0 +1,563 @@
+/*
+ * nau8825.c
+ *
+ * Copyright 2015 Nuvoton Technology Corp.
+ * Author: Meng-Huang Kuo <mhkuo@nuvoton.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/acpi.h>
+#include <asm/div64.h>
+#include <sound/jack.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/delay.h>
+#include "nau8825.h"
+
+static const DECLARE_TLV_DB_SCALE(out_hp_vol_tlv, -5400, 100, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -10350, 50, 0);
+
+static const struct snd_kcontrol_new nau8825_snd_controls[] = {
+
+	SOC_SINGLE_TLV("MIC Volume", NAU8825_ADC_DGAIN_CTRL,
+				NAU8825_ADC_DGAIN_SFT,
+				NAU8825_ADC_VOL_RSCL_RANGE, 0, adc_vol_tlv),
+	SOC_DOUBLE_TLV("HP Volume", NAU8825_HSVOL_CTRL,
+				NAU8825_L_HSVOL_SFT, NAU8825_R_HSVOL_SFT,
+				NAU8825_VOL_RSCL_RANGE, 1, out_hp_vol_tlv),
+};
+
+static const struct snd_kcontrol_new nau8825_hpo_mix[] = {
+
+	SOC_DAPM_SINGLE("HP L Switch", NAU8825_HSVOL_CTRL,
+	NAU8825_L_MUTE_SFT, 1, 1),
+	SOC_DAPM_SINGLE("HP R Switch", NAU8825_HSVOL_CTRL,
+	NAU8825_R_MUTE_SFT, 1, 1),
+};
+
+static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
+
+	/* Input */
+	SND_SOC_DAPM_INPUT("MIC"),
+	SND_SOC_DAPM_SUPPLY("MIC BIAS", NAU8825_BOOST, NAU8825_G_BIAS_SFT, 0,
+				NULL, 0),
+	/* Audio Interface */
+	SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+	/* DACs */
+	SND_SOC_DAPM_DAC("DAC L1", NULL, NAU8825_DAC_CTRL,
+				NAU8825_DAC_L_SFT, 0),
+	SND_SOC_DAPM_DAC("DAC R1", NULL, NAU8825_DAC_CTRL,
+				NAU8825_DAC_R_SFT, 0),
+	/* SPO/HPO/LOUT/Mono Mixer */
+	SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0, nau8825_hpo_mix,
+				ARRAY_SIZE(nau8825_hpo_mix)),
+	SND_SOC_DAPM_SUPPLY("HP amp", NAU8825_CLASS_G_CTRL,
+				NAU8825_CLASS_G_SHIFT, 0, NULL, 0),
+	/* Output */
+	SND_SOC_DAPM_OUTPUT("HPOL"),
+	SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
+
+	{"AIF1TX", NULL, "MIC"},
+	{"DAC L1", NULL, "AIF1RX"},
+	{"DAC R1", NULL, "AIF1RX"},
+	{"HPO MIX", "HP L Switch", "DAC L1"},
+	{"HPO MIX", "HP R Switch", "DAC R1"},
+	{"HPOL", NULL, "HPO MIX"},
+	{"HPOR", NULL, "HPO MIX"},
+	{"HPOL", NULL, "HP amp"},
+	{"HPOR", NULL, "HP amp"},
+};
+
+static int nau8825_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
+	unsigned int val_len = 0;
+
+	switch (params_width(params)) {
+	case 16:
+		break;
+	case 20:
+		val_len |= NAU8825_I2S_DL_20;
+		break;
+	case 24:
+		val_len |= NAU8825_I2S_DL_24;
+		break;
+	case 32:
+		val_len |= NAU8825_I2S_DL_32;
+		break;
+	default:
+		return -EINVAL;
+	}
+	regmap_update_bits(nau8825->regmap, NAU8825_I2S_PCM_CTRL_1,
+				NAU8825_I2S_DL_MASK, val_len);
+	return 0;
+}
+
+static int nau8825_dac_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+	if (mute)
+		regmap_update_bits(nau8825->regmap, NAU8825_DAC_MUTE_CTRL,
+				NAU8825_SOFT_MUTE_MASK, NAU8825_SOFT_MUTE_EN);
+	else
+		regmap_update_bits(nau8825->regmap, NAU8825_DAC_MUTE_CTRL,
+				NAU8825_SOFT_MUTE_MASK, NAU8825_SOFT_MUTE_DIS);
+	return 0;
+}
+
+static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
+	unsigned int reg_val = 0;
+
+	dev_dbg(codec->dev, " %s ###nau8825_set_dai_fmt %x\n", __func__, fmt);
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		reg_val |= NAU8825_I2S_BP_INV;
+		break;
+	default:
+		dev_alert(codec->dev, "Invalid DAI interface format\n");
+		return -EINVAL;
+	}
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		reg_val |= NAU8825_I2S_DF_I2S;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		reg_val |= NAU8825_I2S_DF_LEFT;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		reg_val |= NAU8825_I2S_DF_RIGHT;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		reg_val |= NAU8825_I2S_DF_PCM_A;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		reg_val |= NAU8825_I2S_DF_PCM_B;
+		reg_val |= NAU8825_I2S_PCMB_EN;
+		break;
+	default:
+		dev_alert(codec->dev, "Invalid DAI I2S/PCM format\n");
+		return -EINVAL;
+	}
+	regmap_update_bits(nau8825->regmap, NAU8825_I2S_PCM_CTRL_1,
+				NAU8825_I2S_DL_MASK | NAU8825_I2S_DF_MASK
+				| NAU8825_I2S_BP_MASK | NAU8825_I2S_PCMB_MASK,
+				reg_val);
+	return 0;
+}
+
+static void config_fll_clk_12m(struct snd_soc_codec *codec)
+{
+	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+	regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
+				NAU8825_CLK_MCLK_SRC_MASK, 0x0003);
+	regmap_update_bits(nau8825->regmap, NAU8825_FLL_1,
+				NAU8825_FLL_RATIO_MASK, 0x0001);
+	/* FLL 16-bit fractional input */
+	regmap_write(nau8825->regmap, NAU8825_FLL_2, 0xC49B);
+	/* FLL 10-bit integer input */
+	regmap_update_bits(nau8825->regmap, NAU8825_FLL_3,
+				NAU8825_FLL_INTEGER_MASK, 0x0020);
+	/* FLL pre-scaler */
+	regmap_update_bits(nau8825->regmap, NAU8825_FLL_4,
+				NAU8825_FLL_REF_DIV_MASK, 0x0800);
+	/* select divied VCO input */
+	regmap_update_bits(nau8825->regmap, NAU8825_FLL_5,
+				NAU8825_FLL_FILTER_SW_MASK, 0x0000);
+	/* FLL sigma delta modulator enable */
+	regmap_update_bits(nau8825->regmap, NAU8825_FLL_6,
+				NAU8825_SDM_EN_MASK, 0x4000);
+}
+
+static void set_sys_clk(struct snd_soc_codec *codec, int sys_clk)
+{
+	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s :: sys_clk=%x\n", __func__, sys_clk);
+	switch (sys_clk) {
+	case NAU8825_INTERNALCLOCK:
+		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
+				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_DIS);
+		regmap_update_bits(nau8825->regmap, NAU8825_FLL_6,
+				NAU8825_DCO_EN_MASK, NAU8825_DCO_EN);
+		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
+				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_EN);
+		break;
+	case NAU8825_MCLK:
+	default:
+		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
+				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_DIS);
+		regmap_update_bits(nau8825->regmap, NAU8825_FLL_6,
+				NAU8825_DCO_EN_MASK, NAU8825_DCO_DIS);
+		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
+				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_EN);
+		break;
+	}
+}
+
+static int nau8825_dai_set_sysclk(struct snd_soc_dai *dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+
+	switch (clk_id) {
+	case NAU8825_MCLK:
+		config_fll_clk_12m(codec);
+		set_sys_clk(codec, clk_id);
+		break;
+	case NAU8825_INTERNALCLOCK:
+		set_sys_clk(codec, clk_id);
+		break;
+	default:
+		dev_err(codec->dev, "Wrong clock src\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int nau8825_set_bias_level(struct snd_soc_codec *codec,
+				enum snd_soc_bias_level level)
+{
+	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		/* All power is driven by DAPM system*/
+		dev_dbg(codec->dev, "###nau8825_set_bias_level BIAS_ON\n");
+		regmap_update_bits(nau8825->regmap, NAU8825_BIAS_ADJ,
+			NAU8825_VMID_MASK, NAU8825_VMID_EN);
+		regmap_update_bits(nau8825->regmap, NAU8825_BOOST,
+				NAU8825_G_BIAS_MASK, NAU8825_G_BIAS_EN);
+		break;
+	case SND_SOC_BIAS_OFF:
+		dev_dbg(codec->dev, "###nau8825_set_bias_level OFF\n");
+		set_sys_clk(codec, NAU8825_INTERNALCLOCK);
+		regmap_update_bits(nau8825->regmap, NAU8825_BIAS_ADJ,
+				NAU8825_VMID_MASK, NAU8825_VMID_DIS);
+		regmap_update_bits(nau8825->regmap, NAU8825_BOOST,
+				NAU8825_G_BIAS_MASK, NAU8825_G_BIAS_DIS);
+		break;
+	default:
+		break;
+	}
+	codec->dapm.bias_level = level;
+	dev_dbg(codec->dev, "## nau8825_set_bias_level %d\n", level);
+	return 0;
+}
+
+static const struct reg_default nau8825_reg[] = {
+	/* enable clock source */
+	{0x0001, 0x07FF},
+	/* enable VMID and Bias */
+	{0x0076, 0x3140},
+	/* setup clock divider */
+	{0x0003, 0x0050},
+	/* jack detection configuration */
+	{0x000C, 0x0004},
+	{0x000D, 0x00E0},
+	{0x000F, 0x0801},
+	{0x0012, 0x0010},
+	/* keypad detection configuration */
+	{0x0013, 0x0280},
+	{0x0014, 0x7310},
+	{0x0015, 0x050E},
+	{0x0016, 0x1B2A},
+	/* audio format configuration */
+	{0x001A, 0x0800},
+	{0x001C, 0x000E},
+	{0x001D, 0x0010},
+	/* sampling rate control */
+	{0x002B, 0x0012},
+	/* DAC sampling rate control */
+	{0x002C, 0x0082},
+	/* ADC and DAC mixer control */
+	{0x0030, 0x00D2},
+	{0x0033, 0x00CF},
+	{0x0034, 0x02CF},
+	/* DAC class-G control */
+	{0x006A, 0x1003},
+	{0x0050, 0x2007},
+	/* ADC PGA control */
+	{0x0072, 0x0260},
+	/* DAC power down enabled */
+	{0x0080, 0x03A0},
+	/* enable DAC clock */
+	{0x0073, 0x336C},
+	/* enable MIC Bias */
+	{0x0074, 0x5502},
+	/* powerup output driver */
+	{0x007F, 0x473C},
+	{0x007F, 0x473F},
+	/* DAC power down disabled */
+	{0x0080, 0x00A0},
+	/* adjust MIC Bias */
+	{0x0066, 0x2060},
+	{0x0066, 0x2060},
+	{0x0066, 0x0060},
+};
+
+static int nau8825_codec_remove(struct snd_soc_codec *codec)
+{
+	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+	regmap_write(nau8825->regmap, NAU8825_RESET, 0);
+	regmap_write(nau8825->regmap, NAU8825_RESET, 1);
+	return 0;
+}
+
+static const struct snd_soc_codec_driver soc_codec_driver_nau8825 = {
+
+	.remove	= nau8825_codec_remove,
+	.suspend_bias_off = true,
+	.set_bias_level	= nau8825_set_bias_level,
+	.controls = nau8825_snd_controls,
+	.num_controls = ARRAY_SIZE(nau8825_snd_controls),
+	.dapm_widgets = nau8825_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(nau8825_dapm_widgets),
+	.dapm_routes = nau8825_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(nau8825_dapm_routes),
+};
+
+static bool nau8825_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case NAU8825_RESET:
+	case NAU8825_ENA_CTRL:
+	case NAU8825_CLK_EN:
+	case NAU8825_CLK_DIVIDER:
+	case NAU8825_FLL_1:
+	case NAU8825_FLL_2:
+	case NAU8825_FLL_3:
+	case NAU8825_FLL_4:
+	case NAU8825_FLL_5:
+	case NAU8825_FLL_6:
+	case NAU8825_HEADSET_CTRL:
+	case NAU8825_JACK_DET_CTRL:
+	case NAU8825_IRQ_MASK:
+	case NAU8825_IRQ_STATUS:
+	case NAU8825_IRQ_CLEAR:
+	case NAU8825_IRQ_CTRL:
+	case NAU8825_SAR_ADC:
+	case NAU8825_VDET_COEFFICIENT:
+	case NAU8825_VDET_THRESHOLD_1:
+	case NAU8825_VDET_THRESHOLD_2:
+	case NAU8825_GPIO2_CTRL:
+	case NAU8825_I2S_PCM_CTRL_1:
+	case NAU8825_I2S_PCM_CTRL_2:
+	case NAU8825_ADC_RATE:
+	case NAU8825_DAC_CTRL1:
+	case NAU8825_DAC_CTRL2:
+	case NAU8825_ADC_DGAIN_CTRL:
+	case NAU8825_DAC_MUTE_CTRL:
+	case NAU8825_HSVOL_CTRL:
+	case NAU8825_DACL_CTRL:
+	case NAU8825_DACR_CTRL:
+	case NAU8825_SAR_ADC_OUTPUT:
+	case NAU8825_ANALOG_CTRL_2:
+	case NAU8825_ANALOG_ADC_1:
+	case NAU8825_ANALOG_ADC_2:
+	case NAU8825_DAC_CTRL:
+	case NAU8825_MIC_BIAS:
+	case NAU8825_BOOST:
+	case NAU8825_CLASS_G_CTRL:
+	case NAU8825_BIAS_ADJ:
+	case NAU8825_POWER_UP_CTRL:
+	case NAU8825_CHARGE_BUMP_CTRL:
+	case NAU8825_CHARGE_BUMP_RD:
+	case NAU8825_GENERAL_STATUS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool nau8825_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case NAU8825_RESET:
+	case NAU8825_ENA_CTRL:
+	case NAU8825_CLK_EN:
+	case NAU8825_CLK_DIVIDER:
+	case NAU8825_FLL_1:
+	case NAU8825_FLL_2:
+	case NAU8825_FLL_3:
+	case NAU8825_FLL_4:
+	case NAU8825_FLL_5:
+	case NAU8825_FLL_6:
+	case NAU8825_HEADSET_CTRL:
+	case NAU8825_JACK_DET_CTRL:
+	case NAU8825_IRQ_MASK:
+	case NAU8825_IRQ_CLEAR:
+	case NAU8825_IRQ_CTRL:
+	case NAU8825_SAR_ADC:
+	case NAU8825_VDET_COEFFICIENT:
+	case NAU8825_VDET_THRESHOLD_1:
+	case NAU8825_VDET_THRESHOLD_2:
+	case NAU8825_GPIO2_CTRL:
+	case NAU8825_I2S_PCM_CTRL_1:
+	case NAU8825_I2S_PCM_CTRL_2:
+	case NAU8825_ADC_RATE:
+	case NAU8825_DAC_CTRL1:
+	case NAU8825_DAC_CTRL2:
+	case NAU8825_ADC_DGAIN_CTRL:
+	case NAU8825_DAC_MUTE_CTRL:
+	case NAU8825_HSVOL_CTRL:
+	case NAU8825_DACL_CTRL:
+	case NAU8825_DACR_CTRL:
+	case NAU8825_SAR_ADC_OUTPUT:
+	case NAU8825_ANALOG_CTRL_2:
+	case NAU8825_ANALOG_ADC_1:
+	case NAU8825_ANALOG_ADC_2:
+	case NAU8825_DAC_CTRL:
+	case NAU8825_MIC_BIAS:
+	case NAU8825_BOOST:
+	case NAU8825_CLASS_G_CTRL:
+	case NAU8825_BIAS_ADJ:
+	case NAU8825_POWER_UP_CTRL:
+	case NAU8825_CHARGE_BUMP_CTRL:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config nau8825_regmap = {
+	.reg_bits = 16,
+	.val_bits = 16,
+	.use_single_rw = true,
+	.max_register = NAU8825_MAX_REGISTER,
+	.volatile_reg = nau8825_volatile_register,
+	.readable_reg = nau8825_readable_register,
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults = nau8825_reg,
+	.num_reg_defaults = ARRAY_SIZE(nau8825_reg),
+};
+
+static const struct snd_soc_dai_ops nau8825_dai_ops = {
+	.hw_params	= nau8825_hw_params,
+	.set_sysclk	= nau8825_dai_set_sysclk,
+	.set_fmt	= nau8825_set_dai_fmt,
+	.digital_mute	= nau8825_dac_mute,
+};
+
+static struct snd_soc_dai_driver nau8825_dai_driver[] = {
+	{
+		.name = "nau8825-aif1",
+			.playback = {
+				.stream_name	 = "AIF1 Playback",
+				.channels_min	 = 1,
+				.channels_max	 = 2,
+				.rates		 = NAU8825_RATES,
+				.formats	 = NAU8825_FORMATS,
+			},
+			.capture = {
+				.stream_name	 = "AIF1 Capture",
+				.channels_min	 = 1,
+				.channels_max	 = 2,
+				.rates		 = NAU8825_RATES,
+				.formats	 = NAU8825_FORMATS,
+			},
+		.ops = &nau8825_dai_ops,
+	}
+};
+
+
+static int nau8825_i2c_probe(struct i2c_client *i2c,
+				 const struct i2c_device_id *i2c_id)
+{
+	struct nau8825_priv *nau8825;
+	int i, ret;
+
+	nau8825 = devm_kzalloc(&i2c->dev, sizeof(*nau8825),
+		GFP_KERNEL);
+	if (nau8825 == NULL)
+		return -ENOMEM;
+	nau8825->i2c = i2c;
+	i2c_set_clientdata(i2c, nau8825);
+	nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap);
+	if (IS_ERR(nau8825->regmap)) {
+		ret = PTR_ERR(nau8825->regmap);
+		dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+		goto err_enable;
+	}
+	/* software reset */
+	regmap_write(nau8825->regmap, NAU8825_RESET, 0x01);
+	regmap_write(nau8825->regmap, NAU8825_RESET, 0x02);
+	/*writing initial register values to the codec*/
+	for (i = 0; i < ARRAY_SIZE(nau8825_reg); i++)
+		regmap_write(nau8825->regmap, nau8825_reg[i].reg,
+		nau8825_reg[i].def);
+
+	ret = snd_soc_register_codec(&i2c->dev, &soc_codec_driver_nau8825,
+				nau8825_dai_driver,
+				ARRAY_SIZE(nau8825_dai_driver));
+err_enable:
+	return ret;
+}
+
+static int nau8825_i2c_remove(struct i2c_client *i2c)
+{
+	snd_soc_unregister_codec(&i2c->dev);
+	return 0;
+}
+
+static const struct i2c_device_id nau8825_i2c_id[] = {
+	{"nau8825", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, nau8825_i2c_id);
+
+static const struct acpi_device_id nau8825_acpi_match[] = {
+	{ "10508825", 0 },
+	{},
+};
+MODULE_DEVICE_TABLE(acpi, nau8825_acpi_match);
+
+static struct i2c_driver nau8825_i2c_driver = {
+	.driver = {
+		.name	= "nau8825",
+		.owner	= THIS_MODULE,
+		.acpi_match_table = ACPI_PTR(nau8825_acpi_match),
+	},
+	.probe		= nau8825_i2c_probe,
+	.remove		= (nau8825_i2c_remove),
+	.id_table	= nau8825_i2c_id,
+};
+module_i2c_driver(nau8825_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NAU8825 codec driver");
+MODULE_AUTHOR("Nuvoton");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
new file mode 100644
index 0000000..8f22e39
--- /dev/null
+++ b/sound/soc/codecs/nau8825.h
@@ -0,0 +1,170 @@
+/*
+ * nau8825.h
+ *
+ * Copyright 2015 Nuvoton Technology Corp.
+ * Author: Meng-Huang Kuo <mhkuo@nuvoton.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _NAU8825_H
+#define _NAU8825_H
+
+#define NAU8825_RESET					0x00
+#define NAU8825_ENA_CTRL				0x01
+#define NAU8825_CLK_EN					0x02
+#define NAU8825_CLK_DIVIDER				0x03
+#define NAU8825_FLL_1					0x04
+#define NAU8825_FLL_2					0x05
+#define NAU8825_FLL_3					0x06
+#define NAU8825_FLL_4					0x07
+#define NAU8825_FLL_5					0x08
+#define NAU8825_FLL_6					0x09
+#define NAU8825_HEADSET_CTRL			0x0C
+#define NAU8825_JACK_DET_CTRL			0x0D
+#define NAU8825_IRQ_MASK				0x0F
+#define NAU8825_IRQ_STATUS				0x10
+#define NAU8825_IRQ_CLEAR				0x11
+#define NAU8825_IRQ_CTRL				0x12
+#define NAU8825_SAR_ADC					0x13
+#define NAU8825_VDET_COEFFICIENT		0x14
+#define NAU8825_VDET_THRESHOLD_1		0x15
+#define NAU8825_VDET_THRESHOLD_2		0x16
+#define NAU8825_GPIO2_CTRL				0x1A
+#define NAU8825_I2S_PCM_CTRL_1			0x1C
+#define NAU8825_I2S_PCM_CTRL_2			0x1D
+#define NAU8825_ADC_RATE				0x2B
+#define NAU8825_DAC_CTRL1				0x2C
+#define NAU8825_DAC_CTRL2				0x2D
+#define NAU8825_ADC_DGAIN_CTRL			0x30
+#define NAU8825_DAC_MUTE_CTRL			0x31
+#define NAU8825_HSVOL_CTRL				0x32
+#define NAU8825_DACL_CTRL				0x33
+#define NAU8825_DACR_CTRL				0x34
+#define NAU8825_CLASS_G_CTRL			0x50
+#define NAU8825_SAR_ADC_OUTPUT			0x59
+#define NAU8825_BIAS_ADJ				0x66
+#define NAU8825_ANALOG_CTRL_2			0x6A
+#define NAU8825_ANALOG_ADC_1			0x71
+#define NAU8825_ANALOG_ADC_2			0x72
+#define NAU8825_DAC_CTRL				0x73
+#define NAU8825_MIC_BIAS				0x74
+#define NAU8825_BOOST					0x76
+#define NAU8825_POWER_UP_CTRL			0x7F
+#define NAU8825_CHARGE_BUMP_CTRL		0x80
+#define NAU8825_CHARGE_BUMP_RD			0x81
+#define NAU8825_GENERAL_STATUS			0x82
+#define NAU8825_MAX_REGISTER			0xFF
+
+/* reg. NAU8825_CLK_DIVIDER (0x03) */
+#define NAU8825_SYSCLK_EN_MASK		(0x1 << 15)
+#define NAU8825_SYSCLK_EN			(0x1 << 15)
+#define NAU8825_SYSCLK_DIS			(0x0 << 15)
+#define NAU8825_CLK_MCLK_SRC_MASK	(0xF << 0)
+
+/* reg. NAU8825_FLL_1 (0x04) */
+#define NAU8825_FLL_RATIO_MASK		(0x7F << 0)
+
+/* reg. NAU8825_FLL_3 (0x06) */
+#define NAU8825_FLL_INTEGER_MASK	(0x3FF << 0)
+
+/* reg. NAU8825_FLL_4 (0x07) */
+#define NAU8825_FLL_REF_DIV_MASK	(0x3 << 10)
+
+/* reg. NAU8825_FLL_5 (0x08) */
+#define NAU8825_FLL_FILTER_SW_MASK	(0x1 << 14)
+
+/* reg. NAU8825_FLL_6 (0x09) */
+#define NAU8825_DCO_EN_MASK			(0x1 << 15)
+#define NAU8825_DCO_EN				(0x1 << 15)
+#define NAU8825_DCO_DIS				(0x0 << 15)
+#define NAU8825_SDM_EN_MASK			(0x1 << 14)
+
+/* reg. NAU8825_I2S_PCM_CTRL_1 (0x1C) */
+#define NAU8825_I2S_BP_MASK			(0x1 << 7)
+#define NAU8825_I2S_BP_INV			(0x1 << 7)
+#define NAU8825_I2S_PCMB_MASK		(0x1 << 6)
+#define NAU8825_I2S_PCMB_EN			(0x1 << 6)
+#define NAU8825_I2S_DL_MASK			(0x3 << 2)
+#define NAU8825_I2S_DF_MASK				0x3
+#define NAU8825_I2S_DF_RIGHT			0x0
+#define NAU8825_I2S_DF_LEFT				0x1
+#define NAU8825_I2S_DF_I2S				0x2
+#define NAU8825_I2S_DF_PCM_A			0x3
+#define NAU8825_I2S_DF_PCM_B			0x3
+
+/* reg. NAU8825_I2S_PCM_CTRL_2 (0x1D) */
+#define NAU8825_I2S_MS_MASK			(0x1 << 3)
+
+/* reg. NAU8825_ADC_DGAIN_CTRL (0x30) */
+#define NAU8825_ADC_DGAIN_SFT			0
+
+/* reg. NAU8825_DAC_MUTE_CTRL (0x31) */
+#define NAU8825_SOFT_MUTE_MASK		(0x1 << 13)
+#define NAU8825_SOFT_MUTE_EN		(0x1 << 13)
+#define NAU8825_SOFT_MUTE_DIS		(0x0 << 13)
+
+/* reg. NAU8825_HSVOL_CTRL (0x32) */
+#define NAU8825_R_MUTE				(0x1 << 15)
+#define NAU8825_R_MUTE_SFT				15
+#define NAU8825_L_MUTE				(0x1 << 14)
+#define NAU8825_L_MUTE_SFT				14
+#define NAU8825_L_HSVOL_SFT				6
+#define NAU8825_R_HSVOL_SFT				0
+
+/* reg. NAU8825_CLASS_G_CTRL (0x50) */
+#define NAU8825_CLASS_G_RIGHT_SHIFT		2
+#define NAU8825_CLASS_G_LEFT_SHIFT		1
+#define NAU8825_CLASS_G_SHIFT			0
+
+/* reg. NAU8825_BIAS_ADJ_2 (0x66) */
+#define NAU8825_VMID_MASK			(0x1 << 6)
+#define NAU8825_VMID_EN				(0x1 << 6)
+#define NAU8825_VMID_DIS			(0x0 << 6)
+
+/* reg. NAU8825_ANALOG_CTRL_2 (0x6A) */
+#define NAU8825_VMID_MASK			(0x1 << 6)
+#define NAU8825_VMID_EN				(0x1 << 6)
+#define NAU8825_VMID_DIS			(0x0 << 6)
+
+/* reg. NAU8825_DAC_CTRL (0x73) */
+#define NAU8825_DAC_R_SFT				13
+#define NAU8825_DAC_L_SFT				12
+#define NAU8825_DAC_CLK_R_SFT			9
+#define NAU8825_DAC_CLK_L_SFT			8
+
+/* reg. NAU8825_BOOST (0x76) */
+#define NAU8825_G_BIAS_MASK			(0x1 << 12)
+#define NAU8825_G_BIAS_SFT				12
+#define NAU8825_G_BIAS_EN			(0x1 << 12)
+#define NAU8825_G_BIAS_DIS			(0x0 << 12)
+
+/* Volume Rescale */
+#define NAU8825_VOL_RSCL_RANGE			0x36
+#define NAU8825_ADC_VOL_RSCL_RANGE		0xFF
+
+/* Data format */
+#define NAU8825_I2S_DL_16			(0x0 << 2)
+#define NAU8825_I2S_DL_20			(0x1 << 2)
+#define NAU8825_I2S_DL_24			(0x2 << 2)
+#define NAU8825_I2S_DL_32			(0x3 << 2)
+
+enum {
+	NAU8825_INTERNALCLOCK = 0,
+	NAU8825_MCLK,
+};
+
+#define NAU8825_RATES	SNDRV_PCM_RATE_8000_192000
+#define NAU8825_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+			 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct nau8825_priv {
+	struct snd_soc_codec *codec;
+	struct regmap *regmap;
+	struct i2c_client *i2c;
+	struct snd_soc_jack *jack;
+	struct delayed_work jack_detect_work;
+};
+#endif	/* _NAU8825_H */
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v2] ASoC: Add support for NAU8825 codec to ASoC
@ 2015-04-16  9:56 ` Chih-Chiang Chang
  0 siblings, 0 replies; 8+ messages in thread
From: Chih-Chiang Chang @ 2015-04-16  9:56 UTC (permalink / raw)
  To: Mark Brown; +Cc: tiwai, alsa-devel, Lars-Peter Clausen, lgirdwood, linux-kernel

The NAU88L25 is an ultra-low power high performance audio codec designed
for smartphone, tablet PC, and other portable devices by Nuvoton, now
add linux driver support for it.

Signed-off-by: Chih-Chiang Chang <ccchang12@nuvoton.com>
---
v2->v1:
   - fixes according to Lars-Peter Clausen's review comments
   - removes unused platform data file
   - corrects the naming of DAPM input widget
   - fixes some wrong coding of SOC widgets and other codes
   - adds definition and remark for config FLL clock
   - moves the code of reset hardware registers from codec_probe() to i2c_probe()
   - removes unused codes

 sound/soc/codecs/Kconfig   |   5 +
 sound/soc/codecs/Makefile  |   2 +
 sound/soc/codecs/nau8825.c | 563 +++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/nau8825.h | 170 ++++++++++++++
 4 files changed, 740 insertions(+)
 create mode 100644 sound/soc/codecs/nau8825.c
 create mode 100644 sound/soc/codecs/nau8825.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 061c465..ad7e205 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -76,6 +76,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_MAX9877 if I2C
 	select SND_SOC_MC13783 if MFD_MC13XXX
 	select SND_SOC_ML26124 if I2C
+	select SND_SOC_NAU8825 if I2C
 	select SND_SOC_HDMI_CODEC
 	select SND_SOC_PCM1681 if I2C
 	select SND_SOC_PCM1792A if SPI_MASTER
@@ -468,6 +469,10 @@ config SND_SOC_MAX98925
 config SND_SOC_MAX9850
 	tristate
 
+config SND_SOC_NAU8825
+	tristate "Nuvoton NAU8825 CODEC"
+	depends on I2C
+
 config SND_SOC_PCM1681
 	tristate "Texas Instruments PCM1681 CODEC"
 	depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index abe2d7e..9663e1c 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -69,6 +69,7 @@ snd-soc-max98925-objs := max98925.o
 snd-soc-max9850-objs := max9850.o
 snd-soc-mc13783-objs := mc13783.o
 snd-soc-ml26124-objs := ml26124.o
+snd-soc-nau8825-objs := nau8825.o
 snd-soc-hdmi-codec-objs := hdmi.o
 snd-soc-pcm1681-objs := pcm1681.o
 snd-soc-pcm1792a-codec-objs := pcm1792a.o
@@ -254,6 +255,7 @@ obj-$(CONFIG_SND_SOC_MAX98925)	+= snd-soc-max98925.o
 obj-$(CONFIG_SND_SOC_MAX9850)	+= snd-soc-max9850.o
 obj-$(CONFIG_SND_SOC_MC13783)	+= snd-soc-mc13783.o
 obj-$(CONFIG_SND_SOC_ML26124)	+= snd-soc-ml26124.o
+obj-$(CONFIG_SND_SOC_NAU8825)	+= snd-soc-nau8825.o
 obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
 obj-$(CONFIG_SND_SOC_PCM1681)	+= snd-soc-pcm1681.o
 obj-$(CONFIG_SND_SOC_PCM1792A)	+= snd-soc-pcm1792a-codec.o
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
new file mode 100644
index 0000000..f19c117
--- /dev/null
+++ b/sound/soc/codecs/nau8825.c
@@ -0,0 +1,563 @@
+/*
+ * nau8825.c
+ *
+ * Copyright 2015 Nuvoton Technology Corp.
+ * Author: Meng-Huang Kuo <mhkuo@nuvoton.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/acpi.h>
+#include <asm/div64.h>
+#include <sound/jack.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/delay.h>
+#include "nau8825.h"
+
+static const DECLARE_TLV_DB_SCALE(out_hp_vol_tlv, -5400, 100, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -10350, 50, 0);
+
+static const struct snd_kcontrol_new nau8825_snd_controls[] = {
+
+	SOC_SINGLE_TLV("MIC Volume", NAU8825_ADC_DGAIN_CTRL,
+				NAU8825_ADC_DGAIN_SFT,
+				NAU8825_ADC_VOL_RSCL_RANGE, 0, adc_vol_tlv),
+	SOC_DOUBLE_TLV("HP Volume", NAU8825_HSVOL_CTRL,
+				NAU8825_L_HSVOL_SFT, NAU8825_R_HSVOL_SFT,
+				NAU8825_VOL_RSCL_RANGE, 1, out_hp_vol_tlv),
+};
+
+static const struct snd_kcontrol_new nau8825_hpo_mix[] = {
+
+	SOC_DAPM_SINGLE("HP L Switch", NAU8825_HSVOL_CTRL,
+	NAU8825_L_MUTE_SFT, 1, 1),
+	SOC_DAPM_SINGLE("HP R Switch", NAU8825_HSVOL_CTRL,
+	NAU8825_R_MUTE_SFT, 1, 1),
+};
+
+static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
+
+	/* Input */
+	SND_SOC_DAPM_INPUT("MIC"),
+	SND_SOC_DAPM_SUPPLY("MIC BIAS", NAU8825_BOOST, NAU8825_G_BIAS_SFT, 0,
+				NULL, 0),
+	/* Audio Interface */
+	SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+	/* DACs */
+	SND_SOC_DAPM_DAC("DAC L1", NULL, NAU8825_DAC_CTRL,
+				NAU8825_DAC_L_SFT, 0),
+	SND_SOC_DAPM_DAC("DAC R1", NULL, NAU8825_DAC_CTRL,
+				NAU8825_DAC_R_SFT, 0),
+	/* SPO/HPO/LOUT/Mono Mixer */
+	SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0, nau8825_hpo_mix,
+				ARRAY_SIZE(nau8825_hpo_mix)),
+	SND_SOC_DAPM_SUPPLY("HP amp", NAU8825_CLASS_G_CTRL,
+				NAU8825_CLASS_G_SHIFT, 0, NULL, 0),
+	/* Output */
+	SND_SOC_DAPM_OUTPUT("HPOL"),
+	SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
+
+	{"AIF1TX", NULL, "MIC"},
+	{"DAC L1", NULL, "AIF1RX"},
+	{"DAC R1", NULL, "AIF1RX"},
+	{"HPO MIX", "HP L Switch", "DAC L1"},
+	{"HPO MIX", "HP R Switch", "DAC R1"},
+	{"HPOL", NULL, "HPO MIX"},
+	{"HPOR", NULL, "HPO MIX"},
+	{"HPOL", NULL, "HP amp"},
+	{"HPOR", NULL, "HP amp"},
+};
+
+static int nau8825_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
+	unsigned int val_len = 0;
+
+	switch (params_width(params)) {
+	case 16:
+		break;
+	case 20:
+		val_len |= NAU8825_I2S_DL_20;
+		break;
+	case 24:
+		val_len |= NAU8825_I2S_DL_24;
+		break;
+	case 32:
+		val_len |= NAU8825_I2S_DL_32;
+		break;
+	default:
+		return -EINVAL;
+	}
+	regmap_update_bits(nau8825->regmap, NAU8825_I2S_PCM_CTRL_1,
+				NAU8825_I2S_DL_MASK, val_len);
+	return 0;
+}
+
+static int nau8825_dac_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+	if (mute)
+		regmap_update_bits(nau8825->regmap, NAU8825_DAC_MUTE_CTRL,
+				NAU8825_SOFT_MUTE_MASK, NAU8825_SOFT_MUTE_EN);
+	else
+		regmap_update_bits(nau8825->regmap, NAU8825_DAC_MUTE_CTRL,
+				NAU8825_SOFT_MUTE_MASK, NAU8825_SOFT_MUTE_DIS);
+	return 0;
+}
+
+static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
+	unsigned int reg_val = 0;
+
+	dev_dbg(codec->dev, " %s ###nau8825_set_dai_fmt %x\n", __func__, fmt);
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		reg_val |= NAU8825_I2S_BP_INV;
+		break;
+	default:
+		dev_alert(codec->dev, "Invalid DAI interface format\n");
+		return -EINVAL;
+	}
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		reg_val |= NAU8825_I2S_DF_I2S;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		reg_val |= NAU8825_I2S_DF_LEFT;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		reg_val |= NAU8825_I2S_DF_RIGHT;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		reg_val |= NAU8825_I2S_DF_PCM_A;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		reg_val |= NAU8825_I2S_DF_PCM_B;
+		reg_val |= NAU8825_I2S_PCMB_EN;
+		break;
+	default:
+		dev_alert(codec->dev, "Invalid DAI I2S/PCM format\n");
+		return -EINVAL;
+	}
+	regmap_update_bits(nau8825->regmap, NAU8825_I2S_PCM_CTRL_1,
+				NAU8825_I2S_DL_MASK | NAU8825_I2S_DF_MASK
+				| NAU8825_I2S_BP_MASK | NAU8825_I2S_PCMB_MASK,
+				reg_val);
+	return 0;
+}
+
+static void config_fll_clk_12m(struct snd_soc_codec *codec)
+{
+	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+	regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
+				NAU8825_CLK_MCLK_SRC_MASK, 0x0003);
+	regmap_update_bits(nau8825->regmap, NAU8825_FLL_1,
+				NAU8825_FLL_RATIO_MASK, 0x0001);
+	/* FLL 16-bit fractional input */
+	regmap_write(nau8825->regmap, NAU8825_FLL_2, 0xC49B);
+	/* FLL 10-bit integer input */
+	regmap_update_bits(nau8825->regmap, NAU8825_FLL_3,
+				NAU8825_FLL_INTEGER_MASK, 0x0020);
+	/* FLL pre-scaler */
+	regmap_update_bits(nau8825->regmap, NAU8825_FLL_4,
+				NAU8825_FLL_REF_DIV_MASK, 0x0800);
+	/* select divied VCO input */
+	regmap_update_bits(nau8825->regmap, NAU8825_FLL_5,
+				NAU8825_FLL_FILTER_SW_MASK, 0x0000);
+	/* FLL sigma delta modulator enable */
+	regmap_update_bits(nau8825->regmap, NAU8825_FLL_6,
+				NAU8825_SDM_EN_MASK, 0x4000);
+}
+
+static void set_sys_clk(struct snd_soc_codec *codec, int sys_clk)
+{
+	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s :: sys_clk=%x\n", __func__, sys_clk);
+	switch (sys_clk) {
+	case NAU8825_INTERNALCLOCK:
+		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
+				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_DIS);
+		regmap_update_bits(nau8825->regmap, NAU8825_FLL_6,
+				NAU8825_DCO_EN_MASK, NAU8825_DCO_EN);
+		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
+				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_EN);
+		break;
+	case NAU8825_MCLK:
+	default:
+		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
+				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_DIS);
+		regmap_update_bits(nau8825->regmap, NAU8825_FLL_6,
+				NAU8825_DCO_EN_MASK, NAU8825_DCO_DIS);
+		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
+				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_EN);
+		break;
+	}
+}
+
+static int nau8825_dai_set_sysclk(struct snd_soc_dai *dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+
+	switch (clk_id) {
+	case NAU8825_MCLK:
+		config_fll_clk_12m(codec);
+		set_sys_clk(codec, clk_id);
+		break;
+	case NAU8825_INTERNALCLOCK:
+		set_sys_clk(codec, clk_id);
+		break;
+	default:
+		dev_err(codec->dev, "Wrong clock src\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int nau8825_set_bias_level(struct snd_soc_codec *codec,
+				enum snd_soc_bias_level level)
+{
+	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		/* All power is driven by DAPM system*/
+		dev_dbg(codec->dev, "###nau8825_set_bias_level BIAS_ON\n");
+		regmap_update_bits(nau8825->regmap, NAU8825_BIAS_ADJ,
+			NAU8825_VMID_MASK, NAU8825_VMID_EN);
+		regmap_update_bits(nau8825->regmap, NAU8825_BOOST,
+				NAU8825_G_BIAS_MASK, NAU8825_G_BIAS_EN);
+		break;
+	case SND_SOC_BIAS_OFF:
+		dev_dbg(codec->dev, "###nau8825_set_bias_level OFF\n");
+		set_sys_clk(codec, NAU8825_INTERNALCLOCK);
+		regmap_update_bits(nau8825->regmap, NAU8825_BIAS_ADJ,
+				NAU8825_VMID_MASK, NAU8825_VMID_DIS);
+		regmap_update_bits(nau8825->regmap, NAU8825_BOOST,
+				NAU8825_G_BIAS_MASK, NAU8825_G_BIAS_DIS);
+		break;
+	default:
+		break;
+	}
+	codec->dapm.bias_level = level;
+	dev_dbg(codec->dev, "## nau8825_set_bias_level %d\n", level);
+	return 0;
+}
+
+static const struct reg_default nau8825_reg[] = {
+	/* enable clock source */
+	{0x0001, 0x07FF},
+	/* enable VMID and Bias */
+	{0x0076, 0x3140},
+	/* setup clock divider */
+	{0x0003, 0x0050},
+	/* jack detection configuration */
+	{0x000C, 0x0004},
+	{0x000D, 0x00E0},
+	{0x000F, 0x0801},
+	{0x0012, 0x0010},
+	/* keypad detection configuration */
+	{0x0013, 0x0280},
+	{0x0014, 0x7310},
+	{0x0015, 0x050E},
+	{0x0016, 0x1B2A},
+	/* audio format configuration */
+	{0x001A, 0x0800},
+	{0x001C, 0x000E},
+	{0x001D, 0x0010},
+	/* sampling rate control */
+	{0x002B, 0x0012},
+	/* DAC sampling rate control */
+	{0x002C, 0x0082},
+	/* ADC and DAC mixer control */
+	{0x0030, 0x00D2},
+	{0x0033, 0x00CF},
+	{0x0034, 0x02CF},
+	/* DAC class-G control */
+	{0x006A, 0x1003},
+	{0x0050, 0x2007},
+	/* ADC PGA control */
+	{0x0072, 0x0260},
+	/* DAC power down enabled */
+	{0x0080, 0x03A0},
+	/* enable DAC clock */
+	{0x0073, 0x336C},
+	/* enable MIC Bias */
+	{0x0074, 0x5502},
+	/* powerup output driver */
+	{0x007F, 0x473C},
+	{0x007F, 0x473F},
+	/* DAC power down disabled */
+	{0x0080, 0x00A0},
+	/* adjust MIC Bias */
+	{0x0066, 0x2060},
+	{0x0066, 0x2060},
+	{0x0066, 0x0060},
+};
+
+static int nau8825_codec_remove(struct snd_soc_codec *codec)
+{
+	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+	regmap_write(nau8825->regmap, NAU8825_RESET, 0);
+	regmap_write(nau8825->regmap, NAU8825_RESET, 1);
+	return 0;
+}
+
+static const struct snd_soc_codec_driver soc_codec_driver_nau8825 = {
+
+	.remove	= nau8825_codec_remove,
+	.suspend_bias_off = true,
+	.set_bias_level	= nau8825_set_bias_level,
+	.controls = nau8825_snd_controls,
+	.num_controls = ARRAY_SIZE(nau8825_snd_controls),
+	.dapm_widgets = nau8825_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(nau8825_dapm_widgets),
+	.dapm_routes = nau8825_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(nau8825_dapm_routes),
+};
+
+static bool nau8825_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case NAU8825_RESET:
+	case NAU8825_ENA_CTRL:
+	case NAU8825_CLK_EN:
+	case NAU8825_CLK_DIVIDER:
+	case NAU8825_FLL_1:
+	case NAU8825_FLL_2:
+	case NAU8825_FLL_3:
+	case NAU8825_FLL_4:
+	case NAU8825_FLL_5:
+	case NAU8825_FLL_6:
+	case NAU8825_HEADSET_CTRL:
+	case NAU8825_JACK_DET_CTRL:
+	case NAU8825_IRQ_MASK:
+	case NAU8825_IRQ_STATUS:
+	case NAU8825_IRQ_CLEAR:
+	case NAU8825_IRQ_CTRL:
+	case NAU8825_SAR_ADC:
+	case NAU8825_VDET_COEFFICIENT:
+	case NAU8825_VDET_THRESHOLD_1:
+	case NAU8825_VDET_THRESHOLD_2:
+	case NAU8825_GPIO2_CTRL:
+	case NAU8825_I2S_PCM_CTRL_1:
+	case NAU8825_I2S_PCM_CTRL_2:
+	case NAU8825_ADC_RATE:
+	case NAU8825_DAC_CTRL1:
+	case NAU8825_DAC_CTRL2:
+	case NAU8825_ADC_DGAIN_CTRL:
+	case NAU8825_DAC_MUTE_CTRL:
+	case NAU8825_HSVOL_CTRL:
+	case NAU8825_DACL_CTRL:
+	case NAU8825_DACR_CTRL:
+	case NAU8825_SAR_ADC_OUTPUT:
+	case NAU8825_ANALOG_CTRL_2:
+	case NAU8825_ANALOG_ADC_1:
+	case NAU8825_ANALOG_ADC_2:
+	case NAU8825_DAC_CTRL:
+	case NAU8825_MIC_BIAS:
+	case NAU8825_BOOST:
+	case NAU8825_CLASS_G_CTRL:
+	case NAU8825_BIAS_ADJ:
+	case NAU8825_POWER_UP_CTRL:
+	case NAU8825_CHARGE_BUMP_CTRL:
+	case NAU8825_CHARGE_BUMP_RD:
+	case NAU8825_GENERAL_STATUS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool nau8825_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case NAU8825_RESET:
+	case NAU8825_ENA_CTRL:
+	case NAU8825_CLK_EN:
+	case NAU8825_CLK_DIVIDER:
+	case NAU8825_FLL_1:
+	case NAU8825_FLL_2:
+	case NAU8825_FLL_3:
+	case NAU8825_FLL_4:
+	case NAU8825_FLL_5:
+	case NAU8825_FLL_6:
+	case NAU8825_HEADSET_CTRL:
+	case NAU8825_JACK_DET_CTRL:
+	case NAU8825_IRQ_MASK:
+	case NAU8825_IRQ_CLEAR:
+	case NAU8825_IRQ_CTRL:
+	case NAU8825_SAR_ADC:
+	case NAU8825_VDET_COEFFICIENT:
+	case NAU8825_VDET_THRESHOLD_1:
+	case NAU8825_VDET_THRESHOLD_2:
+	case NAU8825_GPIO2_CTRL:
+	case NAU8825_I2S_PCM_CTRL_1:
+	case NAU8825_I2S_PCM_CTRL_2:
+	case NAU8825_ADC_RATE:
+	case NAU8825_DAC_CTRL1:
+	case NAU8825_DAC_CTRL2:
+	case NAU8825_ADC_DGAIN_CTRL:
+	case NAU8825_DAC_MUTE_CTRL:
+	case NAU8825_HSVOL_CTRL:
+	case NAU8825_DACL_CTRL:
+	case NAU8825_DACR_CTRL:
+	case NAU8825_SAR_ADC_OUTPUT:
+	case NAU8825_ANALOG_CTRL_2:
+	case NAU8825_ANALOG_ADC_1:
+	case NAU8825_ANALOG_ADC_2:
+	case NAU8825_DAC_CTRL:
+	case NAU8825_MIC_BIAS:
+	case NAU8825_BOOST:
+	case NAU8825_CLASS_G_CTRL:
+	case NAU8825_BIAS_ADJ:
+	case NAU8825_POWER_UP_CTRL:
+	case NAU8825_CHARGE_BUMP_CTRL:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config nau8825_regmap = {
+	.reg_bits = 16,
+	.val_bits = 16,
+	.use_single_rw = true,
+	.max_register = NAU8825_MAX_REGISTER,
+	.volatile_reg = nau8825_volatile_register,
+	.readable_reg = nau8825_readable_register,
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults = nau8825_reg,
+	.num_reg_defaults = ARRAY_SIZE(nau8825_reg),
+};
+
+static const struct snd_soc_dai_ops nau8825_dai_ops = {
+	.hw_params	= nau8825_hw_params,
+	.set_sysclk	= nau8825_dai_set_sysclk,
+	.set_fmt	= nau8825_set_dai_fmt,
+	.digital_mute	= nau8825_dac_mute,
+};
+
+static struct snd_soc_dai_driver nau8825_dai_driver[] = {
+	{
+		.name = "nau8825-aif1",
+			.playback = {
+				.stream_name	 = "AIF1 Playback",
+				.channels_min	 = 1,
+				.channels_max	 = 2,
+				.rates		 = NAU8825_RATES,
+				.formats	 = NAU8825_FORMATS,
+			},
+			.capture = {
+				.stream_name	 = "AIF1 Capture",
+				.channels_min	 = 1,
+				.channels_max	 = 2,
+				.rates		 = NAU8825_RATES,
+				.formats	 = NAU8825_FORMATS,
+			},
+		.ops = &nau8825_dai_ops,
+	}
+};
+
+
+static int nau8825_i2c_probe(struct i2c_client *i2c,
+				 const struct i2c_device_id *i2c_id)
+{
+	struct nau8825_priv *nau8825;
+	int i, ret;
+
+	nau8825 = devm_kzalloc(&i2c->dev, sizeof(*nau8825),
+		GFP_KERNEL);
+	if (nau8825 == NULL)
+		return -ENOMEM;
+	nau8825->i2c = i2c;
+	i2c_set_clientdata(i2c, nau8825);
+	nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap);
+	if (IS_ERR(nau8825->regmap)) {
+		ret = PTR_ERR(nau8825->regmap);
+		dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+		goto err_enable;
+	}
+	/* software reset */
+	regmap_write(nau8825->regmap, NAU8825_RESET, 0x01);
+	regmap_write(nau8825->regmap, NAU8825_RESET, 0x02);
+	/*writing initial register values to the codec*/
+	for (i = 0; i < ARRAY_SIZE(nau8825_reg); i++)
+		regmap_write(nau8825->regmap, nau8825_reg[i].reg,
+		nau8825_reg[i].def);
+
+	ret = snd_soc_register_codec(&i2c->dev, &soc_codec_driver_nau8825,
+				nau8825_dai_driver,
+				ARRAY_SIZE(nau8825_dai_driver));
+err_enable:
+	return ret;
+}
+
+static int nau8825_i2c_remove(struct i2c_client *i2c)
+{
+	snd_soc_unregister_codec(&i2c->dev);
+	return 0;
+}
+
+static const struct i2c_device_id nau8825_i2c_id[] = {
+	{"nau8825", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, nau8825_i2c_id);
+
+static const struct acpi_device_id nau8825_acpi_match[] = {
+	{ "10508825", 0 },
+	{},
+};
+MODULE_DEVICE_TABLE(acpi, nau8825_acpi_match);
+
+static struct i2c_driver nau8825_i2c_driver = {
+	.driver = {
+		.name	= "nau8825",
+		.owner	= THIS_MODULE,
+		.acpi_match_table = ACPI_PTR(nau8825_acpi_match),
+	},
+	.probe		= nau8825_i2c_probe,
+	.remove		= (nau8825_i2c_remove),
+	.id_table	= nau8825_i2c_id,
+};
+module_i2c_driver(nau8825_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NAU8825 codec driver");
+MODULE_AUTHOR("Nuvoton");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
new file mode 100644
index 0000000..8f22e39
--- /dev/null
+++ b/sound/soc/codecs/nau8825.h
@@ -0,0 +1,170 @@
+/*
+ * nau8825.h
+ *
+ * Copyright 2015 Nuvoton Technology Corp.
+ * Author: Meng-Huang Kuo <mhkuo@nuvoton.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _NAU8825_H
+#define _NAU8825_H
+
+#define NAU8825_RESET					0x00
+#define NAU8825_ENA_CTRL				0x01
+#define NAU8825_CLK_EN					0x02
+#define NAU8825_CLK_DIVIDER				0x03
+#define NAU8825_FLL_1					0x04
+#define NAU8825_FLL_2					0x05
+#define NAU8825_FLL_3					0x06
+#define NAU8825_FLL_4					0x07
+#define NAU8825_FLL_5					0x08
+#define NAU8825_FLL_6					0x09
+#define NAU8825_HEADSET_CTRL			0x0C
+#define NAU8825_JACK_DET_CTRL			0x0D
+#define NAU8825_IRQ_MASK				0x0F
+#define NAU8825_IRQ_STATUS				0x10
+#define NAU8825_IRQ_CLEAR				0x11
+#define NAU8825_IRQ_CTRL				0x12
+#define NAU8825_SAR_ADC					0x13
+#define NAU8825_VDET_COEFFICIENT		0x14
+#define NAU8825_VDET_THRESHOLD_1		0x15
+#define NAU8825_VDET_THRESHOLD_2		0x16
+#define NAU8825_GPIO2_CTRL				0x1A
+#define NAU8825_I2S_PCM_CTRL_1			0x1C
+#define NAU8825_I2S_PCM_CTRL_2			0x1D
+#define NAU8825_ADC_RATE				0x2B
+#define NAU8825_DAC_CTRL1				0x2C
+#define NAU8825_DAC_CTRL2				0x2D
+#define NAU8825_ADC_DGAIN_CTRL			0x30
+#define NAU8825_DAC_MUTE_CTRL			0x31
+#define NAU8825_HSVOL_CTRL				0x32
+#define NAU8825_DACL_CTRL				0x33
+#define NAU8825_DACR_CTRL				0x34
+#define NAU8825_CLASS_G_CTRL			0x50
+#define NAU8825_SAR_ADC_OUTPUT			0x59
+#define NAU8825_BIAS_ADJ				0x66
+#define NAU8825_ANALOG_CTRL_2			0x6A
+#define NAU8825_ANALOG_ADC_1			0x71
+#define NAU8825_ANALOG_ADC_2			0x72
+#define NAU8825_DAC_CTRL				0x73
+#define NAU8825_MIC_BIAS				0x74
+#define NAU8825_BOOST					0x76
+#define NAU8825_POWER_UP_CTRL			0x7F
+#define NAU8825_CHARGE_BUMP_CTRL		0x80
+#define NAU8825_CHARGE_BUMP_RD			0x81
+#define NAU8825_GENERAL_STATUS			0x82
+#define NAU8825_MAX_REGISTER			0xFF
+
+/* reg. NAU8825_CLK_DIVIDER (0x03) */
+#define NAU8825_SYSCLK_EN_MASK		(0x1 << 15)
+#define NAU8825_SYSCLK_EN			(0x1 << 15)
+#define NAU8825_SYSCLK_DIS			(0x0 << 15)
+#define NAU8825_CLK_MCLK_SRC_MASK	(0xF << 0)
+
+/* reg. NAU8825_FLL_1 (0x04) */
+#define NAU8825_FLL_RATIO_MASK		(0x7F << 0)
+
+/* reg. NAU8825_FLL_3 (0x06) */
+#define NAU8825_FLL_INTEGER_MASK	(0x3FF << 0)
+
+/* reg. NAU8825_FLL_4 (0x07) */
+#define NAU8825_FLL_REF_DIV_MASK	(0x3 << 10)
+
+/* reg. NAU8825_FLL_5 (0x08) */
+#define NAU8825_FLL_FILTER_SW_MASK	(0x1 << 14)
+
+/* reg. NAU8825_FLL_6 (0x09) */
+#define NAU8825_DCO_EN_MASK			(0x1 << 15)
+#define NAU8825_DCO_EN				(0x1 << 15)
+#define NAU8825_DCO_DIS				(0x0 << 15)
+#define NAU8825_SDM_EN_MASK			(0x1 << 14)
+
+/* reg. NAU8825_I2S_PCM_CTRL_1 (0x1C) */
+#define NAU8825_I2S_BP_MASK			(0x1 << 7)
+#define NAU8825_I2S_BP_INV			(0x1 << 7)
+#define NAU8825_I2S_PCMB_MASK		(0x1 << 6)
+#define NAU8825_I2S_PCMB_EN			(0x1 << 6)
+#define NAU8825_I2S_DL_MASK			(0x3 << 2)
+#define NAU8825_I2S_DF_MASK				0x3
+#define NAU8825_I2S_DF_RIGHT			0x0
+#define NAU8825_I2S_DF_LEFT				0x1
+#define NAU8825_I2S_DF_I2S				0x2
+#define NAU8825_I2S_DF_PCM_A			0x3
+#define NAU8825_I2S_DF_PCM_B			0x3
+
+/* reg. NAU8825_I2S_PCM_CTRL_2 (0x1D) */
+#define NAU8825_I2S_MS_MASK			(0x1 << 3)
+
+/* reg. NAU8825_ADC_DGAIN_CTRL (0x30) */
+#define NAU8825_ADC_DGAIN_SFT			0
+
+/* reg. NAU8825_DAC_MUTE_CTRL (0x31) */
+#define NAU8825_SOFT_MUTE_MASK		(0x1 << 13)
+#define NAU8825_SOFT_MUTE_EN		(0x1 << 13)
+#define NAU8825_SOFT_MUTE_DIS		(0x0 << 13)
+
+/* reg. NAU8825_HSVOL_CTRL (0x32) */
+#define NAU8825_R_MUTE				(0x1 << 15)
+#define NAU8825_R_MUTE_SFT				15
+#define NAU8825_L_MUTE				(0x1 << 14)
+#define NAU8825_L_MUTE_SFT				14
+#define NAU8825_L_HSVOL_SFT				6
+#define NAU8825_R_HSVOL_SFT				0
+
+/* reg. NAU8825_CLASS_G_CTRL (0x50) */
+#define NAU8825_CLASS_G_RIGHT_SHIFT		2
+#define NAU8825_CLASS_G_LEFT_SHIFT		1
+#define NAU8825_CLASS_G_SHIFT			0
+
+/* reg. NAU8825_BIAS_ADJ_2 (0x66) */
+#define NAU8825_VMID_MASK			(0x1 << 6)
+#define NAU8825_VMID_EN				(0x1 << 6)
+#define NAU8825_VMID_DIS			(0x0 << 6)
+
+/* reg. NAU8825_ANALOG_CTRL_2 (0x6A) */
+#define NAU8825_VMID_MASK			(0x1 << 6)
+#define NAU8825_VMID_EN				(0x1 << 6)
+#define NAU8825_VMID_DIS			(0x0 << 6)
+
+/* reg. NAU8825_DAC_CTRL (0x73) */
+#define NAU8825_DAC_R_SFT				13
+#define NAU8825_DAC_L_SFT				12
+#define NAU8825_DAC_CLK_R_SFT			9
+#define NAU8825_DAC_CLK_L_SFT			8
+
+/* reg. NAU8825_BOOST (0x76) */
+#define NAU8825_G_BIAS_MASK			(0x1 << 12)
+#define NAU8825_G_BIAS_SFT				12
+#define NAU8825_G_BIAS_EN			(0x1 << 12)
+#define NAU8825_G_BIAS_DIS			(0x0 << 12)
+
+/* Volume Rescale */
+#define NAU8825_VOL_RSCL_RANGE			0x36
+#define NAU8825_ADC_VOL_RSCL_RANGE		0xFF
+
+/* Data format */
+#define NAU8825_I2S_DL_16			(0x0 << 2)
+#define NAU8825_I2S_DL_20			(0x1 << 2)
+#define NAU8825_I2S_DL_24			(0x2 << 2)
+#define NAU8825_I2S_DL_32			(0x3 << 2)
+
+enum {
+	NAU8825_INTERNALCLOCK = 0,
+	NAU8825_MCLK,
+};
+
+#define NAU8825_RATES	SNDRV_PCM_RATE_8000_192000
+#define NAU8825_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+			 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct nau8825_priv {
+	struct snd_soc_codec *codec;
+	struct regmap *regmap;
+	struct i2c_client *i2c;
+	struct snd_soc_jack *jack;
+	struct delayed_work jack_detect_work;
+};
+#endif	/* _NAU8825_H */
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [alsa-devel] [PATCH v2] ASoC: Add support for NAU8825 codec to ASoC
  2015-04-16  9:56 ` Chih-Chiang Chang
  (?)
@ 2015-04-20 14:57 ` Lars-Peter Clausen
  2015-07-07  7:53     ` Chih-Chiang Chang
  -1 siblings, 1 reply; 8+ messages in thread
From: Lars-Peter Clausen @ 2015-04-20 14:57 UTC (permalink / raw)
  To: Chih-Chiang Chang, Mark Brown; +Cc: tiwai, alsa-devel, lgirdwood, linux-kernel

On 04/16/2015 11:56 AM, Chih-Chiang Chang wrote:
> The NAU88L25 is an ultra-low power high performance audio codec designed
> for smartphone, tablet PC, and other portable devices by Nuvoton, now
> add linux driver support for it.
>
> Signed-off-by: Chih-Chiang Chang <ccchang12@nuvoton.com>

Looks pretty good now.

> ---
> v2->v1:
>     - fixes according to Lars-Peter Clausen's review comments
>     - removes unused platform data file
>     - corrects the naming of DAPM input widget
>     - fixes some wrong coding of SOC widgets and other codes
>     - adds definition and remark for config FLL clock
>     - moves the code of reset hardware registers from codec_probe() to i2c_probe()
>     - removes unused codes
>
[...]
> +static const struct snd_kcontrol_new nau8825_snd_controls[] = {
> +

Here and a few other places you leave the first line in the struct 
declaration empty. No need for that.

> +	SOC_SINGLE_TLV("MIC Volume", NAU8825_ADC_DGAIN_CTRL,
> +				NAU8825_ADC_DGAIN_SFT,
> +				NAU8825_ADC_VOL_RSCL_RANGE, 0, adc_vol_tlv),
> +	SOC_DOUBLE_TLV("HP Volume", NAU8825_HSVOL_CTRL,
> +				NAU8825_L_HSVOL_SFT, NAU8825_R_HSVOL_SFT,
> +				NAU8825_VOL_RSCL_RANGE, 1, out_hp_vol_tlv),
> +};
[...]
> +
> +static void config_fll_clk_12m(struct snd_soc_codec *codec)
> +{
> +	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
> +
> +	regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
> +				NAU8825_CLK_MCLK_SRC_MASK, 0x0003);
> +	regmap_update_bits(nau8825->regmap, NAU8825_FLL_1,
> +				NAU8825_FLL_RATIO_MASK, 0x0001);
> +	/* FLL 16-bit fractional input */
> +	regmap_write(nau8825->regmap, NAU8825_FLL_2, 0xC49B);
> +	/* FLL 10-bit integer input */
> +	regmap_update_bits(nau8825->regmap, NAU8825_FLL_3,
> +				NAU8825_FLL_INTEGER_MASK, 0x0020);
> +	/* FLL pre-scaler */
> +	regmap_update_bits(nau8825->regmap, NAU8825_FLL_4,
> +				NAU8825_FLL_REF_DIV_MASK, 0x0800);

This seems to use some constant dividers and multipliers for the FLL. Does 
this expect a specific reference clock from somewhere?

> +	/* select divied VCO input */
> +	regmap_update_bits(nau8825->regmap, NAU8825_FLL_5,
> +				NAU8825_FLL_FILTER_SW_MASK, 0x0000);
> +	/* FLL sigma delta modulator enable */
> +	regmap_update_bits(nau8825->regmap, NAU8825_FLL_6,
> +				NAU8825_SDM_EN_MASK, 0x4000);
> +}
> +
> +static void set_sys_clk(struct snd_soc_codec *codec, int sys_clk)
> +{
> +	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
> +
> +	pr_debug("%s :: sys_clk=%x\n", __func__, sys_clk);
> +	switch (sys_clk) {
> +	case NAU8825_INTERNALCLOCK:
> +		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
> +				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_DIS);
> +		regmap_update_bits(nau8825->regmap, NAU8825_FLL_6,
> +				NAU8825_DCO_EN_MASK, NAU8825_DCO_EN);
> +		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
> +				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_EN);
> +		break;
> +	case NAU8825_MCLK:
> +	default:
> +		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
> +				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_DIS);
> +		regmap_update_bits(nau8825->regmap, NAU8825_FLL_6,
> +				NAU8825_DCO_EN_MASK, NAU8825_DCO_DIS);
> +		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
> +				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_EN);
> +		break;
> +	}
> +}
> +
> +static int nau8825_dai_set_sysclk(struct snd_soc_dai *dai,
> +		int clk_id, unsigned int freq, int dir)
> +{
> +	struct snd_soc_codec *codec = dai->codec;
> +
> +	switch (clk_id) {
> +	case NAU8825_MCLK:
> +		config_fll_clk_12m(codec);
> +		set_sys_clk(codec, clk_id);

Maybe just inline the contents of set_sys_clk here ...

> +		break;
> +	case NAU8825_INTERNALCLOCK:
> +		set_sys_clk(codec, clk_id);

... and here

> +		break;
> +	default:
> +		dev_err(codec->dev, "Wrong clock src\n");
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
[...]
 > +static const struct reg_default nau8825_reg[] = {
 > +	/* enable clock source */
 > +	{0x0001, 0x07FF},
 > +	/* enable VMID and Bias */
 > +	{0x0076, 0x3140},
 > +	/* setup clock divider */
 > +	{0x0003, 0x0050},
 > +	/* jack detection configuration */
 > +	{0x000C, 0x0004},
 > +	{0x000D, 0x00E0},
 > +	{0x000F, 0x0801},
 > +	{0x0012, 0x0010},
 > +	/* keypad detection configuration */
 > +	{0x0013, 0x0280},
 > +	{0x0014, 0x7310},
 > +	{0x0015, 0x050E},
 > +	{0x0016, 0x1B2A},
 > +	/* audio format configuration */
 > +	{0x001A, 0x0800},
 > +	{0x001C, 0x000E},
 > +	{0x001D, 0x0010},
 > +	/* sampling rate control */
 > +	{0x002B, 0x0012},
 > +	/* DAC sampling rate control */
 > +	{0x002C, 0x0082},
 > +	/* ADC and DAC mixer control */
 > +	{0x0030, 0x00D2},
 > +	{0x0033, 0x00CF},
 > +	{0x0034, 0x02CF},
 > +	/* DAC class-G control */
 > +	{0x006A, 0x1003},
 > +	{0x0050, 0x2007},
 > +	/* ADC PGA control */
 > +	{0x0072, 0x0260},
 > +	/* DAC power down enabled */
 > +	{0x0080, 0x03A0},
 > +	/* enable DAC clock */
 > +	{0x0073, 0x336C},
 > +	/* enable MIC Bias */
 > +	{0x0074, 0x5502},
 > +	/* powerup output driver */
 > +	{0x007F, 0x473C},
 > +	{0x007F, 0x473F},
 > +	/* DAC power down disabled */
 > +	{0x0080, 0x00A0},
 > +	/* adjust MIC Bias */
 > +	{0x0066, 0x2060},
 > +	{0x0066, 0x2060},
 > +	{0x0066, 0x0060},
 > +};

The register defaults should hold the content of the registers in the 
power-on reset state of the chip. This is not meant to be used as a 
initialization sequence.

 > +
[...]
> +static struct snd_soc_dai_driver nau8825_dai_driver[] = {
> +	{
> +		.name = "nau8825-aif1",
> +			.playback = {
> +				.stream_name	 = "AIF1 Playback",
> +				.channels_min	 = 1,
> +				.channels_max	 = 2,
> +				.rates		 = NAU8825_RATES,
> +				.formats	 = NAU8825_FORMATS,
> +			},
> +			.capture = {
> +				.stream_name	 = "AIF1 Capture",
> +				.channels_min	 = 1,
> +				.channels_max	 = 2,
> +				.rates		 = NAU8825_RATES,
> +				.formats	 = NAU8825_FORMATS,
> +			},

The indent is a bit strange above.

> +		.ops = &nau8825_dai_ops,
> +	}
> +};
> +
> +
> +static int nau8825_i2c_probe(struct i2c_client *i2c,
> +				 const struct i2c_device_id *i2c_id)
> +{
> +	struct nau8825_priv *nau8825;
> +	int i, ret;
> +
> +	nau8825 = devm_kzalloc(&i2c->dev, sizeof(*nau8825),
> +		GFP_KERNEL);
> +	if (nau8825 == NULL)
> +		return -ENOMEM;
> +	nau8825->i2c = i2c;
> +	i2c_set_clientdata(i2c, nau8825);
> +	nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap);
> +	if (IS_ERR(nau8825->regmap)) {
> +		ret = PTR_ERR(nau8825->regmap);
> +		dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
> +		goto err_enable;

Since there is nothing to do at err_enable, just return ret directly and get 
rid of the label.

> +	}
> +	/* software reset */
> +	regmap_write(nau8825->regmap, NAU8825_RESET, 0x01);
> +	regmap_write(nau8825->regmap, NAU8825_RESET, 0x02);
> +	/*writing initial register values to the codec*/
> +	for (i = 0; i < ARRAY_SIZE(nau8825_reg); i++)
> +		regmap_write(nau8825->regmap, nau8825_reg[i].reg,
> +		nau8825_reg[i].def);
> +
> +	ret = snd_soc_register_codec(&i2c->dev, &soc_codec_driver_nau8825,
> +				nau8825_dai_driver,
> +				ARRAY_SIZE(nau8825_dai_driver));
> +err_enable:
> +	return ret;
> +}
> +
[...]
> +#define NAU8825_RATES	SNDRV_PCM_RATE_8000_192000
> +#define NAU8825_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
> +			 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)

These defines should probably be moved to the C file close to their users.

> +
> +struct nau8825_priv {
> +	struct snd_soc_codec *codec;
> +	struct regmap *regmap;
> +	struct i2c_client *i2c;
> +	struct snd_soc_jack *jack;
> +	struct delayed_work jack_detect_work;

All fields in here except for the regmap field are still unused.

> +};
> +#endif	/* _NAU8825_H */
>


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] ASoC: Add support for NAU8825 codec to ASoC
  2015-04-16  9:56 ` Chih-Chiang Chang
  (?)
  (?)
@ 2015-04-24 18:28 ` Mark Brown
  2015-07-07  8:22     ` Chih-Chiang Chang
  -1 siblings, 1 reply; 8+ messages in thread
From: Mark Brown @ 2015-04-24 18:28 UTC (permalink / raw)
  To: Chih-Chiang Chang
  Cc: tiwai, lgirdwood, Lars-Peter Clausen, alsa-devel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 2481 bytes --]

On Thu, Apr 16, 2015 at 05:56:26PM +0800, Chih-Chiang Chang wrote:

> +static const struct snd_kcontrol_new nau8825_hpo_mix[] = {
> +
> +	SOC_DAPM_SINGLE("HP L Switch", NAU8825_HSVOL_CTRL,
> +	NAU8825_L_MUTE_SFT, 1, 1),
> +	SOC_DAPM_SINGLE("HP R Switch", NAU8825_HSVOL_CTRL,
> +	NAU8825_R_MUTE_SFT, 1, 1),

Indentation - the continuation lines should be lined up with the (.

> +static void set_sys_clk(struct snd_soc_codec *codec, int sys_clk)
> +{

This is only used in set_sysclk(), just merge it into there.

> +static const struct reg_default nau8825_reg[] = {
> +	/* enable clock source */
> +	{0x0001, 0x07FF},
> +	/* enable VMID and Bias */
> +	{0x0076, 0x3140},
> +	/* setup clock divider */
> +	{0x0003, 0x0050},
> +	/* jack detection configuration */
> +	{0x000C, 0x0004},
> +	{0x000D, 0x00E0},
> +	{0x000F, 0x0801},
> +	{0x0012, 0x0010},
> +	/* keypad detection configuration */
> +	{0x0013, 0x0280},
> +	{0x0014, 0x7310},
> +	{0x0015, 0x050E},
> +	{0x0016, 0x1B2A},
> +	/* audio format configuration */
> +	{0x001A, 0x0800},
> +	{0x001C, 0x000E},
> +	{0x001D, 0x0010},

This all looks like normal configuration of the device done through a
fixed magic number table which isn't what patches are for at all - they
are for erratum fixes and similar.  Most if not all of this should be
configured either from userspace or by the machine driver.

> +static bool nau8825_volatile_register(struct device *dev, unsigned int reg)
> +{
> +	switch (reg) {
> +	case NAU8825_RESET:
> +	case NAU8825_ENA_CTRL:
> +	case NAU8825_CLK_EN:
> +	case NAU8825_CLK_DIVIDER:
> +	case NAU8825_FLL_1:
> +	case NAU8825_FLL_2:
> +	case NAU8825_FLL_3:
> +	case NAU8825_FLL_4:
> +	case NAU8825_FLL_5:
> +	case NAU8825_FLL_6:
> +	case NAU8825_HEADSET_CTRL:
> +	case NAU8825_JACK_DET_CTRL:
> +	case NAU8825_IRQ_MASK:
> +	case NAU8825_IRQ_CLEAR:
> +	case NAU8825_IRQ_CTRL:

Are you *sure* all these registers are volatile?  It isn't impossible of
course but it seems like it'd be some very special hardware design.  It
looks like all the registers are being marked as volatile.

> +	/* software reset */
> +	regmap_write(nau8825->regmap, NAU8825_RESET, 0x01);
> +	regmap_write(nau8825->regmap, NAU8825_RESET, 0x02);

We did the reset differently in the removal path...

> +	/*writing initial register values to the codec*/
> +	for (i = 0; i < ARRAY_SIZE(nau8825_reg); i++)
> +		regmap_write(nau8825->regmap, nau8825_reg[i].reg,
> +		nau8825_reg[i].def);

We should use the reset values the CODEC has.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [alsa-devel] [PATCH v2] ASoC: Add support for NAU8825 codec to ASoC
  2015-04-20 14:57 ` [alsa-devel] " Lars-Peter Clausen
@ 2015-07-07  7:53     ` Chih-Chiang Chang
  0 siblings, 0 replies; 8+ messages in thread
From: Chih-Chiang Chang @ 2015-07-07  7:53 UTC (permalink / raw)
  To: Lars-Peter Clausen, Mark Brown
  Cc: tiwai, AP MS30 Linux ALSA, lgirdwood, AP MS30 Linux Kernel community



On 2015/4/20 下午 10:57, Lars-Peter Clausen wrote:
> On 04/16/2015 11:56 AM, Chih-Chiang Chang wrote:
>> The NAU88L25 is an ultra-low power high performance audio codec designed
>> for smartphone, tablet PC, and other portable devices by Nuvoton, now
>> add linux driver support for it.
>>
>> Signed-off-by: Chih-Chiang Chang <ccchang12@nuvoton.com>
> 
> Looks pretty good now.
> 
>> ---
>> v2->v1:
>>     - fixes according to Lars-Peter Clausen's review comments
>>     - removes unused platform data file
>>     - corrects the naming of DAPM input widget
>>     - fixes some wrong coding of SOC widgets and other codes
>>     - adds definition and remark for config FLL clock
>>     - moves the code of reset hardware registers from codec_probe() to i2c_probe()
>>     - removes unused codes
>>
> [...]
>> +static const struct snd_kcontrol_new nau8825_snd_controls[] = {
>> +
> 
> Here and a few other places you leave the first line in the struct 
> declaration empty. No need for that.
> 
Removed the space line in declarations.

>> +	SOC_SINGLE_TLV("MIC Volume", NAU8825_ADC_DGAIN_CTRL,
>> +				NAU8825_ADC_DGAIN_SFT,
>> +				NAU8825_ADC_VOL_RSCL_RANGE, 0, adc_vol_tlv),
>> +	SOC_DOUBLE_TLV("HP Volume", NAU8825_HSVOL_CTRL,
>> +				NAU8825_L_HSVOL_SFT, NAU8825_R_HSVOL_SFT,
>> +				NAU8825_VOL_RSCL_RANGE, 1, out_hp_vol_tlv),
>> +};
> [...]
>> +
>> +static void config_fll_clk_12m(struct snd_soc_codec *codec)
>> +{
>> +	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
>> +
>> +	regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
>> +				NAU8825_CLK_MCLK_SRC_MASK, 0x0003);
>> +	regmap_update_bits(nau8825->regmap, NAU8825_FLL_1,
>> +				NAU8825_FLL_RATIO_MASK, 0x0001);
>> +	/* FLL 16-bit fractional input */
>> +	regmap_write(nau8825->regmap, NAU8825_FLL_2, 0xC49B);
>> +	/* FLL 10-bit integer input */
>> +	regmap_update_bits(nau8825->regmap, NAU8825_FLL_3,
>> +				NAU8825_FLL_INTEGER_MASK, 0x0020);
>> +	/* FLL pre-scaler */
>> +	regmap_update_bits(nau8825->regmap, NAU8825_FLL_4,
>> +				NAU8825_FLL_REF_DIV_MASK, 0x0800);
> 
> This seems to use some constant dividers and multipliers for the FLL. Does 
> this expect a specific reference clock from somewhere?
>
Yes, current code expects ASoC will send 12 MHz MCLK to the codec. But
finally, we will support all possible reference clock frequencies.

>> +	/* select divied VCO input */
>> +	regmap_update_bits(nau8825->regmap, NAU8825_FLL_5,
>> +				NAU8825_FLL_FILTER_SW_MASK, 0x0000);
>> +	/* FLL sigma delta modulator enable */
>> +	regmap_update_bits(nau8825->regmap, NAU8825_FLL_6,
>> +				NAU8825_SDM_EN_MASK, 0x4000);
>> +}
>> +
>> +static void set_sys_clk(struct snd_soc_codec *codec, int sys_clk)
>> +{
>> +	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
>> +
>> +	pr_debug("%s :: sys_clk=%x\n", __func__, sys_clk);
>> +	switch (sys_clk) {
>> +	case NAU8825_INTERNALCLOCK:
>> +		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
>> +				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_DIS);
>> +		regmap_update_bits(nau8825->regmap, NAU8825_FLL_6,
>> +				NAU8825_DCO_EN_MASK, NAU8825_DCO_EN);
>> +		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
>> +				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_EN);
>> +		break;
>> +	case NAU8825_MCLK:
>> +	default:
>> +		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
>> +				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_DIS);
>> +		regmap_update_bits(nau8825->regmap, NAU8825_FLL_6,
>> +				NAU8825_DCO_EN_MASK, NAU8825_DCO_DIS);
>> +		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
>> +				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_EN);
>> +		break;
>> +	}
>> +}
>> +
>> +static int nau8825_dai_set_sysclk(struct snd_soc_dai *dai,
>> +		int clk_id, unsigned int freq, int dir)
>> +{
>> +	struct snd_soc_codec *codec = dai->codec;
>> +
>> +	switch (clk_id) {
>> +	case NAU8825_MCLK:
>> +		config_fll_clk_12m(codec);
>> +		set_sys_clk(codec, clk_id);
> 
> Maybe just inline the contents of set_sys_clk here ...
> 
Since another code as below still uses set_sys_clk(), we reserve
function calling here.

	case SND_SOC_BIAS_OFF:
		dev_dbg(codec->dev, "###nau8825_set_bias_level OFF\n");
		set_sys_clk(codec, NAU8825_INTERNALCLOCK);
		regmap_update_bits(nau8825->regmap, NAU8825_BIAS_ADJ,
				NAU8825_VMID_MASK, NAU8825_VMID_DIS);

>> +		break;
>> +	case NAU8825_INTERNALCLOCK:
>> +		set_sys_clk(codec, clk_id);
> 
> ... and here
> 
The same answer to previous.

>> +		break;
>> +	default:
>> +		dev_err(codec->dev, "Wrong clock src\n");
>> +		return -EINVAL;
>> +	}
>> +	return 0;
>> +}
> [...]
>  > +static const struct reg_default nau8825_reg[] = {
>  > +	/* enable clock source */
>  > +	{0x0001, 0x07FF},
>  > +	/* enable VMID and Bias */
>  > +	{0x0076, 0x3140},
>  > +	/* setup clock divider */
>  > +	{0x0003, 0x0050},
>  > +	/* jack detection configuration */
>  > +	{0x000C, 0x0004},
>  > +	{0x000D, 0x00E0},
>  > +	{0x000F, 0x0801},
>  > +	{0x0012, 0x0010},
>  > +	/* keypad detection configuration */
>  > +	{0x0013, 0x0280},
>  > +	{0x0014, 0x7310},
>  > +	{0x0015, 0x050E},
>  > +	{0x0016, 0x1B2A},
>  > +	/* audio format configuration */
>  > +	{0x001A, 0x0800},
>  > +	{0x001C, 0x000E},
>  > +	{0x001D, 0x0010},
>  > +	/* sampling rate control */
>  > +	{0x002B, 0x0012},
>  > +	/* DAC sampling rate control */
>  > +	{0x002C, 0x0082},
>  > +	/* ADC and DAC mixer control */
>  > +	{0x0030, 0x00D2},
>  > +	{0x0033, 0x00CF},
>  > +	{0x0034, 0x02CF},
>  > +	/* DAC class-G control */
>  > +	{0x006A, 0x1003},
>  > +	{0x0050, 0x2007},
>  > +	/* ADC PGA control */
>  > +	{0x0072, 0x0260},
>  > +	/* DAC power down enabled */
>  > +	{0x0080, 0x03A0},
>  > +	/* enable DAC clock */
>  > +	{0x0073, 0x336C},
>  > +	/* enable MIC Bias */
>  > +	{0x0074, 0x5502},
>  > +	/* powerup output driver */
>  > +	{0x007F, 0x473C},
>  > +	{0x007F, 0x473F},
>  > +	/* DAC power down disabled */
>  > +	{0x0080, 0x00A0},
>  > +	/* adjust MIC Bias */
>  > +	{0x0066, 0x2060},
>  > +	{0x0066, 0x2060},
>  > +	{0x0066, 0x0060},
>  > +};
> 
> The register defaults should hold the content of the registers in the 
> power-on reset state of the chip. This is not meant to be used as a 
> initialization sequence.
> 
Modified code to make register defaults hold the register values in the
power-on reset state.

static const struct reg_default nau8825_reg[] = {
	{0x000, 0x0000},
	{0x001, 0x00ff},
	{0x003, 0x0050},
	{0x004, 0x0000},
	{0x005, 0x3126},
	{0x006, 0x0008},
	{0x007, 0x0010},
	{0x008, 0x0000},
	{0x009, 0x6000},
	{0x00a, 0xf13c},
	{0x00c, 0x000c},
	{0x00d, 0x0000},
	{0x00f, 0x0800},
	{0x010, 0x0000},
	{0x011, 0x0000},
	{0x012, 0x0010},
	{0x013, 0x0015},
	{0x014, 0x0110},
	{0x015, 0x0000},
	...

>  > +
> [...]
>> +static struct snd_soc_dai_driver nau8825_dai_driver[] = {
>> +	{
>> +		.name = "nau8825-aif1",
>> +			.playback = {
>> +				.stream_name	 = "AIF1 Playback",
>> +				.channels_min	 = 1,
>> +				.channels_max	 = 2,
>> +				.rates		 = NAU8825_RATES,
>> +				.formats	 = NAU8825_FORMATS,
>> +			},
>> +			.capture = {
>> +				.stream_name	 = "AIF1 Capture",
>> +				.channels_min	 = 1,
>> +				.channels_max	 = 2,
>> +				.rates		 = NAU8825_RATES,
>> +				.formats	 = NAU8825_FORMATS,
>> +			},
> 
> The indent is a bit strange above.
> 
Fixed the code as below.

static struct snd_soc_dai_driver nau8825_dai_driver[] = {
	{
		.name = "nau8825-aif1",
		.playback = {
			.stream_name	 = "AIF1 Playback",
			.channels_min	 = 1,
			.channels_max	 = 2,
			.rates		 = NAU8825_RATES,
			.formats	 = NAU8825_FORMATS,
		},
		.capture = {
			.stream_name	 = "AIF1 Capture",
			.channels_min	 = 1,
			.channels_max	 = 2,
			.rates		 = NAU8825_RATES,
			.formats	 = NAU8825_FORMATS,
		},
		.ops = &nau8825_dai_ops,
	}
};

>> +		.ops = &nau8825_dai_ops,
>> +	}
>> +};
>> +
>> +
>> +static int nau8825_i2c_probe(struct i2c_client *i2c,
>> +				 const struct i2c_device_id *i2c_id)
>> +{
>> +	struct nau8825_priv *nau8825;
>> +	int i, ret;
>> +
>> +	nau8825 = devm_kzalloc(&i2c->dev, sizeof(*nau8825),
>> +		GFP_KERNEL);
>> +	if (nau8825 == NULL)
>> +		return -ENOMEM;
>> +	nau8825->i2c = i2c;
>> +	i2c_set_clientdata(i2c, nau8825);
>> +	nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap);
>> +	if (IS_ERR(nau8825->regmap)) {
>> +		ret = PTR_ERR(nau8825->regmap);
>> +		dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
>> +		goto err_enable;
> 
> Since there is nothing to do at err_enable, just return ret directly and get 
> rid of the label.
> 
Modified the code as below.

	i2c_set_clientdata(i2c, nau8825);
	nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap);
	if (IS_ERR(nau8825->regmap)) {
		ret = PTR_ERR(nau8825->regmap);
		dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
		return ret;
	}
	/* software reset */
	regmap_write(nau8825->regmap, NAU8825_RESET, 0x00);
	regmap_write(nau8825->regmap, NAU8825_RESET, 0x00);

	/* register sound card */
	ret = snd_soc_register_codec(&i2c->dev, &soc_codec_driver_nau8825,
				nau8825_dai_driver,
				ARRAY_SIZE(nau8825_dai_driver));
	return ret;
}

>> +	}
>> +	/* software reset */
>> +	regmap_write(nau8825->regmap, NAU8825_RESET, 0x01);
>> +	regmap_write(nau8825->regmap, NAU8825_RESET, 0x02);
>> +	/*writing initial register values to the codec*/
>> +	for (i = 0; i < ARRAY_SIZE(nau8825_reg); i++)
>> +		regmap_write(nau8825->regmap, nau8825_reg[i].reg,
>> +		nau8825_reg[i].def);
>> +
>> +	ret = snd_soc_register_codec(&i2c->dev, &soc_codec_driver_nau8825,
>> +				nau8825_dai_driver,
>> +				ARRAY_SIZE(nau8825_dai_driver));
>> +err_enable:
>> +	return ret;
>> +}
>> +
> [...]
>> +#define NAU8825_RATES	SNDRV_PCM_RATE_8000_192000
>> +#define NAU8825_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
>> +			 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
> 
> These defines should probably be moved to the C file close to their users.
>
Move the defines to C file as below.

#define NAU8825_RATES	SNDRV_PCM_RATE_8000_192000
#define NAU8825_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE \
			 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)

static const struct snd_soc_dai_ops nau8825_dai_ops = {
	.trigger	= nau8825_trigger,
	.hw_params	= nau8825_hw_params,
	.set_sysclk	= nau8825_dai_set_sysclk,
	.set_fmt	= nau8825_set_dai_fmt,
	.digital_mute	= nau8825_dac_mute,
};

static struct snd_soc_dai_driver nau8825_dai_driver[] = {
	{
		.name = "nau8825-aif1",

>> +
>> +struct nau8825_priv {
>> +	struct snd_soc_codec *codec;
>> +	struct regmap *regmap;
>> +	struct i2c_client *i2c;
>> +	struct snd_soc_jack *jack;
>> +	struct delayed_work jack_detect_work;
> 
> All fields in here except for the regmap field are still unused.
> 
Remove some unused field. But the i2c field is used in nau8825_i2c_probe().

struct nau8825_priv {
	struct regmap *regmap;
	struct i2c_client *i2c;
};

static int nau8825_i2c_probe(struct i2c_client *i2c,
				 const struct i2c_device_id *i2c_id)
{
	struct nau8825_priv *nau8825;
	int ret;

	nau8825 = devm_kzalloc(&i2c->dev, sizeof(*nau8825),
		GFP_KERNEL);
	if (nau8825 == NULL)
		return -ENOMEM;
	nau8825->i2c = i2c;
	i2c_set_clientdata(i2c, nau8825);
	nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap);

>> +};
>> +#endif	/* _NAU8825_H */
>>
> 

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] ASoC: Add support for NAU8825 codec to ASoC
@ 2015-07-07  7:53     ` Chih-Chiang Chang
  0 siblings, 0 replies; 8+ messages in thread
From: Chih-Chiang Chang @ 2015-07-07  7:53 UTC (permalink / raw)
  To: Lars-Peter Clausen, Mark Brown
  Cc: tiwai, AP MS30 Linux ALSA, lgirdwood, AP MS30 Linux Kernel community



On 2015/4/20 下午 10:57, Lars-Peter Clausen wrote:
> On 04/16/2015 11:56 AM, Chih-Chiang Chang wrote:
>> The NAU88L25 is an ultra-low power high performance audio codec designed
>> for smartphone, tablet PC, and other portable devices by Nuvoton, now
>> add linux driver support for it.
>>
>> Signed-off-by: Chih-Chiang Chang <ccchang12@nuvoton.com>
> 
> Looks pretty good now.
> 
>> ---
>> v2->v1:
>>     - fixes according to Lars-Peter Clausen's review comments
>>     - removes unused platform data file
>>     - corrects the naming of DAPM input widget
>>     - fixes some wrong coding of SOC widgets and other codes
>>     - adds definition and remark for config FLL clock
>>     - moves the code of reset hardware registers from codec_probe() to i2c_probe()
>>     - removes unused codes
>>
> [...]
>> +static const struct snd_kcontrol_new nau8825_snd_controls[] = {
>> +
> 
> Here and a few other places you leave the first line in the struct 
> declaration empty. No need for that.
> 
Removed the space line in declarations.

>> +	SOC_SINGLE_TLV("MIC Volume", NAU8825_ADC_DGAIN_CTRL,
>> +				NAU8825_ADC_DGAIN_SFT,
>> +				NAU8825_ADC_VOL_RSCL_RANGE, 0, adc_vol_tlv),
>> +	SOC_DOUBLE_TLV("HP Volume", NAU8825_HSVOL_CTRL,
>> +				NAU8825_L_HSVOL_SFT, NAU8825_R_HSVOL_SFT,
>> +				NAU8825_VOL_RSCL_RANGE, 1, out_hp_vol_tlv),
>> +};
> [...]
>> +
>> +static void config_fll_clk_12m(struct snd_soc_codec *codec)
>> +{
>> +	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
>> +
>> +	regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
>> +				NAU8825_CLK_MCLK_SRC_MASK, 0x0003);
>> +	regmap_update_bits(nau8825->regmap, NAU8825_FLL_1,
>> +				NAU8825_FLL_RATIO_MASK, 0x0001);
>> +	/* FLL 16-bit fractional input */
>> +	regmap_write(nau8825->regmap, NAU8825_FLL_2, 0xC49B);
>> +	/* FLL 10-bit integer input */
>> +	regmap_update_bits(nau8825->regmap, NAU8825_FLL_3,
>> +				NAU8825_FLL_INTEGER_MASK, 0x0020);
>> +	/* FLL pre-scaler */
>> +	regmap_update_bits(nau8825->regmap, NAU8825_FLL_4,
>> +				NAU8825_FLL_REF_DIV_MASK, 0x0800);
> 
> This seems to use some constant dividers and multipliers for the FLL. Does 
> this expect a specific reference clock from somewhere?
>
Yes, current code expects ASoC will send 12 MHz MCLK to the codec. But
finally, we will support all possible reference clock frequencies.

>> +	/* select divied VCO input */
>> +	regmap_update_bits(nau8825->regmap, NAU8825_FLL_5,
>> +				NAU8825_FLL_FILTER_SW_MASK, 0x0000);
>> +	/* FLL sigma delta modulator enable */
>> +	regmap_update_bits(nau8825->regmap, NAU8825_FLL_6,
>> +				NAU8825_SDM_EN_MASK, 0x4000);
>> +}
>> +
>> +static void set_sys_clk(struct snd_soc_codec *codec, int sys_clk)
>> +{
>> +	struct nau8825_priv *nau8825 = snd_soc_codec_get_drvdata(codec);
>> +
>> +	pr_debug("%s :: sys_clk=%x\n", __func__, sys_clk);
>> +	switch (sys_clk) {
>> +	case NAU8825_INTERNALCLOCK:
>> +		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
>> +				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_DIS);
>> +		regmap_update_bits(nau8825->regmap, NAU8825_FLL_6,
>> +				NAU8825_DCO_EN_MASK, NAU8825_DCO_EN);
>> +		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
>> +				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_EN);
>> +		break;
>> +	case NAU8825_MCLK:
>> +	default:
>> +		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
>> +				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_DIS);
>> +		regmap_update_bits(nau8825->regmap, NAU8825_FLL_6,
>> +				NAU8825_DCO_EN_MASK, NAU8825_DCO_DIS);
>> +		regmap_update_bits(nau8825->regmap, NAU8825_CLK_DIVIDER,
>> +				NAU8825_SYSCLK_EN_MASK, NAU8825_SYSCLK_EN);
>> +		break;
>> +	}
>> +}
>> +
>> +static int nau8825_dai_set_sysclk(struct snd_soc_dai *dai,
>> +		int clk_id, unsigned int freq, int dir)
>> +{
>> +	struct snd_soc_codec *codec = dai->codec;
>> +
>> +	switch (clk_id) {
>> +	case NAU8825_MCLK:
>> +		config_fll_clk_12m(codec);
>> +		set_sys_clk(codec, clk_id);
> 
> Maybe just inline the contents of set_sys_clk here ...
> 
Since another code as below still uses set_sys_clk(), we reserve
function calling here.

	case SND_SOC_BIAS_OFF:
		dev_dbg(codec->dev, "###nau8825_set_bias_level OFF\n");
		set_sys_clk(codec, NAU8825_INTERNALCLOCK);
		regmap_update_bits(nau8825->regmap, NAU8825_BIAS_ADJ,
				NAU8825_VMID_MASK, NAU8825_VMID_DIS);

>> +		break;
>> +	case NAU8825_INTERNALCLOCK:
>> +		set_sys_clk(codec, clk_id);
> 
> ... and here
> 
The same answer to previous.

>> +		break;
>> +	default:
>> +		dev_err(codec->dev, "Wrong clock src\n");
>> +		return -EINVAL;
>> +	}
>> +	return 0;
>> +}
> [...]
>  > +static const struct reg_default nau8825_reg[] = {
>  > +	/* enable clock source */
>  > +	{0x0001, 0x07FF},
>  > +	/* enable VMID and Bias */
>  > +	{0x0076, 0x3140},
>  > +	/* setup clock divider */
>  > +	{0x0003, 0x0050},
>  > +	/* jack detection configuration */
>  > +	{0x000C, 0x0004},
>  > +	{0x000D, 0x00E0},
>  > +	{0x000F, 0x0801},
>  > +	{0x0012, 0x0010},
>  > +	/* keypad detection configuration */
>  > +	{0x0013, 0x0280},
>  > +	{0x0014, 0x7310},
>  > +	{0x0015, 0x050E},
>  > +	{0x0016, 0x1B2A},
>  > +	/* audio format configuration */
>  > +	{0x001A, 0x0800},
>  > +	{0x001C, 0x000E},
>  > +	{0x001D, 0x0010},
>  > +	/* sampling rate control */
>  > +	{0x002B, 0x0012},
>  > +	/* DAC sampling rate control */
>  > +	{0x002C, 0x0082},
>  > +	/* ADC and DAC mixer control */
>  > +	{0x0030, 0x00D2},
>  > +	{0x0033, 0x00CF},
>  > +	{0x0034, 0x02CF},
>  > +	/* DAC class-G control */
>  > +	{0x006A, 0x1003},
>  > +	{0x0050, 0x2007},
>  > +	/* ADC PGA control */
>  > +	{0x0072, 0x0260},
>  > +	/* DAC power down enabled */
>  > +	{0x0080, 0x03A0},
>  > +	/* enable DAC clock */
>  > +	{0x0073, 0x336C},
>  > +	/* enable MIC Bias */
>  > +	{0x0074, 0x5502},
>  > +	/* powerup output driver */
>  > +	{0x007F, 0x473C},
>  > +	{0x007F, 0x473F},
>  > +	/* DAC power down disabled */
>  > +	{0x0080, 0x00A0},
>  > +	/* adjust MIC Bias */
>  > +	{0x0066, 0x2060},
>  > +	{0x0066, 0x2060},
>  > +	{0x0066, 0x0060},
>  > +};
> 
> The register defaults should hold the content of the registers in the 
> power-on reset state of the chip. This is not meant to be used as a 
> initialization sequence.
> 
Modified code to make register defaults hold the register values in the
power-on reset state.

static const struct reg_default nau8825_reg[] = {
	{0x000, 0x0000},
	{0x001, 0x00ff},
	{0x003, 0x0050},
	{0x004, 0x0000},
	{0x005, 0x3126},
	{0x006, 0x0008},
	{0x007, 0x0010},
	{0x008, 0x0000},
	{0x009, 0x6000},
	{0x00a, 0xf13c},
	{0x00c, 0x000c},
	{0x00d, 0x0000},
	{0x00f, 0x0800},
	{0x010, 0x0000},
	{0x011, 0x0000},
	{0x012, 0x0010},
	{0x013, 0x0015},
	{0x014, 0x0110},
	{0x015, 0x0000},
	...

>  > +
> [...]
>> +static struct snd_soc_dai_driver nau8825_dai_driver[] = {
>> +	{
>> +		.name = "nau8825-aif1",
>> +			.playback = {
>> +				.stream_name	 = "AIF1 Playback",
>> +				.channels_min	 = 1,
>> +				.channels_max	 = 2,
>> +				.rates		 = NAU8825_RATES,
>> +				.formats	 = NAU8825_FORMATS,
>> +			},
>> +			.capture = {
>> +				.stream_name	 = "AIF1 Capture",
>> +				.channels_min	 = 1,
>> +				.channels_max	 = 2,
>> +				.rates		 = NAU8825_RATES,
>> +				.formats	 = NAU8825_FORMATS,
>> +			},
> 
> The indent is a bit strange above.
> 
Fixed the code as below.

static struct snd_soc_dai_driver nau8825_dai_driver[] = {
	{
		.name = "nau8825-aif1",
		.playback = {
			.stream_name	 = "AIF1 Playback",
			.channels_min	 = 1,
			.channels_max	 = 2,
			.rates		 = NAU8825_RATES,
			.formats	 = NAU8825_FORMATS,
		},
		.capture = {
			.stream_name	 = "AIF1 Capture",
			.channels_min	 = 1,
			.channels_max	 = 2,
			.rates		 = NAU8825_RATES,
			.formats	 = NAU8825_FORMATS,
		},
		.ops = &nau8825_dai_ops,
	}
};

>> +		.ops = &nau8825_dai_ops,
>> +	}
>> +};
>> +
>> +
>> +static int nau8825_i2c_probe(struct i2c_client *i2c,
>> +				 const struct i2c_device_id *i2c_id)
>> +{
>> +	struct nau8825_priv *nau8825;
>> +	int i, ret;
>> +
>> +	nau8825 = devm_kzalloc(&i2c->dev, sizeof(*nau8825),
>> +		GFP_KERNEL);
>> +	if (nau8825 == NULL)
>> +		return -ENOMEM;
>> +	nau8825->i2c = i2c;
>> +	i2c_set_clientdata(i2c, nau8825);
>> +	nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap);
>> +	if (IS_ERR(nau8825->regmap)) {
>> +		ret = PTR_ERR(nau8825->regmap);
>> +		dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
>> +		goto err_enable;
> 
> Since there is nothing to do at err_enable, just return ret directly and get 
> rid of the label.
> 
Modified the code as below.

	i2c_set_clientdata(i2c, nau8825);
	nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap);
	if (IS_ERR(nau8825->regmap)) {
		ret = PTR_ERR(nau8825->regmap);
		dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
		return ret;
	}
	/* software reset */
	regmap_write(nau8825->regmap, NAU8825_RESET, 0x00);
	regmap_write(nau8825->regmap, NAU8825_RESET, 0x00);

	/* register sound card */
	ret = snd_soc_register_codec(&i2c->dev, &soc_codec_driver_nau8825,
				nau8825_dai_driver,
				ARRAY_SIZE(nau8825_dai_driver));
	return ret;
}

>> +	}
>> +	/* software reset */
>> +	regmap_write(nau8825->regmap, NAU8825_RESET, 0x01);
>> +	regmap_write(nau8825->regmap, NAU8825_RESET, 0x02);
>> +	/*writing initial register values to the codec*/
>> +	for (i = 0; i < ARRAY_SIZE(nau8825_reg); i++)
>> +		regmap_write(nau8825->regmap, nau8825_reg[i].reg,
>> +		nau8825_reg[i].def);
>> +
>> +	ret = snd_soc_register_codec(&i2c->dev, &soc_codec_driver_nau8825,
>> +				nau8825_dai_driver,
>> +				ARRAY_SIZE(nau8825_dai_driver));
>> +err_enable:
>> +	return ret;
>> +}
>> +
> [...]
>> +#define NAU8825_RATES	SNDRV_PCM_RATE_8000_192000
>> +#define NAU8825_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
>> +			 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
> 
> These defines should probably be moved to the C file close to their users.
>
Move the defines to C file as below.

#define NAU8825_RATES	SNDRV_PCM_RATE_8000_192000
#define NAU8825_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE \
			 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)

static const struct snd_soc_dai_ops nau8825_dai_ops = {
	.trigger	= nau8825_trigger,
	.hw_params	= nau8825_hw_params,
	.set_sysclk	= nau8825_dai_set_sysclk,
	.set_fmt	= nau8825_set_dai_fmt,
	.digital_mute	= nau8825_dac_mute,
};

static struct snd_soc_dai_driver nau8825_dai_driver[] = {
	{
		.name = "nau8825-aif1",

>> +
>> +struct nau8825_priv {
>> +	struct snd_soc_codec *codec;
>> +	struct regmap *regmap;
>> +	struct i2c_client *i2c;
>> +	struct snd_soc_jack *jack;
>> +	struct delayed_work jack_detect_work;
> 
> All fields in here except for the regmap field are still unused.
> 
Remove some unused field. But the i2c field is used in nau8825_i2c_probe().

struct nau8825_priv {
	struct regmap *regmap;
	struct i2c_client *i2c;
};

static int nau8825_i2c_probe(struct i2c_client *i2c,
				 const struct i2c_device_id *i2c_id)
{
	struct nau8825_priv *nau8825;
	int ret;

	nau8825 = devm_kzalloc(&i2c->dev, sizeof(*nau8825),
		GFP_KERNEL);
	if (nau8825 == NULL)
		return -ENOMEM;
	nau8825->i2c = i2c;
	i2c_set_clientdata(i2c, nau8825);
	nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap);

>> +};
>> +#endif	/* _NAU8825_H */
>>
> 
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] ASoC: Add support for NAU8825 codec to ASoC
  2015-04-24 18:28 ` Mark Brown
@ 2015-07-07  8:22     ` Chih-Chiang Chang
  0 siblings, 0 replies; 8+ messages in thread
From: Chih-Chiang Chang @ 2015-07-07  8:22 UTC (permalink / raw)
  To: Mark Brown
  Cc: tiwai, lgirdwood, Lars-Peter Clausen, AP MS30 Linux ALSA,
	AP MS30 Linux Kernel community



On 2015/4/25 上午 02:28, Mark Brown wrote:
> On Thu, Apr 16, 2015 at 05:56:26PM +0800, Chih-Chiang Chang wrote:
> 
>> +static const struct snd_kcontrol_new nau8825_hpo_mix[] = {
>> +
>> +	SOC_DAPM_SINGLE("HP L Switch", NAU8825_HSVOL_CTRL,
>> +	NAU8825_L_MUTE_SFT, 1, 1),
>> +	SOC_DAPM_SINGLE("HP R Switch", NAU8825_HSVOL_CTRL,
>> +	NAU8825_R_MUTE_SFT, 1, 1),
> 
> Indentation - the continuation lines should be lined up with the (.
> 
Modified code as below.

static const struct snd_kcontrol_new nau8825_hpo_mix[] = {
	SOC_DAPM_SINGLE("HP L Switch", NAU8825_HSVOL_CTRL,
				NAU8825_L_MUTE_SFT, 1, 1),
	SOC_DAPM_SINGLE("HP R Switch", NAU8825_HSVOL_CTRL,
				NAU8825_R_MUTE_SFT, 1, 1),
};

>> +static void set_sys_clk(struct snd_soc_codec *codec, int sys_clk)
>> +{
> 
> This is only used in set_sysclk(), just merge it into there.
> 
it is also used in the set_bias_level().

	case SND_SOC_BIAS_OFF:
		dev_dbg(codec->dev, "###nau8825_set_bias_level OFF\n");
		set_sys_clk(codec, NAU8825_INTERNALCLOCK);
		regmap_update_bits(nau8825->regmap, NAU8825_BIAS_ADJ,
				NAU8825_VMID_MASK, NAU8825_VMID_DIS);

>> +static const struct reg_default nau8825_reg[] = {
>> +	/* enable clock source */
>> +	{0x0001, 0x07FF},
>> +	/* enable VMID and Bias */
>> +	{0x0076, 0x3140},
>> +	/* setup clock divider */
>> +	{0x0003, 0x0050},
>> +	/* jack detection configuration */
>> +	{0x000C, 0x0004},
>> +	{0x000D, 0x00E0},
>> +	{0x000F, 0x0801},
>> +	{0x0012, 0x0010},
>> +	/* keypad detection configuration */
>> +	{0x0013, 0x0280},
>> +	{0x0014, 0x7310},
>> +	{0x0015, 0x050E},
>> +	{0x0016, 0x1B2A},
>> +	/* audio format configuration */
>> +	{0x001A, 0x0800},
>> +	{0x001C, 0x000E},
>> +	{0x001D, 0x0010},
> 
> This all looks like normal configuration of the device done through a
> fixed magic number table which isn't what patches are for at all - they
> are for erratum fixes and similar.  Most if not all of this should be
> configured either from userspace or by the machine driver.
> 
In original code, the reg_default hold the registers of initialization
sequence. Modified code to make reg_default hold the register values in
the power-on reset state. And remove the code of writing initial
register values in i2c_probe() function.

static const struct reg_default nau8825_reg[] = {
	{0x000, 0x0000},
	{0x001, 0x00ff},
	{0x003, 0x0050},
	{0x004, 0x0000},
	{0x005, 0x3126},
	{0x006, 0x0008},
	{0x007, 0x0010},
	{0x008, 0x0000},
	{0x009, 0x6000},
	{0x00a, 0xf13c},
	{0x00c, 0x000c},
	{0x00d, 0x0000},
	{0x00f, 0x0800},
	{0x010, 0x0000},
	{0x011, 0x0000},
	{0x012, 0x0010},
	{0x013, 0x0015},
	{0x014, 0x0110},
	{0x015, 0x0000},
	...

>> +static bool nau8825_volatile_register(struct device *dev, unsigned int reg)
>> +{
>> +	switch (reg) {
>> +	case NAU8825_RESET:
>> +	case NAU8825_ENA_CTRL:
>> +	case NAU8825_CLK_EN:
>> +	case NAU8825_CLK_DIVIDER:
>> +	case NAU8825_FLL_1:
>> +	case NAU8825_FLL_2:
>> +	case NAU8825_FLL_3:
>> +	case NAU8825_FLL_4:
>> +	case NAU8825_FLL_5:
>> +	case NAU8825_FLL_6:
>> +	case NAU8825_HEADSET_CTRL:
>> +	case NAU8825_JACK_DET_CTRL:
>> +	case NAU8825_IRQ_MASK:
>> +	case NAU8825_IRQ_CLEAR:
>> +	case NAU8825_IRQ_CTRL:
> 
> Are you *sure* all these registers are volatile?  It isn't impossible of
> course but it seems like it'd be some very special hardware design.  It
> looks like all the registers are being marked as volatile.
>
Remove some unnecessary registers.

static bool nau8825_volatile_register(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case NAU8825_RESET:
	case NAU8825_CLK_DIVIDER:
	case NAU8825_FLL_1:
	case NAU8825_FLL_2:
	case NAU8825_FLL_3:
	case NAU8825_FLL_4:
	case NAU8825_FLL_5:
	case NAU8825_FLL_6:
	case NAU8825_ANALOG_CTRL_2:
	case NAU8825_ANALOG_ADC_1:
	case NAU8825_ANALOG_ADC_2:
	case NAU8825_DAC_CTRL:
	case NAU8825_MIC_BIAS:
	case NAU8825_BOOST:
	case NAU8825_CLASSG_CTRL:
	case NAU8825_I2C_DEVICE_ID:
	case NAU8825_BIAS_ADJ:
	case NAU8825_POWER_UP_CTRL:
	case NAU8825_CHARGE_BUMP_CTRL:
	case NAU8825_GENERAL_STATUS:
		return true;
	default:
		return false;
	}
}

>> +	/* software reset */
>> +	regmap_write(nau8825->regmap, NAU8825_RESET, 0x01);
>> +	regmap_write(nau8825->regmap, NAU8825_RESET, 0x02);
> 
> We did the reset differently in the removal path...
> 
For software reset, our codec must write twice in any value. Sync the
code as below in both of i2c_probe() and codec_remove().

	regmap_write(nau8825->regmap, NAU8825_RESET, 0x00);
	regmap_write(nau8825->regmap, NAU8825_RESET, 0x00);

>> +	/*writing initial register values to the codec*/
>> +	for (i = 0; i < ARRAY_SIZE(nau8825_reg); i++)
>> +		regmap_write(nau8825->regmap, nau8825_reg[i].reg,
>> +		nau8825_reg[i].def);
> 
> We should use the reset values the CODEC has.
> 
Yes, removed the code.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] ASoC: Add support for NAU8825 codec to ASoC
@ 2015-07-07  8:22     ` Chih-Chiang Chang
  0 siblings, 0 replies; 8+ messages in thread
From: Chih-Chiang Chang @ 2015-07-07  8:22 UTC (permalink / raw)
  To: Mark Brown
  Cc: tiwai, lgirdwood, Lars-Peter Clausen, AP MS30 Linux ALSA,
	AP MS30 Linux Kernel community



On 2015/4/25 上午 02:28, Mark Brown wrote:
> On Thu, Apr 16, 2015 at 05:56:26PM +0800, Chih-Chiang Chang wrote:
> 
>> +static const struct snd_kcontrol_new nau8825_hpo_mix[] = {
>> +
>> +	SOC_DAPM_SINGLE("HP L Switch", NAU8825_HSVOL_CTRL,
>> +	NAU8825_L_MUTE_SFT, 1, 1),
>> +	SOC_DAPM_SINGLE("HP R Switch", NAU8825_HSVOL_CTRL,
>> +	NAU8825_R_MUTE_SFT, 1, 1),
> 
> Indentation - the continuation lines should be lined up with the (.
> 
Modified code as below.

static const struct snd_kcontrol_new nau8825_hpo_mix[] = {
	SOC_DAPM_SINGLE("HP L Switch", NAU8825_HSVOL_CTRL,
				NAU8825_L_MUTE_SFT, 1, 1),
	SOC_DAPM_SINGLE("HP R Switch", NAU8825_HSVOL_CTRL,
				NAU8825_R_MUTE_SFT, 1, 1),
};

>> +static void set_sys_clk(struct snd_soc_codec *codec, int sys_clk)
>> +{
> 
> This is only used in set_sysclk(), just merge it into there.
> 
it is also used in the set_bias_level().

	case SND_SOC_BIAS_OFF:
		dev_dbg(codec->dev, "###nau8825_set_bias_level OFF\n");
		set_sys_clk(codec, NAU8825_INTERNALCLOCK);
		regmap_update_bits(nau8825->regmap, NAU8825_BIAS_ADJ,
				NAU8825_VMID_MASK, NAU8825_VMID_DIS);

>> +static const struct reg_default nau8825_reg[] = {
>> +	/* enable clock source */
>> +	{0x0001, 0x07FF},
>> +	/* enable VMID and Bias */
>> +	{0x0076, 0x3140},
>> +	/* setup clock divider */
>> +	{0x0003, 0x0050},
>> +	/* jack detection configuration */
>> +	{0x000C, 0x0004},
>> +	{0x000D, 0x00E0},
>> +	{0x000F, 0x0801},
>> +	{0x0012, 0x0010},
>> +	/* keypad detection configuration */
>> +	{0x0013, 0x0280},
>> +	{0x0014, 0x7310},
>> +	{0x0015, 0x050E},
>> +	{0x0016, 0x1B2A},
>> +	/* audio format configuration */
>> +	{0x001A, 0x0800},
>> +	{0x001C, 0x000E},
>> +	{0x001D, 0x0010},
> 
> This all looks like normal configuration of the device done through a
> fixed magic number table which isn't what patches are for at all - they
> are for erratum fixes and similar.  Most if not all of this should be
> configured either from userspace or by the machine driver.
> 
In original code, the reg_default hold the registers of initialization
sequence. Modified code to make reg_default hold the register values in
the power-on reset state. And remove the code of writing initial
register values in i2c_probe() function.

static const struct reg_default nau8825_reg[] = {
	{0x000, 0x0000},
	{0x001, 0x00ff},
	{0x003, 0x0050},
	{0x004, 0x0000},
	{0x005, 0x3126},
	{0x006, 0x0008},
	{0x007, 0x0010},
	{0x008, 0x0000},
	{0x009, 0x6000},
	{0x00a, 0xf13c},
	{0x00c, 0x000c},
	{0x00d, 0x0000},
	{0x00f, 0x0800},
	{0x010, 0x0000},
	{0x011, 0x0000},
	{0x012, 0x0010},
	{0x013, 0x0015},
	{0x014, 0x0110},
	{0x015, 0x0000},
	...

>> +static bool nau8825_volatile_register(struct device *dev, unsigned int reg)
>> +{
>> +	switch (reg) {
>> +	case NAU8825_RESET:
>> +	case NAU8825_ENA_CTRL:
>> +	case NAU8825_CLK_EN:
>> +	case NAU8825_CLK_DIVIDER:
>> +	case NAU8825_FLL_1:
>> +	case NAU8825_FLL_2:
>> +	case NAU8825_FLL_3:
>> +	case NAU8825_FLL_4:
>> +	case NAU8825_FLL_5:
>> +	case NAU8825_FLL_6:
>> +	case NAU8825_HEADSET_CTRL:
>> +	case NAU8825_JACK_DET_CTRL:
>> +	case NAU8825_IRQ_MASK:
>> +	case NAU8825_IRQ_CLEAR:
>> +	case NAU8825_IRQ_CTRL:
> 
> Are you *sure* all these registers are volatile?  It isn't impossible of
> course but it seems like it'd be some very special hardware design.  It
> looks like all the registers are being marked as volatile.
>
Remove some unnecessary registers.

static bool nau8825_volatile_register(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case NAU8825_RESET:
	case NAU8825_CLK_DIVIDER:
	case NAU8825_FLL_1:
	case NAU8825_FLL_2:
	case NAU8825_FLL_3:
	case NAU8825_FLL_4:
	case NAU8825_FLL_5:
	case NAU8825_FLL_6:
	case NAU8825_ANALOG_CTRL_2:
	case NAU8825_ANALOG_ADC_1:
	case NAU8825_ANALOG_ADC_2:
	case NAU8825_DAC_CTRL:
	case NAU8825_MIC_BIAS:
	case NAU8825_BOOST:
	case NAU8825_CLASSG_CTRL:
	case NAU8825_I2C_DEVICE_ID:
	case NAU8825_BIAS_ADJ:
	case NAU8825_POWER_UP_CTRL:
	case NAU8825_CHARGE_BUMP_CTRL:
	case NAU8825_GENERAL_STATUS:
		return true;
	default:
		return false;
	}
}

>> +	/* software reset */
>> +	regmap_write(nau8825->regmap, NAU8825_RESET, 0x01);
>> +	regmap_write(nau8825->regmap, NAU8825_RESET, 0x02);
> 
> We did the reset differently in the removal path...
> 
For software reset, our codec must write twice in any value. Sync the
code as below in both of i2c_probe() and codec_remove().

	regmap_write(nau8825->regmap, NAU8825_RESET, 0x00);
	regmap_write(nau8825->regmap, NAU8825_RESET, 0x00);

>> +	/*writing initial register values to the codec*/
>> +	for (i = 0; i < ARRAY_SIZE(nau8825_reg); i++)
>> +		regmap_write(nau8825->regmap, nau8825_reg[i].reg,
>> +		nau8825_reg[i].def);
> 
> We should use the reset values the CODEC has.
> 
Yes, removed the code.

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2015-07-07  8:22 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-04-16  9:56 [PATCH v2] ASoC: Add support for NAU8825 codec to ASoC Chih-Chiang Chang
2015-04-16  9:56 ` Chih-Chiang Chang
2015-04-20 14:57 ` [alsa-devel] " Lars-Peter Clausen
2015-07-07  7:53   ` Chih-Chiang Chang
2015-07-07  7:53     ` Chih-Chiang Chang
2015-04-24 18:28 ` Mark Brown
2015-07-07  8:22   ` Chih-Chiang Chang
2015-07-07  8:22     ` Chih-Chiang Chang

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.