1/*
2 * Copyright © 2019 Raspberry Pi
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 (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24#include "v3dv_private.h"
25
26/* We don't expect that the packets we use in this file change across hw
27 * versions, so we just explicitly set the V3D_VERSION and include v3dx_pack
28 * here
29 */
30#define V3D_VERSION 33
31#include "broadcom/common/v3d_macros.h"
32#include "broadcom/cle/v3dx_pack.h"
33
34void
35v3dv_cl_init(struct v3dv_job *job, struct v3dv_cl *cl)
36{
37   cl->base = NULL;
38   cl->next = cl->base;
39   cl->bo = NULL;
40   cl->size = 0;
41   cl->job = job;
42   list_inithead(&cl->bo_list);
43}
44
45void
46v3dv_cl_destroy(struct v3dv_cl *cl)
47{
48   list_for_each_entry_safe(struct v3dv_bo, bo, &cl->bo_list, list_link) {
49      assert(cl->job);
50      list_del(&bo->list_link);
51      v3dv_bo_free(cl->job->device, bo);
52   }
53
54   /* Leave the CL in a reset state to catch use after destroy instances */
55   v3dv_cl_init(NULL, cl);
56}
57
58static bool
59cl_alloc_bo(struct v3dv_cl *cl, uint32_t space, bool use_branch)
60{
61   struct v3dv_bo *bo = v3dv_bo_alloc(cl->job->device, space, "CL", true);
62   if (!bo) {
63      fprintf(stderr, "failed to allocate memory for command list\n");
64      v3dv_flag_oom(NULL, cl->job);
65      return false;
66   }
67
68   list_addtail(&bo->list_link, &cl->bo_list);
69
70   bool ok = v3dv_bo_map(cl->job->device, bo, bo->size);
71   if (!ok) {
72      fprintf(stderr, "failed to map command list buffer\n");
73      v3dv_flag_oom(NULL, cl->job);
74      return false;
75   }
76
77   /* Chain to the new BO from the old one if requested */
78   if (use_branch && cl->bo) {
79      cl_emit(cl, BRANCH, branch) {
80         branch.address = v3dv_cl_address(bo, 0);
81      }
82   } else {
83      v3dv_job_add_bo_unchecked(cl->job, bo);
84   }
85
86   cl->bo = bo;
87   cl->base = cl->bo->map;
88   cl->size = cl->bo->size;
89   cl->next = cl->base;
90
91   return true;
92}
93
94uint32_t
95v3dv_cl_ensure_space(struct v3dv_cl *cl, uint32_t space, uint32_t alignment)
96{
97   uint32_t offset = align(v3dv_cl_offset(cl), alignment);
98
99   if (offset + space <= cl->size) {
100      cl->next = cl->base + offset;
101      return offset;
102   }
103
104   cl_alloc_bo(cl, space, false);
105   return 0;
106}
107
108void
109v3dv_cl_ensure_space_with_branch(struct v3dv_cl *cl, uint32_t space)
110{
111   /* We do not want to emit branches from secondary command lists, instead,
112    * we will branch to them when we execute them in a primary using
113    * 'branch to sub list' commands, expecting each linked secondary to
114    * end with a 'return from sub list' command.
115    */
116   bool needs_return_from_sub_list = false;
117   if (cl->job->type == V3DV_JOB_TYPE_GPU_CL_SECONDARY) {
118      if (cl->size > 0) {
119         needs_return_from_sub_list = true;
120         space += cl_packet_length(RETURN_FROM_SUB_LIST);
121      }
122   } else {
123      space += cl_packet_length(BRANCH);
124   }
125
126   if (v3dv_cl_offset(cl) + space <= cl->size)
127      return;
128
129   if (needs_return_from_sub_list)
130      cl_emit(cl, RETURN_FROM_SUB_LIST, ret);
131
132   cl_alloc_bo(cl, space, !needs_return_from_sub_list);
133}
134