saa7146_fops.c 13.9 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <media/saa7146_vv.h>

/****************************************************************************/
/* resource management functions, shamelessly stolen from saa7134 driver */

int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit)
{
	struct saa7146_dev *dev = fh->dev;
	struct saa7146_vv *vv = dev->vv_data;

	if (fh->resources & bit) {
		DEB_D(("already allocated! want: 0x%02x, cur:0x%02x\n",bit,vv->resources));
		/* have it already allocated */
		return 1;
	}

	/* is it free? */
18
	mutex_lock(&dev->lock);
Linus Torvalds's avatar
Linus Torvalds committed
19
20
21
	if (vv->resources & bit) {
		DEB_D(("locked! vv->resources:0x%02x, we want:0x%02x\n",vv->resources,bit));
		/* no, someone else uses it */
22
		mutex_unlock(&dev->lock);
Linus Torvalds's avatar
Linus Torvalds committed
23
24
25
26
27
28
		return 0;
	}
	/* it's free, grab it */
	fh->resources  |= bit;
	vv->resources |= bit;
	DEB_D(("res: get 0x%02x, cur:0x%02x\n",bit,vv->resources));
29
	mutex_unlock(&dev->lock);
Linus Torvalds's avatar
Linus Torvalds committed
30
31
32
33
34
35
36
37
	return 1;
}

void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits)
{
	struct saa7146_dev *dev = fh->dev;
	struct saa7146_vv *vv = dev->vv_data;

38
	BUG_ON((fh->resources & bits) != bits);
Linus Torvalds's avatar
Linus Torvalds committed
39

40
	mutex_lock(&dev->lock);
Linus Torvalds's avatar
Linus Torvalds committed
41
42
43
	fh->resources  &= ~bits;
	vv->resources &= ~bits;
	DEB_D(("res: put 0x%02x, cur:0x%02x\n",bits,vv->resources));
44
	mutex_unlock(&dev->lock);
Linus Torvalds's avatar
Linus Torvalds committed
45
46
47
48
49
50
}


/********************************************************************************/
/* common dma functions */

51
52
void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q,
						struct saa7146_buf *buf)
Linus Torvalds's avatar
Linus Torvalds committed
53
{
54
	struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
Linus Torvalds's avatar
Linus Torvalds committed
55
56
	DEB_EE(("dev:%p, buf:%p\n",dev,buf));

57
	BUG_ON(in_interrupt());
Linus Torvalds's avatar
Linus Torvalds committed
58
59

	videobuf_waiton(&buf->vb,0,0);
60
61
	videobuf_dma_unmap(q, dma);
	videobuf_dma_free(dma);
62
	buf->vb.state = VIDEOBUF_NEEDS_INIT;
Linus Torvalds's avatar
Linus Torvalds committed
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
}


/********************************************************************************/
/* common buffer functions */

