linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
@ 2011-07-06 10:27 Toshiharu Okada
  2011-07-06 11:06 ` Takashi Iwai
  0 siblings, 1 reply; 19+ messages in thread
From: Toshiharu Okada @ 2011-07-06 10:27 UTC (permalink / raw)
  To: perex, tiwai, alsa-devel, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, tomoya-linux,
	Toshiharu Okada


This patch is for SoundCard driver of OKI SEMICONDUCTOR ML7213
IOH(Input/Output Hub).
These ML7213 IOH is companion chip for Intel Atom E6xx series.
ML7213 IOH is for IVI(In-Vehicle Infotainment) use.

[About this driver]
Audio Codec does not exist in ML7213 IOH.
Therefore, this SoundCard driver controls ML26124 Audio Codec connected by
I2S of ML7213 IOH.
This driver consists of two modules, an ALSA sound card driver and I2S
driver.
An ALSA sound card driver performs control of ML26124 by I2C of ML7213 IOH.
When another Audio Codec is connected to I2S of ML7213 IOH,
it can respond by change of I2C control of an ALSA sound card driver.


Signed-off-by: Toshiharu Okada <toshiharu-linux@dsn.okisemi.com>
---
 sound/drivers/Kconfig      |   34 ++
 sound/drivers/Makefile     |    8 +-
 sound/drivers/ioh_i2s.c    | 1310 ++++++++++++++++++++++++++++++++++++++++++++
 sound/drivers/ioh_i2s.h    |  116 ++++
 sound/drivers/ml7213-ioh.c |  985 +++++++++++++++++++++++++++++++++
 5 files changed, 2452 insertions(+), 1 deletions(-)
 create mode 100644 sound/drivers/ioh_i2s.c
 create mode 100644 sound/drivers/ioh_i2s.h
 create mode 100644 sound/drivers/ml7213-ioh.c

diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index c896116..e098a91 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -209,6 +209,39 @@ config SND_AC97_POWER_SAVE
 
 	  See Documentation/sound/alsa/powersave.txt for more details.
 
+config ML7213_I2S
+	tristate "OKI SEMICONDUCTOR ML7213 IOH I2S Driver"
+	help
+	  This driver is OKI SEMICONDUCTOR ML7213 IOH I2S driver.
+	  ML7213 is companion chip for Intel Atom E6xx series.
+	  This driver is required to use ML7213 SoundCard.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called ioh_i2s.
+
+config ML7213_I2S_DEBUG
+	bool "ML7213 I2S driver debug"
+	depends on ML7213_I2S
+	default n
+	help
+	  This option enables the addition of a debugging code to
+	  the OKI SEMICONDUCTOR ML7213 IOH I2S Driver. If you are unsure, say N.
+
+	  To compile this driver as a debugging module, choose Y here: the module
+	  will be called ioh_i2s.
+
+config SND_ML7213_I2S
+	tristate "OKI SEMICONDUCTOR ML7213 SoundCard Driver for ML26124 Audio Codec"
+	depends on ML7213_I2S
+	default y
+	help
+	  This driver is OKI SEMICONDUCTOR ML7213 IOH SoundCard driver
+	  who controls ML26124 Audio Codec connected by I2S of ML7213 IOH.
+	  Control of ML26124 uses I2C of ML7213 IOH.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-ml7213ioh.
+
 config SND_AC97_POWER_SAVE_DEFAULT
 	int "Default time-out for AC97 power-save mode"
 	depends on SND_AC97_POWER_SAVE
@@ -219,4 +252,5 @@ config SND_AC97_POWER_SAVE_DEFAULT
 
 	  See SND_AC97_POWER_SAVE for more details.
 
+
 endif	# SND_DRIVERS
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
index 1a8440c..41350ea 100644
--- a/sound/drivers/Makefile
+++ b/sound/drivers/Makefile
@@ -11,15 +11,21 @@ snd-portman2x4-objs := portman2x4.o
 snd-serial-u16550-objs := serial-u16550.o
 snd-virmidi-objs := virmidi.o
 snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o
+snd-ml7213ioh-objs := ml7213-ioh.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
 obj-$(CONFIG_SND_ALOOP) += snd-aloop.o
 obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o
 obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
+obj-$(CONFIG_SND_ML7213_I2S) += snd-ml7213ioh.o
 obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
 obj-$(CONFIG_SND_MTS64) += snd-mts64.o
 obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
 obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o
-
 obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/
+obj-$(CONFIG_ML7213_I2S) += ioh_i2s.o
+ifeq ($(CONFIG_ML7213_I2S_DEBUG),y)
+EXTRA_CFLAG += -DDEBUG
+endif
+
diff --git a/sound/drivers/ioh_i2s.c b/sound/drivers/ioh_i2s.c
new file mode 100644
index 0000000..a40f6df
--- /dev/null
+++ b/sound/drivers/ioh_i2s.c
@@ -0,0 +1,1310 @@
+/*
+ * Copyright (C) 2010 OKI 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/slab.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/ctype.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#include "ioh_i2s.h"
+
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+
+#include <linux/pci.h>
+#include <linux/pch_dma.h>
+
+#define MAX_I2S_IF	6	/*I2S0 ~ I2S5*/
+
+#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
+
+/* 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;
+	atomic_t rx_busy;
+	atomic_t tx_busy;
+
+	int ignore_rx_overrun;
+
+	/* Transmit side DMA */
+	atomic_t pending_tx;
+
+	struct ioh_dma_config *dma_config;
+
+	char rx_name[16];
+	char tx_name[16];
+
+	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 */
+
+	void *rxbuf_virt;
+	void *txbuf_virt;
+	unsigned char *tx_tail;
+	unsigned char *tx_head;
+	unsigned char *tx_data_head;
+	unsigned char *tx_complete;
+	unsigned char *rx_tail;
+	unsigned char *rx_head;
+	unsigned char *rx_data_head;
+	unsigned char *rx_complete;
+	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;
+
+	spinlock_t tx_lock;
+
+	struct pch_dma_slave		param_tx;
+	struct pch_dma_slave		param_rx;
+	unsigned int			mapbase;
+
+	void *rx_callback_data;
+	void (*rx_done) (void *callback_data, int status);
+	void *tx_callback_data;
+	void (*tx_done) (void *callback_data, int status);
+
+	unsigned int tx_lower_data_flag;
+
+	int dma_tx_unit; /* 1Byte of 2Byte or 4Byte */
+
+	int dma_tx_flag;	/* Now waiting tx DMA completion */
+	int dma_rx_flag;	/* Now waiting rx DMA completion */
+};
+
+static struct ioh_i2s_data devs[MAX_I2S_IF];
+
+/******************************************************************************
+	HAL (Hardware Abstruction Layer)
+*******************************************************************************/
+static void ioh_i2s_reset(struct ioh_i2s_data *priv)
+{
+	int channel = priv->ch;
+
+	iowrite32(1 << channel, 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);
+}
+
+/******************************************************************************
+	DMA Functions
+*******************************************************************************/
+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_common(
+			struct ioh_i2s_data *priv,
+			char *chan_name,
+			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 */
+		priv->param_rx.width = PCH_DMA_WIDTH_2_BYTES;
+		priv->param_rx.dma_dev = &dma_dev->dev;
+		priv->param_rx.chan_id = priv->ch * 2 + 1; /* ch Rx=1,3,...11 */
+		priv->param_rx.rx_reg = (dma_addr_t)((char *)priv->mapbase +\
+					priv->ch * 0x800 +\
+					I2SDRRXMIRROR_OFFSET);
+		chan = dma_request_channel(mask, filter, &priv->param_rx);
+		if (chan == NULL) {
+			dev_err(priv->dev, "Failed dma_request_channel %s for"
+				" I2S %s\n", chan_name, priv->rx_name);
+			return NULL;
+		}
+		priv->chan_rx = chan;
+
+	} else if (dir == DMA_TO_DEVICE) { /* Tx */
+		priv->param_tx.width = PCH_DMA_WIDTH_2_BYTES;
+		priv->param_tx.dma_dev = &dma_dev->dev;
+		priv->param_tx.chan_id = priv->ch * 2; /* DMA ch Tx=0,2,...10 */
+		priv->param_tx.tx_reg = (dma_addr_t)((char *)priv->mapbase +\
+					priv->ch * 0x800 +\
+					I2SDRTXMIRROR_OFFSET);
+		chan = dma_request_channel(mask, filter, &priv->param_tx);
+		if (chan == NULL) {
+			dev_err(priv->dev, "Failed dma_request_channel %s for"
+				" I2S %s\n", chan_name, priv->tx_name);
+			return NULL;
+		}
+		priv->chan_tx = chan;
+	} else {
+		dev_err(priv->dev, "%s:Invalid direction (%d)\n",
+			chan_name, dir);
+		return NULL;
+	}
+
+	return chan;
+}
+
+static void ioh_setup_rx_dma(struct ioh_i2s_data *priv)
+{
+	ioh_request_dma_channel_common(
+		priv,
+		priv->dma_config->rx_chan,
+		DMA_FROM_DEVICE);
+}
+
+static void ioh_setup_tx_dma(struct ioh_i2s_data *priv)
+{
+	ioh_request_dma_channel_common(
+		priv,
+		priv->dma_config->tx_chan,
+		DMA_TO_DEVICE);
+}
+
+static void i2s_dma_tx_complete(void *arg)
+{
+	struct ioh_i2s_data *priv = arg;
+	struct scatterlist *sg = priv->sg_tx_cur;
+	int i;
+
+	dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p sg_len=%d num=%d",
+		__func__, priv->tx_data_head, priv->tx_complete, sg_dma_len(sg),
+		priv->tx_num);
+	dma_sync_sg_for_cpu(priv->dev, sg, priv->tx_num, DMA_TO_DEVICE);
+
+	for (i = 0; i < priv->tx_num; i++, sg++)
+		priv->tx_complete += sg_dma_len(sg) * priv->dma_tx_unit;
+
+	if (priv->tx_complete > priv->tx_tail) {
+		priv->tx_complete =\
+			     (char *)(((u32)priv->tx_complete &  0x00000fff) |\
+			     ((u32)priv->tx_head & 0xfffff000));
+	}
+
+	dev_dbg(priv->dev, "-->data_complete=%p\n", priv->tx_complete);
+
+	if ((priv->tx_complete - 1) == priv->tx_data_head)
+		ioh_i2s_disable_interrupts(priv, IOH_PLAYBACK);
+
+	async_tx_ack(priv->desc_tx);
+	if (priv->tx_done)
+		priv->tx_done(priv->tx_callback_data, IOH_EOK);
+
+	ioh_i2s_enable_tx_empty_ir(priv);
+	priv->dma_tx_flag = 0;
+}
+
+static void i2s_dma_rx_complete(void *arg)
+{
+	struct ioh_i2s_data *priv = arg;
+	struct scatterlist *sg = priv->sg_rx_cur;
+
+	dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p sg_dma_len=%d\n",
+		__func__, priv->rx_data_head, priv->rx_complete,
+		sg_dma_len(sg));
+
+	priv->rx_data_head += sg_dma_len(sg) * priv->dma_tx_unit;
+
+	dev_dbg(priv->dev, "-->data_head=%p\n", priv->rx_data_head);
+
+	async_tx_ack(priv->desc_rx);
+
+	dma_sync_sg_for_cpu(priv->dev, sg, 1, DMA_FROM_DEVICE);
+
+	if (priv->rx_done)
+		priv->rx_done(priv->rx_callback_data, IOH_EOK);
+
+	ioh_i2s_enable_rx_full_ir(priv);
+	priv->dma_rx_flag = 0;
+}
+
+/******************************************************************************
+	Intrrupt Functions
+*******************************************************************************/
+void i2s_tx_almost_empty_ir(struct ioh_i2s_data *priv, int ch)
+{
+	struct dma_async_tx_descriptor *desc;
+	int num;
+	int tx_data_index;
+	int tx_comp_index;
+	struct scatterlist *sg = priv->sg_tx_p;
+
+	dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p\n", __func__,
+		priv->tx_data_head, priv->tx_complete);
+
+	tx_data_index = (((int)priv->tx_data_head) & 0xfff) /\
+			(I2S_AEMPTY_THRESH * I2S_1PERIODS_BYTES);
+	tx_comp_index = (((int)priv->tx_complete) & 0xfff) /\
+			(I2S_AEMPTY_THRESH * I2S_1PERIODS_BYTES);
+
+	if (tx_data_index > tx_comp_index)
+		num = tx_data_index - tx_comp_index - 1;
+	else if (tx_comp_index == (priv->tx_nent - 1))
+		num = tx_data_index;
+	else
+		num = priv->tx_nent - tx_comp_index - 1;
+
+	dev_dbg(priv->dev, "%s: head_index=%d complete_index=%d num=%d\n",
+		__func__, tx_data_index, tx_comp_index, num);
+
+	if (num < 1) { /* there is no data */
+		priv->tx_lower_data_flag += 1;
+		if (priv->tx_lower_data_flag >= 10) {
+			priv->tx_lower_data_flag = 0;
+			priv->tx_data_head = priv->tx_head;
+			priv->tx_complete = priv->tx_tail;
+			ioh_i2s_disable_interrupts(priv, IOH_PLAYBACK);
+			ioh_i2s_disable_tx_empty_ir(priv);
+			dev_dbg(priv->dev, "%s:No data to send\n", __func__);
+		}
+		return; /* No data to transmit */
+	}
+
+	priv->tx_lower_data_flag = 0;
+
+	tx_comp_index = (tx_comp_index + 1) % (priv->tx_nent);
+	sg = sg + tx_comp_index; /* Point head of sg must be sent */
+	priv->sg_tx_cur = sg; /* Save tx condition */
+	priv->tx_num = num; /* Save tx condition */
+
+	dev_dbg(priv->dev, "%s: sg = sg + %d tx_nent=%d\n",
+		__func__, tx_comp_index, priv->tx_nent);
+
+	desc = priv->chan_tx->device->device_prep_slave_sg(priv->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);
+	priv->dma_tx_flag = 1;
+
+	dma_sync_sg_for_device(priv->dev, sg, num, DMA_TO_DEVICE);
+	priv->desc_tx = desc;
+	desc->callback = i2s_dma_tx_complete;
+	desc->callback_param = priv;
+
+	atomic_inc(&priv->pending_tx);
+	desc->tx_submit(desc);
+
+	dma_async_issue_pending(priv->chan_tx);
+}
+
+void i2s_rx_almost_full_ir(struct ioh_i2s_data *priv)
+{
+	struct dma_async_tx_descriptor *desc;
+	struct scatterlist *sg;
+	int rx_data_index;
+
+	sg = priv->sg_rx_p;
+	rx_data_index = (((int)priv->rx_data_head) & 0xfff) /\
+			(I2S_AFULL_THRESH * I2S_1PERIODS_BYTES);
+	if (rx_data_index > priv->rx_nent)
+		rx_data_index = 0;
+
+	dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p data_index=%d\n",
+		__func__, priv->rx_data_head, priv->rx_complete, rx_data_index);
+
+	sg += rx_data_index;
+
+	desc = priv->chan_rx->device->device_prep_slave_sg(priv->chan_rx,
+			sg, 1, DMA_FROM_DEVICE,
+			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(priv->dev, "%s:device_prep_slave_sg Failed\n",
+			__func__);
+		return;
+	}
+
+	priv->sg_rx_cur = sg; /* Save rx condition */
+	ioh_i2s_disable_rx_full_ir(priv);
+	priv->dma_rx_flag = 1;
+	dma_sync_sg_for_device(priv->dev, sg, 1, DMA_FROM_DEVICE);
+
+	priv->desc_rx = desc;
+	desc->callback = i2s_dma_rx_complete;
+	desc->callback_param = priv;
+	desc->tx_submit(desc);
+	dma_async_issue_pending(priv->chan_rx);
+}
+
+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_rx(struct ioh_i2s_data *priv)
+{
+	unsigned int status;
+	int channel = priv->ch;
+	int offset = channel * 0x800;
+
+	if (!priv->dma_rx_flag) {
+		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);
+	}
+}
+
+static inline void ioh_i2s_interrupt_sub_tx(struct ioh_i2s_data *priv)
+{
+	unsigned int status;
+	int channel = priv->ch;
+	int offset = channel * 0x800;
+
+	if (!priv->dma_tx_flag) {
+		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, channel);
+
+		/*Clear the interrupt status */
+		iowrite32(status, priv->iobase + I2SISTTX_OFFSET + offset);
+	}
+}
+
+int ioh_i2s_event(struct ioh_i2s_data *priv)
+{
+	u32 idisp;
+	unsigned long flags;
+	int ret = IRQ_NONE;
+
+	spin_lock_irqsave(&priv->tx_lock, flags);
+
+	idisp = ioread32(priv->iobase + I2SIDISP_OFFSET);
+	if (idisp & BIT(priv->ch + 16)) {
+		dev_dbg(priv->dev, "Rx%d interrupt occures\n", priv->ch);
+		ioh_i2s_interrupt_sub_rx(priv);
+		ret = IRQ_HANDLED;
+	}
+
+	if (idisp & BIT(priv->ch)) {
+		dev_dbg(priv->dev, "Tx%d interrupt occures\n", priv->ch);
+		ioh_i2s_interrupt_sub_tx(priv);
+		ret = IRQ_HANDLED;
+	}
+
+	spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+	return ret;
+}
+
+/******************************************************************************
+	External Functions (Called by Sourdcard driver)
+*******************************************************************************/
+void ioh_i2s_ignore_rx_overrun(struct ioh_i2s_data *priv)
+{
+	priv->ignore_rx_overrun = 1;
+}
+EXPORT_SYMBOL_GPL(ioh_i2s_ignore_rx_overrun);
+
+struct ioh_i2s_data *ioh_i2s_open(int ch, enum ioh_direction dir,
+				     const char *name, void *cbd,
+				     void (*cb)(void *cbd, int status))
+{
+	struct ioh_i2s_data *obj = NULL;
+	struct scatterlist *sg;
+	int rx_size;
+	int rx_num;
+	int tx_size;
+	int tx_num;
+	int i;
+
+	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;
+	}
+	obj = &devs[ch];
+
+	obj->ignore_rx_overrun = 0;
+	obj->dma_tx_unit = 2;	/* Tx transmits wuth 2Byte Unit */
+
+	if (dir) {
+		/* Rx configuration */
+		if (atomic_read(&obj->rx_busy)) {
+			dev_err(obj->dev, "rx i2s%d have already opened\n", ch);
+			atomic_dec(&obj->rx_busy);
+			return 0;
+		}
+		atomic_inc(&obj->rx_busy);
+
+		strcpy(obj->rx_name, name);
+		ioh_setup_rx_dma(obj);
+		if (!obj->chan_rx) {
+			dev_err(obj->dev, "%s:ioh_setup_rx_dma failed\n",
+				__func__);
+			return NULL;
+		}
+
+		obj->rxbuf_virt = dma_alloc_coherent(obj->dev, PAGE_SIZE,
+						&obj->rx_buf_dma, GFP_KERNEL);
+		if (!obj->rxbuf_virt) {
+			dev_err(obj->dev, "dma_alloc_coherent Failed\n");
+			return NULL;
+		}
+
+		rx_size = I2S_AFULL_THRESH * I2S_1PERIODS_BYTES;
+
+		/* The number of scatter list (Franction area is not used) */
+		rx_num = PAGE_SIZE / rx_size;
+
+		dev_dbg(obj->dev, "%s: rx: scatter_num=%d scatter_size=%d\n",
+			__func__, rx_num, rx_size);
+
+		obj->sg_rx_p =\
+		       kzalloc(sizeof(struct scatterlist) *rx_num, GFP_ATOMIC);
+
+		sg = obj->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(obj->rxbuf_virt), rx_size,
+				    rx_size * i);
+			sg_dma_len(sg) = rx_size / obj->dma_tx_unit;
+			sg_dma_address(sg) = obj->rx_buf_dma + sg->offset;
+		}
+
+		obj->rx_head = (unsigned char *)obj->rxbuf_virt;
+		obj->rx_tail = (unsigned char *)obj->rxbuf_virt +\
+				rx_num * rx_size - 1;
+		obj->rx_data_head = (unsigned char *)obj->rxbuf_virt;
+		obj->rx_complete = (unsigned char *)obj->rx_tail;
+
+		obj->rx_nent = rx_num;
+	} else {
+		/* Tx configuration */
+		if (atomic_read(&obj->tx_busy)) {
+			dev_err(obj->dev, "tx i2s%d have already opened\n", ch);
+			atomic_dec(&obj->tx_busy);
+			return 0;
+		}
+		atomic_inc(&obj->tx_busy);
+
+		strcpy(obj->tx_name, name);
+		ioh_setup_tx_dma(obj);
+		if (!obj->chan_tx) {
+			dev_err(obj->dev, "%s:ioh_setup_tx_dma failed\n",
+				__func__);
+			return NULL;
+		}
+
+		obj->txbuf_virt = dma_alloc_coherent(obj->dev, PAGE_SIZE,
+						&obj->tx_buf_dma, GFP_KERNEL);
+
+		if (!obj->txbuf_virt) {
+			dev_err(obj->dev, "dma_alloc_coherent Failed\n");
+			return NULL;
+		}
+
+		obj->tx_head = (unsigned char *)obj->txbuf_virt;
+		obj->tx_tail = (unsigned char *)obj->txbuf_virt + PAGE_SIZE - 1;
+		obj->tx_data_head = (unsigned char *)obj->txbuf_virt;
+		obj->tx_complete = (unsigned char *)obj->tx_tail;
+
+		tx_size = I2S_AEMPTY_THRESH * I2S_1PERIODS_BYTES;
+		if (PAGE_SIZE % tx_size)
+			/* tx_num = The number of scatter list */
+			tx_num = PAGE_SIZE / tx_size + 1;
+		else
+			tx_num = PAGE_SIZE / tx_size;
+
+		dev_dbg(obj->dev, "%s: tx: scatter_num=%d scatter_size=%d\n",
+			__func__, tx_num, tx_size);
+
+		obj->sg_tx_p =\
+		       kzalloc(sizeof(struct scatterlist) *tx_num, GFP_ATOMIC);
+
+		sg_init_table(obj->sg_tx_p, tx_num); /* Initialize SG table */
+		sg = obj->sg_tx_p;
+
+		for (i = 0; i < tx_num; i++, sg++) {
+			if (i == (tx_num - 1)) {
+				if (PAGE_SIZE % tx_size) {
+					sg_set_page(sg,
+						  virt_to_page(obj->txbuf_virt),
+						  PAGE_SIZE % tx_size,
+						  tx_size * i);
+					sg_dma_len(sg) = (PAGE_SIZE % tx_size)\
+							/ obj->dma_tx_unit;
+				} else {
+					sg_set_page(sg,
+						  virt_to_page(obj->txbuf_virt),
+						  tx_size, tx_size * i);
+					sg_dma_len(sg) = tx_size\
+							/ obj->dma_tx_unit;
+				}
+			} else {
+				sg_set_page(sg, virt_to_page(obj->txbuf_virt),
+					    tx_size, tx_size * i);
+				sg_dma_len(sg) = tx_size / obj->dma_tx_unit;
+			}
+			sg_dma_address(sg) = obj->tx_buf_dma + sg->offset;
+		}
+		obj->tx_nent = tx_num;
+	}
+
+	return obj;
+}
+EXPORT_SYMBOL_GPL(ioh_i2s_open);
+
+void ioh_i2s_release(struct ioh_i2s_data *priv, enum ioh_direction dir)
+{
+	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);
+		if (priv->chan_rx) {
+			priv->chan_rx->device->device_control(priv->chan_rx,
+							     DMA_TERMINATE_ALL,
+							     0);
+			dma_release_channel(priv->chan_rx);
+			priv->chan_rx = NULL;
+		}
+
+		kfree(priv->sg_rx_p);
+		if (priv->rxbuf_virt)
+			dma_free_coherent(priv->dev, PAGE_SIZE,
+					  priv->rxbuf_virt,
+					  priv->rx_buf_dma);
+
+		priv->rxbuf_virt = NULL;
+		priv->rx_buf_dma = 0;
+		atomic_dec(&priv->rx_busy);
+
+	} else {
+		ioh_i2s_disable_interrupts(priv, IOH_PLAYBACK);
+		ioh_i2s_disable_tx_empty_ir(priv);
+		if (priv->chan_tx) {
+			priv->chan_tx->device->device_control(priv->chan_tx,
+							     DMA_TERMINATE_ALL,
+							     0);
+			dma_release_channel(priv->chan_tx);
+			priv->chan_tx = NULL;
+		}
+
+		kfree(priv->sg_tx_p);
+		if (priv->txbuf_virt)
+			dma_free_coherent(priv->dev, PAGE_SIZE,
+					  priv->txbuf_virt,
+					  priv->tx_buf_dma);
+
+		priv->txbuf_virt = NULL;
+		priv->tx_buf_dma = 0;
+		atomic_dec(&priv->tx_busy);
+	}
+}
+EXPORT_SYMBOL_GPL(ioh_i2s_release);
+
+
+void ioh_i2s_configure_i2s_regs(struct ioh_i2s_data *priv,
+				    unsigned long bitrate,
+				    struct ioh_i2s_config_reg *config,
+				    enum ioh_direction dir)
+{
+	int ch = priv->ch;
+	int offset = ch * 0x800;
+
+	/* Common register */
+	iowrite32(config->cmn.i2sclkcnt,
+		  priv->iobase + I2SCLKCNT0_OFFSET + 0x10*ch);
+
+	if (dir) {
+		/* 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 {
+		/* 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);
+	}
+}
+EXPORT_SYMBOL_GPL(ioh_i2s_configure_i2s_regs);
+
+void ioh_i2s_write(struct ioh_i2s_data *priv,
+		       const void *data,
+		       int len,
+		       void *callback_data,
+		       void (*done) (void *callback_data, int status))
+{
+	int rem1;
+	int rem2;
+
+	priv->tx_callback_data = callback_data;
+	priv->tx_done = done;
+
+	dev_dbg(priv->dev, "%s: [ch%d] len=%d data_head=%p data_complete=%p",
+		__func__, priv->ch, len, priv->tx_data_head, priv->tx_complete);
+
+	if (priv->tx_data_head > priv->tx_tail)
+		priv->tx_data_head = priv->tx_head;
+
+	if ((priv->tx_data_head + len - 1) <= priv->tx_tail) {
+		memcpy(priv->tx_data_head, data, len);
+		priv->tx_data_head += len;
+	} else {
+		rem1 = priv->tx_tail - priv->tx_data_head + 1;
+		rem2 = len - rem1;
+		memcpy(priv->tx_data_head, data, rem1);
+		priv->tx_data_head = priv->tx_head;
+		memcpy(priv->tx_data_head, data + rem1, rem2);
+		priv->tx_data_head += rem2;
+	}
+	dev_dbg(priv->dev, "-->data_head=%p\n", priv->tx_data_head);
+
+	ioh_i2s_clear_tx_sts_ir(priv);
+	ioh_i2s_tx_clear_dma_mask(priv);
+	ioh_i2s_enable_tx_empty_ir(priv);
+	ioh_i2s_enable_interrupts(priv, IOH_PLAYBACK);
+}
+EXPORT_SYMBOL_GPL(ioh_i2s_write);
+
+void ioh_i2s_read(struct ioh_i2s_data *priv,
+		      void *data,
+		      int len,
+		      void *callback_data,
+		      void (*done) (void *callback_data, int status))
+{
+	int rx_ready_bytes;
+	unsigned int rem1 = 0, rem2 = 0;
+	char *rem;
+	char *copy_head;
+
+	priv->rx_callback_data = callback_data;
+	priv->rx_done = done;
+
+	ioh_i2s_clear_rx_sts_ir(priv);
+	ioh_i2s_rx_clear_dma_mask(priv);
+	ioh_i2s_enable_rx_full_ir(priv);
+	ioh_i2s_enable_interrupts(priv, IOH_CAPTURE);
+
+	dev_dbg(priv->dev, "%s: [ch%d]data_head=%p data_complete=%p len=%d\n",
+		__func__, priv->ch, priv->rx_data_head, priv->rx_complete, len);
+
+	if (((u32)priv->rx_data_head & 0xffff) ==\
+				      ((u32)(priv->rx_complete + 1) & 0xffff)) {
+		dev_dbg(priv->dev, "%s:No data to read\n", __func__);
+		return;
+	}
+
+	if (((u32)(priv->rx_complete + 1) & 0xfff) ==\
+					       (((u32)priv->rx_head) & 0xfff)) {
+		rx_ready_bytes = priv->rx_data_head - priv->rx_head;
+	} else if (priv->rx_complete < priv->rx_data_head) {
+		rx_ready_bytes = priv->rx_data_head - priv->rx_complete - 1;
+	} else {
+		rem1 = priv->rx_tail - priv->rx_complete;
+		if (rem1 < len)
+			rem2 = priv->rx_data_head - priv->rx_head;
+
+		dev_dbg(priv->dev, "%s:rem1=%d rem2=%d\n",
+			__func__, rem1, rem2);
+		rx_ready_bytes = rem1 + rem2;
+	}
+
+	dev_dbg(priv->dev,
+		"%s:rx_ready_bytes=%d(%d, %d) head_index=%d com_index=%d\n",
+		__func__, rx_ready_bytes, rem1, rem2,
+		(((int)priv->rx_data_head) & 0xfff) /\
+					(I2S_AFULL_THRESH * I2S_1PERIODS_BYTES),
+		(((int)priv->rx_complete) & 0xfff) /\
+				       (I2S_AFULL_THRESH * I2S_1PERIODS_BYTES));
+
+	if (priv->rx_data_head > priv->rx_tail)
+		priv->rx_data_head = priv->rx_head;
+
+	if (len > rx_ready_bytes) {
+		dev_dbg(priv->dev, "%s:Not enough data in bufffer\n", __func__);
+		return;
+	}
+
+	if (rem2) {
+		memcpy(data, priv->rx_complete + 1, rem1);
+
+		rem = (char *)data + rem1;
+		memcpy(rem, priv->rx_head, len - rem1);
+		priv->rx_complete = priv->rx_head + len - rem1 - 1;
+	} else {
+		copy_head = (char *)(((u32)(priv->rx_complete + 1) & 0xfff) |\
+			    ((u32)priv->rx_head & 0xfffff000));
+		memcpy(data, copy_head, len);
+		priv->rx_complete += len;
+	}
+
+	priv->rx_complete = (char *)((((u32)priv->rx_complete) & 0x00000fff) |\
+				    (((u32)priv->rx_head) & 0xfffff000));
+
+	dev_dbg(priv->dev, "com_index=%d data_complete=%p\n",
+		(((int)priv->rx_complete) & 0xfff) /\
+		(I2S_AFULL_THRESH * I2S_1PERIODS_BYTES),
+		priv->rx_complete);
+
+}
+EXPORT_SYMBOL_GPL(ioh_i2s_read);
+
+/******************************************************************************
+	PCI Functions
+*******************************************************************************/
+struct ioh_i2s_data *ioh_i2s_create(struct device *dev, void *iobase,
+				       unsigned int mapbase, int ch)
+{
+	struct ioh_i2s_data *obj;
+
+	dev_dbg(dev, "Instantiate an i2s instance with io at v0x%p.\n", iobase);
+
+	obj = &devs[ch];
+	obj->iobase = iobase;
+	obj->mapbase = mapbase;
+	obj->ch = ch;
+	obj->dev = dev;
+
+	atomic_set(&obj->rx_busy, 0);
+	atomic_set(&obj->tx_busy, 0);
+	strcpy(obj->rx_name, "FREE");
+	strcpy(obj->tx_name, "FREE");
+
+	return obj;
+}
+
+void ioh_i2s_destroy(struct ioh_i2s_data *priv)
+{
+}
+
+#define DRV_NAME "ml7213_ioh_i2s"
+#define PCI_VENDOR_ID_ROHM	0X10DB
+#define PCI_DEVICE_ID_ML7213_I2S	0X8033
+
+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,}
+};
+
+struct ioh_i2s_data_pci {
+	struct ioh_i2s_data *devs[MAX_I2S_IF];
+	void __iomem *membase;
+	unsigned int mapbase;
+};
+
+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 handled;
+
+	do {
+		int i;
+
+		handled = 0;
+		for (i = 0; i < MAX_I2S_IF; i++)
+			handled |= ioh_i2s_event(drvdata->devs[i]);
+
+	} while (handled);
+
+	return IRQ_HANDLED;
+}
+
+static int ioh_i2s_pci_probe(struct pci_dev *pdev,
+				 const struct pci_device_id *id)
+{
+	int rv = 0;
+	int i;
+	struct ioh_i2s_data_pci *drvdata;
+	void __iomem *tbl;
+	unsigned int mapbase;
+
+	drvdata = kzalloc(sizeof(struct ioh_i2s_data_pci), 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);
+	mapbase = pci_resource_start(pdev, 1);
+	if (!mapbase) {
+		rv = -ENOMEM;
+		printk(KERN_ERR "pci_resource_start failed\n");
+		goto out_pci_resource_start;
+	}
+	drvdata->membase = tbl;
+	drvdata->mapbase = mapbase;
+
+	for (i = 0; i < MAX_I2S_IF; i++) {
+		drvdata->devs[i] =
+			    ioh_i2s_create(&pdev->dev, tbl, mapbase, i);
+
+		spin_lock_init(&drvdata->devs[i]->tx_lock);
+		drvdata->devs[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_disable;
+	}
+
+	return rv;
+out_pci_resource_start:
+	pci_iounmap(pdev, tbl);
+out_disable:
+	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;
+
+	free_irq(pdev->irq, pdev);
+	for (i = 0; i < MAX_I2S_IF; i++) {
+		if (drvdata->devs[i]) {
+			ioh_i2s_reset(drvdata->devs[i]);
+			ioh_i2s_destroy(drvdata->devs[i]);
+		}
+	}
+	pci_iounmap(pdev, drvdata->membase);
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+	kfree(drvdata);
+}
+
+static int ioh_i2s_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	BUG_ON(1);
+	return -EINVAL;
+}
+
+static int ioh_i2s_pci_resume(struct pci_dev *pdev)
+{
+	BUG_ON(1);
+	return -EINVAL;
+}
+
+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_LICENSE("GPL v2");
+MODULE_AUTHOR("OKI SEMICONDUCTOR ML7213 IOH");
+MODULE_DESCRIPTION("OKI SEMICONDUCTOR ML7213 IOH I2S driver");
+MODULE_DEVICE_TABLE(pci, ioh_pci_tbl);
+MODULE_VERSION("1.0");
diff --git a/sound/drivers/ioh_i2s.h b/sound/drivers/ioh_i2s.h
new file mode 100644
index 0000000..f652cef
--- /dev/null
+++ b/sound/drivers/ioh_i2s.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 OKI 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 USE_CHANNELS_MIN	1
+#define USE_CHANNELS_MAX	2
+#define I2S_1PERIODS_BYTES	2	/* 1 period byte suze (2 means 16bit) */
+
+#define MAX_I2S_RX_CH	6
+#define MAX_I2S_TX_CH	6
+
+struct ioh_i2s_data;
+
+
+enum ioh_direction {
+	IOH_PLAYBACK = 0,
+	IOH_CAPTURE,
+};
+
+struct ioh_i2s_data *ioh_i2s_open(int id, enum ioh_direction dir,
+				     const char *name, void *cbd,
+				     void (*cb)(void *cbd, int status));
+void ioh_i2s_release(struct ioh_i2s_data *priv, enum ioh_direction dir);
+void ioh_i2s_ignore_rx_overrun(struct ioh_i2s_data *priv);
+
+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;
+};
+
+void ioh_i2s_configure_i2s_regs(struct ioh_i2s_data *priv,
+				      unsigned long bitrate,
+				      struct ioh_i2s_config_reg *config,
+				      enum ioh_direction dir);
+
+void ioh_i2s_write(struct ioh_i2s_data *priv,
+		       const void *data,
+		       int len,
+		       void *callback_data,
+		       void (*done) (void *callback_data, int status));
+
+void ioh_i2s_read(struct ioh_i2s_data *priv,
+		      void *data,
+		      int len,
+		      void *callback_data,
+		      void (*done) (void *callback_data, int status));
+
+#endif
diff --git a/sound/drivers/ml7213-ioh.c b/sound/drivers/ml7213-ioh.c
new file mode 100644
index 0000000..9ffb420
--- /dev/null
+++ b/sound/drivers/ml7213-ioh.c
@@ -0,0 +1,985 @@
+/*
+ * Copyright (c) 2010-2011 by Wind River
+ * Copyright (C) 2011 OKI SEMICONDUCTOR CO., LTD.
+ *
+ * This code was derived from the Wind River MSP/I2S audio capture for STA2X11.
+ *
+ * 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/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+
+#include "ioh_i2s.h"
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("OKI SEMICONDUCTOR ML7213 IOH");
+MODULE_DESCRIPTION("OKI SEMICONDUCTOR ML7213 IOH I2S driver");
+MODULE_SUPPORTED_DEVICE("{{ALSA,ml7213i2s sound card}}");
+
+#define USE_FORMATS		SNDRV_PCM_FMTBIT_S16_LE
+#define USE_RATE		(SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_32000|\
+				SNDRV_PCM_RATE_48000)
+#define USE_RATE_MIN		16000
+#define USE_RATE_MAX		48000
+#define MAX_BUFFER_SIZE		(MAX_PERIOD_SIZE*USE_PERIODS_MAX)
+#define MIN_PERIOD_SIZE		64
+#define MAX_PERIOD_SIZE		(I2S_AFULL_THRESH*2) /* 16bit(=1word=2Byte)
+							FIFOsize=128word */
+
+#define USE_PERIODS_MIN		2
+#define USE_PERIODS_MAX		64
+
+#ifndef add_capture_constraints
+#define add_capture_constraints(x) 0
+#endif
+
+/* Direction configuration master(=1) or slave(=0) */
+#define I2S_WRITE_MASTER	1
+#define I2S_READ_MASTER		(I2S_WRITE_MASTER)
+
+/* RW flag */
+#define	SND_CAPTURE_SUBSTREAM	0
+#define	SND_PLAYBACK_SUBSTREAM	1
+
+/* Codec Device Address */
+#define	CODEC_DEV_ADDR		(0x1A)
+
+static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
+static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */
+static struct i2c_client *i2c;
+static struct i2c_board_info ioh_hwmon_info[] = {
+	{I2C_BOARD_INFO("ioh_i2c-0", CODEC_DEV_ADDR+0)},
+	{I2C_BOARD_INFO("ioh_i2c-1", CODEC_DEV_ADDR+0)},
+	{I2C_BOARD_INFO("ioh_i2c-2", CODEC_DEV_ADDR+0)},
+	{I2C_BOARD_INFO("ioh_i2c-3", CODEC_DEV_ADDR+0)},
+	{I2C_BOARD_INFO("ioh_i2c-4", CODEC_DEV_ADDR+0)},
+	{I2C_BOARD_INFO("ioh_i2c-5", CODEC_DEV_ADDR+0)},
+};
+
+static void setup_i2s_read(struct snd_pcm_substream *substream, int ch);
+static void setup_i2s_write(struct snd_pcm_substream *substream, int ch);
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for ml7213i2s sound card.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for ml7213i2s sound card.");
+
+static int ignore_overrun = 1;
+module_param(ignore_overrun, int, 0444);
+MODULE_PARM_DESC(ignore_overrun, "ignore RX overruns (default=0)");
+
+static struct platform_device *_device;
+static struct mutex i2c_mutex;
+
+struct snd_ml7213i2s {
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+};
+
+struct cbdata {
+	struct ioh_i2s_data *priv;
+	int stop;
+	int cnt;
+};
+
+static int errors;
+
+struct snd_ml7213i2s_pcm {
+	struct snd_ml7213i2s *ml7213i2s;
+	spinlock_t lock;
+	unsigned int irq_pos;
+	unsigned int buf_pos;
+	struct snd_pcm_substream *substream;
+	struct cbdata cbd;              /* i2s callback info */
+	unsigned int channels;
+	unsigned int rw;
+	unsigned int rate;
+};
+
+
+static void i2s_read_period(struct snd_pcm_substream *substream);
+static void i2s_write_period(struct snd_pcm_substream *substream);
+static void read_done(void *cbd, int status);
+static void write_done(void *cbd, int status);
+
+static inline void
+snd_card_ml7213i2s_pcm_i2s_capture_start(struct snd_pcm_substream *substream)
+{
+	i2s_read_period(substream);
+}
+
+static inline void
+snd_card_ml7213i2s_pcm_i2s_playback_start(struct snd_pcm_substream *substream)
+{
+	i2s_write_period(substream);
+}
+
+static int snd_card_ml7213i2s_pcm_capture_trigger
+				(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ml7213i2s_pcm *dpcm = runtime->private_data;
+	int err = 0;
+
+	spin_lock(&dpcm->lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		dpcm->cbd.stop = 0;
+		snd_card_ml7213i2s_pcm_i2s_capture_start(substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		pr_debug("stop..\n");
+		if (!dpcm->cbd.stop)
+			dpcm->cbd.stop = 1;
+		else
+			pr_debug("already stopped %d\n", dpcm->cbd.stop);
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+	spin_unlock(&dpcm->lock);
+	return 0;
+}
+
+
+static int snd_card_ml7213i2s_pcm_playback_trigger
+				(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ml7213i2s_pcm *dpcm = runtime->private_data;
+	int err = 0;
+
+	spin_lock(&dpcm->lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		dpcm->cbd.stop = 0;
+		snd_card_ml7213i2s_pcm_i2s_playback_start(substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		pr_debug("stop..\n");
+		if (!dpcm->cbd.stop)
+			dpcm->cbd.stop = 1;
+		else
+			pr_debug("already stopped %d\n", dpcm->cbd.stop);
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+	spin_unlock(&dpcm->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_pcm *dpcm = runtime->private_data;
+
+	dpcm->irq_pos = 0;
+	dpcm->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_pcm *dpcm = runtime->private_data;
+
+	return substream->runtime->period_size*dpcm->buf_pos;
+}
+
+static struct snd_pcm_hardware snd_card_ml7213i2s_capture = {
+	.info =			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		USE_FORMATS,
+	.rates =		USE_RATE,
+	.rate_min =		USE_RATE_MIN,
+	.rate_max =		USE_RATE_MAX,
+	.channels_min =		USE_CHANNELS_MIN,
+	.channels_max =		USE_CHANNELS_MAX,
+	.buffer_bytes_max =	MAX_BUFFER_SIZE,
+	.period_bytes_min =	MIN_PERIOD_SIZE,
+	.period_bytes_max =	MAX_PERIOD_SIZE,
+	.periods_min =		USE_PERIODS_MIN,
+	.periods_max =		USE_PERIODS_MAX,
+	.fifo_size =		0,
+};
+
+static void __snd_card_ml7213i2s_runtime_free(struct snd_pcm_runtime *runtime)
+{
+	struct snd_ml7213i2s_pcm *dpcm;
+	static int cnt;
+
+	dpcm = (struct snd_ml7213i2s_pcm *)runtime->private_data;
+	/* FIXME: This is just a big ball of race right now...
+	 */
+	if (!dpcm->cbd.stop)
+		dpcm->cbd.stop = 1;
+	else {
+		while (dpcm->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_playback_free
+					(struct snd_pcm_runtime *runtime)
+{
+	struct snd_ml7213i2s_pcm *dpcm;
+	dpcm = (struct snd_ml7213i2s_pcm *)runtime->private_data;
+
+	__snd_card_ml7213i2s_runtime_free(runtime);
+
+	ioh_i2s_release(dpcm->cbd.priv, IOH_PLAYBACK);
+	kfree(runtime->private_data);
+}
+
+static void snd_card_ml7213i2s_runtime_capture_free
+					(struct snd_pcm_runtime *runtime)
+{
+	struct snd_ml7213i2s_pcm *dpcm;
+	dpcm = (struct snd_ml7213i2s_pcm *)runtime->private_data;
+
+	__snd_card_ml7213i2s_runtime_free(runtime);
+
+	ioh_i2s_release(dpcm->cbd.priv, IOH_CAPTURE);
+	kfree(runtime->private_data);
+}
+
+static int snd_card_codec_reg_read(unsigned char reg, unsigned char *val)
+{
+	unsigned char data;
+
+	if (i2c_master_send(i2c, &reg, 1) != 1)
+		return -1;
+
+	if (i2c_master_recv(i2c, &data, 1) != 1)
+		return -1;
+
+	*val = data;
+	return 0;
+}
+
+static int snd_card_codec_reg_write(unsigned char reg, unsigned char val)
+{
+	unsigned char buf[2] = {(reg|1), val};
+
+	if (i2c_master_send(i2c, &buf[0], 2) != 2)
+		return -1;
+	return 0;
+}
+
+static int snd_card_codec_set(int number, unsigned int rate,
+			      unsigned int channels)
+{
+	unsigned char data;
+
+	mutex_lock(&i2c_mutex);
+
+	i2c = i2c_new_device(i2c_get_adapter(1), &ioh_hwmon_info[number]);
+	if (!i2c) {
+		mutex_unlock(&i2c_mutex);
+		return -1;
+	}
+
+	snd_card_codec_reg_read(0x30, &data);
+
+	snd_card_codec_reg_write(0x10, 0x01);	/* soft reset assert */
+	snd_card_codec_reg_write(0x10, 0x00);	/* soft reset negate */
+
+	snd_card_codec_reg_write(0x0c, 0x00);
+
+	switch (rate) {
+	case 16000:
+		snd_card_codec_reg_write(0x00, 0x03);
+		snd_card_codec_reg_write(0x02, 0x0c);
+		snd_card_codec_reg_write(0x04, 0x00);
+		snd_card_codec_reg_write(0x06, 0x20);
+		snd_card_codec_reg_write(0x08, 0x00);
+		snd_card_codec_reg_write(0x0a, 0x04);
+		break;
+	case 32000:
+		snd_card_codec_reg_write(0x00, 0x06);
+		snd_card_codec_reg_write(0x02, 0x0c);
+		snd_card_codec_reg_write(0x04, 0x00);
+		snd_card_codec_reg_write(0x06, 0x20);
+		snd_card_codec_reg_write(0x08, 0x00);
+		snd_card_codec_reg_write(0x0a, 0x04);
+		break;
+	case 48000:
+		snd_card_codec_reg_write(0x00, 0x08);
+		snd_card_codec_reg_write(0x02, 0x0c);
+		snd_card_codec_reg_write(0x04, 0x00);
+		snd_card_codec_reg_write(0x06, 0x30);
+		snd_card_codec_reg_write(0x08, 0x00);
+		snd_card_codec_reg_write(0x0a, 0x04);
+		break;
+	default:
+		pr_err("%s:this rate is no support for ml26124\n", __func__);
+		break;
+	}
+
+	snd_card_codec_reg_write(0x0c, 0x03);
+	msleep(20);
+	snd_card_codec_reg_write(0x0c, 0x0f);
+	snd_card_codec_reg_write(0x0e, 0x04);
+
+	snd_card_codec_reg_write(0x60, 0x00);
+	snd_card_codec_reg_write(0x62, 0x00);
+
+	snd_card_codec_reg_write(0x64, 0x00);	/* master/slave */
+
+	snd_card_codec_reg_write(0x20, 0x02);
+	msleep(50);
+
+	snd_card_codec_reg_write(0x20, 0x06);
+	snd_card_codec_reg_write(0x22, 0x0a);
+	snd_card_codec_reg_write(0x24, 0x02);
+	snd_card_codec_reg_write(0x26, 0x13);
+	snd_card_codec_reg_write(0x26, 0x1f);
+	snd_card_codec_reg_write(0x28, 0x02);
+
+	snd_card_codec_reg_write(0x54, 0x02);
+	snd_card_codec_reg_write(0x5a, 0x00);
+
+	snd_card_codec_reg_write(0x12, 0x01);
+	snd_card_codec_reg_write(0x12, 0x03);
+	msleep(20);
+	snd_card_codec_reg_write(0x66, 0x03);
+
+	snd_card_codec_reg_write(0x3a, 0x27);
+	snd_card_codec_reg_write(0x32, 0x20);
+
+	i2c_unregister_device(i2c);
+
+	mutex_unlock(&i2c_mutex);
+	return 0;
+}
+
+static int snd_card_codec_free(int number, unsigned int rate,
+			       unsigned int channels)
+{
+	mutex_lock(&i2c_mutex);
+
+	i2c = i2c_new_device(i2c_get_adapter(1), &ioh_hwmon_info[number]);
+	if (!i2c) {
+		mutex_unlock(&i2c_mutex);
+		return -1;
+	}
+
+	snd_card_codec_reg_write(0x10, 1);	/* soft reset assert */
+
+	i2c_unregister_device(i2c);
+	mutex_unlock(&i2c_mutex);
+	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 = runtime->private_data;
+
+	dpcm->channels = params_channels(hw_params);
+	dpcm->rate = params_rate(hw_params);
+
+
+	switch (dpcm->rw) {
+	case SND_CAPTURE_SUBSTREAM:
+		setup_i2s_read(substream, substream->number);
+		break;
+	case SND_PLAYBACK_SUBSTREAM:
+		setup_i2s_write(substream, substream->number);
+		break;
+	default:
+		return -1;
+	}
+
+	if (snd_card_codec_set(substream->number, dpcm->rate, dpcm->channels))
+		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_pcm *dpcm = substream->runtime->private_data;
+
+	if (snd_card_codec_free(substream->number, dpcm->rate, dpcm->channels))
+		return -1;
+
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static struct snd_ml7213i2s_pcm *
+new_pcm_stream(struct snd_pcm_substream *substream)
+{
+	struct snd_ml7213i2s_pcm *dpcm;
+
+	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
+	if (!dpcm)
+		return dpcm;
+	spin_lock_init(&dpcm->lock);
+	dpcm->substream = substream;
+	return dpcm;
+}
+
+static void i2s_read_period(struct snd_pcm_substream *substream)
+{
+	struct snd_ml7213i2s_pcm *dpcm;
+	struct cbdata *cbd;
+	int period;
+	void *read_ptr;
+	int read_size;
+
+	dpcm = substream->runtime->private_data;
+	cbd = &dpcm->cbd;
+
+	if (!cbd->priv)
+		return;
+
+	period = substream->runtime->period_size;
+
+	read_ptr = substream->runtime->dma_area
+	  +(snd_pcm_lib_period_bytes(substream) * dpcm->irq_pos);
+	read_size = period * I2S_1PERIODS_BYTES * (dpcm->channels);
+
+	ioh_i2s_read(cbd->priv,
+			 read_ptr,
+			 read_size,
+			 substream,
+			 read_done);
+
+	dpcm->irq_pos = (dpcm->irq_pos + 1) % substream->runtime->periods;
+}
+
+static void i2s_write_period(struct snd_pcm_substream *substream)
+{
+	struct snd_ml7213i2s_pcm *dpcm;
+	struct cbdata *cbd;
+	int period;
+	void *write_ptr;
+	int write_size;
+
+	dpcm = substream->runtime->private_data;
+	cbd = &dpcm->cbd;
+
+	if (!cbd->priv)
+		return;
+
+	period = substream->runtime->period_size;
+	write_ptr = substream->runtime->dma_area
+	  +(snd_pcm_lib_period_bytes(substream) * dpcm->irq_pos);
+	write_size = period * I2S_1PERIODS_BYTES * (dpcm->channels);
+
+	ioh_i2s_write(cbd->priv,
+			 write_ptr,
+			 write_size,
+			 substream,
+			 write_done);
+
+	dpcm->irq_pos = (dpcm->irq_pos + 1) % substream->runtime->periods;
+}
+
+static void read_done(void *callback_data, int status)
+{
+	struct snd_pcm_substream *substream =
+		(struct snd_pcm_substream *)callback_data;
+	struct snd_ml7213i2s_pcm *dpcm;
+	struct cbdata *cbd;
+	dpcm = substream->runtime->private_data;
+	cbd = &dpcm->cbd;
+	switch (status) {
+	case IOH_EOK:
+		if (!cbd->stop) {
+			i2s_read_period(substream);
+			dpcm->buf_pos = (dpcm->buf_pos + 1) %
+				substream->runtime->periods;
+			snd_pcm_period_elapsed(dpcm->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");
+		errors++;
+		cbd->stop = 2;
+		break;
+	}
+	if (cbd->stop)
+		pr_debug("stopping... %d\n", cbd->stop);
+}
+
+static void write_done(void *callback_data, int status)
+{
+	struct snd_pcm_substream *substream =
+		(struct snd_pcm_substream *)callback_data;
+	struct snd_ml7213i2s_pcm *dpcm;
+	struct cbdata *cbd;
+	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;
+	}
+	dpcm = substream->runtime->private_data;
+	cbd = &dpcm->cbd;
+
+	switch (status) {
+	case IOH_EOK:
+		if (!cbd->stop) {
+			i2s_write_period(substream);
+			dpcm->buf_pos = (dpcm->buf_pos + 1) %
+				substream->runtime->periods;
+			snd_pcm_period_elapsed(dpcm->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;
+	}
+}
+#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 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 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)
+
+static void i2s_set_default(struct ioh_i2s_config_reg *config)
+{
+	/* Set ML7213 IOH register default value */
+	config->cmn.i2simask = 0x003f003f;
+
+	config->tx.i2saftx = 0x1F;
+	config->tx.i2smsktx = 0x1F;
+	config->tx.i2sisttx = 0xC;
+
+	config->rx.i2scntrx = 0x3F;
+	config->rx.i2smskrx = 0x1F;
+	config->rx.i2sistrx = 0xC;
+}
+
+
+static int i2s_configure_rate(unsigned int rate)
+{
+	int value;
+
+	switch (rate) {
+	/* LR=12288/MCLKFS, BCLKFS=channels*format */
+	case 16000:
+		value = I2SCLKCNT_MCLKFS_768FS | I2SCLKCNT_BCLKFS_32FS;
+		break;
+	case 32000:
+		value = I2SCLKCNT_MCLKFS_384FS | I2SCLKCNT_BCLKFS_32FS;
+		break;
+	case 48000:
+		value = I2SCLKCNT_MCLKFS_256FS | I2SCLKCNT_BCLKFS_32FS;
+		break;
+	default:
+		value = I2SCLKCNT_MCLKFS_256FS | I2SCLKCNT_BCLKFS_32FS;
+		break;
+	}
+	return value;
+}
+
+static void i2s_slave_configure_reg(struct ioh_i2s_config_reg *config,
+				    unsigned int rate)
+{
+	memset(config, 0, sizeof(*config));
+
+	i2s_set_default(config);
+
+	/* Configuration */
+	config->tx.i2scnttx = I2SCNTTX_TXDABIT_16BIT;
+	config->tx.i2saetx = I2S_AEMPTY_THRESH / 2; /* Almost empty threshold */
+	config->rx.i2scntrx = I2SCNTRX_RXDABIT_16BIT;
+	config->rx.i2safrx = I2S_AFULL_THRESH / 2; /* Almost full threshold */
+	config->cmn.i2sclkcnt = i2s_configure_rate(rate);
+}
+
+static void i2s_master_configure_reg(struct ioh_i2s_config_reg *config,
+				     unsigned int rate)
+{
+	memset(config, 0, sizeof(*config));
+
+	i2s_set_default(config);
+
+	/* Configuration */
+	config->tx.i2scnttx = I2SCNTTX_TXDABIT_16BIT;
+	config->tx.i2saetx = I2S_AEMPTY_THRESH / 2; /* Almost empty threshold */
+	config->rx.i2scntrx = I2SCNTRX_RXDABIT_16BIT;
+	config->rx.i2safrx = I2S_AFULL_THRESH / 2; /* Almost full threshold */
+	config->cmn.i2sclkcnt = i2s_configure_rate(rate) | I2SCLKCNT_MSSEL;
+}
+
+static void setup_i2s_read(struct snd_pcm_substream *substream, int ch)
+{
+	struct snd_ml7213i2s_pcm *dpcm;
+	struct ioh_i2s_config_reg config;
+
+	dpcm = substream->runtime->private_data;
+
+	dpcm->cbd.priv = ioh_i2s_open(ch, IOH_CAPTURE, "radio-i2s-in",
+					 substream,
+					 read_done);
+
+	if (!dpcm->cbd.priv) {
+		pr_err("%s: Cannot open the device\n", __func__);
+		return;
+	}
+
+	if (ignore_overrun)
+		ioh_i2s_ignore_rx_overrun(dpcm->cbd.priv);
+
+	if (I2S_READ_MASTER)
+		i2s_master_configure_reg(&config, dpcm->rate);
+	else
+		i2s_slave_configure_reg(&config, dpcm->rate);
+
+	ioh_i2s_configure_i2s_regs(dpcm->cbd.priv, 0, &config, IOH_CAPTURE);
+}
+
+static void setup_i2s_write(struct snd_pcm_substream *substream, int ch)
+{
+	struct snd_ml7213i2s_pcm *dpcm;
+	struct ioh_i2s_config_reg config;
+
+	dpcm = substream->runtime->private_data;
+
+	dpcm->cbd.priv = ioh_i2s_open(ch, IOH_PLAYBACK, "radio-i2s-out",
+					 substream,
+					 write_done);
+
+	if (!dpcm->cbd.priv) {
+		pr_err("%s: Cannot open the device\n", __func__);
+		return;
+	}
+
+	if (ignore_overrun)
+		ioh_i2s_ignore_rx_overrun(dpcm->cbd.priv);
+
+	if (I2S_WRITE_MASTER)
+		i2s_master_configure_reg(&config, dpcm->rate);
+	else
+		i2s_slave_configure_reg(&config, dpcm->rate);
+
+	ioh_i2s_configure_i2s_regs(dpcm->cbd.priv, 0, &config, IOH_PLAYBACK);
+}
+
+static int snd_card_ml7213i2s_capture_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ml7213i2s_pcm *dpcm;
+	int err;
+
+	dpcm = new_pcm_stream(substream);
+	if (dpcm == NULL)
+		return -ENOMEM;
+
+	runtime->private_data = dpcm;
+	/* makes the infrastructure responsible for freeing dpcm */
+	runtime->private_free = snd_card_ml7213i2s_runtime_capture_free;
+	runtime->hw = snd_card_ml7213i2s_capture;
+
+	dpcm->rw = SND_CAPTURE_SUBSTREAM;
+
+	err = add_capture_constraints(runtime);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int snd_card_ml7213i2s_playback_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ml7213i2s_pcm *dpcm;
+	int err;
+
+	dpcm = new_pcm_stream(substream);
+	if (dpcm == NULL)
+		return -ENOMEM;
+
+	runtime->private_data = dpcm;
+	/* makes the infrastructure responsible for freeing dpcm */
+	runtime->private_free = snd_card_ml7213i2s_runtime_playback_free;
+	runtime->hw = snd_card_ml7213i2s_capture;
+
+	dpcm->rw = SND_PLAYBACK_SUBSTREAM;
+
+	err = add_capture_constraints(runtime);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int snd_card_ml7213i2s_capture_close(
+	struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static int snd_card_ml7213i2s_playback_close(
+	struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static struct snd_pcm_ops snd_card_ml7213i2s_capture_ops = {
+	.open =			snd_card_ml7213i2s_capture_open,
+	.close =		snd_card_ml7213i2s_capture_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_capture_trigger,
+	.pointer =		snd_card_ml7213i2s_pcm_pointer,
+};
+
+static struct snd_pcm_ops snd_card_ml7213i2s_playback_ops = {
+	.open =			snd_card_ml7213i2s_playback_open,
+	.close =		snd_card_ml7213i2s_playback_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_playback_trigger,
+	.pointer =		snd_card_ml7213i2s_pcm_pointer,
+};
+
+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;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+			&snd_card_ml7213i2s_capture_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			&snd_card_ml7213i2s_playback_ops);
+	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};
+
+static int __devinit snd_ml7213i2s_probe(struct platform_device *devptr)
+{
+	struct snd_card *card;
+	struct snd_ml7213i2s *ml7213i2s;
+	int err;
+	int dev = devptr->id;
+
+	err = snd_card_create(index, "ml7213i2s", THIS_MODULE,
+			      sizeof(struct snd_ml7213i2s), &card);
+
+	if (err < 0)
+		return err;
+	ml7213i2s = card->private_data;
+	ml7213i2s->card = card;
+	err = snd_card_ml7213i2s_pcm(ml7213i2s, 0);
+	if (err < 0)
+		goto __nodev;
+
+	strcpy(card->driver, "ml7213i2s");
+	strcpy(card->shortname, "ml7213i2s");
+	sprintf(card->longname, "ml7213i2s %i", dev + 1);
+
+	snd_card_set_dev(card, &devptr->dev);
+
+	snd_device_new(card, SNDRV_DEV_LOWLEVEL, ml7213i2s, &ops);
+
+	mutex_init(&i2c_mutex);
+
+	err = snd_card_register(card);
+	if (err == 0) {
+		platform_set_drvdata(devptr, card);
+		return 0;
+	}
+__nodev:
+	snd_card_free(card);
+	return err;
+}
+
+static int __devexit snd_ml7213i2s_remove(struct platform_device *devptr)
+{
+	snd_card_free(platform_get_drvdata(devptr));
+	platform_set_drvdata(devptr, NULL);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int snd_ml7213i2s_suspend(struct platform_device *pdev,
+				  pm_message_t state)
+{
+	struct snd_card *card = platform_get_drvdata(pdev);
+	struct snd_ml7213i2s *ml7213i2s = card->private_data;
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+	snd_pcm_suspend_all(ml7213i2s->pcm);
+	return 0;
+}
+
+static int snd_ml7213i2s_resume(struct platform_device *pdev)
+{
+	struct snd_card *card = platform_get_drvdata(pdev);
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+	return 0;
+}
+#endif
+
+
+#define snd_ml7213i2s_DRIVER	"snd_ml7213i2s"
+
+static struct platform_driver snd_ml7213i2s_driver = {
+	.probe		= snd_ml7213i2s_probe,
+	.remove		= __devexit_p(snd_ml7213i2s_remove),
+#ifdef CONFIG_PM
+	.suspend	= snd_ml7213i2s_suspend,
+	.resume		= snd_ml7213i2s_resume,
+#endif
+	.driver		= {
+		.name	= snd_ml7213i2s_DRIVER
+	},
+};
+
+static void snd_ml7213i2s_unregister(void)
+{
+	platform_device_unregister(_device);
+	platform_driver_unregister(&snd_ml7213i2s_driver);
+}
+
+static int __init alsa_card_ml7213i2s_init(void)
+{
+	int  err;
+	struct platform_device *device;
+
+	err = platform_driver_register(&snd_ml7213i2s_driver);
+	if (err < 0)
+		return err;
+
+	device = platform_device_register_simple(snd_ml7213i2s_DRIVER,
+						 0, NULL, 0);
+	if (IS_ERR(device))
+		return -1;
+	if (!platform_get_drvdata(device)) {
+		platform_device_unregister(device);
+		return -1;
+	}
+	_device = device;
+
+	return 0;
+}
+
+static void __exit alsa_card_ml7213i2s_exit(void)
+{
+	snd_ml7213i2s_unregister();
+}
+
+module_init(alsa_card_ml7213i2s_init)
+module_exit(alsa_card_ml7213i2s_exit)
-- 
1.7.4


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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
  2011-07-06 10:27 [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH Toshiharu Okada
@ 2011-07-06 11:06 ` Takashi Iwai
  2011-07-07  7:54   ` Toshiharu Okada
  0 siblings, 1 reply; 19+ messages in thread
From: Takashi Iwai @ 2011-07-06 11:06 UTC (permalink / raw)
  To: Toshiharu Okada
  Cc: perex, alsa-devel, linux-kernel, qi.wang, yong.y.wang,
	joel.clark, kok.howg.ewe, tomoya-linux

At Wed,  6 Jul 2011 19:27:47 +0900,
Toshiharu Okada wrote:
> 
> 
> This patch is for SoundCard driver of OKI SEMICONDUCTOR ML7213
> IOH(Input/Output Hub).
> These ML7213 IOH is companion chip for Intel Atom E6xx series.
> ML7213 IOH is for IVI(In-Vehicle Infotainment) use.
> 
> [About this driver]
> Audio Codec does not exist in ML7213 IOH.
> Therefore, this SoundCard driver controls ML26124 Audio Codec connected by
> I2S of ML7213 IOH.
> This driver consists of two modules, an ALSA sound card driver and I2S
> driver.
> An ALSA sound card driver performs control of ML26124 by I2C of ML7213 IOH.
> When another Audio Codec is connected to I2S of ML7213 IOH,
> it can respond by change of I2C control of an ALSA sound card driver.
> 
> 
> Signed-off-by: Toshiharu Okada <toshiharu-linux@dsn.okisemi.com>

Thanks for the patch.

I just took a quick glance over the code, and wonder whether this kind
of driver would fit better with ASoC framework.
Have you considered the implementation on ASoC?


thanks,

Takashi

> ---
>  sound/drivers/Kconfig      |   34 ++
>  sound/drivers/Makefile     |    8 +-
>  sound/drivers/ioh_i2s.c    | 1310 ++++++++++++++++++++++++++++++++++++++++++++
>  sound/drivers/ioh_i2s.h    |  116 ++++
>  sound/drivers/ml7213-ioh.c |  985 +++++++++++++++++++++++++++++++++
>  5 files changed, 2452 insertions(+), 1 deletions(-)
>  create mode 100644 sound/drivers/ioh_i2s.c
>  create mode 100644 sound/drivers/ioh_i2s.h
>  create mode 100644 sound/drivers/ml7213-ioh.c
> 
> diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
> index c896116..e098a91 100644
> --- a/sound/drivers/Kconfig
> +++ b/sound/drivers/Kconfig
> @@ -209,6 +209,39 @@ config SND_AC97_POWER_SAVE
>  
>  	  See Documentation/sound/alsa/powersave.txt for more details.
>  
> +config ML7213_I2S
> +	tristate "OKI SEMICONDUCTOR ML7213 IOH I2S Driver"
> +	help
> +	  This driver is OKI SEMICONDUCTOR ML7213 IOH I2S driver.
> +	  ML7213 is companion chip for Intel Atom E6xx series.
> +	  This driver is required to use ML7213 SoundCard.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called ioh_i2s.
> +
> +config ML7213_I2S_DEBUG
> +	bool "ML7213 I2S driver debug"
> +	depends on ML7213_I2S
> +	default n
> +	help
> +	  This option enables the addition of a debugging code to
> +	  the OKI SEMICONDUCTOR ML7213 IOH I2S Driver. If you are unsure, say N.
> +
> +	  To compile this driver as a debugging module, choose Y here: the module
> +	  will be called ioh_i2s.
> +
> +config SND_ML7213_I2S
> +	tristate "OKI SEMICONDUCTOR ML7213 SoundCard Driver for ML26124 Audio Codec"
> +	depends on ML7213_I2S
> +	default y
> +	help
> +	  This driver is OKI SEMICONDUCTOR ML7213 IOH SoundCard driver
> +	  who controls ML26124 Audio Codec connected by I2S of ML7213 IOH.
> +	  Control of ML26124 uses I2C of ML7213 IOH.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called snd-ml7213ioh.
> +
>  config SND_AC97_POWER_SAVE_DEFAULT
>  	int "Default time-out for AC97 power-save mode"
>  	depends on SND_AC97_POWER_SAVE
> @@ -219,4 +252,5 @@ config SND_AC97_POWER_SAVE_DEFAULT
>  
>  	  See SND_AC97_POWER_SAVE for more details.
>  
> +
>  endif	# SND_DRIVERS
> diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
> index 1a8440c..41350ea 100644
> --- a/sound/drivers/Makefile
> +++ b/sound/drivers/Makefile
> @@ -11,15 +11,21 @@ snd-portman2x4-objs := portman2x4.o
>  snd-serial-u16550-objs := serial-u16550.o
>  snd-virmidi-objs := virmidi.o
>  snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o
> +snd-ml7213ioh-objs := ml7213-ioh.o
>  
>  # Toplevel Module Dependency
>  obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
>  obj-$(CONFIG_SND_ALOOP) += snd-aloop.o
>  obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o
>  obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
> +obj-$(CONFIG_SND_ML7213_I2S) += snd-ml7213ioh.o
>  obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
>  obj-$(CONFIG_SND_MTS64) += snd-mts64.o
>  obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
>  obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o
> -
>  obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/
> +obj-$(CONFIG_ML7213_I2S) += ioh_i2s.o
> +ifeq ($(CONFIG_ML7213_I2S_DEBUG),y)
> +EXTRA_CFLAG += -DDEBUG
> +endif
> +
> diff --git a/sound/drivers/ioh_i2s.c b/sound/drivers/ioh_i2s.c
> new file mode 100644
> index 0000000..a40f6df
> --- /dev/null
> +++ b/sound/drivers/ioh_i2s.c
> @@ -0,0 +1,1310 @@
> +/*
> + * Copyright (C) 2010 OKI 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/slab.h>
> +#include <linux/module.h>
> +#include <linux/io.h>
> +#include <linux/ctype.h>
> +#include <linux/interrupt.h>
> +#include <linux/device.h>
> +
> +#include "ioh_i2s.h"
> +
> +#include <linux/dmaengine.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/scatterlist.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +
> +#include <linux/string.h>
> +#include <linux/timer.h>
> +#include <linux/workqueue.h>
> +
> +#include <linux/pci.h>
> +#include <linux/pch_dma.h>
> +
> +#define MAX_I2S_IF	6	/*I2S0 ~ I2S5*/
> +
> +#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
> +
> +/* 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;
> +	atomic_t rx_busy;
> +	atomic_t tx_busy;
> +
> +	int ignore_rx_overrun;
> +
> +	/* Transmit side DMA */
> +	atomic_t pending_tx;
> +
> +	struct ioh_dma_config *dma_config;
> +
> +	char rx_name[16];
> +	char tx_name[16];
> +
> +	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 */
> +
> +	void *rxbuf_virt;
> +	void *txbuf_virt;
> +	unsigned char *tx_tail;
> +	unsigned char *tx_head;
> +	unsigned char *tx_data_head;
> +	unsigned char *tx_complete;
> +	unsigned char *rx_tail;
> +	unsigned char *rx_head;
> +	unsigned char *rx_data_head;
> +	unsigned char *rx_complete;
> +	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;
> +
> +	spinlock_t tx_lock;
> +
> +	struct pch_dma_slave		param_tx;
> +	struct pch_dma_slave		param_rx;
> +	unsigned int			mapbase;
> +
> +	void *rx_callback_data;
> +	void (*rx_done) (void *callback_data, int status);
> +	void *tx_callback_data;
> +	void (*tx_done) (void *callback_data, int status);
> +
> +	unsigned int tx_lower_data_flag;
> +
> +	int dma_tx_unit; /* 1Byte of 2Byte or 4Byte */
> +
> +	int dma_tx_flag;	/* Now waiting tx DMA completion */
> +	int dma_rx_flag;	/* Now waiting rx DMA completion */
> +};
> +
> +static struct ioh_i2s_data devs[MAX_I2S_IF];
> +
> +/******************************************************************************
> +	HAL (Hardware Abstruction Layer)
> +*******************************************************************************/
> +static void ioh_i2s_reset(struct ioh_i2s_data *priv)
> +{
> +	int channel = priv->ch;
> +
> +	iowrite32(1 << channel, 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);
> +}
> +
> +/******************************************************************************
> +	DMA Functions
> +*******************************************************************************/
> +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_common(
> +			struct ioh_i2s_data *priv,
> +			char *chan_name,
> +			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 */
> +		priv->param_rx.width = PCH_DMA_WIDTH_2_BYTES;
> +		priv->param_rx.dma_dev = &dma_dev->dev;
> +		priv->param_rx.chan_id = priv->ch * 2 + 1; /* ch Rx=1,3,...11 */
> +		priv->param_rx.rx_reg = (dma_addr_t)((char *)priv->mapbase +\
> +					priv->ch * 0x800 +\
> +					I2SDRRXMIRROR_OFFSET);
> +		chan = dma_request_channel(mask, filter, &priv->param_rx);
> +		if (chan == NULL) {
> +			dev_err(priv->dev, "Failed dma_request_channel %s for"
> +				" I2S %s\n", chan_name, priv->rx_name);
> +			return NULL;
> +		}
> +		priv->chan_rx = chan;
> +
> +	} else if (dir == DMA_TO_DEVICE) { /* Tx */
> +		priv->param_tx.width = PCH_DMA_WIDTH_2_BYTES;
> +		priv->param_tx.dma_dev = &dma_dev->dev;
> +		priv->param_tx.chan_id = priv->ch * 2; /* DMA ch Tx=0,2,...10 */
> +		priv->param_tx.tx_reg = (dma_addr_t)((char *)priv->mapbase +\
> +					priv->ch * 0x800 +\
> +					I2SDRTXMIRROR_OFFSET);
> +		chan = dma_request_channel(mask, filter, &priv->param_tx);
> +		if (chan == NULL) {
> +			dev_err(priv->dev, "Failed dma_request_channel %s for"
> +				" I2S %s\n", chan_name, priv->tx_name);
> +			return NULL;
> +		}
> +		priv->chan_tx = chan;
> +	} else {
> +		dev_err(priv->dev, "%s:Invalid direction (%d)\n",
> +			chan_name, dir);
> +		return NULL;
> +	}
> +
> +	return chan;
> +}
> +
> +static void ioh_setup_rx_dma(struct ioh_i2s_data *priv)
> +{
> +	ioh_request_dma_channel_common(
> +		priv,
> +		priv->dma_config->rx_chan,
> +		DMA_FROM_DEVICE);
> +}
> +
> +static void ioh_setup_tx_dma(struct ioh_i2s_data *priv)
> +{
> +	ioh_request_dma_channel_common(
> +		priv,
> +		priv->dma_config->tx_chan,
> +		DMA_TO_DEVICE);
> +}
> +
> +static void i2s_dma_tx_complete(void *arg)
> +{
> +	struct ioh_i2s_data *priv = arg;
> +	struct scatterlist *sg = priv->sg_tx_cur;
> +	int i;
> +
> +	dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p sg_len=%d num=%d",
> +		__func__, priv->tx_data_head, priv->tx_complete, sg_dma_len(sg),
> +		priv->tx_num);
> +	dma_sync_sg_for_cpu(priv->dev, sg, priv->tx_num, DMA_TO_DEVICE);
> +
> +	for (i = 0; i < priv->tx_num; i++, sg++)
> +		priv->tx_complete += sg_dma_len(sg) * priv->dma_tx_unit;
> +
> +	if (priv->tx_complete > priv->tx_tail) {
> +		priv->tx_complete =\
> +			     (char *)(((u32)priv->tx_complete &  0x00000fff) |\
> +			     ((u32)priv->tx_head & 0xfffff000));
> +	}
> +
> +	dev_dbg(priv->dev, "-->data_complete=%p\n", priv->tx_complete);
> +
> +	if ((priv->tx_complete - 1) == priv->tx_data_head)
> +		ioh_i2s_disable_interrupts(priv, IOH_PLAYBACK);
> +
> +	async_tx_ack(priv->desc_tx);
> +	if (priv->tx_done)
> +		priv->tx_done(priv->tx_callback_data, IOH_EOK);
> +
> +	ioh_i2s_enable_tx_empty_ir(priv);
> +	priv->dma_tx_flag = 0;
> +}
> +
> +static void i2s_dma_rx_complete(void *arg)
> +{
> +	struct ioh_i2s_data *priv = arg;
> +	struct scatterlist *sg = priv->sg_rx_cur;
> +
> +	dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p sg_dma_len=%d\n",
> +		__func__, priv->rx_data_head, priv->rx_complete,
> +		sg_dma_len(sg));
> +
> +	priv->rx_data_head += sg_dma_len(sg) * priv->dma_tx_unit;
> +
> +	dev_dbg(priv->dev, "-->data_head=%p\n", priv->rx_data_head);
> +
> +	async_tx_ack(priv->desc_rx);
> +
> +	dma_sync_sg_for_cpu(priv->dev, sg, 1, DMA_FROM_DEVICE);
> +
> +	if (priv->rx_done)
> +		priv->rx_done(priv->rx_callback_data, IOH_EOK);
> +
> +	ioh_i2s_enable_rx_full_ir(priv);
> +	priv->dma_rx_flag = 0;
> +}
> +
> +/******************************************************************************
> +	Intrrupt Functions
> +*******************************************************************************/
> +void i2s_tx_almost_empty_ir(struct ioh_i2s_data *priv, int ch)
> +{
> +	struct dma_async_tx_descriptor *desc;
> +	int num;
> +	int tx_data_index;
> +	int tx_comp_index;
> +	struct scatterlist *sg = priv->sg_tx_p;
> +
> +	dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p\n", __func__,
> +		priv->tx_data_head, priv->tx_complete);
> +
> +	tx_data_index = (((int)priv->tx_data_head) & 0xfff) /\
> +			(I2S_AEMPTY_THRESH * I2S_1PERIODS_BYTES);
> +	tx_comp_index = (((int)priv->tx_complete) & 0xfff) /\
> +			(I2S_AEMPTY_THRESH * I2S_1PERIODS_BYTES);
> +
> +	if (tx_data_index > tx_comp_index)
> +		num = tx_data_index - tx_comp_index - 1;
> +	else if (tx_comp_index == (priv->tx_nent - 1))
> +		num = tx_data_index;
> +	else
> +		num = priv->tx_nent - tx_comp_index - 1;
> +
> +	dev_dbg(priv->dev, "%s: head_index=%d complete_index=%d num=%d\n",
> +		__func__, tx_data_index, tx_comp_index, num);
> +
> +	if (num < 1) { /* there is no data */
> +		priv->tx_lower_data_flag += 1;
> +		if (priv->tx_lower_data_flag >= 10) {
> +			priv->tx_lower_data_flag = 0;
> +			priv->tx_data_head = priv->tx_head;
> +			priv->tx_complete = priv->tx_tail;
> +			ioh_i2s_disable_interrupts(priv, IOH_PLAYBACK);
> +			ioh_i2s_disable_tx_empty_ir(priv);
> +			dev_dbg(priv->dev, "%s:No data to send\n", __func__);
> +		}
> +		return; /* No data to transmit */
> +	}
> +
> +	priv->tx_lower_data_flag = 0;
> +
> +	tx_comp_index = (tx_comp_index + 1) % (priv->tx_nent);
> +	sg = sg + tx_comp_index; /* Point head of sg must be sent */
> +	priv->sg_tx_cur = sg; /* Save tx condition */
> +	priv->tx_num = num; /* Save tx condition */
> +
> +	dev_dbg(priv->dev, "%s: sg = sg + %d tx_nent=%d\n",
> +		__func__, tx_comp_index, priv->tx_nent);
> +
> +	desc = priv->chan_tx->device->device_prep_slave_sg(priv->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);
> +	priv->dma_tx_flag = 1;
> +
> +	dma_sync_sg_for_device(priv->dev, sg, num, DMA_TO_DEVICE);
> +	priv->desc_tx = desc;
> +	desc->callback = i2s_dma_tx_complete;
> +	desc->callback_param = priv;
> +
> +	atomic_inc(&priv->pending_tx);
> +	desc->tx_submit(desc);
> +
> +	dma_async_issue_pending(priv->chan_tx);
> +}
> +
> +void i2s_rx_almost_full_ir(struct ioh_i2s_data *priv)
> +{
> +	struct dma_async_tx_descriptor *desc;
> +	struct scatterlist *sg;
> +	int rx_data_index;
> +
> +	sg = priv->sg_rx_p;
> +	rx_data_index = (((int)priv->rx_data_head) & 0xfff) /\
> +			(I2S_AFULL_THRESH * I2S_1PERIODS_BYTES);
> +	if (rx_data_index > priv->rx_nent)
> +		rx_data_index = 0;
> +
> +	dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p data_index=%d\n",
> +		__func__, priv->rx_data_head, priv->rx_complete, rx_data_index);
> +
> +	sg += rx_data_index;
> +
> +	desc = priv->chan_rx->device->device_prep_slave_sg(priv->chan_rx,
> +			sg, 1, DMA_FROM_DEVICE,
> +			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> +	if (!desc) {
> +		dev_err(priv->dev, "%s:device_prep_slave_sg Failed\n",
> +			__func__);
> +		return;
> +	}
> +
> +	priv->sg_rx_cur = sg; /* Save rx condition */
> +	ioh_i2s_disable_rx_full_ir(priv);
> +	priv->dma_rx_flag = 1;
> +	dma_sync_sg_for_device(priv->dev, sg, 1, DMA_FROM_DEVICE);
> +
> +	priv->desc_rx = desc;
> +	desc->callback = i2s_dma_rx_complete;
> +	desc->callback_param = priv;
> +	desc->tx_submit(desc);
> +	dma_async_issue_pending(priv->chan_rx);
> +}
> +
> +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_rx(struct ioh_i2s_data *priv)
> +{
> +	unsigned int status;
> +	int channel = priv->ch;
> +	int offset = channel * 0x800;
> +
> +	if (!priv->dma_rx_flag) {
> +		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);
> +	}
> +}
> +
> +static inline void ioh_i2s_interrupt_sub_tx(struct ioh_i2s_data *priv)
> +{
> +	unsigned int status;
> +	int channel = priv->ch;
> +	int offset = channel * 0x800;
> +
> +	if (!priv->dma_tx_flag) {
> +		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, channel);
> +
> +		/*Clear the interrupt status */
> +		iowrite32(status, priv->iobase + I2SISTTX_OFFSET + offset);
> +	}
> +}
> +
> +int ioh_i2s_event(struct ioh_i2s_data *priv)
> +{
> +	u32 idisp;
> +	unsigned long flags;
> +	int ret = IRQ_NONE;
> +
> +	spin_lock_irqsave(&priv->tx_lock, flags);
> +
> +	idisp = ioread32(priv->iobase + I2SIDISP_OFFSET);
> +	if (idisp & BIT(priv->ch + 16)) {
> +		dev_dbg(priv->dev, "Rx%d interrupt occures\n", priv->ch);
> +		ioh_i2s_interrupt_sub_rx(priv);
> +		ret = IRQ_HANDLED;
> +	}
> +
> +	if (idisp & BIT(priv->ch)) {
> +		dev_dbg(priv->dev, "Tx%d interrupt occures\n", priv->ch);
> +		ioh_i2s_interrupt_sub_tx(priv);
> +		ret = IRQ_HANDLED;
> +	}
> +
> +	spin_unlock_irqrestore(&priv->tx_lock, flags);
> +
> +	return ret;
> +}
> +
> +/******************************************************************************
> +	External Functions (Called by Sourdcard driver)
> +*******************************************************************************/
> +void ioh_i2s_ignore_rx_overrun(struct ioh_i2s_data *priv)
> +{
> +	priv->ignore_rx_overrun = 1;
> +}
> +EXPORT_SYMBOL_GPL(ioh_i2s_ignore_rx_overrun);
> +
> +struct ioh_i2s_data *ioh_i2s_open(int ch, enum ioh_direction dir,
> +				     const char *name, void *cbd,
> +				     void (*cb)(void *cbd, int status))
> +{
> +	struct ioh_i2s_data *obj = NULL;
> +	struct scatterlist *sg;
> +	int rx_size;
> +	int rx_num;
> +	int tx_size;
> +	int tx_num;
> +	int i;
> +
> +	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;
> +	}
> +	obj = &devs[ch];
> +
> +	obj->ignore_rx_overrun = 0;
> +	obj->dma_tx_unit = 2;	/* Tx transmits wuth 2Byte Unit */
> +
> +	if (dir) {
> +		/* Rx configuration */
> +		if (atomic_read(&obj->rx_busy)) {
> +			dev_err(obj->dev, "rx i2s%d have already opened\n", ch);
> +			atomic_dec(&obj->rx_busy);
> +			return 0;
> +		}
> +		atomic_inc(&obj->rx_busy);
> +
> +		strcpy(obj->rx_name, name);
> +		ioh_setup_rx_dma(obj);
> +		if (!obj->chan_rx) {
> +			dev_err(obj->dev, "%s:ioh_setup_rx_dma failed\n",
> +				__func__);
> +			return NULL;
> +		}
> +
> +		obj->rxbuf_virt = dma_alloc_coherent(obj->dev, PAGE_SIZE,
> +						&obj->rx_buf_dma, GFP_KERNEL);
> +		if (!obj->rxbuf_virt) {
> +			dev_err(obj->dev, "dma_alloc_coherent Failed\n");
> +			return NULL;
> +		}
> +
> +		rx_size = I2S_AFULL_THRESH * I2S_1PERIODS_BYTES;
> +
> +		/* The number of scatter list (Franction area is not used) */
> +		rx_num = PAGE_SIZE / rx_size;
> +
> +		dev_dbg(obj->dev, "%s: rx: scatter_num=%d scatter_size=%d\n",
> +			__func__, rx_num, rx_size);
> +
> +		obj->sg_rx_p =\
> +		       kzalloc(sizeof(struct scatterlist) *rx_num, GFP_ATOMIC);
> +
> +		sg = obj->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(obj->rxbuf_virt), rx_size,
> +				    rx_size * i);
> +			sg_dma_len(sg) = rx_size / obj->dma_tx_unit;
> +			sg_dma_address(sg) = obj->rx_buf_dma + sg->offset;
> +		}
> +
> +		obj->rx_head = (unsigned char *)obj->rxbuf_virt;
> +		obj->rx_tail = (unsigned char *)obj->rxbuf_virt +\
> +				rx_num * rx_size - 1;
> +		obj->rx_data_head = (unsigned char *)obj->rxbuf_virt;
> +		obj->rx_complete = (unsigned char *)obj->rx_tail;
> +
> +		obj->rx_nent = rx_num;
> +	} else {
> +		/* Tx configuration */
> +		if (atomic_read(&obj->tx_busy)) {
> +			dev_err(obj->dev, "tx i2s%d have already opened\n", ch);
> +			atomic_dec(&obj->tx_busy);
> +			return 0;
> +		}
> +		atomic_inc(&obj->tx_busy);
> +
> +		strcpy(obj->tx_name, name);
> +		ioh_setup_tx_dma(obj);
> +		if (!obj->chan_tx) {
> +			dev_err(obj->dev, "%s:ioh_setup_tx_dma failed\n",
> +				__func__);
> +			return NULL;
> +		}
> +
> +		obj->txbuf_virt = dma_alloc_coherent(obj->dev, PAGE_SIZE,
> +						&obj->tx_buf_dma, GFP_KERNEL);
> +
> +		if (!obj->txbuf_virt) {
> +			dev_err(obj->dev, "dma_alloc_coherent Failed\n");
> +			return NULL;
> +		}
> +
> +		obj->tx_head = (unsigned char *)obj->txbuf_virt;
> +		obj->tx_tail = (unsigned char *)obj->txbuf_virt + PAGE_SIZE - 1;
> +		obj->tx_data_head = (unsigned char *)obj->txbuf_virt;
> +		obj->tx_complete = (unsigned char *)obj->tx_tail;
> +
> +		tx_size = I2S_AEMPTY_THRESH * I2S_1PERIODS_BYTES;
> +		if (PAGE_SIZE % tx_size)
> +			/* tx_num = The number of scatter list */
> +			tx_num = PAGE_SIZE / tx_size + 1;
> +		else
> +			tx_num = PAGE_SIZE / tx_size;
> +
> +		dev_dbg(obj->dev, "%s: tx: scatter_num=%d scatter_size=%d\n",
> +			__func__, tx_num, tx_size);
> +
> +		obj->sg_tx_p =\
> +		       kzalloc(sizeof(struct scatterlist) *tx_num, GFP_ATOMIC);
> +
> +		sg_init_table(obj->sg_tx_p, tx_num); /* Initialize SG table */
> +		sg = obj->sg_tx_p;
> +
> +		for (i = 0; i < tx_num; i++, sg++) {
> +			if (i == (tx_num - 1)) {
> +				if (PAGE_SIZE % tx_size) {
> +					sg_set_page(sg,
> +						  virt_to_page(obj->txbuf_virt),
> +						  PAGE_SIZE % tx_size,
> +						  tx_size * i);
> +					sg_dma_len(sg) = (PAGE_SIZE % tx_size)\
> +							/ obj->dma_tx_unit;
> +				} else {
> +					sg_set_page(sg,
> +						  virt_to_page(obj->txbuf_virt),
> +						  tx_size, tx_size * i);
> +					sg_dma_len(sg) = tx_size\
> +							/ obj->dma_tx_unit;
> +				}
> +			} else {
> +				sg_set_page(sg, virt_to_page(obj->txbuf_virt),
> +					    tx_size, tx_size * i);
> +				sg_dma_len(sg) = tx_size / obj->dma_tx_unit;
> +			}
> +			sg_dma_address(sg) = obj->tx_buf_dma + sg->offset;
> +		}
> +		obj->tx_nent = tx_num;
> +	}
> +
> +	return obj;
> +}
> +EXPORT_SYMBOL_GPL(ioh_i2s_open);
> +
> +void ioh_i2s_release(struct ioh_i2s_data *priv, enum ioh_direction dir)
> +{
> +	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);
> +		if (priv->chan_rx) {
> +			priv->chan_rx->device->device_control(priv->chan_rx,
> +							     DMA_TERMINATE_ALL,
> +							     0);
> +			dma_release_channel(priv->chan_rx);
> +			priv->chan_rx = NULL;
> +		}
> +
> +		kfree(priv->sg_rx_p);
> +		if (priv->rxbuf_virt)
> +			dma_free_coherent(priv->dev, PAGE_SIZE,
> +					  priv->rxbuf_virt,
> +					  priv->rx_buf_dma);
> +
> +		priv->rxbuf_virt = NULL;
> +		priv->rx_buf_dma = 0;
> +		atomic_dec(&priv->rx_busy);
> +
> +	} else {
> +		ioh_i2s_disable_interrupts(priv, IOH_PLAYBACK);
> +		ioh_i2s_disable_tx_empty_ir(priv);
> +		if (priv->chan_tx) {
> +			priv->chan_tx->device->device_control(priv->chan_tx,
> +							     DMA_TERMINATE_ALL,
> +							     0);
> +			dma_release_channel(priv->chan_tx);
> +			priv->chan_tx = NULL;
> +		}
> +
> +		kfree(priv->sg_tx_p);
> +		if (priv->txbuf_virt)
> +			dma_free_coherent(priv->dev, PAGE_SIZE,
> +					  priv->txbuf_virt,
> +					  priv->tx_buf_dma);
> +
> +		priv->txbuf_virt = NULL;
> +		priv->tx_buf_dma = 0;
> +		atomic_dec(&priv->tx_busy);
> +	}
> +}
> +EXPORT_SYMBOL_GPL(ioh_i2s_release);
> +
> +
> +void ioh_i2s_configure_i2s_regs(struct ioh_i2s_data *priv,
> +				    unsigned long bitrate,
> +				    struct ioh_i2s_config_reg *config,
> +				    enum ioh_direction dir)
> +{
> +	int ch = priv->ch;
> +	int offset = ch * 0x800;
> +
> +	/* Common register */
> +	iowrite32(config->cmn.i2sclkcnt,
> +		  priv->iobase + I2SCLKCNT0_OFFSET + 0x10*ch);
> +
> +	if (dir) {
> +		/* 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 {
> +		/* 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);
> +	}
> +}
> +EXPORT_SYMBOL_GPL(ioh_i2s_configure_i2s_regs);
> +
> +void ioh_i2s_write(struct ioh_i2s_data *priv,
> +		       const void *data,
> +		       int len,
> +		       void *callback_data,
> +		       void (*done) (void *callback_data, int status))
> +{
> +	int rem1;
> +	int rem2;
> +
> +	priv->tx_callback_data = callback_data;
> +	priv->tx_done = done;
> +
> +	dev_dbg(priv->dev, "%s: [ch%d] len=%d data_head=%p data_complete=%p",
> +		__func__, priv->ch, len, priv->tx_data_head, priv->tx_complete);
> +
> +	if (priv->tx_data_head > priv->tx_tail)
> +		priv->tx_data_head = priv->tx_head;
> +
> +	if ((priv->tx_data_head + len - 1) <= priv->tx_tail) {
> +		memcpy(priv->tx_data_head, data, len);
> +		priv->tx_data_head += len;
> +	} else {
> +		rem1 = priv->tx_tail - priv->tx_data_head + 1;
> +		rem2 = len - rem1;
> +		memcpy(priv->tx_data_head, data, rem1);
> +		priv->tx_data_head = priv->tx_head;
> +		memcpy(priv->tx_data_head, data + rem1, rem2);
> +		priv->tx_data_head += rem2;
> +	}
> +	dev_dbg(priv->dev, "-->data_head=%p\n", priv->tx_data_head);
> +
> +	ioh_i2s_clear_tx_sts_ir(priv);
> +	ioh_i2s_tx_clear_dma_mask(priv);
> +	ioh_i2s_enable_tx_empty_ir(priv);
> +	ioh_i2s_enable_interrupts(priv, IOH_PLAYBACK);
> +}
> +EXPORT_SYMBOL_GPL(ioh_i2s_write);
> +
> +void ioh_i2s_read(struct ioh_i2s_data *priv,
> +		      void *data,
> +		      int len,
> +		      void *callback_data,
> +		      void (*done) (void *callback_data, int status))
> +{
> +	int rx_ready_bytes;
> +	unsigned int rem1 = 0, rem2 = 0;
> +	char *rem;
> +	char *copy_head;
> +
> +	priv->rx_callback_data = callback_data;
> +	priv->rx_done = done;
> +
> +	ioh_i2s_clear_rx_sts_ir(priv);
> +	ioh_i2s_rx_clear_dma_mask(priv);
> +	ioh_i2s_enable_rx_full_ir(priv);
> +	ioh_i2s_enable_interrupts(priv, IOH_CAPTURE);
> +
> +	dev_dbg(priv->dev, "%s: [ch%d]data_head=%p data_complete=%p len=%d\n",
> +		__func__, priv->ch, priv->rx_data_head, priv->rx_complete, len);
> +
> +	if (((u32)priv->rx_data_head & 0xffff) ==\
> +				      ((u32)(priv->rx_complete + 1) & 0xffff)) {
> +		dev_dbg(priv->dev, "%s:No data to read\n", __func__);
> +		return;
> +	}
> +
> +	if (((u32)(priv->rx_complete + 1) & 0xfff) ==\
> +					       (((u32)priv->rx_head) & 0xfff)) {
> +		rx_ready_bytes = priv->rx_data_head - priv->rx_head;
> +	} else if (priv->rx_complete < priv->rx_data_head) {
> +		rx_ready_bytes = priv->rx_data_head - priv->rx_complete - 1;
> +	} else {
> +		rem1 = priv->rx_tail - priv->rx_complete;
> +		if (rem1 < len)
> +			rem2 = priv->rx_data_head - priv->rx_head;
> +
> +		dev_dbg(priv->dev, "%s:rem1=%d rem2=%d\n",
> +			__func__, rem1, rem2);
> +		rx_ready_bytes = rem1 + rem2;
> +	}
> +
> +	dev_dbg(priv->dev,
> +		"%s:rx_ready_bytes=%d(%d, %d) head_index=%d com_index=%d\n",
> +		__func__, rx_ready_bytes, rem1, rem2,
> +		(((int)priv->rx_data_head) & 0xfff) /\
> +					(I2S_AFULL_THRESH * I2S_1PERIODS_BYTES),
> +		(((int)priv->rx_complete) & 0xfff) /\
> +				       (I2S_AFULL_THRESH * I2S_1PERIODS_BYTES));
> +
> +	if (priv->rx_data_head > priv->rx_tail)
> +		priv->rx_data_head = priv->rx_head;
> +
> +	if (len > rx_ready_bytes) {
> +		dev_dbg(priv->dev, "%s:Not enough data in bufffer\n", __func__);
> +		return;
> +	}
> +
> +	if (rem2) {
> +		memcpy(data, priv->rx_complete + 1, rem1);
> +
> +		rem = (char *)data + rem1;
> +		memcpy(rem, priv->rx_head, len - rem1);
> +		priv->rx_complete = priv->rx_head + len - rem1 - 1;
> +	} else {
> +		copy_head = (char *)(((u32)(priv->rx_complete + 1) & 0xfff) |\
> +			    ((u32)priv->rx_head & 0xfffff000));
> +		memcpy(data, copy_head, len);
> +		priv->rx_complete += len;
> +	}
> +
> +	priv->rx_complete = (char *)((((u32)priv->rx_complete) & 0x00000fff) |\
> +				    (((u32)priv->rx_head) & 0xfffff000));
> +
> +	dev_dbg(priv->dev, "com_index=%d data_complete=%p\n",
> +		(((int)priv->rx_complete) & 0xfff) /\
> +		(I2S_AFULL_THRESH * I2S_1PERIODS_BYTES),
> +		priv->rx_complete);
> +
> +}
> +EXPORT_SYMBOL_GPL(ioh_i2s_read);
> +
> +/******************************************************************************
> +	PCI Functions
> +*******************************************************************************/
> +struct ioh_i2s_data *ioh_i2s_create(struct device *dev, void *iobase,
> +				       unsigned int mapbase, int ch)
> +{
> +	struct ioh_i2s_data *obj;
> +
> +	dev_dbg(dev, "Instantiate an i2s instance with io at v0x%p.\n", iobase);
> +
> +	obj = &devs[ch];
> +	obj->iobase = iobase;
> +	obj->mapbase = mapbase;
> +	obj->ch = ch;
> +	obj->dev = dev;
> +
> +	atomic_set(&obj->rx_busy, 0);
> +	atomic_set(&obj->tx_busy, 0);
> +	strcpy(obj->rx_name, "FREE");
> +	strcpy(obj->tx_name, "FREE");
> +
> +	return obj;
> +}
> +
> +void ioh_i2s_destroy(struct ioh_i2s_data *priv)
> +{
> +}
> +
> +#define DRV_NAME "ml7213_ioh_i2s"
> +#define PCI_VENDOR_ID_ROHM	0X10DB
> +#define PCI_DEVICE_ID_ML7213_I2S	0X8033
> +
> +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,}
> +};
> +
> +struct ioh_i2s_data_pci {
> +	struct ioh_i2s_data *devs[MAX_I2S_IF];
> +	void __iomem *membase;
> +	unsigned int mapbase;
> +};
> +
> +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 handled;
> +
> +	do {
> +		int i;
> +
> +		handled = 0;
> +		for (i = 0; i < MAX_I2S_IF; i++)
> +			handled |= ioh_i2s_event(drvdata->devs[i]);
> +
> +	} while (handled);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int ioh_i2s_pci_probe(struct pci_dev *pdev,
> +				 const struct pci_device_id *id)
> +{
> +	int rv = 0;
> +	int i;
> +	struct ioh_i2s_data_pci *drvdata;
> +	void __iomem *tbl;
> +	unsigned int mapbase;
> +
> +	drvdata = kzalloc(sizeof(struct ioh_i2s_data_pci), 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);
> +	mapbase = pci_resource_start(pdev, 1);
> +	if (!mapbase) {
> +		rv = -ENOMEM;
> +		printk(KERN_ERR "pci_resource_start failed\n");
> +		goto out_pci_resource_start;
> +	}
> +	drvdata->membase = tbl;
> +	drvdata->mapbase = mapbase;
> +
> +	for (i = 0; i < MAX_I2S_IF; i++) {
> +		drvdata->devs[i] =
> +			    ioh_i2s_create(&pdev->dev, tbl, mapbase, i);
> +
> +		spin_lock_init(&drvdata->devs[i]->tx_lock);
> +		drvdata->devs[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_disable;
> +	}
> +
> +	return rv;
> +out_pci_resource_start:
> +	pci_iounmap(pdev, tbl);
> +out_disable:
> +	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;
> +
> +	free_irq(pdev->irq, pdev);
> +	for (i = 0; i < MAX_I2S_IF; i++) {
> +		if (drvdata->devs[i]) {
> +			ioh_i2s_reset(drvdata->devs[i]);
> +			ioh_i2s_destroy(drvdata->devs[i]);
> +		}
> +	}
> +	pci_iounmap(pdev, drvdata->membase);
> +	pci_disable_device(pdev);
> +	pci_set_drvdata(pdev, NULL);
> +	kfree(drvdata);
> +}
> +
> +static int ioh_i2s_pci_suspend(struct pci_dev *pdev, pm_message_t state)
> +{
> +	BUG_ON(1);
> +	return -EINVAL;
> +}
> +
> +static int ioh_i2s_pci_resume(struct pci_dev *pdev)
> +{
> +	BUG_ON(1);
> +	return -EINVAL;
> +}
> +
> +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_LICENSE("GPL v2");
> +MODULE_AUTHOR("OKI SEMICONDUCTOR ML7213 IOH");
> +MODULE_DESCRIPTION("OKI SEMICONDUCTOR ML7213 IOH I2S driver");
> +MODULE_DEVICE_TABLE(pci, ioh_pci_tbl);
> +MODULE_VERSION("1.0");
> diff --git a/sound/drivers/ioh_i2s.h b/sound/drivers/ioh_i2s.h
> new file mode 100644
> index 0000000..f652cef
> --- /dev/null
> +++ b/sound/drivers/ioh_i2s.h
> @@ -0,0 +1,116 @@
> +/*
> + * Copyright (C) 2010 OKI 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 USE_CHANNELS_MIN	1
> +#define USE_CHANNELS_MAX	2
> +#define I2S_1PERIODS_BYTES	2	/* 1 period byte suze (2 means 16bit) */
> +
> +#define MAX_I2S_RX_CH	6
> +#define MAX_I2S_TX_CH	6
> +
> +struct ioh_i2s_data;
> +
> +
> +enum ioh_direction {
> +	IOH_PLAYBACK = 0,
> +	IOH_CAPTURE,
> +};
> +
> +struct ioh_i2s_data *ioh_i2s_open(int id, enum ioh_direction dir,
> +				     const char *name, void *cbd,
> +				     void (*cb)(void *cbd, int status));
> +void ioh_i2s_release(struct ioh_i2s_data *priv, enum ioh_direction dir);
> +void ioh_i2s_ignore_rx_overrun(struct ioh_i2s_data *priv);
> +
> +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;
> +};
> +
> +void ioh_i2s_configure_i2s_regs(struct ioh_i2s_data *priv,
> +				      unsigned long bitrate,
> +				      struct ioh_i2s_config_reg *config,
> +				      enum ioh_direction dir);
> +
> +void ioh_i2s_write(struct ioh_i2s_data *priv,
> +		       const void *data,
> +		       int len,
> +		       void *callback_data,
> +		       void (*done) (void *callback_data, int status));
> +
> +void ioh_i2s_read(struct ioh_i2s_data *priv,
> +		      void *data,
> +		      int len,
> +		      void *callback_data,
> +		      void (*done) (void *callback_data, int status));
> +
> +#endif
> diff --git a/sound/drivers/ml7213-ioh.c b/sound/drivers/ml7213-ioh.c
> new file mode 100644
> index 0000000..9ffb420
> --- /dev/null
> +++ b/sound/drivers/ml7213-ioh.c
> @@ -0,0 +1,985 @@
> +/*
> + * Copyright (c) 2010-2011 by Wind River
> + * Copyright (C) 2011 OKI SEMICONDUCTOR CO., LTD.
> + *
> + * This code was derived from the Wind River MSP/I2S audio capture for STA2X11.
> + *
> + * 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/init.h>
> +#include <linux/err.h>
> +#include <linux/platform_device.h>
> +#include <linux/jiffies.h>
> +#include <linux/slab.h>
> +#include <linux/moduleparam.h>
> +#include <linux/i2c.h>
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/initval.h>
> +
> +#include "ioh_i2s.h"
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("OKI SEMICONDUCTOR ML7213 IOH");
> +MODULE_DESCRIPTION("OKI SEMICONDUCTOR ML7213 IOH I2S driver");
> +MODULE_SUPPORTED_DEVICE("{{ALSA,ml7213i2s sound card}}");
> +
> +#define USE_FORMATS		SNDRV_PCM_FMTBIT_S16_LE
> +#define USE_RATE		(SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_32000|\
> +				SNDRV_PCM_RATE_48000)
> +#define USE_RATE_MIN		16000
> +#define USE_RATE_MAX		48000
> +#define MAX_BUFFER_SIZE		(MAX_PERIOD_SIZE*USE_PERIODS_MAX)
> +#define MIN_PERIOD_SIZE		64
> +#define MAX_PERIOD_SIZE		(I2S_AFULL_THRESH*2) /* 16bit(=1word=2Byte)
> +							FIFOsize=128word */
> +
> +#define USE_PERIODS_MIN		2
> +#define USE_PERIODS_MAX		64
> +
> +#ifndef add_capture_constraints
> +#define add_capture_constraints(x) 0
> +#endif
> +
> +/* Direction configuration master(=1) or slave(=0) */
> +#define I2S_WRITE_MASTER	1
> +#define I2S_READ_MASTER		(I2S_WRITE_MASTER)
> +
> +/* RW flag */
> +#define	SND_CAPTURE_SUBSTREAM	0
> +#define	SND_PLAYBACK_SUBSTREAM	1
> +
> +/* Codec Device Address */
> +#define	CODEC_DEV_ADDR		(0x1A)
> +
> +static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
> +static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */
> +static struct i2c_client *i2c;
> +static struct i2c_board_info ioh_hwmon_info[] = {
> +	{I2C_BOARD_INFO("ioh_i2c-0", CODEC_DEV_ADDR+0)},
> +	{I2C_BOARD_INFO("ioh_i2c-1", CODEC_DEV_ADDR+0)},
> +	{I2C_BOARD_INFO("ioh_i2c-2", CODEC_DEV_ADDR+0)},
> +	{I2C_BOARD_INFO("ioh_i2c-3", CODEC_DEV_ADDR+0)},
> +	{I2C_BOARD_INFO("ioh_i2c-4", CODEC_DEV_ADDR+0)},
> +	{I2C_BOARD_INFO("ioh_i2c-5", CODEC_DEV_ADDR+0)},
> +};
> +
> +static void setup_i2s_read(struct snd_pcm_substream *substream, int ch);
> +static void setup_i2s_write(struct snd_pcm_substream *substream, int ch);
> +
> +module_param(index, int, 0444);
> +MODULE_PARM_DESC(index, "Index value for ml7213i2s sound card.");
> +module_param(id, charp, 0444);
> +MODULE_PARM_DESC(id, "ID string for ml7213i2s sound card.");
> +
> +static int ignore_overrun = 1;
> +module_param(ignore_overrun, int, 0444);
> +MODULE_PARM_DESC(ignore_overrun, "ignore RX overruns (default=0)");
> +
> +static struct platform_device *_device;
> +static struct mutex i2c_mutex;
> +
> +struct snd_ml7213i2s {
> +	struct snd_card *card;
> +	struct snd_pcm *pcm;
> +};
> +
> +struct cbdata {
> +	struct ioh_i2s_data *priv;
> +	int stop;
> +	int cnt;
> +};
> +
> +static int errors;
> +
> +struct snd_ml7213i2s_pcm {
> +	struct snd_ml7213i2s *ml7213i2s;
> +	spinlock_t lock;
> +	unsigned int irq_pos;
> +	unsigned int buf_pos;
> +	struct snd_pcm_substream *substream;
> +	struct cbdata cbd;              /* i2s callback info */
> +	unsigned int channels;
> +	unsigned int rw;
> +	unsigned int rate;
> +};
> +
> +
> +static void i2s_read_period(struct snd_pcm_substream *substream);
> +static void i2s_write_period(struct snd_pcm_substream *substream);
> +static void read_done(void *cbd, int status);
> +static void write_done(void *cbd, int status);
> +
> +static inline void
> +snd_card_ml7213i2s_pcm_i2s_capture_start(struct snd_pcm_substream *substream)
> +{
> +	i2s_read_period(substream);
> +}
> +
> +static inline void
> +snd_card_ml7213i2s_pcm_i2s_playback_start(struct snd_pcm_substream *substream)
> +{
> +	i2s_write_period(substream);
> +}
> +
> +static int snd_card_ml7213i2s_pcm_capture_trigger
> +				(struct snd_pcm_substream *substream, int cmd)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_ml7213i2s_pcm *dpcm = runtime->private_data;
> +	int err = 0;
> +
> +	spin_lock(&dpcm->lock);
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +	case SNDRV_PCM_TRIGGER_RESUME:
> +		dpcm->cbd.stop = 0;
> +		snd_card_ml7213i2s_pcm_i2s_capture_start(substream);
> +		break;
> +	case SNDRV_PCM_TRIGGER_STOP:
> +	case SNDRV_PCM_TRIGGER_SUSPEND:
> +		pr_debug("stop..\n");
> +		if (!dpcm->cbd.stop)
> +			dpcm->cbd.stop = 1;
> +		else
> +			pr_debug("already stopped %d\n", dpcm->cbd.stop);
> +		break;
> +	default:
> +		err = -EINVAL;
> +		break;
> +	}
> +	spin_unlock(&dpcm->lock);
> +	return 0;
> +}
> +
> +
> +static int snd_card_ml7213i2s_pcm_playback_trigger
> +				(struct snd_pcm_substream *substream, int cmd)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_ml7213i2s_pcm *dpcm = runtime->private_data;
> +	int err = 0;
> +
> +	spin_lock(&dpcm->lock);
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +	case SNDRV_PCM_TRIGGER_RESUME:
> +		dpcm->cbd.stop = 0;
> +		snd_card_ml7213i2s_pcm_i2s_playback_start(substream);
> +		break;
> +	case SNDRV_PCM_TRIGGER_STOP:
> +	case SNDRV_PCM_TRIGGER_SUSPEND:
> +		pr_debug("stop..\n");
> +		if (!dpcm->cbd.stop)
> +			dpcm->cbd.stop = 1;
> +		else
> +			pr_debug("already stopped %d\n", dpcm->cbd.stop);
> +		break;
> +	default:
> +		err = -EINVAL;
> +		break;
> +	}
> +	spin_unlock(&dpcm->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_pcm *dpcm = runtime->private_data;
> +
> +	dpcm->irq_pos = 0;
> +	dpcm->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_pcm *dpcm = runtime->private_data;
> +
> +	return substream->runtime->period_size*dpcm->buf_pos;
> +}
> +
> +static struct snd_pcm_hardware snd_card_ml7213i2s_capture = {
> +	.info =			(SNDRV_PCM_INFO_MMAP |
> +				 SNDRV_PCM_INFO_INTERLEAVED |
> +				 SNDRV_PCM_INFO_RESUME |
> +				 SNDRV_PCM_INFO_MMAP_VALID),
> +	.formats =		USE_FORMATS,
> +	.rates =		USE_RATE,
> +	.rate_min =		USE_RATE_MIN,
> +	.rate_max =		USE_RATE_MAX,
> +	.channels_min =		USE_CHANNELS_MIN,
> +	.channels_max =		USE_CHANNELS_MAX,
> +	.buffer_bytes_max =	MAX_BUFFER_SIZE,
> +	.period_bytes_min =	MIN_PERIOD_SIZE,
> +	.period_bytes_max =	MAX_PERIOD_SIZE,
> +	.periods_min =		USE_PERIODS_MIN,
> +	.periods_max =		USE_PERIODS_MAX,
> +	.fifo_size =		0,
> +};
> +
> +static void __snd_card_ml7213i2s_runtime_free(struct snd_pcm_runtime *runtime)
> +{
> +	struct snd_ml7213i2s_pcm *dpcm;
> +	static int cnt;
> +
> +	dpcm = (struct snd_ml7213i2s_pcm *)runtime->private_data;
> +	/* FIXME: This is just a big ball of race right now...
> +	 */
> +	if (!dpcm->cbd.stop)
> +		dpcm->cbd.stop = 1;
> +	else {
> +		while (dpcm->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_playback_free
> +					(struct snd_pcm_runtime *runtime)
> +{
> +	struct snd_ml7213i2s_pcm *dpcm;
> +	dpcm = (struct snd_ml7213i2s_pcm *)runtime->private_data;
> +
> +	__snd_card_ml7213i2s_runtime_free(runtime);
> +
> +	ioh_i2s_release(dpcm->cbd.priv, IOH_PLAYBACK);
> +	kfree(runtime->private_data);
> +}
> +
> +static void snd_card_ml7213i2s_runtime_capture_free
> +					(struct snd_pcm_runtime *runtime)
> +{
> +	struct snd_ml7213i2s_pcm *dpcm;
> +	dpcm = (struct snd_ml7213i2s_pcm *)runtime->private_data;
> +
> +	__snd_card_ml7213i2s_runtime_free(runtime);
> +
> +	ioh_i2s_release(dpcm->cbd.priv, IOH_CAPTURE);
> +	kfree(runtime->private_data);
> +}
> +
> +static int snd_card_codec_reg_read(unsigned char reg, unsigned char *val)
> +{
> +	unsigned char data;
> +
> +	if (i2c_master_send(i2c, &reg, 1) != 1)
> +		return -1;
> +
> +	if (i2c_master_recv(i2c, &data, 1) != 1)
> +		return -1;
> +
> +	*val = data;
> +	return 0;
> +}
> +
> +static int snd_card_codec_reg_write(unsigned char reg, unsigned char val)
> +{
> +	unsigned char buf[2] = {(reg|1), val};
> +
> +	if (i2c_master_send(i2c, &buf[0], 2) != 2)
> +		return -1;
> +	return 0;
> +}
> +
> +static int snd_card_codec_set(int number, unsigned int rate,
> +			      unsigned int channels)
> +{
> +	unsigned char data;
> +
> +	mutex_lock(&i2c_mutex);
> +
> +	i2c = i2c_new_device(i2c_get_adapter(1), &ioh_hwmon_info[number]);
> +	if (!i2c) {
> +		mutex_unlock(&i2c_mutex);
> +		return -1;
> +	}
> +
> +	snd_card_codec_reg_read(0x30, &data);
> +
> +	snd_card_codec_reg_write(0x10, 0x01);	/* soft reset assert */
> +	snd_card_codec_reg_write(0x10, 0x00);	/* soft reset negate */
> +
> +	snd_card_codec_reg_write(0x0c, 0x00);
> +
> +	switch (rate) {
> +	case 16000:
> +		snd_card_codec_reg_write(0x00, 0x03);
> +		snd_card_codec_reg_write(0x02, 0x0c);
> +		snd_card_codec_reg_write(0x04, 0x00);
> +		snd_card_codec_reg_write(0x06, 0x20);
> +		snd_card_codec_reg_write(0x08, 0x00);
> +		snd_card_codec_reg_write(0x0a, 0x04);
> +		break;
> +	case 32000:
> +		snd_card_codec_reg_write(0x00, 0x06);
> +		snd_card_codec_reg_write(0x02, 0x0c);
> +		snd_card_codec_reg_write(0x04, 0x00);
> +		snd_card_codec_reg_write(0x06, 0x20);
> +		snd_card_codec_reg_write(0x08, 0x00);
> +		snd_card_codec_reg_write(0x0a, 0x04);
> +		break;
> +	case 48000:
> +		snd_card_codec_reg_write(0x00, 0x08);
> +		snd_card_codec_reg_write(0x02, 0x0c);
> +		snd_card_codec_reg_write(0x04, 0x00);
> +		snd_card_codec_reg_write(0x06, 0x30);
> +		snd_card_codec_reg_write(0x08, 0x00);
> +		snd_card_codec_reg_write(0x0a, 0x04);
> +		break;
> +	default:
> +		pr_err("%s:this rate is no support for ml26124\n", __func__);
> +		break;
> +	}
> +
> +	snd_card_codec_reg_write(0x0c, 0x03);
> +	msleep(20);
> +	snd_card_codec_reg_write(0x0c, 0x0f);
> +	snd_card_codec_reg_write(0x0e, 0x04);
> +
> +	snd_card_codec_reg_write(0x60, 0x00);
> +	snd_card_codec_reg_write(0x62, 0x00);
> +
> +	snd_card_codec_reg_write(0x64, 0x00);	/* master/slave */
> +
> +	snd_card_codec_reg_write(0x20, 0x02);
> +	msleep(50);
> +
> +	snd_card_codec_reg_write(0x20, 0x06);
> +	snd_card_codec_reg_write(0x22, 0x0a);
> +	snd_card_codec_reg_write(0x24, 0x02);
> +	snd_card_codec_reg_write(0x26, 0x13);
> +	snd_card_codec_reg_write(0x26, 0x1f);
> +	snd_card_codec_reg_write(0x28, 0x02);
> +
> +	snd_card_codec_reg_write(0x54, 0x02);
> +	snd_card_codec_reg_write(0x5a, 0x00);
> +
> +	snd_card_codec_reg_write(0x12, 0x01);
> +	snd_card_codec_reg_write(0x12, 0x03);
> +	msleep(20);
> +	snd_card_codec_reg_write(0x66, 0x03);
> +
> +	snd_card_codec_reg_write(0x3a, 0x27);
> +	snd_card_codec_reg_write(0x32, 0x20);
> +
> +	i2c_unregister_device(i2c);
> +
> +	mutex_unlock(&i2c_mutex);
> +	return 0;
> +}
> +
> +static int snd_card_codec_free(int number, unsigned int rate,
> +			       unsigned int channels)
> +{
> +	mutex_lock(&i2c_mutex);
> +
> +	i2c = i2c_new_device(i2c_get_adapter(1), &ioh_hwmon_info[number]);
> +	if (!i2c) {
> +		mutex_unlock(&i2c_mutex);
> +		return -1;
> +	}
> +
> +	snd_card_codec_reg_write(0x10, 1);	/* soft reset assert */
> +
> +	i2c_unregister_device(i2c);
> +	mutex_unlock(&i2c_mutex);
> +	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 = runtime->private_data;
> +
> +	dpcm->channels = params_channels(hw_params);
> +	dpcm->rate = params_rate(hw_params);
> +
> +
> +	switch (dpcm->rw) {
> +	case SND_CAPTURE_SUBSTREAM:
> +		setup_i2s_read(substream, substream->number);
> +		break;
> +	case SND_PLAYBACK_SUBSTREAM:
> +		setup_i2s_write(substream, substream->number);
> +		break;
> +	default:
> +		return -1;
> +	}
> +
> +	if (snd_card_codec_set(substream->number, dpcm->rate, dpcm->channels))
> +		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_pcm *dpcm = substream->runtime->private_data;
> +
> +	if (snd_card_codec_free(substream->number, dpcm->rate, dpcm->channels))
> +		return -1;
> +
> +	return snd_pcm_lib_free_pages(substream);
> +}
> +
> +static struct snd_ml7213i2s_pcm *
> +new_pcm_stream(struct snd_pcm_substream *substream)
> +{
> +	struct snd_ml7213i2s_pcm *dpcm;
> +
> +	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
> +	if (!dpcm)
> +		return dpcm;
> +	spin_lock_init(&dpcm->lock);
> +	dpcm->substream = substream;
> +	return dpcm;
> +}
> +
> +static void i2s_read_period(struct snd_pcm_substream *substream)
> +{
> +	struct snd_ml7213i2s_pcm *dpcm;
> +	struct cbdata *cbd;
> +	int period;
> +	void *read_ptr;
> +	int read_size;
> +
> +	dpcm = substream->runtime->private_data;
> +	cbd = &dpcm->cbd;
> +
> +	if (!cbd->priv)
> +		return;
> +
> +	period = substream->runtime->period_size;
> +
> +	read_ptr = substream->runtime->dma_area
> +	  +(snd_pcm_lib_period_bytes(substream) * dpcm->irq_pos);
> +	read_size = period * I2S_1PERIODS_BYTES * (dpcm->channels);
> +
> +	ioh_i2s_read(cbd->priv,
> +			 read_ptr,
> +			 read_size,
> +			 substream,
> +			 read_done);
> +
> +	dpcm->irq_pos = (dpcm->irq_pos + 1) % substream->runtime->periods;
> +}
> +
> +static void i2s_write_period(struct snd_pcm_substream *substream)
> +{
> +	struct snd_ml7213i2s_pcm *dpcm;
> +	struct cbdata *cbd;
> +	int period;
> +	void *write_ptr;
> +	int write_size;
> +
> +	dpcm = substream->runtime->private_data;
> +	cbd = &dpcm->cbd;
> +
> +	if (!cbd->priv)
> +		return;
> +
> +	period = substream->runtime->period_size;
> +	write_ptr = substream->runtime->dma_area
> +	  +(snd_pcm_lib_period_bytes(substream) * dpcm->irq_pos);
> +	write_size = period * I2S_1PERIODS_BYTES * (dpcm->channels);
> +
> +	ioh_i2s_write(cbd->priv,
> +			 write_ptr,
> +			 write_size,
> +			 substream,
> +			 write_done);
> +
> +	dpcm->irq_pos = (dpcm->irq_pos + 1) % substream->runtime->periods;
> +}
> +
> +static void read_done(void *callback_data, int status)
> +{
> +	struct snd_pcm_substream *substream =
> +		(struct snd_pcm_substream *)callback_data;
> +	struct snd_ml7213i2s_pcm *dpcm;
> +	struct cbdata *cbd;
> +	dpcm = substream->runtime->private_data;
> +	cbd = &dpcm->cbd;
> +	switch (status) {
> +	case IOH_EOK:
> +		if (!cbd->stop) {
> +			i2s_read_period(substream);
> +			dpcm->buf_pos = (dpcm->buf_pos + 1) %
> +				substream->runtime->periods;
> +			snd_pcm_period_elapsed(dpcm->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");
> +		errors++;
> +		cbd->stop = 2;
> +		break;
> +	}
> +	if (cbd->stop)
> +		pr_debug("stopping... %d\n", cbd->stop);
> +}
> +
> +static void write_done(void *callback_data, int status)
> +{
> +	struct snd_pcm_substream *substream =
> +		(struct snd_pcm_substream *)callback_data;
> +	struct snd_ml7213i2s_pcm *dpcm;
> +	struct cbdata *cbd;
> +	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;
> +	}
> +	dpcm = substream->runtime->private_data;
> +	cbd = &dpcm->cbd;
> +
> +	switch (status) {
> +	case IOH_EOK:
> +		if (!cbd->stop) {
> +			i2s_write_period(substream);
> +			dpcm->buf_pos = (dpcm->buf_pos + 1) %
> +				substream->runtime->periods;
> +			snd_pcm_period_elapsed(dpcm->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;
> +	}
> +}
> +#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 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 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)
> +
> +static void i2s_set_default(struct ioh_i2s_config_reg *config)
> +{
> +	/* Set ML7213 IOH register default value */
> +	config->cmn.i2simask = 0x003f003f;
> +
> +	config->tx.i2saftx = 0x1F;
> +	config->tx.i2smsktx = 0x1F;
> +	config->tx.i2sisttx = 0xC;
> +
> +	config->rx.i2scntrx = 0x3F;
> +	config->rx.i2smskrx = 0x1F;
> +	config->rx.i2sistrx = 0xC;
> +}
> +
> +
> +static int i2s_configure_rate(unsigned int rate)
> +{
> +	int value;
> +
> +	switch (rate) {
> +	/* LR=12288/MCLKFS, BCLKFS=channels*format */
> +	case 16000:
> +		value = I2SCLKCNT_MCLKFS_768FS | I2SCLKCNT_BCLKFS_32FS;
> +		break;
> +	case 32000:
> +		value = I2SCLKCNT_MCLKFS_384FS | I2SCLKCNT_BCLKFS_32FS;
> +		break;
> +	case 48000:
> +		value = I2SCLKCNT_MCLKFS_256FS | I2SCLKCNT_BCLKFS_32FS;
> +		break;
> +	default:
> +		value = I2SCLKCNT_MCLKFS_256FS | I2SCLKCNT_BCLKFS_32FS;
> +		break;
> +	}
> +	return value;
> +}
> +
> +static void i2s_slave_configure_reg(struct ioh_i2s_config_reg *config,
> +				    unsigned int rate)
> +{
> +	memset(config, 0, sizeof(*config));
> +
> +	i2s_set_default(config);
> +
> +	/* Configuration */
> +	config->tx.i2scnttx = I2SCNTTX_TXDABIT_16BIT;
> +	config->tx.i2saetx = I2S_AEMPTY_THRESH / 2; /* Almost empty threshold */
> +	config->rx.i2scntrx = I2SCNTRX_RXDABIT_16BIT;
> +	config->rx.i2safrx = I2S_AFULL_THRESH / 2; /* Almost full threshold */
> +	config->cmn.i2sclkcnt = i2s_configure_rate(rate);
> +}
> +
> +static void i2s_master_configure_reg(struct ioh_i2s_config_reg *config,
> +				     unsigned int rate)
> +{
> +	memset(config, 0, sizeof(*config));
> +
> +	i2s_set_default(config);
> +
> +	/* Configuration */
> +	config->tx.i2scnttx = I2SCNTTX_TXDABIT_16BIT;
> +	config->tx.i2saetx = I2S_AEMPTY_THRESH / 2; /* Almost empty threshold */
> +	config->rx.i2scntrx = I2SCNTRX_RXDABIT_16BIT;
> +	config->rx.i2safrx = I2S_AFULL_THRESH / 2; /* Almost full threshold */
> +	config->cmn.i2sclkcnt = i2s_configure_rate(rate) | I2SCLKCNT_MSSEL;
> +}
> +
> +static void setup_i2s_read(struct snd_pcm_substream *substream, int ch)
> +{
> +	struct snd_ml7213i2s_pcm *dpcm;
> +	struct ioh_i2s_config_reg config;
> +
> +	dpcm = substream->runtime->private_data;
> +
> +	dpcm->cbd.priv = ioh_i2s_open(ch, IOH_CAPTURE, "radio-i2s-in",
> +					 substream,
> +					 read_done);
> +
> +	if (!dpcm->cbd.priv) {
> +		pr_err("%s: Cannot open the device\n", __func__);
> +		return;
> +	}
> +
> +	if (ignore_overrun)
> +		ioh_i2s_ignore_rx_overrun(dpcm->cbd.priv);
> +
> +	if (I2S_READ_MASTER)
> +		i2s_master_configure_reg(&config, dpcm->rate);
> +	else
> +		i2s_slave_configure_reg(&config, dpcm->rate);
> +
> +	ioh_i2s_configure_i2s_regs(dpcm->cbd.priv, 0, &config, IOH_CAPTURE);
> +}
> +
> +static void setup_i2s_write(struct snd_pcm_substream *substream, int ch)
> +{
> +	struct snd_ml7213i2s_pcm *dpcm;
> +	struct ioh_i2s_config_reg config;
> +
> +	dpcm = substream->runtime->private_data;
> +
> +	dpcm->cbd.priv = ioh_i2s_open(ch, IOH_PLAYBACK, "radio-i2s-out",
> +					 substream,
> +					 write_done);
> +
> +	if (!dpcm->cbd.priv) {
> +		pr_err("%s: Cannot open the device\n", __func__);
> +		return;
> +	}
> +
> +	if (ignore_overrun)
> +		ioh_i2s_ignore_rx_overrun(dpcm->cbd.priv);
> +
> +	if (I2S_WRITE_MASTER)
> +		i2s_master_configure_reg(&config, dpcm->rate);
> +	else
> +		i2s_slave_configure_reg(&config, dpcm->rate);
> +
> +	ioh_i2s_configure_i2s_regs(dpcm->cbd.priv, 0, &config, IOH_PLAYBACK);
> +}
> +
> +static int snd_card_ml7213i2s_capture_open(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_ml7213i2s_pcm *dpcm;
> +	int err;
> +
> +	dpcm = new_pcm_stream(substream);
> +	if (dpcm == NULL)
> +		return -ENOMEM;
> +
> +	runtime->private_data = dpcm;
> +	/* makes the infrastructure responsible for freeing dpcm */
> +	runtime->private_free = snd_card_ml7213i2s_runtime_capture_free;
> +	runtime->hw = snd_card_ml7213i2s_capture;
> +
> +	dpcm->rw = SND_CAPTURE_SUBSTREAM;
> +
> +	err = add_capture_constraints(runtime);
> +	if (err < 0)
> +		return err;
> +
> +	return 0;
> +}
> +
> +static int snd_card_ml7213i2s_playback_open(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_ml7213i2s_pcm *dpcm;
> +	int err;
> +
> +	dpcm = new_pcm_stream(substream);
> +	if (dpcm == NULL)
> +		return -ENOMEM;
> +
> +	runtime->private_data = dpcm;
> +	/* makes the infrastructure responsible for freeing dpcm */
> +	runtime->private_free = snd_card_ml7213i2s_runtime_playback_free;
> +	runtime->hw = snd_card_ml7213i2s_capture;
> +
> +	dpcm->rw = SND_PLAYBACK_SUBSTREAM;
> +
> +	err = add_capture_constraints(runtime);
> +	if (err < 0)
> +		return err;
> +
> +	return 0;
> +}
> +
> +static int snd_card_ml7213i2s_capture_close(
> +	struct snd_pcm_substream *substream)
> +{
> +	return 0;
> +}
> +
> +static int snd_card_ml7213i2s_playback_close(
> +	struct snd_pcm_substream *substream)
> +{
> +	return 0;
> +}
> +
> +static struct snd_pcm_ops snd_card_ml7213i2s_capture_ops = {
> +	.open =			snd_card_ml7213i2s_capture_open,
> +	.close =		snd_card_ml7213i2s_capture_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_capture_trigger,
> +	.pointer =		snd_card_ml7213i2s_pcm_pointer,
> +};
> +
> +static struct snd_pcm_ops snd_card_ml7213i2s_playback_ops = {
> +	.open =			snd_card_ml7213i2s_playback_open,
> +	.close =		snd_card_ml7213i2s_playback_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_playback_trigger,
> +	.pointer =		snd_card_ml7213i2s_pcm_pointer,
> +};
> +
> +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;
> +	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
> +			&snd_card_ml7213i2s_capture_ops);
> +	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
> +			&snd_card_ml7213i2s_playback_ops);
> +	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};
> +
> +static int __devinit snd_ml7213i2s_probe(struct platform_device *devptr)
> +{
> +	struct snd_card *card;
> +	struct snd_ml7213i2s *ml7213i2s;
> +	int err;
> +	int dev = devptr->id;
> +
> +	err = snd_card_create(index, "ml7213i2s", THIS_MODULE,
> +			      sizeof(struct snd_ml7213i2s), &card);
> +
> +	if (err < 0)
> +		return err;
> +	ml7213i2s = card->private_data;
> +	ml7213i2s->card = card;
> +	err = snd_card_ml7213i2s_pcm(ml7213i2s, 0);
> +	if (err < 0)
> +		goto __nodev;
> +
> +	strcpy(card->driver, "ml7213i2s");
> +	strcpy(card->shortname, "ml7213i2s");
> +	sprintf(card->longname, "ml7213i2s %i", dev + 1);
> +
> +	snd_card_set_dev(card, &devptr->dev);
> +
> +	snd_device_new(card, SNDRV_DEV_LOWLEVEL, ml7213i2s, &ops);
> +
> +	mutex_init(&i2c_mutex);
> +
> +	err = snd_card_register(card);
> +	if (err == 0) {
> +		platform_set_drvdata(devptr, card);
> +		return 0;
> +	}
> +__nodev:
> +	snd_card_free(card);
> +	return err;
> +}
> +
> +static int __devexit snd_ml7213i2s_remove(struct platform_device *devptr)
> +{
> +	snd_card_free(platform_get_drvdata(devptr));
> +	platform_set_drvdata(devptr, NULL);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int snd_ml7213i2s_suspend(struct platform_device *pdev,
> +				  pm_message_t state)
> +{
> +	struct snd_card *card = platform_get_drvdata(pdev);
> +	struct snd_ml7213i2s *ml7213i2s = card->private_data;
> +
> +	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
> +	snd_pcm_suspend_all(ml7213i2s->pcm);
> +	return 0;
> +}
> +
> +static int snd_ml7213i2s_resume(struct platform_device *pdev)
> +{
> +	struct snd_card *card = platform_get_drvdata(pdev);
> +
> +	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
> +	return 0;
> +}
> +#endif
> +
> +
> +#define snd_ml7213i2s_DRIVER	"snd_ml7213i2s"
> +
> +static struct platform_driver snd_ml7213i2s_driver = {
> +	.probe		= snd_ml7213i2s_probe,
> +	.remove		= __devexit_p(snd_ml7213i2s_remove),
> +#ifdef CONFIG_PM
> +	.suspend	= snd_ml7213i2s_suspend,
> +	.resume		= snd_ml7213i2s_resume,
> +#endif
> +	.driver		= {
> +		.name	= snd_ml7213i2s_DRIVER
> +	},
> +};
> +
> +static void snd_ml7213i2s_unregister(void)
> +{
> +	platform_device_unregister(_device);
> +	platform_driver_unregister(&snd_ml7213i2s_driver);
> +}
> +
> +static int __init alsa_card_ml7213i2s_init(void)
> +{
> +	int  err;
> +	struct platform_device *device;
> +
> +	err = platform_driver_register(&snd_ml7213i2s_driver);
> +	if (err < 0)
> +		return err;
> +
> +	device = platform_device_register_simple(snd_ml7213i2s_DRIVER,
> +						 0, NULL, 0);
> +	if (IS_ERR(device))
> +		return -1;
> +	if (!platform_get_drvdata(device)) {
> +		platform_device_unregister(device);
> +		return -1;
> +	}
> +	_device = device;
> +
> +	return 0;
> +}
> +
> +static void __exit alsa_card_ml7213i2s_exit(void)
> +{
> +	snd_ml7213i2s_unregister();
> +}
> +
> +module_init(alsa_card_ml7213i2s_init)
> +module_exit(alsa_card_ml7213i2s_exit)
> -- 
> 1.7.4
> 

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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
  2011-07-06 11:06 ` Takashi Iwai
@ 2011-07-07  7:54   ` Toshiharu Okada
  2011-07-07  9:00     ` Takashi Iwai
  0 siblings, 1 reply; 19+ messages in thread
From: Toshiharu Okada @ 2011-07-07  7:54 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: tomoya-linux, kok.howg.ewe, joel.clark, yong.y.wang, qi.wang,
	linux-kernel, alsa-devel, perex

Date: Wed, 06 Jul 2011 13:06:15 +0200
From: Takashi Iwai <tiwai@suse.de>
>
>I just took a quick glance over the code, and wonder whether this kind
>of driver would fit better with ASoC framework.
>Have you considered the implementation on ASoC?

Thank you for your comment.

I referred to the following files and implemented this driver.

http://www.mirrorservice.org/sites/ftp.sourceforge.net/pub/sourceforge/s/project/st/sta2x11/2.6.33/20100910/20100910-alpha-src.zip
sound/drivers/sta2x11i2s.c

Therefore, I am not considering about ASoC.
Do I need to fit this driver to an ASoC framework for an upstream?

Best Regards,
Toshiharu Okada 


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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
  2011-07-07  7:54   ` Toshiharu Okada
@ 2011-07-07  9:00     ` Takashi Iwai
  2011-07-07 12:35       ` Toshiharu Okada
  0 siblings, 1 reply; 19+ messages in thread
From: Takashi Iwai @ 2011-07-07  9:00 UTC (permalink / raw)
  To: Toshiharu Okada
  Cc: tomoya-linux, kok.howg.ewe, joel.clark, yong.y.wang, qi.wang,
	linux-kernel, alsa-devel, perex

At Thu, 7 Jul 2011 16:54:18 +0900,
Toshiharu Okada wrote:
> 
> Date: Wed, 06 Jul 2011 13:06:15 +0200
> From: Takashi Iwai <tiwai@suse.de>
> >
> >I just took a quick glance over the code, and wonder whether this kind
> >of driver would fit better with ASoC framework.
> >Have you considered the implementation on ASoC?
> 
> Thank you for your comment.
> 
> I referred to the following files and implemented this driver.
> 
> http://www.mirrorservice.org/sites/ftp.sourceforge.net/pub/sourceforge/s/project/st/sta2x11/2.6.33/20100910/20100910-alpha-src.zip
> sound/drivers/sta2x11i2s.c
> 
> Therefore, I am not considering about ASoC.
> Do I need to fit this driver to an ASoC framework for an upstream?

Yes, I guess this would be a better choice in your case.


Takashi

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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
  2011-07-07  9:00     ` Takashi Iwai
@ 2011-07-07 12:35       ` Toshiharu Okada
  2011-07-07 16:38         ` Takashi Iwai
  0 siblings, 1 reply; 19+ messages in thread
From: Toshiharu Okada @ 2011-07-07 12:35 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: perex, alsa-devel, linux-kernel, qi.wang, yong.y.wang,
	joel.clark, kok.howg.ewe, tomoya-linux

Date: Thu, 07 Jul 2011 11:00:47 +0200
From: Takashi Iwai <tiwai@suse.de>
>>
>> Therefore, I am not considering about ASoC.
>> Do I need to fit this driver to an ASoC framework for an upstream?
>
>Yes, I guess this would be a better choice in your case.

Thank you for your comment.
>From now on, We study about ASoC.
If there are a document, a website, a driver, etc. which are referred, 
please tell us.
Why is choosing ASoS better?
Is it better to correspond to ASoC, when Audio Codec is unfixed?

Best Regards
Toshiharu Okada 


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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
  2011-07-07 12:35       ` Toshiharu Okada
@ 2011-07-07 16:38         ` Takashi Iwai
  2011-07-08 10:52           ` Toshiharu Okada
  2011-07-09  1:34           ` Mark Brown
  0 siblings, 2 replies; 19+ messages in thread
From: Takashi Iwai @ 2011-07-07 16:38 UTC (permalink / raw)
  To: Toshiharu Okada
  Cc: perex, alsa-devel, linux-kernel, qi.wang, yong.y.wang,
	joel.clark, kok.howg.ewe, tomoya-linux

At Thu, 7 Jul 2011 21:35:17 +0900,
Toshiharu Okada wrote:
> 
> Date: Thu, 07 Jul 2011 11:00:47 +0200
> From: Takashi Iwai <tiwai@suse.de>
> >>
> >> Therefore, I am not considering about ASoC.
> >> Do I need to fit this driver to an ASoC framework for an upstream?
> >
> >Yes, I guess this would be a better choice in your case.
> 
> Thank you for your comment.
> >From now on, We study about ASoC.
> If there are a document, a website, a driver, etc. which are referred, 
> please tell us.

Check Documentation/sound/alsa/soc/* files.

> Why is choosing ASoS better?
> Is it better to correspond to ASoC, when Audio Codec is unfixed?

It's not clear what you mean exactly as "unfixed", but in general, the
decision rather depends on the usage of the device.  If it's designed
for a use as a desktop PC component (such as a PCI card or
onboard-audio on PC), it'd make sense to implement as a stand-alone,
self-contained driver.

OTOH, if it's targeted mainly for embedded area, ASoC is the right
answer.  It's more modularized, and can be more flexibly configured in
the end.


Takashi

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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
  2011-07-07 16:38         ` Takashi Iwai
@ 2011-07-08 10:52           ` Toshiharu Okada
  2011-07-09  1:34           ` Mark Brown
  1 sibling, 0 replies; 19+ messages in thread
From: Toshiharu Okada @ 2011-07-08 10:52 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: perex, alsa-devel, linux-kernel, qi.wang, yong.y.wang,
	joel.clark, kok.howg.ewe, tomoya-linux

Date: Thu, 07 Jul 2011 18:38:33 +0200
From: Takashi Iwai <tiwai@suse.de>
>> Thank you for your comment.
>> From now on, We study about ASoC.
>> If there are a document, a website, a driver, etc. which are referred, 
>> please tell us.
>
>Check Documentation/sound/alsa/soc/* files.
>

Thank you for your reply
First of all, we read a document.

Toshiharu Okada


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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
  2011-07-07 16:38         ` Takashi Iwai
  2011-07-08 10:52           ` Toshiharu Okada
@ 2011-07-09  1:34           ` Mark Brown
  2011-07-09  7:40             ` Takashi Iwai
  1 sibling, 1 reply; 19+ messages in thread
From: Mark Brown @ 2011-07-09  1:34 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Toshiharu Okada, perex, alsa-devel, linux-kernel, qi.wang,
	yong.y.wang, joel.clark, kok.howg.ewe, tomoya-linux

On Thu, Jul 07, 2011 at 06:38:33PM +0200, Takashi Iwai wrote:

> It's not clear what you mean exactly as "unfixed", but in general, the
> decision rather depends on the usage of the device.  If it's designed
> for a use as a desktop PC component (such as a PCI card or
> onboard-audio on PC), it'd make sense to implement as a stand-alone,
> self-contained driver.

> OTOH, if it's targeted mainly for embedded area, ASoC is the right
> answer.  It's more modularized, and can be more flexibly configured in
> the end.

I'd say it depends rather more on the physical system design.  If there
are a bunch of separate chips which are interacted with separately by
software then ASoC makes sense, if the card is one logical object to
hardware a vanilla ALSA driver probably does.

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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
  2011-07-09  1:34           ` Mark Brown
@ 2011-07-09  7:40             ` Takashi Iwai
  0 siblings, 0 replies; 19+ messages in thread
From: Takashi Iwai @ 2011-07-09  7:40 UTC (permalink / raw)
  To: Mark Brown
  Cc: Toshiharu Okada, perex, alsa-devel, linux-kernel, qi.wang,
	yong.y.wang, joel.clark, kok.howg.ewe, tomoya-linux

At Sat, 9 Jul 2011 02:34:41 +0100,
Mark Brown wrote:
> 
> On Thu, Jul 07, 2011 at 06:38:33PM +0200, Takashi Iwai wrote:
> 
> > It's not clear what you mean exactly as "unfixed", but in general, the
> > decision rather depends on the usage of the device.  If it's designed
> > for a use as a desktop PC component (such as a PCI card or
> > onboard-audio on PC), it'd make sense to implement as a stand-alone,
> > self-contained driver.
> 
> > OTOH, if it's targeted mainly for embedded area, ASoC is the right
> > answer.  It's more modularized, and can be more flexibly configured in
> > the end.
> 
> I'd say it depends rather more on the physical system design.  If there
> are a bunch of separate chips which are interacted with separately by
> software then ASoC makes sense, if the card is one logical object to
> hardware a vanilla ALSA driver probably does.

True.  And that's practically what differentiates between
PC-components and embedded devices :)


Takashi

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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
  2011-11-10  5:00               ` Tomoya MORINAGA
@ 2011-11-10 11:22                 ` Mark Brown
  0 siblings, 0 replies; 19+ messages in thread
From: Mark Brown @ 2011-11-10 11:22 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Takashi Iwai, perex, linux-kernel, Wang, Qi, Wang, Yong Y, Clark,
	Joel, Ewe, Kok Howg, Liam Girdwood, alsa-devel

On Thu, Nov 10, 2011 at 02:00:22PM +0900, Tomoya MORINAGA wrote:
> (2011/11/08 23:38), Mark Brown wrote:
> >On Tue, Nov 08, 2011 at 06:03:53PM +0900, Tomoya MORINAGA wrote:

> >>struct snd_ml7213i2s_pcm {
> >>	enum snd_soc_control_type control_type;
> >>	struct snd_ml7213i2s *ml7213i2s;
> >>	spinlock_t lock;
> >>	unsigned int irq_pos;
> >>	unsigned int buf_pos;
> >>	struct snd_pcm_substream *substream;
> >>	struct cbdata cbd;              /* i2s callback info */
> >>	unsigned int channels;
> >>	unsigned int rw;
> >>	unsigned int rate;
> >>	unsigned int ch;
> >>	unsigned int setup_flag;
> >>	unsigned int format;
> >>	unsigned int bclkfs;
> >>	struct mutex i2c_mutex;
> >>};

> >...this looks *really* confused, there's things in here which are a mix
> >of DMA controller and CODEC driver things.  The CODEC and DMA drivers
> >shouldn't know anything about each other, let alone be referencing the
> >same data structure.

> Let me clarify your saying.
> I couldn't understand what your "DMA controller" mean.
> Which "snd_ml7213i2s_pcm" structure member do you mean ?

Things like irq_pos and buf_pos for example.

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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
  2011-11-08 14:38             ` Mark Brown
@ 2011-11-10  5:00               ` Tomoya MORINAGA
  2011-11-10 11:22                 ` Mark Brown
  0 siblings, 1 reply; 19+ messages in thread
From: Tomoya MORINAGA @ 2011-11-10  5:00 UTC (permalink / raw)
  To: Mark Brown
  Cc: Takashi Iwai, perex, linux-kernel, Wang, Qi, Wang, Yong Y, Clark,
	Joel, Ewe, Kok Howg, Liam Girdwood, alsa-devel

(2011/11/08 23:38), Mark Brown wrote:
> On Tue, Nov 08, 2011 at 06:03:53PM +0900, Tomoya MORINAGA wrote:
>
> So, I started looking at this but...
>
>> struct snd_ml7213i2s_pcm {
>> 	enum snd_soc_control_type control_type;
>> 	struct snd_ml7213i2s *ml7213i2s;
>> 	spinlock_t lock;
>> 	unsigned int irq_pos;
>> 	unsigned int buf_pos;
>> 	struct snd_pcm_substream *substream;
>> 	struct cbdata cbd;              /* i2s callback info */
>> 	unsigned int channels;
>> 	unsigned int rw;
>> 	unsigned int rate;
>> 	unsigned int ch;
>> 	unsigned int setup_flag;
>> 	unsigned int format;
>> 	unsigned int bclkfs;
>> 	struct mutex i2c_mutex;
>> };
>
> ...this looks *really* confused, there's things in here which are a mix
> of DMA controller and CODEC driver things.  The CODEC and DMA drivers
> shouldn't know anything about each other, let alone be referencing the
> same data structure.

Let me clarify your saying.
I couldn't understand what your "DMA controller" mean.
Which "snd_ml7213i2s_pcm" structure member do you mean ?


-- 
tomoya
ROHM Co., Ltd.

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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
  2011-11-08 10:39             ` Mark Brown
@ 2011-11-09  2:56               ` Tomoya MORINAGA
  0 siblings, 0 replies; 19+ messages in thread
From: Tomoya MORINAGA @ 2011-11-09  2:56 UTC (permalink / raw)
  To: Mark Brown
  Cc: Takashi Iwai, perex, linux-kernel, Wang, Qi, Wang, Yong Y, Clark,
	Joel, Ewe, Kok Howg, Liam Girdwood, alsa-devel

(2011/11/08 19:39), Mark Brown wrote:
> On Tue, Nov 08, 2011 at 06:03:53PM +0900, Tomoya MORINAGA wrote:
>
>> Though roughly, we re-created 3 files codec driver, platform drive
>> and machine driver. (Not debugged/tested)
>> Could you check these files ?
>
> Please send patches for review using the standard kernel review process
> - see Documentation/SubmittingPatches.  Sending a single enormous review
> makes it much more difficult for people to review as there's so much
> code in the one mail and attachments don't work with people's normal
> workflows.
>
OK, I will post our patches.

Thanks,
-- 
tomoya
ROHM Co., Ltd.

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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
  2011-11-08  9:03           ` Tomoya MORINAGA
  2011-11-08 10:39             ` Mark Brown
@ 2011-11-08 14:38             ` Mark Brown
  2011-11-10  5:00               ` Tomoya MORINAGA
  1 sibling, 1 reply; 19+ messages in thread
From: Mark Brown @ 2011-11-08 14:38 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Takashi Iwai, perex, linux-kernel, Wang, Qi, Wang, Yong Y, Clark,
	Joel, Ewe, Kok Howg, Liam Girdwood, alsa-devel

On Tue, Nov 08, 2011 at 06:03:53PM +0900, Tomoya MORINAGA wrote:

So, I started looking at this but...

> struct snd_ml7213i2s_pcm {
> 	enum snd_soc_control_type control_type;
> 	struct snd_ml7213i2s *ml7213i2s;
> 	spinlock_t lock;
> 	unsigned int irq_pos;
> 	unsigned int buf_pos;
> 	struct snd_pcm_substream *substream;
> 	struct cbdata cbd;              /* i2s callback info */
> 	unsigned int channels;
> 	unsigned int rw;
> 	unsigned int rate;
> 	unsigned int ch;
> 	unsigned int setup_flag;
> 	unsigned int format;
> 	unsigned int bclkfs;
> 	struct mutex i2c_mutex;
> };

...this looks *really* confused, there's things in here which are a mix
of DMA controller and CODEC driver things.  The CODEC and DMA drivers
shouldn't know anything about each other, let alone be referencing the
same data structure.

> /*
>  * wm8731 register cache
>  * We can't read the WM8731 register space when we are
>  * using 2 wire for device control, so we cache them instead.
>  * There is no point in caching the reset register
>  */
> static const u16 wm8731_reg[WM8731_CACHEREGNUM] = {
> 	0x0097, 0x0097, 0x0079, 0x0079,
> 	0x000a, 0x0008, 0x009f, 0x000a,
> 	0x0000, 0x0000
> };

This is is just obviously wrong for this driver.  I stopped reading the
code at this point.

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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
  2011-11-08  9:03           ` Tomoya MORINAGA
@ 2011-11-08 10:39             ` Mark Brown
  2011-11-09  2:56               ` Tomoya MORINAGA
  2011-11-08 14:38             ` Mark Brown
  1 sibling, 1 reply; 19+ messages in thread
From: Mark Brown @ 2011-11-08 10:39 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Takashi Iwai, perex, linux-kernel, Wang, Qi, Wang, Yong Y, Clark,
	Joel, Ewe, Kok Howg, Liam Girdwood, alsa-devel

On Tue, Nov 08, 2011 at 06:03:53PM +0900, Tomoya MORINAGA wrote:

> Though roughly, we re-created 3 files codec driver, platform drive
> and machine driver. (Not debugged/tested)
> Could you check these files ?

Please send patches for review using the standard kernel review process
- see Documentation/SubmittingPatches.  Sending a single enormous review
makes it much more difficult for people to review as there's so much
code in the one mail and attachments don't work with people's normal
workflows.

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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
  2011-10-24 12:20         ` Mark Brown
@ 2011-11-08  9:03           ` Tomoya MORINAGA
  2011-11-08 10:39             ` Mark Brown
  2011-11-08 14:38             ` Mark Brown
  0 siblings, 2 replies; 19+ messages in thread
From: Tomoya MORINAGA @ 2011-11-08  9:03 UTC (permalink / raw)
  To: Mark Brown, Takashi Iwai
  Cc: perex, linux-kernel, Wang, Qi, Wang, Yong Y, Clark, Joel, Ewe,
	Kok Howg, Liam Girdwood, alsa-devel

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

(2011/10/24 21:20), Mark Brown wrote:
> On Mon, Oct 24, 2011 at 09:12:42PM +0900, Tomoya MORINAGA wrote:
>
>> 1. PCI interface function.
>> Any current ASoC drivers don't have PCI interface function.
>> So I don't know where the function should be in machine driver or
>> platform driver.
>
> It depends on what the driver is for.  Probably you want a driver which
> is some combination of machine driver and the various drivers that are
> normally part of the SoC - whatever roles in the system are filled by
> this hardware the driver ought to register subsystem drivers for those
> roles.
>
>> 2. Register Access
>> Can platform driver access register ?
>> According to the soc document, platform driver must not access hardware,
>> however, some drivers looks accessing their hardware.
>
> What makes you say this?  A driver that can't access hardware would be
> rather useless...
>
>

Though roughly, we re-created 3 files codec driver, platform drive and 
machine driver. (Not debugged/tested)
Could you check these files ?

(1)codec driver
    ml26124.c ml26124.h

(2)platform driver
    ml7213ioh-plat.c ml7213ioh-plat.h ioh_i2s_config.h ioh_i2s.h

(3)machine driver
    ml7213ioh-machine.c

Thanks in advance.

-- 
tomoya
ROHM Co., Ltd.

[-- Attachment #2: ml26124.c --]
[-- Type: text/plain, Size: 19969 bytes --]

/*
 * 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 cbdata {
	struct ioh_i2s_data *priv;
	int stop;
	int cnt;
};

struct snd_ml7213i2s_pcm {
	enum snd_soc_control_type control_type;
	struct snd_ml7213i2s *ml7213i2s;
	spinlock_t lock;
	unsigned int irq_pos;
	unsigned int buf_pos;
	struct snd_pcm_substream *substream;
	struct cbdata cbd;              /* i2s callback info */
	unsigned int channels;
	unsigned int rw;
	unsigned int rate;
	unsigned int ch;
	unsigned int setup_flag;
	unsigned int format;
	unsigned int bclkfs;
	struct mutex i2c_mutex;
};

/*
 * wm8731 register cache
 * We can't read the WM8731 register space when we are
 * using 2 wire for device control, so we cache them instead.
 * There is no point in caching the reset register
 */
static const u16 wm8731_reg[WM8731_CACHEREGNUM] = {
	0x0097, 0x0097, 0x0079, 0x0079,
	0x000a, 0x0008, 0x009f, 0x000a,
	0x0000, 0x0000
};

#define wm8731_reset(c)	snd_soc_write(c, WM8731_RESET, 0)

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 snd_ml7213i2s_pcm *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 snd_ml7213i2s_pcm *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 snd_ml7213i2s_pcm *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 snd_ml7213i2s_pcm *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 snd_ml7213i2s_pcm *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 snd_ml7213i2s_pcm *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 snd_ml7213i2s_pcm *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 snd_ml7213i2s_pcm *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 snd_ml7213i2s_pcm *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;
}

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 = ARRAY_SIZE(wm8731_reg),
	.reg_word_size = sizeof(u16),
	.reg_cache_default = wm8731_reg,
};

#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 snd_ml7213i2s_pcm *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");

[-- Attachment #3: ioh_i2s.h --]
[-- Type: text/plain, Size: 3428 bytes --]

/*
 * 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

[-- Attachment #4: ioh_i2s_config.h --]
[-- Type: text/plain, Size: 64670 bytes --]

/*
 * 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

[-- Attachment #5: ml7213ioh-machine.c --]
[-- Type: text/plain, Size: 2280 bytes --]

/*
 * 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");

[-- Attachment #6: ml7213ioh-plat.c --]
[-- Type: text/plain, Size: 66728 bytes --]

/*
 * 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 {
	struct snd_ml7213i2s *ml7213i2s;
	spinlock_t lock;
	unsigned int irq_pos;
	unsigned int buf_pos;
	struct snd_pcm_substream *substream;
	struct cbdata cbd;              /* i2s callback info */
	unsigned int channels;
	unsigned int rw;
	unsigned int rate;
	unsigned int ch;
	unsigned int setup_flag;
	unsigned int format;
	unsigned int bclkfs;
	unsigned int master_mode;
	unsigned int enable_mode;
};

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 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 channel = priv->ch;

	iowrite32(1 << channel, 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_common(
			struct ioh_i2s_data *priv,
			char *chan_name,
			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 */
		priv->param_rx.width = priv->dma_rx_width;
		priv->param_rx.dma_dev = &dma_dev->dev;
		priv->param_rx.chan_id = priv->ch * 2 + 1; /* ch Rx=1,3,...11 */
		priv->param_rx.rx_reg = (dma_addr_t)(priv->mapbase +\
					priv->ch * 0x800 +\
					I2SDRRXMIRROR_OFFSET);
		chan = dma_request_channel(mask, filter, &priv->param_rx);
		if (chan == NULL) {
			dev_err(priv->dev, "Failed dma_request_channel %s for"
				" I2S %s\n", chan_name, priv->rx_name);
			return NULL;
		}
		priv->chan_rx = chan;

	} else if (dir == DMA_TO_DEVICE) { /* Tx */
		priv->param_tx.width = priv->dma_tx_width;
		priv->param_tx.dma_dev = &dma_dev->dev;
		priv->param_tx.chan_id = priv->ch * 2; /* DMA ch Tx=0,2,...10 */

		priv->param_tx.tx_reg = (dma_addr_t)(priv->mapbase +\
					priv->ch * 0x800 +\
					I2SDRTXMIRROR_OFFSET);

		priv->param_tx.tx_reg_list = NULL;

		chan = dma_request_channel(mask, filter, &priv->param_tx);
		if (chan == NULL) {
			dev_err(priv->dev, "Failed dma_request_channel %s for"
				" I2S %s\n", chan_name, priv->tx_name);
			return NULL;
		}
		priv->chan_tx = chan;
	} else {
		dev_err(priv->dev, "%s:Invalid direction (%d)\n",
			chan_name, dir);
		return NULL;
	}

	return chan;
}

static void ioh_setup_rx_dma(struct ioh_i2s_data *priv)
{
	ioh_request_dma_channel_common(
		priv,
		priv->dma_config->rx_chan,
		DMA_FROM_DEVICE);
}

static void ioh_setup_tx_dma(struct ioh_i2s_data *priv)
{
	ioh_request_dma_channel_common(
		priv,
		priv->dma_config->tx_chan,
		DMA_TO_DEVICE);
}

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 scatterlist *sg = priv->sg_tx_p;
	int t_num = 0;
	int l;
	int *ptr_fmt;
	int *ptr32;
	short *ptr16;
	char *ptr8;

	if (priv->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, priv->tx_data_head, priv->tx_complete);

	if ((priv->tx_data_head + ((len/priv->dma_tx_unit)*4)) <=
	    priv->tx_tail) {
		tx_index = (int)(priv->tx_data_head - priv->tx_head) /
				(I2S_AEMPTY_THRESH * 4);
		sg = sg + tx_index;
		t_num = len/(I2S_AEMPTY_THRESH * priv->dma_tx_unit);
		dma_sync_sg_for_cpu(priv->dev, sg, t_num, DMA_TO_DEVICE);

		ptr_fmt = (int *)priv->tx_data_head;
		switch (priv->dma_tx_unit) {
		case 1:
			ptr8 = (char *)data;
			for (l = 0; l < (len/priv->dma_tx_unit); l++)
				*ptr_fmt++ = (int)*ptr8++;
			break;
		case 2:
			ptr16 = (short *)data;
			for (l = 0; l < (len/priv->dma_tx_unit); l++)
				*ptr_fmt++ = (int)*ptr16++;
			break;
		case 4:
			ptr32 = (int *)data;
			for (l = 0; l < (len/priv->dma_tx_unit); l++)
				*ptr_fmt++ = *ptr32++;
			break;
		}
		dma_sync_sg_for_device(priv->dev, sg, t_num, DMA_TO_DEVICE);
		priv->tx_data_head += (len/priv->dma_tx_unit)*4;
	} else {
		rem1 = (priv->tx_tail - priv->tx_data_head)/4;
		rem2 = (len/priv->dma_tx_unit) - rem1;
		tx_index = (int)(priv->tx_data_head-priv->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 *)priv->tx_data_head;
		switch (priv->dma_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);
		priv->tx_data_head = priv->tx_head;
		sg = priv->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 *)priv->tx_data_head;

		switch (priv->dma_tx_unit) {
		case 1:
			ptr8 = (char *)(data+rem1*priv->dma_tx_unit);
			for (l = 0; l < rem2; l++)
				*ptr_fmt++ = (int)*ptr8++;
			break;
		case 2:
			ptr16 = (short *)(data+rem1*priv->dma_tx_unit);
			for (l = 0; l < rem2; l++)
				*ptr_fmt++ = (int)*ptr16++;
			break;
		case 4:
			ptr32 = (int *)(data+rem1*priv->dma_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);
		priv->tx_data_head += rem2*4;
	}

	if (priv->tx_data_head >= priv->tx_tail)
		priv->tx_data_head = priv->tx_head;

	dev_dbg(priv->dev, "-->data_head=%p\n", priv->tx_data_head);

	priv->tx_avail += (len/priv->dma_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) {
		priv->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 {
		priv->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;
	int num = 0;

	if (priv->rxexe_flag) {
		if (priv->rx_done) {
			switch (priv->dma_rx_unit) {
			case 1:
				num = priv->rx_avail/4;
				break;
			case 2:
				num = priv->rx_avail/2;
				break;
			case 4:
				num = priv->rx_avail;
				break;
			}
			priv->rx_done(priv->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;
	int num = 0;
	int avail = 0;

	if (priv->txexe_flag) {
		if (priv->tx_done) {
			switch (priv->dma_tx_unit) {
			case 1:
				num = (INTER_BUFF_SIZE - priv->tx_avail)/4;
				avail = priv->tx_avail/4;
				break;
			case 2:
				num = (INTER_BUFF_SIZE - priv->tx_avail)/2;
				avail = priv->tx_avail/2;
				break;
			case 4:
				num = (INTER_BUFF_SIZE - priv->tx_avail);
				avail = priv->tx_avail;
				break;
			}
			priv->tx_done(priv->tx_callback_data,
				      IOH_EOK, num, avail);
		}
	}
}

static void ioh_i2s_release(struct ioh_i2s_data *priv, enum ioh_direction dir)
{
	if (!priv) {
		dev_err(priv->dev, "%s: i2s is NULL\n", __func__);
		return;
	}

	if (dir) {
		dma_sync_sg_for_cpu(priv->dev, priv->sg_rx_p, priv->rx_nent,
				    DMA_FROM_DEVICE);

		ioh_i2s_disable_interrupts(priv, IOH_CAPTURE);
		ioh_i2s_disable_rx_full_ir(priv);
		if (priv->chan_rx) {
			priv->chan_rx->device->device_control(priv->chan_rx,
							     DMA_TERMINATE_ALL,
							     0);
			dma_release_channel(priv->chan_rx);
			priv->chan_rx = NULL;
		}

		kfree(priv->sg_rx_p);
		if (priv->rxbuf_virt)
			dma_free_coherent(priv->dev, INTER_BUFF_SIZE,
					  priv->rxbuf_virt,
					  priv->rx_buf_dma);

		priv->rxbuf_virt = NULL;
		priv->rx_buf_dma = 0;
		atomic_dec(&priv->rx_busy);

		tasklet_disable(&priv->rx_tasklet);
		tasklet_kill(&priv->rx_tasklet);

	} else {
		dma_sync_sg_for_cpu(priv->dev, priv->sg_tx_p, priv->tx_nent,
				    DMA_TO_DEVICE);

		ioh_i2s_disable_interrupts(priv, IOH_PLAYBACK);
		ioh_i2s_disable_tx_empty_ir(priv);
		if (priv->chan_tx) {
			priv->chan_tx->device->device_control(priv->chan_tx,
							     DMA_TERMINATE_ALL,
							     0);
			dma_release_channel(priv->chan_tx);
			priv->chan_tx = NULL;
		}

		kfree(priv->sg_tx_p);
		if (priv->txbuf_virt)
			dma_free_coherent(priv->dev, INTER_BUFF_SIZE,
					  priv->txbuf_virt,
					  priv->tx_buf_dma);

		priv->txbuf_virt = NULL;
		priv->tx_buf_dma = 0;
		atomic_dec(&priv->tx_busy);

		tasklet_disable(&priv->tx_tasklet);
		tasklet_kill(&priv->tx_tasklet);
	}
}

static struct ioh_i2s_data *ioh_i2s_open(int ch, enum ioh_direction dir,
				     const char *name, void *cbd,
				     void (*cb)(void *cbd, int status,
						int num, int avail))
{
	struct ioh_i2s_data *obj = NULL;
	struct scatterlist *sg;
	int rx_size;
	int rx_num;
	int tx_size;
	int tx_num;
	int i;

	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;
	}

	obj = kzalloc(sizeof(*obj), GFP_KERNEL);

	obj->ignore_rx_overrun = 0;

	obj->dma_tx_width = PCH_DMA_WIDTH_4_BYTES;
	obj->dma_rx_width = PCH_DMA_WIDTH_4_BYTES;

	obj->ch = ch;
	obj->dev = obj->dev;

	atomic_set(&obj->rx_busy, 0);
	atomic_set(&obj->tx_busy, 0);
	strcpy(obj->rx_name, "FREE");
	strcpy(obj->tx_name, "FREE");

	if (dir) {
		/* Rx configuration */
		if (atomic_read(&obj->rx_busy)) {
			dev_err(obj->dev, "rx i2s%d have already opened\n", ch);
			atomic_dec(&obj->rx_busy);
			return 0;
		}
		atomic_inc(&obj->rx_busy);

		strcpy(obj->rx_name, name);
		ioh_setup_rx_dma(obj);
		if (!obj->chan_rx) {
			dev_err(obj->dev, "%s:ioh_setup_rx_dma failed\n",
				__func__);
			return NULL;
		}

		obj->rxbuf_virt = dma_alloc_coherent(obj->dev, INTER_BUFF_SIZE,
						&obj->rx_buf_dma, GFP_KERNEL);
		if (!obj->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);

		obj->sg_rx_p =\
		       kzalloc(sizeof(struct scatterlist) *rx_num, GFP_ATOMIC);

		sg = obj->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(obj->rxbuf_virt), rx_size,
				    rx_size * i);
			sg_dma_len(sg) = rx_size / 4;
			sg_dma_address(sg) = obj->rx_buf_dma + sg->offset;
		}

		obj->rx_head = (unsigned char *)obj->rxbuf_virt;
		obj->rx_tail = (unsigned char *)obj->rxbuf_virt +
			       rx_num * rx_size;
		obj->rx_data_head = (unsigned char *)obj->rxbuf_virt;
		obj->rx_complete = (unsigned char *)obj->rxbuf_virt;
		obj->rx_avail = 0;

		obj->rx_nent = rx_num;
		dma_sync_sg_for_device(obj->dev, obj->sg_rx_p, obj->rx_nent,
				       DMA_FROM_DEVICE);

		tasklet_init(&obj->rx_tasklet, i2s_rx_tasklet,
			     (unsigned long)obj);
	} else {
		/* Tx configuration */
		if (atomic_read(&obj->tx_busy)) {
			dev_err(obj->dev, "tx i2s%d have already opened\n", ch);
			atomic_dec(&obj->tx_busy);
			return 0;
		}
		atomic_inc(&obj->tx_busy);

		strcpy(obj->tx_name, name);
		ioh_setup_tx_dma(obj);
		if (!obj->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;

		obj->txbuf_virt = dma_alloc_coherent(obj->dev, INTER_BUFF_SIZE,
						&obj->tx_buf_dma, GFP_KERNEL);

		if (!obj->txbuf_virt) {
			dev_err(obj->dev, "dma_alloc_coherent Failed\n");
			return NULL;
		}

		obj->tx_head = (unsigned char *)obj->txbuf_virt;
		obj->tx_tail = (unsigned char *)obj->txbuf_virt +
			       INTER_BUFF_SIZE;
		obj->tx_data_head = (unsigned char *)obj->txbuf_virt;
		obj->tx_complete = (unsigned char *)obj->txbuf_virt;
		obj->tx_avail = 0;

		dev_dbg(obj->dev, "%s: tx: scatter_num=%d scatter_size=%d\n",
			__func__, tx_num, tx_size);

		obj->sg_tx_p =\
		       kzalloc(sizeof(struct scatterlist) *tx_num, GFP_ATOMIC);

		sg_init_table(obj->sg_tx_p, tx_num); /* Initialize SG table */
		sg = obj->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(obj->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(obj->txbuf_virt),
						  tx_size, tx_size * i);
					sg_dma_len(sg) = tx_size\
							/ 4;
				}
			} else {
				sg_set_page(sg, virt_to_page(obj->txbuf_virt),
					    tx_size, tx_size * i);
				sg_dma_len(sg) = tx_size / 4;
			}
			sg_dma_address(sg) = obj->tx_buf_dma + sg->offset;
		}
		obj->tx_nent = tx_num;
		dma_sync_sg_for_device(obj->dev, obj->sg_tx_p, obj->tx_nent,
				       DMA_TO_DEVICE);

		tasklet_init(&obj->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 scatterlist *sg = priv->sg_rx_p;
	int rx_index;
	int t_num = 0;
	int *ptr_fmt;
	int *ptr32;
	short *ptr16;
	char *ptr8;
	int l;

	switch (priv->dma_rx_unit) {
	case 1:
		t_num = priv->rx_avail/4;
		break;
	case 2:
		t_num = priv->rx_avail/2;
		break;
	case 4:
		t_num = priv->rx_avail;
		break;
	}

	if (t_num < len) {
		dev_err(priv->dev, "%s[%d]: internal buffer empty\n",
			__func__, priv->ch);
		return;
	}

	if ((priv->rx_complete + ((len/priv->dma_rx_unit)*4)) <=
	    priv->rx_tail) {
		rx_index = (int)(priv->rx_complete - priv->rx_head) /
				(I2S_AFULL_THRESH * 4);
		sg = sg + rx_index;
		t_num = len/(I2S_AFULL_THRESH * priv->dma_rx_unit);
		dma_sync_sg_for_cpu(priv->dev, sg, t_num, DMA_FROM_DEVICE);

		ptr_fmt = (int *)priv->rx_complete;
		switch (priv->dma_rx_unit) {
		case 1:
			ptr8 = (char *)data;
			for (l = 0; l < (len/priv->dma_rx_unit); l++)
				*ptr8++ = (char)*ptr_fmt++;
			break;
		case 2:
			ptr16 = (short *)data;
			for (l = 0; l < (len/priv->dma_rx_unit); l++)
				*ptr16++ = (short)*ptr_fmt++;
			break;
		case 4:
			ptr32 = (int *)data;
			for (l = 0; l < (len/priv->dma_rx_unit); l++)
				*ptr32++ = *ptr_fmt++;
			break;
		}
		dma_sync_sg_for_device(priv->dev, sg, t_num, DMA_FROM_DEVICE);
		priv->rx_complete += (len/priv->dma_rx_unit)*4;
	} else {
		rem1 = (priv->rx_tail - priv->rx_complete)/4;
		rem2 = (len/priv->dma_rx_unit) - rem1;
		rx_index = (int)(priv->rx_complete-priv->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 *)priv->rx_complete;
		switch (priv->dma_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);
		priv->rx_complete = priv->rx_head;
		sg = priv->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 *)priv->rx_complete;
		switch (priv->dma_rx_unit) {
		case 1:
			ptr8 = (char *)(data+rem1*priv->dma_rx_unit);
			for (l = 0; l < rem2; l++)
				*ptr8++ = (char)*ptr_fmt++;
			break;
		case 2:
			ptr16 = (short *)(data+rem1*priv->dma_rx_unit);
			for (l = 0; l < rem2; l++)
				*ptr16++ = (short)*ptr_fmt++;
			break;
		case 4:
			ptr32 = (int *)(data+rem1*priv->dma_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);
		priv->rx_complete += rem2*4;
	}

	if (priv->rx_complete >= priv->rx_tail)
		priv->rx_complete = priv->rx_head;

	priv->rx_avail -= (len/priv->dma_rx_unit)*4;
}

static void i2s_dma_rx_complete(void *arg)
{
	struct ioh_i2s_data *priv = arg;
	struct scatterlist *sg = priv->sg_rx_cur;
	int num = priv->rx_num;
	int i;

	async_tx_ack(priv->desc_rx);

	for (i = 0; i < num; i++, sg++) {
		priv->rx_data_head += sg_dma_len(sg) * 4;
		priv->rx_avail += sg_dma_len(sg) * 4;
	}

	if (priv->rx_data_head >= priv->rx_tail)
		priv->rx_data_head = priv->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 scatterlist *sg = priv->sg_tx_cur;
	int num = priv->tx_num;
	int i;

	async_tx_ack(priv->desc_tx);

	for (i = 0; i < num; i++, sg++) {
		priv->tx_complete += sg_dma_len(sg) * 4;
		priv->tx_avail -= sg_dma_len(sg) * 4;
	}

	if (priv->tx_complete >= priv->tx_tail)
		priv->tx_complete = priv->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 scatterlist *sg = priv->sg_tx_p;

	dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p\n", __func__,
		priv->tx_data_head, priv->tx_complete);

	num = ((int)priv->tx_avail) / (I2S_AEMPTY_THRESH * 4);

	tx_comp_index = (((int)(priv->tx_complete - priv->tx_head))) /\
			(I2S_AEMPTY_THRESH * 4);

	if ((tx_comp_index + num) >= priv->tx_nent)
		num = priv->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(&priv->tx_tasklet);
		return; /* No data to transmit */
	}

	sg = sg + tx_comp_index; /* Point head of sg must be sent */
	priv->sg_tx_cur = sg; /* Save tx condition */
	priv->tx_num = num; /* Save tx condition */

	desc = priv->chan_tx->device->device_prep_slave_sg(priv->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);

	priv->desc_tx = desc;
	desc->callback = i2s_dma_tx_complete;
	desc->callback_param = priv;

	atomic_inc(&priv->pending_tx);
	desc->tx_submit(desc);

	tasklet_schedule(&priv->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;

	num = (int)(INTER_BUFF_SIZE - priv->rx_avail) / (I2S_AFULL_THRESH * 4);
	if (num < 1) {
		dev_err(priv->dev, "%s:Internal buffer full\n",
			__func__);
		tasklet_schedule(&priv->rx_tasklet);
		return;
	}

	sg = priv->sg_rx_p;
	rx_data_index = ((int)(priv->rx_data_head - priv->rx_head)) /\
			(I2S_AFULL_THRESH * 4);

	if ((rx_data_index + num) >= priv->rx_nent)
		num = priv->rx_nent - rx_data_index;

	if (num > I2S_DMA_SG_NUM)
		num = I2S_DMA_SG_NUM;

	sg += rx_data_index;

	desc = priv->chan_rx->device->device_prep_slave_sg(priv->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;
	}

	priv->sg_rx_cur = sg; /* Save rx condition */
	ioh_i2s_disable_rx_full_ir(priv);
	priv->rx_num = num;

	priv->desc_rx = desc;
	desc->callback = i2s_dma_rx_complete;
	desc->callback_param = priv;
	desc->tx_submit(desc);

	tasklet_schedule(&priv->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)
{
	unsigned long flags;

	spin_lock_irqsave(&priv->tx_lock, flags);

	if (idisp & BIT(priv->ch + 16)) {
		dev_dbg(priv->dev, "Rx%d interrupt occures\n", priv->ch);
		ioh_i2s_interrupt_sub_rx(priv);
	}

	if (idisp & BIT(priv->ch)) {
		dev_dbg(priv->dev, "Tx%d interrupt occures\n", priv->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;
	struct ioh_i2s_data *priv;

	priv = &drvdata->devs[0];
	idisp = ioread32(priv->iobase + I2SIDISP_OFFSET);
	for (i = 0; i < MAX_I2S_IF; i++)
		ioh_i2s_event(&drvdata->devs[i], idisp);

	return IRQ_HANDLED;
}

/*****************************************************************************
 *	Sound Card
 *****************************************************************************/
static void i2s_read_period(struct snd_pcm_substream *substream)
{
	struct snd_ml7213i2s_pcm *dpcm;
	struct cbdata *cbd;
	int period;
	void *read_ptr;
	int read_size;

	dpcm = substream->runtime->private_data;
	cbd = &dpcm->cbd;

	if (!cbd->priv)
		return;

	period = substream->runtime->period_size;

	read_ptr = substream->runtime->dma_area
	  +(snd_pcm_lib_period_bytes(substream) * dpcm->irq_pos);
	read_size = period * (substream->runtime->sample_bits/8) *
		    (substream->runtime->channels);

	ioh_i2s_read(cbd->priv,
			 read_ptr,
			 read_size);

	dpcm->irq_pos = (dpcm->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_pcm *dpcm;
	struct cbdata *cbd;
	dpcm = substream->runtime->private_data;

	if (num < snd_card_ml7213i2s_capture[dpcm->ch].period_bytes_max)
		return;

	cbd = &dpcm->cbd;
	switch (status) {
	case IOH_EOK:
		if (!cbd->stop) {
			i2s_read_period(substream);
			dpcm->buf_pos = (dpcm->buf_pos + 1) %
				substream->runtime->periods;
			snd_pcm_period_elapsed(dpcm->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, int ch)
{
	struct snd_ml7213i2s_pcm *dpcm;
	struct ioh_i2s_config_reg config;
	unsigned int master;
	int ret = 0;
	unsigned int byte;

	master = (i2s_config_table[ch].i2sclkcnt >> I2SCLKCNT_MSSEL_OFFSET) & 1;
	dpcm = substream->runtime->private_data;

	dpcm->cbd.priv = ioh_i2s_open(ch, IOH_CAPTURE, "radio-i2s-in",
					 substream,
					 read_done);
	dpcm->setup_flag = 1;

	if (!dpcm->cbd.priv) {
		pr_err("%s: Cannot open the device\n", __func__);
		return -1;
	}

	if (ignore_overrun)
		ioh_i2s_ignore_rx_overrun(dpcm->cbd.priv);

	ret = i2s_rx_configure_reg(&config, dpcm->rate, dpcm);
	if (master)
		dpcm->master_mode |= 1 << (ch + I2S_READ_MASTER_BIT);
	else
		dpcm->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(dpcm->cbd.priv, 0, &config, IOH_CAPTURE,
				   byte);
	return ret;
}

static void i2s_write_period(struct snd_pcm_substream *substream)
{
	struct snd_ml7213i2s_pcm *dpcm;
	struct cbdata *cbd;
	int period;
	void *write_ptr;
	int write_size;

	dpcm = substream->runtime->private_data;
	cbd = &dpcm->cbd;

	if (!cbd->priv)
		return;

	period = substream->runtime->period_size;
	write_ptr = substream->runtime->dma_area
	  +(snd_pcm_lib_period_bytes(substream) * dpcm->irq_pos);
	write_size = period * (substream->runtime->sample_bits/8) *
		     (substream->runtime->channels);

	ioh_i2s_write(cbd->priv,
			 write_ptr,
			 write_size);

	dpcm->irq_pos = (dpcm->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_pcm *dpcm;
	struct cbdata *cbd;

	dpcm = substream->runtime->private_data;

	if (num < snd_card_ml7213i2s_playback[dpcm->ch].period_bytes_max)
		return;
	if (avail >= snd_card_ml7213i2s_playback[dpcm->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 = &dpcm->cbd;

	switch (status) {
	case IOH_EOK:
		if (!cbd->stop) {
			i2s_write_period(substream);
			dpcm->buf_pos = (dpcm->buf_pos + 1) %
				substream->runtime->periods;
			snd_pcm_period_elapsed(dpcm->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, int ch)
{
	struct snd_ml7213i2s_pcm *dpcm;
	struct ioh_i2s_config_reg config;
	unsigned int master;
	int ret = 0;
	unsigned int byte;

	master = (i2s_config_table[ch].i2sclkcnt >> I2SCLKCNT_MSSEL_OFFSET) & 1;
	dpcm = substream->runtime->private_data;

	dpcm->cbd.priv = ioh_i2s_open(ch, IOH_PLAYBACK, "radio-i2s-out",
					 substream,
					 write_done);
	dpcm->setup_flag = 1;

	if (!dpcm->cbd.priv) {
		pr_err("%s: Cannot open the device\n", __func__);
		return -1;
	}

	if (ignore_overrun)
		ioh_i2s_ignore_rx_overrun(dpcm->cbd.priv);

	ret = i2s_tx_configure_reg(&config, dpcm->rate, dpcm);
	if (master)
		dpcm->master_mode |= 1 << (ch + I2S_WRITE_MASTER_BIT);
	else
		dpcm->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(dpcm->cbd.priv, 0, &config, IOH_PLAYBACK,
				   byte);
	return ret;
}

static void __snd_card_ml7213i2s_runtime_free(struct snd_pcm_runtime *runtime)
{
	struct snd_ml7213i2s_pcm *dpcm;
	static int cnt;

	dpcm = (struct snd_ml7213i2s_pcm *)runtime->private_data;
	/* FIXME: This is just a big ball of race right now...
	 */
	if (!dpcm->cbd.stop)
		dpcm->cbd.stop = 1;
	else {
		while (dpcm->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_pcm *dpcm;
	dpcm = (struct snd_ml7213i2s_pcm *)runtime->private_data;

	__snd_card_ml7213i2s_runtime_free(runtime);

	if (dpcm->setup_flag)
		ioh_i2s_release(dpcm->cbd.priv, IOH_CAPTURE);
	kfree(runtime->private_data);
}

static struct snd_ml7213i2s_pcm *
new_pcm_stream(struct snd_pcm_substream *substream)
{
	struct snd_ml7213i2s_pcm *dpcm;

	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
	if (!dpcm)
		return dpcm;
	spin_lock_init(&dpcm->lock);
	dpcm->substream = substream;
	return dpcm;
}

static int snd_card_ml7213i2s_open(struct snd_pcm_substream *substream)
{
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct snd_ml7213i2s_pcm *dpcm;
	int err;

	dpcm = new_pcm_stream(substream);
	if (dpcm == NULL)
		return -ENOMEM;

	runtime->private_data = dpcm;
	/* makes the infrastructure responsible for freeing dpcm */
	runtime->private_free = snd_card_ml7213i2s_runtime_capture_free;

	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
		dpcm->rw = SND_CAPTURE_SUBSTREAM;
		runtime->hw = snd_card_ml7213i2s_capture[substream->number];
	} else {
		dpcm->rw = SND_PLAYBACK_SUBSTREAM;
		runtime->hw = snd_card_ml7213i2s_playback[substream->number];
	}

	dpcm->setup_flag = 0;
	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 = runtime->private_data;
	unsigned int enable_flag = 0;
	unsigned int ch = substream->number;

	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 (dpcm->rw) {
	case SND_CAPTURE_SUBSTREAM:
		if (setup_i2s_read(substream, substream->number))
			return -1;

		dpcm->enable_mode |= 1 << (ch + I2S_READ_ENABLE_BIT);
		enable_flag = (dpcm->enable_mode >> (ch+I2S_WRITE_ENABLE_BIT)) & 1;
		break;
	case SND_PLAYBACK_SUBSTREAM:
		if (setup_i2s_write(substream, substream->number))
			return -1;

		dpcm->enable_mode |= 1 << (ch + I2S_WRITE_ENABLE_BIT);
		enable_flag = (dpcm->enable_mode >> (ch+I2S_READ_ENABLE_BIT)) & 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_pcm *dpcm = substream->runtime->private_data;
	struct ioh_i2s_config_reg config;
	unsigned int flag;
	unsigned int enable_flag = 0;
	unsigned int ch = substream->number;

	switch (dpcm->rw) {
	case SND_CAPTURE_SUBSTREAM:
		i2s_rx_configure_reg(&config, dpcm->rate, dpcm);
		config.cmn.i2sclkcnt &= ~I2SCLKCNT_MSSEL;

		dpcm->master_mode &= ~(1<<(ch+I2S_READ_MASTER_BIT));
		flag = (dpcm->master_mode >> (ch+I2S_WRITE_MASTER_BIT)) & 1;

		dpcm->enable_mode &= ~(1<<(ch+I2S_READ_ENABLE_BIT));
		enable_flag = (dpcm->enable_mode >> (ch+I2S_WRITE_ENABLE_BIT)) & 1;

		if (dpcm->setup_flag) {
			ioh_i2s_stop_i2s_regs(dpcm->cbd.priv, 0, &config,
					      IOH_CAPTURE, flag);
			kfree(dpcm->cbd.priv);
		}
		break;

	case SND_PLAYBACK_SUBSTREAM:
		i2s_tx_configure_reg(&config, dpcm->rate, dpcm);
		config.cmn.i2sclkcnt &= ~I2SCLKCNT_MSSEL;

		dpcm->master_mode &= ~(1<<(ch+I2S_WRITE_MASTER_BIT));
		flag = (dpcm->master_mode >> (ch+I2S_READ_MASTER_BIT)) & 1;

		dpcm->enable_mode &= ~(1<<(ch+I2S_WRITE_ENABLE_BIT));
		enable_flag = (dpcm->enable_mode >> (ch+I2S_READ_ENABLE_BIT)) & 1;

		if (dpcm->setup_flag) {
			ioh_i2s_stop_i2s_regs(dpcm->cbd.priv, 0, &config,
					      IOH_PLAYBACK, flag);
			kfree(dpcm->cbd.priv);
		}
		break;

	default:
		return -1;
	}

	if (!enable_flag) {
		pr_err("%s: enable_flag is NULL\n", __func__);
		return -1;
	}

	return snd_pcm_lib_free_pages(substream);
}

void ioh_i2s_irq_stop(struct ioh_i2s_data *priv, enum ioh_direction dir)
{
	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);
		priv->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);
		priv->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))
{
	priv->txexe_flag = 1;

	priv->tx_data_head = priv->tx_head;
	priv->tx_complete = priv->tx_head;
	priv->tx_avail = 0;

	priv->tx_callback_data = callback_data;
	priv->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))
{
	priv->rxexe_flag = 1;

	priv->rx_data_head = priv->rx_head;
	priv->rx_complete = priv->rx_head;
	priv->rx_avail = 0;

	priv->rx_callback_data = callback_data;
	priv->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_pcm *dpcm;
	struct cbdata *cbd;
	dpcm = substream->runtime->private_data;
	cbd = &dpcm->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_pcm *dpcm = runtime->private_data;
	int err = 0;

	spin_lock(&dpcm->lock);

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_RESUME:
		dpcm->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 (!dpcm->cbd.stop)
			dpcm->cbd.stop = 1;
		else
			pr_debug("already stopped %d\n", dpcm->cbd.stop);
		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
			ioh_i2s_irq_stop(dpcm->cbd.priv, IOH_CAPTURE);
		else
			ioh_i2s_irq_stop(dpcm->cbd.priv, IOH_PLAYBACK);
		break;
	default:
		err = -EINVAL;
		break;
	}

	spin_unlock(&dpcm->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_pcm *dpcm = runtime->private_data;

	dpcm->irq_pos = 0;
	dpcm->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_pcm *dpcm = runtime->private_data;

	return substream->runtime->period_size*dpcm->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->membase = tbl;
	drvdata->mapbase = mapbase;
	drvdata->dev = &pdev->dev;

	for (i = 0; i < MAX_I2S_IF; i++) {
		drvdata->devs[i].iobase = tbl;
		drvdata->devs[i].mapbase = mapbase;
		spin_lock_init(&drvdata->devs[i].tx_lock);
		drvdata->devs[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->membase);
	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[i].iobase;
		save = &drvdata->devs[i].ch_reg_save;

		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[i].iobase + I2SCLKCNT0_OFFSET + 0x10*i);
	}
	drvdata->cmn_reg_save.i2simask =
			   ioread32(drvdata->devs[0].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[i].iobase;
		save = &drvdata->devs[i].ch_reg_save;

		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[i].iobase + I2SCLKCNT0_OFFSET + 0x10*i);
	}

	iowrite32(drvdata->cmn_reg_save.i2simask,
		  drvdata->devs[0].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");

[-- Attachment #7: ml7213ioh-plat.h --]
[-- Type: text/plain, Size: 6184 bytes --]

/*
 * 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;
	atomic_t rx_busy;
	atomic_t tx_busy;

	int ignore_rx_overrun;

	/* Transmit side DMA */
	atomic_t pending_tx;

	struct ioh_dma_config *dma_config;

	char rx_name[16];
	char tx_name[16];

	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;

	spinlock_t tx_lock;

	struct pch_dma_slave		param_tx;
	struct pch_dma_slave		param_rx;
	unsigned int			mapbase;
	dma_addr_t			tx_reg_addr[2];

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

	unsigned int tx_lower_data_flag;

	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_pm_ch_reg ch_reg_save;
};

struct ioh_i2s_data_pci {
	struct ioh_i2s_data devs[MAX_I2S_IF];
	void __iomem *membase;
	unsigned int mapbase;
	struct ioh_i2s_pm_common_reg cmn_reg_save;
	struct snd_card *card;
	struct device *dev;
};

#endif

[-- Attachment #8: ml26124.h --]
[-- Type: text/plain, Size: 4578 bytes --]

/*
 * Copyright (c) 2005 Openedhand Ltd.
 * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
 *
 * This code was derived from the WM8731 Soc Audio driver
 * by Richard Purdie <richard@openedhand.com>
 *
 * 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 _WM8731_H
#define _WM8731_H

/* WM8731 register space */

#define WM8731_LINVOL   0x00
#define WM8731_RINVOL   0x01
#define WM8731_LOUT1V   0x02
#define WM8731_ROUT1V   0x03
#define WM8731_APANA    0x04
#define WM8731_APDIGI   0x05
#define WM8731_PWR      0x06
#define WM8731_IFACE    0x07
#define WM8731_SRATE    0x08
#define WM8731_ACTIVE   0x09
#define WM8731_RESET	0x0f

#define WM8731_CACHEREGNUM 	10

#define WM8731_SYSCLK_XTAL 1
#define WM8731_SYSCLK_MCLK 2

#define WM8731_DAI		0

/* 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

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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
  2011-10-24 12:12       ` Tomoya MORINAGA
@ 2011-10-24 12:20         ` Mark Brown
  2011-11-08  9:03           ` Tomoya MORINAGA
  0 siblings, 1 reply; 19+ messages in thread
From: Mark Brown @ 2011-10-24 12:20 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Takashi Iwai, perex, linux-kernel, Wang, Qi, Wang, Yong Y, Clark,
	Joel, Ewe, Kok Howg, Liam Girdwood, alsa-devel

On Mon, Oct 24, 2011 at 09:12:42PM +0900, Tomoya MORINAGA wrote:

> 1. PCI interface function.
> Any current ASoC drivers don't have PCI interface function.
> So I don't know where the function should be in machine driver or
> platform driver.

It depends on what the driver is for.  Probably you want a driver which
is some combination of machine driver and the various drivers that are
normally part of the SoC - whatever roles in the system are filled by
this hardware the driver ought to register subsystem drivers for those
roles.

> 2. Register Access
> Can platform driver access register ?
> According to the soc document, platform driver must not access hardware,
> however, some drivers looks accessing their hardware.

What makes you say this?  A driver that can't access hardware would be
rather useless...

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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
  2011-10-21 14:16     ` Takashi Iwai
@ 2011-10-24 12:12       ` Tomoya MORINAGA
  2011-10-24 12:20         ` Mark Brown
  0 siblings, 1 reply; 19+ messages in thread
From: Tomoya MORINAGA @ 2011-10-24 12:12 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: perex, linux-kernel, Wang, Qi, Wang, Yong Y, Clark, Joel, Ewe,
	Kok Howg, Liam Girdwood, Mark Brown, alsa-devel

Hi Iwai,

(2011/10/21 23:16), Takashi Iwai wrote:
> At Mon, 17 Oct 2011 13:28:39 +0900,
> Tomoya MORINAGA wrote:
>>
>> Hi Iwai,
>>
>> We have just started porting to ASoC structure.
>
> Please add ASoC maintainers to Cc if you have questions about ASoC.
>
>> I have a question.
>>
>> As you reviewed before, currently, our driver consists of 2 parts,
>> Soundcard driver and I2S driver.
>>
>> Soundcard consists of 2 parts
>>     - ALSA interface / control part
>>     - CODEC control part
>>
>> I2S driver consists of 4 parts
>>    - HAL
>>    - DMA control / interrupt control
>>    - Soundcard interface part
>>    - PCI interface function
>>
>> According to "soc" Documentation,
>> We must divide to 3 parts, platform driver, machine driver and codec driver.
>>
>> So, I divided like the following parts.
>>
>> platform driver
>>    - ALSA interface / control part
>>    - HAL
>>    - DMA control / interrupt control
>>    - Soundcard interface part
>>    - PCI interface function
>>
>> machine driver
>>    - (none)
>>
>> codec driver
>>     - CODEC control part
>>
>> Is the above dividing true ?
>
> Not really.  ASoC is designed to be much more modular.  Take a look at
> the codes in sound/soc/*.  You'll grasp how the components are split.
> (The documents in Documentation/sound/alsa/soc/ are slightly
>   obsoleted...)

Of course, I read Documentation/sound/alsa/soc/ and sound/soc/*.
However, it's not easy for me.

I have just 2 questions.

1. PCI interface function.
Any current ASoC drivers don't have PCI interface function.
So I don't know where the function should be in machine driver or 
platform driver.

2. Register Access
Can platform driver access register ?
According to the soc document, platform driver must not access hardware,
however, some drivers looks accessing their hardware.

>
> The conversion of PCM part is usually straightforward, as found in
> *-pcm.c.  The DAI setup depends on the hardware implementation.
> That's for machine driver.  But it's hard to tell in more details
> until I see the actual code snippet you are working on...

I understand.
I'm going to send our modified driver soon.

Thanks,

-- 
tomoya
ROHM Co., Ltd.

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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
  2011-10-17  4:28   ` Tomoya MORINAGA
@ 2011-10-21 14:16     ` Takashi Iwai
  2011-10-24 12:12       ` Tomoya MORINAGA
  0 siblings, 1 reply; 19+ messages in thread
From: Takashi Iwai @ 2011-10-21 14:16 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: perex, linux-kernel, Wang, Qi, Wang, Yong Y, Clark, Joel, Ewe,
	Kok Howg, Liam Girdwood, Mark Brown, alsa-devel

At Mon, 17 Oct 2011 13:28:39 +0900,
Tomoya MORINAGA wrote:
> 
> Hi Iwai,
> 
> We have just started porting to ASoC structure.

Please add ASoC maintainers to Cc if you have questions about ASoC.

> I have a question.
> 
> As you reviewed before, currently, our driver consists of 2 parts,
> Soundcard driver and I2S driver.
> 
> Soundcard consists of 2 parts
>    - ALSA interface / control part
>    - CODEC control part
> 
> I2S driver consists of 4 parts
>   - HAL
>   - DMA control / interrupt control
>   - Soundcard interface part
>   - PCI interface function
> 
> According to "soc" Documentation,
> We must divide to 3 parts, platform driver, machine driver and codec driver.
> 
> So, I divided like the following parts.
> 
> platform driver
>   - ALSA interface / control part
>   - HAL
>   - DMA control / interrupt control
>   - Soundcard interface part
>   - PCI interface function
> 
> machine driver
>   - (none)
> 
> codec driver
>    - CODEC control part
> 
> Is the above dividing true ?

Not really.  ASoC is designed to be much more modular.  Take a look at
the codes in sound/soc/*.  You'll grasp how the components are split.
(The documents in Documentation/sound/alsa/soc/ are slightly
 obsoleted...)

The conversion of PCM part is usually straightforward, as found in
*-pcm.c.  The DAI setup depends on the hardware implementation.
That's for machine driver.  But it's hard to tell in more details
until I see the actual code snippet you are working on...


Takashi

> 
> Thanks in advance.
> 
> -- tomoya ROHM Co., Ltd. ----- Original Message ----- From: "Takashi 
> Iwai" <tiwai@suse.de> To: "Toshiharu Okada" 
> <toshiharu-linux@dsn.okisemi.com> Cc: <perex@perex.cz>; 
> <alsa-devel@alsa-project.org>; <linux-kernel@vger.kernel.org>; 
> <qi.wang@intel.com>; <yong.y.wang@intel.com>; <joel.clark@intel.com>; 
> <kok.howg.ewe@intel.com>; <tomoya-linux@dsn.okisemi.com> Sent: 
> Wednesday, July 06, 2011 8:06 PM Subject: Re: [PATCH] Add SoundCard 
> driver for OKI SEMICONDUCTOR ML7213 IOH At Wed, 6 Jul 2011 19:27:47 
> +0900, Toshiharu Okada wrote:
>  > >
>  > >
>  > > This patch is for SoundCard driver of OKI SEMICONDUCTOR ML7213
>  > > IOH(Input/Output Hub).
>  > > These ML7213 IOH is companion chip for Intel Atom E6xx series.
>  > > ML7213 IOH is for IVI(In-Vehicle Infotainment) use.
>  > >
>  > > [About this driver]
>  > > Audio Codec does not exist in ML7213 IOH.
>  > > Therefore, this SoundCard driver controls ML26124 Audio Codec 
> connected by
>  > > I2S of ML7213 IOH.
>  > > This driver consists of two modules, an ALSA sound card driver and I2S
>  > > driver.
>  > > An ALSA sound card driver performs control of ML26124 by I2C of ML7213
>  > > IOH.
>  > > When another Audio Codec is connected to I2S of ML7213 IOH,
>  > > it can respond by change of I2C control of an ALSA sound card driver.
>  > >
>  > >
>  > > Signed-off-by: Toshiharu Okada <toshiharu-linux@dsn.okisemi.com>
> Thanks for the patch.
> 
> I just took a quick glance over the code, and wonder whether this kind
> of driver would fit better with ASoC framework.
> Have you considered the implementation on ASoC?
> 
> 
> thanks,
> 
> Takashi
> 
>  > > ---
>  > >  sound/drivers/Kconfig      |   34 ++
>  > >  sound/drivers/Makefile     |    8 +-
>  > >  sound/drivers/ioh_i2s.c    | 1310
>  > > ++++++++++++++++++++++++++++++++++++++++++++
>  > >  sound/drivers/ioh_i2s.h    |  116 ++++
>  > >  sound/drivers/ml7213-ioh.c |  985 +++++++++++++++++++++++++++++++++
>  > >  5 files changed, 2452 insertions(+), 1 deletions(-)
>  > >  create mode 100644 sound/drivers/ioh_i2s.c
>  > >  create mode 100644 sound/drivers/ioh_i2s.h
>  > >  create mode 100644 sound/drivers/ml7213-ioh.c
>  > >
>  > > diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
>  > > index c896116..e098a91 100644
>  > > --- a/sound/drivers/Kconfig
>  > > +++ b/sound/drivers/Kconfig
>  > > @@ -209,6 +209,39 @@ config SND_AC97_POWER_SAVE
>  > >
>  > >    See Documentation/sound/alsa/powersave.txt for more details.
>  > >
>  > > +config ML7213_I2S
>  > > + tristate "OKI SEMICONDUCTOR ML7213 IOH I2S Driver"
>  > > + help
>  > > +   This driver is OKI SEMICONDUCTOR ML7213 IOH I2S driver.
>  > > +   ML7213 is companion chip for Intel Atom E6xx series.
>  > > +   This driver is required to use ML7213 SoundCard.
>  > > +
>  > > +   To compile this driver as a module, choose M here: the module
>  > > +   will be called ioh_i2s.
>  > > +
>  > > +config ML7213_I2S_DEBUG
>  > > + bool "ML7213 I2S driver debug"
>  > > + depends on ML7213_I2S
>  > > + default n
>  > > + help
>  > > +   This option enables the addition of a debugging code to
>  > > +   the OKI SEMICONDUCTOR ML7213 IOH I2S Driver. If you are unsure, 
> say N.
>  > > +
>  > > +   To compile this driver as a debugging module, choose Y here: the
>  > > module
>  > > +   will be called ioh_i2s.
>  > > +
>  > > +config SND_ML7213_I2S
>  > > + tristate "OKI SEMICONDUCTOR ML7213 SoundCard Driver for ML26124 
> Audio
>  > > Codec"
>  > > + depends on ML7213_I2S
>  > > + default y
>  > > + help
>  > > +   This driver is OKI SEMICONDUCTOR ML7213 IOH SoundCard driver
>  > > +   who controls ML26124 Audio Codec connected by I2S of ML7213 IOH.
>  > > +   Control of ML26124 uses I2C of ML7213 IOH.
>  > > +
>  > > +   To compile this driver as a module, choose M here: the module
>  > > +   will be called snd-ml7213ioh.
>  > > +
>  > >  config SND_AC97_POWER_SAVE_DEFAULT
>  > >  int "Default time-out for AC97 power-save mode"
>  > >  depends on SND_AC97_POWER_SAVE
>  > > @@ -219,4 +252,5 @@ config SND_AC97_POWER_SAVE_DEFAULT
>  > >
>  > >    See SND_AC97_POWER_SAVE for more details.
>  > >
>  > > +
>  > >  endif # SND_DRIVERS
>  > > diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
>  > > index 1a8440c..41350ea 100644
>  > > --- a/sound/drivers/Makefile
>  > > +++ b/sound/drivers/Makefile
>  > > @@ -11,15 +11,21 @@ snd-portman2x4-objs := portman2x4.o
>  > >  snd-serial-u16550-objs := serial-u16550.o
>  > >  snd-virmidi-objs := virmidi.o
>  > >  snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o
>  > > +snd-ml7213ioh-objs := ml7213-ioh.o
>  > >
>  > >  # Toplevel Module Dependency
>  > >  obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
>  > >  obj-$(CONFIG_SND_ALOOP) += snd-aloop.o
>  > >  obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o
>  > >  obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
>  > > +obj-$(CONFIG_SND_ML7213_I2S) += snd-ml7213ioh.o
>  > >  obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
>  > >  obj-$(CONFIG_SND_MTS64) += snd-mts64.o
>  > >  obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
>  > >  obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o
>  > > -
>  > >  obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/
>  > > +obj-$(CONFIG_ML7213_I2S) += ioh_i2s.o
>  > > +ifeq ($(CONFIG_ML7213_I2S_DEBUG),y)
>  > > +EXTRA_CFLAG += -DDEBUG
>  > > +endif
>  > > +
>  > > diff --git a/sound/drivers/ioh_i2s.c b/sound/drivers/ioh_i2s.c
>  > > new file mode 100644
>  > > index 0000000..a40f6df
>  > > --- /dev/null
>  > > +++ b/sound/drivers/ioh_i2s.c
>  > > @@ -0,0 +1,1310 @@
>  > > +/*
>  > > + * Copyright (C) 2010 OKI 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/slab.h>
>  > > +#include <linux/module.h>
>  > > +#include <linux/io.h>
>  > > +#include <linux/ctype.h>
>  > > +#include <linux/interrupt.h>
>  > > +#include <linux/device.h>
>  > > +
>  > > +#include "ioh_i2s.h"
>  > > +
>  > > +#include <linux/dmaengine.h>
>  > > +#include <linux/dma-mapping.h>
>  > > +#include <linux/scatterlist.h>
>  > > +#include <linux/completion.h>
>  > > +#include <linux/delay.h>
>  > > +
>  > > +#include <linux/string.h>
>  > > +#include <linux/timer.h>
>  > > +#include <linux/workqueue.h>
>  > > +
>  > > +#include <linux/pci.h>
>  > > +#include <linux/pch_dma.h>
>  > > +
>  > > +#define MAX_I2S_IF 6 /*I2S0 ~ I2S5*/
>  > > +
>  > > +#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
>  > > +
>  > > +/* 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;
>  > > + atomic_t rx_busy;
>  > > + atomic_t tx_busy;
>  > > +
>  > > + int ignore_rx_overrun;
>  > > +
>  > > + /* Transmit side DMA */
>  > > + atomic_t pending_tx;
>  > > +
>  > > + struct ioh_dma_config *dma_config;
>  > > +
>  > > + char rx_name[16];
>  > > + char tx_name[16];
>  > > +
>  > > + 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 */
>  > > +
>  > > + void *rxbuf_virt;
>  > > + void *txbuf_virt;
>  > > + unsigned char *tx_tail;
>  > > + unsigned char *tx_head;
>  > > + unsigned char *tx_data_head;
>  > > + unsigned char *tx_complete;
>  > > + unsigned char *rx_tail;
>  > > + unsigned char *rx_head;
>  > > + unsigned char *rx_data_head;
>  > > + unsigned char *rx_complete;
>  > > + 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;
>  > > +
>  > > + spinlock_t tx_lock;
>  > > +
>  > > + struct pch_dma_slave param_tx;
>  > > + struct pch_dma_slave param_rx;
>  > > + unsigned int mapbase;
>  > > +
>  > > + void *rx_callback_data;
>  > > + void (*rx_done) (void *callback_data, int status);
>  > > + void *tx_callback_data;
>  > > + void (*tx_done) (void *callback_data, int status);
>  > > +
>  > > + unsigned int tx_lower_data_flag;
>  > > +
>  > > + int dma_tx_unit; /* 1Byte of 2Byte or 4Byte */
>  > > +
>  > > + int dma_tx_flag; /* Now waiting tx DMA completion */
>  > > + int dma_rx_flag; /* Now waiting rx DMA completion */
>  > > +};
>  > > +
>  > > +static struct ioh_i2s_data devs[MAX_I2S_IF];
>  > > +
>  > > 
> +/******************************************************************************
>  > > + HAL (Hardware Abstruction Layer)
>  > > 
> +*******************************************************************************/
>  > > +static void ioh_i2s_reset(struct ioh_i2s_data *priv)
>  > > +{
>  > > + int channel = priv->ch;
>  > > +
>  > > + iowrite32(1 << channel, 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);
>  > > +}
>  > > +
>  > > 
> +/******************************************************************************
>  > > + DMA Functions
>  > > 
> +*******************************************************************************/
>  > > +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_common(
>  > > + struct ioh_i2s_data *priv,
>  > > + char *chan_name,
>  > > + 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 */
>  > > + priv->param_rx.width = PCH_DMA_WIDTH_2_BYTES;
>  > > + priv->param_rx.dma_dev = &dma_dev->dev;
>  > > + priv->param_rx.chan_id = priv->ch * 2 + 1; /* ch Rx=1,3,...11 */
>  > > + priv->param_rx.rx_reg = (dma_addr_t)((char *)priv->mapbase +\
>  > > + priv->ch * 0x800 +\
>  > > + I2SDRRXMIRROR_OFFSET);
>  > > + chan = dma_request_channel(mask, filter, &priv->param_rx);
>  > > + if (chan == NULL) {
>  > > + dev_err(priv->dev, "Failed dma_request_channel %s for"
>  > > + " I2S %s\n", chan_name, priv->rx_name);
>  > > + return NULL;
>  > > + }
>  > > + priv->chan_rx = chan;
>  > > +
>  > > + } else if (dir == DMA_TO_DEVICE) { /* Tx */
>  > > + priv->param_tx.width = PCH_DMA_WIDTH_2_BYTES;
>  > > + priv->param_tx.dma_dev = &dma_dev->dev;
>  > > + priv->param_tx.chan_id = priv->ch * 2; /* DMA ch Tx=0,2,...10 */
>  > > + priv->param_tx.tx_reg = (dma_addr_t)((char *)priv->mapbase +\
>  > > + priv->ch * 0x800 +\
>  > > + I2SDRTXMIRROR_OFFSET);
>  > > + chan = dma_request_channel(mask, filter, &priv->param_tx);
>  > > + if (chan == NULL) {
>  > > + dev_err(priv->dev, "Failed dma_request_channel %s for"
>  > > + " I2S %s\n", chan_name, priv->tx_name);
>  > > + return NULL;
>  > > + }
>  > > + priv->chan_tx = chan;
>  > > + } else {
>  > > + dev_err(priv->dev, "%s:Invalid direction (%d)\n",
>  > > + chan_name, dir);
>  > > + return NULL;
>  > > + }
>  > > +
>  > > + return chan;
>  > > +}
>  > > +
>  > > +static void ioh_setup_rx_dma(struct ioh_i2s_data *priv)
>  > > +{
>  > > + ioh_request_dma_channel_common(
>  > > + priv,
>  > > + priv->dma_config->rx_chan,
>  > > + DMA_FROM_DEVICE);
>  > > +}
>  > > +
>  > > +static void ioh_setup_tx_dma(struct ioh_i2s_data *priv)
>  > > +{
>  > > + ioh_request_dma_channel_common(
>  > > + priv,
>  > > + priv->dma_config->tx_chan,
>  > > + DMA_TO_DEVICE);
>  > > +}
>  > > +
>  > > +static void i2s_dma_tx_complete(void *arg)
>  > > +{
>  > > + struct ioh_i2s_data *priv = arg;
>  > > + struct scatterlist *sg = priv->sg_tx_cur;
>  > > + int i;
>  > > +
>  > > + dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p sg_len=%d 
> num=%d",
>  > > + __func__, priv->tx_data_head, priv->tx_complete, sg_dma_len(sg),
>  > > + priv->tx_num);
>  > > + dma_sync_sg_for_cpu(priv->dev, sg, priv->tx_num, DMA_TO_DEVICE);
>  > > +
>  > > + for (i = 0; i < priv->tx_num; i++, sg++)
>  > > + priv->tx_complete += sg_dma_len(sg) * priv->dma_tx_unit;
>  > > +
>  > > + if (priv->tx_complete > priv->tx_tail) {
>  > > + priv->tx_complete =\
>  > > +      (char *)(((u32)priv->tx_complete &  0x00000fff) |\
>  > > +      ((u32)priv->tx_head & 0xfffff000));
>  > > + }
>  > > +
>  > > + dev_dbg(priv->dev, "-->data_complete=%p\n", priv->tx_complete);
>  > > +
>  > > + if ((priv->tx_complete - 1) == priv->tx_data_head)
>  > > + ioh_i2s_disable_interrupts(priv, IOH_PLAYBACK);
>  > > +
>  > > + async_tx_ack(priv->desc_tx);
>  > > + if (priv->tx_done)
>  > > + priv->tx_done(priv->tx_callback_data, IOH_EOK);
>  > > +
>  > > + ioh_i2s_enable_tx_empty_ir(priv);
>  > > + priv->dma_tx_flag = 0;
>  > > +}
>  > > +
>  > > +static void i2s_dma_rx_complete(void *arg)
>  > > +{
>  > > + struct ioh_i2s_data *priv = arg;
>  > > + struct scatterlist *sg = priv->sg_rx_cur;
>  > > +
>  > > + dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p 
> sg_dma_len=%d\n",
>  > > + __func__, priv->rx_data_head, priv->rx_complete,
>  > > + sg_dma_len(sg));
>  > > +
>  > > + priv->rx_data_head += sg_dma_len(sg) * priv->dma_tx_unit;
>  > > +
>  > > + dev_dbg(priv->dev, "-->data_head=%p\n", priv->rx_data_head);
>  > > +
>  > > + async_tx_ack(priv->desc_rx);
>  > > +
>  > > + dma_sync_sg_for_cpu(priv->dev, sg, 1, DMA_FROM_DEVICE);
>  > > +
>  > > + if (priv->rx_done)
>  > > + priv->rx_done(priv->rx_callback_data, IOH_EOK);
>  > > +
>  > > + ioh_i2s_enable_rx_full_ir(priv);
>  > > + priv->dma_rx_flag = 0;
>  > > +}
>  > > +
>  > > 
> +/******************************************************************************
>  > > + Intrrupt Functions
>  > > 
> +*******************************************************************************/
>  > > +void i2s_tx_almost_empty_ir(struct ioh_i2s_data *priv, int ch)
>  > > +{
>  > > + struct dma_async_tx_descriptor *desc;
>  > > + int num;
>  > > + int tx_data_index;
>  > > + int tx_comp_index;
>  > > + struct scatterlist *sg = priv->sg_tx_p;
>  > > +
>  > > + dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p\n", __func__,
>  > > + priv->tx_data_head, priv->tx_complete);
>  > > +
>  > > + tx_data_index = (((int)priv->tx_data_head) & 0xfff) /\
>  > > + (I2S_AEMPTY_THRESH * I2S_1PERIODS_BYTES);
>  > > + tx_comp_index = (((int)priv->tx_complete) & 0xfff) /\
>  > > + (I2S_AEMPTY_THRESH * I2S_1PERIODS_BYTES);
>  > > +
>  > > + if (tx_data_index > tx_comp_index)
>  > > + num = tx_data_index - tx_comp_index - 1;
>  > > + else if (tx_comp_index == (priv->tx_nent - 1))
>  > > + num = tx_data_index;
>  > > + else
>  > > + num = priv->tx_nent - tx_comp_index - 1;
>  > > +
>  > > + dev_dbg(priv->dev, "%s: head_index=%d complete_index=%d num=%d\n",
>  > > + __func__, tx_data_index, tx_comp_index, num);
>  > > +
>  > > + if (num < 1) { /* there is no data */
>  > > + priv->tx_lower_data_flag += 1;
>  > > + if (priv->tx_lower_data_flag >= 10) {
>  > > + priv->tx_lower_data_flag = 0;
>  > > + priv->tx_data_head = priv->tx_head;
>  > > + priv->tx_complete = priv->tx_tail;
>  > > + ioh_i2s_disable_interrupts(priv, IOH_PLAYBACK);
>  > > + ioh_i2s_disable_tx_empty_ir(priv);
>  > > + dev_dbg(priv->dev, "%s:No data to send\n", __func__);
>  > > + }
>  > > + return; /* No data to transmit */
>  > > + }
>  > > +
>  > > + priv->tx_lower_data_flag = 0;
>  > > +
>  > > + tx_comp_index = (tx_comp_index + 1) % (priv->tx_nent);
>  > > + sg = sg + tx_comp_index; /* Point head of sg must be sent */
>  > > + priv->sg_tx_cur = sg; /* Save tx condition */
>  > > + priv->tx_num = num; /* Save tx condition */
>  > > +
>  > > + dev_dbg(priv->dev, "%s: sg = sg + %d tx_nent=%d\n",
>  > > + __func__, tx_comp_index, priv->tx_nent);
>  > > +
>  > > + desc = priv->chan_tx->device->device_prep_slave_sg(priv->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);
>  > > + priv->dma_tx_flag = 1;
>  > > +
>  > > + dma_sync_sg_for_device(priv->dev, sg, num, DMA_TO_DEVICE);
>  > > + priv->desc_tx = desc;
>  > > + desc->callback = i2s_dma_tx_complete;
>  > > + desc->callback_param = priv;
>  > > +
>  > > + atomic_inc(&priv->pending_tx);
>  > > + desc->tx_submit(desc);
>  > > +
>  > > + dma_async_issue_pending(priv->chan_tx);
>  > > +}
>  > > +
>  > > +void i2s_rx_almost_full_ir(struct ioh_i2s_data *priv)
>  > > +{
>  > > + struct dma_async_tx_descriptor *desc;
>  > > + struct scatterlist *sg;
>  > > + int rx_data_index;
>  > > +
>  > > + sg = priv->sg_rx_p;
>  > > + rx_data_index = (((int)priv->rx_data_head) & 0xfff) /\
>  > > + (I2S_AFULL_THRESH * I2S_1PERIODS_BYTES);
>  > > + if (rx_data_index > priv->rx_nent)
>  > > + rx_data_index = 0;
>  > > +
>  > > + dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p 
> data_index=%d\n",
>  > > + __func__, priv->rx_data_head, priv->rx_complete, rx_data_index);
>  > > +
>  > > + sg += rx_data_index;
>  > > +
>  > > + desc = priv->chan_rx->device->device_prep_slave_sg(priv->chan_rx,
>  > > + sg, 1, DMA_FROM_DEVICE,
>  > > + DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
>  > > + if (!desc) {
>  > > + dev_err(priv->dev, "%s:device_prep_slave_sg Failed\n",
>  > > + __func__);
>  > > + return;
>  > > + }
>  > > +
>  > > + priv->sg_rx_cur = sg; /* Save rx condition */
>  > > + ioh_i2s_disable_rx_full_ir(priv);
>  > > + priv->dma_rx_flag = 1;
>  > > + dma_sync_sg_for_device(priv->dev, sg, 1, DMA_FROM_DEVICE);
>  > > +
>  > > + priv->desc_rx = desc;
>  > > + desc->callback = i2s_dma_rx_complete;
>  > > + desc->callback_param = priv;
>  > > + desc->tx_submit(desc);
>  > > + dma_async_issue_pending(priv->chan_rx);
>  > > +}
>  > > +
>  > > +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_rx(struct ioh_i2s_data *priv)
>  > > +{
>  > > + unsigned int status;
>  > > + int channel = priv->ch;
>  > > + int offset = channel * 0x800;
>  > > +
>  > > + if (!priv->dma_rx_flag) {
>  > > + 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);
>  > > + }
>  > > +}
>  > > +
>  > > +static inline void ioh_i2s_interrupt_sub_tx(struct ioh_i2s_data *priv)
>  > > +{
>  > > + unsigned int status;
>  > > + int channel = priv->ch;
>  > > + int offset = channel * 0x800;
>  > > +
>  > > + if (!priv->dma_tx_flag) {
>  > > + 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, channel);
>  > > +
>  > > + /*Clear the interrupt status */
>  > > + iowrite32(status, priv->iobase + I2SISTTX_OFFSET + offset);
>  > > + }
>  > > +}
>  > > +
>  > > +int ioh_i2s_event(struct ioh_i2s_data *priv)
>  > > +{
>  > > + u32 idisp;
>  > > + unsigned long flags;
>  > > + int ret = IRQ_NONE;
>  > > +
>  > > + spin_lock_irqsave(&priv->tx_lock, flags);
>  > > +
>  > > + idisp = ioread32(priv->iobase + I2SIDISP_OFFSET);
>  > > + if (idisp & BIT(priv->ch + 16)) {
>  > > + dev_dbg(priv->dev, "Rx%d interrupt occures\n", priv->ch);
>  > > + ioh_i2s_interrupt_sub_rx(priv);
>  > > + ret = IRQ_HANDLED;
>  > > + }
>  > > +
>  > > + if (idisp & BIT(priv->ch)) {
>  > > + dev_dbg(priv->dev, "Tx%d interrupt occures\n", priv->ch);
>  > > + ioh_i2s_interrupt_sub_tx(priv);
>  > > + ret = IRQ_HANDLED;
>  > > + }
>  > > +
>  > > + spin_unlock_irqrestore(&priv->tx_lock, flags);
>  > > +
>  > > + return ret;
>  > > +}
>  > > +
>  > > 
> +/******************************************************************************
>  > > + External Functions (Called by Sourdcard driver)
>  > > 
> +*******************************************************************************/
>  > > +void ioh_i2s_ignore_rx_overrun(struct ioh_i2s_data *priv)
>  > > +{
>  > > + priv->ignore_rx_overrun = 1;
>  > > +}
>  > > +EXPORT_SYMBOL_GPL(ioh_i2s_ignore_rx_overrun);
>  > > +
>  > > +struct ioh_i2s_data *ioh_i2s_open(int ch, enum ioh_direction dir,
>  > > +      const char *name, void *cbd,
>  > > +      void (*cb)(void *cbd, int status))
>  > > +{
>  > > + struct ioh_i2s_data *obj = NULL;
>  > > + struct scatterlist *sg;
>  > > + int rx_size;
>  > > + int rx_num;
>  > > + int tx_size;
>  > > + int tx_num;
>  > > + int i;
>  > > +
>  > > + 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;
>  > > + }
>  > > + obj = &devs[ch];
>  > > +
>  > > + obj->ignore_rx_overrun = 0;
>  > > + obj->dma_tx_unit = 2; /* Tx transmits wuth 2Byte Unit */
>  > > +
>  > > + if (dir) {
>  > > + /* Rx configuration */
>  > > + if (atomic_read(&obj->rx_busy)) {
>  > > + dev_err(obj->dev, "rx i2s%d have already opened\n", ch);
>  > > + atomic_dec(&obj->rx_busy);
>  > > + return 0;
>  > > + }
>  > > + atomic_inc(&obj->rx_busy);
>  > > +
>  > > + strcpy(obj->rx_name, name);
>  > > + ioh_setup_rx_dma(obj);
>  > > + if (!obj->chan_rx) {
>  > > + dev_err(obj->dev, "%s:ioh_setup_rx_dma failed\n",
>  > > + __func__);
>  > > + return NULL;
>  > > + }
>  > > +
>  > > + obj->rxbuf_virt = dma_alloc_coherent(obj->dev, PAGE_SIZE,
>  > > + &obj->rx_buf_dma, GFP_KERNEL);
>  > > + if (!obj->rxbuf_virt) {
>  > > + dev_err(obj->dev, "dma_alloc_coherent Failed\n");
>  > > + return NULL;
>  > > + }
>  > > +
>  > > + rx_size = I2S_AFULL_THRESH * I2S_1PERIODS_BYTES;
>  > > +
>  > > + /* The number of scatter list (Franction area is not used) */
>  > > + rx_num = PAGE_SIZE / rx_size;
>  > > +
>  > > + dev_dbg(obj->dev, "%s: rx: scatter_num=%d scatter_size=%d\n",
>  > > + __func__, rx_num, rx_size);
>  > > +
>  > > + obj->sg_rx_p =\
>  > > +        kzalloc(sizeof(struct scatterlist) *rx_num, GFP_ATOMIC);
>  > > +
>  > > + sg = obj->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(obj->rxbuf_virt), rx_size,
>  > > +     rx_size * i);
>  > > + sg_dma_len(sg) = rx_size / obj->dma_tx_unit;
>  > > + sg_dma_address(sg) = obj->rx_buf_dma + sg->offset;
>  > > + }
>  > > +
>  > > + obj->rx_head = (unsigned char *)obj->rxbuf_virt;
>  > > + obj->rx_tail = (unsigned char *)obj->rxbuf_virt +\
>  > > + rx_num * rx_size - 1;
>  > > + obj->rx_data_head = (unsigned char *)obj->rxbuf_virt;
>  > > + obj->rx_complete = (unsigned char *)obj->rx_tail;
>  > > +
>  > > + obj->rx_nent = rx_num;
>  > > + } else {
>  > > + /* Tx configuration */
>  > > + if (atomic_read(&obj->tx_busy)) {
>  > > + dev_err(obj->dev, "tx i2s%d have already opened\n", ch);
>  > > + atomic_dec(&obj->tx_busy);
>  > > + return 0;
>  > > + }
>  > > + atomic_inc(&obj->tx_busy);
>  > > +
>  > > + strcpy(obj->tx_name, name);
>  > > + ioh_setup_tx_dma(obj);
>  > > + if (!obj->chan_tx) {
>  > > + dev_err(obj->dev, "%s:ioh_setup_tx_dma failed\n",
>  > > + __func__);
>  > > + return NULL;
>  > > + }
>  > > +
>  > > + obj->txbuf_virt = dma_alloc_coherent(obj->dev, PAGE_SIZE,
>  > > + &obj->tx_buf_dma, GFP_KERNEL);
>  > > +
>  > > + if (!obj->txbuf_virt) {
>  > > + dev_err(obj->dev, "dma_alloc_coherent Failed\n");
>  > > + return NULL;
>  > > + }
>  > > +
>  > > + obj->tx_head = (unsigned char *)obj->txbuf_virt;
>  > > + obj->tx_tail = (unsigned char *)obj->txbuf_virt + PAGE_SIZE - 1;
>  > > + obj->tx_data_head = (unsigned char *)obj->txbuf_virt;
>  > > + obj->tx_complete = (unsigned char *)obj->tx_tail;
>  > > +
>  > > + tx_size = I2S_AEMPTY_THRESH * I2S_1PERIODS_BYTES;
>  > > + if (PAGE_SIZE % tx_size)
>  > > + /* tx_num = The number of scatter list */
>  > > + tx_num = PAGE_SIZE / tx_size + 1;
>  > > + else
>  > > + tx_num = PAGE_SIZE / tx_size;
>  > > +
>  > > + dev_dbg(obj->dev, "%s: tx: scatter_num=%d scatter_size=%d\n",
>  > > + __func__, tx_num, tx_size);
>  > > +
>  > > + obj->sg_tx_p =\
>  > > +        kzalloc(sizeof(struct scatterlist) *tx_num, GFP_ATOMIC);
>  > > +
>  > > + sg_init_table(obj->sg_tx_p, tx_num); /* Initialize SG table */
>  > > + sg = obj->sg_tx_p;
>  > > +
>  > > + for (i = 0; i < tx_num; i++, sg++) {
>  > > + if (i == (tx_num - 1)) {
>  > > + if (PAGE_SIZE % tx_size) {
>  > > + sg_set_page(sg,
>  > > +   virt_to_page(obj->txbuf_virt),
>  > > +   PAGE_SIZE % tx_size,
>  > > +   tx_size * i);
>  > > + sg_dma_len(sg) = (PAGE_SIZE % tx_size)\
>  > > + / obj->dma_tx_unit;
>  > > + } else {
>  > > + sg_set_page(sg,
>  > > +   virt_to_page(obj->txbuf_virt),
>  > > +   tx_size, tx_size * i);
>  > > + sg_dma_len(sg) = tx_size\
>  > > + / obj->dma_tx_unit;
>  > > + }
>  > > + } else {
>  > > + sg_set_page(sg, virt_to_page(obj->txbuf_virt),
>  > > +     tx_size, tx_size * i);
>  > > + sg_dma_len(sg) = tx_size / obj->dma_tx_unit;
>  > > + }
>  > > + sg_dma_address(sg) = obj->tx_buf_dma + sg->offset;
>  > > + }
>  > > + obj->tx_nent = tx_num;
>  > > + }
>  > > +
>  > > + return obj;
>  > > +}
>  > > +EXPORT_SYMBOL_GPL(ioh_i2s_open);
>  > > +
>  > > +void ioh_i2s_release(struct ioh_i2s_data *priv, enum ioh_direction 
> dir)
>  > > +{
>  > > + 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);
>  > > + if (priv->chan_rx) {
>  > > + priv->chan_rx->device->device_control(priv->chan_rx,
>  > > +      DMA_TERMINATE_ALL,
>  > > +      0);
>  > > + dma_release_channel(priv->chan_rx);
>  > > + priv->chan_rx = NULL;
>  > > + }
>  > > +
>  > > + kfree(priv->sg_rx_p);
>  > > + if (priv->rxbuf_virt)
>  > > + dma_free_coherent(priv->dev, PAGE_SIZE,
>  > > +   priv->rxbuf_virt,
>  > > +   priv->rx_buf_dma);
>  > > +
>  > > + priv->rxbuf_virt = NULL;
>  > > + priv->rx_buf_dma = 0;
>  > > + atomic_dec(&priv->rx_busy);
>  > > +
>  > > + } else {
>  > > + ioh_i2s_disable_interrupts(priv, IOH_PLAYBACK);
>  > > + ioh_i2s_disable_tx_empty_ir(priv);
>  > > + if (priv->chan_tx) {
>  > > + priv->chan_tx->device->device_control(priv->chan_tx,
>  > > +      DMA_TERMINATE_ALL,
>  > > +      0);
>  > > + dma_release_channel(priv->chan_tx);
>  > > + priv->chan_tx = NULL;
>  > > + }
>  > > +
>  > > + kfree(priv->sg_tx_p);
>  > > + if (priv->txbuf_virt)
>  > > + dma_free_coherent(priv->dev, PAGE_SIZE,
>  > > +   priv->txbuf_virt,
>  > > +   priv->tx_buf_dma);
>  > > +
>  > > + priv->txbuf_virt = NULL;
>  > > + priv->tx_buf_dma = 0;
>  > > + atomic_dec(&priv->tx_busy);
>  > > + }
>  > > +}
>  > > +EXPORT_SYMBOL_GPL(ioh_i2s_release);
>  > > +
>  > > +
>  > > +void ioh_i2s_configure_i2s_regs(struct ioh_i2s_data *priv,
>  > > +     unsigned long bitrate,
>  > > +     struct ioh_i2s_config_reg *config,
>  > > +     enum ioh_direction dir)
>  > > +{
>  > > + int ch = priv->ch;
>  > > + int offset = ch * 0x800;
>  > > +
>  > > + /* Common register */
>  > > + iowrite32(config->cmn.i2sclkcnt,
>  > > +   priv->iobase + I2SCLKCNT0_OFFSET + 0x10*ch);
>  > > +
>  > > + if (dir) {
>  > > + /* 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 {
>  > > + /* 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);
>  > > + }
>  > > +}
>  > > +EXPORT_SYMBOL_GPL(ioh_i2s_configure_i2s_regs);
>  > > +
>  > > +void ioh_i2s_write(struct ioh_i2s_data *priv,
>  > > +        const void *data,
>  > > +        int len,
>  > > +        void *callback_data,
>  > > +        void (*done) (void *callback_data, int status))
>  > > +{
>  > > + int rem1;
>  > > + int rem2;
>  > > +
>  > > + priv->tx_callback_data = callback_data;
>  > > + priv->tx_done = done;
>  > > +
>  > > + dev_dbg(priv->dev, "%s: [ch%d] len=%d data_head=%p data_complete=%p",
>  > > + __func__, priv->ch, len, priv->tx_data_head, priv->tx_complete);
>  > > +
>  > > + if (priv->tx_data_head > priv->tx_tail)
>  > > + priv->tx_data_head = priv->tx_head;
>  > > +
>  > > + if ((priv->tx_data_head + len - 1) <= priv->tx_tail) {
>  > > + memcpy(priv->tx_data_head, data, len);
>  > > + priv->tx_data_head += len;
>  > > + } else {
>  > > + rem1 = priv->tx_tail - priv->tx_data_head + 1;
>  > > + rem2 = len - rem1;
>  > > + memcpy(priv->tx_data_head, data, rem1);
>  > > + priv->tx_data_head = priv->tx_head;
>  > > + memcpy(priv->tx_data_head, data + rem1, rem2);
>  > > + priv->tx_data_head += rem2;
>  > > + }
>  > > + dev_dbg(priv->dev, "-->data_head=%p\n", priv->tx_data_head);
>  > > +
>  > > + ioh_i2s_clear_tx_sts_ir(priv);
>  > > + ioh_i2s_tx_clear_dma_mask(priv);
>  > > + ioh_i2s_enable_tx_empty_ir(priv);
>  > > + ioh_i2s_enable_interrupts(priv, IOH_PLAYBACK);
>  > > +}
>  > > +EXPORT_SYMBOL_GPL(ioh_i2s_write);
>  > > +
>  > > +void ioh_i2s_read(struct ioh_i2s_data *priv,
>  > > +       void *data,
>  > > +       int len,
>  > > +       void *callback_data,
>  > > +       void (*done) (void *callback_data, int status))
>  > > +{
>  > > + int rx_ready_bytes;
>  > > + unsigned int rem1 = 0, rem2 = 0;
>  > > + char *rem;
>  > > + char *copy_head;
>  > > +
>  > > + priv->rx_callback_data = callback_data;
>  > > + priv->rx_done = done;
>  > > +
>  > > + ioh_i2s_clear_rx_sts_ir(priv);
>  > > + ioh_i2s_rx_clear_dma_mask(priv);
>  > > + ioh_i2s_enable_rx_full_ir(priv);
>  > > + ioh_i2s_enable_interrupts(priv, IOH_CAPTURE);
>  > > +
>  > > + dev_dbg(priv->dev, "%s: [ch%d]data_head=%p data_complete=%p 
> len=%d\n",
>  > > + __func__, priv->ch, priv->rx_data_head, priv->rx_complete, len);
>  > > +
>  > > + if (((u32)priv->rx_data_head & 0xffff) ==\
>  > > +       ((u32)(priv->rx_complete + 1) & 0xffff)) {
>  > > + dev_dbg(priv->dev, "%s:No data to read\n", __func__);
>  > > + return;
>  > > + }
>  > > +
>  > > + if (((u32)(priv->rx_complete + 1) & 0xfff) ==\
>  > > +        (((u32)priv->rx_head) & 0xfff)) {
>  > > + rx_ready_bytes = priv->rx_data_head - priv->rx_head;
>  > > + } else if (priv->rx_complete < priv->rx_data_head) {
>  > > + rx_ready_bytes = priv->rx_data_head - priv->rx_complete - 1;
>  > > + } else {
>  > > + rem1 = priv->rx_tail - priv->rx_complete;
>  > > + if (rem1 < len)
>  > > + rem2 = priv->rx_data_head - priv->rx_head;
>  > > +
>  > > + dev_dbg(priv->dev, "%s:rem1=%d rem2=%d\n",
>  > > + __func__, rem1, rem2);
>  > > + rx_ready_bytes = rem1 + rem2;
>  > > + }
>  > > +
>  > > + dev_dbg(priv->dev,
>  > > + "%s:rx_ready_bytes=%d(%d, %d) head_index=%d com_index=%d\n",
>  > > + __func__, rx_ready_bytes, rem1, rem2,
>  > > + (((int)priv->rx_data_head) & 0xfff) /\
>  > > + (I2S_AFULL_THRESH * I2S_1PERIODS_BYTES),
>  > > + (((int)priv->rx_complete) & 0xfff) /\
>  > > +        (I2S_AFULL_THRESH * I2S_1PERIODS_BYTES));
>  > > +
>  > > + if (priv->rx_data_head > priv->rx_tail)
>  > > + priv->rx_data_head = priv->rx_head;
>  > > +
>  > > + if (len > rx_ready_bytes) {
>  > > + dev_dbg(priv->dev, "%s:Not enough data in bufffer\n", __func__);
>  > > + return;
>  > > + }
>  > > +
>  > > + if (rem2) {
>  > > + memcpy(data, priv->rx_complete + 1, rem1);
>  > > +
>  > > + rem = (char *)data + rem1;
>  > > + memcpy(rem, priv->rx_head, len - rem1);
>  > > + priv->rx_complete = priv->rx_head + len - rem1 - 1;
>  > > + } else {
>  > > + copy_head = (char *)(((u32)(priv->rx_complete + 1) & 0xfff) |\
>  > > +     ((u32)priv->rx_head & 0xfffff000));
>  > > + memcpy(data, copy_head, len);
>  > > + priv->rx_complete += len;
>  > > + }
>  > > +
>  > > + priv->rx_complete = (char *)((((u32)priv->rx_complete) & 
> 0x00000fff) |\
>  > > +     (((u32)priv->rx_head) & 0xfffff000));
>  > > +
>  > > + dev_dbg(priv->dev, "com_index=%d data_complete=%p\n",
>  > > + (((int)priv->rx_complete) & 0xfff) /\
>  > > + (I2S_AFULL_THRESH * I2S_1PERIODS_BYTES),
>  > > + priv->rx_complete);
>  > > +
>  > > +}
>  > > +EXPORT_SYMBOL_GPL(ioh_i2s_read);
>  > > +
>  > > 
> +/******************************************************************************
>  > > + PCI Functions
>  > > 
> +*******************************************************************************/
>  > > +struct ioh_i2s_data *ioh_i2s_create(struct device *dev, void *iobase,
>  > > +        unsigned int mapbase, int ch)
>  > > +{
>  > > + struct ioh_i2s_data *obj;
>  > > +
>  > > + dev_dbg(dev, "Instantiate an i2s instance with io at v0x%p.\n", 
> iobase);
>  > > +
>  > > + obj = &devs[ch];
>  > > + obj->iobase = iobase;
>  > > + obj->mapbase = mapbase;
>  > > + obj->ch = ch;
>  > > + obj->dev = dev;
>  > > +
>  > > + atomic_set(&obj->rx_busy, 0);
>  > > + atomic_set(&obj->tx_busy, 0);
>  > > + strcpy(obj->rx_name, "FREE");
>  > > + strcpy(obj->tx_name, "FREE");
>  > > +
>  > > + return obj;
>  > > +}
>  > > +
>  > > +void ioh_i2s_destroy(struct ioh_i2s_data *priv)
>  > > +{
>  > > +}
>  > > +
>  > > +#define DRV_NAME "ml7213_ioh_i2s"
>  > > +#define PCI_VENDOR_ID_ROHM 0X10DB
>  > > +#define PCI_DEVICE_ID_ML7213_I2S 0X8033
>  > > +
>  > > +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,}
>  > > +};
>  > > +
>  > > +struct ioh_i2s_data_pci {
>  > > + struct ioh_i2s_data *devs[MAX_I2S_IF];
>  > > + void __iomem *membase;
>  > > + unsigned int mapbase;
>  > > +};
>  > > +
>  > > +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 handled;
>  > > +
>  > > + do {
>  > > + int i;
>  > > +
>  > > + handled = 0;
>  > > + for (i = 0; i < MAX_I2S_IF; i++)
>  > > + handled |= ioh_i2s_event(drvdata->devs[i]);
>  > > +
>  > > + } while (handled);
>  > > +
>  > > + return IRQ_HANDLED;
>  > > +}
>  > > +
>  > > +static int ioh_i2s_pci_probe(struct pci_dev *pdev,
>  > > + const struct pci_device_id *id)
>  > > +{
>  > > + int rv = 0;
>  > > + int i;
>  > > + struct ioh_i2s_data_pci *drvdata;
>  > > + void __iomem *tbl;
>  > > + unsigned int mapbase;
>  > > +
>  > > + drvdata = kzalloc(sizeof(struct ioh_i2s_data_pci), 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);
>  > > + mapbase = pci_resource_start(pdev, 1);
>  > > + if (!mapbase) {
>  > > + rv = -ENOMEM;
>  > > + printk(KERN_ERR "pci_resource_start failed\n");
>  > > + goto out_pci_resource_start;
>  > > + }
>  > > + drvdata->membase = tbl;
>  > > + drvdata->mapbase = mapbase;
>  > > +
>  > > + for (i = 0; i < MAX_I2S_IF; i++) {
>  > > + drvdata->devs[i] =
>  > > +     ioh_i2s_create(&pdev->dev, tbl, mapbase, i);
>  > > +
>  > > + spin_lock_init(&drvdata->devs[i]->tx_lock);
>  > > + drvdata->devs[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_disable;
>  > > + }
>  > > +
>  > > + return rv;
>  > > +out_pci_resource_start:
>  > > + pci_iounmap(pdev, tbl);
>  > > +out_disable:
>  > > + 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;
>  > > +
>  > > + free_irq(pdev->irq, pdev);
>  > > + for (i = 0; i < MAX_I2S_IF; i++) {
>  > > + if (drvdata->devs[i]) {
>  > > + ioh_i2s_reset(drvdata->devs[i]);
>  > > + ioh_i2s_destroy(drvdata->devs[i]);
>  > > + }
>  > > + }
>  > > + pci_iounmap(pdev, drvdata->membase);
>  > > + pci_disable_device(pdev);
>  > > + pci_set_drvdata(pdev, NULL);
>  > > + kfree(drvdata);
>  > > +}
>  > > +
>  > > +static int ioh_i2s_pci_suspend(struct pci_dev *pdev, pm_message_t 
> state)
>  > > +{
>  > > + BUG_ON(1);
>  > > + return -EINVAL;
>  > > +}
>  > > +
>  > > +static int ioh_i2s_pci_resume(struct pci_dev *pdev)
>  > > +{
>  > > + BUG_ON(1);
>  > > + return -EINVAL;
>  > > +}
>  > > +
>  > > +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_LICENSE("GPL v2");
>  > > +MODULE_AUTHOR("OKI SEMICONDUCTOR ML7213 IOH");
>  > > +MODULE_DESCRIPTION("OKI SEMICONDUCTOR ML7213 IOH I2S driver");
>  > > +MODULE_DEVICE_TABLE(pci, ioh_pci_tbl);
>  > > +MODULE_VERSION("1.0");
>  > > diff --git a/sound/drivers/ioh_i2s.h b/sound/drivers/ioh_i2s.h
>  > > new file mode 100644
>  > > index 0000000..f652cef
>  > > --- /dev/null
>  > > +++ b/sound/drivers/ioh_i2s.h
>  > > @@ -0,0 +1,116 @@
>  > > +/*
>  > > + * Copyright (C) 2010 OKI 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 USE_CHANNELS_MIN 1
>  > > +#define USE_CHANNELS_MAX 2
>  > > +#define I2S_1PERIODS_BYTES 2 /* 1 period byte suze (2 means 16bit) */
>  > > +
>  > > +#define MAX_I2S_RX_CH 6
>  > > +#define MAX_I2S_TX_CH 6
>  > > +
>  > > +struct ioh_i2s_data;
>  > > +
>  > > +
>  > > +enum ioh_direction {
>  > > + IOH_PLAYBACK = 0,
>  > > + IOH_CAPTURE,
>  > > +};
>  > > +
>  > > +struct ioh_i2s_data *ioh_i2s_open(int id, enum ioh_direction dir,
>  > > +      const char *name, void *cbd,
>  > > +      void (*cb)(void *cbd, int status));
>  > > +void ioh_i2s_release(struct ioh_i2s_data *priv, enum ioh_direction 
> dir);
>  > > +void ioh_i2s_ignore_rx_overrun(struct ioh_i2s_data *priv);
>  > > +
>  > > +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;
>  > > +};
>  > > +
>  > > +void ioh_i2s_configure_i2s_regs(struct ioh_i2s_data *priv,
>  > > +       unsigned long bitrate,
>  > > +       struct ioh_i2s_config_reg *config,
>  > > +       enum ioh_direction dir);
>  > > +
>  > > +void ioh_i2s_write(struct ioh_i2s_data *priv,
>  > > +        const void *data,
>  > > +        int len,
>  > > +        void *callback_data,
>  > > +        void (*done) (void *callback_data, int status));
>  > > +
>  > > +void ioh_i2s_read(struct ioh_i2s_data *priv,
>  > > +       void *data,
>  > > +       int len,
>  > > +       void *callback_data,
>  > > +       void (*done) (void *callback_data, int status));
>  > > +
>  > > +#endif
>  > > diff --git a/sound/drivers/ml7213-ioh.c b/sound/drivers/ml7213-ioh.c
>  > > new file mode 100644
>  > > index 0000000..9ffb420
>  > > --- /dev/null
>  > > +++ b/sound/drivers/ml7213-ioh.c
>  > > @@ -0,0 +1,985 @@
>  > > +/*
>  > > + * Copyright (c) 2010-2011 by Wind River
>  > > + * Copyright (C) 2011 OKI SEMICONDUCTOR CO., LTD.
>  > > + *
>  > > + * This code was derived from the Wind River MSP/I2S audio capture 
> for
>  > > STA2X11.
>  > > + *
>  > > + * 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/init.h>
>  > > +#include <linux/err.h>
>  > > +#include <linux/platform_device.h>
>  > > +#include <linux/jiffies.h>
>  > > +#include <linux/slab.h>
>  > > +#include <linux/moduleparam.h>
>  > > +#include <linux/i2c.h>
>  > > +#include <sound/core.h>
>  > > +#include <sound/pcm.h>
>  > > +#include <sound/initval.h>
>  > > +
>  > > +#include "ioh_i2s.h"
>  > > +
>  > > +MODULE_LICENSE("GPL v2");
>  > > +MODULE_AUTHOR("OKI SEMICONDUCTOR ML7213 IOH");
>  > > +MODULE_DESCRIPTION("OKI SEMICONDUCTOR ML7213 IOH I2S driver");
>  > > +MODULE_SUPPORTED_DEVICE("{{ALSA,ml7213i2s sound card}}");
>  > > +
>  > > +#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE
>  > > +#define USE_RATE (SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_32000|\
>  > > + SNDRV_PCM_RATE_48000)
>  > > +#define USE_RATE_MIN 16000
>  > > +#define USE_RATE_MAX 48000
>  > > +#define MAX_BUFFER_SIZE (MAX_PERIOD_SIZE*USE_PERIODS_MAX)
>  > > +#define MIN_PERIOD_SIZE 64
>  > > +#define MAX_PERIOD_SIZE (I2S_AFULL_THRESH*2) /* 16bit(=1word=2Byte)
>  > > + FIFOsize=128word */
>  > > +
>  > > +#define USE_PERIODS_MIN 2
>  > > +#define USE_PERIODS_MAX 64
>  > > +
>  > > +#ifndef add_capture_constraints
>  > > +#define add_capture_constraints(x) 0
>  > > +#endif
>  > > +
>  > > +/* Direction configuration master(=1) or slave(=0) */
>  > > +#define I2S_WRITE_MASTER 1
>  > > +#define I2S_READ_MASTER (I2S_WRITE_MASTER)
>  > > +
>  > > +/* RW flag */
>  > > +#define SND_CAPTURE_SUBSTREAM 0
>  > > +#define SND_PLAYBACK_SUBSTREAM 1
>  > > +
>  > > +/* Codec Device Address */
>  > > +#define CODEC_DEV_ADDR (0x1A)
>  > > +
>  > > +static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
>  > > +static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
>  > > +static struct i2c_client *i2c;
>  > > +static struct i2c_board_info ioh_hwmon_info[] = {
>  > > + {I2C_BOARD_INFO("ioh_i2c-0", CODEC_DEV_ADDR+0)},
>  > > + {I2C_BOARD_INFO("ioh_i2c-1", CODEC_DEV_ADDR+0)},
>  > > + {I2C_BOARD_INFO("ioh_i2c-2", CODEC_DEV_ADDR+0)},
>  > > + {I2C_BOARD_INFO("ioh_i2c-3", CODEC_DEV_ADDR+0)},
>  > > + {I2C_BOARD_INFO("ioh_i2c-4", CODEC_DEV_ADDR+0)},
>  > > + {I2C_BOARD_INFO("ioh_i2c-5", CODEC_DEV_ADDR+0)},
>  > > +};
>  > > +
>  > > +static void setup_i2s_read(struct snd_pcm_substream *substream, 
> int ch);
>  > > +static void setup_i2s_write(struct snd_pcm_substream *substream, 
> int ch);
>  > > +
>  > > +module_param(index, int, 0444);
>  > > +MODULE_PARM_DESC(index, "Index value for ml7213i2s sound card.");
>  > > +module_param(id, charp, 0444);
>  > > +MODULE_PARM_DESC(id, "ID string for ml7213i2s sound card.");
>  > > +
>  > > +static int ignore_overrun = 1;
>  > > +module_param(ignore_overrun, int, 0444);
>  > > +MODULE_PARM_DESC(ignore_overrun, "ignore RX overruns (default=0)");
>  > > +
>  > > +static struct platform_device *_device;
>  > > +static struct mutex i2c_mutex;
>  > > +
>  > > +struct snd_ml7213i2s {
>  > > + struct snd_card *card;
>  > > + struct snd_pcm *pcm;
>  > > +};
>  > > +
>  > > +struct cbdata {
>  > > + struct ioh_i2s_data *priv;
>  > > + int stop;
>  > > + int cnt;
>  > > +};
>  > > +
>  > > +static int errors;
>  > > +
>  > > +struct snd_ml7213i2s_pcm {
>  > > + struct snd_ml7213i2s *ml7213i2s;
>  > > + spinlock_t lock;
>  > > + unsigned int irq_pos;
>  > > + unsigned int buf_pos;
>  > > + struct snd_pcm_substream *substream;
>  > > + struct cbdata cbd;              /* i2s callback info */
>  > > + unsigned int channels;
>  > > + unsigned int rw;
>  > > + unsigned int rate;
>  > > +};
>  > > +
>  > > +
>  > > +static void i2s_read_period(struct snd_pcm_substream *substream);
>  > > +static void i2s_write_period(struct snd_pcm_substream *substream);
>  > > +static void read_done(void *cbd, int status);
>  > > +static void write_done(void *cbd, int status);
>  > > +
>  > > +static inline void
>  > > +snd_card_ml7213i2s_pcm_i2s_capture_start(struct snd_pcm_substream
>  > > *substream)
>  > > +{
>  > > + i2s_read_period(substream);
>  > > +}
>  > > +
>  > > +static inline void
>  > > +snd_card_ml7213i2s_pcm_i2s_playback_start(struct snd_pcm_substream
>  > > *substream)
>  > > +{
>  > > + i2s_write_period(substream);
>  > > +}
>  > > +
>  > > +static int snd_card_ml7213i2s_pcm_capture_trigger
>  > > + (struct snd_pcm_substream *substream, int cmd)
>  > > +{
>  > > + struct snd_pcm_runtime *runtime = substream->runtime;
>  > > + struct snd_ml7213i2s_pcm *dpcm = runtime->private_data;
>  > > + int err = 0;
>  > > +
>  > > + spin_lock(&dpcm->lock);
>  > > + switch (cmd) {
>  > > + case SNDRV_PCM_TRIGGER_START:
>  > > + case SNDRV_PCM_TRIGGER_RESUME:
>  > > + dpcm->cbd.stop = 0;
>  > > + snd_card_ml7213i2s_pcm_i2s_capture_start(substream);
>  > > + break;
>  > > + case SNDRV_PCM_TRIGGER_STOP:
>  > > + case SNDRV_PCM_TRIGGER_SUSPEND:
>  > > + pr_debug("stop..\n");
>  > > + if (!dpcm->cbd.stop)
>  > > + dpcm->cbd.stop = 1;
>  > > + else
>  > > + pr_debug("already stopped %d\n", dpcm->cbd.stop);
>  > > + break;
>  > > + default:
>  > > + err = -EINVAL;
>  > > + break;
>  > > + }
>  > > + spin_unlock(&dpcm->lock);
>  > > + return 0;
>  > > +}
>  > > +
>  > > +
>  > > +static int snd_card_ml7213i2s_pcm_playback_trigger
>  > > + (struct snd_pcm_substream *substream, int cmd)
>  > > +{
>  > > + struct snd_pcm_runtime *runtime = substream->runtime;
>  > > + struct snd_ml7213i2s_pcm *dpcm = runtime->private_data;
>  > > + int err = 0;
>  > > +
>  > > + spin_lock(&dpcm->lock);
>  > > + switch (cmd) {
>  > > + case SNDRV_PCM_TRIGGER_START:
>  > > + case SNDRV_PCM_TRIGGER_RESUME:
>  > > + dpcm->cbd.stop = 0;
>  > > + snd_card_ml7213i2s_pcm_i2s_playback_start(substream);
>  > > + break;
>  > > + case SNDRV_PCM_TRIGGER_STOP:
>  > > + case SNDRV_PCM_TRIGGER_SUSPEND:
>  > > + pr_debug("stop..\n");
>  > > + if (!dpcm->cbd.stop)
>  > > + dpcm->cbd.stop = 1;
>  > > + else
>  > > + pr_debug("already stopped %d\n", dpcm->cbd.stop);
>  > > + break;
>  > > + default:
>  > > + err = -EINVAL;
>  > > + break;
>  > > + }
>  > > + spin_unlock(&dpcm->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_pcm *dpcm = runtime->private_data;
>  > > +
>  > > + dpcm->irq_pos = 0;
>  > > + dpcm->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_pcm *dpcm = runtime->private_data;
>  > > +
>  > > + return substream->runtime->period_size*dpcm->buf_pos;
>  > > +}
>  > > +
>  > > +static struct snd_pcm_hardware snd_card_ml7213i2s_capture = {
>  > > + .info = (SNDRV_PCM_INFO_MMAP |
>  > > + SNDRV_PCM_INFO_INTERLEAVED |
>  > > + SNDRV_PCM_INFO_RESUME |
>  > > + SNDRV_PCM_INFO_MMAP_VALID),
>  > > + .formats = USE_FORMATS,
>  > > + .rates = USE_RATE,
>  > > + .rate_min = USE_RATE_MIN,
>  > > + .rate_max = USE_RATE_MAX,
>  > > + .channels_min = USE_CHANNELS_MIN,
>  > > + .channels_max = USE_CHANNELS_MAX,
>  > > + .buffer_bytes_max = MAX_BUFFER_SIZE,
>  > > + .period_bytes_min = MIN_PERIOD_SIZE,
>  > > + .period_bytes_max = MAX_PERIOD_SIZE,
>  > > + .periods_min = USE_PERIODS_MIN,
>  > > + .periods_max = USE_PERIODS_MAX,
>  > > + .fifo_size = 0,
>  > > +};
>  > > +
>  > > +static void __snd_card_ml7213i2s_runtime_free(struct snd_pcm_runtime
>  > > *runtime)
>  > > +{
>  > > + struct snd_ml7213i2s_pcm *dpcm;
>  > > + static int cnt;
>  > > +
>  > > + dpcm = (struct snd_ml7213i2s_pcm *)runtime->private_data;
>  > > + /* FIXME: This is just a big ball of race right now...
>  > > + */
>  > > + if (!dpcm->cbd.stop)
>  > > + dpcm->cbd.stop = 1;
>  > > + else {
>  > > + while (dpcm->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_playback_free
>  > > + (struct snd_pcm_runtime *runtime)
>  > > +{
>  > > + struct snd_ml7213i2s_pcm *dpcm;
>  > > + dpcm = (struct snd_ml7213i2s_pcm *)runtime->private_data;
>  > > +
>  > > + __snd_card_ml7213i2s_runtime_free(runtime);
>  > > +
>  > > + ioh_i2s_release(dpcm->cbd.priv, IOH_PLAYBACK);
>  > > + kfree(runtime->private_data);
>  > > +}
>  > > +
>  > > +static void snd_card_ml7213i2s_runtime_capture_free
>  > > + (struct snd_pcm_runtime *runtime)
>  > > +{
>  > > + struct snd_ml7213i2s_pcm *dpcm;
>  > > + dpcm = (struct snd_ml7213i2s_pcm *)runtime->private_data;
>  > > +
>  > > + __snd_card_ml7213i2s_runtime_free(runtime);
>  > > +
>  > > + ioh_i2s_release(dpcm->cbd.priv, IOH_CAPTURE);
>  > > + kfree(runtime->private_data);
>  > > +}
>  > > +
>  > > +static int snd_card_codec_reg_read(unsigned char reg, unsigned 
> char *val)
>  > > +{
>  > > + unsigned char data;
>  > > +
>  > > + if (i2c_master_send(i2c, &reg, 1) != 1)
>  > > + return -1;
>  > > +
>  > > + if (i2c_master_recv(i2c, &data, 1) != 1)
>  > > + return -1;
>  > > +
>  > > + *val = data;
>  > > + return 0;
>  > > +}
>  > > +
>  > > +static int snd_card_codec_reg_write(unsigned char reg, unsigned 
> char val)
>  > > +{
>  > > + unsigned char buf[2] = {(reg|1), val};
>  > > +
>  > > + if (i2c_master_send(i2c, &buf[0], 2) != 2)
>  > > + return -1;
>  > > + return 0;
>  > > +}
>  > > +
>  > > +static int snd_card_codec_set(int number, unsigned int rate,
>  > > +       unsigned int channels)
>  > > +{
>  > > + unsigned char data;
>  > > +
>  > > + mutex_lock(&i2c_mutex);
>  > > +
>  > > + i2c = i2c_new_device(i2c_get_adapter(1), &ioh_hwmon_info[number]);
>  > > + if (!i2c) {
>  > > + mutex_unlock(&i2c_mutex);
>  > > + return -1;
>  > > + }
>  > > +
>  > > + snd_card_codec_reg_read(0x30, &data);
>  > > +
>  > > + snd_card_codec_reg_write(0x10, 0x01); /* soft reset assert */
>  > > + snd_card_codec_reg_write(0x10, 0x00); /* soft reset negate */
>  > > +
>  > > + snd_card_codec_reg_write(0x0c, 0x00);
>  > > +
>  > > + switch (rate) {
>  > > + case 16000:
>  > > + snd_card_codec_reg_write(0x00, 0x03);
>  > > + snd_card_codec_reg_write(0x02, 0x0c);
>  > > + snd_card_codec_reg_write(0x04, 0x00);
>  > > + snd_card_codec_reg_write(0x06, 0x20);
>  > > + snd_card_codec_reg_write(0x08, 0x00);
>  > > + snd_card_codec_reg_write(0x0a, 0x04);
>  > > + break;
>  > > + case 32000:
>  > > + snd_card_codec_reg_write(0x00, 0x06);
>  > > + snd_card_codec_reg_write(0x02, 0x0c);
>  > > + snd_card_codec_reg_write(0x04, 0x00);
>  > > + snd_card_codec_reg_write(0x06, 0x20);
>  > > + snd_card_codec_reg_write(0x08, 0x00);
>  > > + snd_card_codec_reg_write(0x0a, 0x04);
>  > > + break;
>  > > + case 48000:
>  > > + snd_card_codec_reg_write(0x00, 0x08);
>  > > + snd_card_codec_reg_write(0x02, 0x0c);
>  > > + snd_card_codec_reg_write(0x04, 0x00);
>  > > + snd_card_codec_reg_write(0x06, 0x30);
>  > > + snd_card_codec_reg_write(0x08, 0x00);
>  > > + snd_card_codec_reg_write(0x0a, 0x04);
>  > > + break;
>  > > + default:
>  > > + pr_err("%s:this rate is no support for ml26124\n", __func__);
>  > > + break;
>  > > + }
>  > > +
>  > > + snd_card_codec_reg_write(0x0c, 0x03);
>  > > + msleep(20);
>  > > + snd_card_codec_reg_write(0x0c, 0x0f);
>  > > + snd_card_codec_reg_write(0x0e, 0x04);
>  > > +
>  > > + snd_card_codec_reg_write(0x60, 0x00);
>  > > + snd_card_codec_reg_write(0x62, 0x00);
>  > > +
>  > > + snd_card_codec_reg_write(0x64, 0x00); /* master/slave */
>  > > +
>  > > + snd_card_codec_reg_write(0x20, 0x02);
>  > > + msleep(50);
>  > > +
>  > > + snd_card_codec_reg_write(0x20, 0x06);
>  > > + snd_card_codec_reg_write(0x22, 0x0a);
>  > > + snd_card_codec_reg_write(0x24, 0x02);
>  > > + snd_card_codec_reg_write(0x26, 0x13);
>  > > + snd_card_codec_reg_write(0x26, 0x1f);
>  > > + snd_card_codec_reg_write(0x28, 0x02);
>  > > +
>  > > + snd_card_codec_reg_write(0x54, 0x02);
>  > > + snd_card_codec_reg_write(0x5a, 0x00);
>  > > +
>  > > + snd_card_codec_reg_write(0x12, 0x01);
>  > > + snd_card_codec_reg_write(0x12, 0x03);
>  > > + msleep(20);
>  > > + snd_card_codec_reg_write(0x66, 0x03);
>  > > +
>  > > + snd_card_codec_reg_write(0x3a, 0x27);
>  > > + snd_card_codec_reg_write(0x32, 0x20);
>  > > +
>  > > + i2c_unregister_device(i2c);
>  > > +
>  > > + mutex_unlock(&i2c_mutex);
>  > > + return 0;
>  > > +}
>  > > +
>  > > +static int snd_card_codec_free(int number, unsigned int rate,
>  > > +        unsigned int channels)
>  > > +{
>  > > + mutex_lock(&i2c_mutex);
>  > > +
>  > > + i2c = i2c_new_device(i2c_get_adapter(1), &ioh_hwmon_info[number]);
>  > > + if (!i2c) {
>  > > + mutex_unlock(&i2c_mutex);
>  > > + return -1;
>  > > + }
>  > > +
>  > > + snd_card_codec_reg_write(0x10, 1); /* soft reset assert */
>  > > +
>  > > + i2c_unregister_device(i2c);
>  > > + mutex_unlock(&i2c_mutex);
>  > > + 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 = runtime->private_data;
>  > > +
>  > > + dpcm->channels = params_channels(hw_params);
>  > > + dpcm->rate = params_rate(hw_params);
>  > > +
>  > > +
>  > > + switch (dpcm->rw) {
>  > > + case SND_CAPTURE_SUBSTREAM:
>  > > + setup_i2s_read(substream, substream->number);
>  > > + break;
>  > > + case SND_PLAYBACK_SUBSTREAM:
>  > > + setup_i2s_write(substream, substream->number);
>  > > + break;
>  > > + default:
>  > > + return -1;
>  > > + }
>  > > +
>  > > + if (snd_card_codec_set(substream->number, dpcm->rate, 
> dpcm->channels))
>  > > + 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_pcm *dpcm = substream->runtime->private_data;
>  > > +
>  > > + if (snd_card_codec_free(substream->number, dpcm->rate, 
> dpcm->channels))
>  > > + return -1;
>  > > +
>  > > + return snd_pcm_lib_free_pages(substream);
>  > > +}
>  > > +
>  > > +static struct snd_ml7213i2s_pcm *
>  > > +new_pcm_stream(struct snd_pcm_substream *substream)
>  > > +{
>  > > + struct snd_ml7213i2s_pcm *dpcm;
>  > > +
>  > > + dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
>  > > + if (!dpcm)
>  > > + return dpcm;
>  > > + spin_lock_init(&dpcm->lock);
>  > > + dpcm->substream = substream;
>  > > + return dpcm;
>  > > +}
>  > > +
>  > > +static void i2s_read_period(struct snd_pcm_substream *substream)
>  > > +{
>  > > + struct snd_ml7213i2s_pcm *dpcm;
>  > > + struct cbdata *cbd;
>  > > + int period;
>  > > + void *read_ptr;
>  > > + int read_size;
>  > > +
>  > > + dpcm = substream->runtime->private_data;
>  > > + cbd = &dpcm->cbd;
>  > > +
>  > > + if (!cbd->priv)
>  > > + return;
>  > > +
>  > > + period = substream->runtime->period_size;
>  > > +
>  > > + read_ptr = substream->runtime->dma_area
>  > > +   +(snd_pcm_lib_period_bytes(substream) * dpcm->irq_pos);
>  > > + read_size = period * I2S_1PERIODS_BYTES * (dpcm->channels);
>  > > +
>  > > + ioh_i2s_read(cbd->priv,
>  > > + read_ptr,
>  > > + read_size,
>  > > + substream,
>  > > + read_done);
>  > > +
>  > > + dpcm->irq_pos = (dpcm->irq_pos + 1) % substream->runtime->periods;
>  > > +}
>  > > +
>  > > +static void i2s_write_period(struct snd_pcm_substream *substream)
>  > > +{
>  > > + struct snd_ml7213i2s_pcm *dpcm;
>  > > + struct cbdata *cbd;
>  > > + int period;
>  > > + void *write_ptr;
>  > > + int write_size;
>  > > +
>  > > + dpcm = substream->runtime->private_data;
>  > > + cbd = &dpcm->cbd;
>  > > +
>  > > + if (!cbd->priv)
>  > > + return;
>  > > +
>  > > + period = substream->runtime->period_size;
>  > > + write_ptr = substream->runtime->dma_area
>  > > +   +(snd_pcm_lib_period_bytes(substream) * dpcm->irq_pos);
>  > > + write_size = period * I2S_1PERIODS_BYTES * (dpcm->channels);
>  > > +
>  > > + ioh_i2s_write(cbd->priv,
>  > > + write_ptr,
>  > > + write_size,
>  > > + substream,
>  > > + write_done);
>  > > +
>  > > + dpcm->irq_pos = (dpcm->irq_pos + 1) % substream->runtime->periods;
>  > > +}
>  > > +
>  > > +static void read_done(void *callback_data, int status)
>  > > +{
>  > > + struct snd_pcm_substream *substream =
>  > > + (struct snd_pcm_substream *)callback_data;
>  > > + struct snd_ml7213i2s_pcm *dpcm;
>  > > + struct cbdata *cbd;
>  > > + dpcm = substream->runtime->private_data;
>  > > + cbd = &dpcm->cbd;
>  > > + switch (status) {
>  > > + case IOH_EOK:
>  > > + if (!cbd->stop) {
>  > > + i2s_read_period(substream);
>  > > + dpcm->buf_pos = (dpcm->buf_pos + 1) %
>  > > + substream->runtime->periods;
>  > > + snd_pcm_period_elapsed(dpcm->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");
>  > > + errors++;
>  > > + cbd->stop = 2;
>  > > + break;
>  > > + }
>  > > + if (cbd->stop)
>  > > + pr_debug("stopping... %d\n", cbd->stop);
>  > > +}
>  > > +
>  > > +static void write_done(void *callback_data, int status)
>  > > +{
>  > > + struct snd_pcm_substream *substream =
>  > > + (struct snd_pcm_substream *)callback_data;
>  > > + struct snd_ml7213i2s_pcm *dpcm;
>  > > + struct cbdata *cbd;
>  > > + 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;
>  > > + }
>  > > + dpcm = substream->runtime->private_data;
>  > > + cbd = &dpcm->cbd;
>  > > +
>  > > + switch (status) {
>  > > + case IOH_EOK:
>  > > + if (!cbd->stop) {
>  > > + i2s_write_period(substream);
>  > > + dpcm->buf_pos = (dpcm->buf_pos + 1) %
>  > > + substream->runtime->periods;
>  > > + snd_pcm_period_elapsed(dpcm->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;
>  > > + }
>  > > +}
>  > > +#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 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 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)
>  > > +
>  > > +static void i2s_set_default(struct ioh_i2s_config_reg *config)
>  > > +{
>  > > + /* Set ML7213 IOH register default value */
>  > > + config->cmn.i2simask = 0x003f003f;
>  > > +
>  > > + config->tx.i2saftx = 0x1F;
>  > > + config->tx.i2smsktx = 0x1F;
>  > > + config->tx.i2sisttx = 0xC;
>  > > +
>  > > + config->rx.i2scntrx = 0x3F;
>  > > + config->rx.i2smskrx = 0x1F;
>  > > + config->rx.i2sistrx = 0xC;
>  > > +}
>  > > +
>  > > +
>  > > +static int i2s_configure_rate(unsigned int rate)
>  > > +{
>  > > + int value;
>  > > +
>  > > + switch (rate) {
>  > > + /* LR=12288/MCLKFS, BCLKFS=channels*format */
>  > > + case 16000:
>  > > + value = I2SCLKCNT_MCLKFS_768FS | I2SCLKCNT_BCLKFS_32FS;
>  > > + break;
>  > > + case 32000:
>  > > + value = I2SCLKCNT_MCLKFS_384FS | I2SCLKCNT_BCLKFS_32FS;
>  > > + break;
>  > > + case 48000:
>  > > + value = I2SCLKCNT_MCLKFS_256FS | I2SCLKCNT_BCLKFS_32FS;
>  > > + break;
>  > > + default:
>  > > + value = I2SCLKCNT_MCLKFS_256FS | I2SCLKCNT_BCLKFS_32FS;
>  > > + break;
>  > > + }
>  > > + return value;
>  > > +}
>  > > +
>  > > +static void i2s_slave_configure_reg(struct ioh_i2s_config_reg *config,
>  > > +     unsigned int rate)
>  > > +{
>  > > + memset(config, 0, sizeof(*config));
>  > > +
>  > > + i2s_set_default(config);
>  > > +
>  > > + /* Configuration */
>  > > + config->tx.i2scnttx = I2SCNTTX_TXDABIT_16BIT;
>  > > + config->tx.i2saetx = I2S_AEMPTY_THRESH / 2; /* Almost empty 
> threshold */
>  > > + config->rx.i2scntrx = I2SCNTRX_RXDABIT_16BIT;
>  > > + config->rx.i2safrx = I2S_AFULL_THRESH / 2; /* Almost full 
> threshold */
>  > > + config->cmn.i2sclkcnt = i2s_configure_rate(rate);
>  > > +}
>  > > +
>  > > +static void i2s_master_configure_reg(struct ioh_i2s_config_reg 
> *config,
>  > > +      unsigned int rate)
>  > > +{
>  > > + memset(config, 0, sizeof(*config));
>  > > +
>  > > + i2s_set_default(config);
>  > > +
>  > > + /* Configuration */
>  > > + config->tx.i2scnttx = I2SCNTTX_TXDABIT_16BIT;
>  > > + config->tx.i2saetx = I2S_AEMPTY_THRESH / 2; /* Almost empty 
> threshold */
>  > > + config->rx.i2scntrx = I2SCNTRX_RXDABIT_16BIT;
>  > > + config->rx.i2safrx = I2S_AFULL_THRESH / 2; /* Almost full 
> threshold */
>  > > + config->cmn.i2sclkcnt = i2s_configure_rate(rate) | I2SCLKCNT_MSSEL;
>  > > +}
>  > > +
>  > > +static void setup_i2s_read(struct snd_pcm_substream *substream, 
> int ch)
>  > > +{
>  > > + struct snd_ml7213i2s_pcm *dpcm;
>  > > + struct ioh_i2s_config_reg config;
>  > > +
>  > > + dpcm = substream->runtime->private_data;
>  > > +
>  > > + dpcm->cbd.priv = ioh_i2s_open(ch, IOH_CAPTURE, "radio-i2s-in",
>  > > + substream,
>  > > + read_done);
>  > > +
>  > > + if (!dpcm->cbd.priv) {
>  > > + pr_err("%s: Cannot open the device\n", __func__);
>  > > + return;
>  > > + }
>  > > +
>  > > + if (ignore_overrun)
>  > > + ioh_i2s_ignore_rx_overrun(dpcm->cbd.priv);
>  > > +
>  > > + if (I2S_READ_MASTER)
>  > > + i2s_master_configure_reg(&config, dpcm->rate);
>  > > + else
>  > > + i2s_slave_configure_reg(&config, dpcm->rate);
>  > > +
>  > > + ioh_i2s_configure_i2s_regs(dpcm->cbd.priv, 0, &config, IOH_CAPTURE);
>  > > +}
>  > > +
>  > > +static void setup_i2s_write(struct snd_pcm_substream *substream, 
> int ch)
>  > > +{
>  > > + struct snd_ml7213i2s_pcm *dpcm;
>  > > + struct ioh_i2s_config_reg config;
>  > > +
>  > > + dpcm = substream->runtime->private_data;
>  > > +
>  > > + dpcm->cbd.priv = ioh_i2s_open(ch, IOH_PLAYBACK, "radio-i2s-out",
>  > > + substream,
>  > > + write_done);
>  > > +
>  > > + if (!dpcm->cbd.priv) {
>  > > + pr_err("%s: Cannot open the device\n", __func__);
>  > > + return;
>  > > + }
>  > > +
>  > > + if (ignore_overrun)
>  > > + ioh_i2s_ignore_rx_overrun(dpcm->cbd.priv);
>  > > +
>  > > + if (I2S_WRITE_MASTER)
>  > > + i2s_master_configure_reg(&config, dpcm->rate);
>  > > + else
>  > > + i2s_slave_configure_reg(&config, dpcm->rate);
>  > > +
>  > > + ioh_i2s_configure_i2s_regs(dpcm->cbd.priv, 0, &config, IOH_PLAYBACK);
>  > > +}
>  > > +
>  > > +static int snd_card_ml7213i2s_capture_open(struct snd_pcm_substream
>  > > *substream)
>  > > +{
>  > > + struct snd_pcm_runtime *runtime = substream->runtime;
>  > > + struct snd_ml7213i2s_pcm *dpcm;
>  > > + int err;
>  > > +
>  > > + dpcm = new_pcm_stream(substream);
>  > > + if (dpcm == NULL)
>  > > + return -ENOMEM;
>  > > +
>  > > + runtime->private_data = dpcm;
>  > > + /* makes the infrastructure responsible for freeing dpcm */
>  > > + runtime->private_free = snd_card_ml7213i2s_runtime_capture_free;
>  > > + runtime->hw = snd_card_ml7213i2s_capture;
>  > > +
>  > > + dpcm->rw = SND_CAPTURE_SUBSTREAM;
>  > > +
>  > > + err = add_capture_constraints(runtime);
>  > > + if (err < 0)
>  > > + return err;
>  > > +
>  > > + return 0;
>  > > +}
>  > > +
>  > > +static int snd_card_ml7213i2s_playback_open(struct snd_pcm_substream
>  > > *substream)
>  > > +{
>  > > + struct snd_pcm_runtime *runtime = substream->runtime;
>  > > + struct snd_ml7213i2s_pcm *dpcm;
>  > > + int err;
>  > > +
>  > > + dpcm = new_pcm_stream(substream);
>  > > + if (dpcm == NULL)
>  > > + return -ENOMEM;
>  > > +
>  > > + runtime->private_data = dpcm;
>  > > + /* makes the infrastructure responsible for freeing dpcm */
>  > > + runtime->private_free = snd_card_ml7213i2s_runtime_playback_free;
>  > > + runtime->hw = snd_card_ml7213i2s_capture;
>  > > +
>  > > + dpcm->rw = SND_PLAYBACK_SUBSTREAM;
>  > > +
>  > > + err = add_capture_constraints(runtime);
>  > > + if (err < 0)
>  > > + return err;
>  > > +
>  > > + return 0;
>  > > +}
>  > > +
>  > > +static int snd_card_ml7213i2s_capture_close(
>  > > + struct snd_pcm_substream *substream)
>  > > +{
>  > > + return 0;
>  > > +}
>  > > +
>  > > +static int snd_card_ml7213i2s_playback_close(
>  > > + struct snd_pcm_substream *substream)
>  > > +{
>  > > + return 0;
>  > > +}
>  > > +
>  > > +static struct snd_pcm_ops snd_card_ml7213i2s_capture_ops = {
>  > > + .open = snd_card_ml7213i2s_capture_open,
>  > > + .close = snd_card_ml7213i2s_capture_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_capture_trigger,
>  > > + .pointer = snd_card_ml7213i2s_pcm_pointer,
>  > > +};
>  > > +
>  > > +static struct snd_pcm_ops snd_card_ml7213i2s_playback_ops = {
>  > > + .open = snd_card_ml7213i2s_playback_open,
>  > > + .close = snd_card_ml7213i2s_playback_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_playback_trigger,
>  > > + .pointer = snd_card_ml7213i2s_pcm_pointer,
>  > > +};
>  > > +
>  > > +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;
>  > > + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
>  > > + &snd_card_ml7213i2s_capture_ops);
>  > > + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
>  > > + &snd_card_ml7213i2s_playback_ops);
>  > > + 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};
>  > > +
>  > > +static int __devinit snd_ml7213i2s_probe(struct platform_device 
> *devptr)
>  > > +{
>  > > + struct snd_card *card;
>  > > + struct snd_ml7213i2s *ml7213i2s;
>  > > + int err;
>  > > + int dev = devptr->id;
>  > > +
>  > > + err = snd_card_create(index, "ml7213i2s", THIS_MODULE,
>  > > +       sizeof(struct snd_ml7213i2s), &card);
>  > > +
>  > > + if (err < 0)
>  > > + return err;
>  > > + ml7213i2s = card->private_data;
>  > > + ml7213i2s->card = card;
>  > > + err = snd_card_ml7213i2s_pcm(ml7213i2s, 0);
>  > > + if (err < 0)
>  > > + goto __nodev;
>  > > +
>  > > + strcpy(card->driver, "ml7213i2s");
>  > > + strcpy(card->shortname, "ml7213i2s");
>  > > + sprintf(card->longname, "ml7213i2s %i", dev + 1);
>  > > +
>  > > + snd_card_set_dev(card, &devptr->dev);
>  > > +
>  > > + snd_device_new(card, SNDRV_DEV_LOWLEVEL, ml7213i2s, &ops);
>  > > +
>  > > + mutex_init(&i2c_mutex);
>  > > +
>  > > + err = snd_card_register(card);
>  > > + if (err == 0) {
>  > > + platform_set_drvdata(devptr, card);
>  > > + return 0;
>  > > + }
>  > > +__nodev:
>  > > + snd_card_free(card);
>  > > + return err;
>  > > +}
>  > > +
>  > > +static int __devexit snd_ml7213i2s_remove(struct platform_device 
> *devptr)
>  > > +{
>  > > + snd_card_free(platform_get_drvdata(devptr));
>  > > + platform_set_drvdata(devptr, NULL);
>  > > + return 0;
>  > > +}
>  > > +
>  > > +#ifdef CONFIG_PM
>  > > +static int snd_ml7213i2s_suspend(struct platform_device *pdev,
>  > > +   pm_message_t state)
>  > > +{
>  > > + struct snd_card *card = platform_get_drvdata(pdev);
>  > > + struct snd_ml7213i2s *ml7213i2s = card->private_data;
>  > > +
>  > > + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
>  > > + snd_pcm_suspend_all(ml7213i2s->pcm);
>  > > + return 0;
>  > > +}
>  > > +
>  > > +static int snd_ml7213i2s_resume(struct platform_device *pdev)
>  > > +{
>  > > + struct snd_card *card = platform_get_drvdata(pdev);
>  > > +
>  > > + snd_power_change_state(card, SNDRV_CTL_POWER_D0);
>  > > + return 0;
>  > > +}
>  > > +#endif
>  > > +
>  > > +
>  > > +#define snd_ml7213i2s_DRIVER "snd_ml7213i2s"
>  > > +
>  > > +static struct platform_driver snd_ml7213i2s_driver = {
>  > > + .probe = snd_ml7213i2s_probe,
>  > > + .remove = __devexit_p(snd_ml7213i2s_remove),
>  > > +#ifdef CONFIG_PM
>  > > + .suspend = snd_ml7213i2s_suspend,
>  > > + .resume = snd_ml7213i2s_resume,
>  > > +#endif
>  > > + .driver = {
>  > > + .name = snd_ml7213i2s_DRIVER
>  > > + },
>  > > +};
>  > > +
>  > > +static void snd_ml7213i2s_unregister(void)
>  > > +{
>  > > + platform_device_unregister(_device);
>  > > + platform_driver_unregister(&snd_ml7213i2s_driver);
>  > > +}
>  > > +
>  > > +static int __init alsa_card_ml7213i2s_init(void)
>  > > +{
>  > > + int  err;
>  > > + struct platform_device *device;
>  > > +
>  > > + err = platform_driver_register(&snd_ml7213i2s_driver);
>  > > + if (err < 0)
>  > > + return err;
>  > > +
>  > > + device = platform_device_register_simple(snd_ml7213i2s_DRIVER,
>  > > + 0, NULL, 0);
>  > > + if (IS_ERR(device))
>  > > + return -1;
>  > > + if (!platform_get_drvdata(device)) {
>  > > + platform_device_unregister(device);
>  > > + return -1;
>  > > + }
>  > > + _device = device;
>  > > +
>  > > + return 0;
>  > > +}
>  > > +
>  > > +static void __exit alsa_card_ml7213i2s_exit(void)
>  > > +{
>  > > + snd_ml7213i2s_unregister();
>  > > +}
>  > > +
>  > > +module_init(alsa_card_ml7213i2s_init)
>  > > +module_exit(alsa_card_ml7213i2s_exit)
>  > > --
>  > > 1.7.4
>  > >
> 

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

* Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
       [not found] ` <8486F61FC3B94B908BFE654234DD6C97@hacdom.okisemi.com>
@ 2011-10-17  4:28   ` Tomoya MORINAGA
  2011-10-21 14:16     ` Takashi Iwai
  0 siblings, 1 reply; 19+ messages in thread
From: Tomoya MORINAGA @ 2011-10-17  4:28 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: perex, linux-kernel, Wang, Qi, Wang, Yong Y, Clark, Joel, Ewe,
	Kok Howg, alsa-devel

Hi Iwai,

We have just started porting to ASoC structure.
I have a question.

As you reviewed before, currently, our driver consists of 2 parts,
Soundcard driver and I2S driver.

Soundcard consists of 2 parts
   - ALSA interface / control part
   - CODEC control part

I2S driver consists of 4 parts
  - HAL
  - DMA control / interrupt control
  - Soundcard interface part
  - PCI interface function

According to "soc" Documentation,
We must divide to 3 parts, platform driver, machine driver and codec driver.

So, I divided like the following parts.

platform driver
  - ALSA interface / control part
  - HAL
  - DMA control / interrupt control
  - Soundcard interface part
  - PCI interface function

machine driver
  - (none)

codec driver
   - CODEC control part

Is the above dividing true ?

Thanks in advance.

-- tomoya ROHM Co., Ltd. ----- Original Message ----- From: "Takashi 
Iwai" <tiwai@suse.de> To: "Toshiharu Okada" 
<toshiharu-linux@dsn.okisemi.com> Cc: <perex@perex.cz>; 
<alsa-devel@alsa-project.org>; <linux-kernel@vger.kernel.org>; 
<qi.wang@intel.com>; <yong.y.wang@intel.com>; <joel.clark@intel.com>; 
<kok.howg.ewe@intel.com>; <tomoya-linux@dsn.okisemi.com> Sent: 
Wednesday, July 06, 2011 8:06 PM Subject: Re: [PATCH] Add SoundCard 
driver for OKI SEMICONDUCTOR ML7213 IOH At Wed, 6 Jul 2011 19:27:47 
+0900, Toshiharu Okada wrote:
 > >
 > >
 > > This patch is for SoundCard driver of OKI SEMICONDUCTOR ML7213
 > > IOH(Input/Output Hub).
 > > These ML7213 IOH is companion chip for Intel Atom E6xx series.
 > > ML7213 IOH is for IVI(In-Vehicle Infotainment) use.
 > >
 > > [About this driver]
 > > Audio Codec does not exist in ML7213 IOH.
 > > Therefore, this SoundCard driver controls ML26124 Audio Codec 
connected by
 > > I2S of ML7213 IOH.
 > > This driver consists of two modules, an ALSA sound card driver and I2S
 > > driver.
 > > An ALSA sound card driver performs control of ML26124 by I2C of ML7213
 > > IOH.
 > > When another Audio Codec is connected to I2S of ML7213 IOH,
 > > it can respond by change of I2C control of an ALSA sound card driver.
 > >
 > >
 > > Signed-off-by: Toshiharu Okada <toshiharu-linux@dsn.okisemi.com>
Thanks for the patch.

I just took a quick glance over the code, and wonder whether this kind
of driver would fit better with ASoC framework.
Have you considered the implementation on ASoC?


thanks,

Takashi

 > > ---
 > >  sound/drivers/Kconfig      |   34 ++
 > >  sound/drivers/Makefile     |    8 +-
 > >  sound/drivers/ioh_i2s.c    | 1310
 > > ++++++++++++++++++++++++++++++++++++++++++++
 > >  sound/drivers/ioh_i2s.h    |  116 ++++
 > >  sound/drivers/ml7213-ioh.c |  985 +++++++++++++++++++++++++++++++++
 > >  5 files changed, 2452 insertions(+), 1 deletions(-)
 > >  create mode 100644 sound/drivers/ioh_i2s.c
 > >  create mode 100644 sound/drivers/ioh_i2s.h
 > >  create mode 100644 sound/drivers/ml7213-ioh.c
 > >
 > > diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
 > > index c896116..e098a91 100644
 > > --- a/sound/drivers/Kconfig
 > > +++ b/sound/drivers/Kconfig
 > > @@ -209,6 +209,39 @@ config SND_AC97_POWER_SAVE
 > >
 > >    See Documentation/sound/alsa/powersave.txt for more details.
 > >
 > > +config ML7213_I2S
 > > + tristate "OKI SEMICONDUCTOR ML7213 IOH I2S Driver"
 > > + help
 > > +   This driver is OKI SEMICONDUCTOR ML7213 IOH I2S driver.
 > > +   ML7213 is companion chip for Intel Atom E6xx series.
 > > +   This driver is required to use ML7213 SoundCard.
 > > +
 > > +   To compile this driver as a module, choose M here: the module
 > > +   will be called ioh_i2s.
 > > +
 > > +config ML7213_I2S_DEBUG
 > > + bool "ML7213 I2S driver debug"
 > > + depends on ML7213_I2S
 > > + default n
 > > + help
 > > +   This option enables the addition of a debugging code to
 > > +   the OKI SEMICONDUCTOR ML7213 IOH I2S Driver. If you are unsure, 
say N.
 > > +
 > > +   To compile this driver as a debugging module, choose Y here: the
 > > module
 > > +   will be called ioh_i2s.
 > > +
 > > +config SND_ML7213_I2S
 > > + tristate "OKI SEMICONDUCTOR ML7213 SoundCard Driver for ML26124 
Audio
 > > Codec"
 > > + depends on ML7213_I2S
 > > + default y
 > > + help
 > > +   This driver is OKI SEMICONDUCTOR ML7213 IOH SoundCard driver
 > > +   who controls ML26124 Audio Codec connected by I2S of ML7213 IOH.
 > > +   Control of ML26124 uses I2C of ML7213 IOH.
 > > +
 > > +   To compile this driver as a module, choose M here: the module
 > > +   will be called snd-ml7213ioh.
 > > +
 > >  config SND_AC97_POWER_SAVE_DEFAULT
 > >  int "Default time-out for AC97 power-save mode"
 > >  depends on SND_AC97_POWER_SAVE
 > > @@ -219,4 +252,5 @@ config SND_AC97_POWER_SAVE_DEFAULT
 > >
 > >    See SND_AC97_POWER_SAVE for more details.
 > >
 > > +
 > >  endif # SND_DRIVERS
 > > diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
 > > index 1a8440c..41350ea 100644
 > > --- a/sound/drivers/Makefile
 > > +++ b/sound/drivers/Makefile
 > > @@ -11,15 +11,21 @@ snd-portman2x4-objs := portman2x4.o
 > >  snd-serial-u16550-objs := serial-u16550.o
 > >  snd-virmidi-objs := virmidi.o
 > >  snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o
 > > +snd-ml7213ioh-objs := ml7213-ioh.o
 > >
 > >  # Toplevel Module Dependency
 > >  obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
 > >  obj-$(CONFIG_SND_ALOOP) += snd-aloop.o
 > >  obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o
 > >  obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
 > > +obj-$(CONFIG_SND_ML7213_I2S) += snd-ml7213ioh.o
 > >  obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
 > >  obj-$(CONFIG_SND_MTS64) += snd-mts64.o
 > >  obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
 > >  obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o
 > > -
 > >  obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/
 > > +obj-$(CONFIG_ML7213_I2S) += ioh_i2s.o
 > > +ifeq ($(CONFIG_ML7213_I2S_DEBUG),y)
 > > +EXTRA_CFLAG += -DDEBUG
 > > +endif
 > > +
 > > diff --git a/sound/drivers/ioh_i2s.c b/sound/drivers/ioh_i2s.c
 > > new file mode 100644
 > > index 0000000..a40f6df
 > > --- /dev/null
 > > +++ b/sound/drivers/ioh_i2s.c
 > > @@ -0,0 +1,1310 @@
 > > +/*
 > > + * Copyright (C) 2010 OKI 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/slab.h>
 > > +#include <linux/module.h>
 > > +#include <linux/io.h>
 > > +#include <linux/ctype.h>
 > > +#include <linux/interrupt.h>
 > > +#include <linux/device.h>
 > > +
 > > +#include "ioh_i2s.h"
 > > +
 > > +#include <linux/dmaengine.h>
 > > +#include <linux/dma-mapping.h>
 > > +#include <linux/scatterlist.h>
 > > +#include <linux/completion.h>
 > > +#include <linux/delay.h>
 > > +
 > > +#include <linux/string.h>
 > > +#include <linux/timer.h>
 > > +#include <linux/workqueue.h>
 > > +
 > > +#include <linux/pci.h>
 > > +#include <linux/pch_dma.h>
 > > +
 > > +#define MAX_I2S_IF 6 /*I2S0 ~ I2S5*/
 > > +
 > > +#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
 > > +
 > > +/* 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;
 > > + atomic_t rx_busy;
 > > + atomic_t tx_busy;
 > > +
 > > + int ignore_rx_overrun;
 > > +
 > > + /* Transmit side DMA */
 > > + atomic_t pending_tx;
 > > +
 > > + struct ioh_dma_config *dma_config;
 > > +
 > > + char rx_name[16];
 > > + char tx_name[16];
 > > +
 > > + 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 */
 > > +
 > > + void *rxbuf_virt;
 > > + void *txbuf_virt;
 > > + unsigned char *tx_tail;
 > > + unsigned char *tx_head;
 > > + unsigned char *tx_data_head;
 > > + unsigned char *tx_complete;
 > > + unsigned char *rx_tail;
 > > + unsigned char *rx_head;
 > > + unsigned char *rx_data_head;
 > > + unsigned char *rx_complete;
 > > + 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;
 > > +
 > > + spinlock_t tx_lock;
 > > +
 > > + struct pch_dma_slave param_tx;
 > > + struct pch_dma_slave param_rx;
 > > + unsigned int mapbase;
 > > +
 > > + void *rx_callback_data;
 > > + void (*rx_done) (void *callback_data, int status);
 > > + void *tx_callback_data;
 > > + void (*tx_done) (void *callback_data, int status);
 > > +
 > > + unsigned int tx_lower_data_flag;
 > > +
 > > + int dma_tx_unit; /* 1Byte of 2Byte or 4Byte */
 > > +
 > > + int dma_tx_flag; /* Now waiting tx DMA completion */
 > > + int dma_rx_flag; /* Now waiting rx DMA completion */
 > > +};
 > > +
 > > +static struct ioh_i2s_data devs[MAX_I2S_IF];
 > > +
 > > 
+/******************************************************************************
 > > + HAL (Hardware Abstruction Layer)
 > > 
+*******************************************************************************/
 > > +static void ioh_i2s_reset(struct ioh_i2s_data *priv)
 > > +{
 > > + int channel = priv->ch;
 > > +
 > > + iowrite32(1 << channel, 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);
 > > +}
 > > +
 > > 
+/******************************************************************************
 > > + DMA Functions
 > > 
+*******************************************************************************/
 > > +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_common(
 > > + struct ioh_i2s_data *priv,
 > > + char *chan_name,
 > > + 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 */
 > > + priv->param_rx.width = PCH_DMA_WIDTH_2_BYTES;
 > > + priv->param_rx.dma_dev = &dma_dev->dev;
 > > + priv->param_rx.chan_id = priv->ch * 2 + 1; /* ch Rx=1,3,...11 */
 > > + priv->param_rx.rx_reg = (dma_addr_t)((char *)priv->mapbase +\
 > > + priv->ch * 0x800 +\
 > > + I2SDRRXMIRROR_OFFSET);
 > > + chan = dma_request_channel(mask, filter, &priv->param_rx);
 > > + if (chan == NULL) {
 > > + dev_err(priv->dev, "Failed dma_request_channel %s for"
 > > + " I2S %s\n", chan_name, priv->rx_name);
 > > + return NULL;
 > > + }
 > > + priv->chan_rx = chan;
 > > +
 > > + } else if (dir == DMA_TO_DEVICE) { /* Tx */
 > > + priv->param_tx.width = PCH_DMA_WIDTH_2_BYTES;
 > > + priv->param_tx.dma_dev = &dma_dev->dev;
 > > + priv->param_tx.chan_id = priv->ch * 2; /* DMA ch Tx=0,2,...10 */
 > > + priv->param_tx.tx_reg = (dma_addr_t)((char *)priv->mapbase +\
 > > + priv->ch * 0x800 +\
 > > + I2SDRTXMIRROR_OFFSET);
 > > + chan = dma_request_channel(mask, filter, &priv->param_tx);
 > > + if (chan == NULL) {
 > > + dev_err(priv->dev, "Failed dma_request_channel %s for"
 > > + " I2S %s\n", chan_name, priv->tx_name);
 > > + return NULL;
 > > + }
 > > + priv->chan_tx = chan;
 > > + } else {
 > > + dev_err(priv->dev, "%s:Invalid direction (%d)\n",
 > > + chan_name, dir);
 > > + return NULL;
 > > + }
 > > +
 > > + return chan;
 > > +}
 > > +
 > > +static void ioh_setup_rx_dma(struct ioh_i2s_data *priv)
 > > +{
 > > + ioh_request_dma_channel_common(
 > > + priv,
 > > + priv->dma_config->rx_chan,
 > > + DMA_FROM_DEVICE);
 > > +}
 > > +
 > > +static void ioh_setup_tx_dma(struct ioh_i2s_data *priv)
 > > +{
 > > + ioh_request_dma_channel_common(
 > > + priv,
 > > + priv->dma_config->tx_chan,
 > > + DMA_TO_DEVICE);
 > > +}
 > > +
 > > +static void i2s_dma_tx_complete(void *arg)
 > > +{
 > > + struct ioh_i2s_data *priv = arg;
 > > + struct scatterlist *sg = priv->sg_tx_cur;
 > > + int i;
 > > +
 > > + dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p sg_len=%d 
num=%d",
 > > + __func__, priv->tx_data_head, priv->tx_complete, sg_dma_len(sg),
 > > + priv->tx_num);
 > > + dma_sync_sg_for_cpu(priv->dev, sg, priv->tx_num, DMA_TO_DEVICE);
 > > +
 > > + for (i = 0; i < priv->tx_num; i++, sg++)
 > > + priv->tx_complete += sg_dma_len(sg) * priv->dma_tx_unit;
 > > +
 > > + if (priv->tx_complete > priv->tx_tail) {
 > > + priv->tx_complete =\
 > > +      (char *)(((u32)priv->tx_complete &  0x00000fff) |\
 > > +      ((u32)priv->tx_head & 0xfffff000));
 > > + }
 > > +
 > > + dev_dbg(priv->dev, "-->data_complete=%p\n", priv->tx_complete);
 > > +
 > > + if ((priv->tx_complete - 1) == priv->tx_data_head)
 > > + ioh_i2s_disable_interrupts(priv, IOH_PLAYBACK);
 > > +
 > > + async_tx_ack(priv->desc_tx);
 > > + if (priv->tx_done)
 > > + priv->tx_done(priv->tx_callback_data, IOH_EOK);
 > > +
 > > + ioh_i2s_enable_tx_empty_ir(priv);
 > > + priv->dma_tx_flag = 0;
 > > +}
 > > +
 > > +static void i2s_dma_rx_complete(void *arg)
 > > +{
 > > + struct ioh_i2s_data *priv = arg;
 > > + struct scatterlist *sg = priv->sg_rx_cur;
 > > +
 > > + dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p 
sg_dma_len=%d\n",
 > > + __func__, priv->rx_data_head, priv->rx_complete,
 > > + sg_dma_len(sg));
 > > +
 > > + priv->rx_data_head += sg_dma_len(sg) * priv->dma_tx_unit;
 > > +
 > > + dev_dbg(priv->dev, "-->data_head=%p\n", priv->rx_data_head);
 > > +
 > > + async_tx_ack(priv->desc_rx);
 > > +
 > > + dma_sync_sg_for_cpu(priv->dev, sg, 1, DMA_FROM_DEVICE);
 > > +
 > > + if (priv->rx_done)
 > > + priv->rx_done(priv->rx_callback_data, IOH_EOK);
 > > +
 > > + ioh_i2s_enable_rx_full_ir(priv);
 > > + priv->dma_rx_flag = 0;
 > > +}
 > > +
 > > 
+/******************************************************************************
 > > + Intrrupt Functions
 > > 
+*******************************************************************************/
 > > +void i2s_tx_almost_empty_ir(struct ioh_i2s_data *priv, int ch)
 > > +{
 > > + struct dma_async_tx_descriptor *desc;
 > > + int num;
 > > + int tx_data_index;
 > > + int tx_comp_index;
 > > + struct scatterlist *sg = priv->sg_tx_p;
 > > +
 > > + dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p\n", __func__,
 > > + priv->tx_data_head, priv->tx_complete);
 > > +
 > > + tx_data_index = (((int)priv->tx_data_head) & 0xfff) /\
 > > + (I2S_AEMPTY_THRESH * I2S_1PERIODS_BYTES);
 > > + tx_comp_index = (((int)priv->tx_complete) & 0xfff) /\
 > > + (I2S_AEMPTY_THRESH * I2S_1PERIODS_BYTES);
 > > +
 > > + if (tx_data_index > tx_comp_index)
 > > + num = tx_data_index - tx_comp_index - 1;
 > > + else if (tx_comp_index == (priv->tx_nent - 1))
 > > + num = tx_data_index;
 > > + else
 > > + num = priv->tx_nent - tx_comp_index - 1;
 > > +
 > > + dev_dbg(priv->dev, "%s: head_index=%d complete_index=%d num=%d\n",
 > > + __func__, tx_data_index, tx_comp_index, num);
 > > +
 > > + if (num < 1) { /* there is no data */
 > > + priv->tx_lower_data_flag += 1;
 > > + if (priv->tx_lower_data_flag >= 10) {
 > > + priv->tx_lower_data_flag = 0;
 > > + priv->tx_data_head = priv->tx_head;
 > > + priv->tx_complete = priv->tx_tail;
 > > + ioh_i2s_disable_interrupts(priv, IOH_PLAYBACK);
 > > + ioh_i2s_disable_tx_empty_ir(priv);
 > > + dev_dbg(priv->dev, "%s:No data to send\n", __func__);
 > > + }
 > > + return; /* No data to transmit */
 > > + }
 > > +
 > > + priv->tx_lower_data_flag = 0;
 > > +
 > > + tx_comp_index = (tx_comp_index + 1) % (priv->tx_nent);
 > > + sg = sg + tx_comp_index; /* Point head of sg must be sent */
 > > + priv->sg_tx_cur = sg; /* Save tx condition */
 > > + priv->tx_num = num; /* Save tx condition */
 > > +
 > > + dev_dbg(priv->dev, "%s: sg = sg + %d tx_nent=%d\n",
 > > + __func__, tx_comp_index, priv->tx_nent);
 > > +
 > > + desc = priv->chan_tx->device->device_prep_slave_sg(priv->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);
 > > + priv->dma_tx_flag = 1;
 > > +
 > > + dma_sync_sg_for_device(priv->dev, sg, num, DMA_TO_DEVICE);
 > > + priv->desc_tx = desc;
 > > + desc->callback = i2s_dma_tx_complete;
 > > + desc->callback_param = priv;
 > > +
 > > + atomic_inc(&priv->pending_tx);
 > > + desc->tx_submit(desc);
 > > +
 > > + dma_async_issue_pending(priv->chan_tx);
 > > +}
 > > +
 > > +void i2s_rx_almost_full_ir(struct ioh_i2s_data *priv)
 > > +{
 > > + struct dma_async_tx_descriptor *desc;
 > > + struct scatterlist *sg;
 > > + int rx_data_index;
 > > +
 > > + sg = priv->sg_rx_p;
 > > + rx_data_index = (((int)priv->rx_data_head) & 0xfff) /\
 > > + (I2S_AFULL_THRESH * I2S_1PERIODS_BYTES);
 > > + if (rx_data_index > priv->rx_nent)
 > > + rx_data_index = 0;
 > > +
 > > + dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p 
data_index=%d\n",
 > > + __func__, priv->rx_data_head, priv->rx_complete, rx_data_index);
 > > +
 > > + sg += rx_data_index;
 > > +
 > > + desc = priv->chan_rx->device->device_prep_slave_sg(priv->chan_rx,
 > > + sg, 1, DMA_FROM_DEVICE,
 > > + DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
 > > + if (!desc) {
 > > + dev_err(priv->dev, "%s:device_prep_slave_sg Failed\n",
 > > + __func__);
 > > + return;
 > > + }
 > > +
 > > + priv->sg_rx_cur = sg; /* Save rx condition */
 > > + ioh_i2s_disable_rx_full_ir(priv);
 > > + priv->dma_rx_flag = 1;
 > > + dma_sync_sg_for_device(priv->dev, sg, 1, DMA_FROM_DEVICE);
 > > +
 > > + priv->desc_rx = desc;
 > > + desc->callback = i2s_dma_rx_complete;
 > > + desc->callback_param = priv;
 > > + desc->tx_submit(desc);
 > > + dma_async_issue_pending(priv->chan_rx);
 > > +}
 > > +
 > > +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_rx(struct ioh_i2s_data *priv)
 > > +{
 > > + unsigned int status;
 > > + int channel = priv->ch;
 > > + int offset = channel * 0x800;
 > > +
 > > + if (!priv->dma_rx_flag) {
 > > + 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);
 > > + }
 > > +}
 > > +
 > > +static inline void ioh_i2s_interrupt_sub_tx(struct ioh_i2s_data *priv)
 > > +{
 > > + unsigned int status;
 > > + int channel = priv->ch;
 > > + int offset = channel * 0x800;
 > > +
 > > + if (!priv->dma_tx_flag) {
 > > + 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, channel);
 > > +
 > > + /*Clear the interrupt status */
 > > + iowrite32(status, priv->iobase + I2SISTTX_OFFSET + offset);
 > > + }
 > > +}
 > > +
 > > +int ioh_i2s_event(struct ioh_i2s_data *priv)
 > > +{
 > > + u32 idisp;
 > > + unsigned long flags;
 > > + int ret = IRQ_NONE;
 > > +
 > > + spin_lock_irqsave(&priv->tx_lock, flags);
 > > +
 > > + idisp = ioread32(priv->iobase + I2SIDISP_OFFSET);
 > > + if (idisp & BIT(priv->ch + 16)) {
 > > + dev_dbg(priv->dev, "Rx%d interrupt occures\n", priv->ch);
 > > + ioh_i2s_interrupt_sub_rx(priv);
 > > + ret = IRQ_HANDLED;
 > > + }
 > > +
 > > + if (idisp & BIT(priv->ch)) {
 > > + dev_dbg(priv->dev, "Tx%d interrupt occures\n", priv->ch);
 > > + ioh_i2s_interrupt_sub_tx(priv);
 > > + ret = IRQ_HANDLED;
 > > + }
 > > +
 > > + spin_unlock_irqrestore(&priv->tx_lock, flags);
 > > +
 > > + return ret;
 > > +}
 > > +
 > > 
+/******************************************************************************
 > > + External Functions (Called by Sourdcard driver)
 > > 
+*******************************************************************************/
 > > +void ioh_i2s_ignore_rx_overrun(struct ioh_i2s_data *priv)
 > > +{
 > > + priv->ignore_rx_overrun = 1;
 > > +}
 > > +EXPORT_SYMBOL_GPL(ioh_i2s_ignore_rx_overrun);
 > > +
 > > +struct ioh_i2s_data *ioh_i2s_open(int ch, enum ioh_direction dir,
 > > +      const char *name, void *cbd,
 > > +      void (*cb)(void *cbd, int status))
 > > +{
 > > + struct ioh_i2s_data *obj = NULL;
 > > + struct scatterlist *sg;
 > > + int rx_size;
 > > + int rx_num;
 > > + int tx_size;
 > > + int tx_num;
 > > + int i;
 > > +
 > > + 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;
 > > + }
 > > + obj = &devs[ch];
 > > +
 > > + obj->ignore_rx_overrun = 0;
 > > + obj->dma_tx_unit = 2; /* Tx transmits wuth 2Byte Unit */
 > > +
 > > + if (dir) {
 > > + /* Rx configuration */
 > > + if (atomic_read(&obj->rx_busy)) {
 > > + dev_err(obj->dev, "rx i2s%d have already opened\n", ch);
 > > + atomic_dec(&obj->rx_busy);
 > > + return 0;
 > > + }
 > > + atomic_inc(&obj->rx_busy);
 > > +
 > > + strcpy(obj->rx_name, name);
 > > + ioh_setup_rx_dma(obj);
 > > + if (!obj->chan_rx) {
 > > + dev_err(obj->dev, "%s:ioh_setup_rx_dma failed\n",
 > > + __func__);
 > > + return NULL;
 > > + }
 > > +
 > > + obj->rxbuf_virt = dma_alloc_coherent(obj->dev, PAGE_SIZE,
 > > + &obj->rx_buf_dma, GFP_KERNEL);
 > > + if (!obj->rxbuf_virt) {
 > > + dev_err(obj->dev, "dma_alloc_coherent Failed\n");
 > > + return NULL;
 > > + }
 > > +
 > > + rx_size = I2S_AFULL_THRESH * I2S_1PERIODS_BYTES;
 > > +
 > > + /* The number of scatter list (Franction area is not used) */
 > > + rx_num = PAGE_SIZE / rx_size;
 > > +
 > > + dev_dbg(obj->dev, "%s: rx: scatter_num=%d scatter_size=%d\n",
 > > + __func__, rx_num, rx_size);
 > > +
 > > + obj->sg_rx_p =\
 > > +        kzalloc(sizeof(struct scatterlist) *rx_num, GFP_ATOMIC);
 > > +
 > > + sg = obj->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(obj->rxbuf_virt), rx_size,
 > > +     rx_size * i);
 > > + sg_dma_len(sg) = rx_size / obj->dma_tx_unit;
 > > + sg_dma_address(sg) = obj->rx_buf_dma + sg->offset;
 > > + }
 > > +
 > > + obj->rx_head = (unsigned char *)obj->rxbuf_virt;
 > > + obj->rx_tail = (unsigned char *)obj->rxbuf_virt +\
 > > + rx_num * rx_size - 1;
 > > + obj->rx_data_head = (unsigned char *)obj->rxbuf_virt;
 > > + obj->rx_complete = (unsigned char *)obj->rx_tail;
 > > +
 > > + obj->rx_nent = rx_num;
 > > + } else {
 > > + /* Tx configuration */
 > > + if (atomic_read(&obj->tx_busy)) {
 > > + dev_err(obj->dev, "tx i2s%d have already opened\n", ch);
 > > + atomic_dec(&obj->tx_busy);
 > > + return 0;
 > > + }
 > > + atomic_inc(&obj->tx_busy);
 > > +
 > > + strcpy(obj->tx_name, name);
 > > + ioh_setup_tx_dma(obj);
 > > + if (!obj->chan_tx) {
 > > + dev_err(obj->dev, "%s:ioh_setup_tx_dma failed\n",
 > > + __func__);
 > > + return NULL;
 > > + }
 > > +
 > > + obj->txbuf_virt = dma_alloc_coherent(obj->dev, PAGE_SIZE,
 > > + &obj->tx_buf_dma, GFP_KERNEL);
 > > +
 > > + if (!obj->txbuf_virt) {
 > > + dev_err(obj->dev, "dma_alloc_coherent Failed\n");
 > > + return NULL;
 > > + }
 > > +
 > > + obj->tx_head = (unsigned char *)obj->txbuf_virt;
 > > + obj->tx_tail = (unsigned char *)obj->txbuf_virt + PAGE_SIZE - 1;
 > > + obj->tx_data_head = (unsigned char *)obj->txbuf_virt;
 > > + obj->tx_complete = (unsigned char *)obj->tx_tail;
 > > +
 > > + tx_size = I2S_AEMPTY_THRESH * I2S_1PERIODS_BYTES;
 > > + if (PAGE_SIZE % tx_size)
 > > + /* tx_num = The number of scatter list */
 > > + tx_num = PAGE_SIZE / tx_size + 1;
 > > + else
 > > + tx_num = PAGE_SIZE / tx_size;
 > > +
 > > + dev_dbg(obj->dev, "%s: tx: scatter_num=%d scatter_size=%d\n",
 > > + __func__, tx_num, tx_size);
 > > +
 > > + obj->sg_tx_p =\
 > > +        kzalloc(sizeof(struct scatterlist) *tx_num, GFP_ATOMIC);
 > > +
 > > + sg_init_table(obj->sg_tx_p, tx_num); /* Initialize SG table */
 > > + sg = obj->sg_tx_p;
 > > +
 > > + for (i = 0; i < tx_num; i++, sg++) {
 > > + if (i == (tx_num - 1)) {
 > > + if (PAGE_SIZE % tx_size) {
 > > + sg_set_page(sg,
 > > +   virt_to_page(obj->txbuf_virt),
 > > +   PAGE_SIZE % tx_size,
 > > +   tx_size * i);
 > > + sg_dma_len(sg) = (PAGE_SIZE % tx_size)\
 > > + / obj->dma_tx_unit;
 > > + } else {
 > > + sg_set_page(sg,
 > > +   virt_to_page(obj->txbuf_virt),
 > > +   tx_size, tx_size * i);
 > > + sg_dma_len(sg) = tx_size\
 > > + / obj->dma_tx_unit;
 > > + }
 > > + } else {
 > > + sg_set_page(sg, virt_to_page(obj->txbuf_virt),
 > > +     tx_size, tx_size * i);
 > > + sg_dma_len(sg) = tx_size / obj->dma_tx_unit;
 > > + }
 > > + sg_dma_address(sg) = obj->tx_buf_dma + sg->offset;
 > > + }
 > > + obj->tx_nent = tx_num;
 > > + }
 > > +
 > > + return obj;
 > > +}
 > > +EXPORT_SYMBOL_GPL(ioh_i2s_open);
 > > +
 > > +void ioh_i2s_release(struct ioh_i2s_data *priv, enum ioh_direction 
dir)
 > > +{
 > > + 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);
 > > + if (priv->chan_rx) {
 > > + priv->chan_rx->device->device_control(priv->chan_rx,
 > > +      DMA_TERMINATE_ALL,
 > > +      0);
 > > + dma_release_channel(priv->chan_rx);
 > > + priv->chan_rx = NULL;
 > > + }
 > > +
 > > + kfree(priv->sg_rx_p);
 > > + if (priv->rxbuf_virt)
 > > + dma_free_coherent(priv->dev, PAGE_SIZE,
 > > +   priv->rxbuf_virt,
 > > +   priv->rx_buf_dma);
 > > +
 > > + priv->rxbuf_virt = NULL;
 > > + priv->rx_buf_dma = 0;
 > > + atomic_dec(&priv->rx_busy);
 > > +
 > > + } else {
 > > + ioh_i2s_disable_interrupts(priv, IOH_PLAYBACK);
 > > + ioh_i2s_disable_tx_empty_ir(priv);
 > > + if (priv->chan_tx) {
 > > + priv->chan_tx->device->device_control(priv->chan_tx,
 > > +      DMA_TERMINATE_ALL,
 > > +      0);
 > > + dma_release_channel(priv->chan_tx);
 > > + priv->chan_tx = NULL;
 > > + }
 > > +
 > > + kfree(priv->sg_tx_p);
 > > + if (priv->txbuf_virt)
 > > + dma_free_coherent(priv->dev, PAGE_SIZE,
 > > +   priv->txbuf_virt,
 > > +   priv->tx_buf_dma);
 > > +
 > > + priv->txbuf_virt = NULL;
 > > + priv->tx_buf_dma = 0;
 > > + atomic_dec(&priv->tx_busy);
 > > + }
 > > +}
 > > +EXPORT_SYMBOL_GPL(ioh_i2s_release);
 > > +
 > > +
 > > +void ioh_i2s_configure_i2s_regs(struct ioh_i2s_data *priv,
 > > +     unsigned long bitrate,
 > > +     struct ioh_i2s_config_reg *config,
 > > +     enum ioh_direction dir)
 > > +{
 > > + int ch = priv->ch;
 > > + int offset = ch * 0x800;
 > > +
 > > + /* Common register */
 > > + iowrite32(config->cmn.i2sclkcnt,
 > > +   priv->iobase + I2SCLKCNT0_OFFSET + 0x10*ch);
 > > +
 > > + if (dir) {
 > > + /* 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 {
 > > + /* 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);
 > > + }
 > > +}
 > > +EXPORT_SYMBOL_GPL(ioh_i2s_configure_i2s_regs);
 > > +
 > > +void ioh_i2s_write(struct ioh_i2s_data *priv,
 > > +        const void *data,
 > > +        int len,
 > > +        void *callback_data,
 > > +        void (*done) (void *callback_data, int status))
 > > +{
 > > + int rem1;
 > > + int rem2;
 > > +
 > > + priv->tx_callback_data = callback_data;
 > > + priv->tx_done = done;
 > > +
 > > + dev_dbg(priv->dev, "%s: [ch%d] len=%d data_head=%p data_complete=%p",
 > > + __func__, priv->ch, len, priv->tx_data_head, priv->tx_complete);
 > > +
 > > + if (priv->tx_data_head > priv->tx_tail)
 > > + priv->tx_data_head = priv->tx_head;
 > > +
 > > + if ((priv->tx_data_head + len - 1) <= priv->tx_tail) {
 > > + memcpy(priv->tx_data_head, data, len);
 > > + priv->tx_data_head += len;
 > > + } else {
 > > + rem1 = priv->tx_tail - priv->tx_data_head + 1;
 > > + rem2 = len - rem1;
 > > + memcpy(priv->tx_data_head, data, rem1);
 > > + priv->tx_data_head = priv->tx_head;
 > > + memcpy(priv->tx_data_head, data + rem1, rem2);
 > > + priv->tx_data_head += rem2;
 > > + }
 > > + dev_dbg(priv->dev, "-->data_head=%p\n", priv->tx_data_head);
 > > +
 > > + ioh_i2s_clear_tx_sts_ir(priv);
 > > + ioh_i2s_tx_clear_dma_mask(priv);
 > > + ioh_i2s_enable_tx_empty_ir(priv);
 > > + ioh_i2s_enable_interrupts(priv, IOH_PLAYBACK);
 > > +}
 > > +EXPORT_SYMBOL_GPL(ioh_i2s_write);
 > > +
 > > +void ioh_i2s_read(struct ioh_i2s_data *priv,
 > > +       void *data,
 > > +       int len,
 > > +       void *callback_data,
 > > +       void (*done) (void *callback_data, int status))
 > > +{
 > > + int rx_ready_bytes;
 > > + unsigned int rem1 = 0, rem2 = 0;
 > > + char *rem;
 > > + char *copy_head;
 > > +
 > > + priv->rx_callback_data = callback_data;
 > > + priv->rx_done = done;
 > > +
 > > + ioh_i2s_clear_rx_sts_ir(priv);
 > > + ioh_i2s_rx_clear_dma_mask(priv);
 > > + ioh_i2s_enable_rx_full_ir(priv);
 > > + ioh_i2s_enable_interrupts(priv, IOH_CAPTURE);
 > > +
 > > + dev_dbg(priv->dev, "%s: [ch%d]data_head=%p data_complete=%p 
len=%d\n",
 > > + __func__, priv->ch, priv->rx_data_head, priv->rx_complete, len);
 > > +
 > > + if (((u32)priv->rx_data_head & 0xffff) ==\
 > > +       ((u32)(priv->rx_complete + 1) & 0xffff)) {
 > > + dev_dbg(priv->dev, "%s:No data to read\n", __func__);
 > > + return;
 > > + }
 > > +
 > > + if (((u32)(priv->rx_complete + 1) & 0xfff) ==\
 > > +        (((u32)priv->rx_head) & 0xfff)) {
 > > + rx_ready_bytes = priv->rx_data_head - priv->rx_head;
 > > + } else if (priv->rx_complete < priv->rx_data_head) {
 > > + rx_ready_bytes = priv->rx_data_head - priv->rx_complete - 1;
 > > + } else {
 > > + rem1 = priv->rx_tail - priv->rx_complete;
 > > + if (rem1 < len)
 > > + rem2 = priv->rx_data_head - priv->rx_head;
 > > +
 > > + dev_dbg(priv->dev, "%s:rem1=%d rem2=%d\n",
 > > + __func__, rem1, rem2);
 > > + rx_ready_bytes = rem1 + rem2;
 > > + }
 > > +
 > > + dev_dbg(priv->dev,
 > > + "%s:rx_ready_bytes=%d(%d, %d) head_index=%d com_index=%d\n",
 > > + __func__, rx_ready_bytes, rem1, rem2,
 > > + (((int)priv->rx_data_head) & 0xfff) /\
 > > + (I2S_AFULL_THRESH * I2S_1PERIODS_BYTES),
 > > + (((int)priv->rx_complete) & 0xfff) /\
 > > +        (I2S_AFULL_THRESH * I2S_1PERIODS_BYTES));
 > > +
 > > + if (priv->rx_data_head > priv->rx_tail)
 > > + priv->rx_data_head = priv->rx_head;
 > > +
 > > + if (len > rx_ready_bytes) {
 > > + dev_dbg(priv->dev, "%s:Not enough data in bufffer\n", __func__);
 > > + return;
 > > + }
 > > +
 > > + if (rem2) {
 > > + memcpy(data, priv->rx_complete + 1, rem1);
 > > +
 > > + rem = (char *)data + rem1;
 > > + memcpy(rem, priv->rx_head, len - rem1);
 > > + priv->rx_complete = priv->rx_head + len - rem1 - 1;
 > > + } else {
 > > + copy_head = (char *)(((u32)(priv->rx_complete + 1) & 0xfff) |\
 > > +     ((u32)priv->rx_head & 0xfffff000));
 > > + memcpy(data, copy_head, len);
 > > + priv->rx_complete += len;
 > > + }
 > > +
 > > + priv->rx_complete = (char *)((((u32)priv->rx_complete) & 
0x00000fff) |\
 > > +     (((u32)priv->rx_head) & 0xfffff000));
 > > +
 > > + dev_dbg(priv->dev, "com_index=%d data_complete=%p\n",
 > > + (((int)priv->rx_complete) & 0xfff) /\
 > > + (I2S_AFULL_THRESH * I2S_1PERIODS_BYTES),
 > > + priv->rx_complete);
 > > +
 > > +}
 > > +EXPORT_SYMBOL_GPL(ioh_i2s_read);
 > > +
 > > 
+/******************************************************************************
 > > + PCI Functions
 > > 
+*******************************************************************************/
 > > +struct ioh_i2s_data *ioh_i2s_create(struct device *dev, void *iobase,
 > > +        unsigned int mapbase, int ch)
 > > +{
 > > + struct ioh_i2s_data *obj;
 > > +
 > > + dev_dbg(dev, "Instantiate an i2s instance with io at v0x%p.\n", 
iobase);
 > > +
 > > + obj = &devs[ch];
 > > + obj->iobase = iobase;
 > > + obj->mapbase = mapbase;
 > > + obj->ch = ch;
 > > + obj->dev = dev;
 > > +
 > > + atomic_set(&obj->rx_busy, 0);
 > > + atomic_set(&obj->tx_busy, 0);
 > > + strcpy(obj->rx_name, "FREE");
 > > + strcpy(obj->tx_name, "FREE");
 > > +
 > > + return obj;
 > > +}
 > > +
 > > +void ioh_i2s_destroy(struct ioh_i2s_data *priv)
 > > +{
 > > +}
 > > +
 > > +#define DRV_NAME "ml7213_ioh_i2s"
 > > +#define PCI_VENDOR_ID_ROHM 0X10DB
 > > +#define PCI_DEVICE_ID_ML7213_I2S 0X8033
 > > +
 > > +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,}
 > > +};
 > > +
 > > +struct ioh_i2s_data_pci {
 > > + struct ioh_i2s_data *devs[MAX_I2S_IF];
 > > + void __iomem *membase;
 > > + unsigned int mapbase;
 > > +};
 > > +
 > > +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 handled;
 > > +
 > > + do {
 > > + int i;
 > > +
 > > + handled = 0;
 > > + for (i = 0; i < MAX_I2S_IF; i++)
 > > + handled |= ioh_i2s_event(drvdata->devs[i]);
 > > +
 > > + } while (handled);
 > > +
 > > + return IRQ_HANDLED;
 > > +}
 > > +
 > > +static int ioh_i2s_pci_probe(struct pci_dev *pdev,
 > > + const struct pci_device_id *id)
 > > +{
 > > + int rv = 0;
 > > + int i;
 > > + struct ioh_i2s_data_pci *drvdata;
 > > + void __iomem *tbl;
 > > + unsigned int mapbase;
 > > +
 > > + drvdata = kzalloc(sizeof(struct ioh_i2s_data_pci), 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);
 > > + mapbase = pci_resource_start(pdev, 1);
 > > + if (!mapbase) {
 > > + rv = -ENOMEM;
 > > + printk(KERN_ERR "pci_resource_start failed\n");
 > > + goto out_pci_resource_start;
 > > + }
 > > + drvdata->membase = tbl;
 > > + drvdata->mapbase = mapbase;
 > > +
 > > + for (i = 0; i < MAX_I2S_IF; i++) {
 > > + drvdata->devs[i] =
 > > +     ioh_i2s_create(&pdev->dev, tbl, mapbase, i);
 > > +
 > > + spin_lock_init(&drvdata->devs[i]->tx_lock);
 > > + drvdata->devs[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_disable;
 > > + }
 > > +
 > > + return rv;
 > > +out_pci_resource_start:
 > > + pci_iounmap(pdev, tbl);
 > > +out_disable:
 > > + 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;
 > > +
 > > + free_irq(pdev->irq, pdev);
 > > + for (i = 0; i < MAX_I2S_IF; i++) {
 > > + if (drvdata->devs[i]) {
 > > + ioh_i2s_reset(drvdata->devs[i]);
 > > + ioh_i2s_destroy(drvdata->devs[i]);
 > > + }
 > > + }
 > > + pci_iounmap(pdev, drvdata->membase);
 > > + pci_disable_device(pdev);
 > > + pci_set_drvdata(pdev, NULL);
 > > + kfree(drvdata);
 > > +}
 > > +
 > > +static int ioh_i2s_pci_suspend(struct pci_dev *pdev, pm_message_t 
state)
 > > +{
 > > + BUG_ON(1);
 > > + return -EINVAL;
 > > +}
 > > +
 > > +static int ioh_i2s_pci_resume(struct pci_dev *pdev)
 > > +{
 > > + BUG_ON(1);
 > > + return -EINVAL;
 > > +}
 > > +
 > > +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_LICENSE("GPL v2");
 > > +MODULE_AUTHOR("OKI SEMICONDUCTOR ML7213 IOH");
 > > +MODULE_DESCRIPTION("OKI SEMICONDUCTOR ML7213 IOH I2S driver");
 > > +MODULE_DEVICE_TABLE(pci, ioh_pci_tbl);
 > > +MODULE_VERSION("1.0");
 > > diff --git a/sound/drivers/ioh_i2s.h b/sound/drivers/ioh_i2s.h
 > > new file mode 100644
 > > index 0000000..f652cef
 > > --- /dev/null
 > > +++ b/sound/drivers/ioh_i2s.h
 > > @@ -0,0 +1,116 @@
 > > +/*
 > > + * Copyright (C) 2010 OKI 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 USE_CHANNELS_MIN 1
 > > +#define USE_CHANNELS_MAX 2
 > > +#define I2S_1PERIODS_BYTES 2 /* 1 period byte suze (2 means 16bit) */
 > > +
 > > +#define MAX_I2S_RX_CH 6
 > > +#define MAX_I2S_TX_CH 6
 > > +
 > > +struct ioh_i2s_data;
 > > +
 > > +
 > > +enum ioh_direction {
 > > + IOH_PLAYBACK = 0,
 > > + IOH_CAPTURE,
 > > +};
 > > +
 > > +struct ioh_i2s_data *ioh_i2s_open(int id, enum ioh_direction dir,
 > > +      const char *name, void *cbd,
 > > +      void (*cb)(void *cbd, int status));
 > > +void ioh_i2s_release(struct ioh_i2s_data *priv, enum ioh_direction 
dir);
 > > +void ioh_i2s_ignore_rx_overrun(struct ioh_i2s_data *priv);
 > > +
 > > +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;
 > > +};
 > > +
 > > +void ioh_i2s_configure_i2s_regs(struct ioh_i2s_data *priv,
 > > +       unsigned long bitrate,
 > > +       struct ioh_i2s_config_reg *config,
 > > +       enum ioh_direction dir);
 > > +
 > > +void ioh_i2s_write(struct ioh_i2s_data *priv,
 > > +        const void *data,
 > > +        int len,
 > > +        void *callback_data,
 > > +        void (*done) (void *callback_data, int status));
 > > +
 > > +void ioh_i2s_read(struct ioh_i2s_data *priv,
 > > +       void *data,
 > > +       int len,
 > > +       void *callback_data,
 > > +       void (*done) (void *callback_data, int status));
 > > +
 > > +#endif
 > > diff --git a/sound/drivers/ml7213-ioh.c b/sound/drivers/ml7213-ioh.c
 > > new file mode 100644
 > > index 0000000..9ffb420
 > > --- /dev/null
 > > +++ b/sound/drivers/ml7213-ioh.c
 > > @@ -0,0 +1,985 @@
 > > +/*
 > > + * Copyright (c) 2010-2011 by Wind River
 > > + * Copyright (C) 2011 OKI SEMICONDUCTOR CO., LTD.
 > > + *
 > > + * This code was derived from the Wind River MSP/I2S audio capture 
for
 > > STA2X11.
 > > + *
 > > + * 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/init.h>
 > > +#include <linux/err.h>
 > > +#include <linux/platform_device.h>
 > > +#include <linux/jiffies.h>
 > > +#include <linux/slab.h>
 > > +#include <linux/moduleparam.h>
 > > +#include <linux/i2c.h>
 > > +#include <sound/core.h>
 > > +#include <sound/pcm.h>
 > > +#include <sound/initval.h>
 > > +
 > > +#include "ioh_i2s.h"
 > > +
 > > +MODULE_LICENSE("GPL v2");
 > > +MODULE_AUTHOR("OKI SEMICONDUCTOR ML7213 IOH");
 > > +MODULE_DESCRIPTION("OKI SEMICONDUCTOR ML7213 IOH I2S driver");
 > > +MODULE_SUPPORTED_DEVICE("{{ALSA,ml7213i2s sound card}}");
 > > +
 > > +#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE
 > > +#define USE_RATE (SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_32000|\
 > > + SNDRV_PCM_RATE_48000)
 > > +#define USE_RATE_MIN 16000
 > > +#define USE_RATE_MAX 48000
 > > +#define MAX_BUFFER_SIZE (MAX_PERIOD_SIZE*USE_PERIODS_MAX)
 > > +#define MIN_PERIOD_SIZE 64
 > > +#define MAX_PERIOD_SIZE (I2S_AFULL_THRESH*2) /* 16bit(=1word=2Byte)
 > > + FIFOsize=128word */
 > > +
 > > +#define USE_PERIODS_MIN 2
 > > +#define USE_PERIODS_MAX 64
 > > +
 > > +#ifndef add_capture_constraints
 > > +#define add_capture_constraints(x) 0
 > > +#endif
 > > +
 > > +/* Direction configuration master(=1) or slave(=0) */
 > > +#define I2S_WRITE_MASTER 1
 > > +#define I2S_READ_MASTER (I2S_WRITE_MASTER)
 > > +
 > > +/* RW flag */
 > > +#define SND_CAPTURE_SUBSTREAM 0
 > > +#define SND_PLAYBACK_SUBSTREAM 1
 > > +
 > > +/* Codec Device Address */
 > > +#define CODEC_DEV_ADDR (0x1A)
 > > +
 > > +static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
 > > +static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
 > > +static struct i2c_client *i2c;
 > > +static struct i2c_board_info ioh_hwmon_info[] = {
 > > + {I2C_BOARD_INFO("ioh_i2c-0", CODEC_DEV_ADDR+0)},
 > > + {I2C_BOARD_INFO("ioh_i2c-1", CODEC_DEV_ADDR+0)},
 > > + {I2C_BOARD_INFO("ioh_i2c-2", CODEC_DEV_ADDR+0)},
 > > + {I2C_BOARD_INFO("ioh_i2c-3", CODEC_DEV_ADDR+0)},
 > > + {I2C_BOARD_INFO("ioh_i2c-4", CODEC_DEV_ADDR+0)},
 > > + {I2C_BOARD_INFO("ioh_i2c-5", CODEC_DEV_ADDR+0)},
 > > +};
 > > +
 > > +static void setup_i2s_read(struct snd_pcm_substream *substream, 
int ch);
 > > +static void setup_i2s_write(struct snd_pcm_substream *substream, 
int ch);
 > > +
 > > +module_param(index, int, 0444);
 > > +MODULE_PARM_DESC(index, "Index value for ml7213i2s sound card.");
 > > +module_param(id, charp, 0444);
 > > +MODULE_PARM_DESC(id, "ID string for ml7213i2s sound card.");
 > > +
 > > +static int ignore_overrun = 1;
 > > +module_param(ignore_overrun, int, 0444);
 > > +MODULE_PARM_DESC(ignore_overrun, "ignore RX overruns (default=0)");
 > > +
 > > +static struct platform_device *_device;
 > > +static struct mutex i2c_mutex;
 > > +
 > > +struct snd_ml7213i2s {
 > > + struct snd_card *card;
 > > + struct snd_pcm *pcm;
 > > +};
 > > +
 > > +struct cbdata {
 > > + struct ioh_i2s_data *priv;
 > > + int stop;
 > > + int cnt;
 > > +};
 > > +
 > > +static int errors;
 > > +
 > > +struct snd_ml7213i2s_pcm {
 > > + struct snd_ml7213i2s *ml7213i2s;
 > > + spinlock_t lock;
 > > + unsigned int irq_pos;
 > > + unsigned int buf_pos;
 > > + struct snd_pcm_substream *substream;
 > > + struct cbdata cbd;              /* i2s callback info */
 > > + unsigned int channels;
 > > + unsigned int rw;
 > > + unsigned int rate;
 > > +};
 > > +
 > > +
 > > +static void i2s_read_period(struct snd_pcm_substream *substream);
 > > +static void i2s_write_period(struct snd_pcm_substream *substream);
 > > +static void read_done(void *cbd, int status);
 > > +static void write_done(void *cbd, int status);
 > > +
 > > +static inline void
 > > +snd_card_ml7213i2s_pcm_i2s_capture_start(struct snd_pcm_substream
 > > *substream)
 > > +{
 > > + i2s_read_period(substream);
 > > +}
 > > +
 > > +static inline void
 > > +snd_card_ml7213i2s_pcm_i2s_playback_start(struct snd_pcm_substream
 > > *substream)
 > > +{
 > > + i2s_write_period(substream);
 > > +}
 > > +
 > > +static int snd_card_ml7213i2s_pcm_capture_trigger
 > > + (struct snd_pcm_substream *substream, int cmd)
 > > +{
 > > + struct snd_pcm_runtime *runtime = substream->runtime;
 > > + struct snd_ml7213i2s_pcm *dpcm = runtime->private_data;
 > > + int err = 0;
 > > +
 > > + spin_lock(&dpcm->lock);
 > > + switch (cmd) {
 > > + case SNDRV_PCM_TRIGGER_START:
 > > + case SNDRV_PCM_TRIGGER_RESUME:
 > > + dpcm->cbd.stop = 0;
 > > + snd_card_ml7213i2s_pcm_i2s_capture_start(substream);
 > > + break;
 > > + case SNDRV_PCM_TRIGGER_STOP:
 > > + case SNDRV_PCM_TRIGGER_SUSPEND:
 > > + pr_debug("stop..\n");
 > > + if (!dpcm->cbd.stop)
 > > + dpcm->cbd.stop = 1;
 > > + else
 > > + pr_debug("already stopped %d\n", dpcm->cbd.stop);
 > > + break;
 > > + default:
 > > + err = -EINVAL;
 > > + break;
 > > + }
 > > + spin_unlock(&dpcm->lock);
 > > + return 0;
 > > +}
 > > +
 > > +
 > > +static int snd_card_ml7213i2s_pcm_playback_trigger
 > > + (struct snd_pcm_substream *substream, int cmd)
 > > +{
 > > + struct snd_pcm_runtime *runtime = substream->runtime;
 > > + struct snd_ml7213i2s_pcm *dpcm = runtime->private_data;
 > > + int err = 0;
 > > +
 > > + spin_lock(&dpcm->lock);
 > > + switch (cmd) {
 > > + case SNDRV_PCM_TRIGGER_START:
 > > + case SNDRV_PCM_TRIGGER_RESUME:
 > > + dpcm->cbd.stop = 0;
 > > + snd_card_ml7213i2s_pcm_i2s_playback_start(substream);
 > > + break;
 > > + case SNDRV_PCM_TRIGGER_STOP:
 > > + case SNDRV_PCM_TRIGGER_SUSPEND:
 > > + pr_debug("stop..\n");
 > > + if (!dpcm->cbd.stop)
 > > + dpcm->cbd.stop = 1;
 > > + else
 > > + pr_debug("already stopped %d\n", dpcm->cbd.stop);
 > > + break;
 > > + default:
 > > + err = -EINVAL;
 > > + break;
 > > + }
 > > + spin_unlock(&dpcm->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_pcm *dpcm = runtime->private_data;
 > > +
 > > + dpcm->irq_pos = 0;
 > > + dpcm->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_pcm *dpcm = runtime->private_data;
 > > +
 > > + return substream->runtime->period_size*dpcm->buf_pos;
 > > +}
 > > +
 > > +static struct snd_pcm_hardware snd_card_ml7213i2s_capture = {
 > > + .info = (SNDRV_PCM_INFO_MMAP |
 > > + SNDRV_PCM_INFO_INTERLEAVED |
 > > + SNDRV_PCM_INFO_RESUME |
 > > + SNDRV_PCM_INFO_MMAP_VALID),
 > > + .formats = USE_FORMATS,
 > > + .rates = USE_RATE,
 > > + .rate_min = USE_RATE_MIN,
 > > + .rate_max = USE_RATE_MAX,
 > > + .channels_min = USE_CHANNELS_MIN,
 > > + .channels_max = USE_CHANNELS_MAX,
 > > + .buffer_bytes_max = MAX_BUFFER_SIZE,
 > > + .period_bytes_min = MIN_PERIOD_SIZE,
 > > + .period_bytes_max = MAX_PERIOD_SIZE,
 > > + .periods_min = USE_PERIODS_MIN,
 > > + .periods_max = USE_PERIODS_MAX,
 > > + .fifo_size = 0,
 > > +};
 > > +
 > > +static void __snd_card_ml7213i2s_runtime_free(struct snd_pcm_runtime
 > > *runtime)
 > > +{
 > > + struct snd_ml7213i2s_pcm *dpcm;
 > > + static int cnt;
 > > +
 > > + dpcm = (struct snd_ml7213i2s_pcm *)runtime->private_data;
 > > + /* FIXME: This is just a big ball of race right now...
 > > + */
 > > + if (!dpcm->cbd.stop)
 > > + dpcm->cbd.stop = 1;
 > > + else {
 > > + while (dpcm->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_playback_free
 > > + (struct snd_pcm_runtime *runtime)
 > > +{
 > > + struct snd_ml7213i2s_pcm *dpcm;
 > > + dpcm = (struct snd_ml7213i2s_pcm *)runtime->private_data;
 > > +
 > > + __snd_card_ml7213i2s_runtime_free(runtime);
 > > +
 > > + ioh_i2s_release(dpcm->cbd.priv, IOH_PLAYBACK);
 > > + kfree(runtime->private_data);
 > > +}
 > > +
 > > +static void snd_card_ml7213i2s_runtime_capture_free
 > > + (struct snd_pcm_runtime *runtime)
 > > +{
 > > + struct snd_ml7213i2s_pcm *dpcm;
 > > + dpcm = (struct snd_ml7213i2s_pcm *)runtime->private_data;
 > > +
 > > + __snd_card_ml7213i2s_runtime_free(runtime);
 > > +
 > > + ioh_i2s_release(dpcm->cbd.priv, IOH_CAPTURE);
 > > + kfree(runtime->private_data);
 > > +}
 > > +
 > > +static int snd_card_codec_reg_read(unsigned char reg, unsigned 
char *val)
 > > +{
 > > + unsigned char data;
 > > +
 > > + if (i2c_master_send(i2c, &reg, 1) != 1)
 > > + return -1;
 > > +
 > > + if (i2c_master_recv(i2c, &data, 1) != 1)
 > > + return -1;
 > > +
 > > + *val = data;
 > > + return 0;
 > > +}
 > > +
 > > +static int snd_card_codec_reg_write(unsigned char reg, unsigned 
char val)
 > > +{
 > > + unsigned char buf[2] = {(reg|1), val};
 > > +
 > > + if (i2c_master_send(i2c, &buf[0], 2) != 2)
 > > + return -1;
 > > + return 0;
 > > +}
 > > +
 > > +static int snd_card_codec_set(int number, unsigned int rate,
 > > +       unsigned int channels)
 > > +{
 > > + unsigned char data;
 > > +
 > > + mutex_lock(&i2c_mutex);
 > > +
 > > + i2c = i2c_new_device(i2c_get_adapter(1), &ioh_hwmon_info[number]);
 > > + if (!i2c) {
 > > + mutex_unlock(&i2c_mutex);
 > > + return -1;
 > > + }
 > > +
 > > + snd_card_codec_reg_read(0x30, &data);
 > > +
 > > + snd_card_codec_reg_write(0x10, 0x01); /* soft reset assert */
 > > + snd_card_codec_reg_write(0x10, 0x00); /* soft reset negate */
 > > +
 > > + snd_card_codec_reg_write(0x0c, 0x00);
 > > +
 > > + switch (rate) {
 > > + case 16000:
 > > + snd_card_codec_reg_write(0x00, 0x03);
 > > + snd_card_codec_reg_write(0x02, 0x0c);
 > > + snd_card_codec_reg_write(0x04, 0x00);
 > > + snd_card_codec_reg_write(0x06, 0x20);
 > > + snd_card_codec_reg_write(0x08, 0x00);
 > > + snd_card_codec_reg_write(0x0a, 0x04);
 > > + break;
 > > + case 32000:
 > > + snd_card_codec_reg_write(0x00, 0x06);
 > > + snd_card_codec_reg_write(0x02, 0x0c);
 > > + snd_card_codec_reg_write(0x04, 0x00);
 > > + snd_card_codec_reg_write(0x06, 0x20);
 > > + snd_card_codec_reg_write(0x08, 0x00);
 > > + snd_card_codec_reg_write(0x0a, 0x04);
 > > + break;
 > > + case 48000:
 > > + snd_card_codec_reg_write(0x00, 0x08);
 > > + snd_card_codec_reg_write(0x02, 0x0c);
 > > + snd_card_codec_reg_write(0x04, 0x00);
 > > + snd_card_codec_reg_write(0x06, 0x30);
 > > + snd_card_codec_reg_write(0x08, 0x00);
 > > + snd_card_codec_reg_write(0x0a, 0x04);
 > > + break;
 > > + default:
 > > + pr_err("%s:this rate is no support for ml26124\n", __func__);
 > > + break;
 > > + }
 > > +
 > > + snd_card_codec_reg_write(0x0c, 0x03);
 > > + msleep(20);
 > > + snd_card_codec_reg_write(0x0c, 0x0f);
 > > + snd_card_codec_reg_write(0x0e, 0x04);
 > > +
 > > + snd_card_codec_reg_write(0x60, 0x00);
 > > + snd_card_codec_reg_write(0x62, 0x00);
 > > +
 > > + snd_card_codec_reg_write(0x64, 0x00); /* master/slave */
 > > +
 > > + snd_card_codec_reg_write(0x20, 0x02);
 > > + msleep(50);
 > > +
 > > + snd_card_codec_reg_write(0x20, 0x06);
 > > + snd_card_codec_reg_write(0x22, 0x0a);
 > > + snd_card_codec_reg_write(0x24, 0x02);
 > > + snd_card_codec_reg_write(0x26, 0x13);
 > > + snd_card_codec_reg_write(0x26, 0x1f);
 > > + snd_card_codec_reg_write(0x28, 0x02);
 > > +
 > > + snd_card_codec_reg_write(0x54, 0x02);
 > > + snd_card_codec_reg_write(0x5a, 0x00);
 > > +
 > > + snd_card_codec_reg_write(0x12, 0x01);
 > > + snd_card_codec_reg_write(0x12, 0x03);
 > > + msleep(20);
 > > + snd_card_codec_reg_write(0x66, 0x03);
 > > +
 > > + snd_card_codec_reg_write(0x3a, 0x27);
 > > + snd_card_codec_reg_write(0x32, 0x20);
 > > +
 > > + i2c_unregister_device(i2c);
 > > +
 > > + mutex_unlock(&i2c_mutex);
 > > + return 0;
 > > +}
 > > +
 > > +static int snd_card_codec_free(int number, unsigned int rate,
 > > +        unsigned int channels)
 > > +{
 > > + mutex_lock(&i2c_mutex);
 > > +
 > > + i2c = i2c_new_device(i2c_get_adapter(1), &ioh_hwmon_info[number]);
 > > + if (!i2c) {
 > > + mutex_unlock(&i2c_mutex);
 > > + return -1;
 > > + }
 > > +
 > > + snd_card_codec_reg_write(0x10, 1); /* soft reset assert */
 > > +
 > > + i2c_unregister_device(i2c);
 > > + mutex_unlock(&i2c_mutex);
 > > + 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 = runtime->private_data;
 > > +
 > > + dpcm->channels = params_channels(hw_params);
 > > + dpcm->rate = params_rate(hw_params);
 > > +
 > > +
 > > + switch (dpcm->rw) {
 > > + case SND_CAPTURE_SUBSTREAM:
 > > + setup_i2s_read(substream, substream->number);
 > > + break;
 > > + case SND_PLAYBACK_SUBSTREAM:
 > > + setup_i2s_write(substream, substream->number);
 > > + break;
 > > + default:
 > > + return -1;
 > > + }
 > > +
 > > + if (snd_card_codec_set(substream->number, dpcm->rate, 
dpcm->channels))
 > > + 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_pcm *dpcm = substream->runtime->private_data;
 > > +
 > > + if (snd_card_codec_free(substream->number, dpcm->rate, 
dpcm->channels))
 > > + return -1;
 > > +
 > > + return snd_pcm_lib_free_pages(substream);
 > > +}
 > > +
 > > +static struct snd_ml7213i2s_pcm *
 > > +new_pcm_stream(struct snd_pcm_substream *substream)
 > > +{
 > > + struct snd_ml7213i2s_pcm *dpcm;
 > > +
 > > + dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
 > > + if (!dpcm)
 > > + return dpcm;
 > > + spin_lock_init(&dpcm->lock);
 > > + dpcm->substream = substream;
 > > + return dpcm;
 > > +}
 > > +
 > > +static void i2s_read_period(struct snd_pcm_substream *substream)
 > > +{
 > > + struct snd_ml7213i2s_pcm *dpcm;
 > > + struct cbdata *cbd;
 > > + int period;
 > > + void *read_ptr;
 > > + int read_size;
 > > +
 > > + dpcm = substream->runtime->private_data;
 > > + cbd = &dpcm->cbd;
 > > +
 > > + if (!cbd->priv)
 > > + return;
 > > +
 > > + period = substream->runtime->period_size;
 > > +
 > > + read_ptr = substream->runtime->dma_area
 > > +   +(snd_pcm_lib_period_bytes(substream) * dpcm->irq_pos);
 > > + read_size = period * I2S_1PERIODS_BYTES * (dpcm->channels);
 > > +
 > > + ioh_i2s_read(cbd->priv,
 > > + read_ptr,
 > > + read_size,
 > > + substream,
 > > + read_done);
 > > +
 > > + dpcm->irq_pos = (dpcm->irq_pos + 1) % substream->runtime->periods;
 > > +}
 > > +
 > > +static void i2s_write_period(struct snd_pcm_substream *substream)
 > > +{
 > > + struct snd_ml7213i2s_pcm *dpcm;
 > > + struct cbdata *cbd;
 > > + int period;
 > > + void *write_ptr;
 > > + int write_size;
 > > +
 > > + dpcm = substream->runtime->private_data;
 > > + cbd = &dpcm->cbd;
 > > +
 > > + if (!cbd->priv)
 > > + return;
 > > +
 > > + period = substream->runtime->period_size;
 > > + write_ptr = substream->runtime->dma_area
 > > +   +(snd_pcm_lib_period_bytes(substream) * dpcm->irq_pos);
 > > + write_size = period * I2S_1PERIODS_BYTES * (dpcm->channels);
 > > +
 > > + ioh_i2s_write(cbd->priv,
 > > + write_ptr,
 > > + write_size,
 > > + substream,
 > > + write_done);
 > > +
 > > + dpcm->irq_pos = (dpcm->irq_pos + 1) % substream->runtime->periods;
 > > +}
 > > +
 > > +static void read_done(void *callback_data, int status)
 > > +{
 > > + struct snd_pcm_substream *substream =
 > > + (struct snd_pcm_substream *)callback_data;
 > > + struct snd_ml7213i2s_pcm *dpcm;
 > > + struct cbdata *cbd;
 > > + dpcm = substream->runtime->private_data;
 > > + cbd = &dpcm->cbd;
 > > + switch (status) {
 > > + case IOH_EOK:
 > > + if (!cbd->stop) {
 > > + i2s_read_period(substream);
 > > + dpcm->buf_pos = (dpcm->buf_pos + 1) %
 > > + substream->runtime->periods;
 > > + snd_pcm_period_elapsed(dpcm->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");
 > > + errors++;
 > > + cbd->stop = 2;
 > > + break;
 > > + }
 > > + if (cbd->stop)
 > > + pr_debug("stopping... %d\n", cbd->stop);
 > > +}
 > > +
 > > +static void write_done(void *callback_data, int status)
 > > +{
 > > + struct snd_pcm_substream *substream =
 > > + (struct snd_pcm_substream *)callback_data;
 > > + struct snd_ml7213i2s_pcm *dpcm;
 > > + struct cbdata *cbd;
 > > + 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;
 > > + }
 > > + dpcm = substream->runtime->private_data;
 > > + cbd = &dpcm->cbd;
 > > +
 > > + switch (status) {
 > > + case IOH_EOK:
 > > + if (!cbd->stop) {
 > > + i2s_write_period(substream);
 > > + dpcm->buf_pos = (dpcm->buf_pos + 1) %
 > > + substream->runtime->periods;
 > > + snd_pcm_period_elapsed(dpcm->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;
 > > + }
 > > +}
 > > +#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 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 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)
 > > +
 > > +static void i2s_set_default(struct ioh_i2s_config_reg *config)
 > > +{
 > > + /* Set ML7213 IOH register default value */
 > > + config->cmn.i2simask = 0x003f003f;
 > > +
 > > + config->tx.i2saftx = 0x1F;
 > > + config->tx.i2smsktx = 0x1F;
 > > + config->tx.i2sisttx = 0xC;
 > > +
 > > + config->rx.i2scntrx = 0x3F;
 > > + config->rx.i2smskrx = 0x1F;
 > > + config->rx.i2sistrx = 0xC;
 > > +}
 > > +
 > > +
 > > +static int i2s_configure_rate(unsigned int rate)
 > > +{
 > > + int value;
 > > +
 > > + switch (rate) {
 > > + /* LR=12288/MCLKFS, BCLKFS=channels*format */
 > > + case 16000:
 > > + value = I2SCLKCNT_MCLKFS_768FS | I2SCLKCNT_BCLKFS_32FS;
 > > + break;
 > > + case 32000:
 > > + value = I2SCLKCNT_MCLKFS_384FS | I2SCLKCNT_BCLKFS_32FS;
 > > + break;
 > > + case 48000:
 > > + value = I2SCLKCNT_MCLKFS_256FS | I2SCLKCNT_BCLKFS_32FS;
 > > + break;
 > > + default:
 > > + value = I2SCLKCNT_MCLKFS_256FS | I2SCLKCNT_BCLKFS_32FS;
 > > + break;
 > > + }
 > > + return value;
 > > +}
 > > +
 > > +static void i2s_slave_configure_reg(struct ioh_i2s_config_reg *config,
 > > +     unsigned int rate)
 > > +{
 > > + memset(config, 0, sizeof(*config));
 > > +
 > > + i2s_set_default(config);
 > > +
 > > + /* Configuration */
 > > + config->tx.i2scnttx = I2SCNTTX_TXDABIT_16BIT;
 > > + config->tx.i2saetx = I2S_AEMPTY_THRESH / 2; /* Almost empty 
threshold */
 > > + config->rx.i2scntrx = I2SCNTRX_RXDABIT_16BIT;
 > > + config->rx.i2safrx = I2S_AFULL_THRESH / 2; /* Almost full 
threshold */
 > > + config->cmn.i2sclkcnt = i2s_configure_rate(rate);
 > > +}
 > > +
 > > +static void i2s_master_configure_reg(struct ioh_i2s_config_reg 
*config,
 > > +      unsigned int rate)
 > > +{
 > > + memset(config, 0, sizeof(*config));
 > > +
 > > + i2s_set_default(config);
 > > +
 > > + /* Configuration */
 > > + config->tx.i2scnttx = I2SCNTTX_TXDABIT_16BIT;
 > > + config->tx.i2saetx = I2S_AEMPTY_THRESH / 2; /* Almost empty 
threshold */
 > > + config->rx.i2scntrx = I2SCNTRX_RXDABIT_16BIT;
 > > + config->rx.i2safrx = I2S_AFULL_THRESH / 2; /* Almost full 
threshold */
 > > + config->cmn.i2sclkcnt = i2s_configure_rate(rate) | I2SCLKCNT_MSSEL;
 > > +}
 > > +
 > > +static void setup_i2s_read(struct snd_pcm_substream *substream, 
int ch)
 > > +{
 > > + struct snd_ml7213i2s_pcm *dpcm;
 > > + struct ioh_i2s_config_reg config;
 > > +
 > > + dpcm = substream->runtime->private_data;
 > > +
 > > + dpcm->cbd.priv = ioh_i2s_open(ch, IOH_CAPTURE, "radio-i2s-in",
 > > + substream,
 > > + read_done);
 > > +
 > > + if (!dpcm->cbd.priv) {
 > > + pr_err("%s: Cannot open the device\n", __func__);
 > > + return;
 > > + }
 > > +
 > > + if (ignore_overrun)
 > > + ioh_i2s_ignore_rx_overrun(dpcm->cbd.priv);
 > > +
 > > + if (I2S_READ_MASTER)
 > > + i2s_master_configure_reg(&config, dpcm->rate);
 > > + else
 > > + i2s_slave_configure_reg(&config, dpcm->rate);
 > > +
 > > + ioh_i2s_configure_i2s_regs(dpcm->cbd.priv, 0, &config, IOH_CAPTURE);
 > > +}
 > > +
 > > +static void setup_i2s_write(struct snd_pcm_substream *substream, 
int ch)
 > > +{
 > > + struct snd_ml7213i2s_pcm *dpcm;
 > > + struct ioh_i2s_config_reg config;
 > > +
 > > + dpcm = substream->runtime->private_data;
 > > +
 > > + dpcm->cbd.priv = ioh_i2s_open(ch, IOH_PLAYBACK, "radio-i2s-out",
 > > + substream,
 > > + write_done);
 > > +
 > > + if (!dpcm->cbd.priv) {
 > > + pr_err("%s: Cannot open the device\n", __func__);
 > > + return;
 > > + }
 > > +
 > > + if (ignore_overrun)
 > > + ioh_i2s_ignore_rx_overrun(dpcm->cbd.priv);
 > > +
 > > + if (I2S_WRITE_MASTER)
 > > + i2s_master_configure_reg(&config, dpcm->rate);
 > > + else
 > > + i2s_slave_configure_reg(&config, dpcm->rate);
 > > +
 > > + ioh_i2s_configure_i2s_regs(dpcm->cbd.priv, 0, &config, IOH_PLAYBACK);
 > > +}
 > > +
 > > +static int snd_card_ml7213i2s_capture_open(struct snd_pcm_substream
 > > *substream)
 > > +{
 > > + struct snd_pcm_runtime *runtime = substream->runtime;
 > > + struct snd_ml7213i2s_pcm *dpcm;
 > > + int err;
 > > +
 > > + dpcm = new_pcm_stream(substream);
 > > + if (dpcm == NULL)
 > > + return -ENOMEM;
 > > +
 > > + runtime->private_data = dpcm;
 > > + /* makes the infrastructure responsible for freeing dpcm */
 > > + runtime->private_free = snd_card_ml7213i2s_runtime_capture_free;
 > > + runtime->hw = snd_card_ml7213i2s_capture;
 > > +
 > > + dpcm->rw = SND_CAPTURE_SUBSTREAM;
 > > +
 > > + err = add_capture_constraints(runtime);
 > > + if (err < 0)
 > > + return err;
 > > +
 > > + return 0;
 > > +}
 > > +
 > > +static int snd_card_ml7213i2s_playback_open(struct snd_pcm_substream
 > > *substream)
 > > +{
 > > + struct snd_pcm_runtime *runtime = substream->runtime;
 > > + struct snd_ml7213i2s_pcm *dpcm;
 > > + int err;
 > > +
 > > + dpcm = new_pcm_stream(substream);
 > > + if (dpcm == NULL)
 > > + return -ENOMEM;
 > > +
 > > + runtime->private_data = dpcm;
 > > + /* makes the infrastructure responsible for freeing dpcm */
 > > + runtime->private_free = snd_card_ml7213i2s_runtime_playback_free;
 > > + runtime->hw = snd_card_ml7213i2s_capture;
 > > +
 > > + dpcm->rw = SND_PLAYBACK_SUBSTREAM;
 > > +
 > > + err = add_capture_constraints(runtime);
 > > + if (err < 0)
 > > + return err;
 > > +
 > > + return 0;
 > > +}
 > > +
 > > +static int snd_card_ml7213i2s_capture_close(
 > > + struct snd_pcm_substream *substream)
 > > +{
 > > + return 0;
 > > +}
 > > +
 > > +static int snd_card_ml7213i2s_playback_close(
 > > + struct snd_pcm_substream *substream)
 > > +{
 > > + return 0;
 > > +}
 > > +
 > > +static struct snd_pcm_ops snd_card_ml7213i2s_capture_ops = {
 > > + .open = snd_card_ml7213i2s_capture_open,
 > > + .close = snd_card_ml7213i2s_capture_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_capture_trigger,
 > > + .pointer = snd_card_ml7213i2s_pcm_pointer,
 > > +};
 > > +
 > > +static struct snd_pcm_ops snd_card_ml7213i2s_playback_ops = {
 > > + .open = snd_card_ml7213i2s_playback_open,
 > > + .close = snd_card_ml7213i2s_playback_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_playback_trigger,
 > > + .pointer = snd_card_ml7213i2s_pcm_pointer,
 > > +};
 > > +
 > > +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;
 > > + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
 > > + &snd_card_ml7213i2s_capture_ops);
 > > + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
 > > + &snd_card_ml7213i2s_playback_ops);
 > > + 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};
 > > +
 > > +static int __devinit snd_ml7213i2s_probe(struct platform_device 
*devptr)
 > > +{
 > > + struct snd_card *card;
 > > + struct snd_ml7213i2s *ml7213i2s;
 > > + int err;
 > > + int dev = devptr->id;
 > > +
 > > + err = snd_card_create(index, "ml7213i2s", THIS_MODULE,
 > > +       sizeof(struct snd_ml7213i2s), &card);
 > > +
 > > + if (err < 0)
 > > + return err;
 > > + ml7213i2s = card->private_data;
 > > + ml7213i2s->card = card;
 > > + err = snd_card_ml7213i2s_pcm(ml7213i2s, 0);
 > > + if (err < 0)
 > > + goto __nodev;
 > > +
 > > + strcpy(card->driver, "ml7213i2s");
 > > + strcpy(card->shortname, "ml7213i2s");
 > > + sprintf(card->longname, "ml7213i2s %i", dev + 1);
 > > +
 > > + snd_card_set_dev(card, &devptr->dev);
 > > +
 > > + snd_device_new(card, SNDRV_DEV_LOWLEVEL, ml7213i2s, &ops);
 > > +
 > > + mutex_init(&i2c_mutex);
 > > +
 > > + err = snd_card_register(card);
 > > + if (err == 0) {
 > > + platform_set_drvdata(devptr, card);
 > > + return 0;
 > > + }
 > > +__nodev:
 > > + snd_card_free(card);
 > > + return err;
 > > +}
 > > +
 > > +static int __devexit snd_ml7213i2s_remove(struct platform_device 
*devptr)
 > > +{
 > > + snd_card_free(platform_get_drvdata(devptr));
 > > + platform_set_drvdata(devptr, NULL);
 > > + return 0;
 > > +}
 > > +
 > > +#ifdef CONFIG_PM
 > > +static int snd_ml7213i2s_suspend(struct platform_device *pdev,
 > > +   pm_message_t state)
 > > +{
 > > + struct snd_card *card = platform_get_drvdata(pdev);
 > > + struct snd_ml7213i2s *ml7213i2s = card->private_data;
 > > +
 > > + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 > > + snd_pcm_suspend_all(ml7213i2s->pcm);
 > > + return 0;
 > > +}
 > > +
 > > +static int snd_ml7213i2s_resume(struct platform_device *pdev)
 > > +{
 > > + struct snd_card *card = platform_get_drvdata(pdev);
 > > +
 > > + snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 > > + return 0;
 > > +}
 > > +#endif
 > > +
 > > +
 > > +#define snd_ml7213i2s_DRIVER "snd_ml7213i2s"
 > > +
 > > +static struct platform_driver snd_ml7213i2s_driver = {
 > > + .probe = snd_ml7213i2s_probe,
 > > + .remove = __devexit_p(snd_ml7213i2s_remove),
 > > +#ifdef CONFIG_PM
 > > + .suspend = snd_ml7213i2s_suspend,
 > > + .resume = snd_ml7213i2s_resume,
 > > +#endif
 > > + .driver = {
 > > + .name = snd_ml7213i2s_DRIVER
 > > + },
 > > +};
 > > +
 > > +static void snd_ml7213i2s_unregister(void)
 > > +{
 > > + platform_device_unregister(_device);
 > > + platform_driver_unregister(&snd_ml7213i2s_driver);
 > > +}
 > > +
 > > +static int __init alsa_card_ml7213i2s_init(void)
 > > +{
 > > + int  err;
 > > + struct platform_device *device;
 > > +
 > > + err = platform_driver_register(&snd_ml7213i2s_driver);
 > > + if (err < 0)
 > > + return err;
 > > +
 > > + device = platform_device_register_simple(snd_ml7213i2s_DRIVER,
 > > + 0, NULL, 0);
 > > + if (IS_ERR(device))
 > > + return -1;
 > > + if (!platform_get_drvdata(device)) {
 > > + platform_device_unregister(device);
 > > + return -1;
 > > + }
 > > + _device = device;
 > > +
 > > + return 0;
 > > +}
 > > +
 > > +static void __exit alsa_card_ml7213i2s_exit(void)
 > > +{
 > > + snd_ml7213i2s_unregister();
 > > +}
 > > +
 > > +module_init(alsa_card_ml7213i2s_init)
 > > +module_exit(alsa_card_ml7213i2s_exit)
 > > --
 > > 1.7.4
 > >

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

end of thread, other threads:[~2011-11-10 11:22 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-07-06 10:27 [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH Toshiharu Okada
2011-07-06 11:06 ` Takashi Iwai
2011-07-07  7:54   ` Toshiharu Okada
2011-07-07  9:00     ` Takashi Iwai
2011-07-07 12:35       ` Toshiharu Okada
2011-07-07 16:38         ` Takashi Iwai
2011-07-08 10:52           ` Toshiharu Okada
2011-07-09  1:34           ` Mark Brown
2011-07-09  7:40             ` Takashi Iwai
     [not found] <70D251FDDC55405882A8447CC455D56E@hacdom.okisemi.com>
     [not found] ` <8486F61FC3B94B908BFE654234DD6C97@hacdom.okisemi.com>
2011-10-17  4:28   ` Tomoya MORINAGA
2011-10-21 14:16     ` Takashi Iwai
2011-10-24 12:12       ` Tomoya MORINAGA
2011-10-24 12:20         ` Mark Brown
2011-11-08  9:03           ` Tomoya MORINAGA
2011-11-08 10:39             ` Mark Brown
2011-11-09  2:56               ` Tomoya MORINAGA
2011-11-08 14:38             ` Mark Brown
2011-11-10  5:00               ` Tomoya MORINAGA
2011-11-10 11:22                 ` 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).