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