• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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