etnaviv_bo.c revision d8807b2f
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	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
226out_unlock:
227	pthread_mutex_unlock(&table_lock);
228
229	return bo;
230}
231
232/* destroy a buffer object */
233void 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);
250out:
251	pthread_mutex_unlock(&table_lock);
252}
253
254/* get the global flink/DRI2 buffer name */
255int 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
279uint32_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 */
287int 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
303uint32_t etna_bo_size(struct etna_bo *bo)
304{
305	return bo->size;
306}
307
308void *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
326int 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
339void 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}
348