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