[alsa-devel] [PATCH v2] sound/soc/codecs: add LAPIS Semiconductor ML26124

Lars-Peter Clausen lars at metafoo.de
Fri Nov 25 14:04:47 CET 2011


On 11/25/2011 01:39 PM, Tomoya MORINAGA wrote:
> ML26124-01HB/ML26124-02GD is 16bit monaural audio CODEC which has high
> resistance to voltage noise. On chip regulator realizes power supply rejection
> ratio (PSRR) be over 90dB so more than 50dB is improved than ever. ML26124-01HB/
> ML26124-02GD can deliver stable audio performance without being affected by noise
> from the power supply circuit and peripheral components. The chip also includes
> a composite video signal output, which can be applied to various portable device
>  requirements. The ML26124 is realized these functions into very small package
> the size is only 2.56mm x 2.46mm therefore can be construct high quality sound
> system easily.
> ML26124-01HB is 25pin WCSP package; ML26124-02GD is 32pin WQFN package.
> 
> Signed-off-by: Tomoya MORINAGA <tomoya.rohm at gmail.com>
> ---
>  sound/soc/codecs/Kconfig   |    4 +
>  sound/soc/codecs/Makefile  |    2 +
>  sound/soc/codecs/ml26124.c |  532 ++++++++++++++++++++++++++++++++++++++++++++
>  sound/soc/codecs/ml26124.h |  123 ++++++++++
>  4 files changed, 661 insertions(+), 0 deletions(-)
>  create mode 100644 sound/soc/codecs/ml26124.c
>  create mode 100644 sound/soc/codecs/ml26124.h
> 
> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
> index 665d924..ce8e622 100644
> --- a/sound/soc/codecs/Kconfig
> +++ b/sound/soc/codecs/Kconfig
> @@ -38,6 +38,7 @@ config SND_SOC_ALL_CODECS
>  	select SND_SOC_MAX98095 if I2C
>  	select SND_SOC_MAX9850 if I2C
>  	select SND_SOC_MAX9877 if I2C
> +	select SND_SOC_ML26124 if I2C
>  	select SND_SOC_PCM3008
>  	select SND_SOC_SGTL5000 if I2C
>  	select SND_SOC_SN95031 if INTEL_SCU_IPC
> @@ -211,6 +212,9 @@ config SND_SOC_MAX98095
>  config SND_SOC_MAX9850
>  	tristate
>  
> +config SND_SOC_ML26124
> +	tristate
> +
>  config SND_SOC_PCM3008
>         tristate
>  
> diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
> index 5119a7e..c1353ff 100644
> --- a/sound/soc/codecs/Makefile
> +++ b/sound/soc/codecs/Makefile
> @@ -24,6 +24,7 @@ snd-soc-l3-objs := l3.o
>  snd-soc-max98088-objs := max98088.o
>  snd-soc-max98095-objs := max98095.o
>  snd-soc-max9850-objs := max9850.o
> +snd-soc-ml26124-objs := ml26124.o
>  snd-soc-pcm3008-objs := pcm3008.o
>  snd-soc-sgtl5000-objs := sgtl5000.o
>  snd-soc-alc5623-objs := alc5623.o
> @@ -122,6 +123,7 @@ obj-$(CONFIG_SND_SOC_JZ4740_CODEC)	+= snd-soc-jz4740-codec.o
>  obj-$(CONFIG_SND_SOC_MAX98088)	+= snd-soc-max98088.o
>  obj-$(CONFIG_SND_SOC_MAX98095)	+= snd-soc-max98095.o
>  obj-$(CONFIG_SND_SOC_MAX9850)	+= snd-soc-max9850.o
> +obj-$(CONFIG_SND_SOC_ML26124)   += snd-soc-ml26124.o
>  obj-$(CONFIG_SND_SOC_PCM3008)	+= snd-soc-pcm3008.o
>  obj-$(CONFIG_SND_SOC_SGTL5000)  += snd-soc-sgtl5000.o
>  obj-$(CONFIG_SND_SOC_SN95031)	+=snd-soc-sn95031.o
> diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
> new file mode 100644
> index 0000000..56160de
> --- /dev/null
> +++ b/sound/soc/codecs/ml26124.c
> @@ -0,0 +1,532 @@
> +/*
> + * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/init.h>
> +#include <linux/delay.h>
> +#include <linux/pm.h>
> +#include <linux/i2c.h>
> +#include <linux/slab.h>
> +#include <linux/platform_device.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/tlv.h>
> +#include <sound/soc-dapm.h>

Duplicated include, but just remove both soc-dapm.h includes it's not needed
and can actually cause problems.

> +
> +#include "ml26124.h"
> +
> +struct ml26124_priv {
> +	enum snd_soc_control_type control_type;
> +	struct snd_pcm_substream *substream;
> +	unsigned int rate;
> +	unsigned int ch;
> +};
> +
> +/* ML26124 configuration */
> +static const DECLARE_TLV_DB_SCALE(rec_play_digi_vol, -7150, 50, 0);
> +static const DECLARE_TLV_DB_SCALE(digi_boost_vol, -1200, 75, 0);
> +static const DECLARE_TLV_DB_SCALE(eq_band_gain, -7150, 50, 0);
> +static const DECLARE_TLV_DB_SCALE(alclvl, -2250, 150, 0);
> +static const DECLARE_TLV_DB_SCALE(alcmingain, -1200, 600, 0);
> +static const DECLARE_TLV_DB_SCALE(alcmaxgain, -675, 600, 0);
> +static const DECLARE_TLV_DB_SCALE(ngth, -7650, 150, 0);
> +static const DECLARE_TLV_DB_SCALE(plilv, -2250, 150, 0);
> +static const DECLARE_TLV_DB_SCALE(plmingain, -1200, 600, 0);
> +static const DECLARE_TLV_DB_SCALE(plmaxgain, -675, 600, 0);
> +static const DECLARE_TLV_DB_SCALE(plvl, -1200, 75, 0);

