~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/media/common/saa7146_fops.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
2
 
 
3
#include <media/saa7146_vv.h>
 
4
#include <linux/module.h>
 
5
 
 
6
/****************************************************************************/
 
7
/* resource management functions, shamelessly stolen from saa7134 driver */
 
8
 
 
9
int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit)
 
10
{
 
11
        struct saa7146_dev *dev = fh->dev;
 
12
        struct saa7146_vv *vv = dev->vv_data;
 
13
 
 
14
        if (fh->resources & bit) {
 
15
                DEB_D("already allocated! want: 0x%02x, cur:0x%02x\n",
 
16
                      bit, vv->resources);
 
17
                /* have it already allocated */
 
18
                return 1;
 
19
        }
 
20
 
 
21
        /* is it free? */
 
22
        if (vv->resources & bit) {
 
23
                DEB_D("locked! vv->resources:0x%02x, we want:0x%02x\n",
 
24
                      vv->resources, bit);
 
25
                /* no, someone else uses it */
 
26
                return 0;
 
27
        }
 
28
        /* it's free, grab it */
 
29
        fh->resources |= bit;
 
30
        vv->resources |= bit;
 
31
        DEB_D("res: get 0x%02x, cur:0x%02x\n", bit, vv->resources);
 
32
        return 1;
 
33
}
 
34
 
 
35
void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits)
 
36
{
 
37
        struct saa7146_dev *dev = fh->dev;
 
38
        struct saa7146_vv *vv = dev->vv_data;
 
39
 
 
40
        BUG_ON((fh->resources & bits) != bits);
 
41
 
 
42
        fh->resources &= ~bits;
 
43
        vv->resources &= ~bits;
 
44
        DEB_D("res: put 0x%02x, cur:0x%02x\n", bits, vv->resources);
 
45
}
 
46
 
 
47
 
 
48
/********************************************************************************/
 
49
/* common dma functions */
 
50
 
 
51
void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q,
 
52
                                                struct saa7146_buf *buf)
 
53
{
 
54
        struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
 
55
        DEB_EE("dev:%p, buf:%p\n", dev, buf);
 
56
 
 
57
        BUG_ON(in_interrupt());
 
58
 
 
59
        videobuf_waiton(q, &buf->vb, 0, 0);
 
60
        videobuf_dma_unmap(q->dev, dma);
 
61
        videobuf_dma_free(dma);
 
62
        buf->vb.state = VIDEOBUF_NEEDS_INIT;
 
63
}
 
64
 
 
65
 
 
66
/********************************************************************************/
 
67
/* common buffer functions */
 
68
 
 
69
int saa7146_buffer_queue(struct saa7146_dev *dev,
 
70
                         struct saa7146_dmaqueue *q,
 
71
                         struct saa7146_buf *buf)
 
72
{
 
73
        assert_spin_locked(&dev->slock);
 
74
        DEB_EE("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf);
 
75
 
 
76
        BUG_ON(!q);
 
77
 
 
78
        if (NULL == q->curr) {
 
79
                q->curr = buf;
 
80
                DEB_D("immediately activating buffer %p\n", buf);
 
81
                buf->activate(dev,buf,NULL);
 
82
        } else {
 
83
                list_add_tail(&buf->vb.queue,&q->queue);
 
84
                buf->vb.state = VIDEOBUF_QUEUED;
 
85
                DEB_D("adding buffer %p to queue. (active buffer present)\n",
 
86
                      buf);
 
87
        }
 
88
        return 0;
 
89
}
 
90
 
 
91
void saa7146_buffer_finish(struct saa7146_dev *dev,
 
92
                           struct saa7146_dmaqueue *q,
 
93
                           int state)
 
94
{
 
95
        assert_spin_locked(&dev->slock);
 
96
        DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state);
 
97
        DEB_EE("q->curr:%p\n", q->curr);
 
98
 
 
99
        BUG_ON(!q->curr);
 
100
 
 
101
        /* finish current buffer */
 
102
        if (NULL == q->curr) {
 
103
                DEB_D("aiii. no current buffer\n");
 
104
                return;
 
105
        }
 
106
 
 
107
        q->curr->vb.state = state;
 
108
        do_gettimeofday(&q->curr->vb.ts);
 
109
        wake_up(&q->curr->vb.done);
 
110
 
 
111
        q->curr = NULL;
 
112
}
 
