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