xref: /linux/drivers/media/pci/saa7146/hexium_gemini.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
139d08ab9SHans Verkuil // SPDX-License-Identifier: GPL-2.0-or-later
239d08ab9SHans Verkuil /*
339d08ab9SHans Verkuil     hexium_gemini.c - v4l2 driver for Hexium Gemini frame grabber cards
439d08ab9SHans Verkuil 
539d08ab9SHans Verkuil     Visit http://www.mihu.de/linux/saa7146/ and follow the link
639d08ab9SHans Verkuil     to "hexium" for further details about this card.
739d08ab9SHans Verkuil 
839d08ab9SHans Verkuil     Copyright (C) 2003 Michael Hunold <michael@mihu.de>
939d08ab9SHans Verkuil 
1039d08ab9SHans Verkuil */
1139d08ab9SHans Verkuil 
1239d08ab9SHans Verkuil #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1339d08ab9SHans Verkuil 
1439d08ab9SHans Verkuil #define DEBUG_VARIABLE debug
1539d08ab9SHans Verkuil 
1639d08ab9SHans Verkuil #include <media/drv-intf/saa7146_vv.h>
1739d08ab9SHans Verkuil #include <linux/module.h>
1839d08ab9SHans Verkuil #include <linux/kernel.h>
1939d08ab9SHans Verkuil 
2039d08ab9SHans Verkuil static int debug;
2139d08ab9SHans Verkuil module_param(debug, int, 0);
2239d08ab9SHans Verkuil MODULE_PARM_DESC(debug, "debug verbosity");
2339d08ab9SHans Verkuil 
2439d08ab9SHans Verkuil /* global variables */
2539d08ab9SHans Verkuil static int hexium_num;
2639d08ab9SHans Verkuil 
2739d08ab9SHans Verkuil #define HEXIUM_GEMINI			4
2839d08ab9SHans Verkuil #define HEXIUM_GEMINI_DUAL		5
2939d08ab9SHans Verkuil 
30*c116a5c6SHans Verkuil #define HEXIUM_STD (V4L2_STD_PAL | V4L2_STD_SECAM | V4L2_STD_NTSC)
3139d08ab9SHans Verkuil #define HEXIUM_INPUTS	9
3239d08ab9SHans Verkuil static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
33*c116a5c6SHans Verkuil 	{ 0, "CVBS 1",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
34*c116a5c6SHans Verkuil 	{ 1, "CVBS 2",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
35*c116a5c6SHans Verkuil 	{ 2, "CVBS 3",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
36*c116a5c6SHans Verkuil 	{ 3, "CVBS 4",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
37*c116a5c6SHans Verkuil 	{ 4, "CVBS 5",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
38*c116a5c6SHans Verkuil 	{ 5, "CVBS 6",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
39*c116a5c6SHans Verkuil 	{ 6, "Y/C 1",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
40*c116a5c6SHans Verkuil 	{ 7, "Y/C 2",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
41*c116a5c6SHans Verkuil 	{ 8, "Y/C 3",	V4L2_INPUT_TYPE_CAMERA,	0, 0, HEXIUM_STD, 0, V4L2_IN_CAP_STD },
4239d08ab9SHans Verkuil };
4339d08ab9SHans Verkuil 
4439d08ab9SHans Verkuil #define HEXIUM_AUDIOS	0
4539d08ab9SHans Verkuil 
4639d08ab9SHans Verkuil struct hexium_data
4739d08ab9SHans Verkuil {
4839d08ab9SHans Verkuil 	s8 adr;
4939d08ab9SHans Verkuil 	u8 byte;
5039d08ab9SHans Verkuil };
5139d08ab9SHans Verkuil 
5239d08ab9SHans Verkuil #define HEXIUM_GEMINI_V_1_0		1
5339d08ab9SHans Verkuil #define HEXIUM_GEMINI_DUAL_V_1_0	2
5439d08ab9SHans Verkuil 
5539d08ab9SHans Verkuil struct hexium
5639d08ab9SHans Verkuil {
5739d08ab9SHans Verkuil 	int type;
5839d08ab9SHans Verkuil 
5939d08ab9SHans Verkuil 	struct video_device	video_dev;
6039d08ab9SHans Verkuil 	struct i2c_adapter	i2c_adapter;
6139d08ab9SHans Verkuil 
6239d08ab9SHans Verkuil 	int		cur_input;	/* current input */
6339d08ab9SHans Verkuil 	v4l2_std_id	cur_std;	/* current standard */
6439d08ab9SHans Verkuil };
6539d08ab9SHans Verkuil 
6639d08ab9SHans Verkuil /* Samsung KS0127B decoder default registers */
6739d08ab9SHans Verkuil static u8 hexium_ks0127b[0x100]={
6839d08ab9SHans Verkuil /*00*/ 0x00,0x52,0x30,0x40,0x01,0x0C,0x2A,0x10,
6939d08ab9SHans Verkuil /*08*/ 0x00,0x00,0x00,0x60,0x00,0x00,0x0F,0x06,
7039d08ab9SHans Verkuil /*10*/ 0x00,0x00,0xE4,0xC0,0x00,0x00,0x00,0x00,
7139d08ab9SHans Verkuil /*18*/ 0x14,0x9B,0xFE,0xFF,0xFC,0xFF,0x03,0x22,
7239d08ab9SHans Verkuil /*20*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
7339d08ab9SHans Verkuil /*28*/ 0x00,0x00,0x00,0x00,0x00,0x2C,0x9B,0x00,
7439d08ab9SHans Verkuil /*30*/ 0x00,0x00,0x10,0x80,0x80,0x10,0x80,0x80,
7539d08ab9SHans Verkuil /*38*/ 0x01,0x04,0x00,0x00,0x00,0x29,0xC0,0x00,
7639d08ab9SHans Verkuil /*40*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
7739d08ab9SHans Verkuil /*48*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
7839d08ab9SHans Verkuil /*50*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
7939d08ab9SHans Verkuil /*58*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8039d08ab9SHans Verkuil /*60*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8139d08ab9SHans Verkuil /*68*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8239d08ab9SHans Verkuil /*70*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8339d08ab9SHans Verkuil /*78*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8439d08ab9SHans Verkuil /*80*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8539d08ab9SHans Verkuil /*88*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8639d08ab9SHans Verkuil /*90*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8739d08ab9SHans Verkuil /*98*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8839d08ab9SHans Verkuil /*A0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
8939d08ab9SHans Verkuil /*A8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9039d08ab9SHans Verkuil /*B0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9139d08ab9SHans Verkuil /*B8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9239d08ab9SHans Verkuil /*C0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9339d08ab9SHans Verkuil /*C8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9439d08ab9SHans Verkuil /*D0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9539d08ab9SHans Verkuil /*D8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9639d08ab9SHans Verkuil /*E0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9739d08ab9SHans Verkuil /*E8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9839d08ab9SHans Verkuil /*F0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
9939d08ab9SHans Verkuil /*F8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
10039d08ab9SHans Verkuil };
10139d08ab9SHans Verkuil 
10239d08ab9SHans Verkuil static struct hexium_data hexium_pal[] = {
10339d08ab9SHans Verkuil 	{ 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
10439d08ab9SHans Verkuil };
10539d08ab9SHans Verkuil 
10639d08ab9SHans Verkuil static struct hexium_data hexium_ntsc[] = {
10739d08ab9SHans Verkuil 	{ 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
10839d08ab9SHans Verkuil };
10939d08ab9SHans Verkuil 
11039d08ab9SHans Verkuil static struct hexium_data hexium_secam[] = {
11139d08ab9SHans Verkuil 	{ 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
11239d08ab9SHans Verkuil };
11339d08ab9SHans Verkuil 
11439d08ab9SHans Verkuil static struct hexium_data hexium_input_select[] = {
11539d08ab9SHans Verkuil 	{ 0x02, 0x60 },
11639d08ab9SHans Verkuil 	{ 0x02, 0x64 },
11739d08ab9SHans Verkuil 	{ 0x02, 0x61 },
11839d08ab9SHans Verkuil 	{ 0x02, 0x65 },
11939d08ab9SHans Verkuil 	{ 0x02, 0x62 },
12039d08ab9SHans Verkuil 	{ 0x02, 0x66 },
12139d08ab9SHans Verkuil 	{ 0x02, 0x68 },
12239d08ab9SHans Verkuil 	{ 0x02, 0x69 },
12339d08ab9SHans Verkuil 	{ 0x02, 0x6A },
12439d08ab9SHans Verkuil };
12539d08ab9SHans Verkuil 
12639d08ab9SHans Verkuil /* fixme: h_offset = 0 for Hexium Gemini *Dual*, which
12739d08ab9SHans Verkuil    are currently *not* supported*/
12839d08ab9SHans Verkuil static struct saa7146_standard hexium_standards[] = {
12939d08ab9SHans Verkuil 	{
13039d08ab9SHans Verkuil 		.name	= "PAL",	.id	= V4L2_STD_PAL,
13139d08ab9SHans Verkuil 		.v_offset	= 28,	.v_field	= 288,
13239d08ab9SHans Verkuil 		.h_offset	= 1,	.h_pixels	= 680,
13339d08ab9SHans Verkuil 		.v_max_out	= 576,	.h_max_out	= 768,
13439d08ab9SHans Verkuil 	}, {
13539d08ab9SHans Verkuil 		.name	= "NTSC",	.id	= V4L2_STD_NTSC,
13639d08ab9SHans Verkuil 		.v_offset	= 28,	.v_field	= 240,
13739d08ab9SHans Verkuil 		.h_offset	= 1,	.h_pixels	= 640,
13839d08ab9SHans Verkuil 		.v_max_out	= 480,	.h_max_out	= 640,
13939d08ab9SHans Verkuil 	}, {
14039d08ab9SHans Verkuil 		.name	= "SECAM",	.id	= V4L2_STD_SECAM,
14139d08ab9SHans Verkuil 		.v_offset	= 28,	.v_field	= 288,
14239d08ab9SHans Verkuil 		.h_offset	= 1,	.h_pixels	= 720,
14339d08ab9SHans Verkuil 		.v_max_out	= 576,	.h_max_out	= 768,
14439d08ab9SHans Verkuil 	}
14539d08ab9SHans Verkuil };
14639d08ab9SHans Verkuil 
14739d08ab9SHans Verkuil /* bring hardware to a sane state. this has to be done, just in case someone
14839d08ab9SHans Verkuil    wants to capture from this device before it has been properly initialized.
14939d08ab9SHans Verkuil    the capture engine would badly fail, because no valid signal arrives on the
15039d08ab9SHans Verkuil    saa7146, thus leading to timeouts and stuff. */
hexium_init_done(struct saa7146_dev * dev)15139d08ab9SHans Verkuil static int hexium_init_done(struct saa7146_dev *dev)
15239d08ab9SHans Verkuil {
15339d08ab9SHans Verkuil 	struct hexium *hexium = (struct hexium *) dev->ext_priv;
15439d08ab9SHans Verkuil 	union i2c_smbus_data data;
15539d08ab9SHans Verkuil 	int i = 0;
15639d08ab9SHans Verkuil 
15739d08ab9SHans Verkuil 	DEB_D("hexium_init_done called\n");
15839d08ab9SHans Verkuil 
15939d08ab9SHans Verkuil 	/* initialize the helper ics to useful values */
16039d08ab9SHans Verkuil 	for (i = 0; i < sizeof(hexium_ks0127b); i++) {
16139d08ab9SHans Verkuil 		data.byte = hexium_ks0127b[i];
16239d08ab9SHans Verkuil 		if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
16339d08ab9SHans Verkuil 			pr_err("hexium_init_done() failed for address 0x%02x\n",
16439d08ab9SHans Verkuil 			       i);
16539d08ab9SHans Verkuil 		}
16639d08ab9SHans Verkuil 	}
16739d08ab9SHans Verkuil 
16839d08ab9SHans Verkuil 	return 0;
16939d08ab9SHans Verkuil }
17039d08ab9SHans Verkuil 
hexium_set_input(struct hexium * hexium,int input)17139d08ab9SHans Verkuil static int hexium_set_input(struct hexium *hexium, int input)
17239d08ab9SHans Verkuil {
17339d08ab9SHans Verkuil 	union i2c_smbus_data data;
17439d08ab9SHans Verkuil 
17539d08ab9SHans Verkuil 	DEB_D("\n");
17639d08ab9SHans Verkuil 
17739d08ab9SHans Verkuil 	data.byte = hexium_input_select[input].byte;
17839d08ab9SHans Verkuil 	if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, &data)) {
17939d08ab9SHans Verkuil 		return -1;
18039d08ab9SHans Verkuil 	}
18139d08ab9SHans Verkuil 
18239d08ab9SHans Verkuil 	return 0;
18339d08ab9SHans Verkuil }
18439d08ab9SHans Verkuil 
hexium_set_standard(struct hexium * hexium,struct hexium_data * vdec)18539d08ab9SHans Verkuil static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec)
18639d08ab9SHans Verkuil {
18739d08ab9SHans Verkuil 	union i2c_smbus_data data;
18839d08ab9SHans Verkuil 	int i = 0;
18939d08ab9SHans Verkuil 
19039d08ab9SHans Verkuil 	DEB_D("\n");
19139d08ab9SHans Verkuil 
19239d08ab9SHans Verkuil 	while (vdec[i].adr != -1) {
19339d08ab9SHans Verkuil 		data.byte = vdec[i].byte;
19439d08ab9SHans Verkuil 		if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, vdec[i].adr, I2C_SMBUS_BYTE_DATA, &data)) {
19539d08ab9SHans Verkuil 			pr_err("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n",
19639d08ab9SHans Verkuil 			       i);
19739d08ab9SHans Verkuil 			return -1;
19839d08ab9SHans Verkuil 		}
19939d08ab9SHans Verkuil 		i++;
20039d08ab9SHans Verkuil 	}
20139d08ab9SHans Verkuil 	return 0;
20239d08ab9SHans Verkuil }
20339d08ab9SHans Verkuil 
vidioc_enum_input(struct file * file,void * fh,struct v4l2_input * i)20439d08ab9SHans Verkuil static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
20539d08ab9SHans Verkuil {
20639d08ab9SHans Verkuil 	DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
20739d08ab9SHans Verkuil 
20839d08ab9SHans Verkuil 	if (i->index >= HEXIUM_INPUTS)
20939d08ab9SHans Verkuil 		return -EINVAL;
21039d08ab9SHans Verkuil 
21139d08ab9SHans Verkuil 	memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
21239d08ab9SHans Verkuil 
21339d08ab9SHans Verkuil 	DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index);
21439d08ab9SHans Verkuil 	return 0;
21539d08ab9SHans Verkuil }
21639d08ab9SHans Verkuil 
vidioc_g_input(struct file * file,void * fh,unsigned int * input)21739d08ab9SHans Verkuil static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
21839d08ab9SHans Verkuil {
219acdb1573SHans Verkuil 	struct saa7146_dev *dev = video_drvdata(file);
22039d08ab9SHans Verkuil 	struct hexium *hexium = (struct hexium *) dev->ext_priv;
22139d08ab9SHans Verkuil 
22239d08ab9SHans Verkuil 	*input = hexium->cur_input;
22339d08ab9SHans Verkuil 
22439d08ab9SHans Verkuil 	DEB_D("VIDIOC_G_INPUT: %d\n", *input);
22539d08ab9SHans Verkuil 	return 0;
22639d08ab9SHans Verkuil }
22739d08ab9SHans Verkuil 
vidioc_s_input(struct file * file,void * fh,unsigned int input)22839d08ab9SHans Verkuil static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
22939d08ab9SHans Verkuil {
230acdb1573SHans Verkuil 	struct saa7146_dev *dev = video_drvdata(file);
23139d08ab9SHans Verkuil 	struct hexium *hexium = (struct hexium *) dev->ext_priv;
23239d08ab9SHans Verkuil 
23339d08ab9SHans Verkuil 	DEB_EE("VIDIOC_S_INPUT %d\n", input);
23439d08ab9SHans Verkuil 
23539d08ab9SHans Verkuil 	if (input >= HEXIUM_INPUTS)
23639d08ab9SHans Verkuil 		return -EINVAL;
23739d08ab9SHans Verkuil 
23839d08ab9SHans Verkuil 	hexium->cur_input = input;
23939d08ab9SHans Verkuil 	hexium_set_input(hexium, input);
24039d08ab9SHans Verkuil 	return 0;
24139d08ab9SHans Verkuil }
24239d08ab9SHans Verkuil 
24339d08ab9SHans Verkuil static struct saa7146_ext_vv vv_data;
24439d08ab9SHans Verkuil 
24539d08ab9SHans Verkuil /* this function only gets called when the probing was successful */
hexium_attach(struct saa7146_dev * dev,struct saa7146_pci_extension_data * info)24639d08ab9SHans Verkuil static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
24739d08ab9SHans Verkuil {
24839d08ab9SHans Verkuil 	struct hexium *hexium;
24939d08ab9SHans Verkuil 	int ret;
25039d08ab9SHans Verkuil 
25139d08ab9SHans Verkuil 	DEB_EE("\n");
25239d08ab9SHans Verkuil 
25339d08ab9SHans Verkuil 	hexium = kzalloc(sizeof(*hexium), GFP_KERNEL);
25439d08ab9SHans Verkuil 	if (!hexium)
25539d08ab9SHans Verkuil 		return -ENOMEM;
25639d08ab9SHans Verkuil 
25739d08ab9SHans Verkuil 	dev->ext_priv = hexium;
25839d08ab9SHans Verkuil 
25939d08ab9SHans Verkuil 	/* enable i2c-port pins */
26039d08ab9SHans Verkuil 	saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
26139d08ab9SHans Verkuil 
26239d08ab9SHans Verkuil 	strscpy(hexium->i2c_adapter.name, "hexium gemini",
26339d08ab9SHans Verkuil 		sizeof(hexium->i2c_adapter.name));
26439d08ab9SHans Verkuil 	saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
26539d08ab9SHans Verkuil 	if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
26639d08ab9SHans Verkuil 		DEB_S("cannot register i2c-device. skipping.\n");
26739d08ab9SHans Verkuil 		kfree(hexium);
26839d08ab9SHans Verkuil 		return -EFAULT;
26939d08ab9SHans Verkuil 	}
27039d08ab9SHans Verkuil 
27139d08ab9SHans Verkuil 	/*  set HWControl GPIO number 2 */
27239d08ab9SHans Verkuil 	saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
27339d08ab9SHans Verkuil 
27439d08ab9SHans Verkuil 	saa7146_write(dev, DD1_INIT, 0x07000700);
27539d08ab9SHans Verkuil 	saa7146_write(dev, DD1_STREAM_B, 0x00000000);
27639d08ab9SHans Verkuil 	saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
27739d08ab9SHans Verkuil 
27839d08ab9SHans Verkuil 	/* the rest */
27939d08ab9SHans Verkuil 	hexium->cur_input = 0;
28039d08ab9SHans Verkuil 	hexium_init_done(dev);
28139d08ab9SHans Verkuil 
28239d08ab9SHans Verkuil 	hexium_set_standard(hexium, hexium_pal);
28339d08ab9SHans Verkuil 	hexium->cur_std = V4L2_STD_PAL;
28439d08ab9SHans Verkuil 
28539d08ab9SHans Verkuil 	hexium_set_input(hexium, 0);
28639d08ab9SHans Verkuil 	hexium->cur_input = 0;
28739d08ab9SHans Verkuil 
28839d08ab9SHans Verkuil 	ret = saa7146_vv_init(dev, &vv_data);
28939d08ab9SHans Verkuil 	if (ret) {
29039d08ab9SHans Verkuil 		i2c_del_adapter(&hexium->i2c_adapter);
29139d08ab9SHans Verkuil 		kfree(hexium);
29239d08ab9SHans Verkuil 		return ret;
29339d08ab9SHans Verkuil 	}
29439d08ab9SHans Verkuil 
29539d08ab9SHans Verkuil 	vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
29639d08ab9SHans Verkuil 	vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
29739d08ab9SHans Verkuil 	vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
29839d08ab9SHans Verkuil 	ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_VIDEO);
29939d08ab9SHans Verkuil 	if (ret < 0) {
30039d08ab9SHans Verkuil 		pr_err("cannot register capture v4l2 device. skipping.\n");
30139d08ab9SHans Verkuil 		saa7146_vv_release(dev);
30239d08ab9SHans Verkuil 		i2c_del_adapter(&hexium->i2c_adapter);
30339d08ab9SHans Verkuil 		kfree(hexium);
30439d08ab9SHans Verkuil 		return ret;
30539d08ab9SHans Verkuil 	}
30639d08ab9SHans Verkuil 
30739d08ab9SHans Verkuil 	pr_info("found 'hexium gemini' frame grabber-%d\n", hexium_num);
30839d08ab9SHans Verkuil 	hexium_num++;
30939d08ab9SHans Verkuil 
31039d08ab9SHans Verkuil 	return 0;
31139d08ab9SHans Verkuil }
31239d08ab9SHans Verkuil 
hexium_detach(struct saa7146_dev * dev)31339d08ab9SHans Verkuil static int hexium_detach(struct saa7146_dev *dev)
31439d08ab9SHans Verkuil {
31539d08ab9SHans Verkuil 	struct hexium *hexium = (struct hexium *) dev->ext_priv;
31639d08ab9SHans Verkuil 
31739d08ab9SHans Verkuil 	DEB_EE("dev:%p\n", dev);
31839d08ab9SHans Verkuil 
31939d08ab9SHans Verkuil 	saa7146_unregister_device(&hexium->video_dev, dev);
32039d08ab9SHans Verkuil 	saa7146_vv_release(dev);
32139d08ab9SHans Verkuil 
32239d08ab9SHans Verkuil 	hexium_num--;
32339d08ab9SHans Verkuil 
32439d08ab9SHans Verkuil 	i2c_del_adapter(&hexium->i2c_adapter);
32539d08ab9SHans Verkuil 	kfree(hexium);
32639d08ab9SHans Verkuil 	return 0;
32739d08ab9SHans Verkuil }
32839d08ab9SHans Verkuil 
std_callback(struct saa7146_dev * dev,struct saa7146_standard * std)32939d08ab9SHans Verkuil static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
33039d08ab9SHans Verkuil {
33139d08ab9SHans Verkuil 	struct hexium *hexium = (struct hexium *) dev->ext_priv;
33239d08ab9SHans Verkuil 
33339d08ab9SHans Verkuil 	if (V4L2_STD_PAL == std->id) {
33439d08ab9SHans Verkuil 		hexium_set_standard(hexium, hexium_pal);
33539d08ab9SHans Verkuil 		hexium->cur_std = V4L2_STD_PAL;
33639d08ab9SHans Verkuil 		return 0;
33739d08ab9SHans Verkuil 	} else if (V4L2_STD_NTSC == std->id) {
33839d08ab9SHans Verkuil 		hexium_set_standard(hexium, hexium_ntsc);
33939d08ab9SHans Verkuil 		hexium->cur_std = V4L2_STD_NTSC;
34039d08ab9SHans Verkuil 		return 0;
34139d08ab9SHans Verkuil 	} else if (V4L2_STD_SECAM == std->id) {
34239d08ab9SHans Verkuil 		hexium_set_standard(hexium, hexium_secam);
34339d08ab9SHans Verkuil 		hexium->cur_std = V4L2_STD_SECAM;
34439d08ab9SHans Verkuil 		return 0;
34539d08ab9SHans Verkuil 	}
34639d08ab9SHans Verkuil 
34739d08ab9SHans Verkuil 	return -1;
34839d08ab9SHans Verkuil }
34939d08ab9SHans Verkuil 
35039d08ab9SHans Verkuil static struct saa7146_extension hexium_extension;
35139d08ab9SHans Verkuil 
35239d08ab9SHans Verkuil static struct saa7146_pci_extension_data hexium_gemini_4bnc = {
35339d08ab9SHans Verkuil 	.ext_priv = "Hexium Gemini (4 BNC)",
35439d08ab9SHans Verkuil 	.ext = &hexium_extension,
35539d08ab9SHans Verkuil };
35639d08ab9SHans Verkuil 
35739d08ab9SHans Verkuil static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = {
35839d08ab9SHans Verkuil 	.ext_priv = "Hexium Gemini Dual (4 BNC)",
35939d08ab9SHans Verkuil 	.ext = &hexium_extension,
36039d08ab9SHans Verkuil };
36139d08ab9SHans Verkuil 
36239d08ab9SHans Verkuil static const struct pci_device_id pci_tbl[] = {
36339d08ab9SHans Verkuil 	{
36439d08ab9SHans Verkuil 	 .vendor = PCI_VENDOR_ID_PHILIPS,
36539d08ab9SHans Verkuil 	 .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
36639d08ab9SHans Verkuil 	 .subvendor = 0x17c8,
36739d08ab9SHans Verkuil 	 .subdevice = 0x2401,
36839d08ab9SHans Verkuil 	 .driver_data = (unsigned long) &hexium_gemini_4bnc,
36939d08ab9SHans Verkuil 	 },
37039d08ab9SHans Verkuil 	{
37139d08ab9SHans Verkuil 	 .vendor = PCI_VENDOR_ID_PHILIPS,
37239d08ab9SHans Verkuil 	 .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
37339d08ab9SHans Verkuil 	 .subvendor = 0x17c8,
37439d08ab9SHans Verkuil 	 .subdevice = 0x2402,
37539d08ab9SHans Verkuil 	 .driver_data = (unsigned long) &hexium_gemini_dual_4bnc,
37639d08ab9SHans Verkuil 	 },
37739d08ab9SHans Verkuil 	{
37839d08ab9SHans Verkuil 	 .vendor = 0,
37939d08ab9SHans Verkuil 	 }
38039d08ab9SHans Verkuil };
38139d08ab9SHans Verkuil 
38239d08ab9SHans Verkuil MODULE_DEVICE_TABLE(pci, pci_tbl);
38339d08ab9SHans Verkuil 
38439d08ab9SHans Verkuil static struct saa7146_ext_vv vv_data = {
38539d08ab9SHans Verkuil 	.inputs = HEXIUM_INPUTS,
38639d08ab9SHans Verkuil 	.capabilities = 0,
38739d08ab9SHans Verkuil 	.stds = &hexium_standards[0],
38839d08ab9SHans Verkuil 	.num_stds = ARRAY_SIZE(hexium_standards),
38939d08ab9SHans Verkuil 	.std_callback = &std_callback,
39039d08ab9SHans Verkuil };
39139d08ab9SHans Verkuil 
39239d08ab9SHans Verkuil static struct saa7146_extension hexium_extension = {
39339d08ab9SHans Verkuil 	.name = "hexium gemini",
39439d08ab9SHans Verkuil 	.flags = SAA7146_USE_I2C_IRQ,
39539d08ab9SHans Verkuil 
39639d08ab9SHans Verkuil 	.pci_tbl = &pci_tbl[0],
39739d08ab9SHans Verkuil 	.module = THIS_MODULE,
39839d08ab9SHans Verkuil 
39939d08ab9SHans Verkuil 	.attach = hexium_attach,
40039d08ab9SHans Verkuil 	.detach = hexium_detach,
40139d08ab9SHans Verkuil 
40239d08ab9SHans Verkuil 	.irq_mask = 0,
40339d08ab9SHans Verkuil 	.irq_func = NULL,
40439d08ab9SHans Verkuil };
40539d08ab9SHans Verkuil 
hexium_init_module(void)40639d08ab9SHans Verkuil static int __init hexium_init_module(void)
40739d08ab9SHans Verkuil {
40839d08ab9SHans Verkuil 	if (0 != saa7146_register_extension(&hexium_extension)) {
40939d08ab9SHans Verkuil 		DEB_S("failed to register extension\n");
41039d08ab9SHans Verkuil 		return -ENODEV;
41139d08ab9SHans Verkuil 	}
41239d08ab9SHans Verkuil 
41339d08ab9SHans Verkuil 	return 0;
41439d08ab9SHans Verkuil }
41539d08ab9SHans Verkuil 
hexium_cleanup_module(void)41639d08ab9SHans Verkuil static void __exit hexium_cleanup_module(void)
41739d08ab9SHans Verkuil {
41839d08ab9SHans Verkuil 	saa7146_unregister_extension(&hexium_extension);
41939d08ab9SHans Verkuil }
42039d08ab9SHans Verkuil 
42139d08ab9SHans Verkuil module_init(hexium_init_module);
42239d08ab9SHans Verkuil module_exit(hexium_cleanup_module);
42339d08ab9SHans Verkuil 
42439d08ab9SHans Verkuil MODULE_DESCRIPTION("video4linux-2 driver for Hexium Gemini frame grabber cards");
42539d08ab9SHans Verkuil MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
42639d08ab9SHans Verkuil MODULE_LICENSE("GPL");
427