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->pipe_idx = 0;
44 writer->status = VPE_STATUS_OK;
45 }
46
config_writer_set_callback(struct config_writer * writer,void * callback_ctx,config_callback_t callback)47 void config_writer_set_callback(
48 struct config_writer *writer, void *callback_ctx, config_callback_t callback)
49 {
50 writer->callback_ctx = callback_ctx;
51 writer->callback = callback;
52 }
53
config_writer_reset(struct config_writer * writer)54 static void config_writer_reset(struct config_writer *writer)
55 {
56 uint64_t size = writer->buf->cpu_va - writer->base_cpu_va;
57
58 writer->buf->cpu_va -= size;
59 writer->buf->gpu_va -= size;
60 writer->buf->size += size;
61
62 VPE_ASSERT(writer->buf->cpu_va == writer->base_cpu_va);
63 VPE_ASSERT(writer->buf->gpu_va == writer->base_gpu_va);
64 }
65
config_writer_new(struct config_writer * writer)66 static inline void config_writer_new(struct config_writer *writer)
67 {
68 if (writer->status != VPE_STATUS_OK)
69 return;
70
71 uint16_t alignment = writer->gpu_addr_alignment;
72 uint64_t aligned_gpu_address = (writer->buf->gpu_va + alignment) & ~alignment;
73 uint64_t alignment_offset = aligned_gpu_address - writer->buf->gpu_va;
74 writer->buf->gpu_va = aligned_gpu_address;
75 writer->buf->cpu_va = writer->buf->cpu_va + alignment_offset;
76 if (writer->buf->size < alignment_offset) {
77 writer->status = VPE_STATUS_BUFFER_OVERFLOW;
78 return;
79 }
80
81 writer->buf->size -= alignment_offset;
82
83 /* Buffer does not have enough space to write */
84 if (writer->buf->size < sizeof(uint32_t)) {
85 writer->status = VPE_STATUS_BUFFER_OVERFLOW;
86 return;
87 }
88 // new base
89 writer->base_cpu_va = writer->buf->cpu_va;
90 writer->base_gpu_va = writer->buf->gpu_va;
91
92 // new header. don't need to fill it yet until completion
93 writer->buf->cpu_va += sizeof(uint32_t);
94 writer->buf->gpu_va += sizeof(uint32_t);
95 writer->buf->size -= sizeof(uint32_t);
96 writer->completed = false;
97 }
98
config_writer_set_type(struct config_writer * writer,enum config_type type,uint32_t pipe_idx)99 void config_writer_set_type(struct config_writer *writer, enum config_type type, uint32_t pipe_idx)
100 {
101 VPE_ASSERT(type != CONFIG_TYPE_UNKNOWN);
102
103 if (writer->status != VPE_STATUS_OK)
104 return;
105
106 if ((writer->type != type) || (writer->pipe_idx != pipe_idx)) {
107 if (writer->type == CONFIG_TYPE_UNKNOWN) {
108 // new header or only pipe change. don't need to fill it yet until completion
109 writer->pipe_idx = pipe_idx;
110 config_writer_new(writer);
111 } else {
112 // a new config type, close the previous one
113 config_writer_complete(writer);
114
115 writer->pipe_idx = pipe_idx;
116 config_writer_new(writer);
117 }
118 writer->type = type;
119 }
120 }
121
config_writer_force_new_with_type(struct config_writer * writer,enum config_type type)122 void config_writer_force_new_with_type(struct config_writer *writer, enum config_type type)
123 {
124 VPE_ASSERT(type != CONFIG_TYPE_UNKNOWN);
125
126 if (writer->status != VPE_STATUS_OK)
127 return;
128
129 uint64_t size = writer->buf->cpu_va - writer->base_cpu_va;
130
131 if (writer->type == CONFIG_TYPE_UNKNOWN) {
132 // new header. don't need to fill it yet until completion
133 config_writer_new(writer);
134 } else if (size > 0) {
135 // command not empty, close the previous one
136 config_writer_complete(writer);
137 config_writer_new(writer);
138 }
139 writer->type = type;
140 }
141
config_writer_fill(struct config_writer * writer,uint32_t value)142 void config_writer_fill(struct config_writer *writer, uint32_t value)
143 {
144 uint32_t *cmd_space;
145 uint64_t size = writer->buf->cpu_va - writer->base_cpu_va;
146
147 VPE_ASSERT(writer->type != CONFIG_TYPE_UNKNOWN);
148
149 if (writer->status != VPE_STATUS_OK)
150 return;
151
152 // check overflow, open a new one if it is
153 if (writer->type == CONFIG_TYPE_DIRECT) {
154 if (size >= MAX_DIRECT_CONFIG_SIZE) {
155 config_writer_complete(writer);
156 config_writer_new(writer);
157 } else if (writer->completed) {
158 config_writer_new(writer);
159 }
160 } else {
161 if (size >= MAX_INDIRECT_CONFIG_SIZE) {
162 config_writer_complete(writer);
163 config_writer_new(writer);
164 } else if (writer->completed) {
165 config_writer_new(writer);
166 }
167 }
168
169 /* Buffer does not have enough space to write */
170 if (writer->buf->size < sizeof(uint32_t)) {
171 writer->status = VPE_STATUS_BUFFER_OVERFLOW;
172 return;
173 }
174
175 cmd_space = (uint32_t *)(uintptr_t)writer->buf->cpu_va;
176 *cmd_space++ = value;
177 writer->buf->cpu_va += sizeof(uint32_t);
178 writer->buf->gpu_va += sizeof(uint32_t);
179 writer->buf->size -= sizeof(uint32_t);
180 }
181
config_writer_fill_direct_config_packet_header(struct config_writer * writer,struct vpep_direct_config_packet * packet)182 void config_writer_fill_direct_config_packet_header(
183 struct config_writer *writer, struct vpep_direct_config_packet *packet)
184 {
185 uint32_t *cmd_space;
186 uint64_t size = writer->buf->cpu_va - writer->base_cpu_va;
187 uint64_t w_size = sizeof(uint32_t);
188
189 VPE_ASSERT(writer->type == CONFIG_TYPE_DIRECT);
190
191 if (writer->status != VPE_STATUS_OK)
192 return;
193
194 // first + 1 for header, DATA_SIZE + 1 for real data size
195 // for estimate overflow, this function only write packet header
196 if (size + (1 + packet->bits.VPEP_CONFIG_DATA_SIZE + 1) * sizeof(uint32_t) >=
197 MAX_DIRECT_CONFIG_SIZE) {
198 config_writer_complete(writer);
199 config_writer_new(writer);
200 } else if (writer->completed) {
201 config_writer_new(writer);
202 }
203
204 /* Buffer does not have enough space to write */
205 if (writer->buf->size < w_size) {
206 writer->status = VPE_STATUS_BUFFER_OVERFLOW;
207 return;
208 }
209
210 cmd_space = (uint32_t *)(uintptr_t)writer->buf->cpu_va;
211 *cmd_space++ = packet->u32all;
212 writer->buf->cpu_va += w_size;
213 writer->buf->gpu_va += w_size;
214 writer->buf->size -= w_size;
215 }
216
config_writer_fill_direct_config_packet(struct config_writer * writer,struct vpep_direct_config_packet * packet)217 void config_writer_fill_direct_config_packet(
218 struct config_writer *writer, struct vpep_direct_config_packet *packet)
219 {
220 uint32_t *cmd_space;
221 uint64_t size = writer->buf->cpu_va - writer->base_cpu_va;
222 uint64_t w_size = 2 * sizeof(uint32_t);
223
224 VPE_ASSERT(writer->type == CONFIG_TYPE_DIRECT);
225 VPE_ASSERT(packet->bits.VPEP_CONFIG_DATA_SIZE == 0);
226 if (writer->status != VPE_STATUS_OK)
227 return;
228
229 // first + 1 for header, DATA_SIZE + 1 for real data size
230 // this function writes both header and the data
231 if (size + 1 + (packet->bits.VPEP_CONFIG_DATA_SIZE + 1) * sizeof(uint32_t) >=
232 MAX_DIRECT_CONFIG_SIZE) {
233 config_writer_complete(writer);
234 config_writer_new(writer);
235 } else if (writer->completed) {
236 config_writer_new(writer);
237 }
238
239 if (writer->buf->size < w_size) {
240 writer->status = VPE_STATUS_BUFFER_OVERFLOW;
241 return;
242 }
243
244 cmd_space = (uint32_t *)(uintptr_t)writer->buf->cpu_va;
245 *cmd_space++ = packet->u32all; // Write header
246 writer->buf->cpu_va += sizeof(uint32_t);
247 writer->buf->gpu_va += sizeof(uint32_t);
248 writer->buf->size -= sizeof(uint32_t);
249 *cmd_space++ = packet->data[0]; // Write data
250 writer->buf->cpu_va += sizeof(uint32_t);
251 writer->buf->gpu_va += sizeof(uint32_t);
252 writer->buf->size -= sizeof(uint32_t);
253 }
254
config_writer_fill_indirect_data_array(struct config_writer * writer,const uint64_t data_gpuva,uint32_t size)255 void config_writer_fill_indirect_data_array(
256 struct config_writer *writer, const uint64_t data_gpuva, uint32_t size)
257 {
258 VPE_ASSERT(writer->type == CONFIG_TYPE_INDIRECT);
259 VPE_ASSERT(size > 0);
260
261 // the DATA_ARRAY_SIZE is 1-based, hence -1 from actual size
262 config_writer_fill(writer, VPEC_FIELD_VALUE(VPE_IND_CFG_DATA_ARRAY_SIZE, size - 1));
263 config_writer_fill(writer, ADDR_LO(data_gpuva));
264 config_writer_fill(writer, ADDR_HI(data_gpuva));
265 }
266
config_writer_fill_indirect_destination(struct config_writer * writer,const uint32_t offset_index,const uint32_t start_index,const uint32_t offset_data)267 void config_writer_fill_indirect_destination(struct config_writer *writer,
268 const uint32_t offset_index, const uint32_t start_index, const uint32_t offset_data)
269 {
270 VPE_ASSERT(writer->type == CONFIG_TYPE_INDIRECT);
271 config_writer_fill(writer, VPEC_FIELD_VALUE(VPE_IND_CFG_PKT_REGISTER_OFFSET, offset_index));
272 config_writer_fill(writer, start_index);
273 config_writer_fill(writer, VPEC_FIELD_VALUE(VPE_IND_CFG_PKT_REGISTER_OFFSET, offset_data));
274 }
275
config_writer_complete(struct config_writer * writer)276 void config_writer_complete(struct config_writer *writer)
277 {
278 uint32_t *cmd_space = (uint32_t *)(uintptr_t)writer->base_cpu_va;
279 uint64_t size = writer->buf->cpu_va - writer->base_cpu_va;
280
281 if (size <= sizeof(uint32_t)) {
282 config_writer_reset(writer);
283 return;
284 } else if (writer->completed == true) {
285 // completed has already been called for this packet
286 return;
287 }
288
289 if (writer->status != VPE_STATUS_OK)
290 return;
291
292 VPE_ASSERT(writer->type != CONFIG_TYPE_UNKNOWN);
293 VPE_ASSERT(writer->buf->cpu_va != writer->base_cpu_va);
294
295 if (writer->type == CONFIG_TYPE_DIRECT) {
296 // -4 for exclude header
297 // VPEP_DIRECT_CONFIG_ARRAY_SIZE is 1-based, hence need -1
298 *cmd_space = VPE_DIR_CFG_CMD_HEADER(((size - 4) / sizeof(uint32_t) - 1));
299 } else {
300 // -4 DW for header, data array size, data array lo and data array hi
301 // /3 DW for each destination reg
302 // NUM_DST is 1-based, hence need -1
303 uint32_t num_dst = (uint32_t)((size - (4 * sizeof(uint32_t))) / (3 * sizeof(uint32_t)) - 1);
304 *cmd_space = VPE_IND_CFG_CMD_HEADER(num_dst);
305 }
306
307 writer->completed = true;
308
309 if (writer->callback) {
310 writer->callback(
311 writer->callback_ctx, writer->base_gpu_va, writer->base_cpu_va, size, writer->pipe_idx);
312 }
313 }
314