int saa7146_buffer_queue(struct saa7146_dev *dev,
			 struct saa7146_dmaqueue *q,
			 struct saa7146_buf *buf)
{
	assert_spin_locked(&dev->slock);
	DEB_EE(("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf));

	BUG_ON(!q);

	if (NULL == q->curr) {
		q->curr = buf;
		DEB_D(("immediately activating buffer %p\n", buf));
		buf->activate(dev,buf,NULL);
	} else {
		list_add_tail(&buf->vb.queue,&q->queue);
84
		buf->vb.state = VIDEOBUF_QUEUED;
Linus Torvalds's avatar
Linus Torvalds committed
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
		DEB_D(("adding buffer %p to queue. (active buffer present)\n", buf));
	}
	return 0;
}

void saa7146_buffer_finish(struct saa7146_dev *dev,
			   struct saa7146_dmaqueue *q,
			   int state)
{
	assert_spin_locked(&dev->slock);
	DEB_EE(("dev:%p, dmaq:%p, state:%d\n", dev, q, state));
	DEB_EE(("q->curr:%p\n",q->curr));

	BUG_ON(!q->curr);

	/* finish current buffer */
	if (NULL == q->curr) {
		DEB_D(("aiii. no current buffer\n"));
103
		return;
Linus Torvalds's avatar
Linus Torvalds committed
104
	}
105

Linus Torvalds's avatar
Linus Torvalds committed
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
	q->curr->vb.state = state;
	do_gettimeofday(&q->curr->vb.ts);
	wake_up(&q->curr->vb.done);

	q->curr = NULL;
}

void saa7146_buffer_next(struct saa7146_dev *dev,
			 struct saa7146_dmaqueue *q, int vbi)
{
	struct saa7146_buf *buf,*next = NULL;

	BUG_ON(!q);

	DEB_INT(("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi));

	assert_spin_locked(&dev->slock);
	if (!list_empty(&q->queue)) {
		/* activate next one from queue */
		buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue);
		list_del(&buf->vb.queue);
		if (!list_empty(&q->queue))
			next = list_entry(q->queue.next,struct saa7146_buf, vb.queue);
		q->curr = buf;
		DEB_INT(("next buffer: buf:%p, prev:%p, next:%p\n", buf, q->queue.prev,q->queue.next));
		buf->activate(dev,buf,next);
	} else {
		DEB_INT(("no next buffer. stopping.\n"));
		if( 0 != vbi ) {
			/* turn off video-dma3 */
			saa7146_write(dev,MC1, MASK_20);
		} else {
			/* nothing to do -- just prevent next video-dma1 transfer
			   by lowering the protection address */

			// fixme: fix this for vflip != 0

			saa7146_write(dev, PROT_ADDR1, 0);
144
			saa7146_write(dev, MC2, (MASK_02|MASK_18));
Linus Torvalds's avatar
Linus Torvalds committed
145
146
147
148
149

			/* write the address of the rps-program */
			saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
			/* turn on rps */
			saa7146_write(dev, MC1, (MASK_12 | MASK_28));
150

Linus Torvalds's avatar
Linus Torvalds committed
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*
			printk("vdma%d.base_even:     0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
			printk("vdma%d.base_odd:      0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
			printk("vdma%d.prot_addr:     0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
			printk("vdma%d.base_page:     0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
			printk("vdma%d.pitch:         0x%08x\n", 1,saa7146_read(dev,PITCH1));
			printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
*/
		}
		del_timer(&q->timeout);
	}
}

void saa7146_buffer_timeout(unsigned long data)
{
	struct saa7146_dmaqueue *q = (struct saa7146_dmaqueue*)data;
	struct saa7146_dev *dev = q->dev;
	unsigned long flags;

	DEB_EE(("dev:%p, dmaq:%p\n", dev, q));

	spin_lock_irqsave(&dev->slock,flags);
	if (q->curr) {
		DEB_D(("timeout on %p\n", q->curr));
175
		saa7146_buffer_finish(dev,q,VIDEOBUF_ERROR);
Linus Torvalds's avatar
Linus Torvalds committed
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
	}

	/* we don't restart the transfer here like other drivers do. when
	   a streaming capture is disabled, the timeout function will be
	   called for the current buffer. if we activate the next buffer now,
	   we mess up our capture logic. if a timeout occurs on another buffer,
	   then something is seriously broken before, so no need to buffer the
	   next capture IMHO... */
/*
	saa7146_buffer_next(dev,q);
*/
	spin_unlock_irqrestore(&dev->slock,flags);
}

/********************************************************************************/
/* file operations */

193
static int fops_open(struct file *file)
Linus Torvalds's avatar
Linus Torvalds committed
194
{
195
196
	struct video_device *vdev = video_devdata(file);
	struct saa7146_dev *dev = video_drvdata(file);
Linus Torvalds's avatar
Linus Torvalds committed
197
198
199
	struct saa7146_fh *fh = NULL;
	int result = 0;

200
	enum v4l2_buf_type type;
Linus Torvalds's avatar
Linus Torvalds committed
201

202
	DEB_EE(("file:%p, dev:%s\n", file, video_device_node_name(vdev)));
Linus Torvalds's avatar
Linus Torvalds committed
203

204
	if (mutex_lock_interruptible(&saa7146_devices_lock))
Linus Torvalds's avatar
Linus Torvalds committed
205
206
207
208
		return -ERESTARTSYS;

	DEB_D(("using: %p\n",dev));

209
210
211
212
	type = vdev->vfl_type == VFL_TYPE_GRABBER
	     ? V4L2_BUF_TYPE_VIDEO_CAPTURE
	     : V4L2_BUF_TYPE_VBI_CAPTURE;

Linus Torvalds's avatar
Linus Torvalds committed
213
214
215
216
217
218
219
220
	/* check if an extension is registered */
	if( NULL == dev->ext ) {
		DEB_S(("no extension registered for this device.\n"));
		result = -ENODEV;
		goto out;
	}

	/* allocate per open data */
Panagiotis Issaris's avatar
   
Panagiotis Issaris committed
221
	fh = kzalloc(sizeof(*fh),GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
222
223
224
225
226
	if (NULL == fh) {
		DEB_S(("cannot allocate memory for per open data.\n"));
		result = -ENOMEM;
		goto out;
	}
227

Linus Torvalds's avatar
Linus Torvalds committed
228
229
230
231
232
233
	file->private_data = fh;
	fh->dev = dev;
	fh->type = type;

	if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
		DEB_S(("initializing vbi...\n"));
234
235
236
		if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
			result = saa7146_vbi_uops.open(dev,file);
		if (dev->ext_vv_data->vbi_fops.open)
237
			dev->ext_vv_data->vbi_fops.open(file);
Linus Torvalds's avatar
Linus Torvalds committed
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
	} else {
		DEB_S(("initializing video...\n"));
		result = saa7146_video_uops.open(dev,file);
	}

	if (0 != result) {
		goto out;
	}

	if( 0 == try_module_get(dev->ext->module)) {
		result = -EINVAL;
		goto out;
	}

	result = 0;
out:
Al Viro's avatar
Al Viro committed
254
	if (fh && result != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
255
256
257
		kfree(fh);
		file->private_data = NULL;
	}
258
	mutex_unlock(&saa7146_devices_lock);
259
	return result;
Linus Torvalds's avatar
Linus Torvalds committed
260
261
}

262
static int fops_release(struct file *file)
Linus Torvalds's avatar
Linus Torvalds committed
263
264
265
266
{
	struct saa7146_fh  *fh  = file->private_data;
	struct saa7146_dev *dev = fh->dev;

267
	DEB_EE(("file:%p\n", file));
Linus Torvalds's avatar
Linus Torvalds committed
268

269
	if (mutex_lock_interruptible(&saa7146_devices_lock))
Linus Torvalds's avatar
Linus Torvalds committed
270
271
272
		return -ERESTARTSYS;

	if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
273
274
275
		if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
			saa7146_vbi_uops.release(dev,file);
		if (dev->ext_vv_data->vbi_fops.release)
276
			dev->ext_vv_data->vbi_fops.release(file);
Linus Torvalds's avatar
Linus Torvalds committed
277
278
279
280
281
282
283
284
	} else {
		saa7146_video_uops.release(dev,file);
	}

	module_put(dev->ext->module);
	file->private_data = NULL;
	kfree(fh);

285
	mutex_unlock(&saa7146_devices_lock);
Linus Torvalds's avatar
Linus Torvalds committed
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309

	return 0;
}

static int fops_mmap(struct file *file, struct vm_area_struct * vma)
{
	struct saa7146_fh *fh = file->private_data;
	struct videobuf_queue *q;

	switch (fh->type) {
	case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
		DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",file, vma));
		q = &fh->video_q;
		break;
		}
	case V4L2_BUF_TYPE_VBI_CAPTURE: {
		DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",file, vma));
		q = &fh->vbi_q;
		break;
		}
	default:
		BUG();
		return 0;
	}
310

Linus Torvalds's avatar
Linus Torvalds committed
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
	return videobuf_mmap_mapper(q,vma);
}

static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
{
	struct saa7146_fh *fh = file->private_data;
	struct videobuf_buffer *buf = NULL;
	struct videobuf_queue *q;

	DEB_EE(("file:%p, poll:%p\n",file, wait));

	if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
		if( 0 == fh->vbi_q.streaming )
			return videobuf_poll_stream(file, &fh->vbi_q, wait);
		q = &fh->vbi_q;
	} else {
		DEB_D(("using video queue.\n"));
		q = &fh->video_q;
	}

	if (!list_empty(&q->stream))
		buf = list_entry(q->stream.next, struct videobuf_buffer, stream);

	if (!buf) {
		DEB_D(("buf == NULL!\n"));
		return POLLERR;
	}

	poll_wait(file, &buf->done, wait);
340
	if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) {
Linus Torvalds's avatar
Linus Torvalds committed
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
		DEB_D(("poll succeeded!\n"));
		return POLLIN|POLLRDNORM;
	}

	DEB_D(("nothing to poll for, buf->state:%d\n",buf->state));
	return 0;
}

static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
	struct saa7146_fh *fh = file->private_data;

	switch (fh->type) {
	case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
//		DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", file, data, (unsigned long)count));
		return saa7146_video_uops.read(file,data,count,ppos);
		}
	case V4L2_BUF_TYPE_VBI_CAPTURE: {
//		DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n", file, data, (unsigned long)count));
360
361
362
363
		if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
			return saa7146_vbi_uops.read(file,data,count,ppos);
		else
			return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
364
365
366
367
368
369
370
371
		}
		break;
	default:
		BUG();
		return 0;
	}
}

372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
{
	struct saa7146_fh *fh = file->private_data;

	switch (fh->type) {
	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
		return -EINVAL;
	case V4L2_BUF_TYPE_VBI_CAPTURE:
		if (fh->dev->ext_vv_data->vbi_fops.write)
			return fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos);
		else
			return -EINVAL;
	default:
		BUG();
		return -EINVAL;
	}
}

