diff mbox

[v2,RESENT] dma: add R-Car Audio DMAC peri peri driver

Message ID 874n36supd.wl%kuninori.morimoto.gx@gmail.com (mailing list archive)
State Awaiting Upstream
Headers show

Commit Message

Kuninori Morimoto March 10, 2014, 1:34 a.m. UTC
From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>

Add support Audio DMAC peri peri driver
for Renesas R-Car Gen2 SoC, using 'shdma-base'
DMA driver framework.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
resent

 - add missing "dmaengine@vger.kernel.org"

v1 -> v2

 - run scripts/checkpatch.pl
 - ecchange length settings on audmapp_desc_setup()
 - exchange slave_id check on audmapp_find_slave()

 drivers/dma/sh/Kconfig                         |    6 +
 drivers/dma/sh/Makefile                        |    1 +
 drivers/dma/sh/rcar-audmapp.c                  |  325 ++++++++++++++++++++++++
 include/linux/platform_data/dma-rcar-audmapp.h |   34 +++
 4 files changed, 366 insertions(+)
 create mode 100644 drivers/dma/sh/rcar-audmapp.c
 create mode 100644 include/linux/platform_data/dma-rcar-audmapp.h

Comments

Joe Perches March 10, 2014, 1:47 a.m. UTC | #1
On Sun, 2014-03-09 at 18:34 -0700, Kuninori Morimoto wrote:
> Add support Audio DMAC peri peri driver
> for Renesas R-Car Gen2 SoC, using 'shdma-base'
> DMA driver framework.

Trivial notes:

