aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2012-04-13 07:10:24 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2012-07-31 07:06:20 -0400
commit7bedaa5537604f34d1d63c5ec7891e559d2a61ed (patch)
tree7d13d22792109d42ed4bae79f2fa9880d23a476e
parent571fa74034701391b1be2ad193f684acfdeb75d1 (diff)
dmaengine: add OMAP DMA engine driver
Tested-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--drivers/dma/Kconfig6
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/omap-dma.c524
-rw-r--r--include/linux/omap-dma.h22
4 files changed, 553 insertions, 0 deletions
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index eb2b60e8e1cb..8be3bf68c0bd 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -261,6 +261,12 @@ config DMA_SA11X0
261 SA-1110 SoCs. This DMA engine can only be used with on-chip 261 SA-1110 SoCs. This DMA engine can only be used with on-chip
262 devices. 262 devices.
263 263
264config DMA_OMAP
265 tristate "OMAP DMA support"
266 depends on ARCH_OMAP
267 select DMA_ENGINE
268 select DMA_VIRTUAL_CHANNELS
269
264config DMA_ENGINE 270config DMA_ENGINE
265 bool 271 bool
266 272
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index fc05f7ddac7e..ddc291a2116a 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_PCH_DMA) += pch_dma.o
29obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o 29obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
30obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o 30obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
31obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o 31obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
32obj-$(CONFIG_DMA_OMAP) += omap-dma.o
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c
new file mode 100644
index 000000000000..fe33ddd8eb0f
--- /dev/null
+++ b/drivers/dma/omap-dma.c
@@ -0,0 +1,524 @@
1/*
2 * OMAP DMAengine support
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#include <linux/dmaengine.h>
9#include <linux/dma-mapping.h>
10#include <linux/err.h>
11#include <linux/init.h>
12#include <linux/interrupt.h>
13#include <linux/list.h>
14#include <linux/module.h>
15#include <linux/omap-dma.h>
16#include <linux/platform_device.h>
17#include <linux/slab.h>
18#include <linux/spinlock.h>
19
20#include "virt-dma.h"
21#include <plat/dma.h>
22
23struct omap_dmadev {
24 struct dma_device ddev;
25 spinlock_t lock;
26 struct tasklet_struct task;
27 struct list_head pending;
28};
29
30struct omap_chan {
31 struct virt_dma_chan vc;
32 struct list_head node;
33
34 struct dma_slave_config cfg;
35 unsigned dma_sig;
36
37 int dma_ch;
38 struct omap_desc *desc;
39 unsigned sgidx;
40};
41
42struct omap_sg {
43 dma_addr_t addr;
44 uint32_t en; /* number of elements (24-bit) */
45 uint32_t fn; /* number of frames (16-bit) */
46};
47
48struct omap_desc {
49 struct virt_dma_desc vd;
50 enum dma_transfer_direction dir;
51 dma_addr_t dev_addr;
52
53 uint8_t es; /* OMAP_DMA_DATA_TYPE_xxx */
54 uint8_t sync_mode; /* OMAP_DMA_SYNC_xxx */
55 uint8_t sync_type; /* OMAP_DMA_xxx_SYNC* */
56 uint8_t periph_port; /* Peripheral port */
57
58 unsigned sglen;
59 struct omap_sg sg[0];
60};
61
62static const unsigned es_bytes[] = {
63 [OMAP_DMA_DATA_TYPE_S8] = 1,
64 [OMAP_DMA_DATA_TYPE_S16] = 2,
65 [OMAP_DMA_DATA_TYPE_S32] = 4,
66};
67
68static inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d)
69{
70 return container_of(d, struct omap_dmadev, ddev);
71}
72
73static inline struct omap_chan *to_omap_dma_chan(struct dma_chan *c)
74{
75 return container_of(c, struct omap_chan, vc.chan);
76}
77
78static inline struct omap_desc *to_omap_dma_desc(struct dma_async_tx_descriptor *t)
79{
80 return container_of(t, struct omap_desc, vd.tx);
81}
82
83static void omap_dma_desc_free(struct virt_dma_desc *vd)
84{
85 kfree(container_of(vd, struct omap_desc, vd));
86}
87
88static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
89 unsigned idx)
90{
91 struct omap_sg *sg = d->sg + idx;
92
93 if (d->dir == DMA_DEV_TO_MEM)
94 omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
95 OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
96 else
97 omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
98 OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
99
100 omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
101 d->sync_mode, c->dma_sig, d->sync_type);
102
103 omap_start_dma(c->dma_ch);
104}
105
106static void omap_dma_start_desc(struct omap_chan *c)
107{
108 struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
109 struct omap_desc *d;
110
111 if (!vd) {
112 c->desc = NULL;
113 return;
114 }
115
116 list_del(&vd->node);
117
118 c->desc = d = to_omap_dma_desc(&vd->tx);
119 c->sgidx = 0;
120
121 if (d->dir == DMA_DEV_TO_MEM)
122 omap_set_dma_src_params(c->dma_ch, d->periph_port,
123 OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
124 else
125 omap_set_dma_dest_params(c->dma_ch, d->periph_port,
126 OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
127
128 omap_dma_start_sg(c, d, 0);
129}
130
131static void omap_dma_callback(int ch, u16 status, void *data)
132{
133 struct omap_chan *c = data;
134 struct omap_desc *d;
135 unsigned long flags;
136
137 spin_lock_irqsave(&c->vc.lock, flags);
138 d = c->desc;
139 if (d) {
140 if (++c->sgidx < d->sglen) {
141 omap_dma_start_sg(c, d, c->sgidx);
142 } else {
143 omap_dma_start_desc(c);
144 vchan_cookie_complete(&d->vd);
145 }
146 }
147 spin_unlock_irqrestore(&c->vc.lock, flags);
148}
149
150/*
151 * This callback schedules all pending channels. We could be more
152 * clever here by postponing allocation of the real DMA channels to
153 * this point, and freeing them when our virtual channel becomes idle.
154 *
155 * We would then need to deal with 'all channels in-use'
156 */
157static void omap_dma_sched(unsigned long data)
158{
159 struct omap_dmadev *d = (struct omap_dmadev *)data;
160 LIST_HEAD(head);
161
162 spin_lock_irq(&d->lock);
163 list_splice_tail_init(&d->pending, &head);
164 spin_unlock_irq(&d->lock);
165
166 while (!list_empty(&head)) {
167 struct omap_chan *c = list_first_entry(&head,
168 struct omap_chan, node);
169
170 spin_lock_irq(&c->vc.lock);
171 list_del_init(&c->node);
172 omap_dma_start_desc(c);
173 spin_unlock_irq(&c->vc.lock);
174 }
175}