113
 
 
114
void saa7146_buffer_next(struct saa7146_dev *dev,
 
115
                         struct saa7146_dmaqueue *q, int vbi)
 
116
{
 
117
        struct saa7146_buf *buf,*next = NULL;
 
118
 
 
119
        BUG_ON(!q);
 
120
 
 
121
        DEB_INT("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi);
 
122
 
 
123
        assert_spin_locked(&dev->slock);
 
124
        if (!list_empty(&q->queue)) {
 
125
                /* activate next one from queue */
 
126
                buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue);
 
127
                list_del(&buf->vb.queue);
 
128
                if (!list_empty(&q->queue))
 
129
                        next = list_entry(q->queue.next,struct saa7146_buf, vb.queue);
 
130
                q->curr = buf;
 
131
                DEB_INT("next buffer: buf:%p, prev:%p, next:%p\n",
 
132
                        buf, q->queue.prev, q->queue.next);
 
133
                buf->activate(dev,buf,next);
 
134
        } else {
 
135
                DEB_INT("no next buffer. stopping.\n");
 
136
                if( 0 != vbi ) {
 
137
                        /* turn off video-dma3 */
 
138
                        saa7146_write(dev,MC1, MASK_20);
 
139
                } else {
 
140
                        /* nothing to do -- just prevent next video-dma1 transfer
 
141
                           by lowering the protection address */
 
142
 
 
143
                        // fixme: fix this for vflip != 0
 
144
 
 
145
                        saa7146_write(dev, PROT_ADDR1, 0);
 
146
                        saa7146_write(dev, MC2, (MASK_02|MASK_18));
 
147
 
 
148
                        /* write the address of the rps-program */
 
149
                        saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
 
150
                        /* turn on rps */
 
151
                        saa7146_write(dev, MC1, (MASK_12 | MASK_28));
 
152
 
 
153
/*
 
154
                        printk("vdma%d.base_even:     0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
 
155
                        printk("vdma%d.base_odd:      0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
 
156
                        printk("vdma%d.prot_addr:     0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
 
157
                        printk("vdma%d.base_page:     0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
 
158
                        printk("vdma%d.pitch:         0x%08x\n", 1,saa7146_read(dev,PITCH1));
 
159
                        printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
 
160
*/
 
161
                }
 
162
                del_timer(&q->timeout);
 
163
        }
 
164
}
 
165
 
 
166
void saa7146_buffer_timeout(unsigned long data)
 
167
{
 
168
        struct saa7146_dmaqueue *q = (struct saa7146_dmaqueue*)data;
 
169
        struct saa7146_dev *dev = q->dev;
 
170
        unsigned long flags;
 
171
 
 
172
        DEB_EE("dev:%p, dmaq:%p\n", dev, q);
 
173
 
 
174
        spin_lock_irqsave(&dev->slock,flags);
 
175
        if (q->curr) {
 
176
                DEB_D("timeout on %p\n", q->curr);
 
177
                saa7146_buffer_finish(dev,q,VIDEOBUF_ERROR);
 
178
        }
 
179
 
 
180
        /* we don't restart the transfer here like other drivers do. when
 
181
           a streaming capture is disabled, the timeout function will be
 
182
           called for the current buffer. if we activate the next buffer now,
 
183
           we mess up our capture logic. if a timeout occurs on another buffer,
 
184
           then something is seriously broken before, so no need to buffer the
 
185
           next capture IMHO... */
 
186
/*
 
187
        saa7146_buffer_next(dev,q);
 
188
*/
 
189
        spin_unlock_irqrestore(&dev->slock,flags);
 
190
}
 
191
 
 
192
/********************************************************************************/
 
193
/* file operations */
 
194
 
 
195
static int fops_open(struct file *file)
 
196
{
 
197
        struct video_device *vdev = video_devdata(file);
 
198
        struct saa7146_dev *dev = video_drvdata(file);
 
199
        struct saa7146_fh *fh = NULL;
 
200
        int result = 0;
 
201
 
 
202
        enum v4l2_buf_type type;
 
203
 
 
204
        DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev));
 
205
 
 
206
        if (mutex_lock_interruptible(&saa7146_devices_lock))
 
207
                return -ERESTARTSYS;
 
208
 
 
209
        DEB_D("using: %p\n", dev);
 
210
 
 
211
        type = vdev->vfl_type == VFL_TYPE_GRABBER
 
