zte's code,first commit

Change-Id: I9a04da59e459a9bc0d67f101f700d9d7dc8d681b
diff --git a/ap/os/linux/linux-3.4.x/sound/soc/sh/Kconfig b/ap/os/linux/linux-3.4.x/sound/soc/sh/Kconfig
new file mode 100644
index 0000000..d8e06a6
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/sound/soc/sh/Kconfig
@@ -0,0 +1,80 @@
+menu "SoC Audio support for SuperH"
+	depends on SUPERH || ARCH_SHMOBILE
+
+config SND_SOC_PCM_SH7760
+	tristate "SoC Audio support for Renesas SH7760"
+	depends on CPU_SUBTYPE_SH7760 && SH_DMABRG
+	help
+	  Enable this option for SH7760 AC97/I2S audio support.
+
+
+##
+## Audio unit modules
+##
+
+config SND_SOC_SH4_HAC
+	tristate
+	select AC97_BUS
+	select SND_SOC_AC97_BUS
+
+config SND_SOC_SH4_SSI
+	tristate
+
+config SND_SOC_SH4_FSI
+	tristate "SH4 FSI support"
+	help
+	  This option enables FSI sound support
+
+config SND_SOC_SH4_SIU
+	tristate
+	depends on (SUPERH || ARCH_SHMOBILE) && HAVE_CLK
+	select DMA_ENGINE
+	select DMADEVICES
+	select SH_DMAE
+	select FW_LOADER
+
+##
+## Boards
+##
+
+config SND_SH7760_AC97
+	tristate "SH7760 AC97 sound support"
+	depends on CPU_SUBTYPE_SH7760 && SND_SOC_PCM_SH7760
+	select SND_SOC_SH4_HAC
+	select SND_SOC_AC97_CODEC
+	help
+	  This option enables generic sound support for the first
+	  AC97 unit of the SH7760.
+
+config SND_FSI_AK4642
+	tristate "FSI-AK4642 sound support"
+	depends on SND_SOC_SH4_FSI && I2C
+	select SND_SOC_AK4642
+	help
+	  This option enables generic sound support for the
+	  FSI - AK4642 unit
+
+config SND_FSI_DA7210
+	tristate "FSI-DA7210 sound support"
+	depends on SND_SOC_SH4_FSI && I2C
+	select SND_SOC_DA7210
+	help
+	  This option enables generic sound support for the
+	  FSI - DA7210 unit
+
+config SND_FSI_HDMI
+	tristate "FSI-HDMI sound support"
+	depends on SND_SOC_SH4_FSI && FB_SH_MOBILE_HDMI
+	help
+	  This option enables generic sound support for the
+	  FSI - HDMI unit
+
+config SND_SIU_MIGOR
+	tristate "SIU sound support on Migo-R"
+	depends on SH_MIGOR
+	select SND_SOC_SH4_SIU
+	select SND_SOC_WM8978
+	help
+	  This option enables sound support for the SH7722 Migo-R board
+
+endmenu
diff --git a/ap/os/linux/linux-3.4.x/sound/soc/sh/Makefile b/ap/os/linux/linux-3.4.x/sound/soc/sh/Makefile
new file mode 100644
index 0000000..94476d4
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/sound/soc/sh/Makefile
@@ -0,0 +1,26 @@
+## DMA engines
+snd-soc-dma-sh7760-objs	:= dma-sh7760.o
+obj-$(CONFIG_SND_SOC_PCM_SH7760)	+= snd-soc-dma-sh7760.o
+
+## audio units found on some SH-4
+snd-soc-hac-objs	:= hac.o
+snd-soc-ssi-objs	:= ssi.o
+snd-soc-fsi-objs	:= fsi.o
+snd-soc-siu-objs	:= siu_pcm.o siu_dai.o
+obj-$(CONFIG_SND_SOC_SH4_HAC)	+= snd-soc-hac.o
+obj-$(CONFIG_SND_SOC_SH4_SSI)	+= snd-soc-ssi.o
+obj-$(CONFIG_SND_SOC_SH4_FSI)	+= snd-soc-fsi.o
+obj-$(CONFIG_SND_SOC_SH4_SIU)	+= snd-soc-siu.o
+
+## boards
+snd-soc-sh7760-ac97-objs	:= sh7760-ac97.o
+snd-soc-fsi-ak4642-objs		:= fsi-ak4642.o
+snd-soc-fsi-da7210-objs		:= fsi-da7210.o
+snd-soc-fsi-hdmi-objs		:= fsi-hdmi.o
+snd-soc-migor-objs		:= migor.o
+
+obj-$(CONFIG_SND_SH7760_AC97)	+= snd-soc-sh7760-ac97.o
+obj-$(CONFIG_SND_FSI_AK4642)	+= snd-soc-fsi-ak4642.o
+obj-$(CONFIG_SND_FSI_DA7210)	+= snd-soc-fsi-da7210.o
+obj-$(CONFIG_SND_FSI_HDMI)	+= snd-soc-fsi-hdmi.o
+obj-$(CONFIG_SND_SIU_MIGOR)	+= snd-soc-migor.o
diff --git a/ap/os/linux/linux-3.4.x/sound/soc/sh/dma-sh7760.c b/ap/os/linux/linux-3.4.x/sound/soc/sh/dma-sh7760.c
new file mode 100644
index 0000000..312715f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/sound/soc/sh/dma-sh7760.c
@@ -0,0 +1,376 @@
+/*
+ * SH7760 ("camelot") DMABRG audio DMA unit support
+ *
+ * Copyright (C) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
+ *  licensed under the terms outlined in the file COPYING at the root
+ *  of the linux kernel sources.
+ *
+ * The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which
+ * trigger an interrupt when one half of the programmed transfer size
+ * has been xmitted.
+ *
+ * FIXME: little-endian only for now
+ */
+
+#include <linux/module.h>
+#include <linux/gfp.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <asm/dmabrg.h>
+
+
+/* registers and bits */
+#define BRGATXSAR	0x00
+#define BRGARXDAR	0x04
+#define BRGATXTCR	0x08
+#define BRGARXTCR	0x0C
+#define BRGACR		0x10
+#define BRGATXTCNT	0x14
+#define BRGARXTCNT	0x18
+
+#define ACR_RAR		(1 << 18)
+#define ACR_RDS		(1 << 17)
+#define ACR_RDE		(1 << 16)
+#define ACR_TAR		(1 << 2)
+#define ACR_TDS		(1 << 1)
+#define ACR_TDE		(1 << 0)
+
+/* receiver/transmitter data alignment */
+#define ACR_RAM_NONE	(0 << 24)
+#define ACR_RAM_4BYTE	(1 << 24)
+#define ACR_RAM_2WORD	(2 << 24)
+#define ACR_TAM_NONE	(0 << 8)
+#define ACR_TAM_4BYTE	(1 << 8)
+#define ACR_TAM_2WORD	(2 << 8)
+
+
+struct camelot_pcm {
+	unsigned long mmio;  /* DMABRG audio channel control reg MMIO */
+	unsigned int txid;    /* ID of first DMABRG IRQ for this unit */
+
+	struct snd_pcm_substream *tx_ss;
+	unsigned long tx_period_size;
+	unsigned int  tx_period;
+
+	struct snd_pcm_substream *rx_ss;
+	unsigned long rx_period_size;
+	unsigned int  rx_period;
+
+} cam_pcm_data[2] = {
+	{
+		.mmio	=	0xFE3C0040,
+		.txid	=	DMABRGIRQ_A0TXF,
+	},
+	{
+		.mmio	=	0xFE3C0060,
+		.txid	=	DMABRGIRQ_A1TXF,
+	},
+};
+
+#define BRGREG(x)	(*(unsigned long *)(cam->mmio + (x)))
+
+/*
+ * set a minimum of 16kb per period, to avoid interrupt-"storm" and
+ * resulting skipping. In general, the bigger the minimum size, the
+ * better for overall system performance. (The SH7760 is a puny CPU
+ * with a slow SDRAM interface and poor internal bus bandwidth,
+ * *especially* when the LCDC is active).  The minimum for the DMAC
+ * is 8 bytes; 16kbytes are enough to get skip-free playback of a
+ * 44kHz/16bit/stereo MP3 on a lightly loaded system, and maintain
+ * reasonable responsiveness in MPlayer.
+ */
+#define DMABRG_PERIOD_MIN		16 * 1024
+#define DMABRG_PERIOD_MAX		0x03fffffc
+#define DMABRG_PREALLOC_BUFFER		32 * 1024
+#define DMABRG_PREALLOC_BUFFER_MAX	32 * 1024
+
+/* support everything the SSI supports */
+#define DMABRG_RATES	\
+	SNDRV_PCM_RATE_8000_192000
+
+#define DMABRG_FMTS	\
+	(SNDRV_PCM_FMTBIT_S8      | SNDRV_PCM_FMTBIT_U8      |	\
+	 SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_U16_LE  |	\
+	 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE |	\
+	 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE |	\
+	 SNDRV_PCM_FMTBIT_S32_LE  | SNDRV_PCM_FMTBIT_U32_LE)
+
+static struct snd_pcm_hardware camelot_pcm_hardware = {
+	.info = (SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_BATCH),
+	.formats =	DMABRG_FMTS,
+	.rates =	DMABRG_RATES,
+	.rate_min =		8000,
+	.rate_max =		192000,
+	.channels_min =		2,
+	.channels_max =		8,		/* max of the SSI */
+	.buffer_bytes_max =	DMABRG_PERIOD_MAX,
+	.period_bytes_min =	DMABRG_PERIOD_MIN,
+	.period_bytes_max =	DMABRG_PERIOD_MAX / 2,
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		128,
+};
+
+static void camelot_txdma(void *data)
+{
+	struct camelot_pcm *cam = data;
+	cam->tx_period ^= 1;
+	snd_pcm_period_elapsed(cam->tx_ss);
+}
+
+static void camelot_rxdma(void *data)
+{
+	struct camelot_pcm *cam = data;
+	cam->rx_period ^= 1;
+	snd_pcm_period_elapsed(cam->rx_ss);
+}
+
+static int camelot_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
+	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
+	int ret, dmairq;
+
+	snd_soc_set_runtime_hwparams(substream, &camelot_pcm_hardware);
+
+	/* DMABRG buffer half/full events */
+	dmairq = (recv) ? cam->txid + 2 : cam->txid;
+	if (recv) {
+		cam->rx_ss = substream;
+		ret = dmabrg_request_irq(dmairq, camelot_rxdma, cam);
+		if (unlikely(ret)) {
+			pr_debug("audio unit %d irqs already taken!\n",
+			     rtd->cpu_dai->id);
+			return -EBUSY;
+		}
+		(void)dmabrg_request_irq(dmairq + 1,camelot_rxdma, cam);
+	} else {
+		cam->tx_ss = substream;
+		ret = dmabrg_request_irq(dmairq, camelot_txdma, cam);
+		if (unlikely(ret)) {
+			pr_debug("audio unit %d irqs already taken!\n",
+			     rtd->cpu_dai->id);
+			return -EBUSY;
+		}
+		(void)dmabrg_request_irq(dmairq + 1, camelot_txdma, cam);
+	}
+	return 0;
+}
+
+static int camelot_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
+	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
+	int dmairq;
+
+	dmairq = (recv) ? cam->txid + 2 : cam->txid;
+
+	if (recv)
+		cam->rx_ss = NULL;
+	else
+		cam->tx_ss = NULL;
+
+	dmabrg_free_irq(dmairq + 1);
+	dmabrg_free_irq(dmairq);
+
+	return 0;
+}
+
+static int camelot_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
+	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
+	int ret;
+
+	ret = snd_pcm_lib_malloc_pages(substream,
+				       params_buffer_bytes(hw_params));
+	if (ret < 0)
+		return ret;
+
+	if (recv) {
+		cam->rx_period_size = params_period_bytes(hw_params);
+		cam->rx_period = 0;
+	} else {
+		cam->tx_period_size = params_period_bytes(hw_params);
+		cam->tx_period = 0;
+	}
+	return 0;
+}
+
+static int camelot_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int camelot_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
+
+	pr_debug("PCM data: addr 0x%08ulx len %d\n",
+		 (u32)runtime->dma_addr, runtime->dma_bytes);
+ 
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		BRGREG(BRGATXSAR) = (unsigned long)runtime->dma_area;
+		BRGREG(BRGATXTCR) = runtime->dma_bytes;
+	} else {
+		BRGREG(BRGARXDAR) = (unsigned long)runtime->dma_area;
+		BRGREG(BRGARXTCR) = runtime->dma_bytes;
+	}
+
+	return 0;
+}
+
+static inline void dmabrg_play_dma_start(struct camelot_pcm *cam)
+{
+	unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
+	/* start DMABRG engine: XFER start, auto-addr-reload */
+	BRGREG(BRGACR) = acr | ACR_TDE | ACR_TAR | ACR_TAM_2WORD;
+}
+
+static inline void dmabrg_play_dma_stop(struct camelot_pcm *cam)
+{
+	unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
+	/* forcibly terminate data transmission */
+	BRGREG(BRGACR) = acr | ACR_TDS;
+}
+
+static inline void dmabrg_rec_dma_start(struct camelot_pcm *cam)
+{
+	unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
+	/* start DMABRG engine: recv start, auto-reload */
+	BRGREG(BRGACR) = acr | ACR_RDE | ACR_RAR | ACR_RAM_2WORD;
+}
+
+static inline void dmabrg_rec_dma_stop(struct camelot_pcm *cam)
+{
+	unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
+	/* forcibly terminate data receiver */
+	BRGREG(BRGACR) = acr | ACR_RDS;
+}
+
+static int camelot_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
+	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (recv)
+			dmabrg_rec_dma_start(cam);
+		else
+			dmabrg_play_dma_start(cam);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (recv)
+			dmabrg_rec_dma_stop(cam);
+		else
+			dmabrg_play_dma_stop(cam);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t camelot_pos(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
+	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
+	unsigned long pos;
+
+	/* cannot use the DMABRG pointer register: under load, by the
+	 * time ALSA comes around to read the register, it is already
+	 * far ahead (or worse, already done with the fragment) of the
+	 * position at the time the IRQ was triggered, which results in
+	 * fast-playback sound in my test application (ScummVM)
+	 */
+	if (recv)
+		pos = cam->rx_period ? cam->rx_period_size : 0;
+	else
+		pos = cam->tx_period ? cam->tx_period_size : 0;
+
+	return bytes_to_frames(runtime, pos);
+}
+
+static struct snd_pcm_ops camelot_pcm_ops = {
+	.open		= camelot_pcm_open,
+	.close		= camelot_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= camelot_hw_params,
+	.hw_free	= camelot_hw_free,
+	.prepare	= camelot_prepare,
+	.trigger	= camelot_trigger,
+	.pointer	= camelot_pos,
+};
+
+static void camelot_pcm_free(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int camelot_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+
+	/* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
+	 * in MMAP mode (i.e. aplay -M)
+	 */
+	snd_pcm_lib_preallocate_pages_for_all(pcm,
+		SNDRV_DMA_TYPE_CONTINUOUS,
+		snd_dma_continuous_data(GFP_KERNEL),
+		DMABRG_PREALLOC_BUFFER,	DMABRG_PREALLOC_BUFFER_MAX);
+
+	return 0;
+}
+
+static struct snd_soc_platform_driver sh7760_soc_platform = {
+	.ops		= &camelot_pcm_ops,
+	.pcm_new	= camelot_pcm_new,
+	.pcm_free	= camelot_pcm_free,
+};
+
+static int __devinit sh7760_soc_platform_probe(struct platform_device *pdev)
+{
+	return snd_soc_register_platform(&pdev->dev, &sh7760_soc_platform);
+}
+
+static int __devexit sh7760_soc_platform_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+
+static struct platform_driver sh7760_pcm_driver = {
+	.driver = {
+			.name = "sh7760-pcm-audio",
+			.owner = THIS_MODULE,
+	},
+
+	.probe = sh7760_soc_platform_probe,
+	.remove = __devexit_p(sh7760_soc_platform_remove),
+};
+
+module_platform_driver(sh7760_pcm_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/ap/os/linux/linux-3.4.x/sound/soc/sh/fsi-ak4642.c b/ap/os/linux/linux-3.4.x/sound/soc/sh/fsi-ak4642.c
new file mode 100644
index 0000000..97f540a
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/sound/soc/sh/fsi-ak4642.c
@@ -0,0 +1,108 @@
+/*
+ * FSI-AK464x sound support for ms7724se
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <sound/sh_fsi.h>
+
+struct fsi_ak4642_data {
+	const char *name;
+	const char *card;
+	const char *cpu_dai;
+	const char *codec;
+	const char *platform;
+	int id;
+};
+
+static int fsi_ak4642_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dai *codec = rtd->codec_dai;
+	struct snd_soc_dai *cpu = rtd->cpu_dai;
+	int ret;
+
+	ret = snd_soc_dai_set_fmt(codec, SND_SOC_DAIFMT_LEFT_J |
+					 SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(codec, 0, 11289600, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_LEFT_J |
+				       SND_SOC_DAIFMT_CBS_CFS);
+
+	return ret;
+}
+
+static struct snd_soc_dai_link fsi_dai_link = {
+	.codec_dai_name	= "ak4642-hifi",
+	.init		= fsi_ak4642_dai_init,
+};
+
+static struct snd_soc_card fsi_soc_card  = {
+	.owner		= THIS_MODULE,
+	.dai_link	= &fsi_dai_link,
+	.num_links	= 1,
+};
+
+static struct platform_device *fsi_snd_device;
+
+static int fsi_ak4642_probe(struct platform_device *pdev)
+{
+	int ret = -ENOMEM;
+	struct fsi_ak4642_info *pinfo = pdev->dev.platform_data;
+
+	if (!pinfo) {
+		dev_err(&pdev->dev, "no info for fsi ak4642\n");
+		goto out;
+	}
+
+	fsi_snd_device = platform_device_alloc("soc-audio", pinfo->id);
+	if (!fsi_snd_device)
+		goto out;
+
+	fsi_dai_link.name		= pinfo->name;
+	fsi_dai_link.stream_name	= pinfo->name;
+	fsi_dai_link.cpu_dai_name	= pinfo->cpu_dai;
+	fsi_dai_link.platform_name	= pinfo->platform;
+	fsi_dai_link.codec_name		= pinfo->codec;
+	fsi_soc_card.name		= pinfo->card;
+
+	platform_set_drvdata(fsi_snd_device, &fsi_soc_card);
+	ret = platform_device_add(fsi_snd_device);
+
+	if (ret)
+		platform_device_put(fsi_snd_device);
+
+out:
+	return ret;
+}
+
+static int fsi_ak4642_remove(struct platform_device *pdev)
+{
+	platform_device_unregister(fsi_snd_device);
+	return 0;
+}
+
+static struct platform_driver fsi_ak4642 = {
+	.driver = {
+		.name	= "fsi-ak4642-audio",
+	},
+	.probe		= fsi_ak4642_probe,
+	.remove		= fsi_ak4642_remove,
+};
+
+module_platform_driver(fsi_ak4642);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic SH4 FSI-AK4642 sound card");
+MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
diff --git a/ap/os/linux/linux-3.4.x/sound/soc/sh/fsi-da7210.c b/ap/os/linux/linux-3.4.x/sound/soc/sh/fsi-da7210.c
new file mode 100644
index 0000000..1dd3354
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/sound/soc/sh/fsi-da7210.c
@@ -0,0 +1,81 @@
+/*
+ * fsi-da7210.c
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.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;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <sound/sh_fsi.h>
+
+static int fsi_da7210_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dai *codec = rtd->codec_dai;
+	struct snd_soc_dai *cpu = rtd->cpu_dai;
+	int ret;
+
+	ret = snd_soc_dai_set_fmt(codec,
+				   SND_SOC_DAIFMT_I2S |
+				   SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_I2S |
+				       SND_SOC_DAIFMT_CBS_CFS);
+
+	return ret;
+}
+
+static struct snd_soc_dai_link fsi_da7210_dai = {
+	.name		= "DA7210",
+	.stream_name	= "DA7210",
+	.cpu_dai_name	= "fsib-dai", /* FSI B */
+	.codec_dai_name	= "da7210-hifi",
+	.platform_name	= "sh_fsi.0",
+	.codec_name	= "da7210-codec.0-001a",
+	.init		= fsi_da7210_init,
+};
+
+static struct snd_soc_card fsi_soc_card = {
+	.name		= "FSI-DA7210",
+	.owner		= THIS_MODULE,
+	.dai_link	= &fsi_da7210_dai,
+	.num_links	= 1,
+};
+
+static struct platform_device *fsi_da7210_snd_device;
+
+static int __init fsi_da7210_sound_init(void)
+{
+	int ret;
+
+	fsi_da7210_snd_device = platform_device_alloc("soc-audio", FSI_PORT_B);
+	if (!fsi_da7210_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(fsi_da7210_snd_device, &fsi_soc_card);
+	ret = platform_device_add(fsi_da7210_snd_device);
+	if (ret)
+		platform_device_put(fsi_da7210_snd_device);
+
+	return ret;
+}
+
+static void __exit fsi_da7210_sound_exit(void)
+{
+	platform_device_unregister(fsi_da7210_snd_device);
+}
+
+module_init(fsi_da7210_sound_init);
+module_exit(fsi_da7210_sound_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("ALSA SoC FSI DA2710");
+MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/sound/soc/sh/fsi-hdmi.c b/ap/os/linux/linux-3.4.x/sound/soc/sh/fsi-hdmi.c
new file mode 100644
index 0000000..6e41908
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/sound/soc/sh/fsi-hdmi.c
@@ -0,0 +1,118 @@
+/*
+ * FSI - HDMI sound support
+ *
+ * Copyright (C) 2010 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <sound/sh_fsi.h>
+
+struct fsi_hdmi_data {
+	const char *cpu_dai;
+	const char *card;
+	int id;
+};
+
+static int fsi_hdmi_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dai *cpu = rtd->cpu_dai;
+	int ret;
+
+	ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBM_CFM);
+
+	return ret;
+}
+
+static struct snd_soc_dai_link fsi_dai_link = {
+	.name		= "HDMI",
+	.stream_name	= "HDMI",
+	.codec_dai_name	= "sh_mobile_hdmi-hifi",
+	.platform_name	= "sh_fsi2",
+	.codec_name	= "sh-mobile-hdmi",
+	.init		= fsi_hdmi_dai_init,
+};
+
+static struct snd_soc_card fsi_soc_card  = {
+	.owner		= THIS_MODULE,
+	.dai_link	= &fsi_dai_link,
+	.num_links	= 1,
+};
+
+static struct platform_device *fsi_snd_device;
+
+static int fsi_hdmi_probe(struct platform_device *pdev)
+{
+	int ret = -ENOMEM;
+	const struct platform_device_id	*id_entry;
+	struct fsi_hdmi_data *pdata;
+
+	id_entry = pdev->id_entry;
+	if (!id_entry) {
+		dev_err(&pdev->dev, "unknown fsi hdmi\n");
+		return -ENODEV;
+	}
+
+	pdata = (struct fsi_hdmi_data *)id_entry->driver_data;
+
+	fsi_snd_device = platform_device_alloc("soc-audio", pdata->id);
+	if (!fsi_snd_device)
+		goto out;
+
+	fsi_dai_link.cpu_dai_name	= pdata->cpu_dai;
+	fsi_soc_card.name		= pdata->card;
+
+	platform_set_drvdata(fsi_snd_device, &fsi_soc_card);
+	ret = platform_device_add(fsi_snd_device);
+
+	if (ret)
+		platform_device_put(fsi_snd_device);
+
+out:
+	return ret;
+}
+
+static int fsi_hdmi_remove(struct platform_device *pdev)
+{
+	platform_device_unregister(fsi_snd_device);
+	return 0;
+}
+
+static struct fsi_hdmi_data fsi2_a_hdmi = {
+	.cpu_dai	= "fsia-dai",
+	.card		= "FSI2A-HDMI",
+	.id		= FSI_PORT_A,
+};
+
+static struct fsi_hdmi_data fsi2_b_hdmi = {
+	.cpu_dai	= "fsib-dai",
+	.card		= "FSI2B-HDMI",
+	.id		= FSI_PORT_B,
+};
+
+static struct platform_device_id fsi_id_table[] = {
+	/* FSI 2 */
+	{ "sh_fsi2_a_hdmi",	(kernel_ulong_t)&fsi2_a_hdmi },
+	{ "sh_fsi2_b_hdmi",	(kernel_ulong_t)&fsi2_b_hdmi },
+	{},
+};
+
+static struct platform_driver fsi_hdmi = {
+	.driver = {
+		.name	= "fsi-hdmi-audio",
+	},
+	.probe		= fsi_hdmi_probe,
+	.remove		= fsi_hdmi_remove,
+	.id_table	= fsi_id_table,
+};
+
+module_platform_driver(fsi_hdmi);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic SH4 FSI-HDMI sound card");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/ap/os/linux/linux-3.4.x/sound/soc/sh/fsi.c b/ap/os/linux/linux-3.4.x/sound/soc/sh/fsi.c
new file mode 100644
index 0000000..eb0599f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/sound/soc/sh/fsi.c
@@ -0,0 +1,1771 @@
+/*
+ * Fifo-attached Serial Interface (FSI) support for SH7724
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ssi.c
+ * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <linux/io.h>
+#include <linux/scatterlist.h>
+#include <linux/sh_dma.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <sound/soc.h>
+#include <sound/sh_fsi.h>
+
+/* PortA/PortB register */
+#define REG_DO_FMT	0x0000
+#define REG_DOFF_CTL	0x0004
+#define REG_DOFF_ST	0x0008
+#define REG_DI_FMT	0x000C
+#define REG_DIFF_CTL	0x0010
+#define REG_DIFF_ST	0x0014
+#define REG_CKG1	0x0018
+#define REG_CKG2	0x001C
+#define REG_DIDT	0x0020
+#define REG_DODT	0x0024
+#define REG_MUTE_ST	0x0028
+#define REG_OUT_DMAC	0x002C
+#define REG_OUT_SEL	0x0030
+#define REG_IN_DMAC	0x0038
+
+/* master register */
+#define MST_CLK_RST	0x0210
+#define MST_SOFT_RST	0x0214
+#define MST_FIFO_SZ	0x0218
+
+/* core register (depend on FSI version) */
+#define A_MST_CTLR	0x0180
+#define B_MST_CTLR	0x01A0
+#define CPU_INT_ST	0x01F4
+#define CPU_IEMSK	0x01F8
+#define CPU_IMSK	0x01FC
+#define INT_ST		0x0200
+#define IEMSK		0x0204
+#define IMSK		0x0208
+
+/* DO_FMT */
+/* DI_FMT */
+#define CR_BWS_MASK	(0x3 << 20) /* FSI2 */
+#define CR_BWS_24	(0x0 << 20) /* FSI2 */
+#define CR_BWS_16	(0x1 << 20) /* FSI2 */
+#define CR_BWS_20	(0x2 << 20) /* FSI2 */
+
+#define CR_DTMD_PCM		(0x0 << 8) /* FSI2 */
+#define CR_DTMD_SPDIF_PCM	(0x1 << 8) /* FSI2 */
+#define CR_DTMD_SPDIF_STREAM	(0x2 << 8) /* FSI2 */
+
+#define CR_MONO		(0x0 << 4)
+#define CR_MONO_D	(0x1 << 4)
+#define CR_PCM		(0x2 << 4)
+#define CR_I2S		(0x3 << 4)
+#define CR_TDM		(0x4 << 4)
+#define CR_TDM_D	(0x5 << 4)
+
+/* OUT_DMAC */
+/* IN_DMAC */
+#define VDMD_MASK	(0x3 << 4)
+#define VDMD_FRONT	(0x0 << 4) /* Package in front */
+#define VDMD_BACK	(0x1 << 4) /* Package in back */
+#define VDMD_STREAM	(0x2 << 4) /* Stream mode(16bit * 2) */
+
+#define DMA_ON		(0x1 << 0)
+
+/* DOFF_CTL */
+/* DIFF_CTL */
+#define IRQ_HALF	0x00100000
+#define FIFO_CLR	0x00000001
+
+/* DOFF_ST */
+#define ERR_OVER	0x00000010
+#define ERR_UNDER	0x00000001
+#define ST_ERR		(ERR_OVER | ERR_UNDER)
+
+/* CKG1 */
+#define ACKMD_MASK	0x00007000
+#define BPFMD_MASK	0x00000700
+#define DIMD		(1 << 4)
+#define DOMD		(1 << 0)
+
+/* A/B MST_CTLR */
+#define BP	(1 << 4)	/* Fix the signal of Biphase output */
+#define SE	(1 << 0)	/* Fix the master clock */
+
+/* CLK_RST */
+#define CRB	(1 << 4)
+#define CRA	(1 << 0)
+
+/* IO SHIFT / MACRO */
+#define BI_SHIFT	12
+#define BO_SHIFT	8
+#define AI_SHIFT	4
+#define AO_SHIFT	0
+#define AB_IO(param, shift)	(param << shift)
+
+/* SOFT_RST */
+#define PBSR		(1 << 12) /* Port B Software Reset */
+#define PASR		(1 <<  8) /* Port A Software Reset */
+#define IR		(1 <<  4) /* Interrupt Reset */
+#define FSISR		(1 <<  0) /* Software Reset */
+
+/* OUT_SEL (FSI2) */
+#define DMMD		(1 << 4) /* SPDIF output timing 0: Biphase only */
+				 /*			1: Biphase and serial */
+
+/* FIFO_SZ */
+#define FIFO_SZ_MASK	0x7
+
+#define FSI_RATES SNDRV_PCM_RATE_8000_96000
+
+#define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
+
+typedef int (*set_rate_func)(struct device *dev, int rate, int enable);
+
+/*
+ * FSI driver use below type name for variable
+ *
+ * xxx_num	: number of data
+ * xxx_pos	: position of data
+ * xxx_capa	: capacity of data
+ */
+
+/*
+ *	period/frame/sample image
+ *
+ * ex) PCM (2ch)
+ *
+ * period pos					   period pos
+ *   [n]					     [n + 1]
+ *   |<-------------------- period--------------------->|
+ * ==|============================================ ... =|==
+ *   |							|
+ *   ||<-----  frame ----->|<------ frame ----->|  ...	|
+ *   |+--------------------+--------------------+- ...	|
+ *   ||[ sample ][ sample ]|[ sample ][ sample ]|  ...	|
+ *   |+--------------------+--------------------+- ...	|
+ * ==|============================================ ... =|==
+ */
+
+/*
+ *	FSI FIFO image
+ *
+ *	|	     |
+ *	|	     |
+ *	| [ sample ] |
+ *	| [ sample ] |
+ *	| [ sample ] |
+ *	| [ sample ] |
+ *		--> go to codecs
+ */
+
+/*
+ *		struct
+ */
+
+struct fsi_stream_handler;
+struct fsi_stream {
+
+	/*
+	 * these are initialized by fsi_stream_init()
+	 */
+	struct snd_pcm_substream *substream;
+	int fifo_sample_capa;	/* sample capacity of FSI FIFO */
+	int buff_sample_capa;	/* sample capacity of ALSA buffer */
+	int buff_sample_pos;	/* sample position of ALSA buffer */
+	int period_samples;	/* sample number / 1 period */
+	int period_pos;		/* current period position */
+	int sample_width;	/* sample width */
+	int uerr_num;
+	int oerr_num;
+
+	/*
+	 * thse are initialized by fsi_handler_init()
+	 */
+	struct fsi_stream_handler *handler;
+	struct fsi_priv		*priv;
+
+	/*
+	 * these are for DMAEngine
+	 */
+	struct dma_chan		*chan;
+	struct sh_dmae_slave	slave; /* see fsi_handler_init() */
+	struct work_struct	work;
+	dma_addr_t		dma;
+};
+
+struct fsi_priv {
+	void __iomem *base;
+	struct fsi_master *master;
+	struct sh_fsi_port_info *info;
+
+	struct fsi_stream playback;
+	struct fsi_stream capture;
+
+	u32 do_fmt;
+	u32 di_fmt;
+
+	int chan_num:16;
+	int clk_master:1;
+	int spdif:1;
+
+	long rate;
+};
+
+struct fsi_stream_handler {
+	int (*init)(struct fsi_priv *fsi, struct fsi_stream *io);
+	int (*quit)(struct fsi_priv *fsi, struct fsi_stream *io);
+	int (*probe)(struct fsi_priv *fsi, struct fsi_stream *io);
+	int (*transfer)(struct fsi_priv *fsi, struct fsi_stream *io);
+	int (*remove)(struct fsi_priv *fsi, struct fsi_stream *io);
+	void (*start_stop)(struct fsi_priv *fsi, struct fsi_stream *io,
+			   int enable);
+};
+#define fsi_stream_handler_call(io, func, args...)	\
+	(!(io) ? -ENODEV :				\
+	 !((io)->handler->func) ? 0 :			\
+	 (io)->handler->func(args))
+
+struct fsi_core {
+	int ver;
+
+	u32 int_st;
+	u32 iemsk;
+	u32 imsk;
+	u32 a_mclk;
+	u32 b_mclk;
+};
+
+struct fsi_master {
+	void __iomem *base;
+	int irq;
+	struct fsi_priv fsia;
+	struct fsi_priv fsib;
+	struct fsi_core *core;
+	spinlock_t lock;
+};
+
+static int fsi_stream_is_play(struct fsi_priv *fsi, struct fsi_stream *io);
+
+/*
+ *		basic read write function
+ */
+
+static void __fsi_reg_write(u32 __iomem *reg, u32 data)
+{
+	/* valid data area is 24bit */
+	data &= 0x00ffffff;
+
+	__raw_writel(data, reg);
+}
+
+static u32 __fsi_reg_read(u32 __iomem *reg)
+{
+	return __raw_readl(reg);
+}
+
+static void __fsi_reg_mask_set(u32 __iomem *reg, u32 mask, u32 data)
+{
+	u32 val = __fsi_reg_read(reg);
+
+	val &= ~mask;
+	val |= data & mask;
+
+	__fsi_reg_write(reg, val);
+}
+
+#define fsi_reg_write(p, r, d)\
+	__fsi_reg_write((p->base + REG_##r), d)
+
+#define fsi_reg_read(p, r)\
+	__fsi_reg_read((p->base + REG_##r))
+
+#define fsi_reg_mask_set(p, r, m, d)\
+	__fsi_reg_mask_set((p->base + REG_##r), m, d)
+
+#define fsi_master_read(p, r) _fsi_master_read(p, MST_##r)
+#define fsi_core_read(p, r)   _fsi_master_read(p, p->core->r)
+static u32 _fsi_master_read(struct fsi_master *master, u32 reg)
+{
+	u32 ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&master->lock, flags);
+	ret = __fsi_reg_read(master->base + reg);
+	spin_unlock_irqrestore(&master->lock, flags);
+
+	return ret;
+}
+
+#define fsi_master_mask_set(p, r, m, d) _fsi_master_mask_set(p, MST_##r, m, d)
+#define fsi_core_mask_set(p, r, m, d)  _fsi_master_mask_set(p, p->core->r, m, d)
+static void _fsi_master_mask_set(struct fsi_master *master,
+			       u32 reg, u32 mask, u32 data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&master->lock, flags);
+	__fsi_reg_mask_set(master->base + reg, mask, data);
+	spin_unlock_irqrestore(&master->lock, flags);
+}
+
+/*
+ *		basic function
+ */
+
+static struct fsi_master *fsi_get_master(struct fsi_priv *fsi)
+{
+	return fsi->master;
+}
+
+static int fsi_is_clk_master(struct fsi_priv *fsi)
+{
+	return fsi->clk_master;
+}
+
+static int fsi_is_port_a(struct fsi_priv *fsi)
+{
+	return fsi->master->base == fsi->base;
+}
+
+static int fsi_is_spdif(struct fsi_priv *fsi)
+{
+	return fsi->spdif;
+}
+
+static int fsi_is_play(struct snd_pcm_substream *substream)
+{
+	return substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+}
+
+static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+	return  rtd->cpu_dai;
+}
+
+static struct fsi_priv *fsi_get_priv_frm_dai(struct snd_soc_dai *dai)
+{
+	struct fsi_master *master = snd_soc_dai_get_drvdata(dai);
+
+	if (dai->id == 0)
+		return &master->fsia;
+	else
+		return &master->fsib;
+}
+
+static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream)
+{
+	return fsi_get_priv_frm_dai(fsi_get_dai(substream));
+}
+
+static set_rate_func fsi_get_info_set_rate(struct fsi_priv *fsi)
+{
+	if (!fsi->info)
+		return NULL;
+
+	return fsi->info->set_rate;
+}
+
+static u32 fsi_get_info_flags(struct fsi_priv *fsi)
+{
+	if (!fsi->info)
+		return 0;
+
+	return fsi->info->flags;
+}
+
+static u32 fsi_get_port_shift(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	int is_play = fsi_stream_is_play(fsi, io);
+	int is_porta = fsi_is_port_a(fsi);
+	u32 shift;
+
+	if (is_porta)
+		shift = is_play ? AO_SHIFT : AI_SHIFT;
+	else
+		shift = is_play ? BO_SHIFT : BI_SHIFT;
+
+	return shift;
+}
+
+static int fsi_frame2sample(struct fsi_priv *fsi, int frames)
+{
+	return frames * fsi->chan_num;
+}
+
+static int fsi_sample2frame(struct fsi_priv *fsi, int samples)
+{
+	return samples / fsi->chan_num;
+}
+
+static int fsi_get_current_fifo_samples(struct fsi_priv *fsi,
+					struct fsi_stream *io)
+{
+	int is_play = fsi_stream_is_play(fsi, io);
+	u32 status;
+	int frames;
+
+	status = is_play ?
+		fsi_reg_read(fsi, DOFF_ST) :
+		fsi_reg_read(fsi, DIFF_ST);
+
+	frames = 0x1ff & (status >> 8);
+
+	return fsi_frame2sample(fsi, frames);
+}
+
+static void fsi_count_fifo_err(struct fsi_priv *fsi)
+{
+	u32 ostatus = fsi_reg_read(fsi, DOFF_ST);
+	u32 istatus = fsi_reg_read(fsi, DIFF_ST);
+
+	if (ostatus & ERR_OVER)
+		fsi->playback.oerr_num++;
+
+	if (ostatus & ERR_UNDER)
+		fsi->playback.uerr_num++;
+
+	if (istatus & ERR_OVER)
+		fsi->capture.oerr_num++;
+
+	if (istatus & ERR_UNDER)
+		fsi->capture.uerr_num++;
+
+	fsi_reg_write(fsi, DOFF_ST, 0);
+	fsi_reg_write(fsi, DIFF_ST, 0);
+}
+
+/*
+ *		fsi_stream_xx() function
+ */
+static inline int fsi_stream_is_play(struct fsi_priv *fsi,
+				     struct fsi_stream *io)
+{
+	return &fsi->playback == io;
+}
+
+static inline struct fsi_stream *fsi_stream_get(struct fsi_priv *fsi,
+					struct snd_pcm_substream *substream)
+{
+	return fsi_is_play(substream) ? &fsi->playback : &fsi->capture;
+}
+
+static int fsi_stream_is_working(struct fsi_priv *fsi,
+				 struct fsi_stream *io)
+{
+	struct fsi_master *master = fsi_get_master(fsi);
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&master->lock, flags);
+	ret = !!(io->substream && io->substream->runtime);
+	spin_unlock_irqrestore(&master->lock, flags);
+
+	return ret;
+}
+
+static struct fsi_priv *fsi_stream_to_priv(struct fsi_stream *io)
+{
+	return io->priv;
+}
+
+static void fsi_stream_init(struct fsi_priv *fsi,
+			    struct fsi_stream *io,
+			    struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsi_master *master = fsi_get_master(fsi);
+	unsigned long flags;
+
+	spin_lock_irqsave(&master->lock, flags);
+	io->substream	= substream;
+	io->buff_sample_capa	= fsi_frame2sample(fsi, runtime->buffer_size);
+	io->buff_sample_pos	= 0;
+	io->period_samples	= fsi_frame2sample(fsi, runtime->period_size);
+	io->period_pos		= 0;
+	io->sample_width	= samples_to_bytes(runtime, 1);
+	io->oerr_num	= -1; /* ignore 1st err */
+	io->uerr_num	= -1; /* ignore 1st err */
+	fsi_stream_handler_call(io, init, fsi, io);
+	spin_unlock_irqrestore(&master->lock, flags);
+}
+
+static void fsi_stream_quit(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	struct snd_soc_dai *dai = fsi_get_dai(io->substream);
+	struct fsi_master *master = fsi_get_master(fsi);
+	unsigned long flags;
+
+	spin_lock_irqsave(&master->lock, flags);
+
+	if (io->oerr_num > 0)
+		dev_err(dai->dev, "over_run = %d\n", io->oerr_num);
+
+	if (io->uerr_num > 0)
+		dev_err(dai->dev, "under_run = %d\n", io->uerr_num);
+
+	fsi_stream_handler_call(io, quit, fsi, io);
+	io->substream	= NULL;
+	io->buff_sample_capa	= 0;
+	io->buff_sample_pos	= 0;
+	io->period_samples	= 0;
+	io->period_pos		= 0;
+	io->sample_width	= 0;
+	io->oerr_num	= 0;
+	io->uerr_num	= 0;
+	spin_unlock_irqrestore(&master->lock, flags);
+}
+
+static int fsi_stream_transfer(struct fsi_stream *io)
+{
+	struct fsi_priv *fsi = fsi_stream_to_priv(io);
+	if (!fsi)
+		return -EIO;
+
+	return fsi_stream_handler_call(io, transfer, fsi, io);
+}
+
+#define fsi_stream_start(fsi, io)\
+	fsi_stream_handler_call(io, start_stop, fsi, io, 1)
+
+#define fsi_stream_stop(fsi, io)\
+	fsi_stream_handler_call(io, start_stop, fsi, io, 0)
+
+static int fsi_stream_probe(struct fsi_priv *fsi)
+{
+	struct fsi_stream *io;
+	int ret1, ret2;
+
+	io = &fsi->playback;
+	ret1 = fsi_stream_handler_call(io, probe, fsi, io);
+
+	io = &fsi->capture;
+	ret2 = fsi_stream_handler_call(io, probe, fsi, io);
+
+	if (ret1 < 0)
+		return ret1;
+	if (ret2 < 0)
+		return ret2;
+
+	return 0;
+}
+
+static int fsi_stream_remove(struct fsi_priv *fsi)
+{
+	struct fsi_stream *io;
+	int ret1, ret2;
+
+	io = &fsi->playback;
+	ret1 = fsi_stream_handler_call(io, remove, fsi, io);
+
+	io = &fsi->capture;
+	ret2 = fsi_stream_handler_call(io, remove, fsi, io);
+
+	if (ret1 < 0)
+		return ret1;
+	if (ret2 < 0)
+		return ret2;
+
+	return 0;
+}
+
+/*
+ *		irq function
+ */
+
+static void fsi_irq_enable(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	u32 data = AB_IO(1, fsi_get_port_shift(fsi, io));
+	struct fsi_master *master = fsi_get_master(fsi);
+
+	fsi_core_mask_set(master, imsk,  data, data);
+	fsi_core_mask_set(master, iemsk, data, data);
+}
+
+static void fsi_irq_disable(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	u32 data = AB_IO(1, fsi_get_port_shift(fsi, io));
+	struct fsi_master *master = fsi_get_master(fsi);
+
+	fsi_core_mask_set(master, imsk,  data, 0);
+	fsi_core_mask_set(master, iemsk, data, 0);
+}
+
+static u32 fsi_irq_get_status(struct fsi_master *master)
+{
+	return fsi_core_read(master, int_st);
+}
+
+static void fsi_irq_clear_status(struct fsi_priv *fsi)
+{
+	u32 data = 0;
+	struct fsi_master *master = fsi_get_master(fsi);
+
+	data |= AB_IO(1, fsi_get_port_shift(fsi, &fsi->playback));
+	data |= AB_IO(1, fsi_get_port_shift(fsi, &fsi->capture));
+
+	/* clear interrupt factor */
+	fsi_core_mask_set(master, int_st, data, 0);
+}
+
+/*
+ *		SPDIF master clock function
+ *
+ * These functions are used later FSI2
+ */
+static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable)
+{
+	struct fsi_master *master = fsi_get_master(fsi);
+	u32 mask, val;
+
+	if (master->core->ver < 2) {
+		pr_err("fsi: register access err (%s)\n", __func__);
+		return;
+	}
+
+	mask = BP | SE;
+	val = enable ? mask : 0;
+
+	fsi_is_port_a(fsi) ?
+		fsi_core_mask_set(master, a_mclk, mask, val) :
+		fsi_core_mask_set(master, b_mclk, mask, val);
+}
+
+/*
+ *		clock function
+ */
+static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi,
+			      long rate, int enable)
+{
+	struct fsi_master *master = fsi_get_master(fsi);
+	set_rate_func set_rate = fsi_get_info_set_rate(fsi);
+	int fsi_ver = master->core->ver;
+	int ret;
+
+	if (!set_rate)
+		return 0;
+
+	ret = set_rate(dev, rate, enable);
+	if (ret < 0) /* error */
+		return ret;
+
+	if (!enable)
+		return 0;
+
+	if (ret > 0) {
+		u32 data = 0;
+
+		switch (ret & SH_FSI_ACKMD_MASK) {
+		default:
+			/* FALL THROUGH */
+		case SH_FSI_ACKMD_512:
+			data |= (0x0 << 12);
+			break;
+		case SH_FSI_ACKMD_256:
+			data |= (0x1 << 12);
+			break;
+		case SH_FSI_ACKMD_128:
+			data |= (0x2 << 12);
+			break;
+		case SH_FSI_ACKMD_64:
+			data |= (0x3 << 12);
+			break;
+		case SH_FSI_ACKMD_32:
+			if (fsi_ver < 2)
+				dev_err(dev, "unsupported ACKMD\n");
+			else
+				data |= (0x4 << 12);
+			break;
+		}
+
+		switch (ret & SH_FSI_BPFMD_MASK) {
+		default:
+			/* FALL THROUGH */
+		case SH_FSI_BPFMD_32:
+			data |= (0x0 << 8);
+			break;
+		case SH_FSI_BPFMD_64:
+			data |= (0x1 << 8);
+			break;
+		case SH_FSI_BPFMD_128:
+			data |= (0x2 << 8);
+			break;
+		case SH_FSI_BPFMD_256:
+			data |= (0x3 << 8);
+			break;
+		case SH_FSI_BPFMD_512:
+			data |= (0x4 << 8);
+			break;
+		case SH_FSI_BPFMD_16:
+			if (fsi_ver < 2)
+				dev_err(dev, "unsupported ACKMD\n");
+			else
+				data |= (0x7 << 8);
+			break;
+		}
+
+		fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
+		udelay(10);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+/*
+ *		pio data transfer handler
+ */
+static void fsi_pio_push16(struct fsi_priv *fsi, u8 *_buf, int samples)
+{
+	u16 *buf = (u16 *)_buf;
+	int i;
+
+	for (i = 0; i < samples; i++)
+		fsi_reg_write(fsi, DODT, ((u32)*(buf + i) << 8));
+}
+
+static void fsi_pio_pop16(struct fsi_priv *fsi, u8 *_buf, int samples)
+{
+	u16 *buf = (u16 *)_buf;
+	int i;
+
+	for (i = 0; i < samples; i++)
+		*(buf + i) = (u16)(fsi_reg_read(fsi, DIDT) >> 8);
+}
+
+static void fsi_pio_push32(struct fsi_priv *fsi, u8 *_buf, int samples)
+{
+	u32 *buf = (u32 *)_buf;
+	int i;
+
+	for (i = 0; i < samples; i++)
+		fsi_reg_write(fsi, DODT, *(buf + i));
+}
+
+static void fsi_pio_pop32(struct fsi_priv *fsi, u8 *_buf, int samples)
+{
+	u32 *buf = (u32 *)_buf;
+	int i;
+
+	for (i = 0; i < samples; i++)
+		*(buf + i) = fsi_reg_read(fsi, DIDT);
+}
+
+static u8 *fsi_pio_get_area(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	struct snd_pcm_runtime *runtime = io->substream->runtime;
+
+	return runtime->dma_area +
+		samples_to_bytes(runtime, io->buff_sample_pos);
+}
+
+static int fsi_pio_transfer(struct fsi_priv *fsi, struct fsi_stream *io,
+		void (*run16)(struct fsi_priv *fsi, u8 *buf, int samples),
+		void (*run32)(struct fsi_priv *fsi, u8 *buf, int samples),
+		int samples)
+{
+	struct snd_pcm_runtime *runtime;
+	struct snd_pcm_substream *substream;
+	u8 *buf;
+	int over_period;
+
+	if (!fsi_stream_is_working(fsi, io))
+		return -EINVAL;
+
+	over_period	= 0;
+	substream	= io->substream;
+	runtime		= substream->runtime;
+
+	/* FSI FIFO has limit.
+	 * So, this driver can not send periods data at a time
+	 */
+	if (io->buff_sample_pos >=
+	    io->period_samples * (io->period_pos + 1)) {
+
+		over_period = 1;
+		io->period_pos = (io->period_pos + 1) % runtime->periods;
+
+		if (0 == io->period_pos)
+			io->buff_sample_pos = 0;
+	}
+
+	buf = fsi_pio_get_area(fsi, io);
+
+	switch (io->sample_width) {
+	case 2:
+		run16(fsi, buf, samples);
+		break;
+	case 4:
+		run32(fsi, buf, samples);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* update buff_sample_pos */
+	io->buff_sample_pos += samples;
+
+	if (over_period)
+		snd_pcm_period_elapsed(substream);
+
+	return 0;
+}
+
+static int fsi_pio_pop(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	int sample_residues;	/* samples in FSI fifo */
+	int sample_space;	/* ALSA free samples space */
+	int samples;
+
+	sample_residues	= fsi_get_current_fifo_samples(fsi, io);
+	sample_space	= io->buff_sample_capa - io->buff_sample_pos;
+
+	samples = min(sample_residues, sample_space);
+
+	return fsi_pio_transfer(fsi, io,
+				  fsi_pio_pop16,
+				  fsi_pio_pop32,
+				  samples);
+}
+
+static int fsi_pio_push(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	int sample_residues;	/* ALSA residue samples */
+	int sample_space;	/* FSI fifo free samples space */
+	int samples;
+
+	sample_residues	= io->buff_sample_capa - io->buff_sample_pos;
+	sample_space	= io->fifo_sample_capa -
+		fsi_get_current_fifo_samples(fsi, io);
+
+	samples = min(sample_residues, sample_space);
+
+	return fsi_pio_transfer(fsi, io,
+				  fsi_pio_push16,
+				  fsi_pio_push32,
+				  samples);
+}
+
+static void fsi_pio_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
+			       int enable)
+{
+	struct fsi_master *master = fsi_get_master(fsi);
+	u32 clk  = fsi_is_port_a(fsi) ? CRA  : CRB;
+
+	if (enable)
+		fsi_irq_enable(fsi, io);
+	else
+		fsi_irq_disable(fsi, io);
+
+	if (fsi_is_clk_master(fsi))
+		fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
+}
+
+static struct fsi_stream_handler fsi_pio_push_handler = {
+	.transfer	= fsi_pio_push,
+	.start_stop	= fsi_pio_start_stop,
+};
+
+static struct fsi_stream_handler fsi_pio_pop_handler = {
+	.transfer	= fsi_pio_pop,
+	.start_stop	= fsi_pio_start_stop,
+};
+
+static irqreturn_t fsi_interrupt(int irq, void *data)
+{
+	struct fsi_master *master = data;
+	u32 int_st = fsi_irq_get_status(master);
+
+	/* clear irq status */
+	fsi_master_mask_set(master, SOFT_RST, IR, 0);
+	fsi_master_mask_set(master, SOFT_RST, IR, IR);
+
+	if (int_st & AB_IO(1, AO_SHIFT))
+		fsi_stream_transfer(&master->fsia.playback);
+	if (int_st & AB_IO(1, BO_SHIFT))
+		fsi_stream_transfer(&master->fsib.playback);
+	if (int_st & AB_IO(1, AI_SHIFT))
+		fsi_stream_transfer(&master->fsia.capture);
+	if (int_st & AB_IO(1, BI_SHIFT))
+		fsi_stream_transfer(&master->fsib.capture);
+
+	fsi_count_fifo_err(&master->fsia);
+	fsi_count_fifo_err(&master->fsib);
+
+	fsi_irq_clear_status(&master->fsia);
+	fsi_irq_clear_status(&master->fsib);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ *		dma data transfer handler
+ */
+static int fsi_dma_init(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	struct snd_pcm_runtime *runtime = io->substream->runtime;
+	struct snd_soc_dai *dai = fsi_get_dai(io->substream);
+	enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ?
+				DMA_TO_DEVICE : DMA_FROM_DEVICE;
+
+	io->dma = dma_map_single(dai->dev, runtime->dma_area,
+				 snd_pcm_lib_buffer_bytes(io->substream), dir);
+	return 0;
+}
+
+static int fsi_dma_quit(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	struct snd_soc_dai *dai = fsi_get_dai(io->substream);
+	enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ?
+		DMA_TO_DEVICE : DMA_FROM_DEVICE;
+
+	dma_unmap_single(dai->dev, io->dma,
+			 snd_pcm_lib_buffer_bytes(io->substream), dir);
+	return 0;
+}
+
+static void fsi_dma_complete(void *data)
+{
+	struct fsi_stream *io = (struct fsi_stream *)data;
+	struct fsi_priv *fsi = fsi_stream_to_priv(io);
+	struct snd_pcm_runtime *runtime = io->substream->runtime;
+	struct snd_soc_dai *dai = fsi_get_dai(io->substream);
+	enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ?
+		DMA_TO_DEVICE : DMA_FROM_DEVICE;
+
+	dma_sync_single_for_cpu(dai->dev, io->dma,
+			samples_to_bytes(runtime, io->period_samples), dir);
+
+	io->buff_sample_pos += io->period_samples;
+	io->period_pos++;
+
+	if (io->period_pos >= runtime->periods) {
+		io->period_pos = 0;
+		io->buff_sample_pos = 0;
+	}
+
+	fsi_count_fifo_err(fsi);
+	fsi_stream_transfer(io);
+
+	snd_pcm_period_elapsed(io->substream);
+}
+
+static dma_addr_t fsi_dma_get_area(struct fsi_stream *io)
+{
+	struct snd_pcm_runtime *runtime = io->substream->runtime;
+
+	return io->dma + samples_to_bytes(runtime, io->buff_sample_pos);
+}
+
+static void fsi_dma_do_work(struct work_struct *work)
+{
+	struct fsi_stream *io = container_of(work, struct fsi_stream, work);
+	struct fsi_priv *fsi = fsi_stream_to_priv(io);
+	struct dma_chan *chan;
+	struct snd_soc_dai *dai;
+	struct dma_async_tx_descriptor *desc;
+	struct scatterlist sg;
+	struct snd_pcm_runtime *runtime;
+	enum dma_data_direction dir;
+	dma_cookie_t cookie;
+	int is_play = fsi_stream_is_play(fsi, io);
+	int len;
+	dma_addr_t buf;
+
+	if (!fsi_stream_is_working(fsi, io))
+		return;
+
+	dai	= fsi_get_dai(io->substream);
+	chan	= io->chan;
+	runtime	= io->substream->runtime;
+	dir	= is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+	len	= samples_to_bytes(runtime, io->period_samples);
+	buf	= fsi_dma_get_area(io);
+
+	dma_sync_single_for_device(dai->dev, io->dma, len, dir);
+
+	sg_init_table(&sg, 1);
+	sg_set_page(&sg, pfn_to_page(PFN_DOWN(buf)),
+		    len , offset_in_page(buf));
+	sg_dma_address(&sg) = buf;
+	sg_dma_len(&sg) = len;
+
+	desc = dmaengine_prep_slave_sg(chan, &sg, 1, dir,
+				       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(dai->dev, "dmaengine_prep_slave_sg() fail\n");
+		return;
+	}
+
+	desc->callback		= fsi_dma_complete;
+	desc->callback_param	= io;
+
+	cookie = desc->tx_submit(desc);
+	if (cookie < 0) {
+		dev_err(dai->dev, "tx_submit() fail\n");
+		return;
+	}
+
+	dma_async_issue_pending(chan);
+
+	/*
+	 * FIXME
+	 *
+	 * In DMAEngine case, codec and FSI cannot be started simultaneously
+	 * since FSI is using the scheduler work queue.
+	 * Therefore, in capture case, probably FSI FIFO will have got
+	 * overflow error in this point.
+	 * in that case, DMA cannot start transfer until error was cleared.
+	 */
+	if (!is_play) {
+		if (ERR_OVER & fsi_reg_read(fsi, DIFF_ST)) {
+			fsi_reg_mask_set(fsi, DIFF_CTL, FIFO_CLR, FIFO_CLR);
+			fsi_reg_write(fsi, DIFF_ST, 0);
+		}
+	}
+}
+
+static bool fsi_dma_filter(struct dma_chan *chan, void *param)
+{
+	struct sh_dmae_slave *slave = param;
+
+	chan->private = slave;
+
+	return true;
+}
+
+static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	schedule_work(&io->work);
+
+	return 0;
+}
+
+static void fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
+				 int start)
+{
+	u32 bws;
+	u32 dma;
+
+	switch (io->sample_width * start) {
+	case 2:
+		bws = CR_BWS_16;
+		dma = VDMD_STREAM | DMA_ON;
+		break;
+	case 4:
+		bws = CR_BWS_24;
+		dma = VDMD_BACK | DMA_ON;
+		break;
+	default:
+		bws = 0;
+		dma = 0;
+	}
+
+	fsi_reg_mask_set(fsi, DO_FMT, CR_BWS_MASK, bws);
+	fsi_reg_write(fsi, OUT_DMAC, dma);
+}
+
+static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	dma_cap_mask_t mask;
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	io->chan = dma_request_channel(mask, fsi_dma_filter, &io->slave);
+	if (!io->chan)
+		return -EIO;
+
+	INIT_WORK(&io->work, fsi_dma_do_work);
+
+	return 0;
+}
+
+static int fsi_dma_remove(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	cancel_work_sync(&io->work);
+
+	fsi_stream_stop(fsi, io);
+
+	if (io->chan)
+		dma_release_channel(io->chan);
+
+	io->chan = NULL;
+	return 0;
+}
+
+static struct fsi_stream_handler fsi_dma_push_handler = {
+	.init		= fsi_dma_init,
+	.quit		= fsi_dma_quit,
+	.probe		= fsi_dma_probe,
+	.transfer	= fsi_dma_transfer,
+	.remove		= fsi_dma_remove,
+	.start_stop	= fsi_dma_push_start_stop,
+};
+
+/*
+ *		dai ops
+ */
+static void fsi_fifo_init(struct fsi_priv *fsi,
+			  struct fsi_stream *io,
+			  struct device *dev)
+{
+	struct fsi_master *master = fsi_get_master(fsi);
+	int is_play = fsi_stream_is_play(fsi, io);
+	u32 shift, i;
+	int frame_capa;
+
+	/* get on-chip RAM capacity */
+	shift = fsi_master_read(master, FIFO_SZ);
+	shift >>= fsi_get_port_shift(fsi, io);
+	shift &= FIFO_SZ_MASK;
+	frame_capa = 256 << shift;
+	dev_dbg(dev, "fifo = %d words\n", frame_capa);
+
+	/*
+	 * The maximum number of sample data varies depending
+	 * on the number of channels selected for the format.
+	 *
+	 * FIFOs are used in 4-channel units in 3-channel mode
+	 * and in 8-channel units in 5- to 7-channel mode
+	 * meaning that more FIFOs than the required size of DPRAM
+	 * are used.
+	 *
+	 * ex) if 256 words of DP-RAM is connected
+	 * 1 channel:  256 (256 x 1 = 256)
+	 * 2 channels: 128 (128 x 2 = 256)
+	 * 3 channels:  64 ( 64 x 3 = 192)
+	 * 4 channels:  64 ( 64 x 4 = 256)
+	 * 5 channels:  32 ( 32 x 5 = 160)
+	 * 6 channels:  32 ( 32 x 6 = 192)
+	 * 7 channels:  32 ( 32 x 7 = 224)
+	 * 8 channels:  32 ( 32 x 8 = 256)
+	 */
+	for (i = 1; i < fsi->chan_num; i <<= 1)
+		frame_capa >>= 1;
+	dev_dbg(dev, "%d channel %d store\n",
+		fsi->chan_num, frame_capa);
+
+	io->fifo_sample_capa = fsi_frame2sample(fsi, frame_capa);
+
+	/*
+	 * set interrupt generation factor
+	 * clear FIFO
+	 */
+	if (is_play) {
+		fsi_reg_write(fsi,	DOFF_CTL, IRQ_HALF);
+		fsi_reg_mask_set(fsi,	DOFF_CTL, FIFO_CLR, FIFO_CLR);
+	} else {
+		fsi_reg_write(fsi,	DIFF_CTL, IRQ_HALF);
+		fsi_reg_mask_set(fsi,	DIFF_CTL, FIFO_CLR, FIFO_CLR);
+	}
+}
+
+static int fsi_hw_startup(struct fsi_priv *fsi,
+			  struct fsi_stream *io,
+			  struct device *dev)
+{
+	struct fsi_master *master = fsi_get_master(fsi);
+	int fsi_ver = master->core->ver;
+	u32 flags = fsi_get_info_flags(fsi);
+	u32 data = 0;
+
+	/* clock setting */
+	if (fsi_is_clk_master(fsi))
+		data = DIMD | DOMD;
+
+	fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data);
+
+	/* clock inversion (CKG2) */
+	data = 0;
+	if (SH_FSI_LRM_INV & flags)
+		data |= 1 << 12;
+	if (SH_FSI_BRM_INV & flags)
+		data |= 1 << 8;
+	if (SH_FSI_LRS_INV & flags)
+		data |= 1 << 4;
+	if (SH_FSI_BRS_INV & flags)
+		data |= 1 << 0;
+
+	fsi_reg_write(fsi, CKG2, data);
+
+	/* set format */
+	fsi_reg_write(fsi, DO_FMT, fsi->do_fmt);
+	fsi_reg_write(fsi, DI_FMT, fsi->di_fmt);
+
+	/* spdif ? */
+	if (fsi_is_spdif(fsi)) {
+		fsi_spdif_clk_ctrl(fsi, 1);
+		fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD);
+	}
+
+	/*
+	 * FIXME
+	 *
+	 * FSI driver assumed that data package is in-back.
+	 * FSI2 chip can select it.
+	 */
+	if (fsi_ver >= 2) {
+		fsi_reg_write(fsi, OUT_DMAC,	(1 << 4));
+		fsi_reg_write(fsi, IN_DMAC,	(1 << 4));
+	}
+
+	/* irq clear */
+	fsi_irq_disable(fsi, io);
+	fsi_irq_clear_status(fsi);
+
+	/* fifo init */
+	fsi_fifo_init(fsi, io, dev);
+
+	return 0;
+}
+
+static void fsi_hw_shutdown(struct fsi_priv *fsi,
+			    struct device *dev)
+{
+	if (fsi_is_clk_master(fsi))
+		fsi_set_master_clk(dev, fsi, fsi->rate, 0);
+}
+
+static int fsi_dai_startup(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
+{
+	struct fsi_priv *fsi = fsi_get_priv(substream);
+
+	return fsi_hw_startup(fsi, fsi_stream_get(fsi, substream), dai->dev);
+}
+
+static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
+{
+	struct fsi_priv *fsi = fsi_get_priv(substream);
+
+	fsi_hw_shutdown(fsi, dai->dev);
+	fsi->rate = 0;
+}
+
+static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+			   struct snd_soc_dai *dai)
+{
+	struct fsi_priv *fsi = fsi_get_priv(substream);
+	struct fsi_stream *io = fsi_stream_get(fsi, substream);
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		fsi_stream_init(fsi, io, substream);
+		ret = fsi_stream_transfer(io);
+		if (0 == ret)
+			fsi_stream_start(fsi, io);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		fsi_stream_stop(fsi, io);
+		fsi_stream_quit(fsi, io);
+		break;
+	}
+
+	return ret;
+}
+
+static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt)
+{
+	u32 data = 0;
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		data = CR_I2S;
+		fsi->chan_num = 2;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		data = CR_PCM;
+		fsi->chan_num = 2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	fsi->do_fmt = data;
+	fsi->di_fmt = data;
+
+	return 0;
+}
+
+static int fsi_set_fmt_spdif(struct fsi_priv *fsi)
+{
+	struct fsi_master *master = fsi_get_master(fsi);
+	u32 data = 0;
+
+	if (master->core->ver < 2)
+		return -EINVAL;
+
+	data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM;
+	fsi->chan_num = 2;
+	fsi->spdif = 1;
+
+	fsi->do_fmt = data;
+	fsi->di_fmt = data;
+
+	return 0;
+}
+
+static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai);
+	set_rate_func set_rate = fsi_get_info_set_rate(fsi);
+	u32 flags = fsi_get_info_flags(fsi);
+	int ret;
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		fsi->clk_master = 1;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (fsi_is_clk_master(fsi) && !set_rate) {
+		dev_err(dai->dev, "platform doesn't have set_rate\n");
+		return -EINVAL;
+	}
+
+	/* set format */
+	switch (flags & SH_FSI_FMT_MASK) {
+	case SH_FSI_FMT_DAI:
+		ret = fsi_set_fmt_dai(fsi, fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+		break;
+	case SH_FSI_FMT_SPDIF:
+		ret = fsi_set_fmt_spdif(fsi);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params,
+			     struct snd_soc_dai *dai)
+{
+	struct fsi_priv *fsi = fsi_get_priv(substream);
+	long rate = params_rate(params);
+	int ret;
+
+	if (!fsi_is_clk_master(fsi))
+		return 0;
+
+	ret = fsi_set_master_clk(dai->dev, fsi, rate, 1);
+	if (ret < 0)
+		return ret;
+
+	fsi->rate = rate;
+
+	return ret;
+}
+
+static const struct snd_soc_dai_ops fsi_dai_ops = {
+	.startup	= fsi_dai_startup,
+	.shutdown	= fsi_dai_shutdown,
+	.trigger	= fsi_dai_trigger,
+	.set_fmt	= fsi_dai_set_fmt,
+	.hw_params	= fsi_dai_hw_params,
+};
+
+/*
+ *		pcm ops
+ */
+
+static struct snd_pcm_hardware fsi_pcm_hardware = {
+	.info =		SNDRV_PCM_INFO_INTERLEAVED	|
+			SNDRV_PCM_INFO_MMAP		|
+			SNDRV_PCM_INFO_MMAP_VALID,
+	.formats		= FSI_FMTS,
+	.rates			= FSI_RATES,
+	.rate_min		= 8000,
+	.rate_max		= 192000,
+	.channels_min		= 1,
+	.channels_max		= 2,
+	.buffer_bytes_max	= 64 * 1024,
+	.period_bytes_min	= 32,
+	.period_bytes_max	= 8192,
+	.periods_min		= 1,
+	.periods_max		= 32,
+	.fifo_size		= 256,
+};
+
+static int fsi_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int ret = 0;
+
+	snd_soc_set_runtime_hwparams(substream, &fsi_pcm_hardware);
+
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+
+	return ret;
+}
+
+static int fsi_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+}
+
+static int fsi_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream)
+{
+	struct fsi_priv *fsi = fsi_get_priv(substream);
+	struct fsi_stream *io = fsi_stream_get(fsi, substream);
+
+	return fsi_sample2frame(fsi, io->buff_sample_pos);
+}
+
+static struct snd_pcm_ops fsi_pcm_ops = {
+	.open		= fsi_pcm_open,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= fsi_hw_params,
+	.hw_free	= fsi_hw_free,
+	.pointer	= fsi_pointer,
+};
+
+/*
+ *		snd_soc_platform
+ */
+
+#define PREALLOC_BUFFER		(32 * 1024)
+#define PREALLOC_BUFFER_MAX	(32 * 1024)
+
+static void fsi_pcm_free(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int fsi_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+
+	/*
+	 * dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
+	 * in MMAP mode (i.e. aplay -M)
+	 */
+	return snd_pcm_lib_preallocate_pages_for_all(
+		pcm,
+		SNDRV_DMA_TYPE_CONTINUOUS,
+		snd_dma_continuous_data(GFP_KERNEL),
+		PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
+}
+
+/*
+ *		alsa struct
+ */
+
+static struct snd_soc_dai_driver fsi_soc_dai[] = {
+	{
+		.name			= "fsia-dai",
+		.playback = {
+			.rates		= FSI_RATES,
+			.formats	= FSI_FMTS,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.capture = {
+			.rates		= FSI_RATES,
+			.formats	= FSI_FMTS,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.ops = &fsi_dai_ops,
+	},
+	{
+		.name			= "fsib-dai",
+		.playback = {
+			.rates		= FSI_RATES,
+			.formats	= FSI_FMTS,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.capture = {
+			.rates		= FSI_RATES,
+			.formats	= FSI_FMTS,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.ops = &fsi_dai_ops,
+	},
+};
+
+static struct snd_soc_platform_driver fsi_soc_platform = {
+	.ops		= &fsi_pcm_ops,
+	.pcm_new	= fsi_pcm_new,
+	.pcm_free	= fsi_pcm_free,
+};
+
+/*
+ *		platform function
+ */
+static void fsi_handler_init(struct fsi_priv *fsi)
+{
+	fsi->playback.handler	= &fsi_pio_push_handler; /* default PIO */
+	fsi->playback.priv	= fsi;
+	fsi->capture.handler	= &fsi_pio_pop_handler;  /* default PIO */
+	fsi->capture.priv	= fsi;
+
+	if (fsi->info->tx_id) {
+		fsi->playback.slave.slave_id	= fsi->info->tx_id;
+		fsi->playback.handler		= &fsi_dma_push_handler;
+	}
+}
+
+static int fsi_probe(struct platform_device *pdev)
+{
+	struct fsi_master *master;
+	const struct platform_device_id	*id_entry;
+	struct sh_fsi_platform_info *info = pdev->dev.platform_data;
+	struct resource *res;
+	unsigned int irq;
+	int ret;
+
+	id_entry = pdev->id_entry;
+	if (!id_entry) {
+		dev_err(&pdev->dev, "unknown fsi device\n");
+		return -ENODEV;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irq = platform_get_irq(pdev, 0);
+	if (!res || (int)irq <= 0) {
+		dev_err(&pdev->dev, "Not enough FSI platform resources.\n");
+		ret = -ENODEV;
+		goto exit;
+	}
+
+	master = kzalloc(sizeof(*master), GFP_KERNEL);
+	if (!master) {
+		dev_err(&pdev->dev, "Could not allocate master\n");
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	master->base = ioremap_nocache(res->start, resource_size(res));
+	if (!master->base) {
+		ret = -ENXIO;
+		dev_err(&pdev->dev, "Unable to ioremap FSI registers.\n");
+		goto exit_kfree;
+	}
+
+	/* master setting */
+	master->irq		= irq;
+	master->core		= (struct fsi_core *)id_entry->driver_data;
+	spin_lock_init(&master->lock);
+
+	/* FSI A setting */
+	master->fsia.base	= master->base;
+	master->fsia.master	= master;
+	master->fsia.info	= &info->port_a;
+	fsi_handler_init(&master->fsia);
+	ret = fsi_stream_probe(&master->fsia);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "FSIA stream probe failed\n");
+		goto exit_iounmap;
+	}
+
+	/* FSI B setting */
+	master->fsib.base	= master->base + 0x40;
+	master->fsib.master	= master;
+	master->fsib.info	= &info->port_b;
+	fsi_handler_init(&master->fsib);
+	ret = fsi_stream_probe(&master->fsib);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "FSIB stream probe failed\n");
+		goto exit_fsia;
+	}
+
+	pm_runtime_enable(&pdev->dev);
+	dev_set_drvdata(&pdev->dev, master);
+
+	ret = request_irq(irq, &fsi_interrupt, 0,
+			  id_entry->name, master);
+	if (ret) {
+		dev_err(&pdev->dev, "irq request err\n");
+		goto exit_fsib;
+	}
+
+	ret = snd_soc_register_platform(&pdev->dev, &fsi_soc_platform);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "cannot snd soc register\n");
+		goto exit_free_irq;
+	}
+
+	ret = snd_soc_register_dais(&pdev->dev, fsi_soc_dai,
+				    ARRAY_SIZE(fsi_soc_dai));
+	if (ret < 0) {
+		dev_err(&pdev->dev, "cannot snd dai register\n");
+		goto exit_snd_soc;
+	}
+
+	return ret;
+
+exit_snd_soc:
+	snd_soc_unregister_platform(&pdev->dev);
+exit_free_irq:
+	free_irq(irq, master);
+exit_fsib:
+	fsi_stream_remove(&master->fsib);
+exit_fsia:
+	fsi_stream_remove(&master->fsia);
+exit_iounmap:
+	iounmap(master->base);
+	pm_runtime_disable(&pdev->dev);
+exit_kfree:
+	kfree(master);
+	master = NULL;
+exit:
+	return ret;
+}
+
+static int fsi_remove(struct platform_device *pdev)
+{
+	struct fsi_master *master;
+
+	master = dev_get_drvdata(&pdev->dev);
+
+	free_irq(master->irq, master);
+	pm_runtime_disable(&pdev->dev);
+
+	snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(fsi_soc_dai));
+	snd_soc_unregister_platform(&pdev->dev);
+
+	fsi_stream_remove(&master->fsia);
+	fsi_stream_remove(&master->fsib);
+
+	iounmap(master->base);
+	kfree(master);
+
+	return 0;
+}
+
+static void __fsi_suspend(struct fsi_priv *fsi,
+			  struct fsi_stream *io,
+			  struct device *dev)
+{
+	if (!fsi_stream_is_working(fsi, io))
+		return;
+
+	fsi_stream_stop(fsi, io);
+	fsi_hw_shutdown(fsi, dev);
+}
+
+static void __fsi_resume(struct fsi_priv *fsi,
+			 struct fsi_stream *io,
+			 struct device *dev)
+{
+	if (!fsi_stream_is_working(fsi, io))
+		return;
+
+	fsi_hw_startup(fsi, io, dev);
+
+	if (fsi_is_clk_master(fsi) && fsi->rate)
+		fsi_set_master_clk(dev, fsi, fsi->rate, 1);
+
+	fsi_stream_start(fsi, io);
+}
+
+static int fsi_suspend(struct device *dev)
+{
+	struct fsi_master *master = dev_get_drvdata(dev);
+	struct fsi_priv *fsia = &master->fsia;
+	struct fsi_priv *fsib = &master->fsib;
+
+	__fsi_suspend(fsia, &fsia->playback, dev);
+	__fsi_suspend(fsia, &fsia->capture, dev);
+
+	__fsi_suspend(fsib, &fsib->playback, dev);
+	__fsi_suspend(fsib, &fsib->capture, dev);
+
+	return 0;
+}
+
+static int fsi_resume(struct device *dev)
+{
+	struct fsi_master *master = dev_get_drvdata(dev);
+	struct fsi_priv *fsia = &master->fsia;
+	struct fsi_priv *fsib = &master->fsib;
+
+	__fsi_resume(fsia, &fsia->playback, dev);
+	__fsi_resume(fsia, &fsia->capture, dev);
+
+	__fsi_resume(fsib, &fsib->playback, dev);
+	__fsi_resume(fsib, &fsib->capture, dev);
+
+	return 0;
+}
+
+static struct dev_pm_ops fsi_pm_ops = {
+	.suspend		= fsi_suspend,
+	.resume			= fsi_resume,
+};
+
+static struct fsi_core fsi1_core = {
+	.ver	= 1,
+
+	/* Interrupt */
+	.int_st	= INT_ST,
+	.iemsk	= IEMSK,
+	.imsk	= IMSK,
+};
+
+static struct fsi_core fsi2_core = {
+	.ver	= 2,
+
+	/* Interrupt */
+	.int_st	= CPU_INT_ST,
+	.iemsk	= CPU_IEMSK,
+	.imsk	= CPU_IMSK,
+	.a_mclk	= A_MST_CTLR,
+	.b_mclk	= B_MST_CTLR,
+};
+
+static struct platform_device_id fsi_id_table[] = {
+	{ "sh_fsi",	(kernel_ulong_t)&fsi1_core },
+	{ "sh_fsi2",	(kernel_ulong_t)&fsi2_core },
+	{},
+};
+MODULE_DEVICE_TABLE(platform, fsi_id_table);
+
+static struct platform_driver fsi_driver = {
+	.driver 	= {
+		.name	= "fsi-pcm-audio",
+		.pm	= &fsi_pm_ops,
+	},
+	.probe		= fsi_probe,
+	.remove		= fsi_remove,
+	.id_table	= fsi_id_table,
+};
+
+module_platform_driver(fsi_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SuperH onchip FSI audio driver");
+MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
+MODULE_ALIAS("platform:fsi-pcm-audio");
diff --git a/ap/os/linux/linux-3.4.x/sound/soc/sh/hac.c b/ap/os/linux/linux-3.4.x/sound/soc/sh/hac.c
new file mode 100644
index 0000000..3474d7b
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/sound/soc/sh/hac.c
@@ -0,0 +1,339 @@
+/*
+ * Hitachi Audio Controller (AC97) support for SH7760/SH7780
+ *
+ * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
+ *  licensed under the terms outlined in the file COPYING at the root
+ *  of the linux kernel sources.
+ *
+ * dont forget to set IPSEL/OMSEL register bits (in your board code) to
+ * enable HAC output pins!
+ */
+
+/* BIG FAT FIXME: although the SH7760 has 2 independent AC97 units, only
+ * the FIRST can be used since ASoC does not pass any information to the
+ * ac97_read/write() functions regarding WHICH unit to use.  You'll have
+ * to edit the code a bit to use the other AC97 unit.		--mlau
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+/* regs and bits */
+#define HACCR		0x08
+#define HACCSAR		0x20
+#define HACCSDR		0x24
+#define HACPCML		0x28
+#define HACPCMR		0x2C
+#define HACTIER		0x50
+#define	HACTSR		0x54
+#define HACRIER		0x58
+#define HACRSR		0x5C
+#define HACACR		0x60
+
+#define CR_CR		(1 << 15)	/* "codec-ready" indicator */
+#define CR_CDRT		(1 << 11)	/* cold reset */
+#define CR_WMRT		(1 << 10)	/* warm reset */
+#define CR_B9		(1 << 9)	/* the mysterious "bit 9" */
+#define CR_ST		(1 << 5)	/* AC97 link start bit */
+
+#define CSAR_RD		(1 << 19)	/* AC97 data read bit */
+#define CSAR_WR		(0)
+
+#define TSR_CMDAMT	(1 << 31)
+#define TSR_CMDDMT	(1 << 30)
+
+#define RSR_STARY	(1 << 22)
+#define RSR_STDRY	(1 << 21)
+
+#define ACR_DMARX16	(1 << 30)
+#define ACR_DMATX16	(1 << 29)
+#define ACR_TX12ATOM	(1 << 26)
+#define ACR_DMARX20	((1 << 24) | (1 << 22))
+#define ACR_DMATX20	((1 << 23) | (1 << 21))
+
+#define CSDR_SHIFT	4
+#define CSDR_MASK	(0xffff << CSDR_SHIFT)
+#define CSAR_SHIFT	12
+#define CSAR_MASK	(0x7f << CSAR_SHIFT)
+
+#define AC97_WRITE_RETRY	1
+#define AC97_READ_RETRY		5
+
+/* manual-suggested AC97 codec access timeouts (us) */
+#define TMO_E1	500	/* 21 < E1 < 1000 */
+#define TMO_E2	13	/* 13 < E2 */
+#define TMO_E3	21	/* 21 < E3 */
+#define TMO_E4	500	/* 21 < E4 < 1000 */
+
+struct hac_priv {
+	unsigned long mmio;	/* HAC base address */
+} hac_cpu_data[] = {
+#if defined(CONFIG_CPU_SUBTYPE_SH7760)
+	{
+		.mmio	= 0xFE240000,
+	},
+	{
+		.mmio	= 0xFE250000,
+	},
+#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
+	{
+		.mmio	= 0xFFE40000,
+	},
+#else
+#error "Unsupported SuperH SoC"
+#endif
+};
+
+#define HACREG(reg)	(*(unsigned long *)(hac->mmio + (reg)))
+
+/*
+ * AC97 read/write flow as outlined in the SH7760 manual (pages 903-906)
+ */
+static int hac_get_codec_data(struct hac_priv *hac, unsigned short r,
+			      unsigned short *v)
+{
+	unsigned int to1, to2, i;
+	unsigned short adr;
+
+	for (i = AC97_READ_RETRY; i; i--) {
+		*v = 0;
+		/* wait for HAC to receive something from the codec */
+		for (to1 = TMO_E4;
+		     to1 && !(HACREG(HACRSR) & RSR_STARY);
+		     --to1)
+			udelay(1);
+		for (to2 = TMO_E4; 
+		     to2 && !(HACREG(HACRSR) & RSR_STDRY);
+		     --to2)
+			udelay(1);
+
+		if (!to1 && !to2)
+			return 0;	/* codec comm is down */
+
+		adr = ((HACREG(HACCSAR) & CSAR_MASK) >> CSAR_SHIFT);
+		*v  = ((HACREG(HACCSDR) & CSDR_MASK) >> CSDR_SHIFT);
+
+		HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY);
+
+		if (r == adr)
+			break;
+
+		/* manual says: wait at least 21 usec before retrying */
+		udelay(21);
+	}
+	HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY);
+	return i;
+}
+
+static unsigned short hac_read_codec_aux(struct hac_priv *hac,
+					 unsigned short reg)
+{
+	unsigned short val;
+	unsigned int i, to;
+
+	for (i = AC97_READ_RETRY; i; i--) {
+		/* send_read_request */
+		local_irq_disable();
+		HACREG(HACTSR) &= ~(TSR_CMDAMT);
+		HACREG(HACCSAR) = (reg << CSAR_SHIFT) | CSAR_RD;
+		local_irq_enable();
+
+		for (to = TMO_E3;
+		     to && !(HACREG(HACTSR) & TSR_CMDAMT);
+		     --to)
+			udelay(1);
+
+		HACREG(HACTSR) &= ~TSR_CMDAMT;
+		val = 0;
+		if (hac_get_codec_data(hac, reg, &val) != 0)
+			break;
+	}
+
+	return i ? val : ~0;
+}
+
+static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+			   unsigned short val)
+{
+	int unit_id = 0 /* ac97->private_data */;
+	struct hac_priv *hac = &hac_cpu_data[unit_id];
+	unsigned int i, to;
+	/* write_codec_aux */
+	for (i = AC97_WRITE_RETRY; i; i--) {
+		/* send_write_request */
+		local_irq_disable();
+		HACREG(HACTSR) &= ~(TSR_CMDDMT | TSR_CMDAMT);
+		HACREG(HACCSDR) = (val << CSDR_SHIFT);
+		HACREG(HACCSAR) = (reg << CSAR_SHIFT) & (~CSAR_RD);
+		local_irq_enable();
+
+		/* poll-wait for CMDAMT and CMDDMT */
+		for (to = TMO_E1;
+		     to && !(HACREG(HACTSR) & (TSR_CMDAMT|TSR_CMDDMT));
+		     --to)
+			udelay(1);
+
+		HACREG(HACTSR) &= ~(TSR_CMDAMT | TSR_CMDDMT);
+		if (to)
+			break;
+		/* timeout, try again */
+	}
+}
+
+static unsigned short hac_ac97_read(struct snd_ac97 *ac97,
+				    unsigned short reg)
+{
+	int unit_id = 0 /* ac97->private_data */;
+	struct hac_priv *hac = &hac_cpu_data[unit_id];
+	return hac_read_codec_aux(hac, reg);
+}
+
+static void hac_ac97_warmrst(struct snd_ac97 *ac97)
+{
+	int unit_id = 0 /* ac97->private_data */;
+	struct hac_priv *hac = &hac_cpu_data[unit_id];
+	unsigned int tmo;
+
+	HACREG(HACCR) = CR_WMRT | CR_ST | CR_B9;
+	msleep(10);
+	HACREG(HACCR) = CR_ST | CR_B9;
+	for (tmo = 1000; (tmo > 0) && !(HACREG(HACCR) & CR_CR); tmo--)
+		udelay(1);
+
+	if (!tmo)
+		printk(KERN_INFO "hac: reset: AC97 link down!\n");
+	/* settings this bit lets us have a conversation with codec */
+	HACREG(HACACR) |= ACR_TX12ATOM;
+}
+
+static void hac_ac97_coldrst(struct snd_ac97 *ac97)
+{
+	int unit_id = 0 /* ac97->private_data */;
+	struct hac_priv *hac;
+	hac = &hac_cpu_data[unit_id];
+
+	HACREG(HACCR) = 0;
+	HACREG(HACCR) = CR_CDRT | CR_ST | CR_B9;
+	msleep(10);
+	hac_ac97_warmrst(ac97);
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+	.read	= hac_ac97_read,
+	.write	= hac_ac97_write,
+	.reset	= hac_ac97_coldrst,
+	.warm_reset = hac_ac97_warmrst,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static int hac_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params,
+			 struct snd_soc_dai *dai)
+{
+	struct hac_priv *hac = &hac_cpu_data[dai->id];
+	int d = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
+
+	switch (params->msbits) {
+	case 16:
+		HACREG(HACACR) |= d ?  ACR_DMARX16 :  ACR_DMATX16;
+		HACREG(HACACR) &= d ? ~ACR_DMARX20 : ~ACR_DMATX20;
+		break;
+	case 20:
+		HACREG(HACACR) &= d ? ~ACR_DMARX16 : ~ACR_DMATX16;
+		HACREG(HACACR) |= d ?  ACR_DMARX20 :  ACR_DMATX20;
+		break;
+	default:
+		pr_debug("hac: invalid depth %d bit\n", params->msbits);
+		return -EINVAL;
+		break;
+	}
+
+	return 0;
+}
+
+#define AC97_RATES	\
+	SNDRV_PCM_RATE_8000_192000
+
+#define AC97_FMTS	\
+	SNDRV_PCM_FMTBIT_S16_LE
+
+static const struct snd_soc_dai_ops hac_dai_ops = {
+	.hw_params	= hac_hw_params,
+};
+
+static struct snd_soc_dai_driver sh4_hac_dai[] = {
+{
+	.name			= "hac-dai.0",
+	.ac97_control		= 1,
+	.playback = {
+		.rates		= AC97_RATES,
+		.formats	= AC97_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 2,
+	},
+	.capture = {
+		.rates		= AC97_RATES,
+		.formats	= AC97_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 2,
+	},
+	.ops = &hac_dai_ops,
+},
+#ifdef CONFIG_CPU_SUBTYPE_SH7760
+{
+	.name			= "hac-dai.1",
+	.id			= 1,
+	.playback = {
+		.rates		= AC97_RATES,
+		.formats	= AC97_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 2,
+	},
+	.capture = {
+		.rates		= AC97_RATES,
+		.formats	= AC97_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 2,
+	},
+	.ops = &hac_dai_ops,
+
+},
+#endif
+};
+
+static int __devinit hac_soc_platform_probe(struct platform_device *pdev)
+{
+	return snd_soc_register_dais(&pdev->dev, sh4_hac_dai,
+			ARRAY_SIZE(sh4_hac_dai));
+}
+
+static int __devexit hac_soc_platform_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(sh4_hac_dai));
+	return 0;
+}
+
+static struct platform_driver hac_pcm_driver = {
+	.driver = {
+			.name = "hac-pcm-audio",
+			.owner = THIS_MODULE,
+	},
+
+	.probe = hac_soc_platform_probe,
+	.remove = __devexit_p(hac_soc_platform_remove),
+};
+
+module_platform_driver(hac_pcm_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SuperH onchip HAC (AC97) audio driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/ap/os/linux/linux-3.4.x/sound/soc/sh/migor.c b/ap/os/linux/linux-3.4.x/sound/soc/sh/migor.c
new file mode 100644
index 0000000..8526e1e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/sound/soc/sh/migor.c
@@ -0,0 +1,225 @@
+/*
+ * ALSA SoC driver for Migo-R
+ *
+ * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clkdev.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+
+#include <asm/clock.h>
+
+#include <cpu/sh7722.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include "../codecs/wm8978.h"
+#include "siu.h"
+
+/* Default 8000Hz sampling frequency */
+static unsigned long codec_freq = 8000 * 512;
+
+static unsigned int use_count;
+
+/* External clock, sourced from the codec at the SIUMCKB pin */
+static unsigned long siumckb_recalc(struct clk *clk)
+{
+	return codec_freq;
+}
+
+static struct sh_clk_ops siumckb_clk_ops = {
+	.recalc = siumckb_recalc,
+};
+
+static struct clk siumckb_clk = {
+	.ops		= &siumckb_clk_ops,
+	.rate		= 0, /* initialised at run-time */
+};
+
+static struct clk_lookup *siumckb_lookup;
+
+static int migor_hw_params(struct snd_pcm_substream *substream,
+			   struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	int ret;
+	unsigned int rate = params_rate(params);
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8978_PLL, 13000000,
+				     SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8978_OPCLKRATE, rate * 512);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_NB_IF |
+				  SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_fmt(rtd->cpu_dai, SND_SOC_DAIFMT_NB_IF |
+				  SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	codec_freq = rate * 512;
+	/*
+	 * This propagates the parent frequency change to children and
+	 * recalculates the frequency table
+	 */
+	clk_set_rate(&siumckb_clk, codec_freq);
+	dev_dbg(codec_dai->dev, "%s: configure %luHz\n", __func__, codec_freq);
+
+	ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, SIU_CLKB_EXT,
+				     codec_freq / 2, SND_SOC_CLOCK_IN);
+
+	if (!ret)
+		use_count++;
+
+	return ret;
+}
+
+static int migor_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+	if (use_count) {
+		use_count--;
+
+		if (!use_count)
+			snd_soc_dai_set_sysclk(codec_dai, WM8978_PLL, 0,
+					       SND_SOC_CLOCK_IN);
+	} else {
+		dev_dbg(codec_dai->dev, "Unbalanced hw_free!\n");
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops migor_dai_ops = {
+	.hw_params = migor_hw_params,
+	.hw_free = migor_hw_free,
+};
+
+static const struct snd_soc_dapm_widget migor_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone", NULL),
+	SND_SOC_DAPM_MIC("Onboard Microphone", NULL),
+	SND_SOC_DAPM_MIC("External Microphone", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/* Headphone output connected to LHP/RHP, enable OUT4 for VMID */
+	{ "Headphone", NULL,  "OUT4 VMID" },
+	{ "OUT4 VMID", NULL,  "LHP" },
+	{ "OUT4 VMID", NULL,  "RHP" },
+
+	/* On-board microphone */
+	{ "RMICN", NULL, "Mic Bias" },
+	{ "RMICP", NULL, "Mic Bias" },
+	{ "Mic Bias", NULL, "Onboard Microphone" },
+
+	/* External microphone */
+	{ "LMICN", NULL, "Mic Bias" },
+	{ "LMICP", NULL, "Mic Bias" },
+	{ "Mic Bias", NULL, "External Microphone" },
+};
+
+static int migor_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+	snd_soc_dapm_new_controls(dapm, migor_dapm_widgets,
+				  ARRAY_SIZE(migor_dapm_widgets));
+
+	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
+
+	return 0;
+}
+
+/* migor digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link migor_dai = {
+	.name = "wm8978",
+	.stream_name = "WM8978",
+	.cpu_dai_name = "siu-i2s-dai",
+	.codec_dai_name = "wm8978-hifi",
+	.platform_name = "siu-pcm-audio",
+	.codec_name = "wm8978.0-001a",
+	.ops = &migor_dai_ops,
+	.init = migor_dai_init,
+};
+
+/* migor audio machine driver */
+static struct snd_soc_card snd_soc_migor = {
+	.name = "Migo-R",
+	.owner = THIS_MODULE,
+	.dai_link = &migor_dai,
+	.num_links = 1,
+};
+
+static struct platform_device *migor_snd_device;
+
+static int __init migor_init(void)
+{
+	int ret;
+
+	ret = clk_register(&siumckb_clk);
+	if (ret < 0)
+		return ret;
+
+	siumckb_lookup = clkdev_alloc(&siumckb_clk, "siumckb_clk", NULL);
+	if (!siumckb_lookup) {
+		ret = -ENOMEM;
+		goto eclkdevalloc;
+	}
+	clkdev_add(siumckb_lookup);
+
+	/* Port number used on this machine: port B */
+	migor_snd_device = platform_device_alloc("soc-audio", 1);
+	if (!migor_snd_device) {
+		ret = -ENOMEM;
+		goto epdevalloc;
+	}
+
+	platform_set_drvdata(migor_snd_device, &snd_soc_migor);
+
+	ret = platform_device_add(migor_snd_device);
+	if (ret)
+		goto epdevadd;
+
+	return 0;
+
+epdevadd:
+	platform_device_put(migor_snd_device);
+epdevalloc:
+	clkdev_drop(siumckb_lookup);
+eclkdevalloc:
+	clk_unregister(&siumckb_clk);
+	return ret;
+}
+
+static void __exit migor_exit(void)
+{
+	clkdev_drop(siumckb_lookup);
+	clk_unregister(&siumckb_clk);
+	platform_device_unregister(migor_snd_device);
+}
+
+module_init(migor_init);
+module_exit(migor_exit);
+
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_DESCRIPTION("ALSA SoC Migor");
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/sound/soc/sh/sh7760-ac97.c b/ap/os/linux/linux-3.4.x/sound/soc/sh/sh7760-ac97.c
new file mode 100644
index 0000000..4a3568a
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/sound/soc/sh/sh7760-ac97.c
@@ -0,0 +1,73 @@
+/*
+ * Generic AC97 sound support for SH7760
+ *
+ * (c) 2007 Manuel Lauss
+ *
+ * Licensed under the GPLv2.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <asm/io.h>
+
+#define IPSEL 0xFE400034
+
+static struct snd_soc_dai_link sh7760_ac97_dai = {
+	.name = "AC97",
+	.stream_name = "AC97 HiFi",
+	.cpu_dai_name = "hac-dai.0",	/* HAC0 */
+	.codec_dai_name = "ac97-hifi",
+	.platform_name = "sh7760-pcm-audio",
+	.codec_name = "ac97-codec",
+	.ops = NULL,
+};
+
+static struct snd_soc_card sh7760_ac97_soc_machine  = {
+	.name = "SH7760 AC97",
+	.owner = THIS_MODULE,
+	.dai_link = &sh7760_ac97_dai,
+	.num_links = 1,
+};
+
+static struct platform_device *sh7760_ac97_snd_device;
+
+static int __init sh7760_ac97_init(void)
+{
+	int ret;
+	unsigned short ipsel;
+
+	/* enable both AC97 controllers in pinmux reg */
+	ipsel = __raw_readw(IPSEL);
+	__raw_writew(ipsel | (3 << 10), IPSEL);
+
+	ret = -ENOMEM;
+	sh7760_ac97_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!sh7760_ac97_snd_device)
+		goto out;
+
+	platform_set_drvdata(sh7760_ac97_snd_device,
+			     &sh7760_ac97_soc_machine);
+	ret = platform_device_add(sh7760_ac97_snd_device);
+
+	if (ret)
+		platform_device_put(sh7760_ac97_snd_device);
+
+out:
+	return ret;
+}
+
+static void __exit sh7760_ac97_exit(void)
+{
+	platform_device_unregister(sh7760_ac97_snd_device);
+}
+
+module_init(sh7760_ac97_init);
+module_exit(sh7760_ac97_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic SH7760 AC97 sound machine");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/ap/os/linux/linux-3.4.x/sound/soc/sh/siu.h b/ap/os/linux/linux-3.4.x/sound/soc/sh/siu.h
new file mode 100644
index 0000000..83c3430
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/sound/soc/sh/siu.h
@@ -0,0 +1,194 @@
+/*
+ * siu.h - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral.
+ *
+ * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2006 Carlos Munoz <carlos@kenati.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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef SIU_H
+#define SIU_H
+
+/* Common kernel and user-space firmware-building defines and types */
+
+#define YRAM0_SIZE		(0x0040 / 4)		/* 16 */
+#define YRAM1_SIZE		(0x0080 / 4)		/* 32 */
+#define YRAM2_SIZE		(0x0040 / 4)		/* 16 */
+#define YRAM3_SIZE		(0x0080 / 4)		/* 32 */
+#define YRAM4_SIZE		(0x0080 / 4)		/* 32 */
+#define YRAM_DEF_SIZE		(YRAM0_SIZE + YRAM1_SIZE + YRAM2_SIZE + \
+				 YRAM3_SIZE + YRAM4_SIZE)
+#define YRAM_FIR_SIZE		(0x0400 / 4)		/* 256 */
+#define YRAM_IIR_SIZE		(0x0200 / 4)		/* 128 */
+
+#define XRAM0_SIZE		(0x0400 / 4)		/* 256 */
+#define XRAM1_SIZE		(0x0200 / 4)		/* 128 */
+#define XRAM2_SIZE		(0x0200 / 4)		/* 128 */
+
+/* PRAM program array size */
+#define PRAM0_SIZE		(0x0100 / 4)		/* 64 */
+#define PRAM1_SIZE		((0x2000 - 0x0100) / 4)	/* 1984 */
+
+#include <linux/types.h>
+
+struct siu_spb_param {
+	__u32	ab1a;	/* input FIFO address */
+	__u32	ab0a;	/* output FIFO address */
+	__u32	dir;	/* 0=the ather except CPUOUTPUT, 1=CPUINPUT */
+	__u32	event;	/* SPB program starting conditions */
+	__u32	stfifo;	/* STFIFO register setting value */
+	__u32	trdat;	/* TRDAT register setting value */
+};
+
+struct siu_firmware {
+	__u32			yram_fir_coeff[YRAM_FIR_SIZE];
+	__u32			pram0[PRAM0_SIZE];
+	__u32			pram1[PRAM1_SIZE];
+	__u32			yram0[YRAM0_SIZE];
+	__u32			yram1[YRAM1_SIZE];
+	__u32			yram2[YRAM2_SIZE];
+	__u32			yram3[YRAM3_SIZE];
+	__u32			yram4[YRAM4_SIZE];
+	__u32			spbpar_num;
+	struct siu_spb_param	spbpar[32];
+};
+
+#ifdef __KERNEL__
+
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/sh_dma.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#define SIU_PERIOD_BYTES_MAX	8192		/* DMA transfer/period size */
+#define SIU_PERIOD_BYTES_MIN	256		/* DMA transfer/period size */
+#define SIU_PERIODS_MAX		64		/* Max periods in buffer */
+#define SIU_PERIODS_MIN		4		/* Min periods in buffer */
+#define SIU_BUFFER_BYTES_MAX	(SIU_PERIOD_BYTES_MAX * SIU_PERIODS_MAX)
+
+/* SIU ports: only one can be used at a time */
+enum {
+	SIU_PORT_A,
+	SIU_PORT_B,
+	SIU_PORT_NUM,
+};
+
+/* SIU clock configuration */
+enum {
+	SIU_CLKA_PLL,
+	SIU_CLKA_EXT,
+	SIU_CLKB_PLL,
+	SIU_CLKB_EXT
+};
+
+struct device;
+struct siu_info {
+	struct device		*dev;
+	int			port_id;
+	u32 __iomem		*pram;
+	u32 __iomem		*xram;
+	u32 __iomem		*yram;
+	u32 __iomem		*reg;
+	struct siu_firmware	fw;
+};
+
+struct siu_stream {
+	struct tasklet_struct		tasklet;
+	struct snd_pcm_substream	*substream;
+	snd_pcm_format_t		format;
+	size_t				buf_bytes;
+	size_t				period_bytes;
+	int				cur_period;	/* Period currently in dma */
+	u32				volume;
+	snd_pcm_sframes_t		xfer_cnt;	/* Number of frames */
+	u8				rw_flg;		/* transfer status */
+	/* DMA status */
+	struct dma_chan			*chan;		/* DMA channel */
+	struct dma_async_tx_descriptor	*tx_desc;
+	dma_cookie_t			cookie;
+	struct sh_dmae_slave		param;
+};
+
+struct siu_port {
+	unsigned long		play_cap;	/* Used to track full duplex */
+	struct snd_pcm		*pcm;
+	struct siu_stream	playback;
+	struct siu_stream	capture;
+	u32			stfifo;		/* STFIFO value from firmware */
+	u32			trdat;		/* TRDAT value from firmware */
+};
+
+extern struct siu_port *siu_ports[SIU_PORT_NUM];
+
+static inline struct siu_port *siu_port_info(struct snd_pcm_substream *substream)
+{
+	struct platform_device *pdev =
+		to_platform_device(substream->pcm->card->dev);
+	return siu_ports[pdev->id];
+}
+
+/* Register access */
+static inline void siu_write32(u32 __iomem *addr, u32 val)
+{
+	__raw_writel(val, addr);
+}
+
+static inline u32 siu_read32(u32 __iomem *addr)
+{
+	return __raw_readl(addr);
+}
+
+/* SIU registers */
+#define SIU_IFCTL	(0x000 / sizeof(u32))
+#define SIU_SRCTL	(0x004 / sizeof(u32))
+#define SIU_SFORM	(0x008 / sizeof(u32))
+#define SIU_CKCTL	(0x00c / sizeof(u32))
+#define SIU_TRDAT	(0x010 / sizeof(u32))
+#define SIU_STFIFO	(0x014 / sizeof(u32))
+#define SIU_DPAK	(0x01c / sizeof(u32))
+#define SIU_CKREV	(0x020 / sizeof(u32))
+#define SIU_EVNTC	(0x028 / sizeof(u32))
+#define SIU_SBCTL	(0x040 / sizeof(u32))
+#define SIU_SBPSET	(0x044 / sizeof(u32))
+#define SIU_SBFSTS	(0x068 / sizeof(u32))
+#define SIU_SBDVCA	(0x06c / sizeof(u32))
+#define SIU_SBDVCB	(0x070 / sizeof(u32))
+#define SIU_SBACTIV	(0x074 / sizeof(u32))
+#define SIU_DMAIA	(0x090 / sizeof(u32))
+#define SIU_DMAIB	(0x094 / sizeof(u32))
+#define SIU_DMAOA	(0x098 / sizeof(u32))
+#define SIU_DMAOB	(0x09c / sizeof(u32))
+#define SIU_DMAML	(0x0a0 / sizeof(u32))
+#define SIU_SPSTS	(0x0cc / sizeof(u32))
+#define SIU_SPCTL	(0x0d0 / sizeof(u32))
+#define SIU_BRGASEL	(0x100 / sizeof(u32))
+#define SIU_BRRA	(0x104 / sizeof(u32))
+#define SIU_BRGBSEL	(0x108 / sizeof(u32))
+#define SIU_BRRB	(0x10c / sizeof(u32))
+
+extern struct snd_soc_platform_driver siu_platform;
+extern struct siu_info *siu_i2s_data;
+
+int siu_init_port(int port, struct siu_port **port_info, struct snd_card *card);
+void siu_free_port(struct siu_port *port_info);
+
+#endif
+
+#endif /* SIU_H */
diff --git a/ap/os/linux/linux-3.4.x/sound/soc/sh/siu_dai.c b/ap/os/linux/linux-3.4.x/sound/soc/sh/siu_dai.c
new file mode 100644
index 0000000..52d4c17
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/sound/soc/sh/siu_dai.c
@@ -0,0 +1,853 @@
+/*
+ * siu_dai.c - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral.
+ *
+ * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2006 Carlos Munoz <carlos@kenati.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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include <asm/clock.h>
+#include <asm/siu.h>
+
+#include <sound/control.h>
+#include <sound/soc.h>
+
+#include "siu.h"
+
+/* Board specifics */
+#if defined(CONFIG_CPU_SUBTYPE_SH7722)
+# define SIU_MAX_VOLUME		0x1000
+#else
+# define SIU_MAX_VOLUME		0x7fff
+#endif
+
+#define PRAM_SIZE	0x2000
+#define XRAM_SIZE	0x800
+#define YRAM_SIZE	0x800
+
+#define XRAM_OFFSET	0x4000
+#define YRAM_OFFSET	0x6000
+#define REG_OFFSET	0xc000
+
+#define PLAYBACK_ENABLED	1
+#define CAPTURE_ENABLED		2
+
+#define VOLUME_CAPTURE		0
+#define VOLUME_PLAYBACK		1
+#define DFLT_VOLUME_LEVEL	0x08000800
+
+/*
+ * SPDIF is only available on port A and on some SIU implementations it is only
+ * available for input. Due to the lack of hardware to test it, SPDIF is left
+ * disabled in this driver version
+ */
+struct format_flag {
+	u32	i2s;
+	u32	pcm;
+	u32	spdif;
+	u32	mask;
+};
+
+struct port_flag {
+	struct format_flag	playback;
+	struct format_flag	capture;
+};
+
+struct siu_info *siu_i2s_data;
+
+static struct port_flag siu_flags[SIU_PORT_NUM] = {
+	[SIU_PORT_A] = {
+		.playback = {
+			.i2s	= 0x50000000,
+			.pcm	= 0x40000000,
+			.spdif	= 0x80000000,	/* not on all SIU versions */
+			.mask	= 0xd0000000,
+		},
+		.capture = {
+			.i2s	= 0x05000000,
+			.pcm	= 0x04000000,
+			.spdif	= 0x08000000,
+			.mask	= 0x0d000000,
+		},
+	},
+	[SIU_PORT_B] = {
+		.playback = {
+			.i2s	= 0x00500000,
+			.pcm	= 0x00400000,
+			.spdif	= 0,		/* impossible - turn off */
+			.mask	= 0x00500000,
+		},
+		.capture = {
+			.i2s	= 0x00050000,
+			.pcm	= 0x00040000,
+			.spdif	= 0,		/* impossible - turn off */
+			.mask	= 0x00050000,
+		},
+	},
+};
+
+static void siu_dai_start(struct siu_port *port_info)
+{
+	struct siu_info *info = siu_i2s_data;
+	u32 __iomem *base = info->reg;
+
+	dev_dbg(port_info->pcm->card->dev, "%s\n", __func__);
+
+	/* Issue software reset to siu */
+	siu_write32(base + SIU_SRCTL, 0);
+
+	/* Wait for the reset to take effect */
+	udelay(1);
+
+	port_info->stfifo = 0;
+	port_info->trdat = 0;
+
+	/* portA, portB, SIU operate */
+	siu_write32(base + SIU_SRCTL, 0x301);
+
+	/* portA=256fs, portB=256fs */
+	siu_write32(base + SIU_CKCTL, 0x40400000);
+
+	/* portA's BRG does not divide SIUCKA */
+	siu_write32(base + SIU_BRGASEL, 0);
+	siu_write32(base + SIU_BRRA, 0);
+
+	/* portB's BRG divides SIUCKB by half */
+	siu_write32(base + SIU_BRGBSEL, 1);
+	siu_write32(base + SIU_BRRB, 0);
+
+	siu_write32(base + SIU_IFCTL, 0x44440000);
+
+	/* portA: 32 bit/fs, master; portB: 32 bit/fs, master */
+	siu_write32(base + SIU_SFORM, 0x0c0c0000);
+
+	/*
+	 * Volume levels: looks like the DSP firmware implements volume controls
+	 * differently from what's described in the datasheet
+	 */
+	siu_write32(base + SIU_SBDVCA, port_info->playback.volume);
+	siu_write32(base + SIU_SBDVCB, port_info->capture.volume);
+}
+
+static void siu_dai_stop(struct siu_port *port_info)
+{
+	struct siu_info *info = siu_i2s_data;
+	u32 __iomem *base = info->reg;
+
+	/* SIU software reset */
+	siu_write32(base + SIU_SRCTL, 0);
+}
+
+static void siu_dai_spbAselect(struct siu_port *port_info)
+{
+	struct siu_info *info = siu_i2s_data;
+	struct siu_firmware *fw = &info->fw;
+	u32 *ydef = fw->yram0;
+	u32 idx;
+
+	/* path A use */
+	if (!info->port_id)
+		idx = 1;		/* portA */
+	else
+		idx = 2;		/* portB */
+
+	ydef[0] = (fw->spbpar[idx].ab1a << 16) |
+		(fw->spbpar[idx].ab0a << 8) |
+		(fw->spbpar[idx].dir << 7) | 3;
+	ydef[1] = fw->yram0[1];	/* 0x03000300 */
+	ydef[2] = (16 / 2) << 24;
+	ydef[3] = fw->yram0[3];	/* 0 */
+	ydef[4] = fw->yram0[4];	/* 0 */
+	ydef[7] = fw->spbpar[idx].event;
+	port_info->stfifo |= fw->spbpar[idx].stfifo;
+	port_info->trdat |= fw->spbpar[idx].trdat;
+}
+
+static void siu_dai_spbBselect(struct siu_port *port_info)
+{
+	struct siu_info *info = siu_i2s_data;
+	struct siu_firmware *fw = &info->fw;
+	u32 *ydef = fw->yram0;
+	u32 idx;
+
+	/* path B use */
+	if (!info->port_id)
+		idx = 7;		/* portA */
+	else
+		idx = 8;		/* portB */
+
+	ydef[5] = (fw->spbpar[idx].ab1a << 16) |
+		(fw->spbpar[idx].ab0a << 8) | 1;
+	ydef[6] = fw->spbpar[idx].event;
+	port_info->stfifo |= fw->spbpar[idx].stfifo;
+	port_info->trdat |= fw->spbpar[idx].trdat;
+}
+
+static void siu_dai_open(struct siu_stream *siu_stream)
+{
+	struct siu_info *info = siu_i2s_data;
+	u32 __iomem *base = info->reg;
+	u32 srctl, ifctl;
+
+	srctl = siu_read32(base + SIU_SRCTL);
+	ifctl = siu_read32(base + SIU_IFCTL);
+
+	switch (info->port_id) {
+	case SIU_PORT_A:
+		/* portA operates */
+		srctl |= 0x200;
+		ifctl &= ~0xc2;
+		break;
+	case SIU_PORT_B:
+		/* portB operates */
+		srctl |= 0x100;
+		ifctl &= ~0x31;
+		break;
+	}
+
+	siu_write32(base + SIU_SRCTL, srctl);
+	/* Unmute and configure portA */
+	siu_write32(base + SIU_IFCTL, ifctl);
+}
+
+/*
+ * At the moment only fixed Left-upper, Left-lower, Right-upper, Right-lower
+ * packing is supported
+ */
+static void siu_dai_pcmdatapack(struct siu_stream *siu_stream)
+{
+	struct siu_info *info = siu_i2s_data;
+	u32 __iomem *base = info->reg;
+	u32 dpak;
+
+	dpak = siu_read32(base + SIU_DPAK);
+
+	switch (info->port_id) {
+	case SIU_PORT_A:
+		dpak &= ~0xc0000000;
+		break;
+	case SIU_PORT_B:
+		dpak &= ~0x00c00000;
+		break;
+	}
+
+	siu_write32(base + SIU_DPAK, dpak);
+}
+
+static int siu_dai_spbstart(struct siu_port *port_info)
+{
+	struct siu_info *info = siu_i2s_data;
+	u32 __iomem *base = info->reg;
+	struct siu_firmware *fw = &info->fw;
+	u32 *ydef = fw->yram0;
+	int cnt;
+	u32 __iomem *add;
+	u32 *ptr;
+
+	/* Load SPB Program in PRAM */
+	ptr = fw->pram0;
+	add = info->pram;
+	for (cnt = 0; cnt < PRAM0_SIZE; cnt++, add++, ptr++)
+		siu_write32(add, *ptr);
+
+	ptr = fw->pram1;
+	add = info->pram + (0x0100 / sizeof(u32));
+	for (cnt = 0; cnt < PRAM1_SIZE; cnt++, add++, ptr++)
+		siu_write32(add, *ptr);
+
+	/* XRAM initialization */
+	add = info->xram;
+	for (cnt = 0; cnt < XRAM0_SIZE + XRAM1_SIZE + XRAM2_SIZE; cnt++, add++)
+		siu_write32(add, 0);
+
+	/* YRAM variable area initialization */
+	add = info->yram;
+	for (cnt = 0; cnt < YRAM_DEF_SIZE; cnt++, add++)
+		siu_write32(add, ydef[cnt]);
+
+	/* YRAM FIR coefficient area initialization */
+	add = info->yram + (0x0200 / sizeof(u32));
+	for (cnt = 0; cnt < YRAM_FIR_SIZE; cnt++, add++)
+		siu_write32(add, fw->yram_fir_coeff[cnt]);
+
+	/* YRAM IIR coefficient area initialization */
+	add = info->yram + (0x0600 / sizeof(u32));
+	for (cnt = 0; cnt < YRAM_IIR_SIZE; cnt++, add++)
+		siu_write32(add, 0);
+
+	siu_write32(base + SIU_TRDAT, port_info->trdat);
+	port_info->trdat = 0x0;
+
+
+	/* SPB start condition: software */
+	siu_write32(base + SIU_SBACTIV, 0);
+	/* Start SPB */
+	siu_write32(base + SIU_SBCTL, 0xc0000000);
+	/* Wait for program to halt */
+	cnt = 0x10000;
+	while (--cnt && siu_read32(base + SIU_SBCTL) != 0x80000000)
+		cpu_relax();
+
+	if (!cnt)
+		return -EBUSY;
+
+	/* SPB program start address setting */
+	siu_write32(base + SIU_SBPSET, 0x00400000);
+	/* SPB hardware start(FIFOCTL source) */
+	siu_write32(base + SIU_SBACTIV, 0xc0000000);
+
+	return 0;
+}
+
+static void siu_dai_spbstop(struct siu_port *port_info)
+{
+	struct siu_info *info = siu_i2s_data;
+	u32 __iomem *base = info->reg;
+
+	siu_write32(base + SIU_SBACTIV, 0);
+	/* SPB stop */
+	siu_write32(base + SIU_SBCTL, 0);
+
+	port_info->stfifo = 0;
+}
+
+/*		API functions		*/
+
+/* Playback and capture hardware properties are identical */
+static struct snd_pcm_hardware siu_dai_pcm_hw = {
+	.info			= SNDRV_PCM_INFO_INTERLEAVED,
+	.formats		= SNDRV_PCM_FMTBIT_S16,
+	.rates			= SNDRV_PCM_RATE_8000_48000,
+	.rate_min		= 8000,
+	.rate_max		= 48000,
+	.channels_min		= 2,
+	.channels_max		= 2,
+	.buffer_bytes_max	= SIU_BUFFER_BYTES_MAX,
+	.period_bytes_min	= SIU_PERIOD_BYTES_MIN,
+	.period_bytes_max	= SIU_PERIOD_BYTES_MAX,
+	.periods_min		= SIU_PERIODS_MIN,
+	.periods_max		= SIU_PERIODS_MAX,
+};
+
+static int siu_dai_info_volume(struct snd_kcontrol *kctrl,
+			       struct snd_ctl_elem_info *uinfo)
+{
+	struct siu_port *port_info = snd_kcontrol_chip(kctrl);
+
+	dev_dbg(port_info->pcm->card->dev, "%s\n", __func__);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = SIU_MAX_VOLUME;
+
+	return 0;
+}
+
+static int siu_dai_get_volume(struct snd_kcontrol *kctrl,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct siu_port *port_info = snd_kcontrol_chip(kctrl);
+	struct device *dev = port_info->pcm->card->dev;
+	u32 vol;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	switch (kctrl->private_value) {
+	case VOLUME_PLAYBACK:
+		/* Playback is always on port 0 */
+		vol = port_info->playback.volume;
+		ucontrol->value.integer.value[0] = vol & 0xffff;
+		ucontrol->value.integer.value[1] = vol >> 16 & 0xffff;
+		break;
+	case VOLUME_CAPTURE:
+		/* Capture is always on port 1 */
+		vol = port_info->capture.volume;
+		ucontrol->value.integer.value[0] = vol & 0xffff;
+		ucontrol->value.integer.value[1] = vol >> 16 & 0xffff;
+		break;
+	default:
+		dev_err(dev, "%s() invalid private_value=%ld\n",
+			__func__, kctrl->private_value);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int siu_dai_put_volume(struct snd_kcontrol *kctrl,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct siu_port *port_info = snd_kcontrol_chip(kctrl);
+	struct device *dev = port_info->pcm->card->dev;
+	struct siu_info *info = siu_i2s_data;
+	u32 __iomem *base = info->reg;
+	u32 new_vol;
+	u32 cur_vol;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	if (ucontrol->value.integer.value[0] < 0 ||
+	    ucontrol->value.integer.value[0] > SIU_MAX_VOLUME ||
+	    ucontrol->value.integer.value[1] < 0 ||
+	    ucontrol->value.integer.value[1] > SIU_MAX_VOLUME)
+		return -EINVAL;
+
+	new_vol = ucontrol->value.integer.value[0] |
+		ucontrol->value.integer.value[1] << 16;
+
+	/* See comment above - DSP firmware implementation */
+	switch (kctrl->private_value) {
+	case VOLUME_PLAYBACK:
+		/* Playback is always on port 0 */
+		cur_vol = port_info->playback.volume;
+		siu_write32(base + SIU_SBDVCA, new_vol);
+		port_info->playback.volume = new_vol;
+		break;
+	case VOLUME_CAPTURE:
+		/* Capture is always on port 1 */
+		cur_vol = port_info->capture.volume;
+		siu_write32(base + SIU_SBDVCB, new_vol);
+		port_info->capture.volume = new_vol;
+		break;
+	default:
+		dev_err(dev, "%s() invalid private_value=%ld\n",
+			__func__, kctrl->private_value);
+		return -EINVAL;
+	}
+
+	if (cur_vol != new_vol)
+		return 1;
+
+	return 0;
+}
+
+static struct snd_kcontrol_new playback_controls = {
+	.iface		= SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name		= "PCM Playback Volume",
+	.index		= 0,
+	.info		= siu_dai_info_volume,
+	.get		= siu_dai_get_volume,
+	.put		= siu_dai_put_volume,
+	.private_value	= VOLUME_PLAYBACK,
+};
+
+static struct snd_kcontrol_new capture_controls = {
+	.iface		= SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name		= "PCM Capture Volume",
+	.index		= 0,
+	.info		= siu_dai_info_volume,
+	.get		= siu_dai_get_volume,
+	.put		= siu_dai_put_volume,
+	.private_value	= VOLUME_CAPTURE,
+};
+
+int siu_init_port(int port, struct siu_port **port_info, struct snd_card *card)
+{
+	struct device *dev = card->dev;
+	struct snd_kcontrol *kctrl;
+	int ret;
+
+	*port_info = kzalloc(sizeof(**port_info), GFP_KERNEL);
+	if (!*port_info)
+		return -ENOMEM;
+
+	dev_dbg(dev, "%s: port #%d@%p\n", __func__, port, *port_info);
+
+	(*port_info)->playback.volume = DFLT_VOLUME_LEVEL;
+	(*port_info)->capture.volume = DFLT_VOLUME_LEVEL;
+
+	/*
+	 * Add mixer support. The SPB is used to change the volume. Both
+	 * ports use the same SPB. Therefore, we only register one
+	 * control instance since it will be used by both channels.
+	 * In error case we continue without controls.
+	 */
+	kctrl = snd_ctl_new1(&playback_controls, *port_info);
+	ret = snd_ctl_add(card, kctrl);
+	if (ret < 0)
+		dev_err(dev,
+			"failed to add playback controls %p port=%d err=%d\n",
+			kctrl, port, ret);
+
+	kctrl = snd_ctl_new1(&capture_controls, *port_info);
+	ret = snd_ctl_add(card, kctrl);
+	if (ret < 0)
+		dev_err(dev,
+			"failed to add capture controls %p port=%d err=%d\n",
+			kctrl, port, ret);
+
+	return 0;
+}
+
+void siu_free_port(struct siu_port *port_info)
+{
+	kfree(port_info);
+}
+
+static int siu_dai_startup(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
+{
+	struct siu_info *info = snd_soc_dai_get_drvdata(dai);
+	struct snd_pcm_runtime *rt = substream->runtime;
+	struct siu_port	*port_info = siu_port_info(substream);
+	int ret;
+
+	dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__,
+		info->port_id, port_info);
+
+	snd_soc_set_runtime_hwparams(substream, &siu_dai_pcm_hw);
+
+	ret = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS);
+	if (unlikely(ret < 0))
+		return ret;
+
+	siu_dai_start(port_info);
+
+	return 0;
+}
+
+static void siu_dai_shutdown(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
+{
+	struct siu_info *info = snd_soc_dai_get_drvdata(dai);
+	struct siu_port	*port_info = siu_port_info(substream);
+
+	dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__,
+		info->port_id, port_info);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		port_info->play_cap &= ~PLAYBACK_ENABLED;
+	else
+		port_info->play_cap &= ~CAPTURE_ENABLED;
+
+	/* Stop the siu if the other stream is not using it */
+	if (!port_info->play_cap) {
+		/* during stmread or stmwrite ? */
+		BUG_ON(port_info->playback.rw_flg || port_info->capture.rw_flg);
+		siu_dai_spbstop(port_info);
+		siu_dai_stop(port_info);
+	}
+}
+
+/* PCM part of siu_dai_playback_prepare() / siu_dai_capture_prepare() */
+static int siu_dai_prepare(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
+{
+	struct siu_info *info = snd_soc_dai_get_drvdata(dai);
+	struct snd_pcm_runtime *rt = substream->runtime;
+	struct siu_port *port_info = siu_port_info(substream);
+	struct siu_stream *siu_stream;
+	int self, ret;
+
+	dev_dbg(substream->pcm->card->dev,
+		"%s: port %d, active streams %lx, %d channels\n",
+		__func__, info->port_id, port_info->play_cap, rt->channels);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		self = PLAYBACK_ENABLED;
+		siu_stream = &port_info->playback;
+	} else {
+		self = CAPTURE_ENABLED;
+		siu_stream = &port_info->capture;
+	}
+
+	/* Set up the siu if not already done */
+	if (!port_info->play_cap) {
+		siu_stream->rw_flg = 0;	/* stream-data transfer flag */
+
+		siu_dai_spbAselect(port_info);
+		siu_dai_spbBselect(port_info);
+
+		siu_dai_open(siu_stream);
+
+		siu_dai_pcmdatapack(siu_stream);
+
+		ret = siu_dai_spbstart(port_info);
+		if (ret < 0)
+			goto fail;
+	} else {
+		ret = 0;
+	}
+
+	port_info->play_cap |= self;
+
+fail:
+	return ret;
+}
+
+/*
+ * SIU can set bus format to I2S / PCM / SPDIF independently for playback and
+ * capture, however, the current API sets the bus format globally for a DAI.
+ */
+static int siu_dai_set_fmt(struct snd_soc_dai *dai,
+			   unsigned int fmt)
+{
+	struct siu_info *info = snd_soc_dai_get_drvdata(dai);
+	u32 __iomem *base = info->reg;
+	u32 ifctl;
+
+	dev_dbg(dai->dev, "%s: fmt 0x%x on port %d\n",
+		__func__, fmt, info->port_id);
+
+	if (info->port_id < 0)
+		return -ENODEV;
+
+	/* Here select between I2S / PCM / SPDIF */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		ifctl = siu_flags[info->port_id].playback.i2s |
+			siu_flags[info->port_id].capture.i2s;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		ifctl = siu_flags[info->port_id].playback.pcm |
+			siu_flags[info->port_id].capture.pcm;
+		break;
+	/* SPDIF disabled - see comment at the top */
+	default:
+		return -EINVAL;
+	}
+
+	ifctl |= ~(siu_flags[info->port_id].playback.mask |
+		   siu_flags[info->port_id].capture.mask) &
+		siu_read32(base + SIU_IFCTL);
+	siu_write32(base + SIU_IFCTL, ifctl);
+
+	return 0;
+}
+
+static int siu_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+			      unsigned int freq, int dir)
+{
+	struct clk *siu_clk, *parent_clk;
+	char *siu_name, *parent_name;
+	int ret;
+
+	if (dir != SND_SOC_CLOCK_IN)
+		return -EINVAL;
+
+	dev_dbg(dai->dev, "%s: using clock %d\n", __func__, clk_id);
+
+	switch (clk_id) {
+	case SIU_CLKA_PLL:
+		siu_name = "siua_clk";
+		parent_name = "pll_clk";
+		break;
+	case SIU_CLKA_EXT:
+		siu_name = "siua_clk";
+		parent_name = "siumcka_clk";
+		break;
+	case SIU_CLKB_PLL:
+		siu_name = "siub_clk";
+		parent_name = "pll_clk";
+		break;
+	case SIU_CLKB_EXT:
+		siu_name = "siub_clk";
+		parent_name = "siumckb_clk";
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	siu_clk = clk_get(dai->dev, siu_name);
+	if (IS_ERR(siu_clk)) {
+		dev_err(dai->dev, "%s: cannot get a SIU clock: %ld\n", __func__,
+			PTR_ERR(siu_clk));
+		return PTR_ERR(siu_clk);
+	}
+
+	parent_clk = clk_get(dai->dev, parent_name);
+	if (IS_ERR(parent_clk)) {
+		ret = PTR_ERR(parent_clk);
+		dev_err(dai->dev, "cannot get a SIU clock parent: %d\n", ret);
+		goto epclkget;
+	}
+
+	ret = clk_set_parent(siu_clk, parent_clk);
+	if (ret < 0) {
+		dev_err(dai->dev, "cannot reparent the SIU clock: %d\n", ret);
+		goto eclksetp;
+	}
+
+	ret = clk_set_rate(siu_clk, freq);
+	if (ret < 0)
+		dev_err(dai->dev, "cannot set SIU clock rate: %d\n", ret);
+
+	/* TODO: when clkdev gets reference counting we'll move these to siu_dai_shutdown() */
+eclksetp:
+	clk_put(parent_clk);
+epclkget:
+	clk_put(siu_clk);
+
+	return ret;
+}
+
+static const struct snd_soc_dai_ops siu_dai_ops = {
+	.startup	= siu_dai_startup,
+	.shutdown	= siu_dai_shutdown,
+	.prepare	= siu_dai_prepare,
+	.set_sysclk	= siu_dai_set_sysclk,
+	.set_fmt	= siu_dai_set_fmt,
+};
+
+static struct snd_soc_dai_driver siu_i2s_dai = {
+	.name	= "siu-i2s-dai",
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.formats = SNDRV_PCM_FMTBIT_S16,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+	},
+	.capture = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.formats = SNDRV_PCM_FMTBIT_S16,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+	 },
+	.ops = &siu_dai_ops,
+};
+
+static int __devinit siu_probe(struct platform_device *pdev)
+{
+	const struct firmware *fw_entry;
+	struct resource *res, *region;
+	struct siu_info *info;
+	int ret;
+
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+	siu_i2s_data = info;
+	info->dev = &pdev->dev;
+
+	ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev);
+	if (ret)
+		goto ereqfw;
+
+	/*
+	 * Loaded firmware is "const" - read only, but we have to modify it in
+	 * snd_siu_sh7343_spbAselect() and snd_siu_sh7343_spbBselect()
+	 */
+	memcpy(&info->fw, fw_entry->data, fw_entry->size);
+
+	release_firmware(fw_entry);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -ENODEV;
+		goto egetres;
+	}
+
+	region = request_mem_region(res->start, resource_size(res),
+				    pdev->name);
+	if (!region) {
+		dev_err(&pdev->dev, "SIU region already claimed\n");
+		ret = -EBUSY;
+		goto ereqmemreg;
+	}
+
+	ret = -ENOMEM;
+	info->pram = ioremap(res->start, PRAM_SIZE);
+	if (!info->pram)
+		goto emappram;
+	info->xram = ioremap(res->start + XRAM_OFFSET, XRAM_SIZE);
+	if (!info->xram)
+		goto emapxram;
+	info->yram = ioremap(res->start + YRAM_OFFSET, YRAM_SIZE);
+	if (!info->yram)
+		goto emapyram;
+	info->reg = ioremap(res->start + REG_OFFSET, resource_size(res) -
+			    REG_OFFSET);
+	if (!info->reg)
+		goto emapreg;
+
+	dev_set_drvdata(&pdev->dev, info);
+
+	/* register using ARRAY version so we can keep dai name */
+	ret = snd_soc_register_dais(&pdev->dev, &siu_i2s_dai, 1);
+	if (ret < 0)
+		goto edaiinit;
+
+	ret = snd_soc_register_platform(&pdev->dev, &siu_platform);
+	if (ret < 0)
+		goto esocregp;
+
+	pm_runtime_enable(&pdev->dev);
+
+	return ret;
+
+esocregp:
+	snd_soc_unregister_dai(&pdev->dev);
+edaiinit:
+	iounmap(info->reg);
+emapreg:
+	iounmap(info->yram);
+emapyram:
+	iounmap(info->xram);
+emapxram:
+	iounmap(info->pram);
+emappram:
+	release_mem_region(res->start, resource_size(res));
+ereqmemreg:
+egetres:
+ereqfw:
+	kfree(info);
+
+	return ret;
+}
+
+static int __devexit siu_remove(struct platform_device *pdev)
+{
+	struct siu_info *info = dev_get_drvdata(&pdev->dev);
+	struct resource *res;
+
+	pm_runtime_disable(&pdev->dev);
+
+	snd_soc_unregister_platform(&pdev->dev);
+	snd_soc_unregister_dai(&pdev->dev);
+
+	iounmap(info->reg);
+	iounmap(info->yram);
+	iounmap(info->xram);
+	iounmap(info->pram);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res)
+		release_mem_region(res->start, resource_size(res));
+	kfree(info);
+
+	return 0;
+}
+
+static struct platform_driver siu_driver = {
+	.driver 	= {
+		.owner	= THIS_MODULE,
+		.name	= "siu-pcm-audio",
+	},
+	.probe		= siu_probe,
+	.remove		= __devexit_p(siu_remove),
+};
+
+module_platform_driver(siu_driver);
+
+MODULE_AUTHOR("Carlos Munoz <carlos@kenati.com>");
+MODULE_DESCRIPTION("ALSA SoC SH7722 SIU driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/sound/soc/sh/siu_pcm.c b/ap/os/linux/linux-3.4.x/sound/soc/sh/siu_pcm.c
new file mode 100644
index 0000000..5cfcc65
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/sound/soc/sh/siu_pcm.c
@@ -0,0 +1,617 @@
+/*
+ * siu_pcm.c - ALSA driver for Renesas SH7343, SH7722 SIU peripheral.
+ *
+ * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2006 Carlos Munoz <carlos@kenati.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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/siu.h>
+
+#include "siu.h"
+
+#define GET_MAX_PERIODS(buf_bytes, period_bytes) \
+				((buf_bytes) / (period_bytes))
+#define PERIOD_OFFSET(buf_addr, period_num, period_bytes) \
+				((buf_addr) + ((period_num) * (period_bytes)))
+
+#define RWF_STM_RD		0x01		/* Read in progress */
+#define RWF_STM_WT		0x02		/* Write in progress */
+
+struct siu_port *siu_ports[SIU_PORT_NUM];
+
+/* transfersize is number of u32 dma transfers per period */
+static int siu_pcm_stmwrite_stop(struct siu_port *port_info)
+{
+	struct siu_info *info = siu_i2s_data;
+	u32 __iomem *base = info->reg;
+	struct siu_stream *siu_stream = &port_info->playback;
+	u32 stfifo;
+
+	if (!siu_stream->rw_flg)
+		return -EPERM;
+
+	/* output FIFO disable */
+	stfifo = siu_read32(base + SIU_STFIFO);
+	siu_write32(base + SIU_STFIFO, stfifo & ~0x0c180c18);
+	pr_debug("%s: STFIFO %x -> %x\n", __func__,
+		 stfifo, stfifo & ~0x0c180c18);
+
+	/* during stmwrite clear */
+	siu_stream->rw_flg = 0;
+
+	return 0;
+}
+
+static int siu_pcm_stmwrite_start(struct siu_port *port_info)
+{
+	struct siu_stream *siu_stream = &port_info->playback;
+
+	if (siu_stream->rw_flg)
+		return -EPERM;
+
+	/* Current period in buffer */
+	port_info->playback.cur_period = 0;
+
+	/* during stmwrite flag set */
+	siu_stream->rw_flg = RWF_STM_WT;
+
+	/* DMA transfer start */
+	tasklet_schedule(&siu_stream->tasklet);
+
+	return 0;
+}
+
+static void siu_dma_tx_complete(void *arg)
+{
+	struct siu_stream *siu_stream = arg;
+
+	if (!siu_stream->rw_flg)
+		return;
+
+	/* Update completed period count */
+	if (++siu_stream->cur_period >=
+	    GET_MAX_PERIODS(siu_stream->buf_bytes,
+			    siu_stream->period_bytes))
+		siu_stream->cur_period = 0;
+
+	pr_debug("%s: done period #%d (%u/%u bytes), cookie %d\n",
+		__func__, siu_stream->cur_period,
+		siu_stream->cur_period * siu_stream->period_bytes,
+		siu_stream->buf_bytes, siu_stream->cookie);
+
+	tasklet_schedule(&siu_stream->tasklet);
+
+	/* Notify alsa: a period is done */
+	snd_pcm_period_elapsed(siu_stream->substream);
+}
+
+static int siu_pcm_wr_set(struct siu_port *port_info,
+			  dma_addr_t buff, u32 size)
+{
+	struct siu_info *info = siu_i2s_data;
+	u32 __iomem *base = info->reg;
+	struct siu_stream *siu_stream = &port_info->playback;
+	struct snd_pcm_substream *substream = siu_stream->substream;
+	struct device *dev = substream->pcm->card->dev;
+	struct dma_async_tx_descriptor *desc;
+	dma_cookie_t cookie;
+	struct scatterlist sg;
+	u32 stfifo;
+
+	sg_init_table(&sg, 1);
+	sg_set_page(&sg, pfn_to_page(PFN_DOWN(buff)),
+		    size, offset_in_page(buff));
+	sg_dma_len(&sg) = size;
+	sg_dma_address(&sg) = buff;
+
+	desc = dmaengine_prep_slave_sg(siu_stream->chan,
+		&sg, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(dev, "Failed to allocate a dma descriptor\n");
+		return -ENOMEM;
+	}
+
+	desc->callback = siu_dma_tx_complete;
+	desc->callback_param = siu_stream;
+	cookie = desc->tx_submit(desc);
+	if (cookie < 0) {
+		dev_err(dev, "Failed to submit a dma transfer\n");
+		return cookie;
+	}
+
+	siu_stream->tx_desc = desc;
+	siu_stream->cookie = cookie;
+
+	dma_async_issue_pending(siu_stream->chan);
+
+	/* only output FIFO enable */
+	stfifo = siu_read32(base + SIU_STFIFO);
+	siu_write32(base + SIU_STFIFO, stfifo | (port_info->stfifo & 0x0c180c18));
+	dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__,
+		stfifo, stfifo | (port_info->stfifo & 0x0c180c18));
+
+	return 0;
+}
+
+static int siu_pcm_rd_set(struct siu_port *port_info,
+			  dma_addr_t buff, size_t size)
+{
+	struct siu_info *info = siu_i2s_data;
+	u32 __iomem *base = info->reg;
+	struct siu_stream *siu_stream = &port_info->capture;
+	struct snd_pcm_substream *substream = siu_stream->substream;
+	struct device *dev = substream->pcm->card->dev;
+	struct dma_async_tx_descriptor *desc;
+	dma_cookie_t cookie;
+	struct scatterlist sg;
+	u32 stfifo;
+
+	dev_dbg(dev, "%s: %u@%llx\n", __func__, size, (unsigned long long)buff);
+
+	sg_init_table(&sg, 1);
+	sg_set_page(&sg, pfn_to_page(PFN_DOWN(buff)),
+		    size, offset_in_page(buff));
+	sg_dma_len(&sg) = size;
+	sg_dma_address(&sg) = buff;
+
+	desc = dmaengine_prep_slave_sg(siu_stream->chan,
+		&sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(dev, "Failed to allocate dma descriptor\n");
+		return -ENOMEM;
+	}
+
+	desc->callback = siu_dma_tx_complete;
+	desc->callback_param = siu_stream;
+	cookie = desc->tx_submit(desc);
+	if (cookie < 0) {
+		dev_err(dev, "Failed to submit dma descriptor\n");
+		return cookie;
+	}
+
+	siu_stream->tx_desc = desc;
+	siu_stream->cookie = cookie;
+
+	dma_async_issue_pending(siu_stream->chan);
+
+	/* only input FIFO enable */
+	stfifo = siu_read32(base + SIU_STFIFO);
+	siu_write32(base + SIU_STFIFO, siu_read32(base + SIU_STFIFO) |
+		    (port_info->stfifo & 0x13071307));
+	dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__,
+		stfifo, stfifo | (port_info->stfifo & 0x13071307));
+
+	return 0;
+}
+
+static void siu_io_tasklet(unsigned long data)
+{
+	struct siu_stream *siu_stream = (struct siu_stream *)data;
+	struct snd_pcm_substream *substream = siu_stream->substream;
+	struct device *dev = substream->pcm->card->dev;
+	struct snd_pcm_runtime *rt = substream->runtime;
+	struct siu_port *port_info = siu_port_info(substream);
+
+	dev_dbg(dev, "%s: flags %x\n", __func__, siu_stream->rw_flg);
+
+	if (!siu_stream->rw_flg) {
+		dev_dbg(dev, "%s: stream inactive\n", __func__);
+		return;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		dma_addr_t buff;
+		size_t count;
+		u8 *virt;
+
+		buff = (dma_addr_t)PERIOD_OFFSET(rt->dma_addr,
+						siu_stream->cur_period,
+						siu_stream->period_bytes);
+		virt = PERIOD_OFFSET(rt->dma_area,
+				     siu_stream->cur_period,
+				     siu_stream->period_bytes);
+		count = siu_stream->period_bytes;
+
+		/* DMA transfer start */
+		siu_pcm_rd_set(port_info, buff, count);
+	} else {
+		siu_pcm_wr_set(port_info,
+			       (dma_addr_t)PERIOD_OFFSET(rt->dma_addr,
+						siu_stream->cur_period,
+						siu_stream->period_bytes),
+			       siu_stream->period_bytes);
+	}
+}
+
+/* Capture */
+static int siu_pcm_stmread_start(struct siu_port *port_info)
+{
+	struct siu_stream *siu_stream = &port_info->capture;
+
+	if (siu_stream->xfer_cnt > 0x1000000)
+		return -EINVAL;
+	if (siu_stream->rw_flg)
+		return -EPERM;
+
+	/* Current period in buffer */
+	siu_stream->cur_period = 0;
+
+	/* during stmread flag set */
+	siu_stream->rw_flg = RWF_STM_RD;
+
+	tasklet_schedule(&siu_stream->tasklet);
+
+	return 0;
+}
+
+static int siu_pcm_stmread_stop(struct siu_port *port_info)
+{
+	struct siu_info *info = siu_i2s_data;
+	u32 __iomem *base = info->reg;
+	struct siu_stream *siu_stream = &port_info->capture;
+	struct device *dev = siu_stream->substream->pcm->card->dev;
+	u32 stfifo;
+
+	if (!siu_stream->rw_flg)
+		return -EPERM;
+
+	/* input FIFO disable */
+	stfifo = siu_read32(base + SIU_STFIFO);
+	siu_write32(base + SIU_STFIFO, stfifo & ~0x13071307);
+	dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__,
+		stfifo, stfifo & ~0x13071307);
+
+	/* during stmread flag clear */
+	siu_stream->rw_flg = 0;
+
+	return 0;
+}
+
+static int siu_pcm_hw_params(struct snd_pcm_substream *ss,
+			     struct snd_pcm_hw_params *hw_params)
+{
+	struct siu_info *info = siu_i2s_data;
+	struct device *dev = ss->pcm->card->dev;
+	int ret;
+
+	dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id);
+
+	ret = snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params));
+	if (ret < 0)
+		dev_err(dev, "snd_pcm_lib_malloc_pages() failed\n");
+
+	return ret;
+}
+
+static int siu_pcm_hw_free(struct snd_pcm_substream *ss)
+{
+	struct siu_info *info = siu_i2s_data;
+	struct siu_port	*port_info = siu_port_info(ss);
+	struct device *dev = ss->pcm->card->dev;
+	struct siu_stream *siu_stream;
+
+	if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		siu_stream = &port_info->playback;
+	else
+		siu_stream = &port_info->capture;
+
+	dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id);
+
+	return snd_pcm_lib_free_pages(ss);
+}
+
+static bool filter(struct dma_chan *chan, void *slave)
+{
+	struct sh_dmae_slave *param = slave;
+
+	pr_debug("%s: slave ID %d\n", __func__, param->slave_id);
+
+	if (unlikely(param->dma_dev != chan->device->dev))
+		return false;
+
+	chan->private = param;
+	return true;
+}
+
+static int siu_pcm_open(struct snd_pcm_substream *ss)
+{
+	/* Playback / Capture */
+	struct snd_soc_pcm_runtime *rtd = ss->private_data;
+	struct siu_platform *pdata = rtd->platform->dev->platform_data;
+	struct siu_info *info = siu_i2s_data;
+	struct siu_port *port_info = siu_port_info(ss);
+	struct siu_stream *siu_stream;
+	u32 port = info->port_id;
+	struct device *dev = ss->pcm->card->dev;
+	dma_cap_mask_t mask;
+	struct sh_dmae_slave *param;
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	dev_dbg(dev, "%s, port=%d@%p\n", __func__, port, port_info);
+
+	if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		siu_stream = &port_info->playback;
+		param = &siu_stream->param;
+		param->slave_id = port ? pdata->dma_slave_tx_b :
+			pdata->dma_slave_tx_a;
+	} else {
+		siu_stream = &port_info->capture;
+		param = &siu_stream->param;
+		param->slave_id = port ? pdata->dma_slave_rx_b :
+			pdata->dma_slave_rx_a;
+	}
+
+	param->dma_dev = pdata->dma_dev;
+	/* Get DMA channel */
+	siu_stream->chan = dma_request_channel(mask, filter, param);
+	if (!siu_stream->chan) {
+		dev_err(dev, "DMA channel allocation failed!\n");
+		return -EBUSY;
+	}
+
+	siu_stream->substream = ss;
+
+	return 0;
+}
+
+static int siu_pcm_close(struct snd_pcm_substream *ss)
+{
+	struct siu_info *info = siu_i2s_data;
+	struct device *dev = ss->pcm->card->dev;
+	struct siu_port *port_info = siu_port_info(ss);
+	struct siu_stream *siu_stream;
+
+	dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id);
+
+	if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		siu_stream = &port_info->playback;
+	else
+		siu_stream = &port_info->capture;
+
+	dma_release_channel(siu_stream->chan);
+	siu_stream->chan = NULL;
+
+	siu_stream->substream = NULL;
+
+	return 0;
+}
+
+static int siu_pcm_prepare(struct snd_pcm_substream *ss)
+{
+	struct siu_info *info = siu_i2s_data;
+	struct siu_port *port_info = siu_port_info(ss);
+	struct device *dev = ss->pcm->card->dev;
+	struct snd_pcm_runtime 	*rt = ss->runtime;
+	struct siu_stream *siu_stream;
+	snd_pcm_sframes_t xfer_cnt;
+
+	if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		siu_stream = &port_info->playback;
+	else
+		siu_stream = &port_info->capture;
+
+	rt = siu_stream->substream->runtime;
+
+	siu_stream->buf_bytes = snd_pcm_lib_buffer_bytes(ss);
+	siu_stream->period_bytes = snd_pcm_lib_period_bytes(ss);
+
+	dev_dbg(dev, "%s: port=%d, %d channels, period=%u bytes\n", __func__,
+		info->port_id, rt->channels, siu_stream->period_bytes);
+
+	/* We only support buffers that are multiples of the period */
+	if (siu_stream->buf_bytes % siu_stream->period_bytes) {
+		dev_err(dev, "%s() - buffer=%d not multiple of period=%d\n",
+		       __func__, siu_stream->buf_bytes,
+		       siu_stream->period_bytes);
+		return -EINVAL;
+	}
+
+	xfer_cnt = bytes_to_frames(rt, siu_stream->period_bytes);
+	if (!xfer_cnt || xfer_cnt > 0x1000000)
+		return -EINVAL;
+
+	siu_stream->format = rt->format;
+	siu_stream->xfer_cnt = xfer_cnt;
+
+	dev_dbg(dev, "port=%d buf=%lx buf_bytes=%d period_bytes=%d "
+		"format=%d channels=%d xfer_cnt=%d\n", info->port_id,
+		(unsigned long)rt->dma_addr, siu_stream->buf_bytes,
+		siu_stream->period_bytes,
+		siu_stream->format, rt->channels, (int)xfer_cnt);
+
+	return 0;
+}
+
+static int siu_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
+{
+	struct siu_info *info = siu_i2s_data;
+	struct device *dev = ss->pcm->card->dev;
+	struct siu_port *port_info = siu_port_info(ss);
+	int ret;
+
+	dev_dbg(dev, "%s: port=%d@%p, cmd=%d\n", __func__,
+		info->port_id, port_info, cmd);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			ret = siu_pcm_stmwrite_start(port_info);
+		else
+			ret = siu_pcm_stmread_start(port_info);
+
+		if (ret < 0)
+			dev_warn(dev, "%s: start failed on port=%d\n",
+				 __func__, info->port_id);
+
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			siu_pcm_stmwrite_stop(port_info);
+		else
+			siu_pcm_stmread_stop(port_info);
+		ret = 0;
+
+		break;
+	default:
+		dev_err(dev, "%s() unsupported cmd=%d\n", __func__, cmd);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/*
+ * So far only resolution of one period is supported, subject to extending the
+ * dmangine API
+ */
+static snd_pcm_uframes_t siu_pcm_pointer_dma(struct snd_pcm_substream *ss)
+{
+	struct device *dev = ss->pcm->card->dev;
+	struct siu_info *info = siu_i2s_data;
+	u32 __iomem *base = info->reg;
+	struct siu_port *port_info = siu_port_info(ss);
+	struct snd_pcm_runtime *rt = ss->runtime;
+	size_t ptr;
+	struct siu_stream *siu_stream;
+
+	if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		siu_stream = &port_info->playback;
+	else
+		siu_stream = &port_info->capture;
+
+	/*
+	 * ptr is the offset into the buffer where the dma is currently at. We
+	 * check if the dma buffer has just wrapped.
+	 */
+	ptr = PERIOD_OFFSET(rt->dma_addr,
+			    siu_stream->cur_period,
+			    siu_stream->period_bytes) - rt->dma_addr;
+
+	dev_dbg(dev,
+		"%s: port=%d, events %x, FSTS %x, xferred %u/%u, cookie %d\n",
+		__func__, info->port_id, siu_read32(base + SIU_EVNTC),
+		siu_read32(base + SIU_SBFSTS), ptr, siu_stream->buf_bytes,
+		siu_stream->cookie);
+
+	if (ptr >= siu_stream->buf_bytes)
+		ptr = 0;
+
+	return bytes_to_frames(ss->runtime, ptr);
+}
+
+static int siu_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	/* card->dev == socdev->dev, see snd_soc_new_pcms() */
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_pcm *pcm = rtd->pcm;
+	struct siu_info *info = siu_i2s_data;
+	struct platform_device *pdev = to_platform_device(card->dev);
+	int ret;
+	int i;
+
+	/* pdev->id selects between SIUA and SIUB */
+	if (pdev->id < 0 || pdev->id >= SIU_PORT_NUM)
+		return -EINVAL;
+
+	info->port_id = pdev->id;
+
+	/*
+	 * While the siu has 2 ports, only one port can be on at a time (only 1
+	 * SPB). So far all the boards using the siu had only one of the ports
+	 * wired to a codec. To simplify things, we only register one port with
+	 * alsa. In case both ports are needed, it should be changed here
+	 */
+	for (i = pdev->id; i < pdev->id + 1; i++) {
+		struct siu_port **port_info = &siu_ports[i];
+
+		ret = siu_init_port(i, port_info, card);
+		if (ret < 0)
+			return ret;
+
+		ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
+				SNDRV_DMA_TYPE_DEV, NULL,
+				SIU_BUFFER_BYTES_MAX, SIU_BUFFER_BYTES_MAX);
+		if (ret < 0) {
+			dev_err(card->dev,
+			       "snd_pcm_lib_preallocate_pages_for_all() err=%d",
+				ret);
+			goto fail;
+		}
+
+		(*port_info)->pcm = pcm;
+
+		/* IO tasklets */
+		tasklet_init(&(*port_info)->playback.tasklet, siu_io_tasklet,
+			     (unsigned long)&(*port_info)->playback);
+		tasklet_init(&(*port_info)->capture.tasklet, siu_io_tasklet,
+			     (unsigned long)&(*port_info)->capture);
+	}
+
+	dev_info(card->dev, "SuperH SIU driver initialized.\n");
+	return 0;
+
+fail:
+	siu_free_port(siu_ports[pdev->id]);
+	dev_err(card->dev, "SIU: failed to initialize.\n");
+	return ret;
+}
+
+static void siu_pcm_free(struct snd_pcm *pcm)
+{
+	struct platform_device *pdev = to_platform_device(pcm->card->dev);
+	struct siu_port *port_info = siu_ports[pdev->id];
+
+	tasklet_kill(&port_info->capture.tasklet);
+	tasklet_kill(&port_info->playback.tasklet);
+
+	siu_free_port(port_info);
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+
+	dev_dbg(pcm->card->dev, "%s\n", __func__);
+}
+
+static struct snd_pcm_ops siu_pcm_ops = {
+	.open		= siu_pcm_open,
+	.close		= siu_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= siu_pcm_hw_params,
+	.hw_free	= siu_pcm_hw_free,
+	.prepare	= siu_pcm_prepare,
+	.trigger	= siu_pcm_trigger,
+	.pointer	= siu_pcm_pointer_dma,
+};
+
+struct snd_soc_platform_driver siu_platform = {
+	.ops			= &siu_pcm_ops,
+	.pcm_new	= siu_pcm_new,
+	.pcm_free	= siu_pcm_free,
+};
+EXPORT_SYMBOL_GPL(siu_platform);
diff --git a/ap/os/linux/linux-3.4.x/sound/soc/sh/ssi.c b/ap/os/linux/linux-3.4.x/sound/soc/sh/ssi.c
new file mode 100644
index 0000000..ff82b56
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/sound/soc/sh/ssi.c
@@ -0,0 +1,408 @@
+/*
+ * Serial Sound Interface (I2S) support for SH7760/SH7780
+ *
+ * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ *  licensed under the terms outlined in the file COPYING at the root
+ *  of the linux kernel sources.
+ *
+ * dont forget to set IPSEL/OMSEL register bits (in your board code) to
+ * enable SSI output pins!
+ */
+
+/*
+ * LIMITATIONS:
+ *	The SSI unit has only one physical data line, so full duplex is
+ *	impossible.  This can be remedied  on the  SH7760 by  using the
+ *	other SSI unit for recording; however the SH7780 has only 1 SSI
+ *	unit, and its pins are shared with the AC97 unit,  among others.
+ *
+ * FEATURES:
+ *	The SSI features "compressed mode": in this mode it continuously
+ *	streams PCM data over the I2S lines and uses LRCK as a handshake
+ *	signal.  Can be used to send compressed data (AC3/DTS) to a DSP.
+ *	The number of bits sent over the wire in a frame can be adjusted
+ *	and can be independent from the actual sample bit depth. This is
+ *	useful to support TDM mode codecs like the AD1939 which have a
+ *	fixed TDM slot size, regardless of sample resolution.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <asm/io.h>
+
+#define SSICR	0x00
+#define SSISR	0x04
+
+#define CR_DMAEN	(1 << 28)
+#define CR_CHNL_SHIFT	22
+#define CR_CHNL_MASK	(3 << CR_CHNL_SHIFT)
+#define CR_DWL_SHIFT	19
+#define CR_DWL_MASK	(7 << CR_DWL_SHIFT)
+#define CR_SWL_SHIFT	16
+#define CR_SWL_MASK	(7 << CR_SWL_SHIFT)
+#define CR_SCK_MASTER	(1 << 15)	/* bitclock master bit */
+#define CR_SWS_MASTER	(1 << 14)	/* wordselect master bit */
+#define CR_SCKP		(1 << 13)	/* I2Sclock polarity */
+#define CR_SWSP		(1 << 12)	/* LRCK polarity */
+#define CR_SPDP		(1 << 11)
+#define CR_SDTA		(1 << 10)	/* i2s alignment (msb/lsb) */
+#define CR_PDTA		(1 << 9)	/* fifo data alignment */
+#define CR_DEL		(1 << 8)	/* delay data by 1 i2sclk */
+#define CR_BREN		(1 << 7)	/* clock gating in burst mode */
+#define CR_CKDIV_SHIFT	4
+#define CR_CKDIV_MASK	(7 << CR_CKDIV_SHIFT)	/* bitclock divider */
+#define CR_MUTE		(1 << 3)	/* SSI mute */
+#define CR_CPEN		(1 << 2)	/* compressed mode */
+#define CR_TRMD		(1 << 1)	/* transmit/receive select */
+#define CR_EN		(1 << 0)	/* enable SSI */
+
+#define SSIREG(reg)	(*(unsigned long *)(ssi->mmio + (reg)))
+
+struct ssi_priv {
+	unsigned long mmio;
+	unsigned long sysclk;
+	int inuse;
+} ssi_cpu_data[] = {
+#if defined(CONFIG_CPU_SUBTYPE_SH7760)
+	{
+		.mmio	= 0xFE680000,
+	},
+	{
+		.mmio	= 0xFE690000,
+	},
+#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
+	{
+		.mmio	= 0xFFE70000,
+	},
+#else
+#error "Unsupported SuperH SoC"
+#endif
+};
+
+/*
+ * track usage of the SSI; it is simplex-only so prevent attempts of
+ * concurrent playback + capture. FIXME: any locking required?
+ */
+static int ssi_startup(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai)
+{
+	struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
+	if (ssi->inuse) {
+		pr_debug("ssi: already in use!\n");
+		return -EBUSY;
+	} else
+		ssi->inuse = 1;
+	return 0;
+}
+
+static void ssi_shutdown(struct snd_pcm_substream *substream,
+			 struct snd_soc_dai *dai)
+{
+	struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
+
+	ssi->inuse = 0;
+}
+
+static int ssi_trigger(struct snd_pcm_substream *substream, int cmd,
+		       struct snd_soc_dai *dai)
+{
+	struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		SSIREG(SSICR) |= CR_DMAEN | CR_EN;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		SSIREG(SSICR) &= ~(CR_DMAEN | CR_EN);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ssi_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params,
+			 struct snd_soc_dai *dai)
+{
+	struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
+	unsigned long ssicr = SSIREG(SSICR);
+	unsigned int bits, channels, swl, recv, i;
+
+	channels = params_channels(params);
+	bits = params->msbits;
+	recv = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1;
+
+	pr_debug("ssi_hw_params() enter\nssicr was    %08lx\n", ssicr);
+	pr_debug("bits: %u channels: %u\n", bits, channels);
+
+	ssicr &= ~(CR_TRMD | CR_CHNL_MASK | CR_DWL_MASK | CR_PDTA |
+		   CR_SWL_MASK);
+
+	/* direction (send/receive) */
+	if (!recv)
+		ssicr |= CR_TRMD;	/* transmit */
+
+	/* channels */
+	if ((channels < 2) || (channels > 8) || (channels & 1)) {
+		pr_debug("ssi: invalid number of channels\n");
+		return -EINVAL;
+	}
+	ssicr |= ((channels >> 1) - 1) << CR_CHNL_SHIFT;
+
+	/* DATA WORD LENGTH (DWL): databits in audio sample */
+	i = 0;
+	switch (bits) {
+	case 32: ++i;
+	case 24: ++i;
+	case 22: ++i;
+	case 20: ++i;
+	case 18: ++i;
+	case 16: ++i;
+		 ssicr |= i << CR_DWL_SHIFT;
+	case 8:	 break;
+	default:
+		pr_debug("ssi: invalid sample width\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * SYSTEM WORD LENGTH: size in bits of half a frame over the I2S
+	 * wires. This is usually bits_per_sample x channels/2;  i.e. in
+	 * Stereo mode  the SWL equals DWL.  SWL can  be bigger than the
+	 * product of (channels_per_slot x samplebits), e.g.  for codecs
+	 * like the AD1939 which  only accept 32bit wide TDM slots.  For
+	 * "standard" I2S operation we set SWL = chans / 2 * DWL here.
+	 * Waiting for ASoC to get TDM support ;-)
+	 */
+	if ((bits > 16) && (bits <= 24)) {
+		bits = 24;	/* these are padded by the SSI */
+		/*ssicr |= CR_PDTA;*/ /* cpu/data endianness ? */
+	}
+	i = 0;
+	swl = (bits * channels) / 2;
+	switch (swl) {
+	case 256: ++i;
+	case 128: ++i;
+	case 64:  ++i;
+	case 48:  ++i;
+	case 32:  ++i;
+	case 16:  ++i;
+		  ssicr |= i << CR_SWL_SHIFT;
+	case 8:   break;
+	default:
+		pr_debug("ssi: invalid system word length computed\n");
+		return -EINVAL;
+	}
+
+	SSIREG(SSICR) = ssicr;
+
+	pr_debug("ssi_hw_params() leave\nssicr is now %08lx\n", ssicr);
+	return 0;
+}
+
+static int ssi_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
+			  unsigned int freq, int dir)
+{
+	struct ssi_priv *ssi = &ssi_cpu_data[cpu_dai->id];
+
+	ssi->sysclk = freq;
+
+	return 0;
+}
+
+/*
+ * This divider is used to generate the SSI_SCK (I2S bitclock) from the
+ * clock at the HAC_BIT_CLK ("oversampling clock") pin.
+ */
+static int ssi_set_clkdiv(struct snd_soc_dai *dai, int did, int div)
+{
+	struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
+	unsigned long ssicr;
+	int i;
+
+	i = 0;
+	ssicr = SSIREG(SSICR) & ~CR_CKDIV_MASK;
+	switch (div) {
+	case 16: ++i;
+	case 8:  ++i;
+	case 4:  ++i;
+	case 2:  ++i;
+		 SSIREG(SSICR) = ssicr | (i << CR_CKDIV_SHIFT);
+	case 1:  break;
+	default:
+		pr_debug("ssi: invalid sck divider %d\n", div);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ssi_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
+	unsigned long ssicr = SSIREG(SSICR);
+
+	pr_debug("ssi_set_fmt()\nssicr was    0x%08lx\n", ssicr);
+
+	ssicr &= ~(CR_DEL | CR_PDTA | CR_BREN | CR_SWSP | CR_SCKP |
+		   CR_SWS_MASTER | CR_SCK_MASTER);
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		ssicr |= CR_DEL | CR_PDTA;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		ssicr |= CR_DEL;
+		break;
+	default:
+		pr_debug("ssi: unsupported format\n");
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
+	case SND_SOC_DAIFMT_CONT:
+		break;
+	case SND_SOC_DAIFMT_GATED:
+		ssicr |= CR_BREN;
+		break;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		ssicr |= CR_SCKP;	/* sample data at low clkedge */
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		ssicr |= CR_SCKP | CR_SWSP;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		ssicr |= CR_SWSP;	/* word select starts low */
+		break;
+	default:
+		pr_debug("ssi: invalid inversion\n");
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		break;
+	case SND_SOC_DAIFMT_CBS_CFM:
+		ssicr |= CR_SCK_MASTER;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		ssicr |= CR_SWS_MASTER;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		ssicr |= CR_SWS_MASTER | CR_SCK_MASTER;
+		break;
+	default:
+		pr_debug("ssi: invalid master/slave configuration\n");
+		return -EINVAL;
+	}
+
+	SSIREG(SSICR) = ssicr;
+	pr_debug("ssi_set_fmt() leave\nssicr is now 0x%08lx\n", ssicr);
+
+	return 0;
+}
+
+/* the SSI depends on an external clocksource (at HAC_BIT_CLK) even in
+ * Master mode,  so really this is board specific;  the SSI can do any
+ * rate with the right bitclk and divider settings.
+ */
+#define SSI_RATES	\
+	SNDRV_PCM_RATE_8000_192000
+
+/* the SSI can do 8-32 bit samples, with 8 possible channels */
+#define SSI_FMTS	\
+	(SNDRV_PCM_FMTBIT_S8      | SNDRV_PCM_FMTBIT_U8      |	\
+	 SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_U16_LE  |	\
+	 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE |	\
+	 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE |	\
+	 SNDRV_PCM_FMTBIT_S32_LE  | SNDRV_PCM_FMTBIT_U32_LE)
+
+static const struct snd_soc_dai_ops ssi_dai_ops = {
+	.startup	= ssi_startup,
+	.shutdown	= ssi_shutdown,
+	.trigger	= ssi_trigger,
+	.hw_params	= ssi_hw_params,
+	.set_sysclk	= ssi_set_sysclk,
+	.set_clkdiv	= ssi_set_clkdiv,
+	.set_fmt	= ssi_set_fmt,
+};
+
+static struct snd_soc_dai_driver sh4_ssi_dai[] = {
+{
+	.name			= "ssi-dai.0",
+	.playback = {
+		.rates		= SSI_RATES,
+		.formats	= SSI_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 8,
+	},
+	.capture = {
+		.rates		= SSI_RATES,
+		.formats	= SSI_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 8,
+	},
+	.ops = &ssi_dai_ops,
+},
+#ifdef CONFIG_CPU_SUBTYPE_SH7760
+{
+	.name			= "ssi-dai.1",
+	.playback = {
+		.rates		= SSI_RATES,
+		.formats	= SSI_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 8,
+	},
+	.capture = {
+		.rates		= SSI_RATES,
+		.formats	= SSI_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 8,
+	},
+	.ops = &ssi_dai_ops,
+},
+#endif
+};
+
+static int __devinit sh4_soc_dai_probe(struct platform_device *pdev)
+{
+	return snd_soc_register_dais(&pdev->dev, sh4_ssi_dai,
+			ARRAY_SIZE(sh4_ssi_dai));
+}
+
+static int __devexit sh4_soc_dai_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(sh4_ssi_dai));
+	return 0;
+}
+
+static struct platform_driver sh4_ssi_driver = {
+	.driver = {
+			.name = "sh4-ssi-dai",
+			.owner = THIS_MODULE,
+	},
+
+	.probe = sh4_soc_dai_probe,
+	.remove = __devexit_p(sh4_soc_dai_remove),
+};
+
+module_platform_driver(sh4_ssi_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SuperH onchip SSI (I2S) audio driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");