1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * vpx3220a, vpx3216b & vpx3214c video decoder driver version 0.0.1
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2001 Laurent Pinchart <lpinchart@freegates.be>
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <linux/module.h>
9*4882a593Smuzhiyun #include <linux/init.h>
10*4882a593Smuzhiyun #include <linux/delay.h>
11*4882a593Smuzhiyun #include <linux/types.h>
12*4882a593Smuzhiyun #include <linux/slab.h>
13*4882a593Smuzhiyun #include <linux/uaccess.h>
14*4882a593Smuzhiyun #include <linux/i2c.h>
15*4882a593Smuzhiyun #include <linux/videodev2.h>
16*4882a593Smuzhiyun #include <media/v4l2-device.h>
17*4882a593Smuzhiyun #include <media/v4l2-ctrls.h>
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver");
20*4882a593Smuzhiyun MODULE_AUTHOR("Laurent Pinchart");
21*4882a593Smuzhiyun MODULE_LICENSE("GPL");
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun static int debug;
24*4882a593Smuzhiyun module_param(debug, int, 0);
25*4882a593Smuzhiyun MODULE_PARM_DESC(debug, "Debug level (0-1)");
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun #define VPX_TIMEOUT_COUNT 10
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun /* ----------------------------------------------------------------------- */
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun struct vpx3220 {
33*4882a593Smuzhiyun struct v4l2_subdev sd;
34*4882a593Smuzhiyun struct v4l2_ctrl_handler hdl;
35*4882a593Smuzhiyun unsigned char reg[255];
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun v4l2_std_id norm;
38*4882a593Smuzhiyun int input;
39*4882a593Smuzhiyun int enable;
40*4882a593Smuzhiyun };
41*4882a593Smuzhiyun
to_vpx3220(struct v4l2_subdev * sd)42*4882a593Smuzhiyun static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun return container_of(sd, struct vpx3220, sd);
45*4882a593Smuzhiyun }
46*4882a593Smuzhiyun
to_sd(struct v4l2_ctrl * ctrl)47*4882a593Smuzhiyun static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
48*4882a593Smuzhiyun {
49*4882a593Smuzhiyun return &container_of(ctrl->handler, struct vpx3220, hdl)->sd;
50*4882a593Smuzhiyun }
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun static char *inputs[] = { "internal", "composite", "svideo" };
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun /* ----------------------------------------------------------------------- */
55*4882a593Smuzhiyun
vpx3220_write(struct v4l2_subdev * sd,u8 reg,u8 value)56*4882a593Smuzhiyun static inline int vpx3220_write(struct v4l2_subdev *sd, u8 reg, u8 value)
57*4882a593Smuzhiyun {
58*4882a593Smuzhiyun struct i2c_client *client = v4l2_get_subdevdata(sd);
59*4882a593Smuzhiyun struct vpx3220 *decoder = i2c_get_clientdata(client);
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun decoder->reg[reg] = value;
62*4882a593Smuzhiyun return i2c_smbus_write_byte_data(client, reg, value);
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun
vpx3220_read(struct v4l2_subdev * sd,u8 reg)65*4882a593Smuzhiyun static inline int vpx3220_read(struct v4l2_subdev *sd, u8 reg)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun struct i2c_client *client = v4l2_get_subdevdata(sd);
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun return i2c_smbus_read_byte_data(client, reg);
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun
vpx3220_fp_status(struct v4l2_subdev * sd)72*4882a593Smuzhiyun static int vpx3220_fp_status(struct v4l2_subdev *sd)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun unsigned char status;
75*4882a593Smuzhiyun unsigned int i;
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun for (i = 0; i < VPX_TIMEOUT_COUNT; i++) {
78*4882a593Smuzhiyun status = vpx3220_read(sd, 0x29);
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun if (!(status & 4))
81*4882a593Smuzhiyun return 0;
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun udelay(10);
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun if (need_resched())
86*4882a593Smuzhiyun cond_resched();
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun return -1;
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun
vpx3220_fp_write(struct v4l2_subdev * sd,u8 fpaddr,u16 data)92*4882a593Smuzhiyun static int vpx3220_fp_write(struct v4l2_subdev *sd, u8 fpaddr, u16 data)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun struct i2c_client *client = v4l2_get_subdevdata(sd);
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun /* Write the 16-bit address to the FPWR register */
97*4882a593Smuzhiyun if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) {
98*4882a593Smuzhiyun v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
99*4882a593Smuzhiyun return -1;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun if (vpx3220_fp_status(sd) < 0)
103*4882a593Smuzhiyun return -1;
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun /* Write the 16-bit data to the FPDAT register */
106*4882a593Smuzhiyun if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) {
107*4882a593Smuzhiyun v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
108*4882a593Smuzhiyun return -1;
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun return 0;
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun
vpx3220_fp_read(struct v4l2_subdev * sd,u16 fpaddr)114*4882a593Smuzhiyun static int vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun struct i2c_client *client = v4l2_get_subdevdata(sd);
117*4882a593Smuzhiyun s16 data;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun /* Write the 16-bit address to the FPRD register */
120*4882a593Smuzhiyun if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) {
121*4882a593Smuzhiyun v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
122*4882a593Smuzhiyun return -1;
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun if (vpx3220_fp_status(sd) < 0)
126*4882a593Smuzhiyun return -1;
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun /* Read the 16-bit data from the FPDAT register */
129*4882a593Smuzhiyun data = i2c_smbus_read_word_data(client, 0x28);
130*4882a593Smuzhiyun if (data == -1) {
131*4882a593Smuzhiyun v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
132*4882a593Smuzhiyun return -1;
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun return swab16(data);
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun
vpx3220_write_block(struct v4l2_subdev * sd,const u8 * data,unsigned int len)138*4882a593Smuzhiyun static int vpx3220_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun u8 reg;
141*4882a593Smuzhiyun int ret = -1;
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun while (len >= 2) {
144*4882a593Smuzhiyun reg = *data++;
145*4882a593Smuzhiyun ret = vpx3220_write(sd, reg, *data++);
146*4882a593Smuzhiyun if (ret < 0)
147*4882a593Smuzhiyun break;
148*4882a593Smuzhiyun len -= 2;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun return ret;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun
vpx3220_write_fp_block(struct v4l2_subdev * sd,const u16 * data,unsigned int len)154*4882a593Smuzhiyun static int vpx3220_write_fp_block(struct v4l2_subdev *sd,
155*4882a593Smuzhiyun const u16 *data, unsigned int len)
156*4882a593Smuzhiyun {
157*4882a593Smuzhiyun u8 reg;
158*4882a593Smuzhiyun int ret = 0;
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun while (len > 1) {
161*4882a593Smuzhiyun reg = *data++;
162*4882a593Smuzhiyun ret |= vpx3220_fp_write(sd, reg, *data++);
163*4882a593Smuzhiyun len -= 2;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun return ret;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun /* ---------------------------------------------------------------------- */
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun static const unsigned short init_ntsc[] = {
172*4882a593Smuzhiyun 0x1c, 0x00, /* NTSC tint angle */
173*4882a593Smuzhiyun 0x88, 17, /* Window 1 vertical */
174*4882a593Smuzhiyun 0x89, 240, /* Vertical lines in */
175*4882a593Smuzhiyun 0x8a, 240, /* Vertical lines out */
176*4882a593Smuzhiyun 0x8b, 000, /* Horizontal begin */
177*4882a593Smuzhiyun 0x8c, 640, /* Horizontal length */
178*4882a593Smuzhiyun 0x8d, 640, /* Number of pixels */
179*4882a593Smuzhiyun 0x8f, 0xc00, /* Disable window 2 */
180*4882a593Smuzhiyun 0xf0, 0x73, /* 13.5 MHz transport, Forced
181*4882a593Smuzhiyun * mode, latch windows */
182*4882a593Smuzhiyun 0xf2, 0x13, /* NTSC M, composite input */
183*4882a593Smuzhiyun 0xe7, 0x1e1, /* Enable vertical standard
184*4882a593Smuzhiyun * locking @ 240 lines */
185*4882a593Smuzhiyun };
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun static const unsigned short init_pal[] = {
188*4882a593Smuzhiyun 0x88, 23, /* Window 1 vertical begin */
189*4882a593Smuzhiyun 0x89, 288, /* Vertical lines in (16 lines
190*4882a593Smuzhiyun * skipped by the VFE) */
191*4882a593Smuzhiyun 0x8a, 288, /* Vertical lines out (16 lines
192*4882a593Smuzhiyun * skipped by the VFE) */
193*4882a593Smuzhiyun 0x8b, 16, /* Horizontal begin */
194*4882a593Smuzhiyun 0x8c, 768, /* Horizontal length */
195*4882a593Smuzhiyun 0x8d, 784, /* Number of pixels
196*4882a593Smuzhiyun * Must be >= Horizontal begin + Horizontal length */
197*4882a593Smuzhiyun 0x8f, 0xc00, /* Disable window 2 */
198*4882a593Smuzhiyun 0xf0, 0x77, /* 13.5 MHz transport, Forced
199*4882a593Smuzhiyun * mode, latch windows */
200*4882a593Smuzhiyun 0xf2, 0x3d1, /* PAL B,G,H,I, composite input */
201*4882a593Smuzhiyun 0xe7, 0x241, /* PAL/SECAM set to 288 lines */
202*4882a593Smuzhiyun };
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun static const unsigned short init_secam[] = {
205*4882a593Smuzhiyun 0x88, 23, /* Window 1 vertical begin */
206*4882a593Smuzhiyun 0x89, 288, /* Vertical lines in (16 lines
207*4882a593Smuzhiyun * skipped by the VFE) */
208*4882a593Smuzhiyun 0x8a, 288, /* Vertical lines out (16 lines
209*4882a593Smuzhiyun * skipped by the VFE) */
210*4882a593Smuzhiyun 0x8b, 16, /* Horizontal begin */
211*4882a593Smuzhiyun 0x8c, 768, /* Horizontal length */
212*4882a593Smuzhiyun 0x8d, 784, /* Number of pixels
213*4882a593Smuzhiyun * Must be >= Horizontal begin + Horizontal length */
214*4882a593Smuzhiyun 0x8f, 0xc00, /* Disable window 2 */
215*4882a593Smuzhiyun 0xf0, 0x77, /* 13.5 MHz transport, Forced
216*4882a593Smuzhiyun * mode, latch windows */
217*4882a593Smuzhiyun 0xf2, 0x3d5, /* SECAM, composite input */
218*4882a593Smuzhiyun 0xe7, 0x241, /* PAL/SECAM set to 288 lines */
219*4882a593Smuzhiyun };
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun static const unsigned char init_common[] = {
222*4882a593Smuzhiyun 0xf2, 0x00, /* Disable all outputs */
223*4882a593Smuzhiyun 0x33, 0x0d, /* Luma : VIN2, Chroma : CIN
224*4882a593Smuzhiyun * (clamp off) */
225*4882a593Smuzhiyun 0xd8, 0xa8, /* HREF/VREF active high, VREF
226*4882a593Smuzhiyun * pulse = 2, Odd/Even flag */
227*4882a593Smuzhiyun 0x20, 0x03, /* IF compensation 0dB/oct */
228*4882a593Smuzhiyun 0xe0, 0xff, /* Open up all comparators */
229*4882a593Smuzhiyun 0xe1, 0x00,
230*4882a593Smuzhiyun 0xe2, 0x7f,
231*4882a593Smuzhiyun 0xe3, 0x80,
232*4882a593Smuzhiyun 0xe4, 0x7f,
233*4882a593Smuzhiyun 0xe5, 0x80,
234*4882a593Smuzhiyun 0xe6, 0x00, /* Brightness set to 0 */
235*4882a593Smuzhiyun 0xe7, 0xe0, /* Contrast to 1.0, noise shaping
236*4882a593Smuzhiyun * 10 to 8 2-bit error diffusion */
237*4882a593Smuzhiyun 0xe8, 0xf8, /* YUV422, CbCr binary offset,
238*4882a593Smuzhiyun * ... (p.32) */
239*4882a593Smuzhiyun 0xea, 0x18, /* LLC2 connected, output FIFO
240*4882a593Smuzhiyun * reset with VACTintern */
241*4882a593Smuzhiyun 0xf0, 0x8a, /* Half full level to 10, bus
242*4882a593Smuzhiyun * shuffler [7:0, 23:16, 15:8] */
243*4882a593Smuzhiyun 0xf1, 0x18, /* Single clock, sync mode, no
244*4882a593Smuzhiyun * FE delay, no HLEN counter */
245*4882a593Smuzhiyun 0xf8, 0x12, /* Port A, PIXCLK, HF# & FE#
246*4882a593Smuzhiyun * strength to 2 */
247*4882a593Smuzhiyun 0xf9, 0x24, /* Port B, HREF, VREF, PREF &
248*4882a593Smuzhiyun * ALPHA strength to 4 */
249*4882a593Smuzhiyun };
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun static const unsigned short init_fp[] = {
252*4882a593Smuzhiyun 0x59, 0,
253*4882a593Smuzhiyun 0xa0, 2070, /* ACC reference */
254*4882a593Smuzhiyun 0xa3, 0,
255*4882a593Smuzhiyun 0xa4, 0,
256*4882a593Smuzhiyun 0xa8, 30,
257*4882a593Smuzhiyun 0xb2, 768,
258*4882a593Smuzhiyun 0xbe, 27,
259*4882a593Smuzhiyun 0x58, 0,
260*4882a593Smuzhiyun 0x26, 0,
261*4882a593Smuzhiyun 0x4b, 0x298, /* PLL gain */
262*4882a593Smuzhiyun };
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun
vpx3220_init(struct v4l2_subdev * sd,u32 val)265*4882a593Smuzhiyun static int vpx3220_init(struct v4l2_subdev *sd, u32 val)
266*4882a593Smuzhiyun {
267*4882a593Smuzhiyun struct vpx3220 *decoder = to_vpx3220(sd);
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun vpx3220_write_block(sd, init_common, sizeof(init_common));
270*4882a593Smuzhiyun vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1);
271*4882a593Smuzhiyun if (decoder->norm & V4L2_STD_NTSC)
272*4882a593Smuzhiyun vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1);
273*4882a593Smuzhiyun else if (decoder->norm & V4L2_STD_PAL)
274*4882a593Smuzhiyun vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
275*4882a593Smuzhiyun else if (decoder->norm & V4L2_STD_SECAM)
276*4882a593Smuzhiyun vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1);
277*4882a593Smuzhiyun else
278*4882a593Smuzhiyun vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
279*4882a593Smuzhiyun return 0;
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun
vpx3220_status(struct v4l2_subdev * sd,u32 * pstatus,v4l2_std_id * pstd)282*4882a593Smuzhiyun static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
283*4882a593Smuzhiyun {
284*4882a593Smuzhiyun int res = V4L2_IN_ST_NO_SIGNAL, status;
285*4882a593Smuzhiyun v4l2_std_id std = pstd ? *pstd : V4L2_STD_ALL;
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun status = vpx3220_fp_read(sd, 0x0f3);
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status);
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun if (status < 0)
292*4882a593Smuzhiyun return status;
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun if ((status & 0x20) == 0) {
295*4882a593Smuzhiyun res = 0;
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun switch (status & 0x18) {
298*4882a593Smuzhiyun case 0x00:
299*4882a593Smuzhiyun case 0x10:
300*4882a593Smuzhiyun case 0x14:
301*4882a593Smuzhiyun case 0x18:
302*4882a593Smuzhiyun std &= V4L2_STD_PAL;
303*4882a593Smuzhiyun break;
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun case 0x08:
306*4882a593Smuzhiyun std &= V4L2_STD_SECAM;
307*4882a593Smuzhiyun break;
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun case 0x04:
310*4882a593Smuzhiyun case 0x0c:
311*4882a593Smuzhiyun case 0x1c:
312*4882a593Smuzhiyun std &= V4L2_STD_NTSC;
313*4882a593Smuzhiyun break;
314*4882a593Smuzhiyun }
315*4882a593Smuzhiyun } else {
316*4882a593Smuzhiyun std = V4L2_STD_UNKNOWN;
317*4882a593Smuzhiyun }
318*4882a593Smuzhiyun if (pstd)
319*4882a593Smuzhiyun *pstd = std;
320*4882a593Smuzhiyun if (pstatus)
321*4882a593Smuzhiyun *pstatus = res;
322*4882a593Smuzhiyun return 0;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun
vpx3220_querystd(struct v4l2_subdev * sd,v4l2_std_id * std)325*4882a593Smuzhiyun static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
326*4882a593Smuzhiyun {
327*4882a593Smuzhiyun v4l2_dbg(1, debug, sd, "querystd\n");
328*4882a593Smuzhiyun return vpx3220_status(sd, NULL, std);
329*4882a593Smuzhiyun }
330*4882a593Smuzhiyun
vpx3220_g_input_status(struct v4l2_subdev * sd,u32 * status)331*4882a593Smuzhiyun static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status)
332*4882a593Smuzhiyun {
333*4882a593Smuzhiyun v4l2_dbg(1, debug, sd, "g_input_status\n");
334*4882a593Smuzhiyun return vpx3220_status(sd, status, NULL);
335*4882a593Smuzhiyun }
336*4882a593Smuzhiyun
vpx3220_s_std(struct v4l2_subdev * sd,v4l2_std_id std)337*4882a593Smuzhiyun static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
338*4882a593Smuzhiyun {
339*4882a593Smuzhiyun struct vpx3220 *decoder = to_vpx3220(sd);
340*4882a593Smuzhiyun int temp_input;
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun /* Here we back up the input selection because it gets
343*4882a593Smuzhiyun overwritten when we fill the registers with the
344*4882a593Smuzhiyun chosen video norm */
345*4882a593Smuzhiyun temp_input = vpx3220_fp_read(sd, 0xf2);
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun v4l2_dbg(1, debug, sd, "s_std %llx\n", (unsigned long long)std);
348*4882a593Smuzhiyun if (std & V4L2_STD_NTSC) {
349*4882a593Smuzhiyun vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1);
350*4882a593Smuzhiyun v4l2_dbg(1, debug, sd, "norm switched to NTSC\n");
351*4882a593Smuzhiyun } else if (std & V4L2_STD_PAL) {
352*4882a593Smuzhiyun vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
353*4882a593Smuzhiyun v4l2_dbg(1, debug, sd, "norm switched to PAL\n");
354*4882a593Smuzhiyun } else if (std & V4L2_STD_SECAM) {
355*4882a593Smuzhiyun vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1);
356*4882a593Smuzhiyun v4l2_dbg(1, debug, sd, "norm switched to SECAM\n");
357*4882a593Smuzhiyun } else {
358*4882a593Smuzhiyun return -EINVAL;
359*4882a593Smuzhiyun }
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun decoder->norm = std;
362*4882a593Smuzhiyun
363*4882a593Smuzhiyun /* And here we set the backed up video input again */
364*4882a593Smuzhiyun vpx3220_fp_write(sd, 0xf2, temp_input | 0x0010);
365*4882a593Smuzhiyun udelay(10);
366*4882a593Smuzhiyun return 0;
367*4882a593Smuzhiyun }
368*4882a593Smuzhiyun
vpx3220_s_routing(struct v4l2_subdev * sd,u32 input,u32 output,u32 config)369*4882a593Smuzhiyun static int vpx3220_s_routing(struct v4l2_subdev *sd,
370*4882a593Smuzhiyun u32 input, u32 output, u32 config)
371*4882a593Smuzhiyun {
372*4882a593Smuzhiyun int data;
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun /* RJ: input = 0: ST8 (PCTV) input
375*4882a593Smuzhiyun input = 1: COMPOSITE input
376*4882a593Smuzhiyun input = 2: SVHS input */
377*4882a593Smuzhiyun
378*4882a593Smuzhiyun static const int input_vals[3][2] = {
379*4882a593Smuzhiyun {0x0c, 0},
380*4882a593Smuzhiyun {0x0d, 0},
381*4882a593Smuzhiyun {0x0e, 1}
382*4882a593Smuzhiyun };
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun if (input > 2)
385*4882a593Smuzhiyun return -EINVAL;
386*4882a593Smuzhiyun
387*4882a593Smuzhiyun v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]);
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun vpx3220_write(sd, 0x33, input_vals[input][0]);
390*4882a593Smuzhiyun
391*4882a593Smuzhiyun data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020);
392*4882a593Smuzhiyun if (data < 0)
393*4882a593Smuzhiyun return data;
394*4882a593Smuzhiyun /* 0x0010 is required to latch the setting */
395*4882a593Smuzhiyun vpx3220_fp_write(sd, 0xf2,
396*4882a593Smuzhiyun data | (input_vals[input][1] << 5) | 0x0010);
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun udelay(10);
399*4882a593Smuzhiyun return 0;
400*4882a593Smuzhiyun }
401*4882a593Smuzhiyun
vpx3220_s_stream(struct v4l2_subdev * sd,int enable)402*4882a593Smuzhiyun static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable)
403*4882a593Smuzhiyun {
404*4882a593Smuzhiyun v4l2_dbg(1, debug, sd, "s_stream %s\n", enable ? "on" : "off");
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00));
407*4882a593Smuzhiyun return 0;
408*4882a593Smuzhiyun }
409*4882a593Smuzhiyun
vpx3220_s_ctrl(struct v4l2_ctrl * ctrl)410*4882a593Smuzhiyun static int vpx3220_s_ctrl(struct v4l2_ctrl *ctrl)
411*4882a593Smuzhiyun {
412*4882a593Smuzhiyun struct v4l2_subdev *sd = to_sd(ctrl);
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun switch (ctrl->id) {
415*4882a593Smuzhiyun case V4L2_CID_BRIGHTNESS:
416*4882a593Smuzhiyun vpx3220_write(sd, 0xe6, ctrl->val);
417*4882a593Smuzhiyun return 0;
418*4882a593Smuzhiyun case V4L2_CID_CONTRAST:
419*4882a593Smuzhiyun /* Bit 7 and 8 is for noise shaping */
420*4882a593Smuzhiyun vpx3220_write(sd, 0xe7, ctrl->val + 192);
421*4882a593Smuzhiyun return 0;
422*4882a593Smuzhiyun case V4L2_CID_SATURATION:
423*4882a593Smuzhiyun vpx3220_fp_write(sd, 0xa0, ctrl->val);
424*4882a593Smuzhiyun return 0;
425*4882a593Smuzhiyun case V4L2_CID_HUE:
426*4882a593Smuzhiyun vpx3220_fp_write(sd, 0x1c, ctrl->val);
427*4882a593Smuzhiyun return 0;
428*4882a593Smuzhiyun }
429*4882a593Smuzhiyun return -EINVAL;
430*4882a593Smuzhiyun }
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun /* ----------------------------------------------------------------------- */
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun static const struct v4l2_ctrl_ops vpx3220_ctrl_ops = {
435*4882a593Smuzhiyun .s_ctrl = vpx3220_s_ctrl,
436*4882a593Smuzhiyun };
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun static const struct v4l2_subdev_core_ops vpx3220_core_ops = {
439*4882a593Smuzhiyun .init = vpx3220_init,
440*4882a593Smuzhiyun };
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun static const struct v4l2_subdev_video_ops vpx3220_video_ops = {
443*4882a593Smuzhiyun .s_std = vpx3220_s_std,
444*4882a593Smuzhiyun .s_routing = vpx3220_s_routing,
445*4882a593Smuzhiyun .s_stream = vpx3220_s_stream,
446*4882a593Smuzhiyun .querystd = vpx3220_querystd,
447*4882a593Smuzhiyun .g_input_status = vpx3220_g_input_status,
448*4882a593Smuzhiyun };
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun static const struct v4l2_subdev_ops vpx3220_ops = {
451*4882a593Smuzhiyun .core = &vpx3220_core_ops,
452*4882a593Smuzhiyun .video = &vpx3220_video_ops,
453*4882a593Smuzhiyun };
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun /* -----------------------------------------------------------------------
456*4882a593Smuzhiyun * Client management code
457*4882a593Smuzhiyun */
458*4882a593Smuzhiyun
vpx3220_probe(struct i2c_client * client,const struct i2c_device_id * id)459*4882a593Smuzhiyun static int vpx3220_probe(struct i2c_client *client,
460*4882a593Smuzhiyun const struct i2c_device_id *id)
461*4882a593Smuzhiyun {
462*4882a593Smuzhiyun struct vpx3220 *decoder;
463*4882a593Smuzhiyun struct v4l2_subdev *sd;
464*4882a593Smuzhiyun const char *name = NULL;
465*4882a593Smuzhiyun u8 ver;
466*4882a593Smuzhiyun u16 pn;
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun /* Check if the adapter supports the needed features */
469*4882a593Smuzhiyun if (!i2c_check_functionality(client->adapter,
470*4882a593Smuzhiyun I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
471*4882a593Smuzhiyun return -ENODEV;
472*4882a593Smuzhiyun
473*4882a593Smuzhiyun decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
474*4882a593Smuzhiyun if (decoder == NULL)
475*4882a593Smuzhiyun return -ENOMEM;
476*4882a593Smuzhiyun sd = &decoder->sd;
477*4882a593Smuzhiyun v4l2_i2c_subdev_init(sd, client, &vpx3220_ops);
478*4882a593Smuzhiyun decoder->norm = V4L2_STD_PAL;
479*4882a593Smuzhiyun decoder->input = 0;
480*4882a593Smuzhiyun decoder->enable = 1;
481*4882a593Smuzhiyun v4l2_ctrl_handler_init(&decoder->hdl, 4);
482*4882a593Smuzhiyun v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
483*4882a593Smuzhiyun V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
484*4882a593Smuzhiyun v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
485*4882a593Smuzhiyun V4L2_CID_CONTRAST, 0, 63, 1, 32);
486*4882a593Smuzhiyun v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
487*4882a593Smuzhiyun V4L2_CID_SATURATION, 0, 4095, 1, 2048);
488*4882a593Smuzhiyun v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
489*4882a593Smuzhiyun V4L2_CID_HUE, -512, 511, 1, 0);
490*4882a593Smuzhiyun sd->ctrl_handler = &decoder->hdl;
491*4882a593Smuzhiyun if (decoder->hdl.error) {
492*4882a593Smuzhiyun int err = decoder->hdl.error;
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun v4l2_ctrl_handler_free(&decoder->hdl);
495*4882a593Smuzhiyun return err;
496*4882a593Smuzhiyun }
497*4882a593Smuzhiyun v4l2_ctrl_handler_setup(&decoder->hdl);
498*4882a593Smuzhiyun
499*4882a593Smuzhiyun ver = i2c_smbus_read_byte_data(client, 0x00);
500*4882a593Smuzhiyun pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) +
501*4882a593Smuzhiyun i2c_smbus_read_byte_data(client, 0x01);
502*4882a593Smuzhiyun if (ver == 0xec) {
503*4882a593Smuzhiyun switch (pn) {
504*4882a593Smuzhiyun case 0x4680:
505*4882a593Smuzhiyun name = "vpx3220a";
506*4882a593Smuzhiyun break;
507*4882a593Smuzhiyun case 0x4260:
508*4882a593Smuzhiyun name = "vpx3216b";
509*4882a593Smuzhiyun break;
510*4882a593Smuzhiyun case 0x4280:
511*4882a593Smuzhiyun name = "vpx3214c";
512*4882a593Smuzhiyun break;
513*4882a593Smuzhiyun }
514*4882a593Smuzhiyun }
515*4882a593Smuzhiyun if (name)
516*4882a593Smuzhiyun v4l2_info(sd, "%s found @ 0x%x (%s)\n", name,
517*4882a593Smuzhiyun client->addr << 1, client->adapter->name);
518*4882a593Smuzhiyun else
519*4882a593Smuzhiyun v4l2_info(sd, "chip (%02x:%04x) found @ 0x%x (%s)\n",
520*4882a593Smuzhiyun ver, pn, client->addr << 1, client->adapter->name);
521*4882a593Smuzhiyun
522*4882a593Smuzhiyun vpx3220_write_block(sd, init_common, sizeof(init_common));
523*4882a593Smuzhiyun vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1);
524*4882a593Smuzhiyun /* Default to PAL */
525*4882a593Smuzhiyun vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
526*4882a593Smuzhiyun return 0;
527*4882a593Smuzhiyun }
528*4882a593Smuzhiyun
vpx3220_remove(struct i2c_client * client)529*4882a593Smuzhiyun static int vpx3220_remove(struct i2c_client *client)
530*4882a593Smuzhiyun {
531*4882a593Smuzhiyun struct v4l2_subdev *sd = i2c_get_clientdata(client);
532*4882a593Smuzhiyun struct vpx3220 *decoder = to_vpx3220(sd);
533*4882a593Smuzhiyun
534*4882a593Smuzhiyun v4l2_device_unregister_subdev(sd);
535*4882a593Smuzhiyun v4l2_ctrl_handler_free(&decoder->hdl);
536*4882a593Smuzhiyun
537*4882a593Smuzhiyun return 0;
538*4882a593Smuzhiyun }
539*4882a593Smuzhiyun
540*4882a593Smuzhiyun static const struct i2c_device_id vpx3220_id[] = {
541*4882a593Smuzhiyun { "vpx3220a", 0 },
542*4882a593Smuzhiyun { "vpx3216b", 0 },
543*4882a593Smuzhiyun { "vpx3214c", 0 },
544*4882a593Smuzhiyun { }
545*4882a593Smuzhiyun };
546*4882a593Smuzhiyun MODULE_DEVICE_TABLE(i2c, vpx3220_id);
547*4882a593Smuzhiyun
548*4882a593Smuzhiyun static struct i2c_driver vpx3220_driver = {
549*4882a593Smuzhiyun .driver = {
550*4882a593Smuzhiyun .name = "vpx3220",
551*4882a593Smuzhiyun },
552*4882a593Smuzhiyun .probe = vpx3220_probe,
553*4882a593Smuzhiyun .remove = vpx3220_remove,
554*4882a593Smuzhiyun .id_table = vpx3220_id,
555*4882a593Smuzhiyun };
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun module_i2c_driver(vpx3220_driver);
558