• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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