162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci    hexium_gemini.c - v4l2 driver for Hexium Gemini frame grabber cards
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci    Visit http://www.mihu.de/linux/saa7146/ and follow the link
662306a36Sopenharmony_ci    to "hexium" for further details about this card.
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci    Copyright (C) 2003 Michael Hunold <michael@mihu.de>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci*/
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define DEBUG_VARIABLE debug
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <media/drv-intf/saa7146_vv.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/kernel.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic int debug;
2162306a36Sopenharmony_cimodule_param(debug, int, 0);
2262306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "debug verbosity");
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* global variables */
2562306a36Sopenharmony_cistatic int hexium_num;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define HEXIUM_GEMINI			4
2862306a36Sopenharmony_ci#define HEXIUM_GEMINI_DUAL		5
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define HEXIUM_STD (V4L2_STD_PAL | V4L2_STD_SECAM | V4L2_STD_NTSC)
3162306a36Sopenharmony_ci#define HEXIUM_INPUTS	9
3262306a36Sopenharmony_cistatic struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
3362306a36Sopenharmony_ci	{ 0, "CVBS 1",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
3462306a36Sopenharmony_ci	{ 1, "CVBS 2",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
3562306a36Sopenharmony_ci	{ 2, "CVBS 3",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
3662306a36Sopenharmony_ci	{ 3, "CVBS 4",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
3762306a36Sopenharmony_ci	{ 4, "CVBS 5",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
3862306a36Sopenharmony_ci	{ 5, "CVBS 6",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
3962306a36Sopenharmony_ci	{ 6, "Y/C 1",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
4062306a36Sopenharmony_ci	{ 7, "Y/C 2",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
4162306a36Sopenharmony_ci	{ 8, "Y/C 3",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define HEXIUM_AUDIOS	0
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistruct hexium_data
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	s8 adr;
4962306a36Sopenharmony_ci	u8 byte;
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define HEXIUM_GEMINI_V_1_0		1
5362306a36Sopenharmony_ci#define HEXIUM_GEMINI_DUAL_V_1_0	2
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistruct hexium
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	int type;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	struct video_device	video_dev;
6062306a36Sopenharmony_ci	struct i2c_adapter	i2c_adapter;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	int		cur_input;	/* current input */
6362306a36Sopenharmony_ci	v4l2_std_id	cur_std;	/* current standard */
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/* Samsung KS0127B decoder default registers */
6762306a36Sopenharmony_cistatic u8 hexium_ks0127b[0x100]={
6862306a36Sopenharmony_ci/*00*/ 0x00,0x52,0x30,0x40,0x01,0x0C,0x2A,0x10,
6962306a36Sopenharmony_ci/*08*/ 0x00,0x00,0x00,0x60,0x00,0x00,0x0F,0x06,
7062306a36Sopenharmony_ci/*10*/ 0x00,0x00,0xE4,0xC0,0x00,0x00,0x00,0x00,
7162306a36Sopenharmony_ci/*18*/ 0x14,0x9B,0xFE,0xFF,0xFC,0xFF,0x03,0x22,
7262306a36Sopenharmony_ci/*20*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
7362306a36Sopenharmony_ci/*28*/ 0x00,0x00,0x00,0x00,0x00,0x2C,0x9B,0x00,
7462306a36Sopenharmony_ci/*30*/ 0x00,0x00,0x10,0x80,0x80,0x10,0x80,0x80,
7562306a36Sopenharmony_ci/*38*/ 0x01,0x04,0x00,0x00,0x00,0x29,0xC0,0x00,
7662306a36Sopenharmony_ci/*40*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
7762306a36Sopenharmony_ci/*48*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
7862306a36Sopenharmony_ci/*50*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
7962306a36Sopenharmony_ci/*58*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8062306a36Sopenharmony_ci/*60*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8162306a36Sopenharmony_ci/*68*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8262306a36Sopenharmony_ci/*70*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8362306a36Sopenharmony_ci/*78*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8462306a36Sopenharmony_ci/*80*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8562306a36Sopenharmony_ci/*88*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8662306a36Sopenharmony_ci/*90*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8762306a36Sopenharmony_ci/*98*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8862306a36Sopenharmony_ci/*A0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8962306a36Sopenharmony_ci/*A8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9062306a36Sopenharmony_ci/*B0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9162306a36Sopenharmony_ci/*B8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9262306a36Sopenharmony_ci/*C0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9362306a36Sopenharmony_ci/*C8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9462306a36Sopenharmony_ci/*D0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9562306a36Sopenharmony_ci/*D8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9662306a36Sopenharmony_ci/*E0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9762306a36Sopenharmony_ci/*E8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9862306a36Sopenharmony_ci/*F0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9962306a36Sopenharmony_ci/*F8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic struct hexium_data hexium_pal[] = {
10362306a36Sopenharmony_ci	{ 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
10462306a36Sopenharmony_ci};
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic struct hexium_data hexium_ntsc[] = {
10762306a36Sopenharmony_ci	{ 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic struct hexium_data hexium_secam[] = {
11162306a36Sopenharmony_ci	{ 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
11262306a36Sopenharmony_ci};
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic struct hexium_data hexium_input_select[] = {
11562306a36Sopenharmony_ci	{ 0x02, 0x60 },
11662306a36Sopenharmony_ci	{ 0x02, 0x64 },
11762306a36Sopenharmony_ci	{ 0x02, 0x61 },
11862306a36Sopenharmony_ci	{ 0x02, 0x65 },
11962306a36Sopenharmony_ci	{ 0x02, 0x62 },
12062306a36Sopenharmony_ci	{ 0x02, 0x66 },
12162306a36Sopenharmony_ci	{ 0x02, 0x68 },
12262306a36Sopenharmony_ci	{ 0x02, 0x69 },
12362306a36Sopenharmony_ci	{ 0x02, 0x6A },
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/* fixme: h_offset = 0 for Hexium Gemini *Dual*, which
12762306a36Sopenharmony_ci   are currently *not* supported*/
12862306a36Sopenharmony_cistatic struct saa7146_standard hexium_standards[] = {
12962306a36Sopenharmony_ci	{
13062306a36Sopenharmony_ci		.name	= "PAL",	.id	= V4L2_STD_PAL,
13162306a36Sopenharmony_ci		.v_offset	= 28,	.v_field	= 288,
13262306a36Sopenharmony_ci		.h_offset	= 1,	.h_pixels	= 680,
13362306a36Sopenharmony_ci		.v_max_out	= 576,	.h_max_out	= 768,
13462306a36Sopenharmony_ci	}, {
13562306a36Sopenharmony_ci		.name	= "NTSC",	.id	= V4L2_STD_NTSC,
13662306a36Sopenharmony_ci		.v_offset	= 28,	.v_field	= 240,
13762306a36Sopenharmony_ci		.h_offset	= 1,	.h_pixels	= 640,
13862306a36Sopenharmony_ci		.v_max_out	= 480,	.h_max_out	= 640,
13962306a36Sopenharmony_ci	}, {
14062306a36Sopenharmony_ci		.name	= "SECAM",	.id	= V4L2_STD_SECAM,
14162306a36Sopenharmony_ci		.v_offset	= 28,	.v_field	= 288,
14262306a36Sopenharmony_ci		.h_offset	= 1,	.h_pixels	= 720,
14362306a36Sopenharmony_ci		.v_max_out	= 576,	.h_max_out	= 768,
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci};
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/* bring hardware to a sane state. this has to be done, just in case someone
14862306a36Sopenharmony_ci   wants to capture from this device before it has been properly initialized.
14962306a36Sopenharmony_ci   the capture engine would badly fail, because no valid signal arrives on the
15062306a36Sopenharmony_ci   saa7146, thus leading to timeouts and stuff. */
15162306a36Sopenharmony_cistatic int hexium_init_done(struct saa7146_dev *dev)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct hexium *hexium = (struct hexium *) dev->ext_priv;
15462306a36Sopenharmony_ci	union i2c_smbus_data data;
15562306a36Sopenharmony_ci	int i = 0;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	DEB_D("hexium_init_done called\n");
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/* initialize the helper ics to useful values */
16062306a36Sopenharmony_ci	for (i = 0; i < sizeof(hexium_ks0127b); i++) {
16162306a36Sopenharmony_ci		data.byte = hexium_ks0127b[i];
16262306a36Sopenharmony_ci		if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
16362306a36Sopenharmony_ci			pr_err("hexium_init_done() failed for address 0x%02x\n",
16462306a36Sopenharmony_ci			       i);
16562306a36Sopenharmony_ci		}
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return 0;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic int hexium_set_input(struct hexium *hexium, int input)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	union i2c_smbus_data data;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	DEB_D("\n");
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	data.byte = hexium_input_select[input].byte;
17862306a36Sopenharmony_ci	if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, &data)) {
17962306a36Sopenharmony_ci		return -1;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return 0;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	union i2c_smbus_data data;
18862306a36Sopenharmony_ci	int i = 0;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	DEB_D("\n");
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	while (vdec[i].adr != -1) {
19362306a36Sopenharmony_ci		data.byte = vdec[i].byte;
19462306a36Sopenharmony_ci		if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, vdec[i].adr, I2C_SMBUS_BYTE_DATA, &data)) {
19562306a36Sopenharmony_ci			pr_err("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n",
19662306a36Sopenharmony_ci			       i);
19762306a36Sopenharmony_ci			return -1;
19862306a36Sopenharmony_ci		}
19962306a36Sopenharmony_ci		i++;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci	return 0;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (i->index >= HEXIUM_INPUTS)
20962306a36Sopenharmony_ci		return -EINVAL;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index);
21462306a36Sopenharmony_ci	return 0;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct saa7146_dev *dev = video_drvdata(file);
22062306a36Sopenharmony_ci	struct hexium *hexium = (struct hexium *) dev->ext_priv;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	*input = hexium->cur_input;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	DEB_D("VIDIOC_G_INPUT: %d\n", *input);
22562306a36Sopenharmony_ci	return 0;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic int vidioc_s_input(struct file *file, void *fh, unsigned int input)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct saa7146_dev *dev = video_drvdata(file);
23162306a36Sopenharmony_ci	struct hexium *hexium = (struct hexium *) dev->ext_priv;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	DEB_EE("VIDIOC_S_INPUT %d\n", input);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (input >= HEXIUM_INPUTS)
23662306a36Sopenharmony_ci		return -EINVAL;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	hexium->cur_input = input;
23962306a36Sopenharmony_ci	hexium_set_input(hexium, input);
24062306a36Sopenharmony_ci	return 0;
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic struct saa7146_ext_vv vv_data;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci/* this function only gets called when the probing was successful */
24662306a36Sopenharmony_cistatic int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	struct hexium *hexium;
24962306a36Sopenharmony_ci	int ret;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	DEB_EE("\n");
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	hexium = kzalloc(sizeof(*hexium), GFP_KERNEL);
25462306a36Sopenharmony_ci	if (!hexium)
25562306a36Sopenharmony_ci		return -ENOMEM;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	dev->ext_priv = hexium;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* enable i2c-port pins */
26062306a36Sopenharmony_ci	saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	strscpy(hexium->i2c_adapter.name, "hexium gemini",
26362306a36Sopenharmony_ci		sizeof(hexium->i2c_adapter.name));
26462306a36Sopenharmony_ci	saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
26562306a36Sopenharmony_ci	if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
26662306a36Sopenharmony_ci		DEB_S("cannot register i2c-device. skipping.\n");
26762306a36Sopenharmony_ci		kfree(hexium);
26862306a36Sopenharmony_ci		return -EFAULT;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	/*  set HWControl GPIO number 2 */
27262306a36Sopenharmony_ci	saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	saa7146_write(dev, DD1_INIT, 0x07000700);
27562306a36Sopenharmony_ci	saa7146_write(dev, DD1_STREAM_B, 0x00000000);
27662306a36Sopenharmony_ci	saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/* the rest */
27962306a36Sopenharmony_ci	hexium->cur_input = 0;
28062306a36Sopenharmony_ci	hexium_init_done(dev);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	hexium_set_standard(hexium, hexium_pal);
28362306a36Sopenharmony_ci	hexium->cur_std = V4L2_STD_PAL;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	hexium_set_input(hexium, 0);
28662306a36Sopenharmony_ci	hexium->cur_input = 0;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	ret = saa7146_vv_init(dev, &vv_data);
28962306a36Sopenharmony_ci	if (ret) {
29062306a36Sopenharmony_ci		i2c_del_adapter(&hexium->i2c_adapter);
29162306a36Sopenharmony_ci		kfree(hexium);
29262306a36Sopenharmony_ci		return ret;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
29662306a36Sopenharmony_ci	vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
29762306a36Sopenharmony_ci	vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
29862306a36Sopenharmony_ci	ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_VIDEO);
29962306a36Sopenharmony_ci	if (ret < 0) {
30062306a36Sopenharmony_ci		pr_err("cannot register capture v4l2 device. skipping.\n");
30162306a36Sopenharmony_ci		saa7146_vv_release(dev);
30262306a36Sopenharmony_ci		i2c_del_adapter(&hexium->i2c_adapter);
30362306a36Sopenharmony_ci		kfree(hexium);
30462306a36Sopenharmony_ci		return ret;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	pr_info("found 'hexium gemini' frame grabber-%d\n", hexium_num);
30862306a36Sopenharmony_ci	hexium_num++;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	return 0;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic int hexium_detach(struct saa7146_dev *dev)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	struct hexium *hexium = (struct hexium *) dev->ext_priv;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	DEB_EE("dev:%p\n", dev);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	saa7146_unregister_device(&hexium->video_dev, dev);
32062306a36Sopenharmony_ci	saa7146_vv_release(dev);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	hexium_num--;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	i2c_del_adapter(&hexium->i2c_adapter);
32562306a36Sopenharmony_ci	kfree(hexium);
32662306a36Sopenharmony_ci	return 0;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	struct hexium *hexium = (struct hexium *) dev->ext_priv;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	if (V4L2_STD_PAL == std->id) {
33462306a36Sopenharmony_ci		hexium_set_standard(hexium, hexium_pal);
33562306a36Sopenharmony_ci		hexium->cur_std = V4L2_STD_PAL;
33662306a36Sopenharmony_ci		return 0;
33762306a36Sopenharmony_ci	} else if (V4L2_STD_NTSC == std->id) {
33862306a36Sopenharmony_ci		hexium_set_standard(hexium, hexium_ntsc);
33962306a36Sopenharmony_ci		hexium->cur_std = V4L2_STD_NTSC;
34062306a36Sopenharmony_ci		return 0;
34162306a36Sopenharmony_ci	} else if (V4L2_STD_SECAM == std->id) {
34262306a36Sopenharmony_ci		hexium_set_standard(hexium, hexium_secam);
34362306a36Sopenharmony_ci		hexium->cur_std = V4L2_STD_SECAM;
34462306a36Sopenharmony_ci		return 0;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	return -1;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic struct saa7146_extension hexium_extension;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic struct saa7146_pci_extension_data hexium_gemini_4bnc = {
35362306a36Sopenharmony_ci	.ext_priv = "Hexium Gemini (4 BNC)",
35462306a36Sopenharmony_ci	.ext = &hexium_extension,
35562306a36Sopenharmony_ci};
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = {
35862306a36Sopenharmony_ci	.ext_priv = "Hexium Gemini Dual (4 BNC)",
35962306a36Sopenharmony_ci	.ext = &hexium_extension,
36062306a36Sopenharmony_ci};
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic const struct pci_device_id pci_tbl[] = {
36362306a36Sopenharmony_ci	{
36462306a36Sopenharmony_ci	 .vendor = PCI_VENDOR_ID_PHILIPS,
36562306a36Sopenharmony_ci	 .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
36662306a36Sopenharmony_ci	 .subvendor = 0x17c8,
36762306a36Sopenharmony_ci	 .subdevice = 0x2401,
36862306a36Sopenharmony_ci	 .driver_data = (unsigned long) &hexium_gemini_4bnc,
36962306a36Sopenharmony_ci	 },
37062306a36Sopenharmony_ci	{
37162306a36Sopenharmony_ci	 .vendor = PCI_VENDOR_ID_PHILIPS,
37262306a36Sopenharmony_ci	 .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
37362306a36Sopenharmony_ci	 .subvendor = 0x17c8,
37462306a36Sopenharmony_ci	 .subdevice = 0x2402,
37562306a36Sopenharmony_ci	 .driver_data = (unsigned long) &hexium_gemini_dual_4bnc,
37662306a36Sopenharmony_ci	 },
37762306a36Sopenharmony_ci	{
37862306a36Sopenharmony_ci	 .vendor = 0,
37962306a36Sopenharmony_ci	 }
38062306a36Sopenharmony_ci};
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pci_tbl);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic struct saa7146_ext_vv vv_data = {
38562306a36Sopenharmony_ci	.inputs = HEXIUM_INPUTS,
38662306a36Sopenharmony_ci	.capabilities = 0,
38762306a36Sopenharmony_ci	.stds = &hexium_standards[0],
38862306a36Sopenharmony_ci	.num_stds = ARRAY_SIZE(hexium_standards),
38962306a36Sopenharmony_ci	.std_callback = &std_callback,
39062306a36Sopenharmony_ci};
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic struct saa7146_extension hexium_extension = {
39362306a36Sopenharmony_ci	.name = "hexium gemini",
39462306a36Sopenharmony_ci	.flags = SAA7146_USE_I2C_IRQ,
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	.pci_tbl = &pci_tbl[0],
39762306a36Sopenharmony_ci	.module = THIS_MODULE,
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	.attach = hexium_attach,
40062306a36Sopenharmony_ci	.detach = hexium_detach,
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	.irq_mask = 0,
40362306a36Sopenharmony_ci	.irq_func = NULL,
40462306a36Sopenharmony_ci};
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic int __init hexium_init_module(void)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	if (0 != saa7146_register_extension(&hexium_extension)) {
40962306a36Sopenharmony_ci		DEB_S("failed to register extension\n");
41062306a36Sopenharmony_ci		return -ENODEV;
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	return 0;
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic void __exit hexium_cleanup_module(void)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	saa7146_unregister_extension(&hexium_extension);
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cimodule_init(hexium_init_module);
42262306a36Sopenharmony_cimodule_exit(hexium_cleanup_module);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ciMODULE_DESCRIPTION("video4linux-2 driver for Hexium Gemini frame grabber cards");
42562306a36Sopenharmony_ciMODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
42662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
427