panfrost: Get rid of the "free imported BO" logic
[mesa.git] / src / gallium / drivers / panfrost / pan_drm.c
1 /*
2 * © Copyright 2019 Collabora, Ltd.
3 * Copyright 2019 Alyssa Rosenzweig
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 */
25
26 #include <fcntl.h>
27 #include <xf86drm.h>
28
29 #include "drm-uapi/panfrost_drm.h"
30
31 #include "util/u_memory.h"
32 #include "util/os_time.h"
33 #include "os/os_mman.h"
34
35 #include "pan_screen.h"
36 #include "pan_resource.h"
37 #include "pan_context.h"
38 #include "pan_util.h"
39 #include "pandecode/decode.h"
40
41 void
42 panfrost_drm_allocate_slab(struct panfrost_screen *screen,
43 struct panfrost_memory *mem,
44 size_t pages,
45 bool same_va,
46 int extra_flags,
47 int commit_count,
48 int extent)
49 {
50 struct drm_panfrost_create_bo create_bo = {
51 .size = pages * 4096,
52 .flags = 0, // TODO figure out proper flags..
53 };
54 struct drm_panfrost_mmap_bo mmap_bo = {0,};
55 int ret;
56
57 // TODO cache allocations
58 // TODO properly handle errors
59 // TODO take into account extra_flags
60
61 ret = drmIoctl(screen->fd, DRM_IOCTL_PANFROST_CREATE_BO, &create_bo);
62 if (ret) {
63 fprintf(stderr, "DRM_IOCTL_PANFROST_CREATE_BO failed: %d\n", ret);
64 assert(0);
65 }
66
67 mem->gpu = create_bo.offset;
68 mem->gem_handle = create_bo.handle;
69 mem->stack_bottom = 0;
70 mem->size = create_bo.size;
71
72 // TODO map and unmap on demand?
73 mmap_bo.handle = create_bo.handle;
74 ret = drmIoctl(screen->fd, DRM_IOCTL_PANFROST_MMAP_BO, &mmap_bo);
75 if (ret) {
76 fprintf(stderr, "DRM_IOCTL_PANFROST_MMAP_BO failed: %d\n", ret);
77 assert(0);
78 }
79
80 mem->cpu = os_mmap(NULL, mem->size, PROT_READ | PROT_WRITE, MAP_SHARED,
81 screen->fd, mmap_bo.offset);
82 if (mem->cpu == MAP_FAILED) {
83 fprintf(stderr, "mmap failed: %p\n", mem->cpu);
84 assert(0);
85 }
86
87 /* Record the mmap if we're tracing */
88 if (pan_debug & PAN_DBG_TRACE)
89 pandecode_inject_mmap(mem->gpu, mem->cpu, mem->size, NULL);
90 }
91
92 void
93 panfrost_drm_free_slab(struct panfrost_screen *screen, struct panfrost_memory *mem)
94 {
95 struct drm_gem_close gem_close = {
96 .handle = mem->gem_handle,
97 };
98 int ret;
99
100 if (os_munmap((void *) (uintptr_t) mem->cpu, mem->size)) {
101 perror("munmap");
102 abort();
103 }
104
105 mem->cpu = NULL;
106
107 ret = drmIoctl(screen->fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
108 if (ret) {
109 fprintf(stderr, "DRM_IOCTL_GEM_CLOSE failed: %d\n", ret);
110 assert(0);
111 }
112
113 mem->gem_handle = -1;
114 }
115
116 struct panfrost_bo *
117 panfrost_drm_import_bo(struct panfrost_screen *screen, struct winsys_handle *whandle)
118 {
119 struct panfrost_bo *bo = rzalloc(screen, struct panfrost_bo);
120 struct drm_panfrost_get_bo_offset get_bo_offset = {0,};
121 struct drm_panfrost_mmap_bo mmap_bo = {0,};
122 int ret;
123 unsigned gem_handle;
124
125 ret = drmPrimeFDToHandle(screen->fd, whandle->handle, &gem_handle);
126 assert(!ret);
127
128 get_bo_offset.handle = gem_handle;
129 ret = drmIoctl(screen->fd, DRM_IOCTL_PANFROST_GET_BO_OFFSET, &get_bo_offset);
130 assert(!ret);
131
132 bo->gem_handle = gem_handle;
133 bo->gpu = (mali_ptr) get_bo_offset.offset;
134 pipe_reference_init(&bo->reference, 1);
135
136 // TODO map and unmap on demand?
137 mmap_bo.handle = gem_handle;
138 ret = drmIoctl(screen->fd, DRM_IOCTL_PANFROST_MMAP_BO, &mmap_bo);
139 if (ret) {
140 fprintf(stderr, "DRM_IOCTL_PANFROST_MMAP_BO failed: %d\n", ret);
141 assert(0);
142 }
143
144 bo->size = lseek(whandle->handle, 0, SEEK_END);
145 assert(bo->size > 0);
146 bo->cpu = os_mmap(NULL, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
147 screen->fd, mmap_bo.offset);
148 if (bo->cpu == MAP_FAILED) {
149 fprintf(stderr, "mmap failed: %p\n", bo->cpu);
150 assert(0);
151 }
152
153 /* Record the mmap if we're tracing */
154 if (pan_debug & PAN_DBG_TRACE)
155 pandecode_inject_mmap(bo->gpu, bo->cpu, bo->size, NULL);
156
157 return bo;
158 }
159
160 int
161 panfrost_drm_export_bo(struct panfrost_screen *screen, int gem_handle, unsigned int stride, struct winsys_handle *whandle)
162 {
163 struct drm_prime_handle args = {
164 .handle = gem_handle,
165 .flags = DRM_CLOEXEC,
166 };
167
168 int ret = drmIoctl(screen->fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args);
169 if (ret == -1)
170 return FALSE;
171
172 whandle->handle = args.fd;
173 whandle->stride = stride;
174
175 return TRUE;
176 }
177
178 int
179 panfrost_drm_submit_job(struct panfrost_context *ctx, u64 job_desc, int reqs, struct pipe_surface *surf)
180 {
181 struct pipe_context *gallium = (struct pipe_context *) ctx;
182 struct panfrost_screen *screen = pan_screen(gallium->screen);
183 struct drm_panfrost_submit submit = {0,};
184 int bo_handles[7];
185
186 submit.in_syncs = (u64) (uintptr_t) &ctx->out_sync;
187 submit.in_sync_count = 1;
188
189 submit.out_sync = ctx->out_sync;
190
191 submit.jc = job_desc;
192 submit.requirements = reqs;
193
194 if (surf) {
195 struct panfrost_resource *res = pan_resource(surf->texture);
196 assert(res->bo->gem_handle > 0);
197 bo_handles[submit.bo_handle_count++] = res->bo->gem_handle;
198 }
199
200 /* TODO: Add here the transient pools */
201 /* TODO: Add here the BOs listed in the panfrost_job */
202 bo_handles[submit.bo_handle_count++] = ctx->shaders.gem_handle;
203 bo_handles[submit.bo_handle_count++] = ctx->scratchpad.gem_handle;
204 bo_handles[submit.bo_handle_count++] = ctx->tiler_heap.gem_handle;
205 bo_handles[submit.bo_handle_count++] = ctx->varying_mem.gem_handle;
206 bo_handles[submit.bo_handle_count++] = ctx->tiler_polygon_list.gem_handle;
207 submit.bo_handles = (u64) (uintptr_t) bo_handles;
208
209 if (drmIoctl(screen->fd, DRM_IOCTL_PANFROST_SUBMIT, &submit)) {
210 fprintf(stderr, "Error submitting: %m\n");
211 return errno;
212 }
213
214 /* Trace the job if we're doing that */
215 if (pan_debug & PAN_DBG_TRACE) {
216 /* Wait so we can get errors reported back */
217 drmSyncobjWait(screen->fd, &ctx->out_sync, 1, INT64_MAX, 0, NULL);
218 pandecode_replay_jc(submit.jc, FALSE);
219 }
220
221 return 0;
222 }
223
224 int
225 panfrost_drm_submit_vs_fs_job(struct panfrost_context *ctx, bool has_draws, bool is_scanout)
226 {
227 struct pipe_surface *surf = ctx->pipe_framebuffer.cbufs[0];
228 int ret;
229
230 struct panfrost_job *job = panfrost_get_job_for_fbo(ctx);
231
232 if (job->first_job.gpu) {
233 ret = panfrost_drm_submit_job(ctx, job->first_job.gpu, 0, NULL);
234 assert(!ret);
235 }
236
237 if (job->first_tiler.gpu || job->clear) {
238 ret = panfrost_drm_submit_job(ctx, panfrost_fragment_job(ctx, has_draws), PANFROST_JD_REQ_FS, surf);
239 assert(!ret);
240 }
241
242 return ret;
243 }
244
245 struct panfrost_fence *
246 panfrost_fence_create(struct panfrost_context *ctx)
247 {
248 struct pipe_context *gallium = (struct pipe_context *) ctx;
249 struct panfrost_screen *screen = pan_screen(gallium->screen);
250 struct panfrost_fence *f = calloc(1, sizeof(*f));
251 if (!f)
252 return NULL;
253
254 /* Snapshot the last Panfrost's rendering's out fence. We'd rather have
255 * another syncobj instead of a sync file, but this is all we get.
256 * (HandleToFD/FDToHandle just gives you another syncobj ID for the
257 * same syncobj).
258 */
259 drmSyncobjExportSyncFile(screen->fd, ctx->out_sync, &f->fd);
260 if (f->fd == -1) {
261 fprintf(stderr, "export failed\n");
262 free(f);
263 return NULL;
264 }
265
266 pipe_reference_init(&f->reference, 1);
267
268 return f;
269 }
270
271 void
272 panfrost_drm_force_flush_fragment(struct panfrost_context *ctx,
273 struct pipe_fence_handle **fence)
274 {
275 struct pipe_context *gallium = (struct pipe_context *) ctx;
276 struct panfrost_screen *screen = pan_screen(gallium->screen);
277
278 if (!screen->last_fragment_flushed) {
279 drmSyncobjWait(screen->fd, &ctx->out_sync, 1, INT64_MAX, 0, NULL);
280 screen->last_fragment_flushed = true;
281
282 /* The job finished up, so we're safe to clean it up now */
283 panfrost_free_job(ctx, screen->last_job);
284 }
285
286 if (fence) {
287 struct panfrost_fence *f = panfrost_fence_create(ctx);
288 gallium->screen->fence_reference(gallium->screen, fence, NULL);
289 *fence = (struct pipe_fence_handle *)f;
290 }
291 }
292
293 unsigned
294 panfrost_drm_query_gpu_version(struct panfrost_screen *screen)
295 {
296 struct drm_panfrost_get_param get_param = {0,};
297 int ret;
298
299 get_param.param = DRM_PANFROST_PARAM_GPU_PROD_ID;
300 ret = drmIoctl(screen->fd, DRM_IOCTL_PANFROST_GET_PARAM, &get_param);
301 assert(!ret);
302
303 return get_param.value;
304 }
305
306 int
307 panfrost_drm_init_context(struct panfrost_context *ctx)
308 {
309 struct pipe_context *gallium = (struct pipe_context *) ctx;
310 struct panfrost_screen *screen = pan_screen(gallium->screen);
311
312 return drmSyncobjCreate(screen->fd, DRM_SYNCOBJ_CREATE_SIGNALED,
313 &ctx->out_sync);
314 }
315
316 void
317 panfrost_drm_fence_reference(struct pipe_screen *screen,
318 struct pipe_fence_handle **ptr,
319 struct pipe_fence_handle *fence)
320 {
321 struct panfrost_fence **p = (struct panfrost_fence **)ptr;
322 struct panfrost_fence *f = (struct panfrost_fence *)fence;
323 struct panfrost_fence *old = *p;
324
325 if (pipe_reference(&(*p)->reference, &f->reference)) {
326 close(old->fd);
327 free(old);
328 }
329 *p = f;
330 }
331
332 boolean
333 panfrost_drm_fence_finish(struct pipe_screen *pscreen,
334 struct pipe_context *ctx,
335 struct pipe_fence_handle *fence,
336 uint64_t timeout)
337 {
338 struct panfrost_screen *screen = pan_screen(pscreen);
339 struct panfrost_fence *f = (struct panfrost_fence *)fence;
340 int ret;
341
342 unsigned syncobj;
343 ret = drmSyncobjCreate(screen->fd, 0, &syncobj);
344 if (ret) {
345 fprintf(stderr, "Failed to create syncobj to wait on: %m\n");
346 return false;
347 }
348
349 drmSyncobjImportSyncFile(screen->fd, syncobj, f->fd);
350 if (ret) {
351 fprintf(stderr, "Failed to import fence to syncobj: %m\n");
352 return false;
353 }
354
355 uint64_t abs_timeout = os_time_get_absolute_timeout(timeout);
356 if (abs_timeout == OS_TIMEOUT_INFINITE)
357 abs_timeout = INT64_MAX;
358
359 ret = drmSyncobjWait(screen->fd, &syncobj, 1, abs_timeout, 0, NULL);
360
361 drmSyncobjDestroy(screen->fd, syncobj);
362
363 return ret >= 0;
364 }