390
static const struct v4l2_file_operations video_fops =
Linus Torvalds's avatar
Linus Torvalds committed
391
392
393
394
395
{
	.owner		= THIS_MODULE,
	.open		= fops_open,
	.release	= fops_release,
	.read		= fops_read,
396
	.write		= fops_write,
Linus Torvalds's avatar
Linus Torvalds committed
397
398
	.poll		= fops_poll,
	.mmap		= fops_mmap,
399
	.ioctl		= video_ioctl2,
Linus Torvalds's avatar
Linus Torvalds committed
400
401
};

402
static void vv_callback(struct saa7146_dev *dev, unsigned long status)
Linus Torvalds's avatar
Linus Torvalds committed
403
404
{
	u32 isr = status;
405

Linus Torvalds's avatar
Linus Torvalds committed
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
	DEB_INT(("dev:%p, isr:0x%08x\n",dev,(u32)status));

	if (0 != (isr & (MASK_27))) {
		DEB_INT(("irq: RPS0 (0x%08x).\n",isr));
		saa7146_video_uops.irq_done(dev,isr);
	}

	if (0 != (isr & (MASK_28))) {
		u32 mc2 = saa7146_read(dev, MC2);
		if( 0 != (mc2 & MASK_15)) {
			DEB_INT(("irq: RPS1 vbi workaround (0x%08x).\n",isr));
			wake_up(&dev->vv_data->vbi_wq);
			saa7146_write(dev,MC2, MASK_31);
			return;
		}
		DEB_INT(("irq: RPS1 (0x%08x).\n",isr));
		saa7146_vbi_uops.irq_done(dev,isr);
	}
}

