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