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