// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 Mentor Graphics Inc.
*/
#include <linux/types.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/sizes.h>
#include "ipu-prv.h"
#define QUANT_MAP(q) \
((q) == V4L2_QUANTIZATION_FULL_RANGE || \
(q) == V4L2_QUANTIZATION_DEFAULT ? 0 : 1 )
/* identity matrix */
static const struct ipu_ic_csc_params identity = {
.coeff = {
{ 128 , 0 , 0 , },
{ 0 , 128 , 0 , },
{ 0 , 0 , 128 , },
},
.offset = { 0 , 0 , 0 , },
.scale = 2 ,
};
/*
* RGB full-range to RGB limited-range
*
* R_lim = 0.8588 * R_full + 16
* G_lim = 0.8588 * G_full + 16
* B_lim = 0.8588 * B_full + 16
*/
static const struct ipu_ic_csc_params rgbf2rgbl = {
.coeff = {
{ 220 , 0 , 0 , },
{ 0 , 220 , 0 , },
{ 0 , 0 , 220 , },
},
.offset = { 64 , 64 , 64 , },
.scale = 1 ,
};
/*
* RGB limited-range to RGB full-range
*
* R_full = 1.1644 * (R_lim - 16)
* G_full = 1.1644 * (G_lim - 16)
* B_full = 1.1644 * (B_lim - 16)
*/
static const struct ipu_ic_csc_params rgbl2rgbf = {
.coeff = {
{ 149 , 0 , 0 , },
{ 0 , 149 , 0 , },
{ 0 , 0 , 149 , },
},
.offset = { -37 , -37 , -37 , },
.scale = 2 ,
};
/*
* YUV full-range to YUV limited-range
*
* Y_lim = 0.8588 * Y_full + 16
* Cb_lim = 0.8784 * (Cb_full - 128) + 128
* Cr_lim = 0.8784 * (Cr_full - 128) + 128
*/
static const struct ipu_ic_csc_params yuvf2yuvl = {
.coeff = {
{ 220 , 0 , 0 , },
{ 0 , 225 , 0 , },
{ 0 , 0 , 225 , },
},
.offset = { 64 , 62 , 62 , },
.scale = 1 ,
.sat = true ,
};
/*
* YUV limited-range to YUV full-range
*
* Y_full = 1.1644 * (Y_lim - 16)
* Cb_full = 1.1384 * (Cb_lim - 128) + 128
* Cr_full = 1.1384 * (Cr_lim - 128) + 128
*/
static const struct ipu_ic_csc_params yuvl2yuvf = {
.coeff = {
{ 149 , 0 , 0 , },
{ 0 , 146 , 0 , },
{ 0 , 0 , 146 , },
},
.offset = { -37 , -35 , -35 , },
.scale = 2 ,
};
static const struct ipu_ic_csc_params *rgb2rgb[] = {
&identity,
&rgbf2rgbl,
&rgbl2rgbf,
&identity,
};
static const struct ipu_ic_csc_params *yuv2yuv[] = {
&identity,
&yuvf2yuvl,
&yuvl2yuvf,
&identity,
};
/*
* BT.601 RGB full-range to YUV full-range
*
* Y = .2990 * R + .5870 * G + .1140 * B
* U = -.1687 * R - .3313 * G + .5000 * B + 128
* V = .5000 * R - .4187 * G - .0813 * B + 128
*/
static const struct ipu_ic_csc_params rgbf2yuvf_601 = {
.coeff = {
{ 77 , 150 , 29 , },
{ -43 , -85 , 128 , },
{ 128 , -107 , -21 , },
},
.offset = { 0 , 512 , 512 , },
.scale = 1 ,
};
/* BT.601 RGB full-range to YUV limited-range */
static const struct ipu_ic_csc_params rgbf2yuvl_601 = {
.coeff = {
{ 66 , 129 , 25 , },
{ -38 , -74 , 112 , },
{ 112 , -94 , -18 , },
},
.offset = { 64 , 512 , 512 , },
.scale = 1 ,
.sat = true ,
};
/* BT.601 RGB limited-range to YUV full-range */
static const struct ipu_ic_csc_params rgbl2yuvf_601 = {
.coeff = {
{ 89 , 175 , 34 , },
{ -50 , -99 , 149 , },
{ 149 , -125 , -24 , },
},
.offset = { -75 , 512 , 512 , },
.scale = 1 ,
};
/* BT.601 RGB limited-range to YUV limited-range */
static const struct ipu_ic_csc_params rgbl2yuvl_601 = {
.coeff = {
{ 77 , 150 , 29 , },
{ -44 , -87 , 131 , },
{ 131 , -110 , -21 , },
},
.offset = { 0 , 512 , 512 , },
.scale = 1 ,
.sat = true ,
};
/*
* BT.601 YUV full-range to RGB full-range
*
* R = 1. * Y + 0 * (Cb - 128) + 1.4020 * (Cr - 128)
* G = 1. * Y - .3441 * (Cb - 128) - .7141 * (Cr - 128)
* B = 1. * Y + 1.7720 * (Cb - 128) + 0 * (Cr - 128)
*
* equivalently (factoring out the offsets):
*
* R = 1. * Y + 0 * Cb + 1.4020 * Cr - 179.456
* G = 1. * Y - .3441 * Cb - .7141 * Cr + 135.450
* B = 1. * Y + 1.7720 * Cb + 0 * Cr - 226.816
*/
static const struct ipu_ic_csc_params yuvf2rgbf_601 = {
.coeff = {
{ 128 , 0 , 179 , },
{ 128 , -44 , -91 , },
{ 128 , 227 , 0 , },
},
.offset = { -359 , 271 , -454 , },
.scale = 2 ,
};
/* BT.601 YUV full-range to RGB limited-range */
static const struct ipu_ic_csc_params yuvf2rgbl_601 = {
.coeff = {
{ 110 , 0 , 154 , },
{ 110 , -38 , -78 , },
{ 110 , 195 , 0 , },
},
.offset = { -276 , 265 , -358 , },
.scale = 2 ,
};
/* BT.601 YUV limited-range to RGB full-range */
static const struct ipu_ic_csc_params yuvl2rgbf_601 = {
.coeff = {
{ 75 , 0 , 102 , },
{ 75 , -25 , -52 , },
{ 75 , 129 , 0 , },
},
.offset = { -223 , 136 , -277 , },
.scale = 3 ,
};
/* BT.601 YUV limited-range to RGB limited-range */
static const struct ipu_ic_csc_params yuvl2rgbl_601 = {
.coeff = {
{ 128 , 0 , 175 , },
{ 128 , -43 , -89 , },
{ 128 , 222 , 0 , },
},
.offset = { -351 , 265 , -443 , },
.scale = 2 ,
};
static const struct ipu_ic_csc_params *rgb2yuv_601[] = {
&rgbf2yuvf_601,
&rgbf2yuvl_601,
&rgbl2yuvf_601,
&rgbl2yuvl_601,
};
static const struct ipu_ic_csc_params *yuv2rgb_601[] = {
&yuvf2rgbf_601,
&yuvf2rgbl_601,
&yuvl2rgbf_601,
&yuvl2rgbl_601,
};
/*
* REC.709 encoding from RGB full range to YUV full range:
*
* Y = .2126 * R + .7152 * G + .0722 * B
* U = -.1146 * R - .3854 * G + .5000 * B + 128
* V = .5000 * R - .4542 * G - .0458 * B + 128
*/
static const struct ipu_ic_csc_params rgbf2yuvf_709 = {
.coeff = {
{ 54 , 183 , 19 },
{ -29 , -99 , 128 },
{ 128 , -116 , -12 },
},
.offset = { 0 , 512 , 512 },
.scale = 1 ,
};
/* Rec.709 RGB full-range to YUV limited-range */
static const struct ipu_ic_csc_params rgbf2yuvl_709 = {
.coeff = {
{ 47 , 157 , 16 , },
{ -26 , -87 , 112 , },
{ 112 , -102 , -10 , },
},
.offset = { 64 , 512 , 512 , },
.scale = 1 ,
.sat = true ,
};
/* Rec.709 RGB limited-range to YUV full-range */
static const struct ipu_ic_csc_params rgbl2yuvf_709 = {
.coeff = {
{ 63 , 213 , 22 , },
{ -34 , -115 , 149 , },
{ 149 , -135 , -14 , },
},
.offset = { -75 , 512 , 512 , },
.scale = 1 ,
};
/* Rec.709 RGB limited-range to YUV limited-range */
static const struct ipu_ic_csc_params rgbl2yuvl_709 = {
.coeff = {
{ 54 , 183 , 18 , },
{ -30 , -101 , 131 , },
{ 131 , -119 , -12 , },
},
.offset = { 0 , 512 , 512 , },
.scale = 1 ,
.sat = true ,
};
/*
* Inverse REC.709 encoding from YUV full range to RGB full range:
*
* R = 1. * Y + 0 * (Cb - 128) + 1.5748 * (Cr - 128)
* G = 1. * Y - .1873 * (Cb - 128) - .4681 * (Cr - 128)
* B = 1. * Y + 1.8556 * (Cb - 128) + 0 * (Cr - 128)
*
* equivalently (factoring out the offsets):
*
* R = 1. * Y + 0 * Cb + 1.5748 * Cr - 201.574
* G = 1. * Y - .1873 * Cb - .4681 * Cr + 83.891
* B = 1. * Y + 1.8556 * Cb + 0 * Cr - 237.517
*/
static const struct ipu_ic_csc_params yuvf2rgbf_709 = {
.coeff = {
{ 128 , 0 , 202 },
{ 128 , -24 , -60 },
{ 128 , 238 , 0 },
},
.offset = { -403 , 168 , -475 },
.scale = 2 ,
};
/* Rec.709 YUV full-range to RGB limited-range */
static const struct ipu_ic_csc_params yuvf2rgbl_709 = {
.coeff = {
{ 110 , 0 , 173 , },
{ 110 , -21 , -51 , },
{ 110 , 204 , 0 , },
},
.offset = { -314 , 176 , -376 , },
.scale = 2 ,
};
/* Rec.709 YUV limited-range to RGB full-range */
static const struct ipu_ic_csc_params yuvl2rgbf_709 = {
.coeff = {
{ 75 , 0 , 115 , },
{ 75 , -14 , -34 , },
{ 75 , 135 , 0 , },
},
.offset = { -248 , 77 , -289 , },
.scale = 3 ,
};
/* Rec.709 YUV limited-range to RGB limited-range */
static const struct ipu_ic_csc_params yuvl2rgbl_709 = {
.coeff = {
{ 128 , 0 , 197 , },
{ 128 , -23 , -59 , },
{ 128 , 232 , 0 , },
},
.offset = { -394 , 164 , -464 , },
.scale = 2 ,
};
static const struct ipu_ic_csc_params *rgb2yuv_709[] = {
&rgbf2yuvf_709,
&rgbf2yuvl_709,
&rgbl2yuvf_709,
&rgbl2yuvl_709,
};
static const struct ipu_ic_csc_params *yuv2rgb_709[] = {
&yuvf2rgbf_709,
&yuvf2rgbl_709,
&yuvl2rgbf_709,
&yuvl2rgbl_709,
};
static int calc_csc_coeffs(struct ipu_ic_csc *csc)
{
const struct ipu_ic_csc_params **params_tbl;
int tbl_idx;
tbl_idx = (QUANT_MAP(csc->in_cs.quant) << 1 ) |
QUANT_MAP(csc->out_cs.quant);
if (csc->in_cs.cs == csc->out_cs.cs) {
csc->params = (csc->in_cs.cs == IPUV3_COLORSPACE_YUV) ?
*yuv2yuv[tbl_idx] : *rgb2rgb[tbl_idx];
return 0 ;
}
/* YUV <-> RGB encoding is required */
switch (csc->out_cs.enc) {
case V4L2_YCBCR_ENC_601:
params_tbl = (csc->in_cs.cs == IPUV3_COLORSPACE_YUV) ?
yuv2rgb_601 : rgb2yuv_601;
break ;
case V4L2_YCBCR_ENC_709:
params_tbl = (csc->in_cs.cs == IPUV3_COLORSPACE_YUV) ?
yuv2rgb_709 : rgb2yuv_709;
break ;
default :
return -ENOTSUPP;
}
csc->params = *params_tbl[tbl_idx];
return 0 ;
}
int __ipu_ic_calc_csc(struct ipu_ic_csc *csc)
{
return calc_csc_coeffs(csc);
}
EXPORT_SYMBOL_GPL(__ipu_ic_calc_csc);
int ipu_ic_calc_csc(struct ipu_ic_csc *csc,
enum v4l2_ycbcr_encoding in_enc,
enum v4l2_quantization in_quant,
enum ipu_color_space in_cs,
enum v4l2_ycbcr_encoding out_enc,
enum v4l2_quantization out_quant,
enum ipu_color_space out_cs)
{
ipu_ic_fill_colorspace(&csc->in_cs, in_enc, in_quant, in_cs);
ipu_ic_fill_colorspace(&csc->out_cs, out_enc, out_quant, out_cs);
return __ipu_ic_calc_csc(csc);
}
EXPORT_SYMBOL_GPL(ipu_ic_calc_csc);
Messung V0.5 in Prozent C=96 H=96 G=95
¤ Dauer der Verarbeitung: 0.1 Sekunden
(vorverarbeitet am 2026-06-08)
¤
*© Formatika GbR, Deutschland