Some of these TLVs seem to be identical.

> +
> +static const struct snd_kcontrol_new ml26124_snd_controls[] = {
> +	SOC_SINGLE_TLV("Capture Digital Volume", ML26124_RECORD_DIG_VOL, 0, 0xff, 1, rec_play_digi_vol),
> +	SOC_SINGLE_TLV("Playback Digital Volume", ML26124_PLBAK_DIG_VOL, 0, 0xff, 1, rec_play_digi_vol),
> +	SOC_SINGLE_TLV("Digital Boost Volume",  ML26124_DIGI_BOOST_VOL, 0, 0x3f, 0, digi_boost_vol),
> +	SOC_SINGLE_TLV("EQ Band0 Input Volume",  ML26124_EQ_GAIN_BRAND0, 0, 0xff, 1, eq_band_gain),
> +	SOC_SINGLE_TLV("EQ Band1 Input Volume",  ML26124_EQ_GAIN_BRAND1, 0, 0xff, 1, eq_band_gain),
> +	SOC_SINGLE_TLV("EQ Band2 Input Volume",  ML26124_EQ_GAIN_BRAND2, 0, 0xff, 1, eq_band_gain),
> +	SOC_SINGLE_TLV("EQ Band3 Input Volume",  ML26124_EQ_GAIN_BRAND3, 0, 0xff, 1, eq_band_gain),
> +	SOC_SINGLE_TLV("EQ Band4 Input Volume",  ML26124_EQ_GAIN_BRAND4, 0, 0xff, 1, eq_band_gain),
> +	SOC_SINGLE_TLV("ALC Target Level",  ML26124_ALC_TARGET_LEV, 0, 0xf, 1, alclvl),
> +	SOC_SINGLE_TLV("ALC Min Input Volume",  ML26124_ALC_MAXMIN_GAIN, 0, 7, 0, alcmingain),
> +	SOC_SINGLE_TLV("ALC MAX Input Volume",  ML26124_ALC_MAXMIN_GAIN, 4, 7, 1, alcmaxgain),
> +	SOC_SINGLE_TLV("Playback Limitter Min Input Volume",  ML26124_PL_MAXMIN_GAIN, 0, 7, 0, plmingain),
> +	SOC_SINGLE_TLV("Playback Limitter Max Input Volume",  ML26124_PL_MAXMIN_GAIN, 4, 7, 1, plmaxgain),
> +	SOC_SINGLE_TLV("Playback Boost Volume",  ML26124_PLYBAK_BOST_VOL, 0, 0x3f, 0, plvl),
> +	SOC_SINGLE("DC High Pass Filter Switch", ML26124_FILTER_EN, 0, 1, 0),
> +	SOC_SINGLE("NOISE High Pass Filter Switch", ML26124_FILTER_EN, 1, 1, 0),
> +	SOC_SINGLE("EQ BAND0 Switch", ML26124_FILTER_EN, 2, 1, 0),
> +	SOC_SINGLE("EQ BAND1 Switch", ML26124_FILTER_EN, 3, 1, 0),
> +	SOC_SINGLE("EQ BAND2 Switch", ML26124_FILTER_EN, 4, 1, 0),
> +	SOC_SINGLE("EQ BAND3 Switch", ML26124_FILTER_EN, 5, 1, 0),
> +	SOC_SINGLE("EQ BAND4 Switch", ML26124_FILTER_EN, 6, 1, 0),
> +
> +	SOC_SINGLE("Play Limitter Switch", ML26124_FILTER_EN, 0, 1, 0),
> +	SOC_SINGLE("Capture Limitter Switch", ML26124_FILTER_EN, 1, 1, 0),
> +	SOC_SINGLE("Digital Volume Fade Switch", ML26124_FILTER_EN, 3, 1, 0),
> +	SOC_SINGLE("Digital Volume Switch", ML26124_FILTER_EN, 4, 1, 0),
> +};
> +
> +static const struct snd_kcontrol_new ml26124_output_mixer_controls[] = {
> +	SOC_DAPM_SINGLE("DAC Switch", ML26124_SPK_AMP_OUT, 1, 1, 0),
> +	SOC_DAPM_SINGLE("Line in Switch", ML26124_SPK_AMP_OUT, 3, 1, 0),
> +	SOC_DAPM_SINGLE("PGA Switch", ML26124_SPK_AMP_OUT, 5, 1, 0),
> +};
> +
> +/* Input mux */
> +static const char *ml26124_input_select[] = {"Analog MIC in", "Digital MIC in"};

