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