139d08ab9SHans Verkuil // SPDX-License-Identifier: GPL-2.0-only
239d08ab9SHans Verkuil #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
339d08ab9SHans Verkuil 
439d08ab9SHans Verkuil #include <media/drv-intf/saa7146_vv.h>
539d08ab9SHans Verkuil #include <linux/module.h>
639d08ab9SHans Verkuil 
739d08ab9SHans Verkuil /****************************************************************************/
839d08ab9SHans Verkuil /* resource management functions, shamelessly stolen from saa7134 driver */
939d08ab9SHans Verkuil 
saa7146_res_get(struct saa7146_dev * dev,unsigned int bit)10acdb1573SHans Verkuil int saa7146_res_get(struct saa7146_dev *dev, unsigned int bit)
1139d08ab9SHans Verkuil {
1239d08ab9SHans Verkuil 	struct saa7146_vv *vv = dev->vv_data;
1339d08ab9SHans Verkuil 
14acdb1573SHans Verkuil 	if (vv->resources & bit) {
1539d08ab9SHans Verkuil 		DEB_D("already allocated! want: 0x%02x, cur:0x%02x\n",
1639d08ab9SHans Verkuil 		      bit, vv->resources);
1739d08ab9SHans Verkuil 		/* have it already allocated */
1839d08ab9SHans Verkuil 		return 1;
1939d08ab9SHans Verkuil 	}
2039d08ab9SHans Verkuil 
2139d08ab9SHans Verkuil 	/* is it free? */
2239d08ab9SHans Verkuil 	if (vv->resources & bit) {
2339d08ab9SHans Verkuil 		DEB_D("locked! vv->resources:0x%02x, we want:0x%02x\n",
2439d08ab9SHans Verkuil 		      vv->resources, bit);
2539d08ab9SHans Verkuil 		/* no, someone else uses it */
2639d08ab9SHans Verkuil 		return 0;
2739d08ab9SHans Verkuil 	}
2839d08ab9SHans Verkuil 	/* it's free, grab it */
2939d08ab9SHans Verkuil 	vv->resources |= bit;
3039d08ab9SHans Verkuil 	DEB_D("res: get 0x%02x, cur:0x%02x\n", bit, vv->resources);
3139d08ab9SHans Verkuil 	return 1;
3239d08ab9SHans Verkuil }
3339d08ab9SHans Verkuil 
saa7146_res_free(struct saa7146_dev * dev,unsigned int bits)34acdb1573SHans Verkuil void saa7146_res_free(struct saa7146_dev *dev, unsigned int bits)
3539d08ab9SHans Verkuil {
3639d08ab9SHans Verkuil 	struct saa7146_vv *vv = dev->vv_data;
3739d08ab9SHans Verkuil 
38acdb1573SHans Verkuil 	WARN_ON((vv->resources & bits) != bits);
3939d08ab9SHans Verkuil 
4039d08ab9SHans Verkuil 	vv->resources &= ~bits;
4139d08ab9SHans Verkuil 	DEB_D("res: put 0x%02x, cur:0x%02x\n", bits, vv->resources);
4239d08ab9SHans Verkuil }
4339d08ab9SHans Verkuil 
4439d08ab9SHans Verkuil 
4539d08ab9SHans Verkuil /********************************************************************************/
4639d08ab9SHans Verkuil /* common buffer functions */
4739d08ab9SHans Verkuil 
saa7146_buffer_queue(struct saa7146_dev * dev,struct saa7146_dmaqueue * q,struct saa7146_buf * buf)4839d08ab9SHans Verkuil int saa7146_buffer_queue(struct saa7146_dev *dev,
4939d08ab9SHans Verkuil 			 struct saa7146_dmaqueue *q,
5039d08ab9SHans Verkuil 			 struct saa7146_buf *buf)
5139d08ab9SHans Verkuil {
5239d08ab9SHans Verkuil 	assert_spin_locked(&dev->slock);
5339d08ab9SHans Verkuil 	DEB_EE("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf);
5439d08ab9SHans Verkuil 
5540e986c9SHans Verkuil 	if (WARN_ON(!q))
5640e986c9SHans Verkuil 		return -EIO;
5739d08ab9SHans Verkuil 
5839d08ab9SHans Verkuil 	if (NULL == q->curr) {
5939d08ab9SHans Verkuil 		q->curr = buf;
6039d08ab9SHans Verkuil 		DEB_D("immediately activating buffer %p\n", buf);
6139d08ab9SHans Verkuil 		buf->activate(dev,buf,NULL);
6239d08ab9SHans Verkuil 	} else {
630b6e30bdSHans Verkuil 		list_add_tail(&buf->list, &q->queue);
6439d08ab9SHans Verkuil 		DEB_D("adding buffer %p to queue. (active buffer present)\n",
6539d08ab9SHans Verkuil 		      buf);
6639d08ab9SHans Verkuil 	}
6739d08ab9SHans Verkuil 	return 0;
6839d08ab9SHans Verkuil }
6939d08ab9SHans Verkuil 
saa7146_buffer_finish(struct saa7146_dev * dev,struct saa7146_dmaqueue * q,int state)7039d08ab9SHans Verkuil void saa7146_buffer_finish(struct saa7146_dev *dev,
7139d08ab9SHans Verkuil 			   struct saa7146_dmaqueue *q,
7239d08ab9SHans Verkuil 			   int state)
7339d08ab9SHans Verkuil {
740b6e30bdSHans Verkuil 	struct saa7146_vv *vv = dev->vv_data;
750b6e30bdSHans Verkuil 	struct saa7146_buf *buf = q->curr;
760b6e30bdSHans Verkuil 
7739d08ab9SHans Verkuil 	assert_spin_locked(&dev->slock);
7839d08ab9SHans Verkuil 	DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state);
7939d08ab9SHans Verkuil 	DEB_EE("q->curr:%p\n", q->curr);
8039d08ab9SHans Verkuil 
8139d08ab9SHans Verkuil 	/* finish current buffer */
820b6e30bdSHans Verkuil 	if (!buf) {
8339d08ab9SHans Verkuil 		DEB_D("aiii. no current buffer\n");
8439d08ab9SHans Verkuil 		return;
8539d08ab9SHans Verkuil 	}
8639d08ab9SHans Verkuil 
8739d08ab9SHans Verkuil 	q->curr = NULL;
880b6e30bdSHans Verkuil 	buf->vb.vb2_buf.timestamp = ktime_get_ns();
890b6e30bdSHans Verkuil 	if (vv->video_fmt.field == V4L2_FIELD_ALTERNATE)
900b6e30bdSHans Verkuil 		buf->vb.field = vv->last_field;
910b6e30bdSHans Verkuil 	else if (vv->video_fmt.field == V4L2_FIELD_ANY)
920b6e30bdSHans Verkuil 		buf->vb.field = (vv->video_fmt.height > vv->standard->v_max_out / 2)
930b6e30bdSHans Verkuil 			? V4L2_FIELD_INTERLACED
940b6e30bdSHans Verkuil 			: V4L2_FIELD_BOTTOM;
950b6e30bdSHans Verkuil 	else
960b6e30bdSHans Verkuil 		buf->vb.field = vv->video_fmt.field;
970b6e30bdSHans Verkuil 	buf->vb.sequence = vv->seqnr++;
980b6e30bdSHans Verkuil 	vb2_buffer_done(&buf->vb.vb2_buf, state);
9939d08ab9SHans Verkuil }
10039d08ab9SHans Verkuil 
saa7146_buffer_next(struct saa7146_dev * dev,struct saa7146_dmaqueue * q,int vbi)10139d08ab9SHans Verkuil void saa7146_buffer_next(struct saa7146_dev *dev,
10239d08ab9SHans Verkuil 			 struct saa7146_dmaqueue *q, int vbi)
10339d08ab9SHans Verkuil {
10439d08ab9SHans Verkuil 	struct saa7146_buf *buf,*next = NULL;
10539d08ab9SHans Verkuil 
10640e986c9SHans Verkuil 	if (WARN_ON(!q))
10740e986c9SHans Verkuil 		return;
10839d08ab9SHans Verkuil 
10939d08ab9SHans Verkuil 	DEB_INT("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi);
11039d08ab9SHans Verkuil 
11139d08ab9SHans Verkuil 	assert_spin_locked(&dev->slock);
11239d08ab9SHans Verkuil 	if (!list_empty(&q->queue)) {
11339d08ab9SHans Verkuil 		/* activate next one from queue */
1140b6e30bdSHans Verkuil 		buf = list_entry(q->queue.next, struct saa7146_buf, list);
1150b6e30bdSHans Verkuil 		list_del(&buf->list);
11639d08ab9SHans Verkuil 		if (!list_empty(&q->queue))
1170b6e30bdSHans Verkuil 			next = list_entry(q->queue.next, struct saa7146_buf, list);
11839d08ab9SHans Verkuil 		q->curr = buf;
11939d08ab9SHans Verkuil 		DEB_INT("next buffer: buf:%p, prev:%p, next:%p\n",
12039d08ab9SHans Verkuil 			buf, q->queue.prev, q->queue.next);
12139d08ab9SHans Verkuil 		buf->activate(dev,buf,next);
12239d08ab9SHans Verkuil 	} else {
12339d08ab9SHans Verkuil 		DEB_INT("no next buffer. stopping.\n");
12439d08ab9SHans Verkuil 		if( 0 != vbi ) {
12539d08ab9SHans Verkuil 			/* turn off video-dma3 */
12639d08ab9SHans Verkuil 			saa7146_write(dev,MC1, MASK_20);
12739d08ab9SHans Verkuil 		} else {
12839d08ab9SHans Verkuil 			/* nothing to do -- just prevent next video-dma1 transfer
12939d08ab9SHans Verkuil 			   by lowering the protection address */
13039d08ab9SHans Verkuil 
13139d08ab9SHans Verkuil 			// fixme: fix this for vflip != 0
13239d08ab9SHans Verkuil 
13339d08ab9SHans Verkuil 			saa7146_write(dev, PROT_ADDR1, 0);
13439d08ab9SHans Verkuil 			saa7146_write(dev, MC2, (MASK_02|MASK_18));
13539d08ab9SHans Verkuil 
13639d08ab9SHans Verkuil 			/* write the address of the rps-program */
13739d08ab9SHans Verkuil 			saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
13839d08ab9SHans Verkuil 			/* turn on rps */
13939d08ab9SHans Verkuil 			saa7146_write(dev, MC1, (MASK_12 | MASK_28));
14039d08ab9SHans Verkuil 
14139d08ab9SHans Verkuil /*
14239d08ab9SHans Verkuil 			printk("vdma%d.base_even:     0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
14339d08ab9SHans Verkuil 			printk("vdma%d.base_odd:      0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
14439d08ab9SHans Verkuil 			printk("vdma%d.prot_addr:     0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
14539d08ab9SHans Verkuil 			printk("vdma%d.base_page:     0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
14639d08ab9SHans Verkuil 			printk("vdma%d.pitch:         0x%08x\n", 1,saa7146_read(dev,PITCH1));
14739d08ab9SHans Verkuil 			printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
14839d08ab9SHans Verkuil */
14939d08ab9SHans Verkuil 		}
15039d08ab9SHans Verkuil 		del_timer(&q->timeout);
15139d08ab9SHans Verkuil 	}
15239d08ab9SHans Verkuil }
15339d08ab9SHans Verkuil 
saa7146_buffer_timeout(struct timer_list * t)15439d08ab9SHans Verkuil void saa7146_buffer_timeout(struct timer_list *t)
15539d08ab9SHans Verkuil {
15639d08ab9SHans Verkuil 	struct saa7146_dmaqueue *q = from_timer(q, t, timeout);
15739d08ab9SHans Verkuil 	struct saa7146_dev *dev = q->dev;
15839d08ab9SHans Verkuil 	unsigned long flags;
15939d08ab9SHans Verkuil 
16039d08ab9SHans Verkuil 	DEB_EE("dev:%p, dmaq:%p\n", dev, q);
16139d08ab9SHans Verkuil 
16239d08ab9SHans Verkuil 	spin_lock_irqsave(&dev->slock,flags);
16339d08ab9SHans Verkuil 	if (q->curr) {
16439d08ab9SHans Verkuil 		DEB_D("timeout on %p\n", q->curr);
1650b6e30bdSHans Verkuil 		saa7146_buffer_finish(dev, q, VB2_BUF_STATE_ERROR);
16639d08ab9SHans Verkuil 	}
16739d08ab9SHans Verkuil 
16839d08ab9SHans Verkuil 	/* we don't restart the transfer here like other drivers do. when
16939d08ab9SHans Verkuil 	   a streaming capture is disabled, the timeout function will be
17039d08ab9SHans Verkuil 	   called for the current buffer. if we activate the next buffer now,
17139d08ab9SHans Verkuil 	   we mess up our capture logic. if a timeout occurs on another buffer,
17239d08ab9SHans Verkuil 	   then something is seriously broken before, so no need to buffer the
17339d08ab9SHans Verkuil 	   next capture IMHO... */
1740b6e30bdSHans Verkuil 
1750b6e30bdSHans Verkuil 	saa7146_buffer_next(dev, q, 0);
1760b6e30bdSHans Verkuil 
17739d08ab9SHans Verkuil 	spin_unlock_irqrestore(&dev->slock,flags);
17839d08ab9SHans Verkuil }
17939d08ab9SHans Verkuil 
18039d08ab9SHans Verkuil /********************************************************************************/
18139d08ab9SHans Verkuil /* file operations */
18239d08ab9SHans Verkuil 
fops_write(struct file * file,const char __user * data,size_t count,loff_t * ppos)18339d08ab9SHans Verkuil static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
18439d08ab9SHans Verkuil {
18539d08ab9SHans Verkuil 	struct video_device *vdev = video_devdata(file);
186acdb1573SHans Verkuil 	struct saa7146_dev *dev = video_drvdata(file);
18739d08ab9SHans Verkuil 	int ret;
18839d08ab9SHans Verkuil 
1890b6e30bdSHans Verkuil 	if (vdev->vfl_type != VFL_TYPE_VBI || !dev->ext_vv_data->vbi_fops.write)
19039d08ab9SHans Verkuil 		return -EINVAL;
19139d08ab9SHans Verkuil 	if (mutex_lock_interruptible(vdev->lock))
19239d08ab9SHans Verkuil 		return -ERESTARTSYS;
193acdb1573SHans Verkuil 	ret = dev->ext_vv_data->vbi_fops.write(file, data, count, ppos);
19439d08ab9SHans Verkuil 	mutex_unlock(vdev->lock);
19539d08ab9SHans Verkuil 	return ret;
19639d08ab9SHans Verkuil }
19739d08ab9SHans Verkuil 
19839d08ab9SHans Verkuil static const struct v4l2_file_operations video_fops =
19939d08ab9SHans Verkuil {
20039d08ab9SHans Verkuil 	.owner		= THIS_MODULE,
2010b6e30bdSHans Verkuil 	.open		= v4l2_fh_open,
2020b6e30bdSHans Verkuil 	.release	= vb2_fop_release,
2030b6e30bdSHans Verkuil 	.read		= vb2_fop_read,
20439d08ab9SHans Verkuil 	.write		= fops_write,
2050b6e30bdSHans Verkuil 	.poll		= vb2_fop_poll,
2060b6e30bdSHans Verkuil 	.mmap		= vb2_fop_mmap,
20739d08ab9SHans Verkuil 	.unlocked_ioctl	= video_ioctl2,
20839d08ab9SHans Verkuil };
20939d08ab9SHans Verkuil 
vv_callback(struct saa7146_dev * dev,unsigned long status)21039d08ab9SHans Verkuil static void vv_callback(struct saa7146_dev *dev, unsigned long status)
21139d08ab9SHans Verkuil {
21239d08ab9SHans Verkuil 	u32 isr = status;
21339d08ab9SHans Verkuil 
21439d08ab9SHans Verkuil 	DEB_INT("dev:%p, isr:0x%08x\n", dev, (u32)status);
21539d08ab9SHans Verkuil 
21639d08ab9SHans Verkuil 	if (0 != (isr & (MASK_27))) {
21739d08ab9SHans Verkuil 		DEB_INT("irq: RPS0 (0x%08x)\n", isr);
21839d08ab9SHans Verkuil 		saa7146_video_uops.irq_done(dev,isr);
21939d08ab9SHans Verkuil 	}
22039d08ab9SHans Verkuil 
22139d08ab9SHans Verkuil 	if (0 != (isr & (MASK_28))) {
22239d08ab9SHans Verkuil 		u32 mc2 = saa7146_read(dev, MC2);
22339d08ab9SHans Verkuil 		if( 0 != (mc2 & MASK_15)) {
22439d08ab9SHans Verkuil 			DEB_INT("irq: RPS1 vbi workaround (0x%08x)\n", isr);
22539d08ab9SHans Verkuil 			wake_up(&dev->vv_data->vbi_wq);
22639d08ab9SHans Verkuil 			saa7146_write(dev,MC2, MASK_31);
22739d08ab9SHans Verkuil 			return;
22839d08ab9SHans Verkuil 		}
22939d08ab9SHans Verkuil 		DEB_INT("irq: RPS1 (0x%08x)\n", isr);
23039d08ab9SHans Verkuil 		saa7146_vbi_uops.irq_done(dev,isr);
23139d08ab9SHans Verkuil 	}
23239d08ab9SHans Verkuil }
23339d08ab9SHans Verkuil 
23439d08ab9SHans Verkuil static const struct v4l2_ctrl_ops saa7146_ctrl_ops = {
23539d08ab9SHans Verkuil 	.s_ctrl = saa7146_s_ctrl,
23639d08ab9SHans Verkuil };
23739d08ab9SHans Verkuil 
saa7146_vv_init(struct saa7146_dev * dev,struct saa7146_ext_vv * ext_vv)23839d08ab9SHans Verkuil int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
23939d08ab9SHans Verkuil {
24039d08ab9SHans Verkuil 	struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
24139d08ab9SHans Verkuil 	struct v4l2_pix_format *fmt;
24239d08ab9SHans Verkuil 	struct v4l2_vbi_format *vbi;
24339d08ab9SHans Verkuil 	struct saa7146_vv *vv;
24439d08ab9SHans Verkuil 	int err;
24539d08ab9SHans Verkuil 
24639d08ab9SHans Verkuil 	err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev);
24739d08ab9SHans Verkuil 	if (err)
24839d08ab9SHans Verkuil 		return err;
24939d08ab9SHans Verkuil 
25039d08ab9SHans Verkuil 	v4l2_ctrl_handler_init(hdl, 6);
25139d08ab9SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
25239d08ab9SHans Verkuil 		V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
25339d08ab9SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
25439d08ab9SHans Verkuil 		V4L2_CID_CONTRAST, 0, 127, 1, 64);
25539d08ab9SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
25639d08ab9SHans Verkuil 		V4L2_CID_SATURATION, 0, 127, 1, 64);
25739d08ab9SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
25839d08ab9SHans Verkuil 		V4L2_CID_VFLIP, 0, 1, 1, 0);
25939d08ab9SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
26039d08ab9SHans Verkuil 		V4L2_CID_HFLIP, 0, 1, 1, 0);
26139d08ab9SHans Verkuil 	if (hdl->error) {
26239d08ab9SHans Verkuil 		err = hdl->error;
26339d08ab9SHans Verkuil 		v4l2_ctrl_handler_free(hdl);
26439d08ab9SHans Verkuil 		v4l2_device_unregister(&dev->v4l2_dev);
26539d08ab9SHans Verkuil 		return err;
26639d08ab9SHans Verkuil 	}
26739d08ab9SHans Verkuil 	dev->v4l2_dev.ctrl_handler = hdl;
26839d08ab9SHans Verkuil 
26939d08ab9SHans Verkuil 	vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);
27039d08ab9SHans Verkuil 	if (vv == NULL) {
27139d08ab9SHans Verkuil 		ERR("out of memory. aborting.\n");
27239d08ab9SHans Verkuil 		v4l2_ctrl_handler_free(hdl);
27339d08ab9SHans Verkuil 		v4l2_device_unregister(&dev->v4l2_dev);
27439d08ab9SHans Verkuil 		return -ENOMEM;
27539d08ab9SHans Verkuil 	}
27639d08ab9SHans Verkuil 	ext_vv->vid_ops = saa7146_video_ioctl_ops;
27739d08ab9SHans Verkuil 	ext_vv->vbi_ops = saa7146_vbi_ioctl_ops;
27839d08ab9SHans Verkuil 	ext_vv->core_ops = &saa7146_video_ioctl_ops;
27939d08ab9SHans Verkuil 
28039d08ab9SHans Verkuil 	DEB_EE("dev:%p\n", dev);
28139d08ab9SHans Verkuil 
28239d08ab9SHans Verkuil 	/* set default values for video parts of the saa7146 */
28339d08ab9SHans Verkuil 	saa7146_write(dev, BCS_CTRL, 0x80400040);
28439d08ab9SHans Verkuil 
28539d08ab9SHans Verkuil 	/* enable video-port pins */
28639d08ab9SHans Verkuil 	saa7146_write(dev, MC1, (MASK_10 | MASK_26));
28739d08ab9SHans Verkuil 
28839d08ab9SHans Verkuil 	/* save per-device extension data (one extension can
28939d08ab9SHans Verkuil 	   handle different devices that might need different
29039d08ab9SHans Verkuil 	   configuration data) */
29139d08ab9SHans Verkuil 	dev->ext_vv_data = ext_vv;
29239d08ab9SHans Verkuil 
29339d08ab9SHans Verkuil 	saa7146_video_uops.init(dev,vv);
29439d08ab9SHans Verkuil 	if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
29539d08ab9SHans Verkuil 		saa7146_vbi_uops.init(dev,vv);
29639d08ab9SHans Verkuil 
29739d08ab9SHans Verkuil 	fmt = &vv->video_fmt;
29839d08ab9SHans Verkuil 	fmt->width = 384;
29939d08ab9SHans Verkuil 	fmt->height = 288;
30039d08ab9SHans Verkuil 	fmt->pixelformat = V4L2_PIX_FMT_BGR24;
301*e4561809SHans Verkuil 	fmt->field = V4L2_FIELD_INTERLACED;
30239d08ab9SHans Verkuil 	fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
30339d08ab9SHans Verkuil 	fmt->bytesperline = 3 * fmt->width;
30439d08ab9SHans Verkuil 	fmt->sizeimage = fmt->bytesperline * fmt->height;
30539d08ab9SHans Verkuil 
30639d08ab9SHans Verkuil 	vbi = &vv->vbi_fmt;
30739d08ab9SHans Verkuil 	vbi->sampling_rate	= 27000000;
30839d08ab9SHans Verkuil 	vbi->offset		= 248; /* todo */
30939d08ab9SHans Verkuil 	vbi->samples_per_line	= 720 * 2;
31039d08ab9SHans Verkuil 	vbi->sample_format	= V4L2_PIX_FMT_GREY;
31139d08ab9SHans Verkuil 
31239d08ab9SHans Verkuil 	/* fixme: this only works for PAL */
31339d08ab9SHans Verkuil 	vbi->start[0] = 5;
31439d08ab9SHans Verkuil 	vbi->count[0] = 16;
31539d08ab9SHans Verkuil 	vbi->start[1] = 312;
31639d08ab9SHans Verkuil 	vbi->count[1] = 16;
31739d08ab9SHans Verkuil 
31839d08ab9SHans Verkuil 	timer_setup(&vv->vbi_read_timeout, NULL, 0);
31939d08ab9SHans Verkuil 
32039d08ab9SHans Verkuil 	dev->vv_data = vv;
32139d08ab9SHans Verkuil 	dev->vv_callback = &vv_callback;
32239d08ab9SHans Verkuil 
32339d08ab9SHans Verkuil 	return 0;
32439d08ab9SHans Verkuil }
32539d08ab9SHans Verkuil EXPORT_SYMBOL_GPL(saa7146_vv_init);
32639d08ab9SHans Verkuil 
saa7146_vv_release(struct saa7146_dev * dev)32739d08ab9SHans Verkuil int saa7146_vv_release(struct saa7146_dev* dev)
32839d08ab9SHans Verkuil {
32939d08ab9SHans Verkuil 	struct saa7146_vv *vv = dev->vv_data;
33039d08ab9SHans Verkuil 
33139d08ab9SHans Verkuil 	DEB_EE("dev:%p\n", dev);
33239d08ab9SHans Verkuil 
33339d08ab9SHans Verkuil 	v4l2_device_unregister(&dev->v4l2_dev);
33439d08ab9SHans Verkuil 	v4l2_ctrl_handler_free(&dev->ctrl_handler);
33539d08ab9SHans Verkuil 	kfree(vv);
33639d08ab9SHans Verkuil 	dev->vv_data = NULL;
33739d08ab9SHans Verkuil 	dev->vv_callback = NULL;
33839d08ab9SHans Verkuil 
33939d08ab9SHans Verkuil 	return 0;
34039d08ab9SHans Verkuil }
34139d08ab9SHans Verkuil EXPORT_SYMBOL_GPL(saa7146_vv_release);
34239d08ab9SHans Verkuil 
saa7146_register_device(struct video_device * vfd,struct saa7146_dev * dev,char * name,int type)34339d08ab9SHans Verkuil int saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev,
34439d08ab9SHans Verkuil 			    char *name, int type)
34539d08ab9SHans Verkuil {
3460b6e30bdSHans Verkuil 	struct vb2_queue *q;
34739d08ab9SHans Verkuil 	int err;
34839d08ab9SHans Verkuil 	int i;
34939d08ab9SHans Verkuil 
35039d08ab9SHans Verkuil 	DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type);
35139d08ab9SHans Verkuil 
35239d08ab9SHans Verkuil 	vfd->fops = &video_fops;
3530b6e30bdSHans Verkuil 	if (type == VFL_TYPE_VIDEO) {
35439d08ab9SHans Verkuil 		vfd->ioctl_ops = &dev->ext_vv_data->vid_ops;
3550b6e30bdSHans Verkuil 		q = &dev->vv_data->video_dmaq.q;
3560b6e30bdSHans Verkuil 	} else {
35739d08ab9SHans Verkuil 		vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops;
3580b6e30bdSHans Verkuil 		q = &dev->vv_data->vbi_dmaq.q;
3590b6e30bdSHans Verkuil 	}
36039d08ab9SHans Verkuil 	vfd->release = video_device_release_empty;
36139d08ab9SHans Verkuil 	vfd->lock = &dev->v4l2_lock;
36239d08ab9SHans Verkuil 	vfd->v4l2_dev = &dev->v4l2_dev;
36339d08ab9SHans Verkuil 	vfd->tvnorms = 0;
36439d08ab9SHans Verkuil 	for (i = 0; i < dev->ext_vv_data->num_stds; i++)
36539d08ab9SHans Verkuil 		vfd->tvnorms |= dev->ext_vv_data->stds[i].id;
36639d08ab9SHans Verkuil 	strscpy(vfd->name, name, sizeof(vfd->name));
3677777694fSHans Verkuil 	vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE |
36839d08ab9SHans Verkuil 			   V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
36939d08ab9SHans Verkuil 	vfd->device_caps |= dev->ext_vv_data->capabilities;
370727c070cSHans Verkuil 	if (type == VFL_TYPE_VIDEO) {
37139d08ab9SHans Verkuil 		vfd->device_caps &=
37239d08ab9SHans Verkuil 			~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT);
373727c070cSHans Verkuil 	} else if (vfd->device_caps & V4L2_CAP_SLICED_VBI_OUTPUT) {
374727c070cSHans Verkuil 		vfd->vfl_dir = VFL_DIR_TX;
375727c070cSHans Verkuil 		vfd->device_caps &= ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
376727c070cSHans Verkuil 				      V4L2_CAP_AUDIO | V4L2_CAP_TUNER);
377727c070cSHans Verkuil 	} else {
378727c070cSHans Verkuil 		vfd->device_caps &= ~V4L2_CAP_VIDEO_CAPTURE;
379727c070cSHans Verkuil 	}
3800b6e30bdSHans Verkuil 
3810b6e30bdSHans Verkuil 	q->type = type == VFL_TYPE_VIDEO ? V4L2_BUF_TYPE_VIDEO_CAPTURE : V4L2_BUF_TYPE_VBI_CAPTURE;
3820b6e30bdSHans Verkuil 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
3830b6e30bdSHans Verkuil 	q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF;
3840b6e30bdSHans Verkuil 	q->ops = type == VFL_TYPE_VIDEO ? &video_qops : &vbi_qops;
3850b6e30bdSHans Verkuil 	q->mem_ops = &vb2_dma_sg_memops;
3860b6e30bdSHans Verkuil 	q->drv_priv = dev;
3870b6e30bdSHans Verkuil 	q->gfp_flags = __GFP_DMA32;
3880b6e30bdSHans Verkuil 	q->buf_struct_size = sizeof(struct saa7146_buf);
3890b6e30bdSHans Verkuil 	q->lock = &dev->v4l2_lock;
3900b6e30bdSHans Verkuil 	q->min_buffers_needed = 2;
3910b6e30bdSHans Verkuil 	q->dev = &dev->pci->dev;
3920b6e30bdSHans Verkuil 	err = vb2_queue_init(q);
3930b6e30bdSHans Verkuil 	if (err)
3940b6e30bdSHans Verkuil 		return err;
3950b6e30bdSHans Verkuil 	vfd->queue = q;
3960b6e30bdSHans Verkuil 
39739d08ab9SHans Verkuil 	video_set_drvdata(vfd, dev);
39839d08ab9SHans Verkuil 
39939d08ab9SHans Verkuil 	err = video_register_device(vfd, type, -1);
40039d08ab9SHans Verkuil 	if (err < 0) {
40139d08ab9SHans Verkuil 		ERR("cannot register v4l2 device. skipping.\n");
40239d08ab9SHans Verkuil 		return err;
40339d08ab9SHans Verkuil 	}
40439d08ab9SHans Verkuil 
40539d08ab9SHans Verkuil 	pr_info("%s: registered device %s [v4l2]\n",
40639d08ab9SHans Verkuil 		dev->name, video_device_node_name(vfd));
40739d08ab9SHans Verkuil 	return 0;
40839d08ab9SHans Verkuil }
40939d08ab9SHans Verkuil EXPORT_SYMBOL_GPL(saa7146_register_device);
41039d08ab9SHans Verkuil 
saa7146_unregister_device(struct video_device * vfd,struct saa7146_dev * dev)41139d08ab9SHans Verkuil int saa7146_unregister_device(struct video_device *vfd, struct saa7146_dev *dev)
41239d08ab9SHans Verkuil {
41339d08ab9SHans Verkuil 	DEB_EE("dev:%p\n", dev);
41439d08ab9SHans Verkuil 
41539d08ab9SHans Verkuil 	video_unregister_device(vfd);
41639d08ab9SHans Verkuil 	return 0;
41739d08ab9SHans Verkuil }
41839d08ab9SHans Verkuil EXPORT_SYMBOL_GPL(saa7146_unregister_device);
41939d08ab9SHans Verkuil 
saa7146_vv_init_module(void)42039d08ab9SHans Verkuil static int __init saa7146_vv_init_module(void)
42139d08ab9SHans Verkuil {
42239d08ab9SHans Verkuil 	return 0;
42339d08ab9SHans Verkuil }
42439d08ab9SHans Verkuil 
42539d08ab9SHans Verkuil 
saa7146_vv_cleanup_module(void)42639d08ab9SHans Verkuil static void __exit saa7146_vv_cleanup_module(void)
42739d08ab9SHans Verkuil {
42839d08ab9SHans Verkuil }
42939d08ab9SHans Verkuil 
43039d08ab9SHans Verkuil module_init(saa7146_vv_init_module);
43139d08ab9SHans Verkuil module_exit(saa7146_vv_cleanup_module);
43239d08ab9SHans Verkuil 
43339d08ab9SHans Verkuil MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
43439d08ab9SHans Verkuil MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware");
43539d08ab9SHans Verkuil MODULE_LICENSE("GPL");
436