1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3    hexium_orion.c - v4l2 driver for the Hexium Orion frame grabber cards
4
5    Visit http://www.mihu.de/linux/saa7146/ and follow the link
6    to "hexium" for further details about this card.
7
8    Copyright (C) 2003 Michael Hunold <michael@mihu.de>
9
10*/
11
12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13
14#define DEBUG_VARIABLE debug
15
16#include <media/drv-intf/saa7146_vv.h>
17#include <linux/module.h>
18#include <linux/kernel.h>
19
20static int debug;
21module_param(debug, int, 0);
22MODULE_PARM_DESC(debug, "debug verbosity");
23
24/* global variables */
25static int hexium_num;
26
27#define HEXIUM_HV_PCI6_ORION		1
28#define HEXIUM_ORION_1SVHS_3BNC		2
29#define HEXIUM_ORION_4BNC		3
30
31#define HEXIUM_STD (V4L2_STD_PAL | V4L2_STD_SECAM | V4L2_STD_NTSC)
32#define HEXIUM_INPUTS	9
33static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
34	{ 0, "CVBS 1",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
35	{ 1, "CVBS 2",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
36	{ 2, "CVBS 3",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
37	{ 3, "CVBS 4",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
38	{ 4, "CVBS 5",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
39	{ 5, "CVBS 6",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
40	{ 6, "Y/C 1",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
41	{ 7, "Y/C 2",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
42	{ 8, "Y/C 3",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
43};
44
45#define HEXIUM_AUDIOS	0
46
47struct hexium_data
48{
49	s8 adr;
50	u8 byte;
51};
52
53struct hexium
54{
55	int type;
56	struct video_device	video_dev;
57	struct i2c_adapter	i2c_adapter;
58
59	int cur_input;	/* current input */
60};
61
62/* Philips SAA7110 decoder default registers */
63static u8 hexium_saa7110[53]={
64/*00*/ 0x4C,0x3C,0x0D,0xEF,0xBD,0xF0,0x00,0x00,
65/*08*/ 0xF8,0xF8,0x60,0x60,0x40,0x86,0x18,0x90,
66/*10*/ 0x00,0x2C,0x40,0x46,0x42,0x1A,0xFF,0xDA,
67/*18*/ 0xF0,0x8B,0x00,0x00,0x00,0x00,0x00,0x00,
68/*20*/ 0xD9,0x17,0x40,0x41,0x80,0x41,0x80,0x4F,
69/*28*/ 0xFE,0x01,0x0F,0x0F,0x03,0x01,0x81,0x03,
70/*30*/ 0x44,0x75,0x01,0x8C,0x03
71};
72
73static struct {
74	struct hexium_data data[8];
75} hexium_input_select[] = {
76{
77	{ /* cvbs 1 */
78		{ 0x06, 0x00 },
79		{ 0x20, 0xD9 },
80		{ 0x21, 0x17 }, // 0x16,
81		{ 0x22, 0x40 },
82		{ 0x2C, 0x03 },
83		{ 0x30, 0x44 },
84		{ 0x31, 0x75 }, // ??
85		{ 0x21, 0x16 }, // 0x03,
86	}
87}, {
88	{ /* cvbs 2 */
89		{ 0x06, 0x00 },
90		{ 0x20, 0x78 },
91		{ 0x21, 0x07 }, // 0x03,
92		{ 0x22, 0xD2 },
93		{ 0x2C, 0x83 },
94		{ 0x30, 0x60 },
95		{ 0x31, 0xB5 }, // ?
96		{ 0x21, 0x03 },
97	}
98}, {
99	{ /* cvbs 3 */
100		{ 0x06, 0x00 },
101		{ 0x20, 0xBA },
102		{ 0x21, 0x07 }, // 0x05,
103		{ 0x22, 0x91 },
104		{ 0x2C, 0x03 },
105		{ 0x30, 0x60 },
106		{ 0x31, 0xB5 }, // ??
107		{ 0x21, 0x05 }, // 0x03,
108	}
109}, {
110	{ /* cvbs 4 */
111		{ 0x06, 0x00 },
112		{ 0x20, 0xD8 },
113		{ 0x21, 0x17 }, // 0x16,
114		{ 0x22, 0x40 },
115		{ 0x2C, 0x03 },
116		{ 0x30, 0x44 },
117		{ 0x31, 0x75 }, // ??
118		{ 0x21, 0x16 }, // 0x03,
119	}
120}, {
121	{ /* cvbs 5 */
122		{ 0x06, 0x00 },
123		{ 0x20, 0xB8 },
124		{ 0x21, 0x07 }, // 0x05,
125		{ 0x22, 0x91 },
126		{ 0x2C, 0x03 },
127		{ 0x30, 0x60 },
128		{ 0x31, 0xB5 }, // ??
129		{ 0x21, 0x05 }, // 0x03,
130	}
131}, {
132	{ /* cvbs 6 */
133		{ 0x06, 0x00 },
134		{ 0x20, 0x7C },
135		{ 0x21, 0x07 }, // 0x03
136		{ 0x22, 0xD2 },
137		{ 0x2C, 0x83 },
138		{ 0x30, 0x60 },
139		{ 0x31, 0xB5 }, // ??
140		{ 0x21, 0x03 },
141	}
142}, {
143	{ /* y/c 1 */
144		{ 0x06, 0x80 },
145		{ 0x20, 0x59 },
146		{ 0x21, 0x17 },
147		{ 0x22, 0x42 },
148		{ 0x2C, 0xA3 },
149		{ 0x30, 0x44 },
150		{ 0x31, 0x75 },
151		{ 0x21, 0x12 },
152	}
153}, {
154	{ /* y/c 2 */
155		{ 0x06, 0x80 },
156		{ 0x20, 0x9A },
157		{ 0x21, 0x17 },
158		{ 0x22, 0xB1 },
159		{ 0x2C, 0x13 },
160		{ 0x30, 0x60 },
161		{ 0x31, 0xB5 },
162		{ 0x21, 0x14 },
163	}
164}, {
165	{ /* y/c 3 */
166		{ 0x06, 0x80 },
167		{ 0x20, 0x3C },
168		{ 0x21, 0x27 },
169		{ 0x22, 0xC1 },
170		{ 0x2C, 0x23 },
171		{ 0x30, 0x44 },
172		{ 0x31, 0x75 },
173		{ 0x21, 0x21 },
174	}
175}
176};
177
178static struct saa7146_standard hexium_standards[] = {
179	{
180		.name	= "PAL",	.id	= V4L2_STD_PAL,
181		.v_offset	= 16,	.v_field	= 288,
182		.h_offset	= 1,	.h_pixels	= 680,
183		.v_max_out	= 576,	.h_max_out	= 768,
184	}, {
185		.name	= "NTSC",	.id	= V4L2_STD_NTSC,
186		.v_offset	= 16,	.v_field	= 240,
187		.h_offset	= 1,	.h_pixels	= 640,
188		.v_max_out	= 480,	.h_max_out	= 640,
189	}, {
190		.name	= "SECAM",	.id	= V4L2_STD_SECAM,
191		.v_offset	= 16,	.v_field	= 288,
192		.h_offset	= 1,	.h_pixels	= 720,
193		.v_max_out	= 576,	.h_max_out	= 768,
194	}
195};
196
197/* this is only called for old HV-PCI6/Orion cards
198   without eeprom */
199static int hexium_probe(struct saa7146_dev *dev)
200{
201	struct hexium *hexium = NULL;
202	union i2c_smbus_data data;
203	int err = 0;
204
205	DEB_EE("\n");
206
207	/* there are no hexium orion cards with revision 0 saa7146s */
208	if (0 == dev->revision) {
209		return -EFAULT;
210	}
211
212	hexium = kzalloc(sizeof(*hexium), GFP_KERNEL);
213	if (!hexium)
214		return -ENOMEM;
215
216	/* enable i2c-port pins */
217	saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
218
219	saa7146_write(dev, DD1_INIT, 0x01000100);
220	saa7146_write(dev, DD1_STREAM_B, 0x00000000);
221	saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
222
223	strscpy(hexium->i2c_adapter.name, "hexium orion",
224		sizeof(hexium->i2c_adapter.name));
225	saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
226	if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
227		DEB_S("cannot register i2c-device. skipping.\n");
228		kfree(hexium);
229		return -EFAULT;
230	}
231
232	/* set SAA7110 control GPIO 0 */
233	saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTHI);
234	/*  set HWControl GPIO number 2 */
235	saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
236
237	mdelay(10);
238
239	/* detect newer Hexium Orion cards by subsystem ids */
240	if (0x17c8 == dev->pci->subsystem_vendor && 0x0101 == dev->pci->subsystem_device) {
241		pr_info("device is a Hexium Orion w/ 1 SVHS + 3 BNC inputs\n");
242		/* we store the pointer in our private data field */
243		dev->ext_priv = hexium;
244		hexium->type = HEXIUM_ORION_1SVHS_3BNC;
245		return 0;
246	}
247
248	if (0x17c8 == dev->pci->subsystem_vendor && 0x2101 == dev->pci->subsystem_device) {
249		pr_info("device is a Hexium Orion w/ 4 BNC inputs\n");
250		/* we store the pointer in our private data field */
251		dev->ext_priv = hexium;
252		hexium->type = HEXIUM_ORION_4BNC;
253		return 0;
254	}
255
256	/* check if this is an old hexium Orion card by looking at
257	   a saa7110 at address 0x4e */
258	err = i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_READ,
259			     0x00, I2C_SMBUS_BYTE_DATA, &data);
260	if (err == 0) {
261		pr_info("device is a Hexium HV-PCI6/Orion (old)\n");
262		/* we store the pointer in our private data field */
263		dev->ext_priv = hexium;
264		hexium->type = HEXIUM_HV_PCI6_ORION;
265		return 0;
266	}
267
268	i2c_del_adapter(&hexium->i2c_adapter);
269	kfree(hexium);
270	return -EFAULT;
271}
272
273/* bring hardware to a sane state. this has to be done, just in case someone
274   wants to capture from this device before it has been properly initialized.
275   the capture engine would badly fail, because no valid signal arrives on the
276   saa7146, thus leading to timeouts and stuff. */
277static int hexium_init_done(struct saa7146_dev *dev)
278{
279	struct hexium *hexium = (struct hexium *) dev->ext_priv;
280	union i2c_smbus_data data;
281	int i = 0;
282
283	DEB_D("hexium_init_done called\n");
284
285	/* initialize the helper ics to useful values */
286	for (i = 0; i < sizeof(hexium_saa7110); i++) {
287		data.byte = hexium_saa7110[i];
288		if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
289			pr_err("failed for address 0x%02x\n", i);
290		}
291	}
292
293	return 0;
294}
295
296static int hexium_set_input(struct hexium *hexium, int input)
297{
298	union i2c_smbus_data data;
299	int i = 0;
300
301	DEB_D("\n");
302
303	for (i = 0; i < 8; i++) {
304		int adr = hexium_input_select[input].data[i].adr;
305		data.byte = hexium_input_select[input].data[i].byte;
306		if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, adr, I2C_SMBUS_BYTE_DATA, &data)) {
307			return -1;
308		}
309		pr_debug("%d: 0x%02x => 0x%02x\n", input, adr, data.byte);
310	}
311
312	return 0;
313}
314
315static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
316{
317	DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
318
319	if (i->index >= HEXIUM_INPUTS)
320		return -EINVAL;
321
322	memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
323
324	DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index);
325	return 0;
326}
327
328static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
329{
330	struct saa7146_dev *dev = video_drvdata(file);
331	struct hexium *hexium = (struct hexium *) dev->ext_priv;
332
333	*input = hexium->cur_input;
334
335	DEB_D("VIDIOC_G_INPUT: %d\n", *input);
336	return 0;
337}
338
339static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
340{
341	struct saa7146_dev *dev = video_drvdata(file);
342	struct hexium *hexium = (struct hexium *) dev->ext_priv;
343
344	if (input >= HEXIUM_INPUTS)
345		return -EINVAL;
346
347	hexium->cur_input = input;
348	hexium_set_input(hexium, input);
349
350	return 0;
351}
352
353static struct saa7146_ext_vv vv_data;
354
355/* this function only gets called when the probing was successful */
356static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
357{
358	struct hexium *hexium = (struct hexium *) dev->ext_priv;
359	int ret;
360
361	DEB_EE("\n");
362
363	ret = saa7146_vv_init(dev, &vv_data);
364	if (ret) {
365		pr_err("Error in saa7146_vv_init()\n");
366		return ret;
367	}
368
369	vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
370	vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
371	vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
372	if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_VIDEO)) {
373		pr_err("cannot register capture v4l2 device. skipping.\n");
374		return -1;
375	}
376
377	pr_err("found 'hexium orion' frame grabber-%d\n", hexium_num);
378	hexium_num++;
379
380	/* the rest */
381	hexium->cur_input = 0;
382	hexium_init_done(dev);
383	hexium_set_input(hexium, 0);
384
385	return 0;
386}
387
388static int hexium_detach(struct saa7146_dev *dev)
389{
390	struct hexium *hexium = (struct hexium *) dev->ext_priv;
391
392	DEB_EE("dev:%p\n", dev);
393
394	saa7146_unregister_device(&hexium->video_dev, dev);
395	saa7146_vv_release(dev);
396
397	hexium_num--;
398
399	i2c_del_adapter(&hexium->i2c_adapter);
400	kfree(hexium);
401	return 0;
402}
403
404static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
405{
406	return 0;
407}
408
409static struct saa7146_extension extension;
410
411static struct saa7146_pci_extension_data hexium_hv_pci6 = {
412	.ext_priv = "Hexium HV-PCI6 / Orion",
413	.ext = &extension,
414};
415
416static struct saa7146_pci_extension_data hexium_orion_1svhs_3bnc = {
417	.ext_priv = "Hexium HV-PCI6 / Orion (1 SVHS/3 BNC)",
418	.ext = &extension,
419};
420
421static struct saa7146_pci_extension_data hexium_orion_4bnc = {
422	.ext_priv = "Hexium HV-PCI6 / Orion (4 BNC)",
423	.ext = &extension,
424};
425
426static const struct pci_device_id pci_tbl[] = {
427	{
428	 .vendor = PCI_VENDOR_ID_PHILIPS,
429	 .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
430	 .subvendor = 0x0000,
431	 .subdevice = 0x0000,
432	 .driver_data = (unsigned long) &hexium_hv_pci6,
433	 },
434	{
435	 .vendor = PCI_VENDOR_ID_PHILIPS,
436	 .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
437	 .subvendor = 0x17c8,
438	 .subdevice = 0x0101,
439	 .driver_data = (unsigned long) &hexium_orion_1svhs_3bnc,
440	 },
441	{
442	 .vendor = PCI_VENDOR_ID_PHILIPS,
443	 .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
444	 .subvendor = 0x17c8,
445	 .subdevice = 0x2101,
446	 .driver_data = (unsigned long) &hexium_orion_4bnc,
447	 },
448	{
449	 .vendor = 0,
450	 }
451};
452
453MODULE_DEVICE_TABLE(pci, pci_tbl);
454
455static struct saa7146_ext_vv vv_data = {
456	.inputs = HEXIUM_INPUTS,
457	.capabilities = 0,
458	.stds = &hexium_standards[0],
459	.num_stds = ARRAY_SIZE(hexium_standards),
460	.std_callback = &std_callback,
461};
462
463static struct saa7146_extension extension = {
464	.name = "hexium HV-PCI6 Orion",
465	.flags = 0,		// SAA7146_USE_I2C_IRQ,
466
467	.pci_tbl = &pci_tbl[0],
468	.module = THIS_MODULE,
469
470	.probe = hexium_probe,
471	.attach = hexium_attach,
472	.detach = hexium_detach,
473
474	.irq_mask = 0,
475	.irq_func = NULL,
476};
477
478static int __init hexium_init_module(void)
479{
480	if (0 != saa7146_register_extension(&extension)) {
481		DEB_S("failed to register extension\n");
482		return -ENODEV;
483	}
484
485	return 0;
486}
487
488static void __exit hexium_cleanup_module(void)
489{
490	saa7146_unregister_extension(&extension);
491}
492
493module_init(hexium_init_module);
494module_exit(hexium_cleanup_module);
495
496MODULE_DESCRIPTION("video4linux-2 driver for Hexium Orion frame grabber cards");
497MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
498MODULE_LICENSE("GPL");
499