brcm2708-gpu-fw: update to latest version
[openwrt/openwrt.git] / target / linux / brcm2708 / patches-4.4 / 0399-dmaengine-bcm2835-add-slave_sg-support-to-bcm2835-dm.patch
1 From 7cd84b455f4681e4db6cd9e3cd628bcd06df0978 Mon Sep 17 00:00:00 2001
2 From: Martin Sperl <kernel@martin.sperl.org>
3 Date: Wed, 16 Mar 2016 12:25:01 -0700
4 Subject: [PATCH 399/423] dmaengine: bcm2835: add slave_sg support to
5 bcm2835-dma
6
7 Add slave_sg support to bcm2835-dma using shared allocation
8 code for bcm2835_desc and DMA-control blocks already used by
9 dma_cyclic.
10
11 Note that bcm2835_dma_callback had to get modified to support
12 both modes of operation (cyclic and non-cyclic).
13
14 Tested using:
15 * Hifiberry I2S card (using cyclic DMA)
16 * fb_st7735r SPI-framebuffer (using slave_sg DMA via spi-bcm2835)
17 playing BigBuckBunny for audio and video.
18
19 Signed-off-by: Martin Sperl <kernel@martin.sperl.org>
20 Reviewed-by: Eric Anholt <eric@anholt.net>
21 Signed-off-by: Eric Anholt <eric@anholt.net>
22 Signed-off-by: Vinod Koul <vinod.koul@intel.com>
23 ---
24 drivers/dma/bcm2835-dma.c | 113 ++++++++++++++++++++++++++++++++++++++++++++--
25 1 file changed, 108 insertions(+), 5 deletions(-)
26
27 --- a/drivers/dma/bcm2835-dma.c
28 +++ b/drivers/dma/bcm2835-dma.c
29 @@ -260,6 +260,23 @@ static void bcm2835_dma_create_cb_set_le
30 control_block->info |= finalextrainfo;
31 }
32
33 +static inline size_t bcm2835_dma_count_frames_for_sg(
34 + struct bcm2835_chan *c,
35 + struct scatterlist *sgl,
36 + unsigned int sg_len)
37 +{
38 + size_t frames = 0;
39 + struct scatterlist *sgent;
40 + unsigned int i;
41 + size_t plength = bcm2835_dma_max_frame_length(c);
42 +
43 + for_each_sg(sgl, sgent, sg_len, i)
44 + frames += bcm2835_dma_frames_for_length(
45 + sg_dma_len(sgent), plength);
46 +
47 + return frames;
48 +}
49 +
50 /**
51 * bcm2835_dma_create_cb_chain - create a control block and fills data in
52 *
53 @@ -361,6 +378,32 @@ error_cb:
54 return NULL;
55 }
56
57 +static void bcm2835_dma_fill_cb_chain_with_sg(
58 + struct dma_chan *chan,
59 + enum dma_transfer_direction direction,
60 + struct bcm2835_cb_entry *cb,
61 + struct scatterlist *sgl,
62 + unsigned int sg_len)
63 +{
64 + struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
65 + size_t max_len = bcm2835_dma_max_frame_length(c);
66 + unsigned int i, len;
67 + dma_addr_t addr;
68 + struct scatterlist *sgent;
69 +
70 + for_each_sg(sgl, sgent, sg_len, i) {
71 + for (addr = sg_dma_address(sgent), len = sg_dma_len(sgent);
72 + len > 0;
73 + addr += cb->cb->length, len -= cb->cb->length, cb++) {
74 + if (direction == DMA_DEV_TO_MEM)
75 + cb->cb->dst = addr;
76 + else
77 + cb->cb->src = addr;
78 + cb->cb->length = min(len, max_len);
79 + }
80 + }
81 +}
82 +
83 static int bcm2835_dma_abort(void __iomem *chan_base)
84 {
85 unsigned long cs;
86 @@ -428,13 +471,19 @@ static irqreturn_t bcm2835_dma_callback(
87 d = c->desc;
88
89 if (d) {
90 - /* TODO Only works for cyclic DMA */
91 - vchan_cyclic_callback(&d->vd);
92 + if (d->cyclic) {
93 + /* call the cyclic callback */
94 + vchan_cyclic_callback(&d->vd);
95 +
96 + /* Keep the DMA engine running */
97 + writel(BCM2835_DMA_ACTIVE,
98 + c->chan_base + BCM2835_DMA_CS);
99 + } else {
100 + vchan_cookie_complete(&c->desc->vd);
101 + bcm2835_dma_start_desc(c);
102 + }
103 }
104
105 - /* Keep the DMA engine running */
106 - writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS);
107 -
108 spin_unlock_irqrestore(&c->vc.lock, flags);
109
110 return IRQ_HANDLED;
111 @@ -548,6 +597,58 @@ static void bcm2835_dma_issue_pending(st
112 spin_unlock_irqrestore(&c->vc.lock, flags);
113 }
114
115 +static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
116 + struct dma_chan *chan,
117 + struct scatterlist *sgl, unsigned int sg_len,
118 + enum dma_transfer_direction direction,
119 + unsigned long flags, void *context)
120 +{
121 + struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
122 + struct bcm2835_desc *d;
123 + dma_addr_t src = 0, dst = 0;
124 + u32 info = BCM2835_DMA_WAIT_RESP;
125 + u32 extra = BCM2835_DMA_INT_EN;
126 + size_t frames;
127 +
128 + if (!is_slave_direction(direction)) {
129 + dev_err(chan->device->dev,
130 + "%s: bad direction?\n", __func__);
131 + return NULL;
132 + }
133 +
134 + if (c->dreq != 0)
135 + info |= BCM2835_DMA_PER_MAP(c->dreq);
136 +
137 + if (direction == DMA_DEV_TO_MEM) {
138 + if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
139 + return NULL;
140 + src = c->cfg.src_addr;
141 + info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC;
142 + } else {
143 + if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
144 + return NULL;
145 + dst = c->cfg.dst_addr;
146 + info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC;
147 + }
148 +
149 + /* count frames in sg list */
150 + frames = bcm2835_dma_count_frames_for_sg(c, sgl, sg_len);
151 +
152 + /* allocate the CB chain */
153 + d = bcm2835_dma_create_cb_chain(chan, direction, false,
154 + info, extra,
155 + frames, src, dst, 0, 0,
156 + GFP_KERNEL);
157 + if (!d)
158 + return NULL;
159 +
160 + /* fill in frames with scatterlist pointers */
161 + bcm2835_dma_fill_cb_chain_with_sg(chan, direction, d->cb_list,
162 + sgl, sg_len);
163 +
164 + return vchan_tx_prep(&c->vc, &d->vd, flags);
165 +}
166 +
167 static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
168 struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
169 size_t period_len, enum dma_transfer_direction direction,
170 @@ -778,11 +879,13 @@ static int bcm2835_dma_probe(struct plat
171 dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
172 dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask);
173 dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask);
174 + dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
175 od->ddev.device_alloc_chan_resources = bcm2835_dma_alloc_chan_resources;
176 od->ddev.device_free_chan_resources = bcm2835_dma_free_chan_resources;
177 od->ddev.device_tx_status = bcm2835_dma_tx_status;
178 od->ddev.device_issue_pending = bcm2835_dma_issue_pending;
179 od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic;
180 + od->ddev.device_prep_slave_sg = bcm2835_dma_prep_slave_sg;
181 od->ddev.device_config = bcm2835_dma_slave_config;
182 od->ddev.device_terminate_all = bcm2835_dma_terminate_all;
183 od->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);