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