bo: change api for shared objects
[nouveau] / src / nouveau_bo.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 <stdint.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <assert.h>
27
28 #include <sys/mman.h>
29 #include <sys/ioctl.h>
30
31 #include "nouveau_drmif.h"
32 #include "nouveau_local.h"
33
34 int
35 nouveau_bo_init(struct nouveau_device *dev)
36 {
37         return 0;
38 }
39
40 void
41 nouveau_bo_takedown(struct nouveau_device *dev)
42 {
43 }
44
45 static int
46 nouveau_bo_allocated(struct nouveau_bo_priv *nvbo)
47 {
48         if (nvbo->sysmem || nvbo->handle)
49                 return 1;
50         return 0;
51 }
52
53 static int
54 nouveau_bo_ualloc(struct nouveau_bo_priv *nvbo)
55 {
56         if (nvbo->user || nvbo->sysmem) {
57                 assert(nvbo->sysmem);
58                 return 0;
59         }
60
61         nvbo->sysmem = malloc(nvbo->size);
62         if (!nvbo->sysmem)
63                 return -ENOMEM;
64
65         return 0;
66 }
67
68 static void
69 nouveau_bo_ufree(struct nouveau_bo_priv *nvbo)
70 {
71         if (nvbo->sysmem) {
72                 if (!nvbo->user)
73                         free(nvbo->sysmem);
74                 nvbo->sysmem = NULL;
75         }
76 }
77
78 static void
79 nouveau_bo_kfree(struct nouveau_bo_priv *nvbo)
80 {
81         struct nouveau_device_priv *nvdev = nouveau_device(nvbo->base.device);
82         struct drm_gem_close req;
83
84         if (!nvbo->handle)
85                 return;
86
87         if (nvbo->map) {
88                 munmap(nvbo->map, nvbo->size);
89                 nvbo->map = NULL;
90         }
91
92         req.handle = nvbo->handle;
93         nvbo->handle = 0;
94         ioctl(nvdev->fd, DRM_IOCTL_GEM_CLOSE, &req);
95 }
96
97 static int
98 nouveau_bo_kalloc(struct nouveau_bo_priv *nvbo)
99 {
100         struct nouveau_device_priv *nvdev = nouveau_device(nvbo->base.device);
101         struct drm_nouveau_gem_new req;
102         int ret;
103
104         if (nvbo->handle)
105                 return 0;
106
107         req.size = nvbo->size;
108         req.align = nvbo->align;
109
110         req.domain = 0;
111
112         if (nvbo->flags & NOUVEAU_BO_VRAM)
113                 req.domain |= NOUVEAU_GEM_DOMAIN_VRAM;
114
115         if (nvbo->flags & NOUVEAU_BO_GART)
116                 req.domain |= NOUVEAU_GEM_DOMAIN_GART;
117
118         if (nvbo->flags & NOUVEAU_BO_TILED) {
119                 req.domain |= NOUVEAU_GEM_DOMAIN_TILE;
120                 if (nvbo->flags & NOUVEAU_BO_ZTILE)
121                         req.domain |= NOUVEAU_GEM_DOMAIN_TILE_ZETA;
122         }
123
124         if (!req.domain) {
125                 req.domain |= (NOUVEAU_GEM_DOMAIN_VRAM |
126                                NOUVEAU_GEM_DOMAIN_GART);
127         }
128
129         ret = drmCommandWriteRead(nvdev->fd, DRM_NOUVEAU_GEM_NEW,
130                                   &req, sizeof(req));
131         if (ret)
132                 return ret;
133         nvbo->handle = req.handle;
134         nvbo->size = req.size;
135         nvbo->domain = req.domain;
136
137         return 0;
138 }
139
140 static int
141 nouveau_bo_kmap(struct nouveau_bo_priv *nvbo)
142 {
143         struct nouveau_device_priv *nvdev = nouveau_device(nvbo->base.device);
144         struct drm_nouveau_gem_mmap req;
145         int ret;
146
147         if (nvbo->map)
148                 return 0;
149
150         if (!nvbo->handle)
151                 return -EINVAL;
152
153         req.handle = nvbo->handle;
154         ret = drmCommandWriteRead(nvdev->fd, DRM_NOUVEAU_GEM_MMAP,
155                                   &req, sizeof(req));
156         if (ret)
157                 return ret;
158
159         nvbo->map = (void *)(unsigned long)req.vaddr;
160         return 0;
161 }
162
163 int
164 nouveau_bo_new(struct nouveau_device *dev, uint32_t flags, int align,
165                int size, struct nouveau_bo **bo)
166 {
167         struct nouveau_bo_priv *nvbo;
168         int ret;
169
170         if (!dev || !bo || *bo)
171                 return -EINVAL;
172
173         nvbo = calloc(1, sizeof(struct nouveau_bo_priv));
174         if (!nvbo)
175                 return -ENOMEM;
176         nvbo->base.device = dev;
177         nvbo->base.size = size;
178         nvbo->base.handle = bo_to_ptr(nvbo);
179
180         nvbo->refcount = 1;
181         nvbo->flags = flags;
182         nvbo->size = size;
183         nvbo->align = align;
184
185         /*XXX: murder me violently */
186         if (flags & NOUVEAU_BO_TILED) {
187                 nvbo->base.tiled = 1;
188                 if (flags & NOUVEAU_BO_ZTILE)
189                         nvbo->base.tiled |= 2;
190         }
191
192         if (flags & NOUVEAU_BO_PIN) {
193                 ret = nouveau_bo_pin((void *)nvbo, nvbo->flags);
194                 if (ret) {
195                         nouveau_bo_ref(NULL, (void *)nvbo);
196                         return ret;
197                 }
198         }
199
200         *bo = &nvbo->base;
201         return 0;
202 }
203
204 int
205 nouveau_bo_user(struct nouveau_device *dev, void *ptr, int size,
206                 struct nouveau_bo **bo)
207 {
208         struct nouveau_bo_priv *nvbo;
209         int ret;
210
211         ret = nouveau_bo_new(dev, 0, 0, size, bo);
212         if (ret)
213                 return ret;
214         nvbo = nouveau_bo(*bo);
215
216         nvbo->sysmem = ptr;
217         nvbo->user = 1;
218         return 0;
219 }
220
221 static void
222 nouveau_bo_del_cb(void *priv)
223 {
224         struct nouveau_bo_priv *nvbo = priv;
225
226         nouveau_fence_ref(NULL, &nvbo->fence);
227         nouveau_bo_ufree(nvbo);
228         nouveau_bo_kfree(nvbo);
229         free(nvbo);
230 }
231
232 int
233 nouveau_bo_handle_get(struct nouveau_bo *bo, uint32_t *handle)
234 {
235         struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
236         int ret;
237  
238         if (!bo || !handle)
239                 return -EINVAL;
240  
241         if (!nvbo->global_handle) {
242                 struct nouveau_device_priv *nvdev = nouveau_device(bo->device);
243                 struct drm_gem_flink req;
244  
245                 ret = nouveau_bo_kalloc(nvbo);
246                 if (ret)
247                         return ret;
248  
249                 req.handle = nvbo->handle;
250                 ret = ioctl(nvdev->fd, DRM_IOCTL_GEM_FLINK, &req);
251                 if (ret) {
252                         nouveau_bo_kfree(nvbo);
253                         return ret;
254                 }
255  
256                 nvbo->global_handle = req.name;
257         }
258  
259         *handle = nvbo->global_handle;
260         return 0;
261 }
262  
263 int
264 nouveau_bo_handle_ref(struct nouveau_device *dev, uint32_t handle,
265                       struct nouveau_bo **bo)
266 {
267         struct nouveau_device_priv *nvdev = nouveau_device(dev);
268         struct nouveau_bo_priv *nvbo;
269         struct drm_gem_open req;
270         int ret;
271  
272         ret = nouveau_bo_new(dev, 0, 0, 0, bo);
273         if (ret)
274                 return ret;
275         nvbo = nouveau_bo(*bo);
276  
277         req.name = handle;
278         ret = ioctl(nvdev->fd, DRM_IOCTL_GEM_OPEN, &req);
279         if (ret) {
280                 nouveau_bo_ref(NULL, bo);
281                 return ret;
282         }
283  
284         nvbo->size = req.size;
285         nvbo->handle = req.handle;
286         return 0;
287
288
289 static void
290 nouveau_bo_del(struct nouveau_bo **bo)
291 {
292         struct nouveau_bo_priv *nvbo;
293
294         if (!bo || !*bo)
295                 return;
296         nvbo = nouveau_bo(*bo);
297         *bo = NULL;
298
299         if (--nvbo->refcount)
300                 return;
301
302         if (nvbo->pending)
303                 nouveau_pushbuf_flush(nvbo->pending_channel, 0);
304
305         if (nvbo->fence)
306                 nouveau_fence_signal_cb(nvbo->fence, nouveau_bo_del_cb, nvbo);
307         else
308                 nouveau_bo_del_cb(nvbo);
309 }
310
311 int
312 nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pbo)
313 {
314         if (!pbo)
315                 return -EINVAL;
316
317         if (ref)
318                 nouveau_bo(ref)->refcount++;
319
320         if (*pbo)
321                 nouveau_bo_del(pbo);
322
323         *pbo = ref;
324         return 0;
325 }
326
327 int
328 nouveau_bo_map(struct nouveau_bo *bo, uint32_t flags)
329 {
330         struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
331         int ret;
332
333         if (!nvbo || bo->map)
334                 return -EINVAL;
335
336         if (!nouveau_bo_allocated(nvbo)) {
337                 if (nvbo->flags & (NOUVEAU_BO_VRAM | NOUVEAU_BO_GART))
338                         nouveau_bo_kalloc(nvbo);
339
340                 if (!nouveau_bo_allocated(nvbo)) {
341                         ret = nouveau_bo_ualloc(nvbo);
342                         if (ret)
343                                 return ret;
344                 }
345         }
346
347         if (nvbo->sysmem) {
348                 bo->map = nvbo->sysmem;
349         } else {
350                 if (nvbo->pending &&
351                     (nvbo->pending->write_domains || flags & NOUVEAU_BO_WR)) {
352                         nouveau_pushbuf_flush(nvbo->pending_channel, 0);
353                 }
354
355                 nouveau_bo_kmap(nvbo);
356
357                 if (flags & NOUVEAU_BO_WR)
358                         nouveau_fence_wait(&nvbo->fence);
359                 else
360                         nouveau_fence_wait(&nvbo->wr_fence);
361
362                 bo->map = nvbo->map;
363         }
364
365         return 0;
366 }
367
368 void
369 nouveau_bo_unmap(struct nouveau_bo *bo)
370 {
371         bo->map = NULL;
372 }
373
374 uint64_t
375 nouveau_bo_get_drm_map(struct nouveau_bo *bo)
376 {
377         NOUVEAU_ERR("-EINVAL :)\n");
378         return 0;
379 }
380
381 int
382 nouveau_bo_pin(struct nouveau_bo *bo, uint32_t flags)
383 {
384         struct nouveau_device_priv *nvdev = nouveau_device(bo->device);
385         struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
386         struct drm_nouveau_gem_pin req;
387         int ret;
388
389         if (nvbo->pinned)
390                 return 0;
391
392         /* Ensure we have a kernel object... */
393         if (!nvbo->handle) {
394                 if (!(flags & (NOUVEAU_BO_VRAM | NOUVEAU_BO_GART)))
395                         return -EINVAL;
396                 nvbo->flags = flags;
397
398                 ret = nouveau_bo_kalloc(nvbo);
399                 if (ret)
400                         return ret;
401         }
402
403         /* Now force it to stay put :) */
404         req.handle = nvbo->handle;
405         req.domain = 0;
406         if (nvbo->flags & NOUVEAU_BO_VRAM)
407                 req.domain |= NOUVEAU_GEM_DOMAIN_VRAM;
408         if (nvbo->flags & NOUVEAU_BO_GART)
409                 req.domain |= NOUVEAU_GEM_DOMAIN_GART;
410
411         ret = drmCommandWriteRead(nvdev->fd, DRM_NOUVEAU_GEM_PIN, &req,
412                                   sizeof(struct drm_nouveau_gem_pin));
413         if (ret)
414                 return ret;
415         nvbo->offset = req.offset;
416         nvbo->domain = req.domain;
417         nvbo->pinned = 1;
418
419         /* Fill in public nouveau_bo members */
420         if (nvbo->domain & NOUVEAU_GEM_DOMAIN_VRAM)
421                 bo->flags = NOUVEAU_BO_VRAM;
422         if (nvbo->domain & NOUVEAU_GEM_DOMAIN_GART)
423                 bo->flags = NOUVEAU_BO_GART;
424         bo->offset = nvbo->offset;
425
426         return 0;
427 }
428
429 void
430 nouveau_bo_unpin(struct nouveau_bo *bo)
431 {
432         struct nouveau_device_priv *nvdev = nouveau_device(bo->device);
433         struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
434         struct drm_nouveau_gem_unpin req;
435
436         if (!nvbo->pinned)
437                 return;
438
439         req.handle = nvbo->handle;
440         drmCommandWrite(nvdev->fd, DRM_NOUVEAU_GEM_UNPIN, &req, sizeof(req));
441
442         nvbo->pinned = bo->offset = bo->flags = 0;
443 }
444
445 struct drm_nouveau_gem_pushbuf_bo *
446 nouveau_bo_emit_buffer(struct nouveau_channel *chan, struct nouveau_bo *bo)
447 {
448         struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(chan->pushbuf);
449         struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
450         struct drm_nouveau_gem_pushbuf_bo *pbbo;
451         struct nouveau_bo *ref = NULL;
452         int ret;
453
454         if (nvbo->pending)
455                 return nvbo->pending;
456
457         if (!nvbo->handle) {
458                 nouveau_bo_kalloc(nvbo);
459                 if (nvbo->sysmem) {
460                         void *sysmem_tmp = nvbo->sysmem;
461
462                         nvbo->sysmem = NULL;
463                         ret = nouveau_bo_map(bo, NOUVEAU_BO_WR);
464                         if (ret)
465                                 return NULL;
466                         nvbo->sysmem = sysmem_tmp;
467
468                         memcpy(bo->map, nvbo->sysmem, nvbo->size);
469                         nouveau_bo_unmap(bo);
470                         nouveau_bo_ufree(nvbo);
471                 }
472         }
473
474         if (nvpb->nr_buffers >= NOUVEAU_PUSHBUF_MAX_BUFFERS)
475                 return NULL;
476         pbbo = nvpb->buffers + nvpb->nr_buffers++;
477         nvbo->pending = pbbo;
478         nvbo->pending_channel = chan;
479
480         nouveau_bo_ref(bo, &ref);
481         pbbo->user_priv = (uint64_t)(unsigned long)ref;
482         pbbo->handle = nvbo->handle;
483         pbbo->valid_domains = NOUVEAU_GEM_DOMAIN_VRAM | NOUVEAU_GEM_DOMAIN_GART;
484         pbbo->read_domains =
485         pbbo->write_domains = 0;
486         pbbo->presumed_domain = nvbo->domain;
487         pbbo->presumed_offset = nvbo->offset;
488         pbbo->presumed_ok = 1;
489         return pbbo;
490 }