Em Tue, 26 Aug 2014 08:33:12 +0200
Hans Verkuil <hverk...@xs4all.nl> escreveu:

> From: Hans Verkuil <hans.verk...@cisco.com>
> 
> Add support for the tw68 driver. The driver has been out-of-tree for many
> years on gitorious: https://gitorious.org/tw68/tw68-v2
> 
> I have refactored and ported that driver to the latest V4L2 core frameworks.
> 
> Tested with my Techwell tw6805a and tw6816 grabber boards.
> 
> Signed-off-by: Hans Verkuil <hans.verk...@cisco.com>

I would be expecting here the  William M. Brack's SOB too.

Also, the best is to add his original work on one patch (without Kbuild
stuff) and your changes on a separate patch. That helps us to identify
what are your contributions to his code, and what was his original
copyright wording.

> ---
>  drivers/media/pci/Kconfig           |    1 +
>  drivers/media/pci/Makefile          |    1 +
>  drivers/media/pci/tw68/Kconfig      |   10 +
>  drivers/media/pci/tw68/Makefile     |    3 +
>  drivers/media/pci/tw68/tw68-core.c  |  434 ++++++++++++++
>  drivers/media/pci/tw68/tw68-reg.h   |  195 +++++++
>  drivers/media/pci/tw68/tw68-risc.c  |  230 ++++++++
>  drivers/media/pci/tw68/tw68-video.c | 1060 
> +++++++++++++++++++++++++++++++++++
>  drivers/media/pci/tw68/tw68.h       |  231 ++++++++
>  9 files changed, 2165 insertions(+)
>  create mode 100644 drivers/media/pci/tw68/Kconfig
>  create mode 100644 drivers/media/pci/tw68/Makefile
>  create mode 100644 drivers/media/pci/tw68/tw68-core.c
>  create mode 100644 drivers/media/pci/tw68/tw68-reg.h
>  create mode 100644 drivers/media/pci/tw68/tw68-risc.c
>  create mode 100644 drivers/media/pci/tw68/tw68-video.c
>  create mode 100644 drivers/media/pci/tw68/tw68.h
> 
> diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
> index 5c16c9c..9332807 100644
> --- a/drivers/media/pci/Kconfig
> +++ b/drivers/media/pci/Kconfig
> @@ -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
> diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile
> index dc2ebbe..73d9c0f 100644
> --- a/drivers/media/pci/Makefile
> +++ b/drivers/media/pci/Makefile
> @@ -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
> index 0000000..5425ba1
> --- /dev/null
> +++ b/drivers/media/pci/tw68/Kconfig
> @@ -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
> index 0000000..3d02f28
> --- /dev/null
> +++ b/drivers/media/pci/tw68/Makefile
> @@ -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-core.c 
> b/drivers/media/pci/tw68/tw68-core.c
> new file mode 100644
> index 0000000..baf93af
> --- /dev/null
> +++ b/drivers/media/pci/tw68/tw68-core.c
> @@ -0,0 +1,434 @@
> +/*
> + *  tw68-core.c
> + *  Core functions for the Techwell 68xx driver
> + *
> + *  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
> + *
> + *  Refactored and updated to the latest v4l core frameworks:
> + *
> + *  Copyright (C) 2014 Hans Verkuil <hverk...@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
> + *  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.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/kmod.h>
> +#include <linux/sound.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/mutex.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/pm.h>
> +
> +#include <media/v4l2-dev.h>
> +#include "tw68.h"
> +#include "tw68-reg.h"
> +
> +MODULE_DESCRIPTION("v4l2 driver module for tw6800 based video capture 
> cards");
> +MODULE_AUTHOR("William M. Brack");
> +MODULE_AUTHOR("Hans Verkuil <hverk...@xs4all.nl>");
> +MODULE_LICENSE("GPL");
> +
> +static unsigned int latency = UNSET;
> +module_param(latency, int, 0444);
> +MODULE_PARM_DESC(latency, "pci latency timer");
> +
> +static unsigned int video_nr[] = {[0 ... (TW68_MAXBOARDS - 1)] = UNSET };
> +module_param_array(video_nr, int, NULL, 0444);
> +MODULE_PARM_DESC(video_nr, "video device number");
> +
> +static unsigned int card[] = {[0 ... (TW68_MAXBOARDS - 1)] = UNSET };
> +module_param_array(card, int, NULL, 0444);
> +MODULE_PARM_DESC(card, "card type");
> +
> +static atomic_t tw68_instance = ATOMIC_INIT(0);
> +
> +/* ------------------------------------------------------------------ */
> +
> +/*
> + * 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[] = {
> +     {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,}
> +};
> +
> +/* ------------------------------------------------------------------ */
> +
> +
> +/*
> + * The device is given a "soft reset". According to the specifications,
> + * after this "all register content remain unchanged", so we also write
> + * to all specified registers manually as well (mostly to manufacturer's
> + * specified reset values)
> + */
> +static int tw68_hw_init1(struct tw68_dev *dev)
> +{
> +     /* Assure all interrupts are disabled */
> +     tw_writel(TW68_INTMASK, 0);             /* 020 */
> +     /* Clear any pending interrupts */
> +     tw_writel(TW68_INTSTAT, 0xffffffff);    /* 01C */
> +     /* Stop risc processor, set default buffer level */
> +     tw_writel(TW68_DMAC, 0x1600);
> +
> +     tw_writeb(TW68_ACNTL, 0x80);    /* 218  soft reset */
> +     msleep(100);
> +
> +     tw_writeb(TW68_INFORM, 0x40);   /* 208  mux0, 27mhz xtal */
> +     tw_writeb(TW68_OPFORM, 0x04);   /* 20C  analog line-lock */
> +     tw_writeb(TW68_HSYNC, 0);       /* 210  color-killer high sens */
> +     tw_writeb(TW68_ACNTL, 0x42);    /* 218  int vref #2, chroma adc off */
> +
> +     tw_writeb(TW68_CROP_HI, 0x02);  /* 21C  Hactive m.s. bits */
> +     tw_writeb(TW68_VDELAY_LO, 0x12);/* 220  Mfg specified reset value */
> +     tw_writeb(TW68_VACTIVE_LO, 0xf0);
> +     tw_writeb(TW68_HDELAY_LO, 0x0f);
> +     tw_writeb(TW68_HACTIVE_LO, 0xd0);
> +
> +     tw_writeb(TW68_CNTRL1, 0xcd);   /* 230  Wide Chroma BPF B/W
> +                                      *      Secam reduction, Adap comb for
> +                                      *      NTSC, Op Mode 1 */
> +
> +     tw_writeb(TW68_VSCALE_LO, 0);   /* 234 */
> +     tw_writeb(TW68_SCALE_HI, 0x11); /* 238 */
> +     tw_writeb(TW68_HSCALE_LO, 0);   /* 23c */
> +     tw_writeb(TW68_BRIGHT, 0);      /* 240 */
> +     tw_writeb(TW68_CONTRAST, 0x5c); /* 244 */
> +     tw_writeb(TW68_SHARPNESS, 0x51);/* 248 */
> +     tw_writeb(TW68_SAT_U, 0x80);    /* 24C */
> +     tw_writeb(TW68_SAT_V, 0x80);    /* 250 */
> +     tw_writeb(TW68_HUE, 0x00);      /* 254 */
> +
> +     /* TODO - Check that none of these are set by control defaults */
> +     tw_writeb(TW68_SHARP2, 0x53);   /* 258  Mfg specified reset val */
> +     tw_writeb(TW68_VSHARP, 0x80);   /* 25C  Sharpness Coring val 8 */
> +     tw_writeb(TW68_CORING, 0x44);   /* 260  CTI and Vert Peak coring */
> +     tw_writeb(TW68_CNTRL2, 0x00);   /* 268  No power saving enabled */
> +     tw_writeb(TW68_SDT, 0x07);      /* 270  Enable shadow reg, auto-det */
> +     tw_writeb(TW68_SDTR, 0x7f);     /* 274  All stds recog, don't start */
> +     tw_writeb(TW68_CLMPG, 0x50);    /* 280  Clamp end at 40 sys clocks */
> +     tw_writeb(TW68_IAGC, 0x22);     /* 284  Mfg specified reset val */
> +     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, 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 */
> +     /* Bit DETV of VCNTL1 helps sync multi cams/chip board */
> +     tw_writeb(TW68_VCNTL1, 0x04);   /* 2A0 */
> +     tw_writeb(TW68_VCNTL2, 0);      /* 2A4 */
> +     tw_writeb(TW68_CKILL, 0x68);    /* 2A8  Mfg specified reset val */
> +     tw_writeb(TW68_COMB, 0x44);     /* 2AC  Mfg specified reset val */
> +     tw_writeb(TW68_LDLY, 0x30);     /* 2B0  Max positive luma delay */
> +     tw_writeb(TW68_MISC1, 0x14);    /* 2B4  Mfg specified reset val */
> +     tw_writeb(TW68_LOOP, 0xa5);     /* 2B8  Mfg specified reset val */
> +     tw_writeb(TW68_MISC2, 0xe0);    /* 2BC  Enable colour killer */
> +     tw_writeb(TW68_MVSN, 0);        /* 2C0 */
> +     tw_writeb(TW68_CLMD, 0x05);     /* 2CC  slice level auto, clamp med. */
> +     tw_writeb(TW68_IDCNTL, 0);      /* 2D0  Writing zero to this register
> +                                      *      selects NTSC ID detection,
> +                                      *      but doesn't change the
> +                                      *      sensitivity (which has a reset
> +                                      *      value of 1E).  Since we are
> +                                      *      not doing auto-detection, it
> +                                      *      has no real effect */
> +     tw_writeb(TW68_CLCNTL1, 0);     /* 2D4 */
> +     tw_writel(TW68_VBIC, 0x03);     /* 010 */
> +     tw_writel(TW68_CAP_CTL, 0x03);  /* 040  Enable both even & odd flds */
> +     tw_writel(TW68_DMAC, 0x2000);   /* patch set had 0x2080 */
> +     tw_writel(TW68_TESTREG, 0);     /* 02C */
> +
> +     /*
> +      * Some common boards, especially inexpensive single-chip models,
> +      * use the GPIO bits 0-3 to control an on-board video-output mux.
> +      * For these boards, we need to set up the GPIO register into
> +      * "normal" mode, set bits 0-3 as output, and then set those bits
> +      * zero.
> +      *
> +      * Eventually, it would be nice if we could identify these boards
> +      * uniquely, and only do this initialisation if the board has been
> +      * identify.  For the moment, however, it shouldn't hurt anything
> +      * to do these steps.
> +      */
> +     tw_writel(TW68_GPIOC, 0);       /* Set the GPIO to "normal", no ints */
> +     tw_writel(TW68_GPOE, 0x0f);     /* Set bits 0-3 to "output" */
> +     tw_writel(TW68_GPDATA, 0);      /* Set all bits to low state */
> +
> +     /* Initialize the device control structures */
> +     mutex_init(&dev->lock);
> +     spin_lock_init(&dev->slock);
> +
> +     /* Initialize any subsystems */
> +     tw68_video_init1(dev);
> +     return 0;
> +}
> +
> +static irqreturn_t tw68_irq(int irq, void *dev_id)
> +{
> +     struct tw68_dev *dev = dev_id;
> +     u32 status, orig;
> +     int loop;
> +
> +     status = orig = tw_readl(TW68_INTSTAT) & dev->pci_irqmask;
> +     /* Check if anything to do */
> +     if (0 == status)
> +             return IRQ_NONE;        /* Nope - return */
> +     for (loop = 0; loop < 10; loop++) {
> +             if (status & dev->board_virqmask)       /* video interrupt */
> +                     tw68_irq_video_done(dev, status);
> +             status = tw_readl(TW68_INTSTAT) & dev->pci_irqmask;
> +             if (0 == status)
> +                     return IRQ_HANDLED;
> +     }
> +     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);
> +     return IRQ_HANDLED;
> +}
> +
> +static int tw68_initdev(struct pci_dev *pci_dev,
> +                                  const struct pci_device_id *pci_id)
> +{
> +     struct tw68_dev *dev;
> +     int vidnr = -1;
> +     int err;
> +
> +     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)
> +             return err;
> +
> +     /* pci init */
> +     dev->pci = pci_dev;
> +     if (pci_enable_device(pci_dev)) {
> +             err = -EIO;
> +             goto fail1;
> +     }
> +
> +     dev->name = dev->v4l2_dev.name;
> +
> +     if (UNSET != latency) {
> +             pr_info("%s: setting pci latency timer to %d\n",
> +                    dev->name, latency);
> +             pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency);
> +     }
> +
> +     /* 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);
> +     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))) {
> +             pr_info("%s: Oops: no 32bit PCI DMA ???\n", dev->name);
> +             err = -EIO;
> +             goto fail1;
> +     }
> +
> +     switch (pci_id->device) {
> +     case PCI_DEVICE_ID_6800:        /* TW6800 */
> +             dev->vdecoder = TW6800;
> +             dev->board_virqmask = TW68_VID_INTS;
> +             break;
> +     case PCI_DEVICE_ID_6801:        /* Video decoder for TW6802 */
> +             dev->vdecoder = TW6801;
> +             dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX;
> +             break;
> +     case PCI_DEVICE_ID_6804:        /* Video decoder for TW6804 */
> +             dev->vdecoder = TW6804;
> +             dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX;
> +             break;
> +     default:
> +             dev->vdecoder = TWXXXX; /* To be announced */
> +             dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX;
> +             break;
> +     }
> +
> +     /* get mmio */
> +     if (!request_mem_region(pci_resource_start(pci_dev, 0),
> +                             pci_resource_len(pci_dev, 0),
> +                             dev->name)) {
> +             err = -EBUSY;
> +             pr_err("%s: can't get MMIO memory @ 0x%llx\n",
> +                     dev->name,
> +                     (unsigned long long)pci_resource_start(pci_dev, 0));
> +             goto fail1;
> +     }
> +     dev->lmmio = ioremap(pci_resource_start(pci_dev, 0),
> +                          pci_resource_len(pci_dev, 0));
> +     dev->bmmio = (__u8 __iomem *)dev->lmmio;
> +     if (NULL == dev->lmmio) {
> +             err = -EIO;
> +             pr_err("%s: can't ioremap() MMIO memory\n",
> +                    dev->name);
> +             goto fail2;
> +     }
> +     /* initialize hardware #1 */
> +     /* Then do any initialisation wanted before interrupts are on */
> +     tw68_hw_init1(dev);
> +
> +     /* get irq */
> +     err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw68_irq,
> +                       IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
> +     if (err < 0) {
> +             pr_err("%s: can't get IRQ %d\n",
> +                    dev->name, pci_dev->irq);
> +             goto fail3;
> +     }
> +
> +     /*
> +      *  Now do remainder of initialisation, first for
> +      *  things unique for this card, then for general board
> +      */
> +     if (dev->instance < TW68_MAXBOARDS)
> +             vidnr = video_nr[dev->instance];
> +     /* initialise video function first */
> +     err = tw68_video_init2(dev, vidnr);
> +     if (err < 0) {
> +             pr_err("%s: can't register video device\n",
> +                    dev->name);
> +             goto fail4;
> +     }
> +     tw_setl(TW68_INTMASK, dev->pci_irqmask);
> +
> +     pr_info("%s: registered device %s\n",
> +            dev->name, video_device_node_name(&dev->vdev));
> +
> +     return 0;
> +
> +fail4:
> +     video_unregister_device(&dev->vdev);
> +fail3:
> +     iounmap(dev->lmmio);
> +fail2:
> +     release_mem_region(pci_resource_start(pci_dev, 0),
> +                        pci_resource_len(pci_dev, 0));
> +fail1:
> +     v4l2_device_unregister(&dev->v4l2_dev);
> +     return err;
> +}
> +
> +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);
> +
> +     /* shutdown subsystems */
> +     tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN);
> +     tw_writel(TW68_INTMASK, 0);
> +
> +     /* unregister */
> +     video_unregister_device(&dev->vdev);
> +     v4l2_ctrl_handler_free(&dev->hdl);
> +
> +     /* release resources */
> +     iounmap(dev->lmmio);
> +     release_mem_region(pci_resource_start(pci_dev, 0),
> +                        pci_resource_len(pci_dev, 0));
> +
> +     v4l2_device_unregister(&dev->v4l2_dev);
> +}
> +
> +#ifdef CONFIG_PM
> +
> +static int tw68_suspend(struct pci_dev *pci_dev , pm_message_t state)
> +{
> +     struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
> +     struct tw68_dev *dev = container_of(v4l2_dev,
> +                             struct tw68_dev, v4l2_dev);
> +
> +     tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN);
> +     dev->pci_irqmask &= ~TW68_VID_INTS;
> +     tw_writel(TW68_INTMASK, 0);
> +
> +     synchronize_irq(pci_dev->irq);
> +
> +     pci_save_state(pci_dev);
> +     pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state));
> +     vb2_discard_done(&dev->vidq);
> +
> +     return 0;
> +}
> +
> +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;
> +
> +     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.*/
> +
> +     msleep(100);
> +
> +     tw68_set_tvnorm_hw(dev);
> +
> +     /*resume unfinished buffer(s)*/
> +     spin_lock_irqsave(&dev->slock, flags);
> +     buf = container_of(dev->active.next, struct tw68_buf, list);
> +
> +     tw68_video_start_dma(dev, buf);
> +
> +     spin_unlock_irqrestore(&dev->slock, flags);
> +
> +     return 0;
> +}
> +#endif
> +
> +/* ----------------------------------------------------------- */
> +
> +static struct pci_driver tw68_pci_driver = {
> +     .name     = "tw68",
> +     .id_table = tw68_pci_tbl,
> +     .probe    = tw68_initdev,
> +     .remove   = tw68_finidev,
> +#ifdef CONFIG_PM
> +     .suspend  = tw68_suspend,
> +     .resume   = tw68_resume
> +#endif
> +};
> +
> +module_pci_driver(tw68_pci_driver);
> diff --git a/drivers/media/pci/tw68/tw68-reg.h 
> b/drivers/media/pci/tw68/tw68-reg.h
> new file mode 100644
> index 0000000..f60b3a8
> --- /dev/null
> +++ b/drivers/media/pci/tw68/tw68-reg.h
> @@ -0,0 +1,195 @@
> +/*
> + *  tw68-reg.h - TW68xx register offsets
> + *
> + *  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) William M. Brack
> + *
> + *  Refactored and updated to the latest v4l core frameworks:
> + *
> + *  Copyright (C) 2014 Hans Verkuil <hverk...@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
> + *  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.
> +*/
> +
> +#ifndef _TW68_REG_H_
> +#define _TW68_REG_H_
> +
> +/* ---------------------------------------------------------------------- */
> +#define      TW68_DMAC               0x000
> +#define      TW68_DMAP_SA            0x004
> +#define      TW68_DMAP_EXE           0x008
> +#define      TW68_DMAP_PP            0x00c
> +#define      TW68_VBIC               0x010
> +#define      TW68_SBUSC              0x014
> +#define      TW68_SBUSSD             0x018
> +#define      TW68_INTSTAT            0x01C
> +#define      TW68_INTMASK            0x020
> +#define      TW68_GPIOC              0x024
> +#define      TW68_GPOE               0x028
> +#define      TW68_TESTREG            0x02C
> +#define      TW68_SBUSRD             0x030
> +#define      TW68_SBUS_TRIG          0x034
> +#define      TW68_CAP_CTL            0x040
> +#define      TW68_SUBSYS             0x054
> +#define      TW68_I2C_RST            0x064
> +#define      TW68_VBIINST            0x06C
> +/* define bits in FIFO and DMAP Control reg */
> +#define      TW68_DMAP_EN            (1 << 0)
> +#define      TW68_FIFO_EN            (1 << 1)
> +/* define the Interrupt Status Register bits */
> +#define      TW68_SBDONE             (1 << 0)
> +#define      TW68_DMAPI              (1 << 1)
> +#define      TW68_GPINT              (1 << 2)
> +#define      TW68_FFOF               (1 << 3)
> +#define      TW68_FDMIS              (1 << 4)
> +#define      TW68_DMAPERR            (1 << 5)
> +#define      TW68_PABORT             (1 << 6)
> +#define      TW68_SBDONE2            (1 << 12)
> +#define      TW68_SBERR2             (1 << 13)
> +#define      TW68_PPERR              (1 << 14)
> +#define      TW68_FFERR              (1 << 15)
> +#define      TW68_DET50              (1 << 16)
> +#define      TW68_FLOCK              (1 << 17)
> +#define      TW68_CCVALID            (1 << 18)
> +#define      TW68_VLOCK              (1 << 19)
> +#define      TW68_FIELD              (1 << 20)
> +#define      TW68_SLOCK              (1 << 21)
> +#define      TW68_HLOCK              (1 << 22)
> +#define      TW68_VDLOSS             (1 << 23)
> +#define      TW68_SBERR              (1 << 24)
> +/* define the i2c control register bits */
> +#define      TW68_SBMODE             (0)
> +#define      TW68_WREN               (1)
> +#define      TW68_SSCLK              (6)
> +#define      TW68_SSDAT              (7)
> +#define      TW68_SBCLK              (8)
> +#define      TW68_WDLEN              (16)
> +#define      TW68_RDLEN              (20)
> +#define      TW68_SBRW               (24)
> +#define      TW68_SBDEV              (25)
> +
> +#define      TW68_SBMODE_B           (1 << TW68_SBMODE)
> +#define      TW68_WREN_B             (1 << TW68_WREN)
> +#define      TW68_SSCLK_B            (1 << TW68_SSCLK)
> +#define      TW68_SSDAT_B            (1 << TW68_SSDAT)
> +#define      TW68_SBRW_B             (1 << TW68_SBRW)
> +
> +#define      TW68_GPDATA             0x100
> +#define      TW68_STATUS1            0x204
> +#define      TW68_INFORM             0x208
> +#define      TW68_OPFORM             0x20C
> +#define      TW68_HSYNC              0x210
> +#define      TW68_ACNTL              0x218
> +#define      TW68_CROP_HI            0x21C
> +#define      TW68_VDELAY_LO          0x220
> +#define      TW68_VACTIVE_LO         0x224
> +#define      TW68_HDELAY_LO          0x228
> +#define      TW68_HACTIVE_LO         0x22C
> +#define      TW68_CNTRL1             0x230
> +#define      TW68_VSCALE_LO          0x234
> +#define      TW68_SCALE_HI           0x238
> +#define      TW68_HSCALE_LO          0x23C
> +#define      TW68_BRIGHT             0x240
> +#define      TW68_CONTRAST           0x244
> +#define      TW68_SHARPNESS          0x248
> +#define      TW68_SAT_U              0x24C
> +#define      TW68_SAT_V              0x250
> +#define      TW68_HUE                0x254
> +#define      TW68_SHARP2             0x258
> +#define      TW68_VSHARP             0x25C
> +#define      TW68_CORING             0x260
> +#define      TW68_VBICNTL            0x264
> +#define      TW68_CNTRL2             0x268
> +#define      TW68_CC_DATA            0x26C
> +#define      TW68_SDT                0x270
> +#define      TW68_SDTR               0x274
> +#define      TW68_RESERV2            0x278
> +#define      TW68_RESERV3            0x27C
> +#define      TW68_CLMPG              0x280
> +#define      TW68_IAGC               0x284
> +#define      TW68_AGCGAIN            0x288
> +#define      TW68_PEAKWT             0x28C
> +#define      TW68_CLMPL              0x290
> +#define      TW68_SYNCT              0x294
> +#define      TW68_MISSCNT            0x298
> +#define      TW68_PCLAMP             0x29C
> +#define      TW68_VCNTL1             0x2A0
> +#define      TW68_VCNTL2             0x2A4
> +#define      TW68_CKILL              0x2A8
> +#define      TW68_COMB               0x2AC
> +#define      TW68_LDLY               0x2B0
> +#define      TW68_MISC1              0x2B4
> +#define      TW68_LOOP               0x2B8
> +#define      TW68_MISC2              0x2BC
> +#define      TW68_MVSN               0x2C0
> +#define      TW68_STATUS2            0x2C4
> +#define      TW68_HFREF              0x2C8
> +#define      TW68_CLMD               0x2CC
> +#define      TW68_IDCNTL             0x2D0
> +#define      TW68_CLCNTL1            0x2D4
> +
> +/* Audio */
> +#define      TW68_ACKI1              0x300
> +#define      TW68_ACKI2              0x304
> +#define      TW68_ACKI3              0x308
> +#define      TW68_ACKN1              0x30C
> +#define      TW68_ACKN2              0x310
> +#define      TW68_ACKN3              0x314
> +#define      TW68_SDIV               0x318
> +#define      TW68_LRDIV              0x31C
> +#define      TW68_ACCNTL             0x320
> +
> +#define      TW68_VSCTL              0x3B8
> +#define      TW68_CHROMAGVAL         0x3BC
> +
> +#define      TW68_F2CROP_HI          0x3DC
> +#define      TW68_F2VDELAY_LO        0x3E0
> +#define      TW68_F2VACTIVE_LO       0x3E4
> +#define      TW68_F2HDELAY_LO        0x3E8
> +#define      TW68_F2HACTIVE_LO       0x3EC
> +#define      TW68_F2CNT              0x3F0
> +#define      TW68_F2VSCALE_LO        0x3F4
> +#define      TW68_F2SCALE_HI         0x3F8
> +#define      TW68_F2HSCALE_LO        0x3FC
> +
> +#define      RISC_INT_BIT            0x08000000
> +#define      RISC_SYNCO              0xC0000000
> +#define      RISC_SYNCE              0xD0000000
> +#define      RISC_JUMP               0xB0000000
> +#define      RISC_LINESTART          0x90000000
> +#define      RISC_INLINE             0xA0000000
> +
> +#define VideoFormatNTSC               0
> +#define VideoFormatNTSCJapan  0
> +#define VideoFormatPALBDGHI   1
> +#define VideoFormatSECAM      2
> +#define VideoFormatNTSC443    3
> +#define VideoFormatPALM               4
> +#define VideoFormatPALN               5
> +#define VideoFormatPALNC      5
> +#define VideoFormatPAL60      6
> +#define VideoFormatAuto               7
> +
> +#define ColorFormatRGB32      0x00
> +#define ColorFormatRGB24      0x10
> +#define ColorFormatRGB16      0x20
> +#define ColorFormatRGB15      0x30
> +#define ColorFormatYUY2               0x40
> +#define ColorFormatBSWAP         0x04
> +#define ColorFormatWSWAP         0x08
> +#define ColorFormatGamma         0x80
> +#endif
> diff --git a/drivers/media/pci/tw68/tw68-risc.c 
> b/drivers/media/pci/tw68/tw68-risc.c
> new file mode 100644
> index 0000000..7439db2
> --- /dev/null
> +++ b/drivers/media/pci/tw68/tw68-risc.c
> @@ -0,0 +1,230 @@
> +/*
> + *  tw68_risc.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
> + *
> + *  Refactored and updated to the latest v4l core frameworks:
> + *
> + *  Copyright (C) 2014 Hans Verkuil <hverk...@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
> + *  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.
> + */
> +
> +#include "tw68.h"
> +
> +/**
> + *  @rp              pointer to current risc program position
> + *  @sglist  pointer to "scatter-gather list" of buffer pointers
> + *  @offset  offset to target memory buffer
> + *  @sync_line       0 -> no sync, 1 -> odd sync, 2 -> even sync
> + *  @bpl     number of bytes per scan line
> + *  @padding number of bytes of padding to add
> + *  @lines   number of lines in field
> + *  @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, bool jump)
> +{
> +     struct scatterlist *sg;
> +     unsigned int line, todo, done;
> +
> +     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_next(sg);
> +             }
> +             if (bpl <= sg_dma_len(sg) - offset) {
> +                     /* fits into current chunk */
> +                     *(rp++) = cpu_to_le32(RISC_LINESTART |
> +                                           /* (offset<<12) |*/  bpl);
> +                     *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
> +                     offset += bpl;
> +             } else {
> +                     /*
> +                      * scanline needs to be split.  Put the start in
> +                      * whatever memory remains using RISC_LINESTART,
> +                      * then the remainder into following addresses
> +                      * given by the scatter-gather list.
> +                      */
> +                     todo = bpl;     /* one full line to be done */
> +                     /* first fragment */
> +                     done = (sg_dma_len(sg) - offset);
> +                     *(rp++) = cpu_to_le32(RISC_LINESTART |
> +                                             (7 << 24) |
> +                                             done);
> +                     *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
> +                     todo -= done;
> +                     sg = sg_next(sg);
> +                     /* succeeding fragments have no offset */
> +                     while (todo > sg_dma_len(sg)) {
> +                             *(rp++) = cpu_to_le32(RISC_INLINE |
> +                                             (done << 12) |
> +                                             sg_dma_len(sg));
> +                             *(rp++) = cpu_to_le32(sg_dma_address(sg));
> +                             todo -= sg_dma_len(sg);
> +                             sg = sg_next(sg);
> +                             done += sg_dma_len(sg);
> +                     }
> +                     if (todo) {
> +                             /* final chunk - offset 0, count 'todo' */
> +                             *(rp++) = cpu_to_le32(RISC_INLINE |
> +                                                     (done << 12) |
> +                                                     todo);
> +                             *(rp++) = cpu_to_le32(sg_dma_address(sg));
> +                     }
> +                     offset = todo;
> +             }
> +             offset += padding;
> +     }
> +
> +     return rp;
> +}
> +
> +/**
> + * 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".
> + *
> + *   @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 tw68_buf *buf,
> +                     struct scatterlist *sglist,
> +                     unsigned int top_offset,
> +                     unsigned int bottom_offset,
> +                     unsigned int bpl,
> +                     unsigned int padding,
> +                     unsigned int lines)
> +{
> +     u32 instructions, fields;
> +     __le32 *rp;
> +
> +     fields = 0;
> +     if (UNSET != top_offset)
> +             fields++;
> +     if (UNSET != bottom_offset)
> +             fields++;
> +     /*
> +      * estimate risc mem: worst case is one write per page border +
> +      * 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) + 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 = buf->cpu;
> +     if (UNSET != top_offset)        /* generates SYNCO */
> +             rp = tw68_risc_field(rp, sglist, top_offset, 1,
> +                                  bpl, padding, lines, true);
> +     if (UNSET != bottom_offset)     /* generates SYNCE */
> +             rp = tw68_risc_field(rp, sglist, bottom_offset, 2,
> +                                  bpl, padding, lines, top_offset == UNSET);
> +
> +     /* save pointer to jmp instruction address */
> +     buf->jmp = rp;
> +     buf->cpu[1] = cpu_to_le32(buf->dma + 8);
> +     /* assure risc buffer hasn't overflowed */
> +     BUG_ON((buf->jmp - buf->cpu + 2) * sizeof(buf->cpu[0]) > buf->size);
> +     return 0;
> +}
> +
> +#if 0
> +/* ------------------------------------------------------------------ */
> +/* debug helper code                                                  */
> +
> +static void tw68_risc_decode(u32 risc, u32 addr)
> +{
> +#define      RISC_OP(reg)    (((reg) >> 28) & 7)
> +     static struct instr_details {
> +             char *name;
> +             u8 has_data_type;
> +             u8 has_byte_info;
> +             u8 has_addr;
> +     } instr[8] = {
> +             [RISC_OP(RISC_SYNCO)]     = {"syncOdd", 0, 0, 0},
> +             [RISC_OP(RISC_SYNCE)]     = {"syncEven", 0, 0, 0},
> +             [RISC_OP(RISC_JUMP)]      = {"jump", 0, 0, 1},
> +             [RISC_OP(RISC_LINESTART)] = {"lineStart", 1, 1, 1},
> +             [RISC_OP(RISC_INLINE)]    = {"inline", 1, 1, 1},
> +     };
> +     u32 p;
> +
> +     p = RISC_OP(risc);
> +     if (!(risc & 0x80000000) || !instr[p].name) {
> +             pr_debug("0x%08x [ INVALID ]\n", risc);
> +             return;
> +     }
> +     pr_debug("0x%08x %-9s IRQ=%d",
> +             risc, instr[p].name, (risc >> 27) & 1);
> +     if (instr[p].has_data_type)
> +             pr_debug(" Type=%d", (risc >> 24) & 7);
> +     if (instr[p].has_byte_info)
> +             pr_debug(" Start=0x%03x Count=%03u",
> +                     (risc >> 12) & 0xfff, risc & 0xfff);
> +     if (instr[p].has_addr)
> +             pr_debug(" StartAddr=0x%08x", addr);
> +     pr_debug("\n");
> +}
> +
> +void tw68_risc_program_dump(struct tw68_core *core, struct tw68_buf *buf)
> +{
> +     const __le32 *addr;
> +
> +     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));
> +}
> +#endif
> diff --git a/drivers/media/pci/tw68/tw68-video.c 
> b/drivers/media/pci/tw68/tw68-video.c
> new file mode 100644
> index 0000000..66fae23
> --- /dev/null
> +++ b/drivers/media/pci/tw68/tw68-video.c
> @@ -0,0 +1,1060 @@
> +/*
> + *  tw68 functions to handle video data
> + *
> + *  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
> + *
> + *  Refactored and updated to the latest v4l core frameworks:
> + *
> + *  Copyright (C) 2014 Hans Verkuil <hverk...@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
> + *  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.
> + */
> +
> +#include <linux/module.h>
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-event.h>
> +#include <media/videobuf2-dma-sg.h>
> +
> +#include "tw68.h"
> +#include "tw68-reg.h"
> +
> +/* ------------------------------------------------------------------ */
> +/* data structs for video                                             */
> +/*
> + * FIXME -
> + * Note that the saa7134 has formats, e.g. YUV420, which are classified
> + * 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 const struct tw68_format formats[] = {
> +     {
> +             .name           = "15 bpp RGB, le",
> +             .fourcc         = V4L2_PIX_FMT_RGB555,
> +             .depth          = 16,
> +             .twformat       = ColorFormatRGB15,
> +     }, {
> +             .name           = "15 bpp RGB, be",
> +             .fourcc         = V4L2_PIX_FMT_RGB555X,
> +             .depth          = 16,
> +             .twformat       = ColorFormatRGB15 | ColorFormatBSWAP,
> +     }, {
> +             .name           = "16 bpp RGB, le",
> +             .fourcc         = V4L2_PIX_FMT_RGB565,
> +             .depth          = 16,
> +             .twformat       = ColorFormatRGB16,
> +     }, {
> +             .name           = "16 bpp RGB, be",
> +             .fourcc         = V4L2_PIX_FMT_RGB565X,
> +             .depth          = 16,
> +             .twformat       = ColorFormatRGB16 | ColorFormatBSWAP,
> +     }, {
> +             .name           = "24 bpp RGB, le",
> +             .fourcc         = V4L2_PIX_FMT_BGR24,
> +             .depth          = 24,
> +             .twformat       = ColorFormatRGB24,
> +     }, {
> +             .name           = "24 bpp RGB, be",
> +             .fourcc         = V4L2_PIX_FMT_RGB24,
> +             .depth          = 24,
> +             .twformat       = ColorFormatRGB24 | ColorFormatBSWAP,
> +     }, {
> +             .name           = "32 bpp RGB, le",
> +             .fourcc         = V4L2_PIX_FMT_BGR32,
> +             .depth          = 32,
> +             .twformat       = ColorFormatRGB32,
> +     }, {
> +             .name           = "32 bpp RGB, be",
> +             .fourcc         = V4L2_PIX_FMT_RGB32,
> +             .depth          = 32,
> +             .twformat       = ColorFormatRGB32 | ColorFormatBSWAP |
> +                               ColorFormatWSWAP,
> +     }, {
> +             .name           = "4:2:2 packed, YUYV",
> +             .fourcc         = V4L2_PIX_FMT_YUYV,
> +             .depth          = 16,
> +             .twformat       = ColorFormatYUY2,
> +     }, {
> +             .name           = "4:2:2 packed, UYVY",
> +             .fourcc         = V4L2_PIX_FMT_UYVY,
> +             .depth          = 16,
> +             .twformat       = ColorFormatYUY2 | ColorFormatBSWAP,
> +     }
> +};
> +#define FORMATS ARRAY_SIZE(formats)
> +
> +#define NORM_625_50                  \
> +             .h_delay        = 3,    \
> +             .h_delay0       = 133,  \
> +             .h_start        = 0,    \
> +             .h_stop         = 719,  \
> +             .v_delay        = 24,   \
> +             .vbi_v_start_0  = 7,    \
> +             .vbi_v_stop_0   = 22,   \
> +             .video_v_start  = 24,   \
> +             .video_v_stop   = 311,  \
> +             .vbi_v_start_1  = 319
> +
> +#define NORM_525_60                  \
> +             .h_delay        = 8,    \
> +             .h_delay0       = 138,  \
> +             .h_start        = 0,    \
> +             .h_stop         = 719,  \
> +             .v_delay        = 22,   \
> +             .vbi_v_start_0  = 10,   \
> +             .vbi_v_stop_0   = 21,   \
> +             .video_v_start  = 22,   \
> +             .video_v_stop   = 262,  \
> +             .vbi_v_start_1  = 273
> +
> +/*
> + * The following table is searched by tw68_s_std, first for a specific
> + * match, then for an entry which contains the desired id.  The table
> + * entries should therefore be ordered in ascending order of specificity.
> + */
> +static const struct tw68_tvnorm tvnorms[] = {
> +     {
> +             .name           = "PAL", /* autodetect */
> +             .id             = V4L2_STD_PAL,
> +             NORM_625_50,
> +
> +             .sync_control   = 0x18,
> +             .luma_control   = 0x40,
> +             .chroma_ctrl1   = 0x81,
> +             .chroma_gain    = 0x2a,
> +             .chroma_ctrl2   = 0x06,
> +             .vgate_misc     = 0x1c,
> +             .format         = VideoFormatPALBDGHI,
> +     }, {
> +             .name           = "NTSC",
> +             .id             = V4L2_STD_NTSC,
> +             NORM_525_60,
> +
> +             .sync_control   = 0x59,
> +             .luma_control   = 0x40,
> +             .chroma_ctrl1   = 0x89,
> +             .chroma_gain    = 0x2a,
> +             .chroma_ctrl2   = 0x0e,
> +             .vgate_misc     = 0x18,
> +             .format         = VideoFormatNTSC,
> +     }, {
> +             .name           = "SECAM",
> +             .id             = V4L2_STD_SECAM,
> +             NORM_625_50,
> +
> +             .sync_control   = 0x18,
> +             .luma_control   = 0x1b,
> +             .chroma_ctrl1   = 0xd1,
> +             .chroma_gain    = 0x80,
> +             .chroma_ctrl2   = 0x00,
> +             .vgate_misc     = 0x1c,
> +             .format         = VideoFormatSECAM,
> +     }, {
> +             .name           = "PAL-M",
> +             .id             = V4L2_STD_PAL_M,
> +             NORM_525_60,
> +
> +             .sync_control   = 0x59,
> +             .luma_control   = 0x40,
> +             .chroma_ctrl1   = 0xb9,
> +             .chroma_gain    = 0x2a,
> +             .chroma_ctrl2   = 0x0e,
> +             .vgate_misc     = 0x18,
> +             .format         = VideoFormatPALM,
> +     }, {
> +             .name           = "PAL-Nc",
> +             .id             = V4L2_STD_PAL_Nc,
> +             NORM_625_50,
> +
> +             .sync_control   = 0x18,
> +             .luma_control   = 0x40,
> +             .chroma_ctrl1   = 0xa1,
> +             .chroma_gain    = 0x2a,
> +             .chroma_ctrl2   = 0x06,
> +             .vgate_misc     = 0x1c,
> +             .format         = VideoFormatPALNC,
> +     }, {
> +             .name           = "PAL-60",
> +             .id             = V4L2_STD_PAL_60,
> +             .h_delay        = 186,
> +             .h_start        = 0,
> +             .h_stop         = 719,
> +             .v_delay        = 26,
> +             .video_v_start  = 23,
> +             .video_v_stop   = 262,
> +             .vbi_v_start_0  = 10,
> +             .vbi_v_stop_0   = 21,
> +             .vbi_v_start_1  = 273,
> +
> +             .sync_control   = 0x18,
> +             .luma_control   = 0x40,
> +             .chroma_ctrl1   = 0x81,
> +             .chroma_gain    = 0x2a,
> +             .chroma_ctrl2   = 0x06,
> +             .vgate_misc     = 0x1c,
> +             .format         = VideoFormatPAL60,
> +     }
> +};
> +#define TVNORMS ARRAY_SIZE(tvnorms)
> +
> +static const struct tw68_format *format_by_fourcc(unsigned int fourcc)
> +{
> +     unsigned int i;
> +
> +     for (i = 0; i < FORMATS; i++)
> +             if (formats[i].fourcc == fourcc)
> +                     return formats+i;
> +     return NULL;
> +}
> +
> +
> +/* ------------------------------------------------------------------ */
> +/*
> + * 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, const struct tw68_tvnorm *norm)
> +{
> +     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);
> +     }
> +}
> +
> +/*
> + * tw68_set_scale
> + *
> + * Scaling and Cropping for video decoding
> + *
> + * We are working with 3 values for horizontal and vertical - scale,
> + * delay and active.
> + *
> + * HACTIVE represent the actual number of pixels in the "usable" image,
> + * 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
> + *
> + * 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
> + * vertical sync and VBI).
> + *
> + * Note that the number of bytes reaching the FIFO (and hence needing
> + * to be processed by the DMAP program) is completely dependent upon
> + * 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
> + */
> +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;
> +     int comb;
> +
> +     if (V4L2_FIELD_HAS_BOTH(field)) /* if field is interlaced */
> +             height /= 2;            /* we must set for 1-frame */
> +
> +     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),
> +             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 = norm->h_delay0;
> +             break;
> +     default:
> +             hdelay = norm->h_delay;
> +             break;
> +     }
> +
> +     hdelay += norm->h_start;
> +     hactive = norm->h_stop - norm->h_start + 1;
> +
> +     hscale = (hactive * 256) / (width);
> +
> +     vdelay = norm->v_delay;
> +     vactive = ((norm->id & V4L2_STD_525_60) ? 524 : 624) / 2 - 
> norm->video_v_start;
> +     vscale = (vactive * 256) / height;
> +
> +     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));
> +     pr_debug("%s: hactive=%d, hdelay=%d, hscale=%d; "
> +             "vactive=%d, vdelay=%d, vscale=%d\n", __func__,
> +             hactive, hdelay, hscale, vactive, vdelay, vscale);
> +
> +     comb =  ((vdelay & 0x300)  >> 2) |
> +             ((vactive & 0x300) >> 4) |
> +             ((hdelay & 0x300)  >> 6) |
> +             ((hactive & 0x300) >> 8);
> +     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);
> +     tw_writeb(TW68_VDELAY_LO, vdelay & 0xff);
> +     tw_writeb(TW68_VACTIVE_LO, vactive & 0xff);
> +     tw_writeb(TW68_HDELAY_LO, hdelay & 0xff);
> +     tw_writeb(TW68_HACTIVE_LO, hactive & 0xff);
> +
> +     comb = ((vscale & 0xf00) >> 4) | ((hscale & 0xf00) >> 8);
> +     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);
> +     tw_writeb(TW68_HSCALE_LO, hscale);
> +
> +     return 0;
> +}
> +
> +/* ------------------------------------------------------------------ */
> +
> +int tw68_video_start_dma(struct tw68_dev *dev, struct tw68_buf *buf)
> +{
> +     /* Set cropping and scaling */
> +     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->dma));
> +     /* Clear any pending interrupts */
> +     tw_writel(TW68_INTSTAT, dev->board_virqmask);
> +     /* Enable the risc engine and the fifo */
> +     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);
> +     return 0;
> +}
> +
> +/* ------------------------------------------------------------------ */
> +
> +/* 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;

The above seems to be wrong, as PAGE_SIZE is not always 4096.

IMHO, the correct would be to do size /= PAGE_SIZE, if the intent is
to return the number of pages.

> +     return 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)
> +{
> +     unsigned int maxcount;
> +
> +     maxcount = 1024 / tw68_buffer_pages(size);

Again, the 1024 here looks weird, as it seems to also be assuming a
4096 page size.

> +     if (count > maxcount)
> +             count = maxcount;
> +     return count;
> +}
> +
> +/* ------------------------------------------------------------- */
> +/* vb2 queue operations                                          */
> +
> +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_dev *dev = vb2_get_drv_priv(q);
> +     unsigned tot_bufs = q->num_buffers + *num_buffers;
> +
> +     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;
> +
> +     return 0;
> +}
> +
> +/*
> + * 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.
> + *
> + * 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 tw68_buf_queue(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 tw68_buf *prev;
> +     unsigned long flags;
> +
> +     spin_lock_irqsave(&dev->slock, flags);
> +
> +     /* 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_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 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;
> +
> +     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);
> +
> +     rc = dma_map_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
> +     if (!rc)
> +             return -EIO;
> +
> +     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_FIELD_BOTTOM:
> +             tw68_risc_buffer(dev->pci, buf, dma->sgl,
> +                              UNSET, 0, bpl, 0, dev->height);
> +             break;
> +     case V4L2_FIELD_SEQ_TB:
> +             tw68_risc_buffer(dev->pci, buf, dma->sgl,
> +                              0, bpl * (dev->height >> 1),
> +                              bpl, 0, dev->height >> 1);
> +             break;
> +     case V4L2_FIELD_SEQ_BT:
> +             tw68_risc_buffer(dev->pci, buf, dma->sgl,
> +                              bpl * (dev->height >> 1), 0,
> +                              bpl, 0, dev->height >> 1);
> +             break;
> +     case V4L2_FIELD_INTERLACED:
> +     default:
> +             tw68_risc_buffer(dev->pci, buf, dma->sgl,
> +                              0, bpl, bpl, bpl, dev->height >> 1);
> +             break;
> +     }
> +     return 0;
> +}
> +
> +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);

Please put the above statement into a single line.

> +
> +     dev->seqnr = 0;
> +     tw68_video_start_dma(dev, buf);
> +     return 0;
> +}
> +
> +static void tw68_stop_streaming(struct vb2_queue *q)
> +{
> +     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);

Same here. Or if you want to use multiple lines, do it like:

                struct tw68_buf *buf;

                buf = container_of(dev->active.next, struct tw68_buf, list);

(personally, I prefer this way)

Same on other similar places.

> +
> +             list_del(&buf->list);
> +             vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> +     }
> +}
> +
> +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)
> +{
> +     struct tw68_dev *dev =
> +             container_of(ctrl->handler, struct tw68_dev, hdl);
> +
> +     switch (ctrl->id) {
> +     case V4L2_CID_BRIGHTNESS:
> +             tw_writeb(TW68_BRIGHT, ctrl->val);
> +             break;
> +     case V4L2_CID_HUE:
> +             tw_writeb(TW68_HUE, ctrl->val);
> +             break;
> +     case V4L2_CID_CONTRAST:
> +             tw_writeb(TW68_CONTRAST, ctrl->val);
> +             break;
> +     case V4L2_CID_SATURATION:
> +             tw_writeb(TW68_SAT_U, ctrl->val);
> +             tw_writeb(TW68_SAT_V, ctrl->val);
> +             break;
> +     case V4L2_CID_COLOR_KILLER:
> +             if (ctrl->val)
> +                     tw_andorb(TW68_MISC2, 0xe0, 0xe0);
> +             else
> +                     tw_andorb(TW68_MISC2, 0xe0, 0x00);
> +             break;
> +     case V4L2_CID_CHROMA_AGC:
> +             if (ctrl->val)
> +                     tw_andorb(TW68_LOOP, 0x30, 0x20);
> +             else
> +                     tw_andorb(TW68_LOOP, 0x30, 0x00);
> +             break;
> +     }
> +     return 0;
> +}
> +
> +/* ------------------------------------------------------------------ */
> +
> +/*
> + * 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);
> +
> +     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 * (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_dev *dev = video_drvdata(file);
> +     const struct tw68_format *fmt;
> +     enum v4l2_field field;
> +     unsigned int maxh;
> +
> +     fmt = format_by_fourcc(f->fmt.pix.pixelformat);
> +     if (NULL == fmt)
> +             return -EINVAL;
> +
> +     field = f->fmt.pix.field;
> +     maxh  = (dev->tvnorm->id & V4L2_STD_525_60) ? 480 : 576;
> +
> +     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:
> +             field = (f->fmt.pix.height > maxh / 2)
> +                     ? V4L2_FIELD_INTERLACED
> +                     : V4L2_FIELD_BOTTOM;
> +             break;
> +     }
> +
> +     f->fmt.pix.field = field;
> +     if (f->fmt.pix.width  < 48)
> +             f->fmt.pix.width  = 48;
> +     if (f->fmt.pix.height < 32)
> +             f->fmt.pix.height = 32;
> +     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;
> +     f->fmt.pix.bytesperline =
> +             (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;
> +}
> +
> +/*
> + * Note that tw68_s_fmt_vid_cap sets the information into the fh structure,
> + * and it will be used for all future new buffers.  However, there could be
> + * some number of buffers on the "active" chain which will be filled before
> + * the change takes place.
> + */
> +static int tw68_s_fmt_vid_cap(struct file *file, void *priv,
> +                                     struct v4l2_format *f)
> +{
> +     struct tw68_dev *dev = video_drvdata(file);
> +     int err;
> +
> +     err = tw68_try_fmt_vid_cap(file, priv, f);
> +     if (0 != err)
> +             return err;
> +
> +     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_dev *dev = video_drvdata(file);
> +     unsigned int n;
> +
> +     n = i->index;
> +     if (n >= TW68_INPUT_MAX)
> +             return -EINVAL;
> +     i->index = n;
> +     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->input) {
> +             int v1 = tw_readb(TW68_STATUS1);
> +             int v2 = tw_readb(TW68_MVSN);
> +
> +             if (0 != (v1 & (1 << 7)))
> +                     i->status |= V4L2_IN_ST_NO_SYNC;
> +             if (0 != (v1 & (1 << 6)))
> +                     i->status |= V4L2_IN_ST_NO_H_LOCK;
> +             if (0 != (v1 & (1 << 2)))
> +                     i->status |= V4L2_IN_ST_NO_SIGNAL;
> +             if (0 != (v1 & 1 << 1))
> +                     i->status |= V4L2_IN_ST_NO_COLOR;
> +             if (0 != (v2 & (1 << 2)))
> +                     i->status |= V4L2_IN_ST_MACROVISION;
> +     }
> +     i->std = video_devdata(file)->tvnorms;
> +     return 0;
> +}
> +
> +static int tw68_g_input(struct file *file, void *priv, unsigned int *i)
> +{
> +     struct tw68_dev *dev = video_drvdata(file);
> +
> +     *i = dev->input;
> +     return 0;
> +}
> +
> +static int tw68_s_input(struct file *file, void *priv, unsigned int i)
> +{
> +     struct tw68_dev *dev = video_drvdata(file);
> +
> +     if (i >= TW68_INPUT_MAX)
> +             return -EINVAL;
> +     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_dev *dev = video_drvdata(file);
> +
> +     strcpy(cap->driver, "tw68");
> +     strlcpy(cap->card, "Techwell Capture Card",
> +             sizeof(cap->card));
> +     sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
> +     cap->device_caps =
> +             V4L2_CAP_VIDEO_CAPTURE |
> +             V4L2_CAP_READWRITE |
> +             V4L2_CAP_STREAMING;
> +
> +     cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
> +     return 0;
> +}
> +
> +static int tw68_s_std(struct file *file, void *priv, v4l2_std_id id)
> +{
> +     struct tw68_dev *dev = video_drvdata(file);
> +     unsigned int i;
> +
> +     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)
> +                     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)
> +                             break;
> +     }
> +     /* If still not matched, give up */
> +     if (i == TVNORMS)
> +             return -EINVAL;
> +
> +     set_tvnorm(dev, &tvnorms[i]);   /* do the actual setting */
> +     return 0;
> +}
> +
> +static int tw68_g_std(struct file *file, void *priv, v4l2_std_id *id)
> +{
> +     struct tw68_dev *dev = video_drvdata(file);
> +
> +     *id = dev->tvnorm->id;
> +     return 0;
> +}
> +
> +static int tw68_enum_fmt_vid_cap(struct file *file, void  *priv,
> +                                     struct v4l2_fmtdesc *f)
> +{
> +     if (f->index >= FORMATS)
> +             return -EINVAL;
> +
> +     strlcpy(f->description, formats[f->index].name,
> +             sizeof(f->description));
> +
> +     f->pixelformat = formats[f->index].fourcc;
> +
> +     return 0;
> +}
> +
> +/*
> + * Used strictly for internal development and debugging, this routine
> + * prints out the current register contents for the tw68xx device.
> + */
> +static void tw68_dump_regs(struct tw68_dev *dev)
> +{
> +     unsigned char line[80];
> +     int i, j, k;
> +     unsigned char *cptr;
> +
> +     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;
> +             cptr += sprintf(cptr, "%03x  ", i);
> +             /* j steps through the next 4 words */
> +             for (j = i; j < i + 16; j += 4)
> +                     cptr += sprintf(cptr, "%08x ", tw_readl(j));
> +             *cptr++ = ' ';
> +             for (; j < i + 32; j += 4)
> +                     cptr += sprintf(cptr, "%08x ", tw_readl(j));
> +             *cptr++ = '\n';
> +             *cptr = 0;
> +             pr_info("%s", line);
> +     }
> +     /* Next the control regs, which are single-byte, address mod 4 */
> +     while (i < 0x400) {
> +             cptr = line;
> +             cptr += sprintf(cptr, "%03x ", i);
> +             /* Print out 4 groups of 4 bytes */
> +             for (j = 0; j < 4; j++) {
> +                     for (k = 0; k < 4; k++) {
> +                             cptr += sprintf(cptr, "%02x ",
> +                                     tw_readb(i));
> +                             i += 4;
> +                     }
> +                     *cptr++ = ' ';
> +             }
> +             *cptr++ = '\n';
> +             *cptr = 0;
> +             pr_info("%s", line);
> +     }
> +}
> +
> +static int vidioc_log_status(struct file *file, void *priv)
> +{
> +     struct tw68_dev *dev = video_drvdata(file);
> +
> +     tw68_dump_regs(dev);
> +     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_dev *dev = video_drvdata(file);
> +
> +     if (reg->size == 1)
> +             reg->val = tw_readb(reg->reg);
> +     else
> +             reg->val = tw_readl(reg->reg);
> +     return 0;
> +}
> +
> +static int vidioc_s_register(struct file *file, void *priv,
> +                             const struct v4l2_dbg_register *reg)
> +{
> +     struct tw68_dev *dev = video_drvdata(file);
> +
> +     if (reg->size == 1)
> +             tw_writeb(reg->reg, reg->val);
> +     else
> +             tw_writel(reg->reg & 0xffff, reg->val);
> +     return 0;
> +}
> +#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                   = 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                 = 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_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_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
> +};
> +
> +static struct video_device tw68_video_template = {
> +     .name                   = "tw68_video",
> +     .fops                   = &video_fops,
> +     .ioctl_ops              = &video_ioctl_ops,
> +     .release                = video_device_release_empty,
> +     .tvnorms                = TW68_NORMS,
> +};
> +
> +/* ------------------------------------------------------------------ */
> +/* exported stuff                                                     */
> +void tw68_set_tvnorm_hw(struct tw68_dev *dev)
> +{
> +     tw_andorb(TW68_SDT, 0x07, dev->tvnorm->format);
> +}
> +
> +int tw68_video_init1(struct tw68_dev *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 video_nr)
> +{
> +     int ret;
> +
> +     set_tvnorm(dev, &tvnorms[0]);
> +
> +     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);
> +}
> +
> +/*
> + * tw68_irq_video_done
> + */
> +void tw68_irq_video_done(struct tw68_dev *dev, unsigned long status)
> +{
> +     __u32 reg;
> +
> +     /* reset interrupts handled by this routine */
> +     tw_writel(TW68_INTSTAT, status);
> +     /*
> +      * Check most likely first
> +      *
> +      * DMAPI shows we have reached the end of the risc code
> +      * for the current buffer.
> +      */
> +     if (status & TW68_DMAPI) {
> +             struct tw68_buf *buf;
> +
> +             spin_lock(&dev->slock);
> +             buf = list_entry(dev->active.next, struct tw68_buf, list);
> +             list_del(&buf->list);
> +             spin_unlock(&dev->slock);
> +             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))
> +             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)
> +             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);
> +             dev_dbg(&dev->pci->dev, "FFOF interrupt\n");
> +             tw_setl(TW68_DMAC, reg);
> +     }
> +     if (status & TW68_FFERR)
> +             dev_dbg(&dev->pci->dev, "FFERR interrupt\n");
> +}
> diff --git a/drivers/media/pci/tw68/tw68.h b/drivers/media/pci/tw68/tw68.h
> new file mode 100644
> index 0000000..2c8abe2
> --- /dev/null
> +++ b/drivers/media/pci/tw68/tw68.h
> @@ -0,0 +1,231 @@
> +/*
> + *  tw68 driver common header file
> + *
> + *  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
> + *
> + *  Refactored and updated to the latest v4l core frameworks:
> + *
> + *  Copyright (C) 2014 Hans Verkuil <hverk...@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
> + *  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.
> + */
> +
> +#include <linux/version.h>
> +#include <linux/pci.h>
> +#include <linux/videodev2.h>
> +#include <linux/notifier.h>
> +#include <linux/delay.h>
> +#include <linux/mutex.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 "tw68-reg.h"
> +
> +#define      UNSET   (-1U)
> +
> +/* system vendor and device ID's */
> +#define      PCI_VENDOR_ID_TECHWELL  0x1797
> +#define      PCI_DEVICE_ID_6800      0x6800
> +#define      PCI_DEVICE_ID_6801      0x6801
> +#define      PCI_DEVICE_ID_AUDIO2    0x6802
> +#define      PCI_DEVICE_ID_TS3       0x6803
> +#define      PCI_DEVICE_ID_6804      0x6804
> +#define      PCI_DEVICE_ID_AUDIO5    0x6805
> +#define      PCI_DEVICE_ID_TS6       0x6806
> +
> +/* tw6816 based cards */
> +#define      PCI_DEVICE_ID_6816_1   0x6810
> +#define      PCI_DEVICE_ID_6816_2   0x6811
> +#define      PCI_DEVICE_ID_6816_3   0x6812
> +#define      PCI_DEVICE_ID_6816_4   0x6813
> +
> +#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)
> +/* TW6800 chips have trouble with these, so we don't set them for that chip 
> */
> +#define      TW68_VID_INTSX  (TW68_FDMIS | TW68_HLOCK | TW68_VLOCK)
> +
> +#define      TW68_I2C_INTS   (TW68_SBERR | TW68_SBDONE | TW68_SBERR2  | \
> +                      TW68_SBDONE2)
> +
> +enum tw68_decoder_type {
> +     TW6800,
> +     TW6801,
> +     TW6804,
> +     TWXXXX,
> +};
> +
> +/* ----------------------------------------------------------- */
> +/* static data                                                 */
> +
> +struct tw68_tvnorm {
> +     char            *name;
> +     v4l2_std_id     id;
> +
> +     /* video decoder */
> +     u32     sync_control;
> +     u32     luma_control;
> +     u32     chroma_ctrl1;
> +     u32     chroma_gain;
> +     u32     chroma_ctrl2;
> +     u32     vgate_misc;
> +
> +     /* video scaler */
> +     u32     h_delay;
> +     u32     h_delay0;       /* for TW6800 */
> +     u32     h_start;
> +     u32     h_stop;
> +     u32     v_delay;
> +     u32     video_v_start;
> +     u32     video_v_stop;
> +     u32     vbi_v_start_0;
> +     u32     vbi_v_stop_0;
> +     u32     vbi_v_start_1;
> +
> +     /* Techwell specific */
> +     u32     format;
> +};
> +
> +struct tw68_format {
> +     char    *name;
> +     u32     fourcc;
> +     u32     depth;
> +     u32     twformat;
> +};
> +
> +/* ----------------------------------------------------------- */
> +/* card configuration                                          */
> +
> +#define TW68_BOARD_NOAUTO            UNSET
> +#define TW68_BOARD_UNKNOWN           0
> +#define      TW68_BOARD_GENERIC_6802         1
> +
> +#define      TW68_MAXBOARDS                  16
> +#define      TW68_INPUT_MAX                  4
> +
> +/* ----------------------------------------------------------- */
> +/* device / file handle status                                 */
> +
> +#define      BUFFER_TIMEOUT  msecs_to_jiffies(500)   /* 0.5 seconds */
> +
> +struct tw68_dev;     /* forward delclaration */
> +
> +/* buffer for one video/vbi/ts frame */
> +struct tw68_buf {
> +     struct vb2_buffer vb;
> +     struct list_head list;
> +
> +     unsigned int   size;
> +     __le32         *cpu;
> +     __le32         *jmp;
> +     dma_addr_t     dma;
> +};
> +
> +struct tw68_fmt {
> +     char                    *name;
> +     u32                     fourcc; /* v4l2 format id */
> +     int                     depth;
> +     int                     flags;
> +     u32                     twformat;
> +};
> +
> +/* global device status */
> +struct tw68_dev {
> +     struct mutex            lock;
> +     spinlock_t              slock;
> +     u16                     instance;
> +     struct v4l2_device      v4l2_dev;
> +
> +     /* various device info */
> +     enum tw68_decoder_type  vdecoder;
> +     struct video_device     vdev;
> +     struct v4l2_ctrl_handler hdl;
> +
> +     /* pci i/o */
> +     char                    *name;
> +     struct pci_dev          *pci;
> +     unsigned char           pci_rev, pci_lat;
> +     u32                     __iomem *lmmio;
> +     u8                      __iomem *bmmio;
> +     u32                     pci_irqmask;
> +     /* The irq mask to be used will depend upon the chip type */
> +     u32                     board_virqmask;
> +
> +     /* 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 */
> +     const struct tw68_tvnorm *tvnorm;       /* video */
> +
> +     int                     input;
> +};
> +
> +/* ----------------------------------------------------------- */
> +
> +#define tw_readl(reg)                readl(dev->lmmio + ((reg) >> 2))
> +#define      tw_readb(reg)           readb(dev->bmmio + (reg))
> +#define tw_writel(reg, value)        writel((value), dev->lmmio + ((reg) >> 
> 2))
> +#define      tw_writeb(reg, value)   writeb((value), dev->bmmio + (reg))
> +
> +#define tw_andorl(reg, mask, value) \
> +             writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\
> +             ((value) & (mask)), dev->lmmio+((reg)>>2))
> +#define      tw_andorb(reg, mask, value) \
> +             writeb((readb(dev->bmmio + (reg)) & ~(mask)) |\
> +             ((value) & (mask)), dev->bmmio+(reg))
> +#define tw_setl(reg, bit)    tw_andorl((reg), (bit), (bit))
> +#define      tw_setb(reg, bit)       tw_andorb((reg), (bit), (bit))
> +#define      tw_clearl(reg, bit)     \
> +             writel((readl(dev->lmmio + ((reg) >> 2)) & ~(bit)), \
> +             dev->lmmio + ((reg) >> 2))
> +#define      tw_clearb(reg, bit)     \
> +             writeb((readb(dev->bmmio+(reg)) & ~(bit)), \
> +             dev->bmmio + (reg))
> +
> +#define tw_wait(us) { udelay(us); }
> +
> +/* ----------------------------------------------------------- */
> +/* tw68-video.c                                                */
> +
> +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, int video_nr);
> +void tw68_irq_video_done(struct tw68_dev *dev, unsigned long status);
> +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 tw68_buf *buf,
> +     struct scatterlist *sglist, unsigned int top_offset,
> +     unsigned int bottom_offset, unsigned int bpl,
> +     unsigned int padding, unsigned int lines);
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to