Merge branch 'upstream-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, 4096,
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 static void
52 nouveau_pushbuf_fence_signalled(void *priv)
53 {
54 struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(priv);
55
56 nouveau_fence_del(&nvpb->fence);
57 nouveau_resource_free(&nvpb->res);
58 free(nvpb);
59 }
60
61 /* This would be our TTM "superioctl" */
62 int
63 nouveau_pushbuf_flush(struct nouveau_channel *chan)
64 {
65 struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
66 struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(nvchan->pb_tail);
67 struct nouveau_pushbuf_bo *pbbo;
68 struct nouveau_fence *fence = NULL;
69 int sync_hack = 0;
70 int ret;
71
72 if (!nvpb)
73 goto out_realloc;
74
75 if (nvpb->base.remaining == nvpb->res->size / 4)
76 return 0;
77 nvchan->pb_tail = NULL;
78
79 ret = nouveau_fence_new(chan, &fence);
80 if (ret)
81 return ret;
82
83 /* Validate buffers + apply relocations */
84 while ((pbbo = ptr_to_pbbo(nvpb->buffers))) {
85 struct nouveau_pushbuf_reloc *r;
86 struct nouveau_bo *bo = &ptr_to_bo(pbbo->handle)->base;
87
88 ret = nouveau_bo_validate(chan, bo, fence, pbbo->flags);
89 assert (ret == 0);
90
91 sync_hack |= nouveau_bo(bo)->sync_hack;
92 nouveau_bo(bo)->sync_hack = 0;
93
94 while ((r = ptr_to_pbrel(pbbo->relocs))) {
95 uint32_t push;
96
97 if (r->flags & NOUVEAU_BO_LOW) {
98 push = bo->offset + r->data;
99 } else
100 if (r->flags & NOUVEAU_BO_HIGH) {
101 push = (bo->offset + r->data) >> 32;
102 } else {
103 push = r->data;
104 }
105
106 if (r->flags & NOUVEAU_BO_OR) {
107 if (bo->flags & NOUVEAU_BO_VRAM)
108 push |= r->vor;
109 else
110 push |= r->tor;
111 }
112
113 *r->ptr = push;
114 pbbo->relocs = r->next;
115 free(r);
116 }
117
118 nvpb->buffers = pbbo->next;
119 free(pbbo);
120 }
121 nvpb->nr_buffers = 0;
122
123 /* Emit JMP to indirect pushbuf */
124 if (nvchan->dma.free < 1)
125 WAIT_RING_CH(chan, 1);
126 nvchan->dma.free -= 1;
127 #ifdef NOUVEAU_DMA_DEBUG
128 nvchan->dma.push_free = 1;
129 #endif
130 OUT_RING_CH(chan, 0x20000000 | nvpb->res->start);
131
132 /* Add JMP back to master pushbuf from indirect pushbuf */
133 (*nvpb->base.cur++) =
134 0x20000000 | ((nvchan->dma.cur << 2) + nvchan->dma.base);
135
136 /* Fence */
137 nvpb->fence = fence;
138 nouveau_fence_signal_cb(nvpb->fence, nouveau_pushbuf_fence_signalled,
139 nvpb);
140 nouveau_fence_emit(nvpb->fence);
141
142 /* Kickoff */
143 FIRE_RING_CH(chan);
144
145 if (sync_hack) {
146 struct nouveau_fence *f = NULL;
147 nouveau_fence_ref(nvpb->fence, &f);
148 nouveau_fence_wait(&f);
149 }
150
151 /* Allocate space for next push buffer */
152 out_realloc:
153 nvpb = calloc(1, sizeof(struct nouveau_pushbuf_priv));
154 if (!nvpb)
155 return -ENOMEM;
156
157 while (nouveau_resource_alloc(nvchan->pb_heap, 0x2000, NULL,
158 &nvpb->res)) {
159 nouveau_fence_flush(chan);
160 }
161
162 nvpb->base.channel = chan;
163 nvpb->base.remaining = nvpb->res->size / 4;
164 nvpb->base.cur = &nvchan->pushbuf[nvpb->res->start/4];
165 nvchan->pb_tail = &nvpb->base;
166
167 return 0;
168 }
169
170 static struct nouveau_pushbuf_bo *
171 nouveau_pushbuf_emit_buffer(struct nouveau_channel *chan, struct nouveau_bo *bo)
172 {
173 struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
174 struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(nvchan->pb_tail);
175 struct nouveau_pushbuf_bo *pbbo = ptr_to_pbbo(nvpb->buffers);
176
177 while (pbbo) {
178 if (pbbo->handle == bo->handle)
179 return pbbo;
180 pbbo = ptr_to_pbbo(pbbo->next);
181 }
182
183 pbbo = malloc(sizeof(struct nouveau_pushbuf_bo));
184 pbbo->next = nvpb->buffers;
185 nvpb->buffers = pbbo_to_ptr(pbbo);
186 nvpb->nr_buffers++;
187
188 pbbo->handle = bo_to_ptr(bo);
189 pbbo->flags = NOUVEAU_BO_VRAM | NOUVEAU_BO_GART;
190 pbbo->relocs = 0;
191 pbbo->nr_relocs = 0;
192 return pbbo;
193 }
194
195 int
196 nouveau_pushbuf_emit_reloc(struct nouveau_channel *chan, void *ptr,
197 struct nouveau_bo *bo, uint32_t data, uint32_t flags,
198 uint32_t vor, uint32_t tor)
199 {
200 struct nouveau_pushbuf_bo *pbbo;
201 struct nouveau_pushbuf_reloc *r;
202
203 if (!chan)
204 return -EINVAL;
205
206 pbbo = nouveau_pushbuf_emit_buffer(chan, bo);
207 if (!pbbo)
208 return -EFAULT;
209
210 r = malloc(sizeof(struct nouveau_pushbuf_reloc));
211 r->next = pbbo->relocs;
212 pbbo->relocs = pbrel_to_ptr(r);
213 pbbo->nr_relocs++;
214
215 pbbo->flags |= (flags & NOUVEAU_BO_RDWR);
216 pbbo->flags &= (flags | NOUVEAU_BO_RDWR);
217
218 r->handle = bo_to_ptr(r);
219 r->ptr = ptr;
220 r->flags = flags;
221 r->data = data;
222 r->vor = vor;
223 r->tor = tor;
224
225 return 0;
226 }
227