301ac66fcdd91b79a25d2433b9076f513d7d57ab
[platform/upstream/libdrm.git] / freedreno / msm / msm_ringbuffer.c
1 /* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
2
3 /*
4  * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the next
14  * paragraph) shall be included in all copies or substantial portions of the
15  * Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  *
25  * Authors:
26  *    Rob Clark <robclark@freedesktop.org>
27  */
28
29 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
32
33 #include <assert.h>
34 #include <inttypes.h>
35
36 #include "freedreno_ringbuffer.h"
37 #include "msm_priv.h"
38
39 /* represents a single cmd buffer in the submit ioctl.  Each cmd buffer has
40  * a backing bo, and a reloc table.
41  */
42 struct msm_cmd {
43         struct fd_ringbuffer *ring;
44         struct fd_bo *ring_bo;
45
46         /* reloc's table: */
47         struct drm_msm_gem_submit_reloc *relocs;
48         uint32_t nr_relocs, max_relocs;
49 };
50
51 struct msm_ringbuffer {
52         struct fd_ringbuffer base;
53
54         /* submit ioctl related tables:
55          * Note that bos and cmds are tracked by the parent ringbuffer, since
56          * that is global to the submit ioctl call.  The reloc's table is tracked
57          * per cmd-buffer.
58          */
59         struct {
60                 /* bo's table: */
61                 struct drm_msm_gem_submit_bo *bos;
62                 uint32_t nr_bos, max_bos;
63
64                 /* cmd's table: */
65                 struct drm_msm_gem_submit_cmd *cmds;
66                 uint32_t nr_cmds, max_cmds;
67         } submit;
68
69         /* should have matching entries in submit.bos: */
70         /* Note, only in parent ringbuffer */
71         struct fd_bo **bos;
72         uint32_t nr_bos, max_bos;
73
74         /* should have matching entries in submit.cmds: */
75         struct msm_cmd **cmds;
76         uint32_t nr_cmds, max_cmds;
77
78         /* current cmd-buffer: */
79         struct msm_cmd *cmd;
80 };
81
82 static pthread_mutex_t idx_lock = PTHREAD_MUTEX_INITIALIZER;
83 drm_private extern pthread_mutex_t table_lock;
84
85 static void ring_bo_del(struct fd_device *dev, struct fd_bo *bo)
86 {
87         int ret;
88
89         pthread_mutex_lock(&table_lock);
90         ret = fd_bo_cache_free(&to_msm_device(dev)->ring_cache, bo);
91         pthread_mutex_unlock(&table_lock);
92
93         if (ret == 0)
94                 return;
95
96         fd_bo_del(bo);
97 }
98
99 static struct fd_bo * ring_bo_new(struct fd_device *dev, uint32_t size)
100 {
101         struct fd_bo *bo;
102
103         bo = fd_bo_cache_alloc(&to_msm_device(dev)->ring_cache, &size, 0);
104         if (bo)
105                 return bo;
106
107         bo = fd_bo_new(dev, size, 0);
108         if (!bo)
109                 return NULL;
110
111         /* keep ringbuffer bo's out of the normal bo cache: */
112         bo->bo_reuse = FALSE;
113
114         return bo;
115 }
116
117 static void ring_cmd_del(struct msm_cmd *cmd)
118 {
119         if (cmd->ring_bo)
120                 ring_bo_del(cmd->ring->pipe->dev, cmd->ring_bo);
121         free(cmd->relocs);
122         free(cmd);
123 }
124
125 static struct msm_cmd * ring_cmd_new(struct fd_ringbuffer *ring, uint32_t size)
126 {
127         struct msm_cmd *cmd = calloc(1, sizeof(*cmd));
128
129         if (!cmd)
130                 return NULL;
131
132         cmd->ring = ring;
133         cmd->ring_bo = ring_bo_new(ring->pipe->dev, size);
134         if (!cmd->ring_bo)
135                 goto fail;
136
137         return cmd;
138
139 fail:
140         ring_cmd_del(cmd);
141         return NULL;
142 }
143
144 static void *grow(void *ptr, uint32_t nr, uint32_t *max, uint32_t sz)
145 {
146         if ((nr + 1) > *max) {
147                 if ((*max * 2) < (nr + 1))
148                         *max = nr + 5;
149                 else
150                         *max = *max * 2;
151                 ptr = realloc(ptr, *max * sz);
152         }
153         return ptr;
154 }
155
156 #define APPEND(x, name) ({ \
157         (x)->name = grow((x)->name, (x)->nr_ ## name, &(x)->max_ ## name, sizeof((x)->name[0])); \
158         (x)->nr_ ## name ++; \
159 })
160
161 static inline struct msm_ringbuffer * to_msm_ringbuffer(struct fd_ringbuffer *x)
162 {
163         return (struct msm_ringbuffer *)x;
164 }
165
166 static uint32_t append_bo(struct fd_ringbuffer *ring, struct fd_bo *bo)
167 {
168         struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
169         uint32_t idx;
170
171         idx = APPEND(&msm_ring->submit, bos);
172         idx = APPEND(msm_ring, bos);
173
174         msm_ring->submit.bos[idx].flags = 0;
175         msm_ring->submit.bos[idx].handle = bo->handle;
176         msm_ring->submit.bos[idx].presumed = to_msm_bo(bo)->presumed;
177
178         msm_ring->bos[idx] = fd_bo_ref(bo);
179
180         return idx;
181 }
182
183 /* add (if needed) bo, return idx: */
184 static uint32_t bo2idx(struct fd_ringbuffer *ring, struct fd_bo *bo, uint32_t flags)
185 {
186         struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
187         struct msm_bo *msm_bo = to_msm_bo(bo);
188         uint32_t idx;
189         pthread_mutex_lock(&idx_lock);
190         if (!msm_bo->current_ring) {
191                 idx = append_bo(ring, bo);
192                 msm_bo->current_ring = ring;
193                 msm_bo->idx = idx;
194         } else if (msm_bo->current_ring == ring) {
195                 idx = msm_bo->idx;
196         } else {
197                 /* slow-path: */
198                 for (idx = 0; idx < msm_ring->nr_bos; idx++)
199                         if (msm_ring->bos[idx] == bo)
200                                 break;
201                 if (idx == msm_ring->nr_bos) {
202                         /* not found */
203                         idx = append_bo(ring, bo);
204                 }
205         }
206         pthread_mutex_unlock(&idx_lock);
207         if (flags & FD_RELOC_READ)
208                 msm_ring->submit.bos[idx].flags |= MSM_SUBMIT_BO_READ;
209         if (flags & FD_RELOC_WRITE)
210                 msm_ring->submit.bos[idx].flags |= MSM_SUBMIT_BO_WRITE;
211         return idx;
212 }
213
214 static int check_cmd_bo(struct fd_ringbuffer *ring,
215                 struct drm_msm_gem_submit_cmd *cmd, struct fd_bo *bo)
216 {
217         struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
218         return msm_ring->submit.bos[cmd->submit_idx].handle == bo->handle;
219 }
220
221 /* Ensure that submit has corresponding entry in cmds table for the
222  * target cmdstream buffer:
223  */
224 static void get_cmd(struct fd_ringbuffer *ring, struct msm_cmd *target_cmd,
225                 uint32_t submit_offset, uint32_t size, uint32_t type)
226 {
227         struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
228         struct drm_msm_gem_submit_cmd *cmd;
229         uint32_t i;
230
231         /* figure out if we already have a cmd buf: */
232         for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
233                 cmd = &msm_ring->submit.cmds[i];
234                 if ((cmd->submit_offset == submit_offset) &&
235                                 (cmd->size == size) &&
236                                 (cmd->type == type) &&
237                                 check_cmd_bo(ring, cmd, target_cmd->ring_bo))
238                         return;
239         }
240
241         /* create cmd buf if not: */
242         i = APPEND(&msm_ring->submit, cmds);
243         APPEND(msm_ring, cmds);
244         msm_ring->cmds[i] = target_cmd;
245         cmd = &msm_ring->submit.cmds[i];
246         cmd->type = type;
247         cmd->submit_idx = bo2idx(ring, target_cmd->ring_bo, FD_RELOC_READ);
248         cmd->submit_offset = submit_offset;
249         cmd->size = size;
250         cmd->pad = 0;
251 }
252
253 static void * msm_ringbuffer_hostptr(struct fd_ringbuffer *ring)
254 {
255         struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
256         return fd_bo_map(msm_ring->cmd->ring_bo);
257 }
258
259 static uint32_t find_next_reloc_idx(struct msm_cmd *msm_cmd,
260                 uint32_t start, uint32_t offset)
261 {
262         uint32_t i;
263
264         /* a binary search would be more clever.. */
265         for (i = start; i < msm_cmd->nr_relocs; i++) {
266                 struct drm_msm_gem_submit_reloc *reloc = &msm_cmd->relocs[i];
267                 if (reloc->submit_offset >= offset)
268                         return i;
269         }
270
271         return i;
272 }
273
274 static void flush_reset(struct fd_ringbuffer *ring)
275 {
276         struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
277         unsigned i;
278
279         for (i = 0; i < msm_ring->nr_bos; i++) {
280                 struct msm_bo *msm_bo = to_msm_bo(msm_ring->bos[i]);
281                 msm_bo->current_ring = NULL;
282                 fd_bo_del(&msm_bo->base);
283         }
284
285         /* for each of the cmd buffers, clear their reloc's: */
286         for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
287                 struct msm_cmd *target_cmd = msm_ring->cmds[i];
288                 target_cmd->nr_relocs = 0;
289         }
290
291         msm_ring->cmd->nr_relocs = 0;
292         msm_ring->submit.nr_cmds = 0;
293         msm_ring->submit.nr_bos = 0;
294         msm_ring->nr_cmds = 0;
295         msm_ring->nr_bos = 0;
296 }
297
298 static void dump_submit(struct msm_ringbuffer *msm_ring)
299 {
300         uint32_t i, j;
301
302         for (i = 0; i < msm_ring->submit.nr_bos; i++) {
303                 struct drm_msm_gem_submit_bo *bo = &msm_ring->submit.bos[i];
304                 ERROR_MSG("  bos[%d]: handle=%u, flags=%x", i, bo->handle, bo->flags);
305         }
306         for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
307                 struct drm_msm_gem_submit_cmd *cmd = &msm_ring->submit.cmds[i];
308                 struct drm_msm_gem_submit_reloc *relocs = U642VOID(cmd->relocs);
309                 ERROR_MSG("  cmd[%d]: type=%u, submit_idx=%u, submit_offset=%u, size=%u",
310                                 i, cmd->type, cmd->submit_idx, cmd->submit_offset, cmd->size);
311                 for (j = 0; j < cmd->nr_relocs; j++) {
312                         struct drm_msm_gem_submit_reloc *r = &relocs[j];
313                         ERROR_MSG("    reloc[%d]: submit_offset=%u, or=%08x, shift=%d, reloc_idx=%u"
314                                         ", reloc_offset=%"PRIu64, j, r->submit_offset, r->or, r->shift,
315                                         r->reloc_idx, r->reloc_offset);
316                 }
317         }
318 }
319
320 static int msm_ringbuffer_flush(struct fd_ringbuffer *ring, uint32_t *last_start)
321 {
322         struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
323         struct drm_msm_gem_submit req = {
324                         .pipe = to_msm_pipe(ring->pipe)->pipe,
325         };
326         uint32_t i, submit_offset, size;
327         int ret;
328
329         submit_offset = offset_bytes(last_start, ring->start);
330         size = offset_bytes(ring->cur, last_start);
331
332         get_cmd(ring, msm_ring->cmd, submit_offset, size, MSM_SUBMIT_CMD_BUF);
333
334         /* needs to be after get_cmd() as that could create bos/cmds table: */
335         req.bos = VOID2U64(msm_ring->submit.bos),
336         req.nr_bos = msm_ring->submit.nr_bos;
337         req.cmds = VOID2U64(msm_ring->submit.cmds),
338         req.nr_cmds = msm_ring->submit.nr_cmds;
339
340         /* for each of the cmd's fix up their reloc's: */
341         for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
342                 struct drm_msm_gem_submit_cmd *cmd = &msm_ring->submit.cmds[i];
343                 struct msm_cmd *msm_cmd = msm_ring->cmds[i];
344                 uint32_t a = find_next_reloc_idx(msm_cmd, 0, cmd->submit_offset);
345                 uint32_t b = find_next_reloc_idx(msm_cmd, a, cmd->submit_offset + cmd->size);
346                 cmd->relocs = VOID2U64(&msm_cmd->relocs[a]);
347                 cmd->nr_relocs = (b > a) ? b - a : 0;
348         }
349
350         DEBUG_MSG("nr_cmds=%u, nr_bos=%u", req.nr_cmds, req.nr_bos);
351
352         ret = drmCommandWriteRead(ring->pipe->dev->fd, DRM_MSM_GEM_SUBMIT,
353                         &req, sizeof(req));
354         if (ret) {
355                 ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno));
356                 dump_submit(msm_ring);
357         } else if (!ret) {
358                 /* update timestamp on all rings associated with submit: */
359                 for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
360                         struct msm_cmd *msm_cmd = msm_ring->cmds[i];
361                         msm_cmd->ring->last_timestamp = req.fence;
362                 }
363         }
364
365         flush_reset(ring);
366
367         return ret;
368 }
369
370 static void msm_ringbuffer_reset(struct fd_ringbuffer *ring)
371 {
372         flush_reset(ring);
373 }
374
375 static void msm_ringbuffer_emit_reloc(struct fd_ringbuffer *ring,
376                 const struct fd_reloc *r)
377 {
378         struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
379         struct fd_ringbuffer *parent = ring->parent ? ring->parent : ring;
380         struct msm_bo *msm_bo = to_msm_bo(r->bo);
381         struct drm_msm_gem_submit_reloc *reloc;
382         uint32_t idx = APPEND(msm_ring->cmd, relocs);
383         uint32_t addr;
384
385         reloc = &msm_ring->cmd->relocs[idx];
386
387         reloc->reloc_idx = bo2idx(parent, r->bo, r->flags);
388         reloc->reloc_offset = r->offset;
389         reloc->or = r->or;
390         reloc->shift = r->shift;
391         reloc->submit_offset = offset_bytes(ring->cur, ring->start);
392
393         addr = msm_bo->presumed;
394         if (r->shift < 0)
395                 addr >>= -r->shift;
396         else
397                 addr <<= r->shift;
398         (*ring->cur++) = addr | r->or;
399 }
400
401 static void msm_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
402                 struct fd_ringbuffer *target,
403                 uint32_t submit_offset, uint32_t size)
404 {
405         struct msm_cmd *cmd = to_msm_ringbuffer(target)->cmd;
406
407         get_cmd(ring, cmd, submit_offset, size, MSM_SUBMIT_CMD_IB_TARGET_BUF);
408
409         msm_ringbuffer_emit_reloc(ring, &(struct fd_reloc){
410                 .bo = cmd->ring_bo,
411                 .flags = FD_RELOC_READ,
412                 .offset = submit_offset,
413         });
414 }
415
416 static void msm_ringbuffer_destroy(struct fd_ringbuffer *ring)
417 {
418         struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
419         if (msm_ring->cmd)
420                 ring_cmd_del(msm_ring->cmd);
421         free(msm_ring->submit.cmds);
422         free(msm_ring->submit.bos);
423         free(msm_ring->bos);
424         free(msm_ring->cmds);
425         free(msm_ring);
426 }
427
428 static const struct fd_ringbuffer_funcs funcs = {
429                 .hostptr = msm_ringbuffer_hostptr,
430                 .flush = msm_ringbuffer_flush,
431                 .reset = msm_ringbuffer_reset,
432                 .emit_reloc = msm_ringbuffer_emit_reloc,
433                 .emit_reloc_ring = msm_ringbuffer_emit_reloc_ring,
434                 .destroy = msm_ringbuffer_destroy,
435 };
436
437 drm_private struct fd_ringbuffer * msm_ringbuffer_new(struct fd_pipe *pipe,
438                 uint32_t size)
439 {
440         struct msm_ringbuffer *msm_ring;
441         struct fd_ringbuffer *ring = NULL;
442
443         msm_ring = calloc(1, sizeof(*msm_ring));
444         if (!msm_ring) {
445                 ERROR_MSG("allocation failed");
446                 goto fail;
447         }
448
449         ring = &msm_ring->base;
450         ring->funcs = &funcs;
451         ring->pipe = pipe;   /* needed in ring_cmd_new() */
452
453         msm_ring->cmd = ring_cmd_new(ring, size);
454         if (!msm_ring->cmd) {
455                 ERROR_MSG("command buffer allocation failed");
456                 goto fail;
457         }
458
459         return ring;
460 fail:
461         if (ring)
462                 fd_ringbuffer_del(ring);
463         return NULL;
464 }