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