1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
211bd27b2SSteven Toth /*
311bd27b2SSteven Toth * Driver for the NXP SAA7164 PCIe bridge
411bd27b2SSteven Toth *
563a412ecSSteven Toth * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.com>
611bd27b2SSteven Toth */
711bd27b2SSteven Toth
811bd27b2SSteven Toth #include "saa7164.h"
911bd27b2SSteven Toth
1011bd27b2SSteven Toth /* Take the encoder configuration from the port struct and
1111bd27b2SSteven Toth * flush it to the hardware.
1211bd27b2SSteven Toth */
saa7164_vbi_configure(struct saa7164_port * port)1311bd27b2SSteven Toth static void saa7164_vbi_configure(struct saa7164_port *port)
1411bd27b2SSteven Toth {
1511bd27b2SSteven Toth struct saa7164_dev *dev = port->dev;
1611bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s()\n", __func__);
1711bd27b2SSteven Toth
18225b783bSHans Verkuil port->vbi_params.width = port->enc_port->width;
19225b783bSHans Verkuil port->vbi_params.height = port->enc_port->height;
2011bd27b2SSteven Toth port->vbi_params.is_50hz =
21225b783bSHans Verkuil (port->enc_port->encodernorm.id & V4L2_STD_625_50) != 0;
2211bd27b2SSteven Toth
2311bd27b2SSteven Toth /* Set up the DIF (enable it) for analog mode by default */
2411bd27b2SSteven Toth saa7164_api_initialize_dif(port);
2511bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s() ends\n", __func__);
2611bd27b2SSteven Toth }
2711bd27b2SSteven Toth
saa7164_vbi_buffers_dealloc(struct saa7164_port * port)2811bd27b2SSteven Toth static int saa7164_vbi_buffers_dealloc(struct saa7164_port *port)
2911bd27b2SSteven Toth {
3011bd27b2SSteven Toth struct list_head *c, *n, *p, *q, *l, *v;
3111bd27b2SSteven Toth struct saa7164_dev *dev = port->dev;
3211bd27b2SSteven Toth struct saa7164_buffer *buf;
3311bd27b2SSteven Toth struct saa7164_user_buffer *ubuf;
3411bd27b2SSteven Toth
3511bd27b2SSteven Toth /* Remove any allocated buffers */
3611bd27b2SSteven Toth mutex_lock(&port->dmaqueue_lock);
3711bd27b2SSteven Toth
3811bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s(port=%d) dmaqueue\n", __func__, port->nr);
3911bd27b2SSteven Toth list_for_each_safe(c, n, &port->dmaqueue.list) {
4011bd27b2SSteven Toth buf = list_entry(c, struct saa7164_buffer, list);
4111bd27b2SSteven Toth list_del(c);
4211bd27b2SSteven Toth saa7164_buffer_dealloc(buf);
4311bd27b2SSteven Toth }
4411bd27b2SSteven Toth
4511bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s(port=%d) used\n", __func__, port->nr);
4611bd27b2SSteven Toth list_for_each_safe(p, q, &port->list_buf_used.list) {
4711bd27b2SSteven Toth ubuf = list_entry(p, struct saa7164_user_buffer, list);
4811bd27b2SSteven Toth list_del(p);
4911bd27b2SSteven Toth saa7164_buffer_dealloc_user(ubuf);
5011bd27b2SSteven Toth }
5111bd27b2SSteven Toth
5211bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s(port=%d) free\n", __func__, port->nr);
5311bd27b2SSteven Toth list_for_each_safe(l, v, &port->list_buf_free.list) {
5411bd27b2SSteven Toth ubuf = list_entry(l, struct saa7164_user_buffer, list);
5511bd27b2SSteven Toth list_del(l);
5611bd27b2SSteven Toth saa7164_buffer_dealloc_user(ubuf);
5711bd27b2SSteven Toth }
5811bd27b2SSteven Toth
5911bd27b2SSteven Toth mutex_unlock(&port->dmaqueue_lock);
6011bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s(port=%d) done\n", __func__, port->nr);
6111bd27b2SSteven Toth
6211bd27b2SSteven Toth return 0;
6311bd27b2SSteven Toth }
6411bd27b2SSteven Toth
6511bd27b2SSteven Toth /* Dynamic buffer switch at vbi start time */
saa7164_vbi_buffers_alloc(struct saa7164_port * port)6611bd27b2SSteven Toth static int saa7164_vbi_buffers_alloc(struct saa7164_port *port)
6711bd27b2SSteven Toth {
6811bd27b2SSteven Toth struct saa7164_dev *dev = port->dev;
6911bd27b2SSteven Toth struct saa7164_buffer *buf;
7011bd27b2SSteven Toth struct saa7164_user_buffer *ubuf;
714d270cfbSMauro Carvalho Chehab struct tmHWStreamParameters *params = &port->hw_streamingparams;
7211bd27b2SSteven Toth int result = -ENODEV, i;
7311bd27b2SSteven Toth int len = 0;
7411bd27b2SSteven Toth
7511bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s()\n", __func__);
7611bd27b2SSteven Toth
7711bd27b2SSteven Toth /* TODO: NTSC SPECIFIC */
7811bd27b2SSteven Toth /* Init and establish defaults */
7911bd27b2SSteven Toth params->samplesperline = 1440;
8011bd27b2SSteven Toth params->numberoflines = 18;
8111bd27b2SSteven Toth params->pitch = 1440;
8211bd27b2SSteven Toth params->numpagetables = 2 +
8311bd27b2SSteven Toth ((params->numberoflines * params->pitch) / PAGE_SIZE);
8411bd27b2SSteven Toth params->bitspersample = 8;
8511bd27b2SSteven Toth params->linethreshold = 0;
8661ca1500SPeter Huewe params->pagetablelistvirt = NULL;
8761ca1500SPeter Huewe params->pagetablelistphys = NULL;
8811bd27b2SSteven Toth params->numpagetableentries = port->hwcfg.buffercount;
8911bd27b2SSteven Toth
9011bd27b2SSteven Toth /* Allocate the PCI resources, buffers (hard) */
9111bd27b2SSteven Toth for (i = 0; i < port->hwcfg.buffercount; i++) {
9211bd27b2SSteven Toth buf = saa7164_buffer_alloc(port,
9311bd27b2SSteven Toth params->numberoflines *
9411bd27b2SSteven Toth params->pitch);
9511bd27b2SSteven Toth
9611bd27b2SSteven Toth if (!buf) {
9724f711c1SMauro Carvalho Chehab printk(KERN_ERR "%s() failed (errno = %d), unable to allocate buffer\n",
9811bd27b2SSteven Toth __func__, result);
9911bd27b2SSteven Toth result = -ENOMEM;
10011bd27b2SSteven Toth goto failed;
10111bd27b2SSteven Toth } else {
10211bd27b2SSteven Toth
10311bd27b2SSteven Toth mutex_lock(&port->dmaqueue_lock);
10411bd27b2SSteven Toth list_add_tail(&buf->list, &port->dmaqueue.list);
10511bd27b2SSteven Toth mutex_unlock(&port->dmaqueue_lock);
10611bd27b2SSteven Toth
10711bd27b2SSteven Toth }
10811bd27b2SSteven Toth }
10911bd27b2SSteven Toth
11070f23fd6SJustin P. Mattock /* Allocate some kernel buffers for copying
11111bd27b2SSteven Toth * to userpsace.
11211bd27b2SSteven Toth */
11311bd27b2SSteven Toth len = params->numberoflines * params->pitch;
11411bd27b2SSteven Toth
11511bd27b2SSteven Toth if (vbi_buffers < 16)
11611bd27b2SSteven Toth vbi_buffers = 16;
11711bd27b2SSteven Toth if (vbi_buffers > 512)
11811bd27b2SSteven Toth vbi_buffers = 512;
11911bd27b2SSteven Toth
12011bd27b2SSteven Toth for (i = 0; i < vbi_buffers; i++) {
12111bd27b2SSteven Toth
12211bd27b2SSteven Toth ubuf = saa7164_buffer_alloc_user(dev, len);
12311bd27b2SSteven Toth if (ubuf) {
12411bd27b2SSteven Toth mutex_lock(&port->dmaqueue_lock);
12511bd27b2SSteven Toth list_add_tail(&ubuf->list, &port->list_buf_free.list);
12611bd27b2SSteven Toth mutex_unlock(&port->dmaqueue_lock);
12711bd27b2SSteven Toth }
12811bd27b2SSteven Toth
12911bd27b2SSteven Toth }
13011bd27b2SSteven Toth
13111bd27b2SSteven Toth result = 0;
13211bd27b2SSteven Toth
13311bd27b2SSteven Toth failed:
13411bd27b2SSteven Toth return result;
13511bd27b2SSteven Toth }
13611bd27b2SSteven Toth
13711bd27b2SSteven Toth
saa7164_vbi_initialize(struct saa7164_port * port)13811bd27b2SSteven Toth static int saa7164_vbi_initialize(struct saa7164_port *port)
13911bd27b2SSteven Toth {
14011bd27b2SSteven Toth saa7164_vbi_configure(port);
14111bd27b2SSteven Toth return 0;
14211bd27b2SSteven Toth }
14311bd27b2SSteven Toth
14411bd27b2SSteven Toth /* -- V4L2 --------------------------------------------------------- */
vidioc_s_std(struct file * file,void * priv,v4l2_std_id id)145314527acSHans Verkuil static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id)
14611bd27b2SSteven Toth {
14711bd27b2SSteven Toth struct saa7164_vbi_fh *fh = file->private_data;
14811bd27b2SSteven Toth
149225b783bSHans Verkuil return saa7164_s_std(fh->port->enc_port, id);
15011bd27b2SSteven Toth }
15111bd27b2SSteven Toth
vidioc_g_std(struct file * file,void * priv,v4l2_std_id * id)1528d2d41e9SHans Verkuil static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
1538d2d41e9SHans Verkuil {
1548d2d41e9SHans Verkuil struct saa7164_encoder_fh *fh = file->private_data;
1558d2d41e9SHans Verkuil
156225b783bSHans Verkuil return saa7164_g_std(fh->port->enc_port, id);
15711bd27b2SSteven Toth }
15811bd27b2SSteven Toth
vidioc_g_input(struct file * file,void * priv,unsigned int * i)15911bd27b2SSteven Toth static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
16011bd27b2SSteven Toth {
16111bd27b2SSteven Toth struct saa7164_vbi_fh *fh = file->private_data;
16211bd27b2SSteven Toth
163225b783bSHans Verkuil return saa7164_g_input(fh->port->enc_port, i);
16411bd27b2SSteven Toth }
16511bd27b2SSteven Toth
vidioc_s_input(struct file * file,void * priv,unsigned int i)16611bd27b2SSteven Toth static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
16711bd27b2SSteven Toth {
16811bd27b2SSteven Toth struct saa7164_vbi_fh *fh = file->private_data;
16911bd27b2SSteven Toth
170225b783bSHans Verkuil return saa7164_s_input(fh->port->enc_port, i);
17111bd27b2SSteven Toth }
17211bd27b2SSteven Toth
vidioc_g_frequency(struct file * file,void * priv,struct v4l2_frequency * f)17311bd27b2SSteven Toth static int vidioc_g_frequency(struct file *file, void *priv,
17411bd27b2SSteven Toth struct v4l2_frequency *f)
17511bd27b2SSteven Toth {
17611bd27b2SSteven Toth struct saa7164_vbi_fh *fh = file->private_data;
17711bd27b2SSteven Toth
178225b783bSHans Verkuil return saa7164_g_frequency(fh->port->enc_port, f);
17911bd27b2SSteven Toth }
18011bd27b2SSteven Toth
vidioc_s_frequency(struct file * file,void * priv,const struct v4l2_frequency * f)18111bd27b2SSteven Toth static int vidioc_s_frequency(struct file *file, void *priv,
182b530a447SHans Verkuil const struct v4l2_frequency *f)
18311bd27b2SSteven Toth {
18411bd27b2SSteven Toth struct saa7164_vbi_fh *fh = file->private_data;
185225b783bSHans Verkuil int ret = saa7164_s_frequency(fh->port->enc_port, f);
18611bd27b2SSteven Toth
187225b783bSHans Verkuil if (ret == 0)
188225b783bSHans Verkuil saa7164_vbi_initialize(fh->port);
189225b783bSHans Verkuil return ret;
19011bd27b2SSteven Toth }
19111bd27b2SSteven Toth
vidioc_querycap(struct file * file,void * priv,struct v4l2_capability * cap)19211bd27b2SSteven Toth static int vidioc_querycap(struct file *file, void *priv,
19311bd27b2SSteven Toth struct v4l2_capability *cap)
19411bd27b2SSteven Toth {
19511bd27b2SSteven Toth struct saa7164_vbi_fh *fh = file->private_data;
19611bd27b2SSteven Toth struct saa7164_port *port = fh->port;
19711bd27b2SSteven Toth struct saa7164_dev *dev = port->dev;
19811bd27b2SSteven Toth
199cc1e6315SMauro Carvalho Chehab strscpy(cap->driver, dev->name, sizeof(cap->driver));
200c0decac1SMauro Carvalho Chehab strscpy(cap->card, saa7164_boards[dev->board].name,
20111bd27b2SSteven Toth sizeof(cap->card));
20221615365SHans Verkuil cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
20321615365SHans Verkuil V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE |
204534bc3e2SHans Verkuil V4L2_CAP_DEVICE_CAPS;
20511bd27b2SSteven Toth return 0;
20611bd27b2SSteven Toth }
20711bd27b2SSteven Toth
saa7164_vbi_stop_port(struct saa7164_port * port)20811bd27b2SSteven Toth static int saa7164_vbi_stop_port(struct saa7164_port *port)
20911bd27b2SSteven Toth {
21011bd27b2SSteven Toth struct saa7164_dev *dev = port->dev;
21111bd27b2SSteven Toth int ret;
21211bd27b2SSteven Toth
21311bd27b2SSteven Toth ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
21411bd27b2SSteven Toth if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
21511bd27b2SSteven Toth printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n",
21611bd27b2SSteven Toth __func__, ret);
21711bd27b2SSteven Toth ret = -EIO;
21811bd27b2SSteven Toth } else {
21911bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s() Stopped\n", __func__);
22011bd27b2SSteven Toth ret = 0;
22111bd27b2SSteven Toth }
22211bd27b2SSteven Toth
22311bd27b2SSteven Toth return ret;
22411bd27b2SSteven Toth }
22511bd27b2SSteven Toth
saa7164_vbi_acquire_port(struct saa7164_port * port)22611bd27b2SSteven Toth static int saa7164_vbi_acquire_port(struct saa7164_port *port)
22711bd27b2SSteven Toth {
22811bd27b2SSteven Toth struct saa7164_dev *dev = port->dev;
22911bd27b2SSteven Toth int ret;
23011bd27b2SSteven Toth
23111bd27b2SSteven Toth ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
23211bd27b2SSteven Toth if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
23311bd27b2SSteven Toth printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n",
23411bd27b2SSteven Toth __func__, ret);
23511bd27b2SSteven Toth ret = -EIO;
23611bd27b2SSteven Toth } else {
23711bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s() Acquired\n", __func__);
23811bd27b2SSteven Toth ret = 0;
23911bd27b2SSteven Toth }
24011bd27b2SSteven Toth
24111bd27b2SSteven Toth return ret;
24211bd27b2SSteven Toth }
24311bd27b2SSteven Toth
saa7164_vbi_pause_port(struct saa7164_port * port)24411bd27b2SSteven Toth static int saa7164_vbi_pause_port(struct saa7164_port *port)
24511bd27b2SSteven Toth {
24611bd27b2SSteven Toth struct saa7164_dev *dev = port->dev;
24711bd27b2SSteven Toth int ret;
24811bd27b2SSteven Toth
24911bd27b2SSteven Toth ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
25011bd27b2SSteven Toth if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
25111bd27b2SSteven Toth printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n",
25211bd27b2SSteven Toth __func__, ret);
25311bd27b2SSteven Toth ret = -EIO;
25411bd27b2SSteven Toth } else {
25511bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s() Paused\n", __func__);
25611bd27b2SSteven Toth ret = 0;
25711bd27b2SSteven Toth }
25811bd27b2SSteven Toth
25911bd27b2SSteven Toth return ret;
26011bd27b2SSteven Toth }
26111bd27b2SSteven Toth
26211bd27b2SSteven Toth /* Firmware is very windows centric, meaning you have to transition
26311bd27b2SSteven Toth * the part through AVStream / KS Windows stages, forwards or backwards.
26411bd27b2SSteven Toth * States are: stopped, acquired (h/w), paused, started.
26511bd27b2SSteven Toth * We have to leave here will all of the soft buffers on the free list,
26611bd27b2SSteven Toth * else the cfg_post() func won't have soft buffers to correctly configure.
26711bd27b2SSteven Toth */
saa7164_vbi_stop_streaming(struct saa7164_port * port)26811bd27b2SSteven Toth static int saa7164_vbi_stop_streaming(struct saa7164_port *port)
26911bd27b2SSteven Toth {
27011bd27b2SSteven Toth struct saa7164_dev *dev = port->dev;
27111bd27b2SSteven Toth struct saa7164_buffer *buf;
27211bd27b2SSteven Toth struct saa7164_user_buffer *ubuf;
27311bd27b2SSteven Toth struct list_head *c, *n;
27411bd27b2SSteven Toth int ret;
27511bd27b2SSteven Toth
27611bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr);
27711bd27b2SSteven Toth
27811bd27b2SSteven Toth ret = saa7164_vbi_pause_port(port);
27911bd27b2SSteven Toth ret = saa7164_vbi_acquire_port(port);
28011bd27b2SSteven Toth ret = saa7164_vbi_stop_port(port);
28111bd27b2SSteven Toth
28211bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s(port=%d) Hardware stopped\n", __func__,
28311bd27b2SSteven Toth port->nr);
28411bd27b2SSteven Toth
28511bd27b2SSteven Toth /* Reset the state of any allocated buffer resources */
28611bd27b2SSteven Toth mutex_lock(&port->dmaqueue_lock);
28711bd27b2SSteven Toth
28811bd27b2SSteven Toth /* Reset the hard and soft buffer state */
28911bd27b2SSteven Toth list_for_each_safe(c, n, &port->dmaqueue.list) {
29011bd27b2SSteven Toth buf = list_entry(c, struct saa7164_buffer, list);
29111bd27b2SSteven Toth buf->flags = SAA7164_BUFFER_FREE;
29211bd27b2SSteven Toth buf->pos = 0;
29311bd27b2SSteven Toth }
29411bd27b2SSteven Toth
29511bd27b2SSteven Toth list_for_each_safe(c, n, &port->list_buf_used.list) {
29611bd27b2SSteven Toth ubuf = list_entry(c, struct saa7164_user_buffer, list);
29711bd27b2SSteven Toth ubuf->pos = 0;
29811bd27b2SSteven Toth list_move_tail(&ubuf->list, &port->list_buf_free.list);
29911bd27b2SSteven Toth }
30011bd27b2SSteven Toth
30111bd27b2SSteven Toth mutex_unlock(&port->dmaqueue_lock);
30211bd27b2SSteven Toth
30311bd27b2SSteven Toth /* Free any allocated resources */
30411bd27b2SSteven Toth saa7164_vbi_buffers_dealloc(port);
30511bd27b2SSteven Toth
30611bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s(port=%d) Released\n", __func__, port->nr);
30711bd27b2SSteven Toth
30811bd27b2SSteven Toth return ret;
30911bd27b2SSteven Toth }
31011bd27b2SSteven Toth
saa7164_vbi_start_streaming(struct saa7164_port * port)31111bd27b2SSteven Toth static int saa7164_vbi_start_streaming(struct saa7164_port *port)
31211bd27b2SSteven Toth {
31311bd27b2SSteven Toth struct saa7164_dev *dev = port->dev;
31411bd27b2SSteven Toth int result, ret = 0;
31511bd27b2SSteven Toth
31611bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr);
31711bd27b2SSteven Toth
31811bd27b2SSteven Toth port->done_first_interrupt = 0;
31911bd27b2SSteven Toth
32011bd27b2SSteven Toth /* allocate all of the PCIe DMA buffer resources on the fly,
32111bd27b2SSteven Toth * allowing switching between TS and PS payloads without
32211bd27b2SSteven Toth * requiring a complete driver reload.
32311bd27b2SSteven Toth */
32411bd27b2SSteven Toth saa7164_vbi_buffers_alloc(port);
32511bd27b2SSteven Toth
32611bd27b2SSteven Toth /* Configure the encoder with any cache values */
327bc250684SSteven Toth #if 0
328bc250684SSteven Toth saa7164_api_set_encoder(port);
329bc250684SSteven Toth saa7164_api_get_encoder(port);
330bc250684SSteven Toth #endif
33111bd27b2SSteven Toth
33211bd27b2SSteven Toth /* Place the empty buffers on the hardware */
33311bd27b2SSteven Toth saa7164_buffer_cfg_port(port);
33411bd27b2SSteven Toth
33511bd27b2SSteven Toth /* Negotiate format */
33611bd27b2SSteven Toth if (saa7164_api_set_vbi_format(port) != SAA_OK) {
33711bd27b2SSteven Toth printk(KERN_ERR "%s() No supported VBI format\n", __func__);
33811bd27b2SSteven Toth ret = -EIO;
33911bd27b2SSteven Toth goto out;
34011bd27b2SSteven Toth }
34111bd27b2SSteven Toth
34211bd27b2SSteven Toth /* Acquire the hardware */
34311bd27b2SSteven Toth result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
34411bd27b2SSteven Toth if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
34511bd27b2SSteven Toth printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n",
34611bd27b2SSteven Toth __func__, result);
34711bd27b2SSteven Toth
34811bd27b2SSteven Toth ret = -EIO;
34911bd27b2SSteven Toth goto out;
35011bd27b2SSteven Toth } else
35111bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s() Acquired\n", __func__);
35211bd27b2SSteven Toth
35311bd27b2SSteven Toth /* Pause the hardware */
35411bd27b2SSteven Toth result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
35511bd27b2SSteven Toth if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
35611bd27b2SSteven Toth printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n",
35711bd27b2SSteven Toth __func__, result);
35811bd27b2SSteven Toth
35911bd27b2SSteven Toth /* Stop the hardware, regardless */
36011bd27b2SSteven Toth result = saa7164_vbi_stop_port(port);
3612e71064fSDan Carpenter if (result != SAA_OK) {
36224f711c1SMauro Carvalho Chehab printk(KERN_ERR "%s() pause/forced stop transition failed, res = 0x%x\n",
36324f711c1SMauro Carvalho Chehab __func__, result);
36411bd27b2SSteven Toth }
36511bd27b2SSteven Toth
36611bd27b2SSteven Toth ret = -EIO;
36711bd27b2SSteven Toth goto out;
36811bd27b2SSteven Toth } else
36911bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s() Paused\n", __func__);
37011bd27b2SSteven Toth
37111bd27b2SSteven Toth /* Start the hardware */
37211bd27b2SSteven Toth result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN);
37311bd27b2SSteven Toth if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
37411bd27b2SSteven Toth printk(KERN_ERR "%s() run transition failed, result = 0x%x\n",
37511bd27b2SSteven Toth __func__, result);
37611bd27b2SSteven Toth
37711bd27b2SSteven Toth /* Stop the hardware, regardless */
37811bd27b2SSteven Toth result = saa7164_vbi_acquire_port(port);
37911bd27b2SSteven Toth result = saa7164_vbi_stop_port(port);
3802e71064fSDan Carpenter if (result != SAA_OK) {
38124f711c1SMauro Carvalho Chehab printk(KERN_ERR "%s() run/forced stop transition failed, res = 0x%x\n",
38224f711c1SMauro Carvalho Chehab __func__, result);
38311bd27b2SSteven Toth }
38411bd27b2SSteven Toth
38511bd27b2SSteven Toth ret = -EIO;
38611bd27b2SSteven Toth } else
38711bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s() Running\n", __func__);
38811bd27b2SSteven Toth
38911bd27b2SSteven Toth out:
39011bd27b2SSteven Toth return ret;
39111bd27b2SSteven Toth }
39211bd27b2SSteven Toth
saa7164_vbi_fmt(struct file * file,void * priv,struct v4l2_format * f)3935faf7db8SMauro Carvalho Chehab static int saa7164_vbi_fmt(struct file *file, void *priv,
3945faf7db8SMauro Carvalho Chehab struct v4l2_format *f)
39511bd27b2SSteven Toth {
39611bd27b2SSteven Toth /* ntsc */
39711bd27b2SSteven Toth f->fmt.vbi.samples_per_line = 1440;
39811bd27b2SSteven Toth f->fmt.vbi.sampling_rate = 27000000;
39911bd27b2SSteven Toth f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
40011bd27b2SSteven Toth f->fmt.vbi.offset = 0;
40111bd27b2SSteven Toth f->fmt.vbi.flags = 0;
40211bd27b2SSteven Toth f->fmt.vbi.start[0] = 10;
40311bd27b2SSteven Toth f->fmt.vbi.count[0] = 18;
40411bd27b2SSteven Toth f->fmt.vbi.start[1] = 263 + 10 + 1;
40511bd27b2SSteven Toth f->fmt.vbi.count[1] = 18;
406031d2297SHans Verkuil memset(f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved));
40711bd27b2SSteven Toth return 0;
40811bd27b2SSteven Toth }
40911bd27b2SSteven Toth
fops_open(struct file * file)41011bd27b2SSteven Toth static int fops_open(struct file *file)
41111bd27b2SSteven Toth {
412214ce3faSSteven Toth struct saa7164_dev *dev;
413214ce3faSSteven Toth struct saa7164_port *port;
41411bd27b2SSteven Toth struct saa7164_vbi_fh *fh;
415214ce3faSSteven Toth
416214ce3faSSteven Toth port = (struct saa7164_port *)video_get_drvdata(video_devdata(file));
417214ce3faSSteven Toth if (!port)
418214ce3faSSteven Toth return -ENODEV;
419214ce3faSSteven Toth
420214ce3faSSteven Toth dev = port->dev;
42111bd27b2SSteven Toth
42211bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s()\n", __func__);
42311bd27b2SSteven Toth
42411bd27b2SSteven Toth /* allocate + initialize per filehandle data */
42511bd27b2SSteven Toth fh = kzalloc(sizeof(*fh), GFP_KERNEL);
426214ce3faSSteven Toth if (NULL == fh)
42711bd27b2SSteven Toth return -ENOMEM;
42811bd27b2SSteven Toth
42911bd27b2SSteven Toth fh->port = port;
430d6d3fe2fSHans Verkuil v4l2_fh_init(&fh->fh, video_devdata(file));
431d6d3fe2fSHans Verkuil v4l2_fh_add(&fh->fh);
432d6d3fe2fSHans Verkuil file->private_data = fh;
43311bd27b2SSteven Toth
43411bd27b2SSteven Toth return 0;
43511bd27b2SSteven Toth }
43611bd27b2SSteven Toth
fops_release(struct file * file)43711bd27b2SSteven Toth static int fops_release(struct file *file)
43811bd27b2SSteven Toth {
43911bd27b2SSteven Toth struct saa7164_vbi_fh *fh = file->private_data;
44011bd27b2SSteven Toth struct saa7164_port *port = fh->port;
44111bd27b2SSteven Toth struct saa7164_dev *dev = port->dev;
44211bd27b2SSteven Toth
44311bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s()\n", __func__);
44411bd27b2SSteven Toth
44511bd27b2SSteven Toth /* Shut device down on last close */
44611bd27b2SSteven Toth if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
44711bd27b2SSteven Toth if (atomic_dec_return(&port->v4l_reader_count) == 0) {
44811bd27b2SSteven Toth /* stop vbi capture then cancel buffers */
44911bd27b2SSteven Toth saa7164_vbi_stop_streaming(port);
45011bd27b2SSteven Toth }
45111bd27b2SSteven Toth }
45211bd27b2SSteven Toth
453d6d3fe2fSHans Verkuil v4l2_fh_del(&fh->fh);
454d6d3fe2fSHans Verkuil v4l2_fh_exit(&fh->fh);
45511bd27b2SSteven Toth kfree(fh);
45611bd27b2SSteven Toth
45711bd27b2SSteven Toth return 0;
45811bd27b2SSteven Toth }
45911bd27b2SSteven Toth
4605faf7db8SMauro Carvalho Chehab static struct
saa7164_vbi_next_buf(struct saa7164_port * port)4615faf7db8SMauro Carvalho Chehab saa7164_user_buffer *saa7164_vbi_next_buf(struct saa7164_port *port)
46211bd27b2SSteven Toth {
46361ca1500SPeter Huewe struct saa7164_user_buffer *ubuf = NULL;
46411bd27b2SSteven Toth struct saa7164_dev *dev = port->dev;
46511bd27b2SSteven Toth u32 crc;
46611bd27b2SSteven Toth
46711bd27b2SSteven Toth mutex_lock(&port->dmaqueue_lock);
46811bd27b2SSteven Toth if (!list_empty(&port->list_buf_used.list)) {
46911bd27b2SSteven Toth ubuf = list_first_entry(&port->list_buf_used.list,
47011bd27b2SSteven Toth struct saa7164_user_buffer, list);
47111bd27b2SSteven Toth
47211bd27b2SSteven Toth if (crc_checking) {
47311bd27b2SSteven Toth crc = crc32(0, ubuf->data, ubuf->actual_size);
47411bd27b2SSteven Toth if (crc != ubuf->crc) {
475bc250684SSteven Toth printk(KERN_ERR "%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n",
476bc250684SSteven Toth __func__,
47711bd27b2SSteven Toth ubuf, ubuf->crc, crc);
47811bd27b2SSteven Toth }
47911bd27b2SSteven Toth }
48011bd27b2SSteven Toth
48111bd27b2SSteven Toth }
48211bd27b2SSteven Toth mutex_unlock(&port->dmaqueue_lock);
48311bd27b2SSteven Toth
48411bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s() returns %p\n", __func__, ubuf);
48511bd27b2SSteven Toth
48611bd27b2SSteven Toth return ubuf;
48711bd27b2SSteven Toth }
48811bd27b2SSteven Toth
fops_read(struct file * file,char __user * buffer,size_t count,loff_t * pos)48911bd27b2SSteven Toth static ssize_t fops_read(struct file *file, char __user *buffer,
49011bd27b2SSteven Toth size_t count, loff_t *pos)
49111bd27b2SSteven Toth {
49211bd27b2SSteven Toth struct saa7164_vbi_fh *fh = file->private_data;
49311bd27b2SSteven Toth struct saa7164_port *port = fh->port;
49411bd27b2SSteven Toth struct saa7164_user_buffer *ubuf = NULL;
49511bd27b2SSteven Toth struct saa7164_dev *dev = port->dev;
496106d7e37SGavin Hurlbut int ret = 0;
49711bd27b2SSteven Toth int rem, cnt;
49811bd27b2SSteven Toth u8 *p;
49911bd27b2SSteven Toth
50011bd27b2SSteven Toth port->last_read_msecs_diff = port->last_read_msecs;
50111bd27b2SSteven Toth port->last_read_msecs = jiffies_to_msecs(jiffies);
50211bd27b2SSteven Toth port->last_read_msecs_diff = port->last_read_msecs -
50311bd27b2SSteven Toth port->last_read_msecs_diff;
50411bd27b2SSteven Toth
50511bd27b2SSteven Toth saa7164_histogram_update(&port->read_interval,
50611bd27b2SSteven Toth port->last_read_msecs_diff);
50711bd27b2SSteven Toth
50811bd27b2SSteven Toth if (*pos) {
50911bd27b2SSteven Toth printk(KERN_ERR "%s() ESPIPE\n", __func__);
51011bd27b2SSteven Toth return -ESPIPE;
51111bd27b2SSteven Toth }
51211bd27b2SSteven Toth
51311bd27b2SSteven Toth if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
51411bd27b2SSteven Toth if (atomic_inc_return(&port->v4l_reader_count) == 1) {
51511bd27b2SSteven Toth
51611bd27b2SSteven Toth if (saa7164_vbi_initialize(port) < 0) {
51711bd27b2SSteven Toth printk(KERN_ERR "%s() EINVAL\n", __func__);
51811bd27b2SSteven Toth return -EINVAL;
51911bd27b2SSteven Toth }
52011bd27b2SSteven Toth
52111bd27b2SSteven Toth saa7164_vbi_start_streaming(port);
52211bd27b2SSteven Toth msleep(200);
52311bd27b2SSteven Toth }
52411bd27b2SSteven Toth }
52511bd27b2SSteven Toth
52611bd27b2SSteven Toth /* blocking wait for buffer */
52711bd27b2SSteven Toth if ((file->f_flags & O_NONBLOCK) == 0) {
52811bd27b2SSteven Toth if (wait_event_interruptible(port->wait_read,
52911bd27b2SSteven Toth saa7164_vbi_next_buf(port))) {
53011bd27b2SSteven Toth printk(KERN_ERR "%s() ERESTARTSYS\n", __func__);
53111bd27b2SSteven Toth return -ERESTARTSYS;
53211bd27b2SSteven Toth }
53311bd27b2SSteven Toth }
53411bd27b2SSteven Toth
53511bd27b2SSteven Toth /* Pull the first buffer from the used list */
53611bd27b2SSteven Toth ubuf = saa7164_vbi_next_buf(port);
53711bd27b2SSteven Toth
53811bd27b2SSteven Toth while ((count > 0) && ubuf) {
53911bd27b2SSteven Toth
54011bd27b2SSteven Toth /* set remaining bytes to copy */
54111bd27b2SSteven Toth rem = ubuf->actual_size - ubuf->pos;
54211bd27b2SSteven Toth cnt = rem > count ? count : rem;
54311bd27b2SSteven Toth
54411bd27b2SSteven Toth p = ubuf->data + ubuf->pos;
54511bd27b2SSteven Toth
54611bd27b2SSteven Toth dprintk(DBGLVL_VBI,
54711bd27b2SSteven Toth "%s() count=%d cnt=%d rem=%d buf=%p buf->pos=%d\n",
54811bd27b2SSteven Toth __func__, (int)count, cnt, rem, ubuf, ubuf->pos);
54911bd27b2SSteven Toth
55011bd27b2SSteven Toth if (copy_to_user(buffer, p, cnt)) {
55111bd27b2SSteven Toth printk(KERN_ERR "%s() copy_to_user failed\n", __func__);
55211bd27b2SSteven Toth if (!ret) {
55311bd27b2SSteven Toth printk(KERN_ERR "%s() EFAULT\n", __func__);
55411bd27b2SSteven Toth ret = -EFAULT;
55511bd27b2SSteven Toth }
55611bd27b2SSteven Toth goto err;
55711bd27b2SSteven Toth }
55811bd27b2SSteven Toth
55911bd27b2SSteven Toth ubuf->pos += cnt;
56011bd27b2SSteven Toth count -= cnt;
56111bd27b2SSteven Toth buffer += cnt;
56211bd27b2SSteven Toth ret += cnt;
56311bd27b2SSteven Toth
564bc250684SSteven Toth if (ubuf->pos > ubuf->actual_size)
56511bd27b2SSteven Toth printk(KERN_ERR "read() pos > actual, huh?\n");
56611bd27b2SSteven Toth
56711bd27b2SSteven Toth if (ubuf->pos == ubuf->actual_size) {
56811bd27b2SSteven Toth
56911bd27b2SSteven Toth /* finished with current buffer, take next buffer */
57011bd27b2SSteven Toth
57111bd27b2SSteven Toth /* Requeue the buffer on the free list */
57211bd27b2SSteven Toth ubuf->pos = 0;
57311bd27b2SSteven Toth
57411bd27b2SSteven Toth mutex_lock(&port->dmaqueue_lock);
57511bd27b2SSteven Toth list_move_tail(&ubuf->list, &port->list_buf_free.list);
57611bd27b2SSteven Toth mutex_unlock(&port->dmaqueue_lock);
57711bd27b2SSteven Toth
57811bd27b2SSteven Toth /* Dequeue next */
57911bd27b2SSteven Toth if ((file->f_flags & O_NONBLOCK) == 0) {
58011bd27b2SSteven Toth if (wait_event_interruptible(port->wait_read,
58111bd27b2SSteven Toth saa7164_vbi_next_buf(port))) {
58211bd27b2SSteven Toth break;
58311bd27b2SSteven Toth }
58411bd27b2SSteven Toth }
58511bd27b2SSteven Toth ubuf = saa7164_vbi_next_buf(port);
58611bd27b2SSteven Toth }
58711bd27b2SSteven Toth }
58811bd27b2SSteven Toth err:
58911bd27b2SSteven Toth if (!ret && !ubuf) {
59011bd27b2SSteven Toth printk(KERN_ERR "%s() EAGAIN\n", __func__);
59111bd27b2SSteven Toth ret = -EAGAIN;
59211bd27b2SSteven Toth }
59311bd27b2SSteven Toth
59411bd27b2SSteven Toth return ret;
59511bd27b2SSteven Toth }
59611bd27b2SSteven Toth
fops_poll(struct file * file,poll_table * wait)597c23e0cb8SAl Viro static __poll_t fops_poll(struct file *file, poll_table *wait)
59811bd27b2SSteven Toth {
59911bd27b2SSteven Toth struct saa7164_vbi_fh *fh = (struct saa7164_vbi_fh *)file->private_data;
60011bd27b2SSteven Toth struct saa7164_port *port = fh->port;
601c23e0cb8SAl Viro __poll_t mask = 0;
60211bd27b2SSteven Toth
60311bd27b2SSteven Toth port->last_poll_msecs_diff = port->last_poll_msecs;
60411bd27b2SSteven Toth port->last_poll_msecs = jiffies_to_msecs(jiffies);
60511bd27b2SSteven Toth port->last_poll_msecs_diff = port->last_poll_msecs -
60611bd27b2SSteven Toth port->last_poll_msecs_diff;
60711bd27b2SSteven Toth
60811bd27b2SSteven Toth saa7164_histogram_update(&port->poll_interval,
60911bd27b2SSteven Toth port->last_poll_msecs_diff);
61011bd27b2SSteven Toth
611bc250684SSteven Toth if (!video_is_registered(port->v4l_device))
6129de1be6eSMauro Carvalho Chehab return EPOLLERR;
61311bd27b2SSteven Toth
61411bd27b2SSteven Toth if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
61511bd27b2SSteven Toth if (atomic_inc_return(&port->v4l_reader_count) == 1) {
61611bd27b2SSteven Toth if (saa7164_vbi_initialize(port) < 0)
6179de1be6eSMauro Carvalho Chehab return EPOLLERR;
61811bd27b2SSteven Toth saa7164_vbi_start_streaming(port);
61911bd27b2SSteven Toth msleep(200);
62011bd27b2SSteven Toth }
62111bd27b2SSteven Toth }
62211bd27b2SSteven Toth
62311bd27b2SSteven Toth /* blocking wait for buffer */
62411bd27b2SSteven Toth if ((file->f_flags & O_NONBLOCK) == 0) {
62511bd27b2SSteven Toth if (wait_event_interruptible(port->wait_read,
62611bd27b2SSteven Toth saa7164_vbi_next_buf(port))) {
6279de1be6eSMauro Carvalho Chehab return EPOLLERR;
62811bd27b2SSteven Toth }
62911bd27b2SSteven Toth }
63011bd27b2SSteven Toth
63111bd27b2SSteven Toth /* Pull the first buffer from the used list */
632061d55ebSDan Carpenter if (!list_empty(&port->list_buf_used.list))
633a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM;
63411bd27b2SSteven Toth
63511bd27b2SSteven Toth return mask;
63611bd27b2SSteven Toth }
63711bd27b2SSteven Toth static const struct v4l2_file_operations vbi_fops = {
63811bd27b2SSteven Toth .owner = THIS_MODULE,
63911bd27b2SSteven Toth .open = fops_open,
64011bd27b2SSteven Toth .release = fops_release,
64111bd27b2SSteven Toth .read = fops_read,
64211bd27b2SSteven Toth .poll = fops_poll,
64311bd27b2SSteven Toth .unlocked_ioctl = video_ioctl2,
64411bd27b2SSteven Toth };
64511bd27b2SSteven Toth
64611bd27b2SSteven Toth static const struct v4l2_ioctl_ops vbi_ioctl_ops = {
64711bd27b2SSteven Toth .vidioc_s_std = vidioc_s_std,
6488d2d41e9SHans Verkuil .vidioc_g_std = vidioc_g_std,
649225b783bSHans Verkuil .vidioc_enum_input = saa7164_enum_input,
65011bd27b2SSteven Toth .vidioc_g_input = vidioc_g_input,
65111bd27b2SSteven Toth .vidioc_s_input = vidioc_s_input,
652225b783bSHans Verkuil .vidioc_g_tuner = saa7164_g_tuner,
653225b783bSHans Verkuil .vidioc_s_tuner = saa7164_s_tuner,
65411bd27b2SSteven Toth .vidioc_g_frequency = vidioc_g_frequency,
65511bd27b2SSteven Toth .vidioc_s_frequency = vidioc_s_frequency,
65611bd27b2SSteven Toth .vidioc_querycap = vidioc_querycap,
65711bd27b2SSteven Toth .vidioc_g_fmt_vbi_cap = saa7164_vbi_fmt,
65811bd27b2SSteven Toth .vidioc_try_fmt_vbi_cap = saa7164_vbi_fmt,
65911bd27b2SSteven Toth .vidioc_s_fmt_vbi_cap = saa7164_vbi_fmt,
66011bd27b2SSteven Toth };
66111bd27b2SSteven Toth
66211bd27b2SSteven Toth static struct video_device saa7164_vbi_template = {
66311bd27b2SSteven Toth .name = "saa7164",
66411bd27b2SSteven Toth .fops = &vbi_fops,
66511bd27b2SSteven Toth .ioctl_ops = &vbi_ioctl_ops,
66611bd27b2SSteven Toth .minor = -1,
66711bd27b2SSteven Toth .tvnorms = SAA7164_NORMS,
66821615365SHans Verkuil .device_caps = V4L2_CAP_VBI_CAPTURE | V4L2_CAP_READWRITE |
66921615365SHans Verkuil V4L2_CAP_TUNER,
67011bd27b2SSteven Toth };
67111bd27b2SSteven Toth
saa7164_vbi_alloc(struct saa7164_port * port,struct pci_dev * pci,struct video_device * template,char * type)67211bd27b2SSteven Toth static struct video_device *saa7164_vbi_alloc(
67311bd27b2SSteven Toth struct saa7164_port *port,
67411bd27b2SSteven Toth struct pci_dev *pci,
67511bd27b2SSteven Toth struct video_device *template,
67611bd27b2SSteven Toth char *type)
67711bd27b2SSteven Toth {
67811bd27b2SSteven Toth struct video_device *vfd;
67911bd27b2SSteven Toth struct saa7164_dev *dev = port->dev;
68011bd27b2SSteven Toth
68111bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s()\n", __func__);
68211bd27b2SSteven Toth
68311bd27b2SSteven Toth vfd = video_device_alloc();
68411bd27b2SSteven Toth if (NULL == vfd)
68511bd27b2SSteven Toth return NULL;
68611bd27b2SSteven Toth
68711bd27b2SSteven Toth *vfd = *template;
68811bd27b2SSteven Toth snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,
68911bd27b2SSteven Toth type, saa7164_boards[dev->board].name);
69011bd27b2SSteven Toth
691d66de790SHans Verkuil vfd->v4l2_dev = &dev->v4l2_dev;
69211bd27b2SSteven Toth vfd->release = video_device_release;
69311bd27b2SSteven Toth return vfd;
69411bd27b2SSteven Toth }
69511bd27b2SSteven Toth
saa7164_vbi_register(struct saa7164_port * port)69611bd27b2SSteven Toth int saa7164_vbi_register(struct saa7164_port *port)
69711bd27b2SSteven Toth {
69811bd27b2SSteven Toth struct saa7164_dev *dev = port->dev;
69911bd27b2SSteven Toth int result = -ENODEV;
70011bd27b2SSteven Toth
70111bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s()\n", __func__);
70211bd27b2SSteven Toth
703d8018ec1SDaniel W. S. Almeida BUG_ON(port->type != SAA7164_MPEG_VBI);
70411bd27b2SSteven Toth
70511bd27b2SSteven Toth /* Sanity check that the PCI configuration space is active */
70611bd27b2SSteven Toth if (port->hwcfg.BARLocation == 0) {
70724f711c1SMauro Carvalho Chehab printk(KERN_ERR "%s() failed (errno = %d), NO PCI configuration\n",
70811bd27b2SSteven Toth __func__, result);
70911bd27b2SSteven Toth result = -ENOMEM;
71011bd27b2SSteven Toth goto failed;
71111bd27b2SSteven Toth }
71211bd27b2SSteven Toth
71311bd27b2SSteven Toth /* Establish VBI defaults here */
71411bd27b2SSteven Toth
71511bd27b2SSteven Toth /* Allocate and register the video device node */
71611bd27b2SSteven Toth port->v4l_device = saa7164_vbi_alloc(port,
71711bd27b2SSteven Toth dev->pci, &saa7164_vbi_template, "vbi");
71811bd27b2SSteven Toth
71961ca1500SPeter Huewe if (!port->v4l_device) {
72011bd27b2SSteven Toth printk(KERN_INFO "%s: can't allocate vbi device\n",
72111bd27b2SSteven Toth dev->name);
72211bd27b2SSteven Toth result = -ENOMEM;
72311bd27b2SSteven Toth goto failed;
72411bd27b2SSteven Toth }
72511bd27b2SSteven Toth
726225b783bSHans Verkuil port->enc_port = &dev->ports[port->nr - 2];
727214ce3faSSteven Toth video_set_drvdata(port->v4l_device, port);
72811bd27b2SSteven Toth result = video_register_device(port->v4l_device,
72911bd27b2SSteven Toth VFL_TYPE_VBI, -1);
73011bd27b2SSteven Toth if (result < 0) {
73111bd27b2SSteven Toth printk(KERN_INFO "%s: can't register vbi device\n",
73211bd27b2SSteven Toth dev->name);
73311bd27b2SSteven Toth /* TODO: We're going to leak here if we don't dealloc
73411bd27b2SSteven Toth The buffers above. The unreg function can't deal wit it.
73511bd27b2SSteven Toth */
73611bd27b2SSteven Toth goto failed;
73711bd27b2SSteven Toth }
73811bd27b2SSteven Toth
73911bd27b2SSteven Toth printk(KERN_INFO "%s: registered device vbi%d [vbi]\n",
74011bd27b2SSteven Toth dev->name, port->v4l_device->num);
74111bd27b2SSteven Toth
74211bd27b2SSteven Toth /* Configure the hardware defaults */
74311bd27b2SSteven Toth
74411bd27b2SSteven Toth result = 0;
74511bd27b2SSteven Toth failed:
74611bd27b2SSteven Toth return result;
74711bd27b2SSteven Toth }
74811bd27b2SSteven Toth
saa7164_vbi_unregister(struct saa7164_port * port)74911bd27b2SSteven Toth void saa7164_vbi_unregister(struct saa7164_port *port)
75011bd27b2SSteven Toth {
75111bd27b2SSteven Toth struct saa7164_dev *dev = port->dev;
75211bd27b2SSteven Toth
75311bd27b2SSteven Toth dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr);
75411bd27b2SSteven Toth
755d8018ec1SDaniel W. S. Almeida BUG_ON(port->type != SAA7164_MPEG_VBI);
75611bd27b2SSteven Toth
75711bd27b2SSteven Toth if (port->v4l_device) {
75811bd27b2SSteven Toth if (port->v4l_device->minor != -1)
75911bd27b2SSteven Toth video_unregister_device(port->v4l_device);
76011bd27b2SSteven Toth else
76111bd27b2SSteven Toth video_device_release(port->v4l_device);
76211bd27b2SSteven Toth
76311bd27b2SSteven Toth port->v4l_device = NULL;
76411bd27b2SSteven Toth }
76511bd27b2SSteven Toth
76611bd27b2SSteven Toth }
767