17e102996Smaya/*
27e102996Smaya * Copyright (C) 2012-2018 Rob Clark <robclark@freedesktop.org>
37e102996Smaya *
47e102996Smaya * Permission is hereby granted, free of charge, to any person obtaining a
57e102996Smaya * copy of this software and associated documentation files (the "Software"),
67e102996Smaya * to deal in the Software without restriction, including without limitation
77e102996Smaya * the rights to use, copy, modify, merge, publish, distribute, sublicense,
87e102996Smaya * and/or sell copies of the Software, and to permit persons to whom the
97e102996Smaya * Software is furnished to do so, subject to the following conditions:
107e102996Smaya *
117e102996Smaya * The above copyright notice and this permission notice (including the next
127e102996Smaya * paragraph) shall be included in all copies or substantial portions of the
137e102996Smaya * Software.
147e102996Smaya *
157e102996Smaya * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
167e102996Smaya * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
177e102996Smaya * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
187e102996Smaya * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
197e102996Smaya * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
207e102996Smaya * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
217e102996Smaya * SOFTWARE.
227e102996Smaya *
237e102996Smaya * Authors:
247e102996Smaya *    Rob Clark <robclark@freedesktop.org>
257e102996Smaya */
267e102996Smaya
277e102996Smaya#include <assert.h>
287e102996Smaya#include <inttypes.h>
297e102996Smaya
307e102996Smaya#include "util/hash_table.h"
317e102996Smaya#include "util/set.h"
327e102996Smaya#include "util/slab.h"
337e102996Smaya
347e102996Smaya#include "drm/freedreno_ringbuffer.h"
357e102996Smaya#include "msm_priv.h"
367e102996Smaya
377e102996Smaya/* The legacy implementation of submit/ringbuffer, which still does the
387e102996Smaya * traditional reloc and cmd tracking
397e102996Smaya */
407e102996Smaya
417e102996Smaya#define INIT_SIZE 0x1000
427e102996Smaya
437e102996Smayastruct msm_submit {
447ec681f3Smrg   struct fd_submit base;
457e102996Smaya
467ec681f3Smrg   DECLARE_ARRAY(struct drm_msm_gem_submit_bo, submit_bos);
477ec681f3Smrg   DECLARE_ARRAY(struct fd_bo *, bos);
487e102996Smaya
497ec681f3Smrg   /* maps fd_bo to idx in bos table: */
507ec681f3Smrg   struct hash_table *bo_table;
517e102996Smaya
527ec681f3Smrg   struct slab_mempool ring_pool;
537e102996Smaya
547ec681f3Smrg   /* hash-set of associated rings: */
557ec681f3Smrg   struct set *ring_set;
567e102996Smaya
577ec681f3Smrg   /* Allow for sub-allocation of stateobj ring buffers (ie. sharing
587ec681f3Smrg    * the same underlying bo)..
597ec681f3Smrg    *
607ec681f3Smrg    * We also rely on previous stateobj having been fully constructed
617ec681f3Smrg    * so we can reclaim extra space at it's end.
627ec681f3Smrg    */
637ec681f3Smrg   struct fd_ringbuffer *suballoc_ring;
647e102996Smaya};
657e102996SmayaFD_DEFINE_CAST(fd_submit, msm_submit);
667e102996Smaya
677e102996Smaya/* for FD_RINGBUFFER_GROWABLE rb's, tracks the 'finalized' cmdstream buffers
687e102996Smaya * and sizes.  Ie. a finalized buffer can have no more commands appended to
697e102996Smaya * it.
707e102996Smaya */
717e102996Smayastruct msm_cmd {
727ec681f3Smrg   struct fd_bo *ring_bo;
737ec681f3Smrg   unsigned size;
747ec681f3Smrg   DECLARE_ARRAY(struct drm_msm_gem_submit_reloc, relocs);
757e102996Smaya};
767e102996Smaya
777e102996Smayastatic struct msm_cmd *
787e102996Smayacmd_new(struct fd_bo *ring_bo)
797e102996Smaya{
807ec681f3Smrg   struct msm_cmd *cmd = malloc(sizeof(*cmd));
817ec681f3Smrg   cmd->ring_bo = fd_bo_ref(ring_bo);
827ec681f3Smrg   cmd->size = 0;
837ec681f3Smrg   cmd->nr_relocs = cmd->max_relocs = 0;
847ec681f3Smrg   cmd->relocs = NULL;
857ec681f3Smrg   return cmd;
867e102996Smaya}
877e102996Smaya
887e102996Smayastatic void
897e102996Smayacmd_free(struct msm_cmd *cmd)
907e102996Smaya{
917ec681f3Smrg   fd_bo_del(cmd->ring_bo);
927ec681f3Smrg   free(cmd->relocs);
937ec681f3Smrg   free(cmd);
947e102996Smaya}
957e102996Smaya
967e102996Smayastruct msm_ringbuffer {
977ec681f3Smrg   struct fd_ringbuffer base;
987ec681f3Smrg
997ec681f3Smrg   /* for FD_RINGBUFFER_STREAMING rb's which are sub-allocated */
1007ec681f3Smrg   unsigned offset;
1017ec681f3Smrg
1027ec681f3Smrg   union {
1037ec681f3Smrg      /* for _FD_RINGBUFFER_OBJECT case: */
1047ec681f3Smrg      struct {
1057ec681f3Smrg         struct fd_pipe *pipe;
1067ec681f3Smrg         DECLARE_ARRAY(struct fd_bo *, reloc_bos);
1077ec681f3Smrg         struct set *ring_set;
1087ec681f3Smrg      };
1097ec681f3Smrg      /* for other cases: */
1107ec681f3Smrg      struct {
1117ec681f3Smrg         struct fd_submit *submit;
1127ec681f3Smrg         DECLARE_ARRAY(struct msm_cmd *, cmds);
1137ec681f3Smrg      };
1147ec681f3Smrg   } u;
1157ec681f3Smrg
1167ec681f3Smrg   struct msm_cmd *cmd; /* current cmd */
1177ec681f3Smrg   struct fd_bo *ring_bo;
1187e102996Smaya};
1197e102996SmayaFD_DEFINE_CAST(fd_ringbuffer, msm_ringbuffer);
1207e102996Smaya
1217e102996Smayastatic void finalize_current_cmd(struct fd_ringbuffer *ring);
1227ec681f3Smrgstatic struct fd_ringbuffer *
1237ec681f3Smrgmsm_ringbuffer_init(struct msm_ringbuffer *msm_ring, uint32_t size,
1247ec681f3Smrg                    enum fd_ringbuffer_flags flags);
1257e102996Smaya
1267e102996Smaya/* add (if needed) bo to submit and return index: */
1277e102996Smayastatic uint32_t
1287ec681f3Smrgappend_bo(struct msm_submit *submit, struct fd_bo *bo)
1297e102996Smaya{
1307ec681f3Smrg   struct msm_bo *msm_bo = to_msm_bo(bo);
1317ec681f3Smrg   uint32_t idx;
1327ec681f3Smrg
1337ec681f3Smrg   /* NOTE: it is legal to use the same bo on different threads for
1347ec681f3Smrg    * different submits.  But it is not legal to use the same submit
1357ec681f3Smrg    * from given threads.
1367ec681f3Smrg    */
1377ec681f3Smrg   idx = READ_ONCE(msm_bo->idx);
1387ec681f3Smrg
1397ec681f3Smrg   if (unlikely((idx >= submit->nr_submit_bos) ||
1407ec681f3Smrg                (submit->submit_bos[idx].handle != bo->handle))) {
1417ec681f3Smrg      uint32_t hash = _mesa_hash_pointer(bo);
1427ec681f3Smrg      struct hash_entry *entry;
1437ec681f3Smrg
1447ec681f3Smrg      entry = _mesa_hash_table_search_pre_hashed(submit->bo_table, hash, bo);
1457ec681f3Smrg      if (entry) {
1467ec681f3Smrg         /* found */
1477ec681f3Smrg         idx = (uint32_t)(uintptr_t)entry->data;
1487ec681f3Smrg      } else {
1497ec681f3Smrg         idx = APPEND(
1507ec681f3Smrg            submit, submit_bos,
1517ec681f3Smrg            (struct drm_msm_gem_submit_bo){
1527ec681f3Smrg               .flags = bo->reloc_flags & (MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE),
1537ec681f3Smrg               .handle = bo->handle,
1547ec681f3Smrg               .presumed = 0,
1557ec681f3Smrg            });
1567ec681f3Smrg         APPEND(submit, bos, fd_bo_ref(bo));
1577ec681f3Smrg
1587ec681f3Smrg         _mesa_hash_table_insert_pre_hashed(submit->bo_table, hash, bo,
1597ec681f3Smrg                                            (void *)(uintptr_t)idx);
1607ec681f3Smrg      }
1617ec681f3Smrg      msm_bo->idx = idx;
1627ec681f3Smrg   }
1637ec681f3Smrg
1647ec681f3Smrg   return idx;
1657e102996Smaya}
1667e102996Smaya
1677e102996Smayastatic void
1687e102996Smayaappend_ring(struct set *set, struct fd_ringbuffer *ring)
1697e102996Smaya{
1707ec681f3Smrg   uint32_t hash = _mesa_hash_pointer(ring);
1717e102996Smaya
1727ec681f3Smrg   if (!_mesa_set_search_pre_hashed(set, hash, ring)) {
1737ec681f3Smrg      fd_ringbuffer_ref(ring);
1747ec681f3Smrg      _mesa_set_add_pre_hashed(set, hash, ring);
1757ec681f3Smrg   }
1767e102996Smaya}
1777e102996Smaya
1787e102996Smayastatic void
1797e102996Smayamsm_submit_suballoc_ring_bo(struct fd_submit *submit,
1807ec681f3Smrg                            struct msm_ringbuffer *msm_ring, uint32_t size)
1817e102996Smaya{
1827ec681f3Smrg   struct msm_submit *msm_submit = to_msm_submit(submit);
1837ec681f3Smrg   unsigned suballoc_offset = 0;
1847ec681f3Smrg   struct fd_bo *suballoc_bo = NULL;
1857e102996Smaya
1867ec681f3Smrg   if (msm_submit->suballoc_ring) {
1877ec681f3Smrg      struct msm_ringbuffer *suballoc_ring =
1887ec681f3Smrg         to_msm_ringbuffer(msm_submit->suballoc_ring);
1897e102996Smaya
1907ec681f3Smrg      suballoc_bo = suballoc_ring->ring_bo;
1917ec681f3Smrg      suballoc_offset =
1927ec681f3Smrg         fd_ringbuffer_size(msm_submit->suballoc_ring) + suballoc_ring->offset;
1937e102996Smaya
1947ec681f3Smrg      suballoc_offset = align(suballoc_offset, 0x10);
1957e102996Smaya
1967ec681f3Smrg      if ((size + suballoc_offset) > suballoc_bo->size) {
1977ec681f3Smrg         suballoc_bo = NULL;
1987ec681f3Smrg      }
1997ec681f3Smrg   }
2007e102996Smaya
2017ec681f3Smrg   if (!suballoc_bo) {
2027ec681f3Smrg      // TODO possibly larger size for streaming bo?
2037ec681f3Smrg      msm_ring->ring_bo = fd_bo_new_ring(submit->pipe->dev, 0x8000);
2047ec681f3Smrg      msm_ring->offset = 0;
2057ec681f3Smrg   } else {
2067ec681f3Smrg      msm_ring->ring_bo = fd_bo_ref(suballoc_bo);
2077ec681f3Smrg      msm_ring->offset = suballoc_offset;
2087ec681f3Smrg   }
2097e102996Smaya
2107ec681f3Smrg   struct fd_ringbuffer *old_suballoc_ring = msm_submit->suballoc_ring;
2117e102996Smaya
2127ec681f3Smrg   msm_submit->suballoc_ring = fd_ringbuffer_ref(&msm_ring->base);
2137e102996Smaya
2147ec681f3Smrg   if (old_suballoc_ring)
2157ec681f3Smrg      fd_ringbuffer_del(old_suballoc_ring);
2167e102996Smaya}
2177e102996Smaya
2187e102996Smayastatic struct fd_ringbuffer *
2197e102996Smayamsm_submit_new_ringbuffer(struct fd_submit *submit, uint32_t size,
2207ec681f3Smrg                          enum fd_ringbuffer_flags flags)
2217e102996Smaya{
2227ec681f3Smrg   struct msm_submit *msm_submit = to_msm_submit(submit);
2237ec681f3Smrg   struct msm_ringbuffer *msm_ring;
2247e102996Smaya
2257ec681f3Smrg   msm_ring = slab_alloc_st(&msm_submit->ring_pool);
2267e102996Smaya
2277ec681f3Smrg   msm_ring->u.submit = submit;
2287e102996Smaya
2297ec681f3Smrg   /* NOTE: needs to be before _suballoc_ring_bo() since it could
2307ec681f3Smrg    * increment the refcnt of the current ring
2317ec681f3Smrg    */
2327ec681f3Smrg   msm_ring->base.refcnt = 1;
2337e102996Smaya
2347ec681f3Smrg   if (flags & FD_RINGBUFFER_STREAMING) {
2357ec681f3Smrg      msm_submit_suballoc_ring_bo(submit, msm_ring, size);
2367ec681f3Smrg   } else {
2377ec681f3Smrg      if (flags & FD_RINGBUFFER_GROWABLE)
2387ec681f3Smrg         size = INIT_SIZE;
2397e102996Smaya
2407ec681f3Smrg      msm_ring->offset = 0;
2417ec681f3Smrg      msm_ring->ring_bo = fd_bo_new_ring(submit->pipe->dev, size);
2427ec681f3Smrg   }
2437e102996Smaya
2447ec681f3Smrg   if (!msm_ringbuffer_init(msm_ring, size, flags))
2457ec681f3Smrg      return NULL;
2467e102996Smaya
2477ec681f3Smrg   return &msm_ring->base;
2487e102996Smaya}
2497e102996Smaya
2507e102996Smayastatic struct drm_msm_gem_submit_reloc *
2517e102996Smayahandle_stateobj_relocs(struct msm_submit *submit, struct msm_ringbuffer *ring)
2527e102996Smaya{
2537ec681f3Smrg   struct msm_cmd *cmd = ring->cmd;
2547ec681f3Smrg   struct drm_msm_gem_submit_reloc *relocs;
2557e102996Smaya
2567ec681f3Smrg   relocs = malloc(cmd->nr_relocs * sizeof(*relocs));
2577e102996Smaya
2587ec681f3Smrg   for (unsigned i = 0; i < cmd->nr_relocs; i++) {
2597ec681f3Smrg      unsigned idx = cmd->relocs[i].reloc_idx;
2607ec681f3Smrg      struct fd_bo *bo = ring->u.reloc_bos[idx];
2617e102996Smaya
2627ec681f3Smrg      relocs[i] = cmd->relocs[i];
2637ec681f3Smrg      relocs[i].reloc_idx = append_bo(submit, bo);
2647ec681f3Smrg   }
2657e102996Smaya
2667ec681f3Smrg   return relocs;
2677e102996Smaya}
2687e102996Smaya
2697e102996Smayastatic int
2707e102996Smayamsm_submit_flush(struct fd_submit *submit, int in_fence_fd,
2717ec681f3Smrg                 struct fd_submit_fence *out_fence)
2727e102996Smaya{
2737ec681f3Smrg   struct msm_submit *msm_submit = to_msm_submit(submit);
2747ec681f3Smrg   struct msm_pipe *msm_pipe = to_msm_pipe(submit->pipe);
2757ec681f3Smrg   struct drm_msm_gem_submit req = {
2767ec681f3Smrg      .flags = msm_pipe->pipe,
2777ec681f3Smrg      .queueid = msm_pipe->queue_id,
2787ec681f3Smrg   };
2797ec681f3Smrg   int ret;
2807ec681f3Smrg
2817ec681f3Smrg   finalize_current_cmd(submit->primary);
2827ec681f3Smrg   append_ring(msm_submit->ring_set, submit->primary);
2837ec681f3Smrg
2847ec681f3Smrg   unsigned nr_cmds = 0;
2857ec681f3Smrg   unsigned nr_objs = 0;
2867ec681f3Smrg
2877ec681f3Smrg   set_foreach (msm_submit->ring_set, entry) {
2887ec681f3Smrg      struct fd_ringbuffer *ring = (void *)entry->key;
2897ec681f3Smrg      if (ring->flags & _FD_RINGBUFFER_OBJECT) {
2907ec681f3Smrg         nr_cmds += 1;
2917ec681f3Smrg         nr_objs += 1;
2927ec681f3Smrg      } else {
2937ec681f3Smrg         if (ring != submit->primary)
2947ec681f3Smrg            finalize_current_cmd(ring);
2957ec681f3Smrg         nr_cmds += to_msm_ringbuffer(ring)->u.nr_cmds;
2967ec681f3Smrg      }
2977ec681f3Smrg   }
2987ec681f3Smrg
2997ec681f3Smrg   void *obj_relocs[nr_objs];
3007ec681f3Smrg   struct drm_msm_gem_submit_cmd cmds[nr_cmds];
3017ec681f3Smrg   unsigned i = 0, o = 0;
3027ec681f3Smrg
3037ec681f3Smrg   set_foreach (msm_submit->ring_set, entry) {
3047ec681f3Smrg      struct fd_ringbuffer *ring = (void *)entry->key;
3057ec681f3Smrg      struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
3067ec681f3Smrg
3077ec681f3Smrg      debug_assert(i < nr_cmds);
3087ec681f3Smrg
3097ec681f3Smrg      // TODO handle relocs:
3107ec681f3Smrg      if (ring->flags & _FD_RINGBUFFER_OBJECT) {
3117ec681f3Smrg
3127ec681f3Smrg         debug_assert(o < nr_objs);
3137ec681f3Smrg
3147ec681f3Smrg         void *relocs = handle_stateobj_relocs(msm_submit, msm_ring);
3157ec681f3Smrg         obj_relocs[o++] = relocs;
3167ec681f3Smrg
3177ec681f3Smrg         cmds[i].type = MSM_SUBMIT_CMD_IB_TARGET_BUF;
3187ec681f3Smrg         cmds[i].submit_idx = append_bo(msm_submit, msm_ring->ring_bo);
3197ec681f3Smrg         cmds[i].submit_offset = msm_ring->offset;
3207ec681f3Smrg         cmds[i].size = offset_bytes(ring->cur, ring->start);
3217ec681f3Smrg         cmds[i].pad = 0;
3227ec681f3Smrg         cmds[i].nr_relocs = msm_ring->cmd->nr_relocs;
3237ec681f3Smrg         cmds[i].relocs = VOID2U64(relocs);
3247ec681f3Smrg
3257ec681f3Smrg         i++;
3267ec681f3Smrg      } else {
3277ec681f3Smrg         for (unsigned j = 0; j < msm_ring->u.nr_cmds; j++) {
3287ec681f3Smrg            if (ring->flags & FD_RINGBUFFER_PRIMARY) {
3297ec681f3Smrg               cmds[i].type = MSM_SUBMIT_CMD_BUF;
3307ec681f3Smrg            } else {
3317ec681f3Smrg               cmds[i].type = MSM_SUBMIT_CMD_IB_TARGET_BUF;
3327ec681f3Smrg            }
3337ec681f3Smrg            cmds[i].submit_idx =
3347ec681f3Smrg               append_bo(msm_submit, msm_ring->u.cmds[j]->ring_bo);
3357ec681f3Smrg            cmds[i].submit_offset = msm_ring->offset;
3367ec681f3Smrg            cmds[i].size = msm_ring->u.cmds[j]->size;
3377ec681f3Smrg            cmds[i].pad = 0;
3387ec681f3Smrg            cmds[i].nr_relocs = msm_ring->u.cmds[j]->nr_relocs;
3397ec681f3Smrg            cmds[i].relocs = VOID2U64(msm_ring->u.cmds[j]->relocs);
3407ec681f3Smrg
3417ec681f3Smrg            i++;
3427ec681f3Smrg         }
3437ec681f3Smrg      }
3447ec681f3Smrg   }
3457ec681f3Smrg
3467ec681f3Smrg   simple_mtx_lock(&table_lock);
3477ec681f3Smrg   for (unsigned j = 0; j < msm_submit->nr_bos; j++) {
3487ec681f3Smrg      fd_bo_add_fence(msm_submit->bos[j], submit->pipe, submit->fence);
3497ec681f3Smrg   }
3507ec681f3Smrg   simple_mtx_unlock(&table_lock);
3517ec681f3Smrg
3527ec681f3Smrg   if (in_fence_fd != -1) {
3537ec681f3Smrg      req.flags |= MSM_SUBMIT_FENCE_FD_IN | MSM_SUBMIT_NO_IMPLICIT;
3547ec681f3Smrg      req.fence_fd = in_fence_fd;
3557ec681f3Smrg   }
3567ec681f3Smrg
3577ec681f3Smrg   if (out_fence && out_fence->use_fence_fd) {
3587ec681f3Smrg      req.flags |= MSM_SUBMIT_FENCE_FD_OUT;
3597ec681f3Smrg   }
3607ec681f3Smrg
3617ec681f3Smrg   /* needs to be after get_cmd() as that could create bos/cmds table: */
3627ec681f3Smrg   req.bos = VOID2U64(msm_submit->submit_bos),
3637ec681f3Smrg   req.nr_bos = msm_submit->nr_submit_bos;
3647ec681f3Smrg   req.cmds = VOID2U64(cmds), req.nr_cmds = nr_cmds;
3657ec681f3Smrg
3667ec681f3Smrg   DEBUG_MSG("nr_cmds=%u, nr_bos=%u", req.nr_cmds, req.nr_bos);
3677ec681f3Smrg
3687ec681f3Smrg   ret = drmCommandWriteRead(submit->pipe->dev->fd, DRM_MSM_GEM_SUBMIT, &req,
3697ec681f3Smrg                             sizeof(req));
3707ec681f3Smrg   if (ret) {
3717ec681f3Smrg      ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno));
3727ec681f3Smrg      msm_dump_submit(&req);
3737ec681f3Smrg   } else if (!ret && out_fence) {
3747ec681f3Smrg      out_fence->fence.kfence = req.fence;
3757ec681f3Smrg      out_fence->fence.ufence = submit->fence;
3767ec681f3Smrg      out_fence->fence_fd = req.fence_fd;
3777ec681f3Smrg   }
3787ec681f3Smrg
3797ec681f3Smrg   for (unsigned o = 0; o < nr_objs; o++)
3807ec681f3Smrg      free(obj_relocs[o]);
3817ec681f3Smrg
3827ec681f3Smrg   return ret;
3837e102996Smaya}
3847e102996Smaya
3857e102996Smayastatic void
3867e102996Smayaunref_rings(struct set_entry *entry)
3877e102996Smaya{
3887ec681f3Smrg   struct fd_ringbuffer *ring = (void *)entry->key;
3897ec681f3Smrg   fd_ringbuffer_del(ring);
3907e102996Smaya}
3917e102996Smaya
3927e102996Smayastatic void
3937e102996Smayamsm_submit_destroy(struct fd_submit *submit)
3947e102996Smaya{
3957ec681f3Smrg   struct msm_submit *msm_submit = to_msm_submit(submit);
3967e102996Smaya
3977ec681f3Smrg   if (msm_submit->suballoc_ring)
3987ec681f3Smrg      fd_ringbuffer_del(msm_submit->suballoc_ring);
3997e102996Smaya
4007ec681f3Smrg   _mesa_hash_table_destroy(msm_submit->bo_table, NULL);
4017ec681f3Smrg   _mesa_set_destroy(msm_submit->ring_set, unref_rings);
4027e102996Smaya
4037ec681f3Smrg   // TODO it would be nice to have a way to debug_assert() if all
4047ec681f3Smrg   // rb's haven't been free'd back to the slab, because that is
4057ec681f3Smrg   // an indication that we are leaking bo's
4067ec681f3Smrg   slab_destroy(&msm_submit->ring_pool);
4077e102996Smaya
4087ec681f3Smrg   for (unsigned i = 0; i < msm_submit->nr_bos; i++)
4097ec681f3Smrg      fd_bo_del(msm_submit->bos[i]);
4107e102996Smaya
4117ec681f3Smrg   free(msm_submit->submit_bos);
4127ec681f3Smrg   free(msm_submit->bos);
4137ec681f3Smrg   free(msm_submit);
4147e102996Smaya}
4157e102996Smaya
4167e102996Smayastatic const struct fd_submit_funcs submit_funcs = {
4177ec681f3Smrg   .new_ringbuffer = msm_submit_new_ringbuffer,
4187ec681f3Smrg   .flush = msm_submit_flush,
4197ec681f3Smrg   .destroy = msm_submit_destroy,
4207e102996Smaya};
4217e102996Smaya
4227e102996Smayastruct fd_submit *
4237e102996Smayamsm_submit_new(struct fd_pipe *pipe)
4247e102996Smaya{
4257ec681f3Smrg   struct msm_submit *msm_submit = calloc(1, sizeof(*msm_submit));
4267ec681f3Smrg   struct fd_submit *submit;
4277e102996Smaya
4287ec681f3Smrg   msm_submit->bo_table = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
4297ec681f3Smrg                                                  _mesa_key_pointer_equal);
4307ec681f3Smrg   msm_submit->ring_set =
4317ec681f3Smrg      _mesa_set_create(NULL, _mesa_hash_pointer, _mesa_key_pointer_equal);
4327ec681f3Smrg   // TODO tune size:
4337ec681f3Smrg   slab_create(&msm_submit->ring_pool, sizeof(struct msm_ringbuffer), 16);
4347ec681f3Smrg
4357ec681f3Smrg   submit = &msm_submit->base;
4367ec681f3Smrg   submit->funcs = &submit_funcs;
4377ec681f3Smrg
4387ec681f3Smrg   return submit;
4397ec681f3Smrg}
4407e102996Smaya
4417e102996Smayastatic void
4427e102996Smayafinalize_current_cmd(struct fd_ringbuffer *ring)
4437e102996Smaya{
4447ec681f3Smrg   struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
4457e102996Smaya
4467ec681f3Smrg   debug_assert(!(ring->flags & _FD_RINGBUFFER_OBJECT));
4477e102996Smaya
4487ec681f3Smrg   if (!msm_ring->cmd)
4497ec681f3Smrg      return;
4507e102996Smaya
4517ec681f3Smrg   debug_assert(msm_ring->cmd->ring_bo == msm_ring->ring_bo);
4527e102996Smaya
4537ec681f3Smrg   msm_ring->cmd->size = offset_bytes(ring->cur, ring->start);
4547ec681f3Smrg   APPEND(&msm_ring->u, cmds, msm_ring->cmd);
4557ec681f3Smrg   msm_ring->cmd = NULL;
4567e102996Smaya}
4577e102996Smaya
4587e102996Smayastatic void
4597e102996Smayamsm_ringbuffer_grow(struct fd_ringbuffer *ring, uint32_t size)
4607e102996Smaya{
4617ec681f3Smrg   struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
4627ec681f3Smrg   struct fd_pipe *pipe = msm_ring->u.submit->pipe;
4637e102996Smaya
4647ec681f3Smrg   debug_assert(ring->flags & FD_RINGBUFFER_GROWABLE);
4657e102996Smaya
4667ec681f3Smrg   finalize_current_cmd(ring);
4677e102996Smaya
4687ec681f3Smrg   fd_bo_del(msm_ring->ring_bo);
4697ec681f3Smrg   msm_ring->ring_bo = fd_bo_new_ring(pipe->dev, size);
4707ec681f3Smrg   msm_ring->cmd = cmd_new(msm_ring->ring_bo);
4717e102996Smaya
4727ec681f3Smrg   ring->start = fd_bo_map(msm_ring->ring_bo);
4737ec681f3Smrg   ring->end = &(ring->start[size / 4]);
4747ec681f3Smrg   ring->cur = ring->start;
4757ec681f3Smrg   ring->size = size;
4767e102996Smaya}
4777e102996Smaya
4787e102996Smayastatic void
4797e102996Smayamsm_ringbuffer_emit_reloc(struct fd_ringbuffer *ring,
4807ec681f3Smrg                          const struct fd_reloc *reloc)
4817e102996Smaya{
4827ec681f3Smrg   struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
4837ec681f3Smrg   struct fd_pipe *pipe;
4847ec681f3Smrg   unsigned reloc_idx;
4857ec681f3Smrg
4867ec681f3Smrg   if (ring->flags & _FD_RINGBUFFER_OBJECT) {
4877ec681f3Smrg      unsigned idx = APPEND(&msm_ring->u, reloc_bos, fd_bo_ref(reloc->bo));
4887ec681f3Smrg
4897ec681f3Smrg      /* this gets fixed up at submit->flush() time, since this state-
4907ec681f3Smrg       * object rb can be used with many different submits
4917ec681f3Smrg       */
4927ec681f3Smrg      reloc_idx = idx;
4937ec681f3Smrg
4947ec681f3Smrg      pipe = msm_ring->u.pipe;
4957ec681f3Smrg   } else {
4967ec681f3Smrg      struct msm_submit *msm_submit = to_msm_submit(msm_ring->u.submit);
4977ec681f3Smrg
4987ec681f3Smrg      reloc_idx = append_bo(msm_submit, reloc->bo);
4997ec681f3Smrg
5007ec681f3Smrg      pipe = msm_ring->u.submit->pipe;
5017ec681f3Smrg   }
5027ec681f3Smrg
5037ec681f3Smrg   APPEND(msm_ring->cmd, relocs,
5047ec681f3Smrg          (struct drm_msm_gem_submit_reloc){
5057ec681f3Smrg             .reloc_idx = reloc_idx,
5067ec681f3Smrg             .reloc_offset = reloc->offset,
5077ec681f3Smrg             .or = reloc->orlo,
5087ec681f3Smrg             .shift = reloc->shift,
5097ec681f3Smrg             .submit_offset =
5107ec681f3Smrg                offset_bytes(ring->cur, ring->start) + msm_ring->offset,
5117ec681f3Smrg          });
5127ec681f3Smrg
5137ec681f3Smrg   ring->cur++;
5147ec681f3Smrg
5157ec681f3Smrg   if (fd_dev_64b(&pipe->dev_id)) {
5167ec681f3Smrg      APPEND(msm_ring->cmd, relocs,
5177ec681f3Smrg             (struct drm_msm_gem_submit_reloc){
5187ec681f3Smrg                .reloc_idx = reloc_idx,
5197ec681f3Smrg                .reloc_offset = reloc->offset,
5207ec681f3Smrg                .or = reloc->orhi,
5217ec681f3Smrg                .shift = reloc->shift - 32,
5227ec681f3Smrg                .submit_offset =
5237ec681f3Smrg                   offset_bytes(ring->cur, ring->start) + msm_ring->offset,
5247ec681f3Smrg             });
5257ec681f3Smrg
5267ec681f3Smrg      ring->cur++;
5277ec681f3Smrg   }
5287e102996Smaya}
5297e102996Smaya
5307e102996Smayastatic void
5317e102996Smayaappend_stateobj_rings(struct msm_submit *submit, struct fd_ringbuffer *target)
5327e102996Smaya{
5337ec681f3Smrg   struct msm_ringbuffer *msm_target = to_msm_ringbuffer(target);
5347e102996Smaya
5357ec681f3Smrg   debug_assert(target->flags & _FD_RINGBUFFER_OBJECT);
5367e102996Smaya
5377ec681f3Smrg   set_foreach (msm_target->u.ring_set, entry) {
5387ec681f3Smrg      struct fd_ringbuffer *ring = (void *)entry->key;
5397e102996Smaya
5407ec681f3Smrg      append_ring(submit->ring_set, ring);
5417e102996Smaya
5427ec681f3Smrg      if (ring->flags & _FD_RINGBUFFER_OBJECT) {
5437ec681f3Smrg         append_stateobj_rings(submit, ring);
5447ec681f3Smrg      }
5457ec681f3Smrg   }
5467e102996Smaya}
5477e102996Smaya
5487e102996Smayastatic uint32_t
5497e102996Smayamsm_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
5507ec681f3Smrg                               struct fd_ringbuffer *target, uint32_t cmd_idx)
5517e102996Smaya{
5527ec681f3Smrg   struct msm_ringbuffer *msm_target = to_msm_ringbuffer(target);
5537ec681f3Smrg   struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
5547ec681f3Smrg   struct fd_bo *bo;
5557ec681f3Smrg   uint32_t size;
5567ec681f3Smrg
5577ec681f3Smrg   if ((target->flags & FD_RINGBUFFER_GROWABLE) &&
5587ec681f3Smrg       (cmd_idx < msm_target->u.nr_cmds)) {
5597ec681f3Smrg      bo = msm_target->u.cmds[cmd_idx]->ring_bo;
5607ec681f3Smrg      size = msm_target->u.cmds[cmd_idx]->size;
5617ec681f3Smrg   } else {
5627ec681f3Smrg      bo = msm_target->ring_bo;
5637ec681f3Smrg      size = offset_bytes(target->cur, target->start);
5647ec681f3Smrg   }
5657ec681f3Smrg
5667ec681f3Smrg   msm_ringbuffer_emit_reloc(ring, &(struct fd_reloc){
5677ec681f3Smrg                                      .bo = bo,
5687ec681f3Smrg                                      .iova = bo->iova + msm_target->offset,
5697ec681f3Smrg                                      .offset = msm_target->offset,
5707ec681f3Smrg                                   });
5717ec681f3Smrg
5727ec681f3Smrg   if (!size)
5737ec681f3Smrg      return 0;
5747ec681f3Smrg
5757ec681f3Smrg   if ((target->flags & _FD_RINGBUFFER_OBJECT) &&
5767ec681f3Smrg       !(ring->flags & _FD_RINGBUFFER_OBJECT)) {
5777ec681f3Smrg      struct msm_submit *msm_submit = to_msm_submit(msm_ring->u.submit);
5787ec681f3Smrg
5797ec681f3Smrg      append_stateobj_rings(msm_submit, target);
5807ec681f3Smrg   }
5817ec681f3Smrg
5827ec681f3Smrg   if (ring->flags & _FD_RINGBUFFER_OBJECT) {
5837ec681f3Smrg      append_ring(msm_ring->u.ring_set, target);
5847ec681f3Smrg   } else {
5857ec681f3Smrg      struct msm_submit *msm_submit = to_msm_submit(msm_ring->u.submit);
5867ec681f3Smrg      append_ring(msm_submit->ring_set, target);
5877ec681f3Smrg   }
5887ec681f3Smrg
5897ec681f3Smrg   return size;
5907e102996Smaya}
5917e102996Smaya
5927e102996Smayastatic uint32_t
5937e102996Smayamsm_ringbuffer_cmd_count(struct fd_ringbuffer *ring)
5947e102996Smaya{
5957ec681f3Smrg   if (ring->flags & FD_RINGBUFFER_GROWABLE)
5967ec681f3Smrg      return to_msm_ringbuffer(ring)->u.nr_cmds + 1;
5977ec681f3Smrg   return 1;
5987ec681f3Smrg}
5997ec681f3Smrg
6007ec681f3Smrgstatic bool
6017ec681f3Smrgmsm_ringbuffer_check_size(struct fd_ringbuffer *ring)
6027ec681f3Smrg{
6037ec681f3Smrg   assert(!(ring->flags & _FD_RINGBUFFER_OBJECT));
6047ec681f3Smrg   struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
6057ec681f3Smrg   struct fd_submit *submit = msm_ring->u.submit;
6067ec681f3Smrg   struct fd_pipe *pipe = submit->pipe;
6077ec681f3Smrg
6087ec681f3Smrg   if ((fd_device_version(pipe->dev) < FD_VERSION_UNLIMITED_CMDS) &&
6097ec681f3Smrg       ((ring->cur - ring->start) > (ring->size / 4 - 0x1000))) {
6107ec681f3Smrg      return false;
6117ec681f3Smrg   }
6127ec681f3Smrg
6137ec681f3Smrg   if (to_msm_submit(submit)->nr_bos > MAX_ARRAY_SIZE/2) {
6147ec681f3Smrg      return false;
6157ec681f3Smrg   }
6167ec681f3Smrg
6177ec681f3Smrg   return true;
6187e102996Smaya}
6197e102996Smaya
6207e102996Smayastatic void
6217e102996Smayamsm_ringbuffer_destroy(struct fd_ringbuffer *ring)
6227e102996Smaya{
6237ec681f3Smrg   struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
6247e102996Smaya
6257ec681f3Smrg   fd_bo_del(msm_ring->ring_bo);
6267ec681f3Smrg   if (msm_ring->cmd)
6277ec681f3Smrg      cmd_free(msm_ring->cmd);
6287e102996Smaya
6297ec681f3Smrg   if (ring->flags & _FD_RINGBUFFER_OBJECT) {
6307ec681f3Smrg      for (unsigned i = 0; i < msm_ring->u.nr_reloc_bos; i++) {
6317ec681f3Smrg         fd_bo_del(msm_ring->u.reloc_bos[i]);
6327ec681f3Smrg      }
6337e102996Smaya
6347ec681f3Smrg      _mesa_set_destroy(msm_ring->u.ring_set, unref_rings);
6357e102996Smaya
6367ec681f3Smrg      free(msm_ring->u.reloc_bos);
6377ec681f3Smrg      free(msm_ring);
6387ec681f3Smrg   } else {
6397ec681f3Smrg      struct fd_submit *submit = msm_ring->u.submit;
6407e102996Smaya
6417ec681f3Smrg      for (unsigned i = 0; i < msm_ring->u.nr_cmds; i++) {
6427ec681f3Smrg         cmd_free(msm_ring->u.cmds[i]);
6437ec681f3Smrg      }
6447e102996Smaya
6457ec681f3Smrg      free(msm_ring->u.cmds);
6467ec681f3Smrg      slab_free_st(&to_msm_submit(submit)->ring_pool, msm_ring);
6477ec681f3Smrg   }
6487e102996Smaya}
6497e102996Smaya
6507e102996Smayastatic const struct fd_ringbuffer_funcs ring_funcs = {
6517ec681f3Smrg   .grow = msm_ringbuffer_grow,
6527ec681f3Smrg   .emit_reloc = msm_ringbuffer_emit_reloc,
6537ec681f3Smrg   .emit_reloc_ring = msm_ringbuffer_emit_reloc_ring,
6547ec681f3Smrg   .cmd_count = msm_ringbuffer_cmd_count,
6557ec681f3Smrg   .check_size = msm_ringbuffer_check_size,
6567ec681f3Smrg   .destroy = msm_ringbuffer_destroy,
6577e102996Smaya};
6587e102996Smaya
6597e102996Smayastatic inline struct fd_ringbuffer *
6607e102996Smayamsm_ringbuffer_init(struct msm_ringbuffer *msm_ring, uint32_t size,
6617ec681f3Smrg                    enum fd_ringbuffer_flags flags)
6627e102996Smaya{
6637ec681f3Smrg   struct fd_ringbuffer *ring = &msm_ring->base;
6647e102996Smaya
6657ec681f3Smrg   debug_assert(msm_ring->ring_bo);
6667e102996Smaya
6677ec681f3Smrg   uint8_t *base = fd_bo_map(msm_ring->ring_bo);
6687ec681f3Smrg   ring->start = (void *)(base + msm_ring->offset);
6697ec681f3Smrg   ring->end = &(ring->start[size / 4]);
6707ec681f3Smrg   ring->cur = ring->start;
6717e102996Smaya
6727ec681f3Smrg   ring->size = size;
6737ec681f3Smrg   ring->flags = flags;
6747e102996Smaya
6757ec681f3Smrg   ring->funcs = &ring_funcs;
6767e102996Smaya
6777ec681f3Smrg   msm_ring->u.cmds = NULL;
6787ec681f3Smrg   msm_ring->u.nr_cmds = msm_ring->u.max_cmds = 0;
6797e102996Smaya
6807ec681f3Smrg   msm_ring->cmd = cmd_new(msm_ring->ring_bo);
6817e102996Smaya
6827ec681f3Smrg   return ring;
6837e102996Smaya}
6847e102996Smaya
6857e102996Smayastruct fd_ringbuffer *
6867e102996Smayamsm_ringbuffer_new_object(struct fd_pipe *pipe, uint32_t size)
6877e102996Smaya{
6887ec681f3Smrg   struct msm_ringbuffer *msm_ring = malloc(sizeof(*msm_ring));
6897e102996Smaya
6907ec681f3Smrg   msm_ring->u.pipe = pipe;
6917ec681f3Smrg   msm_ring->offset = 0;
6927ec681f3Smrg   msm_ring->ring_bo = fd_bo_new_ring(pipe->dev, size);
6937ec681f3Smrg   msm_ring->base.refcnt = 1;
6947e102996Smaya
6957ec681f3Smrg   msm_ring->u.reloc_bos = NULL;
6967ec681f3Smrg   msm_ring->u.nr_reloc_bos = msm_ring->u.max_reloc_bos = 0;
6977e102996Smaya
6987ec681f3Smrg   msm_ring->u.ring_set =
6997ec681f3Smrg      _mesa_set_create(NULL, _mesa_hash_pointer, _mesa_key_pointer_equal);
7007e102996Smaya
7017ec681f3Smrg   return msm_ringbuffer_init(msm_ring, size, _FD_RINGBUFFER_OBJECT);
7027e102996Smaya}
703