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 #include "etnaviv_priv.h"
     28 #include "etnaviv_drmif.h"
     29 
     30 drm_private pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
     31 drm_private void bo_del(struct etna_bo *bo);
     32 
     33 /* set buffer name, and add to table, call w/ table_lock held: */
     34 static void set_name(struct etna_bo *bo, uint32_t name)
     35 {
     36 	bo->name = name;
     37 	/* add ourself into the name table: */
     38 	drmHashInsert(bo->dev->name_table, name, bo);
     39 }
     40 
     41 /* Called under table_lock */
     42 drm_private void bo_del(struct etna_bo *bo)
     43 {
     44 	if (bo->map)
     45 		drm_munmap(bo->map, bo->size);
     46 
     47 	if (bo->name)
     48 		drmHashDelete(bo->dev->name_table, bo->name);
     49 
     50 	if (bo->handle) {
     51 		struct drm_gem_close req = {
     52 			.handle = bo->handle,
     53 		};
     54 
     55 		drmHashDelete(bo->dev->handle_table, bo->handle);
     56 		drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
     57 	}
     58 
     59 	free(bo);
     60 }
     61 
     62 /* lookup a buffer from it's handle, call w/ table_lock held: */
     63 static struct etna_bo *lookup_bo(void *tbl, uint32_t handle)
     64 {
     65 	struct etna_bo *bo = NULL;
     66 
     67 	if (!drmHashLookup(tbl, handle, (void **)&bo)) {
     68 		/* found, incr refcnt and return: */
     69 		bo = etna_bo_ref(bo);
     70 
     71 		/* don't break the bucket if this bo was found in one */
     72 		list_delinit(&bo->list);
     73 	}
     74 
     75 	return bo;
     76 }
     77 
     78 /* allocate a new buffer object, call w/ table_lock held */
     79 static struct etna_bo *bo_from_handle(struct etna_device *dev,
     80 		uint32_t size, uint32_t handle, uint32_t flags)
     81 {
     82 	struct etna_bo *bo = calloc(sizeof(*bo), 1);
     83 
     84 	if (!bo) {
     85 		struct drm_gem_close req = {
     86 			.handle = handle,
     87 		};
     88 
     89 		drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
     90 
     91 		return NULL;
     92 	}
     93 
     94 	bo->dev = etna_device_ref(dev);
     95 	bo->size = size;
     96 	bo->handle = handle;
     97 	bo->flags = flags;
     98 	atomic_set(&bo->refcnt, 1);
     99 	list_inithead(&bo->list);
    100 	/* add ourselves to the handle table: */
    101 	drmHashInsert(dev->handle_table, handle, bo);
    102 
    103 	return bo;
    104 }
    105 
    106 /* allocate a new (un-tiled) buffer object */
    107 struct etna_bo *etna_bo_new(struct etna_device *dev, uint32_t size,
    108 		uint32_t flags)
    109 {
    110 	struct etna_bo *bo;
    111 	int ret;
    112 	struct drm_etnaviv_gem_new req = {
    113 			.flags = flags,
    114 	};
    115 
    116 	bo = etna_bo_cache_alloc(&dev->bo_cache, &size, flags);
    117 	if (bo)
    118 		return bo;
    119 
    120 	req.size = size;
    121 	ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_GEM_NEW,
    122 			&req, sizeof(req));
    123 	if (ret)
    124 		return NULL;
    125 
    126 	pthread_mutex_lock(&table_lock);
    127 	bo = bo_from_handle(dev, size, req.handle, flags);
    128 	bo->reuse = 1;
    129 	pthread_mutex_unlock(&table_lock);
    130 
    131 	return bo;
    132 }
    133 
    134 struct etna_bo *etna_bo_ref(struct etna_bo *bo)
    135 {
    136 	atomic_inc(&bo->refcnt);
    137 
    138 	return bo;
    139 }
    140 
    141 /* get buffer info */
    142 static int get_buffer_info(struct etna_bo *bo)
    143 {
    144 	int ret;
    145 	struct drm_etnaviv_gem_info req = {
    146 		.handle = bo->handle,
    147 	};
    148 
    149 	ret = drmCommandWriteRead(bo->dev->fd, DRM_ETNAVIV_GEM_INFO,
    150 			&req, sizeof(req));
    151 	if (ret) {
    152 		return ret;
    153 	}
    154 
    155 	/* really all we need for now is mmap offset */
    156 	bo->offset = req.offset;
    157 
    158 	return 0;
    159 }
    160 
    161 /* import a buffer object from DRI2 name */
    162 struct etna_bo *etna_bo_from_name(struct etna_device *dev, uint32_t name)
    163 {
    164 	struct etna_bo *bo;
    165 	struct drm_gem_open req = {
    166 		.name = name,
    167 	};
    168 
    169 	pthread_mutex_lock(&table_lock);
    170 
    171 	/* check name table first, to see if bo is already open: */
    172 	bo = lookup_bo(dev->name_table, name);
    173 	if (bo)
    174 		goto out_unlock;
    175 
    176 	if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
    177 		ERROR_MSG("gem-open failed: %s", strerror(errno));
    178 		goto out_unlock;
    179 	}
    180 
    181 	bo = lookup_bo(dev->handle_table, req.handle);
    182 	if (bo)
    183 		goto out_unlock;
    184 
    185 	bo = bo_from_handle(dev, req.size, req.handle, 0);
    186 	if (bo)
    187 		set_name(bo, name);
    188 
    189 out_unlock:
    190 	pthread_mutex_unlock(&table_lock);
    191 
    192 	return bo;
    193 }
    194 
    195 /* import a buffer from dmabuf fd, does not take ownership of the
    196  * fd so caller should close() the fd when it is otherwise done
    197  * with it (even if it is still using the 'struct etna_bo *')
    198  */
    199 struct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd)
    200 {
    201 	struct etna_bo *bo;
    202 	int ret, size;
    203 	uint32_t handle;
    204 
    205 	/* take the lock before calling drmPrimeFDToHandle to avoid
    206 	 * racing against etna_bo_del, which might invalidate the
    207 	 * returned handle.
    208 	 */
    209 	pthread_mutex_lock(&table_lock);
    210 
    211 	ret = drmPrimeFDToHandle(dev->fd, fd, &handle);
    212 	if (ret) {
    213 		pthread_mutex_unlock(&table_lock);
    214 		return NULL;
    215 	}
    216 
    217 	bo = lookup_bo(dev->handle_table, handle);
    218 	if (bo)
    219 		goto out_unlock;
    220 
    221 	/* lseek() to get bo size */
    222 	size = lseek(fd, 0, SEEK_END);
    223 	lseek(fd, 0, SEEK_CUR);
    224 
    225 	bo = bo_from_handle(dev, size, handle, 0);
    226 
    227 out_unlock:
    228 	pthread_mutex_unlock(&table_lock);
    229 
    230 	return bo;
    231 }
    232 
    233 /* destroy a buffer object */
    234 void etna_bo_del(struct etna_bo *bo)
    235 {
    236 	struct etna_device *dev = bo->dev;
    237 
    238 	if (!bo)
    239 		return;
    240 
    241 	if (!atomic_dec_and_test(&bo->refcnt))
    242 		return;
    243 
    244 	pthread_mutex_lock(&table_lock);
    245 
    246 	if (bo->reuse && (etna_bo_cache_free(&dev->bo_cache, bo) == 0))
    247 		goto out;
    248 
    249 	bo_del(bo);
    250 	etna_device_del_locked(dev);
    251 out:
    252 	pthread_mutex_unlock(&table_lock);
    253 }
    254 
    255 /* get the global flink/DRI2 buffer name */
    256 int etna_bo_get_name(struct etna_bo *bo, uint32_t *name)
    257 {
    258 	if (!bo->name) {
    259 		struct drm_gem_flink req = {
    260 			.handle = bo->handle,
    261 		};
    262 		int ret;
    263 
    264 		ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);
    265 		if (ret) {
    266 			return ret;
    267 		}
    268 
    269 		pthread_mutex_lock(&table_lock);
    270 		set_name(bo, req.name);
    271 		pthread_mutex_unlock(&table_lock);
    272 		bo->reuse = 0;
    273 	}
    274 
    275 	*name = bo->name;
    276 
    277 	return 0;
    278 }
    279 
    280 uint32_t etna_bo_handle(struct etna_bo *bo)
    281 {
    282 	return bo->handle;
    283 }
    284 
    285 /* caller owns the dmabuf fd that is returned and is responsible
    286  * to close() it when done
    287  */
    288 int etna_bo_dmabuf(struct etna_bo *bo)
    289 {
    290 	int ret, prime_fd;
    291 
    292 	ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
    293 				&prime_fd);
    294 	if (ret) {
    295 		ERROR_MSG("failed to get dmabuf fd: %d", ret);
    296 		return ret;
    297 	}
    298 
    299 	bo->reuse = 0;
    300 
    301 	return prime_fd;
    302 }
    303 
    304 uint32_t etna_bo_size(struct etna_bo *bo)
    305 {
    306 	return bo->size;
    307 }
    308 
    309 void *etna_bo_map(struct etna_bo *bo)
    310 {
    311 	if (!bo->map) {
    312 		if (!bo->offset) {
    313 			get_buffer_info(bo);
    314 		}
    315 
    316 		bo->map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE,
    317 				MAP_SHARED, bo->dev->fd, bo->offset);
    318 		if (bo->map == MAP_FAILED) {
    319 			ERROR_MSG("mmap failed: %s", strerror(errno));
    320 			bo->map = NULL;
    321 		}
    322 	}
    323 
    324 	return bo->map;
    325 }
    326 
    327 int etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op)
    328 {
    329 	struct drm_etnaviv_gem_cpu_prep req = {
    330 		.handle = bo->handle,
    331 		.op = op,
    332 	};
    333 
    334 	get_abs_timeout(&req.timeout, 5000000000);
    335 
    336 	return drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_PREP,
    337 			&req, sizeof(req));
    338 }
    339 
    340 void etna_bo_cpu_fini(struct etna_bo *bo)
    341 {
    342 	struct drm_etnaviv_gem_cpu_fini req = {
    343 		.handle = bo->handle,
    344 	};
    345 
    346 	drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_FINI,
    347 			&req, sizeof(req));
    348 }
    349