1 /*
2 * Copyright (C) 2014 Etnaviv 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 (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
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
21 * SOFTWARE.
22 *
23 * Authors:
24 * Christian Gmeiner <christian.gmeiner@gmail.com>
25 */
26
27 #ifdef HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30
31 #include "etnaviv_priv.h"
32 #include "etnaviv_drmif.h"
33
34 drm_private pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
35 drm_private void bo_del(struct etna_bo *bo);
36
37 /* set buffer name, and add to table, call w/ table_lock held: */
set_name(struct etna_bo * bo,uint32_t name)38 static void set_name(struct etna_bo *bo, uint32_t name)
39 {
40 bo->name = name;
41 /* add ourself into the name table: */
42 drmHashInsert(bo->dev->name_table, name, bo);
43 }
44
45 /* Called under table_lock */
bo_del(struct etna_bo * bo)46 drm_private void bo_del(struct etna_bo *bo)
47 {
48 if (bo->map)
49 drm_munmap(bo->map, bo->size);
50
51 if (bo->name)
52 drmHashDelete(bo->dev->name_table, bo->name);
53
54 if (bo->handle) {
55 struct drm_gem_close req = {
56 .handle = bo->handle,
57 };
58
59 drmHashDelete(bo->dev->handle_table, bo->handle);
60 drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
61 }
62
63 free(bo);
64 }
65
66 /* lookup a buffer from it's handle, call w/ table_lock held: */
lookup_bo(void * tbl,uint32_t handle)67 static struct etna_bo *lookup_bo(void *tbl, uint32_t handle)
68 {
69 struct etna_bo *bo = NULL;
70
71 if (!drmHashLookup(tbl, handle, (void **)&bo)) {
72 /* found, incr refcnt and return: */
73 bo = etna_bo_ref(bo);
74
75 /* don't break the bucket if this bo was found in one */
76 list_delinit(&bo->list);
77 }
78
79 return bo;
80 }
81
82 /* allocate a new buffer object, call w/ table_lock held */
bo_from_handle(struct etna_device * dev,uint32_t size,uint32_t handle,uint32_t flags)83 static struct etna_bo *bo_from_handle(struct etna_device *dev,
84 uint32_t size, uint32_t handle, uint32_t flags)
85 {
86 struct etna_bo *bo = calloc(sizeof(*bo), 1);
87
88 if (!bo) {
89 struct drm_gem_close req = {
90 .handle = handle,
91 };
92
93 drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
94
95 return NULL;
96 }
97
98 bo->dev = etna_device_ref(dev);
99 bo->size = size;
100 bo->handle = handle;
101 bo->flags = flags;
102 atomic_set(&bo->refcnt, 1);
103 list_inithead(&bo->list);
104 /* add ourselves to the handle table: */
105 drmHashInsert(dev->handle_table, handle, bo);
106
107 return bo;
108 }
109
110 /* allocate a new (un-tiled) buffer object */
etna_bo_new(struct etna_device * dev,uint32_t size,uint32_t flags)111 struct etna_bo *etna_bo_new(struct etna_device *dev, uint32_t size,
112 uint32_t flags)
113 {
114 struct etna_bo *bo;
115 int ret;
116 struct drm_etnaviv_gem_new req = {
117 .flags = flags,
118 };
119
120 bo = etna_bo_cache_alloc(&dev->bo_cache, &size, flags);
121 if (bo)
122 return bo;
123
124 req.size = size;
125 ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_GEM_NEW,
126 &req, sizeof(req));
127 if (ret)
128 return NULL;
129
130 pthread_mutex_lock(&table_lock);
131 bo = bo_from_handle(dev, size, req.handle, flags);
132 bo->reuse = 1;
133 pthread_mutex_unlock(&table_lock);
134
135 return bo;
136 }
137
etna_bo_ref(struct etna_bo * bo)138 struct etna_bo *etna_bo_ref(struct etna_bo *bo)
139 {
140 atomic_inc(&bo->refcnt);
141
142 return bo;
143 }
144
145 /* get buffer info */
get_buffer_info(struct etna_bo * bo)146 static int get_buffer_info(struct etna_bo *bo)
147 {
148 int ret;
149 struct drm_etnaviv_gem_info req = {
150 .handle = bo->handle,
151 };
152
153 ret = drmCommandWriteRead(bo->dev->fd, DRM_ETNAVIV_GEM_INFO,
154 &req, sizeof(req));
155 if (ret) {
156 return ret;
157 }
158
159 /* really all we need for now is mmap offset */
160 bo->offset = req.offset;
161
162 return 0;
163 }
164
165 /* import a buffer object from DRI2 name */
etna_bo_from_name(struct etna_device * dev,uint32_t name)166 struct etna_bo *etna_bo_from_name(struct etna_device *dev, uint32_t name)
167 {
168 struct etna_bo *bo;
169 struct drm_gem_open req = {
170 .name = name,
171 };
172
173 pthread_mutex_lock(&table_lock);
174
175 /* check name table first, to see if bo is already open: */
176 bo = lookup_bo(dev->name_table, req.handle);
177 if (bo)
178 goto out_unlock;
179
180 if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
181 ERROR_MSG("gem-open failed: %s", strerror(errno));
182 goto out_unlock;
183 }
184
185 bo = lookup_bo(dev->handle_table, req.handle);
186 if (bo)
187 goto out_unlock;
188
189 bo = bo_from_handle(dev, req.size, req.handle, 0);
190 if (bo)
191 set_name(bo, name);
192
193 out_unlock:
194 pthread_mutex_unlock(&table_lock);
195
196 return bo;
197 }
198
199 /* import a buffer from dmabuf fd, does not take ownership of the
200 * fd so caller should close() the fd when it is otherwise done
201 * with it (even if it is still using the 'struct etna_bo *')
202 */
etna_bo_from_dmabuf(struct etna_device * dev,int fd)203 struct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd)
204 {
205 struct etna_bo *bo;
206 int ret, size;
207 uint32_t handle;
208
209 pthread_mutex_lock(&table_lock);
210
211 ret = drmPrimeFDToHandle(dev->fd, fd, &handle);
212 if (ret) {
213 return NULL;
214 }
215
216 bo = lookup_bo(dev->handle_table, handle);
217 if (bo)
218 goto out_unlock;
219
220 /* lseek() to get bo size */
221 size = lseek(fd, 0, SEEK_END);
222 lseek(fd, 0, SEEK_CUR);
223
224 bo = bo_from_handle(dev, size, handle, 0);
225
226 out_unlock:
227 pthread_mutex_unlock(&table_lock);
228
229 return bo;
230 }
231
232 /* destroy a buffer object */
etna_bo_del(struct etna_bo * bo)233 void etna_bo_del(struct etna_bo *bo)
234 {
235 struct etna_device *dev = bo->dev;
236
237 if (!bo)
238 return;
239
240 if (!atomic_dec_and_test(&bo->refcnt))
241 return;
242
243 pthread_mutex_lock(&table_lock);
244
245 if (bo->reuse && (etna_bo_cache_free(&dev->bo_cache, bo) == 0))
246 goto out;
247
248 bo_del(bo);
249 etna_device_del_locked(dev);
250 out:
251 pthread_mutex_unlock(&table_lock);
252 }
253
254 /* get the global flink/DRI2 buffer name */
etna_bo_get_name(struct etna_bo * bo,uint32_t * name)255 int etna_bo_get_name(struct etna_bo *bo, uint32_t *name)
256 {
257 if (!bo->name) {
258 struct drm_gem_flink req = {
259 .handle = bo->handle,
260 };
261 int ret;
262
263 ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);
264 if (ret) {
265 return ret;
266 }
267
268 pthread_mutex_lock(&table_lock);
269 set_name(bo, req.name);
270 pthread_mutex_unlock(&table_lock);
271 bo->reuse = 0;
272 }
273
274 *name = bo->name;
275
276 return 0;
277 }
278
etna_bo_handle(struct etna_bo * bo)279 uint32_t etna_bo_handle(struct etna_bo *bo)
280 {
281 return bo->handle;
282 }
283
284 /* caller owns the dmabuf fd that is returned and is responsible
285 * to close() it when done
286 */
etna_bo_dmabuf(struct etna_bo * bo)287 int etna_bo_dmabuf(struct etna_bo *bo)
288 {
289 int ret, prime_fd;
290
291 ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
292 &prime_fd);
293 if (ret) {
294 ERROR_MSG("failed to get dmabuf fd: %d", ret);
295 return ret;
296 }
297
298 bo->reuse = 0;
299
300 return prime_fd;
301 }
302
etna_bo_size(struct etna_bo * bo)303 uint32_t etna_bo_size(struct etna_bo *bo)
304 {
305 return bo->size;
306 }
307
etna_bo_map(struct etna_bo * bo)308 void *etna_bo_map(struct etna_bo *bo)
309 {
310 if (!bo->map) {
311 if (!bo->offset) {
312 get_buffer_info(bo);
313 }
314
315 bo->map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE,
316 MAP_SHARED, bo->dev->fd, bo->offset);
317 if (bo->map == MAP_FAILED) {
318 ERROR_MSG("mmap failed: %s", strerror(errno));
319 bo->map = NULL;
320 }
321 }
322
323 return bo->map;
324 }
325
etna_bo_cpu_prep(struct etna_bo * bo,uint32_t op)326 int etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op)
327 {
328 struct drm_etnaviv_gem_cpu_prep req = {
329 .handle = bo->handle,
330 .op = op,
331 };
332
333 get_abs_timeout(&req.timeout, 5000000000);
334
335 return drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_PREP,
336 &req, sizeof(req));
337 }
338
etna_bo_cpu_fini(struct etna_bo * bo)339 void etna_bo_cpu_fini(struct etna_bo *bo)
340 {
341 struct drm_etnaviv_gem_cpu_fini req = {
342 .handle = bo->handle,
343 };
344
345 drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_FINI,
346 &req, sizeof(req));
347 }
348