> diff --git a/drivers/dma/sh/rcar-audmapp.c b/drivers/dma/sh/rcar-audmapp.c
[]
> +static int audmapp_chan_probe(struct platform_device *pdev,
> +			      struct audmapp_device *audev, int id)
> +{
[]
> +	auchan = devm_kzalloc(dev, sizeof(struct audmapp_chan), GFP_KERNEL);
> +	if (!auchan) {
> +		dev_err(dev, "No free memory for allocating dma channels!\n");

Unnecessary OOM as the alloc has a generic OOM
and a dump_stack()

[]
> +static int audmapp_probe(struct platform_device *pdev)
> +{
[]
> +	audev = devm_kzalloc(&pdev->dev, sizeof(struct audmapp_device),
> +			     GFP_KERNEL);
> +	if (!audev) {
> +		dev_err(&pdev->dev, "Not enough memory\n");

here too


--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Kuninori Morimoto March 10, 2014, 2:19 a.m. UTC | #2
Hi Joe

> > diff --git a/drivers/dma/sh/rcar-audmapp.c b/drivers/dma/sh/rcar-audmapp.c
> []
> > +static int audmapp_chan_probe(struct platform_device *pdev,
> > +			      struct audmapp_device *audev, int id)
> > +{
> []
> > +	auchan = devm_kzalloc(dev, sizeof(struct audmapp_chan), GFP_KERNEL);
> > +	if (!auchan) {
> > +		dev_err(dev, "No free memory for allocating dma channels!\n");
> 
> Unnecessary OOM as the alloc has a generic OOM
> and a dump_stack()
> 
> []
> > +static int audmapp_probe(struct platform_device *pdev)
> > +{
> []
> > +	audev = devm_kzalloc(&pdev->dev, sizeof(struct audmapp_device),
> > +			     GFP_KERNEL);
> > +	if (!audev) {
> > +		dev_err(&pdev->dev, "Not enough memory\n");
> 
> here too

Thank you.
will fix in v3
--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andy Shevchenko March 10, 2014, 9:13 a.m. UTC | #3
On Sun, 2014-03-09 at 18:34 -0700, Kuninori Morimoto wrote:
> From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>

> 

> Add support Audio DMAC peri peri driver

> for Renesas R-Car Gen2 SoC, using 'shdma-base'

> DMA driver framework.


Few comments below.

> 

> Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>

> ---

> resent

> 

>  - add missing "dmaengine@vger.kernel.org"

> 

> v1 -> v2

> 

>  - run scripts/checkpatch.pl

>  - ecchange length settings on audmapp_desc_setup()

>  - exchange slave_id check on audmapp_find_slave()

> 

>  drivers/dma/sh/Kconfig                         |    6 +

>  drivers/dma/sh/Makefile                        |    1 +

>  drivers/dma/sh/rcar-audmapp.c                  |  325 ++++++++++++++++++++++++

>  include/linux/platform_data/dma-rcar-audmapp.h |   34 +++

>  4 files changed, 366 insertions(+)

>  create mode 100644 drivers/dma/sh/rcar-audmapp.c

>  create mode 100644 include/linux/platform_data/dma-rcar-audmapp.h

> 

> diff --git a/drivers/dma/sh/Kconfig b/drivers/dma/sh/Kconfig

> index dadd9e01..b4c8138 100644

> --- a/drivers/dma/sh/Kconfig

> +++ b/drivers/dma/sh/Kconfig

> @@ -29,6 +29,12 @@ config RCAR_HPB_DMAE

>  	help

>  	  Enable support for the Renesas R-Car series DMA controllers.

>  

> +config RCAR_AUDMAC_PP

> +	tristate "Renesas R-Car Audio DMAC Peripheral Peripheral support"

> +	depends on SH_DMAE_BASE

> +	help

> +	  Enable support for the Renesas R-Car Audio DMAC Peripheral Peripheral controllers.

> +

>  config SHDMA_R8A73A4

>  	def_bool y

>  	depends on ARCH_R8A73A4 && SH_DMAE != n

> diff --git a/drivers/dma/sh/Makefile b/drivers/dma/sh/Makefile

> index e856af2..1ce88b2 100644

> --- a/drivers/dma/sh/Makefile

> +++ b/drivers/dma/sh/Makefile

> @@ -7,3 +7,4 @@ endif

>  shdma-objs := $(shdma-y)

>  obj-$(CONFIG_SUDMAC) += sudmac.o

>  obj-$(CONFIG_RCAR_HPB_DMAE) += rcar-hpbdma.o

> +obj-$(CONFIG_RCAR_AUDMAC_PP) += rcar-audmapp.o

> diff --git a/drivers/dma/sh/rcar-audmapp.c b/drivers/dma/sh/rcar-audmapp.c

> new file mode 100644

> index 0000000..cd3c237

> --- /dev/null

> +++ b/drivers/dma/sh/rcar-audmapp.c

> @@ -0,0 +1,325 @@

> +/*

> + * drivers/dma/sh/rcar-audmapp.c

> + *

> + * Copyright (C) 2013 Renesas Electronics Corporation

> + * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>


2014?

> + *

> + * based on the drivers/dma/sh/shdma.c

> + *

> + * Copyright (C) 2011-2012 Guennadi Liakhovetski <g.liakhovetski@gmx.de>

> + * Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>

> + * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved.

> + * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.

> + *

> + * This is free software; you can redistribute it and/or modify

> + * it under the terms of the GNU General Public License as published by

> + * the Free Software Foundation; either version 2 of the License, or

> + * (at your option) any later version.

> + *

> + */

> +#include <linux/delay.h>

> +#include <linux/init.h>

> +#include <linux/module.h>

> +#include <linux/slab.h>

> +#include <linux/dmaengine.h>

> +#include <linux/platform_data/dma-rcar-audmapp.h>

> +#include <linux/platform_device.h>

> +#include <linux/shdma-base.h>

> +

> +/*

> + * DMA register

> + */

> +#define PDMASAR		0x00

> +#define PDMADAR		0x04

> +#define PDMACHCR	0x0c

> +

> +/* PDMACHCR */

> +#define PDMACHCR_DE		(1 << 0)

> +

> +#define AUDMAPP_MAX_CHANNELS	29

> +

> +/* Default MEMCPY transfer size = 2^2 = 4 bytes */

> +#define LOG2_DEFAULT_XFER_SIZE	2

> +#define AUDMAPP_SLAVE_NUMBER	256

> +#define AUDMAPP_LEN_MAX		(16 * 1024 * 1024)

> +

> +struct audmapp_chan {

> +	struct shdma_chan shdma_chan;

> +	struct audmapp_slave_config *config;

> +	void __iomem *base;

> +};

> +

> +struct audmapp_device {

> +	struct shdma_dev shdma_dev;

> +	struct audmapp_pdata *pdata;

> +	struct device *dev;

> +	void __iomem *chan_reg;

> +};

> +

> +#define to_chan(chan) container_of(chan, struct audmapp_chan, shdma_chan)

> +#define to_dev(chan) container_of(chan->shdma_chan.dma_chan.device,	\

> +				  struct audmapp_device, shdma_dev.dma_dev)

> +

> +static void audmapp_write(struct audmapp_chan *auchan, u32 data, u32 reg)

> +{

> +	struct audmapp_device *audev = to_dev(auchan);

> +	struct device *dev = audev->dev;

> +

> +	dev_dbg(dev, "w %p : %08x\n", auchan->base + reg, data);

> +

> +	iowrite32(data, auchan->base + reg);

> +}

> +

> +static u32 audmapp_read(struct audmapp_chan *auchan, u32 reg)

> +{

> +	return ioread32(auchan->base + reg);

> +}

> +

> +static void audmapp_halt(struct shdma_chan *schan)

> +{

> +	struct audmapp_chan *auchan = to_chan(schan);

> +	int i;

> +

> +	audmapp_write(auchan, 0, PDMACHCR);

> +

> +	for (i = 0; i < 1024; i++) {

> +		if (0 == audmapp_read(auchan, PDMACHCR))

> +			return;

> +		udelay(1);

> +	}

> +}

> +

> +static void audmapp_start_xfer(struct shdma_chan *schan,

> +			       struct shdma_desc *sdecs)

> +{

> +	struct audmapp_chan *auchan = to_chan(schan);

> +	struct audmapp_device *audev = to_dev(auchan);

> +	struct audmapp_slave_config *cfg = auchan->config;

> +	struct device *dev = audev->dev;

> +	u32 chcr = cfg->chcr | PDMACHCR_DE;

> +

> +	dev_dbg(dev, "src/dst/chcr = %x/%x/%x\n",


%pad for src/dst.

> +		cfg->src, cfg->dst, cfg->chcr);

> +

> +	audmapp_write(auchan, cfg->src,	PDMASAR);

> +	audmapp_write(auchan, cfg->dst,	PDMADAR);

> +	audmapp_write(auchan, chcr,	PDMACHCR);

> +}

> +

> +static struct audmapp_slave_config *

> +audmapp_find_slave(struct audmapp_chan *auchan, int slave_id)

> +{

> +	struct audmapp_device *audev = to_dev(auchan);

> +	struct audmapp_pdata *pdata = audev->pdata;

> +	struct audmapp_slave_config *cfg;

> +	int i;

> +

> +	if (slave_id < 0 || slave_id >= AUDMAPP_SLAVE_NUMBER)

> +		return NULL;

> +

> +	for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++)

> +		if (cfg->slave_id == slave_id)

> +			return cfg;

> +

> +	return NULL;

> +}

> +

> +static int audmapp_set_slave(struct shdma_chan *schan, int slave_id,

> +			     dma_addr_t slave_addr, bool try)

> +{

> +	struct audmapp_chan *auchan = to_chan(schan);

> +	struct audmapp_slave_config *cfg =

> +		audmapp_find_slave(auchan, slave_id);

> +

> +	if (!cfg)

> +		return -ENODEV;

> +	if (try)

> +		return 0;

> +

> +	auchan->config	= cfg;

> +

> +	return 0;

> +}

> +

> +static int audmapp_desc_setup(struct shdma_chan *schan,

> +			      struct shdma_desc *sdecs,

> +			      dma_addr_t src, dma_addr_t dst, size_t *len)

> +{

> +	struct audmapp_chan *auchan = to_chan(schan);

> +	struct audmapp_slave_config *cfg = auchan->config;

> +

> +	if (!cfg)

> +		return -ENODEV;

> +

> +	if (*len > schan->max_xfer_len)

> +		*len = schan->max_xfer_len;

> +

> +	return 0;

> +}

> +

> +static void audmapp_setup_xfer(struct shdma_chan *schan,

> +			       int slave_id)

> +{

> +}

> +

> +static dma_addr_t audmapp_slave_addr(struct shdma_chan *schan)

> +{

> +	return 0; /* always fixed address */

> +}

> +

> +static bool audmapp_channel_busy(struct shdma_chan *schan)

> +{

> +	struct audmapp_chan *auchan = to_chan(schan);

> +	u32 chcr = audmapp_read(auchan, PDMACHCR);

> +

> +	return chcr & ~PDMACHCR_DE;

> +}

> +

> +static bool audmapp_desc_completed(struct shdma_chan *schan,

> +				   struct shdma_desc *sdesc)

> +{

> +	return true;

> +}

> +

> +static struct shdma_desc *audmapp_embedded_desc(void *buf, int i)

> +{

> +	return &((struct shdma_desc *)buf)[i];

> +}

> +

> +static const struct shdma_ops audmapp_shdma_ops = {

> +	.halt_channel	= audmapp_halt,

> +	.desc_setup	= audmapp_desc_setup,

> +	.set_slave	= audmapp_set_slave,

> +	.start_xfer	= audmapp_start_xfer,

> +	.embedded_desc	= audmapp_embedded_desc,

> +	.setup_xfer	= audmapp_setup_xfer,

> +	.slave_addr	= audmapp_slave_addr,

> +	.channel_busy	= audmapp_channel_busy,

> +	.desc_completed	= audmapp_desc_completed,

> +};

> +

> +static int audmapp_chan_probe(struct platform_device *pdev,

> +			      struct audmapp_device *audev, int id)

> +{

> +	struct shdma_dev *sdev = &audev->shdma_dev;

> +	struct audmapp_chan *auchan;

> +	struct shdma_chan *schan;

> +	struct device *dev = audev->dev;

> +

> +	auchan = devm_kzalloc(dev, sizeof(struct audmapp_chan), GFP_KERNEL);


sizeof(*auchan)

> +	if (!auchan) {

> +		dev_err(dev, "No free memory for allocating dma channels!\n");

> +		return -ENOMEM;

> +	}

> +

> +	schan = &auchan->shdma_chan;

> +	schan->max_xfer_len = AUDMAPP_LEN_MAX;

> +

> +	shdma_chan_probe(sdev, schan, id);

> +

> +	auchan->base = audev->chan_reg + 0x20 + (0x10 * id);

> +	dev_dbg(dev, "%02d : %p / %p", id, auchan->base, audev->chan_reg);

> +

> +	return 0;

> +}

> +

> +static void audmapp_chan_remove(struct audmapp_device *audev)

> +{

> +	struct dma_device *dma_dev = &audev->shdma_dev.dma_dev;

> +	struct shdma_chan *schan;

> +	int i;

> +

> +	shdma_for_each_chan(schan, &audev->shdma_dev, i) {

> +		BUG_ON(!schan);

> +		shdma_chan_remove(schan);

> +	}

> +	dma_dev->chancnt = 0;

> +}

> +

> +static int audmapp_probe(struct platform_device *pdev)

> +{

> +	struct audmapp_pdata *pdata = pdev->dev.platform_data;

> +	struct audmapp_device *audev;

> +	struct shdma_dev *sdev;

> +	struct dma_device *dma_dev;

> +	struct resource *res;

> +	int err, i;

> +

> +	if (!pdata)

> +		return -ENODEV;

> +

> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

> +

> +	audev = devm_kzalloc(&pdev->dev, sizeof(struct audmapp_device),

> +			     GFP_KERNEL);


sizeof(*audev)

> +	if (!audev) {

> +		dev_err(&pdev->dev, "Not enough memory\n");

> +		return -ENOMEM;

> +	}

> +

> +	audev->dev	= &pdev->dev;

> +	audev->pdata	= pdata;

> +	audev->chan_reg	= devm_ioremap_resource(&pdev->dev, res);

> +	if (IS_ERR(audev->chan_reg))

> +		return PTR_ERR(audev->chan_reg);

> +

> +	sdev		= &audev->shdma_dev;

> +	sdev->ops	= &audmapp_shdma_ops;

> +	sdev->desc_size	= sizeof(struct shdma_desc);

> +

> +	dma_dev			= &sdev->dma_dev;

> +	dma_dev->copy_align	= LOG2_DEFAULT_XFER_SIZE;

> +	dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);

> +

> +	err = shdma_init(&pdev->dev, sdev, AUDMAPP_MAX_CHANNELS);

> +	if (err < 0)

> +		return err;

> +

> +	platform_set_drvdata(pdev, audev);

> +

> +	/* Create DMA Channel */

> +	for (i = 0; i < AUDMAPP_MAX_CHANNELS; i++) {

> +		err = audmapp_chan_probe(pdev, audev, i);

> +		if (err)

> +			goto chan_probe_err;

> +	}

> +

> +	err = dma_async_device_register(dma_dev);

> +	if (err < 0)

> +		goto chan_probe_err;

> +

> +	return err;

> +

> +chan_probe_err:

> +	audmapp_chan_remove(audev);

> +	shdma_cleanup(sdev);

> +

> +	return err;

> +}

> +

> +static int audmapp_remove(struct platform_device *pdev)

> +{

> +	struct audmapp_device *audev = platform_get_drvdata(pdev);

> +	struct dma_device *dma_dev = &audev->shdma_dev.dma_dev;

> +

> +	dma_async_device_unregister(dma_dev);

> +

> +	audmapp_chan_remove(audev);

> +	shdma_cleanup(&audev->shdma_dev);

> +

> +	return 0;

> +}

> +

> +static struct platform_driver audmapp_driver = {

> +	.probe		= audmapp_probe,

> +	.remove		= audmapp_remove,

> +	.driver		= {

> +		.owner	= THIS_MODULE,

> +		.name	= "rcar-audmapp-engine",

> +	},

> +};

> +module_platform_driver(audmapp_driver);

> +

> +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");

> +MODULE_DESCRIPTION("Renesas R-Car Audio DMAC peri-peri driver");

> +MODULE_LICENSE("GPL");

> diff --git a/include/linux/platform_data/dma-rcar-audmapp.h b/include/linux/platform_data/dma-rcar-audmapp.h

> new file mode 100644

> index 0000000..346df66

> --- /dev/null

> +++ b/include/linux/platform_data/dma-rcar-audmapp.h

> @@ -0,0 +1,34 @@

> +/*

> + * include/linux/sh_audma-pp.h

> + *     This file is header file for Audio-DMAC-pp peripheral.

> + *

> + * Copyright (C) 2013 Renesas Electronics Corporation


2014?

> + *

> + * This file is based on the include/linux/sh_dma.h

> + *

> + * Header for the new SH dmaengine driver

> + *

> + * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>

> + *

> + * This program is free software; you can redistribute it and/or modify

> + * it under the terms of the GNU General Public License version 2 as

> + * published by the Free Software Foundation.

> + */

> +#ifndef SH_AUDMAPP_H

> +#define SH_AUDMAPP_H

> +

> +#include <linux/dmaengine.h>

> +

> +struct audmapp_slave_config {

> +	int		slave_id;

> +	dma_addr_t	src;

> +	dma_addr_t	dst;

> +	u32		chcr;

> +};

> +

> +struct audmapp_pdata {

> +	struct audmapp_slave_config *slave;

> +	int slave_num;

> +};

> +

> +#endif /* SH_AUDMAPP_H */



-- 
Andy Shevchenko <andriy.shevchenko@intel.com>
Intel Finland Oy
---------------------------------------------------------------------
Intel Finland Oy
Registered Address: PL 281, 00181 Helsinki 
Business Identity Code: 0357606 - 4 
Domiciled in Helsinki 

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
diff mbox

Patch

diff --git a/drivers/dma/sh/Kconfig b/drivers/dma/sh/Kconfig
index dadd9e01..b4c8138 100644
--- a/drivers/dma/sh/Kconfig
+++ b/drivers/dma/sh/Kconfig
@@ -29,6 +29,12 @@  config RCAR_HPB_DMAE
 	help
 	  Enable support for the Renesas R-Car series DMA controllers.
 
+config RCAR_AUDMAC_PP
+	tristate "Renesas R-Car Audio DMAC Peripheral Peripheral support"
+	depends on SH_DMAE_BASE
+	help
+	  Enable support for the Renesas R-Car Audio DMAC Peripheral Peripheral controllers.
+
 config SHDMA_R8A73A4
 	def_bool y
 	depends on ARCH_R8A73A4 && SH_DMAE != n
diff --git a/drivers/dma/sh/Makefile b/drivers/dma/sh/Makefile
index e856af2..1ce88b2 100644
--- a/drivers/dma/sh/Makefile
+++ b/drivers/dma/sh/Makefile
@@ -7,3 +7,4 @@  endif
 shdma-objs := $(shdma-y)
 obj-$(CONFIG_SUDMAC) += sudmac.o
 obj-$(CONFIG_RCAR_HPB_DMAE) += rcar-hpbdma.o
+obj-$(CONFIG_RCAR_AUDMAC_PP) += rcar-audmapp.o
diff --git a/drivers/dma/sh/rcar-audmapp.c b/drivers/dma/sh/rcar-audmapp.c
new file mode 100644
index 0000000..cd3c237
--- /dev/null
+++ b/drivers/dma/sh/rcar-audmapp.c
@@ -0,0 +1,325 @@ 
+/*
+ * drivers/dma/sh/rcar-audmapp.c
+ *
+ * Copyright (C) 2013 Renesas Electronics Corporation
+ * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * based on the drivers/dma/sh/shdma.c
+ *
+ * Copyright (C) 2011-2012 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
+ * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved.
+ * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/dmaengine.h>
+#include <linux/platform_data/dma-rcar-audmapp.h>
+#include <linux/platform_device.h>
+#include <linux/shdma-base.h>
+
+/*
+ * DMA register
+ */
+#define PDMASAR		0x00
+#define PDMADAR		0x04
+#define PDMACHCR	0x0c
+
+/* PDMACHCR */
+#define PDMACHCR_DE		(1 << 0)
+
+#define AUDMAPP_MAX_CHANNELS	29
+
+/* Default MEMCPY transfer size = 2^2 = 4 bytes */
+#define LOG2_DEFAULT_XFER_SIZE	2
+#define AUDMAPP_SLAVE_NUMBER	256
+#define AUDMAPP_LEN_MAX		(16 * 1024 * 1024)
+
+struct audmapp_chan {
+	struct shdma_chan shdma_chan;
+	struct audmapp_slave_config *config;
+	void __iomem *base;
+};
+
+struct audmapp_device {
+	struct shdma_dev shdma_dev;
+	struct audmapp_pdata *pdata;
+	struct device *dev;
+	void __iomem *chan_reg;
+};
+
+#define to_chan(chan) container_of(chan, struct audmapp_chan, shdma_chan)
+#define to_dev(chan) container_of(chan->shdma_chan.dma_chan.device,	\
+				  struct audmapp_device, shdma_dev.dma_dev)
+
+static void audmapp_write(struct audmapp_chan *auchan, u32 data, u32 reg)
+{
+	struct audmapp_device *audev = to_dev(auchan);
+	struct device *dev = audev->dev;
+
+	dev_dbg(dev, "w %p : %08x\n", auchan->base + reg, data);
+
+	iowrite32(data, auchan->base + reg);
+}
+
+static u32 audmapp_read(struct audmapp_chan *auchan, u32 reg)
+{
+	return ioread32(auchan->base + reg);
+}
+
+static void audmapp_halt(struct shdma_chan *schan)
+{
+	struct audmapp_chan *auchan = to_chan(schan);
+	int i;
+
+	audmapp_write(auchan, 0, PDMACHCR);
+
+	for (i = 0; i < 1024; i++) {
+		if (0 == audmapp_read(auchan, PDMACHCR))
+			return;
+		udelay(1);
+	}
+}
+
+static void audmapp_start_xfer(struct shdma_chan *schan,
+			       struct shdma_desc *sdecs)
+{
+	struct audmapp_chan *auchan = to_chan(schan);
+	struct audmapp_device *audev = to_dev(auchan);
+	struct audmapp_slave_config *cfg = auchan->config;
+	struct device *dev = audev->dev;
+	u32 chcr = cfg->chcr | PDMACHCR_DE;
+
+	dev_dbg(dev, "src/dst/chcr = %x/%x/%x\n",
+		cfg->src, cfg->dst, cfg->chcr);
+
+	audmapp_write(auchan, cfg->src,	PDMASAR);
+	audmapp_write(auchan, cfg->dst,	PDMADAR);
+	audmapp_write(auchan, chcr,	PDMACHCR);
+}
+
+static struct audmapp_slave_config *
+audmapp_find_slave(struct audmapp_chan *auchan, int slave_id)
+{
+	struct audmapp_device *audev = to_dev(auchan);
+	struct audmapp_pdata *pdata = audev->pdata;
+	struct audmapp_slave_config *cfg;
+	int i;
+
+	if (slave_id < 0 || slave_id >= AUDMAPP_SLAVE_NUMBER)
+		return NULL;
+
+	for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++)
+		if (cfg->slave_id == slave_id)
+			return cfg;
+
+	return NULL;
+}
+
+static int audmapp_set_slave(struct shdma_chan *schan, int slave_id,
+			     dma_addr_t slave_addr, bool try)
+{
+	struct audmapp_chan *auchan = to_chan(schan);
+	struct audmapp_slave_config *cfg =
+		audmapp_find_slave(auchan, slave_id);
+
+	if (!cfg)
+		return -ENODEV;
+	if (try)
+		return 0;
+
+	auchan->config	= cfg;
+
+	return 0;
+}
+
+static int audmapp_desc_setup(struct shdma_chan *schan,
+			      struct shdma_desc *sdecs,
+			      dma_addr_t src, dma_addr_t dst, size_t *len)
+{
+	struct audmapp_chan *auchan = to_chan(schan);
+	struct audmapp_slave_config *cfg = auchan->config;
+
+	if (!cfg)
+		return -ENODEV;
+
+	if (*len > schan->max_xfer_len)
+		*len = schan->max_xfer_len;
+
+	return 0;
+}
+
+static void audmapp_setup_xfer(struct shdma_chan *schan,
+			       int slave_id)
+{
+}
+
+static dma_addr_t audmapp_slave_addr(struct shdma_chan *schan)
+{
+	return 0; /* always fixed address */
+}
+
+static bool audmapp_channel_busy(struct shdma_chan *schan)
+{
+	struct audmapp_chan *auchan = to_chan(schan);
+	u32 chcr = audmapp_read(auchan, PDMACHCR);
+
+	return chcr & ~PDMACHCR_DE;
+}
+
+static bool audmapp_desc_completed(struct shdma_chan *schan,
+				   struct shdma_desc *sdesc)
+{
+	return true;
+}
+
+static struct shdma_desc *audmapp_embedded_desc(void *buf, int i)
+{
+	return &((struct shdma_desc *)buf)[i];
+}
+
+static const struct shdma_ops audmapp_shdma_ops = {
+	.halt_channel	= audmapp_halt,
+	.desc_setup	= audmapp_desc_setup,
+	.set_slave	= audmapp_set_slave,
+	.start_xfer	= audmapp_start_xfer,
+	.embedded_desc	= audmapp_embedded_desc,
+	.setup_xfer	= audmapp_setup_xfer,
+	.slave_addr	= audmapp_slave_addr,
+	.channel_busy	= audmapp_channel_busy,
+	.desc_completed	= audmapp_desc_completed,
+};
+
+static int audmapp_chan_probe(struct platform_device *pdev,
+			      struct audmapp_device *audev, int id)
+{
+	struct shdma_dev *sdev = &audev->shdma_dev;
+	struct audmapp_chan *auchan;
+	struct shdma_chan *schan;
+	struct device *dev = audev->dev;
+
+	auchan = devm_kzalloc(dev, sizeof(struct audmapp_chan), GFP_KERNEL);
+	if (!auchan) {
+		dev_err(dev, "No free memory for allocating dma channels!\n");
+		return -ENOMEM;
+	}
+
+	schan = &auchan->shdma_chan;
+	schan->max_xfer_len = AUDMAPP_LEN_MAX;
+
+	shdma_chan_probe(sdev, schan, id);
+
+	auchan->base = audev->chan_reg + 0x20 + (0x10 * id);
+	dev_dbg(dev, "%02d : %p / %p", id, auchan->base, audev->chan_reg);
+
+	return 0;
+}
+
+static void audmapp_chan_remove(struct audmapp_device *audev)
+{
+	struct dma_device *dma_dev = &audev->shdma_dev.dma_dev;
+	struct shdma_chan *schan;
+	int i;
+
+	shdma_for_each_chan(schan, &audev->shdma_dev, i) {
+		BUG_ON(!schan);
+		shdma_chan_remove(schan);
+	}
+	dma_dev->chancnt = 0;
+}
+
+static int audmapp_probe(struct platform_device *pdev)
+{
+	struct audmapp_pdata *pdata = pdev->dev.platform_data;
+	struct audmapp_device *audev;
+	struct shdma_dev *sdev;
+	struct dma_device *dma_dev;
+	struct resource *res;
+	int err, i;
+
+	if (!pdata)
+		return -ENODEV;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	audev = devm_kzalloc(&pdev->dev, sizeof(struct audmapp_device),
+			     GFP_KERNEL);
+	if (!audev) {
+		dev_err(&pdev->dev, "Not enough memory\n");
+		return -ENOMEM;
+	}
+
+	audev->dev	= &pdev->dev;
+	audev->pdata	= pdata;
+	audev->chan_reg	= devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(audev->chan_reg))
+		return PTR_ERR(audev->chan_reg);
+
+	sdev		= &audev->shdma_dev;
+	sdev->ops	= &audmapp_shdma_ops;
+	sdev->desc_size	= sizeof(struct shdma_desc);
+
+	dma_dev			= &sdev->dma_dev;
+	dma_dev->copy_align	= LOG2_DEFAULT_XFER_SIZE;
+	dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
+
+	err = shdma_init(&pdev->dev, sdev, AUDMAPP_MAX_CHANNELS);
+	if (err < 0)
+		return err;
+
+	platform_set_drvdata(pdev, audev);
+
+	/* Create DMA Channel */
+	for (i = 0; i < AUDMAPP_MAX_CHANNELS; i++) {
+		err = audmapp_chan_probe(pdev, audev, i);
+		if (err)
+			goto chan_probe_err;
+	}
+
+	err = dma_async_device_register(dma_dev);
+	if (err < 0)
+		goto chan_probe_err;
+
+	return err;
+
+chan_probe_err:
+	audmapp_chan_remove(audev);
+	shdma_cleanup(sdev);
+
+	return err;
+}
+
+static int audmapp_remove(struct platform_device *pdev)
+{
+	struct audmapp_device *audev = platform_get_drvdata(pdev);
+	struct dma_device *dma_dev = &audev->shdma_dev.dma_dev;
+
+	dma_async_device_unregister(dma_dev);
+
+	audmapp_chan_remove(audev);
+	shdma_cleanup(&audev->shdma_dev);
+
+	return 0;
+}
+
+static struct platform_driver audmapp_driver = {
+	.probe		= audmapp_probe,
+	.remove		= audmapp_remove,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "rcar-audmapp-engine",
+	},
+};
+module_platform_driver(audmapp_driver);
+
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_DESCRIPTION("Renesas R-Car Audio DMAC peri-peri driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/platform_data/dma-rcar-audmapp.h b/include/linux/platform_data/dma-rcar-audmapp.h
new file mode 100644
index 0000000..346df66
--- /dev/null
+++ b/include/linux/platform_data/dma-rcar-audmapp.h
@@ -0,0 +1,34 @@ 
+/*
+ * include/linux/sh_audma-pp.h
+ *     This file is header file for Audio-DMAC-pp peripheral.
+ *
+ * Copyright (C) 2013 Renesas Electronics Corporation
+ *
+ * This file is based on the include/linux/sh_dma.h
+ *
+ * Header for the new SH dmaengine driver
+ *
+ * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef SH_AUDMAPP_H
+#define SH_AUDMAPP_H
+
+#include <linux/dmaengine.h>
+
+struct audmapp_slave_config {
+	int		slave_id;
+	dma_addr_t	src;
+	dma_addr_t	dst;
+	u32		chcr;
+};
+
+struct audmapp_pdata {
+	struct audmapp_slave_config *slave;
+	int slave_num;
+};
+
+#endif /* SH_AUDMAPP_H */