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