linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124
@ 2012-02-23  5:46 Tomoya MORINAGA
  2012-02-23  5:46 ` [PATCH v3] sound/soc/lapis: add platform driver for ML7213 Tomoya MORINAGA
                   ` (2 more replies)
  0 siblings, 3 replies; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-02-23  5:46 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	alsa-devel, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, 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 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@gmail.com>
---
V6
 - Add break at "case SND_SOC_BIAS_ON:"
 - Use switch() statement
 - Delete ASoC cache code.
---
 sound/soc/codecs/Kconfig   |    4 +
 sound/soc/codecs/Makefile  |    2 +
 sound/soc/codecs/ml26124.c |  742 ++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/ml26124.h |  132 ++++++++
 4 files changed, 880 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 fa787d4..321ea08 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -39,6 +39,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_RT5631 if I2C
 	select SND_SOC_SGTL5000 if I2C
@@ -411,6 +412,9 @@ config SND_SOC_LM4857
 config SND_SOC_MAX9877
 	tristate
 
+config SND_SOC_ML26124
+	tristate
+
 config SND_SOC_TPA6130A2
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index a2c7842..b28f48e 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -25,6 +25,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-rt5631-objs := rt5631.o
 snd-soc-sgtl5000-objs := sgtl5000.o
@@ -126,6 +127,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_RT5631)	+= snd-soc-rt5631.o
 obj-$(CONFIG_SND_SOC_SGTL5000)  += snd-soc-sgtl5000.o
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
new file mode 100644
index 0000000..058b831
--- /dev/null
+++ b/sound/soc/codecs/ml26124.c
@@ -0,0 +1,742 @@
+/*
+ * 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 <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "ml26124.h"
+
+#define DVOL_CTL_DVMUTE_ON		BIT(4)	/* Digital volume MUTE On */
+#define DVOL_CTL_DVMUTE_OFF		0	/* Digital volume MUTE Off */
+#define ML26124_SAI_NO_DELAY	BIT(1)
+#define ML26124_SAI_FRAME_SYNC	(BIT(5) | BIT(0)) /* For mono (Telecodec) */
+#define ML26134_CACHESIZE 79
+#define ML26124_VMID	BIT(0)
+#define ML26124_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |\
+		       SNDRV_PCM_RATE_48000)
+#define ML26124_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+#define ML26124_NUM_REGISTER ML26134_CACHESIZE
+
+struct ml26124_priv {
+	u32 mclk;
+	u32 channels;
+	struct regmap *regmap;
+};
+
+struct clk_coeff {
+	u32 mclk;
+	u32 rate;
+	u8 pllnl;
+	u8 pllnh;
+	u8 pllml;
+	u8 pllmh;
+	u8 plldiv;
+};
+
+/* ML26124 configuration */
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7150, 50, 0);
+
+static const DECLARE_TLV_DB_SCALE(alclvl, -2250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(mingain, -1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(maxgain, -675, 600, 0);
+static const DECLARE_TLV_DB_SCALE(boost_vol, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(ngth, -7650, 150, 0);
+
+static const char * const ml26124_companding[] = {"16bit PCM", "u-law",
+						  "A-law"};
+
+static const struct soc_enum ml26124_adc_companding_enum
+	= SOC_ENUM_SINGLE(ML26124_SAI_TRANS_CTL, 6, 3, ml26124_companding);
+
+static const struct soc_enum ml26124_dac_companding_enum
+	= SOC_ENUM_SINGLE(ML26124_SAI_RCV_CTL, 6, 3, ml26124_companding);
+
+static const struct snd_kcontrol_new ml26124_snd_controls[] = {
+	SOC_SINGLE_TLV("Capture Digital Volume", ML26124_RECORD_DIG_VOL, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("Playback Digital Volume", ML26124_PLBAK_DIG_VOL, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("Digital Boost Volume", ML26124_DIGI_BOOST_VOL, 0,
+			0x3f, 0, boost_vol),
+	SOC_SINGLE_TLV("EQ Band0 Volume", ML26124_EQ_GAIN_BRAND0, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("EQ Band1 Volume", ML26124_EQ_GAIN_BRAND1, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("EQ Band2 Volume", ML26124_EQ_GAIN_BRAND2, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("EQ Band3 Volume", ML26124_EQ_GAIN_BRAND3, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("EQ Band4 Volume", ML26124_EQ_GAIN_BRAND4, 0,
+			0xff, 1, digital_tlv),
+	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, mingain),
+	SOC_SINGLE_TLV("ALC Max Input Volume", ML26124_ALC_MAXMIN_GAIN, 4,
+			7, 1, maxgain),
+	SOC_SINGLE_TLV("Playback Limitter Min Input Volume",
+			ML26124_PL_MAXMIN_GAIN, 0, 7, 0, mingain),
+	SOC_SINGLE_TLV("Playback Limitter Max Input Volume",
+			ML26124_PL_MAXMIN_GAIN, 4, 7, 1, maxgain),
+	SOC_SINGLE_TLV("Playback Boost Volume", ML26124_PLYBAK_BOST_VOL, 0,
+			0x3f, 0, boost_vol),
+	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("Zero Cross Comparator Switch", ML26124_PW_ZCCMP_PW_MNG, 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", ML26124_DVOL_CTL, 0, 1, 0),
+	SOC_SINGLE("Capture Limitter", ML26124_DVOL_CTL, 1, 1, 0),
+	SOC_SINGLE("Digital Volume Fade Switch", ML26124_DVOL_CTL, 3, 1, 0),
+	SOC_SINGLE("Digital Switch", ML26124_DVOL_CTL, 4, 1, 0),
+	SOC_ENUM("DAC Companding", ml26124_dac_companding_enum),
+	SOC_ENUM("ADC Companding", ml26124_adc_companding_enum),
+};
+
+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 * const ml26124_input_select[] = {"Analog MIC SingleEnded in",
+						"Analog MIC Differential in",
+						"Digital MIC in"};
+
+static const struct soc_enum ml26124_insel_enum =
+	SOC_ENUM_SINGLE(ML26124_MIC_IF_CTL, 0, 2, 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_SUPPLY("MICBIAS", ML26124_PW_REF_PW_MNG, 2, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("Output Mixer", ML26124_PW_SPAMP_PW_MNG, 0, 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("MDIN"),
+	SND_SOC_DAPM_INPUT("MIN"),
+	SND_SOC_DAPM_INPUT("LIN"),
+	SND_SOC_DAPM_OUTPUT("SPOUT"),
+	SND_SOC_DAPM_OUTPUT("LOUT"),
+};
+
+static const struct snd_soc_dapm_route ml26124_intercon[] = {
+	/* Supply */
+	{"DAC", NULL, "MCLK"},
+	{"ADC", NULL, "MCLK"},
+	{"DAC", NULL, "PLL"},
+	{"ADC", NULL, "PLL"},
+
+	/* output mixer */
+	{"Output Mixer", "PGA Switch", "PGA"},
+	{"Output Mixer", "DAC Switch", "DAC"},
+	{"Output Mixer", "Line in Switch", "LIN"},
+
+	/* outputs */
+	{"LOUT", NULL, "Output Mixer"},
+	{"SPOUT", NULL, "Output Mixer"},
+
+	/* input */
+	{"Input Mux", "Analog MIC SingleEnded in", "MIN"},
+	{"Input Mux", "Analog MIC Differential in", "MIN"},
+	{"ADC", NULL, "Input Mux"},
+};
+
+/* PLLOutputFreq(Hz) = InputMclkFreq(Hz) * PLLM / (PLLN * PLLDIV) */
+static const struct clk_coeff coeff_div[] = {
+	{12288800, 16000, 0xc, 0x0, 0x20, 0x0, 0x4},
+	{12288800, 32000, 0xc, 0x0, 0x20, 0x0, 0x4},
+	{12288800, 48000, 0xc, 0x0, 0x30, 0x0, 0x4},
+};
+
+static struct reg_default ml26124_reg[] = {
+	/* CLOCK control Register */
+	{0x00, 0x00 },	/* Sampling Rate */
+	{0x02, 0x00},	/* PLL NL */
+	{0x04, 0x00},	/* PLLNH */
+	{0x06, 0x00},	/* PLLML */
+	{0x08, 0x00},	/* MLLMH */
+	{0x0a, 0x00},	/* PLLDIV */
+	{0x0c, 0x00},	/* Clock Enable */
+	{0x0e, 0x00},	/* CLK Input/Output Control */
+
+	/* System Control Register */
+	{0x10, 0x00},	/* Software RESET */
+	{0x12, 0x00},	/* Record/Playback Run */
+	{0x14, 0x00},	/* Mic Input/Output control */
+
+	/* Power Management Register */
+	{0x20, 0x00},	/* Reference Power Management */
+	{0x22, 0x00},	/* Input Power Management */
+	{0x24, 0x00},	/* DAC Power Management */
+	{0x26, 0x00},	/* SP-AMP Power Management */
+	{0x28, 0x00},	/* LINEOUT Power Management */
+	{0x2a, 0x00},	/* VIDEO Power Management */
+	{0x2e, 0x00},	/* AC-CMP Power Management */
+
+	/* Analog reference Control Register */
+	{0x30, 0x04},	/* MICBIAS Voltage Control */
+
+	/* Input/Output Amplifier Control Register */
+	{0x32, 0x10},	/* MIC Input Volume */
+	{0x38, 0x00},	/* Mic Boost Volume */
+	{0x3a, 0x33},	/* Speaker AMP Volume */
+	{0x48, 0x00},	/* AMP Volume Control Function Enable */
+	{0x4a, 0x00},	/* Amplifier Volume Fader Control */
+
+	/* Analog Path Control Register */
+	{0x54, 0x00},	/* Speaker AMP Output Control */
+	{0x5a, 0x00},	/* Mic IF Control */
+	{0xe8, 0x01},	/* Mic Select Control */
+
+	/* Audio Interface Control Register */
+	{0x60, 0x00},	/* SAI-Trans Control */
+	{0x62, 0x00},	/* SAI-Receive Control */
+	{0x64, 0x00},	/* SAI Mode select */
+
+	/* DSP Control Register */
+	{0x66, 0x01},	/* Filter Func Enable */
+	{0x68, 0x00},	/* Volume Control Func Enable */
+	{0x6A, 0x00},	/* Mixer & Volume Control*/
+	{0x6C, 0xff},	/* Record Digital Volume */
+	{0x70, 0xff},	/* Playback Digital Volume */
+	{0x72, 0x10},	/* Digital Boost Volume */
+	{0x74, 0xe7},	/* EQ gain Band0 */
+	{0x76, 0xe7},	/* EQ gain Band1 */
+	{0x78, 0xe7},	/* EQ gain Band2 */
+	{0x7A, 0xe7},	/* EQ gain Band3 */
+	{0x7C, 0xe7},	/* EQ gain Band4 */
+	{0x7E, 0x00},	/* HPF2 CutOff*/
+	{0x80, 0x00},	/* EQ Band0 Coef0L */
+	{0x82, 0x00},	/* EQ Band0 Coef0H */
+	{0x84, 0x00},	/* EQ Band0 Coef0L */
+	{0x86, 0x00},	/* EQ Band0 Coef0H */
+	{0x88, 0x00},	/* EQ Band1 Coef0L */
+	{0x8A, 0x00},	/* EQ Band1 Coef0H */
+	{0x8C, 0x00},	/* EQ Band1 Coef0L */
+	{0x8E, 0x00},	/* EQ Band1 Coef0H */
+	{0x90, 0x00},	/* EQ Band2 Coef0L */
+	{0x92, 0x00},	/* EQ Band2 Coef0H */
+	{0x94, 0x00},	/* EQ Band2 Coef0L */
+	{0x96, 0x00},	/* EQ Band2 Coef0H */
+	{0x98, 0x00},	/* EQ Band3 Coef0L */
+	{0x9A, 0x00},	/* EQ Band3 Coef0H */
+	{0x9C, 0x00},	/* EQ Band3 Coef0L */
+	{0x9E, 0x00},	/* EQ Band3 Coef0H */
+	{0xA0, 0x00},	/* EQ Band4 Coef0L */
+	{0xA2, 0x00},	/* EQ Band4 Coef0H */
+	{0xA4, 0x00},	/* EQ Band4 Coef0L */
+	{0xA6, 0x00},	/* EQ Band4 Coef0H */
+
+	/* ALC Control Register */
+	{0xb0, 0x00},	/* ALC Mode */
+	{0xb2, 0x02},	/* ALC Attack Time */
+	{0xb4, 0x03},	/* ALC Decay Time */
+	{0xb6, 0x00},	/* ALC Hold Time */
+	{0xb8, 0x0b},	/* ALC Target Level */
+	{0xba, 0x70},	/* ALC Max/Min Gain */
+	{0xbc, 0x00},	/* Noise Gate Threshold */
+	{0xbe, 0x00},	/* ALC ZeroCross TimeOut */
+
+	/* Playback Limiter Control Register */
+	{0xc0, 0x04},	/* PL Attack Time */
+	{0xc2, 0x05},	/* PL Decay Time */
+	{0xc4, 0x0d},	/* PL Target Level */
+	{0xc6, 0x70},	/* PL Max/Min Gain */
+	{0xc8, 0x10},	/* Playback Boost Volume */
+	{0xca, 0x00},	/* PL ZeroCross TimeOut */
+
+	/* Video Amplifier Control Register */
+	{0xd0, 0x01},	/* VIDEO AMP Gain Control */
+	{0xd2, 0x01},	/* VIDEO AMP Setup 1 */
+	{0xd4, 0x01},	/* VIDEO AMP Control2 */
+};
+
+static int ml26124_update_bits(struct snd_soc_codec *codec, unsigned short reg,
+				unsigned int mask, unsigned int value)
+{
+	int change;
+	unsigned int old, new;
+	int ret;
+
+	ret = snd_soc_read(codec, reg);
+	if (ret < 0)
+		return ret;
+
+	old = ret;
+	new = (old & ~mask) | (value & mask);
+	change = old != new;
+	if (change) {
+		ret = snd_soc_write(codec, reg + 1, new);
+		if (ret < 0)
+			return ret;
+	}
+
+	return change;
+}
+
+/* Get sampling rate value of sampling rate setting register (0x0) */
+static inline int get_srate(int rate)
+{
+	int srate;
+
+	switch (rate) {
+	case 16000:
+		srate = 3;
+		break;
+	case 32000:
+		srate = 6;
+		break;
+	case 48000:
+		srate = 8;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return srate;
+}
+
+static inline int get_coeff(int mclk, int rate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+		if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+			return i;
+	}
+	return -EINVAL;
+}
+
+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 i = get_coeff(priv->mclk, params_rate(hw_params));
+
+	priv->channels = params_channels(hw_params);
+
+	switch (params_rate(hw_params)) {
+	case 16000:
+		ml26124_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
+				    get_srate(coeff_div[i].rate));
+		ml26124_update_bits(codec, ML26124_PLLNL, 0xff,
+				    coeff_div[i].pllnl);
+		ml26124_update_bits(codec, ML26124_PLLNH, 0x1,
+				    coeff_div[i].pllnh);
+		ml26124_update_bits(codec, ML26124_PLLML, 0xff,
+				    coeff_div[i].pllml);
+		ml26124_update_bits(codec, ML26124_PLLMH, 0x3f,
+				    coeff_div[i].pllmh);
+		ml26124_update_bits(codec, ML26124_PLLDIV, 0x1f,
+				    coeff_div[i].plldiv);
+		break;
+	case 32000:
+		ml26124_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
+				    get_srate(coeff_div[i].rate));
+		ml26124_update_bits(codec, ML26124_PLLNL, 0xff,
+				    coeff_div[i].pllnl);
+		ml26124_update_bits(codec, ML26124_PLLNH, 0x1,
+				    coeff_div[i].pllnh);
+		ml26124_update_bits(codec, ML26124_PLLML, 0xff,
+				    coeff_div[i].pllml);
+		ml26124_update_bits(codec, ML26124_PLLMH, 0x3f,
+				    coeff_div[i].pllmh);
+		ml26124_update_bits(codec, ML26124_PLLDIV, 0x1f,
+				    coeff_div[i].plldiv);
+		break;
+	case 48000:
+		ml26124_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
+				    get_srate(coeff_div[i].rate));
+		ml26124_update_bits(codec, ML26124_PLLNL, 0xff,
+				    coeff_div[i].pllnl);
+		ml26124_update_bits(codec, ML26124_PLLNH, 0x1,
+				    coeff_div[i].pllnh);
+		ml26124_update_bits(codec, ML26124_PLLML, 0xff,
+				    coeff_div[i].pllml);
+		ml26124_update_bits(codec, ML26124_PLLMH, 0x3f,
+				    coeff_div[i].pllmh);
+		ml26124_update_bits(codec, ML26124_PLLDIV, 0x1f,
+				    coeff_div[i].plldiv);
+		break;
+	default:
+		pr_err("%s:this rate is no support for ml26124\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ml26124_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	if (mute)
+		ml26124_update_bits(codec, ML26124_DVOL_CTL, BIT(4),
+				    DVOL_CTL_DVMUTE_ON);
+	else
+		ml26124_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)
+{
+	unsigned char mode;
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+	unsigned char mask = ML26124_SAI_NO_DELAY | ML26124_SAI_FRAME_SYNC;
+
+	/* 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;
+	}
+	ml26124_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;
+	}
+if (!priv)
+	return -1;
+
+	/* SAI configuration */
+	if (priv->channels == 1) {
+		ml26124_update_bits(codec, ML26124_SAI_TRANS_CTL, mask,
+				ML26124_SAI_NO_DELAY | ML26124_SAI_FRAME_SYNC);
+		ml26124_update_bits(codec, ML26124_SAI_RCV_CTL, mask,
+				ML26124_SAI_NO_DELAY | ML26124_SAI_FRAME_SYNC);
+	} else {
+		ml26124_update_bits(codec, ML26124_SAI_TRANS_CTL, mask,
+				    ML26124_SAI_NO_DELAY);
+		ml26124_update_bits(codec, ML26124_SAI_RCV_CTL, mask,
+				    ML26124_SAI_NO_DELAY);
+	}
+
+
+	return 0;
+}
+
+static int ml26124_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	switch (clk_id) {
+	case ML26124_USE_PLL:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (freq) {
+	case 12288000:
+		priv->mclk = freq;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int ml26124_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+				  int div_id, int div)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+
+	switch (div_id) {
+	case ML26124_MCLK:
+		ml26124_update_bits(codec, ML26124_CLK_CTL,
+				      BIT(0) | BIT(1), div);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int ml26124_set_bias_level(struct snd_soc_codec *codec,
+		enum snd_soc_bias_level level)
+{
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		pr_debug("%s: level=ON priv=%p\n", __func__, priv);
+		/* VMID ON */
+		ml26124_update_bits(codec, ML26124_PW_REF_PW_MNG,
+				    ML26124_VMID, ML26124_VMID);
+		msleep(500);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		pr_debug("%s: level=PREPARE\n", __func__);
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		pr_debug("%s: level=STANDBY\n", __func__);
+		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+			regcache_sync(priv->regmap);
+		break;
+	case SND_SOC_BIAS_OFF:
+		pr_debug("%s: level=OFF\n", __func__);
+		/* VMID OFF */
+		ml26124_update_bits(codec, ML26124_PW_REF_PW_MNG,
+				    ML26124_VMID, 0);
+		break;
+	}
+	codec->dapm.bias_level = level;
+	return 0;
+}
+
+static int ml26124_pcm_trigger(struct snd_pcm_substream *substream,
+			      int cmd, struct snd_soc_dai *codec_dai)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ml26124_update_bits(codec, ML26124_REC_PLYBAK_RUN, 0x3,
+				    0);
+		return 0;
+		break;
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			ml26124_update_bits(codec,
+					ML26124_REC_PLYBAK_RUN, 0x3, 1);
+		else
+			ml26124_update_bits(codec,
+					ML26124_REC_PLYBAK_RUN, 0x3, 2);
+		break;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops ml26124_dai_ops = {
+	.hw_params	= ml26124_hw_params,
+	.digital_mute	= ml26124_mute,
+	.set_fmt	= ml26124_set_dai_fmt,
+	.set_sysclk	= ml26124_set_dai_sysclk,
+	.set_clkdiv	= ml26124_set_dai_clkdiv,
+	.trigger	= ml26124_pcm_trigger,
+};
+
+static struct snd_soc_dai_driver ml26124_dai = {
+	.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)
+{
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	regcache_sync(priv->regmap);
+	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);
+	codec->control_data = priv->regmap;
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		return ret;
+	}
+
+	/* Software Reset */
+	ml26124_update_bits(codec, ML26124_SW_RST, 0x01, 1);
+	ml26124_update_bits(codec, ML26124_SW_RST, 0x01, 0);
+
+	ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+}
+
+/* power down chip */
+static struct snd_soc_codec_driver soc_codec_dev_ml26124 = {
+	.probe =	ml26124_probe,
+	.suspend =	ml26124_suspend,
+	.resume =	ml26124_resume,
+	.set_bias_level = ml26124_set_bias_level,
+	.dapm_widgets = ml26124_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(ml26124_dapm_widgets),
+	.dapm_routes = ml26124_intercon,
+	.num_dapm_routes = ARRAY_SIZE(ml26124_intercon),
+	.controls = ml26124_snd_controls,
+	.num_controls = ARRAY_SIZE(ml26124_snd_controls),
+};
+
+static const struct regmap_config ml26124_i2c_regmap = {
+	.val_bits = 8,
+	.reg_bits = 8,
+	.max_register = ML26124_NUM_REGISTER,
+	.reg_defaults = ml26124_reg,
+	.num_reg_defaults = ARRAY_SIZE(ml26124_reg),
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static __devinit int ml26124_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct ml26124_priv *priv;
+	int ret;
+
+	priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, priv);
+
+	priv->regmap = regmap_init_i2c(i2c, &ml26124_i2c_regmap);
+	if (IS_ERR(priv->regmap)) {
+		ret = PTR_ERR(priv->regmap);
+		dev_err(&i2c->dev, "regmap_init_i2c() failed: %d\n", ret);
+		return ret;
+	}
+
+	return snd_soc_register_codec(&i2c->dev,
+			&soc_codec_dev_ml26124, &ml26124_dai, 1);
+}
+
+static __devexit int ml26124_i2c_remove(struct i2c_client *client)
+{
+	struct ml26124_priv *priv = i2c_get_clientdata(client);
+
+	snd_soc_unregister_codec(&client->dev);
+	regmap_exit(priv->regmap);
+	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-codec",
+		.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;
+
+	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.rohm@gmail.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..d4a8df5
--- /dev/null
+++ b/sound/soc/codecs/ml26124.h
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+#ifndef ML26124_H
+#define ML26124_H
+
+/* Clock Control Register */
+#define ML26124_SMPLING_RATE		0x00
+#define ML26124_PLLNL			0x02
+#define ML26124_PLLNH			0x04
+#define ML26124_PLLML			0x06
+#define ML26124_PLLMH			0x08
+#define ML26124_PLLDIV			0x0a
+#define ML26124_CLK_EN			0x0c
+#define ML26124_CLK_CTL			0x0e
+
+/* System Control Register */
+#define ML26124_SW_RST			0x10
+#define ML26124_REC_PLYBAK_RUN		0x12
+#define ML26124_MIC_TIM			0x14
+
+/* Power Mnagement Register */
+#define ML26124_PW_REF_PW_MNG		0x20
+#define ML26124_PW_IN_PW_MNG		0x22
+#define ML26124_PW_DAC_PW_MNG		0x24
+#define ML26124_PW_SPAMP_PW_MNG		0x26
+#define ML26124_PW_LOUT_PW_MNG		0x28
+#define ML26124_PW_VOUT_PW_MNG		0x2a
+#define ML26124_PW_ZCCMP_PW_MNG		0x2e
+
+/* Analog Reference Control Register */
+#define ML26124_PW_MICBIAS_VOL		0x30
+
+/* Input/Output Amplifier Control Register */
+#define ML26124_PW_MIC_IN_VOL		0x32
+#define ML26124_PW_MIC_BOST_VOL		0x38
+#define ML26124_PW_SPK_AMP_VOL		0x3a
+#define ML26124_PW_AMP_VOL_FUNC		0x48
+#define ML26124_PW_AMP_VOL_FADE		0x4a
+
+/* Analog Path Control Register */
+#define ML26124_SPK_AMP_OUT		0x54
+#define ML26124_MIC_IF_CTL		0x5a
+#define ML26124_MIC_SELECT		0xe8
+
+/* Audio Interface Control Register */
+#define ML26124_SAI_TRANS_CTL		0x60
+#define ML26124_SAI_RCV_CTL		0x62
+#define ML26124_SAI_MODE_SEL		0x64
+
+/* DSP Control Register */
+#define ML26124_FILTER_EN		0x66
+#define ML26124_DVOL_CTL		0x68
+#define ML26124_MIXER_VOL_CTL		0x6a
+#define ML26124_RECORD_DIG_VOL		0x6c
+#define ML26124_PLBAK_DIG_VOL		0x70
+#define ML26124_DIGI_BOOST_VOL		0x72
+#define ML26124_EQ_GAIN_BRAND0		0x74
+#define ML26124_EQ_GAIN_BRAND1		0x76
+#define ML26124_EQ_GAIN_BRAND2		0x78
+#define ML26124_EQ_GAIN_BRAND3		0x7a
+#define ML26124_EQ_GAIN_BRAND4		0x7c
+#define ML26124_HPF2_CUTOFF		0x7e
+#define ML26124_EQBRAND0_F0L		0x80
+#define ML26124_EQBRAND0_F0H		0x82
+#define ML26124_EQBRAND0_F1L		0x84
+#define ML26124_EQBRAND0_F1H		0x86
+#define ML26124_EQBRAND1_F0L		0x88
+#define ML26124_EQBRAND1_F0H		0x8a
+#define ML26124_EQBRAND1_F1L		0x8c
+#define ML26124_EQBRAND1_F1H		0x8e
+#define ML26124_EQBRAND2_F0L		0x90
+#define ML26124_EQBRAND2_F0H		0x92
+#define ML26124_EQBRAND2_F1L		0x94
+#define ML26124_EQBRAND2_F1H		0x96
+#define ML26124_EQBRAND3_F0L		0x98
+#define ML26124_EQBRAND3_F0H		0x9a
+#define ML26124_EQBRAND3_F1L		0x9c
+#define ML26124_EQBRAND3_F1H		0x9e
+#define ML26124_EQBRAND4_F0L		0xa0
+#define ML26124_EQBRAND4_F0H		0xa2
+#define ML26124_EQBRAND4_F1L		0xa4
+#define ML26124_EQBRAND4_F1H		0xa6
+
+/* ALC Control Register */
+#define ML26124_ALC_MODE		0xb0
+#define ML26124_ALC_ATTACK_TIM		0xb2
+#define ML26124_ALC_DECAY_TIM		0xb4
+#define ML26124_ALC_HOLD_TIM		0xb6
+#define ML26124_ALC_TARGET_LEV		0xb8
+#define ML26124_ALC_MAXMIN_GAIN		0xba
+#define ML26124_NOIS_GATE_THRSH		0xbc
+#define ML26124_ALC_ZERO_TIMOUT		0xbe
+
+/* Playback Limiter Control Register */
+#define ML26124_PL_ATTACKTIME		0xc0
+#define ML26124_PL_DECAYTIME		0xc2
+#define ML26124_PL_TARGETTIME		0xc4
+#define ML26124_PL_MAXMIN_GAIN		0xc6
+#define ML26124_PLYBAK_BOST_VOL		0xc8
+#define ML26124_PL_0CROSS_TIMOUT	0xca
+
+/* Video Amplifer Control Register */
+#define ML26124_VIDEO_AMP_GAIN_CTL	0xd0
+#define ML26124_VIDEO_AMP_SETUP1	0xd2
+#define ML26124_VIDEO_AMP_CTL2		0xd4
+
+/* Clock select for machine driver */
+#define ML26124_USE_PLL			0
+#define ML26124_USE_MCLKI_256FS		1
+#define ML26124_USE_MCLKI_512FS		2
+#define ML26124_USE_MCLKI_1024FS	3
+
+enum ml26124_regs {
+	ML26124_MCLK = 0,
+};
+
+#endif
-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v3] sound/soc/lapis: add platform driver for ML7213
  2012-02-23  5:46 [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124 Tomoya MORINAGA
@ 2012-02-23  5:46 ` Tomoya MORINAGA
  2012-03-02 16:39   ` Mark Brown
  2012-02-23  5:46 ` [PATCH v5] sound/soc/lapis: add machine driver for ML7213 Carrier Board Tomoya MORINAGA
  2012-02-29 23:51 ` [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124 Mark Brown
  2 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-02-23  5:46 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	alsa-devel, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, Tomoya MORINAGA

This driver is for LAPIS Semiconductor ML7213 IOH I2S.

Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>
---
V3
 - Delete parameter "ignore_overrun"
 - Obey kernel comment description rule
 - if() statement replace switch() statement possible
 - Care for read-modify-write
 - Modify interrupt status condition
 - Change DMA function name "filter()"
 - Delete internal buffer
 - Add mapping function between number and channel
 - Delete magic numbers
 - Commonalize/Reduce functions
 - Use error macro (e.g. -ENOMEM) not negative integer value
 - Use ASoC suspend/resume functions.
---
 sound/soc/Kconfig                |    1 +
 sound/soc/Makefile               |    1 +
 sound/soc/lapis/Kconfig          |    4 +
 sound/soc/lapis/Makefile         |    4 +
 sound/soc/lapis/ioh_i2s.h        |   39 +
 sound/soc/lapis/ml7213ioh-plat.c | 1543 ++++++++++++++++++++++++++++++++++++++
 sound/soc/lapis/ml7213ioh-plat.h |  367 +++++++++
 7 files changed, 1959 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/lapis/Kconfig
 create mode 100644 sound/soc/lapis/Makefile
 create mode 100644 sound/soc/lapis/ioh_i2s.h
 create mode 100644 sound/soc/lapis/ml7213ioh-plat.c
 create mode 100644 sound/soc/lapis/ml7213ioh-plat.h

diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 1381db8..ff7678f 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -49,6 +49,7 @@ source "sound/soc/ep93xx/Kconfig"
 source "sound/soc/fsl/Kconfig"
 source "sound/soc/imx/Kconfig"
 source "sound/soc/jz4740/Kconfig"
+source "sound/soc/lapis/Kconfig"
 source "sound/soc/nuc900/Kconfig"
 source "sound/soc/omap/Kconfig"
 source "sound/soc/kirkwood/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 9ea8ac8..9592f41 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_SND_SOC)	+= ep93xx/
 obj-$(CONFIG_SND_SOC)	+= fsl/
 obj-$(CONFIG_SND_SOC)   += imx/
 obj-$(CONFIG_SND_SOC)	+= jz4740/
+obj-$(CONFIG_SND_SOC)	+= lapis/
 obj-$(CONFIG_SND_SOC)	+= mid-x86/
 obj-$(CONFIG_SND_SOC)	+= mxs/
 obj-$(CONFIG_SND_SOC)	+= nuc900/
diff --git a/sound/soc/lapis/Kconfig b/sound/soc/lapis/Kconfig
new file mode 100644
index 0000000..551e385
--- /dev/null
+++ b/sound/soc/lapis/Kconfig
@@ -0,0 +1,4 @@
+config SND_SOC_ML7213_PLATFORM
+	tristate "ML7213 IOH ASoC platform driver"
+	help
+	  This option enables support for the AC Link Controllers in ML7213 IOH SoC.
diff --git a/sound/soc/lapis/Makefile b/sound/soc/lapis/Makefile
new file mode 100644
index 0000000..aba1630
--- /dev/null
+++ b/sound/soc/lapis/Makefile
@@ -0,0 +1,4 @@
+# Platform
+snd-soc-ml7213-plat-objs := ml7213ioh-plat.o
+
+obj-$(CONFIG_SND_SOC_ML7213_PLATFORM) += snd-soc-ml7213-plat.o
diff --git a/sound/soc/lapis/ioh_i2s.h b/sound/soc/lapis/ioh_i2s.h
new file mode 100644
index 0000000..9f19f70
--- /dev/null
+++ b/sound/soc/lapis/ioh_i2s.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#ifndef ML7213_IOH_I2S
+#define ML7213_IOH_I2S
+
+enum ioh_bclkfs {
+	ML7213IOH_BCLKFS0 = 0,
+	ML7213IOH_BCLKFS1,
+	ML7213IOH_BCLKFS2,
+	ML7213IOH_BCLKFS3,
+	ML7213IOH_BCLKFS4,
+	ML7213IOH_BCLKFS5,
+};
+
+enum ioh_mclkfs {
+	ML7213IOH_MCLKFS0 = 6,
+	ML7213IOH_MCLKFS1,
+	ML7213IOH_MCLKFS2,
+	ML7213IOH_MCLKFS3,
+	ML7213IOH_MCLKFS4,
+	ML7213IOH_MCLKFS5,
+};
+
+#endif
diff --git a/sound/soc/lapis/ml7213ioh-plat.c b/sound/soc/lapis/ml7213ioh-plat.c
new file mode 100644
index 0000000..94ebfd6
--- /dev/null
+++ b/sound/soc/lapis/ml7213ioh-plat.c
@@ -0,0 +1,1543 @@
+/*
+ * 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/init.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+#include "ioh_i2s.h"
+#include "ml7213ioh-plat.h"
+
+static struct ioh_i2s_data *i2s_data;
+static struct ioh_i2s_dma dmadata[MAX_I2S_CH];
+
+/* I2S HAL (Hardware Abstruction Layer) */
+static void ioh_i2s_reset(int ch)
+{
+	iowrite32(1 << ch, i2s_data->iobase + I2SSRST_OFFSET);
+	iowrite32(0, i2s_data->iobase + I2SSRST_OFFSET);
+}
+
+static void ioh_i2s_enable_interrupts(int ch, int dir)
+{
+	unsigned int intr_lines = 0;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		intr_lines = 1 << (I2S_IMASK_RX_BIT_START + ch);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		intr_lines = 1 << (I2S_IMASK_TX_BIT_START + ch);
+		break;
+	}
+
+	/* enable interrupts for specified channel */
+	iowrite32(intr_lines, i2s_data->iobase + I2SIMASKCLR_OFFSET);
+}
+
+static void ioh_i2s_disable_interrupts(int ch, int dir)
+{
+	unsigned int intr_lines;
+
+	intr_lines = ioread32(i2s_data->iobase + I2SIMASK_OFFSET);
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		intr_lines |= 1 << (I2S_IMASK_RX_BIT_START + ch);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		intr_lines |= 1 << (I2S_IMASK_TX_BIT_START + ch);
+		break;
+	}
+
+	/* Mask the specific interrupt bits */
+	iowrite32(intr_lines, i2s_data->iobase + I2SIMASK_OFFSET);
+}
+
+#define IOH_FIFO_CLR	0
+#define IOH_FIFO_RUN	1
+
+static void ioh_i2s_ctrl_fifo(int ch, int dir, int ctrl)
+{
+	int offset = ch * 0x800;
+	u32 val;
+	u32 reg_addr = 0;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		reg_addr = I2SFIFOCRX_OFFSET;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		reg_addr = I2SFIFOCTX_OFFSET;
+		break;
+	}
+
+	val = ioread32(i2s_data->iobase + reg_addr + offset);
+	if (ctrl)
+		val |= I2S_FIFO_TX_RUN;
+	else
+		val |= I2S_FIFO_TX_FCLR;
+
+	iowrite32(val, i2s_data->iobase + reg_addr + offset);
+}
+
+/* Clear interrupt status */
+static void ioh_i2s_clear_sts_ir(int ch, int dir)
+{
+	int offset = ch * 0x800;
+	u32 reg_addr = 0;
+	u32 val = 0;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		reg_addr = I2SISTRX_OFFSET;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		reg_addr = I2SISTTX_OFFSET;
+		break;
+	}
+	val = ioread32(i2s_data->iobase + reg_addr + offset) & 0xf;
+	if (val)
+		iowrite32(val, i2s_data->iobase + reg_addr + offset);
+}
+
+static void ioh_i2s_clear_dma_mask(int ch, int dir)
+{
+	u32 val = 0;
+	u32 mask = 0;
+	u32 reg_addr = 0;
+	int offset = ch * 0x800;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		reg_addr = I2SMSKRX_OFFSET;
+		mask = RX_BIT_DMAMSK;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		reg_addr = I2SMSKTX_OFFSET;
+		mask = TX_BIT_DMAMSK;
+		break;
+	}
+
+	val = ioread32(i2s_data->iobase + reg_addr + offset);
+	val &= ~mask; /* Enable Tx DMA Request */
+
+	iowrite32(val, i2s_data->iobase + reg_addr + offset);
+}
+
+#define IOH_DIS_IRQ		0
+#define IOH_EN_IRQ		1
+
+static void ioh_i2s_irq_ctrl(int ch, int dir, int ctrl)
+{
+	u32 val;
+	int offset = ch * 0x800;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		val = ioread32(i2s_data->iobase + I2SMSKRX_OFFSET + offset);
+		if (ctrl) {
+			val &= ~RX_BIT_AFIMSK; /* Enable Almost empty IR */
+			val &= ~RX_BIT_FIMSK; /* Enable Empty IR */
+		} else {
+			val |= RX_BIT_AFIMSK; /* Disble Almost full IR */
+			val |= RX_BIT_FIMSK; /* Disble full IR */
+		}
+		iowrite32(val, i2s_data->iobase + I2SMSKRX_OFFSET + offset);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		val = ioread32(i2s_data->iobase + I2SMSKTX_OFFSET + offset);
+		if (ctrl) {
+			val &= ~TX_BIT_AEIMSK; /* Enable Almost empty IR */
+			val &= ~TX_BIT_EIMSK; /* Enable Empty IR */
+		} else {
+			val |= TX_BIT_AEIMSK; /* Disble Almost empty IR */
+			val |= TX_BIT_EIMSK; /* Disble Empty IR */
+		}
+		iowrite32(val, i2s_data->iobase + I2SMSKTX_OFFSET + offset);
+		break;
+	}
+
+}
+
+/* Linux standard DMA functions */
+static bool ioh_dma_filter(struct dma_chan *chan, void *slave)
+{
+	struct pch_dma_slave *param = slave;
+
+	if ((chan->chan_id == param->chan_id) && (param->dma_dev ==
+						  chan->device->dev)) {
+		chan->private = param;
+		return true;
+	} else {
+		return false;
+	}
+}
+
+int ioh_request_dma_channel(
+		   int ch, struct ioh_i2s_dma *ioh, enum dma_data_direction dir)
+{
+	dma_cap_mask_t mask;
+	struct dma_chan *chan;
+	struct pci_dev *dma_dev;
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(0, 1)); /* Get DMA's dev
+								information */
+
+	switch (dir) {
+	case DMA_FROM_DEVICE:
+		ioh->param_rx.width = ioh->dma_rx_width;
+		ioh->param_rx.dma_dev = &dma_dev->dev;
+		ioh->param_rx.chan_id = ch * 2 + 1; /* ch Rx=1,3,...11 */
+		ioh->param_rx.rx_reg = (dma_addr_t)(i2s_data->mapbase +\
+					ch * 0x800 + I2SDRRXMIRROR_OFFSET);
+		chan = dma_request_channel(mask, ioh_dma_filter,
+					   &ioh->param_rx);
+		if (chan == NULL) {
+			dev_err(i2s_data->dev, "Failed dma_request_channel for"
+				" I2S %d\n", ch);
+			return -ENOMEM;
+		}
+		ioh->chan_rx = chan;
+		break;
+	case DMA_TO_DEVICE:
+		ioh->param_tx.width = ioh->dma_tx_width;
+		ioh->param_tx.dma_dev = &dma_dev->dev;
+		ioh->param_tx.chan_id = ch * 2; /* DMA ch Tx=0,2,...10 */
+
+		ioh->param_tx.tx_reg = (dma_addr_t)(i2s_data->mapbase +\
+					ch * 0x800 + I2SDRTXMIRROR_OFFSET);
+
+		chan = dma_request_channel(mask, ioh_dma_filter,
+					   &ioh->param_tx);
+		if (chan == NULL) {
+			dev_err(i2s_data->dev, "Failed dma_request_channel for"
+				" I2S %d\n", ch);
+			return -ENOMEM;
+		}
+		ioh->chan_tx = chan;
+		break;
+	default:
+		dev_err(i2s_data->dev, "Invalid direction (%d)\n", dir);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void ioh_i2s_stop_i2s_regs(int ch, int dir)
+{
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		/* Interrupt stop */
+		ioh_i2s_irq_ctrl(ch, SNDRV_PCM_STREAM_CAPTURE, IOH_DIS_IRQ);
+
+		/* FIFO setting */
+		ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_CAPTURE, IOH_FIFO_CLR);
+		ioh_i2s_clear_sts_ir(ch, SNDRV_PCM_STREAM_CAPTURE);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		/* Interrupt stop */
+		ioh_i2s_irq_ctrl(ch, SNDRV_PCM_STREAM_PLAYBACK, IOH_DIS_IRQ);
+
+		/* FIFO setting */
+		ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_PLAYBACK, IOH_FIFO_CLR);
+		ioh_i2s_clear_sts_ir(ch, SNDRV_PCM_STREAM_PLAYBACK);
+		break;
+	}
+}
+
+static void ioh_i2s_configure_i2s_regs(int ch, int dir)
+{
+	int offset = ch * 0x800;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		/* Rx register */
+		iowrite32(I2S_AFULL_THRESH / 2,
+		i2s_data->iobase + I2SAFRX_OFFSET + offset);
+		iowrite32(ML7213_I2SAERX_DEFAULT,
+			  i2s_data->iobase + I2SAERX_OFFSET + offset);
+		iowrite32(ML7213_I2SMSKRX_DEFAULT,
+			  i2s_data->iobase + I2SMSKRX_OFFSET + offset);
+		iowrite32(ML7213_I2SISTRX_DEFAULT,
+			  i2s_data->iobase + I2SISTRX_OFFSET + offset);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		iowrite32(0, i2s_data->iobase + I2SAFTX_OFFSET + offset);
+		iowrite32(I2S_AEMPTY_THRESH / 2,
+		i2s_data->iobase + I2SAETX_OFFSET + offset);
+		iowrite32(ML7213_I2SMSKTX_DEFAULT,
+			  i2s_data->iobase + I2SMSKTX_OFFSET + offset);
+		iowrite32(ML7213_I2SISTTX_DEFAULT,
+			  i2s_data->iobase + I2SISTTX_OFFSET + offset);
+		break;
+	}
+}
+
+static void i2s_dma_rx_complete(void *arg)
+{
+	struct ioh_i2s_dma *ioh = (struct ioh_i2s_dma *)arg;
+	struct ml7213i2s_runtime_data *ioh_rtd =\
+				ioh->rx_substream->runtime->private_data;
+
+	pr_debug("%s in rx_cur_period=%d\n", __func__, ioh->rx_cur_period);
+
+	if (ioh_rtd->rx_stop) {
+		pr_debug("%s stopped. return.\n", __func__);
+		return;
+	}
+
+	ioh->rx_cur_period++;
+	if (ioh->rx_cur_period == ioh->buf_frags)
+		ioh->rx_cur_period = 0;
+
+	async_tx_ack(ioh->desc_rx);
+	if (ioh->rx_substream)
+		snd_pcm_period_elapsed(ioh->rx_substream);
+	ioh_i2s_irq_ctrl(ioh->number, SNDRV_PCM_STREAM_CAPTURE, IOH_EN_IRQ);
+}
+
+static void i2s_dma_tx_complete(void *arg)
+{
+	struct ioh_i2s_dma *ioh = (struct ioh_i2s_dma *)arg;
+	struct ml7213i2s_runtime_data *ioh_rtd =\
+				ioh->tx_substream->runtime->private_data;
+
+	pr_debug("%s in tx_cur_period=%d\n", __func__, ioh->tx_cur_period);
+
+	if (ioh_rtd->tx_stop) {
+		pr_debug("%s stopped. return.\n", __func__);
+		return;
+	}
+
+	ioh->tx_cur_period++;
+	if (ioh->tx_cur_period == ioh->buf_frags)
+		ioh->tx_cur_period = 0;
+
+	async_tx_ack(ioh->desc_tx);
+
+	if (ioh->tx_substream)
+		snd_pcm_period_elapsed(ioh->tx_substream);
+
+	ioh_i2s_irq_ctrl(ioh->number, SNDRV_PCM_STREAM_PLAYBACK, IOH_EN_IRQ);
+}
+
+static void i2s_dma_rx_start(struct ioh_i2s_dma *ioh, char *buff)
+{
+	struct dma_async_tx_descriptor *desc;
+	int rx_size;
+	int rx_num;
+	int i;
+	struct scatterlist *sg;
+
+	rx_size = I2S_AFULL_THRESH * 4;
+
+	/* The number of scatter list (Franction area is not used) */
+	if (ioh->period_bytes % rx_size)
+		/* rx_num = The number of scatter list */
+		rx_num = ioh->period_bytes / rx_size + 1;
+	else
+		rx_num = ioh->period_bytes / rx_size;
+
+	dev_dbg(i2s_data->dev, "%s: rx: scatter_num=%d scatter_size=%d\n",
+		__func__, rx_num, rx_size);
+
+	ioh->sg_rx_p =\
+	       kzalloc(sizeof(struct scatterlist) * rx_num, GFP_ATOMIC);
+
+	sg = ioh->sg_rx_p;
+
+	sg_init_table(sg, rx_num); /* Initialize SG table */
+	for (i = 0; i < rx_num; i++, sg++) {
+		sg_set_page(sg,  virt_to_page(buff), rx_size, rx_size * i);
+		sg_dma_len(sg) = rx_size / 4;
+		sg_dma_address(sg) = ioh->physical_addr + sg->offset;
+	}
+	ioh->rx_nent = rx_num;
+
+	dma_sync_sg_for_device(i2s_data->dev, ioh->sg_rx_p, ioh->rx_nent,
+			       DMA_FROM_DEVICE);
+
+	sg = ioh->sg_rx_p;
+
+	desc = ioh->chan_rx->device->device_prep_slave_sg(ioh->chan_rx,
+			sg, rx_num, DMA_FROM_DEVICE,
+			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(i2s_data->dev, "%s:device_prep_slave_sg Failed\n",
+			__func__);
+		return;
+	}
+	ioh_i2s_irq_ctrl(ioh->number, SNDRV_PCM_STREAM_CAPTURE, IOH_DIS_IRQ);
+
+	ioh->desc_rx = desc;
+
+	desc->callback = i2s_dma_rx_complete;
+	desc->callback_param = ioh;
+
+	desc->tx_submit(desc);
+}
+
+static void i2s_dma_tx_start(struct ioh_i2s_dma *ioh, char *buff)
+{
+	struct dma_async_tx_descriptor *desc;
+	int tx_size;
+	int tx_num;
+	int i;
+	struct scatterlist *sg;
+
+	tx_size = I2S_AEMPTY_THRESH * 4;
+	if (ioh->period_bytes % tx_size)
+		/* tx_num = The number of scatter list */
+		tx_num = ioh->period_bytes / tx_size + 1;
+	else
+		tx_num = ioh->period_bytes / tx_size;
+
+	dev_dbg(i2s_data->dev, "%s: tx: scatter_num=%d scatter_size=%d\n",
+		__func__, tx_num, tx_size);
+
+	ioh->sg_tx_p =\
+	       kzalloc(sizeof(struct scatterlist) * tx_num, GFP_ATOMIC);
+
+	sg = ioh->sg_tx_p;
+	sg_init_table(sg, tx_num); /* Initialize SG table */
+
+	for (i = 0; i < tx_num; i++, sg++) {
+		sg_set_page(sg,  virt_to_page(buff), tx_size, tx_size * i);
+		sg_dma_len(sg) = tx_size / 4;
+		sg_dma_address(sg) = ioh->physical_addr + sg->offset;
+	}
+	ioh->tx_nent = tx_num;
+
+	dma_sync_sg_for_device(i2s_data->dev, ioh->sg_tx_p, ioh->tx_nent,
+			       DMA_TO_DEVICE);
+
+	sg = ioh->sg_tx_p;
+
+	desc = ioh->chan_tx->device->device_prep_slave_sg(ioh->chan_tx,
+					sg, tx_num, DMA_TO_DEVICE,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(i2s_data->dev, "%s:device_prep_slave_sg Failed\n",
+			__func__);
+		return;
+	}
+
+	ioh_i2s_irq_ctrl(ioh->number, SNDRV_PCM_STREAM_PLAYBACK, IOH_DIS_IRQ);
+
+	ioh->desc_tx = desc;
+
+	desc->callback = i2s_dma_tx_complete;
+	desc->callback_param = ioh;
+	desc->tx_submit(desc);
+}
+
+static void ioh_i2s_release(int ch, int dir)
+{
+	struct ioh_i2s_dma *ioh;
+
+	ioh = &dmadata[ch];
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		dma_sync_sg_for_cpu(i2s_data->dev, ioh->sg_rx_p, ioh->rx_nent,
+				    DMA_FROM_DEVICE);
+
+		ioh_i2s_disable_interrupts(ch, SNDRV_PCM_STREAM_CAPTURE);
+		ioh_i2s_irq_ctrl(ch, SNDRV_PCM_STREAM_CAPTURE, IOH_DIS_IRQ);
+		if (ioh->chan_rx) {
+			ioh->chan_rx->device->device_control(ioh->chan_rx,
+							     DMA_TERMINATE_ALL,
+							     0);
+			dma_release_channel(ioh->chan_rx);
+			ioh->chan_rx = NULL;
+		}
+
+		kfree(ioh->sg_rx_p);
+		ioh->rx_buf_dma = 0;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		dma_sync_sg_for_cpu(i2s_data->dev, ioh->sg_tx_p, ioh->tx_nent,
+				    DMA_TO_DEVICE);
+
+		ioh_i2s_disable_interrupts(ch, SNDRV_PCM_STREAM_PLAYBACK);
+		ioh_i2s_irq_ctrl(ch, SNDRV_PCM_STREAM_PLAYBACK, IOH_DIS_IRQ);
+		if (ioh->chan_tx) {
+			ioh->chan_tx->device->device_control(ioh->chan_tx,
+							     DMA_TERMINATE_ALL,
+							     0);
+			dma_release_channel(ioh->chan_tx);
+			ioh->chan_tx = NULL;
+		}
+		kfree(ioh->sg_tx_p);
+		ioh->tx_buf_dma = 0;
+		break;
+	}
+}
+
+static inline void ioh_i2s_interrupt_sub_tx(int ch)
+{
+	unsigned int status;
+	int offset = ch * 0x800;
+	struct ioh_i2s_dma *ioh = &dmadata[ch];
+	struct ml7213i2s_runtime_data *ioh_rtd =\
+				ioh->tx_substream->runtime->private_data;
+	char *buff = ioh->dma_addr + ioh->tx_cur_period * ioh->period_bytes;
+
+	status = ioread32(i2s_data->iobase + I2SISTTX_OFFSET + offset);
+
+	if (status & I2S_TX_EINT)
+		dev_dbg(i2s_data->dev, "%s:I2S%d under flow occurs\n",
+			__func__, ch);
+	if (status & I2S_TX_AEINT)
+		if (!ioh_rtd->tx_stop)
+			i2s_dma_tx_start(ioh, buff);
+
+	/*Clear the interrupt status */
+	iowrite32(status, i2s_data->iobase + I2SISTTX_OFFSET + offset);
+}
+
+static inline void ioh_i2s_interrupt_sub_rx(int ch)
+{
+	unsigned int status;
+	int offset = ch * 0x800;
+	struct ioh_i2s_dma *ioh = &dmadata[ch];
+	struct ml7213i2s_runtime_data *ioh_rtd =\
+				ioh->rx_substream->runtime->private_data;
+	char *buff = ioh->dma_addr + ioh->rx_cur_period * ioh->period_bytes;
+
+	status = ioread32(i2s_data->iobase + I2SISTRX_OFFSET + offset);
+
+	if (status & I2S_RX_FINT)
+		dev_dbg(i2s_data->dev, "%s:I2S%d overrun occurs\n",
+			__func__, ch);
+	if (status & I2S_RX_AFINT)
+		if (!ioh_rtd->rx_stop)
+			i2s_dma_rx_start(ioh, buff);
+
+	/*Clear the interrupt status */
+	iowrite32(status, i2s_data->iobase + I2SISTRX_OFFSET + offset);
+}
+
+void ioh_i2s_event(u32 idisp, int ch)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&i2s_data->tx_lock, flags);
+
+	if (idisp & BIT(ch + 16)) {
+		dev_dbg(i2s_data->dev, "Rx%d interrupt occures\n", ch);
+		ioh_i2s_interrupt_sub_rx(ch);
+	}
+
+	if (idisp & BIT(ch)) {
+		dev_dbg(i2s_data->dev, "Tx%d interrupt occures\n", ch);
+		ioh_i2s_interrupt_sub_tx(ch);
+	}
+
+	spin_unlock_irqrestore(&i2s_data->tx_lock, flags);
+	return;
+}
+
+static irqreturn_t ioh_i2s_irq(int irq, void *data)
+{
+	int i;
+	u32 idisp;
+
+	idisp = ioread32(i2s_data->iobase + I2SIDISP_OFFSET);
+	for (i = 0; i < MAX_I2S_CH; i++)
+		ioh_i2s_event(idisp, i);
+
+	return IRQ_HANDLED;
+}
+
+static int snd_card_ml7213i2s_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ml7213i2s_runtime_data *ioh_rtd;
+
+	ioh_rtd = kzalloc(sizeof(*ioh_rtd), GFP_KERNEL);
+	if (ioh_rtd == NULL)
+		return -ENOMEM;
+
+	runtime->private_data = ioh_rtd;
+
+	/* makes the infrastructure responsible for freeing dma */
+	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	snd_soc_set_runtime_hwparams(substream, &ml7213i2s_pcm_hw);
+
+	spin_lock_init(&ioh_rtd->lock);
+	return 0;
+}
+
+static int snd_card_ml7213i2s_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	kfree(runtime->private_data);
+
+	return 0;
+}
+
+static int snd_card_ml7213i2s_hw_params(struct snd_pcm_substream *substream,
+				    struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct ml7213i2s_runtime_data *ioh_rtd = runtime->private_data;
+	struct ioh_i2s_dma *dma =\
+			snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+	ioh_rtd->dma = dma;
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	runtime->dma_bytes = params_buffer_bytes(hw_params);
+
+	return 0;
+}
+
+static int snd_card_ml7213i2s_hw_free(struct snd_pcm_substream *substream)
+{
+	struct ml7213i2s_runtime_data *ioh_rtd;
+	ioh_rtd = substream->runtime->private_data;
+
+	ioh_rtd->dma = NULL;
+	snd_pcm_set_runtime_buffer(substream, NULL);
+
+	return 0;
+}
+
+void ioh_i2s_irq_stop(int ch, int dir)
+{
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ioh_i2s_disable_interrupts(ch, SNDRV_PCM_STREAM_CAPTURE);
+		ioh_i2s_irq_ctrl(ch, SNDRV_PCM_STREAM_CAPTURE, IOH_DIS_IRQ);
+		ioh_i2s_clear_sts_ir(ch, SNDRV_PCM_STREAM_CAPTURE);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ioh_i2s_disable_interrupts(ch, SNDRV_PCM_STREAM_PLAYBACK);
+		ioh_i2s_irq_ctrl(ch, SNDRV_PCM_STREAM_PLAYBACK, IOH_DIS_IRQ);
+		ioh_i2s_clear_sts_ir(ch, SNDRV_PCM_STREAM_PLAYBACK);
+		break;
+	}
+}
+
+static inline void
+snd_card_ml7213i2s_pcm_i2s_start(struct snd_pcm_substream *substream)
+{
+	int ch;
+	struct ioh_i2s_dma *ioh;
+
+	switch (substream->stream) {
+	int ret;
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ch = dmadata[substream->number].mapped_rx_ch;
+		ioh = &dmadata[ch];
+		ioh->rx_substream = substream;
+		ret = ioh_request_dma_channel(ch, &dmadata[ch],
+					      DMA_FROM_DEVICE);
+		if (ret)
+			return;
+
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ch = dmadata[substream->number].mapped_tx_ch;
+		ioh = &dmadata[ch];
+		ioh->tx_substream = substream;
+		ret = ioh_request_dma_channel(ch, &dmadata[ch], DMA_TO_DEVICE);
+		if (ret)
+			return;
+
+		break;
+	}
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ch = dmadata[substream->number].mapped_rx_ch;
+		ioh_i2s_clear_sts_ir(ch, SNDRV_PCM_STREAM_CAPTURE);
+		ioh_i2s_clear_dma_mask(ch, SNDRV_PCM_STREAM_CAPTURE);
+		ioh_i2s_enable_interrupts(ch, SNDRV_PCM_STREAM_CAPTURE);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ch = dmadata[substream->number].mapped_tx_ch;
+		ioh_i2s_clear_sts_ir(ch, SNDRV_PCM_STREAM_PLAYBACK);
+		ioh_i2s_clear_dma_mask(ch, SNDRV_PCM_STREAM_PLAYBACK);
+		ioh_i2s_enable_interrupts(ch, SNDRV_PCM_STREAM_PLAYBACK);
+		break;
+	}
+}
+
+static int snd_card_ml7213i2s_pcm_trigger
+				(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ml7213i2s_runtime_data *ioh_rtd = runtime->private_data;
+	int err = 0;
+	unsigned long flags;
+	int ch;
+	spin_lock_irqsave(&ioh_rtd->lock, flags);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			ioh_rtd->rx_stop = 0;
+		else
+			ioh_rtd->tx_stop = 0;
+
+		snd_card_ml7213i2s_pcm_i2s_start(substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		pr_debug("stop..\n");
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+			ioh_rtd->rx_stop = 1;
+			ch = dmadata[substream->number].mapped_rx_ch;
+			ioh_i2s_irq_stop(ch, SNDRV_PCM_STREAM_CAPTURE);
+		} else {
+			ioh_rtd->tx_stop = 1;
+			ch = dmadata[substream->number].mapped_tx_ch;
+			ioh_i2s_irq_stop(ch, SNDRV_PCM_STREAM_PLAYBACK);
+		}
+		switch (substream->stream) {
+		case SNDRV_PCM_STREAM_CAPTURE:
+			ch = dmadata[substream->number].mapped_rx_ch;
+			ioh_i2s_release(ch, SNDRV_PCM_STREAM_CAPTURE);
+			break;
+		case SNDRV_PCM_STREAM_PLAYBACK:
+			ch = dmadata[substream->number].mapped_tx_ch;
+			ioh_i2s_release(ch, SNDRV_PCM_STREAM_PLAYBACK);
+			break;
+		}
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+
+	spin_unlock_irqrestore(&ioh_rtd->lock, flags);
+
+	return 0;
+}
+
+static int snd_card_ml7213i2s_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ml7213i2s_runtime_data *ioh_rtd = runtime->private_data;
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ioh_rtd->dma->rx_cur_period = 0;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ioh_rtd->dma->tx_cur_period = 0;
+		break;
+	}
+
+	ioh_rtd->dma->dma_addr = runtime->dma_area;
+	ioh_rtd->dma->physical_addr = runtime->dma_addr;
+	ioh_rtd->dma->buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+	ioh_rtd->dma->period_bytes = snd_pcm_lib_period_bytes(substream);
+	ioh_rtd->dma->buf_frags =\
+		ioh_rtd->dma->buffer_bytes / ioh_rtd->dma->period_bytes;
+
+	return 0;
+}
+
+static snd_pcm_uframes_t
+snd_card_ml7213i2s_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct ml7213i2s_runtime_data *ioh_rtd =\
+					substream->runtime->private_data;
+	unsigned long offset = 0;
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		offset =\
+		    ioh_rtd->dma->rx_cur_period * ioh_rtd->dma->period_bytes;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		offset =\
+		    ioh_rtd->dma->tx_cur_period * ioh_rtd->dma->period_bytes;
+		break;
+	}
+
+	return bytes_to_frames(substream->runtime, offset);
+}
+
+static struct snd_pcm_ops snd_card_ml7213i2s_ops = {
+	.open =			snd_card_ml7213i2s_open,
+	.close =		snd_card_ml7213i2s_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_card_ml7213i2s_hw_params,
+	.hw_free =		snd_card_ml7213i2s_hw_free,
+	.prepare =		snd_card_ml7213i2s_pcm_prepare,
+	.trigger =		snd_card_ml7213i2s_pcm_trigger,
+	.pointer =		snd_card_ml7213i2s_pcm_pointer,
+};
+
+static int ml7213ioh_alloc_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size =  ml7213i2s_pcm_hw.buffer_bytes_max;
+
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->private_data = NULL;
+	buf->area = dma_alloc_coherent(pcm->card->dev, size,
+			&buf->addr, GFP_KERNEL);
+	if (!buf->area)
+		return -ENOMEM;
+	buf->bytes = size;
+
+	return 0;
+}
+
+static u64 idma_mask = DMA_BIT_MASK(32);
+static int ml7213ioh_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_soc_dai *dai = rtd->cpu_dai;
+	struct snd_pcm *pcm = rtd->pcm;
+	int ret = 0;
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &idma_mask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+	if (dai->driver->playback.channels_min) {
+		ret = ml7213ioh_alloc_dma_buffer(pcm,
+			SNDRV_PCM_STREAM_PLAYBACK);
+		if (ret)
+			goto out;
+	}
+
+	if (dai->driver->capture.channels_min) {
+		ret = ml7213ioh_alloc_dma_buffer(pcm,
+			SNDRV_PCM_STREAM_CAPTURE);
+		if (ret)
+			goto out;
+	}
+out:
+	return ret;
+}
+
+static void ml7213ioh_pcm_free(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+		buf->area = NULL;
+	}
+}
+
+static struct snd_soc_platform_driver ml7213ioh_soc_platform = {
+	.pcm_new	= ml7213ioh_pcm_new,
+	.pcm_free	= ml7213ioh_pcm_free,
+	.ops		= &snd_card_ml7213i2s_ops,
+};
+
+/* DAI functions */
+static int ml7213i2s_dai_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *hw_params,
+			    struct snd_soc_dai *dai)
+{
+	int ch;
+	int byte;
+
+	switch (params_format(hw_params)) {
+	case SNDRV_PCM_FORMAT_U8:
+		byte = 8;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		byte = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		byte = 24;
+		break;
+	default:
+		pr_err("%s: Failed not support format\n", __func__);
+		return -EINVAL;
+		break;
+	}
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ch = dmadata[substream->number].mapped_rx_ch;
+		dmadata[ch].dma_rx_unit = byte;
+		dmadata[ch].dma_rx_width = PCH_DMA_WIDTH_4_BYTES;
+		ioh_i2s_configure_i2s_regs(ch, SNDRV_PCM_STREAM_CAPTURE);
+
+		/* FIFO setting */
+		ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_CAPTURE, IOH_FIFO_CLR);
+		ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_CAPTURE, IOH_FIFO_RUN);
+
+		/* Interrupt setting */
+		ioh_i2s_clear_sts_ir(ch, SNDRV_PCM_STREAM_CAPTURE);
+		ioh_i2s_irq_ctrl(ch, SNDRV_PCM_STREAM_CAPTURE, IOH_EN_IRQ);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ch = dmadata[substream->number].mapped_tx_ch;
+		dmadata[ch].dma_tx_unit = byte;
+		dmadata[ch].dma_tx_width = PCH_DMA_WIDTH_4_BYTES;
+		ioh_i2s_configure_i2s_regs(ch, SNDRV_PCM_STREAM_PLAYBACK);
+		/* FIFO setting */
+		ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_PLAYBACK, IOH_FIFO_CLR);
+		ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_PLAYBACK, IOH_FIFO_CLR);
+
+		/* Interrupt setting */
+		ioh_i2s_clear_sts_ir(ch, SNDRV_PCM_STREAM_PLAYBACK);
+		ioh_i2s_irq_ctrl(ch, SNDRV_PCM_STREAM_PLAYBACK, IOH_EN_IRQ);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_dai_set_dma_data(dai, substream, &dmadata[ch]);
+
+	return 0;
+}
+
+static int ml7213i2s_dai_hw_free(struct snd_pcm_substream *substream,
+				 struct snd_soc_dai *dai)
+{
+	unsigned int ch;
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ch = dmadata[substream->number].mapped_rx_ch;
+		ioh_i2s_stop_i2s_regs(ch, SNDRV_PCM_STREAM_CAPTURE);
+		break;
+
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ch = dmadata[substream->number].mapped_tx_ch;
+		ioh_i2s_stop_i2s_regs(ch, SNDRV_PCM_STREAM_PLAYBACK);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ml7213i2s_dai_set_dai_fmt(struct snd_soc_dai *dai,
+				     unsigned int fmt)
+{
+	u32 cmn_reg[MAX_I2S_CH];
+	u32 tx_reg[MAX_I2S_CH];
+	u32 rx_reg[MAX_I2S_CH];
+	int i;
+	int offset = 0;
+	void *iobase = i2s_data->iobase;
+
+	/* set master/slave audio interface */
+	for (i = 0; i < MAX_I2S_CH; i++, offset = i * 0x800) {
+		cmn_reg[i] = ioread32(iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+		tx_reg[i] = ioread32(iobase + offset + I2SCNTTX_OFFSET);
+		rx_reg[i] = ioread32(iobase + offset + I2SCNTRX_OFFSET);
+
+		switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+		case SND_SOC_DAIFMT_CBM_CFM:
+			cmn_reg[i] |= I2SCLKCNT_MSSEL;
+			break;
+		case SND_SOC_DAIFMT_CBS_CFS:
+			cmn_reg[i] &= ~I2SCLKCNT_MSSEL;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		/* interface format */
+		switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+		case SND_SOC_DAIFMT_I2S:
+			cmn_reg[i] |= ML7213I2S_LRCLK_FMT_I2S;
+			tx_reg[i] &= ~ML7213I2S_TX_I2S | ~ML7213I2S_TX_DLY |\
+				  ~ML7213I2S_TX_MSB_LSB |\
+				  ~ML7213I2S_TX_LR_POL | ~ML7213I2S_TX_AFT;
+			rx_reg[i] &= ~ML7213I2S_RX_I2S | ~ML7213I2S_RX_DLY |\
+				  ~ML7213I2S_RX_MSB_LSB |\
+				  ~ML7213I2S_RX_LR_POL | ~ML7213I2S_RX_AFT;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		/* clock inversion */
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			cmn_reg[i] &= ~ML7213I2S_BCLKPOL;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		iowrite32(cmn_reg[i], iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+		iowrite32(tx_reg[i], iobase + offset + I2SCNTTX_OFFSET);
+		iowrite32(rx_reg[i], iobase + offset + I2SCNTRX_OFFSET);
+	}
+
+	return 0;
+}
+
+static int ml7213i2s_dai_set_dai_sysclk(struct snd_soc_dai *dai,
+					int clk_id, unsigned int freq, int dir)
+{
+	u32 reg[MAX_I2S_CH];
+	void *iobase = i2s_data->iobase;
+	int i;
+
+	for (i = 0; i < MAX_I2S_CH; i++) {
+		reg[i] = ioread32(iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+		if (clk_id == IOH_MASTERCLKSEL_MCLK)
+			reg[i] &= ~ML7213I2S_MASTER_CLK_SEL;
+		else if (clk_id == IOH_MASTERCLKSEL_MLBCLK)
+			reg[i] |= ML7213I2S_MASTER_CLK_SEL;
+		iowrite32(reg[i], iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+	}
+
+	return 0;
+}
+
+static int ml7213i2s_dai_set_clkdiv(struct snd_soc_dai *dai,
+				  int div_id, int div)
+{
+	u32 bclkfs = 0;
+	u32 mclkfs = 0;
+	void *iobase = i2s_data->iobase;
+	int ch;
+	u32 i2sclkcnt;
+
+	switch (div_id) {
+	case ML7213IOH_BCLKFS0:
+	case ML7213IOH_MCLKFS0:
+		ch = 0;
+		break;
+	case ML7213IOH_BCLKFS1:
+	case ML7213IOH_MCLKFS1:
+		ch = 1;
+		break;
+	case ML7213IOH_BCLKFS2:
+	case ML7213IOH_MCLKFS2:
+		ch = 2;
+		break;
+	case ML7213IOH_BCLKFS3:
+	case ML7213IOH_MCLKFS3:
+		ch = 3;
+		break;
+	case ML7213IOH_BCLKFS4:
+	case ML7213IOH_MCLKFS4:
+		ch = 4;
+		break;
+	case ML7213IOH_BCLKFS5:
+	case ML7213IOH_MCLKFS5:
+		ch = 5;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (div_id) {
+	case ML7213IOH_BCLKFS0:
+	case ML7213IOH_BCLKFS1:
+	case ML7213IOH_BCLKFS2:
+	case ML7213IOH_BCLKFS3:
+	case ML7213IOH_BCLKFS4:
+	case ML7213IOH_BCLKFS5:
+		switch (div) {
+		case 8:
+			bclkfs = IOH_BCLKFS_8FS;
+			break;
+		case 16:
+			bclkfs = IOH_BCLKFS_16FS;
+			break;
+		case 32:
+			bclkfs = IOH_BCLKFS_32FS;
+			break;
+		case 64:
+			bclkfs = IOH_BCLKFS_64FS;
+			break;
+		}
+		break;
+	case ML7213IOH_MCLKFS0:
+	case ML7213IOH_MCLKFS1:
+	case ML7213IOH_MCLKFS2:
+	case ML7213IOH_MCLKFS3:
+	case ML7213IOH_MCLKFS4:
+	case ML7213IOH_MCLKFS5:
+		switch (div) {
+		case 64:
+			mclkfs = IOH_MCLKFS_64FS;
+			break;
+		case 128:
+			mclkfs = IOH_MCLKFS_128FS;
+			break;
+		case 192:
+			mclkfs = IOH_MCLKFS_192FS;
+			break;
+		case 256:
+			mclkfs = IOH_MCLKFS_256FS;
+			break;
+		case 384:
+			mclkfs = IOH_MCLKFS_384FS;
+			break;
+		case 512:
+			mclkfs = IOH_MCLKFS_512FS;
+			break;
+		case 768:
+			mclkfs = IOH_MCLKFS_768FS;
+			break;
+		case 1024:
+			mclkfs = IOH_MCLKFS_1024FS;
+			break;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	i2sclkcnt = ioread32(i2s_data->iobase + I2SCLKCNT0_OFFSET + 0x10 * ch);
+	i2sclkcnt |= bclkfs << I2SCLKCNT_BCLKFS_OFFSET;
+	i2sclkcnt |= mclkfs << I2SCLKCNT_MCLKFS_OFFSET;
+	iowrite32(i2sclkcnt, iobase + I2SCLKCNT0_OFFSET + 0x10 * ch);
+
+	return 0;
+}
+
+static int ml7213i2s_dai_set_channel_map(struct snd_soc_dai *dai,
+		unsigned int tx_num, unsigned int *tx_slot,
+		unsigned int rx_num, unsigned int *rx_slot)
+{
+	int i;
+	unsigned int slot;
+	unsigned int tx_mapped = 0, rx_mapped = 0;
+
+	if ((tx_num > MAX_I2S_CH) || (rx_num > MAX_I2S_CH))
+		return -EINVAL;
+
+	for (i = 0; i < tx_num; i++) {
+		slot = tx_slot[i];
+		if ((slot < MAX_I2S_CH) &&
+				(!(tx_mapped & (1 << slot)))) {
+			dmadata[i].mapped_tx_ch = slot;
+			tx_mapped |= 1 << slot;
+		} else
+			return -EINVAL;
+	}
+
+	for (i = 0; i < rx_num; i++) {
+		slot = rx_slot[i];
+		if ((slot < MAX_I2S_CH) &&
+				(!(rx_mapped & (1 << slot)))) {
+			dmadata[i].mapped_rx_ch = slot;
+			rx_mapped |= 1 << slot;
+		} else
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static void ioh_i2s_save_reg_conf(void)
+{
+	int i;
+	void *iobase;
+	struct ioh_i2s_pm_ch_reg *save;
+	struct ioh_i2s_pm_ch_reg_cmn *save_cmn;
+	int offset;
+
+	iobase = i2s_data->iobase;
+	for (i = 0, offset = 0; i < MAX_I2S_CH; i++, offset = i * 0x800) {
+		save = &i2s_data->ch_reg_save[i];
+
+		save->i2sdrtx = ioread32(iobase + offset + I2SDRTX_OFFSET);
+		save->i2scnttx = ioread32(iobase + offset + I2SCNTTX_OFFSET);
+		save->i2sfifoctx =
+				ioread32(iobase + offset + I2SFIFOCTX_OFFSET);
+		save->i2saftx = ioread32(iobase + offset + I2SAFTX_OFFSET);
+		save->i2saetx = ioread32(iobase + offset + I2SAETX_OFFSET);
+		save->i2smsktx = ioread32(iobase + offset + I2SMSKTX_OFFSET);
+		save->i2sisttx = ioread32(iobase + offset + I2SISTTX_OFFSET);
+
+		save->i2scntrx = ioread32(iobase + offset + I2SCNTRX_OFFSET);
+		save->i2sfifocrx =
+				ioread32(iobase + offset + I2SFIFOCRX_OFFSET);
+		save->i2safrx = ioread32(iobase + offset + I2SAFRX_OFFSET);
+		save->i2saerx = ioread32(iobase + offset + I2SAERX_OFFSET);
+		save->i2smskrx = ioread32(iobase + offset + I2SMSKRX_OFFSET);
+		save->i2sistrx = ioread32(iobase + offset + I2SISTRX_OFFSET);
+	}
+
+	save_cmn = &i2s_data->cmn_reg_save;
+	for (i = 0; i < MAX_I2S_CH; i++) {
+		save_cmn->i2sclkcnt[i] =
+		ioread32(i2s_data->iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+	}
+	save_cmn->i2simask = ioread32(i2s_data->iobase + I2SIMASK_OFFSET);
+}
+
+static void ioh_i2s_restore_reg_conf(void)
+{
+	int i;
+	void *iobase;
+	struct ioh_i2s_pm_ch_reg *save;
+	int offset;
+
+	iobase = i2s_data->iobase;
+	save = &i2s_data->ch_reg_save[0];
+	for (i = 0, offset = 0; i < MAX_I2S_CH; i++, offset = i * 0x800) {
+		iowrite32(save->i2sdrtx, iobase + offset + I2SDRTX_OFFSET);
+		iowrite32(save->i2scnttx, iobase + offset + I2SCNTTX_OFFSET);
+		iowrite32(save->i2sfifoctx,
+			  iobase + offset + I2SFIFOCTX_OFFSET);
+		iowrite32(save->i2saftx, iobase + offset + I2SAFTX_OFFSET);
+		iowrite32(save->i2saetx, iobase + offset + I2SAETX_OFFSET);
+		iowrite32(save->i2smsktx, iobase + offset + I2SMSKTX_OFFSET);
+		iowrite32(save->i2sisttx, iobase + offset + I2SISTTX_OFFSET);
+
+		iowrite32(save->i2scntrx, iobase + offset + I2SCNTRX_OFFSET);
+		iowrite32(save->i2sfifocrx,
+			  iobase + offset + I2SFIFOCRX_OFFSET);
+		iowrite32(save->i2safrx, iobase + offset + I2SAFRX_OFFSET);
+		iowrite32(save->i2saerx, iobase + offset + I2SAERX_OFFSET);
+		iowrite32(save->i2smskrx, iobase + offset + I2SMSKRX_OFFSET);
+		iowrite32(save->i2sistrx, iobase + offset + I2SISTRX_OFFSET);
+	}
+
+	for (i = 0; i < MAX_I2S_CH; i++) {
+		iowrite32(i2s_data->cmn_reg_save.i2sclkcnt[i],
+			 i2s_data->iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+	}
+
+	iowrite32(i2s_data->cmn_reg_save.i2simask,
+		  i2s_data->iobase + I2SIMASK_OFFSET);
+}
+
+static int ml7213i2s_soc_suspend(struct snd_soc_dai *cpu_dai)
+{
+	ioh_i2s_save_reg_conf();
+
+	return 0;
+}
+
+static int ml7213i2s_soc_resume(struct snd_soc_dai *cpu_dai)
+{
+	ioh_i2s_restore_reg_conf();
+
+	return 0;
+}
+#else
+#define ml7213i2s_soc_suspend NULL
+#define spdif_soc_resume NULL
+#endif
+
+static const struct snd_soc_dai_ops ml7213i2s_dai_ops = {
+	.hw_params	= ml7213i2s_dai_hw_params,
+	.hw_free	= ml7213i2s_dai_hw_free,
+	.set_fmt	= ml7213i2s_dai_set_dai_fmt,
+	.set_sysclk	= ml7213i2s_dai_set_dai_sysclk,
+	.set_clkdiv	= ml7213i2s_dai_set_clkdiv,
+	.set_channel_map	= ml7213i2s_dai_set_channel_map,
+};
+
+static struct snd_soc_dai_driver ml7213i2s_dai_data = {
+	.playback = {
+		.channels_min = USE_CHANNELS_MIN,
+		.channels_max = USE_CHANNELS_MAX,
+		.rates = ML7213_I2S_RATES,
+		.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |\
+			   SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.capture = {
+		.channels_min = USE_CHANNELS_MIN,
+		.channels_max = USE_CHANNELS_MAX,
+		.rates = ML7213_I2S_RATES,
+		.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |\
+			   SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.ops = &ml7213i2s_dai_ops,
+#ifdef CONFIG_PM
+	.suspend = ml7213i2s_soc_suspend,
+	.resume = ml7213i2s_soc_resume,
+#endif
+};
+
+/* PCI functions */
+DEFINE_PCI_DEVICE_TABLE(ioh_pci_tbl) = {
+	{
+		.vendor = PCI_VENDOR_ID_ROHM,
+		.device = PCI_DEVICE_ID_ML7213_I2S,
+		.subvendor = PCI_ANY_ID,
+		.subdevice = PCI_ANY_ID,
+	},
+	{0,}
+};
+
+static __devinit int ioh_i2s_probe(struct platform_device *pdev)
+{
+	int rv;
+
+	rv = snd_soc_register_platform(&pdev->dev, &ml7213ioh_soc_platform);
+	if (rv < 0)
+		printk(KERN_ERR "Failed to snd_soc_register_platform\n");
+
+	return rv;
+}
+
+static int __devexit ioh_i2s_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+
+	return 0;
+}
+
+static struct platform_driver ioh_i2s_driver_plat = {
+	.driver = {
+		.name = "ml7213-i2s-audio",
+		.owner = THIS_MODULE,
+	},
+	.probe	= ioh_i2s_probe,
+	.remove	= __devexit_p(ioh_i2s_remove),
+};
+
+static struct platform_driver ioh_dai_driver_plat = {
+	.driver = {
+		.name = "ml7213ioh",
+		.owner = THIS_MODULE,
+	},
+};
+
+static struct platform_device *ioh_platform;
+static struct platform_device *ioh_platform_dai;
+static int ioh_i2s_pci_probe(struct pci_dev *pdev,
+			     const struct pci_device_id *id)
+{
+	int rv = 0;
+	void __iomem *tbl;
+	unsigned int mapbase;
+	int i;
+
+	rv = pci_enable_device(pdev);
+	if (rv)
+		goto enable_device;
+
+	tbl = pci_iomap(pdev, 1, 0);
+	if (!tbl) {
+		rv = -ENOMEM;
+		printk(KERN_ERR "pci_iomap failed\n");
+		goto out_ipmap;
+	}
+
+	mapbase = pci_resource_start(pdev, 1);
+	if (!mapbase) {
+		rv = -ENOMEM;
+		printk(KERN_ERR "pci_resource_start failed\n");
+		goto out_pci_resource;
+	}
+
+	i2s_data = devm_kzalloc(&pdev->dev, sizeof(*i2s_data), GFP_KERNEL);
+	if (!i2s_data) {
+		dev_err(&pdev->dev, "Can't allocate i2s_data\n");
+		rv = -ENOMEM;
+		goto out_kzalloc_data;
+	}
+
+	i2s_data->dev = &pdev->dev;
+	i2s_data->iobase = tbl;
+	i2s_data->mapbase = mapbase;
+	spin_lock_init(&i2s_data->tx_lock);
+
+	rv = request_irq(pdev->irq, ioh_i2s_irq, IRQF_SHARED, "ml7213_ioh",
+			 pdev);
+	if (rv != 0) {
+		printk(KERN_ERR "Failed to allocate irq\n");
+		goto out_irq;
+	}
+
+	dev_set_name(&pdev->dev, "%s", "ml7213ioh");
+
+	rv = snd_soc_register_dai(&pdev->dev, &ml7213i2s_dai_data);
+	if (rv < 0) {
+		printk(KERN_ERR "Failed to snd_soc_register_dai\n");
+		goto out_register_dai;
+	}
+
+	ioh_platform = platform_device_alloc("ml7213-i2s-audio", -1);
+	if (!ioh_platform) {
+		rv = -ENOMEM;
+		goto out_dev_alloc;
+	}
+
+	platform_set_drvdata(ioh_platform, i2s_data);
+
+	rv = platform_device_add(ioh_platform);
+	if (rv) {
+		dev_err(&pdev->dev, "failed to add platform device\n");
+		goto out_plat_dev_add;
+	}
+
+	ioh_platform_dai = platform_device_alloc("ml7213ioh", -1);
+	if (!ioh_platform_dai) {
+		rv = -ENOMEM;
+		goto out_dev_alloc_dai;
+	}
+
+	platform_set_drvdata(ioh_platform_dai, i2s_data);
+
+	rv = platform_device_add(ioh_platform_dai);
+	if (rv) {
+		dev_err(&pdev->dev, "failed to add platform device\n");
+		goto out_plat_dev_add_dai;
+	}
+
+	for (i = 0; i < MAX_I2S_CH; i++)
+		dmadata[i].number = i;
+
+	return 0;
+
+out_plat_dev_add_dai:
+	platform_device_put(ioh_platform_dai);
+out_dev_alloc_dai:
+	platform_device_del(ioh_platform_dai);
+
+out_plat_dev_add:
+	platform_device_put(ioh_platform);
+
+out_dev_alloc:
+	platform_device_del(ioh_platform);
+
+out_register_dai:
+	snd_soc_unregister_platform(&pdev->dev);
+	free_irq(pdev->irq, pdev);
+out_irq:
+out_kzalloc_data:
+out_pci_resource:
+	pci_iounmap(pdev, i2s_data->iobase);
+out_ipmap:
+	pci_disable_device(pdev);
+enable_device:
+
+	return rv;
+}
+
+static void ioh_i2s_pci_remove(struct pci_dev *pdev)
+{
+	int i;
+
+	for (i = 0; i < MAX_I2S_CH; i++)
+		ioh_i2s_reset(i);
+
+	platform_device_unregister(ioh_platform_dai);
+	platform_device_unregister(ioh_platform);
+	snd_soc_unregister_dai(&pdev->dev);
+	snd_soc_unregister_platform(&pdev->dev);
+
+	free_irq(pdev->irq, pdev);
+	pci_iounmap(pdev, i2s_data->iobase);
+	pci_disable_device(pdev);
+}
+
+static int ioh_i2s_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	int ret;
+
+	ret = pci_save_state(pdev);
+	if (ret) {
+		dev_err(&pdev->dev,
+			" %s -pci_save_state returns %d\n", __func__, ret);
+		return ret;
+	}
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	pci_disable_device(pdev);
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+	return 0;
+}
+
+static int ioh_i2s_pci_resume(struct pci_dev *pdev)
+{
+	int ret;
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_err(&pdev->dev,
+		"%s-pci_enable_device failed(ret=%d) ", __func__, ret);
+		return ret;
+	}
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+
+	return 0;
+}
+
+static struct pci_driver ioh_i2s_driver = {
+	.name = DRV_NAME,
+	.probe = ioh_i2s_pci_probe,
+	.remove = __devexit_p(ioh_i2s_pci_remove),
+	.id_table = ioh_pci_tbl,
+#ifdef CONFIG_PM
+	.suspend = ioh_i2s_pci_suspend,
+	.resume = ioh_i2s_pci_resume,
+#endif
+};
+
+static int __init ioh_plat_init(void)
+{
+	platform_driver_register(&ioh_i2s_driver_plat);
+	platform_driver_register(&ioh_dai_driver_plat);
+	return pci_register_driver(&ioh_i2s_driver);
+}
+
+static void __exit ioh_i2s_cleanup(void)
+{
+	pci_unregister_driver(&ioh_i2s_driver);
+	platform_driver_unregister(&ioh_dai_driver_plat);
+	platform_driver_unregister(&ioh_i2s_driver_plat);
+}
+
+module_init(ioh_plat_init);
+module_exit(ioh_i2s_cleanup);
+
+MODULE_AUTHOR("Tomoya MORINAGA <tomoya.rohm@gmail.com>");
+MODULE_DESCRIPTION("LAPIS Semiconductor ML7213 IOH ALSA SoC platform driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/lapis/ml7213ioh-plat.h b/sound/soc/lapis/ml7213ioh-plat.h
new file mode 100644
index 0000000..bd198f7
--- /dev/null
+++ b/sound/soc/lapis/ml7213ioh-plat.h
@@ -0,0 +1,367 @@
+/*
+ * 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.
+ */
+
+#ifndef ML7213IOH_PLAT_H
+#define ML7213IOH_PLAT_H
+
+#include <linux/interrupt.h>
+#include <linux/pch_dma.h>
+
+#define DRV_NAME "ml7213ioh-i2s-pci"
+#define PCI_VENDOR_ID_ROHM	0X10DB
+#define PCI_DEVICE_ID_ML7213_I2S	0X8033
+
+#define I2SCLKCNT_MSSEL		BIT(0)
+#define ML7213I2S_BCLKPOL	BIT(1)
+#define ML7213I2S_LRCLK_FMT	(BIT(4) | BIT(5))
+#define ML7213I2S_LRCLK_FMT_I2S	BIT(4)
+
+#define ML7213I2S_TX_I2S	BIT(0)
+#define ML7213I2S_TX_DLY	BIT(12)
+#define ML7213I2S_TX_MSB_LSB	BIT(13)
+#define ML7213I2S_TX_LR_POL	BIT(14)
+#define ML7213I2S_TX_AFT	BIT(15)
+#define ML7213I2S_RX_I2S	BIT(0)
+#define ML7213I2S_RX_DLY	BIT(12)
+#define ML7213I2S_RX_MSB_LSB	BIT(13)
+#define ML7213I2S_RX_LR_POL	BIT(14)
+#define ML7213I2S_RX_AFT	BIT(15)
+#define ML7213I2S_MASTER_CLK_SEL	BIT(2)
+
+#define ML7213_I2S_RATES \
+	(SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000)
+
+/* ioh_bclkfs_t */
+#define	IOH_BCLKFS_8FS		0
+#define	IOH_BCLKFS_16FS		1
+#define	IOH_BCLKFS_32FS		2
+#define	IOH_BCLKFS_64FS		3
+
+#define	I2SCLKCNT_MCLKFS_OFFSET		(8)
+#define	I2SCLKCNT_BCLKFS_OFFSET		(12)
+
+#define I2SCLKCNT0_OFFSET	0x3000
+#define I2SCLKCNT1_OFFSET	0x3010
+#define I2SCLKCNT2_OFFSET	0x3020
+#define I2SCLKCNT3_OFFSET	0x3030
+#define I2SCLKCNT4_OFFSET	0x3040
+#define I2SCLKCNT5_OFFSET	0x3050
+#define I2SISTATUS_OFFSET	0x3080
+#define I2SIDISP_OFFSET		0x3084
+#define I2SIMASK_OFFSET		0x3088
+#define I2SIMASKCLR_OFFSET	0x308C
+#define I2SSRST_OFFSET		0x3FFC
+#define I2SDRTX_OFFSET		0x0
+#define I2SCNTTX_OFFSET		0x4
+#define I2SFIFOCTX_OFFSET	0x8
+#define I2SAFTX_OFFSET		0xC
+#define I2SAETX_OFFSET		0x10
+#define I2SMSKTX_OFFSET		0x14
+#define I2SISTTX_OFFSET		0x18
+#define I2SMONTX_OFFSET		0x1C
+#define I2SDRRX_OFFSET		0x20
+#define I2SCNTRX_OFFSET		0x24
+#define I2SFIFOCRX_OFFSET	0x28
+#define I2SAFRX_OFFSET		0x2C
+#define I2SAERX_OFFSET		0x30
+#define I2SMSKRX_OFFSET		0x34
+#define I2SISTRX_OFFSET		0x38
+#define I2SMONRX_OFFSET		0x3C
+#define FIRST_TX_OFFSET		0x0
+#define FIRST_RX_OFFSET		0x0
+
+#define I2SDRTXMIRROR_OFFSET	0x100
+#define I2SDRRXMIRROR_OFFSET	0x400
+
+#define I2S_ALL_INTERRUPT_BITS	0x3F003F
+#define I2S_IDISP_BITS		0x3F003F
+#define I2S_IDISP_TX_BITS	0x00003F
+#define I2S_IDISP_RX_BITS	0x3F0000
+#define TX_BIT_FIMSK	0x1	/*Fifo full interrupt mask bit*/
+#define TX_BIT_AFIMSK	0x2	/*Fifo Almost full interrupt mask bit*/
+#define TX_BIT_EIMSK	0x4	/*Fifo empty interrupt mask bit*/
+#define TX_BIT_AEIMSK	0x8	/*Fifo Almost empty interrupt mask bit*/
+#define TX_BIT_DMAMSK	0x10	/*Masks DMA*/
+#define TX_BIT_DMATC	0x100
+#define I2S_TX_ALL_INTR_MASK_BITS (TX_BIT_FIMSK | TX_BIT_AFIMSK | TX_BIT_EIMSK \
+							| TX_BIT_AEIMSK)
+#define I2S_TX_NORMAL_INTR_MASK_BITS (TX_BIT_FIMSK | TX_BIT_AFIMSK)
+#define RX_BIT_FIMSK	0x1	/*Fifo full interrupt mask bit*/
+#define RX_BIT_AFIMSK	0x2	/*Fifo Almost full interrupt mask bit*/
+#define RX_BIT_EIMSK	0x4	/*Fifo empty interrupt mask bit*/
+#define RX_BIT_AEIMSK	0x8	/*Fifo Almost empty interrupt mask bit*/
+#define RX_BIT_DMAMSK	0x10	/*Masks DMA*/
+#define RX_BIT_DMATC	0x100
+#define I2S_RX_ALL_INTR_MASK_BITS (RX_BIT_FIMSK | RX_BIT_AFIMSK | RX_BIT_EIMSK \
+							| RX_BIT_AEIMSK)
+#define I2S_RX_NORMAL_INTR_MASK_BITS (RX_BIT_EIMSK | RX_BIT_AEIMSK)
+#define I2S_TX_FINT	0x1	/*Full Interrupt*/
+#define I2S_TX_AFINT	0x2	/*Almost full interrupt*/
+#define I2S_TX_EINT	0x4	/*Empty interrupt*/
+#define I2S_TX_AEINT	0x8	/*Almost empty interrupt*/
+#define I2S_RX_FINT	0x1	/*Full Interrupt*/
+#define I2S_RX_AFINT	0x2	/*Almost full interrupt*/
+#define I2S_RX_EINT	0x4	/*Empty interrupt*/
+#define I2S_RX_AEINT	0x8	/*Almost empty interrupt*/
+
+#define I2S_FIFO_TX_FCLR	BIT(0)
+#define I2S_FIFO_TX_RUN		BIT(4)
+#define I2S_FIFO_RX_FCLR	BIT(0)
+#define I2S_FIFO_RX_RUN		BIT(4)
+
+#define FIFO_CTRL_BIT_TX_RUN	0x10
+#define FIFO_CTRL_BIT_RX_RUN	0x10
+#define I2S_CNT_BIT_TEL		0x1
+#define I2S_IMASK_TX_BIT_START	0
+#define I2S_IMASK_RX_BIT_START	16
+
+/* DMA processing */
+#define	PERIOD_POS_MAX		I2S_DMA_SG_NUM
+#define	PERIOD_LEN		(I2S_AFULL_THRESH * PERIOD_POS_MAX)
+
+#define	SUPPORT_FORMAT		(SNDRV_PCM_FMTBIT_U8 | \
+				 SNDRV_PCM_FMTBIT_S16_LE | \
+				 SNDRV_PCM_FMTBIT_S32_LE)
+#define	MAX_PERIOD_SIZE		(PERIOD_LEN * 4)
+
+#define USE_CHANNELS_MIN	1
+#define USE_CHANNELS_MAX	2
+#define	MAX_I2S_CH		6		/*I2S0 ~ I2S5*/
+#define USE_PERIODS_MIN		(I2S_DMA_SG_MAX)
+#define USE_PERIODS_MAX		(I2S_DMA_SG_MAX)
+
+#define I2S_AEMPTY_THRESH	64	/* Almost  Empty Threshold */
+#define I2S_AFULL_THRESH	64	/* Almost  Full Threshold */
+
+#define	I2S_DMA_SG_NUM		(128)
+#define	I2S_DMA_SG_MAX		(64)
+
+#define	IOH_MSSEL_MASTER	1
+
+#define ML7213_I2SAERX_DEFAULT	0x1f
+#define ML7213_I2SMSKRX_DEFAULT	0x1f
+#define ML7213_I2SISTRX_DEFAULT	0xC
+
+#define ML7213_I2SMSKTX_DEFAULT	0x1f
+#define ML7213_I2SISTTX_DEFAULT	0xC
+
+enum ioh_i2s_fifo_type {
+	IOH_FIFO_32 = 4,
+	IOH_FIFO_16 = 2,
+	IOH_FIFO_8 = 1,
+};
+
+enum ioh_i2s_status {
+	IOH_EOK = 0,
+	IOH_EDONE = 1,
+	IOH_EUNDERRUN = 2,
+	IOH_EOVERRUN = 3,
+	IOH_EFRAMESYNC = 4,
+};
+
+enum ioh_bclkpol_t {
+	ioh_BCLKPOL_FALLING = 0,
+	ioh_BCLKPOL_RISING,
+};
+
+enum ioh_masterclksel_t {
+	IOH_MASTERCLKSEL_MCLK = 0,
+	IOH_MASTERCLKSEL_MLBCLK,
+};
+
+enum ioh_lrckfmt_t {
+	IOH_LRCLKFMT_I2S = 1,
+	IOH_LRCLKFMT_LONGFRAME,
+	IOH_LRCLKFMT_SHORTFRAME,
+};
+
+enum ioh_mclkfs_t {
+	IOH_MCLKFS_64FS = 0,
+	IOH_MCLKFS_128FS,
+	IOH_MCLKFS_192FS,
+	IOH_MCLKFS_256FS,
+	IOH_MCLKFS_384FS,
+	IOH_MCLKFS_512FS,
+	IOH_MCLKFS_768FS,
+	IOH_MCLKFS_1024FS,
+};
+
+enum ioh_dlyoff_t {
+	IOH_DLYOFF_DLY_ON = 0,		/* date delat on */
+	IOH_DLYOFF_DLY_OFF,		/* date delat off */
+};
+
+enum ioh_lrpol_t {
+	IOH_LRPOL_NO_INVERT = 0,	/* Low of LRCLK is L data.
+					   High of LRCLK is R data. */
+	IOH_LRPOL_INVERT,		/* Low of LRCLK is R data.
+					   High of LRCLK is L data. */
+};
+
+enum ioh_aft_t {
+	IOH_AFR_FRONT = 0,
+	IOH_AFR_BACK,
+};
+
+struct ml7213i2s_runtime_data {
+	spinlock_t lock;
+	int tx_stop;
+	int rx_stop;
+	struct ioh_i2s_dma *dma;
+};
+
+struct ioh_i2s_pm_ch_reg {
+	u32 i2sdrtx;	/* Tx: data register */
+	u32 i2scnttx; /* Tx: control register */
+	u32 i2sfifoctx;	/* Tx: FIFO control register */
+	u32 i2saftx;	/* Tx: almost full threshold setting */
+	u32 i2saetx;	/* Tx: almost empty threshold setting */
+	u32 i2smsktx;	/* Tx: interrupt mask settings */
+	u32 i2sisttx;	/* Tx: for acknowledging interrupts */
+	u32 i2scntrx;	/* Rx: control register */
+	u32 i2sfifocrx; /* Rx: FIFO control register */
+	u32 i2safrx;	/* Rx: almost full threshold setting */
+	u32 i2saerx;	/* Rx: almost empty threshold setting */
+	u32 i2smskrx;	/* Rx: interrupt mask settings */
+	u32 i2sistrx;	/* Rx: for acknowledging interrupts */
+};
+
+struct ioh_i2s_pm_ch_reg_cmn {
+	u32 i2sclkcnt[MAX_I2S_CH];	/*clock control register(ch0~5) */
+	u32 i2simask;		/*interrupt mask */
+};
+
+struct ioh_i2s_data {
+	struct device *dev;
+	void *iobase;
+	unsigned int mapbase;
+	int ignore_rx_overrun;
+	spinlock_t tx_lock;
+	struct ioh_i2s_pm_ch_reg_cmn cmn_reg_save;
+	struct ioh_i2s_pm_ch_reg ch_reg_save[MAX_I2S_CH];
+};
+
+struct ioh_i2s_dma {
+	/* Transmit side DMA */
+	struct scatterlist	*sg_tx_p;
+	struct scatterlist	*sg_rx_p;
+
+	int tx_num;	/* The number of sent sg */
+	int rx_num;	/* The number of sent sg */
+
+	struct dma_chan			*chan_tx;
+	struct dma_chan			*chan_rx;
+
+	int rx_nent;	/* The number of rx scatter list */
+	int tx_nent;	/* The number of tx scatter list */
+
+	struct dma_async_tx_descriptor	*desc_tx;
+	struct dma_async_tx_descriptor	*desc_rx;
+
+	dma_addr_t			tx_buf_dma;
+	dma_addr_t			rx_buf_dma;
+
+	struct pch_dma_slave		param_tx;
+	struct pch_dma_slave		param_rx;
+
+	int dma_tx_unit; /* 1Byte of 2Byte or 4Byte */
+	int dma_rx_unit; /* 1Byte of 2Byte or 4Byte */
+	int dma_tx_width;
+	int dma_rx_width;
+
+	struct snd_pcm_substream *tx_substream;
+	struct snd_pcm_substream *rx_substream;
+
+	int number;
+
+	int mapped_tx_ch;
+	int mapped_rx_ch;
+
+	unsigned char *dma_addr;
+	dma_addr_t physical_addr;
+	unsigned long buffer_bytes;
+	unsigned long period_bytes;
+
+	int buf_frags;
+	int tx_cur_period;
+	int rx_cur_period;
+};
+
+struct ml7213i2s_dai {
+	struct snd_soc_dai_driver dai;
+	struct device	*dev;
+	void *iobase;
+	u32 freq;
+};
+
+struct ioh_i2s_config_common_reg {
+	u32 i2sclkcnt;	/*clock control register(ch0~5) */
+	u32 i2sistatus;	/*interrupt status */
+	u32 i2sidisp;		/*active interrupts */
+	u32 i2simask;		/*interrupt mask */
+	u32 i2simaskclr;	/*interrupt mask clear */
+};
+
+struct ioh_i2s_config_tx_reg {
+	u32 i2sdrtx;	/*data register */
+	u32 i2scnttx; /*control register */
+	u32 i2sfifoctx;	/*FIFO control register */
+	u32 i2saftx;	/*almost full threshold setting */
+	u32 i2saetx;	/*almost empty threshold setting */
+	u32 i2smsktx;	/*interrupt mask settings */
+	u32 i2sisttx;	/*for acknowledging interrupts */
+	u32 i2smontx;	/*monitor register */
+};
+
+struct ioh_i2s_config_rx_reg {
+	u32 i2sdrrx;	/* data register */
+	u32 i2scntrx;	/* control register */
+	u32 i2sfifocrx;/* FIFO control register */
+	u32 i2safrx;	/* almost full threshold setting */
+	u32 i2saerx;	/* almost empty threshold setting */
+	u32 i2smskrx;	/* interrupt mask settings */
+	u32 i2sistrx;	/* for acknowledging interrupts */
+	u32 i2smonrx;	/* monitor register */
+};
+
+struct ioh_i2s_config_reg {
+	/* The common register settings */
+	struct ioh_i2s_config_common_reg cmn;
+
+	/* TX channel settings */
+	struct ioh_i2s_config_tx_reg tx;
+
+	/* RX channel settings */
+	struct ioh_i2s_config_rx_reg rx;
+};
+
+static struct snd_pcm_hardware ml7213i2s_pcm_hw = {
+	.info =			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SUPPORT_FORMAT,
+	.channels_min =	USE_CHANNELS_MIN,
+	.channels_max =	USE_CHANNELS_MAX,
+	.buffer_bytes_max =	MAX_PERIOD_SIZE * USE_PERIODS_MAX,
+	.period_bytes_min =	MAX_PERIOD_SIZE,
+	.period_bytes_max =	MAX_PERIOD_SIZE,
+	.periods_min =	USE_PERIODS_MIN,
+	.periods_max =	USE_PERIODS_MAX,
+	.fifo_size =		0,
+};
+#endif
-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v5] sound/soc/lapis: add machine driver for ML7213 Carrier Board
  2012-02-23  5:46 [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124 Tomoya MORINAGA
  2012-02-23  5:46 ` [PATCH v3] sound/soc/lapis: add platform driver for ML7213 Tomoya MORINAGA
@ 2012-02-23  5:46 ` Tomoya MORINAGA
  2012-03-02 13:05   ` Mark Brown
  2012-02-29 23:51 ` [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124 Mark Brown
  2 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-02-23  5:46 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	alsa-devel, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, Tomoya MORINAGA

This machine driver is for LAPIS Semiconductor ML7213 Carrier Board.

Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>
---
V5
 - Add channe_map function
 - Use error macro (e.g. -ENOMEM) not negative integer value
 - Add clock configuration for codec
---
 sound/soc/lapis/Kconfig             |    5 +
 sound/soc/lapis/Makefile            |    2 +
 sound/soc/lapis/ml7213ioh-machine.c |  209 +++++++++++++++++++++++++++++++++++
 3 files changed, 216 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/lapis/ml7213ioh-machine.c

diff --git a/sound/soc/lapis/Kconfig b/sound/soc/lapis/Kconfig
index 551e385..e869e0c 100644
--- a/sound/soc/lapis/Kconfig
+++ b/sound/soc/lapis/Kconfig
@@ -2,3 +2,8 @@ config SND_SOC_ML7213_PLATFORM
 	tristate "ML7213 IOH ASoC platform driver"
 	help
 	  This option enables support for the AC Link Controllers in ML7213 IOH SoC.
+config SND_SOC_ML7213_MACHINE
+	tristate "ML7213 IOH ASoC machine driver"
+	select SND_SOC_ML7213_PLATFORM
+	help
+	  This is ASoC machine driver for ML7213 IOH
diff --git a/sound/soc/lapis/Makefile b/sound/soc/lapis/Makefile
index aba1630..7ec4bea 100644
--- a/sound/soc/lapis/Makefile
+++ b/sound/soc/lapis/Makefile
@@ -1,4 +1,6 @@
 # Platform
+snd-soc-ml7213-machine-objs := ml7213ioh-machine.o
 snd-soc-ml7213-plat-objs := ml7213ioh-plat.o
 
 obj-$(CONFIG_SND_SOC_ML7213_PLATFORM) += snd-soc-ml7213-plat.o
+obj-$(CONFIG_SND_SOC_ML7213_MACHINE) += snd-soc-ml7213-machine.o
diff --git a/sound/soc/lapis/ml7213ioh-machine.c b/sound/soc/lapis/ml7213ioh-machine.c
new file mode 100644
index 0000000..9fa9e5a
--- /dev/null
+++ b/sound/soc/lapis/ml7213ioh-machine.c
@@ -0,0 +1,209 @@
+/*
+ * ml7213ioh-machine.c -- SoC Audio for LAPIS Semiconductor ML7213 IOH CRB
+ *
+ * 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/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <linux/i2c.h>
+#include "../codecs/ml26124.h"
+#include "ioh_i2s.h"
+
+static const struct snd_soc_dapm_widget ml7213_dapm_widgets[] = {
+	SND_SOC_DAPM_SPK("Speaker", NULL),
+	SND_SOC_DAPM_LINE("LINEOUT", NULL),
+	SND_SOC_DAPM_LINE("LINEIN", NULL),
+	SND_SOC_DAPM_MIC("AnalogMIC", NULL),
+	SND_SOC_DAPM_MIC("DigitalMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route ml7213_routes[] = {
+	{"Speaker", NULL, "SPOUT"},
+	{"LINEOUT", NULL, "LOUT"},
+	{"MIN", NULL, "AnalogMIC"},
+	{"MDIN", NULL, "DigitalMIC"},
+	{"MIN", NULL, "LINEIN"},
+};
+
+static int ml7213_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *hw_params)
+{
+	int bclkfs;
+	int mclkfs;
+	unsigned int clk;
+	int ret;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	unsigned int channel_map[] = {5, 0, 1, 2, 3, 4};
+
+	/* set cpu DAI channel mapping */
+	ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
+		channel_map, ARRAY_SIZE(channel_map), channel_map);
+	if (ret < 0)
+		return ret;
+
+	switch (params_rate(hw_params)) {
+	case 16000:
+	case 32000:
+	case 48000:
+		clk = 12288000;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	mclkfs = clk / params_rate(hw_params);
+
+	switch (params_format(hw_params)) {
+	case SNDRV_PCM_FORMAT_U8:
+	case SNDRV_PCM_FORMAT_S16_LE:
+		bclkfs = 32;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		bclkfs = 64;
+		break;
+	default:
+		pr_err("%s: Failed not support format\n", __func__);
+		return -EINVAL;
+		break;
+	}
+
+	/* set the codec system clock for DAC and ADC */
+	ret = snd_soc_dai_set_sysclk(codec_dai, ML26124_USE_PLL, clk,
+				     SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, ML7213IOH_BCLKFS0, bclkfs);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, ML7213IOH_BCLKFS1, bclkfs);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, ML7213IOH_BCLKFS2, bclkfs);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, ML7213IOH_BCLKFS3, bclkfs);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, ML7213IOH_BCLKFS4, bclkfs);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, ML7213IOH_BCLKFS5, bclkfs);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, ML7213IOH_MCLKFS0, mclkfs);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, ML7213IOH_MCLKFS1, mclkfs);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, ML7213IOH_MCLKFS2, mclkfs);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, ML7213IOH_MCLKFS3, mclkfs);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, ML7213IOH_MCLKFS4, mclkfs);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, ML7213IOH_MCLKFS5, mclkfs);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(codec_dai, ML26124_MCLK, ML26124_USE_PLL);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct snd_soc_ops ml7213_ops = {
+	.hw_params = ml7213_hw_params,
+};
+
+static struct snd_soc_dai_link ioh_i2s_dai = {
+	.name = "ml26124",
+	.stream_name = "ML26124",
+	.cpu_dai_name = "ml7213ioh",
+	.codec_dai_name = "ml26124-hifi",
+	.platform_name	= "ml7213-i2s-audio",
+	.codec_name	= "ml26124-codec.1-001a",
+	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+					SND_SOC_DAIFMT_CBM_CFM,
+	.ops = &ml7213_ops,
+};
+
+static struct snd_soc_card ioh_i2s_card = {
+	.name		= "LAPIS Semiconductor ML7213CRB",
+	.dai_link	= &ioh_i2s_dai,
+	.num_links	= 1,
+	.dapm_widgets	= ml7213_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(ml7213_dapm_widgets),
+	.dapm_routes	= ml7213_routes,
+	.num_dapm_routes	= ARRAY_SIZE(ml7213_routes),
+};
+
+static struct platform_device *ioh_snd_device;
+
+
+static int __init ioh_i2s_init(void)
+{
+	int ret;
+	ioh_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!ioh_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(ioh_snd_device, &ioh_i2s_card);
+	ret = platform_device_add(ioh_snd_device);
+
+	if (ret) {
+		printk(KERN_ERR"%s: error out:\n", __func__);
+		platform_device_put(ioh_snd_device);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void __exit ioh_i2s_exit(void)
+{
+	return platform_device_unregister(ioh_snd_device);
+}
+
+module_init(ioh_i2s_init);
+module_exit(ioh_i2s_exit);
+
+MODULE_AUTHOR("Tomoya MORINAGA <tomoya.rohm@gmail.com>");
+MODULE_DESCRIPTION("LAPIS Semiconductor ML7213 IOH ALSA SoC machine driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform: ml7213ioh-i2s");
-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* Re: [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-02-23  5:46 [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124 Tomoya MORINAGA
  2012-02-23  5:46 ` [PATCH v3] sound/soc/lapis: add platform driver for ML7213 Tomoya MORINAGA
  2012-02-23  5:46 ` [PATCH v5] sound/soc/lapis: add machine driver for ML7213 Carrier Board Tomoya MORINAGA
@ 2012-02-29 23:51 ` Mark Brown
  2012-03-02  8:16   ` Tomoya MORINAGA
  2 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-02-29 23:51 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 3325 bytes --]

On Thu, Feb 23, 2012 at 02:46:49PM +0900, Tomoya MORINAGA wrote:

> +	SOC_SINGLE_TLV("Playback Limitter Min Input Volume",
> +			ML26124_PL_MAXMIN_GAIN, 0, 7, 0, mingain),

Limiter.

> +	SOC_SINGLE("Zero Cross Comparator Switch", ML26124_PW_ZCCMP_PW_MNG, 1,
> +		    1, 0),

Usually just "ZC Switch".

> +	SOC_SINGLE("Digital Switch", ML26124_DVOL_CTL, 4, 1, 0),

This should probably be Digital Playback Switch so it lines up with the
volume control in the UIs (and the Volume controls should be Digital X
Volume).

> +static int ml26124_update_bits(struct snd_soc_codec *codec, unsigned short reg,
> +				unsigned int mask, unsigned int value)

Why are you open coding this in your driver?  There is no point in
replicating subsystem functionality.

> +if (!priv)
> +	return -1;
> +

Coding style and return real error codes (though this looks like it
should never happen).

> +static int ml26124_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
> +				  int div_id, int div)
> +{
> +	struct snd_soc_codec *codec = codec_dai->codec;
> +
> +	switch (div_id) {
> +	case ML26124_MCLK:
> +		ml26124_update_bits(codec, ML26124_CLK_CTL,
> +				      BIT(0) | BIT(1), div);
> +		break;

Why can't the driver calculate this automatically given the MCLK?

> +	case SND_SOC_BIAS_ON:
> +		pr_debug("%s: level=ON priv=%p\n", __func__, priv);

There's already more than enough logging in the subsystem for this.

> +		/* VMID ON */
> +		ml26124_update_bits(codec, ML26124_PW_REF_PW_MNG,
> +				    ML26124_VMID, ML26124_VMID);
> +		msleep(500);

This looks like it should be _STANDBY - usually VMID must be on to power
everything else.

> +static int ml26124_pcm_trigger(struct snd_pcm_substream *substream,
> +			      int cmd, struct snd_soc_dai *codec_dai)
> +{
> +	struct snd_soc_codec *codec = codec_dai->codec;
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_STOP:
> +	case SNDRV_PCM_TRIGGER_SUSPEND:
> +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> +		ml26124_update_bits(codec, ML26124_REC_PLYBAK_RUN, 0x3,
> +				    0);

I'm not sure you've tested this, you can't do I2C I/O from atomic
context and trigger is atomic.  What does this control actually do?

> +		return 0;
> +		break;

Duplicate return and break here.

> +static int ml26124_resume(struct snd_soc_codec *codec)
> +{
> +	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
> +
> +	regcache_sync(priv->regmap);
> +	ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY);

Setting the bias level will duplicate the sync.

> +static int ml26124_probe(struct snd_soc_codec *codec)
> +{
> +	int ret;
> +	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
> +	codec->control_data = priv->regmap;
> +
> +	ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
> +	if (ret < 0) {
> +		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
> +		return ret;
> +	}

You're mixing regmap API usage and ASoC level I2C, this should be
_REGMAP.

> +/* power down chip */
> +static struct snd_soc_codec_driver soc_codec_dev_ml26124 = {

This comment is misplaced.

> +static struct i2c_driver ml26124_i2c_driver = {
> +	.driver = {
> +		.name = "ml26124-codec",

No -codec, the device only does one thing.

> +static int __init ml26124_modinit(void)
> +{
> +	int ret;
> +
> +	ret = i2c_add_driver(&ml26124_i2c_driver);
> +	if (ret != 0)

module_i2c_driver().

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-02-29 23:51 ` [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124 Mark Brown
@ 2012-03-02  8:16   ` Tomoya MORINAGA
  2012-03-02 12:58     ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-02  8:16 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

2012年3月1日8:51 Mark Brown <broonie@opensource.wolfsonmicro.com>:
>> +     SOC_SINGLE("Digital Switch", ML26124_DVOL_CTL, 4, 1, 0),
>
> This should probably be Digital Playback Switch so it lines up with the
> volume control in the UIs (and the Volume controls should be Digital X
> Volume).

No, not only Playback but also for both Playback and Capture.

>> +static int ml26124_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
>> +                               int div_id, int div)
>> +{
>> +     struct snd_soc_codec *codec = codec_dai->codec;
>> +
>> +     switch (div_id) {
>> +     case ML26124_MCLK:
>> +             ml26124_update_bits(codec, ML26124_CLK_CTL,
>> +                                   BIT(0) | BIT(1), div);
>> +             break;
>
> Why can't the driver calculate this automatically given the MCLK?
Sorry, I can't understand your saying.
Why does this driver need to calculate ?
I think this driver can use "div" value directly.

>> +static int ml26124_pcm_trigger(struct snd_pcm_substream *substream,
>> +                           int cmd, struct snd_soc_dai *codec_dai)
>> +{
>> +     struct snd_soc_codec *codec = codec_dai->codec;
>> +
>> +     switch (cmd) {
>> +     case SNDRV_PCM_TRIGGER_STOP:
>> +     case SNDRV_PCM_TRIGGER_SUSPEND:
>> +     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>> +             ml26124_update_bits(codec, ML26124_REC_PLYBAK_RUN, 0x3,
>> +                                 0);
>
> I'm not sure you've tested this, you can't do I2C I/O from atomic
> context and trigger is atomic.  What does this control actually do?

This control starts ether Recording or Playing or both.
Anyway, this processing must be moved somewhere.

>
>> +static int ml26124_probe(struct snd_soc_codec *codec)
>> +{
>> +     int ret;
>> +     struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
>> +     codec->control_data = priv->regmap;
>> +
>> +     ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
>> +     if (ret < 0) {
>> +             dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
>> +             return ret;
>> +     }
>
> You're mixing regmap API usage and ASoC level I2C, this should be
> _REGMAP.
>

Currently, it seems codec control via i2c works well.
Let me know why I should  use SND_SOC_REGMAP not _I2C.


>> +static int __init ml26124_modinit(void)
>> +{
>> +     int ret;
>> +
>> +     ret = i2c_add_driver(&ml26124_i2c_driver);
>> +     if (ret != 0)
>
> module_i2c_driver().

Sorry, I can't understand your saying.

thanks.
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-02  8:16   ` Tomoya MORINAGA
@ 2012-03-02 12:58     ` Mark Brown
  2012-03-06  3:03       ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-02 12:58 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 1528 bytes --]

On Fri, Mar 02, 2012 at 05:16:04PM +0900, Tomoya MORINAGA wrote:
> 2012年3月1日8:51 Mark Brown <broonie@opensource.wolfsonmicro.com>:

> >> +static int ml26124_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
> >> +                               int div_id, int div)
> >> +{
> >> +     struct snd_soc_codec *codec = codec_dai->codec;

> >> +     switch (div_id) {
> >> +     case ML26124_MCLK:
> >> +             ml26124_update_bits(codec, ML26124_CLK_CTL,
> >> +                                   BIT(0) | BIT(1), div);
> >> +             break;

> > Why can't the driver calculate this automatically given the MCLK?

> Sorry, I can't understand your saying.
> Why does this driver need to calculate ?
> I think this driver can use "div" value directly.

Why does the user have to set this divider by hand at all?  There's
already a set_sysclk() function, just have the user specify the clock
they're providing.

> >> +     ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);

> > You're mixing regmap API usage and ASoC level I2C, this should be
> > _REGMAP.

> Currently, it seems codec control via i2c works well.
> Let me know why I should  use SND_SOC_REGMAP not _I2C.

Have you checked what _I2C and _REGMAP do?

> >> +static int __init ml26124_modinit(void)
> >> +{
> >> +     int ret;
> >> +
> >> +     ret = i2c_add_driver(&ml26124_i2c_driver);
> >> +     if (ret != 0)

> > module_i2c_driver().

> Sorry, I can't understand your saying.

Use module_i2c_driver().

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5] sound/soc/lapis: add machine driver for ML7213 Carrier Board
  2012-02-23  5:46 ` [PATCH v5] sound/soc/lapis: add machine driver for ML7213 Carrier Board Tomoya MORINAGA
@ 2012-03-02 13:05   ` Mark Brown
  2012-03-06  5:49     ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-02 13:05 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 884 bytes --]

On Thu, Feb 23, 2012 at 02:46:51PM +0900, Tomoya MORINAGA wrote:

>  	  This option enables support for the AC Link Controllers in ML7213 IOH SoC.
> +config SND_SOC_ML7213_MACHINE
> +	tristate "ML7213 IOH ASoC machine driver"
> +	select SND_SOC_ML7213_PLATFORM
> +	help
> +	  This is ASoC machine driver for ML7213 IOH

This needs to depend on the CODEC as well, how have you tested?

You're also missing a blank line between this and the previous stanza.

> +	default:
> +		pr_err("%s: Failed not support format\n", __func__);
> +		return -EINVAL;
> +		break;

return followed by break isn't entirely sensible.

> +static struct platform_device *ioh_snd_device;
> +
> +
> +static int __init ioh_i2s_init(void)
> +{
> +	int ret;
> +	ioh_snd_device = platform_device_alloc("soc-audio", -1);
> +	if (!ioh_snd_device)
> +		return -ENOMEM;

Use snd_soc_register_card() and a real device.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v3] sound/soc/lapis: add platform driver for ML7213
  2012-02-23  5:46 ` [PATCH v3] sound/soc/lapis: add platform driver for ML7213 Tomoya MORINAGA
@ 2012-03-02 16:39   ` Mark Brown
  2012-03-06  5:48     ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-02 16:39 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 2669 bytes --]

On Thu, Feb 23, 2012 at 02:46:50PM +0900, Tomoya MORINAGA wrote:
> This driver is for LAPIS Semiconductor ML7213 IOH I2S.

I just merged Lars-Peter's dmaengine library code which has been on the
list for a week or so - this should be updated to use that next time
it's posted.  That should save a lot of code from the driver and make
sure it's following best practices for dmaengine use.

> +static struct ioh_i2s_data *i2s_data;
> +static struct ioh_i2s_dma dmadata[MAX_I2S_CH];

Why are these needed, aren't they dynamically allocated by the driver?

> +	case SNDRV_PCM_STREAM_CAPTURE:
> +		offset =\
> +		    ioh_rtd->dma->rx_cur_period * ioh_rtd->dma->period_bytes;

Drop the continuations, line breaks are just whitespace in C outside of
macros and strings.

> +	case SNDRV_PCM_FORMAT_S32_LE:
> +		byte = 24;
> +		break;

That looks wrong...  are you sure you don't support S24_LE or something?

> +		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
> +		case SND_SOC_DAIFMT_NB_NF:
> +			cmn_reg[i] &= ~ML7213I2S_BCLKPOL;
> +			break;
> +		default:
> +			return -EINVAL;

Looking at that code I suspect at least IB_NF is supported too...

> +static int ml7213i2s_dai_set_clkdiv(struct snd_soc_dai *dai,
> +				  int div_id, int div)
> +{

> +	switch (div_id) {
> +	case ML7213IOH_BCLKFS0:

This all looks like BCLK/sample calculations that the driver should
really be able to figure out for itself rather than forcing every user
to replicate the code to do the calculation, calculation in hw_params
would be more normal.

> +#ifdef CONFIG_PM
> +static void ioh_i2s_save_reg_conf(void)

This stuff has exactly one caller, just inline it.  It's also *very*
suspicious that it's not taking an argument specifying the device...

> +#else
> +#define ml7213i2s_soc_suspend NULL
> +#define spdif_soc_resume NULL

Hrm?

> +#ifdef CONFIG_PM
> +	.suspend = ml7213i2s_soc_suspend,
> +	.resume = ml7213i2s_soc_resume,
> +#endif

The whole point with defining the functions to NULL above is to avoid
the ifdef here.

> +static struct platform_driver ioh_i2s_driver_plat = {

> +static struct platform_driver ioh_dai_driver_plat = {

> +static int ioh_i2s_pci_probe(struct pci_dev *pdev,
> +			     const struct pci_device_id *id)

Why are you creating these platform devices?  I don't understand the
function they serve.  The code handling them looks to have quite a few
problems but I'm not clear they should be there in the first place.

> +	rv = request_irq(pdev->irq, ioh_i2s_irq, IRQF_SHARED, "ml7213_ioh",
> +			 pdev);
> +	if (rv != 0) {
> +		printk(KERN_ERR "Failed to allocate irq\n");
> +		goto out_irq;
> +	}

Are you *sure* you're ready to handle interrupts at this point?

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-02 12:58     ` Mark Brown
@ 2012-03-06  3:03       ` Tomoya MORINAGA
  2012-03-06 10:00         ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-06  3:03 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

2012年3月2日21:58 Mark Brown <broonie@opensource.wolfsonmicro.com>:
>> >> +static int ml26124_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
>> >> +                               int div_id, int div)
>> >> +{
>> >> +     struct snd_soc_codec *codec = codec_dai->codec;
>
>> >> +     switch (div_id) {
>> >> +     case ML26124_MCLK:
>> >> +             ml26124_update_bits(codec, ML26124_CLK_CTL,
>> >> +                                   BIT(0) | BIT(1), div);
>> >> +             break;
>
>> > Why can't the driver calculate this automatically given the MCLK?
>
>> Sorry, I can't understand your saying.
>> Why does this driver need to calculate ?
>> I think this driver can use "div" value directly.
>
> Why does the user have to set this divider by hand at all?  There's
> already a set_sysclk() function, just have the user specify the clock
> they're providing.
I see.
This processing will move to set_sysclk()

>> >> +     ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
>> > You're mixing regmap API usage and ASoC level I2C, this should be
>> > _REGMAP.
>> Currently, it seems codec control via i2c works well.
>> Let me know why I should  use SND_SOC_REGMAP not _I2C.
> Have you checked what _I2C and _REGMAP do?
I've just checked it.
If SND_SOC_REGMAP is used, it seems there is no processing in it.
So, can I delete snd_soc_codec_set_cache_io ?

>
>> >> +static int __init ml26124_modinit(void)
>> >> +{
>> >> +     int ret;
>> >> +
>> >> +     ret = i2c_add_driver(&ml26124_i2c_driver);
>> >> +     if (ret != 0)
>
>> > module_i2c_driver().
>
>> Sorry, I can't understand your saying.
>
> Use module_i2c_driver().
I understand.
As a matter of interest, why can I use module_i2c_driver ?
There is no ASoC driver uses module_i2c_driver.

>> +static int ml26124_update_bits(struct snd_soc_codec *codec, unsigned short reg,
>> +                             unsigned int mask, unsigned int value)
>
> Why are you open coding this in your driver?  There is no point in
> replicating subsystem functionality.
For ML26124 spec, it's necessary.
Because the codec's register read-address and write-address is not the same.
E.G.
Sampling Rate Register
R: offset=0
W: offset=1

thanks,
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v3] sound/soc/lapis: add platform driver for ML7213
  2012-03-02 16:39   ` Mark Brown
@ 2012-03-06  5:48     ` Tomoya MORINAGA
  2012-03-06 11:52       ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-06  5:48 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

2012年3月3日1:39 Mark Brown <broonie@opensource.wolfsonmicro.com>:
> On Thu, Feb 23, 2012 at 02:46:50PM +0900, Tomoya MORINAGA wrote:
>> This driver is for LAPIS Semiconductor ML7213 IOH I2S.
>
> I just merged Lars-Peter's dmaengine library code which has been on the
> list for a week or so - this should be updated to use that next time
> it's posted.  That should save a lot of code from the driver and make
> sure it's following best practices for dmaengine use.
Sorry, we can't use this library.

>> +static struct ioh_i2s_data *i2s_data;
>> +static struct ioh_i2s_dma dmadata[MAX_I2S_CH];
>
> Why are these needed, aren't they dynamically allocated by the driver?

You mean ASoC driver can't use global variable ?

>> +     case SNDRV_PCM_STREAM_CAPTURE:
>> +             offset =\
>> +                 ioh_rtd->dma->rx_cur_period * ioh_rtd->dma->period_bytes;
>
> Drop the continuations, line breaks are just whitespace in C outside of
> macros and strings.
I see.

>
>> +     case SNDRV_PCM_FORMAT_S32_LE:
>> +             byte = 24;
>> +             break;
>
> That looks wrong...  are you sure you don't support S24_LE or something?
This is correct.
Because maximum transmit size of ML7213's I2S hw is 24bit.

>> +             switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
>> +             case SND_SOC_DAIFMT_NB_NF:
>> +                     cmn_reg[i] &= ~ML7213I2S_BCLKPOL;
>> +                     break;
>> +             default:
>> +                     return -EINVAL;
>
> Looking at that code I suspect at least IB_NF is supported too...
Yes, our ML7213 I2s looks supporting IB_NF.
I'll add.


>> +static int ml7213i2s_dai_set_clkdiv(struct snd_soc_dai *dai,
>> +                               int div_id, int div)
>> +{
>
>> +     switch (div_id) {
>> +     case ML7213IOH_BCLKFS0:
>
> This all looks like BCLK/sample calculations that the driver should
> really be able to figure out for itself rather than forcing every user
> to replicate the code to do the calculation, calculation in hw_params
> would be more normal.

OK, this will move to hw_params().
and will delete snd_soc_dai_set_clkdiv() of machine driver.

>
>> +#ifdef CONFIG_PM
>> +static void ioh_i2s_save_reg_conf(void)
>
> This stuff has exactly one caller, just inline it.  It's also *very*
> suspicious that it's not taking an argument specifying the device...

I see.
I'll open these codes.

>
>> +#else
>> +#define ml7213i2s_soc_suspend NULL
>> +#define spdif_soc_resume NULL
>
> Hrm?
Ah, I'll modify it to ml7213i2s_soc_resume .

>
>> +#ifdef CONFIG_PM
>> +     .suspend = ml7213i2s_soc_suspend,
>> +     .resume = ml7213i2s_soc_resume,
>> +#endif
>
> The whole point with defining the functions to NULL above is to avoid
> the ifdef here.
I'll delete this "#ifdef".

>
>> +static struct platform_driver ioh_i2s_driver_plat = {
>
>> +static struct platform_driver ioh_dai_driver_plat = {
>
>> +static int ioh_i2s_pci_probe(struct pci_dev *pdev,
>> +                          const struct pci_device_id *id)
>
> Why are you creating these platform devices?  I don't understand the
> function they serve.  The code handling them looks to have quite a few
> problems but I'm not clear they should be there in the first place.
>
if these platform devices aren't used, device detection doesn't work correctly.
So, I added these.
I don't know other way.
Can you show other way ?

>> +     rv = request_irq(pdev->irq, ioh_i2s_irq, IRQF_SHARED, "ml7213_ioh",
>> +                      pdev);
>> +     if (rv != 0) {
>> +             printk(KERN_ERR "Failed to allocate irq\n");
>> +             goto out_irq;
>> +     }
>
> Are you *sure* you're ready to handle interrupts at this point?

This is just registering interrupt handler.
As long as interrupt register is not enabled, the interrupt handler is
not called.
This is common request_irq description.
What's is your concern ?

thanks,
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5] sound/soc/lapis: add machine driver for ML7213 Carrier Board
  2012-03-02 13:05   ` Mark Brown
@ 2012-03-06  5:49     ` Tomoya MORINAGA
  2012-03-06 11:54       ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-06  5:49 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

2012年3月2日22:05 Mark Brown <broonie@opensource.wolfsonmicro.com>:
> On Thu, Feb 23, 2012 at 02:46:51PM +0900, Tomoya MORINAGA wrote:
>
>>         This option enables support for the AC Link Controllers in ML7213 IOH SoC.
>> +config SND_SOC_ML7213_MACHINE
>> +     tristate "ML7213 IOH ASoC machine driver"
>> +     select SND_SOC_ML7213_PLATFORM
>> +     help
>> +       This is ASoC machine driver for ML7213 IOH
>
> This needs to depend on the CODEC as well, how have you tested?
OK, will add this.

When I test this, I selected "Build all ASoC ...." of Kconfig

> You're also missing a blank line between this and the previous stanza.
Sorry, I can't understand your saying.

>
>> +static struct platform_device *ioh_snd_device;
>> +
>> +
>> +static int __init ioh_i2s_init(void)
>> +{
>> +     int ret;
>> +     ioh_snd_device = platform_device_alloc("soc-audio", -1);
>> +     if (!ioh_snd_device)
>> +             return -ENOMEM;
>
> Use snd_soc_register_card() and a real device.
OK.
As a matter of interest, why do you recommend me to use
snd_soc_register_card() ?
At present, many ASoC drivers use platform_device_alloc.

thanks,
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-06  3:03       ` Tomoya MORINAGA
@ 2012-03-06 10:00         ` Mark Brown
  2012-03-06 10:49           ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-06 10:00 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 1400 bytes --]

On Tue, Mar 06, 2012 at 12:03:20PM +0900, Tomoya MORINAGA wrote:
> 2012年3月2日21:58 Mark Brown <broonie@opensource.wolfsonmicro.com>:

> >> Let me know why I should  use SND_SOC_REGMAP not _I2C.

> > Have you checked what _I2C and _REGMAP do?

> I've just checked it.
> If SND_SOC_REGMAP is used, it seems there is no processing in it.

This is not the case.

> So, can I delete snd_soc_codec_set_cache_io ?

No.

> > Use module_i2c_driver().

> I understand.

> As a matter of interest, why can I use module_i2c_driver ?

It is best practice.

> There is no ASoC driver uses module_i2c_driver.

This is not the case.  As I have *REPEATEDLY* told you you should be
sumbitting code against current development versions of the subsystem.

> >> +static int ml26124_update_bits(struct snd_soc_codec *codec, unsigned short reg,
> >> +                             unsigned int mask, unsigned int value)

> > Why are you open coding this in your driver?  There is no point in
> > replicating subsystem functionality.

> For ML26124 spec, it's necessary.
> Because the codec's register read-address and write-address is not the same.
> E.G.
> Sampling Rate Register
> R: offset=0
> W: offset=1

This is not what the above prototype says, the above prototype has only
one register in it.  You need to make this *much* clearer for the
registers it's affecting.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-06 10:00         ` Mark Brown
@ 2012-03-06 10:49           ` Tomoya MORINAGA
  2012-03-06 12:12             ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-06 10:49 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Tue, Mar 06, 2012 19:00 Mark Brown <broonie@opensource.wolfsonmicro.com>:
> This is not the case.  As I have *REPEATEDLY* told you you should be
> sumbitting code against current development versions of the subsystem.
You are right. v3.3-rc6 uses it. sorry for inconvenience.

>> >> +static int ml26124_update_bits(struct snd_soc_codec *codec, unsigned short reg,
>> >> +                             unsigned int mask, unsigned int value)
>> > Why are you open coding this in your driver?  There is no point in
>> > replicating subsystem functionality.
>>
>> For ML26124 spec, it's necessary.
>> Because the codec's register read-address and write-address is not the same.
>> E.G.
>> Sampling Rate Register
>> R: offset=0
>> W: offset=1
>
> This is not what the above prototype says, the above prototype has only
> one register in it.  You need to make this *much* clearer for the
> registers it's affecting.
Sorry, I couldn't understand your saying / concern.
Do you mean I shouldn't use ml26124_update_bits() but snd_soc_update_bits() ?

thanks,
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v3] sound/soc/lapis: add platform driver for ML7213
  2012-03-06  5:48     ` Tomoya MORINAGA
@ 2012-03-06 11:52       ` Mark Brown
  2012-03-07  1:30         ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-06 11:52 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 2655 bytes --]

On Tue, Mar 06, 2012 at 02:48:05PM +0900, Tomoya MORINAGA wrote:
> 2012年3月3日1:39 Mark Brown <broonie@opensource.wolfsonmicro.com>:

> > I just merged Lars-Peter's dmaengine library code which has been on the
> > list for a week or so - this should be updated to use that next time
> > it's posted.  That should save a lot of code from the driver and make
> > sure it's following best practices for dmaengine use.

> Sorry, we can't use this library.

This sort of response really isn't on.  Do you have some reason for
saying this?  Flat out refusing to do things with no reason is not
useful.

> >> +static struct ioh_i2s_data *i2s_data;
> >> +static struct ioh_i2s_dma dmadata[MAX_I2S_CH];

> > Why are these needed, aren't they dynamically allocated by the driver?

> You mean ASoC driver can't use global variable ?

In general no Linux driver should be using a global variable except for
things like this.

> >> +     case SNDRV_PCM_FORMAT_S32_LE:
> >> +             byte = 24;
> >> +             break;

> > That looks wrong...  are you sure you don't support S24_LE or something?

> This is correct.

> Because maximum transmit size of ML7213's I2S hw is 24bit.

Note we often lay out 24 bit audio in 32 bit blocks.

> >> +static struct platform_driver ioh_i2s_driver_plat = {
> >
> >> +static struct platform_driver ioh_dai_driver_plat = {
> >
> >> +static int ioh_i2s_pci_probe(struct pci_dev *pdev,
> >> +                          const struct pci_device_id *id)

> > Why are you creating these platform devices?  I don't understand the
> > function they serve.  The code handling them looks to have quite a few
> > problems but I'm not clear they should be there in the first place.

> if these platform devices aren't used, device detection doesn't work correctly.
> So, I added these.

You've not actually mentioned the problem you were seeing...

> >> +     rv = request_irq(pdev->irq, ioh_i2s_irq, IRQF_SHARED, "ml7213_ioh",
> >> +                      pdev);
> >> +     if (rv != 0) {
> >> +             printk(KERN_ERR "Failed to allocate irq\n");
> >> +             goto out_irq;
> >> +     }

> > Are you *sure* you're ready to handle interrupts at this point?

> This is just registering interrupt handler.
> As long as interrupt register is not enabled, the interrupt handler is
> not called.
> This is common request_irq description.
> What's is your concern ?

Apart from anything else you've got the interrupt requested as
IRQF_SHARED so the interrupt could get called at any time.  It's also
not clear that you've got the hardware in a known good state.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5] sound/soc/lapis: add machine driver for ML7213 Carrier Board
  2012-03-06  5:49     ` Tomoya MORINAGA
@ 2012-03-06 11:54       ` Mark Brown
  2012-03-07  1:57         ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-06 11:54 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 516 bytes --]

On Tue, Mar 06, 2012 at 02:49:24PM +0900, Tomoya MORINAGA wrote:
> 2012年3月2日22:05 Mark Brown <broonie@opensource.wolfsonmicro.com>:

> > You're also missing a blank line between this and the previous stanza.

> Sorry, I can't understand your saying.

Your file should look like other similar files in the kernel.

> > Use snd_soc_register_card() and a real device.
> OK.
> As a matter of interest, why do you recommend me to use
> snd_soc_register_card() ?

It's been the standard since 2.6.38.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-06 10:49           ` Tomoya MORINAGA
@ 2012-03-06 12:12             ` Mark Brown
  2012-03-07  2:16               ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-06 12:12 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 658 bytes --]

On Tue, Mar 06, 2012 at 07:49:16PM +0900, Tomoya MORINAGA wrote:
> On Tue, Mar 06, 2012 19:00 Mark Brown <broonie@opensource.wolfsonmicro.com>:

> > This is not what the above prototype says, the above prototype has only
> > one register in it.  You need to make this *much* clearer for the
> > registers it's affecting.

> Sorry, I couldn't understand your saying / concern.
> Do you mean I shouldn't use ml26124_update_bits() but snd_soc_update_bits() ?

Ideally you should be using the framework features.  If there is some
reason why you can't you need to make it much clearer what the reason
is so people reading the code can understand.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v3] sound/soc/lapis: add platform driver for ML7213
  2012-03-06 11:52       ` Mark Brown
@ 2012-03-07  1:30         ` Tomoya MORINAGA
  2012-03-07 11:46           ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-07  1:30 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Tue, Mar 6, 2012 at 8:52 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
>> > I just merged Lars-Peter's dmaengine library code which has been on the
>> > list for a week or so - this should be updated to use that next time
>> > it's posted.  That should save a lot of code from the driver and make
>> > sure it's following best practices for dmaengine use.
>> Sorry, we can't use this library.
> This sort of response really isn't on.  Do you have some reason for
> saying this?  Flat out refusing to do things with no reason is not
> useful.

Current Intel's dma driver (pch_dma) doesn't work on this library.
Because the library uses function which pch_dma doesn't support.
e.g. device_prep_dma_cyclic
So, we can't use this library.

>> >> +static struct ioh_i2s_data *i2s_data;
>> >> +static struct ioh_i2s_dma dmadata[MAX_I2S_CH];
>> > Why are these needed, aren't they dynamically allocated by the driver?
>> You mean ASoC driver can't use global variable ?
> In general no Linux driver should be using a global variable except for
> things like this.

I see. I'll use dynamic allocation.

>> >> +     case SNDRV_PCM_FORMAT_S32_LE:
>> >> +             byte = 24;
>> >> +             break;
>> > That looks wrong...  are you sure you don't support S24_LE or something?
>> This is correct.
>> Because maximum transmit size of ML7213's I2S hw is 24bit.
> Note we often lay out 24 bit audio in 32 bit blocks.

24bit data doesn't work on our system.
So, we don't support S24_LE.

>> >> +static struct platform_driver ioh_i2s_driver_plat = {
>> >
>> >> +static struct platform_driver ioh_dai_driver_plat = {
>> >
>> >> +static int ioh_i2s_pci_probe(struct pci_dev *pdev,
>> >> +                          const struct pci_device_id *id)
>> > Why are you creating these platform devices?  I don't understand the
>> > function they serve.  The code handling them looks to have quite a few
>> > problems but I'm not clear they should be there in the first place.
>> if these platform devices aren't used, device detection doesn't work correctly.
>> So, I added these.
> You've not actually mentioned the problem you were seeing...

I saw mapping problem between machine driver and platform driver.
e.g.  cpu_dai_name "ml7213ioh"

>> >> +     rv = request_irq(pdev->irq, ioh_i2s_irq, IRQF_SHARED, "ml7213_ioh",
>> >> +                      pdev);
>> >> +     if (rv != 0) {
>> >> +             printk(KERN_ERR "Failed to allocate irq\n");
>> >> +             goto out_irq;
>> >> +     }
>> > Are you *sure* you're ready to handle interrupts at this point?
>> This is just registering interrupt handler.
>> As long as interrupt register is not enabled, the interrupt handler is
>> not called.
>> This is common request_irq description.
>> What's is your concern ?
> Apart from anything else you've got the interrupt requested as
> IRQF_SHARED so the interrupt could get called at any time.  It's also
> not clear that you've got the hardware in a known good state.

Do you mean request_irq should move to somewhere, like open() or
hw_params() or ... ?

thanks.

-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5] sound/soc/lapis: add machine driver for ML7213 Carrier Board
  2012-03-06 11:54       ` Mark Brown
@ 2012-03-07  1:57         ` Tomoya MORINAGA
  2012-03-09  6:38           ` [PATCH v6] " Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-07  1:57 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Tue, Mar 6, 2012 at 8:54 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
>> > You're also missing a blank line between this and the previous stanza.
>> Sorry, I can't understand your saying.
> Your file should look like other similar files in the kernel.

Aha, I could understand.
Need to add blank line before "config SND_SOC_ML7213_MACHINE" line.

thanks.
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-06 12:12             ` Mark Brown
@ 2012-03-07  2:16               ` Tomoya MORINAGA
  2012-03-07 11:48                 ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-07  2:16 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Tue, Mar 6, 2012 at 9:12 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
>> > This is not what the above prototype says, the above prototype has only
>> > one register in it.  You need to make this *much* clearer for the
>> > registers it's affecting.
>> Sorry, I couldn't understand your saying / concern.
>> Do you mean I shouldn't use ml26124_update_bits() but snd_soc_update_bits() ?
> Ideally you should be using the framework features.  If there is some
> reason why you can't you need to make it much clearer what the reason
> is so people reading the code can understand.

If possible, I want to use framework features. However, I judged it is
not possible.
Let me confirm again.
As I said before, all ML26124 registers need to be accessed by
different address when read and write.
Like below
R       W
$00h $01h Sampling setting
$02h $03h PLL setting
...

Even so, can I use framework features ?

thanks,
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v3] sound/soc/lapis: add platform driver for ML7213
  2012-03-07  1:30         ` Tomoya MORINAGA
@ 2012-03-07 11:46           ` Mark Brown
  2012-03-08  2:06             ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-07 11:46 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 1155 bytes --]

On Wed, Mar 07, 2012 at 10:30:05AM +0900, Tomoya MORINAGA wrote:
> On Tue, Mar 6, 2012 at 8:52 PM, Mark Brown

> >> if these platform devices aren't used, device detection doesn't work correctly.
> >> So, I added these.

> > You've not actually mentioned the problem you were seeing...

> I saw mapping problem between machine driver and platform driver.
> e.g.  cpu_dai_name "ml7213ioh"

You've still not explained the problem you're seeing but this sounds
like something that should work.  Please address the

> > Apart from anything else you've got the interrupt requested as
> > IRQF_SHARED so the interrupt could get called at any time.  It's also
> > not clear that you've got the hardware in a known good state.

> Do you mean request_irq should move to somewhere, like open() or
> hw_params() or ... ?

You need to do enough hardware and software initialisation prior to
requesting the interrupt to ensure that if an interrupt does happen it
won't cause any ill effects.  Checks in the interrupt handler to make
sure things like the ALSA layer stuff have been set up can also be used
to make the interrupt handler safer.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-07  2:16               ` Tomoya MORINAGA
@ 2012-03-07 11:48                 ` Mark Brown
  2012-03-08  2:24                   ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-07 11:48 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 598 bytes --]

On Wed, Mar 07, 2012 at 11:16:50AM +0900, Tomoya MORINAGA wrote:

> If possible, I want to use framework features. However, I judged it is
> not possible.
> Let me confirm again.
> As I said before, all ML26124 registers need to be accessed by
> different address when read and write.
> Like below
> R       W
> $00h $01h Sampling setting
> $02h $03h PLL setting
> ...

> Even so, can I use framework features ?

No, in which case (to repeat what I said previously) you need to make
the code *much* clearer so the reader can understand what it is doing
and why it's having to open code this stuff.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v3] sound/soc/lapis: add platform driver for ML7213
  2012-03-07 11:46           ` Mark Brown
@ 2012-03-08  2:06             ` Tomoya MORINAGA
  2012-03-08 11:05               ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-08  2:06 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Wed, Mar 7, 2012 at 8:46 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
>> >> if these platform devices aren't used, device detection doesn't work correctly.
>> >> So, I added these.
>> > You've not actually mentioned the problem you were seeing...
>> I saw mapping problem between machine driver and platform driver.
>> e.g.  cpu_dai_name "ml7213ioh"
> You've still not explained the problem you're seeing but this sounds
> like something that should work.  Please address the

* Faced problem if there are no platform settings.
machine driver's hw_params is not called
platform_driver's functions(snd_pcm_ops, snd_soc_platform_driver) are
not called.

As a result, the following message is not showed . (by soc_pcm.c)
printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
         cpu_dai->name);

>> > Apart from anything else you've got the interrupt requested as
>> > IRQF_SHARED so the interrupt could get called at any time.  It's also
>> > not clear that you've got the hardware in a known good state.
>> Do you mean request_irq should move to somewhere, like open() or
>> hw_params() or ... ?
> You need to do enough hardware and software initialisation prior to
> requesting the interrupt to ensure that if an interrupt does happen it
> won't cause any ill effects.  Checks in the interrupt handler to make
> sure things like the ALSA layer stuff have been set up can also be used
> to make the interrupt handler safer.

I can understand your concern.
Let me clarify your think.
You think that interrupt handler must check whether ALSA initialization has
already finished or not before interrupt process executes.
Right?

thanks.
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-07 11:48                 ` Mark Brown
@ 2012-03-08  2:24                   ` Tomoya MORINAGA
  2012-03-08 11:47                     ` Mark Brown
  2012-03-14 17:40                     ` [alsa-devel] [PATCH v6] " Lars-Peter Clausen
  0 siblings, 2 replies; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-08  2:24 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Wed, Mar 7, 2012 at 8:48 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
>> If possible, I want to use framework features. However, I judged it is
>> not possible.
>> Let me confirm again.
>> As I said before, all ML26124 registers need to be accessed by
>> different address when read and write.
>> Like below
>> R       W
>> $00h $01h Sampling setting
>> $02h $03h PLL setting
>> ...
>> Even so, can I use framework features ?
> No, in which case (to repeat what I said previously) you need to make
> the code *much* clearer so the reader can understand what it is doing
> and why it's having to open code this stuff.

I understand you agree to use ml26124_update_bits(), right ?
If so, do you mean that I should just add explanation (like as I
explained before)
why the function() is necessary to above prototype of ml26124_update_bits() ?

thanks.
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v3] sound/soc/lapis: add platform driver for ML7213
  2012-03-08  2:06             ` Tomoya MORINAGA
@ 2012-03-08 11:05               ` Mark Brown
  2012-03-09  6:26                 ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-08 11:05 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 1152 bytes --]

On Thu, Mar 08, 2012 at 11:06:47AM +0900, Tomoya MORINAGA wrote:
> On Wed, Mar 7, 2012 at 8:46 PM, Mark Brown

> > You've still not explained the problem you're seeing but this sounds
> > like something that should work.  Please address the

> * Faced problem if there are no platform settings.
> machine driver's hw_params is not called
> platform_driver's functions(snd_pcm_ops, snd_soc_platform_driver) are
> not called.

> As a result, the following message is not showed . (by soc_pcm.c)
> printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
>          cpu_dai->name);

This makes no sense.  Registering a platform device will have *no*
impact on ASoC, it only cares if the relevant ASoC function drivers have
come up.  Probably there was some other problem with what you were doing
but it's hard to say...

> I can understand your concern.
> Let me clarify your think.
> You think that interrupt handler must check whether ALSA initialization has
> already finished or not before interrupt process executes.
> Right?

Or it should be safe to run even in the card has not come up yet, either
way is fine.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-08  2:24                   ` Tomoya MORINAGA
@ 2012-03-08 11:47                     ` Mark Brown
  2012-03-09  6:37                       ` [PATCH v7] " Tomoya MORINAGA
  2012-03-14 17:40                     ` [alsa-devel] [PATCH v6] " Lars-Peter Clausen
  1 sibling, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-08 11:47 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 334 bytes --]

On Thu, Mar 08, 2012 at 11:24:36AM +0900, Tomoya MORINAGA wrote:

> If so, do you mean that I should just add explanation (like as I
> explained before)
> why the function() is necessary to above prototype of ml26124_update_bits() ?

Probably also make the interface clearer about the fact that it operates
on multiple registers too.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v3] sound/soc/lapis: add platform driver for ML7213
  2012-03-08 11:05               ` Mark Brown
@ 2012-03-09  6:26                 ` Tomoya MORINAGA
  2012-03-09  6:39                   ` [PATCH v4] " Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-09  6:26 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Thu, Mar 8, 2012 at 8:05 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
>> > You've still not explained the problem you're seeing but this sounds
>> > like something that should work.  Please address the
>> * Faced problem if there are no platform settings.
>> machine driver's hw_params is not called
>> platform_driver's functions(snd_pcm_ops, snd_soc_platform_driver) are
>> not called.
>> As a result, the following message is not showed . (by soc_pcm.c)
>> printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
>>          cpu_dai->name);
> This makes no sense.  Registering a platform device will have *no*
> impact on ASoC, it only cares if the relevant ASoC function drivers have
> come up.  Probably there was some other problem with what you were doing
> but it's hard to say...

I've confirmed that driver detected by ASoC correctly without these
platform functions.
Please check next patch series.

thanks.
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH v7] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-08 11:47                     ` Mark Brown
@ 2012-03-09  6:37                       ` Tomoya MORINAGA
  2012-03-14 14:39                         ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-09  6:37 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	alsa-devel, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, 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 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@gmail.com>
---
V7
 - Add explanation of "ml26124_update_bits()"
 - Use module_i2c_driver()
 - ml26124_set_dai_clkdiv() moved to ml26124_set_dai_sysclk()
 - VMID_ON moved to SND_SOC_BIAS_STANDBY
 - Deleted redundant regcache_sync
 - Modified SND_SOC_I2C to SND_SOC_REGMAP of snd_soc_codec_set_cache_io()
 - Deleted "-codec" post-fix
---
 sound/soc/codecs/Kconfig   |    4 +
 sound/soc/codecs/Makefile  |    2 +
 sound/soc/codecs/ml26124.c |  707 ++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/ml26124.h |  132 ++++++++
 4 files changed, 845 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 6508e8b..e314a66 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -42,6 +42,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_MAX9850 if I2C
 	select SND_SOC_MAX9768 if I2C
 	select SND_SOC_MAX9877 if I2C
+	select SND_SOC_ML26124 if I2C
 	select SND_SOC_PCM3008
 	select SND_SOC_RT5631 if I2C
 	select SND_SOC_SGTL5000 if I2C
@@ -436,5 +437,8 @@ config SND_SOC_MAX9768
 config SND_SOC_MAX9877
 	tristate
 
+config SND_SOC_ML26124
+	tristate
+
 config SND_SOC_TPA6130A2
 	tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 6662eb0..9108ee9 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -29,6 +29,7 @@ snd-soc-max9768-objs := max9768.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-rt5631-objs := rt5631.o
 snd-soc-sgtl5000-objs := sgtl5000.o
@@ -135,6 +136,7 @@ obj-$(CONFIG_SND_SOC_MAX9768)	+= snd-soc-max9768.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_RT5631)	+= snd-soc-rt5631.o
 obj-$(CONFIG_SND_SOC_SGTL5000)  += snd-soc-sgtl5000.o
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
new file mode 100644
index 0000000..db016c1
--- /dev/null
+++ b/sound/soc/codecs/ml26124.c
@@ -0,0 +1,707 @@
+/*
+ * 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 <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "ml26124.h"
+
+#define DVOL_CTL_DVMUTE_ON		BIT(4)	/* Digital volume MUTE On */
+#define DVOL_CTL_DVMUTE_OFF		0	/* Digital volume MUTE Off */
+#define ML26124_SAI_NO_DELAY	BIT(1)
+#define ML26124_SAI_FRAME_SYNC	(BIT(5) | BIT(0)) /* For mono (Telecodec) */
+#define ML26134_CACHESIZE 79
+#define ML26124_VMID	BIT(0)
+#define ML26124_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |\
+		       SNDRV_PCM_RATE_48000)
+#define ML26124_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+#define ML26124_NUM_REGISTER ML26134_CACHESIZE
+
+struct ml26124_priv {
+	u32 mclk;
+	u32 channels;
+	struct regmap *regmap;
+};
+
+struct clk_coeff {
+	u32 mclk;
+	u32 rate;
+	u8 pllnl;
+	u8 pllnh;
+	u8 pllml;
+	u8 pllmh;
+	u8 plldiv;
+};
+
+/* ML26124 configuration */
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7150, 50, 0);
+
+static const DECLARE_TLV_DB_SCALE(alclvl, -2250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(mingain, -1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(maxgain, -675, 600, 0);
+static const DECLARE_TLV_DB_SCALE(boost_vol, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(ngth, -7650, 150, 0);
+
+static const char * const ml26124_companding[] = {"16bit PCM", "u-law",
+						  "A-law"};
+
+static const struct soc_enum ml26124_adc_companding_enum
+	= SOC_ENUM_SINGLE(ML26124_SAI_TRANS_CTL, 6, 3, ml26124_companding);
+
+static const struct soc_enum ml26124_dac_companding_enum
+	= SOC_ENUM_SINGLE(ML26124_SAI_RCV_CTL, 6, 3, ml26124_companding);
+
+static const struct snd_kcontrol_new ml26124_snd_controls[] = {
+	SOC_SINGLE_TLV("Capture Digital Volume", ML26124_RECORD_DIG_VOL, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("Playback Digital Volume", ML26124_PLBAK_DIG_VOL, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("Digital Boost Volume", ML26124_DIGI_BOOST_VOL, 0,
+			0x3f, 0, boost_vol),
+	SOC_SINGLE_TLV("EQ Band0 Volume", ML26124_EQ_GAIN_BRAND0, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("EQ Band1 Volume", ML26124_EQ_GAIN_BRAND1, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("EQ Band2 Volume", ML26124_EQ_GAIN_BRAND2, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("EQ Band3 Volume", ML26124_EQ_GAIN_BRAND3, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("EQ Band4 Volume", ML26124_EQ_GAIN_BRAND4, 0,
+			0xff, 1, digital_tlv),
+	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, mingain),
+	SOC_SINGLE_TLV("ALC Max Input Volume", ML26124_ALC_MAXMIN_GAIN, 4,
+			7, 1, maxgain),
+	SOC_SINGLE_TLV("Playback Limiter Min Input Volume",
+			ML26124_PL_MAXMIN_GAIN, 0, 7, 0, mingain),
+	SOC_SINGLE_TLV("Playback Limiter Max Input Volume",
+			ML26124_PL_MAXMIN_GAIN, 4, 7, 1, maxgain),
+	SOC_SINGLE_TLV("Playback Boost Volume", ML26124_PLYBAK_BOST_VOL, 0,
+			0x3f, 0, boost_vol),
+	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("ZC Switch", ML26124_PW_ZCCMP_PW_MNG, 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 Limiter", ML26124_DVOL_CTL, 0, 1, 0),
+	SOC_SINGLE("Capture Limiter", ML26124_DVOL_CTL, 1, 1, 0),
+	SOC_SINGLE("Digital Volume Fade Switch", ML26124_DVOL_CTL, 3, 1, 0),
+	SOC_SINGLE("Digital Switch", ML26124_DVOL_CTL, 4, 1, 0),
+	SOC_ENUM("DAC Companding", ml26124_dac_companding_enum),
+	SOC_ENUM("ADC Companding", ml26124_adc_companding_enum),
+};
+
+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 * const ml26124_input_select[] = {"Analog MIC SingleEnded in",
+						"Analog MIC Differential in",
+						"Digital MIC in"};
+
+static const struct soc_enum ml26124_insel_enum =
+	SOC_ENUM_SINGLE(ML26124_MIC_IF_CTL, 0, 2, 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_SUPPLY("RUN-REC", ML26124_REC_PLYBAK_RUN, 0, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("RUN-PLAY", ML26124_REC_PLYBAK_RUN, 1, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("MICBIAS", ML26124_PW_REF_PW_MNG, 2, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("Output Mixer", ML26124_PW_SPAMP_PW_MNG, 0, 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("MDIN"),
+	SND_SOC_DAPM_INPUT("MIN"),
+	SND_SOC_DAPM_INPUT("LIN"),
+	SND_SOC_DAPM_OUTPUT("SPOUT"),
+	SND_SOC_DAPM_OUTPUT("LOUT"),
+};
+
+static const struct snd_soc_dapm_route ml26124_intercon[] = {
+	/* Supply */
+	{"DAC", NULL, "MCLK"},
+	{"ADC", NULL, "MCLK"},
+	{"DAC", NULL, "PLL"},
+	{"ADC", NULL, "PLL"},
+	{"DAC", NULL, "RUN-PLAY"},
+	{"ADC", NULL, "RUN-REC"},
+
+	/* output mixer */
+	{"Output Mixer", "PGA Switch", "PGA"},
+	{"Output Mixer", "DAC Switch", "DAC"},
+	{"Output Mixer", "Line in Switch", "LIN"},
+
+	/* outputs */
+	{"LOUT", NULL, "Output Mixer"},
+	{"SPOUT", NULL, "Output Mixer"},
+
+	/* input */
+	{"Input Mux", "Analog MIC SingleEnded in", "MIN"},
+	{"Input Mux", "Analog MIC Differential in", "MIN"},
+	{"ADC", NULL, "Input Mux"},
+};
+
+/* PLLOutputFreq(Hz) = InputMclkFreq(Hz) * PLLM / (PLLN * PLLDIV) */
+static const struct clk_coeff coeff_div[] = {
+	{12288000, 16000, 0xc, 0x0, 0x20, 0x0, 0x4},
+	{12288000, 32000, 0xc, 0x0, 0x20, 0x0, 0x4},
+	{12288000, 48000, 0xc, 0x0, 0x30, 0x0, 0x4},
+};
+
+static struct reg_default ml26124_reg[] = {
+	/* CLOCK control Register */
+	{0x00, 0x00 },	/* Sampling Rate */
+	{0x02, 0x00},	/* PLL NL */
+	{0x04, 0x00},	/* PLLNH */
+	{0x06, 0x00},	/* PLLML */
+	{0x08, 0x00},	/* MLLMH */
+	{0x0a, 0x00},	/* PLLDIV */
+	{0x0c, 0x00},	/* Clock Enable */
+	{0x0e, 0x00},	/* CLK Input/Output Control */
+
+	/* System Control Register */
+	{0x10, 0x00},	/* Software RESET */
+	{0x12, 0x00},	/* Record/Playback Run */
+	{0x14, 0x00},	/* Mic Input/Output control */
+
+	/* Power Management Register */
+	{0x20, 0x00},	/* Reference Power Management */
+	{0x22, 0x00},	/* Input Power Management */
+	{0x24, 0x00},	/* DAC Power Management */
+	{0x26, 0x00},	/* SP-AMP Power Management */
+	{0x28, 0x00},	/* LINEOUT Power Management */
+	{0x2a, 0x00},	/* VIDEO Power Management */
+	{0x2e, 0x00},	/* AC-CMP Power Management */
+
+	/* Analog reference Control Register */
+	{0x30, 0x04},	/* MICBIAS Voltage Control */
+
+	/* Input/Output Amplifier Control Register */
+	{0x32, 0x10},	/* MIC Input Volume */
+	{0x38, 0x00},	/* Mic Boost Volume */
+	{0x3a, 0x33},	/* Speaker AMP Volume */
+	{0x48, 0x00},	/* AMP Volume Control Function Enable */
+	{0x4a, 0x00},	/* Amplifier Volume Fader Control */
+
+	/* Analog Path Control Register */
+	{0x54, 0x00},	/* Speaker AMP Output Control */
+	{0x5a, 0x00},	/* Mic IF Control */
+	{0xe8, 0x01},	/* Mic Select Control */
+
+	/* Audio Interface Control Register */
+	{0x60, 0x00},	/* SAI-Trans Control */
+	{0x62, 0x00},	/* SAI-Receive Control */
+	{0x64, 0x00},	/* SAI Mode select */
+
+	/* DSP Control Register */
+	{0x66, 0x01},	/* Filter Func Enable */
+	{0x68, 0x00},	/* Volume Control Func Enable */
+	{0x6A, 0x00},	/* Mixer & Volume Control*/
+	{0x6C, 0xff},	/* Record Digital Volume */
+	{0x70, 0xff},	/* Playback Digital Volume */
+	{0x72, 0x10},	/* Digital Boost Volume */
+	{0x74, 0xe7},	/* EQ gain Band0 */
+	{0x76, 0xe7},	/* EQ gain Band1 */
+	{0x78, 0xe7},	/* EQ gain Band2 */
+	{0x7A, 0xe7},	/* EQ gain Band3 */
+	{0x7C, 0xe7},	/* EQ gain Band4 */
+	{0x7E, 0x00},	/* HPF2 CutOff*/
+	{0x80, 0x00},	/* EQ Band0 Coef0L */
+	{0x82, 0x00},	/* EQ Band0 Coef0H */
+	{0x84, 0x00},	/* EQ Band0 Coef0L */
+	{0x86, 0x00},	/* EQ Band0 Coef0H */
+	{0x88, 0x00},	/* EQ Band1 Coef0L */
+	{0x8A, 0x00},	/* EQ Band1 Coef0H */
+	{0x8C, 0x00},	/* EQ Band1 Coef0L */
+	{0x8E, 0x00},	/* EQ Band1 Coef0H */
+	{0x90, 0x00},	/* EQ Band2 Coef0L */
+	{0x92, 0x00},	/* EQ Band2 Coef0H */
+	{0x94, 0x00},	/* EQ Band2 Coef0L */
+	{0x96, 0x00},	/* EQ Band2 Coef0H */
+	{0x98, 0x00},	/* EQ Band3 Coef0L */
+	{0x9A, 0x00},	/* EQ Band3 Coef0H */
+	{0x9C, 0x00},	/* EQ Band3 Coef0L */
+	{0x9E, 0x00},	/* EQ Band3 Coef0H */
+	{0xA0, 0x00},	/* EQ Band4 Coef0L */
+	{0xA2, 0x00},	/* EQ Band4 Coef0H */
+	{0xA4, 0x00},	/* EQ Band4 Coef0L */
+	{0xA6, 0x00},	/* EQ Band4 Coef0H */
+
+	/* ALC Control Register */
+	{0xb0, 0x00},	/* ALC Mode */
+	{0xb2, 0x02},	/* ALC Attack Time */
+	{0xb4, 0x03},	/* ALC Decay Time */
+	{0xb6, 0x00},	/* ALC Hold Time */
+	{0xb8, 0x0b},	/* ALC Target Level */
+	{0xba, 0x70},	/* ALC Max/Min Gain */
+	{0xbc, 0x00},	/* Noise Gate Threshold */
+	{0xbe, 0x00},	/* ALC ZeroCross TimeOut */
+
+	/* Playback Limiter Control Register */
+	{0xc0, 0x04},	/* PL Attack Time */
+	{0xc2, 0x05},	/* PL Decay Time */
+	{0xc4, 0x0d},	/* PL Target Level */
+	{0xc6, 0x70},	/* PL Max/Min Gain */
+	{0xc8, 0x10},	/* Playback Boost Volume */
+	{0xca, 0x00},	/* PL ZeroCross TimeOut */
+
+	/* Video Amplifier Control Register */
+	{0xd0, 0x01},	/* VIDEO AMP Gain Control */
+	{0xd2, 0x01},	/* VIDEO AMP Setup 1 */
+	{0xd4, 0x01},	/* VIDEO AMP Control2 */
+};
+
+/**
+ * ml26124_update_bits - Update ML26124 register bit(s)
+ * @codec: audio codec
+ * @reg: codec register address (Must be specified read address,
+ *				 namely even number)
+ * @mask: register mask
+ * @value: new value
+ *
+ * Writes new register value.
+ * All ML26124's registers have a different address in writing and reading.
+ * So, standard snd_soc_update_bits() can't be used.
+ *
+ * Returns 1 for change, 0 for no change, or negative error code.
+ *
+ */
+static int ml26124_update_bits(struct snd_soc_codec *codec, unsigned short reg,
+				unsigned int mask, unsigned int value)
+{
+	int change;
+	unsigned int old, new;
+	int ret;
+
+	ret = snd_soc_read(codec, reg);
+	if (ret < 0)
+		return ret;
+
+	old = ret;
+	new = (old & ~mask) | (value & mask);
+	change = old != new;
+	if (change) {
+		ret = snd_soc_write(codec, reg + 1, new);
+		if (ret < 0)
+			return ret;
+	}
+
+	return change;
+}
+
+/* Get sampling rate value of sampling rate setting register (0x0) */
+static inline int get_srate(int rate)
+{
+	int srate;
+
+	switch (rate) {
+	case 16000:
+		srate = 3;
+		break;
+	case 32000:
+		srate = 6;
+		break;
+	case 48000:
+		srate = 8;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return srate;
+}
+
+static inline int get_coeff(int mclk, int rate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+		if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+			return i;
+	}
+	return -EINVAL;
+}
+#define ML26124_USE_MCLK_IN	BIT(2)
+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 i = get_coeff(priv->mclk, params_rate(hw_params));
+
+	priv->channels = params_channels(hw_params);
+
+	ml26124_update_bits(codec, ML26124_CLK_CTL,
+			    BIT(2), ML26124_USE_MCLK_IN);
+
+	switch (params_rate(hw_params)) {
+	case 16000:
+		ml26124_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
+				    get_srate(coeff_div[i].rate));
+		ml26124_update_bits(codec, ML26124_PLLNL, 0xff,
+				    coeff_div[i].pllnl);
+		ml26124_update_bits(codec, ML26124_PLLNH, 0x1,
+				    coeff_div[i].pllnh);
+		ml26124_update_bits(codec, ML26124_PLLML, 0xff,
+				    coeff_div[i].pllml);
+		ml26124_update_bits(codec, ML26124_PLLMH, 0x3f,
+				    coeff_div[i].pllmh);
+		ml26124_update_bits(codec, ML26124_PLLDIV, 0x1f,
+				    coeff_div[i].plldiv);
+		break;
+	case 32000:
+		ml26124_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
+				    get_srate(coeff_div[i].rate));
+		ml26124_update_bits(codec, ML26124_PLLNL, 0xff,
+				    coeff_div[i].pllnl);
+		ml26124_update_bits(codec, ML26124_PLLNH, 0x1,
+				    coeff_div[i].pllnh);
+		ml26124_update_bits(codec, ML26124_PLLML, 0xff,
+				    coeff_div[i].pllml);
+		ml26124_update_bits(codec, ML26124_PLLMH, 0x3f,
+				    coeff_div[i].pllmh);
+		ml26124_update_bits(codec, ML26124_PLLDIV, 0x1f,
+				    coeff_div[i].plldiv);
+		break;
+	case 48000:
+		ml26124_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
+				    get_srate(coeff_div[i].rate));
+		ml26124_update_bits(codec, ML26124_PLLNL, 0xff,
+				    coeff_div[i].pllnl);
+		ml26124_update_bits(codec, ML26124_PLLNH, 0x1,
+				    coeff_div[i].pllnh);
+		ml26124_update_bits(codec, ML26124_PLLML, 0xff,
+				    coeff_div[i].pllml);
+		ml26124_update_bits(codec, ML26124_PLLMH, 0x3f,
+				    coeff_div[i].pllmh);
+		ml26124_update_bits(codec, ML26124_PLLDIV, 0x1f,
+				    coeff_div[i].plldiv);
+		break;
+	default:
+		pr_err("%s:this rate is no support for ml26124\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ml26124_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	if (mute)
+		ml26124_update_bits(codec, ML26124_DVOL_CTL, BIT(4),
+				    DVOL_CTL_DVMUTE_ON);
+	else
+		ml26124_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)
+{
+	unsigned char mode;
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+	unsigned char mask = ML26124_SAI_NO_DELAY | ML26124_SAI_FRAME_SYNC;
+
+	/* 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;
+	}
+	ml26124_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;
+	}
+
+	/* SAI configuration */
+	if (priv->channels == 1) {
+		ml26124_update_bits(codec, ML26124_SAI_TRANS_CTL, mask,
+				ML26124_SAI_NO_DELAY | ML26124_SAI_FRAME_SYNC);
+		ml26124_update_bits(codec, ML26124_SAI_RCV_CTL, mask,
+				ML26124_SAI_NO_DELAY | ML26124_SAI_FRAME_SYNC);
+	} else {
+		ml26124_update_bits(codec, ML26124_SAI_TRANS_CTL, mask,
+				    ML26124_SAI_NO_DELAY);
+		ml26124_update_bits(codec, ML26124_SAI_RCV_CTL, mask,
+				    ML26124_SAI_NO_DELAY);
+	}
+
+
+	return 0;
+}
+
+static int ml26124_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	switch (clk_id) {
+	case ML26124_USE_PLL:
+		ml26124_update_bits(codec, ML26124_CLK_CTL,
+				    BIT(0) | BIT(1), 0);
+		break;
+	case ML26124_USE_MCLKI_256FS:
+		ml26124_update_bits(codec, ML26124_CLK_CTL,
+				    BIT(0) | BIT(1), 1);
+		break;
+	case ML26124_USE_MCLKI_512FS:
+		ml26124_update_bits(codec, ML26124_CLK_CTL,
+				    BIT(0) | BIT(1), 2);
+		break;
+	case ML26124_USE_MCLKI_1024FS:
+		ml26124_update_bits(codec, ML26124_CLK_CTL,
+				    BIT(0) | BIT(1), 3);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (freq) {
+	case 12288000:
+		priv->mclk = freq;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int ml26124_set_bias_level(struct snd_soc_codec *codec,
+		enum snd_soc_bias_level level)
+{
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		/* VMID ON */
+		ml26124_update_bits(codec, ML26124_PW_REF_PW_MNG,
+				    ML26124_VMID, ML26124_VMID);
+		msleep(500);
+		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+			regcache_sync(priv->regmap);
+		break;
+	case SND_SOC_BIAS_OFF:
+		/* VMID OFF */
+		ml26124_update_bits(codec, ML26124_PW_REF_PW_MNG,
+				    ML26124_VMID, 0);
+		break;
+	}
+	codec->dapm.bias_level = level;
+	return 0;
+}
+
+static const struct snd_soc_dai_ops ml26124_dai_ops = {
+	.hw_params	= ml26124_hw_params,
+	.digital_mute	= ml26124_mute,
+	.set_fmt	= ml26124_set_dai_fmt,
+	.set_sysclk	= ml26124_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver ml26124_dai = {
+	.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);
+	codec->control_data = priv->regmap;
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		return ret;
+	}
+
+	/* Software Reset */
+	ml26124_update_bits(codec, ML26124_SW_RST, 0x01, 1);
+	ml26124_update_bits(codec, ML26124_SW_RST, 0x01, 0);
+
+	ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+}
+
+/* power down chip */
+static struct snd_soc_codec_driver soc_codec_dev_ml26124 = {
+	.probe =	ml26124_probe,
+	.suspend =	ml26124_suspend,
+	.resume =	ml26124_resume,
+	.set_bias_level = ml26124_set_bias_level,
+	.dapm_widgets = ml26124_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(ml26124_dapm_widgets),
+	.dapm_routes = ml26124_intercon,
+	.num_dapm_routes = ARRAY_SIZE(ml26124_intercon),
+	.controls = ml26124_snd_controls,
+	.num_controls = ARRAY_SIZE(ml26124_snd_controls),
+};
+
+static const struct regmap_config ml26124_i2c_regmap = {
+	.val_bits = 8,
+	.reg_bits = 8,
+	.max_register = ML26124_NUM_REGISTER,
+	.reg_defaults = ml26124_reg,
+	.num_reg_defaults = ARRAY_SIZE(ml26124_reg),
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static __devinit int ml26124_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct ml26124_priv *priv;
+	int ret;
+
+	priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, priv);
+
+	priv->regmap = regmap_init_i2c(i2c, &ml26124_i2c_regmap);
+	if (IS_ERR(priv->regmap)) {
+		ret = PTR_ERR(priv->regmap);
+		dev_err(&i2c->dev, "regmap_init_i2c() failed: %d\n", ret);
+		return ret;
+	}
+
+	return snd_soc_register_codec(&i2c->dev,
+			&soc_codec_dev_ml26124, &ml26124_dai, 1);
+}
+
+static __devexit int ml26124_i2c_remove(struct i2c_client *client)
+{
+	struct ml26124_priv *priv = i2c_get_clientdata(client);
+
+	snd_soc_unregister_codec(&client->dev);
+	regmap_exit(priv->regmap);
+	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,
+};
+
+module_i2c_driver(ml26124_i2c_driver);
+
+MODULE_AUTHOR("Tomoya MORINAGA <tomoya.rohm@gmail.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..d4a8df5
--- /dev/null
+++ b/sound/soc/codecs/ml26124.h
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+#ifndef ML26124_H
+#define ML26124_H
+
+/* Clock Control Register */
+#define ML26124_SMPLING_RATE		0x00
+#define ML26124_PLLNL			0x02
+#define ML26124_PLLNH			0x04
+#define ML26124_PLLML			0x06
+#define ML26124_PLLMH			0x08
+#define ML26124_PLLDIV			0x0a
+#define ML26124_CLK_EN			0x0c
+#define ML26124_CLK_CTL			0x0e
+
+/* System Control Register */
+#define ML26124_SW_RST			0x10
+#define ML26124_REC_PLYBAK_RUN		0x12
+#define ML26124_MIC_TIM			0x14
+
+/* Power Mnagement Register */
+#define ML26124_PW_REF_PW_MNG		0x20
+#define ML26124_PW_IN_PW_MNG		0x22
+#define ML26124_PW_DAC_PW_MNG		0x24
+#define ML26124_PW_SPAMP_PW_MNG		0x26
+#define ML26124_PW_LOUT_PW_MNG		0x28
+#define ML26124_PW_VOUT_PW_MNG		0x2a
+#define ML26124_PW_ZCCMP_PW_MNG		0x2e
+
+/* Analog Reference Control Register */
+#define ML26124_PW_MICBIAS_VOL		0x30
+
+/* Input/Output Amplifier Control Register */
+#define ML26124_PW_MIC_IN_VOL		0x32
+#define ML26124_PW_MIC_BOST_VOL		0x38
+#define ML26124_PW_SPK_AMP_VOL		0x3a
+#define ML26124_PW_AMP_VOL_FUNC		0x48
+#define ML26124_PW_AMP_VOL_FADE		0x4a
+
+/* Analog Path Control Register */
+#define ML26124_SPK_AMP_OUT		0x54
+#define ML26124_MIC_IF_CTL		0x5a
+#define ML26124_MIC_SELECT		0xe8
+
+/* Audio Interface Control Register */
+#define ML26124_SAI_TRANS_CTL		0x60
+#define ML26124_SAI_RCV_CTL		0x62
+#define ML26124_SAI_MODE_SEL		0x64
+
+/* DSP Control Register */
+#define ML26124_FILTER_EN		0x66
+#define ML26124_DVOL_CTL		0x68
+#define ML26124_MIXER_VOL_CTL		0x6a
+#define ML26124_RECORD_DIG_VOL		0x6c
+#define ML26124_PLBAK_DIG_VOL		0x70
+#define ML26124_DIGI_BOOST_VOL		0x72
+#define ML26124_EQ_GAIN_BRAND0		0x74
+#define ML26124_EQ_GAIN_BRAND1		0x76
+#define ML26124_EQ_GAIN_BRAND2		0x78
+#define ML26124_EQ_GAIN_BRAND3		0x7a
+#define ML26124_EQ_GAIN_BRAND4		0x7c
+#define ML26124_HPF2_CUTOFF		0x7e
+#define ML26124_EQBRAND0_F0L		0x80
+#define ML26124_EQBRAND0_F0H		0x82
+#define ML26124_EQBRAND0_F1L		0x84
+#define ML26124_EQBRAND0_F1H		0x86
+#define ML26124_EQBRAND1_F0L		0x88
+#define ML26124_EQBRAND1_F0H		0x8a
+#define ML26124_EQBRAND1_F1L		0x8c
+#define ML26124_EQBRAND1_F1H		0x8e
+#define ML26124_EQBRAND2_F0L		0x90
+#define ML26124_EQBRAND2_F0H		0x92
+#define ML26124_EQBRAND2_F1L		0x94
+#define ML26124_EQBRAND2_F1H		0x96
+#define ML26124_EQBRAND3_F0L		0x98
+#define ML26124_EQBRAND3_F0H		0x9a
+#define ML26124_EQBRAND3_F1L		0x9c
+#define ML26124_EQBRAND3_F1H		0x9e
+#define ML26124_EQBRAND4_F0L		0xa0
+#define ML26124_EQBRAND4_F0H		0xa2
+#define ML26124_EQBRAND4_F1L		0xa4
+#define ML26124_EQBRAND4_F1H		0xa6
+
+/* ALC Control Register */
+#define ML26124_ALC_MODE		0xb0
+#define ML26124_ALC_ATTACK_TIM		0xb2
+#define ML26124_ALC_DECAY_TIM		0xb4
+#define ML26124_ALC_HOLD_TIM		0xb6
+#define ML26124_ALC_TARGET_LEV		0xb8
+#define ML26124_ALC_MAXMIN_GAIN		0xba
+#define ML26124_NOIS_GATE_THRSH		0xbc
+#define ML26124_ALC_ZERO_TIMOUT		0xbe
+
+/* Playback Limiter Control Register */
+#define ML26124_PL_ATTACKTIME		0xc0
+#define ML26124_PL_DECAYTIME		0xc2
+#define ML26124_PL_TARGETTIME		0xc4
+#define ML26124_PL_MAXMIN_GAIN		0xc6
+#define ML26124_PLYBAK_BOST_VOL		0xc8
+#define ML26124_PL_0CROSS_TIMOUT	0xca
+
+/* Video Amplifer Control Register */
+#define ML26124_VIDEO_AMP_GAIN_CTL	0xd0
+#define ML26124_VIDEO_AMP_SETUP1	0xd2
+#define ML26124_VIDEO_AMP_CTL2		0xd4
+
+/* Clock select for machine driver */
+#define ML26124_USE_PLL			0
+#define ML26124_USE_MCLKI_256FS		1
+#define ML26124_USE_MCLKI_512FS		2
+#define ML26124_USE_MCLKI_1024FS	3
+
+enum ml26124_regs {
+	ML26124_MCLK = 0,
+};
+
+#endif
-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v6] sound/soc/lapis: add machine driver for ML7213 Carrier Board
  2012-03-07  1:57         ` Tomoya MORINAGA
@ 2012-03-09  6:38           ` Tomoya MORINAGA
  2012-03-14 14:45             ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-09  6:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	alsa-devel, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, Tomoya MORINAGA

This machine driver is for LAPIS Semiconductor ML7213 Carrier Board.

Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>
---
V6
 - Add master/slave settings
 - Use snd_soc_register_card
 - Move set_clkdiv to dai_hw_params
---
 sound/soc/lapis/Kconfig             |    7 ++
 sound/soc/lapis/Makefile            |    2 +
 sound/soc/lapis/ml7213ioh-machine.c |  179 +++++++++++++++++++++++++++++++++++
 3 files changed, 188 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/lapis/ml7213ioh-machine.c

diff --git a/sound/soc/lapis/Kconfig b/sound/soc/lapis/Kconfig
index 551e385..9bc1b4d 100644
--- a/sound/soc/lapis/Kconfig
+++ b/sound/soc/lapis/Kconfig
@@ -2,3 +2,10 @@ config SND_SOC_ML7213_PLATFORM
 	tristate "ML7213 IOH ASoC platform driver"
 	help
 	  This option enables support for the AC Link Controllers in ML7213 IOH SoC.
+
+config SND_SOC_ML7213_MACHINE
+	tristate "ML7213 IOH ASoC machine driver"
+	select SND_SOC_ML7213_PLATFORM
+	select SND_SOC_ML26124
+	help
+	  This is ASoC machine driver for ML7213 IOH
diff --git a/sound/soc/lapis/Makefile b/sound/soc/lapis/Makefile
index aba1630..7ec4bea 100644
--- a/sound/soc/lapis/Makefile
+++ b/sound/soc/lapis/Makefile
@@ -1,4 +1,6 @@
 # Platform
+snd-soc-ml7213-machine-objs := ml7213ioh-machine.o
 snd-soc-ml7213-plat-objs := ml7213ioh-plat.o
 
 obj-$(CONFIG_SND_SOC_ML7213_PLATFORM) += snd-soc-ml7213-plat.o
+obj-$(CONFIG_SND_SOC_ML7213_MACHINE) += snd-soc-ml7213-machine.o
diff --git a/sound/soc/lapis/ml7213ioh-machine.c b/sound/soc/lapis/ml7213ioh-machine.c
new file mode 100644
index 0000000..eab7e70
--- /dev/null
+++ b/sound/soc/lapis/ml7213ioh-machine.c
@@ -0,0 +1,179 @@
+/*
+ * ml7213ioh-machine.c -- SoC Audio for LAPIS Semiconductor ML7213 IOH CRB
+ *
+ * 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/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <linux/i2c.h>
+#include "../codecs/ml26124.h"
+#include "ioh_i2s.h"
+
+static const struct snd_soc_dapm_widget ml7213_dapm_widgets[] = {
+	SND_SOC_DAPM_SPK("Speaker", NULL),
+	SND_SOC_DAPM_LINE("LINEOUT", NULL),
+	SND_SOC_DAPM_LINE("LINEIN", NULL),
+	SND_SOC_DAPM_MIC("AnalogMIC", NULL),
+	SND_SOC_DAPM_MIC("DigitalMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route ml7213_routes[] = {
+	{"Speaker", NULL, "SPOUT"},
+	{"LINEOUT", NULL, "LOUT"},
+	{"MIN", NULL, "AnalogMIC"},
+	{"MDIN", NULL, "DigitalMIC"},
+	{"MIN", NULL, "LINEIN"},
+};
+
+static int ml7213_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *hw_params)
+{
+	int bclkfs;
+	int mclkfs;
+	unsigned int clk;
+	int ret;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	unsigned int channel_map[] = {5, 0, 1, 2, 3, 4};
+
+	/* set cpu DAI channel mapping */
+	ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
+		channel_map, ARRAY_SIZE(channel_map), channel_map);
+	if (ret < 0)
+		return ret;
+
+	switch (params_rate(hw_params)) {
+	case 16000:
+	case 32000:
+	case 48000:
+		clk = 12288000;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	mclkfs = clk / params_rate(hw_params);
+
+	switch (params_format(hw_params)) {
+	case SNDRV_PCM_FORMAT_U8:
+	case SNDRV_PCM_FORMAT_S16_LE:
+		bclkfs = 32;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		bclkfs = 64;
+		break;
+	default:
+		pr_err("%s: Failed not support format\n", __func__);
+		return -EINVAL;
+		break;
+	}
+
+	/* set the codec system clock for DAC and ADC */
+	ret = snd_soc_dai_set_sysclk(codec_dai, ML26124_USE_PLL, clk,
+				     SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	/* set codec DAI configuration */
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+				 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	/* set cpu DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+				 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct snd_soc_ops ml7213_ops = {
+	.hw_params = ml7213_hw_params,
+};
+
+static struct snd_soc_dai_link ml7213crb_dai = {
+	.name = "ml26124",
+	.stream_name = "ML26124",
+	.cpu_dai_name = "ml7213ioh",
+	.codec_dai_name = "ml26124-hifi",
+	.platform_name	= "ml7213-i2s-audio",
+	.codec_name	= "ml26124.1-001a",
+	.ops = &ml7213_ops,
+};
+
+static struct snd_soc_card ml7213crb_card = {
+	.name		= "LAPIS Semiconductor ML7213CRB",
+	.dai_link	= &ml7213crb_dai,
+	.num_links	= 1,
+	.dapm_widgets	= ml7213_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(ml7213_dapm_widgets),
+	.dapm_routes	= ml7213_routes,
+	.num_dapm_routes	= ARRAY_SIZE(ml7213_routes),
+};
+
+static int __devinit lapis_ml7213_ml26124_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ml7213crb_card.dev = &pdev->dev;
+	ret = snd_soc_register_card(&ml7213crb_card);
+	if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __devexit lapis_ml7213_ml26124_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_card(&ml7213crb_card);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static struct platform_driver snd_ml7213crb_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "lapis-ml7213ioh-CRB",
+	},
+	.probe = lapis_ml7213_ml26124_probe,
+	.remove = __devexit_p(lapis_ml7213_ml26124_remove),
+};
+
+static int __init lapis_ml7213_ml26124_init(void)
+{
+	return platform_driver_register(&snd_ml7213crb_driver);
+}
+module_init(lapis_ml7213_ml26124_init);
+
+static void __exit lapis_ml7213_ml26124_exit(void)
+{
+	platform_driver_unregister(&snd_ml7213crb_driver);
+}
+module_exit(lapis_ml7213_ml26124_exit);
+
+MODULE_AUTHOR("Tomoya MORINAGA <tomoya.rohm@gmail.com>");
+MODULE_DESCRIPTION("LAPIS Semiconductor ML7213 IOH ALSA SoC machine driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform: lapis-ml7213ioh-CRB");
-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v4] sound/soc/lapis: add platform driver for ML7213
  2012-03-09  6:26                 ` Tomoya MORINAGA
@ 2012-03-09  6:39                   ` Tomoya MORINAGA
  2012-03-19 12:00                     ` [PATCH v5] " Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-09  6:39 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	alsa-devel, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, Tomoya MORINAGA

This driver is for LAPIS Semiconductor ML7213 IOH I2S.

Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>
---
V4
 - Delete redundant "struct platform_device" settings
 - Move request_irq to at pcm_new()
 - Allocate memory with dynamically not static
 - Delete redundant '\' deteminations
 - Add SND_SOC_DAIFMT_IB_NF setting
 - Move dai_set_clkdiv() to dai_hw_params()
 - Melt ioh_i2s_save/restore_reg_conf into ml7213i2s_soc_suspend/resume
 - Delete redundant "#ifdef CONFIG_PM" condition correctly
---
 sound/soc/Kconfig                |    1 +
 sound/soc/Makefile               |    1 +
 sound/soc/lapis/Kconfig          |    4 +
 sound/soc/lapis/Makefile         |    4 +
 sound/soc/lapis/ioh_i2s.h        |   39 ++
 sound/soc/lapis/ml7213ioh-plat.c | 1320 ++++++++++++++++++++++++++++++++++++++
 sound/soc/lapis/ml7213ioh-plat.h |  376 +++++++++++
 7 files changed, 1745 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/lapis/Kconfig
 create mode 100644 sound/soc/lapis/Makefile
 create mode 100644 sound/soc/lapis/ioh_i2s.h
 create mode 100644 sound/soc/lapis/ml7213ioh-plat.c
 create mode 100644 sound/soc/lapis/ml7213ioh-plat.h

diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 91c9855..97d0d33 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -37,6 +37,7 @@ source "sound/soc/ep93xx/Kconfig"
 source "sound/soc/fsl/Kconfig"
 source "sound/soc/imx/Kconfig"
 source "sound/soc/jz4740/Kconfig"
+source "sound/soc/lapis/Kconfig"
 source "sound/soc/nuc900/Kconfig"
 source "sound/soc/omap/Kconfig"
 source "sound/soc/kirkwood/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 2feaf37..1f266ee 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_SND_SOC)	+= ep93xx/
 obj-$(CONFIG_SND_SOC)	+= fsl/
 obj-$(CONFIG_SND_SOC)   += imx/
 obj-$(CONFIG_SND_SOC)	+= jz4740/
+obj-$(CONFIG_SND_SOC)	+= lapis/
 obj-$(CONFIG_SND_SOC)	+= mid-x86/
 obj-$(CONFIG_SND_SOC)	+= mxs/
 obj-$(CONFIG_SND_SOC)	+= nuc900/
diff --git a/sound/soc/lapis/Kconfig b/sound/soc/lapis/Kconfig
new file mode 100644
index 0000000..551e385
--- /dev/null
+++ b/sound/soc/lapis/Kconfig
@@ -0,0 +1,4 @@
+config SND_SOC_ML7213_PLATFORM
+	tristate "ML7213 IOH ASoC platform driver"
+	help
+	  This option enables support for the AC Link Controllers in ML7213 IOH SoC.
diff --git a/sound/soc/lapis/Makefile b/sound/soc/lapis/Makefile
new file mode 100644
index 0000000..aba1630
--- /dev/null
+++ b/sound/soc/lapis/Makefile
@@ -0,0 +1,4 @@
+# Platform
+snd-soc-ml7213-plat-objs := ml7213ioh-plat.o
+
+obj-$(CONFIG_SND_SOC_ML7213_PLATFORM) += snd-soc-ml7213-plat.o
diff --git a/sound/soc/lapis/ioh_i2s.h b/sound/soc/lapis/ioh_i2s.h
new file mode 100644
index 0000000..9f19f70
--- /dev/null
+++ b/sound/soc/lapis/ioh_i2s.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#ifndef ML7213_IOH_I2S
+#define ML7213_IOH_I2S
+
+enum ioh_bclkfs {
+	ML7213IOH_BCLKFS0 = 0,
+	ML7213IOH_BCLKFS1,
+	ML7213IOH_BCLKFS2,
+	ML7213IOH_BCLKFS3,
+	ML7213IOH_BCLKFS4,
+	ML7213IOH_BCLKFS5,
+};
+
+enum ioh_mclkfs {
+	ML7213IOH_MCLKFS0 = 6,
+	ML7213IOH_MCLKFS1,
+	ML7213IOH_MCLKFS2,
+	ML7213IOH_MCLKFS3,
+	ML7213IOH_MCLKFS4,
+	ML7213IOH_MCLKFS5,
+};
+
+#endif
diff --git a/sound/soc/lapis/ml7213ioh-plat.c b/sound/soc/lapis/ml7213ioh-plat.c
new file mode 100644
index 0000000..1725a2c
--- /dev/null
+++ b/sound/soc/lapis/ml7213ioh-plat.c
@@ -0,0 +1,1320 @@
+/*
+ * 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/init.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+#include "ioh_i2s.h"
+#include "ml7213ioh-plat.h"
+
+static struct ioh_i2s_data *i2s_data;
+static struct ioh_i2s_dma *dmadata;
+
+/* I2S HAL (Hardware Abstruction Layer) */
+static void ioh_i2s_reset(int ch)
+{
+	iowrite32(1 << ch, i2s_data->iobase + I2SSRST_OFFSET);
+	iowrite32(0, i2s_data->iobase + I2SSRST_OFFSET);
+}
+
+static void ioh_i2s_enable_interrupts(int ch, int dir)
+{
+	unsigned int intr_lines = 0;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		intr_lines = 1 << (I2S_IMASK_RX_BIT_START + ch);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		intr_lines = 1 << (I2S_IMASK_TX_BIT_START + ch);
+		break;
+	}
+
+	/* enable interrupts for specified channel */
+	iowrite32(intr_lines, i2s_data->iobase + I2SIMASKCLR_OFFSET);
+}
+
+static void ioh_i2s_disable_interrupts(int ch, int dir)
+{
+	unsigned int intr_lines;
+
+	intr_lines = ioread32(i2s_data->iobase + I2SIMASK_OFFSET);
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		intr_lines |= 1 << (I2S_IMASK_RX_BIT_START + ch);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		intr_lines |= 1 << (I2S_IMASK_TX_BIT_START + ch);
+		break;
+	}
+
+	/* Mask the specific interrupt bits */
+	iowrite32(intr_lines, i2s_data->iobase + I2SIMASK_OFFSET);
+}
+
+#define IOH_FIFO_CLR	0
+#define IOH_FIFO_RUN	1
+
+static void ioh_i2s_ctrl_fifo(int ch, int dir, int ctrl)
+{
+	int offset = ch * 0x800;
+	u32 val;
+	u32 reg_addr = 0;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		reg_addr = I2SFIFOCRX_OFFSET;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		reg_addr = I2SFIFOCTX_OFFSET;
+		break;
+	}
+
+	val = ioread32(i2s_data->iobase + reg_addr + offset);
+	if (ctrl)
+		val |= I2S_FIFO_TX_RUN;
+	else
+		val |= I2S_FIFO_TX_FCLR;
+
+	iowrite32(val, i2s_data->iobase + reg_addr + offset);
+}
+
+/* Clear interrupt status */
+static void ioh_i2s_clear_sts_ir(int ch, int dir)
+{
+	int offset = ch * 0x800;
+	u32 reg_addr = 0;
+	u32 val = 0;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		reg_addr = I2SISTRX_OFFSET;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		reg_addr = I2SISTTX_OFFSET;
+		break;
+	}
+	val = ioread32(i2s_data->iobase + reg_addr + offset) & 0xf;
+	if (val)
+		iowrite32(val, i2s_data->iobase + reg_addr + offset);
+}
+
+static void ioh_i2s_clear_dma_mask(int ch, int dir)
+{
+	u32 val = 0;
+	u32 mask = 0;
+	u32 reg_addr = 0;
+	int offset = ch * 0x800;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		reg_addr = I2SMSKRX_OFFSET;
+		mask = RX_BIT_DMAMSK;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		reg_addr = I2SMSKTX_OFFSET;
+		mask = TX_BIT_DMAMSK;
+		break;
+	}
+
+	val = ioread32(i2s_data->iobase + reg_addr + offset);
+	val &= ~mask; /* Enable Tx DMA Request */
+
+	iowrite32(val, i2s_data->iobase + reg_addr + offset);
+}
+
+#define IOH_DIS_IRQ		0
+#define IOH_EN_IRQ		1
+
+static void ioh_i2s_irq_ctrl(int ch, int dir, int ctrl)
+{
+	u32 val;
+	int offset = ch * 0x800;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		val = ioread32(i2s_data->iobase + I2SMSKRX_OFFSET + offset);
+		if (ctrl) {
+			val &= ~RX_BIT_AFIMSK; /* Enable Almost empty IR */
+			val &= ~RX_BIT_FIMSK; /* Enable Empty IR */
+		} else {
+			val |= RX_BIT_AFIMSK; /* Disble Almost full IR */
+			val |= RX_BIT_FIMSK; /* Disble full IR */
+		}
+		iowrite32(val, i2s_data->iobase + I2SMSKRX_OFFSET + offset);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		val = ioread32(i2s_data->iobase + I2SMSKTX_OFFSET + offset);
+		if (ctrl) {
+			val &= ~TX_BIT_AEIMSK; /* Enable Almost empty IR */
+			val &= ~TX_BIT_EIMSK; /* Enable Empty IR */
+		} else {
+			val |= TX_BIT_AEIMSK; /* Disble Almost empty IR */
+			val |= TX_BIT_EIMSK; /* Disble Empty IR */
+		}
+		iowrite32(val, i2s_data->iobase + I2SMSKTX_OFFSET + offset);
+		break;
+	}
+
+}
+
+/* Linux standard DMA functions */
+static bool ioh_dma_filter(struct dma_chan *chan, void *slave)
+{
+	struct pch_dma_slave *param = slave;
+
+	if ((chan->chan_id == param->chan_id) && (param->dma_dev ==
+						  chan->device->dev)) {
+		chan->private = param;
+		return true;
+	} else {
+		return false;
+	}
+}
+
+int ioh_request_dma_channel(
+		   int ch, struct ioh_i2s_dma *ioh, enum dma_data_direction dir)
+{
+	dma_cap_mask_t mask;
+	struct dma_chan *chan;
+	struct pci_dev *dma_dev;
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(0, 1)); /* Get DMA's dev
+								information */
+
+	switch (dir) {
+	case DMA_FROM_DEVICE:
+		ioh->param_rx.width = ioh->dma_rx_width;
+		ioh->param_rx.dma_dev = &dma_dev->dev;
+		ioh->param_rx.chan_id = ch * 2 + 1; /* ch Rx=1,3,...11 */
+		ioh->param_rx.rx_reg = (dma_addr_t)(i2s_data->mapbase +
+					ch * 0x800 + I2SDRRXMIRROR_OFFSET);
+		chan = dma_request_channel(mask, ioh_dma_filter,
+					   &ioh->param_rx);
+		if (chan == NULL) {
+			dev_err(i2s_data->dev, "Failed dma_request_channel for"
+				" I2S %d\n", ch);
+			return -ENOMEM;
+		}
+		ioh->chan_rx = chan;
+		break;
+	case DMA_TO_DEVICE:
+		ioh->param_tx.width = ioh->dma_tx_width;
+		ioh->param_tx.dma_dev = &dma_dev->dev;
+		ioh->param_tx.chan_id = ch * 2; /* DMA ch Tx=0,2,...10 */
+
+		ioh->param_tx.tx_reg = (dma_addr_t)(i2s_data->mapbase +
+					ch * 0x800 + I2SDRTXMIRROR_OFFSET);
+
+		chan = dma_request_channel(mask, ioh_dma_filter,
+					   &ioh->param_tx);
+		if (chan == NULL) {
+			dev_err(i2s_data->dev, "Failed dma_request_channel for"
+				" I2S %d\n", ch);
+			return -ENOMEM;
+		}
+		ioh->chan_tx = chan;
+		break;
+	default:
+		dev_err(i2s_data->dev, "Invalid direction (%d)\n", dir);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void ioh_i2s_configure_i2s_regs(int ch, int dir)
+{
+	int offset = ch * 0x800;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		/* Rx register */
+		iowrite32(I2S_AFULL_THRESH / 2,
+		i2s_data->iobase + I2SAFRX_OFFSET + offset);
+		iowrite32(ML7213_I2SAERX_DEFAULT,
+			  i2s_data->iobase + I2SAERX_OFFSET + offset);
+		iowrite32(ML7213_I2SMSKRX_DEFAULT,
+			  i2s_data->iobase + I2SMSKRX_OFFSET + offset);
+		iowrite32(ML7213_I2SISTRX_DEFAULT,
+			  i2s_data->iobase + I2SISTRX_OFFSET + offset);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		iowrite32(0, i2s_data->iobase + I2SAFTX_OFFSET + offset);
+		iowrite32(I2S_AEMPTY_THRESH / 2,
+		i2s_data->iobase + I2SAETX_OFFSET + offset);
+		iowrite32(ML7213_I2SMSKTX_DEFAULT,
+			  i2s_data->iobase + I2SMSKTX_OFFSET + offset);
+		iowrite32(ML7213_I2SISTTX_DEFAULT,
+			  i2s_data->iobase + I2SISTTX_OFFSET + offset);
+		break;
+	}
+}
+
+static void i2s_dma_rx_complete(void *arg)
+{
+	struct ioh_i2s_dma *ioh = (struct ioh_i2s_dma *)arg;
+	struct ml7213i2s_runtime_data *ioh_rtd =
+				ioh->rx_substream->runtime->private_data;
+
+	pr_debug("%s in rx_cur_period=%d\n", __func__, ioh->rx_cur_period);
+
+	if (ioh_rtd->rx_stop) {
+		pr_debug("%s stopped. return.\n", __func__);
+		return;
+	}
+
+	ioh->rx_cur_period++;
+	if (ioh->rx_cur_period == ioh->rx_buf_frags)
+		ioh->rx_cur_period = 0;
+
+	async_tx_ack(ioh->desc_rx);
+	if (ioh->rx_substream)
+		snd_pcm_period_elapsed(ioh->rx_substream);
+	ioh_i2s_irq_ctrl(ioh->number, SNDRV_PCM_STREAM_CAPTURE, IOH_EN_IRQ);
+}
+
+static void i2s_dma_tx_complete(void *arg)
+{
+	struct ioh_i2s_dma *ioh = (struct ioh_i2s_dma *)arg;
+	struct ml7213i2s_runtime_data *ioh_rtd =
+				ioh->tx_substream->runtime->private_data;
+
+	if (ioh_rtd->tx_stop) {
+		pr_debug("%s stopped. return.\n", __func__);
+		return;
+	}
+
+	ioh->tx_cur_period++;
+	if (ioh->tx_cur_period == ioh->tx_buf_frags)
+		ioh->tx_cur_period = 0;
+
+	async_tx_ack(ioh->desc_tx);
+
+	if (ioh->tx_substream)
+		snd_pcm_period_elapsed(ioh->tx_substream);
+
+	ioh_i2s_irq_ctrl(ioh->number, SNDRV_PCM_STREAM_PLAYBACK, IOH_EN_IRQ);
+}
+
+static void i2s_dma_rx_start(struct ioh_i2s_dma *ioh, char *buff)
+{
+	struct dma_async_tx_descriptor *desc;
+	int rx_size;
+	int rx_num;
+	int i;
+	struct scatterlist *sg;
+
+	rx_size = I2S_AFULL_THRESH * 4;
+
+	/* The number of scatter list (Franction area is not used) */
+	if (ioh->rx_period_bytes % rx_size)
+		/* rx_num = The number of scatter list */
+		rx_num = ioh->rx_period_bytes / rx_size + 1;
+	else
+		rx_num = ioh->rx_period_bytes / rx_size;
+
+	dev_dbg(i2s_data->dev, "%s: rx: scatter_num=%d scatter_size=%d\n",
+		__func__, rx_num, rx_size);
+
+	ioh->sg_rx_p =
+	       kzalloc(sizeof(struct scatterlist) * rx_num, GFP_ATOMIC);
+
+	sg = ioh->sg_rx_p;
+
+	sg_init_table(sg, rx_num); /* Initialize SG table */
+	for (i = 0; i < rx_num; i++, sg++) {
+		sg_set_page(sg,  virt_to_page(buff), rx_size, rx_size * i);
+		sg_dma_len(sg) = rx_size / 4;
+		sg_dma_address(sg) = ioh->rx_physical_addr + sg->offset;
+	}
+	ioh->rx_nent = rx_num;
+
+	dma_sync_sg_for_device(i2s_data->dev, ioh->sg_rx_p, ioh->rx_nent,
+			       DMA_FROM_DEVICE);
+
+	sg = ioh->sg_rx_p;
+
+	desc = ioh->chan_rx->device->device_prep_slave_sg(ioh->chan_rx,
+			sg, rx_num, DMA_FROM_DEVICE,
+			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(i2s_data->dev, "%s:device_prep_slave_sg Failed\n",
+			__func__);
+		return;
+	}
+	ioh_i2s_irq_ctrl(ioh->number, SNDRV_PCM_STREAM_CAPTURE, IOH_DIS_IRQ);
+
+	ioh->desc_rx = desc;
+
+	desc->callback = i2s_dma_rx_complete;
+	desc->callback_param = ioh;
+
+	desc->tx_submit(desc);
+}
+
+static void i2s_dma_tx_start(struct ioh_i2s_dma *ioh, char *buff)
+{
+	struct dma_async_tx_descriptor *desc;
+	int tx_size;
+	int tx_num;
+	int i;
+	struct scatterlist *sg;
+
+	tx_size = I2S_AEMPTY_THRESH * 4;
+	if (ioh->tx_period_bytes % tx_size)
+		/* tx_num = The number of scatter list */
+		tx_num = ioh->tx_period_bytes / tx_size + 1;
+	else
+		tx_num = ioh->tx_period_bytes / tx_size;
+
+	dev_dbg(i2s_data->dev, "%s: tx: scatter_num=%d scatter_size=%d\n",
+		__func__, tx_num, tx_size);
+
+	ioh->sg_tx_p =
+	       kzalloc(sizeof(struct scatterlist) * tx_num, GFP_ATOMIC);
+
+	sg = ioh->sg_tx_p;
+	sg_init_table(sg, tx_num); /* Initialize SG table */
+
+	for (i = 0; i < tx_num; i++, sg++) {
+		sg_set_page(sg,  virt_to_page(buff), tx_size, tx_size * i);
+		sg_dma_len(sg) = tx_size / 4;
+		sg_dma_address(sg) = ioh->tx_physical_addr + sg->offset;
+	}
+	ioh->tx_nent = tx_num;
+
+	dma_sync_sg_for_device(i2s_data->dev, ioh->sg_tx_p, ioh->tx_nent,
+			       DMA_TO_DEVICE);
+
+	sg = ioh->sg_tx_p;
+
+	desc = ioh->chan_tx->device->device_prep_slave_sg(ioh->chan_tx,
+					sg, tx_num, DMA_TO_DEVICE,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(i2s_data->dev, "%s:device_prep_slave_sg Failed\n",
+			__func__);
+		return;
+	}
+
+	ioh_i2s_irq_ctrl(ioh->number, SNDRV_PCM_STREAM_PLAYBACK, IOH_DIS_IRQ);
+
+	ioh->desc_tx = desc;
+
+	desc->callback = i2s_dma_tx_complete;
+	desc->callback_param = ioh;
+	desc->tx_submit(desc);
+}
+
+static void ioh_release_dma_channel(int ch, int dir)
+{
+	struct ioh_i2s_dma *ioh;
+
+	ioh = &dmadata[ch];
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		dma_sync_sg_for_cpu(i2s_data->dev, ioh->sg_rx_p, ioh->rx_nent,
+				    DMA_FROM_DEVICE);
+
+		if (ioh->chan_rx) {
+			ioh->chan_rx->device->device_control(ioh->chan_rx,
+							     DMA_TERMINATE_ALL,
+							     0);
+			dma_release_channel(ioh->chan_rx);
+			ioh->chan_rx = NULL;
+		}
+
+		kfree(ioh->sg_rx_p);
+		ioh->rx_buf_dma = 0;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		dma_sync_sg_for_cpu(i2s_data->dev, ioh->sg_tx_p, ioh->tx_nent,
+				    DMA_TO_DEVICE);
+
+		if (ioh->chan_tx) {
+			ioh->chan_tx->device->device_control(ioh->chan_tx,
+							     DMA_TERMINATE_ALL,
+							     0);
+			dma_release_channel(ioh->chan_tx);
+			ioh->chan_tx = NULL;
+		}
+		kfree(ioh->sg_tx_p);
+		ioh->tx_buf_dma = 0;
+		break;
+	}
+}
+
+void ioh_i2s_event(u32 idisp, int ch)
+{
+	unsigned long flags;
+	unsigned int status;
+	char *buff;
+	int offset = ch * 0x800;
+	struct ioh_i2s_dma *ioh = &dmadata[ch];
+	struct ml7213i2s_runtime_data *ioh_rtd;
+
+	spin_lock_irqsave(&i2s_data->tx_lock, flags);
+
+	if (idisp & BIT(ch + 16)) {
+		dev_dbg(i2s_data->dev, "Rx%d interrupt occures\n", ch);
+		ioh_rtd = ioh->rx_substream->runtime->private_data;
+		buff = ioh->rx_dma_addr +
+				      ioh->rx_cur_period * ioh->rx_period_bytes;
+		status = ioread32(i2s_data->iobase + I2SISTRX_OFFSET + offset);
+
+		if (status & I2S_RX_FINT)
+			dev_dbg(i2s_data->dev, "%s:I2S%d overrun occurs\n",
+				__func__, ch);
+		if (status & I2S_RX_AFINT)
+			if (!ioh_rtd->rx_stop)
+				i2s_dma_rx_start(ioh, buff);
+
+		/*Clear the interrupt status */
+		iowrite32(status, i2s_data->iobase + I2SISTRX_OFFSET + offset);
+	}
+
+	if (idisp & BIT(ch)) {
+		dev_dbg(i2s_data->dev, "Tx%d interrupt occures\n", ch);
+		ioh_rtd = ioh->tx_substream->runtime->private_data;
+		buff = ioh->tx_dma_addr +
+				      ioh->tx_cur_period * ioh->tx_period_bytes;
+
+		status = ioread32(i2s_data->iobase + I2SISTTX_OFFSET + offset);
+		if (status & I2S_TX_EINT)
+			dev_dbg(i2s_data->dev, "%s:I2S%d under flow occurs\n",
+				__func__, ch);
+		if (status & I2S_TX_AEINT)
+			if (!ioh_rtd->tx_stop)
+				i2s_dma_tx_start(ioh, buff);
+
+		/*Clear the interrupt status */
+		iowrite32(status, i2s_data->iobase + I2SISTTX_OFFSET + offset);
+	}
+	spin_unlock_irqrestore(&i2s_data->tx_lock, flags);
+	return;
+}
+
+static irqreturn_t ioh_i2s_irq(int irq, void *data)
+{
+	int i;
+	u32 idisp;
+
+	idisp = ioread32(i2s_data->iobase + I2SIDISP_OFFSET);
+	for (i = 0; i < MAX_I2S_CH; i++)
+		ioh_i2s_event(idisp, i);
+
+	return IRQ_HANDLED;
+}
+
+static int snd_card_ml7213i2s_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ml7213i2s_runtime_data *ioh_rtd;
+
+	ioh_rtd = kzalloc(sizeof(*ioh_rtd), GFP_KERNEL);
+	if (ioh_rtd == NULL)
+		return -ENOMEM;
+
+	runtime->private_data = ioh_rtd;
+	runtime->hw = ml7213i2s_pcm_hw;
+
+	/* makes the infrastructure responsible for freeing dma */
+	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	snd_soc_set_runtime_hwparams(substream, &ml7213i2s_pcm_hw);
+
+	spin_lock_init(&ioh_rtd->lock);
+	return 0;
+}
+
+static int snd_card_ml7213i2s_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int ch;
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ch = dmadata[substream->number].mapped_rx_ch;
+		ioh_release_dma_channel(ch, SNDRV_PCM_STREAM_CAPTURE);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ch = dmadata[substream->number].mapped_tx_ch;
+		ioh_release_dma_channel(ch, SNDRV_PCM_STREAM_PLAYBACK);
+		break;
+	}
+
+	kfree(runtime->private_data);
+
+	return 0;
+}
+
+static int snd_card_ml7213i2s_hw_params(struct snd_pcm_substream *substream,
+				    struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct ml7213i2s_runtime_data *ioh_rtd = runtime->private_data;
+	struct ioh_i2s_dma *dma =
+			snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ioh_rtd->rx_dma = dma;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ioh_rtd->tx_dma = dma;
+		break;
+	}
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	runtime->dma_bytes = params_buffer_bytes(hw_params);
+
+	return snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+}
+
+static int snd_card_ml7213i2s_hw_free(struct snd_pcm_substream *substream)
+{
+	struct ml7213i2s_runtime_data *ioh_rtd;
+	ioh_rtd = substream->runtime->private_data;
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ioh_rtd->rx_dma = NULL;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ioh_rtd->tx_dma = NULL;
+		break;
+	}
+	snd_pcm_set_runtime_buffer(substream, NULL);
+
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_card_ml7213i2s_pcm_trigger
+				(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ml7213i2s_runtime_data *ioh_rtd = runtime->private_data;
+	int err = 0;
+	unsigned long flags;
+	int ch;
+	struct ioh_i2s_dma *ioh;
+
+	spin_lock_irqsave(&ioh_rtd->lock, flags);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		switch (substream->stream) {
+		case SNDRV_PCM_STREAM_CAPTURE:
+			ioh_rtd->rx_stop = 0;
+			ch = dmadata[substream->number].mapped_rx_ch;
+			pr_debug("%s Capture start.(ch=%d)\n", __func__, ch);
+			ioh = &dmadata[ch];
+			ioh->rx_substream = substream;
+			err = ioh_request_dma_channel(ch, &dmadata[ch],
+						      DMA_FROM_DEVICE);
+			if (err)
+				goto err_out;
+
+			ioh_i2s_configure_i2s_regs(ch,
+						   SNDRV_PCM_STREAM_CAPTURE);
+			ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_CAPTURE,
+					  IOH_FIFO_CLR);
+			ioh_i2s_clear_sts_ir(ch, SNDRV_PCM_STREAM_CAPTURE);
+			ioh_i2s_irq_ctrl(ch, SNDRV_PCM_STREAM_CAPTURE,
+					 IOH_EN_IRQ);
+			ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_CAPTURE,
+					  IOH_FIFO_RUN);
+			ioh_i2s_clear_dma_mask(ch, SNDRV_PCM_STREAM_CAPTURE);
+			ioh_i2s_enable_interrupts(ch, SNDRV_PCM_STREAM_CAPTURE);
+			break;
+		case SNDRV_PCM_STREAM_PLAYBACK:
+			if (!ioh_rtd)
+				return -1;
+			if (!substream)
+				return -1;
+			ioh_rtd->tx_stop = 0;
+			ch = dmadata[substream->number].mapped_tx_ch;
+			pr_debug("%s Playback start.(ch=%d)\n", __func__, ch);
+			ioh = &dmadata[ch];
+			ioh->tx_substream = substream;
+			err = ioh_request_dma_channel(ch, &dmadata[ch],
+						      DMA_TO_DEVICE);
+			if (err)
+				goto err_out;
+
+			ioh_i2s_configure_i2s_regs(ch,
+						   SNDRV_PCM_STREAM_PLAYBACK);
+			ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_PLAYBACK,
+					  IOH_FIFO_CLR);
+			ioh_i2s_clear_sts_ir(ch, SNDRV_PCM_STREAM_PLAYBACK);
+			ioh_i2s_irq_ctrl(ch, SNDRV_PCM_STREAM_PLAYBACK,
+					 IOH_EN_IRQ);
+			ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_PLAYBACK,
+					  IOH_FIFO_RUN);
+			ioh_i2s_clear_dma_mask(ch, SNDRV_PCM_STREAM_PLAYBACK);
+			ioh_i2s_enable_interrupts(ch,
+						  SNDRV_PCM_STREAM_PLAYBACK);
+			break;
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		switch (substream->stream) {
+		case SNDRV_PCM_STREAM_CAPTURE:
+			ioh_rtd->rx_stop = 1;
+			ch = dmadata[substream->number].mapped_rx_ch;
+			pr_debug("%s Capture stop.(ch=%d)\n", __func__, ch);
+			ioh_i2s_disable_interrupts(ch,
+						   SNDRV_PCM_STREAM_CAPTURE);
+			ioh_i2s_irq_ctrl(ch, SNDRV_PCM_STREAM_CAPTURE,
+					 IOH_DIS_IRQ);
+			ioh_i2s_clear_sts_ir(ch, SNDRV_PCM_STREAM_CAPTURE);
+			ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_CAPTURE,
+					  IOH_FIFO_CLR);
+			break;
+		case SNDRV_PCM_STREAM_PLAYBACK:
+			ioh_rtd->tx_stop = 1;
+			ch = dmadata[substream->number].mapped_tx_ch;
+			pr_debug("%s Playback stop.(ch=%d)\n", __func__, ch);
+			ioh_i2s_disable_interrupts(ch,
+						   SNDRV_PCM_STREAM_PLAYBACK);
+			ioh_i2s_irq_ctrl(ch, SNDRV_PCM_STREAM_PLAYBACK,
+					 IOH_DIS_IRQ);
+			ioh_i2s_clear_sts_ir(ch, SNDRV_PCM_STREAM_PLAYBACK);
+			ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_PLAYBACK,
+					  IOH_FIFO_CLR);
+			break;
+		}
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+err_out:
+	spin_unlock_irqrestore(&ioh_rtd->lock, flags);
+
+	return err;
+}
+
+static int snd_card_ml7213i2s_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ml7213i2s_runtime_data *ioh_rtd = runtime->private_data;
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ioh_rtd->rx_dma->rx_cur_period = 0;
+		ioh_rtd->rx_dma->rx_dma_addr = runtime->dma_area;
+		ioh_rtd->rx_dma->rx_physical_addr = runtime->dma_addr;
+		ioh_rtd->rx_dma->rx_buffer_bytes =
+					    snd_pcm_lib_buffer_bytes(substream);
+		ioh_rtd->rx_dma->rx_period_bytes =
+					    snd_pcm_lib_period_bytes(substream);
+		ioh_rtd->rx_dma->rx_buf_frags =
+					ioh_rtd->rx_dma->rx_buffer_bytes /
+					ioh_rtd->rx_dma->rx_period_bytes;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ioh_rtd->tx_dma->tx_cur_period = 0;
+		ioh_rtd->tx_dma->tx_dma_addr = runtime->dma_area;
+		ioh_rtd->tx_dma->tx_physical_addr = runtime->dma_addr;
+		ioh_rtd->tx_dma->tx_buffer_bytes =
+					    snd_pcm_lib_buffer_bytes(substream);
+		ioh_rtd->tx_dma->tx_period_bytes =
+					    snd_pcm_lib_period_bytes(substream);
+		ioh_rtd->tx_dma->tx_buf_frags =
+					ioh_rtd->tx_dma->tx_buffer_bytes /
+					ioh_rtd->tx_dma->tx_period_bytes;
+		break;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t
+snd_card_ml7213i2s_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct ml7213i2s_runtime_data *ioh_rtd =
+					substream->runtime->private_data;
+	unsigned long offset = 0;
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		offset = ioh_rtd->rx_dma->rx_cur_period *
+			 ioh_rtd->rx_dma->rx_period_bytes;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		offset = ioh_rtd->tx_dma->tx_cur_period *
+			 ioh_rtd->tx_dma->tx_period_bytes;
+		break;
+	}
+
+	return bytes_to_frames(substream->runtime, offset);
+}
+
+static struct snd_pcm_ops snd_card_ml7213i2s_ops = {
+	.open =			snd_card_ml7213i2s_open,
+	.close =		snd_card_ml7213i2s_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_card_ml7213i2s_hw_params,
+	.hw_free =		snd_card_ml7213i2s_hw_free,
+	.prepare =		snd_card_ml7213i2s_pcm_prepare,
+	.trigger =		snd_card_ml7213i2s_pcm_trigger,
+	.pointer =		snd_card_ml7213i2s_pcm_pointer,
+};
+
+static u64 idma_mask = DMA_BIT_MASK(32);
+static int ml7213ioh_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_pcm *pcm = rtd->pcm;
+	int ret = 0;
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &idma_mask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	snd_pcm_lib_preallocate_pages_for_all(
+		pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+		snd_dma_continuous_data(GFP_KERNEL),
+		0, 64 * 128 * 4 * 64);
+
+	ret = request_irq(i2s_data->pdev->irq, ioh_i2s_irq, IRQF_SHARED,
+			  "ml7213_ioh", i2s_data->pdev);
+	if (ret != 0)
+		printk(KERN_ERR "Failed to allocate irq\n");
+
+	return ret;
+}
+
+static void ml7213ioh_pcm_free(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+
+	free_irq(i2s_data->pdev->irq, i2s_data->pdev);
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+		buf->area = NULL;
+	}
+}
+
+static struct snd_soc_platform_driver ml7213ioh_soc_platform = {
+	.pcm_new	= ml7213ioh_pcm_new,
+	.pcm_free	= ml7213ioh_pcm_free,
+	.ops		= &snd_card_ml7213i2s_ops,
+};
+
+/* DAI functions */
+static int ml7213i2s_dai_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *hw_params,
+			    struct snd_soc_dai *dai)
+{
+	int ch;
+	int byte;
+	unsigned int clk;
+	u32 i2sclkcnt;
+	u32 bclkfs = 0;
+	u32 mclkfs = 0;
+	void *iobase = i2s_data->iobase;
+
+	switch (params_format(hw_params)) {
+	case SNDRV_PCM_FORMAT_U8:
+		byte = 8;
+		bclkfs = 32;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		byte = 16;
+		bclkfs = 32;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		byte = 24;
+		bclkfs = 64;
+		break;
+	default:
+		pr_err("%s: Failed not support format\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ch = dmadata[substream->number].mapped_rx_ch;
+		dmadata[ch].dma_rx_unit = byte;
+		dmadata[ch].dma_rx_width = PCH_DMA_WIDTH_4_BYTES;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ch = dmadata[substream->number].mapped_tx_ch;
+		dmadata[ch].dma_tx_unit = byte;
+		dmadata[ch].dma_tx_width = PCH_DMA_WIDTH_4_BYTES;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (params_rate(hw_params)) {
+	case 16000:
+	case 32000:
+	case 48000:
+		clk = 12288000;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	mclkfs = clk / params_rate(hw_params);
+
+	switch (params_format(hw_params)) {
+	case SNDRV_PCM_FORMAT_U8:
+	case SNDRV_PCM_FORMAT_S16_LE:
+		bclkfs = 32;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		bclkfs = 64;
+		break;
+	default:
+		pr_err("%s: Failed not support format\n", __func__);
+		return -EINVAL;
+	}
+
+	i2sclkcnt = ioread32(i2s_data->iobase + I2SCLKCNT0_OFFSET + 0x10 * ch);
+	i2sclkcnt |= bclkfs << I2SCLKCNT_BCLKFS_OFFSET;
+	i2sclkcnt |= mclkfs << I2SCLKCNT_MCLKFS_OFFSET;
+	iowrite32(i2sclkcnt, iobase + I2SCLKCNT0_OFFSET + 0x10 * ch);
+
+	snd_soc_dai_set_dma_data(dai, substream, &dmadata[ch]);
+
+	return 0;
+}
+
+static int ml7213i2s_dai_set_dai_fmt(struct snd_soc_dai *dai,
+				     unsigned int fmt)
+{
+	u32 cmn_reg[MAX_I2S_CH];
+	u32 tx_reg[MAX_I2S_CH];
+	u32 rx_reg[MAX_I2S_CH];
+	int i;
+	int offset = 0;
+	void *iobase = i2s_data->iobase;
+
+	/* set master/slave audio interface */
+	for (i = 0; i < MAX_I2S_CH; i++, offset = i * 0x800) {
+		cmn_reg[i] = ioread32(iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+		tx_reg[i] = ioread32(iobase + offset + I2SCNTTX_OFFSET);
+		rx_reg[i] = ioread32(iobase + offset + I2SCNTRX_OFFSET);
+
+		switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+		case SND_SOC_DAIFMT_CBM_CFM:
+			cmn_reg[i] |= I2SCLKCNT_MSSEL;
+			break;
+		case SND_SOC_DAIFMT_CBS_CFS:
+			cmn_reg[i] &= ~I2SCLKCNT_MSSEL;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		/* interface format */
+		switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+		case SND_SOC_DAIFMT_I2S:
+			cmn_reg[i] |= ML7213I2S_LRCLK_FMT_I2S;
+			tx_reg[i] &= ~ML7213I2S_TX_I2S | ~ML7213I2S_TX_DLY |\
+				  ~ML7213I2S_TX_MSB_LSB |\
+				  ~ML7213I2S_TX_LR_POL | ~ML7213I2S_TX_AFT;
+			rx_reg[i] &= ~ML7213I2S_RX_I2S | ~ML7213I2S_RX_DLY |\
+				  ~ML7213I2S_RX_MSB_LSB |\
+				  ~ML7213I2S_RX_LR_POL | ~ML7213I2S_RX_AFT;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		/* clock inversion */
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			cmn_reg[i] &= ~ML7213I2S_BCLKPOL;
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			cmn_reg[i] |= ML7213I2S_BCLKPOL;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		iowrite32(cmn_reg[i], iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+		iowrite32(tx_reg[i], iobase + offset + I2SCNTTX_OFFSET);
+		iowrite32(rx_reg[i], iobase + offset + I2SCNTRX_OFFSET);
+	}
+
+	return 0;
+}
+
+static int ml7213i2s_dai_set_dai_sysclk(struct snd_soc_dai *dai,
+					int clk_id, unsigned int freq, int dir)
+{
+	u32 reg[MAX_I2S_CH];
+	void *iobase = i2s_data->iobase;
+	int i;
+
+	for (i = 0; i < MAX_I2S_CH; i++) {
+		reg[i] = ioread32(iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+		if (clk_id == IOH_MASTERCLKSEL_MCLK)
+			reg[i] &= ~ML7213I2S_MASTER_CLK_SEL;
+		else if (clk_id == IOH_MASTERCLKSEL_MLBCLK)
+			reg[i] |= ML7213I2S_MASTER_CLK_SEL;
+		iowrite32(reg[i], iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+	}
+
+	return 0;
+}
+
+static int ml7213i2s_dai_set_channel_map(struct snd_soc_dai *dai,
+		unsigned int tx_num, unsigned int *tx_slot,
+		unsigned int rx_num, unsigned int *rx_slot)
+{
+	int i;
+	unsigned int slot;
+	unsigned int tx_mapped = 0, rx_mapped = 0;
+
+	if ((tx_num > MAX_I2S_CH) || (rx_num > MAX_I2S_CH))
+		return -EINVAL;
+
+	for (i = 0; i < tx_num; i++) {
+		slot = tx_slot[i];
+		if ((slot < MAX_I2S_CH) &&
+				(!(tx_mapped & (1 << slot)))) {
+			dmadata[i].mapped_tx_ch = slot;
+			tx_mapped |= 1 << slot;
+		} else
+			return -EINVAL;
+	}
+
+	for (i = 0; i < rx_num; i++) {
+		slot = rx_slot[i];
+		if ((slot < MAX_I2S_CH) &&
+				(!(rx_mapped & (1 << slot)))) {
+			dmadata[i].mapped_rx_ch = slot;
+			rx_mapped |= 1 << slot;
+		} else
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int ml7213i2s_soc_suspend(struct snd_soc_dai *cpu_dai)
+{
+	int i;
+	void *iobase;
+	struct ioh_i2s_pm_ch_reg *save;
+	struct ioh_i2s_pm_ch_reg_cmn *save_cmn;
+	int offset;
+
+	iobase = i2s_data->iobase;
+	for (i = 0, offset = 0; i < MAX_I2S_CH; i++, offset = i * 0x800) {
+		save = &i2s_data->ch_reg_save[i];
+
+		save->i2sdrtx = ioread32(iobase + offset + I2SDRTX_OFFSET);
+		save->i2scnttx = ioread32(iobase + offset + I2SCNTTX_OFFSET);
+		save->i2sfifoctx =
+				ioread32(iobase + offset + I2SFIFOCTX_OFFSET);
+		save->i2saftx = ioread32(iobase + offset + I2SAFTX_OFFSET);
+		save->i2saetx = ioread32(iobase + offset + I2SAETX_OFFSET);
+		save->i2smsktx = ioread32(iobase + offset + I2SMSKTX_OFFSET);
+		save->i2sisttx = ioread32(iobase + offset + I2SISTTX_OFFSET);
+
+		save->i2scntrx = ioread32(iobase + offset + I2SCNTRX_OFFSET);
+		save->i2sfifocrx =
+				ioread32(iobase + offset + I2SFIFOCRX_OFFSET);
+		save->i2safrx = ioread32(iobase + offset + I2SAFRX_OFFSET);
+		save->i2saerx = ioread32(iobase + offset + I2SAERX_OFFSET);
+		save->i2smskrx = ioread32(iobase + offset + I2SMSKRX_OFFSET);
+		save->i2sistrx = ioread32(iobase + offset + I2SISTRX_OFFSET);
+	}
+
+	save_cmn = &i2s_data->cmn_reg_save;
+	for (i = 0; i < MAX_I2S_CH; i++) {
+		save_cmn->i2sclkcnt[i] =
+		ioread32(i2s_data->iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+	}
+	save_cmn->i2simask = ioread32(i2s_data->iobase + I2SIMASK_OFFSET);
+
+	return 0;
+}
+
+static int ml7213i2s_soc_resume(struct snd_soc_dai *cpu_dai)
+{
+	int i;
+	void *iobase;
+	struct ioh_i2s_pm_ch_reg *save;
+	int offset;
+
+	iobase = i2s_data->iobase;
+	save = &i2s_data->ch_reg_save[0];
+	for (i = 0, offset = 0; i < MAX_I2S_CH; i++, offset = i * 0x800) {
+		iowrite32(save->i2sdrtx, iobase + offset + I2SDRTX_OFFSET);
+		iowrite32(save->i2scnttx, iobase + offset + I2SCNTTX_OFFSET);
+		iowrite32(save->i2sfifoctx,
+			  iobase + offset + I2SFIFOCTX_OFFSET);
+		iowrite32(save->i2saftx, iobase + offset + I2SAFTX_OFFSET);
+		iowrite32(save->i2saetx, iobase + offset + I2SAETX_OFFSET);
+		iowrite32(save->i2smsktx, iobase + offset + I2SMSKTX_OFFSET);
+		iowrite32(save->i2sisttx, iobase + offset + I2SISTTX_OFFSET);
+
+		iowrite32(save->i2scntrx, iobase + offset + I2SCNTRX_OFFSET);
+		iowrite32(save->i2sfifocrx,
+			  iobase + offset + I2SFIFOCRX_OFFSET);
+		iowrite32(save->i2safrx, iobase + offset + I2SAFRX_OFFSET);
+		iowrite32(save->i2saerx, iobase + offset + I2SAERX_OFFSET);
+		iowrite32(save->i2smskrx, iobase + offset + I2SMSKRX_OFFSET);
+		iowrite32(save->i2sistrx, iobase + offset + I2SISTRX_OFFSET);
+	}
+
+	for (i = 0; i < MAX_I2S_CH; i++) {
+		iowrite32(i2s_data->cmn_reg_save.i2sclkcnt[i],
+			 i2s_data->iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+	}
+
+	iowrite32(i2s_data->cmn_reg_save.i2simask,
+		  i2s_data->iobase + I2SIMASK_OFFSET);
+
+	return 0;
+}
+#else
+#define ml7213i2s_soc_suspend NULL
+#define ml7213i2s_soc_resume NULL
+#endif
+
+static const struct snd_soc_dai_ops ml7213i2s_dai_ops = {
+	.hw_params	= ml7213i2s_dai_hw_params,
+	.set_fmt	= ml7213i2s_dai_set_dai_fmt,
+	.set_sysclk	= ml7213i2s_dai_set_dai_sysclk,
+	.set_channel_map	= ml7213i2s_dai_set_channel_map,
+};
+
+static struct snd_soc_dai_driver ml7213i2s_dai_data = {
+	.playback = {
+		.channels_min = USE_CHANNELS_MIN,
+		.channels_max = USE_CHANNELS_MAX,
+		.rates = ML7213_I2S_RATES,
+		.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |\
+			   SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.capture = {
+		.channels_min = USE_CHANNELS_MIN,
+		.channels_max = USE_CHANNELS_MAX,
+		.rates = ML7213_I2S_RATES,
+		.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |\
+			   SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.ops = &ml7213i2s_dai_ops,
+	.suspend = ml7213i2s_soc_suspend,
+	.resume = ml7213i2s_soc_resume,
+};
+
+/* PCI functions */
+DEFINE_PCI_DEVICE_TABLE(ioh_pci_tbl) = {
+	{
+		.vendor = PCI_VENDOR_ID_ROHM,
+		.device = PCI_DEVICE_ID_ML7213_I2S,
+		.subvendor = PCI_ANY_ID,
+		.subdevice = PCI_ANY_ID,
+	},
+	{0,}
+};
+
+static int ioh_i2s_pci_probe(struct pci_dev *pdev,
+			     const struct pci_device_id *id)
+{
+	int rv = 0;
+	void __iomem *tbl;
+	unsigned int mapbase;
+	int i;
+
+	rv = pci_enable_device(pdev);
+	if (rv)
+		goto enable_device;
+
+	tbl = pci_iomap(pdev, 1, 0);
+	if (!tbl) {
+		rv = -ENOMEM;
+		printk(KERN_ERR "pci_iomap failed\n");
+		goto out_ipmap;
+	}
+
+	mapbase = pci_resource_start(pdev, 1);
+	if (!mapbase) {
+		rv = -ENOMEM;
+		printk(KERN_ERR "pci_resource_start failed\n");
+		goto out_pci_resource;
+	}
+
+	i2s_data = devm_kzalloc(&pdev->dev, sizeof(*i2s_data), GFP_KERNEL);
+	if (!i2s_data) {
+		dev_err(&pdev->dev, "Can't allocate i2s_data\n");
+		rv = -ENOMEM;
+		goto out_kzalloc_data;
+	}
+
+	dmadata = devm_kzalloc(&pdev->dev,
+			       sizeof(*dmadata) * MAX_I2S_CH, GFP_KERNEL);
+	if (!dmadata) {
+		dev_err(&pdev->dev, "Can't allocate dmadata\n");
+		rv = -ENOMEM;
+		goto out_kzalloc_dma;
+	}
+
+	i2s_data->dev = &pdev->dev;
+	i2s_data->pdev = pdev;
+	i2s_data->iobase = tbl;
+	i2s_data->mapbase = mapbase;
+	spin_lock_init(&i2s_data->tx_lock);
+
+	dev_set_name(&pdev->dev, "%s", "ml7213ioh");
+	rv = snd_soc_register_dai(&pdev->dev, &ml7213i2s_dai_data);
+	if (rv < 0) {
+		printk(KERN_ERR "Failed to snd_soc_register_dai\n");
+		goto out_register_dai;
+	}
+
+	dev_set_name(&pdev->dev, "%s", "ml7213-i2s-audio");
+	rv = snd_soc_register_platform(&pdev->dev, &ml7213ioh_soc_platform);
+	if (rv < 0) {
+		printk(KERN_ERR "Failed to snd_soc_register_platform\n");
+		goto out_soc_register_plat;
+	}
+
+	for (i = 0; i < MAX_I2S_CH; i++)
+		dmadata[i].number = i;
+
+	return 0;
+
+out_soc_register_plat:
+	snd_soc_unregister_dai(&pdev->dev);
+out_register_dai:
+out_kzalloc_dma:
+out_kzalloc_data:
+out_pci_resource:
+	pci_iounmap(pdev, i2s_data->iobase);
+out_ipmap:
+	pci_disable_device(pdev);
+enable_device:
+
+	return rv;
+}
+
+static void ioh_i2s_pci_remove(struct pci_dev *pdev)
+{
+	int i;
+
+	for (i = 0; i < MAX_I2S_CH; i++)
+		ioh_i2s_reset(i);
+
+	snd_soc_unregister_platform(&pdev->dev);
+	snd_soc_unregister_dai(&pdev->dev);
+	pci_iounmap(pdev, i2s_data->iobase);
+	pci_disable_device(pdev);
+}
+
+static int ioh_i2s_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	int ret;
+
+	ret = pci_save_state(pdev);
+	if (ret) {
+		dev_err(&pdev->dev,
+			" %s -pci_save_state returns %d\n", __func__, ret);
+		return ret;
+	}
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	pci_disable_device(pdev);
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+	return 0;
+}
+
+static int ioh_i2s_pci_resume(struct pci_dev *pdev)
+{
+	int ret;
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_err(&pdev->dev,
+		"%s-pci_enable_device failed(ret=%d) ", __func__, ret);
+		return ret;
+	}
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+
+	return 0;
+}
+
+static struct pci_driver ioh_i2s_driver = {
+	.name = DRV_NAME,
+	.probe = ioh_i2s_pci_probe,
+	.remove = __devexit_p(ioh_i2s_pci_remove),
+	.id_table = ioh_pci_tbl,
+#ifdef CONFIG_PM
+	.suspend = ioh_i2s_pci_suspend,
+	.resume = ioh_i2s_pci_resume,
+#endif
+};
+
+static int __init ioh_plat_init(void)
+{
+	return pci_register_driver(&ioh_i2s_driver);
+}
+
+static void __exit ioh_i2s_cleanup(void)
+{
+	pci_unregister_driver(&ioh_i2s_driver);
+}
+
+module_init(ioh_plat_init);
+module_exit(ioh_i2s_cleanup);
+
+MODULE_AUTHOR("Tomoya MORINAGA <tomoya.rohm@gmail.com>");
+MODULE_DESCRIPTION("LAPIS Semiconductor ML7213 IOH ALSA SoC platform driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/lapis/ml7213ioh-plat.h b/sound/soc/lapis/ml7213ioh-plat.h
new file mode 100644
index 0000000..e7fe65a
--- /dev/null
+++ b/sound/soc/lapis/ml7213ioh-plat.h
@@ -0,0 +1,376 @@
+/*
+ * 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.
+ */
+
+#ifndef ML7213IOH_PLAT_H
+#define ML7213IOH_PLAT_H
+
+#include <linux/interrupt.h>
+#include <linux/pch_dma.h>
+
+#define DRV_NAME "ml7213ioh-i2s-pci"
+#define PCI_VENDOR_ID_ROHM	0X10DB
+#define PCI_DEVICE_ID_ML7213_I2S	0X8033
+
+#define I2SCLKCNT_MSSEL		BIT(0)
+#define ML7213I2S_BCLKPOL	BIT(1)
+#define ML7213I2S_LRCLK_FMT	(BIT(4) | BIT(5))
+#define ML7213I2S_LRCLK_FMT_I2S	BIT(4)
+
+#define ML7213I2S_TX_I2S	BIT(0)
+#define ML7213I2S_TX_DLY	BIT(12)
+#define ML7213I2S_TX_MSB_LSB	BIT(13)
+#define ML7213I2S_TX_LR_POL	BIT(14)
+#define ML7213I2S_TX_AFT	BIT(15)
+#define ML7213I2S_RX_I2S	BIT(0)
+#define ML7213I2S_RX_DLY	BIT(12)
+#define ML7213I2S_RX_MSB_LSB	BIT(13)
+#define ML7213I2S_RX_LR_POL	BIT(14)
+#define ML7213I2S_RX_AFT	BIT(15)
+#define ML7213I2S_MASTER_CLK_SEL	BIT(2)
+
+#define ML7213_I2S_RATES \
+	(SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000)
+
+/* ioh_bclkfs_t */
+#define	IOH_BCLKFS_8FS		0
+#define	IOH_BCLKFS_16FS		1
+#define	IOH_BCLKFS_32FS		2
+#define	IOH_BCLKFS_64FS		3
+
+#define	I2SCLKCNT_MCLKFS_OFFSET		(8)
+#define	I2SCLKCNT_BCLKFS_OFFSET		(12)
+
+#define I2SCLKCNT0_OFFSET	0x3000
+#define I2SCLKCNT1_OFFSET	0x3010
+#define I2SCLKCNT2_OFFSET	0x3020
+#define I2SCLKCNT3_OFFSET	0x3030
+#define I2SCLKCNT4_OFFSET	0x3040
+#define I2SCLKCNT5_OFFSET	0x3050
+#define I2SISTATUS_OFFSET	0x3080
+#define I2SIDISP_OFFSET		0x3084
+#define I2SIMASK_OFFSET		0x3088
+#define I2SIMASKCLR_OFFSET	0x308C
+#define I2SSRST_OFFSET		0x3FFC
+#define I2SDRTX_OFFSET		0x0
+#define I2SCNTTX_OFFSET		0x4
+#define I2SFIFOCTX_OFFSET	0x8
+#define I2SAFTX_OFFSET		0xC
+#define I2SAETX_OFFSET		0x10
+#define I2SMSKTX_OFFSET		0x14
+#define I2SISTTX_OFFSET		0x18
+#define I2SMONTX_OFFSET		0x1C
+#define I2SDRRX_OFFSET		0x20
+#define I2SCNTRX_OFFSET		0x24
+#define I2SFIFOCRX_OFFSET	0x28
+#define I2SAFRX_OFFSET		0x2C
+#define I2SAERX_OFFSET		0x30
+#define I2SMSKRX_OFFSET		0x34
+#define I2SISTRX_OFFSET		0x38
+#define I2SMONRX_OFFSET		0x3C
+#define FIRST_TX_OFFSET		0x0
+#define FIRST_RX_OFFSET		0x0
+
+#define I2SDRTXMIRROR_OFFSET	0x100
+#define I2SDRRXMIRROR_OFFSET	0x400
+
+#define I2S_ALL_INTERRUPT_BITS	0x3F003F
+#define I2S_IDISP_BITS		0x3F003F
+#define I2S_IDISP_TX_BITS	0x00003F
+#define I2S_IDISP_RX_BITS	0x3F0000
+#define TX_BIT_FIMSK	0x1	/*Fifo full interrupt mask bit*/
+#define TX_BIT_AFIMSK	0x2	/*Fifo Almost full interrupt mask bit*/
+#define TX_BIT_EIMSK	0x4	/*Fifo empty interrupt mask bit*/
+#define TX_BIT_AEIMSK	0x8	/*Fifo Almost empty interrupt mask bit*/
+#define TX_BIT_DMAMSK	0x10	/*Masks DMA*/
+#define TX_BIT_DMATC	0x100
+#define I2S_TX_ALL_INTR_MASK_BITS (TX_BIT_FIMSK | TX_BIT_AFIMSK | TX_BIT_EIMSK \
+							| TX_BIT_AEIMSK)
+#define I2S_TX_NORMAL_INTR_MASK_BITS (TX_BIT_FIMSK | TX_BIT_AFIMSK)
+#define RX_BIT_FIMSK	0x1	/*Fifo full interrupt mask bit*/
+#define RX_BIT_AFIMSK	0x2	/*Fifo Almost full interrupt mask bit*/
+#define RX_BIT_EIMSK	0x4	/*Fifo empty interrupt mask bit*/
+#define RX_BIT_AEIMSK	0x8	/*Fifo Almost empty interrupt mask bit*/
+#define RX_BIT_DMAMSK	0x10	/*Masks DMA*/
+#define RX_BIT_DMATC	0x100
+#define I2S_RX_ALL_INTR_MASK_BITS (RX_BIT_FIMSK | RX_BIT_AFIMSK | RX_BIT_EIMSK \
+							| RX_BIT_AEIMSK)
+#define I2S_RX_NORMAL_INTR_MASK_BITS (RX_BIT_EIMSK | RX_BIT_AEIMSK)
+#define I2S_TX_FINT	0x1	/*Full Interrupt*/
+#define I2S_TX_AFINT	0x2	/*Almost full interrupt*/
+#define I2S_TX_EINT	0x4	/*Empty interrupt*/
+#define I2S_TX_AEINT	0x8	/*Almost empty interrupt*/
+#define I2S_RX_FINT	0x1	/*Full Interrupt*/
+#define I2S_RX_AFINT	0x2	/*Almost full interrupt*/
+#define I2S_RX_EINT	0x4	/*Empty interrupt*/
+#define I2S_RX_AEINT	0x8	/*Almost empty interrupt*/
+
+#define I2S_FIFO_TX_FCLR	BIT(0)
+#define I2S_FIFO_TX_RUN		BIT(4)
+#define I2S_FIFO_RX_FCLR	BIT(0)
+#define I2S_FIFO_RX_RUN		BIT(4)
+
+#define FIFO_CTRL_BIT_TX_RUN	0x10
+#define FIFO_CTRL_BIT_RX_RUN	0x10
+#define I2S_CNT_BIT_TEL		0x1
+#define I2S_IMASK_TX_BIT_START	0
+#define I2S_IMASK_RX_BIT_START	16
+
+/* DMA processing */
+#define	PERIOD_POS_MAX		I2S_DMA_SG_NUM
+#define	PERIOD_LEN		(I2S_AFULL_THRESH * PERIOD_POS_MAX)
+
+#define	SUPPORT_FORMAT		(SNDRV_PCM_FMTBIT_U8 | \
+				 SNDRV_PCM_FMTBIT_S16_LE | \
+				 SNDRV_PCM_FMTBIT_S32_LE)
+#define	MAX_PERIOD_SIZE		(PERIOD_LEN * 4)
+
+#define USE_CHANNELS_MIN	1
+#define USE_CHANNELS_MAX	2
+#define	MAX_I2S_CH		6		/*I2S0 ~ I2S5*/
+#define USE_PERIODS_MIN		(I2S_DMA_SG_MAX)
+#define USE_PERIODS_MAX		(I2S_DMA_SG_MAX)
+
+#define I2S_AEMPTY_THRESH	64	/* Almost  Empty Threshold */
+#define I2S_AFULL_THRESH	64	/* Almost  Full Threshold */
+
+#define	I2S_DMA_SG_NUM		(128)
+#define	I2S_DMA_SG_MAX		(64)
+
+#define	IOH_MSSEL_MASTER	1
+
+#define ML7213_I2SAERX_DEFAULT	0x1f
+#define ML7213_I2SMSKRX_DEFAULT	0x1f
+#define ML7213_I2SISTRX_DEFAULT	0xC
+
+#define ML7213_I2SMSKTX_DEFAULT	0x1f
+#define ML7213_I2SISTTX_DEFAULT	0xC
+
+enum ioh_i2s_fifo_type {
+	IOH_FIFO_32 = 4,
+	IOH_FIFO_16 = 2,
+	IOH_FIFO_8 = 1,
+};
+
+enum ioh_i2s_status {
+	IOH_EOK = 0,
+	IOH_EDONE = 1,
+	IOH_EUNDERRUN = 2,
+	IOH_EOVERRUN = 3,
+	IOH_EFRAMESYNC = 4,
+};
+
+enum ioh_bclkpol_t {
+	ioh_BCLKPOL_FALLING = 0,
+	ioh_BCLKPOL_RISING,
+};
+
+enum ioh_masterclksel_t {
+	IOH_MASTERCLKSEL_MCLK = 0,
+	IOH_MASTERCLKSEL_MLBCLK,
+};
+
+enum ioh_lrckfmt_t {
+	IOH_LRCLKFMT_I2S = 1,
+	IOH_LRCLKFMT_LONGFRAME,
+	IOH_LRCLKFMT_SHORTFRAME,
+};
+
+enum ioh_mclkfs_t {
+	IOH_MCLKFS_64FS = 0,
+	IOH_MCLKFS_128FS,
+	IOH_MCLKFS_192FS,
+	IOH_MCLKFS_256FS,
+	IOH_MCLKFS_384FS,
+	IOH_MCLKFS_512FS,
+	IOH_MCLKFS_768FS,
+	IOH_MCLKFS_1024FS,
+};
+
+enum ioh_dlyoff_t {
+	IOH_DLYOFF_DLY_ON = 0,		/* date delat on */
+	IOH_DLYOFF_DLY_OFF,		/* date delat off */
+};
+
+enum ioh_lrpol_t {
+	IOH_LRPOL_NO_INVERT = 0,	/* Low of LRCLK is L data.
+					   High of LRCLK is R data. */
+	IOH_LRPOL_INVERT,		/* Low of LRCLK is R data.
+					   High of LRCLK is L data. */
+};
+
+enum ioh_aft_t {
+	IOH_AFR_FRONT = 0,
+	IOH_AFR_BACK,
+};
+
+struct ml7213i2s_runtime_data {
+	spinlock_t lock;
+	int tx_stop;
+	int rx_stop;
+	struct ioh_i2s_dma *rx_dma;
+	struct ioh_i2s_dma *tx_dma;
+};
+
+struct ioh_i2s_pm_ch_reg {
+	u32 i2sdrtx;	/* Tx: data register */
+	u32 i2scnttx; /* Tx: control register */
+	u32 i2sfifoctx;	/* Tx: FIFO control register */
+	u32 i2saftx;	/* Tx: almost full threshold setting */
+	u32 i2saetx;	/* Tx: almost empty threshold setting */
+	u32 i2smsktx;	/* Tx: interrupt mask settings */
+	u32 i2sisttx;	/* Tx: for acknowledging interrupts */
+	u32 i2scntrx;	/* Rx: control register */
+	u32 i2sfifocrx; /* Rx: FIFO control register */
+	u32 i2safrx;	/* Rx: almost full threshold setting */
+	u32 i2saerx;	/* Rx: almost empty threshold setting */
+	u32 i2smskrx;	/* Rx: interrupt mask settings */
+	u32 i2sistrx;	/* Rx: for acknowledging interrupts */
+};
+
+struct ioh_i2s_pm_ch_reg_cmn {
+	u32 i2sclkcnt[MAX_I2S_CH];	/*clock control register(ch0~5) */
+	u32 i2simask;		/*interrupt mask */
+};
+
+struct ioh_i2s_data {
+	struct device *dev;
+	void *iobase;
+	unsigned int mapbase;
+	spinlock_t tx_lock;
+	struct ioh_i2s_pm_ch_reg_cmn cmn_reg_save;
+	struct ioh_i2s_pm_ch_reg ch_reg_save[MAX_I2S_CH];
+	struct pci_dev *pdev;
+};
+
+struct ioh_i2s_dma {
+	/* Transmit side DMA */
+	struct scatterlist	*sg_tx_p;
+	struct scatterlist	*sg_rx_p;
+
+	int tx_num;	/* The number of sent sg */
+	int rx_num;	/* The number of sent sg */
+
+	struct dma_chan			*chan_tx;
+	struct dma_chan			*chan_rx;
+
+	int rx_nent;	/* The number of rx scatter list */
+	int tx_nent;	/* The number of tx scatter list */
+
+	struct dma_async_tx_descriptor	*desc_tx;
+	struct dma_async_tx_descriptor	*desc_rx;
+
+	dma_addr_t			tx_buf_dma;
+	dma_addr_t			rx_buf_dma;
+
+	struct pch_dma_slave		param_tx;
+	struct pch_dma_slave		param_rx;
+
+	int dma_tx_unit; /* 1Byte of 2Byte or 4Byte */
+	int dma_rx_unit; /* 1Byte of 2Byte or 4Byte */
+	int dma_tx_width;
+	int dma_rx_width;
+
+	struct snd_pcm_substream *tx_substream;
+	struct snd_pcm_substream *rx_substream;
+
+	int number;
+
+	int mapped_tx_ch;
+	int mapped_rx_ch;
+
+	unsigned char *rx_dma_addr;
+	dma_addr_t rx_physical_addr;
+	unsigned long rx_buffer_bytes;
+	unsigned long rx_period_bytes;
+
+	unsigned char *tx_dma_addr;
+	dma_addr_t tx_physical_addr;
+	unsigned long tx_buffer_bytes;
+	unsigned long tx_period_bytes;
+
+
+	int rx_buf_frags;
+	int tx_buf_frags;
+
+	int tx_cur_period;
+	int rx_cur_period;
+};
+
+struct ml7213i2s_dai {
+	struct snd_soc_dai_driver dai;
+	struct device	*dev;
+	void *iobase;
+	u32 freq;
+};
+
+struct ioh_i2s_config_common_reg {
+	u32 i2sclkcnt;	/*clock control register(ch0~5) */
+	u32 i2sistatus;	/*interrupt status */
+	u32 i2sidisp;		/*active interrupts */
+	u32 i2simask;		/*interrupt mask */
+	u32 i2simaskclr;	/*interrupt mask clear */
+};
+
+struct ioh_i2s_config_tx_reg {
+	u32 i2sdrtx;	/*data register */
+	u32 i2scnttx; /*control register */
+	u32 i2sfifoctx;	/*FIFO control register */
+	u32 i2saftx;	/*almost full threshold setting */
+	u32 i2saetx;	/*almost empty threshold setting */
+	u32 i2smsktx;	/*interrupt mask settings */
+	u32 i2sisttx;	/*for acknowledging interrupts */
+	u32 i2smontx;	/*monitor register */
+};
+
+struct ioh_i2s_config_rx_reg {
+	u32 i2sdrrx;	/* data register */
+	u32 i2scntrx;	/* control register */
+	u32 i2sfifocrx;/* FIFO control register */
+	u32 i2safrx;	/* almost full threshold setting */
+	u32 i2saerx;	/* almost empty threshold setting */
+	u32 i2smskrx;	/* interrupt mask settings */
+	u32 i2sistrx;	/* for acknowledging interrupts */
+	u32 i2smonrx;	/* monitor register */
+};
+
+struct ioh_i2s_config_reg {
+	/* The common register settings */
+	struct ioh_i2s_config_common_reg cmn;
+
+	/* TX channel settings */
+	struct ioh_i2s_config_tx_reg tx;
+
+	/* RX channel settings */
+	struct ioh_i2s_config_rx_reg rx;
+};
+
+static struct snd_pcm_hardware ml7213i2s_pcm_hw = {
+	.info =			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SUPPORT_FORMAT,
+	.channels_min =	1,
+	.channels_max =	2,
+	.buffer_bytes_max =	64 * 128 * 4 * 64,
+	.period_bytes_min =	64 * 4,
+	.period_bytes_max =	64 * 128 * 4,
+	.periods_min =	2,
+	.periods_max =	64,
+	.fifo_size =		0,
+};
+#endif
-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* Re: [PATCH v7] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-09  6:37                       ` [PATCH v7] " Tomoya MORINAGA
@ 2012-03-14 14:39                         ` Mark Brown
  2012-03-15  4:51                           ` [alsa-devel] " Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-14 14:39 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 2066 bytes --]

On Fri, Mar 09, 2012 at 03:37:06PM +0900, Tomoya MORINAGA wrote:

A substantial improvement on previous versions but there's still a few
issues, though they're now fairly small.

> +	/* SAI configuration */
> +	if (priv->channels == 1) {
> +		ml26124_update_bits(codec, ML26124_SAI_TRANS_CTL, mask,
> +				ML26124_SAI_NO_DELAY | ML26124_SAI_FRAME_SYNC);
> +		ml26124_update_bits(codec, ML26124_SAI_RCV_CTL, mask,
> +				ML26124_SAI_NO_DELAY | ML26124_SAI_FRAME_SYNC);
> +	} else {
> +		ml26124_update_bits(codec, ML26124_SAI_TRANS_CTL, mask,
> +				    ML26124_SAI_NO_DELAY);
> +		ml26124_update_bits(codec, ML26124_SAI_RCV_CTL, mask,
> +				    ML26124_SAI_NO_DELAY);
> +	}

What happens if the system switches between 1 channel mode and
multi-channel mode at runtime?  It looks like _SYNC will never be
cleared.

> +static int ml26124_set_dai_sysclk(struct snd_soc_dai *codec_dai,
> +		int clk_id, unsigned int freq, int dir)
> +{
> +	struct snd_soc_codec *codec = codec_dai->codec;
> +	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
> +
> +	switch (clk_id) {
> +	case ML26124_USE_PLL:
> +		ml26124_update_bits(codec, ML26124_CLK_CTL,
> +				    BIT(0) | BIT(1), 0);
> +		break;
> +	case ML26124_USE_MCLKI_256FS:
> +		ml26124_update_bits(codec, ML26124_CLK_CTL,
> +				    BIT(0) | BIT(1), 1);
> +		break;
> +	case ML26124_USE_MCLKI_512FS:
> +		ml26124_update_bits(codec, ML26124_CLK_CTL,
> +				    BIT(0) | BIT(1), 2);
> +		break;
> +	case ML26124_USE_MCLKI_1024FS:
> +		ml26124_update_bits(codec, ML26124_CLK_CTL,
> +				    BIT(0) | BIT(1), 3);
> +		break;

Why not just specify the MCLK rate and then calculate the division based
on the sample rate?

> +	switch (freq) {
> +	case 12288000:
> +		priv->mclk = freq;
> +		break;

This is especially true given that only one MCLK rate is supported.

> +	case SND_SOC_BIAS_STANDBY:
> +		/* VMID ON */
> +		ml26124_update_bits(codec, ML26124_PW_REF_PW_MNG,
> +				    ML26124_VMID, ML26124_VMID);
> +		msleep(500);

This will sleep for 500ms when powering down which probably isn't what's
desired...

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v6] sound/soc/lapis: add machine driver for ML7213 Carrier Board
  2012-03-09  6:38           ` [PATCH v6] " Tomoya MORINAGA
@ 2012-03-14 14:45             ` Mark Brown
  2012-03-15  4:50               ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-14 14:45 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 641 bytes --]

On Fri, Mar 09, 2012 at 03:38:11PM +0900, Tomoya MORINAGA wrote:

> +	/* set codec DAI configuration */
> +	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
> +				 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* set cpu DAI configuration */
> +	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
> +				 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);

This looks very broken, you're setting the two ends of the link to
different configurations.  If this is needed it should be documented why.

Also, this should be set in the dai_link structure as data rather than
set each time.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-08  2:24                   ` Tomoya MORINAGA
  2012-03-08 11:47                     ` Mark Brown
@ 2012-03-14 17:40                     ` Lars-Peter Clausen
  2012-03-14 17:45                       ` Mark Brown
  2012-03-15  6:29                       ` Tomoya MORINAGA
  1 sibling, 2 replies; 70+ messages in thread
From: Lars-Peter Clausen @ 2012-03-14 17:40 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Mark Brown, alsa-devel, qi.wang, Takashi Iwai, linux-kernel,
	yong.y.wang, kok.howg.ewe, Liam Girdwood, joel.clark

On 03/08/2012 03:24 AM, Tomoya MORINAGA wrote:
> On Wed, Mar 7, 2012 at 8:48 PM, Mark Brown
> <broonie@opensource.wolfsonmicro.com> wrote:
>>> If possible, I want to use framework features. However, I judged it is
>>> not possible.
>>> Let me confirm again.
>>> As I said before, all ML26124 registers need to be accessed by
>>> different address when read and write.
>>> Like below
>>> R       W
>>> $00h $01h Sampling setting
>>> $02h $03h PLL setting
>>> ...
>>> Even so, can I use framework features ?
>> No, in which case (to repeat what I said previously) you need to make
>> the code *much* clearer so the reader can understand what it is doing
>> and why it's having to open code this stuff.
> 
> 
> thanks.

If this is for all registers, how does stuff like DAPM work, which uses the
framework internal update_bits function?

If it's the case that the write address is always read address + 1 you can
still use the framework if you set write_flag_mask to 0x01 in your regmap
config. In my opinion it even makes sense to shift all the register numbers
to the left by one and use a 7/9 instead of a 8/8 addr/reg layout. This will
reduce the amount of memory wasted due to holes in the register cache.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-14 17:40                     ` [alsa-devel] [PATCH v6] " Lars-Peter Clausen
@ 2012-03-14 17:45                       ` Mark Brown
  2012-03-15  6:29                       ` Tomoya MORINAGA
  1 sibling, 0 replies; 70+ messages in thread
From: Mark Brown @ 2012-03-14 17:45 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Tomoya MORINAGA, alsa-devel, qi.wang, Takashi Iwai, linux-kernel,
	yong.y.wang, kok.howg.ewe, Liam Girdwood, joel.clark

[-- Attachment #1: Type: text/plain, Size: 592 bytes --]

On Wed, Mar 14, 2012 at 06:40:20PM +0100, Lars-Peter Clausen wrote:

> If this is for all registers, how does stuff like DAPM work, which uses the
> framework internal update_bits function?

I was assuming that this was just for specific registers which were
weird (frankly I've no idea what the chip designers were thinking if
this is actually needed).

Though based on some of the issues I found on previous versions there's
every chance that there's not been any testing...  if that's the case
it's possible this is a misunderstanding of the I2C read/write handling
with the chip address?

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v6] sound/soc/lapis: add machine driver for ML7213 Carrier Board
  2012-03-14 14:45             ` Mark Brown
@ 2012-03-15  4:50               ` Tomoya MORINAGA
  2012-03-15 10:50                 ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-15  4:50 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Wed, Mar 14, 2012 at 11:45 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Fri, Mar 09, 2012 at 03:38:11PM +0900, Tomoya MORINAGA wrote:
>
>> +     /* set codec DAI configuration */
>> +     ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
>> +                              SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     /* set cpu DAI configuration */
>> +     ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
>> +                              SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
>
> This looks very broken, you're setting the two ends of the link to
> different configurations.  If this is needed it should be documented why.
>
> Also, this should be set in the dai_link structure as data rather than
> set each time.

OK.
codec is slave, i2s controller is master.
So, I'll use SND_SOC_DAIFMT_CBS_CFM.

thanks.
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v7] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-14 14:39                         ` Mark Brown
@ 2012-03-15  4:51                           ` Tomoya MORINAGA
  0 siblings, 0 replies; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-15  4:51 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, qi.wang, Takashi Iwai, linux-kernel, yong.y.wang,
	kok.howg.ewe, Liam Girdwood, joel.clark

On Wed, Mar 14, 2012 at 11:39 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Fri, Mar 09, 2012 at 03:37:06PM +0900, Tomoya MORINAGA wrote:
>> +     /* SAI configuration */
>> +     if (priv->channels == 1) {
>> +             ml26124_update_bits(codec, ML26124_SAI_TRANS_CTL, mask,
>> +                             ML26124_SAI_NO_DELAY | ML26124_SAI_FRAME_SYNC);
>> +             ml26124_update_bits(codec, ML26124_SAI_RCV_CTL, mask,
>> +                             ML26124_SAI_NO_DELAY | ML26124_SAI_FRAME_SYNC);
>> +     } else {
>> +             ml26124_update_bits(codec, ML26124_SAI_TRANS_CTL, mask,
>> +                                 ML26124_SAI_NO_DELAY);
>> +             ml26124_update_bits(codec, ML26124_SAI_RCV_CTL, mask,
>> +                                 ML26124_SAI_NO_DELAY);
>> +     }
>
> What happens if the system switches between 1 channel mode and
> multi-channel mode at runtime?  It looks like _SYNC will never be
> cleared.

Sorry,  'priv->channels == 1' means for telephone codec.
However, we don't support this function.
So, I'll delete these lines.

>> +static int ml26124_set_dai_sysclk(struct snd_soc_dai *codec_dai,
>> +             int clk_id, unsigned int freq, int dir)
>> +{
>> +     struct snd_soc_codec *codec = codec_dai->codec;
>> +     struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
>> +
>> +     switch (clk_id) {
>> +     case ML26124_USE_PLL:
>> +             ml26124_update_bits(codec, ML26124_CLK_CTL,
>> +                                 BIT(0) | BIT(1), 0);
>> +             break;
>> +     case ML26124_USE_MCLKI_256FS:
>> +             ml26124_update_bits(codec, ML26124_CLK_CTL,
>> +                                 BIT(0) | BIT(1), 1);
>> +             break;
>> +     case ML26124_USE_MCLKI_512FS:
>> +             ml26124_update_bits(codec, ML26124_CLK_CTL,
>> +                                 BIT(0) | BIT(1), 2);
>> +             break;
>> +     case ML26124_USE_MCLKI_1024FS:
>> +             ml26124_update_bits(codec, ML26124_CLK_CTL,
>> +                                 BIT(0) | BIT(1), 3);
>> +             break;
>
> Why not just specify the MCLK rate and then calculate the division based
> on the sample rate?

OK, I'll do like your saying.

>
>> +     case SND_SOC_BIAS_STANDBY:
>> +             /* VMID ON */
>> +             ml26124_update_bits(codec, ML26124_PW_REF_PW_MNG,
>> +                                 ML26124_VMID, ML26124_VMID);
>> +             msleep(500);
>
> This will sleep for 500ms when powering down which probably isn't what's
> desired...

I'll Move into "if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)" condition.

thanks.
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-14 17:40                     ` [alsa-devel] [PATCH v6] " Lars-Peter Clausen
  2012-03-14 17:45                       ` Mark Brown
@ 2012-03-15  6:29                       ` Tomoya MORINAGA
  2012-03-16  9:55                         ` Tomoya MORINAGA
  1 sibling, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-15  6:29 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Mark Brown, alsa-devel, qi.wang, Takashi Iwai, linux-kernel,
	yong.y.wang, kok.howg.ewe, Liam Girdwood, joel.clark

On Thu, Mar 15, 2012 at 2:40 AM, Lars-Peter Clausen <lars@metafoo.de> wrote:
> If this is for all registers, how does stuff like DAPM work, which uses the
> framework internal update_bits function?
>
> If it's the case that the write address is always read address + 1 you can
> still use the framework if you set write_flag_mask to 0x01 in your regmap
> config. In my opinion it even makes sense to shift all the register numbers
> to the left by one and use a 7/9 instead of a 8/8 addr/reg layout. This will
> reduce the amount of memory wasted due to holes in the register cache.

Thank you for your information.
I've just been investigating these.
I also found ASoC core access i2c driver via snd_soc_update_bits or others.
As a result, write register address is the same as read's.

I'll try to implement these.

BTW, currently, by settting ML26124 codec with fixed value,
I've confirmed both playback and capture work well.

thanks
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v6] sound/soc/lapis: add machine driver for ML7213 Carrier Board
  2012-03-15  4:50               ` Tomoya MORINAGA
@ 2012-03-15 10:50                 ` Mark Brown
  2012-03-16  4:07                   ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-15 10:50 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 247 bytes --]

On Thu, Mar 15, 2012 at 01:50:48PM +0900, Tomoya MORINAGA wrote:

> OK.
> codec is slave, i2s controller is master.
> So, I'll use SND_SOC_DAIFMT_CBS_CFM.

That means "Codec bit clock master frame clock slave".  Are you sure
that's what you want?

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v6] sound/soc/lapis: add machine driver for ML7213 Carrier Board
  2012-03-15 10:50                 ` Mark Brown
@ 2012-03-16  4:07                   ` Tomoya MORINAGA
  2012-03-16 19:06                     ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-16  4:07 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Thu, Mar 15, 2012 at 7:50 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Thu, Mar 15, 2012 at 01:50:48PM +0900, Tomoya MORINAGA wrote:
>
>> OK.
>> codec is slave, i2s controller is master.
>> So, I'll use SND_SOC_DAIFMT_CBS_CFM.
>
> That means "Codec bit clock master frame clock slave".  Are you sure
> that's what you want?

Let me clarify the above.
Does the above "frame clock" mean "data line" of I2S?
If yes, I should have used 'SND_SOC_DAIFMT_CBS_CFS'.

thanks.
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-15  6:29                       ` Tomoya MORINAGA
@ 2012-03-16  9:55                         ` Tomoya MORINAGA
  2012-03-17 21:52                           ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-16  9:55 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Mark Brown, alsa-devel, qi.wang, Takashi Iwai, linux-kernel,
	yong.y.wang, kok.howg.ewe, Liam Girdwood, joel.clark

On Thu, Mar 15, 2012 at 3:29 PM, Tomoya MORINAGA <tomoya.rohm@gmail.com> wrote:
>> If this is for all registers, how does stuff like DAPM work, which uses the
>> framework internal update_bits function?
>>
>> If it's the case that the write address is always read address + 1 you can
>> still use the framework if you set write_flag_mask to 0x01 in your regmap
>> config. In my opinion it even makes sense to shift all the register numbers
>> to the left by one and use a 7/9 instead of a 8/8 addr/reg layout. This will
>> reduce the amount of memory wasted due to holes in the register cache.
>
> Thank you for your information.
> I've just been investigating these.
> I also found ASoC core access i2c driver via snd_soc_update_bits or others.
> As a result, write register address is the same as read's.
>
> I'll try to implement these.
>
> BTW, currently, by settting ML26124 codec with fixed value,
> I've confirmed both playback and capture work well.

Thank you for your help,
almost of ML26124 registers can be set correctly except SUPPLY.
So, I have a question.

When SND_SOC_DAPM_SUPPLY is executed ?
or
Do I need to execute from application (like amixer) ?

thanks in advance.

-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v6] sound/soc/lapis: add machine driver for ML7213 Carrier Board
  2012-03-16  4:07                   ` Tomoya MORINAGA
@ 2012-03-16 19:06                     ` Mark Brown
  2012-03-18 23:45                       ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-16 19:06 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 456 bytes --]

On Fri, Mar 16, 2012 at 01:07:03PM +0900, Tomoya MORINAGA wrote:
> On Thu, Mar 15, 2012 at 7:50 PM, Mark Brown

> > That means "Codec bit clock master frame clock slave".  Are you sure
> > that's what you want?

> Let me clarify the above.
> Does the above "frame clock" mean "data line" of I2S?
> If yes, I should have used 'SND_SOC_DAIFMT_CBS_CFS'.

No, it means the clock which runs at sample rate (usually called the
frame clock or LRCLK).

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-16  9:55                         ` Tomoya MORINAGA
@ 2012-03-17 21:52                           ` Mark Brown
  2012-03-19 11:59                             ` [PATCH v8] " Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-17 21:52 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Lars-Peter Clausen, alsa-devel, qi.wang, Takashi Iwai,
	linux-kernel, yong.y.wang, kok.howg.ewe, Liam Girdwood,
	joel.clark

[-- Attachment #1: Type: text/plain, Size: 241 bytes --]

On Fri, Mar 16, 2012 at 06:55:09PM +0900, Tomoya MORINAGA wrote:

> When SND_SOC_DAPM_SUPPLY is executed ?
> or
> Do I need to execute from application (like amixer) ?

As with all DAPM widgets it is automatically managed by DAPM as needed.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v6] sound/soc/lapis: add machine driver for ML7213 Carrier Board
  2012-03-16 19:06                     ` Mark Brown
@ 2012-03-18 23:45                       ` Tomoya MORINAGA
  2012-03-19 12:02                         ` [PATCH v7] " Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-18 23:45 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Sat, Mar 17, 2012 at 4:06 AM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
>> > That means "Codec bit clock master frame clock slave".  Are you sure
>> > that's what you want?
>
>> Let me clarify the above.
>> Does the above "frame clock" mean "data line" of I2S?
>> If yes, I should have used 'SND_SOC_DAIFMT_CBS_CFS'.
>
> No, it means the clock which runs at sample rate (usually called the
> frame clock or LRCLK).

I could understand it.
The LRCLK is also the same as bit clock for ML26124.
So, I'll use 'SND_SOC_DAIFMT_CBS_CFS'.

thanks
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH v8] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-17 21:52                           ` Mark Brown
@ 2012-03-19 11:59                             ` Tomoya MORINAGA
  2012-03-19 19:07                               ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-19 11:59 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai, lars,
	alsa-devel, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, 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 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@gmail.com>
---
v8
 - Modify codec master/slave setting
 - Modify interconnection/dapm setting
 - Use snd_soc_update_bits not ml26124_update_bits which is delted
 - Change Regmap setting (Add .write_flag_mask = 0x01)
---
 sound/soc/codecs/Kconfig   |    4 +
 sound/soc/codecs/Makefile  |    2 +
 sound/soc/codecs/ml26124.c |  681 ++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/ml26124.h |  184 ++++++++++++
 4 files changed, 871 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 6508e8b..e314a66 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -42,6 +42,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_MAX9850 if I2C
 	select SND_SOC_MAX9768 if I2C
 	select SND_SOC_MAX9877 if I2C
+	select SND_SOC_ML26124 if I2C
 	select SND_SOC_PCM3008
 	select SND_SOC_RT5631 if I2C
 	select SND_SOC_SGTL5000 if I2C
@@ -436,5 +437,8 @@ config SND_SOC_MAX9768
 config SND_SOC_MAX9877
 	tristate
 
+config SND_SOC_ML26124
+	tristate
+
 config SND_SOC_TPA6130A2
 	tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 6662eb0..9108ee9 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -29,6 +29,7 @@ snd-soc-max9768-objs := max9768.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-rt5631-objs := rt5631.o
 snd-soc-sgtl5000-objs := sgtl5000.o
@@ -135,6 +136,7 @@ obj-$(CONFIG_SND_SOC_MAX9768)	+= snd-soc-max9768.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_RT5631)	+= snd-soc-rt5631.o
 obj-$(CONFIG_SND_SOC_SGTL5000)  += snd-soc-sgtl5000.o
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
new file mode 100644
index 0000000..22cb5bf
--- /dev/null
+++ b/sound/soc/codecs/ml26124.c
@@ -0,0 +1,681 @@
+/*
+ * 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 <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "ml26124.h"
+
+#define DVOL_CTL_DVMUTE_ON		BIT(4)	/* Digital volume MUTE On */
+#define DVOL_CTL_DVMUTE_OFF		0	/* Digital volume MUTE Off */
+#define ML26124_SAI_NO_DELAY	BIT(1)
+#define ML26124_SAI_FRAME_SYNC	(BIT(5) | BIT(0)) /* For mono (Telecodec) */
+#define ML26134_CACHESIZE 212
+#define ML26124_VMID	BIT(1)
+#define ML26124_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |\
+		       SNDRV_PCM_RATE_48000)
+#define ML26124_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |\
+			 SNDRV_PCM_FMTBIT_S32_LE)
+#define ML26124_NUM_REGISTER ML26134_CACHESIZE
+
+struct ml26124_priv {
+	u32 mclk;
+	u32 rate;
+	struct regmap *regmap;
+	int clk_in;
+	struct snd_pcm_substream *substream;
+};
+
+struct clk_coeff {
+	u32 mclk;
+	u32 rate;
+	u8 pllnl;
+	u8 pllnh;
+	u8 pllml;
+	u8 pllmh;
+	u8 plldiv;
+};
+
+/* ML26124 configuration */
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7150, 50, 0);
+
+static const DECLARE_TLV_DB_SCALE(alclvl, -2250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(mingain, -1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(maxgain, -675, 600, 0);
+static const DECLARE_TLV_DB_SCALE(boost_vol, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(ngth, -7650, 150, 0);
+
+static const char * const ml26124_companding[] = {"16bit PCM", "u-law",
+						  "A-law"};
+
+static const struct soc_enum ml26124_adc_companding_enum
+	= SOC_ENUM_SINGLE(ML26124_SAI_TRANS_CTL, 6, 3, ml26124_companding);
+
+static const struct soc_enum ml26124_dac_companding_enum
+	= SOC_ENUM_SINGLE(ML26124_SAI_RCV_CTL, 6, 3, ml26124_companding);
+
+static const struct snd_kcontrol_new ml26124_snd_controls[] = {
+	SOC_SINGLE_TLV("Capture Digital Volume", ML26124_RECORD_DIG_VOL, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("Playback Digital Volume", ML26124_PLBAK_DIG_VOL, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("Digital Boost Volume", ML26124_DIGI_BOOST_VOL, 0,
+			0x3f, 0, boost_vol),
+	SOC_SINGLE_TLV("EQ Band0 Volume", ML26124_EQ_GAIN_BRAND0, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("EQ Band1 Volume", ML26124_EQ_GAIN_BRAND1, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("EQ Band2 Volume", ML26124_EQ_GAIN_BRAND2, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("EQ Band3 Volume", ML26124_EQ_GAIN_BRAND3, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("EQ Band4 Volume", ML26124_EQ_GAIN_BRAND4, 0,
+			0xff, 1, digital_tlv),
+	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, mingain),
+	SOC_SINGLE_TLV("ALC Max Input Volume", ML26124_ALC_MAXMIN_GAIN, 4,
+			7, 1, maxgain),
+	SOC_SINGLE_TLV("Playback Limiter Min Input Volume",
+			ML26124_PL_MAXMIN_GAIN, 0, 7, 0, mingain),
+	SOC_SINGLE_TLV("Playback Limiter Max Input Volume",
+			ML26124_PL_MAXMIN_GAIN, 4, 7, 1, maxgain),
+	SOC_SINGLE_TLV("Playback Boost Volume", ML26124_PLYBAK_BOST_VOL, 0,
+			0x3f, 0, boost_vol),
+	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("ZC Switch", ML26124_PW_ZCCMP_PW_MNG, 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 Limiter", ML26124_DVOL_CTL, 0, 1, 0),
+	SOC_SINGLE("Capture Limiter", ML26124_DVOL_CTL, 1, 1, 0),
+	SOC_SINGLE("Digital Volume Fade Switch", ML26124_DVOL_CTL, 3, 1, 0),
+	SOC_SINGLE("Digital Switch", ML26124_DVOL_CTL, 4, 1, 0),
+	SOC_ENUM("DAC Companding", ml26124_dac_companding_enum),
+	SOC_ENUM("ADC Companding", ml26124_adc_companding_enum),
+};
+
+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 loopback 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 * const ml26124_input_select[] = {"Analog MIC SingleEnded in",
+				"Digital MIC in", "Analog MIC Differential in"};
+
+static const struct soc_enum ml26124_insel_enum =
+	SOC_ENUM_SINGLE(ML26124_MIC_IF_CTL, 0, 3, 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("MCLKEN", ML26124_CLK_EN, 0, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("PLLEN", ML26124_CLK_EN, 1, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("PLLOE", ML26124_CLK_EN, 2, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("MICBIAS", ML26124_PW_REF_PW_MNG, 2, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 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("MDIN"),
+	SND_SOC_DAPM_INPUT("MIN"),
+	SND_SOC_DAPM_INPUT("LIN"),
+	SND_SOC_DAPM_OUTPUT("SPOUT"),
+	SND_SOC_DAPM_OUTPUT("LOUT"),
+};
+
+static const struct snd_soc_dapm_route ml26124_intercon[] = {
+	/* Supply */
+	{"DAC", NULL, "MCLKEN"},
+	{"ADC", NULL, "MCLKEN"},
+	{"DAC", NULL, "PLLEN"},
+	{"ADC", NULL, "PLLEN"},
+	{"DAC", NULL, "PLLOE"},
+	{"ADC", NULL, "PLLOE"},
+
+	/* output mixer */
+	{"Output Mixer", "DAC Switch", "DAC"},
+	{"Output Mixer", "Line in loopback Switch", "LIN"},
+
+	/* outputs */
+	{"LOUT", NULL, "Output Mixer"},
+	{"SPOUT", NULL, "Output Mixer"},
+	{"Line Out Enable", NULL, "LOUT"},
+
+	/* input */
+	{"ADC", NULL, "Input Mux"},
+	{"Input Mux", "Analog MIC SingleEnded in", "PGA"},
+	{"Input Mux", "Analog MIC Differential in", "PGA"},
+	{"PGA", NULL, "MIN"},
+};
+
+/* PLLOutputFreq(Hz) = InputMclkFreq(Hz) * PLLM / (PLLN * PLLDIV) */
+static const struct clk_coeff coeff_div[] = {
+	{12288000, 16000, 0xc, 0x0, 0x20, 0x0, 0x4},
+	{12288000, 32000, 0xc, 0x0, 0x20, 0x0, 0x4},
+	{12288000, 48000, 0xc, 0x0, 0x30, 0x0, 0x4},
+};
+
+static struct reg_default ml26124_reg[] = {
+	/* CLOCK control Register */
+	{0x00, 0x00 },	/* Sampling Rate */
+	{0x02, 0x00},	/* PLL NL */
+	{0x04, 0x00},	/* PLLNH */
+	{0x06, 0x00},	/* PLLML */
+	{0x08, 0x00},	/* MLLMH */
+	{0x0a, 0x00},	/* PLLDIV */
+	{0x0c, 0x00},	/* Clock Enable */
+	{0x0e, 0x00},	/* CLK Input/Output Control */
+
+	/* System Control Register */
+	{0x10, 0x00},	/* Software RESET */
+	{0x12, 0x00},	/* Record/Playback Run */
+	{0x14, 0x00},	/* Mic Input/Output control */
+
+	/* Power Management Register */
+	{0x20, 0x00},	/* Reference Power Management */
+	{0x22, 0x00},	/* Input Power Management */
+	{0x24, 0x00},	/* DAC Power Management */
+	{0x26, 0x00},	/* SP-AMP Power Management */
+	{0x28, 0x00},	/* LINEOUT Power Management */
+	{0x2a, 0x00},	/* VIDEO Power Management */
+	{0x2e, 0x00},	/* AC-CMP Power Management */
+
+	/* Analog reference Control Register */
+	{0x30, 0x04},	/* MICBIAS Voltage Control */
+
+	/* Input/Output Amplifier Control Register */
+	{0x32, 0x10},	/* MIC Input Volume */
+	{0x38, 0x00},	/* Mic Boost Volume */
+	{0x3a, 0x33},	/* Speaker AMP Volume */
+	{0x48, 0x00},	/* AMP Volume Control Function Enable */
+	{0x4a, 0x00},	/* Amplifier Volume Fader Control */
+
+	/* Analog Path Control Register */
+	{0x54, 0x00},	/* Speaker AMP Output Control */
+	{0x5a, 0x00},	/* Mic IF Control */
+	{0xe8, 0x01},	/* Mic Select Control */
+
+	/* Audio Interface Control Register */
+	{0x60, 0x00},	/* SAI-Trans Control */
+	{0x62, 0x00},	/* SAI-Receive Control */
+	{0x64, 0x00},	/* SAI Mode select */
+
+	/* DSP Control Register */
+	{0x66, 0x01},	/* Filter Func Enable */
+	{0x68, 0x00},	/* Volume Control Func Enable */
+	{0x6A, 0x00},	/* Mixer & Volume Control*/
+	{0x6C, 0xff},	/* Record Digital Volume */
+	{0x70, 0xff},	/* Playback Digital Volume */
+	{0x72, 0x10},	/* Digital Boost Volume */
+	{0x74, 0xe7},	/* EQ gain Band0 */
+	{0x76, 0xe7},	/* EQ gain Band1 */
+	{0x78, 0xe7},	/* EQ gain Band2 */
+	{0x7A, 0xe7},	/* EQ gain Band3 */
+	{0x7C, 0xe7},	/* EQ gain Band4 */
+	{0x7E, 0x00},	/* HPF2 CutOff*/
+	{0x80, 0x00},	/* EQ Band0 Coef0L */
+	{0x82, 0x00},	/* EQ Band0 Coef0H */
+	{0x84, 0x00},	/* EQ Band0 Coef0L */
+	{0x86, 0x00},	/* EQ Band0 Coef0H */
+	{0x88, 0x00},	/* EQ Band1 Coef0L */
+	{0x8A, 0x00},	/* EQ Band1 Coef0H */
+	{0x8C, 0x00},	/* EQ Band1 Coef0L */
+	{0x8E, 0x00},	/* EQ Band1 Coef0H */
+	{0x90, 0x00},	/* EQ Band2 Coef0L */
+	{0x92, 0x00},	/* EQ Band2 Coef0H */
+	{0x94, 0x00},	/* EQ Band2 Coef0L */
+	{0x96, 0x00},	/* EQ Band2 Coef0H */
+	{0x98, 0x00},	/* EQ Band3 Coef0L */
+	{0x9A, 0x00},	/* EQ Band3 Coef0H */
+	{0x9C, 0x00},	/* EQ Band3 Coef0L */
+	{0x9E, 0x00},	/* EQ Band3 Coef0H */
+	{0xA0, 0x00},	/* EQ Band4 Coef0L */
+	{0xA2, 0x00},	/* EQ Band4 Coef0H */
+	{0xA4, 0x00},	/* EQ Band4 Coef0L */
+	{0xA6, 0x00},	/* EQ Band4 Coef0H */
+
+	/* ALC Control Register */
+	{0xb0, 0x00},	/* ALC Mode */
+	{0xb2, 0x02},	/* ALC Attack Time */
+	{0xb4, 0x03},	/* ALC Decay Time */
+	{0xb6, 0x00},	/* ALC Hold Time */
+	{0xb8, 0x0b},	/* ALC Target Level */
+	{0xba, 0x70},	/* ALC Max/Min Gain */
+	{0xbc, 0x00},	/* Noise Gate Threshold */
+	{0xbe, 0x00},	/* ALC ZeroCross TimeOut */
+
+	/* Playback Limiter Control Register */
+	{0xc0, 0x04},	/* PL Attack Time */
+	{0xc2, 0x05},	/* PL Decay Time */
+	{0xc4, 0x0d},	/* PL Target Level */
+	{0xc6, 0x70},	/* PL Max/Min Gain */
+	{0xc8, 0x10},	/* Playback Boost Volume */
+	{0xca, 0x00},	/* PL ZeroCross TimeOut */
+
+	/* Video Amplifier Control Register */
+	{0xd0, 0x01},	/* VIDEO AMP Gain Control */
+	{0xd2, 0x01},	/* VIDEO AMP Setup 1 */
+	{0xd4, 0x01},	/* VIDEO AMP Control2 */
+};
+
+/* Get sampling rate value of sampling rate setting register (0x0) */
+static inline int get_srate(int rate)
+{
+	int srate;
+
+	switch (rate) {
+	case 16000:
+		srate = 3;
+		break;
+	case 32000:
+		srate = 6;
+		break;
+	case 48000:
+		srate = 8;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return srate;
+}
+
+static inline int get_coeff(int mclk, int rate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+		if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+			return i;
+	}
+	return -EINVAL;
+}
+
+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 i = get_coeff(priv->mclk, params_rate(hw_params));
+
+	priv->substream = substream;
+	priv->rate = params_rate(hw_params);
+
+	if (priv->clk_in) {
+		switch (priv->mclk / params_rate(hw_params)) {
+		case 256:
+			snd_soc_update_bits(codec, ML26124_CLK_CTL,
+					    BIT(0) | BIT(1), 1);
+			break;
+		case 512:
+			snd_soc_update_bits(codec, ML26124_CLK_CTL,
+					    BIT(0) | BIT(1), 2);
+			break;
+		case 1024:
+			snd_soc_update_bits(codec, ML26124_CLK_CTL,
+					    BIT(0) | BIT(1), 3);
+			break;
+		default:
+			dev_err(codec->dev, "Unsupported MCLKI\n");
+			break;
+		}
+	} else {
+		snd_soc_update_bits(codec, ML26124_CLK_CTL,
+				    BIT(0) | BIT(1), 0);
+	}
+
+	switch (params_rate(hw_params)) {
+	case 16000:
+		snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
+				    get_srate(params_rate(hw_params)));
+		snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
+				    coeff_div[i].pllnl);
+		snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
+				    coeff_div[i].pllnh);
+		snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
+				    coeff_div[i].pllml);
+		snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
+				    coeff_div[i].pllmh);
+		snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
+				    coeff_div[i].plldiv);
+		break;
+	case 32000:
+		snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
+				    get_srate(params_rate(hw_params)));
+		snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
+				    coeff_div[i].pllnl);
+		snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
+				    coeff_div[i].pllnh);
+		snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
+				    coeff_div[i].pllml);
+		snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
+				    coeff_div[i].pllmh);
+		snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
+				    coeff_div[i].plldiv);
+		break;
+	case 48000:
+		snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
+				    get_srate(params_rate(hw_params)));
+		snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
+				    coeff_div[i].pllnl);
+		snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
+				    coeff_div[i].pllnh);
+		snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
+				    coeff_div[i].pllml);
+		snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
+				    coeff_div[i].pllmh);
+		snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
+				    coeff_div[i].plldiv);
+		break;
+	default:
+		pr_err("%s:this rate is no support for ml26124\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ml26124_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	switch (priv->substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		snd_soc_update_bits(codec, ML26124_REC_PLYBAK_RUN, BIT(0), 1);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		snd_soc_update_bits(codec, ML26124_REC_PLYBAK_RUN, BIT(1), 2);
+		break;
+	}
+
+	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)
+{
+	unsigned char mode;
+	struct snd_soc_codec *codec = codec_dai->codec;
+
+	/* 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;
+}
+
+static int ml26124_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	switch (clk_id) {
+	case ML26124_USE_PLLOUT:
+		priv->clk_in = ML26124_USE_PLLOUT;
+		break;
+	case ML26124_USE_MCLKI:
+		priv->clk_in = ML26124_USE_MCLKI;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	priv->mclk = freq;
+
+	return 0;
+}
+
+static int ml26124_set_bias_level(struct snd_soc_codec *codec,
+		enum snd_soc_bias_level level)
+{
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		snd_soc_update_bits(codec, ML26124_PW_SPAMP_PW_MNG,
+				    ML26124_R26_MASK, ML26124_BLT_PREAMP_ON);
+		msleep(100);
+		snd_soc_update_bits(codec, ML26124_PW_SPAMP_PW_MNG,
+				    ML26124_R26_MASK,
+				    ML26124_MICBEN_ON | ML26124_BLT_ALL_ON);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		/* VMID ON */
+		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+			snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG,
+					    ML26124_VMID, ML26124_VMID);
+			msleep(500);
+			regcache_sync(priv->regmap);
+		}
+		break;
+	case SND_SOC_BIAS_OFF:
+		/* VMID OFF */
+		snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG,
+				    ML26124_VMID, 0);
+		break;
+	}
+	codec->dapm.bias_level = level;
+	return 0;
+}
+
+static const struct snd_soc_dai_ops ml26124_dai_ops = {
+	.hw_params	= ml26124_hw_params,
+	.digital_mute	= ml26124_mute,
+	.set_fmt	= ml26124_set_dai_fmt,
+	.set_sysclk	= ml26124_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver ml26124_dai = {
+	.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)
+{
+	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);
+	codec->control_data = priv->regmap;
+
+	ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);
+	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);
+
+	return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_ml26124 = {
+	.probe =	ml26124_probe,
+	.suspend =	ml26124_suspend,
+	.resume =	ml26124_resume,
+	.set_bias_level = ml26124_set_bias_level,
+	.dapm_widgets = ml26124_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(ml26124_dapm_widgets),
+	.dapm_routes = ml26124_intercon,
+	.num_dapm_routes = ARRAY_SIZE(ml26124_intercon),
+	.controls = ml26124_snd_controls,
+	.num_controls = ARRAY_SIZE(ml26124_snd_controls),
+};
+
+static const struct regmap_config ml26124_i2c_regmap = {
+	.val_bits = 8,
+	.reg_bits = 8,
+	.max_register = ML26124_NUM_REGISTER,
+	.reg_defaults = ml26124_reg,
+	.num_reg_defaults = ARRAY_SIZE(ml26124_reg),
+	.cache_type = REGCACHE_RBTREE,
+	.write_flag_mask = 0x01,
+};
+
+static __devinit int ml26124_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct ml26124_priv *priv;
+	int ret;
+
+	priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, priv);
+
+	priv->regmap = regmap_init_i2c(i2c, &ml26124_i2c_regmap);
+	if (IS_ERR(priv->regmap)) {
+		ret = PTR_ERR(priv->regmap);
+		dev_err(&i2c->dev, "regmap_init_i2c() failed: %d\n", ret);
+		return ret;
+	}
+
+	return snd_soc_register_codec(&i2c->dev,
+			&soc_codec_dev_ml26124, &ml26124_dai, 1);
+}
+
+static __devexit int ml26124_i2c_remove(struct i2c_client *client)
+{
+	struct ml26124_priv *priv = i2c_get_clientdata(client);
+
+	snd_soc_unregister_codec(&client->dev);
+	regmap_exit(priv->regmap);
+	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,
+};
+
+module_i2c_driver(ml26124_i2c_driver);
+
+MODULE_AUTHOR("Tomoya MORINAGA <tomoya.rohm@gmail.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..5ea0cbb
--- /dev/null
+++ b/sound/soc/codecs/ml26124.h
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ */
+
+#ifndef ML26124_H
+#define ML26124_H
+
+/* Clock Control Register */
+#define ML26124_SMPLING_RATE		0x00
+#define ML26124_PLLNL			0x02
+#define ML26124_PLLNH			0x04
+#define ML26124_PLLML			0x06
+#define ML26124_PLLMH			0x08
+#define ML26124_PLLDIV			0x0a
+#define ML26124_CLK_EN			0x0c
+#define ML26124_CLK_CTL			0x0e
+
+/* System Control Register */
+#define ML26124_SW_RST			0x10
+#define ML26124_REC_PLYBAK_RUN		0x12
+#define ML26124_MIC_TIM			0x14
+
+/* Power Mnagement Register */
+#define ML26124_PW_REF_PW_MNG		0x20
+#define ML26124_PW_IN_PW_MNG		0x22
+#define ML26124_PW_DAC_PW_MNG		0x24
+#define ML26124_PW_SPAMP_PW_MNG		0x26
+#define ML26124_PW_LOUT_PW_MNG		0x28
+#define ML26124_PW_VOUT_PW_MNG		0x2a
+#define ML26124_PW_ZCCMP_PW_MNG		0x2e
+
+/* Analog Reference Control Register */
+#define ML26124_PW_MICBIAS_VOL		0x30
+
+/* Input/Output Amplifier Control Register */
+#define ML26124_PW_MIC_IN_VOL		0x32
+#define ML26124_PW_MIC_BOST_VOL		0x38
+#define ML26124_PW_SPK_AMP_VOL		0x3a
+#define ML26124_PW_AMP_VOL_FUNC		0x48
+#define ML26124_PW_AMP_VOL_FADE		0x4a
+
+/* Analog Path Control Register */
+#define ML26124_SPK_AMP_OUT		0x54
+#define ML26124_MIC_IF_CTL		0x5a
+#define ML26124_MIC_SELECT		0xe8
+
+/* Audio Interface Control Register */
+#define ML26124_SAI_TRANS_CTL		0x60
+#define ML26124_SAI_RCV_CTL		0x62
+#define ML26124_SAI_MODE_SEL		0x64
+
+/* DSP Control Register */
+#define ML26124_FILTER_EN		0x66
+#define ML26124_DVOL_CTL		0x68
+#define ML26124_MIXER_VOL_CTL		0x6a
+#define ML26124_RECORD_DIG_VOL		0x6c
+#define ML26124_PLBAK_DIG_VOL		0x70
+#define ML26124_DIGI_BOOST_VOL		0x72
+#define ML26124_EQ_GAIN_BRAND0		0x74
+#define ML26124_EQ_GAIN_BRAND1		0x76
+#define ML26124_EQ_GAIN_BRAND2		0x78
+#define ML26124_EQ_GAIN_BRAND3		0x7a
+#define ML26124_EQ_GAIN_BRAND4		0x7c
+#define ML26124_HPF2_CUTOFF		0x7e
+#define ML26124_EQBRAND0_F0L		0x80
+#define ML26124_EQBRAND0_F0H		0x82
+#define ML26124_EQBRAND0_F1L		0x84
+#define ML26124_EQBRAND0_F1H		0x86
+#define ML26124_EQBRAND1_F0L		0x88
+#define ML26124_EQBRAND1_F0H		0x8a
+#define ML26124_EQBRAND1_F1L		0x8c
+#define ML26124_EQBRAND1_F1H		0x8e
+#define ML26124_EQBRAND2_F0L		0x90
+#define ML26124_EQBRAND2_F0H		0x92
+#define ML26124_EQBRAND2_F1L		0x94
+#define ML26124_EQBRAND2_F1H		0x96
+#define ML26124_EQBRAND3_F0L		0x98
+#define ML26124_EQBRAND3_F0H		0x9a
+#define ML26124_EQBRAND3_F1L		0x9c
+#define ML26124_EQBRAND3_F1H		0x9e
+#define ML26124_EQBRAND4_F0L		0xa0
+#define ML26124_EQBRAND4_F0H		0xa2
+#define ML26124_EQBRAND4_F1L		0xa4
+#define ML26124_EQBRAND4_F1H		0xa6
+
+/* ALC Control Register */
+#define ML26124_ALC_MODE		0xb0
+#define ML26124_ALC_ATTACK_TIM		0xb2
+#define ML26124_ALC_DECAY_TIM		0xb4
+#define ML26124_ALC_HOLD_TIM		0xb6
+#define ML26124_ALC_TARGET_LEV		0xb8
+#define ML26124_ALC_MAXMIN_GAIN		0xba
+#define ML26124_NOIS_GATE_THRSH		0xbc
+#define ML26124_ALC_ZERO_TIMOUT		0xbe
+
+/* Playback Limiter Control Register */
+#define ML26124_PL_ATTACKTIME		0xc0
+#define ML26124_PL_DECAYTIME		0xc2
+#define ML26124_PL_TARGETTIME		0xc4
+#define ML26124_PL_MAXMIN_GAIN		0xc6
+#define ML26124_PLYBAK_BOST_VOL		0xc8
+#define ML26124_PL_0CROSS_TIMOUT	0xca
+
+/* Video Amplifer Control Register */
+#define ML26124_VIDEO_AMP_GAIN_CTL	0xd0
+#define ML26124_VIDEO_AMP_SETUP1	0xd2
+#define ML26124_VIDEO_AMP_CTL2		0xd4
+
+/* Clock select for machine driver */
+#define ML26124_USE_PLL			0
+#define ML26124_USE_MCLKI_256FS		1
+#define ML26124_USE_MCLKI_512FS		2
+#define ML26124_USE_MCLKI_1024FS	3
+
+/* Register Mask */
+#define ML26124_R0_MASK	0xf
+#define ML26124_R2_MASK	0xff
+#define ML26124_R4_MASK	0x1
+#define ML26124_R6_MASK	0xf
+#define ML26124_R8_MASK	0x3f
+#define ML26124_Ra_MASK	0x1f
+#define ML26124_Rc_MASK	0x1f
+#define ML26124_Re_MASK	0x7
+#define ML26124_R10_MASK	0x1
+#define ML26124_R12_MASK	0x17
+#define ML26124_R14_MASK	0x3f
+#define ML26124_R20_MASK	0x47
+#define ML26124_R22_MASK	0xa
+#define ML26124_R24_MASK	0x2
+#define ML26124_R26_MASK	0x1f
+#define ML26124_R28_MASK	0x2
+#define ML26124_R2a_MASK	0x2
+#define ML26124_R2e_MASK	0x2
+#define ML26124_R30_MASK	0x7
+#define ML26124_R32_MASK	0x3f
+#define ML26124_R38_MASK	0x38
+#define ML26124_R3a_MASK	0x3f
+#define ML26124_R48_MASK	0x3
+#define ML26124_R4a_MASK	0x7
+#define ML26124_R54_MASK	0x2a
+#define ML26124_R5a_MASK	0x3
+#define ML26124_Re8_MASK	0x3
+#define ML26124_R60_MASK	0xff
+#define ML26124_R62_MASK	0xff
+#define ML26124_R64_MASK	0x1
+#define ML26124_R66_MASK	0xff
+#define ML26124_R68_MASK	0x3b
+#define ML26124_R6a_MASK	0xf3
+#define ML26124_R6c_MASK	0xff
+#define ML26124_R70_MASK	0xff
+
+#define ML26124_MCLKEN		BIT(0)
+#define ML26124_PLLEN		BIT(1)
+#define ML26124_PLLOE		BIT(2)
+#define ML26124_MCLKOE		BIT(3)
+
+#define ML26124_BLT_ALL_ON	0x1f
+#define ML26124_BLT_PREAMP_ON	0x13
+
+#define ML26124_MICBEN_ON	BIT(2)
+
+enum ml26124_regs {
+	ML26124_MCLK = 0,
+};
+
+enum ml26124_clk_in {
+	ML26124_USE_PLLOUT = 0,
+	ML26124_USE_MCLKI,
+};
+
+#endif
-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-03-09  6:39                   ` [PATCH v4] " Tomoya MORINAGA
@ 2012-03-19 12:00                     ` Tomoya MORINAGA
  2012-03-21 16:09                       ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-19 12:00 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai, lars,
	alsa-devel, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, Tomoya MORINAGA

This driver is for LAPIS Semiconductor ML7213 IOH I2S.

Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>
---
v5
 - Modify codec master/slave setting
 - Fix DMA setting some issues
 - FIX I2S setting some issues
---
 sound/soc/Kconfig                |    1 +
 sound/soc/Makefile               |    1 +
 sound/soc/lapis/Kconfig          |    4 +
 sound/soc/lapis/Makefile         |    4 +
 sound/soc/lapis/ioh_i2s.h        |   39 ++
 sound/soc/lapis/ml7213ioh-plat.c | 1388 ++++++++++++++++++++++++++++++++++++++
 sound/soc/lapis/ml7213ioh-plat.h |  381 +++++++++++
 7 files changed, 1818 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/lapis/Kconfig
 create mode 100644 sound/soc/lapis/Makefile
 create mode 100644 sound/soc/lapis/ioh_i2s.h
 create mode 100644 sound/soc/lapis/ml7213ioh-plat.c
 create mode 100644 sound/soc/lapis/ml7213ioh-plat.h

diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 91c9855..97d0d33 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -37,6 +37,7 @@ source "sound/soc/ep93xx/Kconfig"
 source "sound/soc/fsl/Kconfig"
 source "sound/soc/imx/Kconfig"
 source "sound/soc/jz4740/Kconfig"
+source "sound/soc/lapis/Kconfig"
 source "sound/soc/nuc900/Kconfig"
 source "sound/soc/omap/Kconfig"
 source "sound/soc/kirkwood/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 2feaf37..1f266ee 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_SND_SOC)	+= ep93xx/
 obj-$(CONFIG_SND_SOC)	+= fsl/
 obj-$(CONFIG_SND_SOC)   += imx/
 obj-$(CONFIG_SND_SOC)	+= jz4740/
+obj-$(CONFIG_SND_SOC)	+= lapis/
 obj-$(CONFIG_SND_SOC)	+= mid-x86/
 obj-$(CONFIG_SND_SOC)	+= mxs/
 obj-$(CONFIG_SND_SOC)	+= nuc900/
diff --git a/sound/soc/lapis/Kconfig b/sound/soc/lapis/Kconfig
new file mode 100644
index 0000000..551e385
--- /dev/null
+++ b/sound/soc/lapis/Kconfig
@@ -0,0 +1,4 @@
+config SND_SOC_ML7213_PLATFORM
+	tristate "ML7213 IOH ASoC platform driver"
+	help
+	  This option enables support for the AC Link Controllers in ML7213 IOH SoC.
diff --git a/sound/soc/lapis/Makefile b/sound/soc/lapis/Makefile
new file mode 100644
index 0000000..aba1630
--- /dev/null
+++ b/sound/soc/lapis/Makefile
@@ -0,0 +1,4 @@
+# Platform
+snd-soc-ml7213-plat-objs := ml7213ioh-plat.o
+
+obj-$(CONFIG_SND_SOC_ML7213_PLATFORM) += snd-soc-ml7213-plat.o
diff --git a/sound/soc/lapis/ioh_i2s.h b/sound/soc/lapis/ioh_i2s.h
new file mode 100644
index 0000000..9f19f70
--- /dev/null
+++ b/sound/soc/lapis/ioh_i2s.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#ifndef ML7213_IOH_I2S
+#define ML7213_IOH_I2S
+
+enum ioh_bclkfs {
+	ML7213IOH_BCLKFS0 = 0,
+	ML7213IOH_BCLKFS1,
+	ML7213IOH_BCLKFS2,
+	ML7213IOH_BCLKFS3,
+	ML7213IOH_BCLKFS4,
+	ML7213IOH_BCLKFS5,
+};
+
+enum ioh_mclkfs {
+	ML7213IOH_MCLKFS0 = 6,
+	ML7213IOH_MCLKFS1,
+	ML7213IOH_MCLKFS2,
+	ML7213IOH_MCLKFS3,
+	ML7213IOH_MCLKFS4,
+	ML7213IOH_MCLKFS5,
+};
+
+#endif
diff --git a/sound/soc/lapis/ml7213ioh-plat.c b/sound/soc/lapis/ml7213ioh-plat.c
new file mode 100644
index 0000000..7b93b2b
--- /dev/null
+++ b/sound/soc/lapis/ml7213ioh-plat.c
@@ -0,0 +1,1388 @@
+/*
+ * 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/init.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+#include "ioh_i2s.h"
+#include "ml7213ioh-plat.h"
+
+static struct ioh_i2s_data *i2s_data;
+static struct ioh_i2s_dma *dmadata;
+
+/* I2S HAL (Hardware Abstruction Layer) */
+static void ioh_i2s_reset(int ch)
+{
+	iowrite32(1 << ch, i2s_data->iobase + I2SSRST_OFFSET);
+	iowrite32(0, i2s_data->iobase + I2SSRST_OFFSET);
+}
+
+static void ioh_i2s_enable_interrupts(int ch, int dir)
+{
+	unsigned int intr_lines = 0;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		intr_lines = 1 << (I2S_IMASK_RX_BIT_START + ch);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		intr_lines = 1 << (I2S_IMASK_TX_BIT_START + ch);
+		break;
+	}
+
+	/* enable interrupts for specified channel */
+	iowrite32(intr_lines, i2s_data->iobase + I2SIMASKCLR_OFFSET);
+}
+
+static void ioh_i2s_disable_interrupts(int ch, int dir)
+{
+	unsigned int intr_lines;
+
+	intr_lines = ioread32(i2s_data->iobase + I2SIMASK_OFFSET);
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		intr_lines |= 1 << (I2S_IMASK_RX_BIT_START + ch);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		intr_lines |= 1 << (I2S_IMASK_TX_BIT_START + ch);
+		break;
+	}
+
+	/* Mask the specific interrupt bits */
+	iowrite32(intr_lines, i2s_data->iobase + I2SIMASK_OFFSET);
+}
+
+#define IOH_FIFO_CLR	0
+#define IOH_FIFO_RUN	1
+
+static void ioh_i2s_ctrl_fifo(int ch, int dir, int ctrl)
+{
+	int offset = ch * 0x800;
+	u32 val;
+	u32 reg_addr = 0;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		reg_addr = I2SFIFOCRX_OFFSET;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		reg_addr = I2SFIFOCTX_OFFSET;
+		break;
+	}
+
+	val = ioread32(i2s_data->iobase + reg_addr + offset);
+	if (ctrl)
+		val |= I2S_FIFO_TX_RUN;
+	else
+		val |= I2S_FIFO_TX_FCLR;
+
+	iowrite32(val, i2s_data->iobase + reg_addr + offset);
+}
+
+/* Clear interrupt status */
+static void ioh_i2s_clear_sts_ir(int ch, int dir)
+{
+	int offset = ch * 0x800;
+	u32 reg_addr = 0;
+	u32 val = 0;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		reg_addr = I2SISTRX_OFFSET;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		reg_addr = I2SISTTX_OFFSET;
+		break;
+	}
+	val = ioread32(i2s_data->iobase + reg_addr + offset) & 0xf;
+	if (val)
+		iowrite32(val, i2s_data->iobase + reg_addr + offset);
+}
+
+static void ioh_i2s_clear_dma_mask(int ch, int dir)
+{
+	u32 val = 0;
+	u32 mask = 0;
+	u32 reg_addr = 0;
+	int offset = ch * 0x800;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		reg_addr = I2SMSKRX_OFFSET;
+		mask = RX_BIT_DMAMSK;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		reg_addr = I2SMSKTX_OFFSET;
+		mask = TX_BIT_DMAMSK;
+		break;
+	}
+
+	val = ioread32(i2s_data->iobase + reg_addr + offset);
+	val &= ~mask; /* Enable Tx DMA Request */
+
+	iowrite32(val, i2s_data->iobase + reg_addr + offset);
+}
+
+#define IOH_DIS_IRQ		0
+#define IOH_EN_IRQ		1
+
+static void ioh_i2s_irq_ctrl(int ch, int dir, int ctrl)
+{
+	u32 val;
+	int offset = ch * 0x800;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		val = ioread32(i2s_data->iobase + I2SMSKRX_OFFSET + offset);
+		if (ctrl) {
+			val &= ~RX_BIT_AFIMSK; /* Enable Almost empty IR */
+			val &= ~RX_BIT_FIMSK; /* Enable Empty IR */
+		} else {
+			val |= RX_BIT_AFIMSK; /* Disble Almost full IR */
+			val |= RX_BIT_FIMSK; /* Disble full IR */
+		}
+		iowrite32(val, i2s_data->iobase + I2SMSKRX_OFFSET + offset);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		val = ioread32(i2s_data->iobase + I2SMSKTX_OFFSET + offset);
+		if (ctrl) {
+			val &= ~TX_BIT_AEIMSK; /* Enable Almost empty IR */
+			val &= ~TX_BIT_EIMSK; /* Enable Empty IR */
+		} else {
+			val |= TX_BIT_AEIMSK; /* Disble Almost empty IR */
+			val |= TX_BIT_EIMSK; /* Disble Empty IR */
+		}
+		iowrite32(val, i2s_data->iobase + I2SMSKTX_OFFSET + offset);
+		break;
+	}
+
+}
+
+/* Linux standard DMA functions */
+static bool ioh_dma_filter(struct dma_chan *chan, void *slave)
+{
+	struct pch_dma_slave *param = slave;
+
+	if ((chan->chan_id == param->chan_id) && (param->dma_dev ==
+						  chan->device->dev)) {
+		chan->private = param;
+		return true;
+	} else {
+		return false;
+	}
+}
+
+int ioh_request_dma_channel(
+		   int ch, struct ioh_i2s_dma *ioh, enum dma_data_direction dir)
+{
+	dma_cap_mask_t mask;
+	struct dma_chan *chan;
+	struct pci_dev *dma_dev;
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(0, 1)); /* Get DMA's dev
+								information */
+
+	switch (dir) {
+	case DMA_FROM_DEVICE:
+		ioh->param_rx.width = ioh->dma_rx_width;
+		ioh->param_rx.dma_dev = &dma_dev->dev;
+		ioh->param_rx.chan_id = ch * 2 + 1; /* ch Rx=1,3,...11 */
+		ioh->param_rx.rx_reg = (dma_addr_t)(i2s_data->mapbase +
+					ch * 0x800 + I2SDRRXMIRROR_OFFSET);
+		chan = dma_request_channel(mask, ioh_dma_filter,
+					   &ioh->param_rx);
+		if (chan == NULL) {
+			dev_err(i2s_data->dev, "Failed dma_request_channel for"
+				" I2S %d\n", ch);
+			return -ENOMEM;
+		}
+		ioh->chan_rx = chan;
+		break;
+	case DMA_TO_DEVICE:
+		ioh->param_tx.width = ioh->dma_tx_width;
+		ioh->param_tx.dma_dev = &dma_dev->dev;
+		ioh->param_tx.chan_id = ch * 2; /* DMA ch Tx=0,2,...10 */
+
+		ioh->param_tx.tx_reg = (dma_addr_t)(i2s_data->mapbase +
+					ch * 0x800 + I2SDRTXMIRROR_OFFSET);
+
+		chan = dma_request_channel(mask, ioh_dma_filter,
+					   &ioh->param_tx);
+		if (chan == NULL) {
+			dev_err(i2s_data->dev, "Failed dma_request_channel for"
+				" I2S %d\n", ch);
+			return -ENOMEM;
+		}
+		ioh->chan_tx = chan;
+		break;
+	default:
+		dev_err(i2s_data->dev, "Invalid direction (%d)\n", dir);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void ioh_i2s_configure_i2s_regs(int ch, int dir)
+{
+	int offset = ch * 0x800;
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		/* Rx register */
+		iowrite32(I2S_AFULL_THRESH / 2,
+		i2s_data->iobase + I2SAFRX_OFFSET + offset);
+		iowrite32(ML7213_I2SAERX_DEFAULT,
+			  i2s_data->iobase + I2SAERX_OFFSET + offset);
+		iowrite32(ML7213_I2SMSKRX_DEFAULT,
+			  i2s_data->iobase + I2SMSKRX_OFFSET + offset);
+		iowrite32(ML7213_I2SISTRX_DEFAULT,
+			  i2s_data->iobase + I2SISTRX_OFFSET + offset);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		iowrite32(0, i2s_data->iobase + I2SAFTX_OFFSET + offset);
+		iowrite32(I2S_AEMPTY_THRESH / 2,
+		i2s_data->iobase + I2SAETX_OFFSET + offset);
+		iowrite32(ML7213_I2SMSKTX_DEFAULT,
+			  i2s_data->iobase + I2SMSKTX_OFFSET + offset);
+		iowrite32(ML7213_I2SISTTX_DEFAULT,
+			  i2s_data->iobase + I2SISTTX_OFFSET + offset);
+		break;
+	}
+}
+
+static void i2s_dma_rx_complete(void *arg)
+{
+	struct ioh_i2s_dma *ioh = (struct ioh_i2s_dma *)arg;
+	struct ml7213i2s_runtime_data *ioh_rtd =
+				ioh->rx_substream->runtime->private_data;
+
+	pr_debug("%s in rx_cur_period=%d\n", __func__, ioh->rx_cur_period);
+
+	if (ioh_rtd->rx_stop) {
+		pr_debug("%s stopped. return.\n", __func__);
+		return;
+	}
+
+	ioh->rx_cur_period++;
+	if (ioh->rx_cur_period == ioh->rx_buf_frags)
+		ioh->rx_cur_period = 0;
+
+	async_tx_ack(ioh->desc_rx);
+	if (ioh->rx_substream)
+		snd_pcm_period_elapsed(ioh->rx_substream);
+	ioh_i2s_irq_ctrl(ioh->number, SNDRV_PCM_STREAM_CAPTURE, IOH_EN_IRQ);
+}
+
+static void i2s_dma_tx_complete(void *arg)
+{
+	struct ioh_i2s_dma *ioh = (struct ioh_i2s_dma *)arg;
+	struct ml7213i2s_runtime_data *ioh_rtd =
+				ioh->tx_substream->runtime->private_data;
+
+	if (ioh_rtd->tx_stop) {
+		pr_debug("%s stopped. return.\n", __func__);
+		return;
+	}
+
+	ioh->tx_cur_period++;
+	if (ioh->tx_cur_period == ioh->tx_buf_frags)
+		ioh->tx_cur_period = 0;
+
+	async_tx_ack(ioh->desc_tx);
+
+	if (ioh->tx_substream)
+		snd_pcm_period_elapsed(ioh->tx_substream);
+
+	ioh_i2s_irq_ctrl(ioh->number, SNDRV_PCM_STREAM_PLAYBACK, IOH_EN_IRQ);
+}
+
+static void i2s_dma_rx_start(struct ioh_i2s_dma *ioh, char *buff)
+{
+	struct dma_async_tx_descriptor *desc;
+	int rx_size;
+	int rx_num;
+	int i;
+	struct scatterlist *sg;
+
+	rx_size = I2S_AFULL_THRESH * ioh->dma_rx_unit;
+
+	/* The number of scatter list (Franction area is not used) */
+	if (ioh->rx_period_bytes % rx_size)
+		/* rx_num = The number of scatter list */
+		rx_num = ioh->rx_period_bytes / rx_size + 1;
+	else
+		rx_num = ioh->rx_period_bytes / rx_size;
+
+	dev_dbg(i2s_data->dev, "%s: rx: scatter_num=%d scatter_size=%d\n",
+		__func__, rx_num, rx_size);
+
+	ioh->sg_rx_p =
+	       kzalloc(sizeof(struct scatterlist) * rx_num, GFP_ATOMIC);
+
+	sg = ioh->sg_rx_p;
+
+	sg_init_table(sg, rx_num); /* Initialize SG table */
+	for (i = 0; i < rx_num; i++, sg++) {
+		sg_set_page(sg,  virt_to_page(buff), rx_size, rx_size * i);
+		sg_dma_len(sg) = rx_size / ioh->dma_rx_unit;
+		sg_dma_address(sg) = ioh->rx_physical_addr + sg->offset;
+	}
+	ioh->rx_nent = rx_num;
+
+	dma_sync_sg_for_device(i2s_data->dev, ioh->sg_rx_p, ioh->rx_nent,
+			       DMA_FROM_DEVICE);
+
+	sg = ioh->sg_rx_p;
+
+	desc = ioh->chan_rx->device->device_prep_slave_sg(ioh->chan_rx,
+			sg, rx_num, DMA_FROM_DEVICE,
+			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(i2s_data->dev, "%s:device_prep_slave_sg Failed\n",
+			__func__);
+		return;
+	}
+	ioh_i2s_irq_ctrl(ioh->number, SNDRV_PCM_STREAM_CAPTURE, IOH_DIS_IRQ);
+
+	ioh->desc_rx = desc;
+
+	desc->callback = i2s_dma_rx_complete;
+	desc->callback_param = ioh;
+
+	desc->tx_submit(desc);
+}
+
+static void i2s_dma_tx_start(struct ioh_i2s_dma *ioh, char *buff)
+{
+	struct dma_async_tx_descriptor *desc;
+	int tx_size;
+	int tx_num;
+	int i;
+	struct scatterlist *sg;
+
+	tx_size = I2S_AEMPTY_THRESH * ioh->dma_tx_unit;
+	if (ioh->tx_period_bytes % tx_size)
+		/* tx_num = The number of scatter list */
+		tx_num = ioh->tx_period_bytes / tx_size + 1;
+	else
+		tx_num = ioh->tx_period_bytes / tx_size;
+
+	dev_dbg(i2s_data->dev, "%s: tx: scatter_num=%d scatter_size=%d\n",
+		__func__, tx_num, tx_size);
+
+	ioh->sg_tx_p =
+	       kzalloc(sizeof(struct scatterlist) * tx_num, GFP_ATOMIC);
+
+	sg = ioh->sg_tx_p;
+	sg_init_table(sg, tx_num); /* Initialize SG table */
+
+	for (i = 0; i < tx_num; i++, sg++) {
+		sg_set_page(sg,  virt_to_page(buff), tx_size, tx_size * i);
+		sg_dma_len(sg) = tx_size / ioh->dma_tx_unit;
+		sg_dma_address(sg) = ioh->tx_physical_addr + sg->offset;
+	}
+	ioh->tx_nent = tx_num;
+
+	dma_sync_sg_for_device(i2s_data->dev, ioh->sg_tx_p, ioh->tx_nent,
+			       DMA_TO_DEVICE);
+
+	sg = ioh->sg_tx_p;
+
+	desc = ioh->chan_tx->device->device_prep_slave_sg(ioh->chan_tx,
+					sg, tx_num, DMA_TO_DEVICE,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(i2s_data->dev, "%s:device_prep_slave_sg Failed\n",
+			__func__);
+		return;
+	}
+
+	ioh_i2s_irq_ctrl(ioh->number, SNDRV_PCM_STREAM_PLAYBACK, IOH_DIS_IRQ);
+
+	ioh->desc_tx = desc;
+
+	desc->callback = i2s_dma_tx_complete;
+	desc->callback_param = ioh;
+	desc->tx_submit(desc);
+}
+
+static void ioh_release_dma_channel(int ch, int dir)
+{
+	struct ioh_i2s_dma *ioh;
+
+	ioh = &dmadata[ch];
+
+	switch (dir) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		dma_sync_sg_for_cpu(i2s_data->dev, ioh->sg_rx_p, ioh->rx_nent,
+				    DMA_FROM_DEVICE);
+
+		if (ioh->chan_rx) {
+			ioh->chan_rx->device->device_control(ioh->chan_rx,
+							     DMA_TERMINATE_ALL,
+							     0);
+			dma_release_channel(ioh->chan_rx);
+			ioh->chan_rx = NULL;
+		}
+
+		kfree(ioh->sg_rx_p);
+		ioh->rx_buf_dma = 0;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		dma_sync_sg_for_cpu(i2s_data->dev, ioh->sg_tx_p, ioh->tx_nent,
+				    DMA_TO_DEVICE);
+
+		if (ioh->chan_tx) {
+			ioh->chan_tx->device->device_control(ioh->chan_tx,
+							     DMA_TERMINATE_ALL,
+							     0);
+			dma_release_channel(ioh->chan_tx);
+			ioh->chan_tx = NULL;
+		}
+		kfree(ioh->sg_tx_p);
+		ioh->tx_buf_dma = 0;
+		break;
+	}
+}
+
+void ioh_i2s_event(u32 idisp, int ch)
+{
+	unsigned long flags;
+	unsigned int status;
+	char *buff;
+	int offset = ch * 0x800;
+	struct ioh_i2s_dma *ioh = &dmadata[ch];
+	struct ml7213i2s_runtime_data *ioh_rtd;
+
+	spin_lock_irqsave(&i2s_data->tx_lock, flags);
+
+	if (idisp & BIT(ch + 16)) {
+		dev_dbg(i2s_data->dev, "Rx%d interrupt occures\n", ch);
+		ioh_rtd = ioh->rx_substream->runtime->private_data;
+		buff = ioh->rx_dma_addr +
+				      ioh->rx_cur_period * ioh->rx_period_bytes;
+		status = ioread32(i2s_data->iobase + I2SISTRX_OFFSET + offset);
+
+		if (status & I2S_RX_FINT)
+			dev_dbg(i2s_data->dev, "%s:I2S%d overrun occurs\n",
+				__func__, ch);
+		if (status & I2S_RX_AFINT)
+			if (!ioh_rtd->rx_stop)
+				i2s_dma_rx_start(ioh, buff);
+
+		/*Clear the interrupt status */
+		iowrite32(status, i2s_data->iobase + I2SISTRX_OFFSET + offset);
+	}
+
+	if (idisp & BIT(ch)) {
+		dev_dbg(i2s_data->dev, "Tx%d interrupt occures\n", ch);
+		ioh_rtd = ioh->tx_substream->runtime->private_data;
+		buff = ioh->tx_dma_addr +
+				      ioh->tx_cur_period * ioh->tx_period_bytes;
+
+		status = ioread32(i2s_data->iobase + I2SISTTX_OFFSET + offset);
+		if (status & I2S_TX_EINT)
+			dev_dbg(i2s_data->dev, "%s:I2S%d under flow occurs\n",
+				__func__, ch);
+		if (status & I2S_TX_AEINT)
+			if (!ioh_rtd->tx_stop)
+				i2s_dma_tx_start(ioh, buff);
+
+		/*Clear the interrupt status */
+		iowrite32(status, i2s_data->iobase + I2SISTTX_OFFSET + offset);
+	}
+	spin_unlock_irqrestore(&i2s_data->tx_lock, flags);
+	return;
+}
+
+static irqreturn_t ioh_i2s_irq(int irq, void *data)
+{
+	int i;
+	u32 idisp;
+
+	idisp = ioread32(i2s_data->iobase + I2SIDISP_OFFSET);
+	for (i = 0; i < MAX_I2S_CH; i++)
+		ioh_i2s_event(idisp, i);
+
+	return IRQ_HANDLED;
+}
+
+static int snd_card_ml7213i2s_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ml7213i2s_runtime_data *ioh_rtd;
+
+	ioh_rtd = kzalloc(sizeof(*ioh_rtd), GFP_KERNEL);
+	if (ioh_rtd == NULL)
+		return -ENOMEM;
+
+	runtime->private_data = ioh_rtd;
+	runtime->hw = ml7213i2s_pcm_hw;
+
+	/* makes the infrastructure responsible for freeing dma */
+	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	snd_soc_set_runtime_hwparams(substream, &ml7213i2s_pcm_hw);
+
+	spin_lock_init(&ioh_rtd->lock);
+	return 0;
+}
+
+static int snd_card_ml7213i2s_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int ch;
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ch = dmadata[substream->number].mapped_rx_ch;
+		ioh_release_dma_channel(ch, SNDRV_PCM_STREAM_CAPTURE);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ch = dmadata[substream->number].mapped_tx_ch;
+		ioh_release_dma_channel(ch, SNDRV_PCM_STREAM_PLAYBACK);
+		break;
+	}
+
+	kfree(runtime->private_data);
+
+	return 0;
+}
+
+static int snd_card_ml7213i2s_hw_params(struct snd_pcm_substream *substream,
+				    struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct ml7213i2s_runtime_data *ioh_rtd = runtime->private_data;
+	struct ioh_i2s_dma *dma =
+			snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+	int err;
+	int ch;
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ioh_rtd->rx_dma = dma;
+		ch = dmadata[substream->number].mapped_rx_ch;
+		err = ioh_request_dma_channel(ch, &dmadata[ch],
+					      DMA_FROM_DEVICE);
+		if (err)
+			return err;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ioh_rtd->tx_dma = dma;
+		ch = dmadata[substream->number].mapped_tx_ch;
+		err = ioh_request_dma_channel(ch, &dmadata[ch],
+					      DMA_TO_DEVICE);
+		if (err)
+			return err;
+		break;
+	}
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	runtime->dma_bytes = params_buffer_bytes(hw_params);
+
+	return snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+}
+
+static int snd_card_ml7213i2s_hw_free(struct snd_pcm_substream *substream)
+{
+	struct ml7213i2s_runtime_data *ioh_rtd;
+	ioh_rtd = substream->runtime->private_data;
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ioh_rtd->rx_dma = NULL;
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ioh_rtd->tx_dma = NULL;
+		break;
+	}
+	snd_pcm_set_runtime_buffer(substream, NULL);
+
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_card_ml7213i2s_pcm_trigger
+				(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ml7213i2s_runtime_data *ioh_rtd = runtime->private_data;
+	int err = 0;
+	unsigned long flags;
+	int ch;
+	struct ioh_i2s_dma *ioh;
+
+	spin_lock_irqsave(&ioh_rtd->lock, flags);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		switch (substream->stream) {
+		case SNDRV_PCM_STREAM_CAPTURE:
+			ioh_rtd->rx_stop = 0;
+			ch = dmadata[substream->number].mapped_rx_ch;
+			pr_debug("%s Capture start.(ch=%d)\n", __func__, ch);
+			ioh = &dmadata[ch];
+			ioh->rx_substream = substream;
+
+			ioh_i2s_configure_i2s_regs(ch,
+						   SNDRV_PCM_STREAM_CAPTURE);
+			ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_CAPTURE,
+					  IOH_FIFO_CLR);
+			ioh_i2s_clear_sts_ir(ch, SNDRV_PCM_STREAM_CAPTURE);
+			ioh_i2s_irq_ctrl(ch, SNDRV_PCM_STREAM_CAPTURE,
+					 IOH_EN_IRQ);
+			ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_CAPTURE,
+					  IOH_FIFO_RUN);
+			ioh_i2s_clear_dma_mask(ch, SNDRV_PCM_STREAM_CAPTURE);
+			ioh_i2s_enable_interrupts(ch, SNDRV_PCM_STREAM_CAPTURE);
+			break;
+		case SNDRV_PCM_STREAM_PLAYBACK:
+			if (!ioh_rtd)
+				return -1;
+			if (!substream)
+				return -1;
+			ioh_rtd->tx_stop = 0;
+			ch = dmadata[substream->number].mapped_tx_ch;
+			pr_debug("%s Playback start.(ch=%d)\n", __func__, ch);
+			ioh = &dmadata[ch];
+			ioh->tx_substream = substream;
+
+			ioh_i2s_configure_i2s_regs(ch,
+						   SNDRV_PCM_STREAM_PLAYBACK);
+			ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_PLAYBACK,
+					  IOH_FIFO_CLR);
+			ioh_i2s_clear_sts_ir(ch, SNDRV_PCM_STREAM_PLAYBACK);
+			ioh_i2s_irq_ctrl(ch, SNDRV_PCM_STREAM_PLAYBACK,
+					 IOH_EN_IRQ);
+			ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_PLAYBACK,
+					  IOH_FIFO_RUN);
+			ioh_i2s_clear_dma_mask(ch, SNDRV_PCM_STREAM_PLAYBACK);
+			ioh_i2s_enable_interrupts(ch,
+						  SNDRV_PCM_STREAM_PLAYBACK);
+			break;
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		switch (substream->stream) {
+		case SNDRV_PCM_STREAM_CAPTURE:
+			ioh_rtd->rx_stop = 1;
+			ch = dmadata[substream->number].mapped_rx_ch;
+			pr_debug("%s Capture stop.(ch=%d)\n", __func__, ch);
+			ioh_i2s_disable_interrupts(ch,
+						   SNDRV_PCM_STREAM_CAPTURE);
+			ioh_i2s_irq_ctrl(ch, SNDRV_PCM_STREAM_CAPTURE,
+					 IOH_DIS_IRQ);
+			ioh_i2s_clear_sts_ir(ch, SNDRV_PCM_STREAM_CAPTURE);
+			ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_CAPTURE,
+					  IOH_FIFO_CLR);
+			break;
+		case SNDRV_PCM_STREAM_PLAYBACK:
+			ioh_rtd->tx_stop = 1;
+			ch = dmadata[substream->number].mapped_tx_ch;
+			pr_debug("%s Playback stop.(ch=%d)\n", __func__, ch);
+			ioh_i2s_disable_interrupts(ch,
+						   SNDRV_PCM_STREAM_PLAYBACK);
+			ioh_i2s_irq_ctrl(ch, SNDRV_PCM_STREAM_PLAYBACK,
+					 IOH_DIS_IRQ);
+			ioh_i2s_clear_sts_ir(ch, SNDRV_PCM_STREAM_PLAYBACK);
+			ioh_i2s_ctrl_fifo(ch, SNDRV_PCM_STREAM_PLAYBACK,
+					  IOH_FIFO_CLR);
+			break;
+		}
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+
+	spin_unlock_irqrestore(&ioh_rtd->lock, flags);
+
+	return err;
+}
+
+static int snd_card_ml7213i2s_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ml7213i2s_runtime_data *ioh_rtd = runtime->private_data;
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ioh_rtd->rx_dma->rx_cur_period = 0;
+		ioh_rtd->rx_dma->rx_dma_addr = runtime->dma_area;
+		ioh_rtd->rx_dma->rx_physical_addr = runtime->dma_addr;
+		ioh_rtd->rx_dma->rx_buffer_bytes =
+					    snd_pcm_lib_buffer_bytes(substream);
+		ioh_rtd->rx_dma->rx_period_bytes =
+					    snd_pcm_lib_period_bytes(substream);
+		ioh_rtd->rx_dma->rx_buf_frags =
+					ioh_rtd->rx_dma->rx_buffer_bytes /
+					ioh_rtd->rx_dma->rx_period_bytes;
+		pr_debug("%s:Capture:buffbytes=%ld periodbytes=%ld frags=%d\n",
+		      __func__, ioh_rtd->rx_dma->rx_buffer_bytes,
+		      ioh_rtd->rx_dma->rx_period_bytes,
+		      ioh_rtd->rx_dma->rx_buf_frags);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ioh_rtd->tx_dma->tx_cur_period = 0;
+		ioh_rtd->tx_dma->tx_dma_addr = runtime->dma_area;
+		ioh_rtd->tx_dma->tx_physical_addr = runtime->dma_addr;
+		ioh_rtd->tx_dma->tx_buffer_bytes =
+					    snd_pcm_lib_buffer_bytes(substream);
+		ioh_rtd->tx_dma->tx_period_bytes =
+					    snd_pcm_lib_period_bytes(substream);
+		ioh_rtd->tx_dma->tx_buf_frags =
+					ioh_rtd->tx_dma->tx_buffer_bytes /
+					ioh_rtd->tx_dma->tx_period_bytes;
+		pr_debug("%s:Playback:buffbytes=%ld periodbytes=%ld frags=%d\n",
+		      __func__, ioh_rtd->tx_dma->tx_buffer_bytes,
+		      ioh_rtd->tx_dma->tx_period_bytes,
+		      ioh_rtd->tx_dma->tx_buf_frags);
+		break;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t
+snd_card_ml7213i2s_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct ml7213i2s_runtime_data *ioh_rtd =
+					substream->runtime->private_data;
+	unsigned long offset = 0;
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		offset = ioh_rtd->rx_dma->rx_cur_period *
+			 ioh_rtd->rx_dma->rx_period_bytes;
+		pr_debug("%s:Capture:offset=0x%lx\n",
+			 __func__, offset);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		offset = ioh_rtd->tx_dma->tx_cur_period *
+			 ioh_rtd->tx_dma->tx_period_bytes;
+		pr_debug("%s:Playback:offset=0x%lx\n",
+			__func__, offset);
+		break;
+	}
+
+	return bytes_to_frames(substream->runtime, offset);
+}
+
+static struct snd_pcm_ops snd_card_ml7213i2s_ops = {
+	.open =			snd_card_ml7213i2s_open,
+	.close =		snd_card_ml7213i2s_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_card_ml7213i2s_hw_params,
+	.hw_free =		snd_card_ml7213i2s_hw_free,
+	.prepare =		snd_card_ml7213i2s_pcm_prepare,
+	.trigger =		snd_card_ml7213i2s_pcm_trigger,
+	.pointer =		snd_card_ml7213i2s_pcm_pointer,
+};
+
+static u64 idma_mask = DMA_BIT_MASK(32);
+static int ml7213ioh_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_pcm *pcm = rtd->pcm;
+	int ret = 0;
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &idma_mask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	snd_pcm_lib_preallocate_pages_for_all(
+		pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+		snd_dma_continuous_data(GFP_KERNEL),
+		0, 64 * 128 * 4 * 64);
+
+	ret = request_irq(i2s_data->pdev->irq, ioh_i2s_irq, IRQF_SHARED,
+			  "ml7213_ioh", i2s_data->pdev);
+	if (ret != 0)
+		printk(KERN_ERR "Failed to allocate irq\n");
+
+	return ret;
+}
+
+static void ml7213ioh_pcm_free(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+
+	free_irq(i2s_data->pdev->irq, i2s_data->pdev);
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+		buf->area = NULL;
+	}
+}
+
+static struct snd_soc_platform_driver ml7213ioh_soc_platform = {
+	.pcm_new	= ml7213ioh_pcm_new,
+	.pcm_free	= ml7213ioh_pcm_free,
+	.ops		= &snd_card_ml7213i2s_ops,
+};
+
+/* DAI functions */
+static int ml7213i2s_dai_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *hw_params,
+			    struct snd_soc_dai *dai)
+{
+	int ch;
+	int byte;
+	unsigned int clk;
+	u32 i2sclkcnt;
+	u32 bclkfs = 0;
+	u32 mclkfs = 0;
+	u32 dabit;
+	int offset;
+	u32 i2scnt;
+	int dma_size;
+	void *iobase = i2s_data->iobase;
+
+	switch (params_format(hw_params)) {
+	case SNDRV_PCM_FORMAT_U8:
+		byte = 1;
+		bclkfs = IOH_BCLKFS_32FS;
+		dma_size = PCH_DMA_WIDTH_1_BYTE;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		byte = 2;
+		bclkfs = IOH_BCLKFS_32FS;
+		dma_size = PCH_DMA_WIDTH_2_BYTES;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		byte = 4;
+		bclkfs = IOH_BCLKFS_64FS;
+		dma_size = PCH_DMA_WIDTH_4_BYTES;
+		break;
+	default:
+		pr_err("%s: Failed not support format\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (byte) {
+	case 1:
+		dabit = 0;
+		break;
+	case 2:
+		dabit = 2 << I2S_DABIT_START;
+		break;
+	case 4:
+		dabit = 5 << I2S_DABIT_START; /* 24-bit transfer */
+		break;
+	}
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ch = dmadata[substream->number].mapped_rx_ch;
+		dmadata[ch].dma_rx_unit = byte;
+		dmadata[ch].dma_rx_width = dma_size;
+		offset = ch * 0x800;
+		i2scnt = ioread32(iobase + offset + I2SCNTRX_OFFSET);
+		if (dabit)
+			i2scnt |= dabit;
+		else
+			i2scnt &= ~(BIT(8) | BIT(9) | BIT(10));
+		iowrite32(i2scnt, iobase + offset + I2SCNTRX_OFFSET);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ch = dmadata[substream->number].mapped_tx_ch;
+		dmadata[ch].dma_tx_unit = byte;
+		dmadata[ch].dma_tx_width = dma_size;
+		offset = ch * 0x800;
+		i2scnt = ioread32(iobase + offset + I2SCNTTX_OFFSET);
+		if (dabit)
+			i2scnt |= dabit;
+		else
+			i2scnt &= ~(BIT(8) | BIT(9) | BIT(10));
+		iowrite32(i2scnt, iobase + offset + I2SCNTTX_OFFSET);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (params_rate(hw_params)) {
+	case 16000:
+	case 32000:
+	case 48000:
+		clk = 12288000;
+		break;
+	default:
+		pr_err("%s: Failed not support rate\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (clk / params_rate(hw_params)) {
+	case 64:
+		mclkfs = 0;
+		break;
+	case 128:
+		mclkfs = 1;
+		break;
+	case 192:
+		mclkfs = 2;
+		break;
+	case 256:
+		mclkfs = 3;
+		break;
+	case 384:
+		mclkfs = 4;
+		break;
+	case 512:
+		mclkfs = 5;
+		break;
+	case 768:
+		mclkfs = 6;
+		break;
+	case 1024:
+		mclkfs = 7;
+		break;
+	default:
+		pr_err("%s: Failed not support mclkfs\n", __func__);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: bclkfs=%d mclkfs=%dFS dabit=%d\n", __func__,
+		bclkfs, clk / params_rate(hw_params), dabit);
+	i2sclkcnt = ioread32(i2s_data->iobase + I2SCLKCNT0_OFFSET + 0x10 * ch);
+	i2sclkcnt &= ~I2S_MCLKFS_MASK;
+	i2sclkcnt &= ~I2S_BCLKFS_MASK;
+	i2sclkcnt |= bclkfs << I2SCLKCNT_BCLKFS_OFFSET;
+	i2sclkcnt |= mclkfs << I2SCLKCNT_MCLKFS_OFFSET;
+	iowrite32(i2sclkcnt, iobase + I2SCLKCNT0_OFFSET + 0x10 * ch);
+
+	snd_soc_dai_set_dma_data(dai, substream, &dmadata[ch]);
+
+	return 0;
+}
+
+static int ml7213i2s_dai_set_dai_fmt(struct snd_soc_dai *dai,
+				     unsigned int fmt)
+{
+	u32 cmn_reg[MAX_I2S_CH];
+	u32 tx_reg[MAX_I2S_CH];
+	u32 rx_reg[MAX_I2S_CH];
+	int i;
+	int offset = 0;
+	void *iobase = i2s_data->iobase;
+
+	/* set master/slave audio interface */
+	for (i = 0; i < MAX_I2S_CH; i++, offset = i * 0x800) {
+		cmn_reg[i] = ioread32(iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+		tx_reg[i] = ioread32(iobase + offset + I2SCNTTX_OFFSET);
+		rx_reg[i] = ioread32(iobase + offset + I2SCNTRX_OFFSET);
+
+		switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+		case SND_SOC_DAIFMT_CBS_CFS:
+			cmn_reg[i] |= I2SCLKCNT_MSSEL;
+			break;
+		case SND_SOC_DAIFMT_CBM_CFM:
+			cmn_reg[i] &= ~I2SCLKCNT_MSSEL;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		/* interface format */
+		switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+		case SND_SOC_DAIFMT_I2S:
+			cmn_reg[i] |= ML7213I2S_LRCLK_FMT_I2S;
+			tx_reg[i] &= ~ML7213I2S_TX_I2S | ~ML7213I2S_TX_DLY |\
+				  ~ML7213I2S_TX_MSB_LSB |\
+				  ~ML7213I2S_TX_LR_POL | ~ML7213I2S_TX_AFT;
+			rx_reg[i] &= ~ML7213I2S_RX_I2S | ~ML7213I2S_RX_DLY |\
+				  ~ML7213I2S_RX_MSB_LSB |\
+				  ~ML7213I2S_RX_LR_POL | ~ML7213I2S_RX_AFT;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		/* clock inversion */
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			cmn_reg[i] &= ~ML7213I2S_BCLKPOL;
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			cmn_reg[i] |= ML7213I2S_BCLKPOL;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		iowrite32(cmn_reg[i], iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+		iowrite32(tx_reg[i], iobase + offset + I2SCNTTX_OFFSET);
+		iowrite32(rx_reg[i], iobase + offset + I2SCNTRX_OFFSET);
+	}
+
+	return 0;
+}
+
+static int ml7213i2s_dai_set_dai_sysclk(struct snd_soc_dai *dai,
+					int clk_id, unsigned int freq, int dir)
+{
+	u32 reg[MAX_I2S_CH];
+	void *iobase = i2s_data->iobase;
+	int i;
+
+	for (i = 0; i < MAX_I2S_CH; i++) {
+		reg[i] = ioread32(iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+		if (clk_id == IOH_MASTERCLKSEL_MCLK)
+			reg[i] &= ~ML7213I2S_MASTER_CLK_SEL;
+		else if (clk_id == IOH_MASTERCLKSEL_MLBCLK)
+			reg[i] |= ML7213I2S_MASTER_CLK_SEL;
+		iowrite32(reg[i], iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+	}
+
+	return 0;
+}
+
+static int ml7213i2s_dai_set_channel_map(struct snd_soc_dai *dai,
+		unsigned int tx_num, unsigned int *tx_slot,
+		unsigned int rx_num, unsigned int *rx_slot)
+{
+	int i;
+	unsigned int slot;
+	unsigned int tx_mapped = 0, rx_mapped = 0;
+
+	if ((tx_num > MAX_I2S_CH) || (rx_num > MAX_I2S_CH))
+		return -EINVAL;
+
+	for (i = 0; i < tx_num; i++) {
+		slot = tx_slot[i];
+		if ((slot < MAX_I2S_CH) &&
+				(!(tx_mapped & (1 << slot)))) {
+			dmadata[i].mapped_tx_ch = slot;
+			tx_mapped |= 1 << slot;
+		} else
+			return -EINVAL;
+	}
+
+	for (i = 0; i < rx_num; i++) {
+		slot = rx_slot[i];
+		if ((slot < MAX_I2S_CH) &&
+				(!(rx_mapped & (1 << slot)))) {
+			dmadata[i].mapped_rx_ch = slot;
+			rx_mapped |= 1 << slot;
+		} else
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int ml7213i2s_soc_suspend(struct snd_soc_dai *cpu_dai)
+{
+	int i;
+	void *iobase;
+	struct ioh_i2s_pm_ch_reg *save;
+	struct ioh_i2s_pm_ch_reg_cmn *save_cmn;
+	int offset;
+
+	iobase = i2s_data->iobase;
+	for (i = 0, offset = 0; i < MAX_I2S_CH; i++, offset = i * 0x800) {
+		save = &i2s_data->ch_reg_save[i];
+
+		save->i2sdrtx = ioread32(iobase + offset + I2SDRTX_OFFSET);
+		save->i2scnttx = ioread32(iobase + offset + I2SCNTTX_OFFSET);
+		save->i2sfifoctx =
+				ioread32(iobase + offset + I2SFIFOCTX_OFFSET);
+		save->i2saftx = ioread32(iobase + offset + I2SAFTX_OFFSET);
+		save->i2saetx = ioread32(iobase + offset + I2SAETX_OFFSET);
+		save->i2smsktx = ioread32(iobase + offset + I2SMSKTX_OFFSET);
+		save->i2sisttx = ioread32(iobase + offset + I2SISTTX_OFFSET);
+
+		save->i2scntrx = ioread32(iobase + offset + I2SCNTRX_OFFSET);
+		save->i2sfifocrx =
+				ioread32(iobase + offset + I2SFIFOCRX_OFFSET);
+		save->i2safrx = ioread32(iobase + offset + I2SAFRX_OFFSET);
+		save->i2saerx = ioread32(iobase + offset + I2SAERX_OFFSET);
+		save->i2smskrx = ioread32(iobase + offset + I2SMSKRX_OFFSET);
+		save->i2sistrx = ioread32(iobase + offset + I2SISTRX_OFFSET);
+	}
+
+	save_cmn = &i2s_data->cmn_reg_save;
+	for (i = 0; i < MAX_I2S_CH; i++) {
+		save_cmn->i2sclkcnt[i] =
+		ioread32(i2s_data->iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+	}
+	save_cmn->i2simask = ioread32(i2s_data->iobase + I2SIMASK_OFFSET);
+
+	return 0;
+}
+
+static int ml7213i2s_soc_resume(struct snd_soc_dai *cpu_dai)
+{
+	int i;
+	void *iobase;
+	struct ioh_i2s_pm_ch_reg *save;
+	int offset;
+
+	iobase = i2s_data->iobase;
+	save = &i2s_data->ch_reg_save[0];
+	for (i = 0, offset = 0; i < MAX_I2S_CH; i++, offset = i * 0x800) {
+		iowrite32(save->i2sdrtx, iobase + offset + I2SDRTX_OFFSET);
+		iowrite32(save->i2scnttx, iobase + offset + I2SCNTTX_OFFSET);
+		iowrite32(save->i2sfifoctx,
+			  iobase + offset + I2SFIFOCTX_OFFSET);
+		iowrite32(save->i2saftx, iobase + offset + I2SAFTX_OFFSET);
+		iowrite32(save->i2saetx, iobase + offset + I2SAETX_OFFSET);
+		iowrite32(save->i2smsktx, iobase + offset + I2SMSKTX_OFFSET);
+		iowrite32(save->i2sisttx, iobase + offset + I2SISTTX_OFFSET);
+
+		iowrite32(save->i2scntrx, iobase + offset + I2SCNTRX_OFFSET);
+		iowrite32(save->i2sfifocrx,
+			  iobase + offset + I2SFIFOCRX_OFFSET);
+		iowrite32(save->i2safrx, iobase + offset + I2SAFRX_OFFSET);
+		iowrite32(save->i2saerx, iobase + offset + I2SAERX_OFFSET);
+		iowrite32(save->i2smskrx, iobase + offset + I2SMSKRX_OFFSET);
+		iowrite32(save->i2sistrx, iobase + offset + I2SISTRX_OFFSET);
+	}
+
+	for (i = 0; i < MAX_I2S_CH; i++) {
+		iowrite32(i2s_data->cmn_reg_save.i2sclkcnt[i],
+			 i2s_data->iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
+	}
+
+	iowrite32(i2s_data->cmn_reg_save.i2simask,
+		  i2s_data->iobase + I2SIMASK_OFFSET);
+
+	return 0;
+}
+#else
+#define ml7213i2s_soc_suspend NULL
+#define ml7213i2s_soc_resume NULL
+#endif
+
+static const struct snd_soc_dai_ops ml7213i2s_dai_ops = {
+	.hw_params	= ml7213i2s_dai_hw_params,
+	.set_fmt	= ml7213i2s_dai_set_dai_fmt,
+	.set_sysclk	= ml7213i2s_dai_set_dai_sysclk,
+	.set_channel_map	= ml7213i2s_dai_set_channel_map,
+};
+
+static struct snd_soc_dai_driver ml7213i2s_dai_data = {
+	.playback = {
+		.channels_min = USE_CHANNELS_MIN,
+		.channels_max = USE_CHANNELS_MAX,
+		.rates = ML7213_I2S_RATES,
+		.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |\
+			   SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.capture = {
+		.channels_min = USE_CHANNELS_MIN,
+		.channels_max = USE_CHANNELS_MAX,
+		.rates = ML7213_I2S_RATES,
+		.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |\
+			   SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.ops = &ml7213i2s_dai_ops,
+	.suspend = ml7213i2s_soc_suspend,
+	.resume = ml7213i2s_soc_resume,
+};
+
+/* PCI functions */
+DEFINE_PCI_DEVICE_TABLE(ioh_pci_tbl) = {
+	{
+		.vendor = PCI_VENDOR_ID_ROHM,
+		.device = PCI_DEVICE_ID_ML7213_I2S,
+		.subvendor = PCI_ANY_ID,
+		.subdevice = PCI_ANY_ID,
+	},
+	{0,}
+};
+
+static int ioh_i2s_pci_probe(struct pci_dev *pdev,
+			     const struct pci_device_id *id)
+{
+	int rv = 0;
+	void __iomem *tbl;
+	unsigned int mapbase;
+	int i;
+
+	rv = pci_enable_device(pdev);
+	if (rv)
+		goto enable_device;
+
+	tbl = pci_iomap(pdev, 1, 0);
+	if (!tbl) {
+		rv = -ENOMEM;
+		printk(KERN_ERR "pci_iomap failed\n");
+		goto out_ipmap;
+	}
+
+	mapbase = pci_resource_start(pdev, 1);
+	if (!mapbase) {
+		rv = -ENOMEM;
+		printk(KERN_ERR "pci_resource_start failed\n");
+		goto out_pci_resource;
+	}
+
+	i2s_data = devm_kzalloc(&pdev->dev, sizeof(*i2s_data), GFP_KERNEL);
+	if (!i2s_data) {
+		dev_err(&pdev->dev, "Can't allocate i2s_data\n");
+		rv = -ENOMEM;
+		goto out_kzalloc_data;
+	}
+
+	dmadata = devm_kzalloc(&pdev->dev,
+			       sizeof(*dmadata) * MAX_I2S_CH, GFP_KERNEL);
+	if (!dmadata) {
+		dev_err(&pdev->dev, "Can't allocate dmadata\n");
+		rv = -ENOMEM;
+		goto out_kzalloc_dma;
+	}
+
+	i2s_data->dev = &pdev->dev;
+	i2s_data->pdev = pdev;
+	i2s_data->iobase = tbl;
+	i2s_data->mapbase = mapbase;
+	spin_lock_init(&i2s_data->tx_lock);
+
+	dev_set_name(&pdev->dev, "%s", "ml7213ioh");
+	rv = snd_soc_register_dai(&pdev->dev, &ml7213i2s_dai_data);
+	if (rv < 0) {
+		printk(KERN_ERR "Failed to snd_soc_register_dai\n");
+		goto out_register_dai;
+	}
+
+	dev_set_name(&pdev->dev, "%s", "ml7213-i2s-audio");
+	rv = snd_soc_register_platform(&pdev->dev, &ml7213ioh_soc_platform);
+	if (rv < 0) {
+		printk(KERN_ERR "Failed to snd_soc_register_platform\n");
+		goto out_soc_register_plat;
+	}
+
+	for (i = 0; i < MAX_I2S_CH; i++)
+		dmadata[i].number = i;
+
+	return 0;
+
+out_soc_register_plat:
+	snd_soc_unregister_dai(&pdev->dev);
+out_register_dai:
+out_kzalloc_dma:
+out_kzalloc_data:
+out_pci_resource:
+	pci_iounmap(pdev, i2s_data->iobase);
+out_ipmap:
+	pci_disable_device(pdev);
+enable_device:
+
+	return rv;
+}
+
+static void ioh_i2s_pci_remove(struct pci_dev *pdev)
+{
+	int i;
+
+	for (i = 0; i < MAX_I2S_CH; i++)
+		ioh_i2s_reset(i);
+
+	snd_soc_unregister_platform(&pdev->dev);
+	snd_soc_unregister_dai(&pdev->dev);
+	pci_iounmap(pdev, i2s_data->iobase);
+	pci_disable_device(pdev);
+}
+
+static int ioh_i2s_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	int ret;
+
+	ret = pci_save_state(pdev);
+	if (ret) {
+		dev_err(&pdev->dev,
+			" %s -pci_save_state returns %d\n", __func__, ret);
+		return ret;
+	}
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	pci_disable_device(pdev);
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+	return 0;
+}
+
+static int ioh_i2s_pci_resume(struct pci_dev *pdev)
+{
+	int ret;
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_err(&pdev->dev,
+		"%s-pci_enable_device failed(ret=%d) ", __func__, ret);
+		return ret;
+	}
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+
+	return 0;
+}
+
+static struct pci_driver ioh_i2s_driver = {
+	.name = DRV_NAME,
+	.probe = ioh_i2s_pci_probe,
+	.remove = __devexit_p(ioh_i2s_pci_remove),
+	.id_table = ioh_pci_tbl,
+#ifdef CONFIG_PM
+	.suspend = ioh_i2s_pci_suspend,
+	.resume = ioh_i2s_pci_resume,
+#endif
+};
+
+static int __init ioh_plat_init(void)
+{
+	return pci_register_driver(&ioh_i2s_driver);
+}
+
+static void __exit ioh_i2s_cleanup(void)
+{
+	pci_unregister_driver(&ioh_i2s_driver);
+}
+
+module_init(ioh_plat_init);
+module_exit(ioh_i2s_cleanup);
+
+MODULE_AUTHOR("Tomoya MORINAGA <tomoya.rohm@gmail.com>");
+MODULE_DESCRIPTION("LAPIS Semiconductor ML7213 IOH ALSA SoC platform driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/lapis/ml7213ioh-plat.h b/sound/soc/lapis/ml7213ioh-plat.h
new file mode 100644
index 0000000..88495d7
--- /dev/null
+++ b/sound/soc/lapis/ml7213ioh-plat.h
@@ -0,0 +1,381 @@
+/*
+ * 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.
+ */
+
+#ifndef ML7213IOH_PLAT_H
+#define ML7213IOH_PLAT_H
+
+#include <linux/interrupt.h>
+#include <linux/pch_dma.h>
+
+#define DRV_NAME "ml7213ioh-i2s-pci"
+#define PCI_VENDOR_ID_ROHM	0X10DB
+#define PCI_DEVICE_ID_ML7213_I2S	0X8033
+
+#define I2SCLKCNT_MSSEL		BIT(0)
+#define ML7213I2S_BCLKPOL	BIT(1)
+#define ML7213I2S_LRCLK_FMT	(BIT(4) | BIT(5))
+#define ML7213I2S_LRCLK_FMT_I2S	BIT(4)
+
+#define ML7213I2S_TX_I2S	BIT(0)
+#define ML7213I2S_TX_DLY	BIT(12)
+#define ML7213I2S_TX_MSB_LSB	BIT(13)
+#define ML7213I2S_TX_LR_POL	BIT(14)
+#define ML7213I2S_TX_AFT	BIT(15)
+#define ML7213I2S_RX_I2S	BIT(0)
+#define ML7213I2S_RX_DLY	BIT(12)
+#define ML7213I2S_RX_MSB_LSB	BIT(13)
+#define ML7213I2S_RX_LR_POL	BIT(14)
+#define ML7213I2S_RX_AFT	BIT(15)
+#define ML7213I2S_MASTER_CLK_SEL	BIT(2)
+
+#define ML7213_I2S_RATES \
+	(SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000)
+
+/* ioh_bclkfs_t */
+#define	IOH_BCLKFS_8FS		0
+#define	IOH_BCLKFS_16FS		1
+#define	IOH_BCLKFS_32FS		2
+#define	IOH_BCLKFS_64FS		3
+
+#define	I2SCLKCNT_MCLKFS_OFFSET		(8)
+#define	I2SCLKCNT_BCLKFS_OFFSET		(12)
+
+#define I2SCLKCNT0_OFFSET	0x3000
+#define I2SCLKCNT1_OFFSET	0x3010
+#define I2SCLKCNT2_OFFSET	0x3020
+#define I2SCLKCNT3_OFFSET	0x3030
+#define I2SCLKCNT4_OFFSET	0x3040
+#define I2SCLKCNT5_OFFSET	0x3050
+#define I2SISTATUS_OFFSET	0x3080
+#define I2SIDISP_OFFSET		0x3084
+#define I2SIMASK_OFFSET		0x3088
+#define I2SIMASKCLR_OFFSET	0x308C
+#define I2SSRST_OFFSET		0x3FFC
+#define I2SDRTX_OFFSET		0x0
+#define I2SCNTTX_OFFSET		0x4
+#define I2SFIFOCTX_OFFSET	0x8
+#define I2SAFTX_OFFSET		0xC
+#define I2SAETX_OFFSET		0x10
+#define I2SMSKTX_OFFSET		0x14
+#define I2SISTTX_OFFSET		0x18
+#define I2SMONTX_OFFSET		0x1C
+#define I2SDRRX_OFFSET		0x20
+#define I2SCNTRX_OFFSET		0x24
+#define I2SFIFOCRX_OFFSET	0x28
+#define I2SAFRX_OFFSET		0x2C
+#define I2SAERX_OFFSET		0x30
+#define I2SMSKRX_OFFSET		0x34
+#define I2SISTRX_OFFSET		0x38
+#define I2SMONRX_OFFSET		0x3C
+#define FIRST_TX_OFFSET		0x0
+#define FIRST_RX_OFFSET		0x0
+
+#define I2SDRTXMIRROR_OFFSET	0x100
+#define I2SDRRXMIRROR_OFFSET	0x400
+
+#define I2S_ALL_INTERRUPT_BITS	0x3F003F
+#define I2S_IDISP_BITS		0x3F003F
+#define I2S_IDISP_TX_BITS	0x00003F
+#define I2S_IDISP_RX_BITS	0x3F0000
+#define TX_BIT_FIMSK	0x1	/*Fifo full interrupt mask bit*/
+#define TX_BIT_AFIMSK	0x2	/*Fifo Almost full interrupt mask bit*/
+#define TX_BIT_EIMSK	0x4	/*Fifo empty interrupt mask bit*/
+#define TX_BIT_AEIMSK	0x8	/*Fifo Almost empty interrupt mask bit*/
+#define TX_BIT_DMAMSK	0x10	/*Masks DMA*/
+#define TX_BIT_DMATC	0x100
+#define I2S_TX_ALL_INTR_MASK_BITS (TX_BIT_FIMSK | TX_BIT_AFIMSK | TX_BIT_EIMSK \
+							| TX_BIT_AEIMSK)
+#define I2S_TX_NORMAL_INTR_MASK_BITS (TX_BIT_FIMSK | TX_BIT_AFIMSK)
+#define RX_BIT_FIMSK	0x1	/*Fifo full interrupt mask bit*/
+#define RX_BIT_AFIMSK	0x2	/*Fifo Almost full interrupt mask bit*/
+#define RX_BIT_EIMSK	0x4	/*Fifo empty interrupt mask bit*/
+#define RX_BIT_AEIMSK	0x8	/*Fifo Almost empty interrupt mask bit*/
+#define RX_BIT_DMAMSK	0x10	/*Masks DMA*/
+#define RX_BIT_DMATC	0x100
+#define I2S_RX_ALL_INTR_MASK_BITS (RX_BIT_FIMSK | RX_BIT_AFIMSK | RX_BIT_EIMSK \
+							| RX_BIT_AEIMSK)
+#define I2S_RX_NORMAL_INTR_MASK_BITS (RX_BIT_EIMSK | RX_BIT_AEIMSK)
+#define I2S_TX_FINT	0x1	/*Full Interrupt*/
+#define I2S_TX_AFINT	0x2	/*Almost full interrupt*/
+#define I2S_TX_EINT	0x4	/*Empty interrupt*/
+#define I2S_TX_AEINT	0x8	/*Almost empty interrupt*/
+#define I2S_RX_FINT	0x1	/*Full Interrupt*/
+#define I2S_RX_AFINT	0x2	/*Almost full interrupt*/
+#define I2S_RX_EINT	0x4	/*Empty interrupt*/
+#define I2S_RX_AEINT	0x8	/*Almost empty interrupt*/
+
+#define I2S_FIFO_TX_FCLR	BIT(0)
+#define I2S_FIFO_TX_RUN		BIT(4)
+#define I2S_FIFO_RX_FCLR	BIT(0)
+#define I2S_FIFO_RX_RUN		BIT(4)
+
+#define FIFO_CTRL_BIT_TX_RUN	0x10
+#define FIFO_CTRL_BIT_RX_RUN	0x10
+#define I2S_CNT_BIT_TEL		0x1
+#define I2S_IMASK_TX_BIT_START	0
+#define I2S_IMASK_RX_BIT_START	16
+
+#define I2S_DABIT_START	8
+
+#define I2S_MCLKFS_MASK		(BIT(8) | BIT(9) | BIT(10))
+#define I2S_BCLKFS_MASK		(BIT(12) | BIT(13))
+
+/* DMA processing */
+#define	PERIOD_POS_MAX		I2S_DMA_SG_NUM
+#define	PERIOD_LEN		(I2S_AFULL_THRESH * PERIOD_POS_MAX)
+
+#define	SUPPORT_FORMAT		(SNDRV_PCM_FMTBIT_U8 | \
+				 SNDRV_PCM_FMTBIT_S16_LE | \
+				 SNDRV_PCM_FMTBIT_S32_LE)
+#define	MAX_PERIOD_SIZE		(PERIOD_LEN * 4)
+
+#define USE_CHANNELS_MIN	1
+#define USE_CHANNELS_MAX	2
+#define	MAX_I2S_CH		6		/*I2S0 ~ I2S5*/
+#define USE_PERIODS_MIN		(I2S_DMA_SG_MAX)
+#define USE_PERIODS_MAX		(I2S_DMA_SG_MAX)
+
+#define I2S_AEMPTY_THRESH	64	/* Almost  Empty Threshold */
+#define I2S_AFULL_THRESH	64	/* Almost  Full Threshold */
+
+#define	I2S_DMA_SG_NUM		(128)
+#define	I2S_DMA_SG_MAX		(64)
+
+#define	IOH_MSSEL_MASTER	1
+
+#define ML7213_I2SAERX_DEFAULT	0x1f
+#define ML7213_I2SMSKRX_DEFAULT	0x1f
+#define ML7213_I2SISTRX_DEFAULT	0xC
+
+#define ML7213_I2SMSKTX_DEFAULT	0x1f
+#define ML7213_I2SISTTX_DEFAULT	0xC
+
+enum ioh_i2s_fifo_type {
+	IOH_FIFO_32 = 4,
+	IOH_FIFO_16 = 2,
+	IOH_FIFO_8 = 1,
+};
+
+enum ioh_i2s_status {
+	IOH_EOK = 0,
+	IOH_EDONE = 1,
+	IOH_EUNDERRUN = 2,
+	IOH_EOVERRUN = 3,
+	IOH_EFRAMESYNC = 4,
+};
+
+enum ioh_bclkpol_t {
+	ioh_BCLKPOL_FALLING = 0,
+	ioh_BCLKPOL_RISING,
+};
+
+enum ioh_masterclksel_t {
+	IOH_MASTERCLKSEL_MCLK = 0,
+	IOH_MASTERCLKSEL_MLBCLK,
+};
+
+enum ioh_lrckfmt_t {
+	IOH_LRCLKFMT_I2S = 1,
+	IOH_LRCLKFMT_LONGFRAME,
+	IOH_LRCLKFMT_SHORTFRAME,
+};
+
+enum ioh_mclkfs_t {
+	IOH_MCLKFS_64FS = 0,
+	IOH_MCLKFS_128FS,
+	IOH_MCLKFS_192FS,
+	IOH_MCLKFS_256FS,
+	IOH_MCLKFS_384FS,
+	IOH_MCLKFS_512FS,
+	IOH_MCLKFS_768FS,
+	IOH_MCLKFS_1024FS,
+};
+
+enum ioh_dlyoff_t {
+	IOH_DLYOFF_DLY_ON = 0,		/* date delat on */
+	IOH_DLYOFF_DLY_OFF,		/* date delat off */
+};
+
+enum ioh_lrpol_t {
+	IOH_LRPOL_NO_INVERT = 0,	/* Low of LRCLK is L data.
+					   High of LRCLK is R data. */
+	IOH_LRPOL_INVERT,		/* Low of LRCLK is R data.
+					   High of LRCLK is L data. */
+};
+
+enum ioh_aft_t {
+	IOH_AFR_FRONT = 0,
+	IOH_AFR_BACK,
+};
+
+struct ml7213i2s_runtime_data {
+	spinlock_t lock;
+	int tx_stop;
+	int rx_stop;
+	struct ioh_i2s_dma *rx_dma;
+	struct ioh_i2s_dma *tx_dma;
+};
+
+struct ioh_i2s_pm_ch_reg {
+	u32 i2sdrtx;	/* Tx: data register */
+	u32 i2scnttx; /* Tx: control register */
+	u32 i2sfifoctx;	/* Tx: FIFO control register */
+	u32 i2saftx;	/* Tx: almost full threshold setting */
+	u32 i2saetx;	/* Tx: almost empty threshold setting */
+	u32 i2smsktx;	/* Tx: interrupt mask settings */
+	u32 i2sisttx;	/* Tx: for acknowledging interrupts */
+	u32 i2scntrx;	/* Rx: control register */
+	u32 i2sfifocrx; /* Rx: FIFO control register */
+	u32 i2safrx;	/* Rx: almost full threshold setting */
+	u32 i2saerx;	/* Rx: almost empty threshold setting */
+	u32 i2smskrx;	/* Rx: interrupt mask settings */
+	u32 i2sistrx;	/* Rx: for acknowledging interrupts */
+};
+
+struct ioh_i2s_pm_ch_reg_cmn {
+	u32 i2sclkcnt[MAX_I2S_CH];	/*clock control register(ch0~5) */
+	u32 i2simask;		/*interrupt mask */
+};
+
+struct ioh_i2s_data {
+	struct device *dev;
+	void *iobase;
+	unsigned int mapbase;
+	spinlock_t tx_lock;
+	struct ioh_i2s_pm_ch_reg_cmn cmn_reg_save;
+	struct ioh_i2s_pm_ch_reg ch_reg_save[MAX_I2S_CH];
+	struct pci_dev *pdev;
+};
+
+struct ioh_i2s_dma {
+	/* Transmit side DMA */
+	struct scatterlist	*sg_tx_p;
+	struct scatterlist	*sg_rx_p;
+
+	int tx_num;	/* The number of sent sg */
+	int rx_num;	/* The number of sent sg */
+
+	struct dma_chan			*chan_tx;
+	struct dma_chan			*chan_rx;
+
+	int rx_nent;	/* The number of rx scatter list */
+	int tx_nent;	/* The number of tx scatter list */
+
+	struct dma_async_tx_descriptor	*desc_tx;
+	struct dma_async_tx_descriptor	*desc_rx;
+
+	dma_addr_t			tx_buf_dma;
+	dma_addr_t			rx_buf_dma;
+
+	struct pch_dma_slave		param_tx;
+	struct pch_dma_slave		param_rx;
+
+	int dma_tx_unit; /* 1Byte of 2Byte or 4Byte */
+	int dma_rx_unit; /* 1Byte of 2Byte or 4Byte */
+	int dma_tx_width;
+	int dma_rx_width;
+
+	struct snd_pcm_substream *tx_substream;
+	struct snd_pcm_substream *rx_substream;
+
+	int number;
+
+	int mapped_tx_ch;
+	int mapped_rx_ch;
+
+	unsigned char *rx_dma_addr;
+	dma_addr_t rx_physical_addr;
+	unsigned long rx_buffer_bytes;
+	unsigned long rx_period_bytes;
+
+	unsigned char *tx_dma_addr;
+	dma_addr_t tx_physical_addr;
+	unsigned long tx_buffer_bytes;
+	unsigned long tx_period_bytes;
+
+
+	int rx_buf_frags;
+	int tx_buf_frags;
+
+	int tx_cur_period;
+	int rx_cur_period;
+};
+
+struct ml7213i2s_dai {
+	struct snd_soc_dai_driver dai;
+	struct device	*dev;
+	void *iobase;
+	u32 freq;
+};
+
+struct ioh_i2s_config_common_reg {
+	u32 i2sclkcnt;	/*clock control register(ch0~5) */
+	u32 i2sistatus;	/*interrupt status */
+	u32 i2sidisp;		/*active interrupts */
+	u32 i2simask;		/*interrupt mask */
+	u32 i2simaskclr;	/*interrupt mask clear */
+};
+
+struct ioh_i2s_config_tx_reg {
+	u32 i2sdrtx;	/*data register */
+	u32 i2scnttx; /*control register */
+	u32 i2sfifoctx;	/*FIFO control register */
+	u32 i2saftx;	/*almost full threshold setting */
+	u32 i2saetx;	/*almost empty threshold setting */
+	u32 i2smsktx;	/*interrupt mask settings */
+	u32 i2sisttx;	/*for acknowledging interrupts */
+	u32 i2smontx;	/*monitor register */
+};
+
+struct ioh_i2s_config_rx_reg {
+	u32 i2sdrrx;	/* data register */
+	u32 i2scntrx;	/* control register */
+	u32 i2sfifocrx;/* FIFO control register */
+	u32 i2safrx;	/* almost full threshold setting */
+	u32 i2saerx;	/* almost empty threshold setting */
+	u32 i2smskrx;	/* interrupt mask settings */
+	u32 i2sistrx;	/* for acknowledging interrupts */
+	u32 i2smonrx;	/* monitor register */
+};
+
+struct ioh_i2s_config_reg {
+	/* The common register settings */
+	struct ioh_i2s_config_common_reg cmn;
+
+	/* TX channel settings */
+	struct ioh_i2s_config_tx_reg tx;
+
+	/* RX channel settings */
+	struct ioh_i2s_config_rx_reg rx;
+};
+
+static struct snd_pcm_hardware ml7213i2s_pcm_hw = {
+	.info =			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SUPPORT_FORMAT,
+	.channels_min =	1,
+	.channels_max =	2,
+	.buffer_bytes_max =	64 * 128 * 4 * 64,
+	.period_bytes_min =	64 * 4,
+	.period_bytes_max =	4 * 1024,
+	.periods_min =	64,
+	.periods_max =	64,
+	.fifo_size =		0,
+};
+#endif
-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v7] sound/soc/lapis: add machine driver for ML7213 Carrier Board
  2012-03-18 23:45                       ` Tomoya MORINAGA
@ 2012-03-19 12:02                         ` Tomoya MORINAGA
  2012-03-19 19:21                           ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-19 12:02 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai, lars,
	alsa-devel, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, Tomoya MORINAGA

This machine driver is for
LAPIS Semiconductor ML7213 Carrier Board + ML26124 Reference Board

Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>
---
v7
 - Modify codec master/slave setting
 - Add MICBIAS setting
 - Add some snd_soc_dapm_enable_pin settings
---
 sound/soc/lapis/Kconfig             |    7 ++
 sound/soc/lapis/Makefile            |    2 +
 sound/soc/lapis/ml7213ioh-machine.c |  186 +++++++++++++++++++++++++++++++++++
 3 files changed, 195 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/lapis/ml7213ioh-machine.c

diff --git a/sound/soc/lapis/Kconfig b/sound/soc/lapis/Kconfig
index 551e385..9bc1b4d 100644
--- a/sound/soc/lapis/Kconfig
+++ b/sound/soc/lapis/Kconfig
@@ -2,3 +2,10 @@ config SND_SOC_ML7213_PLATFORM
 	tristate "ML7213 IOH ASoC platform driver"
 	help
 	  This option enables support for the AC Link Controllers in ML7213 IOH SoC.
+
+config SND_SOC_ML7213_MACHINE
+	tristate "ML7213 IOH ASoC machine driver"
+	select SND_SOC_ML7213_PLATFORM
+	select SND_SOC_ML26124
+	help
+	  This is ASoC machine driver for ML7213 IOH
diff --git a/sound/soc/lapis/Makefile b/sound/soc/lapis/Makefile
index aba1630..7ec4bea 100644
--- a/sound/soc/lapis/Makefile
+++ b/sound/soc/lapis/Makefile
@@ -1,4 +1,6 @@
 # Platform
+snd-soc-ml7213-machine-objs := ml7213ioh-machine.o
 snd-soc-ml7213-plat-objs := ml7213ioh-plat.o
 
 obj-$(CONFIG_SND_SOC_ML7213_PLATFORM) += snd-soc-ml7213-plat.o
+obj-$(CONFIG_SND_SOC_ML7213_MACHINE) += snd-soc-ml7213-machine.o
diff --git a/sound/soc/lapis/ml7213ioh-machine.c b/sound/soc/lapis/ml7213ioh-machine.c
new file mode 100644
index 0000000..e51b1de
--- /dev/null
+++ b/sound/soc/lapis/ml7213ioh-machine.c
@@ -0,0 +1,186 @@
+/*
+ * ml7213ioh-machine.c -- SoC Audio for LAPIS Semiconductor ML7213 IOH CRB
+ *
+ * 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/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <linux/i2c.h>
+#include "../codecs/ml26124.h"
+#include "ioh_i2s.h"
+
+static const struct snd_soc_dapm_widget ml7213_dapm_widgets[] = {
+	SND_SOC_DAPM_SPK("Speaker", NULL),
+	SND_SOC_DAPM_LINE("LINEOUT", NULL),
+	SND_SOC_DAPM_LINE("LINEIN", NULL),
+	SND_SOC_DAPM_MIC("AnalogMIC", NULL),
+	SND_SOC_DAPM_MIC("DigitalMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route ml7213_routes[] = {
+	{"Speaker", NULL, "SPOUT"},
+	{"LINEOUT", NULL, "LOUT"},
+	{"AnalogMIC", NULL, "MICBIAS"},
+	{"MIN", NULL, "AnalogMIC"},
+	{"MDIN", NULL, "DigitalMIC"},
+	{"MIN", NULL, "LINEIN"},
+};
+
+static int ml7213_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *hw_params)
+{
+	int bclkfs;
+	int mclkfs;
+	unsigned int clk;
+	int ret;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	unsigned int channel_map[] = {5, 0, 1, 2, 3, 4};
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+	/* set cpu DAI channel mapping */
+	ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
+		channel_map, ARRAY_SIZE(channel_map), channel_map);
+	if (ret < 0)
+		return ret;
+
+	switch (params_rate(hw_params)) {
+	case 16000:
+	case 32000:
+	case 48000:
+		clk = 12288000;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	mclkfs = clk / params_rate(hw_params);
+
+	switch (params_format(hw_params)) {
+	case SNDRV_PCM_FORMAT_U8:
+	case SNDRV_PCM_FORMAT_S16_LE:
+		bclkfs = 32;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		bclkfs = 64;
+		break;
+	default:
+		pr_err("%s: Failed not support format\n", __func__);
+		return -EINVAL;
+		break;
+	}
+
+	/* set the codec system clock for DAC and ADC */
+	ret = snd_soc_dai_set_sysclk(codec_dai, ML26124_USE_PLLOUT, clk,
+				     SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	/* set codec DAI configuration */
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+				 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	/* set cpu DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+				 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	snd_soc_dapm_enable_pin(dapm, "Speaker");
+	snd_soc_dapm_enable_pin(dapm, "AnalogMIC");
+	snd_soc_dapm_enable_pin(dapm, "MICBIAS");
+
+	return 0;
+}
+
+static struct snd_soc_ops ml7213_ops = {
+	.hw_params = ml7213_hw_params,
+};
+
+static struct snd_soc_dai_link ml7213crb_dai = {
+	.name = "ml26124",
+	.stream_name = "ML26124",
+	.cpu_dai_name = "ml7213ioh",
+	.codec_dai_name = "ml26124-hifi",
+	.platform_name	= "ml7213-i2s-audio",
+	.codec_name	= "ml26124.1-001a",
+	.ops = &ml7213_ops,
+};
+
+static struct snd_soc_card ml7213crb_card = {
+	.name		= "LAPIS Semiconductor ML7213CRB",
+	.dai_link	= &ml7213crb_dai,
+	.num_links	= 1,
+	.dapm_widgets	= ml7213_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(ml7213_dapm_widgets),
+	.dapm_routes	= ml7213_routes,
+	.num_dapm_routes	= ARRAY_SIZE(ml7213_routes),
+};
+
+static int __devinit lapis_ml7213_ml26124_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ml7213crb_card.dev = &pdev->dev;
+	ret = snd_soc_register_card(&ml7213crb_card);
+	if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __devexit lapis_ml7213_ml26124_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_card(&ml7213crb_card);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static struct platform_driver snd_ml7213crb_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "lapis-ml7213ioh-CRB",
+	},
+	.probe = lapis_ml7213_ml26124_probe,
+	.remove = __devexit_p(lapis_ml7213_ml26124_remove),
+};
+
+static int __init lapis_ml7213_ml26124_init(void)
+{
+	return platform_driver_register(&snd_ml7213crb_driver);
+}
+module_init(lapis_ml7213_ml26124_init);
+
+static void __exit lapis_ml7213_ml26124_exit(void)
+{
+	platform_driver_unregister(&snd_ml7213crb_driver);
+}
+module_exit(lapis_ml7213_ml26124_exit);
+
+MODULE_AUTHOR("Tomoya MORINAGA <tomoya.rohm@gmail.com>");
+MODULE_DESCRIPTION("LAPIS Semiconductor ML7213 IOH ALSA SoC machine driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform: lapis-ml7213ioh-CRB");
-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* Re: [PATCH v8] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-19 11:59                             ` [PATCH v8] " Tomoya MORINAGA
@ 2012-03-19 19:07                               ` Mark Brown
  2012-03-21  0:57                                 ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-19 19:07 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, lars, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 408 bytes --]

On Mon, Mar 19, 2012 at 08:59:28PM +0900, 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

Applied, thanks, but:

 - Stop sending patches as followups to old patches, this really doesn't
 - Do try to use subject lines for your patches which are consistent
   with the subsystem.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v7] sound/soc/lapis: add machine driver for ML7213 Carrier Board
  2012-03-19 12:02                         ` [PATCH v7] " Tomoya MORINAGA
@ 2012-03-19 19:21                           ` Mark Brown
  2012-03-21  0:51                             ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-19 19:21 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, lars, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 919 bytes --]

On Mon, Mar 19, 2012 at 09:02:05PM +0900, Tomoya MORINAGA wrote:

> +static int ml7213_hw_params(struct snd_pcm_substream *substream,
> +			    struct snd_pcm_hw_params *hw_params)
> +{

> +	default:
> +		pr_err("%s: Failed not support format\n", __func__);
> +		return -EINVAL;
> +		break;

No need for the break.

> +	snd_soc_dapm_enable_pin(dapm, "Speaker");
> +	snd_soc_dapm_enable_pin(dapm, "AnalogMIC");
> +	snd_soc_dapm_enable_pin(dapm, "MICBIAS");

Why are you doing thiS?  You never disable these things so if this were
needed you may as well do it on init, but since the default state is
enabled anyway this shouldn't be doing anytihng.

> +static int __init lapis_ml7213_ml26124_init(void)
> +{
> +	return platform_driver_register(&snd_ml7213crb_driver);
> +}
> +module_init(lapis_ml7213_ml26124_init);

module_platform_driver().

> +MODULE_ALIAS("platform: lapis-ml7213ioh-CRB");

No space after the colon.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v7] sound/soc/lapis: add machine driver for ML7213 Carrier Board
  2012-03-19 19:21                           ` Mark Brown
@ 2012-03-21  0:51                             ` Tomoya MORINAGA
  0 siblings, 0 replies; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-21  0:51 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, lars, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Tue, Mar 20, 2012 at 4:21 AM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
>> +     snd_soc_dapm_enable_pin(dapm, "Speaker");
>> +     snd_soc_dapm_enable_pin(dapm, "AnalogMIC");
>> +     snd_soc_dapm_enable_pin(dapm, "MICBIAS");
>
> Why are you doing thiS?  You never disable these things so if this were
> needed you may as well do it on init, but since the default state is
> enabled anyway this shouldn't be doing anytihng.

OK.
I've confirmed if deleting these lines, both playback/capture work well.
So, I'll delete these.

thanks.
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v8] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2012-03-19 19:07                               ` Mark Brown
@ 2012-03-21  0:57                                 ` Tomoya MORINAGA
  0 siblings, 0 replies; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-21  0:57 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, lars, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Tue, Mar 20, 2012 at 4:07 AM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
>> ML26124-01HB/ML26124-02GD is 16bit monaural audio CODEC which has high
>> resistance to voltage noise. On chip regulator realizes power supply rejection
>
> Applied, thanks, but:
>
>  - Stop sending patches as followups to old patches, this really doesn't
>  - Do try to use subject lines for your patches which are consistent
>   with the subsystem.

OK.
I'll do the above.

thanks
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-03-19 12:00                     ` [PATCH v5] " Tomoya MORINAGA
@ 2012-03-21 16:09                       ` Mark Brown
  2012-03-22  0:12                         ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-21 16:09 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, lars, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 1819 bytes --]

On Mon, Mar 19, 2012 at 09:00:59PM +0900, Tomoya MORINAGA wrote:
> This driver is for LAPIS Semiconductor ML7213 IOH I2S.

So, I just said to the SPEAr guys that I really want to see support for
non-cyclic dmaengine added to the helper library rather than open
coding.  We're seeing lots of platforms moving to dmaengine (as well as
those that are in already yours is one of three out of tree platforms
I'm aware of that have actively submitted stuff) and with this number of
platforms we really need to get stuff factored out - there's enough
people working on these platforms that it should be possible to add the
support to the dmaengine library code.

A few relatively minor comments below and due to the above I haven't
really reviwed the DMA but overall this looks pretty good.

> +static struct ioh_i2s_data *i2s_data;
> +static struct ioh_i2s_dma *dmadata;

These shouldn't be global, they should be device specific and passed
around.

> +static int ml7213i2s_dai_set_dai_sysclk(struct snd_soc_dai *dai,
> +					int clk_id, unsigned int freq, int dir)
> +{
> +	u32 reg[MAX_I2S_CH];
> +	void *iobase = i2s_data->iobase;
> +	int i;
> +
> +	for (i = 0; i < MAX_I2S_CH; i++) {
> +		reg[i] = ioread32(iobase + I2SCLKCNT0_OFFSET + 0x10 * i);
> +		if (clk_id == IOH_MASTERCLKSEL_MCLK)
> +			reg[i] &= ~ML7213I2S_MASTER_CLK_SEL;
> +		else if (clk_id == IOH_MASTERCLKSEL_MLBCLK)
> +			reg[i] |= ML7213I2S_MASTER_CLK_SEL;
> +		iowrite32(reg[i], iobase + I2SCLKCNT0_OFFSET + 0x10 * i);

This looks like it should be a switch statement on clk_id, this will
also catch bad parameters that might get passed in.

> +		if ((slot < MAX_I2S_CH) &&
> +				(!(tx_mapped & (1 << slot)))) {
> +			dmadata[i].mapped_tx_ch = slot;
> +			tx_mapped |= 1 << slot;
> +		} else
> +			return -EINVAL;

Braces on both sides of the if please.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-03-21 16:09                       ` Mark Brown
@ 2012-03-22  0:12                         ` Tomoya MORINAGA
  2012-03-26 15:40                           ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-03-22  0:12 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, lars, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Thu, Mar 22, 2012 at 1:09 AM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> So, I just said to the SPEAr guys that I really want to see support for
> non-cyclic dmaengine added to the helper library rather than open
> coding.  We're seeing lots of platforms moving to dmaengine (as well as
> those that are in already yours is one of three out of tree platforms
> I'm aware of that have actively submitted stuff) and with this number of
> platforms we really need to get stuff factored out - there's enough
> people working on these platforms that it should be possible to add the
> support to the dmaengine library code.

Do you mean you don't accept our platform/machine driver's patches
unless your DMA library will have supported non-cyclic dmaengine ?
Or if I'll modify your minor comments, can you accept our patches ?

thanks.

-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-03-22  0:12                         ` Tomoya MORINAGA
@ 2012-03-26 15:40                           ` Mark Brown
  2012-05-23  4:17                             ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-03-26 15:40 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, lars, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Thu, Mar 22, 2012 at 09:12:39AM +0900, Tomoya MORINAGA wrote:
> On Thu, Mar 22, 2012 at 1:09 AM, Mark Brown

> > So, I just said to the SPEAr guys that I really want to see support for
> > non-cyclic dmaengine added to the helper library rather than open
> > coding. ?We're seeing lots of platforms moving to dmaengine (as well as

> Do you mean you don't accept our platform/machine driver's patches
> unless your DMA library will have supported non-cyclic dmaengine ?
> Or if I'll modify your minor comments, can you accept our patches ?

I really want to see the dmaengine interaction factored out into a
library.  Like I say we've got lots of platforms doing this all being
submitted at the same time, plus most of the work already there in the
form of the base library.  It shouldn't be too much work for the various
platforms to collaborate on factoring this code out.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-03-26 15:40                           ` Mark Brown
@ 2012-05-23  4:17                             ` Tomoya MORINAGA
  2012-05-23  9:41                               ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-05-23  4:17 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, lars, alsa-devel,
	linux-kernel

On Tue, Mar 27, 2012 at 12:40 AM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> So, I just said to the SPEAr guys that I really want to see support for
> non-cyclic dmaengine added to the helper library rather than open
> coding. ?We're seeing lots of platforms moving to dmaengine (as well as
What is going on this ?
Let me know this status.

-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-05-23  4:17                             ` Tomoya MORINAGA
@ 2012-05-23  9:41                               ` Mark Brown
  2012-05-23 23:46                                 ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-05-23  9:41 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, lars, alsa-devel,
	linux-kernel

[-- Attachment #1: Type: text/plain, Size: 482 bytes --]

On Wed, May 23, 2012 at 01:17:15PM +0900, Tomoya MORINAGA wrote:
> On Tue, Mar 27, 2012 at 12:40 AM, Mark Brown

> > So, I just said to the SPEAr guys that I really want to see support for
> > non-cyclic dmaengine added to the helper library rather than open
> > coding. ?We're seeing lots of platforms moving to dmaengine (as well as

> What is going on this ?
> Let me know this status.

Nobody seems to be working on it as far as I can tell, I've certainly
not seen any patches.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-05-23  9:41                               ` Mark Brown
@ 2012-05-23 23:46                                 ` Tomoya MORINAGA
  2012-05-24 10:07                                   ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-05-23 23:46 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, lars, alsa-devel,
	linux-kernel

On Wed, May 23, 2012 at 6:41 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
>> > So, I just said to the SPEAr guys that I really want to see support for
>> > non-cyclic dmaengine added to the helper library rather than open
>> > coding. ?We're seeing lots of platforms moving to dmaengine (as well as
>
>> What is going on this ?
>> Let me know this status.
>
> Nobody seems to be working on it as far as I can tell, I've certainly
> not seen any patches.

If so, could you accept current platform/machine driver ?

thanks in advance.
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-05-23 23:46                                 ` Tomoya MORINAGA
@ 2012-05-24 10:07                                   ` Mark Brown
  2012-05-25  9:30                                     ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-05-24 10:07 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, lars, alsa-devel,
	linux-kernel

On Thu, May 24, 2012 at 08:46:03AM +0900, Tomoya MORINAGA wrote:
> On Wed, May 23, 2012 at 6:41 PM, Mark Brown

> > Nobody seems to be working on it as far as I can tell, I've certainly
> > not seen any patches.

> If so, could you accept current platform/machine driver ?

Ideally what would be happening here is that you or other people who
have such systems would be working to add the required support to the
core code, there's clearly a need for common code here as there are a
number of different systems that don't have cyclic DMA and it wouldn't
be great to end up with duplicated code.

Is there some great difficulty in factoring out the support for
non-cyclic audio DMA on dmaengine - it seems like if there is we must
have a serious problem in dmaengine which we should fix?  If there is a
substantial difficulty then that's different but it doesn't feel like
we've tried doing common code yet, if there's problems doing that I'd
like to understand what they are before we jump ahead of ourselves.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-05-24 10:07                                   ` Mark Brown
@ 2012-05-25  9:30                                     ` Tomoya MORINAGA
  2012-05-25 10:20                                       ` [alsa-devel] " Vinod Koul
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-05-25  9:30 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, lars, alsa-devel,
	linux-kernel

On Thu, May 24, 2012 at 7:07 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
>> > Nobody seems to be working on it as far as I can tell, I've certainly
>> > not seen any patches.
>
>> If so, could you accept current platform/machine driver ?
>
> Ideally what would be happening here is that you or other people who
> have such systems would be working to add the required support to the
> core code, there's clearly a need for common code here as there are a
> number of different systems that don't have cyclic DMA and it wouldn't
> be great to end up with duplicated code.
>
> Is there some great difficulty in factoring out the support for
> non-cyclic audio DMA on dmaengine - it seems like if there is we must
> have a serious problem in dmaengine which we should fix?  If there is a
> substantial difficulty then that's different but it doesn't feel like
> we've tried doing common code yet, if there's problems doing that I'd
> like to understand what they are before we jump ahead of ourselves.

I'm not so familiar with Linux's DMA idea.
So we don't know whether non-cyclic dmaengine has problem or not.

Until now, we've developed device drivers use DMA driver like UART, SPI.
These drivers are  implemented using the same way, you called
"non-cyclic", and already applied.

As you said, common code for DMA code can be best solution.
However, currently, the code is nothing.
So, I want you to accept our driver as first step.
Because I think supporting new device is more important for linux than
dmaengine common.

thanks
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-05-25  9:30                                     ` Tomoya MORINAGA
@ 2012-05-25 10:20                                       ` Vinod Koul
  2012-05-27 22:19                                         ` Mark Brown
  2012-05-28  5:35                                         ` Tomoya MORINAGA
  0 siblings, 2 replies; 70+ messages in thread
From: Vinod Koul @ 2012-05-25 10:20 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Mark Brown, alsa-devel, lars, Takashi Iwai, linux-kernel, Liam Girdwood

On Fri, 2012-05-25 at 18:30 +0900, Tomoya MORINAGA wrote:
> On Thu, May 24, 2012 at 7:07 PM, Mark Brown
> <broonie@opensource.wolfsonmicro.com> wrote:
> >> > Nobody seems to be working on it as far as I can tell, I've certainly
> >> > not seen any patches.
> >
> >> If so, could you accept current platform/machine driver ?
> >
> > Ideally what would be happening here is that you or other people who
> > have such systems would be working to add the required support to the
> > core code, there's clearly a need for common code here as there are a
> > number of different systems that don't have cyclic DMA and it wouldn't
> > be great to end up with duplicated code.
> >
> > Is there some great difficulty in factoring out the support for
> > non-cyclic audio DMA on dmaengine - it seems like if there is we must
> > have a serious problem in dmaengine which we should fix?  If there is a
> > substantial difficulty then that's different but it doesn't feel like
> > we've tried doing common code yet, if there's problems doing that I'd
> > like to understand what they are before we jump ahead of ourselves.
> 
> I'm not so familiar with Linux's DMA idea.
> So we don't know whether non-cyclic dmaengine has problem or not.
First you should not be writing your own dma driver, it *needs* to use
dmaenegine. We already have bunch of driver supported, so there may be a
case that existing driver works straight or with little modifications.
Using your own dma driver is a dead road, so sooner you move over better
it is.

One you move then it would be easy for you to use soc-dmaengine. If your
driver doesn't support cyclic; nothing stops from you emulating that in
S/W. And given that you have already contributed to dmaengine subsystem,
it should be easy for you :)
> 
> Until now, we've developed device drivers use DMA driver like UART, SPI.
> These drivers are  implemented using the same way, you called
> "non-cyclic", and already applied.
> 
> As you said, common code for DMA code can be best solution.
> However, currently, the code is nothing.
> So, I want you to accept our driver as first step.
> Because I think supporting new device is more important for linux than
> dmaengine common.
> 
> thanks


-- 
~Vinod


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-05-25 10:20                                       ` [alsa-devel] " Vinod Koul
@ 2012-05-27 22:19                                         ` Mark Brown
  2012-05-30 10:50                                           ` Tomoya MORINAGA
  2012-05-28  5:35                                         ` Tomoya MORINAGA
  1 sibling, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-05-27 22:19 UTC (permalink / raw)
  To: Vinod Koul
  Cc: Tomoya MORINAGA, alsa-devel, lars, Takashi Iwai, linux-kernel,
	Liam Girdwood

On Fri, May 25, 2012 at 03:50:31PM +0530, Vinod Koul wrote:
> On Fri, 2012-05-25 at 18:30 +0900, Tomoya MORINAGA wrote:
> > On Thu, May 24, 2012 at 7:07 PM, Mark Brown

> > I'm not so familiar with Linux's DMA idea.
> > So we don't know whether non-cyclic dmaengine has problem or not.

Nobody has written the code, this is the problem!  If the code is not
there, you should try to write it.  If there is some great problem
writing the code then you should t

> First you should not be writing your own dma driver, it *needs* to use
> dmaenegine. We already have bunch of driver supported, so there may be a

He's already done that, their current code is all open coded dmaengine
stuff.

> > As you said, common code for DMA code can be best solution.
> > However, currently, the code is nothing.
> > So, I want you to accept our driver as first step.
> > Because I think supporting new device is more important for linux than
> > dmaengine common.

The existing code is far from nothing, there is a fairly substantial
dmaengine library there already which should share a big chunk of code
with any cyclic support.  If you were saying "this is too hard for
$REASON" that'd be one thing but that's not what you're saying here.
It's possible that there is actually some substantial difficult but
my first instinct would be that it should be relatively straightforward.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-05-25 10:20                                       ` [alsa-devel] " Vinod Koul
  2012-05-27 22:19                                         ` Mark Brown
@ 2012-05-28  5:35                                         ` Tomoya MORINAGA
  1 sibling, 0 replies; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-05-28  5:35 UTC (permalink / raw)
  To: Vinod Koul
  Cc: Mark Brown, alsa-devel, lars, Takashi Iwai, linux-kernel, Liam Girdwood

On Fri, May 25, 2012 at 7:20 PM, Vinod Koul <vinod.koul@linux.intel.com> wrote:
>> >> > Nobody seems to be working on it as far as I can tell, I've certainly
>> >> > not seen any patches.
>> >
>> >> If so, could you accept current platform/machine driver ?
>> >
>> > Ideally what would be happening here is that you or other people who
>> > have such systems would be working to add the required support to the
>> > core code, there's clearly a need for common code here as there are a
>> > number of different systems that don't have cyclic DMA and it wouldn't
>> > be great to end up with duplicated code.
>> >
>> > Is there some great difficulty in factoring out the support for
>> > non-cyclic audio DMA on dmaengine - it seems like if there is we must
>> > have a serious problem in dmaengine which we should fix?  If there is a
>> > substantial difficulty then that's different but it doesn't feel like
>> > we've tried doing common code yet, if there's problems doing that I'd
>> > like to understand what they are before we jump ahead of ourselves.
>>
>> I'm not so familiar with Linux's DMA idea.
>> So we don't know whether non-cyclic dmaengine has problem or not.
> First you should not be writing your own dma driver, it *needs* to use
> dmaenegine. We already have bunch of driver supported, so there may be a
> case that existing driver works straight or with little modifications.
> Using your own dma driver is a dead road, so sooner you move over better
> it is.
I've never developed our own dma driver.
I've developed our ASoC driver using linux standard DMA framework.

-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-05-27 22:19                                         ` Mark Brown
@ 2012-05-30 10:50                                           ` Tomoya MORINAGA
  2012-05-30 12:14                                             ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-05-30 10:50 UTC (permalink / raw)
  To: Mark Brown
  Cc: Vinod Koul, alsa-devel, lars, Takashi Iwai, linux-kernel, Liam Girdwood

On Mon, May 28, 2012 at 7:19 AM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
>> > I'm not so familiar with Linux's DMA idea.
>> > So we don't know whether non-cyclic dmaengine has problem or not.
>
> Nobody has written the code, this is the problem!  If the code is not
> there, you should try to write it.  If there is some great problem
> writing the code then you should t

The latter of the above seems dropped...

>> First you should not be writing your own dma driver, it *needs* to use
>> dmaenegine. We already have bunch of driver supported, so there may be a
>
> He's already done that, their current code is all open coded dmaengine
> stuff.

I don't understand why you say so ?
I don't use any own dma driver, right ? I use only dmaengine's.
If there is own, let me show.

>> > As you said, common code for DMA code can be best solution.
>> > However, currently, the code is nothing.
>> > So, I want you to accept our driver as first step.
>> > Because I think supporting new device is more important for linux than
>> > dmaengine common.
>
> The existing code is far from nothing, there is a fairly substantial
> dmaengine library there already which should share a big chunk of code
> with any cyclic support.  If you were saying "this is too hard for
> $REASON" that'd be one thing but that's not what you're saying here.

If our ASoC supports cyclic dma mode, we must modify both pch_dma
driver and our ASoC driver.
I don't want to do this.
Because I can't understand the merit. In plain words, to me, this
looks insignificant things.
In fact, current all applied ASoC drivers use dmaengine don't use
cyclic mode, right ?

> It's possible that there is actually some substantial difficult but
> my first instinct would be that it should be relatively straightforward.

Let me clarify your saying again.
Which do you want ?
  1) pch_dma must support cyclic dma mode and our ASoC driver must use
the cyclic dma function.
  2) Non-cyclic dma engine should be added to alsa-dmaengine by myself.
  3) Other

Thanks

-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-05-30 10:50                                           ` Tomoya MORINAGA
@ 2012-05-30 12:14                                             ` Mark Brown
  2012-05-31  5:38                                               ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-05-30 12:14 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Vinod Koul, alsa-devel, lars, Takashi Iwai, linux-kernel, Liam Girdwood

[-- Attachment #1: Type: text/plain, Size: 2379 bytes --]

On Wed, May 30, 2012 at 07:50:28PM +0900, Tomoya MORINAGA wrote:
> On Mon, May 28, 2012 at 7:19 AM, Mark Brown
> <broonie@opensource.wolfsonmicro.com> wrote:

> > Nobody has written the code, this is the problem!  If the code is not
> > there, you should try to write it.  If there is some great problem
> > writing the code then you should t

> The latter of the above seems dropped...

...tell us.

> >> First you should not be writing your own dma driver, it *needs* to use
> >> dmaenegine. We already have bunch of driver supported, so there may be a

> > He's already done that, their current code is all open coded dmaengine
> > stuff.

> I don't understand why you say so ?
> I don't use any own dma driver, right ? I use only dmaengine's.
> If there is own, let me show.

Please re-read what I wrote.

> > The existing code is far from nothing, there is a fairly substantial
> > dmaengine library there already which should share a big chunk of code
> > with any cyclic support.  If you were saying "this is too hard for
> > $REASON" that'd be one thing but that's not what you're saying here.

> If our ASoC supports cyclic dma mode, we must modify both pch_dma
> driver and our ASoC driver.

No, all current mainline drivers using the library use cyclic DMA.

> I don't want to do this.
> Because I can't understand the merit. In plain words, to me, this
> looks insignificant things.

The purpose of this change is to factor code out of individual drivers
into generic code rather than having lots of people writing exactly the
same code.  Code duplication at this level is pointless and makes more
work for everyone who will have to maintain the code going forward.

Having looked at Russell's out of tree code I'm even more convinced that
the amount of new code needed for non-cyclic DMA should be pretty
trivial.

> > It's possible that there is actually some substantial difficult but
> > my first instinct would be that it should be relatively straightforward.

> Let me clarify your saying again.
> Which do you want ?
>   1) pch_dma must support cyclic dma mode and our ASoC driver must use
> the cyclic dma function.
>   2) Non-cyclic dma engine should be added to alsa-dmaengine by myself.

Either of these options are fine.

>   3) Other

This would be someone else doing one of the above things.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-05-30 12:14                                             ` Mark Brown
@ 2012-05-31  5:38                                               ` Tomoya MORINAGA
  2012-05-31 10:45                                                 ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-05-31  5:38 UTC (permalink / raw)
  To: Mark Brown
  Cc: Vinod Koul, alsa-devel, lars, Takashi Iwai, linux-kernel, Liam Girdwood

On Wed, May 30, 2012 at 9:14 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
>> >> First you should not be writing your own dma driver, it *needs* to use
>> >> dmaenegine. We already have bunch of driver supported, so there may be a
>> > He's already done that, their current code is all open coded dmaengine
>> > stuff.
>> I don't understand why you say so ?
>> I don't use any own dma driver, right ? I use only dmaengine's.
>> If there is own, let me show.
> Please re-read what I wrote.

Let me clarify.
Do you say native DMA driver API like dmaengine_prep_slave_sg(),
dmaengine_submit() shouldn't be used from ASoC driver, right ?

>> > The existing code is far from nothing, there is a fairly substantial
>> > dmaengine library there already which should share a big chunk of code
>> > with any cyclic support.  If you were saying "this is too hard for
>> > $REASON" that'd be one thing but that's not what you're saying here.
>
>> If our ASoC supports cyclic dma mode, we must modify both pch_dma
>> driver and our ASoC driver.
>
> No, all current mainline drivers using the library use cyclic DMA.

I can't see any driver uses cyclic DMA. (I saw linux-next's tree from
kernel.org.)
Where is your saying "current mainline" ?

>> I don't want to do this.
>> Because I can't understand the merit. In plain words, to me, this
>> looks insignificant things.
> The purpose of this change is to factor code out of individual drivers
> into generic code rather than having lots of people writing exactly the
> same code.  Code duplication at this level is pointless and makes more
> work for everyone who will have to maintain the code going forward.

I understand your saying "merit".

However I think it seems difficult for supporting all devices.
Because hardware dependency control code can't be added.
For example, for ML7213, needs interrupt control both before/after DMA transfer.
However,  in case of using soc-dmaengine, the control can't be done.
Because the processing is in soc-dmaengine.

> Having looked at Russell's out of tree code I'm even more convinced that
> the amount of new code needed for non-cyclic DMA should be pretty
> trivial.

I don't know what you are talking about.


thanks.
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-05-31  5:38                                               ` Tomoya MORINAGA
@ 2012-05-31 10:45                                                 ` Mark Brown
  2012-06-01  8:13                                                   ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-05-31 10:45 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Vinod Koul, alsa-devel, lars, Takashi Iwai, linux-kernel, Liam Girdwood

[-- Attachment #1: Type: text/plain, Size: 1147 bytes --]

On Thu, May 31, 2012 at 02:38:30PM +0900, Tomoya MORINAGA wrote:
> On Wed, May 30, 2012 at 9:14 PM, Mark Brown

> > Please re-read what I wrote.

> Let me clarify.
> Do you say native DMA driver API like dmaengine_prep_slave_sg(),
> dmaengine_submit() shouldn't be used from ASoC driver, right ?

Your driver should be written in terms of the ASoC DMA framework.

> > No, all current mainline drivers using the library use cyclic DMA.

> I can't see any driver uses cyclic DMA. (I saw linux-next's tree from
> kernel.org.)
> Where is your saying "current mainline" ?

Linus' tree, or mine.  Have you even looked at the soc-dmaengine-pcm
code?  It uncondtionally requests a cyclic channel.

> However I think it seems difficult for supporting all devices.
> Because hardware dependency control code can't be added.
> For example, for ML7213, needs interrupt control both before/after DMA transfer.
> However,  in case of using soc-dmaengine, the control can't be done.
> Because the processing is in soc-dmaengine.

Please be more specific.  What are the concrete problems that you see?
Why is it not possible to address them within the framework?

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-05-31 10:45                                                 ` Mark Brown
@ 2012-06-01  8:13                                                   ` Tomoya MORINAGA
  2012-06-01  8:30                                                     ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-06-01  8:13 UTC (permalink / raw)
  To: Mark Brown
  Cc: Vinod Koul, alsa-devel, lars, Takashi Iwai, linux-kernel, Liam Girdwood

On Thu, May 31, 2012 at 7:45 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
>> > Please re-read what I wrote.
>
>> Let me clarify.
>> Do you say native DMA driver API like dmaengine_prep_slave_sg(),
>> dmaengine_submit() shouldn't be used from ASoC driver, right ?
>
> Your driver should be written in terms of the ASoC DMA framework.

I understand your order.
But isn't it only me ?
You mean all ASoC driver uses dmaengine should be written in terms of
the ASoC DMA framework.
Right ?

>> > No, all current mainline drivers using the library use cyclic DMA.
>> I can't see any driver uses cyclic DMA. (I saw linux-next's tree from
>> kernel.org.)
>> Where is your saying "current mainline" ?
> Linus' tree, or mine.

Sorry, I couldn't find.
Is your saying Linus's tree this ?
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
However, any ASoC driver doesn't use cyclic DMA's.


> Have you even looked at the soc-dmaengine-pcm
> code?  It uncondtionally requests a cyclic channel.

I already saw soc-dmaengine-pcm.c

>> However I think it seems difficult for supporting all devices.
>> Because hardware dependency control code can't be added.
>> For example, for ML7213, needs interrupt control both before/after DMA transfer.
>> However,  in case of using soc-dmaengine, the control can't be done.
>> Because the processing is in soc-dmaengine.
> Please be more specific.  What are the concrete problems that you see?
> Why is it not possible to address them within the framework?

I show the code.
+static void i2s_dma_tx_complete(void *arg)
+{
...

+
+       ioh_i2s_irq_ctrl(ioh->number, SNDRV_PCM_STREAM_PLAYBACK,
IOH_EN_IRQ);   ##<<Interrupt control
+}

Our device needs interrupt control both before/after DMA transfer like above.
In your soc-dma-engine, DMA transfer complete code is in soc-dma-engine, right ?
So, the vendor specific code like the above can't be written.

thanks.
-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-06-01  8:13                                                   ` Tomoya MORINAGA
@ 2012-06-01  8:30                                                     ` Mark Brown
  2012-06-11  7:05                                                       ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-06-01  8:30 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Vinod Koul, alsa-devel, lars, Takashi Iwai, linux-kernel, Liam Girdwood

[-- Attachment #1: Type: text/plain, Size: 365 bytes --]

On Fri, Jun 01, 2012 at 05:13:45PM +0900, Tomoya MORINAGA wrote:

> > Linus' tree, or mine.

> Sorry, I couldn't find.
> Is your saying Linus's tree this ?
> git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
> However, any ASoC driver doesn't use cyclic DMA's.

$ grep CYCLIC sound/soc/soc-dmaengine-pcm.c 
	dma_cap_set(DMA_CYCLIC, mask);

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-06-01  8:30                                                     ` Mark Brown
@ 2012-06-11  7:05                                                       ` Tomoya MORINAGA
  2012-06-11  8:54                                                         ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-06-11  7:05 UTC (permalink / raw)
  To: Mark Brown
  Cc: Vinod Koul, alsa-devel, lars, Takashi Iwai, linux-kernel, Liam Girdwood

On Fri, Jun 1, 2012 at 5:30 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> > Sorry, I couldn't find.
> > Is your saying Linus's tree this ?
> > git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
> > However, any ASoC driver doesn't use cyclic DMA's.
>
> $ grep CYCLIC sound/soc/soc-dmaengine-pcm.c
>        dma_cap_set(DMA_CYCLIC, mask);

I already saw this.

You said "all current mainline drivers using the library use cyclic DMA."
Does this mean the following drivers use "snd_dmaengine_pcm_open" ?
  ep93xx-pcm
  imx-pcm-dma-mx2
  mxs-pcm

However, the following drivers don't use any ASoC DMA framework, right ?.
E.g. these drivers call dma_request_channel directly.
  siu_pcm
  fsi
  txx9aclc

That is to say, some drivers ASoC DMA framework. However some drivers
don't use any ASoC DMA framework.

Why didn't you do porting to your saying "ASoC DMA framework"?

thanks.

--
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-06-11  7:05                                                       ` Tomoya MORINAGA
@ 2012-06-11  8:54                                                         ` Mark Brown
  2012-06-13 10:41                                                           ` Tomoya MORINAGA
  0 siblings, 1 reply; 70+ messages in thread
From: Mark Brown @ 2012-06-11  8:54 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Vinod Koul, alsa-devel, lars, Takashi Iwai, linux-kernel, Liam Girdwood

[-- Attachment #1: Type: text/plain, Size: 912 bytes --]

On Mon, Jun 11, 2012 at 04:05:56PM +0900, Tomoya MORINAGA wrote:

> That is to say, some drivers ASoC DMA framework. However some drivers
> don't use any ASoC DMA framework.

There are some drivers that predate the availibility of the dmaengine
framework.

> Why didn't you do porting to your saying "ASoC DMA framework"?

This, and the fact that the PCH driver uses dmaengine without using any
shared code, is the topic of the discussion.  It is entirely natural
that there are no open coded drivers using cyclic DMA via dmaengine,
they have all had that code factored out.

Please stop this and at least look at factoring this code out.  If it
turns out that there's some reason why it's a lot of work that's one
thing but it seems clear that you've not even looked yet.  It's like the
previous issues where you were sending patches which would clearly not
run successfully, it's not a very positive approach.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-06-11  8:54                                                         ` Mark Brown
@ 2012-06-13 10:41                                                           ` Tomoya MORINAGA
  2012-06-13 12:11                                                             ` Mark Brown
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoya MORINAGA @ 2012-06-13 10:41 UTC (permalink / raw)
  To: Mark Brown
  Cc: Vinod Koul, alsa-devel, lars, Takashi Iwai, linux-kernel, Liam Girdwood

On Mon, Jun 11, 2012 at 5:54 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> There are some drivers that predate the availibility of the dmaengine
> framework.

In case, you develop a new ASoC framework,  all previous ASoC drivers
should be adopted to the framework by yourself, don't you ?
Because a new developer must be confused by this.

> This, and the fact that the PCH driver uses dmaengine without using any
> shared code, is the topic of the discussion.  It is entirely natural
> that there are no open coded drivers using cyclic DMA via dmaengine,
> they have all had that code factored out.
>
> Please stop this and at least look at factoring this code out.  If it
> turns out that there's some reason why it's a lot of work that's one
> thing but it seems clear that you've not even looked yet.  It's like the
> previous issues where you were sending patches which would clearly not
> run successfully, it's not a very positive approach.

OK, I'll stop this.

I wanted to finish adding these patch series to upstream because we
spent much resource on this.
But I'm involved in other work, so I can't start now.

Until now thank you for your support.
Regards.

-- 
ROHM Co., Ltd.
tomoya

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [alsa-devel] [PATCH v5] sound/soc/lapis: add platform driver for ML7213
  2012-06-13 10:41                                                           ` Tomoya MORINAGA
@ 2012-06-13 12:11                                                             ` Mark Brown
  0 siblings, 0 replies; 70+ messages in thread
From: Mark Brown @ 2012-06-13 12:11 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Vinod Koul, alsa-devel, lars, Takashi Iwai, linux-kernel, Liam Girdwood

[-- Attachment #1: Type: text/plain, Size: 824 bytes --]

On Wed, Jun 13, 2012 at 07:41:57PM +0900, Tomoya MORINAGA wrote:
> On Mon, Jun 11, 2012 at 5:54 PM, Mark Brown

> > There are some drivers that predate the availibility of the dmaengine
> > framework.

> In case, you develop a new ASoC framework,  all previous ASoC drivers
> should be adopted to the framework by yourself, don't you ?
> Because a new developer must be confused by this.

This is exactly what Lars-Peter did, he updated all the drivers that
used cyclic mode to use the framework.  Drivers using non-cyclic mode
weren't updated as there is some additional work needed in the
framework.

That said, you don't always want to do this - depending on the change
involved it's sometimes simpler and easier to move things over piecemeal
unless there's an actual issue being created by the lack of the new
firmware.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

end of thread, other threads:[~2012-06-13 12:11 UTC | newest]

Thread overview: 70+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-02-23  5:46 [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124 Tomoya MORINAGA
2012-02-23  5:46 ` [PATCH v3] sound/soc/lapis: add platform driver for ML7213 Tomoya MORINAGA
2012-03-02 16:39   ` Mark Brown
2012-03-06  5:48     ` Tomoya MORINAGA
2012-03-06 11:52       ` Mark Brown
2012-03-07  1:30         ` Tomoya MORINAGA
2012-03-07 11:46           ` Mark Brown
2012-03-08  2:06             ` Tomoya MORINAGA
2012-03-08 11:05               ` Mark Brown
2012-03-09  6:26                 ` Tomoya MORINAGA
2012-03-09  6:39                   ` [PATCH v4] " Tomoya MORINAGA
2012-03-19 12:00                     ` [PATCH v5] " Tomoya MORINAGA
2012-03-21 16:09                       ` Mark Brown
2012-03-22  0:12                         ` Tomoya MORINAGA
2012-03-26 15:40                           ` Mark Brown
2012-05-23  4:17                             ` Tomoya MORINAGA
2012-05-23  9:41                               ` Mark Brown
2012-05-23 23:46                                 ` Tomoya MORINAGA
2012-05-24 10:07                                   ` Mark Brown
2012-05-25  9:30                                     ` Tomoya MORINAGA
2012-05-25 10:20                                       ` [alsa-devel] " Vinod Koul
2012-05-27 22:19                                         ` Mark Brown
2012-05-30 10:50                                           ` Tomoya MORINAGA
2012-05-30 12:14                                             ` Mark Brown
2012-05-31  5:38                                               ` Tomoya MORINAGA
2012-05-31 10:45                                                 ` Mark Brown
2012-06-01  8:13                                                   ` Tomoya MORINAGA
2012-06-01  8:30                                                     ` Mark Brown
2012-06-11  7:05                                                       ` Tomoya MORINAGA
2012-06-11  8:54                                                         ` Mark Brown
2012-06-13 10:41                                                           ` Tomoya MORINAGA
2012-06-13 12:11                                                             ` Mark Brown
2012-05-28  5:35                                         ` Tomoya MORINAGA
2012-02-23  5:46 ` [PATCH v5] sound/soc/lapis: add machine driver for ML7213 Carrier Board Tomoya MORINAGA
2012-03-02 13:05   ` Mark Brown
2012-03-06  5:49     ` Tomoya MORINAGA
2012-03-06 11:54       ` Mark Brown
2012-03-07  1:57         ` Tomoya MORINAGA
2012-03-09  6:38           ` [PATCH v6] " Tomoya MORINAGA
2012-03-14 14:45             ` Mark Brown
2012-03-15  4:50               ` Tomoya MORINAGA
2012-03-15 10:50                 ` Mark Brown
2012-03-16  4:07                   ` Tomoya MORINAGA
2012-03-16 19:06                     ` Mark Brown
2012-03-18 23:45                       ` Tomoya MORINAGA
2012-03-19 12:02                         ` [PATCH v7] " Tomoya MORINAGA
2012-03-19 19:21                           ` Mark Brown
2012-03-21  0:51                             ` Tomoya MORINAGA
2012-02-29 23:51 ` [PATCH v6] sound/soc/codecs: add LAPIS Semiconductor ML26124 Mark Brown
2012-03-02  8:16   ` Tomoya MORINAGA
2012-03-02 12:58     ` Mark Brown
2012-03-06  3:03       ` Tomoya MORINAGA
2012-03-06 10:00         ` Mark Brown
2012-03-06 10:49           ` Tomoya MORINAGA
2012-03-06 12:12             ` Mark Brown
2012-03-07  2:16               ` Tomoya MORINAGA
2012-03-07 11:48                 ` Mark Brown
2012-03-08  2:24                   ` Tomoya MORINAGA
2012-03-08 11:47                     ` Mark Brown
2012-03-09  6:37                       ` [PATCH v7] " Tomoya MORINAGA
2012-03-14 14:39                         ` Mark Brown
2012-03-15  4:51                           ` [alsa-devel] " Tomoya MORINAGA
2012-03-14 17:40                     ` [alsa-devel] [PATCH v6] " Lars-Peter Clausen
2012-03-14 17:45                       ` Mark Brown
2012-03-15  6:29                       ` Tomoya MORINAGA
2012-03-16  9:55                         ` Tomoya MORINAGA
2012-03-17 21:52                           ` Mark Brown
2012-03-19 11:59                             ` [PATCH v8] " Tomoya MORINAGA
2012-03-19 19:07                               ` Mark Brown
2012-03-21  0:57                                 ` Tomoya MORINAGA

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).