diff options
Diffstat (limited to 'etnaviv/etnaviv_bo.c')
-rw-r--r-- | etnaviv/etnaviv_bo.c | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/etnaviv/etnaviv_bo.c b/etnaviv/etnaviv_bo.c new file mode 100644 index 00000000..4ad0434e --- /dev/null +++ b/etnaviv/etnaviv_bo.c | |||
@@ -0,0 +1,347 @@ | |||
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: */ | ||
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 */ | ||
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: */ | ||
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 */ | ||
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 */ | ||
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 | |||
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 */ | ||
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, 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 | */ | ||
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 */ | ||
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 */ | ||
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 | |||
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 | */ | ||
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 | |||
303 | uint32_t etna_bo_size(struct etna_bo *bo) | ||
304 | { | ||
305 | return bo->size; | ||
306 | } | ||
307 | |||
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 | |||
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 | |||
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 | } | ||