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(a)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(a)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