212
             ? V4L2_BUF_TYPE_VIDEO_CAPTURE
 
213
             : V4L2_BUF_TYPE_VBI_CAPTURE;
 
214
 
 
215
        /* check if an extension is registered */
 
216
        if( NULL == dev->ext ) {
 
217
                DEB_S("no extension registered for this device\n");
 
218
                result = -ENODEV;
 
219
                goto out;
 
220
        }
 
221
 
 
222
        /* allocate per open data */
 
223
        fh = kzalloc(sizeof(*fh),GFP_KERNEL);
 
224
        if (NULL == fh) {
 
225
                DEB_S("cannot allocate memory for per open data\n");
 
226
                result = -ENOMEM;
 
227
                goto out;
 
228
        }
 
229
 
 
230
        file->private_data = fh;
 
231
        fh->dev = dev;
 
232
        fh->type = type;
 
233
 
 
234
        if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
 
235
                DEB_S("initializing vbi...\n");
 
236
                if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
 
237
                        result = saa7146_vbi_uops.open(dev,file);
 
238
                if (dev->ext_vv_data->vbi_fops.open)
 
239
                        dev->ext_vv_data->vbi_fops.open(file);
 
240
        } else {
 
241
                DEB_S("initializing video...\n");
 
242
                result = saa7146_video_uops.open(dev,file);
 
243
        }
 
244
 
 
245
        if (0 != result) {
 
246
                goto out;
 
247
        }
 
248
 
 
249
        if( 0 == try_module_get(dev->ext->module)) {
 
250
                result = -EINVAL;
 
251
                goto out;
 
252
        }
 
253
 
 
254
        result = 0;
 
255
out:
 
256
        if (fh && result != 0) {
 
257
                kfree(fh);
 
258
                file->private_data = NULL;
 
259
        }
 
260
        mutex_unlock(&saa7146_devices_lock);
 
261
        return result;
 
262
}
 
263
 
 
264
static int fops_release(struct file *file)
 
265
{
 
266
        struct saa7146_fh  *fh  = file->private_data;
 
267
        struct saa7146_dev *dev = fh->dev;
 
268
 
 
269
        DEB_EE("file:%p\n", file);
 
270
 
 
271
        if (mutex_lock_interruptible(&saa7146_devices_lock))
 
272
                return -ERESTARTSYS;
 
273
 
 
274
        if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
 
275
                if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
 
276
                        saa7146_vbi_uops.release(dev,file);
 
277
                if (dev->ext_vv_data->vbi_fops.release)
 
278
                        dev->ext_vv_data->vbi_fops.release(file);
 
279
        } else {
 
280
                saa7146_video_uops.release(dev,file);
 
281
        }
 
282
 
 
283
        module_put(dev->ext->module);
 
284
        file->private_data = NULL;
 
285
        kfree(fh);
 
286
 
 
287
        mutex_unlock(&saa7146_devices_lock);
 
288
 
 
289
        return 0;
 
290
}
 
291
 
 
292
static int fops_mmap(struct file *file, struct vm_area_struct * vma)
 
293
{
 
294
        struct saa7146_fh *fh = file->private_data;
 
295
        struct videobuf_queue *q;
 
296
 
 
297
        switch (fh->type) {
 
298
        case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
 
299
                DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",
 
300
                       file, vma);
 
301
                q = &fh->video_q;
 
302
                break;
 
303
                }
 
304
        case V4L2_BUF_TYPE_VBI_CAPTURE: {
 
305
                DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",
 
306
                       file, vma);
 
307
                q = &fh->vbi_q;
 
308
                break;
 
309
                }
 
310
        default:
 
311
                BUG();
 
312
                return 0;
 
313
        }
 
314
 
 
315
        return videobuf_mmap_mapper(q,vma);
 
316
}
 
317
 
 
318
static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
 
319
{
 
320
        struct saa7146_fh *fh = file->private_data;
 
321
        struct videobuf_buffer *buf = NULL;
 
322
        struct videobuf_queue *q;
 
323
 
 
324
        DEB_EE("file:%p, poll:%p\n", file, wait);
 
325
 
 
326
        if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
 
327
                if( 0 == fh->vbi_q.streaming )
 
328
                        return videobuf_poll_stream(file, &fh->vbi_q, wait);
 
329
                q = &fh->vbi_q;
 
330
        } else {
 
331
                DEB_D("using video queue\n");
 
332
                q = &fh->video_q;
 
333
        }
 
