treewide: kzalloc() -> kcalloc()
[linux-2.6-block.git] / drivers / gpu / drm / amd / display / modules / color / color_gamma.c
CommitLineData
ec7e6bb8
LSL
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
792474b7 30
ec7e6bb8
LSL
31#define NUM_PTS_IN_REGION 16
32#define NUM_REGIONS 32
792474b7 33#define MAX_HW_POINTS (NUM_PTS_IN_REGION*NUM_REGIONS)
ec7e6bb8
LSL
34
35static struct hw_x_point coordinates_x[MAX_HW_POINTS + 2];
792474b7 36
ec7e6bb8 37static struct fixed31_32 pq_table[MAX_HW_POINTS + 2];
44c6f2e5 38static struct fixed31_32 de_pq_table[MAX_HW_POINTS + 2];
792474b7 39
ec7e6bb8 40static bool pq_initialized; /* = false; */
792474b7 41static bool de_pq_initialized; /* = false; */
ec7e6bb8
LSL
42
43/* one-time setup of X points */
44void setup_x_points_distribution(void)
45{
eb0e5154 46 struct fixed31_32 region_size = dc_fixpt_from_int(128);
ec7e6bb8
LSL
47 int32_t segment;
48 uint32_t seg_offset;
49 uint32_t index;
50 struct fixed31_32 increment;
51
792474b7
VP
52 coordinates_x[MAX_HW_POINTS].x = region_size;
53 coordinates_x[MAX_HW_POINTS + 1].x = region_size;
ec7e6bb8
LSL
54
55 for (segment = 6; segment > (6 - NUM_REGIONS); segment--) {
eb0e5154
DL
56 region_size = dc_fixpt_div_int(region_size, 2);
57 increment = dc_fixpt_div_int(region_size,
ec7e6bb8
LSL
58 NUM_PTS_IN_REGION);
59 seg_offset = (segment + (NUM_REGIONS - 7)) * NUM_PTS_IN_REGION;
60 coordinates_x[seg_offset].x = region_size;
61
62 for (index = seg_offset + 1;
63 index < seg_offset + NUM_PTS_IN_REGION;
64 index++) {
eb0e5154 65 coordinates_x[index].x = dc_fixpt_add
ec7e6bb8
LSL
66 (coordinates_x[index-1].x, increment);
67 }
68 }
69}
70
71static void compute_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y)
72{
73 /* consts for PQ gamma formula. */
74 const struct fixed31_32 m1 =
eb0e5154 75 dc_fixpt_from_fraction(159301758, 1000000000);
ec7e6bb8 76 const struct fixed31_32 m2 =
eb0e5154 77 dc_fixpt_from_fraction(7884375, 100000);
ec7e6bb8 78 const struct fixed31_32 c1 =
eb0e5154 79 dc_fixpt_from_fraction(8359375, 10000000);
ec7e6bb8 80 const struct fixed31_32 c2 =
eb0e5154 81 dc_fixpt_from_fraction(188515625, 10000000);
ec7e6bb8 82 const struct fixed31_32 c3 =
eb0e5154 83 dc_fixpt_from_fraction(186875, 10000);
ec7e6bb8
LSL
84
85 struct fixed31_32 l_pow_m1;
86 struct fixed31_32 base;
87
eb0e5154
DL
88 if (dc_fixpt_lt(in_x, dc_fixpt_zero))
89 in_x = dc_fixpt_zero;
ec7e6bb8 90
eb0e5154
DL
91 l_pow_m1 = dc_fixpt_pow(in_x, m1);
92 base = dc_fixpt_div(
93 dc_fixpt_add(c1,
94 (dc_fixpt_mul(c2, l_pow_m1))),
95 dc_fixpt_add(dc_fixpt_one,
96 (dc_fixpt_mul(c3, l_pow_m1))));
97 *out_y = dc_fixpt_pow(base, m2);
ec7e6bb8
LSL
98}
99
792474b7
VP
100static void compute_de_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y)
101{
102 /* consts for dePQ gamma formula. */
103 const struct fixed31_32 m1 =
eb0e5154 104 dc_fixpt_from_fraction(159301758, 1000000000);
792474b7 105 const struct fixed31_32 m2 =
eb0e5154 106 dc_fixpt_from_fraction(7884375, 100000);
792474b7 107 const struct fixed31_32 c1 =
eb0e5154 108 dc_fixpt_from_fraction(8359375, 10000000);
792474b7 109 const struct fixed31_32 c2 =
eb0e5154 110 dc_fixpt_from_fraction(188515625, 10000000);
792474b7 111 const struct fixed31_32 c3 =
eb0e5154 112 dc_fixpt_from_fraction(186875, 10000);
792474b7
VP
113
114 struct fixed31_32 l_pow_m1;
115 struct fixed31_32 base, div;
116
117
eb0e5154
DL
118 if (dc_fixpt_lt(in_x, dc_fixpt_zero))
119 in_x = dc_fixpt_zero;
792474b7 120
eb0e5154
DL
121 l_pow_m1 = dc_fixpt_pow(in_x,
122 dc_fixpt_div(dc_fixpt_one, m2));
123 base = dc_fixpt_sub(l_pow_m1, c1);
792474b7 124
eb0e5154
DL
125 if (dc_fixpt_lt(base, dc_fixpt_zero))
126 base = dc_fixpt_zero;
792474b7 127
eb0e5154 128 div = dc_fixpt_sub(c2, dc_fixpt_mul(c3, l_pow_m1));
792474b7 129
eb0e5154
DL
130 *out_y = dc_fixpt_pow(dc_fixpt_div(base, div),
131 dc_fixpt_div(dc_fixpt_one, m1));
792474b7
VP
132
133}
ec7e6bb8
LSL
134/* one-time pre-compute PQ values - only for sdr_white_level 80 */
135void precompute_pq(void)
136{
137 int i;
138 struct fixed31_32 x;
139 const struct hw_x_point *coord_x = coordinates_x + 32;
140 struct fixed31_32 scaling_factor =
eb0e5154 141 dc_fixpt_from_fraction(80, 10000);
ec7e6bb8
LSL
142
143 /* pow function has problems with arguments too small */
144 for (i = 0; i < 32; i++)
eb0e5154 145 pq_table[i] = dc_fixpt_zero;
ec7e6bb8
LSL
146
147 for (i = 32; i <= MAX_HW_POINTS; i++) {
eb0e5154 148 x = dc_fixpt_mul(coord_x->x, scaling_factor);
ec7e6bb8
LSL
149 compute_pq(x, &pq_table[i]);
150 ++coord_x;
151 }
152}
153
792474b7
VP
154/* one-time pre-compute dePQ values - only for max pixel value 125 FP16 */
155void precompute_de_pq(void)
156{
157 int i;
158 struct fixed31_32 y;
44c6f2e5
VP
159 uint32_t begin_index, end_index;
160
eb0e5154 161 struct fixed31_32 scaling_factor = dc_fixpt_from_int(125);
792474b7 162
44c6f2e5
VP
163 /* X points is 2^-25 to 2^7
164 * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions
165 */
166 begin_index = 13 * NUM_PTS_IN_REGION;
167 end_index = begin_index + 12 * NUM_PTS_IN_REGION;
792474b7 168
44c6f2e5 169 for (i = 0; i <= begin_index; i++)
eb0e5154 170 de_pq_table[i] = dc_fixpt_zero;
44c6f2e5
VP
171
172 for (; i <= end_index; i++) {
173 compute_de_pq(coordinates_x[i].x, &y);
eb0e5154 174 de_pq_table[i] = dc_fixpt_mul(y, scaling_factor);
792474b7 175 }
44c6f2e5
VP
176
177 for (; i <= MAX_HW_POINTS; i++)
178 de_pq_table[i] = de_pq_table[i-1];
792474b7 179}
ec7e6bb8
LSL
180struct dividers {
181 struct fixed31_32 divider1;
182 struct fixed31_32 divider2;
183 struct fixed31_32 divider3;
184};
185
792474b7 186static void build_coefficients(struct gamma_coefficients *coefficients, bool is_2_4)
ec7e6bb8 187{
55a01d40
KK
188 static const int32_t numerator01[] = { 31308, 180000};
189 static const int32_t numerator02[] = { 12920, 4500};
190 static const int32_t numerator03[] = { 55, 99};
191 static const int32_t numerator04[] = { 55, 99};
192 static const int32_t numerator05[] = { 2400, 2200};
ec7e6bb8 193
55a01d40
KK
194 uint32_t i = 0;
195 uint32_t index = is_2_4 == true ? 0:1;
ec7e6bb8
LSL
196
197 do {
eb0e5154 198 coefficients->a0[i] = dc_fixpt_from_fraction(
792474b7 199 numerator01[index], 10000000);
eb0e5154 200 coefficients->a1[i] = dc_fixpt_from_fraction(
792474b7 201 numerator02[index], 1000);
eb0e5154 202 coefficients->a2[i] = dc_fixpt_from_fraction(
792474b7 203 numerator03[index], 1000);
eb0e5154 204 coefficients->a3[i] = dc_fixpt_from_fraction(
792474b7 205 numerator04[index], 1000);
eb0e5154 206 coefficients->user_gamma[i] = dc_fixpt_from_fraction(
792474b7 207 numerator05[index], 1000);
ec7e6bb8
LSL
208
209 ++i;
210 } while (i != ARRAY_SIZE(coefficients->a0));
211}
212
213static struct fixed31_32 translate_from_linear_space(
214 struct fixed31_32 arg,
215 struct fixed31_32 a0,
216 struct fixed31_32 a1,
217 struct fixed31_32 a2,
218 struct fixed31_32 a3,
219 struct fixed31_32 gamma)
220{
eb0e5154 221 const struct fixed31_32 one = dc_fixpt_from_int(1);
ec7e6bb8 222
eb0e5154 223 if (dc_fixpt_lt(one, arg))
ec7e6bb8
LSL
224 return one;
225
eb0e5154
DL
226 if (dc_fixpt_le(arg, dc_fixpt_neg(a0)))
227 return dc_fixpt_sub(
ec7e6bb8 228 a2,
eb0e5154
DL
229 dc_fixpt_mul(
230 dc_fixpt_add(
ec7e6bb8
LSL
231 one,
232 a3),
eb0e5154
DL
233 dc_fixpt_pow(
234 dc_fixpt_neg(arg),
235 dc_fixpt_recip(gamma))));
236 else if (dc_fixpt_le(a0, arg))
237 return dc_fixpt_sub(
238 dc_fixpt_mul(
239 dc_fixpt_add(
ec7e6bb8
LSL
240 one,
241 a3),
eb0e5154 242 dc_fixpt_pow(
ec7e6bb8 243 arg,
eb0e5154 244 dc_fixpt_recip(gamma))),
ec7e6bb8
LSL
245 a2);
246 else
eb0e5154 247 return dc_fixpt_mul(
ec7e6bb8
LSL
248 arg,
249 a1);
250}
251
792474b7
VP
252static struct fixed31_32 translate_to_linear_space(
253 struct fixed31_32 arg,
254 struct fixed31_32 a0,
255 struct fixed31_32 a1,
256 struct fixed31_32 a2,
257 struct fixed31_32 a3,
258 struct fixed31_32 gamma)
259{
260 struct fixed31_32 linear;
261
eb0e5154
DL
262 a0 = dc_fixpt_mul(a0, a1);
263 if (dc_fixpt_le(arg, dc_fixpt_neg(a0)))
792474b7 264
eb0e5154
DL
265 linear = dc_fixpt_neg(
266 dc_fixpt_pow(
267 dc_fixpt_div(
268 dc_fixpt_sub(a2, arg),
269 dc_fixpt_add(
270 dc_fixpt_one, a3)), gamma));
792474b7 271
eb0e5154
DL
272 else if (dc_fixpt_le(dc_fixpt_neg(a0), arg) &&
273 dc_fixpt_le(arg, a0))
274 linear = dc_fixpt_div(arg, a1);
792474b7 275 else
eb0e5154
DL
276 linear = dc_fixpt_pow(
277 dc_fixpt_div(
278 dc_fixpt_add(a2, arg),
279 dc_fixpt_add(
280 dc_fixpt_one, a3)), gamma);
792474b7
VP
281
282 return linear;
283}
284
ec7e6bb8
LSL
285static inline struct fixed31_32 translate_from_linear_space_ex(
286 struct fixed31_32 arg,
287 struct gamma_coefficients *coeff,
288 uint32_t color_index)
289{
290 return translate_from_linear_space(
291 arg,
292 coeff->a0[color_index],
293 coeff->a1[color_index],
294 coeff->a2[color_index],
295 coeff->a3[color_index],
296 coeff->user_gamma[color_index]);
297}
298
792474b7
VP
299
300static inline struct fixed31_32 translate_to_linear_space_ex(
301 struct fixed31_32 arg,
302 struct gamma_coefficients *coeff,
303 uint32_t color_index)
304{
305 return translate_to_linear_space(
306 arg,
307 coeff->a0[color_index],
308 coeff->a1[color_index],
309 coeff->a2[color_index],
310 coeff->a3[color_index],
311 coeff->user_gamma[color_index]);
312}
313
314
ec7e6bb8
LSL
315static bool find_software_points(
316 const struct dc_gamma *ramp,
317 const struct gamma_pixel *axis_x,
318 struct fixed31_32 hw_point,
319 enum channel_name channel,
320 uint32_t *index_to_start,
321 uint32_t *index_left,
322 uint32_t *index_right,
323 enum hw_point_position *pos)
324{
325 const uint32_t max_number = ramp->num_entries + 3;
326
327 struct fixed31_32 left, right;
328
329 uint32_t i = *index_to_start;
330
331 while (i < max_number) {
332 if (channel == CHANNEL_NAME_RED) {
333 left = axis_x[i].r;
334
335 if (i < max_number - 1)
336 right = axis_x[i + 1].r;
337 else
338 right = axis_x[max_number - 1].r;
339 } else if (channel == CHANNEL_NAME_GREEN) {
340 left = axis_x[i].g;
341
342 if (i < max_number - 1)
343 right = axis_x[i + 1].g;
344 else
345 right = axis_x[max_number - 1].g;
346 } else {
347 left = axis_x[i].b;
348
349 if (i < max_number - 1)
350 right = axis_x[i + 1].b;
351 else
352 right = axis_x[max_number - 1].b;
353 }
354
eb0e5154
DL
355 if (dc_fixpt_le(left, hw_point) &&
356 dc_fixpt_le(hw_point, right)) {
ec7e6bb8
LSL
357 *index_to_start = i;
358 *index_left = i;
359
360 if (i < max_number - 1)
361 *index_right = i + 1;
362 else
363 *index_right = max_number - 1;
364
365 *pos = HW_POINT_POSITION_MIDDLE;
366
367 return true;
368 } else if ((i == *index_to_start) &&
eb0e5154 369 dc_fixpt_le(hw_point, left)) {
ec7e6bb8
LSL
370 *index_to_start = i;
371 *index_left = i;
372 *index_right = i;
373
374 *pos = HW_POINT_POSITION_LEFT;
375
376 return true;
377 } else if ((i == max_number - 1) &&
eb0e5154 378 dc_fixpt_le(right, hw_point)) {
ec7e6bb8
LSL
379 *index_to_start = i;
380 *index_left = i;
381 *index_right = i;
382
383 *pos = HW_POINT_POSITION_RIGHT;
384
385 return true;
386 }
387
388 ++i;
389 }
390
391 return false;
392}
393
394static bool build_custom_gamma_mapping_coefficients_worker(
395 const struct dc_gamma *ramp,
396 struct pixel_gamma_point *coeff,
397 const struct hw_x_point *coordinates_x,
398 const struct gamma_pixel *axis_x,
399 enum channel_name channel,
400 uint32_t number_of_points)
401{
402 uint32_t i = 0;
403
404 while (i <= number_of_points) {
405 struct fixed31_32 coord_x;
406
407 uint32_t index_to_start = 0;
408 uint32_t index_left = 0;
409 uint32_t index_right = 0;
410
411 enum hw_point_position hw_pos;
412
413 struct gamma_point *point;
414
415 struct fixed31_32 left_pos;
416 struct fixed31_32 right_pos;
417
ec7e6bb8
LSL
418 if (channel == CHANNEL_NAME_RED)
419 coord_x = coordinates_x[i].regamma_y_red;
420 else if (channel == CHANNEL_NAME_GREEN)
421 coord_x = coordinates_x[i].regamma_y_green;
422 else
423 coord_x = coordinates_x[i].regamma_y_blue;
424
425 if (!find_software_points(
426 ramp, axis_x, coord_x, channel,
427 &index_to_start, &index_left, &index_right, &hw_pos)) {
428 BREAK_TO_DEBUGGER();
429 return false;
430 }
431
432 if (index_left >= ramp->num_entries + 3) {
433 BREAK_TO_DEBUGGER();
434 return false;
435 }
436
437 if (index_right >= ramp->num_entries + 3) {
438 BREAK_TO_DEBUGGER();
439 return false;
440 }
441
442 if (channel == CHANNEL_NAME_RED) {
443 point = &coeff[i].r;
444
445 left_pos = axis_x[index_left].r;
446 right_pos = axis_x[index_right].r;
447 } else if (channel == CHANNEL_NAME_GREEN) {
448 point = &coeff[i].g;
449
450 left_pos = axis_x[index_left].g;
451 right_pos = axis_x[index_right].g;
452 } else {
453 point = &coeff[i].b;
454
455 left_pos = axis_x[index_left].b;
456 right_pos = axis_x[index_right].b;
457 }
458
459 if (hw_pos == HW_POINT_POSITION_MIDDLE)
eb0e5154
DL
460 point->coeff = dc_fixpt_div(
461 dc_fixpt_sub(
ec7e6bb8
LSL
462 coord_x,
463 left_pos),
eb0e5154 464 dc_fixpt_sub(
ec7e6bb8
LSL
465 right_pos,
466 left_pos));
467 else if (hw_pos == HW_POINT_POSITION_LEFT)
eb0e5154 468 point->coeff = dc_fixpt_zero;
ec7e6bb8 469 else if (hw_pos == HW_POINT_POSITION_RIGHT)
eb0e5154 470 point->coeff = dc_fixpt_from_int(2);
ec7e6bb8
LSL
471 else {
472 BREAK_TO_DEBUGGER();
473 return false;
474 }
475
476 point->left_index = index_left;
477 point->right_index = index_right;
478 point->pos = hw_pos;
479
480 ++i;
481 }
482
483 return true;
484}
485
486static struct fixed31_32 calculate_mapped_value(
487 struct pwl_float_data *rgb,
488 const struct pixel_gamma_point *coeff,
489 enum channel_name channel,
490 uint32_t max_index)
491{
492 const struct gamma_point *point;
493
494 struct fixed31_32 result;
495
496 if (channel == CHANNEL_NAME_RED)
497 point = &coeff->r;
498 else if (channel == CHANNEL_NAME_GREEN)
499 point = &coeff->g;
500 else
501 point = &coeff->b;
502
503 if ((point->left_index < 0) || (point->left_index > max_index)) {
504 BREAK_TO_DEBUGGER();
eb0e5154 505 return dc_fixpt_zero;
ec7e6bb8
LSL
506 }
507
508 if ((point->right_index < 0) || (point->right_index > max_index)) {
509 BREAK_TO_DEBUGGER();
eb0e5154 510 return dc_fixpt_zero;
ec7e6bb8
LSL
511 }
512
513 if (point->pos == HW_POINT_POSITION_MIDDLE)
514 if (channel == CHANNEL_NAME_RED)
eb0e5154
DL
515 result = dc_fixpt_add(
516 dc_fixpt_mul(
ec7e6bb8 517 point->coeff,
eb0e5154 518 dc_fixpt_sub(
ec7e6bb8
LSL
519 rgb[point->right_index].r,
520 rgb[point->left_index].r)),
521 rgb[point->left_index].r);
522 else if (channel == CHANNEL_NAME_GREEN)
eb0e5154
DL
523 result = dc_fixpt_add(
524 dc_fixpt_mul(
ec7e6bb8 525 point->coeff,
eb0e5154 526 dc_fixpt_sub(
ec7e6bb8
LSL
527 rgb[point->right_index].g,
528 rgb[point->left_index].g)),
529 rgb[point->left_index].g);
530 else
eb0e5154
DL
531 result = dc_fixpt_add(
532 dc_fixpt_mul(
ec7e6bb8 533 point->coeff,
eb0e5154 534 dc_fixpt_sub(
ec7e6bb8
LSL
535 rgb[point->right_index].b,
536 rgb[point->left_index].b)),
537 rgb[point->left_index].b);
538 else if (point->pos == HW_POINT_POSITION_LEFT) {
539 BREAK_TO_DEBUGGER();
eb0e5154 540 result = dc_fixpt_zero;
ec7e6bb8
LSL
541 } else {
542 BREAK_TO_DEBUGGER();
eb0e5154 543 result = dc_fixpt_one;
ec7e6bb8
LSL
544 }
545
546 return result;
547}
548
792474b7 549static void build_pq(struct pwl_float_data_ex *rgb_regamma,
ec7e6bb8
LSL
550 uint32_t hw_points_num,
551 const struct hw_x_point *coordinate_x,
552 uint32_t sdr_white_level)
553{
554 uint32_t i, start_index;
555
556 struct pwl_float_data_ex *rgb = rgb_regamma;
557 const struct hw_x_point *coord_x = coordinate_x;
558 struct fixed31_32 x;
559 struct fixed31_32 output;
560 struct fixed31_32 scaling_factor =
eb0e5154 561 dc_fixpt_from_fraction(sdr_white_level, 10000);
ec7e6bb8
LSL
562
563 if (!pq_initialized && sdr_white_level == 80) {
564 precompute_pq();
565 pq_initialized = true;
566 }
567
568 /* TODO: start index is from segment 2^-24, skipping first segment
569 * due to x values too small for power calculations
570 */
571 start_index = 32;
572 rgb += start_index;
573 coord_x += start_index;
574
ec7e6bb8
LSL
575 for (i = start_index; i <= hw_points_num; i++) {
576 /* Multiply 0.008 as regamma is 0-1 and FP16 input is 0-125.
577 * FP 1.0 = 80nits
578 */
579 if (sdr_white_level == 80) {
580 output = pq_table[i];
581 } else {
eb0e5154 582 x = dc_fixpt_mul(coord_x->x, scaling_factor);
ec7e6bb8
LSL
583 compute_pq(x, &output);
584 }
585
586 /* should really not happen? */
eb0e5154
DL
587 if (dc_fixpt_lt(output, dc_fixpt_zero))
588 output = dc_fixpt_zero;
589 else if (dc_fixpt_lt(dc_fixpt_one, output))
590 output = dc_fixpt_one;
ec7e6bb8
LSL
591
592 rgb->r = output;
593 rgb->g = output;
594 rgb->b = output;
595
596 ++coord_x;
597 ++rgb;
598 }
599}
600
792474b7 601static void build_de_pq(struct pwl_float_data_ex *de_pq,
ec7e6bb8
LSL
602 uint32_t hw_points_num,
603 const struct hw_x_point *coordinate_x)
604{
605 uint32_t i;
792474b7
VP
606 struct fixed31_32 output;
607
eb0e5154 608 struct fixed31_32 scaling_factor = dc_fixpt_from_int(125);
792474b7
VP
609
610 if (!de_pq_initialized) {
611 precompute_de_pq();
612 de_pq_initialized = true;
613 }
614
615
616 for (i = 0; i <= hw_points_num; i++) {
617 output = de_pq_table[i];
618 /* should really not happen? */
eb0e5154
DL
619 if (dc_fixpt_lt(output, dc_fixpt_zero))
620 output = dc_fixpt_zero;
621 else if (dc_fixpt_lt(scaling_factor, output))
792474b7 622 output = scaling_factor;
44c6f2e5
VP
623 de_pq[i].r = output;
624 de_pq[i].g = output;
625 de_pq[i].b = output;
792474b7
VP
626 }
627}
628
629static void build_regamma(struct pwl_float_data_ex *rgb_regamma,
630 uint32_t hw_points_num,
631 const struct hw_x_point *coordinate_x, bool is_2_4)
632{
633 uint32_t i;
ec7e6bb8
LSL
634
635 struct gamma_coefficients coeff;
636 struct pwl_float_data_ex *rgb = rgb_regamma;
637 const struct hw_x_point *coord_x = coordinate_x;
638
792474b7 639 build_coefficients(&coeff, is_2_4);
ec7e6bb8
LSL
640
641 i = 0;
642
643 while (i != hw_points_num + 1) {
792474b7 644 /*TODO use y vs r,g,b*/
ec7e6bb8
LSL
645 rgb->r = translate_from_linear_space_ex(
646 coord_x->x, &coeff, 0);
792474b7
VP
647 rgb->g = rgb->r;
648 rgb->b = rgb->r;
649 ++coord_x;
650 ++rgb;
651 ++i;
652 }
653}
654
655static void build_degamma(struct pwl_float_data_ex *curve,
656 uint32_t hw_points_num,
657 const struct hw_x_point *coordinate_x, bool is_2_4)
658{
659 uint32_t i;
792474b7 660 struct gamma_coefficients coeff;
44c6f2e5 661 uint32_t begin_index, end_index;
792474b7
VP
662
663 build_coefficients(&coeff, is_2_4);
792474b7
VP
664 i = 0;
665
44c6f2e5
VP
666 /* X points is 2^-25 to 2^7
667 * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions
668 */
669 begin_index = 13 * NUM_PTS_IN_REGION;
670 end_index = begin_index + 12 * NUM_PTS_IN_REGION;
671
672 while (i != begin_index) {
eb0e5154
DL
673 curve[i].r = dc_fixpt_zero;
674 curve[i].g = dc_fixpt_zero;
675 curve[i].b = dc_fixpt_zero;
44c6f2e5
VP
676 i++;
677 }
678
679 while (i != end_index) {
680 curve[i].r = translate_to_linear_space_ex(
681 coordinate_x[i].x, &coeff, 0);
682 curve[i].g = curve[i].r;
683 curve[i].b = curve[i].r;
684 i++;
685 }
792474b7 686 while (i != hw_points_num + 1) {
eb0e5154
DL
687 curve[i].r = dc_fixpt_one;
688 curve[i].g = dc_fixpt_one;
689 curve[i].b = dc_fixpt_one;
44c6f2e5 690 i++;
ec7e6bb8
LSL
691 }
692}
693
55a01d40 694static void scale_gamma(struct pwl_float_data *pwl_rgb,
ec7e6bb8
LSL
695 const struct dc_gamma *ramp,
696 struct dividers dividers)
697{
eb0e5154
DL
698 const struct fixed31_32 max_driver = dc_fixpt_from_int(0xFFFF);
699 const struct fixed31_32 max_os = dc_fixpt_from_int(0xFF00);
ec7e6bb8
LSL
700 struct fixed31_32 scaler = max_os;
701 uint32_t i;
702 struct pwl_float_data *rgb = pwl_rgb;
703 struct pwl_float_data *rgb_last = rgb + ramp->num_entries - 1;
704
705 i = 0;
706
707 do {
eb0e5154
DL
708 if (dc_fixpt_lt(max_os, ramp->entries.red[i]) ||
709 dc_fixpt_lt(max_os, ramp->entries.green[i]) ||
710 dc_fixpt_lt(max_os, ramp->entries.blue[i])) {
ec7e6bb8
LSL
711 scaler = max_driver;
712 break;
713 }
714 ++i;
715 } while (i != ramp->num_entries);
716
717 i = 0;
718
719 do {
eb0e5154 720 rgb->r = dc_fixpt_div(
ec7e6bb8 721 ramp->entries.red[i], scaler);
eb0e5154 722 rgb->g = dc_fixpt_div(
ec7e6bb8 723 ramp->entries.green[i], scaler);
eb0e5154 724 rgb->b = dc_fixpt_div(
ec7e6bb8
LSL
725 ramp->entries.blue[i], scaler);
726
727 ++rgb;
728 ++i;
729 } while (i != ramp->num_entries);
730
eb0e5154 731 rgb->r = dc_fixpt_mul(rgb_last->r,
ec7e6bb8 732 dividers.divider1);
eb0e5154 733 rgb->g = dc_fixpt_mul(rgb_last->g,
ec7e6bb8 734 dividers.divider1);
eb0e5154 735 rgb->b = dc_fixpt_mul(rgb_last->b,
ec7e6bb8
LSL
736 dividers.divider1);
737
738 ++rgb;
739
eb0e5154 740 rgb->r = dc_fixpt_mul(rgb_last->r,
ec7e6bb8 741 dividers.divider2);
eb0e5154 742 rgb->g = dc_fixpt_mul(rgb_last->g,
ec7e6bb8 743 dividers.divider2);
eb0e5154 744 rgb->b = dc_fixpt_mul(rgb_last->b,
ec7e6bb8
LSL
745 dividers.divider2);
746
747 ++rgb;
748
eb0e5154 749 rgb->r = dc_fixpt_mul(rgb_last->r,
ec7e6bb8 750 dividers.divider3);
eb0e5154 751 rgb->g = dc_fixpt_mul(rgb_last->g,
ec7e6bb8 752 dividers.divider3);
eb0e5154 753 rgb->b = dc_fixpt_mul(rgb_last->b,
ec7e6bb8 754 dividers.divider3);
ec7e6bb8
LSL
755}
756
55a01d40 757static void scale_gamma_dx(struct pwl_float_data *pwl_rgb,
ec7e6bb8
LSL
758 const struct dc_gamma *ramp,
759 struct dividers dividers)
760{
761 uint32_t i;
eb0e5154
DL
762 struct fixed31_32 min = dc_fixpt_zero;
763 struct fixed31_32 max = dc_fixpt_one;
ec7e6bb8 764
eb0e5154
DL
765 struct fixed31_32 delta = dc_fixpt_zero;
766 struct fixed31_32 offset = dc_fixpt_zero;
ec7e6bb8
LSL
767
768 for (i = 0 ; i < ramp->num_entries; i++) {
eb0e5154 769 if (dc_fixpt_lt(ramp->entries.red[i], min))
ec7e6bb8
LSL
770 min = ramp->entries.red[i];
771
eb0e5154 772 if (dc_fixpt_lt(ramp->entries.green[i], min))
ec7e6bb8
LSL
773 min = ramp->entries.green[i];
774
eb0e5154 775 if (dc_fixpt_lt(ramp->entries.blue[i], min))
ec7e6bb8
LSL
776 min = ramp->entries.blue[i];
777
eb0e5154 778 if (dc_fixpt_lt(max, ramp->entries.red[i]))
ec7e6bb8
LSL
779 max = ramp->entries.red[i];
780
eb0e5154 781 if (dc_fixpt_lt(max, ramp->entries.green[i]))
ec7e6bb8
LSL
782 max = ramp->entries.green[i];
783
eb0e5154 784 if (dc_fixpt_lt(max, ramp->entries.blue[i]))
ec7e6bb8
LSL
785 max = ramp->entries.blue[i];
786 }
787
eb0e5154
DL
788 if (dc_fixpt_lt(min, dc_fixpt_zero))
789 delta = dc_fixpt_neg(min);
ec7e6bb8 790
eb0e5154 791 offset = dc_fixpt_add(min, max);
ec7e6bb8
LSL
792
793 for (i = 0 ; i < ramp->num_entries; i++) {
eb0e5154
DL
794 pwl_rgb[i].r = dc_fixpt_div(
795 dc_fixpt_add(
ec7e6bb8 796 ramp->entries.red[i], delta), offset);
eb0e5154
DL
797 pwl_rgb[i].g = dc_fixpt_div(
798 dc_fixpt_add(
ec7e6bb8 799 ramp->entries.green[i], delta), offset);
eb0e5154
DL
800 pwl_rgb[i].b = dc_fixpt_div(
801 dc_fixpt_add(
ec7e6bb8
LSL
802 ramp->entries.blue[i], delta), offset);
803
804 }
805
eb0e5154 806 pwl_rgb[i].r = dc_fixpt_sub(dc_fixpt_mul_int(
ec7e6bb8 807 pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r);
eb0e5154 808 pwl_rgb[i].g = dc_fixpt_sub(dc_fixpt_mul_int(
ec7e6bb8 809 pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g);
eb0e5154 810 pwl_rgb[i].b = dc_fixpt_sub(dc_fixpt_mul_int(
ec7e6bb8
LSL
811 pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b);
812 ++i;
eb0e5154 813 pwl_rgb[i].r = dc_fixpt_sub(dc_fixpt_mul_int(
ec7e6bb8 814 pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r);
eb0e5154 815 pwl_rgb[i].g = dc_fixpt_sub(dc_fixpt_mul_int(
ec7e6bb8 816 pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g);
eb0e5154 817 pwl_rgb[i].b = dc_fixpt_sub(dc_fixpt_mul_int(
ec7e6bb8 818 pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b);
55a01d40 819}
ec7e6bb8 820
55a01d40
KK
821/* todo: all these scale_gamma functions are inherently the same but
822 * take different structures as params or different format for ramp
823 * values. We could probably implement it in a more generic fashion
824 */
825static void scale_user_regamma_ramp(struct pwl_float_data *pwl_rgb,
826 const struct regamma_ramp *ramp,
827 struct dividers dividers)
828{
829 unsigned short max_driver = 0xFFFF;
830 unsigned short max_os = 0xFF00;
831 unsigned short scaler = max_os;
832 uint32_t i;
833 struct pwl_float_data *rgb = pwl_rgb;
834 struct pwl_float_data *rgb_last = rgb + GAMMA_RGB_256_ENTRIES - 1;
835
836 i = 0;
837 do {
838 if (ramp->gamma[i] > max_os ||
839 ramp->gamma[i + 256] > max_os ||
840 ramp->gamma[i + 512] > max_os) {
841 scaler = max_driver;
842 break;
843 }
844 i++;
845 } while (i != GAMMA_RGB_256_ENTRIES);
846
847 i = 0;
848 do {
eb0e5154 849 rgb->r = dc_fixpt_from_fraction(
55a01d40 850 ramp->gamma[i], scaler);
eb0e5154 851 rgb->g = dc_fixpt_from_fraction(
55a01d40 852 ramp->gamma[i + 256], scaler);
eb0e5154 853 rgb->b = dc_fixpt_from_fraction(
55a01d40
KK
854 ramp->gamma[i + 512], scaler);
855
856 ++rgb;
857 ++i;
858 } while (i != GAMMA_RGB_256_ENTRIES);
859
eb0e5154 860 rgb->r = dc_fixpt_mul(rgb_last->r,
55a01d40 861 dividers.divider1);
eb0e5154 862 rgb->g = dc_fixpt_mul(rgb_last->g,
55a01d40 863 dividers.divider1);
eb0e5154 864 rgb->b = dc_fixpt_mul(rgb_last->b,
55a01d40
KK
865 dividers.divider1);
866
867 ++rgb;
868
eb0e5154 869 rgb->r = dc_fixpt_mul(rgb_last->r,
55a01d40 870 dividers.divider2);
eb0e5154 871 rgb->g = dc_fixpt_mul(rgb_last->g,
55a01d40 872 dividers.divider2);
eb0e5154 873 rgb->b = dc_fixpt_mul(rgb_last->b,
55a01d40
KK
874 dividers.divider2);
875
876 ++rgb;
877
eb0e5154 878 rgb->r = dc_fixpt_mul(rgb_last->r,
55a01d40 879 dividers.divider3);
eb0e5154 880 rgb->g = dc_fixpt_mul(rgb_last->g,
55a01d40 881 dividers.divider3);
eb0e5154 882 rgb->b = dc_fixpt_mul(rgb_last->b,
55a01d40 883 dividers.divider3);
ec7e6bb8
LSL
884}
885
886/*
887 * RS3+ color transform DDI - 1D LUT adjustment is composed with regamma here
888 * Input is evenly distributed in the output color space as specified in
889 * SetTimings
890 *
891 * Interpolation details:
892 * 1D LUT has 4096 values which give curve correction in 0-1 float range
893 * for evenly spaced points in 0-1 range. lut1D[index] gives correction
894 * for index/4095.
895 * First we find index for which:
896 * index/4095 < regamma_y < (index+1)/4095 =>
897 * index < 4095*regamma_y < index + 1
898 * norm_y = 4095*regamma_y, and index is just truncating to nearest integer
899 * lut1 = lut1D[index], lut2 = lut1D[index+1]
900 *
901 *adjustedY is then linearly interpolating regamma Y between lut1 and lut2
902 */
903static void apply_lut_1d(
904 const struct dc_gamma *ramp,
905 uint32_t num_hw_points,
906 struct dc_transfer_func_distributed_points *tf_pts)
907{
908 int i = 0;
909 int color = 0;
910 struct fixed31_32 *regamma_y;
911 struct fixed31_32 norm_y;
912 struct fixed31_32 lut1;
913 struct fixed31_32 lut2;
914 const int max_lut_index = 4095;
915 const struct fixed31_32 max_lut_index_f =
f3ba7a2f 916 dc_fixpt_from_int(max_lut_index);
ec7e6bb8
LSL
917 int32_t index = 0, index_next = 0;
918 struct fixed31_32 index_f;
919 struct fixed31_32 delta_lut;
920 struct fixed31_32 delta_index;
921
922 if (ramp->type != GAMMA_CS_TFM_1D)
923 return; // this is not expected
924
925 for (i = 0; i < num_hw_points; i++) {
926 for (color = 0; color < 3; color++) {
927 if (color == 0)
928 regamma_y = &tf_pts->red[i];
929 else if (color == 1)
930 regamma_y = &tf_pts->green[i];
931 else
932 regamma_y = &tf_pts->blue[i];
933
eb0e5154 934 norm_y = dc_fixpt_mul(max_lut_index_f,
ec7e6bb8 935 *regamma_y);
eb0e5154 936 index = dc_fixpt_floor(norm_y);
f3ba7a2f 937 index_f = dc_fixpt_from_int(index);
ec7e6bb8
LSL
938
939 if (index < 0 || index > max_lut_index)
940 continue;
941
942 index_next = (index == max_lut_index) ? index : index+1;
943
944 if (color == 0) {
945 lut1 = ramp->entries.red[index];
946 lut2 = ramp->entries.red[index_next];
947 } else if (color == 1) {
948 lut1 = ramp->entries.green[index];
949 lut2 = ramp->entries.green[index_next];
950 } else {
951 lut1 = ramp->entries.blue[index];
952 lut2 = ramp->entries.blue[index_next];
953 }
954
955 // we have everything now, so interpolate
eb0e5154
DL
956 delta_lut = dc_fixpt_sub(lut2, lut1);
957 delta_index = dc_fixpt_sub(norm_y, index_f);
ec7e6bb8 958
eb0e5154
DL
959 *regamma_y = dc_fixpt_add(lut1,
960 dc_fixpt_mul(delta_index, delta_lut));
ec7e6bb8
LSL
961 }
962 }
963}
964
965static void build_evenly_distributed_points(
966 struct gamma_pixel *points,
967 uint32_t numberof_points,
968 struct dividers dividers)
969{
970 struct gamma_pixel *p = points;
971 struct gamma_pixel *p_last = p + numberof_points - 1;
972
973 uint32_t i = 0;
974
975 do {
eb0e5154 976 struct fixed31_32 value = dc_fixpt_from_fraction(i,
ec7e6bb8
LSL
977 numberof_points - 1);
978
979 p->r = value;
980 p->g = value;
981 p->b = value;
982
983 ++p;
984 ++i;
985 } while (i != numberof_points);
986
eb0e5154
DL
987 p->r = dc_fixpt_div(p_last->r, dividers.divider1);
988 p->g = dc_fixpt_div(p_last->g, dividers.divider1);
989 p->b = dc_fixpt_div(p_last->b, dividers.divider1);
ec7e6bb8
LSL
990
991 ++p;
992
eb0e5154
DL
993 p->r = dc_fixpt_div(p_last->r, dividers.divider2);
994 p->g = dc_fixpt_div(p_last->g, dividers.divider2);
995 p->b = dc_fixpt_div(p_last->b, dividers.divider2);
ec7e6bb8
LSL
996
997 ++p;
998
eb0e5154
DL
999 p->r = dc_fixpt_div(p_last->r, dividers.divider3);
1000 p->g = dc_fixpt_div(p_last->g, dividers.divider3);
1001 p->b = dc_fixpt_div(p_last->b, dividers.divider3);
ec7e6bb8
LSL
1002}
1003
1004static inline void copy_rgb_regamma_to_coordinates_x(
1005 struct hw_x_point *coordinates_x,
1006 uint32_t hw_points_num,
1007 const struct pwl_float_data_ex *rgb_ex)
1008{
1009 struct hw_x_point *coords = coordinates_x;
1010 uint32_t i = 0;
1011 const struct pwl_float_data_ex *rgb_regamma = rgb_ex;
1012
55a01d40 1013 while (i <= hw_points_num + 1) {
ec7e6bb8
LSL
1014 coords->regamma_y_red = rgb_regamma->r;
1015 coords->regamma_y_green = rgb_regamma->g;
1016 coords->regamma_y_blue = rgb_regamma->b;
1017
1018 ++coords;
1019 ++rgb_regamma;
1020 ++i;
1021 }
1022}
1023
1024static bool calculate_interpolated_hardware_curve(
1025 const struct dc_gamma *ramp,
1026 struct pixel_gamma_point *coeff128,
1027 struct pwl_float_data *rgb_user,
1028 const struct hw_x_point *coordinates_x,
1029 const struct gamma_pixel *axis_x,
1030 uint32_t number_of_points,
1031 struct dc_transfer_func_distributed_points *tf_pts)
1032{
1033
1034 const struct pixel_gamma_point *coeff = coeff128;
1035 uint32_t max_entries = 3 - 1;
1036
1037 uint32_t i = 0;
1038
1039 for (i = 0; i < 3; i++) {
1040 if (!build_custom_gamma_mapping_coefficients_worker(
1041 ramp, coeff128, coordinates_x, axis_x, i,
1042 number_of_points))
1043 return false;
1044 }
1045
1046 i = 0;
1047 max_entries += ramp->num_entries;
1048
1049 /* TODO: float point case */
1050
1051 while (i <= number_of_points) {
1052 tf_pts->red[i] = calculate_mapped_value(
1053 rgb_user, coeff, CHANNEL_NAME_RED, max_entries);
1054 tf_pts->green[i] = calculate_mapped_value(
1055 rgb_user, coeff, CHANNEL_NAME_GREEN, max_entries);
1056 tf_pts->blue[i] = calculate_mapped_value(
1057 rgb_user, coeff, CHANNEL_NAME_BLUE, max_entries);
1058
1059 ++coeff;
1060 ++i;
1061 }
1062
1063 return true;
1064}
1065
55a01d40
KK
1066/* The "old" interpolation uses a complicated scheme to build an array of
1067 * coefficients while also using an array of 0-255 normalized to 0-1
1068 * Then there's another loop using both of the above + new scaled user ramp
1069 * and we concatenate them. It also searches for points of interpolation and
1070 * uses enums for positions.
1071 *
1072 * This function uses a different approach:
1073 * user ramp is always applied on X with 0/255, 1/255, 2/255, ..., 255/255
1074 * To find index for hwX , we notice the following:
1075 * i/255 <= hwX < (i+1)/255 <=> i <= 255*hwX < i+1
1076 * See apply_lut_1d which is the same principle, but on 4K entry 1D LUT
1077 *
1078 * Once the index is known, combined Y is simply:
1079 * user_ramp(index) + (hwX-index/255)*(user_ramp(index+1) - user_ramp(index)
1080 *
1081 * We should switch to this method in all cases, it's simpler and faster
1082 * ToDo one day - for now this only applies to ADL regamma to avoid regression
1083 * for regular use cases (sRGB and PQ)
1084 */
1085static void interpolate_user_regamma(uint32_t hw_points_num,
1086 struct pwl_float_data *rgb_user,
1087 bool apply_degamma,
1088 struct dc_transfer_func_distributed_points *tf_pts)
1089{
1090 uint32_t i;
1091 uint32_t color = 0;
1092 int32_t index;
1093 int32_t index_next;
1094 struct fixed31_32 *tf_point;
1095 struct fixed31_32 hw_x;
1096 struct fixed31_32 norm_factor =
f3ba7a2f 1097 dc_fixpt_from_int(255);
55a01d40
KK
1098 struct fixed31_32 norm_x;
1099 struct fixed31_32 index_f;
1100 struct fixed31_32 lut1;
1101 struct fixed31_32 lut2;
1102 struct fixed31_32 delta_lut;
1103 struct fixed31_32 delta_index;
1104
1105 i = 0;
1106 /* fixed_pt library has problems handling too small values */
1107 while (i != 32) {
eb0e5154
DL
1108 tf_pts->red[i] = dc_fixpt_zero;
1109 tf_pts->green[i] = dc_fixpt_zero;
1110 tf_pts->blue[i] = dc_fixpt_zero;
55a01d40
KK
1111 ++i;
1112 }
1113 while (i <= hw_points_num + 1) {
1114 for (color = 0; color < 3; color++) {
1115 if (color == 0)
1116 tf_point = &tf_pts->red[i];
1117 else if (color == 1)
1118 tf_point = &tf_pts->green[i];
1119 else
1120 tf_point = &tf_pts->blue[i];
1121
1122 if (apply_degamma) {
1123 if (color == 0)
1124 hw_x = coordinates_x[i].regamma_y_red;
1125 else if (color == 1)
1126 hw_x = coordinates_x[i].regamma_y_green;
1127 else
1128 hw_x = coordinates_x[i].regamma_y_blue;
1129 } else
1130 hw_x = coordinates_x[i].x;
1131
eb0e5154
DL
1132 norm_x = dc_fixpt_mul(norm_factor, hw_x);
1133 index = dc_fixpt_floor(norm_x);
55a01d40
KK
1134 if (index < 0 || index > 255)
1135 continue;
1136
f3ba7a2f 1137 index_f = dc_fixpt_from_int(index);
55a01d40
KK
1138 index_next = (index == 255) ? index : index + 1;
1139
1140 if (color == 0) {
1141 lut1 = rgb_user[index].r;
1142 lut2 = rgb_user[index_next].r;
1143 } else if (color == 1) {
1144 lut1 = rgb_user[index].g;
1145 lut2 = rgb_user[index_next].g;
1146 } else {
1147 lut1 = rgb_user[index].b;
1148 lut2 = rgb_user[index_next].b;
1149 }
1150
1151 // we have everything now, so interpolate
eb0e5154
DL
1152 delta_lut = dc_fixpt_sub(lut2, lut1);
1153 delta_index = dc_fixpt_sub(norm_x, index_f);
55a01d40 1154
eb0e5154
DL
1155 *tf_point = dc_fixpt_add(lut1,
1156 dc_fixpt_mul(delta_index, delta_lut));
55a01d40
KK
1157 }
1158 ++i;
1159 }
1160}
1161
ec7e6bb8
LSL
1162static void build_new_custom_resulted_curve(
1163 uint32_t hw_points_num,
1164 struct dc_transfer_func_distributed_points *tf_pts)
1165{
1166 uint32_t i;
1167
1168 i = 0;
1169
1170 while (i != hw_points_num + 1) {
eb0e5154
DL
1171 tf_pts->red[i] = dc_fixpt_clamp(
1172 tf_pts->red[i], dc_fixpt_zero,
1173 dc_fixpt_one);
1174 tf_pts->green[i] = dc_fixpt_clamp(
1175 tf_pts->green[i], dc_fixpt_zero,
1176 dc_fixpt_one);
1177 tf_pts->blue[i] = dc_fixpt_clamp(
1178 tf_pts->blue[i], dc_fixpt_zero,
1179 dc_fixpt_one);
ec7e6bb8
LSL
1180
1181 ++i;
1182 }
1183}
1184
55a01d40
KK
1185static void apply_degamma_for_user_regamma(struct pwl_float_data_ex *rgb_regamma,
1186 uint32_t hw_points_num)
1187{
1188 uint32_t i;
1189
1190 struct gamma_coefficients coeff;
1191 struct pwl_float_data_ex *rgb = rgb_regamma;
1192 const struct hw_x_point *coord_x = coordinates_x;
1193
1194 build_coefficients(&coeff, true);
1195
1196 i = 0;
1197 while (i != hw_points_num + 1) {
1198 rgb->r = translate_from_linear_space_ex(
1199 coord_x->x, &coeff, 0);
1200 rgb->g = rgb->r;
1201 rgb->b = rgb->r;
1202 ++coord_x;
1203 ++rgb;
1204 ++i;
1205 }
1206}
1207
ec7e6bb8
LSL
1208static bool map_regamma_hw_to_x_user(
1209 const struct dc_gamma *ramp,
1210 struct pixel_gamma_point *coeff128,
1211 struct pwl_float_data *rgb_user,
1212 struct hw_x_point *coords_x,
1213 const struct gamma_pixel *axis_x,
1214 const struct pwl_float_data_ex *rgb_regamma,
1215 uint32_t hw_points_num,
1216 struct dc_transfer_func_distributed_points *tf_pts,
1217 bool mapUserRamp)
1218{
1219 /* setup to spare calculated ideal regamma values */
1220
1221 int i = 0;
1222 struct hw_x_point *coords = coords_x;
1223 const struct pwl_float_data_ex *regamma = rgb_regamma;
1224
1225 if (mapUserRamp) {
1226 copy_rgb_regamma_to_coordinates_x(coords,
1227 hw_points_num,
1228 rgb_regamma);
1229
1230 calculate_interpolated_hardware_curve(
1231 ramp, coeff128, rgb_user, coords, axis_x,
1232 hw_points_num, tf_pts);
1233 } else {
1234 /* just copy current rgb_regamma into tf_pts */
1235 while (i <= hw_points_num) {
1236 tf_pts->red[i] = regamma->r;
1237 tf_pts->green[i] = regamma->g;
1238 tf_pts->blue[i] = regamma->b;
1239
1240 ++regamma;
1241 ++i;
1242 }
1243 }
1244
55a01d40 1245 /* this should be named differently, all it does is clamp to 0-1 */
ec7e6bb8
LSL
1246 build_new_custom_resulted_curve(hw_points_num, tf_pts);
1247
1248 return true;
1249}
1250
792474b7
VP
1251#define _EXTRA_POINTS 3
1252
ec7e6bb8
LSL
1253bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf,
1254 const struct dc_gamma *ramp, bool mapUserRamp)
1255{
1256 struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
1257 struct dividers dividers;
1258
1259 struct pwl_float_data *rgb_user = NULL;
1260 struct pwl_float_data_ex *rgb_regamma = NULL;
1261 struct gamma_pixel *axix_x = NULL;
792474b7 1262 struct pixel_gamma_point *coeff = NULL;
ec7e6bb8
LSL
1263 enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
1264 bool ret = false;
1265
1266 if (output_tf->type == TF_TYPE_BYPASS)
1267 return false;
1268
1269 /* we can use hardcoded curve for plain SRGB TF */
1270 if (output_tf->type == TF_TYPE_PREDEFINED &&
1271 output_tf->tf == TRANSFER_FUNCTION_SRGB &&
1272 (!mapUserRamp && ramp->type == GAMMA_RGB_256))
1273 return true;
1274
1275 output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1276
f7dbe918
MD
1277 rgb_user = kvzalloc(sizeof(*rgb_user) * (ramp->num_entries + _EXTRA_POINTS),
1278 GFP_KERNEL);
ec7e6bb8
LSL
1279 if (!rgb_user)
1280 goto rgb_user_alloc_fail;
f7dbe918
MD
1281 rgb_regamma = kvzalloc(sizeof(*rgb_regamma) * (MAX_HW_POINTS + _EXTRA_POINTS),
1282 GFP_KERNEL);
ec7e6bb8
LSL
1283 if (!rgb_regamma)
1284 goto rgb_regamma_alloc_fail;
f7dbe918
MD
1285 axix_x = kvzalloc(sizeof(*axix_x) * (ramp->num_entries + 3),
1286 GFP_KERNEL);
ec7e6bb8
LSL
1287 if (!axix_x)
1288 goto axix_x_alloc_fail;
f7dbe918 1289 coeff = kvzalloc(sizeof(*coeff) * (MAX_HW_POINTS + _EXTRA_POINTS), GFP_KERNEL);
792474b7
VP
1290 if (!coeff)
1291 goto coeff_alloc_fail;
ec7e6bb8 1292
eb0e5154
DL
1293 dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1294 dividers.divider2 = dc_fixpt_from_int(2);
1295 dividers.divider3 = dc_fixpt_from_fraction(5, 2);
ec7e6bb8
LSL
1296
1297 tf = output_tf->tf;
1298
1299 build_evenly_distributed_points(
1300 axix_x,
1301 ramp->num_entries,
1302 dividers);
1303
1304 if (ramp->type == GAMMA_RGB_256 && mapUserRamp)
1305 scale_gamma(rgb_user, ramp, dividers);
1306 else if (ramp->type == GAMMA_RGB_FLOAT_1024)
1307 scale_gamma_dx(rgb_user, ramp, dividers);
1308
1309 if (tf == TRANSFER_FUNCTION_PQ) {
1310 tf_pts->end_exponent = 7;
1311 tf_pts->x_point_at_y1_red = 125;
1312 tf_pts->x_point_at_y1_green = 125;
1313 tf_pts->x_point_at_y1_blue = 125;
1314
792474b7 1315 build_pq(rgb_regamma,
ec7e6bb8
LSL
1316 MAX_HW_POINTS,
1317 coordinates_x,
1318 output_tf->sdr_ref_white_level);
1319 } else {
1320 tf_pts->end_exponent = 0;
1321 tf_pts->x_point_at_y1_red = 1;
1322 tf_pts->x_point_at_y1_green = 1;
1323 tf_pts->x_point_at_y1_blue = 1;
1324
792474b7 1325 build_regamma(rgb_regamma,
ec7e6bb8 1326 MAX_HW_POINTS,
792474b7 1327 coordinates_x, tf == TRANSFER_FUNCTION_SRGB ? true:false);
ec7e6bb8
LSL
1328 }
1329
792474b7 1330 map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
ec7e6bb8
LSL
1331 coordinates_x, axix_x, rgb_regamma,
1332 MAX_HW_POINTS, tf_pts,
1333 (mapUserRamp || ramp->type != GAMMA_RGB_256) &&
1334 ramp->type != GAMMA_CS_TFM_1D);
1335
1336 if (ramp->type == GAMMA_CS_TFM_1D)
1337 apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
1338
1339 ret = true;
1340
f7dbe918 1341 kvfree(coeff);
792474b7 1342coeff_alloc_fail:
f7dbe918 1343 kvfree(axix_x);
ec7e6bb8 1344axix_x_alloc_fail:
f7dbe918 1345 kvfree(rgb_regamma);
ec7e6bb8 1346rgb_regamma_alloc_fail:
f7dbe918 1347 kvfree(rgb_user);
ec7e6bb8
LSL
1348rgb_user_alloc_fail:
1349 return ret;
1350}
1351
55a01d40
KK
1352bool calculate_user_regamma_coeff(struct dc_transfer_func *output_tf,
1353 const struct regamma_lut *regamma)
1354{
1355 struct gamma_coefficients coeff;
1356 const struct hw_x_point *coord_x = coordinates_x;
1357 uint32_t i = 0;
1358
1359 do {
eb0e5154 1360 coeff.a0[i] = dc_fixpt_from_fraction(
55a01d40 1361 regamma->coeff.A0[i], 10000000);
eb0e5154 1362 coeff.a1[i] = dc_fixpt_from_fraction(
55a01d40 1363 regamma->coeff.A1[i], 1000);
eb0e5154 1364 coeff.a2[i] = dc_fixpt_from_fraction(
55a01d40 1365 regamma->coeff.A2[i], 1000);
eb0e5154 1366 coeff.a3[i] = dc_fixpt_from_fraction(
55a01d40 1367 regamma->coeff.A3[i], 1000);
eb0e5154 1368 coeff.user_gamma[i] = dc_fixpt_from_fraction(
55a01d40
KK
1369 regamma->coeff.gamma[i], 1000);
1370
1371 ++i;
1372 } while (i != 3);
1373
1374 i = 0;
1375 /* fixed_pt library has problems handling too small values */
1376 while (i != 32) {
eb0e5154
DL
1377 output_tf->tf_pts.red[i] = dc_fixpt_zero;
1378 output_tf->tf_pts.green[i] = dc_fixpt_zero;
1379 output_tf->tf_pts.blue[i] = dc_fixpt_zero;
55a01d40
KK
1380 ++coord_x;
1381 ++i;
1382 }
1383 while (i != MAX_HW_POINTS + 1) {
1384 output_tf->tf_pts.red[i] = translate_from_linear_space_ex(
1385 coord_x->x, &coeff, 0);
1386 output_tf->tf_pts.green[i] = translate_from_linear_space_ex(
1387 coord_x->x, &coeff, 1);
1388 output_tf->tf_pts.blue[i] = translate_from_linear_space_ex(
1389 coord_x->x, &coeff, 2);
1390 ++coord_x;
1391 ++i;
1392 }
1393
1394 // this function just clamps output to 0-1
1395 build_new_custom_resulted_curve(MAX_HW_POINTS, &output_tf->tf_pts);
1396 output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1397
1398 return true;
1399}
1400
1401bool calculate_user_regamma_ramp(struct dc_transfer_func *output_tf,
1402 const struct regamma_lut *regamma)
1403{
1404 struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
1405 struct dividers dividers;
1406
1407 struct pwl_float_data *rgb_user = NULL;
1408 struct pwl_float_data_ex *rgb_regamma = NULL;
1409 bool ret = false;
1410
1411 if (regamma == NULL)
1412 return false;
1413
1414 output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1415
6396bb22
KC
1416 rgb_user = kcalloc(GAMMA_RGB_256_ENTRIES + _EXTRA_POINTS,
1417 sizeof(*rgb_user),
1418 GFP_KERNEL);
55a01d40
KK
1419 if (!rgb_user)
1420 goto rgb_user_alloc_fail;
1421
6396bb22
KC
1422 rgb_regamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1423 sizeof(*rgb_regamma),
1424 GFP_KERNEL);
55a01d40
KK
1425 if (!rgb_regamma)
1426 goto rgb_regamma_alloc_fail;
1427
eb0e5154
DL
1428 dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1429 dividers.divider2 = dc_fixpt_from_int(2);
1430 dividers.divider3 = dc_fixpt_from_fraction(5, 2);
55a01d40
KK
1431
1432 scale_user_regamma_ramp(rgb_user, &regamma->ramp, dividers);
1433
1434 if (regamma->flags.bits.applyDegamma == 1) {
1435 apply_degamma_for_user_regamma(rgb_regamma, MAX_HW_POINTS);
1436 copy_rgb_regamma_to_coordinates_x(coordinates_x,
1437 MAX_HW_POINTS, rgb_regamma);
1438 }
1439
1440 interpolate_user_regamma(MAX_HW_POINTS, rgb_user,
1441 regamma->flags.bits.applyDegamma, tf_pts);
1442
1443 // no custom HDR curves!
1444 tf_pts->end_exponent = 0;
1445 tf_pts->x_point_at_y1_red = 1;
1446 tf_pts->x_point_at_y1_green = 1;
1447 tf_pts->x_point_at_y1_blue = 1;
1448
1449 // this function just clamps output to 0-1
1450 build_new_custom_resulted_curve(MAX_HW_POINTS, tf_pts);
1451
1452 ret = true;
1453
1454 kfree(rgb_regamma);
1455rgb_regamma_alloc_fail:
1fafef9d 1456 kvfree(rgb_user);
55a01d40
KK
1457rgb_user_alloc_fail:
1458 return ret;
1459}
1460
792474b7
VP
1461bool mod_color_calculate_degamma_params(struct dc_transfer_func *input_tf,
1462 const struct dc_gamma *ramp, bool mapUserRamp)
1463{
1464 struct dc_transfer_func_distributed_points *tf_pts = &input_tf->tf_pts;
1465 struct dividers dividers;
1466
1467 struct pwl_float_data *rgb_user = NULL;
1468 struct pwl_float_data_ex *curve = NULL;
1469 struct gamma_pixel *axix_x = NULL;
1470 struct pixel_gamma_point *coeff = NULL;
1471 enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
1472 bool ret = false;
1473
1474 if (input_tf->type == TF_TYPE_BYPASS)
1475 return false;
1476
1477 /* we can use hardcoded curve for plain SRGB TF */
1478 if (input_tf->type == TF_TYPE_PREDEFINED &&
1479 input_tf->tf == TRANSFER_FUNCTION_SRGB &&
1480 (!mapUserRamp && ramp->type == GAMMA_RGB_256))
1481 return true;
1482
1483 input_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1484
f7dbe918
MD
1485 rgb_user = kvzalloc(sizeof(*rgb_user) * (ramp->num_entries + _EXTRA_POINTS),
1486 GFP_KERNEL);
792474b7
VP
1487 if (!rgb_user)
1488 goto rgb_user_alloc_fail;
f7dbe918
MD
1489 curve = kvzalloc(sizeof(*curve) * (MAX_HW_POINTS + _EXTRA_POINTS),
1490 GFP_KERNEL);
792474b7
VP
1491 if (!curve)
1492 goto curve_alloc_fail;
f7dbe918
MD
1493 axix_x = kvzalloc(sizeof(*axix_x) * (ramp->num_entries + _EXTRA_POINTS),
1494 GFP_KERNEL);
792474b7
VP
1495 if (!axix_x)
1496 goto axix_x_alloc_fail;
f7dbe918 1497 coeff = kvzalloc(sizeof(*coeff) * (MAX_HW_POINTS + _EXTRA_POINTS), GFP_KERNEL);
792474b7
VP
1498 if (!coeff)
1499 goto coeff_alloc_fail;
1500
eb0e5154
DL
1501 dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1502 dividers.divider2 = dc_fixpt_from_int(2);
1503 dividers.divider3 = dc_fixpt_from_fraction(5, 2);
792474b7
VP
1504
1505 tf = input_tf->tf;
1506
1507 build_evenly_distributed_points(
1508 axix_x,
1509 ramp->num_entries,
1510 dividers);
1511
1512 if (ramp->type == GAMMA_RGB_256 && mapUserRamp)
1513 scale_gamma(rgb_user, ramp, dividers);
1514 else if (ramp->type == GAMMA_RGB_FLOAT_1024)
1515 scale_gamma_dx(rgb_user, ramp, dividers);
1516
1517 if (tf == TRANSFER_FUNCTION_PQ)
1518 build_de_pq(curve,
44c6f2e5
VP
1519 MAX_HW_POINTS,
1520 coordinates_x);
792474b7
VP
1521 else
1522 build_degamma(curve,
44c6f2e5
VP
1523 MAX_HW_POINTS,
1524 coordinates_x,
792474b7
VP
1525 tf == TRANSFER_FUNCTION_SRGB ? true:false);
1526
1527 tf_pts->end_exponent = 0;
1528 tf_pts->x_point_at_y1_red = 1;
1529 tf_pts->x_point_at_y1_green = 1;
1530 tf_pts->x_point_at_y1_blue = 1;
1531
1532 map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
44c6f2e5
VP
1533 coordinates_x, axix_x, curve,
1534 MAX_HW_POINTS, tf_pts,
792474b7
VP
1535 mapUserRamp);
1536
1537 ret = true;
1538
f7dbe918 1539 kvfree(coeff);
792474b7 1540coeff_alloc_fail:
f7dbe918 1541 kvfree(axix_x);
792474b7 1542axix_x_alloc_fail:
f7dbe918 1543 kvfree(curve);
792474b7 1544curve_alloc_fail:
f7dbe918 1545 kvfree(rgb_user);
792474b7
VP
1546rgb_user_alloc_fail:
1547
1548 return ret;
1549
1550}
1551
1552
ec7e6bb8
LSL
1553bool mod_color_calculate_curve(enum dc_transfer_func_predefined trans,
1554 struct dc_transfer_func_distributed_points *points)
1555{
1556 uint32_t i;
1557 bool ret = false;
1558 struct pwl_float_data_ex *rgb_regamma = NULL;
1559
9eee2137
VP
1560 if (trans == TRANSFER_FUNCTION_UNITY ||
1561 trans == TRANSFER_FUNCTION_LINEAR) {
792474b7
VP
1562 points->end_exponent = 0;
1563 points->x_point_at_y1_red = 1;
1564 points->x_point_at_y1_green = 1;
1565 points->x_point_at_y1_blue = 1;
1566
44c6f2e5 1567 for (i = 0; i <= MAX_HW_POINTS ; i++) {
ec7e6bb8
LSL
1568 points->red[i] = coordinates_x[i].x;
1569 points->green[i] = coordinates_x[i].x;
1570 points->blue[i] = coordinates_x[i].x;
1571 }
1572 ret = true;
1573 } else if (trans == TRANSFER_FUNCTION_PQ) {
f7dbe918
MD
1574 rgb_regamma = kvzalloc(sizeof(*rgb_regamma) *
1575 (MAX_HW_POINTS + _EXTRA_POINTS),
1576 GFP_KERNEL);
ec7e6bb8
LSL
1577 if (!rgb_regamma)
1578 goto rgb_regamma_alloc_fail;
792474b7
VP
1579 points->end_exponent = 7;
1580 points->x_point_at_y1_red = 125;
1581 points->x_point_at_y1_green = 125;
1582 points->x_point_at_y1_blue = 125;
1583
1584
1585 build_pq(rgb_regamma,
1586 MAX_HW_POINTS,
1587 coordinates_x,
1588 80);
44c6f2e5 1589 for (i = 0; i <= MAX_HW_POINTS ; i++) {
792474b7
VP
1590 points->red[i] = rgb_regamma[i].r;
1591 points->green[i] = rgb_regamma[i].g;
1592 points->blue[i] = rgb_regamma[i].b;
1593 }
1594 ret = true;
1595
f7dbe918 1596 kvfree(rgb_regamma);
792474b7
VP
1597 } else if (trans == TRANSFER_FUNCTION_SRGB ||
1598 trans == TRANSFER_FUNCTION_BT709) {
f7dbe918
MD
1599 rgb_regamma = kvzalloc(sizeof(*rgb_regamma) *
1600 (MAX_HW_POINTS + _EXTRA_POINTS),
1601 GFP_KERNEL);
792474b7
VP
1602 if (!rgb_regamma)
1603 goto rgb_regamma_alloc_fail;
ec7e6bb8
LSL
1604 points->end_exponent = 0;
1605 points->x_point_at_y1_red = 1;
1606 points->x_point_at_y1_green = 1;
1607 points->x_point_at_y1_blue = 1;
1608
792474b7 1609 build_regamma(rgb_regamma,
ec7e6bb8 1610 MAX_HW_POINTS,
792474b7 1611 coordinates_x, trans == TRANSFER_FUNCTION_SRGB ? true:false);
44c6f2e5 1612 for (i = 0; i <= MAX_HW_POINTS ; i++) {
ec7e6bb8
LSL
1613 points->red[i] = rgb_regamma[i].r;
1614 points->green[i] = rgb_regamma[i].g;
1615 points->blue[i] = rgb_regamma[i].b;
1616 }
1617 ret = true;
1618
f7dbe918 1619 kvfree(rgb_regamma);
ec7e6bb8
LSL
1620 }
1621rgb_regamma_alloc_fail:
1622 return ret;
1623}
1624
1625
792474b7
VP
1626bool mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans,
1627 struct dc_transfer_func_distributed_points *points)
1628{
1629 uint32_t i;
1630 bool ret = false;
1631 struct pwl_float_data_ex *rgb_degamma = NULL;
1632
9eee2137
VP
1633 if (trans == TRANSFER_FUNCTION_UNITY ||
1634 trans == TRANSFER_FUNCTION_LINEAR) {
792474b7 1635
44c6f2e5
VP
1636 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1637 points->red[i] = coordinates_x[i].x;
1638 points->green[i] = coordinates_x[i].x;
1639 points->blue[i] = coordinates_x[i].x;
792474b7
VP
1640 }
1641 ret = true;
1642 } else if (trans == TRANSFER_FUNCTION_PQ) {
f7dbe918
MD
1643 rgb_degamma = kvzalloc(sizeof(*rgb_degamma) *
1644 (MAX_HW_POINTS + _EXTRA_POINTS),
1645 GFP_KERNEL);
792474b7
VP
1646 if (!rgb_degamma)
1647 goto rgb_degamma_alloc_fail;
1648
1649
1650 build_de_pq(rgb_degamma,
44c6f2e5
VP
1651 MAX_HW_POINTS,
1652 coordinates_x);
1653 for (i = 0; i <= MAX_HW_POINTS ; i++) {
792474b7
VP
1654 points->red[i] = rgb_degamma[i].r;
1655 points->green[i] = rgb_degamma[i].g;
1656 points->blue[i] = rgb_degamma[i].b;
1657 }
1658 ret = true;
1659
f7dbe918 1660 kvfree(rgb_degamma);
792474b7
VP
1661 } else if (trans == TRANSFER_FUNCTION_SRGB ||
1662 trans == TRANSFER_FUNCTION_BT709) {
f7dbe918
MD
1663 rgb_degamma = kvzalloc(sizeof(*rgb_degamma) *
1664 (MAX_HW_POINTS + _EXTRA_POINTS),
1665 GFP_KERNEL);
792474b7
VP
1666 if (!rgb_degamma)
1667 goto rgb_degamma_alloc_fail;
1668
1669 build_degamma(rgb_degamma,
44c6f2e5
VP
1670 MAX_HW_POINTS,
1671 coordinates_x, trans == TRANSFER_FUNCTION_SRGB ? true:false);
1672 for (i = 0; i <= MAX_HW_POINTS ; i++) {
792474b7
VP
1673 points->red[i] = rgb_degamma[i].r;
1674 points->green[i] = rgb_degamma[i].g;
1675 points->blue[i] = rgb_degamma[i].b;
1676 }
1677 ret = true;
1678
f7dbe918 1679 kvfree(rgb_degamma);
792474b7
VP
1680 }
1681 points->end_exponent = 0;
1682 points->x_point_at_y1_red = 1;
1683 points->x_point_at_y1_green = 1;
1684 points->x_point_at_y1_blue = 1;
1685
1686rgb_degamma_alloc_fail:
1687 return ret;
1688}
1689
1690