1 /* Copyright 2022 Advanced Micro Devices, Inc.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a
4 * copy of this software and associated documentation files (the "Software"),
5 * to deal in the Software without restriction, including without limitation
6 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 * and/or sell copies of the Software, and to permit persons to whom the
8 * Software is furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
17 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
18 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
19 * OTHER DEALINGS IN THE SOFTWARE.
20 *
21 * Authors: AMD
22 *
23 */
24
25 #include "vpe_assert.h"
26 #include <string.h>
27 #include "color.h"
28 #include "color_gamma.h"
29 #include "color_cs.h"
30 #include "vpe_priv.h"
31 #include "color_gamut.h"
32 #include "common.h"
33 #include "custom_float.h"
34 #include "3dlut_builder.h"
35 #include "shaper_builder.h"
36 #include "geometric_scaling.h"
37
38 static void color_check_input_cm_update(struct vpe_priv *vpe_priv, struct stream_ctx *stream_ctx,
39 const struct vpe_color_space *vcs, const struct vpe_color_adjust *adjustments,
40 bool enable_3dlut, bool geometric_update);
41
42 static void color_check_output_cm_update(
43 struct vpe_priv *vpe_priv, const struct vpe_color_space *vcs, bool geometric_update);
44
45 static bool color_update_input_cs(struct vpe_priv *vpe_priv, enum color_space in_cs,
46 const struct vpe_color_adjust *adjustments, struct vpe_csc_matrix *input_cs,
47 struct vpe_color_adjust *stream_clr_adjustments, struct fixed31_32 *matrix_scaling_factor);
48
49 static bool is_ycbcr(enum color_space in_cs);
50
get_shaper_norm_factor(struct vpe_tonemap_params * tm_params,struct stream_ctx * stream_ctx,uint32_t * shaper_norm_factor)51 static void get_shaper_norm_factor(struct vpe_tonemap_params *tm_params,
52 struct stream_ctx *stream_ctx, uint32_t *shaper_norm_factor)
53 {
54 if (tm_params->shaper_tf == VPE_TF_PQ_NORMALIZED) {
55 if (tm_params->input_pq_norm_factor == 0) {
56 *shaper_norm_factor = stream_ctx->stream.hdr_metadata.max_mastering;
57 } else {
58 *shaper_norm_factor = tm_params->input_pq_norm_factor;
59 }
60 } else {
61 *shaper_norm_factor = HDR_PEAK_WHITE;
62 }
63 }
64
is_ycbcr(enum color_space in_cs)65 static bool is_ycbcr(enum color_space in_cs)
66 {
67 if ((in_cs == COLOR_SPACE_YCBCR601) || (in_cs == COLOR_SPACE_YCBCR601_LIMITED) ||
68 (in_cs == COLOR_SPACE_YCBCR709) || (in_cs == COLOR_SPACE_YCBCR709_LIMITED) ||
69 (in_cs == COLOR_SPACE_2020_YCBCR) || (in_cs == COLOR_SPACE_2020_YCBCR_LIMITED)) {
70 return true;
71 }
72 return false;
73 }
74
color_check_output_cm_update(struct vpe_priv * vpe_priv,const struct vpe_color_space * vcs,bool geometric_update)75 static void color_check_output_cm_update(
76 struct vpe_priv *vpe_priv, const struct vpe_color_space *vcs, bool geometric_update)
77 {
78 enum color_space cs;
79 enum color_transfer_func tf;
80
81 vpe_color_get_color_space_and_tf(vcs, &cs, &tf);
82
83 if (cs == COLOR_SPACE_UNKNOWN || tf == TRANSFER_FUNC_UNKNOWN)
84 VPE_ASSERT(0);
85
86 if (cs != vpe_priv->output_ctx.cs || geometric_update) {
87 vpe_priv->output_ctx.dirty_bits.color_space = 1;
88 vpe_priv->output_ctx.cs = cs;
89 } else {
90 vpe_priv->output_ctx.dirty_bits.color_space = 0;
91 }
92
93 if (tf != vpe_priv->output_ctx.tf || geometric_update) {
94 vpe_priv->output_ctx.dirty_bits.transfer_function = 1;
95 vpe_priv->output_ctx.tf = tf;
96 } else {
97 vpe_priv->output_ctx.dirty_bits.transfer_function = 0;
98 }
99 }
100
color_check_input_cm_update(struct vpe_priv * vpe_priv,struct stream_ctx * stream_ctx,const struct vpe_color_space * vcs,const struct vpe_color_adjust * adjustments,bool enable_3dlut,bool geometric_update)101 static void color_check_input_cm_update(struct vpe_priv *vpe_priv, struct stream_ctx *stream_ctx,
102 const struct vpe_color_space *vcs, const struct vpe_color_adjust *adjustments,
103 bool enable_3dlut, bool geometric_update)
104 {
105 enum color_space cs;
106 enum color_transfer_func tf;
107
108 vpe_color_get_color_space_and_tf(vcs, &cs, &tf);
109 /*
110 * Bias and Scale already does full->limited range conversion.
111 * Hence, the ICSC matrix should always be full range
112 */
113 vpe_convert_full_range_color_enum(&cs);
114
115 if (cs == COLOR_SPACE_UNKNOWN && tf == TRANSFER_FUNC_UNKNOWN)
116 VPE_ASSERT(0);
117
118 if (cs != stream_ctx->cs || enable_3dlut != stream_ctx->enable_3dlut || geometric_update) {
119 stream_ctx->dirty_bits.color_space = 1;
120 stream_ctx->cs = cs;
121 } else {
122 stream_ctx->dirty_bits.color_space = 0;
123 if (adjustments) {
124 if (vpe_color_different_color_adjusts(
125 adjustments, &stream_ctx->color_adjustments)) // the new stream has different
126 // color adjustments params
127 stream_ctx->dirty_bits.color_space = 1;
128 }
129 }
130 // if the new transfer function is different than the old one or the scaling factor is not one
131 // any new stream will start with a transfer function which is not scaled
132 if (tf != stream_ctx->tf || enable_3dlut != stream_ctx->enable_3dlut || geometric_update) {
133 stream_ctx->dirty_bits.transfer_function = 1;
134 stream_ctx->tf = tf;
135 } else {
136 stream_ctx->dirty_bits.transfer_function = 0;
137 }
138
139 stream_ctx->enable_3dlut = enable_3dlut;
140 }
141
vpe_allocate_cm_memory(struct vpe_priv * vpe_priv,const struct vpe_build_param * param)142 static enum vpe_status vpe_allocate_cm_memory(
143 struct vpe_priv *vpe_priv, const struct vpe_build_param *param)
144 {
145 struct stream_ctx *stream_ctx;
146 struct output_ctx *output_ctx;
147 enum vpe_status status = VPE_STATUS_OK;
148
149 for (uint32_t stream_idx = 0; stream_idx < vpe_priv->num_streams; stream_idx++) {
150 stream_ctx = &vpe_priv->stream_ctx[stream_idx];
151
152 if (!stream_ctx->input_cs) {
153 stream_ctx->input_cs =
154 (struct vpe_csc_matrix *)vpe_zalloc(sizeof(struct vpe_csc_matrix));
155 if (!stream_ctx->input_cs) {
156 vpe_log("err: out of memory for input cs!");
157 return VPE_STATUS_NO_MEMORY;
158 }
159 }
160
161 if (!stream_ctx->input_tf) {
162 stream_ctx->input_tf = (struct transfer_func *)vpe_zalloc(sizeof(struct transfer_func));
163 if (!stream_ctx->input_tf) {
164 vpe_log("err: out of memory for input tf!");
165 return VPE_STATUS_NO_MEMORY;
166 }
167 }
168
169 if (!stream_ctx->bias_scale) {
170 stream_ctx->bias_scale =
171 (struct bias_and_scale *)vpe_zalloc(sizeof(struct bias_and_scale));
172 if (!stream_ctx->bias_scale) {
173 vpe_log("err: out of memory for bias and scale!");
174 return VPE_STATUS_NO_MEMORY;
175 }
176 }
177
178 if (!stream_ctx->gamut_remap) {
179 stream_ctx->gamut_remap = vpe_zalloc(sizeof(struct colorspace_transform));
180 if (!stream_ctx->gamut_remap) {
181 vpe_log("err: out of memory for gamut_remap!");
182 return VPE_STATUS_NO_MEMORY;
183 }
184 }
185 if (!stream_ctx->blend_tf) {
186 stream_ctx->blend_tf = vpe_zalloc(sizeof(struct transfer_func));
187 if (!stream_ctx->blend_tf) {
188 vpe_log("err: out of memory for blend tf!");
189 return VPE_STATUS_NO_MEMORY;
190 }
191 }
192 }
193
194 output_ctx = &vpe_priv->output_ctx;
195 if (!output_ctx->output_tf) {
196 output_ctx->output_tf = (struct transfer_func *)vpe_zalloc(sizeof(struct transfer_func));
197 if (!output_ctx->output_tf) {
198 vpe_log("err: out of memory for output tf!");
199 return VPE_STATUS_NO_MEMORY;
200 }
201 }
202
203 return VPE_STATUS_OK;
204 }
205
color_get_icsc_cs(enum color_space ics)206 static enum color_space color_get_icsc_cs(enum color_space ics)
207 {
208 switch (ics) {
209 case COLOR_SPACE_SRGB:
210 case COLOR_SPACE_SRGB_LIMITED:
211 case COLOR_SPACE_MSREF_SCRGB:
212 case COLOR_SPACE_2020_RGB_FULLRANGE:
213 case COLOR_SPACE_2020_RGB_LIMITEDRANGE:
214 case COLOR_SPACE_RGB601:
215 case COLOR_SPACE_RGB601_LIMITED:
216 case COLOR_SPACE_RGB_JFIF:
217 return COLOR_SPACE_SRGB;
218 case COLOR_SPACE_YCBCR_JFIF:
219 case COLOR_SPACE_YCBCR601:
220 case COLOR_SPACE_YCBCR601_LIMITED:
221 return COLOR_SPACE_YCBCR601;
222 case COLOR_SPACE_YCBCR709:
223 case COLOR_SPACE_YCBCR709_LIMITED:
224 return COLOR_SPACE_YCBCR709;
225 case COLOR_SPACE_2020_YCBCR:
226 case COLOR_SPACE_2020_YCBCR_LIMITED:
227 return COLOR_SPACE_2020_YCBCR;
228 default:
229 return COLOR_SPACE_UNKNOWN;
230 }
231 }
232
233 // return true is bypass can be done
color_update_input_cs(struct vpe_priv * vpe_priv,enum color_space in_cs,const struct vpe_color_adjust * adjustments,struct vpe_csc_matrix * input_cs,struct vpe_color_adjust * stream_clr_adjustments,struct fixed31_32 * matrix_scaling_factor)234 static bool color_update_input_cs(struct vpe_priv *vpe_priv, enum color_space in_cs,
235 const struct vpe_color_adjust *adjustments, struct vpe_csc_matrix *input_cs,
236 struct vpe_color_adjust *stream_clr_adjustments, struct fixed31_32 *matrix_scaling_factor)
237 {
238 int i, j;
239 bool use_adjustments = false;
240 int arr_size = sizeof(vpe_input_csc_matrix_fixed) / sizeof(struct vpe_csc_matrix);
241
242 input_cs->cs = COLOR_SPACE_UNKNOWN;
243 use_adjustments = vpe_use_csc_adjust(adjustments);
244 in_cs = color_get_icsc_cs(in_cs);
245
246 for (i = 0; i < arr_size; i++)
247 if (vpe_input_csc_matrix_fixed[i].cs == in_cs) {
248 input_cs->cs = vpe_input_csc_matrix_fixed[i].cs;
249 for (j = 0; j < 12; j++)
250 input_cs->regval[j] = vpe_input_csc_matrix_fixed[i].regval[j];
251 break;
252 }
253
254 if (i == arr_size) {
255 vpe_log("err: unknown cs not handled!");
256 return false;
257 }
258
259 if (use_adjustments && is_ycbcr(in_cs)) { // shader supports only yuv input for color
260 // adjustments
261 vpe_log("Apply color adjustments (contrast, saturation, hue, brightness)");
262 if (!vpe_color_calculate_input_cs(
263 vpe_priv, in_cs, adjustments, input_cs, matrix_scaling_factor))
264 return false;
265 *stream_clr_adjustments = *adjustments;
266 }
267 else { // no adjustments needed, but we still need to update the stream_clr_adjustments as it is valid
268 *stream_clr_adjustments = *adjustments;
269 }
270
271 return true;
272 }
273
274 /* This function generates software points for the ogam gamma programming block.
275 The logic for the blndgam/ogam programming sequence is a function of:
276 1. Output Range (Studio Full)
277 2. 3DLUT usage
278 3. Output format (HDR SDR)
279
280 SDR Out or studio range out
281 TM Case
282 BLNDGAM : NL -> NL*S + B
283 OGAM : Bypass
284 Non TM Case
285 BLNDGAM : L -> NL*S + B
286 OGAM : Bypass
287 Full range HDR Out
288 TM Case
289 BLNDGAM : NL -> L
290 OGAM : L -> NL
291 Non TM Case
292 BLNDGAM : Bypass
293 OGAM : L -> NL
294
295 */
vpe_update_output_gamma(struct vpe_priv * vpe_priv,const struct vpe_build_param * param,struct transfer_func * output_tf,bool geometric_scaling)296 static enum vpe_status vpe_update_output_gamma(struct vpe_priv *vpe_priv,
297 const struct vpe_build_param *param, struct transfer_func *output_tf, bool geometric_scaling)
298 {
299 bool can_bypass = false;
300 struct output_ctx *output_ctx = &vpe_priv->output_ctx;
301 bool is_studio = (param->dst_surface.cs.range == VPE_COLOR_RANGE_STUDIO);
302 enum vpe_status status = VPE_STATUS_OK;
303 struct fixed31_32 y_scale = vpe_fixpt_one;
304
305 if (vpe_is_fp16(param->dst_surface.format)) {
306 y_scale = vpe_fixpt_mul_int(y_scale, CCCS_NORM);
307 }
308
309 if (!geometric_scaling && vpe_is_HDR(output_ctx->tf) && !is_studio)
310 can_bypass = false; //Blending is done in linear light so ogam needs to handle the regam
311 else
312 can_bypass = true;
313
314 vpe_color_update_regamma_tf(
315 vpe_priv, output_ctx->tf, vpe_fixpt_one, y_scale, vpe_fixpt_zero, can_bypass, output_tf);
316
317 return status;
318 }
319
vpe_use_csc_adjust(const struct vpe_color_adjust * adjustments)320 bool vpe_use_csc_adjust(const struct vpe_color_adjust *adjustments)
321 {
322 float epsilon = 0.001f; // steps are 1.0f or 0.01f, so should be plenty
323
324 // see vpe_types.h and vpe_color_adjust definition for VpBlt ranges
325
326 // default brightness = 0
327 if (adjustments->brightness > epsilon || adjustments->brightness < -epsilon)
328 return true;
329
330 // default contrast = 1
331 if (adjustments->contrast > 1 + epsilon || adjustments->contrast < 1 - epsilon)
332 return true;
333
334 // default saturation = 1
335 if (adjustments->saturation > 1 + epsilon || adjustments->saturation < 1 - epsilon)
336 return true;
337
338 // default hue = 0
339 if (adjustments->hue > epsilon || adjustments->hue < -epsilon)
340 return true;
341
342 return false;
343 }
344
345 /* Bias and Scale reference table
346 Encoding Bpp Format Data Range Expansion Bias Scale
347 aRGB 32bpp 8888 Full Zero 0 256/255
348 8888 Limited Zero -16/256 256/(235-16)
349 2101010 Full Zero 0 1024/1023
350 2101010 Limited Zero -64/1024 1024/(940-64)
351 2101010 XR bias Zero -384/1024 1024/510 // not used
352 64bpp fixed 10bpc Full Zero 0 1024/1023 // do we have these?
353 10 bpc limited zero -64/1024 1024/(940-64)
354 12 bpc Full Zero 0 4096/4095
355 12 bpc Limited Zero -256/4096 4096/(3760-256)
356 aCrYCb 32bpp 8888 Full Zero 0 256/255
357 8888 Limited Zero Y:-16/256 Y:256/(235-16)
358 C:-128/256 C:256/(240-16) // See notes
359 below 2101010 Full Zero 0 1024/1023 2101010 Limited Zero
360 Y:-64/1024 Y:1024/(940-64) C:-512/1024 C:1024(960-64) 64bpp fixed 10bpc Full Zero 0
361 1024/1023 10 bpc Limited Zero Y:-64/1024 Y:1024/(940-64) C:-512/1024
362 C:1024(960-64) // See notes below 12 bpc Full Zero 0 4096/4095 12
363 bpc Limited Zero Y:-256/4096 Y:4096/(3760-256) C:-2048/4096 C:4096/(3840-256) //
364 See notes below
365
366 The bias_c we use here in the function are diff with the above table from hw team
367 because the table is to run with CSC matrix which expect chroma
368 from -0.5~+0.5.
369 However the csc matrix we use in ICSC is expecting chroma value
370 from 0.0~1.0.
371 Hence we need a bias for chroma to output a range from 0.0~1.0 instead.
372 So we use the same value as luma (Y) which expects range from 0~1.0 already.
373 */
build_scale_and_bias(struct bias_and_scale * bias_and_scale,const struct vpe_color_space * vcs,enum vpe_surface_pixel_format format)374 static bool build_scale_and_bias(struct bias_and_scale *bias_and_scale,
375 const struct vpe_color_space *vcs, enum vpe_surface_pixel_format format)
376 {
377 struct fixed31_32 scale = vpe_fixpt_one; // RGB or Y
378 struct fixed31_32 scale_c = vpe_fixpt_one; // Cb/Cr
379 struct fixed31_32 bias = vpe_fixpt_zero; // RGB or Y
380 struct fixed31_32 bias_c = vpe_fixpt_from_fraction(-1, 2); // Cb/Cr
381 bool is_chroma_different = false;
382
383 struct custom_float_format fmt;
384 fmt.exponenta_bits = 6;
385 fmt.mantissa_bits = 12;
386 fmt.sign = true;
387
388 if (vpe_is_rgb8(format)) {
389 if (vcs->range == VPE_COLOR_RANGE_FULL) {
390 scale = vpe_fixpt_from_fraction(256, 255);
391 } else if (vcs->range == VPE_COLOR_RANGE_STUDIO) {
392 scale = vpe_fixpt_from_fraction(256, 235 - 16);
393 bias = vpe_fixpt_from_fraction(-16, 256);
394 } // else report error? here just go with default (1.0, 0.0)
395 } else if (vpe_is_rgb10(format)) {
396 if (vcs->range == VPE_COLOR_RANGE_FULL) {
397 scale = vpe_fixpt_from_fraction(1024, 1023);
398 } else if (vcs->range == VPE_COLOR_RANGE_STUDIO) {
399 scale = vpe_fixpt_from_fraction(1024, 940 - 64);
400 bias = vpe_fixpt_from_fraction(-64, 1024);
401 } // else report error? here just go with default (1.0, 0.0)
402 } else if (vpe_is_yuv8(format)) {
403 if (vcs->range == VPE_COLOR_RANGE_FULL) {
404 scale = vpe_fixpt_from_fraction(256, 255);
405 } else if (vcs->range == VPE_COLOR_RANGE_STUDIO) {
406 scale = vpe_fixpt_from_fraction(256, 235 - 16);
407 bias = vpe_fixpt_from_fraction(-16, 256);
408 scale_c = vpe_fixpt_from_fraction(256, 240 - 16);
409 bias_c = vpe_fixpt_from_fraction(-16, 256); // See notes in function comment
410 is_chroma_different = true;
411 } // else report error? not sure if default is right
412 } else if (vpe_is_yuv10(format)) {
413 if (vcs->range == VPE_COLOR_RANGE_FULL) {
414 scale = vpe_fixpt_from_fraction(1024, 1023);
415 } else if (vcs->range == VPE_COLOR_RANGE_STUDIO) {
416 scale = vpe_fixpt_from_fraction(1024, 940 - 64);
417 bias = vpe_fixpt_from_fraction(-64, 1024);
418 scale_c = vpe_fixpt_from_fraction(1024, 960 - 64);
419 bias_c = vpe_fixpt_from_fraction(-64, 1024); // See notes in function comment
420 is_chroma_different = true;
421 } // else report error? not sure if default is right
422 }
423
424 if (!vpe_convert_to_custom_float_format(scale, &fmt, &bias_and_scale->scale_green)) {
425 VPE_ASSERT(0);
426 }
427 if (!vpe_convert_to_custom_float_format(bias, &fmt, &bias_and_scale->bias_green)) {
428 VPE_ASSERT(0);
429 }
430
431 // see definition of scale/bias and scale_c/bias_c
432 // RGB formats only have scale/bias since all color channels are the same
433 // YCbCr have scale/bias for Y (in HW maps to G) and scale_c/bias_c for CrCb (mapping to R,B)
434 if (!is_chroma_different) {
435 bias_and_scale->scale_red = bias_and_scale->scale_green;
436 bias_and_scale->scale_blue = bias_and_scale->scale_green;
437 bias_and_scale->bias_red = bias_and_scale->bias_green;
438 bias_and_scale->bias_blue = bias_and_scale->bias_green;
439 } else {
440 if (!vpe_convert_to_custom_float_format(scale_c, &fmt, &bias_and_scale->scale_red)) {
441 VPE_ASSERT(0);
442 }
443 if (!vpe_convert_to_custom_float_format(bias_c, &fmt, &bias_and_scale->bias_red)) {
444 VPE_ASSERT(0);
445 }
446 bias_and_scale->scale_blue = bias_and_scale->scale_red;
447 bias_and_scale->bias_blue = bias_and_scale->bias_red;
448 }
449
450 return true;
451 }
452
vpe_color_update_regamma_tf(struct vpe_priv * vpe_priv,enum color_transfer_func output_transfer_function,struct fixed31_32 x_scale,struct fixed31_32 y_scale,struct fixed31_32 y_bias,bool can_bypass,struct transfer_func * output_tf)453 bool vpe_color_update_regamma_tf(struct vpe_priv *vpe_priv,
454 enum color_transfer_func output_transfer_function, struct fixed31_32 x_scale,
455 struct fixed31_32 y_scale, struct fixed31_32 y_bias, bool can_bypass,
456 struct transfer_func *output_tf)
457 {
458 struct pwl_params* params = NULL;
459 bool ret = true;
460 bool update = false;
461
462 if (can_bypass || output_transfer_function == TRANSFER_FUNC_HLG) {
463 output_tf->type = TF_TYPE_BYPASS;
464 return true;
465 }
466
467 output_tf->sdr_ref_white_level = 80;
468 output_tf->cm_gamma_type = CM_REGAM;
469 output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
470 output_tf->start_base = y_bias;
471
472 switch (output_transfer_function) {
473 case TRANSFER_FUNC_SRGB:
474 case TRANSFER_FUNC_BT709:
475 case TRANSFER_FUNC_BT1886:
476 case TRANSFER_FUNC_PQ2084:
477 case TRANSFER_FUNC_LINEAR:
478 output_tf->tf = output_transfer_function;
479 break;
480 default:
481 VPE_ASSERT(0);
482 break;
483 }
484
485 for (uint32_t i = 0; i < vpe_priv->pub.caps->resource_caps.num_dpp; i++) {
486 if (vpe_priv->init.debug.disable_lut_caching ||
487 (output_tf->cache_info[i].cm_gamma_type != output_tf->cm_gamma_type) ||
488 (output_tf->cache_info[i].tf != output_tf->tf) ||
489 (output_tf->cache_info[i].x_scale.value != x_scale.value) ||
490 (output_tf->cache_info[i].y_scale.value != y_scale.value) ||
491 (output_tf->cache_info[i].y_bias.value != y_bias.value)) {
492 // if gamma points have been previously generated,
493 // skip the re-gen no matter it was config cached or not
494 update = true;
495 }
496 }
497
498 if (update) {
499 ret = vpe_color_calculate_regamma_params(
500 vpe_priv, x_scale, y_scale, &vpe_priv->cal_buffer, output_tf);
501 if (ret) {
502 for (uint32_t i = 0; i < vpe_priv->pub.caps->resource_caps.num_dpp; i++) {
503 // reset the cache status and mark as dirty to let hw layer to re-cache
504 output_tf->dirty[i] = true;
505 output_tf->config_cache[i].cached = false;
506 output_tf->cache_info[i].cm_gamma_type = output_tf->cm_gamma_type;
507 output_tf->cache_info[i].tf = output_tf->tf;
508 output_tf->cache_info[i].x_scale = x_scale;
509 output_tf->cache_info[i].y_scale = y_scale;
510 output_tf->cache_info[i].y_bias = y_bias;
511 }
512 }
513 }
514 return ret;
515 }
516
vpe_color_update_degamma_tf(struct vpe_priv * vpe_priv,enum color_transfer_func color_input_tf,struct fixed31_32 x_scale,struct fixed31_32 y_scale,struct fixed31_32 y_bias,bool can_bypass,struct transfer_func * input_tf)517 bool vpe_color_update_degamma_tf(struct vpe_priv *vpe_priv, enum color_transfer_func color_input_tf,
518 struct fixed31_32 x_scale, struct fixed31_32 y_scale, struct fixed31_32 y_bias, bool can_bypass,
519 struct transfer_func *input_tf)
520 {
521 bool ret = true;
522 struct pwl_params* params = NULL;
523 bool update = false;
524
525 if (can_bypass || color_input_tf == TRANSFER_FUNC_HLG) {
526 input_tf->type = TF_TYPE_BYPASS;
527 return true;
528 }
529
530 input_tf->cm_gamma_type = CM_DEGAM;
531 input_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
532 input_tf->start_base = y_bias;
533
534 switch (color_input_tf) {
535 case TRANSFER_FUNC_SRGB:
536 case TRANSFER_FUNC_BT709:
537 case TRANSFER_FUNC_BT1886:
538 case TRANSFER_FUNC_PQ2084:
539 case TRANSFER_FUNC_NORMALIZED_PQ:
540 case TRANSFER_FUNC_LINEAR:
541 input_tf->tf = color_input_tf;
542 break;
543 default:
544 VPE_ASSERT(0);
545 break;
546 }
547
548 for (uint32_t i = 0; i < vpe_priv->pub.caps->resource_caps.num_dpp; i++) {
549 if (vpe_priv->init.debug.disable_lut_caching ||
550 (input_tf->cache_info[i].cm_gamma_type != input_tf->cm_gamma_type) ||
551 (input_tf->cache_info[i].tf != input_tf->tf) ||
552 (input_tf->cache_info[i].x_scale.value != x_scale.value) ||
553 (input_tf->cache_info[i].y_scale.value != y_scale.value) ||
554 (input_tf->cache_info[i].y_bias.value != y_bias.value)) {
555 // if gamma points have been previously generated,
556 // skip the re-gen no matter it was config cached or not
557 update = true;
558 }
559 }
560
561 if (update) {
562 ret = vpe_color_calculate_degamma_params(vpe_priv, x_scale, y_scale, input_tf);
563 if (ret) {
564 for (uint32_t i = 0; i < vpe_priv->pub.caps->resource_caps.num_dpp; i++) {
565 // reset the cache status and mark as dirty to let hw layer to re-cache
566 input_tf->dirty[i] = true;
567 input_tf->config_cache[i].cached = false;
568 input_tf->cache_info[i].cm_gamma_type = input_tf->cm_gamma_type;
569 input_tf->cache_info[i].tf = color_input_tf;
570 input_tf->cache_info[i].x_scale = x_scale;
571 input_tf->cache_info[i].y_scale = y_scale;
572 input_tf->cache_info[i].y_bias = y_bias;
573 }
574 }
575 }
576 return ret;
577 }
578
vpe_color_build_tm_cs(const struct vpe_tonemap_params * tm_params,const struct vpe_surface_info * surface_info,struct vpe_color_space * tm_out_cs)579 enum vpe_status vpe_color_build_tm_cs(const struct vpe_tonemap_params *tm_params,
580 const struct vpe_surface_info *surface_info, struct vpe_color_space *tm_out_cs)
581 {
582 tm_out_cs->tf = tm_params->lut_out_tf;
583 tm_out_cs->primaries = tm_params->lut_out_gamut;
584 tm_out_cs->encoding = surface_info->cs.encoding;
585 tm_out_cs->range = VPE_COLOR_RANGE_FULL; // surface_info.cs.range;
586 tm_out_cs->cositing = VPE_CHROMA_COSITING_NONE; // surface_info.cs.cositing;
587
588 return VPE_STATUS_OK;
589 }
590
vpe_color_build_shaper_cs(const struct vpe_tonemap_params * tm_params,struct vpe_surface_info * surface_info,struct vpe_color_space * tm_out_cs)591 enum vpe_status vpe_color_build_shaper_cs(const struct vpe_tonemap_params *tm_params,
592 struct vpe_surface_info *surface_info, struct vpe_color_space *tm_out_cs)
593 {
594 tm_out_cs->tf = tm_params->shaper_tf;
595 tm_out_cs->primaries = tm_params->lut_in_gamut;
596 tm_out_cs->encoding = VPE_PIXEL_ENCODING_RGB; // surface_info.cs.range;
597 tm_out_cs->range = VPE_COLOR_RANGE_FULL; // surface_info.cs.range;
598 tm_out_cs->cositing = VPE_CHROMA_COSITING_NONE; // surface_info.cs.cositing;
599
600 return VPE_STATUS_OK;
601 }
602
vpe_color_update_3dlut(struct vpe_priv * vpe_priv,struct stream_ctx * stream_ctx,bool enable_3dlut)603 enum vpe_status vpe_color_update_3dlut(
604 struct vpe_priv *vpe_priv, struct stream_ctx *stream_ctx, bool enable_3dlut)
605 {
606 if (!enable_3dlut) {
607 stream_ctx->lut3d_func->state.bits.initialized = 0;
608 } else {
609 bool update = false;
610
611 for (uint32_t i = 0; i < vpe_priv->pub.caps->resource_caps.num_mpc_3dlut; i++)
612 if (vpe_priv->init.debug.disable_lut_caching ||
613 (stream_ctx->lut3d_func->cache_info[i].uid_3dlut !=
614 stream_ctx->stream.tm_params.UID))
615 update = true;
616
617 if (update) {
618 vpe_convert_to_tetrahedral(
619 vpe_priv, stream_ctx->stream.tm_params.lut_data,
620 stream_ctx->stream.tm_params.lut_dim, stream_ctx->lut3d_func);
621 for (uint32_t i = 0; i < vpe_priv->pub.caps->resource_caps.num_mpc_3dlut; i++) {
622 stream_ctx->lut3d_func->dirty[i] = true;
623 stream_ctx->lut3d_func->config_cache[i].cached = false;
624 stream_ctx->lut3d_func->cache_info[i].uid_3dlut = stream_ctx->stream.tm_params.UID;
625 }
626 }
627 stream_ctx->lut3d_func->state.bits.initialized = 1;
628 }
629
630 stream_ctx->uid_3dlut = stream_ctx->stream.tm_params.UID;
631
632 return VPE_STATUS_OK;
633 }
634
vpe_color_update_color_space_and_tf(struct vpe_priv * vpe_priv,const struct vpe_build_param * param)635 enum vpe_status vpe_color_update_color_space_and_tf(
636 struct vpe_priv *vpe_priv, const struct vpe_build_param *param)
637 {
638 uint32_t stream_idx;
639 struct stream_ctx *stream_ctx;
640 struct fixed31_32 new_matrix_scaling_factor;
641 struct output_ctx *output_ctx = &vpe_priv->output_ctx;
642 enum vpe_status status = VPE_STATUS_OK;
643 struct fixed31_32 y_scale = vpe_fixpt_one;
644 bool geometric_update = false;
645 bool geometric_scaling = false;
646
647 status = vpe_allocate_cm_memory(vpe_priv, param);
648 if (status == VPE_STATUS_OK) {
649
650 vpe_update_geometric_scaling(vpe_priv, param, &geometric_update, &geometric_scaling);
651 color_check_output_cm_update(vpe_priv, &vpe_priv->output_ctx.surface.cs, geometric_update);
652
653 for (stream_idx = 0; stream_idx < vpe_priv->num_streams; stream_idx++) {
654
655 new_matrix_scaling_factor = vpe_fixpt_one;
656 stream_ctx = &vpe_priv->stream_ctx[stream_idx];
657 stream_ctx->geometric_scaling = geometric_scaling;
658 // GDS needs to preserve 'is_yuv_input' flag to be used in updating the whitepoint
659 if (!geometric_update && !geometric_scaling) { // Non GDS cases
660 stream_ctx->is_yuv_input =
661 stream_ctx->stream.surface_info.cs.encoding == VPE_PIXEL_ENCODING_YCbCr;
662 }
663 bool is_3dlut_enable =
664 stream_ctx->stream.tm_params.UID != 0 || stream_ctx->stream.tm_params.enable_3dlut;
665 bool require_update = stream_ctx->uid_3dlut != stream_ctx->stream.tm_params.UID;
666
667 color_check_input_cm_update(vpe_priv, stream_ctx,
668 &stream_ctx->stream.surface_info.cs, &stream_ctx->stream.color_adj,
669 is_3dlut_enable, geometric_update);
670
671 build_scale_and_bias(stream_ctx->bias_scale,
672 &stream_ctx->stream.surface_info.cs,
673 stream_ctx->stream.surface_info.format);
674
675 if (stream_ctx->dirty_bits.color_space) {
676 if (!color_update_input_cs(vpe_priv, stream_ctx->cs,
677 &stream_ctx->stream.color_adj, stream_ctx->input_cs,
678 &stream_ctx->color_adjustments, &new_matrix_scaling_factor)) {
679 vpe_log("err: input cs not being programmed!");
680 }
681 else {
682 if ((vpe_priv->scale_yuv_matrix) && // the option to scale the matrix yuv to rgb is
683 // on
684 (new_matrix_scaling_factor.value !=
685 vpe_priv->stream_ctx->tf_scaling_factor.value)) {
686 vpe_priv->stream_ctx->tf_scaling_factor = new_matrix_scaling_factor;
687 stream_ctx->dirty_bits.transfer_function = 1; // force tf recalculation
688 }
689 }
690 }
691
692 if (stream_ctx->dirty_bits.transfer_function) {
693 vpe_color_update_degamma_tf(vpe_priv, stream_ctx->tf,
694 vpe_priv->stream_ctx->tf_scaling_factor, y_scale, vpe_fixpt_zero,
695 is_3dlut_enable || geometric_scaling ||
696 vpe_is_fp16(stream_ctx->stream.surface_info.format),
697 stream_ctx->input_tf);
698 }
699
700 if (stream_ctx->dirty_bits.color_space || output_ctx->dirty_bits.color_space) {
701 enum color_space shaper_in_cs;
702 bool can_bypass_gamut = geometric_scaling;
703 if (is_3dlut_enable) {
704 // Convert vpe_color_space -> color_space
705 struct vpe_color_space shaper_params;
706 enum color_space in_cs_3dlut;
707 enum color_transfer_func tf;
708 vpe_color_build_shaper_cs(&stream_ctx->stream.tm_params,
709 &vpe_priv->output_ctx.surface, &shaper_params);
710 vpe_color_get_color_space_and_tf(&shaper_params, &in_cs_3dlut, &tf);
711 shaper_in_cs = in_cs_3dlut;
712 } else {
713 shaper_in_cs = output_ctx->cs;
714 }
715 status = vpe_color_update_gamut(vpe_priv, stream_ctx->cs, shaper_in_cs,
716 stream_ctx->gamut_remap, can_bypass_gamut);
717 }
718
719 if (output_ctx->dirty_bits.transfer_function || output_ctx->dirty_bits.color_space ||
720 require_update) {
721 vpe_priv->resource.update_blnd_gamma(vpe_priv, param, &stream_ctx->stream, stream_ctx->blend_tf);
722 }
723 }
724
725 if (status == VPE_STATUS_OK) {
726 if (output_ctx->dirty_bits.transfer_function ||
727 output_ctx->dirty_bits.color_space) {
728 vpe_update_output_gamma(vpe_priv, param, output_ctx->output_tf, geometric_scaling);
729 }
730 }
731 }
732 return status;
733 }
734
vpe_color_tm_update_hdr_mult(uint16_t shaper_in_exp_max,uint32_t peak_white,struct fixed31_32 * hdr_multiplier,bool enable3dlut,bool is_fp16)735 enum vpe_status vpe_color_tm_update_hdr_mult(uint16_t shaper_in_exp_max, uint32_t peak_white,
736 struct fixed31_32 *hdr_multiplier, bool enable3dlut, bool is_fp16)
737 {
738 if (enable3dlut) {
739 struct fixed31_32 shaper_in_gain;
740 struct fixed31_32 pq_norm_gain;
741
742 shaper_in_gain = vpe_fixpt_from_int((long long)1 << shaper_in_exp_max);
743 if (is_fp16) {
744 *hdr_multiplier = vpe_fixpt_div_int(shaper_in_gain, CCCS_NORM);
745 } else {
746 // HDRMULT = 2^shaper_in_exp_max*(1/PQ(x))
747 vpe_compute_pq(vpe_fixpt_from_fraction((long long)peak_white, 10000), &pq_norm_gain);
748 *hdr_multiplier = vpe_fixpt_div(shaper_in_gain, pq_norm_gain);
749 }
750 } else {
751 *hdr_multiplier = vpe_fixpt_one;
752 }
753
754 return VPE_STATUS_OK;
755 }
756
vpe_color_update_shaper(const struct vpe_priv * vpe_priv,uint16_t shaper_in_exp_max,struct stream_ctx * stream_ctx,enum color_transfer_func tf_in_3dlut,bool enable_3dlut)757 enum vpe_status vpe_color_update_shaper(const struct vpe_priv *vpe_priv, uint16_t shaper_in_exp_max,
758 struct stream_ctx *stream_ctx, enum color_transfer_func tf_in_3dlut, bool enable_3dlut)
759 {
760 enum color_transfer_func tf = TRANSFER_FUNC_LINEAR;
761 bool update = false;
762 enum vpe_status ret = VPE_STATUS_OK;
763 struct transfer_func *shaper_func = stream_ctx->in_shaper_func;
764 struct fixed31_32 pq_norm_gain = vpe_fixpt_one;
765 VPE_ASSERT(shaper_func != NULL);
766 if (!enable_3dlut) {
767 shaper_func->type = TF_TYPE_BYPASS;
768 return VPE_STATUS_OK;
769 }
770
771 // Force PQ curve when FP16 format
772 if (stream_ctx->tf == TRANSFER_FUNC_LINEAR) {
773 // tf = stream_ctx->stream.tm_params.shaper_tf;
774 tf = tf_in_3dlut;
775 pq_norm_gain =
776 vpe_fixpt_mul_int(vpe_fixpt_one, stream_ctx->stream.tm_params.input_pq_norm_factor);
777 }
778
779 // right now shaper is always programmed with linear, once cached, it is always reused.
780 for (uint32_t i = 0; i < vpe_priv->pub.caps->resource_caps.num_mpc_3dlut; i++) {
781 if (vpe_priv->init.debug.disable_lut_caching ||
782 (shaper_func && (shaper_func->cache_info[i].tf != tf))) {
783 // if the caching has the required data cached, skip the update
784 update = true;
785 }
786 }
787
788 shaper_func->type = TF_TYPE_HWPWL;
789 shaper_func->tf = tf;
790
791 if (update) {
792 struct vpe_shaper_setup_in shaper_in;
793
794 shaper_in.shaper_in_max = 1 << 16;
795 shaper_in.use_const_hdr_mult = false; // can't be true. Fix is required.
796
797 ret = vpe_build_shaper(&shaper_in, shaper_func->tf, pq_norm_gain, &shaper_func->pwl);
798
799 if (ret == VPE_STATUS_OK) {
800 for (uint32_t i = 0; i < vpe_priv->pub.caps->resource_caps.num_mpc_3dlut; i++) {
801 shaper_func->dirty[i] = true;
802 shaper_func->config_cache[i].cached = false;
803 shaper_func->cache_info[i].tf = tf;
804 }
805 }
806 }
807 return ret;
808 }
809
vpe_color_update_movable_cm(struct vpe_priv * vpe_priv,const struct vpe_build_param * param)810 enum vpe_status vpe_color_update_movable_cm(
811 struct vpe_priv *vpe_priv, const struct vpe_build_param *param)
812 {
813 enum vpe_status ret = VPE_STATUS_OK;
814
815 uint32_t stream_idx;
816 struct stream_ctx *stream_ctx;
817 struct output_ctx *output_ctx = &vpe_priv->output_ctx;
818
819 for (stream_idx = 0; stream_idx < vpe_priv->num_streams; stream_idx++) {
820 stream_ctx = &vpe_priv->stream_ctx[stream_idx];
821
822 bool enable_3dlut = stream_ctx->stream.tm_params.UID != 0 || stream_ctx->stream.tm_params.enable_3dlut;
823
824 if (stream_ctx->uid_3dlut != stream_ctx->stream.tm_params.UID) {
825
826 uint32_t shaper_norm_factor;
827 struct vpe_color_space tm_out_cs;
828 struct vpe_color_space cs;
829 enum color_space out_lut_cs;
830 enum color_transfer_func tf, lut_in_tf;
831
832 if (!stream_ctx->in_shaper_func) {
833 stream_ctx->in_shaper_func = vpe_zalloc(sizeof(struct transfer_func));
834 if (!stream_ctx->in_shaper_func) {
835 vpe_log("err: out of memory for shaper tf!");
836 ret = VPE_STATUS_NO_MEMORY;
837 goto exit;
838 }
839 }
840
841 if (!stream_ctx->blend_tf) {
842 stream_ctx->blend_tf = vpe_zalloc(sizeof(struct transfer_func));
843 if (!stream_ctx->blend_tf) {
844 vpe_log("err: out of memory for blend/post1d tf!");
845 ret = VPE_STATUS_NO_MEMORY;
846 goto exit;
847 }
848 }
849
850 if (!stream_ctx->lut3d_func) {
851 stream_ctx->lut3d_func = vpe_zalloc(sizeof(struct vpe_3dlut));
852 if (!stream_ctx->lut3d_func) {
853 vpe_log("err: out of memory for 3d lut!");
854 ret = VPE_STATUS_NO_MEMORY;
855 goto exit;
856 }
857 }
858
859 if (!output_ctx->gamut_remap) {
860 output_ctx->gamut_remap = vpe_zalloc(sizeof(struct colorspace_transform));
861 if (!output_ctx->gamut_remap) {
862 vpe_log("err: out of memory for post blend gamut remap!");
863 ret = VPE_STATUS_NO_MEMORY;
864 goto exit;
865 }
866 }
867
868 get_shaper_norm_factor(&stream_ctx->stream.tm_params, stream_ctx, &shaper_norm_factor);
869
870 vpe_color_tm_update_hdr_mult(SHAPER_EXP_MAX_IN, shaper_norm_factor,
871 &stream_ctx->lut3d_func->hdr_multiplier, enable_3dlut,
872 vpe_is_fp16(stream_ctx->stream.surface_info.format));
873
874 vpe_color_build_shaper_cs(
875 &stream_ctx->stream.tm_params, &vpe_priv->output_ctx.surface, &cs);
876 vpe_color_get_color_space_and_tf(&cs, &out_lut_cs, &lut_in_tf);
877 vpe_color_update_shaper(
878 vpe_priv, SHAPER_EXP_MAX_IN, stream_ctx, lut_in_tf, enable_3dlut);
879
880 vpe_color_build_tm_cs(
881 &stream_ctx->stream.tm_params, &vpe_priv->output_ctx.surface, &tm_out_cs);
882
883 vpe_color_get_color_space_and_tf(&tm_out_cs, &out_lut_cs, &tf);
884 vpe_color_update_gamut(vpe_priv, out_lut_cs, vpe_priv->output_ctx.cs,
885 output_ctx->gamut_remap, !enable_3dlut);
886
887 vpe_color_update_3dlut(vpe_priv, stream_ctx, enable_3dlut);
888 }
889 }
890 exit:
891 return ret;
892 }
893
vpe_color_get_color_space_and_tf(const struct vpe_color_space * vcs,enum color_space * cs,enum color_transfer_func * tf)894 void vpe_color_get_color_space_and_tf(
895 const struct vpe_color_space *vcs, enum color_space *cs, enum color_transfer_func *tf)
896 {
897 enum vpe_color_range colorRange = vcs->range;
898
899 *cs = COLOR_SPACE_UNKNOWN;
900 *tf = TRANSFER_FUNC_UNKNOWN;
901
902 switch (vcs->tf) {
903 case VPE_TF_G22:
904 *tf = TRANSFER_FUNC_SRGB;
905 break;
906 case VPE_TF_G24:
907 *tf = TRANSFER_FUNC_BT1886;
908 break;
909 case VPE_TF_PQ:
910 *tf = TRANSFER_FUNC_PQ2084;
911 break;
912 case VPE_TF_PQ_NORMALIZED:
913 *tf = TRANSFER_FUNC_NORMALIZED_PQ;
914 break;
915 case VPE_TF_G10:
916 *tf = TRANSFER_FUNC_LINEAR;
917 break;
918 case VPE_TF_SRGB:
919 *tf = TRANSFER_FUNC_SRGB;
920 break;
921 case VPE_TF_BT709:
922 *tf = TRANSFER_FUNC_BT709;
923 break;
924 case VPE_TF_HLG:
925 *tf = TRANSFER_FUNC_HLG;
926 break;
927 default:
928 break;
929 }
930
931 if (vcs->encoding == VPE_PIXEL_ENCODING_YCbCr) {
932 switch (vcs->tf) {
933 case VPE_TF_G22:
934 *tf = TRANSFER_FUNC_BT709;
935 break;
936 default:
937 break;
938 }
939
940 switch (vcs->primaries) {
941 case VPE_PRIMARIES_BT601:
942 *cs = colorRange == VPE_COLOR_RANGE_FULL ? COLOR_SPACE_YCBCR601
943 : COLOR_SPACE_YCBCR601_LIMITED;
944 break;
945 case VPE_PRIMARIES_BT709:
946 *cs = colorRange == VPE_COLOR_RANGE_FULL ? COLOR_SPACE_YCBCR709
947 : COLOR_SPACE_YCBCR709_LIMITED;
948 break;
949 case VPE_PRIMARIES_BT2020:
950 *cs = colorRange == VPE_COLOR_RANGE_FULL ? COLOR_SPACE_2020_YCBCR
951 : COLOR_SPACE_2020_YCBCR_LIMITED;
952 break;
953 case VPE_PRIMARIES_JFIF:
954 *cs = colorRange == VPE_COLOR_RANGE_FULL ? COLOR_SPACE_YCBCR_JFIF : COLOR_SPACE_UNKNOWN;
955 break;
956 default:
957 break;
958 }
959 } else {
960 switch (vcs->primaries) {
961 case VPE_PRIMARIES_BT601:
962 *cs = colorRange == VPE_COLOR_RANGE_FULL ? COLOR_SPACE_RGB601
963 : COLOR_SPACE_RGB601_LIMITED;
964 break;
965 case VPE_PRIMARIES_BT709:
966 if (vcs->tf == VPE_TF_G10) {
967 *cs = COLOR_SPACE_MSREF_SCRGB;
968 } else {
969 *cs = colorRange == VPE_COLOR_RANGE_FULL ? COLOR_SPACE_SRGB
970 : COLOR_SPACE_SRGB_LIMITED;
971 }
972 break;
973 case VPE_PRIMARIES_BT2020:
974 *cs = colorRange == VPE_COLOR_RANGE_FULL ? COLOR_SPACE_2020_RGB_FULLRANGE
975 : COLOR_SPACE_2020_RGB_LIMITEDRANGE;
976 break;
977 /* VPE doesn't support JFIF format of RGB output, but geometric down scaling will change cs
978 * parameters to JFIF. Therefore, we need to add JFIF format in RGB output to avoid output
979 * color check fail.
980 */
981 case VPE_PRIMARIES_JFIF:
982 *cs = colorRange == VPE_COLOR_RANGE_FULL ? COLOR_SPACE_RGB_JFIF : COLOR_SPACE_UNKNOWN;
983 break;
984 default:
985 break;
986 }
987 }
988 }
989
vpe_is_rgb_equal(const struct pwl_result_data * rgb,uint32_t num)990 bool vpe_is_rgb_equal(const struct pwl_result_data *rgb, uint32_t num)
991 {
992 uint32_t i;
993 bool ret = true;
994
995 for (i = 0; i < num; i++) {
996 if (rgb[i].red_reg != rgb[i].green_reg || rgb[i].blue_reg != rgb[i].red_reg ||
997 rgb[i].blue_reg != rgb[i].green_reg) {
998 ret = false;
999 break;
1000 }
1001 }
1002 return ret;
1003 }
1004
vpe_convert_full_range_color_enum(enum color_space * cs)1005 void vpe_convert_full_range_color_enum(enum color_space *cs)
1006 {
1007 switch (*cs) {
1008 case COLOR_SPACE_YCBCR601_LIMITED:
1009 *cs = COLOR_SPACE_YCBCR601;
1010 break;
1011 case COLOR_SPACE_RGB601_LIMITED:
1012 *cs = COLOR_SPACE_RGB601;
1013 break;
1014 case COLOR_SPACE_YCBCR709_LIMITED:
1015 *cs = COLOR_SPACE_YCBCR709;
1016 break;
1017 case COLOR_SPACE_2020_YCBCR_LIMITED:
1018 *cs = COLOR_SPACE_2020_YCBCR;
1019 break;
1020 case COLOR_SPACE_SRGB_LIMITED:
1021 *cs = COLOR_SPACE_SRGB;
1022 break;
1023 case COLOR_SPACE_2020_RGB_LIMITEDRANGE:
1024 *cs = COLOR_SPACE_2020_RGB_FULLRANGE;
1025 break;
1026 default:
1027 break;
1028 }
1029 }
1030
vpe_is_HDR(enum color_transfer_func tf)1031 bool vpe_is_HDR(enum color_transfer_func tf)
1032 {
1033
1034 return (tf == TRANSFER_FUNC_PQ2084 || tf == TRANSFER_FUNC_HLG || tf == TRANSFER_FUNC_LINEAR);
1035 }
1036
1037 /*
1038 *
1039 * Pixel processing in VPE can be divided int two main paths. Tone maping cases and non tone mapping
1040 * cases. The gain factor supplied by the below function is only applied in the non-tone mapping
1041 * path.
1042 *
1043 * The gain is used to scale the white point in SDR<->HDR conversions.
1044 *
1045 * The policy is as follows:
1046 * HDR -> SDR (None tone mapping case): Map max input pixel value indicated by HDR meta data to
1047 * value of 1. SDR-> HDR : Map nominal value of 1 to display brightness indicated by metadata.
1048 *
1049 * Table outlining handling for full combination can be found in VPE Wolfpack
1050 */
vpe_color_update_whitepoint(const struct vpe_priv * vpe_priv,const struct vpe_build_param * param)1051 enum vpe_status vpe_color_update_whitepoint(
1052 const struct vpe_priv *vpe_priv, const struct vpe_build_param *param)
1053 {
1054
1055 struct stream_ctx *stream = vpe_priv->stream_ctx;
1056 const struct output_ctx *output_ctx = &vpe_priv->output_ctx;
1057 const struct vpe_color_space *vpe_cs = &stream->stream.surface_info.cs;
1058 bool output_isHDR = vpe_is_HDR(vpe_priv->output_ctx.tf);
1059 bool input_isHDR = false;
1060 bool is_yCbCr = false;
1061 bool is_g24 = false;
1062 bool is_fp16 = false;
1063
1064 for (unsigned int stream_index = 0; stream_index < vpe_priv->num_streams; stream_index++) {
1065
1066 input_isHDR = vpe_is_HDR(stream->tf);
1067 is_yCbCr = stream->is_yuv_input;
1068 is_g24 = (vpe_cs->tf == VPE_TF_G24);
1069 is_fp16 = vpe_is_fp16(stream->stream.surface_info.format);
1070 if (!input_isHDR && output_isHDR) {
1071 int sdrWhiteLevel = (is_yCbCr || is_g24) ? SDR_VIDEO_WHITE_POINT : SDR_WHITE_POINT;
1072 stream->white_point_gain = vpe_fixpt_from_fraction(sdrWhiteLevel, 10000);
1073 } else if (input_isHDR && !output_isHDR) {
1074
1075 stream->white_point_gain = stream->stream.hdr_metadata.max_mastering != 0
1076 ? vpe_fixpt_from_fraction(HDR_PEAK_WHITE,
1077 stream->stream.hdr_metadata.max_mastering)
1078 : vpe_fixpt_one;
1079 } else {
1080 stream->white_point_gain = vpe_fixpt_one;
1081 }
1082
1083 if (is_fp16) {
1084 stream->white_point_gain = vpe_fixpt_div_int(stream->white_point_gain, CCCS_NORM);
1085 }
1086
1087 stream++;
1088 }
1089 return VPE_STATUS_OK;
1090 }
1091
vpe_get_range_type(enum color_space color_space,enum vpe_surface_pixel_format format)1092 enum color_range_type vpe_get_range_type(
1093 enum color_space color_space, enum vpe_surface_pixel_format format)
1094 {
1095 bool is_limited = false;
1096
1097 switch (color_space) {
1098 case COLOR_SPACE_SRGB:
1099 case COLOR_SPACE_MSREF_SCRGB:
1100 case COLOR_SPACE_YCBCR601:
1101 case COLOR_SPACE_RGB601:
1102 case COLOR_SPACE_YCBCR709:
1103 case COLOR_SPACE_YCBCR_JFIF:
1104 case COLOR_SPACE_RGB_JFIF:
1105 case COLOR_SPACE_2020_RGB_FULLRANGE:
1106 case COLOR_SPACE_2020_YCBCR:
1107 is_limited = false;
1108 break;
1109 case COLOR_SPACE_SRGB_LIMITED:
1110 case COLOR_SPACE_RGB601_LIMITED:
1111 case COLOR_SPACE_YCBCR601_LIMITED:
1112 case COLOR_SPACE_YCBCR709_LIMITED:
1113 case COLOR_SPACE_2020_RGB_LIMITEDRANGE:
1114 case COLOR_SPACE_2020_YCBCR_LIMITED:
1115 is_limited = true;
1116 break;
1117 default:
1118 VPE_ASSERT(false);
1119 break;
1120 }
1121
1122 if (!is_limited)
1123 return COLOR_RANGE_FULL;
1124
1125 if (vpe_is_8bit(format))
1126 return COLOR_RANGE_LIMITED_8BPC;
1127 else if (vpe_is_10bit(format))
1128 return COLOR_RANGE_LIMITED_10BPC;
1129 else
1130 return COLOR_RANGE_LIMITED_16BPC;
1131 }
1132