const char * const

> +
> +static const struct soc_enum ml26124_insel_enum =
> +	SOC_ENUM_SINGLE(ML26124_MIC_IF_CTL, 0, 1, ml26124_input_select);
> +
> +static const struct snd_kcontrol_new ml26124_input_mux_controls =
> +SOC_DAPM_ENUM("Input Select", ml26124_insel_enum);
> +
> +static const struct snd_kcontrol_new ml26124_line_control =
> +        SOC_DAPM_SINGLE("Switch", ML26124_PW_LOUT_PW_MNG, 1, 1, 0);
> +
> +static const struct snd_soc_dapm_widget ml26124_dapm_widgets[] = {
> +	SND_SOC_DAPM_SUPPLY("MCLK", ML26124_CLK_EN, 0, 0, NULL, 0),
> +	SND_SOC_DAPM_SUPPLY("PLL", ML26124_CLK_EN, 1, 0, NULL, 0),
> +	SND_SOC_DAPM_VMID("VMID"),
> +	SND_SOC_DAPM_MICBIAS("MICBIAS", ML26124_PW_REF_PW_MNG, 0, 0),
> +
> +	SND_SOC_DAPM_MIXER("Output Mixer", ML26124_PW_SPAMP_PW_MNG, 0x1f, 0, 
> +			   &ml26124_output_mixer_controls[0],
> +			   ARRAY_SIZE(ml26124_output_mixer_controls)),
> +	SND_SOC_DAPM_DAC("DAC", "Playback", ML26124_PW_DAC_PW_MNG, 1, 0),
> +
> +	SND_SOC_DAPM_ADC("ADC", "Capture", ML26124_PW_IN_PW_MNG, 1, 0),
> +	SND_SOC_DAPM_PGA("PGA", ML26124_PW_IN_PW_MNG, 3, 0, NULL, 0),
> +
> +	SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
> +			  &ml26124_input_mux_controls),
> +	SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0,
> +			     &ml26124_line_control),
> +	SND_SOC_DAPM_INPUT("VIDEOIN"),
> +	SND_SOC_DAPM_INPUT("MDIN"),
> +	SND_SOC_DAPM_INPUT("MIN"),
> +	SND_SOC_DAPM_INPUT("LIN"),
> +	SND_SOC_DAPM_OUTPUT("VIDEOOUT"),
> +	SND_SOC_DAPM_OUTPUT("SPOUT"),
> +	SND_SOC_DAPM_OUTPUT("LOUT"),
> +};
> +
> +static const struct snd_soc_dapm_route intercon[] = {
> +	/* output mixer */
> +	{"Output Mixer", "PGA Switch", "PGA"},
> +	{"Output Mixer", "DAC Switch", "DAC"},
> +	{"Output Mixer", NULL, "Line in Switch"},
> +	/* outputs */
> +	{"SPOUT", NULL, "Output Mixer"},
> +	{"LOUT", NULL, "Output Mixer"},
> +	{"SPOUT", NULL, "Output Mixer"},
> +
> +	/* input mux */
> +	{"Input Mux", "Analog MIC in", "MICBIAS"},
> +	{"Input Mux", "Digital MIC in", "MICBIAS"},
> +	{"ADC", NULL, "Input Mux"},
> +
> +	/* inputs */
> +	{"DAC", NULL, "MDIN"},
> +	{"PGA", NULL, "MIN"},
> +	{"SPOUT", NULL, "LIN"},
> +};
> +
> +static int ml26124_snd_card_codec_set(int number, struct ml26124_priv *priv,
> +				      struct snd_soc_codec *codec)
> +{
> +	unsigned char data;
> +	unsigned int rate = priv->rate;
> +	unsigned int channels = priv->ch;
> +
> +	data = snd_soc_read(codec, ML26124_PW_MICBIAS_VOL);

data does not seem to be used.

> +	snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 1);	/* soft reset assert */
> +	snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 0);	/* soft reset negate */
> +
> +	snd_soc_update_bits(codec, 0x0c, 0x1f, 0);	/* Stop clock  */
> +
> +	switch (rate) {
> +	case 16000:
> +		snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, 3);
> +		snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, 0x0c);
> +		snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, 0);
> +		snd_soc_update_bits(codec, ML26124_PLLML, 0xff, 0x20);
> +		snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, 0x00);
> +		snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, 0x04);
> +		break;
> +	case 32000:
> +		snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, 6);
> +		snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, 0x0c);
> +		snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, 0);
> +		snd_soc_update_bits(codec, ML26124_PLLML, 0xff, 0x20);
> +		snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, 0x00);
> +		snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, 0x04);
> +		break;
> +	case 48000:
> +		snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, 0x08);
> +		snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, 0x0c);
> +		snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, 0x00);
> +		snd_soc_update_bits(codec, ML26124_PLLML, 0xff, 0x30);
> +		snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, 0x00);
> +		snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, 0x04);
> +		break;
> +	default:
> +		pr_err("%s:this rate is no support for ml26124\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	snd_soc_update_bits(codec, ML26124_CLK_EN, 0x3, 3); /* Start MCLK and PLL */
> +	msleep(20);
> +	snd_soc_update_bits(codec, ML26124_CLK_EN, 0x0f, 0xf); /* Clock control: MCLKI use */
> +	snd_soc_update_bits(codec, ML26124_CLK_CTL, 0x7, 4);
> +
> +	if (channels == 1) {
> +		snd_soc_update_bits(codec, ML26124_SAI_TRANS_CTL, 0xff, 0x23); /* SAI transmitter control */
> +					/* 0x23 : FMTO=1, H=Left L=Right, no-delay*/
> +		snd_soc_update_bits(codec, ML26124_SAI_RCV_CTL, 0xff, 0x23);  /* Receive side SAI control */
> +	} else {
> +		snd_soc_update_bits(codec, ML26124_SAI_TRANS_CTL, 0xff, 0x00);
> +		snd_soc_update_bits(codec, ML26124_SAI_RCV_CTL, 0xff, 0x00);
> +	}
> +
> +	snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG, 0x3, 0x06); /* Analog REference Power Managemet */
> +	snd_soc_update_bits(codec, ML26124_PW_IN_PW_MNG, 0xa, 0x0a); /* Analog Input Power Management */
> +	snd_soc_update_bits(codec, ML26124_PW_DAC_PW_MNG, 0x2, 0x02); /* DAC power Management */
> +
> +	snd_soc_update_bits(codec, ML26124_REC_PLYBAK_RUN, 0x17, 0x01); /* Record/Playback Running Control Register */
> +	snd_soc_update_bits(codec, ML26124_REC_PLYBAK_RUN, 0x17, 0x03);
> +	msleep(20);
> +	snd_soc_update_bits(codec, ML26124_FILTER_EN, 0xff, 0x03); /* DSP filter function Enable */
> +
> +	snd_soc_update_bits(codec, ML26124_PW_SPK_AMP_VOL, 0x3f, 0x27); /* Speaker Amplifier Volume Control */
> +	snd_soc_update_bits(codec, ML26124_PW_MIC_IN_VOL, 0x3f, 0x20); /* Mic Input Volume Control */
> +
> +	return 0;
> +}
> +
> +static int ml26124_hw_params(struct snd_pcm_substream *substream,
> +			    struct snd_pcm_hw_params *hw_params,
> +			    struct snd_soc_dai *dai)
> +{
> +	struct snd_soc_codec *codec = dai->codec;
> +	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
> +
> +	int ret = ml26124_snd_card_codec_set(substream->number, priv, codec);

Since this is the only place where ml26124_snd_card_codec_set is used you
can probably just move the functions code here.

> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int ml26124_snd_card_codec_free(int number, struct ml26124_priv *priv,
> +				       struct snd_soc_codec *codec)
> +{
> +	snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 1);	/* soft reset assert */
> +
> +	return 0;
> +}
> +
> +static int snd_card_ml7213i2s_hw_free(struct snd_pcm_substream *substream,
> +				      struct snd_soc_dai *dai)
> +{
> +	struct ml26124_priv *priv = substream->runtime->private_data;
> +	struct snd_soc_codec *codec = dai->codec;
> +
> +	if (ml26124_snd_card_codec_free(substream->number, priv, codec))
> +			return -1;

Same here.

> +
> +	return snd_pcm_lib_free_pages(substream);
> +}
> +
> +static void ml26124_shutdown(struct snd_pcm_substream *substream,
> +			    struct snd_soc_dai *dai)
> +{
> +}

