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@xxxxxxxxxxx>
---[...]
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[] = {
+
+ 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);
+ /* 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 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;
+}
+
+#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 */