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