]> git.karo-electronics.de Git - linux-beck.git/commitdiff
[media] tw68: refactor and cleanup the tw68 driver
authorHans Verkuil <hans.verkuil@cisco.com>
Wed, 3 Sep 2014 06:36:14 +0000 (03:36 -0300)
committerMauro Carvalho Chehab <m.chehab@samsung.com>
Thu, 4 Sep 2014 14:30:53 +0000 (11:30 -0300)
Refactor and clean up the tw68 driver. It's now using the proper
V4L2 core frameworks.

Tested with my Techwell tw6805a and tw6816 grabber boards.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
14 files changed:
drivers/media/pci/Kconfig
drivers/media/pci/Makefile
drivers/media/pci/tw68/Kconfig [new file with mode: 0644]
drivers/media/pci/tw68/Makefile [new file with mode: 0644]
drivers/media/pci/tw68/tw68-cards.c [deleted file]
drivers/media/pci/tw68/tw68-core.c
drivers/media/pci/tw68/tw68-i2c.c [deleted file]
drivers/media/pci/tw68/tw68-reg.h
drivers/media/pci/tw68/tw68-risc.c
drivers/media/pci/tw68/tw68-ts.c [deleted file]
drivers/media/pci/tw68/tw68-tvaudio.c [deleted file]
drivers/media/pci/tw68/tw68-vbi.c [deleted file]
drivers/media/pci/tw68/tw68-video.c
drivers/media/pci/tw68/tw68.h

index 5c16c9c2203ef4e1a9fdd931ec3ecff5c2dc498a..9332807401767ce10d53aa64b159c67f099356f5 100644 (file)
@@ -20,6 +20,7 @@ source "drivers/media/pci/ivtv/Kconfig"
 source "drivers/media/pci/zoran/Kconfig"
 source "drivers/media/pci/saa7146/Kconfig"
 source "drivers/media/pci/solo6x10/Kconfig"
+source "drivers/media/pci/tw68/Kconfig"
 endif
 
 if MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT
index dc2ebbe27306b18bf57d71f11d14e282c85e2567..73d9c0f1112725ab006136a0927638df0cfb5042 100644 (file)
@@ -21,6 +21,7 @@ obj-$(CONFIG_VIDEO_CX88) += cx88/
 obj-$(CONFIG_VIDEO_BT848) += bt8xx/
 obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
 obj-$(CONFIG_VIDEO_SAA7164) += saa7164/
+obj-$(CONFIG_VIDEO_TW68) += tw68/
 obj-$(CONFIG_VIDEO_MEYE) += meye/
 obj-$(CONFIG_STA2X11_VIP) += sta2x11/
 obj-$(CONFIG_VIDEO_SOLO6X10) += solo6x10/
diff --git a/drivers/media/pci/tw68/Kconfig b/drivers/media/pci/tw68/Kconfig
new file mode 100644 (file)
index 0000000..5425ba1
--- /dev/null
@@ -0,0 +1,10 @@
+config VIDEO_TW68
+       tristate "Techwell tw68x Video For Linux"
+       depends on VIDEO_DEV && PCI && VIDEO_V4L2
+       select I2C_ALGOBIT
+       select VIDEOBUF2_DMA_SG
+       ---help---
+         Support for Techwell tw68xx based frame grabber boards.
+
+         To compile this driver as a module, choose M here: the
+         module will be called tw68.
diff --git a/drivers/media/pci/tw68/Makefile b/drivers/media/pci/tw68/Makefile
new file mode 100644 (file)
index 0000000..3d02f28
--- /dev/null
@@ -0,0 +1,3 @@
+tw68-objs := tw68-core.o tw68-video.o tw68-risc.o
+
+obj-$(CONFIG_VIDEO_TW68) += tw68.o
diff --git a/drivers/media/pci/tw68/tw68-cards.c b/drivers/media/pci/tw68/tw68-cards.c
deleted file mode 100644 (file)
index 62aec4f..0000000
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- *  device driver for Techwell 68xx based cards
- *
- *  Much of this code is derived from the cx88 and sa7134 drivers, which
- *  were in turn derived from the bt87x driver.  The original work was by
- *  Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
- *  Hans Verkuil, Andy Walls and many others.  Their work is gratefully
- *  acknowledged.  Full credit goes to them - any problems within this code
- *  are mine.
- *
- *  Copyright (C) 2009  William M. Brack <wbrack@mmm.com.hk>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/i2c.h>         /* must appear before i2c-algo-bit.h */
-#include <linux/i2c-algo-bit.h>
-
-#include <media/v4l2-common.h>
-#include <media/tveeprom.h>
-
-#include "tw68.h"
-#include "tw68-reg.h"
-
-/* commly used strings */
-#if 0
-static char name_mute[]    = "mute";
-static char name_radio[]   = "Radio";
-static char name_tv[]      = "Television";
-static char name_tv_mono[] = "TV (mono only)";
-static char name_svideo[]  = "S-Video";
-static char name_comp[]    = "Composite";
-#endif
-static char name_comp1[]   = "Composite1";
-static char name_comp2[]   = "Composite2";
-static char name_comp3[]   = "Composite3";
-static char name_comp4[]   = "Composite4";
-
-/* ------------------------------------------------------------------ */
-/* board config info                                                  */
-
-/* If radio_type !=UNSET, radio_addr should be specified
- */
-
-struct tw68_board tw68_boards[] = {
-       [TW68_BOARD_UNKNOWN] = {
-               .name           = "GENERIC",
-               .tuner_type     = TUNER_ABSENT,
-               .radio_type     = UNSET,
-               .tuner_addr     = ADDR_UNSET,
-               .radio_addr     = ADDR_UNSET,
-
-               .inputs         = {
-                       {
-                               .name = name_comp1,
-                               .vmux = 0,
-                       }, {
-                               .name = name_comp2,
-                               .vmux = 1,
-                       }, {
-                               .name = name_comp3,
-                               .vmux = 2,
-                       }, {
-                               .name = name_comp4,
-                               .vmux = 3,
-                       }, {    /* Must have a NULL entry at end of list */
-                               .name = NULL,
-                               .vmux = 0,
-                       }
-               },
-       },
-};
-
-const unsigned int tw68_bcount = ARRAY_SIZE(tw68_boards);
-
-/*
- * Please add any new PCI IDs to: http://pci-ids.ucw.cz.  This keeps
- * the PCI ID database up to date.  Note that the entries must be
- * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs.
- */
-struct pci_device_id tw68_pci_tbl[] = {
-       {
-               .vendor         = PCI_VENDOR_ID_TECHWELL,
-               .device         = PCI_DEVICE_ID_6800,
-               .subvendor      = PCI_ANY_ID,
-               .subdevice      = PCI_ANY_ID,
-               .driver_data    = TW68_BOARD_UNKNOWN,
-       }, {
-               .vendor         = PCI_VENDOR_ID_TECHWELL,
-               .device         = PCI_DEVICE_ID_6801,
-               .subvendor      = PCI_ANY_ID,
-               .subdevice      = PCI_ANY_ID,
-               .driver_data    = TW68_BOARD_UNKNOWN,
-       }, {
-               .vendor         = PCI_VENDOR_ID_TECHWELL,
-               .device         = PCI_DEVICE_ID_6804,
-               .subvendor      = PCI_ANY_ID,
-               .subdevice      = PCI_ANY_ID,
-               .driver_data    = TW68_BOARD_UNKNOWN,
-       }, {
-               .vendor         = PCI_VENDOR_ID_TECHWELL,
-               .device         = PCI_DEVICE_ID_6816_1,
-               .subvendor      = PCI_ANY_ID,
-               .subdevice      = PCI_ANY_ID,
-               .driver_data    = TW68_BOARD_UNKNOWN,
-       }, {
-               .vendor         = PCI_VENDOR_ID_TECHWELL,
-               .device         = PCI_DEVICE_ID_6816_2,
-               .subvendor      = PCI_ANY_ID,
-               .subdevice      = PCI_ANY_ID,
-               .driver_data    = TW68_BOARD_UNKNOWN,
-       }, {
-               .vendor         = PCI_VENDOR_ID_TECHWELL,
-               .device         = PCI_DEVICE_ID_6816_3,
-               .subvendor      = PCI_ANY_ID,
-               .subdevice      = PCI_ANY_ID,
-               .driver_data    = TW68_BOARD_UNKNOWN,
-       }, {
-               .vendor         = PCI_VENDOR_ID_TECHWELL,
-               .device         = PCI_DEVICE_ID_6816_4,
-               .subvendor      = PCI_ANY_ID,
-               .subdevice      = PCI_ANY_ID,
-               .driver_data    = TW68_BOARD_UNKNOWN,
-       }, {
-               /* end of list */
-       }
-};
-MODULE_DEVICE_TABLE(pci, tw68_pci_tbl);
-
-/* ------------------------------------------------------------ */
-/* stuff done before i2c enabled */
-int tw68_board_init1(struct tw68_dev *dev)
-{
-       /* Clear GPIO outputs */
-       tw_writel(TW68_GPOE, 0);
-       /* Remainder of setup according to board ID */
-       switch (dev->board) {
-       case TW68_BOARD_UNKNOWN:
-               printk(KERN_INFO "%s: Unable to determine board type, "
-                               "using generic values\n", dev->name);
-               break;
-       }
-       dev->input = dev->hw_input = &card_in(dev,0);
-       return 0;
-}
-
-int tw68_tuner_setup(struct tw68_dev *dev)
-{
-       return 0;
-}
-
-/* stuff which needs working i2c */
-int tw68_board_init2(struct tw68_dev *dev)
-{
-       return 0;
-}
-
-
index 2c5d7a5f3f8e52c7ace598037619221b9b9de977..baf93af1d764762866ec804ba1e0e5626dea0cc2 100644 (file)
@@ -9,7 +9,11 @@
  *  acknowledged.  Full credit goes to them - any problems within this code
  *  are mine.
  *
- *  Copyright (C) 2009  William M. Brack <wbrack@mmm.com.hk>
+ *  Copyright (C) 2009  William M. Brack
+ *
+ *  Refactored and updated to the latest v4l core frameworks:
+ *
+ *  Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
 #include <linux/init.h>
 #include "tw68-reg.h"
 
 MODULE_DESCRIPTION("v4l2 driver module for tw6800 based video capture cards");
-MODULE_AUTHOR("William M. Brack <wbrack@mmm.com.hk>");
+MODULE_AUTHOR("William M. Brack");
+MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
 MODULE_LICENSE("GPL");
 
-static unsigned int core_debug;
-module_param(core_debug, int, 0644);
-MODULE_PARM_DESC(core_debug, "enable debug messages [core]");
-
-static unsigned int gpio_tracking;
-module_param(gpio_tracking, int, 0644);
-MODULE_PARM_DESC(gpio_tracking, "enable debug messages [gpio]");
-
-static unsigned int alsa = 1;
-module_param(alsa, int, 0644);
-MODULE_PARM_DESC(alsa, "enable/disable ALSA DMA sound [dmasound]");
-
 static unsigned int latency = UNSET;
 module_param(latency, int, 0444);
 MODULE_PARM_DESC(latency, "pci latency timer");
 
-static unsigned int nocomb;
-module_param(nocomb, int, 0644);
-MODULE_PARM_DESC(nocomb, "disable comb filter");
-
 static unsigned int video_nr[] = {[0 ... (TW68_MAXBOARDS - 1)] = UNSET };
-static unsigned int vbi_nr[]   = {[0 ... (TW68_MAXBOARDS - 1)] = UNSET };
-static unsigned int radio_nr[] = {[0 ... (TW68_MAXBOARDS - 1)] = UNSET };
-static unsigned int tuner[]    = {[0 ... (TW68_MAXBOARDS - 1)] = UNSET };
-static unsigned int card[]     = {[0 ... (TW68_MAXBOARDS - 1)] = UNSET };
-
 module_param_array(video_nr, int, NULL, 0444);
-module_param_array(vbi_nr,   int, NULL, 0444);
-module_param_array(radio_nr, int, NULL, 0444);
-module_param_array(tuner,    int, NULL, 0444);
-module_param_array(card,     int, NULL, 0444);
-
 MODULE_PARM_DESC(video_nr, "video device number");
-MODULE_PARM_DESC(vbi_nr,   "vbi device number");
-MODULE_PARM_DESC(radio_nr, "radio device number");
-MODULE_PARM_DESC(tuner,    "tuner type");
-MODULE_PARM_DESC(card,     "card type");
-
-LIST_HEAD(tw68_devlist);
-EXPORT_SYMBOL(tw68_devlist);
-DEFINE_MUTEX(tw68_devlist_lock);
-EXPORT_SYMBOL(tw68_devlist_lock);
-static LIST_HEAD(mops_list);
-static unsigned int tw68_devcount;      /* curr tot num of devices present */
 
-int (*tw68_dmasound_init)(struct tw68_dev *dev);
-EXPORT_SYMBOL(tw68_dmasound_init);
-int (*tw68_dmasound_exit)(struct tw68_dev *dev);
-EXPORT_SYMBOL(tw68_dmasound_exit);
+static unsigned int card[] = {[0 ... (TW68_MAXBOARDS - 1)] = UNSET };
+module_param_array(card, int, NULL, 0444);
+MODULE_PARM_DESC(card, "card type");
 
-#define dprintk(level, fmt, arg...)      if (core_debug & (level)) \
-       printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg)
+static atomic_t tw68_instance = ATOMIC_INIT(0);
 
 /* ------------------------------------------------------------------ */
 
-void tw68_dma_free(struct videobuf_queue *q, struct tw68_buf *buf)
-{
-       struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
-       
-       if (core_debug & DBG_FLOW)
-               printk(KERN_DEBUG "%s: called\n", __func__);
-       BUG_ON(in_interrupt());
-
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36)
-       videobuf_waiton(&buf->vb, 0, 0);
-#else
-       videobuf_waiton(q, &buf->vb, 0, 0);
-#endif
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,35)       
-       videobuf_dma_unmap(q, dma);
-#else
-       videobuf_dma_unmap(q->dev, dma);
-#endif
-       videobuf_dma_free(dma);
-       /* if no risc area allocated, btcx_riscmem_free just returns */
-       btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc);
-       buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
-/* ------------------------------------------------------------------ */
-/* ------------- placeholders for later development ----------------- */
-
-static int tw68_input_init1(struct tw68_dev *dev)
-{
-       return 0;
-}
-
-static void tw68_input_fini(struct tw68_dev *dev)
-{
-       return;
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
-static void tw68_ir_start(struct tw68_dev *dev, struct card_ir *ir)
-{
-       return;
-}
-
-static void tw68_ir_stop(struct tw68_dev *dev)
-{
-       return;
-}
-#endif
-
-/* ------------------------------------------------------------------ */
 /*
- * Buffer handling routines
- *
- * These routines are "generic", i.e. are intended to be used by more
- * than one module, e.g. the video and the transport stream modules.
- * To accomplish this generality, callbacks are used whenever some
- * module-specific test or action is required.
+ * Please add any new PCI IDs to: http://pci-ids.ucw.cz.  This keeps
+ * the PCI ID database up to date.  Note that the entries must be
+ * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs.
  */
-
-/* resends a current buffer in queue after resume */
-int tw68_buffer_requeue(struct tw68_dev *dev,
-                                 struct tw68_dmaqueue *q)
-{
-       struct tw68_buf *buf, *prev;
-
-       dprintk(DBG_FLOW | DBG_TESTING, "%s: called\n", __func__);
-       if (!list_empty(&q->active)) {
-               buf = list_entry(q->active.next, struct tw68_buf, vb.queue);
-               dprintk(DBG_BUFF, "%s: [%p/%d] restart dma\n", __func__,
-                       buf, buf->vb.i);
-               q->start_dma(dev, q, buf);
-               mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
-               return 0;
-       }
-
-       prev = NULL;
-       for (;;) {
-               if (list_empty(&q->queued))
-                       return 0;
-               buf = list_entry(q->queued.next, struct tw68_buf, vb.queue);
-               /* if nothing precedes this one */
-               if (NULL == prev) {
-                       list_move_tail(&buf->vb.queue, &q->active);
-                       q->start_dma(dev, q, buf);
-                       buf->activate(dev, buf, NULL);
-                       dprintk(DBG_BUFF, "%s: [%p/%d] first active\n",
-                               __func__, buf, buf->vb.i);
-
-               } else if (q->buf_compat(prev, buf) &&
-                          (prev->fmt == buf->fmt)) {
-                       list_move_tail(&buf->vb.queue, &q->active);
-                       buf->activate(dev, buf, NULL);
-                       prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
-                       dprintk(DBG_BUFF, "%s: [%p/%d] move to active\n",
-                               __func__, buf, buf->vb.i);
-               } else {
-                       dprintk(DBG_BUFF, "%s: no action taken\n", __func__);
-                       return 0;
-               }
-               prev = buf;
-       }
-}
-
-/* nr of (tw68-)pages for the given buffer size */
-static int tw68_buffer_pages(int size)
-{
-       size  = PAGE_ALIGN(size);
-       size += PAGE_SIZE; /* for non-page-aligned buffers */
-       size /= 4096;
-       return size;
-}
-
-/* calc max # of buffers from size (must not exceed the 4MB virtual
- * address space per DMA channel) */
-int tw68_buffer_count(unsigned int size, unsigned int count)
-{
-       unsigned int maxcount;
-
-       maxcount = 1024 / tw68_buffer_pages(size);
-       if (count > maxcount)
-               count = maxcount;
-       return count;
-}
-
-/*
- * tw68_wakeup
- *
- * Called when the driver completes filling a buffer, and tasks waiting
- * for the data need to be awakened.
- */
-void tw68_wakeup(struct tw68_dmaqueue *q, unsigned int *fc)
-{
-       struct tw68_dev *dev = q->dev;
-       struct tw68_buf *buf;
-
-       dprintk(DBG_FLOW, "%s: called\n", __func__);
-       if (list_empty(&q->active)) {
-               dprintk(DBG_BUFF | DBG_TESTING, "%s: active list empty",
-                       __func__);
-               del_timer(&q->timeout);
-               return;
-       }
-       buf = list_entry(q->active.next, struct tw68_buf, vb.queue);
-       do_gettimeofday(&buf->vb.ts);
-       buf->vb.field_count = (*fc)++;
-       dprintk(DBG_BUFF | DBG_TESTING, "%s: [%p/%d] field_count=%d\n",
-               __func__, buf, buf->vb.i, *fc);
-       buf->vb.state = VIDEOBUF_DONE;
-       list_del(&buf->vb.queue);
-       wake_up(&buf->vb.done);
-       mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
-}
-
-/*
- * tw68_buffer_queue
- *
- * Add specified buffer to specified queue
- */
-void tw68_buffer_queue(struct tw68_dev *dev,
-                        struct tw68_dmaqueue *q,
-                        struct tw68_buf *buf)
-{
-       struct tw68_buf    *prev;
-
-       dprintk(DBG_FLOW, "%s: called\n", __func__);
-       assert_spin_locked(&dev->slock);
-       dprintk(DBG_BUFF, "%s: queuing buffer %p\n", __func__, buf);
-
-       /* append a 'JUMP to stopper' to the buffer risc program */
-       buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_INT_BIT);
-       buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
-
-       /* if this buffer is not "compatible" (in dimensions and format)
-        * with the currently active chain of buffers, we must change
-        * settings before filling it; if a previous buffer has already
-        * been determined to require changes, this buffer must follow
-        * it.  To do this, we maintain a "queued" chain.  If that
-        * chain exists, append this buffer to it */
-       if (!list_empty(&q->queued)) {
-               list_add_tail(&buf->vb.queue, &q->queued);
-               buf->vb.state = VIDEOBUF_QUEUED;
-               dprintk(DBG_BUFF, "%s: [%p/%d] appended to queued\n",
-                       __func__, buf, buf->vb.i);
-
-       /* else if the 'active' chain doesn't yet exist we create it now */
-       } else if (list_empty(&q->active)) {
-               dprintk(DBG_BUFF, "%s: [%p/%d] first active\n",
-                       __func__, buf, buf->vb.i);
-               list_add_tail(&buf->vb.queue, &q->active);
-               q->start_dma(dev, q, buf);      /* 1st one - start dma */
-               /* TODO - why have we removed buf->count and q->count? */
-               buf->activate(dev, buf, NULL);
-
-       /* else we would like to put this buffer on the tail of the
-        * active chain, provided it is "compatible". */
-       } else {
-               /* "compatibility" depends upon the type of buffer */
-               prev = list_entry(q->active.prev, struct tw68_buf, vb.queue);
-               if (q->buf_compat(prev, buf)) {
-                       /* If "compatible", append to active chain */
-                       prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
-                       /* the param 'prev' is only for debug printing */
-                       buf->activate(dev, buf, prev);
-                       list_add_tail(&buf->vb.queue, &q->active);
-                       dprintk(DBG_BUFF, "%s: [%p/%d] appended to active\n",
-                               __func__, buf, buf->vb.i);
-               } else {
-                       /* If "incompatible", append to queued chain */
-                       list_add_tail(&buf->vb.queue, &q->queued);
-                       buf->vb.state = VIDEOBUF_QUEUED;
-                       dprintk(DBG_BUFF, "%s: [%p/%d] incompatible - appended "
-                               "to queued\n", __func__, buf, buf->vb.i);
-               }
-       }
-}
-
-/*
- * tw68_buffer_timeout
- *
- * This routine is set as the video_q.timeout.function
- *
- * Log the event, try to reset the h/w.
- * Flag the current buffer as failed, try to start again with next buff
- */
-void tw68_buffer_timeout(unsigned long data)
-{
-       struct tw68_dmaqueue *q = (struct tw68_dmaqueue *)data;
-       struct tw68_dev *dev = q->dev;
-       struct tw68_buf *buf;
-       unsigned long flags;
-
-       dprintk(DBG_FLOW, "%s: called\n", __func__);
-       spin_lock_irqsave(&dev->slock, flags);
-
-       /* flag all current active buffers as failed */
-       while (!list_empty(&q->active)) {
-               buf = list_entry(q->active.next, struct tw68_buf, vb.queue);
-               list_del(&buf->vb.queue);
-               buf->vb.state = VIDEOBUF_ERROR;
-               wake_up(&buf->vb.done);
-               printk(KERN_INFO "%s/0: [%p/%d] timeout - dma=0x%08lx\n",
-                       dev->name, buf, buf->vb.i,
-                       (unsigned long)buf->risc.dma);
-       }
-       tw68_buffer_requeue(dev, q);
-       spin_unlock_irqrestore(&dev->slock, flags);
-}
+struct pci_device_id tw68_pci_tbl[] = {
+       {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6800)},
+       {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6801)},
+       {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6804)},
+       {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_1)},
+       {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_2)},
+       {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_3)},
+       {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_4)},
+       {0,}
+};
 
 /* ------------------------------------------------------------------ */
