linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/3] sound/soc/codecs: add LAPIS Semiconductor ML26124
@ 2011-11-21  4:08 Tomoya MORINAGA
  2011-11-21  4:08 ` [PATCH 2/3] sound/soc/lapis: add machine driver Tomoya MORINAGA
                   ` (2 more replies)
  0 siblings, 3 replies; 27+ messages in thread
From: Tomoya MORINAGA @ 2011-11-21  4:08 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Lars-Peter Clausen, Dimitris Papastamos, Mike Frysinger,
	Daniel Mack, 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 (PSRR) be over 90dB so more than 50dB is improved than ever. ML26124-01HB/
ML26124-02GD can deliver stable audio performance without being affected by noise
from the power supply circuit and peripheral components. The chip also includes
a composite video signal output, which can be applied to various portable device
 requirements. The ML26124 is realized these functions into very small package
the size is only 2.56mm x 2.46mm therefore can be construct high quality sound
system easily.
ML26124-01HB is 25pin WCSP package; ML26124-02GD is 32pin WQFN package.

Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>
---
 sound/soc/codecs/Kconfig   |    4 +
 sound/soc/codecs/Makefile  |    2 +
 sound/soc/codecs/ml26124.c |  583 ++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/ml26124.h |  123 ++++++++++
 4 files changed, 712 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 4584514..6cb0915 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
@@ -211,6 +212,9 @@ config SND_SOC_DMIC
 config SND_SOC_MAX98088
        tristate
 
+config SND_SOC_ML26124
+	tristate
+
 config SND_SOC_MAX98095
        tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index a2c7842..ad3f460 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -23,6 +23,7 @@ snd-soc-dfbmcs320-objs := dfbmcs320.o
 snd-soc-dmic-objs := dmic.o
 snd-soc-l3-objs := l3.o
 snd-soc-max98088-objs := max98088.o
+snd-soc-ml26124-objs := ml26124.o
 snd-soc-max98095-objs := max98095.o
 snd-soc-max9850-objs := max9850.o
 snd-soc-pcm3008-objs := pcm3008.o
@@ -124,6 +125,7 @@ obj-$(CONFIG_SND_SOC_DMIC)	+= snd-soc-dmic.o
 obj-$(CONFIG_SND_SOC_L3)	+= snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)	+= snd-soc-jz4740-codec.o
 obj-$(CONFIG_SND_SOC_MAX98088)	+= snd-soc-max98088.o
