18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <media/drv-intf/saa7146_vv.h>
58c2ecf20Sopenharmony_ci#include <linux/module.h>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci/****************************************************************************/
88c2ecf20Sopenharmony_ci/* resource management functions, shamelessly stolen from saa7134 driver */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ciint saa7146_res_get(struct saa7146_fh *fh, unsigned int bit)
118c2ecf20Sopenharmony_ci{
128c2ecf20Sopenharmony_ci	struct saa7146_dev *dev = fh->dev;
138c2ecf20Sopenharmony_ci	struct saa7146_vv *vv = dev->vv_data;
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci	if (fh->resources & bit) {
168c2ecf20Sopenharmony_ci		DEB_D("already allocated! want: 0x%02x, cur:0x%02x\n",
178c2ecf20Sopenharmony_ci		      bit, vv->resources);
188c2ecf20Sopenharmony_ci		/* have it already allocated */
198c2ecf20Sopenharmony_ci		return 1;
208c2ecf20Sopenharmony_ci	}
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	/* is it free? */
238c2ecf20Sopenharmony_ci	if (vv->resources & bit) {
248c2ecf20Sopenharmony_ci		DEB_D("locked! vv->resources:0x%02x, we want:0x%02x\n",
258c2ecf20Sopenharmony_ci		      vv->resources, bit);
268c2ecf20Sopenharmony_ci		/* no, someone else uses it */
278c2ecf20Sopenharmony_ci		return 0;
288c2ecf20Sopenharmony_ci	}
298c2ecf20Sopenharmony_ci	/* it's free, grab it */
308c2ecf20Sopenharmony_ci	fh->resources |= bit;
318c2ecf20Sopenharmony_ci	vv->resources |= bit;
328c2ecf20Sopenharmony_ci	DEB_D("res: get 0x%02x, cur:0x%02x\n", bit, vv->resources);
338c2ecf20Sopenharmony_ci	return 1;
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_civoid saa7146_res_free(struct saa7146_fh *fh, unsigned int bits)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct saa7146_dev *dev = fh->dev;
398c2ecf20Sopenharmony_ci	struct saa7146_vv *vv = dev->vv_data;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	BUG_ON((fh->resources & bits) != bits);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	fh->resources &= ~bits;
448c2ecf20Sopenharmony_ci	vv->resources &= ~bits;
458c2ecf20Sopenharmony_ci	DEB_D("res: put 0x%02x, cur:0x%02x\n", bits, vv->resources);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/********************************************************************************/
508c2ecf20Sopenharmony_ci/* common dma functions */
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_civoid saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q,
538c2ecf20Sopenharmony_ci						struct saa7146_buf *buf)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
568c2ecf20Sopenharmony_ci	DEB_EE("dev:%p, buf:%p\n", dev, buf);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	BUG_ON(in_interrupt());
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	videobuf_waiton(q, &buf->vb, 0, 0);
618c2ecf20Sopenharmony_ci	videobuf_dma_unmap(q->dev, dma);
628c2ecf20Sopenharmony_ci	videobuf_dma_free(dma);
638c2ecf20Sopenharmony_ci	buf->vb.state = VIDEOBUF_NEEDS_INIT;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/********************************************************************************/
688c2ecf20Sopenharmony_ci/* common buffer functions */
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ciint saa7146_buffer_queue(struct saa7146_dev *dev,
718c2ecf20Sopenharmony_ci			 struct saa7146_dmaqueue *q,
728c2ecf20Sopenharmony_ci			 struct saa7146_buf *buf)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	assert_spin_locked(&dev->slock);
758c2ecf20Sopenharmony_ci	DEB_EE("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	BUG_ON(!q);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (NULL == q->curr) {
808c2ecf20Sopenharmony_ci		q->curr = buf;
818c2ecf20Sopenharmony_ci		DEB_D("immediately activating buffer %p\n", buf);
828c2ecf20Sopenharmony_ci		buf->activate(dev,buf,NULL);
838c2ecf20Sopenharmony_ci	} else {
848c2ecf20Sopenharmony_ci		list_add_tail(&buf->vb.queue,&q->queue);
858c2ecf20Sopenharmony_ci		buf->vb.state = VIDEOBUF_QUEUED;
868c2ecf20Sopenharmony_ci		DEB_D("adding buffer %p to queue. (active buffer present)\n",
878c2ecf20Sopenharmony_ci		      buf);
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci	return 0;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_civoid saa7146_buffer_finish(struct saa7146_dev *dev,
938c2ecf20Sopenharmony_ci			   struct saa7146_dmaqueue *q,
948c2ecf20Sopenharmony_ci			   int state)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	assert_spin_locked(&dev->slock);
978c2ecf20Sopenharmony_ci	DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state);
988c2ecf20Sopenharmony_ci	DEB_EE("q->curr:%p\n", q->curr);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* finish current buffer */
1018c2ecf20Sopenharmony_ci	if (NULL == q->curr) {
1028c2ecf20Sopenharmony_ci		DEB_D("aiii. no current buffer\n");
1038c2ecf20Sopenharmony_ci		return;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	q->curr->vb.state = state;
1078c2ecf20Sopenharmony_ci	q->curr->vb.ts = ktime_get_ns();
1088c2ecf20Sopenharmony_ci	wake_up(&q->curr->vb.done);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	q->curr = NULL;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_civoid saa7146_buffer_next(struct saa7146_dev *dev,
1148c2ecf20Sopenharmony_ci			 struct saa7146_dmaqueue *q, int vbi)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct saa7146_buf *buf,*next = NULL;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	BUG_ON(!q);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	DEB_INT("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	assert_spin_locked(&dev->slock);
1238c2ecf20Sopenharmony_ci	if (!list_empty(&q->queue)) {
1248c2ecf20Sopenharmony_ci		/* activate next one from queue */
1258c2ecf20Sopenharmony_ci		buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue);
1268c2ecf20Sopenharmony_ci		list_del(&buf->vb.queue);
1278c2ecf20Sopenharmony_ci		if (!list_empty(&q->queue))
1288c2ecf20Sopenharmony_ci			next = list_entry(q->queue.next,struct saa7146_buf, vb.queue);
1298c2ecf20Sopenharmony_ci		q->curr = buf;
1308c2ecf20Sopenharmony_ci		DEB_INT("next buffer: buf:%p, prev:%p, next:%p\n",
1318c2ecf20Sopenharmony_ci			buf, q->queue.prev, q->queue.next);
1328c2ecf20Sopenharmony_ci		buf->activate(dev,buf,next);
1338c2ecf20Sopenharmony_ci	} else {
1348c2ecf20Sopenharmony_ci		DEB_INT("no next buffer. stopping.\n");
1358c2ecf20Sopenharmony_ci		if( 0 != vbi ) {
1368c2ecf20Sopenharmony_ci			/* turn off video-dma3 */
1378c2ecf20Sopenharmony_ci			saa7146_write(dev,MC1, MASK_20);
1388c2ecf20Sopenharmony_ci		} else {
1398c2ecf20Sopenharmony_ci			/* nothing to do -- just prevent next video-dma1 transfer
1408c2ecf20Sopenharmony_ci			   by lowering the protection address */
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci			// fixme: fix this for vflip != 0
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci			saa7146_write(dev, PROT_ADDR1, 0);
1458c2ecf20Sopenharmony_ci			saa7146_write(dev, MC2, (MASK_02|MASK_18));
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci			/* write the address of the rps-program */
1488c2ecf20Sopenharmony_ci			saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
1498c2ecf20Sopenharmony_ci			/* turn on rps */
1508c2ecf20Sopenharmony_ci			saa7146_write(dev, MC1, (MASK_12 | MASK_28));
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/*
1538c2ecf20Sopenharmony_ci			printk("vdma%d.base_even:     0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
1548c2ecf20Sopenharmony_ci			printk("vdma%d.base_odd:      0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
1558c2ecf20Sopenharmony_ci			printk("vdma%d.prot_addr:     0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
1568c2ecf20Sopenharmony_ci			printk("vdma%d.base_page:     0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
1578c2ecf20Sopenharmony_ci			printk("vdma%d.pitch:         0x%08x\n", 1,saa7146_read(dev,PITCH1));
1588c2ecf20Sopenharmony_ci			printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
1598c2ecf20Sopenharmony_ci*/
1608c2ecf20Sopenharmony_ci		}
1618c2ecf20Sopenharmony_ci		del_timer(&q->timeout);
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_civoid saa7146_buffer_timeout(struct timer_list *t)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct saa7146_dmaqueue *q = from_timer(q, t, timeout);
1688c2ecf20Sopenharmony_ci	struct saa7146_dev *dev = q->dev;
1698c2ecf20Sopenharmony_ci	unsigned long flags;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	DEB_EE("dev:%p, dmaq:%p\n", dev, q);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->slock,flags);
1748c2ecf20Sopenharmony_ci	if (q->curr) {
1758c2ecf20Sopenharmony_ci		DEB_D("timeout on %p\n", q->curr);
1768c2ecf20Sopenharmony_ci		saa7146_buffer_finish(dev,q,VIDEOBUF_ERROR);
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* we don't restart the transfer here like other drivers do. when
1808c2ecf20Sopenharmony_ci	   a streaming capture is disabled, the timeout function will be
1818c2ecf20Sopenharmony_ci	   called for the current buffer. if we activate the next buffer now,
1828c2ecf20Sopenharmony_ci	   we mess up our capture logic. if a timeout occurs on another buffer,
1838c2ecf20Sopenharmony_ci	   then something is seriously broken before, so no need to buffer the
1848c2ecf20Sopenharmony_ci	   next capture IMHO... */
1858c2ecf20Sopenharmony_ci/*
1868c2ecf20Sopenharmony_ci	saa7146_buffer_next(dev,q);
1878c2ecf20Sopenharmony_ci*/
1888c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock,flags);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci/********************************************************************************/
1928c2ecf20Sopenharmony_ci/* file operations */
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic int fops_open(struct file *file)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
1978c2ecf20Sopenharmony_ci	struct saa7146_dev *dev = video_drvdata(file);
1988c2ecf20Sopenharmony_ci	struct saa7146_fh *fh = NULL;
1998c2ecf20Sopenharmony_ci	int result = 0;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev));
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(vdev->lock))
2048c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	DEB_D("using: %p\n", dev);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* check if an extension is registered */
2098c2ecf20Sopenharmony_ci	if( NULL == dev->ext ) {
2108c2ecf20Sopenharmony_ci		DEB_S("no extension registered for this device\n");
2118c2ecf20Sopenharmony_ci		result = -ENODEV;
2128c2ecf20Sopenharmony_ci		goto out;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	/* allocate per open data */
2168c2ecf20Sopenharmony_ci	fh = kzalloc(sizeof(*fh),GFP_KERNEL);
2178c2ecf20Sopenharmony_ci	if (NULL == fh) {
2188c2ecf20Sopenharmony_ci		DEB_S("cannot allocate memory for per open data\n");
2198c2ecf20Sopenharmony_ci		result = -ENOMEM;
2208c2ecf20Sopenharmony_ci		goto out;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	v4l2_fh_init(&fh->fh, vdev);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	file->private_data = &fh->fh;
2268c2ecf20Sopenharmony_ci	fh->dev = dev;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (vdev->vfl_type == VFL_TYPE_VBI) {
2298c2ecf20Sopenharmony_ci		DEB_S("initializing vbi...\n");
2308c2ecf20Sopenharmony_ci		if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
2318c2ecf20Sopenharmony_ci			result = saa7146_vbi_uops.open(dev,file);
2328c2ecf20Sopenharmony_ci		if (dev->ext_vv_data->vbi_fops.open)
2338c2ecf20Sopenharmony_ci			dev->ext_vv_data->vbi_fops.open(file);
2348c2ecf20Sopenharmony_ci	} else {
2358c2ecf20Sopenharmony_ci		DEB_S("initializing video...\n");
2368c2ecf20Sopenharmony_ci		result = saa7146_video_uops.open(dev,file);
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (0 != result) {
2408c2ecf20Sopenharmony_ci		goto out;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if( 0 == try_module_get(dev->ext->module)) {
2448c2ecf20Sopenharmony_ci		result = -EINVAL;
2458c2ecf20Sopenharmony_ci		goto out;
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	result = 0;
2498c2ecf20Sopenharmony_ci	v4l2_fh_add(&fh->fh);
2508c2ecf20Sopenharmony_ciout:
2518c2ecf20Sopenharmony_ci	if (fh && result != 0) {
2528c2ecf20Sopenharmony_ci		kfree(fh);
2538c2ecf20Sopenharmony_ci		file->private_data = NULL;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci	mutex_unlock(vdev->lock);
2568c2ecf20Sopenharmony_ci	return result;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic int fops_release(struct file *file)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
2628c2ecf20Sopenharmony_ci	struct saa7146_fh  *fh  = file->private_data;
2638c2ecf20Sopenharmony_ci	struct saa7146_dev *dev = fh->dev;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	DEB_EE("file:%p\n", file);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	mutex_lock(vdev->lock);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (vdev->vfl_type == VFL_TYPE_VBI) {
2708c2ecf20Sopenharmony_ci		if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
2718c2ecf20Sopenharmony_ci			saa7146_vbi_uops.release(dev,file);
2728c2ecf20Sopenharmony_ci		if (dev->ext_vv_data->vbi_fops.release)
2738c2ecf20Sopenharmony_ci			dev->ext_vv_data->vbi_fops.release(file);
2748c2ecf20Sopenharmony_ci	} else {
2758c2ecf20Sopenharmony_ci		saa7146_video_uops.release(dev,file);
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	v4l2_fh_del(&fh->fh);
2798c2ecf20Sopenharmony_ci	v4l2_fh_exit(&fh->fh);
2808c2ecf20Sopenharmony_ci	module_put(dev->ext->module);
2818c2ecf20Sopenharmony_ci	file->private_data = NULL;
2828c2ecf20Sopenharmony_ci	kfree(fh);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	mutex_unlock(vdev->lock);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	return 0;
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic int fops_mmap(struct file *file, struct vm_area_struct * vma)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
2928c2ecf20Sopenharmony_ci	struct saa7146_fh *fh = file->private_data;
2938c2ecf20Sopenharmony_ci	struct videobuf_queue *q;
2948c2ecf20Sopenharmony_ci	int res;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	switch (vdev->vfl_type) {
2978c2ecf20Sopenharmony_ci	case VFL_TYPE_VIDEO: {
2988c2ecf20Sopenharmony_ci		DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",
2998c2ecf20Sopenharmony_ci		       file, vma);
3008c2ecf20Sopenharmony_ci		q = &fh->video_q;
3018c2ecf20Sopenharmony_ci		break;
3028c2ecf20Sopenharmony_ci		}
3038c2ecf20Sopenharmony_ci	case VFL_TYPE_VBI: {
3048c2ecf20Sopenharmony_ci		DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",
3058c2ecf20Sopenharmony_ci		       file, vma);
3068c2ecf20Sopenharmony_ci		if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
3078c2ecf20Sopenharmony_ci			return -ENODEV;
3088c2ecf20Sopenharmony_ci		q = &fh->vbi_q;
3098c2ecf20Sopenharmony_ci		break;
3108c2ecf20Sopenharmony_ci		}
3118c2ecf20Sopenharmony_ci	default:
3128c2ecf20Sopenharmony_ci		BUG();
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(vdev->lock))
3168c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
3178c2ecf20Sopenharmony_ci	res = videobuf_mmap_mapper(q, vma);
3188c2ecf20Sopenharmony_ci	mutex_unlock(vdev->lock);
3198c2ecf20Sopenharmony_ci	return res;
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic __poll_t __fops_poll(struct file *file, struct poll_table_struct *wait)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
3258c2ecf20Sopenharmony_ci	struct saa7146_fh *fh = file->private_data;
3268c2ecf20Sopenharmony_ci	struct videobuf_buffer *buf = NULL;
3278c2ecf20Sopenharmony_ci	struct videobuf_queue *q;
3288c2ecf20Sopenharmony_ci	__poll_t res = v4l2_ctrl_poll(file, wait);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	DEB_EE("file:%p, poll:%p\n", file, wait);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (vdev->vfl_type == VFL_TYPE_VBI) {
3338c2ecf20Sopenharmony_ci		if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
3348c2ecf20Sopenharmony_ci			return res | EPOLLOUT | EPOLLWRNORM;
3358c2ecf20Sopenharmony_ci		if( 0 == fh->vbi_q.streaming )
3368c2ecf20Sopenharmony_ci			return res | videobuf_poll_stream(file, &fh->vbi_q, wait);
3378c2ecf20Sopenharmony_ci		q = &fh->vbi_q;
3388c2ecf20Sopenharmony_ci	} else {
3398c2ecf20Sopenharmony_ci		DEB_D("using video queue\n");
3408c2ecf20Sopenharmony_ci		q = &fh->video_q;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if (!list_empty(&q->stream))
3448c2ecf20Sopenharmony_ci		buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if (!buf) {
3478c2ecf20Sopenharmony_ci		DEB_D("buf == NULL!\n");
3488c2ecf20Sopenharmony_ci		return res | EPOLLERR;
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	poll_wait(file, &buf->done, wait);
3528c2ecf20Sopenharmony_ci	if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) {
3538c2ecf20Sopenharmony_ci		DEB_D("poll succeeded!\n");
3548c2ecf20Sopenharmony_ci		return res | EPOLLIN | EPOLLRDNORM;
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	DEB_D("nothing to poll for, buf->state:%d\n", buf->state);
3588c2ecf20Sopenharmony_ci	return res;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic __poll_t fops_poll(struct file *file, struct poll_table_struct *wait)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
3648c2ecf20Sopenharmony_ci	__poll_t res;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	mutex_lock(vdev->lock);
3678c2ecf20Sopenharmony_ci	res = __fops_poll(file, wait);
3688c2ecf20Sopenharmony_ci	mutex_unlock(vdev->lock);
3698c2ecf20Sopenharmony_ci	return res;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
3758c2ecf20Sopenharmony_ci	struct saa7146_fh *fh = file->private_data;
3768c2ecf20Sopenharmony_ci	int ret;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	switch (vdev->vfl_type) {
3798c2ecf20Sopenharmony_ci	case VFL_TYPE_VIDEO:
3808c2ecf20Sopenharmony_ci/*
3818c2ecf20Sopenharmony_ci		DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun",
3828c2ecf20Sopenharmony_ci		       file, data, (unsigned long)count);
3838c2ecf20Sopenharmony_ci*/
3848c2ecf20Sopenharmony_ci		return saa7146_video_uops.read(file,data,count,ppos);
3858c2ecf20Sopenharmony_ci	case VFL_TYPE_VBI:
3868c2ecf20Sopenharmony_ci/*
3878c2ecf20Sopenharmony_ci		DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n",
3888c2ecf20Sopenharmony_ci		       file, data, (unsigned long)count);
3898c2ecf20Sopenharmony_ci*/
3908c2ecf20Sopenharmony_ci		if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) {
3918c2ecf20Sopenharmony_ci			if (mutex_lock_interruptible(vdev->lock))
3928c2ecf20Sopenharmony_ci				return -ERESTARTSYS;
3938c2ecf20Sopenharmony_ci			ret = saa7146_vbi_uops.read(file, data, count, ppos);
3948c2ecf20Sopenharmony_ci			mutex_unlock(vdev->lock);
3958c2ecf20Sopenharmony_ci			return ret;
3968c2ecf20Sopenharmony_ci		}
3978c2ecf20Sopenharmony_ci		return -EINVAL;
3988c2ecf20Sopenharmony_ci	default:
3998c2ecf20Sopenharmony_ci		BUG();
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
4068c2ecf20Sopenharmony_ci	struct saa7146_fh *fh = file->private_data;
4078c2ecf20Sopenharmony_ci	int ret;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	switch (vdev->vfl_type) {
4108c2ecf20Sopenharmony_ci	case VFL_TYPE_VIDEO:
4118c2ecf20Sopenharmony_ci		return -EINVAL;
4128c2ecf20Sopenharmony_ci	case VFL_TYPE_VBI:
4138c2ecf20Sopenharmony_ci		if (fh->dev->ext_vv_data->vbi_fops.write) {
4148c2ecf20Sopenharmony_ci			if (mutex_lock_interruptible(vdev->lock))
4158c2ecf20Sopenharmony_ci				return -ERESTARTSYS;
4168c2ecf20Sopenharmony_ci			ret = fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos);
4178c2ecf20Sopenharmony_ci			mutex_unlock(vdev->lock);
4188c2ecf20Sopenharmony_ci			return ret;
4198c2ecf20Sopenharmony_ci		}
4208c2ecf20Sopenharmony_ci		return -EINVAL;
4218c2ecf20Sopenharmony_ci	default:
4228c2ecf20Sopenharmony_ci		BUG();
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations video_fops =
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
4298c2ecf20Sopenharmony_ci	.open		= fops_open,
4308c2ecf20Sopenharmony_ci	.release	= fops_release,
4318c2ecf20Sopenharmony_ci	.read		= fops_read,
4328c2ecf20Sopenharmony_ci	.write		= fops_write,
4338c2ecf20Sopenharmony_ci	.poll		= fops_poll,
4348c2ecf20Sopenharmony_ci	.mmap		= fops_mmap,
4358c2ecf20Sopenharmony_ci	.unlocked_ioctl	= video_ioctl2,
4368c2ecf20Sopenharmony_ci};
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic void vv_callback(struct saa7146_dev *dev, unsigned long status)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	u32 isr = status;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	DEB_INT("dev:%p, isr:0x%08x\n", dev, (u32)status);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	if (0 != (isr & (MASK_27))) {
4458c2ecf20Sopenharmony_ci		DEB_INT("irq: RPS0 (0x%08x)\n", isr);
4468c2ecf20Sopenharmony_ci		saa7146_video_uops.irq_done(dev,isr);
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	if (0 != (isr & (MASK_28))) {
4508c2ecf20Sopenharmony_ci		u32 mc2 = saa7146_read(dev, MC2);
4518c2ecf20Sopenharmony_ci		if( 0 != (mc2 & MASK_15)) {
4528c2ecf20Sopenharmony_ci			DEB_INT("irq: RPS1 vbi workaround (0x%08x)\n", isr);
4538c2ecf20Sopenharmony_ci			wake_up(&dev->vv_data->vbi_wq);
4548c2ecf20Sopenharmony_ci			saa7146_write(dev,MC2, MASK_31);
4558c2ecf20Sopenharmony_ci			return;
4568c2ecf20Sopenharmony_ci		}
4578c2ecf20Sopenharmony_ci		DEB_INT("irq: RPS1 (0x%08x)\n", isr);
4588c2ecf20Sopenharmony_ci		saa7146_vbi_uops.irq_done(dev,isr);
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops saa7146_ctrl_ops = {
4638c2ecf20Sopenharmony_ci	.s_ctrl = saa7146_s_ctrl,
4648c2ecf20Sopenharmony_ci};
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ciint saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
4698c2ecf20Sopenharmony_ci	struct v4l2_pix_format *fmt;
4708c2ecf20Sopenharmony_ci	struct v4l2_vbi_format *vbi;
4718c2ecf20Sopenharmony_ci	struct saa7146_vv *vv;
4728c2ecf20Sopenharmony_ci	int err;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev);
4758c2ecf20Sopenharmony_ci	if (err)
4768c2ecf20Sopenharmony_ci		return err;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 6);
4798c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
4808c2ecf20Sopenharmony_ci		V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
4818c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
4828c2ecf20Sopenharmony_ci		V4L2_CID_CONTRAST, 0, 127, 1, 64);
4838c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
4848c2ecf20Sopenharmony_ci		V4L2_CID_SATURATION, 0, 127, 1, 64);
4858c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
4868c2ecf20Sopenharmony_ci		V4L2_CID_VFLIP, 0, 1, 1, 0);
4878c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
4888c2ecf20Sopenharmony_ci		V4L2_CID_HFLIP, 0, 1, 1, 0);
4898c2ecf20Sopenharmony_ci	if (hdl->error) {
4908c2ecf20Sopenharmony_ci		err = hdl->error;
4918c2ecf20Sopenharmony_ci		v4l2_ctrl_handler_free(hdl);
4928c2ecf20Sopenharmony_ci		return err;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci	dev->v4l2_dev.ctrl_handler = hdl;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);
4978c2ecf20Sopenharmony_ci	if (vv == NULL) {
4988c2ecf20Sopenharmony_ci		ERR("out of memory. aborting.\n");
4998c2ecf20Sopenharmony_ci		v4l2_ctrl_handler_free(hdl);
5008c2ecf20Sopenharmony_ci		return -ENOMEM;
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci	ext_vv->vid_ops = saa7146_video_ioctl_ops;
5038c2ecf20Sopenharmony_ci	ext_vv->vbi_ops = saa7146_vbi_ioctl_ops;
5048c2ecf20Sopenharmony_ci	ext_vv->core_ops = &saa7146_video_ioctl_ops;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	DEB_EE("dev:%p\n", dev);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	/* set default values for video parts of the saa7146 */
5098c2ecf20Sopenharmony_ci	saa7146_write(dev, BCS_CTRL, 0x80400040);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	/* enable video-port pins */
5128c2ecf20Sopenharmony_ci	saa7146_write(dev, MC1, (MASK_10 | MASK_26));
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	/* save per-device extension data (one extension can
5158c2ecf20Sopenharmony_ci	   handle different devices that might need different
5168c2ecf20Sopenharmony_ci	   configuration data) */
5178c2ecf20Sopenharmony_ci	dev->ext_vv_data = ext_vv;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	vv->d_clipping.cpu_addr =
5208c2ecf20Sopenharmony_ci		pci_zalloc_consistent(dev->pci, SAA7146_CLIPPING_MEM,
5218c2ecf20Sopenharmony_ci				      &vv->d_clipping.dma_handle);
5228c2ecf20Sopenharmony_ci	if( NULL == vv->d_clipping.cpu_addr ) {
5238c2ecf20Sopenharmony_ci		ERR("out of memory. aborting.\n");
5248c2ecf20Sopenharmony_ci		kfree(vv);
5258c2ecf20Sopenharmony_ci		v4l2_ctrl_handler_free(hdl);
5268c2ecf20Sopenharmony_ci		return -ENOMEM;
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	saa7146_video_uops.init(dev,vv);
5308c2ecf20Sopenharmony_ci	if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
5318c2ecf20Sopenharmony_ci		saa7146_vbi_uops.init(dev,vv);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	vv->ov_fb.fmt.width = vv->standard->h_max_out;
5348c2ecf20Sopenharmony_ci	vv->ov_fb.fmt.height = vv->standard->v_max_out;
5358c2ecf20Sopenharmony_ci	vv->ov_fb.fmt.pixelformat = V4L2_PIX_FMT_RGB565;
5368c2ecf20Sopenharmony_ci	vv->ov_fb.fmt.bytesperline = 2 * vv->ov_fb.fmt.width;
5378c2ecf20Sopenharmony_ci	vv->ov_fb.fmt.sizeimage = vv->ov_fb.fmt.bytesperline * vv->ov_fb.fmt.height;
5388c2ecf20Sopenharmony_ci	vv->ov_fb.fmt.colorspace = V4L2_COLORSPACE_SRGB;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	fmt = &vv->video_fmt;
5418c2ecf20Sopenharmony_ci	fmt->width = 384;
5428c2ecf20Sopenharmony_ci	fmt->height = 288;
5438c2ecf20Sopenharmony_ci	fmt->pixelformat = V4L2_PIX_FMT_BGR24;
5448c2ecf20Sopenharmony_ci	fmt->field = V4L2_FIELD_ANY;
5458c2ecf20Sopenharmony_ci	fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
5468c2ecf20Sopenharmony_ci	fmt->bytesperline = 3 * fmt->width;
5478c2ecf20Sopenharmony_ci	fmt->sizeimage = fmt->bytesperline * fmt->height;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	vbi = &vv->vbi_fmt;
5508c2ecf20Sopenharmony_ci	vbi->sampling_rate	= 27000000;
5518c2ecf20Sopenharmony_ci	vbi->offset		= 248; /* todo */
5528c2ecf20Sopenharmony_ci	vbi->samples_per_line	= 720 * 2;
5538c2ecf20Sopenharmony_ci	vbi->sample_format	= V4L2_PIX_FMT_GREY;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	/* fixme: this only works for PAL */
5568c2ecf20Sopenharmony_ci	vbi->start[0] = 5;
5578c2ecf20Sopenharmony_ci	vbi->count[0] = 16;
5588c2ecf20Sopenharmony_ci	vbi->start[1] = 312;
5598c2ecf20Sopenharmony_ci	vbi->count[1] = 16;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	timer_setup(&vv->vbi_read_timeout, NULL, 0);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING;
5648c2ecf20Sopenharmony_ci	vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY;
5658c2ecf20Sopenharmony_ci	dev->vv_data = vv;
5668c2ecf20Sopenharmony_ci	dev->vv_callback = &vv_callback;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	return 0;
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(saa7146_vv_init);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ciint saa7146_vv_release(struct saa7146_dev* dev)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	struct saa7146_vv *vv = dev->vv_data;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	DEB_EE("dev:%p\n", dev);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	v4l2_device_unregister(&dev->v4l2_dev);
5798c2ecf20Sopenharmony_ci	pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle);
5808c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&dev->ctrl_handler);
5818c2ecf20Sopenharmony_ci	kfree(vv);
5828c2ecf20Sopenharmony_ci	dev->vv_data = NULL;
5838c2ecf20Sopenharmony_ci	dev->vv_callback = NULL;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	return 0;
5868c2ecf20Sopenharmony_ci}
5878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(saa7146_vv_release);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ciint saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev,
5908c2ecf20Sopenharmony_ci			    char *name, int type)
5918c2ecf20Sopenharmony_ci{
5928c2ecf20Sopenharmony_ci	int err;
5938c2ecf20Sopenharmony_ci	int i;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	vfd->fops = &video_fops;
5988c2ecf20Sopenharmony_ci	if (type == VFL_TYPE_VIDEO)
5998c2ecf20Sopenharmony_ci		vfd->ioctl_ops = &dev->ext_vv_data->vid_ops;
6008c2ecf20Sopenharmony_ci	else
6018c2ecf20Sopenharmony_ci		vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops;
6028c2ecf20Sopenharmony_ci	vfd->release = video_device_release_empty;
6038c2ecf20Sopenharmony_ci	vfd->lock = &dev->v4l2_lock;
6048c2ecf20Sopenharmony_ci	vfd->v4l2_dev = &dev->v4l2_dev;
6058c2ecf20Sopenharmony_ci	vfd->tvnorms = 0;
6068c2ecf20Sopenharmony_ci	for (i = 0; i < dev->ext_vv_data->num_stds; i++)
6078c2ecf20Sopenharmony_ci		vfd->tvnorms |= dev->ext_vv_data->stds[i].id;
6088c2ecf20Sopenharmony_ci	strscpy(vfd->name, name, sizeof(vfd->name));
6098c2ecf20Sopenharmony_ci	vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY |
6108c2ecf20Sopenharmony_ci			   V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
6118c2ecf20Sopenharmony_ci	vfd->device_caps |= dev->ext_vv_data->capabilities;
6128c2ecf20Sopenharmony_ci	if (type == VFL_TYPE_VIDEO)
6138c2ecf20Sopenharmony_ci		vfd->device_caps &=
6148c2ecf20Sopenharmony_ci			~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT);
6158c2ecf20Sopenharmony_ci	else
6168c2ecf20Sopenharmony_ci		vfd->device_caps &=
6178c2ecf20Sopenharmony_ci			~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_AUDIO);
6188c2ecf20Sopenharmony_ci	video_set_drvdata(vfd, dev);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	err = video_register_device(vfd, type, -1);
6218c2ecf20Sopenharmony_ci	if (err < 0) {
6228c2ecf20Sopenharmony_ci		ERR("cannot register v4l2 device. skipping.\n");
6238c2ecf20Sopenharmony_ci		return err;
6248c2ecf20Sopenharmony_ci	}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	pr_info("%s: registered device %s [v4l2]\n",
6278c2ecf20Sopenharmony_ci		dev->name, video_device_node_name(vfd));
6288c2ecf20Sopenharmony_ci	return 0;
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(saa7146_register_device);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ciint saa7146_unregister_device(struct video_device *vfd, struct saa7146_dev *dev)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	DEB_EE("dev:%p\n", dev);
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	video_unregister_device(vfd);
6378c2ecf20Sopenharmony_ci	return 0;
6388c2ecf20Sopenharmony_ci}
6398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(saa7146_unregister_device);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_cistatic int __init saa7146_vv_init_module(void)
6428c2ecf20Sopenharmony_ci{
6438c2ecf20Sopenharmony_ci	return 0;
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_cistatic void __exit saa7146_vv_cleanup_module(void)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_cimodule_init(saa7146_vv_init_module);
6528c2ecf20Sopenharmony_cimodule_exit(saa7146_vv_cleanup_module);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
6558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("video4linux driver for saa7146-based hardware");
6568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
657