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