etnaviv_bo.c revision 00a23bda
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
34drm_private pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
35drm_private void bo_del(struct etna_bo *bo);
36
37/* set buffer name, and add to table, call w/ table_lock held: */
38static 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 */
46drm_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: */
67static 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 */
83static 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 */
111struct 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
138struct 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 */
146static 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 */
166struct 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, name);
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
193out_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 */
203struct 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	/* take the lock before calling drmPrimeFDToHandle to avoid
210	 * racing against etna_bo_del, which might invalidate the
211	 * returned handle.
212	 */
213	pthread_mutex_lock(&table_lock);
214
215	ret = drmPrimeFDToHandle(dev->fd, fd, &handle);
216	if (ret) {
217		pthread_mutex_unlock(&table_lock);
218		return NULL;
219	}
220
221	bo = lookup_bo(dev->handle_table, handle);
222	if (bo)
223		goto out_unlock;
224
225	/* lseek() to get bo size */
226	size = lseek(fd, 0, SEEK_END);
227	lseek(fd, 0, SEEK_CUR);
228
229	bo = bo_from_handle(dev, size, handle, 0);
230
231out_unlock:
232	pthread_mutex_unlock(&table_lock);
233
234	return bo;
235}
236
237/* destroy a buffer object */
238void etna_bo_del(struct etna_bo *bo)
239{
240	struct etna_device *dev = bo->dev;
241
242	if (!bo)
243		return;
244
245	if (!atomic_dec_and_test(&bo->refcnt))
246		return;
247
248	pthread_mutex_lock(&table_lock);
249
250	if (bo->reuse && (etna_bo_cache_free(&dev->bo_cache, bo) == 0))
251		goto out;
252
253	bo_del(bo);
254	etna_device_del_locked(dev);
255out:
256	pthread_mutex_unlock(&table_lock);
257}
258
259/* get the global flink/DRI2 buffer name */
260int etna_bo_get_name(struct etna_bo *bo, uint32_t *name)
261{
262	if (!bo->name) {
263		struct drm_gem_flink req = {
264			.handle = bo->handle,
265		};
266		int ret;
267
268		ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);
269		if (ret) {
270			return ret;
271		}
272
273		pthread_mutex_lock(&table_lock);
274		set_name(bo, req.name);
275		pthread_mutex_unlock(&table_lock);
276		bo->reuse = 0;
277	}
278
279	*name = bo->name;
280
281	return 0;
282}
283
284uint32_t etna_bo_handle(struct etna_bo *bo)
285{
286	return bo->handle;
287}
288
289/* caller owns the dmabuf fd that is returned and is responsible
290 * to close() it when done
291 */
292int etna_bo_dmabuf(struct etna_bo *bo)
293{
294	int ret, prime_fd;
295
296	ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
297				&prime_fd);
298	if (ret) {
299		ERROR_MSG("failed to get dmabuf fd: %d", ret);
300		return ret;
301	}
302
303	bo->reuse = 0;
304
305	return prime_fd;
306}
307
308uint32_t etna_bo_size(struct etna_bo *bo)
309{
310	return bo->size;
311}
312
313void *etna_bo_map(struct etna_bo *bo)
314{
315	if (!bo->map) {
316		if (!bo->offset) {
317			get_buffer_info(bo);
318		}
319
320		bo->map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE,
321				MAP_SHARED, bo->dev->fd, bo->offset);
322		if (bo->map == MAP_FAILED) {
323			ERROR_MSG("mmap failed: %s", strerror(errno));
324			bo->map = NULL;
325		}
326	}
327
328	return bo->map;
329}
330
331int etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op)
332{
333	struct drm_etnaviv_gem_cpu_prep req = {
334		.handle = bo->handle,
335		.op = op,
336	};
337
338	get_abs_timeout(&req.timeout, 5000000000);
339
340	return drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_PREP,
341			&req, sizeof(req));
342}
343
344void etna_bo_cpu_fini(struct etna_bo *bo)
345{
346	struct drm_etnaviv_gem_cpu_fini req = {
347		.handle = bo->handle,
348	};
349
350	drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_FINI,
351			&req, sizeof(req));
352}
353