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