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, V4L2_IN_CAP_STD },
  41        { 1, "CVBS 2",  V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
  42        { 2, "CVBS 3",  V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
  43        { 3, "CVBS 4",  V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
  44        { 4, "CVBS 5",  V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
  45        { 5, "CVBS 6",  V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
  46        { 6, "Y/C 1",   V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
  47        { 7, "Y/C 2",   V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
  48        { 8, "Y/C 3",   V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
  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 >= 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        int ret;
 356
 357        DEB_EE((".\n"));
 358
 359        hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL);
 360        if (NULL == hexium) {
 361                printk("hexium_gemini: not enough kernel memory in hexium_attach().\n");
 362                return -ENOMEM;
 363        }
 364        dev->ext_priv = hexium;
 365
 366        /* enable i2c-port pins */
 367        saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
 368
 369        hexium->i2c_adapter = (struct i2c_adapter) {
 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        ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER);
 404        if (ret < 0) {
 405                printk("hexium_gemini: cannot register capture v4l2 device. skipping.\n");
 406                return ret;
 407        }
 408
 409        printk("hexium_gemini: found 'hexium gemini' frame grabber-%d.\n", hexium_num);
 410        hexium_num++;
 411
 412        return 0;
 413}
 414
 415static int hexium_detach(struct saa7146_dev *dev)
 416{
 417        struct hexium *hexium = (struct hexium *) dev->ext_priv;
 418
 419        DEB_EE(("dev:%p\n", dev));
 420
 421        saa7146_unregister_device(&hexium->video_dev, dev);
 422        saa7146_vv_release(dev);
 423
 424        hexium_num--;
 425
 426        i2c_del_adapter(&hexium->i2c_adapter);
 427        kfree(hexium);
 428        return 0;
 429}
 430
 431static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
 432{
 433        struct hexium *hexium = (struct hexium *) dev->ext_priv;
 434
 435        if (V4L2_STD_PAL == std->id) {
 436                hexium_set_standard(hexium, hexium_pal);
 437                hexium->cur_std = V4L2_STD_PAL;
 438                return 0;
 439        } else if (V4L2_STD_NTSC == std->id) {
 440                hexium_set_standard(hexium, hexium_ntsc);
 441                hexium->cur_std = V4L2_STD_NTSC;
 442                return 0;
 443        } else if (V4L2_STD_SECAM == std->id) {
 444                hexium_set_standard(hexium, hexium_secam);
 445                hexium->cur_std = V4L2_STD_SECAM;
 446                return 0;
 447        }
 448
 449        return -1;
 450}
 451
 452static struct saa7146_extension hexium_extension;
 453
 454static struct saa7146_pci_extension_data hexium_gemini_4bnc = {
 455        .ext_priv = "Hexium Gemini (4 BNC)",
 456        .ext = &hexium_extension,
 457};
 458
 459static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = {
 460        .ext_priv = "Hexium Gemini Dual (4 BNC)",
 461        .ext = &hexium_extension,
 462};
 463
 464static struct pci_device_id pci_tbl[] = {
 465        {
 466         .vendor = PCI_VENDOR_ID_PHILIPS,
 467         .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
 468         .subvendor = 0x17c8,
 469         .subdevice = 0x2401,
 470         .driver_data = (unsigned long) &hexium_gemini_4bnc,
 471         },
 472        {
 473         .vendor = PCI_VENDOR_ID_PHILIPS,
 474         .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
 475         .subvendor = 0x17c8,
 476         .subdevice = 0x2402,
 477         .driver_data = (unsigned long) &hexium_gemini_dual_4bnc,
 478         },
 479        {
 480         .vendor = 0,
 481         }
 482};
 483
 484MODULE_DEVICE_TABLE(pci, pci_tbl);
 485
 486static struct saa7146_ext_vv vv_data = {
 487        .inputs = HEXIUM_INPUTS,
 488        .capabilities = 0,
 489        .stds = &hexium_standards[0],
 490        .num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard),
 491        .std_callback = &std_callback,
 492};
 493
 494static struct saa7146_extension hexium_extension = {
 495        .name = "hexium gemini",
 496        .flags = SAA7146_USE_I2C_IRQ,
 497
 498        .pci_tbl = &pci_tbl[0],
 499        .module = THIS_MODULE,
 500
 501        .attach = hexium_attach,
 502        .detach = hexium_detach,
 503
 504        .irq_mask = 0,
 505        .irq_func = NULL,
 506};
 507
 508static int __init hexium_init_module(void)
 509{
 510        if (0 != saa7146_register_extension(&hexium_extension)) {
 511                DEB_S(("failed to register extension.\n"));
 512                return -ENODEV;
 513        }
 514
 515        return 0;
 516}
 517
 518static void __exit hexium_cleanup_module(void)
 519{
 520        saa7146_unregister_extension(&hexium_extension);
 521}
 522
 523module_init(hexium_init_module);
 524module_exit(hexium_cleanup_module);
 525
 526MODULE_DESCRIPTION("video4linux-2 driver for Hexium Gemini frame grabber cards");
 527MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
 528MODULE_LICENSE("GPL");
 529