No need for empty callbacks, just set it to NULL instead.

> +
> +#define DVOL_CTL_DVMUTE_ON		BIT(4)	/* Digital volume MUTE On */
> +#define DVOL_CTL_DVMUTE_OFF		0	/* Digital volume MUTE Off */
> +
> +static int ml26124_mute(struct snd_soc_dai *dai, int mute)
> +{
> +	struct snd_soc_codec *codec = dai->codec;
> +
> +	if (mute)
> +		snd_soc_update_bits(codec, ML26124_DVOL_CTL, BIT(4),
> +					 DVOL_CTL_DVMUTE_ON);
> +	else
> +		snd_soc_update_bits(codec, ML26124_DVOL_CTL, BIT(4),
> +					 DVOL_CTL_DVMUTE_OFF);
> +	return 0;
> +}
> +
> +static int ml26124_set_dai_fmt(struct snd_soc_dai *codec_dai,
> +		unsigned int fmt)
> +{
> +	struct snd_soc_codec *codec = codec_dai->codec;
> +	unsigned char mode;
> +
> +	/* set master/slave audio interface */
> +	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
> +	case SND_SOC_DAIFMT_CBM_CFM:
> +		mode = 1;
> +		break;
> +	case SND_SOC_DAIFMT_CBS_CFS:
> +		mode = 0;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	snd_soc_update_bits(codec, ML26124_SAI_MODE_SEL, BIT(0), mode);
> +
> +	/* interface format */
> +	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> +	case SND_SOC_DAIFMT_I2S:
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	/* clock inversion */
> +	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
> +	case SND_SOC_DAIFMT_NB_NF:
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +#define REF_PM_MICBEN	BIT(2)	/* MIC BIAS Enable */
> +#define REF_PM_LOBIAS	BIT(6)	/* LOUT terminal BIAS Enable (1/2REGOUT) */
> +#define REF_PM_VMID_ON	BIT(1)	/* VMID generation circuit ON */
> +#define REF_PM_VMID_ON_FAST	BIT(0) /* VMID generation circuit Fast mode ON */
> +#define REF_PM_VMID_OFF	0	/* VMID generation circuit OFF */
> +
> +#define ML26134_CACHESIZE 79
> +static const u16 ml26124_reg[ML26134_CACHESIZE] = {
> +	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
> +	0x0, 0x0, 0x0, 				/* System Control */
> +	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,	/* Power Management */
> +	0x4,					/* Analog Reference Control */
> +	0x10, 0x0, 0x33, 0x0, 0x0,	/* Input/Output Amplifier Control */
> +	0x0, 0x0, 0x1,				/* Analog Path Contorl */
> +	0x0, 0x0, 0x0,				/* Audio Interface Control */
> +	0x1, 0x0, 0x0, 0xff, 0xff, 0x10,	/* DSP Control */
> +	0x7, 0x7, 0x7, 0x7, 0x7,		/* DSP Control */
> +	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,	/* DSP Control */
> +	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,	/* DSP Control */
> +	0x0, 0x0, 0x0,
> +	0x0, 0x2, 0x3, 0x0, 0xb, 0x70, 0x0, 0x0,	/* ALC Control*/
> +	0x4, 0x5, 0xd, 0x70, 0x10, 0x0,		/* Playback Limiter Control */
> +	0x1, 0x1, 0x1				/* Video Amplifer Control */
> +};
> +
> +#define ML26124_MICBEN	BIT(2)
> +#define ML26124_VMID	BIT(0) | BIT(1)
> +
> +static int ml26124_set_bias_level(struct snd_soc_codec *codec,
> +		enum snd_soc_bias_level level)
> +{
> +	u8 reg;
> +	switch (level) {
> +	case SND_SOC_BIAS_ON:
> +		/* MICBIAS/VMID ON */
> +		reg = snd_soc_read(codec, ML26124_PW_REF_PW_MNG) & 0x47;
> +		reg |= ML26124_MICBEN | ML26124_VMID;
> +		snd_soc_write(codec, ML26124_PW_REF_PW_MNG, reg);
> +	case SND_SOC_BIAS_PREPARE:
> +		break;
> +	case SND_SOC_BIAS_STANDBY:
> +		/* MICBIAS OFF */
> +		reg = snd_soc_read(codec, ML26124_PW_REF_PW_MNG) & 0x47;
> +		reg &= ~ML26124_MICBEN;
> +		snd_soc_write(codec, ML26124_PW_REF_PW_MNG, reg);
> +		break;
> +	case SND_SOC_BIAS_OFF:
> +		/* MICBIAS/VMID OFF */
> +		reg = snd_soc_read(codec, ML26124_PW_REF_PW_MNG) & 0x47;
> +		reg &= ~(ML26124_MICBEN | ML26124_VMID);
> +		snd_soc_write(codec, ML26124_PW_REF_PW_MNG, reg);
> +		break;
> +	}

Should MICBIAS be a SUPPLY DAPM widget?

> +	codec->dapm.bias_level = level;
> +	return 0;
> +}
> +
> +#define ML26124_RATES SNDRV_PCM_RATE_8000_96000
> +
> +#define ML26124_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
> +	SNDRV_PCM_FMTBIT_S24_LE)
> +
> +static struct snd_soc_dai_ops ml26124_dai_ops = {

const

> +	.hw_params	= ml26124_hw_params,
> +	.hw_free	= snd_card_ml7213i2s_hw_free,
> +	.shutdown	= ml26124_shutdown,
> +	.digital_mute	= ml26124_mute,
> +	.set_fmt	= ml26124_set_dai_fmt,
> +};
> +
> +struct snd_soc_dai_driver ml26124_dai = {

static

> +	.name = "ml26124-hifi",
> +	.playback = {
> +		.stream_name = "Playback",
> +		.channels_min = 1,
> +		.channels_max = 2,
> +		.rates = ML26124_RATES,
> +		.formats = ML26124_FORMATS,},
> +	.capture = {
> +		.stream_name = "Capture",
> +		.channels_min = 1,
> +		.channels_max = 2,
> +		.rates = ML26124_RATES,
> +		.formats = ML26124_FORMATS,},
> +	.ops = &ml26124_dai_ops,
> +	.symmetric_rates = 1,
> +};
> +
> +#ifdef CONFIG_PM
> +static int ml26124_suspend(struct snd_soc_codec *codec, pm_message_t state)
> +{
> +	ml26124_set_bias_level(codec, SND_SOC_BIAS_OFF);
> +
> +	return 0;
> +}
> +
> +static int ml26124_resume(struct snd_soc_codec *codec)
> +{
> +	ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
> +
> +	return 0;
> +}
> +#else
> +#define ml26124_suspend NULL
> +#define ml26124_resume NULL
> +#endif
> +
> +static int ml26124_probe(struct snd_soc_codec *codec)
> +{
> +	int ret;
> +	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
> +	struct snd_soc_dapm_context *dapm = &codec->dapm;
> +
> +	ret = snd_soc_codec_set_cache_io(codec, 7, 8, priv->control_type);

I don't think there is support for 7/8 register types, just use 8/8 instead.

> +	if (ret < 0) {
> +		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* Software Reset */
> +	snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 1);
> +	snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 0);
> +
> +	ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
> +
> +	snd_soc_add_controls(codec, ml26124_snd_controls,
> +			     ARRAY_SIZE(ml26124_snd_controls));
> +
> +	snd_soc_dapm_new_controls(dapm, ml26124_dapm_widgets,
> +				  ARRAY_SIZE(ml26124_dapm_widgets));

