1 /*
2 * Copyright © 2022 Imagination Technologies Ltd.
3 *
4 * based in part on anv driver which is:
5 * Copyright © 2015 Intel Corporation
6 *
7 * based in part on v3dv_cl.c which is:
8 * Copyright © 2019 Raspberry Pi
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice (including the next
18 * paragraph) shall be included in all copies or substantial portions of the
19 * Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * SOFTWARE.
28 */
29
30 #include <assert.h>
31 #include <stdbool.h>
32 #include <stdint.h>
33 #include <vulkan/vulkan.h>
34
35 #include "hwdef/rogue_hw_utils.h"
36 #include "pvr_bo.h"
37 #include "pvr_csb.h"
38 #include "pvr_device_info.h"
39 #include "pvr_private.h"
40 #include "vk_log.h"
41
42 /**
43 * \file pvr_csb.c
44 *
45 * \brief Contains functions to manage Control Stream Builder (csb) object.
46 *
47 * A csb object can be used to create a primary/main control stream, referred
48 * as control stream hereafter, or a secondary control stream, also referred as
49 * a sub control stream. The main difference between these is that, the control
50 * stream is the one directly submitted to the GPU and is terminated using
51 * STREAM_TERMINATE. Whereas, the secondary control stream can be thought of as
52 * an independent set of commands that can be referenced by a primary control
53 * stream to avoid duplication and is instead terminated using STREAM_RETURN,
54 * which means the control stream parser should return to the main stream it
55 * came from.
56 *
57 * Note: Sub control stream is only supported for PVR_CMD_STREAM_TYPE_GRAPHICS
58 * type control streams.
59 */
60
61 /**
62 * \brief Size of the individual csb buffer object.
63 */
64 #define PVR_CMD_BUFFER_CSB_BO_SIZE 4096
65
66 /**
67 * \brief Initializes the csb object.
68 *
69 * \param[in] device Logical device pointer.
70 * \param[in] csb Control Stream Builder object to initialize.
71 *
72 * \sa #pvr_csb_finish()
73 */
pvr_csb_init(struct pvr_device * device,enum pvr_cmd_stream_type stream_type,struct pvr_csb * csb)74 void pvr_csb_init(struct pvr_device *device,
75 enum pvr_cmd_stream_type stream_type,
76 struct pvr_csb *csb)
77 {
78 csb->start = NULL;
79 csb->next = NULL;
80 csb->pvr_bo = NULL;
81 csb->end = NULL;
82 csb->device = device;
83 csb->stream_type = stream_type;
84 csb->status = VK_SUCCESS;
85 list_inithead(&csb->pvr_bo_list);
86 }
87
88 /**
89 * \brief Frees the resources associated with the csb object.
90 *
91 * \param[in] csb Control Stream Builder object to free.
92 *
93 * \sa #pvr_csb_init()
94 */
pvr_csb_finish(struct pvr_csb * csb)95 void pvr_csb_finish(struct pvr_csb *csb)
96 {
97 list_for_each_entry_safe (struct pvr_bo, pvr_bo, &csb->pvr_bo_list, link) {
98 list_del(&pvr_bo->link);
99 pvr_bo_free(csb->device, pvr_bo);
100 }
101
102 /* Leave the csb in a reset state to catch use after destroy instances */
103 pvr_csb_init(NULL, PVR_CMD_STREAM_TYPE_INVALID, csb);
104 }
105
106 /**
107 * \brief Helper function to extend csb memory.
108 *
109 * Allocates a new buffer object and links it with the previous buffer object
110 * using STREAM_LINK dwords and updates csb object to use the new buffer.
111 *
112 * To make sure that we have enough space to emit STREAM_LINK dwords in the
113 * current buffer, a few bytes are reserved at the end, every time a buffer is
114 * created. Every time we allocate a new buffer we fix the current buffer in use
115 * to emit the stream link dwords. This makes sure that when
116 * #pvr_csb_alloc_dwords() is called from #pvr_csb_emit() to add STREAM_LINK0
117 * and STREAM_LINK1, it succeeds without trying to allocate new pages.
118 *
119 * \param[in] csb Control Stream Builder object to extend.
120 * \return true on success and false otherwise.
121 */
pvr_csb_buffer_extend(struct pvr_csb * csb)122 static bool pvr_csb_buffer_extend(struct pvr_csb *csb)
123 {
124 const uint8_t stream_link_space = (pvr_cmd_length(VDMCTRL_STREAM_LINK0) +
125 pvr_cmd_length(VDMCTRL_STREAM_LINK1)) *
126 4;
127 const uint32_t cache_line_size =
128 rogue_get_slc_cache_line_size(&csb->device->pdevice->dev_info);
129 struct pvr_bo *pvr_bo;
130 VkResult result;
131
132 /* Make sure extra space allocated for stream links is sufficient for both
133 * stream types.
134 */
135 STATIC_ASSERT((pvr_cmd_length(VDMCTRL_STREAM_LINK0) +
136 pvr_cmd_length(VDMCTRL_STREAM_LINK1)) ==
137 (pvr_cmd_length(CDMCTRL_STREAM_LINK0) +
138 pvr_cmd_length(CDMCTRL_STREAM_LINK1)));
139
140 result = pvr_bo_alloc(csb->device,
141 csb->device->heaps.general_heap,
142 PVR_CMD_BUFFER_CSB_BO_SIZE,
143 cache_line_size,
144 PVR_BO_ALLOC_FLAG_CPU_MAPPED,
145 &pvr_bo);
146 if (result != VK_SUCCESS) {
147 vk_error(csb->device, result);
148 csb->status = result;
149 return false;
150 }
151
152 /* Chain to the old BO if this is not the first BO in csb */
153 if (csb->pvr_bo) {
154 csb->end += stream_link_space;
155 assert(csb->next + stream_link_space <= csb->end);
156
157 switch (csb->stream_type) {
158 case PVR_CMD_STREAM_TYPE_GRAPHICS:
159 pvr_csb_emit (csb, VDMCTRL_STREAM_LINK0, link) {
160 link.link_addrmsb = pvr_bo->vma->dev_addr;
161 }
162
163 pvr_csb_emit (csb, VDMCTRL_STREAM_LINK1, link) {
164 link.link_addrlsb = pvr_bo->vma->dev_addr;
165 }
166
167 break;
168
169 case PVR_CMD_STREAM_TYPE_COMPUTE:
170 pvr_csb_emit (csb, CDMCTRL_STREAM_LINK0, link) {
171 link.link_addrmsb = pvr_bo->vma->dev_addr;
172 }
173
174 pvr_csb_emit (csb, CDMCTRL_STREAM_LINK1, link) {
175 link.link_addrlsb = pvr_bo->vma->dev_addr;
176 }
177
178 break;
179
180 default:
181 unreachable("Unknown stream type");
182 break;
183 }
184 }
185
186 csb->pvr_bo = pvr_bo;
187 csb->start = pvr_bo->bo->map;
188
189 /* Reserve stream link size at the end to make sure we don't run out of
190 * space when a stream link is required.
191 */
192 csb->end = csb->start + pvr_bo->bo->size - stream_link_space;
193 csb->next = csb->start;
194
195 list_addtail(&pvr_bo->link, &csb->pvr_bo_list);
196
197 return true;
198 }
199
200 /**
201 * \brief Provides a chunk of memory from the current csb buffer. In cases where
202 * the buffer is not able to fulfill the required amount of memory,
203 * #pvr_csb_buffer_extend() is called to allocate a new buffer. Maximum size
204 * allocable in bytes is #PVR_CMD_BUFFER_CSB_BO_SIZE - size of STREAM_LINK0
205 * and STREAM_LINK1 dwords.
206 *
207 * \param[in] csb Control Stream Builder object to allocate from.
208 * \param[in] num_dwords Number of dwords to allocate.
209 * \return Valid host virtual address or NULL otherwise.
210 */
pvr_csb_alloc_dwords(struct pvr_csb * csb,uint32_t num_dwords)211 void *pvr_csb_alloc_dwords(struct pvr_csb *csb, uint32_t num_dwords)
212 {
213 const uint32_t required_space = num_dwords * 4;
214
215 if (csb->status != VK_SUCCESS)
216 return NULL;
217
218 if (csb->next + required_space > csb->end) {
219 bool ret = pvr_csb_buffer_extend(csb);
220 if (!ret)
221 return NULL;
222 }
223
224 void *p = csb->next;
225
226 csb->next += required_space;
227 assert(csb->next <= csb->end);
228
229 return p;
230 }
231
232 /**
233 * \brief Adds VDMCTRL_STREAM_RETURN dword into the control stream pointed by
234 * csb object. Given a VDMCTRL_STREAM_RETURN marks the end of the sub control
235 * stream, we return the status of the control stream as well.
236 *
237 * \param[in] csb Control Stream Builder object to add VDMCTRL_STREAM_RETURN to.
238 * \return VK_SUCCESS on success, or error code otherwise.
239 */
pvr_csb_emit_return(struct pvr_csb * csb)240 VkResult pvr_csb_emit_return(struct pvr_csb *csb)
241 {
242 /* STREAM_RETURN is only supported by graphics control stream. */
243 assert(csb->stream_type == PVR_CMD_STREAM_TYPE_GRAPHICS);
244
245 /* clang-format off */
246 pvr_csb_emit(csb, VDMCTRL_STREAM_RETURN, ret);
247 /* clang-format on */
248
249 return csb->status;
250 }
251
252 /**
253 * \brief Adds STREAM_TERMINATE dword into the control stream pointed by csb
254 * object. Given a STREAM_TERMINATE marks the end of the control stream, we
255 * return the status of the control stream as well.
256 *
257 * \param[in] csb Control Stream Builder object to terminate.
258 * \return VK_SUCCESS on success, or error code otherwise.
259 */
pvr_csb_emit_terminate(struct pvr_csb * csb)260 VkResult pvr_csb_emit_terminate(struct pvr_csb *csb)
261 {
262 switch (csb->stream_type) {
263 case PVR_CMD_STREAM_TYPE_GRAPHICS:
264 /* clang-format off */
265 pvr_csb_emit(csb, VDMCTRL_STREAM_TERMINATE, terminate);
266 /* clang-format on */
267 break;
268
269 case PVR_CMD_STREAM_TYPE_COMPUTE:
270 /* clang-format off */
271 pvr_csb_emit(csb, CDMCTRL_STREAM_TERMINATE, terminate);
272 /* clang-format on */
273 break;
274
275 default:
276 unreachable("Unknown stream type");
277 break;
278 }
279
280 return csb->status;
281 }
282