etnaviv_bo.c revision 7cdc0497
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 struct drm_gem_close req = { 52037b3c26Smrg .handle = bo->handle, 53037b3c26Smrg }; 54037b3c26Smrg 55037b3c26Smrg drmHashDelete(bo->dev->handle_table, bo->handle); 56037b3c26Smrg drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req); 57037b3c26Smrg } 58037b3c26Smrg 59037b3c26Smrg free(bo); 60037b3c26Smrg} 61037b3c26Smrg 62037b3c26Smrg/* lookup a buffer from it's handle, call w/ table_lock held: */ 63037b3c26Smrgstatic struct etna_bo *lookup_bo(void *tbl, uint32_t handle) 64037b3c26Smrg{ 65037b3c26Smrg struct etna_bo *bo = NULL; 66037b3c26Smrg 67037b3c26Smrg if (!drmHashLookup(tbl, handle, (void **)&bo)) { 68037b3c26Smrg /* found, incr refcnt and return: */ 69037b3c26Smrg bo = etna_bo_ref(bo); 70037b3c26Smrg 71037b3c26Smrg /* don't break the bucket if this bo was found in one */ 72037b3c26Smrg list_delinit(&bo->list); 73037b3c26Smrg } 74037b3c26Smrg 75037b3c26Smrg return bo; 76037b3c26Smrg} 77037b3c26Smrg 78037b3c26Smrg/* allocate a new buffer object, call w/ table_lock held */ 79037b3c26Smrgstatic struct etna_bo *bo_from_handle(struct etna_device *dev, 80037b3c26Smrg uint32_t size, uint32_t handle, uint32_t flags) 81037b3c26Smrg{ 82037b3c26Smrg struct etna_bo *bo = calloc(sizeof(*bo), 1); 83037b3c26Smrg 84037b3c26Smrg if (!bo) { 85037b3c26Smrg struct drm_gem_close req = { 86037b3c26Smrg .handle = handle, 87037b3c26Smrg }; 88037b3c26Smrg 89037b3c26Smrg drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req); 90037b3c26Smrg 91037b3c26Smrg return NULL; 92037b3c26Smrg } 93037b3c26Smrg 94037b3c26Smrg bo->dev = etna_device_ref(dev); 95037b3c26Smrg bo->size = size; 96037b3c26Smrg bo->handle = handle; 97037b3c26Smrg bo->flags = flags; 98037b3c26Smrg atomic_set(&bo->refcnt, 1); 99037b3c26Smrg list_inithead(&bo->list); 100037b3c26Smrg /* add ourselves to the handle table: */ 101037b3c26Smrg drmHashInsert(dev->handle_table, handle, bo); 102037b3c26Smrg 103037b3c26Smrg return bo; 104037b3c26Smrg} 105037b3c26Smrg 106037b3c26Smrg/* allocate a new (un-tiled) buffer object */ 1077cdc0497Smrgdrm_public struct etna_bo *etna_bo_new(struct etna_device *dev, uint32_t size, 108037b3c26Smrg uint32_t flags) 109037b3c26Smrg{ 110037b3c26Smrg struct etna_bo *bo; 111037b3c26Smrg int ret; 112037b3c26Smrg struct drm_etnaviv_gem_new req = { 113037b3c26Smrg .flags = flags, 114037b3c26Smrg }; 115037b3c26Smrg 116037b3c26Smrg bo = etna_bo_cache_alloc(&dev->bo_cache, &size, flags); 117037b3c26Smrg if (bo) 118037b3c26Smrg return bo; 119037b3c26Smrg 120037b3c26Smrg req.size = size; 121037b3c26Smrg ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_GEM_NEW, 122037b3c26Smrg &req, sizeof(req)); 123037b3c26Smrg if (ret) 124037b3c26Smrg return NULL; 125037b3c26Smrg 126037b3c26Smrg pthread_mutex_lock(&table_lock); 127037b3c26Smrg bo = bo_from_handle(dev, size, req.handle, flags); 128037b3c26Smrg bo->reuse = 1; 129037b3c26Smrg pthread_mutex_unlock(&table_lock); 130037b3c26Smrg 131037b3c26Smrg return bo; 132037b3c26Smrg} 133037b3c26Smrg 1347cdc0497Smrgdrm_public struct etna_bo *etna_bo_ref(struct etna_bo *bo) 135037b3c26Smrg{ 136037b3c26Smrg atomic_inc(&bo->refcnt); 137037b3c26Smrg 138037b3c26Smrg return bo; 139037b3c26Smrg} 140037b3c26Smrg 141037b3c26Smrg/* get buffer info */ 142037b3c26Smrgstatic int get_buffer_info(struct etna_bo *bo) 143037b3c26Smrg{ 144037b3c26Smrg int ret; 145037b3c26Smrg struct drm_etnaviv_gem_info req = { 146037b3c26Smrg .handle = bo->handle, 147037b3c26Smrg }; 148037b3c26Smrg 149037b3c26Smrg ret = drmCommandWriteRead(bo->dev->fd, DRM_ETNAVIV_GEM_INFO, 150037b3c26Smrg &req, sizeof(req)); 151037b3c26Smrg if (ret) { 152037b3c26Smrg return ret; 153037b3c26Smrg } 154037b3c26Smrg 155037b3c26Smrg /* really all we need for now is mmap offset */ 156037b3c26Smrg bo->offset = req.offset; 157037b3c26Smrg 158037b3c26Smrg return 0; 159037b3c26Smrg} 160037b3c26Smrg 161037b3c26Smrg/* import a buffer object from DRI2 name */ 1627cdc0497Smrgdrm_public struct etna_bo *etna_bo_from_name(struct etna_device *dev, 1637cdc0497Smrg uint32_t name) 164037b3c26Smrg{ 165037b3c26Smrg struct etna_bo *bo; 166037b3c26Smrg struct drm_gem_open req = { 167037b3c26Smrg .name = name, 168037b3c26Smrg }; 169037b3c26Smrg 170037b3c26Smrg pthread_mutex_lock(&table_lock); 171037b3c26Smrg 172037b3c26Smrg /* check name table first, to see if bo is already open: */ 173d8807b2fSmrg bo = lookup_bo(dev->name_table, name); 174037b3c26Smrg if (bo) 175037b3c26Smrg goto out_unlock; 176037b3c26Smrg 177037b3c26Smrg if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) { 178037b3c26Smrg ERROR_MSG("gem-open failed: %s", strerror(errno)); 179037b3c26Smrg goto out_unlock; 180037b3c26Smrg } 181037b3c26Smrg 182037b3c26Smrg bo = lookup_bo(dev->handle_table, req.handle); 183037b3c26Smrg if (bo) 184037b3c26Smrg goto out_unlock; 185037b3c26Smrg 186037b3c26Smrg bo = bo_from_handle(dev, req.size, req.handle, 0); 187037b3c26Smrg if (bo) 188037b3c26Smrg set_name(bo, name); 189037b3c26Smrg 190037b3c26Smrgout_unlock: 191037b3c26Smrg pthread_mutex_unlock(&table_lock); 192037b3c26Smrg 193037b3c26Smrg return bo; 194037b3c26Smrg} 195037b3c26Smrg 196037b3c26Smrg/* import a buffer from dmabuf fd, does not take ownership of the 197037b3c26Smrg * fd so caller should close() the fd when it is otherwise done 198037b3c26Smrg * with it (even if it is still using the 'struct etna_bo *') 199037b3c26Smrg */ 2007cdc0497Smrgdrm_public struct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd) 201037b3c26Smrg{ 202037b3c26Smrg struct etna_bo *bo; 203037b3c26Smrg int ret, size; 204037b3c26Smrg uint32_t handle; 205037b3c26Smrg 20600a23bdaSmrg /* take the lock before calling drmPrimeFDToHandle to avoid 20700a23bdaSmrg * racing against etna_bo_del, which might invalidate the 20800a23bdaSmrg * returned handle. 20900a23bdaSmrg */ 210037b3c26Smrg pthread_mutex_lock(&table_lock); 211037b3c26Smrg 212037b3c26Smrg ret = drmPrimeFDToHandle(dev->fd, fd, &handle); 213037b3c26Smrg if (ret) { 21400a23bdaSmrg pthread_mutex_unlock(&table_lock); 215037b3c26Smrg return NULL; 216037b3c26Smrg } 217037b3c26Smrg 218037b3c26Smrg bo = lookup_bo(dev->handle_table, handle); 219037b3c26Smrg if (bo) 220037b3c26Smrg goto out_unlock; 221037b3c26Smrg 222037b3c26Smrg /* lseek() to get bo size */ 223037b3c26Smrg size = lseek(fd, 0, SEEK_END); 224037b3c26Smrg lseek(fd, 0, SEEK_CUR); 225037b3c26Smrg 226037b3c26Smrg bo = bo_from_handle(dev, size, handle, 0); 227037b3c26Smrg 228037b3c26Smrgout_unlock: 229037b3c26Smrg pthread_mutex_unlock(&table_lock); 230037b3c26Smrg 231037b3c26Smrg return bo; 232037b3c26Smrg} 233037b3c26Smrg 234037b3c26Smrg/* destroy a buffer object */ 2357cdc0497Smrgdrm_public void etna_bo_del(struct etna_bo *bo) 236037b3c26Smrg{ 237037b3c26Smrg struct etna_device *dev = bo->dev; 238037b3c26Smrg 239037b3c26Smrg if (!bo) 240037b3c26Smrg return; 241037b3c26Smrg 242037b3c26Smrg if (!atomic_dec_and_test(&bo->refcnt)) 243037b3c26Smrg return; 244037b3c26Smrg 245037b3c26Smrg pthread_mutex_lock(&table_lock); 246037b3c26Smrg 247037b3c26Smrg if (bo->reuse && (etna_bo_cache_free(&dev->bo_cache, bo) == 0)) 248037b3c26Smrg goto out; 249037b3c26Smrg 250037b3c26Smrg bo_del(bo); 251037b3c26Smrg etna_device_del_locked(dev); 252037b3c26Smrgout: 253037b3c26Smrg pthread_mutex_unlock(&table_lock); 254037b3c26Smrg} 255037b3c26Smrg 256037b3c26Smrg/* get the global flink/DRI2 buffer name */ 2577cdc0497Smrgdrm_public int etna_bo_get_name(struct etna_bo *bo, uint32_t *name) 258037b3c26Smrg{ 259037b3c26Smrg if (!bo->name) { 260037b3c26Smrg struct drm_gem_flink req = { 261037b3c26Smrg .handle = bo->handle, 262037b3c26Smrg }; 263037b3c26Smrg int ret; 264037b3c26Smrg 265037b3c26Smrg ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req); 266037b3c26Smrg if (ret) { 267037b3c26Smrg return ret; 268037b3c26Smrg } 269037b3c26Smrg 270037b3c26Smrg pthread_mutex_lock(&table_lock); 271037b3c26Smrg set_name(bo, req.name); 272037b3c26Smrg pthread_mutex_unlock(&table_lock); 273037b3c26Smrg bo->reuse = 0; 274037b3c26Smrg } 275037b3c26Smrg 276037b3c26Smrg *name = bo->name; 277037b3c26Smrg 278037b3c26Smrg return 0; 279037b3c26Smrg} 280037b3c26Smrg 2817cdc0497Smrgdrm_public uint32_t etna_bo_handle(struct etna_bo *bo) 282037b3c26Smrg{ 283037b3c26Smrg return bo->handle; 284037b3c26Smrg} 285037b3c26Smrg 286037b3c26Smrg/* caller owns the dmabuf fd that is returned and is responsible 287037b3c26Smrg * to close() it when done 288037b3c26Smrg */ 2897cdc0497Smrgdrm_public int etna_bo_dmabuf(struct etna_bo *bo) 290037b3c26Smrg{ 291037b3c26Smrg int ret, prime_fd; 292037b3c26Smrg 293037b3c26Smrg ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC, 294037b3c26Smrg &prime_fd); 295037b3c26Smrg if (ret) { 296037b3c26Smrg ERROR_MSG("failed to get dmabuf fd: %d", ret); 297037b3c26Smrg return ret; 298037b3c26Smrg } 299037b3c26Smrg 300037b3c26Smrg bo->reuse = 0; 301037b3c26Smrg 302037b3c26Smrg return prime_fd; 303037b3c26Smrg} 304037b3c26Smrg 3057cdc0497Smrgdrm_public uint32_t etna_bo_size(struct etna_bo *bo) 306037b3c26Smrg{ 307037b3c26Smrg return bo->size; 308037b3c26Smrg} 309037b3c26Smrg 3107cdc0497Smrgdrm_public void *etna_bo_map(struct etna_bo *bo) 311037b3c26Smrg{ 312037b3c26Smrg if (!bo->map) { 313037b3c26Smrg if (!bo->offset) { 314037b3c26Smrg get_buffer_info(bo); 315037b3c26Smrg } 316037b3c26Smrg 317037b3c26Smrg bo->map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE, 318037b3c26Smrg MAP_SHARED, bo->dev->fd, bo->offset); 319037b3c26Smrg if (bo->map == MAP_FAILED) { 320037b3c26Smrg ERROR_MSG("mmap failed: %s", strerror(errno)); 321037b3c26Smrg bo->map = NULL; 322037b3c26Smrg } 323037b3c26Smrg } 324037b3c26Smrg 325037b3c26Smrg return bo->map; 326037b3c26Smrg} 327037b3c26Smrg 3287cdc0497Smrgdrm_public int etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op) 329037b3c26Smrg{ 330037b3c26Smrg struct drm_etnaviv_gem_cpu_prep req = { 331037b3c26Smrg .handle = bo->handle, 332037b3c26Smrg .op = op, 333037b3c26Smrg }; 334037b3c26Smrg 335037b3c26Smrg get_abs_timeout(&req.timeout, 5000000000); 336037b3c26Smrg 337037b3c26Smrg return drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_PREP, 338037b3c26Smrg &req, sizeof(req)); 339037b3c26Smrg} 340037b3c26Smrg 3417cdc0497Smrgdrm_public void etna_bo_cpu_fini(struct etna_bo *bo) 342037b3c26Smrg{ 343037b3c26Smrg struct drm_etnaviv_gem_cpu_fini req = { 344037b3c26Smrg .handle = bo->handle, 345037b3c26Smrg }; 346037b3c26Smrg 347037b3c26Smrg drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_FINI, 348037b3c26Smrg &req, sizeof(req)); 349037b3c26Smrg} 350