Merge branch 'nouveau-gallium-0.1' into darktama-gallium-0.1
[mesa.git] / src / mesa / drivers / dri / nouveau_winsys / nouveau_pushbuf.c
1 /*
2 * Copyright 2007 Nouveau Project
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <assert.h>
26
27 #include "nouveau_drmif.h"
28 #include "nouveau_dma.h"
29
30 int
31 nouveau_pushbuf_init(struct nouveau_channel *chan)
32 {
33 struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
34
35 if (!nvchan)
36 return -EINVAL;
37
38 /* Everything except first 4KiB of the push buffer is managed by us */
39 if (nouveau_resource_init(&nvchan->pb_heap,
40 nvchan->drm.cmdbuf_size - 4096))
41 return -EINVAL;
42
43 /* Shrink master ring to 4KiB */
44 assert(nvchan->dma.cur <= (4096/4));
45 nvchan->dma.max = (4096 / 4) - 2;
46 nvchan->dma.free = nvchan->dma.max - nvchan->dma.cur;
47
48 return 0;
49 }
50
51 /* This would be our TTM "superioctl" */
52 int
53 nouveau_pushbuf_flush(struct nouveau_channel *chan)
54 {
55 struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
56 struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(nvchan->pb_tail);
57 struct nouveau_pushbuf_bo *pbbo;
58 struct nouveau_fence *fence = NULL;
59 int sync_hack = 0;
60 int ret;
61
62 if (!nvpb)
63 goto out_realloc;
64
65 if (nvpb->base.remaining == nvpb->res->size / 4)
66 return 0;
67
68 ret = nouveau_fence_new(chan, &fence);
69 if (ret)
70 return ret;
71
72 /* Validate buffers + apply relocations */
73 while ((pbbo = ptr_to_pbbo(nvpb->buffers))) {
74 struct nouveau_pushbuf_reloc *r;
75 struct nouveau_bo *bo = &ptr_to_bo(pbbo->handle)->base;
76
77 ret = nouveau_bo_validate(chan, bo, fence, pbbo->flags);
78 assert (ret == 0);
79
80 sync_hack |= nouveau_bo(bo)->sync_hack;
81 nouveau_bo(bo)->sync_hack = 0;
82
83 while ((r = ptr_to_pbrel(pbbo->relocs))) {
84 uint32_t push;
85
86 if (r->flags & NOUVEAU_BO_LOW) {
87 push = bo->offset + r->data;
88 } else
89 if (r->flags & NOUVEAU_BO_HIGH) {
90 push = (bo->offset + r->data) >> 32;
91 } else {
92 push = r->data;
93 }
94
95 if (r->flags & NOUVEAU_BO_OR) {
96 if (bo->flags & NOUVEAU_BO_VRAM)
97 push |= r->vor;
98 else
99 push |= r->tor;
100 }
101
102 *r->ptr = push;
103 pbbo->relocs = r->next;
104 free(r);
105 }
106
107 nvpb->buffers = pbbo->next;
108 free(pbbo);
109 }
110 nvpb->nr_buffers = 0;
111
112 /* Emit JMP to indirect pushbuf */
113 if (nvchan->dma.free < 1)
114 WAIT_RING_CH(chan, 1);
115 nvchan->dma.free -= 1;
116 #ifdef NOUVEAU_DMA_DEBUG
117 nvchan->dma.push_free = 1;
118 #endif
119 OUT_RING_CH(chan, 0x20000000 | (nvpb->res->start + 4096));
120
121 /* Add JMP back to master pushbuf from indirect pushbuf */
122 (*nvpb->base.cur++) =
123 0x20000000 | ((nvchan->dma.cur << 2) + nvchan->dma.base);
124
125 /* Fence */
126 nvpb->fence = fence;
127 nouveau_fence_emit(nvpb->fence);
128
129 /* Kickoff */
130 FIRE_RING_CH(chan);
131
132 /* Allocate space for next push buffer */
133 out_realloc:
134 nvpb = calloc(1, sizeof(struct nouveau_pushbuf_priv));
135 if (!nvpb)
136 return -ENOMEM;
137
138 if (nouveau_resource_alloc(nvchan->pb_heap, 0x2000, NULL, &nvpb->res)) {
139 struct nouveau_pushbuf_priv *e;
140 int nr = 0;
141
142 /* Update fences */
143 nouveau_fence_flush(chan);
144
145 /* Free any push buffers that have already been executed */
146 e = nouveau_pushbuf(nvchan->pb_head);
147 while (e && e->fence) {
148 if (!e->fence || !nouveau_fence(e->fence)->signalled)
149 break;
150 nouveau_fence_del(&e->fence);
151 nouveau_resource_free(&e->res);
152 nr++;
153
154 nvchan->pb_head = e->next;
155 if (nvchan->pb_head == NULL)
156 nvchan->pb_tail = NULL;
157 free(e);
158 e = nouveau_pushbuf(nvchan->pb_head);
159 }
160
161 /* We didn't free any buffers above. As a last resort, busy
162 * wait on the oldest buffer becoming available.
163 */
164 if (!nr) {
165 e = nouveau_pushbuf(nvchan->pb_head);
166 nouveau_fence_wait(&e->fence);
167 nouveau_resource_free(&e->res);
168
169 nvchan->pb_head = e->next;
170 if (nvchan->pb_head == NULL)
171 nvchan->pb_tail = NULL;
172 free(e);
173 }
174
175 if (nouveau_resource_alloc(nvchan->pb_heap, 0x2000, nvpb,
176 &nvpb->res))
177 assert(0);
178 }
179
180 nvpb->base.channel = chan;
181 nvpb->base.remaining = nvpb->res->size / 4;
182 nvpb->base.cur = &nvchan->pushbuf[(nvpb->res->start + 4096)/4];
183
184 if (nvchan->pb_tail) {
185 nouveau_pushbuf(nvchan->pb_tail)->next = &nvpb->base;
186 } else {
187 nvchan->pb_head = &nvpb->base;
188 }
189 nvchan->pb_tail = &nvpb->base;
190
191 if (sync_hack) {
192 struct nouveau_fence *f = NULL;
193 nouveau_fence_ref(nvpb->fence, &f);
194 nouveau_fence_wait(&f);
195 }
196
197 return 0;
198 }
199
200 static struct nouveau_pushbuf_bo *
201 nouveau_pushbuf_emit_buffer(struct nouveau_channel *chan, struct nouveau_bo *bo)
202 {
203 struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
204 struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(nvchan->pb_tail);
205 struct nouveau_pushbuf_bo *pbbo = ptr_to_pbbo(nvpb->buffers);
206
207 while (pbbo) {
208 if (pbbo->handle == bo->handle)
209 return pbbo;
210 pbbo = ptr_to_pbbo(pbbo->next);
211 }
212
213 pbbo = malloc(sizeof(struct nouveau_pushbuf_bo));
214 pbbo->next = nvpb->buffers;
215 nvpb->buffers = pbbo_to_ptr(pbbo);
216 nvpb->nr_buffers++;
217
218 pbbo->handle = bo_to_ptr(bo);
219 pbbo->flags = NOUVEAU_BO_VRAM | NOUVEAU_BO_GART;
220 pbbo->relocs = 0;
221 pbbo->nr_relocs = 0;
222 return pbbo;
223 }
224
225 int
226 nouveau_pushbuf_emit_reloc(struct nouveau_channel *chan, void *ptr,
227 struct nouveau_bo *bo, uint32_t data, uint32_t flags,
228 uint32_t vor, uint32_t tor)
229 {
230 struct nouveau_pushbuf_bo *pbbo;
231 struct nouveau_pushbuf_reloc *r;
232
233 if (!chan)
234 return -EINVAL;
235
236 pbbo = nouveau_pushbuf_emit_buffer(chan, bo);
237 if (!pbbo)
238 return -EFAULT;
239
240 r = malloc(sizeof(struct nouveau_pushbuf_reloc));
241 r->next = pbbo->relocs;
242 pbbo->relocs = pbrel_to_ptr(r);
243 pbbo->nr_relocs++;
244
245 pbbo->flags |= (flags & NOUVEAU_BO_RDWR);
246 pbbo->flags &= (flags | NOUVEAU_BO_RDWR);
247
248 r->handle = bo_to_ptr(r);
249 r->ptr = ptr;
250 r->flags = flags;
251 r->data = data;
252 r->vor = vor;
253 r->tor = tor;
254
255 return 0;
256 }
257