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