[alsa-devel] [PATCH 0/6] Convert JZ4740 to dmaengine
Hi,
This series replaces the custom JZ4740 DMA API with a dmaengine driver. This is done in 3 steps: 1) Add a dmaengine driver which wraps the custom JZ4740 DMA API 2) Update all users of the JZ4740 DMA API to use dmaengine instead 3) Remove the custom API and move all direct hardware access to the dmaengine driver.
The first two patches in the series also make sure that the clock of the DMA core is enabled.
Since the patches in this series depend on each other I'd prefer if they could all go through the DMA tree.
There are a few minor changes to the patch 3 in v2 of this series, all other patches are identical to v1.
- Lars
Lars-Peter Clausen (4): dma: Add a jz4740 dmaengine driver MIPS: jz4740: Register jz4740 DMA device ASoC: jz4740: Use the generic dmaengine PCM driver MIPS: jz4740: Remove custom DMA API
Maarten ter Huurne (2): MIPS: jz4740: Correct clock gate bit for DMA controller MIPS: jz4740: Acquire and enable DMA controller clock
arch/mips/include/asm/mach-jz4740/dma.h | 56 --- arch/mips/include/asm/mach-jz4740/platform.h | 1 + arch/mips/jz4740/Makefile | 2 +- arch/mips/jz4740/board-qi_lb60.c | 1 + arch/mips/jz4740/clock.c | 2 +- arch/mips/jz4740/dma.c | 287 ------------- arch/mips/jz4740/platform.c | 21 + drivers/dma/Kconfig | 6 + drivers/dma/Makefile | 1 + drivers/dma/dma-jz4740.c | 617 +++++++++++++++++++++++++++ sound/soc/jz4740/Kconfig | 1 + sound/soc/jz4740/jz4740-i2s.c | 48 +-- sound/soc/jz4740/jz4740-pcm.c | 310 +------------- sound/soc/jz4740/jz4740-pcm.h | 20 - 14 files changed, 676 insertions(+), 697 deletions(-) delete mode 100644 arch/mips/jz4740/dma.c create mode 100644 drivers/dma/dma-jz4740.c delete mode 100644 sound/soc/jz4740/jz4740-pcm.h
From: Maarten ter Huurne maarten@treewalker.org
Signed-off-by: Maarten ter Huurne maarten@treewalker.org Signed-off-by: Lars-Peter Clausen lars@metafoo.de Acked-by: Ralf Baechle ralf@linux-mips.org --- No changes since v1 --- arch/mips/jz4740/clock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/mips/jz4740/clock.c b/arch/mips/jz4740/clock.c index 484d38a..1b5f554 100644 --- a/arch/mips/jz4740/clock.c +++ b/arch/mips/jz4740/clock.c @@ -687,7 +687,7 @@ static struct clk jz4740_clock_simple_clks[] = { [3] = { .name = "dma", .parent = &jz_clk_high_speed_peripheral.clk, - .gate_bit = JZ_CLOCK_GATE_UART0, + .gate_bit = JZ_CLOCK_GATE_DMAC, .ops = &jz_clk_simple_ops, }, [4] = {
From: Maarten ter Huurne maarten@treewalker.org
Previously, it was assumed that the DMA controller clock is not gated when the kernel starts running. While that is the power-on state, it is safer to not rely on that.
Signed-off-by: Maarten ter Huurne maarten@treewalker.org Signed-off-by: Lars-Peter Clausen lars@metafoo.de Acked-by: Ralf Baechle ralf@linux-mips.org --- No changes since v1 --- arch/mips/jz4740/dma.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)
diff --git a/arch/mips/jz4740/dma.c b/arch/mips/jz4740/dma.c index 317ec6f..0e34b97 100644 --- a/arch/mips/jz4740/dma.c +++ b/arch/mips/jz4740/dma.c @@ -16,6 +16,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/spinlock.h> +#include <linux/clk.h> #include <linux/interrupt.h>
#include <linux/dma-mapping.h> @@ -268,6 +269,7 @@ static irqreturn_t jz4740_dma_irq(int irq, void *dev_id)
static int jz4740_dma_init(void) { + struct clk *clk; unsigned int ret;
jz4740_dma_base = ioremap(JZ4740_DMAC_BASE_ADDR, 0x400); @@ -277,11 +279,29 @@ static int jz4740_dma_init(void)
spin_lock_init(&jz4740_dma_lock);
- ret = request_irq(JZ4740_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL); + clk = clk_get(NULL, "dma"); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + printk(KERN_ERR "JZ4740 DMA: Failed to request clock: %d\n", + ret); + goto err_iounmap; + }
- if (ret) + ret = request_irq(JZ4740_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL); + if (ret) { printk(KERN_ERR "JZ4740 DMA: Failed to request irq: %d\n", ret); + goto err_clkput; + } + + clk_prepare_enable(clk); + + return 0; + +err_clkput: + clk_put(clk);
+err_iounmap: + iounmap(jz4740_dma_base); return ret; } arch_initcall(jz4740_dma_init);
This patch adds dmaengine support for the JZ4740 DMA controller. For now the driver will be a wrapper around the custom JZ4740 DMA API. Once all users of the custom JZ4740 DMA API have been converted to the dmaengine API the custom API will be removed and direct hardware access will be added to the dmaengine driver.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- Changes since v1: * Only save the fifo addr instead of the full slave config * #define JZ4740_DMA_NR_CHANS 6 * Return ENOSYS for unsuppored commands in the device_control() callback --- drivers/dma/Kconfig | 6 + drivers/dma/Makefile | 1 + drivers/dma/dma-jz4740.c | 433 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 440 insertions(+) create mode 100644 drivers/dma/dma-jz4740.c
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index e992489..b3e8952 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -312,6 +312,12 @@ config MMP_PDMA help Support the MMP PDMA engine for PXA and MMP platfrom.
+config DMA_JZ4740 + tristate "JZ4740 DMA support" + depends on MACH_JZ4740 + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + config DMA_ENGINE bool
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index a2b0df5..6127a61 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -38,3 +38,4 @@ obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o obj-$(CONFIG_DMA_OMAP) += omap-dma.o obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o +obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c new file mode 100644 index 0000000..3d42434 --- /dev/null +++ b/drivers/dma/dma-jz4740.c @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2013, Lars-Peter Clausen lars@metafoo.de + * JZ4740 DMAC support + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include <asm/mach-jz4740/dma.h> + +#include "virt-dma.h" + +#define JZ_DMA_NR_CHANS 6 + +struct jz4740_dma_sg { + dma_addr_t addr; + unsigned int len; +}; + +struct jz4740_dma_desc { + struct virt_dma_desc vdesc; + + enum dma_transfer_direction direction; + bool cyclic; + + unsigned int num_sgs; + struct jz4740_dma_sg sg[]; +}; + +struct jz4740_dmaengine_chan { + struct virt_dma_chan vchan; + struct jz4740_dma_chan *jz_chan; + + dma_addr_t fifo_addr; + + struct jz4740_dma_desc *desc; + unsigned int next_sg; +}; + +struct jz4740_dma_dev { + struct dma_device ddev; + + struct jz4740_dmaengine_chan chan[JZ_DMA_NR_CHANS]; +}; + +static struct jz4740_dmaengine_chan *to_jz4740_dma_chan(struct dma_chan *c) +{ + return container_of(c, struct jz4740_dmaengine_chan, vchan.chan); +} + +static struct jz4740_dma_desc *to_jz4740_dma_desc(struct virt_dma_desc *vdesc) +{ + return container_of(vdesc, struct jz4740_dma_desc, vdesc); +} + +static struct jz4740_dma_desc *jz4740_dma_alloc_desc(unsigned int num_sgs) +{ + return kzalloc(sizeof(struct jz4740_dma_desc) + + sizeof(struct jz4740_dma_sg) * num_sgs, GFP_ATOMIC); +} + +static enum jz4740_dma_width jz4740_dma_width(enum dma_slave_buswidth width) +{ + switch (width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + return JZ4740_DMA_WIDTH_8BIT; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + return JZ4740_DMA_WIDTH_16BIT; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + return JZ4740_DMA_WIDTH_32BIT; + default: + return JZ4740_DMA_WIDTH_32BIT; + } +} + +static enum jz4740_dma_transfer_size jz4740_dma_maxburst(u32 maxburst) +{ + if (maxburst <= 1) + return JZ4740_DMA_TRANSFER_SIZE_1BYTE; + else if (maxburst <= 3) + return JZ4740_DMA_TRANSFER_SIZE_2BYTE; + else if (maxburst <= 15) + return JZ4740_DMA_TRANSFER_SIZE_4BYTE; + else if (maxburst <= 31) + return JZ4740_DMA_TRANSFER_SIZE_16BYTE; + + return JZ4740_DMA_TRANSFER_SIZE_32BYTE; +} + +static int jz4740_dma_slave_config(struct dma_chan *c, + const struct dma_slave_config *config) +{ + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + struct jz4740_dma_config jzcfg; + + switch (config->direction) { + case DMA_MEM_TO_DEV: + jzcfg.flags = JZ4740_DMA_SRC_AUTOINC; + jzcfg.transfer_size = jz4740_dma_maxburst(config->dst_maxburst); + chan->fifo_addr = config->dst_addr; + break; + case DMA_DEV_TO_MEM: + jzcfg.flags = JZ4740_DMA_DST_AUTOINC; + jzcfg.transfer_size = jz4740_dma_maxburst(config->src_maxburst); + chan->fifo_addr = config->src_addr; + break; + default: + return -EINVAL; + } + + + jzcfg.src_width = jz4740_dma_width(config->src_addr_width); + jzcfg.dst_width = jz4740_dma_width(config->dst_addr_width); + jzcfg.mode = JZ4740_DMA_MODE_SINGLE; + jzcfg.request_type = config->slave_id; + + jz4740_dma_configure(chan->jz_chan, &jzcfg); + + return 0; +} + +static int jz4740_dma_terminate_all(struct dma_chan *c) +{ + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&chan->vchan.lock, flags); + jz4740_dma_disable(chan->jz_chan); + chan->desc = NULL; + vchan_get_all_descriptors(&chan->vchan, &head); + spin_unlock_irqrestore(&chan->vchan.lock, flags); + + vchan_dma_desc_free_list(&chan->vchan, &head); + + return 0; +} + +static int jz4740_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct dma_slave_config *config = (struct dma_slave_config *)arg; + + switch (cmd) { + case DMA_SLAVE_CONFIG: + return jz4740_dma_slave_config(chan, config); + case DMA_TERMINATE_ALL: + return jz4740_dma_terminate_all(chan); + default: + return -ENOSYS; + } +} + +static int jz4740_dma_start_transfer(struct jz4740_dmaengine_chan *chan) +{ + dma_addr_t src_addr, dst_addr; + struct virt_dma_desc *vdesc; + struct jz4740_dma_sg *sg; + + jz4740_dma_disable(chan->jz_chan); + + if (!chan->desc) { + vdesc = vchan_next_desc(&chan->vchan); + if (!vdesc) + return 0; + chan->desc = to_jz4740_dma_desc(vdesc); + chan->next_sg = 0; + } + + if (chan->next_sg == chan->desc->num_sgs) + chan->next_sg = 0; + + sg = &chan->desc->sg[chan->next_sg]; + + if (chan->desc->direction == DMA_MEM_TO_DEV) { + src_addr = sg->addr; + dst_addr = chan->fifo_addr; + } else { + src_addr = chan->fifo_addr; + dst_addr = sg->addr; + } + jz4740_dma_set_src_addr(chan->jz_chan, src_addr); + jz4740_dma_set_dst_addr(chan->jz_chan, dst_addr); + jz4740_dma_set_transfer_count(chan->jz_chan, sg->len); + + chan->next_sg++; + + jz4740_dma_enable(chan->jz_chan); + + return 0; +} + +static void jz4740_dma_complete_cb(struct jz4740_dma_chan *jz_chan, int error, + void *devid) +{ + struct jz4740_dmaengine_chan *chan = devid; + + spin_lock(&chan->vchan.lock); + if (chan->desc) { + if (chan->desc && chan->desc->cyclic) { + vchan_cyclic_callback(&chan->desc->vdesc); + } else { + if (chan->next_sg == chan->desc->num_sgs) { + chan->desc = NULL; + vchan_cookie_complete(&chan->desc->vdesc); + } + } + } + jz4740_dma_start_transfer(chan); + spin_unlock(&chan->vchan.lock); +} + +static void jz4740_dma_issue_pending(struct dma_chan *c) +{ + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + unsigned long flags; + + spin_lock_irqsave(&chan->vchan.lock, flags); + if (vchan_issue_pending(&chan->vchan) && !chan->desc) + jz4740_dma_start_transfer(chan); + spin_unlock_irqrestore(&chan->vchan.lock, flags); +} + +static struct dma_async_tx_descriptor *jz4740_dma_prep_slave_sg( + struct dma_chan *c, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + struct jz4740_dma_desc *desc; + struct scatterlist *sg; + unsigned int i; + + desc = jz4740_dma_alloc_desc(sg_len); + if (!desc) + return NULL; + + for_each_sg(sgl, sg, sg_len, i) { + desc->sg[i].addr = sg_dma_address(sg); + desc->sg[i].len = sg_dma_len(sg); + } + + desc->num_sgs = sg_len; + desc->direction = direction; + desc->cyclic = false; + + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); +} + +static struct dma_async_tx_descriptor *jz4740_dma_prep_dma_cyclic( + struct dma_chan *c, dma_addr_t buf_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + struct jz4740_dma_desc *desc; + unsigned int num_periods, i; + + if (buf_len % period_len) + return NULL; + + num_periods = buf_len / period_len; + + desc = jz4740_dma_alloc_desc(num_periods); + if (!desc) + return NULL; + + for (i = 0; i < num_periods; i++) { + desc->sg[i].addr = buf_addr; + desc->sg[i].len = period_len; + buf_addr += period_len; + } + + desc->num_sgs = num_periods; + desc->direction = direction; + desc->cyclic = true; + + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); +} + +static size_t jz4740_dma_desc_residue(struct jz4740_dmaengine_chan *chan, + struct jz4740_dma_desc *desc, unsigned int next_sg) +{ + size_t residue = 0; + unsigned int i; + + residue = 0; + + for (i = next_sg; i < desc->num_sgs; i++) + residue += desc->sg[i].len; + + if (next_sg != 0) + residue += jz4740_dma_get_residue(chan->jz_chan); + + return residue; +} + +static enum dma_status jz4740_dma_tx_status(struct dma_chan *c, + dma_cookie_t cookie, struct dma_tx_state *state) +{ + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + struct virt_dma_desc *vdesc; + enum dma_status status; + unsigned long flags; + + status = dma_cookie_status(c, cookie, state); + if (status == DMA_SUCCESS || !state) + return status; + + spin_lock_irqsave(&chan->vchan.lock, flags); + vdesc = vchan_find_desc(&chan->vchan, cookie); + if (cookie == chan->desc->vdesc.tx.cookie) { + state->residue = jz4740_dma_desc_residue(chan, chan->desc, + chan->next_sg); + } else if (vdesc) { + state->residue = jz4740_dma_desc_residue(chan, + to_jz4740_dma_desc(vdesc), 0); + } else { + state->residue = 0; + } + spin_unlock_irqrestore(&chan->vchan.lock, flags); + + return status; +} + +static int jz4740_dma_alloc_chan_resources(struct dma_chan *c) +{ + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + + chan->jz_chan = jz4740_dma_request(chan, NULL); + if (!chan->jz_chan) + return -EBUSY; + + jz4740_dma_set_complete_cb(chan->jz_chan, jz4740_dma_complete_cb); + + return 0; +} + +static void jz4740_dma_free_chan_resources(struct dma_chan *c) +{ + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + + vchan_free_chan_resources(&chan->vchan); + jz4740_dma_free(chan->jz_chan); + chan->jz_chan = NULL; +} + +static void jz4740_dma_desc_free(struct virt_dma_desc *vdesc) +{ + kfree(container_of(vdesc, struct jz4740_dma_desc, vdesc)); +} + +static int jz4740_dma_probe(struct platform_device *pdev) +{ + struct jz4740_dmaengine_chan *chan; + struct jz4740_dma_dev *dmadev; + struct dma_device *dd; + unsigned int i; + int ret; + + dmadev = devm_kzalloc(&pdev->dev, sizeof(*dmadev), GFP_KERNEL); + if (!dmadev) + return -EINVAL; + + dd = &dmadev->ddev; + + dma_cap_set(DMA_SLAVE, dd->cap_mask); + dma_cap_set(DMA_CYCLIC, dd->cap_mask); + dd->device_alloc_chan_resources = jz4740_dma_alloc_chan_resources; + dd->device_free_chan_resources = jz4740_dma_free_chan_resources; + dd->device_tx_status = jz4740_dma_tx_status; + dd->device_issue_pending = jz4740_dma_issue_pending; + dd->device_prep_slave_sg = jz4740_dma_prep_slave_sg; + dd->device_prep_dma_cyclic = jz4740_dma_prep_dma_cyclic; + dd->device_control = jz4740_dma_control; + dd->dev = &pdev->dev; + dd->chancnt = JZ_DMA_NR_CHANS; + INIT_LIST_HEAD(&dd->channels); + + for (i = 0; i < dd->chancnt; i++) { + chan = &dmadev->chan[i]; + chan->vchan.desc_free = jz4740_dma_desc_free; + vchan_init(&chan->vchan, dd); + } + + ret = dma_async_device_register(dd); + if (ret) + return ret; + + platform_set_drvdata(pdev, dmadev); + + return 0; +} + +static int jz4740_dma_remove(struct platform_device *pdev) +{ + struct jz4740_dma_dev *dmadev = platform_get_drvdata(pdev); + + dma_async_device_unregister(&dmadev->ddev); + + return 0; +} + +static struct platform_driver jz4740_dma_driver = { + .probe = jz4740_dma_probe, + .remove = jz4740_dma_remove, + .driver = { + .name = "jz4740-dma", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(jz4740_dma_driver); + +MODULE_AUTHOR("Lars-Peter Clausen lars@metafoo.de"); +MODULE_DESCRIPTION("JZ4740 DMA driver"); +MODULE_LICENSE("GPLv2");
On Thu, May 30, 2013 at 06:25:02PM +0200, Lars-Peter Clausen wrote:
This patch adds dmaengine support for the JZ4740 DMA controller. For now the driver will be a wrapper around the custom JZ4740 DMA API. Once all users of the custom JZ4740 DMA API have been converted to the dmaengine API the custom API will be removed and direct hardware access will be added to the dmaengine driver.
+#include <asm/mach-jz4740/dma.h>
Am bit worried about having header in arch. Why cant we have this drivers header in linux/. That way same IP block cna be reused across archs. I was hoping that you would have move it in 6th patch, but that isnt the case
+static int jz4740_dma_alloc_chan_resources(struct dma_chan *c) +{
- struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
- chan->jz_chan = jz4740_dma_request(chan, NULL);
- if (!chan->jz_chan)
return -EBUSY;
- jz4740_dma_set_complete_cb(chan->jz_chan, jz4740_dma_complete_cb);
- return 0;
Sorry, I didnt reply on this one. The API expects you to allocate a pool of descriptors. These descriptors are to be used in .device_prep_xxx calls later.
-- ~Vinod
On 05/30/2013 07:12 PM, Vinod Koul wrote:
On Thu, May 30, 2013 at 06:25:02PM +0200, Lars-Peter Clausen wrote:
This patch adds dmaengine support for the JZ4740 DMA controller. For now the driver will be a wrapper around the custom JZ4740 DMA API. Once all users of the custom JZ4740 DMA API have been converted to the dmaengine API the custom API will be removed and direct hardware access will be added to the dmaengine driver.
+#include <asm/mach-jz4740/dma.h>
Am bit worried about having header in arch. Why cant we have this drivers header in linux/. That way same IP block cna be reused across archs. I was hoping that you would have move it in 6th patch, but that isnt the case
At the end of this series the header only contains the slave ids used by the various cores on the JZ4740. Since these ids differ from SoC to SoC it doesn't make much sense to move the header to a generic location.
+static int jz4740_dma_alloc_chan_resources(struct dma_chan *c) +{
- struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
- chan->jz_chan = jz4740_dma_request(chan, NULL);
- if (!chan->jz_chan)
return -EBUSY;
- jz4740_dma_set_complete_cb(chan->jz_chan, jz4740_dma_complete_cb);
- return 0;
Sorry, I didnt reply on this one. The API expects you to allocate a pool of descriptors. These descriptors are to be used in .device_prep_xxx calls later.
The size of the descriptor is not fixed, so they can not be pre-allocated. And this is nothing new either, most of the more recently added dmaengine drivers allocate their descriptors on demand.
- Lars
On 05/30/2013 08:46 PM, Lars-Peter Clausen wrote:
+static int jz4740_dma_alloc_chan_resources(struct dma_chan *c) +{
- struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
- chan->jz_chan = jz4740_dma_request(chan, NULL);
- if (!chan->jz_chan)
return -EBUSY;
- jz4740_dma_set_complete_cb(chan->jz_chan, jz4740_dma_complete_cb);
- return 0;
Sorry, I didnt reply on this one. The API expects you to allocate a pool of descriptors. These descriptors are to be used in .device_prep_xxx calls later.
The size of the descriptor is not fixed, so they can not be pre-allocated. And this is nothing new either, most of the more recently added dmaengine drivers allocate their descriptors on demand.
Vinod, are you ok with this explanation?
- Lars
On Mon, Jun 10, 2013 at 07:29:46PM +0200, Lars-Peter Clausen wrote:
On 05/30/2013 08:46 PM, Lars-Peter Clausen wrote:
+static int jz4740_dma_alloc_chan_resources(struct dma_chan *c) +{
- struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
- chan->jz_chan = jz4740_dma_request(chan, NULL);
- if (!chan->jz_chan)
return -EBUSY;
- jz4740_dma_set_complete_cb(chan->jz_chan, jz4740_dma_complete_cb);
- return 0;
Sorry, I didnt reply on this one. The API expects you to allocate a pool of descriptors. These descriptors are to be used in .device_prep_xxx calls later.
The size of the descriptor is not fixed, so they can not be pre-allocated. And this is nothing new either, most of the more recently added dmaengine drivers allocate their descriptors on demand.
Vinod, are you ok with this explanation?
Sorry, I was travelling...
Can you explain more of a bit when you say size is not fixed. Why would it be issue if we allocate descriptors at the alloc_chan. The idea is that you preallocated pool at alloc_chan and since the .device_prep_xxx calls can be called from atomic context as well, you dont need to do this later. You can use use these descriptors at that time. The idea is keep rotating the descriptors from free poll to used one
-- ~Vinod
On 06/12/2013 07:38 AM, Vinod Koul wrote:
On Mon, Jun 10, 2013 at 07:29:46PM +0200, Lars-Peter Clausen wrote:
On 05/30/2013 08:46 PM, Lars-Peter Clausen wrote:
+static int jz4740_dma_alloc_chan_resources(struct dma_chan *c) +{
- struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
- chan->jz_chan = jz4740_dma_request(chan, NULL);
- if (!chan->jz_chan)
return -EBUSY;
- jz4740_dma_set_complete_cb(chan->jz_chan, jz4740_dma_complete_cb);
- return 0;
Sorry, I didnt reply on this one. The API expects you to allocate a pool of descriptors. These descriptors are to be used in .device_prep_xxx calls later.
The size of the descriptor is not fixed, so they can not be pre-allocated. And this is nothing new either, most of the more recently added dmaengine drivers allocate their descriptors on demand.
Vinod, are you ok with this explanation?
Sorry, I was travelling...
Can you explain more of a bit when you say size is not fixed.
This is the function that allocates the descriptor:
static struct jz4740_dma_desc *jz4740_dma_alloc_desc(unsigned int num_sgs) { return kzalloc(sizeof(struct jz4740_dma_desc) + sizeof(struct jz4740_dma_sg) * num_sgs, GFP_ATOMIC); }
So the size depends on the entries in the sg list.
Why would it be issue if we allocate descriptors at the alloc_chan. The idea is that you preallocated pool at alloc_chan and since the .device_prep_xxx calls can be called from atomic context as well, you dont need to do this later. You can use use these descriptors at that time. The idea is keep rotating the descriptors from free poll to used one
Yes, I know all that. And it makes sense to use a pool in certain situations, e.g. if the hardware only supports a limited set of physical descriptors. But in this case the descriptors are completely virtual. Forcing the driver to use a pool would make it more complex, use more memory and also a bit slower (although probably not noticeable).
- Lars
On Wed, Jun 12, 2013 at 02:07:10PM +0200, Lars-Peter Clausen wrote:
On 06/12/2013 07:38 AM, Vinod Koul wrote:
On Mon, Jun 10, 2013 at 07:29:46PM +0200, Lars-Peter Clausen wrote:
On 05/30/2013 08:46 PM, Lars-Peter Clausen wrote:
+static int jz4740_dma_alloc_chan_resources(struct dma_chan *c) +{
- struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
- chan->jz_chan = jz4740_dma_request(chan, NULL);
- if (!chan->jz_chan)
return -EBUSY;
- jz4740_dma_set_complete_cb(chan->jz_chan, jz4740_dma_complete_cb);
- return 0;
Sorry, I didnt reply on this one. The API expects you to allocate a pool of descriptors. These descriptors are to be used in .device_prep_xxx calls later.
The size of the descriptor is not fixed, so they can not be pre-allocated. And this is nothing new either, most of the more recently added dmaengine drivers allocate their descriptors on demand.
Vinod, are you ok with this explanation?
Sorry, I was travelling...
Can you explain more of a bit when you say size is not fixed.
This is the function that allocates the descriptor:
static struct jz4740_dma_desc *jz4740_dma_alloc_desc(unsigned int num_sgs) { return kzalloc(sizeof(struct jz4740_dma_desc) + sizeof(struct jz4740_dma_sg) * num_sgs, GFP_ATOMIC); }
So the size depends on the entries in the sg list.
Why would it be issue if we allocate descriptors at the alloc_chan. The idea is that you preallocated pool at alloc_chan and since the .device_prep_xxx calls can be called from atomic context as well, you dont need to do this later. You can use use these descriptors at that time. The idea is keep rotating the descriptors from free poll to used one
Yes, I know all that. And it makes sense to use a pool in certain situations, e.g. if the hardware only supports a limited set of physical descriptors. But in this case the descriptors are completely virtual. Forcing the driver to use a pool would make it more complex, use more memory and also a bit slower (although probably not noticeable).
Okay, I am going to try applying these now
-- ~Vinod
Register a device for the newly added jz4740 dmaengine driver.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de Acked-by: Ralf Baechle ralf@linux-mips.org --- No changes since v1 --- arch/mips/include/asm/mach-jz4740/platform.h | 1 + arch/mips/jz4740/board-qi_lb60.c | 1 + arch/mips/jz4740/platform.c | 21 +++++++++++++++++++++ 3 files changed, 23 insertions(+)
diff --git a/arch/mips/include/asm/mach-jz4740/platform.h b/arch/mips/include/asm/mach-jz4740/platform.h index 72cfebd..05988c2 100644 --- a/arch/mips/include/asm/mach-jz4740/platform.h +++ b/arch/mips/include/asm/mach-jz4740/platform.h @@ -32,6 +32,7 @@ extern struct platform_device jz4740_codec_device; extern struct platform_device jz4740_adc_device; extern struct platform_device jz4740_wdt_device; extern struct platform_device jz4740_pwm_device; +extern struct platform_device jz4740_dma_device;
void jz4740_serial_device_register(void);
diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c index be2b3de..8a5ec0e 100644 --- a/arch/mips/jz4740/board-qi_lb60.c +++ b/arch/mips/jz4740/board-qi_lb60.c @@ -438,6 +438,7 @@ static struct platform_device *jz_platform_devices[] __initdata = { &jz4740_rtc_device, &jz4740_adc_device, &jz4740_pwm_device, + &jz4740_dma_device, &qi_lb60_gpio_keys, &qi_lb60_pwm_beeper, &qi_lb60_charger_device, diff --git a/arch/mips/jz4740/platform.c b/arch/mips/jz4740/platform.c index e9348fd..35a9d8c 100644 --- a/arch/mips/jz4740/platform.c +++ b/arch/mips/jz4740/platform.c @@ -329,3 +329,24 @@ struct platform_device jz4740_pwm_device = { .name = "jz4740-pwm", .id = -1, }; + +/* DMA */ +static struct resource jz4740_dma_resources[] = { + { + .start = JZ4740_DMAC_BASE_ADDR, + .end = JZ4740_DMAC_BASE_ADDR + 0x400 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = JZ4740_IRQ_DMAC, + .end = JZ4740_IRQ_DMAC, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device jz4740_dma_device = { + .name = "jz4740-dma", + .id = -1, + .num_resources = ARRAY_SIZE(jz4740_dma_resources), + .resource = jz4740_dma_resources, +};
Hello.
On 05/30/2013 08:25 PM, Lars-Peter Clausen wrote:
Register a device for the newly added jz4740 dmaengine driver.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de Acked-by: Ralf Baechle ralf@linux-mips.org
[...]
3 files changed, 23 insertions(+) diff --git a/arch/mips/jz4740/platform.c b/arch/mips/jz4740/platform.c index e9348fd..35a9d8c 100644 --- a/arch/mips/jz4740/platform.c +++ b/arch/mips/jz4740/platform.c @@ -329,3 +329,24 @@ struct platform_device jz4740_pwm_device = {
[...]
+struct platform_device jz4740_dma_device = {
- .name = "jz4740-dma",
- .id = -1,
Why not align all = in this structure?
- .num_resources = ARRAY_SIZE(jz4740_dma_resources),
- .resource = jz4740_dma_resources,
+};
On 05/30/2013 07:24 PM, Sergei Shtylyov wrote:
Hello.
On 05/30/2013 08:25 PM, Lars-Peter Clausen wrote:
Register a device for the newly added jz4740 dmaengine driver.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de Acked-by: Ralf Baechle ralf@linux-mips.org
[...]
3 files changed, 23 insertions(+) diff --git a/arch/mips/jz4740/platform.c b/arch/mips/jz4740/platform.c index e9348fd..35a9d8c 100644 --- a/arch/mips/jz4740/platform.c +++ b/arch/mips/jz4740/platform.c @@ -329,3 +329,24 @@ struct platform_device jz4740_pwm_device = {
[...]
+struct platform_device jz4740_dma_device = {
- .name = "jz4740-dma",
- .id = -1,
Why not align all = in this structure?
Will fix that in the next version, thanks.
- Lars
Since there is a dmaengine driver for the jz4740 DMA controller now we can use the generic dmaengine PCM driver instead of a custom one.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de Acked-by: Mark Brown broonie@opensource.wolfsonmicro.com --- No changes since v1 --- sound/soc/jz4740/Kconfig | 1 + sound/soc/jz4740/jz4740-i2s.c | 48 +++---- sound/soc/jz4740/jz4740-pcm.c | 310 ++---------------------------------------- sound/soc/jz4740/jz4740-pcm.h | 20 --- 4 files changed, 27 insertions(+), 352 deletions(-) delete mode 100644 sound/soc/jz4740/jz4740-pcm.h
diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig index 5351cba..29f76af 100644 --- a/sound/soc/jz4740/Kconfig +++ b/sound/soc/jz4740/Kconfig @@ -1,6 +1,7 @@ config SND_JZ4740_SOC tristate "SoC Audio for Ingenic JZ4740 SoC" depends on MACH_JZ4740 && SND_SOC + select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for codecs attached to the JZ4740 I2S interface. You will also need to select the audio diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index 4c849a4..47d3b4c 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -29,9 +29,12 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/initval.h> +#include <sound/dmaengine_pcm.h> + +#include <asm/mach-jz4740/dma.h>
#include "jz4740-i2s.h" -#include "jz4740-pcm.h" +
#define JZ_REG_AIC_CONF 0x00 #define JZ_REG_AIC_CTRL 0x04 @@ -89,8 +92,8 @@ struct jz4740_i2s { struct clk *clk_aic; struct clk *clk_i2s;
- struct jz4740_pcm_config pcm_config_playback; - struct jz4740_pcm_config pcm_config_capture; + struct snd_dmaengine_dai_dma_data playback_dma_data; + struct snd_dmaengine_dai_dma_data capture_dma_data; };
static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s, @@ -233,8 +236,6 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); - enum jz4740_dma_width dma_width; - struct jz4740_pcm_config *pcm_config; unsigned int sample_size; uint32_t ctrl;
@@ -243,11 +244,9 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: sample_size = 0; - dma_width = JZ4740_DMA_WIDTH_8BIT; break; case SNDRV_PCM_FORMAT_S16: sample_size = 1; - dma_width = JZ4740_DMA_WIDTH_16BIT; break; default: return -EINVAL; @@ -260,22 +259,13 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream, ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO; else ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO; - - pcm_config = &i2s->pcm_config_playback; - pcm_config->dma_config.dst_width = dma_width; - } else { ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK; ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET; - - pcm_config = &i2s->pcm_config_capture; - pcm_config->dma_config.src_width = dma_width; }
jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
- snd_soc_dai_set_dma_data(dai, substream, pcm_config); - return 0; }
@@ -342,25 +332,19 @@ static int jz4740_i2s_resume(struct snd_soc_dai *dai)
static void jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s) { - struct jz4740_dma_config *dma_config; + struct snd_dmaengine_dai_dma_data *dma_data;
/* Playback */ - dma_config = &i2s->pcm_config_playback.dma_config; - dma_config->src_width = JZ4740_DMA_WIDTH_32BIT; - dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE; - dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT; - dma_config->flags = JZ4740_DMA_SRC_AUTOINC; - dma_config->mode = JZ4740_DMA_MODE_SINGLE; - i2s->pcm_config_playback.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO; + dma_data = &i2s->playback_dma_data; + dma_data->maxburst = 16; + dma_data->slave_id = JZ4740_DMA_TYPE_AIC_TRANSMIT; + dma_data->addr = i2s->phys_base + JZ_REG_AIC_FIFO;
/* Capture */ - dma_config = &i2s->pcm_config_capture.dma_config; - dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT; - dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE; - dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE; - dma_config->flags = JZ4740_DMA_DST_AUTOINC; - dma_config->mode = JZ4740_DMA_MODE_SINGLE; - i2s->pcm_config_capture.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO; + dma_data = &i2s->capture_dma_data; + dma_data->maxburst = 16; + dma_data->slave_id = JZ4740_DMA_TYPE_AIC_RECEIVE; + dma_data->addr = i2s->phys_base + JZ_REG_AIC_FIFO; }
static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai) @@ -371,6 +355,8 @@ static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai) clk_prepare_enable(i2s->clk_aic);
jz4740_i2c_init_pcm_config(i2s); + dai->playback_dma_data = &i2s->playback_dma_data; + dai->capture_dma_data = &i2s->capture_dma_data;
conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | diff --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c index 7100592..79fcade 100644 --- a/sound/soc/jz4740/jz4740-pcm.c +++ b/sound/soc/jz4740/jz4740-pcm.c @@ -19,38 +19,14 @@ #include <linux/platform_device.h> #include <linux/slab.h>
-#include <linux/dma-mapping.h> +#include <sound/dmaengine_pcm.h>
-#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/soc.h> - -#include <asm/mach-jz4740/dma.h> -#include "jz4740-pcm.h" - -struct jz4740_runtime_data { - unsigned long dma_period; - dma_addr_t dma_start; - dma_addr_t dma_pos; - dma_addr_t dma_end; - - struct jz4740_dma_chan *dma; - - dma_addr_t fifo_addr; -}; - -/* identify hardware playback capabilities */ static const struct snd_pcm_hardware jz4740_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8, - - .rates = SNDRV_PCM_RATE_8000_48000, - .channels_min = 1, - .channels_max = 2, .period_bytes_min = 16, .period_bytes_max = 2 * PAGE_SIZE, .periods_min = 2, @@ -59,290 +35,22 @@ static const struct snd_pcm_hardware jz4740_pcm_hardware = { .fifo_size = 32, };
-static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd, - struct snd_pcm_substream *substream) -{ - unsigned long count; - - if (prtd->dma_pos == prtd->dma_end) - prtd->dma_pos = prtd->dma_start; - - if (prtd->dma_pos + prtd->dma_period > prtd->dma_end) - count = prtd->dma_end - prtd->dma_pos; - else - count = prtd->dma_period; - - jz4740_dma_disable(prtd->dma); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos); - jz4740_dma_set_dst_addr(prtd->dma, prtd->fifo_addr); - } else { - jz4740_dma_set_src_addr(prtd->dma, prtd->fifo_addr); - jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos); - } - - jz4740_dma_set_transfer_count(prtd->dma, count); - - prtd->dma_pos += count; - - jz4740_dma_enable(prtd->dma); -} - -static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err, - void *dev_id) -{ - struct snd_pcm_substream *substream = dev_id; - struct snd_pcm_runtime *runtime = substream->runtime; - struct jz4740_runtime_data *prtd = runtime->private_data; - - snd_pcm_period_elapsed(substream); - - jz4740_pcm_start_transfer(prtd, substream); -} - -static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct jz4740_runtime_data *prtd = runtime->private_data; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct jz4740_pcm_config *config; - - config = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - - if (!config) - return 0; - - if (!prtd->dma) { - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - prtd->dma = jz4740_dma_request(substream, "PCM Capture"); - else - prtd->dma = jz4740_dma_request(substream, "PCM Playback"); - } - - if (!prtd->dma) - return -EBUSY; - - jz4740_dma_configure(prtd->dma, &config->dma_config); - prtd->fifo_addr = config->fifo_addr; - - jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done); - - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - runtime->dma_bytes = params_buffer_bytes(params); - - prtd->dma_period = params_period_bytes(params); - prtd->dma_start = runtime->dma_addr; - prtd->dma_pos = prtd->dma_start; - prtd->dma_end = prtd->dma_start + runtime->dma_bytes; - - return 0; -} - -static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream) -{ - struct jz4740_runtime_data *prtd = substream->runtime->private_data; - - snd_pcm_set_runtime_buffer(substream, NULL); - if (prtd->dma) { - jz4740_dma_free(prtd->dma); - prtd->dma = NULL; - } - - return 0; -} - -static int jz4740_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct jz4740_runtime_data *prtd = substream->runtime->private_data; - - if (!prtd->dma) - return -EBUSY; - - prtd->dma_pos = prtd->dma_start; - - return 0; -} - -static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct jz4740_runtime_data *prtd = runtime->private_data; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - jz4740_pcm_start_transfer(prtd, substream); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - jz4740_dma_disable(prtd->dma); - break; - default: - break; - } - - return 0; -} - -static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct jz4740_runtime_data *prtd = runtime->private_data; - unsigned long byte_offset; - snd_pcm_uframes_t offset; - struct jz4740_dma_chan *dma = prtd->dma; - - /* prtd->dma_pos points to the end of the current transfer. So by - * subtracting prdt->dma_start we get the offset to the end of the - * current period in bytes. By subtracting the residue of the transfer - * we get the current offset in bytes. */ - byte_offset = prtd->dma_pos - prtd->dma_start; - byte_offset -= jz4740_dma_get_residue(dma); - - offset = bytes_to_frames(runtime, byte_offset); - if (offset >= runtime->buffer_size) - offset = 0; - - return offset; -} - -static int jz4740_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct jz4740_runtime_data *prtd; - - prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); - if (prtd == NULL) - return -ENOMEM; - - snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware); - - runtime->private_data = prtd; - - return 0; -} - -static int jz4740_pcm_close(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct jz4740_runtime_data *prtd = runtime->private_data; - - kfree(prtd); - - return 0; -} - -static int jz4740_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - return remap_pfn_range(vma, vma->vm_start, - substream->dma_buffer.addr >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot); -} - -static struct snd_pcm_ops jz4740_pcm_ops = { - .open = jz4740_pcm_open, - .close = jz4740_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = jz4740_pcm_hw_params, - .hw_free = jz4740_pcm_hw_free, - .prepare = jz4740_pcm_prepare, - .trigger = jz4740_pcm_trigger, - .pointer = jz4740_pcm_pointer, - .mmap = jz4740_pcm_mmap, -}; - -static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = jz4740_pcm_hardware.buffer_bytes_max; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - - buf->area = dma_alloc_noncoherent(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - if (!buf->area) - return -ENOMEM; - - buf->bytes = size; - - return 0; -} - -static void jz4740_pcm_free(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < SNDRV_PCM_STREAM_LAST; ++stream) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - - dma_free_noncoherent(pcm->card->dev, buf->bytes, buf->area, - buf->addr); - buf->area = NULL; - } -} - -static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32); - -static int jz4740_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret = 0; - - if (!card->dev->dma_mask) - card->dev->dma_mask = &jz4740_pcm_dmamask; - - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_BIT_MASK(32); - - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - ret = jz4740_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - goto err; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - ret = jz4740_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto err; - } - -err: - return ret; -} - -static struct snd_soc_platform_driver jz4740_soc_platform = { - .ops = &jz4740_pcm_ops, - .pcm_new = jz4740_pcm_new, - .pcm_free = jz4740_pcm_free, +static const struct snd_dmaengine_pcm_config jz4740_dmaengine_pcm_config = { + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .pcm_hardware = &jz4740_pcm_hardware, + .prealloc_buffer_size = 256 * PAGE_SIZE, };
static int jz4740_pcm_probe(struct platform_device *pdev) { - return snd_soc_register_platform(&pdev->dev, &jz4740_soc_platform); + return snd_dmaengine_pcm_register(&pdev->dev, + &jz4740_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_COMPAT); }
static int jz4740_pcm_remove(struct platform_device *pdev) { - snd_soc_unregister_platform(&pdev->dev); + snd_dmaengine_pcm_unregister(&pdev->dev); return 0; }
diff --git a/sound/soc/jz4740/jz4740-pcm.h b/sound/soc/jz4740/jz4740-pcm.h deleted file mode 100644 index 1220cbb..0000000 --- a/sound/soc/jz4740/jz4740-pcm.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - * 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 _JZ4740_PCM_H -#define _JZ4740_PCM_H - -#include <linux/dma-mapping.h> -#include <asm/mach-jz4740/dma.h> - - -struct jz4740_pcm_config { - struct jz4740_dma_config dma_config; - phys_addr_t fifo_addr; -}; - -#endif
Now that all users of the custom jz4740 DMA API have been converted to use the dmaengine API instead we can remove the custom API and move all the code talking to the hardware to the dmaengine driver.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de Acked-by: Ralf Baechle ralf@linux-mips.org --- No changes since v1 --- arch/mips/include/asm/mach-jz4740/dma.h | 56 ------ arch/mips/jz4740/Makefile | 2 +- arch/mips/jz4740/dma.c | 307 -------------------------------- drivers/dma/dma-jz4740.c | 258 +++++++++++++++++++++++---- 4 files changed, 222 insertions(+), 401 deletions(-) delete mode 100644 arch/mips/jz4740/dma.c
diff --git a/arch/mips/include/asm/mach-jz4740/dma.h b/arch/mips/include/asm/mach-jz4740/dma.h index 98b4e7c..509cd58 100644 --- a/arch/mips/include/asm/mach-jz4740/dma.h +++ b/arch/mips/include/asm/mach-jz4740/dma.h @@ -16,8 +16,6 @@ #ifndef __ASM_MACH_JZ4740_DMA_H__ #define __ASM_MACH_JZ4740_DMA_H__
-struct jz4740_dma_chan; - enum jz4740_dma_request_type { JZ4740_DMA_TYPE_AUTO_REQUEST = 8, JZ4740_DMA_TYPE_UART_TRANSMIT = 20, @@ -33,58 +31,4 @@ enum jz4740_dma_request_type { JZ4740_DMA_TYPE_SLCD = 30, };
-enum jz4740_dma_width { - JZ4740_DMA_WIDTH_32BIT = 0, - JZ4740_DMA_WIDTH_8BIT = 1, - JZ4740_DMA_WIDTH_16BIT = 2, -}; - -enum jz4740_dma_transfer_size { - JZ4740_DMA_TRANSFER_SIZE_4BYTE = 0, - JZ4740_DMA_TRANSFER_SIZE_1BYTE = 1, - JZ4740_DMA_TRANSFER_SIZE_2BYTE = 2, - JZ4740_DMA_TRANSFER_SIZE_16BYTE = 3, - JZ4740_DMA_TRANSFER_SIZE_32BYTE = 4, -}; - -enum jz4740_dma_flags { - JZ4740_DMA_SRC_AUTOINC = 0x2, - JZ4740_DMA_DST_AUTOINC = 0x1, -}; - -enum jz4740_dma_mode { - JZ4740_DMA_MODE_SINGLE = 0, - JZ4740_DMA_MODE_BLOCK = 1, -}; - -struct jz4740_dma_config { - enum jz4740_dma_width src_width; - enum jz4740_dma_width dst_width; - enum jz4740_dma_transfer_size transfer_size; - enum jz4740_dma_request_type request_type; - enum jz4740_dma_flags flags; - enum jz4740_dma_mode mode; -}; - -typedef void (*jz4740_dma_complete_callback_t)(struct jz4740_dma_chan *, int, void *); - -struct jz4740_dma_chan *jz4740_dma_request(void *dev, const char *name); -void jz4740_dma_free(struct jz4740_dma_chan *dma); - -void jz4740_dma_configure(struct jz4740_dma_chan *dma, - const struct jz4740_dma_config *config); - - -void jz4740_dma_enable(struct jz4740_dma_chan *dma); -void jz4740_dma_disable(struct jz4740_dma_chan *dma); - -void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src); -void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst); -void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count); - -uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma); - -void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma, - jz4740_dma_complete_callback_t cb); - #endif /* __ASM_JZ4740_DMA_H__ */ diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile index 63bad0e..28e5535 100644 --- a/arch/mips/jz4740/Makefile +++ b/arch/mips/jz4740/Makefile @@ -4,7 +4,7 @@
# Object file lists.
-obj-y += prom.o irq.o time.o reset.o setup.o dma.o \ +obj-y += prom.o irq.o time.o reset.o setup.o \ gpio.o clock.o platform.o timer.o serial.o
obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o diff --git a/arch/mips/jz4740/dma.c b/arch/mips/jz4740/dma.c deleted file mode 100644 index 0e34b97..0000000 --- a/arch/mips/jz4740/dma.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (C) 2010, Lars-Peter Clausen lars@metafoo.de - * JZ4740 SoC DMA support - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/spinlock.h> -#include <linux/clk.h> -#include <linux/interrupt.h> - -#include <linux/dma-mapping.h> -#include <asm/mach-jz4740/dma.h> -#include <asm/mach-jz4740/base.h> - -#define JZ_REG_DMA_SRC_ADDR(x) (0x00 + (x) * 0x20) -#define JZ_REG_DMA_DST_ADDR(x) (0x04 + (x) * 0x20) -#define JZ_REG_DMA_TRANSFER_COUNT(x) (0x08 + (x) * 0x20) -#define JZ_REG_DMA_REQ_TYPE(x) (0x0C + (x) * 0x20) -#define JZ_REG_DMA_STATUS_CTRL(x) (0x10 + (x) * 0x20) -#define JZ_REG_DMA_CMD(x) (0x14 + (x) * 0x20) -#define JZ_REG_DMA_DESC_ADDR(x) (0x18 + (x) * 0x20) - -#define JZ_REG_DMA_CTRL 0x300 -#define JZ_REG_DMA_IRQ 0x304 -#define JZ_REG_DMA_DOORBELL 0x308 -#define JZ_REG_DMA_DOORBELL_SET 0x30C - -#define JZ_DMA_STATUS_CTRL_NO_DESC BIT(31) -#define JZ_DMA_STATUS_CTRL_DESC_INV BIT(6) -#define JZ_DMA_STATUS_CTRL_ADDR_ERR BIT(4) -#define JZ_DMA_STATUS_CTRL_TRANSFER_DONE BIT(3) -#define JZ_DMA_STATUS_CTRL_HALT BIT(2) -#define JZ_DMA_STATUS_CTRL_COUNT_TERMINATE BIT(1) -#define JZ_DMA_STATUS_CTRL_ENABLE BIT(0) - -#define JZ_DMA_CMD_SRC_INC BIT(23) -#define JZ_DMA_CMD_DST_INC BIT(22) -#define JZ_DMA_CMD_RDIL_MASK (0xf << 16) -#define JZ_DMA_CMD_SRC_WIDTH_MASK (0x3 << 14) -#define JZ_DMA_CMD_DST_WIDTH_MASK (0x3 << 12) -#define JZ_DMA_CMD_INTERVAL_LENGTH_MASK (0x7 << 8) -#define JZ_DMA_CMD_BLOCK_MODE BIT(7) -#define JZ_DMA_CMD_DESC_VALID BIT(4) -#define JZ_DMA_CMD_DESC_VALID_MODE BIT(3) -#define JZ_DMA_CMD_VALID_IRQ_ENABLE BIT(2) -#define JZ_DMA_CMD_TRANSFER_IRQ_ENABLE BIT(1) -#define JZ_DMA_CMD_LINK_ENABLE BIT(0) - -#define JZ_DMA_CMD_FLAGS_OFFSET 22 -#define JZ_DMA_CMD_RDIL_OFFSET 16 -#define JZ_DMA_CMD_SRC_WIDTH_OFFSET 14 -#define JZ_DMA_CMD_DST_WIDTH_OFFSET 12 -#define JZ_DMA_CMD_TRANSFER_SIZE_OFFSET 8 -#define JZ_DMA_CMD_MODE_OFFSET 7 - -#define JZ_DMA_CTRL_PRIORITY_MASK (0x3 << 8) -#define JZ_DMA_CTRL_HALT BIT(3) -#define JZ_DMA_CTRL_ADDRESS_ERROR BIT(2) -#define JZ_DMA_CTRL_ENABLE BIT(0) - - -static void __iomem *jz4740_dma_base; -static spinlock_t jz4740_dma_lock; - -static inline uint32_t jz4740_dma_read(size_t reg) -{ - return readl(jz4740_dma_base + reg); -} - -static inline void jz4740_dma_write(size_t reg, uint32_t val) -{ - writel(val, jz4740_dma_base + reg); -} - -static inline void jz4740_dma_write_mask(size_t reg, uint32_t val, uint32_t mask) -{ - uint32_t val2; - val2 = jz4740_dma_read(reg); - val2 &= ~mask; - val2 |= val; - jz4740_dma_write(reg, val2); -} - -struct jz4740_dma_chan { - unsigned int id; - void *dev; - const char *name; - - enum jz4740_dma_flags flags; - uint32_t transfer_shift; - - jz4740_dma_complete_callback_t complete_cb; - - unsigned used:1; -}; - -#define JZ4740_DMA_CHANNEL(_id) { .id = _id } - -struct jz4740_dma_chan jz4740_dma_channels[] = { - JZ4740_DMA_CHANNEL(0), - JZ4740_DMA_CHANNEL(1), - JZ4740_DMA_CHANNEL(2), - JZ4740_DMA_CHANNEL(3), - JZ4740_DMA_CHANNEL(4), - JZ4740_DMA_CHANNEL(5), -}; - -struct jz4740_dma_chan *jz4740_dma_request(void *dev, const char *name) -{ - unsigned int i; - struct jz4740_dma_chan *dma = NULL; - - spin_lock(&jz4740_dma_lock); - - for (i = 0; i < ARRAY_SIZE(jz4740_dma_channels); ++i) { - if (!jz4740_dma_channels[i].used) { - dma = &jz4740_dma_channels[i]; - dma->used = 1; - break; - } - } - - spin_unlock(&jz4740_dma_lock); - - if (!dma) - return NULL; - - dma->dev = dev; - dma->name = name; - - return dma; -} -EXPORT_SYMBOL_GPL(jz4740_dma_request); - -void jz4740_dma_configure(struct jz4740_dma_chan *dma, - const struct jz4740_dma_config *config) -{ - uint32_t cmd; - - switch (config->transfer_size) { - case JZ4740_DMA_TRANSFER_SIZE_2BYTE: - dma->transfer_shift = 1; - break; - case JZ4740_DMA_TRANSFER_SIZE_4BYTE: - dma->transfer_shift = 2; - break; - case JZ4740_DMA_TRANSFER_SIZE_16BYTE: - dma->transfer_shift = 4; - break; - case JZ4740_DMA_TRANSFER_SIZE_32BYTE: - dma->transfer_shift = 5; - break; - default: - dma->transfer_shift = 0; - break; - } - - cmd = config->flags << JZ_DMA_CMD_FLAGS_OFFSET; - cmd |= config->src_width << JZ_DMA_CMD_SRC_WIDTH_OFFSET; - cmd |= config->dst_width << JZ_DMA_CMD_DST_WIDTH_OFFSET; - cmd |= config->transfer_size << JZ_DMA_CMD_TRANSFER_SIZE_OFFSET; - cmd |= config->mode << JZ_DMA_CMD_MODE_OFFSET; - cmd |= JZ_DMA_CMD_TRANSFER_IRQ_ENABLE; - - jz4740_dma_write(JZ_REG_DMA_CMD(dma->id), cmd); - jz4740_dma_write(JZ_REG_DMA_STATUS_CTRL(dma->id), 0); - jz4740_dma_write(JZ_REG_DMA_REQ_TYPE(dma->id), config->request_type); -} -EXPORT_SYMBOL_GPL(jz4740_dma_configure); - -void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src) -{ - jz4740_dma_write(JZ_REG_DMA_SRC_ADDR(dma->id), src); -} -EXPORT_SYMBOL_GPL(jz4740_dma_set_src_addr); - -void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst) -{ - jz4740_dma_write(JZ_REG_DMA_DST_ADDR(dma->id), dst); -} -EXPORT_SYMBOL_GPL(jz4740_dma_set_dst_addr); - -void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count) -{ - count >>= dma->transfer_shift; - jz4740_dma_write(JZ_REG_DMA_TRANSFER_COUNT(dma->id), count); -} -EXPORT_SYMBOL_GPL(jz4740_dma_set_transfer_count); - -void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma, - jz4740_dma_complete_callback_t cb) -{ - dma->complete_cb = cb; -} -EXPORT_SYMBOL_GPL(jz4740_dma_set_complete_cb); - -void jz4740_dma_free(struct jz4740_dma_chan *dma) -{ - dma->dev = NULL; - dma->complete_cb = NULL; - dma->used = 0; -} -EXPORT_SYMBOL_GPL(jz4740_dma_free); - -void jz4740_dma_enable(struct jz4740_dma_chan *dma) -{ - jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), - JZ_DMA_STATUS_CTRL_NO_DESC | JZ_DMA_STATUS_CTRL_ENABLE, - JZ_DMA_STATUS_CTRL_HALT | JZ_DMA_STATUS_CTRL_NO_DESC | - JZ_DMA_STATUS_CTRL_ENABLE); - - jz4740_dma_write_mask(JZ_REG_DMA_CTRL, - JZ_DMA_CTRL_ENABLE, - JZ_DMA_CTRL_HALT | JZ_DMA_CTRL_ENABLE); -} -EXPORT_SYMBOL_GPL(jz4740_dma_enable); - -void jz4740_dma_disable(struct jz4740_dma_chan *dma) -{ - jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0, - JZ_DMA_STATUS_CTRL_ENABLE); -} -EXPORT_SYMBOL_GPL(jz4740_dma_disable); - -uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma) -{ - uint32_t residue; - residue = jz4740_dma_read(JZ_REG_DMA_TRANSFER_COUNT(dma->id)); - return residue << dma->transfer_shift; -} -EXPORT_SYMBOL_GPL(jz4740_dma_get_residue); - -static void jz4740_dma_chan_irq(struct jz4740_dma_chan *dma) -{ - (void) jz4740_dma_read(JZ_REG_DMA_STATUS_CTRL(dma->id)); - - jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0, - JZ_DMA_STATUS_CTRL_ENABLE | JZ_DMA_STATUS_CTRL_TRANSFER_DONE); - - if (dma->complete_cb) - dma->complete_cb(dma, 0, dma->dev); -} - -static irqreturn_t jz4740_dma_irq(int irq, void *dev_id) -{ - uint32_t irq_status; - unsigned int i; - - irq_status = readl(jz4740_dma_base + JZ_REG_DMA_IRQ); - - for (i = 0; i < 6; ++i) { - if (irq_status & (1 << i)) - jz4740_dma_chan_irq(&jz4740_dma_channels[i]); - } - - return IRQ_HANDLED; -} - -static int jz4740_dma_init(void) -{ - struct clk *clk; - unsigned int ret; - - jz4740_dma_base = ioremap(JZ4740_DMAC_BASE_ADDR, 0x400); - - if (!jz4740_dma_base) - return -EBUSY; - - spin_lock_init(&jz4740_dma_lock); - - clk = clk_get(NULL, "dma"); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - printk(KERN_ERR "JZ4740 DMA: Failed to request clock: %d\n", - ret); - goto err_iounmap; - } - - ret = request_irq(JZ4740_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL); - if (ret) { - printk(KERN_ERR "JZ4740 DMA: Failed to request irq: %d\n", ret); - goto err_clkput; - } - - clk_prepare_enable(clk); - - return 0; - -err_clkput: - clk_put(clk); - -err_iounmap: - iounmap(jz4740_dma_base); - return ret; -} -arch_initcall(jz4740_dma_init); diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c index 3d42434..b0c0c82 100644 --- a/drivers/dma/dma-jz4740.c +++ b/drivers/dma/dma-jz4740.c @@ -22,6 +22,8 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/irq.h> +#include <linux/clk.h>
#include <asm/mach-jz4740/dma.h>
@@ -29,6 +31,76 @@
#define JZ_DMA_NR_CHANS 6
+#define JZ_REG_DMA_SRC_ADDR(x) (0x00 + (x) * 0x20) +#define JZ_REG_DMA_DST_ADDR(x) (0x04 + (x) * 0x20) +#define JZ_REG_DMA_TRANSFER_COUNT(x) (0x08 + (x) * 0x20) +#define JZ_REG_DMA_REQ_TYPE(x) (0x0C + (x) * 0x20) +#define JZ_REG_DMA_STATUS_CTRL(x) (0x10 + (x) * 0x20) +#define JZ_REG_DMA_CMD(x) (0x14 + (x) * 0x20) +#define JZ_REG_DMA_DESC_ADDR(x) (0x18 + (x) * 0x20) + +#define JZ_REG_DMA_CTRL 0x300 +#define JZ_REG_DMA_IRQ 0x304 +#define JZ_REG_DMA_DOORBELL 0x308 +#define JZ_REG_DMA_DOORBELL_SET 0x30C + +#define JZ_DMA_STATUS_CTRL_NO_DESC BIT(31) +#define JZ_DMA_STATUS_CTRL_DESC_INV BIT(6) +#define JZ_DMA_STATUS_CTRL_ADDR_ERR BIT(4) +#define JZ_DMA_STATUS_CTRL_TRANSFER_DONE BIT(3) +#define JZ_DMA_STATUS_CTRL_HALT BIT(2) +#define JZ_DMA_STATUS_CTRL_COUNT_TERMINATE BIT(1) +#define JZ_DMA_STATUS_CTRL_ENABLE BIT(0) + +#define JZ_DMA_CMD_SRC_INC BIT(23) +#define JZ_DMA_CMD_DST_INC BIT(22) +#define JZ_DMA_CMD_RDIL_MASK (0xf << 16) +#define JZ_DMA_CMD_SRC_WIDTH_MASK (0x3 << 14) +#define JZ_DMA_CMD_DST_WIDTH_MASK (0x3 << 12) +#define JZ_DMA_CMD_INTERVAL_LENGTH_MASK (0x7 << 8) +#define JZ_DMA_CMD_BLOCK_MODE BIT(7) +#define JZ_DMA_CMD_DESC_VALID BIT(4) +#define JZ_DMA_CMD_DESC_VALID_MODE BIT(3) +#define JZ_DMA_CMD_VALID_IRQ_ENABLE BIT(2) +#define JZ_DMA_CMD_TRANSFER_IRQ_ENABLE BIT(1) +#define JZ_DMA_CMD_LINK_ENABLE BIT(0) + +#define JZ_DMA_CMD_FLAGS_OFFSET 22 +#define JZ_DMA_CMD_RDIL_OFFSET 16 +#define JZ_DMA_CMD_SRC_WIDTH_OFFSET 14 +#define JZ_DMA_CMD_DST_WIDTH_OFFSET 12 +#define JZ_DMA_CMD_TRANSFER_SIZE_OFFSET 8 +#define JZ_DMA_CMD_MODE_OFFSET 7 + +#define JZ_DMA_CTRL_PRIORITY_MASK (0x3 << 8) +#define JZ_DMA_CTRL_HALT BIT(3) +#define JZ_DMA_CTRL_ADDRESS_ERROR BIT(2) +#define JZ_DMA_CTRL_ENABLE BIT(0) + +enum jz4740_dma_width { + JZ4740_DMA_WIDTH_32BIT = 0, + JZ4740_DMA_WIDTH_8BIT = 1, + JZ4740_DMA_WIDTH_16BIT = 2, +}; + +enum jz4740_dma_transfer_size { + JZ4740_DMA_TRANSFER_SIZE_4BYTE = 0, + JZ4740_DMA_TRANSFER_SIZE_1BYTE = 1, + JZ4740_DMA_TRANSFER_SIZE_2BYTE = 2, + JZ4740_DMA_TRANSFER_SIZE_16BYTE = 3, + JZ4740_DMA_TRANSFER_SIZE_32BYTE = 4, +}; + +enum jz4740_dma_flags { + JZ4740_DMA_SRC_AUTOINC = 0x2, + JZ4740_DMA_DST_AUTOINC = 0x1, +}; + +enum jz4740_dma_mode { + JZ4740_DMA_MODE_SINGLE = 0, + JZ4740_DMA_MODE_BLOCK = 1, +}; + struct jz4740_dma_sg { dma_addr_t addr; unsigned int len; @@ -46,9 +118,10 @@ struct jz4740_dma_desc {
struct jz4740_dmaengine_chan { struct virt_dma_chan vchan; - struct jz4740_dma_chan *jz_chan; + unsigned int id;
dma_addr_t fifo_addr; + unsigned int transfer_shift;
struct jz4740_dma_desc *desc; unsigned int next_sg; @@ -56,10 +129,19 @@ struct jz4740_dmaengine_chan {
struct jz4740_dma_dev { struct dma_device ddev; + void __iomem *base; + struct clk *clk;
struct jz4740_dmaengine_chan chan[JZ_DMA_NR_CHANS]; };
+static struct jz4740_dma_dev *jz4740_dma_chan_get_dev( + struct jz4740_dmaengine_chan *chan) +{ + return container_of(chan->vchan.chan.device, struct jz4740_dma_dev, + ddev); +} + static struct jz4740_dmaengine_chan *to_jz4740_dma_chan(struct dma_chan *c) { return container_of(c, struct jz4740_dmaengine_chan, vchan.chan); @@ -70,6 +152,29 @@ static struct jz4740_dma_desc *to_jz4740_dma_desc(struct virt_dma_desc *vdesc) return container_of(vdesc, struct jz4740_dma_desc, vdesc); }
+static inline uint32_t jz4740_dma_read(struct jz4740_dma_dev *dmadev, + unsigned int reg) +{ + return readl(dmadev->base + reg); +} + +static inline void jz4740_dma_write(struct jz4740_dma_dev *dmadev, + unsigned reg, uint32_t val) +{ + writel(val, dmadev->base + reg); +} + +static inline void jz4740_dma_write_mask(struct jz4740_dma_dev *dmadev, + unsigned int reg, uint32_t val, uint32_t mask) +{ + uint32_t tmp; + + tmp = jz4740_dma_read(dmadev, reg); + tmp &= ~mask; + tmp |= val; + jz4740_dma_write(dmadev, reg, tmp); +} + static struct jz4740_dma_desc *jz4740_dma_alloc_desc(unsigned int num_sgs) { return kzalloc(sizeof(struct jz4740_dma_desc) + @@ -108,30 +213,60 @@ static int jz4740_dma_slave_config(struct dma_chan *c, const struct dma_slave_config *config) { struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); - struct jz4740_dma_config jzcfg; + struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan); + enum jz4740_dma_width src_width; + enum jz4740_dma_width dst_width; + enum jz4740_dma_transfer_size transfer_size; + enum jz4740_dma_flags flags; + uint32_t cmd;
switch (config->direction) { case DMA_MEM_TO_DEV: - jzcfg.flags = JZ4740_DMA_SRC_AUTOINC; - jzcfg.transfer_size = jz4740_dma_maxburst(config->dst_maxburst); + flags = JZ4740_DMA_SRC_AUTOINC; + transfer_size = jz4740_dma_maxburst(config->dst_maxburst); chan->fifo_addr = config->dst_addr; break; case DMA_DEV_TO_MEM: - jzcfg.flags = JZ4740_DMA_DST_AUTOINC; - jzcfg.transfer_size = jz4740_dma_maxburst(config->src_maxburst); + flags = JZ4740_DMA_DST_AUTOINC; + transfer_size = jz4740_dma_maxburst(config->src_maxburst); chan->fifo_addr = config->src_addr; break; default: return -EINVAL; }
+ src_width = jz4740_dma_width(config->src_addr_width); + dst_width = jz4740_dma_width(config->dst_addr_width); + + switch (transfer_size) { + case JZ4740_DMA_TRANSFER_SIZE_2BYTE: + chan->transfer_shift = 1; + break; + case JZ4740_DMA_TRANSFER_SIZE_4BYTE: + chan->transfer_shift = 2; + break; + case JZ4740_DMA_TRANSFER_SIZE_16BYTE: + chan->transfer_shift = 4; + break; + case JZ4740_DMA_TRANSFER_SIZE_32BYTE: + chan->transfer_shift = 5; + break; + default: + chan->transfer_shift = 0; + break; + }
- jzcfg.src_width = jz4740_dma_width(config->src_addr_width); - jzcfg.dst_width = jz4740_dma_width(config->dst_addr_width); - jzcfg.mode = JZ4740_DMA_MODE_SINGLE; - jzcfg.request_type = config->slave_id; + cmd = flags << JZ_DMA_CMD_FLAGS_OFFSET; + cmd |= src_width << JZ_DMA_CMD_SRC_WIDTH_OFFSET; + cmd |= dst_width << JZ_DMA_CMD_DST_WIDTH_OFFSET; + cmd |= transfer_size << JZ_DMA_CMD_TRANSFER_SIZE_OFFSET; + cmd |= JZ4740_DMA_MODE_SINGLE << JZ_DMA_CMD_MODE_OFFSET; + cmd |= JZ_DMA_CMD_TRANSFER_IRQ_ENABLE;
- jz4740_dma_configure(chan->jz_chan, &jzcfg); + jz4740_dma_write(dmadev, JZ_REG_DMA_CMD(chan->id), cmd); + jz4740_dma_write(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), 0); + jz4740_dma_write(dmadev, JZ_REG_DMA_REQ_TYPE(chan->id), + config->slave_id);
return 0; } @@ -139,11 +274,13 @@ static int jz4740_dma_slave_config(struct dma_chan *c, static int jz4740_dma_terminate_all(struct dma_chan *c) { struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan); unsigned long flags; LIST_HEAD(head);
spin_lock_irqsave(&chan->vchan.lock, flags); - jz4740_dma_disable(chan->jz_chan); + jz4740_dma_write_mask(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), 0, + JZ_DMA_STATUS_CTRL_ENABLE); chan->desc = NULL; vchan_get_all_descriptors(&chan->vchan, &head); spin_unlock_irqrestore(&chan->vchan.lock, flags); @@ -170,11 +307,13 @@ static int jz4740_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
static int jz4740_dma_start_transfer(struct jz4740_dmaengine_chan *chan) { + struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan); dma_addr_t src_addr, dst_addr; struct virt_dma_desc *vdesc; struct jz4740_dma_sg *sg;
- jz4740_dma_disable(chan->jz_chan); + jz4740_dma_write_mask(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), 0, + JZ_DMA_STATUS_CTRL_ENABLE);
if (!chan->desc) { vdesc = vchan_next_desc(&chan->vchan); @@ -196,22 +335,27 @@ static int jz4740_dma_start_transfer(struct jz4740_dmaengine_chan *chan) src_addr = chan->fifo_addr; dst_addr = sg->addr; } - jz4740_dma_set_src_addr(chan->jz_chan, src_addr); - jz4740_dma_set_dst_addr(chan->jz_chan, dst_addr); - jz4740_dma_set_transfer_count(chan->jz_chan, sg->len); + jz4740_dma_write(dmadev, JZ_REG_DMA_SRC_ADDR(chan->id), src_addr); + jz4740_dma_write(dmadev, JZ_REG_DMA_DST_ADDR(chan->id), dst_addr); + jz4740_dma_write(dmadev, JZ_REG_DMA_TRANSFER_COUNT(chan->id), + sg->len >> chan->transfer_shift);
chan->next_sg++;
- jz4740_dma_enable(chan->jz_chan); + jz4740_dma_write_mask(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), + JZ_DMA_STATUS_CTRL_NO_DESC | JZ_DMA_STATUS_CTRL_ENABLE, + JZ_DMA_STATUS_CTRL_HALT | JZ_DMA_STATUS_CTRL_NO_DESC | + JZ_DMA_STATUS_CTRL_ENABLE); + + jz4740_dma_write_mask(dmadev, JZ_REG_DMA_CTRL, + JZ_DMA_CTRL_ENABLE, + JZ_DMA_CTRL_HALT | JZ_DMA_CTRL_ENABLE);
return 0; }
-static void jz4740_dma_complete_cb(struct jz4740_dma_chan *jz_chan, int error, - void *devid) +static void jz4740_dma_chan_irq(struct jz4740_dmaengine_chan *chan) { - struct jz4740_dmaengine_chan *chan = devid; - spin_lock(&chan->vchan.lock); if (chan->desc) { if (chan->desc && chan->desc->cyclic) { @@ -227,6 +371,28 @@ static void jz4740_dma_complete_cb(struct jz4740_dma_chan *jz_chan, int error, spin_unlock(&chan->vchan.lock); }
+static irqreturn_t jz4740_dma_irq(int irq, void *devid) +{ + struct jz4740_dma_dev *dmadev = devid; + uint32_t irq_status; + unsigned int i; + + irq_status = readl(dmadev->base + JZ_REG_DMA_IRQ); + + for (i = 0; i < 6; ++i) { + if (irq_status & (1 << i)) { + jz4740_dma_write_mask(dmadev, + JZ_REG_DMA_STATUS_CTRL(i), 0, + JZ_DMA_STATUS_CTRL_ENABLE | + JZ_DMA_STATUS_CTRL_TRANSFER_DONE); + + jz4740_dma_chan_irq(&dmadev->chan[i]); + } + } + + return IRQ_HANDLED; +} + static void jz4740_dma_issue_pending(struct dma_chan *c) { struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); @@ -298,7 +464,8 @@ static struct dma_async_tx_descriptor *jz4740_dma_prep_dma_cyclic( static size_t jz4740_dma_desc_residue(struct jz4740_dmaengine_chan *chan, struct jz4740_dma_desc *desc, unsigned int next_sg) { - size_t residue = 0; + struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan); + unsigned int residue, count; unsigned int i;
residue = 0; @@ -306,8 +473,11 @@ static size_t jz4740_dma_desc_residue(struct jz4740_dmaengine_chan *chan, for (i = next_sg; i < desc->num_sgs; i++) residue += desc->sg[i].len;
- if (next_sg != 0) - residue += jz4740_dma_get_residue(chan->jz_chan); + if (next_sg != 0) { + count = jz4740_dma_read(dmadev, + JZ_REG_DMA_TRANSFER_COUNT(chan->id)); + residue += count << chan->transfer_shift; + }
return residue; } @@ -342,24 +512,12 @@ static enum dma_status jz4740_dma_tx_status(struct dma_chan *c,
static int jz4740_dma_alloc_chan_resources(struct dma_chan *c) { - struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); - - chan->jz_chan = jz4740_dma_request(chan, NULL); - if (!chan->jz_chan) - return -EBUSY; - - jz4740_dma_set_complete_cb(chan->jz_chan, jz4740_dma_complete_cb); - return 0; }
static void jz4740_dma_free_chan_resources(struct dma_chan *c) { - struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); - - vchan_free_chan_resources(&chan->vchan); - jz4740_dma_free(chan->jz_chan); - chan->jz_chan = NULL; + vchan_free_chan_resources(to_virt_chan(c)); }
static void jz4740_dma_desc_free(struct virt_dma_desc *vdesc) @@ -373,7 +531,9 @@ static int jz4740_dma_probe(struct platform_device *pdev) struct jz4740_dma_dev *dmadev; struct dma_device *dd; unsigned int i; + struct resource *res; int ret; + int irq;
dmadev = devm_kzalloc(&pdev->dev, sizeof(*dmadev), GFP_KERNEL); if (!dmadev) @@ -381,6 +541,17 @@ static int jz4740_dma_probe(struct platform_device *pdev)
dd = &dmadev->ddev;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dmadev->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dmadev->base)) + return PTR_ERR(dmadev->base); + + dmadev->clk = clk_get(&pdev->dev, "dma"); + if (IS_ERR(dmadev->clk)) + return PTR_ERR(dmadev->clk); + + clk_prepare_enable(dmadev->clk); + dma_cap_set(DMA_SLAVE, dd->cap_mask); dma_cap_set(DMA_CYCLIC, dd->cap_mask); dd->device_alloc_chan_resources = jz4740_dma_alloc_chan_resources; @@ -396,6 +567,7 @@ static int jz4740_dma_probe(struct platform_device *pdev)
for (i = 0; i < dd->chancnt; i++) { chan = &dmadev->chan[i]; + chan->id = i; chan->vchan.desc_free = jz4740_dma_desc_free; vchan_init(&chan->vchan, dd); } @@ -404,16 +576,28 @@ static int jz4740_dma_probe(struct platform_device *pdev) if (ret) return ret;
+ irq = platform_get_irq(pdev, 0); + ret = request_irq(irq, jz4740_dma_irq, 0, dev_name(&pdev->dev), dmadev); + if (ret) + goto err_unregister; + platform_set_drvdata(pdev, dmadev);
return 0; + +err_unregister: + dma_async_device_unregister(dd); + return ret; }
static int jz4740_dma_remove(struct platform_device *pdev) { struct jz4740_dma_dev *dmadev = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0);
+ free_irq(irq, dmadev); dma_async_device_unregister(&dmadev->ddev); + clk_disable_unprepare(dmadev->clk);
return 0; }
On Thu, May 30, 2013 at 06:25:05PM +0200, Lars-Peter Clausen wrote:
Now that all users of the custom jz4740 DMA API have been converted to use the dmaengine API instead we can remove the custom API and move all the code talking to the hardware to the dmaengine driver.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de Acked-by: Ralf Baechle ralf@linux-mips.org
No changes since v1
arch/mips/include/asm/mach-jz4740/dma.h | 56 ------ arch/mips/jz4740/Makefile | 2 +- arch/mips/jz4740/dma.c | 307 -------------------------------- drivers/dma/dma-jz4740.c | 258 +++++++++++++++++++++++---- 4 files changed, 222 insertions(+), 401 deletions(-) delete mode 100644 arch/mips/jz4740/dma.c
only dma.c, you should remove the dma.h or relocate it to linux/
rest of the series looks fine, and once we have acks from repsective subsystem mainatiners, we should be good to merge
-- ~Vinod
On 05/30/2013 07:20 PM, Vinod Koul wrote:
On Thu, May 30, 2013 at 06:25:05PM +0200, Lars-Peter Clausen wrote:
Now that all users of the custom jz4740 DMA API have been converted to use the dmaengine API instead we can remove the custom API and move all the code talking to the hardware to the dmaengine driver.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de Acked-by: Ralf Baechle ralf@linux-mips.org
No changes since v1
arch/mips/include/asm/mach-jz4740/dma.h | 56 ------ arch/mips/jz4740/Makefile | 2 +- arch/mips/jz4740/dma.c | 307 -------------------------------- drivers/dma/dma-jz4740.c | 258 +++++++++++++++++++++++---- 4 files changed, 222 insertions(+), 401 deletions(-) delete mode 100644 arch/mips/jz4740/dma.c
only dma.c, you should remove the dma.h or relocate it to linux/
As I said the header only contains the slave ids at this point and the dmaengine driver doesn't need it anymore. So I should remove the #include from the driver as well.
rest of the series looks fine, and once we have acks from repsective subsystem mainatiners, we should be good to merge
We already have acks from the maintainers.
- Lars
On Thu, May 30, 2013 at 06:24:59PM +0200, Lars-Peter Clausen wrote:
Hi,
This series replaces the custom JZ4740 DMA API with a dmaengine driver. This is done in 3 steps:
- Add a dmaengine driver which wraps the custom JZ4740 DMA API
- Update all users of the JZ4740 DMA API to use dmaengine instead
- Remove the custom API and move all direct hardware access to the dmaengine driver.
The first two patches in the series also make sure that the clock of the DMA core is enabled.
Since the patches in this series depend on each other I'd prefer if they could all go through the DMA tree.
There are a few minor changes to the patch 3 in v2 of this series, all other patches are identical to v1.
Applied all expect the "ASoC: jz4740: Use the generic dmaengine PCM driver". Can you rebase that on slave dma-next and resend
Fixed up struct alignment manually in 4th one
-- ~Vinod
- Lars
Lars-Peter Clausen (4): dma: Add a jz4740 dmaengine driver MIPS: jz4740: Register jz4740 DMA device ASoC: jz4740: Use the generic dmaengine PCM driver MIPS: jz4740: Remove custom DMA API
Maarten ter Huurne (2): MIPS: jz4740: Correct clock gate bit for DMA controller MIPS: jz4740: Acquire and enable DMA controller clock
arch/mips/include/asm/mach-jz4740/dma.h | 56 --- arch/mips/include/asm/mach-jz4740/platform.h | 1 + arch/mips/jz4740/Makefile | 2 +- arch/mips/jz4740/board-qi_lb60.c | 1 + arch/mips/jz4740/clock.c | 2 +- arch/mips/jz4740/dma.c | 287 ------------- arch/mips/jz4740/platform.c | 21 + drivers/dma/Kconfig | 6 + drivers/dma/Makefile | 1 + drivers/dma/dma-jz4740.c | 617 +++++++++++++++++++++++++++ sound/soc/jz4740/Kconfig | 1 + sound/soc/jz4740/jz4740-i2s.c | 48 +-- sound/soc/jz4740/jz4740-pcm.c | 310 +------------- sound/soc/jz4740/jz4740-pcm.h | 20 - 14 files changed, 676 insertions(+), 697 deletions(-) delete mode 100644 arch/mips/jz4740/dma.c create mode 100644 drivers/dma/dma-jz4740.c delete mode 100644 sound/soc/jz4740/jz4740-pcm.h
-- 1.8.2.1
--
participants (3)
-
Lars-Peter Clausen
-
Sergei Shtylyov
-
Vinod Koul