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