v4.19.13 snapshot.
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
new file mode 100644
index 0000000..dc20f0f
--- /dev/null
+++ b/sound/soc/sh/Kconfig
@@ -0,0 +1,68 @@
+# SPDX-License-Identifier: GPL-2.0
+menu "SoC Audio support for Renesas SoCs"
+	depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
+
+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"
+	select SND_SIMPLE_CARD
+	help
+	  This option enables FSI sound support
+
+config SND_SOC_SH4_SIU
+	tristate
+	depends on ARCH_SHMOBILE && HAVE_CLK
+	select DMA_ENGINE
+	select DMADEVICES
+	select SH_DMAE
+	select FW_LOADER
+
+config SND_SOC_RCAR
+	tristate "R-Car series SRU/SCU/SSIU/SSI support"
+	depends on COMMON_CLK
+	depends on OF || COMPILE_TEST
+	select SND_SIMPLE_CARD_UTILS
+	select REGMAP_MMIO
+	help
+	  This option enables R-Car SRU/SCU/SSIU/SSI sound support
+
+##
+## 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_SIU_MIGOR
+	tristate "SIU sound support on Migo-R"
+	depends on SH_MIGOR && I2C
+	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/sound/soc/sh/Makefile b/sound/soc/sh/Makefile
new file mode 100644
index 0000000..51bd7c8
--- /dev/null
+++ b/sound/soc/sh/Makefile
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0
+## 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
+
+## audio units for R-Car
+obj-$(CONFIG_SND_SOC_RCAR)	+= rcar/
+
+## boards
+snd-soc-sh7760-ac97-objs	:= sh7760-ac97.o
+snd-soc-migor-objs		:= migor.o
+
+obj-$(CONFIG_SND_SH7760_AC97)	+= snd-soc-sh7760-ac97.o
+obj-$(CONFIG_SND_SIU_MIGOR)	+= snd-soc-migor.o
diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c
new file mode 100644
index 0000000..922fb6a
--- /dev/null
+++ b/sound/soc/sh/dma-sh7760.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// SH7760 ("camelot") DMABRG audio DMA unit support
+//
+// Copyright (C) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
+//
+// 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
+
+static const 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),
+	.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 const 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 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 const struct snd_soc_component_driver sh7760_soc_component = {
+	.ops		= &camelot_pcm_ops,
+	.pcm_new	= camelot_pcm_new,
+};
+
+static int sh7760_soc_platform_probe(struct platform_device *pdev)
+{
+	return devm_snd_soc_register_component(&pdev->dev, &sh7760_soc_component,
+					       NULL, 0);
+}
+
+static struct platform_driver sh7760_pcm_driver = {
+	.driver = {
+			.name = "sh7760-pcm-audio",
+	},
+
+	.probe = sh7760_soc_platform_probe,
+};
+
+module_platform_driver(sh7760_pcm_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
new file mode 100644
index 0000000..aa7e902
--- /dev/null
+++ b/sound/soc/sh/fsi.c
@@ -0,0 +1,2108 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// 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>
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.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/pcm_params.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)
+
+/*
+ * bus options
+ *
+ * 0x000000BA
+ *
+ * A : sample widtht 16bit setting
+ * B : sample widtht 24bit setting
+ */
+
+#define SHIFT_16DATA		0
+#define SHIFT_24DATA		4
+
+#define PACKAGE_24BITBUS_BACK		0
+#define PACKAGE_24BITBUS_FRONT		1
+#define PACKAGE_16BITBUS_STREAM		2
+
+#define BUSOP_SET(s, a)	((a) << SHIFT_ ## s ## DATA)
+#define BUSOP_GET(s, a)	(((a) >> SHIFT_ ## s ## DATA) & 0xF)
+
+/*
+ * 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
+ */
+
+/*
+ *	FSI clock
+ *
+ * FSIxCLK [CPG] (ick) ------->	|
+ *				|-> FSI_DIV (div)-> FSI2
+ * FSIxCK [external] (xck) --->	|
+ */
+
+/*
+ *		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;
+
+	/*
+	 * bus options
+	 */
+	u32 bus_option;
+
+	/*
+	 * thse are initialized by fsi_handler_init()
+	 */
+	struct fsi_stream_handler *handler;
+	struct fsi_priv		*priv;
+
+	/*
+	 * these are for DMAEngine
+	 */
+	struct dma_chan		*chan;
+	int			dma_id;
+};
+
+struct fsi_clk {
+	/* see [FSI clock] */
+	struct clk *own;
+	struct clk *xck;
+	struct clk *ick;
+	struct clk *div;
+	int (*set_rate)(struct device *dev,
+			struct fsi_priv *fsi);
+
+	unsigned long rate;
+	unsigned int count;
+};
+
+struct fsi_priv {
+	void __iomem *base;
+	phys_addr_t phys;
+	struct fsi_master *master;
+
+	struct fsi_stream playback;
+	struct fsi_stream capture;
+
+	struct fsi_clk clock;
+
+	u32 fmt;
+
+	int chan_num:16;
+	unsigned int clk_master:1;
+	unsigned int clk_cpg:1;
+	unsigned int spdif:1;
+	unsigned int enable_stream:1;
+	unsigned int bit_clk_inv:1;
+	unsigned int lr_clk_inv:1;
+};
+
+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, struct device *dev);
+	int (*transfer)(struct fsi_priv *fsi, struct fsi_stream *io);
+	int (*remove)(struct fsi_priv *fsi, struct fsi_stream *io);
+	int (*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;
+	struct fsi_priv fsia;
+	struct fsi_priv fsib;
+	const struct fsi_core *core;
+	spinlock_t lock;
+};
+
+static inline int fsi_stream_is_play(struct fsi_priv *fsi,
+				     struct fsi_stream *io)
+{
+	return &fsi->playback == 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 int fsi_version(struct fsi_master *master)
+{
+	return master->core->ver;
+}
+
+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_enable_stream(struct fsi_priv *fsi)
+{
+	return fsi->enable_stream;
+}
+
+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 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 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->bus_option		= 0;
+	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->bus_option		= 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 device *dev)
+{
+	struct fsi_stream *io;
+	int ret1, ret2;
+
+	io = &fsi->playback;
+	ret1 = fsi_stream_handler_call(io, probe, fsi, io, dev);
+
+	io = &fsi->capture;
+	ret2 = fsi_stream_handler_call(io, probe, fsi, io, dev);
+
+	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;
+}
+
+/*
+ *	format/bus/dma setting
+ */
+static void fsi_format_bus_setup(struct fsi_priv *fsi, struct fsi_stream *io,
+				 u32 bus, struct device *dev)
+{
+	struct fsi_master *master = fsi_get_master(fsi);
+	int is_play = fsi_stream_is_play(fsi, io);
+	u32 fmt = fsi->fmt;
+
+	if (fsi_version(master) >= 2) {
+		u32 dma = 0;
+
+		/*
+		 * FSI2 needs DMA/Bus setting
+		 */
+		switch (bus) {
+		case PACKAGE_24BITBUS_FRONT:
+			fmt |= CR_BWS_24;
+			dma |= VDMD_FRONT;
+			dev_dbg(dev, "24bit bus / package in front\n");
+			break;
+		case PACKAGE_16BITBUS_STREAM:
+			fmt |= CR_BWS_16;
+			dma |= VDMD_STREAM;
+			dev_dbg(dev, "16bit bus / stream mode\n");
+			break;
+		case PACKAGE_24BITBUS_BACK:
+		default:
+			fmt |= CR_BWS_24;
+			dma |= VDMD_BACK;
+			dev_dbg(dev, "24bit bus / package in back\n");
+			break;
+		}
+
+		if (is_play)
+			fsi_reg_write(fsi, OUT_DMAC,	dma);
+		else
+			fsi_reg_write(fsi, IN_DMAC,	dma);
+	}
+
+	if (is_play)
+		fsi_reg_write(fsi, DO_FMT, fmt);
+	else
+		fsi_reg_write(fsi, DI_FMT, fmt);
+}
+
+/*
+ *		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;
+
+	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_clk_init(struct device *dev,
+			struct fsi_priv *fsi,
+			int xck,
+			int ick,
+			int div,
+			int (*set_rate)(struct device *dev,
+					struct fsi_priv *fsi))
+{
+	struct fsi_clk *clock = &fsi->clock;
+	int is_porta = fsi_is_port_a(fsi);
+
+	clock->xck	= NULL;
+	clock->ick	= NULL;
+	clock->div	= NULL;
+	clock->rate	= 0;
+	clock->count	= 0;
+	clock->set_rate	= set_rate;
+
+	clock->own = devm_clk_get(dev, NULL);
+	if (IS_ERR(clock->own))
+		return -EINVAL;
+
+	/* external clock */
+	if (xck) {
+		clock->xck = devm_clk_get(dev, is_porta ? "xcka" : "xckb");
+		if (IS_ERR(clock->xck)) {
+			dev_err(dev, "can't get xck clock\n");
+			return -EINVAL;
+		}
+		if (clock->xck == clock->own) {
+			dev_err(dev, "cpu doesn't support xck clock\n");
+			return -EINVAL;
+		}
+	}
+
+	/* FSIACLK/FSIBCLK */
+	if (ick) {
+		clock->ick = devm_clk_get(dev,  is_porta ? "icka" : "ickb");
+		if (IS_ERR(clock->ick)) {
+			dev_err(dev, "can't get ick clock\n");
+			return -EINVAL;
+		}
+		if (clock->ick == clock->own) {
+			dev_err(dev, "cpu doesn't support ick clock\n");
+			return -EINVAL;
+		}
+	}
+
+	/* FSI-DIV */
+	if (div) {
+		clock->div = devm_clk_get(dev,  is_porta ? "diva" : "divb");
+		if (IS_ERR(clock->div)) {
+			dev_err(dev, "can't get div clock\n");
+			return -EINVAL;
+		}
+		if (clock->div == clock->own) {
+			dev_err(dev, "cpu doens't support div clock\n");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+#define fsi_clk_invalid(fsi) fsi_clk_valid(fsi, 0)
+static void fsi_clk_valid(struct fsi_priv *fsi, unsigned long rate)
+{
+	fsi->clock.rate = rate;
+}
+
+static int fsi_clk_is_valid(struct fsi_priv *fsi)
+{
+	return	fsi->clock.set_rate &&
+		fsi->clock.rate;
+}
+
+static int fsi_clk_enable(struct device *dev,
+			  struct fsi_priv *fsi)
+{
+	struct fsi_clk *clock = &fsi->clock;
+	int ret = -EINVAL;
+
+	if (!fsi_clk_is_valid(fsi))
+		return ret;
+
+	if (0 == clock->count) {
+		ret = clock->set_rate(dev, fsi);
+		if (ret < 0) {
+			fsi_clk_invalid(fsi);
+			return ret;
+		}
+
+		clk_enable(clock->xck);
+		clk_enable(clock->ick);
+		clk_enable(clock->div);
+
+		clock->count++;
+	}
+
+	return ret;
+}
+
+static int fsi_clk_disable(struct device *dev,
+			    struct fsi_priv *fsi)
+{
+	struct fsi_clk *clock = &fsi->clock;
+
+	if (!fsi_clk_is_valid(fsi))
+		return -EINVAL;
+
+	if (1 == clock->count--) {
+		clk_disable(clock->xck);
+		clk_disable(clock->ick);
+		clk_disable(clock->div);
+	}
+
+	return 0;
+}
+
+static int fsi_clk_set_ackbpf(struct device *dev,
+			      struct fsi_priv *fsi,
+			      int ackmd, int bpfmd)
+{
+	u32 data = 0;
+
+	/* check ackmd/bpfmd relationship */
+	if (bpfmd > ackmd) {
+		dev_err(dev, "unsupported rate (%d/%d)\n", ackmd, bpfmd);
+		return -EINVAL;
+	}
+
+	/*  ACKMD */
+	switch (ackmd) {
+	case 512:
+		data |= (0x0 << 12);
+		break;
+	case 256:
+		data |= (0x1 << 12);
+		break;
+	case 128:
+		data |= (0x2 << 12);
+		break;
+	case 64:
+		data |= (0x3 << 12);
+		break;
+	case 32:
+		data |= (0x4 << 12);
+		break;
+	default:
+		dev_err(dev, "unsupported ackmd (%d)\n", ackmd);
+		return -EINVAL;
+	}
+
+	/* BPFMD */
+	switch (bpfmd) {
+	case 32:
+		data |= (0x0 << 8);
+		break;
+	case 64:
+		data |= (0x1 << 8);
+		break;
+	case 128:
+		data |= (0x2 << 8);
+		break;
+	case 256:
+		data |= (0x3 << 8);
+		break;
+	case 512:
+		data |= (0x4 << 8);
+		break;
+	case 16:
+		data |= (0x7 << 8);
+		break;
+	default:
+		dev_err(dev, "unsupported bpfmd (%d)\n", bpfmd);
+		return -EINVAL;
+	}
+
+	dev_dbg(dev, "ACKMD/BPFMD = %d/%d\n", ackmd, bpfmd);
+
+	fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
+	udelay(10);
+
+	return 0;
+}
+
+static int fsi_clk_set_rate_external(struct device *dev,
+				     struct fsi_priv *fsi)
+{
+	struct clk *xck = fsi->clock.xck;
+	struct clk *ick = fsi->clock.ick;
+	unsigned long rate = fsi->clock.rate;
+	unsigned long xrate;
+	int ackmd, bpfmd;
+	int ret = 0;
+
+	/* check clock rate */
+	xrate = clk_get_rate(xck);
+	if (xrate % rate) {
+		dev_err(dev, "unsupported clock rate\n");
+		return -EINVAL;
+	}
+
+	clk_set_parent(ick, xck);
+	clk_set_rate(ick, xrate);
+
+	bpfmd = fsi->chan_num * 32;
+	ackmd = xrate / rate;
+
+	dev_dbg(dev, "external/rate = %ld/%ld\n", xrate, rate);
+
+	ret = fsi_clk_set_ackbpf(dev, fsi, ackmd, bpfmd);
+	if (ret < 0)
+		dev_err(dev, "%s failed", __func__);
+
+	return ret;
+}
+
+static int fsi_clk_set_rate_cpg(struct device *dev,
+				struct fsi_priv *fsi)
+{
+	struct clk *ick = fsi->clock.ick;
+	struct clk *div = fsi->clock.div;
+	unsigned long rate = fsi->clock.rate;
+	unsigned long target = 0; /* 12288000 or 11289600 */
+	unsigned long actual, cout;
+	unsigned long diff, min;
+	unsigned long best_cout, best_act;
+	int adj;
+	int ackmd, bpfmd;
+	int ret = -EINVAL;
+
+	if (!(12288000 % rate))
+		target = 12288000;
+	if (!(11289600 % rate))
+		target = 11289600;
+	if (!target) {
+		dev_err(dev, "unsupported rate\n");
+		return ret;
+	}
+
+	bpfmd = fsi->chan_num * 32;
+	ackmd = target / rate;
+	ret = fsi_clk_set_ackbpf(dev, fsi, ackmd, bpfmd);
+	if (ret < 0) {
+		dev_err(dev, "%s failed", __func__);
+		return ret;
+	}
+
+	/*
+	 * The clock flow is
+	 *
+	 * [CPG] = cout => [FSI_DIV] = audio => [FSI] => [codec]
+	 *
+	 * But, it needs to find best match of CPG and FSI_DIV
+	 * combination, since it is difficult to generate correct
+	 * frequency of audio clock from ick clock only.
+	 * Because ick is created from its parent clock.
+	 *
+	 * target	= rate x [512/256/128/64]fs
+	 * cout		= round(target x adjustment)
+	 * actual	= cout / adjustment (by FSI-DIV) ~= target
+	 * audio	= actual
+	 */
+	min = ~0;
+	best_cout = 0;
+	best_act = 0;
+	for (adj = 1; adj < 0xffff; adj++) {
+
+		cout = target * adj;
+		if (cout > 100000000) /* max clock = 100MHz */
+			break;
+
+		/* cout/actual audio clock */
+		cout	= clk_round_rate(ick, cout);
+		actual	= cout / adj;
+
+		/* find best frequency */
+		diff = abs(actual - target);
+		if (diff < min) {
+			min		= diff;
+			best_cout	= cout;
+			best_act	= actual;
+		}
+	}
+
+	ret = clk_set_rate(ick, best_cout);
+	if (ret < 0) {
+		dev_err(dev, "ick clock failed\n");
+		return -EIO;
+	}
+
+	ret = clk_set_rate(div, clk_round_rate(div, best_act));
+	if (ret < 0) {
+		dev_err(dev, "div clock failed\n");
+		return -EIO;
+	}
+
+	dev_dbg(dev, "ick/div = %ld/%ld\n",
+		clk_get_rate(ick), clk_get_rate(div));
+
+	return ret;
+}
+
+static void fsi_pointer_update(struct fsi_stream *io, int size)
+{
+	io->buff_sample_pos += size;
+
+	if (io->buff_sample_pos >=
+	    io->period_samples * (io->period_pos + 1)) {
+		struct snd_pcm_substream *substream = io->substream;
+		struct snd_pcm_runtime *runtime = substream->runtime;
+
+		io->period_pos++;
+
+		if (io->period_pos >= runtime->periods) {
+			io->buff_sample_pos = 0;
+			io->period_pos = 0;
+		}
+
+		snd_pcm_period_elapsed(substream);
+	}
+}
+
+/*
+ *		pio data transfer handler
+ */
+static void fsi_pio_push16(struct fsi_priv *fsi, u8 *_buf, int samples)
+{
+	int i;
+
+	if (fsi_is_enable_stream(fsi)) {
+		/*
+		 * stream mode
+		 * see
+		 *	fsi_pio_push_init()
+		 */
+		u32 *buf = (u32 *)_buf;
+
+		for (i = 0; i < samples / 2; i++)
+			fsi_reg_write(fsi, DODT, buf[i]);
+	} else {
+		/* normal mode */
+		u16 *buf = (u16 *)_buf;
+
+		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)
+{
+	u8 *buf;
+
+	if (!fsi_stream_is_working(fsi, io))
+		return -EINVAL;
+
+	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;
+	}
+
+	fsi_pointer_update(io, samples);
+
+	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 int 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);
+
+	return 0;
+}
+
+static int fsi_pio_push_init(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	/*
+	 * we can use 16bit stream mode
+	 * when "playback" and "16bit data"
+	 * and platform allows "stream mode"
+	 * see
+	 *	fsi_pio_push16()
+	 */
+	if (fsi_is_enable_stream(fsi))
+		io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
+				 BUSOP_SET(16, PACKAGE_16BITBUS_STREAM);
+	else
+		io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
+				 BUSOP_SET(16, PACKAGE_24BITBUS_BACK);
+	return 0;
+}
+
+static int fsi_pio_pop_init(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	/*
+	 * always 24bit bus, package back when "capture"
+	 */
+	io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
+			 BUSOP_SET(16, PACKAGE_24BITBUS_BACK);
+	return 0;
+}
+
+static struct fsi_stream_handler fsi_pio_push_handler = {
+	.init		= fsi_pio_push_init,
+	.transfer	= fsi_pio_push,
+	.start_stop	= fsi_pio_start_stop,
+};
+
+static struct fsi_stream_handler fsi_pio_pop_handler = {
+	.init		= fsi_pio_pop_init,
+	.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)
+{
+	/*
+	 * 24bit data : 24bit bus / package in back
+	 * 16bit data : 16bit bus / stream mode
+	 */
+	io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
+			 BUSOP_SET(16, PACKAGE_16BITBUS_STREAM);
+
+	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);
+
+	fsi_pointer_update(io, io->period_samples);
+
+	fsi_count_fifo_err(fsi);
+}
+
+static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	struct snd_soc_dai *dai = fsi_get_dai(io->substream);
+	struct snd_pcm_substream *substream = io->substream;
+	struct dma_async_tx_descriptor *desc;
+	int is_play = fsi_stream_is_play(fsi, io);
+	enum dma_transfer_direction dir;
+	int ret = -EIO;
+
+	if (is_play)
+		dir = DMA_MEM_TO_DEV;
+	else
+		dir = DMA_DEV_TO_MEM;
+
+	desc = dmaengine_prep_dma_cyclic(io->chan,
+					 substream->runtime->dma_addr,
+					 snd_pcm_lib_buffer_bytes(substream),
+					 snd_pcm_lib_period_bytes(substream),
+					 dir,
+					 DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(dai->dev, "dmaengine_prep_dma_cyclic() fail\n");
+		goto fsi_dma_transfer_err;
+	}
+
+	desc->callback		= fsi_dma_complete;
+	desc->callback_param	= io;
+
+	if (dmaengine_submit(desc) < 0) {
+		dev_err(dai->dev, "tx_submit() fail\n");
+		goto fsi_dma_transfer_err;
+	}
+
+	dma_async_issue_pending(io->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);
+		}
+	}
+
+	ret = 0;
+
+fsi_dma_transfer_err:
+	return ret;
+}
+
+static int fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
+				 int start)
+{
+	struct fsi_master *master = fsi_get_master(fsi);
+	u32 clk  = fsi_is_port_a(fsi) ? CRA  : CRB;
+	u32 enable = start ? DMA_ON : 0;
+
+	fsi_reg_mask_set(fsi, OUT_DMAC, DMA_ON, enable);
+
+	dmaengine_terminate_all(io->chan);
+
+	if (fsi_is_clk_master(fsi))
+		fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
+
+	return 0;
+}
+
+static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev)
+{
+	int is_play = fsi_stream_is_play(fsi, io);
+
+#ifdef CONFIG_SUPERH
+	dma_cap_mask_t mask;
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	io->chan = dma_request_channel(mask, shdma_chan_filter,
+				       (void *)io->dma_id);
+#else
+	io->chan = dma_request_slave_channel(dev, is_play ? "tx" : "rx");
+#endif
+	if (io->chan) {
+		struct dma_slave_config cfg = {};
+		int ret;
+
+		if (is_play) {
+			cfg.dst_addr		= fsi->phys + REG_DODT;
+			cfg.dst_addr_width	= DMA_SLAVE_BUSWIDTH_4_BYTES;
+			cfg.direction		= DMA_MEM_TO_DEV;
+		} else {
+			cfg.src_addr		= fsi->phys + REG_DIDT;
+			cfg.src_addr_width	= DMA_SLAVE_BUSWIDTH_4_BYTES;
+			cfg.direction		= DMA_DEV_TO_MEM;
+		}
+
+		ret = dmaengine_slave_config(io->chan, &cfg);
+		if (ret < 0) {
+			dma_release_channel(io->chan);
+			io->chan = NULL;
+		}
+	}
+
+	if (!io->chan) {
+
+		/* switch to PIO handler */
+		if (is_play)
+			fsi->playback.handler	= &fsi_pio_push_handler;
+		else
+			fsi->capture.handler	= &fsi_pio_pop_handler;
+
+		dev_info(dev, "switch handler (dma => pio)\n");
+
+		/* probe again */
+		return fsi_stream_probe(fsi, dev);
+	}
+
+	return 0;
+}
+
+static int fsi_dma_remove(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	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,
+	.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)
+{
+	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 (fsi->bit_clk_inv)
+		data |= (1 << 0);
+	if (fsi->lr_clk_inv)
+		data |= (1 << 4);
+	if (fsi_is_clk_master(fsi))
+		data <<= 8;
+	fsi_reg_write(fsi, CKG2, data);
+
+	/* spdif ? */
+	if (fsi_is_spdif(fsi)) {
+		fsi_spdif_clk_ctrl(fsi, 1);
+		fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD);
+	}
+
+	/*
+	 * get bus settings
+	 */
+	data = 0;
+	switch (io->sample_width) {
+	case 2:
+		data = BUSOP_GET(16, io->bus_option);
+		break;
+	case 4:
+		data = BUSOP_GET(24, io->bus_option);
+		break;
+	}
+	fsi_format_bus_setup(fsi, io, data, dev);
+
+	/* irq clear */
+	fsi_irq_disable(fsi, io);
+	fsi_irq_clear_status(fsi);
+
+	/* fifo init */
+	fsi_fifo_init(fsi, io, dev);
+
+	/* start master clock */
+	if (fsi_is_clk_master(fsi))
+		return fsi_clk_enable(dev, fsi);
+
+	return 0;
+}
+
+static int fsi_hw_shutdown(struct fsi_priv *fsi,
+			    struct device *dev)
+{
+	/* stop master clock */
+	if (fsi_is_clk_master(fsi))
+		return fsi_clk_disable(dev, fsi);
+
+	return 0;
+}
+
+static int fsi_dai_startup(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
+{
+	struct fsi_priv *fsi = fsi_get_priv(substream);
+
+	fsi_clk_invalid(fsi);
+
+	return 0;
+}
+
+static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
+{
+	struct fsi_priv *fsi = fsi_get_priv(substream);
+
+	fsi_clk_invalid(fsi);
+}
+
+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);
+		if (!ret)
+			ret = fsi_hw_startup(fsi, io, dai->dev);
+		if (!ret)
+			ret = fsi_stream_start(fsi, io);
+		if (!ret)
+			ret = fsi_stream_transfer(io);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (!ret)
+			ret = fsi_hw_shutdown(fsi, dai->dev);
+		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)
+{
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		fsi->fmt = CR_I2S;
+		fsi->chan_num = 2;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		fsi->fmt = CR_PCM;
+		fsi->chan_num = 2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int fsi_set_fmt_spdif(struct fsi_priv *fsi)
+{
+	struct fsi_master *master = fsi_get_master(fsi);
+
+	if (fsi_version(master) < 2)
+		return -EINVAL;
+
+	fsi->fmt = CR_DTMD_SPDIF_PCM | CR_PCM;
+	fsi->chan_num = 2;
+
+	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);
+	int ret;
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		fsi->clk_master = 1; /* codec is slave, cpu is master */
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* set clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_IF:
+		fsi->bit_clk_inv = 0;
+		fsi->lr_clk_inv = 1;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		fsi->bit_clk_inv = 1;
+		fsi->lr_clk_inv = 0;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		fsi->bit_clk_inv = 1;
+		fsi->lr_clk_inv = 1;
+		break;
+	case SND_SOC_DAIFMT_NB_NF:
+	default:
+		fsi->bit_clk_inv = 0;
+		fsi->lr_clk_inv = 0;
+		break;
+	}
+
+	if (fsi_is_clk_master(fsi)) {
+		if (fsi->clk_cpg)
+			fsi_clk_init(dai->dev, fsi, 0, 1, 1,
+				     fsi_clk_set_rate_cpg);
+		else
+			fsi_clk_init(dai->dev, fsi, 1, 1, 0,
+				     fsi_clk_set_rate_external);
+	}
+
+	/* set format */
+	if (fsi_is_spdif(fsi))
+		ret = fsi_set_fmt_spdif(fsi);
+	else
+		ret = fsi_set_fmt_dai(fsi, fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+
+	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);
+
+	if (fsi_is_clk_master(fsi))
+		fsi_clk_valid(fsi, params_rate(params));
+
+	return 0;
+}
+
+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 const struct snd_pcm_hardware fsi_pcm_hardware = {
+	.info =		SNDRV_PCM_INFO_INTERLEAVED	|
+			SNDRV_PCM_INFO_MMAP		|
+			SNDRV_PCM_INFO_MMAP_VALID,
+	.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 const 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_component
+ */
+
+#define PREALLOC_BUFFER		(32 * 1024)
+#define PREALLOC_BUFFER_MAX	(32 * 1024)
+
+static int fsi_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	return snd_pcm_lib_preallocate_pages_for_all(
+		rtd->pcm,
+		SNDRV_DMA_TYPE_DEV,
+		rtd->card->snd_card->dev,
+		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	= 2,
+			.channels_max	= 2,
+		},
+		.capture = {
+			.rates		= FSI_RATES,
+			.formats	= FSI_FMTS,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.ops = &fsi_dai_ops,
+	},
+	{
+		.name			= "fsib-dai",
+		.playback = {
+			.rates		= FSI_RATES,
+			.formats	= FSI_FMTS,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.capture = {
+			.rates		= FSI_RATES,
+			.formats	= FSI_FMTS,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.ops = &fsi_dai_ops,
+	},
+};
+
+static const struct snd_soc_component_driver fsi_soc_component = {
+	.name		= "fsi",
+	.ops		= &fsi_pcm_ops,
+	.pcm_new	= fsi_pcm_new,
+};
+
+/*
+ *		platform function
+ */
+static void fsi_of_parse(char *name,
+			 struct device_node *np,
+			 struct sh_fsi_port_info *info,
+			 struct device *dev)
+{
+	int i;
+	char prop[128];
+	unsigned long flags = 0;
+	struct {
+		char *name;
+		unsigned int val;
+	} of_parse_property[] = {
+		{ "spdif-connection",		SH_FSI_FMT_SPDIF },
+		{ "stream-mode-support",	SH_FSI_ENABLE_STREAM_MODE },
+		{ "use-internal-clock",		SH_FSI_CLK_CPG },
+	};
+
+	for (i = 0; i < ARRAY_SIZE(of_parse_property); i++) {
+		sprintf(prop, "%s,%s", name, of_parse_property[i].name);
+		if (of_get_property(np, prop, NULL))
+			flags |= of_parse_property[i].val;
+	}
+	info->flags = flags;
+
+	dev_dbg(dev, "%s flags : %lx\n", name, info->flags);
+}
+
+static void fsi_port_info_init(struct fsi_priv *fsi,
+			       struct sh_fsi_port_info *info)
+{
+	if (info->flags & SH_FSI_FMT_SPDIF)
+		fsi->spdif = 1;
+
+	if (info->flags & SH_FSI_CLK_CPG)
+		fsi->clk_cpg = 1;
+
+	if (info->flags & SH_FSI_ENABLE_STREAM_MODE)
+		fsi->enable_stream = 1;
+}
+
+static void fsi_handler_init(struct fsi_priv *fsi,
+			     struct sh_fsi_port_info *info)
+{
+	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 (info->tx_id) {
+		fsi->playback.dma_id  = info->tx_id;
+		fsi->playback.handler = &fsi_dma_push_handler;
+	}
+}
+
+static const struct fsi_core fsi1_core = {
+	.ver	= 1,
+
+	/* Interrupt */
+	.int_st	= INT_ST,
+	.iemsk	= IEMSK,
+	.imsk	= IMSK,
+};
+
+static const 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 const struct of_device_id fsi_of_match[] = {
+	{ .compatible = "renesas,sh_fsi",	.data = &fsi1_core},
+	{ .compatible = "renesas,sh_fsi2",	.data = &fsi2_core},
+	{},
+};
+MODULE_DEVICE_TABLE(of, fsi_of_match);
+
+static const struct platform_device_id fsi_id_table[] = {
+	{ "sh_fsi",	(kernel_ulong_t)&fsi1_core },
+	{},
+};
+MODULE_DEVICE_TABLE(platform, fsi_id_table);
+
+static int fsi_probe(struct platform_device *pdev)
+{
+	struct fsi_master *master;
+	struct device_node *np = pdev->dev.of_node;
+	struct sh_fsi_platform_info info;
+	const struct fsi_core *core;
+	struct fsi_priv *fsi;
+	struct resource *res;
+	unsigned int irq;
+	int ret;
+
+	memset(&info, 0, sizeof(info));
+
+	core = NULL;
+	if (np) {
+		core = of_device_get_match_data(&pdev->dev);
+		fsi_of_parse("fsia", np, &info.port_a, &pdev->dev);
+		fsi_of_parse("fsib", np, &info.port_b, &pdev->dev);
+	} else {
+		const struct platform_device_id	*id_entry = pdev->id_entry;
+		if (id_entry)
+			core = (struct fsi_core *)id_entry->driver_data;
+
+		if (pdev->dev.platform_data)
+			memcpy(&info, pdev->dev.platform_data, sizeof(info));
+	}
+
+	if (!core) {
+		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");
+		return -ENODEV;
+	}
+
+	master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
+	if (!master)
+		return -ENOMEM;
+
+	master->base = devm_ioremap_nocache(&pdev->dev,
+					    res->start, resource_size(res));
+	if (!master->base) {
+		dev_err(&pdev->dev, "Unable to ioremap FSI registers.\n");
+		return -ENXIO;
+	}
+
+	/* master setting */
+	master->core		= core;
+	spin_lock_init(&master->lock);
+
+	/* FSI A setting */
+	fsi		= &master->fsia;
+	fsi->base	= master->base;
+	fsi->phys	= res->start;
+	fsi->master	= master;
+	fsi_port_info_init(fsi, &info.port_a);
+	fsi_handler_init(fsi, &info.port_a);
+	ret = fsi_stream_probe(fsi, &pdev->dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "FSIA stream probe failed\n");
+		return ret;
+	}
+
+	/* FSI B setting */
+	fsi		= &master->fsib;
+	fsi->base	= master->base + 0x40;
+	fsi->phys	= res->start + 0x40;
+	fsi->master	= master;
+	fsi_port_info_init(fsi, &info.port_b);
+	fsi_handler_init(fsi, &info.port_b);
+	ret = fsi_stream_probe(fsi, &pdev->dev);
+	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 = devm_request_irq(&pdev->dev, irq, &fsi_interrupt, 0,
+			       dev_name(&pdev->dev), master);
+	if (ret) {
+		dev_err(&pdev->dev, "irq request err\n");
+		goto exit_fsib;
+	}
+
+	ret = devm_snd_soc_register_component(&pdev->dev, &fsi_soc_component,
+				    fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai));
+	if (ret < 0) {
+		dev_err(&pdev->dev, "cannot snd component register\n");
+		goto exit_fsib;
+	}
+
+	return ret;
+
+exit_fsib:
+	pm_runtime_disable(&pdev->dev);
+	fsi_stream_remove(&master->fsib);
+exit_fsia:
+	fsi_stream_remove(&master->fsia);
+
+	return ret;
+}
+
+static int fsi_remove(struct platform_device *pdev)
+{
+	struct fsi_master *master;
+
+	master = dev_get_drvdata(&pdev->dev);
+
+	pm_runtime_disable(&pdev->dev);
+
+	fsi_stream_remove(&master->fsia);
+	fsi_stream_remove(&master->fsib);
+
+	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);
+	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 const struct dev_pm_ops fsi_pm_ops = {
+	.suspend		= fsi_suspend,
+	.resume			= fsi_resume,
+};
+
+static struct platform_driver fsi_driver = {
+	.driver 	= {
+		.name	= "fsi-pcm-audio",
+		.pm	= &fsi_pm_ops,
+		.of_match_table = fsi_of_match,
+	},
+	.probe		= fsi_probe,
+	.remove		= fsi_remove,
+	.id_table	= fsi_id_table,
+};
+
+module_platform_driver(fsi_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("SuperH onchip FSI audio driver");
+MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
+MODULE_ALIAS("platform:fsi-pcm-audio");
diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c
new file mode 100644
index 0000000..c2b4963
--- /dev/null
+++ b/sound/soc/sh/hac.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Hitachi Audio Controller (AC97) support for SH7760/SH7780
+//
+// Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
+//
+// 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);
+}
+
+static struct snd_ac97_bus_ops hac_ac97_ops = {
+	.read	= hac_ac97_read,
+	.write	= hac_ac97_write,
+	.reset	= hac_ac97_coldrst,
+	.warm_reset = hac_ac97_warmrst,
+};
+
+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",
+	.bus_control		= true,
+	.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 const struct snd_soc_component_driver sh4_hac_component = {
+	.name		= "sh4-hac",
+};
+
+static int hac_soc_platform_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = snd_soc_set_ac97_ops(&hac_ac97_ops);
+	if (ret != 0)
+		return ret;
+
+	return snd_soc_register_component(&pdev->dev, &sh4_hac_component,
+					  sh4_hac_dai, ARRAY_SIZE(sh4_hac_dai));
+}
+
+static int hac_soc_platform_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_component(&pdev->dev);
+	snd_soc_set_ac97_ops(NULL);
+	return 0;
+}
+
+static struct platform_driver hac_pcm_driver = {
+	.driver = {
+			.name = "hac-pcm-audio",
+	},
+
+	.probe = hac_soc_platform_probe,
+	.remove = hac_soc_platform_remove,
+};
+
+module_platform_driver(hac_pcm_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("SuperH onchip HAC (AC97) audio driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/sh/migor.c b/sound/soc/sh/migor.c
new file mode 100644
index 0000000..8739c9f
--- /dev/null
+++ b/sound/soc/sh/migor.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC driver for Migo-R
+//
+// Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+
+#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;
+
+	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 const 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" },
+};
+
+/* 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-pcm-audio",
+	.codec_dai_name = "wm8978-hifi",
+	.platform_name = "siu-pcm-audio",
+	.codec_name = "wm8978.0-001a",
+	.dai_fmt = SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_I2S |
+		   SND_SOC_DAIFMT_CBS_CFS,
+	.ops = &migor_dai_ops,
+};
+
+/* 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,
+
+	.dapm_widgets = migor_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(migor_dapm_widgets),
+	.dapm_routes = audio_map,
+	.num_dapm_routes = ARRAY_SIZE(audio_map),
+};
+
+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_create(&siumckb_clk, "siumckb_clk", NULL);
+	if (!siumckb_lookup) {
+		ret = -ENOMEM;
+		goto eclkdevalloc;
+	}
+
+	/* 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/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile
new file mode 100644
index 0000000..5d1ff8e
--- /dev/null
+++ b/sound/soc/sh/rcar/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+snd-soc-rcar-objs	:= core.o gen.o dma.o adg.o ssi.o ssiu.o src.o ctu.o mix.o dvc.o cmd.o
+obj-$(CONFIG_SND_SOC_RCAR)	+= snd-soc-rcar.o
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
new file mode 100644
index 0000000..051f964
--- /dev/null
+++ b/sound/soc/sh/rcar/adg.c
@@ -0,0 +1,646 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Helper routines for R-Car sound ADG.
+//
+//  Copyright (C) 2013  Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+#include <linux/clk-provider.h>
+#include "rsnd.h"
+
+#define CLKA	0
+#define CLKB	1
+#define CLKC	2
+#define CLKI	3
+#define CLKMAX	4
+
+#define CLKOUT	0
+#define CLKOUT1	1
+#define CLKOUT2	2
+#define CLKOUT3	3
+#define CLKOUTMAX 4
+
+#define BRRx_MASK(x) (0x3FF & x)
+
+static struct rsnd_mod_ops adg_ops = {
+	.name = "adg",
+};
+
+struct rsnd_adg {
+	struct clk *clk[CLKMAX];
+	struct clk *clkout[CLKOUTMAX];
+	struct clk_onecell_data onecell;
+	struct rsnd_mod mod;
+	u32 flags;
+	u32 ckr;
+	u32 rbga;
+	u32 rbgb;
+
+	int rbga_rate_for_441khz; /* RBGA */
+	int rbgb_rate_for_48khz;  /* RBGB */
+};
+
+#define LRCLK_ASYNC	(1 << 0)
+#define AUDIO_OUT_48	(1 << 1)
+
+#define for_each_rsnd_clk(pos, adg, i)		\
+	for (i = 0;				\
+	     (i < CLKMAX) &&			\
+	     ((pos) = adg->clk[i]);		\
+	     i++)
+#define for_each_rsnd_clkout(pos, adg, i)	\
+	for (i = 0;				\
+	     (i < CLKOUTMAX) &&			\
+	     ((pos) = adg->clkout[i]);	\
+	     i++)
+#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
+
+static const char * const clk_name[] = {
+	[CLKA]	= "clk_a",
+	[CLKB]	= "clk_b",
+	[CLKC]	= "clk_c",
+	[CLKI]	= "clk_i",
+};
+
+static u32 rsnd_adg_calculate_rbgx(unsigned long div)
+{
+	int i, ratio;
+
+	if (!div)
+		return 0;
+
+	for (i = 3; i >= 0; i--) {
+		ratio = 2 << (i * 2);
+		if (0 == (div % ratio))
+			return (u32)((i << 8) | ((div / ratio) - 1));
+	}
+
+	return ~0;
+}
+
+static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
+{
+	struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+	int id = rsnd_mod_id(ssi_mod);
+	int ws = id;
+
+	if (rsnd_ssi_is_pin_sharing(io)) {
+		switch (id) {
+		case 1:
+		case 2:
+			ws = 0;
+			break;
+		case 4:
+			ws = 3;
+			break;
+		case 8:
+			ws = 7;
+			break;
+		}
+	}
+
+	return (0x6 + ws) << 8;
+}
+
+static void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv,
+				       struct rsnd_dai_stream *io,
+				       unsigned int target_rate,
+				       unsigned int *target_val,
+				       unsigned int *target_en)
+{
+	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	int idx, sel, div, step;
+	unsigned int val, en;
+	unsigned int min, diff;
+	unsigned int sel_rate[] = {
+		clk_get_rate(adg->clk[CLKA]),	/* 0000: CLKA */
+		clk_get_rate(adg->clk[CLKB]),	/* 0001: CLKB */
+		clk_get_rate(adg->clk[CLKC]),	/* 0010: CLKC */
+		adg->rbga_rate_for_441khz,	/* 0011: RBGA */
+		adg->rbgb_rate_for_48khz,	/* 0100: RBGB */
+	};
+
+	min = ~0;
+	val = 0;
+	en = 0;
+	for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
+		idx = 0;
+		step = 2;
+
+		if (!sel_rate[sel])
+			continue;
+
+		for (div = 2; div <= 98304; div += step) {
+			diff = abs(target_rate - sel_rate[sel] / div);
+			if (min > diff) {
+				val = (sel << 8) | idx;
+				min = diff;
+				en = 1 << (sel + 1); /* fixme */
+			}
+
+			/*
+			 * step of 0_0000 / 0_0001 / 0_1101
+			 * are out of order
+			 */
+			if ((idx > 2) && (idx % 2))
+				step *= 2;
+			if (idx == 0x1c) {
+				div += step;
+				step *= 2;
+			}
+			idx++;
+		}
+	}
+
+	if (min == ~0) {
+		dev_err(dev, "no Input clock\n");
+		return;
+	}
+
+	*target_val = val;
+	if (target_en)
+		*target_en = en;
+}
+
+static void rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv,
+				       struct rsnd_dai_stream *io,
+				       unsigned int in_rate,
+				       unsigned int out_rate,
+				       u32 *in, u32 *out, u32 *en)
+{
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	unsigned int target_rate;
+	u32 *target_val;
+	u32 _in;
+	u32 _out;
+	u32 _en;
+
+	/* default = SSI WS */
+	_in =
+	_out = rsnd_adg_ssi_ws_timing_gen2(io);
+
+	target_rate = 0;
+	target_val = NULL;
+	_en = 0;
+	if (runtime->rate != in_rate) {
+		target_rate = out_rate;
+		target_val  = &_out;
+	} else if (runtime->rate != out_rate) {
+		target_rate = in_rate;
+		target_val  = &_in;
+	}
+
+	if (target_rate)
+		__rsnd_adg_get_timesel_ratio(priv, io,
+					     target_rate,
+					     target_val, &_en);
+
+	if (in)
+		*in = _in;
+	if (out)
+		*out = _out;
+	if (en)
+		*en = _en;
+}
+
+int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
+				 struct rsnd_dai_stream *io)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(cmd_mod);
+	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+	struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+	int id = rsnd_mod_id(cmd_mod);
+	int shift = (id % 2) ? 16 : 0;
+	u32 mask, val;
+
+	rsnd_adg_get_timesel_ratio(priv, io,
+				   rsnd_src_get_in_rate(priv, io),
+				   rsnd_src_get_out_rate(priv, io),
+				   NULL, &val, NULL);
+
+	val  = val	<< shift;
+	mask = 0x0f1f	<< shift;
+
+	rsnd_mod_bset(adg_mod, CMDOUT_TIMSEL, mask, val);
+
+	return 0;
+}
+
+int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod,
+				  struct rsnd_dai_stream *io,
+				  unsigned int in_rate,
+				  unsigned int out_rate)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
+	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+	struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+	u32 in, out;
+	u32 mask, en;
+	int id = rsnd_mod_id(src_mod);
+	int shift = (id % 2) ? 16 : 0;
+
+	rsnd_mod_confirm_src(src_mod);
+
+	rsnd_adg_get_timesel_ratio(priv, io,
+				   in_rate, out_rate,
+				   &in, &out, &en);
+
+	in   = in	<< shift;
+	out  = out	<< shift;
+	mask = 0x0f1f	<< shift;
+
+	switch (id / 2) {
+	case 0:
+		rsnd_mod_bset(adg_mod, SRCIN_TIMSEL0,  mask, in);
+		rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL0, mask, out);
+		break;
+	case 1:
+		rsnd_mod_bset(adg_mod, SRCIN_TIMSEL1,  mask, in);
+		rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL1, mask, out);
+		break;
+	case 2:
+		rsnd_mod_bset(adg_mod, SRCIN_TIMSEL2,  mask, in);
+		rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL2, mask, out);
+		break;
+	case 3:
+		rsnd_mod_bset(adg_mod, SRCIN_TIMSEL3,  mask, in);
+		rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL3, mask, out);
+		break;
+	case 4:
+		rsnd_mod_bset(adg_mod, SRCIN_TIMSEL4,  mask, in);
+		rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL4, mask, out);
+		break;
+	}
+
+	if (en)
+		rsnd_mod_bset(adg_mod, DIV_EN, en, en);
+
+	return 0;
+}
+
+static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+	struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	int id = rsnd_mod_id(ssi_mod);
+	int shift = (id % 4) * 8;
+	u32 mask = 0xFF << shift;
+
+	rsnd_mod_confirm_ssi(ssi_mod);
+
+	val = val << shift;
+
+	/*
+	 * SSI 8 is not connected to ADG.
+	 * it works with SSI 7
+	 */
+	if (id == 8)
+		return;
+
+	switch (id / 4) {
+	case 0:
+		rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL0, mask, val);
+		break;
+	case 1:
+		rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL1, mask, val);
+		break;
+	case 2:
+		rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL2, mask, val);
+		break;
+	}
+
+	dev_dbg(dev, "AUDIO_CLK_SEL is 0x%x\n", val);
+}
+
+int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate)
+{
+	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+	struct clk *clk;
+	int i;
+	int sel_table[] = {
+		[CLKA] = 0x1,
+		[CLKB] = 0x2,
+		[CLKC] = 0x3,
+		[CLKI] = 0x0,
+	};
+
+	/*
+	 * find suitable clock from
+	 * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI.
+	 */
+	for_each_rsnd_clk(clk, adg, i) {
+		if (rate == clk_get_rate(clk))
+			return sel_table[i];
+	}
+
+	/*
+	 * find divided clock from BRGA/BRGB
+	 */
+	if (rate == adg->rbga_rate_for_441khz)
+		return 0x10;
+
+	if (rate == adg->rbgb_rate_for_48khz)
+		return 0x20;
+
+	return -EIO;
+}
+
+int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod)
+{
+	rsnd_adg_set_ssi_clk(ssi_mod, 0);
+
+	return 0;
+}
+
+int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+	int data;
+	u32 ckr = 0;
+
+	data = rsnd_adg_clk_query(priv, rate);
+	if (data < 0)
+		return data;
+
+	rsnd_adg_set_ssi_clk(ssi_mod, data);
+
+	if (rsnd_flags_has(adg, LRCLK_ASYNC)) {
+		if (rsnd_flags_has(adg, AUDIO_OUT_48))
+			ckr = 0x80000000;
+	} else {
+		if (0 == (rate % 8000))
+			ckr = 0x80000000;
+	}
+
+	rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr | ckr);
+	rsnd_mod_write(adg_mod, BRRA,  adg->rbga);
+	rsnd_mod_write(adg_mod, BRRB,  adg->rbgb);
+
+	dev_dbg(dev, "CLKOUT is based on BRG%c (= %dHz)\n",
+		(ckr) ? 'B' : 'A',
+		(ckr) ?	adg->rbgb_rate_for_48khz :
+			adg->rbga_rate_for_441khz);
+
+	return 0;
+}
+
+void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
+{
+	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct clk *clk;
+	int i, ret;
+
+	for_each_rsnd_clk(clk, adg, i) {
+		ret = 0;
+		if (enable)
+			ret = clk_prepare_enable(clk);
+		else
+			clk_disable_unprepare(clk);
+
+		if (ret < 0)
+			dev_warn(dev, "can't use clk %d\n", i);
+	}
+}
+
+static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
+			       struct rsnd_adg *adg)
+{
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct clk *clk;
+	int i;
+
+	for (i = 0; i < CLKMAX; i++) {
+		clk = devm_clk_get(dev, clk_name[i]);
+		adg->clk[i] = IS_ERR(clk) ? NULL : clk;
+	}
+}
+
+static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
+				struct rsnd_adg *adg)
+{
+	struct clk *clk;
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct device_node *np = dev->of_node;
+	struct property *prop;
+	u32 ckr, rbgx, rbga, rbgb;
+	u32 rate, div;
+#define REQ_SIZE 2
+	u32 req_rate[REQ_SIZE] = {};
+	uint32_t count = 0;
+	unsigned long req_48kHz_rate, req_441kHz_rate;
+	int i, req_size;
+	const char *parent_clk_name = NULL;
+	static const char * const clkout_name[] = {
+		[CLKOUT]  = "audio_clkout",
+		[CLKOUT1] = "audio_clkout1",
+		[CLKOUT2] = "audio_clkout2",
+		[CLKOUT3] = "audio_clkout3",
+	};
+	int brg_table[] = {
+		[CLKA] = 0x0,
+		[CLKB] = 0x1,
+		[CLKC] = 0x4,
+		[CLKI] = 0x2,
+	};
+
+	ckr = 0;
+	rbga = 2; /* default 1/6 */
+	rbgb = 2; /* default 1/6 */
+
+	/*
+	 * ADG supports BRRA/BRRB output only
+	 * this means all clkout0/1/2/3 will be same rate
+	 */
+	prop = of_find_property(np, "clock-frequency", NULL);
+	if (!prop)
+		goto rsnd_adg_get_clkout_end;
+
+	req_size = prop->length / sizeof(u32);
+	if (req_size > REQ_SIZE) {
+		dev_err(dev,
+			"too many clock-frequency, use top %d\n", REQ_SIZE);
+		req_size = REQ_SIZE;
+	}
+
+	of_property_read_u32_array(np, "clock-frequency", req_rate, req_size);
+	req_48kHz_rate = 0;
+	req_441kHz_rate = 0;
+	for (i = 0; i < req_size; i++) {
+		if (0 == (req_rate[i] % 44100))
+			req_441kHz_rate = req_rate[i];
+		if (0 == (req_rate[i] % 48000))
+			req_48kHz_rate = req_rate[i];
+	}
+
+	if (req_rate[0] % 48000 == 0)
+		rsnd_flags_set(adg, AUDIO_OUT_48);
+
+	if (of_get_property(np, "clkout-lr-asynchronous", NULL))
+		rsnd_flags_set(adg, LRCLK_ASYNC);
+
+	/*
+	 * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
+	 * have 44.1kHz or 48kHz base clocks for now.
+	 *
+	 * SSI itself can divide parent clock by 1/1 - 1/16
+	 * see
+	 *	rsnd_adg_ssi_clk_try_start()
+	 *	rsnd_ssi_master_clk_start()
+	 */
+	adg->rbga_rate_for_441khz	= 0;
+	adg->rbgb_rate_for_48khz	= 0;
+	for_each_rsnd_clk(clk, adg, i) {
+		rate = clk_get_rate(clk);
+
+		if (0 == rate) /* not used */
+			continue;
+
+		/* RBGA */
+		if (!adg->rbga_rate_for_441khz && (0 == rate % 44100)) {
+			div = 6;
+			if (req_441kHz_rate)
+				div = rate / req_441kHz_rate;
+			rbgx = rsnd_adg_calculate_rbgx(div);
+			if (BRRx_MASK(rbgx) == rbgx) {
+				rbga = rbgx;
+				adg->rbga_rate_for_441khz = rate / div;
+				ckr |= brg_table[i] << 20;
+				if (req_441kHz_rate &&
+				    !rsnd_flags_has(adg, AUDIO_OUT_48))
+					parent_clk_name = __clk_get_name(clk);
+			}
+		}
+
+		/* RBGB */
+		if (!adg->rbgb_rate_for_48khz && (0 == rate % 48000)) {
+			div = 6;
+			if (req_48kHz_rate)
+				div = rate / req_48kHz_rate;
+			rbgx = rsnd_adg_calculate_rbgx(div);
+			if (BRRx_MASK(rbgx) == rbgx) {
+				rbgb = rbgx;
+				adg->rbgb_rate_for_48khz = rate / div;
+				ckr |= brg_table[i] << 16;
+				if (req_48kHz_rate &&
+				    rsnd_flags_has(adg, AUDIO_OUT_48))
+					parent_clk_name = __clk_get_name(clk);
+			}
+		}
+	}
+
+	/*
+	 * ADG supports BRRA/BRRB output only.
+	 * this means all clkout0/1/2/3 will be * same rate
+	 */
+
+	of_property_read_u32(np, "#clock-cells", &count);
+	/*
+	 * for clkout
+	 */
+	if (!count) {
+		clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT],
+					      parent_clk_name, 0, req_rate[0]);
+		if (!IS_ERR(clk)) {
+			adg->clkout[CLKOUT] = clk;
+			of_clk_add_provider(np, of_clk_src_simple_get, clk);
+		}
+	}
+	/*
+	 * for clkout0/1/2/3
+	 */
+	else {
+		for (i = 0; i < CLKOUTMAX; i++) {
+			clk = clk_register_fixed_rate(dev, clkout_name[i],
+						      parent_clk_name, 0,
+						      req_rate[0]);
+			if (!IS_ERR(clk))
+				adg->clkout[i] = clk;
+		}
+		adg->onecell.clks	= adg->clkout;
+		adg->onecell.clk_num	= CLKOUTMAX;
+		of_clk_add_provider(np, of_clk_src_onecell_get,
+				    &adg->onecell);
+	}
+
+rsnd_adg_get_clkout_end:
+	adg->ckr = ckr;
+	adg->rbga = rbga;
+	adg->rbgb = rbgb;
+}
+
+#ifdef DEBUG
+static void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct rsnd_adg *adg)
+{
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct clk *clk;
+	int i;
+
+	for_each_rsnd_clk(clk, adg, i)
+		dev_dbg(dev, "%s    : %p : %ld\n",
+			clk_name[i], clk, clk_get_rate(clk));
+
+	dev_dbg(dev, "BRGCKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",
+		adg->ckr, adg->rbga, adg->rbgb);
+	dev_dbg(dev, "BRGA (for 44100 base) = %d\n", adg->rbga_rate_for_441khz);
+	dev_dbg(dev, "BRGB (for 48000 base) = %d\n", adg->rbgb_rate_for_48khz);
+
+	/*
+	 * Actual CLKOUT will be exchanged in rsnd_adg_ssi_clk_try_start()
+	 * by BRGCKR::BRGCKR_31
+	 */
+	for_each_rsnd_clkout(clk, adg, i)
+		dev_dbg(dev, "clkout %d : %p : %ld\n", i,
+			clk, clk_get_rate(clk));
+}
+#else
+#define rsnd_adg_clk_dbg_info(priv, adg)
+#endif
+
+int rsnd_adg_probe(struct rsnd_priv *priv)
+{
+	struct rsnd_adg *adg;
+	struct device *dev = rsnd_priv_to_dev(priv);
+	int ret;
+
+	adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
+	if (!adg)
+		return -ENOMEM;
+
+	ret = rsnd_mod_init(priv, &adg->mod, &adg_ops,
+		      NULL, NULL, 0, 0);
+	if (ret)
+		return ret;
+
+	rsnd_adg_get_clkin(priv, adg);
+	rsnd_adg_get_clkout(priv, adg);
+	rsnd_adg_clk_dbg_info(priv, adg);
+
+	priv->adg = adg;
+
+	rsnd_adg_clk_enable(priv);
+
+	return 0;
+}
+
+void rsnd_adg_remove(struct rsnd_priv *priv)
+{
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct device_node *np = dev->of_node;
+	struct rsnd_adg *adg = priv->adg;
+	struct clk *clk;
+	int i;
+
+	for_each_rsnd_clkout(clk, adg, i)
+		if (adg->clkout[i])
+			clk_unregister_fixed_rate(adg->clkout[i]);
+
+	of_clk_del_provider(np);
+
+	rsnd_adg_clk_disable(priv);
+}
diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c
new file mode 100644
index 0000000..cc191cd
--- /dev/null
+++ b/sound/soc/sh/rcar/cmd.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car CMD support
+//
+// Copyright (C) 2015 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+#include "rsnd.h"
+
+struct rsnd_cmd {
+	struct rsnd_mod mod;
+};
+
+#define CMD_NAME "cmd"
+
+#define rsnd_cmd_nr(priv) ((priv)->cmd_nr)
+#define for_each_rsnd_cmd(pos, priv, i)					\
+	for ((i) = 0;							\
+	     ((i) < rsnd_cmd_nr(priv)) &&				\
+		     ((pos) = (struct rsnd_cmd *)(priv)->cmd + i);	\
+	     i++)
+
+static int rsnd_cmd_init(struct rsnd_mod *mod,
+			 struct rsnd_dai_stream *io,
+			 struct rsnd_priv *priv)
+{
+	struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
+	struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	u32 data;
+	static const u32 path[] = {
+		[1] = 1 << 0,
+		[5] = 1 << 8,
+		[6] = 1 << 12,
+		[9] = 1 << 15,
+	};
+
+	if (!mix && !dvc)
+		return 0;
+
+	if (ARRAY_SIZE(path) < rsnd_mod_id(mod) + 1)
+		return -ENXIO;
+
+	if (mix) {
+		struct rsnd_dai *rdai;
+		struct rsnd_mod *src;
+		struct rsnd_dai_stream *tio;
+		int i;
+
+		/*
+		 * it is assuming that integrater is well understanding about
+		 * data path. Here doesn't check impossible connection,
+		 * like src2 + src5
+		 */
+		data = 0;
+		for_each_rsnd_dai(rdai, priv, i) {
+			tio = &rdai->playback;
+			src = rsnd_io_to_mod_src(tio);
+			if (mix == rsnd_io_to_mod_mix(tio))
+				data |= path[rsnd_mod_id(src)];
+
+			tio = &rdai->capture;
+			src = rsnd_io_to_mod_src(tio);
+			if (mix == rsnd_io_to_mod_mix(tio))
+				data |= path[rsnd_mod_id(src)];
+		}
+
+	} else {
+		struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+
+		static const u8 cmd_case[] = {
+			[0] = 0x3,
+			[1] = 0x3,
+			[2] = 0x4,
+			[3] = 0x1,
+			[4] = 0x2,
+			[5] = 0x4,
+			[6] = 0x1,
+			[9] = 0x2,
+		};
+
+		if (unlikely(!src))
+			return -EIO;
+
+		data = path[rsnd_mod_id(src)] |
+			cmd_case[rsnd_mod_id(src)] << 16;
+	}
+
+	dev_dbg(dev, "ctu/mix path = 0x%08x\n", data);
+
+	rsnd_mod_write(mod, CMD_ROUTE_SLCT, data);
+	rsnd_mod_write(mod, CMD_BUSIF_MODE, rsnd_get_busif_shift(io, mod) | 1);
+	rsnd_mod_write(mod, CMD_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
+
+	rsnd_adg_set_cmd_timsel_gen2(mod, io);
+
+	return 0;
+}
+
+static int rsnd_cmd_start(struct rsnd_mod *mod,
+			  struct rsnd_dai_stream *io,
+			  struct rsnd_priv *priv)
+{
+	rsnd_mod_write(mod, CMD_CTRL, 0x10);
+
+	return 0;
+}
+
+static int rsnd_cmd_stop(struct rsnd_mod *mod,
+			 struct rsnd_dai_stream *io,
+			 struct rsnd_priv *priv)
+{
+	rsnd_mod_write(mod, CMD_CTRL, 0);
+
+	return 0;
+}
+
+static struct rsnd_mod_ops rsnd_cmd_ops = {
+	.name	= CMD_NAME,
+	.init	= rsnd_cmd_init,
+	.start	= rsnd_cmd_start,
+	.stop	= rsnd_cmd_stop,
+};
+
+static struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id)
+{
+	if (WARN_ON(id < 0 || id >= rsnd_cmd_nr(priv)))
+		id = 0;
+
+	return rsnd_mod_get((struct rsnd_cmd *)(priv->cmd) + id);
+}
+int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id)
+{
+	struct rsnd_priv *priv = rsnd_io_to_priv(io);
+	struct rsnd_mod *mod = rsnd_cmd_mod_get(priv, id);
+
+	return rsnd_dai_connect(mod, io, mod->type);
+}
+
+int rsnd_cmd_probe(struct rsnd_priv *priv)
+{
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_cmd *cmd;
+	int i, nr, ret;
+
+	/* This driver doesn't support Gen1 at this point */
+	if (rsnd_is_gen1(priv))
+		return 0;
+
+	/* same number as DVC */
+	nr = priv->dvc_nr;
+	if (!nr)
+		return 0;
+
+	cmd = devm_kcalloc(dev, nr, sizeof(*cmd), GFP_KERNEL);
+	if (!cmd)
+		return -ENOMEM;
+
+	priv->cmd_nr	= nr;
+	priv->cmd	= cmd;
+
+	for_each_rsnd_cmd(cmd, priv, i) {
+		ret = rsnd_mod_init(priv, rsnd_mod_get(cmd),
+				    &rsnd_cmd_ops, NULL,
+				    rsnd_mod_get_status, RSND_MOD_CMD, i);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+void rsnd_cmd_remove(struct rsnd_priv *priv)
+{
+	struct rsnd_cmd *cmd;
+	int i;
+
+	for_each_rsnd_cmd(cmd, priv, i) {
+		rsnd_mod_quit(rsnd_mod_get(cmd));
+	}
+}
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
new file mode 100644
index 0000000..d23c2bb
--- /dev/null
+++ b/sound/soc/sh/rcar/core.c
@@ -0,0 +1,1642 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car SRU/SCU/SSIU/SSI support
+//
+// Copyright (C) 2013 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+//
+// Based on fsi.c
+// Kuninori Morimoto <morimoto.kuninori@renesas.com>
+
+/*
+ * Renesas R-Car sound device structure
+ *
+ * Gen1
+ *
+ * SRU		: Sound Routing Unit
+ *  - SRC	: Sampling Rate Converter
+ *  - CMD
+ *    - CTU	: Channel Count Conversion Unit
+ *    - MIX	: Mixer
+ *    - DVC	: Digital Volume and Mute Function
+ *  - SSI	: Serial Sound Interface
+ *
+ * Gen2
+ *
+ * SCU		: Sampling Rate Converter Unit
+ *  - SRC	: Sampling Rate Converter
+ *  - CMD
+ *   - CTU	: Channel Count Conversion Unit
+ *   - MIX	: Mixer
+ *   - DVC	: Digital Volume and Mute Function
+ * SSIU		: Serial Sound Interface Unit
+ *  - SSI	: Serial Sound Interface
+ */
+
+/*
+ *	driver data Image
+ *
+ * rsnd_priv
+ *   |
+ *   | ** this depends on Gen1/Gen2
+ *   |
+ *   +- gen
+ *   |
+ *   | ** these depend on data path
+ *   | ** gen and platform data control it
+ *   |
+ *   +- rdai[0]
+ *   |   |		 sru     ssiu      ssi
+ *   |   +- playback -> [mod] -> [mod] -> [mod] -> ...
+ *   |   |
+ *   |   |		 sru     ssiu      ssi
+ *   |   +- capture  -> [mod] -> [mod] -> [mod] -> ...
+ *   |
+ *   +- rdai[1]
+ *   |   |		 sru     ssiu      ssi
+ *   |   +- playback -> [mod] -> [mod] -> [mod] -> ...
+ *   |   |
+ *   |   |		 sru     ssiu      ssi
+ *   |   +- capture  -> [mod] -> [mod] -> [mod] -> ...
+ *   ...
+ *   |
+ *   | ** these control ssi
+ *   |
+ *   +- ssi
+ *   |  |
+ *   |  +- ssi[0]
+ *   |  +- ssi[1]
+ *   |  +- ssi[2]
+ *   |  ...
+ *   |
+ *   | ** these control src
+ *   |
+ *   +- src
+ *      |
+ *      +- src[0]
+ *      +- src[1]
+ *      +- src[2]
+ *      ...
+ *
+ *
+ * for_each_rsnd_dai(xx, priv, xx)
+ *  rdai[0] => rdai[1] => rdai[2] => ...
+ *
+ * for_each_rsnd_mod(xx, rdai, xx)
+ *  [mod] => [mod] => [mod] => ...
+ *
+ * rsnd_dai_call(xxx, fn )
+ *  [mod]->fn() -> [mod]->fn() -> [mod]->fn()...
+ *
+ */
+
+/*
+ * you can enable below define if you don't need
+ * DAI status debug message when debugging
+ * see rsnd_dbg_dai_call()
+ *
+ * #define RSND_DEBUG_NO_DAI_CALL 1
+ */
+
+#include <linux/pm_runtime.h>
+#include "rsnd.h"
+
+#define RSND_RATES SNDRV_PCM_RATE_8000_192000
+#define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
+
+static const struct of_device_id rsnd_of_match[] = {
+	{ .compatible = "renesas,rcar_sound-gen1", .data = (void *)RSND_GEN1 },
+	{ .compatible = "renesas,rcar_sound-gen2", .data = (void *)RSND_GEN2 },
+	{ .compatible = "renesas,rcar_sound-gen3", .data = (void *)RSND_GEN3 },
+	{},
+};
+MODULE_DEVICE_TABLE(of, rsnd_of_match);
+
+/*
+ *	rsnd_mod functions
+ */
+void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
+{
+	if (mod->type != type) {
+		struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+		struct device *dev = rsnd_priv_to_dev(priv);
+
+		dev_warn(dev, "%s[%d] is not your expected module\n",
+			 rsnd_mod_name(mod), rsnd_mod_id(mod));
+	}
+}
+
+struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
+				  struct rsnd_mod *mod)
+{
+	if (!mod || !mod->ops || !mod->ops->dma_req)
+		return NULL;
+
+	return mod->ops->dma_req(io, mod);
+}
+
+u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io,
+			 struct rsnd_mod *mod,
+			 enum rsnd_mod_type type)
+{
+	return &mod->status;
+}
+
+int rsnd_mod_init(struct rsnd_priv *priv,
+		  struct rsnd_mod *mod,
+		  struct rsnd_mod_ops *ops,
+		  struct clk *clk,
+		  u32* (*get_status)(struct rsnd_dai_stream *io,
+				     struct rsnd_mod *mod,
+				     enum rsnd_mod_type type),
+		  enum rsnd_mod_type type,
+		  int id)
+{
+	int ret = clk_prepare(clk);
+
+	if (ret)
+		return ret;
+
+	mod->id		= id;
+	mod->ops	= ops;
+	mod->type	= type;
+	mod->clk	= clk;
+	mod->priv	= priv;
+	mod->get_status	= get_status;
+
+	return ret;
+}
+
+void rsnd_mod_quit(struct rsnd_mod *mod)
+{
+	clk_unprepare(mod->clk);
+	mod->clk = NULL;
+}
+
+void rsnd_mod_interrupt(struct rsnd_mod *mod,
+			void (*callback)(struct rsnd_mod *mod,
+					 struct rsnd_dai_stream *io))
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct rsnd_dai_stream *io;
+	struct rsnd_dai *rdai;
+	int i;
+
+	for_each_rsnd_dai(rdai, priv, i) {
+		io = &rdai->playback;
+		if (mod == io->mod[mod->type])
+			callback(mod, io);
+
+		io = &rdai->capture;
+		if (mod == io->mod[mod->type])
+			callback(mod, io);
+	}
+}
+
+int rsnd_io_is_working(struct rsnd_dai_stream *io)
+{
+	/* see rsnd_dai_stream_init/quit() */
+	if (io->substream)
+		return snd_pcm_running(io->substream);
+
+	return 0;
+}
+
+int rsnd_runtime_channel_original_with_params(struct rsnd_dai_stream *io,
+					      struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+
+	/*
+	 * params will be added when refine
+	 * see
+	 *	__rsnd_soc_hw_rule_rate()
+	 *	__rsnd_soc_hw_rule_channels()
+	 */
+	if (params)
+		return params_channels(params);
+	else
+		return runtime->channels;
+}
+
+int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io,
+					       struct snd_pcm_hw_params *params)
+{
+	int chan = rsnd_runtime_channel_original_with_params(io, params);
+	struct rsnd_mod *ctu_mod = rsnd_io_to_mod_ctu(io);
+
+	if (ctu_mod) {
+		u32 converted_chan = rsnd_ctu_converted_channel(ctu_mod);
+
+		if (converted_chan)
+			return converted_chan;
+	}
+
+	return chan;
+}
+
+int rsnd_runtime_channel_for_ssi_with_params(struct rsnd_dai_stream *io,
+					     struct snd_pcm_hw_params *params)
+{
+	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+	int chan = rsnd_io_is_play(io) ?
+		rsnd_runtime_channel_after_ctu_with_params(io, params) :
+		rsnd_runtime_channel_original_with_params(io, params);
+
+	/* Use Multi SSI */
+	if (rsnd_runtime_is_ssi_multi(io))
+		chan /= rsnd_rdai_ssi_lane_get(rdai);
+
+	/* TDM Extend Mode needs 8ch */
+	if (chan == 6)
+		chan = 8;
+
+	return chan;
+}
+
+int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io)
+{
+	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+	int lane = rsnd_rdai_ssi_lane_get(rdai);
+	int chan = rsnd_io_is_play(io) ?
+		rsnd_runtime_channel_after_ctu(io) :
+		rsnd_runtime_channel_original(io);
+
+	return (chan > 2) && (lane > 1);
+}
+
+int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io)
+{
+	return rsnd_runtime_channel_for_ssi(io) >= 6;
+}
+
+/*
+ *	ADINR function
+ */
+u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	struct device *dev = rsnd_priv_to_dev(priv);
+
+	switch (snd_pcm_format_width(runtime->format)) {
+	case 16:
+		return 8 << 16;
+	case 24:
+		return 0 << 16;
+	}
+
+	dev_warn(dev, "not supported sample bits\n");
+
+	return 0;
+}
+
+/*
+ *	DALIGN function
+ */
+u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
+{
+	struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io);
+	struct rsnd_mod *target;
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+
+	/*
+	 * *Hardware* L/R and *Software* L/R are inverted for 16bit data.
+	 *	    31..16 15...0
+	 *	HW: [L ch] [R ch]
+	 *	SW: [R ch] [L ch]
+	 * We need to care about inversion timing to control
+	 * Playback/Capture correctly.
+	 * The point is [DVC] needs *Hardware* L/R, [MEM] needs *Software* L/R
+	 *
+	 * sL/R : software L/R
+	 * hL/R : hardware L/R
+	 * (*)  : conversion timing
+	 *
+	 * Playback
+	 *	     sL/R (*) hL/R     hL/R     hL/R      hL/R     hL/R
+	 *	[MEM] -> [SRC] -> [DVC] -> [CMD] -> [SSIU] -> [SSI] -> codec
+	 *
+	 * Capture
+	 *	     hL/R     hL/R      hL/R     hL/R     hL/R (*) sL/R
+	 *	codec -> [SSI] -> [SSIU] -> [SRC] -> [DVC] -> [CMD] -> [MEM]
+	 */
+	if (rsnd_io_is_play(io)) {
+		struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+
+		target = src ? src : ssiu;
+	} else {
+		struct rsnd_mod *cmd = rsnd_io_to_mod_cmd(io);
+
+		target = cmd ? cmd : ssiu;
+	}
+
+	/* Non target mod or 24bit data needs normal DALIGN */
+	if ((snd_pcm_format_width(runtime->format) != 16) ||
+	    (mod != target))
+		return 0x76543210;
+	/* Target mod needs inverted DALIGN when 16bit */
+	else
+		return 0x67452301;
+}
+
+u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod)
+{
+	enum rsnd_mod_type playback_mods[] = {
+		RSND_MOD_SRC,
+		RSND_MOD_CMD,
+		RSND_MOD_SSIU,
+	};
+	enum rsnd_mod_type capture_mods[] = {
+		RSND_MOD_CMD,
+		RSND_MOD_SRC,
+		RSND_MOD_SSIU,
+	};
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	struct rsnd_mod *tmod = NULL;
+	enum rsnd_mod_type *mods =
+		rsnd_io_is_play(io) ?
+		playback_mods : capture_mods;
+	int i;
+
+	/*
+	 * This is needed for 24bit data
+	 * We need to shift 8bit
+	 *
+	 * Linux 24bit data is located as 0x00******
+	 * HW    24bit data is located as 0x******00
+	 *
+	 */
+	if (snd_pcm_format_width(runtime->format) == 16)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(playback_mods); i++) {
+		tmod = rsnd_io_to_mod(io, mods[i]);
+		if (tmod)
+			break;
+	}
+
+	if (tmod != mod)
+		return 0;
+
+	if (rsnd_io_is_play(io))
+		return  (0 << 20) | /* shift to Left */
+			(8 << 16);  /* 8bit */
+	else
+		return  (1 << 20) | /* shift to Right */
+			(8 << 16);  /* 8bit */
+}
+
+/*
+ *	rsnd_dai functions
+ */
+struct rsnd_mod *rsnd_mod_next(int *iterator,
+			       struct rsnd_dai_stream *io,
+			       enum rsnd_mod_type *array,
+			       int array_size)
+{
+	struct rsnd_mod *mod;
+	enum rsnd_mod_type type;
+	int max = array ? array_size : RSND_MOD_MAX;
+
+	for (; *iterator < max; (*iterator)++) {
+		type = (array) ? array[*iterator] : *iterator;
+		mod = rsnd_io_to_mod(io, type);
+		if (mod)
+			return mod;
+	}
+
+	return NULL;
+}
+
+static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = {
+	{
+		/* CAPTURE */
+		RSND_MOD_AUDMAPP,
+		RSND_MOD_AUDMA,
+		RSND_MOD_DVC,
+		RSND_MOD_MIX,
+		RSND_MOD_CTU,
+		RSND_MOD_CMD,
+		RSND_MOD_SRC,
+		RSND_MOD_SSIU,
+		RSND_MOD_SSIM3,
+		RSND_MOD_SSIM2,
+		RSND_MOD_SSIM1,
+		RSND_MOD_SSIP,
+		RSND_MOD_SSI,
+	}, {
+		/* PLAYBACK */
+		RSND_MOD_AUDMAPP,
+		RSND_MOD_AUDMA,
+		RSND_MOD_SSIM3,
+		RSND_MOD_SSIM2,
+		RSND_MOD_SSIM1,
+		RSND_MOD_SSIP,
+		RSND_MOD_SSI,
+		RSND_MOD_SSIU,
+		RSND_MOD_DVC,
+		RSND_MOD_MIX,
+		RSND_MOD_CTU,
+		RSND_MOD_CMD,
+		RSND_MOD_SRC,
+	},
+};
+
+static int rsnd_status_update(u32 *status,
+			      int shift, int add, int timing)
+{
+	u32 mask	= 0xF << shift;
+	u8 val		= (*status >> shift) & 0xF;
+	u8 next_val	= (val + add) & 0xF;
+	int func_call	= (val == timing);
+
+	if (next_val == 0xF) /* underflow case */
+		func_call = 0;
+	else
+		*status = (*status & ~mask) + (next_val << shift);
+
+	return func_call;
+}
+
+#define rsnd_dai_call(fn, io, param...)					\
+({									\
+	struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io));	\
+	struct rsnd_mod *mod;						\
+	int is_play = rsnd_io_is_play(io);				\
+	int ret = 0, i;							\
+	enum rsnd_mod_type *types = rsnd_mod_sequence[is_play];		\
+	for_each_rsnd_mod_arrays(i, mod, io, types, RSND_MOD_MAX) {	\
+		int tmp = 0;						\
+		u32 *status = mod->get_status(io, mod, types[i]);	\
+		int func_call = rsnd_status_update(status,		\
+						__rsnd_mod_shift_##fn,	\
+						__rsnd_mod_add_##fn,	\
+						__rsnd_mod_call_##fn);	\
+		rsnd_dbg_dai_call(dev, "%s[%d]\t0x%08x %s\n",		\
+			rsnd_mod_name(mod), rsnd_mod_id(mod), *status,	\
+			(func_call && (mod)->ops->fn) ? #fn : "");	\
+		if (func_call && (mod)->ops->fn)			\
+			tmp = (mod)->ops->fn(mod, io, param);		\
+		if (tmp && (tmp != -EPROBE_DEFER))			\
+			dev_err(dev, "%s[%d] : %s error %d\n",		\
+				rsnd_mod_name(mod), rsnd_mod_id(mod),	\
+						     #fn, tmp);		\
+		ret |= tmp;						\
+	}								\
+	ret;								\
+})
+
+int rsnd_dai_connect(struct rsnd_mod *mod,
+		     struct rsnd_dai_stream *io,
+		     enum rsnd_mod_type type)
+{
+	struct rsnd_priv *priv;
+	struct device *dev;
+
+	if (!mod)
+		return -EIO;
+
+	if (io->mod[type] == mod)
+		return 0;
+
+	if (io->mod[type])
+		return -EINVAL;
+
+	priv = rsnd_mod_to_priv(mod);
+	dev = rsnd_priv_to_dev(priv);
+
+	io->mod[type] = mod;
+
+	dev_dbg(dev, "%s[%d] is connected to io (%s)\n",
+		rsnd_mod_name(mod), rsnd_mod_id(mod),
+		rsnd_io_is_play(io) ? "Playback" : "Capture");
+
+	return 0;
+}
+
+static void rsnd_dai_disconnect(struct rsnd_mod *mod,
+				struct rsnd_dai_stream *io,
+				enum rsnd_mod_type type)
+{
+	io->mod[type] = NULL;
+}
+
+int rsnd_rdai_channels_ctrl(struct rsnd_dai *rdai,
+			    int max_channels)
+{
+	if (max_channels > 0)
+		rdai->max_channels = max_channels;
+
+	return rdai->max_channels;
+}
+
+int rsnd_rdai_ssi_lane_ctrl(struct rsnd_dai *rdai,
+			    int ssi_lane)
+{
+	if (ssi_lane > 0)
+		rdai->ssi_lane = ssi_lane;
+
+	return rdai->ssi_lane;
+}
+
+struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id)
+{
+	if ((id < 0) || (id >= rsnd_rdai_nr(priv)))
+		return NULL;
+
+	return priv->rdai + id;
+}
+
+static struct snd_soc_dai_driver
+*rsnd_daidrv_get(struct rsnd_priv *priv, int id)
+{
+	if ((id < 0) || (id >= rsnd_rdai_nr(priv)))
+		return NULL;
+
+	return priv->daidrv + id;
+}
+
+#define rsnd_dai_to_priv(dai) snd_soc_dai_get_drvdata(dai)
+static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai)
+{
+	struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
+
+	return rsnd_rdai_get(priv, dai->id);
+}
+
+/*
+ *	rsnd_soc_dai functions
+ */
+void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io)
+{
+	struct snd_pcm_substream *substream = io->substream;
+
+	/*
+	 * this function should be called...
+	 *
+	 * - if rsnd_dai_pointer_update() returns true
+	 * - without spin lock
+	 */
+
+	snd_pcm_period_elapsed(substream);
+}
+
+static void rsnd_dai_stream_init(struct rsnd_dai_stream *io,
+				struct snd_pcm_substream *substream)
+{
+	io->substream		= substream;
+}
+
+static void rsnd_dai_stream_quit(struct rsnd_dai_stream *io)
+{
+	io->substream		= NULL;
+}
+
+static
+struct snd_soc_dai *rsnd_substream_to_dai(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+	return  rtd->cpu_dai;
+}
+
+static
+struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai,
+					struct snd_pcm_substream *substream)
+{
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		return &rdai->playback;
+	else
+		return &rdai->capture;
+}
+
+static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+			    struct snd_soc_dai *dai)
+{
+	struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
+	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+	struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		ret = rsnd_dai_call(init, io, priv);
+		if (ret < 0)
+			goto dai_trigger_end;
+
+		ret = rsnd_dai_call(start, io, priv);
+		if (ret < 0)
+			goto dai_trigger_end;
+
+		ret = rsnd_dai_call(irq, io, priv, 1);
+		if (ret < 0)
+			goto dai_trigger_end;
+
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		ret = rsnd_dai_call(irq, io, priv, 0);
+
+		ret |= rsnd_dai_call(stop, io, priv);
+
+		ret |= rsnd_dai_call(quit, io, priv);
+
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+dai_trigger_end:
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return ret;
+}
+
+static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		rdai->clk_master = 0;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		rdai->clk_master = 1; /* codec is slave, cpu is master */
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* set format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		rdai->sys_delay = 0;
+		rdai->data_alignment = 0;
+		rdai->frm_clk_inv = 0;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		rdai->sys_delay = 1;
+		rdai->data_alignment = 0;
+		rdai->frm_clk_inv = 1;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		rdai->sys_delay = 1;
+		rdai->data_alignment = 1;
+		rdai->frm_clk_inv = 1;
+		break;
+	}
+
+	/* set clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_IF:
+		rdai->frm_clk_inv = !rdai->frm_clk_inv;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		rdai->bit_clk_inv = !rdai->bit_clk_inv;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		rdai->bit_clk_inv = !rdai->bit_clk_inv;
+		rdai->frm_clk_inv = !rdai->frm_clk_inv;
+		break;
+	case SND_SOC_DAIFMT_NB_NF:
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai,
+				     u32 tx_mask, u32 rx_mask,
+				     int slots, int slot_width)
+{
+	struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
+	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+	struct device *dev = rsnd_priv_to_dev(priv);
+
+	switch (slots) {
+	case 2:
+	case 6:
+	case 8:
+		/* TDM Extend Mode */
+		rsnd_rdai_channels_set(rdai, slots);
+		rsnd_rdai_ssi_lane_set(rdai, 1);
+		break;
+	default:
+		dev_err(dev, "unsupported TDM slots (%d)\n", slots);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static unsigned int rsnd_soc_hw_channels_list[] = {
+	2, 6, 8,
+};
+
+static unsigned int rsnd_soc_hw_rate_list[] = {
+	  8000,
+	 11025,
+	 16000,
+	 22050,
+	 32000,
+	 44100,
+	 48000,
+	 64000,
+	 88200,
+	 96000,
+	176400,
+	192000,
+};
+
+static int rsnd_soc_hw_rule(struct rsnd_priv *priv,
+			    unsigned int *list, int list_num,
+			    struct snd_interval *baseline, struct snd_interval *iv)
+{
+	struct snd_interval p;
+	unsigned int rate;
+	int i;
+
+	snd_interval_any(&p);
+	p.min = UINT_MAX;
+	p.max = 0;
+
+	for (i = 0; i < list_num; i++) {
+
+		if (!snd_interval_test(iv, list[i]))
+			continue;
+
+		rate = rsnd_ssi_clk_query(priv,
+					  baseline->min, list[i], NULL);
+		if (rate > 0) {
+			p.min = min(p.min, list[i]);
+			p.max = max(p.max, list[i]);
+		}
+
+		rate = rsnd_ssi_clk_query(priv,
+					  baseline->max, list[i], NULL);
+		if (rate > 0) {
+			p.min = min(p.min, list[i]);
+			p.max = max(p.max, list[i]);
+		}
+	}
+
+	return snd_interval_refine(iv, &p);
+}
+
+static int __rsnd_soc_hw_rule_rate(struct snd_pcm_hw_params *params,
+				   struct snd_pcm_hw_rule *rule,
+				   int is_play)
+{
+	struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval ic;
+	struct snd_soc_dai *dai = rule->private;
+	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+	struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+	struct rsnd_dai_stream *io = is_play ? &rdai->playback : &rdai->capture;
+
+	/*
+	 * possible sampling rate limitation is same as
+	 * 2ch if it supports multi ssi
+	 * and same as 8ch if TDM 6ch (see rsnd_ssi_config_init())
+	 */
+	ic = *ic_;
+	ic.min =
+	ic.max = rsnd_runtime_channel_for_ssi_with_params(io, params);
+
+	return rsnd_soc_hw_rule(priv, rsnd_soc_hw_rate_list,
+				ARRAY_SIZE(rsnd_soc_hw_rate_list),
+				&ic, ir);
+}
+
+static int rsnd_soc_hw_rule_rate_playback(struct snd_pcm_hw_params *params,
+				 struct snd_pcm_hw_rule *rule)
+{
+	return __rsnd_soc_hw_rule_rate(params, rule, 1);
+}
+
+static int rsnd_soc_hw_rule_rate_capture(struct snd_pcm_hw_params *params,
+					  struct snd_pcm_hw_rule *rule)
+{
+	return __rsnd_soc_hw_rule_rate(params, rule, 0);
+}
+
+static int __rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params,
+				       struct snd_pcm_hw_rule *rule,
+				       int is_play)
+{
+	struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval ic;
+	struct snd_soc_dai *dai = rule->private;
+	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+	struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+	struct rsnd_dai_stream *io = is_play ? &rdai->playback : &rdai->capture;
+
+	/*
+	 * possible sampling rate limitation is same as
+	 * 2ch if it supports multi ssi
+	 * and same as 8ch if TDM 6ch (see rsnd_ssi_config_init())
+	 */
+	ic = *ic_;
+	ic.min =
+	ic.max = rsnd_runtime_channel_for_ssi_with_params(io, params);
+
+	return rsnd_soc_hw_rule(priv, rsnd_soc_hw_channels_list,
+				ARRAY_SIZE(rsnd_soc_hw_channels_list),
+				ir, &ic);
+}
+
+static int rsnd_soc_hw_rule_channels_playback(struct snd_pcm_hw_params *params,
+					      struct snd_pcm_hw_rule *rule)
+{
+	return __rsnd_soc_hw_rule_channels(params, rule, 1);
+}
+
+static int rsnd_soc_hw_rule_channels_capture(struct snd_pcm_hw_params *params,
+					     struct snd_pcm_hw_rule *rule)
+{
+	return __rsnd_soc_hw_rule_channels(params, rule, 0);
+}
+
+static const struct snd_pcm_hardware rsnd_pcm_hardware = {
+	.info =		SNDRV_PCM_INFO_INTERLEAVED	|
+			SNDRV_PCM_INFO_MMAP		|
+			SNDRV_PCM_INFO_MMAP_VALID,
+	.buffer_bytes_max	= 64 * 1024,
+	.period_bytes_min	= 32,
+	.period_bytes_max	= 8192,
+	.periods_min		= 1,
+	.periods_max		= 32,
+	.fifo_size		= 256,
+};
+
+static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+	struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+	struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+	struct snd_pcm_hw_constraint_list *constraint = &rdai->constraint;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned int max_channels = rsnd_rdai_channels_get(rdai);
+	int ret;
+	int i;
+
+	rsnd_dai_stream_init(io, substream);
+
+	/*
+	 * Channel Limitation
+	 * It depends on Platform design
+	 */
+	constraint->list	= rsnd_soc_hw_channels_list;
+	constraint->count	= 0;
+	constraint->mask	= 0;
+
+	for (i = 0; i < ARRAY_SIZE(rsnd_soc_hw_channels_list); i++) {
+		if (rsnd_soc_hw_channels_list[i] > max_channels)
+			break;
+		constraint->count = i + 1;
+	}
+
+	snd_soc_set_runtime_hwparams(substream, &rsnd_pcm_hardware);
+
+	snd_pcm_hw_constraint_list(runtime, 0,
+				   SNDRV_PCM_HW_PARAM_CHANNELS, constraint);
+
+	snd_pcm_hw_constraint_integer(runtime,
+				      SNDRV_PCM_HW_PARAM_PERIODS);
+
+	/*
+	 * Sampling Rate / Channel Limitation
+	 * It depends on Clock Master Mode
+	 */
+	if (rsnd_rdai_is_clk_master(rdai)) {
+		int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+		snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				    is_play ? rsnd_soc_hw_rule_rate_playback :
+					      rsnd_soc_hw_rule_rate_capture,
+				    dai,
+				    SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+		snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+				    is_play ? rsnd_soc_hw_rule_channels_playback :
+					      rsnd_soc_hw_rule_channels_capture,
+				    dai,
+				    SNDRV_PCM_HW_PARAM_RATE, -1);
+	}
+
+	/*
+	 * call rsnd_dai_call without spinlock
+	 */
+	ret = rsnd_dai_call(nolock_start, io, priv);
+	if (ret < 0)
+		rsnd_dai_call(nolock_stop, io, priv);
+
+	return ret;
+}
+
+static void rsnd_soc_dai_shutdown(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+	struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+	struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+
+	/*
+	 * call rsnd_dai_call without spinlock
+	 */
+	rsnd_dai_call(nolock_stop, io, priv);
+
+	rsnd_dai_stream_quit(io);
+}
+
+static int rsnd_soc_dai_prepare(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
+	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+	struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+
+	return rsnd_dai_call(prepare, io, priv);
+}
+
+static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
+	.startup	= rsnd_soc_dai_startup,
+	.shutdown	= rsnd_soc_dai_shutdown,
+	.trigger	= rsnd_soc_dai_trigger,
+	.set_fmt	= rsnd_soc_dai_set_fmt,
+	.set_tdm_slot	= rsnd_soc_set_dai_tdm_slot,
+	.prepare	= rsnd_soc_dai_prepare,
+};
+
+void rsnd_parse_connect_common(struct rsnd_dai *rdai,
+		struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
+		struct device_node *node,
+		struct device_node *playback,
+		struct device_node *capture)
+{
+	struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+	struct device_node *np;
+	struct rsnd_mod *mod;
+	int i;
+
+	if (!node)
+		return;
+
+	i = 0;
+	for_each_child_of_node(node, np) {
+		mod = mod_get(priv, i);
+		if (np == playback)
+			rsnd_dai_connect(mod, &rdai->playback, mod->type);
+		if (np == capture)
+			rsnd_dai_connect(mod, &rdai->capture, mod->type);
+		i++;
+	}
+
+	of_node_put(node);
+}
+
+static struct device_node *rsnd_dai_of_node(struct rsnd_priv *priv,
+					    int *is_graph)
+{
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct device_node *np = dev->of_node;
+	struct device_node *dai_node;
+	struct device_node *ret;
+
+	*is_graph = 0;
+
+	/*
+	 * parse both previous dai (= rcar_sound,dai), and
+	 * graph dai (= ports/port)
+	 */
+	dai_node = of_get_child_by_name(np, RSND_NODE_DAI);
+	if (dai_node) {
+		ret = dai_node;
+		goto of_node_compatible;
+	}
+
+	ret = np;
+
+	dai_node = of_graph_get_next_endpoint(np, NULL);
+	if (dai_node)
+		goto of_node_graph;
+
+	return NULL;
+
+of_node_graph:
+	*is_graph = 1;
+of_node_compatible:
+	of_node_put(dai_node);
+
+	return ret;
+}
+
+static void __rsnd_dai_probe(struct rsnd_priv *priv,
+			     struct device_node *dai_np,
+			     int dai_i)
+{
+	struct device_node *playback, *capture;
+	struct rsnd_dai_stream *io_playback;
+	struct rsnd_dai_stream *io_capture;
+	struct snd_soc_dai_driver *drv;
+	struct rsnd_dai *rdai;
+	struct device *dev = rsnd_priv_to_dev(priv);
+	int io_i;
+
+	rdai		= rsnd_rdai_get(priv, dai_i);
+	drv		= rsnd_daidrv_get(priv, dai_i);
+	io_playback	= &rdai->playback;
+	io_capture	= &rdai->capture;
+
+	snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i);
+
+	rdai->priv	= priv;
+	drv->name	= rdai->name;
+	drv->ops	= &rsnd_soc_dai_ops;
+
+	snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE,
+		 "DAI%d Playback", dai_i);
+	drv->playback.rates		= RSND_RATES;
+	drv->playback.formats		= RSND_FMTS;
+	drv->playback.channels_min	= 2;
+	drv->playback.channels_max	= 8;
+	drv->playback.stream_name	= rdai->playback.name;
+
+	snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE,
+		 "DAI%d Capture", dai_i);
+	drv->capture.rates		= RSND_RATES;
+	drv->capture.formats		= RSND_FMTS;
+	drv->capture.channels_min	= 2;
+	drv->capture.channels_max	= 8;
+	drv->capture.stream_name	= rdai->capture.name;
+
+	rdai->playback.rdai		= rdai;
+	rdai->capture.rdai		= rdai;
+	rsnd_rdai_channels_set(rdai, 2); /* default 2ch */
+	rsnd_rdai_ssi_lane_set(rdai, 1); /* default 1lane */
+
+	for (io_i = 0;; io_i++) {
+		playback = of_parse_phandle(dai_np, "playback", io_i);
+		capture  = of_parse_phandle(dai_np, "capture", io_i);
+
+		if (!playback && !capture)
+			break;
+
+		rsnd_parse_connect_ssi(rdai, playback, capture);
+		rsnd_parse_connect_src(rdai, playback, capture);
+		rsnd_parse_connect_ctu(rdai, playback, capture);
+		rsnd_parse_connect_mix(rdai, playback, capture);
+		rsnd_parse_connect_dvc(rdai, playback, capture);
+
+		of_node_put(playback);
+		of_node_put(capture);
+	}
+
+	if (rsnd_ssi_is_pin_sharing(io_capture) ||
+	    rsnd_ssi_is_pin_sharing(io_playback)) {
+		/* should have symmetric_rates if pin sharing */
+		drv->symmetric_rates = 1;
+	}
+
+	dev_dbg(dev, "%s (%s/%s)\n", rdai->name,
+		rsnd_io_to_mod_ssi(io_playback) ? "play"    : " -- ",
+		rsnd_io_to_mod_ssi(io_capture) ? "capture" : "  --   ");
+}
+
+static int rsnd_dai_probe(struct rsnd_priv *priv)
+{
+	struct device_node *dai_node;
+	struct device_node *dai_np;
+	struct snd_soc_dai_driver *rdrv;
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_dai *rdai;
+	int nr;
+	int is_graph;
+	int dai_i;
+
+	dai_node = rsnd_dai_of_node(priv, &is_graph);
+	if (is_graph)
+		nr = of_graph_get_endpoint_count(dai_node);
+	else
+		nr = of_get_child_count(dai_node);
+
+	if (!nr)
+		return -EINVAL;
+
+	rdrv = devm_kcalloc(dev, nr, sizeof(*rdrv), GFP_KERNEL);
+	rdai = devm_kcalloc(dev, nr, sizeof(*rdai), GFP_KERNEL);
+	if (!rdrv || !rdai)
+		return -ENOMEM;
+
+	priv->rdai_nr	= nr;
+	priv->daidrv	= rdrv;
+	priv->rdai	= rdai;
+
+	/*
+	 * parse all dai
+	 */
+	dai_i = 0;
+	if (is_graph) {
+		for_each_endpoint_of_node(dai_node, dai_np) {
+			__rsnd_dai_probe(priv, dai_np, dai_i);
+			rsnd_ssi_parse_hdmi_connection(priv, dai_np, dai_i);
+			dai_i++;
+		}
+	} else {
+		for_each_child_of_node(dai_node, dai_np)
+			__rsnd_dai_probe(priv, dai_np, dai_i++);
+	}
+
+	return 0;
+}
+
+/*
+ *		pcm ops
+ */
+static int rsnd_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
+	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+	struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+	int ret;
+
+	ret = rsnd_dai_call(hw_params, io, substream, hw_params);
+	if (ret)
+		return ret;
+
+	return snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+}
+
+static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
+	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+	struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+	snd_pcm_uframes_t pointer = 0;
+
+	rsnd_dai_call(pointer, io, &pointer);
+
+	return pointer;
+}
+
+static const struct snd_pcm_ops rsnd_pcm_ops = {
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= rsnd_hw_params,
+	.hw_free	= snd_pcm_lib_free_pages,
+	.pointer	= rsnd_pointer,
+};
+
+/*
+ *		snd_kcontrol
+ */
+static int rsnd_kctrl_info(struct snd_kcontrol *kctrl,
+			   struct snd_ctl_elem_info *uinfo)
+{
+	struct rsnd_kctrl_cfg *cfg = snd_kcontrol_chip(kctrl);
+
+	if (cfg->texts) {
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+		uinfo->count = cfg->size;
+		uinfo->value.enumerated.items = cfg->max;
+		if (uinfo->value.enumerated.item >= cfg->max)
+			uinfo->value.enumerated.item = cfg->max - 1;
+		strlcpy(uinfo->value.enumerated.name,
+			cfg->texts[uinfo->value.enumerated.item],
+			sizeof(uinfo->value.enumerated.name));
+	} else {
+		uinfo->count = cfg->size;
+		uinfo->value.integer.min = 0;
+		uinfo->value.integer.max = cfg->max;
+		uinfo->type = (cfg->max == 1) ?
+			SNDRV_CTL_ELEM_TYPE_BOOLEAN :
+			SNDRV_CTL_ELEM_TYPE_INTEGER;
+	}
+
+	return 0;
+}
+
+static int rsnd_kctrl_get(struct snd_kcontrol *kctrl,
+			  struct snd_ctl_elem_value *uc)
+{
+	struct rsnd_kctrl_cfg *cfg = snd_kcontrol_chip(kctrl);
+	int i;
+
+	for (i = 0; i < cfg->size; i++)
+		if (cfg->texts)
+			uc->value.enumerated.item[i] = cfg->val[i];
+		else
+			uc->value.integer.value[i] = cfg->val[i];
+
+	return 0;
+}
+
+static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
+			  struct snd_ctl_elem_value *uc)
+{
+	struct rsnd_kctrl_cfg *cfg = snd_kcontrol_chip(kctrl);
+	int i, change = 0;
+
+	if (!cfg->accept(cfg->io))
+		return 0;
+
+	for (i = 0; i < cfg->size; i++) {
+		if (cfg->texts) {
+			change |= (uc->value.enumerated.item[i] != cfg->val[i]);
+			cfg->val[i] = uc->value.enumerated.item[i];
+		} else {
+			change |= (uc->value.integer.value[i] != cfg->val[i]);
+			cfg->val[i] = uc->value.integer.value[i];
+		}
+	}
+
+	if (change && cfg->update)
+		cfg->update(cfg->io, cfg->mod);
+
+	return change;
+}
+
+int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io)
+{
+	return 1;
+}
+
+int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io)
+{
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+
+	return !!runtime;
+}
+
+struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg)
+{
+	cfg->cfg.val = cfg->val;
+
+	return &cfg->cfg;
+}
+
+struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg)
+{
+	cfg->cfg.val = &cfg->val;
+
+	return &cfg->cfg;
+}
+
+const char * const volume_ramp_rate[] = {
+	"128 dB/1 step",	 /* 00000 */
+	"64 dB/1 step",		 /* 00001 */
+	"32 dB/1 step",		 /* 00010 */
+	"16 dB/1 step",		 /* 00011 */
+	"8 dB/1 step",		 /* 00100 */
+	"4 dB/1 step",		 /* 00101 */
+	"2 dB/1 step",		 /* 00110 */
+	"1 dB/1 step",		 /* 00111 */
+	"0.5 dB/1 step",	 /* 01000 */
+	"0.25 dB/1 step",	 /* 01001 */
+	"0.125 dB/1 step",	 /* 01010 = VOLUME_RAMP_MAX_MIX */
+	"0.125 dB/2 steps",	 /* 01011 */
+	"0.125 dB/4 steps",	 /* 01100 */
+	"0.125 dB/8 steps",	 /* 01101 */
+	"0.125 dB/16 steps",	 /* 01110 */
+	"0.125 dB/32 steps",	 /* 01111 */
+	"0.125 dB/64 steps",	 /* 10000 */
+	"0.125 dB/128 steps",	 /* 10001 */
+	"0.125 dB/256 steps",	 /* 10010 */
+	"0.125 dB/512 steps",	 /* 10011 */
+	"0.125 dB/1024 steps",	 /* 10100 */
+	"0.125 dB/2048 steps",	 /* 10101 */
+	"0.125 dB/4096 steps",	 /* 10110 */
+	"0.125 dB/8192 steps",	 /* 10111 = VOLUME_RAMP_MAX_DVC */
+};
+
+int rsnd_kctrl_new(struct rsnd_mod *mod,
+		   struct rsnd_dai_stream *io,
+		   struct snd_soc_pcm_runtime *rtd,
+		   const unsigned char *name,
+		   int (*accept)(struct rsnd_dai_stream *io),
+		   void (*update)(struct rsnd_dai_stream *io,
+				  struct rsnd_mod *mod),
+		   struct rsnd_kctrl_cfg *cfg,
+		   const char * const *texts,
+		   int size,
+		   u32 max)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_kcontrol *kctrl;
+	struct snd_kcontrol_new knew = {
+		.iface		= SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name		= name,
+		.info		= rsnd_kctrl_info,
+		.index		= rtd->num,
+		.get		= rsnd_kctrl_get,
+		.put		= rsnd_kctrl_put,
+	};
+	int ret;
+
+	if (size > RSND_MAX_CHANNELS)
+		return -EINVAL;
+
+	kctrl = snd_ctl_new1(&knew, cfg);
+	if (!kctrl)
+		return -ENOMEM;
+
+	ret = snd_ctl_add(card, kctrl);
+	if (ret < 0)
+		return ret;
+
+	cfg->texts	= texts;
+	cfg->max	= max;
+	cfg->size	= size;
+	cfg->accept	= accept;
+	cfg->update	= update;
+	cfg->card	= card;
+	cfg->kctrl	= kctrl;
+	cfg->io		= io;
+	cfg->mod	= mod;
+
+	return 0;
+}
+
+/*
+ *		snd_soc_component
+ */
+
+#define PREALLOC_BUFFER		(32 * 1024)
+#define PREALLOC_BUFFER_MAX	(32 * 1024)
+
+static int rsnd_preallocate_pages(struct snd_soc_pcm_runtime *rtd,
+				  struct rsnd_dai_stream *io,
+				  int stream)
+{
+	struct rsnd_priv *priv = rsnd_io_to_priv(io);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct snd_pcm_substream *substream;
+	int err;
+
+	/*
+	 * use Audio-DMAC dev if we can use IPMMU
+	 * see
+	 *	rsnd_dmaen_attach()
+	 */
+	if (io->dmac_dev)
+		dev = io->dmac_dev;
+
+	for (substream = rtd->pcm->streams[stream].substream;
+	     substream;
+	     substream = substream->next) {
+		err = snd_pcm_lib_preallocate_pages(substream,
+					SNDRV_DMA_TYPE_DEV,
+					dev,
+					PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dai *dai = rtd->cpu_dai;
+	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+	int ret;
+
+	ret = rsnd_dai_call(pcm_new, &rdai->playback, rtd);
+	if (ret)
+		return ret;
+
+	ret = rsnd_dai_call(pcm_new, &rdai->capture, rtd);
+	if (ret)
+		return ret;
+
+	ret = rsnd_preallocate_pages(rtd, &rdai->playback,
+				     SNDRV_PCM_STREAM_PLAYBACK);
+	if (ret)
+		return ret;
+
+	ret = rsnd_preallocate_pages(rtd, &rdai->capture,
+				     SNDRV_PCM_STREAM_CAPTURE);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct snd_soc_component_driver rsnd_soc_component = {
+	.ops		= &rsnd_pcm_ops,
+	.pcm_new	= rsnd_pcm_new,
+	.name		= "rsnd",
+};
+
+static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
+				       struct rsnd_dai_stream *io)
+{
+	int ret;
+
+	ret = rsnd_dai_call(probe, io, priv);
+	if (ret == -EAGAIN) {
+		struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+		struct rsnd_mod *mod;
+		int i;
+
+		/*
+		 * Fallback to PIO mode
+		 */
+
+		/*
+		 * call "remove" for SSI/SRC/DVC
+		 * SSI will be switch to PIO mode if it was DMA mode
+		 * see
+		 *	rsnd_dma_init()
+		 *	rsnd_ssi_fallback()
+		 */
+		rsnd_dai_call(remove, io, priv);
+
+		/*
+		 * remove all mod from io
+		 * and, re connect ssi
+		 */
+		for_each_rsnd_mod(i, mod, io)
+			rsnd_dai_disconnect(mod, io, i);
+		rsnd_dai_connect(ssi_mod, io, RSND_MOD_SSI);
+
+		/*
+		 * fallback
+		 */
+		rsnd_dai_call(fallback, io, priv);
+
+		/*
+		 * retry to "probe".
+		 * DAI has SSI which is PIO mode only now.
+		 */
+		ret = rsnd_dai_call(probe, io, priv);
+	}
+
+	return ret;
+}
+
+/*
+ *	rsnd probe
+ */
+static int rsnd_probe(struct platform_device *pdev)
+{
+	struct rsnd_priv *priv;
+	struct device *dev = &pdev->dev;
+	struct rsnd_dai *rdai;
+	int (*probe_func[])(struct rsnd_priv *priv) = {
+		rsnd_gen_probe,
+		rsnd_dma_probe,
+		rsnd_ssi_probe,
+		rsnd_ssiu_probe,
+		rsnd_src_probe,
+		rsnd_ctu_probe,
+		rsnd_mix_probe,
+		rsnd_dvc_probe,
+		rsnd_cmd_probe,
+		rsnd_adg_probe,
+		rsnd_dai_probe,
+	};
+	int ret, i;
+
+	/*
+	 *	init priv data
+	 */
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENODEV;
+
+	priv->pdev	= pdev;
+	priv->flags	= (unsigned long)of_device_get_match_data(dev);
+	spin_lock_init(&priv->lock);
+
+	/*
+	 *	init each module
+	 */
+	for (i = 0; i < ARRAY_SIZE(probe_func); i++) {
+		ret = probe_func[i](priv);
+		if (ret)
+			return ret;
+	}
+
+	for_each_rsnd_dai(rdai, priv, i) {
+		ret = rsnd_rdai_continuance_probe(priv, &rdai->playback);
+		if (ret)
+			goto exit_snd_probe;
+
+		ret = rsnd_rdai_continuance_probe(priv, &rdai->capture);
+		if (ret)
+			goto exit_snd_probe;
+	}
+
+	dev_set_drvdata(dev, priv);
+
+	/*
+	 *	asoc register
+	 */
+	ret = devm_snd_soc_register_component(dev, &rsnd_soc_component,
+					 priv->daidrv, rsnd_rdai_nr(priv));
+	if (ret < 0) {
+		dev_err(dev, "cannot snd dai register\n");
+		goto exit_snd_probe;
+	}
+
+	pm_runtime_enable(dev);
+
+	dev_info(dev, "probed\n");
+	return ret;
+
+exit_snd_probe:
+	for_each_rsnd_dai(rdai, priv, i) {
+		rsnd_dai_call(remove, &rdai->playback, priv);
+		rsnd_dai_call(remove, &rdai->capture, priv);
+	}
+
+	/*
+	 * adg is very special mod which can't use rsnd_dai_call(remove),
+	 * and it registers ADG clock on probe.
+	 * It should be unregister if probe failed.
+	 * Mainly it is assuming -EPROBE_DEFER case
+	 */
+	rsnd_adg_remove(priv);
+
+	return ret;
+}
+
+static int rsnd_remove(struct platform_device *pdev)
+{
+	struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev);
+	struct rsnd_dai *rdai;
+	void (*remove_func[])(struct rsnd_priv *priv) = {
+		rsnd_ssi_remove,
+		rsnd_ssiu_remove,
+		rsnd_src_remove,
+		rsnd_ctu_remove,
+		rsnd_mix_remove,
+		rsnd_dvc_remove,
+		rsnd_cmd_remove,
+		rsnd_adg_remove,
+	};
+	int ret = 0, i;
+
+	snd_soc_disconnect_sync(&pdev->dev);
+
+	pm_runtime_disable(&pdev->dev);
+
+	for_each_rsnd_dai(rdai, priv, i) {
+		ret |= rsnd_dai_call(remove, &rdai->playback, priv);
+		ret |= rsnd_dai_call(remove, &rdai->capture, priv);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(remove_func); i++)
+		remove_func[i](priv);
+
+	return ret;
+}
+
+static int __maybe_unused rsnd_suspend(struct device *dev)
+{
+	struct rsnd_priv *priv = dev_get_drvdata(dev);
+
+	rsnd_adg_clk_disable(priv);
+
+	return 0;
+}
+
+static int __maybe_unused rsnd_resume(struct device *dev)
+{
+	struct rsnd_priv *priv = dev_get_drvdata(dev);
+
+	rsnd_adg_clk_enable(priv);
+
+	return 0;
+}
+
+static const struct dev_pm_ops rsnd_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(rsnd_suspend, rsnd_resume)
+};
+
+static struct platform_driver rsnd_driver = {
+	.driver	= {
+		.name	= "rcar_sound",
+		.pm	= &rsnd_pm_ops,
+		.of_match_table = rsnd_of_match,
+	},
+	.probe		= rsnd_probe,
+	.remove		= rsnd_remove,
+};
+module_platform_driver(rsnd_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Renesas R-Car audio driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_ALIAS("platform:rcar-pcm-audio");
diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c
new file mode 100644
index 0000000..6a55aa7
--- /dev/null
+++ b/sound/soc/sh/rcar/ctu.c
@@ -0,0 +1,431 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ctu.c
+//
+// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+#include "rsnd.h"
+
+#define CTU_NAME_SIZE	16
+#define CTU_NAME "ctu"
+
+/*
+ * User needs to setup CTU by amixer, and its settings are
+ * based on below registers
+ *
+ * CTUn_CPMDR : amixser set "CTU Pass"
+ * CTUn_SV0xR : amixser set "CTU SV0"
+ * CTUn_SV1xR : amixser set "CTU SV1"
+ * CTUn_SV2xR : amixser set "CTU SV2"
+ * CTUn_SV3xR : amixser set "CTU SV3"
+ *
+ * [CTU Pass]
+ * 0000: default
+ * 0001: Connect input data of channel 0
+ * 0010: Connect input data of channel 1
+ * 0011: Connect input data of channel 2
+ * 0100: Connect input data of channel 3
+ * 0101: Connect input data of channel 4
+ * 0110: Connect input data of channel 5
+ * 0111: Connect input data of channel 6
+ * 1000: Connect input data of channel 7
+ * 1001: Connect calculated data by scale values of matrix row 0
+ * 1010: Connect calculated data by scale values of matrix row 1
+ * 1011: Connect calculated data by scale values of matrix row 2
+ * 1100: Connect calculated data by scale values of matrix row 3
+ *
+ * [CTU SVx]
+ * [Output0] = [SV00, SV01, SV02, SV03, SV04, SV05, SV06, SV07]
+ * [Output1] = [SV10, SV11, SV12, SV13, SV14, SV15, SV16, SV17]
+ * [Output2] = [SV20, SV21, SV22, SV23, SV24, SV25, SV26, SV27]
+ * [Output3] = [SV30, SV31, SV32, SV33, SV34, SV35, SV36, SV37]
+ * [Output4] = [ 0,   0,    0,    0,    0,    0,    0,    0   ]
+ * [Output5] = [ 0,   0,    0,    0,    0,    0,    0,    0   ]
+ * [Output6] = [ 0,   0,    0,    0,    0,    0,    0,    0   ]
+ * [Output7] = [ 0,   0,    0,    0,    0,    0,    0,    0   ]
+ *
+ * [SVxx]
+ * Plus					Minus
+ * value	time		dB	value		time		dB
+ * -----------------------------------------------------------------------
+ * H'7F_FFFF	2		6	H'80_0000	2		6
+ * ...
+ * H'40_0000	1		0	H'C0_0000	1		0
+ * ...
+ * H'00_0001	2.38 x 10^-7	-132
+ * H'00_0000	0		Mute	H'FF_FFFF	2.38 x 10^-7	-132
+ *
+ *
+ * Ex) Input ch -> Output ch
+ *	1ch     ->  0ch
+ *	0ch     ->  1ch
+ *
+ *	amixer set "CTU Reset" on
+ *	amixer set "CTU Pass" 9,10
+ *	amixer set "CTU SV0" 0,4194304
+ *	amixer set "CTU SV1" 4194304,0
+ * or
+ *	amixer set "CTU Reset" on
+ *	amixer set "CTU Pass" 2,1
+ */
+
+struct rsnd_ctu {
+	struct rsnd_mod mod;
+	struct rsnd_kctrl_cfg_m pass;
+	struct rsnd_kctrl_cfg_m sv0;
+	struct rsnd_kctrl_cfg_m sv1;
+	struct rsnd_kctrl_cfg_m sv2;
+	struct rsnd_kctrl_cfg_m sv3;
+	struct rsnd_kctrl_cfg_s reset;
+	int channels;
+	u32 flags;
+};
+
+#define KCTRL_INITIALIZED	(1 << 0)
+
+#define rsnd_ctu_nr(priv) ((priv)->ctu_nr)
+#define for_each_rsnd_ctu(pos, priv, i)					\
+	for ((i) = 0;							\
+	     ((i) < rsnd_ctu_nr(priv)) &&				\
+		     ((pos) = (struct rsnd_ctu *)(priv)->ctu + i);	\
+	     i++)
+
+#define rsnd_mod_to_ctu(_mod)	\
+	container_of((_mod), struct rsnd_ctu, mod)
+
+#define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id)
+
+static void rsnd_ctu_activation(struct rsnd_mod *mod)
+{
+	rsnd_mod_write(mod, CTU_SWRSR, 0);
+	rsnd_mod_write(mod, CTU_SWRSR, 1);
+}
+
+static void rsnd_ctu_halt(struct rsnd_mod *mod)
+{
+	rsnd_mod_write(mod, CTU_CTUIR, 1);
+	rsnd_mod_write(mod, CTU_SWRSR, 0);
+}
+
+int rsnd_ctu_converted_channel(struct rsnd_mod *mod)
+{
+	struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+
+	return ctu->channels;
+}
+
+static int rsnd_ctu_probe_(struct rsnd_mod *mod,
+			   struct rsnd_dai_stream *io,
+			   struct rsnd_priv *priv)
+{
+	return rsnd_cmd_attach(io, rsnd_mod_id(mod) / 4);
+}
+
+static void rsnd_ctu_value_init(struct rsnd_dai_stream *io,
+			       struct rsnd_mod *mod)
+{
+	struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+	u32 cpmdr = 0;
+	u32 scmdr = 0;
+	int i;
+
+	for (i = 0; i < RSND_MAX_CHANNELS; i++) {
+		u32 val = rsnd_kctrl_valm(ctu->pass, i);
+
+		cpmdr |= val << (28 - (i * 4));
+
+		if ((val > 0x8) && (scmdr < (val - 0x8)))
+			scmdr = val - 0x8;
+	}
+
+	rsnd_mod_write(mod, CTU_CTUIR, 1);
+
+	rsnd_mod_write(mod, CTU_ADINR, rsnd_runtime_channel_original(io));
+
+	rsnd_mod_write(mod, CTU_CPMDR, cpmdr);
+
+	rsnd_mod_write(mod, CTU_SCMDR, scmdr);
+
+	if (scmdr > 0) {
+		rsnd_mod_write(mod, CTU_SV00R, rsnd_kctrl_valm(ctu->sv0, 0));
+		rsnd_mod_write(mod, CTU_SV01R, rsnd_kctrl_valm(ctu->sv0, 1));
+		rsnd_mod_write(mod, CTU_SV02R, rsnd_kctrl_valm(ctu->sv0, 2));
+		rsnd_mod_write(mod, CTU_SV03R, rsnd_kctrl_valm(ctu->sv0, 3));
+		rsnd_mod_write(mod, CTU_SV04R, rsnd_kctrl_valm(ctu->sv0, 4));
+		rsnd_mod_write(mod, CTU_SV05R, rsnd_kctrl_valm(ctu->sv0, 5));
+		rsnd_mod_write(mod, CTU_SV06R, rsnd_kctrl_valm(ctu->sv0, 6));
+		rsnd_mod_write(mod, CTU_SV07R, rsnd_kctrl_valm(ctu->sv0, 7));
+	}
+	if (scmdr > 1) {
+		rsnd_mod_write(mod, CTU_SV10R, rsnd_kctrl_valm(ctu->sv1, 0));
+		rsnd_mod_write(mod, CTU_SV11R, rsnd_kctrl_valm(ctu->sv1, 1));
+		rsnd_mod_write(mod, CTU_SV12R, rsnd_kctrl_valm(ctu->sv1, 2));
+		rsnd_mod_write(mod, CTU_SV13R, rsnd_kctrl_valm(ctu->sv1, 3));
+		rsnd_mod_write(mod, CTU_SV14R, rsnd_kctrl_valm(ctu->sv1, 4));
+		rsnd_mod_write(mod, CTU_SV15R, rsnd_kctrl_valm(ctu->sv1, 5));
+		rsnd_mod_write(mod, CTU_SV16R, rsnd_kctrl_valm(ctu->sv1, 6));
+		rsnd_mod_write(mod, CTU_SV17R, rsnd_kctrl_valm(ctu->sv1, 7));
+	}
+	if (scmdr > 2) {
+		rsnd_mod_write(mod, CTU_SV20R, rsnd_kctrl_valm(ctu->sv2, 0));
+		rsnd_mod_write(mod, CTU_SV21R, rsnd_kctrl_valm(ctu->sv2, 1));
+		rsnd_mod_write(mod, CTU_SV22R, rsnd_kctrl_valm(ctu->sv2, 2));
+		rsnd_mod_write(mod, CTU_SV23R, rsnd_kctrl_valm(ctu->sv2, 3));
+		rsnd_mod_write(mod, CTU_SV24R, rsnd_kctrl_valm(ctu->sv2, 4));
+		rsnd_mod_write(mod, CTU_SV25R, rsnd_kctrl_valm(ctu->sv2, 5));
+		rsnd_mod_write(mod, CTU_SV26R, rsnd_kctrl_valm(ctu->sv2, 6));
+		rsnd_mod_write(mod, CTU_SV27R, rsnd_kctrl_valm(ctu->sv2, 7));
+	}
+	if (scmdr > 3) {
+		rsnd_mod_write(mod, CTU_SV30R, rsnd_kctrl_valm(ctu->sv3, 0));
+		rsnd_mod_write(mod, CTU_SV31R, rsnd_kctrl_valm(ctu->sv3, 1));
+		rsnd_mod_write(mod, CTU_SV32R, rsnd_kctrl_valm(ctu->sv3, 2));
+		rsnd_mod_write(mod, CTU_SV33R, rsnd_kctrl_valm(ctu->sv3, 3));
+		rsnd_mod_write(mod, CTU_SV34R, rsnd_kctrl_valm(ctu->sv3, 4));
+		rsnd_mod_write(mod, CTU_SV35R, rsnd_kctrl_valm(ctu->sv3, 5));
+		rsnd_mod_write(mod, CTU_SV36R, rsnd_kctrl_valm(ctu->sv3, 6));
+		rsnd_mod_write(mod, CTU_SV37R, rsnd_kctrl_valm(ctu->sv3, 7));
+	}
+
+	rsnd_mod_write(mod, CTU_CTUIR, 0);
+}
+
+static void rsnd_ctu_value_reset(struct rsnd_dai_stream *io,
+				 struct rsnd_mod *mod)
+{
+	struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+	int i;
+
+	if (!rsnd_kctrl_vals(ctu->reset))
+		return;
+
+	for (i = 0; i < RSND_MAX_CHANNELS; i++) {
+		rsnd_kctrl_valm(ctu->pass, i) = 0;
+		rsnd_kctrl_valm(ctu->sv0,  i) = 0;
+		rsnd_kctrl_valm(ctu->sv1,  i) = 0;
+		rsnd_kctrl_valm(ctu->sv2,  i) = 0;
+		rsnd_kctrl_valm(ctu->sv3,  i) = 0;
+	}
+	rsnd_kctrl_vals(ctu->reset) = 0;
+}
+
+static int rsnd_ctu_init(struct rsnd_mod *mod,
+			 struct rsnd_dai_stream *io,
+			 struct rsnd_priv *priv)
+{
+	rsnd_mod_power_on(mod);
+
+	rsnd_ctu_activation(mod);
+
+	rsnd_ctu_value_init(io, mod);
+
+	return 0;
+}
+
+static int rsnd_ctu_quit(struct rsnd_mod *mod,
+			 struct rsnd_dai_stream *io,
+			 struct rsnd_priv *priv)
+{
+	rsnd_ctu_halt(mod);
+
+	rsnd_mod_power_off(mod);
+
+	return 0;
+}
+
+static int rsnd_ctu_hw_params(struct rsnd_mod *mod,
+			      struct rsnd_dai_stream *io,
+			      struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *fe_params)
+{
+	struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+	struct snd_soc_pcm_runtime *fe = substream->private_data;
+
+	/*
+	 * CTU assumes that it is used under DPCM if user want to use
+	 * channel transfer. Then, CTU should be FE.
+	 * And then, this function will be called *after* BE settings.
+	 * this means, each BE already has fixuped hw_params.
+	 * see
+	 *	dpcm_fe_dai_hw_params()
+	 *	dpcm_be_dai_hw_params()
+	 */
+	ctu->channels = 0;
+	if (fe->dai_link->dynamic) {
+		struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+		struct device *dev = rsnd_priv_to_dev(priv);
+		struct snd_soc_dpcm *dpcm;
+		struct snd_pcm_hw_params *be_params;
+		int stream = substream->stream;
+
+		list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+			be_params = &dpcm->hw_params;
+			if (params_channels(fe_params) != params_channels(be_params))
+				ctu->channels = params_channels(be_params);
+		}
+
+		dev_dbg(dev, "CTU convert channels %d\n", ctu->channels);
+	}
+
+	return 0;
+}
+
+static int rsnd_ctu_pcm_new(struct rsnd_mod *mod,
+			    struct rsnd_dai_stream *io,
+			    struct snd_soc_pcm_runtime *rtd)
+{
+	struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+	int ret;
+
+	if (rsnd_flags_has(ctu, KCTRL_INITIALIZED))
+		return 0;
+
+	/* CTU Pass */
+	ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass",
+			       rsnd_kctrl_accept_anytime,
+			       NULL,
+			       &ctu->pass, RSND_MAX_CHANNELS,
+			       0xC);
+
+	/* ROW0 */
+	ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0",
+			       rsnd_kctrl_accept_anytime,
+			       NULL,
+			       &ctu->sv0, RSND_MAX_CHANNELS,
+			       0x00FFFFFF);
+	if (ret < 0)
+		return ret;
+
+	/* ROW1 */
+	ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1",
+			       rsnd_kctrl_accept_anytime,
+			       NULL,
+			       &ctu->sv1, RSND_MAX_CHANNELS,
+			       0x00FFFFFF);
+	if (ret < 0)
+		return ret;
+
+	/* ROW2 */
+	ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2",
+			       rsnd_kctrl_accept_anytime,
+			       NULL,
+			       &ctu->sv2, RSND_MAX_CHANNELS,
+			       0x00FFFFFF);
+	if (ret < 0)
+		return ret;
+
+	/* ROW3 */
+	ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3",
+			       rsnd_kctrl_accept_anytime,
+			       NULL,
+			       &ctu->sv3, RSND_MAX_CHANNELS,
+			       0x00FFFFFF);
+	if (ret < 0)
+		return ret;
+
+	/* Reset */
+	ret = rsnd_kctrl_new_s(mod, io, rtd, "CTU Reset",
+			       rsnd_kctrl_accept_anytime,
+			       rsnd_ctu_value_reset,
+			       &ctu->reset, 1);
+
+	rsnd_flags_set(ctu, KCTRL_INITIALIZED);
+
+	return ret;
+}
+
+static struct rsnd_mod_ops rsnd_ctu_ops = {
+	.name		= CTU_NAME,
+	.probe		= rsnd_ctu_probe_,
+	.init		= rsnd_ctu_init,
+	.quit		= rsnd_ctu_quit,
+	.hw_params	= rsnd_ctu_hw_params,
+	.pcm_new	= rsnd_ctu_pcm_new,
+};
+
+struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
+{
+	if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv)))
+		id = 0;
+
+	return rsnd_mod_get(rsnd_ctu_get(priv, id));
+}
+
+int rsnd_ctu_probe(struct rsnd_priv *priv)
+{
+	struct device_node *node;
+	struct device_node *np;
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_ctu *ctu;
+	struct clk *clk;
+	char name[CTU_NAME_SIZE];
+	int i, nr, ret;
+
+	/* This driver doesn't support Gen1 at this point */
+	if (rsnd_is_gen1(priv))
+		return 0;
+
+	node = rsnd_ctu_of_node(priv);
+	if (!node)
+		return 0; /* not used is not error */
+
+	nr = of_get_child_count(node);
+	if (!nr) {
+		ret = -EINVAL;
+		goto rsnd_ctu_probe_done;
+	}
+
+	ctu = devm_kcalloc(dev, nr, sizeof(*ctu), GFP_KERNEL);
+	if (!ctu) {
+		ret = -ENOMEM;
+		goto rsnd_ctu_probe_done;
+	}
+
+	priv->ctu_nr	= nr;
+	priv->ctu	= ctu;
+
+	i = 0;
+	ret = 0;
+	for_each_child_of_node(node, np) {
+		ctu = rsnd_ctu_get(priv, i);
+
+		/*
+		 * CTU00, CTU01, CTU02, CTU03 => CTU0
+		 * CTU10, CTU11, CTU12, CTU13 => CTU1
+		 */
+		snprintf(name, CTU_NAME_SIZE, "%s.%d",
+			 CTU_NAME, i / 4);
+
+		clk = devm_clk_get(dev, name);
+		if (IS_ERR(clk)) {
+			ret = PTR_ERR(clk);
+			of_node_put(np);
+			goto rsnd_ctu_probe_done;
+		}
+
+		ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops,
+				    clk, rsnd_mod_get_status, RSND_MOD_CTU, i);
+		if (ret) {
+			of_node_put(np);
+			goto rsnd_ctu_probe_done;
+		}
+
+		i++;
+	}
+
+
+rsnd_ctu_probe_done:
+	of_node_put(node);
+
+	return ret;
+}
+
+void rsnd_ctu_remove(struct rsnd_priv *priv)
+{
+	struct rsnd_ctu *ctu;
+	int i;
+
+	for_each_rsnd_ctu(ctu, priv, i) {
+		rsnd_mod_quit(rsnd_mod_get(ctu));
+	}
+}
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c
new file mode 100644
index 0000000..d65ea7b
--- /dev/null
+++ b/sound/soc/sh/rcar/dma.c
@@ -0,0 +1,802 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car Audio DMAC support
+//
+// Copyright (C) 2015 Renesas Electronics Corp.
+// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+#include <linux/delay.h>
+#include <linux/of_dma.h>
+#include "rsnd.h"
+
+/*
+ * Audio DMAC peri peri register
+ */
+#define PDMASAR		0x00
+#define PDMADAR		0x04
+#define PDMACHCR	0x0c
+
+/* PDMACHCR */
+#define PDMACHCR_DE		(1 << 0)
+
+
+struct rsnd_dmaen {
+	struct dma_chan		*chan;
+	dma_cookie_t		cookie;
+	unsigned int		dma_len;
+};
+
+struct rsnd_dmapp {
+	int			dmapp_id;
+	u32			chcr;
+};
+
+struct rsnd_dma {
+	struct rsnd_mod		mod;
+	struct rsnd_mod		*mod_from;
+	struct rsnd_mod		*mod_to;
+	dma_addr_t		src_addr;
+	dma_addr_t		dst_addr;
+	union {
+		struct rsnd_dmaen en;
+		struct rsnd_dmapp pp;
+	} dma;
+};
+
+struct rsnd_dma_ctrl {
+	void __iomem *base;
+	int dmaen_num;
+	int dmapp_num;
+};
+
+#define rsnd_priv_to_dmac(p)	((struct rsnd_dma_ctrl *)(p)->dma)
+#define rsnd_mod_to_dma(_mod) container_of((_mod), struct rsnd_dma, mod)
+#define rsnd_dma_to_dmaen(dma)	(&(dma)->dma.en)
+#define rsnd_dma_to_dmapp(dma)	(&(dma)->dma.pp)
+
+/* for DEBUG */
+static struct rsnd_mod_ops mem_ops = {
+	.name = "mem",
+};
+
+static struct rsnd_mod mem = {
+};
+
+/*
+ *		Audio DMAC
+ */
+static void __rsnd_dmaen_complete(struct rsnd_mod *mod,
+				  struct rsnd_dai_stream *io)
+{
+	if (rsnd_io_is_working(io))
+		rsnd_dai_period_elapsed(io);
+}
+
+static void rsnd_dmaen_complete(void *data)
+{
+	struct rsnd_mod *mod = data;
+
+	rsnd_mod_interrupt(mod, __rsnd_dmaen_complete);
+}
+
+static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io,
+						   struct rsnd_mod *mod_from,
+						   struct rsnd_mod *mod_to)
+{
+	if ((!mod_from && !mod_to) ||
+	    (mod_from && mod_to))
+		return NULL;
+
+	if (mod_from)
+		return rsnd_mod_dma_req(io, mod_from);
+	else
+		return rsnd_mod_dma_req(io, mod_to);
+}
+
+static int rsnd_dmaen_stop(struct rsnd_mod *mod,
+			   struct rsnd_dai_stream *io,
+			   struct rsnd_priv *priv)
+{
+	struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+	struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+
+	if (dmaen->chan)
+		dmaengine_terminate_all(dmaen->chan);
+
+	return 0;
+}
+
+static int rsnd_dmaen_nolock_stop(struct rsnd_mod *mod,
+				   struct rsnd_dai_stream *io,
+				   struct rsnd_priv *priv)
+{
+	struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+	struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+
+	/*
+	 * DMAEngine release uses mutex lock.
+	 * Thus, it shouldn't be called under spinlock.
+	 * Let's call it under nolock_start
+	 */
+	if (dmaen->chan)
+		dma_release_channel(dmaen->chan);
+
+	dmaen->chan = NULL;
+
+	return 0;
+}
+
+static int rsnd_dmaen_nolock_start(struct rsnd_mod *mod,
+			    struct rsnd_dai_stream *io,
+			    struct rsnd_priv *priv)
+{
+	struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+	struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+	struct device *dev = rsnd_priv_to_dev(priv);
+
+	if (dmaen->chan) {
+		dev_err(dev, "it already has dma channel\n");
+		return -EIO;
+	}
+
+	/*
+	 * DMAEngine request uses mutex lock.
+	 * Thus, it shouldn't be called under spinlock.
+	 * Let's call it under nolock_start
+	 */
+	dmaen->chan = rsnd_dmaen_request_channel(io,
+						 dma->mod_from,
+						 dma->mod_to);
+	if (IS_ERR_OR_NULL(dmaen->chan)) {
+		dmaen->chan = NULL;
+		dev_err(dev, "can't get dma channel\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int rsnd_dmaen_start(struct rsnd_mod *mod,
+			    struct rsnd_dai_stream *io,
+			    struct rsnd_priv *priv)
+{
+	struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+	struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+	struct snd_pcm_substream *substream = io->substream;
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct dma_async_tx_descriptor *desc;
+	struct dma_slave_config cfg = {};
+	int is_play = rsnd_io_is_play(io);
+	int ret;
+
+	cfg.direction	= is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
+	cfg.src_addr	= dma->src_addr;
+	cfg.dst_addr	= dma->dst_addr;
+	cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+	dev_dbg(dev, "%s[%d] %pad -> %pad\n",
+		rsnd_mod_name(mod), rsnd_mod_id(mod),
+		&cfg.src_addr, &cfg.dst_addr);
+
+	ret = dmaengine_slave_config(dmaen->chan, &cfg);
+	if (ret < 0)
+		return ret;
+
+	desc = dmaengine_prep_dma_cyclic(dmaen->chan,
+					 substream->runtime->dma_addr,
+					 snd_pcm_lib_buffer_bytes(substream),
+					 snd_pcm_lib_period_bytes(substream),
+					 is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+					 DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+
+	if (!desc) {
+		dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
+		return -EIO;
+	}
+
+	desc->callback		= rsnd_dmaen_complete;
+	desc->callback_param	= rsnd_mod_get(dma);
+
+	dmaen->dma_len		= snd_pcm_lib_buffer_bytes(substream);
+
+	dmaen->cookie = dmaengine_submit(desc);
+	if (dmaen->cookie < 0) {
+		dev_err(dev, "dmaengine_submit() fail\n");
+		return -EIO;
+	}
+
+	dma_async_issue_pending(dmaen->chan);
+
+	return 0;
+}
+
+struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
+					  struct rsnd_mod *mod, char *name)
+{
+	struct dma_chan *chan = NULL;
+	struct device_node *np;
+	int i = 0;
+
+	for_each_child_of_node(of_node, np) {
+		if (i == rsnd_mod_id(mod) && (!chan))
+			chan = of_dma_request_slave_channel(np, name);
+		i++;
+	}
+
+	/* It should call of_node_put(), since, it is rsnd_xxx_of_node() */
+	of_node_put(of_node);
+
+	return chan;
+}
+
+static int rsnd_dmaen_attach(struct rsnd_dai_stream *io,
+			   struct rsnd_dma *dma,
+			   struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
+{
+	struct rsnd_priv *priv = rsnd_io_to_priv(io);
+	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+	struct dma_chan *chan;
+
+	/* try to get DMAEngine channel */
+	chan = rsnd_dmaen_request_channel(io, mod_from, mod_to);
+	if (IS_ERR_OR_NULL(chan)) {
+		/* Let's follow when -EPROBE_DEFER case */
+		if (PTR_ERR(chan) == -EPROBE_DEFER)
+			return PTR_ERR(chan);
+
+		/*
+		 * DMA failed. try to PIO mode
+		 * see
+		 *	rsnd_ssi_fallback()
+		 *	rsnd_rdai_continuance_probe()
+		 */
+		return -EAGAIN;
+	}
+
+	/*
+	 * use it for IPMMU if needed
+	 * see
+	 *	rsnd_preallocate_pages()
+	 */
+	io->dmac_dev = chan->device->dev;
+
+	dma_release_channel(chan);
+
+	dmac->dmaen_num++;
+
+	return 0;
+}
+
+static int rsnd_dmaen_pointer(struct rsnd_mod *mod,
+			      struct rsnd_dai_stream *io,
+			      snd_pcm_uframes_t *pointer)
+{
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+	struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+	struct dma_tx_state state;
+	enum dma_status status;
+	unsigned int pos = 0;
+
+	status = dmaengine_tx_status(dmaen->chan, dmaen->cookie, &state);
+	if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) {
+		if (state.residue > 0 && state.residue <= dmaen->dma_len)
+			pos = dmaen->dma_len - state.residue;
+	}
+	*pointer = bytes_to_frames(runtime, pos);
+
+	return 0;
+}
+
+static struct rsnd_mod_ops rsnd_dmaen_ops = {
+	.name	= "audmac",
+	.nolock_start = rsnd_dmaen_nolock_start,
+	.nolock_stop  = rsnd_dmaen_nolock_stop,
+	.start	= rsnd_dmaen_start,
+	.stop	= rsnd_dmaen_stop,
+	.pointer= rsnd_dmaen_pointer,
+};
+
+/*
+ *		Audio DMAC peri peri
+ */
+static const u8 gen2_id_table_ssiu[] = {
+	0x00, /* SSI00 */
+	0x04, /* SSI10 */
+	0x08, /* SSI20 */
+	0x0c, /* SSI3  */
+	0x0d, /* SSI4  */
+	0x0e, /* SSI5  */
+	0x0f, /* SSI6  */
+	0x10, /* SSI7  */
+	0x11, /* SSI8  */
+	0x12, /* SSI90 */
+};
+static const u8 gen2_id_table_scu[] = {
+	0x2d, /* SCU_SRCI0 */
+	0x2e, /* SCU_SRCI1 */
+	0x2f, /* SCU_SRCI2 */
+	0x30, /* SCU_SRCI3 */
+	0x31, /* SCU_SRCI4 */
+	0x32, /* SCU_SRCI5 */
+	0x33, /* SCU_SRCI6 */
+	0x34, /* SCU_SRCI7 */
+	0x35, /* SCU_SRCI8 */
+	0x36, /* SCU_SRCI9 */
+};
+static const u8 gen2_id_table_cmd[] = {
+	0x37, /* SCU_CMD0 */
+	0x38, /* SCU_CMD1 */
+};
+
+static u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io,
+			     struct rsnd_mod *mod)
+{
+	struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
+	struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+	struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
+	const u8 *entry = NULL;
+	int id = rsnd_mod_id(mod);
+	int size = 0;
+
+	if (mod == ssi) {
+		entry = gen2_id_table_ssiu;
+		size = ARRAY_SIZE(gen2_id_table_ssiu);
+	} else if (mod == src) {
+		entry = gen2_id_table_scu;
+		size = ARRAY_SIZE(gen2_id_table_scu);
+	} else if (mod == dvc) {
+		entry = gen2_id_table_cmd;
+		size = ARRAY_SIZE(gen2_id_table_cmd);
+	}
+
+	if ((!entry) || (size <= id)) {
+		struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io));
+
+		dev_err(dev, "unknown connection (%s[%d])\n",
+			rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+		/* use non-prohibited SRS number as error */
+		return 0x00; /* SSI00 */
+	}
+
+	return entry[id];
+}
+
+static u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io,
+			       struct rsnd_mod *mod_from,
+			       struct rsnd_mod *mod_to)
+{
+	return	(rsnd_dmapp_get_id(io, mod_from) << 24) +
+		(rsnd_dmapp_get_id(io, mod_to) << 16);
+}
+
+#define rsnd_dmapp_addr(dmac, dma, reg) \
+	(dmac->base + 0x20 + reg + \
+	 (0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id))
+static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg)
+{
+	struct rsnd_mod *mod = rsnd_mod_get(dma);
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+	struct device *dev = rsnd_priv_to_dev(priv);
+
+	dev_dbg(dev, "w %p : %08x\n", rsnd_dmapp_addr(dmac, dma, reg), data);
+
+	iowrite32(data, rsnd_dmapp_addr(dmac, dma, reg));
+}
+
+static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg)
+{
+	struct rsnd_mod *mod = rsnd_mod_get(dma);
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+
+	return ioread32(rsnd_dmapp_addr(dmac, dma, reg));
+}
+
+static void rsnd_dmapp_bset(struct rsnd_dma *dma, u32 data, u32 mask, u32 reg)
+{
+	struct rsnd_mod *mod = rsnd_mod_get(dma);
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+	void __iomem *addr = rsnd_dmapp_addr(dmac, dma, reg);
+	u32 val = ioread32(addr);
+
+	val &= ~mask;
+	val |= (data & mask);
+
+	iowrite32(val, addr);
+}
+
+static int rsnd_dmapp_stop(struct rsnd_mod *mod,
+			   struct rsnd_dai_stream *io,
+			   struct rsnd_priv *priv)
+{
+	struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+	int i;
+
+	rsnd_dmapp_bset(dma, 0,  PDMACHCR_DE, PDMACHCR);
+
+	for (i = 0; i < 1024; i++) {
+		if (0 == (rsnd_dmapp_read(dma, PDMACHCR) & PDMACHCR_DE))
+			return 0;
+		udelay(1);
+	}
+
+	return -EIO;
+}
+
+static int rsnd_dmapp_start(struct rsnd_mod *mod,
+			    struct rsnd_dai_stream *io,
+			    struct rsnd_priv *priv)
+{
+	struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+	struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
+
+	rsnd_dmapp_write(dma, dma->src_addr,	PDMASAR);
+	rsnd_dmapp_write(dma, dma->dst_addr,	PDMADAR);
+	rsnd_dmapp_write(dma, dmapp->chcr,	PDMACHCR);
+
+	return 0;
+}
+
+static int rsnd_dmapp_attach(struct rsnd_dai_stream *io,
+			     struct rsnd_dma *dma,
+			     struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
+{
+	struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
+	struct rsnd_priv *priv = rsnd_io_to_priv(io);
+	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+	struct device *dev = rsnd_priv_to_dev(priv);
+
+	dmapp->dmapp_id = dmac->dmapp_num;
+	dmapp->chcr = rsnd_dmapp_get_chcr(io, mod_from, mod_to) | PDMACHCR_DE;
+
+	dmac->dmapp_num++;
+
+	dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n",
+		dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr);
+
+	return 0;
+}
+
+static struct rsnd_mod_ops rsnd_dmapp_ops = {
+	.name	= "audmac-pp",
+	.start	= rsnd_dmapp_start,
+	.stop	= rsnd_dmapp_stop,
+	.quit	= rsnd_dmapp_stop,
+};
+
+/*
+ *		Common DMAC Interface
+ */
+
+/*
+ *	DMA read/write register offset
+ *
+ *	RSND_xxx_I_N	for Audio DMAC input
+ *	RSND_xxx_O_N	for Audio DMAC output
+ *	RSND_xxx_I_P	for Audio DMAC peri peri input
+ *	RSND_xxx_O_P	for Audio DMAC peri peri output
+ *
+ *	ex) R-Car H2 case
+ *	      mod        / DMAC in    / DMAC out   / DMAC PP in / DMAC pp out
+ *	SSI : 0xec541000 / 0xec241008 / 0xec24100c
+ *	SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000
+ *	SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000
+ *	CMD : 0xec500000 /            / 0xec008000                0xec308000
+ */
+#define RDMA_SSI_I_N(addr, i)	(addr ##_reg - 0x00300000 + (0x40 * i) + 0x8)
+#define RDMA_SSI_O_N(addr, i)	(addr ##_reg - 0x00300000 + (0x40 * i) + 0xc)
+
+#define RDMA_SSIU_I_N(addr, i)	(addr ##_reg - 0x00441000 + (0x1000 * i))
+#define RDMA_SSIU_O_N(addr, i)	(addr ##_reg - 0x00441000 + (0x1000 * i))
+
+#define RDMA_SSIU_I_P(addr, i)	(addr ##_reg - 0x00141000 + (0x1000 * i))
+#define RDMA_SSIU_O_P(addr, i)	(addr ##_reg - 0x00141000 + (0x1000 * i))
+
+#define RDMA_SRC_I_N(addr, i)	(addr ##_reg - 0x00500000 + (0x400 * i))
+#define RDMA_SRC_O_N(addr, i)	(addr ##_reg - 0x004fc000 + (0x400 * i))
+
+#define RDMA_SRC_I_P(addr, i)	(addr ##_reg - 0x00200000 + (0x400 * i))
+#define RDMA_SRC_O_P(addr, i)	(addr ##_reg - 0x001fc000 + (0x400 * i))
+
+#define RDMA_CMD_O_N(addr, i)	(addr ##_reg - 0x004f8000 + (0x400 * i))
+#define RDMA_CMD_O_P(addr, i)	(addr ##_reg - 0x001f8000 + (0x400 * i))
+
+static dma_addr_t
+rsnd_gen2_dma_addr(struct rsnd_dai_stream *io,
+		   struct rsnd_mod *mod,
+		   int is_play, int is_from)
+{
+	struct rsnd_priv *priv = rsnd_io_to_priv(io);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SSI);
+	phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU);
+	int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod);
+	int use_src = !!rsnd_io_to_mod_src(io);
+	int use_cmd = !!rsnd_io_to_mod_dvc(io) ||
+		      !!rsnd_io_to_mod_mix(io) ||
+		      !!rsnd_io_to_mod_ctu(io);
+	int id = rsnd_mod_id(mod);
+	struct dma_addr {
+		dma_addr_t out_addr;
+		dma_addr_t in_addr;
+	} dma_addrs[3][2][3] = {
+		/* SRC */
+		/* Capture */
+		{{{ 0,				0 },
+		  { RDMA_SRC_O_N(src, id),	RDMA_SRC_I_P(src, id) },
+		  { RDMA_CMD_O_N(src, id),	RDMA_SRC_I_P(src, id) } },
+		 /* Playback */
+		 {{ 0,				0, },
+		  { RDMA_SRC_O_P(src, id),	RDMA_SRC_I_N(src, id) },
+		  { RDMA_CMD_O_P(src, id),	RDMA_SRC_I_N(src, id) } }
+		},
+		/* SSI */
+		/* Capture */
+		{{{ RDMA_SSI_O_N(ssi, id),	0 },
+		  { RDMA_SSIU_O_P(ssi, id),	0 },
+		  { RDMA_SSIU_O_P(ssi, id),	0 } },
+		 /* Playback */
+		 {{ 0,				RDMA_SSI_I_N(ssi, id) },
+		  { 0,				RDMA_SSIU_I_P(ssi, id) },
+		  { 0,				RDMA_SSIU_I_P(ssi, id) } }
+		},
+		/* SSIU */
+		/* Capture */
+		{{{ RDMA_SSIU_O_N(ssi, id),	0 },
+		  { RDMA_SSIU_O_P(ssi, id),	0 },
+		  { RDMA_SSIU_O_P(ssi, id),	0 } },
+		 /* Playback */
+		 {{ 0,				RDMA_SSIU_I_N(ssi, id) },
+		  { 0,				RDMA_SSIU_I_P(ssi, id) },
+		  { 0,				RDMA_SSIU_I_P(ssi, id) } } },
+	};
+
+	/* it shouldn't happen */
+	if (use_cmd && !use_src)
+		dev_err(dev, "DVC is selected without SRC\n");
+
+	/* use SSIU or SSI ? */
+	if (is_ssi && rsnd_ssi_use_busif(io))
+		is_ssi++;
+
+	return (is_from) ?
+		dma_addrs[is_ssi][is_play][use_src + use_cmd].out_addr :
+		dma_addrs[is_ssi][is_play][use_src + use_cmd].in_addr;
+}
+
+static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io,
+				struct rsnd_mod *mod,
+				int is_play, int is_from)
+{
+	struct rsnd_priv *priv = rsnd_io_to_priv(io);
+
+	/*
+	 * gen1 uses default DMA addr
+	 */
+	if (rsnd_is_gen1(priv))
+		return 0;
+
+	if (!mod)
+		return 0;
+
+	return rsnd_gen2_dma_addr(io, mod, is_play, is_from);
+}
+
+#define MOD_MAX (RSND_MOD_MAX + 1) /* +Memory */
+static void rsnd_dma_of_path(struct rsnd_mod *this,
+			     struct rsnd_dai_stream *io,
+			     int is_play,
+			     struct rsnd_mod **mod_from,
+			     struct rsnd_mod **mod_to)
+{
+	struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
+	struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+	struct rsnd_mod *ctu = rsnd_io_to_mod_ctu(io);
+	struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
+	struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
+	struct rsnd_mod *mod[MOD_MAX];
+	struct rsnd_mod *mod_start, *mod_end;
+	struct rsnd_priv *priv = rsnd_mod_to_priv(this);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	int nr, i, idx;
+
+	if (!ssi)
+		return;
+
+	nr = 0;
+	for (i = 0; i < MOD_MAX; i++) {
+		mod[i] = NULL;
+		nr += !!rsnd_io_to_mod(io, i);
+	}
+
+	/*
+	 * [S] -*-> [E]
+	 * [S] -*-> SRC -o-> [E]
+	 * [S] -*-> SRC -> DVC -o-> [E]
+	 * [S] -*-> SRC -> CTU -> MIX -> DVC -o-> [E]
+	 *
+	 * playback	[S] = mem
+	 *		[E] = SSI
+	 *
+	 * capture	[S] = SSI
+	 *		[E] = mem
+	 *
+	 * -*->		Audio DMAC
+	 * -o->		Audio DMAC peri peri
+	 */
+	mod_start	= (is_play) ? NULL : ssi;
+	mod_end		= (is_play) ? ssi  : NULL;
+
+	idx = 0;
+	mod[idx++] = mod_start;
+	for (i = 1; i < nr; i++) {
+		if (src) {
+			mod[idx++] = src;
+			src = NULL;
+		} else if (ctu) {
+			mod[idx++] = ctu;
+			ctu = NULL;
+		} else if (mix) {
+			mod[idx++] = mix;
+			mix = NULL;
+		} else if (dvc) {
+			mod[idx++] = dvc;
+			dvc = NULL;
+		}
+	}
+	mod[idx] = mod_end;
+
+	/*
+	 *		| SSI | SRC |
+	 * -------------+-----+-----+
+	 *  is_play	|  o  |  *  |
+	 * !is_play	|  *  |  o  |
+	 */
+	if ((this == ssi) == (is_play)) {
+		*mod_from	= mod[idx - 1];
+		*mod_to		= mod[idx];
+	} else {
+		*mod_from	= mod[0];
+		*mod_to		= mod[1];
+	}
+
+	dev_dbg(dev, "module connection (this is %s[%d])\n",
+		rsnd_mod_name(this), rsnd_mod_id(this));
+	for (i = 0; i <= idx; i++) {
+		dev_dbg(dev, "  %s[%d]%s\n",
+			rsnd_mod_name(mod[i] ? mod[i] : &mem),
+			rsnd_mod_id  (mod[i] ? mod[i] : &mem),
+			(mod[i] == *mod_from) ? " from" :
+			(mod[i] == *mod_to)   ? " to" : "");
+	}
+}
+
+static int rsnd_dma_alloc(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
+			  struct rsnd_mod **dma_mod)
+{
+	struct rsnd_mod *mod_from = NULL;
+	struct rsnd_mod *mod_to = NULL;
+	struct rsnd_priv *priv = rsnd_io_to_priv(io);
+	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_dma *dma;
+	struct rsnd_mod_ops *ops;
+	enum rsnd_mod_type type;
+	int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma,
+		      struct rsnd_mod *mod_from, struct rsnd_mod *mod_to);
+	int is_play = rsnd_io_is_play(io);
+	int ret, dma_id;
+
+	/*
+	 * DMA failed. try to PIO mode
+	 * see
+	 *	rsnd_ssi_fallback()
+	 *	rsnd_rdai_continuance_probe()
+	 */
+	if (!dmac)
+		return -EAGAIN;
+
+	rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to);
+
+	/* for Gen2 or later */
+	if (mod_from && mod_to) {
+		ops	= &rsnd_dmapp_ops;
+		attach	= rsnd_dmapp_attach;
+		dma_id	= dmac->dmapp_num;
+		type	= RSND_MOD_AUDMAPP;
+	} else {
+		ops	= &rsnd_dmaen_ops;
+		attach	= rsnd_dmaen_attach;
+		dma_id	= dmac->dmaen_num;
+		type	= RSND_MOD_AUDMA;
+	}
+
+	/* for Gen1, overwrite */
+	if (rsnd_is_gen1(priv)) {
+		ops	= &rsnd_dmaen_ops;
+		attach	= rsnd_dmaen_attach;
+		dma_id	= dmac->dmaen_num;
+		type	= RSND_MOD_AUDMA;
+	}
+
+	dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
+	if (!dma)
+		return -ENOMEM;
+
+	*dma_mod = rsnd_mod_get(dma);
+
+	ret = rsnd_mod_init(priv, *dma_mod, ops, NULL,
+			    rsnd_mod_get_status, type, dma_id);
+	if (ret < 0)
+		return ret;
+
+	dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n",
+		rsnd_mod_name(*dma_mod), rsnd_mod_id(*dma_mod),
+		rsnd_mod_name(mod_from ? mod_from : &mem),
+		rsnd_mod_id  (mod_from ? mod_from : &mem),
+		rsnd_mod_name(mod_to   ? mod_to   : &mem),
+		rsnd_mod_id  (mod_to   ? mod_to   : &mem));
+
+	ret = attach(io, dma, mod_from, mod_to);
+	if (ret < 0)
+		return ret;
+
+	dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
+	dma->dst_addr = rsnd_dma_addr(io, mod_to,   is_play, 0);
+	dma->mod_from = mod_from;
+	dma->mod_to   = mod_to;
+
+	return 0;
+}
+
+int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
+		    struct rsnd_mod **dma_mod)
+{
+	if (!(*dma_mod)) {
+		int ret = rsnd_dma_alloc(io, mod, dma_mod);
+
+		if (ret < 0)
+			return ret;
+	}
+
+	return rsnd_dai_connect(*dma_mod, io, (*dma_mod)->type);
+}
+
+int rsnd_dma_probe(struct rsnd_priv *priv)
+{
+	struct platform_device *pdev = rsnd_priv_to_pdev(priv);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_dma_ctrl *dmac;
+	struct resource *res;
+
+	/*
+	 * for Gen1
+	 */
+	if (rsnd_is_gen1(priv))
+		return 0;
+
+	/*
+	 * for Gen2 or later
+	 */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp");
+	dmac = devm_kzalloc(dev, sizeof(*dmac), GFP_KERNEL);
+	if (!dmac || !res) {
+		dev_err(dev, "dma allocate failed\n");
+		return 0; /* it will be PIO mode */
+	}
+
+	dmac->dmapp_num = 0;
+	dmac->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(dmac->base))
+		return PTR_ERR(dmac->base);
+
+	priv->dma = dmac;
+
+	/* dummy mem mod for debug */
+	return rsnd_mod_init(NULL, &mem, &mem_ops, NULL, NULL, 0, 0);
+}
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c
new file mode 100644
index 0000000..2b16e0c
--- /dev/null
+++ b/sound/soc/sh/rcar/dvc.c
@@ -0,0 +1,391 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car DVC support
+//
+// Copyright (C) 2014 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+/*
+ * Playback Volume
+ *	amixer set "DVC Out" 100%
+ *
+ * Capture Volume
+ *	amixer set "DVC In" 100%
+ *
+ * Playback Mute
+ *	amixer set "DVC Out Mute" on
+ *
+ * Capture Mute
+ *	amixer set "DVC In Mute" on
+ *
+ * Volume Ramp
+ *	amixer set "DVC Out Ramp Up Rate"   "0.125 dB/64 steps"
+ *	amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps"
+ *	amixer set "DVC Out Ramp" on
+ *	aplay xxx.wav &
+ *	amixer set "DVC Out"  80%  // Volume Down
+ *	amixer set "DVC Out" 100%  // Volume Up
+ */
+
+#include "rsnd.h"
+
+#define RSND_DVC_NAME_SIZE	16
+
+#define DVC_NAME "dvc"
+
+struct rsnd_dvc {
+	struct rsnd_mod mod;
+	struct rsnd_kctrl_cfg_m volume;
+	struct rsnd_kctrl_cfg_m mute;
+	struct rsnd_kctrl_cfg_s ren;	/* Ramp Enable */
+	struct rsnd_kctrl_cfg_s rup;	/* Ramp Rate Up */
+	struct rsnd_kctrl_cfg_s rdown;	/* Ramp Rate Down */
+	u32 flags;
+};
+
+#define KCTRL_INITIALIZED	(1 << 0)
+
+#define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id)
+#define rsnd_dvc_nr(priv) ((priv)->dvc_nr)
+
+#define rsnd_mod_to_dvc(_mod)	\
+	container_of((_mod), struct rsnd_dvc, mod)
+
+#define for_each_rsnd_dvc(pos, priv, i)				\
+	for ((i) = 0;						\
+	     ((i) < rsnd_dvc_nr(priv)) &&			\
+	     ((pos) = (struct rsnd_dvc *)(priv)->dvc + i);	\
+	     i++)
+
+static void rsnd_dvc_activation(struct rsnd_mod *mod)
+{
+	rsnd_mod_write(mod, DVC_SWRSR, 0);
+	rsnd_mod_write(mod, DVC_SWRSR, 1);
+}
+
+static void rsnd_dvc_halt(struct rsnd_mod *mod)
+{
+	rsnd_mod_write(mod, DVC_DVUIR, 1);
+	rsnd_mod_write(mod, DVC_SWRSR, 0);
+}
+
+#define rsnd_dvc_get_vrpdr(dvc) (rsnd_kctrl_vals(dvc->rup) << 8 | \
+				 rsnd_kctrl_vals(dvc->rdown))
+#define rsnd_dvc_get_vrdbr(dvc) (0x3ff - (rsnd_kctrl_valm(dvc->volume, 0) >> 13))
+
+static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io,
+					      struct rsnd_mod *mod)
+{
+	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
+	u32 val[RSND_MAX_CHANNELS];
+	int i;
+
+	/* Enable Ramp */
+	if (rsnd_kctrl_vals(dvc->ren))
+		for (i = 0; i < RSND_MAX_CHANNELS; i++)
+			val[i] = rsnd_kctrl_max(dvc->volume);
+	else
+		for (i = 0; i < RSND_MAX_CHANNELS; i++)
+			val[i] = rsnd_kctrl_valm(dvc->volume, i);
+
+	/* Enable Digital Volume */
+	rsnd_mod_write(mod, DVC_VOL0R, val[0]);
+	rsnd_mod_write(mod, DVC_VOL1R, val[1]);
+	rsnd_mod_write(mod, DVC_VOL2R, val[2]);
+	rsnd_mod_write(mod, DVC_VOL3R, val[3]);
+	rsnd_mod_write(mod, DVC_VOL4R, val[4]);
+	rsnd_mod_write(mod, DVC_VOL5R, val[5]);
+	rsnd_mod_write(mod, DVC_VOL6R, val[6]);
+	rsnd_mod_write(mod, DVC_VOL7R, val[7]);
+}
+
+static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io,
+				 struct rsnd_mod *mod)
+{
+	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
+	u32 adinr = 0;
+	u32 dvucr = 0;
+	u32 vrctr = 0;
+	u32 vrpdr = 0;
+	u32 vrdbr = 0;
+
+	adinr = rsnd_get_adinr_bit(mod, io) |
+		rsnd_runtime_channel_after_ctu(io);
+
+	/* Enable Digital Volume, Zero Cross Mute Mode */
+	dvucr |= 0x101;
+
+	/* Enable Ramp */
+	if (rsnd_kctrl_vals(dvc->ren)) {
+		dvucr |= 0x10;
+
+		/*
+		 * FIXME !!
+		 * use scale-downed Digital Volume
+		 * as Volume Ramp
+		 * 7F FFFF -> 3FF
+		 */
+		vrctr = 0xff;
+		vrpdr = rsnd_dvc_get_vrpdr(dvc);
+		vrdbr = rsnd_dvc_get_vrdbr(dvc);
+	}
+
+	/* Initialize operation */
+	rsnd_mod_write(mod, DVC_DVUIR, 1);
+
+	/* General Information */
+	rsnd_mod_write(mod, DVC_ADINR, adinr);
+	rsnd_mod_write(mod, DVC_DVUCR, dvucr);
+
+	/* Volume Ramp Parameter */
+	rsnd_mod_write(mod, DVC_VRCTR, vrctr);
+	rsnd_mod_write(mod, DVC_VRPDR, vrpdr);
+	rsnd_mod_write(mod, DVC_VRDBR, vrdbr);
+
+	/* Digital Volume Function Parameter */
+	rsnd_dvc_volume_parameter(io, mod);
+
+	/* cancel operation */
+	rsnd_mod_write(mod, DVC_DVUIR, 0);
+}
+
+static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io,
+				   struct rsnd_mod *mod)
+{
+	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
+	u32 zcmcr = 0;
+	u32 vrpdr = 0;
+	u32 vrdbr = 0;
+	int i;
+
+	for (i = 0; i < rsnd_kctrl_size(dvc->mute); i++)
+		zcmcr |= (!!rsnd_kctrl_valm(dvc->mute, i)) << i;
+
+	if (rsnd_kctrl_vals(dvc->ren)) {
+		vrpdr = rsnd_dvc_get_vrpdr(dvc);
+		vrdbr = rsnd_dvc_get_vrdbr(dvc);
+	}
+
+	/* Disable DVC Register access */
+	rsnd_mod_write(mod, DVC_DVUER, 0);
+
+	/* Zero Cross Mute Function */
+	rsnd_mod_write(mod, DVC_ZCMCR, zcmcr);
+
+	/* Volume Ramp Function */
+	rsnd_mod_write(mod, DVC_VRPDR, vrpdr);
+	rsnd_mod_write(mod, DVC_VRDBR, vrdbr);
+	/* add DVC_VRWTR here */
+
+	/* Digital Volume Function Parameter */
+	rsnd_dvc_volume_parameter(io, mod);
+
+	/* Enable DVC Register access */
+	rsnd_mod_write(mod, DVC_DVUER, 1);
+}
+
+static int rsnd_dvc_probe_(struct rsnd_mod *mod,
+			   struct rsnd_dai_stream *io,
+			   struct rsnd_priv *priv)
+{
+	return rsnd_cmd_attach(io, rsnd_mod_id(mod));
+}
+
+static int rsnd_dvc_init(struct rsnd_mod *mod,
+			 struct rsnd_dai_stream *io,
+			 struct rsnd_priv *priv)
+{
+	rsnd_mod_power_on(mod);
+
+	rsnd_dvc_activation(mod);
+
+	rsnd_dvc_volume_init(io, mod);
+
+	rsnd_dvc_volume_update(io, mod);
+
+	return 0;
+}
+
+static int rsnd_dvc_quit(struct rsnd_mod *mod,
+			 struct rsnd_dai_stream *io,
+			 struct rsnd_priv *priv)
+{
+	rsnd_dvc_halt(mod);
+
+	rsnd_mod_power_off(mod);
+
+	return 0;
+}
+
+static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
+			    struct rsnd_dai_stream *io,
+			    struct snd_soc_pcm_runtime *rtd)
+{
+	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
+	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+	int is_play = rsnd_io_is_play(io);
+	int channels = rsnd_rdai_channels_get(rdai);
+	int ret;
+
+	if (rsnd_flags_has(dvc, KCTRL_INITIALIZED))
+		return 0;
+
+	/* Volume */
+	ret = rsnd_kctrl_new_m(mod, io, rtd,
+			is_play ?
+			"DVC Out Playback Volume" : "DVC In Capture Volume",
+			rsnd_kctrl_accept_anytime,
+			rsnd_dvc_volume_update,
+			&dvc->volume, channels,
+			0x00800000 - 1);
+	if (ret < 0)
+		return ret;
+
+	/* Mute */
+	ret = rsnd_kctrl_new_m(mod, io, rtd,
+			is_play ?
+			"DVC Out Mute Switch" : "DVC In Mute Switch",
+			rsnd_kctrl_accept_anytime,
+			rsnd_dvc_volume_update,
+			&dvc->mute, channels,
+			1);
+	if (ret < 0)
+		return ret;
+
+	/* Ramp */
+	ret = rsnd_kctrl_new_s(mod, io, rtd,
+			is_play ?
+			"DVC Out Ramp Switch" : "DVC In Ramp Switch",
+			rsnd_kctrl_accept_anytime,
+			rsnd_dvc_volume_update,
+			&dvc->ren, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = rsnd_kctrl_new_e(mod, io, rtd,
+			is_play ?
+			"DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate",
+			rsnd_kctrl_accept_anytime,
+			rsnd_dvc_volume_update,
+			&dvc->rup,
+			volume_ramp_rate,
+			VOLUME_RAMP_MAX_DVC);
+	if (ret < 0)
+		return ret;
+
+	ret = rsnd_kctrl_new_e(mod, io, rtd,
+			is_play ?
+			"DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate",
+			rsnd_kctrl_accept_anytime,
+			rsnd_dvc_volume_update,
+			&dvc->rdown,
+			volume_ramp_rate,
+			VOLUME_RAMP_MAX_DVC);
+
+	if (ret < 0)
+		return ret;
+
+	rsnd_flags_set(dvc, KCTRL_INITIALIZED);
+
+	return 0;
+}
+
+static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io,
+					 struct rsnd_mod *mod)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+
+	return rsnd_dma_request_channel(rsnd_dvc_of_node(priv),
+					mod, "tx");
+}
+
+static struct rsnd_mod_ops rsnd_dvc_ops = {
+	.name		= DVC_NAME,
+	.dma_req	= rsnd_dvc_dma_req,
+	.probe		= rsnd_dvc_probe_,
+	.init		= rsnd_dvc_init,
+	.quit		= rsnd_dvc_quit,
+	.pcm_new	= rsnd_dvc_pcm_new,
+};
+
+struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
+{
+	if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv)))
+		id = 0;
+
+	return rsnd_mod_get(rsnd_dvc_get(priv, id));
+}
+
+int rsnd_dvc_probe(struct rsnd_priv *priv)
+{
+	struct device_node *node;
+	struct device_node *np;
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_dvc *dvc;
+	struct clk *clk;
+	char name[RSND_DVC_NAME_SIZE];
+	int i, nr, ret;
+
+	/* This driver doesn't support Gen1 at this point */
+	if (rsnd_is_gen1(priv))
+		return 0;
+
+	node = rsnd_dvc_of_node(priv);
+	if (!node)
+		return 0; /* not used is not error */
+
+	nr = of_get_child_count(node);
+	if (!nr) {
+		ret = -EINVAL;
+		goto rsnd_dvc_probe_done;
+	}
+
+	dvc	= devm_kcalloc(dev, nr, sizeof(*dvc), GFP_KERNEL);
+	if (!dvc) {
+		ret = -ENOMEM;
+		goto rsnd_dvc_probe_done;
+	}
+
+	priv->dvc_nr	= nr;
+	priv->dvc	= dvc;
+
+	i = 0;
+	ret = 0;
+	for_each_child_of_node(node, np) {
+		dvc = rsnd_dvc_get(priv, i);
+
+		snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d",
+			 DVC_NAME, i);
+
+		clk = devm_clk_get(dev, name);
+		if (IS_ERR(clk)) {
+			ret = PTR_ERR(clk);
+			of_node_put(np);
+			goto rsnd_dvc_probe_done;
+		}
+
+		ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
+				    clk, rsnd_mod_get_status, RSND_MOD_DVC, i);
+		if (ret) {
+			of_node_put(np);
+			goto rsnd_dvc_probe_done;
+		}
+
+		i++;
+	}
+
+rsnd_dvc_probe_done:
+	of_node_put(node);
+
+	return ret;
+}
+
+void rsnd_dvc_remove(struct rsnd_priv *priv)
+{
+	struct rsnd_dvc *dvc;
+	int i;
+
+	for_each_rsnd_dvc(dvc, priv, i) {
+		rsnd_mod_quit(rsnd_mod_get(dvc));
+	}
+}
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
new file mode 100644
index 0000000..0230301
--- /dev/null
+++ b/sound/soc/sh/rcar/gen.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car Gen1 SRU/SSI support
+//
+// Copyright (C) 2013 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+/*
+ * #define DEBUG
+ *
+ * you can also add below in
+ * ${LINUX}/drivers/base/regmap/regmap.c
+ * for regmap debug
+ *
+ * #define LOG_DEVICE "xxxx.rcar_sound"
+ */
+
+#include "rsnd.h"
+
+struct rsnd_gen {
+	struct rsnd_gen_ops *ops;
+
+	/* RSND_BASE_MAX base */
+	void __iomem *base[RSND_BASE_MAX];
+	phys_addr_t res[RSND_BASE_MAX];
+	struct regmap *regmap[RSND_BASE_MAX];
+
+	/* RSND_REG_MAX base */
+	struct regmap_field *regs[RSND_REG_MAX];
+	const char *reg_name[RSND_REG_MAX];
+};
+
+#define rsnd_priv_to_gen(p)	((struct rsnd_gen *)(p)->gen)
+#define rsnd_reg_name(gen, id)	((gen)->reg_name[id])
+
+struct rsnd_regmap_field_conf {
+	int idx;
+	unsigned int reg_offset;
+	unsigned int id_offset;
+	const char *reg_name;
+};
+
+#define RSND_REG_SET(id, offset, _id_offset, n)	\
+{						\
+	.idx = id,				\
+	.reg_offset = offset,			\
+	.id_offset = _id_offset,		\
+	.reg_name = n,				\
+}
+/* single address mapping */
+#define RSND_GEN_S_REG(id, offset)	\
+	RSND_REG_SET(RSND_REG_##id, offset, 0, #id)
+
+/* multi address mapping */
+#define RSND_GEN_M_REG(id, offset, _id_offset)	\
+	RSND_REG_SET(RSND_REG_##id, offset, _id_offset, #id)
+
+/*
+ *		basic function
+ */
+static int rsnd_is_accessible_reg(struct rsnd_priv *priv,
+				  struct rsnd_gen *gen, enum rsnd_reg reg)
+{
+	if (!gen->regs[reg]) {
+		struct device *dev = rsnd_priv_to_dev(priv);
+
+		dev_err(dev, "unsupported register access %x\n", reg);
+		return 0;
+	}
+
+	return 1;
+}
+
+u32 rsnd_read(struct rsnd_priv *priv,
+	      struct rsnd_mod *mod, enum rsnd_reg reg)
+{
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+	u32 val;
+
+	if (!rsnd_is_accessible_reg(priv, gen, reg))
+		return 0;
+
+	regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
+
+	dev_dbg(dev, "r %s[%d] - %-18s (%4d) : %08x\n",
+		rsnd_mod_name(mod), rsnd_mod_id(mod),
+		rsnd_reg_name(gen, reg), reg, val);
+
+	return val;
+}
+
+void rsnd_write(struct rsnd_priv *priv,
+		struct rsnd_mod *mod,
+		enum rsnd_reg reg, u32 data)
+{
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+
+	if (!rsnd_is_accessible_reg(priv, gen, reg))
+		return;
+
+	regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data);
+
+	dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n",
+		rsnd_mod_name(mod), rsnd_mod_id(mod),
+		rsnd_reg_name(gen, reg), reg, data);
+}
+
+void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
+	       enum rsnd_reg reg, u32 mask, u32 data)
+{
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+
+	if (!rsnd_is_accessible_reg(priv, gen, reg))
+		return;
+
+	regmap_fields_force_update_bits(gen->regs[reg],
+					rsnd_mod_id(mod), mask, data);
+
+	dev_dbg(dev, "b %s[%d] - %-18s (%4d) : %08x/%08x\n",
+		rsnd_mod_name(mod), rsnd_mod_id(mod),
+		rsnd_reg_name(gen, reg), reg, data, mask);
+
+}
+
+phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id)
+{
+	struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+
+	return	gen->res[reg_id];
+}
+
+#define rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf)		\
+	_rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf, ARRAY_SIZE(conf))
+static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
+				 int id_size,
+				 int reg_id,
+				 const char *name,
+				 const struct rsnd_regmap_field_conf *conf,
+				 int conf_size)
+{
+	struct platform_device *pdev = rsnd_priv_to_pdev(priv);
+	struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct resource *res;
+	struct regmap_config regc;
+	struct regmap_field *regs;
+	struct regmap *regmap;
+	struct reg_field regf;
+	void __iomem *base;
+	int i;
+
+	memset(&regc, 0, sizeof(regc));
+	regc.reg_bits = 32;
+	regc.val_bits = 32;
+	regc.reg_stride = 4;
+	regc.name = name;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+	if (!res)
+		res = platform_get_resource(pdev, IORESOURCE_MEM, reg_id);
+	if (!res)
+		return -ENODEV;
+
+	base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	regmap = devm_regmap_init_mmio(dev, base, &regc);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	/* RSND_BASE_MAX base */
+	gen->base[reg_id] = base;
+	gen->regmap[reg_id] = regmap;
+	gen->res[reg_id] = res->start;
+
+	for (i = 0; i < conf_size; i++) {
+
+		regf.reg	= conf[i].reg_offset;
+		regf.id_offset	= conf[i].id_offset;
+		regf.lsb	= 0;
+		regf.msb	= 31;
+		regf.id_size	= id_size;
+
+		regs = devm_regmap_field_alloc(dev, regmap, regf);
+		if (IS_ERR(regs))
+			return PTR_ERR(regs);
+
+		/* RSND_REG_MAX base */
+		gen->regs[conf[i].idx] = regs;
+		gen->reg_name[conf[i].idx] = conf[i].reg_name;
+	}
+
+	return 0;
+}
+
+/*
+ *		Gen2
+ */
+static int rsnd_gen2_probe(struct rsnd_priv *priv)
+{
+	static const struct rsnd_regmap_field_conf conf_ssiu[] = {
+		RSND_GEN_S_REG(SSI_MODE0,	0x800),
+		RSND_GEN_S_REG(SSI_MODE1,	0x804),
+		RSND_GEN_S_REG(SSI_MODE2,	0x808),
+		RSND_GEN_S_REG(SSI_CONTROL,	0x810),
+		RSND_GEN_S_REG(SSI_SYS_STATUS0,	0x840),
+		RSND_GEN_S_REG(SSI_SYS_STATUS1,	0x844),
+		RSND_GEN_S_REG(SSI_SYS_STATUS2,	0x848),
+		RSND_GEN_S_REG(SSI_SYS_STATUS3,	0x84c),
+		RSND_GEN_S_REG(SSI_SYS_STATUS4,	0x880),
+		RSND_GEN_S_REG(SSI_SYS_STATUS5,	0x884),
+		RSND_GEN_S_REG(SSI_SYS_STATUS6,	0x888),
+		RSND_GEN_S_REG(SSI_SYS_STATUS7,	0x88c),
+		RSND_GEN_S_REG(HDMI0_SEL,	0x9e0),
+		RSND_GEN_S_REG(HDMI1_SEL,	0x9e4),
+
+		/* FIXME: it needs SSI_MODE2/3 in the future */
+		RSND_GEN_M_REG(SSI_BUSIF_MODE,	0x0,	0x80),
+		RSND_GEN_M_REG(SSI_BUSIF_ADINR,	0x4,	0x80),
+		RSND_GEN_M_REG(SSI_BUSIF_DALIGN,0x8,	0x80),
+		RSND_GEN_M_REG(SSI_MODE,	0xc,	0x80),
+		RSND_GEN_M_REG(SSI_CTRL,	0x10,	0x80),
+		RSND_GEN_M_REG(SSI_INT_ENABLE,	0x18,	0x80),
+	};
+
+	static const struct rsnd_regmap_field_conf conf_scu[] = {
+		RSND_GEN_M_REG(SRC_I_BUSIF_MODE,0x0,	0x20),
+		RSND_GEN_M_REG(SRC_O_BUSIF_MODE,0x4,	0x20),
+		RSND_GEN_M_REG(SRC_BUSIF_DALIGN,0x8,	0x20),
+		RSND_GEN_M_REG(SRC_ROUTE_MODE0,	0xc,	0x20),
+		RSND_GEN_M_REG(SRC_CTRL,	0x10,	0x20),
+		RSND_GEN_M_REG(SRC_INT_ENABLE0,	0x18,	0x20),
+		RSND_GEN_M_REG(CMD_BUSIF_MODE,	0x184,	0x20),
+		RSND_GEN_M_REG(CMD_BUSIF_DALIGN,0x188,	0x20),
+		RSND_GEN_M_REG(CMD_ROUTE_SLCT,	0x18c,	0x20),
+		RSND_GEN_M_REG(CMD_CTRL,	0x190,	0x20),
+		RSND_GEN_S_REG(SCU_SYS_STATUS0,	0x1c8),
+		RSND_GEN_S_REG(SCU_SYS_INT_EN0,	0x1cc),
+		RSND_GEN_S_REG(SCU_SYS_STATUS1,	0x1d0),
+		RSND_GEN_S_REG(SCU_SYS_INT_EN1,	0x1d4),
+		RSND_GEN_M_REG(SRC_SWRSR,	0x200,	0x40),
+		RSND_GEN_M_REG(SRC_SRCIR,	0x204,	0x40),
+		RSND_GEN_M_REG(SRC_ADINR,	0x214,	0x40),
+		RSND_GEN_M_REG(SRC_IFSCR,	0x21c,	0x40),
+		RSND_GEN_M_REG(SRC_IFSVR,	0x220,	0x40),
+		RSND_GEN_M_REG(SRC_SRCCR,	0x224,	0x40),
+		RSND_GEN_M_REG(SRC_BSDSR,	0x22c,	0x40),
+		RSND_GEN_M_REG(SRC_BSISR,	0x238,	0x40),
+		RSND_GEN_M_REG(CTU_SWRSR,	0x500,	0x100),
+		RSND_GEN_M_REG(CTU_CTUIR,	0x504,	0x100),
+		RSND_GEN_M_REG(CTU_ADINR,	0x508,	0x100),
+		RSND_GEN_M_REG(CTU_CPMDR,	0x510,	0x100),
+		RSND_GEN_M_REG(CTU_SCMDR,	0x514,	0x100),
+		RSND_GEN_M_REG(CTU_SV00R,	0x518,	0x100),
+		RSND_GEN_M_REG(CTU_SV01R,	0x51c,	0x100),
+		RSND_GEN_M_REG(CTU_SV02R,	0x520,	0x100),
+		RSND_GEN_M_REG(CTU_SV03R,	0x524,	0x100),
+		RSND_GEN_M_REG(CTU_SV04R,	0x528,	0x100),
+		RSND_GEN_M_REG(CTU_SV05R,	0x52c,	0x100),
+		RSND_GEN_M_REG(CTU_SV06R,	0x530,	0x100),
+		RSND_GEN_M_REG(CTU_SV07R,	0x534,	0x100),
+		RSND_GEN_M_REG(CTU_SV10R,	0x538,	0x100),
+		RSND_GEN_M_REG(CTU_SV11R,	0x53c,	0x100),
+		RSND_GEN_M_REG(CTU_SV12R,	0x540,	0x100),
+		RSND_GEN_M_REG(CTU_SV13R,	0x544,	0x100),
+		RSND_GEN_M_REG(CTU_SV14R,	0x548,	0x100),
+		RSND_GEN_M_REG(CTU_SV15R,	0x54c,	0x100),
+		RSND_GEN_M_REG(CTU_SV16R,	0x550,	0x100),
+		RSND_GEN_M_REG(CTU_SV17R,	0x554,	0x100),
+		RSND_GEN_M_REG(CTU_SV20R,	0x558,	0x100),
+		RSND_GEN_M_REG(CTU_SV21R,	0x55c,	0x100),
+		RSND_GEN_M_REG(CTU_SV22R,	0x560,	0x100),
+		RSND_GEN_M_REG(CTU_SV23R,	0x564,	0x100),
+		RSND_GEN_M_REG(CTU_SV24R,	0x568,	0x100),
+		RSND_GEN_M_REG(CTU_SV25R,	0x56c,	0x100),
+		RSND_GEN_M_REG(CTU_SV26R,	0x570,	0x100),
+		RSND_GEN_M_REG(CTU_SV27R,	0x574,	0x100),
+		RSND_GEN_M_REG(CTU_SV30R,	0x578,	0x100),
+		RSND_GEN_M_REG(CTU_SV31R,	0x57c,	0x100),
+		RSND_GEN_M_REG(CTU_SV32R,	0x580,	0x100),
+		RSND_GEN_M_REG(CTU_SV33R,	0x584,	0x100),
+		RSND_GEN_M_REG(CTU_SV34R,	0x588,	0x100),
+		RSND_GEN_M_REG(CTU_SV35R,	0x58c,	0x100),
+		RSND_GEN_M_REG(CTU_SV36R,	0x590,	0x100),
+		RSND_GEN_M_REG(CTU_SV37R,	0x594,	0x100),
+		RSND_GEN_M_REG(MIX_SWRSR,	0xd00,	0x40),
+		RSND_GEN_M_REG(MIX_MIXIR,	0xd04,	0x40),
+		RSND_GEN_M_REG(MIX_ADINR,	0xd08,	0x40),
+		RSND_GEN_M_REG(MIX_MIXMR,	0xd10,	0x40),
+		RSND_GEN_M_REG(MIX_MVPDR,	0xd14,	0x40),
+		RSND_GEN_M_REG(MIX_MDBAR,	0xd18,	0x40),
+		RSND_GEN_M_REG(MIX_MDBBR,	0xd1c,	0x40),
+		RSND_GEN_M_REG(MIX_MDBCR,	0xd20,	0x40),
+		RSND_GEN_M_REG(MIX_MDBDR,	0xd24,	0x40),
+		RSND_GEN_M_REG(MIX_MDBER,	0xd28,	0x40),
+		RSND_GEN_M_REG(DVC_SWRSR,	0xe00,	0x100),
+		RSND_GEN_M_REG(DVC_DVUIR,	0xe04,	0x100),
+		RSND_GEN_M_REG(DVC_ADINR,	0xe08,	0x100),
+		RSND_GEN_M_REG(DVC_DVUCR,	0xe10,	0x100),
+		RSND_GEN_M_REG(DVC_ZCMCR,	0xe14,	0x100),
+		RSND_GEN_M_REG(DVC_VRCTR,	0xe18,	0x100),
+		RSND_GEN_M_REG(DVC_VRPDR,	0xe1c,	0x100),
+		RSND_GEN_M_REG(DVC_VRDBR,	0xe20,	0x100),
+		RSND_GEN_M_REG(DVC_VOL0R,	0xe28,	0x100),
+		RSND_GEN_M_REG(DVC_VOL1R,	0xe2c,	0x100),
+		RSND_GEN_M_REG(DVC_VOL2R,	0xe30,	0x100),
+		RSND_GEN_M_REG(DVC_VOL3R,	0xe34,	0x100),
+		RSND_GEN_M_REG(DVC_VOL4R,	0xe38,	0x100),
+		RSND_GEN_M_REG(DVC_VOL5R,	0xe3c,	0x100),
+		RSND_GEN_M_REG(DVC_VOL6R,	0xe40,	0x100),
+		RSND_GEN_M_REG(DVC_VOL7R,	0xe44,	0x100),
+		RSND_GEN_M_REG(DVC_DVUER,	0xe48,	0x100),
+	};
+	static const struct rsnd_regmap_field_conf conf_adg[] = {
+		RSND_GEN_S_REG(BRRA,		0x00),
+		RSND_GEN_S_REG(BRRB,		0x04),
+		RSND_GEN_S_REG(BRGCKR,		0x08),
+		RSND_GEN_S_REG(AUDIO_CLK_SEL0,	0x0c),
+		RSND_GEN_S_REG(AUDIO_CLK_SEL1,	0x10),
+		RSND_GEN_S_REG(AUDIO_CLK_SEL2,	0x14),
+		RSND_GEN_S_REG(DIV_EN,		0x30),
+		RSND_GEN_S_REG(SRCIN_TIMSEL0,	0x34),
+		RSND_GEN_S_REG(SRCIN_TIMSEL1,	0x38),
+		RSND_GEN_S_REG(SRCIN_TIMSEL2,	0x3c),
+		RSND_GEN_S_REG(SRCIN_TIMSEL3,	0x40),
+		RSND_GEN_S_REG(SRCIN_TIMSEL4,	0x44),
+		RSND_GEN_S_REG(SRCOUT_TIMSEL0,	0x48),
+		RSND_GEN_S_REG(SRCOUT_TIMSEL1,	0x4c),
+		RSND_GEN_S_REG(SRCOUT_TIMSEL2,	0x50),
+		RSND_GEN_S_REG(SRCOUT_TIMSEL3,	0x54),
+		RSND_GEN_S_REG(SRCOUT_TIMSEL4,	0x58),
+		RSND_GEN_S_REG(CMDOUT_TIMSEL,	0x5c),
+	};
+	static const struct rsnd_regmap_field_conf conf_ssi[] = {
+		RSND_GEN_M_REG(SSICR,		0x00,	0x40),
+		RSND_GEN_M_REG(SSISR,		0x04,	0x40),
+		RSND_GEN_M_REG(SSITDR,		0x08,	0x40),
+		RSND_GEN_M_REG(SSIRDR,		0x0c,	0x40),
+		RSND_GEN_M_REG(SSIWSR,		0x20,	0x40),
+	};
+	int ret_ssiu;
+	int ret_scu;
+	int ret_adg;
+	int ret_ssi;
+
+	ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSIU, "ssiu", conf_ssiu);
+	ret_scu  = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SCU,  "scu",  conf_scu);
+	ret_adg  = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_ADG,  "adg",  conf_adg);
+	ret_ssi  = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSI,  "ssi",  conf_ssi);
+	if (ret_ssiu < 0 ||
+	    ret_scu  < 0 ||
+	    ret_adg  < 0 ||
+	    ret_ssi  < 0)
+		return ret_ssiu | ret_scu | ret_adg | ret_ssi;
+
+	return 0;
+}
+
+/*
+ *		Gen1
+ */
+
+static int rsnd_gen1_probe(struct rsnd_priv *priv)
+{
+	static const struct rsnd_regmap_field_conf conf_adg[] = {
+		RSND_GEN_S_REG(BRRA,		0x00),
+		RSND_GEN_S_REG(BRRB,		0x04),
+		RSND_GEN_S_REG(BRGCKR,		0x08),
+		RSND_GEN_S_REG(AUDIO_CLK_SEL0,	0x0c),
+		RSND_GEN_S_REG(AUDIO_CLK_SEL1,	0x10),
+	};
+	static const struct rsnd_regmap_field_conf conf_ssi[] = {
+		RSND_GEN_M_REG(SSICR,		0x00,	0x40),
+		RSND_GEN_M_REG(SSISR,		0x04,	0x40),
+		RSND_GEN_M_REG(SSITDR,		0x08,	0x40),
+		RSND_GEN_M_REG(SSIRDR,		0x0c,	0x40),
+		RSND_GEN_M_REG(SSIWSR,		0x20,	0x40),
+	};
+	int ret_adg;
+	int ret_ssi;
+
+	ret_adg  = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG, "adg", conf_adg);
+	ret_ssi  = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, "ssi", conf_ssi);
+	if (ret_adg  < 0 ||
+	    ret_ssi  < 0)
+		return ret_adg | ret_ssi;
+
+	return 0;
+}
+
+/*
+ *		Gen
+ */
+int rsnd_gen_probe(struct rsnd_priv *priv)
+{
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_gen *gen;
+	int ret;
+
+	gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL);
+	if (!gen)
+		return -ENOMEM;
+
+	priv->gen = gen;
+
+	ret = -ENODEV;
+	if (rsnd_is_gen1(priv))
+		ret = rsnd_gen1_probe(priv);
+	else if (rsnd_is_gen2(priv) ||
+		 rsnd_is_gen3(priv))
+		ret = rsnd_gen2_probe(priv);
+
+	if (ret < 0)
+		dev_err(dev, "unknown generation R-Car sound device\n");
+
+	return ret;
+}
diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c
new file mode 100644
index 0000000..8e3b57e
--- /dev/null
+++ b/sound/soc/sh/rcar/mix.c
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mix.c
+//
+// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+/*
+ *		    CTUn	MIXn
+ *		    +------+	+------+
+ * [SRC3 / SRC6] -> |CTU n0| ->	[MIX n0| ->
+ * [SRC4 / SRC9] -> |CTU n1| ->	[MIX n1| ->
+ * [SRC0 / SRC1] -> |CTU n2| ->	[MIX n2| ->
+ * [SRC2 / SRC5] -> |CTU n3| ->	[MIX n3| ->
+ *		    +------+	+------+
+ *
+ * ex)
+ *	DAI0 : playback = <&src0 &ctu02 &mix0 &dvc0 &ssi0>;
+ *	DAI1 : playback = <&src2 &ctu03 &mix0 &dvc0 &ssi0>;
+ *
+ * MIX Volume
+ *	amixer set "MIX",0  100%  // DAI0 Volume
+ *	amixer set "MIX",1  100%  // DAI1 Volume
+ *
+ * Volume Ramp
+ *	amixer set "MIX Ramp Up Rate"   "0.125 dB/1 step"
+ *	amixer set "MIX Ramp Down Rate" "4 dB/1 step"
+ *	amixer set "MIX Ramp" on
+ *	aplay xxx.wav &
+ *	amixer set "MIX",0  80%  // DAI0 Volume Down
+ *	amixer set "MIX",1 100%  // DAI1 Volume Up
+ */
+
+#include "rsnd.h"
+
+#define MIX_NAME_SIZE	16
+#define MIX_NAME "mix"
+
+struct rsnd_mix {
+	struct rsnd_mod mod;
+	struct rsnd_kctrl_cfg_s volumeA; /* MDBAR */
+	struct rsnd_kctrl_cfg_s volumeB; /* MDBBR */
+	struct rsnd_kctrl_cfg_s volumeC; /* MDBCR */
+	struct rsnd_kctrl_cfg_s volumeD; /* MDBDR */
+	struct rsnd_kctrl_cfg_s ren;	/* Ramp Enable */
+	struct rsnd_kctrl_cfg_s rup;	/* Ramp Rate Up */
+	struct rsnd_kctrl_cfg_s rdw;	/* Ramp Rate Down */
+	u32 flags;
+};
+
+#define ONCE_KCTRL_INITIALIZED		(1 << 0)
+#define HAS_VOLA			(1 << 1)
+#define HAS_VOLB			(1 << 2)
+#define HAS_VOLC			(1 << 3)
+#define HAS_VOLD			(1 << 4)
+
+#define VOL_MAX				0x3ff
+
+#define rsnd_mod_to_mix(_mod)	\
+	container_of((_mod), struct rsnd_mix, mod)
+
+#define rsnd_mix_get(priv, id) ((struct rsnd_mix *)(priv->mix) + id)
+#define rsnd_mix_nr(priv) ((priv)->mix_nr)
+#define for_each_rsnd_mix(pos, priv, i)					\
+	for ((i) = 0;							\
+	     ((i) < rsnd_mix_nr(priv)) &&				\
+		     ((pos) = (struct rsnd_mix *)(priv)->mix + i);	\
+	     i++)
+
+static void rsnd_mix_activation(struct rsnd_mod *mod)
+{
+	rsnd_mod_write(mod, MIX_SWRSR, 0);
+	rsnd_mod_write(mod, MIX_SWRSR, 1);
+}
+
+static void rsnd_mix_halt(struct rsnd_mod *mod)
+{
+	rsnd_mod_write(mod, MIX_MIXIR, 1);
+	rsnd_mod_write(mod, MIX_SWRSR, 0);
+}
+
+#define rsnd_mix_get_vol(mix, X) \
+	rsnd_flags_has(mix, HAS_VOL##X) ? \
+		(VOL_MAX - rsnd_kctrl_vals(mix->volume##X)) : 0
+static void rsnd_mix_volume_parameter(struct rsnd_dai_stream *io,
+				      struct rsnd_mod *mod)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_mix *mix = rsnd_mod_to_mix(mod);
+	u32 volA = rsnd_mix_get_vol(mix, A);
+	u32 volB = rsnd_mix_get_vol(mix, B);
+	u32 volC = rsnd_mix_get_vol(mix, C);
+	u32 volD = rsnd_mix_get_vol(mix, D);
+
+	dev_dbg(dev, "MIX A/B/C/D = %02x/%02x/%02x/%02x\n",
+		volA, volB, volC, volD);
+
+	rsnd_mod_write(mod, MIX_MDBAR, volA);
+	rsnd_mod_write(mod, MIX_MDBBR, volB);
+	rsnd_mod_write(mod, MIX_MDBCR, volC);
+	rsnd_mod_write(mod, MIX_MDBDR, volD);
+}
+
+static void rsnd_mix_volume_init(struct rsnd_dai_stream *io,
+				 struct rsnd_mod *mod)
+{
+	struct rsnd_mix *mix = rsnd_mod_to_mix(mod);
+
+	rsnd_mod_write(mod, MIX_MIXIR, 1);
+
+	/* General Information */
+	rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io));
+
+	/* volume step */
+	rsnd_mod_write(mod, MIX_MIXMR, rsnd_kctrl_vals(mix->ren));
+	rsnd_mod_write(mod, MIX_MVPDR, rsnd_kctrl_vals(mix->rup) << 8 |
+				       rsnd_kctrl_vals(mix->rdw));
+
+	/* common volume parameter */
+	rsnd_mix_volume_parameter(io, mod);
+
+	rsnd_mod_write(mod, MIX_MIXIR, 0);
+}
+
+static void rsnd_mix_volume_update(struct rsnd_dai_stream *io,
+				  struct rsnd_mod *mod)
+{
+	/* Disable MIX dB setting */
+	rsnd_mod_write(mod, MIX_MDBER, 0);
+
+	/* common volume parameter */
+	rsnd_mix_volume_parameter(io, mod);
+
+	/* Enable MIX dB setting */
+	rsnd_mod_write(mod, MIX_MDBER, 1);
+}
+
+static int rsnd_mix_probe_(struct rsnd_mod *mod,
+			   struct rsnd_dai_stream *io,
+			   struct rsnd_priv *priv)
+{
+	return rsnd_cmd_attach(io, rsnd_mod_id(mod));
+}
+
+static int rsnd_mix_init(struct rsnd_mod *mod,
+			 struct rsnd_dai_stream *io,
+			 struct rsnd_priv *priv)
+{
+	rsnd_mod_power_on(mod);
+
+	rsnd_mix_activation(mod);
+
+	rsnd_mix_volume_init(io, mod);
+
+	rsnd_mix_volume_update(io, mod);
+
+	return 0;
+}
+
+static int rsnd_mix_quit(struct rsnd_mod *mod,
+			 struct rsnd_dai_stream *io,
+			 struct rsnd_priv *priv)
+{
+	rsnd_mix_halt(mod);
+
+	rsnd_mod_power_off(mod);
+
+	return 0;
+}
+
+static int rsnd_mix_pcm_new(struct rsnd_mod *mod,
+			    struct rsnd_dai_stream *io,
+			    struct snd_soc_pcm_runtime *rtd)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_mix *mix = rsnd_mod_to_mix(mod);
+	struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io);
+	struct rsnd_kctrl_cfg_s *volume;
+	int ret;
+
+	switch (rsnd_mod_id(src_mod)) {
+	case 3:
+	case 6:	/* MDBAR */
+		volume = &mix->volumeA;
+		rsnd_flags_set(mix, HAS_VOLA);
+		break;
+	case 4:
+	case 9:	/* MDBBR */
+		volume = &mix->volumeB;
+		rsnd_flags_set(mix, HAS_VOLB);
+		break;
+	case 0:
+	case 1:	/* MDBCR */
+		volume = &mix->volumeC;
+		rsnd_flags_set(mix, HAS_VOLC);
+		break;
+	case 2:
+	case 5:	/* MDBDR */
+		volume = &mix->volumeD;
+		rsnd_flags_set(mix, HAS_VOLD);
+		break;
+	default:
+		dev_err(dev, "unknown SRC is connected\n");
+		return -EINVAL;
+	}
+
+	/* Volume */
+	ret = rsnd_kctrl_new_s(mod, io, rtd,
+			       "MIX Playback Volume",
+			       rsnd_kctrl_accept_anytime,
+			       rsnd_mix_volume_update,
+			       volume, VOL_MAX);
+	if (ret < 0)
+		return ret;
+	rsnd_kctrl_vals(*volume) = VOL_MAX;
+
+	if (rsnd_flags_has(mix, ONCE_KCTRL_INITIALIZED))
+		return ret;
+
+	/* Ramp */
+	ret = rsnd_kctrl_new_s(mod, io, rtd,
+			       "MIX Ramp Switch",
+			       rsnd_kctrl_accept_anytime,
+			       rsnd_mix_volume_update,
+			       &mix->ren, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = rsnd_kctrl_new_e(mod, io, rtd,
+			       "MIX Ramp Up Rate",
+			       rsnd_kctrl_accept_anytime,
+			       rsnd_mix_volume_update,
+			       &mix->rup,
+			       volume_ramp_rate,
+			       VOLUME_RAMP_MAX_MIX);
+	if (ret < 0)
+		return ret;
+
+	ret = rsnd_kctrl_new_e(mod, io, rtd,
+			       "MIX Ramp Down Rate",
+			       rsnd_kctrl_accept_anytime,
+			       rsnd_mix_volume_update,
+			       &mix->rdw,
+			       volume_ramp_rate,
+			       VOLUME_RAMP_MAX_MIX);
+
+	rsnd_flags_set(mix, ONCE_KCTRL_INITIALIZED);
+
+	return ret;
+}
+
+static struct rsnd_mod_ops rsnd_mix_ops = {
+	.name		= MIX_NAME,
+	.probe		= rsnd_mix_probe_,
+	.init		= rsnd_mix_init,
+	.quit		= rsnd_mix_quit,
+	.pcm_new	= rsnd_mix_pcm_new,
+};
+
+struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id)
+{
+	if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv)))
+		id = 0;
+
+	return rsnd_mod_get(rsnd_mix_get(priv, id));
+}
+
+int rsnd_mix_probe(struct rsnd_priv *priv)
+{
+	struct device_node *node;
+	struct device_node *np;
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_mix *mix;
+	struct clk *clk;
+	char name[MIX_NAME_SIZE];
+	int i, nr, ret;
+
+	/* This driver doesn't support Gen1 at this point */
+	if (rsnd_is_gen1(priv))
+		return 0;
+
+	node = rsnd_mix_of_node(priv);
+	if (!node)
+		return 0; /* not used is not error */
+
+	nr = of_get_child_count(node);
+	if (!nr) {
+		ret = -EINVAL;
+		goto rsnd_mix_probe_done;
+	}
+
+	mix	= devm_kcalloc(dev, nr, sizeof(*mix), GFP_KERNEL);
+	if (!mix) {
+		ret = -ENOMEM;
+		goto rsnd_mix_probe_done;
+	}
+
+	priv->mix_nr	= nr;
+	priv->mix	= mix;
+
+	i = 0;
+	ret = 0;
+	for_each_child_of_node(node, np) {
+		mix = rsnd_mix_get(priv, i);
+
+		snprintf(name, MIX_NAME_SIZE, "%s.%d",
+			 MIX_NAME, i);
+
+		clk = devm_clk_get(dev, name);
+		if (IS_ERR(clk)) {
+			ret = PTR_ERR(clk);
+			of_node_put(np);
+			goto rsnd_mix_probe_done;
+		}
+
+		ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
+				    clk, rsnd_mod_get_status, RSND_MOD_MIX, i);
+		if (ret) {
+			of_node_put(np);
+			goto rsnd_mix_probe_done;
+		}
+
+		i++;
+	}
+
+rsnd_mix_probe_done:
+	of_node_put(node);
+
+	return ret;
+}
+
+void rsnd_mix_remove(struct rsnd_priv *priv)
+{
+	struct rsnd_mix *mix;
+	int i;
+
+	for_each_rsnd_mix(mix, priv, i) {
+		rsnd_mod_quit(rsnd_mod_get(mix));
+	}
+}
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
new file mode 100644
index 0000000..8f7a0ab
--- /dev/null
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -0,0 +1,817 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car
+//
+// Copyright (C) 2013 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+#ifndef RSND_H
+#define RSND_H
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/of_irq.h>
+#include <linux/sh_dma.h>
+#include <linux/workqueue.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#define RSND_GEN1_SRU	0
+#define RSND_GEN1_ADG	1
+#define RSND_GEN1_SSI	2
+
+#define RSND_GEN2_SCU	0
+#define RSND_GEN2_ADG	1
+#define RSND_GEN2_SSIU	2
+#define RSND_GEN2_SSI	3
+
+#define RSND_BASE_MAX	4
+
+/*
+ *	pseudo register
+ *
+ * The register address offsets SRU/SCU/SSIU on Gen1/Gen2 are very different.
+ * This driver uses pseudo register in order to hide it.
+ * see gen1/gen2 for detail
+ */
+enum rsnd_reg {
+	/* SCU (MIX/CTU/DVC) */
+	RSND_REG_SRC_I_BUSIF_MODE,
+	RSND_REG_SRC_O_BUSIF_MODE,
+	RSND_REG_SRC_ROUTE_MODE0,
+	RSND_REG_SRC_SWRSR,
+	RSND_REG_SRC_SRCIR,
+	RSND_REG_SRC_ADINR,
+	RSND_REG_SRC_IFSCR,
+	RSND_REG_SRC_IFSVR,
+	RSND_REG_SRC_SRCCR,
+	RSND_REG_SRC_CTRL,
+	RSND_REG_SRC_BSDSR,
+	RSND_REG_SRC_BSISR,
+	RSND_REG_SRC_INT_ENABLE0,
+	RSND_REG_SRC_BUSIF_DALIGN,
+	RSND_REG_SRCIN_TIMSEL0,
+	RSND_REG_SRCIN_TIMSEL1,
+	RSND_REG_SRCIN_TIMSEL2,
+	RSND_REG_SRCIN_TIMSEL3,
+	RSND_REG_SRCIN_TIMSEL4,
+	RSND_REG_SRCOUT_TIMSEL0,
+	RSND_REG_SRCOUT_TIMSEL1,
+	RSND_REG_SRCOUT_TIMSEL2,
+	RSND_REG_SRCOUT_TIMSEL3,
+	RSND_REG_SRCOUT_TIMSEL4,
+	RSND_REG_SCU_SYS_STATUS0,
+	RSND_REG_SCU_SYS_STATUS1,
+	RSND_REG_SCU_SYS_INT_EN0,
+	RSND_REG_SCU_SYS_INT_EN1,
+	RSND_REG_CMD_CTRL,
+	RSND_REG_CMD_BUSIF_MODE,
+	RSND_REG_CMD_BUSIF_DALIGN,
+	RSND_REG_CMD_ROUTE_SLCT,
+	RSND_REG_CMDOUT_TIMSEL,
+	RSND_REG_CTU_SWRSR,
+	RSND_REG_CTU_CTUIR,
+	RSND_REG_CTU_ADINR,
+	RSND_REG_CTU_CPMDR,
+	RSND_REG_CTU_SCMDR,
+	RSND_REG_CTU_SV00R,
+	RSND_REG_CTU_SV01R,
+	RSND_REG_CTU_SV02R,
+	RSND_REG_CTU_SV03R,
+	RSND_REG_CTU_SV04R,
+	RSND_REG_CTU_SV05R,
+	RSND_REG_CTU_SV06R,
+	RSND_REG_CTU_SV07R,
+	RSND_REG_CTU_SV10R,
+	RSND_REG_CTU_SV11R,
+	RSND_REG_CTU_SV12R,
+	RSND_REG_CTU_SV13R,
+	RSND_REG_CTU_SV14R,
+	RSND_REG_CTU_SV15R,
+	RSND_REG_CTU_SV16R,
+	RSND_REG_CTU_SV17R,
+	RSND_REG_CTU_SV20R,
+	RSND_REG_CTU_SV21R,
+	RSND_REG_CTU_SV22R,
+	RSND_REG_CTU_SV23R,
+	RSND_REG_CTU_SV24R,
+	RSND_REG_CTU_SV25R,
+	RSND_REG_CTU_SV26R,
+	RSND_REG_CTU_SV27R,
+	RSND_REG_CTU_SV30R,
+	RSND_REG_CTU_SV31R,
+	RSND_REG_CTU_SV32R,
+	RSND_REG_CTU_SV33R,
+	RSND_REG_CTU_SV34R,
+	RSND_REG_CTU_SV35R,
+	RSND_REG_CTU_SV36R,
+	RSND_REG_CTU_SV37R,
+	RSND_REG_MIX_SWRSR,
+	RSND_REG_MIX_MIXIR,
+	RSND_REG_MIX_ADINR,
+	RSND_REG_MIX_MIXMR,
+	RSND_REG_MIX_MVPDR,
+	RSND_REG_MIX_MDBAR,
+	RSND_REG_MIX_MDBBR,
+	RSND_REG_MIX_MDBCR,
+	RSND_REG_MIX_MDBDR,
+	RSND_REG_MIX_MDBER,
+	RSND_REG_DVC_SWRSR,
+	RSND_REG_DVC_DVUIR,
+	RSND_REG_DVC_ADINR,
+	RSND_REG_DVC_DVUCR,
+	RSND_REG_DVC_ZCMCR,
+	RSND_REG_DVC_VOL0R,
+	RSND_REG_DVC_VOL1R,
+	RSND_REG_DVC_VOL2R,
+	RSND_REG_DVC_VOL3R,
+	RSND_REG_DVC_VOL4R,
+	RSND_REG_DVC_VOL5R,
+	RSND_REG_DVC_VOL6R,
+	RSND_REG_DVC_VOL7R,
+	RSND_REG_DVC_DVUER,
+	RSND_REG_DVC_VRCTR,
+	RSND_REG_DVC_VRPDR,
+	RSND_REG_DVC_VRDBR,
+
+	/* ADG */
+	RSND_REG_BRRA,
+	RSND_REG_BRRB,
+	RSND_REG_BRGCKR,
+	RSND_REG_DIV_EN,
+	RSND_REG_AUDIO_CLK_SEL0,
+	RSND_REG_AUDIO_CLK_SEL1,
+	RSND_REG_AUDIO_CLK_SEL2,
+
+	/* SSIU */
+	RSND_REG_SSI_MODE,
+	RSND_REG_SSI_MODE0,
+	RSND_REG_SSI_MODE1,
+	RSND_REG_SSI_MODE2,
+	RSND_REG_SSI_CONTROL,
+	RSND_REG_SSI_CTRL,
+	RSND_REG_SSI_BUSIF_MODE,
+	RSND_REG_SSI_BUSIF_ADINR,
+	RSND_REG_SSI_BUSIF_DALIGN,
+	RSND_REG_SSI_INT_ENABLE,
+	RSND_REG_SSI_SYS_STATUS0,
+	RSND_REG_SSI_SYS_STATUS1,
+	RSND_REG_SSI_SYS_STATUS2,
+	RSND_REG_SSI_SYS_STATUS3,
+	RSND_REG_SSI_SYS_STATUS4,
+	RSND_REG_SSI_SYS_STATUS5,
+	RSND_REG_SSI_SYS_STATUS6,
+	RSND_REG_SSI_SYS_STATUS7,
+	RSND_REG_HDMI0_SEL,
+	RSND_REG_HDMI1_SEL,
+
+	/* SSI */
+	RSND_REG_SSICR,
+	RSND_REG_SSISR,
+	RSND_REG_SSITDR,
+	RSND_REG_SSIRDR,
+	RSND_REG_SSIWSR,
+
+	RSND_REG_MAX,
+};
+
+struct rsnd_priv;
+struct rsnd_mod;
+struct rsnd_dai;
+struct rsnd_dai_stream;
+
+/*
+ *	R-Car basic functions
+ */
+#define rsnd_mod_read(m, r) \
+	rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r)
+#define rsnd_mod_write(m, r, d) \
+	rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d)
+#define rsnd_mod_bset(m, r, s, d) \
+	rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d)
+
+u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg);
+void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod,
+		enum rsnd_reg reg, u32 data);
+void rsnd_force_write(struct rsnd_priv *priv, struct rsnd_mod *mod,
+		enum rsnd_reg reg, u32 data);
+void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
+		    u32 mask, u32 data);
+u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
+u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
+u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod);
+
+/*
+ *	R-Car DMA
+ */
+int rsnd_dma_attach(struct rsnd_dai_stream *io,
+		    struct rsnd_mod *mod, struct rsnd_mod **dma_mod);
+int rsnd_dma_probe(struct rsnd_priv *priv);
+struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
+					  struct rsnd_mod *mod, char *name);
+
+/*
+ *	R-Car sound mod
+ */
+enum rsnd_mod_type {
+	RSND_MOD_AUDMAPP,
+	RSND_MOD_AUDMA,
+	RSND_MOD_DVC,
+	RSND_MOD_MIX,
+	RSND_MOD_CTU,
+	RSND_MOD_CMD,
+	RSND_MOD_SRC,
+	RSND_MOD_SSIM3,		/* SSI multi 3 */
+	RSND_MOD_SSIM2,		/* SSI multi 2 */
+	RSND_MOD_SSIM1,		/* SSI multi 1 */
+	RSND_MOD_SSIP,		/* SSI parent */
+	RSND_MOD_SSI,
+	RSND_MOD_SSIU,
+	RSND_MOD_MAX,
+};
+
+struct rsnd_mod_ops {
+	char *name;
+	struct dma_chan* (*dma_req)(struct rsnd_dai_stream *io,
+				    struct rsnd_mod *mod);
+	int (*probe)(struct rsnd_mod *mod,
+		     struct rsnd_dai_stream *io,
+		     struct rsnd_priv *priv);
+	int (*remove)(struct rsnd_mod *mod,
+		      struct rsnd_dai_stream *io,
+		      struct rsnd_priv *priv);
+	int (*init)(struct rsnd_mod *mod,
+		    struct rsnd_dai_stream *io,
+		    struct rsnd_priv *priv);
+	int (*quit)(struct rsnd_mod *mod,
+		    struct rsnd_dai_stream *io,
+		    struct rsnd_priv *priv);
+	int (*start)(struct rsnd_mod *mod,
+		     struct rsnd_dai_stream *io,
+		     struct rsnd_priv *priv);
+	int (*stop)(struct rsnd_mod *mod,
+		    struct rsnd_dai_stream *io,
+		    struct rsnd_priv *priv);
+	int (*irq)(struct rsnd_mod *mod,
+		   struct rsnd_dai_stream *io,
+		   struct rsnd_priv *priv, int enable);
+	int (*pcm_new)(struct rsnd_mod *mod,
+		       struct rsnd_dai_stream *io,
+		       struct snd_soc_pcm_runtime *rtd);
+	int (*hw_params)(struct rsnd_mod *mod,
+			 struct rsnd_dai_stream *io,
+			 struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *hw_params);
+	int (*pointer)(struct rsnd_mod *mod,
+		       struct rsnd_dai_stream *io,
+		       snd_pcm_uframes_t *pointer);
+	int (*fallback)(struct rsnd_mod *mod,
+			struct rsnd_dai_stream *io,
+			struct rsnd_priv *priv);
+	int (*nolock_start)(struct rsnd_mod *mod,
+		    struct rsnd_dai_stream *io,
+		    struct rsnd_priv *priv);
+	int (*nolock_stop)(struct rsnd_mod *mod,
+		    struct rsnd_dai_stream *io,
+		    struct rsnd_priv *priv);
+	int (*prepare)(struct rsnd_mod *mod,
+		       struct rsnd_dai_stream *io,
+		       struct rsnd_priv *priv);
+};
+
+struct rsnd_dai_stream;
+struct rsnd_mod {
+	int id;
+	enum rsnd_mod_type type;
+	struct rsnd_mod_ops *ops;
+	struct rsnd_priv *priv;
+	struct clk *clk;
+	u32 *(*get_status)(struct rsnd_dai_stream *io,
+			   struct rsnd_mod *mod,
+			   enum rsnd_mod_type type);
+	u32 status;
+};
+/*
+ * status
+ *
+ * 0xH0000CBA
+ *
+ * A	0: nolock_start	1: nolock_stop
+ * B	0: init		1: quit
+ * C	0: start	1: stop
+ *
+ * H is always called (see __rsnd_mod_call)
+ * H	0: probe	1: remove
+ * H	0: pcm_new
+ * H	0: fallback
+ * H	0: hw_params
+ * H	0: pointer
+ * H	0: prepare
+ */
+#define __rsnd_mod_shift_nolock_start	0
+#define __rsnd_mod_shift_nolock_stop	0
+#define __rsnd_mod_shift_init		4
+#define __rsnd_mod_shift_quit		4
+#define __rsnd_mod_shift_start		8
+#define __rsnd_mod_shift_stop		8
+#define __rsnd_mod_shift_probe		28 /* always called */
+#define __rsnd_mod_shift_remove		28 /* always called */
+#define __rsnd_mod_shift_irq		28 /* always called */
+#define __rsnd_mod_shift_pcm_new	28 /* always called */
+#define __rsnd_mod_shift_fallback	28 /* always called */
+#define __rsnd_mod_shift_hw_params	28 /* always called */
+#define __rsnd_mod_shift_pointer	28 /* always called */
+#define __rsnd_mod_shift_prepare	28 /* always called */
+
+#define __rsnd_mod_add_probe		0
+#define __rsnd_mod_add_remove		0
+#define __rsnd_mod_add_nolock_start	 1
+#define __rsnd_mod_add_nolock_stop	-1
+#define __rsnd_mod_add_init		 1
+#define __rsnd_mod_add_quit		-1
+#define __rsnd_mod_add_start		 1
+#define __rsnd_mod_add_stop		-1
+#define __rsnd_mod_add_irq		0
+#define __rsnd_mod_add_pcm_new		0
+#define __rsnd_mod_add_fallback		0
+#define __rsnd_mod_add_hw_params	0
+#define __rsnd_mod_add_pointer		0
+#define __rsnd_mod_add_prepare		0
+
+#define __rsnd_mod_call_probe		0
+#define __rsnd_mod_call_remove		0
+#define __rsnd_mod_call_init		0
+#define __rsnd_mod_call_quit		1
+#define __rsnd_mod_call_start		0
+#define __rsnd_mod_call_stop		1
+#define __rsnd_mod_call_irq		0
+#define __rsnd_mod_call_pcm_new		0
+#define __rsnd_mod_call_fallback	0
+#define __rsnd_mod_call_hw_params	0
+#define __rsnd_mod_call_pointer		0
+#define __rsnd_mod_call_nolock_start	0
+#define __rsnd_mod_call_nolock_stop	1
+#define __rsnd_mod_call_prepare		0
+
+#define rsnd_mod_to_priv(mod)	((mod)->priv)
+#define rsnd_mod_name(mod)	((mod)->ops->name)
+#define rsnd_mod_id(mod)	((mod)->id)
+#define rsnd_mod_power_on(mod)	clk_enable((mod)->clk)
+#define rsnd_mod_power_off(mod)	clk_disable((mod)->clk)
+#define rsnd_mod_get(ip)	(&(ip)->mod)
+
+int rsnd_mod_init(struct rsnd_priv *priv,
+		  struct rsnd_mod *mod,
+		  struct rsnd_mod_ops *ops,
+		  struct clk *clk,
+		  u32* (*get_status)(struct rsnd_dai_stream *io,
+				     struct rsnd_mod *mod,
+				     enum rsnd_mod_type type),
+		  enum rsnd_mod_type type,
+		  int id);
+void rsnd_mod_quit(struct rsnd_mod *mod);
+struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
+				  struct rsnd_mod *mod);
+void rsnd_mod_interrupt(struct rsnd_mod *mod,
+			void (*callback)(struct rsnd_mod *mod,
+					 struct rsnd_dai_stream *io));
+u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io,
+			 struct rsnd_mod *mod,
+			 enum rsnd_mod_type type);
+struct rsnd_mod *rsnd_mod_next(int *iterator,
+			       struct rsnd_dai_stream *io,
+			       enum rsnd_mod_type *array,
+			       int array_size);
+#define for_each_rsnd_mod(iterator, pos, io)				\
+	for (iterator = 0;						\
+	     (pos = rsnd_mod_next(&iterator, io, NULL, 0)); iterator++)
+#define for_each_rsnd_mod_arrays(iterator, pos, io, array, size)	\
+	for (iterator = 0;						\
+	     (pos = rsnd_mod_next(&iterator, io, array, size)); iterator++)
+#define for_each_rsnd_mod_array(iterator, pos, io, array)		\
+	for_each_rsnd_mod_arrays(iterator, pos, io, array, ARRAY_SIZE(array))
+
+void rsnd_parse_connect_common(struct rsnd_dai *rdai,
+		struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
+		struct device_node *node,
+		struct device_node *playback,
+		struct device_node *capture);
+
+#define rsnd_runtime_channel_original(io) \
+	rsnd_runtime_channel_original_with_params(io, NULL)
+int rsnd_runtime_channel_original_with_params(struct rsnd_dai_stream *io,
+				struct snd_pcm_hw_params *params);
+#define rsnd_runtime_channel_after_ctu(io)			\
+	rsnd_runtime_channel_after_ctu_with_params(io, NULL)
+int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io,
+				struct snd_pcm_hw_params *params);
+#define rsnd_runtime_channel_for_ssi(io) \
+	rsnd_runtime_channel_for_ssi_with_params(io, NULL)
+int rsnd_runtime_channel_for_ssi_with_params(struct rsnd_dai_stream *io,
+				 struct snd_pcm_hw_params *params);
+int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io);
+int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io);
+
+/*
+ * DT
+ */
+#define rsnd_parse_of_node(priv, node)					\
+	of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, node)
+#define RSND_NODE_DAI	"rcar_sound,dai"
+#define RSND_NODE_SSI	"rcar_sound,ssi"
+#define RSND_NODE_SRC	"rcar_sound,src"
+#define RSND_NODE_CTU	"rcar_sound,ctu"
+#define RSND_NODE_MIX	"rcar_sound,mix"
+#define RSND_NODE_DVC	"rcar_sound,dvc"
+
+/*
+ *	R-Car sound DAI
+ */
+#define RSND_DAI_NAME_SIZE	16
+struct rsnd_dai_stream {
+	char name[RSND_DAI_NAME_SIZE];
+	struct snd_pcm_substream *substream;
+	struct rsnd_mod *mod[RSND_MOD_MAX];
+	struct rsnd_dai *rdai;
+	struct device *dmac_dev; /* for IPMMU */
+	u32 parent_ssi_status;
+};
+#define rsnd_io_to_mod(io, i)	((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL)
+#define rsnd_io_to_mod_ssi(io)	rsnd_io_to_mod((io), RSND_MOD_SSI)
+#define rsnd_io_to_mod_ssiu(io)	rsnd_io_to_mod((io), RSND_MOD_SSIU)
+#define rsnd_io_to_mod_ssip(io)	rsnd_io_to_mod((io), RSND_MOD_SSIP)
+#define rsnd_io_to_mod_src(io)	rsnd_io_to_mod((io), RSND_MOD_SRC)
+#define rsnd_io_to_mod_ctu(io)	rsnd_io_to_mod((io), RSND_MOD_CTU)
+#define rsnd_io_to_mod_mix(io)	rsnd_io_to_mod((io), RSND_MOD_MIX)
+#define rsnd_io_to_mod_dvc(io)	rsnd_io_to_mod((io), RSND_MOD_DVC)
+#define rsnd_io_to_mod_cmd(io)	rsnd_io_to_mod((io), RSND_MOD_CMD)
+#define rsnd_io_to_rdai(io)	((io)->rdai)
+#define rsnd_io_to_priv(io)	(rsnd_rdai_to_priv(rsnd_io_to_rdai(io)))
+#define rsnd_io_is_play(io)	(&rsnd_io_to_rdai(io)->playback == io)
+#define rsnd_io_to_runtime(io) ((io)->substream ? \
+				(io)->substream->runtime : NULL)
+int rsnd_io_is_working(struct rsnd_dai_stream *io);
+
+struct rsnd_dai {
+	char name[RSND_DAI_NAME_SIZE];
+	struct rsnd_dai_stream playback;
+	struct rsnd_dai_stream capture;
+	struct rsnd_priv *priv;
+	struct snd_pcm_hw_constraint_list constraint;
+
+	int max_channels;	/* 2ch - 16ch */
+	int ssi_lane;		/* 1lane - 4lane */
+
+	unsigned int clk_master:1;
+	unsigned int bit_clk_inv:1;
+	unsigned int frm_clk_inv:1;
+	unsigned int sys_delay:1;
+	unsigned int data_alignment:1;
+};
+
+#define rsnd_rdai_nr(priv) ((priv)->rdai_nr)
+#define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master)
+#define rsnd_rdai_to_priv(rdai) ((rdai)->priv)
+#define for_each_rsnd_dai(rdai, priv, i)		\
+	for (i = 0;					\
+	     (i < rsnd_rdai_nr(priv)) &&		\
+	     ((rdai) = rsnd_rdai_get(priv, i));		\
+	     i++)
+
+struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id);
+
+#define rsnd_rdai_channels_set(rdai, max_channels) \
+	rsnd_rdai_channels_ctrl(rdai, max_channels)
+#define rsnd_rdai_channels_get(rdai) \
+	rsnd_rdai_channels_ctrl(rdai, 0)
+int rsnd_rdai_channels_ctrl(struct rsnd_dai *rdai,
+			    int max_channels);
+
+#define rsnd_rdai_ssi_lane_set(rdai, ssi_lane) \
+	rsnd_rdai_ssi_lane_ctrl(rdai, ssi_lane)
+#define rsnd_rdai_ssi_lane_get(rdai) \
+	rsnd_rdai_ssi_lane_ctrl(rdai, 0)
+int rsnd_rdai_ssi_lane_ctrl(struct rsnd_dai *rdai,
+			    int ssi_lane);
+
+void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io);
+int rsnd_dai_connect(struct rsnd_mod *mod,
+		     struct rsnd_dai_stream *io,
+		     enum rsnd_mod_type type);
+
+/*
+ *	R-Car Gen1/Gen2
+ */
+int rsnd_gen_probe(struct rsnd_priv *priv);
+void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
+			       struct rsnd_mod *mod,
+			       enum rsnd_reg reg);
+phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id);
+
+/*
+ *	R-Car ADG
+ */
+int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate);
+int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);
+int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
+int rsnd_adg_probe(struct rsnd_priv *priv);
+void rsnd_adg_remove(struct rsnd_priv *priv);
+int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod,
+				  struct rsnd_dai_stream *io,
+				  unsigned int in_rate,
+				  unsigned int out_rate);
+int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
+				 struct rsnd_dai_stream *io);
+#define rsnd_adg_clk_enable(priv)	rsnd_adg_clk_control(priv, 1)
+#define rsnd_adg_clk_disable(priv)	rsnd_adg_clk_control(priv, 0)
+void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable);
+
+/*
+ *	R-Car sound priv
+ */
+struct rsnd_priv {
+
+	struct platform_device *pdev;
+	spinlock_t lock;
+	unsigned long flags;
+#define RSND_GEN_MASK	(0xF << 0)
+#define RSND_GEN1	(1 << 0)
+#define RSND_GEN2	(2 << 0)
+#define RSND_GEN3	(3 << 0)
+
+	/*
+	 * below value will be filled on rsnd_gen_probe()
+	 */
+	void *gen;
+
+	/*
+	 * below value will be filled on rsnd_adg_probe()
+	 */
+	void *adg;
+
+	/*
+	 * below value will be filled on rsnd_dma_probe()
+	 */
+	void *dma;
+
+	/*
+	 * below value will be filled on rsnd_ssi_probe()
+	 */
+	void *ssi;
+	int ssi_nr;
+
+	/*
+	 * below value will be filled on rsnd_ssiu_probe()
+	 */
+	void *ssiu;
+	int ssiu_nr;
+
+	/*
+	 * below value will be filled on rsnd_src_probe()
+	 */
+	void *src;
+	int src_nr;
+
+	/*
+	 * below value will be filled on rsnd_ctu_probe()
+	 */
+	void *ctu;
+	int ctu_nr;
+
+	/*
+	 * below value will be filled on rsnd_mix_probe()
+	 */
+	void *mix;
+	int mix_nr;
+
+	/*
+	 * below value will be filled on rsnd_dvc_probe()
+	 */
+	void *dvc;
+	int dvc_nr;
+
+	/*
+	 * below value will be filled on rsnd_cmd_probe()
+	 */
+	void *cmd;
+	int cmd_nr;
+
+	/*
+	 * below value will be filled on rsnd_dai_probe()
+	 */
+	struct snd_soc_dai_driver *daidrv;
+	struct rsnd_dai *rdai;
+	int rdai_nr;
+};
+
+#define rsnd_priv_to_pdev(priv)	((priv)->pdev)
+#define rsnd_priv_to_dev(priv)	(&(rsnd_priv_to_pdev(priv)->dev))
+
+#define rsnd_is_gen1(priv)	(((priv)->flags & RSND_GEN_MASK) == RSND_GEN1)
+#define rsnd_is_gen2(priv)	(((priv)->flags & RSND_GEN_MASK) == RSND_GEN2)
+#define rsnd_is_gen3(priv)	(((priv)->flags & RSND_GEN_MASK) == RSND_GEN3)
+
+#define rsnd_flags_has(p, f) ((p)->flags & (f))
+#define rsnd_flags_set(p, f) ((p)->flags |= (f))
+#define rsnd_flags_del(p, f) ((p)->flags &= ~(f))
+
+/*
+ *	rsnd_kctrl
+ */
+struct rsnd_kctrl_cfg {
+	unsigned int max;
+	unsigned int size;
+	u32 *val;
+	const char * const *texts;
+	int (*accept)(struct rsnd_dai_stream *io);
+	void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod);
+	struct rsnd_dai_stream *io;
+	struct snd_card *card;
+	struct snd_kcontrol *kctrl;
+	struct rsnd_mod *mod;
+};
+
+#define RSND_MAX_CHANNELS	8
+struct rsnd_kctrl_cfg_m {
+	struct rsnd_kctrl_cfg cfg;
+	u32 val[RSND_MAX_CHANNELS];
+};
+
+struct rsnd_kctrl_cfg_s {
+	struct rsnd_kctrl_cfg cfg;
+	u32 val;
+};
+#define rsnd_kctrl_size(x)	((x).cfg.size)
+#define rsnd_kctrl_max(x)	((x).cfg.max)
+#define rsnd_kctrl_valm(x, i)	((x).val[i])	/* = (x).cfg.val[i] */
+#define rsnd_kctrl_vals(x)	((x).val)	/* = (x).cfg.val[0] */
+
+int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io);
+int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io);
+struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg);
+struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg);
+int rsnd_kctrl_new(struct rsnd_mod *mod,
+		   struct rsnd_dai_stream *io,
+		   struct snd_soc_pcm_runtime *rtd,
+		   const unsigned char *name,
+		   int (*accept)(struct rsnd_dai_stream *io),
+		   void (*update)(struct rsnd_dai_stream *io,
+				  struct rsnd_mod *mod),
+		   struct rsnd_kctrl_cfg *cfg,
+		   const char * const *texts,
+		   int size,
+		   u32 max);
+
+#define rsnd_kctrl_new_m(mod, io, rtd, name, accept, update, cfg, size, max) \
+	rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_m(cfg), \
+		       NULL, size, max)
+
+#define rsnd_kctrl_new_s(mod, io, rtd, name, accept, update, cfg, max)	\
+	rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \
+		       NULL, 1, max)
+
+#define rsnd_kctrl_new_e(mod, io, rtd, name, accept, update, cfg, texts, size) \
+	rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \
+		       texts, 1, size)
+
+extern const char * const volume_ramp_rate[];
+#define VOLUME_RAMP_MAX_DVC	(0x17 + 1)
+#define VOLUME_RAMP_MAX_MIX	(0x0a + 1)
+
+/*
+ *	R-Car SSI
+ */
+int rsnd_ssi_probe(struct rsnd_priv *priv);
+void rsnd_ssi_remove(struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
+int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
+u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io);
+
+#define RSND_SSI_HDMI_PORT0	0xf0
+#define RSND_SSI_HDMI_PORT1	0xf1
+int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io);
+void rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv,
+				    struct device_node *endpoint,
+				    int dai_i);
+
+#define rsnd_ssi_is_pin_sharing(io)	\
+	__rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
+int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
+
+#define rsnd_ssi_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SSI)
+void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
+			    struct device_node *playback,
+			    struct device_node *capture);
+unsigned int rsnd_ssi_clk_query(struct rsnd_priv *priv,
+		       int param1, int param2, int *idx);
+
+/*
+ *	R-Car SSIU
+ */
+int rsnd_ssiu_attach(struct rsnd_dai_stream *io,
+		     struct rsnd_mod *mod);
+int rsnd_ssiu_probe(struct rsnd_priv *priv);
+void rsnd_ssiu_remove(struct rsnd_priv *priv);
+
+/*
+ *	R-Car SRC
+ */
+int rsnd_src_probe(struct rsnd_priv *priv);
+void rsnd_src_remove(struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id);
+
+#define rsnd_src_get_in_rate(priv, io) rsnd_src_get_rate(priv, io, 1)
+#define rsnd_src_get_out_rate(priv, io) rsnd_src_get_rate(priv, io, 0)
+unsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
+			       struct rsnd_dai_stream *io,
+			       int is_in);
+
+#define rsnd_src_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SRC)
+#define rsnd_parse_connect_src(rdai, playback, capture)			\
+	rsnd_parse_connect_common(rdai, rsnd_src_mod_get,		\
+				  rsnd_src_of_node(rsnd_rdai_to_priv(rdai)), \
+						   playback, capture)
+
+/*
+ *	R-Car CTU
+ */
+int rsnd_ctu_probe(struct rsnd_priv *priv);
+void rsnd_ctu_remove(struct rsnd_priv *priv);
+int rsnd_ctu_converted_channel(struct rsnd_mod *mod);
+struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
+#define rsnd_ctu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_CTU)
+#define rsnd_parse_connect_ctu(rdai, playback, capture)			\
+	rsnd_parse_connect_common(rdai, rsnd_ctu_mod_get,		\
+				  rsnd_ctu_of_node(rsnd_rdai_to_priv(rdai)), \
+						   playback, capture)
+
+/*
+ *	R-Car MIX
+ */
+int rsnd_mix_probe(struct rsnd_priv *priv);
+void rsnd_mix_remove(struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id);
+#define rsnd_mix_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_MIX)
+#define rsnd_parse_connect_mix(rdai, playback, capture)			\
+	rsnd_parse_connect_common(rdai, rsnd_mix_mod_get,		\
+				  rsnd_mix_of_node(rsnd_rdai_to_priv(rdai)), \
+						   playback, capture)
+
+/*
+ *	R-Car DVC
+ */
+int rsnd_dvc_probe(struct rsnd_priv *priv);
+void rsnd_dvc_remove(struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
+#define rsnd_dvc_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DVC)
+#define rsnd_parse_connect_dvc(rdai, playback, capture)			\
+	rsnd_parse_connect_common(rdai, rsnd_dvc_mod_get,		\
+				  rsnd_dvc_of_node(rsnd_rdai_to_priv(rdai)), \
+						   playback, capture)
+
+/*
+ *	R-Car CMD
+ */
+int rsnd_cmd_probe(struct rsnd_priv *priv);
+void rsnd_cmd_remove(struct rsnd_priv *priv);
+int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id);
+
+void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
+#ifdef DEBUG
+#define rsnd_mod_confirm_ssi(mssi)	rsnd_mod_make_sure(mssi, RSND_MOD_SSI)
+#define rsnd_mod_confirm_src(msrc)	rsnd_mod_make_sure(msrc, RSND_MOD_SRC)
+#define rsnd_mod_confirm_dvc(mdvc)	rsnd_mod_make_sure(mdvc, RSND_MOD_DVC)
+#else
+#define rsnd_mod_confirm_ssi(mssi)
+#define rsnd_mod_confirm_src(msrc)
+#define rsnd_mod_confirm_dvc(mdvc)
+#endif
+
+/*
+ * If you don't need interrupt status debug message,
+ * define RSND_DEBUG_NO_IRQ_STATUS as 1 on top of src.c/ssi.c
+ *
+ * #define RSND_DEBUG_NO_IRQ_STATUS 1
+ */
+#define rsnd_dbg_irq_status(dev, param...)		\
+	if (!IS_BUILTIN(RSND_DEBUG_NO_IRQ_STATUS))	\
+		dev_dbg(dev, param)
+
+/*
+ * If you don't need rsnd_dai_call debug message,
+ * define RSND_DEBUG_NO_DAI_CALL as 1 on top of core.c
+ *
+ * #define RSND_DEBUG_NO_DAI_CALL 1
+ */
+#define rsnd_dbg_dai_call(dev, param...)		\
+	if (!IS_BUILTIN(RSND_DEBUG_NO_DAI_CALL))	\
+		dev_dbg(dev, param)
+
+#endif
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
new file mode 100644
index 0000000..beccfba
--- /dev/null
+++ b/sound/soc/sh/rcar/src.c
@@ -0,0 +1,635 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car SRC support
+//
+// Copyright (C) 2013 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+/*
+ * you can enable below define if you don't need
+ * SSI interrupt status debug message when debugging
+ * see rsnd_dbg_irq_status()
+ *
+ * #define RSND_DEBUG_NO_IRQ_STATUS 1
+ */
+
+#include "rsnd.h"
+
+#define SRC_NAME "src"
+
+/* SCU_SYSTEM_STATUS0/1 */
+#define OUF_SRC(id)	((1 << (id + 16)) | (1 << id))
+
+struct rsnd_src {
+	struct rsnd_mod mod;
+	struct rsnd_mod *dma;
+	struct rsnd_kctrl_cfg_s sen;  /* sync convert enable */
+	struct rsnd_kctrl_cfg_s sync; /* sync convert */
+	u32 convert_rate; /* sampling rate convert */
+	int irq;
+};
+
+#define RSND_SRC_NAME_SIZE 16
+
+#define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id)
+#define rsnd_src_nr(priv) ((priv)->src_nr)
+#define rsnd_src_sync_is_enabled(mod) (rsnd_mod_to_src(mod)->sen.val)
+
+#define rsnd_mod_to_src(_mod)				\
+	container_of((_mod), struct rsnd_src, mod)
+
+#define for_each_rsnd_src(pos, priv, i)				\
+	for ((i) = 0;						\
+	     ((i) < rsnd_src_nr(priv)) &&			\
+	     ((pos) = (struct rsnd_src *)(priv)->src + i);	\
+	     i++)
+
+
+/*
+ *		image of SRC (Sampling Rate Converter)
+ *
+ * 96kHz   <-> +-----+	48kHz	+-----+	 48kHz	+-------+
+ * 48kHz   <-> | SRC | <------>	| SSI |	<----->	| codec |
+ * 44.1kHz <-> +-----+		+-----+		+-------+
+ * ...
+ *
+ */
+
+static void rsnd_src_activation(struct rsnd_mod *mod)
+{
+	rsnd_mod_write(mod, SRC_SWRSR, 0);
+	rsnd_mod_write(mod, SRC_SWRSR, 1);
+}
+
+static void rsnd_src_halt(struct rsnd_mod *mod)
+{
+	rsnd_mod_write(mod, SRC_SRCIR, 1);
+	rsnd_mod_write(mod, SRC_SWRSR, 0);
+}
+
+static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io,
+					 struct rsnd_mod *mod)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	int is_play = rsnd_io_is_play(io);
+
+	return rsnd_dma_request_channel(rsnd_src_of_node(priv),
+					mod,
+					is_play ? "rx" : "tx");
+}
+
+static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io,
+				 struct rsnd_mod *mod)
+{
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+	u32 convert_rate;
+
+	if (!runtime)
+		return 0;
+
+	if (!rsnd_src_sync_is_enabled(mod))
+		return src->convert_rate;
+
+	convert_rate = src->sync.val;
+
+	if (!convert_rate)
+		convert_rate = src->convert_rate;
+
+	if (!convert_rate)
+		convert_rate = runtime->rate;
+
+	return convert_rate;
+}
+
+unsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
+			       struct rsnd_dai_stream *io,
+			       int is_in)
+{
+	struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io);
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	unsigned int rate = 0;
+	int is_play = rsnd_io_is_play(io);
+
+	/*
+	 * Playback
+	 * runtime_rate -> [SRC] -> convert_rate
+	 *
+	 * Capture
+	 * convert_rate -> [SRC] -> runtime_rate
+	 */
+
+	if (is_play == is_in)
+		return runtime->rate;
+
+	/*
+	 * return convert rate if SRC is used,
+	 * otherwise, return runtime->rate as usual
+	 */
+	if (src_mod)
+		rate = rsnd_src_convert_rate(io, src_mod);
+
+	if (!rate)
+		rate = runtime->rate;
+
+	return rate;
+}
+
+static int rsnd_src_hw_params(struct rsnd_mod *mod,
+			      struct rsnd_dai_stream *io,
+			      struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *fe_params)
+{
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+	struct snd_soc_pcm_runtime *fe = substream->private_data;
+
+	/*
+	 * SRC assumes that it is used under DPCM if user want to use
+	 * sampling rate convert. Then, SRC should be FE.
+	 * And then, this function will be called *after* BE settings.
+	 * this means, each BE already has fixuped hw_params.
+	 * see
+	 *	dpcm_fe_dai_hw_params()
+	 *	dpcm_be_dai_hw_params()
+	 */
+	src->convert_rate = 0;
+	if (fe->dai_link->dynamic) {
+		int stream = substream->stream;
+		struct snd_soc_dpcm *dpcm;
+		struct snd_pcm_hw_params *be_params;
+
+		list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+			be_params = &dpcm->hw_params;
+
+			if (params_rate(fe_params) != params_rate(be_params))
+				src->convert_rate = params_rate(be_params);
+		}
+	}
+
+	return 0;
+}
+
+static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
+				      struct rsnd_mod *mod)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	int is_play = rsnd_io_is_play(io);
+	int use_src = 0;
+	u32 fin, fout;
+	u32 ifscr, fsrate, adinr;
+	u32 cr, route;
+	u32 bsdsr, bsisr;
+	u32 i_busif, o_busif, tmp;
+	uint ratio;
+
+	if (!runtime)
+		return;
+
+	fin  = rsnd_src_get_in_rate(priv, io);
+	fout = rsnd_src_get_out_rate(priv, io);
+
+	/* 6 - 1/6 are very enough ratio for SRC_BSDSR */
+	if (fin == fout)
+		ratio = 0;
+	else if (fin > fout)
+		ratio = 100 * fin / fout;
+	else
+		ratio = 100 * fout / fin;
+
+	if (ratio > 600) {
+		dev_err(dev, "FSO/FSI ratio error\n");
+		return;
+	}
+
+	use_src = (fin != fout) | rsnd_src_sync_is_enabled(mod);
+
+	/*
+	 * SRC_ADINR
+	 */
+	adinr = rsnd_get_adinr_bit(mod, io) |
+		rsnd_runtime_channel_original(io);
+
+	/*
+	 * SRC_IFSCR / SRC_IFSVR
+	 */
+	ifscr = 0;
+	fsrate = 0;
+	if (use_src) {
+		u64 n;
+
+		ifscr = 1;
+		n = (u64)0x0400000 * fin;
+		do_div(n, fout);
+		fsrate = n;
+	}
+
+	/*
+	 * SRC_SRCCR / SRC_ROUTE_MODE0
+	 */
+	cr	= 0x00011110;
+	route	= 0x0;
+	if (use_src) {
+		route	= 0x1;
+
+		if (rsnd_src_sync_is_enabled(mod)) {
+			cr |= 0x1;
+			route |= rsnd_io_is_play(io) ?
+				(0x1 << 24) : (0x1 << 25);
+		}
+	}
+
+	/*
+	 * SRC_BSDSR / SRC_BSISR
+	 */
+	switch (rsnd_mod_id(mod)) {
+	case 5:
+	case 6:
+	case 7:
+	case 8:
+		bsdsr = 0x02400000; /* 6 - 1/6 */
+		bsisr = 0x00100060; /* 6 - 1/6 */
+		break;
+	default:
+		bsdsr = 0x01800000; /* 6 - 1/6 */
+		bsisr = 0x00100060 ;/* 6 - 1/6 */
+		break;
+	}
+
+	/* BUSIF_MODE */
+	tmp = rsnd_get_busif_shift(io, mod);
+	i_busif = ( is_play ? tmp : 0) | 1;
+	o_busif = (!is_play ? tmp : 0) | 1;
+
+	rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
+
+	rsnd_mod_write(mod, SRC_SRCIR, 1);	/* initialize */
+	rsnd_mod_write(mod, SRC_ADINR, adinr);
+	rsnd_mod_write(mod, SRC_IFSCR, ifscr);
+	rsnd_mod_write(mod, SRC_IFSVR, fsrate);
+	rsnd_mod_write(mod, SRC_SRCCR, cr);
+	rsnd_mod_write(mod, SRC_BSDSR, bsdsr);
+	rsnd_mod_write(mod, SRC_BSISR, bsisr);
+	rsnd_mod_write(mod, SRC_SRCIR, 0);	/* cancel initialize */
+
+	rsnd_mod_write(mod, SRC_I_BUSIF_MODE, i_busif);
+	rsnd_mod_write(mod, SRC_O_BUSIF_MODE, o_busif);
+
+	rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
+
+	rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout);
+}
+
+static int rsnd_src_irq(struct rsnd_mod *mod,
+			struct rsnd_dai_stream *io,
+			struct rsnd_priv *priv,
+			int enable)
+{
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+	u32 sys_int_val, int_val, sys_int_mask;
+	int irq = src->irq;
+	int id = rsnd_mod_id(mod);
+
+	sys_int_val =
+	sys_int_mask = OUF_SRC(id);
+	int_val = 0x3300;
+
+	/*
+	 * IRQ is not supported on non-DT
+	 * see
+	 *	rsnd_src_probe_()
+	 */
+	if ((irq <= 0) || !enable) {
+		sys_int_val = 0;
+		int_val = 0;
+	}
+
+	/*
+	 * WORKAROUND
+	 *
+	 * ignore over flow error when rsnd_src_sync_is_enabled()
+	 */
+	if (rsnd_src_sync_is_enabled(mod))
+		sys_int_val = sys_int_val & 0xffff;
+
+	rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val);
+	rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val);
+	rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val);
+
+	return 0;
+}
+
+static void rsnd_src_status_clear(struct rsnd_mod *mod)
+{
+	u32 val = OUF_SRC(rsnd_mod_id(mod));
+
+	rsnd_mod_write(mod, SCU_SYS_STATUS0, val);
+	rsnd_mod_write(mod, SCU_SYS_STATUS1, val);
+}
+
+static bool rsnd_src_error_occurred(struct rsnd_mod *mod)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	u32 val0, val1;
+	u32 status0, status1;
+	bool ret = false;
+
+	val0 = val1 = OUF_SRC(rsnd_mod_id(mod));
+
+	/*
+	 * WORKAROUND
+	 *
+	 * ignore over flow error when rsnd_src_sync_is_enabled()
+	 */
+	if (rsnd_src_sync_is_enabled(mod))
+		val0 = val0 & 0xffff;
+
+	status0 = rsnd_mod_read(mod, SCU_SYS_STATUS0);
+	status1 = rsnd_mod_read(mod, SCU_SYS_STATUS1);
+	if ((status0 & val0) || (status1 & val1)) {
+		rsnd_dbg_irq_status(dev, "%s[%d] err status : 0x%08x, 0x%08x\n",
+			rsnd_mod_name(mod), rsnd_mod_id(mod),
+			status0, status1);
+
+		ret = true;
+	}
+
+	return ret;
+}
+
+static int rsnd_src_start(struct rsnd_mod *mod,
+			  struct rsnd_dai_stream *io,
+			  struct rsnd_priv *priv)
+{
+	u32 val;
+
+	/*
+	 * WORKAROUND
+	 *
+	 * Enable SRC output if you want to use sync convert together with DVC
+	 */
+	val = (rsnd_io_to_mod_dvc(io) && !rsnd_src_sync_is_enabled(mod)) ?
+		0x01 : 0x11;
+
+	rsnd_mod_write(mod, SRC_CTRL, val);
+
+	return 0;
+}
+
+static int rsnd_src_stop(struct rsnd_mod *mod,
+			 struct rsnd_dai_stream *io,
+			 struct rsnd_priv *priv)
+{
+	rsnd_mod_write(mod, SRC_CTRL, 0);
+
+	return 0;
+}
+
+static int rsnd_src_init(struct rsnd_mod *mod,
+			 struct rsnd_dai_stream *io,
+			 struct rsnd_priv *priv)
+{
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+
+	/* reset sync convert_rate */
+	src->sync.val = 0;
+
+	rsnd_mod_power_on(mod);
+
+	rsnd_src_activation(mod);
+
+	rsnd_src_set_convert_rate(io, mod);
+
+	rsnd_src_status_clear(mod);
+
+	return 0;
+}
+
+static int rsnd_src_quit(struct rsnd_mod *mod,
+			 struct rsnd_dai_stream *io,
+			 struct rsnd_priv *priv)
+{
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+
+	rsnd_src_halt(mod);
+
+	rsnd_mod_power_off(mod);
+
+	/* reset sync convert_rate */
+	src->sync.val = 0;
+
+	return 0;
+}
+
+static void __rsnd_src_interrupt(struct rsnd_mod *mod,
+				 struct rsnd_dai_stream *io)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	bool stop = false;
+
+	spin_lock(&priv->lock);
+
+	/* ignore all cases if not working */
+	if (!rsnd_io_is_working(io))
+		goto rsnd_src_interrupt_out;
+
+	if (rsnd_src_error_occurred(mod))
+		stop = true;
+
+	rsnd_src_status_clear(mod);
+rsnd_src_interrupt_out:
+
+	spin_unlock(&priv->lock);
+
+	if (stop)
+		snd_pcm_stop_xrun(io->substream);
+}
+
+static irqreturn_t rsnd_src_interrupt(int irq, void *data)
+{
+	struct rsnd_mod *mod = data;
+
+	rsnd_mod_interrupt(mod, __rsnd_src_interrupt);
+
+	return IRQ_HANDLED;
+}
+
+static int rsnd_src_probe_(struct rsnd_mod *mod,
+			   struct rsnd_dai_stream *io,
+			   struct rsnd_priv *priv)
+{
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	int irq = src->irq;
+	int ret;
+
+	if (irq > 0) {
+		/*
+		 * IRQ is not supported on non-DT
+		 * see
+		 *	rsnd_src_irq()
+		 */
+		ret = devm_request_irq(dev, irq,
+				       rsnd_src_interrupt,
+				       IRQF_SHARED,
+				       dev_name(dev), mod);
+		if (ret)
+			return ret;
+	}
+
+	ret = rsnd_dma_attach(io, mod, &src->dma);
+
+	return ret;
+}
+
+static int rsnd_src_pcm_new(struct rsnd_mod *mod,
+			    struct rsnd_dai_stream *io,
+			    struct snd_soc_pcm_runtime *rtd)
+{
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+	int ret;
+
+	/*
+	 * enable SRC sync convert if possible
+	 */
+
+	/*
+	 * It can't use SRC Synchronous convert
+	 * when Capture if it uses CMD
+	 */
+	if (rsnd_io_to_mod_cmd(io) && !rsnd_io_is_play(io))
+		return 0;
+
+	/*
+	 * enable sync convert
+	 */
+	ret = rsnd_kctrl_new_s(mod, io, rtd,
+			       rsnd_io_is_play(io) ?
+			       "SRC Out Rate Switch" :
+			       "SRC In Rate Switch",
+			       rsnd_kctrl_accept_anytime,
+			       rsnd_src_set_convert_rate,
+			       &src->sen, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = rsnd_kctrl_new_s(mod, io, rtd,
+			       rsnd_io_is_play(io) ?
+			       "SRC Out Rate" :
+			       "SRC In Rate",
+			       rsnd_kctrl_accept_runtime,
+			       rsnd_src_set_convert_rate,
+			       &src->sync, 192000);
+
+	return ret;
+}
+
+static struct rsnd_mod_ops rsnd_src_ops = {
+	.name	= SRC_NAME,
+	.dma_req = rsnd_src_dma_req,
+	.probe	= rsnd_src_probe_,
+	.init	= rsnd_src_init,
+	.quit	= rsnd_src_quit,
+	.start	= rsnd_src_start,
+	.stop	= rsnd_src_stop,
+	.irq	= rsnd_src_irq,
+	.hw_params = rsnd_src_hw_params,
+	.pcm_new = rsnd_src_pcm_new,
+};
+
+struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
+{
+	if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv)))
+		id = 0;
+
+	return rsnd_mod_get(rsnd_src_get(priv, id));
+}
+
+int rsnd_src_probe(struct rsnd_priv *priv)
+{
+	struct device_node *node;
+	struct device_node *np;
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_src *src;
+	struct clk *clk;
+	char name[RSND_SRC_NAME_SIZE];
+	int i, nr, ret;
+
+	/* This driver doesn't support Gen1 at this point */
+	if (rsnd_is_gen1(priv))
+		return 0;
+
+	node = rsnd_src_of_node(priv);
+	if (!node)
+		return 0; /* not used is not error */
+
+	nr = of_get_child_count(node);
+	if (!nr) {
+		ret = -EINVAL;
+		goto rsnd_src_probe_done;
+	}
+
+	src	= devm_kcalloc(dev, nr, sizeof(*src), GFP_KERNEL);
+	if (!src) {
+		ret = -ENOMEM;
+		goto rsnd_src_probe_done;
+	}
+
+	priv->src_nr	= nr;
+	priv->src	= src;
+
+	i = 0;
+	for_each_child_of_node(node, np) {
+		if (!of_device_is_available(np))
+			goto skip;
+
+		src = rsnd_src_get(priv, i);
+
+		snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d",
+			 SRC_NAME, i);
+
+		src->irq = irq_of_parse_and_map(np, 0);
+		if (!src->irq) {
+			ret = -EINVAL;
+			of_node_put(np);
+			goto rsnd_src_probe_done;
+		}
+
+		clk = devm_clk_get(dev, name);
+		if (IS_ERR(clk)) {
+			ret = PTR_ERR(clk);
+			of_node_put(np);
+			goto rsnd_src_probe_done;
+		}
+
+		ret = rsnd_mod_init(priv, rsnd_mod_get(src),
+				    &rsnd_src_ops, clk, rsnd_mod_get_status,
+				    RSND_MOD_SRC, i);
+		if (ret) {
+			of_node_put(np);
+			goto rsnd_src_probe_done;
+		}
+
+skip:
+		i++;
+	}
+
+	ret = 0;
+
+rsnd_src_probe_done:
+	of_node_put(node);
+
+	return ret;
+}
+
+void rsnd_src_remove(struct rsnd_priv *priv)
+{
+	struct rsnd_src *src;
+	int i;
+
+	for_each_rsnd_src(src, priv, i) {
+		rsnd_mod_quit(rsnd_mod_get(src));
+	}
+}
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
new file mode 100644
index 0000000..a566dae
--- /dev/null
+++ b/sound/soc/sh/rcar/ssi.c
@@ -0,0 +1,1200 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car SSIU/SSI support
+//
+// Copyright (C) 2013 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+//
+// Based on fsi.c
+// Kuninori Morimoto <morimoto.kuninori@renesas.com>
+
+/*
+ * you can enable below define if you don't need
+ * SSI interrupt status debug message when debugging
+ * see rsnd_dbg_irq_status()
+ *
+ * #define RSND_DEBUG_NO_IRQ_STATUS 1
+ */
+
+#include <sound/simple_card_utils.h>
+#include <linux/delay.h>
+#include "rsnd.h"
+#define RSND_SSI_NAME_SIZE 16
+
+/*
+ * SSICR
+ */
+#define	FORCE		(1 << 31)	/* Fixed */
+#define	DMEN		(1 << 28)	/* DMA Enable */
+#define	UIEN		(1 << 27)	/* Underflow Interrupt Enable */
+#define	OIEN		(1 << 26)	/* Overflow Interrupt Enable */
+#define	IIEN		(1 << 25)	/* Idle Mode Interrupt Enable */
+#define	DIEN		(1 << 24)	/* Data Interrupt Enable */
+#define	CHNL_4		(1 << 22)	/* Channels */
+#define	CHNL_6		(2 << 22)	/* Channels */
+#define	CHNL_8		(3 << 22)	/* Channels */
+#define DWL_MASK	(7 << 19)	/* Data Word Length mask */
+#define	DWL_8		(0 << 19)	/* Data Word Length */
+#define	DWL_16		(1 << 19)	/* Data Word Length */
+#define	DWL_18		(2 << 19)	/* Data Word Length */
+#define	DWL_20		(3 << 19)	/* Data Word Length */
+#define	DWL_22		(4 << 19)	/* Data Word Length */
+#define	DWL_24		(5 << 19)	/* Data Word Length */
+#define	DWL_32		(6 << 19)	/* Data Word Length */
+
+#define	SWL_32		(3 << 16)	/* R/W System Word Length */
+#define	SCKD		(1 << 15)	/* Serial Bit Clock Direction */
+#define	SWSD		(1 << 14)	/* Serial WS Direction */
+#define	SCKP		(1 << 13)	/* Serial Bit Clock Polarity */
+#define	SWSP		(1 << 12)	/* Serial WS Polarity */
+#define	SDTA		(1 << 10)	/* Serial Data Alignment */
+#define	PDTA		(1 <<  9)	/* Parallel Data Alignment */
+#define	DEL		(1 <<  8)	/* Serial Data Delay */
+#define	CKDV(v)		(v <<  4)	/* Serial Clock Division Ratio */
+#define	TRMD		(1 <<  1)	/* Transmit/Receive Mode Select */
+#define	EN		(1 <<  0)	/* SSI Module Enable */
+
+/*
+ * SSISR
+ */
+#define	UIRQ		(1 << 27)	/* Underflow Error Interrupt Status */
+#define	OIRQ		(1 << 26)	/* Overflow Error Interrupt Status */
+#define	IIRQ		(1 << 25)	/* Idle Mode Interrupt Status */
+#define	DIRQ		(1 << 24)	/* Data Interrupt Status Flag */
+
+/*
+ * SSIWSR
+ */
+#define CONT		(1 << 8)	/* WS Continue Function */
+#define WS_MODE		(1 << 0)	/* WS Mode */
+
+#define SSI_NAME "ssi"
+
+struct rsnd_ssi {
+	struct rsnd_mod mod;
+	struct rsnd_mod *dma;
+
+	u32 flags;
+	u32 cr_own;
+	u32 cr_clk;
+	u32 cr_mode;
+	u32 cr_en;
+	u32 wsr;
+	int chan;
+	int rate;
+	int irq;
+	unsigned int usrcnt;
+
+	/* for PIO */
+	int byte_pos;
+	int byte_per_period;
+	int next_period_byte;
+};
+
+/* flags */
+#define RSND_SSI_CLK_PIN_SHARE		(1 << 0)
+#define RSND_SSI_NO_BUSIF		(1 << 1) /* SSI+DMA without BUSIF */
+#define RSND_SSI_HDMI0			(1 << 2) /* for HDMI0 */
+#define RSND_SSI_HDMI1			(1 << 3) /* for HDMI1 */
+#define RSND_SSI_PROBED			(1 << 4)
+
+#define for_each_rsnd_ssi(pos, priv, i)					\
+	for (i = 0;							\
+	     (i < rsnd_ssi_nr(priv)) &&					\
+		((pos) = ((struct rsnd_ssi *)(priv)->ssi + i));		\
+	     i++)
+
+#define rsnd_ssi_get(priv, id) ((struct rsnd_ssi *)(priv->ssi) + id)
+#define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
+#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
+#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
+#define rsnd_ssi_is_multi_slave(mod, io) \
+	(rsnd_ssi_multi_slaves(io) & (1 << rsnd_mod_id(mod)))
+#define rsnd_ssi_is_run_mods(mod, io) \
+	(rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod)))
+#define rsnd_ssi_can_output_clk(mod) (!__rsnd_ssi_is_pin_sharing(mod))
+
+int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io)
+{
+	struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+	if (rsnd_flags_has(ssi, RSND_SSI_HDMI0))
+		return RSND_SSI_HDMI_PORT0;
+
+	if (rsnd_flags_has(ssi, RSND_SSI_HDMI1))
+		return RSND_SSI_HDMI_PORT1;
+
+	return 0;
+}
+
+int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
+{
+	struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	int use_busif = 0;
+
+	if (!rsnd_ssi_is_dma_mode(mod))
+		return 0;
+
+	if (!(rsnd_flags_has(ssi, RSND_SSI_NO_BUSIF)))
+		use_busif = 1;
+	if (rsnd_io_to_mod_src(io))
+		use_busif = 1;
+
+	return use_busif;
+}
+
+static void rsnd_ssi_status_clear(struct rsnd_mod *mod)
+{
+	rsnd_mod_write(mod, SSISR, 0);
+}
+
+static u32 rsnd_ssi_status_get(struct rsnd_mod *mod)
+{
+	return rsnd_mod_read(mod, SSISR);
+}
+
+static void rsnd_ssi_status_check(struct rsnd_mod *mod,
+				  u32 bit)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	u32 status;
+	int i;
+
+	for (i = 0; i < 1024; i++) {
+		status = rsnd_ssi_status_get(mod);
+		if (status & bit)
+			return;
+
+		udelay(5);
+	}
+
+	dev_warn(dev, "%s[%d] status check failed\n",
+		 rsnd_mod_name(mod), rsnd_mod_id(mod));
+}
+
+static u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
+{
+	struct rsnd_mod *mod;
+	enum rsnd_mod_type types[] = {
+		RSND_MOD_SSIM1,
+		RSND_MOD_SSIM2,
+		RSND_MOD_SSIM3,
+	};
+	int i, mask;
+
+	mask = 0;
+	for (i = 0; i < ARRAY_SIZE(types); i++) {
+		mod = rsnd_io_to_mod(io, types[i]);
+		if (!mod)
+			continue;
+
+		mask |= 1 << rsnd_mod_id(mod);
+	}
+
+	return mask;
+}
+
+static u32 rsnd_ssi_run_mods(struct rsnd_dai_stream *io)
+{
+	struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+	struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
+	u32 mods;
+
+	mods = rsnd_ssi_multi_slaves_runtime(io) |
+		1 << rsnd_mod_id(ssi_mod);
+
+	if (ssi_parent_mod)
+		mods |= 1 << rsnd_mod_id(ssi_parent_mod);
+
+	return mods;
+}
+
+u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io)
+{
+	if (rsnd_runtime_is_ssi_multi(io))
+		return rsnd_ssi_multi_slaves(io);
+
+	return 0;
+}
+
+unsigned int rsnd_ssi_clk_query(struct rsnd_priv *priv,
+		       int param1, int param2, int *idx)
+{
+	int ssi_clk_mul_table[] = {
+		1, 2, 4, 8, 16, 6, 12,
+	};
+	int j, ret;
+	unsigned int main_rate;
+
+	for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
+
+		/*
+		 * It will set SSIWSR.CONT here, but SSICR.CKDV = 000
+		 * with it is not allowed. (SSIWSR.WS_MODE with
+		 * SSICR.CKDV = 000 is not allowed either).
+		 * Skip it. See SSICR.CKDV
+		 */
+		if (j == 0)
+			continue;
+
+		/*
+		 * this driver is assuming that
+		 * system word is 32bit x chan
+		 * see rsnd_ssi_init()
+		 */
+		main_rate = 32 * param1 * param2 * ssi_clk_mul_table[j];
+
+		ret = rsnd_adg_clk_query(priv, main_rate);
+		if (ret < 0)
+			continue;
+
+		if (idx)
+			*idx = j;
+
+		return main_rate;
+	}
+
+	return 0;
+}
+
+static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
+				     struct rsnd_dai_stream *io)
+{
+	struct rsnd_priv *priv = rsnd_io_to_priv(io);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	int chan = rsnd_runtime_channel_for_ssi(io);
+	int idx, ret;
+	unsigned int main_rate;
+	unsigned int rate = rsnd_io_is_play(io) ?
+		rsnd_src_get_out_rate(priv, io) :
+		rsnd_src_get_in_rate(priv, io);
+
+	if (!rsnd_rdai_is_clk_master(rdai))
+		return 0;
+
+	if (!rsnd_ssi_can_output_clk(mod))
+		return 0;
+
+	if (rsnd_ssi_is_multi_slave(mod, io))
+		return 0;
+
+	if (ssi->usrcnt > 1) {
+		if (ssi->rate != rate) {
+			dev_err(dev, "SSI parent/child should use same rate\n");
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+
+	main_rate = rsnd_ssi_clk_query(priv, rate, chan, &idx);
+	if (!main_rate) {
+		dev_err(dev, "unsupported clock rate\n");
+		return -EIO;
+	}
+
+	ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * SSI clock will be output contiguously
+	 * by below settings.
+	 * This means, rsnd_ssi_master_clk_start()
+	 * and rsnd_ssi_register_setup() are necessary
+	 * for SSI parent
+	 *
+	 * SSICR  : FORCE, SCKD, SWSD
+	 * SSIWSR : CONT
+	 */
+	ssi->cr_clk = FORCE | SWL_32 | SCKD | SWSD | CKDV(idx);
+	ssi->wsr = CONT;
+	ssi->rate = rate;
+
+	dev_dbg(dev, "%s[%d] outputs %u Hz\n",
+		rsnd_mod_name(mod),
+		rsnd_mod_id(mod), rate);
+
+	return 0;
+}
+
+static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod,
+				     struct rsnd_dai_stream *io)
+{
+	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+	if (!rsnd_rdai_is_clk_master(rdai))
+		return;
+
+	if (!rsnd_ssi_can_output_clk(mod))
+		return;
+
+	if (ssi->usrcnt > 1)
+		return;
+
+	ssi->cr_clk	= 0;
+	ssi->rate	= 0;
+
+	rsnd_adg_ssi_clk_stop(mod);
+}
+
+static void rsnd_ssi_config_init(struct rsnd_mod *mod,
+				struct rsnd_dai_stream *io)
+{
+	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	u32 cr_own	= ssi->cr_own;
+	u32 cr_mode	= ssi->cr_mode;
+	u32 wsr		= ssi->wsr;
+	int is_tdm;
+
+	is_tdm = rsnd_runtime_is_ssi_tdm(io);
+
+	/*
+	 * always use 32bit system word.
+	 * see also rsnd_ssi_master_clk_enable()
+	 */
+	cr_own |= FORCE | SWL_32;
+
+	if (rdai->bit_clk_inv)
+		cr_own |= SCKP;
+	if (rdai->frm_clk_inv ^ is_tdm)
+		cr_own |= SWSP;
+	if (rdai->data_alignment)
+		cr_own |= SDTA;
+	if (rdai->sys_delay)
+		cr_own |= DEL;
+
+	/*
+	 * We shouldn't exchange SWSP after running.
+	 * This means, parent needs to care it.
+	 */
+	if (rsnd_ssi_is_parent(mod, io))
+		goto init_end;
+
+	if (rsnd_io_is_play(io))
+		cr_own |= TRMD;
+
+	cr_own &= ~DWL_MASK;
+	switch (snd_pcm_format_width(runtime->format)) {
+	case 16:
+		cr_own |= DWL_16;
+		break;
+	case 24:
+		cr_own |= DWL_24;
+		break;
+	}
+
+	if (rsnd_ssi_is_dma_mode(mod)) {
+		cr_mode = UIEN | OIEN |	/* over/under run */
+			  DMEN;		/* DMA : enable DMA */
+	} else {
+		cr_mode = DIEN;		/* PIO : enable Data interrupt */
+	}
+
+	/*
+	 * TDM Extend Mode
+	 * see
+	 *	rsnd_ssiu_init_gen2()
+	 */
+	wsr = ssi->wsr;
+	if (is_tdm) {
+		wsr	|= WS_MODE;
+		cr_own	|= CHNL_8;
+	}
+init_end:
+	ssi->cr_own	= cr_own;
+	ssi->cr_mode	= cr_mode;
+	ssi->wsr	= wsr;
+}
+
+static void rsnd_ssi_register_setup(struct rsnd_mod *mod)
+{
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+	rsnd_mod_write(mod, SSIWSR,	ssi->wsr);
+	rsnd_mod_write(mod, SSICR,	ssi->cr_own	|
+					ssi->cr_clk	|
+					ssi->cr_mode	|
+					ssi->cr_en);
+}
+
+/*
+ *	SSI mod common functions
+ */
+static int rsnd_ssi_init(struct rsnd_mod *mod,
+			 struct rsnd_dai_stream *io,
+			 struct rsnd_priv *priv)
+{
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+	if (!rsnd_ssi_is_run_mods(mod, io))
+		return 0;
+
+	ssi->usrcnt++;
+
+	rsnd_mod_power_on(mod);
+
+	rsnd_ssi_config_init(mod, io);
+
+	rsnd_ssi_register_setup(mod);
+
+	/* clear error status */
+	rsnd_ssi_status_clear(mod);
+
+	return 0;
+}
+
+static int rsnd_ssi_quit(struct rsnd_mod *mod,
+			 struct rsnd_dai_stream *io,
+			 struct rsnd_priv *priv)
+{
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	struct device *dev = rsnd_priv_to_dev(priv);
+
+	if (!rsnd_ssi_is_run_mods(mod, io))
+		return 0;
+
+	if (!ssi->usrcnt) {
+		dev_err(dev, "%s[%d] usrcnt error\n",
+			rsnd_mod_name(mod), rsnd_mod_id(mod));
+		return -EIO;
+	}
+
+	rsnd_ssi_master_clk_stop(mod, io);
+
+	rsnd_mod_power_off(mod);
+
+	ssi->usrcnt--;
+
+	if (!ssi->usrcnt) {
+		ssi->cr_own	= 0;
+		ssi->cr_mode	= 0;
+		ssi->wsr	= 0;
+	}
+
+	return 0;
+}
+
+static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
+			      struct rsnd_dai_stream *io,
+			      struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params)
+{
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	int chan = params_channels(params);
+
+	/*
+	 * snd_pcm_ops::hw_params will be called *before*
+	 * snd_soc_dai_ops::trigger. Thus, ssi->usrcnt is 0
+	 * in 1st call.
+	 */
+	if (ssi->usrcnt) {
+		/*
+		 * Already working.
+		 * It will happen if SSI has parent/child connection.
+		 * it is error if child <-> parent SSI uses
+		 * different channels.
+		 */
+		if (ssi->chan != chan)
+			return -EIO;
+	}
+
+	ssi->chan = chan;
+
+	return 0;
+}
+
+static int rsnd_ssi_start(struct rsnd_mod *mod,
+			  struct rsnd_dai_stream *io,
+			  struct rsnd_priv *priv)
+{
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+	if (!rsnd_ssi_is_run_mods(mod, io))
+		return 0;
+
+	/*
+	 * EN will be set via SSIU :: SSI_CONTROL
+	 * if Multi channel mode
+	 */
+	if (rsnd_ssi_multi_slaves_runtime(io))
+		return 0;
+
+	/*
+	 * EN is for data output.
+	 * SSI parent EN is not needed.
+	 */
+	if (rsnd_ssi_is_parent(mod, io))
+		return 0;
+
+	ssi->cr_en = EN;
+
+	rsnd_mod_write(mod, SSICR,	ssi->cr_own	|
+					ssi->cr_clk	|
+					ssi->cr_mode	|
+					ssi->cr_en);
+
+	return 0;
+}
+
+static int rsnd_ssi_stop(struct rsnd_mod *mod,
+			 struct rsnd_dai_stream *io,
+			 struct rsnd_priv *priv)
+{
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	u32 cr;
+
+	if (!rsnd_ssi_is_run_mods(mod, io))
+		return 0;
+
+	if (rsnd_ssi_is_parent(mod, io))
+		return 0;
+
+	cr  =	ssi->cr_own	|
+		ssi->cr_clk;
+
+	/*
+	 * disable all IRQ,
+	 * Playback: Wait all data was sent
+	 * Capture:  It might not receave data. Do nothing
+	 */
+	if (rsnd_io_is_play(io)) {
+		rsnd_mod_write(mod, SSICR, cr | EN);
+		rsnd_ssi_status_check(mod, DIRQ);
+	}
+
+	/*
+	 * disable SSI,
+	 * and, wait idle state
+	 */
+	rsnd_mod_write(mod, SSICR, cr);	/* disabled all */
+	rsnd_ssi_status_check(mod, IIRQ);
+
+	ssi->cr_en = 0;
+
+	return 0;
+}
+
+static int rsnd_ssi_irq(struct rsnd_mod *mod,
+			struct rsnd_dai_stream *io,
+			struct rsnd_priv *priv,
+			int enable)
+{
+	u32 val = 0;
+
+	if (rsnd_is_gen1(priv))
+		return 0;
+
+	if (rsnd_ssi_is_parent(mod, io))
+		return 0;
+
+	if (!rsnd_ssi_is_run_mods(mod, io))
+		return 0;
+
+	if (enable)
+		val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000;
+
+	rsnd_mod_write(mod, SSI_INT_ENABLE, val);
+
+	return 0;
+}
+
+static bool rsnd_ssi_pio_interrupt(struct rsnd_mod *mod,
+				   struct rsnd_dai_stream *io);
+static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
+				 struct rsnd_dai_stream *io)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	int is_dma = rsnd_ssi_is_dma_mode(mod);
+	u32 status;
+	bool elapsed = false;
+	bool stop = false;
+
+	spin_lock(&priv->lock);
+
+	/* ignore all cases if not working */
+	if (!rsnd_io_is_working(io))
+		goto rsnd_ssi_interrupt_out;
+
+	status = rsnd_ssi_status_get(mod);
+
+	/* PIO only */
+	if (!is_dma && (status & DIRQ))
+		elapsed = rsnd_ssi_pio_interrupt(mod, io);
+
+	/* DMA only */
+	if (is_dma && (status & (UIRQ | OIRQ))) {
+		rsnd_dbg_irq_status(dev, "%s[%d] err status : 0x%08x\n",
+			rsnd_mod_name(mod), rsnd_mod_id(mod), status);
+
+		stop = true;
+	}
+
+	rsnd_ssi_status_clear(mod);
+rsnd_ssi_interrupt_out:
+	spin_unlock(&priv->lock);
+
+	if (elapsed)
+		rsnd_dai_period_elapsed(io);
+
+	if (stop)
+		snd_pcm_stop_xrun(io->substream);
+
+}
+
+static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
+{
+	struct rsnd_mod *mod = data;
+
+	rsnd_mod_interrupt(mod, __rsnd_ssi_interrupt);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ *		SSI PIO
+ */
+static void rsnd_ssi_parent_attach(struct rsnd_mod *mod,
+				   struct rsnd_dai_stream *io)
+{
+	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+
+	if (!__rsnd_ssi_is_pin_sharing(mod))
+		return;
+
+	if (!rsnd_rdai_is_clk_master(rdai))
+		return;
+
+	switch (rsnd_mod_id(mod)) {
+	case 1:
+	case 2:
+		rsnd_dai_connect(rsnd_ssi_mod_get(priv, 0), io, RSND_MOD_SSIP);
+		break;
+	case 4:
+		rsnd_dai_connect(rsnd_ssi_mod_get(priv, 3), io, RSND_MOD_SSIP);
+		break;
+	case 8:
+		rsnd_dai_connect(rsnd_ssi_mod_get(priv, 7), io, RSND_MOD_SSIP);
+		break;
+	}
+}
+
+static int rsnd_ssi_pcm_new(struct rsnd_mod *mod,
+			    struct rsnd_dai_stream *io,
+			    struct snd_soc_pcm_runtime *rtd)
+{
+	/*
+	 * rsnd_rdai_is_clk_master() will be enabled after set_fmt,
+	 * and, pcm_new will be called after it.
+	 * This function reuse pcm_new at this point.
+	 */
+	rsnd_ssi_parent_attach(mod, io);
+
+	return 0;
+}
+
+static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
+				 struct rsnd_dai_stream *io,
+				 struct rsnd_priv *priv)
+{
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	int ret;
+
+	/*
+	 * SSIP/SSIU/IRQ are not needed on
+	 * SSI Multi slaves
+	 */
+	if (rsnd_ssi_is_multi_slave(mod, io))
+		return 0;
+
+	/*
+	 * It can't judge ssi parent at this point
+	 * see rsnd_ssi_pcm_new()
+	 */
+
+	ret = rsnd_ssiu_attach(io, mod);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * SSI might be called again as PIO fallback
+	 * It is easy to manual handling for IRQ request/free
+	 *
+	 * OTOH, this function might be called many times if platform is
+	 * using MIX. It needs xxx_attach() many times on xxx_probe().
+	 * Because of it, we can't control .probe/.remove calling count by
+	 * mod->status.
+	 * But it don't need to call request_irq() many times.
+	 * Let's control it by RSND_SSI_PROBED flag.
+	 */
+	if (!rsnd_flags_has(ssi, RSND_SSI_PROBED)) {
+		ret = request_irq(ssi->irq,
+				  rsnd_ssi_interrupt,
+				  IRQF_SHARED,
+				  dev_name(dev), mod);
+
+		rsnd_flags_set(ssi, RSND_SSI_PROBED);
+	}
+
+	return ret;
+}
+
+static int rsnd_ssi_common_remove(struct rsnd_mod *mod,
+				  struct rsnd_dai_stream *io,
+				  struct rsnd_priv *priv)
+{
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	struct rsnd_mod *pure_ssi_mod = rsnd_io_to_mod_ssi(io);
+
+	/* Do nothing if non SSI (= SSI parent, multi SSI) mod */
+	if (pure_ssi_mod != mod)
+		return 0;
+
+	/* PIO will request IRQ again */
+	if (rsnd_flags_has(ssi, RSND_SSI_PROBED)) {
+		free_irq(ssi->irq, mod);
+
+		rsnd_flags_del(ssi, RSND_SSI_PROBED);
+	}
+
+	return 0;
+}
+
+/*
+ *	SSI PIO functions
+ */
+static bool rsnd_ssi_pio_interrupt(struct rsnd_mod *mod,
+				   struct rsnd_dai_stream *io)
+{
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	u32 *buf = (u32 *)(runtime->dma_area + ssi->byte_pos);
+	int shift = 0;
+	int byte_pos;
+	bool elapsed = false;
+
+	if (snd_pcm_format_width(runtime->format) == 24)
+		shift = 8;
+
+	/*
+	 * 8/16/32 data can be assesse to TDR/RDR register
+	 * directly as 32bit data
+	 * see rsnd_ssi_init()
+	 */
+	if (rsnd_io_is_play(io))
+		rsnd_mod_write(mod, SSITDR, (*buf) << shift);
+	else
+		*buf = (rsnd_mod_read(mod, SSIRDR) >> shift);
+
+	byte_pos = ssi->byte_pos + sizeof(*buf);
+
+	if (byte_pos >= ssi->next_period_byte) {
+		int period_pos = byte_pos / ssi->byte_per_period;
+
+		if (period_pos >= runtime->periods) {
+			byte_pos = 0;
+			period_pos = 0;
+		}
+
+		ssi->next_period_byte = (period_pos + 1) * ssi->byte_per_period;
+
+		elapsed = true;
+	}
+
+	WRITE_ONCE(ssi->byte_pos, byte_pos);
+
+	return elapsed;
+}
+
+static int rsnd_ssi_pio_init(struct rsnd_mod *mod,
+			     struct rsnd_dai_stream *io,
+			     struct rsnd_priv *priv)
+{
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+	if (!rsnd_ssi_is_parent(mod, io)) {
+		ssi->byte_pos		= 0;
+		ssi->byte_per_period	= runtime->period_size *
+					  runtime->channels *
+					  samples_to_bytes(runtime, 1);
+		ssi->next_period_byte	= ssi->byte_per_period;
+	}
+
+	return rsnd_ssi_init(mod, io, priv);
+}
+
+static int rsnd_ssi_pio_pointer(struct rsnd_mod *mod,
+			    struct rsnd_dai_stream *io,
+			    snd_pcm_uframes_t *pointer)
+{
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+
+	*pointer = bytes_to_frames(runtime, READ_ONCE(ssi->byte_pos));
+
+	return 0;
+}
+
+static int rsnd_ssi_prepare(struct rsnd_mod *mod,
+			    struct rsnd_dai_stream *io,
+			    struct rsnd_priv *priv)
+{
+	return rsnd_ssi_master_clk_start(mod, io);
+}
+
+static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
+	.name	= SSI_NAME,
+	.probe	= rsnd_ssi_common_probe,
+	.remove	= rsnd_ssi_common_remove,
+	.init	= rsnd_ssi_pio_init,
+	.quit	= rsnd_ssi_quit,
+	.start	= rsnd_ssi_start,
+	.stop	= rsnd_ssi_stop,
+	.irq	= rsnd_ssi_irq,
+	.pointer = rsnd_ssi_pio_pointer,
+	.pcm_new = rsnd_ssi_pcm_new,
+	.hw_params = rsnd_ssi_hw_params,
+	.prepare = rsnd_ssi_prepare,
+};
+
+static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
+			      struct rsnd_dai_stream *io,
+			      struct rsnd_priv *priv)
+{
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	int ret;
+
+	/*
+	 * SSIP/SSIU/IRQ/DMA are not needed on
+	 * SSI Multi slaves
+	 */
+	if (rsnd_ssi_is_multi_slave(mod, io))
+		return 0;
+
+	ret = rsnd_ssi_common_probe(mod, io, priv);
+	if (ret)
+		return ret;
+
+	/* SSI probe might be called many times in MUX multi path */
+	ret = rsnd_dma_attach(io, mod, &ssi->dma);
+
+	return ret;
+}
+
+static int rsnd_ssi_fallback(struct rsnd_mod *mod,
+			     struct rsnd_dai_stream *io,
+			     struct rsnd_priv *priv)
+{
+	struct device *dev = rsnd_priv_to_dev(priv);
+
+	/*
+	 * fallback to PIO
+	 *
+	 * SSI .probe might be called again.
+	 * see
+	 *	rsnd_rdai_continuance_probe()
+	 */
+	mod->ops = &rsnd_ssi_pio_ops;
+
+	dev_info(dev, "%s[%d] fallback to PIO mode\n",
+		 rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+	return 0;
+}
+
+static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io,
+					 struct rsnd_mod *mod)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	int is_play = rsnd_io_is_play(io);
+	char *name;
+
+	if (rsnd_ssi_use_busif(io))
+		name = is_play ? "rxu" : "txu";
+	else
+		name = is_play ? "rx" : "tx";
+
+	return rsnd_dma_request_channel(rsnd_ssi_of_node(priv),
+					mod, name);
+}
+
+static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
+	.name	= SSI_NAME,
+	.dma_req = rsnd_ssi_dma_req,
+	.probe	= rsnd_ssi_dma_probe,
+	.remove	= rsnd_ssi_common_remove,
+	.init	= rsnd_ssi_init,
+	.quit	= rsnd_ssi_quit,
+	.start	= rsnd_ssi_start,
+	.stop	= rsnd_ssi_stop,
+	.irq	= rsnd_ssi_irq,
+	.pcm_new = rsnd_ssi_pcm_new,
+	.fallback = rsnd_ssi_fallback,
+	.hw_params = rsnd_ssi_hw_params,
+	.prepare = rsnd_ssi_prepare,
+};
+
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
+{
+	return mod->ops == &rsnd_ssi_dma_ops;
+}
+
+
+/*
+ *		ssi mod function
+ */
+static void rsnd_ssi_connect(struct rsnd_mod *mod,
+			     struct rsnd_dai_stream *io)
+{
+	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+	enum rsnd_mod_type types[] = {
+		RSND_MOD_SSI,
+		RSND_MOD_SSIM1,
+		RSND_MOD_SSIM2,
+		RSND_MOD_SSIM3,
+	};
+	enum rsnd_mod_type type;
+	int i;
+
+	/* try SSI -> SSIM1 -> SSIM2 -> SSIM3 */
+	for (i = 0; i < ARRAY_SIZE(types); i++) {
+		type = types[i];
+		if (!rsnd_io_to_mod(io, type)) {
+			rsnd_dai_connect(mod, io, type);
+			rsnd_rdai_channels_set(rdai, (i + 1) * 2);
+			rsnd_rdai_ssi_lane_set(rdai, (i + 1));
+			return;
+		}
+	}
+}
+
+void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
+			    struct device_node *playback,
+			    struct device_node *capture)
+{
+	struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+	struct device_node *node;
+	struct device_node *np;
+	struct rsnd_mod *mod;
+	int i;
+
+	node = rsnd_ssi_of_node(priv);
+	if (!node)
+		return;
+
+	i = 0;
+	for_each_child_of_node(node, np) {
+		mod = rsnd_ssi_mod_get(priv, i);
+		if (np == playback)
+			rsnd_ssi_connect(mod, &rdai->playback);
+		if (np == capture)
+			rsnd_ssi_connect(mod, &rdai->capture);
+		i++;
+	}
+
+	of_node_put(node);
+}
+
+static void __rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv,
+					     struct rsnd_dai_stream *io,
+					     struct device_node *remote_ep)
+{
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
+	struct rsnd_ssi *ssi;
+	struct device_node *remote_node = of_graph_get_port_parent(remote_ep);
+
+	/* support Gen3 only */
+	if (!rsnd_is_gen3(priv))
+		return;
+
+	if (!mod)
+		return;
+
+	ssi  = rsnd_mod_to_ssi(mod);
+
+	/* HDMI0 */
+	if (strstr(remote_node->full_name, "hdmi@fead0000")) {
+		rsnd_flags_set(ssi, RSND_SSI_HDMI0);
+		dev_dbg(dev, "%s[%d] connected to HDMI0\n",
+			 rsnd_mod_name(mod), rsnd_mod_id(mod));
+	}
+
+	/* HDMI1 */
+	if (strstr(remote_node->full_name, "hdmi@feae0000")) {
+		rsnd_flags_set(ssi, RSND_SSI_HDMI1);
+		dev_dbg(dev, "%s[%d] connected to HDMI1\n",
+			rsnd_mod_name(mod), rsnd_mod_id(mod));
+	}
+}
+
+void rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv,
+				    struct device_node *endpoint,
+				    int dai_i)
+{
+	struct rsnd_dai *rdai = rsnd_rdai_get(priv, dai_i);
+	struct device_node *remote_ep;
+
+	remote_ep = of_graph_get_remote_endpoint(endpoint);
+	if (!remote_ep)
+		return;
+
+	__rsnd_ssi_parse_hdmi_connection(priv, &rdai->playback, remote_ep);
+	__rsnd_ssi_parse_hdmi_connection(priv, &rdai->capture,  remote_ep);
+}
+
+struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
+{
+	if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
+		id = 0;
+
+	return rsnd_mod_get(rsnd_ssi_get(priv, id));
+}
+
+int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
+{
+	if (!mod)
+		return 0;
+
+	return !!(rsnd_flags_has(rsnd_mod_to_ssi(mod), RSND_SSI_CLK_PIN_SHARE));
+}
+
+static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io,
+				struct rsnd_mod *mod,
+				enum rsnd_mod_type type)
+{
+	/*
+	 * SSIP (= SSI parent) needs to be special, otherwise,
+	 * 2nd SSI might doesn't start. see also rsnd_mod_call()
+	 *
+	 * We can't include parent SSI status on SSI, because we don't know
+	 * how many SSI requests parent SSI. Thus, it is localed on "io" now.
+	 * ex) trouble case
+	 *	Playback: SSI0
+	 *	Capture : SSI1 (needs SSI0)
+	 *
+	 * 1) start Capture  ->	SSI0/SSI1 are started.
+	 * 2) start Playback ->	SSI0 doesn't work, because it is already
+	 *			marked as "started" on 1)
+	 *
+	 * OTOH, using each mod's status is good for MUX case.
+	 * It doesn't need to start in 2nd start
+	 * ex)
+	 *	IO-0: SRC0 -> CTU1 -+-> MUX -> DVC -> SSIU -> SSI0
+	 *			    |
+	 *	IO-1: SRC1 -> CTU2 -+
+	 *
+	 * 1) start IO-0 ->	start SSI0
+	 * 2) start IO-1 ->	SSI0 doesn't need to start, because it is
+	 *			already started on 1)
+	 */
+	if (type == RSND_MOD_SSIP)
+		return &io->parent_ssi_status;
+
+	return rsnd_mod_get_status(io, mod, type);
+}
+
+int rsnd_ssi_probe(struct rsnd_priv *priv)
+{
+	struct device_node *node;
+	struct device_node *np;
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_mod_ops *ops;
+	struct clk *clk;
+	struct rsnd_ssi *ssi;
+	char name[RSND_SSI_NAME_SIZE];
+	int i, nr, ret;
+
+	node = rsnd_ssi_of_node(priv);
+	if (!node)
+		return -EINVAL;
+
+	nr = of_get_child_count(node);
+	if (!nr) {
+		ret = -EINVAL;
+		goto rsnd_ssi_probe_done;
+	}
+
+	ssi	= devm_kcalloc(dev, nr, sizeof(*ssi), GFP_KERNEL);
+	if (!ssi) {
+		ret = -ENOMEM;
+		goto rsnd_ssi_probe_done;
+	}
+
+	priv->ssi	= ssi;
+	priv->ssi_nr	= nr;
+
+	i = 0;
+	for_each_child_of_node(node, np) {
+		if (!of_device_is_available(np))
+			goto skip;
+
+		ssi = rsnd_ssi_get(priv, i);
+
+		snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d",
+			 SSI_NAME, i);
+
+		clk = devm_clk_get(dev, name);
+		if (IS_ERR(clk)) {
+			ret = PTR_ERR(clk);
+			of_node_put(np);
+			goto rsnd_ssi_probe_done;
+		}
+
+		if (of_get_property(np, "shared-pin", NULL))
+			rsnd_flags_set(ssi, RSND_SSI_CLK_PIN_SHARE);
+
+		if (of_get_property(np, "no-busif", NULL))
+			rsnd_flags_set(ssi, RSND_SSI_NO_BUSIF);
+
+		ssi->irq = irq_of_parse_and_map(np, 0);
+		if (!ssi->irq) {
+			ret = -EINVAL;
+			of_node_put(np);
+			goto rsnd_ssi_probe_done;
+		}
+
+		if (of_property_read_bool(np, "pio-transfer"))
+			ops = &rsnd_ssi_pio_ops;
+		else
+			ops = &rsnd_ssi_dma_ops;
+
+		ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
+				    rsnd_ssi_get_status, RSND_MOD_SSI, i);
+		if (ret) {
+			of_node_put(np);
+			goto rsnd_ssi_probe_done;
+		}
+skip:
+		i++;
+	}
+
+	ret = 0;
+
+rsnd_ssi_probe_done:
+	of_node_put(node);
+
+	return ret;
+}
+
+void rsnd_ssi_remove(struct rsnd_priv *priv)
+{
+	struct rsnd_ssi *ssi;
+	int i;
+
+	for_each_rsnd_ssi(ssi, priv, i) {
+		rsnd_mod_quit(rsnd_mod_get(ssi));
+	}
+}
diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c
new file mode 100644
index 0000000..016fbf5
--- /dev/null
+++ b/sound/soc/sh/rcar/ssiu.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car SSIU support
+//
+// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+#include "rsnd.h"
+
+#define SSIU_NAME "ssiu"
+
+struct rsnd_ssiu {
+	struct rsnd_mod mod;
+};
+
+#define rsnd_ssiu_nr(priv) ((priv)->ssiu_nr)
+#define for_each_rsnd_ssiu(pos, priv, i)				\
+	for (i = 0;							\
+	     (i < rsnd_ssiu_nr(priv)) &&				\
+		     ((pos) = ((struct rsnd_ssiu *)(priv)->ssiu + i));	\
+	     i++)
+
+static int rsnd_ssiu_init(struct rsnd_mod *mod,
+			  struct rsnd_dai_stream *io,
+			  struct rsnd_priv *priv)
+{
+	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+	u32 multi_ssi_slaves = rsnd_ssi_multi_slaves_runtime(io);
+	int use_busif = rsnd_ssi_use_busif(io);
+	int id = rsnd_mod_id(mod);
+	u32 mask1, val1;
+	u32 mask2, val2;
+
+	/* clear status */
+	switch (id) {
+	case 0:
+	case 1:
+	case 2:
+	case 3:
+	case 4:
+		rsnd_mod_write(mod, SSI_SYS_STATUS0, 0xf << (id * 4));
+		rsnd_mod_write(mod, SSI_SYS_STATUS2, 0xf << (id * 4));
+		rsnd_mod_write(mod, SSI_SYS_STATUS4, 0xf << (id * 4));
+		rsnd_mod_write(mod, SSI_SYS_STATUS6, 0xf << (id * 4));
+		break;
+	case 9:
+		rsnd_mod_write(mod, SSI_SYS_STATUS1, 0xf << 4);
+		rsnd_mod_write(mod, SSI_SYS_STATUS3, 0xf << 4);
+		rsnd_mod_write(mod, SSI_SYS_STATUS5, 0xf << 4);
+		rsnd_mod_write(mod, SSI_SYS_STATUS7, 0xf << 4);
+		break;
+	}
+
+	/*
+	 * SSI_MODE0
+	 */
+	rsnd_mod_bset(mod, SSI_MODE0, (1 << id), !use_busif << id);
+
+	/*
+	 * SSI_MODE1
+	 */
+	mask1 = (1 << 4) | (1 << 20);	/* mask sync bit */
+	mask2 = (1 << 4);		/* mask sync bit */
+	val1  = val2  = 0;
+	if (id == 8) {
+		/*
+		 * SSI8 pin is sharing with SSI7, nothing to do.
+		 */
+	} else if (rsnd_ssi_is_pin_sharing(io)) {
+		int shift = -1;
+
+		switch (id) {
+		case 1:
+			shift = 0;
+			break;
+		case 2:
+			shift = 2;
+			break;
+		case 4:
+			shift = 16;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		mask1 |= 0x3 << shift;
+		val1 = rsnd_rdai_is_clk_master(rdai) ?
+			0x2 << shift : 0x1 << shift;
+
+	} else if (multi_ssi_slaves) {
+
+		mask2 |= 0x00000007;
+		mask1 |= 0x0000000f;
+
+		switch (multi_ssi_slaves) {
+		case 0x0206: /* SSI0/1/2/9 */
+			val2 = (1 << 4) | /* SSI0129 sync */
+				(rsnd_rdai_is_clk_master(rdai) ? 0x2 : 0x1);
+			/* fall through */
+		case 0x0006: /* SSI0/1/2 */
+			val1 = rsnd_rdai_is_clk_master(rdai) ?
+				0xa : 0x5;
+
+			if (!val2)  /* SSI012 sync */
+				val1 |= (1 << 4);
+		}
+	}
+
+	rsnd_mod_bset(mod, SSI_MODE1, mask1, val1);
+	rsnd_mod_bset(mod, SSI_MODE2, mask2, val2);
+
+	return 0;
+}
+
+static struct rsnd_mod_ops rsnd_ssiu_ops_gen1 = {
+	.name	= SSIU_NAME,
+	.init	= rsnd_ssiu_init,
+};
+
+static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
+			       struct rsnd_dai_stream *io,
+			       struct rsnd_priv *priv)
+{
+	int hdmi = rsnd_ssi_hdmi_port(io);
+	int ret;
+	u32 mode = 0;
+
+	ret = rsnd_ssiu_init(mod, io, priv);
+	if (ret < 0)
+		return ret;
+
+	if (rsnd_runtime_is_ssi_tdm(io)) {
+		/*
+		 * TDM Extend Mode
+		 * see
+		 *	rsnd_ssi_config_init()
+		 */
+		mode = 0x1;
+	}
+
+	rsnd_mod_write(mod, SSI_MODE, mode);
+
+	if (rsnd_ssi_use_busif(io)) {
+		rsnd_mod_write(mod, SSI_BUSIF_ADINR,
+			       rsnd_get_adinr_bit(mod, io) |
+			       (rsnd_io_is_play(io) ?
+				rsnd_runtime_channel_after_ctu(io) :
+				rsnd_runtime_channel_original(io)));
+		rsnd_mod_write(mod, SSI_BUSIF_MODE,
+			       rsnd_get_busif_shift(io, mod) | 1);
+		rsnd_mod_write(mod, SSI_BUSIF_DALIGN,
+			       rsnd_get_dalign(mod, io));
+	}
+
+	if (hdmi) {
+		enum rsnd_mod_type rsnd_ssi_array[] = {
+			RSND_MOD_SSIM1,
+			RSND_MOD_SSIM2,
+			RSND_MOD_SSIM3,
+		};
+		struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+		struct rsnd_mod *pos;
+		u32 val;
+		int i, shift;
+
+		i = rsnd_mod_id(ssi_mod);
+
+		/* output all same SSI as default */
+		val =	i << 16 |
+			i << 20 |
+			i << 24 |
+			i << 28 |
+			i;
+
+		for_each_rsnd_mod_array(i, pos, io, rsnd_ssi_array) {
+			shift	= (i * 4) + 16;
+			val	= (val & ~(0xF << shift)) |
+				rsnd_mod_id(pos) << shift;
+		}
+
+		switch (hdmi) {
+		case RSND_SSI_HDMI_PORT0:
+			rsnd_mod_write(mod, HDMI0_SEL, val);
+			break;
+		case RSND_SSI_HDMI_PORT1:
+			rsnd_mod_write(mod, HDMI1_SEL, val);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod,
+				struct rsnd_dai_stream *io,
+				struct rsnd_priv *priv)
+{
+	if (!rsnd_ssi_use_busif(io))
+		return 0;
+
+	rsnd_mod_write(mod, SSI_CTRL, 0x1);
+
+	if (rsnd_ssi_multi_slaves_runtime(io))
+		rsnd_mod_write(mod, SSI_CONTROL, 0x1);
+
+	return 0;
+}
+
+static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
+			       struct rsnd_dai_stream *io,
+			       struct rsnd_priv *priv)
+{
+	if (!rsnd_ssi_use_busif(io))
+		return 0;
+
+	rsnd_mod_write(mod, SSI_CTRL, 0);
+
+	if (rsnd_ssi_multi_slaves_runtime(io))
+		rsnd_mod_write(mod, SSI_CONTROL, 0);
+
+	return 0;
+}
+
+static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = {
+	.name	= SSIU_NAME,
+	.init	= rsnd_ssiu_init_gen2,
+	.start	= rsnd_ssiu_start_gen2,
+	.stop	= rsnd_ssiu_stop_gen2,
+};
+
+static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id)
+{
+	if (WARN_ON(id < 0 || id >= rsnd_ssiu_nr(priv)))
+		id = 0;
+
+	return rsnd_mod_get((struct rsnd_ssiu *)(priv->ssiu) + id);
+}
+
+int rsnd_ssiu_attach(struct rsnd_dai_stream *io,
+		     struct rsnd_mod *ssi_mod)
+{
+	struct rsnd_priv *priv = rsnd_io_to_priv(io);
+	struct rsnd_mod *mod = rsnd_ssiu_mod_get(priv, rsnd_mod_id(ssi_mod));
+
+	rsnd_mod_confirm_ssi(ssi_mod);
+
+	return rsnd_dai_connect(mod, io, mod->type);
+}
+
+int rsnd_ssiu_probe(struct rsnd_priv *priv)
+{
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_ssiu *ssiu;
+	struct rsnd_mod_ops *ops;
+	int i, nr, ret;
+
+	/* same number to SSI */
+	nr	= priv->ssi_nr;
+	ssiu	= devm_kcalloc(dev, nr, sizeof(*ssiu), GFP_KERNEL);
+	if (!ssiu)
+		return -ENOMEM;
+
+	priv->ssiu	= ssiu;
+	priv->ssiu_nr	= nr;
+
+	if (rsnd_is_gen1(priv))
+		ops = &rsnd_ssiu_ops_gen1;
+	else
+		ops = &rsnd_ssiu_ops_gen2;
+
+	for_each_rsnd_ssiu(ssiu, priv, i) {
+		ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu),
+				    ops, NULL, rsnd_mod_get_status,
+				    RSND_MOD_SSIU, i);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+void rsnd_ssiu_remove(struct rsnd_priv *priv)
+{
+	struct rsnd_ssiu *ssiu;
+	int i;
+
+	for_each_rsnd_ssiu(ssiu, priv, i) {
+		rsnd_mod_quit(rsnd_mod_get(ssiu));
+	}
+}
diff --git a/sound/soc/sh/sh7760-ac97.c b/sound/soc/sh/sh7760-ac97.c
new file mode 100644
index 0000000..4bb4c13
--- /dev/null
+++ b/sound/soc/sh/sh7760-ac97.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Generic AC97 sound support for SH7760
+//
+// (c) 2007 Manuel Lauss
+
+#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 v2");
+MODULE_DESCRIPTION("Generic SH7760 AC97 sound machine");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/sh/siu.h b/sound/soc/sh/siu.h
new file mode 100644
index 0000000..63a508f
--- /dev/null
+++ b/sound/soc/sh/siu.h
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// 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>
+
+#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_component_driver siu_component;
+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/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c
new file mode 100644
index 0000000..f2a386f
--- /dev/null
+++ b/sound/soc/sh/siu_dai.c
@@ -0,0 +1,799 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// 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>
+
+#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 const 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 const 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 const 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 ? */
+		if (WARN_ON(port_info->playback.rw_flg || port_info->capture.rw_flg))
+			return;
+		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 siu_probe(struct platform_device *pdev)
+{
+	const struct firmware *fw_entry;
+	struct resource *res, *region;
+	struct siu_info *info;
+	int ret;
+
+	info = devm_kmalloc(&pdev->dev, 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)
+		return ret;
+
+	/*
+	 * 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)
+		return -ENODEV;
+
+	region = devm_request_mem_region(&pdev->dev, res->start,
+					 resource_size(res), pdev->name);
+	if (!region) {
+		dev_err(&pdev->dev, "SIU region already claimed\n");
+		return -EBUSY;
+	}
+
+	info->pram = devm_ioremap(&pdev->dev, res->start, PRAM_SIZE);
+	if (!info->pram)
+		return -ENOMEM;
+	info->xram = devm_ioremap(&pdev->dev, res->start + XRAM_OFFSET,
+				  XRAM_SIZE);
+	if (!info->xram)
+		return -ENOMEM;
+	info->yram = devm_ioremap(&pdev->dev, res->start + YRAM_OFFSET,
+				  YRAM_SIZE);
+	if (!info->yram)
+		return -ENOMEM;
+	info->reg = devm_ioremap(&pdev->dev, res->start + REG_OFFSET,
+			    resource_size(res) - REG_OFFSET);
+	if (!info->reg)
+		return -ENOMEM;
+
+	dev_set_drvdata(&pdev->dev, info);
+
+	/* register using ARRAY version so we can keep dai name */
+	ret = devm_snd_soc_register_component(&pdev->dev, &siu_component,
+					      &siu_i2s_dai, 1);
+	if (ret < 0)
+		return ret;
+
+	pm_runtime_enable(&pdev->dev);
+
+	return 0;
+}
+
+static int siu_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+	return 0;
+}
+
+static struct platform_driver siu_driver = {
+	.driver 	= {
+		.name	= "siu-pcm-audio",
+	},
+	.probe		= siu_probe,
+	.remove		= 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/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c
new file mode 100644
index 0000000..e263757
--- /dev/null
+++ b/sound/soc/sh/siu_pcm.c
@@ -0,0 +1,602 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// 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>
+
+#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 DRV_NAME "siu-i2s"
+#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 = dmaengine_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 = dmaengine_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->shdma_slave.slave_id);
+
+	chan->private = &param->shdma_slave;
+	return true;
+}
+
+static int siu_pcm_open(struct snd_pcm_substream *ss)
+{
+	/* Playback / Capture */
+	struct snd_soc_pcm_runtime *rtd = ss->private_data;
+	struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+	struct siu_platform *pdata = component->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->shdma_slave.slave_id = port ? pdata->dma_slave_tx_b :
+			pdata->dma_slave_tx_a;
+	} else {
+		siu_stream = &port_info->capture;
+		param = &siu_stream->param;
+		param->shdma_slave.slave_id = port ? pdata->dma_slave_rx_b :
+			pdata->dma_slave_rx_a;
+	}
+
+	/* 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);
+
+	dev_dbg(pcm->card->dev, "%s\n", __func__);
+}
+
+static const 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_component_driver siu_component = {
+	.name		= DRV_NAME,
+	.ops			= &siu_pcm_ops,
+	.pcm_new	= siu_pcm_new,
+	.pcm_free	= siu_pcm_free,
+};
+EXPORT_SYMBOL_GPL(siu_component);
diff --git a/sound/soc/sh/ssi.c b/sound/soc/sh/ssi.c
new file mode 100644
index 0000000..8125fa3
--- /dev/null
+++ b/sound/soc/sh/ssi.c
@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Serial Sound Interface (I2S) support for SH7760/SH7780
+//
+// Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
+//
+// 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 const struct snd_soc_component_driver sh4_ssi_component = {
+	.name		= "sh4-ssi",
+};
+
+static int sh4_soc_dai_probe(struct platform_device *pdev)
+{
+	return devm_snd_soc_register_component(&pdev->dev, &sh4_ssi_component,
+					       sh4_ssi_dai,
+					       ARRAY_SIZE(sh4_ssi_dai));
+}
+
+static struct platform_driver sh4_ssi_driver = {
+	.driver = {
+			.name = "sh4-ssi-dai",
+	},
+
+	.probe = sh4_soc_dai_probe,
+};
+
+module_platform_driver(sh4_ssi_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("SuperH onchip SSI (I2S) audio driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");