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 #include <stdint.h>
25 #include <string.h>
26 #include "vpe10_cm_common.h"
27 #include "custom_float.h"
28 #include "reg_helper.h"
29
30 #define CTX_BASE dpp
31 #define CTX vpe10_dpp
32
cm_helper_convert_to_custom_float(struct pwl_result_data * rgb_resulted,struct curve_points3 * corner_points,uint32_t hw_points_num,bool fixpoint)33 static bool cm_helper_convert_to_custom_float(struct pwl_result_data *rgb_resulted,
34 struct curve_points3 *corner_points, uint32_t hw_points_num, bool fixpoint)
35 {
36 struct custom_float_format fmt = {0};
37
38 struct pwl_result_data *rgb = rgb_resulted;
39
40 uint32_t i = 0;
41
42 fmt.exponenta_bits = 6;
43 fmt.mantissa_bits = 12;
44 fmt.sign = false;
45
46 /* corner_points[0] - beginning base, slope offset for R,G,B
47 * corner_points[1] - end base, slope offset for R,G,B
48 */
49 if (!vpe_convert_to_custom_float_format(
50 corner_points[0].red.x, &fmt, &corner_points[0].red.custom_float_x)) {
51 VPE_ASSERT(0);
52 return false;
53 }
54 if (!vpe_convert_to_custom_float_format(
55 corner_points[0].green.x, &fmt, &corner_points[0].green.custom_float_x)) {
56 VPE_ASSERT(0);
57 return false;
58 }
59 if (!vpe_convert_to_custom_float_format(
60 corner_points[0].blue.x, &fmt, &corner_points[0].blue.custom_float_x)) {
61 VPE_ASSERT(0);
62 return false;
63 }
64 if (!vpe_convert_to_custom_float_format(
65 corner_points[0].red.y, &fmt, &corner_points[0].red.custom_float_y)) {
66 VPE_ASSERT(0);
67 return false;
68 }
69 if (!vpe_convert_to_custom_float_format(
70 corner_points[0].green.y, &fmt, &corner_points[0].green.custom_float_y)) {
71 VPE_ASSERT(0);
72 return false;
73 }
74 if (!vpe_convert_to_custom_float_format(
75 corner_points[0].blue.y, &fmt, &corner_points[0].blue.custom_float_y)) {
76 VPE_ASSERT(0);
77 return false;
78 }
79 if (!vpe_convert_to_custom_float_format(
80 corner_points[0].red.slope, &fmt, &corner_points[0].red.custom_float_slope)) {
81 VPE_ASSERT(0);
82 return false;
83 }
84 if (!vpe_convert_to_custom_float_format(
85 corner_points[0].green.slope, &fmt, &corner_points[0].green.custom_float_slope)) {
86 VPE_ASSERT(0);
87 return false;
88 }
89 if (!vpe_convert_to_custom_float_format(
90 corner_points[0].blue.slope, &fmt, &corner_points[0].blue.custom_float_slope)) {
91 VPE_ASSERT(0);
92 return false;
93 }
94
95 if (fixpoint == true) {
96 corner_points[1].red.custom_float_y = vpe_fixpt_clamp_u0d14(corner_points[1].red.y);
97 corner_points[1].green.custom_float_y = vpe_fixpt_clamp_u0d14(corner_points[1].green.y);
98 corner_points[1].blue.custom_float_y = vpe_fixpt_clamp_u0d14(corner_points[1].blue.y);
99 } else {
100 if (!vpe_convert_to_custom_float_format(
101 corner_points[1].red.y, &fmt, &corner_points[1].red.custom_float_y)) {
102 VPE_ASSERT(0);
103 return false;
104 }
105 if (!vpe_convert_to_custom_float_format(
106 corner_points[1].green.y, &fmt, &corner_points[1].green.custom_float_y)) {
107 VPE_ASSERT(0);
108 return false;
109 }
110 if (!vpe_convert_to_custom_float_format(
111 corner_points[1].blue.y, &fmt, &corner_points[1].blue.custom_float_y)) {
112 VPE_ASSERT(0);
113 return false;
114 }
115 }
116
117 fmt.mantissa_bits = 10;
118 fmt.sign = false;
119
120 if (!vpe_convert_to_custom_float_format(
121 corner_points[1].red.x, &fmt, &corner_points[1].red.custom_float_x)) {
122 VPE_ASSERT(0);
123 return false;
124 }
125 if (!vpe_convert_to_custom_float_format(
126 corner_points[1].green.x, &fmt, &corner_points[1].green.custom_float_x)) {
127 VPE_ASSERT(0);
128 return false;
129 }
130 if (!vpe_convert_to_custom_float_format(
131 corner_points[1].blue.x, &fmt, &corner_points[1].blue.custom_float_x)) {
132 VPE_ASSERT(0);
133 return false;
134 }
135
136 if (!vpe_convert_to_custom_float_format(
137 corner_points[1].red.slope, &fmt, &corner_points[1].red.custom_float_slope)) {
138 VPE_ASSERT(0);
139 return false;
140 }
141 if (!vpe_convert_to_custom_float_format(
142 corner_points[1].green.slope, &fmt, &corner_points[1].green.custom_float_slope)) {
143 VPE_ASSERT(0);
144 return false;
145 }
146 if (!vpe_convert_to_custom_float_format(
147 corner_points[1].blue.slope, &fmt, &corner_points[1].blue.custom_float_slope)) {
148 VPE_ASSERT(0);
149 return false;
150 }
151
152 if (hw_points_num == 0 || rgb_resulted == NULL || fixpoint == true)
153 return true;
154
155 fmt.mantissa_bits = 12;
156
157 while (i != hw_points_num) {
158 if (!vpe_convert_to_custom_float_format(rgb->red, &fmt, &rgb->red_reg)) {
159 VPE_ASSERT(0);
160 return false;
161 }
162
163 if (!vpe_convert_to_custom_float_format(rgb->green, &fmt, &rgb->green_reg)) {
164 VPE_ASSERT(0);
165 return false;
166 }
167
168 if (!vpe_convert_to_custom_float_format(rgb->blue, &fmt, &rgb->blue_reg)) {
169 VPE_ASSERT(0);
170 return false;
171 }
172
173 if (!vpe_convert_to_custom_float_format(rgb->delta_red, &fmt, &rgb->delta_red_reg)) {
174 VPE_ASSERT(0);
175 return false;
176 }
177
178 if (!vpe_convert_to_custom_float_format(rgb->delta_green, &fmt, &rgb->delta_green_reg)) {
179 VPE_ASSERT(0);
180 return false;
181 }
182
183 if (!vpe_convert_to_custom_float_format(rgb->delta_blue, &fmt, &rgb->delta_blue_reg)) {
184 VPE_ASSERT(0);
185 return false;
186 }
187
188 ++rgb;
189 ++i;
190 }
191
192 fmt.exponenta_bits = 6;
193 fmt.mantissa_bits = 12;
194 fmt.sign = 1;
195
196 if (!vpe_convert_to_custom_float_format(
197 corner_points[0].red.offset, &fmt, &corner_points[0].red.custom_float_offset)) {
198 VPE_ASSERT(0);
199 return false;
200 }
201
202 if (!vpe_convert_to_custom_float_format(
203 corner_points[0].green.offset, &fmt, &corner_points[0].green.custom_float_offset)) {
204 VPE_ASSERT(0);
205 return false;
206 }
207
208 if (!vpe_convert_to_custom_float_format(
209 corner_points[0].blue.offset, &fmt, &corner_points[0].blue.custom_float_offset)) {
210 VPE_ASSERT(0);
211 return false;
212 }
213 return true;
214 }
215
216 /* driver uses 32 regions or less, but DCN HW has 34, extra 2 are set to 0 */
217 #define MAX_REGIONS_NUMBER 34
218 #define MAX_LOW_POINT 25
219 #define NUMBER_REGIONS 32
220 #define NUMBER_SW_SEGMENTS 16
221
vpe10_cm_helper_translate_curve_to_hw_format(const struct transfer_func * output_tf,struct pwl_params * lut_params,bool fixpoint)222 bool vpe10_cm_helper_translate_curve_to_hw_format(
223 const struct transfer_func *output_tf, struct pwl_params *lut_params, bool fixpoint)
224 {
225 struct curve_points3 *corner_points;
226 struct pwl_result_data *rgb_resulted;
227 struct pwl_result_data *rgb;
228 struct pwl_result_data *rgb_plus_1;
229 struct pwl_result_data *rgb_minus_1;
230
231 int32_t region_start, region_end;
232 int32_t i;
233 uint32_t j, k, seg_distr[MAX_REGIONS_NUMBER], increment, start_index, hw_points;
234
235 if (output_tf == NULL || lut_params == NULL || output_tf->type == TF_TYPE_BYPASS)
236 return false;
237
238 corner_points = lut_params->corner_points;
239 rgb_resulted = lut_params->rgb_resulted;
240 hw_points = 0;
241
242 memset(lut_params, 0, sizeof(struct pwl_params));
243 memset(seg_distr, 0, sizeof(seg_distr));
244
245 if (output_tf->tf == TRANSFER_FUNC_PQ2084) {
246
247 for (i = 0; i < MAX_LOW_POINT; i++)
248 seg_distr[i] = 3;
249
250 // Extra magic point to account for incorrect programming of the lut
251 seg_distr[i] = 1;
252 region_start = -MAX_LOW_POINT;
253 region_end = 1;
254 } else if (output_tf->tf == TRANSFER_FUNC_LINEAR_0_125) {
255
256 int num_regions_linear = MAX_LOW_POINT + 3;
257
258 for (i = 0; i < num_regions_linear; i++)
259 seg_distr[i] = 3;
260
261 region_start = -MAX_LOW_POINT;
262 region_end = 3;
263 } else {
264 seg_distr[0] = 3;
265 seg_distr[1] = 4;
266 seg_distr[2] = 4;
267 seg_distr[3] = 4;
268 seg_distr[4] = 4;
269 seg_distr[5] = 4;
270 seg_distr[6] = 4;
271 seg_distr[7] = 4;
272 seg_distr[8] = 4;
273 seg_distr[9] = 4;
274 seg_distr[10] = 4;
275 seg_distr[11] = 4;
276 seg_distr[12] = 1;
277
278 region_start = -12;
279 region_end = 1;
280 }
281
282 for (i = region_end - region_start; i < MAX_REGIONS_NUMBER; i++)
283 seg_distr[i] = (uint32_t)-1;
284
285 for (k = 0; k < MAX_REGIONS_NUMBER; k++) {
286 if (seg_distr[k] != (uint32_t)-1)
287 hw_points += (1 << seg_distr[k]);
288 }
289
290 j = 0;
291 for (k = 0; k < (uint32_t)(region_end - region_start); k++) {
292 increment = NUMBER_SW_SEGMENTS / (1 << seg_distr[k]);
293 start_index = ((uint32_t)region_start + k + MAX_LOW_POINT) * NUMBER_SW_SEGMENTS;
294 for (i = (int32_t)start_index; i < (int32_t)start_index + NUMBER_SW_SEGMENTS;
295 i += increment) {
296 if (j == hw_points - 1)
297 break;
298 rgb_resulted[j].red = output_tf->tf_pts.red[i];
299 rgb_resulted[j].green = output_tf->tf_pts.green[i];
300 rgb_resulted[j].blue = output_tf->tf_pts.blue[i];
301 j++;
302 }
303 }
304
305 /* last point */
306 start_index = (uint32_t)((region_end + MAX_LOW_POINT) * NUMBER_SW_SEGMENTS);
307 rgb_resulted[hw_points - 1].red = output_tf->tf_pts.red[start_index];
308 rgb_resulted[hw_points - 1].green = output_tf->tf_pts.green[start_index];
309 rgb_resulted[hw_points - 1].blue = output_tf->tf_pts.blue[start_index];
310
311 rgb_resulted[hw_points].red = rgb_resulted[hw_points - 1].red;
312 rgb_resulted[hw_points].green = rgb_resulted[hw_points - 1].green;
313 rgb_resulted[hw_points].blue = rgb_resulted[hw_points - 1].blue;
314
315 // All 3 color channels have same x
316 corner_points[0].red.x = vpe_fixpt_pow(vpe_fixpt_from_int(2), vpe_fixpt_from_int(region_start));
317 corner_points[0].green.x = corner_points[0].red.x;
318 corner_points[0].blue.x = corner_points[0].red.x;
319
320 corner_points[1].red.x = vpe_fixpt_pow(vpe_fixpt_from_int(2), vpe_fixpt_from_int(region_end));
321 corner_points[1].green.x = corner_points[1].red.x;
322 corner_points[1].blue.x = corner_points[1].red.x;
323
324 corner_points[0].red.y = rgb_resulted[0].red;
325 corner_points[0].green.y = rgb_resulted[0].green;
326 corner_points[0].blue.y = rgb_resulted[0].blue;
327
328 corner_points[0].red.slope = vpe_fixpt_div(corner_points[0].red.y, corner_points[0].red.x);
329 corner_points[0].green.slope =
330 vpe_fixpt_div(corner_points[0].green.y, corner_points[0].green.x);
331 corner_points[0].blue.slope = vpe_fixpt_div(corner_points[0].blue.y, corner_points[0].blue.x);
332
333 /* see comment above, m_arrPoints[1].y should be the Y value for the
334 * region end (m_numOfHwPoints), not last HW point(m_numOfHwPoints - 1)
335 */
336 corner_points[1].red.y = rgb_resulted[hw_points - 1].red;
337 corner_points[1].green.y = rgb_resulted[hw_points - 1].green;
338 corner_points[1].blue.y = rgb_resulted[hw_points - 1].blue;
339 corner_points[1].red.slope = vpe_fixpt_zero;
340 corner_points[1].green.slope = vpe_fixpt_zero;
341 corner_points[1].blue.slope = vpe_fixpt_zero;
342
343 lut_params->hw_points_num = hw_points;
344
345 k = 0;
346 for (i = 1; i < MAX_REGIONS_NUMBER; i++) {
347 if (seg_distr[k] != (uint32_t)-1) {
348 lut_params->arr_curve_points[k].segments_num = seg_distr[k];
349 lut_params->arr_curve_points[i].offset =
350 lut_params->arr_curve_points[k].offset + (1 << seg_distr[k]);
351 }
352 k++;
353 }
354
355 if (seg_distr[k] != (uint32_t)-1)
356 lut_params->arr_curve_points[k].segments_num = seg_distr[k];
357
358 rgb = rgb_resulted;
359 rgb_plus_1 = rgb_resulted + 1;
360 rgb_minus_1 = rgb;
361
362 i = 1;
363 while (i != (int32_t)(hw_points + 1)) {
364 if (i >= (int32_t)(hw_points - 1)) {
365 if (vpe_fixpt_lt(rgb_plus_1->red, rgb->red))
366 rgb_plus_1->red = vpe_fixpt_add(rgb->red, rgb_minus_1->delta_red);
367 if (vpe_fixpt_lt(rgb_plus_1->green, rgb->green))
368 rgb_plus_1->green = vpe_fixpt_add(rgb->green, rgb_minus_1->delta_green);
369 if (vpe_fixpt_lt(rgb_plus_1->blue, rgb->blue))
370 rgb_plus_1->blue = vpe_fixpt_add(rgb->blue, rgb_minus_1->delta_blue);
371 }
372
373 rgb->delta_red = vpe_fixpt_sub(rgb_plus_1->red, rgb->red);
374 rgb->delta_green = vpe_fixpt_sub(rgb_plus_1->green, rgb->green);
375 rgb->delta_blue = vpe_fixpt_sub(rgb_plus_1->blue, rgb->blue);
376
377 if (fixpoint == true) {
378 rgb->delta_red_reg = vpe_fixpt_clamp_u0d10(rgb->delta_red);
379 rgb->delta_green_reg = vpe_fixpt_clamp_u0d10(rgb->delta_green);
380 rgb->delta_blue_reg = vpe_fixpt_clamp_u0d10(rgb->delta_blue);
381 rgb->red_reg = vpe_fixpt_clamp_u0d14(rgb->red);
382 rgb->green_reg = vpe_fixpt_clamp_u0d14(rgb->green);
383 rgb->blue_reg = vpe_fixpt_clamp_u0d14(rgb->blue);
384 }
385
386 ++rgb_plus_1;
387 rgb_minus_1 = rgb;
388 ++rgb;
389 ++i;
390 }
391
392 corner_points[0].red.y = vpe_fixpt_zero;
393 corner_points[0].green.y = vpe_fixpt_zero;
394 corner_points[0].blue.y = vpe_fixpt_zero;
395 corner_points[0].red.offset = output_tf->start_base;
396 corner_points[0].green.offset = output_tf->start_base;
397 corner_points[0].blue.offset = output_tf->start_base;
398
399 cm_helper_convert_to_custom_float(rgb_resulted, lut_params->corner_points, hw_points, fixpoint);
400
401 return true;
402 }
403
404 #define NUM_DEGAMMA_REGIONS 9
405 #define MAX_REGIONS_NUMBER_DEGAMMA 16
406 #define MAX_HW_POINTS_DEGAMMA 257
407
vpe10_cm_helper_translate_curve_to_degamma_hw_format(const struct transfer_func * output_tf,struct pwl_params * lut_params)408 bool vpe10_cm_helper_translate_curve_to_degamma_hw_format(
409 const struct transfer_func *output_tf, struct pwl_params *lut_params)
410 {
411 struct curve_points3 *corner_points;
412 struct pwl_result_data *rgb_resulted;
413 struct pwl_result_data *rgb;
414 struct pwl_result_data *rgb_plus_1;
415
416 int32_t region_start, region_end;
417 int32_t i;
418 uint32_t k, seg_distr[MAX_REGIONS_NUMBER_DEGAMMA], num_segments, hw_points;
419
420 if (output_tf == NULL || lut_params == NULL || output_tf->type == TF_TYPE_BYPASS)
421 return false;
422
423 corner_points = lut_params->corner_points;
424 rgb_resulted = lut_params->rgb_resulted;
425 num_segments = 0;
426
427 memset(lut_params, 0, sizeof(struct pwl_params));
428 memset(seg_distr, 0, sizeof(seg_distr));
429
430 region_start = -NUM_DEGAMMA_REGIONS;
431 region_end = 0;
432
433 for (i = 0; i < MAX_HW_POINTS_DEGAMMA; i++) {
434 rgb_resulted[i].red = output_tf->tf_pts.red[i];
435 rgb_resulted[i].green = output_tf->tf_pts.green[i];
436 rgb_resulted[i].blue = output_tf->tf_pts.blue[i];
437 }
438
439 for (k = (uint32_t)(region_end - region_start); k < MAX_REGIONS_NUMBER_DEGAMMA; k++)
440 seg_distr[k] = (uint32_t)-1;
441
442 /* 9 segments
443 * segments are from 2^-8 to 0
444 */
445 seg_distr[0] = 0; /* Since we only have one point in last region */
446 num_segments += 1;
447
448 for (k = 1; k < NUM_DEGAMMA_REGIONS; k++) {
449 seg_distr[k] = k - 1; /* Depends upon the regions' points 2^n; seg_distr = n */
450 num_segments += (1 << seg_distr[k]);
451 }
452 hw_points = num_segments + 1;
453
454 corner_points[0].red.x = vpe_fixpt_pow(vpe_fixpt_from_int(2), vpe_fixpt_from_int(region_start));
455 corner_points[0].green.x = corner_points[0].red.x;
456 corner_points[0].blue.x = corner_points[0].red.x;
457 corner_points[0].red.y = rgb_resulted[0].red;
458 corner_points[0].green.y = rgb_resulted[0].green;
459 corner_points[0].blue.y = rgb_resulted[0].blue;
460 corner_points[0].red.slope = vpe_fixpt_div(corner_points[0].red.y, corner_points[0].red.x);
461 corner_points[0].green.slope = corner_points[0].red.slope;
462 corner_points[0].blue.slope = corner_points[0].red.slope;
463
464 corner_points[1].red.x = vpe_fixpt_pow(vpe_fixpt_from_int(2), vpe_fixpt_from_int(region_end));
465 corner_points[1].green.x = corner_points[1].red.x;
466 corner_points[1].blue.x = corner_points[1].red.x;
467
468 corner_points[1].red.y = rgb_resulted[num_segments].red;
469 corner_points[1].green.y = rgb_resulted[num_segments].green;
470 corner_points[1].blue.y = rgb_resulted[num_segments].blue;
471 corner_points[1].red.slope = vpe_fixpt_zero;
472 corner_points[1].green.slope = vpe_fixpt_zero;
473 corner_points[1].blue.slope = vpe_fixpt_zero;
474
475 // The number of HW points is equal to num_segments+1, however due to bug in lower layer, it
476 // must be set to num_segments
477 lut_params->hw_points_num = num_segments;
478
479 lut_params->arr_curve_points[0].segments_num = seg_distr[0];
480 for (i = 1; i < NUM_DEGAMMA_REGIONS; i++) {
481 lut_params->arr_curve_points[i].segments_num = seg_distr[i];
482 lut_params->arr_curve_points[i].offset =
483 lut_params->arr_curve_points[i - 1].offset + (1 << seg_distr[i - 1]);
484 }
485
486 if (seg_distr[i] != (uint32_t)-1)
487 lut_params->arr_curve_points[k].segments_num = seg_distr[k];
488
489 rgb = rgb_resulted;
490 rgb_plus_1 = rgb_resulted + 1;
491
492 i = 1;
493 while (i != (int32_t)(hw_points)) {
494 if (vpe_fixpt_lt(rgb_plus_1->red, rgb->red))
495 rgb_plus_1->red = rgb->red;
496 if (vpe_fixpt_lt(rgb_plus_1->green, rgb->green))
497 rgb_plus_1->green = rgb->green;
498 if (vpe_fixpt_lt(rgb_plus_1->blue, rgb->blue))
499 rgb_plus_1->blue = rgb->blue;
500
501 rgb->delta_red = vpe_fixpt_sub(rgb_plus_1->red, rgb->red);
502 rgb->delta_green = vpe_fixpt_sub(rgb_plus_1->green, rgb->green);
503 rgb->delta_blue = vpe_fixpt_sub(rgb_plus_1->blue, rgb->blue);
504
505 ++rgb_plus_1;
506 ++rgb;
507 ++i;
508 }
509
510 corner_points[0].red.y = vpe_fixpt_zero;
511 corner_points[0].green.y = vpe_fixpt_zero;
512 corner_points[0].blue.y = vpe_fixpt_zero;
513 corner_points[0].red.offset = output_tf->start_base;
514 corner_points[0].green.offset = output_tf->start_base;
515 corner_points[0].blue.offset = output_tf->start_base;
516
517 cm_helper_convert_to_custom_float(rgb_resulted, lut_params->corner_points, hw_points, false);
518
519 return true;
520 }
521
vpe10_cm_get_tf_pwl_params(const struct transfer_func * output_tf,struct pwl_params ** lut_params,enum cm_type vpe_cm_type)522 void vpe10_cm_get_tf_pwl_params(
523 const struct transfer_func *output_tf, struct pwl_params **lut_params, enum cm_type vpe_cm_type)
524 {
525 int table_index = 0;
526
527 switch (output_tf->tf) {
528 case TRANSFER_FUNC_SRGB:
529 table_index = 0;
530 break;
531 case TRANSFER_FUNC_BT1886:
532 table_index = 1;
533 break;
534 case TRANSFER_FUNC_PQ2084:
535 table_index = 2;
536 break;
537 case TRANSFER_FUNC_BT709:
538 case TRANSFER_FUNC_LINEAR_0_125:
539 table_index = 3;
540 break;
541 default:
542 *lut_params = NULL;
543 return;
544 }
545 *lut_params = &tf_pwl_param_table[vpe_cm_type][table_index];
546 }
547
548 #define REG_FIELD_VALUE_CM(field, value) \
549 ((uint32_t)((value) << reg->shifts.field) & reg->masks.field)
550 #define REG_FIELD_MASK_CM(field) reg->masks.field
551
552 #define REG_SET_CM(reg_offset, init_val, field, val) \
553 do { \
554 config_writer_fill( \
555 config_writer, VPEC_FIELD_VALUE(VPE_DIR_CFG_PKT_DATA_SIZE, 0) | \
556 VPEC_FIELD_VALUE(VPE_DIR_CFG_PKT_REGISTER_OFFSET, reg_offset)); \
557 config_writer_fill(config_writer, \
558 ((init_val & ~(REG_FIELD_MASK_CM(field))) | REG_FIELD_VALUE_CM(field, val))); \
559 } while (0)
560
561 #define REG_SET_2_CM(reg_offset, init_val, f1, v1, f2, v2) \
562 do { \
563 config_writer_fill( \
564 config_writer, VPEC_FIELD_VALUE(VPE_DIR_CFG_PKT_DATA_SIZE, 0) | \
565 VPEC_FIELD_VALUE(VPE_DIR_CFG_PKT_REGISTER_OFFSET, reg_offset)); \
566 config_writer_fill( \
567 config_writer, ((init_val & ~(REG_FIELD_MASK_CM(f1)) & ~(REG_FIELD_MASK_CM(f2))) | \
568 REG_FIELD_VALUE_CM(f1, v1) | REG_FIELD_VALUE_CM(f2, v2))); \
569 } while (0)
570
571 #define REG_SET_4_CM(reg_offset, init_val, f1, v1, f2, v2, f3, v3, f4, v4) \
572 do { \
573 config_writer_fill( \
574 config_writer, VPEC_FIELD_VALUE(VPE_DIR_CFG_PKT_DATA_SIZE, 0) | \
575 VPEC_FIELD_VALUE(VPE_DIR_CFG_PKT_REGISTER_OFFSET, reg_offset)); \
576 config_writer_fill( \
577 config_writer, ((init_val & ~(REG_FIELD_MASK_CM(f1)) & ~(REG_FIELD_MASK_CM(f2)) & \
578 ~(REG_FIELD_MASK_CM(f3)) & ~(REG_FIELD_MASK_CM(f4))) | \
579 REG_FIELD_VALUE_CM(f1, v1) | REG_FIELD_VALUE_CM(f2, v2) | \
580 REG_FIELD_VALUE_CM(f3, v3) | REG_FIELD_VALUE_CM(f4, v4))); \
581 } while (0)
582
vpe10_cm_helper_program_gamcor_xfer_func(struct config_writer * config_writer,const struct pwl_params * params,const struct vpe10_xfer_func_reg * reg)583 void vpe10_cm_helper_program_gamcor_xfer_func(struct config_writer *config_writer,
584 const struct pwl_params *params, const struct vpe10_xfer_func_reg *reg)
585 {
586 // Total: 13 * 4 + (region_end - region_start + 4) = 13*4 + 68 = 120 bytes
587 uint32_t reg_region_cur;
588 unsigned int i = 0;
589 uint16_t packet_data_size = (uint16_t)((reg->region_end - reg->region_start + 1));
590
591 REG_SET_2_CM(reg->start_cntl_b, 0, exp_region_start,
592 params->corner_points[0].blue.custom_float_x, exp_region_start_segment, 0);
593 REG_SET_2_CM(reg->start_cntl_g, 0, exp_region_start,
594 params->corner_points[0].green.custom_float_x, exp_region_start_segment, 0);
595 REG_SET_2_CM(reg->start_cntl_r, 0, exp_region_start,
596 params->corner_points[0].red.custom_float_x, exp_region_start_segment, 0);
597
598 REG_SET_CM(reg->start_base_cntl_r, 0, field_region_start_base,
599 params->corner_points[0].red.custom_float_y);
600 REG_SET_CM(reg->start_base_cntl_g, 0, field_region_start_base,
601 params->corner_points[0].green.custom_float_y);
602 REG_SET_CM(reg->start_base_cntl_b, 0, field_region_start_base,
603 params->corner_points[0].blue.custom_float_y);
604
605 REG_SET_CM(reg->offset_r, 0, field_offset,
606 params->corner_points[0].red.custom_float_offset);
607 REG_SET_CM(reg->offset_g, 0, field_offset,
608 params->corner_points[0].green.custom_float_offset);
609 REG_SET_CM(reg->offset_b, 0, field_offset,
610 params->corner_points[0].blue.custom_float_offset);
611
612 REG_SET_CM(reg->start_slope_cntl_b, 0, // linear slope at start of curve
613 field_region_linear_slope, params->corner_points[0].blue.custom_float_slope);
614 REG_SET_CM(reg->start_slope_cntl_g, 0, field_region_linear_slope,
615 params->corner_points[0].green.custom_float_slope);
616 REG_SET_CM(reg->start_slope_cntl_r, 0, field_region_linear_slope,
617 params->corner_points[0].red.custom_float_slope);
618
619 REG_SET_CM(reg->start_end_cntl1_b, 0, field_region_end_base,
620 params->corner_points[1].blue.custom_float_y);
621 REG_SET_CM(reg->start_end_cntl1_g, 0, field_region_end_base,
622 params->corner_points[1].green.custom_float_y);
623 REG_SET_CM(reg->start_end_cntl1_r, 0, field_region_end_base,
624 params->corner_points[1].red.custom_float_y);
625
626 REG_SET_2_CM(reg->start_end_cntl2_b, 0, field_region_end_slope,
627 params->corner_points[1].blue.custom_float_slope, field_region_end,
628 params->corner_points[1].blue.custom_float_x);
629 REG_SET_2_CM(reg->start_end_cntl2_g, 0, field_region_end_slope,
630 params->corner_points[1].green.custom_float_slope, field_region_end,
631 params->corner_points[1].green.custom_float_x);
632 REG_SET_2_CM(reg->start_end_cntl2_r, 0, field_region_end_slope,
633 params->corner_points[1].red.custom_float_slope, field_region_end,
634 params->corner_points[1].red.custom_float_x);
635
636 // program all the *GAM_RAM?_REGION_start ~ region_end regs in one VPEP_DIRECT_CONFIG packet
637 // with auto inc
638 config_writer_fill(
639 config_writer, VPEC_FIELD_VALUE(VPE_DIR_CFG_PKT_DATA_SIZE, packet_data_size - 1) |
640 VPEC_FIELD_VALUE(VPE_DIR_CFG_PKT_REGISTER_OFFSET, reg->region_start) |
641 0x01); // auto increase on
642
643 for (reg_region_cur = reg->region_start; reg_region_cur <= reg->region_end; reg_region_cur++) {
644
645 const struct gamma_curve *curve0 = &(params->arr_curve_points[2 * i]);
646 const struct gamma_curve *curve1 = &(params->arr_curve_points[(2 * i) + 1]);
647
648 config_writer_fill(
649 config_writer, (((curve0->offset << reg->shifts.exp_region0_lut_offset) &
650 reg->masks.exp_region0_lut_offset) |
651 ((curve0->segments_num << reg->shifts.exp_region0_num_segments) &
652 reg->masks.exp_region0_num_segments) |
653 ((curve1->offset << reg->shifts.exp_region1_lut_offset) &
654 reg->masks.exp_region1_lut_offset) |
655 ((curve1->segments_num << reg->shifts.exp_region1_num_segments) &
656 reg->masks.exp_region1_num_segments)));
657
658 i++;
659 }
660 }
661
vpe10_cm_helper_program_pwl(struct config_writer * config_writer,const struct pwl_result_data * rgb,uint32_t last_base_value,uint32_t num,uint32_t lut_data_reg_offset,uint8_t lut_data_reg_shift,uint32_t lut_data_reg_mask,enum cm_rgb_channel channel)662 void vpe10_cm_helper_program_pwl(struct config_writer *config_writer,
663 const struct pwl_result_data *rgb, uint32_t last_base_value, uint32_t num,
664 uint32_t lut_data_reg_offset, uint8_t lut_data_reg_shift, uint32_t lut_data_reg_mask,
665 enum cm_rgb_channel channel)
666 {
667 uint32_t i;
668 uint32_t lut_data = 0;
669
670 // For LUT, we keep write the same address with entire LUT data, so don't set INC bit
671 config_writer_fill(
672 config_writer, VPEC_FIELD_VALUE(VPE_DIR_CFG_PKT_DATA_SIZE, num) |
673 VPEC_FIELD_VALUE(VPE_DIR_CFG_PKT_REGISTER_OFFSET, lut_data_reg_offset));
674
675 for (i = 0; i < num; i++) {
676 switch (channel) {
677 case CM_PWL_R:
678 lut_data = rgb[i].red_reg;
679 break;
680 case CM_PWL_G:
681 lut_data = rgb[i].green_reg;
682 break;
683 case CM_PWL_B:
684 lut_data = rgb[i].blue_reg;
685 break;
686 }
687 config_writer_fill(config_writer, ((lut_data << lut_data_reg_shift) & lut_data_reg_mask));
688 }
689
690 config_writer_fill(
691 config_writer, ((last_base_value << lut_data_reg_shift) & lut_data_reg_mask));
692 }
693
vpe10_cm_helper_program_color_matrices(struct config_writer * config_writer,const uint16_t * regval,const struct color_matrices_reg * reg)694 void vpe10_cm_helper_program_color_matrices(struct config_writer *config_writer,
695 const uint16_t *regval, const struct color_matrices_reg *reg)
696 {
697 uint32_t cur_csc_reg;
698 unsigned int i = 0;
699 uint16_t packet_data_size = (uint16_t)((reg->csc_c33_c34 - reg->csc_c11_c12 + 1));
700
701 config_writer_fill(
702 config_writer, VPEC_FIELD_VALUE(VPE_DIR_CFG_PKT_DATA_SIZE, packet_data_size - 1) |
703 VPEC_FIELD_VALUE(VPE_DIR_CFG_PKT_REGISTER_OFFSET, reg->csc_c11_c12) |
704 0x01); // auto increase on
705
706 for (cur_csc_reg = reg->csc_c11_c12; cur_csc_reg <= reg->csc_c33_c34; cur_csc_reg++) {
707
708 const uint16_t *regval0 = &(regval[2 * i]);
709 const uint16_t *regval1 = &(regval[(2 * i) + 1]);
710
711 // use C11/C12 mask value for all CSC regs to ease programing
712 config_writer_fill(
713 config_writer, ((uint32_t)(*regval0 << reg->shifts.csc_c11) & reg->masks.csc_c11) |
714 ((uint32_t)(*regval1 << reg->shifts.csc_c12) & reg->masks.csc_c12));
715
716 // Due to the program nature of CSC regs are switchable to different sets
717 // Skip record REG_IS_WRITTEN and LAST_WRITTEN_VAL used in REG_SET* macros.
718 // and those CSC regs will always write at once for all fields
719
720 i++;
721 }
722 }
723