1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#include <linux/module.h>
22#include <linux/init.h>
23#include <linux/delay.h>
24#include <linux/types.h>
25#include <asm/uaccess.h>
26#include <linux/i2c.h>
27#include <linux/videodev2.h>
28#include <media/v4l2-device.h>
29#include <media/v4l2-chip-ident.h>
30#include <media/v4l2-i2c-drv.h>
31
32MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver");
33MODULE_AUTHOR("Laurent Pinchart");
34MODULE_LICENSE("GPL");
35
36static int debug;
37module_param(debug, int, 0);
38MODULE_PARM_DESC(debug, "Debug level (0-1)");
39
40
41#define VPX_TIMEOUT_COUNT 10
42
43
44
45struct vpx3220 {
46 struct v4l2_subdev sd;
47 unsigned char reg[255];
48
49 v4l2_std_id norm;
50 int ident;
51 int input;
52 int enable;
53 int bright;
54 int contrast;
55 int hue;
56 int sat;
57};
58
59static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd)
60{
61 return container_of(sd, struct vpx3220, sd);
62}
63
64static char *inputs[] = { "internal", "composite", "svideo" };
65
66
67
68static inline int vpx3220_write(struct v4l2_subdev *sd, u8 reg, u8 value)
69{
70 struct i2c_client *client = v4l2_get_subdevdata(sd);
71 struct vpx3220 *decoder = i2c_get_clientdata(client);
72
73 decoder->reg[reg] = value;
74 return i2c_smbus_write_byte_data(client, reg, value);
75}
76
77static inline int vpx3220_read(struct v4l2_subdev *sd, u8 reg)
78{
79 struct i2c_client *client = v4l2_get_subdevdata(sd);
80
81 return i2c_smbus_read_byte_data(client, reg);
82}
83
84static int vpx3220_fp_status(struct v4l2_subdev *sd)
85{
86 unsigned char status;
87 unsigned int i;
88
89 for (i = 0; i < VPX_TIMEOUT_COUNT; i++) {
90 status = vpx3220_read(sd, 0x29);
91
92 if (!(status & 4))
93 return 0;
94
95 udelay(10);
96
97 if (need_resched())
98 cond_resched();
99 }
100
101 return -1;
102}
103
104static int vpx3220_fp_write(struct v4l2_subdev *sd, u8 fpaddr, u16 data)
105{
106 struct i2c_client *client = v4l2_get_subdevdata(sd);
107
108
109 if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) {
110 v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
111 return -1;
112 }
113
114 if (vpx3220_fp_status(sd) < 0)
115 return -1;
116
117
118 if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) {
119 v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
120 return -1;
121 }
122
123 return 0;
124}
125
126static u16 vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr)
127{
128 struct i2c_client *client = v4l2_get_subdevdata(sd);
129 s16 data;
130
131
132 if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) {
133 v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
134 return -1;
135 }
136
137 if (vpx3220_fp_status(sd) < 0)
138 return -1;
139
140
141 data = i2c_smbus_read_word_data(client, 0x28);
142 if (data == -1) {
143 v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
144 return -1;
145 }
146
147 return swab16(data);
148}
149
150static int vpx3220_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len)
151{
152 u8 reg;
153 int ret = -1;
154
155 while (len >= 2) {
156 reg = *data++;
157 ret = vpx3220_write(sd, reg, *data++);
158 if (ret < 0)
159 break;
160 len -= 2;
161 }
162
163 return ret;
164}
165
166static int vpx3220_write_fp_block(struct v4l2_subdev *sd,
167 const u16 *data, unsigned int len)
168{
169 u8 reg;
170 int ret = 0;
171
172 while (len > 1) {
173 reg = *data++;
174 ret |= vpx3220_fp_write(sd, reg, *data++);
175 len -= 2;
176 }
177
178 return ret;
179}
180
181
182
183static const unsigned short init_ntsc[] = {
184 0x1c, 0x00,
185 0x88, 17,
186 0x89, 240,
187 0x8a, 240,
188 0x8b, 000,
189 0x8c, 640,
190 0x8d, 640,
191 0x8f, 0xc00,
192 0xf0, 0x73,
193
194 0xf2, 0x13,
195 0xe7, 0x1e1,
196
197};
198
199static const unsigned short init_pal[] = {
200 0x88, 23,
201 0x89, 288,
202
203 0x8a, 288,
204
205 0x8b, 16,
206 0x8c, 768,
207 0x8d, 784,
208
209 0x8f, 0xc00,
210 0xf0, 0x77,
211
212 0xf2, 0x3d1,
213 0xe7, 0x241,
214};
215
216static const unsigned short init_secam[] = {
217 0x88, 23,
218 0x89, 288,
219
220 0x8a, 288,
221
222 0x8b, 16,
223 0x8c, 768,
224 0x8d, 784,
225
226 0x8f, 0xc00,
227 0xf0, 0x77,
228
229 0xf2, 0x3d5,
230 0xe7, 0x241,
231};
232
233static const unsigned char init_common[] = {
234 0xf2, 0x00,
235 0x33, 0x0d,
236
237 0xd8, 0xa8,
238
239 0x20, 0x03,
240 0xe0, 0xff,
241 0xe1, 0x00,
242 0xe2, 0x7f,
243 0xe3, 0x80,
244 0xe4, 0x7f,
245 0xe5, 0x80,
246 0xe6, 0x00,
247 0xe7, 0xe0,
248
249 0xe8, 0xf8,
250
251 0xea, 0x18,
252
253 0xf0, 0x8a,
254
255 0xf1, 0x18,
256
257 0xf8, 0x12,
258
259 0xf9, 0x24,
260
261};
262
263static const unsigned short init_fp[] = {
264 0x59, 0,
265 0xa0, 2070,
266 0xa3, 0,
267 0xa4, 0,
268 0xa8, 30,
269 0xb2, 768,
270 0xbe, 27,
271 0x58, 0,
272 0x26, 0,
273 0x4b, 0x298,
274};
275
276
277static int vpx3220_init(struct v4l2_subdev *sd, u32 val)
278{
279 struct vpx3220 *decoder = to_vpx3220(sd);
280
281 vpx3220_write_block(sd, init_common, sizeof(init_common));
282 vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1);
283 if (decoder->norm & V4L2_STD_NTSC)
284 vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1);
285 else if (decoder->norm & V4L2_STD_PAL)
286 vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
287 else if (decoder->norm & V4L2_STD_SECAM)
288 vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1);
289 else
290 vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
291 return 0;
292}
293
294static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
295{
296 int res = V4L2_IN_ST_NO_SIGNAL, status;
297 v4l2_std_id std = 0;
298
299 status = vpx3220_fp_read(sd, 0x0f3);
300
301 v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status);
302
303 if (status < 0)
304 return status;
305
306 if ((status & 0x20) == 0) {
307 res = 0;
308
309 switch (status & 0x18) {
310 case 0x00:
311 case 0x10:
312 case 0x14:
313 case 0x18:
314 std = V4L2_STD_PAL;
315 break;
316
317 case 0x08:
318 std = V4L2_STD_SECAM;
319 break;
320
321 case 0x04:
322 case 0x0c:
323 case 0x1c:
324 std = V4L2_STD_NTSC;
325 break;
326 }
327 }
328 if (pstd)
329 *pstd = std;
330 if (pstatus)
331 *pstatus = status;
332 return 0;
333}
334
335static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
336{
337 v4l2_dbg(1, debug, sd, "querystd\n");
338 return vpx3220_status(sd, NULL, std);
339}
340
341static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status)
342{
343 v4l2_dbg(1, debug, sd, "g_input_status\n");
344 return vpx3220_status(sd, status, NULL);
345}
346
347static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
348{
349 struct vpx3220 *decoder = to_vpx3220(sd);
350 int temp_input;
351
352
353
354
355 temp_input = vpx3220_fp_read(sd, 0xf2);
356
357 v4l2_dbg(1, debug, sd, "s_std %llx\n", (unsigned long long)std);
358 if (std & V4L2_STD_NTSC) {
359 vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1);
360 v4l2_dbg(1, debug, sd, "norm switched to NTSC\n");
361 } else if (std & V4L2_STD_PAL) {
362 vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
363 v4l2_dbg(1, debug, sd, "norm switched to PAL\n");
364 } else if (std & V4L2_STD_SECAM) {
365 vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1);
366 v4l2_dbg(1, debug, sd, "norm switched to SECAM\n");
367 } else {
368 return -EINVAL;
369 }
370
371 decoder->norm = std;
372
373
374 vpx3220_fp_write(sd, 0xf2, temp_input | 0x0010);
375 udelay(10);
376 return 0;
377}
378
379static int vpx3220_s_routing(struct v4l2_subdev *sd,
380 u32 input, u32 output, u32 config)
381{
382 int data;
383
384
385
386
387
388 const int input_vals[3][2] = {
389 {0x0c, 0},
390 {0x0d, 0},
391 {0x0e, 1}
392 };
393
394 if (input < 0 || input > 2)
395 return -EINVAL;
396
397 v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]);
398
399 vpx3220_write(sd, 0x33, input_vals[input][0]);
400
401 data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020);
402 if (data < 0)
403 return data;
404
405 vpx3220_fp_write(sd, 0xf2,
406 data | (input_vals[input][1] << 5) | 0x0010);
407
408 udelay(10);
409 return 0;
410}
411
412static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable)
413{
414 v4l2_dbg(1, debug, sd, "s_stream %s\n", enable ? "on" : "off");
415
416 vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00));
417 return 0;
418}
419
420static int vpx3220_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
421{
422 switch (qc->id) {
423 case V4L2_CID_BRIGHTNESS:
424 v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
425 break;
426
427 case V4L2_CID_CONTRAST:
428 v4l2_ctrl_query_fill(qc, 0, 63, 1, 32);
429 break;
430
431 case V4L2_CID_SATURATION:
432 v4l2_ctrl_query_fill(qc, 0, 4095, 1, 2048);
433 break;
434
435 case V4L2_CID_HUE:
436 v4l2_ctrl_query_fill(qc, -512, 511, 1, 0);
437 break;
438
439 default:
440 return -EINVAL;
441 }
442 return 0;
443}
444
445static int vpx3220_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
446{
447 struct vpx3220 *decoder = to_vpx3220(sd);
448
449 switch (ctrl->id) {
450 case V4L2_CID_BRIGHTNESS:
451 ctrl->value = decoder->bright;
452 break;
453 case V4L2_CID_CONTRAST:
454 ctrl->value = decoder->contrast;
455 break;
456 case V4L2_CID_SATURATION:
457 ctrl->value = decoder->sat;
458 break;
459 case V4L2_CID_HUE:
460 ctrl->value = decoder->hue;
461 break;
462 default:
463 return -EINVAL;
464 }
465 return 0;
466}
467
468static int vpx3220_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
469{
470 struct vpx3220 *decoder = to_vpx3220(sd);
471
472 switch (ctrl->id) {
473 case V4L2_CID_BRIGHTNESS:
474 if (decoder->bright != ctrl->value) {
475 decoder->bright = ctrl->value;
476 vpx3220_write(sd, 0xe6, decoder->bright);
477 }
478 break;
479 case V4L2_CID_CONTRAST:
480 if (decoder->contrast != ctrl->value) {
481
482 decoder->contrast = ctrl->value;
483 vpx3220_write(sd, 0xe7, decoder->contrast + 192);
484 }
485 break;
486 case V4L2_CID_SATURATION:
487 if (decoder->sat != ctrl->value) {
488 decoder->sat = ctrl->value;
489 vpx3220_fp_write(sd, 0xa0, decoder->sat);
490 }
491 break;
492 case V4L2_CID_HUE:
493 if (decoder->hue != ctrl->value) {
494 decoder->hue = ctrl->value;
495 vpx3220_fp_write(sd, 0x1c, decoder->hue);
496 }
497 break;
498 default:
499 return -EINVAL;
500 }
501 return 0;
502}
503
504static int vpx3220_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
505{
506 struct vpx3220 *decoder = to_vpx3220(sd);
507 struct i2c_client *client = v4l2_get_subdevdata(sd);
508
509 return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0);
510}
511
512
513
514static const struct v4l2_subdev_core_ops vpx3220_core_ops = {
515 .g_chip_ident = vpx3220_g_chip_ident,
516 .init = vpx3220_init,
517 .g_ctrl = vpx3220_g_ctrl,
518 .s_ctrl = vpx3220_s_ctrl,
519 .queryctrl = vpx3220_queryctrl,
520 .s_std = vpx3220_s_std,
521};
522
523static const struct v4l2_subdev_video_ops vpx3220_video_ops = {
524 .s_routing = vpx3220_s_routing,
525 .s_stream = vpx3220_s_stream,
526 .querystd = vpx3220_querystd,
527 .g_input_status = vpx3220_g_input_status,
528};
529
530static const struct v4l2_subdev_ops vpx3220_ops = {
531 .core = &vpx3220_core_ops,
532 .video = &vpx3220_video_ops,
533};
534
535
536
537
538
539static int vpx3220_probe(struct i2c_client *client,
540 const struct i2c_device_id *id)
541{
542 struct vpx3220 *decoder;
543 struct v4l2_subdev *sd;
544 const char *name = NULL;
545 u8 ver;
546 u16 pn;
547
548
549 if (!i2c_check_functionality(client->adapter,
550 I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
551 return -ENODEV;
552
553 decoder = kzalloc(sizeof(struct vpx3220), GFP_KERNEL);
554 if (decoder == NULL)
555 return -ENOMEM;
556 sd = &decoder->sd;
557 v4l2_i2c_subdev_init(sd, client, &vpx3220_ops);
558 decoder->norm = V4L2_STD_PAL;
559 decoder->input = 0;
560 decoder->enable = 1;
561 decoder->bright = 32768;
562 decoder->contrast = 32768;
563 decoder->hue = 32768;
564 decoder->sat = 32768;
565
566 ver = i2c_smbus_read_byte_data(client, 0x00);
567 pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) +
568 i2c_smbus_read_byte_data(client, 0x01);
569 decoder->ident = V4L2_IDENT_VPX3220A;
570 if (ver == 0xec) {
571 switch (pn) {
572 case 0x4680:
573 name = "vpx3220a";
574 break;
575 case 0x4260:
576 name = "vpx3216b";
577 decoder->ident = V4L2_IDENT_VPX3216B;
578 break;
579 case 0x4280:
580 name = "vpx3214c";
581 decoder->ident = V4L2_IDENT_VPX3214C;
582 break;
583 }
584 }
585 if (name)
586 v4l2_info(sd, "%s found @ 0x%x (%s)\n", name,
587 client->addr << 1, client->adapter->name);
588 else
589 v4l2_info(sd, "chip (%02x:%04x) found @ 0x%x (%s)\n",
590 ver, pn, client->addr << 1, client->adapter->name);
591
592 vpx3220_write_block(sd, init_common, sizeof(init_common));
593 vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1);
594
595 vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
596 return 0;
597}
598
599static int vpx3220_remove(struct i2c_client *client)
600{
601 struct v4l2_subdev *sd = i2c_get_clientdata(client);
602
603 v4l2_device_unregister_subdev(sd);
604 kfree(to_vpx3220(sd));
605 return 0;
606}
607
608static const struct i2c_device_id vpx3220_id[] = {
609 { "vpx3220a", 0 },
610 { "vpx3216b", 0 },
611 { "vpx3214c", 0 },
612 { }
613};
614MODULE_DEVICE_TABLE(i2c, vpx3220_id);
615
616static struct v4l2_i2c_driver_data v4l2_i2c_data = {
617 .name = "vpx3220",
618 .probe = vpx3220_probe,
619 .remove = vpx3220_remove,
620 .id_table = vpx3220_id,
621};
622