Add support for Rec.709 encoding and inverse encoding. The determination of the CSC coefficients based on the input/output colorspace parameters are moved to a new function calc_csc_coeffs(). Reported-by: Tim Harvey <tharvey@xxxxxxxxxxxxx> Signed-off-by: Steve Longerbeam <slongerbeam@xxxxxxxxx> --- Changes in v5: - moved API changes to a previous patch. - moved CSC coeff calc to new function calc_csc_coeffs(). Changes in v4: - fix compile error. Chnges in v3: - none. Changes in v2: - only return "Unsupported YCbCr encoding" error if inf != outf, since if inf == outf, the identity matrix can be used. Reported by Tim Harvey. --- drivers/gpu/ipu-v3/ipu-ic.c | 120 ++++++++++++++++++++++++++++-------- 1 file changed, 94 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 02043f23f411..012ea2239e97 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -214,6 +214,23 @@ static const struct ic_encode_coeff ic_encode_identity = { .scale = 2, }; +/* + * 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 ic_encode_coeff ic_encode_rgb2ycbcr_709 = { + .coeff = { + { 54, 183, 19 }, + { 483, 413, 128 }, + { 128, 396, 500 }, + }, + .offset = { 0, 512, 512 }, + .scale = 1, +}; + /* * Inverse BT.601 encoding from YUV full range to RGB full range: * @@ -237,28 +254,42 @@ static const struct ic_encode_coeff ic_encode_ycbcr2rgb_601 = { .scale = 2, }; -static int init_csc(struct ipu_ic *ic, - const struct ipu_ic_colorspace *in, - const struct ipu_ic_colorspace *out, - int csc_index) +/* + * 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 ic_encode_coeff ic_encode_ycbcr2rgb_709 = { + .coeff = { + { 128, 0, 202 }, + { 128, 488, 452 }, + { 128, 238, 0 }, + }, + .offset = { -403, 168, -475 }, + .scale = 2, +}; + +static int calc_csc_coeffs(struct ipu_ic_priv *priv, + struct ic_encode_coeff *coeff_out, + const struct ipu_ic_colorspace *in, + const struct ipu_ic_colorspace *out) { - struct ipu_ic_priv *priv = ic->priv; - const struct ic_encode_coeff *coeff; - u32 __iomem *base; - const u16 (*c)[3]; - const u16 *a; - u32 param; + const struct ic_encode_coeff *encode_coeff; + bool inverse_encode; if (in->colorspace != out->colorspace) { dev_err(priv->ipu->dev, "Cannot convert colorspaces\n"); return -ENOTSUPP; } - if (out->enc != V4L2_YCBCR_ENC_601) { - dev_err(priv->ipu->dev, "Only BT.601 encoding supported\n"); - return -ENOTSUPP; - } - if ((in->cs == IPUV3_COLORSPACE_YUV && in->quant != V4L2_QUANTIZATION_FULL_RANGE) || (out->cs == IPUV3_COLORSPACE_YUV && @@ -275,26 +306,63 @@ static int init_csc(struct ipu_ic *ic, return -ENOTSUPP; } + if (in->cs == out->cs) { + *coeff_out = ic_encode_identity; + + return 0; + } + + inverse_encode = (in->cs == IPUV3_COLORSPACE_YUV); + + switch (out->enc) { + case V4L2_YCBCR_ENC_601: + encode_coeff = inverse_encode ? + &ic_encode_ycbcr2rgb_601 : &ic_encode_rgb2ycbcr_601; + break; + case V4L2_YCBCR_ENC_709: + encode_coeff = inverse_encode ? + &ic_encode_ycbcr2rgb_709 : &ic_encode_rgb2ycbcr_709; + break; + default: + dev_err(priv->ipu->dev, "Unsupported YCbCr encoding\n"); + return -ENOTSUPP; + } + + *coeff_out = *encode_coeff; + + return 0; +} + +static int init_csc(struct ipu_ic *ic, + const struct ipu_ic_colorspace *in, + const struct ipu_ic_colorspace *out, + int csc_index) +{ + struct ipu_ic_priv *priv = ic->priv; + struct ic_encode_coeff coeff; + u32 __iomem *base; + const u16 (*c)[3]; + const u16 *a; + u32 param; + int ret; + + ret = calc_csc_coeffs(priv, &coeff, in, out); + if (ret) + return ret; + base = (u32 __iomem *) (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); - if (in->cs == out->cs) - coeff = &ic_encode_identity; - else if (in->cs == IPUV3_COLORSPACE_YUV) - coeff = &ic_encode_ycbcr2rgb_601; - else - coeff = &ic_encode_rgb2ycbcr_601; - /* Cast to unsigned */ - c = (const u16 (*)[3])coeff->coeff; - a = (const u16 *)coeff->offset; + c = (const u16 (*)[3])coeff.coeff; + a = (const u16 *)coeff.offset; param = ((a[0] & 0x1f) << 27) | ((c[0][0] & 0x1ff) << 18) | ((c[1][1] & 0x1ff) << 9) | (c[2][2] & 0x1ff); writel(param, base++); - param = ((a[0] & 0x1fe0) >> 5) | (coeff->scale << 8) | - (coeff->sat << 10); + param = ((a[0] & 0x1fe0) >> 5) | (coeff.scale << 8) | + (coeff.sat << 10); writel(param, base++); param = ((a[1] & 0x1f) << 27) | ((c[0][1] & 0x1ff) << 18) | -- 2.17.1