1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Color space converter library 4 * 5 * Copyright (c) 2013 Texas Instruments Inc. 6 * 7 * David Griego, <dagriego@biglakesoftware.com> 8 * Dale Farnsworth, <dale@farnsworth.org> 9 * Archit Taneja, <archit@ti.com> 10 */ 11 12 #include <linux/err.h> 13 #include <linux/io.h> 14 #include <linux/module.h> 15 #include <linux/platform_device.h> 16 #include <linux/slab.h> 17 #include <linux/videodev2.h> 18 #include <media/v4l2-common.h> 19 20 #include "csc.h" 21 22 /* 23 * 12 coefficients in the order: 24 * a0, b0, c0, a1, b1, c1, a2, b2, c2, d0, d1, d2 25 */ 26 struct quantization { 27 u16 coeff[12]; 28 }; 29 30 struct colorspace { 31 struct quantization limited; 32 struct quantization full; 33 }; 34 35 struct encoding_direction { 36 struct colorspace r601; 37 struct colorspace r709; 38 }; 39 40 struct csc_coeffs { 41 struct encoding_direction y2r; 42 struct encoding_direction r2y; 43 }; 44 45 /* default colorspace coefficients */ 46 static struct csc_coeffs csc_coeffs = { 47 .y2r = { 48 .r601 = { 49 .limited = { 50 { /* SDTV */ 51 0x0400, 0x0000, 0x057D, 0x0400, 0x1EA7, 0x1D35, 52 0x0400, 0x06EF, 0x1FFE, 0x0D40, 0x0210, 0x0C88, 53 } 54 }, 55 .full = { 56 { /* SDTV */ 57 0x04A8, 0x1FFE, 0x0662, 0x04A8, 0x1E6F, 0x1CBF, 58 0x04A8, 0x0812, 0x1FFF, 0x0C84, 0x0220, 0x0BAC, 59 } 60 }, 61 }, 62 .r709 = { 63 .limited = { 64 { /* HDTV */ 65 0x0400, 0x0000, 0x0629, 0x0400, 0x1F45, 0x1E2B, 66 0x0400, 0x0742, 0x0000, 0x0CEC, 0x0148, 0x0C60, 67 } 68 }, 69 .full = { 70 { /* HDTV */ 71 0x04A8, 0x0000, 0x072C, 0x04A8, 0x1F26, 0x1DDE, 72 0x04A8, 0x0873, 0x0000, 0x0C20, 0x0134, 0x0B7C, 73 } 74 }, 75 }, 76 }, 77 .r2y = { 78 .r601 = { 79 .limited = { 80 { /* SDTV */ 81 0x0132, 0x0259, 0x0075, 0x1F50, 0x1EA5, 0x020B, 82 0x020B, 0x1E4A, 0x1FAB, 0x0000, 0x0200, 0x0200, 83 } 84 }, 85 .full = { 86 { /* SDTV */ 87 0x0107, 0x0204, 0x0064, 0x1F68, 0x1ED6, 0x01C2, 88 0x01C2, 0x1E87, 0x1FB7, 0x0040, 0x0200, 0x0200, 89 } 90 }, 91 }, 92 .r709 = { 93 .limited = { 94 { /* HDTV */ 95 0x00DA, 0x02DC, 0x004A, 0x1F88, 0x1E6C, 0x020C, 96 0x020C, 0x1E24, 0x1FD0, 0x0000, 0x0200, 0x0200, 97 } 98 }, 99 .full = { 100 { /* HDTV */ 101 0x00bb, 0x0275, 0x003f, 0x1f99, 0x1ea5, 0x01c2, 102 0x01c2, 0x1e67, 0x1fd7, 0x0040, 0x0200, 0x0200, 103 } 104 }, 105 }, 106 }, 107 108 }; 109 110 void csc_dump_regs(struct csc_data *csc) 111 { 112 struct device *dev = &csc->pdev->dev; 113 114 #define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, \ 115 ioread32(csc->base + CSC_##r)) 116 117 dev_dbg(dev, "CSC Registers @ %pa:\n", &csc->res->start); 118 119 DUMPREG(CSC00); 120 DUMPREG(CSC01); 121 DUMPREG(CSC02); 122 DUMPREG(CSC03); 123 DUMPREG(CSC04); 124 DUMPREG(CSC05); 125 126 #undef DUMPREG 127 } 128 EXPORT_SYMBOL(csc_dump_regs); 129 130 void csc_set_coeff_bypass(struct csc_data *csc, u32 *csc_reg5) 131 { 132 *csc_reg5 |= CSC_BYPASS; 133 } 134 EXPORT_SYMBOL(csc_set_coeff_bypass); 135 136 /* 137 * set the color space converter coefficient shadow register values 138 */ 139 void csc_set_coeff(struct csc_data *csc, u32 *csc_reg0, 140 struct v4l2_format *src_fmt, struct v4l2_format *dst_fmt) 141 { 142 u32 *csc_reg5 = csc_reg0 + 5; 143 u32 *shadow_csc = csc_reg0; 144 u16 *coeff, *end_coeff; 145 const struct v4l2_pix_format *pix; 146 const struct v4l2_pix_format_mplane *mp; 147 const struct v4l2_format_info *src_finfo, *dst_finfo; 148 enum v4l2_ycbcr_encoding src_ycbcr_enc, dst_ycbcr_enc; 149 enum v4l2_quantization src_quantization, dst_quantization; 150 u32 src_pixelformat, dst_pixelformat; 151 152 if (V4L2_TYPE_IS_MULTIPLANAR(src_fmt->type)) { 153 mp = &src_fmt->fmt.pix_mp; 154 src_pixelformat = mp->pixelformat; 155 src_ycbcr_enc = mp->ycbcr_enc; 156 src_quantization = mp->quantization; 157 } else { 158 pix = &src_fmt->fmt.pix; 159 src_pixelformat = pix->pixelformat; 160 src_ycbcr_enc = pix->ycbcr_enc; 161 src_quantization = pix->quantization; 162 } 163 164 if (V4L2_TYPE_IS_MULTIPLANAR(dst_fmt->type)) { 165 mp = &dst_fmt->fmt.pix_mp; 166 dst_pixelformat = mp->pixelformat; 167 dst_ycbcr_enc = mp->ycbcr_enc; 168 dst_quantization = mp->quantization; 169 } else { 170 pix = &dst_fmt->fmt.pix; 171 dst_pixelformat = pix->pixelformat; 172 dst_ycbcr_enc = pix->ycbcr_enc; 173 dst_quantization = pix->quantization; 174 } 175 176 src_finfo = v4l2_format_info(src_pixelformat); 177 dst_finfo = v4l2_format_info(dst_pixelformat); 178 179 if (v4l2_is_format_yuv(src_finfo) && 180 v4l2_is_format_rgb(dst_finfo)) { 181 /* Y2R */ 182 183 /* 184 * These are not the standard default values but are 185 * set this way for historical compatibility 186 */ 187 if (src_ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) 188 src_ycbcr_enc = V4L2_YCBCR_ENC_601; 189 190 if (src_quantization == V4L2_QUANTIZATION_DEFAULT) 191 src_quantization = V4L2_QUANTIZATION_FULL_RANGE; 192 193 if (src_ycbcr_enc == V4L2_YCBCR_ENC_601) { 194 if (src_quantization == V4L2_QUANTIZATION_FULL_RANGE) 195 coeff = csc_coeffs.y2r.r601.full.coeff; 196 else 197 coeff = csc_coeffs.y2r.r601.limited.coeff; 198 } else if (src_ycbcr_enc == V4L2_YCBCR_ENC_709) { 199 if (src_quantization == V4L2_QUANTIZATION_FULL_RANGE) 200 coeff = csc_coeffs.y2r.r709.full.coeff; 201 else 202 coeff = csc_coeffs.y2r.r709.limited.coeff; 203 } else { 204 /* Should never reach this, but it keeps gcc happy */ 205 coeff = csc_coeffs.y2r.r601.full.coeff; 206 } 207 } else if (v4l2_is_format_rgb(src_finfo) && 208 v4l2_is_format_yuv(dst_finfo)) { 209 /* R2Y */ 210 211 /* 212 * These are not the standard default values but are 213 * set this way for historical compatibility 214 */ 215 if (dst_ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) 216 dst_ycbcr_enc = V4L2_YCBCR_ENC_601; 217 218 if (dst_quantization == V4L2_QUANTIZATION_DEFAULT) 219 dst_quantization = V4L2_QUANTIZATION_FULL_RANGE; 220 221 if (dst_ycbcr_enc == V4L2_YCBCR_ENC_601) { 222 if (dst_quantization == V4L2_QUANTIZATION_FULL_RANGE) 223 coeff = csc_coeffs.r2y.r601.full.coeff; 224 else 225 coeff = csc_coeffs.r2y.r601.limited.coeff; 226 } else if (dst_ycbcr_enc == V4L2_YCBCR_ENC_709) { 227 if (dst_quantization == V4L2_QUANTIZATION_FULL_RANGE) 228 coeff = csc_coeffs.r2y.r709.full.coeff; 229 else 230 coeff = csc_coeffs.r2y.r709.limited.coeff; 231 } else { 232 /* Should never reach this, but it keeps gcc happy */ 233 coeff = csc_coeffs.r2y.r601.full.coeff; 234 } 235 } else { 236 *csc_reg5 |= CSC_BYPASS; 237 return; 238 } 239 240 end_coeff = coeff + 12; 241 242 for (; coeff < end_coeff; coeff += 2) 243 *shadow_csc++ = (*(coeff + 1) << 16) | *coeff; 244 } 245 EXPORT_SYMBOL(csc_set_coeff); 246 247 struct csc_data *csc_create(struct platform_device *pdev, const char *res_name) 248 { 249 struct csc_data *csc; 250 251 dev_dbg(&pdev->dev, "csc_create\n"); 252 253 csc = devm_kzalloc(&pdev->dev, sizeof(*csc), GFP_KERNEL); 254 if (!csc) { 255 dev_err(&pdev->dev, "couldn't alloc csc_data\n"); 256 return ERR_PTR(-ENOMEM); 257 } 258 259 csc->pdev = pdev; 260 261 csc->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 262 res_name); 263 if (csc->res == NULL) { 264 dev_err(&pdev->dev, "missing '%s' platform resources data\n", 265 res_name); 266 return ERR_PTR(-ENODEV); 267 } 268 269 csc->base = devm_ioremap_resource(&pdev->dev, csc->res); 270 if (IS_ERR(csc->base)) 271 return ERR_CAST(csc->base); 272 273 return csc; 274 } 275 EXPORT_SYMBOL(csc_create); 276 277 MODULE_DESCRIPTION("TI VIP/VPE Color Space Converter"); 278 MODULE_AUTHOR("Texas Instruments Inc."); 279 MODULE_LICENSE("GPL v2"); 280