7d5eddb92ff81bc41500125437f94f92294f09d6
[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_BUFMGR_DWORDS (4096 / 2)
31 #define PB_MIN_USER_DWORDS 2048
32
33 static int
34 nouveau_pushbuf_space(struct nouveau_channel *chan, unsigned min)
35 {
36 struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
37 struct nouveau_pushbuf_priv *nvpb = &nvchan->pb;
38
39 assert((min + 1) <= nvchan->dma->max);
40
41 /* Wait for enough space in push buffer */
42 min = min < PB_MIN_USER_DWORDS ? PB_MIN_USER_DWORDS : min;
43 min += 1; /* a bit extra for the NOP */
44 if (nvchan->dma->free < min)
45 WAIT_RING_CH(chan, min);
46
47 /* Insert NOP, may turn into a jump later */
48 RING_SPACE_CH(chan, 1);
49 nvpb->nop_jump = nvchan->dma->cur;
50 OUT_RING_CH(chan, 0);
51
52 /* Any remaining space is available to the user */
53 nvpb->start = nvchan->dma->cur;
54 nvpb->size = nvchan->dma->free;
55 nvpb->base.channel = chan;
56 nvpb->base.remaining = nvpb->size;
57 nvpb->base.cur = &nvchan->pushbuf[nvpb->start];
58
59 return 0;
60 }
61
62 int
63 nouveau_pushbuf_init(struct nouveau_channel *chan)
64 {
65 struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
66 struct nouveau_dma_priv *m = &nvchan->dma_master;
67 struct nouveau_dma_priv *b = &nvchan->dma_bufmgr;
68 int i;
69
70 if (!nvchan)
71 return -EINVAL;
72
73 /* Reassign last bit of push buffer for a "separate" bufmgr
74 * ring buffer
75 */
76 m->max -= PB_BUFMGR_DWORDS;
77 m->free -= PB_BUFMGR_DWORDS;
78
79 b->base = m->base + ((m->max + 2) << 2);
80 b->max = PB_BUFMGR_DWORDS - 2;
81 b->cur = b->put = 0;
82 b->free = b->max - b->cur;
83
84 /* Some NOPs just to be safe
85 *XXX: RING_SKIPS
86 */
87 nvchan->dma = b;
88 RING_SPACE_CH(chan, 8);
89 for (i = 0; i < 8; i++)
90 OUT_RING_CH(chan, 0);
91 nvchan->dma = m;
92
93 nouveau_pushbuf_space(chan, 0);
94 chan->pushbuf = &nvchan->pb.base;
95
96 return 0;
97 }
98
99 static uint32_t
100 nouveau_pushbuf_calc_reloc(struct nouveau_bo *bo,
101 struct nouveau_pushbuf_reloc *r)
102 {
103 uint32_t push;
104
105 if (r->flags & NOUVEAU_BO_LOW) {
106 push = bo->offset + r->data;
107 } else
108 if (r->flags & NOUVEAU_BO_HIGH) {
109 push = (bo->offset + r->data) >> 32;
110 } else {
111 push = r->data;
112 }
113
114 if (r->flags & NOUVEAU_BO_OR) {
115 if (bo->flags & NOUVEAU_BO_VRAM)
116 push |= r->vor;
117 else
118 push |= r->tor;
119 }
120
121 return push;
122 }
123
124 /* This would be our TTM "superioctl" */
125 int
126 nouveau_pushbuf_flush(struct nouveau_channel *chan, unsigned min)
127 {
128 struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
129 struct nouveau_pushbuf_priv *nvpb = &nvchan->pb;
130 struct nouveau_pushbuf_bo *pbbo;
131 struct nouveau_fence *fence = NULL;
132 int ret;
133
134 if (nvpb->base.remaining == nvpb->size)
135 return 0;
136
137 nvpb->size -= nvpb->base.remaining;
138 nvchan->dma->cur += nvpb->size;
139 nvchan->dma->free -= nvpb->size;
140 assert(nvchan->dma->cur <= nvchan->dma->max);
141
142 ret = nouveau_fence_new(chan, &fence);
143 if (ret)
144 return ret;
145
146 nvchan->dma = &nvchan->dma_bufmgr;
147 nvchan->pushbuf[nvpb->nop_jump] = 0x20000000 |
148 (nvchan->dma->base + (nvchan->dma->cur << 2));
149
150 /* Validate buffers + apply relocations */
151 nvchan->user_charge = 0;
152 while ((pbbo = ptr_to_pbbo(nvpb->buffers))) {
153 struct nouveau_pushbuf_reloc *r;
154 struct nouveau_bo *bo = &ptr_to_bo(pbbo->handle)->base;
155
156 ret = nouveau_bo_validate(chan, bo, fence, pbbo->flags);
157 assert (ret == 0);
158
159 if (bo->offset == nouveau_bo(bo)->offset &&
160 bo->flags == nouveau_bo(bo)->flags) {
161 while ((r = ptr_to_pbrel(pbbo->relocs))) {
162 pbbo->relocs = r->next;
163 free(r);
164 }
165
166 nvpb->buffers = pbbo->next;
167 free(pbbo);
168 continue;
169 }
170 bo->offset = nouveau_bo(bo)->offset;
171 bo->flags = nouveau_bo(bo)->flags;
172
173 while ((r = ptr_to_pbrel(pbbo->relocs))) {
174 *r->ptr = nouveau_pushbuf_calc_reloc(bo, r);
175 pbbo->relocs = r->next;
176 free(r);
177 }
178
179 nvpb->buffers = pbbo->next;
180 free(pbbo);
181 }
182 nvpb->nr_buffers = 0;
183
184 /* Switch back to user's ring */
185 RING_SPACE_CH(chan, 1);
186 OUT_RING_CH(chan, 0x20000000 | ((nvpb->start << 2) +
187 nvchan->dma_master.base));
188 nvchan->dma = &nvchan->dma_master;
189
190 /* Fence + kickoff */
191 nouveau_fence_emit(fence);
192 FIRE_RING_CH(chan);
193 nouveau_fence_ref(NULL, &fence);
194
195 /* Allocate space for next push buffer */
196 assert(!nouveau_pushbuf_space(chan, min));
197
198 return 0;
199 }
200
201 static struct nouveau_pushbuf_bo *
202 nouveau_pushbuf_emit_buffer(struct nouveau_channel *chan, struct nouveau_bo *bo)
203 {
204 struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(chan->pushbuf);
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 if (flags & NOUVEAU_BO_DUMMY)
256 *(uint32_t *)ptr = 0;
257 else
258 *(uint32_t *)ptr = nouveau_pushbuf_calc_reloc(bo, r);
259 return 0;
260 }
261