769 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Driver for the NXP SAA7164 PCIe bridge4 *5 * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.com>6 */7 8#include "saa7164.h"9 10/* Take the encoder configuration from the port struct and11 * flush it to the hardware.12 */13static void saa7164_vbi_configure(struct saa7164_port *port)14{15 struct saa7164_dev *dev = port->dev;16 dprintk(DBGLVL_VBI, "%s()\n", __func__);17 18 port->vbi_params.width = port->enc_port->width;19 port->vbi_params.height = port->enc_port->height;20 port->vbi_params.is_50hz =21 (port->enc_port->encodernorm.id & V4L2_STD_625_50) != 0;22 23 /* Set up the DIF (enable it) for analog mode by default */24 saa7164_api_initialize_dif(port);25 dprintk(DBGLVL_VBI, "%s() ends\n", __func__);26}27 28static int saa7164_vbi_buffers_dealloc(struct saa7164_port *port)29{30 struct list_head *c, *n, *p, *q, *l, *v;31 struct saa7164_dev *dev = port->dev;32 struct saa7164_buffer *buf;33 struct saa7164_user_buffer *ubuf;34 35 /* Remove any allocated buffers */36 mutex_lock(&port->dmaqueue_lock);37 38 dprintk(DBGLVL_VBI, "%s(port=%d) dmaqueue\n", __func__, port->nr);39 list_for_each_safe(c, n, &port->dmaqueue.list) {40 buf = list_entry(c, struct saa7164_buffer, list);41 list_del(c);42 saa7164_buffer_dealloc(buf);43 }44 45 dprintk(DBGLVL_VBI, "%s(port=%d) used\n", __func__, port->nr);46 list_for_each_safe(p, q, &port->list_buf_used.list) {47 ubuf = list_entry(p, struct saa7164_user_buffer, list);48 list_del(p);49 saa7164_buffer_dealloc_user(ubuf);50 }51 52 dprintk(DBGLVL_VBI, "%s(port=%d) free\n", __func__, port->nr);53 list_for_each_safe(l, v, &port->list_buf_free.list) {54 ubuf = list_entry(l, struct saa7164_user_buffer, list);55 list_del(l);56 saa7164_buffer_dealloc_user(ubuf);57 }58 59 mutex_unlock(&port->dmaqueue_lock);60 dprintk(DBGLVL_VBI, "%s(port=%d) done\n", __func__, port->nr);61 62 return 0;63}64 65/* Dynamic buffer switch at vbi start time */66static int saa7164_vbi_buffers_alloc(struct saa7164_port *port)67{68 struct saa7164_dev *dev = port->dev;69 struct saa7164_buffer *buf;70 struct saa7164_user_buffer *ubuf;71 struct tmHWStreamParameters *params = &port->hw_streamingparams;72 int result = -ENODEV, i;73 int len = 0;74 75 dprintk(DBGLVL_VBI, "%s()\n", __func__);76 77 /* TODO: NTSC SPECIFIC */78 /* Init and establish defaults */79 params->samplesperline = 1440;80 params->numberoflines = 12;81 params->numberoflines = 18;82 params->pitch = 1600;83 params->pitch = 1440;84 params->numpagetables = 2 +85 ((params->numberoflines * params->pitch) / PAGE_SIZE);86 params->bitspersample = 8;87 params->linethreshold = 0;88 params->pagetablelistvirt = NULL;89 params->pagetablelistphys = NULL;90 params->numpagetableentries = port->hwcfg.buffercount;91 92 /* Allocate the PCI resources, buffers (hard) */93 for (i = 0; i < port->hwcfg.buffercount; i++) {94 buf = saa7164_buffer_alloc(port,95 params->numberoflines *96 params->pitch);97 98 if (!buf) {99 printk(KERN_ERR "%s() failed (errno = %d), unable to allocate buffer\n",100 __func__, result);101 result = -ENOMEM;102 goto failed;103 } else {104 105 mutex_lock(&port->dmaqueue_lock);106 list_add_tail(&buf->list, &port->dmaqueue.list);107 mutex_unlock(&port->dmaqueue_lock);108 109 }110 }111 112 /* Allocate some kernel buffers for copying113 * to userpsace.114 */115 len = params->numberoflines * params->pitch;116 117 if (vbi_buffers < 16)118 vbi_buffers = 16;119 if (vbi_buffers > 512)120 vbi_buffers = 512;121 122 for (i = 0; i < vbi_buffers; i++) {123 124 ubuf = saa7164_buffer_alloc_user(dev, len);125 if (ubuf) {126 mutex_lock(&port->dmaqueue_lock);127 list_add_tail(&ubuf->list, &port->list_buf_free.list);128 mutex_unlock(&port->dmaqueue_lock);129 }130 131 }132 133 result = 0;134 135failed:136 return result;137}138 139 140static int saa7164_vbi_initialize(struct saa7164_port *port)141{142 saa7164_vbi_configure(port);143 return 0;144}145 146/* -- V4L2 --------------------------------------------------------- */147static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id)148{149 struct saa7164_vbi_fh *fh = file->private_data;150 151 return saa7164_s_std(fh->port->enc_port, id);152}153 154static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)155{156 struct saa7164_encoder_fh *fh = file->private_data;157 158 return saa7164_g_std(fh->port->enc_port, id);159}160 161static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)162{163 struct saa7164_vbi_fh *fh = file->private_data;164 165 return saa7164_g_input(fh->port->enc_port, i);166}167 168static int vidioc_s_input(struct file *file, void *priv, unsigned int i)169{170 struct saa7164_vbi_fh *fh = file->private_data;171 172 return saa7164_s_input(fh->port->enc_port, i);173}174 175static int vidioc_g_frequency(struct file *file, void *priv,176 struct v4l2_frequency *f)177{178 struct saa7164_vbi_fh *fh = file->private_data;179 180 return saa7164_g_frequency(fh->port->enc_port, f);181}182 183static int vidioc_s_frequency(struct file *file, void *priv,184 const struct v4l2_frequency *f)185{186 struct saa7164_vbi_fh *fh = file->private_data;187 int ret = saa7164_s_frequency(fh->port->enc_port, f);188 189 if (ret == 0)190 saa7164_vbi_initialize(fh->port);191 return ret;192}193 194static int vidioc_querycap(struct file *file, void *priv,195 struct v4l2_capability *cap)196{197 struct saa7164_vbi_fh *fh = file->private_data;198 struct saa7164_port *port = fh->port;199 struct saa7164_dev *dev = port->dev;200 201 strscpy(cap->driver, dev->name, sizeof(cap->driver));202 strscpy(cap->card, saa7164_boards[dev->board].name,203 sizeof(cap->card));204 cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |205 V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE |206 V4L2_CAP_DEVICE_CAPS;207 return 0;208}209 210static int saa7164_vbi_stop_port(struct saa7164_port *port)211{212 struct saa7164_dev *dev = port->dev;213 int ret;214 215 ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);216 if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {217 printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n",218 __func__, ret);219 ret = -EIO;220 } else {221 dprintk(DBGLVL_VBI, "%s() Stopped\n", __func__);222 ret = 0;223 }224 225 return ret;226}227 228static int saa7164_vbi_acquire_port(struct saa7164_port *port)229{230 struct saa7164_dev *dev = port->dev;231 int ret;232 233 ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);234 if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {235 printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n",236 __func__, ret);237 ret = -EIO;238 } else {239 dprintk(DBGLVL_VBI, "%s() Acquired\n", __func__);240 ret = 0;241 }242 243 return ret;244}245 246static int saa7164_vbi_pause_port(struct saa7164_port *port)247{248 struct saa7164_dev *dev = port->dev;249 int ret;250 251 ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);252 if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {253 printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n",254 __func__, ret);255 ret = -EIO;256 } else {257 dprintk(DBGLVL_VBI, "%s() Paused\n", __func__);258 ret = 0;259 }260 261 return ret;262}263 264/* Firmware is very windows centric, meaning you have to transition265 * the part through AVStream / KS Windows stages, forwards or backwards.266 * States are: stopped, acquired (h/w), paused, started.267 * We have to leave here will all of the soft buffers on the free list,268 * else the cfg_post() func won't have soft buffers to correctly configure.269 */270static int saa7164_vbi_stop_streaming(struct saa7164_port *port)271{272 struct saa7164_dev *dev = port->dev;273 struct saa7164_buffer *buf;274 struct saa7164_user_buffer *ubuf;275 struct list_head *c, *n;276 int ret;277 278 dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr);279 280 ret = saa7164_vbi_pause_port(port);281 ret = saa7164_vbi_acquire_port(port);282 ret = saa7164_vbi_stop_port(port);283 284 dprintk(DBGLVL_VBI, "%s(port=%d) Hardware stopped\n", __func__,285 port->nr);286 287 /* Reset the state of any allocated buffer resources */288 mutex_lock(&port->dmaqueue_lock);289 290 /* Reset the hard and soft buffer state */291 list_for_each_safe(c, n, &port->dmaqueue.list) {292 buf = list_entry(c, struct saa7164_buffer, list);293 buf->flags = SAA7164_BUFFER_FREE;294 buf->pos = 0;295 }296 297 list_for_each_safe(c, n, &port->list_buf_used.list) {298 ubuf = list_entry(c, struct saa7164_user_buffer, list);299 ubuf->pos = 0;300 list_move_tail(&ubuf->list, &port->list_buf_free.list);301 }302 303 mutex_unlock(&port->dmaqueue_lock);304 305 /* Free any allocated resources */306 saa7164_vbi_buffers_dealloc(port);307 308 dprintk(DBGLVL_VBI, "%s(port=%d) Released\n", __func__, port->nr);309 310 return ret;311}312 313static int saa7164_vbi_start_streaming(struct saa7164_port *port)314{315 struct saa7164_dev *dev = port->dev;316 int result, ret = 0;317 318 dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr);319 320 port->done_first_interrupt = 0;321 322 /* allocate all of the PCIe DMA buffer resources on the fly,323 * allowing switching between TS and PS payloads without324 * requiring a complete driver reload.325 */326 saa7164_vbi_buffers_alloc(port);327 328 /* Configure the encoder with any cache values */329#if 0330 saa7164_api_set_encoder(port);331 saa7164_api_get_encoder(port);332#endif333 334 /* Place the empty buffers on the hardware */335 saa7164_buffer_cfg_port(port);336 337 /* Negotiate format */338 if (saa7164_api_set_vbi_format(port) != SAA_OK) {339 printk(KERN_ERR "%s() No supported VBI format\n", __func__);340 ret = -EIO;341 goto out;342 }343 344 /* Acquire the hardware */345 result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);346 if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {347 printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n",348 __func__, result);349 350 ret = -EIO;351 goto out;352 } else353 dprintk(DBGLVL_VBI, "%s() Acquired\n", __func__);354 355 /* Pause the hardware */356 result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);357 if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {358 printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n",359 __func__, result);360 361 /* Stop the hardware, regardless */362 result = saa7164_vbi_stop_port(port);363 if (result != SAA_OK) {364 printk(KERN_ERR "%s() pause/forced stop transition failed, res = 0x%x\n",365 __func__, result);366 }367 368 ret = -EIO;369 goto out;370 } else371 dprintk(DBGLVL_VBI, "%s() Paused\n", __func__);372 373 /* Start the hardware */374 result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN);375 if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {376 printk(KERN_ERR "%s() run transition failed, result = 0x%x\n",377 __func__, result);378 379 /* Stop the hardware, regardless */380 result = saa7164_vbi_acquire_port(port);381 result = saa7164_vbi_stop_port(port);382 if (result != SAA_OK) {383 printk(KERN_ERR "%s() run/forced stop transition failed, res = 0x%x\n",384 __func__, result);385 }386 387 ret = -EIO;388 } else389 dprintk(DBGLVL_VBI, "%s() Running\n", __func__);390 391out:392 return ret;393}394 395static int saa7164_vbi_fmt(struct file *file, void *priv,396 struct v4l2_format *f)397{398 /* ntsc */399 f->fmt.vbi.samples_per_line = 1440;400 f->fmt.vbi.sampling_rate = 27000000;401 f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;402 f->fmt.vbi.offset = 0;403 f->fmt.vbi.flags = 0;404 f->fmt.vbi.start[0] = 10;405 f->fmt.vbi.count[0] = 18;406 f->fmt.vbi.start[1] = 263 + 10 + 1;407 f->fmt.vbi.count[1] = 18;408 memset(f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved));409 return 0;410}411 412static int fops_open(struct file *file)413{414 struct saa7164_dev *dev;415 struct saa7164_port *port;416 struct saa7164_vbi_fh *fh;417 418 port = (struct saa7164_port *)video_get_drvdata(video_devdata(file));419 if (!port)420 return -ENODEV;421 422 dev = port->dev;423 424 dprintk(DBGLVL_VBI, "%s()\n", __func__);425 426 /* allocate + initialize per filehandle data */427 fh = kzalloc(sizeof(*fh), GFP_KERNEL);428 if (NULL == fh)429 return -ENOMEM;430 431 fh->port = port;432 v4l2_fh_init(&fh->fh, video_devdata(file));433 v4l2_fh_add(&fh->fh);434 file->private_data = fh;435 436 return 0;437}438 439static int fops_release(struct file *file)440{441 struct saa7164_vbi_fh *fh = file->private_data;442 struct saa7164_port *port = fh->port;443 struct saa7164_dev *dev = port->dev;444 445 dprintk(DBGLVL_VBI, "%s()\n", __func__);446 447 /* Shut device down on last close */448 if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {449 if (atomic_dec_return(&port->v4l_reader_count) == 0) {450 /* stop vbi capture then cancel buffers */451 saa7164_vbi_stop_streaming(port);452 }453 }454 455 v4l2_fh_del(&fh->fh);456 v4l2_fh_exit(&fh->fh);457 kfree(fh);458 459 return 0;460}461 462static struct463saa7164_user_buffer *saa7164_vbi_next_buf(struct saa7164_port *port)464{465 struct saa7164_user_buffer *ubuf = NULL;466 struct saa7164_dev *dev = port->dev;467 u32 crc;468 469 mutex_lock(&port->dmaqueue_lock);470 if (!list_empty(&port->list_buf_used.list)) {471 ubuf = list_first_entry(&port->list_buf_used.list,472 struct saa7164_user_buffer, list);473 474 if (crc_checking) {475 crc = crc32(0, ubuf->data, ubuf->actual_size);476 if (crc != ubuf->crc) {477 printk(KERN_ERR "%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n",478 __func__,479 ubuf, ubuf->crc, crc);480 }481 }482 483 }484 mutex_unlock(&port->dmaqueue_lock);485 486 dprintk(DBGLVL_VBI, "%s() returns %p\n", __func__, ubuf);487 488 return ubuf;489}490 491static ssize_t fops_read(struct file *file, char __user *buffer,492 size_t count, loff_t *pos)493{494 struct saa7164_vbi_fh *fh = file->private_data;495 struct saa7164_port *port = fh->port;496 struct saa7164_user_buffer *ubuf = NULL;497 struct saa7164_dev *dev = port->dev;498 int ret = 0;499 int rem, cnt;500 u8 *p;501 502 port->last_read_msecs_diff = port->last_read_msecs;503 port->last_read_msecs = jiffies_to_msecs(jiffies);504 port->last_read_msecs_diff = port->last_read_msecs -505 port->last_read_msecs_diff;506 507 saa7164_histogram_update(&port->read_interval,508 port->last_read_msecs_diff);509 510 if (*pos) {511 printk(KERN_ERR "%s() ESPIPE\n", __func__);512 return -ESPIPE;513 }514 515 if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {516 if (atomic_inc_return(&port->v4l_reader_count) == 1) {517 518 if (saa7164_vbi_initialize(port) < 0) {519 printk(KERN_ERR "%s() EINVAL\n", __func__);520 return -EINVAL;521 }522 523 saa7164_vbi_start_streaming(port);524 msleep(200);525 }526 }527 528 /* blocking wait for buffer */529 if ((file->f_flags & O_NONBLOCK) == 0) {530 if (wait_event_interruptible(port->wait_read,531 saa7164_vbi_next_buf(port))) {532 printk(KERN_ERR "%s() ERESTARTSYS\n", __func__);533 return -ERESTARTSYS;534 }535 }536 537 /* Pull the first buffer from the used list */538 ubuf = saa7164_vbi_next_buf(port);539 540 while ((count > 0) && ubuf) {541 542 /* set remaining bytes to copy */543 rem = ubuf->actual_size - ubuf->pos;544 cnt = rem > count ? count : rem;545 546 p = ubuf->data + ubuf->pos;547 548 dprintk(DBGLVL_VBI,549 "%s() count=%d cnt=%d rem=%d buf=%p buf->pos=%d\n",550 __func__, (int)count, cnt, rem, ubuf, ubuf->pos);551 552 if (copy_to_user(buffer, p, cnt)) {553 printk(KERN_ERR "%s() copy_to_user failed\n", __func__);554 if (!ret) {555 printk(KERN_ERR "%s() EFAULT\n", __func__);556 ret = -EFAULT;557 }558 goto err;559 }560 561 ubuf->pos += cnt;562 count -= cnt;563 buffer += cnt;564 ret += cnt;565 566 if (ubuf->pos > ubuf->actual_size)567 printk(KERN_ERR "read() pos > actual, huh?\n");568 569 if (ubuf->pos == ubuf->actual_size) {570 571 /* finished with current buffer, take next buffer */572 573 /* Requeue the buffer on the free list */574 ubuf->pos = 0;575 576 mutex_lock(&port->dmaqueue_lock);577 list_move_tail(&ubuf->list, &port->list_buf_free.list);578 mutex_unlock(&port->dmaqueue_lock);579 580 /* Dequeue next */581 if ((file->f_flags & O_NONBLOCK) == 0) {582 if (wait_event_interruptible(port->wait_read,583 saa7164_vbi_next_buf(port))) {584 break;585 }586 }587 ubuf = saa7164_vbi_next_buf(port);588 }589 }590err:591 if (!ret && !ubuf) {592 printk(KERN_ERR "%s() EAGAIN\n", __func__);593 ret = -EAGAIN;594 }595 596 return ret;597}598 599static __poll_t fops_poll(struct file *file, poll_table *wait)600{601 struct saa7164_vbi_fh *fh = (struct saa7164_vbi_fh *)file->private_data;602 struct saa7164_port *port = fh->port;603 __poll_t mask = 0;604 605 port->last_poll_msecs_diff = port->last_poll_msecs;606 port->last_poll_msecs = jiffies_to_msecs(jiffies);607 port->last_poll_msecs_diff = port->last_poll_msecs -608 port->last_poll_msecs_diff;609 610 saa7164_histogram_update(&port->poll_interval,611 port->last_poll_msecs_diff);612 613 if (!video_is_registered(port->v4l_device))614 return EPOLLERR;615 616 if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {617 if (atomic_inc_return(&port->v4l_reader_count) == 1) {618 if (saa7164_vbi_initialize(port) < 0)619 return EPOLLERR;620 saa7164_vbi_start_streaming(port);621 msleep(200);622 }623 }624 625 /* blocking wait for buffer */626 if ((file->f_flags & O_NONBLOCK) == 0) {627 if (wait_event_interruptible(port->wait_read,628 saa7164_vbi_next_buf(port))) {629 return EPOLLERR;630 }631 }632 633 /* Pull the first buffer from the used list */634 if (!list_empty(&port->list_buf_used.list))635 mask |= EPOLLIN | EPOLLRDNORM;636 637 return mask;638}639static const struct v4l2_file_operations vbi_fops = {640 .owner = THIS_MODULE,641 .open = fops_open,642 .release = fops_release,643 .read = fops_read,644 .poll = fops_poll,645 .unlocked_ioctl = video_ioctl2,646};647 648static const struct v4l2_ioctl_ops vbi_ioctl_ops = {649 .vidioc_s_std = vidioc_s_std,650 .vidioc_g_std = vidioc_g_std,651 .vidioc_enum_input = saa7164_enum_input,652 .vidioc_g_input = vidioc_g_input,653 .vidioc_s_input = vidioc_s_input,654 .vidioc_g_tuner = saa7164_g_tuner,655 .vidioc_s_tuner = saa7164_s_tuner,656 .vidioc_g_frequency = vidioc_g_frequency,657 .vidioc_s_frequency = vidioc_s_frequency,658 .vidioc_querycap = vidioc_querycap,659 .vidioc_g_fmt_vbi_cap = saa7164_vbi_fmt,660 .vidioc_try_fmt_vbi_cap = saa7164_vbi_fmt,661 .vidioc_s_fmt_vbi_cap = saa7164_vbi_fmt,662};663 664static struct video_device saa7164_vbi_template = {665 .name = "saa7164",666 .fops = &vbi_fops,667 .ioctl_ops = &vbi_ioctl_ops,668 .minor = -1,669 .tvnorms = SAA7164_NORMS,670 .device_caps = V4L2_CAP_VBI_CAPTURE | V4L2_CAP_READWRITE |671 V4L2_CAP_TUNER,672};673 674static struct video_device *saa7164_vbi_alloc(675 struct saa7164_port *port,676 struct pci_dev *pci,677 struct video_device *template,678 char *type)679{680 struct video_device *vfd;681 struct saa7164_dev *dev = port->dev;682 683 dprintk(DBGLVL_VBI, "%s()\n", __func__);684 685 vfd = video_device_alloc();686 if (NULL == vfd)687 return NULL;688 689 *vfd = *template;690 snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,691 type, saa7164_boards[dev->board].name);692 693 vfd->v4l2_dev = &dev->v4l2_dev;694 vfd->release = video_device_release;695 return vfd;696}697 698int saa7164_vbi_register(struct saa7164_port *port)699{700 struct saa7164_dev *dev = port->dev;701 int result = -ENODEV;702 703 dprintk(DBGLVL_VBI, "%s()\n", __func__);704 705 BUG_ON(port->type != SAA7164_MPEG_VBI);706 707 /* Sanity check that the PCI configuration space is active */708 if (port->hwcfg.BARLocation == 0) {709 printk(KERN_ERR "%s() failed (errno = %d), NO PCI configuration\n",710 __func__, result);711 result = -ENOMEM;712 goto failed;713 }714 715 /* Establish VBI defaults here */716 717 /* Allocate and register the video device node */718 port->v4l_device = saa7164_vbi_alloc(port,719 dev->pci, &saa7164_vbi_template, "vbi");720 721 if (!port->v4l_device) {722 printk(KERN_INFO "%s: can't allocate vbi device\n",723 dev->name);724 result = -ENOMEM;725 goto failed;726 }727 728 port->enc_port = &dev->ports[port->nr - 2];729 video_set_drvdata(port->v4l_device, port);730 result = video_register_device(port->v4l_device,731 VFL_TYPE_VBI, -1);732 if (result < 0) {733 printk(KERN_INFO "%s: can't register vbi device\n",734 dev->name);735 /* TODO: We're going to leak here if we don't dealloc736 The buffers above. The unreg function can't deal wit it.737 */738 goto failed;739 }740 741 printk(KERN_INFO "%s: registered device vbi%d [vbi]\n",742 dev->name, port->v4l_device->num);743 744 /* Configure the hardware defaults */745 746 result = 0;747failed:748 return result;749}750 751void saa7164_vbi_unregister(struct saa7164_port *port)752{753 struct saa7164_dev *dev = port->dev;754 755 dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr);756 757 BUG_ON(port->type != SAA7164_MPEG_VBI);758 759 if (port->v4l_device) {760 if (port->v4l_device->minor != -1)761 video_unregister_device(port->v4l_device);762 else763 video_device_release(port->v4l_device);764 765 port->v4l_device = NULL;766 }767 768}769