334
 
 
335
        if (!list_empty(&q->stream))
 
336
                buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
 
337
 
 
338
        if (!buf) {
 
339
                DEB_D("buf == NULL!\n");
 
340
                return POLLERR;
 
341
        }
 
342
 
 
343
        poll_wait(file, &buf->done, wait);
 
344
        if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) {
 
345
                DEB_D("poll succeeded!\n");
 
346
                return POLLIN|POLLRDNORM;
 
347
        }
 
348
 
 
349
        DEB_D("nothing to poll for, buf->state:%d\n", buf->state);
 
350
        return 0;
 
351
}
 
352
 
 
353
static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
 
354
{
 
355
        struct saa7146_fh *fh = file->private_data;
 
356
 
 
357
        switch (fh->type) {
 
358
        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 
359
/*
 
360
                DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun",
 
361
                       file, data, (unsigned long)count);
 
362
*/
 
363
                return saa7146_video_uops.read(file,data,count,ppos);
 
364
        case V4L2_BUF_TYPE_VBI_CAPTURE:
 
365
/*
 
366
                DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n",
 
367
                       file, data, (unsigned long)count);
 
368
*/
 
369
                if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
 
370
                        return saa7146_vbi_uops.read(file,data,count,ppos);
 
371
                return -EINVAL;
 
372
        default:
 
373
                BUG();
 
374
                return 0;
 
375
        }
 
376
}
 
377
 
 
378
static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
 
379
{
 
380
        struct saa7146_fh *fh = file->private_data;
 
381
 
 
382
        switch (fh->type) {
 
383
        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 
384
                return -EINVAL;
 
385
        case V4L2_BUF_TYPE_VBI_CAPTURE:
 
386
                if (fh->dev->ext_vv_data->vbi_fops.write)
 
387
                        return fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos);
 
388
                else
 
389
                        return -EINVAL;
 
390
        default:
 
391
                BUG();
 
392
                return -EINVAL;
 
393
        }
 
394
}
 
395
 
 
396
static const struct v4l2_file_operations video_fops =
 
397
{
 
398
        .owner          = THIS_MODULE,
 
399
        .open           = fops_open,
 
400
        .release        = fops_release,
 
401
        .read           = fops_read,
 
402
        .write          = fops_write,
 
403
        .poll           = fops_poll,
 
404
        .mmap           = fops_mmap,
 
405
        .unlocked_ioctl = video_ioctl2,
 
406
};
 
407
 
 
408
static void vv_callback(struct saa7146_dev *dev, unsigned long status)
 
409
{
 
410
        u32 isr = status;
 
411
 
 
412
        DEB_INT("dev:%p, isr:0x%08x\n", dev, (u32)status);
 
413
 
 
414
        if (0 != (isr & (MASK_27))) {
 
415
                DEB_INT("irq: RPS0 (0x%08x)\n", isr);
 
416
                saa7146_video_uops.irq_done(dev,isr);
 
417
        }
 
418
 
 
419
        if (0 != (isr & (MASK_28))) {
 
420
                u32 mc2 = saa7146_read(dev, MC2);
 
421
                if( 0 != (mc2 & MASK_15)) {
 
422
                        DEB_INT("irq: RPS1 vbi workaround (0x%08x)\n", isr);
 
423
                        wake_up(&dev->vv_data->vbi_wq);
 
424
                        saa7146_write(dev,MC2, MASK_31);
 
425
                        return;
 
426
                }
 
427
                DEB_INT("irq: RPS1 (0x%08x)\n", isr);
 
428
                saa7146_vbi_uops.irq_done(dev,isr);
 
429
        }
 
430
}
 
431
 
 
432
int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
 
433
{
 
434
        struct saa7146_vv *vv;
 
435
        int err;
 
436
 
 
437
        err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev);
 
438
        if (err)
 
439
                return err;
 
440
 
 
441
        vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);
 
442
        if (vv == NULL) {
 
443
                ERR("out of memory. aborting.\n");
 
444
                return -ENOMEM;
 
445
        }
 
446
        ext_vv->ops = saa7146_video_ioctl_ops;
 
447
        ext_vv->core_ops = &saa7146_video_ioctl_ops;
 
448
 
 
449
        DEB_EE("dev:%p\n", dev);
 
