1/* 2 * Copyright (C) 2017-2019 Lima 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 shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 */ 23 24#include <stdlib.h> 25#include <sys/types.h> 26#include <unistd.h> 27#include <fcntl.h> 28 29#include "xf86drm.h" 30#include "drm-uapi/lima_drm.h" 31 32#include "util/u_hash_table.h" 33#include "util/os_time.h" 34#include "os/os_mman.h" 35 36#include "state_tracker/drm_driver.h" 37 38#include "lima_screen.h" 39#include "lima_bo.h" 40 41#define PTR_TO_UINT(x) ((unsigned)((intptr_t)(x))) 42 43static unsigned handle_hash(void *key) 44{ 45 return PTR_TO_UINT(key); 46} 47 48static int handle_compare(void *key1, void *key2) 49{ 50 return PTR_TO_UINT(key1) != PTR_TO_UINT(key2); 51} 52 53bool lima_bo_table_init(struct lima_screen *screen) 54{ 55 screen->bo_handles = util_hash_table_create(handle_hash, handle_compare); 56 if (!screen->bo_handles) 57 return false; 58 59 screen->bo_flink_names = util_hash_table_create(handle_hash, handle_compare); 60 if (!screen->bo_flink_names) 61 goto err_out0; 62 63 mtx_init(&screen->bo_table_lock, mtx_plain); 64 return true; 65 66err_out0: 67 util_hash_table_destroy(screen->bo_handles); 68 return false; 69} 70 71void lima_bo_table_fini(struct lima_screen *screen) 72{ 73 mtx_destroy(&screen->bo_table_lock); 74 util_hash_table_destroy(screen->bo_handles); 75 util_hash_table_destroy(screen->bo_flink_names); 76} 77 78static void lima_close_kms_handle(struct lima_screen *screen, uint32_t handle) 79{ 80 struct drm_gem_close args = { 81 .handle = handle, 82 }; 83 84 drmIoctl(screen->fd, DRM_IOCTL_GEM_CLOSE, &args); 85} 86 87static bool lima_bo_get_info(struct lima_bo *bo) 88{ 89 struct drm_lima_gem_info req = { 90 .handle = bo->handle, 91 }; 92 93 if(drmIoctl(bo->screen->fd, DRM_IOCTL_LIMA_GEM_INFO, &req)) 94 return false; 95 96 bo->offset = req.offset; 97 bo->va = req.va; 98 return true; 99} 100 101struct lima_bo *lima_bo_create(struct lima_screen *screen, 102 uint32_t size, uint32_t flags) 103{ 104 struct lima_bo *bo; 105 struct drm_lima_gem_create req = { 106 .size = size, 107 .flags = flags, 108 }; 109 110 if (!(bo = calloc(1, sizeof(*bo)))) 111 return NULL; 112 113 if (drmIoctl(screen->fd, DRM_IOCTL_LIMA_GEM_CREATE, &req)) 114 goto err_out0; 115 116 bo->screen = screen; 117 bo->size = req.size; 118 bo->handle = req.handle; 119 p_atomic_set(&bo->refcnt, 1); 120 121 if (!lima_bo_get_info(bo)) 122 goto err_out1; 123 124 return bo; 125 126err_out1: 127 lima_close_kms_handle(screen, bo->handle); 128err_out0: 129 free(bo); 130 return NULL; 131} 132 133void lima_bo_free(struct lima_bo *bo) 134{ 135 if (!p_atomic_dec_zero(&bo->refcnt)) 136 return; 137 138 struct lima_screen *screen = bo->screen; 139 mtx_lock(&screen->bo_table_lock); 140 util_hash_table_remove(screen->bo_handles, 141 (void *)(uintptr_t)bo->handle); 142 if (bo->flink_name) 143 util_hash_table_remove(screen->bo_flink_names, 144 (void *)(uintptr_t)bo->flink_name); 145 mtx_unlock(&screen->bo_table_lock); 146 147 if (bo->map) 148 lima_bo_unmap(bo); 149 150 lima_close_kms_handle(screen, bo->handle); 151 free(bo); 152} 153 154void *lima_bo_map(struct lima_bo *bo) 155{ 156 if (!bo->map) { 157 bo->map = os_mmap(0, bo->size, PROT_READ | PROT_WRITE, 158 MAP_SHARED, bo->screen->fd, bo->offset); 159 if (bo->map == MAP_FAILED) 160 bo->map = NULL; 161 } 162 163 return bo->map; 164} 165 166void lima_bo_unmap(struct lima_bo *bo) 167{ 168 if (bo->map) { 169 os_munmap(bo->map, bo->size); 170 bo->map = NULL; 171 } 172} 173 174bool lima_bo_export(struct lima_bo *bo, struct winsys_handle *handle) 175{ 176 struct lima_screen *screen = bo->screen; 177 178 switch (handle->type) { 179 case WINSYS_HANDLE_TYPE_SHARED: 180 if (!bo->flink_name) { 181 struct drm_gem_flink flink = { 182 .handle = bo->handle, 183 .name = 0, 184 }; 185 if (drmIoctl(screen->fd, DRM_IOCTL_GEM_FLINK, &flink)) 186 return false; 187 188 bo->flink_name = flink.name; 189 190 mtx_lock(&screen->bo_table_lock); 191 util_hash_table_set(screen->bo_flink_names, 192 (void *)(uintptr_t)bo->flink_name, bo); 193 mtx_unlock(&screen->bo_table_lock); 194 } 195 handle->handle = bo->flink_name; 196 return true; 197 198 case WINSYS_HANDLE_TYPE_KMS: 199 mtx_lock(&screen->bo_table_lock); 200 util_hash_table_set(screen->bo_handles, 201 (void *)(uintptr_t)bo->handle, bo); 202 mtx_unlock(&screen->bo_table_lock); 203 204 handle->handle = bo->handle; 205 return true; 206 207 case WINSYS_HANDLE_TYPE_FD: 208 if (drmPrimeHandleToFD(screen->fd, bo->handle, DRM_CLOEXEC, 209 (int*)&handle->handle)) 210 return false; 211 212 mtx_lock(&screen->bo_table_lock); 213 util_hash_table_set(screen->bo_handles, 214 (void *)(uintptr_t)bo->handle, bo); 215 mtx_unlock(&screen->bo_table_lock); 216 return true; 217 218 default: 219 return false; 220 } 221} 222 223struct lima_bo *lima_bo_import(struct lima_screen *screen, 224 struct winsys_handle *handle) 225{ 226 struct lima_bo *bo = NULL; 227 struct drm_gem_open req = {0}; 228 uint32_t dma_buf_size = 0; 229 unsigned h = handle->handle; 230 231 mtx_lock(&screen->bo_table_lock); 232 233 /* Convert a DMA buf handle to a KMS handle now. */ 234 if (handle->type == WINSYS_HANDLE_TYPE_FD) { 235 uint32_t prime_handle; 236 off_t size; 237 238 /* Get a KMS handle. */ 239 if (drmPrimeFDToHandle(screen->fd, h, &prime_handle)) { 240 mtx_unlock(&screen->bo_table_lock); 241 return NULL; 242 } 243 244 /* Query the buffer size. */ 245 size = lseek(h, 0, SEEK_END); 246 if (size == (off_t)-1) { 247 mtx_unlock(&screen->bo_table_lock); 248 lima_close_kms_handle(screen, prime_handle); 249 return NULL; 250 } 251 lseek(h, 0, SEEK_SET); 252 253 dma_buf_size = size; 254 h = prime_handle; 255 } 256 257 switch (handle->type) { 258 case WINSYS_HANDLE_TYPE_SHARED: 259 bo = util_hash_table_get(screen->bo_flink_names, 260 (void *)(uintptr_t)h); 261 break; 262 case WINSYS_HANDLE_TYPE_KMS: 263 case WINSYS_HANDLE_TYPE_FD: 264 bo = util_hash_table_get(screen->bo_handles, 265 (void *)(uintptr_t)h); 266 break; 267 default: 268 mtx_unlock(&screen->bo_table_lock); 269 return NULL; 270 } 271 272 if (bo) { 273 p_atomic_inc(&bo->refcnt); 274 mtx_unlock(&screen->bo_table_lock); 275 return bo; 276 } 277 278 if (!(bo = calloc(1, sizeof(*bo)))) { 279 mtx_unlock(&screen->bo_table_lock); 280 if (handle->type == WINSYS_HANDLE_TYPE_FD) 281 lima_close_kms_handle(screen, h); 282 return NULL; 283 } 284 285 bo->screen = screen; 286 p_atomic_set(&bo->refcnt, 1); 287 288 switch (handle->type) { 289 case WINSYS_HANDLE_TYPE_SHARED: 290 req.name = h; 291 if (drmIoctl(screen->fd, DRM_IOCTL_GEM_OPEN, &req)) { 292 mtx_unlock(&screen->bo_table_lock); 293 free(bo); 294 return NULL; 295 } 296 bo->handle = req.handle; 297 bo->flink_name = h; 298 bo->size = req.size; 299 break; 300 case WINSYS_HANDLE_TYPE_FD: 301 bo->handle = h; 302 bo->size = dma_buf_size; 303 break; 304 default: 305 /* not possible */ 306 assert(0); 307 } 308 309 if (lima_bo_get_info(bo)) { 310 if (handle->type == WINSYS_HANDLE_TYPE_SHARED) 311 util_hash_table_set(screen->bo_flink_names, 312 (void *)(uintptr_t)bo->flink_name, bo); 313 util_hash_table_set(screen->bo_handles, 314 (void*)(uintptr_t)bo->handle, bo); 315 } 316 else { 317 lima_close_kms_handle(screen, bo->handle); 318 free(bo); 319 bo = NULL; 320 } 321 322 mtx_unlock(&screen->bo_table_lock); 323 324 return bo; 325} 326 327bool lima_bo_wait(struct lima_bo *bo, uint32_t op, uint64_t timeout_ns) 328{ 329 int64_t abs_timeout = os_time_get_absolute_timeout(timeout_ns); 330 struct drm_lima_gem_wait req = { 331 .handle = bo->handle, 332 .op = op, 333 .timeout_ns = abs_timeout, 334 }; 335 336 return drmIoctl(bo->screen->fd, DRM_IOCTL_LIMA_GEM_WAIT, &req) == 0; 337} 338