-/* early init (no i2c, no irq) */
 
-/* Called from tw68_hw_init1 and tw68_resume */
-static int tw68_hw_enable1(struct tw68_dev *dev)
-{
-       return 0;
-}
 
 /*
  * The device is given a "soft reset". According to the specifications,
@@ -367,7 +91,6 @@ static int tw68_hw_enable1(struct tw68_dev *dev)
  */
 static int tw68_hw_init1(struct tw68_dev *dev)
 {
-       dprintk(DBG_FLOW, "%s: called\n", __func__);
        /* Assure all interrupts are disabled */
        tw_writel(TW68_INTMASK, 0);             /* 020 */
        /* Clear any pending interrupts */
@@ -415,7 +138,7 @@ static int tw68_hw_init1(struct tw68_dev *dev)
        tw_writeb(TW68_AGCGAIN, 0xf0);  /* 288  AGC gain when loop disabled */
        tw_writeb(TW68_PEAKWT, 0xd8);   /* 28C  White peak threshold */
        tw_writeb(TW68_CLMPL, 0x3c);    /* 290  Y channel clamp level */
-//     tw_writeb(TW68_SYNCT, 0x38);    /* 294  Sync amplitude */
+/*     tw_writeb(TW68_SYNCT, 0x38);*/  /* 294  Sync amplitude */
        tw_writeb(TW68_SYNCT, 0x30);    /* 294  Sync amplitude */
        tw_writeb(TW68_MISSCNT, 0x44);  /* 298  Horiz sync, VCR detect sens */
        tw_writeb(TW68_PCLAMP, 0x28);   /* 29C  Clamp pos from PLL sync */
@@ -465,80 +188,9 @@ static int tw68_hw_init1(struct tw68_dev *dev)
 
        /* Initialize any subsystems */
        tw68_video_init1(dev);
-       tw68_vbi_init1(dev);
-       if (card_has_mpeg(dev))
-               tw68_ts_init1(dev);
-       tw68_input_init1(dev);
-
-       /* Do any other h/w early initialisation at this point */
-       tw68_hw_enable1(dev);
-
-       return 0;
-}
-
-/* late init (with i2c + irq) */
-static int tw68_hw_enable2(struct tw68_dev *dev)
-{
-
-       dprintk(DBG_FLOW, "%s: called\n", __func__);
-#ifdef TW68_TESTING
-       dev->pci_irqmask |= TW68_I2C_INTS;
-#endif
-       tw_setl(TW68_INTMASK, dev->pci_irqmask);
        return 0;
 }
 
-static int tw68_hw_init2(struct tw68_dev *dev)
-{
-       dprintk(DBG_FLOW, "%s: called\n", __func__);
-       tw68_video_init2(dev);  /* initialise video function first */
-       tw68_tvaudio_init2(dev);/* audio next */
-
-       /* all other board-related things, incl. enabling interrupts */
-       tw68_hw_enable2(dev);
-       return 0;
-}
-
-/* shutdown */
-static int tw68_hwfini(struct tw68_dev *dev)
-{
-       dprintk(DBG_FLOW, "%s: called\n", __func__);
-       if (card_has_mpeg(dev))
-               tw68_ts_fini(dev);
-       tw68_input_fini(dev);
-       tw68_vbi_fini(dev);
-       tw68_tvaudio_fini(dev);
-       return 0;
-}
-
-static void __devinit must_configure_manually(void)
-{
-       unsigned int i, p;
-
-       printk(KERN_WARNING
-              "tw68: <rant>\n"
-              "tw68:  Congratulations!  Your TV card vendor saved a few\n"
-              "tw68:  cents for a eeprom, thus your pci board has no\n"
-              "tw68:  subsystem ID and I can't identify it automatically\n"
-              "tw68: </rant>\n"
-              "tw68: I feel better now.  Ok, here is the good news:\n"
-              "tw68: You can use the card=<nr> insmod option to specify\n"
-              "tw68: which board you have.  The list:\n");
-       for (i = 0; i < tw68_bcount; i++) {
-               printk(KERN_WARNING "tw68:   card=%d -> %-40.40s",
-                      i, tw68_boards[i].name);
-               for (p = 0; tw68_pci_tbl[p].driver_data; p++) {
-                       if (tw68_pci_tbl[p].driver_data != i)
-                               continue;
-                       printk(" %04x:%04x",
-                              tw68_pci_tbl[p].subvendor,
-                              tw68_pci_tbl[p].subdevice);
-               }
-               printk("\n");
-       }
-}
-
-
 static irqreturn_t tw68_irq(int irq, void *dev_id)
 {
        struct tw68_dev *dev = dev_id;
@@ -548,126 +200,39 @@ static irqreturn_t tw68_irq(int irq, void *dev_id)
        status = orig = tw_readl(TW68_INTSTAT) & dev->pci_irqmask;
        /* Check if anything to do */
        if (0 == status)
-               return IRQ_RETVAL(0);   /* Nope - return */
+               return IRQ_NONE;        /* Nope - return */
        for (loop = 0; loop < 10; loop++) {
                if (status & dev->board_virqmask)       /* video interrupt */
                        tw68_irq_video_done(dev, status);
-#ifdef TW68_TESTING
-               if (status & TW68_I2C_INTS)
-                       tw68_irq_i2c(dev, status);
-#endif
                status = tw_readl(TW68_INTSTAT) & dev->pci_irqmask;
                if (0 == status)
-                       goto out;
+                       return IRQ_HANDLED;
        }
-       dprintk(DBG_UNEXPECTED, "%s: **** INTERRUPT NOT HANDLED - clearing mask"
-                               " (orig 0x%08x, cur 0x%08x)",
-                               dev->name, orig, tw_readl(TW68_INTSTAT));
-       dprintk(DBG_UNEXPECTED, "%s: pci_irqmask 0x%08x; board_virqmask "
-                               "0x%08x ****\n", dev->name,
-                               dev->pci_irqmask, dev->board_virqmask);
+       dev_dbg(&dev->pci->dev, "%s: **** INTERRUPT NOT HANDLED - clearing mask (orig 0x%08x, cur 0x%08x)",
+                       dev->name, orig, tw_readl(TW68_INTSTAT));
+       dev_dbg(&dev->pci->dev, "%s: pci_irqmask 0x%08x; board_virqmask 0x%08x ****\n",
+                       dev->name, dev->pci_irqmask, dev->board_virqmask);
        tw_clearl(TW68_INTMASK, dev->pci_irqmask);
-out:
-       return IRQ_RETVAL(1);
-}
-
-int tw68_set_dmabits(struct tw68_dev *dev)
-{
-       return 0;
-}
-
-static struct video_device *vdev_init(struct tw68_dev *dev,
-                                     struct video_device *template,
-                                     char *type)
-{
-       struct video_device *vfd;
-
-       dprintk(DBG_FLOW, "%s: called\n", __func__);
-       vfd = video_device_alloc();
-       if (NULL == vfd)
-               return NULL;
-       *vfd = *template;
-       vfd->minor   = -1;
-       vfd->parent  = &dev->pci->dev;
-       vfd->release = video_device_release;
-       /* vfd->debug   = tw_video_debug; */
-       snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
-                dev->name, type, tw68_boards[dev->board].name);
-       return vfd;
+       return IRQ_HANDLED;
 }
 
-static void tw68_unregister_video(struct tw68_dev *dev)
-{
-
-       dprintk(DBG_FLOW, "%s: called\n", __func__);
-       if (dev->video_dev) {
-               if (-1 != dev->video_dev->minor)
-                       video_unregister_device(dev->video_dev);
-               else
-                       video_device_release(dev->video_dev);
-               dev->video_dev = NULL;
-       }
-       if (dev->vbi_dev) {
-               if (-1 != dev->vbi_dev->minor)
-                       video_unregister_device(dev->vbi_dev);
-               else
-                       video_device_release(dev->vbi_dev);
-               dev->vbi_dev = NULL;
-       }
-       if (dev->radio_dev) {
-               if (-1 != dev->radio_dev->minor)
-                       video_unregister_device(dev->radio_dev);
-               else
-                       video_device_release(dev->radio_dev);
-               dev->radio_dev = NULL;
-       }
-}
-
-static void mpeg_ops_attach(struct tw68_mpeg_ops *ops,
-                           struct tw68_dev *dev)
-{
-       int err;
-
-       dprintk(DBG_FLOW, "%s: called\n", __func__);
-       if (NULL != dev->mops)
-               return;
-       if (tw68_boards[dev->board].mpeg != ops->type)
-               return;
-       err = ops->init(dev);
-       if (0 != err)
-               return;
-       dev->mops = ops;
-}
-
-static void mpeg_ops_detach(struct tw68_mpeg_ops *ops,
-                           struct tw68_dev *dev)
-{
-
-       if (NULL == dev->mops)
-               return;
-       if (dev->mops != ops)
-               return;
-       dev->mops->fini(dev);
-       dev->mops = NULL;
-}
-
-static int __devinit tw68_initdev(struct pci_dev *pci_dev,
+static int tw68_initdev(struct pci_dev *pci_dev,
                                     const struct pci_device_id *pci_id)
 {
        struct tw68_dev *dev;
-       struct tw68_mpeg_ops *mops;
+       int vidnr = -1;
        int err;
 
-       if (tw68_devcount == TW68_MAXBOARDS)
-               return -ENOMEM;
-
-       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       dev = devm_kzalloc(&pci_dev->dev, sizeof(*dev), GFP_KERNEL);
        if (NULL == dev)
                return -ENOMEM;
 
+       dev->instance = v4l2_device_set_name(&dev->v4l2_dev, "tw68",
+                                               &tw68_instance);
+
        err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev);
        if (err)
-               goto fail0;
+               return err;
 
        /* pci init */
        dev->pci = pci_dev;
@@ -676,33 +241,10 @@ static int __devinit tw68_initdev(struct pci_dev *pci_dev,
                goto fail1;
        }
 
-       dev->nr = tw68_devcount;
-       sprintf(dev->name, "tw%x[%d]", pci_dev->device, dev->nr);
+       dev->name = dev->v4l2_dev.name;
 
-       /* pci quirks */
-       if (pci_pci_problems) {
-               if (pci_pci_problems & PCIPCI_TRITON)
-                       printk(KERN_INFO "%s: quirk: PCIPCI_TRITON\n",
-                               dev->name);
-               if (pci_pci_problems & PCIPCI_NATOMA)
-                       printk(KERN_INFO "%s: quirk: PCIPCI_NATOMA\n",
-                               dev->name);
-               if (pci_pci_problems & PCIPCI_VIAETBF)
-                       printk(KERN_INFO "%s: quirk: PCIPCI_VIAETBF\n",
-                               dev->name);
-               if (pci_pci_problems & PCIPCI_VSFX)
-                       printk(KERN_INFO "%s: quirk: PCIPCI_VSFX\n",
-                               dev->name);
-#ifdef PCIPCI_ALIMAGIK
-               if (pci_pci_problems & PCIPCI_ALIMAGIK) {
-                       printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK "
-                               "-- latency fixup\n", dev->name);
-                       latency = 0x0A;
-               }
-#endif
-       }
        if (UNSET != latency) {
-               printk(KERN_INFO "%s: setting pci latency timer to %d\n",
+               pr_info("%s: setting pci latency timer to %d\n",
                       dev->name, latency);
                pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency);
        }
@@ -710,13 +252,12 @@ static int __devinit tw68_initdev(struct pci_dev *pci_dev,
        /* print pci info */
        pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev);
        pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER,  &dev->pci_lat);
-       printk(KERN_INFO "%s: found at %s, rev: %d, irq: %d, "
-              "latency: %d, mmio: 0x%llx\n", dev->name,
-              pci_name(pci_dev), dev->pci_rev, pci_dev->irq, dev->pci_lat,
-              (unsigned long long)pci_resource_start(pci_dev, 0));
+       pr_info("%s: found at %s, rev: %d, irq: %d, latency: %d, mmio: 0x%llx\n",
+               dev->name, pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
+               dev->pci_lat, (u64)pci_resource_start(pci_dev, 0));
        pci_set_master(pci_dev);
        if (!pci_dma_supported(pci_dev, DMA_BIT_MASK(32))) {
-               printk("%s: Oops: no 32bit PCI DMA ???\n", dev->name);
+               pr_info("%s: Oops: no 32bit PCI DMA ???\n", dev->name);
                err = -EIO;
                goto fail1;
        }
@@ -730,7 +271,7 @@ static int __devinit tw68_initdev(struct pci_dev *pci_dev,
                dev->vdecoder = TW6801;
                dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX;
                break;
-       case PCI_DEVICE_ID_6804:        /* Video decoder for TW6805 */
+       case PCI_DEVICE_ID_6804:        /* Video decoder for TW6804 */
                dev->vdecoder = TW6804;
                dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX;
                break;
@@ -739,35 +280,13 @@ static int __devinit tw68_initdev(struct pci_dev *pci_dev,
                dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX;
                break;
        }
-       /* board config */
-       dev->board = pci_id->driver_data;
-       if (card[dev->nr] >= 0 &&
-           card[dev->nr] < tw68_bcount)
-               dev->board = card[dev->nr];
-       if (TW68_BOARD_NOAUTO == dev->board) {
-               must_configure_manually();
-               dev->board = TW68_BOARD_UNKNOWN;
-       }
-       dev->autodetected = card[dev->nr] != dev->board;
-       dev->tuner_type = tw68_boards[dev->board].tuner_type;
-       dev->tuner_addr = tw68_boards[dev->board].tuner_addr;
-       dev->radio_type = tw68_boards[dev->board].radio_type;
-       dev->radio_addr = tw68_boards[dev->board].radio_addr;
-       dev->tda9887_conf = tw68_boards[dev->board].tda9887_conf;
-       if (UNSET != tuner[dev->nr])
-               dev->tuner_type = tuner[dev->nr];
-       printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
-               dev->name, pci_dev->subsystem_vendor,
-               pci_dev->subsystem_device, tw68_boards[dev->board].name,
-               dev->board, dev->autodetected ?
-               "autodetected" : "insmod option");
 
        /* get mmio */
        if (!request_mem_region(pci_resource_start(pci_dev, 0),
                                pci_resource_len(pci_dev, 0),
                                dev->name)) {
                err = -EBUSY;
-               printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx\n",
+               pr_err("%s: can't get MMIO memory @ 0x%llx\n",
                        dev->name,
                        (unsigned long long)pci_resource_start(pci_dev, 0));
                goto fail1;
@@ -777,185 +296,75 @@ static int __devinit tw68_initdev(struct pci_dev *pci_dev,
        dev->bmmio = (__u8 __iomem *)dev->lmmio;
        if (NULL == dev->lmmio) {
                err = -EIO;
-               printk(KERN_ERR "%s: can't ioremap() MMIO memory\n",
+               pr_err("%s: can't ioremap() MMIO memory\n",
                       dev->name);
                goto fail2;
        }
        /* initialize hardware #1 */
-       /* First, take care of anything unique to a particular card */
-       tw68_board_init1(dev);
        /* Then do any initialisation wanted before interrupts are on */
        tw68_hw_init1(dev);
 
        /* get irq */
-       err = request_irq(pci_dev->irq, tw68_irq,
+       err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw68_irq,
                          IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
        if (err < 0) {
-               printk(KERN_ERR "%s: can't get IRQ %d\n",
+               pr_err("%s: can't get IRQ %d\n",
                       dev->name, pci_dev->irq);
                goto fail3;
        }
 
-#ifdef TW68_TESTING
-       dev->pci_irqmask |= TW68_SBDONE;
-       tw_setl(TW68_INTMASK, dev->pci_irqmask);
-       printk(KERN_INFO "Calling tw68_i2c_register\n");
-       /* Register the i2c bus */
-       tw68_i2c_register(dev);
-#endif
-
        /*
         *  Now do remainder of initialisation, first for
         *  things unique for this card, then for general board
         */
-       tw68_board_init2(dev);
-
-       tw68_hw_init2(dev);
-
-#if 0
-       /* load i2c helpers */
-       if (card_is_empress(dev)) {
-               struct v4l2_subdev *sd =
-                       v4l2_i2c_new_subdev(&dev->i2c_adap, "saa6752hs",
-                               "saa6752hs", 0x20);
-
-               if (sd)
-                       sd->grp_id = GRP_EMPRESS;
-       }
-
-       request_submodules(dev);
-#endif
-
-       v4l2_prio_init(&dev->prio);
-
-       mutex_lock(&tw68_devlist_lock);
-       list_for_each_entry(mops, &mops_list, next)
-               mpeg_ops_attach(mops, dev);
-       list_add_tail(&dev->devlist, &tw68_devlist);
-       mutex_unlock(&tw68_devlist_lock);
-
-       /* check for signal */
-       tw68_irq_video_signalchange(dev);
-
-#if 0
-       if (TUNER_ABSENT != dev->tuner_type)
-               tw_call_all(dev, core, s_standby, 0);
-#endif
-
-       dev->video_dev = vdev_init(dev, &tw68_video_template, "video");
-       err = video_register_device(dev->video_dev, VFL_TYPE_GRABBER,
-                                   video_nr[dev->nr]);
+       if (dev->instance < TW68_MAXBOARDS)
+               vidnr = video_nr[dev->instance];
+       /* initialise video function first */
+       err = tw68_video_init2(dev, vidnr);
        if (err < 0) {
-               printk(KERN_INFO "%s: can't register video device\n",
+               pr_err("%s: can't register video device\n",
                       dev->name);
                goto fail4;
        }
-       printk(KERN_INFO "%s: registered device video%d [v4l2]\n",
-              dev->name, dev->video_dev->num);
-
-       dev->vbi_dev = vdev_init(dev, &tw68_video_template, "vbi");
-
-       err = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
-                                   vbi_nr[dev->nr]);
-       if (err < 0) {
-               printk(KERN_INFO "%s: can't register vbi device\n",
-                       dev->name);
-               goto fail4;
-       }
-       printk(KERN_INFO "%s: registered device vbi%d\n",
-              dev->name, dev->vbi_dev->num);
-
-       if (card_has_radio(dev)) {
-               dev->radio_dev = vdev_init(dev, &tw68_radio_template,
-                                          "radio");
-               err = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
-                                           radio_nr[dev->nr]);
-               if (err < 0) {
-                       /* TODO - need to unregister vbi? */
-                       printk(KERN_INFO "%s: can't register radio device\n",
-                               dev->name);
-                       goto fail4;
-               }
-               printk(KERN_INFO "%s: registered device radio%d\n",
-                      dev->name, dev->radio_dev->num);
-       }
-
-       /* everything worked */
-       tw68_devcount++;
+       tw_setl(TW68_INTMASK, dev->pci_irqmask);
 
-       if (tw68_dmasound_init && !dev->dmasound.priv_data)
-               tw68_dmasound_init(dev);
+       pr_info("%s: registered device %s\n",
+              dev->name, video_device_node_name(&dev->vdev));
 
        return 0;
 
- fail4:
-       tw68_unregister_video(dev);
-#ifdef TW68_TESTING
-       tw68_i2c_unregister(dev);
-#endif
-       free_irq(pci_dev->irq, dev);
- fail3:
-       tw68_hwfini(dev);
+fail4:
+       video_unregister_device(&dev->vdev);
+fail3:
        iounmap(dev->lmmio);
- fail2:
+fail2:
        release_mem_region(pci_resource_start(pci_dev, 0),
                           pci_resource_len(pci_dev, 0));
- fail1:
+fail1:
        v4l2_device_unregister(&dev->v4l2_dev);
- fail0:
-       kfree(dev);
        return err;
 }
 
-static void __devexit tw68_finidev(struct pci_dev *pci_dev)
+static void tw68_finidev(struct pci_dev *pci_dev)
 {
        struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
        struct tw68_dev *dev =
                container_of(v4l2_dev, struct tw68_dev, v4l2_dev);
-       struct tw68_mpeg_ops *mops;
-
-       dprintk(DBG_FLOW, "%s: called\n", __func__);
-       /* Release DMA sound modules if present */
-       if (tw68_dmasound_exit && dev->dmasound.priv_data)
-               tw68_dmasound_exit(dev);
 
        /* shutdown subsystems */
-       tw68_hwfini(dev);
        tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN);
        tw_writel(TW68_INTMASK, 0);
 
        /* unregister */
-       mutex_lock(&tw68_devlist_lock);
-       list_del(&dev->devlist);
-       list_for_each_entry(mops, &mops_list, next)
-               mpeg_ops_detach(mops, dev);
-       mutex_unlock(&tw68_devlist_lock);
-       tw68_devcount--;
-
-#ifdef TW68_TESTING
-       tw68_i2c_unregister(dev);
-#endif
-       tw68_unregister_video(dev);
-
-
-       /* the DMA sound modules should be unloaded before reaching
-          this, but just in case they are still present... */
-       if (dev->dmasound.priv_data != NULL) {
-               free_irq(pci_dev->irq, &dev->dmasound);
-               dev->dmasound.priv_data = NULL;
-       }
-
+       video_unregister_device(&dev->vdev);
+       v4l2_ctrl_handler_free(&dev->hdl);
 
        /* release resources */
-       free_irq(pci_dev->irq, dev);
        iounmap(dev->lmmio);
        release_mem_region(pci_resource_start(pci_dev, 0),
                           pci_resource_len(pci_dev, 0));
 
        v4l2_device_unregister(&dev->v4l2_dev);
-
-       /* free memory */
-       kfree(dev);
 }
 
 #ifdef CONFIG_PM
@@ -966,28 +375,15 @@ static int tw68_suspend(struct pci_dev *pci_dev , pm_message_t state)
        struct tw68_dev *dev = container_of(v4l2_dev,
                                struct tw68_dev, v4l2_dev);
 
-       dprintk(DBG_FLOW, "%s: called\n", __func__);
        tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN);
        dev->pci_irqmask &= ~TW68_VID_INTS;
        tw_writel(TW68_INTMASK, 0);
 
-       dev->insuspend = 1;
        synchronize_irq(pci_dev->irq);
 
-       /* Disable timeout timers - if we have active buffers, we will
-          fill them on resume*/
-
-       del_timer(&dev->video_q.timeout);
-       del_timer(&dev->vbi_q.timeout);
-       del_timer(&dev->ts_q.timeout);
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
-   if (dev->remote)
-               tw68_ir_stop(dev);
-#endif
-
        pci_save_state(pci_dev);
        pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state));
+       vb2_discard_done(&dev->vidq);
 
        return 0;
 }
@@ -997,54 +393,25 @@ static int tw68_resume(struct pci_dev *pci_dev)
        struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
        struct tw68_dev *dev = container_of(v4l2_dev,
                                            struct tw68_dev, v4l2_dev);
+       struct tw68_buf *buf;
        unsigned long flags;
 
