1 // SPDX-License-Identifier: MIT 2 // 3 // Copyright 2024 Advanced Micro Devices, Inc. 4 5 #include "dc_spl.h" 6 #include "dc_spl_scl_easf_filters.h" 7 #include "dc_spl_isharp_filters.h" 8 #include "spl_debug.h" 9 10 #define IDENTITY_RATIO(ratio) (SPL_NAMESPACE(spl_fixpt_u3d19(ratio)) == (1 << 19)) 11 #define MIN_VIEWPORT_SIZE 12 12 13 static bool spl_is_yuv420(enum spl_pixel_format format) 14 { 15 if ((format >= SPL_PIXEL_FORMAT_420BPP8) && 16 (format <= SPL_PIXEL_FORMAT_420BPP10)) 17 return true; 18 19 return false; 20 } 21 22 static bool spl_is_rgb8(enum spl_pixel_format format) 23 { 24 if (format == SPL_PIXEL_FORMAT_ARGB8888) 25 return true; 26 27 return false; 28 } 29 30 static bool spl_is_video_format(enum spl_pixel_format format) 31 { 32 if (format >= SPL_PIXEL_FORMAT_VIDEO_BEGIN 33 && format <= SPL_PIXEL_FORMAT_VIDEO_END) 34 return true; 35 else 36 return false; 37 } 38 39 static bool spl_is_subsampled_format(enum spl_pixel_format format) 40 { 41 if (format >= SPL_PIXEL_FORMAT_SUBSAMPLED_BEGIN 42 && format <= SPL_PIXEL_FORMAT_SUBSAMPLED_END) 43 return true; 44 else 45 return false; 46 } 47 48 static struct spl_rect intersect_rec(const struct spl_rect *r0, const struct spl_rect *r1) 49 { 50 struct spl_rect rec; 51 int r0_x_end = r0->x + r0->width; 52 int r1_x_end = r1->x + r1->width; 53 int r0_y_end = r0->y + r0->height; 54 int r1_y_end = r1->y + r1->height; 55 56 rec.x = r0->x > r1->x ? r0->x : r1->x; 57 rec.width = r0_x_end > r1_x_end ? r1_x_end - rec.x : r0_x_end - rec.x; 58 rec.y = r0->y > r1->y ? r0->y : r1->y; 59 rec.height = r0_y_end > r1_y_end ? r1_y_end - rec.y : r0_y_end - rec.y; 60 61 /* in case that there is no intersection */ 62 if (rec.width < 0 || rec.height < 0) 63 memset(&rec, 0, sizeof(rec)); 64 65 return rec; 66 } 67 68 static struct spl_rect shift_rec(const struct spl_rect *rec_in, int x, int y) 69 { 70 struct spl_rect rec_out = *rec_in; 71 72 rec_out.x += x; 73 rec_out.y += y; 74 75 return rec_out; 76 } 77 78 static void spl_opp_adjust_rect(struct spl_rect *rec, const struct spl_opp_adjust *adjust) 79 { 80 if ((rec->x + adjust->x) >= 0) 81 rec->x += adjust->x; 82 83 if ((rec->y + adjust->y) >= 0) 84 rec->y += adjust->y; 85 86 if ((rec->width + adjust->width) >= 1) 87 rec->width += adjust->width; 88 89 if ((rec->height + adjust->height) >= 1) 90 rec->height += adjust->height; 91 } 92 93 static struct spl_rect calculate_plane_rec_in_timing_active( 94 struct spl_in *spl_in, 95 const struct spl_rect *rec_in) 96 { 97 /* 98 * The following diagram shows an example where we map a 1920x1200 99 * desktop to a 2560x1440 timing with a plane rect in the middle 100 * of the screen. To map a plane rect from Stream Source to Timing 101 * Active space, we first multiply stream scaling ratios (i.e 2304/1920 102 * horizontal and 1440/1200 vertical) to the plane's x and y, then 103 * we add stream destination offsets (i.e 128 horizontal, 0 vertical). 104 * This will give us a plane rect's position in Timing Active. However 105 * we have to remove the fractional. The rule is that we find left/right 106 * and top/bottom positions and round the value to the adjacent integer. 107 * 108 * Stream Source Space 109 * ------------ 110 * __________________________________________________ 111 * |Stream Source (1920 x 1200) ^ | 112 * | y | 113 * | <------- w --------|> | 114 * | __________________V | 115 * |<-- x -->|Plane//////////////| ^ | 116 * | |(pre scale)////////| | | 117 * | |///////////////////| | | 118 * | |///////////////////| h | 119 * | |///////////////////| | | 120 * | |///////////////////| | | 121 * | |///////////////////| V | 122 * | | 123 * | | 124 * |__________________________________________________| 125 * 126 * 127 * Timing Active Space 128 * --------------------------------- 129 * 130 * Timing Active (2560 x 1440) 131 * __________________________________________________ 132 * |*****| Stteam Destination (2304 x 1440) |*****| 133 * |*****| |*****| 134 * |<128>| |*****| 135 * |*****| __________________ |*****| 136 * |*****| |Plane/////////////| |*****| 137 * |*****| |(post scale)//////| |*****| 138 * |*****| |//////////////////| |*****| 139 * |*****| |//////////////////| |*****| 140 * |*****| |//////////////////| |*****| 141 * |*****| |//////////////////| |*****| 142 * |*****| |*****| 143 * |*****| |*****| 144 * |*****| |*****| 145 * |*****|______________________________________|*****| 146 * 147 * So the resulting formulas are shown below: 148 * 149 * recout_x = 128 + round(plane_x * 2304 / 1920) 150 * recout_w = 128 + round((plane_x + plane_w) * 2304 / 1920) - recout_x 151 * recout_y = 0 + round(plane_y * 1440 / 1200) 152 * recout_h = 0 + round((plane_y + plane_h) * 1440 / 1200) - recout_y 153 * 154 * NOTE: fixed point division is not error free. To reduce errors 155 * introduced by fixed point division, we divide only after 156 * multiplication is complete. 157 */ 158 const struct spl_rect *stream_src = &spl_in->basic_out.src_rect; 159 const struct spl_rect *stream_dst = &spl_in->basic_out.dst_rect; 160 struct spl_rect rec_out = {0}; 161 struct spl_fixed31_32 temp; 162 163 164 temp = SPL_NAMESPACE(spl_fixpt_from_fraction( 165 rec_in->x * (long long)stream_dst->width, 166 stream_src->width)); 167 rec_out.x = stream_dst->x + spl_fixpt_round(temp); 168 169 temp = SPL_NAMESPACE(spl_fixpt_from_fraction( 170 (rec_in->x + rec_in->width) * (long long)stream_dst->width, 171 stream_src->width)); 172 rec_out.width = stream_dst->x + spl_fixpt_round(temp) - rec_out.x; 173 174 temp = SPL_NAMESPACE(spl_fixpt_from_fraction( 175 rec_in->y * (long long)stream_dst->height, 176 stream_src->height)); 177 rec_out.y = stream_dst->y + spl_fixpt_round(temp); 178 179 temp = SPL_NAMESPACE(spl_fixpt_from_fraction( 180 (rec_in->y + rec_in->height) * (long long)stream_dst->height, 181 stream_src->height)); 182 rec_out.height = stream_dst->y + spl_fixpt_round(temp) - rec_out.y; 183 184 return rec_out; 185 } 186 187 static struct spl_rect calculate_mpc_slice_in_timing_active( 188 struct spl_in *spl_in, 189 struct spl_rect *plane_clip_rec) 190 { 191 bool use_recout_width_aligned = 192 spl_in->basic_in.num_h_slices_recout_width_align.use_recout_width_aligned; 193 int mpc_slice_count = 194 spl_in->basic_in.num_h_slices_recout_width_align.num_slices_recout_width.mpc_num_h_slices; 195 int recout_width_align = 196 spl_in->basic_in.num_h_slices_recout_width_align.num_slices_recout_width.mpc_recout_width_align; 197 int mpc_slice_idx = spl_in->basic_in.mpc_h_slice_index; 198 int epimo = mpc_slice_count - plane_clip_rec->width % mpc_slice_count - 1; 199 struct spl_rect mpc_rec; 200 201 if (spl_in->basic_in.custom_width != 0) { 202 mpc_rec.width = spl_in->basic_in.custom_width; 203 mpc_rec.x = spl_in->basic_in.custom_x; 204 mpc_rec.height = plane_clip_rec->height; 205 mpc_rec.y = plane_clip_rec->y; 206 } else if (use_recout_width_aligned) { 207 mpc_rec.width = recout_width_align; 208 if ((mpc_rec.width * (mpc_slice_idx + 1)) > plane_clip_rec->width) { 209 mpc_rec.width = plane_clip_rec->width % recout_width_align; 210 mpc_rec.x = plane_clip_rec->x + recout_width_align * mpc_slice_idx; 211 } else 212 mpc_rec.x = plane_clip_rec->x + mpc_rec.width * mpc_slice_idx; 213 mpc_rec.height = plane_clip_rec->height; 214 mpc_rec.y = plane_clip_rec->y; 215 216 } else { 217 mpc_rec.width = plane_clip_rec->width / mpc_slice_count; 218 mpc_rec.x = plane_clip_rec->x + mpc_rec.width * mpc_slice_idx; 219 mpc_rec.height = plane_clip_rec->height; 220 mpc_rec.y = plane_clip_rec->y; 221 } 222 SPL_ASSERT(mpc_slice_count == 1 || 223 spl_in->basic_out.view_format != SPL_VIEW_3D_SIDE_BY_SIDE || 224 mpc_rec.width % 2 == 0); 225 226 /* extra pixels in the division remainder need to go to pipes after 227 * the extra pixel index minus one(epimo) defined here as: 228 */ 229 if ((use_recout_width_aligned == false) && 230 mpc_slice_idx > epimo && spl_in->basic_in.custom_width == 0) { 231 mpc_rec.x += mpc_slice_idx - epimo - 1; 232 mpc_rec.width += 1; 233 } 234 235 if (spl_in->basic_out.view_format == SPL_VIEW_3D_TOP_AND_BOTTOM) { 236 SPL_ASSERT(mpc_rec.height % 2 == 0); 237 mpc_rec.height /= 2; 238 } 239 return mpc_rec; 240 } 241 242 static struct spl_rect calculate_odm_slice_in_timing_active(struct spl_in *spl_in) 243 { 244 int odm_slice_count = spl_in->basic_out.odm_combine_factor; 245 int odm_slice_idx = spl_in->odm_slice_index; 246 bool is_last_odm_slice = (odm_slice_idx + 1) == odm_slice_count; 247 int h_active = spl_in->basic_out.output_size.width; 248 int v_active = spl_in->basic_out.output_size.height; 249 int odm_slice_width; 250 struct spl_rect odm_rec; 251 252 if (spl_in->basic_out.odm_combine_factor > 0) { 253 odm_slice_width = h_active / odm_slice_count; 254 /* 255 * deprecated, caller must pass in odm slice rect i.e OPP input 256 * rect in timing active for the new interface. 257 */ 258 if (spl_in->basic_out.use_two_pixels_per_container && (odm_slice_width % 2)) 259 odm_slice_width++; 260 261 odm_rec.x = odm_slice_width * odm_slice_idx; 262 odm_rec.width = is_last_odm_slice ? 263 /* last slice width is the reminder of h_active */ 264 h_active - odm_slice_width * (odm_slice_count - 1) : 265 /* odm slice width is the floor of h_active / count */ 266 odm_slice_width; 267 odm_rec.y = 0; 268 odm_rec.height = v_active; 269 270 return odm_rec; 271 } 272 273 return spl_in->basic_out.odm_slice_rect; 274 } 275 276 static void spl_calculate_recout(struct spl_in *spl_in, struct spl_scratch *spl_scratch, struct spl_out *spl_out) 277 { 278 /* 279 * A plane clip represents the desired plane size and position in Stream 280 * Source Space. Stream Source is the destination where all planes are 281 * blended (i.e. positioned, scaled and overlaid). It is a canvas where 282 * all planes associated with the current stream are drawn together. 283 * After Stream Source is completed, we will further scale and 284 * reposition the entire canvas of the stream source to Stream 285 * Destination in Timing Active Space. This could be due to display 286 * overscan adjustment where we will need to rescale and reposition all 287 * the planes so they can fit into a TV with overscan or downscale 288 * upscale features such as GPU scaling or VSR. 289 * 290 * This two step blending is a virtual procedure in software. In 291 * hardware there is no such thing as Stream Source. all planes are 292 * blended once in Timing Active Space. Software virtualizes a Stream 293 * Source space to decouple the math complicity so scaling param 294 * calculation focuses on one step at a time. 295 * 296 * In the following two diagrams, user applied 10% overscan adjustment 297 * so the Stream Source needs to be scaled down a little before mapping 298 * to Timing Active Space. As a result the Plane Clip is also scaled 299 * down by the same ratio, Plane Clip position (i.e. x and y) with 300 * respect to Stream Source is also scaled down. To map it in Timing 301 * Active Space additional x and y offsets from Stream Destination are 302 * added to Plane Clip as well. 303 * 304 * Stream Source Space 305 * ------------ 306 * __________________________________________________ 307 * |Stream Source (3840 x 2160) ^ | 308 * | y | 309 * | | | 310 * | __________________V | 311 * |<-- x -->|Plane Clip/////////| | 312 * | |(pre scale)////////| | 313 * | |///////////////////| | 314 * | |///////////////////| | 315 * | |///////////////////| | 316 * | |///////////////////| | 317 * | |///////////////////| | 318 * | | 319 * | | 320 * |__________________________________________________| 321 * 322 * 323 * Timing Active Space (3840 x 2160) 324 * --------------------------------- 325 * 326 * Timing Active 327 * __________________________________________________ 328 * | y_____________________________________________ | 329 * |x |Stream Destination (3456 x 1944) | | 330 * | | | | 331 * | | __________________ | | 332 * | | |Plane Clip////////| | | 333 * | | |(post scale)//////| | | 334 * | | |//////////////////| | | 335 * | | |//////////////////| | | 336 * | | |//////////////////| | | 337 * | | |//////////////////| | | 338 * | | | | 339 * | | | | 340 * | |____________________________________________| | 341 * |__________________________________________________| 342 * 343 * 344 * In Timing Active Space a plane clip could be further sliced into 345 * pieces called MPC slices. Each Pipe Context is responsible for 346 * processing only one MPC slice so the plane processing workload can be 347 * distributed to multiple DPP Pipes. MPC slices could be blended 348 * together to a single ODM slice. Each ODM slice is responsible for 349 * processing a portion of Timing Active divided horizontally so the 350 * output pixel processing workload can be distributed to multiple OPP 351 * pipes. All ODM slices are mapped together in ODM block so all MPC 352 * slices belong to different ODM slices could be pieced together to 353 * form a single image in Timing Active. MPC slices must belong to 354 * single ODM slice. If an MPC slice goes across ODM slice boundary, it 355 * needs to be divided into two MPC slices one for each ODM slice. 356 * 357 * In the following diagram the output pixel processing workload is 358 * divided horizontally into two ODM slices one for each OPP blend tree. 359 * OPP0 blend tree is responsible for processing left half of Timing 360 * Active, while OPP2 blend tree is responsible for processing right 361 * half. 362 * 363 * The plane has two MPC slices. However since the right MPC slice goes 364 * across ODM boundary, two DPP pipes are needed one for each OPP blend 365 * tree. (i.e. DPP1 for OPP0 blend tree and DPP2 for OPP2 blend tree). 366 * 367 * Assuming that we have a Pipe Context associated with OPP0 and DPP1 368 * working on processing the plane in the diagram. We want to know the 369 * width and height of the shaded rectangle and its relative position 370 * with respect to the ODM slice0. This is called the recout of the pipe 371 * context. 372 * 373 * Planes can be at arbitrary size and position and there could be an 374 * arbitrary number of MPC and ODM slices. The algorithm needs to take 375 * all scenarios into account. 376 * 377 * Timing Active Space (3840 x 2160) 378 * --------------------------------- 379 * 380 * Timing Active 381 * __________________________________________________ 382 * |OPP0(ODM slice0)^ |OPP2(ODM slice1) | 383 * | y | | 384 * | | <- w -> | 385 * | _____V________|____ | 386 * | |DPP0 ^ |DPP1 |DPP2| | 387 * |<------ x |-----|->|/////| | | 388 * | | | |/////| | | 389 * | | h |/////| | | 390 * | | | |/////| | | 391 * | |_____V__|/////|____| | 392 * | | | 393 * | | | 394 * | | | 395 * |_________________________|________________________| 396 * 397 * 398 */ 399 struct spl_rect plane_clip; 400 struct spl_rect mpc_slice_of_plane_clip; 401 struct spl_rect odm_slice; 402 struct spl_rect overlapping_area; 403 404 plane_clip = calculate_plane_rec_in_timing_active(spl_in, 405 &spl_in->basic_in.clip_rect); 406 /* guard plane clip from drawing beyond stream dst here */ 407 plane_clip = intersect_rec(&plane_clip, 408 &spl_in->basic_out.dst_rect); 409 mpc_slice_of_plane_clip = calculate_mpc_slice_in_timing_active( 410 spl_in, &plane_clip); 411 odm_slice = calculate_odm_slice_in_timing_active(spl_in); 412 overlapping_area = intersect_rec(&mpc_slice_of_plane_clip, &odm_slice); 413 414 if (overlapping_area.height > 0 && 415 overlapping_area.width > 0) { 416 /* shift the overlapping area so it is with respect to current 417 * ODM slice's position 418 */ 419 spl_scratch->scl_data.recout = shift_rec( 420 &overlapping_area, 421 -odm_slice.x, -odm_slice.y); 422 spl_scratch->scl_data.recout.height -= 423 spl_in->debug.visual_confirm_base_offset; 424 spl_scratch->scl_data.recout.height -= 425 spl_in->debug.visual_confirm_dpp_offset; 426 } else 427 /* if there is no overlap, zero recout */ 428 memset(&spl_scratch->scl_data.recout, 0, 429 sizeof(struct spl_rect)); 430 } 431 432 /* Calculate scaling ratios */ 433 static void spl_calculate_scaling_ratios(struct spl_in *spl_in, 434 struct spl_scratch *spl_scratch, 435 struct spl_out *spl_out) 436 { 437 const int in_w = spl_in->basic_out.src_rect.width; 438 const int in_h = spl_in->basic_out.src_rect.height; 439 const int out_w = spl_in->basic_out.dst_rect.width; 440 const int out_h = spl_in->basic_out.dst_rect.height; 441 struct spl_rect surf_src = spl_in->basic_in.src_rect; 442 443 /*Swap surf_src height and width since scaling ratios are in recout rotation*/ 444 if (spl_in->basic_in.rotation == SPL_ROTATION_ANGLE_90 || 445 spl_in->basic_in.rotation == SPL_ROTATION_ANGLE_270) 446 spl_swap(surf_src.height, surf_src.width); 447 448 spl_scratch->scl_data.ratios.horz = SPL_NAMESPACE(spl_fixpt_from_fraction( 449 surf_src.width, 450 spl_in->basic_in.dst_rect.width)); 451 spl_scratch->scl_data.ratios.vert = SPL_NAMESPACE(spl_fixpt_from_fraction( 452 surf_src.height, 453 spl_in->basic_in.dst_rect.height)); 454 455 if (spl_in->basic_out.view_format == SPL_VIEW_3D_SIDE_BY_SIDE) 456 spl_scratch->scl_data.ratios.horz.value *= 2; 457 else if (spl_in->basic_out.view_format == SPL_VIEW_3D_TOP_AND_BOTTOM) 458 spl_scratch->scl_data.ratios.vert.value *= 2; 459 460 spl_scratch->scl_data.ratios.vert.value = spl_div64_s64( 461 spl_scratch->scl_data.ratios.vert.value * in_h, out_h); 462 spl_scratch->scl_data.ratios.horz.value = spl_div64_s64( 463 spl_scratch->scl_data.ratios.horz.value * in_w, out_w); 464 465 spl_scratch->scl_data.ratios.horz_c = spl_scratch->scl_data.ratios.horz; 466 spl_scratch->scl_data.ratios.vert_c = spl_scratch->scl_data.ratios.vert; 467 468 if (spl_is_yuv420(spl_in->basic_in.format)) { 469 spl_scratch->scl_data.ratios.horz_c.value /= 2; 470 spl_scratch->scl_data.ratios.vert_c.value /= 2; 471 } 472 spl_scratch->scl_data.ratios.horz = spl_fixpt_truncate( 473 spl_scratch->scl_data.ratios.horz, 19); 474 spl_scratch->scl_data.ratios.vert = spl_fixpt_truncate( 475 spl_scratch->scl_data.ratios.vert, 19); 476 spl_scratch->scl_data.ratios.horz_c = spl_fixpt_truncate( 477 spl_scratch->scl_data.ratios.horz_c, 19); 478 spl_scratch->scl_data.ratios.vert_c = spl_fixpt_truncate( 479 spl_scratch->scl_data.ratios.vert_c, 19); 480 481 /* 482 * Coefficient table and some registers are different based on ratio 483 * that is output/input. Currently we calculate input/output 484 * Store 1/ratio in recip_ratio for those lookups 485 */ 486 spl_scratch->scl_data.recip_ratios.horz = SPL_NAMESPACE(spl_fixpt_recip( 487 spl_scratch->scl_data.ratios.horz)); 488 spl_scratch->scl_data.recip_ratios.vert = SPL_NAMESPACE(spl_fixpt_recip( 489 spl_scratch->scl_data.ratios.vert)); 490 spl_scratch->scl_data.recip_ratios.horz_c = SPL_NAMESPACE(spl_fixpt_recip( 491 spl_scratch->scl_data.ratios.horz_c)); 492 spl_scratch->scl_data.recip_ratios.vert_c = SPL_NAMESPACE(spl_fixpt_recip( 493 spl_scratch->scl_data.ratios.vert_c)); 494 } 495 496 /* Calculate Viewport size */ 497 static void spl_calculate_viewport_size(struct spl_in *spl_in, struct spl_scratch *spl_scratch) 498 { 499 spl_scratch->scl_data.viewport.width = spl_fixpt_ceil(spl_fixpt_mul_int(spl_scratch->scl_data.ratios.horz, 500 spl_scratch->scl_data.recout.width)); 501 spl_scratch->scl_data.viewport.height = spl_fixpt_ceil(spl_fixpt_mul_int(spl_scratch->scl_data.ratios.vert, 502 spl_scratch->scl_data.recout.height)); 503 spl_scratch->scl_data.viewport_c.width = spl_fixpt_ceil(spl_fixpt_mul_int(spl_scratch->scl_data.ratios.horz_c, 504 spl_scratch->scl_data.recout.width)); 505 spl_scratch->scl_data.viewport_c.height = spl_fixpt_ceil(spl_fixpt_mul_int(spl_scratch->scl_data.ratios.vert_c, 506 spl_scratch->scl_data.recout.height)); 507 if (spl_in->basic_in.rotation == SPL_ROTATION_ANGLE_90 || 508 spl_in->basic_in.rotation == SPL_ROTATION_ANGLE_270) { 509 spl_swap(spl_scratch->scl_data.viewport.width, spl_scratch->scl_data.viewport.height); 510 spl_swap(spl_scratch->scl_data.viewport_c.width, spl_scratch->scl_data.viewport_c.height); 511 } 512 } 513 514 static void spl_get_vp_scan_direction(enum spl_rotation_angle rotation, 515 bool horizontal_mirror, 516 bool *orthogonal_rotation, 517 bool *flip_vert_scan_dir, 518 bool *flip_horz_scan_dir) 519 { 520 *orthogonal_rotation = false; 521 *flip_vert_scan_dir = false; 522 *flip_horz_scan_dir = false; 523 if (rotation == SPL_ROTATION_ANGLE_180) { 524 *flip_vert_scan_dir = true; 525 *flip_horz_scan_dir = true; 526 } else if (rotation == SPL_ROTATION_ANGLE_90) { 527 *orthogonal_rotation = true; 528 *flip_horz_scan_dir = true; 529 } else if (rotation == SPL_ROTATION_ANGLE_270) { 530 *orthogonal_rotation = true; 531 *flip_vert_scan_dir = true; 532 } 533 534 if (horizontal_mirror) 535 *flip_horz_scan_dir = !*flip_horz_scan_dir; 536 } 537 538 /* 539 * We completely calculate vp offset, size and inits here based entirely on scaling 540 * ratios and recout for pixel perfect pipe combine. 541 */ 542 static void spl_calculate_init_and_vp(bool flip_scan_dir, 543 int recout_offset_within_recout_full, 544 int recout_size, 545 int src_size, 546 int taps, 547 struct spl_fixed31_32 ratio, 548 struct spl_fixed31_32 init_adj, 549 struct spl_fixed31_32 *init, 550 int *vp_offset, 551 int *vp_size) 552 { 553 struct spl_fixed31_32 temp; 554 int int_part; 555 556 /* 557 * First of the taps starts sampling pixel number <init_int_part> corresponding to recout 558 * pixel 1. Next recout pixel samples int part of <init + scaling ratio> and so on. 559 * All following calculations are based on this logic. 560 * 561 * Init calculated according to formula: 562 * init = (scaling_ratio + number_of_taps + 1) / 2 563 * init_bot = init + scaling_ratio 564 * to get pixel perfect combine add the fraction from calculating vp offset 565 */ 566 temp = spl_fixpt_mul_int(ratio, recout_offset_within_recout_full); 567 *vp_offset = spl_fixpt_floor(temp); 568 temp.value &= 0xffffffff; 569 *init = spl_fixpt_add(spl_fixpt_div_int(spl_fixpt_add_int(ratio, taps + 1), 2), temp); 570 *init = spl_fixpt_add(*init, init_adj); 571 *init = spl_fixpt_truncate(*init, 19); 572 573 /* 574 * If viewport has non 0 offset and there are more taps than covered by init then 575 * we should decrease the offset and increase init so we are never sampling 576 * outside of viewport. 577 */ 578 int_part = spl_fixpt_floor(*init); 579 if (int_part < taps) { 580 int_part = taps - int_part; 581 if (int_part > *vp_offset) 582 int_part = *vp_offset; 583 *vp_offset -= int_part; 584 *init = spl_fixpt_add_int(*init, int_part); 585 } 586 /* 587 * If taps are sampling outside of viewport at end of recout and there are more pixels 588 * available in the surface we should increase the viewport size, regardless set vp to 589 * only what is used. 590 */ 591 temp = spl_fixpt_add(*init, spl_fixpt_mul_int(ratio, recout_size - 1)); 592 *vp_size = spl_fixpt_floor(temp); 593 if (*vp_size + *vp_offset > src_size) 594 *vp_size = src_size - *vp_offset; 595 596 /* We did all the math assuming we are scanning same direction as display does, 597 * however mirror/rotation changes how vp scans vs how it is offset. If scan direction 598 * is flipped we simply need to calculate offset from the other side of plane. 599 * Note that outside of viewport all scaling hardware works in recout space. 600 */ 601 if (flip_scan_dir) 602 *vp_offset = src_size - *vp_offset - *vp_size; 603 } 604 605 /*Calculate inits and viewport */ 606 static void spl_calculate_inits_and_viewports(struct spl_in *spl_in, 607 struct spl_scratch *spl_scratch) 608 { 609 struct spl_rect src = spl_in->basic_in.src_rect; 610 struct spl_rect recout_dst_in_active_timing; 611 struct spl_rect recout_clip_in_active_timing; 612 struct spl_rect recout_clip_in_recout_dst; 613 struct spl_rect overlap_in_active_timing; 614 struct spl_rect odm_slice = calculate_odm_slice_in_timing_active(spl_in); 615 int vpc_div = spl_is_subsampled_format(spl_in->basic_in.format) ? 2 : 1; 616 bool orthogonal_rotation, flip_vert_scan_dir, flip_horz_scan_dir; 617 struct spl_fixed31_32 init_adj_h = spl_fixpt_zero; 618 struct spl_fixed31_32 init_adj_v = spl_fixpt_zero; 619 620 recout_clip_in_active_timing = shift_rec( 621 &spl_scratch->scl_data.recout, odm_slice.x, odm_slice.y); 622 recout_dst_in_active_timing = calculate_plane_rec_in_timing_active( 623 spl_in, &spl_in->basic_in.dst_rect); 624 overlap_in_active_timing = intersect_rec(&recout_clip_in_active_timing, 625 &recout_dst_in_active_timing); 626 if (overlap_in_active_timing.width > 0 && 627 overlap_in_active_timing.height > 0) 628 recout_clip_in_recout_dst = shift_rec(&overlap_in_active_timing, 629 -recout_dst_in_active_timing.x, 630 -recout_dst_in_active_timing.y); 631 else 632 memset(&recout_clip_in_recout_dst, 0, sizeof(struct spl_rect)); 633 /* 634 * Work in recout rotation since that requires less transformations 635 */ 636 spl_get_vp_scan_direction( 637 spl_in->basic_in.rotation, 638 spl_in->basic_in.horizontal_mirror, 639 &orthogonal_rotation, 640 &flip_vert_scan_dir, 641 &flip_horz_scan_dir); 642 643 if (spl_is_subsampled_format(spl_in->basic_in.format)) { 644 /* this gives the direction of the cositing (negative will move 645 * left, right otherwise) 646 */ 647 int h_sign = flip_horz_scan_dir ? -1 : 1; 648 int v_sign = flip_vert_scan_dir ? -1 : 1; 649 650 switch (spl_in->basic_in.cositing) { 651 case CHROMA_COSITING_TOPLEFT: 652 init_adj_h = SPL_NAMESPACE(spl_fixpt_from_fraction(h_sign, 4)); 653 init_adj_v = SPL_NAMESPACE(spl_fixpt_from_fraction(v_sign, 4)); 654 break; 655 case CHROMA_COSITING_LEFT: 656 init_adj_h = SPL_NAMESPACE(spl_fixpt_from_fraction(h_sign, 4)); 657 init_adj_v = spl_fixpt_zero; 658 break; 659 case CHROMA_COSITING_NONE: 660 default: 661 init_adj_h = spl_fixpt_zero; 662 init_adj_v = spl_fixpt_zero; 663 break; 664 } 665 } 666 667 if (orthogonal_rotation) { 668 spl_swap(src.width, src.height); 669 spl_swap(flip_vert_scan_dir, flip_horz_scan_dir); 670 spl_swap(init_adj_h, init_adj_v); 671 } 672 673 spl_calculate_init_and_vp( 674 flip_horz_scan_dir, 675 recout_clip_in_recout_dst.x, 676 spl_scratch->scl_data.recout.width, 677 src.width, 678 spl_scratch->scl_data.taps.h_taps, 679 spl_scratch->scl_data.ratios.horz, 680 spl_fixpt_zero, 681 &spl_scratch->scl_data.inits.h, 682 &spl_scratch->scl_data.viewport.x, 683 &spl_scratch->scl_data.viewport.width); 684 spl_calculate_init_and_vp( 685 flip_horz_scan_dir, 686 recout_clip_in_recout_dst.x, 687 spl_scratch->scl_data.recout.width, 688 src.width / vpc_div, 689 spl_scratch->scl_data.taps.h_taps_c, 690 spl_scratch->scl_data.ratios.horz_c, 691 init_adj_h, 692 &spl_scratch->scl_data.inits.h_c, 693 &spl_scratch->scl_data.viewport_c.x, 694 &spl_scratch->scl_data.viewport_c.width); 695 spl_calculate_init_and_vp( 696 flip_vert_scan_dir, 697 recout_clip_in_recout_dst.y, 698 spl_scratch->scl_data.recout.height, 699 src.height, 700 spl_scratch->scl_data.taps.v_taps, 701 spl_scratch->scl_data.ratios.vert, 702 spl_fixpt_zero, 703 &spl_scratch->scl_data.inits.v, 704 &spl_scratch->scl_data.viewport.y, 705 &spl_scratch->scl_data.viewport.height); 706 spl_calculate_init_and_vp( 707 flip_vert_scan_dir, 708 recout_clip_in_recout_dst.y, 709 spl_scratch->scl_data.recout.height, 710 src.height / vpc_div, 711 spl_scratch->scl_data.taps.v_taps_c, 712 spl_scratch->scl_data.ratios.vert_c, 713 init_adj_v, 714 &spl_scratch->scl_data.inits.v_c, 715 &spl_scratch->scl_data.viewport_c.y, 716 &spl_scratch->scl_data.viewport_c.height); 717 if (orthogonal_rotation) { 718 spl_swap(spl_scratch->scl_data.viewport.x, spl_scratch->scl_data.viewport.y); 719 spl_swap(spl_scratch->scl_data.viewport.width, spl_scratch->scl_data.viewport.height); 720 spl_swap(spl_scratch->scl_data.viewport_c.x, spl_scratch->scl_data.viewport_c.y); 721 spl_swap(spl_scratch->scl_data.viewport_c.width, spl_scratch->scl_data.viewport_c.height); 722 } 723 spl_scratch->scl_data.viewport.x += src.x; 724 spl_scratch->scl_data.viewport.y += src.y; 725 SPL_ASSERT(src.x % vpc_div == 0 && src.y % vpc_div == 0); 726 spl_scratch->scl_data.viewport_c.x += src.x / vpc_div; 727 spl_scratch->scl_data.viewport_c.y += src.y / vpc_div; 728 } 729 730 static void spl_handle_3d_recout(struct spl_in *spl_in, struct spl_rect *recout) 731 { 732 /* 733 * Handle side by side and top bottom 3d recout offsets after vp calculation 734 * since 3d is special and needs to calculate vp as if there is no recout offset 735 * This may break with rotation, good thing we aren't mixing hw rotation and 3d 736 */ 737 if (spl_in->basic_in.mpc_h_slice_index) { 738 SPL_ASSERT(spl_in->basic_in.rotation == SPL_ROTATION_ANGLE_0 || 739 (spl_in->basic_out.view_format != SPL_VIEW_3D_TOP_AND_BOTTOM && 740 spl_in->basic_out.view_format != SPL_VIEW_3D_SIDE_BY_SIDE)); 741 if (spl_in->basic_out.view_format == SPL_VIEW_3D_TOP_AND_BOTTOM) 742 recout->y += recout->height; 743 else if (spl_in->basic_out.view_format == SPL_VIEW_3D_SIDE_BY_SIDE) 744 recout->x += recout->width; 745 } 746 } 747 748 static void spl_clamp_viewport(struct spl_rect *viewport, int min_viewport_size) 749 { 750 if (min_viewport_size == 0) 751 min_viewport_size = MIN_VIEWPORT_SIZE; 752 /* Clamp minimum viewport size */ 753 if (viewport->height < min_viewport_size) 754 viewport->height = min_viewport_size; 755 if (viewport->width < min_viewport_size) 756 viewport->width = min_viewport_size; 757 } 758 759 static enum scl_mode spl_get_dscl_mode(const struct spl_in *spl_in, 760 const struct spl_scaler_data *data, 761 bool enable_isharp, bool enable_easf) 762 { 763 const long long one = spl_fixpt_one.value; 764 enum spl_pixel_format pixel_format = spl_in->basic_in.format; 765 766 /* Bypass if ratio is 1:1 with no ISHARP or force scale on */ 767 if (data->ratios.horz.value == one 768 && data->ratios.vert.value == one 769 && data->ratios.horz_c.value == one 770 && data->ratios.vert_c.value == one 771 && !spl_in->basic_out.always_scale 772 && !enable_isharp) 773 return SCL_MODE_SCALING_444_BYPASS; 774 775 if (!spl_is_subsampled_format(pixel_format)) { 776 if (spl_is_video_format(pixel_format)) 777 return SCL_MODE_SCALING_444_YCBCR_ENABLE; 778 else 779 return SCL_MODE_SCALING_444_RGB_ENABLE; 780 } 781 782 /* 783 * Bypass YUV if Y is 1:1 with no ISHARP 784 * Do not bypass UV at 1:1 for cositing to be applied 785 */ 786 if (!enable_isharp) { 787 if (data->ratios.horz.value == one && data->ratios.vert.value == one && !spl_in->basic_out.always_scale) 788 return SCL_MODE_SCALING_420_LUMA_BYPASS; 789 } 790 791 return SCL_MODE_SCALING_420_YCBCR_ENABLE; 792 } 793 794 static void spl_choose_lls_policy(enum spl_pixel_format format, 795 enum linear_light_scaling *lls_pref) 796 { 797 if (spl_is_subsampled_format(format)) 798 *lls_pref = LLS_PREF_NO; 799 else /* RGB or YUV444 */ 800 *lls_pref = LLS_PREF_YES; 801 } 802 803 /* Enable EASF ?*/ 804 static bool enable_easf(struct spl_in *spl_in, struct spl_scratch *spl_scratch) 805 { 806 int vratio = 0; 807 int hratio = 0; 808 bool skip_easf = false; 809 810 if (spl_in->disable_easf) 811 skip_easf = true; 812 813 vratio = spl_fixpt_ceil(spl_scratch->scl_data.ratios.vert); 814 hratio = spl_fixpt_ceil(spl_scratch->scl_data.ratios.horz); 815 816 /* 817 * No EASF support for downscaling > 2:1 818 * EASF support for upscaling or downscaling up to 2:1 819 */ 820 if ((vratio > 2) || (hratio > 2)) 821 skip_easf = true; 822 823 /* 824 * If lls_pref is LLS_PREF_DONT_CARE, then use pixel format 825 * to determine whether to use LINEAR or NONLINEAR scaling 826 */ 827 if (spl_in->lls_pref == LLS_PREF_DONT_CARE) 828 spl_choose_lls_policy(spl_in->basic_in.format, 829 &spl_in->lls_pref); 830 831 /* Check for linear scaling or EASF preferred */ 832 if (spl_in->lls_pref != LLS_PREF_YES && !spl_in->prefer_easf) 833 skip_easf = true; 834 835 return skip_easf; 836 } 837 838 /* Check if video is in fullscreen mode */ 839 static bool spl_is_video_fullscreen(struct spl_in *spl_in) 840 { 841 if (spl_is_video_format(spl_in->basic_in.format) && spl_in->is_fullscreen) 842 return true; 843 return false; 844 } 845 846 static bool spl_get_isharp_en(struct spl_in *spl_in, 847 struct spl_scratch *spl_scratch) 848 { 849 bool enable_isharp = false; 850 int vratio = 0; 851 int hratio = 0; 852 struct spl_taps taps = spl_scratch->scl_data.taps; 853 bool fullscreen = spl_is_video_fullscreen(spl_in); 854 855 /* Return if adaptive sharpness is disabled */ 856 if (spl_in->adaptive_sharpness.enable == false) 857 return enable_isharp; 858 859 vratio = spl_fixpt_ceil(spl_scratch->scl_data.ratios.vert); 860 hratio = spl_fixpt_ceil(spl_scratch->scl_data.ratios.horz); 861 862 /* No iSHARP support for downscaling */ 863 if (vratio > 1 || hratio > 1) 864 return enable_isharp; 865 866 // Scaling is up to 1:1 (no scaling) or upscaling 867 868 /* 869 * Apply sharpness to RGB and YUV (NV12/P010) 870 * surfaces based on policy setting 871 */ 872 if (!spl_is_video_format(spl_in->basic_in.format) && 873 (spl_in->sharpen_policy == SHARPEN_YUV)) 874 return enable_isharp; 875 else if ((spl_is_video_format(spl_in->basic_in.format) && !fullscreen) && 876 (spl_in->sharpen_policy == SHARPEN_RGB_FULLSCREEN_YUV)) 877 return enable_isharp; 878 else if (!spl_in->is_fullscreen && 879 spl_in->sharpen_policy == SHARPEN_FULLSCREEN_ALL) 880 return enable_isharp; 881 882 /* 883 * Apply sharpness if supports horizontal taps 4,6 AND 884 * vertical taps 3, 4, 6 885 */ 886 if ((taps.h_taps == 4 || taps.h_taps == 6) && 887 (taps.v_taps == 3 || taps.v_taps == 4 || taps.v_taps == 6)) 888 enable_isharp = true; 889 890 return enable_isharp; 891 } 892 893 /* Calculate number of tap with adaptive scaling off */ 894 static void spl_get_taps_non_adaptive_scaler( 895 struct spl_scratch *spl_scratch, 896 const struct spl_taps *in_taps, 897 bool is_subsampled) 898 { 899 bool check_max_downscale = false; 900 901 if (in_taps->h_taps == 0) { 902 if (spl_fixpt_ceil(spl_scratch->scl_data.ratios.horz) > 1) 903 spl_scratch->scl_data.taps.h_taps = spl_min(2 * spl_fixpt_ceil( 904 spl_scratch->scl_data.ratios.horz), 8); 905 else 906 spl_scratch->scl_data.taps.h_taps = 4; 907 } else 908 spl_scratch->scl_data.taps.h_taps = in_taps->h_taps; 909 910 if (in_taps->v_taps == 0) { 911 if (spl_fixpt_ceil(spl_scratch->scl_data.ratios.vert) > 1) 912 spl_scratch->scl_data.taps.v_taps = spl_min(2 * spl_fixpt_ceil( 913 spl_scratch->scl_data.ratios.vert), 8); 914 else 915 spl_scratch->scl_data.taps.v_taps = 4; 916 } else 917 spl_scratch->scl_data.taps.v_taps = in_taps->v_taps; 918 919 if (in_taps->v_taps_c == 0) { 920 if (spl_fixpt_ceil(spl_scratch->scl_data.ratios.vert_c) > 1) 921 spl_scratch->scl_data.taps.v_taps_c = spl_min(2 * spl_fixpt_ceil( 922 spl_scratch->scl_data.ratios.vert_c), 8); 923 else 924 spl_scratch->scl_data.taps.v_taps_c = 4; 925 } else 926 spl_scratch->scl_data.taps.v_taps_c = in_taps->v_taps_c; 927 928 if (in_taps->h_taps_c == 0) { 929 if (spl_fixpt_ceil(spl_scratch->scl_data.ratios.horz_c) > 1) 930 spl_scratch->scl_data.taps.h_taps_c = spl_min(2 * spl_fixpt_ceil( 931 spl_scratch->scl_data.ratios.horz_c), 8); 932 else 933 spl_scratch->scl_data.taps.h_taps_c = 4; 934 } else if ((in_taps->h_taps_c % 2) != 0 && in_taps->h_taps_c != 1) 935 /* Only 1 and even h_taps_c are supported by hw */ 936 spl_scratch->scl_data.taps.h_taps_c = in_taps->h_taps_c - 1; 937 else 938 spl_scratch->scl_data.taps.h_taps_c = in_taps->h_taps_c; 939 940 941 /* 942 * Max downscale supported is 6.0x. Add ASSERT to catch if go beyond that 943 */ 944 check_max_downscale = spl_fixpt_le(spl_scratch->scl_data.ratios.horz, 945 SPL_NAMESPACE(spl_fixpt_from_fraction(6, 1))); 946 SPL_ASSERT(check_max_downscale); 947 check_max_downscale = spl_fixpt_le(spl_scratch->scl_data.ratios.vert, 948 SPL_NAMESPACE(spl_fixpt_from_fraction(6, 1))); 949 SPL_ASSERT(check_max_downscale); 950 check_max_downscale = spl_fixpt_le(spl_scratch->scl_data.ratios.horz_c, 951 SPL_NAMESPACE(spl_fixpt_from_fraction(6, 1))); 952 SPL_ASSERT(check_max_downscale); 953 check_max_downscale = spl_fixpt_le(spl_scratch->scl_data.ratios.vert_c, 954 SPL_NAMESPACE(spl_fixpt_from_fraction(6, 1))); 955 SPL_ASSERT(check_max_downscale); 956 957 958 if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.horz)) 959 spl_scratch->scl_data.taps.h_taps = 1; 960 if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert)) 961 spl_scratch->scl_data.taps.v_taps = 1; 962 if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.horz_c) && !is_subsampled) 963 spl_scratch->scl_data.taps.h_taps_c = 1; 964 if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert_c) && !is_subsampled) 965 spl_scratch->scl_data.taps.v_taps_c = 1; 966 967 } 968 969 /* Calculate optimal number of taps */ 970 static bool spl_get_optimal_number_of_taps( 971 int max_downscale_src_width, struct spl_in *spl_in, struct spl_scratch *spl_scratch, 972 const struct spl_taps *in_taps, bool *enable_easf_v, bool *enable_easf_h, 973 bool *enable_isharp) 974 { 975 int num_part_y, num_part_c; 976 unsigned int max_taps_y, max_taps_c; 977 unsigned int min_taps_y, min_taps_c; 978 enum lb_memory_config lb_config; 979 bool skip_easf = false; 980 bool is_subsampled = spl_is_subsampled_format(spl_in->basic_in.format); 981 982 if (spl_scratch->scl_data.viewport.width > spl_scratch->scl_data.h_active && 983 max_downscale_src_width != 0 && 984 spl_scratch->scl_data.viewport.width > max_downscale_src_width) { 985 spl_get_taps_non_adaptive_scaler(spl_scratch, in_taps, is_subsampled); 986 *enable_easf_v = false; 987 *enable_easf_h = false; 988 *enable_isharp = false; 989 return false; 990 } 991 992 /* Disable adaptive scaler and sharpener when integer scaling is enabled */ 993 if (spl_in->scaling_quality.integer_scaling) { 994 spl_get_taps_non_adaptive_scaler(spl_scratch, in_taps, is_subsampled); 995 *enable_easf_v = false; 996 *enable_easf_h = false; 997 *enable_isharp = false; 998 return true; 999 } 1000 1001 /* Check if we are using EASF or not */ 1002 skip_easf = enable_easf(spl_in, spl_scratch); 1003 1004 /* 1005 * Set default taps if none are provided 1006 * From programming guide: taps = min{ ceil(2*H_RATIO,1), 8} for downscaling 1007 * taps = 4 for upscaling 1008 */ 1009 if (skip_easf) { 1010 spl_get_taps_non_adaptive_scaler(spl_scratch, in_taps, is_subsampled); 1011 } 1012 else { 1013 if (spl_is_video_format(spl_in->basic_in.format)) { 1014 spl_scratch->scl_data.taps.h_taps = 6; 1015 spl_scratch->scl_data.taps.v_taps = 6; 1016 spl_scratch->scl_data.taps.h_taps_c = 4; 1017 spl_scratch->scl_data.taps.v_taps_c = 4; 1018 } else { /* RGB */ 1019 spl_scratch->scl_data.taps.h_taps = 6; 1020 spl_scratch->scl_data.taps.v_taps = 6; 1021 spl_scratch->scl_data.taps.h_taps_c = 6; 1022 spl_scratch->scl_data.taps.v_taps_c = 6; 1023 } 1024 1025 /* Override mode: keep EASF enabled but use input taps if valid */ 1026 if (spl_in->override_easf) { 1027 spl_scratch->scl_data.taps.h_taps = (in_taps->h_taps != 0) ? in_taps->h_taps : spl_scratch->scl_data.taps.h_taps; 1028 spl_scratch->scl_data.taps.v_taps = (in_taps->v_taps != 0) ? in_taps->v_taps : spl_scratch->scl_data.taps.v_taps; 1029 spl_scratch->scl_data.taps.h_taps_c = (in_taps->h_taps_c != 0) ? in_taps->h_taps_c : spl_scratch->scl_data.taps.h_taps_c; 1030 spl_scratch->scl_data.taps.v_taps_c = (in_taps->v_taps_c != 0) ? in_taps->v_taps_c : spl_scratch->scl_data.taps.v_taps_c; 1031 1032 if ((spl_scratch->scl_data.taps.h_taps > 6) || (spl_scratch->scl_data.taps.v_taps > 6)) 1033 skip_easf = true; 1034 if ((spl_scratch->scl_data.taps.h_taps > 1) && (spl_scratch->scl_data.taps.h_taps % 2)) 1035 spl_scratch->scl_data.taps.h_taps--; 1036 if ((spl_scratch->scl_data.taps.h_taps_c > 1) && (spl_scratch->scl_data.taps.h_taps_c % 2)) 1037 spl_scratch->scl_data.taps.h_taps_c--; 1038 } 1039 } 1040 1041 /*Ensure we can support the requested number of vtaps*/ 1042 min_taps_y = spl_fixpt_ceil(spl_scratch->scl_data.ratios.vert); 1043 min_taps_c = spl_fixpt_ceil(spl_scratch->scl_data.ratios.vert_c); 1044 1045 /* Use LB_MEMORY_CONFIG_3 for 4:2:0 */ 1046 if (spl_is_yuv420(spl_in->basic_in.format)) 1047 lb_config = LB_MEMORY_CONFIG_3; 1048 else 1049 lb_config = LB_MEMORY_CONFIG_0; 1050 // Determine max vtap support by calculating how much line buffer can fit 1051 spl_in->callbacks.spl_calc_lb_num_partitions(spl_in->basic_out.alpha_en, &spl_scratch->scl_data, 1052 lb_config, &num_part_y, &num_part_c); 1053 /* MAX_V_TAPS = MIN (NUM_LINES - MAX(CEILING(V_RATIO,1)-2, 0), 8) */ 1054 if (spl_fixpt_ceil(spl_scratch->scl_data.ratios.vert) > 2) 1055 if ((spl_fixpt_ceil(spl_scratch->scl_data.ratios.vert) - 2) > num_part_y) 1056 max_taps_y = 0; 1057 else 1058 max_taps_y = num_part_y - (spl_fixpt_ceil(spl_scratch->scl_data.ratios.vert) - 2); 1059 else 1060 max_taps_y = num_part_y; 1061 1062 if (spl_fixpt_ceil(spl_scratch->scl_data.ratios.vert_c) > 2) 1063 if ((spl_fixpt_ceil(spl_scratch->scl_data.ratios.vert_c) - 2) > num_part_c) 1064 max_taps_c = 0; 1065 else 1066 max_taps_c = num_part_c - (spl_fixpt_ceil(spl_scratch->scl_data.ratios.vert_c) - 2); 1067 else 1068 max_taps_c = num_part_c; 1069 1070 if (max_taps_y < min_taps_y) 1071 return false; 1072 else if (max_taps_c < min_taps_c) 1073 return false; 1074 1075 if (spl_scratch->scl_data.taps.v_taps > max_taps_y) 1076 spl_scratch->scl_data.taps.v_taps = max_taps_y; 1077 1078 if (spl_scratch->scl_data.taps.v_taps_c > max_taps_c) 1079 spl_scratch->scl_data.taps.v_taps_c = max_taps_c; 1080 1081 if (!skip_easf) { 1082 /* 1083 * RGB ( L + NL ) and Linear HDR support 6x6, 6x4, 6x3, 4x4, 4x3 1084 * NL YUV420 only supports 6x6, 6x4 for Y and 4x4 for UV 1085 * 1086 * If LB does not support 3, 4, or 6 taps, then disable EASF_V 1087 * and only enable EASF_H. So for RGB, support 6x2, 4x2 1088 * and for NL YUV420, support 6x2 for Y and 4x2 for UV 1089 * 1090 * All other cases, have to disable EASF_V and EASF_H 1091 * 1092 * If optimal no of taps is 5, then set it to 4 1093 * If optimal no of taps is 7 or 8, then fine since max tap is 6 1094 * 1095 */ 1096 if (spl_scratch->scl_data.taps.v_taps == 5) 1097 spl_scratch->scl_data.taps.v_taps = 4; 1098 1099 if (spl_scratch->scl_data.taps.v_taps_c == 5) 1100 spl_scratch->scl_data.taps.v_taps_c = 4; 1101 1102 if (spl_scratch->scl_data.taps.h_taps == 5) 1103 spl_scratch->scl_data.taps.h_taps = 4; 1104 1105 if (spl_scratch->scl_data.taps.h_taps_c == 5) 1106 spl_scratch->scl_data.taps.h_taps_c = 4; 1107 1108 if (spl_is_video_format(spl_in->basic_in.format)) { 1109 if (spl_scratch->scl_data.taps.h_taps <= 4) { 1110 *enable_easf_v = false; 1111 *enable_easf_h = false; 1112 } else if (spl_scratch->scl_data.taps.v_taps <= 3) { 1113 *enable_easf_v = false; 1114 *enable_easf_h = true; 1115 } else { 1116 *enable_easf_v = true; 1117 *enable_easf_h = true; 1118 } 1119 SPL_ASSERT((spl_scratch->scl_data.taps.v_taps > 1) && 1120 (spl_scratch->scl_data.taps.v_taps_c > 1)); 1121 } else { /* RGB */ 1122 if (spl_scratch->scl_data.taps.h_taps <= 3) { 1123 *enable_easf_v = false; 1124 *enable_easf_h = false; 1125 } else if (spl_scratch->scl_data.taps.v_taps < 3) { 1126 *enable_easf_v = false; 1127 *enable_easf_h = true; 1128 } else { 1129 *enable_easf_v = true; 1130 *enable_easf_h = true; 1131 } 1132 SPL_ASSERT(spl_scratch->scl_data.taps.v_taps > 1); 1133 } 1134 } else { 1135 *enable_easf_v = false; 1136 *enable_easf_h = false; 1137 } // end of if prefer_easf 1138 1139 /* Sharpener requires scaler to be enabled, including for 1:1 1140 * Check if ISHARP can be enabled 1141 * If ISHARP is not enabled, set taps to 1 if ratio is 1:1 1142 * except for chroma taps. Keep previous taps so it can 1143 * handle cositing 1144 */ 1145 1146 *enable_isharp = spl_get_isharp_en(spl_in, spl_scratch); 1147 if (!*enable_isharp && !spl_in->basic_out.always_scale) { 1148 if ((IDENTITY_RATIO(spl_scratch->scl_data.ratios.horz)) && 1149 (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert))) { 1150 spl_scratch->scl_data.taps.h_taps = 1; 1151 spl_scratch->scl_data.taps.v_taps = 1; 1152 if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.horz_c) && !is_subsampled) 1153 spl_scratch->scl_data.taps.h_taps_c = 1; 1154 1155 if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert_c) && !is_subsampled) 1156 spl_scratch->scl_data.taps.v_taps_c = 1; 1157 1158 *enable_easf_v = false; 1159 *enable_easf_h = false; 1160 } else { 1161 if ((!*enable_easf_h) && 1162 (IDENTITY_RATIO(spl_scratch->scl_data.ratios.horz))) 1163 spl_scratch->scl_data.taps.h_taps = 1; 1164 1165 if ((!*enable_easf_v) && 1166 (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert))) 1167 spl_scratch->scl_data.taps.v_taps = 1; 1168 1169 if ((!*enable_easf_h) && !is_subsampled && 1170 (IDENTITY_RATIO(spl_scratch->scl_data.ratios.horz_c))) 1171 spl_scratch->scl_data.taps.h_taps_c = 1; 1172 1173 if ((!*enable_easf_v) && !is_subsampled && 1174 (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert_c))) 1175 spl_scratch->scl_data.taps.v_taps_c = 1; 1176 1177 } 1178 } 1179 return true; 1180 } 1181 1182 static void spl_set_black_color_data(enum spl_pixel_format format, 1183 struct scl_black_color *scl_black_color) 1184 { 1185 bool ycbcr = spl_is_video_format(format); 1186 if (ycbcr) { 1187 scl_black_color->offset_rgb_y = BLACK_OFFSET_RGB_Y; 1188 scl_black_color->offset_rgb_cbcr = BLACK_OFFSET_CBCR; 1189 } else { 1190 scl_black_color->offset_rgb_y = 0x0; 1191 scl_black_color->offset_rgb_cbcr = 0x0; 1192 } 1193 } 1194 1195 static void spl_set_manual_ratio_init_data(struct dscl_prog_data *dscl_prog_data, 1196 const struct spl_scaler_data *scl_data) 1197 { 1198 struct spl_fixed31_32 bot; 1199 1200 dscl_prog_data->ratios.h_scale_ratio = SPL_NAMESPACE(spl_fixpt_u3d19( 1201 scl_data->ratios.horz)) << 5; 1202 dscl_prog_data->ratios.v_scale_ratio = SPL_NAMESPACE(spl_fixpt_u3d19( 1203 scl_data->ratios.vert)) << 5; 1204 dscl_prog_data->ratios.h_scale_ratio_c = SPL_NAMESPACE(spl_fixpt_u3d19( 1205 scl_data->ratios.horz_c)) << 5; 1206 dscl_prog_data->ratios.v_scale_ratio_c = SPL_NAMESPACE(spl_fixpt_u3d19( 1207 scl_data->ratios.vert_c)) << 5; 1208 /* 1209 * 0.24 format for fraction, first five bits zeroed 1210 */ 1211 dscl_prog_data->init.h_filter_init_frac = 1212 SPL_NAMESPACE(spl_fixpt_u0d19(scl_data->inits.h)) << 5; 1213 dscl_prog_data->init.h_filter_init_int = 1214 spl_fixpt_floor(scl_data->inits.h); 1215 dscl_prog_data->init.h_filter_init_frac_c = 1216 SPL_NAMESPACE(spl_fixpt_u0d19(scl_data->inits.h_c)) << 5; 1217 dscl_prog_data->init.h_filter_init_int_c = 1218 spl_fixpt_floor(scl_data->inits.h_c); 1219 dscl_prog_data->init.v_filter_init_frac = 1220 SPL_NAMESPACE(spl_fixpt_u0d19(scl_data->inits.v)) << 5; 1221 dscl_prog_data->init.v_filter_init_int = 1222 spl_fixpt_floor(scl_data->inits.v); 1223 dscl_prog_data->init.v_filter_init_frac_c = 1224 SPL_NAMESPACE(spl_fixpt_u0d19(scl_data->inits.v_c)) << 5; 1225 dscl_prog_data->init.v_filter_init_int_c = 1226 spl_fixpt_floor(scl_data->inits.v_c); 1227 1228 bot = spl_fixpt_add(scl_data->inits.v, scl_data->ratios.vert); 1229 dscl_prog_data->init.v_filter_init_bot_frac = SPL_NAMESPACE(spl_fixpt_u0d19(bot)) << 5; 1230 dscl_prog_data->init.v_filter_init_bot_int = spl_fixpt_floor(bot); 1231 bot = spl_fixpt_add(scl_data->inits.v_c, scl_data->ratios.vert_c); 1232 dscl_prog_data->init.v_filter_init_bot_frac_c = SPL_NAMESPACE(spl_fixpt_u0d19(bot)) << 5; 1233 dscl_prog_data->init.v_filter_init_bot_int_c = spl_fixpt_floor(bot); 1234 } 1235 1236 static void spl_set_taps_data(struct dscl_prog_data *dscl_prog_data, 1237 const struct spl_scaler_data *scl_data) 1238 { 1239 dscl_prog_data->taps.v_taps = scl_data->taps.v_taps - 1; 1240 dscl_prog_data->taps.h_taps = scl_data->taps.h_taps - 1; 1241 dscl_prog_data->taps.v_taps_c = scl_data->taps.v_taps_c - 1; 1242 dscl_prog_data->taps.h_taps_c = scl_data->taps.h_taps_c - 1; 1243 } 1244 1245 /* Populate dscl prog data structure from scaler data calculated by SPL */ 1246 static void spl_set_dscl_prog_data(struct spl_in *spl_in, struct spl_scratch *spl_scratch, 1247 struct spl_out *spl_out, bool enable_easf_v, bool enable_easf_h, bool enable_isharp) 1248 { 1249 struct dscl_prog_data *dscl_prog_data = spl_out->dscl_prog_data; 1250 1251 const struct spl_scaler_data *data = &spl_scratch->scl_data; 1252 1253 struct scl_black_color *scl_black_color = &dscl_prog_data->scl_black_color; 1254 1255 bool enable_easf = enable_easf_v || enable_easf_h; 1256 1257 // Set values for recout 1258 dscl_prog_data->recout = spl_scratch->scl_data.recout; 1259 // Set values for MPC Size 1260 dscl_prog_data->mpc_size.width = spl_scratch->scl_data.h_active; 1261 dscl_prog_data->mpc_size.height = spl_scratch->scl_data.v_active; 1262 1263 // SCL_MODE - Set SCL_MODE data 1264 dscl_prog_data->dscl_mode = spl_get_dscl_mode(spl_in, data, enable_isharp, 1265 enable_easf); 1266 1267 // SCL_BLACK_COLOR 1268 spl_set_black_color_data(spl_in->basic_in.format, scl_black_color); 1269 1270 /* Manually calculate scale ratio and init values */ 1271 spl_set_manual_ratio_init_data(dscl_prog_data, data); 1272 1273 // Set HTaps/VTaps 1274 spl_set_taps_data(dscl_prog_data, data); 1275 // Set viewport 1276 dscl_prog_data->viewport = spl_scratch->scl_data.viewport; 1277 // Set viewport_c 1278 dscl_prog_data->viewport_c = spl_scratch->scl_data.viewport_c; 1279 // Set filters data 1280 SPL_NAMESPACE(spl_set_filters_data(dscl_prog_data, data, enable_easf_v, enable_easf_h)); 1281 } 1282 1283 /* Calculate C0-C3 coefficients based on HDR_mult */ 1284 static void spl_calculate_c0_c3_hdr(struct dscl_prog_data *dscl_prog_data, uint32_t sdr_white_level_nits) 1285 { 1286 struct spl_fixed31_32 hdr_mult, c0_mult, c1_mult, c2_mult; 1287 struct spl_fixed31_32 c0_calc, c1_calc, c2_calc; 1288 struct spl_custom_float_format fmt; 1289 uint32_t hdr_multx100_int; 1290 1291 if ((sdr_white_level_nits >= 80) && (sdr_white_level_nits <= 480)) 1292 hdr_multx100_int = sdr_white_level_nits * 100 / 80; 1293 else 1294 hdr_multx100_int = 100; /* default for 80 nits otherwise */ 1295 1296 hdr_mult = SPL_NAMESPACE(spl_fixpt_from_fraction((long long)hdr_multx100_int, 100LL)); 1297 c0_mult = SPL_NAMESPACE(spl_fixpt_from_fraction(2126LL, 10000LL)); 1298 c1_mult = SPL_NAMESPACE(spl_fixpt_from_fraction(7152LL, 10000LL)); 1299 c2_mult = SPL_NAMESPACE(spl_fixpt_from_fraction(722LL, 10000LL)); 1300 1301 c0_calc = SPL_NAMESPACE(spl_fixpt_mul(hdr_mult, SPL_NAMESPACE(spl_fixpt_mul(c0_mult, 1302 SPL_NAMESPACE(spl_fixpt_from_fraction(16384LL, 125LL)))))); 1303 c1_calc = SPL_NAMESPACE(spl_fixpt_mul(hdr_mult, SPL_NAMESPACE(spl_fixpt_mul(c1_mult, 1304 SPL_NAMESPACE(spl_fixpt_from_fraction(16384LL, 125LL)))))); 1305 c2_calc = SPL_NAMESPACE(spl_fixpt_mul(hdr_mult, SPL_NAMESPACE(spl_fixpt_mul(c2_mult, 1306 SPL_NAMESPACE(spl_fixpt_from_fraction(16384LL, 125LL)))))); 1307 1308 fmt.exponenta_bits = 5; 1309 fmt.mantissa_bits = 10; 1310 fmt.sign = true; 1311 1312 // fp1.5.10, C0 coefficient (LN_rec709: HDR_MULT * 0.212600 * 2^14/125) 1313 SPL_NAMESPACE(spl_convert_to_custom_float_format(c0_calc, &fmt, 1314 &dscl_prog_data->easf_matrix_c0)); 1315 // fp1.5.10, C1 coefficient (LN_rec709: HDR_MULT * 0.715200 * 2^14/125) 1316 SPL_NAMESPACE(spl_convert_to_custom_float_format(c1_calc, &fmt, 1317 &dscl_prog_data->easf_matrix_c1)); 1318 // fp1.5.10, C2 coefficient (LN_rec709: HDR_MULT * 0.072200 * 2^14/125) 1319 SPL_NAMESPACE(spl_convert_to_custom_float_format(c2_calc, &fmt, 1320 &dscl_prog_data->easf_matrix_c2)); 1321 dscl_prog_data->easf_matrix_c3 = 0x0; // fp1.5.10, C3 coefficient 1322 } 1323 1324 /* Set EASF data */ 1325 static void spl_set_easf_data(struct spl_scratch *spl_scratch, struct spl_out *spl_out, bool enable_easf_v, 1326 bool enable_easf_h, enum linear_light_scaling lls_pref, 1327 enum spl_pixel_format format, enum system_setup setup, 1328 uint32_t sdr_white_level_nits) 1329 { 1330 struct dscl_prog_data *dscl_prog_data = spl_out->dscl_prog_data; 1331 if (enable_easf_v) { 1332 dscl_prog_data->easf_v_en = true; 1333 dscl_prog_data->easf_v_ring = 0; 1334 dscl_prog_data->easf_v_sharp_factor = 1; 1335 dscl_prog_data->easf_v_bf1_en = 1; // 1-bit, BF1 calculation enable, 0=disable, 1=enable 1336 dscl_prog_data->easf_v_bf2_mode = 0xF; // 4-bit, BF2 calculation mode 1337 /* 2-bit, BF3 chroma mode correction calculation mode */ 1338 dscl_prog_data->easf_v_bf3_mode = SPL_NAMESPACE(spl_get_v_bf3_mode( 1339 spl_scratch->scl_data.recip_ratios.vert)); 1340 /* FP1.5.10 [ minCoef ]*/ 1341 dscl_prog_data->easf_v_ringest_3tap_dntilt_uptilt = 1342 SPL_NAMESPACE(spl_get_3tap_dntilt_uptilt_offset(spl_scratch->scl_data.taps.v_taps, 1343 spl_scratch->scl_data.recip_ratios.vert)); 1344 /* FP1.5.10 [ upTiltMaxVal ]*/ 1345 dscl_prog_data->easf_v_ringest_3tap_uptilt_max = 1346 SPL_NAMESPACE(spl_get_3tap_uptilt_maxval(spl_scratch->scl_data.taps.v_taps, 1347 spl_scratch->scl_data.recip_ratios.vert)); 1348 /* FP1.5.10 [ dnTiltSlope ]*/ 1349 dscl_prog_data->easf_v_ringest_3tap_dntilt_slope = 1350 SPL_NAMESPACE(spl_get_3tap_dntilt_slope(spl_scratch->scl_data.taps.v_taps, 1351 spl_scratch->scl_data.recip_ratios.vert)); 1352 /* FP1.5.10 [ upTilt1Slope ]*/ 1353 dscl_prog_data->easf_v_ringest_3tap_uptilt1_slope = 1354 SPL_NAMESPACE(spl_get_3tap_uptilt1_slope(spl_scratch->scl_data.taps.v_taps, 1355 spl_scratch->scl_data.recip_ratios.vert)); 1356 /* FP1.5.10 [ upTilt2Slope ]*/ 1357 dscl_prog_data->easf_v_ringest_3tap_uptilt2_slope = 1358 SPL_NAMESPACE(spl_get_3tap_uptilt2_slope(spl_scratch->scl_data.taps.v_taps, 1359 spl_scratch->scl_data.recip_ratios.vert)); 1360 /* FP1.5.10 [ upTilt2Offset ]*/ 1361 dscl_prog_data->easf_v_ringest_3tap_uptilt2_offset = 1362 SPL_NAMESPACE(spl_get_3tap_uptilt2_offset(spl_scratch->scl_data.taps.v_taps, 1363 spl_scratch->scl_data.recip_ratios.vert)); 1364 /* FP1.5.10; (2.0) Ring reducer gain for 4 or 6-tap mode [H_REDUCER_GAIN4] */ 1365 dscl_prog_data->easf_v_ringest_eventap_reduceg1 = 1366 SPL_NAMESPACE(spl_get_reducer_gain4(spl_scratch->scl_data.taps.v_taps, 1367 spl_scratch->scl_data.recip_ratios.vert)); 1368 /* FP1.5.10; (2.5) Ring reducer gain for 6-tap mode [V_REDUCER_GAIN6] */ 1369 dscl_prog_data->easf_v_ringest_eventap_reduceg2 = 1370 SPL_NAMESPACE(spl_get_reducer_gain6(spl_scratch->scl_data.taps.v_taps, 1371 spl_scratch->scl_data.recip_ratios.vert)); 1372 /* FP1.5.10; (-0.135742) Ring gain for 6-tap set to -139/1024 */ 1373 dscl_prog_data->easf_v_ringest_eventap_gain1 = 1374 SPL_NAMESPACE(spl_get_gainRing4(spl_scratch->scl_data.taps.v_taps, 1375 spl_scratch->scl_data.recip_ratios.vert)); 1376 /* FP1.5.10; (-0.024414) Ring gain for 6-tap set to -25/1024 */ 1377 dscl_prog_data->easf_v_ringest_eventap_gain2 = 1378 SPL_NAMESPACE(spl_get_gainRing6(spl_scratch->scl_data.taps.v_taps, 1379 spl_scratch->scl_data.recip_ratios.vert)); 1380 dscl_prog_data->easf_v_bf_maxa = 63; //Vertical Max BF value A in U0.6 format.Selected if V_FCNTL == 0 1381 dscl_prog_data->easf_v_bf_maxb = 63; //Vertical Max BF value A in U0.6 format.Selected if V_FCNTL == 1 1382 dscl_prog_data->easf_v_bf_mina = 0; //Vertical Min BF value A in U0.6 format.Selected if V_FCNTL == 0 1383 dscl_prog_data->easf_v_bf_minb = 0; //Vertical Min BF value A in U0.6 format.Selected if V_FCNTL == 1 1384 if (lls_pref == LLS_PREF_YES) { 1385 dscl_prog_data->easf_v_bf2_flat1_gain = 4; // U1.3, BF2 Flat1 Gain control 1386 dscl_prog_data->easf_v_bf2_flat2_gain = 8; // U4.0, BF2 Flat2 Gain control 1387 dscl_prog_data->easf_v_bf2_roc_gain = 4; // U2.2, Rate Of Change control 1388 1389 dscl_prog_data->easf_v_bf1_pwl_in_seg0 = 0x600; // S0.10, BF1 PWL Segment 0 = -512 1390 dscl_prog_data->easf_v_bf1_pwl_base_seg0 = 0; // U0.6, BF1 Base PWL Segment 0 1391 dscl_prog_data->easf_v_bf1_pwl_slope_seg0 = 3; // S7.3, BF1 Slope PWL Segment 0 1392 dscl_prog_data->easf_v_bf1_pwl_in_seg1 = 0x7EC; // S0.10, BF1 PWL Segment 1 = -20 1393 dscl_prog_data->easf_v_bf1_pwl_base_seg1 = 12; // U0.6, BF1 Base PWL Segment 1 1394 dscl_prog_data->easf_v_bf1_pwl_slope_seg1 = 326; // S7.3, BF1 Slope PWL Segment 1 1395 dscl_prog_data->easf_v_bf1_pwl_in_seg2 = 0; // S0.10, BF1 PWL Segment 2 1396 dscl_prog_data->easf_v_bf1_pwl_base_seg2 = 63; // U0.6, BF1 Base PWL Segment 2 1397 dscl_prog_data->easf_v_bf1_pwl_slope_seg2 = 0; // S7.3, BF1 Slope PWL Segment 2 1398 dscl_prog_data->easf_v_bf1_pwl_in_seg3 = 16; // S0.10, BF1 PWL Segment 3 1399 dscl_prog_data->easf_v_bf1_pwl_base_seg3 = 63; // U0.6, BF1 Base PWL Segment 3 1400 dscl_prog_data->easf_v_bf1_pwl_slope_seg3 = 0x7C8; // S7.3, BF1 Slope PWL Segment 3 = -56 1401 dscl_prog_data->easf_v_bf1_pwl_in_seg4 = 32; // S0.10, BF1 PWL Segment 4 1402 dscl_prog_data->easf_v_bf1_pwl_base_seg4 = 56; // U0.6, BF1 Base PWL Segment 4 1403 dscl_prog_data->easf_v_bf1_pwl_slope_seg4 = 0x7D0; // S7.3, BF1 Slope PWL Segment 4 = -48 1404 dscl_prog_data->easf_v_bf1_pwl_in_seg5 = 48; // S0.10, BF1 PWL Segment 5 1405 dscl_prog_data->easf_v_bf1_pwl_base_seg5 = 50; // U0.6, BF1 Base PWL Segment 5 1406 dscl_prog_data->easf_v_bf1_pwl_slope_seg5 = 0x710; // S7.3, BF1 Slope PWL Segment 5 = -240 1407 dscl_prog_data->easf_v_bf1_pwl_in_seg6 = 64; // S0.10, BF1 PWL Segment 6 1408 dscl_prog_data->easf_v_bf1_pwl_base_seg6 = 20; // U0.6, BF1 Base PWL Segment 6 1409 dscl_prog_data->easf_v_bf1_pwl_slope_seg6 = 0x760; // S7.3, BF1 Slope PWL Segment 6 = -160 1410 dscl_prog_data->easf_v_bf1_pwl_in_seg7 = 80; // S0.10, BF1 PWL Segment 7 1411 dscl_prog_data->easf_v_bf1_pwl_base_seg7 = 0; // U0.6, BF1 Base PWL Segment 7 1412 1413 dscl_prog_data->easf_v_bf3_pwl_in_set0 = 0x000; // FP0.6.6, BF3 Input value PWL Segment 0 1414 dscl_prog_data->easf_v_bf3_pwl_base_set0 = 63; // S0.6, BF3 Base PWL Segment 0 1415 dscl_prog_data->easf_v_bf3_pwl_slope_set0 = 0x12C5; // FP1.6.6, BF3 Slope PWL Segment 0 1416 dscl_prog_data->easf_v_bf3_pwl_in_set1 = 1417 0x0B37; // FP0.6.6, BF3 Input value PWL Segment 1 (0.0078125 * 125^3) 1418 dscl_prog_data->easf_v_bf3_pwl_base_set1 = 62; // S0.6, BF3 Base PWL Segment 1 1419 dscl_prog_data->easf_v_bf3_pwl_slope_set1 = 1420 0x13B8; // FP1.6.6, BF3 Slope PWL Segment 1 1421 dscl_prog_data->easf_v_bf3_pwl_in_set2 = 1422 0x0BB7; // FP0.6.6, BF3 Input value PWL Segment 2 (0.03125 * 125^3) 1423 dscl_prog_data->easf_v_bf3_pwl_base_set2 = 20; // S0.6, BF3 Base PWL Segment 2 1424 dscl_prog_data->easf_v_bf3_pwl_slope_set2 = 1425 0x1356; // FP1.6.6, BF3 Slope PWL Segment 2 1426 dscl_prog_data->easf_v_bf3_pwl_in_set3 = 1427 0x0BF7; // FP0.6.6, BF3 Input value PWL Segment 3 (0.0625 * 125^3) 1428 dscl_prog_data->easf_v_bf3_pwl_base_set3 = 0; // S0.6, BF3 Base PWL Segment 3 1429 dscl_prog_data->easf_v_bf3_pwl_slope_set3 = 1430 0x136B; // FP1.6.6, BF3 Slope PWL Segment 3 1431 dscl_prog_data->easf_v_bf3_pwl_in_set4 = 1432 0x0C37; // FP0.6.6, BF3 Input value PWL Segment 4 (0.125 * 125^3) 1433 dscl_prog_data->easf_v_bf3_pwl_base_set4 = 0x4E; // S0.6, BF3 Base PWL Segment 4 = -50 1434 dscl_prog_data->easf_v_bf3_pwl_slope_set4 = 1435 0x1200; // FP1.6.6, BF3 Slope PWL Segment 4 1436 dscl_prog_data->easf_v_bf3_pwl_in_set5 = 1437 0x0CF7; // FP0.6.6, BF3 Input value PWL Segment 5 (1.0 * 125^3) 1438 dscl_prog_data->easf_v_bf3_pwl_base_set5 = 0x41; // S0.6, BF3 Base PWL Segment 5 = -63 1439 } else { 1440 dscl_prog_data->easf_v_bf2_flat1_gain = 13; // U1.3, BF2 Flat1 Gain control 1441 dscl_prog_data->easf_v_bf2_flat2_gain = 15; // U4.0, BF2 Flat2 Gain control 1442 dscl_prog_data->easf_v_bf2_roc_gain = 14; // U2.2, Rate Of Change control 1443 1444 dscl_prog_data->easf_v_bf1_pwl_in_seg0 = 0x440; // S0.10, BF1 PWL Segment 0 = -960 1445 dscl_prog_data->easf_v_bf1_pwl_base_seg0 = 0; // U0.6, BF1 Base PWL Segment 0 1446 dscl_prog_data->easf_v_bf1_pwl_slope_seg0 = 2; // S7.3, BF1 Slope PWL Segment 0 1447 dscl_prog_data->easf_v_bf1_pwl_in_seg1 = 0x7C4; // S0.10, BF1 PWL Segment 1 = -60 1448 dscl_prog_data->easf_v_bf1_pwl_base_seg1 = 12; // U0.6, BF1 Base PWL Segment 1 1449 dscl_prog_data->easf_v_bf1_pwl_slope_seg1 = 109; // S7.3, BF1 Slope PWL Segment 1 1450 dscl_prog_data->easf_v_bf1_pwl_in_seg2 = 0; // S0.10, BF1 PWL Segment 2 1451 dscl_prog_data->easf_v_bf1_pwl_base_seg2 = 63; // U0.6, BF1 Base PWL Segment 2 1452 dscl_prog_data->easf_v_bf1_pwl_slope_seg2 = 0; // S7.3, BF1 Slope PWL Segment 2 1453 dscl_prog_data->easf_v_bf1_pwl_in_seg3 = 48; // S0.10, BF1 PWL Segment 3 1454 dscl_prog_data->easf_v_bf1_pwl_base_seg3 = 63; // U0.6, BF1 Base PWL Segment 3 1455 dscl_prog_data->easf_v_bf1_pwl_slope_seg3 = 0x7ED; // S7.3, BF1 Slope PWL Segment 3 = -19 1456 dscl_prog_data->easf_v_bf1_pwl_in_seg4 = 96; // S0.10, BF1 PWL Segment 4 1457 dscl_prog_data->easf_v_bf1_pwl_base_seg4 = 56; // U0.6, BF1 Base PWL Segment 4 1458 dscl_prog_data->easf_v_bf1_pwl_slope_seg4 = 0x7F0; // S7.3, BF1 Slope PWL Segment 4 = -16 1459 dscl_prog_data->easf_v_bf1_pwl_in_seg5 = 144; // S0.10, BF1 PWL Segment 5 1460 dscl_prog_data->easf_v_bf1_pwl_base_seg5 = 50; // U0.6, BF1 Base PWL Segment 5 1461 dscl_prog_data->easf_v_bf1_pwl_slope_seg5 = 0x7B0; // S7.3, BF1 Slope PWL Segment 5 = -80 1462 dscl_prog_data->easf_v_bf1_pwl_in_seg6 = 192; // S0.10, BF1 PWL Segment 6 1463 dscl_prog_data->easf_v_bf1_pwl_base_seg6 = 20; // U0.6, BF1 Base PWL Segment 6 1464 dscl_prog_data->easf_v_bf1_pwl_slope_seg6 = 0x7CB; // S7.3, BF1 Slope PWL Segment 6 = -53 1465 dscl_prog_data->easf_v_bf1_pwl_in_seg7 = 240; // S0.10, BF1 PWL Segment 7 1466 dscl_prog_data->easf_v_bf1_pwl_base_seg7 = 0; // U0.6, BF1 Base PWL Segment 7 1467 1468 dscl_prog_data->easf_v_bf3_pwl_in_set0 = 0x000; // FP0.6.6, BF3 Input value PWL Segment 0 1469 dscl_prog_data->easf_v_bf3_pwl_base_set0 = 63; // S0.6, BF3 Base PWL Segment 0 1470 dscl_prog_data->easf_v_bf3_pwl_slope_set0 = 0x0000; // FP1.6.6, BF3 Slope PWL Segment 0 1471 dscl_prog_data->easf_v_bf3_pwl_in_set1 = 1472 0x06C0; // FP0.6.6, BF3 Input value PWL Segment 1 (0.0625) 1473 dscl_prog_data->easf_v_bf3_pwl_base_set1 = 63; // S0.6, BF3 Base PWL Segment 1 1474 dscl_prog_data->easf_v_bf3_pwl_slope_set1 = 0x1896; // FP1.6.6, BF3 Slope PWL Segment 1 1475 dscl_prog_data->easf_v_bf3_pwl_in_set2 = 1476 0x0700; // FP0.6.6, BF3 Input value PWL Segment 2 (0.125) 1477 dscl_prog_data->easf_v_bf3_pwl_base_set2 = 20; // S0.6, BF3 Base PWL Segment 2 1478 dscl_prog_data->easf_v_bf3_pwl_slope_set2 = 0x1810; // FP1.6.6, BF3 Slope PWL Segment 2 1479 dscl_prog_data->easf_v_bf3_pwl_in_set3 = 1480 0x0740; // FP0.6.6, BF3 Input value PWL Segment 3 (0.25) 1481 dscl_prog_data->easf_v_bf3_pwl_base_set3 = 0; // S0.6, BF3 Base PWL Segment 3 1482 dscl_prog_data->easf_v_bf3_pwl_slope_set3 = 1483 0x1878; // FP1.6.6, BF3 Slope PWL Segment 3 1484 dscl_prog_data->easf_v_bf3_pwl_in_set4 = 1485 0x0761; // FP0.6.6, BF3 Input value PWL Segment 4 (0.375) 1486 dscl_prog_data->easf_v_bf3_pwl_base_set4 = 0x44; // S0.6, BF3 Base PWL Segment 4 = -60 1487 dscl_prog_data->easf_v_bf3_pwl_slope_set4 = 0x1760; // FP1.6.6, BF3 Slope PWL Segment 4 1488 dscl_prog_data->easf_v_bf3_pwl_in_set5 = 1489 0x0780; // FP0.6.6, BF3 Input value PWL Segment 5 (0.5) 1490 dscl_prog_data->easf_v_bf3_pwl_base_set5 = 0x41; // S0.6, BF3 Base PWL Segment 5 = -63 1491 } 1492 } else 1493 dscl_prog_data->easf_v_en = false; 1494 1495 if (enable_easf_h) { 1496 dscl_prog_data->easf_h_en = true; 1497 dscl_prog_data->easf_h_ring = 0; 1498 dscl_prog_data->easf_h_sharp_factor = 1; 1499 dscl_prog_data->easf_h_bf1_en = 1500 1; // 1-bit, BF1 calculation enable, 0=disable, 1=enable 1501 dscl_prog_data->easf_h_bf2_mode = 1502 0xF; // 4-bit, BF2 calculation mode 1503 /* 2-bit, BF3 chroma mode correction calculation mode */ 1504 dscl_prog_data->easf_h_bf3_mode = SPL_NAMESPACE(spl_get_h_bf3_mode( 1505 spl_scratch->scl_data.recip_ratios.horz)); 1506 /* FP1.5.10; (2.0) Ring reducer gain for 4 or 6-tap mode [H_REDUCER_GAIN4] */ 1507 dscl_prog_data->easf_h_ringest_eventap_reduceg1 = 1508 SPL_NAMESPACE(spl_get_reducer_gain4(spl_scratch->scl_data.taps.h_taps, 1509 spl_scratch->scl_data.recip_ratios.horz)); 1510 /* FP1.5.10; (2.5) Ring reducer gain for 6-tap mode [V_REDUCER_GAIN6] */ 1511 dscl_prog_data->easf_h_ringest_eventap_reduceg2 = 1512 SPL_NAMESPACE(spl_get_reducer_gain6(spl_scratch->scl_data.taps.h_taps, 1513 spl_scratch->scl_data.recip_ratios.horz)); 1514 /* FP1.5.10; (-0.135742) Ring gain for 6-tap set to -139/1024 */ 1515 dscl_prog_data->easf_h_ringest_eventap_gain1 = 1516 SPL_NAMESPACE(spl_get_gainRing4(spl_scratch->scl_data.taps.h_taps, 1517 spl_scratch->scl_data.recip_ratios.horz)); 1518 /* FP1.5.10; (-0.024414) Ring gain for 6-tap set to -25/1024 */ 1519 dscl_prog_data->easf_h_ringest_eventap_gain2 = 1520 SPL_NAMESPACE(spl_get_gainRing6(spl_scratch->scl_data.taps.h_taps, 1521 spl_scratch->scl_data.recip_ratios.horz)); 1522 dscl_prog_data->easf_h_bf_maxa = 63; //Horz Max BF value A in U0.6 format.Selected if H_FCNTL==0 1523 dscl_prog_data->easf_h_bf_maxb = 63; //Horz Max BF value B in U0.6 format.Selected if H_FCNTL==1 1524 dscl_prog_data->easf_h_bf_mina = 0; //Horz Min BF value B in U0.6 format.Selected if H_FCNTL==0 1525 dscl_prog_data->easf_h_bf_minb = 0; //Horz Min BF value B in U0.6 format.Selected if H_FCNTL==1 1526 if (lls_pref == LLS_PREF_YES) { 1527 dscl_prog_data->easf_h_bf2_flat1_gain = 4; // U1.3, BF2 Flat1 Gain control 1528 dscl_prog_data->easf_h_bf2_flat2_gain = 8; // U4.0, BF2 Flat2 Gain control 1529 dscl_prog_data->easf_h_bf2_roc_gain = 4; // U2.2, Rate Of Change control 1530 1531 dscl_prog_data->easf_h_bf1_pwl_in_seg0 = 0x600; // S0.10, BF1 PWL Segment 0 = -512 1532 dscl_prog_data->easf_h_bf1_pwl_base_seg0 = 0; // U0.6, BF1 Base PWL Segment 0 1533 dscl_prog_data->easf_h_bf1_pwl_slope_seg0 = 3; // S7.3, BF1 Slope PWL Segment 0 1534 dscl_prog_data->easf_h_bf1_pwl_in_seg1 = 0x7EC; // S0.10, BF1 PWL Segment 1 = -20 1535 dscl_prog_data->easf_h_bf1_pwl_base_seg1 = 12; // U0.6, BF1 Base PWL Segment 1 1536 dscl_prog_data->easf_h_bf1_pwl_slope_seg1 = 326; // S7.3, BF1 Slope PWL Segment 1 1537 dscl_prog_data->easf_h_bf1_pwl_in_seg2 = 0; // S0.10, BF1 PWL Segment 2 1538 dscl_prog_data->easf_h_bf1_pwl_base_seg2 = 63; // U0.6, BF1 Base PWL Segment 2 1539 dscl_prog_data->easf_h_bf1_pwl_slope_seg2 = 0; // S7.3, BF1 Slope PWL Segment 2 1540 dscl_prog_data->easf_h_bf1_pwl_in_seg3 = 16; // S0.10, BF1 PWL Segment 3 1541 dscl_prog_data->easf_h_bf1_pwl_base_seg3 = 63; // U0.6, BF1 Base PWL Segment 3 1542 dscl_prog_data->easf_h_bf1_pwl_slope_seg3 = 0x7C8; // S7.3, BF1 Slope PWL Segment 3 = -56 1543 dscl_prog_data->easf_h_bf1_pwl_in_seg4 = 32; // S0.10, BF1 PWL Segment 4 1544 dscl_prog_data->easf_h_bf1_pwl_base_seg4 = 56; // U0.6, BF1 Base PWL Segment 4 1545 dscl_prog_data->easf_h_bf1_pwl_slope_seg4 = 0x7D0; // S7.3, BF1 Slope PWL Segment 4 = -48 1546 dscl_prog_data->easf_h_bf1_pwl_in_seg5 = 48; // S0.10, BF1 PWL Segment 5 1547 dscl_prog_data->easf_h_bf1_pwl_base_seg5 = 50; // U0.6, BF1 Base PWL Segment 5 1548 dscl_prog_data->easf_h_bf1_pwl_slope_seg5 = 0x710; // S7.3, BF1 Slope PWL Segment 5 = -240 1549 dscl_prog_data->easf_h_bf1_pwl_in_seg6 = 64; // S0.10, BF1 PWL Segment 6 1550 dscl_prog_data->easf_h_bf1_pwl_base_seg6 = 20; // U0.6, BF1 Base PWL Segment 6 1551 dscl_prog_data->easf_h_bf1_pwl_slope_seg6 = 0x760; // S7.3, BF1 Slope PWL Segment 6 = -160 1552 dscl_prog_data->easf_h_bf1_pwl_in_seg7 = 80; // S0.10, BF1 PWL Segment 7 1553 dscl_prog_data->easf_h_bf1_pwl_base_seg7 = 0; // U0.6, BF1 Base PWL Segment 7 1554 1555 dscl_prog_data->easf_h_bf3_pwl_in_set0 = 0x000; // FP0.6.6, BF3 Input value PWL Segment 0 1556 dscl_prog_data->easf_h_bf3_pwl_base_set0 = 63; // S0.6, BF3 Base PWL Segment 0 1557 dscl_prog_data->easf_h_bf3_pwl_slope_set0 = 0x12C5; // FP1.6.6, BF3 Slope PWL Segment 0 1558 dscl_prog_data->easf_h_bf3_pwl_in_set1 = 1559 0x0B37; // FP0.6.6, BF3 Input value PWL Segment 1 (0.0078125 * 125^3) 1560 dscl_prog_data->easf_h_bf3_pwl_base_set1 = 62; // S0.6, BF3 Base PWL Segment 1 1561 dscl_prog_data->easf_h_bf3_pwl_slope_set1 = 0x13B8; // FP1.6.6, BF3 Slope PWL Segment 1 1562 dscl_prog_data->easf_h_bf3_pwl_in_set2 = 1563 0x0BB7; // FP0.6.6, BF3 Input value PWL Segment 2 (0.03125 * 125^3) 1564 dscl_prog_data->easf_h_bf3_pwl_base_set2 = 20; // S0.6, BF3 Base PWL Segment 2 1565 dscl_prog_data->easf_h_bf3_pwl_slope_set2 = 0x1356; // FP1.6.6, BF3 Slope PWL Segment 2 1566 dscl_prog_data->easf_h_bf3_pwl_in_set3 = 1567 0x0BF7; // FP0.6.6, BF3 Input value PWL Segment 3 (0.0625 * 125^3) 1568 dscl_prog_data->easf_h_bf3_pwl_base_set3 = 0; // S0.6, BF3 Base PWL Segment 3 1569 dscl_prog_data->easf_h_bf3_pwl_slope_set3 = 0x136B; // FP1.6.6, BF3 Slope PWL Segment 3 1570 dscl_prog_data->easf_h_bf3_pwl_in_set4 = 1571 0x0C37; // FP0.6.6, BF3 Input value PWL Segment 4 (0.125 * 125^3) 1572 dscl_prog_data->easf_h_bf3_pwl_base_set4 = 0x4E; // S0.6, BF3 Base PWL Segment 4 = -50 1573 dscl_prog_data->easf_h_bf3_pwl_slope_set4 = 0x1200; // FP1.6.6, BF3 Slope PWL Segment 4 1574 dscl_prog_data->easf_h_bf3_pwl_in_set5 = 1575 0x0CF7; // FP0.6.6, BF3 Input value PWL Segment 5 (1.0 * 125^3) 1576 dscl_prog_data->easf_h_bf3_pwl_base_set5 = 0x41; // S0.6, BF3 Base PWL Segment 5 = -63 1577 } else { 1578 dscl_prog_data->easf_h_bf2_flat1_gain = 13; // U1.3, BF2 Flat1 Gain control 1579 dscl_prog_data->easf_h_bf2_flat2_gain = 15; // U4.0, BF2 Flat2 Gain control 1580 dscl_prog_data->easf_h_bf2_roc_gain = 14; // U2.2, Rate Of Change control 1581 1582 dscl_prog_data->easf_h_bf1_pwl_in_seg0 = 0x440; // S0.10, BF1 PWL Segment 0 = -960 1583 dscl_prog_data->easf_h_bf1_pwl_base_seg0 = 0; // U0.6, BF1 Base PWL Segment 0 1584 dscl_prog_data->easf_h_bf1_pwl_slope_seg0 = 2; // S7.3, BF1 Slope PWL Segment 0 1585 dscl_prog_data->easf_h_bf1_pwl_in_seg1 = 0x7C4; // S0.10, BF1 PWL Segment 1 = -60 1586 dscl_prog_data->easf_h_bf1_pwl_base_seg1 = 12; // U0.6, BF1 Base PWL Segment 1 1587 dscl_prog_data->easf_h_bf1_pwl_slope_seg1 = 109; // S7.3, BF1 Slope PWL Segment 1 1588 dscl_prog_data->easf_h_bf1_pwl_in_seg2 = 0; // S0.10, BF1 PWL Segment 2 1589 dscl_prog_data->easf_h_bf1_pwl_base_seg2 = 63; // U0.6, BF1 Base PWL Segment 2 1590 dscl_prog_data->easf_h_bf1_pwl_slope_seg2 = 0; // S7.3, BF1 Slope PWL Segment 2 1591 dscl_prog_data->easf_h_bf1_pwl_in_seg3 = 48; // S0.10, BF1 PWL Segment 3 1592 dscl_prog_data->easf_h_bf1_pwl_base_seg3 = 63; // U0.6, BF1 Base PWL Segment 3 1593 dscl_prog_data->easf_h_bf1_pwl_slope_seg3 = 0x7ED; // S7.3, BF1 Slope PWL Segment 3 = -19 1594 dscl_prog_data->easf_h_bf1_pwl_in_seg4 = 96; // S0.10, BF1 PWL Segment 4 1595 dscl_prog_data->easf_h_bf1_pwl_base_seg4 = 56; // U0.6, BF1 Base PWL Segment 4 1596 dscl_prog_data->easf_h_bf1_pwl_slope_seg4 = 0x7F0; // S7.3, BF1 Slope PWL Segment 4 = -16 1597 dscl_prog_data->easf_h_bf1_pwl_in_seg5 = 144; // S0.10, BF1 PWL Segment 5 1598 dscl_prog_data->easf_h_bf1_pwl_base_seg5 = 50; // U0.6, BF1 Base PWL Segment 5 1599 dscl_prog_data->easf_h_bf1_pwl_slope_seg5 = 0x7B0; // S7.3, BF1 Slope PWL Segment 5 = -80 1600 dscl_prog_data->easf_h_bf1_pwl_in_seg6 = 192; // S0.10, BF1 PWL Segment 6 1601 dscl_prog_data->easf_h_bf1_pwl_base_seg6 = 20; // U0.6, BF1 Base PWL Segment 6 1602 dscl_prog_data->easf_h_bf1_pwl_slope_seg6 = 0x7CB; // S7.3, BF1 Slope PWL Segment 6 = -53 1603 dscl_prog_data->easf_h_bf1_pwl_in_seg7 = 240; // S0.10, BF1 PWL Segment 7 1604 dscl_prog_data->easf_h_bf1_pwl_base_seg7 = 0; // U0.6, BF1 Base PWL Segment 7 1605 1606 dscl_prog_data->easf_h_bf3_pwl_in_set0 = 0x000; // FP0.6.6, BF3 Input value PWL Segment 0 1607 dscl_prog_data->easf_h_bf3_pwl_base_set0 = 63; // S0.6, BF3 Base PWL Segment 0 1608 dscl_prog_data->easf_h_bf3_pwl_slope_set0 = 0x0000; // FP1.6.6, BF3 Slope PWL Segment 0 1609 dscl_prog_data->easf_h_bf3_pwl_in_set1 = 1610 0x06C0; // FP0.6.6, BF3 Input value PWL Segment 1 (0.0625) 1611 dscl_prog_data->easf_h_bf3_pwl_base_set1 = 63; // S0.6, BF3 Base PWL Segment 1 1612 dscl_prog_data->easf_h_bf3_pwl_slope_set1 = 0x1896; // FP1.6.6, BF3 Slope PWL Segment 1 1613 dscl_prog_data->easf_h_bf3_pwl_in_set2 = 1614 0x0700; // FP0.6.6, BF3 Input value PWL Segment 2 (0.125) 1615 dscl_prog_data->easf_h_bf3_pwl_base_set2 = 20; // S0.6, BF3 Base PWL Segment 2 1616 dscl_prog_data->easf_h_bf3_pwl_slope_set2 = 0x1810; // FP1.6.6, BF3 Slope PWL Segment 2 1617 dscl_prog_data->easf_h_bf3_pwl_in_set3 = 1618 0x0740; // FP0.6.6, BF3 Input value PWL Segment 3 (0.25) 1619 dscl_prog_data->easf_h_bf3_pwl_base_set3 = 0; // S0.6, BF3 Base PWL Segment 3 1620 dscl_prog_data->easf_h_bf3_pwl_slope_set3 = 0x1878; // FP1.6.6, BF3 Slope PWL Segment 3 1621 dscl_prog_data->easf_h_bf3_pwl_in_set4 = 1622 0x0761; // FP0.6.6, BF3 Input value PWL Segment 4 (0.375) 1623 dscl_prog_data->easf_h_bf3_pwl_base_set4 = 0x44; // S0.6, BF3 Base PWL Segment 4 = -60 1624 dscl_prog_data->easf_h_bf3_pwl_slope_set4 = 0x1760; // FP1.6.6, BF3 Slope PWL Segment 4 1625 dscl_prog_data->easf_h_bf3_pwl_in_set5 = 1626 0x0780; // FP0.6.6, BF3 Input value PWL Segment 5 (0.5) 1627 dscl_prog_data->easf_h_bf3_pwl_base_set5 = 0x41; // S0.6, BF3 Base PWL Segment 5 = -63 1628 } // if (lls_pref == LLS_PREF_YES) 1629 } else 1630 dscl_prog_data->easf_h_en = false; 1631 1632 if (lls_pref == LLS_PREF_YES) { 1633 dscl_prog_data->easf_ltonl_en = 1; // Linear input 1634 if ((setup == HDR_L) && (spl_is_rgb8(format))) { 1635 /* Calculate C0-C3 coefficients based on HDR multiplier */ 1636 spl_calculate_c0_c3_hdr(dscl_prog_data, sdr_white_level_nits); 1637 } else { // HDR_L ( DWM ) and SDR_L 1638 dscl_prog_data->easf_matrix_c0 = 1639 0x4EF7; // fp1.5.10, C0 coefficient (LN_rec709: 0.2126 * (2^14)/125 = 27.86590720) 1640 dscl_prog_data->easf_matrix_c1 = 1641 0x55DC; // fp1.5.10, C1 coefficient (LN_rec709: 0.7152 * (2^14)/125 = 93.74269440) 1642 dscl_prog_data->easf_matrix_c2 = 1643 0x48BB; // fp1.5.10, C2 coefficient (LN_rec709: 0.0722 * (2^14)/125 = 9.46339840) 1644 dscl_prog_data->easf_matrix_c3 = 1645 0x0; // fp1.5.10, C3 coefficient 1646 } 1647 } else { 1648 dscl_prog_data->easf_ltonl_en = 0; // Non-Linear input 1649 dscl_prog_data->easf_matrix_c0 = 1650 0x3434; // fp1.5.10, C0 coefficient (LN_BT2020: 0.262695312500000) 1651 dscl_prog_data->easf_matrix_c1 = 1652 0x396D; // fp1.5.10, C1 coefficient (LN_BT2020: 0.678222656250000) 1653 dscl_prog_data->easf_matrix_c2 = 1654 0x2B97; // fp1.5.10, C2 coefficient (LN_BT2020: 0.059295654296875) 1655 dscl_prog_data->easf_matrix_c3 = 1656 0x0; // fp1.5.10, C3 coefficient 1657 } 1658 1659 if (spl_is_subsampled_format(format)) { /* TODO: 0 = RGB, 1 = YUV */ 1660 dscl_prog_data->easf_matrix_mode = 1; 1661 /* 1662 * 2-bit, BF3 chroma mode correction calculation mode 1663 * Needs to be disabled for YUV420 mode 1664 * Override lookup value 1665 */ 1666 dscl_prog_data->easf_v_bf3_mode = 0; 1667 dscl_prog_data->easf_h_bf3_mode = 0; 1668 } else 1669 dscl_prog_data->easf_matrix_mode = 0; 1670 1671 } 1672 1673 /*Set isharp noise detection */ 1674 static void spl_set_isharp_noise_det_mode(struct dscl_prog_data *dscl_prog_data, 1675 const struct spl_scaler_data *data) 1676 { 1677 // ISHARP_NOISEDET_MODE 1678 // 0: 3x5 as VxH 1679 // 1: 4x5 as VxH 1680 // 2: 1681 // 3: 5x5 as VxH 1682 if (data->taps.v_taps == 6) 1683 dscl_prog_data->isharp_noise_det.mode = 3; 1684 else if (data->taps.v_taps == 4) 1685 dscl_prog_data->isharp_noise_det.mode = 1; 1686 else if (data->taps.v_taps == 3) 1687 dscl_prog_data->isharp_noise_det.mode = 0; 1688 }; 1689 /* Set Sharpener data */ 1690 static void spl_set_isharp_data(struct dscl_prog_data *dscl_prog_data, 1691 struct adaptive_sharpness adp_sharpness, bool enable_isharp, 1692 enum linear_light_scaling lls_pref, enum spl_pixel_format format, 1693 const struct spl_scaler_data *data, struct spl_fixed31_32 ratio, 1694 enum system_setup setup, enum scale_to_sharpness_policy scale_to_sharpness_policy) 1695 { 1696 /* Turn off sharpener if not required */ 1697 if (!enable_isharp) { 1698 dscl_prog_data->isharp_en = 0; 1699 return; 1700 } 1701 1702 SPL_NAMESPACE(spl_build_isharp_1dlut_from_reference_curve(ratio, setup, adp_sharpness, 1703 scale_to_sharpness_policy)); 1704 memcpy(dscl_prog_data->isharp_delta, SPL_NAMESPACE(spl_get_pregen_filter_isharp_1D_lut(setup)), 1705 sizeof(uint32_t) * ISHARP_LUT_TABLE_SIZE); 1706 dscl_prog_data->sharpness_level = adp_sharpness.sharpness_level; 1707 1708 dscl_prog_data->isharp_en = 1; // ISHARP_EN 1709 // Set ISHARP_NOISEDET_MODE if htaps = 6-tap 1710 if (data->taps.h_taps == 6) { 1711 dscl_prog_data->isharp_noise_det.enable = 1; /* ISHARP_NOISEDET_EN */ 1712 spl_set_isharp_noise_det_mode(dscl_prog_data, data); /* ISHARP_NOISEDET_MODE */ 1713 } else 1714 dscl_prog_data->isharp_noise_det.enable = 0; // ISHARP_NOISEDET_EN 1715 // Program noise detection threshold 1716 dscl_prog_data->isharp_noise_det.uthreshold = 24; // ISHARP_NOISEDET_UTHRE 1717 dscl_prog_data->isharp_noise_det.dthreshold = 4; // ISHARP_NOISEDET_DTHRE 1718 // Program noise detection gain 1719 dscl_prog_data->isharp_noise_det.pwl_start_in = 3; // ISHARP_NOISEDET_PWL_START_IN 1720 dscl_prog_data->isharp_noise_det.pwl_end_in = 13; // ISHARP_NOISEDET_PWL_END_IN 1721 dscl_prog_data->isharp_noise_det.pwl_slope = 1623; // ISHARP_NOISEDET_PWL_SLOPE 1722 1723 if (lls_pref == LLS_PREF_NO) /* ISHARP_FMT_MODE */ 1724 dscl_prog_data->isharp_fmt.mode = 1; 1725 else 1726 dscl_prog_data->isharp_fmt.mode = 0; 1727 1728 dscl_prog_data->isharp_fmt.norm = 0x3C00; // ISHARP_FMT_NORM 1729 dscl_prog_data->isharp_lba.mode = 0; // ISHARP_LBA_MODE 1730 1731 if (setup == SDR_L) { 1732 // ISHARP_LBA_PWL_SEG0: ISHARP Local Brightness Adjustment PWL Segment 0 1733 dscl_prog_data->isharp_lba.in_seg[0] = 0; // ISHARP LBA PWL for Seg 0. INPUT value in U0.10 format 1734 dscl_prog_data->isharp_lba.base_seg[0] = 0; // ISHARP LBA PWL for Seg 0. BASE value in U0.6 format 1735 dscl_prog_data->isharp_lba.slope_seg[0] = 62; // ISHARP LBA for Seg 0. SLOPE value in S5.3 format 1736 // ISHARP_LBA_PWL_SEG1: ISHARP LBA PWL Segment 1 1737 dscl_prog_data->isharp_lba.in_seg[1] = 130; // ISHARP LBA PWL for Seg 1. INPUT value in U0.10 format 1738 dscl_prog_data->isharp_lba.base_seg[1] = 63; // ISHARP LBA PWL for Seg 1. BASE value in U0.6 format 1739 dscl_prog_data->isharp_lba.slope_seg[1] = 0; // ISHARP LBA for Seg 1. SLOPE value in S5.3 format 1740 // ISHARP_LBA_PWL_SEG2: ISHARP LBA PWL Segment 2 1741 dscl_prog_data->isharp_lba.in_seg[2] = 450; // ISHARP LBA PWL for Seg 2. INPUT value in U0.10 format 1742 dscl_prog_data->isharp_lba.base_seg[2] = 63; // ISHARP LBA PWL for Seg 2. BASE value in U0.6 format 1743 dscl_prog_data->isharp_lba.slope_seg[2] = 0x18D; // ISHARP LBA for Seg 2. SLOPE value in S5.3 format = -115 1744 // ISHARP_LBA_PWL_SEG3: ISHARP LBA PWL Segment 3 1745 dscl_prog_data->isharp_lba.in_seg[3] = 520; // ISHARP LBA PWL for Seg 3.INPUT value in U0.10 format 1746 dscl_prog_data->isharp_lba.base_seg[3] = 0; // ISHARP LBA PWL for Seg 3. BASE value in U0.6 format 1747 dscl_prog_data->isharp_lba.slope_seg[3] = 0; // ISHARP LBA for Seg 3. SLOPE value in S5.3 format 1748 // ISHARP_LBA_PWL_SEG4: ISHARP LBA PWL Segment 4 1749 dscl_prog_data->isharp_lba.in_seg[4] = 520; // ISHARP LBA PWL for Seg 4.INPUT value in U0.10 format 1750 dscl_prog_data->isharp_lba.base_seg[4] = 0; // ISHARP LBA PWL for Seg 4. BASE value in U0.6 format 1751 dscl_prog_data->isharp_lba.slope_seg[4] = 0; // ISHARP LBA for Seg 4. SLOPE value in S5.3 format 1752 // ISHARP_LBA_PWL_SEG5: ISHARP LBA PWL Segment 5 1753 dscl_prog_data->isharp_lba.in_seg[5] = 520; // ISHARP LBA PWL for Seg 5.INPUT value in U0.10 format 1754 dscl_prog_data->isharp_lba.base_seg[5] = 0; // ISHARP LBA PWL for Seg 5. BASE value in U0.6 format 1755 } else if (setup == HDR_L) { 1756 // ISHARP_LBA_PWL_SEG0: ISHARP Local Brightness Adjustment PWL Segment 0 1757 dscl_prog_data->isharp_lba.in_seg[0] = 0; // ISHARP LBA PWL for Seg 0. INPUT value in U0.10 format 1758 dscl_prog_data->isharp_lba.base_seg[0] = 0; // ISHARP LBA PWL for Seg 0. BASE value in U0.6 format 1759 dscl_prog_data->isharp_lba.slope_seg[0] = 32; // ISHARP LBA for Seg 0. SLOPE value in S5.3 format 1760 // ISHARP_LBA_PWL_SEG1: ISHARP LBA PWL Segment 1 1761 dscl_prog_data->isharp_lba.in_seg[1] = 254; // ISHARP LBA PWL for Seg 1. INPUT value in U0.10 format 1762 dscl_prog_data->isharp_lba.base_seg[1] = 63; // ISHARP LBA PWL for Seg 1. BASE value in U0.6 format 1763 dscl_prog_data->isharp_lba.slope_seg[1] = 0; // ISHARP LBA for Seg 1. SLOPE value in S5.3 format 1764 // ISHARP_LBA_PWL_SEG2: ISHARP LBA PWL Segment 2 1765 dscl_prog_data->isharp_lba.in_seg[2] = 559; // ISHARP LBA PWL for Seg 2. INPUT value in U0.10 format 1766 dscl_prog_data->isharp_lba.base_seg[2] = 63; // ISHARP LBA PWL for Seg 2. BASE value in U0.6 format 1767 dscl_prog_data->isharp_lba.slope_seg[2] = 0x10C; // ISHARP LBA for Seg 2. SLOPE value in S5.3 format = -244 1768 // ISHARP_LBA_PWL_SEG3: ISHARP LBA PWL Segment 3 1769 dscl_prog_data->isharp_lba.in_seg[3] = 592; // ISHARP LBA PWL for Seg 3.INPUT value in U0.10 format 1770 dscl_prog_data->isharp_lba.base_seg[3] = 0; // ISHARP LBA PWL for Seg 3. BASE value in U0.6 format 1771 dscl_prog_data->isharp_lba.slope_seg[3] = 0; // ISHARP LBA for Seg 3. SLOPE value in S5.3 format 1772 // ISHARP_LBA_PWL_SEG4: ISHARP LBA PWL Segment 4 1773 dscl_prog_data->isharp_lba.in_seg[4] = 1023; // ISHARP LBA PWL for Seg 4.INPUT value in U0.10 format 1774 dscl_prog_data->isharp_lba.base_seg[4] = 0; // ISHARP LBA PWL for Seg 4. BASE value in U0.6 format 1775 dscl_prog_data->isharp_lba.slope_seg[4] = 0; // ISHARP LBA for Seg 4. SLOPE value in S5.3 format 1776 // ISHARP_LBA_PWL_SEG5: ISHARP LBA PWL Segment 5 1777 dscl_prog_data->isharp_lba.in_seg[5] = 1023; // ISHARP LBA PWL for Seg 5.INPUT value in U0.10 format 1778 dscl_prog_data->isharp_lba.base_seg[5] = 0; // ISHARP LBA PWL for Seg 5. BASE value in U0.6 format 1779 } else { 1780 // ISHARP_LBA_PWL_SEG0: ISHARP Local Brightness Adjustment PWL Segment 0 1781 dscl_prog_data->isharp_lba.in_seg[0] = 0; // ISHARP LBA PWL for Seg 0. INPUT value in U0.10 format 1782 dscl_prog_data->isharp_lba.base_seg[0] = 0; // ISHARP LBA PWL for Seg 0. BASE value in U0.6 format 1783 dscl_prog_data->isharp_lba.slope_seg[0] = 40; // ISHARP LBA for Seg 0. SLOPE value in S5.3 format 1784 // ISHARP_LBA_PWL_SEG1: ISHARP LBA PWL Segment 1 1785 dscl_prog_data->isharp_lba.in_seg[1] = 204; // ISHARP LBA PWL for Seg 1. INPUT value in U0.10 format 1786 dscl_prog_data->isharp_lba.base_seg[1] = 63; // ISHARP LBA PWL for Seg 1. BASE value in U0.6 format 1787 dscl_prog_data->isharp_lba.slope_seg[1] = 0; // ISHARP LBA for Seg 1. SLOPE value in S5.3 format 1788 // ISHARP_LBA_PWL_SEG2: ISHARP LBA PWL Segment 2 1789 dscl_prog_data->isharp_lba.in_seg[2] = 818; // ISHARP LBA PWL for Seg 2. INPUT value in U0.10 format 1790 dscl_prog_data->isharp_lba.base_seg[2] = 63; // ISHARP LBA PWL for Seg 2. BASE value in U0.6 format 1791 dscl_prog_data->isharp_lba.slope_seg[2] = 0x1D9; // ISHARP LBA for Seg 2. SLOPE value in S5.3 format = -39 1792 // ISHARP_LBA_PWL_SEG3: ISHARP LBA PWL Segment 3 1793 dscl_prog_data->isharp_lba.in_seg[3] = 1023; // ISHARP LBA PWL for Seg 3.INPUT value in U0.10 format 1794 dscl_prog_data->isharp_lba.base_seg[3] = 0; // ISHARP LBA PWL for Seg 3. BASE value in U0.6 format 1795 dscl_prog_data->isharp_lba.slope_seg[3] = 0; // ISHARP LBA for Seg 3. SLOPE value in S5.3 format 1796 // ISHARP_LBA_PWL_SEG4: ISHARP LBA PWL Segment 4 1797 dscl_prog_data->isharp_lba.in_seg[4] = 1023; // ISHARP LBA PWL for Seg 4.INPUT value in U0.10 format 1798 dscl_prog_data->isharp_lba.base_seg[4] = 0; // ISHARP LBA PWL for Seg 4. BASE value in U0.6 format 1799 dscl_prog_data->isharp_lba.slope_seg[4] = 0; // ISHARP LBA for Seg 4. SLOPE value in S5.3 format 1800 // ISHARP_LBA_PWL_SEG5: ISHARP LBA PWL Segment 5 1801 dscl_prog_data->isharp_lba.in_seg[5] = 1023; // ISHARP LBA PWL for Seg 5.INPUT value in U0.10 format 1802 dscl_prog_data->isharp_lba.base_seg[5] = 0; // ISHARP LBA PWL for Seg 5. BASE value in U0.6 format 1803 } 1804 1805 // Program the nldelta soft clip values 1806 if (lls_pref == LLS_PREF_YES) { 1807 dscl_prog_data->isharp_nldelta_sclip.enable_p = 0; /* ISHARP_NLDELTA_SCLIP_EN_P */ 1808 dscl_prog_data->isharp_nldelta_sclip.pivot_p = 0; /* ISHARP_NLDELTA_SCLIP_PIVOT_P */ 1809 dscl_prog_data->isharp_nldelta_sclip.slope_p = 0; /* ISHARP_NLDELTA_SCLIP_SLOPE_P */ 1810 dscl_prog_data->isharp_nldelta_sclip.enable_n = 1; /* ISHARP_NLDELTA_SCLIP_EN_N */ 1811 dscl_prog_data->isharp_nldelta_sclip.pivot_n = 71; /* ISHARP_NLDELTA_SCLIP_PIVOT_N */ 1812 dscl_prog_data->isharp_nldelta_sclip.slope_n = 16; /* ISHARP_NLDELTA_SCLIP_SLOPE_N */ 1813 } else { 1814 dscl_prog_data->isharp_nldelta_sclip.enable_p = 1; /* ISHARP_NLDELTA_SCLIP_EN_P */ 1815 dscl_prog_data->isharp_nldelta_sclip.pivot_p = 70; /* ISHARP_NLDELTA_SCLIP_PIVOT_P */ 1816 dscl_prog_data->isharp_nldelta_sclip.slope_p = 24; /* ISHARP_NLDELTA_SCLIP_SLOPE_P */ 1817 dscl_prog_data->isharp_nldelta_sclip.enable_n = 1; /* ISHARP_NLDELTA_SCLIP_EN_N */ 1818 dscl_prog_data->isharp_nldelta_sclip.pivot_n = 70; /* ISHARP_NLDELTA_SCLIP_PIVOT_N */ 1819 dscl_prog_data->isharp_nldelta_sclip.slope_n = 24; /* ISHARP_NLDELTA_SCLIP_SLOPE_N */ 1820 } 1821 1822 // Set the values as per lookup table 1823 SPL_NAMESPACE(spl_set_blur_scale_data(dscl_prog_data, data)); 1824 } 1825 1826 /* Calculate recout, scaling ratio, and viewport, then get optimal number of taps */ 1827 static bool spl_calculate_number_of_taps(struct spl_in *spl_in, struct spl_scratch *spl_scratch, struct spl_out *spl_out, 1828 bool *enable_easf_v, bool *enable_easf_h, bool *enable_isharp) 1829 { 1830 bool res = false; 1831 1832 memset(spl_scratch, 0, sizeof(struct spl_scratch)); 1833 spl_scratch->scl_data.h_active = spl_in->h_active; 1834 spl_scratch->scl_data.v_active = spl_in->v_active; 1835 1836 // All SPL calls 1837 /* recout calculation */ 1838 /* depends on h_active */ 1839 spl_calculate_recout(spl_in, spl_scratch, spl_out); 1840 /* depends on pixel format */ 1841 spl_calculate_scaling_ratios(spl_in, spl_scratch, spl_out); 1842 /* Adjust recout for opp if needed */ 1843 spl_opp_adjust_rect(&spl_scratch->scl_data.recout, &spl_in->basic_in.opp_recout_adjust); 1844 /* depends on scaling ratios and recout, does not calculate offset yet */ 1845 spl_calculate_viewport_size(spl_in, spl_scratch); 1846 1847 res = spl_get_optimal_number_of_taps( 1848 spl_in->basic_out.max_downscale_src_width, spl_in, 1849 spl_scratch, &spl_in->scaling_quality, enable_easf_v, 1850 enable_easf_h, enable_isharp); 1851 return res; 1852 } 1853 1854 /* Calculate scaler parameters */ 1855 bool SPL_NAMESPACE(spl_calculate_scaler_params(struct spl_in *spl_in, struct spl_out *spl_out)) 1856 { 1857 bool res = false; 1858 bool enable_easf_v = false; 1859 bool enable_easf_h = false; 1860 int vratio = 0; 1861 int hratio = 0; 1862 struct spl_scratch spl_scratch; 1863 struct spl_fixed31_32 isharp_scale_ratio; 1864 enum system_setup setup; 1865 bool enable_isharp = false; 1866 const struct spl_scaler_data *data = &spl_scratch.scl_data; 1867 1868 res = spl_calculate_number_of_taps(spl_in, &spl_scratch, spl_out, 1869 &enable_easf_v, &enable_easf_h, &enable_isharp); 1870 1871 /* 1872 * Depends on recout, scaling ratios, h_active and taps 1873 * May need to re-check lb size after this in some obscure scenario 1874 */ 1875 if (res) 1876 spl_calculate_inits_and_viewports(spl_in, &spl_scratch); 1877 // Handle 3d recout 1878 spl_handle_3d_recout(spl_in, &spl_scratch.scl_data.recout); 1879 // Clamp 1880 spl_clamp_viewport(&spl_scratch.scl_data.viewport, spl_in->min_viewport_size); 1881 1882 // Save all calculated parameters in dscl_prog_data structure to program hw registers 1883 spl_set_dscl_prog_data(spl_in, &spl_scratch, spl_out, enable_easf_v, enable_easf_h, enable_isharp); 1884 1885 if (!res) 1886 return res; 1887 1888 if (spl_in->lls_pref == LLS_PREF_YES) { 1889 if (spl_in->is_hdr_on) 1890 setup = HDR_L; 1891 else 1892 setup = SDR_L; 1893 } else { 1894 if (spl_in->is_hdr_on) 1895 setup = HDR_NL; 1896 else 1897 setup = SDR_NL; 1898 } 1899 1900 // Set EASF 1901 spl_set_easf_data(&spl_scratch, spl_out, enable_easf_v, enable_easf_h, spl_in->lls_pref, 1902 spl_in->basic_in.format, setup, spl_in->sdr_white_level_nits); 1903 1904 // Set iSHARP 1905 vratio = spl_fixpt_ceil(spl_scratch.scl_data.ratios.vert); 1906 hratio = spl_fixpt_ceil(spl_scratch.scl_data.ratios.horz); 1907 if (vratio <= hratio) 1908 isharp_scale_ratio = spl_scratch.scl_data.recip_ratios.vert; 1909 else 1910 isharp_scale_ratio = spl_scratch.scl_data.recip_ratios.horz; 1911 1912 spl_set_isharp_data(spl_out->dscl_prog_data, spl_in->adaptive_sharpness, enable_isharp, 1913 spl_in->lls_pref, spl_in->basic_in.format, data, isharp_scale_ratio, setup, 1914 spl_in->debug.scale_to_sharpness_policy); 1915 1916 return res; 1917 } 1918 1919 /* External interface to get number of taps only */ 1920 bool SPL_NAMESPACE(spl_get_number_of_taps(struct spl_in *spl_in, struct spl_out *spl_out)) 1921 { 1922 bool res = false; 1923 bool enable_easf_v = false; 1924 bool enable_easf_h = false; 1925 bool enable_isharp = false; 1926 struct spl_scratch spl_scratch; 1927 struct dscl_prog_data *dscl_prog_data = spl_out->dscl_prog_data; 1928 const struct spl_scaler_data *data = &spl_scratch.scl_data; 1929 1930 res = spl_calculate_number_of_taps(spl_in, &spl_scratch, spl_out, 1931 &enable_easf_v, &enable_easf_h, &enable_isharp); 1932 spl_set_taps_data(dscl_prog_data, data); 1933 return res; 1934 } 1935