1/* 2 * Copyright (C) 2021 Collabora Ltd. 3 * 4 * Derived from tu_drm.c which is: 5 * Copyright © 2018 Google, Inc. 6 * Copyright © 2015 Intel Corporation 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the "Software"), 10 * to deal in the Software without restriction, including without limitation 11 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 * and/or sell copies of the Software, and to permit persons to whom the 13 * Software is furnished to do so, subject to the following conditions: 14 * 15 * The above copyright notice and this permission notice (including the next 16 * paragraph) shall be included in all copies or substantial portions of the 17 * Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 * DEALINGS IN THE SOFTWARE. 26 */ 27 28#include <xf86drm.h> 29 30#include "panvk_private.h" 31 32static VkResult 33sync_create(struct panvk_device *device, 34 struct panvk_syncobj *sync, 35 bool signaled) 36{ 37 const struct panfrost_device *pdev = &device->physical_device->pdev; 38 39 struct drm_syncobj_create create = { 40 .flags = signaled ? DRM_SYNCOBJ_CREATE_SIGNALED : 0, 41 }; 42 43 int ret = drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_CREATE, &create); 44 if (ret) 45 return VK_ERROR_OUT_OF_HOST_MEMORY; 46 47 sync->permanent = create.handle; 48 49 return VK_SUCCESS; 50} 51 52static void 53sync_set_temporary(struct panvk_device *device, struct panvk_syncobj *sync, 54 uint32_t syncobj) 55{ 56 const struct panfrost_device *pdev = &device->physical_device->pdev; 57 58 if (sync->temporary) { 59 struct drm_syncobj_destroy destroy = { .handle = sync->temporary }; 60 drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_DESTROY, &destroy); 61 } 62 63 sync->temporary = syncobj; 64} 65 66static void 67sync_destroy(struct panvk_device *device, struct panvk_syncobj *sync) 68{ 69 const struct panfrost_device *pdev = &device->physical_device->pdev; 70 71 if (!sync) 72 return; 73 74 sync_set_temporary(device, sync, 0); 75 struct drm_syncobj_destroy destroy = { .handle = sync->permanent }; 76 drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_DESTROY, &destroy); 77} 78 79static VkResult 80sync_import(struct panvk_device *device, struct panvk_syncobj *sync, 81 bool temporary, bool sync_fd, int fd) 82{ 83 const struct panfrost_device *pdev = &device->physical_device->pdev; 84 int ret; 85 86 if (!sync_fd) { 87 uint32_t *dst = temporary ? &sync->temporary : &sync->permanent; 88 89 struct drm_syncobj_handle handle = { .fd = fd }; 90 ret = drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &handle); 91 if (ret) 92 return VK_ERROR_INVALID_EXTERNAL_HANDLE; 93 94 if (*dst) { 95 struct drm_syncobj_destroy destroy = { .handle = *dst }; 96 drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_DESTROY, &destroy); 97 } 98 *dst = handle.handle; 99 close(fd); 100 } else { 101 assert(temporary); 102 103 struct drm_syncobj_create create = {}; 104 105 if (fd == -1) 106 create.flags |= DRM_SYNCOBJ_CREATE_SIGNALED; 107 108 ret = drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_CREATE, &create); 109 if (ret) 110 return VK_ERROR_INVALID_EXTERNAL_HANDLE; 111 112 if (fd != -1) { 113 struct drm_syncobj_handle handle = { 114 .fd = fd, 115 .handle = create.handle, 116 .flags = DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE, 117 }; 118 119 ret = drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &handle); 120 if (ret) { 121 struct drm_syncobj_destroy destroy = { .handle = create.handle }; 122 drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_DESTROY, &destroy); 123 return VK_ERROR_INVALID_EXTERNAL_HANDLE; 124 } 125 close(fd); 126 } 127 128 sync_set_temporary(device, sync, create.handle); 129 } 130 131 return VK_SUCCESS; 132} 133 134static VkResult 135sync_export(struct panvk_device *device, struct panvk_syncobj *sync, 136 bool sync_fd, int *p_fd) 137{ 138 const struct panfrost_device *pdev = &device->physical_device->pdev; 139 140 struct drm_syncobj_handle handle = { 141 .handle = sync->temporary ? : sync->permanent, 142 .flags = sync_fd ? DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE : 0, 143 .fd = -1, 144 }; 145 int ret = drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &handle); 146 if (ret) 147 return vk_error(device, VK_ERROR_INVALID_EXTERNAL_HANDLE); 148 149 /* restore permanent payload on export */ 150 sync_set_temporary(device, sync, 0); 151 152 *p_fd = handle.fd; 153 return VK_SUCCESS; 154} 155 156VkResult 157panvk_CreateSemaphore(VkDevice _device, 158 const VkSemaphoreCreateInfo *pCreateInfo, 159 const VkAllocationCallbacks *pAllocator, 160 VkSemaphore *pSemaphore) 161{ 162 VK_FROM_HANDLE(panvk_device, device, _device); 163 struct panvk_semaphore *sem = 164 vk_object_zalloc(&device->vk, pAllocator, sizeof(*sem), 165 VK_OBJECT_TYPE_SEMAPHORE); 166 if (!sem) 167 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); 168 169 VkResult ret = sync_create(device, &sem->syncobj, false); 170 if (ret != VK_SUCCESS) { 171 vk_free2(&device->vk.alloc, pAllocator, sync); 172 return ret; 173 } 174 175 *pSemaphore = panvk_semaphore_to_handle(sem); 176 return VK_SUCCESS; 177} 178 179void 180panvk_DestroySemaphore(VkDevice _device, VkSemaphore _sem, const VkAllocationCallbacks *pAllocator) 181{ 182 VK_FROM_HANDLE(panvk_device, device, _device); 183 VK_FROM_HANDLE(panvk_semaphore, sem, _sem); 184 185 sync_destroy(device, &sem->syncobj); 186 vk_object_free(&device->vk, pAllocator, sem); 187} 188 189VkResult 190panvk_ImportSemaphoreFdKHR(VkDevice _device, const VkImportSemaphoreFdInfoKHR *info) 191{ 192 VK_FROM_HANDLE(panvk_device, device, _device); 193 VK_FROM_HANDLE(panvk_semaphore, sem, info->semaphore); 194 bool temp = info->flags & VK_SEMAPHORE_IMPORT_TEMPORARY_BIT; 195 bool sync_fd = info->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; 196 197 return sync_import(device, &sem->syncobj, temp, sync_fd, info->fd); 198} 199 200VkResult 201panvk_GetSemaphoreFdKHR(VkDevice _device, const VkSemaphoreGetFdInfoKHR *info, int *pFd) 202{ 203 VK_FROM_HANDLE(panvk_device, device, _device); 204 VK_FROM_HANDLE(panvk_semaphore, sem, info->semaphore); 205 bool sync_fd = info->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; 206 207 return sync_export(device, &sem->syncobj, sync_fd, pFd); 208} 209 210VkResult 211panvk_CreateFence(VkDevice _device, 212 const VkFenceCreateInfo *info, 213 const VkAllocationCallbacks *pAllocator, 214 VkFence *pFence) 215{ 216 VK_FROM_HANDLE(panvk_device, device, _device); 217 struct panvk_fence *fence = 218 vk_object_zalloc(&device->vk, pAllocator, sizeof(*fence), 219 VK_OBJECT_TYPE_FENCE); 220 if (!fence) 221 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); 222 223 VkResult ret = sync_create(device, &fence->syncobj, 224 info->flags & VK_FENCE_CREATE_SIGNALED_BIT); 225 if (ret != VK_SUCCESS) { 226 vk_free2(&device->vk.alloc, pAllocator, fence); 227 return ret; 228 } 229 230 *pFence = panvk_fence_to_handle(fence); 231 return VK_SUCCESS; 232} 233 234void 235panvk_DestroyFence(VkDevice _device, VkFence _fence, 236 const VkAllocationCallbacks *pAllocator) 237{ 238 VK_FROM_HANDLE(panvk_device, device, _device); 239 VK_FROM_HANDLE(panvk_fence, fence, _fence); 240 241 sync_destroy(device, &fence->syncobj); 242 vk_object_free(&device->vk, pAllocator, fence); 243} 244 245VkResult 246panvk_ImportFenceFdKHR(VkDevice _device, const VkImportFenceFdInfoKHR *info) 247{ 248 VK_FROM_HANDLE(panvk_device, device, _device); 249 VK_FROM_HANDLE(panvk_fence, fence, info->fence); 250 bool sync_fd = info->handleType == VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT; 251 bool temp = info->flags & VK_FENCE_IMPORT_TEMPORARY_BIT; 252 253 return sync_import(device, &fence->syncobj, temp, sync_fd, info->fd); 254} 255 256VkResult 257panvk_GetFenceFdKHR(VkDevice _device, const VkFenceGetFdInfoKHR *info, int *pFd) 258{ 259 VK_FROM_HANDLE(panvk_device, device, _device); 260 VK_FROM_HANDLE(panvk_fence, fence, info->fence); 261 bool sync_fd = info->handleType == VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT; 262 263 return sync_export(device, &fence->syncobj, sync_fd, pFd); 264} 265 266static VkResult 267drm_syncobj_wait(struct panvk_device *device, 268 const uint32_t *handles, uint32_t count_handles, 269 int64_t timeout_nsec, bool wait_all) 270{ 271 const struct panfrost_device *pdev = &device->physical_device->pdev; 272 struct drm_syncobj_wait wait = { 273 .handles = (uint64_t) (uintptr_t) handles, 274 .count_handles = count_handles, 275 .timeout_nsec = timeout_nsec, 276 .flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT | 277 (wait_all ? DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL : 0) 278 }; 279 280 int ret = drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); 281 if (ret) { 282 if (errno == ETIME) 283 return VK_TIMEOUT; 284 285 assert(0); 286 return VK_ERROR_DEVICE_LOST; /* TODO */ 287 } 288 return VK_SUCCESS; 289} 290 291static uint64_t 292gettime_ns(void) 293{ 294 struct timespec current; 295 clock_gettime(CLOCK_MONOTONIC, ¤t); 296 return (uint64_t)current.tv_sec * 1000000000 + current.tv_nsec; 297} 298 299/* and the kernel converts it right back to relative timeout - very smart UAPI */ 300static uint64_t 301absolute_timeout(uint64_t timeout) 302{ 303 if (timeout == 0) 304 return 0; 305 uint64_t current_time = gettime_ns(); 306 uint64_t max_timeout = (uint64_t) INT64_MAX - current_time; 307 308 timeout = MIN2(max_timeout, timeout); 309 310 return (current_time + timeout); 311} 312 313VkResult 314panvk_WaitForFences(VkDevice _device, 315 uint32_t fenceCount, 316 const VkFence *pFences, 317 VkBool32 waitAll, 318 uint64_t timeout) 319{ 320 VK_FROM_HANDLE(panvk_device, device, _device); 321 322 if (panvk_device_is_lost(device)) 323 return VK_ERROR_DEVICE_LOST; 324 325 uint32_t handles[fenceCount]; 326 for (unsigned i = 0; i < fenceCount; ++i) { 327 VK_FROM_HANDLE(panvk_fence, fence, pFences[i]); 328 329 if (fence->syncobj.temporary) { 330 handles[i] = fence->syncobj.temporary; 331 } else { 332 handles[i] = fence->syncobj.permanent; 333 } 334 } 335 336 return drm_syncobj_wait(device, handles, fenceCount, absolute_timeout(timeout), waitAll); 337} 338 339VkResult 340panvk_ResetFences(VkDevice _device, uint32_t fenceCount, const VkFence *pFences) 341{ 342 VK_FROM_HANDLE(panvk_device, device, _device); 343 const struct panfrost_device *pdev = &device->physical_device->pdev; 344 int ret; 345 346 uint32_t handles[fenceCount]; 347 for (unsigned i = 0; i < fenceCount; ++i) { 348 VK_FROM_HANDLE(panvk_fence, fence, pFences[i]); 349 350 sync_set_temporary(device, &fence->syncobj, 0); 351 handles[i] = fence->syncobj.permanent; 352 } 353 354 struct drm_syncobj_array objs = { 355 .handles = (uint64_t) (uintptr_t) handles, 356 .count_handles = fenceCount, 357 }; 358 359 ret = drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_RESET, &objs); 360 if (ret) { 361 panvk_device_set_lost(device, "DRM_IOCTL_SYNCOBJ_RESET failure: %s", 362 strerror(errno)); 363 } 364 365 return VK_SUCCESS; 366} 367 368VkResult 369panvk_GetFenceStatus(VkDevice _device, VkFence _fence) 370{ 371 VK_FROM_HANDLE(panvk_device, device, _device); 372 VK_FROM_HANDLE(panvk_fence, fence, _fence); 373 uint32_t handle = fence->syncobj.temporary ? : fence->syncobj.permanent; 374 VkResult result; 375 376 result = drm_syncobj_wait(device, &handle, 1, 0, false); 377 if (result == VK_TIMEOUT) 378 result = VK_NOT_READY; 379 return result; 380} 381 382int 383panvk_signal_syncobjs(struct panvk_device *device, 384 struct panvk_syncobj *syncobj1, 385 struct panvk_syncobj *syncobj2) 386{ 387 const struct panfrost_device *pdev = &device->physical_device->pdev; 388 uint32_t handles[2], count = 0; 389 390 if (syncobj1) 391 handles[count++] = syncobj1->temporary ?: syncobj1->permanent; 392 393 if (syncobj2) 394 handles[count++] = syncobj2->temporary ?: syncobj2->permanent; 395 396 if (!count) 397 return 0; 398 399 struct drm_syncobj_array objs = { 400 .handles = (uintptr_t) handles, 401 .count_handles = count 402 }; 403 404 return drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_SIGNAL, &objs); 405} 406 407int 408panvk_syncobj_to_fd(struct panvk_device *device, struct panvk_syncobj *sync) 409{ 410 const struct panfrost_device *pdev = &device->physical_device->pdev; 411 struct drm_syncobj_handle handle = { .handle = sync->permanent }; 412 int ret; 413 414 ret = drmIoctl(pdev->fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &handle); 415 416 return ret ? -1 : handle.fd; 417} 418