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