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