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 #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 /* This would be our TTM "superioctl" */
100 int
101 nouveau_pushbuf_flush(struct nouveau_channel *chan, unsigned min)
102 {
103 struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
104 struct nouveau_pushbuf_priv *nvpb = &nvchan->pb;
105 struct nouveau_pushbuf_bo *pbbo;
106 struct nouveau_fence *fence = NULL;
107 int ret;
108
109 if (nvpb->base.remaining == nvpb->size)
110 return 0;
111
112 nvpb->size -= nvpb->base.remaining;
113 nvchan->dma->cur += nvpb->size;
114 nvchan->dma->free -= nvpb->size;
115 assert(nvchan->dma->cur <= nvchan->dma->max);
116
117 ret = nouveau_fence_new(chan, &fence);
118 if (ret)
119 return ret;
120
121 nvchan->dma = &nvchan->dma_bufmgr;
122 nvchan->pushbuf[nvpb->nop_jump] = 0x20000000 |
123 (nvchan->dma->base + (nvchan->dma->cur << 2));
124
125 /* Validate buffers + apply relocations */
126 nvchan->user_charge = 0;
127 while ((pbbo = ptr_to_pbbo(nvpb->buffers))) {
128 struct nouveau_pushbuf_reloc *r;
129 struct nouveau_bo *bo = &ptr_to_bo(pbbo->handle)->base;
130
131 ret = nouveau_bo_validate(chan, bo, fence, pbbo->flags);
132 assert (ret == 0);
133
134 if (bo->offset == nouveau_bo(bo)->offset &&
135 bo->flags == nouveau_bo(bo)->flags) {
136 /*XXX: could avoid reloc in this case, except with the
137 * current design we'd confuse the GPU quite a bit
138 * if we did this. Will fix soon.
139 */
140 }
141 bo->offset = nouveau_bo(bo)->offset;
142 bo->flags = nouveau_bo(bo)->flags;
143
144 while ((r = ptr_to_pbrel(pbbo->relocs))) {
145 uint32_t push;
146
147 if (r->flags & NOUVEAU_BO_LOW) {
148 push = bo->offset + r->data;
149 } else
150 if (r->flags & NOUVEAU_BO_HIGH) {
151 push = (bo->offset + r->data) >> 32;
152 } else {
153 push = r->data;
154 }
155
156 if (r->flags & NOUVEAU_BO_OR) {
157 if (bo->flags & NOUVEAU_BO_VRAM)
158 push |= r->vor;
159 else
160 push |= r->tor;
161 }
162
163 *r->ptr = push;
164 pbbo->relocs = r->next;
165 free(r);
166 }
167
168 nvpb->buffers = pbbo->next;
169 free(pbbo);
170 }
171 nvpb->nr_buffers = 0;
172
173 /* Switch back to user's ring */
174 RING_SPACE_CH(chan, 1);
175 OUT_RING_CH(chan, 0x20000000 | ((nvpb->start << 2) +
176 nvchan->dma_master.base));
177 nvchan->dma = &nvchan->dma_master;
178
179 /* Fence + kickoff */
180 nouveau_fence_emit(fence);
181 FIRE_RING_CH(chan);
182 nouveau_fence_ref(NULL, &fence);
183
184 /* Allocate space for next push buffer */
185 assert(!nouveau_pushbuf_space(chan, min));
186
187 return 0;
188 }
189
190 static struct nouveau_pushbuf_bo *
191 nouveau_pushbuf_emit_buffer(struct nouveau_channel *chan, struct nouveau_bo *bo)
192 {
193 struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(chan->pushbuf);
194 struct nouveau_pushbuf_bo *pbbo = ptr_to_pbbo(nvpb->buffers);
195
196 while (pbbo) {
197 if (pbbo->handle == bo->handle)
198 return pbbo;
199 pbbo = ptr_to_pbbo(pbbo->next);
200 }
201
202 pbbo = malloc(sizeof(struct nouveau_pushbuf_bo));
203 pbbo->next = nvpb->buffers;
204 nvpb->buffers = pbbo_to_ptr(pbbo);
205 nvpb->nr_buffers++;
206
207 pbbo->handle = bo_to_ptr(bo);
208 pbbo->flags = NOUVEAU_BO_VRAM | NOUVEAU_BO_GART;
209 pbbo->relocs = 0;
210 pbbo->nr_relocs = 0;
211 return pbbo;
212 }
213
214 int
215 nouveau_pushbuf_emit_reloc(struct nouveau_channel *chan, void *ptr,
216 struct nouveau_bo *bo, uint32_t data, uint32_t flags,
217 uint32_t vor, uint32_t tor)
218 {
219 struct nouveau_pushbuf_bo *pbbo;
220 struct nouveau_pushbuf_reloc *r;
221
222 if (!chan)
223 return -EINVAL;
224
225 pbbo = nouveau_pushbuf_emit_buffer(chan, bo);
226 if (!pbbo)
227 return -EFAULT;
228
229 r = malloc(sizeof(struct nouveau_pushbuf_reloc));
230 r->next = pbbo->relocs;
231 pbbo->relocs = pbrel_to_ptr(r);
232 pbbo->nr_relocs++;
233
234 pbbo->flags |= (flags & NOUVEAU_BO_RDWR);
235 pbbo->flags &= (flags | NOUVEAU_BO_RDWR);
236
237 r->handle = bo_to_ptr(r);
238 r->ptr = ptr;
239 r->flags = flags;
240 r->data = data;
241 r->vor = vor;
242 r->tor = tor;
243
244 return 0;
245 }
246