1 /* Copyright 2022 Advanced Micro Devices, Inc.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a
4 * copy of this software and associated documentation files (the "Software"),
5 * to deal in the Software without restriction, including without limitation
6 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 * and/or sell copies of the Software, and to permit persons to whom the
8 * Software is furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
17 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
18 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
19 * OTHER DEALINGS IN THE SOFTWARE.
20 *
21 * Authors: AMD
22 *
23 */
24
25 #include <string.h>
26 #include "common.h"
27 #include "vpe_priv.h"
28 #include "color.h"
29 #include "color_gamma.h"
30 #include "hw_shared.h"
31
32 #define PRECISE_LUT_REGION_START 224
33 #define PRECISE_LUT_REGION_END 239
34
35 static struct hw_x_point coordinates_x[MAX_HW_POINTS + 2];
36 static struct hw_x_point coordinates_x_degamma[MAX_HW_POINTS_DEGAMMA];
37
38 // these are helpers for calculations to reduce stack usage
39 // do not depend on these being preserved across calls
40
41 /* Helper to optimize gamma calculation, only use in translate_from_linear, in
42 * particular the vpe_fixpt_pow function which is very expensive
43 * The idea is that our regions for X points are exponential and currently they all use
44 * the same number of points (NUM_PTS_IN_REGION) and in each region every point
45 * is exactly 2x the one at the same index in the previous region. In other words
46 * X[i] = 2 * X[i-NUM_PTS_IN_REGION] for i>=16
47 * The other fact is that (2x)^gamma = 2^gamma * x^gamma
48 * So we compute and save x^gamma for the first 16 regions, and for every next region
49 * just multiply with 2^gamma which can be computed once, and save the result so we
50 * recursively compute all the values.
51 */
52
53 /*
54 * Regamma coefficients are used for both regamma and degamma. Degamma
55 * coefficients are calculated in our formula using the regamma coefficients.
56 */
57 /*sRGB 709 2.2 2.4 P3*/
58 static const int32_t numerator01[] = {31308, 180000, 0, 0, 0};
59 static const int32_t numerator02[] = {12920, 4500, 0, 0, 0};
60 static const int32_t numerator03[] = {55, 99, 0, 0, 0};
61 static const int32_t numerator04[] = {55, 99, 0, 0, 0};
62 static const int32_t numerator05[] = {
63 2400, 2222, 2200, 2400, 2600}; // the standard REC 709 states 0.45. Inverse of that is 2.22
64
65 /* one-time setup of X points */
vpe_color_setup_x_points_distribution(void)66 void vpe_color_setup_x_points_distribution(void)
67 {
68 struct fixed31_32 region_size = vpe_fixpt_from_int(128);
69 int32_t segment;
70 uint32_t seg_offset;
71 uint32_t index;
72 struct fixed31_32 increment;
73
74 coordinates_x[MAX_HW_POINTS].x = region_size;
75 coordinates_x[MAX_HW_POINTS + 1].x = region_size;
76
77 for (segment = 6; segment > (6 - NUM_REGIONS); segment--) {
78 region_size = vpe_fixpt_div_int(region_size, 2);
79 increment = vpe_fixpt_div_int(region_size, NUM_PTS_IN_REGION);
80 seg_offset = (uint32_t)((segment + (NUM_REGIONS - 7)) * NUM_PTS_IN_REGION);
81
82 coordinates_x[seg_offset].x = region_size;
83
84 for (index = seg_offset + 1; index < seg_offset + NUM_PTS_IN_REGION; index++) {
85 coordinates_x[index].x = vpe_fixpt_add(coordinates_x[index - 1].x, increment);
86 }
87 }
88 }
89
90 /* Setting up x points for DEGAMMA once */
vpe_color_setup_x_points_distribution_degamma(void)91 void vpe_color_setup_x_points_distribution_degamma(void)
92 {
93 struct fixed31_32 region_size = vpe_fixpt_from_int(1);
94 int32_t segment;
95 uint32_t index = 0;
96 uint32_t numptsdegamma = 1;
97 uint32_t segment_offset;
98
99 /* Since region = -8 only has 1 point setting it up before the loop */
100 coordinates_x_degamma[0].x = vpe_fixpt_div(vpe_fixpt_from_int(1), vpe_fixpt_from_int(512));
101
102 for (segment = -7; segment <= 0; segment++) {
103 segment_offset = numptsdegamma;
104 numptsdegamma *= 2;
105
106 for (index = segment_offset; index < numptsdegamma; index++) {
107 coordinates_x_degamma[index].x =
108 vpe_fixpt_div(vpe_fixpt_from_int(index), vpe_fixpt_from_int(256));
109 }
110 }
111 coordinates_x_degamma[MAX_HW_POINTS_DEGAMMA - 1].x = region_size;
112 }
113
vpe_compute_pq(struct fixed31_32 in_x,struct fixed31_32 * out_y)114 void vpe_compute_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y)
115 {
116 /* consts for PQ gamma formula. */
117 const struct fixed31_32 m1 = vpe_fixpt_from_fraction(159301758, 1000000000);
118 const struct fixed31_32 m2 = vpe_fixpt_from_fraction(7884375, 100000);
119 const struct fixed31_32 c1 = vpe_fixpt_from_fraction(8359375, 10000000);
120 const struct fixed31_32 c2 = vpe_fixpt_from_fraction(188515625, 10000000);
121 const struct fixed31_32 c3 = vpe_fixpt_from_fraction(186875, 10000);
122
123 struct fixed31_32 l_pow_m1;
124 struct fixed31_32 base;
125
126 if (vpe_fixpt_le(vpe_fixpt_one, in_x)) {
127 *out_y = vpe_fixpt_one;
128 return;
129 }
130
131 if (vpe_fixpt_lt(in_x, vpe_fixpt_zero))
132 in_x = vpe_fixpt_zero;
133
134 l_pow_m1 = vpe_fixpt_pow(in_x, m1);
135 base = vpe_fixpt_div(vpe_fixpt_add(c1, (vpe_fixpt_mul(c2, l_pow_m1))),
136 vpe_fixpt_add(vpe_fixpt_one, (vpe_fixpt_mul(c3, l_pow_m1))));
137 *out_y = vpe_fixpt_pow(base, m2);
138 }
139
compute_de_pq(struct fixed31_32 in_x,struct fixed31_32 * out_y)140 static void compute_de_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y)
141 {
142 /* consts for dePQ gamma formula. */
143 const struct fixed31_32 m1 = vpe_fixpt_from_fraction(159301758, 1000000000);
144 const struct fixed31_32 m2 = vpe_fixpt_from_fraction(7884375, 100000);
145 const struct fixed31_32 c1 = vpe_fixpt_from_fraction(8359375, 10000000);
146 const struct fixed31_32 c2 = vpe_fixpt_from_fraction(188515625, 10000000);
147 const struct fixed31_32 c3 = vpe_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 if (vpe_fixpt_lt(in_x, vpe_fixpt_zero))
154 in_x = vpe_fixpt_zero;
155
156 if (vpe_fixpt_le(vpe_fixpt_one, in_x)) {
157 *out_y = vpe_fixpt_one;
158 return;
159 }
160
161 l_pow_m1 = vpe_fixpt_pow(in_x, vpe_fixpt_div(vpe_fixpt_one, m2));
162 base = vpe_fixpt_sub(l_pow_m1, c1);
163
164 div = vpe_fixpt_sub(c2, vpe_fixpt_mul(c3, l_pow_m1));
165
166 base2 = vpe_fixpt_div(base, div);
167 // avoid complex numbers
168 if (vpe_fixpt_lt(base2, vpe_fixpt_zero))
169 base2 = vpe_fixpt_sub(vpe_fixpt_zero, base2);
170
171 *out_y = vpe_fixpt_pow(base2, vpe_fixpt_div(vpe_fixpt_one, m1));
172 }
173
174 /* one-time pre-compute PQ values - only for sdr_white_level 80 */
precompute_pq(void)175 static void precompute_pq(void)
176 {
177 int i;
178 struct fixed31_32 x;
179 const struct hw_x_point *coord_x = coordinates_x + 32;
180 struct fixed31_32 scaling_factor = vpe_fixpt_from_fraction(80, 10000);
181
182 struct fixed31_32 *pq_table = vpe_color_get_table(type_pq_table);
183
184 /* pow function has problems with arguments too small */
185 for (i = 0; i < 32; i++)
186 pq_table[i] = vpe_fixpt_zero;
187
188 for (i = 32; i <= MAX_HW_POINTS; i++) {
189 x = vpe_fixpt_mul(coord_x->x, scaling_factor);
190 vpe_compute_pq(x, &pq_table[i]);
191 ++coord_x;
192 }
193 }
194
195 /* one-time pre-compute dePQ values - only for max pixel value 125 FP16.
196 yuv2rgbScaling is used when the output yuv->rgb is scaled down
197 due to limited range of the yuv2rgb matrix
198 */
199
precompute_de_pq(struct fixed31_32 x_scale,struct fixed31_32 y_scale)200 static void precompute_de_pq(struct fixed31_32 x_scale, struct fixed31_32 y_scale)
201 {
202 uint32_t i;
203 struct fixed31_32 y;
204 struct fixed31_32 *de_pq_table = vpe_color_get_table(type_de_pq_table);
205
206 for (i = 0; i < MAX_HW_POINTS_DEGAMMA; i++) {
207 compute_de_pq(vpe_fixpt_mul(coordinates_x_degamma[i].x, x_scale), &y);
208 de_pq_table[i] = vpe_fixpt_mul(y, y_scale);
209 }
210 }
211
build_coefficients(struct gamma_coefficients * coefficients,enum color_transfer_func type)212 static bool build_coefficients(
213 struct gamma_coefficients *coefficients, enum color_transfer_func type)
214 {
215
216 uint32_t i = 0;
217 uint32_t index = 0;
218 bool ret = true;
219
220 if (type == TRANSFER_FUNC_SRGB)
221 index = 0;
222 else if (type == TRANSFER_FUNC_BT709)
223 index = 1;
224 else if (type == TRANSFER_FUNC_BT1886)
225 index = 3;
226 else {
227 VPE_ASSERT(0);
228 ret = false;
229 goto release;
230 }
231
232 do {
233 coefficients->a0[i] = vpe_fixpt_from_fraction(numerator01[index], 10000000);
234 coefficients->a1[i] = vpe_fixpt_from_fraction(numerator02[index], 1000);
235 coefficients->a2[i] = vpe_fixpt_from_fraction(numerator03[index], 1000);
236 coefficients->a3[i] = vpe_fixpt_from_fraction(numerator04[index], 1000);
237 coefficients->user_gamma[i] = vpe_fixpt_from_fraction(numerator05[index], 1000);
238
239 ++i;
240 } while (i != ARRAY_SIZE(coefficients->a0));
241 release:
242 return ret;
243 }
244
245 // bt.1886
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)246 static struct fixed31_32 translate_to_linear_space(struct fixed31_32 arg, struct fixed31_32 a0,
247 struct fixed31_32 a1, struct fixed31_32 a2, struct fixed31_32 a3, struct fixed31_32 gamma)
248 {
249 struct fixed31_32 linear;
250
251 a0 = vpe_fixpt_mul(a0, a1);
252 if (vpe_fixpt_le(arg, vpe_fixpt_neg(a0)))
253
254 linear = vpe_fixpt_neg(vpe_fixpt_pow(
255 vpe_fixpt_div(vpe_fixpt_sub(a2, arg), vpe_fixpt_add(vpe_fixpt_one, a3)), gamma));
256
257 else if (vpe_fixpt_le(vpe_fixpt_neg(a0), arg) && vpe_fixpt_le(arg, a0))
258 linear = vpe_fixpt_div(arg, a1);
259 else
260 linear = vpe_fixpt_pow(
261 vpe_fixpt_div(vpe_fixpt_add(a2, arg), vpe_fixpt_add(vpe_fixpt_one, a3)), gamma);
262
263 return linear;
264 }
265
translate_to_linear_space_ex(struct fixed31_32 arg,struct gamma_coefficients * coeff,uint32_t color_index)266 static inline struct fixed31_32 translate_to_linear_space_ex(
267 struct fixed31_32 arg, struct gamma_coefficients *coeff, uint32_t color_index)
268 {
269 if (vpe_fixpt_le(vpe_fixpt_one, arg))
270 return vpe_fixpt_one;
271
272 return translate_to_linear_space(arg, coeff->a0[color_index], coeff->a1[color_index],
273 coeff->a2[color_index], coeff->a3[color_index], coeff->user_gamma[color_index]);
274 }
275
translate_from_linear_space(struct translate_from_linear_space_args * args)276 static struct fixed31_32 translate_from_linear_space(struct translate_from_linear_space_args *args)
277 {
278 const struct fixed31_32 one = vpe_fixpt_from_int(1);
279
280 struct fixed31_32 scratch_1, scratch_2;
281 struct calculate_buffer *cal_buffer = args->cal_buffer;
282
283 if (vpe_fixpt_le(one, args->arg))
284 return one;
285
286 if (vpe_fixpt_le(args->arg, vpe_fixpt_neg(args->a0))) {
287 scratch_1 = vpe_fixpt_add(one, args->a3);
288 scratch_2 = vpe_fixpt_pow(vpe_fixpt_neg(args->arg), vpe_fixpt_recip(args->gamma));
289 scratch_1 = vpe_fixpt_mul(scratch_1, scratch_2);
290 scratch_1 = vpe_fixpt_sub(args->a2, scratch_1);
291
292 return scratch_1;
293 } else if (vpe_fixpt_le(args->a0, args->arg)) {
294 if (cal_buffer->buffer_index == 0) {
295 cal_buffer->gamma_of_2 =
296 vpe_fixpt_pow(vpe_fixpt_from_int(2), vpe_fixpt_recip(args->gamma));
297 }
298 scratch_1 = vpe_fixpt_add(one, args->a3);
299 // In the first region (first 16 points) and in the
300 // region delimited by START/END we calculate with
301 // full precision to avoid error accumulation.
302 if ((cal_buffer->buffer_index >= PRECISE_LUT_REGION_START &&
303 cal_buffer->buffer_index <= PRECISE_LUT_REGION_END) ||
304 (cal_buffer->buffer_index < 16))
305 scratch_2 = vpe_fixpt_pow(args->arg, vpe_fixpt_recip(args->gamma));
306 else
307 scratch_2 = vpe_fixpt_mul(
308 cal_buffer->gamma_of_2, cal_buffer->buffer[cal_buffer->buffer_index % 16]);
309
310 if (cal_buffer->buffer_index != -1) {
311 cal_buffer->buffer[cal_buffer->buffer_index % 16] = scratch_2;
312 cal_buffer->buffer_index++;
313 }
314
315 scratch_1 = vpe_fixpt_mul(scratch_1, scratch_2);
316 scratch_1 = vpe_fixpt_sub(scratch_1, args->a2);
317
318 return scratch_1;
319 } else
320 return vpe_fixpt_mul(args->arg, args->a1);
321 }
322
translate_from_linear_space_ex(struct fixed31_32 arg,struct gamma_coefficients * coeff,uint32_t color_index,struct calculate_buffer * cal_buffer)323 static struct fixed31_32 translate_from_linear_space_ex(struct fixed31_32 arg,
324 struct gamma_coefficients *coeff, uint32_t color_index, struct calculate_buffer *cal_buffer)
325 {
326 struct translate_from_linear_space_args scratch_gamma_args = {0};
327
328 scratch_gamma_args.arg = arg;
329 scratch_gamma_args.a0 = coeff->a0[color_index];
330 scratch_gamma_args.a1 = coeff->a1[color_index];
331 scratch_gamma_args.a2 = coeff->a2[color_index];
332 scratch_gamma_args.a3 = coeff->a3[color_index];
333 scratch_gamma_args.gamma = coeff->user_gamma[color_index];
334 scratch_gamma_args.cal_buffer = cal_buffer;
335
336 return translate_from_linear_space(&scratch_gamma_args);
337 }
338
build_pq(uint32_t hw_points_num,const struct hw_x_point * coordinate_x,struct fixed31_32 x_scale,struct fixed31_32 y_scale,struct pwl_float_data_ex * rgb_regamma)339 static void build_pq(uint32_t hw_points_num, const struct hw_x_point *coordinate_x,
340 struct fixed31_32 x_scale, struct fixed31_32 y_scale, struct pwl_float_data_ex *rgb_regamma)
341 {
342 uint32_t i, curve_start_index;
343
344 struct pwl_float_data_ex *rgb = rgb_regamma;
345 const struct hw_x_point *coord_x = coordinate_x;
346 struct fixed31_32 output;
347 struct fixed31_32 slope;
348
349 /* Curve Start index is from segment 2^-24, the first segment must be
350 * treated as a linear interpolation due to numbers being to small for power
351 * operations
352 */
353 curve_start_index = 32;
354 vpe_compute_pq(vpe_fixpt_mul(coord_x[curve_start_index].x, x_scale), &output);
355 output = vpe_fixpt_mul(output, y_scale);
356 slope = vpe_fixpt_div(output, coord_x[curve_start_index].x);
357
358 for (i = 0; i < curve_start_index; i++) {
359 output = vpe_fixpt_mul(coord_x->x, slope);
360 rgb->r = output;
361 rgb->g = output;
362 rgb->b = output;
363
364 ++coord_x;
365 ++rgb;
366 }
367
368 for (i = curve_start_index; i <= hw_points_num; i++) {
369
370 vpe_compute_pq(vpe_fixpt_mul(coord_x->x, x_scale), &output);
371 output = vpe_fixpt_mul(output, y_scale);
372 rgb->r = output;
373 rgb->g = output;
374 rgb->b = output;
375
376 ++coord_x;
377 ++rgb;
378 }
379 }
380
build_de_pq(uint32_t hw_points_num,const struct hw_x_point * coordinate_x_degamma,struct fixed31_32 x_scale,struct fixed31_32 y_scale,struct transfer_func_distributed_points * de_pq)381 static void build_de_pq(uint32_t hw_points_num, const struct hw_x_point *coordinate_x_degamma,
382 struct fixed31_32 x_scale, struct fixed31_32 y_scale,
383 struct transfer_func_distributed_points *de_pq)
384 {
385 struct fixed31_32 output;
386
387 for (uint32_t i = 0; i < hw_points_num; i++) {
388 compute_de_pq(vpe_fixpt_mul(coordinates_x_degamma[i].x, x_scale), &output);
389 output = vpe_fixpt_mul(output, y_scale);
390 de_pq->red[i] = output;
391 de_pq->green[i] = output;
392 de_pq->blue[i] = output;
393 }
394 }
395
build_degamma(uint32_t hw_points_num,const struct hw_x_point * coordinate_x_degamma,enum color_transfer_func type,struct fixed31_32 x_scale,struct fixed31_32 y_scale,struct transfer_func_distributed_points * curve)396 static bool build_degamma(uint32_t hw_points_num, const struct hw_x_point *coordinate_x_degamma,
397 enum color_transfer_func type, struct fixed31_32 x_scale, struct fixed31_32 y_scale,
398 struct transfer_func_distributed_points *curve)
399 {
400 uint32_t i;
401 struct gamma_coefficients coeff;
402 struct fixed31_32 output;
403 bool ret = false;
404
405 if (!build_coefficients(&coeff, type))
406 goto release;
407
408 /* De-gamma X is 2^-8 to 2^0 i.e. 9 regions
409 */
410
411 i = 0;
412 while (i != MAX_HW_POINTS_DEGAMMA) {
413 output = vpe_fixpt_mul(coordinate_x_degamma[i].x, x_scale);
414
415 output = translate_to_linear_space_ex(output, &coeff, 0);
416 output = vpe_fixpt_mul(output, y_scale);
417
418 curve->red[i] = output;
419 curve->green[i] = output;
420 curve->blue[i] = output;
421 i++;
422 }
423 ret = true;
424 release:
425 return ret;
426 }
427
build_regamma(struct vpe_priv * vpe_priv,uint32_t hw_points_num,const struct hw_x_point * coordinate_x,enum color_transfer_func type,struct fixed31_32 x_scale,struct fixed31_32 y_scale,struct calculate_buffer * cal_buffer,struct pwl_float_data_ex * rgb_regamma)428 static bool build_regamma(struct vpe_priv *vpe_priv, uint32_t hw_points_num,
429 const struct hw_x_point *coordinate_x, enum color_transfer_func type, struct fixed31_32 x_scale,
430 struct fixed31_32 y_scale, struct calculate_buffer *cal_buffer,
431 struct pwl_float_data_ex *rgb_regamma)
432 {
433 uint32_t i;
434 bool ret = false;
435
436 struct gamma_coefficients *coeff;
437 struct pwl_float_data_ex *rgb = rgb_regamma;
438 const struct hw_x_point *coord_x = coordinate_x;
439
440 coeff = (struct gamma_coefficients *)vpe_zalloc(sizeof(*coeff));
441 if (!coeff)
442 goto release;
443
444 if (!build_coefficients(coeff, type))
445 goto release;
446
447 memset(cal_buffer->buffer, 0, NUM_PTS_IN_REGION * sizeof(struct fixed31_32));
448 cal_buffer->buffer_index = 0; // see variable definition for more info
449
450 i = 0;
451 while (i <= hw_points_num) {
452 /* TODO use y vs r,g,b */
453 rgb->r = vpe_fixpt_mul(coord_x->x, x_scale);
454 rgb->r = translate_from_linear_space_ex(rgb->r, coeff, 0, cal_buffer);
455 rgb->r = vpe_fixpt_mul(rgb->r, y_scale);
456 rgb->g = rgb->r;
457 rgb->b = rgb->r;
458 ++coord_x;
459 ++rgb;
460 ++i;
461 }
462 cal_buffer->buffer_index = -1;
463 ret = true;
464 release:
465 vpe_free(coeff);
466 return ret;
467 }
468
build_new_custom_resulted_curve(uint32_t hw_points_num,struct transfer_func_distributed_points * tf_pts)469 static void build_new_custom_resulted_curve(
470 uint32_t hw_points_num, struct transfer_func_distributed_points *tf_pts)
471 {
472 uint32_t i = 0;
473
474 while (i != hw_points_num + 1) {
475 tf_pts->red[i] = vpe_fixpt_clamp(tf_pts->red[i], vpe_fixpt_zero, vpe_fixpt_one);
476 tf_pts->green[i] = vpe_fixpt_clamp(tf_pts->green[i], vpe_fixpt_zero, vpe_fixpt_one);
477 tf_pts->blue[i] = vpe_fixpt_clamp(tf_pts->blue[i], vpe_fixpt_zero, vpe_fixpt_one);
478
479 ++i;
480 }
481 }
482
map_regamma_hw_to_x_user(struct hw_x_point * coords_x,const struct pwl_float_data_ex * rgb_regamma,uint32_t hw_points_num,struct transfer_func_distributed_points * tf_pts,bool doClamping)483 static bool map_regamma_hw_to_x_user(struct hw_x_point *coords_x, const struct pwl_float_data_ex *rgb_regamma,
484 uint32_t hw_points_num, struct transfer_func_distributed_points *tf_pts, bool doClamping)
485 {
486 /* setup to spare calculated ideal regamma values */
487
488 uint32_t i = 0;
489 struct hw_x_point *coords = coords_x;
490 const struct pwl_float_data_ex *regamma = rgb_regamma;
491
492 /* just copy current rgb_regamma into tf_pts */
493 while (i <= hw_points_num) {
494 tf_pts->red[i] = regamma->r;
495 tf_pts->green[i] = regamma->g;
496 tf_pts->blue[i] = regamma->b;
497
498 ++regamma;
499 ++i;
500 }
501
502 if (doClamping) {
503 /* this should be named differently, all it does is clamp to 0-1 */
504 build_new_custom_resulted_curve(hw_points_num, tf_pts);
505 }
506
507 return true;
508 }
509
calculate_curve(struct vpe_priv * vpe_priv,enum color_transfer_func trans,struct fixed31_32 x_scale,struct fixed31_32 y_scale,struct transfer_func_distributed_points * points,struct pwl_float_data_ex * rgb_regamma,struct calculate_buffer * cal_buffer)510 static bool calculate_curve(struct vpe_priv *vpe_priv, enum color_transfer_func trans,
511 struct fixed31_32 x_scale, struct fixed31_32 y_scale,
512 struct transfer_func_distributed_points *points, struct pwl_float_data_ex *rgb_regamma,
513 struct calculate_buffer *cal_buffer)
514 {
515 int hdr_norm = vpe_priv->resource.internal_hdr_normalization;
516 struct fixed31_32 combined_scale;
517
518 bool ret = false;
519 switch (trans)
520 {
521 case TRANSFER_FUNC_SRGB:
522 case TRANSFER_FUNC_BT709:
523 case TRANSFER_FUNC_BT1886:
524 build_regamma(vpe_priv, MAX_HW_POINTS, coordinates_x, trans, x_scale, y_scale, cal_buffer,
525 rgb_regamma);
526 ret = true;
527 break;
528 case TRANSFER_FUNC_PQ2084:
529 build_pq(MAX_HW_POINTS, coordinates_x, x_scale, y_scale, rgb_regamma);
530 ret = true;
531 break;
532 case TRANSFER_FUNC_LINEAR:
533 combined_scale = vpe_fixpt_div_int(vpe_fixpt_one, hdr_norm);
534 combined_scale = vpe_fixpt_mul(combined_scale, y_scale);
535 combined_scale = vpe_fixpt_mul(combined_scale, x_scale);
536 for (int i = 0; i < MAX_HW_POINTS; i++) {
537 rgb_regamma[i].r = vpe_fixpt_mul(coordinates_x[i].x, combined_scale);
538 rgb_regamma[i].g = rgb_regamma[i].r;
539 rgb_regamma[i].b = rgb_regamma[i].r;
540 }
541
542 ret = true;
543 break;
544 case TRANSFER_FUNC_NORMALIZED_PQ:
545 case TRANSFER_FUNC_UNKNOWN:
546 break;
547 default:
548 break;
549 }
550
551 return ret;
552 }
553
554 #define _EXTRA_POINTS 3
555
vpe_color_calculate_degamma_params(struct vpe_priv * vpe_priv,struct fixed31_32 x_scale,struct fixed31_32 y_scale,struct transfer_func * input_tf)556 bool vpe_color_calculate_degamma_params(struct vpe_priv *vpe_priv, struct fixed31_32 x_scale,
557 struct fixed31_32 y_scale, struct transfer_func *input_tf)
558 {
559 struct transfer_func_distributed_points *tf_pts = &input_tf->tf_pts;
560 enum color_transfer_func tf = input_tf->tf;
561 int hdr_norm = vpe_priv->resource.internal_hdr_normalization;
562 bool ret = false;
563 struct fixed31_32 scale_combined;
564 struct fixed31_32 output;
565
566
567
568 switch (tf)
569 {
570 case TRANSFER_FUNC_PQ2084:
571 case TRANSFER_FUNC_NORMALIZED_PQ:
572 build_de_pq(MAX_HW_POINTS_DEGAMMA, coordinates_x_degamma, x_scale, y_scale, tf_pts);
573 ret = true;
574 break;
575 case TRANSFER_FUNC_SRGB:
576 case TRANSFER_FUNC_BT709:
577 case TRANSFER_FUNC_BT1886:
578 build_degamma(MAX_HW_POINTS_DEGAMMA, coordinates_x_degamma, tf, x_scale, y_scale, tf_pts);
579 ret = true;
580 break;
581 case TRANSFER_FUNC_LINEAR:
582 scale_combined = vpe_fixpt_mul(vpe_fixpt_from_int(hdr_norm), x_scale);
583 scale_combined = vpe_fixpt_mul(scale_combined, y_scale);
584
585 for (int i = 0; i < MAX_HW_POINTS_DEGAMMA; i ++) {
586 output = vpe_fixpt_mul(coordinates_x_degamma[i].x, scale_combined);
587 tf_pts->red[i] = output;
588 tf_pts->green[i] = output;
589 tf_pts->blue[i] = output;
590 }
591 ret = true;
592 break;
593 default:
594 break;
595 }
596 return ret;
597 }
598
vpe_color_calculate_regamma_params(struct vpe_priv * vpe_priv,struct fixed31_32 x_scale,struct fixed31_32 y_scale,struct calculate_buffer * cal_buffer,struct transfer_func * output_tf)599 bool vpe_color_calculate_regamma_params(struct vpe_priv *vpe_priv, struct fixed31_32 x_scale,
600 struct fixed31_32 y_scale, struct calculate_buffer *cal_buffer, struct transfer_func *output_tf)
601 {
602 struct transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
603 struct pwl_float_data_ex *rgb_regamma = NULL;
604 struct pixel_gamma_point *coeff = NULL;
605 enum color_transfer_func tf;
606 bool ret = false;
607
608 rgb_regamma = (struct pwl_float_data_ex *)vpe_zalloc(
609 (MAX_HW_POINTS + _EXTRA_POINTS) * sizeof(*rgb_regamma));
610 if (!rgb_regamma)
611 goto rgb_regamma_alloc_fail;
612
613 coeff =
614 (struct pixel_gamma_point *)vpe_zalloc((MAX_HW_POINTS + _EXTRA_POINTS) * sizeof(*coeff));
615 if (!coeff)
616 goto coeff_alloc_fail;
617
618 tf = output_tf->tf;
619
620 ret = calculate_curve(vpe_priv, tf, x_scale, y_scale, tf_pts, rgb_regamma, cal_buffer);
621
622 if (ret) {
623 map_regamma_hw_to_x_user(coordinates_x, rgb_regamma, MAX_HW_POINTS, tf_pts, false);
624 }
625
626 vpe_free(coeff);
627 coeff_alloc_fail:
628 vpe_free(rgb_regamma);
629 rgb_regamma_alloc_fail:
630 return ret;
631 }
632