summaryrefslogtreecommitdiff
path: root/drivers/media/platform/ti-vpe/csc.c
blob: f0c45d187b5f9eb013c7d0ab975f4fad8a26f5a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Color space converter library
 *
 * Copyright (c) 2013 Texas Instruments Inc.
 *
 * David Griego, <dagriego@biglakesoftware.com>
 * Dale Farnsworth, <dale@farnsworth.org>
 * Archit Taneja, <archit@ti.com>
 */

#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/videodev2.h>

#include "csc.h"

/*
 * 16 coefficients in the order:
 * a0, b0, c0, a1, b1, c1, a2, b2, c2, d0, d1, d2
 * (we may need to pass non-default values from user space later on, we might
 * need to make the coefficient struct more easy to populate)
 */
struct colorspace_coeffs {
	u16	sd[12];
	u16	hd[12];
};

/* VIDEO_RANGE: limited range, GRAPHICS_RANGE: full range */
#define	CSC_COEFFS_VIDEO_RANGE_Y2R	0
#define	CSC_COEFFS_GRAPHICS_RANGE_Y2R	1
#define	CSC_COEFFS_VIDEO_RANGE_R2Y	2
#define	CSC_COEFFS_GRAPHICS_RANGE_R2Y	3

/* default colorspace coefficients */
static struct colorspace_coeffs colorspace_coeffs[4] = {
	[CSC_COEFFS_VIDEO_RANGE_Y2R] = {
		{
			/* SDTV */
			0x0400, 0x0000, 0x057D, 0x0400, 0x1EA7, 0x1D35,
			0x0400, 0x06EF, 0x1FFE, 0x0D40, 0x0210, 0x0C88,
		},
		{
			/* HDTV */
			0x0400, 0x0000, 0x0629, 0x0400, 0x1F45, 0x1E2B,
			0x0400, 0x0742, 0x0000, 0x0CEC, 0x0148, 0x0C60,
		},
	},
	[CSC_COEFFS_GRAPHICS_RANGE_Y2R] = {
		{
			/* SDTV */
			0x04A8, 0x1FFE, 0x0662, 0x04A8, 0x1E6F, 0x1CBF,
			0x04A8, 0x0812, 0x1FFF, 0x0C84, 0x0220, 0x0BAC,
		},
		{
			/* HDTV */
			0x04A8, 0x0000, 0x072C, 0x04A8, 0x1F26, 0x1DDE,
			0x04A8, 0x0873, 0x0000, 0x0C20, 0x0134, 0x0B7C,
		},
	},
	[CSC_COEFFS_VIDEO_RANGE_R2Y] = {
		{
			/* SDTV */
			0x0132, 0x0259, 0x0075, 0x1F50, 0x1EA5, 0x020B,
			0x020B, 0x1E4A, 0x1FAB, 0x0000, 0x0200, 0x0200,
		},
		{
			/* HDTV */
			0x00DA, 0x02DC, 0x004A, 0x1F88, 0x1E6C, 0x020C,
			0x020C, 0x1E24, 0x1FD0, 0x0000, 0x0200, 0x0200,
		},
	},
	[CSC_COEFFS_GRAPHICS_RANGE_R2Y] = {
		{
			/* SDTV */
			0x0107, 0x0204, 0x0064, 0x1F68, 0x1ED6, 0x01C2,
			0x01C2, 0x1E87, 0x1FB7, 0x0040, 0x0200, 0x0200,
		},
		{
			/* HDTV */
			0x00bb, 0x0275, 0x003f, 0x1f99, 0x1ea5, 0x01c2,
			0x01c2, 0x1e67, 0x1fd7, 0x0040, 0x0200, 0x0200,
		},
	},
};

void csc_dump_regs(struct csc_data *csc)
{
	struct device *dev = &csc->pdev->dev;

#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, \
	ioread32(csc->base + CSC_##r))

	dev_dbg(dev, "CSC Registers @ %pa:\n", &csc->res->start);

	DUMPREG(CSC00);
	DUMPREG(CSC01);
	DUMPREG(CSC02);
	DUMPREG(CSC03);
	DUMPREG(CSC04);
	DUMPREG(CSC05);

#undef DUMPREG
}
EXPORT_SYMBOL(csc_dump_regs);

void csc_set_coeff_bypass(struct csc_data *csc, u32 *csc_reg5)
{
	*csc_reg5 |= CSC_BYPASS;
}
EXPORT_SYMBOL(csc_set_coeff_bypass);

/*
 * set the color space converter coefficient shadow register values
 */
void csc_set_coeff(struct csc_data *csc, u32 *csc_reg0,
		enum v4l2_colorspace src_colorspace,
		enum v4l2_colorspace dst_colorspace)
{
	u32 *csc_reg5 = csc_reg0 + 5;
	u32 *shadow_csc = csc_reg0;
	struct colorspace_coeffs *sd_hd_coeffs;
	u16 *coeff, *end_coeff;
	enum v4l2_colorspace yuv_colorspace;
	int sel = 0;

	/*
	 * support only graphics data range(full range) for now, a control ioctl
	 * would be nice here
	 */
	/* Y2R */
	if (dst_colorspace == V4L2_COLORSPACE_SRGB &&
			(src_colorspace == V4L2_COLORSPACE_SMPTE170M ||
			src_colorspace == V4L2_COLORSPACE_REC709)) {
		/* Y2R */
		sel = 1;
		yuv_colorspace = src_colorspace;
	} else if ((dst_colorspace == V4L2_COLORSPACE_SMPTE170M ||
			dst_colorspace == V4L2_COLORSPACE_REC709) &&
			src_colorspace == V4L2_COLORSPACE_SRGB) {
		/* R2Y */
		sel = 3;
		yuv_colorspace = dst_colorspace;
	} else {
		*csc_reg5 |= CSC_BYPASS;
		return;
	}

	sd_hd_coeffs = &colorspace_coeffs[sel];

	/* select between SD or HD coefficients */
	if (yuv_colorspace == V4L2_COLORSPACE_SMPTE170M)
		coeff = sd_hd_coeffs->sd;
	else
		coeff = sd_hd_coeffs->hd;

	end_coeff = coeff + 12;

	for (; coeff < end_coeff; coeff += 2)
		*shadow_csc++ = (*(coeff + 1) << 16) | *coeff;
}
EXPORT_SYMBOL(csc_set_coeff);

struct csc_data *csc_create(struct platform_device *pdev, const char *res_name)
{
	struct csc_data *csc;

	dev_dbg(&pdev->dev, "csc_create\n");

	csc = devm_kzalloc(&pdev->dev, sizeof(*csc), GFP_KERNEL);
	if (!csc) {
		dev_err(&pdev->dev, "couldn't alloc csc_data\n");
		return ERR_PTR(-ENOMEM);
	}

	csc->pdev = pdev;

	csc->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
						res_name);
	if (csc->res == NULL) {
		dev_err(&pdev->dev, "missing '%s' platform resources data\n",
			res_name);
		return ERR_PTR(-ENODEV);
	}

	csc->base = devm_ioremap_resource(&pdev->dev, csc->res);
	if (IS_ERR(csc->base)) {
		dev_err(&pdev->dev, "failed to ioremap\n");
		return ERR_CAST(csc->base);
	}

	return csc;
}
EXPORT_SYMBOL(csc_create);

MODULE_DESCRIPTION("TI VIP/VPE Color Space Converter");
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_LICENSE("GPL v2");