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