148406b85a7836a126b16348f45f169421757f56
[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 while ((r = ptr_to_pbrel(pbbo->relocs))) {
135 uint32_t push;
136
137 if (r->flags & NOUVEAU_BO_LOW) {
138 push = bo->offset + r->data;
139 } else
140 if (r->flags & NOUVEAU_BO_HIGH) {
141 push = (bo->offset + r->data) >> 32;
142 } else {
143 push = r->data;
144 }
145
146 if (r->flags & NOUVEAU_BO_OR) {
147 if (bo->flags & NOUVEAU_BO_VRAM)
148 push |= r->vor;
149 else
150 push |= r->tor;
151 }
152
153 *r->ptr = push;
154 pbbo->relocs = r->next;
155 free(r);
156 }
157
158 nvpb->buffers = pbbo->next;
159 free(pbbo);
160 }
161 nvpb->nr_buffers = 0;
162
163 /* Switch back to user's ring */
164 RING_SPACE_CH(chan, 1);
165 OUT_RING_CH(chan, 0x20000000 | ((nvpb->start << 2) +
166 nvchan->dma_master.base));
167 nvchan->dma = &nvchan->dma_master;
168
169 /* Fence + kickoff */
170 nouveau_fence_emit(fence);
171 FIRE_RING_CH(chan);
172 nouveau_fence_del(&fence);
173
174 /* Allocate space for next push buffer */
175 assert(!nouveau_pushbuf_space(chan, min));
176
177 return 0;
178 }
179
180 static struct nouveau_pushbuf_bo *
181 nouveau_pushbuf_emit_buffer(struct nouveau_channel *chan, struct nouveau_bo *bo)
182 {
183 struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(chan->pushbuf);
184 struct nouveau_pushbuf_bo *pbbo = ptr_to_pbbo(nvpb->buffers);
185
186 while (pbbo) {
187 if (pbbo->handle == bo->handle)
188 return pbbo;
189 pbbo = ptr_to_pbbo(pbbo->next);
190 }
191
192 pbbo = malloc(sizeof(struct nouveau_pushbuf_bo));
193 pbbo->next = nvpb->buffers;
194 nvpb->buffers = pbbo_to_ptr(pbbo);
195 nvpb->nr_buffers++;
196
197 pbbo->handle = bo_to_ptr(bo);
198 pbbo->flags = NOUVEAU_BO_VRAM | NOUVEAU_BO_GART;
199 pbbo->relocs = 0;
200 pbbo->nr_relocs = 0;
201 return pbbo;
202 }
203
204 int
205 nouveau_pushbuf_emit_reloc(struct nouveau_channel *chan, void *ptr,
206 struct nouveau_bo *bo, uint32_t data, uint32_t flags,
207 uint32_t vor, uint32_t tor)
208 {
209 struct nouveau_pushbuf_bo *pbbo;
210 struct nouveau_pushbuf_reloc *r;
211
212 if (!chan)
213 return -EINVAL;
214
215 pbbo = nouveau_pushbuf_emit_buffer(chan, bo);
216 if (!pbbo)
217 return -EFAULT;
218
219 r = malloc(sizeof(struct nouveau_pushbuf_reloc));
220 r->next = pbbo->relocs;
221 pbbo->relocs = pbrel_to_ptr(r);
222 pbbo->nr_relocs++;
223
224 pbbo->flags |= (flags & NOUVEAU_BO_RDWR);
225 pbbo->flags &= (flags | NOUVEAU_BO_RDWR);
226
227 r->handle = bo_to_ptr(r);
228 r->ptr = ptr;
229 r->flags = flags;
230 r->data = data;
231 r->vor = vor;
232 r->tor = tor;
233
234 return 0;
235 }
236