lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <s5hhb6z3byw.wl%tiwai@suse.de>
Date:	Wed, 06 Jul 2011 13:06:15 +0200
From:	Takashi Iwai <tiwai@...e.de>
To:	Toshiharu Okada <toshiharu-linux@....okisemi.com>
Cc:	perex@...ex.cz, alsa-devel@...a-project.org,
	linux-kernel@...r.kernel.org, qi.wang@...el.com,
	yong.y.wang@...el.com, joel.clark@...el.com,
	kok.howg.ewe@...el.com, tomoya-linux@....okisemi.com
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@....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
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