Source
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
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
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
+
struct nouveau_bo *bo;
39
+
int ret;
40
+
41
+
if (min < PB_MIN_USER_DWORDS)
42
+
min = PB_MIN_USER_DWORDS;
43
+
44
+
nvpb->current_offset = chan->cur - nvpb->pushbuf;
45
+
if (chan->cur + min + 2 <= chan->end)
46
+
return 0;
47
+
48
+
nvpb->current++;
49
+
if (nvpb->current == CALPB_BUFFERS)
50
+
nvpb->current = 0;
51
+
bo = nvpb->buffer[nvpb->current];
52
+
53
+
ret = nouveau_bo_map(bo, NOUVEAU_BO_WR);
54
+
if (ret)
55
+
return ret;
56
+
57
+
nvpb->size = (bo->size - 8) / 4;
58
+
nvpb->pushbuf = bo->map;
59
+
nvpb->current_offset = 0;
60
+
61
+
chan->cur = nvpb->pushbuf;
62
+
chan->end = nvpb->pushbuf + nvpb->size;
63
+
64
+
nouveau_bo_unmap(bo);
65
+
return 0;
66
+
}
67
+
68
+
static void
69
+
nouveau_pushbuf_fini_call(struct nouveau_channel *chan)
70
+
{
71
+
struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
72
+
struct nouveau_pushbuf_priv *nvpb = &nvchan->pb;
73
+
int i;
74
+
75
+
for (i = 0; i < CALPB_BUFFERS; i++)
76
+
nouveau_bo_ref(NULL, &nvpb->buffer[i]);
77
+
nvpb->pushbuf = NULL;
78
+
}
79
+
80
+
static int
81
+
nouveau_pushbuf_init_call(struct nouveau_channel *chan, int buf_size)
82
+
{
83
+
struct drm_nouveau_gem_pushbuf req;
84
+
struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
85
+
struct nouveau_pushbuf_priv *nvpb = &nvchan->pb;
86
+
struct nouveau_device *dev = chan->device;
87
+
uint32_t flags = 0;
88
+
int i, ret;
89
+
90
+
if (nvchan->drm.pushbuf_domains & NOUVEAU_GEM_DOMAIN_GART)
91
+
flags |= NOUVEAU_BO_GART;
92
+
else
93
+
flags |= NOUVEAU_BO_VRAM;
94
+
95
+
req.channel = chan->id;
96
+
req.nr_push = 0;
97
+
ret = drmCommandWriteRead(nouveau_device(dev)->fd,
98
+
DRM_NOUVEAU_GEM_PUSHBUF, &req, sizeof(req));
99
+
if (ret)
100
+
return ret;
101
+
102
+
for (i = 0; i < CALPB_BUFFERS; i++) {
103
+
ret = nouveau_bo_new(dev, flags | NOUVEAU_BO_MAP,
104
+
0, buf_size, &nvpb->buffer[i]);
105
+
if (ret) {
106
+
nouveau_pushbuf_fini_call(chan);
107
+
return ret;
108
+
}
109
+
}
110
+
111
+
nvpb->cal_suffix0 = req.suffix0;
112
+
nvpb->cal_suffix1 = req.suffix1;
113
+
return 0;
114
+
}
115
+
116
+
int
117
+
nouveau_pushbuf_init(struct nouveau_channel *chan, int buf_size)
118
+
{
119
+
struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
120
+
struct nouveau_pushbuf_priv *nvpb = &nvchan->pb;
121
+
int ret;
122
+
123
+
ret = nouveau_pushbuf_init_call(chan, buf_size);
124
+
if (ret)
125
+
return ret;
126
+
127
+
ret = nouveau_pushbuf_space(chan, 0);
128
+
if (ret)
129
+
return ret;
130
+
131
+
nvpb->buffers = calloc(NOUVEAU_GEM_MAX_BUFFERS,
132
+
sizeof(struct drm_nouveau_gem_pushbuf_bo));
133
+
nvpb->relocs = calloc(NOUVEAU_GEM_MAX_RELOCS,
134
+
sizeof(struct drm_nouveau_gem_pushbuf_reloc));
135
+
return 0;
136
+
}
137
+
138
+
void
139
+
nouveau_pushbuf_fini(struct nouveau_channel *chan)
140
+
{
141
+
struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
142
+
struct nouveau_pushbuf_priv *nvpb = &nvchan->pb;
143
+
nouveau_pushbuf_fini_call(chan);
144
+
free(nvpb->buffers);
145
+
free(nvpb->relocs);
146
+
}
147
+
148
+
static int
149
+
nouveau_pushbuf_bo_add(struct nouveau_channel *chan, struct nouveau_bo *bo,
150
+
unsigned offset, unsigned length)
151
+
{
152
+
struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
153
+
struct nouveau_pushbuf_priv *nvpb = &nvchan->pb;
154
+
struct drm_nouveau_gem_pushbuf_push *p = &nvpb->push[nvpb->nr_push++];
155
+
struct drm_nouveau_gem_pushbuf_bo *pbbo;
156
+
struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
157
+
158
+
pbbo = nouveau_bo_emit_buffer(chan, bo);
159
+
if (!pbbo)
160
+
return -ENOMEM;
161
+
pbbo->valid_domains &= nvchan->drm.pushbuf_domains;
162
+
pbbo->read_domains |= nvchan->drm.pushbuf_domains;
163
+
nvbo->pending_refcnt++;
164
+
165
+
p->bo_index = pbbo - nvpb->buffers;
166
+
p->offset = offset;
167
+
p->length = length;
168
+
return 0;
169
+
}
170
+
171
+
int
172
+
nouveau_pushbuf_submit(struct nouveau_channel *chan, struct nouveau_bo *bo,
173
+
unsigned offset, unsigned length)
174
+
{
175
+
struct nouveau_pushbuf_priv *nvpb = &nouveau_channel(chan)->pb;
176
+
int ret, len;
177
+
178
+
if ((AVAIL_RING(chan) + nvpb->current_offset) != nvpb->size) {
179
+
if (nvpb->cal_suffix0 || nvpb->cal_suffix1) {
180
+
*(chan->cur++) = nvpb->cal_suffix0;
181
+
*(chan->cur++) = nvpb->cal_suffix1;
182
+
}
183
+
184
+
len = (chan->cur - nvpb->pushbuf) - nvpb->current_offset;
185
+
186
+
ret = nouveau_pushbuf_bo_add(chan, nvpb->buffer[nvpb->current],
187
+
nvpb->current_offset * 4, len * 4);
188
+
if (ret)
189
+
return ret;
190
+
191
+
nvpb->current_offset += len;
192
+
}
193
+
194
+
return bo ? nouveau_pushbuf_bo_add(chan, bo, offset, length) : 0;
195
+
}
196
+
197
+
static void
198
+
nouveau_pushbuf_bo_unref(struct nouveau_pushbuf_priv *nvpb, int index)
199
+
{
200
+
struct drm_nouveau_gem_pushbuf_bo *pbbo = &nvpb->buffers[index];
201
+
struct nouveau_bo *bo = (void *)(unsigned long)pbbo->user_priv;
202
+
struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
203
+
204
+
if (--nvbo->pending_refcnt)
205
+
return;
206
+
207
+
if (pbbo->presumed.valid == 0) {
208
+
nvbo->domain = pbbo->presumed.domain;
209
+
nvbo->offset = pbbo->presumed.offset;
210
+
}
211
+
212
+
nvbo->pending = NULL;
213
+
nouveau_bo_ref(NULL, &bo);
214
+
215
+
/* we only ever remove from the tail of the pending lists,
216
+
* so this is safe.
217
+
*/
218
+
nvpb->nr_buffers--;
219
+
}
220
+
221
+
int
222
+
nouveau_pushbuf_flush(struct nouveau_channel *chan, unsigned min)
223
+
{
224
+
struct nouveau_device_priv *nvdev = nouveau_device(chan->device);
225
+
struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
226
+
struct nouveau_pushbuf_priv *nvpb = &nvchan->pb;
227
+
struct drm_nouveau_gem_pushbuf req;
228
+
unsigned i;
229
+
int ret;
230
+
231
+
ret = nouveau_pushbuf_submit(chan, NULL, 0, 0);
232
+
if (ret)
233
+
return ret;
234
+
235
+
if (!nvpb->nr_push)
236
+
return 0;
237
+
238
+
req.channel = chan->id;
239
+
req.nr_push = nvpb->nr_push;
240
+
req.push = (uint64_t)(unsigned long)nvpb->push;
241
+
req.nr_buffers = nvpb->nr_buffers;
242
+
req.buffers = (uint64_t)(unsigned long)nvpb->buffers;
243
+
req.nr_relocs = nvpb->nr_relocs;
244
+
req.relocs = (uint64_t)(unsigned long)nvpb->relocs;
245
+
req.suffix0 = nvpb->cal_suffix0;
246
+
req.suffix1 = nvpb->cal_suffix1;
247
+
248
+
do {
249
+
ret = drmCommandWriteRead(nvdev->fd, DRM_NOUVEAU_GEM_PUSHBUF,
250
+
&req, sizeof(req));
251
+
} while (ret == -EAGAIN);
252
+
nvpb->cal_suffix0 = req.suffix0;
253
+
nvpb->cal_suffix1 = req.suffix1;
254
+
nvdev->base.vm_vram_size = req.vram_available;
255
+
nvdev->base.vm_gart_size = req.gart_available;
256
+
257
+
/* Update presumed offset/domain for any buffers that moved.
258
+
* Dereference all buffers on validate list
259
+
*/
260
+
for (i = 0; i < nvpb->nr_relocs; i++) {
261
+
nouveau_pushbuf_bo_unref(nvpb, nvpb->relocs[i].bo_index);
262
+
nouveau_pushbuf_bo_unref(nvpb, nvpb->relocs[i].reloc_bo_index);
263
+
}
264
+
265
+
for (i = 0; i < nvpb->nr_push; i++)
266
+
nouveau_pushbuf_bo_unref(nvpb, nvpb->push[i].bo_index);
267
+
268
+
nvpb->nr_buffers = 0;
269
+
nvpb->nr_relocs = 0;
270
+
nvpb->nr_push = 0;
271
+
272
+
/* Allocate space for next push buffer */
273
+
if (nouveau_pushbuf_space(chan, min))
274
+
assert(0);
275
+
276
+
if (chan->flush_notify)
277
+
chan->flush_notify(chan);
278
+
279
+
nvpb->marker = NULL;
280
+
return ret;
281
+
}
282
+
283
+
int
284
+
nouveau_pushbuf_marker_emit(struct nouveau_channel *chan,
285
+
unsigned wait_dwords, unsigned wait_relocs)
286
+
{
287
+
struct nouveau_pushbuf_priv *nvpb = &nouveau_channel(chan)->pb;
288
+
289
+
if (AVAIL_RING(chan) < wait_dwords)
290
+
return nouveau_pushbuf_flush(chan, wait_dwords);
291
+
292
+
if (nvpb->nr_relocs + wait_relocs >= NOUVEAU_GEM_MAX_RELOCS)
293
+
return nouveau_pushbuf_flush(chan, wait_dwords);
294
+
295
+
nvpb->marker = chan->cur;
296
+
nvpb->marker_offset = nvpb->current_offset;
297
+
nvpb->marker_push = nvpb->nr_push;
298
+
nvpb->marker_relocs = nvpb->nr_relocs;
299
+
return 0;
300
+
}
301
+
302
+
void
303
+
nouveau_pushbuf_marker_undo(struct nouveau_channel *chan)
304
+
{
305
+
struct nouveau_pushbuf_priv *nvpb = &nouveau_channel(chan)->pb;
306
+
unsigned i;
307
+
308
+
if (!nvpb->marker)
309
+
return;
310
+
311
+
/* undo any relocs/buffers added to the list since last marker */
312
+
for (i = nvpb->marker_relocs; i < nvpb->nr_relocs; i++) {
313
+
nouveau_pushbuf_bo_unref(nvpb, nvpb->relocs[i].bo_index);
314
+
nouveau_pushbuf_bo_unref(nvpb, nvpb->relocs[i].reloc_bo_index);
315
+
}
316
+
nvpb->nr_relocs = nvpb->marker_relocs;
317
+
318
+
for (i = nvpb->marker_push; i < nvpb->nr_push; i++)
319
+
nouveau_pushbuf_bo_unref(nvpb, nvpb->push[i].bo_index);
320
+
nvpb->nr_push = nvpb->marker_push;
321
+
322
+
/* reset pushbuf back to last marker */
323
+
chan->cur = nvpb->marker;
324
+
nvpb->current_offset = nvpb->marker_offset;
325
+
nvpb->marker = NULL;
326
+
}
327
+
328
+
int
329
+
nouveau_pushbuf_emit_reloc(struct nouveau_channel *chan, void *ptr,
330
+
struct nouveau_bo *bo, uint32_t data, uint32_t data2,
331
+
uint32_t flags, uint32_t vor, uint32_t tor)
332
+
{
333
+
struct nouveau_pushbuf_priv *nvpb = &nouveau_channel(chan)->pb;
334
+
int ret;
335
+
336
+
ret = nouveau_reloc_emit(chan, nvpb->buffer[nvpb->current],
337
+
(char *)ptr - (char *)nvpb->pushbuf, ptr,
338
+
bo, data, data2, flags, vor, tor);
339
+
if (ret)
340
+
return ret;
341
+
342
+
return 0;
343
+
}
344
+