+obj-$(CONFIG_SND_SOC_ML26124)	+= snd-soc-ml26124.o
 obj-$(CONFIG_SND_SOC_MAX98095)	+= snd-soc-max98095.o
 obj-$(CONFIG_SND_SOC_MAX9850)	+= snd-soc-max9850.o
 obj-$(CONFIG_SND_SOC_PCM3008)	+= snd-soc-pcm3008.o
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
new file mode 100644
index 0000000..646af7d
--- /dev/null
+++ b/sound/soc/codecs/ml26124.c
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "ml26124.h"
+
+struct ml26124_priv {
+	enum snd_soc_control_type control_type;
+	spinlock_t lock;
+	struct snd_pcm_substream *substream;
+	unsigned int rate;
+	unsigned int ch;
+	struct mutex i2c_mutex;
+};
+
+static const char *ml26124_lrmcon[] = {"Use L", "Use R", "Use (L+R)", "Use (L+R)/2"};
+static const char *ml26124_dvfconcon[] = {"1/fs=20.8us", "2/fs", "4/fs", "8/fs",
+"16/fs", "32/fs", "64/fs", "128/fs", "256/fs", "512/fs", "1024/fs", "2048/fs",
+"4096/fs", "8192/fs", "16384/fs=341msec"};
+static const char *ml26124_hpf2cut[] = {"FS=8n/11.025n/16n = 80/110/120",
+					"100/138/150", "130/179/195",
+					"160/221/240", "200/276/300",
+					"260/358/390", "320/441/480",
+					"400/551/600"};
+static const char *ml26124_alcmode[] = {"ALC mode", "Limitter mode"};
+static const char *ml26124_ngtyp[] = {"Gain hold mode", "Mute mode"};
+static const char *ml26124_alcatk[] = {"ALC mode=4/fs  Limiter mode=ALC mode/4",
+     "8/fs", "16/fs", "32/fs", "64/fs", "128/fs", "256/fs", "512/fs", "1024/fs",
+     "2048/fs", "4096/fs", "8192/fs", "16384", "32768", "65536", "131072"};
+static const char *ml26124_alcdcy[] = {"Limitter mode=4/fs  ALC mode=Limitter mode * 4",
+     "8/fs", "16/fs", "32/fs", "64/fs", "128/fs", "256/fs", "512/fs", "1024/fs",
+     "2048/fs", "4096/fs", "8192/fs", "16384", "32768", "65536", "131072"};
+static const char *ml26124_alchld[] = {"128/fs", "256/fs", "512/fs", "1024/fs",
+     "2048/fs", "4096/fs", "8192/fs", "16384/fs", "32768/fs", "65536/fs", "131072/fs",
+     "262144/fs", "524288/fs", "1048576/fs", "2097152/fs"};
+static const char *ml26124_alczctm[] = {"128/fs", "256/fs", "512/fs", "1024/fs"};
+static const char *ml26124_platk[] = {"1/fs=20.8us", "2/fs", "4/fs", "8/fs",
+"16/fs", "32/fs", "64/fs", "128/fs", "256/fs", "512/fs", "1024/fs", "2048/fs",
+"4096/fs", "8192/fs", "16384/fs=341msec", "32768/fs"};
+static const char *ml26124_pldcy[] = {"4/fs", "8/fs", "16/fs", "32/fs", "64/fs",
+     "128/fs", "256/fs", "512/fs", "1024/fs", "2048/fs", "4096/fs", "8192/fs",
+     "16384", "32768", "65536", "131072"};
+static const char *ml26124_plzctm[] = {"128/fs", "256/fs", "512/fs", "1024/fs"};
+
+static const char *ml26124_input_select[] = {"Analog MIC-in", "Digital MIC-in"};
+
+static const struct soc_enum ml26124_enum[] = {
+/* #define SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts) */
+	SOC_ENUM_SINGLE(ML26124_MIXER_VOL_CTL, 0, 4, ml26124_lrmcon),
+	SOC_ENUM_SINGLE(ML26124_MIXER_VOL_CTL, 4, 15, ml26124_dvfconcon),
+	SOC_ENUM_SINGLE(ML26124_HPF2_CUTOFF, 0, 8, ml26124_hpf2cut),
+	SOC_ENUM_SINGLE(ML26124_ALC_MODE, 0, 2, ml26124_alcmode),
+	SOC_ENUM_SINGLE(ML26124_ALC_MODE, 2, 2, ml26124_ngtyp),
+	SOC_ENUM_SINGLE(ML26124_ALC_ATTACK_TIM, 0, 16, ml26124_alcatk),
+	SOC_ENUM_SINGLE(ML26124_ALC_DECAY_TIM, 0, 16, ml26124_alcdcy),
+	SOC_ENUM_SINGLE(ML26124_ALC_HOLD_TIM, 0, 16, ml26124_alchld),
+	SOC_ENUM_SINGLE(ML26124_ALC_ZERO_TIMOUT, 0, 4, ml26124_alczctm),
+	SOC_ENUM_SINGLE(ML26124_PL_ATTACKTIME, 0, 16, ml26124_platk),
+	SOC_ENUM_SINGLE(ML26124_PL_DECAYTIME, 0, 16, ml26124_pldcy),
+	SOC_ENUM_SINGLE(ML26124_PL_0CROSS_TIMOUT, 0, 4, ml26124_plzctm),
+	SOC_ENUM_SINGLE(ML26124_MIC_IF_CTL, 0, 1, ml26124_input_select),
+};
+
+/* ML26124 configuration */
+static const DECLARE_TLV_DB_SCALE(rec_play_digi_vol, -7150, 50, 0);
+static const DECLARE_TLV_DB_SCALE(digi_boost_vol, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(eq_band_gain, -7150, 50, 0);
+static const DECLARE_TLV_DB_SCALE(alclvl, -2250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(alcmingain, -1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(alcmaxgain, -675, 600, 0);
+static const DECLARE_TLV_DB_SCALE(ngth, -7650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(plilv, -2250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(plmingain, -1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(plmaxgain, -675, 600, 0);
+static const DECLARE_TLV_DB_SCALE(plvl, -1200, 75, 0);
+
+static const struct snd_kcontrol_new ml26124_snd_controls[] = {
+	SOC_SINGLE_TLV("Record Digital Volume", ML26124_RECORD_DIG_VOL, 0, 0xff, 1, rec_play_digi_vol),
+	SOC_SINGLE_TLV("Playback Digital Volume", ML26124_PLBAK_DIG_VOL, 0, 0xff, 1, rec_play_digi_vol),
+	SOC_SINGLE_TLV("Digital Boost Volume",  ML26124_DIGI_BOOST_VOL, 0, 0x3f, 0, digi_boost_vol),
+	SOC_SINGLE_TLV("EQ Band0 Gain Setting",  ML26124_EQ_GAIN_BRAND0, 0, 0xff, 1, eq_band_gain),
+	SOC_SINGLE_TLV("EQ Band1 Gain Setting",  ML26124_EQ_GAIN_BRAND1, 0, 0xff, 1, eq_band_gain),
+	SOC_SINGLE_TLV("EQ Band2 Gain Setting",  ML26124_EQ_GAIN_BRAND2, 0, 0xff, 1, eq_band_gain),
+	SOC_SINGLE_TLV("EQ Band3 Gain Setting",  ML26124_EQ_GAIN_BRAND3, 0, 0xff, 1, eq_band_gain),
+	SOC_SINGLE_TLV("EQ Band4 Gain Setting",  ML26124_EQ_GAIN_BRAND4, 0, 0xff, 1, eq_band_gain),
+	SOC_SINGLE_TLV("ALC Target Level",  ML26124_ALC_TARGET_LEV, 0, 0xf, 1, alclvl),
+	SOC_SINGLE_TLV("ALC Min Gain Control",  ML26124_ALC_MAXMIN_GAIN, 0, 7, 0, alcmingain),
+	SOC_SINGLE_TLV("ALC MAX Gain Control",  ML26124_ALC_MAXMIN_GAIN, 4, 7, 1, alcmaxgain),
+	SOC_SINGLE_TLV("Noise Gate Threshold",  ML26124_NOIS_GATE_THRSH, 0, 0x1f, 0, ngth),
+	SOC_SINGLE_TLV("Playback Limitter Target Level",  ML26124_PL_TARGETTIME, 0, 0xf, 1, plilv),
+	SOC_SINGLE_TLV("Playback Limitter Min Gain",  ML26124_PL_MAXMIN_GAIN, 0, 7, 0, plmingain),
+	SOC_SINGLE_TLV("Playback Limitter Max Gain",  ML26124_PL_MAXMIN_GAIN, 4, 7, 1, plmaxgain),
+	SOC_SINGLE_TLV("Playback Boost Volume",  ML26124_PLYBAK_BOST_VOL, 0, 0x3f, 0, plvl),
+};
+
+static const struct snd_kcontrol_new ml26124_dsp_controls[] = {
+	SOC_SINGLE("Play Limitter ON/OFF", ML26124_FILTER_EN, 0, 1, 0),
+	SOC_SINGLE("Record ALC ON/OFF", ML26124_FILTER_EN, 1, 1, 0),
+	SOC_SINGLE("Digital Volume Fade ON/OFF", ML26124_FILTER_EN, 3, 1, 0),
+	SOC_SINGLE("Ditital Volume MUTE", ML26124_FILTER_EN, 4, 1, 0),
+	SOC_SINGLE("Set ALC position", ML26124_FILTER_EN, 5, 1, 0),
+	SOC_SINGLE("Noise Gate ON/OFF", ML26124_ALC_MODE, 1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget ml26124_dapm_widgets[] = {
+	SND_SOC_DAPM_VMID("VMID"),
+	SND_SOC_DAPM_MICBIAS("MICBIAS", ML26124_PW_REF_PW_MNG, 0, 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_DAC("DAC", "Playback", ML26124_PW_DAC_PW_MNG, 1, 0),
+	SND_SOC_DAPM_SPK("Speaker", NULL),
+	SND_SOC_DAPM_LINE("LINEOUT", NULL),
+	SND_SOC_DAPM_INPUT("VIDEOIN"),
+	SND_SOC_DAPM_INPUT("MDIN"),
+	SND_SOC_DAPM_INPUT("MIN"),
+	SND_SOC_DAPM_INPUT("LIN"),
+	SND_SOC_DAPM_INPUT("SDIN"),
+	SND_SOC_DAPM_OUTPUT("VIDEOOUT"),
+	SND_SOC_DAPM_OUTPUT("SPOUT"),
+	SND_SOC_DAPM_OUTPUT("LOUT"),
+	SND_SOC_DAPM_OUTPUT("SDOUT"),
+};
+
+#define	CODEC_DEV_ADDR		(0x1A)
+
+
+static struct i2c_board_info ioh_hwmon_info[] = {
+	{I2C_BOARD_INFO("ioh_i2c-0", CODEC_DEV_ADDR + 1)},
+	{I2C_BOARD_INFO("ioh_i2c-1", CODEC_DEV_ADDR + 2)},
+	{I2C_BOARD_INFO("ioh_i2c-2", CODEC_DEV_ADDR + 3)},
+	{I2C_BOARD_INFO("ioh_i2c-3", CODEC_DEV_ADDR + 4)},
+	{I2C_BOARD_INFO("ioh_i2c-4", CODEC_DEV_ADDR + 5)},
+	{I2C_BOARD_INFO("ioh_i2c-5", CODEC_DEV_ADDR + 0)},
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+};
+
+static int snd_card_codec_reg_read(struct ml26124_priv *priv,
+				   unsigned char reg, unsigned char *val)
+{
+	unsigned char data;
+	struct i2c_client *i2c;
+
+	mutex_lock(&priv->i2c_mutex);
+
+	i2c = i2c_new_device(i2c_get_adapter(1),
+			     &ioh_hwmon_info[priv->substream->number]);
+	if (!i2c) {
+		mutex_unlock(&priv->i2c_mutex);
+		return -1;
+	}
+
+	if (i2c_master_send(i2c, &reg, 1) != 1) {
+		mutex_unlock(&priv->i2c_mutex);
+		return -1;
+	}
+
+	if (i2c_master_recv(i2c, &data, 1) != 1) {
+		mutex_unlock(&priv->i2c_mutex);
+		return -1;
+	}
+
+	*val = data;
+	i2c_unregister_device(i2c);
+	mutex_unlock(&priv->i2c_mutex);
+
+	return 0;
+}
+
+static int snd_card_codec_reg_write(struct ml26124_priv *priv,
+				    unsigned char reg, unsigned char val)
+{
+	unsigned char buf[2] = {(reg|1), val};
+	struct i2c_client *i2c;
+
+	mutex_lock(&priv->i2c_mutex);
+
+	i2c = i2c_new_device(i2c_get_adapter(1),
+			     &ioh_hwmon_info[priv->substream->number]);
+
+	if (i2c_master_send(i2c, &buf[0], 2) != 2) {
+		mutex_unlock(&priv->i2c_mutex);
+		return -1;
+	}
+
+	i2c_unregister_device(i2c);
+	mutex_unlock(&priv->i2c_mutex);
+
+	return 0;
+}
+
+static int snd_card_codec_set(int number, struct ml26124_priv *priv)
+{
+	unsigned char data;
+	unsigned int rate = priv->rate;
+	unsigned int channels = priv->ch;
+
+	snd_card_codec_reg_read(priv, 0x30, &data);	 /* Read MICVIAS Voltage */
+
+	snd_card_codec_reg_write(priv, 0x10, 0x01);	/* soft reset assert */
+	snd_card_codec_reg_write(priv, 0x10, 0x00);	/* soft reset negate */
+
+	snd_card_codec_reg_write(priv, 0x0c, 0x00);	/* Stop clock  */
+
+	switch (rate) {
+	case 16000:
+		snd_card_codec_reg_write(priv, 0x00, 0x03);
+		snd_card_codec_reg_write(priv, 0x02, 0x0c);
+		snd_card_codec_reg_write(priv, 0x04, 0x00);
+		snd_card_codec_reg_write(priv, 0x06, 0x20);
+		snd_card_codec_reg_write(priv, 0x08, 0x00);
+		snd_card_codec_reg_write(priv, 0x0a, 0x04);
+		break;
+	case 32000:
+		snd_card_codec_reg_write(priv, 0x00, 0x06);
+		snd_card_codec_reg_write(priv, 0x02, 0x0c);
+		snd_card_codec_reg_write(priv, 0x04, 0x00);
+		snd_card_codec_reg_write(priv, 0x06, 0x20);
+		snd_card_codec_reg_write(priv, 0x08, 0x00);
+		snd_card_codec_reg_write(priv, 0x0a, 0x04);
+		break;
+	case 48000:
+		snd_card_codec_reg_write(priv, 0x00, 0x08);
+		snd_card_codec_reg_write(priv, 0x02, 0x0c);
+		snd_card_codec_reg_write(priv, 0x04, 0x00);
+		snd_card_codec_reg_write(priv, 0x06, 0x30);
+		snd_card_codec_reg_write(priv, 0x08, 0x00);
+		snd_card_codec_reg_write(priv, 0x0a, 0x04);
+		break;
+	default:
+		pr_err("%s:this rate is no support for ml26124\n", __func__);
+		break;
+	}
+
+	snd_card_codec_reg_write(priv, 0x0c, 0x03); /* Start MCLK and PLL */
+	msleep(20);
+	snd_card_codec_reg_write(priv, 0x0c, 0x0f); /* Clock control: MCLKI use */
+	snd_card_codec_reg_write(priv, 0x0e, 0x04);
+
+	if (channels == 1) {
+		snd_card_codec_reg_write(priv, 0x60, 0x23); /* SAI transmitter control */
+					/* 0x23 : FMTO=1, H=Left L=Right, no-delay*/
+		snd_card_codec_reg_write(priv, 0x62, 0x23);  /* Receive side SAI control */
+	} else {
+		snd_card_codec_reg_write(priv, 0x60, 0x00);
+		snd_card_codec_reg_write(priv, 0x62, 0x00);
+	}
+
+	snd_card_codec_reg_write(priv, 0x64, 0x00); /* master/slave set slave */
+
+	snd_card_codec_reg_write(priv, 0x20, 0x02); /* VMID on. normal mode */
+	msleep(50);
+
+	snd_card_codec_reg_write(priv, 0x20, 0x06); /* Analog REference Power Managemet */
+	snd_card_codec_reg_write(priv, 0x22, 0x0a); /* Analog Input Power Management */
+	snd_card_codec_reg_write(priv, 0x24, 0x02); /* DAC power Management */
+	snd_card_codec_reg_write(priv, 0x26, 0x13);
+	snd_card_codec_reg_write(priv, 0x26, 0x1f); /* Speaker Amplified Poer Management */
+	snd_card_codec_reg_write(priv, 0x28, 0x02); /* LOUT Control Regsister */
+
+	snd_card_codec_reg_write(priv, 0x54, 0x02); /* Speaker Amplifier output Control */
+	snd_card_codec_reg_write(priv, 0x5a, 0x00); /* Mic Interface Control Register */
+
+	snd_card_codec_reg_write(priv, 0x12, 0x01); /* Record/Playback Running Control Register */
+	snd_card_codec_reg_write(priv, 0x12, 0x03);
+	msleep(20);
+	snd_card_codec_reg_write(priv, 0x66, 0x03); /* DSP filter function Enable */
+
+	snd_card_codec_reg_write(priv, 0x3a, 0x27); /* Speaker Amplifier Volume Control */
+	snd_card_codec_reg_write(priv, 0x32, 0x20); /* Mic Input Volume Control */
+
+	return 0;
+}
+
+static int ml26124_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *hw_params,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	if (snd_card_codec_set(substream->number, priv))
+		return -1;
+
+	return snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+}
+
+static int snd_card_codec_free(int number, struct ml26124_priv *priv)
+{
+	snd_card_codec_reg_write(priv, 0x10, 1);	/* soft reset assert */
+
+	return 0;
+}
+
+static int snd_card_ml7213i2s_hw_free(struct snd_pcm_substream *substream,
+				      struct snd_soc_dai *dai)
+{
+	struct ml26124_priv *priv = substream->runtime->private_data;
+
+	if (snd_card_codec_free(substream->number, priv))
+			return -1;
+
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int ml26124_pcm_prepare(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	return 0;
+}
+
+static void ml26124_shutdown(struct snd_pcm_substream *substream,
+			    struct snd_soc_dai *dai)
+{
+}
+
+#define ML26124_DVOL_CTL		0x68	/* Digital Volume Conotrol
+						   Function Enable Register */
+#define DVOL_CTL_DVMUTE_ON		BIT(4)	/* Digital volume MUTE On */
+#define DVOL_CTL_DVMUTE_OFF		0	/* Digital volume MUTE Off */
+
+static int ml26124_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	if (mute)
+		snd_card_codec_reg_write(priv, ML26124_DVOL_CTL,
+					 DVOL_CTL_DVMUTE_ON);
+	else
+		snd_card_codec_reg_write(priv, ML26124_DVOL_CTL,
+					 DVOL_CTL_DVMUTE_OFF);
+	return 0;
+}
+
+static int ml26124_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+	unsigned char mode;
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		mode = 1;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		mode = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+	snd_card_codec_reg_write(priv, ML26124_SAI_MODE_SEL, mode);
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+#define ML26124_REF_PM	0x20
+#define REF_PM_MICBEN	BIT(2)	/* MIC BIAS Enable */
+#define REF_PM_LOBIAS	BIT(6)	/* LOUT terminal BIAS Enable (1/2REGOUT) */
+#define REF_PM_VMID_ON	BIT(1)	/* VMID generation circuit ON */
+#define REF_PM_VMID_ON_FAST	BIT(0) /* VMID generation circuit Fast mode ON */
+#define REF_PM_VMID_OFF	0	/* VMID generation circuit OFF */
+
+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:
+	case SND_SOC_BIAS_PREPARE:
+	case SND_SOC_BIAS_STANDBY:
+		snd_card_codec_reg_write(priv, ML26124_REF_PM, REF_PM_VMID_ON);
+		msleep(50);
+		snd_card_codec_reg_write(priv, ML26124_REF_PM,
+					 REF_PM_VMID_ON | REF_PM_MICBEN);
+		break;
+	case SND_SOC_BIAS_OFF:
+		snd_card_codec_reg_write(priv, ML26124_REF_PM,
+					 REF_PM_VMID_OFF | REF_PM_MICBEN);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define ML26124_RATES SNDRV_PCM_RATE_8000_96000
+
+#define ML26124_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+	SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops ml26124_dai_ops = {
+	.prepare	= ml26124_pcm_prepare,
+	.hw_params	= ml26124_hw_params,
+	.hw_free	= snd_card_ml7213i2s_hw_free,
+	.shutdown	= ml26124_shutdown,
+	.digital_mute	= ml26124_mute,
+	.set_fmt	= ml26124_set_dai_fmt,
+};
+
+struct snd_soc_dai_driver ml26124_dai = {
+	.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)
+{
+	snd_soc_add_controls(codec, ml26124_snd_controls,
+			     ARRAY_SIZE(ml26124_snd_controls));
+
+	snd_soc_dapm_new_controls(codec, ml26124_dapm_widgets,
+				  ARRAY_SIZE(ml26124_dapm_widgets));
+	return 0;
+}
+
+/* power down chip */
+static int ml26124_remove(struct snd_soc_codec *codec)
+{
+	return 0;
+}
+#define REG_CACHE_SIZE		0x79
+static struct snd_soc_codec_driver soc_codec_dev_ml26124 = {
+	.probe =	ml26124_probe,
+	.remove =	ml26124_remove,
+	.suspend =	ml26124_suspend,
+	.resume =	ml26124_resume,
+	.set_bias_level = ml26124_set_bias_level,
+	.reg_cache_size = REG_CACHE_SIZE,
+	.reg_word_size = sizeof(u8),
+};
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int ml26124_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct ml26124_priv *priv;
+	int ret;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (priv == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, priv);
+	priv->control_type = SND_SOC_I2C;
+
+	ret =  snd_soc_register_codec(&i2c->dev,
+			&soc_codec_dev_ml26124, &ml26124_dai, 1);
+	if (ret < 0)
+		kfree(priv);
+
+	mutex_init(&priv->i2c_mutex);
+
+	return ret;
+}
+
+static __devexit int ml26124_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	kfree(i2c_get_clientdata(client));
+	return 0;
+}
+
+static const struct i2c_device_id ml26124_i2c_id[] = {
+	{ "ml26124", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ml26124_i2c_id);
+
+static struct i2c_driver ml26124_i2c_driver = {
+	.driver = {
+		.name = "ml26124-codec",
+		.owner = THIS_MODULE,
+	},
+	.probe =    ml26124_i2c_probe,
+	.remove =   __devexit_p(ml26124_i2c_remove),
+	.id_table = ml26124_i2c_id,
+};
+#endif
+
+static int __init ml26124_modinit(void)
+{
+	int ret = 0;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	ret = i2c_add_driver(&ml26124_i2c_driver);
+	if (ret != 0) {
+		pr_err("Failed to register ML26124 I2C driver: %d\n", ret);
+	}
+#endif
+	return ret;
+}
+module_init(ml26124_modinit);
+
+static void __exit ml26124_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&ml26124_i2c_driver);
+#endif
+}
+module_exit(ml26124_exit);
+
+MODULE_AUTHOR("Tomoya MORINAGA <tomoya-linux@dsn.lapis-semi.com>");
+MODULE_DESCRIPTION("LAPIS Semiconductor ML26124 ALSA SoC codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ml26124.h b/sound/soc/codecs/ml26124.h
new file mode 100644
index 0000000..c1de498
--- /dev/null
+++ b/sound/soc/codecs/ml26124.h
@@ -0,0 +1,123 @@
+/*
+ * 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
+//#define ML26124_CTL			0x
+
+/* 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_VOL_CTL_EN		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
+
+#endif
-- 
1.7.4.4


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

* [PATCH 2/3] sound/soc/lapis: add machine driver
  2011-11-21  4:08 [PATCH 1/3] sound/soc/codecs: add LAPIS Semiconductor ML26124 Tomoya MORINAGA
@ 2011-11-21  4:08 ` Tomoya MORINAGA
  2011-11-21 11:34   ` Mark Brown
  2011-11-21  4:08 ` [PATCH 3/3] sound/soc/lapis: add platform driver Tomoya MORINAGA
  2011-11-21 11:26 ` [PATCH 1/3] sound/soc/codecs: add LAPIS Semiconductor ML26124 Mark Brown
  2 siblings, 1 reply; 27+ messages in thread
From: Tomoya MORINAGA @ 2011-11-21  4:08 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Lars-Peter Clausen, Dimitris Papastamos, Mike Frysinger,
	Daniel Mack, alsa-devel, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, Tomoya MORINAGA

Add machine driver for LAPIS Semiconductor ML7213 IOH.

Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>
---
 sound/soc/Kconfig                   |    1 +
 sound/soc/Makefile                  |    1 +
 sound/soc/lapis/Kconfig             |    6 ++
 sound/soc/lapis/Makefile            |    4 ++
 sound/soc/lapis/ml7213ioh-machine.c |   87 +++++++++++++++++++++++++++++++++++
 5 files changed, 99 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/lapis/Kconfig
 create mode 100644 sound/soc/lapis/Makefile
 create mode 100644 sound/soc/lapis/ml7213ioh-machine.c

diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 1381db8..674142b 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -60,6 +60,7 @@ source "sound/soc/s6000/Kconfig"
 source "sound/soc/sh/Kconfig"
 source "sound/soc/tegra/Kconfig"
 source "sound/soc/txx9/Kconfig"
+source "sound/soc/lapis/Kconfig"
 
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 9ea8ac8..6586d99 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_SND_SOC)	+= s6000/
 obj-$(CONFIG_SND_SOC)	+= sh/
 obj-$(CONFIG_SND_SOC)	+= tegra/
 obj-$(CONFIG_SND_SOC)	+= txx9/
+obj-$(CONFIG_SND_SOC)	+= lapis/
diff --git a/sound/soc/lapis/Kconfig b/sound/soc/lapis/Kconfig
new file mode 100644
index 0000000..1bd5200
--- /dev/null
+++ b/sound/soc/lapis/Kconfig
@@ -0,0 +1,6 @@
+config SND_SOC_ML7213_MACHINE
+	tristate "ML7213 IOH ASoC machine driver"
+	depends on SND_SOC_TXX9ACLC
+	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
new file mode 100644
index 0000000..468d922
--- /dev/null
+++ b/sound/soc/lapis/Makefile
@@ -0,0 +1,4 @@
+# Platform
+snd-soc-ml7213-machine-objs := ml7213ioh-machine.o
+
+obj-$(CONFIG_SND_SOC_ML7213_MACHINE) += snd-soc-ml7213-plat.o
diff --git a/sound/soc/lapis/ml7213ioh-machine.c b/sound/soc/lapis/ml7213ioh-machine.c
new file mode 100644
index 0000000..7f5629c
--- /dev/null
+++ b/sound/soc/lapis/ml7213ioh-machine.c
@@ -0,0 +1,87 @@
+/*
+ * 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>
+
+static struct snd_soc_dai_link ioh_i2s_dai = {
+	.name = "I2S",
+	.stream_name = "I2S HiFi",
+	.cpu_dai_name = "ml7213ioh-i2s",
+	.codec_dai_name = "ml7213ioh-hifi",
+	.platform_name	= "ml7213ioh-pcm-audio",
+	.codec_name	= "ml26124-codec",
+};
+
+static struct snd_soc_card ioh_i2s_card = {
+	.name		= "ml7213ioh i2s",
+	.dai_link	= &ioh_i2s_dai,
+	.num_links	= 1,
+};
+
+static struct platform_device *soc_pdev;
+
+static int __init ioh_i2s_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	soc_pdev = platform_device_alloc("soc-audio", -1);
+	if (!soc_pdev)
+		return -ENOMEM;
+	platform_set_drvdata(soc_pdev, &ioh_i2s_card);
+	ret = platform_device_add(soc_pdev);
+	if (ret) {
+		platform_device_put(soc_pdev);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __exit ioh_i2s_remove(struct platform_device *pdev)
+{
+	platform_device_unregister(soc_pdev);
+	return 0;
+}
+
+static struct platform_driver ioh_i2s_driver = {
+	.remove = ioh_i2s_remove,
+	.driver = {
+		.name = "ml7213ioh-machine",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init ioh_i2s_init(void)
+{
+	return platform_driver_probe(&ioh_i2s_driver,
+				     ioh_i2s_probe);
+}
+
+static void __exit ioh_i2s_exit(void)
+{
+	platform_driver_unregister(&ioh_i2s_driver);
+}
+
+module_init(ioh_i2s_init);
+module_exit(ioh_i2s_exit);
+
+MODULE_AUTHOR("Tomoya MORINAGA <tomoya-linux@dsn.lapis-semi.com>");
+MODULE_DESCRIPTION("LAPIS Semiconductor ML7213 IOH ALSA SoC machine driver");
+MODULE_LICENSE("GPL");
-- 
1.7.4.4


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

* [PATCH 3/3] sound/soc/lapis: add platform driver
  2011-11-21  4:08 [PATCH 1/3] sound/soc/codecs: add LAPIS Semiconductor ML26124 Tomoya MORINAGA
  2011-11-21  4:08 ` [PATCH 2/3] sound/soc/lapis: add machine driver Tomoya MORINAGA
@ 2011-11-21  4:08 ` Tomoya MORINAGA
  2011-11-21 11:45   ` Mark Brown
  2011-11-21 11:26 ` [PATCH 1/3] sound/soc/codecs: add LAPIS Semiconductor ML26124 Mark Brown
  2 siblings, 1 reply; 27+ messages in thread
From: Tomoya MORINAGA @ 2011-11-21  4:08 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Lars-Peter Clausen, Dimitris Papastamos, Mike Frysinger,
	Daniel Mack, alsa-devel, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, Tomoya MORINAGA

Add platform driver for LAPIS Semiconductor ML7213 IOH

Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>
---
 sound/soc/lapis/Kconfig          |    6 +
 sound/soc/lapis/Makefile         |    2 +
 sound/soc/lapis/ioh_i2s.h        |  118 ++
 sound/soc/lapis/ioh_i2s_config.h | 1978 ++++++++++++++++++++++++++++++
 sound/soc/lapis/ml7213ioh-plat.c | 2488 ++++++++++++++++++++++++++++++++++++++
 sound/soc/lapis/ml7213ioh-plat.h |  213 ++++
 6 files changed, 4805 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/lapis/ioh_i2s.h
 create mode 100644 sound/soc/lapis/ioh_i2s_config.h
 create mode 100644 sound/soc/lapis/ml7213ioh-plat.c
 create mode 100644 sound/soc/lapis/ml7213ioh-plat.h

diff --git a/sound/soc/lapis/Kconfig b/sound/soc/lapis/Kconfig
index 1bd5200..fc7774f 100644
--- a/sound/soc/lapis/Kconfig
+++ b/sound/soc/lapis/Kconfig
@@ -1,3 +1,9 @@
+config SND_SOC_ML7213_PLATFORM
+	tristate "ML7213 IOH ASoC platform driver"
+	depends on HAS_TXX9_ACLC && TXX9_DMAC
+	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"
 	depends on SND_SOC_TXX9ACLC
diff --git a/sound/soc/lapis/Makefile b/sound/soc/lapis/Makefile
index 468d922..142ba96 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-machine.o
 obj-$(CONFIG_SND_SOC_ML7213_MACHINE) += 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..7474d6f
--- /dev/null
+++ b/sound/soc/lapis/ioh_i2s.h
@@ -0,0 +1,118 @@
+/*
+ * 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
+
+#define I2S_AEMPTY_THRESH	64	/* Almost  Empty Threshold */
+#define I2S_AFULL_THRESH	64	/* Almost  Full Threshold */
+
+#define MAX_I2S_RX_CH	MAX_I2S_CH
+#define MAX_I2S_TX_CH	MAX_I2S_CH
+
+#define	INTER_BUFF_SIZE		(I2S_AEMPTY_THRESH*4 \
+				 *I2S_DMA_SG_NUM \
+				 *I2S_DMA_SG_MAX)
+#define	I2S_DMA_SG_NUM		(128)
+#define	I2S_DMA_SG_MAX		(64)
+
+struct ioh_i2s_data;
+
+
+enum ioh_direction {
+	IOH_PLAYBACK = 0,
+	IOH_CAPTURE,
+};
+
+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,
+};
+
+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;
+};
+
+/* For power management save/retore use */
+struct ioh_i2s_pm_common_reg {
+	u32 i2sclkcnt[6];
+	u32 i2simask;
+};
+
+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 */
+};
+
+#endif
diff --git a/sound/soc/lapis/ioh_i2s_config.h b/sound/soc/lapis/ioh_i2s_config.h
new file mode 100644
index 0000000..a71d7dd
--- /dev/null
+++ b/sound/soc/lapis/ioh_i2s_config.h
@@ -0,0 +1,1978 @@
+/*
+ * 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_CONFIG
+#define ML7213_IOH_I2S_CONFIG
+
+#include "ioh_i2s.h"
+
+#define	IOH_I2S_USE_PARAM	(1)
+#define	I2S_SUPPORT_FS_NUM	(7)
+
+#define	PERIOD_POS_MAX		(I2S_DMA_SG_NUM)
+#define	PERIOD_LEN_TX		(I2S_AEMPTY_THRESH * PERIOD_POS_MAX)
+#define	PERIOD_LEN_RX		(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_TX	(PERIOD_LEN_TX*4)
+#define	MAX_PERIOD_SIZE_RX	(PERIOD_LEN_RX*4)
+
+#if (PERIOD_LEN_TX < (I2S_AEMPTY_THRESH*I2S_DMA_SG_NUM))
+	#error IOH_I2S_CONFIG Error : PERIOD_LEN_TX is enlarged more.
+#endif
+
+#if (PERIOD_LEN_RX < (I2S_AFULL_THRESH*I2S_DMA_SG_NUM))
+	#error IOH_I2S_CONFIG Error : PERIOD_LEN_RX is enlarged more.
+#endif
+
+
+/* ######################################################################## */
+/* ###		Parameter setup possible value				### */
+/* ######################################################################## */
+/* ioh_mssel_t */
+#define	ioh_mssel_slave		(0)
+#define	ioh_mssel_master	(1)
+
+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,
+};
+
+/* ioh_dabit_t */
+#define	ioh_dabit_8bit		(0)
+#define	ioh_dabit_16bit		(2)
+#define	ioh_dabit_24bit		(5)
+
+/* ioh_bclkfs_t */
+#define	ioh_bclkfs_8fs		(0)
+#define	ioh_bclkfs_16fs		(1)
+#define	ioh_bclkfs_32fs		(2)
+#define	ioh_bclkfs_64fs		(3)
+
+/* ioh_tel_t */
+#define	ioh_tel_i2s_fmt		(0)
+#define	ioh_tel_tel_fmt		(1)
+
+enum ioh_dlyoff_t {
+	ioh_dlyoff_dly_on = 0,		/* date delat on */
+	ioh_dlyoff_dly_off,		/* date delat off */
+};
+
+/* ioh_lsb_t */
+#define	ioh_lsb_msb_first	(0)
+#define	ioh_lsb_lsb_first	(1)
+
+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_aft_front = 0,
+	ioh_aft_back,
+};
+
+
+struct i2s_config_tab_t {
+	unsigned int i2sclkcnt;
+	unsigned int i2scnttx;
+	unsigned int i2scntrx;
+	unsigned int i2s_mclk;
+};
+
+struct i2s_config_rate_sub_t {
+	unsigned int rate;
+	unsigned int mclkfs;
+};
+
+struct i2s_config_rate_t {
+	struct i2s_config_rate_sub_t i2s_config_rate_sub[I2S_SUPPORT_FS_NUM];
+};
+
+
+/* ######################################################################## */
+/* ###		Parameter Config PreProcessor				### */
+/* ######################################################################## */
+
+/* I2S Config Value */
+#define USE_CHANNELS_MIN	1
+#define USE_CHANNELS_MAX	2
+
+#define	MAX_I2S_CH		6		/*I2S0 ~ I2S5*/
+
+
+/* =================== I2S CH0 config =================== */
+#define	I2S_CH0_MCLK		(12288000) /* Master Clock Frequency[Hz] */
+  #if IOH_I2S_USE_PARAM
+	#define	I2S_CH0_FS_16000	16000
+	#define	I2S_CH0_FS_32000	32000
+	#define	I2S_CH0_FS_48000	48000
+  #else
+	#define	I2S_CH0_FS_8000		8000
+	#define	I2S_CH0_FS_11025	11025
+	#define	I2S_CH0_FS_22050	22050
+	#define	I2S_CH0_FS_44100	44100
+  #endif
+
+/* select master or slave. The value is ioh_mssel_t */
+#define	I2S_CH0_MSSEL			(ioh_mssel_master)
+/* select MCLK or MLBCLK into Master Clock. The value is enum ioh_masterclk_t */
+#define	I2S_CH0_MASTERCLKSEL		(ioh_masterclksel_mclk)
+
+	/* I2S CH0 stereo config */
+/* select BCLK polarity. The value is enum ioh_bclkpol_t */
+#define	I2S_CH0_BCLKPOL_STEREO		(ioh_bclkpol_falling)
+/* select DAI format. The value is enum ioh_lrckfmt_t */
+#define	I2S_CH0_LRCKFMT_STEREO		(ioh_lrclkfmt_i2s)
+
+/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH0_TX_DLYOFF_STEREO	(ioh_dlyoff_dly_on)
+/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH0_TX_LSB_STEREO		(ioh_lsb_msb_first)
+/* select LRCLK polarity of TX side. The value is ioh_lrpol_t */
+#define	I2S_CH0_TX_LRPOL_STEREO		(ioh_lrpol_no_invert)
+/* select transmit data front or back of TX side. The value is ioh_aft_t */
+#define	I2S_CH0_TX_AFT_STEREO		(ioh_aft_front)
+
+/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH0_RX_DLYOFF_STEREO	(ioh_dlyoff_dly_on)
+/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH0_RX_LSB_STEREO		(ioh_lsb_msb_first)
+/* select LRCLK polarity of RX side. The value is ioh_lrpol_t */
+#define	I2S_CH0_RX_LRPOL_STEREO		(ioh_lrpol_no_invert)
+/* select transmit data front or back of RX side. The value is ioh_aft_t */
+#define	I2S_CH0_RX_AFT_STEREO		(ioh_aft_front)
+
+	/* I2S CH0 monaural config */
+/* select BCLK polarity. The value is enum ioh_bclkpol_t */
+#define	I2S_CH0_BCLKPOL_MONO		(ioh_bclkpol_falling)
+/* select DAI format. The value is enum ioh_lrckfmt_t */
+#define	I2S_CH0_LRCKFMT_MONO		(ioh_lrclkfmt_longframe)
+
+/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH0_TX_DLYOFF_MONO		(ioh_dlyoff_dly_off)
+/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH0_TX_LSB_MONO		(ioh_lsb_msb_first)
+/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH0_RX_DLYOFF_MONO		(ioh_dlyoff_dly_off)
+/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH0_RX_LSB_MONO		(ioh_lsb_msb_first)
+
+
+/* =================== I2S CH1 config =================== */
+#define	I2S_CH1_MCLK		(12288000) /* Master Clock Frequency[Hz] */
+  #if IOH_I2S_USE_PARAM
+	#define	I2S_CH1_FS_16000	16000
+	#define	I2S_CH1_FS_32000	32000
+	#define	I2S_CH1_FS_48000	48000
+  #else
+	#define	I2S_CH1_FS_8000		8000
+	#define	I2S_CH1_FS_11025	11025
+	#define	I2S_CH1_FS_22050	22050
+	#define	I2S_CH1_FS_44100	44100
+  #endif
+
+/* select master or slave. The value is ioh_mssel_t */
+#define	I2S_CH1_MSSEL			(ioh_mssel_master)
+/* select MCLK or MLBCLK into Master Clock. The value is enum ioh_masterclk_t */
+#define	I2S_CH1_MASTERCLKSEL		(ioh_masterclksel_mclk)
+
+	/* I2S CH1 stereo config */
+/* select BCLK polarity. The value is enum ioh_bclkpol_t */
+#define	I2S_CH1_BCLKPOL_STEREO		(ioh_bclkpol_falling)
+/* select DAI format. The value is enum ioh_lrckfmt_t */
+#define	I2S_CH1_LRCKFMT_STEREO		(ioh_lrclkfmt_i2s)
+
+/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH1_TX_DLYOFF_STEREO	(ioh_dlyoff_dly_on)
+/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH1_TX_LSB_STEREO		(ioh_lsb_msb_first)
+/* select LRCLK polarity of TX side. The value is ioh_lrpol_t */
+#define	I2S_CH1_TX_LRPOL_STEREO		(ioh_lrpol_no_invert)
+/* select transmit data front or back of TX side. The value is ioh_aft_t */
+#define	I2S_CH1_TX_AFT_STEREO		(ioh_aft_front)
+
+/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH1_RX_DLYOFF_STEREO	(ioh_dlyoff_dly_on)
+/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH1_RX_LSB_STEREO		(ioh_lsb_msb_first)
+/* select LRCLK polarity of RX side. The value is ioh_lrpol_t */
+#define	I2S_CH1_RX_LRPOL_STEREO		(ioh_lrpol_no_invert)
+/* select transmit data front or back of RX side. The value is ioh_aft_t */
+#define	I2S_CH1_RX_AFT_STEREO		(ioh_aft_front)
+
+	/* I2S CH1 monaural config */
+/* select BCLK polarity. The value is enum ioh_bclkpol_t */
+#define	I2S_CH1_BCLKPOL_MONO		(ioh_bclkpol_falling)
+/* select DAI format. The value is enum ioh_lrckfmt_t */
+#define	I2S_CH1_LRCKFMT_MONO		(ioh_lrclkfmt_longframe)
+
+/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH1_TX_DLYOFF_MONO		(ioh_dlyoff_dly_off)
+/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH1_TX_LSB_MONO		(ioh_lsb_msb_first)
+/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH1_RX_DLYOFF_MONO		(ioh_dlyoff_dly_off)
+/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH1_RX_LSB_MONO		(ioh_lsb_msb_first)
+
+
+/* =================== I2S CH2 config =================== */
+#define	I2S_CH2_MCLK		(12288000) /* Master Clock Frequency[Hz] */
+  #if IOH_I2S_USE_PARAM
+	#define	I2S_CH2_FS_16000	16000
+	#define	I2S_CH2_FS_32000	32000
+	#define	I2S_CH2_FS_48000	48000
+  #else
+	#define	I2S_CH2_FS_8000		8000
+	#define	I2S_CH2_FS_11025	11025
+	#define	I2S_CH2_FS_22050	22050
+	#define	I2S_CH2_FS_44100	44100
+  #endif
+
+/* select master or slave. The value is ioh_mssel_t */
+#define	I2S_CH2_MSSEL			(ioh_mssel_master)
+/* select MCLK or MLBCLK into Master Clock. The value is enum ioh_masterclk_t */
+#define	I2S_CH2_MASTERCLKSEL		(ioh_masterclksel_mclk)
+
+	/* I2S CH2 stereo config */
+/* select BCLK polarity. The value is enum ioh_bclkpol_t */
+#define	I2S_CH2_BCLKPOL_STEREO		(ioh_bclkpol_falling)
+/* select DAI format. The value is enum ioh_lrckfmt_t */
+#define	I2S_CH2_LRCKFMT_STEREO		(ioh_lrclkfmt_i2s)
+
+/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH2_TX_DLYOFF_STEREO	(ioh_dlyoff_dly_on)
+/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH2_TX_LSB_STEREO		(ioh_lsb_msb_first)
+/* select LRCLK polarity of TX side. The value is ioh_lrpol_t */
+#define	I2S_CH2_TX_LRPOL_STEREO		(ioh_lrpol_no_invert)
+/* select transmit data front or back of TX side. The value is ioh_aft_t */
+#define	I2S_CH2_TX_AFT_STEREO		(ioh_aft_front)
+
+/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH2_RX_DLYOFF_STEREO	(ioh_dlyoff_dly_on)
+/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH2_RX_LSB_STEREO		(ioh_lsb_msb_first)
+/* select LRCLK polarity of RX side. The value is ioh_lrpol_t */
+#define	I2S_CH2_RX_LRPOL_STEREO		(ioh_lrpol_no_invert)
+/* select transmit data front or back of RX side. The value is ioh_aft_t */
+#define	I2S_CH2_RX_AFT_STEREO		(ioh_aft_front)
+
+	/* I2S CH2 monaural config */
+/* select BCLK polarity. The value is enum ioh_bclkpol_t */
+#define	I2S_CH2_BCLKPOL_MONO		(ioh_bclkpol_falling)
+/* select DAI format. The value is enum ioh_lrckfmt_t */
+#define	I2S_CH2_LRCKFMT_MONO		(ioh_lrclkfmt_longframe)
+
+/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH2_TX_DLYOFF_MONO		(ioh_dlyoff_dly_off)
+/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH2_TX_LSB_MONO		(ioh_lsb_msb_first)
+/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH2_RX_DLYOFF_MONO		(ioh_dlyoff_dly_off)
+/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH2_RX_LSB_MONO		(ioh_lsb_msb_first)
+
+
+/* =================== I2S CH3 config =================== */
+#define	I2S_CH3_MCLK		(12288000) /* Master Clock Frequency[Hz] */
+  #if IOH_I2S_USE_PARAM
+	#define	I2S_CH3_FS_16000	16000
+	#define	I2S_CH3_FS_32000	32000
+	#define	I2S_CH3_FS_48000	48000
+  #else
+	#define	I2S_CH3_FS_8000		8000
+	#define	I2S_CH3_FS_11025	11025
+	#define	I2S_CH3_FS_22050	22050
+	#define	I2S_CH3_FS_44100	44100
+  #endif
+
+/* select master or slave. The value is ioh_mssel_t */
+#define	I2S_CH3_MSSEL			(ioh_mssel_master)
+/* select MCLK or MLBCLK into Master Clock. The value is enum ioh_masterclk_t */
+#define	I2S_CH3_MASTERCLKSEL		(ioh_masterclksel_mclk)
+
+	/* I2S CH3 stereo config */
+/* select BCLK polarity. The value is enum ioh_bclkpol_t */
+#define	I2S_CH3_BCLKPOL_STEREO		(ioh_bclkpol_falling)
+/* select DAI format. The value is enum ioh_lrckfmt_t */
+#define	I2S_CH3_LRCKFMT_STEREO		(ioh_lrclkfmt_i2s)
+
+/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH3_TX_DLYOFF_STEREO	(ioh_dlyoff_dly_on)
+/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH3_TX_LSB_STEREO		(ioh_lsb_msb_first)
+/* select LRCLK polarity of TX side. The value is ioh_lrpol_t */
+#define	I2S_CH3_TX_LRPOL_STEREO		(ioh_lrpol_no_invert)
+/* select transmit data front or back of TX side. The value is ioh_aft_t */
+#define	I2S_CH3_TX_AFT_STEREO		(ioh_aft_front)
+
+/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH3_RX_DLYOFF_STEREO	(ioh_dlyoff_dly_on)
+/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH3_RX_LSB_STEREO		(ioh_lsb_msb_first)
+/* select LRCLK polarity of RX side. The value is ioh_lrpol_t */
+#define	I2S_CH3_RX_LRPOL_STEREO		(ioh_lrpol_no_invert)
+/* select transmit data front or back of RX side. The value is ioh_aft_t */
+#define	I2S_CH3_RX_AFT_STEREO		(ioh_aft_front)
+
+	/* I2S CH3 monaural config */
+/* select BCLK polarity. The value is enum ioh_bclkpol_t */
+#define	I2S_CH3_BCLKPOL_MONO		(ioh_bclkpol_falling)
+/* select DAI format. The value is enum ioh_lrckfmt_t */
+#define	I2S_CH3_LRCKFMT_MONO		(ioh_lrclkfmt_longframe)
+
+/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH3_TX_DLYOFF_MONO		(ioh_dlyoff_dly_off)
+/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH3_TX_LSB_MONO		(ioh_lsb_msb_first)
+/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH3_RX_DLYOFF_MONO		(ioh_dlyoff_dly_off)
+/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH3_RX_LSB_MONO		(ioh_lsb_msb_first)
+
+
+/* =================== I2S CH4 config =================== */
+#define	I2S_CH4_MCLK		(12288000) /* Master Clock Frequency[Hz] */
+  #if IOH_I2S_USE_PARAM
+	#define	I2S_CH4_FS_16000	16000
+	#define	I2S_CH4_FS_32000	32000
+	#define	I2S_CH4_FS_48000	48000
+  #else
+	#define	I2S_CH4_FS_8000		8000
+	#define	I2S_CH4_FS_11025	11025
+	#define	I2S_CH4_FS_22050	22050
+	#define	I2S_CH4_FS_44100	44100
+  #endif
+
+/* select master or slave. The value is ioh_mssel_t */
+#define	I2S_CH4_MSSEL			(ioh_mssel_master)
+/* select MCLK or MLBCLK into Master Clock. The value is enum ioh_masterclk_t */
+#define	I2S_CH4_MASTERCLKSEL		(ioh_masterclksel_mclk)
+
+	/* I2S CH4 stereo config */
+/* select BCLK polarity. The value is enum ioh_bclkpol_t */
+#define	I2S_CH4_BCLKPOL_STEREO		(ioh_bclkpol_falling)
+/* select DAI format. The value is enum ioh_lrckfmt_t */
+#define	I2S_CH4_LRCKFMT_STEREO		(ioh_lrclkfmt_i2s)
+
+/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH4_TX_DLYOFF_STEREO	(ioh_dlyoff_dly_on)
+/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH4_TX_LSB_STEREO		(ioh_lsb_msb_first)
+/* select LRCLK polarity of TX side. The value is ioh_lrpol_t */
+#define	I2S_CH4_TX_LRPOL_STEREO		(ioh_lrpol_no_invert)
+/* select transmit data front or back of TX side. The value is ioh_aft_t */
+#define	I2S_CH4_TX_AFT_STEREO		(ioh_aft_front)
+
+/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH4_RX_DLYOFF_STEREO	(ioh_dlyoff_dly_on)
+/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH4_RX_LSB_STEREO		(ioh_lsb_msb_first)
+/* select LRCLK polarity of RX side. The value is ioh_lrpol_t */
+#define	I2S_CH4_RX_LRPOL_STEREO		(ioh_lrpol_no_invert)
+/* select transmit data front or back of RX side. The value is ioh_aft_t */
+#define	I2S_CH4_RX_AFT_STEREO		(ioh_aft_front)
+
+	/* I2S CH4 monaural config */
+/* select BCLK polarity. The value is enum ioh_bclkpol_t */
+#define	I2S_CH4_BCLKPOL_MONO		(ioh_bclkpol_falling)
+/* select DAI format. The value is enum ioh_lrckfmt_t */
+#define	I2S_CH4_LRCKFMT_MONO		(ioh_lrclkfmt_longframe)
+
+/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH4_TX_DLYOFF_MONO		(ioh_dlyoff_dly_off)
+/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH4_TX_LSB_MONO		(ioh_lsb_msb_first)
+/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH4_RX_DLYOFF_MONO		(ioh_dlyoff_dly_off)
+/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH4_RX_LSB_MONO		(ioh_lsb_msb_first)
+
+
+/* =================== I2S CH5 config =================== */
+#define	I2S_CH5_MCLK		(12288000) /* Master Clock Frequency[Hz] */
+  #if IOH_I2S_USE_PARAM
+	#define	I2S_CH5_FS_16000	16000
+	#define	I2S_CH5_FS_32000	32000
+	#define	I2S_CH5_FS_48000	48000
+  #else
+	#define	I2S_CH5_FS_8000		8000
+	#define	I2S_CH5_FS_11025	11025
+	#define	I2S_CH5_FS_22050	22050
+	#define	I2S_CH5_FS_44100	44100
+  #endif
+
+/* select master or slave. The value is ioh_mssel_t */
+#define	I2S_CH5_MSSEL			(ioh_mssel_master)
+/* select MCLK or MLBCLK into Master Clock. The value is enum ioh_masterclk_t */
+#define	I2S_CH5_MASTERCLKSEL		(ioh_masterclksel_mclk)
+
+	/* I2S CH5 stereo config */
+/* select BCLK polarity. The value is enum ioh_bclkpol_t */
+#define	I2S_CH5_BCLKPOL_STEREO		(ioh_bclkpol_falling)
+/* select DAI format. The value is enum ioh_lrckfmt_t */
+#define	I2S_CH5_LRCKFMT_STEREO		(ioh_lrclkfmt_i2s)
+
+/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH5_TX_DLYOFF_STEREO	(ioh_dlyoff_dly_on)
+/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH5_TX_LSB_STEREO		(ioh_lsb_msb_first)
+/* select LRCLK polarity of TX side. The value is ioh_lrpol_t */
+#define	I2S_CH5_TX_LRPOL_STEREO		(ioh_lrpol_no_invert)
+/* select transmit data front or back of TX side. The value is ioh_aft_t */
+#define	I2S_CH5_TX_AFT_STEREO		(ioh_aft_front)
+
+/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH5_RX_DLYOFF_STEREO	(ioh_dlyoff_dly_on)
+/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH5_RX_LSB_STEREO		(ioh_lsb_msb_first)
+/* select LRCLK polarity of RX side. The value is ioh_lrpol_t */
+#define	I2S_CH5_RX_LRPOL_STEREO		(ioh_lrpol_no_invert)
+/* select transmit data front or back of RX side. The value is ioh_aft_t */
+#define	I2S_CH5_RX_AFT_STEREO		(ioh_aft_front)
+
+	/* I2S CH5 monaural config */
+/* select BCLK polarity. The value is enum ioh_bclkpol_t */
+#define	I2S_CH5_BCLKPOL_MONO		(ioh_bclkpol_falling)
+/* select DAI format. The value is enum ioh_lrckfmt_t */
+#define	I2S_CH5_LRCKFMT_MONO		(ioh_lrclkfmt_longframe)
+
+/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH5_TX_DLYOFF_MONO		(ioh_dlyoff_dly_off)
+/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH5_TX_LSB_MONO		(ioh_lsb_msb_first)
+/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
+#define	I2S_CH5_RX_DLYOFF_MONO		(ioh_dlyoff_dly_off)
+/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
+#define	I2S_CH5_RX_LSB_MONO		(ioh_lsb_msb_first)
+
+
+/* ######################################################################## */
+/* ###		Parameter Check PreProcessor				### */
+/* ######################################################################## */
+
+/* ===== CH0 Parameter Error Check ==== */
+#define	USE_RATE_MAX_CH0	(0)
+#ifdef	I2S_CH0_FS_8000
+	#define	USE_RATE_CH0_8000	SNDRV_PCM_RATE_8000
+	#define	USE_RATE_MIN_CH0	(8000)
+	#if (USE_RATE_MAX_CH0 < 8000)
+		#undef	USE_RATE_MAX_CH0
+		#define	USE_RATE_MAX_CH0	(8000)
+	#endif
+#else
+	#define	USE_RATE_CH0_8000	0
+#endif
+
+#ifdef	I2S_CH0_FS_11025
+	#define	USE_RATE_CH0_11025	SNDRV_PCM_RATE_11025
+	#ifndef	USE_RATE_MIN_CH0
+		#define	USE_RATE_MIN_CH0	(11025)
+	#endif
+	#if (USE_RATE_MAX_CH0 < 11025)
+		#undef	USE_RATE_MAX_CH0
+		#define	USE_RATE_MAX_CH0	(11025)
+	#endif
+#else
+	#define	USE_RATE_CH0_11025	0
+#endif
+
+#ifdef	I2S_CH0_FS_16000
+	#define	USE_RATE_CH0_16000	SNDRV_PCM_RATE_16000
+	#ifndef	USE_RATE_MIN_CH0
+		#define	USE_RATE_MIN_CH0	(16000)
+	#endif
+	#if (USE_RATE_MAX_CH0 < 16000)
+		#undef	USE_RATE_MAX_CH0
+		#define	USE_RATE_MAX_CH0	(16000)
+	#endif
+#else
+	#define	USE_RATE_CH0_16000	0
+#endif
+
+#ifdef	I2S_CH0_FS_22050
+	#define	USE_RATE_CH0_22050	SNDRV_PCM_RATE_22050
+	#ifndef	USE_RATE_MIN_CH0
+		#define	USE_RATE_MIN_CH0	(22050)
+	#endif
+	#if (USE_RATE_MAX_CH0 < 22050)
+		#undef	USE_RATE_MAX_CH0
+		#define	USE_RATE_MAX_CH0	(22050)
+	#endif
+#else
+	#define	USE_RATE_CH0_22050	0
+#endif
+
+#ifdef	I2S_CH0_FS_32000
+	#define	USE_RATE_CH0_32000	SNDRV_PCM_RATE_32000
+	#ifndef	USE_RATE_MIN_CH0
+		#define	USE_RATE_MIN_CH0	(32000)
+	#endif
+	#if (USE_RATE_MAX_CH0 < 32000)
+		#undef	USE_RATE_MAX_CH0
+		#define	USE_RATE_MAX_CH0	(32000)
+	#endif
+#else
+	#define	USE_RATE_CH0_32000	0
+#endif
+
+#ifdef	I2S_CH0_FS_44100
+	#define	USE_RATE_CH0_44100	SNDRV_PCM_RATE_44100
+	#ifndef	USE_RATE_MIN_CH0
+		#define	USE_RATE_MIN_CH0	(44100)
+	#endif
+	#if (USE_RATE_MAX_CH0 < 44100)
+		#undef	USE_RATE_MAX_CH0
+		#define	USE_RATE_MAX_CH0	(44100)
+	#endif
+#else
+	#define	USE_RATE_CH0_44100	0
+#endif
+
+#ifdef	I2S_CH0_FS_48000
+	#define	USE_RATE_CH0_48000	SNDRV_PCM_RATE_48000
+	#ifndef	USE_RATE_MIN_CH0
+		#define	USE_RATE_MIN_CH0	(48000)
+	#endif
+	#if (USE_RATE_MAX_CH0 < 48000)
+		#undef	USE_RATE_MAX_CH0
+		#define	USE_RATE_MAX_CH0	(48000)
+	#endif
+#else
+	#define	USE_RATE_CH0_48000	0
+#endif
+
+#define	USE_RATE_CH0	(USE_RATE_CH0_8000|USE_RATE_CH0_11025 |		\
+			 USE_RATE_CH0_16000|USE_RATE_CH0_22050 |	\
+			 USE_RATE_CH0_32000|USE_RATE_CH0_44100 |	\
+			 USE_RATE_CH0_48000)
+
+
+#define I2S_CH0_64FS		(I2S_CH0_MCLK/64)
+#define I2S_CH0_128FS		(I2S_CH0_MCLK/128)
+#define I2S_CH0_192FS		(I2S_CH0_MCLK/192)
+#define I2S_CH0_256FS		(I2S_CH0_MCLK/256)
+#define I2S_CH0_384FS		(I2S_CH0_MCLK/384)
+#define I2S_CH0_512FS		(I2S_CH0_MCLK/512)
+#define I2S_CH0_768FS		(I2S_CH0_MCLK/768)
+#define I2S_CH0_1024FS		(I2S_CH0_MCLK/1024)
+
+
+#if (I2S_CH0_MSSEL == ioh_mssel_master)
+#ifdef	I2S_CH0_FS_8000
+	#if (I2S_CH0_64FS != I2S_CH0_FS_8000) && \
+		(I2S_CH0_128FS != I2S_CH0_FS_8000) && \
+		(I2S_CH0_192FS != I2S_CH0_FS_8000) && \
+		(I2S_CH0_256FS != I2S_CH0_FS_8000) && \
+		(I2S_CH0_384FS != I2S_CH0_FS_8000) && \
+		(I2S_CH0_512FS != I2S_CH0_FS_8000) && \
+		(I2S_CH0_768FS != I2S_CH0_FS_8000) && \
+		(I2S_CH0_1024FS != I2S_CH0_FS_8000)
+		#error IOH_I2S_CH0_CONFIG Error : \
+			Sampling frequency 8000Hz can not generate.
+	#endif
+#endif
+
+#ifdef	I2S_CH0_FS_11025
+	#if (I2S_CH0_64FS != I2S_CH0_FS_11025) && \
+		(I2S_CH0_128FS != I2S_CH0_FS_11025) && \
+		(I2S_CH0_192FS != I2S_CH0_FS_11025) && \
+		(I2S_CH0_256FS != I2S_CH0_FS_11025) && \
+		(I2S_CH0_384FS != I2S_CH0_FS_11025) && \
+		(I2S_CH0_512FS != I2S_CH0_FS_11025) && \
+		(I2S_CH0_768FS != I2S_CH0_FS_11025) && \
+		(I2S_CH0_1024FS != I2S_CH0_FS_11025)
+		#error IOH_I2S_CH0_CONFIG Error : \
+			Sampling frequency 11025Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH0_FS_16000
+	#if (I2S_CH0_64FS != I2S_CH0_FS_16000) && \
+		(I2S_CH0_128FS != I2S_CH0_FS_16000) && \
+		(I2S_CH0_192FS != I2S_CH0_FS_16000) && \
+		(I2S_CH0_256FS != I2S_CH0_FS_16000) && \
+		(I2S_CH0_384FS != I2S_CH0_FS_16000) && \
+		(I2S_CH0_512FS != I2S_CH0_FS_16000) && \
+		(I2S_CH0_768FS != I2S_CH0_FS_16000) && \
+		(I2S_CH0_1024FS != I2S_CH0_FS_16000)
+		#error IOH_I2S_CH0_CONFIG Error : \
+			Sampling frequency 16000Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH0_FS_22050
+	#if (I2S_CH0_64FS != I2S_CH0_FS_22050) && \
+		(I2S_CH0_128FS != I2S_CH0_FS_22050) && \
+		(I2S_CH0_192FS != I2S_CH0_FS_22050) && \
+		(I2S_CH0_256FS != I2S_CH0_FS_22050) && \
+		(I2S_CH0_384FS != I2S_CH0_FS_22050) && \
+		(I2S_CH0_512FS != I2S_CH0_FS_22050) && \
+		(I2S_CH0_768FS != I2S_CH0_FS_22050) && \
+		(I2S_CH0_1024FS != I2S_CH0_FS_22050)
+		#error IOH_I2S_CH0_CONFIG Error : \
+			Sampling frequency 22050Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH0_FS_32000
+	#if (I2S_CH0_64FS != I2S_CH0_FS_32000) && \
+		(I2S_CH0_128FS != I2S_CH0_FS_32000) && \
+		(I2S_CH0_192FS != I2S_CH0_FS_32000) && \
+		(I2S_CH0_256FS != I2S_CH0_FS_32000) && \
+		(I2S_CH0_384FS != I2S_CH0_FS_32000) && \
+		(I2S_CH0_512FS != I2S_CH0_FS_32000) && \
+		(I2S_CH0_768FS != I2S_CH0_FS_32000) && \
+		(I2S_CH0_1024FS != I2S_CH0_FS_32000)
+		#error IOH_I2S_CH0_CONFIG Error : \
+			Sampling frequency 32000Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH0_FS_44100
+	#if (I2S_CH0_64FS != I2S_CH0_FS_44100) && \
+		(I2S_CH0_128FS != I2S_CH0_FS_44100) && \
+		(I2S_CH0_192FS != I2S_CH0_FS_44100) && \
+		(I2S_CH0_256FS != I2S_CH0_FS_44100) && \
+		(I2S_CH0_384FS != I2S_CH0_FS_44100) && \
+		(I2S_CH0_512FS != I2S_CH0_FS_44100) && \
+		(I2S_CH0_768FS != I2S_CH0_FS_44100) && \
+		(I2S_CH0_1024FS != I2S_CH0_FS_44100)
+		#error IOH_I2S_CH0_CONFIG Error : \
+			Sampling frequency 44100Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH0_FS_48000
+	#if (I2S_CH0_64FS != I2S_CH0_FS_48000) && \
+		(I2S_CH0_128FS != I2S_CH0_FS_48000) && \
+		(I2S_CH0_192FS != I2S_CH0_FS_48000) && \
+		(I2S_CH0_256FS != I2S_CH0_FS_48000) && \
+		(I2S_CH0_384FS != I2S_CH0_FS_48000) && \
+		(I2S_CH0_512FS != I2S_CH0_FS_48000) && \
+		(I2S_CH0_768FS != I2S_CH0_FS_48000) && \
+		(I2S_CH0_1024FS != I2S_CH0_FS_48000)
+		#error IOH_I2S_CH0_CONFIG Error : \
+			Sampling frequency 48000Hz can not generate.
+	#endif
+#endif
+#endif	/* end of [#if (I2S_CH0_MSSEL == ioh_mssel_master)] */
+
+
+/* ===== CH1 Parameter Error Check ==== */
+#define	USE_RATE_MAX_CH1	(0)
+#ifdef	I2S_CH1_FS_8000
+	#define	USE_RATE_CH1_8000	SNDRV_PCM_RATE_8000
+	#define	USE_RATE_MIN_CH1	(8000)
+	#if (USE_RATE_MAX_CH1 < 8000)
+		#undef	USE_RATE_MAX_CH1
+		#define	USE_RATE_MAX_CH1	(8000)
+	#endif
+#else
+	#define	USE_RATE_CH1_8000	0
+#endif
+
+#ifdef	I2S_CH1_FS_11025
+	#define	USE_RATE_CH1_11025	SNDRV_PCM_RATE_11025
+	#ifndef	USE_RATE_MIN_CH1
+		#define	USE_RATE_MIN_CH1	(11025)
+	#endif
+	#if (USE_RATE_MAX_CH1 < 11025)
+		#undef	USE_RATE_MAX_CH1
+		#define	USE_RATE_MAX_CH1	(11025)
+	#endif
+#else
+	#define	USE_RATE_CH1_11025	0
+#endif
+
+#ifdef	I2S_CH1_FS_16000
+	#define	USE_RATE_CH1_16000	SNDRV_PCM_RATE_16000
+	#ifndef	USE_RATE_MIN_CH1
+		#define	USE_RATE_MIN_CH1	(16000)
+	#endif
+	#if (USE_RATE_MAX_CH1 < 16000)
+		#undef	USE_RATE_MAX_CH1
+		#define	USE_RATE_MAX_CH1	(16000)
+	#endif
+#else
+	#define	USE_RATE_CH1_16000	0
+#endif
+
+#ifdef	I2S_CH1_FS_22050
+	#define	USE_RATE_CH1_22050	SNDRV_PCM_RATE_22050
+	#ifndef	USE_RATE_MIN_CH1
+		#define	USE_RATE_MIN_CH1	(22050)
+	#endif
+	#if (USE_RATE_MAX_CH1 < 22050)
+		#undef	USE_RATE_MAX_CH1
+		#define	USE_RATE_MAX_CH1	(22050)
+	#endif
+#else
+	#define	USE_RATE_CH1_22050	0
+#endif
+
+#ifdef	I2S_CH1_FS_32000
+	#define	USE_RATE_CH1_32000	SNDRV_PCM_RATE_32000
+	#ifndef	USE_RATE_MIN_CH1
+		#define	USE_RATE_MIN_CH1	(32000)
+	#endif
+	#if (USE_RATE_MAX_CH1 < 32000)
+		#undef	USE_RATE_MAX_CH1
+		#define	USE_RATE_MAX_CH1	(32000)
+	#endif
+#else
+	#define	USE_RATE_CH1_32000	0
+#endif
+
+#ifdef	I2S_CH1_FS_44100
+	#define	USE_RATE_CH1_44100	SNDRV_PCM_RATE_44100
+	#ifndef	USE_RATE_MIN_CH1
+		#define	USE_RATE_MIN_CH1	(44100)
+	#endif
+	#if (USE_RATE_MAX_CH1 < 44100)
+		#undef	USE_RATE_MAX_CH1
+		#define	USE_RATE_MAX_CH1	(44100)
+	#endif
+#else
+	#define	USE_RATE_CH1_44100	0
+#endif
+
+#ifdef	I2S_CH1_FS_48000
+	#define	USE_RATE_CH1_48000	SNDRV_PCM_RATE_48000
+	#ifndef	USE_RATE_MIN_CH1
+		#define	USE_RATE_MIN_CH1	(48000)
+	#endif
+	#if (USE_RATE_MAX_CH1 < 48000)
+		#undef	USE_RATE_MAX_CH1
+		#define	USE_RATE_MAX_CH1	(48000)
+	#endif
+#else
+	#define	USE_RATE_CH1_48000	0
+#endif
+
+#define	USE_RATE_CH1	(USE_RATE_CH1_8000|USE_RATE_CH1_11025 |		\
+			 USE_RATE_CH1_16000|USE_RATE_CH1_22050 |	\
+			 USE_RATE_CH1_32000|USE_RATE_CH1_44100 |	\
+			 USE_RATE_CH1_48000)
+
+
+#define I2S_CH1_64FS		(I2S_CH1_MCLK/64)
+#define I2S_CH1_128FS		(I2S_CH1_MCLK/128)
+#define I2S_CH1_192FS		(I2S_CH1_MCLK/192)
+#define I2S_CH1_256FS		(I2S_CH1_MCLK/256)
+#define I2S_CH1_384FS		(I2S_CH1_MCLK/384)
+#define I2S_CH1_512FS		(I2S_CH1_MCLK/512)
+#define I2S_CH1_768FS		(I2S_CH1_MCLK/768)
+#define I2S_CH1_1024FS		(I2S_CH1_MCLK/1024)
+
+
+#if (I2S_CH1_MSSEL == ioh_mssel_master)
+#ifdef	I2S_CH1_FS_8000
+	#if (I2S_CH1_64FS != I2S_CH1_FS_8000) && \
+		(I2S_CH1_128FS != I2S_CH1_FS_8000) && \
+		(I2S_CH1_192FS != I2S_CH1_FS_8000) && \
+		(I2S_CH1_256FS != I2S_CH1_FS_8000) && \
+		(I2S_CH1_384FS != I2S_CH1_FS_8000) && \
+		(I2S_CH1_512FS != I2S_CH1_FS_8000) && \
+		(I2S_CH1_768FS != I2S_CH1_FS_8000) && \
+		(I2S_CH1_1024FS != I2S_CH1_FS_8000)
+		#error IOH_I2S_CH1_CONFIG Error : \
+			Sampling frequency 8000Hz can not generate.
+	#endif
+#endif
+
+#ifdef	I2S_CH1_FS_11025
+	#if (I2S_CH1_64FS != I2S_CH1_FS_11025) && \
+		(I2S_CH1_128FS != I2S_CH1_FS_11025) && \
+		(I2S_CH1_192FS != I2S_CH1_FS_11025) && \
+		(I2S_CH1_256FS != I2S_CH1_FS_11025) && \
+		(I2S_CH1_384FS != I2S_CH1_FS_11025) && \
+		(I2S_CH1_512FS != I2S_CH1_FS_11025) && \
+		(I2S_CH1_768FS != I2S_CH1_FS_11025) && \
+		(I2S_CH1_1024FS != I2S_CH1_FS_11025)
+		#error IOH_I2S_CH1_CONFIG Error : \
+			Sampling frequency 11025Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH1_FS_16000
+	#if (I2S_CH1_64FS != I2S_CH1_FS_16000) && \
+		(I2S_CH1_128FS != I2S_CH1_FS_16000) && \
+		(I2S_CH1_192FS != I2S_CH1_FS_16000) && \
+		(I2S_CH1_256FS != I2S_CH1_FS_16000) && \
+		(I2S_CH1_384FS != I2S_CH1_FS_16000) && \
+		(I2S_CH1_512FS != I2S_CH1_FS_16000) && \
+		(I2S_CH1_768FS != I2S_CH1_FS_16000) && \
+		(I2S_CH1_1024FS != I2S_CH1_FS_16000)
+		#error IOH_I2S_CH1_CONFIG Error : \
+			Sampling frequency 16000Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH1_FS_22050
+	#if (I2S_CH1_64FS != I2S_CH1_FS_22050) && \
+		(I2S_CH1_128FS != I2S_CH1_FS_22050) && \
+		(I2S_CH1_192FS != I2S_CH1_FS_22050) && \
+		(I2S_CH1_256FS != I2S_CH1_FS_22050) && \
+		(I2S_CH1_384FS != I2S_CH1_FS_22050) && \
+		(I2S_CH1_512FS != I2S_CH1_FS_22050) && \
+		(I2S_CH1_768FS != I2S_CH1_FS_22050) && \
+		(I2S_CH1_1024FS != I2S_CH1_FS_22050)
+		#error IOH_I2S_CH1_CONFIG Error : \
+			Sampling frequency 22050Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH1_FS_32000
+	#if (I2S_CH1_64FS != I2S_CH1_FS_32000) && \
+		(I2S_CH1_128FS != I2S_CH1_FS_32000) && \
+		(I2S_CH1_192FS != I2S_CH1_FS_32000) && \
+		(I2S_CH1_256FS != I2S_CH1_FS_32000) && \
+		(I2S_CH1_384FS != I2S_CH1_FS_32000) && \
+		(I2S_CH1_512FS != I2S_CH1_FS_32000) && \
+		(I2S_CH1_768FS != I2S_CH1_FS_32000) && \
+		(I2S_CH1_1024FS != I2S_CH1_FS_32000)
+		#error IOH_I2S_CH1_CONFIG Error : \
+			Sampling frequency 32000Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH1_FS_44100
+	#if (I2S_CH1_64FS != I2S_CH1_FS_44100) && \
+		(I2S_CH1_128FS != I2S_CH1_FS_44100) && \
+		(I2S_CH1_192FS != I2S_CH1_FS_44100) && \
+		(I2S_CH1_256FS != I2S_CH1_FS_44100) && \
+		(I2S_CH1_384FS != I2S_CH1_FS_44100) && \
+		(I2S_CH1_512FS != I2S_CH1_FS_44100) && \
+		(I2S_CH1_768FS != I2S_CH1_FS_44100) && \
+		(I2S_CH1_1024FS != I2S_CH1_FS_44100)
+		#error IOH_I2S_CH1_CONFIG Error : \
+			Sampling frequency 44100Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH1_FS_48000
+	#if (I2S_CH1_64FS != I2S_CH1_FS_48000) && \
+		(I2S_CH1_128FS != I2S_CH1_FS_48000) && \
+		(I2S_CH1_192FS != I2S_CH1_FS_48000) && \
+		(I2S_CH1_256FS != I2S_CH1_FS_48000) && \
+		(I2S_CH1_384FS != I2S_CH1_FS_48000) && \
+		(I2S_CH1_512FS != I2S_CH1_FS_48000) && \
+		(I2S_CH1_768FS != I2S_CH1_FS_48000) && \
+		(I2S_CH1_1024FS != I2S_CH1_FS_48000)
+		#error IOH_I2S_CH1_CONFIG Error : \
+			Sampling frequency 48000Hz can not generate.
+	#endif
+#endif
+#endif	/* end of [#if (I2S_CH1_MSSEL == ioh_mssel_master)] */
+
+
+/* ===== CH2 Parameter Error Check ==== */
+#define	USE_RATE_MAX_CH2	(0)
+#ifdef	I2S_CH2_FS_8000
+	#define	USE_RATE_CH2_8000	SNDRV_PCM_RATE_8000
+	#define	USE_RATE_MIN_CH2	(8000)
+	#if (USE_RATE_MAX_CH2 < 8000)
+		#undef	USE_RATE_MAX_CH2
+		#define	USE_RATE_MAX_CH2	(8000)
+	#endif
+#else
+	#define	USE_RATE_CH2_8000	0
+#endif
+
+#ifdef	I2S_CH2_FS_11025
+	#define	USE_RATE_CH2_11025	SNDRV_PCM_RATE_11025
+	#ifndef	USE_RATE_MIN_CH2
+		#define	USE_RATE_MIN_CH2	(11025)
+	#endif
+	#if (USE_RATE_MAX_CH2 < 11025)
+		#undef	USE_RATE_MAX_CH2
+		#define	USE_RATE_MAX_CH2	(11025)
+	#endif
+#else
+	#define	USE_RATE_CH2_11025	0
+#endif
+
+#ifdef	I2S_CH2_FS_16000
+	#define	USE_RATE_CH2_16000	SNDRV_PCM_RATE_16000
+	#ifndef	USE_RATE_MIN_CH2
+		#define	USE_RATE_MIN_CH2	(16000)
+	#endif
+	#if (USE_RATE_MAX_CH2 < 16000)
+		#undef	USE_RATE_MAX_CH2
+		#define	USE_RATE_MAX_CH2	(16000)
+	#endif
+#else
+	#define	USE_RATE_CH2_16000	0
+#endif
+
+#ifdef	I2S_CH2_FS_22050
+	#define	USE_RATE_CH2_22050	SNDRV_PCM_RATE_22050
+	#ifndef	USE_RATE_MIN_CH2
+		#define	USE_RATE_MIN_CH2	(22050)
+	#endif
+	#if (USE_RATE_MAX_CH2 < 22050)
+		#undef	USE_RATE_MAX_CH2
+		#define	USE_RATE_MAX_CH2	(22050)
+	#endif
+#else
+	#define	USE_RATE_CH2_22050	0
+#endif
+
+#ifdef	I2S_CH2_FS_32000
+	#define	USE_RATE_CH2_32000	SNDRV_PCM_RATE_32000
+	#ifndef	USE_RATE_MIN_CH2
+		#define	USE_RATE_MIN_CH2	(32000)
+	#endif
+	#if (USE_RATE_MAX_CH2 < 32000)
+		#undef	USE_RATE_MAX_CH2
+		#define	USE_RATE_MAX_CH2	(32000)
+	#endif
+#else
+	#define	USE_RATE_CH2_32000	0
+#endif
+
+#ifdef	I2S_CH2_FS_44100
+	#define	USE_RATE_CH2_44100	SNDRV_PCM_RATE_44100
+	#ifndef	USE_RATE_MIN_CH2
+		#define	USE_RATE_MIN_CH2	(44100)
+	#endif
+	#if (USE_RATE_MAX_CH2 < 44100)
+		#undef	USE_RATE_MAX_CH2
+		#define	USE_RATE_MAX_CH2	(44100)
+	#endif
+#else
+	#define	USE_RATE_CH2_44100	0
+#endif
+
+#ifdef	I2S_CH2_FS_48000
+	#define	USE_RATE_CH2_48000	SNDRV_PCM_RATE_48000
+	#ifndef	USE_RATE_MIN_CH2
+		#define	USE_RATE_MIN_CH2	(48000)
+	#endif
+	#if (USE_RATE_MAX_CH2 < 48000)
+		#undef	USE_RATE_MAX_CH2
+		#define	USE_RATE_MAX_CH2	(48000)
+	#endif
+#else
+	#define	USE_RATE_CH2_48000	0
+#endif
+
+#define	USE_RATE_CH2	(USE_RATE_CH2_8000|USE_RATE_CH2_11025 |		\
+			 USE_RATE_CH2_16000|USE_RATE_CH2_22050 |	\
+			 USE_RATE_CH2_32000|USE_RATE_CH2_44100 |	\
+			 USE_RATE_CH2_48000)
+
+
+#define I2S_CH2_64FS		(I2S_CH2_MCLK/64)
+#define I2S_CH2_128FS		(I2S_CH2_MCLK/128)
+#define I2S_CH2_192FS		(I2S_CH2_MCLK/192)
+#define I2S_CH2_256FS		(I2S_CH2_MCLK/256)
+#define I2S_CH2_384FS		(I2S_CH2_MCLK/384)
+#define I2S_CH2_512FS		(I2S_CH2_MCLK/512)
+#define I2S_CH2_768FS		(I2S_CH2_MCLK/768)
+#define I2S_CH2_1024FS		(I2S_CH2_MCLK/1024)
+
+
+#if (I2S_CH2_MSSEL == ioh_mssel_master)
+#ifdef	I2S_CH2_FS_8000
+	#if (I2S_CH2_64FS != I2S_CH2_FS_8000) && \
+		(I2S_CH2_128FS != I2S_CH2_FS_8000) && \
+		(I2S_CH2_192FS != I2S_CH2_FS_8000) && \
+		(I2S_CH2_256FS != I2S_CH2_FS_8000) && \
+		(I2S_CH2_384FS != I2S_CH2_FS_8000) && \
+		(I2S_CH2_512FS != I2S_CH2_FS_8000) && \
+		(I2S_CH2_768FS != I2S_CH2_FS_8000) && \
+		(I2S_CH2_1024FS != I2S_CH2_FS_8000)
+		#error IOH_I2S_CH2_CONFIG Error : \
+			Sampling frequency 8000Hz can not generate.
+	#endif
+#endif
+
+#ifdef	I2S_CH2_FS_11025
+	#if (I2S_CH2_64FS != I2S_CH2_FS_11025) && \
+		(I2S_CH2_128FS != I2S_CH2_FS_11025) && \
+		(I2S_CH2_192FS != I2S_CH2_FS_11025) && \
+		(I2S_CH2_256FS != I2S_CH2_FS_11025) && \
+		(I2S_CH2_384FS != I2S_CH2_FS_11025) && \
+		(I2S_CH2_512FS != I2S_CH2_FS_11025) && \
+		(I2S_CH2_768FS != I2S_CH2_FS_11025) && \
+		(I2S_CH2_1024FS != I2S_CH2_FS_11025)
+		#error IOH_I2S_CH2_CONFIG Error : \
+			Sampling frequency 11025Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH2_FS_16000
+	#if (I2S_CH2_64FS != I2S_CH2_FS_16000) && \
+		(I2S_CH2_128FS != I2S_CH2_FS_16000) && \
+		(I2S_CH2_192FS != I2S_CH2_FS_16000) && \
+		(I2S_CH2_256FS != I2S_CH2_FS_16000) && \
+		(I2S_CH2_384FS != I2S_CH2_FS_16000) && \
+		(I2S_CH2_512FS != I2S_CH2_FS_16000) && \
+		(I2S_CH2_768FS != I2S_CH2_FS_16000) && \
+		(I2S_CH2_1024FS != I2S_CH2_FS_16000)
+		#error IOH_I2S_CH2_CONFIG Error : \
+			Sampling frequency 16000Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH2_FS_22050
+	#if (I2S_CH2_64FS != I2S_CH2_FS_22050) && \
+		(I2S_CH2_128FS != I2S_CH2_FS_22050) && \
+		(I2S_CH2_192FS != I2S_CH2_FS_22050) && \
+		(I2S_CH2_256FS != I2S_CH2_FS_22050) && \
+		(I2S_CH2_384FS != I2S_CH2_FS_22050) && \
+		(I2S_CH2_512FS != I2S_CH2_FS_22050) && \
+		(I2S_CH2_768FS != I2S_CH2_FS_22050) && \
+		(I2S_CH2_1024FS != I2S_CH2_FS_22050)
+		#error IOH_I2S_CH2_CONFIG Error : \
+			Sampling frequency 22050Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH2_FS_32000
+	#if (I2S_CH2_64FS != I2S_CH2_FS_32000) && \
+		(I2S_CH2_128FS != I2S_CH2_FS_32000) && \
+		(I2S_CH2_192FS != I2S_CH2_FS_32000) && \
+		(I2S_CH2_256FS != I2S_CH2_FS_32000) && \
+		(I2S_CH2_384FS != I2S_CH2_FS_32000) && \
+		(I2S_CH2_512FS != I2S_CH2_FS_32000) && \
+		(I2S_CH2_768FS != I2S_CH2_FS_32000) && \
+		(I2S_CH2_1024FS != I2S_CH2_FS_32000)
+		#error IOH_I2S_CH2_CONFIG Error : \
+			Sampling frequency 32000Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH2_FS_44100
+	#if (I2S_CH2_64FS != I2S_CH2_FS_44100) && \
+		(I2S_CH2_128FS != I2S_CH2_FS_44100) && \
+		(I2S_CH2_192FS != I2S_CH2_FS_44100) && \
+		(I2S_CH2_256FS != I2S_CH2_FS_44100) && \
+		(I2S_CH2_384FS != I2S_CH2_FS_44100) && \
+		(I2S_CH2_512FS != I2S_CH2_FS_44100) && \
+		(I2S_CH2_768FS != I2S_CH2_FS_44100) && \
+		(I2S_CH2_1024FS != I2S_CH2_FS_44100)
+		#error IOH_I2S_CH2_CONFIG Error : \
+			Sampling frequency 44100Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH2_FS_48000
+	#if (I2S_CH2_64FS != I2S_CH2_FS_48000) && \
+		(I2S_CH2_128FS != I2S_CH2_FS_48000) && \
+		(I2S_CH2_192FS != I2S_CH2_FS_48000) && \
+		(I2S_CH2_256FS != I2S_CH2_FS_48000) && \
+		(I2S_CH2_384FS != I2S_CH2_FS_48000) && \
+		(I2S_CH2_512FS != I2S_CH2_FS_48000) && \
+		(I2S_CH2_768FS != I2S_CH2_FS_48000) && \
+		(I2S_CH2_1024FS != I2S_CH2_FS_48000)
+		#error IOH_I2S_CH2_CONFIG Error : \
+			Sampling frequency 48000Hz can not generate.
+	#endif
+#endif
+#endif	/* end of [#if (I2S_CH2_MSSEL == ioh_mssel_master)] */
+
+
+/* ===== CH3 Parameter Error Check ==== */
+#define	USE_RATE_MAX_CH3	(0)
+#ifdef	I2S_CH3_FS_8000
+	#define	USE_RATE_CH3_8000	SNDRV_PCM_RATE_8000
+	#define	USE_RATE_MIN_CH3	(8000)
+	#if (USE_RATE_MAX_CH3 < 8000)
+		#undef	USE_RATE_MAX_CH3
+		#define	USE_RATE_MAX_CH3	(8000)
+	#endif
+#else
+	#define	USE_RATE_CH3_8000	0
+#endif
+
+#ifdef	I2S_CH3_FS_11025
+	#define	USE_RATE_CH3_11025	SNDRV_PCM_RATE_11025
+	#ifndef	USE_RATE_MIN_CH3
+		#define	USE_RATE_MIN_CH3	(11025)
+	#endif
+	#if (USE_RATE_MAX_CH3 < 11025)
+		#undef	USE_RATE_MAX_CH3
+		#define	USE_RATE_MAX_CH3	(11025)
+	#endif
+#else
+	#define	USE_RATE_CH3_11025	0
+#endif
+
+#ifdef	I2S_CH3_FS_16000
+	#define	USE_RATE_CH3_16000	SNDRV_PCM_RATE_16000
+	#ifndef	USE_RATE_MIN_CH3
+		#define	USE_RATE_MIN_CH3	(16000)
+	#endif
+	#if (USE_RATE_MAX_CH3 < 16000)
+		#undef	USE_RATE_MAX_CH3
+		#define	USE_RATE_MAX_CH3	(16000)
+	#endif
+#else
+	#define	USE_RATE_CH3_16000	0
+#endif
+
+#ifdef	I2S_CH3_FS_22050
+	#define	USE_RATE_CH3_22050	SNDRV_PCM_RATE_22050
+	#ifndef	USE_RATE_MIN_CH3
+		#define	USE_RATE_MIN_CH3	(22050)
+	#endif
+	#if (USE_RATE_MAX_CH3 < 22050)
+		#undef	USE_RATE_MAX_CH3
+		#define	USE_RATE_MAX_CH3	(22050)
+	#endif
+#else
+	#define	USE_RATE_CH3_22050	0
+#endif
+
+#ifdef	I2S_CH3_FS_32000
+	#define	USE_RATE_CH3_32000	SNDRV_PCM_RATE_32000
+	#ifndef	USE_RATE_MIN_CH3
+		#define	USE_RATE_MIN_CH3	(32000)
+	#endif
+	#if (USE_RATE_MAX_CH3 < 32000)
+		#undef	USE_RATE_MAX_CH3
+		#define	USE_RATE_MAX_CH3	(32000)
+	#endif
+#else
+	#define	USE_RATE_CH3_32000	0
+#endif
+
+#ifdef	I2S_CH3_FS_44100
+	#define	USE_RATE_CH3_44100	SNDRV_PCM_RATE_44100
+	#ifndef	USE_RATE_MIN_CH3
+		#define	USE_RATE_MIN_CH3	(44100)
+	#endif
+	#if (USE_RATE_MAX_CH3 < 44100)
+		#undef	USE_RATE_MAX_CH3
+		#define	USE_RATE_MAX_CH3	(44100)
+	#endif
+#else
+	#define	USE_RATE_CH3_44100	0
+#endif
+
+#ifdef	I2S_CH3_FS_48000
+	#define	USE_RATE_CH3_48000	SNDRV_PCM_RATE_48000
+	#ifndef	USE_RATE_MIN_CH3
+		#define	USE_RATE_MIN_CH3	(48000)
+	#endif
+	#if (USE_RATE_MAX_CH3 < 48000)
+		#undef	USE_RATE_MAX_CH3
+		#define	USE_RATE_MAX_CH3	(48000)
+	#endif
+#else
+	#define	USE_RATE_CH3_48000	0
+#endif
+
+#define	USE_RATE_CH3	(USE_RATE_CH3_8000|USE_RATE_CH3_11025 |		\
+			 USE_RATE_CH3_16000|USE_RATE_CH3_22050 |	\
+			 USE_RATE_CH3_32000|USE_RATE_CH3_44100 |	\
+			 USE_RATE_CH3_48000)
+
+
+#define I2S_CH3_64FS		(I2S_CH3_MCLK/64)
+#define I2S_CH3_128FS		(I2S_CH3_MCLK/128)
+#define I2S_CH3_192FS		(I2S_CH3_MCLK/192)
+#define I2S_CH3_256FS		(I2S_CH3_MCLK/256)
+#define I2S_CH3_384FS		(I2S_CH3_MCLK/384)
+#define I2S_CH3_512FS		(I2S_CH3_MCLK/512)
+#define I2S_CH3_768FS		(I2S_CH3_MCLK/768)
+#define I2S_CH3_1024FS		(I2S_CH3_MCLK/1024)
+
+
+#if (I2S_CH3_MSSEL == ioh_mssel_master)
+#ifdef	I2S_CH3_FS_8000
+	#if (I2S_CH3_64FS != I2S_CH3_FS_8000) && \
+		(I2S_CH3_128FS != I2S_CH3_FS_8000) && \
+		(I2S_CH3_192FS != I2S_CH3_FS_8000) && \
+		(I2S_CH3_256FS != I2S_CH3_FS_8000) && \
+		(I2S_CH3_384FS != I2S_CH3_FS_8000) && \
+		(I2S_CH3_512FS != I2S_CH3_FS_8000) && \
+		(I2S_CH3_768FS != I2S_CH3_FS_8000) && \
+		(I2S_CH3_1024FS != I2S_CH3_FS_8000)
+		#error IOH_I2S_CH3_CONFIG Error : \
+			Sampling frequency 8000Hz can not generate.
+	#endif
+#endif
+
+#ifdef	I2S_CH3_FS_11025
+	#if (I2S_CH3_64FS != I2S_CH3_FS_11025) && \
+		(I2S_CH3_128FS != I2S_CH3_FS_11025) && \
+		(I2S_CH3_192FS != I2S_CH3_FS_11025) && \
+		(I2S_CH3_256FS != I2S_CH3_FS_11025) && \
+		(I2S_CH3_384FS != I2S_CH3_FS_11025) && \
+		(I2S_CH3_512FS != I2S_CH3_FS_11025) && \
+		(I2S_CH3_768FS != I2S_CH3_FS_11025) && \
+		(I2S_CH3_1024FS != I2S_CH3_FS_11025)
+		#error IOH_I2S_CH3_CONFIG Error : \
+			Sampling frequency 11025Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH3_FS_16000
+	#if (I2S_CH3_64FS != I2S_CH3_FS_16000) && \
+		(I2S_CH3_128FS != I2S_CH3_FS_16000) && \
+		(I2S_CH3_192FS != I2S_CH3_FS_16000) && \
+		(I2S_CH3_256FS != I2S_CH3_FS_16000) && \
+		(I2S_CH3_384FS != I2S_CH3_FS_16000) && \
+		(I2S_CH3_512FS != I2S_CH3_FS_16000) && \
+		(I2S_CH3_768FS != I2S_CH3_FS_16000) && \
+		(I2S_CH3_1024FS != I2S_CH3_FS_16000)
+		#error IOH_I2S_CH3_CONFIG Error : \
+			Sampling frequency 16000Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH3_FS_22050
+	#if (I2S_CH3_64FS != I2S_CH3_FS_22050) && \
+		(I2S_CH3_128FS != I2S_CH3_FS_22050) && \
+		(I2S_CH3_192FS != I2S_CH3_FS_22050) && \
+		(I2S_CH3_256FS != I2S_CH3_FS_22050) && \
+		(I2S_CH3_384FS != I2S_CH3_FS_22050) && \
+		(I2S_CH3_512FS != I2S_CH3_FS_22050) && \
+		(I2S_CH3_768FS != I2S_CH3_FS_22050) && \
+		(I2S_CH3_1024FS != I2S_CH3_FS_22050)
+		#error IOH_I2S_CH3_CONFIG Error : \
+			Sampling frequency 22050Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH3_FS_32000
+	#if (I2S_CH3_64FS != I2S_CH3_FS_32000) && \
+		(I2S_CH3_128FS != I2S_CH3_FS_32000) && \
+		(I2S_CH3_192FS != I2S_CH3_FS_32000) && \
+		(I2S_CH3_256FS != I2S_CH3_FS_32000) && \
+		(I2S_CH3_384FS != I2S_CH3_FS_32000) && \
+		(I2S_CH3_512FS != I2S_CH3_FS_32000) && \
+		(I2S_CH3_768FS != I2S_CH3_FS_32000) && \
+		(I2S_CH3_1024FS != I2S_CH3_FS_32000)
+		#error IOH_I2S_CH3_CONFIG Error : \
+			Sampling frequency 32000Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH3_FS_44100
+	#if (I2S_CH3_64FS != I2S_CH3_FS_44100) && \
+		(I2S_CH3_128FS != I2S_CH3_FS_44100) && \
+		(I2S_CH3_192FS != I2S_CH3_FS_44100) && \
+		(I2S_CH3_256FS != I2S_CH3_FS_44100) && \
+		(I2S_CH3_384FS != I2S_CH3_FS_44100) && \
+		(I2S_CH3_512FS != I2S_CH3_FS_44100) && \
+		(I2S_CH3_768FS != I2S_CH3_FS_44100) && \
+		(I2S_CH3_1024FS != I2S_CH3_FS_44100)
+		#error IOH_I2S_CH3_CONFIG Error : \
+			Sampling frequency 44100Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH3_FS_48000
+	#if (I2S_CH3_64FS != I2S_CH3_FS_48000) && \
+		(I2S_CH3_128FS != I2S_CH3_FS_48000) && \
+		(I2S_CH3_192FS != I2S_CH3_FS_48000) && \
+		(I2S_CH3_256FS != I2S_CH3_FS_48000) && \
+		(I2S_CH3_384FS != I2S_CH3_FS_48000) && \
+		(I2S_CH3_512FS != I2S_CH3_FS_48000) && \
+		(I2S_CH3_768FS != I2S_CH3_FS_48000) && \
+		(I2S_CH3_1024FS != I2S_CH3_FS_48000)
+		#error IOH_I2S_CH3_CONFIG Error : \
+			Sampling frequency 48000Hz can not generate.
+	#endif
+#endif
+#endif	/* end of [#if (I2S_CH3_MSSEL == ioh_mssel_master)] */
+
+
+/* ===== CH4 Parameter Error Check ==== */
+#define	USE_RATE_MAX_CH4	(0)
+#ifdef	I2S_CH4_FS_8000
+	#define	USE_RATE_CH4_8000	SNDRV_PCM_RATE_8000
+	#define	USE_RATE_MIN_CH4	(8000)
+	#if (USE_RATE_MAX_CH4 < 8000)
+		#undef	USE_RATE_MAX_CH4
+		#define	USE_RATE_MAX_CH4	(8000)
+	#endif
+#else
+	#define	USE_RATE_CH4_8000	0
+#endif
+
+#ifdef	I2S_CH4_FS_11025
+	#define	USE_RATE_CH4_11025	SNDRV_PCM_RATE_11025
+	#ifndef	USE_RATE_MIN_CH4
+		#define	USE_RATE_MIN_CH4	(11025)
+	#endif
+	#if (USE_RATE_MAX_CH4 < 11025)
+		#undef	USE_RATE_MAX_CH4
+		#define	USE_RATE_MAX_CH4	(11025)
+	#endif
+#else
+	#define	USE_RATE_CH4_11025	0
+#endif
+
+#ifdef	I2S_CH4_FS_16000
+	#define	USE_RATE_CH4_16000	SNDRV_PCM_RATE_16000
+	#ifndef	USE_RATE_MIN_CH4
+		#define	USE_RATE_MIN_CH4	(16000)
+	#endif
+	#if (USE_RATE_MAX_CH4 < 16000)
+		#undef	USE_RATE_MAX_CH4
+		#define	USE_RATE_MAX_CH4	(16000)
+	#endif
+#else
+	#define	USE_RATE_CH4_16000	0
+#endif
+
+#ifdef	I2S_CH4_FS_22050
+	#define	USE_RATE_CH4_22050	SNDRV_PCM_RATE_22050
+	#ifndef	USE_RATE_MIN_CH4
+		#define	USE_RATE_MIN_CH4	(22050)
+	#endif
+	#if (USE_RATE_MAX_CH4 < 22050)
+		#undef	USE_RATE_MAX_CH4
+		#define	USE_RATE_MAX_CH4	(22050)
+	#endif
+#else
+	#define	USE_RATE_CH4_22050	0
+#endif
+
+#ifdef	I2S_CH4_FS_32000
+	#define	USE_RATE_CH4_32000	SNDRV_PCM_RATE_32000
+	#ifndef	USE_RATE_MIN_CH4
+		#define	USE_RATE_MIN_CH4	(32000)
+	#endif
+	#if (USE_RATE_MAX_CH4 < 32000)
+		#undef	USE_RATE_MAX_CH4
+		#define	USE_RATE_MAX_CH4	(32000)
+	#endif
+#else
+	#define	USE_RATE_CH4_32000	0
+#endif
+
+#ifdef	I2S_CH4_FS_44100
+	#define	USE_RATE_CH4_44100	SNDRV_PCM_RATE_44100
+	#ifndef	USE_RATE_MIN_CH4
+		#define	USE_RATE_MIN_CH4	(44100)
+	#endif
+	#if (USE_RATE_MAX_CH4 < 44100)
+		#undef	USE_RATE_MAX_CH4
+		#define	USE_RATE_MAX_CH4	(44100)
+	#endif
+#else
+	#define	USE_RATE_CH4_44100	0
+#endif
+
+#ifdef	I2S_CH4_FS_48000
+	#define	USE_RATE_CH4_48000	SNDRV_PCM_RATE_48000
+	#ifndef	USE_RATE_MIN_CH4
+		#define	USE_RATE_MIN_CH4	(48000)
+	#endif
+	#if (USE_RATE_MAX_CH4 < 48000)
+		#undef	USE_RATE_MAX_CH4
+		#define	USE_RATE_MAX_CH4	(48000)
+	#endif
+#else
+	#define	USE_RATE_CH4_48000	0
+#endif
+
+#define	USE_RATE_CH4	(USE_RATE_CH4_8000|USE_RATE_CH4_11025 |		\
+			 USE_RATE_CH4_16000|USE_RATE_CH4_22050 |	\
+			 USE_RATE_CH4_32000|USE_RATE_CH4_44100 |	\
+			 USE_RATE_CH4_48000)
+
+
+#define I2S_CH4_64FS		(I2S_CH4_MCLK/64)
+#define I2S_CH4_128FS		(I2S_CH4_MCLK/128)
+#define I2S_CH4_192FS		(I2S_CH4_MCLK/192)
+#define I2S_CH4_256FS		(I2S_CH4_MCLK/256)
+#define I2S_CH4_384FS		(I2S_CH4_MCLK/384)
+#define I2S_CH4_512FS		(I2S_CH4_MCLK/512)
+#define I2S_CH4_768FS		(I2S_CH4_MCLK/768)
+#define I2S_CH4_1024FS		(I2S_CH4_MCLK/1024)
+
+
+#if (I2S_CH4_MSSEL == ioh_mssel_master)
+#ifdef	I2S_CH4_FS_8000
+	#if (I2S_CH4_64FS != I2S_CH4_FS_8000) && \
+		(I2S_CH4_128FS != I2S_CH4_FS_8000) && \
+		(I2S_CH4_192FS != I2S_CH4_FS_8000) && \
+		(I2S_CH4_256FS != I2S_CH4_FS_8000) && \
+		(I2S_CH4_384FS != I2S_CH4_FS_8000) && \
+		(I2S_CH4_512FS != I2S_CH4_FS_8000) && \
+		(I2S_CH4_768FS != I2S_CH4_FS_8000) && \
+		(I2S_CH4_1024FS != I2S_CH4_FS_8000)
+		#error IOH_I2S_CH4_CONFIG Error : \
+			Sampling frequency 8000Hz can not generate.
+	#endif
+#endif
+
+#ifdef	I2S_CH4_FS_11025
+	#if (I2S_CH4_64FS != I2S_CH4_FS_11025) && \
+		(I2S_CH4_128FS != I2S_CH4_FS_11025) && \
+		(I2S_CH4_192FS != I2S_CH4_FS_11025) && \
+		(I2S_CH4_256FS != I2S_CH4_FS_11025) && \
+		(I2S_CH4_384FS != I2S_CH4_FS_11025) && \
+		(I2S_CH4_512FS != I2S_CH4_FS_11025) && \
+		(I2S_CH4_768FS != I2S_CH4_FS_11025) && \
+		(I2S_CH4_1024FS != I2S_CH4_FS_11025)
+		#error IOH_I2S_CH4_CONFIG Error : \
+			Sampling frequency 11025Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH4_FS_16000
+	#if (I2S_CH4_64FS != I2S_CH4_FS_16000) && \
+		(I2S_CH4_128FS != I2S_CH4_FS_16000) && \
+		(I2S_CH4_192FS != I2S_CH4_FS_16000) && \
+		(I2S_CH4_256FS != I2S_CH4_FS_16000) && \
+		(I2S_CH4_384FS != I2S_CH4_FS_16000) && \
+		(I2S_CH4_512FS != I2S_CH4_FS_16000) && \
+		(I2S_CH4_768FS != I2S_CH4_FS_16000) && \
+		(I2S_CH4_1024FS != I2S_CH4_FS_16000)
+		#error IOH_I2S_CH4_CONFIG Error : \
+			Sampling frequency 16000Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH4_FS_22050
+	#if (I2S_CH4_64FS != I2S_CH4_FS_22050) && \
+		(I2S_CH4_128FS != I2S_CH4_FS_22050) && \
+		(I2S_CH4_192FS != I2S_CH4_FS_22050) && \
+		(I2S_CH4_256FS != I2S_CH4_FS_22050) && \
+		(I2S_CH4_384FS != I2S_CH4_FS_22050) && \
+		(I2S_CH4_512FS != I2S_CH4_FS_22050) && \
+		(I2S_CH4_768FS != I2S_CH4_FS_22050) && \
+		(I2S_CH4_1024FS != I2S_CH4_FS_22050)
+		#error IOH_I2S_CH4_CONFIG Error : \
+			Sampling frequency 22050Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH4_FS_32000
+	#if (I2S_CH4_64FS != I2S_CH4_FS_32000) && \
+		(I2S_CH4_128FS != I2S_CH4_FS_32000) && \
+		(I2S_CH4_192FS != I2S_CH4_FS_32000) && \
+		(I2S_CH4_256FS != I2S_CH4_FS_32000) && \
+		(I2S_CH4_384FS != I2S_CH4_FS_32000) && \
+		(I2S_CH4_512FS != I2S_CH4_FS_32000) && \
+		(I2S_CH4_768FS != I2S_CH4_FS_32000) && \
+		(I2S_CH4_1024FS != I2S_CH4_FS_32000)
+		#error IOH_I2S_CH4_CONFIG Error : \
+			Sampling frequency 32000Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH4_FS_44100
+	#if (I2S_CH4_64FS != I2S_CH4_FS_44100) && \
+		(I2S_CH4_128FS != I2S_CH4_FS_44100) && \
+		(I2S_CH4_192FS != I2S_CH4_FS_44100) && \
+		(I2S_CH4_256FS != I2S_CH4_FS_44100) && \
+		(I2S_CH4_384FS != I2S_CH4_FS_44100) && \
+		(I2S_CH4_512FS != I2S_CH4_FS_44100) && \
+		(I2S_CH4_768FS != I2S_CH4_FS_44100) && \
+		(I2S_CH4_1024FS != I2S_CH4_FS_44100)
+		#error IOH_I2S_CH4_CONFIG Error : \
+			Sampling frequency 44100Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH4_FS_48000
+	#if (I2S_CH4_64FS != I2S_CH4_FS_48000) && \
+		(I2S_CH4_128FS != I2S_CH4_FS_48000) && \
+		(I2S_CH4_192FS != I2S_CH4_FS_48000) && \
+		(I2S_CH4_256FS != I2S_CH4_FS_48000) && \
+		(I2S_CH4_384FS != I2S_CH4_FS_48000) && \
+		(I2S_CH4_512FS != I2S_CH4_FS_48000) && \
+		(I2S_CH4_768FS != I2S_CH4_FS_48000) && \
+		(I2S_CH4_1024FS != I2S_CH4_FS_48000)
+		#error IOH_I2S_CH4_CONFIG Error : \
+			Sampling frequency 48000Hz can not generate.
+	#endif
+#endif
+#endif	/* end of [#if (I2S_CH4_MSSEL == ioh_mssel_master)] */
+
+
+/* ===== CH5 Parameter Error Check ==== */
+#define	USE_RATE_MAX_CH5	(0)
+#ifdef	I2S_CH5_FS_8000
+	#define	USE_RATE_CH5_8000	SNDRV_PCM_RATE_8000
+	#define	USE_RATE_MIN_CH5	(8000)
+	#if (USE_RATE_MAX_CH5 < 8000)
+		#undef	USE_RATE_MAX_CH5
+		#define	USE_RATE_MAX_CH5	(8000)
+	#endif
+#else
+	#define	USE_RATE_CH5_8000	0
+#endif
+
+#ifdef	I2S_CH5_FS_11025
+	#define	USE_RATE_CH5_11025	SNDRV_PCM_RATE_11025
+	#ifndef	USE_RATE_MIN_CH5
+		#define	USE_RATE_MIN_CH5	(11025)
+	#endif
+	#if (USE_RATE_MAX_CH5 < 11025)
+		#undef	USE_RATE_MAX_CH5
+		#define	USE_RATE_MAX_CH5	(11025)
+	#endif
+#else
+	#define	USE_RATE_CH5_11025	0
+#endif
+
+#ifdef	I2S_CH5_FS_16000
+	#define	USE_RATE_CH5_16000	SNDRV_PCM_RATE_16000
+	#ifndef	USE_RATE_MIN_CH5
+		#define	USE_RATE_MIN_CH5	(16000)
+	#endif
+	#if (USE_RATE_MAX_CH5 < 16000)
+		#undef	USE_RATE_MAX_CH5
+		#define	USE_RATE_MAX_CH5	(16000)
+	#endif
+#else
+	#define	USE_RATE_CH5_16000	0
+#endif
+
+#ifdef	I2S_CH5_FS_22050
+	#define	USE_RATE_CH5_22050	SNDRV_PCM_RATE_22050
+	#ifndef	USE_RATE_MIN_CH5
+		#define	USE_RATE_MIN_CH5	(22050)
+	#endif
+	#if (USE_RATE_MAX_CH5 < 22050)
+		#undef	USE_RATE_MAX_CH5
+		#define	USE_RATE_MAX_CH5	(22050)
+	#endif
+#else
+	#define	USE_RATE_CH5_22050	0
+#endif
+
+#ifdef	I2S_CH5_FS_32000
+	#define	USE_RATE_CH5_32000	SNDRV_PCM_RATE_32000
+	#ifndef	USE_RATE_MIN_CH5
+		#define	USE_RATE_MIN_CH5	(32000)
+	#endif
+	#if (USE_RATE_MAX_CH5 < 32000)
+		#undef	USE_RATE_MAX_CH5
+		#define	USE_RATE_MAX_CH5	(32000)
+	#endif
+#else
+	#define	USE_RATE_CH5_32000	0
+#endif
+
+#ifdef	I2S_CH5_FS_44100
+	#define	USE_RATE_CH5_44100	SNDRV_PCM_RATE_44100
+	#ifndef	USE_RATE_MIN_CH5
+		#define	USE_RATE_MIN_CH5	(44100)
+	#endif
+	#if (USE_RATE_MAX_CH5 < 44100)
+		#undef	USE_RATE_MAX_CH5
+		#define	USE_RATE_MAX_CH5	(44100)
+	#endif
+#else
+	#define	USE_RATE_CH5_44100	0
+#endif
+
+#ifdef	I2S_CH5_FS_48000
+	#define	USE_RATE_CH5_48000	SNDRV_PCM_RATE_48000
+	#ifndef	USE_RATE_MIN_CH5
+		#define	USE_RATE_MIN_CH5	(48000)
+	#endif
+	#if (USE_RATE_MAX_CH5 < 48000)
+		#undef	USE_RATE_MAX_CH5
+		#define	USE_RATE_MAX_CH5	(48000)
+	#endif
+#else
+	#define	USE_RATE_CH5_48000	0
+#endif
+
+#define	USE_RATE_CH5	(USE_RATE_CH5_8000|USE_RATE_CH5_11025 |		\
+			 USE_RATE_CH5_16000|USE_RATE_CH5_22050 |	\
+			 USE_RATE_CH5_32000|USE_RATE_CH5_44100 |	\
+			 USE_RATE_CH5_48000)
+
+
+#define I2S_CH5_64FS		(I2S_CH5_MCLK/64)
+#define I2S_CH5_128FS		(I2S_CH5_MCLK/128)
+#define I2S_CH5_192FS		(I2S_CH5_MCLK/192)
+#define I2S_CH5_256FS		(I2S_CH5_MCLK/256)
+#define I2S_CH5_384FS		(I2S_CH5_MCLK/384)
+#define I2S_CH5_512FS		(I2S_CH5_MCLK/512)
+#define I2S_CH5_768FS		(I2S_CH5_MCLK/768)
+#define I2S_CH5_1024FS		(I2S_CH5_MCLK/1024)
+
+
+#if (I2S_CH5_MSSEL == ioh_mssel_master)
+#ifdef	I2S_CH5_FS_8000
+	#if (I2S_CH5_64FS != I2S_CH5_FS_8000) && \
+		(I2S_CH5_128FS != I2S_CH5_FS_8000) && \
+		(I2S_CH5_192FS != I2S_CH5_FS_8000) && \
+		(I2S_CH5_256FS != I2S_CH5_FS_8000) && \
+		(I2S_CH5_384FS != I2S_CH5_FS_8000) && \
+		(I2S_CH5_512FS != I2S_CH5_FS_8000) && \
+		(I2S_CH5_768FS != I2S_CH5_FS_8000) && \
+		(I2S_CH5_1024FS != I2S_CH5_FS_8000)
+		#error IOH_I2S_CH5_CONFIG Error : \
+			Sampling frequency 8000Hz can not generate.
+	#endif
+#endif
+
+#ifdef	I2S_CH5_FS_11025
+	#if (I2S_CH5_64FS != I2S_CH5_FS_11025) && \
+		(I2S_CH5_128FS != I2S_CH5_FS_11025) && \
+		(I2S_CH5_192FS != I2S_CH5_FS_11025) && \
+		(I2S_CH5_256FS != I2S_CH5_FS_11025) && \
+		(I2S_CH5_384FS != I2S_CH5_FS_11025) && \
+		(I2S_CH5_512FS != I2S_CH5_FS_11025) && \
+		(I2S_CH5_768FS != I2S_CH5_FS_11025) && \
+		(I2S_CH5_1024FS != I2S_CH5_FS_11025)
+		#error IOH_I2S_CH5_CONFIG Error : \
+			Sampling frequency 11025Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH5_FS_16000
+	#if (I2S_CH5_64FS != I2S_CH5_FS_16000) && \
+		(I2S_CH5_128FS != I2S_CH5_FS_16000) && \
+		(I2S_CH5_192FS != I2S_CH5_FS_16000) && \
+		(I2S_CH5_256FS != I2S_CH5_FS_16000) && \
+		(I2S_CH5_384FS != I2S_CH5_FS_16000) && \
+		(I2S_CH5_512FS != I2S_CH5_FS_16000) && \
+		(I2S_CH5_768FS != I2S_CH5_FS_16000) && \
+		(I2S_CH5_1024FS != I2S_CH5_FS_16000)
+		#error IOH_I2S_CH5_CONFIG Error : \
+			Sampling frequency 16000Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH5_FS_22050
+	#if (I2S_CH5_64FS != I2S_CH5_FS_22050) && \
+		(I2S_CH5_128FS != I2S_CH5_FS_22050) && \
+		(I2S_CH5_192FS != I2S_CH5_FS_22050) && \
+		(I2S_CH5_256FS != I2S_CH5_FS_22050) && \
+		(I2S_CH5_384FS != I2S_CH5_FS_22050) && \
+		(I2S_CH5_512FS != I2S_CH5_FS_22050) && \
+		(I2S_CH5_768FS != I2S_CH5_FS_22050) && \
+		(I2S_CH5_1024FS != I2S_CH5_FS_22050)
+		#error IOH_I2S_CH5_CONFIG Error : \
+			Sampling frequency 22050Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH5_FS_32000
+	#if (I2S_CH5_64FS != I2S_CH5_FS_32000) && \
+		(I2S_CH5_128FS != I2S_CH5_FS_32000) && \
+		(I2S_CH5_192FS != I2S_CH5_FS_32000) && \
+		(I2S_CH5_256FS != I2S_CH5_FS_32000) && \
+		(I2S_CH5_384FS != I2S_CH5_FS_32000) && \
+		(I2S_CH5_512FS != I2S_CH5_FS_32000) && \
+		(I2S_CH5_768FS != I2S_CH5_FS_32000) && \
+		(I2S_CH5_1024FS != I2S_CH5_FS_32000)
+		#error IOH_I2S_CH5_CONFIG Error : \
+			Sampling frequency 32000Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH5_FS_44100
+	#if (I2S_CH5_64FS != I2S_CH5_FS_44100) && \
+		(I2S_CH5_128FS != I2S_CH5_FS_44100) && \
+		(I2S_CH5_192FS != I2S_CH5_FS_44100) && \
+		(I2S_CH5_256FS != I2S_CH5_FS_44100) && \
+		(I2S_CH5_384FS != I2S_CH5_FS_44100) && \
+		(I2S_CH5_512FS != I2S_CH5_FS_44100) && \
+		(I2S_CH5_768FS != I2S_CH5_FS_44100) && \
+		(I2S_CH5_1024FS != I2S_CH5_FS_44100)
+		#error IOH_I2S_CH5_CONFIG Error : \
+			Sampling frequency 44100Hz can not generate.
+	#endif
+#endif
+#ifdef	I2S_CH5_FS_48000
+	#if (I2S_CH5_64FS != I2S_CH5_FS_48000) && \
+		(I2S_CH5_128FS != I2S_CH5_FS_48000) && \
+		(I2S_CH5_192FS != I2S_CH5_FS_48000) && \
+		(I2S_CH5_256FS != I2S_CH5_FS_48000) && \
+		(I2S_CH5_384FS != I2S_CH5_FS_48000) && \
+		(I2S_CH5_512FS != I2S_CH5_FS_48000) && \
+		(I2S_CH5_768FS != I2S_CH5_FS_48000) && \
+		(I2S_CH5_1024FS != I2S_CH5_FS_48000)
+		#error IOH_I2S_CH5_CONFIG Error : \
+			Sampling frequency 48000Hz can not generate.
+	#endif
+#endif
+#endif	/* end of [#if (I2S_CH5_MSSEL == ioh_mssel_master)] */
+
+
+
+/* ######################################################################## */
+/* ###		Parameter Table Data					### */
+/* ######################################################################## */
+
+#define	I2SCLKCNT_MSSEL_OFFSET		(0)
+#define	I2SCLKCNT_BCLKPOL_OFFSET	(1)
+#define	I2SCLKCNT_MASTERCLKSEL_OFFSET	(2)
+#define	I2SCLKCNT_LRCKFMT_OFFSET	(4)
+#define	I2SCLKCNT_MCLKFS_OFFSET		(8)
+#define	I2SCLKCNT_BCLKFS_OFFSET		(12)
+
+#define	I2SCNT_DABIT_OFFSET		(8)
+
+#define	I2SCNTTX_TX_TEL_OFFSET		(0)
+#define	I2SCNTTX_TX_DLYOFF_OFFSET	(12)
+#define	I2SCNTTX_TX_LSB_OFFSET		(13)
+#define	I2SCNTTX_TX_LRPOL_OFFSET	(14)
+#define	I2SCNTTX_TX_AFT_OFFSET		(15)
+
+#define	I2SCNTRX_RX_TEL_OFFSET		(0)
+#define	I2SCNTRX_RX_DLYOFF_OFFSET	(12)
+#define	I2SCNTRX_RX_LSB_OFFSET		(13)
+#define	I2SCNTRX_RX_LRPOL_OFFSET	(14)
+#define	I2SCNTRX_RX_AFT_OFFSET		(15)
+
+
+#define	STEREO_OFFSET			0
+#define	MONAURAL_OFFSET			MAX_I2S_CH
+static const struct i2s_config_tab_t i2s_config_table[MAX_I2S_CH*2] = {
+	/* CH0 Stereo */
+	{
+		(I2S_CH0_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
+		  (I2S_CH0_BCLKPOL_STEREO << I2SCLKCNT_BCLKPOL_OFFSET) | \
+		  (I2S_CH0_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
+		  (I2S_CH0_LRCKFMT_STEREO << I2SCLKCNT_LRCKFMT_OFFSET),
+
+		  (I2S_CH0_TX_DLYOFF_STEREO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
+		  (I2S_CH0_TX_LSB_STEREO << I2SCNTTX_TX_LSB_OFFSET) | \
+		  (I2S_CH0_TX_LRPOL_STEREO << I2SCNTTX_TX_LRPOL_OFFSET) | \
+		  (I2S_CH0_TX_AFT_STEREO << I2SCNTTX_TX_AFT_OFFSET),
+
+		  (I2S_CH0_RX_DLYOFF_STEREO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
+		  (I2S_CH0_RX_LSB_STEREO << I2SCNTRX_RX_LSB_OFFSET) | \
+		  (I2S_CH0_RX_LRPOL_STEREO << I2SCNTRX_RX_LRPOL_OFFSET) | \
+		  (I2S_CH0_RX_AFT_STEREO << I2SCNTRX_RX_AFT_OFFSET),
+
+		I2S_CH0_MCLK,
+	},
+	/* CH1 Stereo */
+	{
+		(I2S_CH1_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
+		  (I2S_CH1_BCLKPOL_STEREO << I2SCLKCNT_BCLKPOL_OFFSET) | \
+		  (I2S_CH1_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
+		  (I2S_CH1_LRCKFMT_STEREO << I2SCLKCNT_LRCKFMT_OFFSET),
+
+		  (I2S_CH1_TX_DLYOFF_STEREO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
+		  (I2S_CH1_TX_LSB_STEREO << I2SCNTTX_TX_LSB_OFFSET) | \
+		  (I2S_CH1_TX_LRPOL_STEREO << I2SCNTTX_TX_LRPOL_OFFSET) | \
+		  (I2S_CH1_TX_AFT_STEREO << I2SCNTTX_TX_AFT_OFFSET),
+
+		  (I2S_CH1_RX_DLYOFF_STEREO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
+		  (I2S_CH1_RX_LSB_STEREO << I2SCNTRX_RX_LSB_OFFSET) | \
+		  (I2S_CH1_RX_LRPOL_STEREO << I2SCNTRX_RX_LRPOL_OFFSET) | \
+		  (I2S_CH1_RX_AFT_STEREO << I2SCNTRX_RX_AFT_OFFSET),
+
+		I2S_CH1_MCLK,
+	},
+	/* CH2 Stereo */
+	{
+		(I2S_CH2_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
+		  (I2S_CH2_BCLKPOL_STEREO << I2SCLKCNT_BCLKPOL_OFFSET) | \
+		  (I2S_CH2_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
+		  (I2S_CH2_LRCKFMT_STEREO << I2SCLKCNT_LRCKFMT_OFFSET),
+
+		  (I2S_CH2_TX_DLYOFF_STEREO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
+		  (I2S_CH2_TX_LSB_STEREO << I2SCNTTX_TX_LSB_OFFSET) | \
+		  (I2S_CH2_TX_LRPOL_STEREO << I2SCNTTX_TX_LRPOL_OFFSET) | \
+		  (I2S_CH2_TX_AFT_STEREO << I2SCNTTX_TX_AFT_OFFSET),
+
+		  (I2S_CH2_RX_DLYOFF_STEREO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
+		  (I2S_CH2_RX_LSB_STEREO << I2SCNTRX_RX_LSB_OFFSET) | \
+		  (I2S_CH2_RX_LRPOL_STEREO << I2SCNTRX_RX_LRPOL_OFFSET) | \
+		  (I2S_CH2_RX_AFT_STEREO << I2SCNTRX_RX_AFT_OFFSET),
+
+		I2S_CH2_MCLK,
+	},
+	/* CH3 Stereo */
+	{
+		(I2S_CH3_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
+		  (I2S_CH3_BCLKPOL_STEREO << I2SCLKCNT_BCLKPOL_OFFSET) | \
+		  (I2S_CH3_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
+		  (I2S_CH3_LRCKFMT_STEREO << I2SCLKCNT_LRCKFMT_OFFSET),
+
+		  (I2S_CH3_TX_DLYOFF_STEREO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
+		  (I2S_CH3_TX_LSB_STEREO << I2SCNTTX_TX_LSB_OFFSET) | \
+		  (I2S_CH3_TX_LRPOL_STEREO << I2SCNTTX_TX_LRPOL_OFFSET) | \
+		  (I2S_CH3_TX_AFT_STEREO << I2SCNTTX_TX_AFT_OFFSET),
+
+		  (I2S_CH3_RX_DLYOFF_STEREO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
+		  (I2S_CH3_RX_LSB_STEREO << I2SCNTRX_RX_LSB_OFFSET) | \
+		  (I2S_CH3_RX_LRPOL_STEREO << I2SCNTRX_RX_LRPOL_OFFSET) | \
+		  (I2S_CH3_RX_AFT_STEREO << I2SCNTRX_RX_AFT_OFFSET),
+
+		I2S_CH3_MCLK,
+	},
+	/* CH4 Stereo */
+	{
+		(I2S_CH4_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
+		  (I2S_CH4_BCLKPOL_STEREO << I2SCLKCNT_BCLKPOL_OFFSET) | \
+		  (I2S_CH4_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
+		  (I2S_CH4_LRCKFMT_STEREO << I2SCLKCNT_LRCKFMT_OFFSET),
+
+		  (I2S_CH4_TX_DLYOFF_STEREO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
+		  (I2S_CH4_TX_LSB_STEREO << I2SCNTTX_TX_LSB_OFFSET) | \
+		  (I2S_CH4_TX_LRPOL_STEREO << I2SCNTTX_TX_LRPOL_OFFSET) | \
+		  (I2S_CH4_TX_AFT_STEREO << I2SCNTTX_TX_AFT_OFFSET),
+
+		  (I2S_CH4_RX_DLYOFF_STEREO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
+		  (I2S_CH4_RX_LSB_STEREO << I2SCNTRX_RX_LSB_OFFSET) | \
+		  (I2S_CH4_RX_LRPOL_STEREO << I2SCNTRX_RX_LRPOL_OFFSET) | \
+		  (I2S_CH4_RX_AFT_STEREO << I2SCNTRX_RX_AFT_OFFSET),
+
+		I2S_CH4_MCLK,
+	},
+	/* CH5 Stereo */
+	{
+		(I2S_CH5_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
+		  (I2S_CH5_BCLKPOL_STEREO << I2SCLKCNT_BCLKPOL_OFFSET) | \
+		  (I2S_CH5_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
+		  (I2S_CH5_LRCKFMT_STEREO << I2SCLKCNT_LRCKFMT_OFFSET),
+
+		  (I2S_CH5_TX_DLYOFF_STEREO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
+		  (I2S_CH5_TX_LSB_STEREO << I2SCNTTX_TX_LSB_OFFSET) | \
+		  (I2S_CH5_TX_LRPOL_STEREO << I2SCNTTX_TX_LRPOL_OFFSET) | \
+		  (I2S_CH5_TX_AFT_STEREO << I2SCNTTX_TX_AFT_OFFSET),
+
+		  (I2S_CH5_RX_DLYOFF_STEREO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
+		  (I2S_CH5_RX_LSB_STEREO << I2SCNTRX_RX_LSB_OFFSET) | \
+		  (I2S_CH5_RX_LRPOL_STEREO << I2SCNTRX_RX_LRPOL_OFFSET) | \
+		  (I2S_CH5_RX_AFT_STEREO << I2SCNTRX_RX_AFT_OFFSET),
+
+		I2S_CH5_MCLK,
+	},
+
+
+	/* CH0 Mono */
+	{
+		(I2S_CH0_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
+		  (I2S_CH0_BCLKPOL_MONO << I2SCLKCNT_BCLKPOL_OFFSET) | \
+		  (I2S_CH0_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
+		  (I2S_CH0_LRCKFMT_MONO << I2SCLKCNT_LRCKFMT_OFFSET),
+
+		  (I2S_CH0_TX_DLYOFF_MONO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
+		  (I2S_CH0_TX_LSB_MONO << I2SCNTTX_TX_LSB_OFFSET) | \
+		  (ioh_lrpol_no_invert << I2SCNTTX_TX_LRPOL_OFFSET) | \
+		  (ioh_aft_front << I2SCNTTX_TX_AFT_OFFSET),
+
+		  (I2S_CH0_RX_DLYOFF_MONO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
+		  (I2S_CH0_RX_LSB_MONO << I2SCNTRX_RX_LSB_OFFSET) | \
+		  (ioh_lrpol_no_invert << I2SCNTRX_RX_LRPOL_OFFSET) | \
+		  (ioh_aft_front << I2SCNTRX_RX_AFT_OFFSET),
+
+		I2S_CH0_MCLK,
+	},
+	/* CH1 Mono */
+	{
+		(I2S_CH1_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
+		  (I2S_CH1_BCLKPOL_MONO << I2SCLKCNT_BCLKPOL_OFFSET) | \
+		  (I2S_CH1_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
+		  (I2S_CH1_LRCKFMT_MONO << I2SCLKCNT_LRCKFMT_OFFSET),
+
+		  (I2S_CH1_TX_DLYOFF_MONO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
+		  (I2S_CH1_TX_LSB_MONO << I2SCNTTX_TX_LSB_OFFSET) | \
+		  (ioh_lrpol_no_invert << I2SCNTTX_TX_LRPOL_OFFSET) | \
+		  (ioh_aft_front << I2SCNTTX_TX_AFT_OFFSET),
+
+		  (I2S_CH1_RX_DLYOFF_MONO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
+		  (I2S_CH1_RX_LSB_MONO << I2SCNTRX_RX_LSB_OFFSET) | \
+		  (ioh_lrpol_no_invert << I2SCNTRX_RX_LRPOL_OFFSET) | \
+		  (ioh_aft_front << I2SCNTRX_RX_AFT_OFFSET),
+
+		I2S_CH1_MCLK,
+	},
+	/* CH2 Mono */
+	{
+		(I2S_CH2_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
+		  (I2S_CH2_BCLKPOL_MONO << I2SCLKCNT_BCLKPOL_OFFSET) | \
+		  (I2S_CH2_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
+		  (I2S_CH2_LRCKFMT_MONO << I2SCLKCNT_LRCKFMT_OFFSET),
+
+		  (I2S_CH2_TX_DLYOFF_MONO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
+		  (I2S_CH2_TX_LSB_MONO << I2SCNTTX_TX_LSB_OFFSET) | \
+		  (ioh_lrpol_no_invert << I2SCNTTX_TX_LRPOL_OFFSET) | \
+		  (ioh_aft_front << I2SCNTTX_TX_AFT_OFFSET),
+
+		  (I2S_CH2_RX_DLYOFF_MONO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
+		  (I2S_CH2_RX_LSB_MONO << I2SCNTRX_RX_LSB_OFFSET) | \
+		  (ioh_lrpol_no_invert << I2SCNTRX_RX_LRPOL_OFFSET) | \
+		  (ioh_aft_front << I2SCNTRX_RX_AFT_OFFSET),
+
+		I2S_CH2_MCLK,
+	},
+	/* CH3 Mono */
+	{
+		(I2S_CH3_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
+		  (I2S_CH3_BCLKPOL_MONO << I2SCLKCNT_BCLKPOL_OFFSET) | \
+		  (I2S_CH3_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
+		  (I2S_CH3_LRCKFMT_MONO << I2SCLKCNT_LRCKFMT_OFFSET),
+
+		  (I2S_CH3_TX_DLYOFF_MONO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
+		  (I2S_CH3_TX_LSB_MONO << I2SCNTTX_TX_LSB_OFFSET) | \
+		  (ioh_lrpol_no_invert << I2SCNTTX_TX_LRPOL_OFFSET) | \
+		  (ioh_aft_front << I2SCNTTX_TX_AFT_OFFSET),
+
+		  (I2S_CH3_RX_DLYOFF_MONO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
+		  (I2S_CH3_RX_LSB_MONO << I2SCNTRX_RX_LSB_OFFSET) | \
+		  (ioh_lrpol_no_invert << I2SCNTRX_RX_LRPOL_OFFSET) | \
+		  (ioh_aft_front << I2SCNTRX_RX_AFT_OFFSET),
+
+		I2S_CH3_MCLK,
+	},
+	/* CH4 Mono */
+	{
+		(I2S_CH4_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
+		  (I2S_CH4_BCLKPOL_MONO << I2SCLKCNT_BCLKPOL_OFFSET) | \
+		  (I2S_CH4_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
+		  (I2S_CH4_LRCKFMT_MONO << I2SCLKCNT_LRCKFMT_OFFSET),
+
+		  (I2S_CH4_TX_DLYOFF_MONO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
+		  (I2S_CH4_TX_LSB_MONO << I2SCNTTX_TX_LSB_OFFSET) | \
+		  (ioh_lrpol_no_invert << I2SCNTTX_TX_LRPOL_OFFSET) | \
+		  (ioh_aft_front << I2SCNTTX_TX_AFT_OFFSET),
+
+		  (I2S_CH4_RX_DLYOFF_MONO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
+		  (I2S_CH4_RX_LSB_MONO << I2SCNTRX_RX_LSB_OFFSET) | \
+		  (ioh_lrpol_no_invert << I2SCNTRX_RX_LRPOL_OFFSET) | \
+		  (ioh_aft_front << I2SCNTRX_RX_AFT_OFFSET),
+
+		I2S_CH4_MCLK,
+	},
+	/* CH5 Mono */
+	{
+		(I2S_CH5_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
+		  (I2S_CH5_BCLKPOL_MONO << I2SCLKCNT_BCLKPOL_OFFSET) | \
+		  (I2S_CH5_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
+		  (I2S_CH5_LRCKFMT_MONO << I2SCLKCNT_LRCKFMT_OFFSET),
+
+		  (I2S_CH5_TX_DLYOFF_MONO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
+		  (I2S_CH5_TX_LSB_MONO << I2SCNTTX_TX_LSB_OFFSET) | \
+		  (ioh_lrpol_no_invert << I2SCNTTX_TX_LRPOL_OFFSET) | \
+		  (ioh_aft_front << I2SCNTTX_TX_AFT_OFFSET),
+
+		  (I2S_CH5_RX_DLYOFF_MONO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
+		  (I2S_CH5_RX_LSB_MONO << I2SCNTRX_RX_LSB_OFFSET) | \
+		  (ioh_lrpol_no_invert << I2SCNTRX_RX_LRPOL_OFFSET) | \
+		  (ioh_aft_front << I2SCNTRX_RX_AFT_OFFSET),
+
+		I2S_CH5_MCLK,
+	},
+
+};
+
+
+#endif
diff --git a/sound/soc/lapis/ml7213ioh-plat.c b/sound/soc/lapis/ml7213ioh-plat.c
new file mode 100644
index 0000000..9093a43
--- /dev/null
+++ b/sound/soc/lapis/ml7213ioh-plat.c
@@ -0,0 +1,2488 @@
+/*
+ * 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 <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+
+#include "ioh_i2s.h"
+#include "ioh_i2s_config.h"
+#include "ml7213ioh-plat.h"
+
+#define USE_PERIODS_MIN		(I2S_DMA_SG_MAX)
+#define USE_PERIODS_MAX		(I2S_DMA_SG_MAX)
+#ifndef add_capture_constraints
+#define add_capture_constraints(x) 0
+#endif
+
+#define	I2S_WRITE_MASTER_BIT	(0)
+#define	I2S_READ_MASTER_BIT	(8)
+
+#define	I2S_WRITE_ENABLE_BIT	(0)
+#define	I2S_READ_ENABLE_BIT	(8)
+
+/* RW flag */
+#define	SND_CAPTURE_SUBSTREAM	0
+#define	SND_PLAYBACK_SUBSTREAM	1
+
+/* Codec Device Address */
+
+#define	I2SCNTRX_RXTEL		BIT(0)
+#define I2SCNTRX_RXDABIT_8BIT	0
+#define I2SCNTRX_RXDABIT_14BIT	BIT(8)
+#define I2SCNTRX_RXDABIT_16BIT	BIT(9)
+#define I2SCNTRX_RXDABIT_18BIT	(BIT(8) | BIT(9))
+#define I2SCNTRX_RXDABIT_20BIT	BIT(10)
+#define I2SCNTRX_RXDABIT_24BIT	(BIT(8) | BIT(10))
+#define	I2SCNTRX_RXDLYOFF	BIT(12)
+#define	I2SCNTRX_RX_LSB		BIT(13)
+
+#define	I2SCNTTX_TXTEL		I2SCNTRX_RXTEL
+#define I2SCNTTX_TXDABIT_8BIT	I2SCNTRX_RXDABIT_8BIT
+#define I2SCNTTX_TXDABIT_14BIT	I2SCNTRX_RXDABIT_14BIT
+#define I2SCNTTX_TXDABIT_16BIT	I2SCNTRX_RXDABIT_16BIT
+#define I2SCNTTX_TXDABIT_18BIT	I2SCNTRX_RXDABIT_18BIT
+#define I2SCNTTX_TXDABIT_20BIT	I2SCNTRX_RXDABIT_20BIT
+#define I2SCNTTX_TXDABIT_24BIT	I2SCNTRX_RXDABIT_24BIT
+#define	I2SCNTTX_TXDLYOFF	I2SCNTRX_RXDLYOFF
+#define	I2SCNTTX_TX_LSB		I2SCNTRX_RX_LSB
+
+#define I2SCLKCNT_MCLKFS_64FS	0
+#define I2SCLKCNT_MCLKFS_128FS	BIT(8)
+#define I2SCLKCNT_MCLKFS_192FS	BIT(9)
+#define I2SCLKCNT_MCLKFS_256FS	(BIT(8) | BIT(9))
+#define I2SCLKCNT_MCLKFS_384FS	BIT(10)
+#define I2SCLKCNT_MCLKFS_512FS	(BIT(8) | BIT(10))
+#define I2SCLKCNT_MCLKFS_768FS	(BIT(9) | BIT(10))
+#define I2SCLKCNT_MCLKFS_1024FS	(BIT(8) | BIT(9) | BIT(10))
+
+#define I2SCLKCNT_BCLKFS_8FS	0
+#define I2SCLKCNT_BCLKFS_16FS	BIT(12)
+#define I2SCLKCNT_BCLKFS_32FS	BIT(13)
+#define I2SCLKCNT_BCLKFS_64FS	(BIT(12) | BIT(13))
+
+#define I2SCLKCNT_MSSEL		BIT(0)
+#define I2SCLKCNT_BCLKPOL	BIT(1)
+
+#define I2SCLKCNT_LRCKFMT_I2S	0
+#define I2SCLKCNT_LRCKFMT_LONG	BIT(5)
+#define I2SCLKCNT_LRCKFMT_SHORT	(BIT(4)|BIT(5))
+
+#define DRV_NAME "ml7213_ioh_i2s"
+#define PCI_VENDOR_ID_ROHM	0X10DB
+#define PCI_DEVICE_ID_ML7213_I2S	0X8033
+
+struct snd_ml7213i2s {
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	struct ioh_i2s_data_pci *pci_dat;
+};
+
+struct cbdata {
+	struct ioh_i2s_data *priv;
+	int stop;
+	int cnt;
+};
+
+struct snd_ml7213i2s_pcm {
+	unsigned int channels;
+	unsigned int rate;
+	unsigned int ch;
+	unsigned int format;
+	unsigned int bclkfs;
+};
+
+struct snd_ml7213i2s_dma {
+	unsigned int master_mode;
+	spinlock_t lock;
+	unsigned int irq_pos;
+	unsigned int buf_pos;
+	struct snd_pcm_substream *substream;
+	struct cbdata cbd;              /* i2s callback info */
+	unsigned int rw;
+};
+
+
+static struct snd_pcm_hardware snd_card_ml7213i2s_capture[MAX_I2S_CH] = {
+	{
+		.info =			(SNDRV_PCM_INFO_MMAP |
+					 SNDRV_PCM_INFO_INTERLEAVED |
+					 SNDRV_PCM_INFO_RESUME |
+					 SNDRV_PCM_INFO_MMAP_VALID),
+		.formats =		SUPPORT_FORMAT,
+		.rates =		USE_RATE_CH0,
+		.rate_min =		USE_RATE_MIN_CH0,
+		.rate_max =		USE_RATE_MAX_CH0,
+		.channels_min =		USE_CHANNELS_MIN,
+		.channels_max =		USE_CHANNELS_MAX,
+		.buffer_bytes_max =	(MAX_PERIOD_SIZE_RX *
+					 USE_PERIODS_MAX),
+		.period_bytes_min =	MAX_PERIOD_SIZE_RX,
+		.period_bytes_max =	MAX_PERIOD_SIZE_RX,
+		.periods_min =		USE_PERIODS_MIN,
+		.periods_max =		USE_PERIODS_MAX,
+		.fifo_size =		0,
+	},
+	{
+		.info =			(SNDRV_PCM_INFO_MMAP |
+					 SNDRV_PCM_INFO_INTERLEAVED |
+					 SNDRV_PCM_INFO_RESUME |
+					 SNDRV_PCM_INFO_MMAP_VALID),
+		.formats =		SUPPORT_FORMAT,
+		.rates =		USE_RATE_CH1,
+		.rate_min =		USE_RATE_MIN_CH1,
+		.rate_max =		USE_RATE_MAX_CH1,
+		.channels_min =		USE_CHANNELS_MIN,
+		.channels_max =		USE_CHANNELS_MAX,
+		.buffer_bytes_max =	(MAX_PERIOD_SIZE_RX *
+					 USE_PERIODS_MAX),
+		.period_bytes_min =	MAX_PERIOD_SIZE_RX,
+		.period_bytes_max =	MAX_PERIOD_SIZE_RX,
+		.periods_min =		USE_PERIODS_MIN,
+		.periods_max =		USE_PERIODS_MAX,
+		.fifo_size =		0,
+	},
+	{
+		.info =			(SNDRV_PCM_INFO_MMAP |
+					 SNDRV_PCM_INFO_INTERLEAVED |
+					 SNDRV_PCM_INFO_RESUME |
+					 SNDRV_PCM_INFO_MMAP_VALID),
+		.formats =		SUPPORT_FORMAT,
+		.rates =		USE_RATE_CH2,
+		.rate_min =		USE_RATE_MIN_CH2,
+		.rate_max =		USE_RATE_MAX_CH2,
+		.channels_min =		USE_CHANNELS_MIN,
+		.channels_max =		USE_CHANNELS_MAX,
+		.buffer_bytes_max =	(MAX_PERIOD_SIZE_RX *
+					 USE_PERIODS_MAX),
+		.period_bytes_min =	MAX_PERIOD_SIZE_RX,
+		.period_bytes_max =	MAX_PERIOD_SIZE_RX,
+		.periods_min =		USE_PERIODS_MIN,
+		.periods_max =		USE_PERIODS_MAX,
+		.fifo_size =		0,
+	},
+	{
+		.info =			(SNDRV_PCM_INFO_MMAP |
+					 SNDRV_PCM_INFO_INTERLEAVED |
+					 SNDRV_PCM_INFO_RESUME |
+					 SNDRV_PCM_INFO_MMAP_VALID),
+		.formats =		SUPPORT_FORMAT,
+		.rates =		USE_RATE_CH3,
+		.rate_min =		USE_RATE_MIN_CH3,
+		.rate_max =		USE_RATE_MAX_CH3,
+		.channels_min =		USE_CHANNELS_MIN,
+		.channels_max =		USE_CHANNELS_MAX,
+		.buffer_bytes_max =	(MAX_PERIOD_SIZE_RX *
+					 USE_PERIODS_MAX),
+		.period_bytes_min =	MAX_PERIOD_SIZE_RX,
+		.period_bytes_max =	MAX_PERIOD_SIZE_RX,
+		.periods_min =		USE_PERIODS_MIN,
+		.periods_max =		USE_PERIODS_MAX,
+		.fifo_size =		0,
+	},
+	{
+		.info =			(SNDRV_PCM_INFO_MMAP |
+					 SNDRV_PCM_INFO_INTERLEAVED |
+					 SNDRV_PCM_INFO_RESUME |
+					 SNDRV_PCM_INFO_MMAP_VALID),
+		.formats =		SUPPORT_FORMAT,
+		.rates =		USE_RATE_CH4,
+		.rate_min =		USE_RATE_MIN_CH4,
+		.rate_max =		USE_RATE_MAX_CH4,
+		.channels_min =		USE_CHANNELS_MIN,
+		.channels_max =		USE_CHANNELS_MAX,
+		.buffer_bytes_max =	(MAX_PERIOD_SIZE_RX *
+					 USE_PERIODS_MAX),
+		.period_bytes_min =	MAX_PERIOD_SIZE_RX,
+		.period_bytes_max =	MAX_PERIOD_SIZE_RX,
+		.periods_min =		USE_PERIODS_MIN,
+		.periods_max =		USE_PERIODS_MAX,
+		.fifo_size =		0,
+	},
+	{
+		.info =			(SNDRV_PCM_INFO_MMAP |
+					 SNDRV_PCM_INFO_INTERLEAVED |
+					 SNDRV_PCM_INFO_RESUME |
+					 SNDRV_PCM_INFO_MMAP_VALID),
+		.formats =		SUPPORT_FORMAT,
+		.rates =		USE_RATE_CH5,
+		.rate_min =		USE_RATE_MIN_CH5,
+		.rate_max =		USE_RATE_MAX_CH5,
+		.channels_min =		USE_CHANNELS_MIN,
+		.channels_max =		USE_CHANNELS_MAX,
+		.buffer_bytes_max =	(MAX_PERIOD_SIZE_RX *
+					 USE_PERIODS_MAX),
+		.period_bytes_min =	MAX_PERIOD_SIZE_RX,
+		.period_bytes_max =	MAX_PERIOD_SIZE_RX,
+		.periods_min =		USE_PERIODS_MIN,
+		.periods_max =		USE_PERIODS_MAX,
+		.fifo_size =		0,
+	},
+};
+
+static struct snd_pcm_hardware snd_card_ml7213i2s_playback[MAX_I2S_CH] = {
+	{
+		.info =			(SNDRV_PCM_INFO_MMAP |
+					 SNDRV_PCM_INFO_INTERLEAVED |
+					 SNDRV_PCM_INFO_RESUME |
+					 SNDRV_PCM_INFO_MMAP_VALID),
+		.formats =		SUPPORT_FORMAT,
+		.rates =		USE_RATE_CH0,
+		.rate_min =		USE_RATE_MIN_CH0,
+		.rate_max =		USE_RATE_MAX_CH0,
+		.channels_min =		USE_CHANNELS_MIN,
+		.channels_max =		USE_CHANNELS_MAX,
+		.buffer_bytes_max =	(MAX_PERIOD_SIZE_TX *
+					 USE_PERIODS_MAX),
+		.period_bytes_min =	MAX_PERIOD_SIZE_TX,
+		.period_bytes_max =	MAX_PERIOD_SIZE_TX,
+		.periods_min =		USE_PERIODS_MIN,
+		.periods_max =		USE_PERIODS_MAX,
+		.fifo_size =		0,
+	},
+	{
+		.info =			(SNDRV_PCM_INFO_MMAP |
+					 SNDRV_PCM_INFO_INTERLEAVED |
+					 SNDRV_PCM_INFO_RESUME |
+					 SNDRV_PCM_INFO_MMAP_VALID),
+		.formats =		SUPPORT_FORMAT,
+		.rates =		USE_RATE_CH1,
+		.rate_min =		USE_RATE_MIN_CH1,
+		.rate_max =		USE_RATE_MAX_CH1,
+		.channels_min =		USE_CHANNELS_MIN,
+		.channels_max =		USE_CHANNELS_MAX,
+		.buffer_bytes_max =	(MAX_PERIOD_SIZE_TX *
+					 USE_PERIODS_MAX),
+		.period_bytes_min =	MAX_PERIOD_SIZE_TX,
+		.period_bytes_max =	MAX_PERIOD_SIZE_TX,
+		.periods_min =		USE_PERIODS_MIN,
+		.periods_max =		USE_PERIODS_MAX,
+		.fifo_size =		0,
+	},
+	{
+		.info =			(SNDRV_PCM_INFO_MMAP |
+					 SNDRV_PCM_INFO_INTERLEAVED |
+					 SNDRV_PCM_INFO_RESUME |
+					 SNDRV_PCM_INFO_MMAP_VALID),
+		.formats =		SUPPORT_FORMAT,
+		.rates =		USE_RATE_CH2,
+		.rate_min =		USE_RATE_MIN_CH2,
+		.rate_max =		USE_RATE_MAX_CH2,
+		.channels_min =		USE_CHANNELS_MIN,
+		.channels_max =		USE_CHANNELS_MAX,
+		.buffer_bytes_max =	(MAX_PERIOD_SIZE_TX *
+					 USE_PERIODS_MAX),
+		.period_bytes_min =	MAX_PERIOD_SIZE_TX,
+		.period_bytes_max =	MAX_PERIOD_SIZE_TX,
+		.periods_min =		USE_PERIODS_MIN,
+		.periods_max =		USE_PERIODS_MAX,
+		.fifo_size =		0,
+	},
+	{
+		.info =			(SNDRV_PCM_INFO_MMAP |
+					 SNDRV_PCM_INFO_INTERLEAVED |
+					 SNDRV_PCM_INFO_RESUME |
+					 SNDRV_PCM_INFO_MMAP_VALID),
+		.formats =		SUPPORT_FORMAT,
+		.rates =		USE_RATE_CH3,
+		.rate_min =		USE_RATE_MIN_CH3,
+		.rate_max =		USE_RATE_MAX_CH3,
+		.channels_min =		USE_CHANNELS_MIN,
+		.channels_max =		USE_CHANNELS_MAX,
+		.buffer_bytes_max =	(MAX_PERIOD_SIZE_TX *
+					 USE_PERIODS_MAX),
+		.period_bytes_min =	MAX_PERIOD_SIZE_TX,
+		.period_bytes_max =	MAX_PERIOD_SIZE_TX,
+		.periods_min =		USE_PERIODS_MIN,
+		.periods_max =		USE_PERIODS_MAX,
+		.fifo_size =		0,
+	},
+	{
+		.info =			(SNDRV_PCM_INFO_MMAP |
+					 SNDRV_PCM_INFO_INTERLEAVED |
+					 SNDRV_PCM_INFO_RESUME |
+					 SNDRV_PCM_INFO_MMAP_VALID),
+		.formats =		SUPPORT_FORMAT,
+		.rates =		USE_RATE_CH4,
+		.rate_min =		USE_RATE_MIN_CH4,
+		.rate_max =		USE_RATE_MAX_CH4,
+		.channels_min =		USE_CHANNELS_MIN,
+		.channels_max =		USE_CHANNELS_MAX,
+		.buffer_bytes_max =	(MAX_PERIOD_SIZE_TX *
+					 USE_PERIODS_MAX),
+		.period_bytes_min =	MAX_PERIOD_SIZE_TX,
+		.period_bytes_max =	MAX_PERIOD_SIZE_TX,
+		.periods_min =		USE_PERIODS_MIN,
+		.periods_max =		USE_PERIODS_MAX,
+		.fifo_size =		0,
+	},
+	{
+		.info =			(SNDRV_PCM_INFO_MMAP |
+					 SNDRV_PCM_INFO_INTERLEAVED |
+					 SNDRV_PCM_INFO_RESUME |
+					 SNDRV_PCM_INFO_MMAP_VALID),
+		.formats =		SUPPORT_FORMAT,
+		.rates =		USE_RATE_CH5,
+		.rate_min =		USE_RATE_MIN_CH5,
+		.rate_max =		USE_RATE_MAX_CH5,
+		.channels_min =		USE_CHANNELS_MIN,
+		.channels_max =		USE_CHANNELS_MAX,
+		.buffer_bytes_max =	(MAX_PERIOD_SIZE_TX *
+					 USE_PERIODS_MAX),
+		.period_bytes_min =	MAX_PERIOD_SIZE_TX,
+		.period_bytes_max =	MAX_PERIOD_SIZE_TX,
+		.periods_min =		USE_PERIODS_MIN,
+		.periods_max =		USE_PERIODS_MAX,
+		.fifo_size =		0,
+	},
+};
+
+static struct ioh_i2s_dma dmadata[6];
+static int ignore_overrun = 1;
+static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
+module_param(ignore_overrun, int, 0444);
+module_param(index, int, 0444);
+MODULE_PARM_DESC(ignore_overrun, "ignore RX overruns (default=0)");
+MODULE_PARM_DESC(index, "Index value for ML7213 IOH I2S Controller Reference");
+
+/*****************************************************************************
+ *	I2S HAL (Hardware Abstruction Layer)
+ *****************************************************************************/
+static void ioh_i2s_reset(struct ioh_i2s_data *priv, int ch)
+{
+	iowrite32(1 << ch, priv->iobase + I2SSRST_OFFSET);
+	iowrite32(0, priv->iobase + I2SSRST_OFFSET);
+}
+
+static void ioh_i2s_enable_interrupts(struct ioh_i2s_data *priv,
+				      enum dma_data_direction dir)
+{
+	int channel = priv->ch;
+	unsigned int intr_lines;
+
+	if (dir)
+		intr_lines = 1 << (I2S_IMASK_RX_BIT_START + channel);
+
+	else
+		intr_lines = 1 << (I2S_IMASK_TX_BIT_START + channel);
+
+	/*enable interrupts for specified channel */
+	iowrite32(intr_lines, priv->iobase + I2SIMASKCLR_OFFSET);
+}
+
+static void ioh_i2s_disable_interrupts(struct ioh_i2s_data *priv,
+				       enum dma_data_direction dir)
+{
+	int channel = priv->ch;
+	unsigned int intr_lines;
+
+	/*intr_lines&=I2S_ALL_INTERRUPT_BITS; */
+	intr_lines = ioread32(priv->iobase + I2SIMASK_OFFSET);
+
+	/*disable interrupts for specified channel */
+	if (dir)
+		intr_lines |= 1 << (I2S_IMASK_RX_BIT_START + channel);
+	else
+		intr_lines |= 1 << (I2S_IMASK_TX_BIT_START + channel);
+
+	/*Mask the specific interrupt bits */
+	iowrite32(intr_lines, priv->iobase + I2SIMASK_OFFSET);
+}
+
+/* Run FIFO */
+static void ioh_i2s_run_tx_fifo(struct ioh_i2s_data *priv)
+{
+	int ch = priv->ch;
+	int offset = ch * 0x800;
+	u32 val;
+
+	val = ioread32(priv->iobase + I2SFIFOCTX_OFFSET + offset);
+	val |= I2S_FIFO_TX_RUN;
+
+	iowrite32(val, priv->iobase + I2SFIFOCTX_OFFSET + offset);
+}
+
+/* Clear TX FIFO */
+static void ioh_i2s_clear_tx_fifo(struct ioh_i2s_data *priv)
+{
+	int ch = priv->ch;
+	int offset = ch * 0x800;
+	u32 val;
+
+	val = ioread32(priv->iobase + I2SFIFOCTX_OFFSET + offset);
+	val |= I2S_FIFO_TX_FCLR;
+
+	iowrite32(val, priv->iobase + I2SFIFOCTX_OFFSET + offset);
+}
+
+/* Clear interrupt status */
+static void ioh_i2s_clear_tx_sts_ir(struct ioh_i2s_data *priv)
+{
+	int ch = priv->ch;
+	int offset = ch * 0x800;
+
+	iowrite32(I2S_TX_FINT | I2S_TX_AFINT | I2S_TX_EINT | I2S_TX_AEINT,
+		priv->iobase + I2SISTTX_OFFSET + offset);
+}
+
+/* Run FIFO */
+static void ioh_i2s_run_rx_fifo(struct ioh_i2s_data *priv)
+{
+	int ch = priv->ch;
+	int offset = ch * 0x800;
+	u32 val;
+
+	val = ioread32(priv->iobase + I2SFIFOCRX_OFFSET + offset);
+	val |= I2S_FIFO_RX_RUN;
+	iowrite32(val, priv->iobase + I2SFIFOCRX_OFFSET + offset);
+}
+
+/* Clear RX FIFO */
+static void ioh_i2s_clear_rx_fifo(struct ioh_i2s_data *priv)
+{
+	int ch = priv->ch;
+	int offset = ch * 0x800;
+	u32 val;
+
+	val = ioread32(priv->iobase + I2SFIFOCRX_OFFSET + offset);
+	val |= I2S_FIFO_RX_FCLR;
+	iowrite32(val, priv->iobase + I2SFIFOCRX_OFFSET + offset);
+}
+
+/* Clear interrupt status */
+static void ioh_i2s_clear_rx_sts_ir(struct ioh_i2s_data *priv)
+{
+	int ch = priv->ch;
+	int offset = ch * 0x800;
+
+	iowrite32(I2S_RX_FINT | I2S_RX_AFINT | I2S_RX_EINT | I2S_RX_AEINT,
+		priv->iobase + I2SISTRX_OFFSET + offset);
+}
+
+/* Clear DMA mask setting */
+static void ioh_i2s_tx_clear_dma_mask(struct ioh_i2s_data *priv)
+{
+	int ch = priv->ch;
+	int offset = ch * 0x800;
+	u32 val;
+
+	val = ioread32(priv->iobase + I2SMSKTX_OFFSET + offset);
+	val &= ~TX_BIT_DMAMSK; /* Enable Tx DMA Request */
+
+	iowrite32(val, priv->iobase + I2SMSKTX_OFFSET + offset);
+}
+
+/* Clear DMA mask setting */
+static void ioh_i2s_rx_clear_dma_mask(struct ioh_i2s_data *priv)
+{
+	int ch = priv->ch;
+	int offset = ch * 0x800;
+	u32 val;
+
+	val = ioread32(priv->iobase + I2SMSKRX_OFFSET + offset);
+	val &= ~RX_BIT_DMAMSK; /* Enable Rx DMA Request */
+	iowrite32(val, priv->iobase + I2SMSKRX_OFFSET + offset);
+}
+
+/* Clear the mask setting of the corresponding interrupt source bit */
+static void ioh_i2s_enable_tx_empty_ir(struct ioh_i2s_data *priv)
+{
+	int ch = priv->ch;
+	int offset = ch * 0x800;
+	u32 val;
+
+	val = ioread32(priv->iobase + I2SMSKTX_OFFSET + offset);
+	val &= ~TX_BIT_AEIMSK; /* Enable Almost empty interrupt */
+	val &= ~TX_BIT_EIMSK; /* Enable Empty interrupt */
+
+	iowrite32(val, priv->iobase + I2SMSKTX_OFFSET + offset);
+}
+
+/* Clear the mask setting of the corresponding interrupt source bit */
+static void ioh_i2s_enable_rx_full_ir(struct ioh_i2s_data *priv)
+{
+	int ch = priv->ch;
+	int offset = ch * 0x800;
+	u32 val;
+	val = ioread32(priv->iobase + I2SMSKRX_OFFSET + offset);
+
+	val &= ~RX_BIT_AFIMSK; /* Enable Almost empty interrupt */
+	val &= ~RX_BIT_FIMSK; /* Enable Empty interrupt */
+
+	iowrite32(val, priv->iobase + I2SMSKRX_OFFSET + offset);
+}
+
+static void ioh_i2s_disable_tx_empty_ir(struct ioh_i2s_data *priv)
+{
+	int ch = priv->ch;
+	int offset = ch * 0x800;
+	u32 val;
+
+	val = ioread32(priv->iobase + I2SMSKTX_OFFSET + offset);
+	val |= TX_BIT_AEIMSK; /* Disble Almost empty interrupt */
+	val |= TX_BIT_EIMSK; /* Disble Empty interrupt */
+
+	iowrite32(val, priv->iobase + I2SMSKTX_OFFSET + offset);
+}
+
+static void ioh_i2s_disable_rx_full_ir(struct ioh_i2s_data *priv)
+{
+	int ch = priv->ch;
+	int offset = ch * 0x800;
+	u32 val;
+
+	val = ioread32(priv->iobase + I2SMSKRX_OFFSET + offset);
+	val |= RX_BIT_AFIMSK; /* Disble Almost full interrupt */
+	val |= RX_BIT_FIMSK; /* Disble full interrupt */
+
+	iowrite32(val, priv->iobase + I2SMSKRX_OFFSET + offset);
+}
+
+/*****************************************************************************
+ *	I2S Middle ware
+ *****************************************************************************/
+static bool 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;
+	}
+}
+
+static	struct dma_chan *ioh_request_dma_channel(
+			struct ioh_i2s_data *priv, struct ioh_i2s_dma *dma,
+			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 */
+
+	if (dir == DMA_FROM_DEVICE) { /* Rx */
+		dma->param_rx.width = dma->dma_rx_width;
+		dma->param_rx.dma_dev = &dma_dev->dev;
+		dma->param_rx.chan_id = priv->ch * 2 + 1; /* ch Rx=1,3,...11 */
+		dma->param_rx.rx_reg = (dma_addr_t)(priv->mapbase +\
+					priv->ch * 0x800 +\
+					I2SDRRXMIRROR_OFFSET);
+		chan = dma_request_channel(mask, filter, &dma->param_rx);
+		if (chan == NULL) {
+			dev_err(priv->dev, "Failed dma_request_channel %s for"
+				" I2S %d\n", dma->dma_config->rx_chan, priv->ch);
+			return NULL;
+		}
+		dma->chan_rx = chan;
+
+	} else if (dir == DMA_TO_DEVICE) { /* Tx */
+		dma->param_tx.width = dma->dma_tx_width;
+		dma->param_tx.dma_dev = &dma_dev->dev;
+		dma->param_tx.chan_id = priv->ch * 2; /* DMA ch Tx=0,2,...10 */
+
+		dma->param_tx.tx_reg = (dma_addr_t)(priv->mapbase +\
+					priv->ch * 0x800 +\
+					I2SDRTXMIRROR_OFFSET);
+
+		dma->param_tx.tx_reg_list = NULL;
+
+		chan = dma_request_channel(mask, filter, &dma->param_tx);
+		if (chan == NULL) {
+			dev_err(priv->dev, "Failed dma_request_channel %s for"
+				" I2S %d\n", dma->dma_config->tx_chan, priv->ch);
+			return NULL;
+		}
+		dma->chan_tx = chan;
+	} else {
+		dev_err(priv->dev, "Invalid direction (%d)\n", dir);
+		return NULL;
+	}
+
+	return chan;
+}
+
+static void ioh_i2s_ignore_rx_overrun(struct ioh_i2s_data *priv)
+{
+	priv->ignore_rx_overrun = 1;
+}
+
+static void ioh_i2s_write(struct ioh_i2s_data *priv,
+		       const void *data,
+		       int len)
+{
+	int rem1;
+	int rem2;
+	int tx_index;
+	struct ioh_i2s_dma *dma = &dmadata[priv->ch];
+	struct scatterlist *sg = dma->sg_tx_p;
+	int t_num = 0;
+	int l;
+	int *ptr_fmt;
+	int *ptr32;
+	short *ptr16;
+	char *ptr8;
+	int tx_unit = dmadata[priv->ch].dma_tx_unit;
+
+	if (dma->tx_avail >= INTER_BUFF_SIZE) {
+		dev_err(priv->dev, "%s[%d]: internal buffer full\n",
+			__func__, priv->ch);
+		return;
+	}
+
+	dev_dbg(priv->dev, "%s: [ch%d] len=%d data_head=%p data_complete=%p",
+		__func__, priv->ch, len, dma->tx_data_head, dma->tx_complete);
+
+	if ((dma->tx_data_head + ((len/tx_unit)*4)) <=
+	    dma->tx_tail) {
+		tx_index = (int)(dma->tx_data_head - dma->tx_head) /
+				(I2S_AEMPTY_THRESH * 4);
+		sg = sg + tx_index;
+		t_num = len/(I2S_AEMPTY_THRESH * tx_unit);
+		dma_sync_sg_for_cpu(priv->dev, sg, t_num, DMA_TO_DEVICE);
+
+		ptr_fmt = (int *)dma->tx_data_head;
+		switch (tx_unit) {
+		case 1:
+			ptr8 = (char *)data;
+			for (l = 0; l < (len/tx_unit); l++)
+				*ptr_fmt++ = (int)*ptr8++;
+			break;
+		case 2:
+			ptr16 = (short *)data;
+			for (l = 0; l < (len/tx_unit); l++)
+				*ptr_fmt++ = (int)*ptr16++;
+			break;
+		case 4:
+			ptr32 = (int *)data;
+			for (l = 0; l < (len/tx_unit); l++)
+				*ptr_fmt++ = *ptr32++;
+			break;
+		}
+		dma_sync_sg_for_device(priv->dev, sg, t_num, DMA_TO_DEVICE);
+		dma->tx_data_head += (len/tx_unit)*4;
+	} else {
+		rem1 = (dma->tx_tail - dma->tx_data_head)/4;
+		rem2 = (len/tx_unit) - rem1;
+		tx_index = (int)(dma->tx_data_head-dma->tx_head) /
+				(I2S_AEMPTY_THRESH * 4);
+		sg = sg + tx_index;
+		t_num = rem1/I2S_AEMPTY_THRESH;
+		dma_sync_sg_for_cpu(priv->dev, sg, t_num, DMA_TO_DEVICE);
+		ptr_fmt = (int *)dma->tx_data_head;
+		switch (tx_unit) {
+		case 1:
+			ptr8 = (char *)data;
+			for (l = 0; l < rem1; l++)
+				*ptr_fmt++ = (int)*ptr8++;
+			break;
+		case 2:
+			ptr16 = (short *)data;
+			for (l = 0; l < rem1; l++)
+				*ptr_fmt++ = (int)*ptr16++;
+			break;
+		case 4:
+			ptr32 = (int *)data;
+			for (l = 0; l < rem1; l++)
+				*ptr_fmt++ = *ptr32++;
+			break;
+		}
+
+		dma_sync_sg_for_device(priv->dev, sg, t_num, DMA_TO_DEVICE);
+		dma->tx_data_head = dma->tx_head;
+		sg = dma->sg_tx_p;
+		t_num = rem2/I2S_AEMPTY_THRESH;
+		dma_sync_sg_for_cpu(priv->dev, sg, t_num, DMA_TO_DEVICE);
+
+		ptr_fmt = (int *)dma->tx_data_head;
+
+		switch (tx_unit) {
+		case 1:
+			ptr8 = (char *)(data+rem1*tx_unit);
+			for (l = 0; l < rem2; l++)
+				*ptr_fmt++ = (int)*ptr8++;
+			break;
+		case 2:
+			ptr16 = (short *)(data+rem1*tx_unit);
+			for (l = 0; l < rem2; l++)
+				*ptr_fmt++ = (int)*ptr16++;
+			break;
+		case 4:
+			ptr32 = (int *)(data+rem1*tx_unit);
+			for (l = 0; l < rem2; l++)
+				*ptr_fmt++ = *ptr32++;
+			break;
+		}
+
+		dma_sync_sg_for_device(priv->dev, sg, t_num, DMA_TO_DEVICE);
+		dma->tx_data_head += rem2*4;
+	}
+
+	if (dma->tx_data_head >= dma->tx_tail)
+		dma->tx_data_head = dma->tx_head;
+
+	dev_dbg(priv->dev, "-->data_head=%p\n", dma->tx_data_head);
+
+	dma->tx_avail += (len/tx_unit)*4;
+}
+
+static void ioh_i2s_stop_i2s_regs(struct ioh_i2s_data *priv,
+			       unsigned long bitrate,
+			       struct ioh_i2s_config_reg *config,
+			       enum ioh_direction dir,
+			       unsigned int flag)
+{
+	int ch = priv->ch;
+
+	if (dir) {
+		/* Interrupt stop */
+		ioh_i2s_disable_rx_full_ir(priv);
+
+		/* FIFO setting */
+		ioh_i2s_clear_rx_fifo(priv);
+		ioh_i2s_clear_rx_sts_ir(priv);
+	} else {
+		/* Interrupt stop */
+		ioh_i2s_disable_tx_empty_ir(priv);
+
+		/* FIFO setting */
+		ioh_i2s_clear_tx_fifo(priv);
+		ioh_i2s_clear_tx_sts_ir(priv);
+	}
+
+	/* Common register */
+	if (!flag) {
+		iowrite32(config->cmn.i2sclkcnt,
+			  priv->iobase + I2SCLKCNT0_OFFSET + 0x10*ch);
+	}
+}
+
+static void ioh_i2s_configure_i2s_regs(struct ioh_i2s_data *priv,
+				    unsigned long bitrate,
+				    struct ioh_i2s_config_reg *config,
+				    enum ioh_direction dir,
+				    unsigned int unit)
+{
+	int ch = priv->ch;
+	int offset = ch * 0x800;
+
+	/* Common register */
+	iowrite32(config->cmn.i2sclkcnt,
+		  priv->iobase + I2SCLKCNT0_OFFSET + 0x10*ch);
+
+	if (dir) {
+		dmadata[ch].dma_rx_unit = unit;
+		/* Rx register */
+		iowrite32(config->rx.i2scntrx,
+			  priv->iobase + I2SCNTRX_OFFSET + offset);
+		iowrite32(config->rx.i2sfifocrx,
+			  priv->iobase + I2SFIFOCRX_OFFSET + offset);
+		iowrite32(config->rx.i2safrx,
+			  priv->iobase + I2SAFRX_OFFSET + offset);
+		iowrite32(config->rx.i2saerx,
+			  priv->iobase + I2SAERX_OFFSET + offset);
+		iowrite32(config->rx.i2smskrx,
+			  priv->iobase + I2SMSKRX_OFFSET + offset);
+		iowrite32(config->rx.i2sistrx,
+			  priv->iobase + I2SISTRX_OFFSET + offset);
+
+		/* FIFO setting */
+		ioh_i2s_clear_rx_fifo(priv);
+		ioh_i2s_run_rx_fifo(priv);
+
+		/* Interrupt setting */
+		ioh_i2s_clear_rx_sts_ir(priv);
+		ioh_i2s_enable_rx_full_ir(priv);
+
+	} else {
+		dmadata[ch].dma_tx_unit = unit;
+		/* Tx register */
+		iowrite32(config->tx.i2scnttx,
+			  priv->iobase + I2SCNTTX_OFFSET + offset);
+		iowrite32(config->tx.i2sfifoctx,
+			  priv->iobase + I2SFIFOCTX_OFFSET + offset);
+		iowrite32(config->tx.i2saftx,
+			  priv->iobase + I2SAFTX_OFFSET + offset);
+		iowrite32(config->tx.i2saetx,
+			  priv->iobase + I2SAETX_OFFSET + offset);
+		iowrite32(config->tx.i2smsktx,
+			  priv->iobase + I2SMSKTX_OFFSET + offset);
+		iowrite32(config->tx.i2sisttx,
+			  priv->iobase + I2SISTTX_OFFSET + offset);
+
+		/* FIFO setting */
+		ioh_i2s_clear_tx_fifo(priv);
+		ioh_i2s_run_tx_fifo(priv);
+
+		/* Interrupt setting */
+		ioh_i2s_clear_tx_sts_ir(priv);
+		ioh_i2s_enable_tx_empty_ir(priv);
+	}
+}
+
+static void i2s_rx_tasklet(unsigned long data)
+{
+	struct ioh_i2s_data *priv = (struct ioh_i2s_data *)data;
+	struct ioh_i2s_dma *dma = &dmadata[priv->ch];
+	int num = 0;
+
+	if (dma->rxexe_flag) {
+		if (dma->rx_done) {
+			switch (dma->dma_rx_unit) {
+			case 1:
+				num = dma->rx_avail/4;
+				break;
+			case 2:
+				num = dma->rx_avail/2;
+				break;
+			case 4:
+				num = dma->rx_avail;
+				break;
+			}
+			dma->rx_done(dma->rx_callback_data,
+				      IOH_EOK, num, num);
+		}
+	}
+}
+
+static void i2s_tx_tasklet(unsigned long data)
+{
+	struct ioh_i2s_data *priv = (struct ioh_i2s_data *)data;
+	struct ioh_i2s_dma *dma = &dmadata[priv->ch];
+	int num = 0;
+	int avail = 0;
+
+	if (dma->txexe_flag) {
+		if (dma->tx_done) {
+			switch (dmadata[priv->ch].dma_tx_unit) {
+			case 1:
+				num = (INTER_BUFF_SIZE - dma->tx_avail)/4;
+				avail = dma->tx_avail/4;
+				break;
+			case 2:
+				num = (INTER_BUFF_SIZE - dma->tx_avail)/2;
+				avail = dma->tx_avail/2;
+				break;
+			case 4:
+				num = (INTER_BUFF_SIZE - dma->tx_avail);
+				avail = dma->tx_avail;
+				break;
+			}
+			dma->tx_done(dma->tx_callback_data,
+				      IOH_EOK, num, avail);
+		}
+	}
+}
+
+static void ioh_i2s_release(struct ioh_i2s_data *priv, enum ioh_direction dir)
+{
+	struct ioh_i2s_dma *dma;
+
+	if (!priv) {
+		dev_err(priv->dev, "%s: i2s is NULL\n", __func__);
+		return;
+	}
+
+	dma = &dmadata[priv->ch];
+	if (dir) {
+		dma_sync_sg_for_cpu(priv->dev, dma->sg_rx_p, dma->rx_nent,
+				    DMA_FROM_DEVICE);
+
+		ioh_i2s_disable_interrupts(priv, IOH_CAPTURE);
+		ioh_i2s_disable_rx_full_ir(priv);
+		if (dma->chan_rx) {
+			dma->chan_rx->device->device_control(dma->chan_rx,
+							     DMA_TERMINATE_ALL,
+							     0);
+			dma_release_channel(dma->chan_rx);
+			dma->chan_rx = NULL;
+		}
+
+		kfree(dma->sg_rx_p);
+		if (dma->rxbuf_virt)
+			dma_free_coherent(priv->dev, INTER_BUFF_SIZE,
+					  dma->rxbuf_virt,
+					  dma->rx_buf_dma);
+
+		dma->rxbuf_virt = NULL;
+		dma->rx_buf_dma = 0;
+		atomic_dec(&dma->rx_busy);
+
+		tasklet_disable(&dma->rx_tasklet);
+		tasklet_kill(&dma->rx_tasklet);
+
+	} else {
+		dma_sync_sg_for_cpu(priv->dev, dma->sg_tx_p, dma->tx_nent,
+				    DMA_TO_DEVICE);
+
+		ioh_i2s_disable_interrupts(priv, IOH_PLAYBACK);
+		ioh_i2s_disable_tx_empty_ir(priv);
+		if (dma->chan_tx) {
+			dma->chan_tx->device->device_control(dma->chan_tx,
+							     DMA_TERMINATE_ALL,
+							     0);
+			dma_release_channel(dma->chan_tx);
+			dma->chan_tx = NULL;
+		}
+
+		kfree(dma->sg_tx_p);
+		if (dma->txbuf_virt)
+			dma_free_coherent(priv->dev, INTER_BUFF_SIZE,
+					  dma->txbuf_virt,
+					  dma->tx_buf_dma);
+
+		dma->txbuf_virt = NULL;
+		dma->tx_buf_dma = 0;
+		atomic_dec(&dma->tx_busy);
+
+		tasklet_disable(&dma->tx_tasklet);
+		tasklet_kill(&dma->tx_tasklet);
+	}
+}
+
+static struct ioh_i2s_data *ioh_i2s_open(int ch, enum ioh_direction dir,
+				     const char *name)
+{
+	struct ioh_i2s_data *obj = NULL;
+	struct scatterlist *sg;
+	int rx_size;
+	int rx_num;
+	int tx_size;
+	int tx_num;
+	int i;
+	struct ioh_i2s_dma *dma;
+
+	if (ch >= MAX_I2S_IF) {
+		dev_err(obj->dev,
+			"Tried to open i2s with number %d which is more then"
+			" the available number\n", ch);
+		return 0;
+	}
+
+	dma = &dmadata[ch];
+	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+	obj->ignore_rx_overrun = 0;
+	obj->ch = ch;
+
+	atomic_set(&dma->rx_busy, 0);
+	atomic_set(&dma->tx_busy, 0);
+
+	dma->dma_tx_width = PCH_DMA_WIDTH_4_BYTES;
+	dma->dma_rx_width = PCH_DMA_WIDTH_4_BYTES;
+
+	if (dir) {
+		/* Rx configuration */
+		if (atomic_read(&dma->rx_busy)) {
+			dev_err(obj->dev, "rx i2s%d have already opened\n", ch);
+			atomic_dec(&dma->rx_busy);
+			return 0;
+		}
+		atomic_inc(&dma->rx_busy);
+
+		ioh_request_dma_channel(obj, dma, DMA_FROM_DEVICE);
+		if (!dma->chan_rx) {
+			dev_err(obj->dev, "%s:ioh_setup_rx_dma failed\n",
+				__func__);
+			return NULL;
+		}
+
+		dma->rxbuf_virt = dma_alloc_coherent(obj->dev, INTER_BUFF_SIZE,
+						&dma->rx_buf_dma, GFP_KERNEL);
+		if (!dma->rxbuf_virt) {
+			dev_err(obj->dev, "dma_alloc_coherent Failed\n");
+			return NULL;
+		}
+
+		rx_size = I2S_AFULL_THRESH * 4;
+		/* The number of scatter list (Franction area is not used) */
+		rx_num = INTER_BUFF_SIZE / rx_size;
+
+		dev_dbg(obj->dev, "%s: rx: scatter_num=%d scatter_size=%d\n",
+			__func__, rx_num, rx_size);
+
+		dma->sg_rx_p =\
+		       kzalloc(sizeof(struct scatterlist) *rx_num, GFP_ATOMIC);
+
+		sg = dma->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(dma->rxbuf_virt), rx_size,
+				    rx_size * i);
+			sg_dma_len(sg) = rx_size / 4;
+			sg_dma_address(sg) = dma->rx_buf_dma + sg->offset;
+		}
+
+		dma->rx_head = (unsigned char *)dma->rxbuf_virt;
+		dma->rx_tail = (unsigned char *)dma->rxbuf_virt +
+			       rx_num * rx_size;
+		dma->rx_data_head = (unsigned char *)dma->rxbuf_virt;
+		dma->rx_complete = (unsigned char *)dma->rxbuf_virt;
+		dma->rx_avail = 0;
+
+		dma->rx_nent = rx_num;
+		dma_sync_sg_for_device(obj->dev, dma->sg_rx_p, dma->rx_nent,
+				       DMA_FROM_DEVICE);
+
+		tasklet_init(&dma->rx_tasklet, i2s_rx_tasklet,
+			     (unsigned long)obj);
+	} else {
+		/* Tx configuration */
+		if (atomic_read(&dma->tx_busy)) {
+			dev_err(obj->dev, "tx i2s%d have already opened\n", ch);
+			atomic_dec(&dma->tx_busy);
+			return 0;
+		}
+		atomic_inc(&dma->tx_busy);
+
+		ioh_request_dma_channel(obj, dma, DMA_TO_DEVICE);
+
+		if (!dma->chan_tx) {
+			dev_err(obj->dev, "%s:ioh_setup_tx_dma failed\n",
+				__func__);
+			return NULL;
+		}
+
+		tx_size = I2S_AEMPTY_THRESH * 4;
+		if (INTER_BUFF_SIZE % tx_size)
+			/* tx_num = The number of scatter list */
+			tx_num = INTER_BUFF_SIZE / tx_size + 1;
+		else
+			tx_num = INTER_BUFF_SIZE / tx_size;
+
+		dma->txbuf_virt = dma_alloc_coherent(obj->dev, INTER_BUFF_SIZE,
+						&dma->tx_buf_dma, GFP_KERNEL);
+
+		if (!dma->txbuf_virt) {
+			dev_err(obj->dev, "dma_alloc_coherent Failed\n");
+			return NULL;
+		}
+
+		dma->tx_head = (unsigned char *)dma->txbuf_virt;
+		dma->tx_tail = (unsigned char *)dma->txbuf_virt +
+			       INTER_BUFF_SIZE;
+		dma->tx_data_head = (unsigned char *)dma->txbuf_virt;
+		dma->tx_complete = (unsigned char *)dma->txbuf_virt;
+		dma->tx_avail = 0;
+
+		dev_dbg(obj->dev, "%s: tx: scatter_num=%d scatter_size=%d\n",
+			__func__, tx_num, tx_size);
+
+		dma->sg_tx_p =\
+		       kzalloc(sizeof(struct scatterlist) *tx_num, GFP_ATOMIC);
+
+		sg_init_table(dma->sg_tx_p, tx_num); /* Initialize SG table */
+		sg = dma->sg_tx_p;
+
+		for (i = 0; i < tx_num; i++, sg++) {
+			if (i == (tx_num - 1)) {
+				if (INTER_BUFF_SIZE % tx_size) {
+					sg_set_page(sg,
+						  virt_to_page(dma->txbuf_virt),
+						  INTER_BUFF_SIZE % tx_size,
+						  tx_size * i);
+					sg_dma_len(sg) =
+						  (INTER_BUFF_SIZE % tx_size)
+						  / 4;
+				} else {
+					sg_set_page(sg,
+						  virt_to_page(dma->txbuf_virt),
+						  tx_size, tx_size * i);
+					sg_dma_len(sg) = tx_size\
+							/ 4;
+				}
+			} else {
+				sg_set_page(sg, virt_to_page(dma->txbuf_virt),
+					    tx_size, tx_size * i);
+				sg_dma_len(sg) = tx_size / 4;
+			}
+			sg_dma_address(sg) = dma->tx_buf_dma + sg->offset;
+		}
+		dma->tx_nent = tx_num;
+		dma_sync_sg_for_device(obj->dev, dma->sg_tx_p, dma->tx_nent,
+				       DMA_TO_DEVICE);
+
+		tasklet_init(&dma->tx_tasklet, i2s_tx_tasklet,
+			     (unsigned long)obj);
+	}
+
+	return obj;
+}
+
+static void ioh_i2s_read(struct ioh_i2s_data *priv,
+		      void *data,
+		      int len)
+{
+	unsigned int rem1 = 0, rem2 = 0;
+	struct ioh_i2s_dma *dma = &dmadata[priv->ch];
+	struct scatterlist *sg = dma->sg_rx_p;
+	int rx_index;
+	int t_num = 0;
+	int *ptr_fmt;
+	int *ptr32;
+	short *ptr16;
+	char *ptr8;
+	int l;
+	int rx_unit = dma->dma_rx_unit;
+
+	switch (rx_unit) {
+	case 1:
+		t_num = dma->rx_avail/4;
+		break;
+	case 2:
+		t_num = dma->rx_avail/2;
+		break;
+	case 4:
+		t_num = dma->rx_avail;
+		break;
+	}
+
+	if (t_num < len) {
+		dev_err(priv->dev, "%s[%d]: internal buffer empty\n",
+			__func__, priv->ch);
+		return;
+	}
+
+	if ((dma->rx_complete + ((len/rx_unit)*4)) <=
+	    dma->rx_tail) {
+		rx_index = (int)(dma->rx_complete - dma->rx_head) /
+				(I2S_AFULL_THRESH * 4);
+		sg = sg + rx_index;
+		t_num = len/(I2S_AFULL_THRESH * rx_unit);
+		dma_sync_sg_for_cpu(priv->dev, sg, t_num, DMA_FROM_DEVICE);
+
+		ptr_fmt = (int *)dma->rx_complete;
+		switch (rx_unit) {
+		case 1:
+			ptr8 = (char *)data;
+			for (l = 0; l < (len/rx_unit); l++)
+				*ptr8++ = (char)*ptr_fmt++;
+			break;
+		case 2:
+			ptr16 = (short *)data;
+			for (l = 0; l < (len/rx_unit); l++)
+				*ptr16++ = (short)*ptr_fmt++;
+			break;
+		case 4:
+			ptr32 = (int *)data;
+			for (l = 0; l < (len/rx_unit); l++)
+				*ptr32++ = *ptr_fmt++;
+			break;
+		}
+		dma_sync_sg_for_device(priv->dev, sg, t_num, DMA_FROM_DEVICE);
+		dma->rx_complete += (len/rx_unit)*4;
+	} else {
+		rem1 = (dma->rx_tail - dma->rx_complete)/4;
+		rem2 = (len/rx_unit) - rem1;
+		rx_index = (int)(dma->rx_complete-dma->rx_head) /
+				(I2S_AFULL_THRESH * 4);
+		sg = sg + rx_index;
+		t_num = rem1/I2S_AFULL_THRESH;
+		dma_sync_sg_for_cpu(priv->dev, sg, t_num, DMA_FROM_DEVICE);
+		ptr_fmt = (int *)dma->rx_complete;
+		switch (rx_unit) {
+		case 1:
+			ptr8 = (char *)data;
+			for (l = 0; l < rem1; l++)
+				*ptr8++ = (char)*ptr_fmt++;
+			break;
+		case 2:
+			ptr16 = (short *)data;
+			for (l = 0; l < rem1; l++)
+				*ptr16++ = (short)*ptr_fmt++;
+			break;
+		case 4:
+			ptr32 = (int *)data;
+			for (l = 0; l < rem1; l++)
+				*ptr32++ = *ptr_fmt++;
+			break;
+		}
+		dma_sync_sg_for_device(priv->dev, sg, t_num, DMA_FROM_DEVICE);
+		dma->rx_complete = dma->rx_head;
+		sg = dma->sg_rx_p;
+		t_num = rem2/I2S_AFULL_THRESH;
+		dma_sync_sg_for_cpu(priv->dev, sg, t_num, DMA_FROM_DEVICE);
+		ptr_fmt = (int *)dma->rx_complete;
+		switch (rx_unit) {
+		case 1:
+			ptr8 = (char *)(data+rem1*rx_unit);
+			for (l = 0; l < rem2; l++)
+				*ptr8++ = (char)*ptr_fmt++;
+			break;
+		case 2:
+			ptr16 = (short *)(data+rem1*rx_unit);
+			for (l = 0; l < rem2; l++)
+				*ptr16++ = (short)*ptr_fmt++;
+			break;
+		case 4:
+			ptr32 = (int *)(data+rem1*rx_unit);
+			for (l = 0; l < rem2; l++)
+				*ptr32++ = *ptr_fmt++;
+			break;
+		}
+		dma_sync_sg_for_device(priv->dev, sg, t_num, DMA_FROM_DEVICE);
+		dma->rx_complete += rem2*4;
+	}
+
+	if (dma->rx_complete >= dma->rx_tail)
+		dma->rx_complete = dma->rx_head;
+
+	dma->rx_avail -= (len/rx_unit)*4;
+}
+
+static void i2s_dma_rx_complete(void *arg)
+{
+	struct ioh_i2s_data *priv = arg;
+	struct ioh_i2s_dma *dma = &dmadata[priv->ch];
+	struct scatterlist *sg = dma->sg_rx_cur;
+	int num = dma->rx_num;
+	int i;
+
+	async_tx_ack(dma->desc_rx);
+
+	for (i = 0; i < num; i++, sg++) {
+		dma->rx_data_head += sg_dma_len(sg) * 4;
+		dma->rx_avail += sg_dma_len(sg) * 4;
+	}
+
+	if (dma->rx_data_head >= dma->rx_tail)
+		dma->rx_data_head = dma->rx_head;
+
+	ioh_i2s_clear_rx_sts_ir(priv);
+	ioh_i2s_enable_rx_full_ir(priv);
+}
+
+static void i2s_dma_tx_complete(void *arg)
+{
+	struct ioh_i2s_data *priv = arg;
+	struct ioh_i2s_dma *dma = &dmadata[priv->ch];
+	struct scatterlist *sg = dma->sg_tx_cur;
+	int num = dma->tx_num;
+	int i;
+
+	async_tx_ack(dma->desc_tx);
+
+	for (i = 0; i < num; i++, sg++) {
+		dma->tx_complete += sg_dma_len(sg) * 4;
+		dma->tx_avail -= sg_dma_len(sg) * 4;
+	}
+
+	if (dma->tx_complete >= dma->tx_tail)
+		dma->tx_complete = dma->tx_head;
+	ioh_i2s_clear_tx_sts_ir(priv);
+	ioh_i2s_enable_tx_empty_ir(priv);
+}
+
+/*****************************************************************************
+ *	Interrupt control
+ *****************************************************************************/
+static void i2s_tx_almost_empty_ir(struct ioh_i2s_data *priv)
+{
+	struct dma_async_tx_descriptor *desc;
+	int num;
+	int tx_comp_index;
+	struct ioh_i2s_dma *dma = &dmadata[priv->ch];
+	struct scatterlist *sg = dma->sg_tx_p;
+
+	dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p\n", __func__,
+		dma->tx_data_head, dma->tx_complete);
+
+	num = ((int)dma->tx_avail) / (I2S_AEMPTY_THRESH * 4);
+
+	tx_comp_index = (((int)(dma->tx_complete - dma->tx_head))) /\
+			(I2S_AEMPTY_THRESH * 4);
+
+	if ((tx_comp_index + num) >= dma->tx_nent)
+		num = dma->tx_nent - tx_comp_index;
+
+	if (num > I2S_DMA_SG_NUM)
+		num = I2S_DMA_SG_NUM;
+
+	if (!num) {
+		dev_err(priv->dev, "%s:Internal buffer empty\n",
+			__func__);
+		tasklet_schedule(&dma->tx_tasklet);
+		return; /* No data to transmit */
+	}
+
+	sg = sg + tx_comp_index; /* Point head of sg must be sent */
+	dma->sg_tx_cur = sg; /* Save tx condition */
+	dma->tx_num = num; /* Save tx condition */
+
+	desc = dma->chan_tx->device->device_prep_slave_sg(dma->chan_tx,
+					sg, num, DMA_TO_DEVICE,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+
+	if (!desc) {
+		dev_err(priv->dev, "%s:device_prep_slave_sg Failed\n",
+			__func__);
+		return;
+	}
+
+	/* To prevent this function from calling again before DMA completion */
+	ioh_i2s_disable_tx_empty_ir(priv);
+
+	dma->desc_tx = desc;
+	desc->callback = i2s_dma_tx_complete;
+	desc->callback_param = priv;
+
+	atomic_inc(&dma->pending_tx);
+	desc->tx_submit(desc);
+
+	tasklet_schedule(&dma->tx_tasklet);
+}
+
+void i2s_tx_empty_ir(struct ioh_i2s_data *priv, int ch)
+{
+	dev_warn(priv->dev, "%s:I2S under flow occurs(ch=%d)\n", __func__, ch);
+}
+
+void i2s_rx_full_ir(struct ioh_i2s_data *priv, int ch)
+{
+	dev_warn(priv->dev, "%s:I2S overrun occurs(ch=%d)\n", __func__, ch);
+}
+
+static inline void ioh_i2s_interrupt_sub_tx(struct ioh_i2s_data *priv)
+{
+	unsigned int status;
+	int channel = priv->ch;
+	int offset = channel * 0x800;
+
+	status = ioread32(priv->iobase + I2SISTTX_OFFSET + offset);
+	if (status & I2S_TX_EINT)
+		i2s_tx_empty_ir(priv, channel);
+	if (status & I2S_TX_AEINT)
+		i2s_tx_almost_empty_ir(priv);
+
+	/*Clear the interrupt status */
+	iowrite32(status, priv->iobase + I2SISTTX_OFFSET + offset);
+}
+
+static void i2s_rx_almost_full_ir(struct ioh_i2s_data *priv)
+{
+	struct dma_async_tx_descriptor *desc;
+	struct scatterlist *sg;
+	int rx_data_index;
+	int num;
+	struct ioh_i2s_dma *dma = &dmadata[priv->ch];
+
+	num = (int)(INTER_BUFF_SIZE - dma->rx_avail) / (I2S_AFULL_THRESH * 4);
+	if (num < 1) {
+		dev_err(priv->dev, "%s:Internal buffer full\n",
+			__func__);
+		tasklet_schedule(&dma->rx_tasklet);
+		return;
+	}
+
+	sg = dma->sg_rx_p;
+	rx_data_index = ((int)(dma->rx_data_head - dma->rx_head)) /\
+			(I2S_AFULL_THRESH * 4);
+
+	if ((rx_data_index + num) >= dma->rx_nent)
+		num = dma->rx_nent - rx_data_index;
+
+	if (num > I2S_DMA_SG_NUM)
+		num = I2S_DMA_SG_NUM;
+
+	sg += rx_data_index;
+
+	desc = dma->chan_rx->device->device_prep_slave_sg(dma->chan_rx,
+			sg, num, DMA_FROM_DEVICE,
+			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(priv->dev, "%s:device_prep_slave_sg Failed\n",
+			__func__);
+		return;
+	}
+
+	dma->sg_rx_cur = sg; /* Save rx condition */
+	ioh_i2s_disable_rx_full_ir(priv);
+	dma->rx_num = num;
+
+	dma->desc_rx = desc;
+	desc->callback = i2s_dma_rx_complete;
+	desc->callback_param = priv;
+	desc->tx_submit(desc);
+
+	tasklet_schedule(&dma->rx_tasklet);
+}
+
+static inline void ioh_i2s_interrupt_sub_rx(struct ioh_i2s_data *priv)
+{
+	unsigned int status;
+	int channel = priv->ch;
+	int offset = channel * 0x800;
+
+	status = ioread32(priv->iobase + I2SISTRX_OFFSET + offset);
+	if (status & I2S_RX_FINT)
+		i2s_rx_full_ir(priv, channel);
+	if (status & I2S_RX_AFINT)
+		i2s_rx_almost_full_ir(priv);
+
+	/*Clear the interrupt status */
+	iowrite32(status, priv->iobase + I2SISTRX_OFFSET + offset);
+}
+
+void ioh_i2s_event(struct ioh_i2s_data *priv, u32 idisp, int ch)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->tx_lock, flags);
+
+	if (idisp & BIT(ch + 16)) {
+		dev_dbg(priv->dev, "Rx%d interrupt occures\n", ch);
+		ioh_i2s_interrupt_sub_rx(priv);
+	}
+
+	if (idisp & BIT(ch)) {
+		dev_dbg(priv->dev, "Tx%d interrupt occures\n", ch);
+		ioh_i2s_interrupt_sub_tx(priv);
+	}
+
+	spin_unlock_irqrestore(&priv->tx_lock, flags);
+	return;
+}
+
+static irqreturn_t ioh_i2s_irq(int irq, void *data)
+{
+	struct pci_dev *pdev = (struct pci_dev *)data;
+	struct ioh_i2s_data_pci *drvdata = pci_get_drvdata(pdev);
+	int i;
+	u32 idisp;
+
+	idisp = ioread32(drvdata->devs.iobase + I2SIDISP_OFFSET);
+	for (i = 0; i < MAX_I2S_IF; i++)
+		ioh_i2s_event(&drvdata->devs, idisp, i);
+
+	return IRQ_HANDLED;
+}
+
+/*****************************************************************************
+ *	Sound Card
+ *****************************************************************************/
+static void i2s_read_period(struct snd_pcm_substream *substream)
+{
+	struct snd_ml7213i2s_dma *dma;
+	struct cbdata *cbd;
+	int period;
+	void *read_ptr;
+	int read_size;
+
+	dma = substream->runtime->private_data;
+	cbd = &dma->cbd;
+
+	if (!cbd->priv)
+		return;
+
+	period = substream->runtime->period_size;
+
+	read_ptr = substream->runtime->dma_area
+	  +(snd_pcm_lib_period_bytes(substream) * dma->irq_pos);
+	read_size = period * (substream->runtime->sample_bits/8) *
+		    (substream->runtime->channels);
+
+	ioh_i2s_read(cbd->priv,
+			 read_ptr,
+			 read_size);
+
+	dma->irq_pos = (dma->irq_pos + 1) % substream->runtime->periods;
+}
+
+static void read_done(void *callback_data, int status, int num, int avail)
+{
+	struct snd_pcm_substream *substream =
+		(struct snd_pcm_substream *)callback_data;
+	struct snd_ml7213i2s_dma *dma;
+	struct cbdata *cbd;
+	dma = substream->runtime->private_data;
+
+	if (num < snd_card_ml7213i2s_capture[substream->number].period_bytes_max)
+		return;
+
+	cbd = &dma->cbd;
+	switch (status) {
+	case IOH_EOK:
+		if (!cbd->stop) {
+			i2s_read_period(substream);
+			dma->buf_pos = (dma->buf_pos + 1) %
+				substream->runtime->periods;
+			snd_pcm_period_elapsed(substream);
+		}
+
+		cbd->cnt++;
+		break;
+	case IOH_EDONE:
+		pr_debug("Done stopping channel %d\n", cbd->stop);
+		cbd->stop = 2;
+		break;
+
+	case IOH_EOVERRUN:
+		if (ignore_overrun)
+			pr_debug("overrun ignore\n");
+		else {
+			pr_err("RX overrun\n");
+			cbd->stop = 2;
+		}
+		break;
+
+	case IOH_EFRAMESYNC:
+		pr_err("Frame sync error\n");
+		cbd->stop = 2;
+		break;
+	}
+	if (cbd->stop)
+		pr_debug("stopping... %d\n", cbd->stop);
+}
+
+static int i2s_config_rate_reg(struct ioh_i2s_config_reg *config,
+				    unsigned int rate,
+				    struct snd_ml7213i2s_pcm *dpcm)
+{
+	unsigned int i2s_mclk;
+	unsigned int ret;
+
+	i2s_mclk = i2s_config_table[dpcm->ch+STEREO_OFFSET].i2s_mclk;
+	if ((i2s_mclk/64) == rate)
+		ret = ioh_mclkfs_64fs << I2SCLKCNT_MCLKFS_OFFSET;
+	else if ((i2s_mclk/128) == rate)
+		ret = ioh_mclkfs_128fs << I2SCLKCNT_MCLKFS_OFFSET;
+	else if ((i2s_mclk/192) == rate)
+		ret = ioh_mclkfs_192fs << I2SCLKCNT_MCLKFS_OFFSET;
+	else if ((i2s_mclk/256) == rate)
+		ret = ioh_mclkfs_256fs << I2SCLKCNT_MCLKFS_OFFSET;
+	else if ((i2s_mclk/384) == rate)
+		ret = ioh_mclkfs_384fs << I2SCLKCNT_MCLKFS_OFFSET;
+	else if ((i2s_mclk/512) == rate)
+		ret = ioh_mclkfs_512fs << I2SCLKCNT_MCLKFS_OFFSET;
+	else if ((i2s_mclk/768) == rate)
+		ret = ioh_mclkfs_768fs << I2SCLKCNT_MCLKFS_OFFSET;
+	else if ((i2s_mclk/1024) == rate)
+		ret = ioh_mclkfs_1024fs << I2SCLKCNT_MCLKFS_OFFSET;
+	else
+		ret = 0;
+
+	return ret;
+}
+
+static int i2s_rx_configure_reg(struct ioh_i2s_config_reg *config,
+				    unsigned int rate,
+				    struct snd_ml7213i2s_pcm *dpcm)
+{
+	int ret = 0;
+
+	memset(config, 0, sizeof(*config));
+
+	/* Set ML7213 IOH register default value */
+	config->cmn.i2simask = 0x003f003f;
+
+	config->rx.i2saerx = 0x1F;
+	config->rx.i2smskrx = 0x1F;
+	config->rx.i2sistrx = 0xC;
+
+	/* Configuration */
+	if (dpcm->channels == 1) {
+		config->rx.i2scntrx =
+			i2s_config_table[dpcm->ch+MONAURAL_OFFSET].i2scntrx |
+			dpcm->format |
+			(ioh_tel_tel_fmt << I2SCNTRX_RX_TEL_OFFSET);
+		config->cmn.i2sclkcnt = dpcm->bclkfs |
+			i2s_config_table[dpcm->ch+MONAURAL_OFFSET].i2sclkcnt;
+	} else {
+		config->rx.i2scntrx =
+			i2s_config_table[dpcm->ch+STEREO_OFFSET].i2scntrx |
+			dpcm->format |
+			(ioh_tel_i2s_fmt << I2SCNTRX_RX_TEL_OFFSET);
+		config->cmn.i2sclkcnt = dpcm->bclkfs |
+			i2s_config_table[dpcm->ch+STEREO_OFFSET].i2sclkcnt;
+	}
+
+	config->cmn.i2sclkcnt |= i2s_config_rate_reg(config, rate, dpcm);
+	config->rx.i2safrx = I2S_AFULL_THRESH / 2; /* Almost full threshold */
+	if (((config->cmn.i2sclkcnt & 0x3000) == 0x3000) &&
+	    ((config->cmn.i2sclkcnt & 0x7) == 0x2)) {
+		pr_err("%s: Failed not support setting\n", __func__);
+		ret = -1;
+	}
+	return ret;
+}
+
+static int setup_i2s_read(struct snd_pcm_substream *substream,
+			  struct snd_ml7213i2s_pcm *dpcm)
+{
+	struct snd_ml7213i2s_dma *dma;
+	struct ioh_i2s_config_reg config;
+	unsigned int master;
+	int ret = 0;
+	unsigned int byte;
+	int ch = substream->number;
+
+	master = (i2s_config_table[ch].i2sclkcnt >> I2SCLKCNT_MSSEL_OFFSET) & 1;
+	dma = substream->runtime->private_data;
+
+	dma->cbd.priv = ioh_i2s_open(ch, IOH_CAPTURE, "radio-i2s-in");
+
+	if (!dma->cbd.priv) {
+		pr_err("%s: Cannot open the device\n", __func__);
+		return -1;
+	}
+
+	if (ignore_overrun)
+		ioh_i2s_ignore_rx_overrun(dma->cbd.priv);
+
+	ret = i2s_rx_configure_reg(&config, dpcm->rate, dpcm);
+	if (master)
+		dma->master_mode |= 1 << (ch + I2S_READ_MASTER_BIT);
+	else
+		dma->master_mode |= 0 << (ch + I2S_READ_MASTER_BIT);
+
+	switch (dpcm->format) {
+	case (ioh_dabit_8bit << I2SCNT_DABIT_OFFSET):
+		byte = 1;
+		break;
+	case (ioh_dabit_16bit << I2SCNT_DABIT_OFFSET):
+		byte = 2;
+		break;
+	case (ioh_dabit_24bit << I2SCNT_DABIT_OFFSET):
+		byte = 4;
+		break;
+	default:
+		pr_err("%s: format error\n", __func__);
+		return -1;
+		break;
+	}
+
+	ioh_i2s_configure_i2s_regs(dma->cbd.priv, 0, &config, IOH_CAPTURE,
+				   byte);
+	return ret;
+}
+
+static void i2s_write_period(struct snd_pcm_substream *substream)
+{
+	struct snd_ml7213i2s_dma *dma;
+	struct cbdata *cbd;
+	int period;
+	void *write_ptr;
+	int write_size;
+
+	dma = substream->runtime->private_data;
+	cbd = &dma->cbd;
+
+	if (!cbd->priv)
+		return;
+
+	period = substream->runtime->period_size;
+	write_ptr = substream->runtime->dma_area
+	  +(snd_pcm_lib_period_bytes(substream) * dma->irq_pos);
+	write_size = period * (substream->runtime->sample_bits/8) *
+		     (substream->runtime->channels);
+
+	ioh_i2s_write(cbd->priv, write_ptr, write_size);
+
+	dma->irq_pos = (dma->irq_pos + 1) % substream->runtime->periods;
+}
+
+static void write_done(void *callback_data, int status, int num, int avail)
+{
+	struct snd_pcm_substream *substream =
+		(struct snd_pcm_substream *)callback_data;
+	struct snd_ml7213i2s_dma *dma;
+	struct cbdata *cbd;
+	int ch = substream->number;
+
+	dma = substream->runtime->private_data;
+
+	if (num < snd_card_ml7213i2s_playback[ch].period_bytes_max)
+		return;
+	if (avail >= snd_card_ml7213i2s_playback[ch].period_bytes_max*2)
+		return;
+
+	if (!substream) {
+		pr_debug("%s:!substream NULL\n", __func__);
+		return;
+	}
+	if (!substream->runtime) {
+		pr_debug("%s:!substream->runtime NULL\n", __func__);
+		return;
+	}
+	if (!substream->runtime->private_data) {
+		pr_debug("%s:!substream->runtime->private_data NULL\n",
+			__func__);
+		return;
+	}
+	cbd = &dma->cbd;
+
+	switch (status) {
+	case IOH_EOK:
+		if (!cbd->stop) {
+			i2s_write_period(substream);
+			dma->buf_pos = (dma->buf_pos + 1) %
+				substream->runtime->periods;
+			snd_pcm_period_elapsed(dma->substream);
+		}
+		cbd->cnt++;
+		break;
+	case IOH_EDONE:
+		pr_debug("Done stopping channel %d\n", cbd->stop);
+		cbd->stop = 2;
+		break;
+	default:
+		pr_debug("%s:default(%d)\n", __func__, status);
+	break;
+	}
+}
+
+static int i2s_tx_configure_reg(struct ioh_i2s_config_reg *config,
+				    unsigned int rate,
+				    struct snd_ml7213i2s_pcm *dpcm)
+{
+	int ret = 0;
+
+	memset(config, 0, sizeof(*config));
+
+	/* Set ML7213 IOH register default value */
+	config->cmn.i2simask = 0x003f003f;
+
+	config->tx.i2saftx = 0x0;
+	config->tx.i2smsktx = 0x1F;
+	config->tx.i2sisttx = 0xC;
+
+	/* Configuration */
+	if (dpcm->channels == 1) {
+		config->tx.i2scnttx =
+			i2s_config_table[dpcm->ch+MONAURAL_OFFSET].i2scnttx |
+			dpcm->format |
+			(ioh_tel_tel_fmt << I2SCNTTX_TX_TEL_OFFSET);
+		config->cmn.i2sclkcnt = dpcm->bclkfs |
+			i2s_config_table[dpcm->ch+MONAURAL_OFFSET].i2sclkcnt;
+	} else {
+		config->tx.i2scnttx =
+			i2s_config_table[dpcm->ch+STEREO_OFFSET].i2scnttx |
+			dpcm->format |
+			(ioh_tel_i2s_fmt << I2SCNTTX_TX_TEL_OFFSET);
+		config->cmn.i2sclkcnt = dpcm->bclkfs |
+			i2s_config_table[dpcm->ch+STEREO_OFFSET].i2sclkcnt;
+	}
+
+	config->cmn.i2sclkcnt |= i2s_config_rate_reg(config, rate, dpcm);
+	config->tx.i2saetx = I2S_AEMPTY_THRESH / 2; /* Almost empty threshold */
+	if (((config->cmn.i2sclkcnt & 0x3000) == 0x3000) &&
+	    ((config->cmn.i2sclkcnt & 0x7) == 0x2)) {
+		pr_err("%s: Failed not support setting\n", __func__);
+		ret = -1;
+	}
+	return ret;
+}
+
+static int setup_i2s_write(struct snd_pcm_substream *substream,
+			   struct snd_ml7213i2s_pcm *dpcm)
+{
+	struct ioh_i2s_config_reg config;
+	unsigned int master;
+	int ret = 0;
+	unsigned int byte;
+	struct snd_ml7213i2s_dma *dma;
+	int ch = substream->number;
+
+	master = (i2s_config_table[ch].i2sclkcnt >> I2SCLKCNT_MSSEL_OFFSET) & 1;
+	dma = substream->runtime->private_data;
+
+	dma->cbd.priv = ioh_i2s_open(ch, IOH_PLAYBACK, "radio-i2s-out");
+
+	if (!dma->cbd.priv) {
+		pr_err("%s: Cannot open the device\n", __func__);
+		return -1;
+	}
+
+	if (ignore_overrun)
+		ioh_i2s_ignore_rx_overrun(dma->cbd.priv);
+
+	ret = i2s_tx_configure_reg(&config, dpcm->rate, dpcm);
+	if (master)
+		dma->master_mode |= 1 << (ch + I2S_WRITE_MASTER_BIT);
+	else
+		dma->master_mode |= 0 << (ch + I2S_WRITE_MASTER_BIT);
+
+	switch (dpcm->format) {
+	case (ioh_dabit_8bit << I2SCNT_DABIT_OFFSET):
+		byte = 1;
+		break;
+	case (ioh_dabit_16bit << I2SCNT_DABIT_OFFSET):
+		byte = 2;
+		break;
+	case (ioh_dabit_24bit << I2SCNT_DABIT_OFFSET):
+		byte = 4;
+		break;
+	default:
+		pr_err("%s: format error\n", __func__);
+		return -1;
+		break;
+	}
+
+	ioh_i2s_configure_i2s_regs(dma->cbd.priv, 0, &config, IOH_PLAYBACK,
+				   byte);
+	return ret;
+}
+
+static void __snd_card_ml7213i2s_runtime_free(struct snd_pcm_runtime *runtime)
+{
+	struct snd_ml7213i2s_dma *dma;
+	static int cnt;
+
+	dma = (struct snd_ml7213i2s_dma *)runtime->private_data;
+	/* FIXME: This is just a big ball of race right now...
+	 */
+	if (!dma->cbd.stop)
+		dma->cbd.stop = 1;
+	else {
+		while (dma->cbd.stop != 2) {
+			if (cnt++ > 100) {
+				pr_debug("oops, failed to close ml7213i2s..\n");
+				pr_debug("it's ok if i2s isn't running\n");
+				break;
+			}
+			msleep(20);
+		}
+	}
+}
+
+static void snd_card_ml7213i2s_runtime_capture_free
+					(struct snd_pcm_runtime *runtime)
+{
+	struct snd_ml7213i2s_dma *dma = runtime->private_data;
+
+	__snd_card_ml7213i2s_runtime_free(runtime);
+
+	ioh_i2s_release(dma->cbd.priv, IOH_CAPTURE);
+	kfree(runtime->private_data);
+}
+
+static struct snd_ml7213i2s_dma *
+new_pcm_stream(struct snd_pcm_substream *substream)
+{
+	struct snd_ml7213i2s_dma *dma;
+
+	dma = kzalloc(sizeof(*dma), GFP_KERNEL);
+	if (!dma)
+		return dma;
+	spin_lock_init(&dma->lock);
+	dma->substream = substream;
+	return dma;
+}
+
+static int snd_card_ml7213i2s_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ml7213i2s_dma *dma;
+	int err;
+
+	dma = new_pcm_stream(substream);
+	if (dma == NULL)
+		return -ENOMEM;
+
+	runtime->private_data = dma;
+	/* makes the infrastructure responsible for freeing dma */
+	runtime->private_free = snd_card_ml7213i2s_runtime_capture_free;
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		dma->rw = SND_CAPTURE_SUBSTREAM;
+		runtime->hw = snd_card_ml7213i2s_capture[substream->number];
+	} else {
+		dma->rw = SND_PLAYBACK_SUBSTREAM;
+		runtime->hw = snd_card_ml7213i2s_playback[substream->number];
+	}
+
+	err = add_capture_constraints(runtime);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int snd_card_ml7213i2s_close(struct snd_pcm_substream *substream)
+{
+	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_ml7213i2s_pcm dpcm;
+	struct snd_ml7213i2s_dma *dma = runtime->private_data;
+
+	memset(&dpcm, 0, sizeof(dpcm));
+	dpcm.channels = params_channels(hw_params);
+	dpcm.ch = substream->number;
+	dpcm.rate = params_rate(hw_params);
+	switch (params_format(hw_params)) {
+	case SNDRV_PCM_FORMAT_U8:
+		dpcm.format = ioh_dabit_8bit << I2SCNT_DABIT_OFFSET;
+		if (dpcm.channels == 1)
+			dpcm.bclkfs = ioh_bclkfs_32fs <<
+				       I2SCLKCNT_BCLKFS_OFFSET;
+		else
+			dpcm.bclkfs = ioh_bclkfs_32fs <<
+				       I2SCLKCNT_BCLKFS_OFFSET;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		dpcm.format = ioh_dabit_16bit << I2SCNT_DABIT_OFFSET;
+		if (dpcm.channels == 1)
+			dpcm.bclkfs = ioh_bclkfs_32fs <<
+				       I2SCLKCNT_BCLKFS_OFFSET;
+		else
+			dpcm.bclkfs = ioh_bclkfs_32fs <<
+				       I2SCLKCNT_BCLKFS_OFFSET;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		dpcm.format = ioh_dabit_24bit << I2SCNT_DABIT_OFFSET;
+		if (dpcm.channels == 1)
+			dpcm.bclkfs = ioh_bclkfs_64fs <<
+				       I2SCLKCNT_BCLKFS_OFFSET;
+		else
+			dpcm.bclkfs = ioh_bclkfs_64fs <<
+				       I2SCLKCNT_BCLKFS_OFFSET;
+		break;
+	default:
+		pr_err("%s: Failed not support format\n", __func__);
+		return -1;
+		break;
+	}
+
+	switch (dma->rw) {
+	case SND_CAPTURE_SUBSTREAM:
+		if (setup_i2s_read(substream, &dpcm))
+			return -1;
+		break;
+	case SND_PLAYBACK_SUBSTREAM:
+		if (setup_i2s_write(substream, &dpcm))
+			return -1;
+		break;
+	default:
+		return -1;
+	}
+
+	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 snd_ml7213i2s_dma *dma = substream->runtime->private_data;
+	unsigned int flag;
+	unsigned int ch = substream->number;
+
+	switch (dma->rw) {
+	case SND_CAPTURE_SUBSTREAM:
+		dma->master_mode &= ~(1<<(ch+I2S_READ_MASTER_BIT));
+		flag = (dma->master_mode >> (ch+I2S_WRITE_MASTER_BIT)) & 1;
+		ioh_i2s_stop_i2s_regs(dma->cbd.priv, 0, NULL,
+				      IOH_CAPTURE, 1);
+		kfree(dma->cbd.priv);
+		break;
+
+	case SND_PLAYBACK_SUBSTREAM:
+		dma->master_mode &= ~(1<<(ch+I2S_WRITE_MASTER_BIT));
+		flag = (dma->master_mode >> (ch+I2S_READ_MASTER_BIT)) & 1;
+		ioh_i2s_stop_i2s_regs(dma->cbd.priv, 0, NULL,
+				      IOH_PLAYBACK, 1);
+		kfree(dma->cbd.priv);
+		break;
+
+	default:
+		return -1;
+	}
+
+	return snd_pcm_lib_free_pages(substream);
+}
+
+void ioh_i2s_irq_stop(struct ioh_i2s_data *priv, enum ioh_direction dir)
+{
+	struct ioh_i2s_dma *dma = &dmadata[priv->ch];
+
+	if (!priv) {
+		dev_err(priv->dev, "%s: i2s is NULL\n", __func__);
+		return;
+	}
+
+	if (dir) {
+		ioh_i2s_disable_interrupts(priv, IOH_CAPTURE);
+		ioh_i2s_disable_rx_full_ir(priv);
+		ioh_i2s_clear_rx_sts_ir(priv);
+		dma->rxexe_flag = 0;
+
+	} else {
+		ioh_i2s_disable_interrupts(priv, IOH_PLAYBACK);
+		ioh_i2s_disable_tx_empty_ir(priv);
+		ioh_i2s_clear_tx_sts_ir(priv);
+		dma->txexe_flag = 0;
+	}
+}
+
+void ioh_i2s_write_start(struct ioh_i2s_data *priv, void *callback_data,
+			 void (*done) (void *callback_data, int status,
+				       int num, int avail))
+{
+	struct ioh_i2s_dma *dma = &dmadata[priv->ch];
+
+	dma->txexe_flag = 1;
+
+	dma->tx_data_head = dma->tx_head;
+	dma->tx_complete = dma->tx_head;
+	dma->tx_avail = 0;
+
+	dma->tx_callback_data = callback_data;
+	dma->tx_done = done;
+
+	ioh_i2s_clear_tx_sts_ir(priv);
+	ioh_i2s_tx_clear_dma_mask(priv);
+	ioh_i2s_enable_interrupts(priv, IOH_PLAYBACK);
+}
+
+void ioh_i2s_read_start(struct ioh_i2s_data *priv, void *callback_data,
+			void (*done) (void *callback_data, int status,
+				      int num, int avail))
+{
+	struct ioh_i2s_dma *dma = &dmadata[priv->ch];
+
+	dma->rxexe_flag = 1;
+
+	dma->rx_data_head = dma->rx_head;
+	dma->rx_complete = dma->rx_head;
+	dma->rx_avail = 0;
+
+	dma->rx_callback_data = callback_data;
+	dma->rx_done = done;
+
+	ioh_i2s_clear_rx_sts_ir(priv);
+	ioh_i2s_rx_clear_dma_mask(priv);
+	ioh_i2s_enable_interrupts(priv, IOH_CAPTURE);
+}
+
+static inline void
+snd_card_ml7213i2s_pcm_i2s_start(struct snd_pcm_substream *substream)
+{
+	struct snd_ml7213i2s_dma *dma;
+	struct cbdata *cbd;
+	dma = substream->runtime->private_data;
+	cbd = &dma->cbd;
+
+	if (!cbd->priv)
+		return;
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		ioh_i2s_read_start(cbd->priv, substream, read_done);
+	} else {
+		ioh_i2s_write_start(cbd->priv, substream, write_done);
+		i2s_write_period(substream);
+	}
+}
+
+static int snd_card_ml7213i2s_pcm_trigger
+				(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ml7213i2s_dma *dma = runtime->private_data;
+	int err = 0;
+
+	spin_lock(&dma->lock);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		dma->cbd.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 (!dma->cbd.stop)
+			dma->cbd.stop = 1;
+		else
+			pr_debug("already stopped %d\n", dma->cbd.stop);
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			ioh_i2s_irq_stop(dma->cbd.priv, IOH_CAPTURE);
+		else
+			ioh_i2s_irq_stop(dma->cbd.priv, IOH_PLAYBACK);
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+
+	spin_unlock(&dma->lock);
+	return 0;
+}
+
+static int snd_card_ml7213i2s_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ml7213i2s_dma *dma = runtime->private_data;
+
+	dma->irq_pos = 0;
+	dma->buf_pos = 0;
+
+	snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
+			bytes_to_samples(runtime, runtime->dma_bytes));
+
+	return 0;
+}
+
+static snd_pcm_uframes_t
+snd_card_ml7213i2s_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ml7213i2s_dma *dma = runtime->private_data;
+
+	return substream->runtime->period_size*dma->buf_pos;
+}
+
+static struct snd_pcm_ops snd_card_ml7213i2s_capture_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_pcm_probe(struct snd_soc_platform *platform)
+{
+	return 0;
+}
+
+static int ml7213ioh_pcm_remove(struct snd_soc_platform *platform)
+{
+	return 0;
+}
+
+static struct snd_soc_platform_driver ml7213ioh_soc_platform = {
+	.probe		= ml7213ioh_pcm_probe,
+	.remove		= ml7213ioh_pcm_remove,
+	.ops		= &snd_card_ml7213i2s_capture_ops,
+};
+
+static int __devinit snd_card_ml7213i2s_pcm(struct snd_ml7213i2s *ml7213i2s,
+					     int device)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	/* The number of I2S interface is (Rx + Tx) x 6CH */
+	err = snd_pcm_new(ml7213i2s->card, "ml7213i2s PCM", device,
+			       MAX_I2S_TX_CH, MAX_I2S_RX_CH, &pcm);
+	if (err < 0)
+		return err;
+	ml7213i2s->pcm = pcm;
+	pcm->private_data = ml7213i2s;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "ml7213i2s PCM");
+
+	snd_pcm_lib_preallocate_pages_for_all(
+		pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+		snd_dma_continuous_data(GFP_KERNEL),
+		0, 64*1024);
+	return 0;
+}
+
+static struct snd_device_ops ops = {NULL};
+
+/*****************************************************************************
+ *	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;
+	int i;
+	int err;
+	struct ioh_i2s_data_pci *drvdata;
+	void __iomem *tbl;
+	unsigned int mapbase;
+	struct snd_card *card;
+	struct snd_ml7213i2s *ml7213i2s;
+	int dev = 0;
+
+	drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
+
+	pci_set_drvdata(pdev, drvdata);
+
+	rv = pci_enable_device(pdev);
+	if (rv)
+		goto out_free;
+
+	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_start;
+	}
+
+	drvdata->devs.dev = &pdev->dev;
+	drvdata->devs.iobase = tbl;
+	drvdata->devs.mapbase = mapbase;
+	spin_lock_init(&drvdata->devs.tx_lock);
+
+	for (i = 0; i < MAX_I2S_IF; i++)
+		dmadata[i].dma_config = &ioh_dma_config[i];
+
+	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;
+	}
+
+	err = snd_card_create(index, "ml7213i2s", THIS_MODULE,
+			      sizeof(struct snd_ml7213i2s), &card);
+
+	if (err < 0)
+		goto out_snd_card_create;
+
+	ml7213i2s = card->private_data;
+	ml7213i2s->card = card;
+	ml7213i2s->pci_dat = drvdata;
+	err = snd_card_ml7213i2s_pcm(ml7213i2s, 0);
+	if (err < 0)
+		goto snd_card_ml7213i2s_pc;
+
+	strcpy(card->driver, "ml7213i2s");
+	strcpy(card->shortname, "ml7213i2s");
+	sprintf(card->longname, "ml7213i2s %i", dev + 1);
+
+	snd_card_set_dev(card, &pdev->dev);
+	snd_device_new(card, SNDRV_DEV_LOWLEVEL, ml7213i2s, &ops);
+	err = snd_card_register(card);
+	if (err)
+		goto out_snd_card_register;
+
+	drvdata->card = card;
+
+	snd_soc_register_platform(&pdev->dev, &ml7213ioh_soc_platform);
+
+	return 0;
+
+out_snd_card_register:
+snd_card_ml7213i2s_pc:
+	snd_card_free(card);
+out_snd_card_create:
+	free_irq(pdev->irq, pdev);
+out_irq:
+out_pci_resource_start:
+	pci_iounmap(pdev, tbl);
+out_ipmap:
+	pci_disable_device(pdev);
+out_free:
+	kfree(drvdata);
+
+	return rv;
+}
+
+static void ioh_i2s_pci_remove(struct pci_dev *pdev)
+{
+	struct ioh_i2s_data_pci *drvdata = pci_get_drvdata(pdev);
+	int i;
+
+	for (i = 0; i < MAX_I2S_IF; i++)
+		ioh_i2s_reset(&drvdata->devs, i);
+
+	snd_soc_unregister_platform(&pdev->dev);
+	kfree(drvdata);
+	pci_disable_device(pdev);
+	pci_iounmap(pdev, drvdata->devs.iobase);
+	free_irq(pdev->irq, pdev);
+	snd_card_free(drvdata->card);
+	pci_set_drvdata(pdev, NULL);
+}
+
+static void ioh_i2s_save_reg_conf(struct pci_dev *pdev)
+{
+	int i;
+	struct ioh_i2s_data_pci *drvdata = pci_get_drvdata(pdev);
+	void *iobase;
+	struct ioh_i2s_pm_ch_reg *save;
+	int offset;
+
+	for (i = 0, offset = 0; i < MAX_I2S_IF; i++, offset = i * 0x800) {
+		iobase = drvdata->devs.iobase;
+		save = &drvdata->devs.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);
+	}
+
+	for (i = 0; i < MAX_I2S_IF; i++) {
+		drvdata->cmn_reg_save.i2sclkcnt[i] =
+		ioread32(drvdata->devs.iobase + I2SCLKCNT0_OFFSET + 0x10*i);
+	}
+	drvdata->cmn_reg_save.i2simask =
+			   ioread32(drvdata->devs.iobase + I2SIMASK_OFFSET);
+}
+
+static int ioh_i2s_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	int ret;
+
+	ioh_i2s_save_reg_conf(pdev);
+	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 void ioh_i2s_restore_reg_conf(struct pci_dev *pdev)
+{
+	int i;
+	struct ioh_i2s_data_pci *drvdata = pci_get_drvdata(pdev);
+	void *iobase;
+	struct ioh_i2s_pm_ch_reg *save;
+	int offset;
+
+	for (i = 0, offset = 0; i < MAX_I2S_IF; i++, offset = i * 0x800) {
+		iobase = drvdata->devs.iobase;
+		save = &drvdata->devs.ch_reg_save[i];
+
+		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_IF; i++) {
+		iowrite32(drvdata->cmn_reg_save.i2sclkcnt[i],
+			 drvdata->devs.iobase + I2SCLKCNT0_OFFSET + 0x10*i);
+	}
+
+	iowrite32(drvdata->cmn_reg_save.i2simask,
+		  drvdata->devs.iobase + I2SIMASK_OFFSET);
+}
+
+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);
+	ioh_i2s_restore_reg_conf(pdev);
+
+	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_i2s_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_i2s_init);
+module_exit(ioh_i2s_cleanup);
+
+MODULE_AUTHOR("Tomoya MORINAGA <tomoya-linux@dsn.lapis-semi.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..1dbc5cd
--- /dev/null
+++ b/sound/soc/lapis/ml7213ioh-plat.h
@@ -0,0 +1,213 @@
+/*
+ * 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 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 TX_OFFSET_INCREMENT	0x800
+
+#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 MAX_I2S_IF	MAX_I2S_CH
+
+/* DMA channel name configuration */
+static struct ioh_dma_config {
+	char rx_chan[8];
+	char tx_chan[8];
+} ioh_dma_config[] = {
+	{ /* I2S0 */
+		.tx_chan = "i2s_tx0",
+		.rx_chan = "i2s_rx0",
+	},
+	{ /* I2S1 */
+		.tx_chan = "i2s_tx1",
+		.rx_chan = "i2s_rx1",
+	},
+	{ /* I2S2 */
+		.tx_chan = "i2s_tx2",
+		.rx_chan = "i2s_rx2",
+	},
+	{ /* I2S3 */
+		.tx_chan = "i2s_tx3",
+		.rx_chan = "i2s_rx3",
+	},
+	{ /* I2S4 */
+		.tx_chan = "i2s_tx4",
+		.rx_chan = "i2s_rx4",
+	},
+	{ /* I2S5 */
+		.tx_chan = "i2s_tx5",
+		.rx_chan = "i2s_rx5",
+	},
+};
+
+struct ioh_i2s_data {
+	struct device *dev;
+	void *iobase;
+	int ch;
+	int ignore_rx_overrun;
+	spinlock_t tx_lock;
+	unsigned int mapbase;
+	struct ioh_i2s_pm_ch_reg ch_reg_save[6];
+};
+
+struct ioh_i2s_dma {
+	atomic_t rx_busy;
+	atomic_t tx_busy;
+
+	/* Transmit side DMA */
+	atomic_t pending_tx;
+
+	struct ioh_dma_config *dma_config;
+
+	struct scatterlist	*sg_tx_p;
+	struct scatterlist	*sg_rx_p;
+
+	struct scatterlist	*sg_tx_cur; /* current head of tx sg */
+	struct scatterlist	*sg_rx_cur; /* current head of tx sg */
+
+	int tx_num;	/* The number of sent sg */
+	int rx_num;	/* The number of sent sg */
+
+	void *rxbuf_virt;
+	void *txbuf_virt;
+	unsigned char *tx_tail;
+	unsigned char *tx_head;
+	unsigned char *tx_data_head;
+	unsigned char *tx_complete;
+	unsigned int  tx_avail;
+	unsigned char *rx_tail;
+	unsigned char *rx_head;
+	unsigned char *rx_data_head;
+	unsigned char *rx_complete;
+	unsigned int rx_avail;
+
+	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;
+
+	void *rx_callback_data;
+	void (*rx_done) (void *callback_data, int status, int num, int avail);
+	void *tx_callback_data;
+	void (*tx_done) (void *callback_data, int status, int num, int avail);
+
+	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;
+
+	int txexe_flag;
+	int rxexe_flag;
+
+	struct tasklet_struct	tx_tasklet;
+	struct tasklet_struct	rx_tasklet;
+};
+
+struct ioh_i2s_data_pci {
+	struct ioh_i2s_data devs;
+	struct ioh_i2s_pm_common_reg cmn_reg_save;
+	struct snd_card *card;
+};
+
+#endif
-- 
1.7.4.4


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

* Re: [PATCH 1/3] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2011-11-21  4:08 [PATCH 1/3] sound/soc/codecs: add LAPIS Semiconductor ML26124 Tomoya MORINAGA
  2011-11-21  4:08 ` [PATCH 2/3] sound/soc/lapis: add machine driver Tomoya MORINAGA
  2011-11-21  4:08 ` [PATCH 3/3] sound/soc/lapis: add platform driver Tomoya MORINAGA
@ 2011-11-21 11:26 ` Mark Brown
  2011-11-22 10:47   ` Tomoya MORINAGA
  2 siblings, 1 reply; 27+ messages in thread
From: Mark Brown @ 2011-11-21 11:26 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Mon, Nov 21, 2011 at 01:08:50PM +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

So, in general the big overall issue with this code is that it's really
not following the general coding styles and standards for a modern ASoC
driver.  I've pointed out some issues below but there's rather a lot of
them.  In general you should always try to ensure that the code you're
submitting looks like other code for the relevant subsystem.

The big thing that's sticking out at me is the register write sequence
you've got in the middle of the driver - it looks awfully like it's hard
coding a particular use case.

> +struct ml26124_priv {
> +	enum snd_soc_control_type control_type;
> +	spinlock_t lock;
> +	struct snd_pcm_substream *substream;
> +	unsigned int rate;
> +	unsigned int ch;
> +	struct mutex i2c_mutex;

What are these locks for?  Especially the spinlock looks highly
suspicous.

> +static const char *ml26124_lrmcon[] = {"Use L", "Use R", "Use (L+R)", "Use (L+R)/2"};

The "Use" isn't terribly idomatic, remove it.

> +static const struct soc_enum ml26124_enum[] = {
> +/* #define SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts) */
> +	SOC_ENUM_SINGLE(ML26124_MIXER_VOL_CTL, 0, 4, ml26124_lrmcon),
> +	SOC_ENUM_SINGLE(ML26124_MIXER_VOL_CTL, 4, 15, ml26124_dvfconcon),

Don't use a big table of enums, indexing into it is illegible and error
prone.

> +static const struct snd_kcontrol_new ml26124_snd_controls[] = {
> +	SOC_SINGLE_TLV("Record Digital Volume", ML26124_RECORD_DIG_VOL, 0, 0xff, 1, rec_play_digi_vol),

Capture, not Record.

> +	SOC_SINGLE_TLV("EQ Band1 Gain Setting",  ML26124_EQ_GAIN_BRAND1, 0, 0xff, 1, eq_band_gain),

Volume not Gain Setting.  As a rule anything with TLV information should
be a Volume.

> +static const struct snd_kcontrol_new ml26124_dsp_controls[] = {
> +	SOC_SINGLE("Play Limitter ON/OFF", ML26124_FILTER_EN, 0, 1, 0),

Switch.

> +	SOC_SINGLE("Ditital Volume MUTE", ML26124_FILTER_EN, 4, 1, 0),

Switch.

> +	SOC_SINGLE("Set ALC position", ML26124_FILTER_EN, 5, 1, 0),

What does this actually do?  From the name it *really* doesn't look like
a mixer input.

> +	SND_SOC_DAPM_SPK("Speaker", NULL),
> +	SND_SOC_DAPM_LINE("LINEOUT", NULL),

These should be _OUTPUT() pins - SPK and LINE widgets should only be
used by machine drivers.

> +#define	CODEC_DEV_ADDR		(0x1A)

> +static struct i2c_board_info ioh_hwmon_info[] = {
> +	{I2C_BOARD_INFO("ioh_i2c-0", CODEC_DEV_ADDR + 1)},
> +	{I2C_BOARD_INFO("ioh_i2c-1", CODEC_DEV_ADDR + 2)},
> +	{I2C_BOARD_INFO("ioh_i2c-2", CODEC_DEV_ADDR + 3)},
> +	{I2C_BOARD_INFO("ioh_i2c-3", CODEC_DEV_ADDR + 4)},
> +	{I2C_BOARD_INFO("ioh_i2c-4", CODEC_DEV_ADDR + 5)},
> +	{I2C_BOARD_INFO("ioh_i2c-5", CODEC_DEV_ADDR + 0)},
> +};

Absolutely no.  Your driver should be a standard I2C driver, device
registration should be done by the board not the chip driver.

> +static const struct snd_soc_dapm_route intercon[] = {
> +};

I can't see how any of the DAPM widgets you specify will ever work with
no interconnections.

> +
> +static int snd_card_codec_reg_read(struct ml26124_priv *priv,
> +				   unsigned char reg, unsigned char *val)
> +{
> +	unsigned char data;
> +	struct i2c_client *i2c;

Use the standard register access code, don't open code things in your
driver unless there's a good reason to.  Current best practice for most
I2C or SPI devices is to use regmap, see any recently added driver for
examples.

> +static int snd_card_codec_set(int number, struct ml26124_priv *priv)
> +{

Namespacing - this isn't a generic ALSA or ASoC function.

> +	unsigned char data;
> +	unsigned int rate = priv->rate;
> +	unsigned int channels = priv->ch;
> +
> +	snd_card_codec_reg_read(priv, 0x30, &data);	 /* Read MICVIAS Voltage */
> +
> +	snd_card_codec_reg_write(priv, 0x10, 0x01);	/* soft reset assert */
> +	snd_card_codec_reg_write(priv, 0x10, 0x00);	/* soft reset negate */
> +
> +	snd_card_codec_reg_write(priv, 0x0c, 0x00);	/* Stop clock  */ 

Use snd_soc_update_bits() and the other standard register access
functions.

> +	switch (rate) {
> +	case 16000:
> +		snd_card_codec_reg_write(priv, 0x00, 0x03);
> +		snd_card_codec_reg_write(priv, 0x02, 0x0c);
> +		snd_card_codec_reg_write(priv, 0x04, 0x00);
> +		snd_card_codec_reg_write(priv, 0x06, 0x20);
> +		snd_card_codec_reg_write(priv, 0x08, 0x00);
> +		snd_card_codec_reg_write(priv, 0x0a, 0x04);
> +		break;

This sort of magic number based code without even registers being named
isn't really of the standard required for maintainability or review.

> +	default:
> +		pr_err("%s:this rate is no support for ml26124\n", __func__);
> +		break;
> +	}

If the driver has detected an error it should return an error; printing
the rate that was requested as part of the error message would also be
useful.

> +	snd_card_codec_reg_write(priv, 0x64, 0x00); /* master/slave set slave */

This looks like it should be in set_dai_fmt().

> +
> +	snd_card_codec_reg_write(priv, 0x20, 0x02); /* VMID on. normal mode */
> +	msleep(50);

This and much of the code looks like it should be in set_bias_level().

> +	snd_card_codec_reg_write(priv, 0x26, 0x1f); /* Speaker Amplified Poer Management */
> +	snd_card_codec_reg_write(priv, 0x28, 0x02); /* LOUT Control Regsister */
> +
> +	snd_card_codec_reg_write(priv, 0x54, 0x02); /* Speaker Amplifier output Control */
> +	snd_card_codec_reg_write(priv, 0x5a, 0x00); /* Mic Interface Control Register */

This looks awfully like it's supposed to be DAPM.

> +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);
> +
> +	if (snd_card_codec_set(substream->number, priv))
> +		return -1;

Return real error codes if you're going to detect errors.

> +
> +	return snd_pcm_lib_malloc_pages(substream,
> +					params_buffer_bytes(hw_params));

Why is a CODEC driver allocing memory to buffer audio?

> +static int ml26124_pcm_prepare(struct snd_pcm_substream *substream,
> +			      struct snd_soc_dai *dai)
> +{
> +	return 0;
> +}

Remove empty functions.

> +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);
> +
> +	if (mute)
> +		snd_card_codec_reg_write(priv, ML26124_DVOL_CTL,
> +					 DVOL_CTL_DVMUTE_ON);
> +	else
> +		snd_card_codec_reg_write(priv, ML26124_DVOL_CTL,
> +					 DVOL_CTL_DVMUTE_OFF);

I suspect you mean to use snd_soc_update_bits() here.

> +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:
> +	case SND_SOC_BIAS_PREPARE:
> +	case SND_SOC_BIAS_STANDBY:
> +		snd_card_codec_reg_write(priv, ML26124_REF_PM, REF_PM_VMID_ON);
> +		msleep(50);
> +		snd_card_codec_reg_write(priv, ML26124_REF_PM,
> +					 REF_PM_VMID_ON | REF_PM_MICBEN);

This looks much more like what I'd expect in terms of coding style -
meaingful defines and use of set_bias_level().  Also note that during
audio startup you'll transition STANDBY->PREPARE->ON and the reverse on
shutdown, I really don't think you want to be doing this for anything
except STANDBY.

I also can't see how this will play nicely with the above register write
sequence - you probably want to just remove the register write sequence.

> +static int ml26124_probe(struct snd_soc_codec *codec)
> +{
> +	snd_soc_add_controls(codec, ml26124_snd_controls,
> +			     ARRAY_SIZE(ml26124_snd_controls));
> +
> +	snd_soc_dapm_new_controls(codec, ml26124_dapm_widgets,
> +				  ARRAY_SIZE(ml26124_dapm_widgets));

Initialise these from your codec driver structure.

> +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
> +static __devinit int ml26124_i2c_probe(struct i2c_client *i2c,
> +				      const struct i2c_device_id *id)

Since there's no visible SPI support just unconditionally use I2C.

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

Remove the -codec, it's redundant.

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

* Re: [PATCH 2/3] sound/soc/lapis: add machine driver
  2011-11-21  4:08 ` [PATCH 2/3] sound/soc/lapis: add machine driver Tomoya MORINAGA
@ 2011-11-21 11:34   ` Mark Brown
  2011-12-02  5:33     ` Tomoya MORINAGA
  0 siblings, 1 reply; 27+ messages in thread
From: Mark Brown @ 2011-11-21 11:34 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Mon, Nov 21, 2011 at 01:08:51PM +0900, Tomoya MORINAGA wrote:
> Add machine driver for LAPIS Semiconductor ML7213 IOH.

This needs to come after you add the base platform support in the series
- clearly this won't work without the CPU side drivers it depends on.

As with the CODEC driver you really should be looking at the standards
for modern drivers and following them.

> @@ -60,6 +60,7 @@ source "sound/soc/s6000/Kconfig"
>  source "sound/soc/sh/Kconfig"
>  source "sound/soc/tegra/Kconfig"
>  source "sound/soc/txx9/Kconfig"
> +source "sound/soc/lapis/Kconfig"

Keep the Makefile and Kconfig entries sorted.

> +static struct snd_soc_dai_link ioh_i2s_dai = {
> +	.name = "I2S",
> +	.stream_name = "I2S HiFi",
> +	.cpu_dai_name = "ml7213ioh-i2s",
> +	.codec_dai_name = "ml7213ioh-hifi",

I'm very surprised this works...  If nothing else I'd expec the CODEC
DAI to have a different name to the CPU.

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

You should register the card as a standard platform device for new
drivers and use snd_soc_register_card().

> +static struct platform_driver ioh_i2s_driver = {
> +	.remove = ioh_i2s_remove,
> +	.driver = {
> +		.name = "ml7213ioh-machine",
> +		.owner = THIS_MODULE,
> +	},
> +};

You're already doing the platform device bit so it should be be a case
of changing the body of probe() and remove().

> +static int __init ioh_i2s_init(void)
> +{
> +	return platform_driver_probe(&ioh_i2s_driver,
> +				     ioh_i2s_probe);
> +}

Just make this a standard platform_driver_register().

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

* Re: [PATCH 3/3] sound/soc/lapis: add platform driver
  2011-11-21  4:08 ` [PATCH 3/3] sound/soc/lapis: add platform driver Tomoya MORINAGA
@ 2011-11-21 11:45   ` Mark Brown
  2011-12-12  8:28     ` Tomoya MORINAGA
  0 siblings, 1 reply; 27+ messages in thread
From: Mark Brown @ 2011-11-21 11:45 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Mon, Nov 21, 2011 at 01:08:52PM +0900, Tomoya MORINAGA wrote:

> +/* =================== I2S CH0 config =================== */
> +#define	I2S_CH0_MCLK		(12288000) /* Master Clock Frequency[Hz] */

This looks like it should be board specific?

> +  #if IOH_I2S_USE_PARAM
> +	#define	I2S_CH0_FS_16000	16000
> +	#define	I2S_CH0_FS_32000	32000
> +	#define	I2S_CH0_FS_48000	48000
> +  #else
> +	#define	I2S_CH0_FS_8000		8000
> +	#define	I2S_CH0_FS_11025	11025
> +	#define	I2S_CH0_FS_22050	22050
> +	#define	I2S_CH0_FS_44100	44100
> +  #endif

What are these ifdefs for?  This should be configured at runtime.

> +/* select master or slave. The value is ioh_mssel_t */
> +#define	I2S_CH0_MSSEL			(ioh_mssel_master)
> +/* select MCLK or MLBCLK into Master Clock. The value is enum ioh_masterclk_t */
> +#define	I2S_CH0_MASTERCLKSEL		(ioh_masterclksel_mclk)

This and most of the following defines also look like compile time
configuration for things that should be runtime configured.  As with
the previous patches in the series the big picture issue here is that
you need to update your code to reflect the practices of modern
mainline Linux code - here the major issue is that you appear to be
doing all your configuration with #defines rather than by using runtime
configuration from the machine driver as is normal for ASoC code.

I got through a bunch more defines but stopped reviewing at some point
as there was so much of this issue.

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

* Re: [PATCH 1/3] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2011-11-21 11:26 ` [PATCH 1/3] sound/soc/codecs: add LAPIS Semiconductor ML26124 Mark Brown
@ 2011-11-22 10:47   ` Tomoya MORINAGA
  2011-11-22 11:19     ` Mark Brown
  0 siblings, 1 reply; 27+ messages in thread
From: Tomoya MORINAGA @ 2011-11-22 10:47 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

Hi Mark,

Thank you for your comments.
I have some questions.

2011/11/21 Mark Brown <broonie@opensource.wolfsonmicro.com> wrote:
>> +static const struct snd_kcontrol_new ml26124_snd_controls[] = {
>> +     SOC_SINGLE_TLV("Record Digital Volume", ML26124_RECORD_DIG_VOL, 0, 0xff, 1, rec_play_digi_vol),
>
> Capture, not Record.

OK,
BTW, What's TLV ? Let me know the full spell of this "TLV".

>
>> +static const struct snd_kcontrol_new ml26124_dsp_controls[] = {
>> +     SOC_SINGLE("Play Limitter ON/OFF", ML26124_FILTER_EN, 0, 1, 0),
>
Do you mean SOC_SINGLE("Play Limitter Switch", ML26124_FILTER_EN, 0, 1, 0) ?


>> +     SOC_SINGLE("Set ALC position", ML26124_FILTER_EN, 5, 1, 0),
>
> What does this actually do?  From the name it *really* doesn't look like
> a mixer input.
>
The above means where connects the ALC.
So, this doesn't relate to mixer input.


>> +static const struct snd_soc_dapm_route intercon[] = {
>> +};
>
> I can't see how any of the DAPM widgets you specify will ever work with
> no interconnections.
I see.
However I couldn't understand the meaning of the widgets. So I didn't
write anything.
For example, wm8731 writes like below.
Could you explain this ?

static const struct snd_soc_dapm_route wm8731_intercon[] = {
	{"DAC", NULL, "OSC", wm8731_check_osc},
	{"ADC", NULL, "OSC", wm8731_check_osc},
	{"DAC", NULL, "ACTIVE"},
	{"ADC", NULL, "ACTIVE"},

	/* output mixer */
	{"Output Mixer", "Line Bypass Switch", "Line Input"},
	{"Output Mixer", "HiFi Playback Switch", "DAC"},
	{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},

	/* outputs */
	{"RHPOUT", NULL, "Output Mixer"},
	{"ROUT", NULL, "Output Mixer"},
	{"LHPOUT", NULL, "Output Mixer"},
	{"LOUT", NULL, "Output Mixer"},

	/* input mux */
	{"Input Mux", "Line In", "Line Input"},
	{"Input Mux", "Mic", "Mic Bias"},
	{"ADC", NULL, "Input Mux"},

	/* inputs */
	{"Line Input", NULL, "LLINEIN"},
	{"Line Input", NULL, "RLINEIN"},
	{"Mic Bias", NULL, "MICIN"},
};



>
>> +
>> +static int snd_card_codec_reg_read(struct ml26124_priv *priv,
>> +                                unsigned char reg, unsigned char *val)
>> +{
>> +     unsigned char data;
>> +     struct i2c_client *i2c;
>
> Use the standard register access code, don't open code things in your
> driver unless there's a good reason to.  Current best practice for most
> I2C or SPI devices is to use regmap, see any recently added driver for
> examples.
What's "regmap" mean ? or do you mean drivers/mfd/* ?
Could you tell me this ?

>> +     unsigned char data;
>> +     unsigned int rate = priv->rate;
>> +     unsigned int channels = priv->ch;
>> +
>> +     snd_card_codec_reg_read(priv, 0x30, &data);      /* Read MICVIAS Voltage */
>> +
>> +     snd_card_codec_reg_write(priv, 0x10, 0x01);     /* soft reset assert */
>> +     snd_card_codec_reg_write(priv, 0x10, 0x00);     /* soft reset negate */
>> +
>> +     snd_card_codec_reg_write(priv, 0x0c, 0x00);     /* Stop clock  */
>
> Use snd_soc_update_bits() and the other standard register access
> functions.
Using snd_soc_update_bits(), need to register ".read" method.
Is the same as the above  snd_card_codec_reg_read ?

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

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

* Re: [PATCH 1/3] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2011-11-22 10:47   ` Tomoya MORINAGA
@ 2011-11-22 11:19     ` Mark Brown
  2011-11-25  2:03       ` Tomoya MORINAGA
  0 siblings, 1 reply; 27+ messages in thread
From: Mark Brown @ 2011-11-22 11:19 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Tue, Nov 22, 2011 at 07:47:10PM +0900, Tomoya MORINAGA wrote:
> 2011/11/21 Mark Brown <broonie@opensource.wolfsonmicro.com> wrote:

> BTW, What's TLV ? Let me know the full spell of this "TLV".

Tag/Length/Value.

> >> +static const struct snd_kcontrol_new ml26124_dsp_controls[] = {
> >> +     SOC_SINGLE("Play Limitter ON/OFF", ML26124_FILTER_EN, 0, 1, 0),

> Do you mean SOC_SINGLE("Play Limitter Switch", ML26124_FILTER_EN, 0, 1, 0) ?

Yes.

> >> +     SOC_SINGLE("Set ALC position", ML26124_FILTER_EN, 5, 1, 0),

> > What does this actually do?  From the name it *really* doesn't look like
> > a mixer input.

> The above means where connects the ALC.
> So, this doesn't relate to mixer input.

If this control is not a mixer input it should not be a mixer input in
the driver.  Though with your explanation I still don't understand what
it does so presumably the naming also needs to be improved.

> However I couldn't understand the meaning of the widgets. So I didn't
> write anything.

See Documentation/sound/alsa/soc/dapm.txt.

> >> +static int snd_card_codec_reg_read(struct ml26124_priv *priv,
> >> +                                unsigned char reg, unsigned char *val)
> >> +{
> >> +     unsigned char data;
> >> +     struct i2c_client *i2c;

> > Use the standard register access code, don't open code things in your
> > driver unless there's a good reason to.  Current best practice for most
> > I2C or SPI devices is to use regmap, see any recently added driver for
> > examples.

> What's "regmap" mean ? or do you mean drivers/mfd/* ?
> Could you tell me this ?

drivers/base/regmap and also grep in sound/soc/codecs/*.c

> > Use snd_soc_update_bits() and the other standard register access
> > functions.

> Using snd_soc_update_bits(), need to register ".read" method.

Or use a cache.

> Is the same as the above  snd_card_codec_reg_read ?

I don't understand this question.

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

* Re: [PATCH 1/3] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2011-11-22 11:19     ` Mark Brown
@ 2011-11-25  2:03       ` Tomoya MORINAGA
  2011-11-25 11:02         ` Mark Brown
  0 siblings, 1 reply; 27+ messages in thread
From: Tomoya MORINAGA @ 2011-11-25  2:03 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

Hi Mark,

>> > Use the standard register access code, don't open code things in your
>> > driver unless there's a good reason to.  Current best practice for most
>> > I2C or SPI devices is to use regmap, see any recently added driver for
>> > examples.
>
>> What's "regmap" mean ? or do you mean drivers/mfd/* ?
>> Could you tell me this ?
>
> drivers/base/regmap and also grep in sound/soc/codecs/*.c
Though grepping "regmap" at sound/soc/codecs/*.c, I couldn't find any file.
Is this true ?

Thanks,
tomoya

2011/11/22 Mark Brown <broonie@opensource.wolfsonmicro.com>:
> On Tue, Nov 22, 2011 at 07:47:10PM +0900, Tomoya MORINAGA wrote:
>> 2011/11/21 Mark Brown <broonie@opensource.wolfsonmicro.com> wrote:
>
>> BTW, What's TLV ? Let me know the full spell of this "TLV".
>
> Tag/Length/Value.
>
>> >> +static const struct snd_kcontrol_new ml26124_dsp_controls[] = {
>> >> +     SOC_SINGLE("Play Limitter ON/OFF", ML26124_FILTER_EN, 0, 1, 0),
>
>> Do you mean SOC_SINGLE("Play Limitter Switch", ML26124_FILTER_EN, 0, 1, 0) ?
>
> Yes.
>
>> >> +     SOC_SINGLE("Set ALC position", ML26124_FILTER_EN, 5, 1, 0),
>
>> > What does this actually do?  From the name it *really* doesn't look like
>> > a mixer input.
>
>> The above means where connects the ALC.
>> So, this doesn't relate to mixer input.
>
> If this control is not a mixer input it should not be a mixer input in
> the driver.  Though with your explanation I still don't understand what
> it does so presumably the naming also needs to be improved.
>
>> However I couldn't understand the meaning of the widgets. So I didn't
>> write anything.
>
> See Documentation/sound/alsa/soc/dapm.txt.
>
>> >> +static int snd_card_codec_reg_read(struct ml26124_priv *priv,
>> >> +                                unsigned char reg, unsigned char *val)
>> >> +{
>> >> +     unsigned char data;
>> >> +     struct i2c_client *i2c;
>
>> > Use the standard register access code, don't open code things in your
>> > driver unless there's a good reason to.  Current best practice for most
>> > I2C or SPI devices is to use regmap, see any recently added driver for
>> > examples.
>
>> What's "regmap" mean ? or do you mean drivers/mfd/* ?
>> Could you tell me this ?
>
> drivers/base/regmap and also grep in sound/soc/codecs/*.c
>
>> > Use snd_soc_update_bits() and the other standard register access
>> > functions.
>
>> Using snd_soc_update_bits(), need to register ".read" method.
>
> Or use a cache.
>
>> Is the same as the above  snd_card_codec_reg_read ?
>
> I don't understand this question.
>

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

* Re: [PATCH 1/3] sound/soc/codecs: add LAPIS Semiconductor ML26124
  2011-11-25  2:03       ` Tomoya MORINAGA
@ 2011-11-25 11:02         ` Mark Brown
  0 siblings, 0 replies; 27+ messages in thread
From: Mark Brown @ 2011-11-25 11:02 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Fri, Nov 25, 2011 at 11:03:17AM +0900, Tomoya MORINAGA wrote:

> > drivers/base/regmap and also grep in sound/soc/codecs/*.c

> Though grepping "regmap" at sound/soc/codecs/*.c, I couldn't find any file.
> Is this true ?

You should always submit new code against the current development
version of the subsystem you're submitting against.

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

* Re: [PATCH 2/3] sound/soc/lapis: add machine driver
  2011-11-21 11:34   ` Mark Brown
@ 2011-12-02  5:33     ` Tomoya MORINAGA
  2011-12-02  6:14       ` Takashi Iwai
  2011-12-02 11:46       ` Mark Brown
  0 siblings, 2 replies; 27+ messages in thread
From: Tomoya MORINAGA @ 2011-12-02  5:33 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

2011/11/21 Mark Brown <broonie@opensource.wolfsonmicro.com>:
> On Mon, Nov 21, 2011 at 01:08:51PM +0900, Tomoya MORINAGA wrote:
>> Add machine driver for LAPIS Semiconductor ML7213 IOH.
>
> This needs to come after you add the base platform support in the series
> - clearly this won't work without the CPU side drivers it depends on.

Most of the chipset drivers for Intel Atom E6xx series have already
accepted by upstream.
e.g. i2c-eg20t, pch_dma, pch_uart, spi-topcliff-pch, ml-ioh-gpio, etc...

BTW, every time I send mail to "alsa-devel@alsa-project.org",
I receive returned mail "Your message to Alsa-devel awaits moderator approval".
Is this normal things ?
Of course, I've already subscribed to "alsa-devel@alsa-project.org".

thanks,
tomoya

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

* Re: [PATCH 2/3] sound/soc/lapis: add machine driver
  2011-12-02  5:33     ` Tomoya MORINAGA
@ 2011-12-02  6:14       ` Takashi Iwai
  2011-12-02  6:18         ` Tomoya MORINAGA
  2011-12-02 11:46       ` Mark Brown
  1 sibling, 1 reply; 27+ messages in thread
From: Takashi Iwai @ 2011-12-02  6:14 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Mark Brown, Liam Girdwood, Jaroslav Kysela, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

At Fri, 2 Dec 2011 14:33:59 +0900,
Tomoya MORINAGA wrote:
> BTW, every time I send mail to "alsa-devel@alsa-project.org",
> I receive returned mail "Your message to Alsa-devel awaits moderator approval".
> Is this normal things ?
> Of course, I've already subscribed to "alsa-devel@alsa-project.org".

Did you subscribe with your gmail address, too?


Takashi

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

* Re: [PATCH 2/3] sound/soc/lapis: add machine driver
  2011-12-02  6:14       ` Takashi Iwai
@ 2011-12-02  6:18         ` Tomoya MORINAGA
  2011-12-02  6:39           ` Takashi Iwai
  0 siblings, 1 reply; 27+ messages in thread
From: Tomoya MORINAGA @ 2011-12-02  6:18 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Mark Brown, Liam Girdwood, Jaroslav Kysela, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

2011/12/2 Takashi Iwai <tiwai@suse.de>:
> At Fri, 2 Dec 2011 14:33:59 +0900,
> Tomoya MORINAGA wrote:
>> BTW, every time I send mail to "alsa-devel@alsa-project.org",
>> I receive returned mail "Your message to Alsa-devel awaits moderator approval".
>> Is this normal things ?
>> Of course, I've already subscribed to "alsa-devel@alsa-project.org".
>
> Did you subscribe with your gmail address, too?
Yes, I subscribed this gmail address.

thanks,
tomoya

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

* Re: [PATCH 2/3] sound/soc/lapis: add machine driver
  2011-12-02  6:18         ` Tomoya MORINAGA
@ 2011-12-02  6:39           ` Takashi Iwai
  2011-12-02  6:52             ` Tomoya MORINAGA
  0 siblings, 1 reply; 27+ messages in thread
From: Takashi Iwai @ 2011-12-02  6:39 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Mark Brown, Liam Girdwood, Jaroslav Kysela, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

At Fri, 2 Dec 2011 15:18:06 +0900,
Tomoya MORINAGA wrote:
> 
> 2011/12/2 Takashi Iwai <tiwai@suse.de>:
> > At Fri, 2 Dec 2011 14:33:59 +0900,
> > Tomoya MORINAGA wrote:
> >> BTW, every time I send mail to "alsa-devel@alsa-project.org",
> >> I receive returned mail "Your message to Alsa-devel awaits moderator approval".
> >> Is this normal things ?
> >> Of course, I've already subscribed to "alsa-devel@alsa-project.org".
> >
> > Did you subscribe with your gmail address, too?
> Yes, I subscribed this gmail address.

The mail-server doesn't think so.
Maybe with a different address like mail.google.com or such?


Takashi

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

* Re: [PATCH 2/3] sound/soc/lapis: add machine driver
  2011-12-02  6:39           ` Takashi Iwai
@ 2011-12-02  6:52             ` Tomoya MORINAGA
  2011-12-02  7:01               ` Takashi Iwai
  0 siblings, 1 reply; 27+ messages in thread
From: Tomoya MORINAGA @ 2011-12-02  6:52 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Mark Brown, Liam Girdwood, Jaroslav Kysela, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

2011/12/2 Takashi Iwai <tiwai@suse.de>:
> At Fri, 2 Dec 2011 15:18:06 +0900,
> Tomoya MORINAGA wrote:
>>
>> 2011/12/2 Takashi Iwai <tiwai@suse.de>:
>> > At Fri, 2 Dec 2011 14:33:59 +0900,
>> > Tomoya MORINAGA wrote:
>> >> BTW, every time I send mail to "alsa-devel@alsa-project.org",
>> >> I receive returned mail "Your message to Alsa-devel awaits moderator approval".
>> >> Is this normal things ?
>> >> Of course, I've already subscribed to "alsa-devel@alsa-project.org".
>> >
>> > Did you subscribe with your gmail address, too?
>> Yes, I subscribed this gmail address.
>
> The mail-server doesn't think so.
> Maybe with a different address like mail.google.com or such?
>
For confirmation, I registered my e-mail account again yesterday.
As a result, I got the following mail (Already registered).

From: Majordomo@vger.kernel.org
12月1日 (1日前)

>>>> auth bd2de885 subscribe alsa-devel tomoya.rohm@gmail.com
**** Address already subscribed to alsa-devel

So, I think registration for mailing list is success with this gmail address.
What should I do for next?

thanks,
tomoya

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

* Re: [PATCH 2/3] sound/soc/lapis: add machine driver
  2011-12-02  6:52             ` Tomoya MORINAGA
@ 2011-12-02  7:01               ` Takashi Iwai
  2011-12-02  7:13                 ` Tomoya MORINAGA
  0 siblings, 1 reply; 27+ messages in thread
From: Takashi Iwai @ 2011-12-02  7:01 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Mark Brown, Liam Girdwood, Jaroslav Kysela, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

At Fri, 2 Dec 2011 15:52:28 +0900,
Tomoya MORINAGA wrote:
> 
> 2011/12/2 Takashi Iwai <tiwai@suse.de>:
> > At Fri, 2 Dec 2011 15:18:06 +0900,
> > Tomoya MORINAGA wrote:
> >>
> >> 2011/12/2 Takashi Iwai <tiwai@suse.de>:
> >> > At Fri, 2 Dec 2011 14:33:59 +0900,
> >> > Tomoya MORINAGA wrote:
> >> >> BTW, every time I send mail to "alsa-devel@alsa-project.org",
> >> >> I receive returned mail "Your message to Alsa-devel awaits moderator approval".
> >> >> Is this normal things ?
> >> >> Of course, I've already subscribed to "alsa-devel@alsa-project.org".
> >> >
> >> > Did you subscribe with your gmail address, too?
> >> Yes, I subscribed this gmail address.
> >
> > The mail-server doesn't think so.
> > Maybe with a different address like mail.google.com or such?
> >
> For confirmation, I registered my e-mail account again yesterday.
> As a result, I got the following mail (Already registered).
> 
> From: Majordomo@vger.kernel.org
> 12月1日 (1日前)

Why vger?


Takashi

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

* Re: [PATCH 2/3] sound/soc/lapis: add machine driver
  2011-12-02  7:01               ` Takashi Iwai
@ 2011-12-02  7:13                 ` Tomoya MORINAGA
  2011-12-02  7:16                   ` Takashi Iwai
  0 siblings, 1 reply; 27+ messages in thread
From: Tomoya MORINAGA @ 2011-12-02  7:13 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Mark Brown, Liam Girdwood, Jaroslav Kysela, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

2011/12/2 Takashi Iwai <tiwai@suse.de>:>> For confirmation, I
registered my e-mail account again yesterday.
>> As a result, I got the following mail (Already registered).>>>> From: Majordomo@vger.kernel.org>> 12月1日 (1日前)>> Why vger?>
I referred http://vger.kernel.org/vger-lists.html#alsa-devel.
This looks not TRUE.

I must subscribe from below ?
https://lists.sourceforge.net/lists/listinfo/alsa-announce

thanks
tomoya

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

* Re: [PATCH 2/3] sound/soc/lapis: add machine driver
  2011-12-02  7:13                 ` Tomoya MORINAGA
@ 2011-12-02  7:16                   ` Takashi Iwai
  0 siblings, 0 replies; 27+ messages in thread
From: Takashi Iwai @ 2011-12-02  7:16 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Mark Brown, Liam Girdwood, Jaroslav Kysela, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

At Fri, 2 Dec 2011 16:13:04 +0900,
Tomoya MORINAGA wrote:
> 
> 2011/12/2 Takashi Iwai <tiwai@suse.de>:>> For confirmation, I
> registered my e-mail account again yesterday.
> >> As a result, I got the following mail (Already registered).>>>> From: Majordomo@vger.kernel.org>> 12月1日 (1日前)>> Why vger?>
> I referred http://vger.kernel.org/vger-lists.html#alsa-devel.
> This looks not TRUE.

alsa-devel isn't hosted on vger.
There is one list, but it's just passed to alsa-project.org host.

> I must subscribe from below ?
> https://lists.sourceforge.net/lists/listinfo/alsa-announce

No, it's a different list.  Go through the navigation on
www.alsa-project.org.


Takashi

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

* Re: [PATCH 2/3] sound/soc/lapis: add machine driver
  2011-12-02  5:33     ` Tomoya MORINAGA
  2011-12-02  6:14       ` Takashi Iwai
@ 2011-12-02 11:46       ` Mark Brown
  2011-12-02 12:09         ` Tomoya MORINAGA
  1 sibling, 1 reply; 27+ messages in thread
From: Mark Brown @ 2011-12-02 11:46 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Fri, Dec 02, 2011 at 02:33:59PM +0900, Tomoya MORINAGA wrote:
> 2011/11/21 Mark Brown <broonie@opensource.wolfsonmicro.com>:

> > This needs to come after you add the base platform support in the series
> > - clearly this won't work without the CPU side drivers it depends on.

> Most of the chipset drivers for Intel Atom E6xx series have already
> accepted by upstream.
> e.g. i2c-eg20t, pch_dma, pch_uart, spi-topcliff-pch, ml-ioh-gpio, etc...

The CPU side audio drivers are not there - there seemed to be some quite
serious review issues with those.

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

* Re: [PATCH 2/3] sound/soc/lapis: add machine driver
  2011-12-02 11:46       ` Mark Brown
@ 2011-12-02 12:09         ` Tomoya MORINAGA
  2011-12-02 12:22           ` Mark Brown
  0 siblings, 1 reply; 27+ messages in thread
From: Tomoya MORINAGA @ 2011-12-02 12:09 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

2011/12/2 Mark Brown <broonie@opensource.wolfsonmicro.com>:
> On Fri, Dec 02, 2011 at 02:33:59PM +0900, Tomoya MORINAGA wrote:
>> 2011/11/21 Mark Brown <broonie@opensource.wolfsonmicro.com>:
>
>> > This needs to come after you add the base platform support in the series
>> > - clearly this won't work without the CPU side drivers it depends on.
>
>> Most of the chipset drivers for Intel Atom E6xx series have already
>> accepted by upstream.
>> e.g. i2c-eg20t, pch_dma, pch_uart, spi-topcliff-pch, ml-ioh-gpio, etc...
>
> The CPU side audio drivers are not there - there seemed to be some quite
> serious review issues with those.
>
Let me clarify your saying.
Does the above "audio drivers" mean i2s (of south bridge) driver or HD
audio (of north bridge) or another?

Thanks,
tomoya

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

* Re: [PATCH 2/3] sound/soc/lapis: add machine driver
  2011-12-02 12:09         ` Tomoya MORINAGA
@ 2011-12-02 12:22           ` Mark Brown
  2011-12-02 12:35             ` Tomoya MORINAGA
  0 siblings, 1 reply; 27+ messages in thread
From: Mark Brown @ 2011-12-02 12:22 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Fri, Dec 02, 2011 at 09:09:34PM +0900, Tomoya MORINAGA wrote:
> 2011/12/2 Mark Brown <broonie@opensource.wolfsonmicro.com>:

> > The CPU side audio drivers are not there - there seemed to be some quite
> > serious review issues with those.

> Let me clarify your saying.
> Does the above "audio drivers" mean i2s (of south bridge) driver or HD
> audio (of north bridge) or another?

I2S and audio DMA.

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

* Re: [PATCH 2/3] sound/soc/lapis: add machine driver
  2011-12-02 12:22           ` Mark Brown
@ 2011-12-02 12:35             ` Tomoya MORINAGA
  2011-12-02 12:49               ` Mark Brown
  0 siblings, 1 reply; 27+ messages in thread
From: Tomoya MORINAGA @ 2011-12-02 12:35 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

2011/12/2 Mark Brown <broonie@opensource.wolfsonmicro.com>:
> On Fri, Dec 02, 2011 at 09:09:34PM +0900, Tomoya MORINAGA wrote:
>> 2011/12/2 Mark Brown <broonie@opensource.wolfsonmicro.com>:
>
>> > The CPU side audio drivers are not there - there seemed to be some quite
>> > serious review issues with those.
>
>> Let me clarify your saying.
>> Does the above "audio drivers" mean i2s (of south bridge) driver or HD
>> audio (of north bridge) or another?
>
> I2S and audio DMA.

OK.
As you know, I post i2s/dma driver once. I understand need to do much
modification.
I'm going to develop codec driver, machine driver and
platform(i2s/dma) driver in turn obeying modern ASoC framework.

Do you mean you cannot review this codec driver any more unless I
provide other 2 drivers (machine and platform) ?

Thanks,
tomoya

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

* Re: [PATCH 2/3] sound/soc/lapis: add machine driver
  2011-12-02 12:35             ` Tomoya MORINAGA
@ 2011-12-02 12:49               ` Mark Brown
  0 siblings, 0 replies; 27+ messages in thread
From: Mark Brown @ 2011-12-02 12:49 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

On Fri, Dec 02, 2011 at 09:35:57PM +0900, Tomoya MORINAGA wrote:

> Do you mean you cannot review this codec driver any more unless I
> provide other 2 drivers (machine and platform) ?

No.  This is the review of the machine driver.  The machine driver
depends on both the CODEC and the CPU drivers.

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

* Re: [PATCH 3/3] sound/soc/lapis: add platform driver
  2011-11-21 11:45   ` Mark Brown
@ 2011-12-12  8:28     ` Tomoya MORINAGA
  2011-12-12 10:05       ` [alsa-devel] " Mark Brown
  0 siblings, 1 reply; 27+ messages in thread
From: Tomoya MORINAGA @ 2011-12-12  8:28 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Lars-Peter Clausen,
	Dimitris Papastamos, Mike Frysinger, Daniel Mack, alsa-devel,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

Searching git log,
tegra_i2s.c seems modern.
I have a question.

According to platfom.txt, describes like below.
struct snd_soc_ops {
...
};

On the other hand, tegra_i2s.c describes like below.

static const struct snd_soc_dai_ops tegra_i2s_dai_ops = {
...
};

Which is true as modern driver ? or case by case ?


>> +#define      I2S_CH0_MCLK            (12288000) /* Master Clock Frequency[Hz] */
> This looks like it should be board specific?
Should our platform driver use "clk_get()" ?
If no, how should our driver get the value ?

thanks in advance.

tomoya

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

* Re: [alsa-devel] [PATCH 3/3] sound/soc/lapis: add platform driver
  2011-12-12  8:28     ` Tomoya MORINAGA
@ 2011-12-12 10:05       ` Mark Brown
  2011-12-13  4:38         ` Tomoya MORINAGA
  0 siblings, 1 reply; 27+ messages in thread
From: Mark Brown @ 2011-12-12 10:05 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Dimitris Papastamos, alsa-devel, Lars-Peter Clausen,
	Mike Frysinger, qi.wang, Takashi Iwai, linux-kernel, yong.y.wang,
	kok.howg.ewe, Daniel Mack, Liam Girdwood, joel.clark

On Mon, Dec 12, 2011 at 05:28:13PM +0900, Tomoya MORINAGA wrote:

> struct snd_soc_ops {
> ...
> };

> On the other hand, tegra_i2s.c describes like below.

> static const struct snd_soc_dai_ops tegra_i2s_dai_ops = {
> ...
> };

> Which is true as modern driver ? or case by case ?

The latter.  With things like this it would be *really* helpful if you
could take a step back and think about what the differences mean and why
they are different.

> 
> >> +#define      I2S_CH0_MCLK            (12288000) /* Master Clock Frequency[Hz] */
> > This looks like it should be board specific?
> Should our platform driver use "clk_get()" ?
> If no, how should our driver get the value ?

Again, with things like this it would be really helpful if you could
attempt to answer questions for yourself.  Have you looked at how other
platforms configure clock rates from machine drivers?

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

* Re: [alsa-devel] [PATCH 3/3] sound/soc/lapis: add platform driver
  2011-12-12 10:05       ` [alsa-devel] " Mark Brown
@ 2011-12-13  4:38         ` Tomoya MORINAGA
  2011-12-13  4:59           ` Mark Brown
  0 siblings, 1 reply; 27+ messages in thread
From: Tomoya MORINAGA @ 2011-12-13  4:38 UTC (permalink / raw)
  To: Mark Brown
  Cc: Dimitris Papastamos, alsa-devel, Lars-Peter Clausen,
	Mike Frysinger, qi.wang, Takashi Iwai, linux-kernel, yong.y.wang,
	kok.howg.ewe, Daniel Mack, Liam Girdwood, joel.clark

2011/12/12 Mark Brown <broonie@opensource.wolfsonmicro.com>:
> On Mon, Dec 12, 2011 at 05:28:13PM +0900, Tomoya MORINAGA wrote:
>
>> struct snd_soc_ops {
>> ...
>> };
>
>> On the other hand, tegra_i2s.c describes like below.
>
>> static const struct snd_soc_dai_ops tegra_i2s_dai_ops = {
>> ...
>> };
>
>> Which is true as modern driver ? or case by case ?
>
> The latter.  With things like this it would be *really* helpful if you
> could take a step back and think about what the differences mean and why
> they are different.

Our driver needs ".pointer" method.
However, "struct snd_soc_dai_ops" doesn't have the method.
So, I think "struct snd_soc_dai_ops" cannot be applied to our driver.
Searching other drivers, "blackfin/bf5xx-i2s-pcm.c" uses "struct
snd_soc_ops" not "struct snd_soc_dai_ops".
Let me know your opinion.

Thanks,

tomoya

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

* Re: [alsa-devel] [PATCH 3/3] sound/soc/lapis: add platform driver
  2011-12-13  4:38         ` Tomoya MORINAGA
@ 2011-12-13  4:59           ` Mark Brown
  0 siblings, 0 replies; 27+ messages in thread
From: Mark Brown @ 2011-12-13  4:59 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Dimitris Papastamos, alsa-devel, Lars-Peter Clausen,
	Mike Frysinger, qi.wang, Takashi Iwai, linux-kernel, yong.y.wang,
	kok.howg.ewe, Daniel Mack, Liam Girdwood, joel.clark

On Tue, Dec 13, 2011 at 01:38:25PM +0900, Tomoya MORINAGA wrote:
> 2011/12/12 Mark Brown <broonie@opensource.wolfsonmicro.com>:

> > The latter.  With things like this it would be *really* helpful if you
> > could take a step back and think about what the differences mean and why
> > they are different.

> Our driver needs ".pointer" method.
> However, "struct snd_soc_dai_ops" doesn't have the method.
> So, I think "struct snd_soc_dai_ops" cannot be applied to our driver.

What makes you claim this - *why* does your DAI driver need a pointer
method?

> Searching other drivers, "blackfin/bf5xx-i2s-pcm.c" uses "struct
> snd_soc_ops" not "struct snd_soc_dai_ops".
> Let me know your opinion.

Of course the DMA driver uses the ops for DMA drivers!  Alternatively if
your question above is about your DMA driver then why is your DMA driver
using DAI ops?

To repeat what I said above in a different way *please* put more effort
into understanding things and trying to solve problems for yourself, or
improving the way you ask questions.

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

end of thread, other threads:[~2011-12-13  5:00 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-11-21  4:08 [PATCH 1/3] sound/soc/codecs: add LAPIS Semiconductor ML26124 Tomoya MORINAGA
2011-11-21  4:08 ` [PATCH 2/3] sound/soc/lapis: add machine driver Tomoya MORINAGA
2011-11-21 11:34   ` Mark Brown
2011-12-02  5:33     ` Tomoya MORINAGA
2011-12-02  6:14       ` Takashi Iwai
2011-12-02  6:18         ` Tomoya MORINAGA
2011-12-02  6:39           ` Takashi Iwai
2011-12-02  6:52             ` Tomoya MORINAGA
2011-12-02  7:01               ` Takashi Iwai
2011-12-02  7:13                 ` Tomoya MORINAGA
2011-12-02  7:16                   ` Takashi Iwai
2011-12-02 11:46       ` Mark Brown
2011-12-02 12:09         ` Tomoya MORINAGA
2011-12-02 12:22           ` Mark Brown
2011-12-02 12:35             ` Tomoya MORINAGA
2011-12-02 12:49               ` Mark Brown
2011-11-21  4:08 ` [PATCH 3/3] sound/soc/lapis: add platform driver Tomoya MORINAGA
2011-11-21 11:45   ` Mark Brown
2011-12-12  8:28     ` Tomoya MORINAGA
2011-12-12 10:05       ` [alsa-devel] " Mark Brown
2011-12-13  4:38         ` Tomoya MORINAGA
2011-12-13  4:59           ` Mark Brown
2011-11-21 11:26 ` [PATCH 1/3] sound/soc/codecs: add LAPIS Semiconductor ML26124 Mark Brown
2011-11-22 10:47   ` Tomoya MORINAGA
2011-11-22 11:19     ` Mark Brown
2011-11-25  2:03       ` Tomoya MORINAGA
2011-11-25 11:02         ` Mark Brown

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).