-       dprintk(DBG_FLOW, "%s: called\n", __func__);
        pci_set_power_state(pci_dev, PCI_D0);
        pci_restore_state(pci_dev);
 
        /* Do things that are done in tw68_initdev ,
                except of initializing memory structures.*/
 
-       tw68_board_init1(dev);
-
-       /* tw68_hw_init1 */
-       if (tw68_boards[dev->board].video_out)
-               tw68_videoport_init(dev);
-       if (card_has_mpeg(dev))
-               tw68_ts_init_hw(dev);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
-       if (dev->remote)
-               tw68_ir_start(dev, dev->remote);
-#endif
-       tw68_hw_enable1(dev);
-
        msleep(100);
 
-       tw68_board_init2(dev);
-
-       /*tw68_hw_init2*/
        tw68_set_tvnorm_hw(dev);
-       tw68_tvaudio_setmute(dev);
-/*     tw68_tvaudio_setvolume(dev, dev->ctl_volume); */
-       tw68_tvaudio_init(dev);
-       tw68_irq_video_signalchange(dev);
 
        /*resume unfinished buffer(s)*/
        spin_lock_irqsave(&dev->slock, flags);
-       tw68_buffer_requeue(dev, &dev->video_q);
-       tw68_buffer_requeue(dev, &dev->vbi_q);
-       tw68_buffer_requeue(dev, &dev->ts_q);
-
-       /* FIXME: Disable DMA audio sound - temporary till proper support
-                 is implemented*/
+       buf = container_of(dev->active.next, struct tw68_buf, list);
 
-       dev->dmasound.dma_running = 0;
+       tw68_video_start_dma(dev, buf);
 
-       /* start DMA now*/
-       dev->insuspend = 0;
-       smp_wmb();
-       tw68_set_dmabits(dev);
        spin_unlock_irqrestore(&dev->slock, flags);
 
        return 0;
@@ -1057,35 +424,11 @@ static struct pci_driver tw68_pci_driver = {
        .name     = "tw68",
        .id_table = tw68_pci_tbl,
        .probe    = tw68_initdev,
-       .remove   = __devexit_p(tw68_finidev),
+       .remove   = tw68_finidev,
 #ifdef CONFIG_PM
        .suspend  = tw68_suspend,
        .resume   = tw68_resume
 #endif
 };
 
-static int tw68_init(void)
-{
-       if (core_debug & DBG_FLOW)
-               printk(KERN_DEBUG "%s: called\n", __func__);
-       INIT_LIST_HEAD(&tw68_devlist);
-       printk(KERN_INFO "tw68: v4l2 driver version %d.%d.%d loaded\n",
-               (TW68_VERSION_CODE >> 16) & 0xff,
-               (TW68_VERSION_CODE >> 8) & 0xff,
-               TW68_VERSION_CODE & 0xff);
-#if 0
-       printk(KERN_INFO "tw68: snapshot date %04d-%02d-%02d\n",
-               SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
-#endif
-       return pci_register_driver(&tw68_pci_driver);
-}
-
-static void module_cleanup(void)
-{
-       if (core_debug & DBG_FLOW)
-               printk(KERN_DEBUG "%s: called\n", __func__);
-       pci_unregister_driver(&tw68_pci_driver);
-}
-
-module_init(tw68_init);
-module_exit(module_cleanup);
+module_pci_driver(tw68_pci_driver);
diff --git a/drivers/media/pci/tw68/tw68-i2c.c b/drivers/media/pci/tw68/tw68-i2c.c
deleted file mode 100644 (file)
index 38659d0..0000000
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- *  tw68 code to handle the i2c interface.
- *
- *  Much of this code is derived from the bt87x driver.  The original
- *  work was by Gerd Knorr; more recently the code was enhanced by Mauro
- *  Carvalho Chehab.  Their work is gratefully acknowledged.  Full credit
- *  goes to them - any problems within this code are mine.
- *
- *  Copyright (C) 2009  William M. Brack <wbrack@mmm.com.hk>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-
-#include "tw68.h"
-#include <media/v4l2-common.h>
-#include <linux/i2c-algo-bit.h>
-
-/*----------------------------------------------------------------*/
-
-static unsigned int i2c_debug;
-module_param(i2c_debug, int, 0644);
-MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
-
-#if 0
-static unsigned int i2c_scan;
-module_param(i2c_scan, int, 0444);
-MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
-#endif
-
-#define d1printk if (1 == i2c_debug) printk
-
-#define        I2C_CLOCK       0xa6    /* 99.4 kHz */
-
-/*----------------------------------------------------------------------*/
-/* Although the TW68xx i2c controller has a "hardware" mode, where all of
- * the low-level i2c/smbbus is handled by the chip, it appears that mode
- * is not suitable for linux i2c handling routines because extended "bursts"
- * of data (sequences of bytes without intervening START/STOP bits) are
- * not possible.  Instead, we put the chip into "software" mode, and handle
- * the i2c bus at a low level.  To accomplish this, we use the routines
- * from the i2c modules.
- *
- * Because the particular boards which I had for testing did not have any
- * devices attached to the i2c bus, I have been unable to test these
- * routines.
- */
-
-/*----------------------------------------------------------------------*/
-/* I2C functions - "bit-banging" adapter (software i2c)                */
-
-/* tw68_bit_setcl
- * Handles "toggling" the i2c clock bit
- */
-static void tw68_bit_setscl(void *data, int state)
-{
-       struct tw68_dev *dev = data;
-
-       tw_andorb(TW68_SBUSC, (state ? 1 : 0) << TW68_SSCLK, TW68_SSCLK_B);
-}
-
-/* tw68_bit_setsda
- * Handles "toggling" the i2c data bit
- */
-static void tw68_bit_setsda(void *data, int state)
-{
-       struct tw68_dev *dev = data;
-
-       tw_andorb(TW68_SBUSC, (state ? 1 : 0) << TW68_SSDAT, TW68_SSDAT_B);
-}
-
-/* tw68_bit_getscl
- *
- * Returns the current state of the clock bit
- */
-static int tw68_bit_getscl(void *data)
-{
-       struct tw68_dev *dev = data;
-
-       return (tw_readb(TW68_SBUSC) & TW68_SSCLK_B) ? 1 : 0;
-}
-
-/* tw68_bit_getsda
- *
- * Returns the current state of the data bit
- */
-static int tw68_bit_getsda(void *data)
-{
-       struct tw68_dev *dev = data;
-
-       return (tw_readb(TW68_SBUSC) & TW68_SSDAT_B) ? 1 : 0;
-}
-
-static struct i2c_algo_bit_data __devinitdata tw68_i2c_algo_bit_template = {
-       .setsda  = tw68_bit_setsda,
-       .setscl  = tw68_bit_setscl,
-       .getsda  = tw68_bit_getsda,
-       .getscl  = tw68_bit_getscl,
-       .udelay  = 16,
-       .timeout = 200,
-};
-
-static struct i2c_client tw68_client_template = {
-       .name           = "tw68 internal",
-};
-
-/*----------------------------------------------------------------*/
-
-static int attach_inform(struct i2c_client *client)
-{
-/*     struct tw68_dev *dev = client->adapter->algo_data; */
-
-       d1printk("%s i2c attach [addr=0x%x,client=%s]\n",
-               client->driver->driver.name, client->addr, client->name);
-
-       switch (client->addr) {
-               /* No info yet on what addresses to expect */
-       }
-
-       return 0;
-}
-
-static struct i2c_adapter tw68_adap_sw_template = {
-       .owner          = THIS_MODULE,
-       .name           = "tw68_sw",
-       .client_register = attach_inform,
-};
-
-static int tw68_i2c_eeprom(struct tw68_dev *dev, unsigned char *eedata,
-                          int len)
-{
-       unsigned char buf;
-       int i, err;
-
-       dev->i2c_client.addr = 0xa0 >> 1;
-       buf = 256 - len;
-
-       err = i2c_master_send(&dev->i2c_client, &buf, 1);
-       if (1 != err) {
-               printk(KERN_INFO "%s: Huh, no eeprom present (err = %d)?\n",
-                       dev->name, err);
-               return -1;
-       }
-       err = i2c_master_recv(&dev->i2c_client, eedata, len);
-       if (len != err) {
-               printk(KERN_WARNING "%s: i2c eeprom read error (err=%d)\n",
-                       dev->name, err);
-               return -1;
-       }
-
-       for (i = 0; i < len; i++) {
-               if (0 == (i % 16))
-                       printk(KERN_INFO "%s: i2c eeprom %02x:",
-                               dev->name, i);
-               printk(KERN_INFO " %02x", eedata[i]);
-               if (15 == (i % 16))
-                       printk("\n");
-       }
-       return 0;
-}
-
-#if 0
-static char *i2c_devs[128] = {
-       [0xa0 >> 1] = "eeprom",
-};
-
-static void do_i2c_scan(char *name, struct i2c_client *c)
-{
-       unsigned char buf;
-       int i, rc;
-
-       for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
-               c->addr = i;
-               rc = i2c_master_recv(c, &buf, 1);
-               if (rc < 0)
-                       continue;
-               printk(KERN_INFO "%s: i2c scan: found device "
-                                "@ 0x%x [%s]\n", name, i << 1,
-                                i2c_devs[i] ? i2c_devs[i] : "???");
-       }
-}
-#endif
-
-int __devinit tw68_i2c_register(struct tw68_dev *dev)
-{
-       int rc;
-
-printk(KERN_DEBUG "%s: Registering i2c module\n", __func__);
-       tw_writeb(TW68_I2C_RST, 1);     /* reset the i2c module */
-
-       memcpy(&dev->i2c_client, &tw68_client_template,
-               sizeof(tw68_client_template));
-
-       memcpy(&dev->i2c_adap, &tw68_adap_sw_template,
-               sizeof(tw68_adap_sw_template));
-       dev->i2c_adap.algo_data = &dev->i2c_algo;
-       dev->i2c_adap.dev.parent = &dev->pci->dev;
-
-       memcpy(&dev->i2c_algo, &tw68_i2c_algo_bit_template,
-               sizeof(tw68_i2c_algo_bit_template));
-       dev->i2c_algo.data = dev;
-       /* TODO - may want to set better name (see bttv code) */
-
-       i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
-       dev->i2c_client.adapter = &dev->i2c_adap;
-
-       /* Assure chip is in "software" mode */
-       tw_writel(TW68_SBUSC, TW68_SSDAT | TW68_SSCLK);
-       tw68_bit_setscl(dev, 1);
-       tw68_bit_setsda(dev, 1);
-
-       rc = i2c_bit_add_bus(&dev->i2c_adap);
-
-       tw68_i2c_eeprom(dev, dev->eedata, sizeof(dev->eedata));
-#if 0
-       if (i2c_scan)
-               do_i2c_scan(dev->name, &dev->i2c_client);
-#endif
-
-       return rc;
-}
-
-int tw68_i2c_unregister(struct tw68_dev *dev)
-{
-       i2c_del_adapter(&dev->i2c_adap);
-       return 0;
-}
index 314bc43cd9d3ee7ad14e9420fb6178f20138d0db..f60b3a896fa7589e8b6229812d3bb932fcd67642 100644 (file)
@@ -8,7 +8,11 @@
  *  acknowledged.  Full credit goes to them - any problems within this code
  *  are mine.
  *
- *  Copyright (C) William M. Brack <wbrack@mmm.com.hk>
+ *  Copyright (C) William M. Brack
+ *
+ *  Refactored and updated to the latest v4l core frameworks:
+ *
+ *  Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 #ifndef _TW68_REG_H_
index 66273bbd51c5339618306b59556c1fe9e9404b6a..7439db212a69d2f401eacd5586505f59b85657b2 100644 (file)
@@ -9,7 +9,11 @@
  *  acknowledged.  Full credit goes to them - any problems within this code
  *  are mine.
  *
- *  Copyright (C) 2009  William M. Brack <wbrack@mmm.com.hk>
+ *  Copyright (C) 2009  William M. Brack
+ *
+ *  Refactored and updated to the latest v4l core frameworks:
+ *
+ *  Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
 #include "tw68.h"
 