426
427
428
429
430
431
int saa7146_vv_devinit(struct saa7146_dev *dev)
{
	return v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev);
}
EXPORT_SYMBOL_GPL(saa7146_vv_devinit);

Linus Torvalds's avatar
Linus Torvalds committed
432
433
int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
{
434
435
436
	struct saa7146_vv *vv;

	vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);
437
	if (vv == NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
438
		ERR(("out of memory. aborting.\n"));
439
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
440
	}
441
442
	ext_vv->ops = saa7146_video_ioctl_ops;
	ext_vv->core_ops = &saa7146_video_ioctl_ops;
Linus Torvalds's avatar
Linus Torvalds committed
443
444
445
446
447
448
449
450
451
452
453
454
455

	DEB_EE(("dev:%p\n",dev));

	/* set default values for video parts of the saa7146 */
	saa7146_write(dev, BCS_CTRL, 0x80400040);

	/* enable video-port pins */
	saa7146_write(dev, MC1, (MASK_10 | MASK_26));

	/* save per-device extension data (one extension can
	   handle different devices that might need different
	   configuration data) */
	dev->ext_vv_data = ext_vv;
456
457

	vv->d_clipping.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_CLIPPING_MEM, &vv->d_clipping.dma_handle);
Linus Torvalds's avatar
Linus Torvalds committed
458
459
460
461
462
463
464
465
	if( NULL == vv->d_clipping.cpu_addr ) {
		ERR(("out of memory. aborting.\n"));
		kfree(vv);
		return -1;
	}
	memset(vv->d_clipping.cpu_addr, 0x0, SAA7146_CLIPPING_MEM);

	saa7146_video_uops.init(dev,vv);
