* [PATCH V2 01/12] DMA: PL330: Add support runtime PM for PL330 DMAC
2011-07-13 8:47 [PATCH V2 00/12] To use DMA generic APIs for Samsung DMA Kukjin Kim
@ 2011-07-13 8:47 ` Kukjin Kim
2011-07-13 9:07 ` Russell King - ARM Linux
2011-07-13 8:47 ` [PATCH V2 02/12] DMA: PL330: Update PL330 DMA API driver Kukjin Kim
` (10 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Kukjin Kim @ 2011-07-13 8:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Boojin Kim <boojin.kim@samsung.com>
Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
Cc: Vinod Koul <vinod.koul@intel.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
drivers/dma/pl330.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 75 insertions(+), 3 deletions(-)
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 6abe1ec..39e9ffd 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -17,6 +17,7 @@
#include <linux/interrupt.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl330.h>
+#include <linux/pm_runtime.h>
#define NR_DEFAULT_DESC 16
@@ -83,6 +84,8 @@ struct dma_pl330_dmac {
/* Peripheral channels connected to this DMAC */
struct dma_pl330_chan peripherals[0]; /* keep at end */
+
+ struct clk *clk;
};
struct dma_pl330_desc {
@@ -688,7 +691,8 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
pi->mcbufsz = pdat->mcbuf_sz;
res = &adev->res;
- request_mem_region(res->start, resource_size(res), "dma-pl330");
+ request_mem_region(res->start,
+ resource_size(res), dev_name(&adev->dev));
pi->base = ioremap(res->start, resource_size(res));
if (!pi->base) {
@@ -696,6 +700,30 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
goto probe_err1;
}
+ pdmac->clk = clk_get(&adev->dev, "dma");
+ if (IS_ERR(pdmac->clk)) {
+ dev_err(&adev->dev, "Cannot get operation clock.\n");
+ ret = -EINVAL;
+ goto probe_err1;
+ }
+
+ amba_set_drvdata(adev, pdmac);
+
+#ifdef CONFIG_PM_RUNTIME
+ /* to use the runtime PM helper functions */
+ pm_runtime_enable(&adev->dev);
+
+ /* enable the power domain */
+ if (pm_runtime_get_sync(&adev->dev)) {
+ dev_err(&adev->dev, "failed to get runtime pm\n");
+ ret = -ENODEV;
+ goto probe_err1;
+ }
+#else
+ /* enable dma clk */
+ clk_enable(pdmac->clk);
+#endif
+
irq = adev->irq[0];
ret = request_irq(irq, pl330_irq_handler, 0,
dev_name(&adev->dev), pi);
@@ -763,8 +791,6 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
goto probe_err4;
}
- amba_set_drvdata(adev, pdmac);
-
dev_info(&adev->dev,
"Loaded driver for PL330 DMAC-%d\n", adev->periphid);
dev_info(&adev->dev,
@@ -825,6 +851,13 @@ static int __devexit pl330_remove(struct amba_device *adev)
res = &adev->res;
release_mem_region(res->start, resource_size(res));
+#ifdef CONFIG_PM_RUNTIME
+ pm_runtime_put(&adev->dev);
+ pm_runtime_disable(&adev->dev);
+#else
+ clk_disable(pdmac->clk);
+#endif
+
kfree(pdmac);
return 0;
@@ -838,10 +871,49 @@ static struct amba_id pl330_ids[] = {
{ 0, 0 },
};
+#ifdef CONFIG_PM_RUNTIME
+static int pl330_runtime_suspend(struct device *dev)
+{
+ struct dma_pl330_dmac *pdmac = dev_get_drvdata(dev);
+
+ if (!pdmac) {
+ dev_err(dev, "failed to get dmac\n");
+ return -ENODEV;
+ }
+
+ clk_disable(pdmac->clk);
+
+ return 0;
+}
+
+static int pl330_runtime_resume(struct device *dev)
+{
+ struct dma_pl330_dmac *pdmac = dev_get_drvdata(dev);
+
+ if (!pdmac) {
+ dev_err(dev, "failed to get dmac\n");
+ return -ENODEV;
+ }
+
+ clk_enable(pdmac->clk);
+
+ return 0;
+}
+#else
+#define pl330_runtime_suspend NULL
+#define pl330_runtime_resume NULL
+#endif /* CONFIG_PM_RUNTIME */
+
+static const struct dev_pm_ops pl330_pm_ops = {
+ .runtime_suspend = pl330_runtime_suspend,
+ .runtime_resume = pl330_runtime_resume,
+};
+
static struct amba_driver pl330_driver = {
.drv = {
.owner = THIS_MODULE,
.name = "dma-pl330",
+ .pm = &pl330_pm_ops,
},
.id_table = pl330_ids,
.probe = pl330_probe,
--
1.7.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH V2 01/12] DMA: PL330: Add support runtime PM for PL330 DMAC
2011-07-13 8:47 ` [PATCH V2 01/12] DMA: PL330: Add support runtime PM for PL330 DMAC Kukjin Kim
@ 2011-07-13 9:07 ` Russell King - ARM Linux
0 siblings, 0 replies; 23+ messages in thread
From: Russell King - ARM Linux @ 2011-07-13 9:07 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, Jul 13, 2011 at 05:47:26PM +0900, Kukjin Kim wrote:
> @@ -688,7 +691,8 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
> pi->mcbufsz = pdat->mcbuf_sz;
>
> res = &adev->res;
> - request_mem_region(res->start, resource_size(res), "dma-pl330");
> + request_mem_region(res->start,
> + resource_size(res), dev_name(&adev->dev));
The convention that we've been using is:
10004000-10004fff : mb:aaci
10004000-10004fff : aaci-pl041
10005000-10005fff : mb:mmci
10005000-10005fff : mmci-pl18x
10006000-10006fff : mb:kmi0
10006000-10006fff : kmi-pl050
10007000-10007fff : mb:kmi1
10007000-10007fff : kmi-pl050
10009000-10009fff : mb:uart0
10009000-10009fff : uart-pl011
1000a000-1000afff : mb:uart1
1000a000-1000afff : uart-pl011
1000b000-1000bfff : mb:uart2
1000b000-1000bfff : uart-pl011
1000c000-1000cfff : mb:uart3
1000c000-1000cfff : uart-pl011
The parent resource uses the device name. The child resource is the
driver name. That (a) avoids useless duplication of data in /proc/iomem
and (b) is more informative when you look at /proc/iomem about what's
where.
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH V2 02/12] DMA: PL330: Update PL330 DMA API driver
2011-07-13 8:47 [PATCH V2 00/12] To use DMA generic APIs for Samsung DMA Kukjin Kim
2011-07-13 8:47 ` [PATCH V2 01/12] DMA: PL330: Add support runtime PM for PL330 DMAC Kukjin Kim
@ 2011-07-13 8:47 ` Kukjin Kim
2011-07-13 8:47 ` [PATCH V2 03/12] DMA: PL330: Add DMA capabilities Kukjin Kim
` (9 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Kukjin Kim @ 2011-07-13 8:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Boojin Kim <boojin.kim@samsung.com>
This patch updates following 3 items.
1. Removes unneccessary code.
2. Add AMBA, PL330 configuration
3. Change the meaning of 'peri_id' variable
from PL330 event number to specific dma id by user.
Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
Cc: Vinod Koul <vinod.koul@intel.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
drivers/dma/Kconfig | 3 ++-
drivers/dma/pl330.c | 31 ++++++++++++++++---------------
include/linux/amba/pl330.h | 2 +-
3 files changed, 19 insertions(+), 17 deletions(-)
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 25cf327..569cb14 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -193,7 +193,8 @@ config ARCH_HAS_ASYNC_TX_FIND_CHANNEL
config PL330_DMA
tristate "DMA API Driver for PL330"
select DMA_ENGINE
- depends on PL330
+ depends on ARM_AMBA
+ select PL330
help
Select if your platform has one or more PL330 DMACs.
You need to provide platform specific settings via
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 39e9ffd..9bdda7b 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -455,7 +455,7 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch)
async_tx_ack(&desc->txd);
desc->req.rqtype = peri->rqtype;
- desc->req.peri = peri->peri_id;
+ desc->req.peri = pch->chan.chan_id;
dma_async_tx_descriptor_init(&desc->txd, &pch->chan);
@@ -577,7 +577,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
struct dma_pl330_peri *peri = chan->private;
struct scatterlist *sg;
unsigned long flags;
- int i, burst_size;
+ int i;
dma_addr_t addr;
if (unlikely(!pch || !sgl || !sg_len))
@@ -594,7 +594,6 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
}
addr = peri->fifo_addr;
- burst_size = peri->burst_sz;
first = NULL;
@@ -642,7 +641,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
sg_dma_address(sg), addr, sg_dma_len(sg));
}
- desc->rqcfg.brst_size = burst_size;
+ desc->rqcfg.brst_size = peri->burst_sz;
desc->rqcfg.brst_len = 1;
}
@@ -749,17 +748,19 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
struct dma_pl330_peri *peri = &pdat->peri[i];
pch = &pdmac->peripherals[i];
- switch (peri->rqtype) {
- case MEMTOMEM:
- dma_cap_set(DMA_MEMCPY, pd->cap_mask);
- break;
- case MEMTODEV:
- case DEVTOMEM:
- dma_cap_set(DMA_SLAVE, pd->cap_mask);
- break;
- default:
- dev_err(&adev->dev, "DEVTODEV Not Supported\n");
- continue;
+ if (peri) {
+ switch (peri->rqtype) {
+ case MEMTOMEM:
+ dma_cap_set(DMA_MEMCPY, pd->cap_mask);
+ break;
+ case MEMTODEV:
+ case DEVTOMEM:
+ dma_cap_set(DMA_SLAVE, pd->cap_mask);
+ break;
+ default:
+ dev_err(&adev->dev, "DEVTODEV Not Supported\n");
+ continue;
+ }
}
INIT_LIST_HEAD(&pch->work_list);
diff --git a/include/linux/amba/pl330.h b/include/linux/amba/pl330.h
index cbee7de..17b0ada 100644
--- a/include/linux/amba/pl330.h
+++ b/include/linux/amba/pl330.h
@@ -19,7 +19,7 @@ struct dma_pl330_peri {
* Peri_Req i/f of the DMAC that is
* peripheral could be reached from.
*/
- u8 peri_id; /* {0, 31} */
+ u8 peri_id; /* specific dma id */
enum pl330_reqtype rqtype;
/* For M->D and D->M Channels */
--
1.7.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH V2 03/12] DMA: PL330: Add DMA capabilities
2011-07-13 8:47 [PATCH V2 00/12] To use DMA generic APIs for Samsung DMA Kukjin Kim
2011-07-13 8:47 ` [PATCH V2 01/12] DMA: PL330: Add support runtime PM for PL330 DMAC Kukjin Kim
2011-07-13 8:47 ` [PATCH V2 02/12] DMA: PL330: Update PL330 DMA API driver Kukjin Kim
@ 2011-07-13 8:47 ` Kukjin Kim
2011-07-13 9:14 ` Russell King - ARM Linux
2011-07-15 4:45 ` Chanho Park
2011-07-13 8:47 ` [PATCH V2 04/12] ARM: SAMSUNG: Update to use PL330-DMA driver Kukjin Kim
` (8 subsequent siblings)
11 siblings, 2 replies; 23+ messages in thread
From: Kukjin Kim @ 2011-07-13 8:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Boojin Kim <boojin.kim@samsung.com>
This patch adds DMA_CYCLIC capability that is used for audio driver
and SLAVE_CONFIG capability for transmit between device and memory.
Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
Cc: Vinod Koul <vinod.koul@intel.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
drivers/dma/pl330.c | 187 +++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 173 insertions(+), 14 deletions(-)
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 9bdda7b..2162ac5 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -69,6 +69,9 @@ struct dma_pl330_chan {
* NULL if the channel is available to be acquired.
*/
void *pl330_chid;
+
+ /* taks for cyclic capability */
+ struct tasklet_struct *cyclic_task;
};
struct dma_pl330_dmac {
@@ -105,6 +108,7 @@ struct dma_pl330_desc {
/* The channel which currently holds this desc */
struct dma_pl330_chan *pchan;
+ bool cyclic;
};
static inline struct dma_pl330_chan *
@@ -184,6 +188,60 @@ static inline void fill_queue(struct dma_pl330_chan *pch)
}
}
+static void pl330_tasklet_cyclic(unsigned long data)
+{
+ struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data;
+ struct dma_pl330_desc *desc, *_dt;
+ unsigned long flags;
+ LIST_HEAD(list);
+
+ spin_lock_irqsave(&pch->lock, flags);
+
+ /* Pick up ripe tomatoes */
+ list_for_each_entry_safe(desc, _dt, &pch->work_list, node)
+ if ((desc->status == DONE) && desc->cyclic) {
+ dma_async_tx_callback callback;
+
+ list_move_tail(&desc->node, &pch->work_list);
+ pch->completed = desc->txd.cookie;
+
+ desc->status = PREP;
+
+ /* Try to submit a req imm.
+ next to the last completed cookie */
+ fill_queue(pch);
+
+ /* Make sure the PL330 Channel thread is active */
+ pl330_chan_ctrl(pch->pl330_chid, PL330_OP_START);
+
+ callback = desc->txd.callback;
+ if (callback)
+ callback(desc->txd.callback_param);
+
+ }
+
+ spin_unlock_irqrestore(&pch->lock, flags);
+}
+
+static void pl330_cyclic_free(struct dma_pl330_chan *pch)
+{
+ struct dma_pl330_dmac *pdmac = pch->dmac;
+ struct dma_pl330_desc *desc, *_dt;
+ unsigned long flags;
+ LIST_HEAD(list);
+
+ spin_lock_irqsave(&pdmac->pool_lock, flags);
+
+ list_for_each_entry_safe(desc, _dt, &pch->work_list, node)
+ if (desc->cyclic)
+ list_move_tail(&desc->node, &list);
+
+ list_splice_tail_init(&list, &pdmac->desc_pool);
+
+ spin_unlock_irqrestore(&pdmac->pool_lock, flags);
+ pch->cyclic_task = NULL;
+}
+
static void pl330_tasklet(unsigned long data)
{
struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data;
@@ -227,6 +285,9 @@ static void dma_pl330_rqcb(void *token, enum pl330_op_err err)
spin_unlock_irqrestore(&pch->lock, flags);
+ if (pch->cyclic_task)
+ tasklet_schedule(pch->cyclic_task);
+ else
tasklet_schedule(&pch->task);
}
@@ -256,25 +317,58 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg)
{
struct dma_pl330_chan *pch = to_pchan(chan);
- struct dma_pl330_desc *desc;
+ struct dma_pl330_desc *desc, *_dt;
unsigned long flags;
+ struct dma_pl330_dmac *pdmac = pch->dmac;
+ struct dma_slave_config *slave_config;
+ struct dma_pl330_peri *peri;
+ int i;
+ LIST_HEAD(list);
- /* Only supports DMA_TERMINATE_ALL */
- if (cmd != DMA_TERMINATE_ALL)
- return -ENXIO;
-
- spin_lock_irqsave(&pch->lock, flags);
-
- /* FLUSH the PL330 Channel thread */
- pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);
+ switch (cmd) {
+ case DMA_TERMINATE_ALL:
+ spin_lock_irqsave(&pch->lock, flags);
- /* Mark all desc done */
- list_for_each_entry(desc, &pch->work_list, node)
- desc->status = DONE;
+ /* FLUSH the PL330 Channel thread */
+ pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);
- spin_unlock_irqrestore(&pch->lock, flags);
+ /* Mark all desc done */
+ list_for_each_entry_safe(desc, _dt, &pch->work_list , node) {
+ desc->status = DONE;
+ pch->completed = desc->txd.cookie;
+ list_move_tail(&desc->node, &list);
+ }
- pl330_tasklet((unsigned long) pch);
+ list_splice_tail_init(&list, &pdmac->desc_pool);
+ spin_unlock_irqrestore(&pch->lock, flags);
+ break;
+ case DMA_SLAVE_CONFIG:
+ slave_config = (struct dma_slave_config *)arg;
+ peri = pch->chan.private;
+
+ if (slave_config->direction == DMA_TO_DEVICE) {
+ if (slave_config->dst_addr)
+ peri->fifo_addr = slave_config->dst_addr;
+ if (slave_config->dst_addr_width) {
+ i = 0;
+ while (slave_config->dst_addr_width != (1 << i))
+ i++;
+ peri->burst_sz = i;
+ }
+ } else if (slave_config->direction == DMA_FROM_DEVICE) {
+ if (slave_config->src_addr)
+ peri->fifo_addr = slave_config->src_addr;
+ if (slave_config->src_addr_width) {
+ i = 0;
+ while (slave_config->src_addr_width != (1 << i))
+ i++;
+ peri->burst_sz = i;
+ }
+ }
+ break;
+ default:
+ return -ENXIO;
+ }
return 0;
}
@@ -291,6 +385,9 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
pl330_release_channel(pch->pl330_chid);
pch->pl330_chid = NULL;
+ if (pch->cyclic_task)
+ pl330_cyclic_free(pch);
+
spin_unlock_irqrestore(&pch->lock, flags);
}
@@ -522,6 +619,66 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len)
return burst_len;
}
+static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
+ struct dma_chan *chan, dma_addr_t dma_addr, size_t len,
+ size_t period_len, enum dma_data_direction direction)
+{
+ struct dma_pl330_desc *desc;
+ struct dma_pl330_chan *pch = to_pchan(chan);
+ struct dma_pl330_peri *peri = chan->private;
+ dma_addr_t dst;
+ dma_addr_t src;
+
+ pch = to_pchan(chan);
+ if (!pch) {
+ dev_err(pch->dmac->pif.dev, "%s:%d Unable to fetch desc\n",
+ __func__, __LINE__);
+ return NULL;
+ }
+
+ desc = pl330_get_desc(pch);
+ if (!desc) {
+ dev_err(pch->dmac->pif.dev, "%s:%d Unable to fetch desc\n",
+ __func__, __LINE__);
+ return NULL;
+ }
+
+ switch (direction) {
+ case DMA_TO_DEVICE:
+ desc->rqcfg.src_inc = 1;
+ desc->rqcfg.dst_inc = 0;
+ src = dma_addr;
+ dst = peri->fifo_addr;
+ break;
+ case DMA_FROM_DEVICE:
+ desc->rqcfg.src_inc = 0;
+ desc->rqcfg.dst_inc = 1;
+ src = peri->fifo_addr;
+ dst = dma_addr;
+ break;
+ default:
+ dev_err(pch->dmac->pif.dev, "%s:%d Invalid dma direction\n",
+ __func__, __LINE__);
+ return NULL;
+ }
+
+ desc->rqcfg.brst_size = peri->burst_sz;
+ desc->rqcfg.brst_len = 1;
+
+ if (!pch->cyclic_task) {
+ pch->cyclic_task =
+ kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL);
+ tasklet_init(pch->cyclic_task,
+ pl330_tasklet_cyclic, (unsigned int)pch);
+ }
+
+ desc->cyclic = true;
+
+ fill_px(&desc->px, dst, src, period_len);
+
+ return &desc->txd;
+}
+
static struct dma_async_tx_descriptor *
pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
dma_addr_t src, size_t len, unsigned long flags)
@@ -756,6 +913,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
case MEMTODEV:
case DEVTOMEM:
dma_cap_set(DMA_SLAVE, pd->cap_mask);
+ dma_cap_set(DMA_CYCLIC, pd->cap_mask);
break;
default:
dev_err(&adev->dev, "DEVTODEV Not Supported\n");
@@ -781,6 +939,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
pd->device_alloc_chan_resources = pl330_alloc_chan_resources;
pd->device_free_chan_resources = pl330_free_chan_resources;
pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy;
+ pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic;
pd->device_tx_status = pl330_tx_status;
pd->device_prep_slave_sg = pl330_prep_slave_sg;
pd->device_control = pl330_control;
--
1.7.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH V2 03/12] DMA: PL330: Add DMA capabilities
2011-07-13 8:47 ` [PATCH V2 03/12] DMA: PL330: Add DMA capabilities Kukjin Kim
@ 2011-07-13 9:14 ` Russell King - ARM Linux
2011-07-13 11:04 ` boojin
2011-07-15 4:45 ` Chanho Park
1 sibling, 1 reply; 23+ messages in thread
From: Russell King - ARM Linux @ 2011-07-13 9:14 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, Jul 13, 2011 at 05:47:28PM +0900, Kukjin Kim wrote:
> +static void pl330_tasklet_cyclic(unsigned long data)
> +{
> + struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data;
> + struct dma_pl330_desc *desc, *_dt;
> + unsigned long flags;
> + LIST_HEAD(list);
> +
> + spin_lock_irqsave(&pch->lock, flags);
> +
> + /* Pick up ripe tomatoes */
> + list_for_each_entry_safe(desc, _dt, &pch->work_list, node)
> + if ((desc->status == DONE) && desc->cyclic) {
> + dma_async_tx_callback callback;
> +
> + list_move_tail(&desc->node, &pch->work_list);
> + pch->completed = desc->txd.cookie;
> +
> + desc->status = PREP;
> +
> + /* Try to submit a req imm.
> + next to the last completed cookie */
> + fill_queue(pch);
> +
> + /* Make sure the PL330 Channel thread is active */
> + pl330_chan_ctrl(pch->pl330_chid, PL330_OP_START);
> +
> + callback = desc->txd.callback;
> + if (callback)
> + callback(desc->txd.callback_param);
How does this work when callbacks are allowed to queue new requests?
Aren't you going to deadlock on the spinlock?
I don't see 'list' being used in this function either.
> +
> + }
> +
> + spin_unlock_irqrestore(&pch->lock, flags);
> +}
> +
> +static void pl330_cyclic_free(struct dma_pl330_chan *pch)
> +{
> + struct dma_pl330_dmac *pdmac = pch->dmac;
> + struct dma_pl330_desc *desc, *_dt;
> + unsigned long flags;
> + LIST_HEAD(list);
> +
> + spin_lock_irqsave(&pdmac->pool_lock, flags);
> +
> + list_for_each_entry_safe(desc, _dt, &pch->work_list, node)
> + if (desc->cyclic)
> + list_move_tail(&desc->node, &list);
> +
> + list_splice_tail_init(&list, &pdmac->desc_pool);
As you're not using 'list' after this point, would 'list_splice_tail'
do here?
> +
> + spin_unlock_irqrestore(&pdmac->pool_lock, flags);
> + pch->cyclic_task = NULL;
> +}
> +
> static void pl330_tasklet(unsigned long data)
> {
> struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data;
> @@ -227,6 +285,9 @@ static void dma_pl330_rqcb(void *token, enum pl330_op_err err)
>
> spin_unlock_irqrestore(&pch->lock, flags);
>
> + if (pch->cyclic_task)
> + tasklet_schedule(pch->cyclic_task);
> + else
> tasklet_schedule(&pch->task);
This 'tasklet_schedule' wants to be indented.
> }
>
> @@ -256,25 +317,58 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
> static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg)
> {
> struct dma_pl330_chan *pch = to_pchan(chan);
> - struct dma_pl330_desc *desc;
> + struct dma_pl330_desc *desc, *_dt;
> unsigned long flags;
> + struct dma_pl330_dmac *pdmac = pch->dmac;
> + struct dma_slave_config *slave_config;
> + struct dma_pl330_peri *peri;
> + int i;
> + LIST_HEAD(list);
>
> - /* Only supports DMA_TERMINATE_ALL */
> - if (cmd != DMA_TERMINATE_ALL)
> - return -ENXIO;
> -
> - spin_lock_irqsave(&pch->lock, flags);
> -
> - /* FLUSH the PL330 Channel thread */
> - pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);
> + switch (cmd) {
> + case DMA_TERMINATE_ALL:
> + spin_lock_irqsave(&pch->lock, flags);
>
> - /* Mark all desc done */
> - list_for_each_entry(desc, &pch->work_list, node)
> - desc->status = DONE;
> + /* FLUSH the PL330 Channel thread */
> + pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);
>
> - spin_unlock_irqrestore(&pch->lock, flags);
> + /* Mark all desc done */
> + list_for_each_entry_safe(desc, _dt, &pch->work_list , node) {
> + desc->status = DONE;
> + pch->completed = desc->txd.cookie;
> + list_move_tail(&desc->node, &list);
> + }
>
> - pl330_tasklet((unsigned long) pch);
> + list_splice_tail_init(&list, &pdmac->desc_pool);
Again, would list_splice_tail() do here?
> + spin_unlock_irqrestore(&pch->lock, flags);
> + break;
> + case DMA_SLAVE_CONFIG:
> + slave_config = (struct dma_slave_config *)arg;
> + peri = pch->chan.private;
> +
> + if (slave_config->direction == DMA_TO_DEVICE) {
> + if (slave_config->dst_addr)
> + peri->fifo_addr = slave_config->dst_addr;
> + if (slave_config->dst_addr_width) {
> + i = 0;
> + while (slave_config->dst_addr_width != (1 << i))
> + i++;
> + peri->burst_sz = i;
> + }
> + } else if (slave_config->direction == DMA_FROM_DEVICE) {
> + if (slave_config->src_addr)
> + peri->fifo_addr = slave_config->src_addr;
> + if (slave_config->src_addr_width) {
> + i = 0;
> + while (slave_config->src_addr_width != (1 << i))
> + i++;
> + peri->burst_sz = i;
> + }
> + }
It would make more sense to store the M2P and P2M address/width/burst size
separately, so you don't have to make DMA_SLAVE_CONFIG calls before every
transfer.
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH V2 03/12] DMA: PL330: Add DMA capabilities
2011-07-13 9:14 ` Russell King - ARM Linux
@ 2011-07-13 11:04 ` boojin
0 siblings, 0 replies; 23+ messages in thread
From: boojin @ 2011-07-13 11:04 UTC (permalink / raw)
To: linux-arm-kernel
This patch adds DMA cyclic capability and Slave configuration.
DMA cyclic capability is only used for audio circular buffer.
Russell king wrote:
> On Wed, Jul 13, 2011 at 05:47:28PM +0900, Kukjin Kim wrote:
> > +static void pl330_tasklet_cyclic(unsigned long data)
> > +{
> > + struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data;
> > + struct dma_pl330_desc *desc, *_dt;
> > + unsigned long flags;
> > + LIST_HEAD(list);
> > +
> > + spin_lock_irqsave(&pch->lock, flags);
> > +
> > + /* Pick up ripe tomatoes */
> > + list_for_each_entry_safe(desc, _dt, &pch->work_list, node)
> > + if ((desc->status == DONE) && desc->cyclic) {
> > + dma_async_tx_callback callback;
> > +
> > + list_move_tail(&desc->node, &pch->work_list);
> > + pch->completed = desc->txd.cookie;
> > +
> > + desc->status = PREP;
> > +
> > + /* Try to submit a req imm.
> > + next to the last completed cookie */
> > + fill_queue(pch);
> > +
> > + /* Make sure the PL330 Channel thread is active */
> > + pl330_chan_ctrl(pch->pl330_chid, PL330_OP_START);
> > +
> > + callback = desc->txd.callback;
> > + if (callback)
> > + callback(desc->txd.callback_param);
>
> How does this work when callbacks are allowed to queue new requests?
> Aren't you going to deadlock on the spinlock?
>
> I don't see 'list' being used in this function either.
>
>
Cyclic capability re-uses the requests that were submitted through
tx_submit().
It's possible because Cyclic capability uses same DMA configuration.
This pl330_tasklet_cyclic() makes the status of the done request into
'PREP'.
And, re-submit it to PL330.
There are no deadlock because callback function just gathers transmit
position.
And Cyclic capability doesn't use 'list' for queue new requests.
> > +
> > + }
> > +
> > + spin_unlock_irqrestore(&pch->lock, flags);
> > +}
> > +
> > +static void pl330_cyclic_free(struct dma_pl330_chan *pch)
> > +{
> > + struct dma_pl330_dmac *pdmac = pch->dmac;
> > + struct dma_pl330_desc *desc, *_dt;
> > + unsigned long flags;
> > + LIST_HEAD(list);
> > +
> > + spin_lock_irqsave(&pdmac->pool_lock, flags);
> > +
> > + list_for_each_entry_safe(desc, _dt, &pch->work_list, node)
> > + if (desc->cyclic)
> > + list_move_tail(&desc->node, &list);
> > +
> > + list_splice_tail_init(&list, &pdmac->desc_pool);
>
> As you're not using 'list' after this point, would 'list_splice_tail'
> do here?
Yes, You're right. the 'list' isn't used after device_free_chan_resources().
So, I release it to desc_pool by using 'list_splice_tail'
>
> > +
> > + spin_unlock_irqrestore(&pdmac->pool_lock, flags);
> > + pch->cyclic_task = NULL;
> > +}
> > +
> > static void pl330_tasklet(unsigned long data)
> > {
> > struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data;
> > @@ -227,6 +285,9 @@ static void dma_pl330_rqcb(void *token, enum
> pl330_op_err err)
> >
> > spin_unlock_irqrestore(&pch->lock, flags);
> >
> > + if (pch->cyclic_task)
> > + tasklet_schedule(pch->cyclic_task);
> > + else
> > tasklet_schedule(&pch->task);
>
> This 'tasklet_schedule' wants to be indented.
I will address your guide. Thanks
>
> > }
> >
> > @@ -256,25 +317,58 @@ static int pl330_alloc_chan_resources(struct
> dma_chan *chan)
> > static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
> unsigned long arg)
> > {
> > struct dma_pl330_chan *pch = to_pchan(chan);
> > - struct dma_pl330_desc *desc;
> > + struct dma_pl330_desc *desc, *_dt;
> > unsigned long flags;
> > + struct dma_pl330_dmac *pdmac = pch->dmac;
> > + struct dma_slave_config *slave_config;
> > + struct dma_pl330_peri *peri;
> > + int i;
> > + LIST_HEAD(list);
> >
> > - /* Only supports DMA_TERMINATE_ALL */
> > - if (cmd != DMA_TERMINATE_ALL)
> > - return -ENXIO;
> > -
> > - spin_lock_irqsave(&pch->lock, flags);
> > -
> > - /* FLUSH the PL330 Channel thread */
> > - pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);
> > + switch (cmd) {
> > + case DMA_TERMINATE_ALL:
> > + spin_lock_irqsave(&pch->lock, flags);
> >
> > - /* Mark all desc done */
> > - list_for_each_entry(desc, &pch->work_list, node)
> > - desc->status = DONE;
> > + /* FLUSH the PL330 Channel thread */
> > + pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);
> >
> > - spin_unlock_irqrestore(&pch->lock, flags);
> > + /* Mark all desc done */
> > + list_for_each_entry_safe(desc, _dt, &pch->work_list , node)
> {
> > + desc->status = DONE;
> > + pch->completed = desc->txd.cookie;
> > + list_move_tail(&desc->node, &list);
> > + }
> >
> > - pl330_tasklet((unsigned long) pch);
> > + list_splice_tail_init(&list, &pdmac->desc_pool);
>
> Again, would list_splice_tail() do here?
This code is for 'flush' operation that releases all of the pre-loaded
requests.
I uses 'list_splice_tail_init' for it instead of 'pl330_tasklet' that also
calls 'list_splice_tail_init'.
>
> > + spin_unlock_irqrestore(&pch->lock, flags);
> > + break;
> > + case DMA_SLAVE_CONFIG:
> > + slave_config = (struct dma_slave_config *)arg;
> > + peri = pch->chan.private;
> > +
> > + if (slave_config->direction == DMA_TO_DEVICE) {
> > + if (slave_config->dst_addr)
> > + peri->fifo_addr = slave_config->dst_addr;
> > + if (slave_config->dst_addr_width) {
> > + i = 0;
> > + while (slave_config->dst_addr_width != (1 <<
i))
> > + i++;
> > + peri->burst_sz = i;
> > + }
> > + } else if (slave_config->direction == DMA_FROM_DEVICE) {
> > + if (slave_config->src_addr)
> > + peri->fifo_addr = slave_config->src_addr;
> > + if (slave_config->src_addr_width) {
> > + i = 0;
> > + while (slave_config->src_addr_width != (1 <<
i))
> > + i++;
> > + peri->burst_sz = i;
> > + }
> > + }
>
> It would make more sense to store the M2P and P2M address/width/burst size
> separately, so you don't have to make DMA_SLAVE_CONFIG calls before every
> transfer.
DMA_SLAVE_CONFIG is called the first time that DMA client driver requests
DMA channel.
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH V2 03/12] DMA: PL330: Add DMA capabilities
2011-07-13 8:47 ` [PATCH V2 03/12] DMA: PL330: Add DMA capabilities Kukjin Kim
2011-07-13 9:14 ` Russell King - ARM Linux
@ 2011-07-15 4:45 ` Chanho Park
2011-07-16 1:11 ` Boojin Kim
1 sibling, 1 reply; 23+ messages in thread
From: Chanho Park @ 2011-07-15 4:45 UTC (permalink / raw)
To: linux-arm-kernel
Hello,
2011/7/13 Kukjin Kim <kgene.kim@samsung.com>:
> From: Boojin Kim <boojin.kim@samsung.com>
>
> This patch adds DMA_CYCLIC capability that is used for audio driver
> and SLAVE_CONFIG capability for transmit between device and memory.
>
> Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
> Cc: Vinod Koul <vinod.koul@intel.com>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> ---
> ?drivers/dma/pl330.c | ?187 +++++++++++++++++++++++++++++++++++++++++++++++----
> ?1 files changed, 173 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
> index 9bdda7b..2162ac5 100644
> --- a/drivers/dma/pl330.c
> +++ b/drivers/dma/pl330.c
> @@ -69,6 +69,9 @@ struct dma_pl330_chan {
> ? ? ? ? * NULL if the channel is available to be acquired.
> ? ? ? ? */
> ? ? ? ?void *pl330_chid;
> +
> + ? ? ? /* taks for cyclic capability */
> + ? ? ? struct tasklet_struct *cyclic_task;
> ?};
>
> ?struct dma_pl330_dmac {
> @@ -105,6 +108,7 @@ struct dma_pl330_desc {
>
> ? ? ? ?/* The channel which currently holds this desc */
> ? ? ? ?struct dma_pl330_chan *pchan;
> + ? ? ? bool cyclic;
> ?};
>
> ?static inline struct dma_pl330_chan *
> @@ -184,6 +188,60 @@ static inline void fill_queue(struct dma_pl330_chan *pch)
> ? ? ? ?}
> ?}
>
> +static void pl330_tasklet_cyclic(unsigned long data)
> +{
> + ? ? ? struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data;
> + ? ? ? struct dma_pl330_desc *desc, *_dt;
> + ? ? ? unsigned long flags;
> + ? ? ? LIST_HEAD(list);
> +
> + ? ? ? spin_lock_irqsave(&pch->lock, flags);
> +
> + ? ? ? /* Pick up ripe tomatoes */
> + ? ? ? list_for_each_entry_safe(desc, _dt, &pch->work_list, node)
> + ? ? ? ? ? ? ? if ((desc->status == DONE) && desc->cyclic) {
> + ? ? ? ? ? ? ? ? ? ? ? dma_async_tx_callback callback;
> +
> + ? ? ? ? ? ? ? ? ? ? ? list_move_tail(&desc->node, &pch->work_list);
> + ? ? ? ? ? ? ? ? ? ? ? pch->completed = desc->txd.cookie;
> +
> + ? ? ? ? ? ? ? ? ? ? ? desc->status = PREP;
> +
> + ? ? ? ? ? ? ? ? ? ? ? /* Try to submit a req imm.
> + ? ? ? ? ? ? ? ? ? ? ? next to the last completed cookie */
> + ? ? ? ? ? ? ? ? ? ? ? fill_queue(pch);
> +
> + ? ? ? ? ? ? ? ? ? ? ? /* Make sure the PL330 Channel thread is active */
> + ? ? ? ? ? ? ? ? ? ? ? pl330_chan_ctrl(pch->pl330_chid, PL330_OP_START);
> +
> + ? ? ? ? ? ? ? ? ? ? ? callback = desc->txd.callback;
> + ? ? ? ? ? ? ? ? ? ? ? if (callback)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? callback(desc->txd.callback_param);
> +
> + ? ? ? ? ? ? ? }
> +
> + ? ? ? spin_unlock_irqrestore(&pch->lock, flags);
> +}
> +
> +static void pl330_cyclic_free(struct dma_pl330_chan *pch)
> +{
> + ? ? ? struct dma_pl330_dmac *pdmac = pch->dmac;
> + ? ? ? struct dma_pl330_desc *desc, *_dt;
> + ? ? ? unsigned long flags;
> + ? ? ? LIST_HEAD(list);
> +
> + ? ? ? spin_lock_irqsave(&pdmac->pool_lock, flags);
> +
> + ? ? ? list_for_each_entry_safe(desc, _dt, &pch->work_list, node)
> + ? ? ? if (desc->cyclic)
> + ? ? ? ? ? ? ? list_move_tail(&desc->node, &list);
nitpick: indentation will be required after for_each
> +
> + ? ? ? list_splice_tail_init(&list, &pdmac->desc_pool);
> +
> + ? ? ? spin_unlock_irqrestore(&pdmac->pool_lock, flags);
> + ? ? ? pch->cyclic_task = NULL;
> +}
> +
> ?static void pl330_tasklet(unsigned long data)
> ?{
> ? ? ? ?struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data;
> @@ -227,6 +285,9 @@ static void dma_pl330_rqcb(void *token, enum pl330_op_err err)
>
> ? ? ? ?spin_unlock_irqrestore(&pch->lock, flags);
>
> + ? ? ? if (pch->cyclic_task)
> + ? ? ? ? ? ? ? tasklet_schedule(pch->cyclic_task);
> + ? ? ? else
> ? ? ? ?tasklet_schedule(&pch->task);
> ?}
>
> @@ -256,25 +317,58 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
> ?static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg)
> ?{
> ? ? ? ?struct dma_pl330_chan *pch = to_pchan(chan);
> - ? ? ? struct dma_pl330_desc *desc;
> + ? ? ? struct dma_pl330_desc *desc, *_dt;
> ? ? ? ?unsigned long flags;
> + ? ? ? struct dma_pl330_dmac *pdmac = pch->dmac;
> + ? ? ? struct dma_slave_config *slave_config;
> + ? ? ? struct dma_pl330_peri *peri;
> + ? ? ? int i;
> + ? ? ? LIST_HEAD(list);
>
> - ? ? ? /* Only supports DMA_TERMINATE_ALL */
> - ? ? ? if (cmd != DMA_TERMINATE_ALL)
> - ? ? ? ? ? ? ? return -ENXIO;
> -
> - ? ? ? spin_lock_irqsave(&pch->lock, flags);
> -
> - ? ? ? /* FLUSH the PL330 Channel thread */
> - ? ? ? pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);
> + ? ? ? switch (cmd) {
> + ? ? ? case DMA_TERMINATE_ALL:
> + ? ? ? ? ? ? ? spin_lock_irqsave(&pch->lock, flags);
>
> - ? ? ? /* Mark all desc done */
> - ? ? ? list_for_each_entry(desc, &pch->work_list, node)
> - ? ? ? ? ? ? ? desc->status = DONE;
> + ? ? ? ? ? ? ? /* FLUSH the PL330 Channel thread */
> + ? ? ? ? ? ? ? pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);
>
> - ? ? ? spin_unlock_irqrestore(&pch->lock, flags);
> + ? ? ? ? ? ? ? /* Mark all desc done */
> + ? ? ? ? ? ? ? list_for_each_entry_safe(desc, _dt, &pch->work_list , node) {
> + ? ? ? ? ? ? ? ? ? ? ? desc->status = DONE;
> + ? ? ? ? ? ? ? ? ? ? ? pch->completed = desc->txd.cookie;
> + ? ? ? ? ? ? ? ? ? ? ? list_move_tail(&desc->node, &list);
> + ? ? ? ? ? ? ? }
>
> - ? ? ? pl330_tasklet((unsigned long) pch);
> + ? ? ? ? ? ? ? list_splice_tail_init(&list, &pdmac->desc_pool);
> + ? ? ? ? ? ? ? spin_unlock_irqrestore(&pch->lock, flags);
> + ? ? ? ? ? ? ? break;
> + ? ? ? case DMA_SLAVE_CONFIG:
> + ? ? ? ? ? ? ? slave_config = (struct dma_slave_config *)arg;
> + ? ? ? ? ? ? ? peri = pch->chan.private;
> +
> + ? ? ? ? ? ? ? if (slave_config->direction == DMA_TO_DEVICE) {
> + ? ? ? ? ? ? ? ? ? ? ? if (slave_config->dst_addr)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? peri->fifo_addr = slave_config->dst_addr;
> + ? ? ? ? ? ? ? ? ? ? ? if (slave_config->dst_addr_width) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? i = 0;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? while (slave_config->dst_addr_width != (1 << i))
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? i++;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? peri->burst_sz = i;
> + ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? } else if (slave_config->direction == DMA_FROM_DEVICE) {
> + ? ? ? ? ? ? ? ? ? ? ? if (slave_config->src_addr)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? peri->fifo_addr = slave_config->src_addr;
> + ? ? ? ? ? ? ? ? ? ? ? if (slave_config->src_addr_width) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? i = 0;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? while (slave_config->src_addr_width != (1 << i))
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? i++;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? peri->burst_sz = i;
> + ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? break;
> + ? ? ? default:
> + ? ? ? ? ? ? ? return -ENXIO;
To print error message in the default case will be better.
> + ? ? ? }
>
> ? ? ? ?return 0;
> ?}
> @@ -291,6 +385,9 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
> ? ? ? ?pl330_release_channel(pch->pl330_chid);
> ? ? ? ?pch->pl330_chid = NULL;
>
> + ? ? ? if (pch->cyclic_task)
> + ? ? ? ? ? ? ? pl330_cyclic_free(pch);
> +
> ? ? ? ?spin_unlock_irqrestore(&pch->lock, flags);
> ?}
>
> @@ -522,6 +619,66 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len)
> ? ? ? ?return burst_len;
> ?}
>
> +static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
> + ? ? ? ? ? ? ? struct dma_chan *chan, dma_addr_t dma_addr, size_t len,
> + ? ? ? ? ? ? ? size_t period_len, enum dma_data_direction direction)
> +{
> + ? ? ? struct dma_pl330_desc *desc;
> + ? ? ? struct dma_pl330_chan *pch = to_pchan(chan);
> + ? ? ? struct dma_pl330_peri *peri = chan->private;
> + ? ? ? dma_addr_t dst;
> + ? ? ? dma_addr_t src;
> +
> + ? ? ? pch = to_pchan(chan);
> + ? ? ? if (!pch) {
> + ? ? ? ? ? ? ? dev_err(pch->dmac->pif.dev, "%s:%d Unable to fetch desc\n",
> + ? ? ? ? ? ? ? ? ? ? ? __func__, __LINE__);
> + ? ? ? ? ? ? ? return NULL;
> + ? ? ? }
'pch' is already acquired when defined. It's duplicated.
dev_err has a incorrect parameter because pch is NULL.
Accessing pch->dmac will be failed.
> +
> + ? ? ? desc = pl330_get_desc(pch);
> + ? ? ? if (!desc) {
> + ? ? ? ? ? ? ? dev_err(pch->dmac->pif.dev, "%s:%d Unable to fetch desc\n",
> + ? ? ? ? ? ? ? ? ? ? ? __func__, __LINE__);
> + ? ? ? ? ? ? ? return NULL;
> + ? ? ? }
> +
> + ? ? ? switch (direction) {
> + ? ? ? case DMA_TO_DEVICE:
> + ? ? ? ? ? ? ? desc->rqcfg.src_inc = 1;
> + ? ? ? ? ? ? ? desc->rqcfg.dst_inc = 0;
> + ? ? ? ? ? ? ? src = dma_addr;
> + ? ? ? ? ? ? ? dst = peri->fifo_addr;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case DMA_FROM_DEVICE:
> + ? ? ? ? ? ? ? desc->rqcfg.src_inc = 0;
> + ? ? ? ? ? ? ? desc->rqcfg.dst_inc = 1;
> + ? ? ? ? ? ? ? src = peri->fifo_addr;
> + ? ? ? ? ? ? ? dst = dma_addr;
> + ? ? ? ? ? ? ? break;
> + ? ? ? default:
> + ? ? ? ? ? ? ? dev_err(pch->dmac->pif.dev, "%s:%d Invalid dma direction\n",
> + ? ? ? ? ? ? ? __func__, __LINE__);
> + ? ? ? ? ? ? ? return NULL;
> + ? ? ? }
> +
> + ? ? ? desc->rqcfg.brst_size = peri->burst_sz;
> + ? ? ? desc->rqcfg.brst_len = 1;
> +
> + ? ? ? if (!pch->cyclic_task) {
> + ? ? ? ? ? ? ? pch->cyclic_task =
> + ? ? ? ? ? ? ? ? ? ? ? kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL);
> + ? ? ? ? ? ? ? tasklet_init(pch->cyclic_task,
> + ? ? ? ? ? ? ? ? ? ? ? pl330_tasklet_cyclic, (unsigned int)pch);
> + ? ? ? }
> +
> + ? ? ? desc->cyclic = true;
> +
> + ? ? ? fill_px(&desc->px, dst, src, period_len);
> +
> + ? ? ? return &desc->txd;
> +}
> +
> ?static struct dma_async_tx_descriptor *
> ?pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
> ? ? ? ? ? ? ? ?dma_addr_t src, size_t len, unsigned long flags)
> @@ -756,6 +913,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
> ? ? ? ? ? ? ? ? ? ? ? ?case MEMTODEV:
> ? ? ? ? ? ? ? ? ? ? ? ?case DEVTOMEM:
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?dma_cap_set(DMA_SLAVE, pd->cap_mask);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dma_cap_set(DMA_CYCLIC, pd->cap_mask);
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?break;
> ? ? ? ? ? ? ? ? ? ? ? ?default:
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?dev_err(&adev->dev, "DEVTODEV Not Supported\n");
> @@ -781,6 +939,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
> ? ? ? ?pd->device_alloc_chan_resources = pl330_alloc_chan_resources;
> ? ? ? ?pd->device_free_chan_resources = pl330_free_chan_resources;
> ? ? ? ?pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy;
> + ? ? ? pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic;
> ? ? ? ?pd->device_tx_status = pl330_tx_status;
> ? ? ? ?pd->device_prep_slave_sg = pl330_prep_slave_sg;
> ? ? ? ?pd->device_control = pl330_control;
> --
> 1.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
>
--
Best Regards,
Chanho Park
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH V2 03/12] DMA: PL330: Add DMA capabilities
2011-07-15 4:45 ` Chanho Park
@ 2011-07-16 1:11 ` Boojin Kim
0 siblings, 0 replies; 23+ messages in thread
From: Boojin Kim @ 2011-07-16 1:11 UTC (permalink / raw)
To: linux-arm-kernel
Chanho Park wrote:
> 2011/7/13 Kukjin Kim <kgene.kim@samsung.com>:
> > From: Boojin Kim <boojin.kim@samsung.com>
> >
> > This patch adds DMA_CYCLIC capability that is used for audio driver
> > and SLAVE_CONFIG capability for transmit between device and memory.
> >
> > Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
> > Cc: Vinod Koul <vinod.koul@intel.com>
> > Cc: Dan Williams <dan.j.williams@intel.com>
> > Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> > ---
> > ?drivers/dma/pl330.c | ?187
> +++++++++++++++++++++++++++++++++++++++++++++++----
> > ?1 files changed, 173 insertions(+), 14 deletions(-)
> >
> > diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
> > index 9bdda7b..2162ac5 100644
> > --- a/drivers/dma/pl330.c
> > +++ b/drivers/dma/pl330.c
> > @@ -69,6 +69,9 @@ struct dma_pl330_chan {
> > ? ? ? ? * NULL if the channel is available to be acquired.
> > ? ? ? ? */
> > ? ? ? ?void *pl330_chid;
> > +
> > + ? ? ? /* taks for cyclic capability */
> > + ? ? ? struct tasklet_struct *cyclic_task;
> > ?};
> >
> > ?struct dma_pl330_dmac {
> > @@ -105,6 +108,7 @@ struct dma_pl330_desc {
> >
> > ? ? ? ?/* The channel which currently holds this desc */
> > ? ? ? ?struct dma_pl330_chan *pchan;
> > + ? ? ? bool cyclic;
> > ?};
> >
> > ?static inline struct dma_pl330_chan *
> > @@ -184,6 +188,60 @@ static inline void fill_queue(struct dma_pl330_chan
> *pch)
> > ? ? ? ?}
> > ?}
> >
> > +static void pl330_tasklet_cyclic(unsigned long data)
> > +{
> > + ? ? ? struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data;
> > + ? ? ? struct dma_pl330_desc *desc, *_dt;
> > + ? ? ? unsigned long flags;
> > + ? ? ? LIST_HEAD(list);
> > +
> > + ? ? ? spin_lock_irqsave(&pch->lock, flags);
> > +
> > + ? ? ? /* Pick up ripe tomatoes */
> > + ? ? ? list_for_each_entry_safe(desc, _dt, &pch->work_list, node)
> > + ? ? ? ? ? ? ? if ((desc->status == DONE) && desc->cyclic) {
> > + ? ? ? ? ? ? ? ? ? ? ? dma_async_tx_callback callback;
> > +
> > + ? ? ? ? ? ? ? ? ? ? ? list_move_tail(&desc->node, &pch->work_list);
> > + ? ? ? ? ? ? ? ? ? ? ? pch->completed = desc->txd.cookie;
> > +
> > + ? ? ? ? ? ? ? ? ? ? ? desc->status = PREP;
> > +
> > + ? ? ? ? ? ? ? ? ? ? ? /* Try to submit a req imm.
> > + ? ? ? ? ? ? ? ? ? ? ? next to the last completed cookie */
> > + ? ? ? ? ? ? ? ? ? ? ? fill_queue(pch);
> > +
> > + ? ? ? ? ? ? ? ? ? ? ? /* Make sure the PL330 Channel thread is active
*/
> > + ? ? ? ? ? ? ? ? ? ? ? pl330_chan_ctrl(pch->pl330_chid,
PL330_OP_START);
> > +
> > + ? ? ? ? ? ? ? ? ? ? ? callback = desc->txd.callback;
> > + ? ? ? ? ? ? ? ? ? ? ? if (callback)
> > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? callback(desc->txd.callback_param);
> > +
> > + ? ? ? ? ? ? ? }
> > +
> > + ? ? ? spin_unlock_irqrestore(&pch->lock, flags);
> > +}
> > +
> > +static void pl330_cyclic_free(struct dma_pl330_chan *pch)
> > +{
> > + ? ? ? struct dma_pl330_dmac *pdmac = pch->dmac;
> > + ? ? ? struct dma_pl330_desc *desc, *_dt;
> > + ? ? ? unsigned long flags;
> > + ? ? ? LIST_HEAD(list);
> > +
> > + ? ? ? spin_lock_irqsave(&pdmac->pool_lock, flags);
> > +
> > + ? ? ? list_for_each_entry_safe(desc, _dt, &pch->work_list, node)
> > + ? ? ? if (desc->cyclic)
> > + ? ? ? ? ? ? ? list_move_tail(&desc->node, &list);
>
> nitpick: indentation will be required after for_each
>
I will address your comment.
> > +
> > + ? ? ? list_splice_tail_init(&list, &pdmac->desc_pool);
> > +
> > + ? ? ? spin_unlock_irqrestore(&pdmac->pool_lock, flags);
> > + ? ? ? pch->cyclic_task = NULL;
> > +}
> > +
> > ?static void pl330_tasklet(unsigned long data)
> > ?{
> > ? ? ? ?struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data;
> > @@ -227,6 +285,9 @@ static void dma_pl330_rqcb(void *token, enum
> pl330_op_err err)
> >
> > ? ? ? ?spin_unlock_irqrestore(&pch->lock, flags);
> >
> > + ? ? ? if (pch->cyclic_task)
> > + ? ? ? ? ? ? ? tasklet_schedule(pch->cyclic_task);
> > + ? ? ? else
> > ? ? ? ?tasklet_schedule(&pch->task);
> > ?}
> >
> > @@ -256,25 +317,58 @@ static int pl330_alloc_chan_resources(struct
> dma_chan *chan)
> > ?static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
> unsigned long arg)
> > ?{
> > ? ? ? ?struct dma_pl330_chan *pch = to_pchan(chan);
> > - ? ? ? struct dma_pl330_desc *desc;
> > + ? ? ? struct dma_pl330_desc *desc, *_dt;
> > ? ? ? ?unsigned long flags;
> > + ? ? ? struct dma_pl330_dmac *pdmac = pch->dmac;
> > + ? ? ? struct dma_slave_config *slave_config;
> > + ? ? ? struct dma_pl330_peri *peri;
> > + ? ? ? int i;
> > + ? ? ? LIST_HEAD(list);
> >
> > - ? ? ? /* Only supports DMA_TERMINATE_ALL */
> > - ? ? ? if (cmd != DMA_TERMINATE_ALL)
> > - ? ? ? ? ? ? ? return -ENXIO;
> > -
> > - ? ? ? spin_lock_irqsave(&pch->lock, flags);
> > -
> > - ? ? ? /* FLUSH the PL330 Channel thread */
> > - ? ? ? pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);
> > + ? ? ? switch (cmd) {
> > + ? ? ? case DMA_TERMINATE_ALL:
> > + ? ? ? ? ? ? ? spin_lock_irqsave(&pch->lock, flags);
> >
> > - ? ? ? /* Mark all desc done */
> > - ? ? ? list_for_each_entry(desc, &pch->work_list, node)
> > - ? ? ? ? ? ? ? desc->status = DONE;
> > + ? ? ? ? ? ? ? /* FLUSH the PL330 Channel thread */
> > + ? ? ? ? ? ? ? pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);
> >
> > - ? ? ? spin_unlock_irqrestore(&pch->lock, flags);
> > + ? ? ? ? ? ? ? /* Mark all desc done */
> > + ? ? ? ? ? ? ? list_for_each_entry_safe(desc, _dt, &pch->work_list ,
node)
> {
> > + ? ? ? ? ? ? ? ? ? ? ? desc->status = DONE;
> > + ? ? ? ? ? ? ? ? ? ? ? pch->completed = desc->txd.cookie;
> > + ? ? ? ? ? ? ? ? ? ? ? list_move_tail(&desc->node, &list);
> > + ? ? ? ? ? ? ? }
> >
> > - ? ? ? pl330_tasklet((unsigned long) pch);
> > + ? ? ? ? ? ? ? list_splice_tail_init(&list, &pdmac->desc_pool);
> > + ? ? ? ? ? ? ? spin_unlock_irqrestore(&pch->lock, flags);
> > + ? ? ? ? ? ? ? break;
> > + ? ? ? case DMA_SLAVE_CONFIG:
> > + ? ? ? ? ? ? ? slave_config = (struct dma_slave_config *)arg;
> > + ? ? ? ? ? ? ? peri = pch->chan.private;
> > +
> > + ? ? ? ? ? ? ? if (slave_config->direction == DMA_TO_DEVICE) {
> > + ? ? ? ? ? ? ? ? ? ? ? if (slave_config->dst_addr)
> > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? peri->fifo_addr =
slave_config->dst_addr;
> > + ? ? ? ? ? ? ? ? ? ? ? if (slave_config->dst_addr_width) {
> > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? i = 0;
> > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? while (slave_config->dst_addr_width !=
(1 <<
> i))
> > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? i++;
> > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? peri->burst_sz = i;
> > + ? ? ? ? ? ? ? ? ? ? ? }
> > + ? ? ? ? ? ? ? } else if (slave_config->direction == DMA_FROM_DEVICE) {
> > + ? ? ? ? ? ? ? ? ? ? ? if (slave_config->src_addr)
> > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? peri->fifo_addr =
slave_config->src_addr;
> > + ? ? ? ? ? ? ? ? ? ? ? if (slave_config->src_addr_width) {
> > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? i = 0;
> > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? while (slave_config->src_addr_width !=
(1 <<
> i))
> > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? i++;
> > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? peri->burst_sz = i;
> > + ? ? ? ? ? ? ? ? ? ? ? }
> > + ? ? ? ? ? ? ? }
> > + ? ? ? ? ? ? ? break;
> > + ? ? ? default:
> > + ? ? ? ? ? ? ? return -ENXIO;
>
> To print error message in the default case will be better.
>
I will address your comment.
> > + ? ? ? }
> >
> > ? ? ? ?return 0;
> > ?}
> > @@ -291,6 +385,9 @@ static void pl330_free_chan_resources(struct
> dma_chan *chan)
> > ? ? ? ?pl330_release_channel(pch->pl330_chid);
> > ? ? ? ?pch->pl330_chid = NULL;
> >
> > + ? ? ? if (pch->cyclic_task)
> > + ? ? ? ? ? ? ? pl330_cyclic_free(pch);
> > +
> > ? ? ? ?spin_unlock_irqrestore(&pch->lock, flags);
> > ?}
> >
> > @@ -522,6 +619,66 @@ static inline int get_burst_len(struct
> dma_pl330_desc *desc, size_t len)
> > ? ? ? ?return burst_len;
> > ?}
> >
> > +static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
> > + ? ? ? ? ? ? ? struct dma_chan *chan, dma_addr_t dma_addr, size_t len,
> > + ? ? ? ? ? ? ? size_t period_len, enum dma_data_direction direction)
> > +{
> > + ? ? ? struct dma_pl330_desc *desc;
> > + ? ? ? struct dma_pl330_chan *pch = to_pchan(chan);
> > + ? ? ? struct dma_pl330_peri *peri = chan->private;
> > + ? ? ? dma_addr_t dst;
> > + ? ? ? dma_addr_t src;
> > +
> > + ? ? ? pch = to_pchan(chan);
> > + ? ? ? if (!pch) {
> > + ? ? ? ? ? ? ? dev_err(pch->dmac->pif.dev, "%s:%d Unable to fetch
desc\n",
> > + ? ? ? ? ? ? ? ? ? ? ? __func__, __LINE__);
> > + ? ? ? ? ? ? ? return NULL;
> > + ? ? ? }
>
> 'pch' is already acquired when defined. It's duplicated.
> dev_err has a incorrect parameter because pch is NULL.
> Accessing pch->dmac will be failed.
I will address your comment.
>
> > +
> > + ? ? ? desc = pl330_get_desc(pch);
> > + ? ? ? if (!desc) {
> > + ? ? ? ? ? ? ? dev_err(pch->dmac->pif.dev, "%s:%d Unable to fetch
desc\n",
> > + ? ? ? ? ? ? ? ? ? ? ? __func__, __LINE__);
> > + ? ? ? ? ? ? ? return NULL;
> > + ? ? ? }
> > +
> > + ? ? ? switch (direction) {
> > + ? ? ? case DMA_TO_DEVICE:
> > + ? ? ? ? ? ? ? desc->rqcfg.src_inc = 1;
> > + ? ? ? ? ? ? ? desc->rqcfg.dst_inc = 0;
> > + ? ? ? ? ? ? ? src = dma_addr;
> > + ? ? ? ? ? ? ? dst = peri->fifo_addr;
> > + ? ? ? ? ? ? ? break;
> > + ? ? ? case DMA_FROM_DEVICE:
> > + ? ? ? ? ? ? ? desc->rqcfg.src_inc = 0;
> > + ? ? ? ? ? ? ? desc->rqcfg.dst_inc = 1;
> > + ? ? ? ? ? ? ? src = peri->fifo_addr;
> > + ? ? ? ? ? ? ? dst = dma_addr;
> > + ? ? ? ? ? ? ? break;
> > + ? ? ? default:
> > + ? ? ? ? ? ? ? dev_err(pch->dmac->pif.dev, "%s:%d Invalid dma
> direction\n",
> > + ? ? ? ? ? ? ? __func__, __LINE__);
> > + ? ? ? ? ? ? ? return NULL;
> > + ? ? ? }
> > +
> > + ? ? ? desc->rqcfg.brst_size = peri->burst_sz;
> > + ? ? ? desc->rqcfg.brst_len = 1;
> > +
> > + ? ? ? if (!pch->cyclic_task) {
> > + ? ? ? ? ? ? ? pch->cyclic_task =
> > + ? ? ? ? ? ? ? ? ? ? ? kmalloc(sizeof(struct tasklet_struct),
GFP_KERNEL);
> > + ? ? ? ? ? ? ? tasklet_init(pch->cyclic_task,
> > + ? ? ? ? ? ? ? ? ? ? ? pl330_tasklet_cyclic, (unsigned int)pch);
> > + ? ? ? }
> > +
> > + ? ? ? desc->cyclic = true;
> > +
> > + ? ? ? fill_px(&desc->px, dst, src, period_len);
> > +
> > + ? ? ? return &desc->txd;
> > +}
> > +
> > ?static struct dma_async_tx_descriptor *
> > ?pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
> > ? ? ? ? ? ? ? ?dma_addr_t src, size_t len, unsigned long flags)
> > @@ -756,6 +913,7 @@ pl330_probe(struct amba_device *adev, const struct
> amba_id *id)
> > ? ? ? ? ? ? ? ? ? ? ? ?case MEMTODEV:
> > ? ? ? ? ? ? ? ? ? ? ? ?case DEVTOMEM:
> > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?dma_cap_set(DMA_SLAVE, pd->cap_mask);
> > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dma_cap_set(DMA_CYCLIC, pd->cap_mask);
> > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?break;
> > ? ? ? ? ? ? ? ? ? ? ? ?default:
> > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?dev_err(&adev->dev, "DEVTODEV Not
> Supported\n");
> > @@ -781,6 +939,7 @@ pl330_probe(struct amba_device *adev, const struct
> amba_id *id)
> > ? ? ? ?pd->device_alloc_chan_resources = pl330_alloc_chan_resources;
> > ? ? ? ?pd->device_free_chan_resources = pl330_free_chan_resources;
> > ? ? ? ?pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy;
> > + ? ? ? pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic;
> > ? ? ? ?pd->device_tx_status = pl330_tx_status;
> > ? ? ? ?pd->device_prep_slave_sg = pl330_prep_slave_sg;
> > ? ? ? ?pd->device_control = pl330_control;
> > --
> > 1.7.1
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-samsung-
> soc" in
> > the body of a message to majordomo at vger.kernel.org
> > More majordomo info at ?http://vger.kernel.org/majordomo-info.html
> >
>
>
>
> --
> Best Regards,
> Chanho Park
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH V2 04/12] ARM: SAMSUNG: Update to use PL330-DMA driver
2011-07-13 8:47 [PATCH V2 00/12] To use DMA generic APIs for Samsung DMA Kukjin Kim
` (2 preceding siblings ...)
2011-07-13 8:47 ` [PATCH V2 03/12] DMA: PL330: Add DMA capabilities Kukjin Kim
@ 2011-07-13 8:47 ` Kukjin Kim
2011-07-13 8:47 ` [PATCH V2 05/12] ARM: SAMSUNG: Add common DMA operations Kukjin Kim
` (7 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Kukjin Kim @ 2011-07-13 8:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Boojin Kim <boojin.kim@samsung.com>
This patch adds to support PL330-DMA driver on DMADEVICE for S5P SoCs.
Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
arch/arm/mach-exynos4/include/mach/dma.h | 4 +-
arch/arm/mach-s5p64x0/include/mach/dma.h | 2 +-
arch/arm/mach-s5pc100/include/mach/dma.h | 2 +-
arch/arm/mach-s5pv210/include/mach/dma.h | 2 +-
arch/arm/plat-samsung/Kconfig | 8 +++++++
.../include/plat/{s3c-dma-pl330.h => dma-pl330.h} | 21 +++++++++++++------
.../plat-samsung/include/plat/s3c-pl330-pdata.h | 2 +-
7 files changed, 28 insertions(+), 13 deletions(-)
rename arch/arm/plat-samsung/include/plat/{s3c-dma-pl330.h => dma-pl330.h} (84%)
diff --git a/arch/arm/mach-exynos4/include/mach/dma.h b/arch/arm/mach-exynos4/include/mach/dma.h
index 81209eb..201842a 100644
--- a/arch/arm/mach-exynos4/include/mach/dma.h
+++ b/arch/arm/mach-exynos4/include/mach/dma.h
@@ -20,7 +20,7 @@
#ifndef __MACH_DMA_H
#define __MACH_DMA_H
-/* This platform uses the common S3C DMA API driver for PL330 */
-#include <plat/s3c-dma-pl330.h>
+/* This platform uses the common DMA API driver for PL330 */
+#include <plat/dma-pl330.h>
#endif /* __MACH_DMA_H */
diff --git a/arch/arm/mach-s5p64x0/include/mach/dma.h b/arch/arm/mach-s5p64x0/include/mach/dma.h
index 81209eb..1beae2c 100644
--- a/arch/arm/mach-s5p64x0/include/mach/dma.h
+++ b/arch/arm/mach-s5p64x0/include/mach/dma.h
@@ -21,6 +21,6 @@
#define __MACH_DMA_H
/* This platform uses the common S3C DMA API driver for PL330 */
-#include <plat/s3c-dma-pl330.h>
+#include <plat/dma-pl330.h>
#endif /* __MACH_DMA_H */
diff --git a/arch/arm/mach-s5pc100/include/mach/dma.h b/arch/arm/mach-s5pc100/include/mach/dma.h
index 81209eb..1beae2c 100644
--- a/arch/arm/mach-s5pc100/include/mach/dma.h
+++ b/arch/arm/mach-s5pc100/include/mach/dma.h
@@ -21,6 +21,6 @@
#define __MACH_DMA_H
/* This platform uses the common S3C DMA API driver for PL330 */
-#include <plat/s3c-dma-pl330.h>
+#include <plat/dma-pl330.h>
#endif /* __MACH_DMA_H */
diff --git a/arch/arm/mach-s5pv210/include/mach/dma.h b/arch/arm/mach-s5pv210/include/mach/dma.h
index 81209eb..1beae2c 100644
--- a/arch/arm/mach-s5pv210/include/mach/dma.h
+++ b/arch/arm/mach-s5pv210/include/mach/dma.h
@@ -21,6 +21,6 @@
#define __MACH_DMA_H
/* This platform uses the common S3C DMA API driver for PL330 */
-#include <plat/s3c-dma-pl330.h>
+#include <plat/dma-pl330.h>
#endif /* __MACH_DMA_H */
diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig
index 4d79519..cb170a6 100644
--- a/arch/arm/plat-samsung/Kconfig
+++ b/arch/arm/plat-samsung/Kconfig
@@ -300,6 +300,14 @@ config S3C_PL330_DMA
help
S3C DMA API Driver for PL330 DMAC.
+config DMADEV_PL330
+ bool
+ select DMADEVICES
+ select PL330_DMA
+ select ARM_AMBA
+ help
+ Use DMA device engine for PL330 DMAC.
+
comment "Power management"
config SAMSUNG_PM_DEBUG
diff --git a/arch/arm/plat-samsung/include/plat/s3c-dma-pl330.h b/arch/arm/plat-samsung/include/plat/dma-pl330.h
similarity index 84%
rename from arch/arm/plat-samsung/include/plat/s3c-dma-pl330.h
rename to arch/arm/plat-samsung/include/plat/dma-pl330.h
index 8107442..c402719 100644
--- a/arch/arm/plat-samsung/include/plat/s3c-dma-pl330.h
+++ b/arch/arm/plat-samsung/include/plat/dma-pl330.h
@@ -8,19 +8,18 @@
* (at your option) any later version.
*/
-#ifndef __S3C_DMA_PL330_H_
-#define __S3C_DMA_PL330_H_
-
-#define S3C2410_DMAF_AUTOSTART (1 << 0)
-#define S3C2410_DMAF_CIRCULAR (1 << 1)
+#ifndef __DMA_PL330_H_
+#define __DMA_PL330_H_ __FILE__
+#define S3C2410_DMAF_AUTOSTART (1 << 0)
+#define S3C2410_DMAF_CIRCULAR (1 << 1)
/*
* PL330 can assign any channel to communicate with
* any of the peripherals attched to the DMAC.
* For the sake of consistency across client drivers,
* We keep the channel names unchanged and only add
* missing peripherals are added.
- * Order is not important since S3C PL330 API driver
+ * Order is not important since DMA PL330 API driver
* use these just as IDs.
*/
enum dma_ch {
@@ -84,6 +83,14 @@ enum dma_ch {
DMACH_SLIMBUS4_TX,
DMACH_SLIMBUS5_RX,
DMACH_SLIMBUS5_TX,
+ DMACH_MTOM_0,
+ DMACH_MTOM_1,
+ DMACH_MTOM_2,
+ DMACH_MTOM_3,
+ DMACH_MTOM_4,
+ DMACH_MTOM_5,
+ DMACH_MTOM_6,
+ DMACH_MTOM_7,
/* END Marker, also used to denote a reserved channel */
DMACH_MAX,
};
@@ -95,4 +102,4 @@ static inline bool s3c_dma_has_circular(void)
#include <plat/dma.h>
-#endif /* __S3C_DMA_PL330_H_ */
+#endif /* __DMA_PL330_H_ */
diff --git a/arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h b/arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h
index bf5e2a9..64fdf66 100644
--- a/arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h
+++ b/arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h
@@ -12,7 +12,7 @@
#ifndef __S3C_PL330_PDATA_H
#define __S3C_PL330_PDATA_H
-#include <plat/s3c-dma-pl330.h>
+#include <plat/dma-pl330.h>
/*
* Every PL330 DMAC has max 32 peripheral interfaces,
--
1.7.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH V2 05/12] ARM: SAMSUNG: Add common DMA operations
2011-07-13 8:47 [PATCH V2 00/12] To use DMA generic APIs for Samsung DMA Kukjin Kim
` (3 preceding siblings ...)
2011-07-13 8:47 ` [PATCH V2 04/12] ARM: SAMSUNG: Update to use PL330-DMA driver Kukjin Kim
@ 2011-07-13 8:47 ` Kukjin Kim
2011-07-15 2:53 ` Grant Likely
2011-07-13 8:47 ` [PATCH V2 06/12] ARM: EXYNOS4: Use generic DMA PL330 driver Kukjin Kim
` (6 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Kukjin Kim @ 2011-07-13 8:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Boojin Kim <boojin.kim@samsung.com>
This patch adds common DMA operations which are used for Samsung DMA
drivers. Currently there are two types of DMA driver for Samsung SoCs.
The one is S3C-DMA for S3C SoCs and the other is PL330-DMA for S5P SoCs.
This patch provides funcion pointers for common DMA operations to DMA
client driver like SPI and Audio. It makes DMA client drivers support
multi-platform.
In addition, this common DMA operations implement the shared actions
that are needed for DMA client driver. For example shared actions are
filter() function for dma_request_channel() and parameter passing for
device_prep_slave_sg().
Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
arch/arm/mach-s3c2410/include/mach/dma.h | 3 +-
arch/arm/plat-samsung/Makefile | 6 +-
arch/arm/plat-samsung/dma-ops.c | 131 +++++++++++++++++++++++
arch/arm/plat-samsung/include/plat/dma-ops.h | 47 +++++++++
arch/arm/plat-samsung/include/plat/dma-pl330.h | 3 +
arch/arm/plat-samsung/include/plat/dma.h | 2 +-
arch/arm/plat-samsung/s3c-dma-ops.c | 132 ++++++++++++++++++++++++
7 files changed, 320 insertions(+), 4 deletions(-)
create mode 100644 arch/arm/plat-samsung/dma-ops.c
create mode 100644 arch/arm/plat-samsung/include/plat/dma-ops.h
create mode 100644 arch/arm/plat-samsung/s3c-dma-ops.c
diff --git a/arch/arm/mach-s3c2410/include/mach/dma.h b/arch/arm/mach-s3c2410/include/mach/dma.h
index b2b2a5b..71a662d 100644
--- a/arch/arm/mach-s3c2410/include/mach/dma.h
+++ b/arch/arm/mach-s3c2410/include/mach/dma.h
@@ -13,7 +13,6 @@
#ifndef __ASM_ARCH_DMA_H
#define __ASM_ARCH_DMA_H __FILE__
-#include <plat/dma.h>
#include <linux/sysdev.h>
#define MAX_DMA_TRANSFER_SIZE 0x100000 /* Data Unit is half word */
@@ -51,6 +50,8 @@ enum dma_ch {
DMACH_MAX, /* the end entry */
};
+#include <plat/dma.h>
+
#define DMACH_LOW_LEVEL (1<<28) /* use this to specifiy hardware ch no */
/* we have 4 dma channels */
diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile
index 53eb15b..9ecf2aa 100644
--- a/arch/arm/plat-samsung/Makefile
+++ b/arch/arm/plat-samsung/Makefile
@@ -62,9 +62,11 @@ obj-$(CONFIG_SAMSUNG_DEV_PWM) += dev-pwm.o
# DMA support
-obj-$(CONFIG_S3C_DMA) += dma.o
+obj-$(CONFIG_S3C_DMA) += dma.o s3c-dma-ops.o
-obj-$(CONFIG_S3C_PL330_DMA) += s3c-pl330.o
+obj-$(CONFIG_DMADEV_PL330) += dma-ops.o
+
+obj-$(CONFIG_S3C_PL330_DMA) += s3c-pl330.o s3c-dma-ops.o
# PM support
diff --git a/arch/arm/plat-samsung/dma-ops.c b/arch/arm/plat-samsung/dma-ops.c
new file mode 100644
index 0000000..94a9d33
--- /dev/null
+++ b/arch/arm/plat-samsung/dma-ops.c
@@ -0,0 +1,131 @@
+/* linux/arch/arm/plat-samsung/dma-ops.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Samsung DMA Operations
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/dmaengine.h>
+#include <linux/amba/pl330.h>
+
+#include <mach/dma.h>
+
+static bool filter(struct dma_chan *chan, void *param)
+{
+ struct dma_pl330_peri *peri = (struct dma_pl330_peri *)chan->private;
+ unsigned dma_ch = (unsigned)param;
+
+ if (peri->peri_id != dma_ch)
+ return false;
+
+ return true;
+}
+
+static unsigned dma_request(enum dma_ch dma_ch, struct samsung_dma_info *info)
+{
+ struct dma_chan *chan;
+ dma_cap_mask_t mask;
+ struct dma_slave_config slave_config;
+
+ dma_cap_zero(mask);
+ dma_cap_set(info->cap, mask);
+
+ chan = dma_request_channel(mask, filter, (void *)dma_ch);
+
+ if (info->direction == DMA_FROM_DEVICE) {
+ memset(&slave_config, 0, sizeof(struct dma_slave_config));
+ slave_config.direction = info->direction;
+ slave_config.src_addr = info->fifo;
+ slave_config.src_addr_width = info->width;
+ dmaengine_slave_config(chan, &slave_config);
+ } else if (info->direction == DMA_TO_DEVICE) {
+ memset(&slave_config, 0, sizeof(struct dma_slave_config));
+ slave_config.direction = info->direction;
+ slave_config.dst_addr = info->fifo;
+ slave_config.dst_addr_width = info->width;
+ dmaengine_slave_config(chan, &slave_config);
+ }
+
+ return (unsigned)chan;
+}
+
+static int dma_release(unsigned ch, struct s3c2410_dma_client *client)
+{
+ dma_release_channel((struct dma_chan *)ch);
+
+ return 0;
+}
+
+static int dma_prepare(unsigned ch, struct samsung_dma_prep_info *info)
+{
+ struct scatterlist sg;
+ struct dma_chan *chan = (struct dma_chan *)ch;
+ struct dma_async_tx_descriptor *desc;
+
+ switch (info->cap) {
+ case DMA_SLAVE:
+ sg_init_table(&sg, 1);
+ sg_dma_len(&sg) = info->len;
+ sg_set_page(&sg, pfn_to_page(PFN_DOWN(info->buf)),
+ info->len, offset_in_page(info->buf));
+ sg_dma_address(&sg) = info->buf;
+
+ desc = chan->device->device_prep_slave_sg(chan,
+ &sg, 1, info->direction, DMA_PREP_INTERRUPT);
+ break;
+ case DMA_CYCLIC:
+ desc = chan->device->device_prep_dma_cyclic(chan,
+ info->buf, info->len, info->period, info->direction);
+ break;
+ default:
+ dev_err(&chan->dev->device, "unsupported format\n");
+ return -EFAULT;
+ }
+
+ if (!desc) {
+ dev_err(&chan->dev->device, "cannot prepare cyclic dma\n");
+ return -EFAULT;
+ }
+
+ desc->callback = info->fp;
+ desc->callback_param = info->fp_param;
+
+ dmaengine_submit((struct dma_async_tx_descriptor *)desc);
+
+ return 0;
+}
+
+static inline int dma_trigger(unsigned ch)
+{
+ dma_async_issue_pending((struct dma_chan *)ch);
+
+ return 0;
+}
+
+static inline int dma_flush(unsigned ch)
+{
+ return dmaengine_terminate_all((struct dma_chan *)ch);
+}
+
+static struct samsung_dma_ops dmaeng_ops = {
+ .request = dma_request,
+ .release = dma_release,
+ .prepare = dma_prepare,
+ .trigger = dma_trigger,
+ .started = NULL,
+ .flush = dma_flush,
+ .stop = dma_flush,
+};
+
+void *samsung_dma_get_ops(void)
+{
+ return (void *)&dmaeng_ops;
+}
+EXPORT_SYMBOL(samsung_dma_get_ops);
diff --git a/arch/arm/plat-samsung/include/plat/dma-ops.h b/arch/arm/plat-samsung/include/plat/dma-ops.h
new file mode 100644
index 0000000..eea4130
--- /dev/null
+++ b/arch/arm/plat-samsung/include/plat/dma-ops.h
@@ -0,0 +1,47 @@
+/* arch/arm/plat-samsung/include/plat/dma-ops.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Samsung DMA support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/dmaengine.h>
+
+struct samsung_dma_prep_info {
+ enum dma_transaction_type cap;
+ enum dma_data_direction direction;
+ unsigned buf;
+ unsigned long period;
+ unsigned long len;
+ void (*fp)(void *data);
+ void *fp_param;
+};
+
+struct samsung_dma_info {
+ enum dma_transaction_type cap;
+ enum dma_data_direction direction;
+ enum dma_slave_buswidth width;
+ unsigned fifo;
+ struct s3c2410_dma_client *client;
+};
+
+struct samsung_dma_ops {
+ unsigned (*request)(enum dma_ch ch, struct samsung_dma_info *info);
+ int (*release)(unsigned ch, struct s3c2410_dma_client *client);
+ int (*prepare)(unsigned ch, struct samsung_dma_prep_info *info);
+ int (*trigger)(unsigned ch);
+ int (*started)(unsigned ch);
+ int (*flush)(unsigned ch);
+ int (*stop)(unsigned ch);
+};
+
+/*
+ * samsung_dma_get_ops
+ * get the set of dma operations
+ */
+extern void *samsung_dma_get_ops(void);
diff --git a/arch/arm/plat-samsung/include/plat/dma-pl330.h b/arch/arm/plat-samsung/include/plat/dma-pl330.h
index c402719..be84bec 100644
--- a/arch/arm/plat-samsung/include/plat/dma-pl330.h
+++ b/arch/arm/plat-samsung/include/plat/dma-pl330.h
@@ -1,4 +1,7 @@
/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
* Copyright (C) 2010 Samsung Electronics Co. Ltd.
* Jaswinder Singh <jassi.brar@samsung.com>
*
diff --git a/arch/arm/plat-samsung/include/plat/dma.h b/arch/arm/plat-samsung/include/plat/dma.h
index 2e8f8c6..90da162 100644
--- a/arch/arm/plat-samsung/include/plat/dma.h
+++ b/arch/arm/plat-samsung/include/plat/dma.h
@@ -124,4 +124,4 @@ extern int s3c2410_dma_getposition(unsigned int channel,
extern int s3c2410_dma_set_opfn(unsigned int, s3c2410_dma_opfn_t rtn);
extern int s3c2410_dma_set_buffdone_fn(unsigned int, s3c2410_dma_cbfn_t rtn);
-
+#include <plat/dma-ops.h>
diff --git a/arch/arm/plat-samsung/s3c-dma-ops.c b/arch/arm/plat-samsung/s3c-dma-ops.c
new file mode 100644
index 0000000..17b1be0
--- /dev/null
+++ b/arch/arm/plat-samsung/s3c-dma-ops.c
@@ -0,0 +1,132 @@
+/* linux/arch/arm/plat-samsung/s3c-dma-ops.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Samsung S3C-DMA Operations
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <mach/dma.h>
+
+struct cb_data {
+ void (*fp) (void *);
+ void *fp_param;
+ unsigned ch;
+ struct list_head node;
+};
+
+static LIST_HEAD(dma_list);
+
+static void dma_cb(struct s3c2410_dma_chan *channel,
+ void *param, int size, enum s3c2410_dma_buffresult res)
+{
+ struct cb_data *data = (struct cb_data *)param;
+
+ data->fp(data->fp_param);
+}
+
+static unsigned dma_request(enum dma_ch dma_ch, struct samsung_dma_info *info)
+{
+ struct cb_data *data;
+
+ if (s3c2410_dma_request(dma_ch, info->client, NULL) < 0) {
+ s3c2410_dma_free(dma_ch, info->client);
+ return 0;
+ }
+
+ data = kmalloc(sizeof(struct cb_data), GFP_KERNEL);
+ data->fp = NULL;
+ data->ch = (unsigned)dma_ch;
+ list_add_tail(&data->node, &dma_list);
+
+ if (info->direction == DMA_FROM_DEVICE)
+ s3c2410_dma_devconfig(dma_ch, S3C2410_DMASRC_HW, info->fifo);
+ else
+ s3c2410_dma_devconfig(dma_ch, S3C2410_DMASRC_MEM, info->fifo);
+
+ if (info->cap == DMA_CYCLIC)
+ s3c2410_dma_setflags(dma_ch, S3C2410_DMAF_CIRCULAR);
+
+ s3c2410_dma_config(dma_ch, info->width);
+
+ return (unsigned)dma_ch;
+}
+
+static int dma_release(unsigned ch, struct s3c2410_dma_client *client)
+{
+ struct cb_data *data;
+
+ list_for_each_entry(data, &dma_list, node)
+ if (data->ch == ch)
+ break;
+ list_del(&data->node);
+
+ s3c2410_dma_free((enum dma_ch)ch, client);
+ kfree(data);
+
+ return 0;
+}
+
+static int dma_prepare(unsigned ch, struct samsung_dma_prep_info *info)
+{
+ struct cb_data *data;
+
+ list_for_each_entry(data, &dma_list, node)
+ if (data->ch == ch)
+ break;
+
+ if (!data->fp) {
+ s3c2410_dma_set_buffdone_fn((enum dma_ch)ch, dma_cb);
+ data->fp = info->fp;
+ data->fp_param = info->fp_param;
+ }
+ s3c2410_dma_enqueue((enum dma_ch)ch, (void *)data, info->buf,
+ info->period);
+
+ return 0;
+}
+
+static inline int dma_trigger(unsigned ch)
+{
+ return s3c2410_dma_ctrl(ch, S3C2410_DMAOP_START);
+}
+
+static inline int dma_started(unsigned ch)
+{
+ return s3c2410_dma_ctrl(ch, S3C2410_DMAOP_STARTED);
+}
+
+static inline int dma_flush(unsigned ch)
+{
+ return s3c2410_dma_ctrl(ch, S3C2410_DMAOP_FLUSH);
+}
+
+static inline int dma_stop(unsigned ch)
+{
+ return s3c2410_dma_ctrl(ch, S3C2410_DMAOP_STOP);
+}
+
+static struct samsung_dma_ops s3c_dma_ops = {
+ .request = dma_request,
+ .release = dma_release,
+ .prepare = dma_prepare,
+ .trigger = dma_trigger,
+ .started = dma_started,
+ .flush = dma_flush,
+ .stop = dma_stop,
+};
+
+void *samsung_dma_get_ops(void)
+{
+ return (void *)&s3c_dma_ops;
+}
+EXPORT_SYMBOL(samsung_dma_get_ops);
--
1.7.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH V2 05/12] ARM: SAMSUNG: Add common DMA operations
2011-07-13 8:47 ` [PATCH V2 05/12] ARM: SAMSUNG: Add common DMA operations Kukjin Kim
@ 2011-07-15 2:53 ` Grant Likely
2011-07-16 0:39 ` Boojin Kim
0 siblings, 1 reply; 23+ messages in thread
From: Grant Likely @ 2011-07-15 2:53 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, Jul 13, 2011 at 05:47:30PM +0900, Kukjin Kim wrote:
> From: Boojin Kim <boojin.kim@samsung.com>
>
> This patch adds common DMA operations which are used for Samsung DMA
> drivers. Currently there are two types of DMA driver for Samsung SoCs.
> The one is S3C-DMA for S3C SoCs and the other is PL330-DMA for S5P SoCs.
> This patch provides funcion pointers for common DMA operations to DMA
> client driver like SPI and Audio. It makes DMA client drivers support
> multi-platform.
> In addition, this common DMA operations implement the shared actions
> that are needed for DMA client driver. For example shared actions are
> filter() function for dma_request_channel() and parameter passing for
> device_prep_slave_sg().
>
> Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> ---
> arch/arm/mach-s3c2410/include/mach/dma.h | 3 +-
> arch/arm/plat-samsung/Makefile | 6 +-
> arch/arm/plat-samsung/dma-ops.c | 131 +++++++++++++++++++++++
> arch/arm/plat-samsung/include/plat/dma-ops.h | 47 +++++++++
> arch/arm/plat-samsung/include/plat/dma-pl330.h | 3 +
> arch/arm/plat-samsung/include/plat/dma.h | 2 +-
> arch/arm/plat-samsung/s3c-dma-ops.c | 132 ++++++++++++++++++++++++
> 7 files changed, 320 insertions(+), 4 deletions(-)
> create mode 100644 arch/arm/plat-samsung/dma-ops.c
> create mode 100644 arch/arm/plat-samsung/include/plat/dma-ops.h
> create mode 100644 arch/arm/plat-samsung/s3c-dma-ops.c
>
> diff --git a/arch/arm/mach-s3c2410/include/mach/dma.h b/arch/arm/mach-s3c2410/include/mach/dma.h
> index b2b2a5b..71a662d 100644
> --- a/arch/arm/mach-s3c2410/include/mach/dma.h
> +++ b/arch/arm/mach-s3c2410/include/mach/dma.h
> @@ -13,7 +13,6 @@
> #ifndef __ASM_ARCH_DMA_H
> #define __ASM_ARCH_DMA_H __FILE__
>
> -#include <plat/dma.h>
> #include <linux/sysdev.h>
>
> #define MAX_DMA_TRANSFER_SIZE 0x100000 /* Data Unit is half word */
> @@ -51,6 +50,8 @@ enum dma_ch {
> DMACH_MAX, /* the end entry */
> };
>
> +#include <plat/dma.h>
> +
> #define DMACH_LOW_LEVEL (1<<28) /* use this to specifiy hardware ch no */
>
> /* we have 4 dma channels */
> diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile
> index 53eb15b..9ecf2aa 100644
> --- a/arch/arm/plat-samsung/Makefile
> +++ b/arch/arm/plat-samsung/Makefile
> @@ -62,9 +62,11 @@ obj-$(CONFIG_SAMSUNG_DEV_PWM) += dev-pwm.o
>
> # DMA support
>
> -obj-$(CONFIG_S3C_DMA) += dma.o
> +obj-$(CONFIG_S3C_DMA) += dma.o s3c-dma-ops.o
>
> -obj-$(CONFIG_S3C_PL330_DMA) += s3c-pl330.o
> +obj-$(CONFIG_DMADEV_PL330) += dma-ops.o
> +
> +obj-$(CONFIG_S3C_PL330_DMA) += s3c-pl330.o s3c-dma-ops.o
>
> # PM support
>
> diff --git a/arch/arm/plat-samsung/dma-ops.c b/arch/arm/plat-samsung/dma-ops.c
> new file mode 100644
> index 0000000..94a9d33
> --- /dev/null
> +++ b/arch/arm/plat-samsung/dma-ops.c
> @@ -0,0 +1,131 @@
> +/* linux/arch/arm/plat-samsung/dma-ops.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com
> + *
> + * Samsung DMA Operations
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/dmaengine.h>
> +#include <linux/amba/pl330.h>
> +
> +#include <mach/dma.h>
> +
> +static bool filter(struct dma_chan *chan, void *param)
> +{
> + struct dma_pl330_peri *peri = (struct dma_pl330_peri *)chan->private;
Is this code pl330 specific? If so, 'pl330' should probably be
referenced in the function name.
> + unsigned dma_ch = (unsigned)param;
> +
> + if (peri->peri_id != dma_ch)
> + return false;
> +
> + return true;
> +}
> +
> +static unsigned dma_request(enum dma_ch dma_ch, struct samsung_dma_info *info)
> +{
> + struct dma_chan *chan;
> + dma_cap_mask_t mask;
> + struct dma_slave_config slave_config;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(info->cap, mask);
> +
> + chan = dma_request_channel(mask, filter, (void *)dma_ch);
> +
> + if (info->direction == DMA_FROM_DEVICE) {
> + memset(&slave_config, 0, sizeof(struct dma_slave_config));
> + slave_config.direction = info->direction;
> + slave_config.src_addr = info->fifo;
> + slave_config.src_addr_width = info->width;
> + dmaengine_slave_config(chan, &slave_config);
> + } else if (info->direction == DMA_TO_DEVICE) {
> + memset(&slave_config, 0, sizeof(struct dma_slave_config));
> + slave_config.direction = info->direction;
> + slave_config.dst_addr = info->fifo;
> + slave_config.dst_addr_width = info->width;
> + dmaengine_slave_config(chan, &slave_config);
> + }
> +
> + return (unsigned)chan;
> +}
> +
> +static int dma_release(unsigned ch, struct s3c2410_dma_client *client)
> +{
> + dma_release_channel((struct dma_chan *)ch);
> +
> + return 0;
> +}
> +
> +static int dma_prepare(unsigned ch, struct samsung_dma_prep_info *info)
> +{
> + struct scatterlist sg;
> + struct dma_chan *chan = (struct dma_chan *)ch;
> + struct dma_async_tx_descriptor *desc;
> +
> + switch (info->cap) {
> + case DMA_SLAVE:
> + sg_init_table(&sg, 1);
> + sg_dma_len(&sg) = info->len;
> + sg_set_page(&sg, pfn_to_page(PFN_DOWN(info->buf)),
> + info->len, offset_in_page(info->buf));
> + sg_dma_address(&sg) = info->buf;
> +
> + desc = chan->device->device_prep_slave_sg(chan,
> + &sg, 1, info->direction, DMA_PREP_INTERRUPT);
> + break;
> + case DMA_CYCLIC:
> + desc = chan->device->device_prep_dma_cyclic(chan,
> + info->buf, info->len, info->period, info->direction);
> + break;
> + default:
> + dev_err(&chan->dev->device, "unsupported format\n");
> + return -EFAULT;
> + }
> +
> + if (!desc) {
> + dev_err(&chan->dev->device, "cannot prepare cyclic dma\n");
> + return -EFAULT;
> + }
> +
> + desc->callback = info->fp;
> + desc->callback_param = info->fp_param;
> +
> + dmaengine_submit((struct dma_async_tx_descriptor *)desc);
> +
> + return 0;
> +}
> +
> +static inline int dma_trigger(unsigned ch)
> +{
> + dma_async_issue_pending((struct dma_chan *)ch);
> +
> + return 0;
> +}
> +
> +static inline int dma_flush(unsigned ch)
> +{
> + return dmaengine_terminate_all((struct dma_chan *)ch);
> +}
> +
> +static struct samsung_dma_ops dmaeng_ops = {
> + .request = dma_request,
> + .release = dma_release,
> + .prepare = dma_prepare,
> + .trigger = dma_trigger,
> + .started = NULL,
> + .flush = dma_flush,
> + .stop = dma_flush,
> +};
Even though these function are all local statics, you should use a
samsung prefix to avoid namespace collisions. So, they should be
named something like: samsung_dmaeng_ops, samsung_dmaeng_request(),
samsung_dmaeng_release(), etc. The ops structure and the functions
should have the same prefix.
> +
> +void *samsung_dma_get_ops(void)
> +{
> + return (void *)&dmaeng_ops;
> +}
> +EXPORT_SYMBOL(samsung_dma_get_ops);
If all that is needed is a reference to the dma ops, then you could
simply export samsung_dmaeng_ops() without a separate function.
> diff --git a/arch/arm/plat-samsung/include/plat/dma-ops.h b/arch/arm/plat-samsung/include/plat/dma-ops.h
> new file mode 100644
> index 0000000..eea4130
> --- /dev/null
> +++ b/arch/arm/plat-samsung/include/plat/dma-ops.h
> @@ -0,0 +1,47 @@
> +/* arch/arm/plat-samsung/include/plat/dma-ops.h
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com
> + *
> + * Samsung DMA support
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#include <linux/dmaengine.h>
> +
> +struct samsung_dma_prep_info {
> + enum dma_transaction_type cap;
> + enum dma_data_direction direction;
> + unsigned buf;
> + unsigned long period;
> + unsigned long len;
> + void (*fp)(void *data);
> + void *fp_param;
> +};
> +
> +struct samsung_dma_info {
> + enum dma_transaction_type cap;
> + enum dma_data_direction direction;
> + enum dma_slave_buswidth width;
> + unsigned fifo;
> + struct s3c2410_dma_client *client;
> +};
> +
> +struct samsung_dma_ops {
> + unsigned (*request)(enum dma_ch ch, struct samsung_dma_info *info);
> + int (*release)(unsigned ch, struct s3c2410_dma_client *client);
> + int (*prepare)(unsigned ch, struct samsung_dma_prep_info *info);
> + int (*trigger)(unsigned ch);
> + int (*started)(unsigned ch);
> + int (*flush)(unsigned ch);
> + int (*stop)(unsigned ch);
> +};
> +
> +/*
> + * samsung_dma_get_ops
> + * get the set of dma operations
> + */
> +extern void *samsung_dma_get_ops(void);
> diff --git a/arch/arm/plat-samsung/include/plat/dma-pl330.h b/arch/arm/plat-samsung/include/plat/dma-pl330.h
> index c402719..be84bec 100644
> --- a/arch/arm/plat-samsung/include/plat/dma-pl330.h
> +++ b/arch/arm/plat-samsung/include/plat/dma-pl330.h
> @@ -1,4 +1,7 @@
> /*
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com
> + *
> * Copyright (C) 2010 Samsung Electronics Co. Ltd.
> * Jaswinder Singh <jassi.brar@samsung.com>
> *
Heh, this patch doesn't make any changes to this file, and samsung
already has a copyright notice on it anyway. You should probably drop
this hunk.
> diff --git a/arch/arm/plat-samsung/include/plat/dma.h b/arch/arm/plat-samsung/include/plat/dma.h
> index 2e8f8c6..90da162 100644
> --- a/arch/arm/plat-samsung/include/plat/dma.h
> +++ b/arch/arm/plat-samsung/include/plat/dma.h
> @@ -124,4 +124,4 @@ extern int s3c2410_dma_getposition(unsigned int channel,
> extern int s3c2410_dma_set_opfn(unsigned int, s3c2410_dma_opfn_t rtn);
> extern int s3c2410_dma_set_buffdone_fn(unsigned int, s3c2410_dma_cbfn_t rtn);
>
> -
nitpick: Unrelated whitespace change. One blank line of whitespace is
sufficient anyway.
> +#include <plat/dma-ops.h>
> diff --git a/arch/arm/plat-samsung/s3c-dma-ops.c b/arch/arm/plat-samsung/s3c-dma-ops.c
> new file mode 100644
> index 0000000..17b1be0
> --- /dev/null
> +++ b/arch/arm/plat-samsung/s3c-dma-ops.c
> @@ -0,0 +1,132 @@
> +/* linux/arch/arm/plat-samsung/s3c-dma-ops.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com
> + *
> + * Samsung S3C-DMA Operations
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +#include <mach/dma.h>
> +
> +struct cb_data {
struct s3c2410_cb_data {
> + void (*fp) (void *);
> + void *fp_param;
> + unsigned ch;
> + struct list_head node;
> +};
> +
> +static LIST_HEAD(dma_list);
> +
> +static void dma_cb(struct s3c2410_dma_chan *channel,
> + void *param, int size, enum s3c2410_dma_buffresult res)
> +{
> + struct cb_data *data = (struct cb_data *)param;
param is a void*. The (struct cb_data*) cast is not needed.
> +
> + data->fp(data->fp_param);
> +}
> +
> +static unsigned dma_request(enum dma_ch dma_ch, struct samsung_dma_info *info)
These functions should *definitely* be named differently from the
dma_* ops in the other file so that you can differentiate between
them, and to make them grep-friendly. This goes for *all* file-scope
symbols.
> +{
> + struct cb_data *data;
> +
> + if (s3c2410_dma_request(dma_ch, info->client, NULL) < 0) {
> + s3c2410_dma_free(dma_ch, info->client);
> + return 0;
> + }
> +
> + data = kmalloc(sizeof(struct cb_data), GFP_KERNEL);
kzalloc()
> + data->fp = NULL;
Drop this line after converting to kzalloc()
> + data->ch = (unsigned)dma_ch;
Is the cast necessary?
> + list_add_tail(&data->node, &dma_list);
> +
> + if (info->direction == DMA_FROM_DEVICE)
> + s3c2410_dma_devconfig(dma_ch, S3C2410_DMASRC_HW, info->fifo);
> + else
> + s3c2410_dma_devconfig(dma_ch, S3C2410_DMASRC_MEM, info->fifo);
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH V2 05/12] ARM: SAMSUNG: Add common DMA operations
2011-07-15 2:53 ` Grant Likely
@ 2011-07-16 0:39 ` Boojin Kim
2011-07-16 0:50 ` Grant Likely
0 siblings, 1 reply; 23+ messages in thread
From: Boojin Kim @ 2011-07-16 0:39 UTC (permalink / raw)
To: linux-arm-kernel
Grant Likely wrote:
> On Wed, Jul 13, 2011 at 05:47:30PM +0900, Kukjin Kim wrote:
> > From: Boojin Kim <boojin.kim@samsung.com>
> >
> > This patch adds common DMA operations which are used for Samsung DMA
> > drivers. Currently there are two types of DMA driver for Samsung SoCs.
> > The one is S3C-DMA for S3C SoCs and the other is PL330-DMA for S5P SoCs.
> > This patch provides funcion pointers for common DMA operations to DMA
> > client driver like SPI and Audio. It makes DMA client drivers support
> > multi-platform.
> > In addition, this common DMA operations implement the shared actions
> > that are needed for DMA client driver. For example shared actions are
> > filter() function for dma_request_channel() and parameter passing for
> > device_prep_slave_sg().
> >
> > Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
> > Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> > ---
> > arch/arm/mach-s3c2410/include/mach/dma.h | 3 +-
> > arch/arm/plat-samsung/Makefile | 6 +-
> > arch/arm/plat-samsung/dma-ops.c | 131
> +++++++++++++++++++++++
> > arch/arm/plat-samsung/include/plat/dma-ops.h | 47 +++++++++
> > arch/arm/plat-samsung/include/plat/dma-pl330.h | 3 +
> > arch/arm/plat-samsung/include/plat/dma.h | 2 +-
> > arch/arm/plat-samsung/s3c-dma-ops.c | 132
> ++++++++++++++++++++++++
> > 7 files changed, 320 insertions(+), 4 deletions(-)
> > create mode 100644 arch/arm/plat-samsung/dma-ops.c
> > create mode 100644 arch/arm/plat-samsung/include/plat/dma-ops.h
> > create mode 100644 arch/arm/plat-samsung/s3c-dma-ops.c
> >
> > diff --git a/arch/arm/mach-s3c2410/include/mach/dma.h b/arch/arm/mach-
> s3c2410/include/mach/dma.h
> > index b2b2a5b..71a662d 100644
> > --- a/arch/arm/mach-s3c2410/include/mach/dma.h
> > +++ b/arch/arm/mach-s3c2410/include/mach/dma.h
> > @@ -13,7 +13,6 @@
> > #ifndef __ASM_ARCH_DMA_H
> > #define __ASM_ARCH_DMA_H __FILE__
> >
> > -#include <plat/dma.h>
> > #include <linux/sysdev.h>
> >
> > #define MAX_DMA_TRANSFER_SIZE 0x100000 /* Data Unit is half word */
> > @@ -51,6 +50,8 @@ enum dma_ch {
> > DMACH_MAX, /* the end entry */
> > };
> >
> > +#include <plat/dma.h>
> > +
> > #define DMACH_LOW_LEVEL (1<<28) /* use this to specifiy hardware
> ch no */
> >
> > /* we have 4 dma channels */
> > diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-
> samsung/Makefile
> > index 53eb15b..9ecf2aa 100644
> > --- a/arch/arm/plat-samsung/Makefile
> > +++ b/arch/arm/plat-samsung/Makefile
> > @@ -62,9 +62,11 @@ obj-$(CONFIG_SAMSUNG_DEV_PWM) += dev-pwm.o
> >
> > # DMA support
> >
> > -obj-$(CONFIG_S3C_DMA) += dma.o
> > +obj-$(CONFIG_S3C_DMA) += dma.o s3c-dma-ops.o
> >
> > -obj-$(CONFIG_S3C_PL330_DMA) += s3c-pl330.o
> > +obj-$(CONFIG_DMADEV_PL330) += dma-ops.o
> > +
> > +obj-$(CONFIG_S3C_PL330_DMA) += s3c-pl330.o s3c-dma-ops.o
> >
> > # PM support
> >
> > diff --git a/arch/arm/plat-samsung/dma-ops.c b/arch/arm/plat-
> samsung/dma-ops.c
> > new file mode 100644
> > index 0000000..94a9d33
> > --- /dev/null
> > +++ b/arch/arm/plat-samsung/dma-ops.c
> > @@ -0,0 +1,131 @@
> > +/* linux/arch/arm/plat-samsung/dma-ops.c
> > + *
> > + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> > + * http://www.samsung.com
> > + *
> > + * Samsung DMA Operations
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > +*/
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/errno.h>
> > +#include <linux/dmaengine.h>
> > +#include <linux/amba/pl330.h>
> > +
> > +#include <mach/dma.h>
> > +
> > +static bool filter(struct dma_chan *chan, void *param)
> > +{
> > + struct dma_pl330_peri *peri = (struct dma_pl330_peri *)chan-
> >private;
>
> Is this code pl330 specific? If so, 'pl330' should probably be
> referenced in the function name.
I will address your comment.
>
> > + unsigned dma_ch = (unsigned)param;
> > +
> > + if (peri->peri_id != dma_ch)
> > + return false;
> > +
> > + return true;
> > +}
> > +
> > +static unsigned dma_request(enum dma_ch dma_ch, struct samsung_dma_info
> *info)
> > +{
> > + struct dma_chan *chan;
> > + dma_cap_mask_t mask;
> > + struct dma_slave_config slave_config;
> > +
> > + dma_cap_zero(mask);
> > + dma_cap_set(info->cap, mask);
> > +
> > + chan = dma_request_channel(mask, filter, (void *)dma_ch);
> > +
> > + if (info->direction == DMA_FROM_DEVICE) {
> > + memset(&slave_config, 0, sizeof(struct dma_slave_config));
> > + slave_config.direction = info->direction;
> > + slave_config.src_addr = info->fifo;
> > + slave_config.src_addr_width = info->width;
> > + dmaengine_slave_config(chan, &slave_config);
> > + } else if (info->direction == DMA_TO_DEVICE) {
> > + memset(&slave_config, 0, sizeof(struct dma_slave_config));
> > + slave_config.direction = info->direction;
> > + slave_config.dst_addr = info->fifo;
> > + slave_config.dst_addr_width = info->width;
> > + dmaengine_slave_config(chan, &slave_config);
> > + }
> > +
> > + return (unsigned)chan;
> > +}
> > +
> > +static int dma_release(unsigned ch, struct s3c2410_dma_client *client)
> > +{
> > + dma_release_channel((struct dma_chan *)ch);
> > +
> > + return 0;
> > +}
> > +
> > +static int dma_prepare(unsigned ch, struct samsung_dma_prep_info *info)
> > +{
> > + struct scatterlist sg;
> > + struct dma_chan *chan = (struct dma_chan *)ch;
> > + struct dma_async_tx_descriptor *desc;
> > +
> > + switch (info->cap) {
> > + case DMA_SLAVE:
> > + sg_init_table(&sg, 1);
> > + sg_dma_len(&sg) = info->len;
> > + sg_set_page(&sg, pfn_to_page(PFN_DOWN(info->buf)),
> > + info->len, offset_in_page(info->buf));
> > + sg_dma_address(&sg) = info->buf;
> > +
> > + desc = chan->device->device_prep_slave_sg(chan,
> > + &sg, 1, info->direction, DMA_PREP_INTERRUPT);
> > + break;
> > + case DMA_CYCLIC:
> > + desc = chan->device->device_prep_dma_cyclic(chan,
> > + info->buf, info->len, info->period,
info->direction);
> > + break;
> > + default:
> > + dev_err(&chan->dev->device, "unsupported format\n");
> > + return -EFAULT;
> > + }
> > +
> > + if (!desc) {
> > + dev_err(&chan->dev->device, "cannot prepare cyclic dma\n");
> > + return -EFAULT;
> > + }
> > +
> > + desc->callback = info->fp;
> > + desc->callback_param = info->fp_param;
> > +
> > + dmaengine_submit((struct dma_async_tx_descriptor *)desc);
> > +
> > + return 0;
> > +}
> > +
> > +static inline int dma_trigger(unsigned ch)
> > +{
> > + dma_async_issue_pending((struct dma_chan *)ch);
> > +
> > + return 0;
> > +}
> > +
> > +static inline int dma_flush(unsigned ch)
> > +{
> > + return dmaengine_terminate_all((struct dma_chan *)ch);
> > +}
> > +
> > +static struct samsung_dma_ops dmaeng_ops = {
> > + .request = dma_request,
> > + .release = dma_release,
> > + .prepare = dma_prepare,
> > + .trigger = dma_trigger,
> > + .started = NULL,
> > + .flush = dma_flush,
> > + .stop = dma_flush,
> > +};
>
> Even though these function are all local statics, you should use a
> samsung prefix to avoid namespace collisions. So, they should be
> named something like: samsung_dmaeng_ops, samsung_dmaeng_request(),
> samsung_dmaeng_release(), etc. The ops structure and the functions
> should have the same prefix.
I will address your comment.
>
> > +
> > +void *samsung_dma_get_ops(void)
> > +{
> > + return (void *)&dmaeng_ops;
> > +}
> > +EXPORT_SYMBOL(samsung_dma_get_ops);
>
> If all that is needed is a reference to the dma ops, then you could
> simply export samsung_dmaeng_ops() without a separate function.
Grant, Thanks for your comments. I can't understand this comment well.
Do you mean to change function name from 'samsung_dma_get_ops()' to
'samsung_dmaeng_ops()' ?
Or export 'dmaeng_ops' variable instead of making this
'samsung_dma_get_ops()' function
>
> > diff --git a/arch/arm/plat-samsung/include/plat/dma-ops.h
> b/arch/arm/plat-samsung/include/plat/dma-ops.h
> > new file mode 100644
> > index 0000000..eea4130
> > --- /dev/null
> > +++ b/arch/arm/plat-samsung/include/plat/dma-ops.h
> > @@ -0,0 +1,47 @@
> > +/* arch/arm/plat-samsung/include/plat/dma-ops.h
> > + *
> > + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> > + * http://www.samsung.com
> > + *
> > + * Samsung DMA support
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > +*/
> > +
> > +#include <linux/dmaengine.h>
> > +
> > +struct samsung_dma_prep_info {
> > + enum dma_transaction_type cap;
> > + enum dma_data_direction direction;
> > + unsigned buf;
> > + unsigned long period;
> > + unsigned long len;
> > + void (*fp)(void *data);
> > + void *fp_param;
> > +};
> > +
> > +struct samsung_dma_info {
> > + enum dma_transaction_type cap;
> > + enum dma_data_direction direction;
> > + enum dma_slave_buswidth width;
> > + unsigned fifo;
> > + struct s3c2410_dma_client *client;
> > +};
> > +
> > +struct samsung_dma_ops {
> > + unsigned (*request)(enum dma_ch ch, struct samsung_dma_info *info);
> > + int (*release)(unsigned ch, struct s3c2410_dma_client *client);
> > + int (*prepare)(unsigned ch, struct samsung_dma_prep_info *info);
> > + int (*trigger)(unsigned ch);
> > + int (*started)(unsigned ch);
> > + int (*flush)(unsigned ch);
> > + int (*stop)(unsigned ch);
> > +};
> > +
> > +/*
> > + * samsung_dma_get_ops
> > + * get the set of dma operations
> > + */
> > +extern void *samsung_dma_get_ops(void);
> > diff --git a/arch/arm/plat-samsung/include/plat/dma-pl330.h
> b/arch/arm/plat-samsung/include/plat/dma-pl330.h
> > index c402719..be84bec 100644
> > --- a/arch/arm/plat-samsung/include/plat/dma-pl330.h
> > +++ b/arch/arm/plat-samsung/include/plat/dma-pl330.h
> > @@ -1,4 +1,7 @@
> > /*
> > + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> > + * http://www.samsung.com
> > + *
> > * Copyright (C) 2010 Samsung Electronics Co. Ltd.
> > * Jaswinder Singh <jassi.brar@samsung.com>
> > *
>
>
> Heh, this patch doesn't make any changes to this file, and samsung
> already has a copyright notice on it anyway. You should probably drop
> this hunk.
I will address your comment.
>
> > diff --git a/arch/arm/plat-samsung/include/plat/dma.h b/arch/arm/plat-
> samsung/include/plat/dma.h
> > index 2e8f8c6..90da162 100644
> > --- a/arch/arm/plat-samsung/include/plat/dma.h
> > +++ b/arch/arm/plat-samsung/include/plat/dma.h
> > @@ -124,4 +124,4 @@ extern int s3c2410_dma_getposition(unsigned int
> channel,
> > extern int s3c2410_dma_set_opfn(unsigned int, s3c2410_dma_opfn_t rtn);
> > extern int s3c2410_dma_set_buffdone_fn(unsigned int, s3c2410_dma_cbfn_t
> rtn);
> >
> > -
>
> nitpick: Unrelated whitespace change. One blank line of whitespace is
> sufficient anyway.
I will address your comment.
> > +#include <plat/dma-ops.h>
> > diff --git a/arch/arm/plat-samsung/s3c-dma-ops.c b/arch/arm/plat-
> samsung/s3c-dma-ops.c
> > new file mode 100644
> > index 0000000..17b1be0
> > --- /dev/null
> > +++ b/arch/arm/plat-samsung/s3c-dma-ops.c
> > @@ -0,0 +1,132 @@
> > +/* linux/arch/arm/plat-samsung/s3c-dma-ops.c
> > + *
> > + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> > + * http://www.samsung.com
> > + *
> > + * Samsung S3C-DMA Operations
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > +*/
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/errno.h>
> > +#include <linux/slab.h>
> > +#include <linux/types.h>
> > +
> > +#include <mach/dma.h>
> > +
> > +struct cb_data {
>
> struct s3c2410_cb_data {
>
> > + void (*fp) (void *);
> > + void *fp_param;
> > + unsigned ch;
> > + struct list_head node;
> > +};
> > +
> > +static LIST_HEAD(dma_list);
> > +
> > +static void dma_cb(struct s3c2410_dma_chan *channel,
> > + void *param, int size, enum s3c2410_dma_buffresult res)
> > +{
> > + struct cb_data *data = (struct cb_data *)param;
>
> param is a void*. The (struct cb_data*) cast is not needed.
>
> > +
> > + data->fp(data->fp_param);
> > +}
> > +
> > +static unsigned dma_request(enum dma_ch dma_ch, struct samsung_dma_info
> *info)
>
> These functions should *definitely* be named differently from the
> dma_* ops in the other file so that you can differentiate between
> them, and to make them grep-friendly. This goes for *all* file-scope
> symbols.
I will address your comment.
>
> > +{
> > + struct cb_data *data;
> > +
> > + if (s3c2410_dma_request(dma_ch, info->client, NULL) < 0) {
> > + s3c2410_dma_free(dma_ch, info->client);
> > + return 0;
> > + }
> > +
> > + data = kmalloc(sizeof(struct cb_data), GFP_KERNEL);
>
> kzalloc()
>
> > + data->fp = NULL;
>
> Drop this line after converting to kzalloc()
I will address your comment.
>
> > + data->ch = (unsigned)dma_ch;
>
> Is the cast necessary?
>
> > + list_add_tail(&data->node, &dma_list);
> > +
> > + if (info->direction == DMA_FROM_DEVICE)
> > + s3c2410_dma_devconfig(dma_ch, S3C2410_DMASRC_HW,
info->fifo);
> > + else
> > + s3c2410_dma_devconfig(dma_ch, S3C2410_DMASRC_MEM, info-
> >fifo);
>
> From arch/arm/plat-samsung/include/plat/dma.h:
> enum s3c2410_dmasrc {
> S3C2410_DMASRC_HW, /* source is memory */
> S3C2410_DMASRC_MEM /* source is hardware */
> };
>
> from dma-mapping.h:
> enum dma_data_direction {
> DMA_BIDIRECTIONAL = 0,
> DMA_TO_DEVICE = 1,
> DMA_FROM_DEVICE = 2,
> DMA_NONE = 3,
> };
>
> /me thinks it would all look nicer if s3c2410 dma just replaced
> s3c2410_dmasrc to dma_data_direction, and from what I can tell it
> would just be a simple search and replace. :-)
>
I will address your comment.
> > +
> > + if (info->cap == DMA_CYCLIC)
> > + s3c2410_dma_setflags(dma_ch, S3C2410_DMAF_CIRCULAR);
> > +
> > + s3c2410_dma_config(dma_ch, info->width);
> > +
> > + return (unsigned)dma_ch;
> > +}
> > +
> > +static int dma_release(unsigned ch, struct s3c2410_dma_client *client)
>
> unsigned int
>
> > +{
> > + struct cb_data *data;
> > +
> > + list_for_each_entry(data, &dma_list, node)
> > + if (data->ch == ch)
> > + break;
>
> nit: incorrect indentation. Use tab characters instead of spaces.
> I've got "set list" and "set listchars=tab:\|-,trail:-" in my ~/.vimrc
> so I can see the difference between tabs and spaces.
>
I will address your comment.
> > + list_del(&data->node);
>
> What happens if the channel isn't found in the list? Can that
> situation happen?
>
> What happens if two drivers call dma_release simultaneously? It
> looks like a mutex is needed to protext the dma_list.
>
> > +
> > + s3c2410_dma_free((enum dma_ch)ch, client);
>
> All the casting in this patch makes me nervous. When a lot of casting
> is required, I wonder if the API needs to be changed.
I will remove casting to "enum dma_ch"
>
> > + kfree(data);
> > +
> > + return 0;
> > +}
> > +
> > +static int dma_prepare(unsigned ch, struct samsung_dma_prep_info *info)
> > +{
> > + struct cb_data *data;
> > +
> > + list_for_each_entry(data, &dma_list, node)
> > + if (data->ch == ch)
> > + break;
> > +
> > + if (!data->fp) {
> > + s3c2410_dma_set_buffdone_fn((enum dma_ch)ch, dma_cb);
> > + data->fp = info->fp;
> > + data->fp_param = info->fp_param;
> > + }
> > + s3c2410_dma_enqueue((enum dma_ch)ch, (void *)data, info->buf,
> > + info->period);
> > +
> > + return 0;
> > +}
> > +
> > +static inline int dma_trigger(unsigned ch)
> > +{
> > + return s3c2410_dma_ctrl(ch, S3C2410_DMAOP_START);
> > +}
> > +
> > +static inline int dma_started(unsigned ch)
> > +{
> > + return s3c2410_dma_ctrl(ch, S3C2410_DMAOP_STARTED);
> > +}
> > +
> > +static inline int dma_flush(unsigned ch)
> > +{
> > + return s3c2410_dma_ctrl(ch, S3C2410_DMAOP_FLUSH);
> > +}
> > +
> > +static inline int dma_stop(unsigned ch)
> > +{
> > + return s3c2410_dma_ctrl(ch, S3C2410_DMAOP_STOP);
> > +}
> > +
> > +static struct samsung_dma_ops s3c_dma_ops = {
> > + .request = dma_request,
> > + .release = dma_release,
> > + .prepare = dma_prepare,
> > + .trigger = dma_trigger,
> > + .started = dma_started,
> > + .flush = dma_flush,
> > + .stop = dma_stop,
> > +};
> > +
> > +void *samsung_dma_get_ops(void)
> > +{
> > + return (void *)&s3c_dma_ops;
> > +}
> > +EXPORT_SYMBOL(samsung_dma_get_ops);
>
> This is a problem. This patch adds two implementations of
> samsung_dma_get_ops(). New code needs to be multiplatform friendly.
> That means that the code nees to handle both dma-ops.c and
> s3c-dma-ops.c compiled into the kernel at the same time and select the
> correct one at runtime.
>
I will address your comment.
> g.
>
> > --
> > 1.7.1
> >
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH V2 05/12] ARM: SAMSUNG: Add common DMA operations
2011-07-16 0:39 ` Boojin Kim
@ 2011-07-16 0:50 ` Grant Likely
0 siblings, 0 replies; 23+ messages in thread
From: Grant Likely @ 2011-07-16 0:50 UTC (permalink / raw)
To: linux-arm-kernel
On Sat, Jul 16, 2011 at 09:39:31AM +0900, Boojin Kim wrote:
> Grant Likely wrote:
> > > +void *samsung_dma_get_ops(void)
> > > +{
> > > + return (void *)&dmaeng_ops;
> > > +}
> > > +EXPORT_SYMBOL(samsung_dma_get_ops);
> >
> > If all that is needed is a reference to the dma ops, then you could
> > simply export samsung_dmaeng_ops() without a separate function.
>
> Grant, Thanks for your comments. I can't understand this comment well.
> Do you mean to change function name from 'samsung_dma_get_ops()' to
> 'samsung_dmaeng_ops()' ?
> Or export 'dmaeng_ops' variable instead of making this
> 'samsung_dma_get_ops()' function
I mean export dmaeng_ops instead of using a function. You also get
some type checking by not casting it into a (void*).
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH V2 06/12] ARM: EXYNOS4: Use generic DMA PL330 driver
2011-07-13 8:47 [PATCH V2 00/12] To use DMA generic APIs for Samsung DMA Kukjin Kim
` (4 preceding siblings ...)
2011-07-13 8:47 ` [PATCH V2 05/12] ARM: SAMSUNG: Add common DMA operations Kukjin Kim
@ 2011-07-13 8:47 ` Kukjin Kim
2011-07-13 8:47 ` [PATCH V2 07/12] ARM: S5PV210: " Kukjin Kim
` (5 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Kukjin Kim @ 2011-07-13 8:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Boojin Kim <boojin.kim@samsung.com>
This patch makes Samsung EXYNOS4 to use DMA PL330 driver
on DMADEVICE. The EXYNOS4 uses DMA generic APIs instead of
SAMSUNG specific S3C-PL330 APIs.
Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
arch/arm/mach-exynos4/Kconfig | 2 +-
arch/arm/mach-exynos4/clock.c | 16 +-
arch/arm/mach-exynos4/dma.c | 323 +++++++++++++++++++++++++++--------------
3 files changed, 221 insertions(+), 120 deletions(-)
diff --git a/arch/arm/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig
index 1435fc3..5cc9b7a 100644
--- a/arch/arm/mach-exynos4/Kconfig
+++ b/arch/arm/mach-exynos4/Kconfig
@@ -11,7 +11,7 @@ if ARCH_EXYNOS4
config CPU_EXYNOS4210
bool
- select S3C_PL330_DMA
+ select DMADEV_PL330
help
Enable EXYNOS4210 CPU support
diff --git a/arch/arm/mach-exynos4/clock.c b/arch/arm/mach-exynos4/clock.c
index 871f9d5..1b2e78c 100644
--- a/arch/arm/mach-exynos4/clock.c
+++ b/arch/arm/mach-exynos4/clock.c
@@ -47,6 +47,11 @@ static struct clk clk_sclk_usbphy1 = {
.id = -1,
};
+static struct clk dummy_apb_pclk = {
+ .name = "apb_pclk",
+ .id = -1,
+};
+
static int exynos4_clksrc_mask_top_ctrl(struct clk *clk, int enable)
{
return s5p_gatectrl(S5P_CLKSRC_MASK_TOP, clk, enable);
@@ -485,16 +490,11 @@ static struct clk init_clocks_off[] = {
.enable = exynos4_clk_ip_fsys_ctrl,
.ctrlbit = (1 << 10),
}, {
- .name = "pdma",
- .id = 0,
+ .name = "dma",
+ .id = -1,
.enable = exynos4_clk_ip_fsys_ctrl,
.ctrlbit = (1 << 0),
}, {
- .name = "pdma",
- .id = 1,
- .enable = exynos4_clk_ip_fsys_ctrl,
- .ctrlbit = (1 << 1),
- }, {
.name = "adc",
.id = -1,
.enable = exynos4_clk_ip_peril_ctrl,
@@ -1212,5 +1212,7 @@ void __init exynos4_register_clocks(void)
s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
+ s3c24xx_register_clock(&dummy_apb_pclk);
+
s3c_pwmclk_init();
}
diff --git a/arch/arm/mach-exynos4/dma.c b/arch/arm/mach-exynos4/dma.c
index 564bb53..e1c00cf 100644
--- a/arch/arm/mach-exynos4/dma.c
+++ b/arch/arm/mach-exynos4/dma.c
@@ -21,151 +21,250 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/pl330.h>
+#include <asm/irq.h>
#include <plat/devs.h>
#include <plat/irqs.h>
#include <mach/map.h>
#include <mach/irqs.h>
-
-#include <plat/s3c-pl330-pdata.h>
+#include <mach/dma.h>
static u64 dma_dmamask = DMA_BIT_MASK(32);
-static struct resource exynos4_pdma0_resource[] = {
- [0] = {
- .start = EXYNOS4_PA_PDMA0,
- .end = EXYNOS4_PA_PDMA0 + SZ_4K,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_PDMA0,
- .end = IRQ_PDMA0,
- .flags = IORESOURCE_IRQ,
+struct dma_pl330_peri pdma0_peri[32] = {
+ {
+ .peri_id = (u8)DMACH_PCM0_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_PCM0_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_PCM2_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_PCM2_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_MSM_REQ0,
+ }, {
+ .peri_id = (u8)DMACH_MSM_REQ2,
+ }, {
+ .peri_id = (u8)DMACH_SPI0_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_SPI0_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_SPI2_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_SPI2_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_I2S0S_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_I2S0_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_I2S0_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_UART0_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_UART0_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_UART2_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_UART2_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_UART4_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_UART4_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_SLIMBUS0_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_SLIMBUS0_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_SLIMBUS2_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_SLIMBUS2_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_SLIMBUS4_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_SLIMBUS4_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_AC97_MICIN,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_AC97_PCMIN,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_AC97_PCMOUT,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
},
};
-static struct s3c_pl330_platdata exynos4_pdma0_pdata = {
- .peri = {
- [0] = DMACH_PCM0_RX,
- [1] = DMACH_PCM0_TX,
- [2] = DMACH_PCM2_RX,
- [3] = DMACH_PCM2_TX,
- [4] = DMACH_MSM_REQ0,
- [5] = DMACH_MSM_REQ2,
- [6] = DMACH_SPI0_RX,
- [7] = DMACH_SPI0_TX,
- [8] = DMACH_SPI2_RX,
- [9] = DMACH_SPI2_TX,
- [10] = DMACH_I2S0S_TX,
- [11] = DMACH_I2S0_RX,
- [12] = DMACH_I2S0_TX,
- [13] = DMACH_I2S2_RX,
- [14] = DMACH_I2S2_TX,
- [15] = DMACH_UART0_RX,
- [16] = DMACH_UART0_TX,
- [17] = DMACH_UART2_RX,
- [18] = DMACH_UART2_TX,
- [19] = DMACH_UART4_RX,
- [20] = DMACH_UART4_TX,
- [21] = DMACH_SLIMBUS0_RX,
- [22] = DMACH_SLIMBUS0_TX,
- [23] = DMACH_SLIMBUS2_RX,
- [24] = DMACH_SLIMBUS2_TX,
- [25] = DMACH_SLIMBUS4_RX,
- [26] = DMACH_SLIMBUS4_TX,
- [27] = DMACH_AC97_MICIN,
- [28] = DMACH_AC97_PCMIN,
- [29] = DMACH_AC97_PCMOUT,
- [30] = DMACH_MAX,
- [31] = DMACH_MAX,
- },
+struct dma_pl330_platdata exynos4_pdma0_pdata = {
+ .nr_valid_peri = 32,
+ .peri = pdma0_peri,
};
-static struct platform_device exynos4_device_pdma0 = {
- .name = "s3c-pl330",
- .id = 0,
- .num_resources = ARRAY_SIZE(exynos4_pdma0_resource),
- .resource = exynos4_pdma0_resource,
- .dev = {
+struct amba_device exynos4_device_pdma0 = {
+ .dev = {
+ .init_name = "dma-pl330.0",
.dma_mask = &dma_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &exynos4_pdma0_pdata,
- },
+ },
+ .res = {
+ .start = EXYNOS4_PA_PDMA0,
+ .end = EXYNOS4_PA_PDMA0 + SZ_4K,
+ .flags = IORESOURCE_MEM,
+ },
+ .irq = {IRQ_PDMA0, NO_IRQ},
+ .periphid = 0x00041330,
};
-static struct resource exynos4_pdma1_resource[] = {
- [0] = {
- .start = EXYNOS4_PA_PDMA1,
- .end = EXYNOS4_PA_PDMA1 + SZ_4K,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_PDMA1,
- .end = IRQ_PDMA1,
- .flags = IORESOURCE_IRQ,
+struct dma_pl330_peri pdma1_peri[32] = {
+ {
+ .peri_id = (u8)DMACH_PCM0_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_PCM0_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_PCM1_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_PCM1_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_MSM_REQ1,
+ }, {
+ .peri_id = (u8)DMACH_MSM_REQ3,
+ }, {
+ .peri_id = (u8)DMACH_SPI1_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_SPI1_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_I2S0S_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_I2S0_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_I2S0_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_I2S1_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_I2S1_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_UART0_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_UART0_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_UART1_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_UART1_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_UART3_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_UART3_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_SLIMBUS1_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_SLIMBUS1_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_SLIMBUS3_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_SLIMBUS3_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_SLIMBUS5_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_SLIMBUS5_TX,
+ .rqtype = MEMTODEV,
},
};
-static struct s3c_pl330_platdata exynos4_pdma1_pdata = {
- .peri = {
- [0] = DMACH_PCM0_RX,
- [1] = DMACH_PCM0_TX,
- [2] = DMACH_PCM1_RX,
- [3] = DMACH_PCM1_TX,
- [4] = DMACH_MSM_REQ1,
- [5] = DMACH_MSM_REQ3,
- [6] = DMACH_SPI1_RX,
- [7] = DMACH_SPI1_TX,
- [8] = DMACH_I2S0S_TX,
- [9] = DMACH_I2S0_RX,
- [10] = DMACH_I2S0_TX,
- [11] = DMACH_I2S1_RX,
- [12] = DMACH_I2S1_TX,
- [13] = DMACH_UART0_RX,
- [14] = DMACH_UART0_TX,
- [15] = DMACH_UART1_RX,
- [16] = DMACH_UART1_TX,
- [17] = DMACH_UART3_RX,
- [18] = DMACH_UART3_TX,
- [19] = DMACH_SLIMBUS1_RX,
- [20] = DMACH_SLIMBUS1_TX,
- [21] = DMACH_SLIMBUS3_RX,
- [22] = DMACH_SLIMBUS3_TX,
- [23] = DMACH_SLIMBUS5_RX,
- [24] = DMACH_SLIMBUS5_TX,
- [25] = DMACH_SLIMBUS0AUX_RX,
- [26] = DMACH_SLIMBUS0AUX_TX,
- [27] = DMACH_SPDIF,
- [28] = DMACH_MAX,
- [29] = DMACH_MAX,
- [30] = DMACH_MAX,
- [31] = DMACH_MAX,
- },
+struct dma_pl330_platdata exynos4_pdma1_pdata = {
+ .nr_valid_peri = 32,
+ .peri = pdma1_peri,
};
-static struct platform_device exynos4_device_pdma1 = {
- .name = "s3c-pl330",
- .id = 1,
- .num_resources = ARRAY_SIZE(exynos4_pdma1_resource),
- .resource = exynos4_pdma1_resource,
- .dev = {
+struct amba_device exynos4_device_pdma1 = {
+ .dev = {
+ .init_name = "dma-pl330.1",
.dma_mask = &dma_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &exynos4_pdma1_pdata,
},
-};
-
-static struct platform_device *exynos4_dmacs[] __initdata = {
- &exynos4_device_pdma0,
- &exynos4_device_pdma1,
+ .res = {
+ .start = EXYNOS4_PA_PDMA1,
+ .end = EXYNOS4_PA_PDMA1 + SZ_4K,
+ .flags = IORESOURCE_MEM,
+ },
+ .irq = {IRQ_PDMA1, NO_IRQ},
+ .periphid = 0x00041330,
};
static int __init exynos4_dma_init(void)
{
- platform_add_devices(exynos4_dmacs, ARRAY_SIZE(exynos4_dmacs));
+ amba_device_register(&exynos4_device_pdma0, &iomem_resource);
return 0;
}
--
1.7.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH V2 07/12] ARM: S5PV210: Use generic DMA PL330 driver
2011-07-13 8:47 [PATCH V2 00/12] To use DMA generic APIs for Samsung DMA Kukjin Kim
` (5 preceding siblings ...)
2011-07-13 8:47 ` [PATCH V2 06/12] ARM: EXYNOS4: Use generic DMA PL330 driver Kukjin Kim
@ 2011-07-13 8:47 ` Kukjin Kim
2011-07-13 8:47 ` [PATCH V2 08/12] ARM: S5PC100: " Kukjin Kim
` (4 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Kukjin Kim @ 2011-07-13 8:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Boojin Kim <boojin.kim@samsung.com>
This patch makes Samsung S5PV210 to use DMA PL330 driver
on DMADEVICE. The S5PV210 uses DMA generic APIs instead of
SAMSUNG specific S3C-PL330 APIs.
Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
arch/arm/mach-s5pv210/Kconfig | 2 +-
arch/arm/mach-s5pv210/clock.c | 16 ++--
arch/arm/mach-s5pv210/dma.c | 232 +++++++++++++++++++----------------------
3 files changed, 118 insertions(+), 132 deletions(-)
diff --git a/arch/arm/mach-s5pv210/Kconfig b/arch/arm/mach-s5pv210/Kconfig
index 37b5a97..fb11e38 100644
--- a/arch/arm/mach-s5pv210/Kconfig
+++ b/arch/arm/mach-s5pv210/Kconfig
@@ -11,7 +11,7 @@ if ARCH_S5PV210
config CPU_S5PV210
bool
- select S3C_PL330_DMA
+ select DMADEV_PL330
select S5P_EXT_INT
select S5P_HRT
select S5PV210_PM if PM
diff --git a/arch/arm/mach-s5pv210/clock.c b/arch/arm/mach-s5pv210/clock.c
index 2d59949..dbe7285 100644
--- a/arch/arm/mach-s5pv210/clock.c
+++ b/arch/arm/mach-s5pv210/clock.c
@@ -221,6 +221,11 @@ static struct clk clk_pcmcdclk2 = {
.id = -1,
};
+static struct clk dummy_apb_pclk = {
+ .name = "apb_pclk",
+ .id = -1,
+};
+
static struct clk *clkset_vpllsrc_list[] = {
[0] = &clk_fin_vpll,
[1] = &clk_sclk_hdmi27m,
@@ -311,18 +316,12 @@ static struct clk_ops clk_fout_apll_ops = {
static struct clk init_clocks_off[] = {
{
- .name = "pdma",
- .id = 0,
+ .name = "dma",
+ .id = -1,
.parent = &clk_hclk_psys.clk,
.enable = s5pv210_clk_ip0_ctrl,
.ctrlbit = (1 << 3),
}, {
- .name = "pdma",
- .id = 1,
- .parent = &clk_hclk_psys.clk,
- .enable = s5pv210_clk_ip0_ctrl,
- .ctrlbit = (1 << 4),
- }, {
.name = "rot",
.id = -1,
.parent = &clk_hclk_dsys.clk,
@@ -1239,5 +1238,6 @@ void __init s5pv210_register_clocks(void)
s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
+ s3c24xx_register_clock(&dummy_apb_pclk);
s3c_pwmclk_init();
}
diff --git a/arch/arm/mach-s5pv210/dma.c b/arch/arm/mach-s5pv210/dma.c
index 497d343..1042f42 100644
--- a/arch/arm/mach-s5pv210/dma.c
+++ b/arch/arm/mach-s5pv210/dma.c
@@ -1,4 +1,8 @@
-/*
+/* linux/arch/arm/mach-s5pv210/dma.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
* Copyright (C) 2010 Samsung Electronics Co. Ltd.
* Jaswinder Singh <jassi.brar@samsung.com>
*
@@ -17,151 +21,133 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/pl330.h>
+#include <asm/irq.h>
#include <plat/devs.h>
#include <plat/irqs.h>
#include <mach/map.h>
#include <mach/irqs.h>
-
-#include <plat/s3c-pl330-pdata.h>
+#include <mach/dma.h>
static u64 dma_dmamask = DMA_BIT_MASK(32);
-static struct resource s5pv210_pdma0_resource[] = {
- [0] = {
- .start = S5PV210_PA_PDMA0,
- .end = S5PV210_PA_PDMA0 + SZ_4K,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_PDMA0,
- .end = IRQ_PDMA0,
- .flags = IORESOURCE_IRQ,
+struct dma_pl330_peri pdma0_peri[32] = {
+ {
+ .peri_id = (u8)DMACH_UART0_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART0_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_UART1_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART1_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_UART2_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART2_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_UART3_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART3_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = DMACH_MAX,
+ }, {
+ .peri_id = (u8)DMACH_I2S0_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_I2S0_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_I2S0S_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_I2S1_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_I2S1_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_MAX,
+ }, {
+ .peri_id = (u8)DMACH_MAX,
+ }, {
+ .peri_id = (u8)DMACH_SPI0_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_SPI0_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_SPI1_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_SPI1_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_MAX,
+ }, {
+ .peri_id = (u8)DMACH_MAX,
+ }, {
+ .peri_id = (u8)DMACH_AC97_MICIN,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_AC97_PCMIN,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_AC97_PCMOUT,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_MAX,
+ }, {
+ .peri_id = (u8)DMACH_PWM,
+ }, {
+ .peri_id = (u8)DMACH_SPDIF,
},
};
-static struct s3c_pl330_platdata s5pv210_pdma0_pdata = {
- .peri = {
- [0] = DMACH_UART0_RX,
- [1] = DMACH_UART0_TX,
- [2] = DMACH_UART1_RX,
- [3] = DMACH_UART1_TX,
- [4] = DMACH_UART2_RX,
- [5] = DMACH_UART2_TX,
- [6] = DMACH_UART3_RX,
- [7] = DMACH_UART3_TX,
- [8] = DMACH_MAX,
- [9] = DMACH_I2S0_RX,
- [10] = DMACH_I2S0_TX,
- [11] = DMACH_I2S0S_TX,
- [12] = DMACH_I2S1_RX,
- [13] = DMACH_I2S1_TX,
- [14] = DMACH_MAX,
- [15] = DMACH_MAX,
- [16] = DMACH_SPI0_RX,
- [17] = DMACH_SPI0_TX,
- [18] = DMACH_SPI1_RX,
- [19] = DMACH_SPI1_TX,
- [20] = DMACH_MAX,
- [21] = DMACH_MAX,
- [22] = DMACH_AC97_MICIN,
- [23] = DMACH_AC97_PCMIN,
- [24] = DMACH_AC97_PCMOUT,
- [25] = DMACH_MAX,
- [26] = DMACH_PWM,
- [27] = DMACH_SPDIF,
- [28] = DMACH_MAX,
- [29] = DMACH_MAX,
- [30] = DMACH_MAX,
- [31] = DMACH_MAX,
- },
+struct dma_pl330_platdata s5pv210_pdma0_pdata = {
+ .nr_valid_peri = 32,
+ .peri = pdma0_peri,
};
-static struct platform_device s5pv210_device_pdma0 = {
- .name = "s3c-pl330",
- .id = 0,
- .num_resources = ARRAY_SIZE(s5pv210_pdma0_resource),
- .resource = s5pv210_pdma0_resource,
- .dev = {
+struct amba_device s5pv210_device_pdma0 = {
+ .dev = {
+ .init_name = "dma-pl330.0",
.dma_mask = &dma_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &s5pv210_pdma0_pdata,
- },
-};
-
-static struct resource s5pv210_pdma1_resource[] = {
- [0] = {
- .start = S5PV210_PA_PDMA1,
- .end = S5PV210_PA_PDMA1 + SZ_4K,
+ },
+ .res = {
+ .start = S5PV210_PA_PDMA0,
+ .end = S5PV210_PA_PDMA0 + SZ_4K,
.flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_PDMA1,
- .end = IRQ_PDMA1,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct s3c_pl330_platdata s5pv210_pdma1_pdata = {
- .peri = {
- [0] = DMACH_UART0_RX,
- [1] = DMACH_UART0_TX,
- [2] = DMACH_UART1_RX,
- [3] = DMACH_UART1_TX,
- [4] = DMACH_UART2_RX,
- [5] = DMACH_UART2_TX,
- [6] = DMACH_UART3_RX,
- [7] = DMACH_UART3_TX,
- [8] = DMACH_MAX,
- [9] = DMACH_I2S0_RX,
- [10] = DMACH_I2S0_TX,
- [11] = DMACH_I2S0S_TX,
- [12] = DMACH_I2S1_RX,
- [13] = DMACH_I2S1_TX,
- [14] = DMACH_I2S2_RX,
- [15] = DMACH_I2S2_TX,
- [16] = DMACH_SPI0_RX,
- [17] = DMACH_SPI0_TX,
- [18] = DMACH_SPI1_RX,
- [19] = DMACH_SPI1_TX,
- [20] = DMACH_MAX,
- [21] = DMACH_MAX,
- [22] = DMACH_PCM0_RX,
- [23] = DMACH_PCM0_TX,
- [24] = DMACH_PCM1_RX,
- [25] = DMACH_PCM1_TX,
- [26] = DMACH_MSM_REQ0,
- [27] = DMACH_MSM_REQ1,
- [28] = DMACH_MSM_REQ2,
- [29] = DMACH_MSM_REQ3,
- [30] = DMACH_PCM2_RX,
- [31] = DMACH_PCM2_TX,
- },
-};
-
-static struct platform_device s5pv210_device_pdma1 = {
- .name = "s3c-pl330",
- .id = 1,
- .num_resources = ARRAY_SIZE(s5pv210_pdma1_resource),
- .resource = s5pv210_pdma1_resource,
- .dev = {
- .dma_mask = &dma_dmamask,
- .coherent_dma_mask = DMA_BIT_MASK(32),
- .platform_data = &s5pv210_pdma1_pdata,
- },
-};
-
-static struct platform_device *s5pv210_dmacs[] __initdata = {
- &s5pv210_device_pdma0,
- &s5pv210_device_pdma1,
+ },
+ .irq = {IRQ_PDMA0, NO_IRQ},
+ .periphid = 0x00041330,
};
static int __init s5pv210_dma_init(void)
{
- platform_add_devices(s5pv210_dmacs, ARRAY_SIZE(s5pv210_dmacs));
+ amba_device_register(&s5pv210_device_pdma0, &iomem_resource);
return 0;
}
--
1.7.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH V2 08/12] ARM: S5PC100: Use generic DMA PL330 driver
2011-07-13 8:47 [PATCH V2 00/12] To use DMA generic APIs for Samsung DMA Kukjin Kim
` (6 preceding siblings ...)
2011-07-13 8:47 ` [PATCH V2 07/12] ARM: S5PV210: " Kukjin Kim
@ 2011-07-13 8:47 ` Kukjin Kim
2011-07-13 8:47 ` [PATCH V2 09/12] ARM: S5P64X0: " Kukjin Kim
` (3 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Kukjin Kim @ 2011-07-13 8:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Boojin Kim <boojin.kim@samsung.com>
This patch makes Samsung S5PC100 to use DMA PL330 driver
on DMADEVICE. The S5PC100 uses DMA generic APIs instead of
SAMSUNG specific S3C-PL330 APIs.
Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
arch/arm/mach-s5pc100/Kconfig | 2 +-
arch/arm/mach-s5pc100/clock.c | 23 ++---
arch/arm/mach-s5pc100/dma.c | 249 +++++++++++++++++++++--------------------
3 files changed, 136 insertions(+), 138 deletions(-)
diff --git a/arch/arm/mach-s5pc100/Kconfig b/arch/arm/mach-s5pc100/Kconfig
index 608722f..379fadc 100644
--- a/arch/arm/mach-s5pc100/Kconfig
+++ b/arch/arm/mach-s5pc100/Kconfig
@@ -10,7 +10,7 @@ if ARCH_S5PC100
config CPU_S5PC100
bool
select S5P_EXT_INT
- select S3C_PL330_DMA
+ select DMADEV_PL330
help
Enable S5PC100 CPU support
diff --git a/arch/arm/mach-s5pc100/clock.c b/arch/arm/mach-s5pc100/clock.c
index 0305e9b..891acfe 100644
--- a/arch/arm/mach-s5pc100/clock.c
+++ b/arch/arm/mach-s5pc100/clock.c
@@ -34,6 +34,11 @@ static struct clk s5p_clk_otgphy = {
.id = -1,
};
+static struct clk dummy_apb_pclk = {
+ .name = "apb_pclk",
+ .id = -1,
+};
+
static struct clk *clk_src_mout_href_list[] = {
[0] = &s5p_clk_27m,
[1] = &clk_fin_hpll,
@@ -416,12 +421,6 @@ static struct clk init_clocks_off[] = {
.enable = s5pc100_d0_0_ctrl,
.ctrlbit = (1 << 4),
}, {
- .name = "mdma",
- .id = -1,
- .parent = &clk_div_d0_bus.clk,
- .enable = s5pc100_d0_0_ctrl,
- .ctrlbit = (1 << 3),
- }, {
.name = "cfcon",
.id = -1,
.parent = &clk_div_d0_bus.clk,
@@ -488,14 +487,8 @@ static struct clk init_clocks_off[] = {
.enable = s5pc100_d1_0_ctrl,
.ctrlbit = (1 << 2),
}, {
- .name = "pdma",
- .id = 1,
- .parent = &clk_div_d1_bus.clk,
- .enable = s5pc100_d1_0_ctrl,
- .ctrlbit = (1 << 1),
- }, {
- .name = "pdma",
- .id = 0,
+ .name = "dma",
+ .id = -1,
.parent = &clk_div_d1_bus.clk,
.enable = s5pc100_d1_0_ctrl,
.ctrlbit = (1 << 0),
@@ -1394,5 +1387,7 @@ void __init s5pc100_register_clocks(void)
s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
+ s3c24xx_register_clock(&dummy_apb_pclk);
+
s3c_pwmclk_init();
}
diff --git a/arch/arm/mach-s5pc100/dma.c b/arch/arm/mach-s5pc100/dma.c
index bf4cd0f..405048d 100644
--- a/arch/arm/mach-s5pc100/dma.c
+++ b/arch/arm/mach-s5pc100/dma.c
@@ -1,4 +1,8 @@
-/*
+/* linux/arch/arm/mach-s5pc100/dma.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
* Copyright (C) 2010 Samsung Electronics Co. Ltd.
* Jaswinder Singh <jassi.brar@samsung.com>
*
@@ -17,150 +21,149 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/pl330.h>
+#include <asm/irq.h>
#include <plat/devs.h>
+#include <plat/irqs.h>
#include <mach/map.h>
#include <mach/irqs.h>
-
-#include <plat/s3c-pl330-pdata.h>
+#include <mach/dma.h>
static u64 dma_dmamask = DMA_BIT_MASK(32);
-static struct resource s5pc100_pdma0_resource[] = {
- [0] = {
- .start = S5PC100_PA_PDMA0,
- .end = S5PC100_PA_PDMA0 + SZ_4K,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_PDMA0,
- .end = IRQ_PDMA0,
- .flags = IORESOURCE_IRQ,
+struct dma_pl330_peri pdma0_peri[32] = {
+ {
+ .peri_id = (u8)DMACH_UART0_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART0_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_UART1_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART1_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_UART2_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART2_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_UART3_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART3_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = DMACH_IRDA,
+ }, {
+ .peri_id = (u8)DMACH_I2S0_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_I2S0_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_I2S0S_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_I2S1_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_I2S1_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_I2S2_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_I2S2_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_SPI0_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_SPI0_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_SPI1_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_SPI1_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_SPI2_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_SPI2_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_AC97_MICIN,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_AC97_PCMIN,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_AC97_PCMOUT,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_EXTERNAL,
+ }, {
+ .peri_id = (u8)DMACH_PWM,
+ }, {
+ .peri_id = (u8)DMACH_SPDIF,
+ }, {
+ .peri_id = (u8)DMACH_HSI_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_HSI_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_SPDIF,
},
};
-static struct s3c_pl330_platdata s5pc100_pdma0_pdata = {
- .peri = {
- [0] = DMACH_UART0_RX,
- [1] = DMACH_UART0_TX,
- [2] = DMACH_UART1_RX,
- [3] = DMACH_UART1_TX,
- [4] = DMACH_UART2_RX,
- [5] = DMACH_UART2_TX,
- [6] = DMACH_UART3_RX,
- [7] = DMACH_UART3_TX,
- [8] = DMACH_IRDA,
- [9] = DMACH_I2S0_RX,
- [10] = DMACH_I2S0_TX,
- [11] = DMACH_I2S0S_TX,
- [12] = DMACH_I2S1_RX,
- [13] = DMACH_I2S1_TX,
- [14] = DMACH_I2S2_RX,
- [15] = DMACH_I2S2_TX,
- [16] = DMACH_SPI0_RX,
- [17] = DMACH_SPI0_TX,
- [18] = DMACH_SPI1_RX,
- [19] = DMACH_SPI1_TX,
- [20] = DMACH_SPI2_RX,
- [21] = DMACH_SPI2_TX,
- [22] = DMACH_AC97_MICIN,
- [23] = DMACH_AC97_PCMIN,
- [24] = DMACH_AC97_PCMOUT,
- [25] = DMACH_EXTERNAL,
- [26] = DMACH_PWM,
- [27] = DMACH_SPDIF,
- [28] = DMACH_HSI_RX,
- [29] = DMACH_HSI_TX,
- [30] = DMACH_MAX,
- [31] = DMACH_MAX,
- },
+struct dma_pl330_platdata s5pc100_pdma0_pdata = {
+ .nr_valid_peri = 32,
+ .peri = pdma0_peri,
};
-static struct platform_device s5pc100_device_pdma0 = {
- .name = "s3c-pl330",
- .id = 0,
- .num_resources = ARRAY_SIZE(s5pc100_pdma0_resource),
- .resource = s5pc100_pdma0_resource,
- .dev = {
+struct amba_device s5pc100_device_pdma0 = {
+ .dev = {
+ .init_name = "dma-pl330.0",
.dma_mask = &dma_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &s5pc100_pdma0_pdata,
- },
-};
-
-static struct resource s5pc100_pdma1_resource[] = {
- [0] = {
- .start = S5PC100_PA_PDMA1,
- .end = S5PC100_PA_PDMA1 + SZ_4K,
+ },
+ .res = {
+ .start = S5PC100_PA_PDMA0,
+ .end = S5PC100_PA_PDMA0 + SZ_4K,
.flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_PDMA1,
- .end = IRQ_PDMA1,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct s3c_pl330_platdata s5pc100_pdma1_pdata = {
- .peri = {
- [0] = DMACH_UART0_RX,
- [1] = DMACH_UART0_TX,
- [2] = DMACH_UART1_RX,
- [3] = DMACH_UART1_TX,
- [4] = DMACH_UART2_RX,
- [5] = DMACH_UART2_TX,
- [6] = DMACH_UART3_RX,
- [7] = DMACH_UART3_TX,
- [8] = DMACH_IRDA,
- [9] = DMACH_I2S0_RX,
- [10] = DMACH_I2S0_TX,
- [11] = DMACH_I2S0S_TX,
- [12] = DMACH_I2S1_RX,
- [13] = DMACH_I2S1_TX,
- [14] = DMACH_I2S2_RX,
- [15] = DMACH_I2S2_TX,
- [16] = DMACH_SPI0_RX,
- [17] = DMACH_SPI0_TX,
- [18] = DMACH_SPI1_RX,
- [19] = DMACH_SPI1_TX,
- [20] = DMACH_SPI2_RX,
- [21] = DMACH_SPI2_TX,
- [22] = DMACH_PCM0_RX,
- [23] = DMACH_PCM0_TX,
- [24] = DMACH_PCM1_RX,
- [25] = DMACH_PCM1_TX,
- [26] = DMACH_MSM_REQ0,
- [27] = DMACH_MSM_REQ1,
- [28] = DMACH_MSM_REQ2,
- [29] = DMACH_MSM_REQ3,
- [30] = DMACH_MAX,
- [31] = DMACH_MAX,
- },
-};
-
-static struct platform_device s5pc100_device_pdma1 = {
- .name = "s3c-pl330",
- .id = 1,
- .num_resources = ARRAY_SIZE(s5pc100_pdma1_resource),
- .resource = s5pc100_pdma1_resource,
- .dev = {
- .dma_mask = &dma_dmamask,
- .coherent_dma_mask = DMA_BIT_MASK(32),
- .platform_data = &s5pc100_pdma1_pdata,
- },
-};
-
-static struct platform_device *s5pc100_dmacs[] __initdata = {
- &s5pc100_device_pdma0,
- &s5pc100_device_pdma1,
+ },
+ .irq = {IRQ_PDMA0, NO_IRQ},
+ .periphid = 0x00041330,
};
static int __init s5pc100_dma_init(void)
{
- platform_add_devices(s5pc100_dmacs, ARRAY_SIZE(s5pc100_dmacs));
+ amba_device_register(&s5pc100_device_pdma0, &iomem_resource);
return 0;
}
--
1.7.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH V2 09/12] ARM: S5P64X0: Use generic DMA PL330 driver
2011-07-13 8:47 [PATCH V2 00/12] To use DMA generic APIs for Samsung DMA Kukjin Kim
` (7 preceding siblings ...)
2011-07-13 8:47 ` [PATCH V2 08/12] ARM: S5PC100: " Kukjin Kim
@ 2011-07-13 8:47 ` Kukjin Kim
2011-07-13 8:47 ` [PATCH V2 10/12] ARM: SAMSUNG: Remove S3C-PL330-DMA driver Kukjin Kim
` (2 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Kukjin Kim @ 2011-07-13 8:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Boojin Kim <boojin.kim@samsung.com>
This patch makes Samsung S5P64X0 to use DMA PL330 driver
on DMADEVICE. The S5P64X0 uses DMA generic APIs instead of
SAMSUNG specific S3C-PL330 APIs.
Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
arch/arm/mach-s5p64x0/Kconfig | 4 +-
arch/arm/mach-s5p64x0/clock-s5p6440.c | 9 +-
arch/arm/mach-s5p64x0/clock-s5p6450.c | 9 +-
arch/arm/mach-s5p64x0/dma.c | 303 +++++++++++++++++++++++----------
4 files changed, 228 insertions(+), 97 deletions(-)
diff --git a/arch/arm/mach-s5p64x0/Kconfig b/arch/arm/mach-s5p64x0/Kconfig
index 017af4c..7108e7e 100644
--- a/arch/arm/mach-s5p64x0/Kconfig
+++ b/arch/arm/mach-s5p64x0/Kconfig
@@ -9,14 +9,14 @@ if ARCH_S5P64X0
config CPU_S5P6440
bool
- select S3C_PL330_DMA
+ select DMADEV_PL330
select S5P_HRT
help
Enable S5P6440 CPU support
config CPU_S5P6450
bool
- select S3C_PL330_DMA
+ select DMADEV_PL330
select S5P_HRT
help
Enable S5P6450 CPU support
diff --git a/arch/arm/mach-s5p64x0/clock-s5p6440.c b/arch/arm/mach-s5p64x0/clock-s5p6440.c
index 9f12c2e..5f7ad9e 100644
--- a/arch/arm/mach-s5p64x0/clock-s5p6440.c
+++ b/arch/arm/mach-s5p64x0/clock-s5p6440.c
@@ -153,7 +153,7 @@ static struct clk init_clocks_off[] = {
.enable = s5p64x0_hclk0_ctrl,
.ctrlbit = (1 << 8),
}, {
- .name = "pdma",
+ .name = "dma",
.id = -1,
.parent = &clk_hclk_low.clk,
.enable = s5p64x0_hclk0_ctrl,
@@ -533,6 +533,11 @@ static struct clksrc_clk *sysclks[] = {
&clk_pclk_low,
};
+static struct clk dummy_apb_pclk = {
+ .name = "apb_pclk",
+ .id = -1,
+};
+
void __init_or_cpufreq s5p6440_setup_clocks(void)
{
struct clk *xtal_clk;
@@ -615,5 +620,7 @@ void __init s5p6440_register_clocks(void)
s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
+ s3c24xx_register_clock(&dummy_apb_pclk);
+
s3c_pwmclk_init();
}
diff --git a/arch/arm/mach-s5p64x0/clock-s5p6450.c b/arch/arm/mach-s5p64x0/clock-s5p6450.c
index 4eec457..62d4707 100644
--- a/arch/arm/mach-s5p64x0/clock-s5p6450.c
+++ b/arch/arm/mach-s5p64x0/clock-s5p6450.c
@@ -189,7 +189,7 @@ static struct clk init_clocks_off[] = {
.enable = s5p64x0_hclk0_ctrl,
.ctrlbit = (1 << 3),
}, {
- .name = "pdma",
+ .name = "dma",
.id = -1,
.parent = &clk_hclk_low.clk,
.enable = s5p64x0_hclk0_ctrl,
@@ -583,6 +583,11 @@ static struct clksrc_clk *sysclks[] = {
&clk_sclk_audio0,
};
+static struct clk dummy_apb_pclk = {
+ .name = "apb_pclk",
+ .id = -1,
+};
+
void __init_or_cpufreq s5p6450_setup_clocks(void)
{
struct clk *xtal_clk;
@@ -662,5 +667,7 @@ void __init s5p6450_register_clocks(void)
s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
+ s3c24xx_register_clock(&dummy_apb_pclk);
+
s3c_pwmclk_init();
}
diff --git a/arch/arm/mach-s5p64x0/dma.c b/arch/arm/mach-s5p64x0/dma.c
index d7ad944..4295657 100644
--- a/arch/arm/mach-s5p64x0/dma.c
+++ b/arch/arm/mach-s5p64x0/dma.c
@@ -19,130 +19,247 @@
* 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/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/pl330.h>
+
+#include <asm/irq.h>
#include <mach/map.h>
#include <mach/irqs.h>
#include <mach/regs-clock.h>
+#include <mach/dma.h>
#include <plat/devs.h>
-#include <plat/s3c-pl330-pdata.h>
+#include <plat/irqs.h>
static u64 dma_dmamask = DMA_BIT_MASK(32);
-static struct resource s5p64x0_pdma_resource[] = {
- [0] = {
- .start = S5P64X0_PA_PDMA,
- .end = S5P64X0_PA_PDMA + SZ_4K,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_DMA0,
- .end = IRQ_DMA0,
- .flags = IORESOURCE_IRQ,
+struct dma_pl330_peri s5p6440_pdma_peri[32] = {
+ {
+ .peri_id = (u8)DMACH_UART0_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART0_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_UART1_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART1_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_UART2_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART2_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_UART3_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART3_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = DMACH_MAX,
+ }, {
+ .peri_id = DMACH_MAX,
+ }, {
+ .peri_id = (u8)DMACH_PCM0_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_PCM0_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_I2S0_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_I2S0_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_SPI0_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_SPI0_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_MAX,
+ }, {
+ .peri_id = (u8)DMACH_MAX,
+ }, {
+ .peri_id = (u8)DMACH_MAX,
+ }, {
+ .peri_id = (u8)DMACH_MAX,
+ }, {
+ .peri_id = (u8)DMACH_SPI1_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_SPI1_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 1,
},
};
-static struct s3c_pl330_platdata s5p6440_pdma_pdata = {
- .peri = {
- [0] = DMACH_UART0_RX,
- [1] = DMACH_UART0_TX,
- [2] = DMACH_UART1_RX,
- [3] = DMACH_UART1_TX,
- [4] = DMACH_UART2_RX,
- [5] = DMACH_UART2_TX,
- [6] = DMACH_UART3_RX,
- [7] = DMACH_UART3_TX,
- [8] = DMACH_MAX,
- [9] = DMACH_MAX,
- [10] = DMACH_PCM0_TX,
- [11] = DMACH_PCM0_RX,
- [12] = DMACH_I2S0_TX,
- [13] = DMACH_I2S0_RX,
- [14] = DMACH_SPI0_TX,
- [15] = DMACH_SPI0_RX,
- [16] = DMACH_MAX,
- [17] = DMACH_MAX,
- [18] = DMACH_MAX,
- [19] = DMACH_MAX,
- [20] = DMACH_SPI1_TX,
- [21] = DMACH_SPI1_RX,
- [22] = DMACH_MAX,
- [23] = DMACH_MAX,
- [24] = DMACH_MAX,
- [25] = DMACH_MAX,
- [26] = DMACH_MAX,
- [27] = DMACH_MAX,
- [28] = DMACH_MAX,
- [29] = DMACH_PWM,
- [30] = DMACH_MAX,
- [31] = DMACH_MAX,
- },
+struct dma_pl330_platdata s5p6440_pdma_pdata = {
+ .nr_valid_peri = 32,
+ .peri = s5p6440_pdma_peri,
};
-static struct s3c_pl330_platdata s5p6450_pdma_pdata = {
- .peri = {
- [0] = DMACH_UART0_RX,
- [1] = DMACH_UART0_TX,
- [2] = DMACH_UART1_RX,
- [3] = DMACH_UART1_TX,
- [4] = DMACH_UART2_RX,
- [5] = DMACH_UART2_TX,
- [6] = DMACH_UART3_RX,
- [7] = DMACH_UART3_TX,
- [8] = DMACH_UART4_RX,
- [9] = DMACH_UART4_TX,
- [10] = DMACH_PCM0_TX,
- [11] = DMACH_PCM0_RX,
- [12] = DMACH_I2S0_TX,
- [13] = DMACH_I2S0_RX,
- [14] = DMACH_SPI0_TX,
- [15] = DMACH_SPI0_RX,
- [16] = DMACH_PCM1_TX,
- [17] = DMACH_PCM1_RX,
- [18] = DMACH_PCM2_TX,
- [19] = DMACH_PCM2_RX,
- [20] = DMACH_SPI1_TX,
- [21] = DMACH_SPI1_RX,
- [22] = DMACH_USI_TX,
- [23] = DMACH_USI_RX,
- [24] = DMACH_MAX,
- [25] = DMACH_I2S1_TX,
- [26] = DMACH_I2S1_RX,
- [27] = DMACH_I2S2_TX,
- [28] = DMACH_I2S2_RX,
- [29] = DMACH_PWM,
- [30] = DMACH_UART5_RX,
- [31] = DMACH_UART5_TX,
+struct dma_pl330_peri s5p6450_pdma_peri[32] = {
+ {
+ .peri_id = (u8)DMACH_UART0_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART0_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_UART1_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART1_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_UART2_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART2_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_UART3_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART3_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_UART4_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART4_TX,
+ .rqtype = MEMTODEV,
+ }, {
+ .peri_id = (u8)DMACH_PCM0_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_PCM0_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_I2S0_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_I2S0_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_SPI0_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_SPI0_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_PCM1_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_PCM1_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_PCM2_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_PCM2_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_SPI1_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_SPI1_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_USI_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_USI_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 1,
+ }, {
+ .peri_id = (u8)DMACH_MAX,
+ }, {
+ .peri_id = (u8)DMACH_I2S1_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_I2S1_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_I2S2_TX,
+ .rqtype = MEMTODEV,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_I2S2_RX,
+ .rqtype = DEVTOMEM,
+ .burst_sz = 4,
+ }, {
+ .peri_id = (u8)DMACH_PWM,
+ }, {
+ .peri_id = (u8)DMACH_UART5_RX,
+ .rqtype = DEVTOMEM,
+ }, {
+ .peri_id = (u8)DMACH_UART5_TX,
+ .rqtype = MEMTODEV,
},
};
-static struct platform_device s5p64x0_device_pdma = {
- .name = "s3c-pl330",
- .id = -1,
- .num_resources = ARRAY_SIZE(s5p64x0_pdma_resource),
- .resource = s5p64x0_pdma_resource,
- .dev = {
+struct dma_pl330_platdata s5p6450_pdma_pdata = {
+ .nr_valid_peri = 32,
+ .peri = s5p6450_pdma_peri,
+};
+
+struct amba_device s5p64x0_device_pdma = {
+ .dev = {
+ .init_name = "dma-pl330",
.dma_mask = &dma_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
- },
+ },
+ .res = {
+ .start = S5P64X0_PA_PDMA,
+ .end = S5P64X0_PA_PDMA + SZ_4K,
+ .flags = IORESOURCE_MEM,
+ },
+ .irq = {IRQ_DMA0, NO_IRQ},
+ .periphid = 0x00041330,
};
static int __init s5p64x0_dma_init(void)
{
- unsigned int id;
-
- id = __raw_readl(S5P64X0_SYS_ID) & 0xFF000;
+ unsigned int id = __raw_readl(S5P64X0_SYS_ID) & 0xFF000;
if (id == 0x50000)
s5p64x0_device_pdma.dev.platform_data = &s5p6450_pdma_pdata;
else
s5p64x0_device_pdma.dev.platform_data = &s5p6440_pdma_pdata;
- platform_device_register(&s5p64x0_device_pdma);
+ amba_device_register(&s5p64x0_device_pdma, &iomem_resource);
return 0;
}
--
1.7.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH V2 10/12] ARM: SAMSUNG: Remove S3C-PL330-DMA driver
2011-07-13 8:47 [PATCH V2 00/12] To use DMA generic APIs for Samsung DMA Kukjin Kim
` (8 preceding siblings ...)
2011-07-13 8:47 ` [PATCH V2 09/12] ARM: S5P64X0: " Kukjin Kim
@ 2011-07-13 8:47 ` Kukjin Kim
2011-07-13 8:47 ` [PATCH V2 11/12] spi/s3c64xx: Add support DMA engine API Kukjin Kim
2011-07-13 8:47 ` [PATCH V2 12/12] ASoC: Samsung: Update DMA interface Kukjin Kim
11 siblings, 0 replies; 23+ messages in thread
From: Kukjin Kim @ 2011-07-13 8:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Boojin Kim <boojin.kim@samsung.com>
Since DMA generic APIs can be used for Samsung DMA now so that
the s3c-pl330 which includes Samsung specific DMA APIs can be
removed.
Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
Cc: Jassi Brar <jassisinghbrar@gmail.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
arch/arm/plat-samsung/Kconfig | 6 -
arch/arm/plat-samsung/Makefile | 2 -
arch/arm/plat-samsung/include/plat/dma-pl330.h | 8 +-
.../plat-samsung/include/plat/s3c-pl330-pdata.h | 32 -
arch/arm/plat-samsung/s3c-pl330.c | 1244 --------------------
5 files changed, 5 insertions(+), 1287 deletions(-)
delete mode 100644 arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h
delete mode 100644 arch/arm/plat-samsung/s3c-pl330.c
diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig
index cb170a6..db789d7 100644
--- a/arch/arm/plat-samsung/Kconfig
+++ b/arch/arm/plat-samsung/Kconfig
@@ -294,12 +294,6 @@ config S3C_DMA
help
Internal configuration for S3C DMA core
-config S3C_PL330_DMA
- bool
- select PL330
- help
- S3C DMA API Driver for PL330 DMAC.
-
config DMADEV_PL330
bool
select DMADEVICES
diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile
index 9ecf2aa..ea98621 100644
--- a/arch/arm/plat-samsung/Makefile
+++ b/arch/arm/plat-samsung/Makefile
@@ -66,8 +66,6 @@ obj-$(CONFIG_S3C_DMA) += dma.o s3c-dma-ops.o
obj-$(CONFIG_DMADEV_PL330) += dma-ops.o
-obj-$(CONFIG_S3C_PL330_DMA) += s3c-pl330.o s3c-dma-ops.o
-
# PM support
obj-$(CONFIG_PM) += pm.o
diff --git a/arch/arm/plat-samsung/include/plat/dma-pl330.h b/arch/arm/plat-samsung/include/plat/dma-pl330.h
index be84bec..dbc1288 100644
--- a/arch/arm/plat-samsung/include/plat/dma-pl330.h
+++ b/arch/arm/plat-samsung/include/plat/dma-pl330.h
@@ -14,8 +14,6 @@
#ifndef __DMA_PL330_H_
#define __DMA_PL330_H_ __FILE__
-#define S3C2410_DMAF_AUTOSTART (1 << 0)
-#define S3C2410_DMAF_CIRCULAR (1 << 1)
/*
* PL330 can assign any channel to communicate with
* any of the peripherals attched to the DMAC.
@@ -98,11 +96,15 @@ enum dma_ch {
DMACH_MAX,
};
+struct s3c2410_dma_client {
+ char *name;
+};
+
static inline bool s3c_dma_has_circular(void)
{
return true;
}
-#include <plat/dma.h>
+#include <plat/dma-ops.h>
#endif /* __DMA_PL330_H_ */
diff --git a/arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h b/arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h
deleted file mode 100644
index 64fdf66..0000000
--- a/arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* linux/arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h
- *
- * Copyright (C) 2010 Samsung Electronics Co. Ltd.
- * Jaswinder Singh <jassi.brar@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef __S3C_PL330_PDATA_H
-#define __S3C_PL330_PDATA_H
-
-#include <plat/dma-pl330.h>
-
-/*
- * Every PL330 DMAC has max 32 peripheral interfaces,
- * of which some may be not be really used in your
- * DMAC's configuration.
- * Populate this array of 32 peri i/fs with relevant
- * channel IDs for used peri i/f and DMACH_MAX for
- * those unused.
- *
- * The platforms just need to provide this info
- * to the S3C DMA API driver for PL330.
- */
-struct s3c_pl330_platdata {
- enum dma_ch peri[32];
-};
-
-#endif /* __S3C_PL330_PDATA_H */
diff --git a/arch/arm/plat-samsung/s3c-pl330.c b/arch/arm/plat-samsung/s3c-pl330.c
deleted file mode 100644
index f85638c..0000000
--- a/arch/arm/plat-samsung/s3c-pl330.c
+++ /dev/null
@@ -1,1244 +0,0 @@
-/* linux/arch/arm/plat-samsung/s3c-pl330.c
- *
- * Copyright (C) 2010 Samsung Electronics Co. Ltd.
- * Jaswinder Singh <jassi.brar@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-
-#include <asm/hardware/pl330.h>
-
-#include <plat/s3c-pl330-pdata.h>
-
-/**
- * struct s3c_pl330_dmac - Logical representation of a PL330 DMAC.
- * @busy_chan: Number of channels currently busy.
- * @peri: List of IDs of peripherals this DMAC can work with.
- * @node: To attach to the global list of DMACs.
- * @pi: PL330 configuration info for the DMAC.
- * @kmcache: Pool to quickly allocate xfers for all channels in the dmac.
- * @clk: Pointer of DMAC operation clock.
- */
-struct s3c_pl330_dmac {
- unsigned busy_chan;
- enum dma_ch *peri;
- struct list_head node;
- struct pl330_info *pi;
- struct kmem_cache *kmcache;
- struct clk *clk;
-};
-
-/**
- * struct s3c_pl330_xfer - A request submitted by S3C DMA clients.
- * @token: Xfer ID provided by the client.
- * @node: To attach to the list of xfers on a channel.
- * @px: Xfer for PL330 core.
- * @chan: Owner channel of this xfer.
- */
-struct s3c_pl330_xfer {
- void *token;
- struct list_head node;
- struct pl330_xfer px;
- struct s3c_pl330_chan *chan;
-};
-
-/**
- * struct s3c_pl330_chan - Logical channel to communicate with
- * a Physical peripheral.
- * @pl330_chan_id: Token of a hardware channel thread of PL330 DMAC.
- * NULL if the channel is available to be acquired.
- * @id: ID of the peripheral that this channel can communicate with.
- * @options: Options specified by the client.
- * @sdaddr: Address provided via s3c2410_dma_devconfig.
- * @node: To attach to the global list of channels.
- * @lrq: Pointer to the last submitted pl330_req to PL330 core.
- * @xfer_list: To manage list of xfers enqueued.
- * @req: Two requests to communicate with the PL330 engine.
- * @callback_fn: Callback function to the client.
- * @rqcfg: Channel configuration for the xfers.
- * @xfer_head: Pointer to the xfer to be next executed.
- * @dmac: Pointer to the DMAC that manages this channel, NULL if the
- * channel is available to be acquired.
- * @client: Client of this channel. NULL if the
- * channel is available to be acquired.
- */
-struct s3c_pl330_chan {
- void *pl330_chan_id;
- enum dma_ch id;
- unsigned int options;
- unsigned long sdaddr;
- struct list_head node;
- struct pl330_req *lrq;
- struct list_head xfer_list;
- struct pl330_req req[2];
- s3c2410_dma_cbfn_t callback_fn;
- struct pl330_reqcfg rqcfg;
- struct s3c_pl330_xfer *xfer_head;
- struct s3c_pl330_dmac *dmac;
- struct s3c2410_dma_client *client;
-};
-
-/* All DMACs in the platform */
-static LIST_HEAD(dmac_list);
-
-/* All channels to peripherals in the platform */
-static LIST_HEAD(chan_list);
-
-/*
- * Since we add resources(DMACs and Channels) to the global pool,
- * we need to guard access to the resources using a global lock
- */
-static DEFINE_SPINLOCK(res_lock);
-
-/* Returns the channel with ID 'id' in the chan_list */
-static struct s3c_pl330_chan *id_to_chan(const enum dma_ch id)
-{
- struct s3c_pl330_chan *ch;
-
- list_for_each_entry(ch, &chan_list, node)
- if (ch->id == id)
- return ch;
-
- return NULL;
-}
-
-/* Allocate a new channel with ID 'id' and add to chan_list */
-static void chan_add(const enum dma_ch id)
-{
- struct s3c_pl330_chan *ch = id_to_chan(id);
-
- /* Return if the channel already exists */
- if (ch)
- return;
-
- ch = kmalloc(sizeof(*ch), GFP_KERNEL);
- /* Return silently to work with other channels */
- if (!ch)
- return;
-
- ch->id = id;
- ch->dmac = NULL;
-
- list_add_tail(&ch->node, &chan_list);
-}
-
-/* If the channel is not yet acquired by any client */
-static bool chan_free(struct s3c_pl330_chan *ch)
-{
- if (!ch)
- return false;
-
- /* Channel points to some DMAC only when it's acquired */
- return ch->dmac ? false : true;
-}
-
-/*
- * Returns 0 is peripheral i/f is invalid or not present on the dmac.
- * Index + 1, otherwise.
- */
-static unsigned iface_of_dmac(struct s3c_pl330_dmac *dmac, enum dma_ch ch_id)
-{
- enum dma_ch *id = dmac->peri;
- int i;
-
- /* Discount invalid markers */
- if (ch_id == DMACH_MAX)
- return 0;
-
- for (i = 0; i < PL330_MAX_PERI; i++)
- if (id[i] == ch_id)
- return i + 1;
-
- return 0;
-}
-
-/* If all channel threads of the DMAC are busy */
-static inline bool dmac_busy(struct s3c_pl330_dmac *dmac)
-{
- struct pl330_info *pi = dmac->pi;
-
- return (dmac->busy_chan < pi->pcfg.num_chan) ? false : true;
-}
-
-/*
- * Returns the number of free channels that
- * can be handled by this dmac only.
- */
-static unsigned ch_onlyby_dmac(struct s3c_pl330_dmac *dmac)
-{
- enum dma_ch *id = dmac->peri;
- struct s3c_pl330_dmac *d;
- struct s3c_pl330_chan *ch;
- unsigned found, count = 0;
- enum dma_ch p;
- int i;
-
- for (i = 0; i < PL330_MAX_PERI; i++) {
- p = id[i];
- ch = id_to_chan(p);
-
- if (p == DMACH_MAX || !chan_free(ch))
- continue;
-
- found = 0;
- list_for_each_entry(d, &dmac_list, node) {
- if (d != dmac && iface_of_dmac(d, ch->id)) {
- found = 1;
- break;
- }
- }
- if (!found)
- count++;
- }
-
- return count;
-}
-
-/*
- * Measure of suitability of 'dmac' handling 'ch'
- *
- * 0 indicates 'dmac' can not handle 'ch' either
- * because it is not supported by the hardware or
- * because all dmac channels are currently busy.
- *
- * >0 vlaue indicates 'dmac' has the capability.
- * The bigger the value the more suitable the dmac.
- */
-#define MAX_SUIT UINT_MAX
-#define MIN_SUIT 0
-
-static unsigned suitablility(struct s3c_pl330_dmac *dmac,
- struct s3c_pl330_chan *ch)
-{
- struct pl330_info *pi = dmac->pi;
- enum dma_ch *id = dmac->peri;
- struct s3c_pl330_dmac *d;
- unsigned s;
- int i;
-
- s = MIN_SUIT;
- /* If all the DMAC channel threads are busy */
- if (dmac_busy(dmac))
- return s;
-
- for (i = 0; i < PL330_MAX_PERI; i++)
- if (id[i] == ch->id)
- break;
-
- /* If the 'dmac' can't talk to 'ch' */
- if (i == PL330_MAX_PERI)
- return s;
-
- s = MAX_SUIT;
- list_for_each_entry(d, &dmac_list, node) {
- /*
- * If some other dmac can talk to this
- * peri and has some channel free.
- */
- if (d != dmac && iface_of_dmac(d, ch->id) && !dmac_busy(d)) {
- s = 0;
- break;
- }
- }
- if (s)
- return s;
-
- s = 100;
-
- /* Good if free chans are more, bad otherwise */
- s += (pi->pcfg.num_chan - dmac->busy_chan) - ch_onlyby_dmac(dmac);
-
- return s;
-}
-
-/* More than one DMAC may have capability to transfer data with the
- * peripheral. This function assigns most suitable DMAC to manage the
- * channel and hence communicate with the peripheral.
- */
-static struct s3c_pl330_dmac *map_chan_to_dmac(struct s3c_pl330_chan *ch)
-{
- struct s3c_pl330_dmac *d, *dmac = NULL;
- unsigned sn, sl = MIN_SUIT;
-
- list_for_each_entry(d, &dmac_list, node) {
- sn = suitablility(d, ch);
-
- if (sn == MAX_SUIT)
- return d;
-
- if (sn > sl)
- dmac = d;
- }
-
- return dmac;
-}
-
-/* Acquire the channel for peripheral 'id' */
-static struct s3c_pl330_chan *chan_acquire(const enum dma_ch id)
-{
- struct s3c_pl330_chan *ch = id_to_chan(id);
- struct s3c_pl330_dmac *dmac;
-
- /* If the channel doesn't exist or is already acquired */
- if (!ch || !chan_free(ch)) {
- ch = NULL;
- goto acq_exit;
- }
-
- dmac = map_chan_to_dmac(ch);
- /* If couldn't map */
- if (!dmac) {
- ch = NULL;
- goto acq_exit;
- }
-
- dmac->busy_chan++;
- ch->dmac = dmac;
-
-acq_exit:
- return ch;
-}
-
-/* Delete xfer from the queue */
-static inline void del_from_queue(struct s3c_pl330_xfer *xfer)
-{
- struct s3c_pl330_xfer *t;
- struct s3c_pl330_chan *ch;
- int found;
-
- if (!xfer)
- return;
-
- ch = xfer->chan;
-
- /* Make sure xfer is in the queue */
- found = 0;
- list_for_each_entry(t, &ch->xfer_list, node)
- if (t == xfer) {
- found = 1;
- break;
- }
-
- if (!found)
- return;
-
- /* If xfer is last entry in the queue */
- if (xfer->node.next == &ch->xfer_list)
- t = list_entry(ch->xfer_list.next,
- struct s3c_pl330_xfer, node);
- else
- t = list_entry(xfer->node.next,
- struct s3c_pl330_xfer, node);
-
- /* If there was only one node left */
- if (t == xfer)
- ch->xfer_head = NULL;
- else if (ch->xfer_head == xfer)
- ch->xfer_head = t;
-
- list_del(&xfer->node);
-}
-
-/* Provides pointer to the next xfer in the queue.
- * If CIRCULAR option is set, the list is left intact,
- * otherwise the xfer is removed from the list.
- * Forced delete 'pluck' can be set to override the CIRCULAR option.
- */
-static struct s3c_pl330_xfer *get_from_queue(struct s3c_pl330_chan *ch,
- int pluck)
-{
- struct s3c_pl330_xfer *xfer = ch->xfer_head;
-
- if (!xfer)
- return NULL;
-
- /* If xfer is last entry in the queue */
- if (xfer->node.next == &ch->xfer_list)
- ch->xfer_head = list_entry(ch->xfer_list.next,
- struct s3c_pl330_xfer, node);
- else
- ch->xfer_head = list_entry(xfer->node.next,
- struct s3c_pl330_xfer, node);
-
- if (pluck || !(ch->options & S3C2410_DMAF_CIRCULAR))
- del_from_queue(xfer);
-
- return xfer;
-}
-
-static inline void add_to_queue(struct s3c_pl330_chan *ch,
- struct s3c_pl330_xfer *xfer, int front)
-{
- struct pl330_xfer *xt;
-
- /* If queue empty */
- if (ch->xfer_head == NULL)
- ch->xfer_head = xfer;
-
- xt = &ch->xfer_head->px;
- /* If the head already submitted (CIRCULAR head) */
- if (ch->options & S3C2410_DMAF_CIRCULAR &&
- (xt == ch->req[0].x || xt == ch->req[1].x))
- ch->xfer_head = xfer;
-
- /* If this is a resubmission, it should go at the head */
- if (front) {
- ch->xfer_head = xfer;
- list_add(&xfer->node, &ch->xfer_list);
- } else {
- list_add_tail(&xfer->node, &ch->xfer_list);
- }
-}
-
-static inline void _finish_off(struct s3c_pl330_xfer *xfer,
- enum s3c2410_dma_buffresult res, int ffree)
-{
- struct s3c_pl330_chan *ch;
-
- if (!xfer)
- return;
-
- ch = xfer->chan;
-
- /* Do callback */
- if (ch->callback_fn)
- ch->callback_fn(NULL, xfer->token, xfer->px.bytes, res);
-
- /* Force Free or if buffer is not needed anymore */
- if (ffree || !(ch->options & S3C2410_DMAF_CIRCULAR))
- kmem_cache_free(ch->dmac->kmcache, xfer);
-}
-
-static inline int s3c_pl330_submit(struct s3c_pl330_chan *ch,
- struct pl330_req *r)
-{
- struct s3c_pl330_xfer *xfer;
- int ret = 0;
-
- /* If already submitted */
- if (r->x)
- return 0;
-
- xfer = get_from_queue(ch, 0);
- if (xfer) {
- r->x = &xfer->px;
-
- /* Use max bandwidth for M<->M xfers */
- if (r->rqtype == MEMTOMEM) {
- struct pl330_info *pi = xfer->chan->dmac->pi;
- int burst = 1 << ch->rqcfg.brst_size;
- u32 bytes = r->x->bytes;
- int bl;
-
- bl = pi->pcfg.data_bus_width / 8;
- bl *= pi->pcfg.data_buf_dep;
- bl /= burst;
-
- /* src/dst_burst_len can't be more than 16 */
- if (bl > 16)
- bl = 16;
-
- while (bl > 1) {
- if (!(bytes % (bl * burst)))
- break;
- bl--;
- }
-
- ch->rqcfg.brst_len = bl;
- } else {
- ch->rqcfg.brst_len = 1;
- }
-
- ret = pl330_submit_req(ch->pl330_chan_id, r);
-
- /* If submission was successful */
- if (!ret) {
- ch->lrq = r; /* latest submitted req */
- return 0;
- }
-
- r->x = NULL;
-
- /* If both of the PL330 ping-pong buffers filled */
- if (ret == -EAGAIN) {
- dev_err(ch->dmac->pi->dev, "%s:%d!\n",
- __func__, __LINE__);
- /* Queue back again */
- add_to_queue(ch, xfer, 1);
- ret = 0;
- } else {
- dev_err(ch->dmac->pi->dev, "%s:%d!\n",
- __func__, __LINE__);
- _finish_off(xfer, S3C2410_RES_ERR, 0);
- }
- }
-
- return ret;
-}
-
-static void s3c_pl330_rq(struct s3c_pl330_chan *ch,
- struct pl330_req *r, enum pl330_op_err err)
-{
- unsigned long flags;
- struct s3c_pl330_xfer *xfer;
- struct pl330_xfer *xl = r->x;
- enum s3c2410_dma_buffresult res;
-
- spin_lock_irqsave(&res_lock, flags);
-
- r->x = NULL;
-
- s3c_pl330_submit(ch, r);
-
- spin_unlock_irqrestore(&res_lock, flags);
-
- /* Map result to S3C DMA API */
- if (err == PL330_ERR_NONE)
- res = S3C2410_RES_OK;
- else if (err == PL330_ERR_ABORT)
- res = S3C2410_RES_ABORT;
- else
- res = S3C2410_RES_ERR;
-
- /* If last request had some xfer */
- if (xl) {
- xfer = container_of(xl, struct s3c_pl330_xfer, px);
- _finish_off(xfer, res, 0);
- } else {
- dev_info(ch->dmac->pi->dev, "%s:%d No Xfer?!\n",
- __func__, __LINE__);
- }
-}
-
-static void s3c_pl330_rq0(void *token, enum pl330_op_err err)
-{
- struct pl330_req *r = token;
- struct s3c_pl330_chan *ch = container_of(r,
- struct s3c_pl330_chan, req[0]);
- s3c_pl330_rq(ch, r, err);
-}
-
-static void s3c_pl330_rq1(void *token, enum pl330_op_err err)
-{
- struct pl330_req *r = token;
- struct s3c_pl330_chan *ch = container_of(r,
- struct s3c_pl330_chan, req[1]);
- s3c_pl330_rq(ch, r, err);
-}
-
-/* Release an acquired channel */
-static void chan_release(struct s3c_pl330_chan *ch)
-{
- struct s3c_pl330_dmac *dmac;
-
- if (chan_free(ch))
- return;
-
- dmac = ch->dmac;
- ch->dmac = NULL;
- dmac->busy_chan--;
-}
-
-int s3c2410_dma_ctrl(enum dma_ch id, enum s3c2410_chan_op op)
-{
- struct s3c_pl330_xfer *xfer;
- enum pl330_chan_op pl330op;
- struct s3c_pl330_chan *ch;
- unsigned long flags;
- int idx, ret;
-
- spin_lock_irqsave(&res_lock, flags);
-
- ch = id_to_chan(id);
-
- if (!ch || chan_free(ch)) {
- ret = -EINVAL;
- goto ctrl_exit;
- }
-
- switch (op) {
- case S3C2410_DMAOP_START:
- /* Make sure both reqs are enqueued */
- idx = (ch->lrq == &ch->req[0]) ? 1 : 0;
- s3c_pl330_submit(ch, &ch->req[idx]);
- s3c_pl330_submit(ch, &ch->req[1 - idx]);
- pl330op = PL330_OP_START;
- break;
-
- case S3C2410_DMAOP_STOP:
- pl330op = PL330_OP_ABORT;
- break;
-
- case S3C2410_DMAOP_FLUSH:
- pl330op = PL330_OP_FLUSH;
- break;
-
- case S3C2410_DMAOP_PAUSE:
- case S3C2410_DMAOP_RESUME:
- case S3C2410_DMAOP_TIMEOUT:
- case S3C2410_DMAOP_STARTED:
- spin_unlock_irqrestore(&res_lock, flags);
- return 0;
-
- default:
- spin_unlock_irqrestore(&res_lock, flags);
- return -EINVAL;
- }
-
- ret = pl330_chan_ctrl(ch->pl330_chan_id, pl330op);
-
- if (pl330op == PL330_OP_START) {
- spin_unlock_irqrestore(&res_lock, flags);
- return ret;
- }
-
- idx = (ch->lrq == &ch->req[0]) ? 1 : 0;
-
- /* Abort the current xfer */
- if (ch->req[idx].x) {
- xfer = container_of(ch->req[idx].x,
- struct s3c_pl330_xfer, px);
-
- /* Drop xfer during FLUSH */
- if (pl330op == PL330_OP_FLUSH)
- del_from_queue(xfer);
-
- ch->req[idx].x = NULL;
-
- spin_unlock_irqrestore(&res_lock, flags);
- _finish_off(xfer, S3C2410_RES_ABORT,
- pl330op == PL330_OP_FLUSH ? 1 : 0);
- spin_lock_irqsave(&res_lock, flags);
- }
-
- /* Flush the whole queue */
- if (pl330op == PL330_OP_FLUSH) {
-
- if (ch->req[1 - idx].x) {
- xfer = container_of(ch->req[1 - idx].x,
- struct s3c_pl330_xfer, px);
-
- del_from_queue(xfer);
-
- ch->req[1 - idx].x = NULL;
-
- spin_unlock_irqrestore(&res_lock, flags);
- _finish_off(xfer, S3C2410_RES_ABORT, 1);
- spin_lock_irqsave(&res_lock, flags);
- }
-
- /* Finish off the remaining in the queue */
- xfer = ch->xfer_head;
- while (xfer) {
-
- del_from_queue(xfer);
-
- spin_unlock_irqrestore(&res_lock, flags);
- _finish_off(xfer, S3C2410_RES_ABORT, 1);
- spin_lock_irqsave(&res_lock, flags);
-
- xfer = ch->xfer_head;
- }
- }
-
-ctrl_exit:
- spin_unlock_irqrestore(&res_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(s3c2410_dma_ctrl);
-
-int s3c2410_dma_enqueue(enum dma_ch id, void *token,
- dma_addr_t addr, int size)
-{
- struct s3c_pl330_chan *ch;
- struct s3c_pl330_xfer *xfer;
- unsigned long flags;
- int idx, ret = 0;
-
- spin_lock_irqsave(&res_lock, flags);
-
- ch = id_to_chan(id);
-
- /* Error if invalid or free channel */
- if (!ch || chan_free(ch)) {
- ret = -EINVAL;
- goto enq_exit;
- }
-
- /* Error if size is unaligned */
- if (ch->rqcfg.brst_size && size % (1 << ch->rqcfg.brst_size)) {
- ret = -EINVAL;
- goto enq_exit;
- }
-
- xfer = kmem_cache_alloc(ch->dmac->kmcache, GFP_ATOMIC);
- if (!xfer) {
- ret = -ENOMEM;
- goto enq_exit;
- }
-
- xfer->token = token;
- xfer->chan = ch;
- xfer->px.bytes = size;
- xfer->px.next = NULL; /* Single request */
-
- /* For S3C DMA API, direction is always fixed for all xfers */
- if (ch->req[0].rqtype == MEMTODEV) {
- xfer->px.src_addr = addr;
- xfer->px.dst_addr = ch->sdaddr;
- } else {
- xfer->px.src_addr = ch->sdaddr;
- xfer->px.dst_addr = addr;
- }
-
- add_to_queue(ch, xfer, 0);
-
- /* Try submitting on either request */
- idx = (ch->lrq == &ch->req[0]) ? 1 : 0;
-
- if (!ch->req[idx].x)
- s3c_pl330_submit(ch, &ch->req[idx]);
- else
- s3c_pl330_submit(ch, &ch->req[1 - idx]);
-
- spin_unlock_irqrestore(&res_lock, flags);
-
- if (ch->options & S3C2410_DMAF_AUTOSTART)
- s3c2410_dma_ctrl(id, S3C2410_DMAOP_START);
-
- return 0;
-
-enq_exit:
- spin_unlock_irqrestore(&res_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(s3c2410_dma_enqueue);
-
-int s3c2410_dma_request(enum dma_ch id,
- struct s3c2410_dma_client *client,
- void *dev)
-{
- struct s3c_pl330_dmac *dmac;
- struct s3c_pl330_chan *ch;
- unsigned long flags;
- int ret = 0;
-
- spin_lock_irqsave(&res_lock, flags);
-
- ch = chan_acquire(id);
- if (!ch) {
- ret = -EBUSY;
- goto req_exit;
- }
-
- dmac = ch->dmac;
-
- ch->pl330_chan_id = pl330_request_channel(dmac->pi);
- if (!ch->pl330_chan_id) {
- chan_release(ch);
- ret = -EBUSY;
- goto req_exit;
- }
-
- ch->client = client;
- ch->options = 0; /* Clear any option */
- ch->callback_fn = NULL; /* Clear any callback */
- ch->lrq = NULL;
-
- ch->rqcfg.brst_size = 2; /* Default word size */
- ch->rqcfg.swap = SWAP_NO;
- ch->rqcfg.scctl = SCCTRL0; /* Noncacheable and nonbufferable */
- ch->rqcfg.dcctl = DCCTRL0; /* Noncacheable and nonbufferable */
- ch->rqcfg.privileged = 0;
- ch->rqcfg.insnaccess = 0;
-
- /* Set invalid direction */
- ch->req[0].rqtype = DEVTODEV;
- ch->req[1].rqtype = ch->req[0].rqtype;
-
- ch->req[0].cfg = &ch->rqcfg;
- ch->req[1].cfg = ch->req[0].cfg;
-
- ch->req[0].peri = iface_of_dmac(dmac, id) - 1; /* Original index */
- ch->req[1].peri = ch->req[0].peri;
-
- ch->req[0].token = &ch->req[0];
- ch->req[0].xfer_cb = s3c_pl330_rq0;
- ch->req[1].token = &ch->req[1];
- ch->req[1].xfer_cb = s3c_pl330_rq1;
-
- ch->req[0].x = NULL;
- ch->req[1].x = NULL;
-
- /* Reset xfer list */
- INIT_LIST_HEAD(&ch->xfer_list);
- ch->xfer_head = NULL;
-
-req_exit:
- spin_unlock_irqrestore(&res_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(s3c2410_dma_request);
-
-int s3c2410_dma_free(enum dma_ch id, struct s3c2410_dma_client *client)
-{
- struct s3c_pl330_chan *ch;
- struct s3c_pl330_xfer *xfer;
- unsigned long flags;
- int ret = 0;
- unsigned idx;
-
- spin_lock_irqsave(&res_lock, flags);
-
- ch = id_to_chan(id);
-
- if (!ch || chan_free(ch))
- goto free_exit;
-
- /* Refuse if someone else wanted to free the channel */
- if (ch->client != client) {
- ret = -EBUSY;
- goto free_exit;
- }
-
- /* Stop any active xfer, Flushe the queue and do callbacks */
- pl330_chan_ctrl(ch->pl330_chan_id, PL330_OP_FLUSH);
-
- /* Abort the submitted requests */
- idx = (ch->lrq == &ch->req[0]) ? 1 : 0;
-
- if (ch->req[idx].x) {
- xfer = container_of(ch->req[idx].x,
- struct s3c_pl330_xfer, px);
-
- ch->req[idx].x = NULL;
- del_from_queue(xfer);
-
- spin_unlock_irqrestore(&res_lock, flags);
- _finish_off(xfer, S3C2410_RES_ABORT, 1);
- spin_lock_irqsave(&res_lock, flags);
- }
-
- if (ch->req[1 - idx].x) {
- xfer = container_of(ch->req[1 - idx].x,
- struct s3c_pl330_xfer, px);
-
- ch->req[1 - idx].x = NULL;
- del_from_queue(xfer);
-
- spin_unlock_irqrestore(&res_lock, flags);
- _finish_off(xfer, S3C2410_RES_ABORT, 1);
- spin_lock_irqsave(&res_lock, flags);
- }
-
- /* Pluck and Abort the queued requests in order */
- do {
- xfer = get_from_queue(ch, 1);
-
- spin_unlock_irqrestore(&res_lock, flags);
- _finish_off(xfer, S3C2410_RES_ABORT, 1);
- spin_lock_irqsave(&res_lock, flags);
- } while (xfer);
-
- ch->client = NULL;
-
- pl330_release_channel(ch->pl330_chan_id);
-
- ch->pl330_chan_id = NULL;
-
- chan_release(ch);
-
-free_exit:
- spin_unlock_irqrestore(&res_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(s3c2410_dma_free);
-
-int s3c2410_dma_config(enum dma_ch id, int xferunit)
-{
- struct s3c_pl330_chan *ch;
- struct pl330_info *pi;
- unsigned long flags;
- int i, dbwidth, ret = 0;
-
- spin_lock_irqsave(&res_lock, flags);
-
- ch = id_to_chan(id);
-
- if (!ch || chan_free(ch)) {
- ret = -EINVAL;
- goto cfg_exit;
- }
-
- pi = ch->dmac->pi;
- dbwidth = pi->pcfg.data_bus_width / 8;
-
- /* Max size of xfer can be pcfg.data_bus_width */
- if (xferunit > dbwidth) {
- ret = -EINVAL;
- goto cfg_exit;
- }
-
- i = 0;
- while (xferunit != (1 << i))
- i++;
-
- /* If valid value */
- if (xferunit == (1 << i))
- ch->rqcfg.brst_size = i;
- else
- ret = -EINVAL;
-
-cfg_exit:
- spin_unlock_irqrestore(&res_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(s3c2410_dma_config);
-
-/* Options that are supported by this driver */
-#define S3C_PL330_FLAGS (S3C2410_DMAF_CIRCULAR | S3C2410_DMAF_AUTOSTART)
-
-int s3c2410_dma_setflags(enum dma_ch id, unsigned int options)
-{
- struct s3c_pl330_chan *ch;
- unsigned long flags;
- int ret = 0;
-
- spin_lock_irqsave(&res_lock, flags);
-
- ch = id_to_chan(id);
-
- if (!ch || chan_free(ch) || options & ~(S3C_PL330_FLAGS))
- ret = -EINVAL;
- else
- ch->options = options;
-
- spin_unlock_irqrestore(&res_lock, flags);
-
- return 0;
-}
-EXPORT_SYMBOL(s3c2410_dma_setflags);
-
-int s3c2410_dma_set_buffdone_fn(enum dma_ch id, s3c2410_dma_cbfn_t rtn)
-{
- struct s3c_pl330_chan *ch;
- unsigned long flags;
- int ret = 0;
-
- spin_lock_irqsave(&res_lock, flags);
-
- ch = id_to_chan(id);
-
- if (!ch || chan_free(ch))
- ret = -EINVAL;
- else
- ch->callback_fn = rtn;
-
- spin_unlock_irqrestore(&res_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(s3c2410_dma_set_buffdone_fn);
-
-int s3c2410_dma_devconfig(enum dma_ch id, enum s3c2410_dmasrc source,
- unsigned long address)
-{
- struct s3c_pl330_chan *ch;
- unsigned long flags;
- int ret = 0;
-
- spin_lock_irqsave(&res_lock, flags);
-
- ch = id_to_chan(id);
-
- if (!ch || chan_free(ch)) {
- ret = -EINVAL;
- goto devcfg_exit;
- }
-
- switch (source) {
- case S3C2410_DMASRC_HW: /* P->M */
- ch->req[0].rqtype = DEVTOMEM;
- ch->req[1].rqtype = DEVTOMEM;
- ch->rqcfg.src_inc = 0;
- ch->rqcfg.dst_inc = 1;
- break;
- case S3C2410_DMASRC_MEM: /* M->P */
- ch->req[0].rqtype = MEMTODEV;
- ch->req[1].rqtype = MEMTODEV;
- ch->rqcfg.src_inc = 1;
- ch->rqcfg.dst_inc = 0;
- break;
- default:
- ret = -EINVAL;
- goto devcfg_exit;
- }
-
- ch->sdaddr = address;
-
-devcfg_exit:
- spin_unlock_irqrestore(&res_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(s3c2410_dma_devconfig);
-
-int s3c2410_dma_getposition(enum dma_ch id, dma_addr_t *src, dma_addr_t *dst)
-{
- struct s3c_pl330_chan *ch = id_to_chan(id);
- struct pl330_chanstatus status;
- int ret;
-
- if (!ch || chan_free(ch))
- return -EINVAL;
-
- ret = pl330_chan_status(ch->pl330_chan_id, &status);
- if (ret < 0)
- return ret;
-
- *src = status.src_addr;
- *dst = status.dst_addr;
-
- return 0;
-}
-EXPORT_SYMBOL(s3c2410_dma_getposition);
-
-static irqreturn_t pl330_irq_handler(int irq, void *data)
-{
- if (pl330_update(data))
- return IRQ_HANDLED;
- else
- return IRQ_NONE;
-}
-
-static int pl330_probe(struct platform_device *pdev)
-{
- struct s3c_pl330_dmac *s3c_pl330_dmac;
- struct s3c_pl330_platdata *pl330pd;
- struct pl330_info *pl330_info;
- struct resource *res;
- int i, ret, irq;
-
- pl330pd = pdev->dev.platform_data;
-
- /* Can't do without the list of _32_ peripherals */
- if (!pl330pd || !pl330pd->peri) {
- dev_err(&pdev->dev, "platform data missing!\n");
- return -ENODEV;
- }
-
- pl330_info = kzalloc(sizeof(*pl330_info), GFP_KERNEL);
- if (!pl330_info)
- return -ENOMEM;
-
- pl330_info->pl330_data = NULL;
- pl330_info->dev = &pdev->dev;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -ENODEV;
- goto probe_err1;
- }
-
- request_mem_region(res->start, resource_size(res), pdev->name);
-
- pl330_info->base = ioremap(res->start, resource_size(res));
- if (!pl330_info->base) {
- ret = -ENXIO;
- goto probe_err2;
- }
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- ret = irq;
- goto probe_err3;
- }
-
- ret = request_irq(irq, pl330_irq_handler, 0,
- dev_name(&pdev->dev), pl330_info);
- if (ret)
- goto probe_err4;
-
- /* Allocate a new DMAC */
- s3c_pl330_dmac = kmalloc(sizeof(*s3c_pl330_dmac), GFP_KERNEL);
- if (!s3c_pl330_dmac) {
- ret = -ENOMEM;
- goto probe_err5;
- }
-
- /* Get operation clock and enable it */
- s3c_pl330_dmac->clk = clk_get(&pdev->dev, "pdma");
- if (IS_ERR(s3c_pl330_dmac->clk)) {
- dev_err(&pdev->dev, "Cannot get operation clock.\n");
- ret = -EINVAL;
- goto probe_err6;
- }
- clk_enable(s3c_pl330_dmac->clk);
-
- ret = pl330_add(pl330_info);
- if (ret)
- goto probe_err7;
-
- /* Hook the info */
- s3c_pl330_dmac->pi = pl330_info;
-
- /* No busy channels */
- s3c_pl330_dmac->busy_chan = 0;
-
- s3c_pl330_dmac->kmcache = kmem_cache_create(dev_name(&pdev->dev),
- sizeof(struct s3c_pl330_xfer), 0, 0, NULL);
-
- if (!s3c_pl330_dmac->kmcache) {
- ret = -ENOMEM;
- goto probe_err8;
- }
-
- /* Get the list of peripherals */
- s3c_pl330_dmac->peri = pl330pd->peri;
-
- /* Attach to the list of DMACs */
- list_add_tail(&s3c_pl330_dmac->node, &dmac_list);
-
- /* Create a channel for each peripheral in the DMAC
- * that is, if it doesn't already exist
- */
- for (i = 0; i < PL330_MAX_PERI; i++)
- if (s3c_pl330_dmac->peri[i] != DMACH_MAX)
- chan_add(s3c_pl330_dmac->peri[i]);
-
- printk(KERN_INFO
- "Loaded driver for PL330 DMAC-%d %s\n", pdev->id, pdev->name);
- printk(KERN_INFO
- "\tDBUFF-%ux%ubytes Num_Chans-%u Num_Peri-%u Num_Events-%u\n",
- pl330_info->pcfg.data_buf_dep,
- pl330_info->pcfg.data_bus_width / 8, pl330_info->pcfg.num_chan,
- pl330_info->pcfg.num_peri, pl330_info->pcfg.num_events);
-
- return 0;
-
-probe_err8:
- pl330_del(pl330_info);
-probe_err7:
- clk_disable(s3c_pl330_dmac->clk);
- clk_put(s3c_pl330_dmac->clk);
-probe_err6:
- kfree(s3c_pl330_dmac);
-probe_err5:
- free_irq(irq, pl330_info);
-probe_err4:
-probe_err3:
- iounmap(pl330_info->base);
-probe_err2:
- release_mem_region(res->start, resource_size(res));
-probe_err1:
- kfree(pl330_info);
-
- return ret;
-}
-
-static int pl330_remove(struct platform_device *pdev)
-{
- struct s3c_pl330_dmac *dmac, *d;
- struct s3c_pl330_chan *ch;
- unsigned long flags;
- int del, found;
-
- if (!pdev->dev.platform_data)
- return -EINVAL;
-
- spin_lock_irqsave(&res_lock, flags);
-
- found = 0;
- list_for_each_entry(d, &dmac_list, node)
- if (d->pi->dev == &pdev->dev) {
- found = 1;
- break;
- }
-
- if (!found) {
- spin_unlock_irqrestore(&res_lock, flags);
- return 0;
- }
-
- dmac = d;
-
- /* Remove all Channels that are managed only by this DMAC */
- list_for_each_entry(ch, &chan_list, node) {
-
- /* Only channels that are handled by this DMAC */
- if (iface_of_dmac(dmac, ch->id))
- del = 1;
- else
- continue;
-
- /* Don't remove if some other DMAC has it too */
- list_for_each_entry(d, &dmac_list, node)
- if (d != dmac && iface_of_dmac(d, ch->id)) {
- del = 0;
- break;
- }
-
- if (del) {
- spin_unlock_irqrestore(&res_lock, flags);
- s3c2410_dma_free(ch->id, ch->client);
- spin_lock_irqsave(&res_lock, flags);
- list_del(&ch->node);
- kfree(ch);
- }
- }
-
- /* Disable operation clock */
- clk_disable(dmac->clk);
- clk_put(dmac->clk);
-
- /* Remove the DMAC */
- list_del(&dmac->node);
- kfree(dmac);
-
- spin_unlock_irqrestore(&res_lock, flags);
-
- return 0;
-}
-
-static struct platform_driver pl330_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "s3c-pl330",
- },
- .probe = pl330_probe,
- .remove = pl330_remove,
-};
-
-static int __init pl330_init(void)
-{
- return platform_driver_register(&pl330_driver);
-}
-module_init(pl330_init);
-
-static void __exit pl330_exit(void)
-{
- platform_driver_unregister(&pl330_driver);
- return;
-}
-module_exit(pl330_exit);
-
-MODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>");
-MODULE_DESCRIPTION("Driver for PL330 DMA Controller");
-MODULE_LICENSE("GPL");
--
1.7.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH V2 11/12] spi/s3c64xx: Add support DMA engine API
2011-07-13 8:47 [PATCH V2 00/12] To use DMA generic APIs for Samsung DMA Kukjin Kim
` (9 preceding siblings ...)
2011-07-13 8:47 ` [PATCH V2 10/12] ARM: SAMSUNG: Remove S3C-PL330-DMA driver Kukjin Kim
@ 2011-07-13 8:47 ` Kukjin Kim
2011-07-13 8:47 ` [PATCH V2 12/12] ASoC: Samsung: Update DMA interface Kukjin Kim
11 siblings, 0 replies; 23+ messages in thread
From: Kukjin Kim @ 2011-07-13 8:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Boojin Kim <boojin.kim@samsung.com>
This patch adds to support DMA generic API to transfer raw
SPI data. Basiclly the spi driver uses DMA generic API if
architecture supports it. Otherwise, uses Samsung specific
S3C-PL330 APIs.
Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
Cc: Grant Likely <grant.likely@secretlab.ca>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
drivers/spi/spi_s3c64xx.c | 141 ++++++++++++++++++++++-----------------------
1 files changed, 69 insertions(+), 72 deletions(-)
diff --git a/drivers/spi/spi_s3c64xx.c b/drivers/spi/spi_s3c64xx.c
index 8945e20..a4cf76a 100644
--- a/drivers/spi/spi_s3c64xx.c
+++ b/drivers/spi/spi_s3c64xx.c
@@ -172,6 +172,9 @@ struct s3c64xx_spi_driver_data {
unsigned state;
unsigned cur_mode, cur_bpw;
unsigned cur_speed;
+ unsigned rx_ch;
+ unsigned tx_ch;
+ struct samsung_dma_ops *ops;
};
static struct s3c2410_dma_client s3c64xx_spi_dma_client = {
@@ -227,6 +230,38 @@ static void flush_fifo(struct s3c64xx_spi_driver_data *sdd)
writel(val, regs + S3C64XX_SPI_CH_CFG);
}
+static void s3c64xx_spi_dma_rxcb(void *data)
+{
+ struct s3c64xx_spi_driver_data *sdd
+ = (struct s3c64xx_spi_driver_data *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sdd->lock, flags);
+
+ sdd->state &= ~RXBUSY;
+ /* If the other done */
+ if (!(sdd->state & TXBUSY))
+ complete(&sdd->xfer_completion);
+
+ spin_unlock_irqrestore(&sdd->lock, flags);
+}
+
+static void s3c64xx_spi_dma_txcb(void *data)
+{
+ struct s3c64xx_spi_driver_data *sdd
+ = (struct s3c64xx_spi_driver_data *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sdd->lock, flags);
+
+ sdd->state &= ~TXBUSY;
+ /* If the other done */
+ if (!(sdd->state & RXBUSY))
+ complete(&sdd->xfer_completion);
+
+ spin_unlock_irqrestore(&sdd->lock, flags);
+}
+
static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
struct spi_device *spi,
struct spi_transfer *xfer, int dma_mode)
@@ -234,6 +269,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
void __iomem *regs = sdd->regs;
u32 modecfg, chcfg;
+ struct samsung_dma_prep_info info;
modecfg = readl(regs + S3C64XX_SPI_MODE_CFG);
modecfg &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON);
@@ -259,10 +295,14 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
chcfg |= S3C64XX_SPI_CH_TXCH_ON;
if (dma_mode) {
modecfg |= S3C64XX_SPI_MODE_TXDMA_ON;
- s3c2410_dma_config(sdd->tx_dmach, sdd->cur_bpw / 8);
- s3c2410_dma_enqueue(sdd->tx_dmach, (void *)sdd,
- xfer->tx_dma, xfer->len);
- s3c2410_dma_ctrl(sdd->tx_dmach, S3C2410_DMAOP_START);
+ info.cap = DMA_SLAVE;
+ info.direction = DMA_TO_DEVICE;
+ info.buf = xfer->tx_dma;
+ info.len = xfer->len;
+ info.fp = s3c64xx_spi_dma_txcb;
+ info.fp_param = sdd;
+ sdd->ops->prepare(sdd->tx_ch, &info);
+ sdd->ops->trigger(sdd->tx_ch);
} else {
switch (sdd->cur_bpw) {
case 32:
@@ -294,10 +334,14 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
| S3C64XX_SPI_PACKET_CNT_EN,
regs + S3C64XX_SPI_PACKET_CNT);
- s3c2410_dma_config(sdd->rx_dmach, sdd->cur_bpw / 8);
- s3c2410_dma_enqueue(sdd->rx_dmach, (void *)sdd,
- xfer->rx_dma, xfer->len);
- s3c2410_dma_ctrl(sdd->rx_dmach, S3C2410_DMAOP_START);
+ info.cap = DMA_SLAVE;
+ info.direction = DMA_FROM_DEVICE;
+ info.buf = xfer->rx_dma;
+ info.len = xfer->len;
+ info.fp = s3c64xx_spi_dma_rxcb;
+ info.fp_param = sdd;
+ sdd->ops->prepare(sdd->rx_ch, &info);
+ sdd->ops->trigger(sdd->rx_ch);
}
}
@@ -483,46 +527,6 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
}
}
-static void s3c64xx_spi_dma_rxcb(struct s3c2410_dma_chan *chan, void *buf_id,
- int size, enum s3c2410_dma_buffresult res)
-{
- struct s3c64xx_spi_driver_data *sdd = buf_id;
- unsigned long flags;
-
- spin_lock_irqsave(&sdd->lock, flags);
-
- if (res == S3C2410_RES_OK)
- sdd->state &= ~RXBUSY;
- else
- dev_err(&sdd->pdev->dev, "DmaAbrtRx-%d\n", size);
-
- /* If the other done */
- if (!(sdd->state & TXBUSY))
- complete(&sdd->xfer_completion);
-
- spin_unlock_irqrestore(&sdd->lock, flags);
-}
-
-static void s3c64xx_spi_dma_txcb(struct s3c2410_dma_chan *chan, void *buf_id,
- int size, enum s3c2410_dma_buffresult res)
-{
- struct s3c64xx_spi_driver_data *sdd = buf_id;
- unsigned long flags;
-
- spin_lock_irqsave(&sdd->lock, flags);
-
- if (res == S3C2410_RES_OK)
- sdd->state &= ~TXBUSY;
- else
- dev_err(&sdd->pdev->dev, "DmaAbrtTx-%d \n", size);
-
- /* If the other done */
- if (!(sdd->state & RXBUSY))
- complete(&sdd->xfer_completion);
-
- spin_unlock_irqrestore(&sdd->lock, flags);
-}
-
#define XFER_DMAADDR_INVALID DMA_BIT_MASK(32)
static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd,
@@ -697,12 +701,10 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd,
if (use_dma) {
if (xfer->tx_buf != NULL
&& (sdd->state & TXBUSY))
- s3c2410_dma_ctrl(sdd->tx_dmach,
- S3C2410_DMAOP_FLUSH);
+ sdd->ops->stop(sdd->tx_ch);
if (xfer->rx_buf != NULL
&& (sdd->state & RXBUSY))
- s3c2410_dma_ctrl(sdd->rx_dmach,
- S3C2410_DMAOP_FLUSH);
+ sdd->ops->stop(sdd->rx_ch);
}
goto out;
@@ -742,24 +744,19 @@ out:
static int acquire_dma(struct s3c64xx_spi_driver_data *sdd)
{
- if (s3c2410_dma_request(sdd->rx_dmach,
- &s3c64xx_spi_dma_client, NULL) < 0) {
- dev_err(&sdd->pdev->dev, "cannot get RxDMA\n");
- return 0;
- }
- s3c2410_dma_set_buffdone_fn(sdd->rx_dmach, s3c64xx_spi_dma_rxcb);
- s3c2410_dma_devconfig(sdd->rx_dmach, S3C2410_DMASRC_HW,
- sdd->sfr_start + S3C64XX_SPI_RX_DATA);
-
- if (s3c2410_dma_request(sdd->tx_dmach,
- &s3c64xx_spi_dma_client, NULL) < 0) {
- dev_err(&sdd->pdev->dev, "cannot get TxDMA\n");
- s3c2410_dma_free(sdd->rx_dmach, &s3c64xx_spi_dma_client);
- return 0;
- }
- s3c2410_dma_set_buffdone_fn(sdd->tx_dmach, s3c64xx_spi_dma_txcb);
- s3c2410_dma_devconfig(sdd->tx_dmach, S3C2410_DMASRC_MEM,
- sdd->sfr_start + S3C64XX_SPI_TX_DATA);
+
+ struct samsung_dma_info info;
+ sdd->ops = samsung_dma_get_ops();
+
+ info.cap = DMA_SLAVE;
+ info.client = &s3c64xx_spi_dma_client;
+ info.direction = DMA_FROM_DEVICE;
+ info.fifo = sdd->sfr_start + S3C64XX_SPI_RX_DATA;
+ info.width = sdd->cur_bpw / 8;
+ sdd->rx_ch = sdd->ops->request(sdd->rx_dmach, &info);
+ info.direction = DMA_TO_DEVICE;
+ info.fifo = sdd->sfr_start + S3C64XX_SPI_TX_DATA;
+ sdd->tx_ch = sdd->ops->request(sdd->tx_dmach, &info);
return 1;
}
@@ -800,8 +797,8 @@ static void s3c64xx_spi_work(struct work_struct *work)
spin_unlock_irqrestore(&sdd->lock, flags);
/* Free DMA channels */
- s3c2410_dma_free(sdd->tx_dmach, &s3c64xx_spi_dma_client);
- s3c2410_dma_free(sdd->rx_dmach, &s3c64xx_spi_dma_client);
+ sdd->ops->release(sdd->rx_ch, &s3c64xx_spi_dma_client);
+ sdd->ops->release(sdd->tx_ch, &s3c64xx_spi_dma_client);
}
static int s3c64xx_spi_transfer(struct spi_device *spi,
--
1.7.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH V2 12/12] ASoC: Samsung: Update DMA interface
2011-07-13 8:47 [PATCH V2 00/12] To use DMA generic APIs for Samsung DMA Kukjin Kim
` (10 preceding siblings ...)
2011-07-13 8:47 ` [PATCH V2 11/12] spi/s3c64xx: Add support DMA engine API Kukjin Kim
@ 2011-07-13 8:47 ` Kukjin Kim
2011-07-13 23:57 ` Mark Brown
11 siblings, 1 reply; 23+ messages in thread
From: Kukjin Kim @ 2011-07-13 8:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Boojin Kim <boojin.kim@samsung.com>
This patch adds to support the DMA PL330 driver that uses
DMA generic API. Samsung sound driver uses DMA generic API
if architecture supports it. Otherwise, use samsung specific
S3C-PL330 API driver to transfer PCM data.
Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
Cc: Jassi Brar <jassisinghbrar@gmail.com>
Cc: Liam Girdwood <lrg@ti.com>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
arch/arm/mach-s3c2410/include/mach/dma.h | 2 +-
arch/arm/mach-s3c64xx/include/mach/dma.h | 2 +-
arch/arm/plat-samsung/include/plat/dma-pl330.h | 2 +-
sound/soc/samsung/ac97.c | 8 +-
sound/soc/samsung/dma.c | 158 ++++++++++--------------
sound/soc/samsung/dma.h | 4 +-
6 files changed, 79 insertions(+), 97 deletions(-)
diff --git a/arch/arm/mach-s3c2410/include/mach/dma.h b/arch/arm/mach-s3c2410/include/mach/dma.h
index 71a662d..c69d60a 100644
--- a/arch/arm/mach-s3c2410/include/mach/dma.h
+++ b/arch/arm/mach-s3c2410/include/mach/dma.h
@@ -197,7 +197,7 @@ struct s3c2410_dma_chan {
typedef unsigned long dma_device_t;
-static inline bool s3c_dma_has_circular(void)
+static inline bool dma_has_circular(void)
{
return false;
}
diff --git a/arch/arm/mach-s3c64xx/include/mach/dma.h b/arch/arm/mach-s3c64xx/include/mach/dma.h
index 0a5d926..d752e27 100644
--- a/arch/arm/mach-s3c64xx/include/mach/dma.h
+++ b/arch/arm/mach-s3c64xx/include/mach/dma.h
@@ -58,7 +58,7 @@ enum dma_ch {
DMACH_MAX /* the end */
};
-static __inline__ bool s3c_dma_has_circular(void)
+static inline bool dma_has_circular(void)
{
return true;
}
diff --git a/arch/arm/plat-samsung/include/plat/dma-pl330.h b/arch/arm/plat-samsung/include/plat/dma-pl330.h
index dbc1288..4bd0e49 100644
--- a/arch/arm/plat-samsung/include/plat/dma-pl330.h
+++ b/arch/arm/plat-samsung/include/plat/dma-pl330.h
@@ -100,7 +100,7 @@ struct s3c2410_dma_client {
char *name;
};
-static inline bool s3c_dma_has_circular(void)
+static inline bool dma_has_circular(void)
{
return true;
}
diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c
index f97110e..e09cb9d 100644
--- a/sound/soc/samsung/ac97.c
+++ b/sound/soc/samsung/ac97.c
@@ -271,7 +271,9 @@ static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
- s3c2410_dma_ctrl(dma_data->channel, S3C2410_DMAOP_STARTED);
+ if (!dma_data->ops)
+ dma_data->ops = samsung_dma_get_ops();
+ dma_data->ops->started(dma_data->channel);
return 0;
}
@@ -317,7 +319,9 @@ static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
- s3c2410_dma_ctrl(dma_data->channel, S3C2410_DMAOP_STARTED);
+ if (!dma_data->ops)
+ dma_data->ops = samsung_dma_get_ops();
+ dma_data->ops->started(dma_data->channel);
return 0;
}
diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c
index 5cb3b88..b5f5f6b 100644
--- a/sound/soc/samsung/dma.c
+++ b/sound/soc/samsung/dma.c
@@ -17,6 +17,9 @@
#include <linux/slab.h>
#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/amba/pl330.h>
+
#include <sound/soc.h>
#include <sound/pcm_params.h>
@@ -62,77 +65,80 @@ struct runtime_data {
struct s3c_dma_params *params;
};
+static void audio_buffdone(void *data);
+
/* dma_enqueue
*
* place a dma buffer onto the queue for the dma system
* to handle.
-*/
+ */
static void dma_enqueue(struct snd_pcm_substream *substream)
{
struct runtime_data *prtd = substream->runtime->private_data;
dma_addr_t pos = prtd->dma_pos;
unsigned int limit;
- int ret;
+ struct samsung_dma_prep_info dma_info;
pr_debug("Entered %s\n", __func__);
- if (s3c_dma_has_circular())
- limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period;
+ limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period;
+
+ if (dma_has_circular())
+ dma_info.cap = DMA_CYCLIC;
else
- limit = prtd->dma_limit;
+ dma_info.cap = DMA_SLAVE;
- pr_debug("%s: loaded %d, limit %d\n",
- __func__, prtd->dma_loaded, limit);
+ dma_info.direction =
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ dma_info.fp = audio_buffdone;
+ dma_info.fp_param = substream;
+ dma_info.period = prtd->dma_period;
+ dma_info.len = prtd->dma_period*limit;
while (prtd->dma_loaded < limit) {
- unsigned long len = prtd->dma_period;
-
pr_debug("dma_loaded: %d\n", prtd->dma_loaded);
- if ((pos + len) > prtd->dma_end) {
- len = prtd->dma_end - pos;
- pr_debug("%s: corrected dma len %ld\n", __func__, len);
+ if ((pos + dma_info.period) > prtd->dma_end) {
+ dma_info.period = prtd->dma_end - pos;
+ pr_debug("%s: corrected dma len %ld\n",
+ __func__, dma_info.period);
}
- ret = s3c2410_dma_enqueue(prtd->params->channel,
- substream, pos, len);
+ dma_info.buf = pos;
+ prtd->params->ops->prepare(prtd->params->ch, &dma_info);
- if (ret == 0) {
- prtd->dma_loaded++;
- pos += prtd->dma_period;
- if (pos >= prtd->dma_end)
- pos = prtd->dma_start;
- } else
- break;
+ prtd->dma_loaded++;
+ pos += prtd->dma_period;
+ if (pos >= prtd->dma_end)
+ pos = prtd->dma_start;
}
prtd->dma_pos = pos;
}
-static void audio_buffdone(struct s3c2410_dma_chan *channel,
- void *dev_id, int size,
- enum s3c2410_dma_buffresult result)
+static void audio_buffdone(void *data)
{
- struct snd_pcm_substream *substream = dev_id;
- struct runtime_data *prtd;
+ struct snd_pcm_substream *substream = data;
+ struct runtime_data *prtd = substream->runtime->private_data;
pr_debug("Entered %s\n", __func__);
- if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR)
- return;
+ if (prtd->state & ST_RUNNING) {
+ prtd->dma_pos += prtd->dma_period;
+ if (prtd->dma_pos >= prtd->dma_end)
+ prtd->dma_pos = prtd->dma_start;
- prtd = substream->runtime->private_data;
+ if (substream)
+ snd_pcm_period_elapsed(substream);
- if (substream)
- snd_pcm_period_elapsed(substream);
-
- spin_lock(&prtd->lock);
- if (prtd->state & ST_RUNNING && !s3c_dma_has_circular()) {
- prtd->dma_loaded--;
- dma_enqueue(substream);
+ spin_lock(&prtd->lock);
+ if (!dma_has_circular()) {
+ prtd->dma_loaded--;
+ dma_enqueue(substream);
+ }
+ spin_unlock(&prtd->lock);
}
-
- spin_unlock(&prtd->lock);
}
static int dma_hw_params(struct snd_pcm_substream *substream,
@@ -144,8 +150,7 @@ static int dma_hw_params(struct snd_pcm_substream *substream,
unsigned long totbytes = params_buffer_bytes(params);
struct s3c_dma_params *dma =
snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
- int ret = 0;
-
+ struct samsung_dma_info dma_info;
pr_debug("Entered %s\n", __func__);
@@ -154,32 +159,26 @@ static int dma_hw_params(struct snd_pcm_substream *substream,
if (!dma)
return 0;
- /* this may get called several times by oss emulation
- * with different params -HW */
if (prtd->params == NULL) {
- /* prepare DMA */
prtd->params = dma;
- pr_debug("params %p, client %p, channel %d\n", prtd->params,
- prtd->params->client, prtd->params->channel);
-
- ret = s3c2410_dma_request(prtd->params->channel,
- prtd->params->client, NULL);
-
- if (ret < 0) {
- printk(KERN_ERR "failed to get dma channel\n");
- return ret;
- }
-
- /* use the circular buffering if we have it available. */
- if (s3c_dma_has_circular())
- s3c2410_dma_setflags(prtd->params->channel,
- S3C2410_DMAF_CIRCULAR);
+ prtd->params->ops = samsung_dma_get_ops();
+
+ /* request channel */
+ if (dma_has_circular())
+ dma_info.cap = DMA_CYCLIC;
+ else
+ dma_info.cap = DMA_SLAVE;
+ dma_info.client = prtd->params->client;
+ dma_info.direction =
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ dma_info.width = prtd->params->dma_size;
+ dma_info.fifo = prtd->params->dma_addr;
+ prtd->params->ch = prtd->params->ops->request(
+ prtd->params->channel, &dma_info);
}
- s3c2410_dma_set_buffdone_fn(prtd->params->channel,
- audio_buffdone);
-
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
runtime->dma_bytes = totbytes;
@@ -206,7 +205,8 @@ static int dma_hw_free(struct snd_pcm_substream *substream)
snd_pcm_set_runtime_buffer(substream, NULL);
if (prtd->params) {
- s3c2410_dma_free(prtd->params->channel, prtd->params->client);
+ prtd->params->ops->release(prtd->params->ch,
+ prtd->params->client);
prtd->params = NULL;
}
@@ -225,23 +225,8 @@ static int dma_prepare(struct snd_pcm_substream *substream)
if (!prtd->params)
return 0;
- /* channel needs configuring for mem=>device, increment memory addr,
- * sync to pclk, half-word transfers to the IIS-FIFO. */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- s3c2410_dma_devconfig(prtd->params->channel,
- S3C2410_DMASRC_MEM,
- prtd->params->dma_addr);
- } else {
- s3c2410_dma_devconfig(prtd->params->channel,
- S3C2410_DMASRC_HW,
- prtd->params->dma_addr);
- }
+ prtd->params->ops->flush(prtd->params->ch);
- s3c2410_dma_config(prtd->params->channel,
- prtd->params->dma_size);
-
- /* flush the DMA channel */
- s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH);
prtd->dma_loaded = 0;
prtd->dma_pos = prtd->dma_start;
@@ -265,14 +250,14 @@ static int dma_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
prtd->state |= ST_RUNNING;
- s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_START);
+ prtd->params->ops->trigger(prtd->params->ch);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
prtd->state &= ~ST_RUNNING;
- s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STOP);
+ prtd->params->ops->stop(prtd->params->ch);
break;
default:
@@ -291,21 +276,12 @@ dma_pointer(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct runtime_data *prtd = runtime->private_data;
unsigned long res;
- dma_addr_t src, dst;
pr_debug("Entered %s\n", __func__);
- spin_lock(&prtd->lock);
- s3c2410_dma_getposition(prtd->params->channel, &src, &dst);
-
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- res = dst - prtd->dma_start;
- else
- res = src - prtd->dma_start;
-
- spin_unlock(&prtd->lock);
+ res = prtd->dma_pos - prtd->dma_start;
- pr_debug("Pointer %x %x\n", src, dst);
+ pr_debug("Pointer offset: %lu\n", res);
/* we seem to be getting the odd error from the pcm library due
* to out-of-bounds pointers. this is maybe due to the dma engine
diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h
index c506592..7d1ead7 100644
--- a/sound/soc/samsung/dma.h
+++ b/sound/soc/samsung/dma.h
@@ -6,7 +6,7 @@
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
- * ALSA PCM interface for the Samsung S3C24xx CPU
+ * ALSA PCM interface for the Samsung SoC
*/
#ifndef _S3C_AUDIO_H
@@ -17,6 +17,8 @@ struct s3c_dma_params {
int channel; /* Channel ID */
dma_addr_t dma_addr;
int dma_size; /* Size of the DMA transfer */
+ unsigned ch;
+ struct samsung_dma_ops *ops;
};
#endif
--
1.7.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH V2 12/12] ASoC: Samsung: Update DMA interface
2011-07-13 8:47 ` [PATCH V2 12/12] ASoC: Samsung: Update DMA interface Kukjin Kim
@ 2011-07-13 23:57 ` Mark Brown
2011-07-16 0:01 ` Kukjin Kim
0 siblings, 1 reply; 23+ messages in thread
From: Mark Brown @ 2011-07-13 23:57 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, Jul 13, 2011 at 05:47:37PM +0900, Kukjin Kim wrote:
> -static inline bool s3c_dma_has_circular(void)
> +static inline bool dma_has_circular(void)
> {
> return false;
> }
The namespacing here doesn't look great, this is still a Samsung
specific internal API.
Otherwise this looks good.
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH V2 12/12] ASoC: Samsung: Update DMA interface
2011-07-13 23:57 ` Mark Brown
@ 2011-07-16 0:01 ` Kukjin Kim
0 siblings, 0 replies; 23+ messages in thread
From: Kukjin Kim @ 2011-07-16 0:01 UTC (permalink / raw)
To: linux-arm-kernel
Mark Brown wrote:
>
> On Wed, Jul 13, 2011 at 05:47:37PM +0900, Kukjin Kim wrote:
>
> > -static inline bool s3c_dma_has_circular(void)
> > +static inline bool dma_has_circular(void)
> > {
> > return false;
> > }
>
> The namespacing here doesn't look great, this is still a Samsung
> specific internal API.
>
OK, will keep it.
> Otherwise this looks good.
Thanks.
Best regards,
Kgene.
--
Kukjin Kim <kgene.kim@samsung.com>, Senior Engineer,
SW Solution Development Team, Samsung Electronics Co., Ltd.
^ permalink raw reply [flat|nested] 23+ messages in thread