[alsa-devel] [PATCH] ASoC: Add support for NAU8825 codec to ASoC

Lars-Peter Clausen lars at metafoo.de
Fri Apr 10 10:38:07 CEST 2015


On 04/10/2015 09:21 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 at nuvoton.com>
> ---
>   include/sound/nau8825_plat.h |  22 ++
>   sound/soc/codecs/Kconfig     |   5 +
>   sound/soc/codecs/Makefile    |   2 +
>   sound/soc/codecs/nau8825.c   | 593 +++++++++++++++++++++++++++++++++++++++++++
>   sound/soc/codecs/nau8825.h   | 150 +++++++++++
>   5 files changed, 772 insertions(+)
>   create mode 100644 include/sound/nau8825_plat.h
>   create mode 100644 sound/soc/codecs/nau8825.c
>   create mode 100644 sound/soc/codecs/nau8825.h
>
> diff --git a/include/sound/nau8825_plat.h b/include/sound/nau8825_plat.h
> new file mode 100644
> index 0000000..87afcd3
> --- /dev/null
> +++ b/include/sound/nau8825_plat.h

the preferred place for platform_data files is include/linux/platform_data/, 
but the driver doesn't seem to use the platform data anyway, so maybe drop it?

[...]
> diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
> new file mode 100644
> index 0000000..a8c8f59
> --- /dev/null
> +++ b/sound/soc/codecs/nau8825.c
> @@ -0,0 +1,593 @@
> +/*
> + * linux/sound/soc/codecs/nau8825.c

No need to include the file path in the header of the file, this will just 
become outdated if the file is ever moved.

[...]
> +static int nau8825_hw_params(struct snd_pcm_substream *substream,
> +				struct snd_pcm_hw_params *params,
> +				struct snd_soc_dai *dai);
> +static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai,
> +				unsigned int fmt);
> +static int nau8825_set_bias_level(struct snd_soc_codec *codec,
> +				enum snd_soc_bias_level level);
> +static int nau8825_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
> +				unsigned int freq, int dir);

These forward declarations don't seem to be necessary.

[...]
> +static const struct snd_kcontrol_new nau8825_snd_controls[] = {
> +

> +	SOC_SINGLE("HP Class OP", NAU8825_CLASS_G_CTRL, NAU8825_CLASS_G_SHIFT,
> +				 1, 0),

What is "Class OP"?

> +	SOC_SINGLE("DAC Right", NAU8825_DAC_CTRL, NAU8825_DAC_R_SFT, 1, 0),
> +	SOC_SINGLE("DAC Left", NAU8825_DAC_CTRL, NAU8825_DAC_L_SFT, 1, 0),

The same bits are controlled by the DAC DAPM widgets, there shouldn't be any 
controls for them.

> +	SOC_SINGLE("DAC Right Clock", NAU8825_DAC_CTRL, NAU8825_DAC_CLK_R_SFT,
> +				1, 0),
> +	SOC_SINGLE("DAC Left Clock", NAU8825_DAC_CTRL, NAU8825_DAC_CLK_L_SFT,
> +				1, 0),

The clock controls should probably not be user controllable but rather be 
DAPM supply widgets.

> +};
> +
> +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[] = {
> +
> +	SND_SOC_DAPM_INPUT("Mic Jack"),

Input and output widgets should have the same name as the matching pin of 
the CODEC.

> +	SND_SOC_DAPM_MICBIAS("MIC BIAS", NAU8825_BOOST, NAU8825_G_BIAS_SFT, 0),

New drivers shouldn't use DAPM_MICBIAS widgets as they are known to be 
broken. Use a DAPM_SUPPLY widget instead.

> +	SND_SOC_DAPM_SUPPLY("micbias", SND_SOC_NOPM, 0, 0,
> +				NULL, SND_SOC_DAPM_PRE_PMU
> +				| SND_SOC_DAPM_POST_PMD),
> +	SND_SOC_DAPM_SUPPLY("vmid", SND_SOC_NOPM, 0, 0, NULL,
> +				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),

Both the vmid and micbias supply seem to be unused and don't do anything either.

> +	/* ADCs */
> +	SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0),
> +	SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0),
> +	/* ADC IF1  */
> +	SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),

This one seems to be unused, and doesn't do anything either.

