1 /* 2 * Copyright 2016 Advanced Micro Devices, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: AMD 23 * 24 */ 25 26 #include <linux/mm.h> 27 #include <linux/slab.h> 28 29 #include "dc.h" 30 #include "opp.h" 31 #include "color_gamma.h" 32 33 #define NUM_PTS_IN_REGION 16 34 #define NUM_REGIONS 32 35 #define MAX_HW_POINTS (NUM_PTS_IN_REGION*NUM_REGIONS) 36 37 static struct hw_x_point coordinates_x[MAX_HW_POINTS + 2]; 38 39 static struct fixed31_32 pq_table[MAX_HW_POINTS + 2]; 40 static struct fixed31_32 de_pq_table[MAX_HW_POINTS + 2]; 41 42 // these are helpers for calculations to reduce stack usage 43 // do not depend on these being preserved across calls 44 static struct fixed31_32 scratch_1; 45 static struct fixed31_32 scratch_2; 46 static struct translate_from_linear_space_args scratch_gamma_args; 47 48 /* Helper to optimize gamma calculation, only use in translate_from_linear, in 49 * particular the dc_fixpt_pow function which is very expensive 50 * The idea is that our regions for X points are exponential and currently they all use 51 * the same number of points (NUM_PTS_IN_REGION) and in each region every point 52 * is exactly 2x the one at the same index in the previous region. In other words 53 * X[i] = 2 * X[i-NUM_PTS_IN_REGION] for i>=16 54 * The other fact is that (2x)^gamma = 2^gamma * x^gamma 55 * So we compute and save x^gamma for the first 16 regions, and for every next region 56 * just multiply with 2^gamma which can be computed once, and save the result so we 57 * recursively compute all the values. 58 */ 59 static struct fixed31_32 pow_buffer[NUM_PTS_IN_REGION]; 60 static struct fixed31_32 gamma_of_2; // 2^gamma 61 int pow_buffer_ptr = -1; 62 /*sRGB 709 2.2 2.4 P3*/ 63 static const int32_t gamma_numerator01[] = { 31308, 180000, 0, 0, 0}; 64 static const int32_t gamma_numerator02[] = { 12920, 4500, 0, 0, 0}; 65 static const int32_t gamma_numerator03[] = { 55, 99, 0, 0, 0}; 66 static const int32_t gamma_numerator04[] = { 55, 99, 0, 0, 0}; 67 static const int32_t gamma_numerator05[] = { 2400, 2200, 2200, 2400, 2600}; 68 69 static bool pq_initialized; /* = false; */ 70 static bool de_pq_initialized; /* = false; */ 71 72 /* one-time setup of X points */ 73 void setup_x_points_distribution(void) 74 { 75 struct fixed31_32 region_size = dc_fixpt_from_int(128); 76 int32_t segment; 77 uint32_t seg_offset; 78 uint32_t index; 79 struct fixed31_32 increment; 80 81 coordinates_x[MAX_HW_POINTS].x = region_size; 82 coordinates_x[MAX_HW_POINTS + 1].x = region_size; 83 84 for (segment = 6; segment > (6 - NUM_REGIONS); segment--) { 85 region_size = dc_fixpt_div_int(region_size, 2); 86 increment = dc_fixpt_div_int(region_size, 87 NUM_PTS_IN_REGION); 88 seg_offset = (segment + (NUM_REGIONS - 7)) * NUM_PTS_IN_REGION; 89 coordinates_x[seg_offset].x = region_size; 90 91 for (index = seg_offset + 1; 92 index < seg_offset + NUM_PTS_IN_REGION; 93 index++) { 94 coordinates_x[index].x = dc_fixpt_add 95 (coordinates_x[index-1].x, increment); 96 } 97 } 98 } 99 100 void log_x_points_distribution(struct dal_logger *logger) 101 { 102 int i = 0; 103 104 if (logger != NULL) { 105 LOG_GAMMA_WRITE("Log X Distribution\n"); 106 107 for (i = 0; i < MAX_HW_POINTS; i++) 108 LOG_GAMMA_WRITE("%llu\n", coordinates_x[i].x.value); 109 } 110 } 111 112 static void compute_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y) 113 { 114 /* consts for PQ gamma formula. */ 115 const struct fixed31_32 m1 = 116 dc_fixpt_from_fraction(159301758, 1000000000); 117 const struct fixed31_32 m2 = 118 dc_fixpt_from_fraction(7884375, 100000); 119 const struct fixed31_32 c1 = 120 dc_fixpt_from_fraction(8359375, 10000000); 121 const struct fixed31_32 c2 = 122 dc_fixpt_from_fraction(188515625, 10000000); 123 const struct fixed31_32 c3 = 124 dc_fixpt_from_fraction(186875, 10000); 125 126 struct fixed31_32 l_pow_m1; 127 struct fixed31_32 base; 128 129 if (dc_fixpt_lt(in_x, dc_fixpt_zero)) 130 in_x = dc_fixpt_zero; 131 132 l_pow_m1 = dc_fixpt_pow(in_x, m1); 133 base = dc_fixpt_div( 134 dc_fixpt_add(c1, 135 (dc_fixpt_mul(c2, l_pow_m1))), 136 dc_fixpt_add(dc_fixpt_one, 137 (dc_fixpt_mul(c3, l_pow_m1)))); 138 *out_y = dc_fixpt_pow(base, m2); 139 } 140 141 static void compute_de_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y) 142 { 143 /* consts for dePQ gamma formula. */ 144 const struct fixed31_32 m1 = 145 dc_fixpt_from_fraction(159301758, 1000000000); 146 const struct fixed31_32 m2 = 147 dc_fixpt_from_fraction(7884375, 100000); 148 const struct fixed31_32 c1 = 149 dc_fixpt_from_fraction(8359375, 10000000); 150 const struct fixed31_32 c2 = 151 dc_fixpt_from_fraction(188515625, 10000000); 152 const struct fixed31_32 c3 = 153 dc_fixpt_from_fraction(186875, 10000); 154 155 struct fixed31_32 l_pow_m1; 156 struct fixed31_32 base, div; 157 struct fixed31_32 base2; 158 159 160 if (dc_fixpt_lt(in_x, dc_fixpt_zero)) 161 in_x = dc_fixpt_zero; 162 163 l_pow_m1 = dc_fixpt_pow(in_x, 164 dc_fixpt_div(dc_fixpt_one, m2)); 165 base = dc_fixpt_sub(l_pow_m1, c1); 166 167 div = dc_fixpt_sub(c2, dc_fixpt_mul(c3, l_pow_m1)); 168 169 base2 = dc_fixpt_div(base, div); 170 //avoid complex numbers 171 if (dc_fixpt_lt(base2, dc_fixpt_zero)) 172 base2 = dc_fixpt_sub(dc_fixpt_zero, base2); 173 174 175 *out_y = dc_fixpt_pow(base2, dc_fixpt_div(dc_fixpt_one, m1)); 176 177 } 178 179 180 /*de gamma, none linear to linear*/ 181 static void compute_hlg_eotf(struct fixed31_32 in_x, 182 struct fixed31_32 *out_y, 183 uint32_t sdr_white_level, uint32_t max_luminance_nits) 184 { 185 struct fixed31_32 a; 186 struct fixed31_32 b; 187 struct fixed31_32 c; 188 struct fixed31_32 threshold; 189 struct fixed31_32 x; 190 191 struct fixed31_32 scaling_factor = 192 dc_fixpt_from_fraction(max_luminance_nits, sdr_white_level); 193 a = dc_fixpt_from_fraction(17883277, 100000000); 194 b = dc_fixpt_from_fraction(28466892, 100000000); 195 c = dc_fixpt_from_fraction(55991073, 100000000); 196 threshold = dc_fixpt_from_fraction(1, 2); 197 198 if (dc_fixpt_lt(in_x, threshold)) { 199 x = dc_fixpt_mul(in_x, in_x); 200 x = dc_fixpt_div_int(x, 3); 201 } else { 202 x = dc_fixpt_sub(in_x, c); 203 x = dc_fixpt_div(x, a); 204 x = dc_fixpt_exp(x); 205 x = dc_fixpt_add(x, b); 206 x = dc_fixpt_div_int(x, 12); 207 } 208 *out_y = dc_fixpt_mul(x, scaling_factor); 209 210 } 211 212 /*re gamma, linear to none linear*/ 213 static void compute_hlg_oetf(struct fixed31_32 in_x, struct fixed31_32 *out_y, 214 uint32_t sdr_white_level, uint32_t max_luminance_nits) 215 { 216 struct fixed31_32 a; 217 struct fixed31_32 b; 218 struct fixed31_32 c; 219 struct fixed31_32 threshold; 220 struct fixed31_32 x; 221 222 struct fixed31_32 scaling_factor = 223 dc_fixpt_from_fraction(sdr_white_level, max_luminance_nits); 224 a = dc_fixpt_from_fraction(17883277, 100000000); 225 b = dc_fixpt_from_fraction(28466892, 100000000); 226 c = dc_fixpt_from_fraction(55991073, 100000000); 227 threshold = dc_fixpt_from_fraction(1, 12); 228 x = dc_fixpt_mul(in_x, scaling_factor); 229 230 231 if (dc_fixpt_lt(x, threshold)) { 232 x = dc_fixpt_mul(x, dc_fixpt_from_fraction(3, 1)); 233 *out_y = dc_fixpt_pow(x, dc_fixpt_half); 234 } else { 235 x = dc_fixpt_mul(x, dc_fixpt_from_fraction(12, 1)); 236 x = dc_fixpt_sub(x, b); 237 x = dc_fixpt_log(x); 238 x = dc_fixpt_mul(a, x); 239 *out_y = dc_fixpt_add(x, c); 240 } 241 } 242 243 244 /* one-time pre-compute PQ values - only for sdr_white_level 80 */ 245 void precompute_pq(void) 246 { 247 int i; 248 struct fixed31_32 x; 249 const struct hw_x_point *coord_x = coordinates_x + 32; 250 struct fixed31_32 scaling_factor = 251 dc_fixpt_from_fraction(80, 10000); 252 253 /* pow function has problems with arguments too small */ 254 for (i = 0; i < 32; i++) 255 pq_table[i] = dc_fixpt_zero; 256 257 for (i = 32; i <= MAX_HW_POINTS; i++) { 258 x = dc_fixpt_mul(coord_x->x, scaling_factor); 259 compute_pq(x, &pq_table[i]); 260 ++coord_x; 261 } 262 } 263 264 /* one-time pre-compute dePQ values - only for max pixel value 125 FP16 */ 265 void precompute_de_pq(void) 266 { 267 int i; 268 struct fixed31_32 y; 269 uint32_t begin_index, end_index; 270 271 struct fixed31_32 scaling_factor = dc_fixpt_from_int(125); 272 273 /* X points is 2^-25 to 2^7 274 * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions 275 */ 276 begin_index = 13 * NUM_PTS_IN_REGION; 277 end_index = begin_index + 12 * NUM_PTS_IN_REGION; 278 279 for (i = 0; i <= begin_index; i++) 280 de_pq_table[i] = dc_fixpt_zero; 281 282 for (; i <= end_index; i++) { 283 compute_de_pq(coordinates_x[i].x, &y); 284 de_pq_table[i] = dc_fixpt_mul(y, scaling_factor); 285 } 286 287 for (; i <= MAX_HW_POINTS; i++) 288 de_pq_table[i] = de_pq_table[i-1]; 289 } 290 struct dividers { 291 struct fixed31_32 divider1; 292 struct fixed31_32 divider2; 293 struct fixed31_32 divider3; 294 }; 295 296 297 static bool build_coefficients(struct gamma_coefficients *coefficients, enum dc_transfer_func_predefined type) 298 { 299 300 uint32_t i = 0; 301 uint32_t index = 0; 302 bool ret = true; 303 304 if (type == TRANSFER_FUNCTION_SRGB) 305 index = 0; 306 else if (type == TRANSFER_FUNCTION_BT709) 307 index = 1; 308 else if (type == TRANSFER_FUNCTION_GAMMA22) 309 index = 2; 310 else if (type == TRANSFER_FUNCTION_GAMMA24) 311 index = 3; 312 else if (type == TRANSFER_FUNCTION_GAMMA26) 313 index = 4; 314 else { 315 ret = false; 316 goto release; 317 } 318 319 do { 320 coefficients->a0[i] = dc_fixpt_from_fraction( 321 gamma_numerator01[index], 10000000); 322 coefficients->a1[i] = dc_fixpt_from_fraction( 323 gamma_numerator02[index], 1000); 324 coefficients->a2[i] = dc_fixpt_from_fraction( 325 gamma_numerator03[index], 1000); 326 coefficients->a3[i] = dc_fixpt_from_fraction( 327 gamma_numerator04[index], 1000); 328 coefficients->user_gamma[i] = dc_fixpt_from_fraction( 329 gamma_numerator05[index], 1000); 330 331 ++i; 332 } while (i != ARRAY_SIZE(coefficients->a0)); 333 release: 334 return ret; 335 } 336 337 static struct fixed31_32 translate_from_linear_space( 338 struct translate_from_linear_space_args *args) 339 { 340 const struct fixed31_32 one = dc_fixpt_from_int(1); 341 342 if (dc_fixpt_le(one, args->arg)) 343 return one; 344 345 if (dc_fixpt_le(args->arg, dc_fixpt_neg(args->a0))) { 346 scratch_1 = dc_fixpt_add(one, args->a3); 347 scratch_2 = dc_fixpt_pow( 348 dc_fixpt_neg(args->arg), 349 dc_fixpt_recip(args->gamma)); 350 scratch_1 = dc_fixpt_mul(scratch_1, scratch_2); 351 scratch_1 = dc_fixpt_sub(args->a2, scratch_1); 352 353 return scratch_1; 354 } else if (dc_fixpt_le(args->a0, args->arg)) { 355 if (pow_buffer_ptr == 0) { 356 gamma_of_2 = dc_fixpt_pow(dc_fixpt_from_int(2), 357 dc_fixpt_recip(args->gamma)); 358 } 359 scratch_1 = dc_fixpt_add(one, args->a3); 360 if (pow_buffer_ptr < 16) 361 scratch_2 = dc_fixpt_pow(args->arg, 362 dc_fixpt_recip(args->gamma)); 363 else 364 scratch_2 = dc_fixpt_mul(gamma_of_2, 365 pow_buffer[pow_buffer_ptr%16]); 366 367 if (pow_buffer_ptr != -1) { 368 pow_buffer[pow_buffer_ptr%16] = scratch_2; 369 pow_buffer_ptr++; 370 } 371 372 scratch_1 = dc_fixpt_mul(scratch_1, scratch_2); 373 scratch_1 = dc_fixpt_sub(scratch_1, args->a2); 374 375 return scratch_1; 376 } 377 else 378 return dc_fixpt_mul(args->arg, args->a1); 379 } 380 381 382 static struct fixed31_32 translate_from_linear_space_long( 383 struct translate_from_linear_space_args *args) 384 { 385 const struct fixed31_32 one = dc_fixpt_from_int(1); 386 387 if (dc_fixpt_lt(one, args->arg)) 388 return one; 389 390 if (dc_fixpt_le(args->arg, dc_fixpt_neg(args->a0))) 391 return dc_fixpt_sub( 392 args->a2, 393 dc_fixpt_mul( 394 dc_fixpt_add( 395 one, 396 args->a3), 397 dc_fixpt_pow( 398 dc_fixpt_neg(args->arg), 399 dc_fixpt_recip(args->gamma)))); 400 else if (dc_fixpt_le(args->a0, args->arg)) 401 return dc_fixpt_sub( 402 dc_fixpt_mul( 403 dc_fixpt_add( 404 one, 405 args->a3), 406 dc_fixpt_pow( 407 args->arg, 408 dc_fixpt_recip(args->gamma))), 409 args->a2); 410 else 411 return dc_fixpt_mul( 412 args->arg, 413 args->a1); 414 } 415 416 static struct fixed31_32 calculate_gamma22(struct fixed31_32 arg, bool use_eetf) 417 { 418 struct fixed31_32 gamma = dc_fixpt_from_fraction(22, 10); 419 420 scratch_gamma_args.arg = arg; 421 scratch_gamma_args.a0 = dc_fixpt_zero; 422 scratch_gamma_args.a1 = dc_fixpt_zero; 423 scratch_gamma_args.a2 = dc_fixpt_zero; 424 scratch_gamma_args.a3 = dc_fixpt_zero; 425 scratch_gamma_args.gamma = gamma; 426 427 if (use_eetf) 428 return translate_from_linear_space_long(&scratch_gamma_args); 429 430 return translate_from_linear_space(&scratch_gamma_args); 431 } 432 433 434 static struct fixed31_32 translate_to_linear_space( 435 struct fixed31_32 arg, 436 struct fixed31_32 a0, 437 struct fixed31_32 a1, 438 struct fixed31_32 a2, 439 struct fixed31_32 a3, 440 struct fixed31_32 gamma) 441 { 442 struct fixed31_32 linear; 443 444 a0 = dc_fixpt_mul(a0, a1); 445 if (dc_fixpt_le(arg, dc_fixpt_neg(a0))) 446 447 linear = dc_fixpt_neg( 448 dc_fixpt_pow( 449 dc_fixpt_div( 450 dc_fixpt_sub(a2, arg), 451 dc_fixpt_add( 452 dc_fixpt_one, a3)), gamma)); 453 454 else if (dc_fixpt_le(dc_fixpt_neg(a0), arg) && 455 dc_fixpt_le(arg, a0)) 456 linear = dc_fixpt_div(arg, a1); 457 else 458 linear = dc_fixpt_pow( 459 dc_fixpt_div( 460 dc_fixpt_add(a2, arg), 461 dc_fixpt_add( 462 dc_fixpt_one, a3)), gamma); 463 464 return linear; 465 } 466 467 static struct fixed31_32 translate_from_linear_space_ex( 468 struct fixed31_32 arg, 469 struct gamma_coefficients *coeff, 470 uint32_t color_index) 471 { 472 scratch_gamma_args.arg = arg; 473 scratch_gamma_args.a0 = coeff->a0[color_index]; 474 scratch_gamma_args.a1 = coeff->a1[color_index]; 475 scratch_gamma_args.a2 = coeff->a2[color_index]; 476 scratch_gamma_args.a3 = coeff->a3[color_index]; 477 scratch_gamma_args.gamma = coeff->user_gamma[color_index]; 478 479 return translate_from_linear_space(&scratch_gamma_args); 480 } 481 482 483 static inline struct fixed31_32 translate_to_linear_space_ex( 484 struct fixed31_32 arg, 485 struct gamma_coefficients *coeff, 486 uint32_t color_index) 487 { 488 return translate_to_linear_space( 489 arg, 490 coeff->a0[color_index], 491 coeff->a1[color_index], 492 coeff->a2[color_index], 493 coeff->a3[color_index], 494 coeff->user_gamma[color_index]); 495 } 496 497 498 static bool find_software_points( 499 const struct dc_gamma *ramp, 500 const struct gamma_pixel *axis_x, 501 struct fixed31_32 hw_point, 502 enum channel_name channel, 503 uint32_t *index_to_start, 504 uint32_t *index_left, 505 uint32_t *index_right, 506 enum hw_point_position *pos) 507 { 508 const uint32_t max_number = ramp->num_entries + 3; 509 510 struct fixed31_32 left, right; 511 512 uint32_t i = *index_to_start; 513 514 while (i < max_number) { 515 if (channel == CHANNEL_NAME_RED) { 516 left = axis_x[i].r; 517 518 if (i < max_number - 1) 519 right = axis_x[i + 1].r; 520 else 521 right = axis_x[max_number - 1].r; 522 } else if (channel == CHANNEL_NAME_GREEN) { 523 left = axis_x[i].g; 524 525 if (i < max_number - 1) 526 right = axis_x[i + 1].g; 527 else 528 right = axis_x[max_number - 1].g; 529 } else { 530 left = axis_x[i].b; 531 532 if (i < max_number - 1) 533 right = axis_x[i + 1].b; 534 else 535 right = axis_x[max_number - 1].b; 536 } 537 538 if (dc_fixpt_le(left, hw_point) && 539 dc_fixpt_le(hw_point, right)) { 540 *index_to_start = i; 541 *index_left = i; 542 543 if (i < max_number - 1) 544 *index_right = i + 1; 545 else 546 *index_right = max_number - 1; 547 548 *pos = HW_POINT_POSITION_MIDDLE; 549 550 return true; 551 } else if ((i == *index_to_start) && 552 dc_fixpt_le(hw_point, left)) { 553 *index_to_start = i; 554 *index_left = i; 555 *index_right = i; 556 557 *pos = HW_POINT_POSITION_LEFT; 558 559 return true; 560 } else if ((i == max_number - 1) && 561 dc_fixpt_le(right, hw_point)) { 562 *index_to_start = i; 563 *index_left = i; 564 *index_right = i; 565 566 *pos = HW_POINT_POSITION_RIGHT; 567 568 return true; 569 } 570 571 ++i; 572 } 573 574 return false; 575 } 576 577 static bool build_custom_gamma_mapping_coefficients_worker( 578 const struct dc_gamma *ramp, 579 struct pixel_gamma_point *coeff, 580 const struct hw_x_point *coordinates_x, 581 const struct gamma_pixel *axis_x, 582 enum channel_name channel, 583 uint32_t number_of_points) 584 { 585 uint32_t i = 0; 586 587 while (i <= number_of_points) { 588 struct fixed31_32 coord_x; 589 590 uint32_t index_to_start = 0; 591 uint32_t index_left = 0; 592 uint32_t index_right = 0; 593 594 enum hw_point_position hw_pos; 595 596 struct gamma_point *point; 597 598 struct fixed31_32 left_pos; 599 struct fixed31_32 right_pos; 600 601 if (channel == CHANNEL_NAME_RED) 602 coord_x = coordinates_x[i].regamma_y_red; 603 else if (channel == CHANNEL_NAME_GREEN) 604 coord_x = coordinates_x[i].regamma_y_green; 605 else 606 coord_x = coordinates_x[i].regamma_y_blue; 607 608 if (!find_software_points( 609 ramp, axis_x, coord_x, channel, 610 &index_to_start, &index_left, &index_right, &hw_pos)) { 611 BREAK_TO_DEBUGGER(); 612 return false; 613 } 614 615 if (index_left >= ramp->num_entries + 3) { 616 BREAK_TO_DEBUGGER(); 617 return false; 618 } 619 620 if (index_right >= ramp->num_entries + 3) { 621 BREAK_TO_DEBUGGER(); 622 return false; 623 } 624 625 if (channel == CHANNEL_NAME_RED) { 626 point = &coeff[i].r; 627 628 left_pos = axis_x[index_left].r; 629 right_pos = axis_x[index_right].r; 630 } else if (channel == CHANNEL_NAME_GREEN) { 631 point = &coeff[i].g; 632 633 left_pos = axis_x[index_left].g; 634 right_pos = axis_x[index_right].g; 635 } else { 636 point = &coeff[i].b; 637 638 left_pos = axis_x[index_left].b; 639 right_pos = axis_x[index_right].b; 640 } 641 642 if (hw_pos == HW_POINT_POSITION_MIDDLE) 643 point->coeff = dc_fixpt_div( 644 dc_fixpt_sub( 645 coord_x, 646 left_pos), 647 dc_fixpt_sub( 648 right_pos, 649 left_pos)); 650 else if (hw_pos == HW_POINT_POSITION_LEFT) 651 point->coeff = dc_fixpt_zero; 652 else if (hw_pos == HW_POINT_POSITION_RIGHT) 653 point->coeff = dc_fixpt_from_int(2); 654 else { 655 BREAK_TO_DEBUGGER(); 656 return false; 657 } 658 659 point->left_index = index_left; 660 point->right_index = index_right; 661 point->pos = hw_pos; 662 663 ++i; 664 } 665 666 return true; 667 } 668 669 static struct fixed31_32 calculate_mapped_value( 670 struct pwl_float_data *rgb, 671 const struct pixel_gamma_point *coeff, 672 enum channel_name channel, 673 uint32_t max_index) 674 { 675 const struct gamma_point *point; 676 677 struct fixed31_32 result; 678 679 if (channel == CHANNEL_NAME_RED) 680 point = &coeff->r; 681 else if (channel == CHANNEL_NAME_GREEN) 682 point = &coeff->g; 683 else 684 point = &coeff->b; 685 686 if ((point->left_index < 0) || (point->left_index > max_index)) { 687 BREAK_TO_DEBUGGER(); 688 return dc_fixpt_zero; 689 } 690 691 if ((point->right_index < 0) || (point->right_index > max_index)) { 692 BREAK_TO_DEBUGGER(); 693 return dc_fixpt_zero; 694 } 695 696 if (point->pos == HW_POINT_POSITION_MIDDLE) 697 if (channel == CHANNEL_NAME_RED) 698 result = dc_fixpt_add( 699 dc_fixpt_mul( 700 point->coeff, 701 dc_fixpt_sub( 702 rgb[point->right_index].r, 703 rgb[point->left_index].r)), 704 rgb[point->left_index].r); 705 else if (channel == CHANNEL_NAME_GREEN) 706 result = dc_fixpt_add( 707 dc_fixpt_mul( 708 point->coeff, 709 dc_fixpt_sub( 710 rgb[point->right_index].g, 711 rgb[point->left_index].g)), 712 rgb[point->left_index].g); 713 else 714 result = dc_fixpt_add( 715 dc_fixpt_mul( 716 point->coeff, 717 dc_fixpt_sub( 718 rgb[point->right_index].b, 719 rgb[point->left_index].b)), 720 rgb[point->left_index].b); 721 else if (point->pos == HW_POINT_POSITION_LEFT) { 722 BREAK_TO_DEBUGGER(); 723 result = dc_fixpt_zero; 724 } else { 725 BREAK_TO_DEBUGGER(); 726 result = dc_fixpt_one; 727 } 728 729 return result; 730 } 731 732 static void build_pq(struct pwl_float_data_ex *rgb_regamma, 733 uint32_t hw_points_num, 734 const struct hw_x_point *coordinate_x, 735 uint32_t sdr_white_level) 736 { 737 uint32_t i, start_index; 738 739 struct pwl_float_data_ex *rgb = rgb_regamma; 740 const struct hw_x_point *coord_x = coordinate_x; 741 struct fixed31_32 x; 742 struct fixed31_32 output; 743 struct fixed31_32 scaling_factor = 744 dc_fixpt_from_fraction(sdr_white_level, 10000); 745 746 if (!pq_initialized && sdr_white_level == 80) { 747 precompute_pq(); 748 pq_initialized = true; 749 } 750 751 /* TODO: start index is from segment 2^-24, skipping first segment 752 * due to x values too small for power calculations 753 */ 754 start_index = 32; 755 rgb += start_index; 756 coord_x += start_index; 757 758 for (i = start_index; i <= hw_points_num; i++) { 759 /* Multiply 0.008 as regamma is 0-1 and FP16 input is 0-125. 760 * FP 1.0 = 80nits 761 */ 762 if (sdr_white_level == 80) { 763 output = pq_table[i]; 764 } else { 765 x = dc_fixpt_mul(coord_x->x, scaling_factor); 766 compute_pq(x, &output); 767 } 768 769 /* should really not happen? */ 770 if (dc_fixpt_lt(output, dc_fixpt_zero)) 771 output = dc_fixpt_zero; 772 else if (dc_fixpt_lt(dc_fixpt_one, output)) 773 output = dc_fixpt_one; 774 775 rgb->r = output; 776 rgb->g = output; 777 rgb->b = output; 778 779 ++coord_x; 780 ++rgb; 781 } 782 } 783 784 static void build_de_pq(struct pwl_float_data_ex *de_pq, 785 uint32_t hw_points_num, 786 const struct hw_x_point *coordinate_x) 787 { 788 uint32_t i; 789 struct fixed31_32 output; 790 791 struct fixed31_32 scaling_factor = dc_fixpt_from_int(125); 792 793 if (!de_pq_initialized) { 794 precompute_de_pq(); 795 de_pq_initialized = true; 796 } 797 798 799 for (i = 0; i <= hw_points_num; i++) { 800 output = de_pq_table[i]; 801 /* should really not happen? */ 802 if (dc_fixpt_lt(output, dc_fixpt_zero)) 803 output = dc_fixpt_zero; 804 else if (dc_fixpt_lt(scaling_factor, output)) 805 output = scaling_factor; 806 de_pq[i].r = output; 807 de_pq[i].g = output; 808 de_pq[i].b = output; 809 } 810 } 811 812 static bool build_regamma(struct pwl_float_data_ex *rgb_regamma, 813 uint32_t hw_points_num, 814 const struct hw_x_point *coordinate_x, enum dc_transfer_func_predefined type) 815 { 816 uint32_t i; 817 bool ret = false; 818 819 struct gamma_coefficients *coeff; 820 struct pwl_float_data_ex *rgb = rgb_regamma; 821 const struct hw_x_point *coord_x = coordinate_x; 822 823 coeff = kvzalloc(sizeof(*coeff), GFP_KERNEL); 824 if (!coeff) 825 goto release; 826 827 if (!build_coefficients(coeff, type)) 828 goto release; 829 830 memset(pow_buffer, 0, NUM_PTS_IN_REGION * sizeof(struct fixed31_32)); 831 pow_buffer_ptr = 0; // see variable definition for more info 832 i = 0; 833 while (i <= hw_points_num) { 834 /*TODO use y vs r,g,b*/ 835 rgb->r = translate_from_linear_space_ex( 836 coord_x->x, coeff, 0); 837 rgb->g = rgb->r; 838 rgb->b = rgb->r; 839 ++coord_x; 840 ++rgb; 841 ++i; 842 } 843 pow_buffer_ptr = -1; // reset back to no optimize 844 ret = true; 845 release: 846 kvfree(coeff); 847 return ret; 848 } 849 850 static void hermite_spline_eetf(struct fixed31_32 input_x, 851 struct fixed31_32 max_display, 852 struct fixed31_32 min_display, 853 struct fixed31_32 max_content, 854 struct fixed31_32 *out_x) 855 { 856 struct fixed31_32 min_lum_pq; 857 struct fixed31_32 max_lum_pq; 858 struct fixed31_32 max_content_pq; 859 struct fixed31_32 ks; 860 struct fixed31_32 E1; 861 struct fixed31_32 E2; 862 struct fixed31_32 E3; 863 struct fixed31_32 t; 864 struct fixed31_32 t2; 865 struct fixed31_32 t3; 866 struct fixed31_32 two; 867 struct fixed31_32 three; 868 struct fixed31_32 temp1; 869 struct fixed31_32 temp2; 870 struct fixed31_32 a = dc_fixpt_from_fraction(15, 10); 871 struct fixed31_32 b = dc_fixpt_from_fraction(5, 10); 872 struct fixed31_32 epsilon = dc_fixpt_from_fraction(1, 1000000); // dc_fixpt_epsilon is a bit too small 873 874 if (dc_fixpt_eq(max_content, dc_fixpt_zero)) { 875 *out_x = dc_fixpt_zero; 876 return; 877 } 878 879 compute_pq(input_x, &E1); 880 compute_pq(dc_fixpt_div(min_display, max_content), &min_lum_pq); 881 compute_pq(dc_fixpt_div(max_display, max_content), &max_lum_pq); 882 compute_pq(dc_fixpt_one, &max_content_pq); // always 1? DAL2 code is weird 883 a = dc_fixpt_div(dc_fixpt_add(dc_fixpt_one, b), max_content_pq); // (1+b)/maxContent 884 ks = dc_fixpt_sub(dc_fixpt_mul(a, max_lum_pq), b); // a * max_lum_pq - b 885 886 if (dc_fixpt_lt(E1, ks)) 887 E2 = E1; 888 else if (dc_fixpt_le(ks, E1) && dc_fixpt_le(E1, dc_fixpt_one)) { 889 if (dc_fixpt_lt(epsilon, dc_fixpt_sub(dc_fixpt_one, ks))) 890 // t = (E1 - ks) / (1 - ks) 891 t = dc_fixpt_div(dc_fixpt_sub(E1, ks), 892 dc_fixpt_sub(dc_fixpt_one, ks)); 893 else 894 t = dc_fixpt_zero; 895 896 two = dc_fixpt_from_int(2); 897 three = dc_fixpt_from_int(3); 898 899 t2 = dc_fixpt_mul(t, t); 900 t3 = dc_fixpt_mul(t2, t); 901 temp1 = dc_fixpt_mul(two, t3); 902 temp2 = dc_fixpt_mul(three, t2); 903 904 // (2t^3 - 3t^2 + 1) * ks 905 E2 = dc_fixpt_mul(ks, dc_fixpt_add(dc_fixpt_one, 906 dc_fixpt_sub(temp1, temp2))); 907 908 // (-2t^3 + 3t^2) * max_lum_pq 909 E2 = dc_fixpt_add(E2, dc_fixpt_mul(max_lum_pq, 910 dc_fixpt_sub(temp2, temp1))); 911 912 temp1 = dc_fixpt_mul(two, t2); 913 temp2 = dc_fixpt_sub(dc_fixpt_one, ks); 914 915 // (t^3 - 2t^2 + t) * (1-ks) 916 E2 = dc_fixpt_add(E2, dc_fixpt_mul(temp2, 917 dc_fixpt_add(t, dc_fixpt_sub(t3, temp1)))); 918 } else 919 E2 = dc_fixpt_one; 920 921 temp1 = dc_fixpt_sub(dc_fixpt_one, E2); 922 temp2 = dc_fixpt_mul(temp1, temp1); 923 temp2 = dc_fixpt_mul(temp2, temp2); 924 // temp2 = (1-E2)^4 925 926 E3 = dc_fixpt_add(E2, dc_fixpt_mul(min_lum_pq, temp2)); 927 compute_de_pq(E3, out_x); 928 929 *out_x = dc_fixpt_div(*out_x, dc_fixpt_div(max_display, max_content)); 930 } 931 932 static bool build_freesync_hdr(struct pwl_float_data_ex *rgb_regamma, 933 uint32_t hw_points_num, 934 const struct hw_x_point *coordinate_x, 935 const struct freesync_hdr_tf_params *fs_params) 936 { 937 uint32_t i; 938 struct pwl_float_data_ex *rgb = rgb_regamma; 939 const struct hw_x_point *coord_x = coordinate_x; 940 struct fixed31_32 scaledX = dc_fixpt_zero; 941 struct fixed31_32 scaledX1 = dc_fixpt_zero; 942 struct fixed31_32 max_display; 943 struct fixed31_32 min_display; 944 struct fixed31_32 max_content; 945 struct fixed31_32 clip = dc_fixpt_one; 946 struct fixed31_32 output; 947 bool use_eetf = false; 948 bool is_clipped = false; 949 struct fixed31_32 sdr_white_level; 950 951 if (fs_params->max_content == 0 || 952 fs_params->max_display == 0) 953 return false; 954 955 max_display = dc_fixpt_from_int(fs_params->max_display); 956 min_display = dc_fixpt_from_fraction(fs_params->min_display, 10000); 957 max_content = dc_fixpt_from_int(fs_params->max_content); 958 sdr_white_level = dc_fixpt_from_int(fs_params->sdr_white_level); 959 960 if (fs_params->min_display > 1000) // cap at 0.1 at the bottom 961 min_display = dc_fixpt_from_fraction(1, 10); 962 if (fs_params->max_display < 100) // cap at 100 at the top 963 max_display = dc_fixpt_from_int(100); 964 965 // only max used, we don't adjust min luminance 966 if (fs_params->max_content > fs_params->max_display) 967 use_eetf = true; 968 else 969 max_content = max_display; 970 971 if (!use_eetf) 972 pow_buffer_ptr = 0; // see var definition for more info 973 rgb += 32; // first 32 points have problems with fixed point, too small 974 coord_x += 32; 975 for (i = 32; i <= hw_points_num; i++) { 976 if (!is_clipped) { 977 if (use_eetf) { 978 /*max content is equal 1 */ 979 scaledX1 = dc_fixpt_div(coord_x->x, 980 dc_fixpt_div(max_content, sdr_white_level)); 981 hermite_spline_eetf(scaledX1, max_display, min_display, 982 max_content, &scaledX); 983 } else 984 scaledX = dc_fixpt_div(coord_x->x, 985 dc_fixpt_div(max_display, sdr_white_level)); 986 987 if (dc_fixpt_lt(scaledX, clip)) { 988 if (dc_fixpt_lt(scaledX, dc_fixpt_zero)) 989 output = dc_fixpt_zero; 990 else 991 output = calculate_gamma22(scaledX, use_eetf); 992 993 rgb->r = output; 994 rgb->g = output; 995 rgb->b = output; 996 } else { 997 is_clipped = true; 998 rgb->r = clip; 999 rgb->g = clip; 1000 rgb->b = clip; 1001 } 1002 } else { 1003 rgb->r = clip; 1004 rgb->g = clip; 1005 rgb->b = clip; 1006 } 1007 1008 ++coord_x; 1009 ++rgb; 1010 } 1011 pow_buffer_ptr = -1; 1012 1013 return true; 1014 } 1015 1016 static bool build_degamma(struct pwl_float_data_ex *curve, 1017 uint32_t hw_points_num, 1018 const struct hw_x_point *coordinate_x, enum dc_transfer_func_predefined type) 1019 { 1020 uint32_t i; 1021 struct gamma_coefficients coeff; 1022 uint32_t begin_index, end_index; 1023 bool ret = false; 1024 1025 if (!build_coefficients(&coeff, type)) 1026 goto release; 1027 1028 i = 0; 1029 1030 /* X points is 2^-25 to 2^7 1031 * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions 1032 */ 1033 begin_index = 13 * NUM_PTS_IN_REGION; 1034 end_index = begin_index + 12 * NUM_PTS_IN_REGION; 1035 1036 while (i != begin_index) { 1037 curve[i].r = dc_fixpt_zero; 1038 curve[i].g = dc_fixpt_zero; 1039 curve[i].b = dc_fixpt_zero; 1040 i++; 1041 } 1042 1043 while (i != end_index) { 1044 curve[i].r = translate_to_linear_space_ex( 1045 coordinate_x[i].x, &coeff, 0); 1046 curve[i].g = curve[i].r; 1047 curve[i].b = curve[i].r; 1048 i++; 1049 } 1050 while (i != hw_points_num + 1) { 1051 curve[i].r = dc_fixpt_one; 1052 curve[i].g = dc_fixpt_one; 1053 curve[i].b = dc_fixpt_one; 1054 i++; 1055 } 1056 ret = true; 1057 release: 1058 return ret; 1059 } 1060 1061 1062 1063 1064 1065 static void build_hlg_degamma(struct pwl_float_data_ex *degamma, 1066 uint32_t hw_points_num, 1067 const struct hw_x_point *coordinate_x, 1068 uint32_t sdr_white_level, uint32_t max_luminance_nits) 1069 { 1070 uint32_t i; 1071 1072 struct pwl_float_data_ex *rgb = degamma; 1073 const struct hw_x_point *coord_x = coordinate_x; 1074 1075 i = 0; 1076 //check when i == 434 1077 while (i != hw_points_num + 1) { 1078 compute_hlg_eotf(coord_x->x, &rgb->r, sdr_white_level, max_luminance_nits); 1079 rgb->g = rgb->r; 1080 rgb->b = rgb->r; 1081 ++coord_x; 1082 ++rgb; 1083 ++i; 1084 } 1085 } 1086 1087 1088 static void build_hlg_regamma(struct pwl_float_data_ex *regamma, 1089 uint32_t hw_points_num, 1090 const struct hw_x_point *coordinate_x, 1091 uint32_t sdr_white_level, uint32_t max_luminance_nits) 1092 { 1093 uint32_t i; 1094 1095 struct pwl_float_data_ex *rgb = regamma; 1096 const struct hw_x_point *coord_x = coordinate_x; 1097 1098 i = 0; 1099 1100 //when i == 471 1101 while (i != hw_points_num + 1) { 1102 compute_hlg_oetf(coord_x->x, &rgb->r, sdr_white_level, max_luminance_nits); 1103 rgb->g = rgb->r; 1104 rgb->b = rgb->r; 1105 ++coord_x; 1106 ++rgb; 1107 ++i; 1108 } 1109 } 1110 1111 static void scale_gamma(struct pwl_float_data *pwl_rgb, 1112 const struct dc_gamma *ramp, 1113 struct dividers dividers) 1114 { 1115 const struct fixed31_32 max_driver = dc_fixpt_from_int(0xFFFF); 1116 const struct fixed31_32 max_os = dc_fixpt_from_int(0xFF00); 1117 struct fixed31_32 scaler = max_os; 1118 uint32_t i; 1119 struct pwl_float_data *rgb = pwl_rgb; 1120 struct pwl_float_data *rgb_last = rgb + ramp->num_entries - 1; 1121 1122 i = 0; 1123 1124 do { 1125 if (dc_fixpt_lt(max_os, ramp->entries.red[i]) || 1126 dc_fixpt_lt(max_os, ramp->entries.green[i]) || 1127 dc_fixpt_lt(max_os, ramp->entries.blue[i])) { 1128 scaler = max_driver; 1129 break; 1130 } 1131 ++i; 1132 } while (i != ramp->num_entries); 1133 1134 i = 0; 1135 1136 do { 1137 rgb->r = dc_fixpt_div( 1138 ramp->entries.red[i], scaler); 1139 rgb->g = dc_fixpt_div( 1140 ramp->entries.green[i], scaler); 1141 rgb->b = dc_fixpt_div( 1142 ramp->entries.blue[i], scaler); 1143 1144 ++rgb; 1145 ++i; 1146 } while (i != ramp->num_entries); 1147 1148 rgb->r = dc_fixpt_mul(rgb_last->r, 1149 dividers.divider1); 1150 rgb->g = dc_fixpt_mul(rgb_last->g, 1151 dividers.divider1); 1152 rgb->b = dc_fixpt_mul(rgb_last->b, 1153 dividers.divider1); 1154 1155 ++rgb; 1156 1157 rgb->r = dc_fixpt_mul(rgb_last->r, 1158 dividers.divider2); 1159 rgb->g = dc_fixpt_mul(rgb_last->g, 1160 dividers.divider2); 1161 rgb->b = dc_fixpt_mul(rgb_last->b, 1162 dividers.divider2); 1163 1164 ++rgb; 1165 1166 rgb->r = dc_fixpt_mul(rgb_last->r, 1167 dividers.divider3); 1168 rgb->g = dc_fixpt_mul(rgb_last->g, 1169 dividers.divider3); 1170 rgb->b = dc_fixpt_mul(rgb_last->b, 1171 dividers.divider3); 1172 } 1173 1174 static void scale_gamma_dx(struct pwl_float_data *pwl_rgb, 1175 const struct dc_gamma *ramp, 1176 struct dividers dividers) 1177 { 1178 uint32_t i; 1179 struct fixed31_32 min = dc_fixpt_zero; 1180 struct fixed31_32 max = dc_fixpt_one; 1181 1182 struct fixed31_32 delta = dc_fixpt_zero; 1183 struct fixed31_32 offset = dc_fixpt_zero; 1184 1185 for (i = 0 ; i < ramp->num_entries; i++) { 1186 if (dc_fixpt_lt(ramp->entries.red[i], min)) 1187 min = ramp->entries.red[i]; 1188 1189 if (dc_fixpt_lt(ramp->entries.green[i], min)) 1190 min = ramp->entries.green[i]; 1191 1192 if (dc_fixpt_lt(ramp->entries.blue[i], min)) 1193 min = ramp->entries.blue[i]; 1194 1195 if (dc_fixpt_lt(max, ramp->entries.red[i])) 1196 max = ramp->entries.red[i]; 1197 1198 if (dc_fixpt_lt(max, ramp->entries.green[i])) 1199 max = ramp->entries.green[i]; 1200 1201 if (dc_fixpt_lt(max, ramp->entries.blue[i])) 1202 max = ramp->entries.blue[i]; 1203 } 1204 1205 if (dc_fixpt_lt(min, dc_fixpt_zero)) 1206 delta = dc_fixpt_neg(min); 1207 1208 offset = dc_fixpt_add(min, max); 1209 1210 for (i = 0 ; i < ramp->num_entries; i++) { 1211 pwl_rgb[i].r = dc_fixpt_div( 1212 dc_fixpt_add( 1213 ramp->entries.red[i], delta), offset); 1214 pwl_rgb[i].g = dc_fixpt_div( 1215 dc_fixpt_add( 1216 ramp->entries.green[i], delta), offset); 1217 pwl_rgb[i].b = dc_fixpt_div( 1218 dc_fixpt_add( 1219 ramp->entries.blue[i], delta), offset); 1220 1221 } 1222 1223 pwl_rgb[i].r = dc_fixpt_sub(dc_fixpt_mul_int( 1224 pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r); 1225 pwl_rgb[i].g = dc_fixpt_sub(dc_fixpt_mul_int( 1226 pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g); 1227 pwl_rgb[i].b = dc_fixpt_sub(dc_fixpt_mul_int( 1228 pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b); 1229 ++i; 1230 pwl_rgb[i].r = dc_fixpt_sub(dc_fixpt_mul_int( 1231 pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r); 1232 pwl_rgb[i].g = dc_fixpt_sub(dc_fixpt_mul_int( 1233 pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g); 1234 pwl_rgb[i].b = dc_fixpt_sub(dc_fixpt_mul_int( 1235 pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b); 1236 } 1237 1238 /* todo: all these scale_gamma functions are inherently the same but 1239 * take different structures as params or different format for ramp 1240 * values. We could probably implement it in a more generic fashion 1241 */ 1242 static void scale_user_regamma_ramp(struct pwl_float_data *pwl_rgb, 1243 const struct regamma_ramp *ramp, 1244 struct dividers dividers) 1245 { 1246 unsigned short max_driver = 0xFFFF; 1247 unsigned short max_os = 0xFF00; 1248 unsigned short scaler = max_os; 1249 uint32_t i; 1250 struct pwl_float_data *rgb = pwl_rgb; 1251 struct pwl_float_data *rgb_last = rgb + GAMMA_RGB_256_ENTRIES - 1; 1252 1253 i = 0; 1254 do { 1255 if (ramp->gamma[i] > max_os || 1256 ramp->gamma[i + 256] > max_os || 1257 ramp->gamma[i + 512] > max_os) { 1258 scaler = max_driver; 1259 break; 1260 } 1261 i++; 1262 } while (i != GAMMA_RGB_256_ENTRIES); 1263 1264 i = 0; 1265 do { 1266 rgb->r = dc_fixpt_from_fraction( 1267 ramp->gamma[i], scaler); 1268 rgb->g = dc_fixpt_from_fraction( 1269 ramp->gamma[i + 256], scaler); 1270 rgb->b = dc_fixpt_from_fraction( 1271 ramp->gamma[i + 512], scaler); 1272 1273 ++rgb; 1274 ++i; 1275 } while (i != GAMMA_RGB_256_ENTRIES); 1276 1277 rgb->r = dc_fixpt_mul(rgb_last->r, 1278 dividers.divider1); 1279 rgb->g = dc_fixpt_mul(rgb_last->g, 1280 dividers.divider1); 1281 rgb->b = dc_fixpt_mul(rgb_last->b, 1282 dividers.divider1); 1283 1284 ++rgb; 1285 1286 rgb->r = dc_fixpt_mul(rgb_last->r, 1287 dividers.divider2); 1288 rgb->g = dc_fixpt_mul(rgb_last->g, 1289 dividers.divider2); 1290 rgb->b = dc_fixpt_mul(rgb_last->b, 1291 dividers.divider2); 1292 1293 ++rgb; 1294 1295 rgb->r = dc_fixpt_mul(rgb_last->r, 1296 dividers.divider3); 1297 rgb->g = dc_fixpt_mul(rgb_last->g, 1298 dividers.divider3); 1299 rgb->b = dc_fixpt_mul(rgb_last->b, 1300 dividers.divider3); 1301 } 1302 1303 /* 1304 * RS3+ color transform DDI - 1D LUT adjustment is composed with regamma here 1305 * Input is evenly distributed in the output color space as specified in 1306 * SetTimings 1307 * 1308 * Interpolation details: 1309 * 1D LUT has 4096 values which give curve correction in 0-1 float range 1310 * for evenly spaced points in 0-1 range. lut1D[index] gives correction 1311 * for index/4095. 1312 * First we find index for which: 1313 * index/4095 < regamma_y < (index+1)/4095 => 1314 * index < 4095*regamma_y < index + 1 1315 * norm_y = 4095*regamma_y, and index is just truncating to nearest integer 1316 * lut1 = lut1D[index], lut2 = lut1D[index+1] 1317 * 1318 * adjustedY is then linearly interpolating regamma Y between lut1 and lut2 1319 * 1320 * Custom degamma on Linux uses the same interpolation math, so is handled here 1321 */ 1322 static void apply_lut_1d( 1323 const struct dc_gamma *ramp, 1324 uint32_t num_hw_points, 1325 struct dc_transfer_func_distributed_points *tf_pts) 1326 { 1327 int i = 0; 1328 int color = 0; 1329 struct fixed31_32 *regamma_y; 1330 struct fixed31_32 norm_y; 1331 struct fixed31_32 lut1; 1332 struct fixed31_32 lut2; 1333 const int max_lut_index = 4095; 1334 const struct fixed31_32 max_lut_index_f = 1335 dc_fixpt_from_int(max_lut_index); 1336 int32_t index = 0, index_next = 0; 1337 struct fixed31_32 index_f; 1338 struct fixed31_32 delta_lut; 1339 struct fixed31_32 delta_index; 1340 1341 if (ramp->type != GAMMA_CS_TFM_1D && ramp->type != GAMMA_CUSTOM) 1342 return; // this is not expected 1343 1344 for (i = 0; i < num_hw_points; i++) { 1345 for (color = 0; color < 3; color++) { 1346 if (color == 0) 1347 regamma_y = &tf_pts->red[i]; 1348 else if (color == 1) 1349 regamma_y = &tf_pts->green[i]; 1350 else 1351 regamma_y = &tf_pts->blue[i]; 1352 1353 norm_y = dc_fixpt_mul(max_lut_index_f, 1354 *regamma_y); 1355 index = dc_fixpt_floor(norm_y); 1356 index_f = dc_fixpt_from_int(index); 1357 1358 if (index < 0 || index > max_lut_index) 1359 continue; 1360 1361 index_next = (index == max_lut_index) ? index : index+1; 1362 1363 if (color == 0) { 1364 lut1 = ramp->entries.red[index]; 1365 lut2 = ramp->entries.red[index_next]; 1366 } else if (color == 1) { 1367 lut1 = ramp->entries.green[index]; 1368 lut2 = ramp->entries.green[index_next]; 1369 } else { 1370 lut1 = ramp->entries.blue[index]; 1371 lut2 = ramp->entries.blue[index_next]; 1372 } 1373 1374 // we have everything now, so interpolate 1375 delta_lut = dc_fixpt_sub(lut2, lut1); 1376 delta_index = dc_fixpt_sub(norm_y, index_f); 1377 1378 *regamma_y = dc_fixpt_add(lut1, 1379 dc_fixpt_mul(delta_index, delta_lut)); 1380 } 1381 } 1382 } 1383 1384 static void build_evenly_distributed_points( 1385 struct gamma_pixel *points, 1386 uint32_t numberof_points, 1387 struct dividers dividers) 1388 { 1389 struct gamma_pixel *p = points; 1390 struct gamma_pixel *p_last; 1391 1392 uint32_t i = 0; 1393 1394 // This function should not gets called with 0 as a parameter 1395 ASSERT(numberof_points > 0); 1396 p_last = p + numberof_points - 1; 1397 1398 do { 1399 struct fixed31_32 value = dc_fixpt_from_fraction(i, 1400 numberof_points - 1); 1401 1402 p->r = value; 1403 p->g = value; 1404 p->b = value; 1405 1406 ++p; 1407 ++i; 1408 } while (i < numberof_points); 1409 1410 p->r = dc_fixpt_div(p_last->r, dividers.divider1); 1411 p->g = dc_fixpt_div(p_last->g, dividers.divider1); 1412 p->b = dc_fixpt_div(p_last->b, dividers.divider1); 1413 1414 ++p; 1415 1416 p->r = dc_fixpt_div(p_last->r, dividers.divider2); 1417 p->g = dc_fixpt_div(p_last->g, dividers.divider2); 1418 p->b = dc_fixpt_div(p_last->b, dividers.divider2); 1419 1420 ++p; 1421 1422 p->r = dc_fixpt_div(p_last->r, dividers.divider3); 1423 p->g = dc_fixpt_div(p_last->g, dividers.divider3); 1424 p->b = dc_fixpt_div(p_last->b, dividers.divider3); 1425 } 1426 1427 static inline void copy_rgb_regamma_to_coordinates_x( 1428 struct hw_x_point *coordinates_x, 1429 uint32_t hw_points_num, 1430 const struct pwl_float_data_ex *rgb_ex) 1431 { 1432 struct hw_x_point *coords = coordinates_x; 1433 uint32_t i = 0; 1434 const struct pwl_float_data_ex *rgb_regamma = rgb_ex; 1435 1436 while (i <= hw_points_num + 1) { 1437 coords->regamma_y_red = rgb_regamma->r; 1438 coords->regamma_y_green = rgb_regamma->g; 1439 coords->regamma_y_blue = rgb_regamma->b; 1440 1441 ++coords; 1442 ++rgb_regamma; 1443 ++i; 1444 } 1445 } 1446 1447 static bool calculate_interpolated_hardware_curve( 1448 const struct dc_gamma *ramp, 1449 struct pixel_gamma_point *coeff128, 1450 struct pwl_float_data *rgb_user, 1451 const struct hw_x_point *coordinates_x, 1452 const struct gamma_pixel *axis_x, 1453 uint32_t number_of_points, 1454 struct dc_transfer_func_distributed_points *tf_pts) 1455 { 1456 1457 const struct pixel_gamma_point *coeff = coeff128; 1458 uint32_t max_entries = 3 - 1; 1459 1460 uint32_t i = 0; 1461 1462 for (i = 0; i < 3; i++) { 1463 if (!build_custom_gamma_mapping_coefficients_worker( 1464 ramp, coeff128, coordinates_x, axis_x, i, 1465 number_of_points)) 1466 return false; 1467 } 1468 1469 i = 0; 1470 max_entries += ramp->num_entries; 1471 1472 /* TODO: float point case */ 1473 1474 while (i <= number_of_points) { 1475 tf_pts->red[i] = calculate_mapped_value( 1476 rgb_user, coeff, CHANNEL_NAME_RED, max_entries); 1477 tf_pts->green[i] = calculate_mapped_value( 1478 rgb_user, coeff, CHANNEL_NAME_GREEN, max_entries); 1479 tf_pts->blue[i] = calculate_mapped_value( 1480 rgb_user, coeff, CHANNEL_NAME_BLUE, max_entries); 1481 1482 ++coeff; 1483 ++i; 1484 } 1485 1486 return true; 1487 } 1488 1489 /* The "old" interpolation uses a complicated scheme to build an array of 1490 * coefficients while also using an array of 0-255 normalized to 0-1 1491 * Then there's another loop using both of the above + new scaled user ramp 1492 * and we concatenate them. It also searches for points of interpolation and 1493 * uses enums for positions. 1494 * 1495 * This function uses a different approach: 1496 * user ramp is always applied on X with 0/255, 1/255, 2/255, ..., 255/255 1497 * To find index for hwX , we notice the following: 1498 * i/255 <= hwX < (i+1)/255 <=> i <= 255*hwX < i+1 1499 * See apply_lut_1d which is the same principle, but on 4K entry 1D LUT 1500 * 1501 * Once the index is known, combined Y is simply: 1502 * user_ramp(index) + (hwX-index/255)*(user_ramp(index+1) - user_ramp(index) 1503 * 1504 * We should switch to this method in all cases, it's simpler and faster 1505 * ToDo one day - for now this only applies to ADL regamma to avoid regression 1506 * for regular use cases (sRGB and PQ) 1507 */ 1508 static void interpolate_user_regamma(uint32_t hw_points_num, 1509 struct pwl_float_data *rgb_user, 1510 bool apply_degamma, 1511 struct dc_transfer_func_distributed_points *tf_pts) 1512 { 1513 uint32_t i; 1514 uint32_t color = 0; 1515 int32_t index; 1516 int32_t index_next; 1517 struct fixed31_32 *tf_point; 1518 struct fixed31_32 hw_x; 1519 struct fixed31_32 norm_factor = 1520 dc_fixpt_from_int(255); 1521 struct fixed31_32 norm_x; 1522 struct fixed31_32 index_f; 1523 struct fixed31_32 lut1; 1524 struct fixed31_32 lut2; 1525 struct fixed31_32 delta_lut; 1526 struct fixed31_32 delta_index; 1527 1528 i = 0; 1529 /* fixed_pt library has problems handling too small values */ 1530 while (i != 32) { 1531 tf_pts->red[i] = dc_fixpt_zero; 1532 tf_pts->green[i] = dc_fixpt_zero; 1533 tf_pts->blue[i] = dc_fixpt_zero; 1534 ++i; 1535 } 1536 while (i <= hw_points_num + 1) { 1537 for (color = 0; color < 3; color++) { 1538 if (color == 0) 1539 tf_point = &tf_pts->red[i]; 1540 else if (color == 1) 1541 tf_point = &tf_pts->green[i]; 1542 else 1543 tf_point = &tf_pts->blue[i]; 1544 1545 if (apply_degamma) { 1546 if (color == 0) 1547 hw_x = coordinates_x[i].regamma_y_red; 1548 else if (color == 1) 1549 hw_x = coordinates_x[i].regamma_y_green; 1550 else 1551 hw_x = coordinates_x[i].regamma_y_blue; 1552 } else 1553 hw_x = coordinates_x[i].x; 1554 1555 norm_x = dc_fixpt_mul(norm_factor, hw_x); 1556 index = dc_fixpt_floor(norm_x); 1557 if (index < 0 || index > 255) 1558 continue; 1559 1560 index_f = dc_fixpt_from_int(index); 1561 index_next = (index == 255) ? index : index + 1; 1562 1563 if (color == 0) { 1564 lut1 = rgb_user[index].r; 1565 lut2 = rgb_user[index_next].r; 1566 } else if (color == 1) { 1567 lut1 = rgb_user[index].g; 1568 lut2 = rgb_user[index_next].g; 1569 } else { 1570 lut1 = rgb_user[index].b; 1571 lut2 = rgb_user[index_next].b; 1572 } 1573 1574 // we have everything now, so interpolate 1575 delta_lut = dc_fixpt_sub(lut2, lut1); 1576 delta_index = dc_fixpt_sub(norm_x, index_f); 1577 1578 *tf_point = dc_fixpt_add(lut1, 1579 dc_fixpt_mul(delta_index, delta_lut)); 1580 } 1581 ++i; 1582 } 1583 } 1584 1585 static void build_new_custom_resulted_curve( 1586 uint32_t hw_points_num, 1587 struct dc_transfer_func_distributed_points *tf_pts) 1588 { 1589 uint32_t i; 1590 1591 i = 0; 1592 1593 while (i != hw_points_num + 1) { 1594 tf_pts->red[i] = dc_fixpt_clamp( 1595 tf_pts->red[i], dc_fixpt_zero, 1596 dc_fixpt_one); 1597 tf_pts->green[i] = dc_fixpt_clamp( 1598 tf_pts->green[i], dc_fixpt_zero, 1599 dc_fixpt_one); 1600 tf_pts->blue[i] = dc_fixpt_clamp( 1601 tf_pts->blue[i], dc_fixpt_zero, 1602 dc_fixpt_one); 1603 1604 ++i; 1605 } 1606 } 1607 1608 static void apply_degamma_for_user_regamma(struct pwl_float_data_ex *rgb_regamma, 1609 uint32_t hw_points_num) 1610 { 1611 uint32_t i; 1612 1613 struct gamma_coefficients coeff; 1614 struct pwl_float_data_ex *rgb = rgb_regamma; 1615 const struct hw_x_point *coord_x = coordinates_x; 1616 1617 build_coefficients(&coeff, true); 1618 1619 i = 0; 1620 while (i != hw_points_num + 1) { 1621 rgb->r = translate_from_linear_space_ex( 1622 coord_x->x, &coeff, 0); 1623 rgb->g = rgb->r; 1624 rgb->b = rgb->r; 1625 ++coord_x; 1626 ++rgb; 1627 ++i; 1628 } 1629 } 1630 1631 static bool map_regamma_hw_to_x_user( 1632 const struct dc_gamma *ramp, 1633 struct pixel_gamma_point *coeff128, 1634 struct pwl_float_data *rgb_user, 1635 struct hw_x_point *coords_x, 1636 const struct gamma_pixel *axis_x, 1637 const struct pwl_float_data_ex *rgb_regamma, 1638 uint32_t hw_points_num, 1639 struct dc_transfer_func_distributed_points *tf_pts, 1640 bool mapUserRamp) 1641 { 1642 /* setup to spare calculated ideal regamma values */ 1643 1644 int i = 0; 1645 struct hw_x_point *coords = coords_x; 1646 const struct pwl_float_data_ex *regamma = rgb_regamma; 1647 1648 if (ramp && mapUserRamp) { 1649 copy_rgb_regamma_to_coordinates_x(coords, 1650 hw_points_num, 1651 rgb_regamma); 1652 1653 calculate_interpolated_hardware_curve( 1654 ramp, coeff128, rgb_user, coords, axis_x, 1655 hw_points_num, tf_pts); 1656 } else { 1657 /* just copy current rgb_regamma into tf_pts */ 1658 while (i <= hw_points_num) { 1659 tf_pts->red[i] = regamma->r; 1660 tf_pts->green[i] = regamma->g; 1661 tf_pts->blue[i] = regamma->b; 1662 1663 ++regamma; 1664 ++i; 1665 } 1666 } 1667 1668 /* this should be named differently, all it does is clamp to 0-1 */ 1669 build_new_custom_resulted_curve(hw_points_num, tf_pts); 1670 1671 return true; 1672 } 1673 1674 #define _EXTRA_POINTS 3 1675 1676 bool calculate_user_regamma_coeff(struct dc_transfer_func *output_tf, 1677 const struct regamma_lut *regamma) 1678 { 1679 struct gamma_coefficients coeff; 1680 const struct hw_x_point *coord_x = coordinates_x; 1681 uint32_t i = 0; 1682 1683 do { 1684 coeff.a0[i] = dc_fixpt_from_fraction( 1685 regamma->coeff.A0[i], 10000000); 1686 coeff.a1[i] = dc_fixpt_from_fraction( 1687 regamma->coeff.A1[i], 1000); 1688 coeff.a2[i] = dc_fixpt_from_fraction( 1689 regamma->coeff.A2[i], 1000); 1690 coeff.a3[i] = dc_fixpt_from_fraction( 1691 regamma->coeff.A3[i], 1000); 1692 coeff.user_gamma[i] = dc_fixpt_from_fraction( 1693 regamma->coeff.gamma[i], 1000); 1694 1695 ++i; 1696 } while (i != 3); 1697 1698 i = 0; 1699 /* fixed_pt library has problems handling too small values */ 1700 while (i != 32) { 1701 output_tf->tf_pts.red[i] = dc_fixpt_zero; 1702 output_tf->tf_pts.green[i] = dc_fixpt_zero; 1703 output_tf->tf_pts.blue[i] = dc_fixpt_zero; 1704 ++coord_x; 1705 ++i; 1706 } 1707 while (i != MAX_HW_POINTS + 1) { 1708 output_tf->tf_pts.red[i] = translate_from_linear_space_ex( 1709 coord_x->x, &coeff, 0); 1710 output_tf->tf_pts.green[i] = translate_from_linear_space_ex( 1711 coord_x->x, &coeff, 1); 1712 output_tf->tf_pts.blue[i] = translate_from_linear_space_ex( 1713 coord_x->x, &coeff, 2); 1714 ++coord_x; 1715 ++i; 1716 } 1717 1718 // this function just clamps output to 0-1 1719 build_new_custom_resulted_curve(MAX_HW_POINTS, &output_tf->tf_pts); 1720 output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; 1721 1722 return true; 1723 } 1724 1725 bool calculate_user_regamma_ramp(struct dc_transfer_func *output_tf, 1726 const struct regamma_lut *regamma) 1727 { 1728 struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts; 1729 struct dividers dividers; 1730 1731 struct pwl_float_data *rgb_user = NULL; 1732 struct pwl_float_data_ex *rgb_regamma = NULL; 1733 bool ret = false; 1734 1735 if (regamma == NULL) 1736 return false; 1737 1738 output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; 1739 1740 rgb_user = kcalloc(GAMMA_RGB_256_ENTRIES + _EXTRA_POINTS, 1741 sizeof(*rgb_user), 1742 GFP_KERNEL); 1743 if (!rgb_user) 1744 goto rgb_user_alloc_fail; 1745 1746 rgb_regamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS, 1747 sizeof(*rgb_regamma), 1748 GFP_KERNEL); 1749 if (!rgb_regamma) 1750 goto rgb_regamma_alloc_fail; 1751 1752 dividers.divider1 = dc_fixpt_from_fraction(3, 2); 1753 dividers.divider2 = dc_fixpt_from_int(2); 1754 dividers.divider3 = dc_fixpt_from_fraction(5, 2); 1755 1756 scale_user_regamma_ramp(rgb_user, ®amma->ramp, dividers); 1757 1758 if (regamma->flags.bits.applyDegamma == 1) { 1759 apply_degamma_for_user_regamma(rgb_regamma, MAX_HW_POINTS); 1760 copy_rgb_regamma_to_coordinates_x(coordinates_x, 1761 MAX_HW_POINTS, rgb_regamma); 1762 } 1763 1764 interpolate_user_regamma(MAX_HW_POINTS, rgb_user, 1765 regamma->flags.bits.applyDegamma, tf_pts); 1766 1767 // no custom HDR curves! 1768 tf_pts->end_exponent = 0; 1769 tf_pts->x_point_at_y1_red = 1; 1770 tf_pts->x_point_at_y1_green = 1; 1771 tf_pts->x_point_at_y1_blue = 1; 1772 1773 // this function just clamps output to 0-1 1774 build_new_custom_resulted_curve(MAX_HW_POINTS, tf_pts); 1775 1776 ret = true; 1777 1778 kfree(rgb_regamma); 1779 rgb_regamma_alloc_fail: 1780 kfree(rgb_user); 1781 rgb_user_alloc_fail: 1782 return ret; 1783 } 1784 1785 bool mod_color_calculate_degamma_params(struct dc_color_caps *dc_caps, 1786 struct dc_transfer_func *input_tf, 1787 const struct dc_gamma *ramp, bool mapUserRamp) 1788 { 1789 struct dc_transfer_func_distributed_points *tf_pts = &input_tf->tf_pts; 1790 struct dividers dividers; 1791 struct pwl_float_data *rgb_user = NULL; 1792 struct pwl_float_data_ex *curve = NULL; 1793 struct gamma_pixel *axis_x = NULL; 1794 struct pixel_gamma_point *coeff = NULL; 1795 enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB; 1796 uint32_t i; 1797 bool ret = false; 1798 1799 if (input_tf->type == TF_TYPE_BYPASS) 1800 return false; 1801 1802 /* we can use hardcoded curve for plain SRGB TF 1803 * If linear, it's bypass if on user ramp 1804 */ 1805 if (input_tf->type == TF_TYPE_PREDEFINED) { 1806 if ((input_tf->tf == TRANSFER_FUNCTION_SRGB || 1807 input_tf->tf == TRANSFER_FUNCTION_LINEAR) && 1808 !mapUserRamp) 1809 return true; 1810 1811 if (dc_caps != NULL && 1812 dc_caps->dpp.dcn_arch == 1) { 1813 1814 if (input_tf->tf == TRANSFER_FUNCTION_PQ && 1815 dc_caps->dpp.dgam_rom_caps.pq == 1) 1816 return true; 1817 1818 if (input_tf->tf == TRANSFER_FUNCTION_GAMMA22 && 1819 dc_caps->dpp.dgam_rom_caps.gamma2_2 == 1) 1820 return true; 1821 1822 // HLG OOTF not accounted for 1823 if (input_tf->tf == TRANSFER_FUNCTION_HLG && 1824 dc_caps->dpp.dgam_rom_caps.hlg == 1) 1825 return true; 1826 } 1827 } 1828 1829 input_tf->type = TF_TYPE_DISTRIBUTED_POINTS; 1830 1831 if (mapUserRamp && ramp && ramp->type == GAMMA_RGB_256) { 1832 rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS, 1833 sizeof(*rgb_user), 1834 GFP_KERNEL); 1835 if (!rgb_user) 1836 goto rgb_user_alloc_fail; 1837 1838 axis_x = kvcalloc(ramp->num_entries + _EXTRA_POINTS, sizeof(*axis_x), 1839 GFP_KERNEL); 1840 if (!axis_x) 1841 goto axis_x_alloc_fail; 1842 1843 dividers.divider1 = dc_fixpt_from_fraction(3, 2); 1844 dividers.divider2 = dc_fixpt_from_int(2); 1845 dividers.divider3 = dc_fixpt_from_fraction(5, 2); 1846 1847 build_evenly_distributed_points( 1848 axis_x, 1849 ramp->num_entries, 1850 dividers); 1851 1852 scale_gamma(rgb_user, ramp, dividers); 1853 } 1854 1855 curve = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*curve), 1856 GFP_KERNEL); 1857 if (!curve) 1858 goto curve_alloc_fail; 1859 1860 coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff), 1861 GFP_KERNEL); 1862 if (!coeff) 1863 goto coeff_alloc_fail; 1864 1865 tf = input_tf->tf; 1866 1867 if (tf == TRANSFER_FUNCTION_PQ) 1868 build_de_pq(curve, 1869 MAX_HW_POINTS, 1870 coordinates_x); 1871 else if (tf == TRANSFER_FUNCTION_SRGB || 1872 tf == TRANSFER_FUNCTION_BT709 || 1873 tf == TRANSFER_FUNCTION_GAMMA22 || 1874 tf == TRANSFER_FUNCTION_GAMMA24 || 1875 tf == TRANSFER_FUNCTION_GAMMA26) 1876 build_degamma(curve, 1877 MAX_HW_POINTS, 1878 coordinates_x, 1879 tf); 1880 else if (tf == TRANSFER_FUNCTION_HLG) 1881 build_hlg_degamma(curve, 1882 MAX_HW_POINTS, 1883 coordinates_x, 1884 80, 1000); 1885 else if (tf == TRANSFER_FUNCTION_LINEAR) { 1886 // just copy coordinates_x into curve 1887 i = 0; 1888 while (i != MAX_HW_POINTS + 1) { 1889 curve[i].r = coordinates_x[i].x; 1890 curve[i].g = curve[i].r; 1891 curve[i].b = curve[i].r; 1892 i++; 1893 } 1894 } else 1895 goto invalid_tf_fail; 1896 1897 tf_pts->end_exponent = 0; 1898 tf_pts->x_point_at_y1_red = 1; 1899 tf_pts->x_point_at_y1_green = 1; 1900 tf_pts->x_point_at_y1_blue = 1; 1901 1902 if (input_tf->tf == TRANSFER_FUNCTION_PQ) { 1903 /* just copy current rgb_regamma into tf_pts */ 1904 struct pwl_float_data_ex *curvePt = curve; 1905 int i = 0; 1906 1907 while (i <= MAX_HW_POINTS) { 1908 tf_pts->red[i] = curvePt->r; 1909 tf_pts->green[i] = curvePt->g; 1910 tf_pts->blue[i] = curvePt->b; 1911 ++curvePt; 1912 ++i; 1913 } 1914 } else { 1915 //clamps to 0-1 1916 map_regamma_hw_to_x_user(ramp, coeff, rgb_user, 1917 coordinates_x, axis_x, curve, 1918 MAX_HW_POINTS, tf_pts, 1919 mapUserRamp && ramp && ramp->type == GAMMA_RGB_256); 1920 } 1921 1922 1923 1924 if (ramp && ramp->type == GAMMA_CUSTOM) 1925 apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts); 1926 1927 ret = true; 1928 1929 invalid_tf_fail: 1930 kvfree(coeff); 1931 coeff_alloc_fail: 1932 kvfree(curve); 1933 curve_alloc_fail: 1934 kvfree(axis_x); 1935 axis_x_alloc_fail: 1936 kvfree(rgb_user); 1937 rgb_user_alloc_fail: 1938 1939 return ret; 1940 } 1941 1942 static bool calculate_curve(enum dc_transfer_func_predefined trans, 1943 struct dc_transfer_func_distributed_points *points, 1944 struct pwl_float_data_ex *rgb_regamma, 1945 const struct freesync_hdr_tf_params *fs_params, 1946 uint32_t sdr_ref_white_level) 1947 { 1948 uint32_t i; 1949 bool ret = false; 1950 1951 if (trans == TRANSFER_FUNCTION_UNITY || 1952 trans == TRANSFER_FUNCTION_LINEAR) { 1953 points->end_exponent = 0; 1954 points->x_point_at_y1_red = 1; 1955 points->x_point_at_y1_green = 1; 1956 points->x_point_at_y1_blue = 1; 1957 1958 for (i = 0; i <= MAX_HW_POINTS ; i++) { 1959 rgb_regamma[i].r = coordinates_x[i].x; 1960 rgb_regamma[i].g = coordinates_x[i].x; 1961 rgb_regamma[i].b = coordinates_x[i].x; 1962 } 1963 1964 ret = true; 1965 } else if (trans == TRANSFER_FUNCTION_PQ) { 1966 points->end_exponent = 7; 1967 points->x_point_at_y1_red = 125; 1968 points->x_point_at_y1_green = 125; 1969 points->x_point_at_y1_blue = 125; 1970 1971 build_pq(rgb_regamma, 1972 MAX_HW_POINTS, 1973 coordinates_x, 1974 sdr_ref_white_level); 1975 1976 ret = true; 1977 } else if (trans == TRANSFER_FUNCTION_GAMMA22 && 1978 fs_params != NULL && fs_params->skip_tm == 0) { 1979 build_freesync_hdr(rgb_regamma, 1980 MAX_HW_POINTS, 1981 coordinates_x, 1982 fs_params); 1983 1984 ret = true; 1985 } else if (trans == TRANSFER_FUNCTION_HLG) { 1986 points->end_exponent = 4; 1987 points->x_point_at_y1_red = 12; 1988 points->x_point_at_y1_green = 12; 1989 points->x_point_at_y1_blue = 12; 1990 1991 build_hlg_regamma(rgb_regamma, 1992 MAX_HW_POINTS, 1993 coordinates_x, 1994 80, 1000); 1995 1996 ret = true; 1997 } else { 1998 // trans == TRANSFER_FUNCTION_SRGB 1999 // trans == TRANSFER_FUNCTION_BT709 2000 // trans == TRANSFER_FUNCTION_GAMMA22 2001 // trans == TRANSFER_FUNCTION_GAMMA24 2002 // trans == TRANSFER_FUNCTION_GAMMA26 2003 points->end_exponent = 0; 2004 points->x_point_at_y1_red = 1; 2005 points->x_point_at_y1_green = 1; 2006 points->x_point_at_y1_blue = 1; 2007 2008 build_regamma(rgb_regamma, 2009 MAX_HW_POINTS, 2010 coordinates_x, 2011 trans); 2012 2013 ret = true; 2014 } 2015 2016 return ret; 2017 } 2018 2019 bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, 2020 const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed, 2021 const struct freesync_hdr_tf_params *fs_params) 2022 { 2023 struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts; 2024 struct dividers dividers; 2025 2026 struct pwl_float_data *rgb_user = NULL; 2027 struct pwl_float_data_ex *rgb_regamma = NULL; 2028 struct gamma_pixel *axis_x = NULL; 2029 struct pixel_gamma_point *coeff = NULL; 2030 enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB; 2031 bool ret = false; 2032 2033 if (output_tf->type == TF_TYPE_BYPASS) 2034 return false; 2035 2036 /* we can use hardcoded curve for plain SRGB TF */ 2037 if (output_tf->type == TF_TYPE_PREDEFINED && canRomBeUsed == true && 2038 output_tf->tf == TRANSFER_FUNCTION_SRGB) { 2039 if (ramp == NULL) 2040 return true; 2041 if ((ramp->is_identity && ramp->type != GAMMA_CS_TFM_1D) || 2042 (!mapUserRamp && ramp->type == GAMMA_RGB_256)) 2043 return true; 2044 } 2045 2046 output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; 2047 2048 if (ramp && ramp->type != GAMMA_CS_TFM_1D && 2049 (mapUserRamp || ramp->type != GAMMA_RGB_256)) { 2050 rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS, 2051 sizeof(*rgb_user), 2052 GFP_KERNEL); 2053 if (!rgb_user) 2054 goto rgb_user_alloc_fail; 2055 2056 axis_x = kvcalloc(ramp->num_entries + 3, sizeof(*axis_x), 2057 GFP_KERNEL); 2058 if (!axis_x) 2059 goto axis_x_alloc_fail; 2060 2061 dividers.divider1 = dc_fixpt_from_fraction(3, 2); 2062 dividers.divider2 = dc_fixpt_from_int(2); 2063 dividers.divider3 = dc_fixpt_from_fraction(5, 2); 2064 2065 build_evenly_distributed_points( 2066 axis_x, 2067 ramp->num_entries, 2068 dividers); 2069 2070 if (ramp->type == GAMMA_RGB_256 && mapUserRamp) 2071 scale_gamma(rgb_user, ramp, dividers); 2072 else if (ramp->type == GAMMA_RGB_FLOAT_1024) 2073 scale_gamma_dx(rgb_user, ramp, dividers); 2074 } 2075 2076 rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, 2077 sizeof(*rgb_regamma), 2078 GFP_KERNEL); 2079 if (!rgb_regamma) 2080 goto rgb_regamma_alloc_fail; 2081 2082 coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff), 2083 GFP_KERNEL); 2084 if (!coeff) 2085 goto coeff_alloc_fail; 2086 2087 tf = output_tf->tf; 2088 2089 ret = calculate_curve(tf, 2090 tf_pts, 2091 rgb_regamma, 2092 fs_params, 2093 output_tf->sdr_ref_white_level); 2094 2095 if (ret) { 2096 map_regamma_hw_to_x_user(ramp, coeff, rgb_user, 2097 coordinates_x, axis_x, rgb_regamma, 2098 MAX_HW_POINTS, tf_pts, 2099 (mapUserRamp || (ramp && ramp->type != GAMMA_RGB_256)) && 2100 (ramp && ramp->type != GAMMA_CS_TFM_1D)); 2101 2102 if (ramp && ramp->type == GAMMA_CS_TFM_1D) 2103 apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts); 2104 } 2105 2106 kvfree(coeff); 2107 coeff_alloc_fail: 2108 kvfree(rgb_regamma); 2109 rgb_regamma_alloc_fail: 2110 kvfree(axis_x); 2111 axis_x_alloc_fail: 2112 kvfree(rgb_user); 2113 rgb_user_alloc_fail: 2114 return ret; 2115 } 2116 2117 bool mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans, 2118 struct dc_transfer_func_distributed_points *points) 2119 { 2120 uint32_t i; 2121 bool ret = false; 2122 struct pwl_float_data_ex *rgb_degamma = NULL; 2123 2124 if (trans == TRANSFER_FUNCTION_UNITY || 2125 trans == TRANSFER_FUNCTION_LINEAR) { 2126 2127 for (i = 0; i <= MAX_HW_POINTS ; i++) { 2128 points->red[i] = coordinates_x[i].x; 2129 points->green[i] = coordinates_x[i].x; 2130 points->blue[i] = coordinates_x[i].x; 2131 } 2132 ret = true; 2133 } else if (trans == TRANSFER_FUNCTION_PQ) { 2134 rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, 2135 sizeof(*rgb_degamma), 2136 GFP_KERNEL); 2137 if (!rgb_degamma) 2138 goto rgb_degamma_alloc_fail; 2139 2140 2141 build_de_pq(rgb_degamma, 2142 MAX_HW_POINTS, 2143 coordinates_x); 2144 for (i = 0; i <= MAX_HW_POINTS ; i++) { 2145 points->red[i] = rgb_degamma[i].r; 2146 points->green[i] = rgb_degamma[i].g; 2147 points->blue[i] = rgb_degamma[i].b; 2148 } 2149 ret = true; 2150 2151 kvfree(rgb_degamma); 2152 } else if (trans == TRANSFER_FUNCTION_SRGB || 2153 trans == TRANSFER_FUNCTION_BT709 || 2154 trans == TRANSFER_FUNCTION_GAMMA22 || 2155 trans == TRANSFER_FUNCTION_GAMMA24 || 2156 trans == TRANSFER_FUNCTION_GAMMA26) { 2157 rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, 2158 sizeof(*rgb_degamma), 2159 GFP_KERNEL); 2160 if (!rgb_degamma) 2161 goto rgb_degamma_alloc_fail; 2162 2163 build_degamma(rgb_degamma, 2164 MAX_HW_POINTS, 2165 coordinates_x, 2166 trans); 2167 for (i = 0; i <= MAX_HW_POINTS ; i++) { 2168 points->red[i] = rgb_degamma[i].r; 2169 points->green[i] = rgb_degamma[i].g; 2170 points->blue[i] = rgb_degamma[i].b; 2171 } 2172 ret = true; 2173 2174 kvfree(rgb_degamma); 2175 } else if (trans == TRANSFER_FUNCTION_HLG) { 2176 rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, 2177 sizeof(*rgb_degamma), 2178 GFP_KERNEL); 2179 if (!rgb_degamma) 2180 goto rgb_degamma_alloc_fail; 2181 2182 build_hlg_degamma(rgb_degamma, 2183 MAX_HW_POINTS, 2184 coordinates_x, 2185 80, 1000); 2186 for (i = 0; i <= MAX_HW_POINTS ; i++) { 2187 points->red[i] = rgb_degamma[i].r; 2188 points->green[i] = rgb_degamma[i].g; 2189 points->blue[i] = rgb_degamma[i].b; 2190 } 2191 ret = true; 2192 kvfree(rgb_degamma); 2193 } 2194 points->end_exponent = 0; 2195 points->x_point_at_y1_red = 1; 2196 points->x_point_at_y1_green = 1; 2197 points->x_point_at_y1_blue = 1; 2198 2199 rgb_degamma_alloc_fail: 2200 return ret; 2201 } 2202