1d722e3fbSopenharmony_ci/* 2d722e3fbSopenharmony_ci * Copyright (C) 2014 Etnaviv Project 3d722e3fbSopenharmony_ci * 4d722e3fbSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5d722e3fbSopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6d722e3fbSopenharmony_ci * to deal in the Software without restriction, including without limitation 7d722e3fbSopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8d722e3fbSopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9d722e3fbSopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10d722e3fbSopenharmony_ci * 11d722e3fbSopenharmony_ci * The above copyright notice and this permission notice (including the next 12d722e3fbSopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13d722e3fbSopenharmony_ci * Software. 14d722e3fbSopenharmony_ci * 15d722e3fbSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16d722e3fbSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17d722e3fbSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18d722e3fbSopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19d722e3fbSopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20d722e3fbSopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21d722e3fbSopenharmony_ci * SOFTWARE. 22d722e3fbSopenharmony_ci * 23d722e3fbSopenharmony_ci * Authors: 24d722e3fbSopenharmony_ci * Christian Gmeiner <christian.gmeiner@gmail.com> 25d722e3fbSopenharmony_ci */ 26d722e3fbSopenharmony_ci 27d722e3fbSopenharmony_ci#include "etnaviv_priv.h" 28d722e3fbSopenharmony_ci#include "etnaviv_drmif.h" 29d722e3fbSopenharmony_ci 30d722e3fbSopenharmony_cidrm_private pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER; 31d722e3fbSopenharmony_cidrm_private void bo_del(struct etna_bo *bo); 32d722e3fbSopenharmony_ci 33d722e3fbSopenharmony_ci/* set buffer name, and add to table, call w/ table_lock held: */ 34d722e3fbSopenharmony_cistatic void set_name(struct etna_bo *bo, uint32_t name) 35d722e3fbSopenharmony_ci{ 36d722e3fbSopenharmony_ci bo->name = name; 37d722e3fbSopenharmony_ci /* add ourself into the name table: */ 38d722e3fbSopenharmony_ci drmHashInsert(bo->dev->name_table, name, bo); 39d722e3fbSopenharmony_ci} 40d722e3fbSopenharmony_ci 41d722e3fbSopenharmony_ci/* Called under table_lock */ 42d722e3fbSopenharmony_cidrm_private void bo_del(struct etna_bo *bo) 43d722e3fbSopenharmony_ci{ 44d722e3fbSopenharmony_ci if (bo->map) 45d722e3fbSopenharmony_ci drm_munmap(bo->map, bo->size); 46d722e3fbSopenharmony_ci 47d722e3fbSopenharmony_ci if (bo->name) 48d722e3fbSopenharmony_ci drmHashDelete(bo->dev->name_table, bo->name); 49d722e3fbSopenharmony_ci 50d722e3fbSopenharmony_ci if (bo->handle) { 51d722e3fbSopenharmony_ci drmHashDelete(bo->dev->handle_table, bo->handle); 52d722e3fbSopenharmony_ci drmCloseBufferHandle(bo->dev->fd, bo->handle); 53d722e3fbSopenharmony_ci } 54d722e3fbSopenharmony_ci 55d722e3fbSopenharmony_ci free(bo); 56d722e3fbSopenharmony_ci} 57d722e3fbSopenharmony_ci 58d722e3fbSopenharmony_ci/* lookup a buffer from it's handle, call w/ table_lock held: */ 59d722e3fbSopenharmony_cistatic struct etna_bo *lookup_bo(void *tbl, uint32_t handle) 60d722e3fbSopenharmony_ci{ 61d722e3fbSopenharmony_ci struct etna_bo *bo = NULL; 62d722e3fbSopenharmony_ci 63d722e3fbSopenharmony_ci if (!drmHashLookup(tbl, handle, (void **)&bo)) { 64d722e3fbSopenharmony_ci /* found, incr refcnt and return: */ 65d722e3fbSopenharmony_ci bo = etna_bo_ref(bo); 66d722e3fbSopenharmony_ci 67d722e3fbSopenharmony_ci /* don't break the bucket if this bo was found in one */ 68d722e3fbSopenharmony_ci list_delinit(&bo->list); 69d722e3fbSopenharmony_ci } 70d722e3fbSopenharmony_ci 71d722e3fbSopenharmony_ci return bo; 72d722e3fbSopenharmony_ci} 73d722e3fbSopenharmony_ci 74d722e3fbSopenharmony_ci/* allocate a new buffer object, call w/ table_lock held */ 75d722e3fbSopenharmony_cistatic struct etna_bo *bo_from_handle(struct etna_device *dev, 76d722e3fbSopenharmony_ci uint32_t size, uint32_t handle, uint32_t flags) 77d722e3fbSopenharmony_ci{ 78d722e3fbSopenharmony_ci struct etna_bo *bo = calloc(sizeof(*bo), 1); 79d722e3fbSopenharmony_ci 80d722e3fbSopenharmony_ci if (!bo) { 81d722e3fbSopenharmony_ci drmCloseBufferHandle(dev->fd, handle); 82d722e3fbSopenharmony_ci return NULL; 83d722e3fbSopenharmony_ci } 84d722e3fbSopenharmony_ci 85d722e3fbSopenharmony_ci bo->dev = etna_device_ref(dev); 86d722e3fbSopenharmony_ci bo->size = size; 87d722e3fbSopenharmony_ci bo->handle = handle; 88d722e3fbSopenharmony_ci bo->flags = flags; 89d722e3fbSopenharmony_ci atomic_set(&bo->refcnt, 1); 90d722e3fbSopenharmony_ci list_inithead(&bo->list); 91d722e3fbSopenharmony_ci /* add ourselves to the handle table: */ 92d722e3fbSopenharmony_ci drmHashInsert(dev->handle_table, handle, bo); 93d722e3fbSopenharmony_ci 94d722e3fbSopenharmony_ci return bo; 95d722e3fbSopenharmony_ci} 96d722e3fbSopenharmony_ci 97d722e3fbSopenharmony_ci/* allocate a new (un-tiled) buffer object */ 98d722e3fbSopenharmony_cidrm_public struct etna_bo *etna_bo_new(struct etna_device *dev, uint32_t size, 99d722e3fbSopenharmony_ci uint32_t flags) 100d722e3fbSopenharmony_ci{ 101d722e3fbSopenharmony_ci struct etna_bo *bo; 102d722e3fbSopenharmony_ci int ret; 103d722e3fbSopenharmony_ci struct drm_etnaviv_gem_new req = { 104d722e3fbSopenharmony_ci .flags = flags, 105d722e3fbSopenharmony_ci }; 106d722e3fbSopenharmony_ci 107d722e3fbSopenharmony_ci bo = etna_bo_cache_alloc(&dev->bo_cache, &size, flags); 108d722e3fbSopenharmony_ci if (bo) 109d722e3fbSopenharmony_ci return bo; 110d722e3fbSopenharmony_ci 111d722e3fbSopenharmony_ci req.size = size; 112d722e3fbSopenharmony_ci ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_GEM_NEW, 113d722e3fbSopenharmony_ci &req, sizeof(req)); 114d722e3fbSopenharmony_ci if (ret) 115d722e3fbSopenharmony_ci return NULL; 116d722e3fbSopenharmony_ci 117d722e3fbSopenharmony_ci pthread_mutex_lock(&table_lock); 118d722e3fbSopenharmony_ci bo = bo_from_handle(dev, size, req.handle, flags); 119d722e3fbSopenharmony_ci bo->reuse = 1; 120d722e3fbSopenharmony_ci pthread_mutex_unlock(&table_lock); 121d722e3fbSopenharmony_ci 122d722e3fbSopenharmony_ci return bo; 123d722e3fbSopenharmony_ci} 124d722e3fbSopenharmony_ci 125d722e3fbSopenharmony_cidrm_public struct etna_bo *etna_bo_ref(struct etna_bo *bo) 126d722e3fbSopenharmony_ci{ 127d722e3fbSopenharmony_ci atomic_inc(&bo->refcnt); 128d722e3fbSopenharmony_ci 129d722e3fbSopenharmony_ci return bo; 130d722e3fbSopenharmony_ci} 131d722e3fbSopenharmony_ci 132d722e3fbSopenharmony_ci/* get buffer info */ 133d722e3fbSopenharmony_cistatic int get_buffer_info(struct etna_bo *bo) 134d722e3fbSopenharmony_ci{ 135d722e3fbSopenharmony_ci int ret; 136d722e3fbSopenharmony_ci struct drm_etnaviv_gem_info req = { 137d722e3fbSopenharmony_ci .handle = bo->handle, 138d722e3fbSopenharmony_ci }; 139d722e3fbSopenharmony_ci 140d722e3fbSopenharmony_ci ret = drmCommandWriteRead(bo->dev->fd, DRM_ETNAVIV_GEM_INFO, 141d722e3fbSopenharmony_ci &req, sizeof(req)); 142d722e3fbSopenharmony_ci if (ret) { 143d722e3fbSopenharmony_ci return ret; 144d722e3fbSopenharmony_ci } 145d722e3fbSopenharmony_ci 146d722e3fbSopenharmony_ci /* really all we need for now is mmap offset */ 147d722e3fbSopenharmony_ci bo->offset = req.offset; 148d722e3fbSopenharmony_ci 149d722e3fbSopenharmony_ci return 0; 150d722e3fbSopenharmony_ci} 151d722e3fbSopenharmony_ci 152d722e3fbSopenharmony_ci/* import a buffer object from DRI2 name */ 153d722e3fbSopenharmony_cidrm_public struct etna_bo *etna_bo_from_name(struct etna_device *dev, 154d722e3fbSopenharmony_ci uint32_t name) 155d722e3fbSopenharmony_ci{ 156d722e3fbSopenharmony_ci struct etna_bo *bo; 157d722e3fbSopenharmony_ci struct drm_gem_open req = { 158d722e3fbSopenharmony_ci .name = name, 159d722e3fbSopenharmony_ci }; 160d722e3fbSopenharmony_ci 161d722e3fbSopenharmony_ci pthread_mutex_lock(&table_lock); 162d722e3fbSopenharmony_ci 163d722e3fbSopenharmony_ci /* check name table first, to see if bo is already open: */ 164d722e3fbSopenharmony_ci bo = lookup_bo(dev->name_table, name); 165d722e3fbSopenharmony_ci if (bo) 166d722e3fbSopenharmony_ci goto out_unlock; 167d722e3fbSopenharmony_ci 168d722e3fbSopenharmony_ci if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) { 169d722e3fbSopenharmony_ci ERROR_MSG("gem-open failed: %s", strerror(errno)); 170d722e3fbSopenharmony_ci goto out_unlock; 171d722e3fbSopenharmony_ci } 172d722e3fbSopenharmony_ci 173d722e3fbSopenharmony_ci bo = lookup_bo(dev->handle_table, req.handle); 174d722e3fbSopenharmony_ci if (bo) 175d722e3fbSopenharmony_ci goto out_unlock; 176d722e3fbSopenharmony_ci 177d722e3fbSopenharmony_ci bo = bo_from_handle(dev, req.size, req.handle, 0); 178d722e3fbSopenharmony_ci if (bo) 179d722e3fbSopenharmony_ci set_name(bo, name); 180d722e3fbSopenharmony_ci 181d722e3fbSopenharmony_ciout_unlock: 182d722e3fbSopenharmony_ci pthread_mutex_unlock(&table_lock); 183d722e3fbSopenharmony_ci 184d722e3fbSopenharmony_ci return bo; 185d722e3fbSopenharmony_ci} 186d722e3fbSopenharmony_ci 187d722e3fbSopenharmony_ci/* import a buffer from dmabuf fd, does not take ownership of the 188d722e3fbSopenharmony_ci * fd so caller should close() the fd when it is otherwise done 189d722e3fbSopenharmony_ci * with it (even if it is still using the 'struct etna_bo *') 190d722e3fbSopenharmony_ci */ 191d722e3fbSopenharmony_cidrm_public struct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd) 192d722e3fbSopenharmony_ci{ 193d722e3fbSopenharmony_ci struct etna_bo *bo; 194d722e3fbSopenharmony_ci int ret, size; 195d722e3fbSopenharmony_ci uint32_t handle; 196d722e3fbSopenharmony_ci 197d722e3fbSopenharmony_ci /* take the lock before calling drmPrimeFDToHandle to avoid 198d722e3fbSopenharmony_ci * racing against etna_bo_del, which might invalidate the 199d722e3fbSopenharmony_ci * returned handle. 200d722e3fbSopenharmony_ci */ 201d722e3fbSopenharmony_ci pthread_mutex_lock(&table_lock); 202d722e3fbSopenharmony_ci 203d722e3fbSopenharmony_ci ret = drmPrimeFDToHandle(dev->fd, fd, &handle); 204d722e3fbSopenharmony_ci if (ret) { 205d722e3fbSopenharmony_ci pthread_mutex_unlock(&table_lock); 206d722e3fbSopenharmony_ci return NULL; 207d722e3fbSopenharmony_ci } 208d722e3fbSopenharmony_ci 209d722e3fbSopenharmony_ci bo = lookup_bo(dev->handle_table, handle); 210d722e3fbSopenharmony_ci if (bo) 211d722e3fbSopenharmony_ci goto out_unlock; 212d722e3fbSopenharmony_ci 213d722e3fbSopenharmony_ci /* lseek() to get bo size */ 214d722e3fbSopenharmony_ci size = lseek(fd, 0, SEEK_END); 215d722e3fbSopenharmony_ci lseek(fd, 0, SEEK_CUR); 216d722e3fbSopenharmony_ci 217d722e3fbSopenharmony_ci bo = bo_from_handle(dev, size, handle, 0); 218d722e3fbSopenharmony_ci 219d722e3fbSopenharmony_ciout_unlock: 220d722e3fbSopenharmony_ci pthread_mutex_unlock(&table_lock); 221d722e3fbSopenharmony_ci 222d722e3fbSopenharmony_ci return bo; 223d722e3fbSopenharmony_ci} 224d722e3fbSopenharmony_ci 225d722e3fbSopenharmony_ci/* destroy a buffer object */ 226d722e3fbSopenharmony_cidrm_public void etna_bo_del(struct etna_bo *bo) 227d722e3fbSopenharmony_ci{ 228d722e3fbSopenharmony_ci struct etna_device *dev = bo->dev; 229d722e3fbSopenharmony_ci 230d722e3fbSopenharmony_ci if (!bo) 231d722e3fbSopenharmony_ci return; 232d722e3fbSopenharmony_ci 233d722e3fbSopenharmony_ci if (!atomic_dec_and_test(&bo->refcnt)) 234d722e3fbSopenharmony_ci return; 235d722e3fbSopenharmony_ci 236d722e3fbSopenharmony_ci pthread_mutex_lock(&table_lock); 237d722e3fbSopenharmony_ci 238d722e3fbSopenharmony_ci if (bo->reuse && (etna_bo_cache_free(&dev->bo_cache, bo) == 0)) 239d722e3fbSopenharmony_ci goto out; 240d722e3fbSopenharmony_ci 241d722e3fbSopenharmony_ci bo_del(bo); 242d722e3fbSopenharmony_ci etna_device_del_locked(dev); 243d722e3fbSopenharmony_ciout: 244d722e3fbSopenharmony_ci pthread_mutex_unlock(&table_lock); 245d722e3fbSopenharmony_ci} 246d722e3fbSopenharmony_ci 247d722e3fbSopenharmony_ci/* get the global flink/DRI2 buffer name */ 248d722e3fbSopenharmony_cidrm_public int etna_bo_get_name(struct etna_bo *bo, uint32_t *name) 249d722e3fbSopenharmony_ci{ 250d722e3fbSopenharmony_ci if (!bo->name) { 251d722e3fbSopenharmony_ci struct drm_gem_flink req = { 252d722e3fbSopenharmony_ci .handle = bo->handle, 253d722e3fbSopenharmony_ci }; 254d722e3fbSopenharmony_ci int ret; 255d722e3fbSopenharmony_ci 256d722e3fbSopenharmony_ci ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req); 257d722e3fbSopenharmony_ci if (ret) { 258d722e3fbSopenharmony_ci return ret; 259d722e3fbSopenharmony_ci } 260d722e3fbSopenharmony_ci 261d722e3fbSopenharmony_ci pthread_mutex_lock(&table_lock); 262d722e3fbSopenharmony_ci set_name(bo, req.name); 263d722e3fbSopenharmony_ci pthread_mutex_unlock(&table_lock); 264d722e3fbSopenharmony_ci bo->reuse = 0; 265d722e3fbSopenharmony_ci } 266d722e3fbSopenharmony_ci 267d722e3fbSopenharmony_ci *name = bo->name; 268d722e3fbSopenharmony_ci 269d722e3fbSopenharmony_ci return 0; 270d722e3fbSopenharmony_ci} 271d722e3fbSopenharmony_ci 272d722e3fbSopenharmony_cidrm_public uint32_t etna_bo_handle(struct etna_bo *bo) 273d722e3fbSopenharmony_ci{ 274d722e3fbSopenharmony_ci return bo->handle; 275d722e3fbSopenharmony_ci} 276d722e3fbSopenharmony_ci 277d722e3fbSopenharmony_ci/* caller owns the dmabuf fd that is returned and is responsible 278d722e3fbSopenharmony_ci * to close() it when done 279d722e3fbSopenharmony_ci */ 280d722e3fbSopenharmony_cidrm_public int etna_bo_dmabuf(struct etna_bo *bo) 281d722e3fbSopenharmony_ci{ 282d722e3fbSopenharmony_ci int ret, prime_fd; 283d722e3fbSopenharmony_ci 284d722e3fbSopenharmony_ci ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC, 285d722e3fbSopenharmony_ci &prime_fd); 286d722e3fbSopenharmony_ci if (ret) { 287d722e3fbSopenharmony_ci ERROR_MSG("failed to get dmabuf fd: %d", ret); 288d722e3fbSopenharmony_ci return ret; 289d722e3fbSopenharmony_ci } 290d722e3fbSopenharmony_ci 291d722e3fbSopenharmony_ci bo->reuse = 0; 292d722e3fbSopenharmony_ci 293d722e3fbSopenharmony_ci return prime_fd; 294d722e3fbSopenharmony_ci} 295d722e3fbSopenharmony_ci 296d722e3fbSopenharmony_cidrm_public uint32_t etna_bo_size(struct etna_bo *bo) 297d722e3fbSopenharmony_ci{ 298d722e3fbSopenharmony_ci return bo->size; 299d722e3fbSopenharmony_ci} 300d722e3fbSopenharmony_ci 301d722e3fbSopenharmony_cidrm_public void *etna_bo_map(struct etna_bo *bo) 302d722e3fbSopenharmony_ci{ 303d722e3fbSopenharmony_ci if (!bo->map) { 304d722e3fbSopenharmony_ci if (!bo->offset) { 305d722e3fbSopenharmony_ci get_buffer_info(bo); 306d722e3fbSopenharmony_ci } 307d722e3fbSopenharmony_ci 308d722e3fbSopenharmony_ci bo->map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE, 309d722e3fbSopenharmony_ci MAP_SHARED, bo->dev->fd, bo->offset); 310d722e3fbSopenharmony_ci if (bo->map == MAP_FAILED) { 311d722e3fbSopenharmony_ci ERROR_MSG("mmap failed: %s", strerror(errno)); 312d722e3fbSopenharmony_ci bo->map = NULL; 313d722e3fbSopenharmony_ci } 314d722e3fbSopenharmony_ci } 315d722e3fbSopenharmony_ci 316d722e3fbSopenharmony_ci return bo->map; 317d722e3fbSopenharmony_ci} 318d722e3fbSopenharmony_ci 319d722e3fbSopenharmony_cidrm_public int etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op) 320d722e3fbSopenharmony_ci{ 321d722e3fbSopenharmony_ci struct drm_etnaviv_gem_cpu_prep req = { 322d722e3fbSopenharmony_ci .handle = bo->handle, 323d722e3fbSopenharmony_ci .op = op, 324d722e3fbSopenharmony_ci }; 325d722e3fbSopenharmony_ci 326d722e3fbSopenharmony_ci get_abs_timeout(&req.timeout, 5000000000); 327d722e3fbSopenharmony_ci 328d722e3fbSopenharmony_ci return drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_PREP, 329d722e3fbSopenharmony_ci &req, sizeof(req)); 330d722e3fbSopenharmony_ci} 331d722e3fbSopenharmony_ci 332d722e3fbSopenharmony_cidrm_public void etna_bo_cpu_fini(struct etna_bo *bo) 333d722e3fbSopenharmony_ci{ 334d722e3fbSopenharmony_ci struct drm_etnaviv_gem_cpu_fini req = { 335d722e3fbSopenharmony_ci .handle = bo->handle, 336d722e3fbSopenharmony_ci }; 337d722e3fbSopenharmony_ci 338d722e3fbSopenharmony_ci drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_FINI, 339d722e3fbSopenharmony_ci &req, sizeof(req)); 340d722e3fbSopenharmony_ci} 341