diff options
| author | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-04-13 07:10:24 -0400 |
|---|---|---|
| committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-07-31 07:06:20 -0400 |
| commit | 7bedaa5537604f34d1d63c5ec7891e559d2a61ed (patch) | |
| tree | 7d13d22792109d42ed4bae79f2fa9880d23a476e | |
| parent | 571fa74034701391b1be2ad193f684acfdeb75d1 (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/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/dma/Makefile | 1 | ||||
| -rw-r--r-- | drivers/dma/omap-dma.c | 524 | ||||
| -rw-r--r-- | include/linux/omap-dma.h | 22 |
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 | ||
| 264 | config DMA_OMAP | ||
| 265 | tristate "OMAP DMA support" | ||
| 266 | depends on ARCH_OMAP | ||
| 267 | select DMA_ENGINE | ||
| 268 | select DMA_VIRTUAL_CHANNELS | ||
| 269 | |||
| 264 | config DMA_ENGINE | 270 | config 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 | |||
| 29 | obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o | 29 | obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o |
| 30 | obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o | 30 | obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o |
| 31 | obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o | 31 | obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o |
| 32 | obj-$(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 | |||
| 23 | struct omap_dmadev { | ||
| 24 | struct dma_device ddev; | ||
| 25 | spinlock_t lock; | ||
| 26 | struct tasklet_struct task; | ||
| 27 | struct list_head pending; | ||
| 28 | }; | ||
| 29 | |||
| 30 | struct 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 | |||
| 42 | struct 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 | |||
| 48 | struct 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 | |||
| 62 | static 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 | |||
| 68 | static inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d) | ||
| 69 | { | ||
| 70 | return container_of(d, struct omap_dmadev, ddev); | ||
| 71 | } | ||
| 72 | |||
| 73 | static 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 | |||
| 78 | static 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 | |||
| 83 | static void omap_dma_desc_free(struct virt_dma_desc *vd) | ||
| 84 | { | ||
| 85 | kfree(container_of(vd, struct omap_desc, vd)); | ||
| 86 | } | ||
| 87 | |||
| 88 | static 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 | |||
| 106 | static 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 | |||
| 131 | static 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 | */ | ||
| 157 | static 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 | } | ||
