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