Home | History | Annotate | Download | only in etnaviv
      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 (at) 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 
     34 drm_private pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
     35 drm_private void bo_del(struct etna_bo *bo);
     36 
     37 /* set buffer name, and add to table, call w/ table_lock held: */
     38 static 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 */
     46 drm_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: */
     67 static 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 */
     83 static 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 */
    111 struct 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 
    138 struct 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 */
    146 static 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 */
    166 struct 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, req.handle);
    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 
    193 out_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  */
    203 struct 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 
    226 out_unlock:
    227 	pthread_mutex_unlock(&table_lock);
    228 
    229 	return bo;
    230 }
    231 
    232 /* destroy a buffer object */
    233 void 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);
    250 out:
    251 	pthread_mutex_unlock(&table_lock);
    252 }
    253 
    254 /* get the global flink/DRI2 buffer name */
    255 int 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 
    279 uint32_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  */
    287 int 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 
    303 uint32_t etna_bo_size(struct etna_bo *bo)
    304 {
    305 	return bo->size;
    306 }
    307 
    308 void *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 
    326 int 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 
    339 void 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