drm2: Some functions are named differently
[dragonfly.git] / sys / dev / drm2 / drm_gem.c
1 /*-
2  * Copyright (c) 2011 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Konstantin Belousov under sponsorship from
6  * the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD: src/sys/dev/drm2/drm_gem.c,v 1.1 2012/05/22 11:07:44 kib Exp $
30  */
31
32 #include "opt_vm.h"
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/limits.h>
37 #include <sys/lock.h>
38 #include <sys/mutex.h>
39
40 #include <vm/vm.h>
41 #include <vm/vm_page.h>
42
43 #include <dev/drm2/drmP.h>
44 #include <dev/drm2/drm.h>
45 #include <dev/drm2/drm_sarea.h>
46
47 /*
48  * We make up offsets for buffer objects so we can recognize them at
49  * mmap time.
50  */
51
52 /* pgoff in mmap is an unsigned long, so we need to make sure that
53  * the faked up offset will fit
54  */
55
56 #if ULONG_MAX == UINT64_MAX
57 #define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1)
58 #define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 16)
59 #else
60 #define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFUL >> PAGE_SHIFT) + 1)
61 #define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFUL >> PAGE_SHIFT) * 16)
62 #endif
63
64 int
65 drm_gem_init(struct drm_device *dev)
66 {
67         struct drm_gem_mm *mm;
68
69         drm_gem_names_init(&dev->object_names);
70         mm = kmalloc(sizeof(*mm), DRM_MEM_DRIVER, M_WAITOK);
71         dev->mm_private = mm;
72         if (drm_ht_create(&mm->offset_hash, 19) != 0) {
73                 kfree(mm, DRM_MEM_DRIVER);
74                 return (ENOMEM);
75         }
76         mm->idxunr = new_unrhdr(0, DRM_GEM_MAX_IDX, NULL);
77         return (0);
78 }
79
80 void
81 drm_gem_destroy(struct drm_device *dev)
82 {
83         struct drm_gem_mm *mm;
84
85         mm = dev->mm_private;
86         dev->mm_private = NULL;
87         drm_ht_remove(&mm->offset_hash);
88         delete_unrhdr(mm->idxunr);
89         kfree(mm, DRM_MEM_DRIVER);
90         drm_gem_names_fini(&dev->object_names);
91 }
92
93 int
94 drm_gem_object_init(struct drm_device *dev, struct drm_gem_object *obj,
95     size_t size)
96 {
97
98         KASSERT((size & (PAGE_SIZE - 1)) == 0,
99             ("Bad size %ju", (uintmax_t)size));
100
101         obj->dev = dev;
102         obj->vm_obj = vm_pager_allocate(OBJT_DEFAULT, NULL, size,
103             VM_PROT_READ | VM_PROT_WRITE, 0, curthread->td_ucred);
104
105         obj->refcount = 1;
106         obj->handle_count = 0;
107         obj->size = size;
108
109         return (0);
110 }
111
112 int
113 drm_gem_private_object_init(struct drm_device *dev, struct drm_gem_object *obj,
114     size_t size)
115 {
116
117         MPASS((size & (PAGE_SIZE - 1)) == 0);
118
119         obj->dev = dev;
120         obj->vm_obj = NULL;
121
122         obj->refcount = 1;
123         atomic_set(&obj->handle_count, 0);
124         obj->size = size;
125
126         return (0);
127 }
128
129
130 struct drm_gem_object *
131 drm_gem_object_alloc(struct drm_device *dev, size_t size)
132 {
133         struct drm_gem_object *obj;
134
135         obj = kmalloc(sizeof(*obj), DRM_MEM_DRIVER, M_WAITOK | M_ZERO);
136         if (drm_gem_object_init(dev, obj, size) != 0)
137                 goto free;
138
139         if (dev->driver->gem_init_object != NULL &&
140             dev->driver->gem_init_object(obj) != 0)
141                 goto dealloc;
142         return (obj);
143 dealloc:
144         vm_object_deallocate(obj->vm_obj);
145 free:
146         kfree(obj, DRM_MEM_DRIVER);
147         return (NULL);
148 }
149
150 void
151 drm_gem_object_free(struct drm_gem_object *obj)
152 {
153         struct drm_device *dev;
154
155         dev = obj->dev;
156         DRM_LOCK_ASSERT(dev);
157         if (dev->driver->gem_free_object != NULL)
158                 dev->driver->gem_free_object(obj);
159 }
160
161 void
162 drm_gem_object_reference(struct drm_gem_object *obj)
163 {
164
165         KASSERT(obj->refcount > 0, ("Dandling obj %p", obj));
166         refcount_acquire(&obj->refcount);
167 }
168
169 void
170 drm_gem_object_unreference(struct drm_gem_object *obj)
171 {
172
173         if (obj == NULL)
174                 return;
175         if (refcount_release(&obj->refcount))
176                 drm_gem_object_free(obj);
177 }
178
179 void
180 drm_gem_object_unreference_unlocked(struct drm_gem_object *obj)
181 {
182         struct drm_device *dev;
183
184         if (obj == NULL)
185                 return;
186         dev = obj->dev;
187         DRM_LOCK(dev);
188         drm_gem_object_unreference(obj);
189         DRM_UNLOCK(dev);
190 }
191
192 void
193 drm_gem_object_handle_reference(struct drm_gem_object *obj)
194 {
195
196         drm_gem_object_reference(obj);
197         atomic_add_rel_int(&obj->handle_count, 1);
198 }
199
200 void
201 drm_gem_object_handle_free(struct drm_gem_object *obj)
202 {
203         struct drm_device *dev;
204         struct drm_gem_object *obj1;
205
206         dev = obj->dev;
207         if (obj->name != 0) {
208                 obj1 = drm_gem_names_remove(&dev->object_names, obj->name);
209                 obj->name = 0;
210                 drm_gem_object_unreference(obj1);
211         }
212 }
213
214 void
215 drm_gem_object_handle_unreference(struct drm_gem_object *obj)
216 {
217
218         if (obj == NULL ||
219             atomic_load_acq_int(&obj->handle_count) == 0)
220                 return;
221
222         if (atomic_fetchadd_int(&obj->handle_count, -1) == 1)
223                 drm_gem_object_handle_free(obj);
224         drm_gem_object_unreference(obj);
225 }
226
227 void
228 drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj)
229 {
230
231         if (obj == NULL ||
232             atomic_load_acq_int(&obj->handle_count) == 0)
233                 return;
234
235         if (atomic_fetchadd_int(&obj->handle_count, -1) == 1)
236                 drm_gem_object_handle_free(obj);
237         drm_gem_object_unreference_unlocked(obj);
238 }
239
240 int
241 drm_gem_handle_create(struct drm_file *file_priv, struct drm_gem_object *obj,
242     uint32_t *handle)
243 {
244         int error;
245
246         error = drm_gem_name_create(&file_priv->object_names, obj, handle);
247         if (error != 0)
248                 return (error);
249         drm_gem_object_handle_reference(obj);
250         return (0);
251 }
252
253 int
254 drm_gem_handle_delete(struct drm_file *file_priv, uint32_t handle)
255 {
256         struct drm_gem_object *obj;
257
258         obj = drm_gem_names_remove(&file_priv->object_names, handle);
259         if (obj == NULL)
260                 return (EINVAL);
261         drm_gem_object_handle_unreference_unlocked(obj);
262         return (0);
263 }
264
265 void
266 drm_gem_object_release(struct drm_gem_object *obj)
267 {
268
269         /*
270          * obj->vm_obj can be NULL for private gem objects.
271          */
272         vm_object_deallocate(obj->vm_obj);
273 }
274
275 int
276 drm_gem_open_ioctl(struct drm_device *dev, void *data,
277     struct drm_file *file_priv)
278 {
279         struct drm_gem_open *args;
280         struct drm_gem_object *obj;
281         int ret;
282         uint32_t handle;
283
284         if (!drm_core_check_feature(dev, DRIVER_GEM))
285                 return (ENODEV);
286         args = data;
287
288         obj = drm_gem_name_ref(&dev->object_names, args->name,
289             (void (*)(void *))drm_gem_object_reference);
290         if (obj == NULL)
291                 return (ENOENT);
292         handle = 0;
293         ret = drm_gem_handle_create(file_priv, obj, &handle);
294         drm_gem_object_unreference_unlocked(obj);
295         if (ret != 0)
296                 return (ret);
297         
298         args->handle = handle;
299         args->size = obj->size;
300
301         return (0);
302 }
303
304 void
305 drm_gem_open(struct drm_device *dev, struct drm_file *file_priv)
306 {
307
308         drm_gem_names_init(&file_priv->object_names);
309 }
310
311 static int
312 drm_gem_object_release_handle(uint32_t name, void *ptr, void *arg)
313 {
314         struct drm_gem_object *obj;
315
316         obj = ptr;
317         drm_gem_object_handle_unreference(obj);
318         return (0);
319 }
320
321 void
322 drm_gem_release(struct drm_device *dev, struct drm_file *file_priv)
323 {
324
325         drm_gem_names_foreach(&file_priv->object_names,
326             drm_gem_object_release_handle, NULL);
327         drm_gem_names_fini(&file_priv->object_names);
328 }
329
330 int
331 drm_gem_close_ioctl(struct drm_device *dev, void *data,
332     struct drm_file *file_priv)
333 {
334         struct drm_gem_close *args;
335
336         if (!drm_core_check_feature(dev, DRIVER_GEM))
337                 return (ENODEV);
338         args = data;
339
340         return (drm_gem_handle_delete(file_priv, args->handle));
341 }
342
343 int
344 drm_gem_flink_ioctl(struct drm_device *dev, void *data,
345     struct drm_file *file_priv)
346 {
347         struct drm_gem_flink *args;
348         struct drm_gem_object *obj;
349         int error;
350
351         if (!drm_core_check_feature(dev, DRIVER_GEM))
352                 return (ENODEV);
353         args = data;
354
355         obj = drm_gem_name_ref(&file_priv->object_names, args->handle,
356             (void (*)(void *))drm_gem_object_reference);
357         if (obj == NULL)
358                 return (ENOENT);
359         error = drm_gem_name_create(&dev->object_names, obj, &obj->name);
360         if (error != 0) {
361                 if (error == EALREADY)
362                         error = 0;
363                 drm_gem_object_unreference_unlocked(obj);
364         }
365         if (error == 0)
366                 args->name = obj->name;
367         return (error);
368 }
369
370 struct drm_gem_object *
371 drm_gem_object_lookup(struct drm_device *dev, struct drm_file *file_priv,
372     uint32_t handle)
373 {
374         struct drm_gem_object *obj;
375
376         obj = drm_gem_name_ref(&file_priv->object_names, handle,
377             (void (*)(void *))drm_gem_object_reference);
378         return (obj);
379 }
380
381 static struct drm_gem_object *
382 drm_gem_object_from_offset(struct drm_device *dev, vm_ooffset_t offset)
383 {
384         struct drm_gem_object *obj;
385         struct drm_gem_mm *mm;
386         struct drm_hash_item *map_list;
387
388         if ((offset & DRM_GEM_MAPPING_MASK) != DRM_GEM_MAPPING_KEY)
389                 return (NULL);
390         offset &= ~DRM_GEM_MAPPING_KEY;
391         mm = dev->mm_private;
392         if (drm_ht_find_item(&mm->offset_hash, DRM_GEM_MAPPING_IDX(offset),
393             &map_list) != 0) {
394         DRM_DEBUG("drm_gem_object_from_offset: offset 0x%jx obj not found\n",
395                     (uintmax_t)offset);
396                 return (NULL);
397         }
398         obj = member2struct(drm_gem_object, map_list, map_list);
399         return (obj);
400 }
401
402 int
403 drm_gem_create_mmap_offset(struct drm_gem_object *obj)
404 {
405         struct drm_device *dev;
406         struct drm_gem_mm *mm;
407         int ret;
408
409         if (obj->on_map)
410                 return (0);
411         dev = obj->dev;
412         mm = dev->mm_private;
413         ret = 0;
414
415         obj->map_list.key = alloc_unr(mm->idxunr);
416         ret = drm_ht_insert_item(&mm->offset_hash, &obj->map_list);
417         if (ret != 0) {
418                 DRM_ERROR("failed to add to map hash\n");
419                 free_unr(mm->idxunr, obj->map_list.key);
420                 return (ret);
421         }
422         obj->on_map = true;
423         return (0);
424 }
425
426 void
427 drm_gem_free_mmap_offset(struct drm_gem_object *obj)
428 {
429         struct drm_hash_item *list;
430         struct drm_gem_mm *mm;
431
432         if (!obj->on_map)
433                 return;
434         mm = obj->dev->mm_private;
435         list = &obj->map_list;
436
437         drm_ht_remove_item(&mm->offset_hash, list);
438         free_unr(mm->idxunr, list->key);
439         obj->on_map = false;
440 }
441
442 int
443 drm_gem_mmap_single(struct cdev *kdev, vm_ooffset_t *offset, vm_size_t size,
444     struct vm_object **obj_res, int nprot)
445 {
446         struct drm_device *dev;
447         struct drm_gem_object *gem_obj;
448         struct vm_object *vm_obj;
449
450         dev = drm_get_device_from_kdev(kdev);
451         if ((dev->driver->driver_features & DRIVER_GEM) == 0)
452                 return (ENODEV);
453         DRM_LOCK(dev);
454         gem_obj = drm_gem_object_from_offset(dev, *offset);
455         if (gem_obj == NULL) {
456                 DRM_UNLOCK(dev);
457                 return (ENODEV);
458         }
459         drm_gem_object_reference(gem_obj);
460         DRM_UNLOCK(dev);
461         vm_obj = cdev_pager_allocate(gem_obj, OBJT_MGTDEVICE,
462             dev->driver->gem_pager_ops, size, nprot,
463             DRM_GEM_MAPPING_MAPOFF(*offset), curthread->td_ucred);
464         if (vm_obj == NULL) {
465                 drm_gem_object_unreference_unlocked(gem_obj);
466                 return (EINVAL);
467         }
468         *offset = DRM_GEM_MAPPING_MAPOFF(*offset);
469         *obj_res = vm_obj;
470         return (0);
471 }
472
473 void
474 drm_gem_pager_dtr(void *handle)
475 {
476         struct drm_gem_object *obj;
477         struct drm_device *dev;
478
479         obj = handle;
480         dev = obj->dev;
481
482         DRM_LOCK(dev);
483         drm_gem_free_mmap_offset(obj);
484         drm_gem_object_unreference(obj);
485         DRM_UNLOCK(dev);
486 }