Skip to contentSkip to conversation

sound/soc/codecs: add LAPIS Semiconductor ML26124

PATCH Superseded Tomoya MORINAGA 3 participants 5 messages Nov 25, 2011 – Nov 28, 2011

Subsystems (2)

v2 of 8 Superseded Latest is v8 →

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 sound/soc/codecs: add LAPIS Semiconductor ML26124

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.

---
 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
sound/soc/codecs/Kconfig +4 -0
index 665d924..ce8e622 100644
@@ -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
 
sound/soc/codecs/Makefile +2 -0
index 5119a7e..c1353ff 100644
@@ -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
sound/soc/codecs/ml26124.c new file +532 -0

532 lines not on this page · raw message

sound/soc/codecs/ml26124.h new file +123 -0

123 lines not on this page · raw message

Signature · 1 line
1.7.4.4
#1 lore raw
Quoted 91 lines · Tomoya MORINAGA

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.

---
 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
sound/soc/codecs/Kconfig +4 -0
index 665d924..ce8e622 100644
@@ -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
 
sound/soc/codecs/Makefile +2 -0
index 5119a7e..c1353ff 100644
@@ -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
sound/soc/codecs/ml26124.c new file +22 -0
new file mode 100644
index 0000000..56160de
@@ -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>

sound/soc/codecs/ml26124.c line 32

Quoted 10 lines
sound/soc/codecs/ml26124.c new file +10 -0
+#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.

Quoted 12 lines
sound/soc/codecs/ml26124.c new file +12 -0
+
+#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);

sound/soc/codecs/ml26124.c line 54

Quoted 10 lines
sound/soc/codecs/ml26124.c new file +10 -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.

Quoted 28 lines
sound/soc/codecs/ml26124.c new file +28 -0
+
+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),

sound/soc/codecs/ml26124.c line 92

Quoted 10 lines
sound/soc/codecs/ml26124.c new file +10 -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

Quoted 56 lines
sound/soc/codecs/ml26124.c new file +56 -0
+
+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"},

sound/soc/codecs/ml26124.c line 158

Quoted 10 lines
sound/soc/codecs/ml26124.c new file +10 -0
+};
+
+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.

Quoted 62 lines
sound/soc/codecs/ml26124.c new file +62 -0
+	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;

sound/soc/codecs/ml26124.c line 230

Quoted 10 lines
sound/soc/codecs/ml26124.c new file +10 -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.

Quoted 12 lines
sound/soc/codecs/ml26124.c new file +12 -0
+	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;

sound/soc/codecs/ml26124.c line 252

Quoted 10 lines
sound/soc/codecs/ml26124.c new file +10 -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.

sound/soc/codecs/ml26124.c line 260

Quoted 8 lines
sound/soc/codecs/ml26124.c new file +8 -0
+
+	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.

Quoted 98 lines
sound/soc/codecs/ml26124.c new file +98 -0
+
+#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;

sound/soc/codecs/ml26124.c line 368

Quoted 10 lines
sound/soc/codecs/ml26124.c new file +10 -0
+		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?

sound/soc/codecs/ml26124.c line 378

Quoted 10 lines
sound/soc/codecs/ml26124.c new file +10 -0
+	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

sound/soc/codecs/ml26124.c line 386

Quoted 8 lines
sound/soc/codecs/ml26124.c new file +8 -0
+	.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

Quoted 33 lines
sound/soc/codecs/ml26124.c new file +33 -0
+	.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

sound/soc/codecs/ml26124.c line 429

Quoted 10 lines
sound/soc/codecs/ml26124.c new file +10 -0
+#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.

Quoted 6 lines
sound/soc/codecs/ml26124.c new file +6 -0
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		return ret;
+	}
+
+	/* Software Reset */

sound/soc/codecs/ml26124.c line 445

Quoted 10 lines
sound/soc/codecs/ml26124.c new file +10 -0
+	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.

sound/soc/codecs/ml26124.c line 454

