linux/drivers/media/video/hexium_gemini.c
<<
>>
Prefs
   1/*
   2    hexium_gemini.c - v4l2 driver for Hexium Gemini frame grabber cards
   3
   4    Visit http://www.mihu.de/linux/saa7146/ and follow the link
   5    to "hexium" for further details about this card.
   6
   7    Copyright (C) 2003 Michael Hunold <michael@mihu.de>
   8
   9    This program is free software; you can redistribute it and/or modify
  10    it under the terms of the GNU General Public License as published by
  11    the Free Software Foundation; either version 2 of the License, or
  12    (at your option) any later version.
  13
  14    This program is distributed in the hope that it will be useful,
  15    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17    GNU General Public License for more details.
  18
  19    You should have received a copy of the GNU General Public License
  20    along with this program; if not, write to the Free Software
  21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22*/
  23
  24#define DEBUG_VARIABLE debug
  25
  26#include <media/saa7146_vv.h>
  27
  28static int debug;
  29module_param(debug, int, 0);
  30MODULE_PARM_DESC(debug, "debug verbosity");
  31
  32/* global variables */
  33static int hexium_num;
  34
  35#define HEXIUM_GEMINI                   4
  36#define HEXIUM_GEMINI_DUAL              5
  37
  38#define HEXIUM_INPUTS   9
  39static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
  40        { 0, "CVBS 1",  V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
  41        { 1, "CVBS 2",  V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
  42        { 2, "CVBS 3",  V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
  43        { 3, "CVBS 4",  V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
  44        { 4, "CVBS 5",  V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
  45        { 5, "CVBS 6",  V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
  46        { 6, "Y/C 1",   V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
  47        { 7, "Y/C 2",   V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
  48        { 8, "Y/C 3",   V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
  49};
  50
  51#define HEXIUM_AUDIOS   0
  52
  53struct hexium_data
  54{
  55        s8 adr;
  56        u8 byte;
  57};
  58
  59#define HEXIUM_CONTROLS 1
  60static struct v4l2_queryctrl hexium_controls[] = {
  61        { V4L2_CID_PRIVATE_BASE, V4L2_CTRL_TYPE_BOOLEAN, "B/W", 0, 1, 1, 0, 0 },
  62};
  63
  64#define HEXIUM_GEMINI_V_1_0             1
  65#define HEXIUM_GEMINI_DUAL_V_1_0        2
  66
  67struct hexium
  68{
  69        int type;
  70
  71        struct video_device     *video_dev;
  72        struct i2c_adapter      i2c_adapter;
  73
  74        int             cur_input;      /* current input */
  75        v4l2_std_id     cur_std;        /* current standard */
  76        int             cur_bw;         /* current black/white status */
  77};
  78
  79/* Samsung KS0127B decoder default registers */
  80static u8 hexium_ks0127b[0x100]={
  81/*00*/ 0x00,0x52,0x30,0x40,0x01,0x0C,0x2A,0x10,
  82/*08*/ 0x00,0x00,0x00,0x60,0x00,0x00,0x0F,0x06,
  83/*10*/ 0x00,0x00,0xE4,0xC0,0x00,0x00,0x00,0x00,
  84/*18*/ 0x14,0x9B,0xFE,0xFF,0xFC,0xFF,0x03,0x22,
  85/*20*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  86/*28*/ 0x00,0x00,0x00,0x00,0x00,0x2C,0x9B,0x00,
  87/*30*/ 0x00,0x00,0x10,0x80,0x80,0x10,0x80,0x80,
  88/*38*/ 0x01,0x04,0x00,0x00,0x00,0x29,0xC0,0x00,
  89/*40*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  90/*48*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  91/*50*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  92/*58*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  93/*60*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  94/*68*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  95/*70*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  96/*78*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  97/*80*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  98/*88*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  99/*90*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 100/*98*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 101/*A0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 102/*A8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 103/*B0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 104/*B8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 105/*C0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 106/*C8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 107/*D0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 108/*D8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 109/*E0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 110/*E8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 111/*F0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 112/*F8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
 113};
 114
 115static struct hexium_data hexium_pal[] = {
 116        { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
 117};
 118
 119static struct hexium_data hexium_pal_bw[] = {
 120        { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
 121};
 122
 123static struct hexium_data hexium_ntsc[] = {
 124        { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
 125};
 126
 127static struct hexium_data hexium_ntsc_bw[] = {
 128        { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
 129};
 130
 131static struct hexium_data hexium_secam[] = {
 132        { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
 133};
 134
 135static struct hexium_data hexium_input_select[] = {
 136        { 0x02, 0x60 },
 137        { 0x02, 0x64 },
 138        { 0x02, 0x61 },
 139        { 0x02, 0x65 },
 140        { 0x02, 0x62 },
 141        { 0x02, 0x66 },
 142        { 0x02, 0x68 },
 143        { 0x02, 0x69 },
 144        { 0x02, 0x6A },
 145};
 146
 147/* fixme: h_offset = 0 for Hexium Gemini *Dual*, which
 148   are currently *not* supported*/
 149static struct saa7146_standard hexium_standards[] = {
 150        {
 151                .name   = "PAL",        .id     = V4L2_STD_PAL,
 152                .v_offset       = 28,   .v_field        = 288,
 153                .h_offset       = 1,    .h_pixels       = 680,
 154                .v_max_out      = 576,  .h_max_out      = 768,
 155        }, {
 156                .name   = "NTSC",       .id     = V4L2_STD_NTSC,
 157                .v_offset       = 28,   .v_field        = 240,
 158                .h_offset       = 1,    .h_pixels       = 640,
 159                .v_max_out      = 480,  .h_max_out      = 640,
 160        }, {
 161                .name   = "SECAM",      .id     = V4L2_STD_SECAM,
 162                .v_offset       = 28,   .v_field        = 288,
 163                .h_offset       = 1,    .h_pixels       = 720,
 164                .v_max_out      = 576,  .h_max_out      = 768,
 165        }
 166};
 167
 168/* bring hardware to a sane state. this has to be done, just in case someone
 169   wants to capture from this device before it has been properly initialized.
 170   the capture engine would badly fail, because no valid signal arrives on the
 171   saa7146, thus leading to timeouts and stuff. */
 172static int hexium_init_done(struct saa7146_dev *dev)
 173{
 174        struct hexium *hexium = (struct hexium *) dev->ext_priv;
 175        union i2c_smbus_data data;
 176        int i = 0;
 177
 178        DEB_D(("hexium_init_done called.\n"));
 179
 180        /* initialize the helper ics to useful values */
 181        for (i = 0; i < sizeof(hexium_ks0127b); i++) {
 182                data.byte = hexium_ks0127b[i];
 183                if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
 184                        printk("hexium_gemini: hexium_init_done() failed for address 0x%02x\n", i);
 185                }
 186        }
 187
 188        return 0;
 189}
 190
 191static int hexium_set_input(struct hexium *hexium, int input)
 192{
 193        union i2c_smbus_data data;
 194
 195        DEB_D((".\n"));
 196
 197        data.byte = hexium_input_select[input].byte;
 198        if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, &data)) {
 199                return -1;
 200        }
 201
 202        return 0;
 203}
 204
 205static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec)
 206{
 207        union i2c_smbus_data data;
 208        int i = 0;
 209
 210        DEB_D((".\n"));
 211
 212        while (vdec[i].adr != -1) {
 213                data.byte = vdec[i].byte;
 214                if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, vdec[i].adr, I2C_SMBUS_BYTE_DATA, &data)) {
 215                        printk("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n", i);
 216                        return -1;
 217                }
 218                i++;
 219        }
 220        return 0;
 221}
 222
 223static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
 224{
 225        DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
 226
 227        if (i->index >= HEXIUM_INPUTS)
 228                return -EINVAL;
 229
 230        memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
 231
 232        DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
 233        return 0;
 234}
 235
 236static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
 237{
 238        struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
 239        struct hexium *hexium = (struct hexium *) dev->ext_priv;
 240
 241        *input = hexium->cur_input;
 242
 243        DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
 244        return 0;
 245}
 246
 247static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
 248{
 249        struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
 250        struct hexium *hexium = (struct hexium *) dev->ext_priv;
 251
 252        DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
 253
 254        if (input < 0 || input >= HEXIUM_INPUTS)
 255                return -EINVAL;
 256
 257        hexium->cur_input = input;
 258        hexium_set_input(hexium, input);
 259        return 0;
 260}
 261
 262/* the saa7146 provides some controls (brightness, contrast, saturation)
 263   which gets registered *after* this function. because of this we have
 264   to return with a value != 0 even if the function succeded.. */
 265static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc)
 266{
 267        struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
 268        int i;
 269
 270        for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
 271                if (hexium_controls[i].id == qc->id) {
 272                        *qc = hexium_controls[i];
 273                        DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id));
 274                        return 0;
 275                }
 276        }
 277        return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc);
 278}
 279
 280static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
 281{
 282        struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
 283        struct hexium *hexium = (struct hexium *) dev->ext_priv;
 284        int i;
 285
 286        for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
 287                if (hexium_controls[i].id == vc->id)
 288                        break;
 289        }
 290
 291        if (i < 0)
 292                return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc);
 293
 294        if (vc->id == V4L2_CID_PRIVATE_BASE) {
 295                vc->value = hexium->cur_bw;
 296                DEB_D(("VIDIOC_G_CTRL BW:%d.\n", vc->value));
 297                return 0;
 298        }
 299        return -EINVAL;
 300}
 301
 302static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
 303{
 304        struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
 305        struct hexium *hexium = (struct hexium *) dev->ext_priv;
 306        int i = 0;
 307
 308        for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
 309                if (hexium_controls[i].id == vc->id)
 310                        break;
 311        }
 312
 313        if (i < 0)
 314                return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc);
 315
 316        if (vc->id == V4L2_CID_PRIVATE_BASE)
 317                hexium->cur_bw = vc->value;
 318
 319        DEB_D(("VIDIOC_S_CTRL BW:%d.\n", hexium->cur_bw));
 320
 321        if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
 322                hexium_set_standard(hexium, hexium_pal);
 323                return 0;
 324        }
 325        if (0 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
 326                hexium_set_standard(hexium, hexium_ntsc);
 327                return 0;
 328        }
 329        if (0 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) {
 330                hexium_set_standard(hexium, hexium_secam);
 331                return 0;
 332        }
 333        if (1 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
 334                hexium_set_standard(hexium, hexium_pal_bw);
 335                return 0;
 336        }
 337        if (1 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
 338                hexium_set_standard(hexium, hexium_ntsc_bw);
 339                return 0;
 340        }
 341        if (1 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std)
 342                /* fixme: is there no bw secam mode? */
 343                return -EINVAL;
 344
 345        return -EINVAL;
 346}
 347
 348
 349static struct saa7146_ext_vv vv_data;
 350
 351/* this function only gets called when the probing was successful */
 352static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
 353{
 354        struct hexium *hexium = (struct hexium *) dev->ext_priv;
 355
 356        DEB_EE((".\n"));
 357
 358        hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL);
 359        if (NULL == hexium) {
 360                printk("hexium_gemini: not enough kernel memory in hexium_attach().\n");
 361                return -ENOMEM;
 362        }
 363        dev->ext_priv = hexium;
 364
 365        /* enable i2c-port pins */
 366        saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
 367
 368        hexium->i2c_adapter = (struct i2c_adapter) {
 369                .class = I2C_CLASS_TV_ANALOG,
 370                .name = "hexium gemini",
 371        };
 372        saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
 373        if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
 374                DEB_S(("cannot register i2c-device. skipping.\n"));
 375                kfree(hexium);
 376                return -EFAULT;
 377        }
 378
 379        /*  set HWControl GPIO number 2 */
 380        saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
 381
 382        saa7146_write(dev, DD1_INIT, 0x07000700);
 383        saa7146_write(dev, DD1_STREAM_B, 0x00000000);
 384        saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
 385
 386        /* the rest */
 387        hexium->cur_input = 0;
 388        hexium_init_done(dev);
 389
 390        hexium_set_standard(hexium, hexium_pal);
 391        hexium->cur_std = V4L2_STD_PAL;
 392
 393        hexium_set_input(hexium, 0);
 394        hexium->cur_input = 0;
 395
 396        saa7146_vv_init(dev, &vv_data);
 397        vv_data.ops.vidioc_queryctrl = vidioc_queryctrl;
 398        vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl;
 399        vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl;
 400        vv_data.ops.vidioc_enum_input = vidioc_enum_input;
 401        vv_data.ops.vidioc_g_input = vidioc_g_input;
 402        vv_data.ops.vidioc_s_input = vidioc_s_input;
 403        if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER)) {
 404                printk("hexium_gemini: cannot register capture v4l2 device. skipping.\n");
 405                return -1;
 406        }
 407
 408        printk("hexium_gemini: found 'hexium gemini' frame grabber-%d.\n", hexium_num);
 409        hexium_num++;
 410
 411        return 0;
 412}
 413
 414static int hexium_detach(struct saa7146_dev *dev)
 415{
 416        struct hexium *hexium = (struct hexium *) dev->ext_priv;
 417
 418        DEB_EE(("dev:%p\n", dev));
 419
 420        saa7146_unregister_device(&hexium->video_dev, dev);
 421        saa7146_vv_release(dev);
 422
 423        hexium_num--;
 424
 425        i2c_del_adapter(&hexium->i2c_adapter);
 426        kfree(hexium);
 427        return 0;
 428}
 429
 430static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
 431{
 432        struct hexium *hexium = (struct hexium *) dev->ext_priv;
 433
 434        if (V4L2_STD_PAL == std->id) {
 435                hexium_set_standard(hexium, hexium_pal);
 436                hexium->cur_std = V4L2_STD_PAL;
 437                return 0;
 438        } else if (V4L2_STD_NTSC == std->id) {
 439                hexium_set_standard(hexium, hexium_ntsc);
 440                hexium->cur_std = V4L2_STD_NTSC;
 441                return 0;
 442        } else if (V4L2_STD_SECAM == std->id) {
 443                hexium_set_standard(hexium, hexium_secam);
 444                hexium->cur_std = V4L2_STD_SECAM;
 445                return 0;
 446        }
 447
 448        return -1;
 449}
 450
 451static struct saa7146_extension hexium_extension;
 452
 453static struct saa7146_pci_extension_data hexium_gemini_4bnc = {
 454        .ext_priv = "Hexium Gemini (4 BNC)",
 455        .ext = &hexium_extension,
 456};
 457
 458static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = {
 459        .ext_priv = "Hexium Gemini Dual (4 BNC)",
 460        .ext = &hexium_extension,
 461};
 462
 463static struct pci_device_id pci_tbl[] = {
 464        {
 465         .vendor = PCI_VENDOR_ID_PHILIPS,
 466         .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
 467         .subvendor = 0x17c8,
 468         .subdevice = 0x2401,
 469         .driver_data = (unsigned long) &hexium_gemini_4bnc,
 470         },
 471        {
 472         .vendor = PCI_VENDOR_ID_PHILIPS,
 473         .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
 474         .subvendor = 0x17c8,
 475         .subdevice = 0x2402,
 476         .driver_data = (unsigned long) &hexium_gemini_dual_4bnc,
 477         },
 478        {
 479         .vendor = 0,
 480         }
 481};
 482
 483MODULE_DEVICE_TABLE(pci, pci_tbl);
 484
 485static struct saa7146_ext_vv vv_data = {
 486        .inputs = HEXIUM_INPUTS,
 487        .capabilities = 0,
 488        .stds = &hexium_standards[0],
 489        .num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard),
 490        .std_callback = &std_callback,
 491};
 492
 493static struct saa7146_extension hexium_extension = {
 494        .name = "hexium gemini",
 495        .flags = SAA7146_USE_I2C_IRQ,
 496
 497        .pci_tbl = &pci_tbl[0],
 498        .module = THIS_MODULE,
 499
 500        .attach = hexium_attach,
 501        .detach = hexium_detach,
 502
 503        .irq_mask = 0,
 504        .irq_func = NULL,
 505};
 506
 507static int __init hexium_init_module(void)
 508{
 509        if (0 != saa7146_register_extension(&hexium_extension)) {
 510                DEB_S(("failed to register extension.\n"));
 511                return -ENODEV;
 512        }
 513
 514        return 0;
 515}
 516
 517static void __exit hexium_cleanup_module(void)
 518{
 519        saa7146_unregister_extension(&hexium_extension);
 520}
 521
 522module_init(hexium_init_module);
 523module_exit(hexium_cleanup_module);
 524
 525MODULE_DESCRIPTION("video4linux-2 driver for Hexium Gemini frame grabber cards");
 526MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
 527MODULE_LICENSE("GPL");
 528