466
467
	if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
		saa7146_vbi_uops.init(dev,vv);
468

Linus Torvalds's avatar
Linus Torvalds committed
469
470
471
472
473
	dev->vv_data = vv;
	dev->vv_callback = &vv_callback;

	return 0;
}
474
EXPORT_SYMBOL_GPL(saa7146_vv_init);
Linus Torvalds's avatar
Linus Torvalds committed
475
476
477
478
479
480

int saa7146_vv_release(struct saa7146_dev* dev)
{
	struct saa7146_vv *vv = dev->vv_data;

	DEB_EE(("dev:%p\n",dev));
481

482
	v4l2_device_unregister(&dev->v4l2_dev);
483
	pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle);
484
	kfree(vv);
Linus Torvalds's avatar
Linus Torvalds committed
485
486
	dev->vv_data = NULL;
	dev->vv_callback = NULL;
487

Linus Torvalds's avatar
Linus Torvalds committed
488
489
	return 0;
}
490
EXPORT_SYMBOL_GPL(saa7146_vv_release);
Linus Torvalds's avatar
Linus Torvalds committed
491
492
493
494
495

int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
			    char *name, int type)
{
	struct video_device *vfd;
496
	int err;
497
	int i;
Linus Torvalds's avatar
Linus Torvalds committed
498
499
500
501

	DEB_EE(("dev:%p, name:'%s', type:%d\n",dev,name,type));

	// released by vfd->release
502
	vfd = video_device_alloc();
Linus Torvalds's avatar
Linus Torvalds committed
503
504
505
	if (vfd == NULL)
		return -ENOMEM;

506
	vfd->fops = &video_fops;
507
	vfd->ioctl_ops = &dev->ext_vv_data->ops;
Linus Torvalds's avatar
Linus Torvalds committed
508
	vfd->release = video_device_release;
509
510
511
	vfd->tvnorms = 0;
	for (i = 0; i < dev->ext_vv_data->num_stds; i++)
		vfd->tvnorms |= dev->ext_vv_data->stds[i].id;
512
	strlcpy(vfd->name, name, sizeof(vfd->name));
513
	video_set_drvdata(vfd, dev);
Linus Torvalds's avatar
Linus Torvalds committed
514

515
516
	err = video_register_device(vfd, type, -1);
	if (err < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
517
		ERR(("cannot register v4l2 device. skipping.\n"));
518
		video_device_release(vfd);
519
		return err;
Linus Torvalds's avatar
Linus Torvalds committed
520
521
	}

522
523
	INFO(("%s: registered device %s [v4l2]\n",
		dev->name, video_device_node_name(vfd)));
Linus Torvalds's avatar
Linus Torvalds committed
524
525
526
527

	*vid = vfd;
	return 0;
}
528
EXPORT_SYMBOL_GPL(saa7146_register_device);
Linus Torvalds's avatar
Linus Torvalds committed
529
530
531
532
533
534
535
536
537
538

int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev)
{
	DEB_EE(("dev:%p\n",dev));

	video_unregister_device(*vid);
	*vid = NULL;

	return 0;
}
539
EXPORT_SYMBOL_GPL(saa7146_unregister_device);
Linus Torvalds's avatar
Linus Torvalds committed
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556

static int __init saa7146_vv_init_module(void)
{
	return 0;
}


static void __exit saa7146_vv_cleanup_module(void)
{
}

module_init(saa7146_vv_init_module);
module_exit(saa7146_vv_cleanup_module);

MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware");
MODULE_LICENSE("GPL");