diff options
-rw-r--r-- | drivers/dma/edma.c | 77 |
1 files changed, 51 insertions, 26 deletions
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index f9075129d27c..4c1c258a5b54 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c | |||
@@ -56,6 +56,7 @@ struct edma_desc { | |||
56 | struct list_head node; | 56 | struct list_head node; |
57 | int absync; | 57 | int absync; |
58 | int pset_nr; | 58 | int pset_nr; |
59 | int processed; | ||
59 | struct edmacc_param pset[0]; | 60 | struct edmacc_param pset[0]; |
60 | }; | 61 | }; |
61 | 62 | ||
@@ -104,22 +105,34 @@ static void edma_desc_free(struct virt_dma_desc *vdesc) | |||
104 | /* Dispatch a queued descriptor to the controller (caller holds lock) */ | 105 | /* Dispatch a queued descriptor to the controller (caller holds lock) */ |
105 | static void edma_execute(struct edma_chan *echan) | 106 | static void edma_execute(struct edma_chan *echan) |
106 | { | 107 | { |
107 | struct virt_dma_desc *vdesc = vchan_next_desc(&echan->vchan); | 108 | struct virt_dma_desc *vdesc; |
108 | struct edma_desc *edesc; | 109 | struct edma_desc *edesc; |
109 | int i; | 110 | struct device *dev = echan->vchan.chan.device->dev; |
110 | 111 | int i, j, left, nslots; | |
111 | if (!vdesc) { | 112 | |
112 | echan->edesc = NULL; | 113 | /* If either we processed all psets or we're still not started */ |
113 | return; | 114 | if (!echan->edesc || |
115 | echan->edesc->pset_nr == echan->edesc->processed) { | ||
116 | /* Get next vdesc */ | ||
117 | vdesc = vchan_next_desc(&echan->vchan); | ||
118 | if (!vdesc) { | ||
119 | echan->edesc = NULL; | ||
120 | return; | ||
121 | } | ||
122 | list_del(&vdesc->node); | ||
123 | echan->edesc = to_edma_desc(&vdesc->tx); | ||
114 | } | 124 | } |
115 | 125 | ||
116 | list_del(&vdesc->node); | 126 | edesc = echan->edesc; |
117 | 127 | ||
118 | echan->edesc = edesc = to_edma_desc(&vdesc->tx); | 128 | /* Find out how many left */ |
129 | left = edesc->pset_nr - edesc->processed; | ||
130 | nslots = min(MAX_NR_SG, left); | ||
119 | 131 | ||
120 | /* Write descriptor PaRAM set(s) */ | 132 | /* Write descriptor PaRAM set(s) */ |
121 | for (i = 0; i < edesc->pset_nr; i++) { | 133 | for (i = 0; i < nslots; i++) { |
122 | edma_write_slot(echan->slot[i], &edesc->pset[i]); | 134 | j = i + edesc->processed; |
135 | edma_write_slot(echan->slot[i], &edesc->pset[j]); | ||
123 | dev_dbg(echan->vchan.chan.device->dev, | 136 | dev_dbg(echan->vchan.chan.device->dev, |
124 | "\n pset[%d]:\n" | 137 | "\n pset[%d]:\n" |
125 | " chnum\t%d\n" | 138 | " chnum\t%d\n" |
@@ -132,24 +145,31 @@ static void edma_execute(struct edma_chan *echan) | |||
132 | " bidx\t%08x\n" | 145 | " bidx\t%08x\n" |
133 | " cidx\t%08x\n" | 146 | " cidx\t%08x\n" |
134 | " lkrld\t%08x\n", | 147 | " lkrld\t%08x\n", |
135 | i, echan->ch_num, echan->slot[i], | 148 | j, echan->ch_num, echan->slot[i], |
136 | edesc->pset[i].opt, | 149 | edesc->pset[j].opt, |
137 | edesc->pset[i].src, | 150 | edesc->pset[j].src, |
138 | edesc->pset[i].dst, | 151 | edesc->pset[j].dst, |
139 | edesc->pset[i].a_b_cnt, | 152 | edesc->pset[j].a_b_cnt, |
140 | edesc->pset[i].ccnt, | 153 | edesc->pset[j].ccnt, |
141 | edesc->pset[i].src_dst_bidx, | 154 | edesc->pset[j].src_dst_bidx, |
142 | edesc->pset[i].src_dst_cidx, | 155 | edesc->pset[j].src_dst_cidx, |
143 | edesc->pset[i].link_bcntrld); | 156 | edesc->pset[j].link_bcntrld); |
144 | /* Link to the previous slot if not the last set */ | 157 | /* Link to the previous slot if not the last set */ |
145 | if (i != (edesc->pset_nr - 1)) | 158 | if (i != (nslots - 1)) |
146 | edma_link(echan->slot[i], echan->slot[i+1]); | 159 | edma_link(echan->slot[i], echan->slot[i+1]); |
147 | /* Final pset links to the dummy pset */ | 160 | /* Final pset links to the dummy pset */ |
148 | else | 161 | else |
149 | edma_link(echan->slot[i], echan->ecc->dummy_slot); | 162 | edma_link(echan->slot[i], echan->ecc->dummy_slot); |
150 | } | 163 | } |
151 | 164 | ||
152 | edma_start(echan->ch_num); | 165 | edesc->processed += nslots; |
166 | |||
167 | edma_resume(echan->ch_num); | ||
168 | |||
169 | if (edesc->processed <= MAX_NR_SG) { | ||
170 | dev_dbg(dev, "first transfer starting %d\n", echan->ch_num); | ||
171 | edma_start(echan->ch_num); | ||
172 | } | ||
153 | } | 173 | } |
154 | 174 | ||
155 | static int edma_terminate_all(struct edma_chan *echan) | 175 | static int edma_terminate_all(struct edma_chan *echan) |
@@ -368,19 +388,24 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data) | |||
368 | struct edma_desc *edesc; | 388 | struct edma_desc *edesc; |
369 | unsigned long flags; | 389 | unsigned long flags; |
370 | 390 | ||
371 | /* Stop the channel */ | 391 | /* Pause the channel */ |
372 | edma_stop(echan->ch_num); | 392 | edma_pause(echan->ch_num); |
373 | 393 | ||
374 | switch (ch_status) { | 394 | switch (ch_status) { |
375 | case DMA_COMPLETE: | 395 | case DMA_COMPLETE: |
376 | dev_dbg(dev, "transfer complete on channel %d\n", ch_num); | ||
377 | |||
378 | spin_lock_irqsave(&echan->vchan.lock, flags); | 396 | spin_lock_irqsave(&echan->vchan.lock, flags); |
379 | 397 | ||
380 | edesc = echan->edesc; | 398 | edesc = echan->edesc; |
381 | if (edesc) { | 399 | if (edesc) { |
400 | if (edesc->processed == edesc->pset_nr) { | ||
401 | dev_dbg(dev, "Transfer complete, stopping channel %d\n", ch_num); | ||
402 | edma_stop(echan->ch_num); | ||
403 | vchan_cookie_complete(&edesc->vdesc); | ||
404 | } else { | ||
405 | dev_dbg(dev, "Intermediate transfer complete on channel %d\n", ch_num); | ||
406 | } | ||
407 | |||
382 | edma_execute(echan); | 408 | edma_execute(echan); |
383 | vchan_cookie_complete(&edesc->vdesc); | ||
384 | } | 409 | } |
385 | 410 | ||
386 | spin_unlock_irqrestore(&echan->vchan.lock, flags); | 411 | spin_unlock_irqrestore(&echan->vchan.lock, flags); |