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#include "etnaviv_priv.h"
28037b3c26Smrg#include "etnaviv_drmif.h"
29037b3c26Smrg
30037b3c26Smrgdrm_private pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
31037b3c26Smrgdrm_private void bo_del(struct etna_bo *bo);
32037b3c26Smrg
33037b3c26Smrg/* set buffer name, and add to table, call w/ table_lock held: */
34037b3c26Smrgstatic void set_name(struct etna_bo *bo, uint32_t name)
35037b3c26Smrg{
36037b3c26Smrg	bo->name = name;
37037b3c26Smrg	/* add ourself into the name table: */
38037b3c26Smrg	drmHashInsert(bo->dev->name_table, name, bo);
39037b3c26Smrg}
40037b3c26Smrg
41037b3c26Smrg/* Called under table_lock */
42037b3c26Smrgdrm_private void bo_del(struct etna_bo *bo)
43037b3c26Smrg{
44037b3c26Smrg	if (bo->map)
45037b3c26Smrg		drm_munmap(bo->map, bo->size);
46037b3c26Smrg
47037b3c26Smrg	if (bo->name)
48037b3c26Smrg		drmHashDelete(bo->dev->name_table, bo->name);
49037b3c26Smrg
50037b3c26Smrg	if (bo->handle) {
51037b3c26Smrg		drmHashDelete(bo->dev->handle_table, bo->handle);
5249ef06a4Smrg		drmCloseBufferHandle(bo->dev->fd, bo->handle);
53037b3c26Smrg	}
54037b3c26Smrg
55037b3c26Smrg	free(bo);
56037b3c26Smrg}
57037b3c26Smrg
58037b3c26Smrg/* lookup a buffer from it's handle, call w/ table_lock held: */
59037b3c26Smrgstatic struct etna_bo *lookup_bo(void *tbl, uint32_t handle)
60037b3c26Smrg{
61037b3c26Smrg	struct etna_bo *bo = NULL;
62037b3c26Smrg
63037b3c26Smrg	if (!drmHashLookup(tbl, handle, (void **)&bo)) {
64037b3c26Smrg		/* found, incr refcnt and return: */
65037b3c26Smrg		bo = etna_bo_ref(bo);
66037b3c26Smrg
67037b3c26Smrg		/* don't break the bucket if this bo was found in one */
68037b3c26Smrg		list_delinit(&bo->list);
69037b3c26Smrg	}
70037b3c26Smrg
71037b3c26Smrg	return bo;
72037b3c26Smrg}
73037b3c26Smrg
74037b3c26Smrg/* allocate a new buffer object, call w/ table_lock held */
75037b3c26Smrgstatic struct etna_bo *bo_from_handle(struct etna_device *dev,
76037b3c26Smrg		uint32_t size, uint32_t handle, uint32_t flags)
77037b3c26Smrg{
78037b3c26Smrg	struct etna_bo *bo = calloc(sizeof(*bo), 1);
79037b3c26Smrg
80037b3c26Smrg	if (!bo) {
8149ef06a4Smrg		drmCloseBufferHandle(dev->fd, handle);
82037b3c26Smrg		return NULL;
83037b3c26Smrg	}
84037b3c26Smrg
85037b3c26Smrg	bo->dev = etna_device_ref(dev);
86037b3c26Smrg	bo->size = size;
87037b3c26Smrg	bo->handle = handle;
88037b3c26Smrg	bo->flags = flags;
89037b3c26Smrg	atomic_set(&bo->refcnt, 1);
90037b3c26Smrg	list_inithead(&bo->list);
91037b3c26Smrg	/* add ourselves to the handle table: */
92037b3c26Smrg	drmHashInsert(dev->handle_table, handle, bo);
93037b3c26Smrg
94037b3c26Smrg	return bo;
95037b3c26Smrg}
96037b3c26Smrg
97037b3c26Smrg/* allocate a new (un-tiled) buffer object */
987cdc0497Smrgdrm_public struct etna_bo *etna_bo_new(struct etna_device *dev, uint32_t size,
99037b3c26Smrg		uint32_t flags)
100037b3c26Smrg{
101037b3c26Smrg	struct etna_bo *bo;
102037b3c26Smrg	int ret;
103037b3c26Smrg	struct drm_etnaviv_gem_new req = {
104037b3c26Smrg			.flags = flags,
105037b3c26Smrg	};
106037b3c26Smrg
107037b3c26Smrg	bo = etna_bo_cache_alloc(&dev->bo_cache, &size, flags);
108037b3c26Smrg	if (bo)
109037b3c26Smrg		return bo;
110037b3c26Smrg
111037b3c26Smrg	req.size = size;
112037b3c26Smrg	ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_GEM_NEW,
113037b3c26Smrg			&req, sizeof(req));
114037b3c26Smrg	if (ret)
115037b3c26Smrg		return NULL;
116037b3c26Smrg
117037b3c26Smrg	pthread_mutex_lock(&table_lock);
118037b3c26Smrg	bo = bo_from_handle(dev, size, req.handle, flags);
119037b3c26Smrg	bo->reuse = 1;
120037b3c26Smrg	pthread_mutex_unlock(&table_lock);
121037b3c26Smrg
122037b3c26Smrg	return bo;
123037b3c26Smrg}
124037b3c26Smrg
1257cdc0497Smrgdrm_public struct etna_bo *etna_bo_ref(struct etna_bo *bo)
126037b3c26Smrg{
127037b3c26Smrg	atomic_inc(&bo->refcnt);
128037b3c26Smrg
129037b3c26Smrg	return bo;
130037b3c26Smrg}
131037b3c26Smrg
132037b3c26Smrg/* get buffer info */
133037b3c26Smrgstatic int get_buffer_info(struct etna_bo *bo)
134037b3c26Smrg{
135037b3c26Smrg	int ret;
136037b3c26Smrg	struct drm_etnaviv_gem_info req = {
137037b3c26Smrg		.handle = bo->handle,
138037b3c26Smrg	};
139037b3c26Smrg
140037b3c26Smrg	ret = drmCommandWriteRead(bo->dev->fd, DRM_ETNAVIV_GEM_INFO,
141037b3c26Smrg			&req, sizeof(req));
142037b3c26Smrg	if (ret) {
143037b3c26Smrg		return ret;
144037b3c26Smrg	}
145037b3c26Smrg
146037b3c26Smrg	/* really all we need for now is mmap offset */
147037b3c26Smrg	bo->offset = req.offset;
148037b3c26Smrg
149037b3c26Smrg	return 0;
150037b3c26Smrg}
151037b3c26Smrg
152037b3c26Smrg/* import a buffer object from DRI2 name */
1537cdc0497Smrgdrm_public struct etna_bo *etna_bo_from_name(struct etna_device *dev,
1547cdc0497Smrg		uint32_t name)
155037b3c26Smrg{
156037b3c26Smrg	struct etna_bo *bo;
157037b3c26Smrg	struct drm_gem_open req = {
158037b3c26Smrg		.name = name,
159037b3c26Smrg	};
160037b3c26Smrg
161037b3c26Smrg	pthread_mutex_lock(&table_lock);
162037b3c26Smrg
163037b3c26Smrg	/* check name table first, to see if bo is already open: */
164d8807b2fSmrg	bo = lookup_bo(dev->name_table, name);
165037b3c26Smrg	if (bo)
166037b3c26Smrg		goto out_unlock;
167037b3c26Smrg
168037b3c26Smrg	if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
169037b3c26Smrg		ERROR_MSG("gem-open failed: %s", strerror(errno));
170037b3c26Smrg		goto out_unlock;
171037b3c26Smrg	}
172037b3c26Smrg
173037b3c26Smrg	bo = lookup_bo(dev->handle_table, req.handle);
174037b3c26Smrg	if (bo)
175037b3c26Smrg		goto out_unlock;
176037b3c26Smrg
177037b3c26Smrg	bo = bo_from_handle(dev, req.size, req.handle, 0);
178037b3c26Smrg	if (bo)
179037b3c26Smrg		set_name(bo, name);
180037b3c26Smrg
181037b3c26Smrgout_unlock:
182037b3c26Smrg	pthread_mutex_unlock(&table_lock);
183037b3c26Smrg
184037b3c26Smrg	return bo;
185037b3c26Smrg}
186037b3c26Smrg
187037b3c26Smrg/* import a buffer from dmabuf fd, does not take ownership of the
188037b3c26Smrg * fd so caller should close() the fd when it is otherwise done
189037b3c26Smrg * with it (even if it is still using the 'struct etna_bo *')
190037b3c26Smrg */
1917cdc0497Smrgdrm_public struct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd)
192037b3c26Smrg{
193037b3c26Smrg	struct etna_bo *bo;
194037b3c26Smrg	int ret, size;
195037b3c26Smrg	uint32_t handle;
196037b3c26Smrg
19700a23bdaSmrg	/* take the lock before calling drmPrimeFDToHandle to avoid
19800a23bdaSmrg	 * racing against etna_bo_del, which might invalidate the
19900a23bdaSmrg	 * returned handle.
20000a23bdaSmrg	 */
201037b3c26Smrg	pthread_mutex_lock(&table_lock);
202037b3c26Smrg
203037b3c26Smrg	ret = drmPrimeFDToHandle(dev->fd, fd, &handle);
204037b3c26Smrg	if (ret) {
20500a23bdaSmrg		pthread_mutex_unlock(&table_lock);
206037b3c26Smrg		return NULL;
207037b3c26Smrg	}
208037b3c26Smrg
209037b3c26Smrg	bo = lookup_bo(dev->handle_table, handle);
210037b3c26Smrg	if (bo)
211037b3c26Smrg		goto out_unlock;
212037b3c26Smrg
213037b3c26Smrg	/* lseek() to get bo size */
214037b3c26Smrg	size = lseek(fd, 0, SEEK_END);
215037b3c26Smrg	lseek(fd, 0, SEEK_CUR);
216037b3c26Smrg
217037b3c26Smrg	bo = bo_from_handle(dev, size, handle, 0);
218037b3c26Smrg
219037b3c26Smrgout_unlock:
220037b3c26Smrg	pthread_mutex_unlock(&table_lock);
221037b3c26Smrg
222037b3c26Smrg	return bo;
223037b3c26Smrg}
224037b3c26Smrg
225037b3c26Smrg/* destroy a buffer object */
2267cdc0497Smrgdrm_public void etna_bo_del(struct etna_bo *bo)
227037b3c26Smrg{
228037b3c26Smrg	struct etna_device *dev = bo->dev;
229037b3c26Smrg
230037b3c26Smrg	if (!bo)
231037b3c26Smrg		return;
232037b3c26Smrg
233037b3c26Smrg	if (!atomic_dec_and_test(&bo->refcnt))
234037b3c26Smrg		return;
235037b3c26Smrg
236037b3c26Smrg	pthread_mutex_lock(&table_lock);
237037b3c26Smrg
238037b3c26Smrg	if (bo->reuse && (etna_bo_cache_free(&dev->bo_cache, bo) == 0))
239037b3c26Smrg		goto out;
240037b3c26Smrg
241037b3c26Smrg	bo_del(bo);
242037b3c26Smrg	etna_device_del_locked(dev);
243037b3c26Smrgout:
244037b3c26Smrg	pthread_mutex_unlock(&table_lock);
245037b3c26Smrg}
246037b3c26Smrg
247037b3c26Smrg/* get the global flink/DRI2 buffer name */
2487cdc0497Smrgdrm_public int etna_bo_get_name(struct etna_bo *bo, uint32_t *name)
249037b3c26Smrg{
250037b3c26Smrg	if (!bo->name) {
251037b3c26Smrg		struct drm_gem_flink req = {
252037b3c26Smrg			.handle = bo->handle,
253037b3c26Smrg		};
254037b3c26Smrg		int ret;
255037b3c26Smrg
256037b3c26Smrg		ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);
257037b3c26Smrg		if (ret) {
258037b3c26Smrg			return ret;
259037b3c26Smrg		}
260037b3c26Smrg
261037b3c26Smrg		pthread_mutex_lock(&table_lock);
262037b3c26Smrg		set_name(bo, req.name);
263037b3c26Smrg		pthread_mutex_unlock(&table_lock);
264037b3c26Smrg		bo->reuse = 0;
265037b3c26Smrg	}
266037b3c26Smrg
267037b3c26Smrg	*name = bo->name;
268037b3c26Smrg
269037b3c26Smrg	return 0;
270037b3c26Smrg}
271037b3c26Smrg
2727cdc0497Smrgdrm_public uint32_t etna_bo_handle(struct etna_bo *bo)
273037b3c26Smrg{
274037b3c26Smrg	return bo->handle;
275037b3c26Smrg}
276037b3c26Smrg
277037b3c26Smrg/* caller owns the dmabuf fd that is returned and is responsible
278037b3c26Smrg * to close() it when done
279037b3c26Smrg */
2807cdc0497Smrgdrm_public int etna_bo_dmabuf(struct etna_bo *bo)
281037b3c26Smrg{
282037b3c26Smrg	int ret, prime_fd;
283037b3c26Smrg
284037b3c26Smrg	ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
285037b3c26Smrg				&prime_fd);
286037b3c26Smrg	if (ret) {
287037b3c26Smrg		ERROR_MSG("failed to get dmabuf fd: %d", ret);
288037b3c26Smrg		return ret;
289037b3c26Smrg	}
290037b3c26Smrg
291037b3c26Smrg	bo->reuse = 0;
292037b3c26Smrg
293037b3c26Smrg	return prime_fd;
294037b3c26Smrg}
295037b3c26Smrg
2967cdc0497Smrgdrm_public uint32_t etna_bo_size(struct etna_bo *bo)
297037b3c26Smrg{
298037b3c26Smrg	return bo->size;
299037b3c26Smrg}
300037b3c26Smrg
3017cdc0497Smrgdrm_public void *etna_bo_map(struct etna_bo *bo)
302037b3c26Smrg{
303037b3c26Smrg	if (!bo->map) {
304037b3c26Smrg		if (!bo->offset) {
305037b3c26Smrg			get_buffer_info(bo);
306037b3c26Smrg		}
307037b3c26Smrg
308037b3c26Smrg		bo->map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE,
309037b3c26Smrg				MAP_SHARED, bo->dev->fd, bo->offset);
310037b3c26Smrg		if (bo->map == MAP_FAILED) {
311037b3c26Smrg			ERROR_MSG("mmap failed: %s", strerror(errno));
312037b3c26Smrg			bo->map = NULL;
313037b3c26Smrg		}
314037b3c26Smrg	}
315037b3c26Smrg
316037b3c26Smrg	return bo->map;
317037b3c26Smrg}
318037b3c26Smrg
3197cdc0497Smrgdrm_public int etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op)
320037b3c26Smrg{
321037b3c26Smrg	struct drm_etnaviv_gem_cpu_prep req = {
322037b3c26Smrg		.handle = bo->handle,
323037b3c26Smrg		.op = op,
324037b3c26Smrg	};
325037b3c26Smrg
326037b3c26Smrg	get_abs_timeout(&req.timeout, 5000000000);
327037b3c26Smrg
328037b3c26Smrg	return drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_PREP,
329037b3c26Smrg			&req, sizeof(req));
330037b3c26Smrg}
331037b3c26Smrg
3327cdc0497Smrgdrm_public void etna_bo_cpu_fini(struct etna_bo *bo)
333037b3c26Smrg{
334037b3c26Smrg	struct drm_etnaviv_gem_cpu_fini req = {
335037b3c26Smrg		.handle = bo->handle,
336037b3c26Smrg	};
337037b3c26Smrg
338037b3c26Smrg	drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_FINI,
339037b3c26Smrg			&req, sizeof(req));
340037b3c26Smrg}
341