1 /* Copyright 2022 Advanced Micro Devices, Inc.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a
4 * copy of this software and associated documentation files (the "Software"),
5 * to deal in the Software without restriction, including without limitation
6 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 * and/or sell copies of the Software, and to permit persons to whom the
8 * Software is furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
17 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
18 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
19 * OTHER DEALINGS IN THE SOFTWARE.
20 *
21 * Authors: AMD
22 *
23 */
24
25 #include "vpe_assert.h"
26 #include "config_writer.h"
27 #include "reg_helper.h"
28 #include "common.h"
29
30 // in bytes
31 #define MAX_DIRECT_CONFIG_SIZE (4 * 0x10000)
32 #define MAX_INDIRECT_CONFIG_SIZE ((4 + 16 * 3) * sizeof(uint32_t))
33
config_writer_init(struct config_writer * writer,struct vpe_buf * buf)34 void config_writer_init(struct config_writer *writer, struct vpe_buf *buf)
35 {
36 writer->base_cpu_va = buf->cpu_va;
37 writer->base_gpu_va = buf->gpu_va;
38 writer->buf = buf;
39 writer->type = CONFIG_TYPE_UNKNOWN;
40 writer->callback_ctx = NULL;
41 writer->callback = NULL;
42 writer->completed = false;
43 writer->status = VPE_STATUS_OK;
44 }
45
config_writer_set_callback(struct config_writer * writer,void * callback_ctx,config_callback_t callback)46 void config_writer_set_callback(
47 struct config_writer *writer, void *callback_ctx, config_callback_t callback)
48 {
49 writer->callback_ctx = callback_ctx;
50 writer->callback = callback;
51 }
52
config_writer_new(struct config_writer * writer)53 static inline void config_writer_new(struct config_writer *writer)
54 {
55 if (writer->status != VPE_STATUS_OK)
56 return;
57
58 /* Buffer does not have enough space to write */
59 if (writer->buf->size < sizeof(uint32_t)) {
60 writer->status = VPE_STATUS_BUFFER_OVERFLOW;
61 return;
62 }
63 // new base
64 writer->base_cpu_va = writer->buf->cpu_va;
65 writer->base_gpu_va = writer->buf->gpu_va;
66
67 // new header. don't need to fill it yet until completion
68 writer->buf->cpu_va += sizeof(uint32_t);
69 writer->buf->gpu_va += sizeof(uint32_t);
70 writer->buf->size -= sizeof(uint32_t);
71 writer->completed = false;
72 }
73
config_writer_set_type(struct config_writer * writer,enum config_type type)74 void config_writer_set_type(struct config_writer *writer, enum config_type type)
75 {
76 VPE_ASSERT(type != CONFIG_TYPE_UNKNOWN);
77
78 if (writer->status != VPE_STATUS_OK)
79 return;
80
81 if (writer->type != type) {
82 if (writer->type == CONFIG_TYPE_UNKNOWN) {
83 // new header. don't need to fill it yet until completion
84 config_writer_new(writer);
85 } else {
86 // a new config type, close the previous one
87 config_writer_complete(writer);
88
89 config_writer_new(writer);
90 }
91 writer->type = type;
92 }
93 }
94
config_writer_force_new_with_type(struct config_writer * writer,enum config_type type)95 void config_writer_force_new_with_type(struct config_writer *writer, enum config_type type)
96 {
97 VPE_ASSERT(type != CONFIG_TYPE_UNKNOWN);
98
99 if (writer->status != VPE_STATUS_OK)
100 return;
101
102 uint64_t size = writer->buf->cpu_va - writer->base_cpu_va;
103
104 if (writer->type == CONFIG_TYPE_UNKNOWN) {
105 // new header. don't need to fill it yet until completion
106 config_writer_new(writer);
107 } else if (size > 0) {
108 // command not empty, close the previous one
109 config_writer_complete(writer);
110 config_writer_new(writer);
111 }
112 writer->type = type;
113 }
114
config_writer_fill(struct config_writer * writer,uint32_t value)115 void config_writer_fill(struct config_writer *writer, uint32_t value)
116 {
117 uint32_t *cmd_space;
118 uint64_t size = writer->buf->cpu_va - writer->base_cpu_va;
119
120 VPE_ASSERT(writer->type != CONFIG_TYPE_UNKNOWN);
121
122 if (writer->status != VPE_STATUS_OK)
123 return;
124
125 // check overflow, open a new one if it is
126 if (writer->type == CONFIG_TYPE_DIRECT) {
127 if (size >= MAX_DIRECT_CONFIG_SIZE) {
128 config_writer_complete(writer);
129 config_writer_new(writer);
130 } else if (writer->completed) {
131 config_writer_new(writer);
132 }
133 } else {
134 if (size >= MAX_INDIRECT_CONFIG_SIZE) {
135 config_writer_complete(writer);
136 config_writer_new(writer);
137 } else if (writer->completed) {
138 config_writer_new(writer);
139 }
140 }
141
142 /* Buffer does not have enough space to write */
143 if (writer->buf->size < sizeof(uint32_t)) {
144 writer->status = VPE_STATUS_BUFFER_OVERFLOW;
145 return;
146 }
147
148 cmd_space = (uint32_t *)(uintptr_t)writer->buf->cpu_va;
149 *cmd_space++ = value;
150 writer->buf->cpu_va += sizeof(uint32_t);
151 writer->buf->gpu_va += sizeof(uint32_t);
152 writer->buf->size -= sizeof(uint32_t);
153 }
154
config_writer_fill_direct_config_packet_header(struct config_writer * writer,struct vpep_direct_config_packet * packet)155 void config_writer_fill_direct_config_packet_header(
156 struct config_writer *writer, struct vpep_direct_config_packet *packet)
157 {
158 uint32_t *cmd_space;
159 uint64_t size = writer->buf->cpu_va - writer->base_cpu_va;
160 uint64_t w_size = sizeof(uint32_t);
161
162 VPE_ASSERT(writer->type == CONFIG_TYPE_DIRECT);
163
164 if (writer->status != VPE_STATUS_OK)
165 return;
166
167 // first + 1 for header, DATA_SIZE + 1 for real data size
168 // for estimate overflow, this function only write packet header
169 if (size + (1 + packet->bits.VPEP_CONFIG_DATA_SIZE + 1) * sizeof(uint32_t) >=
170 MAX_DIRECT_CONFIG_SIZE) {
171 config_writer_complete(writer);
172 config_writer_new(writer);
173 } else if (writer->completed) {
174 config_writer_new(writer);
175 }
176
177 /* Buffer does not have enough space to write */
178 if (writer->buf->size < w_size) {
179 writer->status = VPE_STATUS_BUFFER_OVERFLOW;
180 return;
181 }
182
183 cmd_space = (uint32_t *)(uintptr_t)writer->buf->cpu_va;
184 *cmd_space++ = packet->u32all;
185 writer->buf->cpu_va += w_size;
186 writer->buf->gpu_va += w_size;
187 writer->buf->size -= w_size;
188 }
189
config_writer_fill_direct_config_packet(struct config_writer * writer,struct vpep_direct_config_packet * packet)190 void config_writer_fill_direct_config_packet(
191 struct config_writer *writer, struct vpep_direct_config_packet *packet)
192 {
193 uint32_t *cmd_space;
194 uint64_t size = writer->buf->cpu_va - writer->base_cpu_va;
195 uint64_t w_size = 2 * sizeof(uint32_t);
196
197 VPE_ASSERT(writer->type == CONFIG_TYPE_DIRECT);
198 VPE_ASSERT(packet->bits.VPEP_CONFIG_DATA_SIZE == 0);
199 if (writer->status != VPE_STATUS_OK)
200 return;
201
202 // first + 1 for header, DATA_SIZE + 1 for real data size
203 // this function writes both header and the data
204 if (size + 1 + (packet->bits.VPEP_CONFIG_DATA_SIZE + 1) * sizeof(uint32_t) >=
205 MAX_DIRECT_CONFIG_SIZE) {
206 config_writer_complete(writer);
207 config_writer_new(writer);
208 } else if (writer->completed) {
209 config_writer_new(writer);
210 }
211
212 if (writer->buf->size < w_size) {
213 writer->status = VPE_STATUS_BUFFER_OVERFLOW;
214 return;
215 }
216
217 cmd_space = (uint32_t *)(uintptr_t)writer->buf->cpu_va;
218 *cmd_space++ = packet->u32all; // Write header
219 writer->buf->cpu_va += sizeof(uint32_t);
220 writer->buf->gpu_va += sizeof(uint32_t);
221 writer->buf->size -= sizeof(uint32_t);
222 *cmd_space++ = packet->data[0]; // Write data
223 writer->buf->cpu_va += sizeof(uint32_t);
224 writer->buf->gpu_va += sizeof(uint32_t);
225 writer->buf->size -= sizeof(uint32_t);
226 }
227
config_writer_fill_indirect_data_array(struct config_writer * writer,const uint64_t data_gpuva,uint32_t size)228 void config_writer_fill_indirect_data_array(
229 struct config_writer *writer, const uint64_t data_gpuva, uint32_t size)
230 {
231 VPE_ASSERT(writer->type == CONFIG_TYPE_INDIRECT);
232 VPE_ASSERT(size > 0);
233
234 // the DATA_ARRAY_SIZE is 1-based, hence -1 from actual size
235 config_writer_fill(writer, VPEC_FIELD_VALUE(VPE_IND_CFG_DATA_ARRAY_SIZE, size - 1));
236 config_writer_fill(writer, ADDR_LO(data_gpuva));
237 config_writer_fill(writer, ADDR_HI(data_gpuva));
238 }
239
config_writer_fill_indirect_destination(struct config_writer * writer,const uint32_t offset_index,const uint32_t start_index,const uint32_t offset_data)240 void config_writer_fill_indirect_destination(struct config_writer *writer,
241 const uint32_t offset_index, const uint32_t start_index, const uint32_t offset_data)
242 {
243 VPE_ASSERT(writer->type == CONFIG_TYPE_INDIRECT);
244 config_writer_fill(writer, VPEC_FIELD_VALUE(VPE_IND_CFG_PKT_REGISTER_OFFSET, offset_index));
245 config_writer_fill(writer, start_index);
246 config_writer_fill(writer, VPEC_FIELD_VALUE(VPE_IND_CFG_PKT_REGISTER_OFFSET, offset_data));
247 }
248
config_writer_complete(struct config_writer * writer)249 void config_writer_complete(struct config_writer *writer)
250 {
251 uint32_t *cmd_space = (uint32_t *)(uintptr_t)writer->base_cpu_va;
252 uint64_t size = writer->buf->cpu_va - writer->base_cpu_va;
253
254 if (writer->status != VPE_STATUS_OK)
255 return;
256
257 VPE_ASSERT(writer->type != CONFIG_TYPE_UNKNOWN);
258 VPE_ASSERT(writer->buf->cpu_va != writer->base_cpu_va);
259
260 if (writer->type == CONFIG_TYPE_DIRECT) {
261 // -4 for exclude header
262 // VPEP_DIRECT_CONFIG_ARRAY_SIZE is 1-based, hence need -1
263 *cmd_space = VPE_DIR_CFG_CMD_HEADER(((size - 4) / sizeof(uint32_t) - 1));
264 } else {
265 // -4 DW for header, data array size, data array lo and data array hi
266 // /3 DW for each destination reg
267 // NUM_DST is 1-based, hence need -1
268 uint32_t num_dst = (uint32_t)((size - (4 * sizeof(uint32_t))) / (3 * sizeof(uint32_t)) - 1);
269 *cmd_space = VPE_IND_CFG_CMD_HEADER(num_dst);
270 }
271
272 writer->completed = true;
273
274 if (writer->callback) {
275 writer->callback(writer->callback_ctx, writer->base_gpu_va, writer->base_cpu_va, size);
276 }
277 }
278