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 "dc.h" 27 #include "opp.h" 28 #include "color_gamma.h" 29 30 31 #define NUM_PTS_IN_REGION 16 32 #define NUM_REGIONS 32 33 #define MAX_HW_POINTS (NUM_PTS_IN_REGION*NUM_REGIONS) 34 35 static struct hw_x_point coordinates_x[MAX_HW_POINTS + 2]; 36 37 static struct fixed31_32 pq_table[MAX_HW_POINTS + 2]; 38 static struct fixed31_32 de_pq_table[MAX_HW_POINTS + 2]; 39 40 static bool pq_initialized; /* = false; */ 41 static bool de_pq_initialized; /* = false; */ 42 43 /* one-time setup of X points */ 44 void setup_x_points_distribution(void) 45 { 46 struct fixed31_32 region_size = dc_fixpt_from_int(128); 47 int32_t segment; 48 uint32_t seg_offset; 49 uint32_t index; 50 struct fixed31_32 increment; 51 52 coordinates_x[MAX_HW_POINTS].x = region_size; 53 coordinates_x[MAX_HW_POINTS + 1].x = region_size; 54 55 for (segment = 6; segment > (6 - NUM_REGIONS); segment--) { 56 region_size = dc_fixpt_div_int(region_size, 2); 57 increment = dc_fixpt_div_int(region_size, 58 NUM_PTS_IN_REGION); 59 seg_offset = (segment + (NUM_REGIONS - 7)) * NUM_PTS_IN_REGION; 60 coordinates_x[seg_offset].x = region_size; 61 62 for (index = seg_offset + 1; 63 index < seg_offset + NUM_PTS_IN_REGION; 64 index++) { 65 coordinates_x[index].x = dc_fixpt_add 66 (coordinates_x[index-1].x, increment); 67 } 68 } 69 } 70 71 static void compute_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y) 72 { 73 /* consts for PQ gamma formula. */ 74 const struct fixed31_32 m1 = 75 dc_fixpt_from_fraction(159301758, 1000000000); 76 const struct fixed31_32 m2 = 77 dc_fixpt_from_fraction(7884375, 100000); 78 const struct fixed31_32 c1 = 79 dc_fixpt_from_fraction(8359375, 10000000); 80 const struct fixed31_32 c2 = 81 dc_fixpt_from_fraction(188515625, 10000000); 82 const struct fixed31_32 c3 = 83 dc_fixpt_from_fraction(186875, 10000); 84 85 struct fixed31_32 l_pow_m1; 86 struct fixed31_32 base; 87 88 if (dc_fixpt_lt(in_x, dc_fixpt_zero)) 89 in_x = dc_fixpt_zero; 90 91 l_pow_m1 = dc_fixpt_pow(in_x, m1); 92 base = dc_fixpt_div( 93 dc_fixpt_add(c1, 94 (dc_fixpt_mul(c2, l_pow_m1))), 95 dc_fixpt_add(dc_fixpt_one, 96 (dc_fixpt_mul(c3, l_pow_m1)))); 97 *out_y = dc_fixpt_pow(base, m2); 98 } 99 100 static void compute_de_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y) 101 { 102 /* consts for dePQ gamma formula. */ 103 const struct fixed31_32 m1 = 104 dc_fixpt_from_fraction(159301758, 1000000000); 105 const struct fixed31_32 m2 = 106 dc_fixpt_from_fraction(7884375, 100000); 107 const struct fixed31_32 c1 = 108 dc_fixpt_from_fraction(8359375, 10000000); 109 const struct fixed31_32 c2 = 110 dc_fixpt_from_fraction(188515625, 10000000); 111 const struct fixed31_32 c3 = 112 dc_fixpt_from_fraction(186875, 10000); 113 114 struct fixed31_32 l_pow_m1; 115 struct fixed31_32 base, div; 116 117 118 if (dc_fixpt_lt(in_x, dc_fixpt_zero)) 119 in_x = dc_fixpt_zero; 120 121 l_pow_m1 = dc_fixpt_pow(in_x, 122 dc_fixpt_div(dc_fixpt_one, m2)); 123 base = dc_fixpt_sub(l_pow_m1, c1); 124 125 if (dc_fixpt_lt(base, dc_fixpt_zero)) 126 base = dc_fixpt_zero; 127 128 div = dc_fixpt_sub(c2, dc_fixpt_mul(c3, l_pow_m1)); 129 130 *out_y = dc_fixpt_pow(dc_fixpt_div(base, div), 131 dc_fixpt_div(dc_fixpt_one, m1)); 132 133 } 134 135 /*de gamma, none linear to linear*/ 136 static void compute_hlg_oetf(struct fixed31_32 in_x, bool is_light0_12, struct fixed31_32 *out_y) 137 { 138 struct fixed31_32 a; 139 struct fixed31_32 b; 140 struct fixed31_32 c; 141 struct fixed31_32 threshold; 142 struct fixed31_32 reference_white_level; 143 144 a = dc_fixpt_from_fraction(17883277, 100000000); 145 if (is_light0_12) { 146 /*light 0-12*/ 147 b = dc_fixpt_from_fraction(28466892, 100000000); 148 c = dc_fixpt_from_fraction(55991073, 100000000); 149 threshold = dc_fixpt_one; 150 reference_white_level = dc_fixpt_half; 151 } else { 152 /*light 0-1*/ 153 b = dc_fixpt_from_fraction(2372241, 100000000); 154 c = dc_fixpt_add(dc_fixpt_one, dc_fixpt_from_fraction(429347, 100000000)); 155 threshold = dc_fixpt_from_fraction(1, 12); 156 reference_white_level = dc_fixpt_pow(dc_fixpt_from_fraction(3, 1), dc_fixpt_half); 157 } 158 if (dc_fixpt_lt(threshold, in_x)) 159 *out_y = dc_fixpt_add(c, dc_fixpt_mul(a, dc_fixpt_log(dc_fixpt_sub(in_x, b)))); 160 else 161 *out_y = dc_fixpt_mul(dc_fixpt_pow(in_x, dc_fixpt_half), reference_white_level); 162 } 163 164 /*re gamma, linear to none linear*/ 165 static void compute_hlg_eotf(struct fixed31_32 in_x, bool is_light0_12, struct fixed31_32 *out_y) 166 { 167 struct fixed31_32 a; 168 struct fixed31_32 b; 169 struct fixed31_32 c; 170 struct fixed31_32 reference_white_level; 171 172 a = dc_fixpt_from_fraction(17883277, 100000000); 173 if (is_light0_12) { 174 /*light 0-12*/ 175 b = dc_fixpt_from_fraction(28466892, 100000000); 176 c = dc_fixpt_from_fraction(55991073, 100000000); 177 reference_white_level = dc_fixpt_from_fraction(4, 1); 178 } else { 179 /*light 0-1*/ 180 b = dc_fixpt_from_fraction(2372241, 100000000); 181 c = dc_fixpt_add(dc_fixpt_one, dc_fixpt_from_fraction(429347, 100000000)); 182 reference_white_level = dc_fixpt_from_fraction(1, 3); 183 } 184 if (dc_fixpt_lt(dc_fixpt_half, in_x)) 185 *out_y = dc_fixpt_add(dc_fixpt_exp(dc_fixpt_div(dc_fixpt_sub(in_x, c), a)), b); 186 else 187 *out_y = dc_fixpt_mul(dc_fixpt_pow(in_x, dc_fixpt_from_fraction(2, 1)), reference_white_level); 188 } 189 190 191 /* one-time pre-compute PQ values - only for sdr_white_level 80 */ 192 void precompute_pq(void) 193 { 194 int i; 195 struct fixed31_32 x; 196 const struct hw_x_point *coord_x = coordinates_x + 32; 197 struct fixed31_32 scaling_factor = 198 dc_fixpt_from_fraction(80, 10000); 199 200 /* pow function has problems with arguments too small */ 201 for (i = 0; i < 32; i++) 202 pq_table[i] = dc_fixpt_zero; 203 204 for (i = 32; i <= MAX_HW_POINTS; i++) { 205 x = dc_fixpt_mul(coord_x->x, scaling_factor); 206 compute_pq(x, &pq_table[i]); 207 ++coord_x; 208 } 209 } 210 211 /* one-time pre-compute dePQ values - only for max pixel value 125 FP16 */ 212 void precompute_de_pq(void) 213 { 214 int i; 215 struct fixed31_32 y; 216 uint32_t begin_index, end_index; 217 218 struct fixed31_32 scaling_factor = dc_fixpt_from_int(125); 219 220 /* X points is 2^-25 to 2^7 221 * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions 222 */ 223 begin_index = 13 * NUM_PTS_IN_REGION; 224 end_index = begin_index + 12 * NUM_PTS_IN_REGION; 225 226 for (i = 0; i <= begin_index; i++) 227 de_pq_table[i] = dc_fixpt_zero; 228 229 for (; i <= end_index; i++) { 230 compute_de_pq(coordinates_x[i].x, &y); 231 de_pq_table[i] = dc_fixpt_mul(y, scaling_factor); 232 } 233 234 for (; i <= MAX_HW_POINTS; i++) 235 de_pq_table[i] = de_pq_table[i-1]; 236 } 237 struct dividers { 238 struct fixed31_32 divider1; 239 struct fixed31_32 divider2; 240 struct fixed31_32 divider3; 241 }; 242 243 static void build_coefficients(struct gamma_coefficients *coefficients, bool is_2_4) 244 { 245 static const int32_t numerator01[] = { 31308, 180000}; 246 static const int32_t numerator02[] = { 12920, 4500}; 247 static const int32_t numerator03[] = { 55, 99}; 248 static const int32_t numerator04[] = { 55, 99}; 249 static const int32_t numerator05[] = { 2400, 2200}; 250 251 uint32_t i = 0; 252 uint32_t index = is_2_4 == true ? 0:1; 253 254 do { 255 coefficients->a0[i] = dc_fixpt_from_fraction( 256 numerator01[index], 10000000); 257 coefficients->a1[i] = dc_fixpt_from_fraction( 258 numerator02[index], 1000); 259 coefficients->a2[i] = dc_fixpt_from_fraction( 260 numerator03[index], 1000); 261 coefficients->a3[i] = dc_fixpt_from_fraction( 262 numerator04[index], 1000); 263 coefficients->user_gamma[i] = dc_fixpt_from_fraction( 264 numerator05[index], 1000); 265 266 ++i; 267 } while (i != ARRAY_SIZE(coefficients->a0)); 268 } 269 270 static struct fixed31_32 translate_from_linear_space( 271 struct fixed31_32 arg, 272 struct fixed31_32 a0, 273 struct fixed31_32 a1, 274 struct fixed31_32 a2, 275 struct fixed31_32 a3, 276 struct fixed31_32 gamma) 277 { 278 const struct fixed31_32 one = dc_fixpt_from_int(1); 279 280 if (dc_fixpt_lt(one, arg)) 281 return one; 282 283 if (dc_fixpt_le(arg, dc_fixpt_neg(a0))) 284 return dc_fixpt_sub( 285 a2, 286 dc_fixpt_mul( 287 dc_fixpt_add( 288 one, 289 a3), 290 dc_fixpt_pow( 291 dc_fixpt_neg(arg), 292 dc_fixpt_recip(gamma)))); 293 else if (dc_fixpt_le(a0, arg)) 294 return dc_fixpt_sub( 295 dc_fixpt_mul( 296 dc_fixpt_add( 297 one, 298 a3), 299 dc_fixpt_pow( 300 arg, 301 dc_fixpt_recip(gamma))), 302 a2); 303 else 304 return dc_fixpt_mul( 305 arg, 306 a1); 307 } 308 309 static struct fixed31_32 translate_to_linear_space( 310 struct fixed31_32 arg, 311 struct fixed31_32 a0, 312 struct fixed31_32 a1, 313 struct fixed31_32 a2, 314 struct fixed31_32 a3, 315 struct fixed31_32 gamma) 316 { 317 struct fixed31_32 linear; 318 319 a0 = dc_fixpt_mul(a0, a1); 320 if (dc_fixpt_le(arg, dc_fixpt_neg(a0))) 321 322 linear = dc_fixpt_neg( 323 dc_fixpt_pow( 324 dc_fixpt_div( 325 dc_fixpt_sub(a2, arg), 326 dc_fixpt_add( 327 dc_fixpt_one, a3)), gamma)); 328 329 else if (dc_fixpt_le(dc_fixpt_neg(a0), arg) && 330 dc_fixpt_le(arg, a0)) 331 linear = dc_fixpt_div(arg, a1); 332 else 333 linear = dc_fixpt_pow( 334 dc_fixpt_div( 335 dc_fixpt_add(a2, arg), 336 dc_fixpt_add( 337 dc_fixpt_one, a3)), gamma); 338 339 return linear; 340 } 341 342 static inline struct fixed31_32 translate_from_linear_space_ex( 343 struct fixed31_32 arg, 344 struct gamma_coefficients *coeff, 345 uint32_t color_index) 346 { 347 return translate_from_linear_space( 348 arg, 349 coeff->a0[color_index], 350 coeff->a1[color_index], 351 coeff->a2[color_index], 352 coeff->a3[color_index], 353 coeff->user_gamma[color_index]); 354 } 355 356 357 static inline struct fixed31_32 translate_to_linear_space_ex( 358 struct fixed31_32 arg, 359 struct gamma_coefficients *coeff, 360 uint32_t color_index) 361 { 362 return translate_to_linear_space( 363 arg, 364 coeff->a0[color_index], 365 coeff->a1[color_index], 366 coeff->a2[color_index], 367 coeff->a3[color_index], 368 coeff->user_gamma[color_index]); 369 } 370 371 372 static bool find_software_points( 373 const struct dc_gamma *ramp, 374 const struct gamma_pixel *axis_x, 375 struct fixed31_32 hw_point, 376 enum channel_name channel, 377 uint32_t *index_to_start, 378 uint32_t *index_left, 379 uint32_t *index_right, 380 enum hw_point_position *pos) 381 { 382 const uint32_t max_number = ramp->num_entries + 3; 383 384 struct fixed31_32 left, right; 385 386 uint32_t i = *index_to_start; 387 388 while (i < max_number) { 389 if (channel == CHANNEL_NAME_RED) { 390 left = axis_x[i].r; 391 392 if (i < max_number - 1) 393 right = axis_x[i + 1].r; 394 else 395 right = axis_x[max_number - 1].r; 396 } else if (channel == CHANNEL_NAME_GREEN) { 397 left = axis_x[i].g; 398 399 if (i < max_number - 1) 400 right = axis_x[i + 1].g; 401 else 402 right = axis_x[max_number - 1].g; 403 } else { 404 left = axis_x[i].b; 405 406 if (i < max_number - 1) 407 right = axis_x[i + 1].b; 408 else 409 right = axis_x[max_number - 1].b; 410 } 411 412 if (dc_fixpt_le(left, hw_point) && 413 dc_fixpt_le(hw_point, right)) { 414 *index_to_start = i; 415 *index_left = i; 416 417 if (i < max_number - 1) 418 *index_right = i + 1; 419 else 420 *index_right = max_number - 1; 421 422 *pos = HW_POINT_POSITION_MIDDLE; 423 424 return true; 425 } else if ((i == *index_to_start) && 426 dc_fixpt_le(hw_point, left)) { 427 *index_to_start = i; 428 *index_left = i; 429 *index_right = i; 430 431 *pos = HW_POINT_POSITION_LEFT; 432 433 return true; 434 } else if ((i == max_number - 1) && 435 dc_fixpt_le(right, hw_point)) { 436 *index_to_start = i; 437 *index_left = i; 438 *index_right = i; 439 440 *pos = HW_POINT_POSITION_RIGHT; 441 442 return true; 443 } 444 445 ++i; 446 } 447 448 return false; 449 } 450 451 static bool build_custom_gamma_mapping_coefficients_worker( 452 const struct dc_gamma *ramp, 453 struct pixel_gamma_point *coeff, 454 const struct hw_x_point *coordinates_x, 455 const struct gamma_pixel *axis_x, 456 enum channel_name channel, 457 uint32_t number_of_points) 458 { 459 uint32_t i = 0; 460 461 while (i <= number_of_points) { 462 struct fixed31_32 coord_x; 463 464 uint32_t index_to_start = 0; 465 uint32_t index_left = 0; 466 uint32_t index_right = 0; 467 468 enum hw_point_position hw_pos; 469 470 struct gamma_point *point; 471 472 struct fixed31_32 left_pos; 473 struct fixed31_32 right_pos; 474 475 if (channel == CHANNEL_NAME_RED) 476 coord_x = coordinates_x[i].regamma_y_red; 477 else if (channel == CHANNEL_NAME_GREEN) 478 coord_x = coordinates_x[i].regamma_y_green; 479 else 480 coord_x = coordinates_x[i].regamma_y_blue; 481 482 if (!find_software_points( 483 ramp, axis_x, coord_x, channel, 484 &index_to_start, &index_left, &index_right, &hw_pos)) { 485 BREAK_TO_DEBUGGER(); 486 return false; 487 } 488 489 if (index_left >= ramp->num_entries + 3) { 490 BREAK_TO_DEBUGGER(); 491 return false; 492 } 493 494 if (index_right >= ramp->num_entries + 3) { 495 BREAK_TO_DEBUGGER(); 496 return false; 497 } 498 499 if (channel == CHANNEL_NAME_RED) { 500 point = &coeff[i].r; 501 502 left_pos = axis_x[index_left].r; 503 right_pos = axis_x[index_right].r; 504 } else if (channel == CHANNEL_NAME_GREEN) { 505 point = &coeff[i].g; 506 507 left_pos = axis_x[index_left].g; 508 right_pos = axis_x[index_right].g; 509 } else { 510 point = &coeff[i].b; 511 512 left_pos = axis_x[index_left].b; 513 right_pos = axis_x[index_right].b; 514 } 515 516 if (hw_pos == HW_POINT_POSITION_MIDDLE) 517 point->coeff = dc_fixpt_div( 518 dc_fixpt_sub( 519 coord_x, 520 left_pos), 521 dc_fixpt_sub( 522 right_pos, 523 left_pos)); 524 else if (hw_pos == HW_POINT_POSITION_LEFT) 525 point->coeff = dc_fixpt_zero; 526 else if (hw_pos == HW_POINT_POSITION_RIGHT) 527 point->coeff = dc_fixpt_from_int(2); 528 else { 529 BREAK_TO_DEBUGGER(); 530 return false; 531 } 532 533 point->left_index = index_left; 534 point->right_index = index_right; 535 point->pos = hw_pos; 536 537 ++i; 538 } 539 540 return true; 541 } 542 543 static struct fixed31_32 calculate_mapped_value( 544 struct pwl_float_data *rgb, 545 const struct pixel_gamma_point *coeff, 546 enum channel_name channel, 547 uint32_t max_index) 548 { 549 const struct gamma_point *point; 550 551 struct fixed31_32 result; 552 553 if (channel == CHANNEL_NAME_RED) 554 point = &coeff->r; 555 else if (channel == CHANNEL_NAME_GREEN) 556 point = &coeff->g; 557 else 558 point = &coeff->b; 559 560 if ((point->left_index < 0) || (point->left_index > max_index)) { 561 BREAK_TO_DEBUGGER(); 562 return dc_fixpt_zero; 563 } 564 565 if ((point->right_index < 0) || (point->right_index > max_index)) { 566 BREAK_TO_DEBUGGER(); 567 return dc_fixpt_zero; 568 } 569 570 if (point->pos == HW_POINT_POSITION_MIDDLE) 571 if (channel == CHANNEL_NAME_RED) 572 result = dc_fixpt_add( 573 dc_fixpt_mul( 574 point->coeff, 575 dc_fixpt_sub( 576 rgb[point->right_index].r, 577 rgb[point->left_index].r)), 578 rgb[point->left_index].r); 579 else if (channel == CHANNEL_NAME_GREEN) 580 result = dc_fixpt_add( 581 dc_fixpt_mul( 582 point->coeff, 583 dc_fixpt_sub( 584 rgb[point->right_index].g, 585 rgb[point->left_index].g)), 586 rgb[point->left_index].g); 587 else 588 result = dc_fixpt_add( 589 dc_fixpt_mul( 590 point->coeff, 591 dc_fixpt_sub( 592 rgb[point->right_index].b, 593 rgb[point->left_index].b)), 594 rgb[point->left_index].b); 595 else if (point->pos == HW_POINT_POSITION_LEFT) { 596 BREAK_TO_DEBUGGER(); 597 result = dc_fixpt_zero; 598 } else { 599 BREAK_TO_DEBUGGER(); 600 result = dc_fixpt_one; 601 } 602 603 return result; 604 } 605 606 static void build_pq(struct pwl_float_data_ex *rgb_regamma, 607 uint32_t hw_points_num, 608 const struct hw_x_point *coordinate_x, 609 uint32_t sdr_white_level) 610 { 611 uint32_t i, start_index; 612 613 struct pwl_float_data_ex *rgb = rgb_regamma; 614 const struct hw_x_point *coord_x = coordinate_x; 615 struct fixed31_32 x; 616 struct fixed31_32 output; 617 struct fixed31_32 scaling_factor = 618 dc_fixpt_from_fraction(sdr_white_level, 10000); 619 620 if (!pq_initialized && sdr_white_level == 80) { 621 precompute_pq(); 622 pq_initialized = true; 623 } 624 625 /* TODO: start index is from segment 2^-24, skipping first segment 626 * due to x values too small for power calculations 627 */ 628 start_index = 32; 629 rgb += start_index; 630 coord_x += start_index; 631 632 for (i = start_index; i <= hw_points_num; i++) { 633 /* Multiply 0.008 as regamma is 0-1 and FP16 input is 0-125. 634 * FP 1.0 = 80nits 635 */ 636 if (sdr_white_level == 80) { 637 output = pq_table[i]; 638 } else { 639 x = dc_fixpt_mul(coord_x->x, scaling_factor); 640 compute_pq(x, &output); 641 } 642 643 /* should really not happen? */ 644 if (dc_fixpt_lt(output, dc_fixpt_zero)) 645 output = dc_fixpt_zero; 646 else if (dc_fixpt_lt(dc_fixpt_one, output)) 647 output = dc_fixpt_one; 648 649 rgb->r = output; 650 rgb->g = output; 651 rgb->b = output; 652 653 ++coord_x; 654 ++rgb; 655 } 656 } 657 658 static void build_de_pq(struct pwl_float_data_ex *de_pq, 659 uint32_t hw_points_num, 660 const struct hw_x_point *coordinate_x) 661 { 662 uint32_t i; 663 struct fixed31_32 output; 664 665 struct fixed31_32 scaling_factor = dc_fixpt_from_int(125); 666 667 if (!de_pq_initialized) { 668 precompute_de_pq(); 669 de_pq_initialized = true; 670 } 671 672 673 for (i = 0; i <= hw_points_num; i++) { 674 output = de_pq_table[i]; 675 /* should really not happen? */ 676 if (dc_fixpt_lt(output, dc_fixpt_zero)) 677 output = dc_fixpt_zero; 678 else if (dc_fixpt_lt(scaling_factor, output)) 679 output = scaling_factor; 680 de_pq[i].r = output; 681 de_pq[i].g = output; 682 de_pq[i].b = output; 683 } 684 } 685 686 static void build_regamma(struct pwl_float_data_ex *rgb_regamma, 687 uint32_t hw_points_num, 688 const struct hw_x_point *coordinate_x, bool is_2_4) 689 { 690 uint32_t i; 691 692 struct gamma_coefficients coeff; 693 struct pwl_float_data_ex *rgb = rgb_regamma; 694 const struct hw_x_point *coord_x = coordinate_x; 695 696 build_coefficients(&coeff, is_2_4); 697 698 i = 0; 699 700 while (i != hw_points_num + 1) { 701 /*TODO use y vs r,g,b*/ 702 rgb->r = translate_from_linear_space_ex( 703 coord_x->x, &coeff, 0); 704 rgb->g = rgb->r; 705 rgb->b = rgb->r; 706 ++coord_x; 707 ++rgb; 708 ++i; 709 } 710 } 711 712 static void build_degamma(struct pwl_float_data_ex *curve, 713 uint32_t hw_points_num, 714 const struct hw_x_point *coordinate_x, bool is_2_4) 715 { 716 uint32_t i; 717 struct gamma_coefficients coeff; 718 uint32_t begin_index, end_index; 719 720 build_coefficients(&coeff, is_2_4); 721 i = 0; 722 723 /* X points is 2^-25 to 2^7 724 * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions 725 */ 726 begin_index = 13 * NUM_PTS_IN_REGION; 727 end_index = begin_index + 12 * NUM_PTS_IN_REGION; 728 729 while (i != begin_index) { 730 curve[i].r = dc_fixpt_zero; 731 curve[i].g = dc_fixpt_zero; 732 curve[i].b = dc_fixpt_zero; 733 i++; 734 } 735 736 while (i != end_index) { 737 curve[i].r = translate_to_linear_space_ex( 738 coordinate_x[i].x, &coeff, 0); 739 curve[i].g = curve[i].r; 740 curve[i].b = curve[i].r; 741 i++; 742 } 743 while (i != hw_points_num + 1) { 744 curve[i].r = dc_fixpt_one; 745 curve[i].g = dc_fixpt_one; 746 curve[i].b = dc_fixpt_one; 747 i++; 748 } 749 } 750 751 static void build_hlg_degamma(struct pwl_float_data_ex *degamma, 752 uint32_t hw_points_num, 753 const struct hw_x_point *coordinate_x, bool is_light0_12) 754 { 755 uint32_t i; 756 757 struct pwl_float_data_ex *rgb = degamma; 758 const struct hw_x_point *coord_x = coordinate_x; 759 760 i = 0; 761 762 while (i != hw_points_num + 1) { 763 compute_hlg_oetf(coord_x->x, is_light0_12, &rgb->r); 764 rgb->g = rgb->r; 765 rgb->b = rgb->r; 766 ++coord_x; 767 ++rgb; 768 ++i; 769 } 770 } 771 772 static void build_hlg_regamma(struct pwl_float_data_ex *regamma, 773 uint32_t hw_points_num, 774 const struct hw_x_point *coordinate_x, bool is_light0_12) 775 { 776 uint32_t i; 777 778 struct pwl_float_data_ex *rgb = regamma; 779 const struct hw_x_point *coord_x = coordinate_x; 780 781 i = 0; 782 783 while (i != hw_points_num + 1) { 784 compute_hlg_eotf(coord_x->x, is_light0_12, &rgb->r); 785 rgb->g = rgb->r; 786 rgb->b = rgb->r; 787 ++coord_x; 788 ++rgb; 789 ++i; 790 } 791 } 792 793 static void scale_gamma(struct pwl_float_data *pwl_rgb, 794 const struct dc_gamma *ramp, 795 struct dividers dividers) 796 { 797 const struct fixed31_32 max_driver = dc_fixpt_from_int(0xFFFF); 798 const struct fixed31_32 max_os = dc_fixpt_from_int(0xFF00); 799 struct fixed31_32 scaler = max_os; 800 uint32_t i; 801 struct pwl_float_data *rgb = pwl_rgb; 802 struct pwl_float_data *rgb_last = rgb + ramp->num_entries - 1; 803 804 i = 0; 805 806 do { 807 if (dc_fixpt_lt(max_os, ramp->entries.red[i]) || 808 dc_fixpt_lt(max_os, ramp->entries.green[i]) || 809 dc_fixpt_lt(max_os, ramp->entries.blue[i])) { 810 scaler = max_driver; 811 break; 812 } 813 ++i; 814 } while (i != ramp->num_entries); 815 816 i = 0; 817 818 do { 819 rgb->r = dc_fixpt_div( 820 ramp->entries.red[i], scaler); 821 rgb->g = dc_fixpt_div( 822 ramp->entries.green[i], scaler); 823 rgb->b = dc_fixpt_div( 824 ramp->entries.blue[i], scaler); 825 826 ++rgb; 827 ++i; 828 } while (i != ramp->num_entries); 829 830 rgb->r = dc_fixpt_mul(rgb_last->r, 831 dividers.divider1); 832 rgb->g = dc_fixpt_mul(rgb_last->g, 833 dividers.divider1); 834 rgb->b = dc_fixpt_mul(rgb_last->b, 835 dividers.divider1); 836 837 ++rgb; 838 839 rgb->r = dc_fixpt_mul(rgb_last->r, 840 dividers.divider2); 841 rgb->g = dc_fixpt_mul(rgb_last->g, 842 dividers.divider2); 843 rgb->b = dc_fixpt_mul(rgb_last->b, 844 dividers.divider2); 845 846 ++rgb; 847 848 rgb->r = dc_fixpt_mul(rgb_last->r, 849 dividers.divider3); 850 rgb->g = dc_fixpt_mul(rgb_last->g, 851 dividers.divider3); 852 rgb->b = dc_fixpt_mul(rgb_last->b, 853 dividers.divider3); 854 } 855 856 static void scale_gamma_dx(struct pwl_float_data *pwl_rgb, 857 const struct dc_gamma *ramp, 858 struct dividers dividers) 859 { 860 uint32_t i; 861 struct fixed31_32 min = dc_fixpt_zero; 862 struct fixed31_32 max = dc_fixpt_one; 863 864 struct fixed31_32 delta = dc_fixpt_zero; 865 struct fixed31_32 offset = dc_fixpt_zero; 866 867 for (i = 0 ; i < ramp->num_entries; i++) { 868 if (dc_fixpt_lt(ramp->entries.red[i], min)) 869 min = ramp->entries.red[i]; 870 871 if (dc_fixpt_lt(ramp->entries.green[i], min)) 872 min = ramp->entries.green[i]; 873 874 if (dc_fixpt_lt(ramp->entries.blue[i], min)) 875 min = ramp->entries.blue[i]; 876 877 if (dc_fixpt_lt(max, ramp->entries.red[i])) 878 max = ramp->entries.red[i]; 879 880 if (dc_fixpt_lt(max, ramp->entries.green[i])) 881 max = ramp->entries.green[i]; 882 883 if (dc_fixpt_lt(max, ramp->entries.blue[i])) 884 max = ramp->entries.blue[i]; 885 } 886 887 if (dc_fixpt_lt(min, dc_fixpt_zero)) 888 delta = dc_fixpt_neg(min); 889 890 offset = dc_fixpt_add(min, max); 891 892 for (i = 0 ; i < ramp->num_entries; i++) { 893 pwl_rgb[i].r = dc_fixpt_div( 894 dc_fixpt_add( 895 ramp->entries.red[i], delta), offset); 896 pwl_rgb[i].g = dc_fixpt_div( 897 dc_fixpt_add( 898 ramp->entries.green[i], delta), offset); 899 pwl_rgb[i].b = dc_fixpt_div( 900 dc_fixpt_add( 901 ramp->entries.blue[i], delta), offset); 902 903 } 904 905 pwl_rgb[i].r = dc_fixpt_sub(dc_fixpt_mul_int( 906 pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r); 907 pwl_rgb[i].g = dc_fixpt_sub(dc_fixpt_mul_int( 908 pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g); 909 pwl_rgb[i].b = dc_fixpt_sub(dc_fixpt_mul_int( 910 pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b); 911 ++i; 912 pwl_rgb[i].r = dc_fixpt_sub(dc_fixpt_mul_int( 913 pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r); 914 pwl_rgb[i].g = dc_fixpt_sub(dc_fixpt_mul_int( 915 pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g); 916 pwl_rgb[i].b = dc_fixpt_sub(dc_fixpt_mul_int( 917 pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b); 918 } 919 920 /* todo: all these scale_gamma functions are inherently the same but 921 * take different structures as params or different format for ramp 922 * values. We could probably implement it in a more generic fashion 923 */ 924 static void scale_user_regamma_ramp(struct pwl_float_data *pwl_rgb, 925 const struct regamma_ramp *ramp, 926 struct dividers dividers) 927 { 928 unsigned short max_driver = 0xFFFF; 929 unsigned short max_os = 0xFF00; 930 unsigned short scaler = max_os; 931 uint32_t i; 932 struct pwl_float_data *rgb = pwl_rgb; 933 struct pwl_float_data *rgb_last = rgb + GAMMA_RGB_256_ENTRIES - 1; 934 935 i = 0; 936 do { 937 if (ramp->gamma[i] > max_os || 938 ramp->gamma[i + 256] > max_os || 939 ramp->gamma[i + 512] > max_os) { 940 scaler = max_driver; 941 break; 942 } 943 i++; 944 } while (i != GAMMA_RGB_256_ENTRIES); 945 946 i = 0; 947 do { 948 rgb->r = dc_fixpt_from_fraction( 949 ramp->gamma[i], scaler); 950 rgb->g = dc_fixpt_from_fraction( 951 ramp->gamma[i + 256], scaler); 952 rgb->b = dc_fixpt_from_fraction( 953 ramp->gamma[i + 512], scaler); 954 955 ++rgb; 956 ++i; 957 } while (i != GAMMA_RGB_256_ENTRIES); 958 959 rgb->r = dc_fixpt_mul(rgb_last->r, 960 dividers.divider1); 961 rgb->g = dc_fixpt_mul(rgb_last->g, 962 dividers.divider1); 963 rgb->b = dc_fixpt_mul(rgb_last->b, 964 dividers.divider1); 965 966 ++rgb; 967 968 rgb->r = dc_fixpt_mul(rgb_last->r, 969 dividers.divider2); 970 rgb->g = dc_fixpt_mul(rgb_last->g, 971 dividers.divider2); 972 rgb->b = dc_fixpt_mul(rgb_last->b, 973 dividers.divider2); 974 975 ++rgb; 976 977 rgb->r = dc_fixpt_mul(rgb_last->r, 978 dividers.divider3); 979 rgb->g = dc_fixpt_mul(rgb_last->g, 980 dividers.divider3); 981 rgb->b = dc_fixpt_mul(rgb_last->b, 982 dividers.divider3); 983 } 984 985 /* 986 * RS3+ color transform DDI - 1D LUT adjustment is composed with regamma here 987 * Input is evenly distributed in the output color space as specified in 988 * SetTimings 989 * 990 * Interpolation details: 991 * 1D LUT has 4096 values which give curve correction in 0-1 float range 992 * for evenly spaced points in 0-1 range. lut1D[index] gives correction 993 * for index/4095. 994 * First we find index for which: 995 * index/4095 < regamma_y < (index+1)/4095 => 996 * index < 4095*regamma_y < index + 1 997 * norm_y = 4095*regamma_y, and index is just truncating to nearest integer 998 * lut1 = lut1D[index], lut2 = lut1D[index+1] 999 * 1000 * adjustedY is then linearly interpolating regamma Y between lut1 and lut2 1001 * 1002 * Custom degamma on Linux uses the same interpolation math, so is handled here 1003 */ 1004 static void apply_lut_1d( 1005 const struct dc_gamma *ramp, 1006 uint32_t num_hw_points, 1007 struct dc_transfer_func_distributed_points *tf_pts) 1008 { 1009 int i = 0; 1010 int color = 0; 1011 struct fixed31_32 *regamma_y; 1012 struct fixed31_32 norm_y; 1013 struct fixed31_32 lut1; 1014 struct fixed31_32 lut2; 1015 const int max_lut_index = 4095; 1016 const struct fixed31_32 max_lut_index_f = 1017 dc_fixpt_from_int(max_lut_index); 1018 int32_t index = 0, index_next = 0; 1019 struct fixed31_32 index_f; 1020 struct fixed31_32 delta_lut; 1021 struct fixed31_32 delta_index; 1022 1023 if (ramp->type != GAMMA_CS_TFM_1D && ramp->type != GAMMA_CUSTOM) 1024 return; // this is not expected 1025 1026 for (i = 0; i < num_hw_points; i++) { 1027 for (color = 0; color < 3; color++) { 1028 if (color == 0) 1029 regamma_y = &tf_pts->red[i]; 1030 else if (color == 1) 1031 regamma_y = &tf_pts->green[i]; 1032 else 1033 regamma_y = &tf_pts->blue[i]; 1034 1035 norm_y = dc_fixpt_mul(max_lut_index_f, 1036 *regamma_y); 1037 index = dc_fixpt_floor(norm_y); 1038 index_f = dc_fixpt_from_int(index); 1039 1040 if (index < 0 || index > max_lut_index) 1041 continue; 1042 1043 index_next = (index == max_lut_index) ? index : index+1; 1044 1045 if (color == 0) { 1046 lut1 = ramp->entries.red[index]; 1047 lut2 = ramp->entries.red[index_next]; 1048 } else if (color == 1) { 1049 lut1 = ramp->entries.green[index]; 1050 lut2 = ramp->entries.green[index_next]; 1051 } else { 1052 lut1 = ramp->entries.blue[index]; 1053 lut2 = ramp->entries.blue[index_next]; 1054 } 1055 1056 // we have everything now, so interpolate 1057 delta_lut = dc_fixpt_sub(lut2, lut1); 1058 delta_index = dc_fixpt_sub(norm_y, index_f); 1059 1060 *regamma_y = dc_fixpt_add(lut1, 1061 dc_fixpt_mul(delta_index, delta_lut)); 1062 } 1063 } 1064 } 1065 1066 static void build_evenly_distributed_points( 1067 struct gamma_pixel *points, 1068 uint32_t numberof_points, 1069 struct dividers dividers) 1070 { 1071 struct gamma_pixel *p = points; 1072 struct gamma_pixel *p_last; 1073 1074 uint32_t i = 0; 1075 1076 // This function should not gets called with 0 as a parameter 1077 ASSERT(numberof_points > 0); 1078 p_last = p + numberof_points - 1; 1079 1080 do { 1081 struct fixed31_32 value = dc_fixpt_from_fraction(i, 1082 numberof_points - 1); 1083 1084 p->r = value; 1085 p->g = value; 1086 p->b = value; 1087 1088 ++p; 1089 ++i; 1090 } while (i < numberof_points); 1091 1092 p->r = dc_fixpt_div(p_last->r, dividers.divider1); 1093 p->g = dc_fixpt_div(p_last->g, dividers.divider1); 1094 p->b = dc_fixpt_div(p_last->b, dividers.divider1); 1095 1096 ++p; 1097 1098 p->r = dc_fixpt_div(p_last->r, dividers.divider2); 1099 p->g = dc_fixpt_div(p_last->g, dividers.divider2); 1100 p->b = dc_fixpt_div(p_last->b, dividers.divider2); 1101 1102 ++p; 1103 1104 p->r = dc_fixpt_div(p_last->r, dividers.divider3); 1105 p->g = dc_fixpt_div(p_last->g, dividers.divider3); 1106 p->b = dc_fixpt_div(p_last->b, dividers.divider3); 1107 } 1108 1109 static inline void copy_rgb_regamma_to_coordinates_x( 1110 struct hw_x_point *coordinates_x, 1111 uint32_t hw_points_num, 1112 const struct pwl_float_data_ex *rgb_ex) 1113 { 1114 struct hw_x_point *coords = coordinates_x; 1115 uint32_t i = 0; 1116 const struct pwl_float_data_ex *rgb_regamma = rgb_ex; 1117 1118 while (i <= hw_points_num + 1) { 1119 coords->regamma_y_red = rgb_regamma->r; 1120 coords->regamma_y_green = rgb_regamma->g; 1121 coords->regamma_y_blue = rgb_regamma->b; 1122 1123 ++coords; 1124 ++rgb_regamma; 1125 ++i; 1126 } 1127 } 1128 1129 static bool calculate_interpolated_hardware_curve( 1130 const struct dc_gamma *ramp, 1131 struct pixel_gamma_point *coeff128, 1132 struct pwl_float_data *rgb_user, 1133 const struct hw_x_point *coordinates_x, 1134 const struct gamma_pixel *axis_x, 1135 uint32_t number_of_points, 1136 struct dc_transfer_func_distributed_points *tf_pts) 1137 { 1138 1139 const struct pixel_gamma_point *coeff = coeff128; 1140 uint32_t max_entries = 3 - 1; 1141 1142 uint32_t i = 0; 1143 1144 for (i = 0; i < 3; i++) { 1145 if (!build_custom_gamma_mapping_coefficients_worker( 1146 ramp, coeff128, coordinates_x, axis_x, i, 1147 number_of_points)) 1148 return false; 1149 } 1150 1151 i = 0; 1152 max_entries += ramp->num_entries; 1153 1154 /* TODO: float point case */ 1155 1156 while (i <= number_of_points) { 1157 tf_pts->red[i] = calculate_mapped_value( 1158 rgb_user, coeff, CHANNEL_NAME_RED, max_entries); 1159 tf_pts->green[i] = calculate_mapped_value( 1160 rgb_user, coeff, CHANNEL_NAME_GREEN, max_entries); 1161 tf_pts->blue[i] = calculate_mapped_value( 1162 rgb_user, coeff, CHANNEL_NAME_BLUE, max_entries); 1163 1164 ++coeff; 1165 ++i; 1166 } 1167 1168 return true; 1169 } 1170 1171 /* The "old" interpolation uses a complicated scheme to build an array of 1172 * coefficients while also using an array of 0-255 normalized to 0-1 1173 * Then there's another loop using both of the above + new scaled user ramp 1174 * and we concatenate them. It also searches for points of interpolation and 1175 * uses enums for positions. 1176 * 1177 * This function uses a different approach: 1178 * user ramp is always applied on X with 0/255, 1/255, 2/255, ..., 255/255 1179 * To find index for hwX , we notice the following: 1180 * i/255 <= hwX < (i+1)/255 <=> i <= 255*hwX < i+1 1181 * See apply_lut_1d which is the same principle, but on 4K entry 1D LUT 1182 * 1183 * Once the index is known, combined Y is simply: 1184 * user_ramp(index) + (hwX-index/255)*(user_ramp(index+1) - user_ramp(index) 1185 * 1186 * We should switch to this method in all cases, it's simpler and faster 1187 * ToDo one day - for now this only applies to ADL regamma to avoid regression 1188 * for regular use cases (sRGB and PQ) 1189 */ 1190 static void interpolate_user_regamma(uint32_t hw_points_num, 1191 struct pwl_float_data *rgb_user, 1192 bool apply_degamma, 1193 struct dc_transfer_func_distributed_points *tf_pts) 1194 { 1195 uint32_t i; 1196 uint32_t color = 0; 1197 int32_t index; 1198 int32_t index_next; 1199 struct fixed31_32 *tf_point; 1200 struct fixed31_32 hw_x; 1201 struct fixed31_32 norm_factor = 1202 dc_fixpt_from_int(255); 1203 struct fixed31_32 norm_x; 1204 struct fixed31_32 index_f; 1205 struct fixed31_32 lut1; 1206 struct fixed31_32 lut2; 1207 struct fixed31_32 delta_lut; 1208 struct fixed31_32 delta_index; 1209 1210 i = 0; 1211 /* fixed_pt library has problems handling too small values */ 1212 while (i != 32) { 1213 tf_pts->red[i] = dc_fixpt_zero; 1214 tf_pts->green[i] = dc_fixpt_zero; 1215 tf_pts->blue[i] = dc_fixpt_zero; 1216 ++i; 1217 } 1218 while (i <= hw_points_num + 1) { 1219 for (color = 0; color < 3; color++) { 1220 if (color == 0) 1221 tf_point = &tf_pts->red[i]; 1222 else if (color == 1) 1223 tf_point = &tf_pts->green[i]; 1224 else 1225 tf_point = &tf_pts->blue[i]; 1226 1227 if (apply_degamma) { 1228 if (color == 0) 1229 hw_x = coordinates_x[i].regamma_y_red; 1230 else if (color == 1) 1231 hw_x = coordinates_x[i].regamma_y_green; 1232 else 1233 hw_x = coordinates_x[i].regamma_y_blue; 1234 } else 1235 hw_x = coordinates_x[i].x; 1236 1237 norm_x = dc_fixpt_mul(norm_factor, hw_x); 1238 index = dc_fixpt_floor(norm_x); 1239 if (index < 0 || index > 255) 1240 continue; 1241 1242 index_f = dc_fixpt_from_int(index); 1243 index_next = (index == 255) ? index : index + 1; 1244 1245 if (color == 0) { 1246 lut1 = rgb_user[index].r; 1247 lut2 = rgb_user[index_next].r; 1248 } else if (color == 1) { 1249 lut1 = rgb_user[index].g; 1250 lut2 = rgb_user[index_next].g; 1251 } else { 1252 lut1 = rgb_user[index].b; 1253 lut2 = rgb_user[index_next].b; 1254 } 1255 1256 // we have everything now, so interpolate 1257 delta_lut = dc_fixpt_sub(lut2, lut1); 1258 delta_index = dc_fixpt_sub(norm_x, index_f); 1259 1260 *tf_point = dc_fixpt_add(lut1, 1261 dc_fixpt_mul(delta_index, delta_lut)); 1262 } 1263 ++i; 1264 } 1265 } 1266 1267 static void build_new_custom_resulted_curve( 1268 uint32_t hw_points_num, 1269 struct dc_transfer_func_distributed_points *tf_pts) 1270 { 1271 uint32_t i; 1272 1273 i = 0; 1274 1275 while (i != hw_points_num + 1) { 1276 tf_pts->red[i] = dc_fixpt_clamp( 1277 tf_pts->red[i], dc_fixpt_zero, 1278 dc_fixpt_one); 1279 tf_pts->green[i] = dc_fixpt_clamp( 1280 tf_pts->green[i], dc_fixpt_zero, 1281 dc_fixpt_one); 1282 tf_pts->blue[i] = dc_fixpt_clamp( 1283 tf_pts->blue[i], dc_fixpt_zero, 1284 dc_fixpt_one); 1285 1286 ++i; 1287 } 1288 } 1289 1290 static void apply_degamma_for_user_regamma(struct pwl_float_data_ex *rgb_regamma, 1291 uint32_t hw_points_num) 1292 { 1293 uint32_t i; 1294 1295 struct gamma_coefficients coeff; 1296 struct pwl_float_data_ex *rgb = rgb_regamma; 1297 const struct hw_x_point *coord_x = coordinates_x; 1298 1299 build_coefficients(&coeff, true); 1300 1301 i = 0; 1302 while (i != hw_points_num + 1) { 1303 rgb->r = translate_from_linear_space_ex( 1304 coord_x->x, &coeff, 0); 1305 rgb->g = rgb->r; 1306 rgb->b = rgb->r; 1307 ++coord_x; 1308 ++rgb; 1309 ++i; 1310 } 1311 } 1312 1313 static bool map_regamma_hw_to_x_user( 1314 const struct dc_gamma *ramp, 1315 struct pixel_gamma_point *coeff128, 1316 struct pwl_float_data *rgb_user, 1317 struct hw_x_point *coords_x, 1318 const struct gamma_pixel *axis_x, 1319 const struct pwl_float_data_ex *rgb_regamma, 1320 uint32_t hw_points_num, 1321 struct dc_transfer_func_distributed_points *tf_pts, 1322 bool mapUserRamp) 1323 { 1324 /* setup to spare calculated ideal regamma values */ 1325 1326 int i = 0; 1327 struct hw_x_point *coords = coords_x; 1328 const struct pwl_float_data_ex *regamma = rgb_regamma; 1329 1330 if (mapUserRamp) { 1331 copy_rgb_regamma_to_coordinates_x(coords, 1332 hw_points_num, 1333 rgb_regamma); 1334 1335 calculate_interpolated_hardware_curve( 1336 ramp, coeff128, rgb_user, coords, axis_x, 1337 hw_points_num, tf_pts); 1338 } else { 1339 /* just copy current rgb_regamma into tf_pts */ 1340 while (i <= hw_points_num) { 1341 tf_pts->red[i] = regamma->r; 1342 tf_pts->green[i] = regamma->g; 1343 tf_pts->blue[i] = regamma->b; 1344 1345 ++regamma; 1346 ++i; 1347 } 1348 } 1349 1350 /* this should be named differently, all it does is clamp to 0-1 */ 1351 build_new_custom_resulted_curve(hw_points_num, tf_pts); 1352 1353 return true; 1354 } 1355 1356 #define _EXTRA_POINTS 3 1357 1358 bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, 1359 const struct dc_gamma *ramp, bool mapUserRamp) 1360 { 1361 struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts; 1362 struct dividers dividers; 1363 1364 struct pwl_float_data *rgb_user = NULL; 1365 struct pwl_float_data_ex *rgb_regamma = NULL; 1366 struct gamma_pixel *axix_x = NULL; 1367 struct pixel_gamma_point *coeff = NULL; 1368 enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB; 1369 bool ret = false; 1370 1371 if (output_tf->type == TF_TYPE_BYPASS) 1372 return false; 1373 1374 /* we can use hardcoded curve for plain SRGB TF */ 1375 if (output_tf->type == TF_TYPE_PREDEFINED && 1376 output_tf->tf == TRANSFER_FUNCTION_SRGB && 1377 (!mapUserRamp && ramp->type == GAMMA_RGB_256)) 1378 return true; 1379 1380 output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; 1381 1382 rgb_user = kcalloc(ramp->num_entries + _EXTRA_POINTS, 1383 sizeof(*rgb_user), 1384 GFP_KERNEL); 1385 if (!rgb_user) 1386 goto rgb_user_alloc_fail; 1387 rgb_regamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS, 1388 sizeof(*rgb_regamma), 1389 GFP_KERNEL); 1390 if (!rgb_regamma) 1391 goto rgb_regamma_alloc_fail; 1392 axix_x = kcalloc(ramp->num_entries + 3, sizeof(*axix_x), 1393 GFP_KERNEL); 1394 if (!axix_x) 1395 goto axix_x_alloc_fail; 1396 coeff = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff), 1397 GFP_KERNEL); 1398 if (!coeff) 1399 goto coeff_alloc_fail; 1400 1401 dividers.divider1 = dc_fixpt_from_fraction(3, 2); 1402 dividers.divider2 = dc_fixpt_from_int(2); 1403 dividers.divider3 = dc_fixpt_from_fraction(5, 2); 1404 1405 tf = output_tf->tf; 1406 1407 build_evenly_distributed_points( 1408 axix_x, 1409 ramp->num_entries, 1410 dividers); 1411 1412 if (ramp->type == GAMMA_RGB_256 && mapUserRamp) 1413 scale_gamma(rgb_user, ramp, dividers); 1414 else if (ramp->type == GAMMA_RGB_FLOAT_1024) 1415 scale_gamma_dx(rgb_user, ramp, dividers); 1416 1417 if (tf == TRANSFER_FUNCTION_PQ) { 1418 tf_pts->end_exponent = 7; 1419 tf_pts->x_point_at_y1_red = 125; 1420 tf_pts->x_point_at_y1_green = 125; 1421 tf_pts->x_point_at_y1_blue = 125; 1422 1423 build_pq(rgb_regamma, 1424 MAX_HW_POINTS, 1425 coordinates_x, 1426 output_tf->sdr_ref_white_level); 1427 } else { 1428 tf_pts->end_exponent = 0; 1429 tf_pts->x_point_at_y1_red = 1; 1430 tf_pts->x_point_at_y1_green = 1; 1431 tf_pts->x_point_at_y1_blue = 1; 1432 1433 build_regamma(rgb_regamma, 1434 MAX_HW_POINTS, 1435 coordinates_x, tf == TRANSFER_FUNCTION_SRGB ? true:false); 1436 } 1437 1438 map_regamma_hw_to_x_user(ramp, coeff, rgb_user, 1439 coordinates_x, axix_x, rgb_regamma, 1440 MAX_HW_POINTS, tf_pts, 1441 (mapUserRamp || ramp->type != GAMMA_RGB_256) && 1442 ramp->type != GAMMA_CS_TFM_1D); 1443 1444 if (ramp->type == GAMMA_CS_TFM_1D) 1445 apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts); 1446 1447 ret = true; 1448 1449 kvfree(coeff); 1450 coeff_alloc_fail: 1451 kvfree(axix_x); 1452 axix_x_alloc_fail: 1453 kvfree(rgb_regamma); 1454 rgb_regamma_alloc_fail: 1455 kvfree(rgb_user); 1456 rgb_user_alloc_fail: 1457 return ret; 1458 } 1459 1460 bool calculate_user_regamma_coeff(struct dc_transfer_func *output_tf, 1461 const struct regamma_lut *regamma) 1462 { 1463 struct gamma_coefficients coeff; 1464 const struct hw_x_point *coord_x = coordinates_x; 1465 uint32_t i = 0; 1466 1467 do { 1468 coeff.a0[i] = dc_fixpt_from_fraction( 1469 regamma->coeff.A0[i], 10000000); 1470 coeff.a1[i] = dc_fixpt_from_fraction( 1471 regamma->coeff.A1[i], 1000); 1472 coeff.a2[i] = dc_fixpt_from_fraction( 1473 regamma->coeff.A2[i], 1000); 1474 coeff.a3[i] = dc_fixpt_from_fraction( 1475 regamma->coeff.A3[i], 1000); 1476 coeff.user_gamma[i] = dc_fixpt_from_fraction( 1477 regamma->coeff.gamma[i], 1000); 1478 1479 ++i; 1480 } while (i != 3); 1481 1482 i = 0; 1483 /* fixed_pt library has problems handling too small values */ 1484 while (i != 32) { 1485 output_tf->tf_pts.red[i] = dc_fixpt_zero; 1486 output_tf->tf_pts.green[i] = dc_fixpt_zero; 1487 output_tf->tf_pts.blue[i] = dc_fixpt_zero; 1488 ++coord_x; 1489 ++i; 1490 } 1491 while (i != MAX_HW_POINTS + 1) { 1492 output_tf->tf_pts.red[i] = translate_from_linear_space_ex( 1493 coord_x->x, &coeff, 0); 1494 output_tf->tf_pts.green[i] = translate_from_linear_space_ex( 1495 coord_x->x, &coeff, 1); 1496 output_tf->tf_pts.blue[i] = translate_from_linear_space_ex( 1497 coord_x->x, &coeff, 2); 1498 ++coord_x; 1499 ++i; 1500 } 1501 1502 // this function just clamps output to 0-1 1503 build_new_custom_resulted_curve(MAX_HW_POINTS, &output_tf->tf_pts); 1504 output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; 1505 1506 return true; 1507 } 1508 1509 bool calculate_user_regamma_ramp(struct dc_transfer_func *output_tf, 1510 const struct regamma_lut *regamma) 1511 { 1512 struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts; 1513 struct dividers dividers; 1514 1515 struct pwl_float_data *rgb_user = NULL; 1516 struct pwl_float_data_ex *rgb_regamma = NULL; 1517 bool ret = false; 1518 1519 if (regamma == NULL) 1520 return false; 1521 1522 output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; 1523 1524 rgb_user = kcalloc(GAMMA_RGB_256_ENTRIES + _EXTRA_POINTS, 1525 sizeof(*rgb_user), 1526 GFP_KERNEL); 1527 if (!rgb_user) 1528 goto rgb_user_alloc_fail; 1529 1530 rgb_regamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS, 1531 sizeof(*rgb_regamma), 1532 GFP_KERNEL); 1533 if (!rgb_regamma) 1534 goto rgb_regamma_alloc_fail; 1535 1536 dividers.divider1 = dc_fixpt_from_fraction(3, 2); 1537 dividers.divider2 = dc_fixpt_from_int(2); 1538 dividers.divider3 = dc_fixpt_from_fraction(5, 2); 1539 1540 scale_user_regamma_ramp(rgb_user, ®amma->ramp, dividers); 1541 1542 if (regamma->flags.bits.applyDegamma == 1) { 1543 apply_degamma_for_user_regamma(rgb_regamma, MAX_HW_POINTS); 1544 copy_rgb_regamma_to_coordinates_x(coordinates_x, 1545 MAX_HW_POINTS, rgb_regamma); 1546 } 1547 1548 interpolate_user_regamma(MAX_HW_POINTS, rgb_user, 1549 regamma->flags.bits.applyDegamma, tf_pts); 1550 1551 // no custom HDR curves! 1552 tf_pts->end_exponent = 0; 1553 tf_pts->x_point_at_y1_red = 1; 1554 tf_pts->x_point_at_y1_green = 1; 1555 tf_pts->x_point_at_y1_blue = 1; 1556 1557 // this function just clamps output to 0-1 1558 build_new_custom_resulted_curve(MAX_HW_POINTS, tf_pts); 1559 1560 ret = true; 1561 1562 kfree(rgb_regamma); 1563 rgb_regamma_alloc_fail: 1564 kfree(rgb_user); 1565 rgb_user_alloc_fail: 1566 return ret; 1567 } 1568 1569 bool mod_color_calculate_degamma_params(struct dc_transfer_func *input_tf, 1570 const struct dc_gamma *ramp, bool mapUserRamp) 1571 { 1572 struct dc_transfer_func_distributed_points *tf_pts = &input_tf->tf_pts; 1573 struct dividers dividers; 1574 1575 struct pwl_float_data *rgb_user = NULL; 1576 struct pwl_float_data_ex *curve = NULL; 1577 struct gamma_pixel *axix_x = NULL; 1578 struct pixel_gamma_point *coeff = NULL; 1579 enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB; 1580 bool ret = false; 1581 1582 if (input_tf->type == TF_TYPE_BYPASS) 1583 return false; 1584 1585 /* we can use hardcoded curve for plain SRGB TF */ 1586 if (input_tf->type == TF_TYPE_PREDEFINED && 1587 input_tf->tf == TRANSFER_FUNCTION_SRGB && 1588 (!mapUserRamp && ramp->type == GAMMA_RGB_256)) 1589 return true; 1590 1591 input_tf->type = TF_TYPE_DISTRIBUTED_POINTS; 1592 1593 rgb_user = kcalloc(ramp->num_entries + _EXTRA_POINTS, 1594 sizeof(*rgb_user), 1595 GFP_KERNEL); 1596 if (!rgb_user) 1597 goto rgb_user_alloc_fail; 1598 curve = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*curve), 1599 GFP_KERNEL); 1600 if (!curve) 1601 goto curve_alloc_fail; 1602 axix_x = kcalloc(ramp->num_entries + _EXTRA_POINTS, sizeof(*axix_x), 1603 GFP_KERNEL); 1604 if (!axix_x) 1605 goto axix_x_alloc_fail; 1606 coeff = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff), 1607 GFP_KERNEL); 1608 if (!coeff) 1609 goto coeff_alloc_fail; 1610 1611 dividers.divider1 = dc_fixpt_from_fraction(3, 2); 1612 dividers.divider2 = dc_fixpt_from_int(2); 1613 dividers.divider3 = dc_fixpt_from_fraction(5, 2); 1614 1615 tf = input_tf->tf; 1616 1617 build_evenly_distributed_points( 1618 axix_x, 1619 ramp->num_entries, 1620 dividers); 1621 1622 if (ramp->type == GAMMA_RGB_256 && mapUserRamp) 1623 scale_gamma(rgb_user, ramp, dividers); 1624 else if (ramp->type == GAMMA_RGB_FLOAT_1024) 1625 scale_gamma_dx(rgb_user, ramp, dividers); 1626 1627 if (tf == TRANSFER_FUNCTION_PQ) 1628 build_de_pq(curve, 1629 MAX_HW_POINTS, 1630 coordinates_x); 1631 else 1632 build_degamma(curve, 1633 MAX_HW_POINTS, 1634 coordinates_x, 1635 tf == TRANSFER_FUNCTION_SRGB ? true:false); 1636 1637 tf_pts->end_exponent = 0; 1638 tf_pts->x_point_at_y1_red = 1; 1639 tf_pts->x_point_at_y1_green = 1; 1640 tf_pts->x_point_at_y1_blue = 1; 1641 1642 map_regamma_hw_to_x_user(ramp, coeff, rgb_user, 1643 coordinates_x, axix_x, curve, 1644 MAX_HW_POINTS, tf_pts, 1645 mapUserRamp && ramp->type != GAMMA_CUSTOM); 1646 if (ramp->type == GAMMA_CUSTOM) 1647 apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts); 1648 1649 ret = true; 1650 1651 kvfree(coeff); 1652 coeff_alloc_fail: 1653 kvfree(axix_x); 1654 axix_x_alloc_fail: 1655 kvfree(curve); 1656 curve_alloc_fail: 1657 kvfree(rgb_user); 1658 rgb_user_alloc_fail: 1659 1660 return ret; 1661 1662 } 1663 1664 1665 bool mod_color_calculate_curve(enum dc_transfer_func_predefined trans, 1666 struct dc_transfer_func_distributed_points *points) 1667 { 1668 uint32_t i; 1669 bool ret = false; 1670 struct pwl_float_data_ex *rgb_regamma = NULL; 1671 1672 if (trans == TRANSFER_FUNCTION_UNITY || 1673 trans == TRANSFER_FUNCTION_LINEAR) { 1674 points->end_exponent = 0; 1675 points->x_point_at_y1_red = 1; 1676 points->x_point_at_y1_green = 1; 1677 points->x_point_at_y1_blue = 1; 1678 1679 for (i = 0; i <= MAX_HW_POINTS ; i++) { 1680 points->red[i] = coordinates_x[i].x; 1681 points->green[i] = coordinates_x[i].x; 1682 points->blue[i] = coordinates_x[i].x; 1683 } 1684 ret = true; 1685 } else if (trans == TRANSFER_FUNCTION_PQ) { 1686 rgb_regamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS, 1687 sizeof(*rgb_regamma), 1688 GFP_KERNEL); 1689 if (!rgb_regamma) 1690 goto rgb_regamma_alloc_fail; 1691 points->end_exponent = 7; 1692 points->x_point_at_y1_red = 125; 1693 points->x_point_at_y1_green = 125; 1694 points->x_point_at_y1_blue = 125; 1695 1696 1697 build_pq(rgb_regamma, 1698 MAX_HW_POINTS, 1699 coordinates_x, 1700 80); 1701 for (i = 0; i <= MAX_HW_POINTS ; i++) { 1702 points->red[i] = rgb_regamma[i].r; 1703 points->green[i] = rgb_regamma[i].g; 1704 points->blue[i] = rgb_regamma[i].b; 1705 } 1706 ret = true; 1707 1708 kvfree(rgb_regamma); 1709 } else if (trans == TRANSFER_FUNCTION_SRGB || 1710 trans == TRANSFER_FUNCTION_BT709) { 1711 rgb_regamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS, 1712 sizeof(*rgb_regamma), 1713 GFP_KERNEL); 1714 if (!rgb_regamma) 1715 goto rgb_regamma_alloc_fail; 1716 points->end_exponent = 0; 1717 points->x_point_at_y1_red = 1; 1718 points->x_point_at_y1_green = 1; 1719 points->x_point_at_y1_blue = 1; 1720 1721 build_regamma(rgb_regamma, 1722 MAX_HW_POINTS, 1723 coordinates_x, trans == TRANSFER_FUNCTION_SRGB ? true:false); 1724 for (i = 0; i <= MAX_HW_POINTS ; i++) { 1725 points->red[i] = rgb_regamma[i].r; 1726 points->green[i] = rgb_regamma[i].g; 1727 points->blue[i] = rgb_regamma[i].b; 1728 } 1729 ret = true; 1730 1731 kvfree(rgb_regamma); 1732 } else if (trans == TRANSFER_FUNCTION_HLG || 1733 trans == TRANSFER_FUNCTION_HLG12) { 1734 rgb_regamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS, 1735 sizeof(*rgb_regamma), 1736 GFP_KERNEL); 1737 if (!rgb_regamma) 1738 goto rgb_regamma_alloc_fail; 1739 1740 build_hlg_regamma(rgb_regamma, 1741 MAX_HW_POINTS, 1742 coordinates_x, 1743 trans == TRANSFER_FUNCTION_HLG12 ? true:false); 1744 for (i = 0; i <= MAX_HW_POINTS ; i++) { 1745 points->red[i] = rgb_regamma[i].r; 1746 points->green[i] = rgb_regamma[i].g; 1747 points->blue[i] = rgb_regamma[i].b; 1748 } 1749 ret = true; 1750 kvfree(rgb_regamma); 1751 } 1752 rgb_regamma_alloc_fail: 1753 return ret; 1754 } 1755 1756 1757 bool mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans, 1758 struct dc_transfer_func_distributed_points *points) 1759 { 1760 uint32_t i; 1761 bool ret = false; 1762 struct pwl_float_data_ex *rgb_degamma = NULL; 1763 1764 if (trans == TRANSFER_FUNCTION_UNITY || 1765 trans == TRANSFER_FUNCTION_LINEAR) { 1766 1767 for (i = 0; i <= MAX_HW_POINTS ; i++) { 1768 points->red[i] = coordinates_x[i].x; 1769 points->green[i] = coordinates_x[i].x; 1770 points->blue[i] = coordinates_x[i].x; 1771 } 1772 ret = true; 1773 } else if (trans == TRANSFER_FUNCTION_PQ) { 1774 rgb_degamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS, 1775 sizeof(*rgb_degamma), 1776 GFP_KERNEL); 1777 if (!rgb_degamma) 1778 goto rgb_degamma_alloc_fail; 1779 1780 1781 build_de_pq(rgb_degamma, 1782 MAX_HW_POINTS, 1783 coordinates_x); 1784 for (i = 0; i <= MAX_HW_POINTS ; i++) { 1785 points->red[i] = rgb_degamma[i].r; 1786 points->green[i] = rgb_degamma[i].g; 1787 points->blue[i] = rgb_degamma[i].b; 1788 } 1789 ret = true; 1790 1791 kvfree(rgb_degamma); 1792 } else if (trans == TRANSFER_FUNCTION_SRGB || 1793 trans == TRANSFER_FUNCTION_BT709) { 1794 rgb_degamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS, 1795 sizeof(*rgb_degamma), 1796 GFP_KERNEL); 1797 if (!rgb_degamma) 1798 goto rgb_degamma_alloc_fail; 1799 1800 build_degamma(rgb_degamma, 1801 MAX_HW_POINTS, 1802 coordinates_x, trans == TRANSFER_FUNCTION_SRGB ? true:false); 1803 for (i = 0; i <= MAX_HW_POINTS ; i++) { 1804 points->red[i] = rgb_degamma[i].r; 1805 points->green[i] = rgb_degamma[i].g; 1806 points->blue[i] = rgb_degamma[i].b; 1807 } 1808 ret = true; 1809 1810 kvfree(rgb_degamma); 1811 } else if (trans == TRANSFER_FUNCTION_HLG || 1812 trans == TRANSFER_FUNCTION_HLG12) { 1813 rgb_degamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS, 1814 sizeof(*rgb_degamma), 1815 GFP_KERNEL); 1816 if (!rgb_degamma) 1817 goto rgb_degamma_alloc_fail; 1818 1819 build_hlg_degamma(rgb_degamma, 1820 MAX_HW_POINTS, 1821 coordinates_x, 1822 trans == TRANSFER_FUNCTION_HLG12 ? true:false); 1823 for (i = 0; i <= MAX_HW_POINTS ; i++) { 1824 points->red[i] = rgb_degamma[i].r; 1825 points->green[i] = rgb_degamma[i].g; 1826 points->blue[i] = rgb_degamma[i].b; 1827 } 1828 ret = true; 1829 kvfree(rgb_degamma); 1830 } 1831 points->end_exponent = 0; 1832 points->x_point_at_y1_red = 1; 1833 points->x_point_at_y1_green = 1; 1834 points->x_point_at_y1_blue = 1; 1835 1836 rgb_degamma_alloc_fail: 1837 return ret; 1838 } 1839 1840 1841