6436fea416244b46a1195b339922eb302fb7c8ca
[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 if (bo->map)
48 os_munmap(bo->map, bo->size);
49
50 if (bo->handle) {
51 struct drm_gem_close req = {
52 .handle = bo->handle,
53 };
54
55 if (bo->name)
56 _mesa_hash_table_remove_key(bo->dev->name_table, &bo->name);
57
58 _mesa_hash_table_remove_key(bo->dev->handle_table, &bo->handle);
59 drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
60 }
61
62 free(bo);
63 }
64
65 /* lookup a buffer from it's handle, call w/ etna_drm_table_lock held: */
66 static struct etna_bo *lookup_bo(void *tbl, uint32_t handle)
67 {
68 struct etna_bo *bo = NULL;
69 struct hash_entry *entry = _mesa_hash_table_search(tbl, &handle);
70
71 if (entry) {
72 /* found, incr refcnt and return: */
73 bo = etna_bo_ref(entry->data);
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/ etna_drm_table_lock held */
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 p_atomic_set(&bo->refcnt, 1);
103 list_inithead(&bo->list);
104 /* add ourselves to the handle table: */
105 _mesa_hash_table_insert(dev->handle_table, &bo->handle, bo);
106
107 return bo;
108 }
109
110 /* allocate a new (un-tiled) buffer object */
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(&etna_drm_table_lock);
131 bo = bo_from_handle(dev, size, req.handle, flags);
132 bo->reuse = 1;
133 pthread_mutex_unlock(&etna_drm_table_lock);
134
135 return bo;
136 }
137
138 struct etna_bo *etna_bo_ref(struct etna_bo *bo)
139 {
140 p_atomic_inc(&bo->refcnt);
141
142 return bo;
143 }
144
145 /* get buffer info */
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 */
166 struct etna_bo *etna_bo_from_name(struct etna_device *dev,
167 uint32_t name)
168 {
169 struct etna_bo *bo;
170 struct drm_gem_open req = {
171 .name = name,
172 };
173
174 pthread_mutex_lock(&etna_drm_table_lock);
175
176 /* check name table first, to see if bo is already open: */
177 bo = lookup_bo(dev->name_table, name);
178 if (bo)
179 goto out_unlock;
180
181 if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
182 ERROR_MSG("gem-open failed: %s", strerror(errno));
183 goto out_unlock;
184 }
185
186 bo = lookup_bo(dev->handle_table, req.handle);
187 if (bo)
188 goto out_unlock;
189
190 bo = bo_from_handle(dev, req.size, req.handle, 0);
191 if (bo)
192 set_name(bo, name);
193
194 out_unlock:
195 pthread_mutex_unlock(&etna_drm_table_lock);
196
197 return bo;
198 }
199
200 /* import a buffer from dmabuf fd, does not take ownership of the
201 * fd so caller should close() the fd when it is otherwise done
202 * with it (even if it is still using the 'struct etna_bo *')
203 */
204 struct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd)
205 {
206 struct etna_bo *bo;
207 int ret, size;
208 uint32_t handle;
209
210 /* take the lock before calling drmPrimeFDToHandle to avoid
211 * racing against etna_bo_del, which might invalidate the
212 * returned handle.
213 */
214 pthread_mutex_lock(&etna_drm_table_lock);
215
216 ret = drmPrimeFDToHandle(dev->fd, fd, &handle);
217 if (ret) {
218 pthread_mutex_unlock(&etna_drm_table_lock);
219 return NULL;
220 }
221
222 bo = lookup_bo(dev->handle_table, handle);
223 if (bo)
224 goto out_unlock;
225
226 /* lseek() to get bo size */
227 size = lseek(fd, 0, SEEK_END);
228 lseek(fd, 0, SEEK_CUR);
229
230 bo = bo_from_handle(dev, size, handle, 0);
231
232 out_unlock:
233 pthread_mutex_unlock(&etna_drm_table_lock);
234
235 return bo;
236 }
237
238 /* destroy a buffer object */
239 void etna_bo_del(struct etna_bo *bo)
240 {
241 if (!bo)
242 return;
243
244 struct etna_device *dev = bo->dev;
245
246 if (!p_atomic_dec_zero(&bo->refcnt))
247 return;
248
249 pthread_mutex_lock(&etna_drm_table_lock);
250
251 if (bo->reuse && (etna_bo_cache_free(&dev->bo_cache, bo) == 0))
252 goto out;
253
254 _etna_bo_del(bo);
255 etna_device_del_locked(dev);
256 out:
257 pthread_mutex_unlock(&etna_drm_table_lock);
258 }
259
260 /* get the global flink/DRI2 buffer name */
261 int etna_bo_get_name(struct etna_bo *bo, uint32_t *name)
262 {
263 if (!bo->name) {
264 struct drm_gem_flink req = {
265 .handle = bo->handle,
266 };
267 int ret;
268
269 ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);
270 if (ret) {
271 return ret;
272 }
273
274 pthread_mutex_lock(&etna_drm_table_lock);
275 set_name(bo, req.name);
276 pthread_mutex_unlock(&etna_drm_table_lock);
277 bo->reuse = 0;
278 }
279
280 *name = bo->name;
281
282 return 0;
283 }
284
285 uint32_t etna_bo_handle(struct etna_bo *bo)
286 {
287 return bo->handle;
288 }
289
290 /* caller owns the dmabuf fd that is returned and is responsible
291 * to close() it when done
292 */
293 int etna_bo_dmabuf(struct etna_bo *bo)
294 {
295 int ret, prime_fd;
296
297 ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
298 &prime_fd);
299 if (ret) {
300 ERROR_MSG("failed to get dmabuf fd: %d", ret);
301 return ret;
302 }
303
304 bo->reuse = 0;
305
306 return prime_fd;
307 }
308
309 uint32_t etna_bo_size(struct etna_bo *bo)
310 {
311 return bo->size;
312 }
313
314 void *etna_bo_map(struct etna_bo *bo)
315 {
316 if (!bo->map) {
317 if (!bo->offset) {
318 get_buffer_info(bo);
319 }
320
321 bo->map = os_mmap(0, bo->size, PROT_READ | PROT_WRITE,
322 MAP_SHARED, bo->dev->fd, bo->offset);
323 if (bo->map == MAP_FAILED) {
324 ERROR_MSG("mmap failed: %s", strerror(errno));
325 bo->map = NULL;
326 }
327 }
328
329 return bo->map;
330 }
331
332 int etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op)
333 {
334 struct drm_etnaviv_gem_cpu_prep req = {
335 .handle = bo->handle,
336 .op = op,
337 };
338
339 get_abs_timeout(&req.timeout, 5000000000);
340
341 return drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_PREP,
342 &req, sizeof(req));
343 }
344
345 void etna_bo_cpu_fini(struct etna_bo *bo)
346 {
347 struct drm_etnaviv_gem_cpu_fini req = {
348 .handle = bo->handle,
349 };
350
351 drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_FINI,
352 &req, sizeof(req));
353 }