1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#define DEBUG_VARIABLE debug
25
26#include <media/saa7146_vv.h>
27
28static int debug;
29module_param(debug, int, 0);
30MODULE_PARM_DESC(debug, "debug verbosity");
31
32
33static int hexium_num;
34
35#define HEXIUM_GEMINI 4
36#define HEXIUM_GEMINI_DUAL 5
37
38#define HEXIUM_INPUTS 9
39static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
40 { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
41 { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
42 { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
43 { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
44 { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
45 { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
46 { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
47 { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
48 { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
49};
50
51#define HEXIUM_AUDIOS 0
52
53struct hexium_data
54{
55 s8 adr;
56 u8 byte;
57};
58
59#define HEXIUM_CONTROLS 1
60static struct v4l2_queryctrl hexium_controls[] = {
61 { V4L2_CID_PRIVATE_BASE, V4L2_CTRL_TYPE_BOOLEAN, "B/W", 0, 1, 1, 0, 0 },
62};
63
64#define HEXIUM_GEMINI_V_1_0 1
65#define HEXIUM_GEMINI_DUAL_V_1_0 2
66
67struct hexium
68{
69 int type;
70
71 struct video_device *video_dev;
72 struct i2c_adapter i2c_adapter;
73
74 int cur_input;
75 v4l2_std_id cur_std;
76 int cur_bw;
77};
78
79
80static u8 hexium_ks0127b[0x100]={
81 0x00,0x52,0x30,0x40,0x01,0x0C,0x2A,0x10,
82 0x00,0x00,0x00,0x60,0x00,0x00,0x0F,0x06,
83 0x00,0x00,0xE4,0xC0,0x00,0x00,0x00,0x00,
84 0x14,0x9B,0xFE,0xFF,0xFC,0xFF,0x03,0x22,
85 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
86 0x00,0x00,0x00,0x00,0x00,0x2C,0x9B,0x00,
87 0x00,0x00,0x10,0x80,0x80,0x10,0x80,0x80,
88 0x01,0x04,0x00,0x00,0x00,0x29,0xC0,0x00,
89 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
90 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
91 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
92 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
93 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
94 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
95 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
96 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
97 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
98 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
99 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
100 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
101 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
102 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
103 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
104 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
105 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
106 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
107 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
108 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
109 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
110 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
111 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
112 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
113};
114
115static struct hexium_data hexium_pal[] = {
116 { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
117};
118
119static struct hexium_data hexium_pal_bw[] = {
120 { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
121};
122
123static struct hexium_data hexium_ntsc[] = {
124 { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
125};
126
127static struct hexium_data hexium_ntsc_bw[] = {
128 { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
129};
130
131static struct hexium_data hexium_secam[] = {
132 { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
133};
134
135static struct hexium_data hexium_input_select[] = {
136 { 0x02, 0x60 },
137 { 0x02, 0x64 },
138 { 0x02, 0x61 },
139 { 0x02, 0x65 },
140 { 0x02, 0x62 },
141 { 0x02, 0x66 },
142 { 0x02, 0x68 },
143 { 0x02, 0x69 },
144 { 0x02, 0x6A },
145};
146
147
148
149static struct saa7146_standard hexium_standards[] = {
150 {
151 .name = "PAL", .id = V4L2_STD_PAL,
152 .v_offset = 28, .v_field = 288,
153 .h_offset = 1, .h_pixels = 680,
154 .v_max_out = 576, .h_max_out = 768,
155 }, {
156 .name = "NTSC", .id = V4L2_STD_NTSC,
157 .v_offset = 28, .v_field = 240,
158 .h_offset = 1, .h_pixels = 640,
159 .v_max_out = 480, .h_max_out = 640,
160 }, {
161 .name = "SECAM", .id = V4L2_STD_SECAM,
162 .v_offset = 28, .v_field = 288,
163 .h_offset = 1, .h_pixels = 720,
164 .v_max_out = 576, .h_max_out = 768,
165 }
166};
167
168
169
170
171
172static int hexium_init_done(struct saa7146_dev *dev)
173{
174 struct hexium *hexium = (struct hexium *) dev->ext_priv;
175 union i2c_smbus_data data;
176 int i = 0;
177
178 DEB_D(("hexium_init_done called.\n"));
179
180
181 for (i = 0; i < sizeof(hexium_ks0127b); i++) {
182 data.byte = hexium_ks0127b[i];
183 if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
184 printk("hexium_gemini: hexium_init_done() failed for address 0x%02x\n", i);
185 }
186 }
187
188 return 0;
189}
190
191static int hexium_set_input(struct hexium *hexium, int input)
192{
193 union i2c_smbus_data data;
194
195 DEB_D((".\n"));
196
197 data.byte = hexium_input_select[input].byte;
198 if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, &data)) {
199 return -1;
200 }
201
202 return 0;
203}
204
205static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec)
206{
207 union i2c_smbus_data data;
208 int i = 0;
209
210 DEB_D((".\n"));
211
212 while (vdec[i].adr != -1) {
213 data.byte = vdec[i].byte;
214 if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, vdec[i].adr, I2C_SMBUS_BYTE_DATA, &data)) {
215 printk("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n", i);
216 return -1;
217 }
218 i++;
219 }
220 return 0;
221}
222
223static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
224{
225 DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
226
227 if (i->index >= HEXIUM_INPUTS)
228 return -EINVAL;
229
230 memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
231
232 DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
233 return 0;
234}
235
236static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
237{
238 struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
239 struct hexium *hexium = (struct hexium *) dev->ext_priv;
240
241 *input = hexium->cur_input;
242
243 DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
244 return 0;
245}
246
247static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
248{
249 struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
250 struct hexium *hexium = (struct hexium *) dev->ext_priv;
251
252 DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
253
254 if (input >= HEXIUM_INPUTS)
255 return -EINVAL;
256
257 hexium->cur_input = input;
258 hexium_set_input(hexium, input);
259 return 0;
260}
261
262
263
264
265static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc)
266{
267 struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
268 int i;
269
270 for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
271 if (hexium_controls[i].id == qc->id) {
272 *qc = hexium_controls[i];
273 DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id));
274 return 0;
275 }
276 }
277 return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc);
278}
279
280static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
281{
282 struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
283 struct hexium *hexium = (struct hexium *) dev->ext_priv;
284 int i;
285
286 for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
287 if (hexium_controls[i].id == vc->id)
288 break;
289 }
290
291 if (i < 0)
292 return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc);
293
294 if (vc->id == V4L2_CID_PRIVATE_BASE) {
295 vc->value = hexium->cur_bw;
296 DEB_D(("VIDIOC_G_CTRL BW:%d.\n", vc->value));
297 return 0;
298 }
299 return -EINVAL;
300}
301
302static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
303{
304 struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
305 struct hexium *hexium = (struct hexium *) dev->ext_priv;
306 int i = 0;
307
308 for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
309 if (hexium_controls[i].id == vc->id)
310 break;
311 }
312
313 if (i < 0)
314 return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc);
315
316 if (vc->id == V4L2_CID_PRIVATE_BASE)
317 hexium->cur_bw = vc->value;
318
319 DEB_D(("VIDIOC_S_CTRL BW:%d.\n", hexium->cur_bw));
320
321 if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
322 hexium_set_standard(hexium, hexium_pal);
323 return 0;
324 }
325 if (0 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
326 hexium_set_standard(hexium, hexium_ntsc);
327 return 0;
328 }
329 if (0 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) {
330 hexium_set_standard(hexium, hexium_secam);
331 return 0;
332 }
333 if (1 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
334 hexium_set_standard(hexium, hexium_pal_bw);
335 return 0;
336 }
337 if (1 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
338 hexium_set_standard(hexium, hexium_ntsc_bw);
339 return 0;
340 }
341 if (1 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std)
342
343 return -EINVAL;
344
345 return -EINVAL;
346}
347
348
349static struct saa7146_ext_vv vv_data;
350
351
352static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
353{
354 struct hexium *hexium = (struct hexium *) dev->ext_priv;
355 int ret;
356
357 DEB_EE((".\n"));
358
359 hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL);
360 if (NULL == hexium) {
361 printk("hexium_gemini: not enough kernel memory in hexium_attach().\n");
362 return -ENOMEM;
363 }
364 dev->ext_priv = hexium;
365
366
367 saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
368
369 hexium->i2c_adapter = (struct i2c_adapter) {
370 .name = "hexium gemini",
371 };
372 saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
373 if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
374 DEB_S(("cannot register i2c-device. skipping.\n"));
375 kfree(hexium);
376 return -EFAULT;
377 }
378
379
380 saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
381
382 saa7146_write(dev, DD1_INIT, 0x07000700);
383 saa7146_write(dev, DD1_STREAM_B, 0x00000000);
384 saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
385
386
387 hexium->cur_input = 0;
388 hexium_init_done(dev);
389
390 hexium_set_standard(hexium, hexium_pal);
391 hexium->cur_std = V4L2_STD_PAL;
392
393 hexium_set_input(hexium, 0);
394 hexium->cur_input = 0;
395
396 saa7146_vv_init(dev, &vv_data);
397 vv_data.ops.vidioc_queryctrl = vidioc_queryctrl;
398 vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl;
399 vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl;
400 vv_data.ops.vidioc_enum_input = vidioc_enum_input;
401 vv_data.ops.vidioc_g_input = vidioc_g_input;
402 vv_data.ops.vidioc_s_input = vidioc_s_input;
403 ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER);
404 if (ret < 0) {
405 printk("hexium_gemini: cannot register capture v4l2 device. skipping.\n");
406 return ret;
407 }
408
409 printk("hexium_gemini: found 'hexium gemini' frame grabber-%d.\n", hexium_num);
410 hexium_num++;
411
412 return 0;
413}
414
415static int hexium_detach(struct saa7146_dev *dev)
416{
417 struct hexium *hexium = (struct hexium *) dev->ext_priv;
418
419 DEB_EE(("dev:%p\n", dev));
420
421 saa7146_unregister_device(&hexium->video_dev, dev);
422 saa7146_vv_release(dev);
423
424 hexium_num--;
425
426 i2c_del_adapter(&hexium->i2c_adapter);
427 kfree(hexium);
428 return 0;
429}
430
431static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
432{
433 struct hexium *hexium = (struct hexium *) dev->ext_priv;
434
435 if (V4L2_STD_PAL == std->id) {
436 hexium_set_standard(hexium, hexium_pal);
437 hexium->cur_std = V4L2_STD_PAL;
438 return 0;
439 } else if (V4L2_STD_NTSC == std->id) {
440 hexium_set_standard(hexium, hexium_ntsc);
441 hexium->cur_std = V4L2_STD_NTSC;
442 return 0;
443 } else if (V4L2_STD_SECAM == std->id) {
444 hexium_set_standard(hexium, hexium_secam);
445 hexium->cur_std = V4L2_STD_SECAM;
446 return 0;
447 }
448
449 return -1;
450}
451
452static struct saa7146_extension hexium_extension;
453
454static struct saa7146_pci_extension_data hexium_gemini_4bnc = {
455 .ext_priv = "Hexium Gemini (4 BNC)",
456 .ext = &hexium_extension,
457};
458
459static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = {
460 .ext_priv = "Hexium Gemini Dual (4 BNC)",
461 .ext = &hexium_extension,
462};
463
464static struct pci_device_id pci_tbl[] = {
465 {
466 .vendor = PCI_VENDOR_ID_PHILIPS,
467 .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
468 .subvendor = 0x17c8,
469 .subdevice = 0x2401,
470 .driver_data = (unsigned long) &hexium_gemini_4bnc,
471 },
472 {
473 .vendor = PCI_VENDOR_ID_PHILIPS,
474 .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
475 .subvendor = 0x17c8,
476 .subdevice = 0x2402,
477 .driver_data = (unsigned long) &hexium_gemini_dual_4bnc,
478 },
479 {
480 .vendor = 0,
481 }
482};
483
484MODULE_DEVICE_TABLE(pci, pci_tbl);
485
486static struct saa7146_ext_vv vv_data = {
487 .inputs = HEXIUM_INPUTS,
488 .capabilities = 0,
489 .stds = &hexium_standards[0],
490 .num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard),
491 .std_callback = &std_callback,
492};
493
494static struct saa7146_extension hexium_extension = {
495 .name = "hexium gemini",
496 .flags = SAA7146_USE_I2C_IRQ,
497
498 .pci_tbl = &pci_tbl[0],
499 .module = THIS_MODULE,
500
501 .attach = hexium_attach,
502 .detach = hexium_detach,
503
504 .irq_mask = 0,
505 .irq_func = NULL,
506};
507
508static int __init hexium_init_module(void)
509{
510 if (0 != saa7146_register_extension(&hexium_extension)) {
511 DEB_S(("failed to register extension.\n"));
512 return -ENODEV;
513 }
514
515 return 0;
516}
517
518static void __exit hexium_cleanup_module(void)
519{
520 saa7146_unregister_extension(&hexium_extension);
521}
522
523module_init(hexium_init_module);
524module_exit(hexium_cleanup_module);
525
526MODULE_DESCRIPTION("video4linux-2 driver for Hexium Gemini frame grabber cards");
527MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
528MODULE_LICENSE("GPL");
529