Initial hacky relocation stuff.
[nouveau] / src / nouveau_dma.c
1 #include <stdint.h>
2 #include <assert.h>
3 #include <errno.h>
4
5 #include "nouveau_drmif.h"
6 #include "nouveau_dma.h"
7 #include "nouveau_local.h"
8
9 #define READ_GET(ch) ((*(ch)->get - (ch)->dma.base) >> 2)
10 #define WRITE_PUT(ch, val) do {                       \
11         *(ch)->put = (((val) << 2) + (ch)->dma.base); \
12 } while(0)
13
14 void
15 nouveau_dma_channel_init(struct nouveau_channel *userchan)
16 {
17         struct nouveau_channel_priv *chan = nouveau_channel(userchan);
18         int i;
19
20         chan->dma.base = chan->drm.put_base;
21         chan->dma.cur  = chan->dma.put = RING_SKIPS;
22         chan->dma.max  = (chan->drm.cmdbuf_size >> 2) - 2;
23         chan->dma.free = chan->dma.max - chan->dma.cur;
24
25         for (i = 0; i < RING_SKIPS; i++)
26                 chan->pushbuf[i] = 0x00000000;
27 }
28
29 #define CHECK_TIMEOUT() do {                                                   \
30         if ((NOUVEAU_TIME_MSEC() - t_start) > NOUVEAU_DMA_TIMEOUT)             \
31                 return - EBUSY;                                                \
32 } while(0)
33
34 int
35 nouveau_dma_wait(struct nouveau_channel *userchan, int size)
36 {
37         struct nouveau_channel_priv *chan = nouveau_channel(userchan);
38         uint32_t get, t_start;
39
40         FIRE_RING_CH(userchan);
41
42         t_start = NOUVEAU_TIME_MSEC();
43         while (chan->dma.free < size) {
44                 get = READ_GET(chan);
45
46                 if (chan->dma.put >= get) {
47                         chan->dma.free = chan->dma.max - chan->dma.cur;
48
49                         if (chan->dma.free < size) {
50 #ifdef NOUVEAU_DMA_DEBUG
51                                 chan->dma.push_free = 1;
52 #endif
53                                 OUT_RING_CH(userchan,
54                                             0x20000000 | chan->dma.base);
55                                 if (get <= RING_SKIPS) {
56                                         /*corner case - will be idle*/
57                                         if (chan->dma.put <= RING_SKIPS)
58                                                 WRITE_PUT(chan, RING_SKIPS + 1);
59
60                                         do {
61                                                 CHECK_TIMEOUT();
62                                                 get = READ_GET(chan);
63                                         } while (get <= RING_SKIPS);
64                                 }
65
66                                 WRITE_PUT(chan, RING_SKIPS);
67                                 chan->dma.cur  = chan->dma.put = RING_SKIPS;
68                                 chan->dma.free = get - (RING_SKIPS + 1);
69                         }
70                 } else {
71                         chan->dma.free = get - chan->dma.cur - 1;
72                 }
73
74                 CHECK_TIMEOUT();
75         }
76
77         return 0;
78 }
79
80 #ifdef NOUVEAU_DMA_SUBCHAN_LRU
81 void
82 nouveau_dma_subc_bind(struct nouveau_grobj *grobj)
83 {
84         struct nouveau_channel_priv *chan = nouveau_channel(grobj->channel);
85         int subc = -1, i;
86         
87         for (i = 0; i < 8; i++) {
88                 if (chan->subchannel[i].grobj &&
89                     chan->subchannel[i].grobj->bound == 
90                     NOUVEAU_GROBJ_EXPLICIT_BIND)
91                         continue;
92                 if (chan->subchannel[i].seq < chan->subchannel[subc].seq)
93                         subc = i;
94         }
95         assert(subc >= 0);
96
97         if (chan->subchannel[subc].grobj)
98                 chan->subchannel[subc].grobj->bound = 0;
99         chan->subchannel[subc].grobj = grobj;
100         grobj->subc  = subc;
101         grobj->bound = NOUVEAU_GROBJ_BOUND;
102
103         nouveau_dma_begin(grobj->channel, grobj, 0, 1);
104         nouveau_dma_out  (grobj->channel, grobj->handle);
105 }
106 #endif
107
108 void
109 nouveau_dma_kickoff(struct nouveau_channel *userchan)
110 {
111         struct nouveau_channel_priv *chan = nouveau_channel(userchan);
112         uint32_t put_offset;
113         int i;
114
115         if (chan->dma.cur == chan->dma.put)
116                 return;
117
118         if (chan->num_relocs) {
119                 nouveau_bo_validate(userchan);
120                 
121                 for (i = 0; i < chan->num_relocs; i++) {
122                         struct nouveau_bo_reloc *r = &chan->relocs[i];
123                         uint32_t push;
124
125                         if (r->flags & NOUVEAU_BO_LOW) {
126                                 push = r->bo->base.offset + r->data;
127                         } else
128                         if (r->flags & NOUVEAU_BO_HIGH) {
129                                 push = (r->bo->base.offset + r->data) >> 32;
130                         } else {
131                                 push = r->data;
132                         }
133
134                         if (r->flags & NOUVEAU_BO_OR) {
135                                 if (r->flags & NOUVEAU_BO_VRAM)
136                                         push |= r->vor;
137                                 else
138                                         push |= r->tor;
139                         }
140
141                         *r->ptr = push;
142                 }
143
144                 chan->num_relocs = 0;
145         }
146
147 #ifdef NOUVEAU_DMA_DEBUG
148         if (chan->dma.push_free) {
149                 NOUVEAU_ERR("Packet incomplete: %d left\n", chan->dma.push_free);
150                 return;
151         }
152 #endif
153
154         put_offset = (chan->dma.cur << 2) + chan->dma.base;
155 #ifdef NOUVEAU_DMA_TRACE
156         NOUVEAU_MSG("FIRE_RING %d/0x%08x\n", chan->drm.channel, put_offset);
157 #endif
158         chan->dma.put  = chan->dma.cur;
159         NOUVEAU_DMA_BARRIER;
160         *chan->put     = put_offset;
161         NOUVEAU_DMA_BARRIER;
162 }