Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: MIT
2//
3// Copyright 2024 Advanced Micro Devices, Inc.
4
5#include "dc_spl.h"
6#include "dc_spl_scl_filters.h"
7#include "dc_spl_isharp_filters.h"
8
9#define IDENTITY_RATIO(ratio) (dc_fixpt_u2d19(ratio) == (1 << 19))
10#define MIN_VIEWPORT_SIZE 12
11
12static struct spl_rect intersect_rec(const struct spl_rect *r0, const struct spl_rect *r1)
13{
14 struct spl_rect rec;
15 int r0_x_end = r0->x + r0->width;
16 int r1_x_end = r1->x + r1->width;
17 int r0_y_end = r0->y + r0->height;
18 int r1_y_end = r1->y + r1->height;
19
20 rec.x = r0->x > r1->x ? r0->x : r1->x;
21 rec.width = r0_x_end > r1_x_end ? r1_x_end - rec.x : r0_x_end - rec.x;
22 rec.y = r0->y > r1->y ? r0->y : r1->y;
23 rec.height = r0_y_end > r1_y_end ? r1_y_end - rec.y : r0_y_end - rec.y;
24
25 /* in case that there is no intersection */
26 if (rec.width < 0 || rec.height < 0)
27 memset(&rec, 0, sizeof(rec));
28
29 return rec;
30}
31
32static struct spl_rect shift_rec(const struct spl_rect *rec_in, int x, int y)
33{
34 struct spl_rect rec_out = *rec_in;
35
36 rec_out.x += x;
37 rec_out.y += y;
38
39 return rec_out;
40}
41
42static struct spl_rect calculate_plane_rec_in_timing_active(
43 struct spl_in *spl_in,
44 const struct spl_rect *rec_in)
45{
46 /*
47 * The following diagram shows an example where we map a 1920x1200
48 * desktop to a 2560x1440 timing with a plane rect in the middle
49 * of the screen. To map a plane rect from Stream Source to Timing
50 * Active space, we first multiply stream scaling ratios (i.e 2304/1920
51 * horizontal and 1440/1200 vertical) to the plane's x and y, then
52 * we add stream destination offsets (i.e 128 horizontal, 0 vertical).
53 * This will give us a plane rect's position in Timing Active. However
54 * we have to remove the fractional. The rule is that we find left/right
55 * and top/bottom positions and round the value to the adjacent integer.
56 *
57 * Stream Source Space
58 * ------------
59 * __________________________________________________
60 * |Stream Source (1920 x 1200) ^ |
61 * | y |
62 * | <------- w --------|> |
63 * | __________________V |
64 * |<-- x -->|Plane//////////////| ^ |
65 * | |(pre scale)////////| | |
66 * | |///////////////////| | |
67 * | |///////////////////| h |
68 * | |///////////////////| | |
69 * | |///////////////////| | |
70 * | |///////////////////| V |
71 * | |
72 * | |
73 * |__________________________________________________|
74 *
75 *
76 * Timing Active Space
77 * ---------------------------------
78 *
79 * Timing Active (2560 x 1440)
80 * __________________________________________________
81 * |*****| Stteam Destination (2304 x 1440) |*****|
82 * |*****| |*****|
83 * |<128>| |*****|
84 * |*****| __________________ |*****|
85 * |*****| |Plane/////////////| |*****|
86 * |*****| |(post scale)//////| |*****|
87 * |*****| |//////////////////| |*****|
88 * |*****| |//////////////////| |*****|
89 * |*****| |//////////////////| |*****|
90 * |*****| |//////////////////| |*****|
91 * |*****| |*****|
92 * |*****| |*****|
93 * |*****| |*****|
94 * |*****|______________________________________|*****|
95 *
96 * So the resulting formulas are shown below:
97 *
98 * recout_x = 128 + round(plane_x * 2304 / 1920)
99 * recout_w = 128 + round((plane_x + plane_w) * 2304 / 1920) - recout_x
100 * recout_y = 0 + round(plane_y * 1440 / 1280)
101 * recout_h = 0 + round((plane_y + plane_h) * 1440 / 1200) - recout_y
102 *
103 * NOTE: fixed point division is not error free. To reduce errors
104 * introduced by fixed point division, we divide only after
105 * multiplication is complete.
106 */
107 const struct spl_rect *stream_src = &spl_in->basic_out.src_rect;
108 const struct spl_rect *stream_dst = &spl_in->basic_out.dst_rect;
109 struct spl_rect rec_out = {0};
110 struct fixed31_32 temp;
111
112
113 temp = dc_fixpt_from_fraction(rec_in->x * (long long)stream_dst->width,
114 stream_src->width);
115 rec_out.x = stream_dst->x + dc_fixpt_round(temp);
116
117 temp = dc_fixpt_from_fraction(
118 (rec_in->x + rec_in->width) * (long long)stream_dst->width,
119 stream_src->width);
120 rec_out.width = stream_dst->x + dc_fixpt_round(temp) - rec_out.x;
121
122 temp = dc_fixpt_from_fraction(rec_in->y * (long long)stream_dst->height,
123 stream_src->height);
124 rec_out.y = stream_dst->y + dc_fixpt_round(temp);
125
126 temp = dc_fixpt_from_fraction(
127 (rec_in->y + rec_in->height) * (long long)stream_dst->height,
128 stream_src->height);
129 rec_out.height = stream_dst->y + dc_fixpt_round(temp) - rec_out.y;
130
131 return rec_out;
132}
133
134static struct spl_rect calculate_mpc_slice_in_timing_active(
135 struct spl_in *spl_in,
136 struct spl_rect *plane_clip_rec)
137{
138 int mpc_slice_count = spl_in->basic_in.mpc_combine_h;
139 int mpc_slice_idx = spl_in->basic_in.mpc_combine_v;
140 int epimo = mpc_slice_count - plane_clip_rec->width % mpc_slice_count - 1;
141 struct spl_rect mpc_rec;
142
143 mpc_rec.width = plane_clip_rec->width / mpc_slice_count;
144 mpc_rec.x = plane_clip_rec->x + mpc_rec.width * mpc_slice_idx;
145 mpc_rec.height = plane_clip_rec->height;
146 mpc_rec.y = plane_clip_rec->y;
147 ASSERT(mpc_slice_count == 1 ||
148 spl_in->basic_out.view_format != SPL_VIEW_3D_SIDE_BY_SIDE ||
149 mpc_rec.width % 2 == 0);
150
151 /* extra pixels in the division remainder need to go to pipes after
152 * the extra pixel index minus one(epimo) defined here as:
153 */
154 if (mpc_slice_idx > epimo) {
155 mpc_rec.x += mpc_slice_idx - epimo - 1;
156 mpc_rec.width += 1;
157 }
158
159 if (spl_in->basic_out.view_format == SPL_VIEW_3D_TOP_AND_BOTTOM) {
160 ASSERT(mpc_rec.height % 2 == 0);
161 mpc_rec.height /= 2;
162 }
163 return mpc_rec;
164}
165
166static struct spl_rect calculate_odm_slice_in_timing_active(struct spl_in *spl_in)
167{
168 int odm_slice_count = spl_in->basic_out.odm_combine_factor;
169 int odm_slice_idx = spl_in->odm_slice_index;
170 bool is_last_odm_slice = (odm_slice_idx + 1) == odm_slice_count;
171 int h_active = spl_in->basic_out.output_size.width;
172 int v_active = spl_in->basic_out.output_size.height;
173 int odm_slice_width;
174 struct spl_rect odm_rec;
175
176 if (spl_in->basic_out.odm_combine_factor > 0) {
177 odm_slice_width = h_active / odm_slice_count;
178 /*
179 * deprecated, caller must pass in odm slice rect i.e OPP input
180 * rect in timing active for the new interface.
181 */
182 if (spl_in->basic_out.use_two_pixels_per_container && (odm_slice_width % 2))
183 odm_slice_width++;
184
185 odm_rec.x = odm_slice_width * odm_slice_idx;
186 odm_rec.width = is_last_odm_slice ?
187 /* last slice width is the reminder of h_active */
188 h_active - odm_slice_width * (odm_slice_count - 1) :
189 /* odm slice width is the floor of h_active / count */
190 odm_slice_width;
191 odm_rec.y = 0;
192 odm_rec.height = v_active;
193
194 return odm_rec;
195 }
196
197 return spl_in->basic_out.odm_slice_rect;
198}
199
200static void spl_calculate_recout(struct spl_in *spl_in, struct spl_out *spl_out)
201{
202 /*
203 * A plane clip represents the desired plane size and position in Stream
204 * Source Space. Stream Source is the destination where all planes are
205 * blended (i.e. positioned, scaled and overlaid). It is a canvas where
206 * all planes associated with the current stream are drawn together.
207 * After Stream Source is completed, we will further scale and
208 * reposition the entire canvas of the stream source to Stream
209 * Destination in Timing Active Space. This could be due to display
210 * overscan adjustment where we will need to rescale and reposition all
211 * the planes so they can fit into a TV with overscan or downscale
212 * upscale features such as GPU scaling or VSR.
213 *
214 * This two step blending is a virtual procedure in software. In
215 * hardware there is no such thing as Stream Source. all planes are
216 * blended once in Timing Active Space. Software virtualizes a Stream
217 * Source space to decouple the math complicity so scaling param
218 * calculation focuses on one step at a time.
219 *
220 * In the following two diagrams, user applied 10% overscan adjustment
221 * so the Stream Source needs to be scaled down a little before mapping
222 * to Timing Active Space. As a result the Plane Clip is also scaled
223 * down by the same ratio, Plane Clip position (i.e. x and y) with
224 * respect to Stream Source is also scaled down. To map it in Timing
225 * Active Space additional x and y offsets from Stream Destination are
226 * added to Plane Clip as well.
227 *
228 * Stream Source Space
229 * ------------
230 * __________________________________________________
231 * |Stream Source (3840 x 2160) ^ |
232 * | y |
233 * | | |
234 * | __________________V |
235 * |<-- x -->|Plane Clip/////////| |
236 * | |(pre scale)////////| |
237 * | |///////////////////| |
238 * | |///////////////////| |
239 * | |///////////////////| |
240 * | |///////////////////| |
241 * | |///////////////////| |
242 * | |
243 * | |
244 * |__________________________________________________|
245 *
246 *
247 * Timing Active Space (3840 x 2160)
248 * ---------------------------------
249 *
250 * Timing Active
251 * __________________________________________________
252 * | y_____________________________________________ |
253 * |x |Stream Destination (3456 x 1944) | |
254 * | | | |
255 * | | __________________ | |
256 * | | |Plane Clip////////| | |
257 * | | |(post scale)//////| | |
258 * | | |//////////////////| | |
259 * | | |//////////////////| | |
260 * | | |//////////////////| | |
261 * | | |//////////////////| | |
262 * | | | |
263 * | | | |
264 * | |____________________________________________| |
265 * |__________________________________________________|
266 *
267 *
268 * In Timing Active Space a plane clip could be further sliced into
269 * pieces called MPC slices. Each Pipe Context is responsible for
270 * processing only one MPC slice so the plane processing workload can be
271 * distributed to multiple DPP Pipes. MPC slices could be blended
272 * together to a single ODM slice. Each ODM slice is responsible for
273 * processing a portion of Timing Active divided horizontally so the
274 * output pixel processing workload can be distributed to multiple OPP
275 * pipes. All ODM slices are mapped together in ODM block so all MPC
276 * slices belong to different ODM slices could be pieced together to
277 * form a single image in Timing Active. MPC slices must belong to
278 * single ODM slice. If an MPC slice goes across ODM slice boundary, it
279 * needs to be divided into two MPC slices one for each ODM slice.
280 *
281 * In the following diagram the output pixel processing workload is
282 * divided horizontally into two ODM slices one for each OPP blend tree.
283 * OPP0 blend tree is responsible for processing left half of Timing
284 * Active, while OPP2 blend tree is responsible for processing right
285 * half.
286 *
287 * The plane has two MPC slices. However since the right MPC slice goes
288 * across ODM boundary, two DPP pipes are needed one for each OPP blend
289 * tree. (i.e. DPP1 for OPP0 blend tree and DPP2 for OPP2 blend tree).
290 *
291 * Assuming that we have a Pipe Context associated with OPP0 and DPP1
292 * working on processing the plane in the diagram. We want to know the
293 * width and height of the shaded rectangle and its relative position
294 * with respect to the ODM slice0. This is called the recout of the pipe
295 * context.
296 *
297 * Planes can be at arbitrary size and position and there could be an
298 * arbitrary number of MPC and ODM slices. The algorithm needs to take
299 * all scenarios into account.
300 *
301 * Timing Active Space (3840 x 2160)
302 * ---------------------------------
303 *
304 * Timing Active
305 * __________________________________________________
306 * |OPP0(ODM slice0)^ |OPP2(ODM slice1) |
307 * | y | |
308 * | | <- w -> |
309 * | _____V________|____ |
310 * | |DPP0 ^ |DPP1 |DPP2| |
311 * |<------ x |-----|->|/////| | |
312 * | | | |/////| | |
313 * | | h |/////| | |
314 * | | | |/////| | |
315 * | |_____V__|/////|____| |
316 * | | |
317 * | | |
318 * | | |
319 * |_________________________|________________________|
320 *
321 *
322 */
323 struct spl_rect plane_clip;
324 struct spl_rect mpc_slice_of_plane_clip;
325 struct spl_rect odm_slice;
326 struct spl_rect overlapping_area;
327
328 plane_clip = calculate_plane_rec_in_timing_active(spl_in,
329 &spl_in->basic_in.clip_rect);
330 /* guard plane clip from drawing beyond stream dst here */
331 plane_clip = intersect_rec(&plane_clip,
332 &spl_in->basic_out.dst_rect);
333 mpc_slice_of_plane_clip = calculate_mpc_slice_in_timing_active(
334 spl_in, &plane_clip);
335 odm_slice = calculate_odm_slice_in_timing_active(spl_in);
336 overlapping_area = intersect_rec(&mpc_slice_of_plane_clip, &odm_slice);
337
338 if (overlapping_area.height > 0 &&
339 overlapping_area.width > 0) {
340 /* shift the overlapping area so it is with respect to current
341 * ODM slice's position
342 */
343 spl_out->scl_data.recout = shift_rec(
344 &overlapping_area,
345 -odm_slice.x, -odm_slice.y);
346 spl_out->scl_data.recout.height -=
347 spl_in->debug.visual_confirm_base_offset;
348 spl_out->scl_data.recout.height -=
349 spl_in->debug.visual_confirm_dpp_offset;
350 } else
351 /* if there is no overlap, zero recout */
352 memset(&spl_out->scl_data.recout, 0,
353 sizeof(struct spl_rect));
354}
355/* Calculate scaling ratios */
356static void spl_calculate_scaling_ratios(struct spl_in *spl_in, struct spl_out *spl_out)
357{
358 const int in_w = spl_in->basic_out.src_rect.width;
359 const int in_h = spl_in->basic_out.src_rect.height;
360 const int out_w = spl_in->basic_out.dst_rect.width;
361 const int out_h = spl_in->basic_out.dst_rect.height;
362 struct spl_rect surf_src = spl_in->basic_in.src_rect;
363
364 /*Swap surf_src height and width since scaling ratios are in recout rotation*/
365 if (spl_in->basic_in.rotation == SPL_ROTATION_ANGLE_90 ||
366 spl_in->basic_in.rotation == SPL_ROTATION_ANGLE_270)
367 swap(surf_src.height, surf_src.width);
368
369 spl_out->scl_data.ratios.horz = dc_fixpt_from_fraction(
370 surf_src.width,
371 spl_in->basic_in.dst_rect.width);
372 spl_out->scl_data.ratios.vert = dc_fixpt_from_fraction(
373 surf_src.height,
374 spl_in->basic_in.dst_rect.height);
375
376 if (spl_in->basic_out.view_format == SPL_VIEW_3D_SIDE_BY_SIDE)
377 spl_out->scl_data.ratios.horz.value *= 2;
378 else if (spl_in->basic_out.view_format == SPL_VIEW_3D_TOP_AND_BOTTOM)
379 spl_out->scl_data.ratios.vert.value *= 2;
380
381 spl_out->scl_data.ratios.vert.value = div64_s64(
382 spl_out->scl_data.ratios.vert.value * in_h, out_h);
383 spl_out->scl_data.ratios.horz.value = div64_s64(
384 spl_out->scl_data.ratios.horz.value * in_w, out_w);
385
386 spl_out->scl_data.ratios.horz_c = spl_out->scl_data.ratios.horz;
387 spl_out->scl_data.ratios.vert_c = spl_out->scl_data.ratios.vert;
388
389 if (spl_in->basic_in.format == SPL_PIXEL_FORMAT_420BPP8
390 || spl_in->basic_in.format == SPL_PIXEL_FORMAT_420BPP10) {
391 spl_out->scl_data.ratios.horz_c.value /= 2;
392 spl_out->scl_data.ratios.vert_c.value /= 2;
393 }
394 spl_out->scl_data.ratios.horz = dc_fixpt_truncate(
395 spl_out->scl_data.ratios.horz, 19);
396 spl_out->scl_data.ratios.vert = dc_fixpt_truncate(
397 spl_out->scl_data.ratios.vert, 19);
398 spl_out->scl_data.ratios.horz_c = dc_fixpt_truncate(
399 spl_out->scl_data.ratios.horz_c, 19);
400 spl_out->scl_data.ratios.vert_c = dc_fixpt_truncate(
401 spl_out->scl_data.ratios.vert_c, 19);
402}
403/* Calculate Viewport size */
404static void spl_calculate_viewport_size(struct spl_in *spl_in, struct spl_out *spl_out)
405{
406 spl_out->scl_data.viewport.width = dc_fixpt_ceil(dc_fixpt_mul_int(spl_out->scl_data.ratios.horz,
407 spl_out->scl_data.recout.width));
408 spl_out->scl_data.viewport.height = dc_fixpt_ceil(dc_fixpt_mul_int(spl_out->scl_data.ratios.vert,
409 spl_out->scl_data.recout.height));
410 spl_out->scl_data.viewport_c.width = dc_fixpt_ceil(dc_fixpt_mul_int(spl_out->scl_data.ratios.horz_c,
411 spl_out->scl_data.recout.width));
412 spl_out->scl_data.viewport_c.height = dc_fixpt_ceil(dc_fixpt_mul_int(spl_out->scl_data.ratios.vert_c,
413 spl_out->scl_data.recout.height));
414 if (spl_in->basic_in.rotation == SPL_ROTATION_ANGLE_90 ||
415 spl_in->basic_in.rotation == SPL_ROTATION_ANGLE_270) {
416 swap(spl_out->scl_data.viewport.width, spl_out->scl_data.viewport.height);
417 swap(spl_out->scl_data.viewport_c.width, spl_out->scl_data.viewport_c.height);
418 }
419}
420static void spl_get_vp_scan_direction(enum spl_rotation_angle rotation,
421 bool horizontal_mirror,
422 bool *orthogonal_rotation,
423 bool *flip_vert_scan_dir,
424 bool *flip_horz_scan_dir)
425{
426 *orthogonal_rotation = false;
427 *flip_vert_scan_dir = false;
428 *flip_horz_scan_dir = false;
429 if (rotation == SPL_ROTATION_ANGLE_180) {
430 *flip_vert_scan_dir = true;
431 *flip_horz_scan_dir = true;
432 } else if (rotation == SPL_ROTATION_ANGLE_90) {
433 *orthogonal_rotation = true;
434 *flip_horz_scan_dir = true;
435 } else if (rotation == SPL_ROTATION_ANGLE_270) {
436 *orthogonal_rotation = true;
437 *flip_vert_scan_dir = true;
438 }
439
440 if (horizontal_mirror)
441 *flip_horz_scan_dir = !*flip_horz_scan_dir;
442}
443/*
444 * We completely calculate vp offset, size and inits here based entirely on scaling
445 * ratios and recout for pixel perfect pipe combine.
446 */
447static void spl_calculate_init_and_vp(bool flip_scan_dir,
448 int recout_offset_within_recout_full,
449 int recout_size,
450 int src_size,
451 int taps,
452 struct fixed31_32 ratio,
453 struct fixed31_32 init_adj,
454 struct fixed31_32 *init,
455 int *vp_offset,
456 int *vp_size)
457{
458 struct fixed31_32 temp;
459 int int_part;
460
461 /*
462 * First of the taps starts sampling pixel number <init_int_part> corresponding to recout
463 * pixel 1. Next recout pixel samples int part of <init + scaling ratio> and so on.
464 * All following calculations are based on this logic.
465 *
466 * Init calculated according to formula:
467 * init = (scaling_ratio + number_of_taps + 1) / 2
468 * init_bot = init + scaling_ratio
469 * to get pixel perfect combine add the fraction from calculating vp offset
470 */
471 temp = dc_fixpt_mul_int(ratio, recout_offset_within_recout_full);
472 *vp_offset = dc_fixpt_floor(temp);
473 temp.value &= 0xffffffff;
474 *init = dc_fixpt_add(dc_fixpt_div_int(dc_fixpt_add_int(ratio, taps + 1), 2), temp);
475 *init = dc_fixpt_add(*init, init_adj);
476 *init = dc_fixpt_truncate(*init, 19);
477
478 /*
479 * If viewport has non 0 offset and there are more taps than covered by init then
480 * we should decrease the offset and increase init so we are never sampling
481 * outside of viewport.
482 */
483 int_part = dc_fixpt_floor(*init);
484 if (int_part < taps) {
485 int_part = taps - int_part;
486 if (int_part > *vp_offset)
487 int_part = *vp_offset;
488 *vp_offset -= int_part;
489 *init = dc_fixpt_add_int(*init, int_part);
490 }
491 /*
492 * If taps are sampling outside of viewport at end of recout and there are more pixels
493 * available in the surface we should increase the viewport size, regardless set vp to
494 * only what is used.
495 */
496 temp = dc_fixpt_add(*init, dc_fixpt_mul_int(ratio, recout_size - 1));
497 *vp_size = dc_fixpt_floor(temp);
498 if (*vp_size + *vp_offset > src_size)
499 *vp_size = src_size - *vp_offset;
500
501 /* We did all the math assuming we are scanning same direction as display does,
502 * however mirror/rotation changes how vp scans vs how it is offset. If scan direction
503 * is flipped we simply need to calculate offset from the other side of plane.
504 * Note that outside of viewport all scaling hardware works in recout space.
505 */
506 if (flip_scan_dir)
507 *vp_offset = src_size - *vp_offset - *vp_size;
508}
509
510static bool spl_is_yuv420(enum spl_pixel_format format)
511{
512 if ((format >= SPL_PIXEL_FORMAT_VIDEO_BEGIN) &&
513 (format <= SPL_PIXEL_FORMAT_VIDEO_END))
514 return true;
515
516 return false;
517}
518
519/*Calculate inits and viewport */
520static void spl_calculate_inits_and_viewports(struct spl_in *spl_in, struct spl_out *spl_out)
521{
522 struct spl_rect src = spl_in->basic_in.src_rect;
523 struct spl_rect recout_dst_in_active_timing;
524 struct spl_rect recout_clip_in_active_timing;
525 struct spl_rect recout_clip_in_recout_dst;
526 struct spl_rect overlap_in_active_timing;
527 struct spl_rect odm_slice = calculate_odm_slice_in_timing_active(spl_in);
528 int vpc_div = (spl_in->basic_in.format == SPL_PIXEL_FORMAT_420BPP8
529 || spl_in->basic_in.format == SPL_PIXEL_FORMAT_420BPP10) ? 2 : 1;
530 bool orthogonal_rotation, flip_vert_scan_dir, flip_horz_scan_dir;
531 struct fixed31_32 init_adj_h = dc_fixpt_zero;
532 struct fixed31_32 init_adj_v = dc_fixpt_zero;
533
534 recout_clip_in_active_timing = shift_rec(
535 &spl_out->scl_data.recout, odm_slice.x, odm_slice.y);
536 recout_dst_in_active_timing = calculate_plane_rec_in_timing_active(
537 spl_in, &spl_in->basic_in.dst_rect);
538 overlap_in_active_timing = intersect_rec(&recout_clip_in_active_timing,
539 &recout_dst_in_active_timing);
540 if (overlap_in_active_timing.width > 0 &&
541 overlap_in_active_timing.height > 0)
542 recout_clip_in_recout_dst = shift_rec(&overlap_in_active_timing,
543 -recout_dst_in_active_timing.x,
544 -recout_dst_in_active_timing.y);
545 else
546 memset(&recout_clip_in_recout_dst, 0, sizeof(struct spl_rect));
547 /*
548 * Work in recout rotation since that requires less transformations
549 */
550 spl_get_vp_scan_direction(
551 spl_in->basic_in.rotation,
552 spl_in->basic_in.horizontal_mirror,
553 &orthogonal_rotation,
554 &flip_vert_scan_dir,
555 &flip_horz_scan_dir);
556
557 if (orthogonal_rotation) {
558 swap(src.width, src.height);
559 swap(flip_vert_scan_dir, flip_horz_scan_dir);
560 }
561
562 if (spl_is_yuv420(spl_in->basic_in.format)) {
563 /* this gives the direction of the cositing (negative will move
564 * left, right otherwise)
565 */
566 int sign = 1;
567
568 switch (spl_in->basic_in.cositing) {
569
570 case CHROMA_COSITING_LEFT:
571 init_adj_h = dc_fixpt_zero;
572 init_adj_v = dc_fixpt_from_fraction(sign, 2);
573 break;
574 case CHROMA_COSITING_NONE:
575 init_adj_h = dc_fixpt_from_fraction(sign, 2);
576 init_adj_v = dc_fixpt_from_fraction(sign, 2);
577 break;
578 case CHROMA_COSITING_TOPLEFT:
579 default:
580 init_adj_h = dc_fixpt_zero;
581 init_adj_v = dc_fixpt_zero;
582 break;
583 }
584 }
585
586 spl_calculate_init_and_vp(
587 flip_horz_scan_dir,
588 recout_clip_in_recout_dst.x,
589 spl_out->scl_data.recout.width,
590 src.width,
591 spl_out->scl_data.taps.h_taps,
592 spl_out->scl_data.ratios.horz,
593 dc_fixpt_zero,
594 &spl_out->scl_data.inits.h,
595 &spl_out->scl_data.viewport.x,
596 &spl_out->scl_data.viewport.width);
597 spl_calculate_init_and_vp(
598 flip_horz_scan_dir,
599 recout_clip_in_recout_dst.x,
600 spl_out->scl_data.recout.width,
601 src.width / vpc_div,
602 spl_out->scl_data.taps.h_taps_c,
603 spl_out->scl_data.ratios.horz_c,
604 init_adj_h,
605 &spl_out->scl_data.inits.h_c,
606 &spl_out->scl_data.viewport_c.x,
607 &spl_out->scl_data.viewport_c.width);
608 spl_calculate_init_and_vp(
609 flip_vert_scan_dir,
610 recout_clip_in_recout_dst.y,
611 spl_out->scl_data.recout.height,
612 src.height,
613 spl_out->scl_data.taps.v_taps,
614 spl_out->scl_data.ratios.vert,
615 dc_fixpt_zero,
616 &spl_out->scl_data.inits.v,
617 &spl_out->scl_data.viewport.y,
618 &spl_out->scl_data.viewport.height);
619 spl_calculate_init_and_vp(
620 flip_vert_scan_dir,
621 recout_clip_in_recout_dst.y,
622 spl_out->scl_data.recout.height,
623 src.height / vpc_div,
624 spl_out->scl_data.taps.v_taps_c,
625 spl_out->scl_data.ratios.vert_c,
626 init_adj_v,
627 &spl_out->scl_data.inits.v_c,
628 &spl_out->scl_data.viewport_c.y,
629 &spl_out->scl_data.viewport_c.height);
630 if (orthogonal_rotation) {
631 swap(spl_out->scl_data.viewport.x, spl_out->scl_data.viewport.y);
632 swap(spl_out->scl_data.viewport.width, spl_out->scl_data.viewport.height);
633 swap(spl_out->scl_data.viewport_c.x, spl_out->scl_data.viewport_c.y);
634 swap(spl_out->scl_data.viewport_c.width, spl_out->scl_data.viewport_c.height);
635 }
636 spl_out->scl_data.viewport.x += src.x;
637 spl_out->scl_data.viewport.y += src.y;
638 ASSERT(src.x % vpc_div == 0 && src.y % vpc_div == 0);
639 spl_out->scl_data.viewport_c.x += src.x / vpc_div;
640 spl_out->scl_data.viewport_c.y += src.y / vpc_div;
641}
642static void spl_handle_3d_recout(struct spl_in *spl_in, struct spl_rect *recout)
643{
644 /*
645 * Handle side by side and top bottom 3d recout offsets after vp calculation
646 * since 3d is special and needs to calculate vp as if there is no recout offset
647 * This may break with rotation, good thing we aren't mixing hw rotation and 3d
648 */
649 if (spl_in->basic_in.mpc_combine_v) {
650 ASSERT(spl_in->basic_in.rotation == SPL_ROTATION_ANGLE_0 ||
651 (spl_in->basic_out.view_format != SPL_VIEW_3D_TOP_AND_BOTTOM &&
652 spl_in->basic_out.view_format != SPL_VIEW_3D_SIDE_BY_SIDE));
653 if (spl_in->basic_out.view_format == SPL_VIEW_3D_TOP_AND_BOTTOM)
654 recout->y += recout->height;
655 else if (spl_in->basic_out.view_format == SPL_VIEW_3D_SIDE_BY_SIDE)
656 recout->x += recout->width;
657 }
658}
659
660static void spl_clamp_viewport(struct spl_rect *viewport)
661{
662 /* Clamp minimum viewport size */
663 if (viewport->height < MIN_VIEWPORT_SIZE)
664 viewport->height = MIN_VIEWPORT_SIZE;
665 if (viewport->width < MIN_VIEWPORT_SIZE)
666 viewport->width = MIN_VIEWPORT_SIZE;
667}
668static bool spl_dscl_is_420_format(enum spl_pixel_format format)
669{
670 if (format == SPL_PIXEL_FORMAT_420BPP8 ||
671 format == SPL_PIXEL_FORMAT_420BPP10)
672 return true;
673 else
674 return false;
675}
676static bool spl_dscl_is_video_format(enum spl_pixel_format format)
677{
678 if (format >= SPL_PIXEL_FORMAT_VIDEO_BEGIN
679 && format <= SPL_PIXEL_FORMAT_VIDEO_END)
680 return true;
681 else
682 return false;
683}
684static enum scl_mode spl_get_dscl_mode(const struct spl_in *spl_in,
685 const struct spl_scaler_data *data)
686{
687 const long long one = dc_fixpt_one.value;
688 enum spl_pixel_format pixel_format = spl_in->basic_in.format;
689
690 if (data->ratios.horz.value == one
691 && data->ratios.vert.value == one
692 && data->ratios.horz_c.value == one
693 && data->ratios.vert_c.value == one
694 && !spl_in->basic_out.always_scale)
695 return SCL_MODE_SCALING_444_BYPASS;
696
697 if (!spl_dscl_is_420_format(pixel_format)) {
698 if (spl_dscl_is_video_format(pixel_format))
699 return SCL_MODE_SCALING_444_YCBCR_ENABLE;
700 else
701 return SCL_MODE_SCALING_444_RGB_ENABLE;
702 }
703 if (data->ratios.horz.value == one && data->ratios.vert.value == one)
704 return SCL_MODE_SCALING_420_LUMA_BYPASS;
705 if (data->ratios.horz_c.value == one && data->ratios.vert_c.value == one)
706 return SCL_MODE_SCALING_420_CHROMA_BYPASS;
707
708 return SCL_MODE_SCALING_420_YCBCR_ENABLE;
709}
710/* Calculate optimal number of taps */
711static bool spl_get_optimal_number_of_taps(
712 int max_downscale_src_width, struct spl_in *spl_in, struct spl_out *spl_out,
713 const struct spl_taps *in_taps)
714{
715 int num_part_y, num_part_c;
716 int max_taps_y, max_taps_c;
717 int min_taps_y, min_taps_c;
718 enum lb_memory_config lb_config;
719
720 if (spl_out->scl_data.viewport.width > spl_out->scl_data.h_active &&
721 max_downscale_src_width != 0 &&
722 spl_out->scl_data.viewport.width > max_downscale_src_width)
723 return false;
724 /*
725 * Set default taps if none are provided
726 * From programming guide: taps = min{ ceil(2*H_RATIO,1), 8} for downscaling
727 * taps = 4 for upscaling
728 */
729 if (in_taps->h_taps == 0) {
730 if (dc_fixpt_ceil(spl_out->scl_data.ratios.horz) > 1)
731 spl_out->scl_data.taps.h_taps = min(2 * dc_fixpt_ceil(spl_out->scl_data.ratios.horz), 8);
732 else
733 spl_out->scl_data.taps.h_taps = 4;
734 } else
735 spl_out->scl_data.taps.h_taps = in_taps->h_taps;
736 if (in_taps->v_taps == 0) {
737 if (dc_fixpt_ceil(spl_out->scl_data.ratios.vert) > 1)
738 spl_out->scl_data.taps.v_taps = min(dc_fixpt_ceil(dc_fixpt_mul_int(
739 spl_out->scl_data.ratios.vert, 2)), 8);
740 else
741 spl_out->scl_data.taps.v_taps = 4;
742 } else
743 spl_out->scl_data.taps.v_taps = in_taps->v_taps;
744 if (in_taps->v_taps_c == 0) {
745 if (dc_fixpt_ceil(spl_out->scl_data.ratios.vert_c) > 1)
746 spl_out->scl_data.taps.v_taps_c = min(dc_fixpt_ceil(dc_fixpt_mul_int(
747 spl_out->scl_data.ratios.vert_c, 2)), 8);
748 else
749 spl_out->scl_data.taps.v_taps_c = 4;
750 } else
751 spl_out->scl_data.taps.v_taps_c = in_taps->v_taps_c;
752 if (in_taps->h_taps_c == 0) {
753 if (dc_fixpt_ceil(spl_out->scl_data.ratios.horz_c) > 1)
754 spl_out->scl_data.taps.h_taps_c = min(2 * dc_fixpt_ceil(spl_out->scl_data.ratios.horz_c), 8);
755 else
756 spl_out->scl_data.taps.h_taps_c = 4;
757 } else if ((in_taps->h_taps_c % 2) != 0 && in_taps->h_taps_c != 1)
758 /* Only 1 and even h_taps_c are supported by hw */
759 spl_out->scl_data.taps.h_taps_c = in_taps->h_taps_c - 1;
760 else
761 spl_out->scl_data.taps.h_taps_c = in_taps->h_taps_c;
762
763 /*Ensure we can support the requested number of vtaps*/
764 min_taps_y = dc_fixpt_ceil(spl_out->scl_data.ratios.vert);
765 min_taps_c = dc_fixpt_ceil(spl_out->scl_data.ratios.vert_c);
766
767 /* Use LB_MEMORY_CONFIG_3 for 4:2:0 */
768 if ((spl_in->basic_in.format == SPL_PIXEL_FORMAT_420BPP8)
769 || (spl_in->basic_in.format == SPL_PIXEL_FORMAT_420BPP10))
770 lb_config = LB_MEMORY_CONFIG_3;
771 else
772 lb_config = LB_MEMORY_CONFIG_0;
773 // Determine max vtap support by calculating how much line buffer can fit
774 spl_in->funcs->spl_calc_lb_num_partitions(spl_in->basic_out.alpha_en, &spl_out->scl_data,
775 lb_config, &num_part_y, &num_part_c);
776 /* MAX_V_TAPS = MIN (NUM_LINES - MAX(CEILING(V_RATIO,1)-2, 0), 8) */
777 if (dc_fixpt_ceil(spl_out->scl_data.ratios.vert) > 2)
778 max_taps_y = num_part_y - (dc_fixpt_ceil(spl_out->scl_data.ratios.vert) - 2);
779 else
780 max_taps_y = num_part_y;
781
782 if (dc_fixpt_ceil(spl_out->scl_data.ratios.vert_c) > 2)
783 max_taps_c = num_part_c - (dc_fixpt_ceil(spl_out->scl_data.ratios.vert_c) - 2);
784 else
785 max_taps_c = num_part_c;
786
787 if (max_taps_y < min_taps_y)
788 return false;
789 else if (max_taps_c < min_taps_c)
790 return false;
791
792 if (spl_out->scl_data.taps.v_taps > max_taps_y)
793 spl_out->scl_data.taps.v_taps = max_taps_y;
794
795 if (spl_out->scl_data.taps.v_taps_c > max_taps_c)
796 spl_out->scl_data.taps.v_taps_c = max_taps_c;
797 if (spl_in->prefer_easf) {
798 // EASF can be enabled only for taps 3,4,6
799 // If optimal no of taps is 5, then set it to 4
800 // If optimal no of taps is 7 or 8, then set it to 6
801 if (spl_out->scl_data.taps.v_taps == 5)
802 spl_out->scl_data.taps.v_taps = 4;
803 if (spl_out->scl_data.taps.v_taps == 7 || spl_out->scl_data.taps.v_taps == 8)
804 spl_out->scl_data.taps.v_taps = 6;
805
806 if (spl_out->scl_data.taps.v_taps_c == 5)
807 spl_out->scl_data.taps.v_taps_c = 4;
808 if (spl_out->scl_data.taps.v_taps_c == 7 || spl_out->scl_data.taps.v_taps_c == 8)
809 spl_out->scl_data.taps.v_taps_c = 6;
810
811 if (spl_out->scl_data.taps.h_taps == 5)
812 spl_out->scl_data.taps.h_taps = 4;
813 if (spl_out->scl_data.taps.h_taps == 7 || spl_out->scl_data.taps.h_taps == 8)
814 spl_out->scl_data.taps.h_taps = 6;
815
816 if (spl_out->scl_data.taps.h_taps_c == 5)
817 spl_out->scl_data.taps.h_taps_c = 4;
818 if (spl_out->scl_data.taps.h_taps_c == 7 || spl_out->scl_data.taps.h_taps_c == 8)
819 spl_out->scl_data.taps.h_taps_c = 6;
820
821 } // end of if prefer_easf
822 if (!spl_in->basic_out.always_scale) {
823 if (IDENTITY_RATIO(spl_out->scl_data.ratios.horz))
824 spl_out->scl_data.taps.h_taps = 1;
825 if (IDENTITY_RATIO(spl_out->scl_data.ratios.vert))
826 spl_out->scl_data.taps.v_taps = 1;
827 if (IDENTITY_RATIO(spl_out->scl_data.ratios.horz_c))
828 spl_out->scl_data.taps.h_taps_c = 1;
829 if (IDENTITY_RATIO(spl_out->scl_data.ratios.vert_c))
830 spl_out->scl_data.taps.v_taps_c = 1;
831 }
832 return true;
833}
834static void spl_set_black_color_data(enum spl_pixel_format format,
835 struct scl_black_color *scl_black_color)
836{
837 bool ycbcr = format >= SPL_PIXEL_FORMAT_VIDEO_BEGIN
838 && format <= SPL_PIXEL_FORMAT_VIDEO_END;
839 if (ycbcr) {
840 scl_black_color->offset_rgb_y = BLACK_OFFSET_RGB_Y;
841 scl_black_color->offset_rgb_cbcr = BLACK_OFFSET_CBCR;
842 } else {
843 scl_black_color->offset_rgb_y = 0x0;
844 scl_black_color->offset_rgb_cbcr = 0x0;
845 }
846}
847
848static void spl_set_manual_ratio_init_data(struct dscl_prog_data *dscl_prog_data,
849 const struct spl_scaler_data *scl_data)
850{
851 struct fixed31_32 bot;
852
853 dscl_prog_data->ratios.h_scale_ratio = dc_fixpt_u3d19(scl_data->ratios.horz) << 5;
854 dscl_prog_data->ratios.v_scale_ratio = dc_fixpt_u3d19(scl_data->ratios.vert) << 5;
855 dscl_prog_data->ratios.h_scale_ratio_c = dc_fixpt_u3d19(scl_data->ratios.horz_c) << 5;
856 dscl_prog_data->ratios.v_scale_ratio_c = dc_fixpt_u3d19(scl_data->ratios.vert_c) << 5;
857 /*
858 * 0.24 format for fraction, first five bits zeroed
859 */
860 dscl_prog_data->init.h_filter_init_frac =
861 dc_fixpt_u0d19(scl_data->inits.h) << 5;
862 dscl_prog_data->init.h_filter_init_int =
863 dc_fixpt_floor(scl_data->inits.h);
864 dscl_prog_data->init.h_filter_init_frac_c =
865 dc_fixpt_u0d19(scl_data->inits.h_c) << 5;
866 dscl_prog_data->init.h_filter_init_int_c =
867 dc_fixpt_floor(scl_data->inits.h_c);
868 dscl_prog_data->init.v_filter_init_frac =
869 dc_fixpt_u0d19(scl_data->inits.v) << 5;
870 dscl_prog_data->init.v_filter_init_int =
871 dc_fixpt_floor(scl_data->inits.v);
872 dscl_prog_data->init.v_filter_init_frac_c =
873 dc_fixpt_u0d19(scl_data->inits.v_c) << 5;
874 dscl_prog_data->init.v_filter_init_int_c =
875 dc_fixpt_floor(scl_data->inits.v_c);
876
877 bot = dc_fixpt_add(scl_data->inits.v, scl_data->ratios.vert);
878 dscl_prog_data->init.v_filter_init_bot_frac = dc_fixpt_u0d19(bot) << 5;
879 dscl_prog_data->init.v_filter_init_bot_int = dc_fixpt_floor(bot);
880 bot = dc_fixpt_add(scl_data->inits.v_c, scl_data->ratios.vert_c);
881 dscl_prog_data->init.v_filter_init_bot_frac_c = dc_fixpt_u0d19(bot) << 5;
882 dscl_prog_data->init.v_filter_init_bot_int_c = dc_fixpt_floor(bot);
883}
884
885static void spl_set_taps_data(struct dscl_prog_data *dscl_prog_data,
886 const struct spl_scaler_data *scl_data)
887{
888 dscl_prog_data->taps.v_taps = scl_data->taps.v_taps - 1;
889 dscl_prog_data->taps.h_taps = scl_data->taps.h_taps - 1;
890 dscl_prog_data->taps.v_taps_c = scl_data->taps.v_taps_c - 1;
891 dscl_prog_data->taps.h_taps_c = scl_data->taps.h_taps_c - 1;
892}
893static const uint16_t *spl_dscl_get_filter_coeffs_64p(int taps, struct fixed31_32 ratio)
894{
895 if (taps == 8)
896 return spl_get_filter_8tap_64p(ratio);
897 else if (taps == 7)
898 return spl_get_filter_7tap_64p(ratio);
899 else if (taps == 6)
900 return spl_get_filter_6tap_64p(ratio);
901 else if (taps == 5)
902 return spl_get_filter_5tap_64p(ratio);
903 else if (taps == 4)
904 return spl_get_filter_4tap_64p(ratio);
905 else if (taps == 3)
906 return spl_get_filter_3tap_64p(ratio);
907 else if (taps == 2)
908 return spl_get_filter_2tap_64p();
909 else if (taps == 1)
910 return NULL;
911 else {
912 /* should never happen, bug */
913 return NULL;
914 }
915}
916static void spl_set_filters_data(struct dscl_prog_data *dscl_prog_data,
917 const struct spl_scaler_data *data)
918{
919 dscl_prog_data->filter_h = spl_dscl_get_filter_coeffs_64p(
920 data->taps.h_taps, data->ratios.horz);
921 dscl_prog_data->filter_v = spl_dscl_get_filter_coeffs_64p(
922 data->taps.v_taps, data->ratios.vert);
923 dscl_prog_data->filter_h_c = spl_dscl_get_filter_coeffs_64p(
924 data->taps.h_taps_c, data->ratios.horz_c);
925 dscl_prog_data->filter_v_c = spl_dscl_get_filter_coeffs_64p(
926 data->taps.v_taps_c, data->ratios.vert_c);
927}
928
929static const uint16_t *spl_dscl_get_blur_scale_coeffs_64p(int taps)
930{
931 if ((taps == 3) || (taps == 4) || (taps == 6))
932 return spl_get_filter_isharp_bs_4tap_64p();
933 else {
934 /* should never happen, bug */
935 return NULL;
936 }
937}
938static void spl_set_blur_scale_data(struct dscl_prog_data *dscl_prog_data,
939 const struct spl_scaler_data *data)
940{
941 dscl_prog_data->filter_blur_scale_h = spl_dscl_get_blur_scale_coeffs_64p(
942 data->taps.h_taps);
943 dscl_prog_data->filter_blur_scale_v = spl_dscl_get_blur_scale_coeffs_64p(
944 data->taps.v_taps);
945}
946
947/* Populate dscl prog data structure from scaler data calculated by SPL */
948static void spl_set_dscl_prog_data(struct spl_in *spl_in, struct spl_out *spl_out)
949{
950 struct dscl_prog_data *dscl_prog_data = spl_out->dscl_prog_data;
951
952 const struct spl_scaler_data *data = &spl_out->scl_data;
953
954 struct scl_black_color *scl_black_color = &dscl_prog_data->scl_black_color;
955
956 // Set values for recout
957 dscl_prog_data->recout = spl_out->scl_data.recout;
958 // Set values for MPC Size
959 dscl_prog_data->mpc_size.width = spl_out->scl_data.h_active;
960 dscl_prog_data->mpc_size.height = spl_out->scl_data.v_active;
961
962 // SCL_MODE - Set SCL_MODE data
963 dscl_prog_data->dscl_mode = spl_get_dscl_mode(spl_in, data);
964
965 // SCL_BLACK_COLOR
966 spl_set_black_color_data(spl_in->basic_in.format, scl_black_color);
967
968 /* Manually calculate scale ratio and init values */
969 spl_set_manual_ratio_init_data(dscl_prog_data, data);
970
971 // Set HTaps/VTaps
972 spl_set_taps_data(dscl_prog_data, data);
973 // Set viewport
974 dscl_prog_data->viewport = spl_out->scl_data.viewport;
975 // Set viewport_c
976 dscl_prog_data->viewport_c = spl_out->scl_data.viewport_c;
977 // Set filters data
978 spl_set_filters_data(dscl_prog_data, data);
979}
980/* Enable EASF ?*/
981static bool enable_easf(int scale_ratio, int taps,
982 enum linear_light_scaling lls_pref, bool prefer_easf)
983{
984 // Is downscaling > 6:1 ?
985 if (scale_ratio > 6) {
986 // END - No EASF support for downscaling > 6:1
987 return false;
988 }
989 // Is upscaling or downscaling up to 2:1?
990 if (scale_ratio <= 2) {
991 // Is linear scaling or EASF preferred?
992 if (lls_pref == LLS_PREF_YES || prefer_easf) {
993 // LB support taps 3, 4, 6
994 if (taps == 3 || taps == 4 || taps == 6) {
995 // END - EASF supported
996 return true;
997 }
998 }
999 }
1000 // END - EASF not supported
1001 return false;
1002}
1003/* Set EASF data */
1004static void spl_set_easf_data(struct dscl_prog_data *dscl_prog_data,
1005 bool enable_easf_v, bool enable_easf_h, enum linear_light_scaling lls_pref,
1006 enum spl_pixel_format format)
1007{
1008 if (spl_is_yuv420(format)) /* TODO: 0 = RGB, 1 = YUV */
1009 dscl_prog_data->easf_matrix_mode = 1;
1010 else
1011 dscl_prog_data->easf_matrix_mode = 0;
1012
1013 if (enable_easf_v) {
1014 dscl_prog_data->easf_v_en = true;
1015 dscl_prog_data->easf_v_ring = 0;
1016 dscl_prog_data->easf_v_sharp_factor = 1;
1017 dscl_prog_data->easf_v_bf1_en = 1; // 1-bit, BF1 calculation enable, 0=disable, 1=enable
1018 dscl_prog_data->easf_v_bf2_mode = 0xF; // 4-bit, BF2 calculation mode
1019 dscl_prog_data->easf_v_bf3_mode = 2; // 2-bit, BF3 chroma mode correction calculation mode
1020 dscl_prog_data->easf_v_bf2_flat1_gain = 4; // U1.3, BF2 Flat1 Gain control
1021 dscl_prog_data->easf_v_bf2_flat2_gain = 8; // U4.0, BF2 Flat2 Gain control
1022 dscl_prog_data->easf_v_bf2_roc_gain = 4; // U2.2, Rate Of Change control
1023 dscl_prog_data->easf_v_ringest_3tap_dntilt_uptilt =
1024 0x9F00;// FP1.5.10 [minCoef] (-0.036109167214271)
1025 dscl_prog_data->easf_v_ringest_3tap_uptilt_max =
1026 0x24FE; // FP1.5.10 [upTiltMaxVal] ( 0.904556445553545)
1027 dscl_prog_data->easf_v_ringest_3tap_dntilt_slope =
1028 0x3940; // FP1.5.10 [dnTiltSlope] ( 0.910488988173371)
1029 dscl_prog_data->easf_v_ringest_3tap_uptilt1_slope =
1030 0x359C; // FP1.5.10 [upTilt1Slope] ( 0.125620179040899)
1031 dscl_prog_data->easf_v_ringest_3tap_uptilt2_slope =
1032 0x359C; // FP1.5.10 [upTilt2Slope] ( 0.006786817723568)
1033 dscl_prog_data->easf_v_ringest_3tap_uptilt2_offset =
1034 0x9F00; // FP1.5.10 [upTilt2Offset] (-0.006139059716651)
1035 dscl_prog_data->easf_v_ringest_eventap_reduceg1 =
1036 0x4000; // FP1.5.10; (2.0) Ring reducer gain for 4 or 6-tap mode [H_REDUCER_GAIN4]
1037 dscl_prog_data->easf_v_ringest_eventap_reduceg2 =
1038 0x4100; // FP1.5.10; (2.5) Ring reducer gain for 6-tap mode [V_REDUCER_GAIN6]
1039 dscl_prog_data->easf_v_ringest_eventap_gain1 =
1040 0xB058; // FP1.5.10; (-0.135742) Ring gain for 6-tap set to -139/1024
1041 dscl_prog_data->easf_v_ringest_eventap_gain2 =
1042 0xA640; // FP1.5.10; (-0.024414) Ring gain for 6-tap set to -25/1024
1043 dscl_prog_data->easf_v_bf_maxa = 63; //Vertical Max BF value A in U0.6 format.Selected if V_FCNTL == 0
1044 dscl_prog_data->easf_v_bf_maxb = 63; //Vertical Max BF value A in U0.6 format.Selected if V_FCNTL == 1
1045 dscl_prog_data->easf_v_bf_mina = 0; //Vertical Min BF value A in U0.6 format.Selected if V_FCNTL == 0
1046 dscl_prog_data->easf_v_bf_minb = 0; //Vertical Min BF value A in U0.6 format.Selected if V_FCNTL == 1
1047 dscl_prog_data->easf_v_bf1_pwl_in_seg0 = -512; // S0.10, BF1 PWL Segment 0
1048 dscl_prog_data->easf_v_bf1_pwl_base_seg0 = 0; // U0.6, BF1 Base PWL Segment 0
1049 dscl_prog_data->easf_v_bf1_pwl_slope_seg0 = 3; // S7.3, BF1 Slope PWL Segment 0
1050 dscl_prog_data->easf_v_bf1_pwl_in_seg1 = -20; // S0.10, BF1 PWL Segment 1
1051 dscl_prog_data->easf_v_bf1_pwl_base_seg1 = 12; // U0.6, BF1 Base PWL Segment 1
1052 dscl_prog_data->easf_v_bf1_pwl_slope_seg1 = 326; // S7.3, BF1 Slope PWL Segment 1
1053 dscl_prog_data->easf_v_bf1_pwl_in_seg2 = 0; // S0.10, BF1 PWL Segment 2
1054 dscl_prog_data->easf_v_bf1_pwl_base_seg2 = 63; // U0.6, BF1 Base PWL Segment 2
1055 dscl_prog_data->easf_v_bf1_pwl_slope_seg2 = 0; // S7.3, BF1 Slope PWL Segment 2
1056 dscl_prog_data->easf_v_bf1_pwl_in_seg3 = 16; // S0.10, BF1 PWL Segment 3
1057 dscl_prog_data->easf_v_bf1_pwl_base_seg3 = 63; // U0.6, BF1 Base PWL Segment 3
1058 dscl_prog_data->easf_v_bf1_pwl_slope_seg3 = -56; // S7.3, BF1 Slope PWL Segment 3
1059 dscl_prog_data->easf_v_bf1_pwl_in_seg4 = 32; // S0.10, BF1 PWL Segment 4
1060 dscl_prog_data->easf_v_bf1_pwl_base_seg4 = 56; // U0.6, BF1 Base PWL Segment 4
1061 dscl_prog_data->easf_v_bf1_pwl_slope_seg4 = -48; // S7.3, BF1 Slope PWL Segment 4
1062 dscl_prog_data->easf_v_bf1_pwl_in_seg5 = 48; // S0.10, BF1 PWL Segment 5
1063 dscl_prog_data->easf_v_bf1_pwl_base_seg5 = 50; // U0.6, BF1 Base PWL Segment 5
1064 dscl_prog_data->easf_v_bf1_pwl_slope_seg5 = -240; // S7.3, BF1 Slope PWL Segment 5
1065 dscl_prog_data->easf_v_bf1_pwl_in_seg6 = 64; // S0.10, BF1 PWL Segment 6
1066 dscl_prog_data->easf_v_bf1_pwl_base_seg6 = 20; // U0.6, BF1 Base PWL Segment 6
1067 dscl_prog_data->easf_v_bf1_pwl_slope_seg6 = -160; // S7.3, BF1 Slope PWL Segment 6
1068 dscl_prog_data->easf_v_bf1_pwl_in_seg7 = 80; // S0.10, BF1 PWL Segment 7
1069 dscl_prog_data->easf_v_bf1_pwl_base_seg7 = 0; // U0.6, BF1 Base PWL Segment 7
1070 if (lls_pref == LLS_PREF_YES) {
1071 dscl_prog_data->easf_v_bf3_pwl_in_set0 = 0x000; // FP0.6.6, BF3 Input value PWL Segment 0
1072 dscl_prog_data->easf_v_bf3_pwl_base_set0 = 63; // S0.6, BF3 Base PWL Segment 0
1073 dscl_prog_data->easf_v_bf3_pwl_slope_set0 = 0x12C5; // FP1.6.6, BF3 Slope PWL Segment 0
1074 dscl_prog_data->easf_v_bf3_pwl_in_set1 =
1075 0x0B37; // FP0.6.6, BF3 Input value PWL Segment 1 (0.0078125 * 125^3)
1076 dscl_prog_data->easf_v_bf3_pwl_base_set1 = 62; // S0.6, BF3 Base PWL Segment 1
1077 dscl_prog_data->easf_v_bf3_pwl_slope_set1 =
1078 0x13B8; // FP1.6.6, BF3 Slope PWL Segment 1
1079 dscl_prog_data->easf_v_bf3_pwl_in_set2 =
1080 0x0BB7; // FP0.6.6, BF3 Input value PWL Segment 2 (0.03125 * 125^3)
1081 dscl_prog_data->easf_v_bf3_pwl_base_set2 = 20; // S0.6, BF3 Base PWL Segment 2
1082 dscl_prog_data->easf_v_bf3_pwl_slope_set2 =
1083 0x1356; // FP1.6.6, BF3 Slope PWL Segment 2
1084 dscl_prog_data->easf_v_bf3_pwl_in_set3 =
1085 0x0BF7; // FP0.6.6, BF3 Input value PWL Segment 3 (0.0625 * 125^3)
1086 dscl_prog_data->easf_v_bf3_pwl_base_set3 = 0; // S0.6, BF3 Base PWL Segment 3
1087 dscl_prog_data->easf_v_bf3_pwl_slope_set3 =
1088 0x136B; // FP1.6.6, BF3 Slope PWL Segment 3
1089 dscl_prog_data->easf_v_bf3_pwl_in_set4 =
1090 0x0C37; // FP0.6.6, BF3 Input value PWL Segment 4 (0.125 * 125^3)
1091 dscl_prog_data->easf_v_bf3_pwl_base_set4 = -50; // S0.6, BF3 Base PWL Segment 4
1092 dscl_prog_data->easf_v_bf3_pwl_slope_set4 =
1093 0x1200; // FP1.6.6, BF3 Slope PWL Segment 4
1094 dscl_prog_data->easf_v_bf3_pwl_in_set5 =
1095 0x0CF7; // FP0.6.6, BF3 Input value PWL Segment 5 (1.0 * 125^3)
1096 dscl_prog_data->easf_v_bf3_pwl_base_set5 = -63; // S0.6, BF3 Base PWL Segment 5
1097 } else {
1098 dscl_prog_data->easf_v_bf3_pwl_in_set0 = 0x000; // FP0.6.6, BF3 Input value PWL Segment 0
1099 dscl_prog_data->easf_v_bf3_pwl_base_set0 = 63; // S0.6, BF3 Base PWL Segment 0
1100 dscl_prog_data->easf_v_bf3_pwl_slope_set0 = 0x0000; // FP1.6.6, BF3 Slope PWL Segment 0
1101 dscl_prog_data->easf_v_bf3_pwl_in_set1 =
1102 0x06C0; // FP0.6.6, BF3 Input value PWL Segment 1 (0.0625)
1103 dscl_prog_data->easf_v_bf3_pwl_base_set1 = 63; // S0.6, BF3 Base PWL Segment 1
1104 dscl_prog_data->easf_v_bf3_pwl_slope_set1 = 0x1896; // FP1.6.6, BF3 Slope PWL Segment 1
1105 dscl_prog_data->easf_v_bf3_pwl_in_set2 =
1106 0x0700; // FP0.6.6, BF3 Input value PWL Segment 2 (0.125)
1107 dscl_prog_data->easf_v_bf3_pwl_base_set2 = 20; // S0.6, BF3 Base PWL Segment 2
1108 dscl_prog_data->easf_v_bf3_pwl_slope_set2 = 0x1810; // FP1.6.6, BF3 Slope PWL Segment 2
1109 dscl_prog_data->easf_v_bf3_pwl_in_set3 =
1110 0x0740; // FP0.6.6, BF3 Input value PWL Segment 3 (0.25)
1111 dscl_prog_data->easf_v_bf3_pwl_base_set3 = 0; // S0.6, BF3 Base PWL Segment 3
1112 dscl_prog_data->easf_v_bf3_pwl_slope_set3 =
1113 0x1878; // FP1.6.6, BF3 Slope PWL Segment 3
1114 dscl_prog_data->easf_v_bf3_pwl_in_set4 =
1115 0x0761; // FP0.6.6, BF3 Input value PWL Segment 4 (0.375)
1116 dscl_prog_data->easf_v_bf3_pwl_base_set4 = -60; // S0.6, BF3 Base PWL Segment 4
1117 dscl_prog_data->easf_v_bf3_pwl_slope_set4 = 0x1760; // FP1.6.6, BF3 Slope PWL Segment 4
1118 dscl_prog_data->easf_v_bf3_pwl_in_set5 =
1119 0x0780; // FP0.6.6, BF3 Input value PWL Segment 5 (0.5)
1120 dscl_prog_data->easf_v_bf3_pwl_base_set5 = -63; // S0.6, BF3 Base PWL Segment 5
1121 }
1122 } else
1123 dscl_prog_data->easf_v_en = false;
1124
1125 if (enable_easf_h) {
1126 dscl_prog_data->easf_h_en = true;
1127 dscl_prog_data->easf_h_ring = 0;
1128 dscl_prog_data->easf_h_sharp_factor = 1;
1129 dscl_prog_data->easf_h_bf1_en =
1130 1; // 1-bit, BF1 calculation enable, 0=disable, 1=enable
1131 dscl_prog_data->easf_h_bf2_mode =
1132 0xF; // 4-bit, BF2 calculation mode
1133 dscl_prog_data->easf_h_bf3_mode =
1134 2; // 2-bit, BF3 chroma mode correction calculation mode
1135 dscl_prog_data->easf_h_bf2_flat1_gain = 4; // U1.3, BF2 Flat1 Gain control
1136 dscl_prog_data->easf_h_bf2_flat2_gain = 8; // U4.0, BF2 Flat2 Gain control
1137 dscl_prog_data->easf_h_bf2_roc_gain = 4; // U2.2, Rate Of Change control
1138 dscl_prog_data->easf_h_ringest_eventap_reduceg1 =
1139 0x4000; // FP1.5.10; (2.0) Ring reducer gain for 4 or 6-tap mode [H_REDUCER_GAIN4]
1140 dscl_prog_data->easf_h_ringest_eventap_reduceg2 =
1141 0x4100; // FP1.5.10; (2.5) Ring reducer gain for 6-tap mode [V_REDUCER_GAIN6]
1142 dscl_prog_data->easf_h_ringest_eventap_gain1 =
1143 0xB058; // FP1.5.10; (-0.135742) Ring gain for 6-tap set to -139/1024
1144 dscl_prog_data->easf_h_ringest_eventap_gain2 =
1145 0xA640; // FP1.5.10; (-0.024414) Ring gain for 6-tap set to -25/1024
1146 dscl_prog_data->easf_h_bf_maxa = 63; //Horz Max BF value A in U0.6 format.Selected if H_FCNTL==0
1147 dscl_prog_data->easf_h_bf_maxb = 63; //Horz Max BF value B in U0.6 format.Selected if H_FCNTL==1
1148 dscl_prog_data->easf_h_bf_mina = 0; //Horz Min BF value B in U0.6 format.Selected if H_FCNTL==0
1149 dscl_prog_data->easf_h_bf_minb = 0; //Horz Min BF value B in U0.6 format.Selected if H_FCNTL==1
1150 dscl_prog_data->easf_h_bf1_pwl_in_seg0 = -512; // S0.10, BF1 PWL Segment 0
1151 dscl_prog_data->easf_h_bf1_pwl_base_seg0 = 0; // U0.6, BF1 Base PWL Segment 0
1152 dscl_prog_data->easf_h_bf1_pwl_slope_seg0 = 3; // S7.3, BF1 Slope PWL Segment 0
1153 dscl_prog_data->easf_h_bf1_pwl_in_seg1 = -20; // S0.10, BF1 PWL Segment 1
1154 dscl_prog_data->easf_h_bf1_pwl_base_seg1 = 12; // U0.6, BF1 Base PWL Segment 1
1155 dscl_prog_data->easf_h_bf1_pwl_slope_seg1 = 326; // S7.3, BF1 Slope PWL Segment 1
1156 dscl_prog_data->easf_h_bf1_pwl_in_seg2 = 0; // S0.10, BF1 PWL Segment 2
1157 dscl_prog_data->easf_h_bf1_pwl_base_seg2 = 63; // U0.6, BF1 Base PWL Segment 2
1158 dscl_prog_data->easf_h_bf1_pwl_slope_seg2 = 0; // S7.3, BF1 Slope PWL Segment 2
1159 dscl_prog_data->easf_h_bf1_pwl_in_seg3 = 16; // S0.10, BF1 PWL Segment 3
1160 dscl_prog_data->easf_h_bf1_pwl_base_seg3 = 63; // U0.6, BF1 Base PWL Segment 3
1161 dscl_prog_data->easf_h_bf1_pwl_slope_seg3 = -56; // S7.3, BF1 Slope PWL Segment 3
1162 dscl_prog_data->easf_h_bf1_pwl_in_seg4 = 32; // S0.10, BF1 PWL Segment 4
1163 dscl_prog_data->easf_h_bf1_pwl_base_seg4 = 56; // U0.6, BF1 Base PWL Segment 4
1164 dscl_prog_data->easf_h_bf1_pwl_slope_seg4 = -48; // S7.3, BF1 Slope PWL Segment 4
1165 dscl_prog_data->easf_h_bf1_pwl_in_seg5 = 48; // S0.10, BF1 PWL Segment 5
1166 dscl_prog_data->easf_h_bf1_pwl_base_seg5 = 50; // U0.6, BF1 Base PWL Segment 5
1167 dscl_prog_data->easf_h_bf1_pwl_slope_seg5 = -240; // S7.3, BF1 Slope PWL Segment 5
1168 dscl_prog_data->easf_h_bf1_pwl_in_seg6 = 64; // S0.10, BF1 PWL Segment 6
1169 dscl_prog_data->easf_h_bf1_pwl_base_seg6 = 20; // U0.6, BF1 Base PWL Segment 6
1170 dscl_prog_data->easf_h_bf1_pwl_slope_seg6 = -160; // S7.3, BF1 Slope PWL Segment 6
1171 dscl_prog_data->easf_h_bf1_pwl_in_seg7 = 80; // S0.10, BF1 PWL Segment 7
1172 dscl_prog_data->easf_h_bf1_pwl_base_seg7 = 0; // U0.6, BF1 Base PWL Segment 7
1173 if (lls_pref == LLS_PREF_YES) {
1174 dscl_prog_data->easf_h_bf3_pwl_in_set0 = 0x000; // FP0.6.6, BF3 Input value PWL Segment 0
1175 dscl_prog_data->easf_h_bf3_pwl_base_set0 = 63; // S0.6, BF3 Base PWL Segment 0
1176 dscl_prog_data->easf_h_bf3_pwl_slope_set0 = 0x12C5; // FP1.6.6, BF3 Slope PWL Segment 0
1177 dscl_prog_data->easf_h_bf3_pwl_in_set1 =
1178 0x0B37; // FP0.6.6, BF3 Input value PWL Segment 1 (0.0078125 * 125^3)
1179 dscl_prog_data->easf_h_bf3_pwl_base_set1 = 62; // S0.6, BF3 Base PWL Segment 1
1180 dscl_prog_data->easf_h_bf3_pwl_slope_set1 = 0x13B8; // FP1.6.6, BF3 Slope PWL Segment 1
1181 dscl_prog_data->easf_h_bf3_pwl_in_set2 =
1182 0x0BB7; // FP0.6.6, BF3 Input value PWL Segment 2 (0.03125 * 125^3)
1183 dscl_prog_data->easf_h_bf3_pwl_base_set2 = 20; // S0.6, BF3 Base PWL Segment 2
1184 dscl_prog_data->easf_h_bf3_pwl_slope_set2 = 0x1356; // FP1.6.6, BF3 Slope PWL Segment 2
1185 dscl_prog_data->easf_h_bf3_pwl_in_set3 =
1186 0x0BF7; // FP0.6.6, BF3 Input value PWL Segment 3 (0.0625 * 125^3)
1187 dscl_prog_data->easf_h_bf3_pwl_base_set3 = 0; // S0.6, BF3 Base PWL Segment 3
1188 dscl_prog_data->easf_h_bf3_pwl_slope_set3 = 0x136B; // FP1.6.6, BF3 Slope PWL Segment 3
1189 dscl_prog_data->easf_h_bf3_pwl_in_set4 =
1190 0x0C37; // FP0.6.6, BF3 Input value PWL Segment 4 (0.125 * 125^3)
1191 dscl_prog_data->easf_h_bf3_pwl_base_set4 = -50; // S0.6, BF3 Base PWL Segment 4
1192 dscl_prog_data->easf_h_bf3_pwl_slope_set4 = 0x1200; // FP1.6.6, BF3 Slope PWL Segment 4
1193 dscl_prog_data->easf_h_bf3_pwl_in_set5 =
1194 0x0CF7; // FP0.6.6, BF3 Input value PWL Segment 5 (1.0 * 125^3)
1195 dscl_prog_data->easf_h_bf3_pwl_base_set5 = -63; // S0.6, BF3 Base PWL Segment 5
1196 } else {
1197 dscl_prog_data->easf_h_bf3_pwl_in_set0 = 0x000; // FP0.6.6, BF3 Input value PWL Segment 0
1198 dscl_prog_data->easf_h_bf3_pwl_base_set0 = 63; // S0.6, BF3 Base PWL Segment 0
1199 dscl_prog_data->easf_h_bf3_pwl_slope_set0 = 0x0000; // FP1.6.6, BF3 Slope PWL Segment 0
1200 dscl_prog_data->easf_h_bf3_pwl_in_set1 =
1201 0x06C0; // FP0.6.6, BF3 Input value PWL Segment 1 (0.0625)
1202 dscl_prog_data->easf_h_bf3_pwl_base_set1 = 63; // S0.6, BF3 Base PWL Segment 1
1203 dscl_prog_data->easf_h_bf3_pwl_slope_set1 = 0x1896; // FP1.6.6, BF3 Slope PWL Segment 1
1204 dscl_prog_data->easf_h_bf3_pwl_in_set2 =
1205 0x0700; // FP0.6.6, BF3 Input value PWL Segment 2 (0.125)
1206 dscl_prog_data->easf_h_bf3_pwl_base_set2 = 20; // S0.6, BF3 Base PWL Segment 2
1207 dscl_prog_data->easf_h_bf3_pwl_slope_set2 = 0x1810; // FP1.6.6, BF3 Slope PWL Segment 2
1208 dscl_prog_data->easf_h_bf3_pwl_in_set3 =
1209 0x0740; // FP0.6.6, BF3 Input value PWL Segment 3 (0.25)
1210 dscl_prog_data->easf_h_bf3_pwl_base_set3 = 0; // S0.6, BF3 Base PWL Segment 3
1211 dscl_prog_data->easf_h_bf3_pwl_slope_set3 = 0x1878; // FP1.6.6, BF3 Slope PWL Segment 3
1212 dscl_prog_data->easf_h_bf3_pwl_in_set4 =
1213 0x0761; // FP0.6.6, BF3 Input value PWL Segment 4 (0.375)
1214 dscl_prog_data->easf_h_bf3_pwl_base_set4 = -60; // S0.6, BF3 Base PWL Segment 4
1215 dscl_prog_data->easf_h_bf3_pwl_slope_set4 = 0x1760; // FP1.6.6, BF3 Slope PWL Segment 4
1216 dscl_prog_data->easf_h_bf3_pwl_in_set5 =
1217 0x0780; // FP0.6.6, BF3 Input value PWL Segment 5 (0.5)
1218 dscl_prog_data->easf_h_bf3_pwl_base_set5 = -63; // S0.6, BF3 Base PWL Segment 5
1219 } // if (lls_pref == LLS_PREF_YES)
1220 } else
1221 dscl_prog_data->easf_h_en = false;
1222
1223 if (lls_pref == LLS_PREF_YES) {
1224 dscl_prog_data->easf_ltonl_en = 1; // Linear input
1225 dscl_prog_data->easf_matrix_c0 =
1226 0x504E; // fp1.5.10, C0 coefficient (LN_BT2020: 0.2627 * (2^14)/125 = 34.43750000)
1227 dscl_prog_data->easf_matrix_c1 =
1228 0x558E; // fp1.5.10, C1 coefficient (LN_BT2020: 0.6780 * (2^14)/125 = 88.87500000)
1229 dscl_prog_data->easf_matrix_c2 =
1230 0x47C6; // fp1.5.10, C2 coefficient (LN_BT2020: 0.0593 * (2^14)/125 = 7.77343750)
1231 dscl_prog_data->easf_matrix_c3 =
1232 0x0; // fp1.5.10, C3 coefficient
1233 } else {
1234 dscl_prog_data->easf_ltonl_en = 0; // Non-Linear input
1235 dscl_prog_data->easf_matrix_c0 =
1236 0x3434; // fp1.5.10, C0 coefficient (LN_BT2020: 0.262695312500000)
1237 dscl_prog_data->easf_matrix_c1 =
1238 0x396D; // fp1.5.10, C1 coefficient (LN_BT2020: 0.678222656250000)
1239 dscl_prog_data->easf_matrix_c2 =
1240 0x2B97; // fp1.5.10, C2 coefficient (LN_BT2020: 0.059295654296875)
1241 dscl_prog_data->easf_matrix_c3 =
1242 0x0; // fp1.5.10, C3 coefficient
1243 }
1244}
1245/*Set isharp noise detection */
1246static void spl_set_isharp_noise_det_mode(struct dscl_prog_data *dscl_prog_data)
1247{
1248 // ISHARP_NOISEDET_MODE
1249 // 0: 3x5 as VxH
1250 // 1: 4x5 as VxH
1251 // 2:
1252 // 3: 5x5 as VxH
1253 if (dscl_prog_data->taps.v_taps == 6)
1254 dscl_prog_data->isharp_noise_det.mode = 3; // ISHARP_NOISEDET_MODE
1255 else if (dscl_prog_data->taps.h_taps == 4)
1256 dscl_prog_data->isharp_noise_det.mode = 1; // ISHARP_NOISEDET_MODE
1257 else if (dscl_prog_data->taps.h_taps == 3)
1258 dscl_prog_data->isharp_noise_det.mode = 0; // ISHARP_NOISEDET_MODE
1259};
1260/* Set Sharpener data */
1261static void spl_set_isharp_data(struct dscl_prog_data *dscl_prog_data,
1262 struct adaptive_sharpness adp_sharpness, bool enable_isharp,
1263 enum linear_light_scaling lls_pref, enum spl_pixel_format format,
1264 const struct spl_scaler_data *data)
1265{
1266 /* Turn off sharpener if not required */
1267 if (!enable_isharp) {
1268 dscl_prog_data->isharp_en = 0;
1269 return;
1270 }
1271
1272 dscl_prog_data->isharp_en = 1; // ISHARP_EN
1273 dscl_prog_data->isharp_noise_det.enable = 1; // ISHARP_NOISEDET_EN
1274 // Set ISHARP_NOISEDET_MODE if htaps = 6-tap
1275 if (dscl_prog_data->taps.h_taps == 6)
1276 spl_set_isharp_noise_det_mode(dscl_prog_data); // ISHARP_NOISEDET_MODE
1277 // Program noise detection threshold
1278 dscl_prog_data->isharp_noise_det.uthreshold = 24; // ISHARP_NOISEDET_UTHRE
1279 dscl_prog_data->isharp_noise_det.dthreshold = 4; // ISHARP_NOISEDET_DTHRE
1280 // Program noise detection gain
1281 dscl_prog_data->isharp_noise_det.pwl_start_in = 3; // ISHARP_NOISEDET_PWL_START_IN
1282 dscl_prog_data->isharp_noise_det.pwl_end_in = 13; // ISHARP_NOISEDET_PWL_END_IN
1283 dscl_prog_data->isharp_noise_det.pwl_slope = 1623; // ISHARP_NOISEDET_PWL_SLOPE
1284
1285 if ((lls_pref == LLS_PREF_NO) && !spl_is_yuv420(format)) /* ISHARP_FMT_MODE */
1286 dscl_prog_data->isharp_fmt.mode = 1;
1287 else
1288 dscl_prog_data->isharp_fmt.mode = 0;
1289
1290 dscl_prog_data->isharp_fmt.norm = 0x3C00; // ISHARP_FMT_NORM
1291 dscl_prog_data->isharp_lba.mode = 0; // ISHARP_LBA_MODE
1292 // ISHARP_LBA_PWL_SEG0: ISHARP Local Brightness Adjustment PWL Segment 0
1293 dscl_prog_data->isharp_lba.in_seg[0] = 0; // ISHARP LBA PWL for Seg 0. INPUT value in U0.10 format
1294 dscl_prog_data->isharp_lba.base_seg[0] = 0; // ISHARP LBA PWL for Seg 0. BASE value in U0.6 format
1295 dscl_prog_data->isharp_lba.slope_seg[0] = 32; // ISHARP LBA for Seg 0. SLOPE value in S5.3 format
1296 // ISHARP_LBA_PWL_SEG1: ISHARP LBA PWL Segment 1
1297 dscl_prog_data->isharp_lba.in_seg[1] = 256; // ISHARP LBA PWL for Seg 1. INPUT value in U0.10 format
1298 dscl_prog_data->isharp_lba.base_seg[1] = 63; // ISHARP LBA PWL for Seg 1. BASE value in U0.6 format
1299 dscl_prog_data->isharp_lba.slope_seg[1] = 0; // ISHARP LBA for Seg 1. SLOPE value in S5.3 format
1300 // ISHARP_LBA_PWL_SEG2: ISHARP LBA PWL Segment 2
1301 dscl_prog_data->isharp_lba.in_seg[2] = 614; // ISHARP LBA PWL for Seg 2. INPUT value in U0.10 format
1302 dscl_prog_data->isharp_lba.base_seg[2] = 63; // ISHARP LBA PWL for Seg 2. BASE value in U0.6 format
1303 dscl_prog_data->isharp_lba.slope_seg[2] = -20; // ISHARP LBA for Seg 2. SLOPE value in S5.3 format
1304 // ISHARP_LBA_PWL_SEG3: ISHARP LBA PWL Segment 3
1305 dscl_prog_data->isharp_lba.in_seg[3] = 1023; // ISHARP LBA PWL for Seg 3.INPUT value in U0.10 format
1306 dscl_prog_data->isharp_lba.base_seg[3] = 0; // ISHARP LBA PWL for Seg 3. BASE value in U0.6 format
1307 dscl_prog_data->isharp_lba.slope_seg[3] = 0; // ISHARP LBA for Seg 3. SLOPE value in S5.3 format
1308 // ISHARP_LBA_PWL_SEG4: ISHARP LBA PWL Segment 4
1309 dscl_prog_data->isharp_lba.in_seg[4] = 1023; // ISHARP LBA PWL for Seg 4.INPUT value in U0.10 format
1310 dscl_prog_data->isharp_lba.base_seg[4] = 0; // ISHARP LBA PWL for Seg 4. BASE value in U0.6 format
1311 dscl_prog_data->isharp_lba.slope_seg[4] = 0; // ISHARP LBA for Seg 4. SLOPE value in S5.3 format
1312 // ISHARP_LBA_PWL_SEG5: ISHARP LBA PWL Segment 5
1313 dscl_prog_data->isharp_lba.in_seg[5] = 1023; // ISHARP LBA PWL for Seg 5.INPUT value in U0.10 format
1314 dscl_prog_data->isharp_lba.base_seg[5] = 0; // ISHARP LBA PWL for Seg 5. BASE value in U0.6 format
1315 switch (adp_sharpness.sharpness) {
1316 case SHARPNESS_LOW:
1317 dscl_prog_data->isharp_delta = spl_get_filter_isharp_1D_lut_0p5x();
1318 break;
1319 case SHARPNESS_MID:
1320 dscl_prog_data->isharp_delta = spl_get_filter_isharp_1D_lut_1p0x();
1321 break;
1322 case SHARPNESS_HIGH:
1323 dscl_prog_data->isharp_delta = spl_get_filter_isharp_1D_lut_2p0x();
1324 break;
1325 default:
1326 BREAK_TO_DEBUGGER();
1327 }
1328
1329 // Program the nldelta soft clip values
1330 if (lls_pref == LLS_PREF_YES) {
1331 dscl_prog_data->isharp_nldelta_sclip.enable_p = 0; /* ISHARP_NLDELTA_SCLIP_EN_P */
1332 dscl_prog_data->isharp_nldelta_sclip.pivot_p = 0; /* ISHARP_NLDELTA_SCLIP_PIVOT_P */
1333 dscl_prog_data->isharp_nldelta_sclip.slope_p = 0; /* ISHARP_NLDELTA_SCLIP_SLOPE_P */
1334 dscl_prog_data->isharp_nldelta_sclip.enable_n = 1; /* ISHARP_NLDELTA_SCLIP_EN_N */
1335 dscl_prog_data->isharp_nldelta_sclip.pivot_n = 71; /* ISHARP_NLDELTA_SCLIP_PIVOT_N */
1336 dscl_prog_data->isharp_nldelta_sclip.slope_n = 16; /* ISHARP_NLDELTA_SCLIP_SLOPE_N */
1337 } else {
1338 dscl_prog_data->isharp_nldelta_sclip.enable_p = 1; /* ISHARP_NLDELTA_SCLIP_EN_P */
1339 dscl_prog_data->isharp_nldelta_sclip.pivot_p = 70; /* ISHARP_NLDELTA_SCLIP_PIVOT_P */
1340 dscl_prog_data->isharp_nldelta_sclip.slope_p = 24; /* ISHARP_NLDELTA_SCLIP_SLOPE_P */
1341 dscl_prog_data->isharp_nldelta_sclip.enable_n = 1; /* ISHARP_NLDELTA_SCLIP_EN_N */
1342 dscl_prog_data->isharp_nldelta_sclip.pivot_n = 70; /* ISHARP_NLDELTA_SCLIP_PIVOT_N */
1343 dscl_prog_data->isharp_nldelta_sclip.slope_n = 24; /* ISHARP_NLDELTA_SCLIP_SLOPE_N */
1344 }
1345
1346 // Set the values as per lookup table
1347 spl_set_blur_scale_data(dscl_prog_data, data);
1348}
1349static bool spl_get_isharp_en(struct adaptive_sharpness adp_sharpness,
1350 int vscale_ratio, int hscale_ratio, struct spl_taps taps,
1351 enum spl_pixel_format format)
1352{
1353 bool enable_isharp = false;
1354
1355 if (adp_sharpness.enable == false)
1356 return enable_isharp; // Return if adaptive sharpness is disabled
1357 // Is downscaling ?
1358 if (vscale_ratio > 1 || hscale_ratio > 1) {
1359 // END - No iSHARP support for downscaling
1360 return enable_isharp;
1361 }
1362 // Scaling is up to 1:1 (no scaling) or upscaling
1363
1364 /* Only apply sharpness to NV12 and not P010 */
1365 if (format != SPL_PIXEL_FORMAT_420BPP8)
1366 return enable_isharp;
1367
1368 // LB support horizontal taps 4,6 or vertical taps 3, 4, 6
1369 if (taps.h_taps == 4 || taps.h_taps == 6 ||
1370 taps.v_taps == 3 || taps.v_taps == 4 || taps.v_taps == 6) {
1371 // END - iSHARP supported
1372 enable_isharp = true;
1373 }
1374 return enable_isharp;
1375}
1376
1377static bool spl_choose_lls_policy(enum spl_pixel_format format,
1378 enum spl_transfer_func_type tf_type,
1379 enum spl_transfer_func_predefined tf_predefined_type,
1380 enum linear_light_scaling *lls_pref)
1381{
1382 if (spl_is_yuv420(format)) {
1383 *lls_pref = LLS_PREF_NO;
1384 if ((tf_type == SPL_TF_TYPE_PREDEFINED) || (tf_type == SPL_TF_TYPE_DISTRIBUTED_POINTS))
1385 return true;
1386 } else { /* RGB or YUV444 */
1387 if (tf_type == SPL_TF_TYPE_PREDEFINED) {
1388 if ((tf_predefined_type == SPL_TRANSFER_FUNCTION_HLG) ||
1389 (tf_predefined_type == SPL_TRANSFER_FUNCTION_HLG12))
1390 *lls_pref = LLS_PREF_NO;
1391 else
1392 *lls_pref = LLS_PREF_YES;
1393 return true;
1394 } else if (tf_type == SPL_TF_TYPE_BYPASS) {
1395 *lls_pref = LLS_PREF_YES;
1396 return true;
1397 }
1398 }
1399 *lls_pref = LLS_PREF_NO;
1400 return false;
1401}
1402
1403/* Calculate scaler parameters */
1404bool spl_calculate_scaler_params(struct spl_in *spl_in, struct spl_out *spl_out)
1405{
1406 bool res = false;
1407 bool enable_easf_v = false;
1408 bool enable_easf_h = false;
1409 bool lls_enable_easf = true;
1410 const struct spl_scaler_data *data = &spl_out->scl_data;
1411 // All SPL calls
1412 /* recout calculation */
1413 /* depends on h_active */
1414 spl_calculate_recout(spl_in, spl_out);
1415 /* depends on pixel format */
1416 spl_calculate_scaling_ratios(spl_in, spl_out);
1417 /* depends on scaling ratios and recout, does not calculate offset yet */
1418 spl_calculate_viewport_size(spl_in, spl_out);
1419
1420 res = spl_get_optimal_number_of_taps(
1421 spl_in->basic_out.max_downscale_src_width, spl_in,
1422 spl_out, &spl_in->scaling_quality);
1423 /*
1424 * Depends on recout, scaling ratios, h_active and taps
1425 * May need to re-check lb size after this in some obscure scenario
1426 */
1427 if (res)
1428 spl_calculate_inits_and_viewports(spl_in, spl_out);
1429 // Handle 3d recout
1430 spl_handle_3d_recout(spl_in, &spl_out->scl_data.recout);
1431 // Clamp
1432 spl_clamp_viewport(&spl_out->scl_data.viewport);
1433
1434 if (!res)
1435 return res;
1436
1437 /*
1438 * If lls_pref is LLS_PREF_DONT_CARE, then use pixel format and transfer
1439 * function to determine whether to use LINEAR or NONLINEAR scaling
1440 */
1441 if (spl_in->lls_pref == LLS_PREF_DONT_CARE)
1442 lls_enable_easf = spl_choose_lls_policy(spl_in->basic_in.format,
1443 spl_in->basic_in.tf_type, spl_in->basic_in.tf_predefined_type,
1444 &spl_in->lls_pref);
1445
1446 // Save all calculated parameters in dscl_prog_data structure to program hw registers
1447 spl_set_dscl_prog_data(spl_in, spl_out);
1448
1449 int vratio = dc_fixpt_ceil(spl_out->scl_data.ratios.vert);
1450 int hratio = dc_fixpt_ceil(spl_out->scl_data.ratios.horz);
1451 if (!lls_enable_easf || spl_in->disable_easf) {
1452 enable_easf_v = false;
1453 enable_easf_h = false;
1454 } else {
1455 /* Enable EASF on vertical? */
1456 enable_easf_v = enable_easf(vratio, spl_out->scl_data.taps.v_taps, spl_in->lls_pref, spl_in->prefer_easf);
1457 /* Enable EASF on horizontal? */
1458 enable_easf_h = enable_easf(hratio, spl_out->scl_data.taps.h_taps, spl_in->lls_pref, spl_in->prefer_easf);
1459 }
1460 // Set EASF
1461 spl_set_easf_data(spl_out->dscl_prog_data, enable_easf_v, enable_easf_h, spl_in->lls_pref,
1462 spl_in->basic_in.format);
1463 // Set iSHARP
1464 bool enable_isharp = spl_get_isharp_en(spl_in->adaptive_sharpness, vratio, hratio,
1465 spl_out->scl_data.taps, spl_in->basic_in.format);
1466 spl_set_isharp_data(spl_out->dscl_prog_data, spl_in->adaptive_sharpness, enable_isharp,
1467 spl_in->lls_pref, spl_in->basic_in.format, data);
1468
1469 return res;
1470}