2 * Copyright (C) 2014 Etnaviv Project
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:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * Christian Gmeiner <christian.gmeiner@gmail.com>
27 #include "os/os_mman.h"
28 #include "util/hash_table.h"
30 #include "etnaviv_priv.h"
31 #include "etnaviv_drmif.h"
33 pthread_mutex_t etna_drm_table_lock
= PTHREAD_MUTEX_INITIALIZER
;
34 void _etna_bo_del(struct etna_bo
*bo
);
36 /* set buffer name, and add to table, call w/ etna_drm_table_lock held: */
37 static void set_name(struct etna_bo
*bo
, uint32_t name
)
40 /* add ourself into the name table: */
41 _mesa_hash_table_insert(bo
->dev
->name_table
, &bo
->name
, bo
);
44 /* Called under etna_drm_table_lock */
45 void _etna_bo_del(struct etna_bo
*bo
)
50 util_vma_heap_free(&bo
->dev
->address_space
, bo
->va
, bo
->size
);
53 os_munmap(bo
->map
, bo
->size
);
56 struct drm_gem_close req
= {
61 _mesa_hash_table_remove_key(bo
->dev
->name_table
, &bo
->name
);
63 _mesa_hash_table_remove_key(bo
->dev
->handle_table
, &bo
->handle
);
64 drmIoctl(bo
->dev
->fd
, DRM_IOCTL_GEM_CLOSE
, &req
);
70 /* lookup a buffer from it's handle, call w/ etna_drm_table_lock held: */
71 static struct etna_bo
*lookup_bo(void *tbl
, uint32_t handle
)
73 struct etna_bo
*bo
= NULL
;
74 struct hash_entry
*entry
= _mesa_hash_table_search(tbl
, &handle
);
77 /* found, incr refcnt and return: */
78 bo
= etna_bo_ref(entry
->data
);
80 /* don't break the bucket if this bo was found in one */
81 list_delinit(&bo
->list
);
87 /* allocate a new buffer object, call w/ etna_drm_table_lock held */
88 static struct etna_bo
*bo_from_handle(struct etna_device
*dev
,
89 uint32_t size
, uint32_t handle
, uint32_t flags
)
91 struct etna_bo
*bo
= calloc(sizeof(*bo
), 1);
94 struct drm_gem_close req
= {
98 drmIoctl(dev
->fd
, DRM_IOCTL_GEM_CLOSE
, &req
);
103 bo
->dev
= etna_device_ref(dev
);
107 p_atomic_set(&bo
->refcnt
, 1);
108 list_inithead(&bo
->list
);
109 /* add ourselves to the handle table: */
110 _mesa_hash_table_insert(dev
->handle_table
, &bo
->handle
, bo
);
112 if (dev
->use_softpin
)
113 bo
->va
= util_vma_heap_alloc(&dev
->address_space
, bo
->size
, 4096);
118 /* allocate a new (un-tiled) buffer object */
119 struct etna_bo
*etna_bo_new(struct etna_device
*dev
, uint32_t size
,
124 struct drm_etnaviv_gem_new req
= {
128 bo
= etna_bo_cache_alloc(&dev
->bo_cache
, &size
, flags
);
133 ret
= drmCommandWriteRead(dev
->fd
, DRM_ETNAVIV_GEM_NEW
,
138 pthread_mutex_lock(&etna_drm_table_lock
);
139 bo
= bo_from_handle(dev
, size
, req
.handle
, flags
);
141 pthread_mutex_unlock(&etna_drm_table_lock
);
148 struct etna_bo
*etna_bo_ref(struct etna_bo
*bo
)
150 p_atomic_inc(&bo
->refcnt
);
155 /* get buffer info */
156 static int get_buffer_info(struct etna_bo
*bo
)
159 struct drm_etnaviv_gem_info req
= {
160 .handle
= bo
->handle
,
163 ret
= drmCommandWriteRead(bo
->dev
->fd
, DRM_ETNAVIV_GEM_INFO
,
169 /* really all we need for now is mmap offset */
170 bo
->offset
= req
.offset
;
175 /* import a buffer object from DRI2 name */
176 struct etna_bo
*etna_bo_from_name(struct etna_device
*dev
,
180 struct drm_gem_open req
= {
184 pthread_mutex_lock(&etna_drm_table_lock
);
186 /* check name table first, to see if bo is already open: */
187 bo
= lookup_bo(dev
->name_table
, name
);
191 if (drmIoctl(dev
->fd
, DRM_IOCTL_GEM_OPEN
, &req
)) {
192 ERROR_MSG("gem-open failed: %s", strerror(errno
));
196 bo
= lookup_bo(dev
->handle_table
, req
.handle
);
200 bo
= bo_from_handle(dev
, req
.size
, req
.handle
, 0);
207 pthread_mutex_unlock(&etna_drm_table_lock
);
212 /* import a buffer from dmabuf fd, does not take ownership of the
213 * fd so caller should close() the fd when it is otherwise done
214 * with it (even if it is still using the 'struct etna_bo *')
216 struct etna_bo
*etna_bo_from_dmabuf(struct etna_device
*dev
, int fd
)
222 /* take the lock before calling drmPrimeFDToHandle to avoid
223 * racing against etna_bo_del, which might invalidate the
226 pthread_mutex_lock(&etna_drm_table_lock
);
228 ret
= drmPrimeFDToHandle(dev
->fd
, fd
, &handle
);
230 pthread_mutex_unlock(&etna_drm_table_lock
);
234 bo
= lookup_bo(dev
->handle_table
, handle
);
238 /* lseek() to get bo size */
239 size
= lseek(fd
, 0, SEEK_END
);
240 lseek(fd
, 0, SEEK_CUR
);
242 bo
= bo_from_handle(dev
, size
, handle
, 0);
247 pthread_mutex_unlock(&etna_drm_table_lock
);
252 /* destroy a buffer object */
253 void etna_bo_del(struct etna_bo
*bo
)
258 struct etna_device
*dev
= bo
->dev
;
260 if (!p_atomic_dec_zero(&bo
->refcnt
))
263 pthread_mutex_lock(&etna_drm_table_lock
);
265 if (bo
->reuse
&& (etna_bo_cache_free(&dev
->bo_cache
, bo
) == 0))
269 etna_device_del_locked(dev
);
271 pthread_mutex_unlock(&etna_drm_table_lock
);
274 /* get the global flink/DRI2 buffer name */
275 int etna_bo_get_name(struct etna_bo
*bo
, uint32_t *name
)
278 struct drm_gem_flink req
= {
279 .handle
= bo
->handle
,
283 ret
= drmIoctl(bo
->dev
->fd
, DRM_IOCTL_GEM_FLINK
, &req
);
288 pthread_mutex_lock(&etna_drm_table_lock
);
289 set_name(bo
, req
.name
);
290 pthread_mutex_unlock(&etna_drm_table_lock
);
299 uint32_t etna_bo_handle(struct etna_bo
*bo
)
304 /* caller owns the dmabuf fd that is returned and is responsible
305 * to close() it when done
307 int etna_bo_dmabuf(struct etna_bo
*bo
)
311 ret
= drmPrimeHandleToFD(bo
->dev
->fd
, bo
->handle
, DRM_CLOEXEC
,
314 ERROR_MSG("failed to get dmabuf fd: %d", ret
);
323 uint32_t etna_bo_size(struct etna_bo
*bo
)
328 uint32_t etna_bo_gpu_va(struct etna_bo
*bo
)
333 void *etna_bo_map(struct etna_bo
*bo
)
340 bo
->map
= os_mmap(0, bo
->size
, PROT_READ
| PROT_WRITE
,
341 MAP_SHARED
, bo
->dev
->fd
, bo
->offset
);
342 if (bo
->map
== MAP_FAILED
) {
343 ERROR_MSG("mmap failed: %s", strerror(errno
));
351 int etna_bo_cpu_prep(struct etna_bo
*bo
, uint32_t op
)
353 struct drm_etnaviv_gem_cpu_prep req
= {
354 .handle
= bo
->handle
,
358 get_abs_timeout(&req
.timeout
, 5000000000);
360 return drmCommandWrite(bo
->dev
->fd
, DRM_ETNAVIV_GEM_CPU_PREP
,
364 void etna_bo_cpu_fini(struct etna_bo
*bo
)
366 struct drm_etnaviv_gem_cpu_fini req
= {
367 .handle
= bo
->handle
,
370 drmCommandWrite(bo
->dev
->fd
, DRM_ETNAVIV_GEM_CPU_FINI
,