450
 
 
451
        /* set default values for video parts of the saa7146 */
 
452
        saa7146_write(dev, BCS_CTRL, 0x80400040);
 
453
 
 
454
        /* enable video-port pins */
 
455
        saa7146_write(dev, MC1, (MASK_10 | MASK_26));
 
456
 
 
457
        /* save per-device extension data (one extension can
 
458
           handle different devices that might need different
 
459
           configuration data) */
 
460
        dev->ext_vv_data = ext_vv;
 
461
 
 
462
        vv->d_clipping.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_CLIPPING_MEM, &vv->d_clipping.dma_handle);
 
463
        if( NULL == vv->d_clipping.cpu_addr ) {
 
464
                ERR("out of memory. aborting.\n");
 
465
                kfree(vv);
 
466
                return -1;
 
467
        }
 
468
        memset(vv->d_clipping.cpu_addr, 0x0, SAA7146_CLIPPING_MEM);
 
469
 
 
470
        saa7146_video_uops.init(dev,vv);
 
471
        if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
 
472
                saa7146_vbi_uops.init(dev,vv);
 
473
 
 
474
        dev->vv_data = vv;
 
475
        dev->vv_callback = &vv_callback;
 
476
 
 
477
        return 0;
 
478
}
 
479
EXPORT_SYMBOL_GPL(saa7146_vv_init);
 
480
 
 
481
int saa7146_vv_release(struct saa7146_dev* dev)
 
482
{
 
483
        struct saa7146_vv *vv = dev->vv_data;
 
484
 
 
485
        DEB_EE("dev:%p\n", dev);
 
486
 
 
487
        v4l2_device_unregister(&dev->v4l2_dev);
 
488
        pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle);
 
489
        kfree(vv);
 
490
        dev->vv_data = NULL;
 
491
        dev->vv_callback = NULL;
 
492
 
 
493
        return 0;
 
494
}
 
495
EXPORT_SYMBOL_GPL(saa7146_vv_release);
 
496
 
 
497
int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
 
498
                            char *name, int type)
 
499
{
 
500
        struct video_device *vfd;
 
501
        int err;
 
502
        int i;
 
503
 
 
504
        DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type);
 
505
 
 
506
        // released by vfd->release
 
507
        vfd = video_device_alloc();
 
508
        if (vfd == NULL)
 
509
                return -ENOMEM;
 
510
 
 
511
        vfd->fops = &video_fops;
 
512
        vfd->ioctl_ops = &dev->ext_vv_data->ops;
 
513
        vfd->release = video_device_release;
 
514
        vfd->lock = &dev->v4l2_lock;
 
515
        vfd->tvnorms = 0;
 
516
        for (i = 0; i < dev->ext_vv_data->num_stds; i++)
 
517
                vfd->tvnorms |= dev->ext_vv_data->stds[i].id;
 
518
        strlcpy(vfd->name, name, sizeof(vfd->name));
 
519
        video_set_drvdata(vfd, dev);
 
520
 
 
521
        err = video_register_device(vfd, type, -1);
 
522
        if (err < 0) {
 
523
                ERR("cannot register v4l2 device. skipping.\n");
 
524
                video_device_release(vfd);
 
525
                return err;
 
526
        }
 
527
 
 
528
        pr_info("%s: registered device %s [v4l2]\n",
 
529
                dev->name, video_device_node_name(vfd));
 
530
 
 
531
        *vid = vfd;
 
532
        return 0;
 
533
}
 
534
EXPORT_SYMBOL_GPL(saa7146_register_device);
 
535
 
 
536
int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev)
 
537
{
 
538
        DEB_EE("dev:%p\n", dev);
 
539
 
 
540
        video_unregister_device(*vid);
 
541
        *vid = NULL;
 
542
 
 
543
        return 0;
 
544
}
 
545
EXPORT_SYMBOL_GPL(saa7146_unregister_device);
 
546
 
 
547
static int __init saa7146_vv_init_module(void)
 
548
{
 
549
        return 0;
 
550
}
 
551
 
 
552
 
 
553
static void __exit saa7146_vv_cleanup_module(void)
 
554
{
 
555
}
 
556
 
 
557
module_init(saa7146_vv_init_module);
 
558
module_exit(saa7146_vv_cleanup_module);
 
559
 
 
560
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
 
561
MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware");
 
562
MODULE_LICENSE("GPL");