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