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