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 #include <math.h>
25 #include "vpe_types.h"
26 #include "vpe_priv.h"
27 #include "vpe_version.h"
28 #include "common.h"
29
30 #ifdef VPE_BUILD_1_0
31 #include "vpe10_resource.h"
32 #endif
33
34 #ifdef VPE_BUILD_1_1
35 #include "vpe11_resource.h"
36 #endif
37
38 static const struct vpe_debug_options debug_defaults = {
39 .flags = {0},
40 .cm_in_bypass = 0,
41 .vpcnvc_bypass = 0,
42 .mpc_bypass = 0,
43 .identity_3dlut = 0,
44 .sce_3dlut = 0,
45 .disable_reuse_bit = 0,
46 .bg_bit_depth = 0,
47 .bypass_gamcor = 0,
48 .bypass_ogam = 0,
49 .bypass_dpp_gamut_remap = 0,
50 .bypass_post_csc = 0,
51 .bg_color_fill_only = 0,
52 .assert_when_not_support = 0,
53 .enable_mem_low_power =
54 {
55 .bits =
56 {
57 .cm = false,
58 .dscl = false,
59 .mpc = false,
60 },
61 },
62 .expansion_mode = 1,
63 .clamping_setting = 1,
64 .clamping_params =
65 {
66 .r_clamp_component_lower = 0x1000,
67 .g_clamp_component_lower = 0x1000,
68 .b_clamp_component_lower = 0x1000,
69 .r_clamp_component_upper = 0xEB00,
70 .g_clamp_component_upper = 0xEB00,
71 .b_clamp_component_upper = 0xEB00,
72 .clamping_range = 4,
73 },
74 .bypass_per_pixel_alpha = 0,
75 .opp_pipe_crc_ctrl = 0,
76 .dpp_crc_ctrl = 0,
77 .mpc_crc_ctrl = 0,
78 .visual_confirm_params = {{{0}}},
79 .skip_optimal_tap_check = 0,
80 .disable_3dlut_cache = 0,
81 .bypass_blndgam = 0
82 };
83
vpe_resource_parse_ip_version(uint8_t major,uint8_t minor,uint8_t rev_id)84 enum vpe_ip_level vpe_resource_parse_ip_version(
85 uint8_t major, uint8_t minor, uint8_t rev_id)
86 {
87 enum vpe_ip_level ip_level = VPE_IP_LEVEL_UNKNOWN;
88 switch (VPE_VERSION(major, minor, rev_id)) {
89 #ifdef VPE_BUILD_1_0
90 case VPE_VERSION(6, 1, 0):
91 case VPE_VERSION(6, 1, 3):
92 ip_level = VPE_IP_LEVEL_1_0;
93 break;
94 #endif
95 #ifdef VPE_BUILD_1_1
96 case VPE_VERSION(6, 1, 1):
97 case VPE_VERSION(6, 1, 2):
98 ip_level = VPE_IP_LEVEL_1_1;
99 break;
100 #endif
101 default:
102 ip_level = VPE_IP_LEVEL_UNKNOWN;
103 break;
104 }
105 return ip_level;
106 }
107
vpe_construct_resource(struct vpe_priv * vpe_priv,enum vpe_ip_level level,struct resource * res)108 enum vpe_status vpe_construct_resource(
109 struct vpe_priv *vpe_priv, enum vpe_ip_level level, struct resource *res)
110 {
111 enum vpe_status status = VPE_STATUS_OK;
112 switch (level) {
113 #ifdef VPE_BUILD_1_0
114 case VPE_IP_LEVEL_1_0:
115 status = vpe10_construct_resource(vpe_priv, res);
116 break;
117 #endif
118 #ifdef VPE_BUILD_1_1
119 case VPE_IP_LEVEL_1_1:
120 status = vpe11_construct_resource(vpe_priv, res);
121 break;
122 #endif
123 default:
124 status = VPE_STATUS_NOT_SUPPORTED;
125 vpe_log("invalid ip level: %d", (int)level);
126 break;
127 }
128
129 vpe_priv->init.debug = debug_defaults;
130 vpe_priv->expansion_mode = vpe_priv->init.debug.expansion_mode;
131 if (res)
132 res->vpe_priv = vpe_priv;
133
134 return status;
135 }
136
vpe_destroy_resource(struct vpe_priv * vpe_priv,struct resource * res)137 void vpe_destroy_resource(struct vpe_priv *vpe_priv, struct resource *res)
138 {
139 switch (vpe_priv->pub.level) {
140 #ifdef VPE_BUILD_1_0
141 case VPE_IP_LEVEL_1_0:
142 vpe10_destroy_resource(vpe_priv, res);
143 break;
144 #endif
145 #ifdef VPE_BUILD_1_1
146 case VPE_IP_LEVEL_1_1:
147 vpe11_destroy_resource(vpe_priv, res);
148 break;
149 #endif
150 default:
151 break;
152 }
153 }
154
vpe_alloc_segment_ctx(struct vpe_priv * vpe_priv,uint16_t num_segments)155 struct segment_ctx *vpe_alloc_segment_ctx(struct vpe_priv *vpe_priv, uint16_t num_segments)
156 {
157 struct segment_ctx *segment_ctx_base;
158
159 segment_ctx_base = (struct segment_ctx *)vpe_zalloc(sizeof(struct segment_ctx) * num_segments);
160
161 if (!segment_ctx_base)
162 return NULL;
163
164 return segment_ctx_base;
165 }
166
vpe_alloc_stream_ctx(struct vpe_priv * vpe_priv,uint32_t num_streams)167 struct stream_ctx *vpe_alloc_stream_ctx(struct vpe_priv *vpe_priv, uint32_t num_streams)
168 {
169 struct stream_ctx *ctx_base, *ctx;
170 uint32_t i;
171
172 ctx_base = (struct stream_ctx *)vpe_zalloc(sizeof(struct stream_ctx) * num_streams);
173 if (!ctx_base)
174 return NULL;
175
176 for (i = 0; i < num_streams; i++) {
177 ctx = &ctx_base[i];
178 ctx->cs = COLOR_SPACE_UNKNOWN;
179 ctx->tf = TRANSFER_FUNC_UNKNOWN;
180 ctx->vpe_priv = vpe_priv;
181 vpe_color_set_adjustments_to_default(&ctx->color_adjustments);
182 ctx->tf_scaling_factor = vpe_fixpt_one;
183 ctx->stream.flags.geometric_scaling = 0;
184 ctx->stream.tm_params.UID = 0;
185 ctx->uid_3dlut = 0;
186 }
187
188 return ctx_base;
189 }
190
vpe_free_stream_ctx(struct vpe_priv * vpe_priv)191 void vpe_free_stream_ctx(struct vpe_priv *vpe_priv)
192 {
193 uint16_t i;
194 struct stream_ctx *ctx;
195
196 if (!vpe_priv->stream_ctx || !vpe_priv->num_streams)
197 return;
198
199 for (i = 0; i < vpe_priv->num_streams; i++) {
200 ctx = &vpe_priv->stream_ctx[i];
201 if (ctx->input_tf) {
202 vpe_free(ctx->input_tf);
203 ctx->input_tf = NULL;
204 }
205
206 if (ctx->bias_scale) {
207 vpe_free(ctx->bias_scale);
208 ctx->bias_scale = NULL;
209 }
210
211 if (ctx->input_cs) {
212 vpe_free(ctx->input_cs);
213 ctx->input_cs = NULL;
214 }
215
216 if (ctx->gamut_remap) {
217 vpe_free(ctx->gamut_remap);
218 ctx->gamut_remap = NULL;
219 }
220
221 if (ctx->in_shaper_func) {
222 vpe_free(ctx->in_shaper_func);
223 ctx->in_shaper_func = NULL;
224 }
225
226 if (ctx->blend_tf) {
227 vpe_free(ctx->blend_tf);
228 ctx->blend_tf = NULL;
229 }
230
231 if (ctx->lut3d_func) {
232 vpe_free(ctx->lut3d_func);
233 ctx->lut3d_func = NULL;
234 }
235
236 if (ctx->lut3d_cache) {
237 vpe_free(ctx->lut3d_cache);
238 ctx->lut3d_cache = NULL;
239 }
240
241 if (ctx->segment_ctx) {
242 vpe_free(ctx->segment_ctx);
243 ctx->segment_ctx = NULL;
244 }
245 }
246 vpe_free(vpe_priv->stream_ctx);
247 vpe_priv->stream_ctx = NULL;
248 vpe_priv->num_streams = 0;
249 vpe_priv->num_virtual_streams = 0;
250 }
251
vpe_free_output_ctx(struct vpe_priv * vpe_priv)252 void vpe_free_output_ctx(struct vpe_priv *vpe_priv)
253 {
254 if (vpe_priv->output_ctx.gamut_remap)
255 vpe_free(vpe_priv->output_ctx.gamut_remap);
256
257 if (vpe_priv->output_ctx.output_tf)
258 vpe_free(vpe_priv->output_ctx.output_tf);
259 }
260
vpe_pipe_reset(struct vpe_priv * vpe_priv)261 void vpe_pipe_reset(struct vpe_priv *vpe_priv)
262 {
263 int i;
264 struct pipe_ctx *pipe_ctx;
265
266 for (i = 0; i < vpe_priv->num_pipe; i++) {
267 pipe_ctx = &vpe_priv->pipe_ctx[i];
268 pipe_ctx->is_top_pipe = true;
269 pipe_ctx->owner = PIPE_CTX_NO_OWNER;
270 pipe_ctx->top_pipe_idx = 0xff;
271 }
272 }
273
vpe_pipe_reclaim(struct vpe_priv * vpe_priv,struct vpe_cmd_info * cmd_info)274 void vpe_pipe_reclaim(struct vpe_priv *vpe_priv, struct vpe_cmd_info *cmd_info)
275 {
276 int i, j;
277 struct pipe_ctx *pipe_ctx;
278
279 for (i = 0; i < vpe_priv->num_pipe; i++) {
280 pipe_ctx = &vpe_priv->pipe_ctx[i];
281 if (pipe_ctx->owner != PIPE_CTX_NO_OWNER) {
282 for (j = 0; j < cmd_info->num_inputs; j++)
283 if (pipe_ctx->owner == cmd_info->inputs[j].stream_idx)
284 break;
285
286 if (j == cmd_info->num_inputs) {
287 // that stream no longer exists
288 pipe_ctx->is_top_pipe = true;
289 pipe_ctx->owner = PIPE_CTX_NO_OWNER;
290 pipe_ctx->top_pipe_idx = 0xff;
291 }
292 }
293 }
294 }
295
vpe_pipe_find_owner(struct vpe_priv * vpe_priv,uint32_t stream_idx,bool * reuse)296 struct pipe_ctx *vpe_pipe_find_owner(struct vpe_priv *vpe_priv, uint32_t stream_idx, bool *reuse)
297 {
298 int i;
299 struct pipe_ctx *pipe_ctx;
300 struct pipe_ctx *free_pipe = NULL;
301
302 for (i = 0; i < vpe_priv->num_pipe; i++) {
303 pipe_ctx = &vpe_priv->pipe_ctx[i];
304
305 if (!free_pipe && (pipe_ctx->owner == PIPE_CTX_NO_OWNER))
306 free_pipe = pipe_ctx;
307 // re-use the same pipe
308 else if (pipe_ctx->owner == stream_idx) {
309 *reuse = true;
310 return pipe_ctx;
311 }
312 }
313
314 if (free_pipe) {
315 free_pipe->owner = stream_idx;
316 }
317 *reuse = false;
318 return free_pipe;
319 }
320
calculate_recout(struct segment_ctx * segment)321 static void calculate_recout(struct segment_ctx *segment)
322 {
323 struct stream_ctx *stream_ctx = segment->stream_ctx;
324 struct scaler_data *data = &segment->scaler_data;
325 struct vpe_rect *dst_rect;
326 int32_t split_count, split_idx;
327
328 dst_rect = &stream_ctx->stream.scaling_info.dst_rect;
329
330 split_count = stream_ctx->num_segments - 1;
331 split_idx = segment->segment_idx;
332
333 // src & dst rect has been clipped earlier
334 data->recout.x = 0;
335 data->recout.y = 0;
336 data->recout.width = dst_rect->width;
337 data->recout.height = dst_rect->height;
338
339 if (split_count) {
340 /* extra pixels in the division remainder need to go to pipes after
341 * the extra pixel index minus one(epimo) defined here as:
342 */
343 int32_t epimo = split_count - (int32_t)data->recout.width % (split_count + 1);
344
345 data->recout.x += ((int32_t)data->recout.width / (split_count + 1)) * split_idx;
346 if (split_idx > epimo)
347 data->recout.x += split_idx - epimo - 1;
348
349 data->recout.width =
350 data->recout.width / (uint32_t)(split_count + 1) + (split_idx > epimo ? 1 : 0);
351 }
352 }
353
calculate_scaling_ratios(struct scaler_data * scl_data,struct vpe_rect * src_rect,struct vpe_rect * dst_rect,enum vpe_surface_pixel_format format)354 void calculate_scaling_ratios(struct scaler_data *scl_data, struct vpe_rect *src_rect,
355 struct vpe_rect *dst_rect, enum vpe_surface_pixel_format format)
356 {
357 // no rotation support
358
359 scl_data->ratios.horz = vpe_fixpt_from_fraction(src_rect->width, dst_rect->width);
360 scl_data->ratios.vert = vpe_fixpt_from_fraction(src_rect->height, dst_rect->height);
361 scl_data->ratios.horz_c = scl_data->ratios.horz;
362 scl_data->ratios.vert_c = scl_data->ratios.vert;
363
364 if (vpe_is_yuv420(format)) {
365 scl_data->ratios.horz_c.value /= 2;
366 scl_data->ratios.vert_c.value /= 2;
367 }
368
369 scl_data->ratios.horz = vpe_fixpt_truncate(scl_data->ratios.horz, 19);
370 scl_data->ratios.vert = vpe_fixpt_truncate(scl_data->ratios.vert, 19);
371 scl_data->ratios.horz_c = vpe_fixpt_truncate(scl_data->ratios.horz_c, 19);
372 scl_data->ratios.vert_c = vpe_fixpt_truncate(scl_data->ratios.vert_c, 19);
373 }
374
375 /*
376 * This is a preliminary vp size calculation to allow us to check taps support.
377 * The result is completely overridden afterwards.
378 */
calculate_viewport_size(struct segment_ctx * segment_ctx)379 static void calculate_viewport_size(struct segment_ctx *segment_ctx)
380 {
381 struct scaler_data *data = &segment_ctx->scaler_data;
382
383 data->viewport.width =
384 (uint32_t)vpe_fixpt_ceil(vpe_fixpt_mul_int(data->ratios.horz, (int)data->recout.width));
385 data->viewport.height =
386 (uint32_t)vpe_fixpt_ceil(vpe_fixpt_mul_int(data->ratios.vert, (int)data->recout.height));
387 data->viewport_c.width =
388 (uint32_t)vpe_fixpt_ceil(vpe_fixpt_mul_int(data->ratios.horz_c, (int)data->recout.width));
389 data->viewport_c.height =
390 (uint32_t)vpe_fixpt_ceil(vpe_fixpt_mul_int(data->ratios.vert_c, (int)data->recout.height));
391 }
392
393 /*
394 * We completely calculate vp offset, size and inits here based entirely on scaling
395 * ratios and recout for pixel perfect pipe combine.
396 */
calculate_init_and_vp(bool flip_scan_dir,int32_t recout_offset,uint32_t recout_size,uint32_t src_size,uint32_t taps,struct fixed31_32 ratio,struct fixed31_32 init_adj,struct fixed31_32 * init,int32_t * vp_offset,uint32_t * vp_size)397 static void calculate_init_and_vp(bool flip_scan_dir, int32_t recout_offset, uint32_t recout_size,
398 uint32_t src_size, uint32_t taps, struct fixed31_32 ratio, struct fixed31_32 init_adj,
399 struct fixed31_32 *init, int32_t *vp_offset, uint32_t *vp_size)
400 {
401
402 struct fixed31_32 src_offset, temp;
403 int32_t int_part;
404
405 /*
406 * First of the taps starts sampling pixel number <init_int_part> corresponding to recout
407 * pixel 1. Next recout pixel samples int part of <init + scaling ratio> and so on.
408 * All following calculations are based on this logic.
409 */
410 src_offset = vpe_fixpt_mul_int(ratio, recout_offset);
411 *vp_offset = vpe_fixpt_floor(src_offset);
412
413 // calculate the phase
414 init->value = src_offset.value & 0xffffffff; // for phase accumulation
415 *init = vpe_fixpt_add(*init, init_adj);
416 int_part = vpe_fixpt_floor(vpe_fixpt_from_fraction(taps, 2)) +
417 1; // middle point of the sampling window
418 *init = vpe_fixpt_add_int(*init, int_part);
419 *init = vpe_fixpt_truncate(*init, 19);
420 /*
421 * If there are more pixels on the left hand side (top for vertical scaling) of the
422 * sampling point which can be covered by the taps, init value needs go get increased
423 * to be able to buffer the pixels as much as taps.
424 */
425 if (int_part < (int32_t)taps) {
426 int32_t left = (int32_t)taps - int_part;
427 if (left > *vp_offset)
428 left = *vp_offset;
429 *vp_offset -= left;
430 *init = vpe_fixpt_add_int(*init, left);
431 }
432 /*
433 * If taps are sampling outside of viewport at end of recout and there are more pixels
434 * available in the surface we should increase the viewport size, regardless set vp to
435 * only what is used.
436 */
437 temp = vpe_fixpt_add(*init, vpe_fixpt_mul_int(ratio, (int)(recout_size - 1)));
438 *vp_size = (uint32_t)vpe_fixpt_floor(temp);
439 if ((uint32_t)((int32_t)*vp_size + *vp_offset) > src_size)
440 *vp_size = (uint32_t)((int32_t)src_size - *vp_offset);
441 /* We did all the math assuming we are scanning same direction as display does,
442 * however mirror/rotation changes how vp scans vs how it is offset. If scan direction
443 * is flipped we simply need to calculate offset from the other side of plane.
444 * Note that outside of viewport all scaling hardware works in recout space.
445 */
446 if (flip_scan_dir)
447 *vp_offset = (int32_t)src_size - *vp_offset - (int32_t)*vp_size;
448 }
449
get_vp_scan_direction(enum vpe_rotation_angle rotation,bool horizontal_mirror,bool * orthogonal_rotation,bool * flip_vert_scan_dir,bool * flip_horz_scan_dir)450 static inline void get_vp_scan_direction(enum vpe_rotation_angle rotation, bool horizontal_mirror,
451 bool *orthogonal_rotation, bool *flip_vert_scan_dir, bool *flip_horz_scan_dir)
452 {
453 *orthogonal_rotation = false;
454 *flip_vert_scan_dir = false;
455 *flip_horz_scan_dir = false;
456 if (rotation == VPE_ROTATION_ANGLE_180) {
457 *flip_vert_scan_dir = true;
458 *flip_horz_scan_dir = true;
459 } else if (rotation == VPE_ROTATION_ANGLE_90) {
460 *orthogonal_rotation = true;
461 *flip_horz_scan_dir = true;
462 } else if (rotation == VPE_ROTATION_ANGLE_270) {
463 *orthogonal_rotation = true;
464 *flip_vert_scan_dir = true;
465 }
466
467 if (horizontal_mirror)
468 *flip_horz_scan_dir = !*flip_horz_scan_dir;
469 }
470
calculate_inits_and_viewports(struct segment_ctx * segment_ctx)471 static enum vpe_status calculate_inits_and_viewports(struct segment_ctx *segment_ctx)
472 {
473 struct stream_ctx *stream_ctx = segment_ctx->stream_ctx;
474 struct vpe_surface_info *surface_info = &stream_ctx->stream.surface_info;
475 struct vpe_rect src_rect = stream_ctx->stream.scaling_info.src_rect;
476 struct vpe_rect *dst_rect = &stream_ctx->stream.scaling_info.dst_rect;
477 struct scaler_data *data = &segment_ctx->scaler_data;
478 uint32_t vpc_h_div = vpe_is_yuv420(data->format) ? 2 : 1;
479 uint32_t vpc_v_div = vpe_is_yuv420(data->format) ? 2 : 1;
480 bool orthogonal_rotation, flip_vert_scan_dir, flip_horz_scan_dir;
481 struct fixed31_32 init_adj_h = vpe_fixpt_zero;
482 struct fixed31_32 init_adj_v = vpe_fixpt_zero;
483
484 get_vp_scan_direction(stream_ctx->stream.rotation, stream_ctx->stream.horizontal_mirror,
485 &orthogonal_rotation, &flip_vert_scan_dir, &flip_horz_scan_dir);
486
487 if (orthogonal_rotation) {
488 swap(src_rect.width, src_rect.height);
489 swap(flip_vert_scan_dir, flip_horz_scan_dir);
490 }
491
492 if (flip_horz_scan_dir) {
493 if (stream_ctx->flip_horizonal_output)
494 // flip at the output instead
495 flip_horz_scan_dir = false;
496 }
497
498 if (vpe_is_yuv420(data->format)) {
499 int sign = -1; // this gives the direction of the cositing (negative will move left, right
500 // otherwise)
501 switch (surface_info->cs.cositing) {
502
503 case VPE_CHROMA_COSITING_LEFT:
504 init_adj_h = vpe_fixpt_zero;
505 init_adj_v = vpe_fixpt_from_fraction(sign, 4);
506 break;
507 case VPE_CHROMA_COSITING_NONE:
508 init_adj_h = vpe_fixpt_from_fraction(sign, 4);
509 init_adj_v = vpe_fixpt_from_fraction(sign, 4);
510 break;
511 case VPE_CHROMA_COSITING_TOPLEFT:
512 default:
513 init_adj_h = vpe_fixpt_zero;
514 init_adj_v = vpe_fixpt_zero;
515 break;
516 }
517 }
518
519 calculate_init_and_vp(flip_horz_scan_dir, data->recout.x, data->recout.width, src_rect.width,
520 data->taps.h_taps, data->ratios.horz, vpe_fixpt_zero, &data->inits.h, &data->viewport.x,
521 &data->viewport.width);
522 calculate_init_and_vp(flip_horz_scan_dir, data->recout.x, data->recout.width,
523 src_rect.width / vpc_h_div, data->taps.h_taps_c, data->ratios.horz_c, init_adj_h,
524 &data->inits.h_c, &data->viewport_c.x, &data->viewport_c.width);
525 calculate_init_and_vp(flip_vert_scan_dir, data->recout.y, data->recout.height, src_rect.height,
526 data->taps.v_taps, data->ratios.vert, vpe_fixpt_zero, &data->inits.v, &data->viewport.y,
527 &data->viewport.height);
528 calculate_init_and_vp(flip_vert_scan_dir, data->recout.y, data->recout.height,
529 src_rect.height / vpc_v_div, data->taps.v_taps_c, data->ratios.vert_c, init_adj_v,
530 &data->inits.v_c, &data->viewport_c.y, &data->viewport_c.height);
531
532 // convert to absolute address
533 data->viewport.x += src_rect.x;
534 data->viewport.y += src_rect.y;
535 data->viewport_c.x += src_rect.x / (int32_t)vpc_h_div;
536 data->viewport_c.y += src_rect.y / (int32_t)vpc_v_div;
537
538 return VPE_STATUS_OK;
539 }
540
vpe_get_num_segments(struct vpe_priv * vpe_priv,const struct vpe_rect * src,const struct vpe_rect * dst,const uint32_t max_seg_width)541 uint16_t vpe_get_num_segments(struct vpe_priv *vpe_priv, const struct vpe_rect *src,
542 const struct vpe_rect *dst, const uint32_t max_seg_width)
543 {
544 int num_seg_src = (int)(ceil((double)src->width / max_seg_width));
545 int num_seg_dst = (int)(ceil((double)dst->width / max_seg_width));
546 return (uint16_t)(max(max(num_seg_src, num_seg_dst), 1));
547 }
548
vpe_clip_stream(struct vpe_rect * src_rect,struct vpe_rect * dst_rect,const struct vpe_rect * target_rect)549 void vpe_clip_stream(
550 struct vpe_rect *src_rect, struct vpe_rect *dst_rect, const struct vpe_rect *target_rect)
551 {
552 struct fixed31_32 scaling_ratio_h;
553 struct fixed31_32 scaling_ratio_v;
554
555 struct vpe_rect clipped_dst_rect, clipped_src_rect;
556 uint32_t clipped_pixels;
557
558 clipped_dst_rect = *dst_rect;
559 clipped_src_rect = *src_rect;
560
561 scaling_ratio_h = vpe_fixpt_from_fraction(src_rect->width, dst_rect->width);
562 scaling_ratio_v = vpe_fixpt_from_fraction(src_rect->height, dst_rect->height);
563
564 if (dst_rect->x < target_rect->x) {
565 clipped_pixels = (uint32_t)(target_rect->x - dst_rect->x);
566 clipped_dst_rect.x = target_rect->x;
567 clipped_dst_rect.width -= clipped_pixels;
568 clipped_pixels = (uint32_t)vpe_fixpt_round(
569 vpe_fixpt_mul_int(scaling_ratio_h, (int)(target_rect->x - dst_rect->x)));
570 clipped_src_rect.x += (int32_t)clipped_pixels;
571 clipped_src_rect.width -= clipped_pixels;
572 }
573 if (dst_rect->y < target_rect->y) {
574 clipped_pixels = (uint32_t)(target_rect->y - dst_rect->y);
575 clipped_dst_rect.y = target_rect->y;
576 clipped_dst_rect.height -= clipped_pixels;
577 clipped_pixels = (uint32_t)vpe_fixpt_round(
578 vpe_fixpt_mul_int(scaling_ratio_v, (int)(target_rect->y - dst_rect->y)));
579 clipped_src_rect.y += (int32_t)clipped_pixels;
580 clipped_src_rect.height -= clipped_pixels;
581 }
582 if (dst_rect->x + (int32_t)dst_rect->width > target_rect->x + (int32_t)target_rect->width) {
583 clipped_dst_rect.width =
584 (uint32_t)(target_rect->x + (int32_t)target_rect->width - clipped_dst_rect.x);
585 clipped_src_rect.width = (uint32_t)vpe_fixpt_round(
586 vpe_fixpt_mul_int(scaling_ratio_h, (int)clipped_dst_rect.width));
587 }
588 if (dst_rect->y + (int32_t)dst_rect->height > target_rect->y + (int32_t)target_rect->height) {
589 clipped_dst_rect.height =
590 (uint32_t)(target_rect->y + (int32_t)target_rect->height - clipped_dst_rect.y);
591 clipped_src_rect.height = (uint32_t)vpe_fixpt_round(
592 vpe_fixpt_mul_int(scaling_ratio_v, (int)clipped_dst_rect.height));
593 }
594
595 *src_rect = clipped_src_rect;
596 *dst_rect = clipped_dst_rect;
597 }
598
vpe_resource_build_scaling_params(struct segment_ctx * segment_ctx)599 enum vpe_status vpe_resource_build_scaling_params(struct segment_ctx *segment_ctx)
600 {
601 struct stream_ctx *stream_ctx = segment_ctx->stream_ctx;
602 struct scaler_data *scl_data = &segment_ctx->scaler_data;
603 struct dpp *dpp = stream_ctx->vpe_priv->resource.dpp[0];
604
605 scl_data->format = stream_ctx->stream.surface_info.format;
606 scl_data->lb_params.alpha_en = stream_ctx->per_pixel_alpha;
607
608 // h/v active will be set later
609
610 /* recout.x is temporary for viewport calculation,
611 * will be finalized in calculate_dst_viewport_and_active()
612 */
613
614 calculate_recout(segment_ctx);
615 calculate_viewport_size(segment_ctx);
616
617 if (scl_data->viewport.height < 1 || scl_data->viewport.width < 1)
618 return VPE_STATUS_VIEWPORT_SIZE_NOT_SUPPORTED;
619
620 if (!dpp->funcs->validate_number_of_taps(dpp, scl_data)) {
621 return VPE_STATUS_SCALING_RATIO_NOT_SUPPORTED;
622 }
623
624 calculate_inits_and_viewports(segment_ctx);
625
626 if (scl_data->viewport.height < VPE_MIN_VIEWPORT_SIZE ||
627 scl_data->viewport.width < VPE_MIN_VIEWPORT_SIZE)
628 return VPE_STATUS_VIEWPORT_SIZE_NOT_SUPPORTED;
629
630 return VPE_STATUS_OK;
631 }
632
vpe_handle_output_h_mirror(struct vpe_priv * vpe_priv)633 void vpe_handle_output_h_mirror(struct vpe_priv *vpe_priv)
634 {
635 uint16_t stream_idx;
636 int seg_idx;
637 struct stream_ctx *stream_ctx;
638
639 // swap the stream output location
640 for (stream_idx = 0; stream_idx < vpe_priv->num_streams; stream_idx++) {
641 stream_ctx = &vpe_priv->stream_ctx[stream_idx];
642 if (stream_ctx->flip_horizonal_output) {
643 struct segment_ctx *first_seg, *last_seg;
644
645 // swap the segment output order, init the last segment first
646 first_seg = &stream_ctx->segment_ctx[0];
647 last_seg = &stream_ctx->segment_ctx[stream_ctx->num_segments - 1];
648
649 // last segment becomes first
650 last_seg->scaler_data.dst_viewport.x = first_seg->scaler_data.dst_viewport.x;
651
652 for (seg_idx = (int)(stream_ctx->num_segments - 2); seg_idx >= 0; seg_idx--) {
653 struct segment_ctx *prev_seg, *curr_seg;
654
655 // set the x in reverse order
656 prev_seg = &stream_ctx->segment_ctx[seg_idx + 1];
657 curr_seg = &stream_ctx->segment_ctx[seg_idx];
658
659 curr_seg->scaler_data.dst_viewport.x =
660 prev_seg->scaler_data.dst_viewport.x +
661 (int32_t)prev_seg->scaler_data.dst_viewport.width;
662
663 curr_seg->scaler_data.dst_viewport_c.x =
664 prev_seg->scaler_data.dst_viewport_c.x +
665 (int32_t)prev_seg->scaler_data.dst_viewport_c.width;
666 }
667 }
668 }
669 }
670
vpe_resource_build_bit_depth_reduction_params(struct opp * opp,struct bit_depth_reduction_params * fmt_bit_depth)671 void vpe_resource_build_bit_depth_reduction_params(
672 struct opp *opp, struct bit_depth_reduction_params *fmt_bit_depth)
673 {
674 struct vpe_priv *vpe_priv = opp->vpe_priv;
675 struct vpe_surface_info *dst_surface = &vpe_priv->output_ctx.surface;
676 enum color_depth display_color_depth;
677 memset(fmt_bit_depth, 0, sizeof(*fmt_bit_depth));
678
679 display_color_depth = vpe_get_color_depth(dst_surface->format);
680
681 switch (display_color_depth) {
682 case COLOR_DEPTH_888:
683 case COLOR_DEPTH_101010:
684 fmt_bit_depth->flags.TRUNCATE_ENABLED = 1;
685 fmt_bit_depth->flags.TRUNCATE_DEPTH = (display_color_depth == COLOR_DEPTH_888) ? 1 : 2;
686 fmt_bit_depth->flags.TRUNCATE_MODE = 1;
687 break;
688 default:
689 break;
690 }
691 }
692
vpe_frontend_config_callback(void * ctx,uint64_t cfg_base_gpu,uint64_t cfg_base_cpu,uint64_t size)693 void vpe_frontend_config_callback(
694 void *ctx, uint64_t cfg_base_gpu, uint64_t cfg_base_cpu, uint64_t size)
695 {
696 struct config_frontend_cb_ctx *cb_ctx = (struct config_frontend_cb_ctx*)ctx;
697 struct vpe_priv *vpe_priv = cb_ctx->vpe_priv;
698 struct stream_ctx *stream_ctx = &vpe_priv->stream_ctx[cb_ctx->stream_idx];
699 enum vpe_cmd_type cmd_type;
700
701 if (cb_ctx->stream_sharing) {
702 VPE_ASSERT(stream_ctx->num_configs <
703 (int)(sizeof(stream_ctx->configs) / sizeof(struct config_record)));
704
705 stream_ctx->configs[stream_ctx->num_configs].config_base_addr = cfg_base_gpu;
706 stream_ctx->configs[stream_ctx->num_configs].config_size = size;
707 stream_ctx->num_configs++;
708 } else if (cb_ctx->stream_op_sharing) {
709 cmd_type = cb_ctx->cmd_type;
710
711 VPE_ASSERT(
712 stream_ctx->num_stream_op_configs[cmd_type] <
713 (int)(sizeof(stream_ctx->stream_op_configs[cmd_type]) / sizeof(struct config_record)));
714
715 stream_ctx->stream_op_configs[cmd_type][stream_ctx->num_stream_op_configs[cmd_type]]
716 .config_base_addr = cfg_base_gpu;
717 stream_ctx->stream_op_configs[cmd_type][stream_ctx->num_stream_op_configs[cmd_type]]
718 .config_size = size;
719 stream_ctx->num_stream_op_configs[cmd_type]++;
720 }
721
722 vpe_priv->vpe_desc_writer.add_config_desc(
723 &vpe_priv->vpe_desc_writer, cfg_base_gpu, false, (uint8_t)vpe_priv->config_writer.buf->tmz);
724 }
725
vpe_backend_config_callback(void * ctx,uint64_t cfg_base_gpu,uint64_t cfg_base_cpu,uint64_t size)726 void vpe_backend_config_callback(
727 void *ctx, uint64_t cfg_base_gpu, uint64_t cfg_base_cpu, uint64_t size)
728 {
729 struct config_backend_cb_ctx *cb_ctx = (struct config_backend_cb_ctx*)ctx;
730 struct vpe_priv *vpe_priv = cb_ctx->vpe_priv;
731 struct output_ctx *output_ctx = &vpe_priv->output_ctx;
732
733 if (cb_ctx->share) {
734 VPE_ASSERT(
735 output_ctx->num_configs < (sizeof(output_ctx->configs) / sizeof(struct config_record)));
736
737 output_ctx->configs[output_ctx->num_configs].config_base_addr = cfg_base_gpu;
738 output_ctx->configs[output_ctx->num_configs].config_size = size;
739 output_ctx->num_configs++;
740 }
741
742 vpe_priv->vpe_desc_writer.add_config_desc(
743 &vpe_priv->vpe_desc_writer, cfg_base_gpu, false, (uint8_t)vpe_priv->config_writer.buf->tmz);
744 }
745