Use the table based setup for the controls and dapm controls. And probably
also for the routes which don't seem to be added at all right now.

> +
> +	return 0;
> +}
> +
> +/* power down chip */
> +static int ml26124_remove(struct snd_soc_codec *codec)
> +{
> +	return 0;
> +}

Again, no need for empty callbacks.


> +#define REG_CACHE_SIZE		0x79
> +static struct snd_soc_codec_driver soc_codec_dev_ml26124 = {
> +	.probe =	ml26124_probe,
> +	.remove =	ml26124_remove,
> +	.suspend =	ml26124_suspend,
> +	.resume =	ml26124_resume,
> +	.set_bias_level = ml26124_set_bias_level,
> +	.reg_cache_size = REG_CACHE_SIZE,
> +	.reg_word_size = sizeof(u8),
> +	.reg_cache_default = ml26124_reg,
> +};
> +
> +static __devinit int ml26124_i2c_probe(struct i2c_client *i2c,
> +				      const struct i2c_device_id *id)
> +{
> +	struct ml26124_priv *priv;
> +	int ret;
> +
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +	if (priv == NULL)
> +		return -ENOMEM;
> +
> +	i2c_set_clientdata(i2c, priv);
> +	priv->control_type = SND_SOC_I2C;

If SND_SOC_I2C is the only value which is going to be assigned to
control_type, you can pass it to snd_soc_codec_set_cache_io directly and
remove control_type.

> +
> +	ret =  snd_soc_register_codec(&i2c->dev,
> +			&soc_codec_dev_ml26124, &ml26124_dai, 1);
> +	if (ret < 0)
> +		kfree(priv);
> +
> +	return ret;
> +}
> +
> +static __devexit int ml26124_i2c_remove(struct i2c_client *client)
> +{
> +	snd_soc_unregister_codec(&client->dev);
> +	kfree(i2c_get_clientdata(client));
> +	return 0;
> +}
> +
> +static const struct i2c_device_id ml26124_i2c_id[] = {
> +	{ "ml26124", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, ml26124_i2c_id);
> +
> +static struct i2c_driver ml26124_i2c_driver = {
> +	.driver = {
> +		.name = "ml26124",
> +		.owner = THIS_MODULE,
> +	},
> +	.probe =    ml26124_i2c_probe,
> +	.remove =   __devexit_p(ml26124_i2c_remove),
> +	.id_table = ml26124_i2c_id,
> +};
> +
> +static int __init ml26124_modinit(void)
> +{
> +	int ret = 0;
> +
> +	ret = i2c_add_driver(&ml26124_i2c_driver);
> +	if (ret != 0) {
> +		pr_err("Failed to register ML26124 I2C driver: %d\n", ret);
> +	}
> +
> +	return ret;
> +}
> +module_init(ml26124_modinit);
> +
> +static void __exit ml26124_exit(void)
> +{
> +	i2c_del_driver(&ml26124_i2c_driver);
> +}
> +module_exit(ml26124_exit);
> +
> +MODULE_AUTHOR("Tomoya MORINAGA <tomoya-linux at dsn.lapis-semi.com>");
> +MODULE_DESCRIPTION("LAPIS Semiconductor ML26124 ALSA SoC codec driver");
> +MODULE_LICENSE("GPL");
> diff --git a/sound/soc/codecs/ml26124.h b/sound/soc/codecs/ml26124.h
> new file mode 100644
> index 0000000..f295701


More information about the Alsa-devel mailing list