|  | // 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); |