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