xref: /aosp_15_r20/external/mesa3d/src/amd/vpelib/src/core/color.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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                 &param->streams[stream_idx].surface_info.cs, &param->streams[stream_idx].color_adj,
659                 is_3dlut_enable, geometric_update);
660 
661             build_scale_and_bias(stream_ctx->bias_scale,
662                 &param->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