Merge branch 'mesa_7_7_branch'
[mesa.git] / src / gallium / drivers / nouveau / nouveau_stateobj.h
1 #ifndef __NOUVEAU_STATEOBJ_H__
2 #define __NOUVEAU_STATEOBJ_H__
3
4 #include "util/u_debug.h"
5
6 #ifdef DEBUG
7 #define DEBUG_NOUVEAU_STATEOBJ
8 #endif /* DEBUG */
9
10 struct nouveau_stateobj_reloc {
11 struct nouveau_bo *bo;
12
13 struct nouveau_grobj *gr;
14 uint32_t push_offset;
15 uint32_t mthd;
16
17 uint32_t data;
18 unsigned flags;
19 unsigned vor;
20 unsigned tor;
21 };
22
23 struct nouveau_stateobj_start {
24 struct nouveau_grobj *gr;
25 uint32_t mthd;
26 uint32_t size;
27 unsigned offset;
28 };
29
30 struct nouveau_stateobj {
31 struct pipe_reference reference;
32
33 struct nouveau_stateobj_start *start;
34 struct nouveau_stateobj_reloc *reloc;
35
36 /* Common memory pool for data. */
37 uint32_t *pool;
38 unsigned pool_cur;
39
40 #ifdef DEBUG_NOUVEAU_STATEOBJ
41 unsigned start_alloc;
42 unsigned reloc_alloc;
43 unsigned pool_alloc;
44 #endif /* DEBUG_NOUVEAU_STATEOBJ */
45
46 unsigned total; /* includes begin_ring */
47 unsigned cur; /* excludes begin_ring, offset from "cur_start" */
48 unsigned cur_start;
49 unsigned cur_reloc;
50 };
51
52 static INLINE void
53 so_dump(struct nouveau_stateobj *so)
54 {
55 unsigned i, nr, total = 0;
56
57 for (i = 0; i < so->cur_start; i++) {
58 if (so->start[i].gr->subc > -1)
59 debug_printf("+0x%04x: 0x%08x\n", total++,
60 (so->start[i].size << 18) | (so->start[i].gr->subc << 13)
61 | so->start[i].mthd);
62 else
63 debug_printf("+0x%04x: 0x%08x\n", total++,
64 (so->start[i].size << 18) | so->start[i].mthd);
65 for (nr = 0; nr < so->start[i].size; nr++, total++)
66 debug_printf("+0x%04x: 0x%08x\n", total,
67 so->pool[so->start[i].offset + nr]);
68 }
69 }
70
71 static INLINE struct nouveau_stateobj *
72 so_new(unsigned start, unsigned push, unsigned reloc)
73 {
74 struct nouveau_stateobj *so;
75
76 so = MALLOC(sizeof(struct nouveau_stateobj));
77 pipe_reference_init(&so->reference, 1);
78 so->total = so->cur = so->cur_start = so->cur_reloc = 0;
79
80 #ifdef DEBUG_NOUVEAU_STATEOBJ
81 so->start_alloc = start;
82 so->reloc_alloc = reloc;
83 so->pool_alloc = push;
84 #endif /* DEBUG_NOUVEAU_STATEOBJ */
85
86 so->start = MALLOC(start * sizeof(struct nouveau_stateobj_start));
87 so->reloc = MALLOC(reloc * sizeof(struct nouveau_stateobj_reloc));
88 so->pool = MALLOC(push * sizeof(uint32_t));
89 so->pool_cur = 0;
90
91 if (!so->start || !so->reloc || !so->pool) {
92 debug_printf("malloc failed\n");
93 assert(0);
94 }
95
96 return so;
97 }
98
99 static INLINE void
100 so_ref(struct nouveau_stateobj *ref, struct nouveau_stateobj **pso)
101 {
102 struct nouveau_stateobj *so = *pso;
103 int i;
104
105 if (pipe_reference(&(*pso)->reference, &ref->reference)) {
106 FREE(so->start);
107 for (i = 0; i < so->cur_reloc; i++)
108 nouveau_bo_ref(NULL, &so->reloc[i].bo);
109 FREE(so->reloc);
110 FREE(so->pool);
111 FREE(so);
112 }
113 *pso = ref;
114 }
115
116 static INLINE void
117 so_data(struct nouveau_stateobj *so, uint32_t data)
118 {
119 #ifdef DEBUG_NOUVEAU_STATEOBJ
120 if (so->cur >= so->start[so->cur_start - 1].size) {
121 debug_printf("exceeding specified size\n");
122 assert(0);
123 }
124 #endif /* DEBUG_NOUVEAU_STATEOBJ */
125
126 so->pool[so->start[so->cur_start - 1].offset + so->cur++] = data;
127 }
128
129 static INLINE void
130 so_datap(struct nouveau_stateobj *so, uint32_t *data, unsigned size)
131 {
132 #ifdef DEBUG_NOUVEAU_STATEOBJ
133 if ((so->cur + size) > so->start[so->cur_start - 1].size) {
134 debug_printf("exceeding specified size\n");
135 assert(0);
136 }
137 #endif /* DEBUG_NOUVEAU_STATEOBJ */
138
139 while (size--)
140 so->pool[so->start[so->cur_start - 1].offset + so->cur++] =
141 *data++;
142 }
143
144 static INLINE void
145 so_method(struct nouveau_stateobj *so, struct nouveau_grobj *gr,
146 unsigned mthd, unsigned size)
147 {
148 struct nouveau_stateobj_start *start;
149
150 #ifdef DEBUG_NOUVEAU_STATEOBJ
151 if (so->start_alloc <= so->cur_start) {
152 debug_printf("exceeding num_start size\n");
153 assert(0);
154 } else
155 #endif /* DEBUG_NOUVEAU_STATEOBJ */
156 start = so->start;
157
158 #ifdef DEBUG_NOUVEAU_STATEOBJ
159 if (so->cur_start > 0 && start[so->cur_start - 1].size > so->cur) {
160 debug_printf("previous so_method was not filled\n");
161 assert(0);
162 }
163 #endif /* DEBUG_NOUVEAU_STATEOBJ */
164
165 so->start = start;
166 start[so->cur_start].gr = gr;
167 start[so->cur_start].mthd = mthd;
168 start[so->cur_start].size = size;
169
170 #ifdef DEBUG_NOUVEAU_STATEOBJ
171 if (so->pool_alloc < (size + so->pool_cur)) {
172 debug_printf("exceeding num_pool size\n");
173 assert(0);
174 }
175 #endif /* DEBUG_NOUVEAU_STATEOBJ */
176
177 start[so->cur_start].offset = so->pool_cur;
178 so->pool_cur += size;
179
180 so->cur_start++;
181 /* The 1 is for *this* begin_ring. */
182 so->total += so->cur + 1;
183 so->cur = 0;
184 }
185
186 static INLINE void
187 so_reloc(struct nouveau_stateobj *so, struct nouveau_bo *bo,
188 unsigned data, unsigned flags, unsigned vor, unsigned tor)
189 {
190 struct nouveau_stateobj_reloc *r;
191
192 #ifdef DEBUG_NOUVEAU_STATEOBJ
193 if (so->reloc_alloc <= so->cur_reloc) {
194 debug_printf("exceeding num_reloc size\n");
195 assert(0);
196 } else
197 #endif /* DEBUG_NOUVEAU_STATEOBJ */
198 r = so->reloc;
199
200 so->reloc = r;
201 r[so->cur_reloc].bo = NULL;
202 nouveau_bo_ref(bo, &(r[so->cur_reloc].bo));
203 r[so->cur_reloc].gr = so->start[so->cur_start-1].gr;
204 r[so->cur_reloc].push_offset = so->total + so->cur;
205 r[so->cur_reloc].data = data;
206 r[so->cur_reloc].flags = flags;
207 r[so->cur_reloc].mthd = so->start[so->cur_start-1].mthd +
208 (so->cur << 2);
209 r[so->cur_reloc].vor = vor;
210 r[so->cur_reloc].tor = tor;
211
212 so_data(so, data);
213 so->cur_reloc++;
214 }
215
216 /* Determine if this buffer object is referenced by this state object. */
217 static INLINE boolean
218 so_bo_is_reloc(struct nouveau_stateobj *so, struct nouveau_bo *bo)
219 {
220 int i;
221
222 for (i = 0; i < so->cur_reloc; i++)
223 if (so->reloc[i].bo == bo)
224 return true;
225
226 return false;
227 }
228
229 static INLINE void
230 so_emit(struct nouveau_channel *chan, struct nouveau_stateobj *so)
231 {
232 struct nouveau_pushbuf *pb = chan->pushbuf;
233 unsigned nr, i;
234 int ret = 0;
235
236 #ifdef DEBUG_NOUVEAU_STATEOBJ
237 if (so->start[so->cur_start - 1].size > so->cur) {
238 debug_printf("emit: previous so_method was not filled\n");
239 assert(0);
240 }
241 #endif /* DEBUG_NOUVEAU_STATEOBJ */
242
243 /* We cannot update total in case we so_emit again. */
244 nr = so->total + so->cur;
245
246 /* This will flush if we need space.
247 * We don't actually need the marker.
248 */
249 if ((ret = nouveau_pushbuf_marker_emit(chan, nr, so->cur_reloc))) {
250 debug_printf("so_emit failed marker emit with error %d\n", ret);
251 assert(0);
252 }
253
254 /* Submit data. This will ensure proper binding of objects. */
255 for (i = 0; i < so->cur_start; i++) {
256 BEGIN_RING(chan, so->start[i].gr, so->start[i].mthd, so->start[i].size);
257 OUT_RINGp(chan, &(so->pool[so->start[i].offset]), so->start[i].size);
258 }
259
260 for (i = 0; i < so->cur_reloc; i++) {
261 struct nouveau_stateobj_reloc *r = &so->reloc[i];
262
263 if ((ret = nouveau_pushbuf_emit_reloc(chan, pb->cur - nr +
264 r->push_offset, r->bo, r->data,
265 0, r->flags, r->vor, r->tor))) {
266 debug_printf("so_emit failed reloc with error %d\n", ret);
267 assert(0);
268 }
269 }
270 }
271
272 static INLINE void
273 so_emit_reloc_markers(struct nouveau_channel *chan, struct nouveau_stateobj *so)
274 {
275 struct nouveau_pushbuf *pb = chan->pushbuf;
276 struct nouveau_grobj *gr = NULL;
277 unsigned i;
278 int ret = 0;
279
280 if (!so)
281 return;
282
283 /* If we need to flush in flush notify, then we have a problem anyway. */
284 for (i = 0; i < so->cur_reloc; i++) {
285 struct nouveau_stateobj_reloc *r = &so->reloc[i];
286
287 #ifdef DEBUG_NOUVEAU_STATEOBJ
288 if (r->mthd & 0x40000000) {
289 debug_printf("error: NI mthd 0x%08X\n", r->mthd);
290 continue;
291 }
292 #endif /* DEBUG_NOUVEAU_STATEOBJ */
293
294 /* The object needs to be bound and the system must know the
295 * subchannel is being used. Otherwise it will discard it.
296 */
297 if (gr != r->gr) {
298 BEGIN_RING(chan, r->gr, 0x100, 1);
299 OUT_RING(chan, 0);
300 gr = r->gr;
301 }
302
303 /* Some relocs really don't like to be hammered,
304 * NOUVEAU_BO_DUMMY makes sure it only
305 * happens when needed.
306 */
307 ret = OUT_RELOC(chan, r->bo, (r->gr->subc << 13) | (1<< 18) |
308 r->mthd, (r->flags & (NOUVEAU_BO_VRAM | NOUVEAU_BO_GART
309 | NOUVEAU_BO_RDWR)) | NOUVEAU_BO_DUMMY, 0, 0);
310 if (ret) {
311 debug_printf("OUT_RELOC failed %d\n", ret);
312 assert(0);
313 }
314
315 ret = OUT_RELOC(chan, r->bo, r->data, r->flags |
316 NOUVEAU_BO_DUMMY, r->vor, r->tor);
317 if (ret) {
318 debug_printf("OUT_RELOC failed %d\n", ret);
319 assert(0);
320 }
321
322 pb->remaining -= 2;
323 }
324 }
325
326 #endif