Quoted 9 lines
sound/soc/codecs/ml26124.c new file +9 -0
+
+	return 0;
+}
+
+/* power down chip */
+static int ml26124_remove(struct snd_soc_codec *codec)
+{
+	return 0;
+}

Again, no need for empty callbacks.

Quoted 14 lines
sound/soc/codecs/ml26124.c new file +14 -0
+#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)

sound/soc/codecs/ml26124.c line 478

Quoted 10 lines
sound/soc/codecs/ml26124.c new file +10 -0
+{
+	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.

Quoted 57 lines
sound/soc/codecs/ml26124.c new file +54 -0
+
+	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 <[email protected]>");
+MODULE_DESCRIPTION("LAPIS Semiconductor ML26124 ALSA SoC codec driver");
+MODULE_LICENSE("GPL");
sound/soc/codecs/ml26124.h new file +0 -0
new file mode 100644
index 0000000..f295701
#2 lore raw
In reply to Tomoya MORINAGA

On Fri, Nov 25, 2011 at 09:39:03PM +0900, Tomoya MORINAGA wrote:

This is better than the previous version but the major issue is that the driver isn't really fitting in with the subsystem - it's still relying on hard coded register write sequences for example.

Quoted 3 lines

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/

You should fix the word wrapping in your commit message to be well within 80 columns.

sound/soc/codecs/ml26124.c line 37

Quoted 2 lines
sound/soc/codecs/ml26124.c new file +2 -0
+struct ml26124_priv {
+	enum snd_soc_control_type control_type;

Since the device only supports I2C you should just unconditionally use I2C. You should also use regmap for register access.

sound/soc/codecs/ml26124.c line 82

Quoted 5 lines
sound/soc/codecs/ml26124.c new file +4 -0
+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("Digital Volume Switch", ML26124_FILTER_EN, 4, 1, 0),

This needs either a Playback or Capture in there to match up with the above unless it genuinely does mute both which would be really weird.

sound/soc/codecs/ml26124.c line 106

Quoted 1 line
sound/soc/codecs/ml26124.c new file +1 -0
+	SND_SOC_DAPM_VMID("VMID"),

Remove this unless it's actually used in your routing.

sound/soc/codecs/ml26124.c line 107

Quoted 1 line
sound/soc/codecs/ml26124.c new file +1 -0
+	SND_SOC_DAPM_MICBIAS("MICBIAS", ML26124_PW_REF_PW_MNG, 0, 0),

This should be a supply widget, MICBIAS widgets are legacy.

sound/soc/codecs/ml26124.c line 111

Quoted 3 lines
sound/soc/codecs/ml26124.c new file +3 -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)),

Do you *really* have a shift of 0x1f for the enable bit?

sound/soc/codecs/ml26124.c line 162

Quoted 12 lines
sound/soc/codecs/ml26124.c new file +12 -0
+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);
+	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  */

The same issue as with your previous submission applies to this function
- you shouldn't have hard coded register write sequences in your code, the driver should use the facilities of the subsystem to allow the user to configure it at runtime. Given how big an issue this is I've stopped reading at this point.

#3 lore raw

2011/11/25 Mark Brown <[email protected]>: wrote

Quoted 10 lines
Quoted 6 lines

+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("Digital Volume Switch", ML26124_FILTER_EN, 4, 1, 0),

This needs either a Playback or Capture in there to match up with the above unless it genuinely does mute both which would be really weird.

Digital Volume Switch is used for both Playback and Capture.

Regards,
tomoya

#4 lore raw
Quoted 8 lines · Tomoya MORINAGA

2011/11/25 Mark Brown <[email protected]>: wrote

Quoted 4 lines
Quoted 1 line

+     SOC_SINGLE("Digital Volume Switch", ML26124_FILTER_EN, 4, 1, 0),

This needs either a Playback or Capture in there to match up with the above unless it genuinely does mute both which would be really weird.

Digital Volume Switch is used for both Playback and Capture.

That's *really* odd. If that is the case it should probably just be "Digital Switch" - having both Volume and Switch doesn't make any sense.