1f208b26eSSteve Longerbeam // SPDX-License-Identifier: GPL-2.0+ 2f208b26eSSteve Longerbeam /* 3f208b26eSSteve Longerbeam * Copyright (C) 2019 Mentor Graphics Inc. 4f208b26eSSteve Longerbeam */ 5f208b26eSSteve Longerbeam 6f208b26eSSteve Longerbeam #include <linux/types.h> 7f208b26eSSteve Longerbeam #include <linux/init.h> 8f208b26eSSteve Longerbeam #include <linux/errno.h> 9f208b26eSSteve Longerbeam #include <linux/err.h> 10f208b26eSSteve Longerbeam #include <linux/sizes.h> 11f208b26eSSteve Longerbeam #include "ipu-prv.h" 12f208b26eSSteve Longerbeam 13e3e4820dSSteve Longerbeam #define QUANT_MAP(q) \ 14e3e4820dSSteve Longerbeam ((q) == V4L2_QUANTIZATION_FULL_RANGE || \ 15e3e4820dSSteve Longerbeam (q) == V4L2_QUANTIZATION_DEFAULT ? 0 : 1) 16e3e4820dSSteve Longerbeam 17f208b26eSSteve Longerbeam /* identity matrix */ 18f208b26eSSteve Longerbeam static const struct ipu_ic_csc_params identity = { 19f208b26eSSteve Longerbeam .coeff = { 20f208b26eSSteve Longerbeam { 128, 0, 0, }, 21f208b26eSSteve Longerbeam { 0, 128, 0, }, 22f208b26eSSteve Longerbeam { 0, 0, 128, }, 23f208b26eSSteve Longerbeam }, 24f208b26eSSteve Longerbeam .offset = { 0, 0, 0, }, 25f208b26eSSteve Longerbeam .scale = 2, 26f208b26eSSteve Longerbeam }; 27f208b26eSSteve Longerbeam 28e3e4820dSSteve Longerbeam /* 29e3e4820dSSteve Longerbeam * RGB full-range to RGB limited-range 30e3e4820dSSteve Longerbeam * 31e3e4820dSSteve Longerbeam * R_lim = 0.8588 * R_full + 16 32e3e4820dSSteve Longerbeam * G_lim = 0.8588 * G_full + 16 33e3e4820dSSteve Longerbeam * B_lim = 0.8588 * B_full + 16 34e3e4820dSSteve Longerbeam */ 35e3e4820dSSteve Longerbeam static const struct ipu_ic_csc_params rgbf2rgbl = { 36e3e4820dSSteve Longerbeam .coeff = { 37e3e4820dSSteve Longerbeam { 220, 0, 0, }, 38e3e4820dSSteve Longerbeam { 0, 220, 0, }, 39e3e4820dSSteve Longerbeam { 0, 0, 220, }, 40e3e4820dSSteve Longerbeam }, 41e3e4820dSSteve Longerbeam .offset = { 64, 64, 64, }, 42e3e4820dSSteve Longerbeam .scale = 1, 43e3e4820dSSteve Longerbeam }; 44e3e4820dSSteve Longerbeam 45e3e4820dSSteve Longerbeam /* 46e3e4820dSSteve Longerbeam * RGB limited-range to RGB full-range 47e3e4820dSSteve Longerbeam * 48e3e4820dSSteve Longerbeam * R_full = 1.1644 * (R_lim - 16) 49e3e4820dSSteve Longerbeam * G_full = 1.1644 * (G_lim - 16) 50e3e4820dSSteve Longerbeam * B_full = 1.1644 * (B_lim - 16) 51e3e4820dSSteve Longerbeam */ 52e3e4820dSSteve Longerbeam static const struct ipu_ic_csc_params rgbl2rgbf = { 53e3e4820dSSteve Longerbeam .coeff = { 54e3e4820dSSteve Longerbeam { 149, 0, 0, }, 55e3e4820dSSteve Longerbeam { 0, 149, 0, }, 56e3e4820dSSteve Longerbeam { 0, 0, 149, }, 57e3e4820dSSteve Longerbeam }, 58e3e4820dSSteve Longerbeam .offset = { -37, -37, -37, }, 59e3e4820dSSteve Longerbeam .scale = 2, 60e3e4820dSSteve Longerbeam }; 61e3e4820dSSteve Longerbeam 62e3e4820dSSteve Longerbeam /* 63e3e4820dSSteve Longerbeam * YUV full-range to YUV limited-range 64e3e4820dSSteve Longerbeam * 65e3e4820dSSteve Longerbeam * Y_lim = 0.8588 * Y_full + 16 66e3e4820dSSteve Longerbeam * Cb_lim = 0.8784 * (Cb_full - 128) + 128 67e3e4820dSSteve Longerbeam * Cr_lim = 0.8784 * (Cr_full - 128) + 128 68e3e4820dSSteve Longerbeam */ 69e3e4820dSSteve Longerbeam static const struct ipu_ic_csc_params yuvf2yuvl = { 70e3e4820dSSteve Longerbeam .coeff = { 71e3e4820dSSteve Longerbeam { 220, 0, 0, }, 72e3e4820dSSteve Longerbeam { 0, 225, 0, }, 73e3e4820dSSteve Longerbeam { 0, 0, 225, }, 74e3e4820dSSteve Longerbeam }, 75e3e4820dSSteve Longerbeam .offset = { 64, 62, 62, }, 76e3e4820dSSteve Longerbeam .scale = 1, 77e3e4820dSSteve Longerbeam .sat = true, 78e3e4820dSSteve Longerbeam }; 79e3e4820dSSteve Longerbeam 80e3e4820dSSteve Longerbeam /* 81e3e4820dSSteve Longerbeam * YUV limited-range to YUV full-range 82e3e4820dSSteve Longerbeam * 83e3e4820dSSteve Longerbeam * Y_full = 1.1644 * (Y_lim - 16) 84e3e4820dSSteve Longerbeam * Cb_full = 1.1384 * (Cb_lim - 128) + 128 85e3e4820dSSteve Longerbeam * Cr_full = 1.1384 * (Cr_lim - 128) + 128 86e3e4820dSSteve Longerbeam */ 87e3e4820dSSteve Longerbeam static const struct ipu_ic_csc_params yuvl2yuvf = { 88e3e4820dSSteve Longerbeam .coeff = { 89e3e4820dSSteve Longerbeam { 149, 0, 0, }, 90e3e4820dSSteve Longerbeam { 0, 146, 0, }, 91e3e4820dSSteve Longerbeam { 0, 0, 146, }, 92e3e4820dSSteve Longerbeam }, 93e3e4820dSSteve Longerbeam .offset = { -37, -35, -35, }, 94e3e4820dSSteve Longerbeam .scale = 2, 95e3e4820dSSteve Longerbeam }; 96e3e4820dSSteve Longerbeam 97f208b26eSSteve Longerbeam static const struct ipu_ic_csc_params *rgb2rgb[] = { 98f208b26eSSteve Longerbeam &identity, 99e3e4820dSSteve Longerbeam &rgbf2rgbl, 100e3e4820dSSteve Longerbeam &rgbl2rgbf, 101e3e4820dSSteve Longerbeam &identity, 102f208b26eSSteve Longerbeam }; 103f208b26eSSteve Longerbeam 104f208b26eSSteve Longerbeam static const struct ipu_ic_csc_params *yuv2yuv[] = { 105f208b26eSSteve Longerbeam &identity, 106e3e4820dSSteve Longerbeam &yuvf2yuvl, 107e3e4820dSSteve Longerbeam &yuvl2yuvf, 108e3e4820dSSteve Longerbeam &identity, 109f208b26eSSteve Longerbeam }; 110f208b26eSSteve Longerbeam 111f208b26eSSteve Longerbeam /* 112f208b26eSSteve Longerbeam * BT.601 RGB full-range to YUV full-range 113f208b26eSSteve Longerbeam * 114f208b26eSSteve Longerbeam * Y = .2990 * R + .5870 * G + .1140 * B 115f208b26eSSteve Longerbeam * U = -.1687 * R - .3313 * G + .5000 * B + 128 116f208b26eSSteve Longerbeam * V = .5000 * R - .4187 * G - .0813 * B + 128 117f208b26eSSteve Longerbeam */ 118f208b26eSSteve Longerbeam static const struct ipu_ic_csc_params rgbf2yuvf_601 = { 119f208b26eSSteve Longerbeam .coeff = { 120f208b26eSSteve Longerbeam { 77, 150, 29, }, 121f208b26eSSteve Longerbeam { -43, -85, 128, }, 122f208b26eSSteve Longerbeam { 128, -107, -21, }, 123f208b26eSSteve Longerbeam }, 124f208b26eSSteve Longerbeam .offset = { 0, 512, 512, }, 125f208b26eSSteve Longerbeam .scale = 1, 126f208b26eSSteve Longerbeam }; 127f208b26eSSteve Longerbeam 128e3e4820dSSteve Longerbeam /* BT.601 RGB full-range to YUV limited-range */ 129e3e4820dSSteve Longerbeam static const struct ipu_ic_csc_params rgbf2yuvl_601 = { 130e3e4820dSSteve Longerbeam .coeff = { 131e3e4820dSSteve Longerbeam { 66, 129, 25, }, 132e3e4820dSSteve Longerbeam { -38, -74, 112, }, 133e3e4820dSSteve Longerbeam { 112, -94, -18, }, 134e3e4820dSSteve Longerbeam }, 135e3e4820dSSteve Longerbeam .offset = { 64, 512, 512, }, 136e3e4820dSSteve Longerbeam .scale = 1, 137e3e4820dSSteve Longerbeam .sat = true, 138e3e4820dSSteve Longerbeam }; 139e3e4820dSSteve Longerbeam 140e3e4820dSSteve Longerbeam /* BT.601 RGB limited-range to YUV full-range */ 141e3e4820dSSteve Longerbeam static const struct ipu_ic_csc_params rgbl2yuvf_601 = { 142e3e4820dSSteve Longerbeam .coeff = { 143e3e4820dSSteve Longerbeam { 89, 175, 34, }, 144e3e4820dSSteve Longerbeam { -50, -99, 149, }, 145e3e4820dSSteve Longerbeam { 149, -125, -24, }, 146e3e4820dSSteve Longerbeam }, 147e3e4820dSSteve Longerbeam .offset = { -75, 512, 512, }, 148e3e4820dSSteve Longerbeam .scale = 1, 149e3e4820dSSteve Longerbeam }; 150e3e4820dSSteve Longerbeam 151e3e4820dSSteve Longerbeam /* BT.601 RGB limited-range to YUV limited-range */ 152e3e4820dSSteve Longerbeam static const struct ipu_ic_csc_params rgbl2yuvl_601 = { 153e3e4820dSSteve Longerbeam .coeff = { 154e3e4820dSSteve Longerbeam { 77, 150, 29, }, 155e3e4820dSSteve Longerbeam { -44, -87, 131, }, 156e3e4820dSSteve Longerbeam { 131, -110, -21, }, 157e3e4820dSSteve Longerbeam }, 158e3e4820dSSteve Longerbeam .offset = { 0, 512, 512, }, 159e3e4820dSSteve Longerbeam .scale = 1, 160e3e4820dSSteve Longerbeam .sat = true, 161e3e4820dSSteve Longerbeam }; 162e3e4820dSSteve Longerbeam 163f208b26eSSteve Longerbeam /* 164f208b26eSSteve Longerbeam * BT.601 YUV full-range to RGB full-range 165f208b26eSSteve Longerbeam * 166f208b26eSSteve Longerbeam * R = 1. * Y + 0 * (Cb - 128) + 1.4020 * (Cr - 128) 167f208b26eSSteve Longerbeam * G = 1. * Y - .3441 * (Cb - 128) - .7141 * (Cr - 128) 168f208b26eSSteve Longerbeam * B = 1. * Y + 1.7720 * (Cb - 128) + 0 * (Cr - 128) 169f208b26eSSteve Longerbeam * 170f208b26eSSteve Longerbeam * equivalently (factoring out the offsets): 171f208b26eSSteve Longerbeam * 172f208b26eSSteve Longerbeam * R = 1. * Y + 0 * Cb + 1.4020 * Cr - 179.456 173f208b26eSSteve Longerbeam * G = 1. * Y - .3441 * Cb - .7141 * Cr + 135.450 174f208b26eSSteve Longerbeam * B = 1. * Y + 1.7720 * Cb + 0 * Cr - 226.816 175f208b26eSSteve Longerbeam */ 176f208b26eSSteve Longerbeam static const struct ipu_ic_csc_params yuvf2rgbf_601 = { 177f208b26eSSteve Longerbeam .coeff = { 178f208b26eSSteve Longerbeam { 128, 0, 179, }, 179f208b26eSSteve Longerbeam { 128, -44, -91, }, 180f208b26eSSteve Longerbeam { 128, 227, 0, }, 181f208b26eSSteve Longerbeam }, 182f208b26eSSteve Longerbeam .offset = { -359, 271, -454, }, 183f208b26eSSteve Longerbeam .scale = 2, 184f208b26eSSteve Longerbeam }; 185f208b26eSSteve Longerbeam 186e3e4820dSSteve Longerbeam /* BT.601 YUV full-range to RGB limited-range */ 187e3e4820dSSteve Longerbeam static const struct ipu_ic_csc_params yuvf2rgbl_601 = { 188e3e4820dSSteve Longerbeam .coeff = { 189e3e4820dSSteve Longerbeam { 110, 0, 154, }, 190e3e4820dSSteve Longerbeam { 110, -38, -78, }, 191e3e4820dSSteve Longerbeam { 110, 195, 0, }, 192e3e4820dSSteve Longerbeam }, 193e3e4820dSSteve Longerbeam .offset = { -276, 265, -358, }, 194e3e4820dSSteve Longerbeam .scale = 2, 195e3e4820dSSteve Longerbeam }; 196e3e4820dSSteve Longerbeam 197e3e4820dSSteve Longerbeam /* BT.601 YUV limited-range to RGB full-range */ 198e3e4820dSSteve Longerbeam static const struct ipu_ic_csc_params yuvl2rgbf_601 = { 199e3e4820dSSteve Longerbeam .coeff = { 200e3e4820dSSteve Longerbeam { 75, 0, 102, }, 201e3e4820dSSteve Longerbeam { 75, -25, -52, }, 202e3e4820dSSteve Longerbeam { 75, 129, 0, }, 203e3e4820dSSteve Longerbeam }, 204e3e4820dSSteve Longerbeam .offset = { -223, 136, -277, }, 205e3e4820dSSteve Longerbeam .scale = 3, 206e3e4820dSSteve Longerbeam }; 207e3e4820dSSteve Longerbeam 208e3e4820dSSteve Longerbeam /* BT.601 YUV limited-range to RGB limited-range */ 209e3e4820dSSteve Longerbeam static const struct ipu_ic_csc_params yuvl2rgbl_601 = { 210e3e4820dSSteve Longerbeam .coeff = { 211e3e4820dSSteve Longerbeam { 128, 0, 175, }, 212e3e4820dSSteve Longerbeam { 128, -43, -89, }, 213e3e4820dSSteve Longerbeam { 128, 222, 0, }, 214e3e4820dSSteve Longerbeam }, 215e3e4820dSSteve Longerbeam .offset = { -351, 265, -443, }, 216e3e4820dSSteve Longerbeam .scale = 2, 217e3e4820dSSteve Longerbeam }; 218e3e4820dSSteve Longerbeam 219f208b26eSSteve Longerbeam static const struct ipu_ic_csc_params *rgb2yuv_601[] = { 220f208b26eSSteve Longerbeam &rgbf2yuvf_601, 221e3e4820dSSteve Longerbeam &rgbf2yuvl_601, 222e3e4820dSSteve Longerbeam &rgbl2yuvf_601, 223e3e4820dSSteve Longerbeam &rgbl2yuvl_601, 224f208b26eSSteve Longerbeam }; 225f208b26eSSteve Longerbeam 226f208b26eSSteve Longerbeam static const struct ipu_ic_csc_params *yuv2rgb_601[] = { 227f208b26eSSteve Longerbeam &yuvf2rgbf_601, 228e3e4820dSSteve Longerbeam &yuvf2rgbl_601, 229e3e4820dSSteve Longerbeam &yuvl2rgbf_601, 230e3e4820dSSteve Longerbeam &yuvl2rgbl_601, 231f208b26eSSteve Longerbeam }; 232f208b26eSSteve Longerbeam 233f208b26eSSteve Longerbeam static int calc_csc_coeffs(struct ipu_ic_csc *csc) 234f208b26eSSteve Longerbeam { 235e3e4820dSSteve Longerbeam const struct ipu_ic_csc_params **params_tbl; 236e3e4820dSSteve Longerbeam int tbl_idx; 237e3e4820dSSteve Longerbeam 238f208b26eSSteve Longerbeam if (csc->out_cs.enc != V4L2_YCBCR_ENC_601) 239f208b26eSSteve Longerbeam return -ENOTSUPP; 240f208b26eSSteve Longerbeam 241e3e4820dSSteve Longerbeam tbl_idx = (QUANT_MAP(csc->in_cs.quant) << 1) | 242e3e4820dSSteve Longerbeam QUANT_MAP(csc->out_cs.quant); 243f208b26eSSteve Longerbeam 244f208b26eSSteve Longerbeam if (csc->in_cs.cs == csc->out_cs.cs) { 245f208b26eSSteve Longerbeam csc->params = (csc->in_cs.cs == IPUV3_COLORSPACE_YUV) ? 246e3e4820dSSteve Longerbeam *yuv2yuv[tbl_idx] : *rgb2rgb[tbl_idx]; 247e3e4820dSSteve Longerbeam 248f208b26eSSteve Longerbeam return 0; 249f208b26eSSteve Longerbeam } 250f208b26eSSteve Longerbeam 251e3e4820dSSteve Longerbeam /* YUV <-> RGB encoding is required */ 252e3e4820dSSteve Longerbeam 253e3e4820dSSteve Longerbeam params_tbl = (csc->in_cs.cs == IPUV3_COLORSPACE_YUV) ? 254e3e4820dSSteve Longerbeam yuv2rgb_601 : rgb2yuv_601; 255e3e4820dSSteve Longerbeam 256e3e4820dSSteve Longerbeam csc->params = *params_tbl[tbl_idx]; 257f208b26eSSteve Longerbeam 258f208b26eSSteve Longerbeam return 0; 259f208b26eSSteve Longerbeam } 260f208b26eSSteve Longerbeam 261f208b26eSSteve Longerbeam int __ipu_ic_calc_csc(struct ipu_ic_csc *csc) 262f208b26eSSteve Longerbeam { 263f208b26eSSteve Longerbeam return calc_csc_coeffs(csc); 264f208b26eSSteve Longerbeam } 265f208b26eSSteve Longerbeam EXPORT_SYMBOL_GPL(__ipu_ic_calc_csc); 266f208b26eSSteve Longerbeam 267f208b26eSSteve Longerbeam int ipu_ic_calc_csc(struct ipu_ic_csc *csc, 268f208b26eSSteve Longerbeam enum v4l2_ycbcr_encoding in_enc, 269f208b26eSSteve Longerbeam enum v4l2_quantization in_quant, 270f208b26eSSteve Longerbeam enum ipu_color_space in_cs, 271f208b26eSSteve Longerbeam enum v4l2_ycbcr_encoding out_enc, 272f208b26eSSteve Longerbeam enum v4l2_quantization out_quant, 273f208b26eSSteve Longerbeam enum ipu_color_space out_cs) 274f208b26eSSteve Longerbeam { 275f208b26eSSteve Longerbeam ipu_ic_fill_colorspace(&csc->in_cs, in_enc, in_quant, in_cs); 276f208b26eSSteve Longerbeam ipu_ic_fill_colorspace(&csc->out_cs, out_enc, out_quant, out_cs); 277f208b26eSSteve Longerbeam 278f208b26eSSteve Longerbeam return __ipu_ic_calc_csc(csc); 279f208b26eSSteve Longerbeam } 280f208b26eSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_ic_calc_csc); 281