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 "vpe_command.h"
27 #include "config_writer.h"
28 #include "reg_helper.h"
29 #include "common.h"
30
31 // in bytes
32 #define MAX_DIRECT_CONFIG_SIZE (4 * 0x10000)
33 #define MAX_INDIRECT_CONFIG_SIZE ((4 + 16 * 3) * sizeof(uint32_t))
34
config_writer_init(struct config_writer * writer,struct vpe_buf * buf)35 void config_writer_init(struct config_writer *writer, struct vpe_buf *buf)
36 {
37 writer->base_cpu_va = buf->cpu_va;
38 writer->base_gpu_va = buf->gpu_va;
39 writer->buf = buf;
40 writer->type = CONFIG_TYPE_UNKNOWN;
41 writer->callback_ctx = NULL;
42 writer->callback = NULL;
43 writer->completed = false;
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_new(struct config_writer * writer)54 static inline void config_writer_new(struct config_writer *writer)
55 {
56 if (writer->status != VPE_STATUS_OK)
57 return;
58
59 /* Buffer does not have enough space to write */
60 if (writer->buf->size < sizeof(uint32_t)) {
61 writer->status = VPE_STATUS_BUFFER_OVERFLOW;
62 return;
63 }
64 // new base
65 writer->base_cpu_va = writer->buf->cpu_va;
66 writer->base_gpu_va = writer->buf->gpu_va;
67
68 // new header. don't need to fill it yet until completion
69 writer->buf->cpu_va += sizeof(uint32_t);
70 writer->buf->gpu_va += sizeof(uint32_t);
71 writer->buf->size -= sizeof(uint32_t);
72 writer->completed = false;
73 }
74
config_writer_set_type(struct config_writer * writer,enum config_type type)75 void config_writer_set_type(struct config_writer *writer, enum config_type type)
76 {
77 VPE_ASSERT(type != CONFIG_TYPE_UNKNOWN);
78
79 if (writer->status != VPE_STATUS_OK)
80 return;
81
82 if (writer->type != type) {
83 if (writer->type == CONFIG_TYPE_UNKNOWN) {
84 // new header. don't need to fill it yet until completion
85 config_writer_new(writer);
86 } else {
87 // a new config type, close the previous one
88 config_writer_complete(writer);
89
90 config_writer_new(writer);
91 }
92 writer->type = type;
93 }
94 }
95
config_writer_fill(struct config_writer * writer,uint32_t value)96 void config_writer_fill(struct config_writer *writer, uint32_t value)
97 {
98 uint32_t *cmd_space;
99 uint64_t size = writer->buf->cpu_va - writer->base_cpu_va;
100
101 VPE_ASSERT(writer->type != CONFIG_TYPE_UNKNOWN);
102
103 if (writer->status != VPE_STATUS_OK)
104 return;
105
106 // check overflow, open a new one if it is
107 if (writer->type == CONFIG_TYPE_DIRECT) {
108 if (size >= MAX_DIRECT_CONFIG_SIZE) {
109 config_writer_complete(writer);
110 config_writer_new(writer);
111 } else if (writer->completed) {
112 config_writer_new(writer);
113 }
114 } else {
115 if (size >= MAX_INDIRECT_CONFIG_SIZE) {
116 config_writer_complete(writer);
117 config_writer_new(writer);
118 } else if (writer->completed) {
119 config_writer_new(writer);
120 }
121 }
122
123 /* Buffer does not have enough space to write */
124 if (writer->buf->size < sizeof(uint32_t)) {
125 writer->status = VPE_STATUS_BUFFER_OVERFLOW;
126 return;
127 }
128
129 cmd_space = (uint32_t *)(uintptr_t)writer->buf->cpu_va;
130 *cmd_space++ = value;
131 writer->buf->cpu_va += sizeof(uint32_t);
132 writer->buf->gpu_va += sizeof(uint32_t);
133 writer->buf->size -= sizeof(uint32_t);
134 }
135
config_writer_fill_direct_config_packet_header(struct config_writer * writer,struct vpep_direct_config_packet * packet)136 void config_writer_fill_direct_config_packet_header(
137 struct config_writer *writer, struct vpep_direct_config_packet *packet)
138 {
139 uint32_t *cmd_space;
140 uint64_t size = writer->buf->cpu_va - writer->base_cpu_va;
141 uint64_t w_size = sizeof(uint32_t);
142
143 VPE_ASSERT(writer->type == CONFIG_TYPE_DIRECT);
144
145 if (writer->status != VPE_STATUS_OK)
146 return;
147
148 // first + 1 for header, DATA_SIZE + 1 for real data size
149 // for estimate overflow, this function only write packet header
150 if (size + (1 + packet->bits.VPEP_CONFIG_DATA_SIZE + 1) * sizeof(uint32_t) >=
151 MAX_DIRECT_CONFIG_SIZE) {
152 config_writer_complete(writer);
153 config_writer_new(writer);
154 } else if (writer->completed) {
155 config_writer_new(writer);
156 }
157
158 /* Buffer does not have enough space to write */
159 if (writer->buf->size < w_size) {
160 writer->status = VPE_STATUS_BUFFER_OVERFLOW;
161 return;
162 }
163
164 cmd_space = (uint32_t *)(uintptr_t)writer->buf->cpu_va;
165 *cmd_space++ = packet->u32all;
166 writer->buf->cpu_va += w_size;
167 writer->buf->gpu_va += w_size;
168 writer->buf->size -= w_size;
169 }
170
config_writer_fill_direct_config_packet(struct config_writer * writer,struct vpep_direct_config_packet * packet)171 void config_writer_fill_direct_config_packet(
172 struct config_writer *writer, struct vpep_direct_config_packet *packet)
173 {
174 uint32_t *cmd_space;
175 uint64_t size = writer->buf->cpu_va - writer->base_cpu_va;
176 uint64_t w_size = 2 * sizeof(uint32_t);
177
178 VPE_ASSERT(writer->type == CONFIG_TYPE_DIRECT);
179 VPE_ASSERT(packet->bits.VPEP_CONFIG_DATA_SIZE == 0);
180 if (writer->status != VPE_STATUS_OK)
181 return;
182
183 // first + 1 for header, DATA_SIZE + 1 for real data size
184 // this function writes both header and the data
185 if (size + 1 + (packet->bits.VPEP_CONFIG_DATA_SIZE + 1) * sizeof(uint32_t) >=
186 MAX_DIRECT_CONFIG_SIZE) {
187 config_writer_complete(writer);
188 config_writer_new(writer);
189 } else if (writer->completed) {
190 config_writer_new(writer);
191 }
192
193 if (writer->buf->size < w_size) {
194 writer->status = VPE_STATUS_BUFFER_OVERFLOW;
195 return;
196 }
197
198 cmd_space = (uint32_t *)(uintptr_t)writer->buf->cpu_va;
199 *cmd_space++ = packet->u32all; // Write header
200 writer->buf->cpu_va += sizeof(uint32_t);
201 writer->buf->gpu_va += sizeof(uint32_t);
202 writer->buf->size -= sizeof(uint32_t);
203 *cmd_space++ = packet->data[0]; // Write data
204 writer->buf->cpu_va += sizeof(uint32_t);
205 writer->buf->gpu_va += sizeof(uint32_t);
206 writer->buf->size -= sizeof(uint32_t);
207 }
208
config_writer_fill_indirect_data_array(struct config_writer * writer,const uint64_t data_gpuva,uint32_t size)209 void config_writer_fill_indirect_data_array(
210 struct config_writer *writer, const uint64_t data_gpuva, uint32_t size)
211 {
212 VPE_ASSERT(writer->type == CONFIG_TYPE_INDIRECT);
213 VPE_ASSERT(size > 0);
214
215 // the DATA_ARRAY_SIZE is 1-based, hence -1 from actual size
216 config_writer_fill(writer, VPEC_FIELD_VALUE(VPE_IND_CFG_DATA_ARRAY_SIZE, size - 1));
217 config_writer_fill(writer, ADDR_LO(data_gpuva));
218 config_writer_fill(writer, ADDR_HI(data_gpuva));
219 }
220
config_writer_fill_indirect_destination(struct config_writer * writer,const uint32_t offset_index,const uint32_t start_index,const uint32_t offset_data)221 void config_writer_fill_indirect_destination(struct config_writer *writer,
222 const uint32_t offset_index, const uint32_t start_index, const uint32_t offset_data)
223 {
224 VPE_ASSERT(writer->type == CONFIG_TYPE_INDIRECT);
225 config_writer_fill(writer, VPEC_FIELD_VALUE(VPE_IND_CFG_PKT_REGISTER_OFFSET, offset_index));
226 config_writer_fill(writer, start_index);
227 config_writer_fill(writer, VPEC_FIELD_VALUE(VPE_IND_CFG_PKT_REGISTER_OFFSET, offset_data));
228 }
229
config_writer_complete(struct config_writer * writer)230 void config_writer_complete(struct config_writer *writer)
231 {
232 uint32_t *cmd_space = (uint32_t *)(uintptr_t)writer->base_cpu_va;
233 uint64_t size = writer->buf->cpu_va - writer->base_cpu_va;
234
235 if (writer->status != VPE_STATUS_OK)
236 return;
237
238 VPE_ASSERT(writer->type != CONFIG_TYPE_UNKNOWN);
239 VPE_ASSERT(writer->buf->cpu_va != writer->base_cpu_va);
240
241 if (writer->type == CONFIG_TYPE_DIRECT) {
242 // -4 for exclude header
243 // VPEP_DIRECT_CONFIG_ARRAY_SIZE is 1-based, hence need -1
244 *cmd_space = VPE_DIR_CFG_CMD_HEADER(((size - 4) / sizeof(uint32_t) - 1));
245 } else {
246 // -4 DW for header, data array size, data array lo and data array hi
247 // /3 DW for each destination reg
248 // NUM_DST is 1-based, hence need -1
249 uint32_t num_dst = (uint32_t)((size - (4 * sizeof(uint32_t))) / (3 * sizeof(uint32_t)) - 1);
250 *cmd_space = VPE_IND_CFG_CMD_HEADER(num_dst);
251 }
252
253 writer->completed = true;
254
255 if (writer->callback) {
256 writer->callback(writer->callback_ctx, writer->base_gpu_va, writer->base_cpu_va, size);
257 }
258 }
259