> +	/* 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_E("DAC L1", NULL, NAU8825_DAC_CTRL,
> +				NAU8825_DAC_L_SFT, 0, NULL,
> +				SND_SOC_DAPM_PRE_PMD),

Just use DAC instead of DAC_E if you don't have to implement a callback

> +	SND_SOC_DAPM_DAC_E("DAC R1", NULL, NAU8825_DAC_CTRL,
> +				NAU8825_DAC_R_SFT, 0, NULL,
> +				SND_SOC_DAPM_PRE_PMD),

Same here.

> +	/* 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_PGA_S("HP amp", 1, NAU8825_CLASS_G_CTRL,
> +				NAU8825_CLASS_G_SHIFT, 0, NULL, 0),

No need for _S if there is no sub-sequencing involved.

> +	/* Output Lines */
> +	SND_SOC_DAPM_OUTPUT("HPOL"),
> +	SND_SOC_DAPM_OUTPUT("HPOR"),
> +};
> +
[...
> +static int nau8825_hw_params(struct snd_pcm_substream *substream,
> +				struct snd_pcm_hw_params *params,
> +				struct snd_soc_dai *tmp)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct snd_soc_codec *codec = rtd->codec;


rtd->codec does not necessarily point to your CODEC device, use dai->codec 
instead. As rule of thumb you should never have to look at the 
snd_soc_pcm_runtime in a CODEC driver.


[...]
> +
> +static void config_fll_clk_12m(struct snd_soc_codec *codec)
> +{
> +	snd_soc_update_bits(codec, NAU8825_CLK_DIVIDER, 0x000F, 0x0003);
> +	snd_soc_update_bits(codec, NAU8825_FL_1, 0x007F, 0x0001);
> +	snd_soc_write(codec, NAU8825_FL_2, 0xC49B);
> +	snd_soc_update_bits(codec, NAU8825_FL_3, 0x03FF, 0x0020);
> +	snd_soc_update_bits(codec, NAU8825_FL_4, 0x0C00, 0x0800);
> +	snd_soc_update_bits(codec, NAU8825_FL_5, 0x2000, 0x0000);
> +	snd_soc_update_bits(codec, NAU8825_FL_6, 0x4000, 0x4000);

So what do these magic values do?

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

static

> +{
> +	pr_debug("%s :: sys_clk=%x\n", __func__, sys_clk);
[...
> +static int nau8825_codec_suspend(struct snd_soc_codec *codec)
> +{
> +	nau8825_set_bias_level(codec, SND_SOC_BIAS_OFF);
> +	return 0;
> +}

set the suspend_bias_off flag in your codec driver to let the core take care 
of this.

> +
> +static int nau8825_resume(struct snd_soc_codec *codec)
> +{
> +	nau8825_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
> +	return 0;
> +}

This is not necessary the core already takes care of it.

> +
[...]
> +static int nau8825_codec_probe(struct snd_soc_codec *codec)
> +{
> +	int i;
> +	struct nau8825_priv *nau8825;
> +
> +	nau8825 = snd_soc_codec_get_drvdata(codec);
> +	nau8825->codec = codec;
> +	/*writing initial register values to the codec*/
> +	for (i = 0; i < ARRAY_SIZE(nau8825_reg); i++)
> +		snd_soc_write(codec, nau8825_reg[i].reg, nau8825_reg[i].def);

If that is really necessary use regmap_sync() and preferably in the i2c 
probe function. But I'd expect that a soft reset should put the device in 
the default register configuration?

> +	return 0;
> +}
> +
[...]
> +static struct snd_soc_codec_driver soc_codec_driver_nau8825 = {

const

> +	.probe = nau8825_codec_probe,
> +	.remove	= nau8825_codec_remove,
> +	.suspend = nau8825_codec_suspend,
> +	.resume	= nau8825_resume,
> +	.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 struct snd_soc_dai_ops nau8825_dai_ops = {

const

> +	.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_platform_data *pdata = dev_get_platdata(&i2c->dev);
> +	struct nau8825_priv *nau8825;
> +	int ret;
> +
> +	nau8825 = devm_kzalloc(&i2c->dev, sizeof(struct nau8825_priv),

preferred form for this is sizeof(*nau8825)

[...]
> +MODULE_DESCRIPTION("ASoC NAU8825 codec driver");
> +MODULE_AUTHOR("Nuvoton");
> +MODULE_LICENSE("GPL v2");
> +
> +

No need for the extra new lines at the end

> diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
> new file mode 100644
> index 0000000..b16d4d1
> --- /dev/null
> +++ b/sound/soc/codecs/nau8825.h
> @@ -0,0 +1,150 @@
> +/*
[...]
> +
> +struct nau8825_priv {
> +	struct snd_soc_codec *codec;
> +	struct nau8825_platform_data pdata;
> +	struct regmap *regmap;
> +	struct i2c_client *i2c;
> +	struct snd_soc_jack *hp_jack;
> +	struct snd_soc_jack *mic_jack;
> +	struct delayed_work delayed_work;
> +
> +	struct workqueue_struct *workqueue;
> +	struct mutex mutex;
> +	unsigned int irq;
> +	bool jd_status;
> +	bool bp_status;
> +	int	jack_type;
> +};

It looks like pretty much all of the fields in the struct are not used by 
the driver.

> +#endif	/* _NAU8825_H */
>



More information about the Alsa-devel mailing list