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