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 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: AMD
23 *
24 */
25 #include "vpe_assert.h"
26 #include "common.h"
27 #include "vpe_priv.h"
28 #include "vpe10_command.h"
29 #include "vpe10_cmd_builder.h"
30 #include "vpe10_vpe_desc_writer.h"
31 #include "reg_helper.h"
32
33 /***** Internal helpers *****/
34 static void get_np_and_subop(struct vpe_priv *vpe_priv, struct vpe_cmd_info *cmd_info, struct plane_desc_header *header);
35
36 static enum VPE_PLANE_CFG_ELEMENT_SIZE vpe_get_element_size(
37 enum vpe_surface_pixel_format format, int plane_idx);
38
vpe10_construct_cmd_builder(struct vpe_priv * vpe_priv,struct cmd_builder * builder)39 void vpe10_construct_cmd_builder(struct vpe_priv *vpe_priv, struct cmd_builder *builder)
40 {
41 builder->build_noops = vpe10_build_noops;
42 builder->build_vpe_cmd = vpe10_build_vpe_cmd;
43 builder->build_plane_descriptor = vpe10_build_plane_descriptor;
44 }
45
vpe10_build_noops(struct vpe_priv * vpe_priv,uint32_t ** ppbuf,uint32_t num_dwords)46 enum vpe_status vpe10_build_noops(struct vpe_priv *vpe_priv, uint32_t **ppbuf, uint32_t num_dwords)
47 {
48 uint32_t i;
49 uint32_t *buffer = *ppbuf;
50 uint32_t noop = VPE_CMD_HEADER(VPE_CMD_OPCODE_NOP, 0);
51
52 for (i = 0; i < num_dwords; i++)
53 *buffer++ = noop;
54
55 *ppbuf = buffer;
56
57 return VPE_STATUS_OK;
58 }
59
vpe10_build_vpe_cmd(struct vpe_priv * vpe_priv,struct vpe_build_bufs * cur_bufs,uint32_t cmd_idx)60 enum vpe_status vpe10_build_vpe_cmd(
61 struct vpe_priv *vpe_priv, struct vpe_build_bufs *cur_bufs, uint32_t cmd_idx)
62 {
63 struct cmd_builder *builder = &vpe_priv->resource.cmd_builder;
64 struct vpe_desc_writer *vpe_desc_writer = &vpe_priv->vpe_desc_writer;
65 struct vpe_buf *emb_buf = &cur_bufs->emb_buf;
66 struct vpe_cmd_info *cmd_info = &vpe_priv->vpe_cmd_info[cmd_idx];
67 struct output_ctx *output_ctx;
68 struct pipe_ctx *pipe_ctx = NULL;
69 uint32_t i, j;
70
71 vpe_desc_writer->init(vpe_desc_writer, &cur_bufs->cmd_buf, cmd_info->cd);
72
73 // plane descriptor
74 builder->build_plane_descriptor(vpe_priv, emb_buf, cmd_idx);
75
76 vpe_desc_writer->add_plane_desc(
77 vpe_desc_writer, vpe_priv->plane_desc_writer.base_gpu_va, (uint8_t)emb_buf->tmz);
78
79 // reclaim any pipe if the owner no longer presents
80 vpe_pipe_reclaim(vpe_priv, cmd_info);
81
82 config_writer_init(&vpe_priv->config_writer, emb_buf);
83
84 // frontend programming
85 for (i = 0; i < cmd_info->num_inputs; i++) {
86 bool reuse;
87 struct stream_ctx *stream_ctx;
88 enum vpe_cmd_type cmd_type = VPE_CMD_TYPE_COUNT;
89
90 // keep using the same pipe whenever possible
91 // this would allow reuse of the previous register configs
92 pipe_ctx = vpe_pipe_find_owner(vpe_priv, cmd_info->inputs[i].stream_idx, &reuse);
93 VPE_ASSERT(pipe_ctx);
94
95 if (!reuse) {
96 vpe_priv->resource.program_frontend(vpe_priv, pipe_ctx->pipe_idx, cmd_idx, i, false);
97 } else {
98 if (vpe_priv->init.debug.disable_reuse_bit)
99 reuse = false;
100
101 stream_ctx = &vpe_priv->stream_ctx[cmd_info->inputs[i].stream_idx];
102
103 // frame specific for same type of command
104 if (cmd_info->ops == VPE_CMD_OPS_BG)
105 cmd_type = VPE_CMD_TYPE_BG;
106 else if (cmd_info->ops == VPE_CMD_OPS_COMPOSITING)
107 cmd_type = VPE_CMD_TYPE_COMPOSITING;
108 else if (cmd_info->ops == VPE_CMD_OPS_BG_VSCF_INPUT)
109 cmd_type = VPE_CMD_TYPE_BG_VSCF_INPUT;
110 else if (cmd_info->ops == VPE_CMD_OPS_BG_VSCF_OUTPUT)
111 cmd_type = VPE_CMD_TYPE_BG_VSCF_OUTPUT;
112 else {
113 VPE_ASSERT(0);
114 return VPE_STATUS_ERROR;
115 }
116
117 // follow the same order of config generation in "non-reuse" case
118 // stream sharing
119 VPE_ASSERT(stream_ctx->num_configs);
120 for (j = 0; j < stream_ctx->num_configs; j++) {
121 vpe_desc_writer->add_config_desc(vpe_desc_writer,
122 stream_ctx->configs[j].config_base_addr, reuse, (uint8_t)emb_buf->tmz);
123 }
124
125 // stream-op sharing
126 for (j = 0; j < stream_ctx->num_stream_op_configs[cmd_type]; j++) {
127 vpe_desc_writer->add_config_desc(vpe_desc_writer,
128 stream_ctx->stream_op_configs[cmd_type][j].config_base_addr, reuse,
129 (uint8_t)emb_buf->tmz);
130 }
131
132 // command specific
133 vpe_priv->resource.program_frontend(vpe_priv, pipe_ctx->pipe_idx, cmd_idx, i, true);
134 }
135 }
136
137 VPE_ASSERT(pipe_ctx);
138
139 // If config writer has been crashed due to buffer overflow
140 if (vpe_priv->config_writer.status != VPE_STATUS_OK) {
141 return vpe_priv->config_writer.status;
142 }
143
144 // backend programming
145 output_ctx = &vpe_priv->output_ctx;
146 if (!output_ctx->num_configs) {
147 vpe_priv->resource.program_backend(vpe_priv, pipe_ctx->pipe_idx, cmd_idx, false);
148 } else {
149 bool reuse = !vpe_priv->init.debug.disable_reuse_bit;
150 // re-use output register configs
151 for (j = 0; j < output_ctx->num_configs; j++) {
152 vpe_desc_writer->add_config_desc(vpe_desc_writer,
153 output_ctx->configs[j].config_base_addr, reuse, (uint8_t)emb_buf->tmz);
154 }
155
156 vpe_priv->resource.program_backend(vpe_priv, pipe_ctx->pipe_idx, cmd_idx, true);
157 }
158
159 /* If writer crashed due to buffer overflow */
160 if (vpe_desc_writer->status != VPE_STATUS_OK) {
161 return vpe_desc_writer->status;
162 }
163 vpe_desc_writer->complete(vpe_desc_writer);
164
165 return VPE_STATUS_OK;
166 }
167
vpe10_build_plane_descriptor(struct vpe_priv * vpe_priv,struct vpe_buf * buf,uint32_t cmd_idx)168 enum vpe_status vpe10_build_plane_descriptor(
169 struct vpe_priv *vpe_priv, struct vpe_buf *buf, uint32_t cmd_idx)
170 {
171 struct stream_ctx *stream_ctx;
172 struct vpe_surface_info *surface_info;
173 int32_t stream_idx;
174 struct vpe_cmd_info *cmd_info;
175 PHYSICAL_ADDRESS_LOC *addrloc;
176 struct plane_desc_src src;
177 struct plane_desc_dst dst;
178 struct plane_desc_header header = {0};
179 struct cmd_builder *builder = &vpe_priv->resource.cmd_builder;
180 struct plane_desc_writer *plane_desc_writer = &vpe_priv->plane_desc_writer;
181
182 cmd_info = &vpe_priv->vpe_cmd_info[cmd_idx];
183
184 VPE_ASSERT(cmd_info->num_inputs == 1);
185
186 // obtains number of planes for each source/destination stream
187 get_np_and_subop(vpe_priv, cmd_info, &header);
188
189 plane_desc_writer->init(&vpe_priv->plane_desc_writer, buf, &header);
190 stream_idx = cmd_info->inputs[0].stream_idx;
191 stream_ctx = &vpe_priv->stream_ctx[stream_idx];
192 surface_info = &stream_ctx->stream.surface_info;
193
194 src.tmz = surface_info->address.tmz_surface;
195 src.swizzle = surface_info->swizzle;
196 src.rotation = stream_ctx->stream.rotation;
197
198 if (surface_info->address.type == VPE_PLN_ADDR_TYPE_VIDEO_PROGRESSIVE) {
199 addrloc = &surface_info->address.video_progressive.luma_addr;
200
201 src.base_addr_lo = addrloc->u.low_part;
202 src.base_addr_hi = (uint32_t)addrloc->u.high_part;
203 src.pitch = (uint16_t)surface_info->plane_size.surface_pitch;
204 src.viewport_x = (uint16_t)cmd_info->inputs[0].scaler_data.viewport.x;
205 src.viewport_y = (uint16_t)cmd_info->inputs[0].scaler_data.viewport.y;
206 src.viewport_w = (uint16_t)cmd_info->inputs[0].scaler_data.viewport.width;
207 src.viewport_h = (uint16_t)cmd_info->inputs[0].scaler_data.viewport.height;
208 src.elem_size = (uint8_t)(vpe_get_element_size(surface_info->format, 0));
209
210 plane_desc_writer->add_source(&vpe_priv->plane_desc_writer, &src, true);
211
212 if (vpe_is_dual_plane_format(surface_info->format)) {
213 addrloc = &surface_info->address.video_progressive.chroma_addr;
214
215 src.base_addr_lo = addrloc->u.low_part;
216 src.base_addr_hi = (uint32_t)addrloc->u.high_part;
217 src.pitch = (uint16_t)surface_info->plane_size.chroma_pitch;
218 src.viewport_x = (uint16_t)cmd_info->inputs[0].scaler_data.viewport_c.x;
219 src.viewport_y = (uint16_t)cmd_info->inputs[0].scaler_data.viewport_c.y;
220 src.viewport_w = (uint16_t)cmd_info->inputs[0].scaler_data.viewport_c.width;
221 src.viewport_h = (uint16_t)cmd_info->inputs[0].scaler_data.viewport_c.height;
222 src.elem_size = (uint8_t)(vpe_get_element_size(surface_info->format, 1));
223
224 plane_desc_writer->add_source(&vpe_priv->plane_desc_writer, &src, false);
225 }
226 } else {
227 addrloc = &surface_info->address.grph.addr;
228
229 src.base_addr_lo = addrloc->u.low_part;
230 src.base_addr_hi = (uint32_t)addrloc->u.high_part;
231 src.pitch = (uint16_t)surface_info->plane_size.surface_pitch;
232 src.viewport_x = (uint16_t)cmd_info->inputs[0].scaler_data.viewport.x;
233 src.viewport_y = (uint16_t)cmd_info->inputs[0].scaler_data.viewport.y;
234 src.viewport_w = (uint16_t)cmd_info->inputs[0].scaler_data.viewport.width;
235 src.viewport_h = (uint16_t)cmd_info->inputs[0].scaler_data.viewport.height;
236 src.elem_size = (uint8_t)(vpe_get_element_size(surface_info->format, 0));
237
238 plane_desc_writer->add_source(&vpe_priv->plane_desc_writer, &src, true);
239 }
240
241 surface_info = &vpe_priv->output_ctx.surface;
242
243 VPE_ASSERT(surface_info->address.type == VPE_PLN_ADDR_TYPE_GRAPHICS);
244
245 addrloc = &surface_info->address.grph.addr;
246
247 dst.tmz = surface_info->address.tmz_surface;
248 dst.swizzle = surface_info->swizzle;
249
250 if (stream_ctx->flip_horizonal_output)
251 dst.mirror = VPE_MIRROR_HORIZONTAL;
252 else
253 dst.mirror = VPE_MIRROR_NONE;
254
255 dst.base_addr_lo = addrloc->u.low_part;
256 dst.base_addr_hi = (uint32_t)addrloc->u.high_part;
257 dst.pitch = (uint16_t)surface_info->plane_size.surface_pitch;
258 dst.viewport_x = (uint16_t)cmd_info->outputs[0].dst_viewport.x;
259 dst.viewport_y = (uint16_t)cmd_info->outputs[0].dst_viewport.y;
260 dst.viewport_w = (uint16_t)cmd_info->outputs[0].dst_viewport.width;
261 dst.viewport_h = (uint16_t)cmd_info->outputs[0].dst_viewport.height;
262 dst.elem_size = (uint8_t)(vpe_get_element_size(surface_info->format, 0));
263
264 plane_desc_writer->add_destination(&vpe_priv->plane_desc_writer, &dst, true);
265
266 return vpe_priv->plane_desc_writer.status;
267 }
268
get_np_and_subop(struct vpe_priv * vpe_priv,struct vpe_cmd_info * cmd_info,struct plane_desc_header * header)269 static void get_np_and_subop(struct vpe_priv *vpe_priv, struct vpe_cmd_info *cmd_info,
270 struct plane_desc_header *header)
271 {
272 header->npd1 = 0;
273
274 header->subop = VPE_PLANE_CFG_SUBOP_1_TO_1;
275
276 if (cmd_info->num_inputs == 1) {
277 header->nps1 = 0;
278 if (vpe_is_dual_plane_format(
279 vpe_priv->stream_ctx[cmd_info->inputs[0].stream_idx].stream.surface_info.format))
280 header->nps0 = VPE_PLANE_CFG_TWO_PLANES;
281 else
282 header->nps0 = VPE_PLANE_CFG_ONE_PLANE;
283 } else if (cmd_info->num_inputs == 2) {
284 if (vpe_is_dual_plane_format(
285 vpe_priv->stream_ctx[cmd_info->inputs[0].stream_idx].stream.surface_info.format))
286 header->nps0 = VPE_PLANE_CFG_TWO_PLANES;
287 else
288 header->nps0 = VPE_PLANE_CFG_ONE_PLANE;
289
290 if (vpe_is_dual_plane_format(
291 vpe_priv->stream_ctx[cmd_info->inputs[1].stream_idx].stream.surface_info.format))
292 header->nps1 = VPE_PLANE_CFG_TWO_PLANES;
293 else
294 header->nps1 = VPE_PLANE_CFG_ONE_PLANE;
295 } else {
296 header->nps0 = 0;
297 header->nps1 = 0;
298 header->npd0 = 0;
299 return;
300 }
301
302 if (vpe_is_dual_plane_format(vpe_priv->output_ctx.surface.format))
303 header->npd0 = 1;
304 else
305 header->npd0 = 0;
306 }
307
vpe_get_element_size(enum vpe_surface_pixel_format format,int plane_idx)308 static enum VPE_PLANE_CFG_ELEMENT_SIZE vpe_get_element_size(
309 enum vpe_surface_pixel_format format, int plane_idx)
310 {
311 switch (format) {
312 // nv12/21
313 case VPE_SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr:
314 case VPE_SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb:
315 if (plane_idx == 0)
316 return VPE_PLANE_CFG_ELEMENT_SIZE_8BPE;
317 else
318 return VPE_PLANE_CFG_ELEMENT_SIZE_16BPE;
319 // P010
320 case VPE_SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr:
321 case VPE_SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb:
322 if (plane_idx == 0)
323 return VPE_PLANE_CFG_ELEMENT_SIZE_16BPE;
324 else
325 return VPE_PLANE_CFG_ELEMENT_SIZE_32BPE;
326 // 64bpp
327 case VPE_SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616:
328 case VPE_SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F:
329 case VPE_SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F:
330 case VPE_SURFACE_PIXEL_FORMAT_GRPH_RGBA16161616F:
331 case VPE_SURFACE_PIXEL_FORMAT_GRPH_BGRA16161616F:
332 return VPE_PLANE_CFG_ELEMENT_SIZE_64BPE;
333 default:
334 break;
335 }
336 return VPE_PLANE_CFG_ELEMENT_SIZE_32BPE;
337 }
338