1
From 56674d9155f939b950f5f7813cc3ede205560aad Mon Sep 17 00:00:00 2001
2
From: =?UTF-8?q?Juha=20Riihim=C3=A4ki?= <juha.riihimaki@nokia.com>
3
Date: Mon, 18 Feb 2013 16:58:24 +0000
4
Subject: [PATCH 08/70] omap_dma: add scatter gather list support
6
Content-Type: text/plain; charset=UTF-8
7
Content-Transfer-Encoding: 8bit
9
Signed-off-by: Juha Riihimäki <juha.riihimaki@nokia.com>
11
hw/dma/omap_dma.c | 411 +++++++++++++++++++++++++++++++++++-------------------
12
1 file changed, 268 insertions(+), 143 deletions(-)
14
diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c
15
index 7d365bb..c3971a7 100644
16
--- a/hw/dma/omap_dma.c
17
+++ b/hw/dma/omap_dma.c
18
@@ -98,6 +98,12 @@ struct omap_dma_channel_s {
23
+ struct omap_dma_desc_linked_list_s {
31
@@ -132,7 +138,10 @@ struct omap_dma_s {
33
#define END_PKT_INTR (1 << 7)
34
#define TRANS_ERR_INTR (1 << 8)
35
+#define SV_ERR_INTR (1 << 10)
36
#define MISALIGN_INTR (1 << 11)
37
+#define END_DRAIN_INTR (1 << 12)
38
+#define END_SBLOCK_INTR (1 << 14)
40
static inline void omap_dma_interrupts_update(struct omap_dma_s *s)
42
@@ -259,6 +268,100 @@ static void omap_dma_deactivate_channel(struct omap_dma_s *s,
46
+static void omap_dma4_write_ch(struct omap_dma_s *s,
47
+ struct omap_dma_channel_s *ch,
48
+ uint32_t offset, uint32_t value);
50
+static int omap_dma_sgl_next(struct omap_dma_s *s,
51
+ struct omap_dma_channel_s *ch)
53
+ if ((ch->sgl.next >> 2) == 0x3fffffff) {
54
+ ch->status |= END_SBLOCK_INTR;
57
+ int type = (ch->sgl.ptr >> 4) & 7; /* NEXT_DESCRIPTOR_TYPE */
58
+ int src_valid = (ch->sgl.ptr >> 2) & 3; /* SRC_VALID */
59
+ int dst_valid = ch->sgl.ptr & 3; /* DEST_VALID */
60
+ if (type < 1 || type > 3) {
61
+ hw_error("%s: unknown descriptor type %d\n", __func__, type);
63
+ hwaddr addr = (hwaddr)ch->sgl.next;
64
+ ch->sgl.ptr &= ~0xff;
66
+ cpu_physical_memory_read(addr, data, 4);
67
+ uint32_t word = ldl_p(data);
68
+ ch->sgl.next = word & ~3;
69
+ ch->sgl.ptr |= (word & 1) << 7; /* PAUSE_LINK_LIST */
70
+ cpu_physical_memory_read(addr + 4, data, 4);
72
+ ch->sgl.ptr |= (word >> 25) & 0x70; /* NEXT_DESCRIPTOR_TYPE */
73
+ ch->sgl.ptr |= (word >> 26) & 3; /* DEST_VALID */
74
+ ch->sgl.ptr |= (word >> 22) & 0x0c; /* SRC_VALID */
75
+ ch->sgl.number = word & 0xffffff;
77
+ ch->interrupts &= ~0x20; /* BLOCK_IE */
78
+ ch->interrupts |= (word >> 23) & 0x20; /* BLOCK_IE */
81
+ switch (src_valid) {
83
+ ch->addr[0] = ch->active_set.src;
86
+ cpu_physical_memory_read(addr, data, 4);
87
+ ch->addr[0] = ldl_p(data);
93
+ hw_error("%s: unknown src addressing mode %d\n",
94
+ __func__, src_valid);
97
+ switch (dst_valid) {
99
+ ch->addr[1] = ch->active_set.dest;
102
+ cpu_physical_memory_read(addr, data, 4);
103
+ ch->addr[1] = ldl_p(data);
109
+ hw_error("%s: unknown dest addressing mode %d\n",
110
+ __func__, dst_valid);
113
+ if (type == 1 || type == 2) {
114
+ cpu_physical_memory_read(addr, data, 4);
115
+ word = ldl_p(data);
116
+ ch->interrupts = word >> 16;
117
+ ch->frames = word & 0xffff;
118
+ cpu_physical_memory_read(addr + 4, data, 4);
119
+ word = ldl_p(data);
120
+ ch->element_index[0] = (int16_t)(word >> 16);
121
+ ch->element_index[1] = (int16_t)(word & 0xffff);
122
+ cpu_physical_memory_read(addr + 8, data, 4);
123
+ ch->frame_index[1] = ldl_p(data);
124
+ cpu_physical_memory_read(addr + 12, data, 4);
125
+ ch->frame_index[0] = ldl_p(data);
127
+ cpu_physical_memory_read(addr + 16, data, 4);
128
+ ch->color = ldl_p(data);
129
+ cpu_physical_memory_read(addr + 20, data, 4);
130
+ omap_dma4_write_ch(s, ch, 0x10, ldl_p(data)); /* CSDP */
131
+ cpu_physical_memory_read(addr + 24, data, 4);
132
+ omap_dma4_write_ch(s, ch, 0x04, ldl_p(data)); /* CLNK_CTRL */
133
+ cpu_physical_memory_read(addr + 28, data, 4);
134
+ omap_dma4_write_ch(s, ch, 0x00, ldl_p(data)); /* CCR */
140
static void omap_dma_enable_channel(struct omap_dma_s *s,
141
struct omap_dma_channel_s *ch)
143
@@ -366,6 +469,34 @@ static void omap_dma_process_request(struct omap_dma_s *s, int request)
144
omap_dma_interrupts_update(s);
147
+static void omap_dma_end_of_block(struct omap_dma_s *s,
148
+ struct omap_dma_channel_s *ch)
150
+ if (ch->omap_3_1_compatible_disable) {
151
+ omap_dma_disable_channel(s, ch);
152
+ if (ch->link_enabled) {
153
+ omap_dma_enable_channel(s, &s->ch[ch->link_next_ch]);
156
+ int sgl_pause = (ch->sgl.ptr >> 7) & 1; /* PAUSE_LINK_LIST */
157
+ if (((ch->sgl.ptr >> 8) & 3) != 1 || /* TRANSFER_MODE != linked list */
158
+ !omap_dma_sgl_next(s, ch) || /* end of list */
160
+ if (!ch->auto_init) {
161
+ omap_dma_disable_channel(s, ch);
162
+ } else if (ch->repeat || ch->end_prog) {
163
+ omap_dma_channel_load(ch);
165
+ ch->waiting_end_prog = 1;
166
+ omap_dma_deactivate_channel(s, ch);
171
+ if (ch->interrupts & END_BLOCK_INTR)
172
+ ch->status |= END_BLOCK_INTR;
175
static void omap_dma_transfer_generic(struct soc_dma_ch_s *dma)
178
@@ -458,25 +589,7 @@ static void omap_dma_transfer_generic(struct soc_dma_ch_s *dma)
179
if (a->frame == a->frames) {
181
/* Disable the channel */
183
- if (ch->omap_3_1_compatible_disable) {
184
- omap_dma_disable_channel(s, ch);
185
- if (ch->link_enabled)
186
- omap_dma_enable_channel(s,
187
- &s->ch[ch->link_next_ch]);
189
- if (!ch->auto_init)
190
- omap_dma_disable_channel(s, ch);
191
- else if (ch->repeat || ch->end_prog)
192
- omap_dma_channel_load(ch);
194
- ch->waiting_end_prog = 1;
195
- omap_dma_deactivate_channel(s, ch);
199
- if (ch->interrupts & END_BLOCK_INTR)
200
- ch->status |= END_BLOCK_INTR;
201
+ omap_dma_end_of_block(s, ch);
204
} while (status == ch->status && ch->active);
205
@@ -507,6 +620,11 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma)
209
+ if (((ch->sgl.ptr >> 8) & 3) == 1 && /* TRANSFER_MODE == linked list */
210
+ a->frame >= a->frames) {
211
+ omap_dma_channel_load(ch);
214
src_p = &s->mpu->port[ch->port[0]];
215
dest_p = &s->mpu->port[ch->port[1]];
216
if ((!ch->constant_fill && !src_p->addr_valid(s->mpu, a->src)) ||
217
@@ -626,24 +744,7 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma)
218
if (min_elems == elements[omap_dma_intr_block]) {
220
/* Disable the channel */
222
- if (ch->omap_3_1_compatible_disable) {
223
- omap_dma_disable_channel(s, ch);
224
- if (ch->link_enabled)
225
- omap_dma_enable_channel(s, &s->ch[ch->link_next_ch]);
227
- if (!ch->auto_init)
228
- omap_dma_disable_channel(s, ch);
229
- else if (ch->repeat || ch->end_prog)
230
- omap_dma_channel_load(ch);
232
- ch->waiting_end_prog = 1;
233
- omap_dma_deactivate_channel(s, ch);
237
- if (ch->interrupts & END_BLOCK_INTR)
238
- ch->status |= END_BLOCK_INTR;
239
+ omap_dma_end_of_block(s, ch);
242
/* Update packet number */
243
@@ -747,6 +848,9 @@ void omap_dma_reset(struct soc_dma_s *dma)
244
s->ch[i].priority = 0;
245
s->ch[i].interleave_disabled = 0;
247
+ s->ch[i].sgl.ptr = 0;
248
+ s->ch[i].sgl.next = 0xfffffffc;
249
+ s->ch[i].sgl.number = 0;
253
@@ -1531,6 +1635,7 @@ static void omap_dma_write(void *opaque, hwaddr addr,
254
case 0x404 ... 0x4fe:
255
if (s->model <= omap_dma_3_1)
260
if (omap_dma_sys_write(s, addr, value))
261
@@ -1699,9 +1804,9 @@ static void omap_dma_interrupts_4_update(struct omap_dma_s *s)
264
for (bmp = 0, bit = 1; bit; ch ++, bit <<= 1)
265
- if ((ch->status &= ch->interrupts)) {
266
+ if ((ch->status & ch->interrupts)) {
268
- ch->cstatus |= ch->status;
269
+ ch->cstatus |= ch->status & ch->interrupts;
272
if ((s->irqstat[0] |= s->irqen[0] & bmp))
273
@@ -1868,96 +1973,36 @@ static uint64_t omap_dma4_read(void *opaque, hwaddr addr,
276
case 0x50: /* DMA4_CDP */
277
- case 0x54: /* DMA4_CNDP */
278
- case 0x58: /* DMA4_CCDN */
282
- OMAP_BAD_REG(0x80 + chnum * 0x60 + addr);
287
-static void omap_dma4_write(void *opaque, hwaddr addr,
288
- uint64_t value, unsigned size)
290
- struct omap_dma_s *s = (struct omap_dma_s *) opaque;
291
- int chnum, irqn = 0;
292
- struct omap_dma_channel_s *ch;
295
- return omap_badwidth_write16(opaque, addr, value);
299
- case 0x14: /* DMA4_IRQSTATUS_L3 */
302
- case 0x10: /* DMA4_IRQSTATUS_L2 */
305
- case 0x0c: /* DMA4_IRQSTATUS_L1 */
308
- case 0x08: /* DMA4_IRQSTATUS_L0 */
309
- s->irqstat[irqn] &= ~value;
310
- if (!s->irqstat[irqn])
311
- qemu_irq_lower(s->irq[irqn]);
314
- case 0x24: /* DMA4_IRQENABLE_L3 */
317
- case 0x20: /* DMA4_IRQENABLE_L2 */
320
- case 0x1c: /* DMA4_IRQENABLE_L1 */
323
- case 0x18: /* DMA4_IRQENABLE_L0 */
324
- s->irqen[irqn] = value;
327
- case 0x2c: /* DMA4_OCP_SYSCONFIG */
328
- if (value & 2) { /* SOFTRESET */
329
- if (!cpu_is_omap3630(s->mpu)) { /* N/A on 3630GP */
330
- omap_dma_reset(s->dma);
332
+ if (cpu_is_omap3630(s->mpu)) {
333
+ return ch->sgl.ptr;
335
- s->ocp = value & 0x3321;
336
- if (((s->ocp >> 12) & 3) == 3) /* MIDLEMODE */
337
- fprintf(stderr, "%s: invalid DMA power mode\n", __FUNCTION__);
340
- case 0x78: /* DMA4_GCR */
341
- s->gcr = value & 0x00ff00ff;
342
- if ((value & 0xff) == 0x00) /* MAX_CHANNEL_FIFO_DEPTH */
343
- fprintf(stderr, "%s: wrong FIFO depth in GCR\n", __FUNCTION__);
347
- case 0x80 ... 0xfff:
349
- chnum = addr / 0x60;
350
- ch = s->ch + chnum;
351
- addr -= chnum * 0x60;
352
+ case 0x54: /* DMA4_CNDP */
353
+ if (cpu_is_omap3630(s->mpu)) {
354
+ return ch->sgl.next;
358
- case 0x00: /* DMA4_REVISION */
359
- case 0x28: /* DMA4_SYSSTATUS */
360
- case 0x64: /* DMA4_CAPS_0 */
361
- case 0x6c: /* DMA4_CAPS_2 */
362
- case 0x70: /* DMA4_CAPS_3 */
363
- case 0x74: /* DMA4_CAPS_4 */
366
+ case 0x58: /* DMA4_CCDN */
367
+ if (cpu_is_omap3630(s->mpu)) {
368
+ return ch->sgl.number;
373
- OMAP_BAD_REG(addr);
378
- /* Per-channel registers */
380
+ OMAP_BAD_REG(0x80 + chnum * 0x60 + addr);
384
+static void omap_dma4_write_ch(struct omap_dma_s *s,
385
+ struct omap_dma_channel_s *ch,
386
+ uint32_t offset, uint32_t value)
389
case 0x00: /* DMA4_CCR */
390
ch->buf_disable = (value >> 25) & 1;
391
ch->src_sync = (value >> 24) & 1; /* XXX For CamDMA must be 1 */
392
@@ -1979,11 +2024,15 @@ static void omap_dma4_write(void *opaque, hwaddr addr,
393
ch->sync = (value & 0x001f) | ((value >> 14) & 0x0060);
394
/* XXX must be 0x01 for CamDMA */
396
- if (value & 0x0080)
397
- omap_dma_enable_channel(s, ch);
399
+ if (value & 0x0080) {
400
+ if (((ch->sgl.ptr >> 8) & 3) != 1 || /* TRANSFER_MODE */
401
+ !(ch->sgl.ptr & (1 << 10)) || /* FAST */
402
+ omap_dma_sgl_next(s, ch)) {
403
+ omap_dma_enable_channel(s, ch);
406
omap_dma_disable_channel(s, ch);
411
case 0x04: /* DMA4_CLNK_CTRL */
412
@@ -2068,41 +2117,117 @@ static void omap_dma4_write(void *opaque, hwaddr addr,
416
- case 0x44: /* DMA4_COLOR */
417
- /* XXX only in sDMA */
421
case 0x34: /* DMA4_CSAC */
422
case 0x38: /* DMA4_CDAC */
423
case 0x3c: /* DMA4_CCEN */
424
case 0x40: /* DMA4_CCFN */
425
- /* f.ex. linux kernel writes zeroes to these registers as well
426
- when performing a DMA channel reset. let's just ignore the
427
- writes instead of reporting "dummy" errors */
428
- /*OMAP_RO_REG(0x80 + chnum * 0x60 + addr);*/
432
+ case 0x44: /* DMA4_COLOR */
433
+ /* XXX only in sDMA */
437
case 0x50: /* DMA4_CDP */
438
if (cpu_is_omap3630(s->mpu)) {
439
- if ((value >> 8) & 3) { /* TRANSFER_MODE */
440
- hw_error("%s: linked list transfer mode not supported",
444
- OMAP_BAD_REG(0x80 + chnum * 0x60 + addr);
445
+ ch->sgl.ptr = value & 0x7ff;
449
case 0x54: /* DMA4_CNDP */
450
+ if (cpu_is_omap3630(s->mpu)) {
451
+ ch->sgl.next = value & ~3;
455
case 0x58: /* DMA4_CCDN */
456
- if (!cpu_is_omap3630(s->mpu)) {
457
- OMAP_BAD_REG(0x80 + chnum * 0x60 + addr);
458
+ if (cpu_is_omap3630(s->mpu)) {
459
+ ch->sgl.number = value & 0xffff;
464
- OMAP_BAD_REG(0x80 + chnum * 0x60 + addr);
465
+ fprintf(stderr, "%s: unknown register 0x%x\n",
466
+ __func__, offset + 0x80);
471
+static void omap_dma4_write(void *opaque, hwaddr addr,
472
+ uint64_t value, unsigned size)
474
+ struct omap_dma_s *s = (struct omap_dma_s *) opaque;
475
+ int chnum, irqn = 0;
478
+ return omap_badwidth_write16(opaque, addr, value);
482
+ case 0x14: /* DMA4_IRQSTATUS_L3 */
485
+ case 0x10: /* DMA4_IRQSTATUS_L2 */
488
+ case 0x0c: /* DMA4_IRQSTATUS_L1 */
491
+ case 0x08: /* DMA4_IRQSTATUS_L0 */
492
+ s->irqstat[irqn] &= ~value;
493
+ if (!s->irqstat[irqn]) {
494
+ qemu_irq_lower(s->irq[irqn]);
498
+ case 0x24: /* DMA4_IRQENABLE_L3 */
500
+ case 0x20: /* DMA4_IRQENABLE_L2 */
502
+ case 0x1c: /* DMA4_IRQENABLE_L1 */
504
+ case 0x18: /* DMA4_IRQENABLE_L0 */
505
+ s->irqen[irqn] = value;
508
+ case 0x2c: /* DMA4_OCP_SYSCONFIG */
509
+ if (value & 2) { /* SOFTRESET */
510
+ /* N/A on 3630GP?? */
511
+ omap_dma_reset(s->dma);
513
+ s->ocp = value & 0x3321;
514
+ if (((s->ocp >> 12) & 3) == 3) { /* MIDLEMODE */
515
+ fprintf(stderr, "%s: invalid DMA power mode\n", __func__);
519
+ case 0x78: /* DMA4_GCR */
520
+ s->gcr = value & 0x00ff00ff;
521
+ if ((value & 0xff) == 0x00) { /* MAX_CHANNEL_FIFO_DEPTH */
522
+ fprintf(stderr, "%s: wrong FIFO depth in GCR\n", __func__);
526
+ case 0x80 ... 0xfff:
528
+ chnum = addr / 0x60;
529
+ addr -= chnum * 0x60;
530
+ omap_dma4_write_ch(s, s->ch + chnum, (uint32_t)addr, value);
533
+ case 0x00: /* DMA4_REVISION */
534
+ case 0x28: /* DMA4_SYSSTATUS */
535
+ case 0x64: /* DMA4_CAPS_0 */
536
+ case 0x6c: /* DMA4_CAPS_2 */
537
+ case 0x70: /* DMA4_CAPS_3 */
538
+ case 0x74: /* DMA4_CAPS_4 */
543
+ OMAP_BAD_REG(addr);