xref: /linux/drivers/gpu/drm/amd/display/dc/spl/dc_spl.c (revision 429508c84d95811dd1300181dfe84743caff9a38)
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 
12 static 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 
32 static 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 
42 static 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 * 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) * 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 * 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) * 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 
134 static 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 
166 static 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 
200 static 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 */
356 static 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 */
404 static 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 }
420 static 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  */
447 static 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 
510 static 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 */
520 static 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 }
642 static 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 
660 static 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 }
668 static 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 }
676 static 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 }
684 static 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 */
711 static 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 }
834 static 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 
848 static 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 
885 static 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 }
893 static 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 }
916 static 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 
929 static 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 }
938 static 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 */
948 static 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 ?*/
981 static 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 */
1004 static 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 */
1246 static 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 */
1261 static 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 }
1349 static 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 
1377 static 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 */
1404 bool 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 }
1471