etnaviv_bo.c revision d8807b2f
1037b3c26Smrg/*
2037b3c26Smrg * Copyright (C) 2014 Etnaviv Project
3037b3c26Smrg *
4037b3c26Smrg * Permission is hereby granted, free of charge, to any person obtaining a
5037b3c26Smrg * copy of this software and associated documentation files (the "Software"),
6037b3c26Smrg * to deal in the Software without restriction, including without limitation
7037b3c26Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8037b3c26Smrg * and/or sell copies of the Software, and to permit persons to whom the
9037b3c26Smrg * Software is furnished to do so, subject to the following conditions:
10037b3c26Smrg *
11037b3c26Smrg * The above copyright notice and this permission notice (including the next
12037b3c26Smrg * paragraph) shall be included in all copies or substantial portions of the
13037b3c26Smrg * Software.
14037b3c26Smrg *
15037b3c26Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16037b3c26Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17037b3c26Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18037b3c26Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19037b3c26Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20037b3c26Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21037b3c26Smrg * SOFTWARE.
22037b3c26Smrg *
23037b3c26Smrg * Authors:
24037b3c26Smrg *    Christian Gmeiner <christian.gmeiner@gmail.com>
25037b3c26Smrg */
26037b3c26Smrg
27037b3c26Smrg#ifdef HAVE_CONFIG_H
28037b3c26Smrg# include <config.h>
29037b3c26Smrg#endif
30037b3c26Smrg
31037b3c26Smrg#include "etnaviv_priv.h"
32037b3c26Smrg#include "etnaviv_drmif.h"
33037b3c26Smrg
34037b3c26Smrgdrm_private pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
35037b3c26Smrgdrm_private void bo_del(struct etna_bo *bo);
36037b3c26Smrg
37037b3c26Smrg/* set buffer name, and add to table, call w/ table_lock held: */
38037b3c26Smrgstatic void set_name(struct etna_bo *bo, uint32_t name)
39037b3c26Smrg{
40037b3c26Smrg	bo->name = name;
41037b3c26Smrg	/* add ourself into the name table: */
42037b3c26Smrg	drmHashInsert(bo->dev->name_table, name, bo);
43037b3c26Smrg}
44037b3c26Smrg
45037b3c26Smrg/* Called under table_lock */
46037b3c26Smrgdrm_private void bo_del(struct etna_bo *bo)
47037b3c26Smrg{
48037b3c26Smrg	if (bo->map)
49037b3c26Smrg		drm_munmap(bo->map, bo->size);
50037b3c26Smrg
51037b3c26Smrg	if (bo->name)
52037b3c26Smrg		drmHashDelete(bo->dev->name_table, bo->name);
53037b3c26Smrg
54037b3c26Smrg	if (bo->handle) {
55037b3c26Smrg		struct drm_gem_close req = {
56037b3c26Smrg			.handle = bo->handle,
57037b3c26Smrg		};
58037b3c26Smrg
59037b3c26Smrg		drmHashDelete(bo->dev->handle_table, bo->handle);
60037b3c26Smrg		drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
61037b3c26Smrg	}
62037b3c26Smrg
63037b3c26Smrg	free(bo);
64037b3c26Smrg}
65037b3c26Smrg
66037b3c26Smrg/* lookup a buffer from it's handle, call w/ table_lock held: */
67037b3c26Smrgstatic struct etna_bo *lookup_bo(void *tbl, uint32_t handle)
68037b3c26Smrg{
69037b3c26Smrg	struct etna_bo *bo = NULL;
70037b3c26Smrg
71037b3c26Smrg	if (!drmHashLookup(tbl, handle, (void **)&bo)) {
72037b3c26Smrg		/* found, incr refcnt and return: */
73037b3c26Smrg		bo = etna_bo_ref(bo);
74037b3c26Smrg
75037b3c26Smrg		/* don't break the bucket if this bo was found in one */
76037b3c26Smrg		list_delinit(&bo->list);
77037b3c26Smrg	}
78037b3c26Smrg
79037b3c26Smrg	return bo;
80037b3c26Smrg}
81037b3c26Smrg
82037b3c26Smrg/* allocate a new buffer object, call w/ table_lock held */
83037b3c26Smrgstatic struct etna_bo *bo_from_handle(struct etna_device *dev,
84037b3c26Smrg		uint32_t size, uint32_t handle, uint32_t flags)
85037b3c26Smrg{
86037b3c26Smrg	struct etna_bo *bo = calloc(sizeof(*bo), 1);
87037b3c26Smrg
88037b3c26Smrg	if (!bo) {
89037b3c26Smrg		struct drm_gem_close req = {
90037b3c26Smrg			.handle = handle,
91037b3c26Smrg		};
92037b3c26Smrg
93037b3c26Smrg		drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
94037b3c26Smrg
95037b3c26Smrg		return NULL;
96037b3c26Smrg	}
97037b3c26Smrg
98037b3c26Smrg	bo->dev = etna_device_ref(dev);
99037b3c26Smrg	bo->size = size;
100037b3c26Smrg	bo->handle = handle;
101037b3c26Smrg	bo->flags = flags;
102037b3c26Smrg	atomic_set(&bo->refcnt, 1);
103037b3c26Smrg	list_inithead(&bo->list);
104037b3c26Smrg	/* add ourselves to the handle table: */
105037b3c26Smrg	drmHashInsert(dev->handle_table, handle, bo);
106037b3c26Smrg
107037b3c26Smrg	return bo;
108037b3c26Smrg}
109037b3c26Smrg
110037b3c26Smrg/* allocate a new (un-tiled) buffer object */
111037b3c26Smrgstruct etna_bo *etna_bo_new(struct etna_device *dev, uint32_t size,
112037b3c26Smrg		uint32_t flags)
113037b3c26Smrg{
114037b3c26Smrg	struct etna_bo *bo;
115037b3c26Smrg	int ret;
116037b3c26Smrg	struct drm_etnaviv_gem_new req = {
117037b3c26Smrg			.flags = flags,
118037b3c26Smrg	};
119037b3c26Smrg
120037b3c26Smrg	bo = etna_bo_cache_alloc(&dev->bo_cache, &size, flags);
121037b3c26Smrg	if (bo)
122037b3c26Smrg		return bo;
123037b3c26Smrg
124037b3c26Smrg	req.size = size;
125037b3c26Smrg	ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_GEM_NEW,
126037b3c26Smrg			&req, sizeof(req));
127037b3c26Smrg	if (ret)
128037b3c26Smrg		return NULL;
129037b3c26Smrg
130037b3c26Smrg	pthread_mutex_lock(&table_lock);
131037b3c26Smrg	bo = bo_from_handle(dev, size, req.handle, flags);
132037b3c26Smrg	bo->reuse = 1;
133037b3c26Smrg	pthread_mutex_unlock(&table_lock);
134037b3c26Smrg
135037b3c26Smrg	return bo;
136037b3c26Smrg}
137037b3c26Smrg
138037b3c26Smrgstruct etna_bo *etna_bo_ref(struct etna_bo *bo)
139037b3c26Smrg{
140037b3c26Smrg	atomic_inc(&bo->refcnt);
141037b3c26Smrg
142037b3c26Smrg	return bo;
143037b3c26Smrg}
144037b3c26Smrg
145037b3c26Smrg/* get buffer info */
146037b3c26Smrgstatic int get_buffer_info(struct etna_bo *bo)
147037b3c26Smrg{
148037b3c26Smrg	int ret;
149037b3c26Smrg	struct drm_etnaviv_gem_info req = {
150037b3c26Smrg		.handle = bo->handle,
151037b3c26Smrg	};
152037b3c26Smrg
153037b3c26Smrg	ret = drmCommandWriteRead(bo->dev->fd, DRM_ETNAVIV_GEM_INFO,
154037b3c26Smrg			&req, sizeof(req));
155037b3c26Smrg	if (ret) {
156037b3c26Smrg		return ret;
157037b3c26Smrg	}
158037b3c26Smrg
159037b3c26Smrg	/* really all we need for now is mmap offset */
160037b3c26Smrg	bo->offset = req.offset;
161037b3c26Smrg
162037b3c26Smrg	return 0;
163037b3c26Smrg}
164037b3c26Smrg
165037b3c26Smrg/* import a buffer object from DRI2 name */
166037b3c26Smrgstruct etna_bo *etna_bo_from_name(struct etna_device *dev, uint32_t name)
167037b3c26Smrg{
168037b3c26Smrg	struct etna_bo *bo;
169037b3c26Smrg	struct drm_gem_open req = {
170037b3c26Smrg		.name = name,
171037b3c26Smrg	};
172037b3c26Smrg
173037b3c26Smrg	pthread_mutex_lock(&table_lock);
174037b3c26Smrg
175037b3c26Smrg	/* check name table first, to see if bo is already open: */
176d8807b2fSmrg	bo = lookup_bo(dev->name_table, name);
177037b3c26Smrg	if (bo)
178037b3c26Smrg		goto out_unlock;
179037b3c26Smrg
180037b3c26Smrg	if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
181037b3c26Smrg		ERROR_MSG("gem-open failed: %s", strerror(errno));
182037b3c26Smrg		goto out_unlock;
183037b3c26Smrg	}
184037b3c26Smrg
185037b3c26Smrg	bo = lookup_bo(dev->handle_table, req.handle);
186037b3c26Smrg	if (bo)
187037b3c26Smrg		goto out_unlock;
188037b3c26Smrg
189037b3c26Smrg	bo = bo_from_handle(dev, req.size, req.handle, 0);
190037b3c26Smrg	if (bo)
191037b3c26Smrg		set_name(bo, name);
192037b3c26Smrg
193037b3c26Smrgout_unlock:
194037b3c26Smrg	pthread_mutex_unlock(&table_lock);
195037b3c26Smrg
196037b3c26Smrg	return bo;
197037b3c26Smrg}
198037b3c26Smrg
199037b3c26Smrg/* import a buffer from dmabuf fd, does not take ownership of the
200037b3c26Smrg * fd so caller should close() the fd when it is otherwise done
201037b3c26Smrg * with it (even if it is still using the 'struct etna_bo *')
202037b3c26Smrg */
203037b3c26Smrgstruct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd)
204037b3c26Smrg{
205037b3c26Smrg	struct etna_bo *bo;
206037b3c26Smrg	int ret, size;
207037b3c26Smrg	uint32_t handle;
208037b3c26Smrg
209037b3c26Smrg	pthread_mutex_lock(&table_lock);
210037b3c26Smrg
211037b3c26Smrg	ret = drmPrimeFDToHandle(dev->fd, fd, &handle);
212037b3c26Smrg	if (ret) {
213037b3c26Smrg		return NULL;
214037b3c26Smrg	}
215037b3c26Smrg
216037b3c26Smrg	bo = lookup_bo(dev->handle_table, handle);
217037b3c26Smrg	if (bo)
218037b3c26Smrg		goto out_unlock;
219037b3c26Smrg
220037b3c26Smrg	/* lseek() to get bo size */
221037b3c26Smrg	size = lseek(fd, 0, SEEK_END);
222037b3c26Smrg	lseek(fd, 0, SEEK_CUR);
223037b3c26Smrg
224037b3c26Smrg	bo = bo_from_handle(dev, size, handle, 0);
225037b3c26Smrg
226037b3c26Smrgout_unlock:
227037b3c26Smrg	pthread_mutex_unlock(&table_lock);
228037b3c26Smrg
229037b3c26Smrg	return bo;
230037b3c26Smrg}
231037b3c26Smrg
232037b3c26Smrg/* destroy a buffer object */
233037b3c26Smrgvoid etna_bo_del(struct etna_bo *bo)
234037b3c26Smrg{
235037b3c26Smrg	struct etna_device *dev = bo->dev;
236037b3c26Smrg
237037b3c26Smrg	if (!bo)
238037b3c26Smrg		return;
239037b3c26Smrg
240037b3c26Smrg	if (!atomic_dec_and_test(&bo->refcnt))
241037b3c26Smrg		return;
242037b3c26Smrg
243037b3c26Smrg	pthread_mutex_lock(&table_lock);
244037b3c26Smrg
245037b3c26Smrg	if (bo->reuse && (etna_bo_cache_free(&dev->bo_cache, bo) == 0))
246037b3c26Smrg		goto out;
247037b3c26Smrg
248037b3c26Smrg	bo_del(bo);
249037b3c26Smrg	etna_device_del_locked(dev);
250037b3c26Smrgout:
251037b3c26Smrg	pthread_mutex_unlock(&table_lock);
252037b3c26Smrg}
253037b3c26Smrg
254037b3c26Smrg/* get the global flink/DRI2 buffer name */
255037b3c26Smrgint etna_bo_get_name(struct etna_bo *bo, uint32_t *name)
256037b3c26Smrg{
257037b3c26Smrg	if (!bo->name) {
258037b3c26Smrg		struct drm_gem_flink req = {
259037b3c26Smrg			.handle = bo->handle,
260037b3c26Smrg		};
261037b3c26Smrg		int ret;
262037b3c26Smrg
263037b3c26Smrg		ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);
264037b3c26Smrg		if (ret) {
265037b3c26Smrg			return ret;
266037b3c26Smrg		}
267037b3c26Smrg
268037b3c26Smrg		pthread_mutex_lock(&table_lock);
269037b3c26Smrg		set_name(bo, req.name);
270037b3c26Smrg		pthread_mutex_unlock(&table_lock);
271037b3c26Smrg		bo->reuse = 0;
272037b3c26Smrg	}
273037b3c26Smrg
274037b3c26Smrg	*name = bo->name;
275037b3c26Smrg
276037b3c26Smrg	return 0;
277037b3c26Smrg}
278037b3c26Smrg
279037b3c26Smrguint32_t etna_bo_handle(struct etna_bo *bo)
280037b3c26Smrg{
281037b3c26Smrg	return bo->handle;
282037b3c26Smrg}
283037b3c26Smrg
284037b3c26Smrg/* caller owns the dmabuf fd that is returned and is responsible
285037b3c26Smrg * to close() it when done
286037b3c26Smrg */
287037b3c26Smrgint etna_bo_dmabuf(struct etna_bo *bo)
288037b3c26Smrg{
289037b3c26Smrg	int ret, prime_fd;
290037b3c26Smrg
291037b3c26Smrg	ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
292037b3c26Smrg				&prime_fd);
293037b3c26Smrg	if (ret) {
294037b3c26Smrg		ERROR_MSG("failed to get dmabuf fd: %d", ret);
295037b3c26Smrg		return ret;
296037b3c26Smrg	}
297037b3c26Smrg
298037b3c26Smrg	bo->reuse = 0;
299037b3c26Smrg
300037b3c26Smrg	return prime_fd;
301037b3c26Smrg}
302037b3c26Smrg
303037b3c26Smrguint32_t etna_bo_size(struct etna_bo *bo)
304037b3c26Smrg{
305037b3c26Smrg	return bo->size;
306037b3c26Smrg}
307037b3c26Smrg
308037b3c26Smrgvoid *etna_bo_map(struct etna_bo *bo)
309037b3c26Smrg{
310037b3c26Smrg	if (!bo->map) {
311037b3c26Smrg		if (!bo->offset) {
312037b3c26Smrg			get_buffer_info(bo);
313037b3c26Smrg		}
314037b3c26Smrg
315037b3c26Smrg		bo->map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE,
316037b3c26Smrg				MAP_SHARED, bo->dev->fd, bo->offset);
317037b3c26Smrg		if (bo->map == MAP_FAILED) {
318037b3c26Smrg			ERROR_MSG("mmap failed: %s", strerror(errno));
319037b3c26Smrg			bo->map = NULL;
320037b3c26Smrg		}
321037b3c26Smrg	}
322037b3c26Smrg
323037b3c26Smrg	return bo->map;
324037b3c26Smrg}
325037b3c26Smrg
326037b3c26Smrgint etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op)
327037b3c26Smrg{
328037b3c26Smrg	struct drm_etnaviv_gem_cpu_prep req = {
329037b3c26Smrg		.handle = bo->handle,
330037b3c26Smrg		.op = op,
331037b3c26Smrg	};
332037b3c26Smrg
333037b3c26Smrg	get_abs_timeout(&req.timeout, 5000000000);
334037b3c26Smrg
335037b3c26Smrg	return drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_PREP,
336037b3c26Smrg			&req, sizeof(req));
337037b3c26Smrg}
338037b3c26Smrg
339037b3c26Smrgvoid etna_bo_cpu_fini(struct etna_bo *bo)
340037b3c26Smrg{
341037b3c26Smrg	struct drm_etnaviv_gem_cpu_fini req = {
342037b3c26Smrg		.handle = bo->handle,
343037b3c26Smrg	};
344037b3c26Smrg
345037b3c26Smrg	drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_FINI,
346037b3c26Smrg			&req, sizeof(req));
347037b3c26Smrg}
348