-#define NO_SYNC_LINE (-1U)
-
 /**
  *  @rp                pointer to current risc program position
  *  @sglist    pointer to "scatter-gather list" of buffer pointers
  *  @bpl       number of bytes per scan line
  *  @padding   number of bytes of padding to add
  *  @lines     number of lines in field
- *  @lpi       lines per IRQ, or 0 to not generate irqs
- *             Note: IRQ to be generated _after_ lpi lines are transferred
+ *  @jump      insert a jump at the start
  */
 static __le32 *tw68_risc_field(__le32 *rp, struct scatterlist *sglist,
                            unsigned int offset, u32 sync_line,
                            unsigned int bpl, unsigned int padding,
-                           unsigned int lines, unsigned int lpi)
+                           unsigned int lines, bool jump)
 {
        struct scatterlist *sg;
        unsigned int line, todo, done;
 
-       /* sync instruction */
-       if (sync_line != NO_SYNC_LINE) {
-               if (sync_line == 1)
-                       *(rp++) = cpu_to_le32(RISC_SYNCO);
-               else
-                       *(rp++) = cpu_to_le32(RISC_SYNCE);
+       if (jump) {
+               *(rp++) = cpu_to_le32(RISC_JUMP);
                *(rp++) = 0;
        }
+
+       /* sync instruction */
+       if (sync_line == 1)
+               *(rp++) = cpu_to_le32(RISC_SYNCO);
+       else
+               *(rp++) = cpu_to_le32(RISC_SYNCE);
+       *(rp++) = 0;
+
        /* scan lines */
        sg = sglist;
        for (line = 0; line < lines; line++) {
                /* calculate next starting position */
                while (offset && offset >= sg_dma_len(sg)) {
                        offset -= sg_dma_len(sg);
-                       sg++;
+                       sg = sg_next(sg);
                }
                if (bpl <= sg_dma_len(sg) - offset) {
                        /* fits into current chunk */
@@ -86,7 +87,7 @@ static __le32 *tw68_risc_field(__le32 *rp, struct scatterlist *sglist,
                                                done);
                        *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
                        todo -= done;
-                       sg++;
+                       sg = sg_next(sg);
                        /* succeeding fragments have no offset */
                        while (todo > sg_dma_len(sg)) {
                                *(rp++) = cpu_to_le32(RISC_INLINE |
@@ -94,7 +95,7 @@ static __le32 *tw68_risc_field(__le32 *rp, struct scatterlist *sglist,
                                                sg_dma_len(sg));
                                *(rp++) = cpu_to_le32(sg_dma_address(sg));
                                todo -= sg_dma_len(sg);
-                               sg++;
+                               sg = sg_next(sg);
                                done += sg_dma_len(sg);
                        }
                        if (todo) {
@@ -107,9 +108,6 @@ static __le32 *tw68_risc_field(__le32 *rp, struct scatterlist *sglist,
                        offset = todo;
                }
                offset += padding;
-               /* If this line needs an interrupt, put it in */
-               if (lpi && line > 0 && !(line % lpi))
-                       *(rp-2) |= RISC_INT_BIT;
        }
 
        return rp;
@@ -118,25 +116,25 @@ static __le32 *tw68_risc_field(__le32 *rp, struct scatterlist *sglist,
 /**
  * tw68_risc_buffer
  *
- *     This routine is called by tw68-video.  It allocates
- *     memory for the dma controller "program" and then fills in that
- *     memory with the appropriate "instructions".
+ *     This routine is called by tw68-video.  It allocates
+ *     memory for the dma controller "program" and then fills in that
+ *     memory with the appropriate "instructions".
  *
- *     @pci_dev        structure with info about the pci
- *                     slot which our device is in.
- *     @risc           structure with info about the memory
- *                     used for our controller program.
- *     @sglist         scatter-gather list entry
- *     @top_offset     offset within the risc program area for the
- *                     first odd frame line
- *     @bottom_offset  offset within the risc program area for the
- *                     first even frame line
- *     @bpl            number of data bytes per scan line
- *     @padding        number of extra bytes to add at end of line
- *     @lines          number of scan lines
+ *     @pci_dev        structure with info about the pci
+ *                     slot which our device is in.
+ *     @risc           structure with info about the memory
+ *                     used for our controller program.
+ *     @sglist         scatter-gather list entry
+ *     @top_offset     offset within the risc program area for the
+ *                     first odd frame line
+ *     @bottom_offset  offset within the risc program area for the
+ *                     first even frame line
+ *     @bpl            number of data bytes per scan line
+ *     @padding        number of extra bytes to add at end of line
+ *     @lines          number of scan lines
  */
 int tw68_risc_buffer(struct pci_dev *pci,
-                       struct btcx_riscmem *risc,
+                       struct tw68_buf *buf,
                        struct scatterlist *sglist,
                        unsigned int top_offset,
                        unsigned int bottom_offset,
@@ -146,7 +144,6 @@ int tw68_risc_buffer(struct pci_dev *pci,
 {
        u32 instructions, fields;
        __le32 *rp;
-       int rc;
 
        fields = 0;
        if (UNSET != top_offset)
@@ -155,29 +152,31 @@ int tw68_risc_buffer(struct pci_dev *pci,
                fields++;
        /*
         * estimate risc mem: worst case is one write per page border +
-        * one write per scan line + syncs + jump (all 2 dwords).
+        * one write per scan line + syncs + 2 jumps (all 2 dwords).
         * Padding can cause next bpl to start close to a page border.
         * First DMA region may be smaller than PAGE_SIZE
         */
        instructions  = fields * (1 + (((bpl + padding) * lines) /
-                        PAGE_SIZE) + lines) + 2;
-       rc = btcx_riscmem_alloc(pci, risc, instructions * 8);
-       if (rc < 0)
-               return rc;
+                        PAGE_SIZE) + lines) + 4;
+       buf->size = instructions * 8;
+       buf->cpu = pci_alloc_consistent(pci, buf->size, &buf->dma);
+       if (buf->cpu == NULL)
+               return -ENOMEM;
 
        /* write risc instructions */
-       rp = risc->cpu;
+       rp = buf->cpu;
        if (UNSET != top_offset)        /* generates SYNCO */
                rp = tw68_risc_field(rp, sglist, top_offset, 1,
-                                    bpl, padding, lines, 0);
+                                    bpl, padding, lines, true);
        if (UNSET != bottom_offset)     /* generates SYNCE */
                rp = tw68_risc_field(rp, sglist, bottom_offset, 2,
-                                    bpl, padding, lines, 0);
+                                    bpl, padding, lines, top_offset == UNSET);
 
        /* save pointer to jmp instruction address */
-       risc->jmp = rp;
+       buf->jmp = rp;
+       buf->cpu[1] = cpu_to_le32(buf->dma + 8);
        /* assure risc buffer hasn't overflowed */
-       BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+       BUG_ON((buf->jmp - buf->cpu + 2) * sizeof(buf->cpu[0]) > buf->size);
        return 0;
 }
 
@@ -204,65 +203,28 @@ static void tw68_risc_decode(u32 risc, u32 addr)
 
        p = RISC_OP(risc);
        if (!(risc & 0x80000000) || !instr[p].name) {
-               printk(KERN_DEBUG "0x%08x [ INVALID ]\n", risc);
+               pr_debug("0x%08x [ INVALID ]\n", risc);
                return;
        }
-       printk(KERN_DEBUG "0x%08x %-9s IRQ=%d",
+       pr_debug("0x%08x %-9s IRQ=%d",
                risc, instr[p].name, (risc >> 27) & 1);
        if (instr[p].has_data_type)
-               printk(KERN_DEBUG " Type=%d", (risc >> 24) & 7);
+               pr_debug(" Type=%d", (risc >> 24) & 7);
        if (instr[p].has_byte_info)
-               printk(KERN_DEBUG " Start=0x%03x Count=%03u",
+               pr_debug(" Start=0x%03x Count=%03u",
                        (risc >> 12) & 0xfff, risc & 0xfff);
        if (instr[p].has_addr)
-               printk(KERN_DEBUG " StartAddr=0x%08x", addr);
-       printk(KERN_DEBUG "\n");
+               pr_debug(" StartAddr=0x%08x", addr);
+       pr_debug("\n");
 }
 
-void tw68_risc_program_dump(struct tw68_core *core,
-                           struct btcx_riscmem *risc)
+void tw68_risc_program_dump(struct tw68_core *core, struct tw68_buf *buf)
 {
-       __le32 *addr;
+       const __le32 *addr;
 
-       printk(KERN_DEBUG "%s: risc_program_dump: risc=%p, "
-                         "risc->cpu=0x%p, risc->jmp=0x%p\n",
-                         core->name, risc, risc->cpu, risc->jmp);
-       for (addr = risc->cpu; addr <= risc->jmp; addr += 2)
+       pr_debug("%s: risc_program_dump: risc=%p, buf->cpu=0x%p, buf->jmp=0x%p\n",
+                 core->name, buf, buf->cpu, buf->jmp);
+       for (addr = buf->cpu; addr <= buf->jmp; addr += 2)
                tw68_risc_decode(*addr, *(addr+1));
 }
-EXPORT_SYMBOL_GPL(tw68_risc_program_dump);
 #endif
-
-/*
- * tw68_risc_stopper
- *     Normally, the risc code generated for a buffer ends with a
- *     JUMP instruction to direct the DMAP processor to the code for
- *     the next buffer.  However, when there is no additional buffer
- *     currently available, the code instead jumps to this routine.
- *
- *     My first try for a "stopper" program was just a simple
- *     "jump to self" instruction.  Unfortunately, this caused the
- *     video FIFO to overflow.  My next attempt was to just disable
- *     the DMAP processor.  Unfortunately, this caused the video
- *     decoder to lose its synchronization.  The solution to this was to
- *     add a "Sync-Odd" instruction, which "eats" all the video data
- *     until the start of the next odd field.
- */
-int tw68_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc)
-{
-       __le32 *rp;
-       int rc;
-
-       rc = btcx_riscmem_alloc(pci, risc, 8*4);
-       if (rc < 0)
-               return rc;
-
-       /* write risc inststructions */
-       rp = risc->cpu;
-       *(rp++) = cpu_to_le32(RISC_SYNCO);
-       *(rp++) = 0;
-       *(rp++) = cpu_to_le32(RISC_JUMP);
-       *(rp++) = cpu_to_le32(risc->dma);
-       risc->jmp = risc->cpu;
-       return 0;
-}
diff --git a/drivers/media/pci/tw68/tw68-ts.c b/drivers/media/pci/tw68/tw68-ts.c
deleted file mode 100644 (file)
index dacd6e6..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- *  tw68_ts.c
- *  Part of the device driver for Techwell 68xx based cards
- *
- *  Much of this code is derived from the cx88 and sa7134 drivers, which
- *  were in turn derived from the bt87x driver.  The original work was by
- *  Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
- *  Hans Verkuil, Andy Walls and many others.  Their work is gratefully
- *  acknowledged.  Full credit goes to them - any problems within this code
- *  are mine.
- *
- *  Copyright (C) 2009  William M. Brack <wbrack@mmm.com.hk>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "tw68.h"
-
-int tw68_ts_init1(struct tw68_dev *dev)
-{
-       return 0;
-}
-
-int tw68_ts_ini(struct tw68_dev *dev)
-{
-       return 0;
-}
-
-int tw68_ts_fini(struct tw68_dev *dev)
-{
-       return 0;
-}
-
-void tw68_irq_ts_done(struct tw68_dev *dev, unsigned long status)
-{
-       return;
-}
-
-int tw68_ts_register(struct tw68_mpeg_ops *ops)
-{
-       return 0;
-}
-
-void tw68_ts_unregister(struct tw68_mpeg_ops *ops)
-{
-       return;
-}
-
-int tw68_ts_init_hw(struct tw68_dev *dev)
-{
-       return 0;
-}
-
-
diff --git a/drivers/media/pci/tw68/tw68-tvaudio.c b/drivers/media/pci/tw68/tw68-tvaudio.c
deleted file mode 100644 (file)
index 656d462..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- *  tw68_controls.c
- *  Part of the device driver for Techwell 68xx based cards
- *
- *  Much of this code is derived from the cx88 and sa7134 drivers, which
- *  were in turn derived from the bt87x driver.  The original work was by
- *  Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
- *  Hans Verkuil, Andy Walls and many others.  Their work is gratefully
- *  acknowledged.  Full credit goes to them - any problems within this code
- *  are mine.
- *
- *  Copyright (C) 2009  William M. Brack <wbrack@mmm.com.hk>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "tw68.h"
-
-int tw68_tvaudio_rx2mode(u32 rx)
-{
-       return 0;
-}
-
-void tw68_tvaudio_setmute(struct tw68_dev *dev)
-{
-       return;
-}
-
-void tw68_tvaudio_setinput(struct tw68_dev *dev, struct tw68_input *in)
-{
-       return;
-}
-
-void tw68_tvaudio_setvolume(struct tw68_dev *dev, int level)
-{
-       return;
-}
-
-int tw68_tvaudio_getstereo(struct tw68_dev *dev)
-{
-       return 0;
-}
-
-void tw68_tvaudio_init(struct tw68_dev *dev)
-{
-       return;
-}
-
-int tw68_tvaudio_init2(struct tw68_dev *dev)
-{
-       return 0;
-}
-
-int tw68_tvaudio_fini(struct tw68_dev *dev)
-{
-       return 0;
-}
-
-int tw68_tvaudio_do_scan(struct tw68_dev *dev)
-{
-       return 0;
-}
-
-void tw68_enable_i2s(struct tw68_dev *dev)
-{
-       return;
-}
-
diff --git a/drivers/media/pci/tw68/tw68-vbi.c b/drivers/media/pci/tw68/tw68-vbi.c
deleted file mode 100644 (file)
index fbad3b9..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- *  tw68_controls.c
- *  Part of the device driver for Techwell 68xx based cards
- *
- *  Much of this code is derived from the cx88 and sa7134 drivers, which
- *  were in turn derived from the bt87x driver.  The original work was by
- *  Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
- *  Hans Verkuil, Andy Walls and many others.  Their work is gratefully
- *  acknowledged.  Full credit goes to them - any problems within this code
- *  are mine.
- *
- *  Copyright (C) 2009  William M. Brack <wbrack@mmm.com.hk>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "tw68.h"
-
-static int buffer_setup(struct videobuf_queue *q, unsigned int *count,
-                       unsigned int *size) {
-       printk(KERN_INFO "%s: shouldn't be here!\n", __func__);
-       return 0;
-}
-static int buffer_prepare(struct videobuf_queue *q,
-                         struct videobuf_buffer *vb,
-                         enum v4l2_field field)
-{
-       printk(KERN_INFO "%s: shouldn't be here!\n", __func__);
-       return 0;
-}
-static void buffer_queue(struct videobuf_queue *q,
-                        struct videobuf_buffer *vb)
-{
-       printk(KERN_INFO "%s: shouldn't be here!\n", __func__);
-}
-static void buffer_release(struct videobuf_queue *q,
-                          struct videobuf_buffer *vb)
-{
-       printk(KERN_INFO "%s: shouldn't be here!\n", __func__);
-}
-struct videobuf_queue_ops tw68_vbi_qops = {
-       .buf_setup    = buffer_setup,
-       .buf_prepare  = buffer_prepare,
-       .buf_queue    = buffer_queue,
-       .buf_release  = buffer_release,
-};
-
-/* ------------------------------------------------------------------ */
-
-int tw68_vbi_init1(struct tw68_dev *dev)
-{
-       return 0;
-}
-
-int tw68_vbi_fini(struct tw68_dev *dev)
-{
-       return 0;
-}
-
-void tw68_irq_vbi_done(struct tw68_dev *dev, unsigned long status)
-{
-       return;
-}
-
index ca08ca38d3bdfc7564369446c4d8de02c29714ff..66fae2345fdd9a5642353ccec95639651e4d52f4 100644 (file)
@@ -8,7 +8,11 @@
  *  acknowledged.  Full credit goes to them - any problems within this code
  *  are mine.
  *
- *  Copyright (C) 2009  William M. Brack <wbrack@mmm.com.hk>
+ *  Copyright (C) 2009  William M. Brack
+ *
+ *  Refactored and updated to the latest v4l core frameworks:
+ *
+ *  Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
 #include <linux/module.h>
 #include <media/v4l2-common.h>
-#include <linux/sort.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-dma-sg.h>
 
 #include "tw68.h"
 #include "tw68-reg.h"
 
-unsigned int video_debug;
-
-static unsigned int gbuffers   = 8;
-static unsigned int noninterlaced; /* 0 */
-static unsigned int gbufsz     = 768*576*4;
-static unsigned int gbufsz_max = 768*576*4;
-static char secam[]            = "--";
-
-module_param(video_debug, int, 0644);
-MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
-module_param(gbuffers, int, 0444);
-MODULE_PARM_DESC(gbuffers, "number of capture buffers, range 2-32");
-module_param(noninterlaced, int, 0644);
-MODULE_PARM_DESC(noninterlaced, "capture non interlaced video");
-module_param_string(secam, secam, sizeof(secam), 0644);
-MODULE_PARM_DESC(secam, "force SECAM variant, either DK,L or Lc");
-
-#define dprintk(level, fmt, arg...)     if (video_debug & (level)) \
-       printk(KERN_DEBUG "%s/0: " fmt, dev->name , ## arg)
-
 /* ------------------------------------------------------------------ */
 /* data structs for video                                             */
 /*
@@ -60,7 +41,7 @@ MODULE_PARM_DESC(secam, "force SECAM variant, either DK,L or Lc");
  * as "planar".  These affect overlay mode, and are flagged with a field
  * ".planar" in the format.  Do we need to implement this in this driver?
  */
-static struct tw68_format formats[] = {
+static const struct tw68_format formats[] = {
        {
                .name           = "15 bpp RGB, le",
                .fourcc         = V4L2_PIX_FMT_RGB555,
@@ -145,47 +126,8 @@ static struct tw68_format formats[] = {
  * match, then for an entry which contains the desired id.  The table
  * entries should therefore be ordered in ascending order of specificity.
  */
-static struct tw68_tvnorm tvnorms[]            = {
+static const struct tw68_tvnorm tvnorms[] = {
        {
-               .name           = "PAL-BG",
-               .id             = V4L2_STD_PAL_BG,
-               NORM_625_50,
-
-               .sync_control   = 0x18,
-               .luma_control   = 0x40,
-               .chroma_ctrl1   = 0x81,
-               .chroma_gain    = 0x2a,
-               .chroma_ctrl2   = 0x06,
-               .vgate_misc     = 0x1c,
-               .format         = VideoFormatPALBDGHI,
-
-       }, {
-               .name           = "PAL-I",
-               .id             = V4L2_STD_PAL_I,
-               NORM_625_50,
-
-               .sync_control   = 0x18,
-               .luma_control   = 0x40,
-               .chroma_ctrl1   = 0x81,
-               .chroma_gain    = 0x2a,
-               .chroma_ctrl2   = 0x06,
-               .vgate_misc     = 0x1c,
-               .format         = VideoFormatPALBDGHI,
-
-       }, {
-               .name           = "PAL-DK",
-               .id             = V4L2_STD_PAL_DK,
-               NORM_625_50,
-
-               .sync_control   = 0x18,
-               .luma_control   = 0x40,
-               .chroma_ctrl1   = 0x81,
-               .chroma_gain    = 0x2a,
-               .chroma_ctrl2   = 0x06,
-               .vgate_misc     = 0x1c,
-               .format         = VideoFormatPALBDGHI,
-
-       }, {
                .name           = "PAL", /* autodetect */
                .id             = V4L2_STD_PAL,
                NORM_625_50,
@@ -197,7 +139,6 @@ static struct tw68_tvnorm tvnorms[]         = {
                .chroma_ctrl2   = 0x06,
                .vgate_misc     = 0x1c,
                .format         = VideoFormatPALBDGHI,
-
        }, {
                .name           = "NTSC",
                .id             = V4L2_STD_NTSC,
@@ -210,46 +151,6 @@ static struct tw68_tvnorm tvnorms[]                = {
                .chroma_ctrl2   = 0x0e,
                .vgate_misc     = 0x18,
                .format         = VideoFormatNTSC,
-
-       }, {
-               .name           = "SECAM-DK",
-               .id             = V4L2_STD_SECAM_DK,
-               NORM_625_50,
-
-               .sync_control   = 0x18,
-               .luma_control   = 0x1b,
-               .chroma_ctrl1   = 0xd1,
-               .chroma_gain    = 0x80,
-               .chroma_ctrl2   = 0x00,
-               .vgate_misc     = 0x1c,
-               .format         = VideoFormatSECAM,
-
-       }, {
-               .name           = "SECAM-L",
-               .id             = V4L2_STD_SECAM_L,
-               NORM_625_50,
-
-               .sync_control   = 0x18,
-               .luma_control   = 0x1b,
-               .chroma_ctrl1   = 0xd1,
-               .chroma_gain    = 0x80,
-               .chroma_ctrl2   = 0x00,
-               .vgate_misc     = 0x1c,
-               .format         = VideoFormatSECAM,
-
-       }, {
-               .name           = "SECAM-LC",
-               .id             = V4L2_STD_SECAM_LC,
-               NORM_625_50,
-
-               .sync_control   = 0x18,
-               .luma_control   = 0x1b,
-               .chroma_ctrl1   = 0xd1,
-               .chroma_gain    = 0x80,
-               .chroma_ctrl2   = 0x00,
-               .vgate_misc     = 0x1c,
-               .format         = VideoFormatSECAM,
-
        }, {
                .name           = "SECAM",
                .id             = V4L2_STD_SECAM,
@@ -262,7 +163,6 @@ static struct tw68_tvnorm tvnorms[]         = {
                .chroma_ctrl2   = 0x00,
                .vgate_misc     = 0x1c,
                .format         = VideoFormatSECAM,
-
        }, {
                .name           = "PAL-M",
                .id             = V4L2_STD_PAL_M,
@@ -275,7 +175,6 @@ static struct tw68_tvnorm tvnorms[]         = {
                .chroma_ctrl2   = 0x0e,
                .vgate_misc     = 0x18,
                .format         = VideoFormatPALM,
-
        }, {
                .name           = "PAL-Nc",
                .id             = V4L2_STD_PAL_Nc,
@@ -288,7 +187,6 @@ static struct tw68_tvnorm tvnorms[]         = {
                .chroma_ctrl2   = 0x06,
                .vgate_misc     = 0x1c,
                .format         = VideoFormatPALNC,
-
        }, {
                .name           = "PAL-60",
                .id             = V4L2_STD_PAL_60,
@@ -309,127 +207,11 @@ static struct tw68_tvnorm tvnorms[]              = {
                .chroma_ctrl2   = 0x06,
                .vgate_misc     = 0x1c,
                .format         = VideoFormatPAL60,
-
-       }, {
-/*
- *     FIXME:  The following are meant to be "catch-all", and need
- *             to be further thought out!
- */
-               .name           = "STD-525-60",
-               .id             = V4L2_STD_525_60,
-               NORM_525_60,
-
-               .sync_control   = 0x59,
-               .luma_control   = 0x40,
-               .chroma_ctrl1   = 0x89,
-               .chroma_gain    = 0x2a,
-               .chroma_ctrl2   = 0x0e,
-               .vgate_misc     = 0x18,
-               .format         = VideoFormatNTSC,
-
-       }, {
-               .name           = "STD-625-50",
-               .id             = V4L2_STD_625_50,
-               NORM_625_50,
-
-               .sync_control   = 0x18,
-               .luma_control   = 0x40,
-               .chroma_ctrl1   = 0x81,
-               .chroma_gain    = 0x2a,
-               .chroma_ctrl2   = 0x06,
-               .vgate_misc     = 0x1c,
-               .format         = VideoFormatPALBDGHI,
        }
 };
 #define TVNORMS ARRAY_SIZE(tvnorms)
 
-static const struct v4l2_queryctrl no_ctrl             = {
-       .name           = "42",
-       .flags          = V4L2_CTRL_FLAG_DISABLED,
-};
-static const struct v4l2_queryctrl video_ctrls[]               = {
-       /* --- video --- */
-       {
-               .id             = V4L2_CID_BRIGHTNESS,
-               .name           = "Brightness",
-               .minimum        = -128,
-               .maximum        = 127,
-               .step           = 1,
-               .default_value  = 20,
-               .type           = V4L2_CTRL_TYPE_INTEGER,
-       }, {
-               .id             = V4L2_CID_CONTRAST,
-               .name           = "Contrast",
-               .minimum        = 0,
-               .maximum        = 255,
-               .step           = 1,
-               .default_value  = 100,
-               .type           = V4L2_CTRL_TYPE_INTEGER,
-       }, {
-               .id             = V4L2_CID_SATURATION,
-               .name           = "Saturation",
-               .minimum        = 0,
-               .maximum        = 255,
-               .step           = 1,
-               .default_value  = 128,
-               .type           = V4L2_CTRL_TYPE_INTEGER,
-       }, {
-               .id             = V4L2_CID_HUE,
-               .name           = "Hue",
-               .minimum        = -128,
-               .maximum        = 127,
-               .step           = 1,
-               .default_value  = 0,
-               .type           = V4L2_CTRL_TYPE_INTEGER,
-       }, {
-               .id             = V4L2_CID_COLOR_KILLER,
-               .name           = "Color Killer",
-               .minimum        = 0,
-               .maximum        = 1,
-               .default_value  = 1,
-               .type           = V4L2_CTRL_TYPE_BOOLEAN,
-       }, {
-               .id             = V4L2_CID_CHROMA_AGC,
-               .name           = "Chroma AGC",
-               .minimum        = 0,
-               .maximum        = 1,
-               .default_value  = 1,
-               .type           = V4L2_CTRL_TYPE_BOOLEAN,
-       },
-       /* --- audio --- */
-       {
-               .id             = V4L2_CID_AUDIO_MUTE,
-               .name           = "Mute",
-               .minimum        = 0,
-               .maximum        = 1,
-               .type           = V4L2_CTRL_TYPE_BOOLEAN,
-       }, {
-               .id             = V4L2_CID_AUDIO_VOLUME,
-               .name           = "Volume",
-               .minimum        = -15,
-               .maximum        = 15,
-               .step           = 1,
-               .default_value  = 0,
-               .type           = V4L2_CTRL_TYPE_INTEGER,
-       }
-};
-static const unsigned int CTRLS = ARRAY_SIZE(video_ctrls);
-
-/*
- * Routine to lookup a control by its ID, and return a pointer
- * to the entry in the video_ctrls array for that control.
- */
-static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id)
-{
-       unsigned int i;
-
-       for (i = 0; i < CTRLS; i++)
-               if (video_ctrls[i].id == id)
-                       return video_ctrls+i;
-       return NULL;
-}
-
-static struct tw68_format *format_by_fourcc(unsigned int fourcc)
+static const struct tw68_format *format_by_fourcc(unsigned int fourcc)
 {
        unsigned int i;
 
@@ -439,99 +221,22 @@ static struct tw68_format *format_by_fourcc(unsigned int fourcc)
        return NULL;
 }
 
-/* ----------------------------------------------------------------------- */
-/* resource management                                                     */
-
-static int res_get(struct tw68_fh *fh, unsigned int bit)
-{
-       struct tw68_dev *dev = fh->dev;
-
-       if (fh->resources & bit)
-               /* have it already allocated */
-               return 1;
-
-       /* is it free? */
-       mutex_lock(&dev->lock);
-       if (dev->resources & bit) {
-               /* no, someone else uses it */
-               mutex_unlock(&fh->dev->lock);
-               return 0;
-       }
-       /* it's free, grab it */
-       fh->resources  |= bit;
-       dev->resources |= bit;
-       dprintk(DBG_FLOW, "%s: %d\n", __func__, bit);
-       mutex_unlock(&dev->lock);
-       return 1;
-}
-
-static int res_check(struct tw68_fh *fh, unsigned int bit)
-{
-       return fh->resources & bit;
-}
-
-static int res_locked(struct tw68_dev *dev, unsigned int bit)
-{
-       return dev->resources & bit;
-}
-
-static void res_free(struct tw68_fh *fh,
-                    unsigned int bits)
-{
-       struct tw68_dev *dev = fh->dev;
-
-       BUG_ON((fh->resources & bits) != bits);
-
-       mutex_lock(&fh->dev->lock);
-       fh->resources  &= ~bits;
-       fh->dev->resources &= ~bits;
-       dprintk(DBG_FLOW, "%s: %d\n", __func__, bits);
-       mutex_unlock(&fh->dev->lock);
-}
 
 /* ------------------------------------------------------------------ */
 /*
  * Note that the cropping rectangles are described in terms of a single
  * frame, i.e. line positions are only 1/2 the interlaced equivalent
  */
-static void set_tvnorm(struct tw68_dev *dev, struct tw68_tvnorm *norm)
+static void set_tvnorm(struct tw68_dev *dev, const struct tw68_tvnorm *norm)
 {
-       dprintk(DBG_FLOW, "%s: %s\n", __func__, norm->name);
-       dev->tvnorm = norm;
-
-       /* setup cropping */
-       dev->crop_bounds.left    = norm->h_start;
-       dev->crop_defrect.left   = norm->h_start;
-       dev->crop_bounds.width   = norm->h_stop - norm->h_start + 1;
-       dev->crop_defrect.width  = norm->h_stop - norm->h_start + 1;
-
-       dev->crop_bounds.top     = norm->video_v_start;
-       dev->crop_defrect.top    = norm->video_v_start;
-       dev->crop_bounds.height  = (((norm->id & V4L2_STD_525_60) ?
-                                   524 : 624)) / 2 - dev->crop_bounds.top;
-       dev->crop_defrect.height = (norm->video_v_stop -
-                                   norm->video_v_start + 1);
-
-       dev->crop_current = dev->crop_defrect;
-
        if (norm != dev->tvnorm) {
+               dev->width = 720;
+               dev->height = (norm->id & V4L2_STD_525_60) ? 480 : 576;
                dev->tvnorm = norm;
                tw68_set_tvnorm_hw(dev);
        }
 }
 
-static void video_mux(struct tw68_dev *dev, int input)
-{
-       dprintk(DBG_FLOW, "%s: input = %d [%s]\n", __func__, input,
-               card_in(dev, input).name);
-       /*
-        * dev->input shows current application request,
-        * dev->hw_input shows current hardware setting
-        */
-       dev->input = &card_in(dev, input);
-       tw68_tvaudio_setinput(dev, &card_in(dev, input));
-}
-
 /*
  * tw68_set_scale
  *
@@ -544,7 +249,7 @@ static void video_mux(struct tw68_dev *dev, int input)
  * before scaling.  HDELAY represents the number of pixels skipped
  * between the start of the horizontal sync and the start of the image.
  * HSCALE is calculated using the formula
- *     HSCALE = (HACTIVE / (#pixels desired)) * 256
+ *     HSCALE = (HACTIVE / (#pixels desired)) * 256
  *
  * The vertical registers are similar, except based upon the total number
  * of lines in the image, and the first line of the image (i.e. ignoring
@@ -555,16 +260,16 @@ static void video_mux(struct tw68_dev *dev, int input)
  * these values, especially HSCALE.
  *
  * Parameters:
- *     @dev            pointer to the device structure, needed for
- *                     getting current norm (as well as debug print)
- *     @width          actual image width (from user buffer)
- *     @height         actual image height
- *     @field          indicates Top, Bottom or Interlaced
+ *     @dev            pointer to the device structure, needed for
+ *                     getting current norm (as well as debug print)
+ *     @width          actual image width (from user buffer)
+ *     @height         actual image height
+ *     @field          indicates Top, Bottom or Interlaced
  */
 static int tw68_set_scale(struct tw68_dev *dev, unsigned int width,
                          unsigned int height, enum v4l2_field field)
 {
-
+       const struct tw68_tvnorm *norm = dev->tvnorm;
        /* set individually for debugging clarity */
        int hactive, hdelay, hscale;
        int vactive, vdelay, vscale;
@@ -573,41 +278,38 @@ static int tw68_set_scale(struct tw68_dev *dev, unsigned int width,
        if (V4L2_FIELD_HAS_BOTH(field)) /* if field is interlaced */
                height /= 2;            /* we must set for 1-frame */
 
-       dprintk(DBG_FLOW, "%s: width=%d, height=%d, both=%d\n  Crop rect: "
-                   "top=%d, left=%d, width=%d height=%d\n"
-                   "  tvnorm h_delay=%d, h_start=%d, h_stop=%d, "
-                   "v_delay=%d, v_start=%d, v_stop=%d\n" , __func__,
+       pr_debug("%s: width=%d, height=%d, both=%d\n"
+                "  tvnorm h_delay=%d, h_start=%d, h_stop=%d, "
+                "v_delay=%d, v_start=%d, v_stop=%d\n" , __func__,
                width, height, V4L2_FIELD_HAS_BOTH(field),
-               dev->crop_bounds.top, dev->crop_bounds.left,
-               dev->crop_bounds.width, dev->crop_bounds.height,
-               dev->tvnorm->h_delay, dev->tvnorm->h_start, dev->tvnorm->h_stop,
-               dev->tvnorm->v_delay, dev->tvnorm->video_v_start,
-               dev->tvnorm->video_v_stop);
+               norm->h_delay, norm->h_start, norm->h_stop,
+               norm->v_delay, norm->video_v_start,
+               norm->video_v_stop);
 
        switch (dev->vdecoder) {
        case TW6800:
-               hdelay = dev->tvnorm->h_delay0;
+               hdelay = norm->h_delay0;
                break;
        default:
-               hdelay = dev->tvnorm->h_delay;
+               hdelay = norm->h_delay;
                break;
        }
-       hdelay += dev->crop_bounds.left;
-       hactive = dev->crop_bounds.width;
+
+       hdelay += norm->h_start;
+       hactive = norm->h_stop - norm->h_start + 1;
 
        hscale = (hactive * 256) / (width);
 
-       vdelay = dev->tvnorm->v_delay + dev->crop_bounds.top -
-                dev->crop_defrect.top;
-       vactive = dev->crop_bounds.height;
+       vdelay = norm->v_delay;
+       vactive = ((norm->id & V4L2_STD_525_60) ? 524 : 624) / 2 - norm->video_v_start;
        vscale = (vactive * 256) / height;
 
-       dprintk(DBG_FLOW, "%s: %dx%d [%s%s,%s]\n", __func__,
+       pr_debug("%s: %dx%d [%s%s,%s]\n", __func__,
                width, height,
                V4L2_FIELD_HAS_TOP(field)    ? "T" : "",
                V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
                v4l2_norm_to_name(dev->tvnorm->id));
-       dprintk(DBG_FLOW, "%s: hactive=%d, hdelay=%d, hscale=%d; "
+       pr_debug("%s: hactive=%d, hdelay=%d, hscale=%d; "
                "vactive=%d, vdelay=%d, vscale=%d\n", __func__,
                hactive, hdelay, hscale, vactive, vdelay, vscale);
 
@@ -615,7 +317,7 @@ static int tw68_set_scale(struct tw68_dev *dev, unsigned int width,
                ((vactive & 0x300) >> 4) |
                ((hdelay & 0x300)  >> 6) |
                ((hactive & 0x300) >> 8);
-       dprintk(DBG_FLOW, "%s: setting CROP_HI=%02x, VDELAY_LO=%02x, "
+       pr_debug("%s: setting CROP_HI=%02x, VDELAY_LO=%02x, "
                "VACTIVE_LO=%02x, HDELAY_LO=%02x, HACTIVE_LO=%02x\n",
                __func__, comb, vdelay, vactive, hdelay, hactive);
        tw_writeb(TW68_CROP_HI, comb);
@@ -625,7 +327,7 @@ static int tw68_set_scale(struct tw68_dev *dev, unsigned int width,
        tw_writeb(TW68_HACTIVE_LO, hactive & 0xff);
 
        comb = ((vscale & 0xf00) >> 4) | ((hscale & 0xf00) >> 8);
-       dprintk(DBG_FLOW, "%s: setting SCALE_HI=%02x, VSCALE_LO=%02x, "
+       pr_debug("%s: setting SCALE_HI=%02x, VSCALE_LO=%02x, "
                "HSCALE_LO=%02x\n", __func__, comb, vscale, hscale);
        tw_writeb(TW68_SCALE_HI, comb);
        tw_writeb(TW68_VSCALE_LO, vscale);
@@ -636,28 +338,21 @@ static int tw68_set_scale(struct tw68_dev *dev, unsigned int width,
 
 /* ------------------------------------------------------------------ */
 
-static int tw68_video_start_dma(struct tw68_dev *dev, struct tw68_dmaqueue *q,
-                               struct tw68_buf *buf) {
-
-       dprintk(DBG_FLOW, "%s: Starting risc program\n", __func__);
-       /* Assure correct input */
-       if (dev->hw_input != dev->input) {
-               dev->hw_input = dev->input;
-               tw_andorb(TW68_INFORM, 0x03 << 2, dev->input->vmux << 2);
-       }
+int tw68_video_start_dma(struct tw68_dev *dev, struct tw68_buf *buf)
+{
        /* Set cropping and scaling */
-       tw68_set_scale(dev, buf->vb.width, buf->vb.height, buf->vb.field);
+       tw68_set_scale(dev, dev->width, dev->height, dev->field);
        /*
         *  Set start address for RISC program.  Note that if the DMAP
         *  processor is currently running, it must be stopped before
         *  a new address can be set.
         */
        tw_clearl(TW68_DMAC, TW68_DMAP_EN);
-       tw_writel(TW68_DMAP_SA, cpu_to_le32(buf->risc.dma));
+       tw_writel(TW68_DMAP_SA, cpu_to_le32(buf->dma));
        /* Clear any pending interrupts */
        tw_writel(TW68_INTSTAT, dev->board_virqmask);
        /* Enable the risc engine and the fifo */
-       tw_andorl(TW68_DMAC, 0xff, buf->fmt->twformat |
+       tw_andorl(TW68_DMAC, 0xff, dev->fmt->twformat |
                ColorFormatGamma | TW68_DMAP_EN | TW68_FIFO_EN);
        dev->pci_irqmask |= dev->board_virqmask;
        tw_setl(TW68_INTMASK, dev->pci_irqmask);
@@ -665,693 +360,295 @@ static int tw68_video_start_dma(struct tw68_dev *dev, struct tw68_dmaqueue *q,
 }
 
 /* ------------------------------------------------------------------ */
-/* videobuf queue operations                                          */
 
-/*
- * check_buf_fmt
- *
- * callback from tw68-core buffer_queue to determine whether the
- * current buffer and the previous one are "compatible" (i.e. the
- * risc programs can be chained without requiring a format change)
- */
-static int tw68_check_video_fmt(struct tw68_buf *prev, struct tw68_buf *buf)
+/* nr of (tw68-)pages for the given buffer size */
+static int tw68_buffer_pages(int size)
 {
-       return (prev->vb.width  == buf->vb.width  &&
-               prev->vb.height == buf->vb.height &&
-               prev->fmt       == buf->fmt);
+       size  = PAGE_ALIGN(size);
+       size += PAGE_SIZE; /* for non-page-aligned buffers */
+       size /= 4096;
+       return size;
 }
 
-/*
- * buffer_setup
- *
- * Calculate required size of buffer and maximum number allowed
- */
-static int
-buffer_setup(struct videobuf_queue *q, unsigned int *count,
-            unsigned int *size)
+/* calc max # of buffers from size (must not exceed the 4MB virtual
+ * address space per DMA channel) */
+static int tw68_buffer_count(unsigned int size, unsigned int count)
 {
-       struct tw68_fh *fh = q->priv_data;
+       unsigned int maxcount;
 
-       *size = fh->fmt->depth * fh->width * fh->height >> 3;
-       if (0 == *count)
-               *count = gbuffers;
-       *count = tw68_buffer_count(*size, *count);
-       return 0;
+       maxcount = 1024 / tw68_buffer_pages(size);
+       if (count > maxcount)
+               count = maxcount;
+       return count;
 }
 
-static int buffer_activate(struct tw68_dev *dev, struct tw68_buf *buf,
-                          struct tw68_buf *next)
-{
-       dprintk(DBG_BUFF, "%s: dev=%p, buf=%p, next=%p\n",
-               __func__, dev, buf, next);
-       if (dev->hw_input != dev->input) {
-               dev->hw_input = dev->input;
-               tw_andorb(TW68_INFORM, 0x03 << 2,
-                         dev->hw_input->vmux << 2);
-       }
-       buf->vb.state = VIDEOBUF_ACTIVE;
-       /* TODO - need to assure scaling/cropping are set correctly */
-       mod_timer(&dev->video_q.timeout, jiffies+BUFFER_TIMEOUT);
-       return 0;
-}
+/* ------------------------------------------------------------- */
+/* vb2 queue operations                                          */
 
-/*
-* buffer_prepare
-*
-* Set the ancilliary information into the buffer structure.  This
-* includes generating the necessary risc program if it hasn't already
-* been done for the current buffer format.
-* The structure fh contains the details of the format requested by the
-* user - type, width, height and #fields.  This is compared with the
-* last format set for the current buffer.  If they differ, the risc
-* code (which controls the filling of the buffer) is (re-)generated.
-*/
-static int
-buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
-              enum v4l2_field field)
+static int tw68_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+                          unsigned int *num_buffers, unsigned int *num_planes,
+                          unsigned int sizes[], void *alloc_ctxs[])
 {
-       struct tw68_fh   *fh  = q->priv_data;
-       struct tw68_dev  *dev = fh->dev;
-       struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb);
-       struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
-       int rc, init_buffer = 0;
-       unsigned int maxw, maxh;
-
-       BUG_ON(NULL == fh->fmt);
-       maxw = dev->tvnorm->h_stop - dev->tvnorm->h_start + 1;
-       maxh = 2*(dev->tvnorm->video_v_stop - dev->tvnorm->video_v_start + 1);
-       if (fh->width  < 48 || fh->width  > maxw || fh->height > maxh
-               || fh->height < 16) {
-               dprintk(DBG_UNEXPECTED, "%s: invalid dimensions - "
-                       "fh->width=%d, fh->height=%d, maxw=%d, maxh=%d\n",
-                       __func__, fh->width, fh->height, maxw, maxh);
-               return -EINVAL;
-       }
-       buf->vb.size = (fh->width * fh->height * (fh->fmt->depth)) >> 3;
-       if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
-               return -EINVAL;
-
-       if (buf->fmt       != fh->fmt    ||
-           buf->vb.width  != fh->width  ||
-           buf->vb.height != fh->height ||
-           buf->vb.field  != field) {
-               dprintk(DBG_BUFF, "%s: buf - fmt=%p, width=%3d, height=%3d, "
-                       "field=%d\n%s: fh  - fmt=%p, width=%3d, height=%3d, "
-                       "field=%d\n", __func__, buf->fmt, buf->vb.width,
-                       buf->vb.height, buf->vb.field, __func__, fh->fmt,
-                       fh->width, fh->height, field);
-               buf->fmt       = fh->fmt;
-               buf->vb.width  = fh->width;
-               buf->vb.height = fh->height;
-               buf->vb.field  = field;
-               init_buffer = 1;        /* force risc code re-generation */
-       }
-       buf->input = dev->input;
+       struct tw68_dev *dev = vb2_get_drv_priv(q);
+       unsigned tot_bufs = q->num_buffers + *num_buffers;
 
-       if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
-               rc = videobuf_iolock(q, &buf->vb, NULL);
-               if (0 != rc)
-                       goto fail;
-               init_buffer = 1;        /* force risc code re-generation */
-       }
-       dprintk(DBG_BUFF, "%s: q=%p, vb=%p, init_buffer=%d\n",
-               __func__, q, vb, init_buffer);
-
-       if (init_buffer) {
-               buf->bpl = buf->vb.width * (buf->fmt->depth) >> 3;
-               dprintk(DBG_TESTING, "%s: Generating new risc code "
-                       "[%dx%dx%d](%d)\n", __func__, buf->vb.width,
-                       buf->vb.height, buf->fmt->depth, buf->bpl);
-               switch (buf->vb.field) {
-               case V4L2_FIELD_TOP:
-                       tw68_risc_buffer(dev->pci, &buf->risc,
-                                        dma->sglist,
-                                        0, UNSET,
-                                        buf->bpl, 0,
-                                        buf->vb.height);
-                       break;
-               case V4L2_FIELD_BOTTOM:
-                       tw68_risc_buffer(dev->pci, &buf->risc,
-                                        dma->sglist,
-                                        UNSET, 0,
-                                        buf->bpl, 0,
-                                        buf->vb.height);
-                       break;
-               case V4L2_FIELD_INTERLACED:
-                       tw68_risc_buffer(dev->pci, &buf->risc,
-                                        dma->sglist,
-                                        0, buf->bpl,
-                                        buf->bpl, buf->bpl,
-                                        buf->vb.height >> 1);
-                       break;
-               case V4L2_FIELD_SEQ_TB:
-                       tw68_risc_buffer(dev->pci, &buf->risc,
-                                        dma->sglist,
-                                        0, buf->bpl * (buf->vb.height >> 1),
-                                        buf->bpl, 0,
-                                        buf->vb.height >> 1);
-                       break;
-               case V4L2_FIELD_SEQ_BT:
-                       tw68_risc_buffer(dev->pci, &buf->risc,
-                                        dma->sglist,
-                                        buf->bpl * (buf->vb.height >> 1), 0,
-                                        buf->bpl, 0,
-                                        buf->vb.height >> 1);
-                       break;
-               default:
-                       BUG();
-               }
-       }
-       dprintk(DBG_BUFF, "%s: [%p/%d] - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
-               __func__, buf, buf->vb.i, fh->width, fh->height,
-               fh->fmt->depth, fh->fmt->name, (unsigned long)buf->risc.dma);
+       sizes[0] = (dev->fmt->depth * dev->width * dev->height) >> 3;
+       /*
+        * We allow create_bufs, but only if the sizeimage is the same as the
+        * current sizeimage. The tw68_buffer_count calculation becomes quite
+        * difficult otherwise.
+        */
+       if (fmt && fmt->fmt.pix.sizeimage < sizes[0])
+               return -EINVAL;
+       *num_planes = 1;
+       if (tot_bufs < 2)
+               tot_bufs = 2;
+       tot_bufs = tw68_buffer_count(sizes[0], tot_bufs);
+       *num_buffers = tot_bufs - q->num_buffers;
 
-       buf->vb.state = VIDEOBUF_PREPARED;
-       buf->activate = buffer_activate;
        return 0;
-
- fail:
-       tw68_dma_free(q, buf);
-       return rc;
 }
 
 /*
- * buffer_queue
+ * The risc program for each buffers works as follows: it starts with a simple
+ * 'JUMP to addr + 8', which is effectively a NOP. Then the program to DMA the
+ * buffer follows and at the end we have a JUMP back to the start + 8 (skipping
+ * the initial JUMP).
+ *
+ * This is the program of the first buffer to be queued if the active list is
+ * empty and it just keeps DMAing this buffer without generating any interrupts.
+ *
+ * If a new buffer is added then the initial JUMP in the program generates an
+ * interrupt as well which signals that the previous buffer has been DMAed
+ * successfully and that it can be returned to userspace.
+ *
+ * It also sets the final jump of the previous buffer to the start of the new
+ * buffer, thus chaining the new buffer into the DMA chain. This is a single
+ * atomic u32 write, so there is no race condition.
  *
- * Callback whenever a buffer has been requested (by read() or QBUF)
+ * The end-result of all this that you only get an interrupt when a buffer
+ * is ready, so the control flow is very easy.
  */
-static void
-buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+static void tw68_buf_queue(struct vb2_buffer *vb)
 {
-       struct tw68_fh  *fh = q->priv_data;
-       struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb);
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct tw68_dev *dev = vb2_get_drv_priv(vq);
+       struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb);
+       struct tw68_buf *prev;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->slock, flags);
 
-       tw68_buffer_queue(fh->dev, &fh->dev->video_q, buf);
+       /* append a 'JUMP to start of buffer' to the buffer risc program */
+       buf->jmp[0] = cpu_to_le32(RISC_JUMP);
+       buf->jmp[1] = cpu_to_le32(buf->dma + 8);
+
+       if (!list_empty(&dev->active)) {
+               prev = list_entry(dev->active.prev, struct tw68_buf, list);
+               buf->cpu[0] |= cpu_to_le32(RISC_INT_BIT);
+               prev->jmp[1] = cpu_to_le32(buf->dma);
+       }
+       list_add_tail(&buf->list, &dev->active);
+       spin_unlock_irqrestore(&dev->slock, flags);
 }
 
 /*
- * buffer_release
+ * buffer_prepare
  *
- * Free a buffer previously allocated.
+ * Set the ancilliary information into the buffer structure.  This
+ * includes generating the necessary risc program if it hasn't already
+ * been done for the current buffer format.
+ * The structure fh contains the details of the format requested by the
+ * user - type, width, height and #fields.  This is compared with the
+ * last format set for the current buffer.  If they differ, the risc
+ * code (which controls the filling of the buffer) is (re-)generated.
  */
-static void buffer_release(struct videobuf_queue *q,
-                          struct videobuf_buffer *vb)
+static int tw68_buf_prepare(struct vb2_buffer *vb)
 {
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct tw68_dev *dev = vb2_get_drv_priv(vq);
        struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb);
+       struct sg_table *dma = vb2_dma_sg_plane_desc(vb, 0);
+       unsigned size, bpl;
+       int rc;
 
-       tw68_dma_free(q, buf);
-}
-
-static struct videobuf_queue_ops video_qops = {
-       .buf_setup    = buffer_setup,
-       .buf_prepare  = buffer_prepare,
-       .buf_queue    = buffer_queue,
-       .buf_release  = buffer_release,
-};
-
-/* ------------------------------------------------------------------ */
+       size = (dev->width * dev->height * dev->fmt->depth) >> 3;
+       if (vb2_plane_size(vb, 0) < size)
+               return -EINVAL;
+       vb2_set_plane_payload(vb, 0, size);
 
-static int tw68_g_ctrl_internal(struct tw68_dev *dev, struct tw68_fh *fh,
-                               struct v4l2_control *c)
-{
-       const struct v4l2_queryctrl *ctrl;
+       rc = dma_map_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
+       if (!rc)
+               return -EIO;
 
-       dprintk(DBG_FLOW, "%s\n", __func__);
-       ctrl = ctrl_by_id(c->id);
-       if (NULL == ctrl)
-               return -EINVAL;
-       switch (c->id) {
-       case V4L2_CID_BRIGHTNESS:
-               c->value = (char)tw_readb(TW68_BRIGHT);
-               break;
-       case V4L2_CID_HUE:
-               c->value = (char)tw_readb(TW68_HUE);
-               break;
-       case V4L2_CID_CONTRAST:
-               c->value = tw_readb(TW68_CONTRAST);
-               break;
-       case V4L2_CID_SATURATION:
-               c->value = tw_readb(TW68_SAT_U);
-               break;
-       case V4L2_CID_COLOR_KILLER:
-               c->value = 0 != (tw_readb(TW68_MISC2) & 0xe0);
+       bpl = (dev->width * dev->fmt->depth) >> 3;
+       switch (dev->field) {
+       case V4L2_FIELD_TOP:
+               tw68_risc_buffer(dev->pci, buf, dma->sgl,
+                                0, UNSET, bpl, 0, dev->height);
                break;
-       case V4L2_CID_CHROMA_AGC:
-               c->value = 0 != (tw_readb(TW68_LOOP) & 0x30);
+       case V4L2_FIELD_BOTTOM:
+               tw68_risc_buffer(dev->pci, buf, dma->sgl,
+                                UNSET, 0, bpl, 0, dev->height);
                break;
-       case V4L2_CID_AUDIO_MUTE:
-               /*hack to suppresss tvtime complaint */
-               c->value = 0;
+       case V4L2_FIELD_SEQ_TB:
+               tw68_risc_buffer(dev->pci, buf, dma->sgl,
+                                0, bpl * (dev->height >> 1),
+                                bpl, 0, dev->height >> 1);
                break;
-#if 0
-       case V4L2_CID_AUDIO_VOLUME:
-               c->value = dev->ctl_volume;
+       case V4L2_FIELD_SEQ_BT:
+               tw68_risc_buffer(dev->pci, buf, dma->sgl,
+                                bpl * (dev->height >> 1), 0,
+                                bpl, 0, dev->height >> 1);
                break;
-#endif
+       case V4L2_FIELD_INTERLACED:
        default:
-               return -EINVAL;
+               tw68_risc_buffer(dev->pci, buf, dma->sgl,
+                                0, bpl, bpl, bpl, dev->height >> 1);
+               break;
        }
        return 0;
 }
 
-static int tw68_g_ctrl(struct file *file, void *priv, struct v4l2_control *c)
+static void tw68_buf_finish(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct tw68_dev *dev = vb2_get_drv_priv(vq);
+       struct sg_table *dma = vb2_dma_sg_plane_desc(vb, 0);
+       struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb);
+
+       dma_unmap_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
+
+       pci_free_consistent(dev->pci, buf->size, buf->cpu, buf->dma);
+}
+
+static int tw68_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+       struct tw68_dev *dev = vb2_get_drv_priv(q);
+       struct tw68_buf *buf =
+               container_of(dev->active.next, struct tw68_buf, list);
+
+       dev->seqnr = 0;
+       tw68_video_start_dma(dev, buf);
+       return 0;
+}
+
+static void tw68_stop_streaming(struct vb2_queue *q)
 {
-       struct tw68_fh *fh = priv;
+       struct tw68_dev *dev = vb2_get_drv_priv(q);
+
+       /* Stop risc & fifo */
+       tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN);
+       while (!list_empty(&dev->active)) {
+               struct tw68_buf *buf =
+                       container_of(dev->active.next, struct tw68_buf, list);
 
-       return tw68_g_ctrl_internal(fh->dev, fh, c);
+               list_del(&buf->list);
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+       }
 }
 
-static int tw68_s_ctrl_value(struct tw68_dev *dev, __u32 id, int val)
+static struct vb2_ops tw68_video_qops = {
+       .queue_setup    = tw68_queue_setup,
+       .buf_queue      = tw68_buf_queue,
+       .buf_prepare    = tw68_buf_prepare,
+       .buf_finish     = tw68_buf_finish,
+       .start_streaming = tw68_start_streaming,
+       .stop_streaming = tw68_stop_streaming,
+       .wait_prepare   = vb2_ops_wait_prepare,
+       .wait_finish    = vb2_ops_wait_finish,
+};
+
+/* ------------------------------------------------------------------ */
+
+static int tw68_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       int err = 0;
+       struct tw68_dev *dev =
+               container_of(ctrl->handler, struct tw68_dev, hdl);
 
-       dprintk(DBG_FLOW, "%s\n", __func__);
-       switch (id) {
+       switch (ctrl->id) {
        case V4L2_CID_BRIGHTNESS:
-               tw_writeb(TW68_BRIGHT, val);
+               tw_writeb(TW68_BRIGHT, ctrl->val);
                break;
        case V4L2_CID_HUE:
-               tw_writeb(TW68_HUE, val);
+               tw_writeb(TW68_HUE, ctrl->val);
                break;
        case V4L2_CID_CONTRAST:
-               tw_writeb(TW68_CONTRAST, val);
+               tw_writeb(TW68_CONTRAST, ctrl->val);
                break;
        case V4L2_CID_SATURATION:
-               tw_writeb(TW68_SAT_U, val);
-               tw_writeb(TW68_SAT_V, val);
+               tw_writeb(TW68_SAT_U, ctrl->val);
+               tw_writeb(TW68_SAT_V, ctrl->val);
                break;
        case V4L2_CID_COLOR_KILLER:
-               if (val)
+               if (ctrl->val)
                        tw_andorb(TW68_MISC2, 0xe0, 0xe0);
                else
                        tw_andorb(TW68_MISC2, 0xe0, 0x00);
                break;
        case V4L2_CID_CHROMA_AGC:
-               if (val)
+               if (ctrl->val)
                        tw_andorb(TW68_LOOP, 0x30, 0x20);
                else
                        tw_andorb(TW68_LOOP, 0x30, 0x00);
                break;
-       case V4L2_CID_AUDIO_MUTE:
-               /* hack to suppress tvtime complaint */
-               break;
-#if 0
-       case V4L2_CID_AUDIO_VOLUME:
-               dev->ctl_volume = val;
-               tw68_tvaudio_setvolume(dev, dev->ctl_volume);
-               break;
-       case V4L2_CID_HFLIP:
-               dev->ctl_mirror = val;
-               break;
-       case V4L2_CID_PRIVATE_AUTOMUTE:
-       {
-               struct v4l2_priv_tun_config tda9887_cfg;
-
-               tda9887_cfg.tuner = TUNER_TDA9887;
-               tda9887_cfg.priv = &dev->tda9887_conf;
-
-               dev->ctl_automute = val;
-               if (dev->tda9887_conf) {
-                       if (dev->ctl_automute)
-                               dev->tda9887_conf |= TDA9887_AUTOMUTE;
-                       else
-                               dev->tda9887_conf &= ~TDA9887_AUTOMUTE;
-
-                       tw_call_all(dev, tuner, s_config, &tda9887_cfg);
-               }
-               break;
-       }
-#endif
-       default:
-               err = -EINVAL;
        }
-       return err;
+       return 0;
 }
 
-static int tw68_s_ctrl_internal(struct tw68_dev *dev,  struct tw68_fh *fh,
-                        struct v4l2_control *c)
-{
-       const struct v4l2_queryctrl *ctrl;
-       int err;
-
-       dprintk(DBG_FLOW, "%s\n", __func__);
-       if (fh) {
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,34)
-               err = v4l2_prio_check(&dev->prio, &fh->prio);
-#else
-               err = v4l2_prio_check(&dev->prio, fh->prio);
-#endif
-               if (0 != err)
-                       return err;
-       }
+/* ------------------------------------------------------------------ */
 
-       mutex_lock(&dev->lock);
+/*
+ * Note that this routine returns what is stored in the fh structure, and
+ * does not interrogate any of the device registers.
+ */
+static int tw68_g_fmt_vid_cap(struct file *file, void *priv,
+                               struct v4l2_format *f)
+{
+       struct tw68_dev *dev = video_drvdata(file);
 
-       ctrl = ctrl_by_id(c->id);
-       if (NULL == ctrl) {
-               err = -EINVAL;
-               goto error;
-       }
-
-       dprintk(DBG_BUFF, "%s: name=%s val=%d\n", __func__,
-               ctrl->name, c->value);
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_BOOLEAN:
-       case V4L2_CTRL_TYPE_MENU:
-       case V4L2_CTRL_TYPE_INTEGER:
-               if (c->value < ctrl->minimum)
-                       c->value = ctrl->minimum;
-               if (c->value > ctrl->maximum)
-                       c->value = ctrl->maximum;
-               break;
-       default:
-               /* nothing */;
-       };
-       err = tw68_s_ctrl_value(dev, c->id, c->value);
-
-error:
-       mutex_unlock(&dev->lock);
-       return err;
-}
-
-static int tw68_s_ctrl(struct file *file, void *f, struct v4l2_control *c)
-{
-       struct tw68_fh *fh = f;
-
-       return tw68_s_ctrl_internal(fh->dev, fh, c);
-}
-
-/* ------------------------------------------------------------------ */
-
-/*
- * Returns a pointer to the currently used queue (e.g. video, vbi, etc.)
- */
-static struct videobuf_queue *tw68_queue(struct tw68_fh *fh)
-{
-       struct videobuf_queue *q = NULL;
-
-       switch (fh->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               q = &fh->cap;
-               break;
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               q = &fh->vbi;
-               break;
-       default:
-               BUG();
-       }
-       return q;
-}
-
-static int tw68_resource(struct tw68_fh *fh)
-{
-       if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return RESOURCE_VIDEO;
-
-       if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
-               return RESOURCE_VBI;
-
-       BUG();
-       return 0;
-}
-
-static int video_open(struct file *file)
-{
-       int minor = video_devdata(file)->minor;
-       struct tw68_dev *dev;
-       struct tw68_fh *fh;
-       enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-       int radio = 0;
-
-       mutex_lock(&tw68_devlist_lock);
-       list_for_each_entry(dev, &tw68_devlist, devlist) {
-               if (dev->video_dev && (dev->video_dev->minor == minor))
-                       goto found;
-               if (dev->radio_dev && (dev->radio_dev->minor == minor)) {
-                       radio = 1;
-                       goto found;
-               }
-               if (dev->vbi_dev && (dev->vbi_dev->minor == minor)) {
-                       type = V4L2_BUF_TYPE_VBI_CAPTURE;
-                       goto found;
-               }
-       }
-       mutex_unlock(&tw68_devlist_lock);
-       return -ENODEV;
-
-found:
-       mutex_unlock(&tw68_devlist_lock);
-
-       dprintk(DBG_FLOW, "%s: minor=%d radio=%d type=%s\n", __func__, minor,
-               radio, v4l2_type_names[type]);
-
-       /* allocate + initialize per filehandle data */
-       fh = kzalloc(sizeof(*fh), GFP_KERNEL);
-       if (NULL == fh)
-               return -ENOMEM;
-
-       file->private_data = fh;
-       fh->dev      = dev;
-       fh->radio    = radio;
-       fh->type     = type;
-       fh->fmt      = format_by_fourcc(V4L2_PIX_FMT_BGR24);
-       fh->width    = 720;
-       fh->height   = 576;
-       v4l2_prio_open(&dev->prio, &fh->prio);
-
-       videobuf_queue_sg_init(&fh->cap, &video_qops,
-                           &dev->pci->dev, &dev->slock,
-                           V4L2_BUF_TYPE_VIDEO_CAPTURE,
-                           V4L2_FIELD_INTERLACED,
-                           sizeof(struct tw68_buf),
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,37)
-                           fh
-#else
-                           fh, &dev->lock
-#endif
-             );
-       videobuf_queue_sg_init(&fh->vbi, &tw68_vbi_qops,
-                           &dev->pci->dev, &dev->slock,
-                           V4L2_BUF_TYPE_VBI_CAPTURE,
-                           V4L2_FIELD_SEQ_TB,
-                           sizeof(struct tw68_buf),
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,37)
-                           fh
-#else
-                           fh, &dev->lock
-#endif
-             );
-       if (fh->radio) {
-               /* switch to radio mode */
-               tw68_tvaudio_setinput(dev, &card(dev).radio);
-               tw_call_all(dev, tuner, s_radio);
-       } else {
-               /* switch to video/vbi mode */
-               tw68_tvaudio_setinput(dev, dev->input);
-       }
-       return 0;
-}
-
-static ssize_t
-video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
-{
-       struct tw68_fh *fh = file->private_data;
-
-       switch (fh->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               if (res_locked(fh->dev, RESOURCE_VIDEO))
-                       return -EBUSY;
-               return videobuf_read_one(tw68_queue(fh),
-                                        data, count, ppos,
-                                        file->f_flags & O_NONBLOCK);
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               if (!res_get(fh, RESOURCE_VBI))
-                       return -EBUSY;
-               return videobuf_read_stream(tw68_queue(fh),
-                                           data, count, ppos, 1,
-                                           file->f_flags & O_NONBLOCK);
-               break;
-       default:
-               BUG();
-               return 0;
-       }
-}
-
-static unsigned int
-video_poll(struct file *file, struct poll_table_struct *wait)
-{
-       struct tw68_fh *fh = file->private_data;
-       struct videobuf_buffer *buf = NULL;
-
-       if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)
-               return videobuf_poll_stream(file, &fh->vbi, wait);
-
-       if (res_check(fh, RESOURCE_VIDEO)) {
-               if (!list_empty(&fh->cap.stream))
-                       buf = list_entry(fh->cap.stream.next,
-                               struct videobuf_buffer, stream);
-       } else {
-               mutex_lock(&fh->cap.vb_lock);
-               if (UNSET == fh->cap.read_off) {
-                       /* need to capture a new frame */
-                       if (res_locked(fh->dev, RESOURCE_VIDEO))
-                               goto err;
-                       if (0 != fh->cap.ops->buf_prepare(&fh->cap,
-                                       fh->cap.read_buf, fh->cap.field))
-                               goto err;
-                       fh->cap.ops->buf_queue(&fh->cap, fh->cap.read_buf);
-                       fh->cap.read_off = 0;
-               }
-               mutex_unlock(&fh->cap.vb_lock);
-               buf = fh->cap.read_buf;
-       }
-
-       if (!buf)
-               return POLLERR;
-
-       poll_wait(file, &buf->done, wait);
-       if (buf->state == VIDEOBUF_DONE ||
-           buf->state == VIDEOBUF_ERROR)
-               return POLLIN | POLLRDNORM;
-       return 0;
-
-err:
-       mutex_unlock(&fh->cap.vb_lock);
-       return POLLERR;
-}
-
-static int video_release(struct file *file)
-{
-       struct tw68_fh  *fh  = file->private_data;
-       struct tw68_dev *dev = fh->dev;
-
-       /* stop video capture */
-       if (res_check(fh, RESOURCE_VIDEO)) {
-               videobuf_streamoff(&fh->cap);
-               res_free(fh , RESOURCE_VIDEO);
-       }
-       if (fh->cap.read_buf) {
-               buffer_release(&fh->cap, fh->cap.read_buf);
-               kfree(fh->cap.read_buf);
-       }
-
-       /* stop vbi capture */
-       if (res_check(fh, RESOURCE_VBI)) {
-               videobuf_stop(&fh->vbi);
-               res_free(fh, RESOURCE_VBI);
-       }
-
-#if 0
-       tw_call_all(dev, core, s_standby, 0);
-#endif
-
-       /* free stuff */
-       videobuf_mmap_free(&fh->cap);
-       videobuf_mmap_free(&fh->vbi);
-
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,34)
-       v4l2_prio_close(&dev->prio, &fh->prio);
-#else
-       v4l2_prio_close(&dev->prio, fh->prio);
-#endif
-       file->private_data = NULL;
-       kfree(fh);
-       return 0;
-}
-
-static int video_mmap(struct file *file, struct vm_area_struct * vma)
-{
-       struct tw68_fh *fh = file->private_data;
-
-       return videobuf_mmap_mapper(tw68_queue(fh), vma);
-}
-
-/* ------------------------------------------------------------------ */
-
-#if 0
-static int tw68_try_get_set_fmt_vbi_cap(struct file *file, void *priv,
-                                               struct v4l2_format *f)
-{
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
-       struct tw68_tvnorm *norm = dev->tvnorm;
-
-       f->fmt.vbi.sampling_rate = 6750000 * 4;
-       f->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */;
-       f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
-       f->fmt.vbi.offset = 64 * 4;
-       f->fmt.vbi.start[0] = norm->vbi_v_start_0;
-       f->fmt.vbi.count[0] = norm->vbi_v_stop_0 - norm->vbi_v_start_0 + 1;
-       f->fmt.vbi.start[1] = norm->vbi_v_start_1;
-       f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
-       f->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */
-
-#if 0
-       if (V4L2_STD_PAL == norm->id) {
-               /* FIXME */
-               f->fmt.vbi.start[0] += 3;
-               f->fmt.vbi.start[1] += 3*2;
-       }
-#endif
-       return 0;
-}
-#endif
-
-/*
- * Note that this routine returns what is stored in the fh structure, and
- * does not interrogate any of the device registers.
- */
-static int tw68_g_fmt_vid_cap(struct file *file, void *priv,
-                               struct v4l2_format *f)
-{
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
-
-       dprintk(DBG_FLOW, "%s\n", __func__);
-       f->fmt.pix.width        = fh->width;
-       f->fmt.pix.height       = fh->height;
-       f->fmt.pix.field        = fh->cap.field;
-       f->fmt.pix.pixelformat  = fh->fmt->fourcc;
+       f->fmt.pix.width        = dev->width;
+       f->fmt.pix.height       = dev->height;
+       f->fmt.pix.field        = dev->field;
+       f->fmt.pix.pixelformat  = dev->fmt->fourcc;
        f->fmt.pix.bytesperline =
-               (f->fmt.pix.width * (fh->fmt->depth)) >> 3;
+               (f->fmt.pix.width * (dev->fmt->depth)) >> 3;
        f->fmt.pix.sizeimage =
                f->fmt.pix.height * f->fmt.pix.bytesperline;
        f->fmt.pix.colorspace   = V4L2_COLORSPACE_SMPTE170M;
+       f->fmt.pix.priv = 0;
        return 0;
 }
 
 static int tw68_try_fmt_vid_cap(struct file *file, void *priv,
                                                struct v4l2_format *f)
 {
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
-       struct tw68_format *fmt;
+       struct tw68_dev *dev = video_drvdata(file);
+       const struct tw68_format *fmt;
        enum v4l2_field field;
-       unsigned int maxw, maxh;
+       unsigned int maxh;
 
-       dprintk(DBG_FLOW, "%s\n", __func__);
        fmt = format_by_fourcc(f->fmt.pix.pixelformat);
        if (NULL == fmt)
                return -EINVAL;
 
        field = f->fmt.pix.field;
-       maxw  = min(dev->crop_current.width*4,  dev->crop_bounds.width);
-       maxh  = min(dev->crop_current.height*4, dev->crop_bounds.height);
+       maxh  = (dev->tvnorm->id & V4L2_STD_525_60) ? 480 : 576;
 
-       if (V4L2_FIELD_ANY == field) {
-               field = (f->fmt.pix.height > maxh/2)
-                       ? V4L2_FIELD_INTERLACED
-                       : V4L2_FIELD_BOTTOM;
-       }
        switch (field) {
        case V4L2_FIELD_TOP:
        case V4L2_FIELD_BOTTOM:
                break;
        case V4L2_FIELD_INTERLACED:
+       case V4L2_FIELD_SEQ_BT:
+       case V4L2_FIELD_SEQ_TB:
                maxh = maxh * 2;
                break;
        default:
-               return -EINVAL;
+               field = (f->fmt.pix.height > maxh / 2)
+                       ? V4L2_FIELD_INTERLACED
+                       : V4L2_FIELD_BOTTOM;
+               break;
        }
 
        f->fmt.pix.field = field;
@@ -1359,8 +656,8 @@ static int tw68_try_fmt_vid_cap(struct file *file, void *priv,
                f->fmt.pix.width  = 48;
        if (f->fmt.pix.height < 32)
                f->fmt.pix.height = 32;
-       if (f->fmt.pix.width > maxw)
-               f->fmt.pix.width = maxw;
+       if (f->fmt.pix.width > 720)
+               f->fmt.pix.width = 720;
        if (f->fmt.pix.height > maxh)
                f->fmt.pix.height = maxh;
        f->fmt.pix.width &= ~0x03;
@@ -1368,7 +665,7 @@ static int tw68_try_fmt_vid_cap(struct file *file, void *priv,
                (f->fmt.pix.width * (fmt->depth)) >> 3;
        f->fmt.pix.sizeimage =
                f->fmt.pix.height * f->fmt.pix.bytesperline;
-
+       f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
        return 0;
 }
 
@@ -1381,76 +678,35 @@ static int tw68_try_fmt_vid_cap(struct file *file, void *priv,
 static int tw68_s_fmt_vid_cap(struct file *file, void *priv,
                                        struct v4l2_format *f)
 {
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
+       struct tw68_dev *dev = video_drvdata(file);
        int err;
 
-       dprintk(DBG_FLOW, "%s\n", __func__);
        err = tw68_try_fmt_vid_cap(file, priv, f);
        if (0 != err)
                return err;
 
-       fh->fmt       = format_by_fourcc(f->fmt.pix.pixelformat);
-       fh->width     = f->fmt.pix.width;
-       fh->height    = f->fmt.pix.height;
-       fh->cap.field = f->fmt.pix.field;
-       /*
-        * The following lines are to make v4l2-test program happy.
-        * The docs should be checked to assure they make sense.
-        */
-       f->fmt.pix.colorspace   = V4L2_COLORSPACE_SMPTE170M;
-       f->fmt.pix.priv = 0;
-       return 0;
-}
-
-static int tw68_queryctrl(struct file *file, void *priv,
-                         struct v4l2_queryctrl *c)
-{
-       const struct v4l2_queryctrl *ctrl;
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
-
-       dprintk(DBG_FLOW, "%s\n", __func__);
-       if ((c->id <  V4L2_CID_BASE || c->id >= V4L2_CID_LASTP1)
-#if 0
-            && (c->id <  V4L2_CID_PRIVATE_BASE ||
-            c->id >= V4L2_CID_PRIVATE_LASTP1)
-#endif
-       )
-               return -EINVAL;
-       ctrl = ctrl_by_id(c->id);
-       if (NULL == ctrl)
-               return -EINVAL;
-       *c = *ctrl;
+       dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+       dev->width = f->fmt.pix.width;
+       dev->height = f->fmt.pix.height;
+       dev->field = f->fmt.pix.field;
        return 0;
 }
 
 static int tw68_enum_input(struct file *file, void *priv,
                                        struct v4l2_input *i)
 {
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
+       struct tw68_dev *dev = video_drvdata(file);
        unsigned int n;
 
        n = i->index;
-       dprintk(DBG_FLOW, "%s: index is %d\n", __func__, n);
-       if (n >= TW68_INPUT_MAX) {
-               dprintk(DBG_FLOW, "%s: INPUT_MAX reached\n", __func__);
+       if (n >= TW68_INPUT_MAX)
                return -EINVAL;
-       }
-       if (NULL == card_in(dev, n).name) {
-               dprintk(DBG_FLOW, "%s: End of list\n", __func__);
-               return -EINVAL;
-       }
-       memset(i, 0, sizeof(*i));
        i->index = n;
-       i->type  = V4L2_INPUT_TYPE_CAMERA;
-       strcpy(i->name, card_in(dev, n).name);
-       if (card_in(dev, n).tv)
-               i->type = V4L2_INPUT_TYPE_TUNER;
-       i->audioset = 1;
+       i->type = V4L2_INPUT_TYPE_CAMERA;
+       snprintf(i->name, sizeof(i->name), "Composite %d", n);
+
        /* If the query is for the current input, get live data */
-       if (n == dev->hw_input->vmux) {
+       if (n == dev->input) {
                int v1 = tw_readb(TW68_STATUS1);
                int v2 = tw_readb(TW68_MVSN);
 
@@ -1465,305 +721,86 @@ static int tw68_enum_input(struct file *file, void *priv,
                if (0 != (v2 & (1 << 2)))
                        i->status |= V4L2_IN_ST_MACROVISION;
        }
-       i->std = TW68_NORMS;
+       i->std = video_devdata(file)->tvnorms;
        return 0;
 }
 
 static int tw68_g_input(struct file *file, void *priv, unsigned int *i)
 {
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
+       struct tw68_dev *dev = video_drvdata(file);
 
-       dprintk(DBG_FLOW, "%s\n", __func__);
-       *i = dev->input->vmux;
+       *i = dev->input;
        return 0;
 }
 
 static int tw68_s_input(struct file *file, void *priv, unsigned int i)
 {
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
-       int err;
+       struct tw68_dev *dev = video_drvdata(file);
 
-       dprintk(DBG_FLOW, "%s\n", __func__);
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,34)
-               err = v4l2_prio_check(&dev->prio, &fh->prio);
-#else
-               err = v4l2_prio_check(&dev->prio, fh->prio);
-#endif
-               if (0 != err)
-       if (0 != err)
-               return err;
-
-       if (i < 0  ||  i >= TW68_INPUT_MAX)
-               return -EINVAL;
-       if (NULL == card_in(dev, i).name)
+       if (i >= TW68_INPUT_MAX)
                return -EINVAL;
-       mutex_lock(&dev->lock);
-       video_mux(dev, i);
-       mutex_unlock(&dev->lock);
+       dev->input = i;
+       tw_andorb(TW68_INFORM, 0x03 << 2, dev->input << 2);
        return 0;
 }
 
 static int tw68_querycap(struct file *file, void  *priv,
                                        struct v4l2_capability *cap)
 {
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
+       struct tw68_dev *dev = video_drvdata(file);
 
-       unsigned int tuner_type = dev->tuner_type;
-
-       dprintk(DBG_FLOW, "%s\n", __func__);
        strcpy(cap->driver, "tw68");
-       strlcpy(cap->card, tw68_boards[dev->board].name,
+       strlcpy(cap->card, "Techwell Capture Card",
                sizeof(cap->card));
        sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
-       cap->version = TW68_VERSION_CODE;
-       cap->capabilities =
+       cap->device_caps =
                V4L2_CAP_VIDEO_CAPTURE |
-               V4L2_CAP_VBI_CAPTURE |
                V4L2_CAP_READWRITE |
-               V4L2_CAP_STREAMING |
-               V4L2_CAP_TUNER;
+               V4L2_CAP_STREAMING;
 
-       if ((tuner_type == TUNER_ABSENT) || (tuner_type == UNSET))
-               cap->capabilities &= ~V4L2_CAP_TUNER;
+       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
        return 0;
 }
 
-static int tw68_s_std_internal(struct tw68_dev *dev, struct tw68_fh *fh,
-                       v4l2_std_id *id)
+static int tw68_s_std(struct file *file, void *priv, v4l2_std_id id)
 {
-/*     unsigned long flags; */
+       struct tw68_dev *dev = video_drvdata(file);
        unsigned int i;
-       v4l2_std_id fixup;
-       int err;
 
-       dprintk(DBG_FLOW, "%s\n", __func__);
-       if (fh) {
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,34)
-               err = v4l2_prio_check(&dev->prio, &fh->prio);
-#else
-               err = v4l2_prio_check(&dev->prio, fh->prio);
-#endif
-               if (0 != err)
-               if (0 != err)
-                       return err;
-       }
+       if (vb2_is_busy(&dev->vidq))
+               return -EBUSY;
 
        /* Look for match on complete norm id (may have mult bits) */
        for (i = 0; i < TVNORMS; i++) {
-               if (*id == tvnorms[i].id)
+               if (id == tvnorms[i].id)
                        break;
        }
 
        /* If no exact match, look for norm which contains this one */
-       if (i == TVNORMS)
-               for (i = 0; i < TVNORMS; i++) {
-                       if (*id & tvnorms[i].id)
+       if (i == TVNORMS) {
+               for (i = 0; i < TVNORMS; i++)
+                       if (id & tvnorms[i].id)
                                break;
-               }
+       }
        /* If still not matched, give up */
        if (i == TVNORMS)
                return -EINVAL;
 
-       /* TODO - verify this additional work with SECAM applies to TW */
-       if ((*id & V4L2_STD_SECAM) && (secam[0] != '-')) {
-               if (secam[0] == 'L' || secam[0] == 'l') {
-                       if (secam[1] == 'C' || secam[1] == 'c')
-                               fixup = V4L2_STD_SECAM_LC;
-                       else
-                               fixup = V4L2_STD_SECAM_L;
-               } else {
-                       if (secam[0] == 'D' || secam[0] == 'd')
-                               fixup = V4L2_STD_SECAM_DK;
-                       else
-                               fixup = V4L2_STD_SECAM;
-               }
-               for (i = 0; i < TVNORMS; i++)
-                       if (fixup == tvnorms[i].id)
-                               break;
-       }
-
-       *id = tvnorms[i].id;
-       mutex_lock(&dev->lock);
        set_tvnorm(dev, &tvnorms[i]);   /* do the actual setting */
-       tw68_tvaudio_do_scan(dev);
-       mutex_unlock(&dev->lock);
        return 0;
 }
 
-static int tw68_s_std(struct file *file, void *priv, v4l2_std_id *id)
-{
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
-
-       dprintk(DBG_FLOW, "%s\n", __func__);
-       return tw68_s_std_internal(fh->dev, fh, id);
-}
-
 static int tw68_g_std(struct file *file, void *priv, v4l2_std_id *id)
 {
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
+       struct tw68_dev *dev = video_drvdata(file);
 
-       dprintk(DBG_FLOW, "%s\n", __func__);
        *id = dev->tvnorm->id;
        return 0;
 }
 
-static int tw68_g_tuner(struct file *file, void *priv,
-                                       struct v4l2_tuner *t)
-{
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
-       int n;
-
-       if (unlikely(UNSET == dev->tuner_type))
-               return -EINVAL;
-       if (0 != t->index)
-               return -EINVAL;
-       memset(t, 0, sizeof(*t));
-       for (n = 0; n < TW68_INPUT_MAX; n++)
-               if (card_in(dev, n).tv)
-                       break;
-       if (n == TW68_INPUT_MAX)
-               return -EINVAL;
-#if 0
-       if (NULL != card_in(dev, n).name) {
-               strcpy(t->name, "Television");
-               t->type = V4L2_TUNER_ANALOG_TV;
-               t->capability = V4L2_TUNER_CAP_NORM |
-                       V4L2_TUNER_CAP_STEREO |
-                       V4L2_TUNER_CAP_LANG1 |
-                       V4L2_TUNER_CAP_LANG2;
-               t->rangehigh = 0xffffffffUL;
-               t->rxsubchans = tw68_tvaudio_getstereo(dev);
-               t->audmode = tw68_tvaudio_rx2mode(t->rxsubchans);
-       }
-       if (0 != (saa_readb(TW68_STATUS_VIDEO1) & 0x03))
-               t->signal = 0xffff;
-#endif
-       return 0;
-}
-
-static int tw68_s_tuner(struct file *file, void *priv,
-                                       struct v4l2_tuner *t)
-{
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
-       int err;
-#if 0
-       int rx, mode
-#endif
-
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,34)
-               err = v4l2_prio_check(&dev->prio, &fh->prio);
-#else
-               err = v4l2_prio_check(&dev->prio, fh->prio);
-#endif
-               if (0 != err)
-       if (0 != err)
-               return err;
-
-#if 0
-       mode = dev->thread.mode;
-       if (UNSET == mode) {
-               rx   = tw68_tvaudio_getstereo(dev);
-               mode = tw68_tvaudio_rx2mode(t->rxsubchans);
-       }
-       if (mode != t->audmode)
-               dev->thread.mode = t->audmode;
-#endif
-       return 0;
-}
-
-static int tw68_g_frequency(struct file *file, void *priv,
-                                       struct v4l2_frequency *f)
-{
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
-
-       if (unlikely(dev->tuner_type))
-               return -EINVAL;
-       f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
-/*     f->frequency = dev->ctl_freq; */
-
-       return 0;
-}
-
-static int tw68_s_frequency(struct file *file, void *priv,
-                                       struct v4l2_frequency *f)
-{
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
-       int err;
-
-       if (unlikely(UNSET == dev->tuner_type))
-               return -EINVAL;
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,34)
-               err = v4l2_prio_check(&dev->prio, &fh->prio);
-#else
-               err = v4l2_prio_check(&dev->prio, fh->prio);
-#endif
-               if (0 != err)
-       if (0 != err)
-               return err;
-
-       if (0 != f->tuner)
-               return -EINVAL;
-       if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type)
-               return -EINVAL;
-       if (1 == fh->radio && V4L2_TUNER_RADIO != f->type)
-               return -EINVAL;
-       mutex_lock(&dev->lock);
-/*     dev->ctl_freq = f->frequency; */
-
-       tw_call_all(dev, tuner, s_frequency, f);
-
-       tw68_tvaudio_do_scan(dev);
-       mutex_unlock(&dev->lock);
-       return 0;
-}
-
-static int tw68_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
-       strcpy(a->name, "audio");
-       return 0;
-}
-
-static int tw68_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
-       return 0;
-}
-
-static int tw68_g_priority(struct file *file, void *f, enum v4l2_priority *p)
-{
-       struct tw68_fh *fh = f;
-       struct tw68_dev *dev = fh->dev;
-
-       dprintk(DBG_FLOW, "%s\n", __func__);
-       *p = v4l2_prio_max(&dev->prio);
-       return 0;
-}
-
-static int tw68_s_priority(struct file *file, void *f,
-                                       enum v4l2_priority prio)
-{
-       struct tw68_fh *fh = f;
-       struct tw68_dev *dev = fh->dev;
-
-       dprintk(DBG_FLOW, "%s\n", __func__);
-       return v4l2_prio_change(&dev->prio, &fh->prio, prio);
-}
-
 static int tw68_enum_fmt_vid_cap(struct file *file, void  *priv,
                                        struct v4l2_fmtdesc *f)
 {
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
-
-       dprintk(DBG_FLOW, "%s\n", __func__);
        if (f->index >= FORMATS)
                return -EINVAL;
 
@@ -1775,149 +812,6 @@ static int tw68_enum_fmt_vid_cap(struct file *file, void  *priv,
        return 0;
 }
 
-static int tw68_cropcap(struct file *file, void *priv,
-                                       struct v4l2_cropcap *cap)
-{
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
-
-       dprintk(DBG_FLOW, "%s\n", __func__);
-       if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
-       cap->bounds  = dev->crop_bounds;
-       cap->defrect = dev->crop_defrect;
-       cap->pixelaspect.numerator   = 1;
-       cap->pixelaspect.denominator = 1;
-       if (dev->tvnorm->id & V4L2_STD_525_60) {
-               cap->pixelaspect.numerator   = 11;
-               cap->pixelaspect.denominator = 10;
-       }
-       if (dev->tvnorm->id & V4L2_STD_625_50) {
-               cap->pixelaspect.numerator   = 54;
-               cap->pixelaspect.denominator = 59;
-       }
-       return 0;
-}
-
-static int tw68_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
-{
-       struct tw68_fh *fh = f;
-       struct tw68_dev *dev = fh->dev;
-
-       dprintk(DBG_FLOW, "%s\n", __func__);
-       if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
-       crop->c = dev->crop_current;
-       return 0;
-}
-
-static int tw68_s_crop(struct file *file, void *f, struct v4l2_crop *crop)
-{
-       struct tw68_fh *fh = f;
-       struct tw68_dev *dev = fh->dev;
-       struct v4l2_rect *b = &dev->crop_bounds;
-
-       dprintk(DBG_FLOW, "%s\n", __func__);
-       if (res_locked(fh->dev, RESOURCE_VIDEO))
-               return -EBUSY;
-
-       if ((crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
-           (crop->c.height < 0) || (crop->c.width < 0)) {
-               dprintk(DBG_UNEXPECTED, "%s: invalid request\n", __func__);
-               return -EINVAL;
-       }
-
-       if (crop->c.top < b->top)
-               crop->c.top = b->top;
-       if (crop->c.top > b->top + b->height)
-               crop->c.top = b->top + b->height;
-       if (crop->c.height > b->top - crop->c.top + b->height)
-               crop->c.height = b->top - crop->c.top + b->height;
-
-       if (crop->c.left < b->left)
-               crop->c.left = b->left;
-       if (crop->c.left > b->left + b->width)
-               crop->c.left = b->left + b->width;
-       if (crop->c.width > b->left - crop->c.left + b->width)
-               crop->c.width = b->left - crop->c.left + b->width;
-
-       dprintk(DBG_FLOW, "%s: setting cropping rectangle: top=%d, left=%d, "
-                   "width=%d, height=%d\n", __func__, crop->c.top,
-                   crop->c.left, crop->c.width, crop->c.height);
-       dev->crop_current = crop->c;
-       return 0;
-}
-
-/*
- * Wrappers for the v4l2_ioctl_ops functions
- */
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
-static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
-{
-       struct tw68_fh *fh = file->private_data;
-       return videobuf_cgmbuf(tw68_queue(fh), mbuf, 8);
-}
-#endif
-
-static int tw68_reqbufs(struct file *file, void *priv,
-                                       struct v4l2_requestbuffers *p)
-{
-       struct tw68_fh *fh = priv;
-       return videobuf_reqbufs(tw68_queue(fh), p);
-}
-
-static int tw68_querybuf(struct file *file, void *priv,
-                                       struct v4l2_buffer *b)
-{
-       struct tw68_fh *fh = priv;
-       return videobuf_querybuf(tw68_queue(fh), b);
-}
-
-static int tw68_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
-       struct tw68_fh *fh = priv;
-       return videobuf_qbuf(tw68_queue(fh), b);
-}
-
-static int tw68_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
-       struct tw68_fh *fh = priv;
-       return videobuf_dqbuf(tw68_queue(fh), b,
-                               file->f_flags & O_NONBLOCK);
-}
-
-static int tw68_streamon(struct file *file, void *priv,
-                                       enum v4l2_buf_type type)
-{
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
-       int res = tw68_resource(fh);
-
-       dprintk(DBG_FLOW, "%s\n", __func__);
-       if (!res_get(fh, res))
-               return -EBUSY;
-
-       tw68_buffer_requeue(dev, &dev->video_q);
-       return videobuf_streamon(tw68_queue(fh));
-}
-
-static int tw68_streamoff(struct file *file, void *priv,
-                                       enum v4l2_buf_type type)
-{
-       int err;
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
-       int res = tw68_resource(fh);
-
-       dprintk(DBG_FLOW, "%s\n", __func__);
-       err = videobuf_streamoff(tw68_queue(fh));
-       if (err < 0)
-               return err;
-       res_free(fh, res);
-       return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
 /*
  * Used strictly for internal development and debugging, this routine
  * prints out the current register contents for the tw68xx device.
@@ -1928,7 +822,7 @@ static void tw68_dump_regs(struct tw68_dev *dev)
        int i, j, k;
        unsigned char *cptr;
 
-       printk(KERN_DEBUG "Full dump of TW68 registers:\n");
+       pr_info("Full dump of TW68 registers:\n");
        /* First we do the PCI regs, 8 4-byte regs per line */
        for (i = 0; i < 0x100; i += 32) {
                cptr = line;
@@ -1941,7 +835,7 @@ static void tw68_dump_regs(struct tw68_dev *dev)
                        cptr += sprintf(cptr, "%08x ", tw_readl(j));
                *cptr++ = '\n';
                *cptr = 0;
-               printk(KERN_DEBUG "%s", line);
+               pr_info("%s", line);
        }
        /* Next the control regs, which are single-byte, address mod 4 */
        while (i < 0x400) {
@@ -1958,29 +852,24 @@ static void tw68_dump_regs(struct tw68_dev *dev)
                }
                *cptr++ = '\n';
                *cptr = 0;
-               printk(KERN_DEBUG "%s", line);
+               pr_info("%s", line);
        }
 }
 
 static int vidioc_log_status(struct file *file, void *priv)
 {
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev;
+       struct tw68_dev *dev = video_drvdata(file);
 
        tw68_dump_regs(dev);
-       return 0;
+       return v4l2_ctrl_log_status(file, priv);
 }
 
+#ifdef CONFIG_VIDEO_ADV_DEBUG
 static int vidioc_g_register(struct file *file, void *priv,
                              struct v4l2_dbg_register *reg)
 {
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev; /* needed for tw_readb */
+       struct tw68_dev *dev = video_drvdata(file);
 
-       dprintk(DBG_FLOW, "%s\n", __func__);
-       if (!v4l2_chip_match_host(&reg->match))
-               dprintk(DBG_UNEXPECTED, "%s: match failed\n", __func__);
-               return -EINVAL;
        if (reg->size == 1)
                reg->val = tw_readb(reg->reg);
        else
@@ -1989,17 +878,10 @@ static int vidioc_g_register(struct file *file, void *priv,
 }
 
 static int vidioc_s_register(struct file *file, void *priv,
-                               struct v4l2_dbg_register *reg)
+                               const struct v4l2_dbg_register *reg)
 {
-       struct tw68_fh *fh = priv;
-       struct tw68_dev *dev = fh->dev; /* needed for tw_writeb */
+       struct tw68_dev *dev = video_drvdata(file);
 
-       dprintk(DBG_FLOW, "%s: request to set reg 0x%04x to 0x%02x\n",
-               __func__, (unsigned int)reg->reg, (unsigned int)reg->val);
-       if (!v4l2_chip_match_host(&reg->match)) {
-               dprintk(DBG_UNEXPECTED, "%s: match failed\n", __func__);
-               return -EINVAL;
-       }
        if (reg->size == 1)
                tw_writeb(reg->reg, reg->val);
        else
@@ -2008,151 +890,120 @@ static int vidioc_s_register(struct file *file, void *priv,
 }
 #endif
 
+static const struct v4l2_ctrl_ops tw68_ctrl_ops = {
+       .s_ctrl = tw68_s_ctrl,
+};
+
 static const struct v4l2_file_operations video_fops = {
        .owner                  = THIS_MODULE,
-       .open                   = video_open,
-       .release                = video_release,
-       .read                   = video_read,
-       .poll                   = video_poll,
-       .mmap                   = video_mmap,
-       .ioctl                  = video_ioctl2,
+       .open                   = v4l2_fh_open,
+       .release                = vb2_fop_release,
+       .read                   = vb2_fop_read,
+       .poll                   = vb2_fop_poll,
+       .mmap                   = vb2_fop_mmap,
+       .unlocked_ioctl         = video_ioctl2,
 };
 
 static const struct v4l2_ioctl_ops video_ioctl_ops = {
        .vidioc_querycap                = tw68_querycap,
        .vidioc_enum_fmt_vid_cap        = tw68_enum_fmt_vid_cap,
-       .vidioc_reqbufs                 = tw68_reqbufs,
-       .vidioc_querybuf                = tw68_querybuf,
-       .vidioc_qbuf                    = tw68_qbuf,
-       .vidioc_dqbuf                   = tw68_dqbuf,
+       .vidioc_reqbufs                 = vb2_ioctl_reqbufs,
+       .vidioc_create_bufs             = vb2_ioctl_create_bufs,
+       .vidioc_querybuf                = vb2_ioctl_querybuf,
+       .vidioc_qbuf                    = vb2_ioctl_qbuf,
+       .vidioc_dqbuf                   = vb2_ioctl_dqbuf,
        .vidioc_s_std                   = tw68_s_std,
        .vidioc_g_std                   = tw68_g_std,
        .vidioc_enum_input              = tw68_enum_input,
        .vidioc_g_input                 = tw68_g_input,
        .vidioc_s_input                 = tw68_s_input,
-       .vidioc_queryctrl               = tw68_queryctrl,
-       .vidioc_g_ctrl                  = tw68_g_ctrl,
-       .vidioc_s_ctrl                  = tw68_s_ctrl,
-       .vidioc_streamon                = tw68_streamon,
-       .vidioc_streamoff               = tw68_streamoff,
-       .vidioc_g_priority              = tw68_g_priority,
-       .vidioc_s_priority              = tw68_s_priority,
+       .vidioc_streamon                = vb2_ioctl_streamon,
+       .vidioc_streamoff               = vb2_ioctl_streamoff,
        .vidioc_g_fmt_vid_cap           = tw68_g_fmt_vid_cap,
        .vidioc_try_fmt_vid_cap         = tw68_try_fmt_vid_cap,
        .vidioc_s_fmt_vid_cap           = tw68_s_fmt_vid_cap,
-       .vidioc_cropcap                 = tw68_cropcap,
-       .vidioc_g_crop                  = tw68_g_crop,
-       .vidioc_s_crop                  = tw68_s_crop,
-/*
- * Functions not yet implemented / not yet passing tests.
- */
-
-#if 0
-       .vidioc_g_fmt_vbi_cap           = tw68_try_get_set_fmt_vbi_cap,
-       .vidioc_try_fmt_vbi_cap         = tw68_try_get_set_fmt_vbi_cap,
-       .vidioc_s_fmt_vbi_cap           = tw68_try_get_set_fmt_vbi_cap,
-#endif
-       .vidioc_g_audio                 = tw68_g_audio,
-       .vidioc_s_audio                 = tw68_s_audio,
-       .vidioc_g_tuner                 = tw68_g_tuner,
-       .vidioc_s_tuner                 = tw68_s_tuner,
-       .vidioc_g_frequency             = tw68_g_frequency,
-       .vidioc_s_frequency             = tw68_s_frequency,
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
-       .vidiocgmbuf                    = vidiocgmbuf,
-#endif
-#ifdef CONFIG_VIDEO_ADV_DEBUG
        .vidioc_log_status              = vidioc_log_status,
+       .vidioc_subscribe_event         = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event       = v4l2_event_unsubscribe,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
        .vidioc_g_register              = vidioc_g_register,
        .vidioc_s_register              = vidioc_s_register,
 #endif
 };
 
-/* ------------------------------------------------------------------ */
-/* exported stuff                                                     */
-struct video_device tw68_video_template = {
+static struct video_device tw68_video_template = {
        .name                   = "tw68_video",
        .fops                   = &video_fops,
        .ioctl_ops              = &video_ioctl_ops,
-       .minor                  = -1,
+       .release                = video_device_release_empty,
        .tvnorms                = TW68_NORMS,
-       .current_norm           = V4L2_STD_PAL,
-};
-
-struct video_device tw68_radio_template = {
-       .name                   = "tw68_radio",
 };
 
-int tw68_videoport_init(struct tw68_dev *dev)
-{
-       return 0;
-}
-
+/* ------------------------------------------------------------------ */
+/* exported stuff                                                     */
 void tw68_set_tvnorm_hw(struct tw68_dev *dev)
 {
        tw_andorb(TW68_SDT, 0x07, dev->tvnorm->format);
-       return;
 }
 
 int tw68_video_init1(struct tw68_dev *dev)
 {
-       int i;
-
-       dprintk(DBG_FLOW, "%s\n", __func__);
-       /* sanitycheck insmod options */
-       if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
-               gbuffers = 2;
-       if (gbufsz < 0 || gbufsz > gbufsz_max)
-               gbufsz = gbufsz_max;
-       gbufsz = (gbufsz + PAGE_SIZE - 1) & PAGE_MASK;
-
-       /* put some sensible defaults into the data structures ... */
-       for (i = 0; i < CTRLS; i++)
-               tw68_s_ctrl_value(dev, video_ctrls[i].id,
-                                 video_ctrls[i].default_value);
-#if 0
-       if (dev->tda9887_conf && dev->ctl_automute)
-               dev->tda9887_conf |= TDA9887_AUTOMUTE;
-       dev->automute       = 0;
-#endif
-       INIT_LIST_HEAD(&dev->video_q.queued);
-       INIT_LIST_HEAD(&dev->video_q.active);
-       init_timer(&dev->video_q.timeout);
-       dev->video_q.timeout.function   = tw68_buffer_timeout;
-       dev->video_q.timeout.data       = (unsigned long)(&dev->video_q);
-       dev->video_q.dev                = dev;
-       dev->video_q.buf_compat         = tw68_check_video_fmt;
-       dev->video_q.start_dma          = tw68_video_start_dma;
-       tw68_risc_stopper(dev->pci, &dev->video_q.stopper);
-
-       if (tw68_boards[dev->board].video_out)
-               tw68_videoport_init(dev);
-
+       struct v4l2_ctrl_handler *hdl = &dev->hdl;
+
+       v4l2_ctrl_handler_init(hdl, 6);
+       v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, -128, 127, 1, 20);
+       v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 255, 1, 100);
+       v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
+                       V4L2_CID_SATURATION, 0, 255, 1, 128);
+       /* NTSC only */
+       v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
+                       V4L2_CID_HUE, -128, 127, 1, 0);
+       v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
+                       V4L2_CID_COLOR_KILLER, 0, 1, 1, 0);
+       v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
+                       V4L2_CID_CHROMA_AGC, 0, 1, 1, 1);
+       if (hdl->error) {
+               v4l2_ctrl_handler_free(hdl);
+               return hdl->error;
+       }
+       dev->v4l2_dev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_setup(hdl);
        return 0;
 }
 
-int tw68_video_init2(struct tw68_dev *dev)
+int tw68_video_init2(struct tw68_dev *dev, int video_nr)
 {
-       dprintk(DBG_FLOW, "%s\n", __func__);
+       int ret;
+
        set_tvnorm(dev, &tvnorms[0]);
-       video_mux(dev, 0);
-/*
-       tw68_tvaudio_setmut(dev);
-       tw68_tvaudio_setvolume(dev, dev->ctl_volume);
-*/
-       return 0;
-}
 
-/*
- * tw68_irq_video_signalchange
- *
- * TODO:
- * Check for presence of video signal.  If not present, mute audio.
- * If present, log type of signal present.
- */
-void tw68_irq_video_signalchange(struct tw68_dev *dev)
-{
-       return;
+       dev->fmt      = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+       dev->width    = 720;
+       dev->height   = 576;
+       dev->field    = V4L2_FIELD_INTERLACED;
+
+       INIT_LIST_HEAD(&dev->active);
+       dev->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       dev->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       dev->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ | VB2_DMABUF;
+       dev->vidq.ops = &tw68_video_qops;
+       dev->vidq.mem_ops = &vb2_dma_sg_memops;
+       dev->vidq.drv_priv = dev;
+       dev->vidq.gfp_flags = __GFP_DMA32;
+       dev->vidq.buf_struct_size = sizeof(struct tw68_buf);
+       dev->vidq.lock = &dev->lock;
+       dev->vidq.min_buffers_needed = 2;
+       ret = vb2_queue_init(&dev->vidq);
+       if (ret)
+               return ret;
+       dev->vdev = tw68_video_template;
+       dev->vdev.v4l2_dev = &dev->v4l2_dev;
+       dev->vdev.lock = &dev->lock;
+       dev->vdev.queue = &dev->vidq;
+       video_set_drvdata(&dev->vdev, dev);
+       return video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr);
 }
 
 /*
@@ -2171,60 +1022,39 @@ void tw68_irq_video_done(struct tw68_dev *dev, unsigned long status)
         * for the current buffer.
         */
        if (status & TW68_DMAPI) {
-               struct tw68_dmaqueue *q = &dev->video_q;
-               dprintk(DBG_FLOW | DBG_TESTING, "DMAPI interrupt\n");
+               struct tw68_buf *buf;
+
                spin_lock(&dev->slock);
-               /*
-                * tw68_wakeup will take care of the buffer handling,
-                * plus any non-video requirements.
-                */
-               tw68_wakeup(q, &dev->video_fieldcount);
+               buf = list_entry(dev->active.next, struct tw68_buf, list);
+               list_del(&buf->list);
                spin_unlock(&dev->slock);
-               /* Check whether we have gotten into 'stopper' code */
-               reg = tw_readl(TW68_DMAP_PP);
-               if ((reg >= q->stopper.dma) &&
-                   (reg < q->stopper.dma + q->stopper.size)) {
-                       /* Yes - log the information */
-                       dprintk(DBG_FLOW | DBG_TESTING,
-                               "%s: stopper risc code entered\n", __func__);
-               }
+               v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+               buf->vb.v4l2_buf.field = dev->field;
+               buf->vb.v4l2_buf.sequence = dev->seqnr++;
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
                status &= ~(TW68_DMAPI);
                if (0 == status)
                        return;
        }
-       if (status & (TW68_VLOCK | TW68_HLOCK)) { /* lost sync */
-               dprintk(DBG_UNUSUAL, "Lost sync\n");
-       }
-       if (status & TW68_PABORT) {     /* TODO - what should we do? */
-               dprintk(DBG_UNEXPECTED, "PABORT interrupt\n");
-       }
-       if (status & TW68_DMAPERR) {
-               dprintk(DBG_UNEXPECTED, "DMAPERR interrupt\n");
-#if 0
-               /* Stop risc & fifo */
-               tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN);
-               tw_clearl(TW68_INTMASK, dev->board_virqmask);
-               dev->pci_irqmask &= ~dev->board_virqmask;
-#endif
-       }
+       if (status & (TW68_VLOCK | TW68_HLOCK))
+               dev_dbg(&dev->pci->dev, "Lost sync\n");
+       if (status & TW68_PABORT)
+               dev_err(&dev->pci->dev, "PABORT interrupt\n");
+       if (status & TW68_DMAPERR)
+               dev_err(&dev->pci->dev, "DMAPERR interrupt\n");
        /*
         * On TW6800, FDMIS is apparently generated if video input is switched
         * during operation.  Therefore, it is not enabled for that chip.
         */
-       if (status & TW68_FDMIS) {      /* logic error somewhere */
-               dprintk(DBG_UNEXPECTED, "FDMIS interrupt\n");
-               /* Stop risc & fifo */
-//             tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN);
-//             tw_clearl(TW68_INTMASK, dev->board_virqmask);
-//             dev->pci_irqmask &= ~dev->board_virqmask;
-       }
-       if (status & TW68_FFOF) {       /* probably a logic error */
+       if (status & TW68_FDMIS)
+               dev_dbg(&dev->pci->dev, "FDMIS interrupt\n");
+       if (status & TW68_FFOF) {
+               /* probably a logic error */
                reg = tw_readl(TW68_DMAC) & TW68_FIFO_EN;
                tw_clearl(TW68_DMAC, TW68_FIFO_EN);
-               dprintk(DBG_UNUSUAL, "FFOF interrupt\n");
+               dev_dbg(&dev->pci->dev, "FFOF interrupt\n");
                tw_setl(TW68_DMAC, reg);
        }
        if (status & TW68_FFERR)
-               dprintk(DBG_UNEXPECTED, "FFERR interrupt\n");
-       return;
+               dev_dbg(&dev->pci->dev, "FFERR interrupt\n");
 }
index e723efb5e6234118a5ba7e713138061471c0d060..2c8abe26b13b5c2349989b270092da7fb0832dca 100644 (file)
@@ -8,7 +8,11 @@
  *  acknowledged.  Full credit goes to them - any problems within this code
  *  are mine.
  *
- *  Copyright (C) 2009  William M. Brack <wbrack@mmm.com.hk>
+ *  Copyright (C) 2009  William M. Brack
+ *
+ *  Refactored and updated to the latest v4l core frameworks:
+ *
+ *  Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- *  02111-1307  USA
  */
 
 #include <linux/version.h>
-#define        TW68_VERSION_CODE       KERNEL_VERSION(0, 0, 8)
-
 #include <linux/pci.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
 #include <linux/videodev2.h>
-#include <linux/kdev_t.h>
-#include <linux/input.h>
 #include <linux/notifier.h>
 #include <linux/delay.h>
 #include <linux/mutex.h>
-
-#include <asm/io.h>
+#include <linux/io.h>
 
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
+#include <media/videobuf2-dma-sg.h>
 
-#include <media/tuner.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
-#  include <media/ir-common.h>
-#endif
-#include <media/ir-kbd-i2c.h>
-#include <media/videobuf-dma-sg.h>
-
-#include "btcx-risc.h"
 #include "tw68-reg.h"
 
 #define        UNSET   (-1U)
 
-/*
- * dprintk statement within the code use a 'level' argument.  For
- * our purposes, we use the following levels:
- */
-#define        DBG_UNEXPECTED          (1 << 0)
-#define        DBG_UNUSUAL             (1 << 1)
-#define        DBG_TESTING             (1 << 2)
-#define        DBG_BUFF                (1 << 3)
-#define        DBG_FLOW                (1 << 15)
-
 /* system vendor and device ID's */
 #define        PCI_VENDOR_ID_TECHWELL  0x1797
 #define        PCI_DEVICE_ID_6800      0x6800
 #define        PCI_DEVICE_ID_6816_3   0x6812
 #define        PCI_DEVICE_ID_6816_4   0x6813
 
-/* subsystem vendor ID's */
-#define        TW68_PCI_ID_TECHWELL    0x1797
-
-#define TW68_NORMS (\
-       V4L2_STD_NTSC   | V4L2_STD_PAL       | V4L2_STD_SECAM    | \
-       V4L2_STD_PAL_BG | V4L2_STD_PAL_DK    | V4L2_STD_PAL_I    | \
-       V4L2_STD_PAL_M  | V4L2_STD_PAL_Nc    | V4L2_STD_PAL_60   | \
-       V4L2_STD_525_60 | V4L2_STD_625_50    | \
-       V4L2_STD_SECAM_L| V4L2_STD_SECAM_LC  | V4L2_STD_SECAM_DK)
+#define TW68_NORMS ( \
+       V4L2_STD_NTSC    | V4L2_STD_PAL       | V4L2_STD_SECAM    | \
+       V4L2_STD_PAL_M   | V4L2_STD_PAL_Nc    | V4L2_STD_PAL_60)
 
 #define        TW68_VID_INTS   (TW68_FFERR | TW68_PABORT | TW68_DMAPERR | \
                         TW68_FFOF   | TW68_DMAPI)
 #define        TW68_I2C_INTS   (TW68_SBERR | TW68_SBDONE | TW68_SBERR2  | \
                         TW68_SBDONE2)
 
-typedef enum {
+enum tw68_decoder_type {
        TW6800,
        TW6801,
        TW6804,
        TWXXXX,
-} TW68_DECODER_TYPE;
+};
+
 /* ----------------------------------------------------------- */
 /* static data                                                 */
 
@@ -153,164 +124,24 @@ struct tw68_format {
 #define        TW68_BOARD_GENERIC_6802         1
 
 #define        TW68_MAXBOARDS                  16
-#define        TW68_INPUT_MAX                  8
-
-/* ----------------------------------------------------------- */
-/* enums                                                      */
-
-enum tw68_mpeg_type {
-       TW68_MPEG_UNUSED,
-       TW68_MPEG_EMPRESS,
-       TW68_MPEG_DVB,
-};
-
-enum tw68_audio_in {
-       TV      = 1,
-       LINE1   = 2,
-       LINE2   = 3,
-       LINE2_LEFT,
-};
-
-enum tw68_video_out {
-       CCIR656 = 1,
-};
-
-/* Structs for card definition */
-struct tw68_input {
-       char                    *name;          /* text description */
-       unsigned int            vmux;           /* mux value */
-       enum tw68_audio_in      mux;
-       unsigned int            gpio;
-       unsigned int            tv:1;
-};
-
-struct tw68_board {
-       char                    *name;
-       unsigned int            audio_clock;
-
-       /* input switching */
-       unsigned int            gpiomask;
-       struct tw68_input       inputs[TW68_INPUT_MAX];
-       struct tw68_input       radio;
-       struct tw68_input       mute;
-
-       /* i2c chip info */
-       unsigned int            tuner_type;
-       unsigned int            radio_type;
-       unsigned char           tuner_addr;
-       unsigned char           radio_addr;
-
-       unsigned int            tda9887_conf;
-       unsigned int            tuner_config;
-
-       enum tw68_video_out     video_out;
-       enum tw68_mpeg_type     mpeg;
-       unsigned int            vid_port_opts;
-};
-
-#define card_has_radio(dev)    (NULL != tw68_boards[dev->board].radio.name)
-#define card_has_mpeg(dev)     (TW68_MPEG_UNUSED != \
-                                       tw68_boards[dev->board].mpeg)
-#define card_in(dev, n)                (tw68_boards[dev->board].inputs[n])
-#define card(dev)              (tw68_boards[dev->board])
+#define        TW68_INPUT_MAX                  4
 
 /* ----------------------------------------------------------- */
 /* device / file handle status                                 */
 
-#define        RESOURCE_VIDEO                  1
-#define        RESOURCE_VBI                    2
-
-#define        INTERLACE_AUTO                  0
-#define        INTERLACE_ON                    1
-#define        INTERLACE_OFF                   2
-
 #define        BUFFER_TIMEOUT  msecs_to_jiffies(500)   /* 0.5 seconds */
 
 struct tw68_dev;       /* forward delclaration */
 
-/* tvaudio thread status */
-struct tw68_thread {
-       struct task_struct      *thread;
-       unsigned int            scan1;
-       unsigned int            scan2;
-       unsigned int            mode;
-       unsigned int            stopped;
-};
-
 /* buffer for one video/vbi/ts frame */
 struct tw68_buf {
-       /* common v4l buffer stuff -- must be first */
-       struct videobuf_buffer vb;
-
-       /* tw68 specific */
-       struct tw68_format      *fmt;
-       struct tw68_input       *input;
-       unsigned int            top_seen;
-       int (*activate)(struct tw68_dev *dev,
-                       struct tw68_buf *buf,
-                       struct tw68_buf *next);
-       struct btcx_riscmem     risc;
-       unsigned int            bpl;
-};
+       struct vb2_buffer vb;
+       struct list_head list;
 
-struct tw68_dmaqueue {
-       struct tw68_dev         *dev;
-       struct list_head        active;
-       struct list_head        queued;
-       struct timer_list       timeout;
-       struct btcx_riscmem     stopper;
-       int (*buf_compat)(struct tw68_buf *prev,
-                         struct tw68_buf *buf);
-       int (*start_dma)(struct tw68_dev *dev,
-                        struct tw68_dmaqueue *q,
-                        struct tw68_buf *buf);
-};
-
-/* video filehandle status */
-struct tw68_fh {
-       struct tw68_dev         *dev;
-       unsigned int            radio;
-       enum v4l2_buf_type      type;
-       unsigned int            resources;
-       enum v4l2_priority      prio;
-
-       /* video capture */
-       struct tw68_format      *fmt;
-       unsigned int            width, height;
-       struct videobuf_queue   cap;    /* also used for overlay */
-
-       /* vbi capture */
-       struct videobuf_queue   vbi;
-};
-
-/* dmasound dsp status */
-struct tw68_dmasound {
-       struct mutex            lock;
-       int                     minor_mixer;
-       int                     minor_dsp;
-       unsigned int            users_dsp;
-
-       /* mixer */
-       enum tw68_audio_in      input;
-       unsigned int            count;
-       unsigned int            line1;
-       unsigned int            line2;
-
-       /* dsp */
-       unsigned int            afmt;
-       unsigned int            rate;
-       unsigned int            channels;
-       unsigned int            recording_on;
-       unsigned int            dma_running;
-       unsigned int            blocks;
-       unsigned int            blksize;
-       unsigned int            bufsize;
-       struct videobuf_dmabuf  dma;
-       unsigned int            dma_blk;
-       unsigned int            read_offset;
-       unsigned int            read_count;
-       void                    *priv_data;
-       struct snd_pcm_substream        *substream;
+       unsigned int   size;
+       __le32         *cpu;
+       __le32         *jmp;
+       dma_addr_t     dma;
 };
 
 struct tw68_fmt {
@@ -321,58 +152,20 @@ struct tw68_fmt {
        u32                     twformat;
 };
 
-/* ts/mpeg status */
-struct tw68_ts {
-       /* TS capture */
-       int                     nr_packets;
-       int                     nr_bufs;
-};
-
-/* ts/mpeg ops */
-struct tw68_mpeg_ops {
-       enum tw68_mpeg_type     type;
-       struct list_head        next;
-       int                     (*init)(struct tw68_dev *dev);
-       int                     (*fini)(struct tw68_dev *dev);
-       void                    (*signal_change)(struct tw68_dev *dev);
-};
-
-enum tw68_ts_status {
-       TW68_TS_STOPPED,
-       TW68_TS_BUFF_DONE,
-       TW68_TS_STARTED,
-};
-
 /* global device status */
 struct tw68_dev {
-       struct list_head        devlist;
        struct mutex            lock;
        spinlock_t              slock;
-       struct v4l2_prio_state  prio;
+       u16                     instance;
        struct v4l2_device      v4l2_dev;
-       /* workstruct for loading modules */
-       struct work_struct request_module_wk;
-
-       /* insmod option/autodetected */
-       int                     autodetected;
 
        /* various device info */
-       TW68_DECODER_TYPE       vdecoder;
-       unsigned int            resources;
-       struct video_device     *video_dev;
-       struct video_device     *radio_dev;
-       struct video_device     *vbi_dev;
-       struct tw68_dmasound    dmasound;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
-       /* infrared remote */
-       int                     has_remote;
-       struct card_ir          *remote;
-#endif
+       enum tw68_decoder_type  vdecoder;
+       struct video_device     vdev;
+       struct v4l2_ctrl_handler hdl;
 
        /* pci i/o */
-       char                    name[32];
-       int                     nr;
+       char                    *name;
        struct pci_dev          *pci;
        unsigned char           pci_rev, pci_lat;
        u32                     __iomem *lmmio;
@@ -381,75 +174,18 @@ struct tw68_dev {
        /* The irq mask to be used will depend upon the chip type */
        u32                     board_virqmask;
 
-       /* config info */
-       unsigned int            board;
-       unsigned int            tuner_type;
-       unsigned int            radio_type;
-       unsigned char           tuner_addr;
-       unsigned char           radio_addr;
-
-       unsigned int            tda9887_conf;
-       unsigned int            gpio_value;
-
-       /* i2c i/o */
-       struct i2c_algo_bit_data i2c_algo;
-       struct i2c_adapter      i2c_adap;
-       struct i2c_client       i2c_client;
-       u32                     i2c_state;
-       u32                     i2c_done;
-       wait_queue_head_t       i2c_queue;
-       int                     i2c_rc;
-       unsigned char           eedata[256];
-
-       /* video+ts+vbi capture */
-       struct tw68_dmaqueue    video_q;
-       struct tw68_dmaqueue    vbi_q;
-       unsigned int            video_fieldcount;
-       unsigned int            vbi_fieldcount;
+       /* video capture */
+       const struct tw68_format *fmt;
+       unsigned                width, height;
+       unsigned                seqnr;
+       unsigned                field;
+       struct vb2_queue        vidq;
+       struct list_head        active;
 
        /* various v4l controls */
-       struct tw68_tvnorm      *tvnorm;        /* video */
-       struct tw68_tvaudio     *tvaudio;
-#if 0
-       unsigned int            ctl_input;
-       int                     ctl_bright;
-       int                     ctl_contrast;
-       int                     ctl_hue;
-       int                     ctl_saturation;
-       int                     ctl_freq;
-       int                     ctl_mute;       /* audio */
-       int                     ctl_volume;
-       int                     ctl_invert;     /* private */
-       int                     ctl_mirror;
-       int                     ctl_y_odd;
-       int                     ctl_y_even;
-       int                     ctl_automute;
-#endif
-
-       /* crop */
-       struct v4l2_rect        crop_bounds;
-       struct v4l2_rect        crop_defrect;
-       struct v4l2_rect        crop_current;
-
-       /* other global state info */
-       unsigned int            automute;
-       struct tw68_thread      thread;
-       /* input is latest requested by app, hw_input is current hw setting */
-       struct tw68_input       *input;
-       struct tw68_input       *hw_input;
-       unsigned int            hw_mute;
-       int                     last_carrier;
-       int                     nosignal;
-       unsigned int            insuspend;
-
-       /* TW68_MPEG_* */
-       struct tw68_ts          ts;
-       struct tw68_dmaqueue    ts_q;
-       enum tw68_ts_status     ts_state;
-       unsigned int            buff_cnt;
-       struct tw68_mpeg_ops    *mops;
-
-       void (*gate_ctrl)(struct tw68_dev *dev, int open);
+       const struct tw68_tvnorm *tvnorm;       /* video */
+
+       int                     input;
 };
 
 /* ----------------------------------------------------------- */
@@ -473,116 +209,23 @@ struct tw68_dev {
 #define        tw_clearb(reg, bit)     \
                writeb((readb(dev->bmmio+(reg)) & ~(bit)), \
                dev->bmmio + (reg))
-#define tw_call_all(dev, o, f, args...) do {                           \
-       if (dev->gate_ctrl)                                             \
-               dev->gate_ctrl(dev, 1);                                 \
-       v4l2_device_call_all(&(dev)->v4l2_dev, 0, o, f , ##args);       \
-       if (dev->gate_ctrl)                                             \
-               dev->gate_ctrl(dev, 0);                                 \
-} while (0)
 
 #define tw_wait(us) { udelay(us); }
 
-static inline struct tw68_dev *to_tw68_dev(struct v4l2_device *v4l2_dev)
-{
-       return container_of(v4l2_dev, struct tw68_dev, v4l2_dev);
-}
-
-/* ----------------------------------------------------------- */
-/* tw68-core.c                                                */
-
-extern struct list_head  tw68_devlist;
-extern struct mutex tw68_devlist_lock;
-extern unsigned int irq_debug;
-
-int tw68_buffer_count(unsigned int size, unsigned int count);
-void tw68_buffer_queue(struct tw68_dev *dev, struct tw68_dmaqueue *q,
-                     struct tw68_buf *buf);
-void tw68_buffer_timeout(unsigned long data);
-int tw68_set_dmabits(struct tw68_dev *dev);
-void tw68_dma_free(struct videobuf_queue *q, struct tw68_buf *buf);
-void tw68_wakeup(struct tw68_dmaqueue *q, unsigned int *field_count);
-int tw68_buffer_requeue(struct tw68_dev *dev, struct tw68_dmaqueue *q);
-
-/* ----------------------------------------------------------- */
-/* tw68-cards.c                                                */
-
-extern struct tw68_board tw68_boards[];
-extern const unsigned int tw68_bcount;
-extern struct pci_device_id __devinitdata tw68_pci_tbl[];
-
-int tw68_board_init1(struct tw68_dev *dev);
-int tw68_board_init2(struct tw68_dev *dev);
-int tw68_tuner_callback(void *priv, int component, int command, int arg);
-
-/* ----------------------------------------------------------- */
-/* tw68-i2c.c                                                  */
-
-int tw68_i2c_register(struct tw68_dev *dev);
-int tw68_i2c_unregister(struct tw68_dev *dev);
-void tw68_irq_i2c(struct tw68_dev *dev, int status);
-
 /* ----------------------------------------------------------- */
 /* tw68-video.c                                                */
 
-extern unsigned int video_debug;
-extern struct video_device tw68_video_template;
-extern struct video_device tw68_radio_template;
-
-int tw68_videoport_init(struct tw68_dev *dev);
 void tw68_set_tvnorm_hw(struct tw68_dev *dev);
 
 int tw68_video_init1(struct tw68_dev *dev);
-int tw68_video_init2(struct tw68_dev *dev);
-void tw68_irq_video_signalchange(struct tw68_dev *dev);
+int tw68_video_init2(struct tw68_dev *dev, int video_nr);
 void tw68_irq_video_done(struct tw68_dev *dev, unsigned long status);
-
-/* ----------------------------------------------------------- */
-/* tw68-ts.c                                                   */
-
-int tw68_ts_init1(struct tw68_dev *dev);
-int tw68_ts_fini(struct tw68_dev *dev);
-void tw68_irq_ts_done(struct tw68_dev *dev, unsigned long status);
-
-int tw68_ts_register(struct tw68_mpeg_ops *ops);
-void tw68_ts_unregister(struct tw68_mpeg_ops *ops);
-
-int tw68_ts_init_hw(struct tw68_dev *dev);
-
-/* ----------------------------------------------------------- */
-/* tw68-vbi.c                                                  */
-
-extern struct videobuf_queue_ops tw68_vbi_qops;
-extern struct video_device tw68_vbi_template;
-
-int tw68_vbi_init1(struct tw68_dev *dev);
-int tw68_vbi_fini(struct tw68_dev *dev);
-void tw68_irq_vbi_done(struct tw68_dev *dev, unsigned long status);
-
-/* ----------------------------------------------------------- */
-/* tw68-tvaudio.c                                              */
-
-int tw68_tvaudio_rx2mode(u32 rx);
-
-void tw68_tvaudio_setmute(struct tw68_dev *dev);
-void tw68_tvaudio_setinput(struct tw68_dev *dev,
-                             struct tw68_input *in);
-void tw68_tvaudio_setvolume(struct tw68_dev *dev, int level);
-int tw68_tvaudio_getstereo(struct tw68_dev *dev);
-void tw68_tvaudio_init(struct tw68_dev *dev);
-int tw68_tvaudio_init2(struct tw68_dev *dev);
-int tw68_tvaudio_fini(struct tw68_dev *dev);
-int tw68_tvaudio_do_scan(struct tw68_dev *dev);
-int tw_dsp_writel(struct tw68_dev *dev, int reg, u32 value);
-void tw68_enable_i2s(struct tw68_dev *dev);
+int tw68_video_start_dma(struct tw68_dev *dev, struct tw68_buf *buf);
 
 /* ----------------------------------------------------------- */
 /* tw68-risc.c                                                 */
 
-int tw68_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+int tw68_risc_buffer(struct pci_dev *pci, struct tw68_buf *buf,
        struct scatterlist *sglist, unsigned int top_offset,
        unsigned int bottom_offset, unsigned int bpl,
        unsigned int padding, unsigned int lines);
-int tw68_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc);
-int tw68_risc_overlay(struct tw68_fh *fh, struct btcx_riscmem *risc,
-                     int field_type);