1 // SPDX-License-Identifier: GPL-2.0-only
3 * Color space converter library
5 * Copyright (c) 2013 Texas Instruments Inc.
7 * David Griego, <dagriego@biglakesoftware.com>
8 * Dale Farnsworth, <dale@farnsworth.org>
9 * Archit Taneja, <archit@ti.com>
12 #include <linux/err.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>
23 * 12 coefficients in the order:
24 * a0, b0, c0, a1, b1, c1, a2, b2, c2, d0, d1, d2
31 struct quantization limited
;
32 struct quantization full
;
35 struct encoding_direction
{
36 struct colorspace r601
;
37 struct colorspace r709
;
41 struct encoding_direction y2r
;
42 struct encoding_direction r2y
;
45 /* default colorspace coefficients */
46 static struct csc_coeffs csc_coeffs
= {
51 0x0400, 0x0000, 0x057D, 0x0400, 0x1EA7, 0x1D35,
52 0x0400, 0x06EF, 0x1FFE, 0x0D40, 0x0210, 0x0C88,
57 0x04A8, 0x1FFE, 0x0662, 0x04A8, 0x1E6F, 0x1CBF,
58 0x04A8, 0x0812, 0x1FFF, 0x0C84, 0x0220, 0x0BAC,
65 0x0400, 0x0000, 0x0629, 0x0400, 0x1F45, 0x1E2B,
66 0x0400, 0x0742, 0x0000, 0x0CEC, 0x0148, 0x0C60,
71 0x04A8, 0x0000, 0x072C, 0x04A8, 0x1F26, 0x1DDE,
72 0x04A8, 0x0873, 0x0000, 0x0C20, 0x0134, 0x0B7C,
81 0x0132, 0x0259, 0x0075, 0x1F50, 0x1EA5, 0x020B,
82 0x020B, 0x1E4A, 0x1FAB, 0x0000, 0x0200, 0x0200,
87 0x0107, 0x0204, 0x0064, 0x1F68, 0x1ED6, 0x01C2,
88 0x01C2, 0x1E87, 0x1FB7, 0x0040, 0x0200, 0x0200,
95 0x00DA, 0x02DC, 0x004A, 0x1F88, 0x1E6C, 0x020C,
96 0x020C, 0x1E24, 0x1FD0, 0x0000, 0x0200, 0x0200,
101 0x00bb, 0x0275, 0x003f, 0x1f99, 0x1ea5, 0x01c2,
102 0x01c2, 0x1e67, 0x1fd7, 0x0040, 0x0200, 0x0200,
110 void csc_dump_regs(struct csc_data
*csc
)
112 struct device
*dev
= &csc
->pdev
->dev
;
114 #define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, \
115 ioread32(csc->base + CSC_##r))
117 dev_dbg(dev
, "CSC Registers @ %pa:\n", &csc
->res
->start
);
128 EXPORT_SYMBOL(csc_dump_regs
);
130 void csc_set_coeff_bypass(struct csc_data
*csc
, u32
*csc_reg5
)
132 *csc_reg5
|= CSC_BYPASS
;
134 EXPORT_SYMBOL(csc_set_coeff_bypass
);
137 * set the color space converter coefficient shadow register values
139 void csc_set_coeff(struct csc_data
*csc
, u32
*csc_reg0
,
140 struct v4l2_format
*src_fmt
, struct v4l2_format
*dst_fmt
)
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
;
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
;
158 pix
= &src_fmt
->fmt
.pix
;
159 src_pixelformat
= pix
->pixelformat
;
160 src_ycbcr_enc
= pix
->ycbcr_enc
;
161 src_quantization
= pix
->quantization
;
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
;
170 pix
= &dst_fmt
->fmt
.pix
;
171 dst_pixelformat
= pix
->pixelformat
;
172 dst_ycbcr_enc
= pix
->ycbcr_enc
;
173 dst_quantization
= pix
->quantization
;
176 src_finfo
= v4l2_format_info(src_pixelformat
);
177 dst_finfo
= v4l2_format_info(dst_pixelformat
);
179 if (v4l2_is_format_yuv(src_finfo
) &&
180 v4l2_is_format_rgb(dst_finfo
)) {
184 * These are not the standard default values but are
185 * set this way for historical compatibility
187 if (src_ycbcr_enc
== V4L2_YCBCR_ENC_DEFAULT
)
188 src_ycbcr_enc
= V4L2_YCBCR_ENC_601
;
190 if (src_quantization
== V4L2_QUANTIZATION_DEFAULT
)
191 src_quantization
= V4L2_QUANTIZATION_FULL_RANGE
;
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
;
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
;
202 coeff
= csc_coeffs
.y2r
.r709
.limited
.coeff
;
204 /* Should never reach this, but it keeps gcc happy */
205 coeff
= csc_coeffs
.y2r
.r601
.full
.coeff
;
207 } else if (v4l2_is_format_rgb(src_finfo
) &&
208 v4l2_is_format_yuv(dst_finfo
)) {
212 * These are not the standard default values but are
213 * set this way for historical compatibility
215 if (dst_ycbcr_enc
== V4L2_YCBCR_ENC_DEFAULT
)
216 dst_ycbcr_enc
= V4L2_YCBCR_ENC_601
;
218 if (dst_quantization
== V4L2_QUANTIZATION_DEFAULT
)
219 dst_quantization
= V4L2_QUANTIZATION_FULL_RANGE
;
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
;
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
;
230 coeff
= csc_coeffs
.r2y
.r709
.limited
.coeff
;
232 /* Should never reach this, but it keeps gcc happy */
233 coeff
= csc_coeffs
.r2y
.r601
.full
.coeff
;
236 *csc_reg5
|= CSC_BYPASS
;
240 end_coeff
= coeff
+ 12;
242 for (; coeff
< end_coeff
; coeff
+= 2)
243 *shadow_csc
++ = (*(coeff
+ 1) << 16) | *coeff
;
245 EXPORT_SYMBOL(csc_set_coeff
);
247 struct csc_data
*csc_create(struct platform_device
*pdev
, const char *res_name
)
249 struct csc_data
*csc
;
251 dev_dbg(&pdev
->dev
, "csc_create\n");
253 csc
= devm_kzalloc(&pdev
->dev
, sizeof(*csc
), GFP_KERNEL
);
255 dev_err(&pdev
->dev
, "couldn't alloc csc_data\n");
256 return ERR_PTR(-ENOMEM
);
261 csc
->res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
,
263 if (csc
->res
== NULL
) {
264 dev_err(&pdev
->dev
, "missing '%s' platform resources data\n",
266 return ERR_PTR(-ENODEV
);
269 csc
->base
= devm_ioremap_resource(&pdev
->dev
, csc
->res
);
270 if (IS_ERR(csc
->base
)) {
271 dev_err(&pdev
->dev
, "failed to ioremap\n");
272 return ERR_CAST(csc
->base
);
277 EXPORT_SYMBOL(csc_create
);
279 MODULE_DESCRIPTION("TI VIP/VPE Color Space Converter");
280 MODULE_AUTHOR("Texas Instruments Inc.");
281 MODULE_LICENSE("GPL v2");