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 <string.h>
26
27#include "xf86drm.h"
28#include "libsync.h"
29#include "drm-uapi/lima_drm.h"
30
31#include "util/ralloc.h"
32#include "util/u_dynarray.h"
33#include "util/os_time.h"
34
35#include "lima_screen.h"
36#include "lima_context.h"
37#include "lima_submit.h"
38#include "lima_bo.h"
39#include "lima_util.h"
40
41struct lima_submit {
42   struct lima_screen *screen;
43   uint32_t pipe;
44   uint32_t ctx;
45
46   int in_sync_fd;
47   uint32_t in_sync;
48   uint32_t out_sync;
49
50   struct util_dynarray gem_bos;
51   struct util_dynarray bos;
52};
53
54
55#define VOID2U64(x) ((uint64_t)(unsigned long)(x))
56
57struct lima_submit *lima_submit_create(struct lima_context *ctx, uint32_t pipe)
58{
59   struct lima_submit *s;
60
61   s = rzalloc(ctx, struct lima_submit);
62   if (!s)
63      return NULL;
64
65   s->screen = lima_screen(ctx->base.screen);
66   s->pipe = pipe;
67   s->ctx = ctx->id;
68   s->in_sync_fd = -1;
69
70   int err = drmSyncobjCreate(s->screen->fd, DRM_SYNCOBJ_CREATE_SIGNALED,
71                              &s->out_sync);
72   if (err)
73      goto err_out0;
74
75   err = drmSyncobjCreate(s->screen->fd, DRM_SYNCOBJ_CREATE_SIGNALED,
76                          &s->in_sync);
77   if (err)
78      goto err_out1;
79
80   util_dynarray_init(&s->gem_bos, s);
81
82   return s;
83
84err_out1:
85   drmSyncobjDestroy(s->screen->fd, s->out_sync);
86err_out0:
87   ralloc_free(s);
88   return NULL;
89}
90
91void lima_submit_free(struct lima_submit *submit)
92{
93   if (submit->in_sync_fd >= 0)
94      close(submit->in_sync_fd);
95   drmSyncobjDestroy(submit->screen->fd, submit->in_sync);
96   drmSyncobjDestroy(submit->screen->fd, submit->out_sync);
97}
98
99bool lima_submit_add_bo(struct lima_submit *submit, struct lima_bo *bo, uint32_t flags)
100{
101   util_dynarray_foreach(&submit->gem_bos, struct drm_lima_gem_submit_bo, gem_bo) {
102      if (bo->handle == gem_bo->handle) {
103         gem_bo->flags |= flags;
104         return true;
105      }
106   }
107
108   struct drm_lima_gem_submit_bo *submit_bo =
109      util_dynarray_grow(&submit->gem_bos, sizeof(*submit_bo));
110   submit_bo->handle = bo->handle;
111   submit_bo->flags = flags;
112
113   struct lima_bo **jbo = util_dynarray_grow(&submit->bos, sizeof(*jbo));
114   *jbo = bo;
115
116   /* prevent bo from being freed when submit start */
117   lima_bo_reference(bo);
118
119   return true;
120}
121
122bool lima_submit_start(struct lima_submit *submit, void *frame, uint32_t size)
123{
124   struct drm_lima_gem_submit req = {
125      .ctx = submit->ctx,
126      .pipe = submit->pipe,
127      .nr_bos = submit->gem_bos.size / sizeof(struct drm_lima_gem_submit_bo),
128      .bos = VOID2U64(util_dynarray_begin(&submit->gem_bos)),
129      .frame = VOID2U64(frame),
130      .frame_size = size,
131   };
132
133   if (submit->in_sync_fd >= 0) {
134      int err = drmSyncobjImportSyncFile(submit->screen->fd, submit->in_sync,
135                                         submit->in_sync_fd);
136      if (err)
137         return false;
138
139      req.in_sync[0] = submit->in_sync;
140      close(submit->in_sync_fd);
141      submit->in_sync_fd = -1;
142   }
143
144   bool ret = drmIoctl(submit->screen->fd, DRM_IOCTL_LIMA_GEM_SUBMIT, &req) == 0;
145
146   util_dynarray_foreach(&submit->bos, struct lima_bo *, bo) {
147      lima_bo_free(*bo);
148   }
149
150   util_dynarray_clear(&submit->gem_bos);
151   util_dynarray_clear(&submit->bos);
152   return ret;
153}
154
155bool lima_submit_wait(struct lima_submit *submit, uint64_t timeout_ns)
156{
157   int64_t abs_timeout = os_time_get_absolute_timeout(timeout_ns);
158
159   return !drmSyncobjWait(submit->screen->fd, &submit->out_sync, 1, abs_timeout, 0, NULL);
160}
161
162bool lima_submit_has_bo(struct lima_submit *submit, struct lima_bo *bo, bool all)
163{
164   util_dynarray_foreach(&submit->gem_bos, struct drm_lima_gem_submit_bo, gem_bo) {
165      if (bo->handle == gem_bo->handle) {
166         if (all)
167            return true;
168         else
169            return gem_bo->flags & LIMA_SUBMIT_BO_WRITE;
170      }
171   }
172
173   return false;
174}
175
176bool lima_submit_add_in_sync(struct lima_submit *submit, int fd)
177{
178   return !sync_accumulate("lima", &submit->in_sync_fd, fd);
179}
180
181bool lima_submit_get_out_sync(struct lima_submit *submit, int *fd)
182{
183   return !drmSyncobjExportSyncFile(submit->screen->fd, submit->out_sync, fd);
184}
185