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