17ec681f3Smrg/*
27ec681f3Smrg * Copyright (C) 2014 Etnaviv Project
37ec681f3Smrg *
47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
57ec681f3Smrg * copy of this software and associated documentation files (the "Software"),
67ec681f3Smrg * to deal in the Software without restriction, including without limitation
77ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
87ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the
97ec681f3Smrg * Software is furnished to do so, subject to the following conditions:
107ec681f3Smrg *
117ec681f3Smrg * The above copyright notice and this permission notice (including the next
127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the
137ec681f3Smrg * Software.
147ec681f3Smrg *
157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
187ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
197ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
207ec681f3Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
217ec681f3Smrg * SOFTWARE.
227ec681f3Smrg *
237ec681f3Smrg * Authors:
247ec681f3Smrg *    Christian Gmeiner <christian.gmeiner@gmail.com>
257ec681f3Smrg */
267ec681f3Smrg
277ec681f3Smrg#include "os/os_mman.h"
287ec681f3Smrg#include "util/hash_table.h"
297ec681f3Smrg
307ec681f3Smrg#include "etnaviv_priv.h"
317ec681f3Smrg#include "etnaviv_drmif.h"
327ec681f3Smrg
337ec681f3Smrgsimple_mtx_t etna_drm_table_lock = _SIMPLE_MTX_INITIALIZER_NP;
347ec681f3Smrgvoid _etna_bo_del(struct etna_bo *bo);
357ec681f3Smrg
367ec681f3Smrg/* set buffer name, and add to table, call w/ etna_drm_table_lock held: */
377ec681f3Smrgstatic void set_name(struct etna_bo *bo, uint32_t name)
387ec681f3Smrg{
397ec681f3Smrg	simple_mtx_assert_locked(&etna_drm_table_lock);
407ec681f3Smrg
417ec681f3Smrg	bo->name = name;
427ec681f3Smrg	/* add ourself into the name table: */
437ec681f3Smrg	_mesa_hash_table_insert(bo->dev->name_table, &bo->name, bo);
447ec681f3Smrg}
457ec681f3Smrg
467ec681f3Smrg/* Called under etna_drm_table_lock */
477ec681f3Smrgvoid _etna_bo_del(struct etna_bo *bo)
487ec681f3Smrg{
497ec681f3Smrg	VG_BO_FREE(bo);
507ec681f3Smrg
517ec681f3Smrg	simple_mtx_assert_locked(&etna_drm_table_lock);
527ec681f3Smrg
537ec681f3Smrg	if (bo->va)
547ec681f3Smrg		util_vma_heap_free(&bo->dev->address_space, bo->va, bo->size);
557ec681f3Smrg
567ec681f3Smrg	if (bo->map)
577ec681f3Smrg		os_munmap(bo->map, bo->size);
587ec681f3Smrg
597ec681f3Smrg	if (bo->handle) {
607ec681f3Smrg		struct drm_gem_close req = {
617ec681f3Smrg			.handle = bo->handle,
627ec681f3Smrg		};
637ec681f3Smrg
647ec681f3Smrg		if (bo->name)
657ec681f3Smrg			_mesa_hash_table_remove_key(bo->dev->name_table, &bo->name);
667ec681f3Smrg
677ec681f3Smrg		_mesa_hash_table_remove_key(bo->dev->handle_table, &bo->handle);
687ec681f3Smrg		drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
697ec681f3Smrg	}
707ec681f3Smrg
717ec681f3Smrg	free(bo);
727ec681f3Smrg}
737ec681f3Smrg
747ec681f3Smrg/* lookup a buffer from it's handle, call w/ etna_drm_table_lock held: */
757ec681f3Smrgstatic struct etna_bo *lookup_bo(void *tbl, uint32_t handle)
767ec681f3Smrg{
777ec681f3Smrg	struct etna_bo *bo = NULL;
787ec681f3Smrg	struct hash_entry *entry;
797ec681f3Smrg
807ec681f3Smrg	simple_mtx_assert_locked(&etna_drm_table_lock);
817ec681f3Smrg
827ec681f3Smrg	entry = _mesa_hash_table_search(tbl, &handle);
837ec681f3Smrg
847ec681f3Smrg	if (entry) {
857ec681f3Smrg		/* found, incr refcnt and return: */
867ec681f3Smrg		bo = etna_bo_ref(entry->data);
877ec681f3Smrg
887ec681f3Smrg		/* don't break the bucket if this bo was found in one */
897ec681f3Smrg		if (list_is_linked(&bo->list)) {
907ec681f3Smrg			VG_BO_OBTAIN(bo);
917ec681f3Smrg			etna_device_ref(bo->dev);
927ec681f3Smrg			list_delinit(&bo->list);
937ec681f3Smrg		}
947ec681f3Smrg	}
957ec681f3Smrg
967ec681f3Smrg	return bo;
977ec681f3Smrg}
987ec681f3Smrg
997ec681f3Smrg/* allocate a new buffer object, call w/ etna_drm_table_lock held */
1007ec681f3Smrgstatic struct etna_bo *bo_from_handle(struct etna_device *dev,
1017ec681f3Smrg		uint32_t size, uint32_t handle, uint32_t flags)
1027ec681f3Smrg{
1037ec681f3Smrg	struct etna_bo *bo = calloc(sizeof(*bo), 1);
1047ec681f3Smrg
1057ec681f3Smrg	simple_mtx_assert_locked(&etna_drm_table_lock);
1067ec681f3Smrg
1077ec681f3Smrg	if (!bo) {
1087ec681f3Smrg		struct drm_gem_close req = {
1097ec681f3Smrg			.handle = handle,
1107ec681f3Smrg		};
1117ec681f3Smrg
1127ec681f3Smrg		drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
1137ec681f3Smrg
1147ec681f3Smrg		return NULL;
1157ec681f3Smrg	}
1167ec681f3Smrg
1177ec681f3Smrg	bo->dev = etna_device_ref(dev);
1187ec681f3Smrg	bo->size = size;
1197ec681f3Smrg	bo->handle = handle;
1207ec681f3Smrg	bo->flags = flags;
1217ec681f3Smrg	p_atomic_set(&bo->refcnt, 1);
1227ec681f3Smrg	list_inithead(&bo->list);
1237ec681f3Smrg	/* add ourselves to the handle table: */
1247ec681f3Smrg	_mesa_hash_table_insert(dev->handle_table, &bo->handle, bo);
1257ec681f3Smrg
1267ec681f3Smrg	if (dev->use_softpin)
1277ec681f3Smrg		bo->va = util_vma_heap_alloc(&dev->address_space, bo->size, 4096);
1287ec681f3Smrg
1297ec681f3Smrg	return bo;
1307ec681f3Smrg}
1317ec681f3Smrg
1327ec681f3Smrg/* allocate a new (un-tiled) buffer object */
1337ec681f3Smrgstruct etna_bo *etna_bo_new(struct etna_device *dev, uint32_t size,
1347ec681f3Smrg		uint32_t flags)
1357ec681f3Smrg{
1367ec681f3Smrg	struct etna_bo *bo;
1377ec681f3Smrg	int ret;
1387ec681f3Smrg	struct drm_etnaviv_gem_new req = {
1397ec681f3Smrg			.flags = flags,
1407ec681f3Smrg	};
1417ec681f3Smrg
1427ec681f3Smrg	bo = etna_bo_cache_alloc(&dev->bo_cache, &size, flags);
1437ec681f3Smrg	if (bo)
1447ec681f3Smrg		return bo;
1457ec681f3Smrg
1467ec681f3Smrg	req.size = size;
1477ec681f3Smrg	ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_GEM_NEW,
1487ec681f3Smrg			&req, sizeof(req));
1497ec681f3Smrg	if (ret)
1507ec681f3Smrg		return NULL;
1517ec681f3Smrg
1527ec681f3Smrg	simple_mtx_lock(&etna_drm_table_lock);
1537ec681f3Smrg	bo = bo_from_handle(dev, size, req.handle, flags);
1547ec681f3Smrg	bo->reuse = 1;
1557ec681f3Smrg	simple_mtx_unlock(&etna_drm_table_lock);
1567ec681f3Smrg
1577ec681f3Smrg	VG_BO_ALLOC(bo);
1587ec681f3Smrg
1597ec681f3Smrg	return bo;
1607ec681f3Smrg}
1617ec681f3Smrg
1627ec681f3Smrgstruct etna_bo *etna_bo_ref(struct etna_bo *bo)
1637ec681f3Smrg{
1647ec681f3Smrg	p_atomic_inc(&bo->refcnt);
1657ec681f3Smrg
1667ec681f3Smrg	return bo;
1677ec681f3Smrg}
1687ec681f3Smrg
1697ec681f3Smrg/* get buffer info */
1707ec681f3Smrgstatic int get_buffer_info(struct etna_bo *bo)
1717ec681f3Smrg{
1727ec681f3Smrg	int ret;
1737ec681f3Smrg	struct drm_etnaviv_gem_info req = {
1747ec681f3Smrg		.handle = bo->handle,
1757ec681f3Smrg	};
1767ec681f3Smrg
1777ec681f3Smrg	ret = drmCommandWriteRead(bo->dev->fd, DRM_ETNAVIV_GEM_INFO,
1787ec681f3Smrg			&req, sizeof(req));
1797ec681f3Smrg	if (ret) {
1807ec681f3Smrg		return ret;
1817ec681f3Smrg	}
1827ec681f3Smrg
1837ec681f3Smrg	/* really all we need for now is mmap offset */
1847ec681f3Smrg	bo->offset = req.offset;
1857ec681f3Smrg
1867ec681f3Smrg	return 0;
1877ec681f3Smrg}
1887ec681f3Smrg
1897ec681f3Smrg/* import a buffer object from DRI2 name */
1907ec681f3Smrgstruct etna_bo *etna_bo_from_name(struct etna_device *dev,
1917ec681f3Smrg		uint32_t name)
1927ec681f3Smrg{
1937ec681f3Smrg	struct etna_bo *bo;
1947ec681f3Smrg	struct drm_gem_open req = {
1957ec681f3Smrg		.name = name,
1967ec681f3Smrg	};
1977ec681f3Smrg
1987ec681f3Smrg	simple_mtx_lock(&etna_drm_table_lock);
1997ec681f3Smrg
2007ec681f3Smrg	/* check name table first, to see if bo is already open: */
2017ec681f3Smrg	bo = lookup_bo(dev->name_table, name);
2027ec681f3Smrg	if (bo)
2037ec681f3Smrg		goto out_unlock;
2047ec681f3Smrg
2057ec681f3Smrg	if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
2067ec681f3Smrg		ERROR_MSG("gem-open failed: %s", strerror(errno));
2077ec681f3Smrg		goto out_unlock;
2087ec681f3Smrg	}
2097ec681f3Smrg
2107ec681f3Smrg	bo = lookup_bo(dev->handle_table, req.handle);
2117ec681f3Smrg	if (bo)
2127ec681f3Smrg		goto out_unlock;
2137ec681f3Smrg
2147ec681f3Smrg	bo = bo_from_handle(dev, req.size, req.handle, 0);
2157ec681f3Smrg	if (bo) {
2167ec681f3Smrg		set_name(bo, name);
2177ec681f3Smrg		VG_BO_ALLOC(bo);
2187ec681f3Smrg	}
2197ec681f3Smrg
2207ec681f3Smrgout_unlock:
2217ec681f3Smrg	simple_mtx_unlock(&etna_drm_table_lock);
2227ec681f3Smrg
2237ec681f3Smrg	return bo;
2247ec681f3Smrg}
2257ec681f3Smrg
2267ec681f3Smrg/* import a buffer from dmabuf fd, does not take ownership of the
2277ec681f3Smrg * fd so caller should close() the fd when it is otherwise done
2287ec681f3Smrg * with it (even if it is still using the 'struct etna_bo *')
2297ec681f3Smrg */
2307ec681f3Smrgstruct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd)
2317ec681f3Smrg{
2327ec681f3Smrg	struct etna_bo *bo;
2337ec681f3Smrg	int ret, size;
2347ec681f3Smrg	uint32_t handle;
2357ec681f3Smrg
2367ec681f3Smrg	/* take the lock before calling drmPrimeFDToHandle to avoid
2377ec681f3Smrg	 * racing against etna_bo_del, which might invalidate the
2387ec681f3Smrg	 * returned handle.
2397ec681f3Smrg	 */
2407ec681f3Smrg	simple_mtx_lock(&etna_drm_table_lock);
2417ec681f3Smrg
2427ec681f3Smrg	ret = drmPrimeFDToHandle(dev->fd, fd, &handle);
2437ec681f3Smrg	if (ret) {
2447ec681f3Smrg		simple_mtx_unlock(&etna_drm_table_lock);
2457ec681f3Smrg		return NULL;
2467ec681f3Smrg	}
2477ec681f3Smrg
2487ec681f3Smrg	bo = lookup_bo(dev->handle_table, handle);
2497ec681f3Smrg	if (bo)
2507ec681f3Smrg		goto out_unlock;
2517ec681f3Smrg
2527ec681f3Smrg	/* lseek() to get bo size */
2537ec681f3Smrg	size = lseek(fd, 0, SEEK_END);
2547ec681f3Smrg	lseek(fd, 0, SEEK_CUR);
2557ec681f3Smrg
2567ec681f3Smrg	bo = bo_from_handle(dev, size, handle, 0);
2577ec681f3Smrg
2587ec681f3Smrg	VG_BO_ALLOC(bo);
2597ec681f3Smrg
2607ec681f3Smrgout_unlock:
2617ec681f3Smrg	simple_mtx_unlock(&etna_drm_table_lock);
2627ec681f3Smrg
2637ec681f3Smrg	return bo;
2647ec681f3Smrg}
2657ec681f3Smrg
2667ec681f3Smrg/* destroy a buffer object */
2677ec681f3Smrgvoid etna_bo_del(struct etna_bo *bo)
2687ec681f3Smrg{
2697ec681f3Smrg	if (!bo)
2707ec681f3Smrg		return;
2717ec681f3Smrg
2727ec681f3Smrg	struct etna_device *dev = bo->dev;
2737ec681f3Smrg
2747ec681f3Smrg	simple_mtx_lock(&etna_drm_table_lock);
2757ec681f3Smrg
2767ec681f3Smrg	/* Must test under table lock to avoid racing with the from_dmabuf/name
2777ec681f3Smrg	 * paths, which rely on the BO refcount to be stable over the lookup, so
2787ec681f3Smrg	 * they can grab a reference when the BO is found in the hash.
2797ec681f3Smrg	 */
2807ec681f3Smrg	if (!p_atomic_dec_zero(&bo->refcnt))
2817ec681f3Smrg	   goto out;
2827ec681f3Smrg
2837ec681f3Smrg	if (bo->reuse && (etna_bo_cache_free(&dev->bo_cache, bo) == 0))
2847ec681f3Smrg		goto out;
2857ec681f3Smrg
2867ec681f3Smrg	_etna_bo_del(bo);
2877ec681f3Smrg	etna_device_del_locked(dev);
2887ec681f3Smrgout:
2897ec681f3Smrg	simple_mtx_unlock(&etna_drm_table_lock);
2907ec681f3Smrg}
2917ec681f3Smrg
2927ec681f3Smrg/* get the global flink/DRI2 buffer name */
2937ec681f3Smrgint etna_bo_get_name(struct etna_bo *bo, uint32_t *name)
2947ec681f3Smrg{
2957ec681f3Smrg	if (!bo->name) {
2967ec681f3Smrg		struct drm_gem_flink req = {
2977ec681f3Smrg			.handle = bo->handle,
2987ec681f3Smrg		};
2997ec681f3Smrg		int ret;
3007ec681f3Smrg
3017ec681f3Smrg		ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);
3027ec681f3Smrg		if (ret) {
3037ec681f3Smrg			return ret;
3047ec681f3Smrg		}
3057ec681f3Smrg
3067ec681f3Smrg		simple_mtx_lock(&etna_drm_table_lock);
3077ec681f3Smrg		set_name(bo, req.name);
3087ec681f3Smrg		simple_mtx_unlock(&etna_drm_table_lock);
3097ec681f3Smrg		bo->reuse = 0;
3107ec681f3Smrg	}
3117ec681f3Smrg
3127ec681f3Smrg	*name = bo->name;
3137ec681f3Smrg
3147ec681f3Smrg	return 0;
3157ec681f3Smrg}
3167ec681f3Smrg
3177ec681f3Smrguint32_t etna_bo_handle(struct etna_bo *bo)
3187ec681f3Smrg{
3197ec681f3Smrg	return bo->handle;
3207ec681f3Smrg}
3217ec681f3Smrg
3227ec681f3Smrg/* caller owns the dmabuf fd that is returned and is responsible
3237ec681f3Smrg * to close() it when done
3247ec681f3Smrg */
3257ec681f3Smrgint etna_bo_dmabuf(struct etna_bo *bo)
3267ec681f3Smrg{
3277ec681f3Smrg	int ret, prime_fd;
3287ec681f3Smrg
3297ec681f3Smrg	ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
3307ec681f3Smrg				&prime_fd);
3317ec681f3Smrg	if (ret) {
3327ec681f3Smrg		ERROR_MSG("failed to get dmabuf fd: %d", ret);
3337ec681f3Smrg		return ret;
3347ec681f3Smrg	}
3357ec681f3Smrg
3367ec681f3Smrg	bo->reuse = 0;
3377ec681f3Smrg
3387ec681f3Smrg	return prime_fd;
3397ec681f3Smrg}
3407ec681f3Smrg
3417ec681f3Smrguint32_t etna_bo_size(struct etna_bo *bo)
3427ec681f3Smrg{
3437ec681f3Smrg	return bo->size;
3447ec681f3Smrg}
3457ec681f3Smrg
3467ec681f3Smrguint32_t etna_bo_gpu_va(struct etna_bo *bo)
3477ec681f3Smrg{
3487ec681f3Smrg	return bo->va;
3497ec681f3Smrg}
3507ec681f3Smrg
3517ec681f3Smrgvoid *etna_bo_map(struct etna_bo *bo)
3527ec681f3Smrg{
3537ec681f3Smrg	if (!bo->map) {
3547ec681f3Smrg		if (!bo->offset) {
3557ec681f3Smrg			get_buffer_info(bo);
3567ec681f3Smrg		}
3577ec681f3Smrg
3587ec681f3Smrg		bo->map = os_mmap(0, bo->size, PROT_READ | PROT_WRITE,
3597ec681f3Smrg				  MAP_SHARED, bo->dev->fd, bo->offset);
3607ec681f3Smrg		if (bo->map == MAP_FAILED) {
3617ec681f3Smrg			ERROR_MSG("mmap failed: %s", strerror(errno));
3627ec681f3Smrg			bo->map = NULL;
3637ec681f3Smrg		}
3647ec681f3Smrg	}
3657ec681f3Smrg
3667ec681f3Smrg	return bo->map;
3677ec681f3Smrg}
3687ec681f3Smrg
3697ec681f3Smrgint etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op)
3707ec681f3Smrg{
3717ec681f3Smrg	struct drm_etnaviv_gem_cpu_prep req = {
3727ec681f3Smrg		.handle = bo->handle,
3737ec681f3Smrg		.op = op,
3747ec681f3Smrg	};
3757ec681f3Smrg
3767ec681f3Smrg	get_abs_timeout(&req.timeout, 5000000000);
3777ec681f3Smrg
3787ec681f3Smrg	return drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_PREP,
3797ec681f3Smrg			&req, sizeof(req));
3807ec681f3Smrg}
3817ec681f3Smrg
3827ec681f3Smrgvoid etna_bo_cpu_fini(struct etna_bo *bo)
3837ec681f3Smrg{
3847ec681f3Smrg	struct drm_etnaviv_gem_cpu_fini req = {
3857ec681f3Smrg		.handle = bo->handle,
3867ec681f3Smrg	};
3877ec681f3Smrg
3887ec681f3Smrg	drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_FINI,
3897ec681f3Smrg			&req, sizeof(req));
3907ec681f3Smrg}
391